Skip to main content

yui_core/conc/num/
int.rs

1//! Integer extension traits and concrete impls for `i32`, `i64`, `i128`, and `BigInt`.
2//!
3//! See: <https://en.wikipedia.org/wiki/Integer>
4
5use num_bigint::BigInt;
6use num_traits::{One, Signed, ToPrimitive, FromPrimitive};
7use crate::abst::{AddGrp, AddGrpOps, AddMon, AddMonOps, EucRing, EucRingOps, MathType, Mon, MonOps, Ring, RingOps};
8use crate::ext::DivRound;
9
10/// Helper trait bundling [`EucRingOps`] for [`IntType`].
11pub trait IntOps<T = Self>: EucRingOps<T> {}
12
13/// Integers: a signed, totally-ordered [`EucRing`] convertible to and from
14/// the native numeric types.
15pub trait IntType: EucRing + IntOps + Signed + PartialOrd + Ord + FromPrimitive + ToPrimitive
16where for<'a> &'a Self: EucRingOps<Self> {}
17
18impl<T> DivRound for T
19where T: IntType, for<'x> &'x T: IntOps<T> {
20    // Rounds half away from zero.
21    fn div_round(&self, q: &Self) -> Self {
22        let d = self / q;
23        let r = self % q;
24
25        if r.is_zero() || (&r + &r).abs() < q.abs() {
26            return d
27        }
28
29        // the exact quotient exceeds `d` exactly when `r` and `q` agree in sign.
30        if r.is_negative() == q.is_negative() {
31            d + Self::one()
32        } else {
33            d - Self::one()
34        }
35    }
36}
37
38macro_rules! impl_ops {
39    ($trait:ident, $type:ty) => {
40        impl $trait for $type {}
41        impl $trait<$type> for &$type {}
42    };
43}
44
45macro_rules! impl_integer {
46    ($type:ident) => {
47        impl_ops!(AddMonOps, $type);
48        impl_ops!(AddGrpOps, $type);
49        impl_ops!(MonOps, $type);
50        impl_ops!(RingOps, $type);
51        impl_ops!(EucRingOps, $type);
52        impl_ops!(IntOps, $type);
53
54        impl MathType for $type {
55            fn math_symbol() -> String {
56                String::from("Z")
57            }
58        }
59
60        impl AddMon for $type {}
61        impl AddGrp for $type {}
62        impl Mon for $type {}
63        impl Ring for $type {
64            fn inv(&self) -> Option<Self> {
65                if self.is_unit() {
66                    Some(self.clone())
67                } else {
68                    None
69                }
70            }
71
72            fn is_unit(&self) -> bool {
73                self.is_one() || (-self).is_one()
74            }
75
76            fn normalizing_unit(&self) -> Self {
77                if !self.is_negative() {
78                    Self::one()
79                } else {
80                    -Self::one()
81                }
82            }
83
84            fn c_weight(&self) -> f64 {
85                self.abs().to_f64().unwrap()
86            }
87        }
88
89        impl EucRing for $type {
90            fn gcd(x: &Self, y: &Self) -> Self {
91                num_integer::Integer::gcd(x, y)
92            }
93
94            fn gcdx(x: &Self, y: &Self) -> (Self, Self, Self) {
95                let num_integer::ExtendedGcd{ gcd: d, x: s, y: t } = num_integer::Integer::extended_gcd(x, y);
96                (d, s, t)
97            }
98
99            fn lcm(x: &Self, y: &Self) -> Self {
100                num_integer::Integer::lcm(x, y)
101            }
102        }
103
104        impl IntType for $type {}
105    }
106}
107
108impl_integer!(i32);
109impl_integer!(i64);
110impl_integer!(i128);
111impl_integer!(BigInt);
112
113
114mod tex {
115    use crate::util::tex::TeX;
116    use num_bigint::BigInt;
117
118    macro_rules! impl_tex_int {
119        ($type:ident) => {
120            impl TeX for $type {
121                fn tex_math_symbol() -> String {
122                    String::from("\\mathbb{Z}")
123                }
124                fn tex_string(&self) -> String {
125                    self.to_string()
126                }
127            }
128        }
129    }
130
131    impl_tex_int!(i32);
132    impl_tex_int!(i64);
133    impl_tex_int!(i128);
134    impl_tex_int!(BigInt);
135}
136
137#[cfg(test)]
138mod tests {
139    use super::*;
140
141    #[test]
142    fn check_type() {
143        fn check<T>() where T: IntType, for<'a> &'a T: IntOps<T> {}
144        check::<i32>();
145        check::<i64>();
146        check::<i128>();
147        check::<BigInt>();
148    }
149
150    #[test]
151    fn int_is_unit() {
152        assert!(1.is_unit());
153        assert!((-1).is_unit());
154        assert!(!2.is_unit());
155    }
156
157    #[test]
158    fn int_inv() {
159        assert_eq!(1.inv(), Some(1));
160        assert_eq!((-1).inv(), Some(-1));
161        assert_eq!(2.inv(), None);
162    }
163
164    #[test]
165    fn int_normalizing_unit() {
166        assert_eq!(1.normalizing_unit(), 1);
167        assert_eq!((-1).normalizing_unit(), -1);
168        assert_eq!(2.normalizing_unit(), 1);
169    }
170
171    #[test]
172    fn int_divides() {
173        assert!(2.divides(&4));
174        assert!(!3.divides(&4));
175        assert!(!0.divides(&1));
176    }
177
178    #[test]
179    fn gcd_i32() {
180        let (a, b) = (240, 46);
181        let d = i32::gcd(&a, &b);
182        assert_eq!(d, 2);
183
184        let (a, b) = (24, 0);
185        let d = i32::gcd(&a, &b);
186        assert_eq!(d, 24);
187
188        let (a, b) = (0, -24);
189        let d = i32::gcd(&a, &b);
190        assert_eq!(d, 24);
191
192        let (a, b) = (0, 0);
193        let d = i32::gcd(&a, &b);
194        assert_eq!(d, 0);
195    }
196
197    #[test]
198    fn gcdx_i32() {
199        let (a, b) = (240, 46);
200        let (d, s, t) = i32::gcdx(&a, &b);
201        assert_eq!(d, 2);
202        assert_eq!(s * a + t * b, d);
203
204        let (a, b) = (24, 0);
205        let (d, s, t) = i32::gcdx(&a, &b);
206        assert_eq!(d, 24);
207        assert_eq!(s * a + t * b, d);
208
209        let (a, b) = (0, 0);
210        let (d, s, t) = i32::gcdx(&a, &b);
211        assert_eq!(d, 0);
212        assert_eq!(s * a + t * b, d);
213    }
214
215    #[test]
216    fn div_round() {
217        assert_eq!(12.div_round(&5), 2);
218        assert_eq!(13.div_round(&5), 3);
219        assert_eq!((-12).div_round(&5), -2);
220        assert_eq!((-13).div_round(&5), -3);
221    }
222
223    #[test]
224    fn div_round_half() {
225        // halves round away from zero.
226        assert_eq!(5.div_round(&2), 3);
227        assert_eq!((-5).div_round(&2), -3);
228        assert_eq!(5.div_round(&-2), -3);
229        assert_eq!((-5).div_round(&-2), 3);
230    }
231
232    #[test]
233    fn div_round_large() {
234        // must stay exact however large the terms.
235        let a = (1i64 << 60) + 1;
236        assert_eq!(a.div_round(&1), a);
237
238        let b = BigInt::from(10).pow(400);
239        assert_eq!(b.div_round(&BigInt::from(10).pow(399)), BigInt::from(10));
240    }
241
242    #[test]
243    fn tex() {
244        use crate::util::tex::TeX;
245        assert_eq!(i32::tex_math_symbol(), "\\mathbb{Z}");
246        assert_eq!((-2).tex_string(), "-2");
247    }
248}