use super::{
BigInt, BigUint, Cache, Evaluator,
LangErr::MissingTerm,
O::{Empty, Eval, Store},
Sign, Vec, vec,
};
#[cfg(feature = "rand")]
use super::{BufRead, LangErr, LendingIterator as _};
#[cfg(feature = "std")]
use super::{E, EvalIter};
#[cfg(not(feature = "rand"))]
use super::{
LangErr::{
DivByZero, ExpDivByZero, ExpIsNotIntOrOneHalf, InvalidAbs, InvalidDec, InvalidPar,
InvalidQuit, InvalidRound, InvalidStore, ModIsNotInt, ModZero, NotEnoughPrevResults,
NotNonNegIntFact, SqrtDoesNotExist, TrailingSyms,
},
O::Exit,
Ratio,
};
#[cfg(not(feature = "rand"))]
use alloc::{borrow::ToOwned as _, string::ToString as _};
#[cfg(not(feature = "rand"))]
use num_traits::Pow as _;
#[cfg(feature = "rand")]
use num_traits::ToPrimitive as _;
#[cfg(feature = "rand")]
use std::io::{self, Error, Read};
#[expect(clippy::too_many_lines, reason = "a lot to test")]
#[cfg(not(feature = "rand"))]
#[test]
fn empty() {
assert_eq!(
Evaluator::new(b"\n", &mut Cache::new(), &mut None, &mut Vec::new()).evaluate(),
Ok(Empty(&None))
);
assert_eq!(
Evaluator::new(
b" \t \t \n",
&mut Cache::new(),
&mut Some(Ratio::from_integer(BigInt::from_biguint(
Sign::Minus,
BigUint::new(vec![12])
))),
&mut Vec::new()
)
.evaluate()
.map(|a| a.to_string()),
Ok("> -12".to_owned())
);
assert_eq!(
Evaluator::new(
b"\t\n",
&mut Cache::new(),
&mut Some(Ratio::new(
BigInt::from_biguint(Sign::Minus, BigUint::new(vec![4])),
BigInt::from_biguint(Sign::Plus, BigUint::new(vec![6]))
)),
&mut Vec::new()
)
.evaluate()
.map(|a| a.to_string()),
Ok("> -0.666666667".to_owned())
);
assert_eq!(
Evaluator::new(
b"\t\n",
&mut Cache::new(),
&mut Some(Ratio::new(
BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1])),
BigInt::from_biguint(Sign::Plus, BigUint::new(vec![4_230_196_224, 6]))
)),
&mut Vec::new()
)
.evaluate()
.map(|a| a.to_string()),
Ok("> 0.000000000".to_owned())
);
assert_eq!(
Evaluator::new(
b"\t\n",
&mut Cache::new(),
&mut Some(Ratio::new(
BigInt::from_biguint(Sign::Plus, BigUint::new(vec![17])),
BigInt::from_biguint(Sign::Plus, BigUint::new(vec![4_230_196_224, 6]))
)),
&mut Vec::new()
)
.evaluate()
.map(|a| a.to_string()),
Ok("> 0.000000001".to_owned())
);
assert_eq!(
Evaluator::new(
b"\t\n",
&mut Cache::new(),
&mut Some(Ratio::new(
BigInt::from_biguint(Sign::Plus, BigUint::new(vec![3])),
BigInt::from_biguint(Sign::Plus, BigUint::new(vec![10]))
)),
&mut Vec::new()
)
.evaluate()
.map(|a| a.to_string()),
Ok("> 0.3".to_owned())
);
assert_eq!(
Evaluator::new(
b"\t\n",
&mut Cache::new(),
&mut Some(Ratio::new(
BigInt::from_biguint(Sign::Minus, BigUint::new(vec![203])),
BigInt::from_biguint(Sign::Plus, BigUint::new(vec![10]))
)),
&mut Vec::new()
)
.evaluate()
.map(|a| a.to_string()),
Ok("> -20.3".to_owned())
);
assert_eq!(
Evaluator::new(
&[0u8; 0],
&mut Cache::new(),
&mut Some(Ratio::new(
BigInt::from_biguint(Sign::Minus, BigUint::new(vec![203])),
BigInt::from_biguint(Sign::Plus, BigUint::new(vec![10]))
)),
&mut Vec::new()
)
.evaluate()
.map(|a| a.to_string()),
Ok("> -20.3".to_owned())
);
}
#[expect(clippy::unreachable, reason = "want to crash when there is a bug")]
#[cfg(not(feature = "rand"))]
#[test]
fn number_literal() {
assert_eq!(
Evaluator::new(b"0", &mut Cache::new(), &mut None, &mut Vec::new()).get_rational(),
Ok(Some(Ratio::from_integer(BigInt::from_biguint(
Sign::NoSign,
BigUint::new(Vec::new())
))))
);
assert_eq!(
Evaluator::new(b"0000.00000", &mut Cache::new(), &mut None, &mut Vec::new()).get_rational(),
Ok(Some(Ratio::from_integer(BigInt::from_biguint(
Sign::NoSign,
BigUint::new(Vec::new())
))))
);
assert_eq!(
Evaluator::new(b"1 0", &mut Cache::new(), &mut None, &mut Vec::new()).get_rational(),
Ok(Some(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![1])
))))
);
let int = b"3397450981271938475135134759823759835414";
let frac = b"913759810573549872354897210539127530981570";
let left = Ratio::from_integer(
BigInt::parse_bytes(int, 10).unwrap_or_else(|| unreachable!("bug in BigInt::parse_bytes")),
);
let right = Ratio::new(
BigInt::parse_bytes(frac, 10).unwrap_or_else(|| unreachable!("bug in BigInt::parse_bytes")),
BigInt::from_biguint(Sign::Plus, BigUint::new(vec![10]).pow(frac.len() + 1)),
);
let mut vec = Vec::new();
vec.extend_from_slice(int);
vec.push(b'.');
vec.push(b'0');
vec.extend_from_slice(frac);
assert_eq!(
Evaluator::new(
vec.as_slice(),
&mut Cache::new(),
&mut None,
&mut Vec::new()
)
.get_rational(),
Ok(Some(left + right))
);
assert_eq!(
Evaluator::new(
b"000000014.0000000000000",
&mut Cache::new(),
&mut None,
&mut Vec::new()
)
.get_rational(),
Ok(Some(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![14])
))))
);
assert_eq!(
Evaluator::new(b"1.", &mut Cache::new(), &mut None, &mut Vec::new()).get_rational(),
Err(InvalidDec(2))
);
assert_eq!(
Evaluator::new(b"1. 2", &mut Cache::new(), &mut None, &mut Vec::new()).get_rational(),
Err(InvalidDec(2))
);
assert_eq!(
Evaluator::new(b"a1", &mut Cache::new(), &mut None, &mut Vec::new()).get_rational(),
Ok(None)
);
assert_eq!(
Evaluator::new(b" 1", &mut Cache::new(), &mut None, &mut Vec::new()).get_rational(),
Ok(None)
);
assert_eq!(
Evaluator::new(b"\t1", &mut Cache::new(), &mut None, &mut Vec::new()).get_rational(),
Ok(None)
);
assert_eq!(
Evaluator::new(b"-1", &mut Cache::new(), &mut None, &mut Vec::new()).get_rational(),
Ok(None)
);
assert_eq!(
Evaluator::new(b"1/2", &mut Cache::new(), &mut None, &mut Vec::new()).get_rational(),
Ok(Some(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![1])
))))
);
assert_eq!(
Evaluator::new(b"130alj", &mut Cache::new(), &mut None, &mut Vec::new()).get_rational(),
Ok(Some(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![130])
))))
);
}
#[cfg(not(feature = "rand"))]
#[test]
fn par() {
assert_eq!(
Evaluator::new(b"(1", &mut Cache::new(), &mut None, &mut Vec::new()).get_par(),
Err(InvalidPar(2))
);
assert_eq!(
Evaluator::new(b"((1\t + 2)", &mut Cache::new(), &mut None, &mut Vec::new()).get_par(),
Err(InvalidPar(9))
);
assert_eq!(
Evaluator::new(b"( 0 \t )", &mut Cache::new(), &mut None, &mut Vec::new()).get_par(),
Ok(Some(Ratio::from_integer(BigInt::from_biguint(
Sign::NoSign,
BigUint::new(Vec::new())
))))
);
assert_eq!(
Evaluator::new(b"( - \t 5 )", &mut Cache::new(), &mut None, &mut Vec::new()).get_par(),
Ok(Some(Ratio::from_integer(BigInt::from_biguint(
Sign::Minus,
BigUint::new(vec![5])
))))
);
assert_eq!(
Evaluator::new(
b"( ( 2 -\t 5) * 9 )",
&mut Cache::new(),
&mut None,
&mut Vec::new()
)
.get_par(),
Ok(Some(Ratio::from_integer(BigInt::from_biguint(
Sign::Minus,
BigUint::new(vec![27])
))))
);
}
#[expect(clippy::too_many_lines, reason = "a lot to test")]
#[expect(
clippy::indexing_slicing,
clippy::unwrap_used,
reason = "comments justify correctness"
)]
#[cfg(not(feature = "rand"))]
#[test]
fn recall_expression() {
assert_eq!(
Evaluator::new(b"1", &mut Cache::new(), &mut None, &mut Vec::new()).get_recall(),
Ok(None)
);
assert_eq!(
Evaluator::new(b"a", &mut Cache::new(), &mut None, &mut Vec::new()).get_recall(),
Ok(None)
);
assert_eq!(
Evaluator::new(b" @", &mut Cache::new(), &mut None, &mut Vec::new()).get_recall(),
Ok(None)
);
assert_eq!(
Evaluator::new(b"\t@", &mut Cache::new(), &mut None, &mut Vec::new()).get_recall(),
Ok(None)
);
assert_eq!(
Evaluator::new(b"@", &mut Cache::new(), &mut None, &mut Vec::new()).get_recall(),
Err(NotEnoughPrevResults(0))
);
assert_eq!(
Evaluator::new(b"@4", &mut Cache::new(), &mut None, &mut Vec::new()).get_recall(),
Err(NotEnoughPrevResults(0))
);
assert_eq!(
Evaluator::new(b"@0", &mut Cache::new(), &mut None, &mut Vec::new()).get_recall(),
Err(NotEnoughPrevResults(0))
);
let mut prev = None;
let mut cache = Cache::new();
_ = Evaluator::new(b"1\n", &mut cache, &mut prev, &mut Vec::new())
.evaluate()
.unwrap();
_ = Evaluator::new(b" s \r\n", &mut cache, &mut prev, &mut Vec::new())
.evaluate()
.unwrap();
assert_eq!(
Evaluator::new(b"@", &mut cache, &mut prev, &mut Vec::new()).get_recall(),
Ok(Some(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![1])
))))
);
assert_eq!(
Evaluator::new(b"@&", &mut cache, &mut prev, &mut Vec::new()).get_recall(),
Ok(Some(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![1])
))))
);
assert_eq!(
Evaluator::new(b"@0", &mut cache, &mut prev, &mut Vec::new()).get_recall(),
Ok(Some(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![1])
))))
);
assert_eq!(
Evaluator::new(b"@9", &mut cache, &mut prev, &mut Vec::new()).get_recall(),
Ok(Some(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![1])
))))
);
assert_eq!(
Evaluator::new(b"@ 2", &mut cache, &mut prev, &mut Vec::new()).get_recall(),
Ok(Some(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![1])
))))
);
assert_eq!(
Evaluator::new(b"@\t2", &mut cache, &mut prev, &mut Vec::new()).get_recall(),
Ok(Some(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![1])
))))
);
assert_eq!(
Evaluator::new(b"@10", &mut cache, &mut prev, &mut Vec::new()).get_recall(),
Ok(Some(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![1])
))))
);
assert_eq!(
Evaluator::new(b"@2", &mut cache, &mut prev, &mut Vec::new()).get_recall(),
Err(NotEnoughPrevResults(1))
);
_ = Evaluator::new(b"2\r\n", &mut cache, &mut prev, &mut Vec::new())
.evaluate()
.unwrap();
_ = Evaluator::new(b"s\n", &mut cache, &mut prev, &mut Vec::new())
.evaluate()
.unwrap();
assert_eq!(
Evaluator::new(b"@", &mut cache, &mut prev, &mut Vec::new()).get_recall(),
Ok(Some(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![2])
))))
);
assert_eq!(
Evaluator::new(b"@2", &mut cache, &mut prev, &mut Vec::new()).get_recall(),
Ok(Some(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![1])
))))
);
assert_eq!(
Evaluator::new(b"@3", &mut cache, &mut prev, &mut Vec::new()).get_recall(),
Err(NotEnoughPrevResults(2))
);
let mut v = vec![0, b'\n'];
for i in b'3'..=b'8' {
v[0] = i;
_ = Evaluator::new(v.as_slice(), &mut cache, &mut prev, &mut Vec::new())
.evaluate()
.unwrap();
_ = Evaluator::new(b"s\n", &mut cache, &mut prev, &mut Vec::new())
.evaluate()
.unwrap();
}
v[0] = b'@';
for i in b'1'..=b'8' {
v[1] = i;
assert_eq!(
Evaluator::new(v.as_slice(), &mut cache, &mut prev, &mut Vec::new()).get_recall(),
Ok(Some(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![u32::from(b'9' - i)])
))))
);
}
assert_eq!(
Evaluator::new(b"@@", &mut cache, &mut prev, &mut Vec::new()).get_recall(),
Ok(Some(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![8])
))))
);
_ = Evaluator::new(b"9\r\n", &mut cache, &mut prev, &mut Vec::new())
.evaluate()
.unwrap();
_ = Evaluator::new(b"s\n", &mut cache, &mut prev, &mut Vec::new())
.evaluate()
.unwrap();
for i in b'1'..=b'8' {
v[1] = i;
assert_eq!(
Evaluator::new(v.as_slice(), &mut cache, &mut prev, &mut Vec::new()).get_recall(),
Ok(Some(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![u32::from((b'9' + 1) - i)])
))))
);
}
}
#[cfg(not(feature = "rand"))]
#[test]
fn abs() {
assert_eq!(
Evaluator::new(b"|1", &mut Cache::new(), &mut None, &mut Vec::new()).get_abs(),
Err(InvalidAbs(2))
);
assert_eq!(
Evaluator::new(b"||1 + 2|", &mut Cache::new(), &mut None, &mut Vec::new()).get_abs(),
Err(InvalidAbs(8))
);
assert_eq!(
Evaluator::new(
b"| 0\t \t |",
&mut Cache::new(),
&mut None,
&mut Vec::new()
)
.get_abs(),
Ok(Some(Ratio::from_integer(BigInt::from_biguint(
Sign::NoSign,
BigUint::new(Vec::new())
))))
);
assert_eq!(
Evaluator::new(b"| - 5 |", &mut Cache::new(), &mut None, &mut Vec::new()).get_abs(),
Ok(Some(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![5])
))))
);
assert_eq!(
Evaluator::new(
b"| \t| 2 - 5| * 9 |",
&mut Cache::new(),
&mut None,
&mut Vec::new()
)
.get_abs(),
Ok(Some(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![27])
))))
);
assert_eq!(
Evaluator::new(b" \t|9|", &mut Cache::new(), &mut None, &mut Vec::new()).get_abs(),
Ok(None)
);
}
#[cfg(not(feature = "rand"))]
#[test]
fn round() {
assert_eq!(
Evaluator::new(b"round(1", &mut Cache::new(), &mut None, &mut Vec::new()).get_round(),
Err(InvalidRound(7))
);
assert_eq!(
Evaluator::new(b"round(1,", &mut Cache::new(), &mut None, &mut Vec::new()).get_round(),
Err(InvalidRound(8))
);
assert_eq!(
Evaluator::new(b"round(1,2", &mut Cache::new(), &mut None, &mut Vec::new()).get_round(),
Err(InvalidRound(9))
);
assert_eq!(
Evaluator::new(
b"round(1,10)",
&mut Cache::new(),
&mut None,
&mut Vec::new()
)
.get_round(),
Err(InvalidRound(9))
);
assert_eq!(
Evaluator::new(b"round(1,a)", &mut Cache::new(), &mut None, &mut Vec::new()).get_round(),
Err(InvalidRound(8))
);
assert_eq!(
Evaluator::new(
b"round(2, 7)",
&mut Cache::new(),
&mut None,
&mut Vec::new()
)
.get_round(),
Ok(Some(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![2])
))))
);
assert_eq!(
Evaluator::new(
b"round(2.677, 1)",
&mut Cache::new(),
&mut None,
&mut Vec::new()
)
.get_round(),
Ok(Some(Ratio::new(
BigInt::from_biguint(Sign::Plus, BigUint::new(vec![27])),
BigInt::from_biguint(Sign::Plus, BigUint::new(vec![10]))
)))
);
}
#[expect(clippy::too_many_lines, reason = "a lot to test")]
#[cfg(feature = "rand")]
#[test]
fn rand() {
assert_eq!(
Evaluator::new(
b"rand(1",
&mut Cache::new(),
&mut None,
&mut Vec::new(),
&mut rand::rng()
)
.get_rand(),
Err(LangErr::InvalidRand(6))
);
assert_eq!(
Evaluator::new(
b"rand(1,2",
&mut Cache::new(),
&mut None,
&mut Vec::new(),
&mut rand::rng()
)
.get_rand(),
Err(LangErr::InvalidRand(8))
);
assert_eq!(
Evaluator::new(
b"rand(1/2,3/4)",
&mut Cache::new(),
&mut None,
&mut Vec::new(),
&mut rand::rng(),
)
.get_rand(),
Err(LangErr::RandNoInts(13))
);
assert_eq!(
Evaluator::new(
b"rand(-100000000000000000000000,-1000000000000000000000)",
&mut Cache::new(),
&mut None,
&mut Vec::new(),
&mut rand::rng(),
)
.get_rand(),
Err(LangErr::RandNoInts(55))
);
assert_eq!(
Evaluator::new(
b"rand(2/3,1/3)",
&mut Cache::new(),
&mut None,
&mut Vec::new(),
&mut rand::rng(),
)
.get_rand(),
Err(LangErr::RandInvalidArgs(13))
);
assert_eq!(
Evaluator::new(
b" rand(2/3,2)",
&mut Cache::new(),
&mut None,
&mut Vec::new(),
&mut rand::rng(),
)
.get_rand(),
Err(MissingTerm(0))
);
assert!(
Evaluator::new(
b"rand(2, 7)",
&mut Cache::new(),
&mut None,
&mut Vec::new(),
&mut rand::rng()
)
.get_rand()
.is_ok_and(|r| {
let int = r.numer();
int >= &BigInt::from_biguint(Sign::Plus, BigUint::new(vec![2]))
&& *int <= BigInt::from_biguint(Sign::Plus, BigUint::new(vec![7]))
})
);
assert!(
Evaluator::new(
b"rand()",
&mut Cache::new(),
&mut None,
&mut Vec::new(),
&mut rand::rng()
)
.get_rand()
.is_ok_and(|r| {
let int = r.numer();
int >= &BigInt::from(i64::MIN) && *int <= BigInt::from(i64::MAX)
})
);
for _ in 0..100u8 {
assert!(
Evaluator::new(
b"rand(2, 2)",
&mut Cache::new(),
&mut None,
&mut Vec::new(),
&mut rand::rng()
)
.get_rand()
.is_ok_and(|r| *r.numer() == BigInt::from_biguint(Sign::Plus, BigUint::new(vec![2])))
);
}
}
#[expect(
clippy::indexing_slicing,
clippy::unwrap_used,
reason = "comment justifies correctness"
)]
#[cfg(feature = "rand")]
#[test]
#[ignore = "slow"]
fn rand_uni() {
const COUNT: u32 = 999_999;
#[expect(
clippy::integer_division,
clippy::integer_division_remainder_used,
reason = "correct"
)]
const LOWER: u32 = COUNT * 33 / 100;
#[expect(
clippy::integer_division,
clippy::integer_division_remainder_used,
reason = "correct"
)]
const UPPER: u32 = COUNT * 101 / 300;
let mut vals = [0u32; 3];
let mut vec = Vec::new();
let mut cache = Cache::new();
let mut none = None;
for _ in 1..COUNT {
vals[usize::try_from(
Evaluator::new(
b"rand(-1, 1)",
&mut cache,
&mut none,
&mut vec,
&mut rand::rng(),
)
.get_rand()
.unwrap()
.numer()
.to_i32()
.unwrap()
+ 1i32,
)
.unwrap()] += 1;
}
assert_eq!(
vals.into_iter().try_fold(false, |_, r| {
if (LOWER..=UPPER).contains(&r) {
Ok(true)
} else {
Err(false)
}
}),
Ok(true)
);
}
#[allow(
clippy::allow_attributes,
reason = "unwrap_used only fires when rand is not enabled"
)]
#[allow(clippy::unwrap_used, reason = "comments justify correctness")]
#[test]
fn term() {
#[cfg(not(feature = "rand"))]
assert_eq!(
Evaluator::new(b"0000.00000", &mut Cache::new(), &mut None, &mut Vec::new()).get_term(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::NoSign,
BigUint::new(Vec::new())
)))
);
#[cfg(not(feature = "rand"))]
assert_eq!(
Evaluator::new(b"(4)", &mut Cache::new(), &mut None, &mut Vec::new()).get_term(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![4])
)))
);
#[cfg(not(feature = "rand"))]
assert_eq!(
Evaluator::new(
b"round(-2/3,2)",
&mut Cache::new(),
&mut None,
&mut Vec::new()
)
.get_term(),
Ok(Ratio::new(
BigInt::from_biguint(Sign::Minus, BigUint::new(vec![67])),
BigInt::from_biguint(Sign::Plus, BigUint::new(vec![100]))
))
);
#[cfg(feature = "rand")]
drop(
Evaluator::new(
b"rand()",
&mut Cache::new(),
&mut None,
&mut Vec::new(),
&mut rand::rng(),
)
.get_term()
.unwrap(),
);
#[cfg(feature = "rand")]
drop(
Evaluator::new(
b"rand(-13/93, 833)",
&mut Cache::new(),
&mut None,
&mut Vec::new(),
&mut rand::rng(),
)
.get_term()
.unwrap(),
);
#[cfg(not(feature = "rand"))]
assert_eq!(
Evaluator::new(b"rand()", &mut Cache::new(), &mut None, &mut Vec::new()).get_term(),
Err(MissingTerm(0))
);
#[cfg(not(feature = "rand"))]
assert_eq!(
Evaluator::new(b"|4|", &mut Cache::new(), &mut None, &mut Vec::new()).get_term(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![4])
)))
);
#[cfg(not(feature = "rand"))]
assert_eq!(
Evaluator::new(b" 2", &mut Cache::new(), &mut None, &mut Vec::new()).get_term(),
Err(MissingTerm(0))
);
#[cfg(not(feature = "rand"))]
let mut prev = None;
#[cfg(not(feature = "rand"))]
let mut cache = Cache::new();
#[cfg(not(feature = "rand"))]
{
_ = Evaluator::new(b"1\n", &mut cache, &mut prev, &mut Vec::new())
.evaluate()
.unwrap();
_ = Evaluator::new(b"s\n", &mut cache, &mut prev, &mut Vec::new())
.evaluate()
.unwrap();
}
#[cfg(not(feature = "rand"))]
assert_eq!(
Evaluator::new(b"@", &mut cache, &mut prev, &mut Vec::new()).get_term(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![1])
)))
);
}
#[expect(clippy::unwrap_used, reason = "comments justify correctness")]
#[cfg(not(feature = "rand"))]
#[test]
fn factorial() {
assert_eq!(
Evaluator::new(b"(-1)!", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(),
Err(NotNonNegIntFact(5))
);
assert_eq!(
Evaluator::new(b"2.5!", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(),
Err(NotNonNegIntFact(4))
);
assert_eq!(
Evaluator::new(b"7", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![7])
)))
);
assert_eq!(
Evaluator::new(b"(7)", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![7])
)))
);
assert_eq!(
Evaluator::new(b"|7|", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![7])
)))
);
let mut prev = None;
let mut cache = Cache::new();
_ = Evaluator::new(b"3\n", &mut cache, &mut prev, &mut Vec::new())
.evaluate()
.unwrap();
_ = Evaluator::new(b"s\n", &mut cache, &mut prev, &mut Vec::new())
.evaluate()
.unwrap();
assert_eq!(
Evaluator::new(b"@!", &mut cache, &mut prev, &mut Vec::new()).get_fact(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![6])
)))
);
assert_eq!(
Evaluator::new(b"@", &mut cache, &mut prev, &mut Vec::new()).get_fact(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![3])
)))
);
assert_eq!(
Evaluator::new(b"0.0!", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![1])
)))
);
assert_eq!(
Evaluator::new(b"1!", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![1])
)))
);
assert_eq!(
Evaluator::new(b"4! \t", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![24])
)))
);
assert_eq!(
Evaluator::new(b"3!! ", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![720])
)))
);
assert_eq!(
Evaluator::new(b"2!+3", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![2])
)))
);
assert_eq!(
Evaluator::new(b" 2!", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(),
Err(MissingTerm(0))
);
assert_eq!(
Evaluator::new(b"\t2!", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(),
Err(MissingTerm(0))
);
assert_eq!(
Evaluator::new(b"-2!", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(),
Err(MissingTerm(0))
);
}
#[expect(
clippy::cognitive_complexity,
clippy::too_many_lines,
reason = "a lot to test"
)]
#[cfg(not(feature = "rand"))]
#[test]
fn exp() {
assert_eq!(
Evaluator::new(b"1 ^\t 0", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![1])
)))
);
assert_eq!(
Evaluator::new(b"1^0.5", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![1])
)))
);
assert_eq!(
Evaluator::new(b"1^(-1/2)", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![1])
)))
);
assert_eq!(
Evaluator::new(b"1.0^(-2)", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![1])
)))
);
assert_eq!(
Evaluator::new(b"0^0", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![1])
)))
);
assert_eq!(
Evaluator::new(b"0^1", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::NoSign,
BigUint::new(vec![0])
)))
);
assert_eq!(
Evaluator::new(b"0^0.5", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::NoSign,
BigUint::new(vec![0])
)))
);
assert_eq!(
Evaluator::new(b"4^0", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![1])
)))
);
assert_eq!(
Evaluator::new(b"4^1", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![4])
)))
);
assert_eq!(
Evaluator::new(b"4^(-2)", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(),
Ok(Ratio::new(
BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1])),
BigInt::from_biguint(Sign::Plus, BigUint::new(vec![16]))
))
);
assert_eq!(
Evaluator::new(b"(-4)^0", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![1])
)))
);
assert_eq!(
Evaluator::new(b"(-4)^1", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Minus,
BigUint::new(vec![4])
)))
);
assert_eq!(
Evaluator::new(b"(-4)^2", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![16])
)))
);
assert_eq!(
Evaluator::new(b"(-4)^(-2)", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(),
Ok(Ratio::new(
BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1])),
BigInt::from_biguint(Sign::Plus, BigUint::new(vec![16]))
))
);
assert_eq!(
Evaluator::new(b"(-4)^(-3)", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(),
Ok(Ratio::new(
BigInt::from_biguint(Sign::Minus, BigUint::new(vec![1])),
BigInt::from_biguint(Sign::Plus, BigUint::new(vec![64]))
))
);
assert_eq!(
Evaluator::new(b"(2/3)^0", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![1])
)))
);
assert_eq!(
Evaluator::new(b"(2/3)^(2)", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(),
Ok(Ratio::new(
BigInt::from_biguint(Sign::Plus, BigUint::new(vec![4])),
BigInt::from_biguint(Sign::Plus, BigUint::new(vec![9]))
))
);
assert_eq!(
Evaluator::new(b"(2/3)^(-3)", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(),
Ok(Ratio::new(
BigInt::from_biguint(Sign::Plus, BigUint::new(vec![27])),
BigInt::from_biguint(Sign::Plus, BigUint::new(vec![8]))
))
);
assert_eq!(
Evaluator::new(b"(-2/3)^0", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![1])
)))
);
assert_eq!(
Evaluator::new(b"(-2/3)^(2)", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(),
Ok(Ratio::new(
BigInt::from_biguint(Sign::Plus, BigUint::new(vec![4])),
BigInt::from_biguint(Sign::Plus, BigUint::new(vec![9]))
))
);
assert_eq!(
Evaluator::new(b"(-2/3)^(3)", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(),
Ok(Ratio::new(
BigInt::from_biguint(Sign::Minus, BigUint::new(vec![8])),
BigInt::from_biguint(Sign::Plus, BigUint::new(vec![27]))
))
);
assert_eq!(
Evaluator::new(
b"(-2/3)^(-2)",
&mut Cache::new(),
&mut None,
&mut Vec::new()
)
.get_exps(),
Ok(Ratio::new(
BigInt::from_biguint(Sign::Plus, BigUint::new(vec![9])),
BigInt::from_biguint(Sign::Plus, BigUint::new(vec![4]))
))
);
assert_eq!(
Evaluator::new(
b"(-2/3)^(-3)",
&mut Cache::new(),
&mut None,
&mut Vec::new()
)
.get_exps(),
Ok(Ratio::new(
BigInt::from_biguint(Sign::Minus, BigUint::new(vec![27])),
BigInt::from_biguint(Sign::Plus, BigUint::new(vec![8]))
))
);
assert_eq!(
Evaluator::new(
b"(4/9)^(-1/2)",
&mut Cache::new(),
&mut None,
&mut Vec::new()
)
.get_exps(),
Ok(Ratio::new(
BigInt::from_biguint(Sign::Plus, BigUint::new(vec![3])),
BigInt::from_biguint(Sign::Plus, BigUint::new(vec![2]))
))
);
assert_eq!(
Evaluator::new(b"0^(-1)", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(),
Err(ExpDivByZero(6))
);
assert_eq!(
Evaluator::new(b"2^(1/3)", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(),
Err(ExpIsNotIntOrOneHalf(7))
);
assert_eq!(
Evaluator::new(b"2^(1/2)", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(),
Err(SqrtDoesNotExist(7))
);
assert_eq!(
Evaluator::new(b"3!", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![6])
)))
);
assert_eq!(
Evaluator::new(b"2^3!", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![64])
)))
);
assert_eq!(
Evaluator::new(b"3!^2", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![36])
)))
);
assert_eq!(
Evaluator::new(b" 2^2", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(),
Err(MissingTerm(0))
);
assert_eq!(
Evaluator::new(b"\t2^2", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(),
Err(MissingTerm(0))
);
}
#[cfg(not(feature = "rand"))]
#[test]
fn neg() {
assert_eq!(
Evaluator::new(b"-1", &mut Cache::new(), &mut None, &mut Vec::new()).get_neg(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Minus,
BigUint::new(vec![1])
)))
);
assert_eq!(
Evaluator::new(b"- \t - 1", &mut Cache::new(), &mut None, &mut Vec::new()).get_neg(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![1])
)))
);
assert_eq!(
Evaluator::new(b"-0", &mut Cache::new(), &mut None, &mut Vec::new()).get_neg(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::NoSign,
BigUint::new(Vec::new())
)))
);
assert_eq!(
Evaluator::new(b"-2^2", &mut Cache::new(), &mut None, &mut Vec::new()).get_neg(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Minus,
BigUint::new(vec![4])
)))
);
assert_eq!(
Evaluator::new(b"2.0^2.0", &mut Cache::new(), &mut None, &mut Vec::new()).get_neg(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![4])
)))
);
assert_eq!(
Evaluator::new(b" -2", &mut Cache::new(), &mut None, &mut Vec::new()).get_neg(),
Err(MissingTerm(0))
);
assert_eq!(
Evaluator::new(b"\t-2", &mut Cache::new(), &mut None, &mut Vec::new()).get_neg(),
Err(MissingTerm(0))
);
}
#[expect(
clippy::cognitive_complexity,
clippy::too_many_lines,
reason = "a lot to test"
)]
#[cfg(not(feature = "rand"))]
#[test]
fn mult() {
assert_eq!(
Evaluator::new(b"2 * 3", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![6])
)))
);
assert_eq!(
Evaluator::new(
b"-2 * \t 3",
&mut Cache::new(),
&mut None,
&mut Vec::new()
)
.get_mults(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Minus,
BigUint::new(vec![6])
)))
);
assert_eq!(
Evaluator::new(
b"2\t * -3.0",
&mut Cache::new(),
&mut None,
&mut Vec::new()
)
.get_mults(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Minus,
BigUint::new(vec![6])
)))
);
assert_eq!(
Evaluator::new(b"-2.5*-3.5", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(),
Ok(Ratio::new(
BigInt::from_biguint(Sign::Plus, BigUint::new(vec![35])),
BigInt::from_biguint(Sign::Plus, BigUint::new(vec![4]))
))
);
assert_eq!(
Evaluator::new(b"4.0\t / 6", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(),
Ok(Ratio::new(
BigInt::from_biguint(Sign::Plus, BigUint::new(vec![2])),
BigInt::from_biguint(Sign::Plus, BigUint::new(vec![3]))
))
);
assert_eq!(
Evaluator::new(b"6/3", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![2])
)))
);
assert_eq!(
Evaluator::new(b"-6/3", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Minus,
BigUint::new(vec![2])
)))
);
assert_eq!(
Evaluator::new(b"6/-3", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Minus,
BigUint::new(vec![2])
)))
);
assert_eq!(
Evaluator::new(
b"- 6 /\t - 3",
&mut Cache::new(),
&mut None,
&mut Vec::new()
)
.get_mults(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![2])
)))
);
assert!(
Evaluator::new(b"1/1.5", &mut Cache::new(), &mut None, &mut Vec::new())
.get_mults()
.is_ok_and(|r| {
Evaluator::new(b"1/3/2", &mut Cache::new(), &mut None, &mut Vec::new())
.get_mults()
.is_ok_and(|r2| r != r2)
})
);
assert!(
Evaluator::new(b"1/1.5", &mut Cache::new(), &mut None, &mut Vec::new())
.get_mults()
.is_ok_and(|r| {
Evaluator::new(b"1/(3/2)", &mut Cache::new(), &mut None, &mut Vec::new())
.get_mults()
.is_ok_and(|r2| r == r2)
})
);
assert_eq!(
Evaluator::new(b"-2.0", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Minus,
BigUint::new(vec![2])
)))
);
assert_eq!(
Evaluator::new(b" 2*2", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(),
Err(MissingTerm(0))
);
assert_eq!(
Evaluator::new(b"\t2/2", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(),
Err(MissingTerm(0))
);
assert_eq!(
Evaluator::new(
b"4.0\t mod 6",
&mut Cache::new(),
&mut None,
&mut Vec::new()
)
.get_mults(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![4])
),))
);
assert_eq!(
Evaluator::new(b"5 mod 3", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![2])
)))
);
assert_eq!(
Evaluator::new(b"-5 mod 3", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![1])
)))
);
assert_eq!(
Evaluator::new(b"5 mod -3", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![2])
)))
);
assert_eq!(
Evaluator::new(
b"-5 mod\t -3",
&mut Cache::new(),
&mut None,
&mut Vec::new()
)
.get_mults(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![1])
)))
);
assert_eq!(
Evaluator::new(b"2/0", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(),
Err(DivByZero(3))
);
assert_eq!(
Evaluator::new(b"2 mod 0", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(),
Err(ModZero(7))
);
assert_eq!(
Evaluator::new(b"3.2 mod 1", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(),
Err(ModIsNotInt(4))
);
assert_eq!(
Evaluator::new(b"3 mod 3.2", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(),
Err(ModIsNotInt(9))
);
assert_eq!(
Evaluator::new(b"2*2^2", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![8])
)))
);
assert_eq!(
Evaluator::new(b"8/2^2", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![2])
)))
);
assert_eq!(
Evaluator::new(b"8 mod 3^2", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![8])
)))
);
}
#[expect(clippy::too_many_lines, reason = "a lot to test")]
#[cfg(not(feature = "rand"))]
#[test]
fn add() {
assert_eq!(
Evaluator::new(b"2 + 3", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![5])
)))
);
assert_eq!(
Evaluator::new(b"-2 + 3", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![1])
)))
);
assert_eq!(
Evaluator::new(
b"2 + \t -3.0",
&mut Cache::new(),
&mut None,
&mut Vec::new()
)
.get_adds(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Minus,
BigUint::new(vec![1])
)))
);
assert_eq!(
Evaluator::new(b"-2.5+-3.5", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Minus,
BigUint::new(vec![6])
)))
);
assert_eq!(
Evaluator::new(b"4.0\t - 6", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Minus,
BigUint::new(vec![2])
)))
);
assert_eq!(
Evaluator::new(b"6-3", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![3])
)))
);
assert_eq!(
Evaluator::new(b"-6-3", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Minus,
BigUint::new(vec![9])
)))
);
assert_eq!(
Evaluator::new(b"6--3", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![9])
)))
);
assert_eq!(
Evaluator::new(
b"- 6 -\t - 3",
&mut Cache::new(),
&mut None,
&mut Vec::new()
)
.get_adds(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Minus,
BigUint::new(vec![3])
)))
);
assert_eq!(
Evaluator::new(b"2 * 8", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![16])
)))
);
assert_eq!(
Evaluator::new(b"8 /\t 2", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![4])
)))
);
assert_eq!(
Evaluator::new(b" 2+2", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(),
Err(MissingTerm(0))
);
assert_eq!(
Evaluator::new(b" 2-2", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(),
Err(MissingTerm(0))
);
assert_eq!(
Evaluator::new(b"2+2*2", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![6])
)))
);
assert_eq!(
Evaluator::new(b"2+2/2", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![3])
)))
);
assert_eq!(
Evaluator::new(b"2-2*2", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Minus,
BigUint::new(vec![2])
)))
);
assert_eq!(
Evaluator::new(b"2-2/2", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(),
Ok(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![1])
)))
);
}
#[cfg(not(feature = "rand"))]
#[test]
fn exit() {
assert_eq!(
Evaluator::new(b" q \n", &mut Cache::new(), &mut None, &mut Vec::new()).evaluate(),
Ok(Exit)
);
assert_eq!(
Evaluator::new(
b" q \r\n",
&mut Cache::new(),
&mut None,
&mut Vec::new()
)
.evaluate(),
Ok(Exit)
);
assert_eq!(
Evaluator::new(b"q\n", &mut Cache::new(), &mut None, &mut Vec::new()).evaluate(),
Ok(Exit)
);
assert_eq!(
Evaluator::new(b"q\r\n", &mut Cache::new(), &mut None, &mut Vec::new()).evaluate(),
Ok(Exit)
);
assert_eq!(
Evaluator::new(b"\rq\n", &mut Cache::new(), &mut None, &mut Vec::new()).evaluate(),
Err(MissingTerm(0))
);
assert_eq!(
Evaluator::new(b"\tq\n", &mut Cache::new(), &mut None, &mut Vec::new()).evaluate(),
Ok(Exit)
);
assert_eq!(
Evaluator::new(b"q\n\n", &mut Cache::new(), &mut None, &mut Vec::new()).evaluate(),
Err(InvalidQuit)
);
assert_eq!(
Evaluator::new(b"\nq\n", &mut Cache::new(), &mut None, &mut Vec::new()).evaluate(),
Err(MissingTerm(0))
);
assert_eq!(
Evaluator::new(b"q", &mut Cache::new(), &mut None, &mut Vec::new()).evaluate(),
Ok(Exit)
);
}
#[expect(clippy::unwrap_used, reason = "comment justifies correctness")]
#[cfg(not(feature = "rand"))]
#[test]
fn store() {
let mut prev = None;
let mut cache = Cache::new();
_ = Evaluator::new(b"1\n", &mut cache, &mut prev, &mut Vec::new())
.evaluate()
.unwrap();
assert!(cache.is_empty());
assert_eq!(
Evaluator::new(b"s\n", &mut cache, &mut prev, &mut Vec::new()).evaluate(),
Ok(Store(&Some(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![1])
)))))
);
assert_eq!(cache.len(), 1);
assert_eq!(
Evaluator::new(b"s2\n", &mut Cache::new(), &mut None, &mut Vec::new()).evaluate(),
Err(InvalidStore)
);
}
#[expect(clippy::too_many_lines, reason = "a lot to test")]
#[cfg(not(feature = "rand"))]
#[test]
fn eval() {
use core::str::FromStr as _;
let mut prev = None;
let mut cache = Cache::new();
let mut exp = Vec::new();
assert_eq!(
Evaluator::new(b"1+2\n", &mut cache, &mut prev, &mut exp).evaluate(),
Ok(Eval(&Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![3])
))))
);
assert_eq!(
Evaluator::new(b"\t s \n", &mut cache, &mut prev, &mut exp).evaluate(),
Ok(Store(&Some(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![3])
)))))
);
assert_eq!(
Evaluator::new(b"-1/2+2*@\n", &mut cache, &mut prev, &mut exp).evaluate(),
Ok(Eval(&Ratio::new(
BigInt::from_biguint(Sign::Plus, BigUint::new(vec![11])),
BigInt::from_biguint(Sign::Plus, BigUint::new(vec![2]))
)))
);
assert_eq!(
Evaluator::new(b"s\n", &mut cache, &mut prev, &mut exp).evaluate(),
Ok(Store(&Some(Ratio::new(
BigInt::from_biguint(Sign::Plus, BigUint::new(vec![11])),
BigInt::from_biguint(Sign::Plus, BigUint::new(vec![2]))
))))
);
assert_eq!(
Evaluator::new(b"@^@2!\r\n", &mut cache, &mut prev, &mut exp).evaluate(),
Ok(Eval(&Ratio::new(
BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1_771_561])),
BigInt::from_biguint(Sign::Plus, BigUint::new(vec![64]))
)))
);
assert_eq!(
Evaluator::new(b"s\n", &mut cache, &mut prev, &mut exp).evaluate(),
Ok(Store(&Some(Ratio::new(
BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1_771_561])),
BigInt::from_biguint(Sign::Plus, BigUint::new(vec![64]))
))))
);
assert!(
Evaluator::new(
b" \t 1 + (2 * |(7.98\t - 12/7)|) / 4!^@3!^|1-3|\t \n",
&mut cache,
&mut prev,
&mut exp
)
.evaluate().is_ok_and(|r| {
Ratio::from_str("2841328814244153299237884950647090899374680152474331/2841328814244153299237884950647090899374680152473600").is_ok_and(|r2| {
r == Eval(&r2)
})
})
);
assert_eq!(
Evaluator::new(
b" \t round(19/6,0)!\t \r\n",
&mut cache,
&mut prev,
&mut exp
)
.evaluate(),
Ok(Eval(&Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![6])
))))
);
assert_eq!(
Evaluator::new(
b" \t 2^round(19/6,0)!\t \r\n",
&mut cache,
&mut prev,
&mut exp
)
.evaluate(),
Ok(Eval(&Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![64])
))))
);
assert_eq!(
Evaluator::new(b"round(19/6,0)^2\t\n", &mut cache, &mut prev, &mut exp).evaluate(),
Ok(Eval(&Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![9])
))))
);
assert_eq!(
Evaluator::new(&[255, 255, 255, b'\n'], &mut cache, &mut prev, &mut exp).evaluate(),
Err(MissingTerm(0))
);
assert_eq!(
Evaluator::new(&[b'2', 255, b'\n'], &mut cache, &mut prev, &mut exp).evaluate(),
Err(TrailingSyms(1))
);
assert_eq!(
Evaluator::new(b"2\n\n", &mut cache, &mut prev, &mut exp).evaluate(),
Err(TrailingSyms(1))
);
assert_eq!(
prev,
Some(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![9])
)))
);
assert_eq!(
Evaluator::new(b"\n", &mut cache, &mut prev.clone(), &mut exp).evaluate(),
Ok(Empty(&Some(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![9])
)))))
);
assert_eq!(
prev,
Some(Ratio::from_integer(BigInt::from_biguint(
Sign::Plus,
BigUint::new(vec![9])
)))
);
assert_eq!(
Evaluator::new(b"\r\n", &mut cache, &mut prev.clone(), &mut exp).evaluate(),
Ok(Empty(&prev))
);
assert_eq!(
Evaluator::new(&[0u8; 0], &mut cache, &mut prev.clone(), &mut exp).evaluate(),
Ok(Empty(&prev))
);
}
#[cfg(feature = "rand")]
#[test]
fn eval_iter() {
struct Reader<'a> {
data: &'a [u8],
err: bool,
}
impl<'a> Reader<'a> {
fn new(data: &'a [u8]) -> Self {
Self { data, err: true }
}
}
impl Read for Reader<'_> {
#[expect(clippy::indexing_slicing, reason = "comment justifies correctness")]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
if self.err {
self.err = false;
Err(Error::other(""))
} else {
let len = usize::min(buf.len(), self.data.len());
buf[..len].copy_from_slice(&self.data[..len]);
Ok(len)
}
}
}
impl BufRead for Reader<'_> {
fn fill_buf(&mut self) -> io::Result<&[u8]> {
if self.err {
self.err = false;
Err(Error::other(""))
} else {
Ok(self.data)
}
}
#[expect(clippy::indexing_slicing, reason = "comment justifies correctness")]
fn consume(&mut self, amount: usize) {
self.data = &self.data[amount..];
}
}
let mut iter = EvalIter::new(Reader::new(
b"1+2\n4\n\nq\n5\ns\nrand() + rand(-139/@, 2984/134)\nab",
));
assert!(
iter.lend_next()
.is_some_and(|res| res.map_or_else(|e| matches!(e, E::Error(_)), |_| false))
);
assert!(iter.lend_next().is_some_and(|res| {
res.is_ok_and(|e| matches!(e, Eval(r) if r.numer().to_i32() == Some(3i32)))
}));
assert!(iter.lend_next().is_some_and(|res| {
res.is_ok_and(|e| matches!(e, Eval(r) if r.numer().to_i32() == Some(4i32)))
}));
assert!(iter.lend_next().is_some_and(|res| {
res.is_ok_and(
|e| matches!(e, Empty(r) if r.as_ref().is_some_and(|val| val.numer().to_i32() == Some(4i32))),
)
}));
assert!(iter.lend_next().is_none());
assert!(iter.lend_next().is_some_and(|res| {
res.is_ok_and(|e| matches!(e, Eval(r) if r.numer().to_i32() == Some(5i32)))
}));
assert!(iter.lend_next().is_some_and(|res| {
res.is_ok_and(
|e| matches!(e, Store(r) if r.as_ref().is_some_and(|val| val.numer().to_i32() == Some(5i32))),
)
}));
assert!(
iter.lend_next()
.is_some_and(|res| res.is_ok_and(|e| matches!(e, Eval(r) if r.is_integer())))
);
assert!(iter.lend_next().is_some_and(|res| {
res.is_err_and(|err| matches!(err, E::LangErr(ref e) if matches!(*e, MissingTerm(_))))
}));
assert!(iter.lend_next().is_none());
assert!(iter.lend_next().is_none());
assert!(iter.lend_next().is_none());
}
#[ignore = "only to silence lint"]
#[cfg(feature = "std")]
#[test]
fn silence_dead_pub_in_binary_when_only_std() {
drop(E::LangErr(MissingTerm(0)));
drop(EvalIter::new(b""));
}