diff --git a/examples/flat.rs b/examples/flat.rs index 1f2115b..75605c2 100644 --- a/examples/flat.rs +++ b/examples/flat.rs @@ -2,8 +2,7 @@ use circuit_cas::poly::flat; use circuit_cas::var; fn main() { - let poly = (2 - * ((var!("x", 1, 5) ^ 5) * (var!("x", 1, 2) ^ 5) * (var!("x", 2, 5) ^ 1))) + let poly = (2 * ((var!("x", 1, 5) ^ 5) * (var!("x", 1, 2) ^ 5) * (var!("x", 2, 5) ^ 1))) + (3 * ((var!("x", 1, 9) ^ 5) * (var!("x", 1, 2) ^ 5) * (var!("x", 2, 5) ^ 1))); let x = var!("x"); @@ -11,13 +10,13 @@ fn main() { let z = var!("z"); let other = -3 * ((&x ^ 2) * (&y ^ 4)); - let mono = (&x^2)*(&y^4); + let mono = (&x ^ 2) * (&y ^ 4); - let inside = (&x^2)*(&y^2)*(&z^1); + let inside = (&x ^ 2) * (&y ^ 2) * (&z ^ 1); - if mono.contains(&inside){ + if mono.contains(&inside) { println!("{inside}\u{2286}{mono}"); - }else{ + } else { println!("{inside}\u{2284}{mono}"); } diff --git a/src/circuit/dag.rs b/src/circuit/dag.rs index 8c56cb8..e28f2a6 100644 --- a/src/circuit/dag.rs +++ b/src/circuit/dag.rs @@ -1,4 +1,4 @@ -use slotmap::{new_key_type, SlotMap}; +use slotmap::{SlotMap, new_key_type}; use std::cell::RefCell; use std::collections::HashMap; use std::ops::{Add, Mul}; @@ -53,12 +53,20 @@ impl Circuit { } pub fn add(&mut self, left: NodeId, right: NodeId) -> NodeId { - let (l, r) = if left <= right { (left, right) } else { (right, left) }; + let (l, r) = if left <= right { + (left, right) + } else { + (right, left) + }; self.node(Node::Sum(l, r)) } pub fn mul(&mut self, left: NodeId, right: NodeId) -> NodeId { - let (l, r) = if left <= right { (left, right) } else { (right, left) }; + let (l, r) = if left <= right { + (left, right) + } else { + (right, left) + }; self.node(Node::Prod(l, r)) } @@ -89,12 +97,18 @@ pub trait CircuitExt { impl CircuitExt for Rc>> { fn leaf(&self, v: V) -> CircuitNode { let id = self.borrow_mut().node(Node::Leaf(v)); - CircuitNode { id, circuit: self.clone() } + CircuitNode { + id, + circuit: self.clone(), + } } fn get_node(&self, id: NodeId) -> Option> { self.borrow().get(id)?; - Some(CircuitNode { id, circuit: self.clone() }) + Some(CircuitNode { + id, + circuit: self.clone(), + }) } } @@ -103,7 +117,10 @@ impl Add for CircuitNode { fn add(self, rhs: Self) -> Self { let id = self.circuit.borrow_mut().add(self.id, rhs.id); - CircuitNode { id, circuit: self.circuit } + CircuitNode { + id, + circuit: self.circuit, + } } } @@ -112,7 +129,10 @@ impl Mul for CircuitNode { fn mul(self, rhs: Self) -> Self { let id = self.circuit.borrow_mut().mul(self.id, rhs.id); - CircuitNode { id, circuit: self.circuit } + CircuitNode { + id, + circuit: self.circuit, + } } } @@ -168,5 +188,3 @@ mod tests { assert_eq!(circuit.borrow().len(), 10); } } - - diff --git a/src/circuit/mod.rs b/src/circuit/mod.rs index f2884ca..8d8ef0b 100644 --- a/src/circuit/mod.rs +++ b/src/circuit/mod.rs @@ -1 +1,2 @@ pub mod dag; +pub mod quotient; diff --git a/src/circuit/quotient.rs b/src/circuit/quotient.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/circuit/quotient.rs @@ -0,0 +1 @@ + diff --git a/src/fmt.rs b/src/fmt.rs index b48c5d0..77f056d 100644 --- a/src/fmt.rs +++ b/src/fmt.rs @@ -1,34 +1,35 @@ - -pub fn num_to_subscript(s:String)->String{ - s.chars().map(|c| match c{ - '0'=>'\u{2080}', - '1'=>'\u{2081}', - '2'=>'\u{2082}', - '3'=>'\u{2083}', - '4'=>'\u{2084}', - '5'=>'\u{2085}', - '6'=>'\u{2086}', - '7'=>'\u{2087}', - '8'=>'\u{2088}', - '9'=>'\u{2089}', - _=>c - }).collect() +pub fn num_to_subscript(s: String) -> String { + s.chars() + .map(|c| match c { + '0' => '\u{2080}', + '1' => '\u{2081}', + '2' => '\u{2082}', + '3' => '\u{2083}', + '4' => '\u{2084}', + '5' => '\u{2085}', + '6' => '\u{2086}', + '7' => '\u{2087}', + '8' => '\u{2088}', + '9' => '\u{2089}', + _ => c, + }) + .collect() } -pub fn num_to_superscript(s:String)->String{ - s.chars().map(|c| match c{ - '0'=>'\u{2070}', - '1'=>'\u{20B9}', - '2'=>'\u{00B2}', - '3'=>'\u{00B3}', - '4'=>'\u{2074}', - '5'=>'\u{2075}', - '6'=>'\u{2076}', - '7'=>'\u{2077}', - '8'=>'\u{2078}', - '9'=>'\u{2079}', - _=>c - }).collect() +pub fn num_to_superscript(s: String) -> String { + s.chars() + .map(|c| match c { + '0' => '\u{2070}', + '1' => '\u{20B9}', + '2' => '\u{00B2}', + '3' => '\u{00B3}', + '4' => '\u{2074}', + '5' => '\u{2075}', + '6' => '\u{2076}', + '7' => '\u{2077}', + '8' => '\u{2078}', + '9' => '\u{2079}', + _ => c, + }) + .collect() } - - diff --git a/src/lib.rs b/src/lib.rs index c624ce1..0ca2ea6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,3 @@ -pub mod poly; pub mod circuit; pub mod fmt; +pub mod poly; diff --git a/src/poly/flat.rs b/src/poly/flat.rs index 5c5df2a..d3fce96 100644 --- a/src/poly/flat.rs +++ b/src/poly/flat.rs @@ -53,7 +53,7 @@ impl>> FromIterator<(i32, U)> for Poly { } } -#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] +#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct Mono { pub term: Vec<(V, u32)>, } @@ -103,9 +103,11 @@ impl> FromIterator<(U, u32)> for Mono { term.sort(); // Check duplicate variables - assert!((term[..]) - .windows(2) - .all(|window| window[0].0 != window[1].0)); + assert!( + (term[..]) + .windows(2) + .all(|window| window[0].0 != window[1].0) + ); Mono { term } } @@ -195,6 +197,22 @@ impl Mul> for i32 { } } +impl Mul for Poly { + type Output = Poly; + + fn mul(self, other: Poly) -> Self::Output { + let mut result = Poly::default(); + for (m1, c1) in &self.mono { + for (m2, c2) in &other.mono { + let entry = result.mono.entry(m1.clone() * m2.clone()).or_insert(0); + *entry += c1 * c2; + } + } + result.mono.retain(|_, &mut c| c != 0); + result + } +} + impl Add for Poly { type Output = Poly; @@ -220,107 +238,3 @@ impl Sub for Poly { self } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_mono_contains() { - let a: Mono = [("x", 2), ("y", 1)].into(); - - // Lower exponent of same variable is contained - assert!(a.contains(&Mono::from([("x", 1)]))); - - // Higher exponent of same variable is not contained - assert!(!a.contains(&Mono::from([("x", 3)]))); - - // Identical monomial is contained - assert!(a.contains(&Mono::from([("x", 2), ("y", 1)]))); - - // Variable absent from self is not contained - assert!(!a.contains(&Mono::from([("x", 2), ("z", 1)]))); - - // Subset of variables with lower exponents is contained - assert!(a.contains(&Mono::from([("x", 1), ("y", 1)]))); - - // Single variable with exact exponent is contained - assert!(a.contains(&Mono::from([("x", 2)]))); - - // Insufficient exponent in self means not contained - assert!(!Mono::::from([("x", 1)]).contains(&Mono::from([("x", 2)]))); - - // Missing variable in self means not contained - assert!(!Mono::::from([("x", 1), ("y", 1)]).contains(&Mono::from([("x", 2)]))); - assert!(!Mono::::from([("x", 1)]).contains(&Mono::from([("x", 1), ("y", 1)]))); - } - - #[test] - fn test_mono_mul() { - // Same variable: exponents add - let a: Mono = [("x", 2)].into(); - let b: Mono = [("x", 3)].into(); - assert_eq!(a * b, Mono::from([("x", 5)])); - - // Disjoint variables: both appear in result - let a: Mono = [("x", 2)].into(); - let b: Mono = [("y", 3)].into(); - assert_eq!(a * b, Mono::from([("x", 2), ("y", 3)])); - - // Mixed: shared and disjoint variables - let a: Mono = [("x", 1), ("y", 2)].into(); - let b: Mono = [("y", 1), ("z", 3)].into(); - assert_eq!(a * b, Mono::from([("x", 1), ("y", 3), ("z", 3)])); - - // Commutativity - let a: Mono = [("x", 2), ("z", 1)].into(); - let b: Mono = [("y", 3)].into(); - assert_eq!(a.clone() * b.clone(), b * a); - - // Multiply by constant monomial (empty term vec = 1) - let a: Mono = [("x", 4)].into(); - let one: Mono = Mono { term: vec![] }; - assert_eq!(a.clone() * one, a); - } - - #[test] - fn test_poly_add() { - // Distinct monomials are collected as separate terms - let a: Poly = [(1, [("x", 2)]), (2, [("y", 1)])].into(); - let b: Poly = [(3, [("z", 1)])].into(); - let expected: Poly = [(1, [("x", 2)]), (2, [("y", 1)]), (3, [("z", 1)])].into(); - assert_eq!(a + b, expected); - - // Coefficients of matching monomials are summed - let a: Poly = [(2, [("x", 1)])].into(); - let b: Poly = [(3, [("x", 1)])].into(); - let expected: Poly = [(5, [("x", 1)])].into(); - assert_eq!(a + b, expected); - - // Terms that cancel sum to zero are dropped - let a: Poly = [(1, [("x", 1)])].into(); - let b: Poly = [(-1, [("x", 1)])].into(); - let expected: Poly = Poly::default(); - assert_eq!(a + b, expected); - } - - #[test] - fn test_poly_sub() { - // Distinct monomials are collected as separate terms with negated rhs coefficients - let a: Poly = [(3, [("x", 2)])].into(); - let b: Poly = [(1, [("y", 1)])].into(); - let expected: Poly = [(3, [("x", 2)]), (-1, [("y", 1)])].into(); - assert_eq!(a - b, expected); - - // Coefficients of matching monomials are subtracted - let a: Poly = [(5, [("x", 1)])].into(); - let b: Poly = [(3, [("x", 1)])].into(); - let expected: Poly = [(2, [("x", 1)])].into(); - assert_eq!(a - b, expected); - - // Subtracting equal polynomials yields zero - let a: Poly = [(4, [("x", 2)]), (1, [("y", 1)])].into(); - let b: Poly = [(4, [("x", 2)]), (1, [("y", 1)])].into(); - assert_eq!(a - b, Poly::default()); - } -} diff --git a/src/poly/mod.rs b/src/poly/mod.rs index a0bdeae..1105870 100644 --- a/src/poly/mod.rs +++ b/src/poly/mod.rs @@ -1,2 +1,5 @@ pub mod flat; pub mod var; + +#[cfg(test)] +mod tests; diff --git a/src/poly/tests.rs b/src/poly/tests.rs new file mode 100644 index 0000000..7a5c841 --- /dev/null +++ b/src/poly/tests.rs @@ -0,0 +1,102 @@ + +use super::flat::{Mono, Poly}; +use super::var::StaticVar; + +#[test] +fn test_mono_contains() { + let a: Mono = [("x", 2), ("y", 1)].into(); + + // Lower exponent of same variable is contained + assert!(a.contains(&Mono::from([("x", 1)]))); + + // Higher exponent of same variable is not contained + assert!(!a.contains(&Mono::from([("x", 3)]))); + + // Identical monomial is contained + assert!(a.contains(&Mono::from([("x", 2), ("y", 1)]))); + + // Variable absent from self is not contained + assert!(!a.contains(&Mono::from([("x", 2), ("z", 1)]))); + + // Subset of variables with lower exponents is contained + assert!(a.contains(&Mono::from([("x", 1), ("y", 1)]))); + + // Single variable with exact exponent is contained + assert!(a.contains(&Mono::from([("x", 2)]))); + + // Insufficient exponent in self means not contained + assert!(!Mono::::from([("x", 1)]).contains(&Mono::from([("x", 2)]))); + + // Missing variable in self means not contained + assert!(!Mono::::from([("x", 1), ("y", 1)]).contains(&Mono::from([("x", 2)]))); + assert!(!Mono::::from([("x", 1)]).contains(&Mono::from([("x", 1), ("y", 1)]))); +} + +#[test] +fn test_mono_mul() { + // Same variable: exponents add + let a: Mono = [("x", 2)].into(); + let b: Mono = [("x", 3)].into(); + assert_eq!(a * b, Mono::from([("x", 5)])); + + // Disjoint variables: both appear in result + let a: Mono = [("x", 2)].into(); + let b: Mono = [("y", 3)].into(); + assert_eq!(a * b, Mono::from([("x", 2), ("y", 3)])); + + // Mixed: shared and disjoint variables + let a: Mono = [("x", 1), ("y", 2)].into(); + let b: Mono = [("y", 1), ("z", 3)].into(); + assert_eq!(a * b, Mono::from([("x", 1), ("y", 3), ("z", 3)])); + + // Commutativity + let a: Mono = [("x", 2), ("z", 1)].into(); + let b: Mono = [("y", 3)].into(); + assert_eq!(a.clone() * b.clone(), b * a); + + // Multiply by constant monomial (empty term vec = 1) + let a: Mono = [("x", 4)].into(); + let one: Mono = Mono { term: vec![] }; + assert_eq!(a.clone() * one, a); +} + +#[test] +fn test_poly_add() { + // Distinct monomials are collected as separate terms + let a: Poly = [(1, [("x", 2)]), (2, [("y", 1)])].into(); + let b: Poly = [(3, [("z", 1)])].into(); + let expected: Poly = [(1, [("x", 2)]), (2, [("y", 1)]), (3, [("z", 1)])].into(); + assert_eq!(a + b, expected); + + // Coefficients of matching monomials are summed + let a: Poly = [(2, [("x", 1)])].into(); + let b: Poly = [(3, [("x", 1)])].into(); + let expected: Poly = [(5, [("x", 1)])].into(); + assert_eq!(a + b, expected); + + // Terms that cancel sum to zero are dropped + let a: Poly = [(1, [("x", 1)])].into(); + let b: Poly = [(-1, [("x", 1)])].into(); + let expected: Poly = Poly::default(); + assert_eq!(a + b, expected); +} + +#[test] +fn test_poly_sub() { + // Distinct monomials are collected as separate terms with negated rhs coefficients + let a: Poly = [(3, [("x", 2)])].into(); + let b: Poly = [(1, [("y", 1)])].into(); + let expected: Poly = [(3, [("x", 2)]), (-1, [("y", 1)])].into(); + assert_eq!(a - b, expected); + + // Coefficients of matching monomials are subtracted + let a: Poly = [(5, [("x", 1)])].into(); + let b: Poly = [(3, [("x", 1)])].into(); + let expected: Poly = [(2, [("x", 1)])].into(); + assert_eq!(a - b, expected); + + // Subtracting equal polynomials yields zero + let a: Poly = [(4, [("x", 2)]), (1, [("y", 1)])].into(); + let b: Poly = [(4, [("x", 2)]), (1, [("y", 1)])].into(); + assert_eq!(a - b, Poly::default()); +} diff --git a/src/poly/var.rs b/src/poly/var.rs index b7d4095..f345fb4 100644 --- a/src/poly/var.rs +++ b/src/poly/var.rs @@ -17,7 +17,15 @@ impl Display for StaticVar { let num_indices = self.indices.len(); match num_indices { 0 => write!(fmt, "{}", self.name), - _ => write!(fmt, "{}{}", self.name, self.indices.iter().map(|x| num_to_subscript(x.to_string())).join(",")), + _ => write!( + fmt, + "{}{}", + self.name, + self.indices + .iter() + .map(|x| num_to_subscript(x.to_string())) + .join(",") + ), } } }