use lambdaworks_math::{
field::{
element::FieldElement,
traits::{IsField, IsPrimeField},
},
polynomial::Polynomial,
};
use crate::{errors::InterpreterError, interpreter::Polenta};
pub trait PolentaUtilExt<F: IsField> {
fn poly_as_felt(
poly: &Polynomial<FieldElement<F>>,
) -> Result<FieldElement<F>, InterpreterError>;
fn felt_as_poly(felt: FieldElement<F>) -> Polynomial<FieldElement<F>>;
fn poly_print(poly: &Polynomial<FieldElement<F>>) -> String;
fn poly_pow(
poly: &Polynomial<FieldElement<F>>,
exponent: FieldElement<F>,
) -> Polynomial<FieldElement<F>>;
fn poly_is_zero(poly: &Polynomial<FieldElement<F>>) -> bool;
fn poly_from_bool(b: bool) -> Polynomial<FieldElement<F>>;
}
impl<F: IsPrimeField> PolentaUtilExt<F> for Polenta<F> {
fn poly_as_felt(
poly: &Polynomial<FieldElement<F>>,
) -> Result<FieldElement<F>, InterpreterError> {
if poly.coeff_len() > 1 {
return Err(InterpreterError::ExpectedConstant);
}
Ok(poly.leading_coefficient())
}
fn poly_is_zero(poly: &Polynomial<FieldElement<F>>) -> bool {
poly.coeff_len() == 0
}
fn poly_from_bool(b: bool) -> Polynomial<FieldElement<F>> {
if b {
Polynomial::new_monomial(FieldElement::one(), 0)
} else {
Polynomial::zero()
}
}
fn felt_as_poly(felt: FieldElement<F>) -> Polynomial<FieldElement<F>> {
Polynomial::new_monomial(felt, 0)
}
fn poly_print(poly: &Polynomial<FieldElement<F>>) -> String {
let coeff_decimals = poly
.coefficients()
.iter()
.map(|coeff| repr_to_decimal(&format!("{}", coeff.representative())))
.collect::<Vec<_>>();
let result = coeff_decimals
.iter()
.enumerate()
.rev()
.map(|(i, coeff)| match (i, coeff.as_str()) {
(_, "0") => String::new(),
(0, ___) => format!("{}", coeff),
(1, "1") => "x".to_string(),
(1, ___) => format!("{}*x", coeff),
(_, "1") => format!("x^{}", i),
(_, ___) => format!("{}*x^{}", coeff, i),
})
.filter(|s| !s.is_empty())
.collect::<Vec<_>>()
.join(" + ");
if result.is_empty() {
"0".to_string()
} else {
result
}
}
fn poly_pow(
poly: &Polynomial<FieldElement<F>>,
exponent: FieldElement<F>,
) -> Polynomial<FieldElement<F>> {
let mut exp = exponent.representative();
let zero = F::RepresentativeType::from(0u16);
let one_bit = F::RepresentativeType::from(1u16);
let mut result = Polynomial::new_monomial(FieldElement::one(), 0);
let mut base = poly.clone();
while exp > zero {
if (exp & one_bit) == one_bit {
result = result * base.clone();
}
exp = exp >> 1usize;
if exp > zero {
base = base.clone() * base;
}
}
result
}
}
fn repr_to_decimal(s: &str) -> String {
let hex = match s.strip_prefix("0x").or_else(|| s.strip_prefix("0X")) {
Some(h) => h,
None => return s.to_string(),
};
let mut digits: Vec<u32> = vec![0];
for c in hex.chars() {
let nibble = c.to_digit(16).expect("invalid hex digit");
let mut carry = 0u32;
for d in digits.iter_mut() {
let val = *d * 16 + carry;
*d = val % 10;
carry = val / 10;
}
while carry > 0 {
digits.push(carry % 10);
carry /= 10;
}
carry = nibble;
for d in digits.iter_mut() {
if carry == 0 {
break;
}
let val = *d + carry;
*d = val % 10;
carry = val / 10;
}
while carry > 0 {
digits.push(carry % 10);
carry /= 10;
}
}
let s: String = digits
.iter()
.rev()
.map(|d| char::from_digit(*d, 10).unwrap())
.collect();
let trimmed = s.trim_start_matches('0');
if trimmed.is_empty() {
"0".to_string()
} else {
trimmed.to_string()
}
}