rust_polynomial/
mono.rs

1use std::{
2    default::Default,
3    fmt::{Debug, Display, Error},
4    i64,
5    iter::Sum,
6    num::IntErrorKind,
7    ops::{Add, Div, Mul, Neg},
8    str::FromStr,
9};
10
11use num::{Num, NumCast, Signed};
12
13/// Trait pattern to allow only **numbers** for generic value
14pub trait MonomialValue:
15    Num + NumCast + Signed + Copy + Default + Debug + Display + FromStr + PartialOrd
16{
17}
18
19impl<T> MonomialValue for T where
20    T: Num + NumCast + Signed + Copy + Default + Debug + Display + FromStr + PartialOrd
21{
22}
23
24/// [Monomial](https://en.wikipedia.org/wiki/Monomial) representation
25#[derive(Default, Debug, PartialEq, PartialOrd, Clone, Copy)]
26pub struct Monomial<T> {
27    pub(crate) value: T,
28    pub(crate) exp: i32,
29}
30
31impl<T: MonomialValue> Monomial<T> {
32    ///  - `value`: Coefficient
33    ///  - `exp`: Exponent
34    pub fn new(value: T, mut exp: i32) -> Monomial<T> {
35        if T::is_zero(&value) {
36            exp = 0;
37        }
38
39        Monomial { value, exp }
40    }
41
42    pub fn get_value(&self) -> T {
43        self.value
44    }
45
46    pub fn get_exp(&self) -> i32 {
47        self.exp
48    }
49
50    /// Check if other `Monomial` has same **exponent**
51    pub fn is_operable(&self, other: &Self) -> bool {
52        self.exp == other.exp
53    }
54}
55
56/// # Example expresion
57///
58///```rust
59/// let str = "4x^2";
60///```
61impl<T: MonomialValue> TryFrom<&str> for Monomial<T> {
62    type Error = &'static str;
63
64    fn try_from(value: &str) -> Result<Self, Self::Error> {
65        let clean_value = value
66            .trim()
67            .to_lowercase()
68            .replace(" ", "")
69            .replace("^", "")
70            .replace("+", "");
71
72        let mut split: Vec<&str> = clean_value.split("x").collect();
73
74        if split.is_empty() {
75            return Ok(Monomial::default());
76        }
77
78        if "-" == split[0] {
79            split[0] = "-1";
80        }
81
82        let base = match split[0].parse::<T>() {
83            Ok(v) => v,
84            Err(_) if split[0].is_empty() => T::one(),
85            Err(_) => return Err("Not valid base"),
86        };
87
88        let exp = match split.len() {
89            1 => 0,
90            2 => match split[1].parse::<i32>() {
91                Ok(v) => v,
92                Err(err) if err.kind() == &IntErrorKind::Empty => 1,
93                Err(_) => return Err("Not valid exponent"),
94            },
95            _ => return Err("To much symbols"),
96        };
97
98        Ok(Monomial { value: base, exp })
99    }
100}
101
102impl<T: MonomialValue> Neg for Monomial<T> {
103    type Output = Self;
104
105    fn neg(self) -> Self::Output {
106        Monomial::new(-self.value, self.exp)
107    }
108}
109
110impl<T: MonomialValue> Add for Monomial<T> {
111    type Output = Result<Self, &'static str>;
112
113    fn add(self, rhs: Self) -> Self::Output {
114        if !self.is_operable(&rhs) {
115            return Err("Monomials only allow add same exponent");
116        }
117
118        Ok(Monomial::new(self.value + rhs.value, self.exp))
119    }
120}
121
122impl<T: MonomialValue> Mul for Monomial<T> {
123    type Output = Self;
124
125    fn mul(self, rhs: Self) -> Self::Output {
126        Monomial::new(self.value * rhs.value, self.exp + rhs.exp)
127    }
128}
129
130impl<T: MonomialValue> Div for Monomial<T> {
131    type Output = Self;
132
133    fn div(self, rhs: Self) -> Self::Output {
134        Monomial::new(self.value / rhs.value, self.exp - rhs.exp)
135    }
136}
137
138impl<T: MonomialValue> Sum<Self> for Monomial<T> {
139    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
140        let mut exp = 0;
141        let mut sum = T::zero();
142        for mono in iter {
143            exp = mono.get_exp();
144            sum = sum + mono.get_value();
145        }
146
147        Monomial::new(sum, exp)
148    }
149}
150
151/// # Example expresion
152///
153///```rust
154/// let str = "4x^2";
155///```
156impl<T: MonomialValue> Display for Monomial<T> {
157    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
158        let val: i64 = T::to_i64(&self.value).ok_or(Error)?;
159        let base: String = match val {
160            -1 if self.exp == 0 => "-1".to_string(),
161            -1 => "-".to_string(),
162            1 if self.exp == 0 => "1".to_string(),
163            1 => "".to_string(),
164            _ => format!("{}", self.value),
165        };
166
167        let exp: String = match self.exp {
168            0 => "".to_string(),
169            1 => "x".to_string(),
170            _ => format!("x^{}", self.exp),
171        };
172
173        write!(f, "{}{}", base, exp)
174    }
175}