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