Simplify query argument.
Truth table and array utils. Count distinct atomic values.
This commit is contained in:
parent
912cf6f1e5
commit
f3fa0334c2
@ -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!(
|
||||
|
@ -2,4 +2,5 @@ pub mod expression;
|
||||
pub mod operator;
|
||||
#[macro_use]
|
||||
pub mod helpers;
|
||||
pub mod simplify;
|
||||
pub mod simplify;
|
||||
mod truth_table;
|
154
src/expressions/truth_table.rs
Normal file
154
src/expressions/truth_table.rs
Normal 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"]);
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@ mod parsing;
|
||||
mod routing;
|
||||
mod language;
|
||||
mod config;
|
||||
mod utils;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
|
@ -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
42
src/utils/array.rs
Normal 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
1
src/utils/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod array;
|
Loading…
x
Reference in New Issue
Block a user