From 5be7a59d4f774cbc0e2b8426911d59ab7d3a0375 Mon Sep 17 00:00:00 2001 From: Ben Ruijl Date: Wed, 6 Nov 2024 09:22:51 +0100 Subject: [PATCH] Revise formatting system - Add self-rings and a ring element wrapper - Rename pretty_str to format --- examples/fuel_backend.rs | 18 +- src/api/cpp.rs | 154 +-- src/api/python.rs | 479 ++++---- src/domains.rs | 329 +++++- src/domains/algebraic_number.rs | 34 +- src/domains/atom.rs | 49 +- src/domains/factorized_rational_polynomial.rs | 268 ++++- src/domains/finite_field.rs | 62 +- src/domains/float.rs | 112 +- src/domains/integer.rs | 104 +- src/domains/rational.rs | 139 ++- src/domains/rational_polynomial.rs | 125 +- src/poly.rs | 19 +- src/poly/polynomial.rs | 148 ++- src/poly/resultant.rs | 74 +- src/poly/series.rs | 136 ++- src/poly/univariate.rs | 301 +++-- src/printer.rs | 1045 ++++------------- src/tensors/matrix.rs | 138 ++- symbolica.pyi | 25 +- 20 files changed, 2100 insertions(+), 1659 deletions(-) diff --git a/examples/fuel_backend.rs b/examples/fuel_backend.rs index 8c20605..ff406fa 100644 --- a/examples/fuel_backend.rs +++ b/examples/fuel_backend.rs @@ -5,9 +5,9 @@ use std::{ use smartstring::{LazyCompact, SmartString}; use symbolica::{ - domains::{integer::Z, rational::Q, rational_polynomial::RationalPolynomial}, + domains::{integer::Z, rational::Q, rational_polynomial::RationalPolynomial, SelfRing}, parser::Token, - printer::{PrintOptions, RationalPolynomialPrinter}, + printer::{PrintOptions, PrintState}, state::State, }; @@ -56,16 +56,10 @@ fn main() { .to_rational_polynomial(&Q, &Z, &vars, &var_names) .unwrap(); - let out_str = format!( - "{}", - RationalPolynomialPrinter { - poly: &r, - opts: print_opt, - add_parentheses: false - } - ); - - writeln!(&mut stdout, "{}", out_str).unwrap(); + buffer.clear(); + r.format(&print_opt, PrintState::new(), &mut buffer) + .unwrap(); + writeln!(stdout, "{}", buffer).unwrap(); buffer.clear(); } diff --git a/src/api/cpp.rs b/src/api/cpp.rs index b123dde..18839c6 100644 --- a/src/api/cpp.rs +++ b/src/api/cpp.rs @@ -8,13 +8,13 @@ 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; use crate::{ domains::factorized_rational_polynomial::FactorizedRationalPolynomial, - domains::rational_polynomial::RationalPolynomial, - printer::{FactorizedRationalPolynomialPrinter, PrintOptions, RationalPolynomialPrinter}, + domains::rational_polynomial::RationalPolynomial, printer::PrintOptions, printer::PrintState, state::State, }; @@ -169,6 +169,7 @@ unsafe extern "C" fn simplify( macro_rules! to_rational { ($in_field: expr, $exp_size: ty) => { + symbolica.local_state.buffer.clear(); if prime == 0 { let r: RationalPolynomial = token .to_rational_polynomial( @@ -179,17 +180,8 @@ unsafe extern "C" fn simplify( ) .unwrap(); - symbolica.local_state.buffer.clear(); - write!( - &mut symbolica.local_state.buffer, - "{}\0", // add the NUL character - RationalPolynomialPrinter { - poly: &r, - opts, - add_parentheses: false, - } - ) - .unwrap(); + r.format(&opts, PrintState::new(), &mut symbolica.local_state.buffer) + .unwrap(); } else if prime <= u32::MAX as c_ulonglong { let field = Zp::new(prime as u32); let rf: RationalPolynomial = token @@ -201,17 +193,8 @@ unsafe extern "C" fn simplify( ) .unwrap(); - symbolica.local_state.buffer.clear(); - write!( - &mut symbolica.local_state.buffer, - "{}\0", // add the NUL character - RationalPolynomialPrinter { - poly: &rf, - opts, - add_parentheses: false, - } - ) - .unwrap(); + rf.format(&opts, PrintState::new(), &mut symbolica.local_state.buffer) + .unwrap(); } else if prime == Mersenne64::PRIME { let field = FiniteField::::new(Mersenne64::new()); let rf: RationalPolynomial, $exp_size> = token @@ -223,17 +206,8 @@ unsafe extern "C" fn simplify( ) .unwrap(); - symbolica.local_state.buffer.clear(); - write!( - &mut symbolica.local_state.buffer, - "{}\0", // add the NUL character - RationalPolynomialPrinter { - poly: &rf, - opts, - add_parentheses: false, - } - ) - .unwrap(); + rf.format(&opts, PrintState::new(), &mut symbolica.local_state.buffer) + .unwrap(); } else { let field = Zp64::new(prime as u64); let rf: RationalPolynomial = token @@ -245,18 +219,15 @@ unsafe extern "C" fn simplify( ) .unwrap(); - symbolica.local_state.buffer.clear(); - write!( - &mut symbolica.local_state.buffer, - "{}\0", // add the NUL character - RationalPolynomialPrinter { - poly: &rf, - opts, - add_parentheses: false, - } - ) - .unwrap(); + rf.format(&opts, PrintState::new(), &mut symbolica.local_state.buffer) + .unwrap(); } + + write!( + &mut symbolica.local_state.buffer, + "\0", // add the NUL character + ) + .unwrap() }; } @@ -264,10 +235,18 @@ unsafe extern "C" fn simplify( symbolica.local_state.input_has_rational_numbers, symbolica.local_state.exp_fits_in_u8, ) { - (false, true) => to_rational!(Z, u8), - (true, true) => to_rational!(Q, u8), - (false, false) => to_rational!(Z, u16), - (true, false) => to_rational!(Q, u16), + (false, true) => { + to_rational!(Z, u8); + } + (true, true) => { + to_rational!(Q, u8); + } + (false, false) => { + to_rational!(Z, u16); + } + (true, false) => { + to_rational!(Q, u16); + } } unsafe { CStr::from_bytes_with_nul_unchecked(symbolica.local_state.buffer.as_bytes()) }.as_ptr() @@ -296,6 +275,7 @@ unsafe extern "C" fn simplify_factorized( macro_rules! to_rational { ($in_field: expr, $exp_size: ty) => { + symbolica.local_state.buffer.clear(); if prime == 0 { let r: FactorizedRationalPolynomial = token .to_factorized_rational_polynomial( @@ -306,17 +286,8 @@ unsafe extern "C" fn simplify_factorized( ) .unwrap(); - symbolica.local_state.buffer.clear(); - write!( - &mut symbolica.local_state.buffer, - "{}\0", // add the NUL character - FactorizedRationalPolynomialPrinter { - poly: &r, - opts, - add_parentheses: false, - } - ) - .unwrap(); + r.format(&opts, PrintState::new(), &mut symbolica.local_state.buffer) + .unwrap(); } else if prime <= u32::MAX as c_ulonglong { let field = Zp::new(prime as u32); let rf: FactorizedRationalPolynomial = token @@ -328,17 +299,8 @@ unsafe extern "C" fn simplify_factorized( ) .unwrap(); - symbolica.local_state.buffer.clear(); - write!( - &mut symbolica.local_state.buffer, - "{}\0", // add the NUL character - FactorizedRationalPolynomialPrinter { - poly: &rf, - opts, - add_parentheses: false, - } - ) - .unwrap(); + rf.format(&opts, PrintState::new(), &mut symbolica.local_state.buffer) + .unwrap(); } else if prime == Mersenne64::PRIME { let field = FiniteField::::new(Mersenne64::new()); let rf: FactorizedRationalPolynomial, $exp_size> = token @@ -350,17 +312,8 @@ unsafe extern "C" fn simplify_factorized( ) .unwrap(); - symbolica.local_state.buffer.clear(); - write!( - &mut symbolica.local_state.buffer, - "{}\0", // add the NUL character - FactorizedRationalPolynomialPrinter { - poly: &rf, - opts, - add_parentheses: false, - } - ) - .unwrap(); + rf.format(&opts, PrintState::new(), &mut symbolica.local_state.buffer) + .unwrap(); } else { let field = Zp64::new(prime as u64); let rf: FactorizedRationalPolynomial = token @@ -372,18 +325,15 @@ unsafe extern "C" fn simplify_factorized( ) .unwrap(); - symbolica.local_state.buffer.clear(); - write!( - &mut symbolica.local_state.buffer, - "{}\0", // add the NUL character - FactorizedRationalPolynomialPrinter { - poly: &rf, - opts, - add_parentheses: false, - } - ) - .unwrap(); + rf.format(&opts, PrintState::new(), &mut symbolica.local_state.buffer) + .unwrap(); } + + write!( + &mut symbolica.local_state.buffer, + "\0", // add the NUL character + ) + .unwrap() }; } @@ -391,10 +341,18 @@ unsafe extern "C" fn simplify_factorized( symbolica.local_state.input_has_rational_numbers, symbolica.local_state.exp_fits_in_u8, ) { - (false, true) => to_rational!(Z, u8), - (true, true) => to_rational!(Q, u8), - (false, false) => to_rational!(Z, u16), - (true, false) => to_rational!(Q, u16), + (false, true) => { + to_rational!(Z, u8); + } + (true, true) => { + to_rational!(Q, u8); + } + (false, false) => { + to_rational!(Z, u16); + } + (true, false) => { + to_rational!(Q, u16); + } } unsafe { CStr::from_bytes_with_nul_unchecked(symbolica.local_state.buffer.as_bytes()) }.as_ptr() diff --git a/src/api/python.rs b/src/api/python.rs index 33e16c6..829bb76 100644 --- a/src/api/python.rs +++ b/src/api/python.rs @@ -44,7 +44,7 @@ use crate::{ rational_polynomial::{ FromNumeratorAndDenominator, RationalPolynomial, RationalPolynomialField, }, - Ring, + Ring, SelfRing, }, evaluate::{ CompileOptions, CompiledEvaluator, EvaluationFn, ExpressionEvaluator, FunctionMap, @@ -62,9 +62,7 @@ use crate::{ factor::Factorize, groebner::GroebnerBasis, polynomial::MultivariatePolynomial, series::Series, GrevLexOrder, LexOrder, Variable, INLINED_EXPONENTS, }, - printer::{ - AtomPrinter, MatrixPrinter, PolynomialPrinter, PrintOptions, RationalPolynomialPrinter, - }, + printer::{AtomPrinter, PrintOptions, PrintState}, state::{FunctionAttribute, RecycledAtom, State, Workspace}, streaming::{TermStreamer, TermStreamerConfig}, tensors::matrix::Matrix, @@ -1342,7 +1340,8 @@ impl PythonTransformer { double_star_for_exponentiation = false, square_brackets_for_function = false, num_exp_as_superscript = true, - latex = false) + latex = false, + precision = None) )] pub fn print( &self, @@ -1358,6 +1357,7 @@ impl PythonTransformer { square_brackets_for_function: bool, num_exp_as_superscript: bool, latex: bool, + precision: Option, ) -> PyResult { return append_transformer!( self, @@ -1373,7 +1373,8 @@ impl PythonTransformer { double_star_for_exponentiation, square_brackets_for_function, num_exp_as_superscript, - latex + latex, + precision, },) ); } @@ -2344,7 +2345,7 @@ impl PythonExpression { /// Examples /// -------- /// >>> a = Expression.parse('128378127123 z^(2/3)*w^2/x/y + y^4 + z^34 + x^(x+2)+3/5+f(x,x^2)') - /// >>> print(a.pretty_str(number_thousands_separator='_', multiplication_operator=' ')) + /// >>> print(a.format(number_thousands_separator='_', multiplication_operator=' ')) #[pyo3(signature = (terms_on_new_line = false, color_top_level_sum = true, @@ -2357,9 +2358,10 @@ impl PythonExpression { double_star_for_exponentiation = false, square_brackets_for_function = false, num_exp_as_superscript = true, - latex = false) + latex = false, + precision = None) )] - pub fn pretty_str( + pub fn format( &self, terms_on_new_line: bool, color_top_level_sum: bool, @@ -2373,6 +2375,7 @@ impl PythonExpression { square_brackets_for_function: bool, num_exp_as_superscript: bool, latex: bool, + precision: Option, ) -> PyResult { Ok(format!( "{}", @@ -2390,7 +2393,8 @@ impl PythonExpression { double_star_for_exponentiation, square_brackets_for_function, num_exp_as_superscript, - latex + latex, + precision, }, ) )) @@ -5350,7 +5354,7 @@ impl PythonPolynomial { /// Examples /// -------- /// >>> p = FiniteFieldPolynomial.parse("3*x^2+2*x+7*x^3", ['x'], 11) - /// >>> print(p.pretty_str(symmetric_representation_for_finite_field=True)) + /// >>> print(p.format(symmetric_representation_for_finite_field=True)) #[pyo3(signature = (terms_on_new_line = false, color_top_level_sum = true, @@ -5363,9 +5367,10 @@ impl PythonPolynomial { double_star_for_exponentiation = false, square_brackets_for_function = false, num_exp_as_superscript = true, - latex = false) + latex = false, + precision = None) )] - pub fn pretty_str( + pub fn format( &self, terms_on_new_line: bool, color_top_level_sum: bool, @@ -5379,56 +5384,48 @@ impl PythonPolynomial { square_brackets_for_function: bool, num_exp_as_superscript: bool, latex: bool, + precision: Option, ) -> PyResult { - Ok(format!( - "{}", - PolynomialPrinter::new_with_options( - &self.poly, - PrintOptions { - terms_on_new_line, - color_top_level_sum, - color_builtin_symbols, - print_finite_field, - symmetric_representation_for_finite_field, - explicit_rational_polynomial, - number_thousands_separator, - multiplication_operator, - double_star_for_exponentiation, - square_brackets_for_function, - num_exp_as_superscript, - latex - }, - ) + Ok(self.poly.format_string( + &PrintOptions { + terms_on_new_line, + color_top_level_sum, + color_builtin_symbols, + print_finite_field, + symmetric_representation_for_finite_field, + explicit_rational_polynomial, + number_thousands_separator, + multiplication_operator, + double_star_for_exponentiation, + square_brackets_for_function, + num_exp_as_superscript, + latex, + precision, + }, + PrintState::new(), )) } /// Convert the polynomial into a portable string. pub fn __repr__(&self) -> PyResult { - Ok(format!( - "{}", - PolynomialPrinter { - poly: &self.poly, - opts: PrintOptions::file() - } - )) + Ok(self + .poly + .format_string(&PrintOptions::file(), PrintState::new())) } /// Print the polynomial in a human-readable format. pub fn __str__(&self) -> PyResult { - Ok(format!( - "{}", - PolynomialPrinter { - poly: &self.poly, - opts: PrintOptions::default() - } - )) + Ok(self + .poly + .format_string(&PrintOptions::default(), PrintState::new())) } /// Convert the polynomial into a LaTeX string. pub fn to_latex(&self) -> PyResult { Ok(format!( "$${}$$", - PolynomialPrinter::new_with_options(&self.poly, PrintOptions::latex(),) + self.poly + .format_string(&PrintOptions::latex(), PrintState::new()) )) } @@ -6180,7 +6177,7 @@ impl PythonFiniteFieldPolynomial { /// Examples /// -------- /// >>> p = FiniteFieldPolynomial.parse("3*x^2+2*x+7*x^3", ['x'], 11) - /// >>> print(p.pretty_str(symmetric_representation_for_finite_field=True)) + /// >>> print(p.format(symmetric_representation_for_finite_field=True)) #[pyo3(signature = (terms_on_new_line = false, color_top_level_sum = true, @@ -6193,9 +6190,10 @@ impl PythonFiniteFieldPolynomial { double_star_for_exponentiation = false, square_brackets_for_function = false, num_exp_as_superscript = true, - latex = false) + latex = false, + precision = None) )] - pub fn pretty_str( + pub fn format( &self, terms_on_new_line: bool, color_top_level_sum: bool, @@ -6209,56 +6207,48 @@ impl PythonFiniteFieldPolynomial { square_brackets_for_function: bool, num_exp_as_superscript: bool, latex: bool, + precision: Option, ) -> PyResult { - Ok(format!( - "{}", - PolynomialPrinter::new_with_options( - &self.poly, - PrintOptions { - terms_on_new_line, - color_top_level_sum, - color_builtin_symbols, - print_finite_field, - symmetric_representation_for_finite_field, - explicit_rational_polynomial, - number_thousands_separator, - multiplication_operator, - double_star_for_exponentiation, - square_brackets_for_function, - num_exp_as_superscript, - latex - }, - ) + Ok(self.poly.format_string( + &PrintOptions { + terms_on_new_line, + color_top_level_sum, + color_builtin_symbols, + print_finite_field, + symmetric_representation_for_finite_field, + explicit_rational_polynomial, + number_thousands_separator, + multiplication_operator, + double_star_for_exponentiation, + square_brackets_for_function, + num_exp_as_superscript, + latex, + precision, + }, + PrintState::new(), )) } /// Convert the polynomial into a portable string. pub fn __repr__(&self) -> PyResult { - Ok(format!( - "{}", - PolynomialPrinter { - poly: &self.poly, - opts: PrintOptions::file() - } - )) + Ok(self + .poly + .format_string(&PrintOptions::file(), PrintState::new())) } /// Print the polynomial in a human-readable format. pub fn __str__(&self) -> PyResult { - Ok(format!( - "{}", - PolynomialPrinter { - poly: &self.poly, - opts: PrintOptions::default() - } - )) + Ok(self + .poly + .format_string(&PrintOptions::default(), PrintState::new())) } /// Convert the polynomial into a LaTeX string. pub fn to_latex(&self) -> PyResult { Ok(format!( "$${}$$", - PolynomialPrinter::new_with_options(&self.poly, PrintOptions::latex(),) + self.poly + .format_string(&PrintOptions::latex(), PrintState::new()) )) } @@ -6767,7 +6757,7 @@ impl PythonPrimeTwoPolynomial { /// Examples /// -------- /// >>> p = FiniteFieldPolynomial.parse("3*x^2+2*x+7*x^3", ['x'], 11) - /// >>> print(p.pretty_str(symmetric_representation_for_finite_field=True)) + /// >>> print(p.format(symmetric_representation_for_finite_field=True)) #[pyo3(signature = (terms_on_new_line = false, color_top_level_sum = true, @@ -6780,9 +6770,10 @@ impl PythonPrimeTwoPolynomial { double_star_for_exponentiation = false, square_brackets_for_function = false, num_exp_as_superscript = true, - latex = false) + latex = false, + precision = None) )] - pub fn pretty_str( + pub fn format( &self, terms_on_new_line: bool, color_top_level_sum: bool, @@ -6796,56 +6787,48 @@ impl PythonPrimeTwoPolynomial { square_brackets_for_function: bool, num_exp_as_superscript: bool, latex: bool, + precision: Option, ) -> PyResult { - Ok(format!( - "{}", - PolynomialPrinter::new_with_options( - &self.poly, - PrintOptions { - terms_on_new_line, - color_top_level_sum, - color_builtin_symbols, - print_finite_field, - symmetric_representation_for_finite_field, - explicit_rational_polynomial, - number_thousands_separator, - multiplication_operator, - double_star_for_exponentiation, - square_brackets_for_function, - num_exp_as_superscript, - latex - }, - ) + Ok(self.poly.format_string( + &PrintOptions { + terms_on_new_line, + color_top_level_sum, + color_builtin_symbols, + print_finite_field, + symmetric_representation_for_finite_field, + explicit_rational_polynomial, + number_thousands_separator, + multiplication_operator, + double_star_for_exponentiation, + square_brackets_for_function, + num_exp_as_superscript, + latex, + precision, + }, + PrintState::new(), )) } /// Convert the polynomial into a portable string. pub fn __repr__(&self) -> PyResult { - Ok(format!( - "{}", - PolynomialPrinter { - poly: &self.poly, - opts: PrintOptions::file() - } - )) + Ok(self + .poly + .format_string(&PrintOptions::file(), PrintState::new())) } /// Print the polynomial in a human-readable format. pub fn __str__(&self) -> PyResult { - Ok(format!( - "{}", - PolynomialPrinter { - poly: &self.poly, - opts: PrintOptions::default() - } - )) + Ok(self + .poly + .format_string(&PrintOptions::default(), PrintState::new())) } /// Convert the polynomial into a LaTeX string. pub fn to_latex(&self) -> PyResult { Ok(format!( "$${}$$", - PolynomialPrinter::new_with_options(&self.poly, PrintOptions::latex(),) + self.poly + .format_string(&PrintOptions::latex(), PrintState::new()) )) } @@ -7314,7 +7297,7 @@ impl PythonGaloisFieldPrimeTwoPolynomial { /// Examples /// -------- /// >>> p = FiniteFieldPolynomial.parse("3*x^2+2*x+7*x^3", ['x'], 11) - /// >>> print(p.pretty_str(symmetric_representation_for_finite_field=True)) + /// >>> print(p.format(symmetric_representation_for_finite_field=True)) #[pyo3(signature = (terms_on_new_line = false, color_top_level_sum = true, @@ -7327,9 +7310,10 @@ impl PythonGaloisFieldPrimeTwoPolynomial { double_star_for_exponentiation = false, square_brackets_for_function = false, num_exp_as_superscript = true, - latex = false) + latex = false, + precision = None) )] - pub fn pretty_str( + pub fn format( &self, terms_on_new_line: bool, color_top_level_sum: bool, @@ -7343,56 +7327,48 @@ impl PythonGaloisFieldPrimeTwoPolynomial { square_brackets_for_function: bool, num_exp_as_superscript: bool, latex: bool, + precision: Option, ) -> PyResult { - Ok(format!( - "{}", - PolynomialPrinter::new_with_options( - &self.poly, - PrintOptions { - terms_on_new_line, - color_top_level_sum, - color_builtin_symbols, - print_finite_field, - symmetric_representation_for_finite_field, - explicit_rational_polynomial, - number_thousands_separator, - multiplication_operator, - double_star_for_exponentiation, - square_brackets_for_function, - num_exp_as_superscript, - latex - }, - ) + Ok(self.poly.format_string( + &PrintOptions { + terms_on_new_line, + color_top_level_sum, + color_builtin_symbols, + print_finite_field, + symmetric_representation_for_finite_field, + explicit_rational_polynomial, + number_thousands_separator, + multiplication_operator, + double_star_for_exponentiation, + square_brackets_for_function, + num_exp_as_superscript, + latex, + precision, + }, + PrintState::new(), )) } /// Convert the polynomial into a portable string. pub fn __repr__(&self) -> PyResult { - Ok(format!( - "{}", - PolynomialPrinter { - poly: &self.poly, - opts: PrintOptions::file() - } - )) + Ok(self + .poly + .format_string(&PrintOptions::file(), PrintState::new())) } /// Print the polynomial in a human-readable format. pub fn __str__(&self) -> PyResult { - Ok(format!( - "{}", - PolynomialPrinter { - poly: &self.poly, - opts: PrintOptions::default() - } - )) + Ok(self + .poly + .format_string(&PrintOptions::default(), PrintState::new())) } /// Convert the polynomial into a LaTeX string. pub fn to_latex(&self) -> PyResult { Ok(format!( "$${}$$", - PolynomialPrinter::new_with_options(&self.poly, PrintOptions::latex(),) + self.poly + .format_string(&PrintOptions::latex(), PrintState::new()) )) } @@ -7865,7 +7841,7 @@ impl PythonGaloisFieldPolynomial { /// Examples /// -------- /// >>> p = FiniteFieldPolynomial.parse("3*x^2+2*x+7*x^3", ['x'], 11) - /// >>> print(p.pretty_str(symmetric_representation_for_finite_field=True)) + /// >>> print(p.format(symmetric_representation_for_finite_field=True)) #[pyo3(signature = (terms_on_new_line = false, color_top_level_sum = true, @@ -7878,9 +7854,10 @@ impl PythonGaloisFieldPolynomial { double_star_for_exponentiation = false, square_brackets_for_function = false, num_exp_as_superscript = true, - latex = false) + latex = false, + precision = None) )] - pub fn pretty_str( + pub fn format( &self, terms_on_new_line: bool, color_top_level_sum: bool, @@ -7894,56 +7871,48 @@ impl PythonGaloisFieldPolynomial { square_brackets_for_function: bool, num_exp_as_superscript: bool, latex: bool, + precision: Option, ) -> PyResult { - Ok(format!( - "{}", - PolynomialPrinter::new_with_options( - &self.poly, - PrintOptions { - terms_on_new_line, - color_top_level_sum, - color_builtin_symbols, - print_finite_field, - symmetric_representation_for_finite_field, - explicit_rational_polynomial, - number_thousands_separator, - multiplication_operator, - double_star_for_exponentiation, - square_brackets_for_function, - num_exp_as_superscript, - latex - }, - ) + Ok(self.poly.format_string( + &PrintOptions { + terms_on_new_line, + color_top_level_sum, + color_builtin_symbols, + print_finite_field, + symmetric_representation_for_finite_field, + explicit_rational_polynomial, + number_thousands_separator, + multiplication_operator, + double_star_for_exponentiation, + square_brackets_for_function, + num_exp_as_superscript, + latex, + precision, + }, + PrintState::new(), )) } /// Convert the polynomial into a portable string. pub fn __repr__(&self) -> PyResult { - Ok(format!( - "{}", - PolynomialPrinter { - poly: &self.poly, - opts: PrintOptions::file() - } - )) + Ok(self + .poly + .format_string(&PrintOptions::file(), PrintState::new())) } /// Print the polynomial in a human-readable format. pub fn __str__(&self) -> PyResult { - Ok(format!( - "{}", - PolynomialPrinter { - poly: &self.poly, - opts: PrintOptions::default() - } - )) + Ok(self + .poly + .format_string(&PrintOptions::default(), PrintState::new())) } /// Convert the polynomial into a LaTeX string. pub fn to_latex(&self) -> PyResult { Ok(format!( "$${}$$", - PolynomialPrinter::new_with_options(&self.poly, PrintOptions::latex(),) + self.poly + .format_string(&PrintOptions::latex(), PrintState::new()) )) } @@ -8417,7 +8386,7 @@ impl PythonNumberFieldPolynomial { /// Examples /// -------- /// >>> p = FiniteFieldPolynomial.parse("3*x^2+2*x+7*x^3", ['x'], 11) - /// >>> print(p.pretty_str(symmetric_representation_for_finite_field=True)) + /// >>> print(p.format(symmetric_representation_for_finite_field=True)) #[pyo3(signature = (terms_on_new_line = false, color_top_level_sum = true, @@ -8430,9 +8399,10 @@ impl PythonNumberFieldPolynomial { double_star_for_exponentiation = false, square_brackets_for_function = false, num_exp_as_superscript = true, - latex = false) + latex = false, + precision = None) )] - pub fn pretty_str( + pub fn format( &self, terms_on_new_line: bool, color_top_level_sum: bool, @@ -8446,56 +8416,48 @@ impl PythonNumberFieldPolynomial { square_brackets_for_function: bool, num_exp_as_superscript: bool, latex: bool, + precision: Option, ) -> PyResult { - Ok(format!( - "{}", - PolynomialPrinter::new_with_options( - &self.poly, - PrintOptions { - terms_on_new_line, - color_top_level_sum, - color_builtin_symbols, - print_finite_field, - symmetric_representation_for_finite_field, - explicit_rational_polynomial, - number_thousands_separator, - multiplication_operator, - double_star_for_exponentiation, - square_brackets_for_function, - num_exp_as_superscript, - latex - }, - ) + Ok(self.poly.format_string( + &PrintOptions { + terms_on_new_line, + color_top_level_sum, + color_builtin_symbols, + print_finite_field, + symmetric_representation_for_finite_field, + explicit_rational_polynomial, + number_thousands_separator, + multiplication_operator, + double_star_for_exponentiation, + square_brackets_for_function, + num_exp_as_superscript, + latex, + precision, + }, + PrintState::new(), )) } /// Convert the polynomial into a portable string. pub fn __repr__(&self) -> PyResult { - Ok(format!( - "{}", - PolynomialPrinter { - poly: &self.poly, - opts: PrintOptions::file() - } - )) + Ok(self + .poly + .format_string(&PrintOptions::file(), PrintState::new())) } /// Print the polynomial in a human-readable format. pub fn __str__(&self) -> PyResult { - Ok(format!( - "{}", - PolynomialPrinter { - poly: &self.poly, - opts: PrintOptions::default() - } - )) + Ok(self + .poly + .format_string(&PrintOptions::default(), PrintState::new())) } /// Convert the polynomial into a LaTeX string. pub fn to_latex(&self) -> PyResult { Ok(format!( "$${}$$", - PolynomialPrinter::new_with_options(&self.poly, PrintOptions::latex(),) + self.poly + .format_string(&PrintOptions::latex(), PrintState::new()) )) } @@ -9021,33 +8983,24 @@ impl PythonRationalPolynomial { /// Convert the rational polynomial into a portable string. pub fn __repr__(&self) -> PyResult { - Ok(format!( - "{}", - RationalPolynomialPrinter { - poly: &self.poly, - opts: PrintOptions::file(), - add_parentheses: false, - } - )) + Ok(self + .poly + .format_string(&PrintOptions::file(), PrintState::new())) } /// Print the rational polynomial in a human-readable format. pub fn __str__(&self) -> PyResult { - Ok(format!( - "{}", - RationalPolynomialPrinter { - poly: &self.poly, - opts: PrintOptions::default(), - add_parentheses: false, - } - )) + Ok(self + .poly + .format_string(&PrintOptions::default(), PrintState::new())) } /// Convert the rational polynomial into a LaTeX string. pub fn to_latex(&self) -> PyResult { Ok(format!( "$${}$$", - RationalPolynomialPrinter::new_with_options(&self.poly, PrintOptions::latex(),) + self.poly + .format_string(&PrintOptions::latex(), PrintState::new()) )) } @@ -9347,33 +9300,24 @@ impl PythonFiniteFieldRationalPolynomial { /// Convert the rational polynomial into a portable string. pub fn __repr__(&self) -> PyResult { - Ok(format!( - "{}", - RationalPolynomialPrinter { - poly: &self.poly, - opts: PrintOptions::file(), - add_parentheses: false, - } - )) + Ok(self + .poly + .format_string(&PrintOptions::file(), PrintState::new())) } /// Print the rational polynomial in a human-readable format. pub fn __str__(&self) -> PyResult { - Ok(format!( - "{}", - RationalPolynomialPrinter { - poly: &self.poly, - opts: PrintOptions::default(), - add_parentheses: false, - } - )) + Ok(self + .poly + .format_string(&PrintOptions::default(), PrintState::new())) } /// Convert the rational polynomial into a LaTeX string. pub fn to_latex(&self) -> PyResult { Ok(format!( "$${}$$", - RationalPolynomialPrinter::new_with_options(&self.poly, PrintOptions::latex(),) + self.poly + .format_string(&PrintOptions::latex(), PrintState::new()) )) } @@ -10175,7 +10119,8 @@ impl PythonMatrix { pub fn to_latex(&self) -> PyResult { Ok(format!( "$${}$$", - MatrixPrinter::new_with_options(&self.matrix, PrintOptions::latex(),) + self.matrix + .format_string(&PrintOptions::latex(), PrintState::new()) )) } diff --git a/src/domains.rs b/src/domains.rs index ec254e0..f9ddc9f 100644 --- a/src/domains.rs +++ b/src/domains.rs @@ -8,13 +8,15 @@ 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; use crate::poly::Variable; -use crate::printer::PrintOptions; +use crate::printer::{PrintOptions, PrintState}; pub trait InternalOrdering { /// Compare two elements using an internal ordering. @@ -69,6 +71,27 @@ 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; + + fn format_string(&self, opts: &PrintOptions, state: PrintState) -> String { + let mut s = String::new(); + self.format(opts, state, &mut s) + .expect("Could not write to string"); + s + } +} + pub trait Ring: Clone + PartialEq + Eq + Hash + Debug + Display { type Element: Clone + PartialEq + Eq + Hash + InternalOrdering + Debug; @@ -95,13 +118,13 @@ pub trait Ring: Clone + PartialEq + Eq + Hash + Debug + Display { fn size(&self) -> Integer; fn sample(&self, rng: &mut impl rand::RngCore, range: (i64, i64)) -> Self::Element; - fn fmt_display( + fn format( &self, element: &Self::Element, opts: &PrintOptions, - in_product: bool, // can be used to add parentheses - f: &mut Formatter<'_>, - ) -> Result<(), Error>; + state: PrintState, + f: &mut W, + ) -> Result; fn printer<'a>(&'a self, element: &'a Self::Element) -> RingPrinter<'a, Self> { RingPrinter::new(self, element) @@ -124,7 +147,7 @@ pub struct RingPrinter<'a, R: Ring> { pub ring: &'a R, pub element: &'a R::Element, pub opts: PrintOptions, - pub in_product: bool, + pub state: PrintState, } impl<'a, R: Ring> RingPrinter<'a, R> { @@ -133,7 +156,7 @@ impl<'a, R: Ring> RingPrinter<'a, R> { ring, element, opts: PrintOptions::default(), - in_product: false, + state: PrintState::default(), } } } @@ -141,6 +164,296 @@ 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 - .fmt_display(self.element, &self.opts, self.in_product, f) + .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 6203f23..00e913d 100644 --- a/src/domains/algebraic_number.rs +++ b/src/domains/algebraic_number.rs @@ -9,7 +9,6 @@ use crate::{ factor::Factorize, gcd::PolynomialGCD, polynomial::MultivariatePolynomial, Exponent, Variable, }, - printer::PolynomialPrinter, }; use super::{ @@ -18,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. @@ -488,35 +487,14 @@ impl Ring for AlgebraicExtension { AlgebraicNumber { poly } } - fn fmt_display( + fn format( &self, element: &Self::Element, opts: &crate::printer::PrintOptions, - in_product: bool, // can be used to add parentheses - f: &mut std::fmt::Formatter<'_>, - ) -> Result<(), std::fmt::Error> { - if f.sign_plus() { - f.write_str("+")?; - } - - if in_product { - f.write_str("(")?; - } - - write!( - f, - "{}", - PolynomialPrinter { - poly: &element.poly, - opts: *opts, - } - )?; - - if in_product { - f.write_str(")")?; - } - - Ok(()) + state: crate::printer::PrintState, + f: &mut W, + ) -> Result { + element.poly.format(opts, state, f) } } diff --git a/src/domains/atom.rs b/src/domains/atom.rs index 8f3ece3..85da7dc 100644 --- a/src/domains/atom.rs +++ b/src/domains/atom.rs @@ -1,11 +1,11 @@ -use std::fmt::Write; - use crate::{ atom::{Atom, AtomView}, poly::Variable, }; -use super::{integer::Integer, Derivable, EuclideanDomain, Field, InternalOrdering, Ring}; +use super::{ + integer::Integer, Derivable, EuclideanDomain, Field, InternalOrdering, Ring, SelfRing, +}; use rand::Rng; @@ -126,32 +126,33 @@ impl Ring for AtomField { 0.into() } - fn fmt_display( + fn format( &self, element: &Self::Element, - _opts: &crate::printer::PrintOptions, - mut in_product: bool, // can be used to add parentheses - f: &mut std::fmt::Formatter<'_>, - ) -> Result<(), std::fmt::Error> { - if f.sign_plus() { - f.write_char('+')?; - } - - if !matches!(element.as_view(), AtomView::Add(_)) { - in_product = false; - } - - if in_product { - write!(f, "(")?; - } + opts: &crate::printer::PrintOptions, + state: crate::printer::PrintState, + f: &mut W, + ) -> Result { + element.as_view().format(f, opts, state) + } +} - std::fmt::Display::fmt(element, f)?; +impl SelfRing for Atom { + fn is_zero(&self) -> bool { + self.is_zero() + } - if in_product { - write!(f, ")")?; - } + fn is_one(&self) -> bool { + self.is_one() + } - Ok(()) + fn format( + &self, + opts: &crate::printer::PrintOptions, + state: crate::printer::PrintState, + f: &mut W, + ) -> Result { + self.as_view().format(f, opts, state) } } diff --git a/src/domains/factorized_rational_polynomial.rs b/src/domains/factorized_rational_polynomial.rs index b2b21fb..5a4e04a 100644 --- a/src/domains/factorized_rational_polynomial.rs +++ b/src/domains/factorized_rational_polynomial.rs @@ -1,7 +1,7 @@ use std::{ borrow::Cow, cmp::Ordering, - fmt::{Display, Error, Formatter, Write}, + fmt::{Display, Error}, marker::PhantomData, ops::{Add, Div, Mul, Neg, Sub}, sync::Arc, @@ -12,14 +12,14 @@ use crate::{ factor::Factorize, gcd::PolynomialGCD, polynomial::MultivariatePolynomial, Exponent, Variable, }, - printer::{FactorizedRationalPolynomialPrinter, PrintOptions}, + printer::{PrintOptions, PrintState}, }; 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)] @@ -147,6 +147,244 @@ impl FactorizedRationalPolynomial { } } +impl SelfRing for FactorizedRationalPolynomial { + 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 { + let has_numer_coeff = !self.numerator.ring.is_one(&self.numer_coeff); + let has_denom_coeff = !self.numerator.ring.is_one(&self.denom_coeff); + + if opts.explicit_rational_polynomial { + if state.in_sum { + f.write_char('+')?; + } + + if !R::is_zero(&self.numer_coeff) && has_numer_coeff { + f.write_char('[')?; + self.numerator + .ring + .format(&self.numer_coeff, opts, PrintState::new(), f)?; + f.write_str("]*")?; + } + + if self.denominators.is_empty() && !has_denom_coeff { + if self.numerator.is_zero() { + if state.in_sum { + f.write_str("+0")?; + } else { + f.write_char('0')?; + } + } else { + f.write_char('[')?; + self.numerator.format(opts, PrintState::new(), f)?; + f.write_str("]")?; + } + } else { + f.write_char('[')?; + self.numerator.format(opts, PrintState::new(), f)?; + + if has_denom_coeff { + f.write_char(',')?; + self.numerator + .ring + .format(&self.denom_coeff, opts, PrintState::new(), f)?; + f.write_str(",1")?; + } + + for (d, p) in &self.denominators { + f.write_char(',')?; + d.format(opts, PrintState::new(), f)?; + f.write_fmt(format_args!(",{}", p))?; + } + + f.write_char(']')?; + } + + return Ok(false); + } + + if R::is_zero(&self.numer_coeff) { + if state.in_sum { + f.write_str("+0")?; + } else { + f.write_char('0')?; + } + + return Ok(false); + } + + if self.denominators.is_empty() && !has_denom_coeff { + let write_par = has_numer_coeff && !self.numerator.is_one() && 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 has_numer_coeff { + state.in_product |= !self.numerator.is_one(); + self.numerator + .ring + .format(&self.numer_coeff, opts, state, f)?; + state.in_sum = false; + + if self.numerator.is_one() { + return Ok(false); + } + f.write_char(opts.multiplication_operator)?; + } + + if write_par { + self.numerator.format(opts, state, f)?; + f.write_char(')')?; + return Ok(false); + } else { + return self.numerator.format(opts, state, f); + } + } + + state.suppress_one = false; + + let write_par = 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 opts.latex { + if has_numer_coeff { + state.suppress_one = true; + state.in_product = true; + self.numerator + .ring + .format(&self.numer_coeff, opts, state, f)?; + state.suppress_one = false; + } + + f.write_str("\\frac{")?; + self.numerator.format(opts, PrintState::new(), f)?; + f.write_str("}{")?; + + if has_denom_coeff { + self.numerator.ring.format( + &self.denom_coeff, + opts, + state.step(false, !self.denominators.is_empty(), false), + f, + )?; + } + + for (d, p) in &self.denominators { + d.format(opts, state.step(false, true, *p != 1), f)?; + + if *p != 1 { + f.write_fmt(format_args!("^{}", p))?; + } + } + + f.write_char('}')?; + + if write_par { + f.write_char(')')?; + } + + return Ok(false); + } + + state.in_product = true; + if has_numer_coeff || self.numerator.is_one() { + self.numerator + .ring + .format(&self.numer_coeff, opts, state, f)?; + state.in_sum = false; + + if !self.numerator.is_one() { + f.write_char(opts.multiplication_operator)?; + } + } + + if !self.numerator.is_one() { + self.numerator.format(opts, state, f)?; + } + + f.write_char('/')?; + + if self.denominators.is_empty() { + return self.numerator.ring.format( + &self.denom_coeff, + opts, + state.step(false, true, false), + f, + ); + } + + state.in_product = has_denom_coeff || self.denominators.len() > 1; + + if state.in_product { + f.write_char('(')?; + } + + if has_denom_coeff { + self.numerator.ring.format( + &self.denom_coeff, + opts, + state.step(false, true, false), + f, + )?; + } + + for (i, (d, p)) in self.denominators.iter().enumerate() { + if has_denom_coeff || i > 0 { + f.write_char(opts.multiplication_operator)?; + } + + d.format(opts, state.step(false, true, *p != 1), f)?; + + if *p != 1 { + f.write_fmt(format_args!( + "{}{}", + if opts.double_star_for_exponentiation { + "**" + } else { + "^" + }, + p + ))?; + } + } + + if state.in_product { + f.write_char(')')?; + } + + if write_par { + f.write_char(')')?; + } + + Ok(false) + } +} + impl FromNumeratorAndFactorizedDenominator for FactorizedRationalPolynomial { @@ -491,7 +729,8 @@ where impl Display for FactorizedRationalPolynomial { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - FactorizedRationalPolynomialPrinter::new(self).fmt(f) + self.format(&PrintOptions::from_fmt(f), PrintState::from_fmt(f), f) + .map(|_| ()) } } @@ -614,25 +853,14 @@ where todo!("Sampling a polynomial is not possible yet") } - fn fmt_display( + fn format( &self, element: &Self::Element, opts: &PrintOptions, - in_product: bool, - f: &mut Formatter<'_>, - ) -> Result<(), Error> { - if f.sign_plus() { - f.write_char('+')?; - } - - f.write_fmt(format_args!( - "{}", - FactorizedRationalPolynomialPrinter { - poly: element, - opts: *opts, - add_parentheses: in_product - }, - )) + state: PrintState, + f: &mut W, + ) -> Result { + element.format(opts, state, f) } } diff --git a/src/domains/finite_field.rs b/src/domains/finite_field.rs index eb7e1c8..fe1cf82 100644 --- a/src/domains/finite_field.rs +++ b/src/domains/finite_field.rs @@ -6,7 +6,7 @@ use std::ops::{Deref, Neg}; use crate::domains::integer::Integer; use crate::poly::gcd::PolynomialGCD; use crate::poly::Variable::Temporary; -use crate::printer::PrintOptions; +use crate::printer::{PrintOptions, PrintState}; use super::algebraic_number::AlgebraicExtension; use super::integer::Z; @@ -402,17 +402,17 @@ impl Ring for Zp { FiniteFieldElement(r as u32) } - fn fmt_display( + fn format( &self, element: &Self::Element, opts: &PrintOptions, - _in_product: bool, - f: &mut Formatter<'_>, - ) -> Result<(), Error> { + state: PrintState, + f: &mut W, + ) -> Result { if opts.symmetric_representation_for_finite_field { - self.to_symmetric_integer(element).fmt(f) + Z.format(&self.to_symmetric_integer(element), opts, state, f) } else { - self.from_element(element).fmt(f) + Z.format(&self.from_element(element).into(), opts, state, f) } } } @@ -701,17 +701,17 @@ impl Ring for Zp64 { FiniteFieldElement(r as u64) } - fn fmt_display( + fn format( &self, element: &Self::Element, opts: &PrintOptions, - _in_product: bool, - f: &mut Formatter<'_>, - ) -> Result<(), Error> { + state: PrintState, + f: &mut W, + ) -> Result { if opts.symmetric_representation_for_finite_field { - self.to_symmetric_integer(element).fmt(f) + Z.format(&self.to_symmetric_integer(element), opts, state, f) } else { - self.from_element(element).fmt(f) + Z.format(&self.from_element(element).into(), opts, state, f) } } } @@ -970,17 +970,17 @@ impl Ring for FiniteField { rng.gen_range(0..2) } - fn fmt_display( + fn format( &self, element: &Self::Element, opts: &PrintOptions, - _in_product: bool, - f: &mut Formatter<'_>, - ) -> Result<(), Error> { + state: PrintState, + f: &mut W, + ) -> Result { if opts.symmetric_representation_for_finite_field { - self.to_symmetric_integer(element).fmt(f) + Z.format(&self.to_symmetric_integer(element), opts, state, f) } else { - self.from_element(element).fmt(f) + Z.format(&self.from_element(element).0.into(), opts, state, f) } } } @@ -1222,17 +1222,17 @@ impl Ring for FiniteField { r as u64 } - fn fmt_display( + fn format( &self, element: &Self::Element, opts: &PrintOptions, - _in_product: bool, - f: &mut Formatter<'_>, - ) -> Result<(), Error> { + state: PrintState, + f: &mut W, + ) -> Result { if opts.symmetric_representation_for_finite_field { - self.to_symmetric_integer(element).fmt(f) + Z.format(&self.to_symmetric_integer(element), opts, state, f) } else { - self.from_element(element).fmt(f) + Z.format(&self.from_element(element).0.into(), opts, state, f) } } } @@ -1426,17 +1426,17 @@ impl Ring for FiniteField { Z.sample(rng, range).symmetric_mod(&self.p) } - fn fmt_display( + fn format( &self, element: &Self::Element, opts: &PrintOptions, - _in_product: bool, - f: &mut Formatter<'_>, - ) -> Result<(), Error> { + state: PrintState, + f: &mut W, + ) -> Result { if opts.symmetric_representation_for_finite_field { - self.to_symmetric_integer(element).fmt(f) + Z.format(&self.to_symmetric_integer(element), opts, state, f) } else { - self.from_element(element).fmt(f) + Z.format(&self.from_element(element).into(), opts, state, f) } } } diff --git a/src/domains/float.rs b/src/domains/float.rs index dcb9385..6e683c5 100644 --- a/src/domains/float.rs +++ b/src/domains/float.rs @@ -11,7 +11,7 @@ use wide::{f64x2, f64x4}; use crate::domains::integer::Integer; -use super::{rational::Rational, EuclideanDomain, Field, InternalOrdering, Ring}; +use super::{rational::Rational, EuclideanDomain, Field, InternalOrdering, Ring, SelfRing}; use rug::{ ops::{CompleteRound, Pow}, Assign, Float as MultiPrecisionFloat, @@ -150,14 +150,26 @@ impl Ring fo } #[inline(always)] - fn fmt_display( + fn format( &self, element: &Self::Element, - _opts: &crate::printer::PrintOptions, - _in_product: bool, // can be used to add parentheses - f: &mut Formatter<'_>, - ) -> Result<(), fmt::Error> { - Display::fmt(element, f) + opts: &crate::printer::PrintOptions, + state: crate::printer::PrintState, + f: &mut W, + ) -> Result { + if let Some(p) = opts.precision { + if state.in_sum { + f.write_fmt(format_args!("{:+.*}", p, element))? + } else { + f.write_fmt(format_args!("{:.*}", p, element))? + } + } else if state.in_sum { + f.write_fmt(format_args!("{:+}", element))? + } else { + f.write_fmt(format_args!("{}", element))? + } + + Ok(false) } #[inline(always)] @@ -166,6 +178,74 @@ impl Ring fo } } +impl SelfRing for F64 { + #[inline(always)] + fn is_zero(&self) -> bool { + SingleFloat::is_zero(self) + } + + #[inline(always)] + fn is_one(&self) -> bool { + SingleFloat::is_one(self) + } + + #[inline(always)] + fn format( + &self, + opts: &crate::printer::PrintOptions, + state: crate::printer::PrintState, + f: &mut W, + ) -> Result { + if let Some(p) = opts.precision { + if state.in_sum { + f.write_fmt(format_args!("{:+.*}", p, self))? + } else { + f.write_fmt(format_args!("{:.*}", p, self))? + } + } else if state.in_sum { + f.write_fmt(format_args!("{:+}", self))? + } else { + f.write_fmt(format_args!("{}", self))? + } + + Ok(false) + } +} + +impl SelfRing for Float { + #[inline(always)] + fn is_zero(&self) -> bool { + SingleFloat::is_zero(self) + } + + #[inline(always)] + fn is_one(&self) -> bool { + SingleFloat::is_one(self) + } + + #[inline(always)] + fn format( + &self, + opts: &crate::printer::PrintOptions, + state: crate::printer::PrintState, + f: &mut W, + ) -> Result { + if let Some(p) = opts.precision { + if state.in_sum { + f.write_fmt(format_args!("{:+.*}", p, self))? + } else { + f.write_fmt(format_args!("{:.*}", p, self))? + } + } else if state.in_sum { + f.write_fmt(format_args!("{:+}", self))? + } else { + f.write_fmt(format_args!("{}", self))? + } + + Ok(false) + } +} + impl EuclideanDomain for FloatField { @@ -1050,11 +1130,19 @@ impl Display for Float { // the original float value may not be reconstructible // from this output if f.precision().is_none() { - f.write_fmt(format_args!( - "{0:.1$}", - self.0, - (self.0.prec() as f64 * LOG10_2).floor() as usize - )) + if f.sign_plus() { + f.write_fmt(format_args!( + "{0:+.1$}", + self.0, + (self.0.prec() as f64 * LOG10_2).floor() as usize + )) + } else { + f.write_fmt(format_args!( + "{0:.1$}", + self.0, + (self.0.prec() as f64 * LOG10_2).floor() as usize + )) + } } else { Display::fmt(&self.0, f) } diff --git a/src/domains/integer.rs b/src/domains/integer.rs index 93d5f59..5186715 100644 --- a/src/domains/integer.rs +++ b/src/domains/integer.rs @@ -11,7 +11,11 @@ use rug::{ Complete, Integer as MultiPrecisionInteger, }; -use crate::{printer::PrintOptions, tensors::matrix::Matrix, utils}; +use crate::{ + printer::{PrintOptions, PrintState}, + tensors::matrix::Matrix, + utils, +}; use super::{ finite_field::{ @@ -20,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] = [ @@ -1168,32 +1172,82 @@ impl Ring for IntegerRing { Integer::Natural(r) } - fn fmt_display( + fn format( &self, element: &Self::Element, opts: &PrintOptions, - _in_product: bool, - f: &mut Formatter<'_>, - ) -> Result<(), Error> { - if opts.explicit_rational_polynomial { - match element { - Integer::Natural(n) => n.fmt(f), - Integer::Double(n) => n.fmt(f), - Integer::Large(r) => { + state: PrintState, + f: &mut W, + ) -> 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 { + write!(f, "+")?; + return Ok(true); + } else { + write!(f, "")?; + return Ok(true); + } + } else if *n == -1 { + write!(f, "-")?; + return Ok(true); + } + } + + if state.in_sum { + write!(f, "{:+}", n)? + } else { + write!(f, "{}", n)? + } + } + Integer::Double(n) => { + if state.in_sum { + write!(f, "{:+}", n)? + } else { + write!(f, "{}", n)? + } + } + Integer::Large(r) => { + if opts.explicit_rational_polynomial { // 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()) - } else if f.sign_plus() { - write!(f, "+#{:X}", r) + write!(f, "-#{:X}", r.as_abs())? + } else if state.in_sum { + write!(f, "+#{:X}", r)? } else { - write!(f, "#{:X}", r) + write!(f, "#{:X}", r)? } + } else if state.in_sum { + write!(f, "{:+}", r)? + } else { + write!(f, "{}", r)? } } - } else { - element.fmt(f) } + + Ok(false) } } @@ -2180,14 +2234,20 @@ impl Ring for MultiPrecisionIntegerRing { MultiPrecisionInteger::from(r) } - fn fmt_display( + fn format( &self, element: &Self::Element, _opts: &PrintOptions, - _in_product: bool, - f: &mut Formatter<'_>, - ) -> Result<(), Error> { - element.fmt(f) + state: PrintState, + f: &mut W, + ) -> Result { + if state.in_sum { + write!(f, "{:+}", element)? + } else { + write!(f, "{}", element)? + } + + Ok(false) } } diff --git a/src/domains/rational.rs b/src/domains/rational.rs index 18ac498..0f74cca 100644 --- a/src/domains/rational.rs +++ b/src/domains/rational.rs @@ -1,12 +1,12 @@ use std::{ borrow::Cow, - fmt::{Display, Error, Formatter, Write}, + fmt::{Display, Error, Formatter}, ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}, }; use crate::{ poly::{gcd::LARGE_U32_PRIMES, polynomial::PolynomialRing, Exponent}, - printer::PrintOptions, + printer::{PrintOptions, PrintState}, }; use super::{ @@ -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. @@ -118,6 +118,13 @@ pub struct Fraction { } impl Fraction { + pub fn new(numerator: R::Element, denominator: R::Element) -> Fraction { + Fraction { + numerator, + denominator, + } + } + pub fn numerator(&self) -> R::Element { self.numerator.clone() } @@ -323,20 +330,119 @@ impl Ring for FractionField { } } - fn fmt_display( + fn format( &self, element: &Self::Element, opts: &PrintOptions, - _in_product: bool, - f: &mut Formatter<'_>, - ) -> Result<(), Error> { - self.ring.fmt_display(&element.numerator, opts, true, f)?; - if !self.ring.is_one(&element.denominator) { + mut state: PrintState, + f: &mut W, + ) -> Result { + let has_denom = !self.ring.is_one(&element.denominator); + + 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.ring.format( + &element.numerator, + 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.ring.fmt_display(&element.denominator, opts, true, f)?; + self.ring + .format(&element.denominator, opts, state.step(false, true, true), f)?; } - Ok(()) + if write_par { + f.write_char(')')?; + } + + 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(|_| ()) } } @@ -423,17 +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 a8e2a5a..ee95ee1 100644 --- a/src/domains/rational_polynomial.rs +++ b/src/domains/rational_polynomial.rs @@ -1,7 +1,7 @@ use std::{ borrow::Cow, cmp::Ordering, - fmt::{Display, Error, Formatter, Write}, + fmt::{Display, Error}, marker::PhantomData, ops::{Add, Div, Mul, Neg, Sub}, sync::Arc, @@ -14,14 +14,14 @@ use crate::{ factor::Factorize, gcd::PolynomialGCD, polynomial::MultivariatePolynomial, univariate::UnivariatePolynomial, Exponent, Variable, }, - printer::{PrintOptions, RationalPolynomialPrinter}, + printer::{PrintOptions, PrintState}, }; 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)] @@ -165,6 +165,85 @@ impl RationalPolynomial { } } +impl SelfRing for RationalPolynomial { + fn is_zero(&self) -> bool { + self.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 { + if opts.explicit_rational_polynomial { + if state.in_sum { + f.write_char('+')?; + } + + if self.denominator.is_one() { + if self.numerator.is_zero() { + f.write_char('0')?; + } else { + f.write_char('[')?; + self.numerator.format(opts, PrintState::new(), f)?; + f.write_char(']')?; + } + } else { + f.write_char('[')?; + self.numerator.format(opts, PrintState::new(), f)?; + f.write_char(',')?; + self.denominator.format(opts, PrintState::new(), f)?; + f.write_char(']')?; + } + + return Ok(false); + } + + if self.denominator.is_one() { + self.numerator.format(opts, state, f) + } else { + let write_par = 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 opts.latex { + if state.in_sum { + f.write_char('+')?; + } + f.write_str("\\frac{")?; + self.numerator.format(opts, PrintState::new(), f)?; + f.write_str("}{")?; + self.denominator.format(opts, PrintState::new(), f)?; + f.write_str("}")?; + } else { + state.suppress_one = false; + self.numerator + .format(opts, state.step(state.in_sum, true, false), f)?; + f.write_char('/')?; + self.denominator + .format(opts, state.step(false, false, true), f)?; + } + + if write_par { + f.write_char(')')?; + } + Ok(false) + } + } +} + impl FromNumeratorAndDenominator for RationalPolynomial { @@ -434,8 +513,8 @@ where ) -> RationalPolynomial { if f.is_zero() { return RationalPolynomial { - numerator: MultivariatePolynomial::new_zero(&f.field.ring), - denominator: MultivariatePolynomial::new_one(&f.field.ring), + numerator: MultivariatePolynomial::new_zero(&f.ring.ring), + denominator: MultivariatePolynomial::new_one(&f.ring.ring), }; } @@ -456,7 +535,7 @@ where }); let mut res = - RationalPolynomial::new(&f.field.ring, f.coefficients[0].get_variables().clone()); + RationalPolynomial::new(&f.ring.ring, f.coefficients[0].get_variables().clone()); let mut exp = vec![E::zero(); f.coefficients[0].get_variables().len()]; exp[pos] = E::one(); @@ -473,7 +552,8 @@ where impl Display for RationalPolynomial { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - RationalPolynomialPrinter::new(self).fmt(f) + self.format(&PrintOptions::from_fmt(f), PrintState::from_fmt(f), f) + .map(|_| ()) } } @@ -593,25 +673,14 @@ where todo!("Sampling a polynomial is not possible yet") } - fn fmt_display( + fn format( &self, element: &Self::Element, opts: &PrintOptions, - in_product: bool, - f: &mut Formatter<'_>, - ) -> Result<(), Error> { - if f.sign_plus() { - f.write_char('+')?; - } - - f.write_fmt(format_args!( - "{}", - RationalPolynomialPrinter { - poly: element, - opts: *opts, - add_parentheses: in_product - }, - )) + state: PrintState, + f: &mut W, + ) -> Result { + element.format(opts, state, f) } } @@ -1034,7 +1103,7 @@ where d_exp[i - 1] = d_exp[i - 1].clone() + s_cor - + t_cor.derivative().div_coeff(&(t_cor.field.nth(i as u64))); + + t_cor.derivative().div_coeff(&(t_cor.ring.nth(i as u64))); let t_full = Self::from_univariate(t_cor); @@ -1117,7 +1186,7 @@ where .to_multivariate_polynomial_list(&[var, new_var], true); let mut bivar_poly = MultivariatePolynomial::new( - &p.field, + &p.ring, Some(ll.len()), p.coefficients[0].get_variables().clone(), ); @@ -1128,7 +1197,7 @@ where // convert defining polynomial to a univariate polynomial in t with rational polynomial coefficients let def_uni = sqf .to_univariate(new_var) - .map_coeff(|c| c.clone().into(), p.field.clone()); + .map_coeff(|c| c.clone().into(), p.ring.clone()); // write the polynomial in x and t as a polynomial in x with rational polynomial coefficients in t and // all other variables and solve a diophantine equation @@ -1158,7 +1227,7 @@ where let monic = bivar_poly_scaled.rem(&def_biv); // convert the result to a multivariate rational polynomial - let mut res = p.field.zero(); + let mut res = p.ring.zero(); for t in &monic { let mut exp = vec![E::zero(); p.lcoeff().numerator.nvars()]; exp.copy_from_slice(t.exponents); @@ -1181,7 +1250,7 @@ where let eval = monic.replace(new_var, &sol); - let mut res = p.field.zero(); + let mut res = p.ring.zero(); for t in &eval { let mut exp = vec![E::zero(); p.lcoeff().numerator.nvars()]; exp.copy_from_slice(t.exponents); 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 46fadad..275a6b3 100755 --- a/src/poly/polynomial.rs +++ b/src/poly/polynomial.rs @@ -11,8 +11,8 @@ 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::printer::{PolynomialPrinter, PrintOptions}; +use crate::domains::{Derivable, EuclideanDomain, Field, InternalOrdering, Ring, SelfRing}; +use crate::printer::{PrintOptions, PrintState}; use super::gcd::PolynomialGCD; use super::univariate::UnivariatePolynomial; @@ -149,35 +149,14 @@ impl Ring for PolynomialRing { todo!("Sampling a polynomial is not possible yet") } - fn fmt_display( + fn format( &self, element: &Self::Element, opts: &PrintOptions, - in_product: bool, - f: &mut std::fmt::Formatter<'_>, - ) -> Result<(), std::fmt::Error> { - if f.sign_plus() { - f.write_str("+")?; - } - - if in_product { - f.write_str("(")?; - } - - write!( - f, - "{}", - PolynomialPrinter { - poly: element, - opts: *opts, - } - )?; - - if in_product { - f.write_str(")")?; - } - - Ok(()) + state: PrintState, + f: &mut W, + ) -> Result { + element.format(opts, state, f) } } @@ -730,6 +709,114 @@ impl MultivariatePolynomial { } } +impl SelfRing for MultivariatePolynomial { + #[inline] + fn is_zero(&self) -> bool { + self.is_zero() + } + + #[inline] + fn is_one(&self) -> bool { + self.is_one() + } + + fn format( + &self, + opts: &PrintOptions, + mut state: PrintState, + f: &mut W, + ) -> Result { + if self.is_constant() { + if self.is_zero() { + if state.in_sum { + f.write_str("+")?; + } + f.write_char('0')?; + return Ok(false); + } else { + return self.ring.format(&self.coefficients[0], opts, state, f); + } + } + + let add_paren = self.nterms() > 1 && state.in_product + || (state.in_exp + && (self.nterms() > 1 + || self.exponents(0).iter().filter(|e| **e > E::zero()).count() > 1 + || !self.ring.is_one(&self.coefficients[0]))); + + if add_paren { + if state.in_sum { + f.write_str("+")?; + state.in_sum = false; + } + + state.in_product = false; + state.in_exp = false; + f.write_str("(")?; + } + let in_product = state.in_product; + + let var_map: Vec = self + .variables + .as_ref() + .iter() + .map(|v| { + v.to_string_with_state(PrintState { + in_exp: true, + ..state + }) + }) + .collect(); + + for monomial in self { + let has_var = monomial.exponents.iter().any(|e| !e.is_zero()); + state.in_product = in_product || has_var; + state.suppress_one = has_var; // any products before should not be considered + + let mut suppressed_one = self.ring.format(monomial.coefficient, opts, state, f)?; + + for (var_id, e) in var_map.iter().zip(monomial.exponents) { + if e.is_zero() { + continue; + } + if suppressed_one { + suppressed_one = false; + } else if !opts.latex { + f.write_char(opts.multiplication_operator)?; + } + + f.write_str(var_id)?; + + if e.to_u32() != 1 { + if opts.latex { + write!(f, "^{{{}}}", e)?; + } else if opts.double_star_for_exponentiation { + write!(f, "**{}", e)?; + } else { + write!(f, "^{}", e)?; + } + } + } + + state.in_sum = true; + } + + if self.is_zero() { + f.write_char('0')?; + } + + if opts.print_finite_field { + f.write_fmt(format_args!("{}", self.ring))?; + } + + if add_paren { + f.write_str(")")?; + } + + Ok(false) + } +} + impl std::fmt::Debug for MultivariatePolynomial { @@ -757,7 +844,8 @@ impl impl Display for MultivariatePolynomial { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - PolynomialPrinter::new(self).fmt(f) + self.format(&PrintOptions::from_fmt(f), PrintState::from_fmt(f), f) + .map(|_| ()) } } @@ -1702,7 +1790,7 @@ impl MultivariatePolynomial { return p; } - p.coefficients = vec![p.field.zero(); self.degree(var).to_u32() as usize + 1]; + p.coefficients = vec![p.ring.zero(); self.degree(var).to_u32() as usize + 1]; for (q, e) in self.coefficients.iter().zip(self.exponents_iter()) { p.coefficients[e[var].to_u32() as usize] = q.clone(); } diff --git a/src/poly/resultant.rs b/src/poly/resultant.rs index 993aef7..72cb574 100644 --- a/src/poly/resultant.rs +++ b/src/poly/resultant.rs @@ -7,24 +7,24 @@ impl UnivariatePolynomial { pub fn resultant_prs(&self, other: &Self) -> F::Element { if self.degree() < other.degree() { if self.degree() % 2 == 1 && other.degree() % 2 == 1 { - return self.field.neg(&other.resultant_prs(self)); + return self.ring.neg(&other.resultant_prs(self)); } else { return other.resultant_prs(self); } } if other.is_constant() { - return self.field.pow(&other.get_constant(), self.degree() as u64); + return self.ring.pow(&other.get_constant(), self.degree() as u64); } let mut a = self.clone(); let mut a_new = other.clone(); let mut deg = a.degree() as u64 - a_new.degree() as u64; - let mut neg_lc = self.field.one(); //unused + let mut neg_lc = self.ring.one(); //unused let mut init = false; - let mut beta = self.field.pow(&self.field.neg(&self.field.one()), deg + 1); - let mut psi = self.field.neg(&self.field.one()); + let mut beta = self.ring.pow(&self.ring.neg(&self.ring.one()), deg + 1); + let mut psi = self.ring.neg(&self.ring.one()); let mut lcs = vec![(a.lcoeff(), a.degree() as u64)]; while !a_new.is_constant() { @@ -35,23 +35,23 @@ impl UnivariatePolynomial { } else if deg == 1 { neg_lc.clone() } else { - let a = self.field.pow(&neg_lc, deg); - let psi_old = self.field.pow(&psi, deg - 1); - let (q, r) = self.field.quot_rem(&a, &psi_old); + let a = self.ring.pow(&neg_lc, deg); + let psi_old = self.ring.pow(&psi, deg - 1); + let (q, r) = self.ring.quot_rem(&a, &psi_old); debug_assert!(F::is_zero(&r)); q }; deg = a.degree() as u64 - a_new.degree() as u64; - beta = self.field.mul(&neg_lc, &self.field.pow(&psi, deg)); + beta = self.ring.mul(&neg_lc, &self.ring.pow(&psi, deg)); } else { init = true; } - neg_lc = self.field.neg(a_new.coefficients.last().unwrap()); + neg_lc = self.ring.neg(a_new.coefficients.last().unwrap()); let (_, mut r) = a - .mul_coeff(&self.field.pow(&neg_lc, deg + 1)) + .mul_coeff(&self.ring.pow(&neg_lc, deg + 1)) .quot_rem(&a_new); if (deg + 1) % 2 == 1 { r = -r; @@ -65,12 +65,12 @@ impl UnivariatePolynomial { lcs.push((a_new.lcoeff(), 0)); if a_new.is_zero() { - return self.field.zero(); + return self.ring.zero(); } // compute the resultant from the PRS, using the fundamental theorem - let mut rho = self.field.one(); - let mut den = self.field.one(); + let mut rho = self.ring.one(); + let mut den = self.ring.one(); for k in 1..lcs.len() { let mut deg = lcs[k as usize - 1].1 as i64 - lcs[k as usize].1 as i64; for l in k..lcs.len() - 1 { @@ -78,22 +78,22 @@ impl UnivariatePolynomial { } if deg > 0 { - self.field - .mul_assign(&mut rho, &self.field.pow(&lcs[k].0, deg as u64)); + self.ring + .mul_assign(&mut rho, &self.ring.pow(&lcs[k].0, deg as u64)); } else if deg < 0 { - self.field - .mul_assign(&mut den, &self.field.pow(&lcs[k].0, (-deg) as u64)); + self.ring + .mul_assign(&mut den, &self.ring.pow(&lcs[k].0, (-deg) as u64)); } } - self.field.quot_rem(&rho, &den).0 + self.ring.quot_rem(&rho, &den).0 } /// Compute the resultant using a primitive polynomial remainder sequence. pub fn resultant_primitive(&self, other: &Self) -> F::Element { if self.degree() < other.degree() { if self.degree() % 2 == 1 && other.degree() % 2 == 1 { - return self.field.neg(&other.resultant_primitive(self)); + return self.ring.neg(&other.resultant_primitive(self)); } else { return other.resultant_primitive(self); } @@ -104,11 +104,11 @@ impl UnivariatePolynomial { let mut v = vec![a.degree()]; let mut c = vec![a.lcoeff()]; - let mut ab = vec![(self.field.one(), self.field.one())]; + let mut ab = vec![(self.ring.one(), self.ring.one())]; while !a_new.is_constant() { let n = a.degree() as u64 + 1 - a_new.degree() as u64; - let alpha = self.field.pow(&a_new.lcoeff(), n); + let alpha = self.ring.pow(&a_new.lcoeff(), n); let (_, mut r) = a.clone().mul_coeff(&alpha).quot_rem(&a_new); @@ -132,27 +132,27 @@ impl UnivariatePolynomial { sign += w[0] * w[1]; } - let mut res = self.field.pow(&r, *v.last().unwrap() as u64); + let mut res = self.ring.pow(&r, *v.last().unwrap() as u64); if sign % 2 == 1 { - res = self.field.neg(&res); + res = self.ring.neg(&res); }; v.push(0); - let mut num = self.field.one(); - let mut den = self.field.one(); + let mut num = self.ring.one(); + let mut den = self.ring.one(); for i in 1..c.len() { - self.field.mul_assign( + self.ring.mul_assign( &mut res, - &self.field.pow(&c[i], v[i - 1] as u64 - v[i + 1] as u64), + &self.ring.pow(&c[i], v[i - 1] as u64 - v[i + 1] as u64), ); - self.field - .mul_assign(&mut num, &self.field.pow(&ab[i].1, v[i] as u64)); - self.field - .mul_assign(&mut den, &self.field.pow(&ab[i].0, v[i] as u64)); + self.ring + .mul_assign(&mut num, &self.ring.pow(&ab[i].1, v[i] as u64)); + self.ring + .mul_assign(&mut den, &self.ring.pow(&ab[i].0, v[i] as u64)); } - let (q, r) = self.field.quot_rem(&self.field.mul(&num, &res), &den); + let (q, r) = self.ring.quot_rem(&self.ring.mul(&num, &res), &den); assert!(F::is_zero(&r)); q } @@ -185,16 +185,16 @@ impl UnivariatePolynomial { sign += w[0] * w[1]; } - let mut res = self.field.pow(&r, *v.last().unwrap() as u64); + let mut res = self.ring.pow(&r, *v.last().unwrap() as u64); if sign % 2 == 1 { - res = self.field.neg(&res); + res = self.ring.neg(&res); }; v.push(0); for i in 1..c.len() { - self.field.mul_assign( + self.ring.mul_assign( &mut res, - &self.field.pow(&c[i], v[i - 1] as u64 - v[i + 1] as u64), + &self.ring.pow(&c[i], v[i - 1] as u64 - v[i + 1] as u64), ); } diff --git a/src/poly/series.rs b/src/poly/series.rs index 0258eb5..6bbafea 100644 --- a/src/poly/series.rs +++ b/src/poly/series.rs @@ -9,10 +9,12 @@ use crate::{ atom::{Atom, AtomView, FunctionBuilder}, coefficient::CoefficientView, domains::{ - atom::AtomField, integer::Integer, rational::Rational, EuclideanDomain, InternalOrdering, - Ring, RingPrinter, + atom::AtomField, + integer::Integer, + rational::{Rational, Q}, + EuclideanDomain, InternalOrdering, Ring, }, - printer::PrintOptions, + printer::{PrintOptions, PrintState}, state::State, }; @@ -53,60 +55,8 @@ impl std::fmt::Debug for Series { impl std::fmt::Display for Series { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - let v = self.variable.to_string(); - - if self.coefficients.is_empty() { - return write!(f, "𝒪({}^{})", v, self.absolute_order()); - } - - let mut first = true; - for (e, c) in self.coefficients.iter().enumerate() { - if F::is_zero(c) { - continue; - } - - if first { - first = false; - } else { - write!(f, "+")?; - } - - let p = RingPrinter { - element: c, - ring: &self.field, - opts: PrintOptions::default(), - in_product: true, - }; - - let e = self.get_exponent(e); - - if e.is_zero() { - write!(f, "{}", p)?; - } else if e.is_one() { - if self.field.is_one(c) { - write!(f, "{}", v)?; - } else { - write!(f, "{}*{}", p, v)?; - } - } else if self.field.is_one(c) { - if e.is_integer() { - write!(f, "{}^{}", v, e)?; - } else { - write!(f, "{}^({})", v, e)?; - } - } else if e.is_integer() { - write!(f, "{}*{}^{}", p, v, e)?; - } else { - write!(f, "{}*{}^({})", p, v, e)?; - } - } - - let o = self.absolute_order(); - if o.is_integer() { - write!(f, "+𝒪({}^{})", v, o) - } else { - write!(f, "+𝒪({}^({}))", v, o) - } + self.format(&PrintOptions::from_fmt(f), PrintState::from_fmt(f), f) + .map(|_| ()) } } @@ -515,6 +465,78 @@ impl Series { self } + + pub fn format( + &self, + opts: &PrintOptions, + mut state: PrintState, + f: &mut W, + ) -> Result { + let v = self.variable.to_string_with_state(PrintState { + in_exp: true, + ..state + }); + + if self.coefficients.is_empty() { + write!(f, "𝒪({}^{})", v, self.absolute_order())?; + return Ok(false); + } + + let add_paren = state.in_product || state.in_exp; + if add_paren { + if state.in_sum { + f.write_str("+")?; + state.in_sum = false; + } + + state.in_product = false; + state.in_exp = false; + f.write_str("(")?; + } + + for (e, c) in self.coefficients.iter().enumerate() { + if F::is_zero(c) { + continue; + } + + let e = self.get_exponent(e); + + state.suppress_one = !e.is_zero(); + let suppressed_one = self.field.format( + c, + opts, + state.step(state.in_sum, state.in_product, false), + f, + )?; + + if !suppressed_one && !e.is_zero() { + f.write_char(opts.multiplication_operator)?; + } + + if e.is_one() { + write!(f, "{}", v)?; + } else if !e.is_zero() { + write!(f, "{}^", v)?; + Q.format(&e, opts, state.step(false, false, true), f)?; + } + + state.in_sum = true; + state.in_product = true; + } + + let o = self.absolute_order(); + if o.is_integer() { + write!(f, "+𝒪({}^{})", v, o)?; + } else { + write!(f, "+𝒪({}^({}))", v, o)?; + } + + if add_paren { + f.write_str(")")?; + } + + Ok(false) + } } impl PartialEq for Series { diff --git a/src/poly/univariate.rs b/src/poly/univariate.rs index cbce6f3..aabd824 100644 --- a/src/poly/univariate.rs +++ b/src/poly/univariate.rs @@ -9,9 +9,9 @@ use crate::{ float::{Complex, FloatField, NumericalFloatLike, Real, SingleFloat}, integer::{Integer, IntegerRing, Z}, rational::{Rational, RationalField, Q}, - EuclideanDomain, Field, InternalOrdering, Ring, RingPrinter, + EuclideanDomain, Field, InternalOrdering, Ring, SelfRing, }, - printer::PrintOptions, + printer::{PrintOptions, PrintState}, }; use super::{ @@ -36,7 +36,7 @@ impl UnivariatePolynomialRing { pub fn new_from_poly(poly: &UnivariatePolynomial) -> UnivariatePolynomialRing { UnivariatePolynomialRing { - ring: poly.field.clone(), + ring: poly.ring.clone(), variable: poly.variable.clone(), } } @@ -127,28 +127,14 @@ impl Ring for UnivariatePolynomialRing { todo!("Sampling a polynomial is not possible yet") } - fn fmt_display( + fn format( &self, element: &Self::Element, - _opts: &PrintOptions, - in_product: bool, - f: &mut std::fmt::Formatter<'_>, - ) -> Result<(), std::fmt::Error> { - if f.sign_plus() { - f.write_str("+")?; - } - - if in_product { - f.write_str("(")?; - } - - ::fmt(element, f)?; - - if in_product { - f.write_str(")")?; - } - - Ok(()) + opts: &PrintOptions, + state: PrintState, + f: &mut W, + ) -> Result { + element.format(opts, state, f) } } @@ -171,7 +157,7 @@ impl EuclideanDomain for UnivariatePolynomialRing { pub struct UnivariatePolynomial { pub coefficients: Vec, pub variable: Arc, - pub field: F, + pub ring: F, } impl InternalOrdering for UnivariatePolynomial { @@ -202,46 +188,8 @@ impl std::fmt::Debug for UnivariatePolynomial { impl std::fmt::Display for UnivariatePolynomial { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - if self.is_zero() { - return write!(f, "0"); - } - - let v = self.variable.to_string(); - - let mut first = true; - for (e, c) in self.coefficients.iter().enumerate() { - if F::is_zero(c) { - continue; - } - - if first { - first = false; - } else { - write!(f, "+")?; - } - - let p = RingPrinter { - element: c, - ring: &self.field, - opts: PrintOptions::default(), - in_product: true, - }; - - if e == 0 { - write!(f, "{}", p)?; - } else if e == 1 { - if self.field.is_one(c) { - write!(f, "{}", v)?; - } else { - write!(f, "{}*{}", p, v)?; - } - } else if self.field.is_one(c) { - write!(f, "{}^{}", v, e)?; - } else { - write!(f, "{}*{}^{}", p, v, e)?; - } - } - Ok(()) + self.format(&PrintOptions::from_fmt(f), PrintState::from_fmt(f), f) + .map(|_| ()) } } @@ -253,7 +201,7 @@ impl UnivariatePolynomial { pub fn new(field: &F, cap: Option, variable: Arc) -> Self { Self { coefficients: Vec::with_capacity(cap.unwrap_or(0)), - field: field.clone(), + ring: field.clone(), variable, } } @@ -263,7 +211,7 @@ impl UnivariatePolynomial { pub fn zero(&self) -> Self { Self { coefficients: vec![], - field: self.field.clone(), + ring: self.ring.clone(), variable: self.variable.clone(), } } @@ -274,7 +222,7 @@ impl UnivariatePolynomial { pub fn zero_with_capacity(&self, cap: usize) -> Self { Self { coefficients: Vec::with_capacity(cap), - field: self.field.clone(), + ring: self.ring.clone(), variable: self.variable.clone(), } } @@ -289,7 +237,7 @@ impl UnivariatePolynomial { Self { coefficients: vec![coeff], - field: self.field.clone(), + ring: self.ring.clone(), variable: self.variable.clone(), } } @@ -298,8 +246,8 @@ impl UnivariatePolynomial { #[inline] pub fn one(&self) -> Self { Self { - coefficients: vec![self.field.one()], - field: self.field.clone(), + coefficients: vec![self.ring.one()], + ring: self.ring.clone(), variable: self.variable.clone(), } } @@ -311,12 +259,12 @@ impl UnivariatePolynomial { return self.zero(); } - let mut coefficients = vec![self.field.zero(); exponent + 1]; + let mut coefficients = vec![self.ring.zero(); exponent + 1]; coefficients[exponent] = coeff; Self { coefficients, - field: self.field.clone(), + ring: self.ring.clone(), variable: self.variable.clone(), } } @@ -328,7 +276,7 @@ impl UnivariatePolynomial { #[inline] pub fn is_one(&self) -> bool { - self.coefficients.len() == 1 && self.field.is_one(&self.coefficients[0]) + self.coefficients.len() == 1 && self.ring.is_one(&self.coefficients[0]) } /// Returns true if the polynomial is constant. @@ -341,7 +289,7 @@ impl UnivariatePolynomial { #[inline] pub fn get_constant(&self) -> F::Element { if self.is_zero() { - return self.field.zero(); + return self.ring.zero(); } self.coefficients[0].clone() @@ -361,7 +309,7 @@ impl UnivariatePolynomial { pub fn lcoeff(&self) -> F::Element { self.coefficients .last() - .unwrap_or(&self.field.zero()) + .unwrap_or(&self.ring.zero()) .clone() } @@ -400,7 +348,7 @@ impl UnivariatePolynomial { } let mut a = self.zero(); - a.coefficients = vec![self.field.zero(); self.degree() + exp + 1]; + a.coefficients = vec![self.ring.zero(); self.degree() + exp + 1]; for (cn, c) in a.coefficients.iter_mut().skip(exp).zip(&self.coefficients) { *cn = c.clone(); @@ -420,7 +368,7 @@ impl UnivariatePolynomial { return a; } - a.coefficients = vec![self.field.zero(); self.degree() - exp + 1]; + a.coefficients = vec![self.ring.zero(); self.degree() - exp + 1]; for (cn, c) in a .coefficients @@ -436,7 +384,7 @@ impl UnivariatePolynomial { pub fn mul_coeff(mut self, coeff: &F::Element) -> Self { for c in &mut self.coefficients { if !F::is_zero(c) { - self.field.mul_assign(c, coeff); + self.ring.mul_assign(c, coeff); } } @@ -475,9 +423,9 @@ impl UnivariatePolynomial { let mut res = self.coefficients.last().unwrap().clone(); for c in self.coefficients.iter().rev().skip(1) { if !F::is_zero(c) { - res = self.field.add(&self.field.mul(&res, x), c); + res = self.ring.add(&self.ring.mul(&res, x), c); } else { - self.field.mul_assign(&mut res, x); + self.ring.mul_assign(&mut res, x); } } @@ -492,7 +440,7 @@ impl UnivariatePolynomial { let mut res = self.zero(); res.coefficients - .resize(self.coefficients.len() - 1, self.field.zero()); + .resize(self.coefficients.len() - 1, self.ring.zero()); for (p, (nc, oc)) in res .coefficients @@ -501,7 +449,7 @@ impl UnivariatePolynomial { .enumerate() { if !F::is_zero(oc) { - *nc = self.field.mul(oc, &self.field.nth(p as u64 + 1)); + *nc = self.ring.mul(oc, &self.ring.nth(p as u64 + 1)); } } @@ -511,7 +459,7 @@ impl UnivariatePolynomial { /// Convert from a univariate polynomial to a multivariate polynomial. pub fn to_multivariate(self) -> MultivariatePolynomial { let mut res = MultivariatePolynomial::new( - &self.field, + &self.ring, self.degree().into(), Arc::new(vec![self.variable.as_ref().clone()]), ); @@ -532,7 +480,7 @@ impl UnivariatePolynomial { for k in 0..d { for j in (k..d).rev() { let (s, c) = poly.coefficients.split_at_mut(j + 1); - self.field.add_mul_assign(&mut s[j], &c[0], shift); + self.ring.add_mul_assign(&mut s[j], &c[0], shift); } } @@ -540,6 +488,101 @@ impl UnivariatePolynomial { } } +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 { + if self.is_constant() { + if self.is_zero() { + if state.in_sum { + f.write_str("+")?; + } + f.write_char('0')?; + return Ok(false); + } else { + return self.ring.format(&self.coefficients[0], opts, state, f); + } + } + + 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 + && (non_zero > 1 + || self + .coefficients + .iter() + .filter(|c| !self.ring.is_one(c)) + .count() + > 0)); + + if add_paren { + if state.in_sum { + f.write_str("+")?; + state.in_sum = false; + } + + state.in_product = false; + state.in_exp = false; + f.write_str("(")?; + } + + 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; + + if F::is_zero(c) { + continue; + } + + let suppressed_one = self.ring.format( + c, + opts, + state.step(state.in_sum, state.in_product, false), + f, + )?; + + if !suppressed_one && e > 0 { + f.write_char(opts.multiplication_operator)?; + } + + 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 add_paren { + f.write_str(")")?; + } + + Ok(false) + } +} + impl UnivariatePolynomial { /// Isolate the real roots of the polynomial. The result is a list of intervals with rational bounds that contain exactly one root, /// and the multiplicity of that root. @@ -549,7 +592,7 @@ impl UnivariatePolynomial { let stripped = self.map_coeff( |coeff| { - let coeff = self.field.div(coeff, &c); + let coeff = self.ring.div(coeff, &c); debug_assert!(coeff.is_integer()); coeff.numerator() }, @@ -1023,7 +1066,7 @@ impl R { if self.is_zero() { - return self.field.zero().re; + return self.ring.zero().re; } let last = self.coefficients.last().unwrap(); @@ -1035,13 +1078,13 @@ impl R { if self.is_zero() { - return self.field.zero().re; + return self.ring.zero().re; } let last = &self.coefficients[0]; @@ -1053,7 +1096,7 @@ impl { - roots.push(self.field.zero()); + roots.push(self.ring.zero()); return Ok(roots); } Err(mut roots) => { - roots.push(self.field.zero()); + roots.push(self.ring.zero()); return Err(roots); } } @@ -1180,7 +1223,7 @@ impl Add for UnivariatePolynomial { type Output = Self; fn add(mut self, mut other: Self) -> Self::Output { - assert_eq!(self.field, other.field); + assert_eq!(self.ring, other.ring); if self.variable != other.variable { panic!("Cannot multiply polynomials with different variables"); @@ -1198,7 +1241,7 @@ impl Add for UnivariatePolynomial { } for (i, c) in other.coefficients.iter().enumerate() { - self.field.add_assign(&mut self.coefficients[i], c); + self.ring.add_assign(&mut self.coefficients[i], c); } self.truncate(); @@ -1236,7 +1279,7 @@ impl Neg for UnivariatePolynomial { fn neg(mut self) -> Self::Output { // Negate coefficients of all terms. for c in &mut self.coefficients { - *c = self.field.neg(c); + *c = self.ring.neg(c); } self } @@ -1247,7 +1290,7 @@ impl<'a, 'b, F: Ring> Mul<&'a UnivariatePolynomial> for &'b UnivariatePolynom #[inline] fn mul(self, rhs: &'a UnivariatePolynomial) -> Self::Output { - assert_eq!(self.field, rhs.field); + assert_eq!(self.ring, rhs.ring); if self.is_zero() || rhs.is_zero() { return self.zero(); @@ -1263,7 +1306,7 @@ impl<'a, 'b, F: Ring> Mul<&'a UnivariatePolynomial> for &'b UnivariatePolynom if n == 0 { let mut r = rhs.clone(); for c in &mut r.coefficients { - self.field.mul_assign(c, &self.coefficients[0]); + self.ring.mul_assign(c, &self.coefficients[0]); } return r; } @@ -1271,13 +1314,13 @@ impl<'a, 'b, F: Ring> Mul<&'a UnivariatePolynomial> for &'b UnivariatePolynom if m == 0 { let mut r = self.clone(); for c in &mut r.coefficients { - self.field.mul_assign(c, &rhs.coefficients[0]); + self.ring.mul_assign(c, &rhs.coefficients[0]); } return r; } let mut res = self.zero(); - res.coefficients = vec![self.field.zero(); n + m + 1]; + res.coefficients = vec![self.ring.zero(); n + m + 1]; for (e1, c1) in self.coefficients.iter().enumerate() { if F::is_zero(c1) { @@ -1286,7 +1329,7 @@ impl<'a, 'b, F: Ring> Mul<&'a UnivariatePolynomial> for &'b UnivariatePolynom for (e2, c2) in rhs.coefficients.iter().enumerate() { if !F::is_zero(c2) { - self.field + self.ring .add_mul_assign(&mut res.coefficients[e1 + e2], c1, c2); } } @@ -1327,16 +1370,16 @@ impl UnivariatePolynomial { /// Get the content from the coefficients. pub fn content(&self) -> F::Element { if self.coefficients.is_empty() { - return self.field.zero(); + return self.ring.zero(); } let mut c = self.coefficients.first().unwrap().clone(); for cc in self.coefficients.iter().skip(1) { // early return if possible (not possible for rationals) - if F::one_is_gcd_unit() && self.field.is_one(&c) { + if F::one_is_gcd_unit() && self.ring.is_one(&c) { break; } - c = self.field.gcd(&c, cc); + c = self.ring.gcd(&c, cc); } c } @@ -1344,7 +1387,7 @@ impl UnivariatePolynomial { /// Divide every coefficient with `other`. pub fn div_coeff(mut self, other: &F::Element) -> Self { for c in &mut self.coefficients { - let (quot, rem) = self.field.quot_rem(c, other); + let (quot, rem) = self.ring.quot_rem(c, other); debug_assert!(F::is_zero(&rem)); *c = quot; } @@ -1371,7 +1414,7 @@ impl UnivariatePolynomial { } // check if the leading coefficients divide - if !F::is_zero(&self.field.rem(&self.lcoeff(), &div.lcoeff())) { + if !F::is_zero(&self.ring.rem(&self.lcoeff(), &div.lcoeff())) { return None; } @@ -1379,33 +1422,33 @@ impl UnivariatePolynomial { return None; } - if self.field.characteristic().is_zero() { + if self.ring.characteristic().is_zero() { // test division of constant term (evaluation at x_i = 0) let c = div.get_constant(); if !F::is_zero(&c) - && !self.field.is_one(&c) - && !F::is_zero(&self.field.rem(&self.get_constant(), &c)) + && !self.ring.is_one(&c) + && !F::is_zero(&self.ring.rem(&self.get_constant(), &c)) { return None; } // test division at x_i = 1 - let mut num = self.field.zero(); + let mut num = self.ring.zero(); for c in &self.coefficients { if !F::is_zero(c) { - self.field.add_assign(&mut num, c); + self.ring.add_assign(&mut num, c); } } - let mut den = self.field.zero(); + let mut den = self.ring.zero(); for c in &div.coefficients { if !F::is_zero(c) { - self.field.add_assign(&mut den, c); + self.ring.add_assign(&mut den, c); } } if !F::is_zero(&den) - && !self.field.is_one(&den) - && !F::is_zero(&self.field.rem(&num, &den)) + && !self.ring.is_one(&den) + && !F::is_zero(&self.ring.rem(&num, &den)) { return None; } @@ -1445,14 +1488,12 @@ impl UnivariatePolynomial { } let mut q = self.zero(); - q.coefficients = vec![self.field.zero(); n + 1 - m]; + q.coefficients = vec![self.ring.zero(); n + 1 - m]; let mut r = self.clone(); while n >= m { - let (qq, rr) = self - .field - .quot_rem(&r.coefficients[n], &div.coefficients[m]); + let (qq, rr) = self.ring.quot_rem(&r.coefficients[n], &div.coefficients[m]); if !F::is_zero(&rr) { return (self.zero(), r); } @@ -1498,7 +1539,7 @@ impl UnivariatePolynomial { let mut res = self.zero(); res.coefficients - .resize(self.coefficients.len() + 1, self.field.zero()); + .resize(self.coefficients.len() + 1, self.ring.zero()); for (p, (nc, oc)) in res .coefficients @@ -1508,7 +1549,7 @@ impl UnivariatePolynomial { .enumerate() { if !F::is_zero(oc) { - let (q, r) = self.field.quot_rem(oc, &self.field.nth(p as u64 + 1)); + let (q, r) = self.ring.quot_rem(oc, &self.ring.nth(p as u64 + 1)); if !F::is_zero(&r) { panic!("Could not compute integral since there is a remainder in the division of the exponent number."); } @@ -1524,8 +1565,8 @@ impl UnivariatePolynomial { /// Make the polynomial monic, i.e., make the leading coefficient `1` by /// multiplying all monomials with `1/lcoeff`. pub fn make_monic(self) -> Self { - if self.lcoeff() != self.field.one() { - let ci = self.field.inv(&self.lcoeff()); + if self.lcoeff() != self.ring.one() { + let ci = self.ring.inv(&self.lcoeff()); self.mul_coeff(&ci) } else { self @@ -1563,10 +1604,10 @@ impl UnivariatePolynomial { let mut r0 = self.clone().make_monic(); let mut r1 = other.clone().make_monic(); - let mut s0 = self.constant(self.field.inv(&self.lcoeff())); + let mut s0 = self.constant(self.ring.inv(&self.lcoeff())); let mut s1 = self.zero(); let mut t0 = self.zero(); - let mut t1 = self.constant(self.field.inv(&other.lcoeff())); + let mut t1 = self.constant(self.ring.inv(&other.lcoeff())); while !r1.is_zero() { let (q, r) = r0.quot_rem(&r1); @@ -1574,7 +1615,7 @@ impl UnivariatePolynomial { return (r1, s1, t1); } - let a = self.field.inv(&r.lcoeff()); + let a = self.ring.inv(&r.lcoeff()); (r1, r0) = (r.mul_coeff(&a), r1); (s1, s0) = ((s0 - &q * &s1).mul_coeff(&a), s1); (t1, t0) = ((t0 - q * &t1).mul_coeff(&a), t1); @@ -1646,7 +1687,7 @@ impl UnivariatePolynomial { // normalize the gcd let l = d.coefficients.last().unwrap().clone(); for x in &mut d.coefficients { - self.field.div_assign(x, &l); + self.ring.div_assign(x, &l); } d @@ -1669,15 +1710,15 @@ impl UnivariatePolynomial { let mut n = self.degree(); let m = div.degree(); - let u = self.field.inv(&div.coefficients[m]); + let u = self.ring.inv(&div.coefficients[m]); let mut q = self.zero(); - q.coefficients = vec![self.field.zero(); n - m + 1]; + q.coefficients = vec![self.ring.zero(); n - m + 1]; let mut r = self.clone(); while n >= m { - let qq = self.field.mul(&r.coefficients[n], &u); + let qq = self.ring.mul(&r.coefficients[n], &u); r = r - div.mul_exp(n - m).mul_coeff(&qq); q.coefficients[n - m] = qq; n = r.degree(); @@ -1693,7 +1734,7 @@ impl UnivariatePolynomial> { /// Convert a univariate polynomial of multivariate polynomials to a multivariate polynomial. pub fn flatten(self) -> MultivariatePolynomial { if self.is_zero() { - return self.field.zero(); + return self.ring.zero(); } let Some(pos) = self.coefficients[0] @@ -1706,7 +1747,7 @@ impl UnivariatePolynomial> { let n_vars = self.coefficients[0].get_vars().len(); let mut res = MultivariatePolynomial::new( - &self.field.ring, + &self.ring.ring, self.degree().into(), self.coefficients[0].get_vars().clone(), ); diff --git a/src/printer.rs b/src/printer.rs index 8e4cdde..ff4e788 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -1,4 +1,4 @@ -use std::fmt::{self, Display, Write}; +use std::fmt::{self, Error, Write}; use colored::Colorize; @@ -8,13 +8,8 @@ use crate::{ VarView, }, coefficient::CoefficientView, - domains::{ - factorized_rational_polynomial::FactorizedRationalPolynomial, - finite_field::FiniteFieldCore, rational_polynomial::RationalPolynomial, Ring, RingPrinter, - }, - poly::{polynomial::MultivariatePolynomial, Exponent, MonomialOrder}, + domains::{finite_field::FiniteFieldCore, SelfRing}, state::State, - tensors::matrix::{Matrix, Vector}, }; #[derive(Debug, Copy, Clone)] @@ -31,6 +26,7 @@ pub struct PrintOptions { pub square_brackets_for_function: bool, pub num_exp_as_superscript: bool, pub latex: bool, + pub precision: Option, } impl PrintOptions { @@ -49,6 +45,7 @@ impl PrintOptions { square_brackets_for_function: true, num_exp_as_superscript: false, latex: false, + precision: None, } } @@ -67,6 +64,7 @@ impl PrintOptions { square_brackets_for_function: false, num_exp_as_superscript: false, latex: true, + precision: None, } } @@ -85,6 +83,7 @@ impl PrintOptions { square_brackets_for_function: false, num_exp_as_superscript: false, latex: false, + precision: None, } } @@ -95,6 +94,18 @@ impl PrintOptions { ..Self::file() } } + + pub fn from_fmt(f: &std::fmt::Formatter) -> PrintOptions { + PrintOptions { + precision: f.precision(), + ..Default::default() + } + } + + pub fn update_with_fmt(mut self, f: &std::fmt::Formatter) -> Self { + self.precision = f.precision(); + self + } } impl Default for PrintOptions { @@ -112,16 +123,62 @@ impl Default for PrintOptions { square_brackets_for_function: false, num_exp_as_superscript: false, latex: false, + precision: None, } } } #[derive(Debug, Copy, Clone)] pub struct PrintState { - pub level: usize, + pub in_sum: bool, + pub in_product: bool, + pub suppress_one: bool, + pub in_exp: bool, pub top_level_add_child: bool, - pub explicit_sign: bool, pub superscript: bool, + pub level: u16, +} + +impl Default for PrintState { + fn default() -> Self { + Self::new() + } +} + +impl PrintState { + pub const fn new() -> PrintState { + Self { + in_sum: false, + in_product: false, + in_exp: false, + suppress_one: false, + top_level_add_child: true, + superscript: false, + level: 0, + } + } + + pub fn from_fmt(f: &std::fmt::Formatter) -> PrintState { + PrintState { + in_sum: f.sign_plus(), + ..Default::default() + } + } + + pub fn update_with_fmt(mut self, f: &std::fmt::Formatter) -> Self { + self.in_sum = f.sign_plus(); + self + } + + pub fn step(self, in_sum: bool, in_product: bool, in_exp: bool) -> Self { + Self { + in_sum, + in_product, + in_exp, + level: self.level + 1, + ..self + } + } } macro_rules! define_formatters { @@ -133,12 +190,12 @@ macro_rules! define_formatters { f: &mut fmt::Formatter, ) -> fmt::Result; - fn fmt_output( + fn fmt_output( &self, - f: &mut fmt::Formatter, + f: &mut W, print_opts: &PrintOptions, print_state: PrintState, - ) -> fmt::Result; + ) -> Result; })+ }; } @@ -174,12 +231,17 @@ impl<'a> AtomPrinter<'a> { impl<'a> fmt::Display for AtomPrinter<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let print_state = PrintState { + in_sum: false, + in_product: false, + in_exp: false, + suppress_one: false, level: 0, top_level_add_child: false, - explicit_sign: false, superscript: false, }; - self.atom.fmt_output(f, &self.print_opts, print_state) + self.atom + .format(f, &self.print_opts, print_state) + .map(|_| ()) } } @@ -215,12 +277,12 @@ impl<'a> AtomView<'a> { } } - fn fmt_output( + pub fn format( &self, - fmt: &mut fmt::Formatter, + fmt: &mut W, opts: &PrintOptions, print_state: PrintState, - ) -> fmt::Result { + ) -> Result { match self { AtomView::Num(n) => n.fmt_output(fmt, opts, print_state), AtomView::Var(v) => v.fmt_output(fmt, opts, print_state), @@ -378,13 +440,13 @@ impl<'a> fmt::Debug for AtomView<'a> { } impl<'a> FormattedPrintVar for VarView<'a> { - fn fmt_output( + fn fmt_output( &self, - f: &mut fmt::Formatter, + f: &mut W, opts: &PrintOptions, print_state: PrintState, - ) -> fmt::Result { - if print_state.explicit_sign { + ) -> Result { + if print_state.in_sum { if print_state.top_level_add_child && opts.color_top_level_sum { f.write_fmt(format_args!("{}", "+".yellow()))?; } else { @@ -408,7 +470,9 @@ impl<'a> FormattedPrintVar for VarView<'a> { f.write_fmt(format_args!("{}", name.purple())) } else { f.write_str(name) - } + }?; + + Ok(false) } fn fmt_debug(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -421,18 +485,18 @@ impl<'a> FormattedPrintNum for NumView<'a> { ::fmt(self, f) } - fn fmt_output( + fn fmt_output( &self, - f: &mut fmt::Formatter, + f: &mut W, opts: &PrintOptions, - print_state: PrintState, - ) -> fmt::Result { + mut print_state: PrintState, + ) -> Result { /// Input must be digits only. - fn format_num( + fn format_num( mut s: String, opts: &PrintOptions, print_state: &PrintState, - f: &mut fmt::Formatter, + f: &mut W, ) -> fmt::Result { if print_state.superscript { let map = ['⁰', '¹', '²', '³', '⁴', '⁵', '⁶', '⁷', '⁸', '⁹']; @@ -477,16 +541,24 @@ impl<'a> FormattedPrintNum for NumView<'a> { } else { f.write_char('-')?; } - } else if print_state.explicit_sign { + + print_state.in_sum = false; + } else if print_state.in_sum { if print_state.top_level_add_child && opts.color_top_level_sum { f.write_fmt(format_args!("{}", "+".yellow()))?; } else { f.write_char('+')?; } + + print_state.in_sum = false; } match d { CoefficientView::Natural(num, den) => { + if den == 1 && print_state.suppress_one && (num == 1 || num == -1) { + return Ok(true); + } + if !opts.latex && (opts.number_thousands_separator.is_some() || print_state.superscript) { @@ -495,20 +567,21 @@ impl<'a> FormattedPrintNum for NumView<'a> { f.write_char('/')?; format_num(den.to_string(), opts, &print_state, f)?; } - Ok(()) } else if den != 1 { if opts.latex { - f.write_fmt(format_args!("\\frac{{{}}}{{{}}}", num.unsigned_abs(), den)) + f.write_fmt(format_args!("\\frac{{{}}}{{{}}}", num.unsigned_abs(), den))?; } else { - f.write_fmt(format_args!("{}/{}", num.unsigned_abs(), den)) + f.write_fmt(format_args!("{}/{}", num.unsigned_abs(), den))?; } } else { - f.write_fmt(format_args!("{}", num.unsigned_abs())) + f.write_fmt(format_args!("{}", num.unsigned_abs()))?; } + + Ok(false) } CoefficientView::Float(fl) => { - let float = fl.to_float(); - f.write_fmt(format_args!("{}", float)) + fl.to_float().format(opts, print_state, f)?; + Ok(false) } CoefficientView::Large(r) => { let rat = r.to_rat().abs(); @@ -520,24 +593,24 @@ impl<'a> FormattedPrintNum for NumView<'a> { f.write_char('/')?; format_num(rat.denominator().to_string(), opts, &print_state, f)?; } - Ok(()) } else if !rat.is_integer() { if opts.latex { f.write_fmt(format_args!( "\\frac{{{}}}{{{}}}", rat.numerator(), rat.denominator(), - )) + ))?; } else { f.write_fmt(format_args!( "{}/{}", rat.numerator_ref(), rat.denominator_ref() - )) + ))?; } } else { - f.write_fmt(format_args!("{}", rat.numerator_ref())) + f.write_fmt(format_args!("{}", rat.numerator_ref()))?; } + Ok(false) } CoefficientView::FiniteField(num, fi) => { let ff = State::get_finite_field(fi); @@ -545,16 +618,13 @@ impl<'a> FormattedPrintNum for NumView<'a> { "[{}%{}]", ff.from_element(&num), ff.get_prime() - )) + ))?; + Ok(false) + } + CoefficientView::RationalPolynomial(p) => { + f.write_char('[')?; + p.deserialize().format(opts, print_state, f) } - CoefficientView::RationalPolynomial(p) => f.write_fmt(format_args!( - "[{}]", - RationalPolynomialPrinter { - poly: &p.deserialize(), - opts: *opts, - add_parentheses: false, - } - )), } } } @@ -564,12 +634,23 @@ impl<'a> FormattedPrintMul for MulView<'a> { ::fmt(self, f) } - fn fmt_output( + fn fmt_output( &self, - f: &mut fmt::Formatter, + f: &mut W, opts: &PrintOptions, mut print_state: PrintState, - ) -> fmt::Result { + ) -> Result { + let add_paren = print_state.in_exp; + if add_paren { + if print_state.in_sum { + print_state.in_sum = false; + f.write_char('+')?; + } + + f.write_char('(')?; + print_state.in_exp = false; + } + // write the coefficient first let mut first = true; let mut skip_num = false; @@ -589,7 +670,7 @@ impl<'a> FormattedPrintMul for MulView<'a> { } skip_num = true; - } else if print_state.explicit_sign { + } else if print_state.in_sum { if print_state.top_level_add_child && opts.color_top_level_sum { f.write_fmt(format_args!("{}", "+".yellow()))?; } else { @@ -599,7 +680,7 @@ impl<'a> FormattedPrintMul for MulView<'a> { print_state.top_level_add_child = false; print_state.level += 1; - print_state.explicit_sign = false; + print_state.in_sum = false; for x in self.iter().take(if skip_num { self.get_nargs() - 1 } else { @@ -620,28 +701,32 @@ impl<'a> FormattedPrintMul for MulView<'a> { } else { f.write_char('(')?; } - x.fmt_output(f, opts, print_state)?; + x.format(f, opts, print_state)?; if opts.latex { f.write_str("\\right)")?; } else { f.write_char(')')?; } } else { - x.fmt_output(f, opts, print_state)?; + x.format(f, opts, print_state)?; } } - Ok(()) + + if add_paren { + f.write_char(')')?; + } + Ok(false) } } impl<'a> FormattedPrintFn for FunView<'a> { - fn fmt_output( + fn fmt_output( &self, - f: &mut fmt::Formatter, + f: &mut W, opts: &PrintOptions, mut print_state: PrintState, - ) -> fmt::Result { - if print_state.explicit_sign { + ) -> Result { + if print_state.in_sum { if print_state.top_level_add_child && opts.color_top_level_sum { f.write_fmt(format_args!("{}", "+".yellow()))?; } else { @@ -676,7 +761,8 @@ impl<'a> FormattedPrintFn for FunView<'a> { print_state.top_level_add_child = false; print_state.level += 1; - print_state.explicit_sign = false; + print_state.in_sum = false; + print_state.suppress_one = false; let mut first = true; for x in self.iter() { if !first { @@ -684,16 +770,18 @@ impl<'a> FormattedPrintFn for FunView<'a> { } first = false; - x.fmt_output(f, opts, print_state)?; + x.format(f, opts, print_state)?; } if opts.latex { - f.write_str("\\right)") + f.write_str("\\right)")?; } else if opts.square_brackets_for_function { - f.write_char(']') + f.write_char(']')?; } else { - f.write_char(')') + f.write_char(')')?; } + + Ok(false) } fn fmt_debug(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -702,13 +790,13 @@ impl<'a> FormattedPrintFn for FunView<'a> { } impl<'a> FormattedPrintPow for PowView<'a> { - fn fmt_output( + fn fmt_output( &self, - f: &mut fmt::Formatter, + f: &mut W, opts: &PrintOptions, mut print_state: PrintState, - ) -> fmt::Result { - if print_state.explicit_sign { + ) -> Result { + if print_state.in_sum { if print_state.top_level_add_child && opts.color_top_level_sum { f.write_fmt(format_args!("{}", "+".yellow()))?; } else { @@ -721,7 +809,8 @@ impl<'a> FormattedPrintPow for PowView<'a> { print_state.top_level_add_child = false; print_state.level += 1; - print_state.explicit_sign = false; + print_state.in_sum = false; + print_state.suppress_one = false; let mut superscript_exponent = false; if opts.latex { @@ -729,8 +818,9 @@ impl<'a> FormattedPrintPow for PowView<'a> { if n.get_coeff_view() == CoefficientView::Natural(-1, 1) { // TODO: construct the numerator f.write_str("\\frac{1}{")?; - b.fmt_output(f, opts, print_state)?; - return f.write_char('}'); + b.format(f, opts, print_state)?; + f.write_char('}')?; + return Ok(false); } } } else if opts.num_exp_as_superscript { @@ -762,14 +852,14 @@ impl<'a> FormattedPrintPow for PowView<'a> { } else { f.write_char('(')?; } - b.fmt_output(f, opts, print_state)?; + b.format(f, opts, print_state)?; if opts.latex { f.write_str("\\right)")?; } else { f.write_char(')')?; } } else { - b.fmt_output(f, opts, print_state)?; + b.format(f, opts, print_state)?; } if !superscript_exponent { @@ -782,8 +872,8 @@ impl<'a> FormattedPrintPow for PowView<'a> { if opts.latex { f.write_char('{')?; - e.fmt_output(f, opts, print_state)?; - f.write_char('}') + e.format(f, opts, print_state)?; + f.write_char('}')?; } else { let exp_needs_parentheses = matches!(e, AtomView::Add(_) | AtomView::Mul(_)) || if let AtomView::Num(n) = e { @@ -794,13 +884,15 @@ impl<'a> FormattedPrintPow for PowView<'a> { if exp_needs_parentheses { f.write_char('(')?; - e.fmt_output(f, opts, print_state)?; - f.write_char(')') + e.format(f, opts, print_state)?; + f.write_char(')')?; } else { print_state.superscript = superscript_exponent; - e.fmt_output(f, opts, print_state) + e.format(f, opts, print_state)?; } } + + Ok(false) } fn fmt_debug(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -809,746 +901,52 @@ impl<'a> FormattedPrintPow for PowView<'a> { } impl<'a> FormattedPrintAdd for AddView<'a> { - fn fmt_output( + fn fmt_output( &self, - f: &mut fmt::Formatter, + f: &mut W, opts: &PrintOptions, mut print_state: PrintState, - ) -> fmt::Result { + ) -> Result { let mut first = true; print_state.top_level_add_child = print_state.level == 0; print_state.level += 1; + print_state.suppress_one = false; - for x in self.iter() { - if !first && print_state.top_level_add_child && opts.terms_on_new_line { - f.write_char('\n')?; - f.write_char('\t')?; - } - print_state.explicit_sign = !first; - first = false; - - x.fmt_output(f, opts, print_state)?; - } - Ok(()) - } - - fn fmt_debug(&self, f: &mut fmt::Formatter) -> fmt::Result { - ::fmt(self, f) - } -} - -pub struct FactorizedRationalPolynomialPrinter<'a, R: Ring, E: Exponent> { - pub poly: &'a FactorizedRationalPolynomial, - pub opts: PrintOptions, - pub add_parentheses: bool, -} - -impl<'a, R: Ring, E: Exponent> FactorizedRationalPolynomialPrinter<'a, R, E> { - pub fn new( - poly: &'a FactorizedRationalPolynomial, - ) -> FactorizedRationalPolynomialPrinter<'a, R, E> { - FactorizedRationalPolynomialPrinter { - poly, - opts: PrintOptions::default(), - add_parentheses: false, - } - } - - pub fn new_with_options( - poly: &'a FactorizedRationalPolynomial, - opts: PrintOptions, - ) -> FactorizedRationalPolynomialPrinter<'a, R, E> { - FactorizedRationalPolynomialPrinter { - poly, - opts, - add_parentheses: false, - } - } -} - -impl<'a, R: Ring, E: Exponent> Display for FactorizedRationalPolynomialPrinter<'a, R, E> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - if self.opts.explicit_rational_polynomial { - if !R::is_zero(&self.poly.numer_coeff) - && !self.poly.numerator.ring.is_one(&self.poly.numer_coeff) - { - f.write_fmt(format_args!( - "[{}]*", - RingPrinter { - ring: &self.poly.numerator.ring, - element: &self.poly.numer_coeff, - opts: self.opts, - in_product: false - } - ))?; - } - - if self.poly.denominators.is_empty() - && self.poly.numerator.ring.is_one(&self.poly.denom_coeff) - { - if self.poly.numerator.is_zero() { - f.write_char('0')?; - } else { - f.write_fmt(format_args!( - "[{}]", - PolynomialPrinter { - poly: &self.poly.numerator, - opts: self.opts, - } - ))?; - } - } else { - f.write_fmt(format_args!( - "[{}", - PolynomialPrinter { - poly: &self.poly.numerator, - - opts: self.opts, - }, - ))?; - - if !self.poly.numerator.ring.is_one(&self.poly.denom_coeff) { - f.write_fmt(format_args!( - ",{},1", - RingPrinter { - ring: &self.poly.numerator.ring, - element: &self.poly.denom_coeff, - opts: self.opts, - in_product: false - }, - ))?; - } - - for (d, p) in &self.poly.denominators { - f.write_fmt(format_args!( - ",{}", - PolynomialPrinter { - poly: d, - - opts: self.opts, - } - ))?; - f.write_fmt(format_args!(",{}", p))?; - } - - f.write_char(']')?; - } - - return Ok(()); - } - - if R::is_zero(&self.poly.numer_coeff) { - return f.write_char('0'); - } - - if self.poly.denominators.is_empty() - && self.poly.numerator.ring.is_one(&self.poly.denom_coeff) - { - if !self.poly.numerator.ring.is_one(&self.poly.numer_coeff) { - f.write_fmt(format_args!( - "{}", - RingPrinter { - ring: &self.poly.numerator.ring, - element: &self.poly.numer_coeff, - opts: self.opts, - in_product: false - } - ))?; - } - - if (self.poly.numerator.ring.is_one(&self.poly.numer_coeff) && !self.add_parentheses) - || self.poly.numerator.nterms() < 2 - { - if !self.poly.numerator.ring.is_one(&self.poly.numer_coeff) { - if self.poly.numerator.is_one() { - return Ok(()); - } - - f.write_char('*')?; - } - - f.write_fmt(format_args!( - "{}", - PolynomialPrinter { - poly: &self.poly.numerator, - - opts: self.opts, - } - )) - } else { - if !self.poly.numerator.ring.is_one(&self.poly.numer_coeff) { - if self.poly.numerator.is_one() { - return Ok(()); - } - - f.write_char('*')?; - } - - f.write_fmt(format_args!( - "({})", - PolynomialPrinter { - poly: &self.poly.numerator, - - opts: self.opts, - } - )) - } - } else { - if self.opts.latex { - if !self.poly.numerator.ring.is_one(&self.poly.numer_coeff) { - f.write_fmt(format_args!( - "{} ", - RingPrinter { - ring: &self.poly.numerator.ring, - element: &self.poly.numer_coeff, - opts: self.opts, - in_product: false - } - ))?; - } - - f.write_fmt(format_args!( - "\\frac{{{}}}{{", - PolynomialPrinter { - poly: &self.poly.numerator, - - opts: self.opts, - }, - ))?; - - if !self.poly.numerator.ring.is_one(&self.poly.denom_coeff) { - f.write_fmt(format_args!( - "{}", - RingPrinter { - ring: &self.poly.numerator.ring, - element: &self.poly.denom_coeff, - opts: self.opts, - in_product: false - } - ))?; - } - - for (d, p) in &self.poly.denominators { - if *p == 1 { - f.write_fmt(format_args!( - "({})", - PolynomialPrinter { - poly: d, - opts: self.opts, - } - ))?; - } else { - f.write_fmt(format_args!( - "({})^{}", - PolynomialPrinter { - poly: d, - opts: self.opts, - }, - p - ))?; - } - } - - return f.write_str("}}"); - } - - if !self.poly.numerator.ring.is_one(&self.poly.numer_coeff) { - f.write_fmt(format_args!( - "{}*", - RingPrinter { - ring: &self.poly.numerator.ring, - element: &self.poly.numer_coeff, - opts: self.opts, - in_product: false - } - ))?; - } - - if self.poly.numerator.nterms() < 2 { - f.write_fmt(format_args!( - "{}", - PolynomialPrinter { - poly: &self.poly.numerator, - opts: self.opts, - } - ))?; - } else { - f.write_fmt(format_args!( - "({})", - PolynomialPrinter { - poly: &self.poly.numerator, - opts: self.opts, - } - ))?; - } - - f.write_char('/')?; - - if self.poly.denominators.is_empty() { - return f.write_fmt(format_args!( - "{}", - RingPrinter { - ring: &self.poly.numerator.ring, - element: &self.poly.denom_coeff, - opts: self.opts, - in_product: true - } - )); - } - - if self.poly.numerator.ring.is_one(&self.poly.denom_coeff) - && self.poly.denominators.len() == 1 - && self.poly.denominators[0].0.nterms() == 1 - && self.poly.denominators[0].1 == 1 - { - let (d, _) = &self.poly.denominators[0]; - let var_count = d.exponents.iter().filter(|x| !x.is_zero()).count(); - - if var_count == 0 || d.ring.is_one(&d.coefficients[0]) && var_count == 1 { - return f.write_fmt(format_args!( - "{}", - PolynomialPrinter { - poly: d, - opts: self.opts, - } - )); - } - } - - f.write_char('(')?; // TODO: add special cases for 1 argument - - if !self.poly.numerator.ring.is_one(&self.poly.denom_coeff) { - f.write_fmt(format_args!( - "{}", - RingPrinter { - ring: &self.poly.numerator.ring, - element: &self.poly.denom_coeff, - opts: self.opts, - in_product: true - } - ))?; - } - - for (d, p) in &self.poly.denominators { - if *p == 1 { - f.write_fmt(format_args!( - "({})", - PolynomialPrinter { - poly: d, - opts: self.opts, - } - ))?; - } else { - f.write_fmt(format_args!( - "({}){}{}", - PolynomialPrinter { - poly: d, - opts: self.opts, - }, - if self.opts.double_star_for_exponentiation { - "**" - } else { - "^" - }, - p - ))?; - } - } - - f.write_char(')') - } - } -} - -pub struct RationalPolynomialPrinter<'a, R: Ring, E: Exponent> { - pub poly: &'a RationalPolynomial, - pub opts: PrintOptions, - pub add_parentheses: bool, -} - -impl<'a, R: Ring, E: Exponent> RationalPolynomialPrinter<'a, R, E> { - pub fn new(poly: &'a RationalPolynomial) -> RationalPolynomialPrinter<'a, R, E> { - RationalPolynomialPrinter { - poly, - opts: PrintOptions::default(), - add_parentheses: false, - } - } - - pub fn new_with_options( - poly: &'a RationalPolynomial, - opts: PrintOptions, - ) -> RationalPolynomialPrinter<'a, R, E> { - RationalPolynomialPrinter { - poly, - opts, - add_parentheses: false, - } - } -} - -impl<'a, R: Ring, E: Exponent> Display for RationalPolynomialPrinter<'a, R, E> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - if self.opts.explicit_rational_polynomial { - if self.poly.denominator.is_one() { - if self.poly.numerator.is_zero() { - f.write_char('0')?; + let add_paren = print_state.in_product || print_state.in_exp; + if add_paren { + if print_state.in_sum { + if print_state.top_level_add_child && opts.color_top_level_sum { + f.write_fmt(format_args!("{}", "+".yellow()))?; } else { - f.write_fmt(format_args!( - "[{}]", - PolynomialPrinter { - poly: &self.poly.numerator, - opts: self.opts, - } - ))?; + f.write_char('+')?; } - } else { - f.write_fmt(format_args!( - "[{},{}]", - PolynomialPrinter { - poly: &self.poly.numerator, - opts: self.opts, - }, - PolynomialPrinter { - poly: &self.poly.denominator, - opts: self.opts, - } - ))?; } - return Ok(()); + print_state.in_sum = false; + print_state.in_product = false; + print_state.in_exp = false; + f.write_char('(')?; } - if self.poly.denominator.is_one() { - if !self.add_parentheses || self.poly.numerator.nterms() < 2 { - f.write_fmt(format_args!( - "{}", - PolynomialPrinter { - poly: &self.poly.numerator, - opts: self.opts, - } - )) - } else { - f.write_fmt(format_args!( - "({})", - PolynomialPrinter { - poly: &self.poly.numerator, - opts: self.opts, - } - )) - } - } else { - if self.opts.latex { - return f.write_fmt(format_args!( - "\\frac{{{}}}{{{}}}", - PolynomialPrinter { - poly: &self.poly.numerator, - opts: self.opts, - }, - PolynomialPrinter { - poly: &self.poly.denominator, - opts: self.opts, - } - )); - } - - if self.poly.numerator.nterms() < 2 { - f.write_fmt(format_args!( - "{}", - PolynomialPrinter { - poly: &self.poly.numerator, - opts: self.opts, - } - ))?; - } else { - f.write_fmt(format_args!( - "({})", - PolynomialPrinter { - poly: &self.poly.numerator, - opts: self.opts, - } - ))?; - } - - if self.poly.denominator.nterms() == 1 { - let var_count = self - .poly - .denominator - .exponents - .iter() - .filter(|x| !x.is_zero()) - .count(); - - if var_count == 0 - || self - .poly - .denominator - .ring - .is_one(&self.poly.denominator.coefficients[0]) - && var_count == 1 - { - return f.write_fmt(format_args!( - "/{}", - PolynomialPrinter { - poly: &self.poly.denominator, - opts: self.opts, - } - )); - } - } - - f.write_fmt(format_args!( - "/({})", - PolynomialPrinter { - poly: &self.poly.denominator, - - opts: self.opts, - } - )) - } - } -} -pub struct PolynomialPrinter<'a, F: Ring + Display, E: Exponent, O: MonomialOrder> { - pub poly: &'a MultivariatePolynomial, - pub opts: PrintOptions, -} - -impl<'a, R: Ring + Display, E: Exponent, O: MonomialOrder> PolynomialPrinter<'a, R, E, O> { - pub fn new(poly: &'a MultivariatePolynomial) -> PolynomialPrinter<'a, R, E, O> { - PolynomialPrinter { - poly, - opts: PrintOptions::default(), - } - } - - pub fn new_with_options( - poly: &'a MultivariatePolynomial, - opts: PrintOptions, - ) -> PolynomialPrinter<'a, R, E, O> { - PolynomialPrinter { poly, opts } - } -} - -impl<'a, F: Ring + Display, E: Exponent, O: MonomialOrder> Display - for PolynomialPrinter<'a, F, E, O> -{ - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - if f.sign_plus() { - f.write_char('+')?; - } - - let var_map: Vec = self - .poly - .variables - .as_ref() - .iter() - .map(|v| v.to_string()) - .collect(); - - let mut is_first_term = true; - for monomial in self.poly { - let mut is_first_factor = true; - if self.poly.ring.is_one(monomial.coefficient) { - if !is_first_term { - write!(f, "+")?; - } - } else if monomial - .coefficient - .eq(&self.poly.ring.neg(&self.poly.ring.one())) - { - write!(f, "-")?; - } else { - if is_first_term { - self.poly - .ring - .fmt_display(monomial.coefficient, &self.opts, true, f)?; - } else { - write!( - f, - "{:+}", - RingPrinter { - ring: &self.poly.ring, - element: monomial.coefficient, - opts: self.opts, - in_product: true - } - )?; - } - is_first_factor = false; - } - is_first_term = false; - for (var_id, e) in var_map.iter().zip(monomial.exponents) { - if e.is_zero() { - continue; - } - if is_first_factor { - is_first_factor = false; - } else if !self.opts.latex { - write!(f, "*")?; - } - - f.write_str(var_id)?; - - if e.to_u32() != 1 { - if self.opts.latex { - write!(f, "^{{{}}}", e)?; - } else if self.opts.double_star_for_exponentiation { - write!(f, "**{}", e)?; - } else { - write!(f, "^{}", e)?; - } - } - } - if is_first_factor { - write!(f, "1")?; - } - } - if is_first_term { - write!(f, "0")?; - } - - if self.opts.print_finite_field { - Display::fmt(&self.poly.ring, f)?; - } - - Ok(()) - } -} - -pub struct MatrixPrinter<'a, F: Ring + Display> { - pub matrix: &'a Matrix, - pub opts: PrintOptions, -} - -impl<'a, F: Ring + Display> MatrixPrinter<'a, F> { - pub fn new(matrix: &'a Matrix) -> MatrixPrinter<'a, F> { - MatrixPrinter { - matrix, - - opts: PrintOptions::default(), - } - } - - pub fn new_with_options(matrix: &'a Matrix, opts: PrintOptions) -> MatrixPrinter<'a, F> { - MatrixPrinter { matrix, opts } - } -} - -impl<'a, F: Ring + Display> Display for MatrixPrinter<'a, F> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - if self.opts.latex { - f.write_str("\\begin{pmatrix}")?; - - for (ri, r) in self.matrix.row_iter().enumerate() { - for (ci, c) in r.iter().enumerate() { - f.write_fmt(format_args!( - "{}", - RingPrinter { - ring: &self.matrix.field, - element: c, - opts: self.opts, - in_product: false, - }, - ))?; - - if ci + 1 < self.matrix.ncols as usize { - f.write_str(" & ")?; - } - } - if ri + 1 < self.matrix.nrows as usize { - f.write_str(r" \\ ")?; - } + for x in self.iter() { + if !first && print_state.top_level_add_child && opts.terms_on_new_line { + f.write_char('\n')?; + f.write_char('\t')?; } + first = false; - f.write_str("\\end{pmatrix}") - } else { - f.write_char('{')?; - for (ri, r) in self.matrix.row_iter().enumerate() { - f.write_char('{')?; - for (ci, c) in r.iter().enumerate() { - f.write_fmt(format_args!( - "{}", - RingPrinter { - ring: &self.matrix.field, - element: c, - opts: self.opts, - in_product: false, - }, - ))?; - - if ci + 1 < self.matrix.ncols as usize { - f.write_char(',')?; - } - } - f.write_char('}')?; - if ri + 1 < self.matrix.nrows as usize { - f.write_char(',')?; - } - } - f.write_char('}') + x.format(f, opts, print_state)?; + print_state.in_sum = true; } - } -} - -pub struct VectorPrinter<'a, F: Ring + Display> { - pub vector: &'a Vector, - pub opts: PrintOptions, -} -impl<'a, F: Ring + Display> VectorPrinter<'a, F> { - pub fn new(vector: &'a Vector) -> VectorPrinter<'a, F> { - VectorPrinter { - vector, - opts: PrintOptions::default(), + if add_paren { + f.write_char(')')?; } + Ok(false) } - pub fn new_with_options(vector: &'a Vector, opts: PrintOptions) -> VectorPrinter<'a, F> { - VectorPrinter { vector, opts } - } -} - -impl<'a, F: Ring + Display> Display for VectorPrinter<'a, F> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - if self.opts.latex { - f.write_str("\\begin{pvector}")?; - - for (ri, r) in self.vector.data.iter().enumerate() { - f.write_fmt(format_args!( - "{}", - RingPrinter { - ring: &self.vector.field, - element: r, - opts: self.opts, - in_product: false, - }, - ))?; - - if ri + 1 < self.vector.data.len() { - f.write_str(" & ")?; - } - } - - f.write_str("\\end{pvector}") - } else { - f.write_char('{')?; - for (ri, r) in self.vector.data.iter().enumerate() { - f.write_fmt(format_args!( - "{}", - RingPrinter { - ring: &self.vector.field, - element: r, - opts: self.opts, - in_product: false, - }, - ))?; - - if ri + 1 < self.vector.data.len() { - f.write_char(',')?; - } - } - f.write_char('}') - } + fn fmt_debug(&self, f: &mut fmt::Formatter) -> fmt::Result { + ::fmt(self, f) } } @@ -1558,8 +956,8 @@ mod test { use crate::{ atom::Atom, - domains::{finite_field::Zp, integer::Z}, - printer::{AtomPrinter, PolynomialPrinter, PrintOptions}, + domains::{finite_field::Zp, integer::Z, SelfRing}, + printer::{AtomPrinter, PrintOptions, PrintState}, state::{FunctionAttribute, State}, }; @@ -1612,20 +1010,20 @@ mod test { let a = Atom::parse("15 x^2") .unwrap() .to_polynomial::<_, u8>(&Zp::new(17), None); - assert_eq!( - format!( - "{}", - PolynomialPrinter::new_with_options( - &a, - PrintOptions { - print_finite_field: true, - symmetric_representation_for_finite_field: true, - ..PrintOptions::file() - } - ) - ), - "-2*x^2 % 17" - ); + + let mut s = String::new(); + a.format( + &PrintOptions { + print_finite_field: true, + symmetric_representation_for_finite_field: true, + ..PrintOptions::file() + }, + PrintState::new(), + &mut s, + ) + .unwrap(); + + assert_eq!(s, "-2*x^2 % 17"); } #[test] @@ -1647,16 +1045,27 @@ mod test { .unwrap() .to_factorized_rational_polynomial::<_, _, u8>(&Z, &Z, None); assert!( - format!("{}", a) == "15*x^2/((1+x)(2+x))" || format!("{}", a) == "15*x^2/((2+x)(1+x))" + format!("{}", a) == "15*x^2/((1+x)*(2+x))" + || format!("{}", a) == "15*x^2/((2+x)*(1+x))" ); let a = Atom::parse("(15 x^2 + 6) / ((1+x)(x+2))") .unwrap() .to_factorized_rational_polynomial::<_, _, u8>(&Z, &Z, None); assert!( - format!("{}", a) == "3*(2+5*x^2)/((1+x)(2+x))" - || format!("{}", a) == "3*(2+5*x^2)/((2+x)(1+x))" + format!("{}", a) == "3*(2+5*x^2)/((1+x)*(2+x))" + || format!("{}", a) == "3*(2+5*x^2)/((2+x)*(1+x))" ); + + let a = Atom::parse("1/(v1*v2)") + .unwrap() + .to_factorized_rational_polynomial::<_, _, u8>(&Z, &Z, None); + assert!(format!("{}", a) == "1/(v1*v2)" || format!("{}", a) == "1/(v2*v1)"); + + let a = Atom::parse("-1/(2+v1)") + .unwrap() + .to_factorized_rational_polynomial::<_, _, u8>(&Z, &Z, None); + assert!(format!("{}", a) == "-1/(2+v1)"); } #[test] diff --git a/src/tensors/matrix.rs b/src/tensors/matrix.rs index 2a7428c..725f867 100644 --- a/src/tensors/matrix.rs +++ b/src/tensors/matrix.rs @@ -8,10 +8,10 @@ use crate::{ domains::{ integer::Z, rational::{Rational, Q}, - Derivable, EuclideanDomain, Field, Ring, + Derivable, EuclideanDomain, Field, InternalOrdering, Ring, SelfRing, }, poly::Variable, - printer::{MatrixPrinter, VectorPrinter}, + printer::{PrintOptions, PrintState}, }; /// An n-dimensional vector. @@ -124,6 +124,62 @@ impl Vector { } } +impl SelfRing for Vector { + fn is_one(&self) -> bool { + self.data.iter().all(|e| self.field.is_one(e)) + } + + fn is_zero(&self) -> bool { + self.data.iter().all(F::is_zero) + } + + fn format( + &self, + opts: &PrintOptions, + mut state: PrintState, + f: &mut W, + ) -> Result { + if state.in_sum { + f.write_char('+')?; + } + state.in_sum = false; + state.in_product = false; + state.in_exp = false; + + if opts.latex { + f.write_str("\\begin{pvector}")?; + + for (ri, r) in self.data.iter().enumerate() { + self.field.format(r, opts, PrintState::new(), f)?; + + if ri + 1 < self.data.len() { + f.write_str(" & ")?; + } + } + + f.write_str("\\end{pvector}")?; + Ok(false) + } else { + f.write_char('{')?; + for (ri, r) in self.data.iter().enumerate() { + self.field.format(r, opts, PrintState::new(), f)?; + + if ri + 1 < self.data.len() { + f.write_char(',')?; + } + } + f.write_char('}')?; + Ok(false) + } + } +} + +impl InternalOrdering for Vector { + fn internal_cmp(&self, other: &Self) -> std::cmp::Ordering { + self.data.internal_cmp(&other.data) + } +} + impl Vector { /// Compute the derivative in the variable `x`. pub fn derivative(&self, x: &Variable) -> Vector { @@ -331,7 +387,8 @@ impl IndexMut for Vector { impl Display for Vector { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - VectorPrinter::new(self).fmt(f) + self.format(&PrintOptions::from_fmt(f), PrintState::from_fmt(f), f) + .map(|_| ()) } } @@ -657,6 +714,78 @@ impl Matrix { } } +impl SelfRing for Matrix { + fn is_one(&self) -> bool { + self.data.iter().enumerate().all(|(i, e)| { + i as u32 % self.ncols == i as u32 / self.ncols && self.field.is_one(e) || F::is_zero(e) + }) + } + + fn is_zero(&self) -> bool { + self.data.iter().all(F::is_zero) + } + + fn format( + &self, + opts: &PrintOptions, + mut state: PrintState, + f: &mut W, + ) -> Result { + if state.in_sum { + f.write_char('+')?; + } + state.in_sum = false; + state.in_product = false; + state.in_exp = false; + + if opts.latex { + f.write_str("\\begin{pmatrix}")?; + + for (ri, r) in self.row_iter().enumerate() { + for (ci, c) in r.iter().enumerate() { + self.field.format(c, opts, state, f)?; + + if ci + 1 < self.ncols as usize { + f.write_str(" & ")?; + } + } + if ri + 1 < self.nrows as usize { + f.write_str(r" \\ ")?; + } + } + + f.write_str("\\end{pmatrix}")?; + Ok(false) + } else { + f.write_char('{')?; + for (ri, r) in self.row_iter().enumerate() { + f.write_char('{')?; + for (ci, c) in r.iter().enumerate() { + self.field.format(c, opts, state, f)?; + + if ci + 1 < self.ncols as usize { + f.write_char(',')?; + } + } + f.write_char('}')?; + if ri + 1 < self.nrows as usize { + f.write_char(',')?; + } + } + f.write_char('}')?; + Ok(false) + } + } +} + +impl InternalOrdering for Matrix { + fn internal_cmp(&self, other: &Self) -> std::cmp::Ordering { + (self.nrows, self.ncols) + .cmp(&(other.nrows, other.ncols)) + .then_with(|| self.data.internal_cmp(&other.data)) + } +} + impl Index for Matrix { type Output = [F::Element]; @@ -687,7 +816,8 @@ impl IndexMut<(u32, u32)> for Matrix { impl Display for Matrix { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - MatrixPrinter::new(self).fmt(f) + self.format(&PrintOptions::from_fmt(f), PrintState::from_fmt(f), f) + .map(|_| ()) } } diff --git a/symbolica.pyi b/symbolica.pyi index 8228f52..4d1bc54 100644 --- a/symbolica.pyi +++ b/symbolica.pyi @@ -376,7 +376,7 @@ class Expression: def get_byte_size(self) -> int: """ Get the number of bytes that this expression takes up in memory.""" - def pretty_str( + def format( self, terms_on_new_line: bool = False, color_top_level_sum: bool = True, @@ -390,6 +390,7 @@ class Expression: square_brackets_for_function: bool = False, num_exp_as_superscript: bool = True, latex: bool = False, + precision: Optional[int] = None, ) -> str: """ Convert the expression into a human-readable string, with tunable settings. @@ -397,7 +398,7 @@ class Expression: Examples -------- >>> a = Expression.parse('128378127123 z^(2/3)*w^2/x/y + y^4 + z^34 + x^(x+2)+3/5+f(x,x^2)') - >>> print(a.pretty_str(number_thousands_separator='_', multiplication_operator=' ')) + >>> print(a.format(number_thousands_separator='_', multiplication_operator=' ')) Yields `z³⁴+x^(x+2)+y⁴+f(x,x²)+128_378_127_123 z^(2/3) w² x⁻¹ y⁻¹+3/5`. """ @@ -2219,7 +2220,7 @@ class Polynomial: def to_latex(self) -> str: """Convert the polynomial into a LaTeX string.""" - def pretty_str( + def format( self, terms_on_new_line: bool = False, color_top_level_sum: bool = True, @@ -2233,6 +2234,7 @@ class Polynomial: square_brackets_for_function: bool = False, num_exp_as_superscript: bool = True, latex: bool = False, + precision: Optional[int] = None, ) -> str: """ Convert the polynomial into a human-readable string, with tunable settings. @@ -2240,7 +2242,7 @@ class Polynomial: Examples -------- >>> p = FiniteFieldPolynomial.parse("3*x^2+2*x+7*x^3", ['x'], 11) - >>> print(p.pretty_str(symmetric_representation_for_finite_field=True)) + >>> print(p.format(symmetric_representation_for_finite_field=True)) Yields `z³⁴+x^(x+2)+y⁴+f(x,x²)+128_378_127_123 z^(2/3) w² x⁻¹ y⁻¹+3/5`. """ @@ -2488,7 +2490,7 @@ class IntegerPolynomial: def to_latex(self) -> str: """Convert the polynomial into a LaTeX string.""" - def pretty_str( + def format( self, terms_on_new_line: bool = False, color_top_level_sum: bool = True, @@ -2502,6 +2504,7 @@ class IntegerPolynomial: square_brackets_for_function: bool = False, num_exp_as_superscript: bool = True, latex: bool = False, + precision: Optional[int] = None, ) -> str: """ Convert the polynomial into a human-readable string, with tunable settings. @@ -2509,7 +2512,7 @@ class IntegerPolynomial: Examples -------- >>> p = FiniteFieldPolynomial.parse("3*x^2+2*x+7*x^3", ['x'], 11) - >>> print(p.pretty_str(symmetric_representation_for_finite_field=True)) + >>> print(p.format(symmetric_representation_for_finite_field=True)) Yields `z³⁴+x^(x+2)+y⁴+f(x,x²)+128_378_127_123 z^(2/3) w² x⁻¹ y⁻¹+3/5`. """ @@ -2649,7 +2652,7 @@ class NumberFieldPolynomial: def to_latex(self) -> str: """Convert the polynomial into a LaTeX string.""" - def pretty_str( + def format( self, terms_on_new_line: bool = False, color_top_level_sum: bool = True, @@ -2663,6 +2666,7 @@ class NumberFieldPolynomial: square_brackets_for_function: bool = False, num_exp_as_superscript: bool = True, latex: bool = False, + precision: Optional[int] = None, ) -> str: """ Convert the polynomial into a human-readable string, with tunable settings. @@ -2670,7 +2674,7 @@ class NumberFieldPolynomial: Examples -------- >>> p = FiniteFieldNumberFieldPolynomial.parse("3*x^2+2*x+7*x^3", ['x'], 11) - >>> print(p.pretty_str(symmetric_representation_for_finite_field=True)) + >>> print(p.format(symmetric_representation_for_finite_field=True)) Yields `z³⁴+x^(x+2)+y⁴+f(x,x²)+128_378_127_123 z^(2/3) w² x⁻¹ y⁻¹+3/5`. """ @@ -2859,7 +2863,7 @@ class FiniteFieldPolynomial: def to_latex(self) -> str: """Convert the polynomial into a LaTeX string.""" - def pretty_str( + def format( self, terms_on_new_line: bool = False, color_top_level_sum: bool = True, @@ -2873,6 +2877,7 @@ class FiniteFieldPolynomial: square_brackets_for_function: bool = False, num_exp_as_superscript: bool = True, latex: bool = False, + precision: Optional[int] = None, ) -> str: """ Convert the polynomial into a human-readable string, with tunable settings. @@ -2880,7 +2885,7 @@ class FiniteFieldPolynomial: Examples -------- >>> p = FiniteFieldPolynomial.parse("3*x^2+2*x+7*x^3", ['x'], 11) - >>> print(p.pretty_str(symmetric_representation_for_finite_field=True)) + >>> print(p.format(symmetric_representation_for_finite_field=True)) Yields `z³⁴+x^(x+2)+y⁴+f(x,x²)+128_378_127_123 z^(2/3) w² x⁻¹ y⁻¹+3/5`. """