mirror of
https://github.com/kmc7468/cs220.git
synced 2025-12-14 22:18:46 +00:00
243 lines
5.5 KiB
Rust
243 lines
5.5 KiB
Rust
//! Semiring
|
|
|
|
use std::collections::HashMap;
|
|
use std::fmt::Debug;
|
|
|
|
use itertools::Itertools;
|
|
|
|
/// Semiring.
|
|
///
|
|
/// Consult <https://en.wikipedia.org/wiki/Semiring>.
|
|
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<T: Semiring>(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<u64>` as follows:
|
|
///
|
|
/// ```ignore
|
|
/// Polynomial {
|
|
/// coefficients: {
|
|
/// 2: 1,
|
|
/// 1: 5,
|
|
/// 0: 6,
|
|
/// },
|
|
/// }
|
|
/// ```
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub struct Polynomial<C: Semiring> {
|
|
coefficients: HashMap<u64, C>,
|
|
}
|
|
|
|
impl<C: Semiring> Semiring for Polynomial<C> {
|
|
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<u64, C> = 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<C: Semiring> Polynomial<C> {
|
|
/// 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<C: Semiring> From<C> for Polynomial<C> {
|
|
fn from(value: C) -> Self {
|
|
Self {
|
|
coefficients: HashMap::from([(0, value)]),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Given a string `s`, parse it into a `Polynomial<C>`.
|
|
/// 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<C: Semiring> std::str::FromStr for Polynomial<C> {
|
|
type Err = (); // Ignore this for now...
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
let mut coefficients: HashMap<u64, C> = 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 })
|
|
}
|
|
}
|