Skip to main content

yui_core/conc/poly/
var2.rs

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