Skip to main content

aces/
multiset.rs

1use std::{
2    str::FromStr,
3    fmt::{self, Write},
4};
5use crate::{AcesError, AcesErrorKind};
6
7/// A scalar type common for node capacity, harc weight and state.
8///
9/// Valid multiplicities are nonnegative integers or _ω_.
10#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
11#[repr(transparent)]
12pub struct Multiplicity(u64);
13
14impl Multiplicity {
15    #[inline]
16    pub const fn omega() -> Self {
17        Multiplicity(u64::max_value())
18    }
19
20    pub fn finite(value: u64) -> Option<Self> {
21        if value < u64::max_value() {
22            Some(Multiplicity(value))
23        } else {
24            None
25        }
26    }
27
28    #[inline]
29    pub const fn zero() -> Self {
30        Multiplicity(0)
31    }
32
33    #[inline]
34    pub const fn one() -> Self {
35        Multiplicity(1)
36    }
37
38    #[inline]
39    pub fn is_omega(self) -> bool {
40        self.0 == u64::max_value()
41    }
42
43    #[inline]
44    pub fn is_zero(self) -> bool {
45        self.0 == 0
46    }
47
48    #[inline]
49    pub fn is_finite(self) -> bool {
50        self.0 < u64::max_value()
51    }
52
53    #[inline]
54    pub fn is_positive(self) -> bool {
55        self.0 > 0
56    }
57
58    pub fn checked_add(self, other: Self) -> Option<Self> {
59        if self.0 == u64::max_value() {
60            Some(self)
61        } else if other.0 == u64::max_value() {
62            Some(other)
63        } else {
64            self.0.checked_add(other.0).and_then(|result| {
65                if result == u64::max_value() {
66                    None
67                } else {
68                    Some(Multiplicity(result))
69                }
70            })
71        }
72    }
73
74    pub fn checked_sub(self, other: Self) -> Option<Self> {
75        if self.0 == u64::max_value() {
76            Some(self)
77        } else if other.0 == u64::max_value() {
78            if self.0 == 0 {
79                Some(self)
80            } else {
81                None
82            }
83        } else {
84            self.0.checked_sub(other.0).map(Multiplicity)
85        }
86    }
87
88    /// Note: don't use this for evaluation of state transition; call
89    /// [`checked_add()`] instead.
90    ///
91    /// [`checked_add()`]: Multiplicity::checked_add()
92    pub fn saturating_add(self, other: Self) -> Self {
93        if self.0 == u64::max_value() {
94            self
95        } else {
96            let result = self.0.saturating_add(other.0);
97
98            if result == u64::max_value() {
99                Multiplicity(u64::max_value() - 1)
100            } else {
101                Multiplicity(result)
102            }
103        }
104    }
105
106    /// Note: don't use this for evaluation of state transition; call
107    /// [`checked_sub()`] instead.
108    ///
109    /// [`checked_sub()`]: Multiplicity::checked_sub()
110    pub fn saturating_sub(self, other: Self) -> Self {
111        if self.0 == u64::max_value() {
112            self
113        } else if other.0 == u64::max_value() {
114            if self.0 == 0 {
115                self
116            } else {
117                Multiplicity(0)
118            }
119        } else {
120            let result = self.0.saturating_sub(other.0);
121
122            Multiplicity(result)
123        }
124    }
125}
126
127impl fmt::Debug for Multiplicity {
128    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
129        write!(f, "Multiplicity({})", self)
130    }
131}
132
133impl fmt::Display for Multiplicity {
134    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
135        if self.is_finite() {
136            self.0.fmt(f)
137        } else {
138            f.write_char('ω')
139        }
140    }
141}
142
143impl FromStr for Multiplicity {
144    type Err = AcesError;
145
146    fn from_str(s: &str) -> Result<Self, Self::Err> {
147        if s.eq_ignore_ascii_case("omega") || s == "ω" || s == "Ω" {
148            Ok(Multiplicity::omega())
149        } else {
150            match s.parse::<u64>() {
151                Ok(value) => Multiplicity::finite(value).ok_or_else(|| {
152                    AcesError::from(AcesErrorKind::MultiplicityOverflow("parsing".into()))
153                }),
154                Err(err) => Err(AcesError::from(AcesErrorKind::from(err))),
155            }
156        }
157    }
158}
159
160/// A maximum number of tokens a node may hold.
161///
162/// This is the type of values of the function _cap<sub>U</sub>_,
163/// which maps nodes in the carrier of a c-e structure _U_ to their
164/// capacities.
165pub type Capacity = Multiplicity;
166
167/// Multiplicity of a harc (a monomial when attached to a node).
168///
169/// Weight of a monomial occurring in effects of a node indicates the
170/// number of tokens to take out of that node if a corresponding
171/// transaction fires.  Weight of a monomial occurring in causes of a
172/// node indicates the number of tokens to put into that node if a
173/// corresponding transaction fires.  Accordingly, the weights imply
174/// constraints a state must satisfy for a transaction to fire: a
175/// minimal number of tokens to be supplied by pre-set nodes, so that
176/// token transfer may happen, and a maximal number of tokens in
177/// post-set nodes, so that node capacities aren't exceeded.  The
178/// _&omega;_ weight attached to effects of a node indicates that the
179/// presence of any token in that node inhibits firing.
180pub type Weight = Multiplicity;