use core::{convert::Infallible, fmt::Display, marker::PhantomData}; use num_traits::{float::FloatCore, PrimInt, Unsigned}; #[cfg(feature = "ufmt")] use ufmt::derive::uDebug; use crate::{Decimal, DecimalError}; #[derive(Debug)] #[cfg_attr(feature = "ufmt", derive(uDebug))] pub enum StackCalcError { StackOverflow, StackUnderflow, DecimalError(DecimalError), } impl Display for StackCalcError { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { StackCalcError::StackOverflow => f.write_str("stack overflow"), StackCalcError::StackUnderflow => f.write_str("stack underflow"), StackCalcError::DecimalError(de) => Display::fmt(de, f), } } } #[derive(Debug, Clone)] pub struct StackCalc { stack: [Decimal; STACK], index: usize, phantom: PhantomData, } #[cfg(feature = "ufmt")] impl ufmt::uDebug for StackCalc { fn fmt(&self, f: &mut ufmt::Formatter<'_, W>) -> Result<(), W::Error> where W: ufmt::uWrite + ?Sized, { f.debug_struct("StackCalc")? .field("stack", &self.stack.as_slice())? .field("index", &self.index)? .finish() } } impl Default for StackCalc { fn default() -> Self { StackCalc { stack: [Decimal::default(); STACK], index: 0, phantom: PhantomData, } } } impl From for StackCalcError { fn from(val: DecimalError) -> Self { StackCalcError::DecimalError(val) } } impl From for StackCalcError { fn from(_: Infallible) -> Self { unreachable!() } } impl StackCalc { pub fn is_full(&self) -> bool { self.index == STACK } pub fn is_empty(&self) -> bool { self.index == 0 } pub fn len(&self) -> usize { self.index } pub fn reset(&mut self) { self.index = 0; } pub fn last(&self) -> Option> { if self.is_empty() { return None; } Some(self.stack[self.index - 1]) } pub fn iter(&self) -> impl Iterator> { self.stack.iter().take(self.index) } pub fn push( &mut self, val: impl TryInto, Error: Into>, ) -> Result<(), StackCalcError> { if self.is_full() { return Err(StackCalcError::StackOverflow); } self.stack[self.index] = val.try_into().map_err(Into::into)?; self.index += 1; Ok(()) } pub fn pop(&mut self) -> Result, StackCalcError> { if self.is_empty() { return Err(StackCalcError::StackUnderflow); } self.index -= 1; Ok(self.stack[self.index]) } fn pop_pair_float(&mut self) -> Result<(F, F), StackCalcError> { let a = self.pop()?; let b = self.pop()?; let a = a.to_float::()?; let b = b.to_float::()?; Ok((a, b)) } fn calc_pair_float( &mut self, f: impl FnOnce(F, F) -> Result, ) -> Result, StackCalcError> { let (a, b) = self.pop_pair_float()?; let res = Decimal::from_float(f(a, b)?)?; self.push(res)?; Ok(res) } pub fn add(&mut self) -> Result, StackCalcError> { self.calc_pair_float(|a, b| Ok(b + a)) } pub fn sub(&mut self) -> Result, StackCalcError> { self.calc_pair_float(|a, b| Ok(b - a)) } pub fn mul(&mut self) -> Result, StackCalcError> { self.calc_pair_float(|a, b| Ok(b * a)) } pub fn div(&mut self) -> Result, StackCalcError> { self.calc_pair_float(|a, b| Ok(b / a)) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_add() -> Result<(), StackCalcError> { let mut calc = StackCalc::::default(); calc.push(Decimal::default())?; calc.push(1337.42)?; assert_eq!(calc.add()?, Decimal::try_from(1337.42)?); calc.push(1337.42)?; assert_eq!(calc.add()?, Decimal::try_from(1337.42 * 2.0)?); calc.push(2.0)?; calc.push(3.0)?; assert!(matches!( calc.push(3.0).unwrap_err(), StackCalcError::StackOverflow )); calc.add()?; assert_eq!(calc.last().unwrap(), Decimal::try_from(5.0)?); calc.add()?; assert_eq!( calc.last().unwrap(), Decimal::try_from(1337.42 * 2.0 + 5.0)? ); assert!(matches!( calc.add().unwrap_err(), StackCalcError::StackUnderflow )); Ok(()) } #[test] fn test_add_10() -> Result<(), StackCalcError> { let mut calc = StackCalc::::default(); calc.push(Decimal::new(false, [3, 0, 0, 0, 0], false, 0))?; calc.push(Decimal::new(false, [7, 0, 0, 0, 0], false, 0))?; assert_eq!(calc.add()?, Decimal::new(false, [1, 0, 0, 0, 0], false, 1)); Ok(()) } }