From 6a8312dfe446018ab201254ce08d5baf3ef89d04 Mon Sep 17 00:00:00 2001 From: asteri Date: Wed, 22 Apr 2026 00:20:59 +0200 Subject: [PATCH] wip flat representation of polynomial over Z --- .gitignore | 1 + Cargo.lock | 25 +++++++ Cargo.toml | 7 ++ src/bin/flat.rs | 34 +++++++++ src/lib.rs | 1 + src/poly/flat.rs | 181 +++++++++++++++++++++++++++++++++++++++++++++++ src/poly/mod.rs | 2 + src/poly/var.rs | 63 +++++++++++++++++ 8 files changed, 314 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/bin/flat.rs create mode 100644 src/lib.rs create mode 100644 src/poly/flat.rs create mode 100644 src/poly/mod.rs create mode 100644 src/poly/var.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..10c36b2 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,25 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "circuit-polynomial" +version = "0.1.0" +dependencies = [ + "itertools", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..d961077 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "circuit-polynomial" +version = "0.1.0" +edition = "2024" + +[dependencies] +itertools = "0.14.0" diff --git a/src/bin/flat.rs b/src/bin/flat.rs new file mode 100644 index 0000000..1569a8b --- /dev/null +++ b/src/bin/flat.rs @@ -0,0 +1,34 @@ +use circuit_polynomial::poly::flat; +use circuit_polynomial::poly::flat::Mono; +use circuit_polynomial::{var,poly::var::StaticVar}; + +fn main() { + let poly: flat::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), + ], + ), + ] + .into(); + + let x=var!("x"); + let y=var!("y"); + let mono = 3*((&x^2)*(&y^4)); + + println!("{poly}"); + + let z=poly+mono; + println!("{z}"); +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..0fc1585 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1 @@ +pub mod poly; diff --git a/src/poly/flat.rs b/src/poly/flat.rs new file mode 100644 index 0000000..e6b36c5 --- /dev/null +++ b/src/poly/flat.rs @@ -0,0 +1,181 @@ +use itertools::Itertools; +use std::fmt::{self, Display}; + +use std::ops::{Add, BitXor, Mul}; + +use super::var::{StaticVar, Var}; +use std::collections::HashMap; + +#[derive(Clone, Debug)] +pub struct Poly { + mono: HashMap, i32>, +} + +impl Default for Poly { + fn default() -> Self { + Poly { + mono: Default::default(), + } + } +} + +impl Display for Poly { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + match self.mono.is_empty() { + true => write!(fmt, "∅"), + false => write!( + fmt, + "{}", + self.mono + .iter() + .map(|(m, c)| format!("{}{}", c, m)) + .join(" + ") + ), + } + } +} + +impl From for Poly +where + Poly: FromIterator<::Item>, +{ + fn from(value: T) -> Self { + value.into_iter().collect() + } +} + +impl>> FromIterator<(i32, U)> for Poly { + fn from_iter>(iter: T) -> Self { + Poly { + mono: iter.into_iter().map(|(c, m)| (m.into(), c)).collect(), + } + } +} + +#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] +pub struct Mono { + pub term: Vec<(V, u32)>, +} + +impl From for Mono +where + Mono: FromIterator<::Item>, +{ + fn from(value: T) -> Self { + value.into_iter().collect() + } +} + +impl> FromIterator<(U, u32)> for Mono { + fn from_iter>(iter: T) -> Self { + let mut term = iter + .into_iter() + .map(|(t, pow)| (t.into(), pow)) + .collect::>(); + term.sort(); + + // Check duplicate variables + assert!((term[..]) + .windows(2) + .all(|window| window[0].0 != window[1].0)); + + Mono { term } + } +} + +impl Display for Mono { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + write!( + fmt, + "{}", + self.term + .iter() + .map(|(t, p)| match p { + 1 => format!("{t}"), + _ => format!("{t}^{p}"), + }) + .join("") + ) + } +} + +impl BitXor for StaticVar { + type Output = Mono; + + fn bitxor(self, exp: u32) -> Self::Output { + [(self, exp)].into() + } +} + +impl<'a> BitXor for &'a StaticVar { + type Output = Mono; + + fn bitxor(self, exp: u32) -> Self::Output { + [(self.clone(), exp)].into() + } +} + +impl Mul for Mono { + type Output = Self; + + fn mul(self, other: Mono) -> Self::Output { + let mut a_term = self.term.into_iter().peekable(); + let mut b_term = other.term.into_iter().peekable(); + + let mut result: Vec<(V, u32)> = Default::default(); + + loop { + match (a_term.peek(), b_term.peek()) { + (Some((a_var, _)), Some((b_var, _))) => { + if a_var < b_var { + result.push(a_term.next().unwrap()); + } else if a_var > b_var { + result.push(b_term.next().unwrap()); + } else { + let (var, a_exp) = a_term.next().unwrap(); + let (_, b_exp) = b_term.next().unwrap(); + result.push((var, a_exp + b_exp)); + } + } + (Some(a), None) => { + result.push(a.clone()); + a_term.next(); + } + (None, Some(b)) => { + result.push(b.clone()); + b_term.next(); + } + (None, None) => { + break; + } + } + } + + Mono { term: result } + } +} + +impl Mul> for i32 { + type Output = Poly; + + fn mul(self, mono: Mono) -> Self::Output { + let mut poly: HashMap, i32> = Default::default(); + + poly.insert(mono, self); + + Poly { mono: poly } + } +} + +impl Add for Poly { + type Output = Poly; + + fn add(mut self, other: Poly) -> Self::Output { + for (mono, coeff) in other.mono { + let entry = self.mono.entry(mono).or_insert(0); + *entry += coeff; + } + self.mono.retain(|_, &mut coeff| coeff != 0); + self + } +} diff --git a/src/poly/mod.rs b/src/poly/mod.rs new file mode 100644 index 0000000..a0bdeae --- /dev/null +++ b/src/poly/mod.rs @@ -0,0 +1,2 @@ +pub mod flat; +pub mod var; diff --git a/src/poly/var.rs b/src/poly/var.rs new file mode 100644 index 0000000..2359e95 --- /dev/null +++ b/src/poly/var.rs @@ -0,0 +1,63 @@ +use itertools::Itertools; +use std::fmt::{self, Debug, Display, Formatter}; +use std::hash::Hash; + +#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct StaticVar { + name: &'static str, + indices: Vec, +} + +pub trait Var: PartialEq + Eq + PartialOrd + Ord + Clone + Hash + Debug + Display {} + +impl Display for StaticVar { + fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), fmt::Error> { + let num_indices = self.indices.len(); + match num_indices { + 0 => write!(fmt, "{}", self.name), + _ => write!(fmt, "{}_{{{}}}", self.name, self.indices.iter().join(",")), + } + } +} + +impl Var for StaticVar {} + +impl From<&'static str> for StaticVar { + fn from(name: &'static str) -> Self { + StaticVar { + name, + indices: Default::default(), + } + } +} + +impl From<(&'static str, u32)> for StaticVar { + fn from(values: (&'static str, u32)) -> Self { + StaticVar { + name: values.0, + indices: vec![values.1], + } + } +} + +impl From<(&'static str, u32, u32)> for StaticVar { + fn from(values: (&'static str, u32, u32)) -> Self { + StaticVar { + name: values.0, + indices: vec![values.1, values.2], + } + } +} + +#[macro_export] +macro_rules! var { + ($name:expr) => { + ::circuit_polynomial::poly::var::StaticVar::from($name) + }; + ($name:expr, $idx1:expr) => { + ::circuit_polynomial::poly::var::StaticVar::from(($name, $idx1)) + }; + ($name:expr, $idx1:expr, $idx2:expr) => { + ::circuit_polynomial::poly::var::StaticVar::from(($name, $idx1, $idx2)) + }; +}