yui_core/abst/ring.rs
1//! Ring: an additive group `+` together with an associative multiplication `·`
2//! that distributes over addition.
3//!
4//! Multiplication is **not** assumed commutative.
5//!
6//! See: <https://en.wikipedia.org/wiki/Ring_(mathematics)>
7
8use crate::num::Sign;
9use crate::abst::{AddGrp, AddGrpOps, Mon, MonOps};
10
11/// Helper trait bundling [`AddGrpOps`] and [`MonOps`] for [`Ring`].
12pub trait RingOps<T = Self>:
13 AddGrpOps<T> +
14 MonOps<T>
15{}
16
17/// A ring: an [`AddGrp`] together with an associative multiplication `·`
18/// distributing over addition `+`, with multiplicative identity [`One`](num_traits::One).
19///
20/// See: <https://en.wikipedia.org/wiki/Ring_(mathematics)>
21pub trait Ring:
22 AddGrp +
23 Mon +
24 RingOps
25where
26 for<'a> &'a Self: RingOps<Self>
27{
28 /// `+1` if `s` is positive, `-1` otherwise.
29 fn from_sign(s: Sign) -> Self {
30 if s.is_positive() {
31 Self::one()
32 } else {
33 -Self::one()
34 }
35 }
36
37 /// Multiplicative inverse, if this element is a unit; otherwise `None`.
38 fn inv(&self) -> Option<Self>;
39
40 /// `true` iff this element is a unit (has a multiplicative inverse).
41 ///
42 /// See: <https://en.wikipedia.org/wiki/Unit_(ring_theory)>
43 fn is_unit(&self) -> bool;
44
45 /// A unit `u` such that `self * u` is the chosen canonical associate of `self`.
46 ///
47 /// For example, in `ℤ` the normalizing unit of `-n` is `-1` (giving `n`);
48 /// in a field every nonzero element is its own normalizer (returning `self.inv()`).
49 ///
50 /// See: <https://en.wikipedia.org/wiki/Associate_element>
51 fn normalizing_unit(&self) -> Self;
52
53 /// The canonical associate of `self` (i.e. `self * self.normalizing_unit()`).
54 fn normalized(&self) -> Self {
55 self.clone().into_normalized()
56 }
57
58 /// Like [`normalized`](Self::normalized), but consuming.
59 fn into_normalized(self) -> Self {
60 let u = self.normalizing_unit();
61 if u.is_one() {
62 self
63 } else {
64 self * u
65 }
66 }
67
68 /// `true` iff `self` is `+1` or `-1`.
69 fn is_pm_one(&self) -> bool {
70 self.is_one() || (-self).is_one()
71 }
72
73 /// Heuristic cost of working with this element, used by chain-reduction
74 /// pivot selection. Default: `0.0` for zero, `1.0` otherwise.
75 fn c_weight(&self) -> f64 {
76 if self.is_zero() {
77 0.0
78 } else {
79 1.0
80 }
81 }
82}
83
84#[cfg(test)]
85mod tests {
86 use crate::abst::Ring;
87
88 #[test]
89 fn is_pm_one() {
90 assert!(1.is_pm_one());
91 assert!((-1).is_pm_one());
92 assert!(!2.is_pm_one());
93 assert!(!(-2).is_pm_one());
94 }
95
96 #[test]
97 fn normalized() {
98 assert_eq!(3.normalized(), 3);
99 assert_eq!((-3).normalized(), 3);
100 }
101
102}