1use crate::abelian::AbelianGroup;
2use crate::ring::Ring;
3
4pub trait Module<R: Ring>: AbelianGroup {
12 fn scale(self, scalar: R) -> Self;
13}
14
15impl Module<f32> for f32 {
16 fn scale(self, scalar: f32) -> Self {
17 self * scalar
18 }
19}
20
21impl Module<f64> for f64 {
22 fn scale(self, scalar: f64) -> Self {
23 self * scalar
24 }
25}
26
27impl<F: crate::field::Field + AbelianGroup> Module<F> for (F, F) {
28 fn scale(self, scalar: F) -> Self {
29 (self.0.mul(scalar.clone()), self.1.mul(scalar))
30 }
31}
32
33#[cfg(test)]
34mod tests {
35 use super::*;
36 use crate::semiring::Semiring;
37 use karpal_core::Semigroup;
38
39 #[test]
40 fn f64_scale() {
41 assert!((3.0f64.scale(2.0) - 6.0).abs() < 1e-10);
42 }
43
44 #[test]
45 fn f64_scale_one() {
46 assert!((5.0f64.scale(f64::one()) - 5.0).abs() < 1e-10);
47 }
48
49 #[test]
50 fn tuple_scale() {
51 let v = (1.0f64, 2.0f64).scale(3.0);
52 assert!((v.0 - 3.0).abs() < 1e-10);
53 assert!((v.1 - 6.0).abs() < 1e-10);
54 }
55
56 #[test]
57 fn tuple_scale_distributes_over_add() {
58 let a = (1.0f64, 2.0);
59 let b = (3.0f64, 4.0);
60 let sum_scaled = a.combine(b).scale(2.0);
61 let scaled_sum = a.scale(2.0).combine(b.scale(2.0));
62 assert!((sum_scaled.0 - scaled_sum.0).abs() < 1e-10);
63 assert!((sum_scaled.1 - scaled_sum.1).abs() < 1e-10);
64 }
65}
66
67#[cfg(test)]
68mod law_tests {
69 use super::*;
70 use crate::semiring::Semiring;
71 use karpal_core::Semigroup;
72 use proptest::prelude::*;
73
74 proptest! {
75 #[test]
76 fn scale_one_identity(a in -100.0f64..100.0) {
77 prop_assert!((a.scale(f64::one()) - a).abs() < 1e-10);
78 }
79
80 #[test]
81 fn scale_compatibility(
82 a in -10.0f64..10.0,
83 r in -10.0f64..10.0,
84 s in -10.0f64..10.0
85 ) {
86 let left = a.scale(r).scale(s);
87 let right = a.scale(r.mul(s));
88 prop_assert!((left - right).abs() < 1e-6, "left={}, right={}", left, right);
89 }
90
91 #[test]
92 fn scale_distributes_over_group_add(
93 a in -10.0f64..10.0,
94 b in -10.0f64..10.0,
95 r in -10.0f64..10.0
96 ) {
97 let left = a.combine(b).scale(r);
98 let right = a.scale(r).combine(b.scale(r));
99 prop_assert!((left - right).abs() < 1e-6, "left={}, right={}", left, right);
100 }
101
102 #[test]
103 fn scale_distributes_over_scalar_add(
104 a in -10.0f64..10.0,
105 r in -10.0f64..10.0,
106 s in -10.0f64..10.0
107 ) {
108 let left = a.scale(r.add(s));
109 let right = a.scale(r).combine(a.scale(s));
110 prop_assert!((left - right).abs() < 1e-6, "left={}, right={}", left, right);
111 }
112 }
113}