1use std::collections::HashMap;
17use std::fmt::{Display, Debug};
18use std::ops::{Add, AddAssign, Neg, Sub, SubAssign, Mul, MulAssign};
19use itertools::Itertools;
20use num_traits::Zero;
21use auto_impl_ops::auto_ops;
22use crate::abst::{MathType, AddMon, AddMonOps, AddGrp, AddGrpOps, Ring, RingOps, RMod, RModOps};
23
24use super::lc_key::*;
25use super::lc_data::{LcData, LcDataIter, LcDataIntoIter};
26
27#[derive(PartialEq, Eq, Clone, Default, Debug)]
31#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
32#[cfg_attr(feature = "serde", serde(transparent))]
33pub struct Lc<X, R>
34where
35 X: LcKey,
36 R: Ring, for<'x> &'x R: RingOps<R>
37{
38 data: LcData<X, R>,
39 #[cfg_attr(feature = "serde", serde(skip))]
40 r_zero: R
41}
42
43impl<X, R> Lc<X, R>
44where
45 X: LcKey,
46 R: Ring, for<'x> &'x R: RingOps<R>
47{
48 pub fn new() -> Self {
49 Self { data: LcData::Zero, r_zero: R::zero() }
50 }
51
52 pub fn nterms(&self) -> usize {
53 self.data.len()
54 }
55
56 pub fn any_term(&self) -> Option<(&X, &R)> {
57 self.iter().next()
58 }
59
60 pub fn keys(&self) -> impl Iterator<Item = &X> {
61 self.iter().map(|(k, _)| k)
62 }
63
64 pub fn is_singleton(&self) -> bool {
65 self.nterms() == 1 &&
66 self.iter().next().unwrap().1.is_one()
67 }
68
69 pub fn as_singleton(&self) -> Option<X> {
70 if !self.is_singleton() {
71 None?
72 }
73 self.iter().next().map(|(x, _)| x.clone())
74 }
75
76 pub fn coeff(&self, x: &X) -> &R {
77 self.data.get(x).unwrap_or(&self.r_zero)
78 }
79
80 pub fn iter(&self) -> LcDataIter<'_, X, R> {
81 self.data.iter()
82 }
83
84 pub fn map<Y, S, F>(self, f: F) -> Lc<Y, S>
85 where
86 Y: LcKey,
87 S: Ring, for<'x> &'x S: RingOps<S>,
88 F: Fn(X, R) -> (Y, S)
89 {
90 self.into_iter().map(|(x, r)| f(x, r)).collect()
91 }
92
93 pub fn map_coeffs<S, F>(self, f: F) -> Lc<X, S>
94 where
95 S: Ring, for<'x> &'x S: RingOps<S>,
96 F: Fn(R) -> S
97 {
98 self.map(|x, r| (x, f(r)))
99 }
100
101 pub fn map_keys<Y, F>(self, f: F) -> Lc<Y, R>
102 where
103 Y: LcKey,
104 F: Fn(X) -> Y
105 {
106 self.map(|x, r| (f(x), r))
107 }
108
109 pub fn map_ref<Y, S, F>(&self, f: F) -> Lc<Y, S>
110 where
111 Y: LcKey,
112 S: Ring, for<'x> &'x S: RingOps<S>,
113 F: Fn(&X, &R) -> (Y, S)
114 {
115 self.iter().map(|(x, r)| f(x, r)).collect()
116 }
117
118 pub fn filter<F>(self, f: F) -> Self
119 where F: Fn(&X) -> bool {
120 self.into_iter().filter(|(x, _)| f(x)).collect()
121 }
122
123 pub fn filtered<F>(&self, f: F) -> Self
124 where F: Fn(&X) -> bool {
125 self.iter().filter_map(|(x, a)|
126 if f(x) {
127 Some((x.clone(), a.clone()))
128 } else {
129 None
130 }
131 ).collect()
132 }
133
134 pub fn add_pairs<I>(&mut self, pairs: I)
137 where I: IntoIterator<Item = (X, R)> {
138 for (x, r) in pairs {
139 self.data.add_pair_unreduced(x, r);
140 }
141 self.data.reduce();
142 }
143
144 pub fn add_pairs_ref<'a, I>(&mut self, pairs: I)
147 where I: IntoIterator<Item = (&'a X, R)>, X: 'a {
148 for (x, r) in pairs {
149 self.data.add_pair_ref_unreduced(x, r);
150 }
151 self.data.reduce();
152 }
153
154 pub fn add_pair(&mut self, rhs: (X, R)) {
155 self.add_pairs([rhs]);
156 }
157
158 pub fn add_pair_ref(&mut self, rhs: (&X, R)) {
159 self.add_pairs_ref([rhs]);
160 }
161
162 pub fn apply<F, Y: LcKey>(&self, f: F) -> Lc<Y, R>
163 where F: Fn(&X) -> Lc<Y, R> {
164 self.iter().flat_map(|(x, r)| {
165 f(x).into_iter().map(move |(y, s)| {
166 (y, r * &s)
167 })
168 }).collect()
169 }
170
171 pub fn apply_bilin<Y, Z, F>(&self, other: &Lc<Y, R>, x_map: F) -> Lc<Z, R>
172 where Y: LcKey, Z: LcKey, F: Fn(&X, &Y) -> Z {
173 match (&self.data, &other.data) {
174 (LcData::Zero, _) | (_, LcData::Zero) => Lc::zero(),
175 (LcData::Single(x, r), _) =>
176 other.map_ref(|y, s| (x_map(x, y), r * s)),
177 (_, LcData::Single(y, s)) =>
178 self.map_ref(|x, r| (x_map(x, y), r * s)),
179 (LcData::Many(_), LcData::Many(_)) => {
180 let x_map = &x_map;
181 let mut res = Lc::zero();
182 res.add_pairs(self.iter().flat_map(|(x, r)|
183 other.iter().map(move |(y, s)| (x_map(x, y), r * s))
184 ));
185 res
186 }
187 }
188 }
189
190 pub fn sort_terms_by<F>(&self, cmp: F) -> impl Iterator<Item = (&X, &R)>
191 where F: Fn(&X, &X) -> std::cmp::Ordering {
192 self.iter().sorted_by(|(x, _), (y, _)| cmp(x, y))
193 }
194
195 pub fn to_string_by<F>(&self, cmp: F, descending: bool) -> String
196 where F: Fn(&X, &X) -> std::cmp::Ordering {
197 use crate::util::format::lc;
198 if descending {
199 lc( self.sort_terms_by(|x, y| cmp(x, y).reverse()) )
200 } else {
201 lc( self.sort_terms_by(cmp) )
202 }
203 }
204
205 pub fn is_homogeneous<T, F>(&self, f: F) -> bool
206 where T: PartialEq, F: Fn(&X) -> T {
207 self.keys().map(f).all_equal()
208 }
209
210 pub fn homogeneous_value<T, F>(&self, f: F) -> Option<T>
211 where T: PartialEq, F: Fn(&X) -> T {
212 let mut iter = self.keys();
213 let first = f(iter.next()?);
214 if iter.all(|k| f(k) == first) { Some(first) } else { None }
215 }
216}
217
218impl<X, R> From<X> for Lc<X, R>
219where
220 X: LcKey,
221 R: Ring, for<'x> &'x R: RingOps<R>
222{
223 fn from(x: X) -> Self {
224 Self::from((x, R::one()))
225 }
226}
227
228impl<X, R> From<(X, R)> for Lc<X, R>
229where
230 X: LcKey,
231 R: Ring, for<'x> &'x R: RingOps<R>
232{
233 fn from(value: (X, R)) -> Self {
234 Self::from_iter([value])
235 }
236}
237
238impl<X, R> From<HashMap<X, R>> for Lc<X, R>
239where
240 X: LcKey,
241 R: Ring, for<'x> &'x R: RingOps<R>
242{
243 fn from(value: HashMap<X, R>) -> Self {
244 Self::from_iter(value)
245 }
246}
247
248impl<X, R> FromIterator<(X, R)> for Lc<X, R>
249where
250 X: LcKey,
251 R: Ring, for<'x> &'x R: RingOps<R>
252{
253 fn from_iter<T: IntoIterator<Item = (X, R)>>(iter: T) -> Self {
254 let mut res = Self::new();
255 res.add_pairs(iter);
256 res
257 }
258}
259
260impl<X, R> IntoIterator for Lc<X, R>
261where
262 X: LcKey,
263 R: Ring, for<'x> &'x R: RingOps<R>
264{
265 type Item = (X, R);
266 type IntoIter = LcDataIntoIter<X, R>;
267
268 fn into_iter(self) -> Self::IntoIter {
269 self.data.into_iter()
270 }
271}
272
273impl<X, R> Display for Lc<X, R>
274where
275 X: LcKey,
276 R: Ring, for<'x> &'x R: RingOps<R>
277{
278 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
279 f.write_str(&self.to_string_by(X::cmp, false))
280 }
281}
282
283impl<X, R> Zero for Lc<X, R>
284where
285 X: LcKey,
286 R: Ring, for<'x> &'x R: RingOps<R>
287{
288 fn zero() -> Self {
289 Self::new()
290 }
291
292 fn is_zero(&self) -> bool {
293 self.data.is_empty()
294 }
295}
296
297impl<X, R> Neg for Lc<X, R>
298where
299 X: LcKey,
300 R: Ring, for<'x> &'x R: RingOps<R>
301{
302 type Output = Self;
303
304 fn neg(self) -> Self::Output {
305 self.map_coeffs(|r| -r)
306 }
307}
308
309impl<X, R> Neg for &Lc<X, R>
310where
311 X: LcKey,
312 R: Ring, for<'x> &'x R: RingOps<R>
313{
314 type Output = Lc<X, R>;
315
316 fn neg(self) -> Self::Output {
317 self.map_ref(|x, r| (x.clone(), -r))
318 }
319}
320
321#[auto_ops(val_val, ref_val)]
325impl<X, R> AddAssign<Lc<X, R>> for Lc<X, R>
326where
327 X: LcKey,
328 R: Ring, for<'x> &'x R: RingOps<R>
329{
330 fn add_assign(&mut self, rhs: Self) {
331 self.add_pairs(rhs.data);
332 }
333}
334
335#[auto_ops(val_ref, ref_ref)]
336impl<X, R> AddAssign<&Lc<X, R>> for Lc<X, R>
337where
338 X: LcKey,
339 R: Ring, for<'x> &'x R: RingOps<R>
340{
341 fn add_assign(&mut self, rhs: &Self) {
342 self.add_pairs_ref(rhs.data.iter().map(|(x, r)| (x, r.clone())));
343 }
344}
345
346#[auto_ops(val_val, ref_val)]
347impl<X, R> SubAssign<Lc<X, R>> for Lc<X, R>
348where
349 X: LcKey,
350 R: Ring, for<'x> &'x R: RingOps<R>
351{
352 fn sub_assign(&mut self, rhs: Self) {
353 self.add_pairs(rhs.data.into_iter().map(|(x, r)| (x, -r)));
354 }
355}
356
357#[auto_ops(val_ref, ref_ref)]
358impl<X, R> SubAssign<&Lc<X, R>> for Lc<X, R>
359where
360 X: LcKey,
361 R: Ring, for<'x> &'x R: RingOps<R>
362{
363 fn sub_assign(&mut self, rhs: &Self) {
364 self.add_pairs_ref(rhs.data.iter().map(|(x, r)| (x, -r)));
365 }
366}
367
368#[auto_ops]
369impl<X, R> MulAssign<&R> for Lc<X, R>
370where
371 X: LcKey,
372 R: Ring, for<'x> &'x R: RingOps<R>
373{
374 fn mul_assign(&mut self, rhs: &R) {
375 if rhs.is_one() {
376 return
377 }
378
379 self.data.map_coeffs_in_place(|r| r * rhs);
380 }
381}
382
383#[auto_ops]
384impl<X, R> Mul for &Lc<X, R>
385where
386 X: LcMulKey,
387 R: Ring, for<'x> &'x R: RingOps<R>
388{
389 type Output = Lc<X, R>;
390
391 fn mul(self, rhs: Self) -> Self::Output {
392 self.apply_bilin(rhs, |x, y| x.mul_ref(y))
393 }
394}
395
396macro_rules! impl_alg_ops {
397 ($trait:ident) => {
398 impl<X, R> $trait<Self> for Lc<X, R>
399 where X: LcKey, R: Ring, for<'x> &'x R: RingOps<R> {}
400
401 impl<X, R> $trait<Lc<X, R>> for &Lc<X, R>
402 where X: LcKey, R: Ring, for<'x> &'x R: RingOps<R> {}
403 };
404}
405
406impl_alg_ops!(AddMonOps);
407impl_alg_ops!(AddGrpOps);
408
409impl<X, R> MathType for Lc<X, R>
410where
411 X: LcKey,
412 R: Ring, for<'x> &'x R: RingOps<R>
413{
414 fn math_symbol() -> String {
415 format!("{}<{}>", R::math_symbol(), X::math_symbol())
416 }
417}
418
419impl<X, R> AddMon for Lc<X, R>
420where
421 X: LcKey,
422 R: Ring, for<'x> &'x R: RingOps<R>
423{}
424
425impl<X, R> AddGrp for Lc<X, R>
426where
427 X: LcKey,
428 R: Ring, for<'x> &'x R: RingOps<R>
429{}
430
431
432impl<X, R> RModOps<R, Self> for Lc<X, R>
433where
434 X: LcKey,
435 R: Ring, for<'x> &'x R: RingOps<R>
436{}
437
438impl<X, R> RModOps<R, Lc<X, R>> for &Lc<X, R>
439where
440 X: LcKey,
441 R: Ring, for<'x> &'x R: RingOps<R>
442{}
443
444impl<X, R> RMod for Lc<X, R>
445where
446 X: LcKey,
447 R: Ring, for<'x> &'x R: RingOps<R>
448{
449 type R = R;
450}
451
452#[cfg(test)]
453mod tests {
454 use num_traits::Zero;
455 use maplit::hashmap;
456 use crate::abst::{MathType, AddMon};
457 use crate::lc::{AsKey, Lc};
458
459 type X = AsKey<i32>;
460 fn e(i: i32) -> X {
461 X::from(i)
462 }
463
464 #[test]
465 fn math_symbol() {
466 type L = Lc<X, i32>;
467 let symbol = L::math_symbol();
468 assert_eq!(symbol, "Z<Free<i32>>");
469 }
470
471 #[test]
472 fn fmt() {
473 type L = Lc<X, i32>;
474
475 let z = L::from(hashmap!{ e(1) => 1 });
476 assert_eq!(z.to_string(), "<1>");
477
478 let z = L::from(hashmap!{ e(1) => -1 });
479 assert_eq!(z.to_string(), "-<1>");
480
481 let z = L::from(hashmap!{ e(1) => 2 });
482 assert_eq!(z.to_string(), "2<1>");
483
484 let z = L::from(hashmap!{ e(1) => 1, e(2) => 1 });
485 assert_eq!(z.to_string(), "<1> + <2>");
486
487 let z = L::from(hashmap!{ e(1) => -1, e(2) => -1 });
488 assert_eq!(z.to_string(), "-<1> - <2>");
489
490 let z = L::from(hashmap!{ e(1) => 2, e(2) => 3 });
491 assert_eq!(z.to_string(), "2<1> + 3<2>");
492
493 let z = L::from(hashmap!{ e(1) => -2, e(2) => -3 });
494 assert_eq!(z.to_string(), "-2<1> - 3<2>");
495 }
496
497 #[test]
498 fn default() {
499 type L = Lc<X, i32>;
500 let z = L::default();
501 assert!(z.data.is_empty());
502 }
503
504 #[test]
505 fn from_singleton() {
506 type L = Lc<X, i32>;
507 let x = e(0);
508 let z = L::from(x);
509 assert_eq!(z, L::from(hashmap!{ e(0) => 1 }));
510 }
511
512 #[test]
513 fn from_pair() {
514 type L = Lc<X, i32>;
515 let x = e(0);
516 let z = L::from((x, 2));
517 assert_eq!(z, L::from(hashmap!{ e(0) => 2 }));
518 }
519
520 #[test]
521 fn from_iter() {
522 type L = Lc<X, i32>;
523 let z = L::from_iter([(e(0), 1), (e(1), 0), (e(2), 2)]);
524
525 assert!(!z.is_zero());
526 assert_eq!(z.nterms(), 2);
527 assert_eq!(z.coeff(&e(0)), &1);
528 assert_eq!(z.coeff(&e(2)), &2);
529 }
530
531 #[test]
532 fn into_singleton() {
533 type L = Lc<X, i32>;
534 let z = L::from(e(0));
535
536 assert!(z.is_singleton());
537 assert_eq!(z.as_singleton(), Some(e(0)));
538
539 let z = L::from((e(0), 2));
540 assert!(!z.is_singleton());
541 assert_eq!(z.as_singleton(), None);
542
543 let z = L::from_iter([(e(0), 1), (e(1), 1)]);
544 assert!(!z.is_singleton());
545 assert_eq!(z.as_singleton(), None);
546 }
547
548 #[test]
549 fn eq() {
550 type L = Lc<X, i32>;
551 let z1 = L::from(hashmap!{ e(1) => 1, e(2) => 2 });
552 let z2 = L::from(hashmap!{ e(2) => 2, e(1) => 1 });
553 let z3 = L::from(hashmap!{ e(1) => 1 });
554
555 assert_eq!(z1, z2);
556 assert_ne!(z1, z3);
557 }
558
559 #[test]
560 fn zero() {
561 type L = Lc<X, i32>;
562 let z = L::zero();
563
564 assert!(z.data.is_empty());
565 assert!(z.is_zero());
566
567 let z = L::from(hashmap!{ e(1) => 1 });
568
569 assert!(!z.data.is_empty());
570 assert!(!z.is_zero());
571 }
572
573 #[test]
574 fn add_pair_reduced() {
575 type L = Lc<X, i32>;
576
577 let mut z = L::from(hashmap!{ e(1) => 1, e(2) => 2, e(3) => 1 });
579 z.add_pair((e(1), -1));
580 assert_eq!(z, L::from(hashmap!{ e(2) => 2, e(3) => 1 }));
581 assert_eq!(z.nterms(), 2);
582
583 z.add_pair((e(2), -1));
584 z.add_pair((e(3), -1));
585 assert_eq!(z, L::from(hashmap!{ e(2) => 1 }));
586 assert_eq!(z.nterms(), 1);
587 }
588
589 #[test]
590 fn add_pairs_reduced() {
591 type L = Lc<X, i32>;
592
593 let mut z = L::from(hashmap!{ e(1) => 1, e(2) => 2, e(3) => 1 });
594 z.add_pairs([(e(1), -1), (e(2), -1), (e(3), -1)]);
595
596 assert_eq!(z, L::from(hashmap!{ e(2) => 1 }));
597 assert_eq!(z.nterms(), 1);
598
599 z.add_pairs([(e(2), -1)]);
600 assert!(z.is_zero());
601 assert_eq!(z.nterms(), 0);
602 }
603
604 #[test]
605 fn add() {
606 type L = Lc<X, i32>;
607 let z1 = L::from(hashmap!{ e(1) => 1, e(2) => 2 });
608 let z2 = L::from(hashmap!{ e(2) => 20, e(3) => 30 });
609 let w = z1 + z2;
610
611 assert_eq!(w, L::from(hashmap!{ e(1) => 1, e(2) => 22, e(3) => 30 }));
612 }
613
614 #[test]
615 fn add_ref() {
616 type L = Lc<X, i32>;
617 let z1 = L::from(hashmap!{ e(1) => 1, e(2) => 2 });
618 let z2 = L::from(hashmap!{ e(2) => 20, e(3) => 30 });
619 let w = &z1 + &z2;
620
621 assert_eq!(w, L::from(hashmap!{ e(1) => 1, e(2) => 22, e(3) => 30 }));
622 }
623
624 #[test]
625 fn add_assign() {
626 type L = Lc<X, i32>;
627 let mut z1 = L::from(hashmap!{ e(1) => 1, e(2) => 2 });
628 let z2 = L::from(hashmap!{ e(2) => 20, e(3) => 30 });
629 z1 += z2;
630
631 assert_eq!(z1, L::from(hashmap!{ e(1) => 1, e(2) => 22, e(3) => 30 }));
632 }
633
634 #[test]
635 fn add_assign_ref() {
636 type L = Lc<X, i32>;
637 let mut z1 = L::from(hashmap!{ e(1) => 1, e(2) => 2 });
638 let z2 = L::from(hashmap!{ e(2) => 20, e(3) => 30 });
639 z1 += &z2;
640
641 assert_eq!(z1, L::from(hashmap!{ e(1) => 1, e(2) => 22, e(3) => 30 }));
642 }
643
644 #[test]
645 fn sum() {
646 type L = Lc<X, i32>;
647 let z1 = L::from(hashmap!{ e(1) => 1, e(2) => 2 });
648 let z2 = L::from(hashmap!{ e(2) => 20, e(3) => 30 });
649 let z3 = L::from(hashmap!{ e(3) => 300, e(4) => 400 });
650 let w = L::sum([z1, z2, z3]);
651
652 assert_eq!(w, L::from(hashmap!{ e(1) => 1, e(2) => 22, e(3) => 330, e(4) => 400 }));
653 }
654
655 #[test]
656 fn sum_ref() {
657 type L = Lc<X, i32>;
658 let z1 = L::from(hashmap!{ e(1) => 1, e(2) => 2 });
659 let z2 = L::from(hashmap!{ e(2) => 20, e(3) => 30 });
660 let z3 = L::from(hashmap!{ e(3) => 300, e(4) => 400 });
661 let w = L::sum([&z1, &z2, &z3]);
662
663 assert_eq!(w, L::from(hashmap!{ e(1) => 1, e(2) => 22, e(3) => 330, e(4) => 400 }));
664 }
665
666 #[test]
667 fn neg() {
668 type L = Lc<X, i32>;
669 let z = L::from(hashmap!{ e(1) => 1, e(2) => 2 });
670 assert_eq!(-z, L::from(hashmap!{ e(1) => -1, e(2) => -2 }));
671 }
672
673 #[test]
674 fn neg_ref() {
675 type L = Lc<X, i32>;
676 let z = L::from(hashmap!{ e(1) => 1, e(2) => 2 });
677 assert_eq!(-(&z), L::from(hashmap!{ e(1) => -1, e(2) => -2 }));
678 }
679
680 #[test]
681 fn sub() {
682 type L = Lc<X, i32>;
683 let z1 = L::from(hashmap!{ e(1) => 1, e(2) => 2 });
684 let z2 = L::from(hashmap!{ e(2) => 20, e(3) => 30 });
685 let w = z1 - z2;
686
687 assert_eq!(w, L::from(hashmap!{ e(1) => 1, e(2) => -18, e(3) => -30 }));
688 }
689
690 #[test]
691 fn sub_ref() {
692 type L = Lc<X, i32>;
693 let z1 = L::from(hashmap!{ e(1) => 1, e(2) => 2 });
694 let z2 = L::from(hashmap!{ e(2) => 20, e(3) => 30 });
695 let w = &z1 - &z2;
696
697 assert_eq!(w, L::from(hashmap!{ e(1) => 1, e(2) => -18, e(3) => -30 }));
698 }
699
700 #[test]
701 fn sub_assign() {
702 type L = Lc<X, i32>;
703 let mut z1 = L::from(hashmap!{ e(1) => 1, e(2) => 2 });
704 let z2 = L::from(hashmap!{ e(2) => 20, e(3) => 30 });
705 z1 -= z2;
706
707 assert_eq!(z1, L::from(hashmap!{ e(1) => 1, e(2) => -18, e(3) => -30 }));
708 }
709
710 #[test]
711 fn sub_assign_ref() {
712 type L = Lc<X, i32>;
713 let mut z1 = L::from(hashmap!{ e(1) => 1, e(2) => 2 });
714 let z2 = L::from(hashmap!{ e(2) => 20, e(3) => 30 });
715 z1 -= &z2;
716
717 assert_eq!(z1, L::from(hashmap!{ e(1) => 1, e(2) => -18, e(3) => -30 }));
718 }
719
720 #[test]
724 fn op_forms_consistent() {
725 use std::collections::HashMap;
726 type L = Lc<X, i32>;
727
728 let lc = |pairs: &[(i32, i32)]| -> L {
729 L::from_iter(pairs.iter().map(|&(k, c)| (e(k), c)))
730 };
731 let reference = |a: &[(i32, i32)], b: &[(i32, i32)], sign: i32| -> L {
732 let mut m: HashMap<i32, i32> = HashMap::new();
733 for &(k, c) in a { *m.entry(k).or_default() += c; }
734 for &(k, c) in b { *m.entry(k).or_default() += sign * c; }
735 L::from_iter(m.into_iter().filter(|&(_, c)| c != 0).map(|(k, c)| (e(k), c)))
736 };
737
738 let cases: &[&[(i32, i32)]] = &[
739 &[],
740 &[(1, 5)],
741 &[(1, -5)],
742 &[(1, 1), (2, 2)],
743 &[(2, 20), (3, 30)],
744 &[(1, 3), (2, -2), (3, 7)],
745 &[(1, -3), (2, 2), (3, -7)], &[(1, 1), (2, 1), (3, 1), (4, 1), (5, 1)],
747 ];
748
749 for a in cases {
750 for b in cases {
751 let (la, lb) = (lc(a), lc(b));
752 let exp_add = reference(a, b, 1);
753 let exp_sub = reference(a, b, -1);
754
755 assert_eq!(la.clone() + lb.clone(), exp_add, "Add val_val {a:?} {b:?}");
756 assert_eq!(la.clone() + &lb, exp_add, "Add val_ref {a:?} {b:?}");
757 assert_eq!(&la + lb.clone(), exp_add, "Add ref_val {a:?} {b:?}");
758 assert_eq!(&la + &lb, exp_add, "Add ref_ref {a:?} {b:?}");
759 { let mut t = la.clone(); t += lb.clone(); assert_eq!(t, exp_add, "+= val {a:?} {b:?}"); }
760 { let mut t = la.clone(); t += &lb; assert_eq!(t, exp_add, "+= ref {a:?} {b:?}"); }
761
762 assert_eq!(la.clone() - lb.clone(), exp_sub, "Sub val_val {a:?} {b:?}");
763 assert_eq!(la.clone() - &lb, exp_sub, "Sub val_ref {a:?} {b:?}");
764 assert_eq!(&la - lb.clone(), exp_sub, "Sub ref_val {a:?} {b:?}");
765 assert_eq!(&la - &lb, exp_sub, "Sub ref_ref {a:?} {b:?}");
766 { let mut t = la.clone(); t -= lb.clone(); assert_eq!(t, exp_sub, "-= val {a:?} {b:?}"); }
767 { let mut t = la.clone(); t -= &lb; assert_eq!(t, exp_sub, "-= ref {a:?} {b:?}"); }
768
769 let _ = &la + &lb;
771 let _ = &la - &lb;
772 assert_eq!(la, lc(a), "lhs mutated {a:?}");
773 assert_eq!(lb, lc(b), "rhs mutated {b:?}");
774 }
775 }
776 }
777
778 #[test]
779 fn mul() {
780 type L = Lc<X, i32>;
781 let z = L::from(hashmap!{ e(1) => 1, e(2) => 2 });
782 let r = 2;
783 let w = z * r;
784
785 assert_eq!(w, L::from(hashmap!{ e(1) => 2, e(2) => 4 }));
786 }
787
788 #[test]
789 fn mul_ref() {
790 type L = Lc<X, i32>;
791 let z = L::from(hashmap!{ e(1) => 1, e(2) => 2 });
792 let r = 2;
793 let w = z * r;
794
795 assert_eq!(w, L::from(hashmap!{ e(1) => 2, e(2) => 4 }));
796 }
797
798 #[test]
799 fn mul_assign() {
800 type L = Lc<X, i32>;
801 let mut z = L::from(hashmap!{ e(1) => 1, e(2) => 2 });
802 let r = 2;
803 z *= r;
804
805 assert_eq!(z, L::from(hashmap!{ e(1) => 2, e(2) => 4 }));
806 }
807
808 #[test]
809 fn mul_assign_ref() {
810 type L = Lc<X, i32>;
811 let mut z = L::from(hashmap!{ e(1) => 1, e(2) => 2 });
812 let r = 2;
813 z *= &r;
814
815 assert_eq!(z, L::from(hashmap!{ e(1) => 2, e(2) => 4 }));
816 }
817
818 #[test]
819 fn map_coeffs() {
820 type L = Lc<X, i32>;
821 let z = L::from(hashmap!{ e(1) => 1, e(2) => 2 });
822 let w = z.map_coeffs(|a| a * 10);
823
824 assert_eq!(w, L::from(hashmap!{ e(1) => 10, e(2) => 20 }));
825 }
826
827 #[test]
828 fn map_keys() {
829 type L = Lc<X, i32>;
830 let z = L::from(hashmap!{ e(1) => 1, e(2) => 2 });
831 let w = z.map_keys(|x| e(x.0 * 10));
832
833 assert_eq!(w, L::from(hashmap!{ e(10) => 1, e(20) => 2 }));
834 }
835
836 #[test]
837 fn filter_keys() {
838 type L = Lc<X, i32>;
839 let z = L::from_iter( (1..10).map(|i| (e(i), i * 10)) );
840 let w = z.filtered(|x| x.0 % 3 == 0 );
841 assert_eq!(w, L::from(hashmap!{ e(3) => 30, e(6) => 60, e(9) => 90}))
842 }
843
844 #[test]
845 #[cfg(feature = "serde")]
846 fn serialize() {
847 type L = Lc<X, i32>;
848 let z = L::from(hashmap!{ e(1) => 1, e(2) => 2 });
849 let ser = serde_json::to_string(&z).unwrap();
850 let deser = serde_json::from_str::<L>(&ser).unwrap();
851 assert_eq!(z, deser);
852 }
853}