1use 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#[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())) }
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; self.1 += &rhs.1; }
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; 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 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> { 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> { 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 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 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 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}