198 lines
5.4 KiB
Rust
198 lines
5.4 KiB
Rust
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<F: FloatCore, const STACK: usize = 2, const DSIZE: usize = 5, EXP = u16> {
|
|
stack: [Decimal<DSIZE, EXP>; STACK],
|
|
index: usize,
|
|
phantom: PhantomData<F>,
|
|
}
|
|
|
|
#[cfg(feature = "ufmt")]
|
|
impl<F: FloatCore, const STACK: usize, const DSIZE: usize, EXP: ufmt::uDebug> ufmt::uDebug
|
|
for StackCalc<F, STACK, DSIZE, EXP>
|
|
{
|
|
fn fmt<W>(&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<F: FloatCore, const STACK: usize, const DSIZE: usize, EXP: PrimInt> Default
|
|
for StackCalc<F, STACK, DSIZE, EXP>
|
|
{
|
|
fn default() -> Self {
|
|
StackCalc {
|
|
stack: [Decimal::default(); STACK],
|
|
index: 0,
|
|
phantom: PhantomData,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<DecimalError> for StackCalcError {
|
|
fn from(val: DecimalError) -> Self {
|
|
StackCalcError::DecimalError(val)
|
|
}
|
|
}
|
|
|
|
impl From<Infallible> for StackCalcError {
|
|
fn from(_: Infallible) -> Self {
|
|
unreachable!()
|
|
}
|
|
}
|
|
|
|
impl<F: FloatCore, const STACK: usize, const DSIZE: usize, EXP: PrimInt + Unsigned>
|
|
StackCalc<F, STACK, DSIZE, EXP>
|
|
{
|
|
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<Decimal<DSIZE, EXP>> {
|
|
if self.is_empty() {
|
|
return None;
|
|
}
|
|
Some(self.stack[self.index - 1])
|
|
}
|
|
|
|
pub fn iter(&self) -> impl Iterator<Item = &Decimal<DSIZE, EXP>> {
|
|
self.stack.iter().take(self.index)
|
|
}
|
|
|
|
pub fn push(
|
|
&mut self,
|
|
val: impl TryInto<Decimal<DSIZE, EXP>, Error: Into<StackCalcError>>,
|
|
) -> 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<Decimal<DSIZE, EXP>, 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::<F>()?;
|
|
let b = b.to_float::<F>()?;
|
|
Ok((a, b))
|
|
}
|
|
|
|
fn calc_pair_float(
|
|
&mut self,
|
|
f: impl FnOnce(F, F) -> Result<F, StackCalcError>,
|
|
) -> Result<Decimal<DSIZE, EXP>, 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<Decimal<DSIZE, EXP>, StackCalcError> {
|
|
self.calc_pair_float(|a, b| Ok(b + a))
|
|
}
|
|
|
|
pub fn sub(&mut self) -> Result<Decimal<DSIZE, EXP>, StackCalcError> {
|
|
self.calc_pair_float(|a, b| Ok(b - a))
|
|
}
|
|
|
|
pub fn mul(&mut self) -> Result<Decimal<DSIZE, EXP>, StackCalcError> {
|
|
self.calc_pair_float(|a, b| Ok(b * a))
|
|
}
|
|
|
|
pub fn div(&mut self) -> Result<Decimal<DSIZE, EXP>, 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::<f64, 3, 7>::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::<f64, 3, 5>::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(())
|
|
}
|
|
}
|