//! Semiring use std::collections::HashMap; use std::fmt::Debug; use itertools::Itertools; /// Semiring. /// /// Consult . pub trait Semiring: Debug + Clone + PartialEq { /// Additive identity. fn zero() -> Self; /// Multiplicative identity. fn one() -> Self; /// Addition operation. fn add(&self, rhs: &Self) -> Self; /// Multiplication operation. fn mul(&self, rhs: &Self) -> Self; } /// Converts integer to semiring value. pub fn from_usize(value: usize) -> T { let mut result = T::zero(); let one = T::one(); for _ in 0..value { result = T::add(&result, &one); } result } impl Semiring for u64 { fn zero() -> Self { 0 } fn one() -> Self { 1 } fn add(&self, rhs: &Self) -> Self { self + rhs } fn mul(&self, rhs: &Self) -> Self { self * rhs } } impl Semiring for i64 { fn zero() -> Self { 0 } fn one() -> Self { 1 } fn add(&self, rhs: &Self) -> Self { self + rhs } fn mul(&self, rhs: &Self) -> Self { self * rhs } } impl Semiring for f64 { fn zero() -> Self { 0.0 } fn one() -> Self { 1.0 } fn add(&self, rhs: &Self) -> Self { self + rhs } fn mul(&self, rhs: &Self) -> Self { self * rhs } } /// Polynomials with coefficient in `C`. /// /// For example, polynomial `x^2 + 5x + 6` is represented in `Polynomial` as follows: /// /// ```ignore /// Polynomial { /// coefficients: { /// 2: 1, /// 1: 5, /// 0: 6, /// }, /// } /// ``` #[derive(Debug, Clone, PartialEq, Eq)] pub struct Polynomial { coefficients: HashMap, } impl Semiring for Polynomial { fn zero() -> Self { Self { coefficients: HashMap::new(), } } fn one() -> Self { Self { coefficients: HashMap::from([(0, C::one())]), } } fn add(&self, rhs: &Self) -> Self { let mut coefficients = self.coefficients.clone(); for (deg, coef) in &rhs.coefficients { _ = coefficients .entry(*deg) .and_modify(|value| *value = value.add(coef)) .or_insert(coef.clone()); } coefficients.retain(|_, coef| *coef != C::zero()); Self { coefficients } } fn mul(&self, rhs: &Self) -> Self { let mut coefficients: HashMap = HashMap::new(); for (ldeg, lcoef) in &self.coefficients { for (rdeg, rcoef) in &rhs.coefficients { let coef = lcoef.mul(rcoef); _ = coefficients .entry(ldeg + rdeg) .and_modify(|value| *value = value.add(&coef)) .or_insert(coef); } } coefficients.retain(|_, coef| *coef != C::zero()); Self { coefficients } } } impl Polynomial { /// Constructs polynomial `x`. pub fn x() -> Self { Self { coefficients: HashMap::from([(1, C::one())]), } } /// Evaluates the polynomial with the given value. pub fn eval(&self, value: C) -> C { let mut result = C::zero(); for (deg, coef) in &self.coefficients { let mut xn = C::one(); let mut n = 0; while n < *deg { xn = xn.mul(&value); n += 1; } result = result.add(&coef.mul(&xn)); } result } /// Constructs polynomial `ax^n`. pub fn term(a: C, n: u64) -> Self { Self { coefficients: HashMap::from([(n, a)]), } } } impl From for Polynomial { fn from(value: C) -> Self { Self { coefficients: HashMap::from([(0, value)]), } } } /// Given a string `s`, parse it into a `Polynomial`. /// You may assume that `s` follows the criteria below. /// Therefore, you do not have to return `Err`. /// /// Assumptions: /// - Each term is separated by ` + `. /// - Each term is one of the following form: `a`, `x`, `ax`, `x^n`, and `ax^n`, where `a` is a /// `usize` number and `n` is a `u64` number. This `a` should then be converted to a `C` type. /// - In `a`, it is guaranteed that `a >= 1`. /// - In `ax` and `ax^n`, it is guaranteed that `a >= 2`. /// - In `x^n` and `ax^n`, it is guaranteed that `n >= 2`. /// - All terms have unique degrees. /// /// Consult `assignment06/grade.rs` for example valid strings. /// /// Hint: `.split`, `.parse`, and `Polynomial::term` impl std::str::FromStr for Polynomial { type Err = (); // Ignore this for now... fn from_str(s: &str) -> Result { let mut coefficients: HashMap = HashMap::new(); for term in s.split(" + ") { let has_x: bool = term.contains('x'); let has_power = term.contains('^'); let deg: u64 = if has_power { term.split("^").nth(1).unwrap().parse().unwrap() } else if has_x { 1 } else { 0 }; let coef: usize = if has_x && term.find("x").unwrap() == 0 { 1 } else if has_x { term.split("x").nth(0).unwrap().parse().unwrap() } else { term.parse().unwrap() }; let _unused = coefficients.insert(deg, from_usize(coef)); } Ok(Self { coefficients }) } }