From f600d8bf35c7edb82de60da6e9fcc61855783b64 Mon Sep 17 00:00:00 2001 From: Ben Ruijl Date: Wed, 13 Nov 2024 16:21:18 +0100 Subject: [PATCH] Add self-rings and a ring element wrapper - Formatter uses + and - instead of +1* and -1* --- src/api/cpp.rs | 1 + src/domains.rs | 314 +++++++++++++++++- src/domains/algebraic_number.rs | 4 +- src/domains/atom.rs | 28 +- src/domains/factorized_rational_polynomial.rs | 37 ++- src/domains/finite_field.rs | 10 +- src/domains/float.rs | 12 +- src/domains/integer.rs | 62 +++- src/domains/rational.rs | 94 +++++- src/domains/rational_polynomial.rs | 24 +- src/poly.rs | 19 +- src/poly/polynomial.rs | 54 +-- src/poly/univariate.rs | 61 ++-- src/printer.rs | 5 +- 14 files changed, 602 insertions(+), 123 deletions(-) diff --git a/src/api/cpp.rs b/src/api/cpp.rs index 32ae5f9..18839c6 100644 --- a/src/api/cpp.rs +++ b/src/api/cpp.rs @@ -8,6 +8,7 @@ use smartstring::{LazyCompact, SmartString}; use crate::domains::finite_field::{FiniteField, FiniteFieldCore, Mersenne64, Zp, Zp64}; use crate::domains::integer::{IntegerRing, Z}; use crate::domains::rational::Q; +use crate::domains::SelfRing; use crate::parser::Token; use crate::poly::Variable; use crate::LicenseManager; diff --git a/src/domains.rs b/src/domains.rs index 1b0d8e4..720766b 100644 --- a/src/domains.rs +++ b/src/domains.rs @@ -8,8 +8,10 @@ pub mod integer; pub mod rational; pub mod rational_polynomial; +use std::borrow::Borrow; use std::fmt::{Debug, Display, Error, Formatter}; use std::hash::Hash; +use std::ops::{Add, Mul, Sub}; use integer::Integer; @@ -69,6 +71,20 @@ pub trait Derivable: Ring { fn derivative(&self, e: &::Element, x: &Variable) -> ::Element; } +/// Rings whose elements contain all the knowledge of the ring itself, +/// for example integers. A counterexample would be finite field elements, +/// as they do not store the prime. +pub trait SelfRing: Clone + PartialEq + Eq + Hash + InternalOrdering + Debug + Display { + fn is_zero(&self) -> bool; + fn is_one(&self) -> bool; + fn format( + &self, + opts: &PrintOptions, + state: PrintState, + f: &mut W, + ) -> Result; +} + pub trait Ring: Clone + PartialEq + Eq + Hash + Debug + Display { type Element: Clone + PartialEq + Eq + Hash + InternalOrdering + Debug; @@ -101,7 +117,7 @@ pub trait Ring: Clone + PartialEq + Eq + Hash + Debug + Display { opts: &PrintOptions, state: PrintState, f: &mut W, - ) -> Result<(), Error>; + ) -> Result; fn printer<'a>(&'a self, element: &'a Self::Element) -> RingPrinter<'a, Self> { RingPrinter::new(self, element) @@ -140,11 +156,297 @@ impl<'a, R: Ring> RingPrinter<'a, R> { impl<'a, R: Ring> Display for RingPrinter<'a, R> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - self.ring.format( - self.element, - &self.opts.clone().update_with_fmt(f), - self.state.clone().update_with_fmt(f), - f, + self.ring + .format( + self.element, + &self.opts.clone().update_with_fmt(f), + self.state.clone().update_with_fmt(f), + f, + ) + .map(|_| ()) + } +} + +/// A ring element wrapped together with its ring. +#[derive(Clone)] +pub struct WrappedRingElement> { + pub ring: C, + pub element: R::Element, +} + +impl> WrappedRingElement { + pub fn new(ring: C, element: R::Element) -> Self { + WrappedRingElement { ring, element } + } + + pub fn ring(&self) -> &R { + self.ring.borrow() + } +} + +impl> Ring for WrappedRingElement { + type Element = WrappedRingElement; + + fn add(&self, a: &Self::Element, b: &Self::Element) -> Self::Element { + WrappedRingElement { + ring: self.ring.clone(), + element: self.ring().add(&a.element, &b.element), + } + } + + fn sub(&self, a: &Self::Element, b: &Self::Element) -> Self::Element { + WrappedRingElement { + ring: self.ring.clone(), + element: self.ring().sub(&a.element, &b.element), + } + } + + fn mul(&self, a: &Self::Element, b: &Self::Element) -> Self::Element { + WrappedRingElement { + ring: self.ring.clone(), + element: self.ring().mul(&a.element, &b.element), + } + } + + fn add_assign(&self, a: &mut Self::Element, b: &Self::Element) { + self.ring().add_assign(&mut a.element, &b.element); + } + + fn sub_assign(&self, a: &mut Self::Element, b: &Self::Element) { + self.ring().sub_assign(&mut a.element, &b.element); + } + + fn mul_assign(&self, a: &mut Self::Element, b: &Self::Element) { + self.ring().mul_assign(&mut a.element, &b.element); + } + + fn add_mul_assign(&self, a: &mut Self::Element, b: &Self::Element, c: &Self::Element) { + self.ring() + .add_mul_assign(&mut a.element, &b.element, &c.element); + } + + fn sub_mul_assign(&self, a: &mut Self::Element, b: &Self::Element, c: &Self::Element) { + self.ring() + .sub_mul_assign(&mut a.element, &b.element, &c.element); + } + + fn neg(&self, a: &Self::Element) -> Self::Element { + WrappedRingElement { + ring: self.ring.clone(), + element: self.ring().neg(&a.element), + } + } + + fn zero(&self) -> Self::Element { + WrappedRingElement { + ring: self.ring.clone(), + element: self.ring().zero(), + } + } + + fn one(&self) -> Self::Element { + WrappedRingElement { + ring: self.ring.clone(), + element: self.ring().one(), + } + } + + fn nth(&self, n: u64) -> Self::Element { + WrappedRingElement { + ring: self.ring.clone(), + element: self.ring().nth(n), + } + } + + fn pow(&self, b: &Self::Element, e: u64) -> Self::Element { + WrappedRingElement { + ring: self.ring.clone(), + element: self.ring().pow(&b.element, e), + } + } + + fn is_zero(a: &Self::Element) -> bool { + R::is_zero(&a.element) + } + + fn is_one(&self, a: &Self::Element) -> bool { + self.ring().is_one(&a.element) + } + + fn one_is_gcd_unit() -> bool { + R::one_is_gcd_unit() + } + + fn characteristic(&self) -> Integer { + self.ring().characteristic() + } + + fn size(&self) -> Integer { + self.ring().size() + } + + fn sample(&self, rng: &mut impl rand::RngCore, range: (i64, i64)) -> Self::Element { + WrappedRingElement { + ring: self.ring.clone(), + element: self.ring().sample(rng, range), + } + } + + fn format( + &self, + element: &Self::Element, + opts: &PrintOptions, + state: PrintState, + f: &mut W, + ) -> Result { + self.ring().format(&element.element, opts, state, f) + } +} + +impl> Debug for WrappedRingElement { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + self.ring() + .format( + &self.element, + &PrintOptions::default(), + PrintState::default(), + f, + ) + .map(|_| ()) + } +} + +impl> Display for WrappedRingElement { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + self.ring() + .format( + &self.element, + &PrintOptions::default(), + PrintState::default(), + f, + ) + .map(|_| ()) + } +} + +impl> PartialEq for WrappedRingElement { + fn eq(&self, other: &Self) -> bool { + self.element == other.element + } +} + +impl> Eq for WrappedRingElement {} + +impl> Hash for WrappedRingElement { + fn hash(&self, state: &mut H) { + self.element.hash(state) + } +} + +impl> InternalOrdering for WrappedRingElement { + fn internal_cmp(&self, other: &Self) -> std::cmp::Ordering { + self.element.internal_cmp(&other.element) + } +} + +impl> SelfRing for WrappedRingElement { + fn is_zero(&self) -> bool { + R::is_zero(&self.element) + } + + fn is_one(&self) -> bool { + self.ring().is_one(&self.element) + } + + fn format( + &self, + opts: &PrintOptions, + state: PrintState, + f: &mut W, + ) -> Result { + self.ring().format(&self.element, opts, state, f) + } +} + +impl> Add for WrappedRingElement { + type Output = WrappedRingElement; + + fn add(self, rhs: Self) -> Self::Output { + WrappedRingElement { + element: self.ring().add(&self.element, &rhs.element), + ring: self.ring, + } + } +} + +impl> Sub for WrappedRingElement { + type Output = WrappedRingElement; + + fn sub(self, rhs: Self) -> Self::Output { + WrappedRingElement { + element: self.ring().sub(&self.element, &rhs.element), + ring: self.ring, + } + } +} + +impl> Mul for WrappedRingElement { + type Output = WrappedRingElement; + + fn mul(self, rhs: Self) -> Self::Output { + WrappedRingElement { + element: self.ring().mul(&self.element, &rhs.element), + ring: self.ring, + } + } +} + +impl> EuclideanDomain for WrappedRingElement { + fn rem(&self, a: &Self::Element, b: &Self::Element) -> Self::Element { + WrappedRingElement { + ring: self.ring.clone(), + element: self.ring().rem(&a.element, &b.element), + } + } + + fn quot_rem(&self, a: &Self::Element, b: &Self::Element) -> (Self::Element, Self::Element) { + let (quot, rem) = self.ring().quot_rem(&a.element, &b.element); + ( + WrappedRingElement { + ring: self.ring.clone(), + element: quot, + }, + WrappedRingElement { + ring: self.ring.clone(), + element: rem, + }, ) } + + fn gcd(&self, a: &Self::Element, b: &Self::Element) -> Self::Element { + WrappedRingElement { + ring: self.ring.clone(), + element: self.ring().gcd(&a.element, &b.element), + } + } +} + +impl> Field for WrappedRingElement { + fn div(&self, a: &Self::Element, b: &Self::Element) -> Self::Element { + WrappedRingElement { + ring: self.ring.clone(), + element: self.ring().div(&a.element, &b.element), + } + } + + fn div_assign(&self, a: &mut Self::Element, b: &Self::Element) { + self.ring().div_assign(&mut a.element, &b.element); + } + + fn inv(&self, a: &Self::Element) -> Self::Element { + WrappedRingElement { + ring: self.ring.clone(), + element: self.ring().inv(&a.element), + } + } } diff --git a/src/domains/algebraic_number.rs b/src/domains/algebraic_number.rs index 21ef9bc..00e913d 100644 --- a/src/domains/algebraic_number.rs +++ b/src/domains/algebraic_number.rs @@ -17,7 +17,7 @@ use super::{ }, integer::Integer, rational::Rational, - EuclideanDomain, Field, InternalOrdering, Ring, + EuclideanDomain, Field, InternalOrdering, Ring, SelfRing, }; /// An algebraic number ring, with a monic, irreducible defining polynomial. @@ -493,7 +493,7 @@ impl Ring for AlgebraicExtension { opts: &crate::printer::PrintOptions, state: crate::printer::PrintState, f: &mut W, - ) -> Result<(), std::fmt::Error> { + ) -> Result { element.poly.format(opts, state, f) } } diff --git a/src/domains/atom.rs b/src/domains/atom.rs index 6a47874..7db3081 100644 --- a/src/domains/atom.rs +++ b/src/domains/atom.rs @@ -3,7 +3,9 @@ use crate::{ poly::Variable, }; -use super::{integer::Integer, Derivable, EuclideanDomain, Field, InternalOrdering, Ring}; +use super::{ + integer::Integer, Derivable, EuclideanDomain, Field, InternalOrdering, Ring, SelfRing, +}; use rand::Rng; @@ -130,8 +132,28 @@ impl Ring for AtomField { opts: &crate::printer::PrintOptions, state: crate::printer::PrintState, f: &mut W, - ) -> Result<(), std::fmt::Error> { - element.as_view().format(f, opts, state) + ) -> Result { + // TODO: return true when result is + or - + element.as_view().format(f, opts, state).map(|_| false) + } +} + +impl SelfRing for Atom { + fn is_zero(&self) -> bool { + self.is_zero() + } + + fn is_one(&self) -> bool { + self.is_one() + } + + fn format( + &self, + opts: &crate::printer::PrintOptions, + state: crate::printer::PrintState, + f: &mut W, + ) -> Result { + self.as_view().format(f, opts, state).map(|_| false) } } diff --git a/src/domains/factorized_rational_polynomial.rs b/src/domains/factorized_rational_polynomial.rs index 661f92f..d1c5013 100644 --- a/src/domains/factorized_rational_polynomial.rs +++ b/src/domains/factorized_rational_polynomial.rs @@ -19,7 +19,7 @@ use super::{ finite_field::{FiniteField, FiniteFieldCore, FiniteFieldWorkspace, ToFiniteField}, integer::{Integer, IntegerRing, Z}, rational::RationalField, - EuclideanDomain, Field, InternalOrdering, Ring, + EuclideanDomain, Field, InternalOrdering, Ring, SelfRing, }; #[derive(Clone, PartialEq, Eq, Hash, Debug)] @@ -145,13 +145,23 @@ impl FactorizedRationalPolynomial { && self.numerator.ring.is_one(&self.numer_coeff) && self.numerator.ring.is_one(&self.denom_coeff) } +} + +impl SelfRing for FactorizedRationalPolynomial { + fn is_zero(&self) -> bool { + self.is_zero() + } - pub fn format( + fn is_one(&self) -> bool { + self.is_one() + } + + fn format( &self, opts: &PrintOptions, mut state: PrintState, f: &mut W, - ) -> Result<(), Error> { + ) -> Result { if opts.explicit_rational_polynomial { if state.in_sum { f.write_char('+')?; @@ -198,15 +208,17 @@ impl FactorizedRationalPolynomial { f.write_char(']')?; } - return Ok(()); + return Ok(false); } if R::is_zero(&self.numer_coeff) { if state.in_sum { - return f.write_str("+0"); + f.write_str("+0")?; } else { - return f.write_char('0'); + f.write_char('0')?; } + + return Ok(false); } if self.denominators.is_empty() && self.numerator.ring.is_one(&self.denom_coeff) { @@ -223,7 +235,7 @@ impl FactorizedRationalPolynomial { { if !self.numerator.ring.is_one(&self.numer_coeff) { if self.numerator.is_one() { - return Ok(()); + return Ok(false); } f.write_char('*')?; @@ -233,7 +245,7 @@ impl FactorizedRationalPolynomial { } else { if !self.numerator.ring.is_one(&self.numer_coeff) { if self.numerator.is_one() { - return Ok(()); + return Ok(false); } f.write_char('*')?; @@ -271,7 +283,8 @@ impl FactorizedRationalPolynomial { } } - return f.write_str("}}"); + f.write_str("}}")?; + return Ok(false); } if !self.numerator.ring.is_one(&self.numer_coeff) { @@ -339,7 +352,8 @@ impl FactorizedRationalPolynomial { } } - f.write_char(')') + f.write_char(')')?; + Ok(false) } } } @@ -689,6 +703,7 @@ where impl Display for FactorizedRationalPolynomial { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.format(&PrintOptions::from_fmt(f), PrintState::from_fmt(f), f) + .map(|_| ()) } } @@ -817,7 +832,7 @@ where opts: &PrintOptions, state: PrintState, f: &mut W, - ) -> Result<(), Error> { + ) -> Result { element.format(opts, state, f) } } diff --git a/src/domains/finite_field.rs b/src/domains/finite_field.rs index fce8b09..fe1cf82 100644 --- a/src/domains/finite_field.rs +++ b/src/domains/finite_field.rs @@ -408,7 +408,7 @@ impl Ring for Zp { opts: &PrintOptions, state: PrintState, f: &mut W, - ) -> Result<(), Error> { + ) -> Result { if opts.symmetric_representation_for_finite_field { Z.format(&self.to_symmetric_integer(element), opts, state, f) } else { @@ -707,7 +707,7 @@ impl Ring for Zp64 { opts: &PrintOptions, state: PrintState, f: &mut W, - ) -> Result<(), Error> { + ) -> Result { if opts.symmetric_representation_for_finite_field { Z.format(&self.to_symmetric_integer(element), opts, state, f) } else { @@ -976,7 +976,7 @@ impl Ring for FiniteField { opts: &PrintOptions, state: PrintState, f: &mut W, - ) -> Result<(), Error> { + ) -> Result { if opts.symmetric_representation_for_finite_field { Z.format(&self.to_symmetric_integer(element), opts, state, f) } else { @@ -1228,7 +1228,7 @@ impl Ring for FiniteField { opts: &PrintOptions, state: PrintState, f: &mut W, - ) -> Result<(), Error> { + ) -> Result { if opts.symmetric_representation_for_finite_field { Z.format(&self.to_symmetric_integer(element), opts, state, f) } else { @@ -1432,7 +1432,7 @@ impl Ring for FiniteField { opts: &PrintOptions, state: PrintState, f: &mut W, - ) -> Result<(), Error> { + ) -> Result { if opts.symmetric_representation_for_finite_field { Z.format(&self.to_symmetric_integer(element), opts, state, f) } else { diff --git a/src/domains/float.rs b/src/domains/float.rs index b629ed8..c7c9597 100644 --- a/src/domains/float.rs +++ b/src/domains/float.rs @@ -156,20 +156,22 @@ impl Ring fo opts: &crate::printer::PrintOptions, state: crate::printer::PrintState, f: &mut W, - ) -> Result<(), fmt::Error> { + ) -> Result { if opts.precision.is_none() { if state.in_sum { - f.write_fmt(format_args!("{:+}", element)) + f.write_fmt(format_args!("{:+}", element))? } else { - f.write_fmt(format_args!("{}", element)) + f.write_fmt(format_args!("{}", element))? } } else { if state.in_sum { - f.write_fmt(format_args!("{:+.*}", opts.precision.unwrap(), element)) + f.write_fmt(format_args!("{:+.*}", opts.precision.unwrap(), element))? } else { - f.write_fmt(format_args!("{:.*}", opts.precision.unwrap(), element)) + f.write_fmt(format_args!("{:.*}", opts.precision.unwrap(), element))? } } + + Ok(false) } #[inline(always)] diff --git a/src/domains/integer.rs b/src/domains/integer.rs index 83ab709..5186715 100644 --- a/src/domains/integer.rs +++ b/src/domains/integer.rs @@ -24,7 +24,7 @@ use super::{ }, float::{FloatField, NumericalFloatLike, Real, RealNumberLike, SingleFloat}, rational::Rational, - EuclideanDomain, InternalOrdering, Ring, + EuclideanDomain, InternalOrdering, Ring, SelfRing, }; pub const SMALL_PRIMES: [i64; 100] = [ @@ -1178,32 +1178,54 @@ impl Ring for IntegerRing { opts: &PrintOptions, state: PrintState, f: &mut W, - ) -> Result<(), Error> { - match element { + ) -> Result { + element.format(opts, state, f) + } +} + +impl SelfRing for Integer { + fn is_zero(&self) -> bool { + self.is_zero() + } + + fn is_one(&self) -> bool { + self.is_one() + } + + fn format( + &self, + opts: &PrintOptions, + state: PrintState, + f: &mut W, + ) -> Result { + match self { Integer::Natural(n) => { if state.suppress_one { if *n == 1 { if state.in_sum { - return write!(f, "+"); + write!(f, "+")?; + return Ok(true); } else { - return write!(f, ""); + write!(f, "")?; + return Ok(true); } } else if *n == -1 { - return write!(f, "-"); + write!(f, "-")?; + return Ok(true); } } if state.in_sum { - write!(f, "{:+}", n) + write!(f, "{:+}", n)? } else { - write!(f, "{}", n) + write!(f, "{}", n)? } } Integer::Double(n) => { if state.in_sum { - write!(f, "{:+}", n) + write!(f, "{:+}", n)? } else { - write!(f, "{}", n) + write!(f, "{}", n)? } } Integer::Large(r) => { @@ -1211,19 +1233,21 @@ impl Ring for IntegerRing { // write the GMP number in hexadecimal representation, // since the conversion is much faster than for the decimal representation if r.is_negative() { - write!(f, "-#{:X}", r.as_abs()) + write!(f, "-#{:X}", r.as_abs())? } else if state.in_sum { - write!(f, "+#{:X}", r) + write!(f, "+#{:X}", r)? } else { - write!(f, "#{:X}", r) + write!(f, "#{:X}", r)? } } else if state.in_sum { - write!(f, "{:+}", r) + write!(f, "{:+}", r)? } else { - write!(f, "{}", r) + write!(f, "{}", r)? } } } + + Ok(false) } } @@ -2216,12 +2240,14 @@ impl Ring for MultiPrecisionIntegerRing { _opts: &PrintOptions, state: PrintState, f: &mut W, - ) -> Result<(), Error> { + ) -> Result { if state.in_sum { - write!(f, "{:+}", element) + write!(f, "{:+}", element)? } else { - write!(f, "{}", element) + write!(f, "{}", element)? } + + Ok(false) } } diff --git a/src/domains/rational.rs b/src/domains/rational.rs index d4da4fd..0f74cca 100644 --- a/src/domains/rational.rs +++ b/src/domains/rational.rs @@ -1,6 +1,6 @@ use std::{ borrow::Cow, - fmt::{Display, Error, Formatter, Write}, + fmt::{Display, Error, Formatter}, ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}, }; @@ -14,7 +14,7 @@ use super::{ FiniteField, FiniteFieldCore, FiniteFieldWorkspace, ToFiniteField, Two, Zp, Z2, }, integer::{Integer, IntegerRing, Z}, - EuclideanDomain, Field, InternalOrdering, Ring, + EuclideanDomain, Field, InternalOrdering, Ring, SelfRing, }; /// The field of rational numbers. @@ -336,7 +336,7 @@ impl Ring for FractionField { opts: &PrintOptions, mut state: PrintState, f: &mut W, - ) -> Result<(), Error> { + ) -> Result { let has_denom = !self.ring.is_one(&element.denominator); let write_par = has_denom && state.in_exp; @@ -350,7 +350,7 @@ impl Ring for FractionField { state.in_exp = false; } - self.ring.format( + if self.ring.format( &element.numerator, opts, PrintState { @@ -360,7 +360,9 @@ impl Ring for FractionField { ..state }, f, - )?; + )? { + return Ok(true); + }; if has_denom { f.write_char('/')?; @@ -372,7 +374,75 @@ impl Ring for FractionField { f.write_char(')')?; } - Ok(()) + Ok(false) + } +} + +impl SelfRing for Fraction +where + R::Element: SelfRing, +{ + fn is_zero(&self) -> bool { + self.numerator.is_zero() + } + + fn is_one(&self) -> bool { + self.numerator.is_one() && self.denominator.is_one() + } + + fn format( + &self, + opts: &PrintOptions, + mut state: PrintState, + f: &mut W, + ) -> Result { + let has_denom = !self.denominator.is_one(); + + let write_par = has_denom && state.in_exp; + if write_par { + if state.in_sum { + state.in_sum = false; + f.write_char('+')?; + } + + f.write_char('(')?; + state.in_exp = false; + } + + if self.numerator.format( + opts, + PrintState { + in_product: state.in_product || has_denom, + suppress_one: state.suppress_one && !has_denom, + level: state.level + 1, + ..state + }, + f, + )? { + return Ok(true); + } + + if has_denom { + f.write_char('/')?; + self.denominator + .format(opts, state.step(false, true, true), f)?; + } + + if write_par { + f.write_char(')')?; + } + + Ok(false) + } +} + +impl Display for Fraction +where + R::Element: SelfRing, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + self.format(&PrintOptions::default(), PrintState::new(), f) + .map(|_| ()) } } @@ -459,18 +529,6 @@ impl Default for Rational { } } -impl Display for Rational { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.numerator.fmt(f)?; - if self.denominator != 1 { - f.write_char('/')?; - write!(f, "{}", self.denominator)?; - } - - Ok(()) - } -} - impl From for Rational { /// Convert a floating point number to its exact rational number equivalent. /// Use [`Rational::truncate_denominator`] to get an approximation with a smaller denominator. diff --git a/src/domains/rational_polynomial.rs b/src/domains/rational_polynomial.rs index 8a17eaa..7d49179 100644 --- a/src/domains/rational_polynomial.rs +++ b/src/domains/rational_polynomial.rs @@ -21,7 +21,7 @@ use super::{ finite_field::{FiniteField, FiniteFieldCore, FiniteFieldWorkspace, ToFiniteField}, integer::{Integer, IntegerRing, Z}, rational::RationalField, - Derivable, EuclideanDomain, Field, InternalOrdering, Ring, + Derivable, EuclideanDomain, Field, InternalOrdering, Ring, SelfRing, }; #[derive(Clone, PartialEq, Eq, Hash, Debug)] @@ -163,13 +163,23 @@ impl RationalPolynomial { true, ) } +} + +impl SelfRing for RationalPolynomial { + fn is_zero(&self) -> bool { + self.is_zero() + } - pub fn format( + fn is_one(&self) -> bool { + self.numerator.is_one() && self.denominator.is_one() + } + + fn format( &self, opts: &PrintOptions, state: PrintState, f: &mut W, - ) -> Result<(), Error> { + ) -> Result { if opts.explicit_rational_polynomial { if state.in_sum { f.write_char('+')?; @@ -191,7 +201,7 @@ impl RationalPolynomial { f.write_char(']')?; } - return Ok(()); + return Ok(false); } if self.denominator.is_one() { @@ -205,7 +215,8 @@ impl RationalPolynomial { self.numerator.format(opts, PrintState::new(), f)?; f.write_str("}{")?; self.denominator.format(opts, PrintState::new(), f)?; - return f.write_str("}"); + f.write_str("}")?; + return Ok(false); } self.numerator @@ -550,6 +561,7 @@ where impl Display for RationalPolynomial { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.format(&PrintOptions::from_fmt(f), PrintState::from_fmt(f), f) + .map(|_| ()) } } @@ -675,7 +687,7 @@ where opts: &PrintOptions, state: PrintState, f: &mut W, - ) -> Result<(), Error> { + ) -> Result { element.format(opts, state, f) } } diff --git a/src/poly.rs b/src/poly.rs index bf64622..1e8968e 100644 --- a/src/poly.rs +++ b/src/poly.rs @@ -27,8 +27,9 @@ use crate::domains::factorized_rational_polynomial::{ }; use crate::domains::integer::Integer; use crate::domains::rational_polynomial::{FromNumeratorAndDenominator, RationalPolynomial}; -use crate::domains::{EuclideanDomain, Ring}; +use crate::domains::{EuclideanDomain, Ring, RingPrinter}; use crate::parser::{Operator, Token}; +use crate::printer::{PrintOptions, PrintState}; use crate::state::{State, Workspace}; use crate::utils; @@ -393,6 +394,22 @@ impl Variable { } } + pub fn to_string_with_state(&self, state: PrintState) -> String { + match self { + Variable::Symbol(v) => State::get_name(*v).to_string(), + Variable::Temporary(t) => format!("_TMP_{}", *t), + Variable::Function(_, a) | Variable::Other(a) => format!( + "{}", + RingPrinter { + element: a.as_ref(), + ring: &AtomField::new(), + opts: PrintOptions::default(), + state, + } + ), + } + } + pub fn to_atom(&self) -> Atom { match self { Variable::Symbol(s) => Atom::new_var(*s), diff --git a/src/poly/polynomial.rs b/src/poly/polynomial.rs index 347d073..c2cf955 100755 --- a/src/poly/polynomial.rs +++ b/src/poly/polynomial.rs @@ -11,7 +11,7 @@ use std::sync::Arc; use crate::domains::algebraic_number::AlgebraicExtension; use crate::domains::integer::{Integer, IntegerRing}; use crate::domains::rational::{RationalField, Q}; -use crate::domains::{Derivable, EuclideanDomain, Field, InternalOrdering, Ring}; +use crate::domains::{Derivable, EuclideanDomain, Field, InternalOrdering, Ring, SelfRing}; use crate::printer::{PrintOptions, PrintState}; use super::gcd::PolynomialGCD; @@ -155,7 +155,7 @@ impl Ring for PolynomialRing { opts: &PrintOptions, state: PrintState, f: &mut W, - ) -> Result<(), std::fmt::Error> { + ) -> Result { element.format(opts, state, f) } } @@ -707,13 +707,25 @@ impl MultivariatePolynomial { res } +} + +impl SelfRing for MultivariatePolynomial { + #[inline] + fn is_zero(&self) -> bool { + self.is_zero() + } + + #[inline] + fn is_one(&self) -> bool { + self.is_one() + } - pub fn format( + fn format( &self, opts: &PrintOptions, mut state: PrintState, f: &mut W, - ) -> Result<(), std::fmt::Error> { + ) -> Result { let add_paren = self.nterms() > 1 && state.in_product || state.in_exp; if add_paren { @@ -732,26 +744,20 @@ impl MultivariatePolynomial { .variables .as_ref() .iter() - .map(|v| v.to_string()) + .map(|v| { + v.to_string_with_state(PrintState { + in_exp: true, + ..state + }) + }) .collect(); - let mut is_first_term = true; for monomial in self { - state.in_product = in_product || monomial.exponents.iter().any(|e| !e.is_zero()); - state.in_sum |= !is_first_term; - state.suppress_one = true; - is_first_term = false; - - self.ring.format(monomial.coefficient, opts, state, f)?; - - // TODO: is there a better way to avoid `-*`? - let mut is_first_factor = if self.ring.is_one(monomial.coefficient) { - true - } else if monomial.coefficient.eq(&self.ring.neg(&self.ring.one())) { - true - } else { - false - }; + let has_var = monomial.exponents.iter().any(|e| !e.is_zero()); + state.in_product = in_product || has_var; + state.suppress_one = in_product || has_var; + + let mut is_first_factor = self.ring.format(monomial.coefficient, opts, state, f)?; for (var_id, e) in var_map.iter().zip(monomial.exponents) { if e.is_zero() { @@ -775,6 +781,8 @@ impl MultivariatePolynomial { } } } + + state.in_sum = true; } if self.is_zero() { @@ -789,7 +797,8 @@ impl MultivariatePolynomial { f.write_str(")")?; } - Ok(()) + // FIXME: treat constant case + Ok(false) } } @@ -821,6 +830,7 @@ impl impl Display for MultivariatePolynomial { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { self.format(&PrintOptions::from_fmt(f), PrintState::from_fmt(f), f) + .map(|_| ()) } } diff --git a/src/poly/univariate.rs b/src/poly/univariate.rs index 61806a3..2c3f747 100644 --- a/src/poly/univariate.rs +++ b/src/poly/univariate.rs @@ -9,7 +9,7 @@ use crate::{ float::{Complex, FloatField, NumericalFloatLike, Real, SingleFloat}, integer::{Integer, IntegerRing, Z}, rational::{Rational, RationalField, Q}, - EuclideanDomain, Field, InternalOrdering, Ring, + EuclideanDomain, Field, InternalOrdering, Ring, SelfRing, }, printer::{PrintOptions, PrintState}, }; @@ -133,7 +133,7 @@ impl Ring for UnivariatePolynomialRing { opts: &PrintOptions, state: PrintState, f: &mut W, - ) -> Result<(), std::fmt::Error> { + ) -> Result { element.format(opts, state, f) } } @@ -189,6 +189,7 @@ impl std::fmt::Debug for UnivariatePolynomial { impl std::fmt::Display for UnivariatePolynomial { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { self.format(&PrintOptions::from_fmt(f), PrintState::from_fmt(f), f) + .map(|_| ()) } } @@ -485,13 +486,23 @@ impl UnivariatePolynomial { poly } +} + +impl SelfRing for UnivariatePolynomial { + fn is_zero(&self) -> bool { + self.is_zero() + } + + fn is_one(&self) -> bool { + self.is_one() + } fn format( &self, opts: &PrintOptions, mut state: PrintState, f: &mut W, - ) -> Result<(), std::fmt::Error> { + ) -> Result { let non_zero = self.coefficients.iter().filter(|c| !F::is_zero(c)).count(); let add_paren = non_zero > 1 && state.in_product || state.in_exp; @@ -507,7 +518,10 @@ impl UnivariatePolynomial { f.write_str("(")?; } - let v = self.variable.to_string(); // TODO: needs to_exp + let v = self.variable.to_string_with_state(PrintState { + in_exp: true, + ..state + }); for (e, c) in self.coefficients.iter().enumerate() { state.suppress_one = e > 0; @@ -516,37 +530,36 @@ impl UnivariatePolynomial { continue; } - if e == 0 { - self.field - .format(c, opts, state.step(state.in_sum, true, false), f)?; - } else if e == 1 { - if self.field.is_one(c) { - write!(f, "+{}", v)?; - } else { - self.field - .format(c, opts, state.step(true, true, false), f)?; - write!(f, "*{}", v)?; - } - } else if self.field.is_one(c) { - write!(f, "+{}^{}", v, e)?; - } else { - self.field - .format(c, opts, state.step(true, true, false), f)?; + let first_factor = self.field.format( + c, + opts, + state.step(state.in_sum, state.in_product, false), + f, + )?; + + if first_factor && e > 0 { + f.write_str("*")?; + } - // TODO: check for -1 to avoid printing -* - write!(f, "*{}^{}", v, e)?; + if e == 1 { + write!(f, "{}", v)?; + } else if e > 1 { + write!(f, "{}^{}", v, e)?; } + + state.in_sum = true; + state.in_product = true; } if self.is_zero() { f.write_char('0')?; } - if state.in_product { + if add_paren { f.write_str(")")?; } - Ok(()) + Ok(false) } } diff --git a/src/printer.rs b/src/printer.rs index fa19cf9..101411b 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -10,7 +10,7 @@ use crate::{ coefficient::CoefficientView, domains::{ factorized_rational_polynomial::FactorizedRationalPolynomial, - finite_field::FiniteFieldCore, Ring, + finite_field::FiniteFieldCore, Ring, SelfRing, }, poly::Exponent, state::State, @@ -616,6 +616,7 @@ impl<'a> FormattedPrintNum for NumView<'a> { f.write_char('[')?; p.deserialize() .format(opts, print_state.step(false, false, false), f) + .map(|_| ()) } } } @@ -1085,7 +1086,7 @@ mod test { use crate::{ atom::Atom, - domains::{finite_field::Zp, integer::Z}, + domains::{finite_field::Zp, integer::Z, SelfRing}, printer::{AtomPrinter, PrintOptions, PrintState}, state::{FunctionAttribute, State}, };