Simplify query argument.

Truth table and array utils.

Count distinct atomic values.
This commit is contained in:
Martin Berg Alstad 2024-06-08 21:41:30 +02:00
parent 912cf6f1e5
commit f3fa0334c2
7 changed files with 252 additions and 10 deletions

View File

@ -25,10 +25,6 @@ impl Expression {
}
}
pub fn is_not(&self) -> bool {
matches!(self, Expression::Not(_))
}
pub fn exists(&self, atomic_value: &str) -> bool {
match self {
Expression::Not(expr) => expr.exists(atomic_value),
@ -36,6 +32,26 @@ impl Expression {
Expression::Atomic(value) => value == atomic_value,
}
}
pub fn count_distinct(&self) -> usize {
self.count_distinct_with_visited(vec![].as_mut())
}
fn count_distinct_with_visited(&self, visited: &mut Vec<String>) -> usize {
match self {
Expression::Not(expr) => expr.count_distinct_with_visited(visited),
Expression::Binary { left, right, .. } =>
left.count_distinct_with_visited(visited) + right.count_distinct_with_visited(visited),
Expression::Atomic(value) => {
if visited.contains(value) {
0
} else {
visited.push(value.clone());
1
}
}
}
}
}
impl OppositeEq for Expression {
@ -89,6 +105,30 @@ mod tests {
use crate::{and, atomic, implies, not, or};
use crate::expressions::expression::Expression;
#[test]
fn test_count_distinct() {
let expression = and!(
atomic!("a"),
or!(
atomic!("b"),
atomic!("c")
)
);
assert_eq!(expression.count_distinct(), 3);
}
#[test]
fn test_count_distinct_duplicates() {
let expression = and!(
atomic!("a"),
or!(
atomic!("b"),
atomic!("a")
)
);
assert_eq!(expression.count_distinct(), 2);
}
#[test]
fn test_expression_a_and_not_b_display() {
let expression = and!(

View File

@ -2,4 +2,5 @@ pub mod expression;
pub mod operator;
#[macro_use]
pub mod helpers;
pub mod simplify;
pub mod simplify;
mod truth_table;

View File

@ -0,0 +1,154 @@
use serde::{Deserialize, Serialize};
use crate::expressions::expression::Expression;
use crate::utils::array::Distinct;
type TruthMatrix = Vec<Vec<bool>>;
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TruthTable {
header: Vec<String>,
truth_matrix: TruthMatrix,
}
#[derive(Debug, Default, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum Hide {
#[default]
None,
True,
False,
}
#[derive(Debug, Default, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum Sort {
#[default]
Default,
TrueFirst,
FalseFirst,
}
#[derive(Debug, Default, Deserialize)]
pub struct TruthTableOptions {
pub sort: Sort,
pub hide: Hide,
}
impl TruthTable {
pub fn new(expression: &Expression, options: TruthTableOptions) -> Self {
let header = Self::extract_header(expression);
let truth_matrix = Self::generate_truth_matrix(expression);
Self { header, truth_matrix }
}
/// Extracts the header for the truth table from the expression
/// Duplicate values are removed.
/// - Arguments
/// - `expression` - The expression to extract the header from
/// - Returns
/// - A vector of strings representing the header
/// # Example
/// ```
/// let expression = TruthTable::extract_header(&atomic!("A"));
/// let complex_expression = TruthTable::extract_header(&implies!(and!(atomic!("A"), atomic!("B")), or!(atomic!("C"), atomic!("D"))));
/// assert_eq!(expression, vec!["A"]);
/// assert_eq!(complex_expression, vec!["A", "B", "A ⋀ B", "C", "D", "(C D)", "A ⋀ B ➔ (C D)"]);
/// ```
fn extract_header(expression: &Expression) -> Vec<String> {
match expression {
not @ Expression::Not(expr) => {
let mut header = Self::extract_header(expr);
header.push(not.to_string());
header.distinct();
header
}
binary @ Expression::Binary { left, right, .. } => {
let mut header = Self::extract_header(left);
header.extend(Self::extract_header(right));
header.push(binary.to_string());
header.distinct();
header
}
Expression::Atomic(value) => vec![value.clone()],
}
}
fn generate_truth_matrix(expression: &Expression) -> TruthMatrix {
todo!()
}
fn helper_matrix(number_of_atomics: usize) -> TruthMatrix {
todo!("Create a matrix with 2^number_of_atomics rows and number_of_atomics columns")
}
fn resolve_expression(expression: &Expression, row: &[bool]) -> bool {
todo!("Resolve the expression with the given row of booleans")
}
fn find_expression(expression: Expression, expressions: &[Expression]) -> Option<usize> {
todo!("Find the expression in the truth table and return index")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_atomic_expression() {
let expression = atomic!("A");
let header = TruthTable::extract_header(&expression);
assert_eq!(header, vec!["A"]);
}
#[test]
fn test_not_expression() {
let expression = not!(atomic!("A"));
let header = TruthTable::extract_header(&expression);
assert_eq!(header, vec!["A", "¬A"]);
}
#[test]
fn test_binary_and_expression() {
let expression = and!(atomic!("A"), atomic!("B"));
let header = TruthTable::extract_header(&expression);
assert_eq!(header, vec!["A", "B", "A ⋀ B"]);
}
#[test]
fn test_binary_or_expression() {
let expression = or!(atomic!("A"), atomic!("B"));
let header = TruthTable::extract_header(&expression);
assert_eq!(header, vec!["A", "B", "(A B)"]);
}
#[test]
fn test_binary_implies_expression() {
let expression = implies!(atomic!("A"), atomic!("B"));
let header = TruthTable::extract_header(&expression);
assert_eq!(header, vec!["A", "B", "A ➔ B"]);
}
#[test]
fn test_complex_expression() {
let expression = implies!(and!(atomic!("A"), atomic!("B")), or!(atomic!("C"), atomic!("D")));
let header = TruthTable::extract_header(&expression);
assert_eq!(header, vec!["A", "B", "A ⋀ B", "C", "D", "(C D)", "A ⋀ B ➔ (C D)"]);
}
#[test]
fn test_equal_expressions_should_not_duplicate() {
let expression = and!(atomic!("A"), and!(atomic!("A"), and!(atomic!("A"), atomic!("A"))));
let header = TruthTable::extract_header(&expression);
assert_eq!(header, vec!["A", "A ⋀ A", "A ⋀ A ⋀ A", "A ⋀ A ⋀ A ⋀ A"]);
}
#[test]
fn test_somewhat_equal() {
let expression = and!(atomic!("A"), and!(or!(not!(atomic!("A")), atomic!("B")), atomic!("A")));
let header = TruthTable::extract_header(&expression);
assert_eq!(header, vec!["A", "¬A", "B", "(¬A B)", "(¬A B) ⋀ A", "A ⋀ (¬A B) ⋀ A"]);
}
}

View File

@ -9,6 +9,7 @@ mod parsing;
mod routing;
mod language;
mod config;
mod utils;
#[tokio::main]
async fn main() {

View File

@ -42,13 +42,16 @@ struct SimplifyResponse {
// TODO
async fn simplify(Path(path): Path<String>, query: Query<QueryOptions>, accept_language: Option<AcceptLanguage>) -> Response {
if let Ok(expression) = Expression::try_from(path.as_str()) {
let simplified = expression.simplify();
if let Ok(mut expression) = Expression::try_from(path.as_str()) {
let before = expression.to_string();
if query.simplify {
expression = expression.simplify();
}
Json(SimplifyResponse {
before: expression.to_string(),
after: simplified.to_string(),
before,
after: expression.to_string(),
order_of_operations: vec![], // TODO
expression: simplified,
expression,
}).into_response()
} else {
(StatusCode::BAD_REQUEST, "Invalid expression").into_response()

42
src/utils/array.rs Normal file
View File

@ -0,0 +1,42 @@
use std::ops::{Deref, DerefMut};
#[macro_export]
macro_rules! set {
() => { std::collections::HashSet::new() };
($($x:expr),*) => {
{
let mut temp_set = std::collections::HashSet::new();
$(
temp_set.insert($x);
)*
temp_set
}
};
}
pub trait Distinct {
fn distinct(&mut self);
}
impl<T: PartialEq + Clone> Distinct for Vec<T> {
fn distinct(&mut self) {
*self = self.iter()
.fold(vec![], |mut acc, x| {
if !acc.contains(x) {
acc.push(x.clone());
}
acc
});
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_distinct() {
let mut vec = vec![1, 2, 3, 1, 2, 3];
vec.distinct();
assert_eq!(vec, vec![1, 2, 3]);
}
}

1
src/utils/mod.rs Normal file
View File

@ -0,0 +1 @@
pub mod array;