Skip to main content

yui_core/conc/poly/
var.rs

1//! Univariate monomial `X^d`. With `I = isize` it is a Laurent monomial
2//! (allowing `d < 0`).
3//!
4//! See: <https://en.wikipedia.org/wiki/Monomial>,
5//! <https://en.wikipedia.org/wiki/Laurent_polynomial>
6
7use std::fmt::{Display, Debug};
8use std::ops::{AddAssign, Mul, MulAssign, DivAssign, SubAssign, Div};
9use std::str::FromStr;
10use num_traits::{Zero, One, ToPrimitive, Pow, FromPrimitive};
11use auto_impl_ops::auto_ops;
12
13use crate::abst::{MathType, IndexType};
14use crate::lc::LcKey;
15use crate::util::format::superscript;
16use crate::util::parse_err::ParseErr;
17use super::{Mono, MonoOrd};
18
19/// A univariate monomial `X^d`, where the variable symbol `X` is a const
20/// generic and `I` is the exponent type (`usize` or `isize`).
21#[derive(Clone, Copy, PartialEq, Eq, Hash, Default, PartialOrd, Ord)]
22#[cfg_attr(feature = "serde", derive(serde_with::DeserializeFromStr))]
23pub struct Var<const X: char, I>(
24    I
25);
26
27impl<const X: char, I> Var<X, I> {
28    pub fn var_symbol() -> char {
29        X
30    }
31
32    pub fn eval<R>(&self, x: &R) -> R
33    where R: Mul<Output = R>, I: Copy, for<'x> &'x R: Pow<I, Output = R> {
34        x.pow(self.0)
35    }
36
37    fn to_string_u(&self, unicode: bool) -> String
38    where I: ToPrimitive {
39        fmt_mono(&X.to_string(), &self.0, unicode)
40    }
41}
42
43impl<const X: char, I> FromStr for Var<X, I>
44where I: Zero + FromStr + FromPrimitive {
45    type Err = ParseErr;
46    fn from_str(s: &str) -> Result<Self, Self::Err> {
47        if s == "1" {
48            Ok(Self(I::zero()))
49        } else if let Some(d) = parse_mono_deg(&X.to_string(), s) {
50            Ok(Self(d))
51        } else {
52            Err(ParseErr::invalid(s, &format!("a monomial in {X}")))
53        }
54    }
55}
56
57impl<const X: char, I> From<I> for Var<X, I> {
58    fn from(d: I) -> Self {
59        Self(d)
60    }
61}
62
63impl<const X: char, I> One for Var<X, I>
64where I: for<'x >AddAssign<&'x I> + Zero {
65    fn one() -> Self {
66        Self::from(I::zero()) // x^0 = 1.
67    }
68}
69
70// note: clippy flags the `+`/`-` below, but exponents add on × and subtract on ÷.
71// `#[allow]` doesn't help: `#[auto_ops]` regenerates these impls and drops it.
72#[auto_ops]
73impl<const X: char, I> MulAssign<&Var<X, I>> for Var<X, I>
74where I: for<'x >AddAssign<&'x I> {
75    fn mul_assign(&mut self, rhs: &Var<X, I>) {
76        self.0 += &rhs.0 // x^i * x^j = x^{i+j}
77    }
78}
79
80#[auto_ops]
81impl<const X: char, I> DivAssign<&Var<X, I>> for Var<X, I>
82where I: for<'x >SubAssign<&'x I> {
83    fn div_assign(&mut self, rhs: &Var<X, I>) {
84        self.0 -= &rhs.0 // x^i / x^j = x^{i-j}
85    }
86}
87
88impl<const X: char, I> Display for Var<X, I>
89where I: ToPrimitive {
90    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91        let s = self.to_string_u(true);
92        f.write_str(&s)
93    }
94}
95
96impl<const X: char, I> Debug for Var<X, I>
97where I: ToPrimitive {
98    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
99        Display::fmt(self, f)
100    }
101}
102
103#[cfg(feature = "serde")]
104impl<const X: char, I> serde::Serialize for Var<X, I>
105where I: ToPrimitive {
106    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
107    where S: serde::Serializer {
108        serializer.serialize_str(&self.to_string_u(false))
109    }
110}
111
112impl<const X: char, I> MathType for Var<X, I>
113where I: IndexType + ToPrimitive {
114    fn math_symbol() -> String {
115        format!("{X}")
116    }
117}
118
119impl<const X: char, I> MonoOrd for Var<X, I>
120where I: IndexType + ToPrimitive {
121    fn cmp_lex(&self, other: &Self) -> std::cmp::Ordering {
122        I::cmp(&self.0, &other.0)
123    }
124
125    fn cmp_grlex(&self, other: &Self) -> std::cmp::Ordering {
126        I::cmp(&self.0, &other.0)
127    }
128}
129
130impl<const X: char, I> LcKey for Var<X, I>
131where I: IndexType + ToPrimitive {}
132
133macro_rules! impl_univar_unsigned {
134    ($I:ty) => {
135        impl<const X: char> Mono for Var<X, $I> {
136            type Deg = $I;
137
138            fn deg(&self) -> Self::Deg {
139                self.0
140            }
141
142            fn is_unit(&self) -> bool {
143                self.0.is_zero()
144            }
145
146            fn inv(&self) -> Option<Self> {
147                if self.is_unit() {
148                    Some(Self(0))
149                } else {
150                    None
151                }
152            }
153
154            fn divides(&self, other: &Self) -> bool {
155                self.0 <= other.0
156            }
157        }
158    };
159}
160
161macro_rules! impl_univar_signed {
162    ($I:ty) => {
163        impl<const X: char> Mono for Var<X, $I> {
164            type Deg = $I;
165
166            fn deg(&self) -> Self::Deg {
167                self.0
168            }
169
170            fn is_unit(&self) -> bool {
171                true
172            }
173
174            fn inv(&self) -> Option<Self> { // (x^i)^{-1} = x^{-i}
175                Some(Self(-self.0))
176            }
177
178            fn divides(&self, _other: &Self) -> bool {
179                true
180            }
181        }
182    };
183}
184
185impl_univar_unsigned!(usize);
186impl_univar_signed!  (isize);
187
188mod tex {
189    use crate::util::tex::TeX;
190    use super::*;
191
192    impl<const X: char, I> TeX for Var<X, I>
193    where I: ToPrimitive {
194        fn tex_math_symbol() -> String {
195            String::from(X)
196        }
197        fn tex_string(&self) -> String {
198            self.to_string_u(false)
199        }
200    }
201}
202
203pub(crate) fn fmt_mono<I>(x: &str, d: &I, unicode: bool) -> String
204where I: ToPrimitive {
205    let d = d.to_isize().unwrap();
206    if d.is_zero() {
207        "1".to_string()
208    } else if d.is_one() {
209        x.to_string()
210    } else if unicode {
211        let e = superscript(d);
212        format!("{x}{e}")
213    } else {
214        let d = d.to_string();
215        if d.len() == 1 {
216            format!("{x}^{d}")
217        } else {
218            format!("{x}^{{{d}}}")
219        }
220    }
221}
222
223pub(crate) fn parse_mono_deg<I>(x: &str, s: &str) -> Option<I>
224where I: FromStr + FromPrimitive {
225    use regex::Regex;
226
227    let p1 = format!(r"^{x}\^(-?[0-9]+)$");
228    let p2 = format!(r"^{x}\^\{{(-?[0-9]+)\}}$");
229
230    let r1 = Regex::new(&p1).unwrap();
231    let r2 = Regex::new(&p2).unwrap();
232
233    if s == "1" {
234        I::from_i32(0)
235    } else if s == x {
236        I::from_i32(1)
237    } else if let Some(c) = r1.captures(s) {
238        I::from_str(&c[1]).ok()
239    } else if let Some(c) = r2.captures(s) {
240        I::from_str(&c[1]).ok()
241    } else {
242        None
243    }
244}
245
246#[cfg(test)]
247mod tests {
248    use super::*;
249
250    #[test]
251    fn init() {
252        type M = Var<'X',usize>;
253        let x = M::from;
254
255        let d = x(2);
256
257        assert_eq!(d.0, 2);
258        assert_eq!(d.deg(), 2);
259        assert_eq!(M::var_symbol(), 'X');
260    }
261
262    #[test]
263    fn from_str() {
264        type M = Var<'X',isize>;
265        let x = M::from;
266
267        assert_eq!(M::from_str("1"), Ok(M::one()));
268        assert_eq!(M::from_str("X"), Ok(x(1)));
269        assert_eq!(M::from_str("X^2"), Ok(x(2)));
270        assert_eq!(M::from_str("X^{-2}"), Ok(x(-2)));
271        assert!(M::from_str("2").is_err());
272        assert!(M::from_str("x").is_err());
273
274        // unbraced, beyond one digit and negative
275        assert_eq!(M::from_str("X^23"), Ok(x(23)));
276        assert_eq!(M::from_str("X^-2"), Ok(x(-2)));
277    }
278
279    #[test]
280    fn display() {
281        type M = Var<'X', isize>;
282        let x = M::from;
283
284        let d = x(0);
285        assert_eq!(&d.to_string(), "1");
286
287        let d = x(1);
288        assert_eq!(&d.to_string(), "X");
289
290        let d = x(2);
291        assert_eq!(&d.to_string(), "X²");
292
293        let d = x(-1);
294        assert_eq!(&d.to_string(), "X⁻¹");
295
296        let d = x(-2);
297        assert_eq!(&d.to_string(), "X⁻²");
298    }
299
300    #[test]
301    fn neg_opt_unsigned() {
302        type M = Var<'X',usize>;
303        let x = M::from;
304
305        let d = x(0);
306        assert_eq!(d.inv(), Some(x(0)));
307
308        let d = x(1);
309        assert_eq!(d.inv(), None);
310    }
311
312    #[test]
313    fn neg_opt_signed() {
314        type M = Var<'X',isize>;
315        let x = M::from;
316
317        let d = x(0);
318        assert_eq!(d.inv(), Some(x(0)));
319
320        let d = x(1);
321        assert_eq!(d.inv(), Some(x(-1)));
322
323        let d = x(-3);
324        assert_eq!(d.inv(), Some(x(3)));
325    }
326
327    #[test]
328    fn eval() {
329        type M = Var<'X', usize>;
330        let x = M::from;
331
332        let d = x(0);
333        assert_eq!(d.eval(&2), 1);
334
335        let d = x(1);
336        assert_eq!(d.eval(&2), 2);
337
338        let d = x(2);
339        assert_eq!(d.eval(&2), 4);
340    }
341
342    #[test]
343    fn ord() {
344        type M = Var<'X', isize>;
345        let x = M::from;
346
347        assert!(x(0) < x(1));
348        assert!(x(1) < x(2));
349        assert!(x(-1) < x(0));
350    }
351
352    #[test]
353    #[cfg(feature = "serde")]
354    fn serialize() {
355        type M = Var<'X', isize>;
356        let x = M::from;
357
358        let d = x(0);
359        let ser = serde_json::to_string(&d).unwrap();
360        let des = serde_json::from_str::<M>(&ser).unwrap();
361
362        assert_eq!(&ser, "\"1\"");
363        assert_eq!(d, des);
364
365        let d = x(1);
366        let ser = serde_json::to_string(&d).unwrap();
367        let des = serde_json::from_str::<M>(&ser).unwrap();
368
369        assert_eq!(&ser, "\"X\"");
370        assert_eq!(d, des);
371
372        let d = x(2);
373        let ser = serde_json::to_string(&d).unwrap();
374        let des = serde_json::from_str::<M>(&ser).unwrap();
375
376        assert_eq!(&ser, "\"X^2\"");
377        assert_eq!(d, des);
378
379        let d = x(21);
380        let ser = serde_json::to_string(&d).unwrap();
381        let des = serde_json::from_str::<M>(&ser).unwrap();
382
383        assert_eq!(&ser, "\"X^{21}\"");
384        assert_eq!(d, des);
385
386        let d = x(-2);
387        let ser = serde_json::to_string(&d).unwrap();
388        let des = serde_json::from_str::<M>(&ser).unwrap();
389
390        assert_eq!(&ser, "\"X^{-2}\"");
391        assert_eq!(d, des);
392    }
393}