sonicapi/state/
arithmetics.rs1use core::str::FromStr;
25
26use aluvm::LibSite;
27use strict_types::value::StrictNum;
28use strict_types::StrictVal;
29
30use crate::LIB_NAME_SONIC;
31
32#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
33#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
34#[strict_type(lib = LIB_NAME_SONIC, tags = custom)]
35#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(rename_all = "camelCase"))]
36pub enum StateArithm {
37 #[strict_type(tag = 0x00, dumb)]
38 Fungible,
39
40 #[strict_type(tag = 0x01)]
41 NonFungible,
42 #[strict_type(tag = 0xFF)]
45 AluVM(
46 LibSite,
49 ),
50}
51
52impl StateArithm {
53 pub fn calculator(&self) -> StateCalc {
54 match self {
55 Self::Fungible => StateCalc::Fungible(StrictVal::Number(StrictNum::Uint(0))),
56 Self::NonFungible => StateCalc::NonFungible(vec![]),
57 Self::AluVM(_) => StateCalc::AluVM,
58 }
59 }
60}
61
62#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Display, Error)]
63#[display(doc_comments)]
64pub enum StateCalcError {
65 Overflow,
67
68 UncountableState,
70
71 Unsupported,
73}
74
75#[derive(Clone, Eq, PartialEq, Debug)]
76pub enum StateCalc {
77 NonFungible(Vec<StrictVal>),
78 Fungible(StrictVal),
79 AluVM,
81}
82
83impl StateCalc {
84 pub fn accumulate(&mut self, state: &StrictVal) -> Result<(), StateCalcError> {
85 match self {
86 Self::NonFungible(states) => {
87 states.push(state.clone());
88 Ok(())
89 }
90 Self::Fungible(value) => {
91 let (val, add) = match (state, value) {
92 (StrictVal::String(s), StrictVal::Number(StrictNum::Uint(val))) if u64::from_str(s).is_ok() => {
94 let add = u64::from_str(s).unwrap();
95 (val, add)
96 }
97 (StrictVal::Number(StrictNum::Uint(add)), StrictVal::Number(StrictNum::Uint(val))) => (val, *add),
98 _ => return Err(StateCalcError::UncountableState),
99 };
100 *val = val.checked_add(add).ok_or(StateCalcError::Overflow)?;
101 Ok(())
102 }
103 Self::AluVM => Err(StateCalcError::Unsupported),
104 }
105 }
106
107 pub fn lessen(&mut self, state: &StrictVal) -> Result<(), StateCalcError> {
108 match self {
109 Self::NonFungible(states) => {
110 if let Some(pos) = states.iter().position(|s| s == state) {
111 states.remove(pos);
112 Ok(())
113 } else {
114 Err(StateCalcError::UncountableState)
115 }
116 }
117 Self::Fungible(value) => {
118 let (val, dec) = match (state, value) {
119 (StrictVal::String(s), StrictVal::Number(StrictNum::Uint(val))) if u64::from_str(s).is_ok() => {
121 let dec = u64::from_str(s).unwrap();
122 (val, dec)
123 }
124 (StrictVal::Number(StrictNum::Uint(dec)), StrictVal::Number(StrictNum::Uint(val))) => (val, *dec),
125 _ => return Err(StateCalcError::UncountableState),
126 };
127 if dec > *val {
128 return Err(StateCalcError::Overflow);
129 }
130 *val -= dec;
131 Ok(())
132 }
133 Self::AluVM => Err(StateCalcError::Unsupported),
134 }
135 }
136
137 pub fn diff(&self) -> Result<Vec<StrictVal>, StateCalcError> {
138 Ok(match self {
139 Self::NonFungible(items) => items.clone(),
140 Self::Fungible(value) => match value {
141 StrictVal::Number(StrictNum::Uint(val)) => {
142 if val.eq(&u64::MIN) {
143 vec![]
144 } else {
145 vec![value.clone()]
146 }
147 }
148 _ => return Err(StateCalcError::UncountableState),
149 },
150 Self::AluVM => return Err(StateCalcError::Unsupported),
151 })
152 }
153
154 pub fn is_satisfied(&self, target: &StrictVal) -> bool {
155 match self {
156 Self::NonFungible(items) => items.contains(target),
157 Self::Fungible(value) => {
158 if value == target {
159 true
160 } else if let StrictVal::Number(StrictNum::Uint(val)) = value {
161 if let StrictVal::Number(StrictNum::Uint(tgt)) = target {
162 val >= tgt
163 } else {
164 false
165 }
166 } else {
167 false
168 }
169 }
170 Self::AluVM => false,
171 }
172 }
173}
174
175#[cfg(test)]
176mod test {
177 #![cfg_attr(coverage_nightly, coverage(off))]
178 use super::*;
179
180 #[test]
181 fn arithm_fungible() {
182 let mut calc = StateArithm::Fungible.calculator();
183 let mut acc = 0u64;
184 for n in 0..5u64 {
185 calc.accumulate(&svnum!(n)).unwrap();
186 acc += n;
187 }
188 assert_eq!(calc.diff().unwrap(), [svnum!(acc)]);
189 assert!(calc.is_satisfied(&svnum!(acc)));
190 assert!(calc.is_satisfied(&svnum!(acc - 1)));
191 assert!(!calc.is_satisfied(&svnum!(acc + 1)));
192
193 for n in 0..2u64 {
194 calc.lessen(&svnum!(n)).unwrap();
195 acc -= n;
196 }
197
198 assert_eq!(calc.diff().unwrap(), [svnum!(acc)]);
199 assert!(calc.is_satisfied(&svnum!(acc)));
200 assert!(calc.is_satisfied(&svnum!(acc - 1)));
201 assert!(!calc.is_satisfied(&svnum!(acc + 1)));
202 }
203
204 #[test]
205 fn arithm_nonfungible() {
206 let mut calc = StateArithm::NonFungible.calculator();
207 for n in 0..5u64 {
208 calc.accumulate(&svnum!(n)).unwrap();
209 }
210 assert_eq!(calc.diff().unwrap(), [svnum!(0u64), svnum!(1u64), svnum!(2u64), svnum!(3u64), svnum!(4u64)]);
211 assert!(calc.is_satisfied(&svnum!(0u64)));
212 assert!(calc.is_satisfied(&svnum!(1u64)));
213 assert!(calc.is_satisfied(&svnum!(2u64)));
214 assert!(calc.is_satisfied(&svnum!(3u64)));
215 assert!(calc.is_satisfied(&svnum!(4u64)));
216 assert!(!calc.is_satisfied(&svnum!(5u64)));
217
218 for n in 0..2u64 {
219 calc.lessen(&svnum!(n)).unwrap();
220 }
221
222 assert_eq!(calc.diff().unwrap(), [svnum!(2u64), svnum!(3u64), svnum!(4u64)]);
223 assert!(!calc.is_satisfied(&svnum!(0u64)));
224 assert!(!calc.is_satisfied(&svnum!(1u64)));
225 assert!(calc.is_satisfied(&svnum!(2u64)));
226 assert!(calc.is_satisfied(&svnum!(3u64)));
227 assert!(calc.is_satisfied(&svnum!(4u64)));
228 assert!(!calc.is_satisfied(&svnum!(5u64)));
229 }
230}