use core::str::FromStr;
use aluvm::LibSite;
use strict_types::value::StrictNum;
use strict_types::StrictVal;
use crate::LIB_NAME_SONIC;
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_SONIC, tags = custom)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(rename_all = "camelCase"))]
pub enum StateArithm {
#[strict_type(tag = 0x00, dumb)]
Fungible,
#[strict_type(tag = 0x01)]
NonFungible,
#[strict_type(tag = 0xFF)]
AluVM(
LibSite,
),
}
impl StateArithm {
pub fn calculator(&self) -> StateCalc {
match self {
Self::Fungible => StateCalc::Fungible(StrictVal::Number(StrictNum::Uint(0))),
Self::NonFungible => StateCalc::NonFungible(vec![]),
Self::AluVM(_) => StateCalc::AluVM,
}
}
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Display, Error)]
#[display(doc_comments)]
pub enum StateCalcError {
Overflow,
UncountableState,
Unsupported,
}
#[derive(Clone, Eq, PartialEq, Debug)]
pub enum StateCalc {
NonFungible(Vec<StrictVal>),
Fungible(StrictVal),
AluVM,
}
impl StateCalc {
pub fn accumulate(&mut self, state: &StrictVal) -> Result<(), StateCalcError> {
match self {
Self::NonFungible(states) => {
states.push(state.clone());
Ok(())
}
Self::Fungible(value) => {
let (val, add) = match (state, value) {
(StrictVal::String(s), StrictVal::Number(StrictNum::Uint(val))) if u64::from_str(s).is_ok() => {
let add = u64::from_str(s).unwrap();
(val, add)
}
(StrictVal::Number(StrictNum::Uint(add)), StrictVal::Number(StrictNum::Uint(val))) => (val, *add),
_ => return Err(StateCalcError::UncountableState),
};
*val = val.checked_add(add).ok_or(StateCalcError::Overflow)?;
Ok(())
}
Self::AluVM => Err(StateCalcError::Unsupported),
}
}
pub fn lessen(&mut self, state: &StrictVal) -> Result<(), StateCalcError> {
match self {
Self::NonFungible(states) => {
if let Some(pos) = states.iter().position(|s| s == state) {
states.remove(pos);
Ok(())
} else {
Err(StateCalcError::UncountableState)
}
}
Self::Fungible(value) => {
let (val, dec) = match (state, value) {
(StrictVal::String(s), StrictVal::Number(StrictNum::Uint(val))) if u64::from_str(s).is_ok() => {
let dec = u64::from_str(s).unwrap();
(val, dec)
}
(StrictVal::Number(StrictNum::Uint(dec)), StrictVal::Number(StrictNum::Uint(val))) => (val, *dec),
_ => return Err(StateCalcError::UncountableState),
};
if dec > *val {
return Err(StateCalcError::Overflow);
}
*val -= dec;
Ok(())
}
Self::AluVM => Err(StateCalcError::Unsupported),
}
}
pub fn diff(&self) -> Result<Vec<StrictVal>, StateCalcError> {
Ok(match self {
Self::NonFungible(items) => items.clone(),
Self::Fungible(value) => match value {
StrictVal::Number(StrictNum::Uint(val)) => {
if val.eq(&u64::MIN) {
vec![]
} else {
vec![value.clone()]
}
}
_ => return Err(StateCalcError::UncountableState),
},
Self::AluVM => return Err(StateCalcError::Unsupported),
})
}
pub fn is_satisfied(&self, target: &StrictVal) -> bool {
match self {
Self::NonFungible(items) => items.contains(target),
Self::Fungible(value) => {
if value == target {
true
} else if let StrictVal::Number(StrictNum::Uint(val)) = value {
if let StrictVal::Number(StrictNum::Uint(tgt)) = target {
val >= tgt
} else {
false
}
} else {
false
}
}
Self::AluVM => false,
}
}
}
#[cfg(test)]
mod test {
#![cfg_attr(coverage_nightly, coverage(off))]
use super::*;
#[test]
fn arithm_fungible() {
let mut calc = StateArithm::Fungible.calculator();
let mut acc = 0u64;
for n in 0..5u64 {
calc.accumulate(&svnum!(n)).unwrap();
acc += n;
}
assert_eq!(calc.diff().unwrap(), [svnum!(acc)]);
assert!(calc.is_satisfied(&svnum!(acc)));
assert!(calc.is_satisfied(&svnum!(acc - 1)));
assert!(!calc.is_satisfied(&svnum!(acc + 1)));
for n in 0..2u64 {
calc.lessen(&svnum!(n)).unwrap();
acc -= n;
}
assert_eq!(calc.diff().unwrap(), [svnum!(acc)]);
assert!(calc.is_satisfied(&svnum!(acc)));
assert!(calc.is_satisfied(&svnum!(acc - 1)));
assert!(!calc.is_satisfied(&svnum!(acc + 1)));
}
#[test]
fn arithm_nonfungible() {
let mut calc = StateArithm::NonFungible.calculator();
for n in 0..5u64 {
calc.accumulate(&svnum!(n)).unwrap();
}
assert_eq!(calc.diff().unwrap(), [svnum!(0u64), svnum!(1u64), svnum!(2u64), svnum!(3u64), svnum!(4u64)]);
assert!(calc.is_satisfied(&svnum!(0u64)));
assert!(calc.is_satisfied(&svnum!(1u64)));
assert!(calc.is_satisfied(&svnum!(2u64)));
assert!(calc.is_satisfied(&svnum!(3u64)));
assert!(calc.is_satisfied(&svnum!(4u64)));
assert!(!calc.is_satisfied(&svnum!(5u64)));
for n in 0..2u64 {
calc.lessen(&svnum!(n)).unwrap();
}
assert_eq!(calc.diff().unwrap(), [svnum!(2u64), svnum!(3u64), svnum!(4u64)]);
assert!(!calc.is_satisfied(&svnum!(0u64)));
assert!(!calc.is_satisfied(&svnum!(1u64)));
assert!(calc.is_satisfied(&svnum!(2u64)));
assert!(calc.is_satisfied(&svnum!(3u64)));
assert!(calc.is_satisfied(&svnum!(4u64)));
assert!(!calc.is_satisfied(&svnum!(5u64)));
}
}