tests.rs (63575B)
1 use super::{ 2 BigInt, BigUint, Cache, Evaluator, 3 LangErr::MissingTerm, 4 O::{Empty, Eval, Store}, 5 Sign, Vec, vec, 6 }; 7 #[cfg(feature = "rand")] 8 use super::{BufRead, LangErr, LendingIterator as _}; 9 #[cfg(feature = "std")] 10 use super::{E, EvalIter}; 11 #[cfg(not(feature = "rand"))] 12 use super::{ 13 LangErr::{ 14 DivByZero, ExpDivByZero, ExpIsNotIntOrOneHalf, InvalidAbs, InvalidDec, InvalidPar, 15 InvalidQuit, InvalidRound, InvalidStore, ModIsNotInt, ModZero, NotEnoughPrevResults, 16 NotNonNegIntFact, SqrtDoesNotExist, TrailingSyms, 17 }, 18 O::Exit, 19 Ratio, 20 }; 21 #[cfg(not(feature = "rand"))] 22 use alloc::{borrow::ToOwned as _, string::ToString as _}; 23 #[cfg(not(feature = "rand"))] 24 use num_traits::Pow as _; 25 #[cfg(feature = "rand")] 26 use num_traits::ToPrimitive as _; 27 #[cfg(feature = "rand")] 28 use std::io::{self, Error, Read}; 29 #[expect(clippy::too_many_lines, reason = "a lot to test")] 30 #[cfg(not(feature = "rand"))] 31 #[test] 32 fn empty() { 33 // Empty expressions without a previous result return nothing. 34 assert_eq!( 35 Evaluator::new(b"\n", &mut Cache::new(), &mut None, &mut Vec::new()).evaluate(), 36 Ok(Empty(&None)) 37 ); 38 assert_eq!( 39 Evaluator::new( 40 b" \t \t \n", 41 &mut Cache::new(), 42 &mut Some(Ratio::from_integer(BigInt::from_biguint( 43 Sign::Minus, 44 BigUint::new(vec![12]) 45 ))), 46 &mut Vec::new() 47 ) 48 .evaluate() 49 .map(|a| a.to_string()), 50 Ok("> -12".to_owned()) 51 ); 52 assert_eq!( 53 Evaluator::new( 54 b"\t\n", 55 &mut Cache::new(), 56 &mut Some(Ratio::new( 57 BigInt::from_biguint(Sign::Minus, BigUint::new(vec![4])), 58 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![6])) 59 )), 60 &mut Vec::new() 61 ) 62 .evaluate() 63 .map(|a| a.to_string()), 64 Ok("> -0.666666667".to_owned()) 65 ); 66 assert_eq!( 67 Evaluator::new( 68 b"\t\n", 69 &mut Cache::new(), 70 &mut Some(Ratio::new( 71 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1])), 72 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![4_230_196_224, 6])) 73 )), 74 &mut Vec::new() 75 ) 76 .evaluate() 77 .map(|a| a.to_string()), 78 Ok("> 0.000000000".to_owned()) 79 ); 80 assert_eq!( 81 Evaluator::new( 82 b"\t\n", 83 &mut Cache::new(), 84 &mut Some(Ratio::new( 85 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![17])), 86 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![4_230_196_224, 6])) 87 )), 88 &mut Vec::new() 89 ) 90 .evaluate() 91 .map(|a| a.to_string()), 92 Ok("> 0.000000001".to_owned()) 93 ); 94 assert_eq!( 95 Evaluator::new( 96 b"\t\n", 97 &mut Cache::new(), 98 &mut Some(Ratio::new( 99 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![3])), 100 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![10])) 101 )), 102 &mut Vec::new() 103 ) 104 .evaluate() 105 .map(|a| a.to_string()), 106 Ok("> 0.3".to_owned()) 107 ); 108 assert_eq!( 109 Evaluator::new( 110 b"\t\n", 111 &mut Cache::new(), 112 &mut Some(Ratio::new( 113 BigInt::from_biguint(Sign::Minus, BigUint::new(vec![203])), 114 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![10])) 115 )), 116 &mut Vec::new() 117 ) 118 .evaluate() 119 .map(|a| a.to_string()), 120 Ok("> -20.3".to_owned()) 121 ); 122 assert_eq!( 123 Evaluator::new( 124 &[0u8; 0], 125 &mut Cache::new(), 126 &mut Some(Ratio::new( 127 BigInt::from_biguint(Sign::Minus, BigUint::new(vec![203])), 128 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![10])) 129 )), 130 &mut Vec::new() 131 ) 132 .evaluate() 133 .map(|a| a.to_string()), 134 Ok("> -20.3".to_owned()) 135 ); 136 } 137 #[expect(clippy::unreachable, reason = "want to crash when there is a bug")] 138 #[cfg(not(feature = "rand"))] 139 #[test] 140 fn number_literal() { 141 // Normal 0 is fine. 142 assert_eq!( 143 Evaluator::new(b"0", &mut Cache::new(), &mut None, &mut Vec::new()).get_rational(), 144 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 145 Sign::NoSign, 146 BigUint::new(Vec::new()) 147 )))) 148 ); 149 // Leading 0s and trailing 0s are fine. 150 assert_eq!( 151 Evaluator::new(b"0000.00000", &mut Cache::new(), &mut None, &mut Vec::new()).get_rational(), 152 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 153 Sign::NoSign, 154 BigUint::new(Vec::new()) 155 )))) 156 ); 157 // Parsing stops at first non-(digit/period). 158 assert_eq!( 159 Evaluator::new(b"1 0", &mut Cache::new(), &mut None, &mut Vec::new()).get_rational(), 160 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 161 Sign::Plus, 162 BigUint::new(vec![1]) 163 )))) 164 ); 165 let int = b"3397450981271938475135134759823759835414"; 166 let frac = b"913759810573549872354897210539127530981570"; 167 let left = Ratio::from_integer( 168 BigInt::parse_bytes(int, 10).unwrap_or_else(|| unreachable!("bug in BigInt::parse_bytes")), 169 ); 170 let right = Ratio::new( 171 BigInt::parse_bytes(frac, 10).unwrap_or_else(|| unreachable!("bug in BigInt::parse_bytes")), 172 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![10]).pow(frac.len() + 1)), 173 ); 174 let mut vec = Vec::new(); 175 vec.extend_from_slice(int); 176 vec.push(b'.'); 177 vec.push(b'0'); 178 vec.extend_from_slice(frac); 179 // Test a number whose integer and fraction portions are larger than u128::MAX. 180 assert_eq!( 181 Evaluator::new( 182 vec.as_slice(), 183 &mut Cache::new(), 184 &mut None, 185 &mut Vec::new() 186 ) 187 .get_rational(), 188 Ok(Some(left + right)) 189 ); 190 // Leading 0s and trailing 0s for a non-zero value are fine. 191 assert_eq!( 192 Evaluator::new( 193 b"000000014.0000000000000", 194 &mut Cache::new(), 195 &mut None, 196 &mut Vec::new() 197 ) 198 .get_rational(), 199 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 200 Sign::Plus, 201 BigUint::new(vec![14]) 202 )))) 203 ); 204 // A sequence of digits followed immediately by a decimal point but no digits after is invalid. 205 assert_eq!( 206 Evaluator::new(b"1.", &mut Cache::new(), &mut None, &mut Vec::new()).get_rational(), 207 Err(InvalidDec(2)) 208 ); 209 // A sequence of digits followed immediately by a decimal point but no digits after is invalid. 210 // This just shows that spaces are not ignored in number literals. 211 assert_eq!( 212 Evaluator::new(b"1. 2", &mut Cache::new(), &mut None, &mut Vec::new()).get_rational(), 213 Err(InvalidDec(2)) 214 ); 215 // Non-whitespace starting the input is valid but produces no value. 216 // This also shows that an invalid byte sequence does not produce an error here. 217 assert_eq!( 218 Evaluator::new(b"a1", &mut Cache::new(), &mut None, &mut Vec::new()).get_rational(), 219 Ok(None) 220 ); 221 // A space starting the input is valid but produces no value. 222 assert_eq!( 223 Evaluator::new(b" 1", &mut Cache::new(), &mut None, &mut Vec::new()).get_rational(), 224 Ok(None) 225 ); 226 // A tab starting the input is valid but produces no value. 227 assert_eq!( 228 Evaluator::new(b"\t1", &mut Cache::new(), &mut None, &mut Vec::new()).get_rational(), 229 Ok(None) 230 ); 231 // Negative literals don't exist, so this should succeed but produce nothing. 232 assert_eq!( 233 Evaluator::new(b"-1", &mut Cache::new(), &mut None, &mut Vec::new()).get_rational(), 234 Ok(None) 235 ); 236 // '/' is division and not part of a number literal so only the "numerator" is parsed. 237 assert_eq!( 238 Evaluator::new(b"1/2", &mut Cache::new(), &mut None, &mut Vec::new()).get_rational(), 239 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 240 Sign::Plus, 241 BigUint::new(vec![1]) 242 )))) 243 ); 244 // A sequence of digits followed by invalid bytes is valid and produces a number equal to the digits before the invalid bytes. 245 assert_eq!( 246 Evaluator::new(b"130alj", &mut Cache::new(), &mut None, &mut Vec::new()).get_rational(), 247 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 248 Sign::Plus, 249 BigUint::new(vec![130]) 250 )))) 251 ); 252 } 253 #[cfg(not(feature = "rand"))] 254 #[test] 255 fn par() { 256 // Missing closing ')' 257 assert_eq!( 258 Evaluator::new(b"(1", &mut Cache::new(), &mut None, &mut Vec::new()).get_par(), 259 Err(InvalidPar(2)) 260 ); 261 assert_eq!( 262 Evaluator::new(b"((1\t + 2)", &mut Cache::new(), &mut None, &mut Vec::new()).get_par(), 263 Err(InvalidPar(9)) 264 ); 265 assert_eq!( 266 Evaluator::new(b"( 0 \t )", &mut Cache::new(), &mut None, &mut Vec::new()).get_par(), 267 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 268 Sign::NoSign, 269 BigUint::new(Vec::new()) 270 )))) 271 ); 272 assert_eq!( 273 Evaluator::new(b"( - \t 5 )", &mut Cache::new(), &mut None, &mut Vec::new()).get_par(), 274 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 275 Sign::Minus, 276 BigUint::new(vec![5]) 277 )))) 278 ); 279 assert_eq!( 280 Evaluator::new( 281 b"( ( 2 -\t 5) * 9 )", 282 &mut Cache::new(), 283 &mut None, 284 &mut Vec::new() 285 ) 286 .get_par(), 287 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 288 Sign::Minus, 289 BigUint::new(vec![27]) 290 )))) 291 ); 292 } 293 #[expect(clippy::too_many_lines, reason = "a lot to test")] 294 #[expect( 295 clippy::indexing_slicing, 296 clippy::unwrap_used, 297 reason = "comments justify correctness" 298 )] 299 #[cfg(not(feature = "rand"))] 300 #[test] 301 fn recall_expression() { 302 // If the input does not start with '@', then it's valid but produces nothing. 303 assert_eq!( 304 Evaluator::new(b"1", &mut Cache::new(), &mut None, &mut Vec::new()).get_recall(), 305 Ok(None) 306 ); 307 assert_eq!( 308 Evaluator::new(b"a", &mut Cache::new(), &mut None, &mut Vec::new()).get_recall(), 309 Ok(None) 310 ); 311 assert_eq!( 312 Evaluator::new(b" @", &mut Cache::new(), &mut None, &mut Vec::new()).get_recall(), 313 Ok(None) 314 ); 315 assert_eq!( 316 Evaluator::new(b"\t@", &mut Cache::new(), &mut None, &mut Vec::new()).get_recall(), 317 Ok(None) 318 ); 319 // Invalid recall expression since there are no previous results. 320 assert_eq!( 321 Evaluator::new(b"@", &mut Cache::new(), &mut None, &mut Vec::new()).get_recall(), 322 Err(NotEnoughPrevResults(0)) 323 ); 324 // Invalid recall expression since there are no previous results. 325 assert_eq!( 326 Evaluator::new(b"@4", &mut Cache::new(), &mut None, &mut Vec::new()).get_recall(), 327 Err(NotEnoughPrevResults(0)) 328 ); 329 // Invalid recall expression since there are no previous results. 330 // The input violates our grammar, but this error happens before that. 331 assert_eq!( 332 Evaluator::new(b"@0", &mut Cache::new(), &mut None, &mut Vec::new()).get_recall(), 333 Err(NotEnoughPrevResults(0)) 334 ); 335 // Successfully extract previous expression. 336 let mut prev = None; 337 let mut cache = Cache::new(); 338 // Quick check that `Ok` is returned. 339 _ = Evaluator::new(b"1\n", &mut cache, &mut prev, &mut Vec::new()) 340 .evaluate() 341 .unwrap(); 342 // Quick check that `Ok` is returned. 343 _ = Evaluator::new(b" s \r\n", &mut cache, &mut prev, &mut Vec::new()) 344 .evaluate() 345 .unwrap(); 346 assert_eq!( 347 Evaluator::new(b"@", &mut cache, &mut prev, &mut Vec::new()).get_recall(), 348 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 349 Sign::Plus, 350 BigUint::new(vec![1]) 351 )))) 352 ); 353 // Invalid characters are ignored at this stage. 354 assert_eq!( 355 Evaluator::new(b"@&", &mut cache, &mut prev, &mut Vec::new()).get_recall(), 356 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 357 Sign::Plus, 358 BigUint::new(vec![1]) 359 )))) 360 ); 361 // 0 is not a valid stored value only 1-8 are. 362 assert_eq!( 363 Evaluator::new(b"@0", &mut cache, &mut prev, &mut Vec::new()).get_recall(), 364 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 365 Sign::Plus, 366 BigUint::new(vec![1]) 367 )))) 368 ); 369 // 9 is not a valid stored value only 1-8 are. 370 assert_eq!( 371 Evaluator::new(b"@9", &mut cache, &mut prev, &mut Vec::new()).get_recall(), 372 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 373 Sign::Plus, 374 BigUint::new(vec![1]) 375 )))) 376 ); 377 // Spaces are not cleaned; otherwise this would error since we only have 1 stored value. 378 assert_eq!( 379 Evaluator::new(b"@ 2", &mut cache, &mut prev, &mut Vec::new()).get_recall(), 380 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 381 Sign::Plus, 382 BigUint::new(vec![1]) 383 )))) 384 ); 385 // Tabs are not cleaned; otherwise this would error since we only have 1 stored value. 386 assert_eq!( 387 Evaluator::new(b"@\t2", &mut cache, &mut prev, &mut Vec::new()).get_recall(), 388 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 389 Sign::Plus, 390 BigUint::new(vec![1]) 391 )))) 392 ); 393 // One digits are looked at so this is not @<ten>. 394 assert_eq!( 395 Evaluator::new(b"@10", &mut cache, &mut prev, &mut Vec::new()).get_recall(), 396 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 397 Sign::Plus, 398 BigUint::new(vec![1]) 399 )))) 400 ); 401 // Invalid recall expression since there is only one stored result. 402 assert_eq!( 403 Evaluator::new(b"@2", &mut cache, &mut prev, &mut Vec::new()).get_recall(), 404 Err(NotEnoughPrevResults(1)) 405 ); 406 // Quick check that `Ok` is returned. 407 _ = Evaluator::new(b"2\r\n", &mut cache, &mut prev, &mut Vec::new()) 408 .evaluate() 409 .unwrap(); 410 // Quick check that `Ok` is returned. 411 _ = Evaluator::new(b"s\n", &mut cache, &mut prev, &mut Vec::new()) 412 .evaluate() 413 .unwrap(); 414 // Stored values correct. 415 assert_eq!( 416 Evaluator::new(b"@", &mut cache, &mut prev, &mut Vec::new()).get_recall(), 417 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 418 Sign::Plus, 419 BigUint::new(vec![2]) 420 )))) 421 ); 422 assert_eq!( 423 Evaluator::new(b"@2", &mut cache, &mut prev, &mut Vec::new()).get_recall(), 424 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 425 Sign::Plus, 426 BigUint::new(vec![1]) 427 )))) 428 ); 429 // Invalid recall expression since there are only three stored results. 430 assert_eq!( 431 Evaluator::new(b"@3", &mut cache, &mut prev, &mut Vec::new()).get_recall(), 432 Err(NotEnoughPrevResults(2)) 433 ); 434 let mut v = vec![0, b'\n']; 435 for i in b'3'..=b'8' { 436 // `v.len() > 0`. 437 v[0] = i; 438 _ = Evaluator::new(v.as_slice(), &mut cache, &mut prev, &mut Vec::new()) 439 .evaluate() 440 .unwrap(); 441 _ = Evaluator::new(b"s\n", &mut cache, &mut prev, &mut Vec::new()) 442 .evaluate() 443 .unwrap(); 444 } 445 v[0] = b'@'; 446 for i in b'1'..=b'8' { 447 // `v.len() > 1`. 448 v[1] = i; 449 // Cache is filled up correctly storing all previous values. 450 assert_eq!( 451 Evaluator::new(v.as_slice(), &mut cache, &mut prev, &mut Vec::new()).get_recall(), 452 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 453 Sign::Plus, 454 BigUint::new(vec![u32::from(b'9' - i)]) 455 )))) 456 ); 457 } 458 // Only parses the first @ since the second @ is not a digit between 1 and 8. 459 assert_eq!( 460 Evaluator::new(b"@@", &mut cache, &mut prev, &mut Vec::new()).get_recall(), 461 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 462 Sign::Plus, 463 BigUint::new(vec![8]) 464 )))) 465 ); 466 // Quick check that `Ok` is returned. 467 _ = Evaluator::new(b"9\r\n", &mut cache, &mut prev, &mut Vec::new()) 468 .evaluate() 469 .unwrap(); 470 // Quick check that `Ok` is returned. 471 _ = Evaluator::new(b"s\n", &mut cache, &mut prev, &mut Vec::new()) 472 .evaluate() 473 .unwrap(); 474 // Oldest value is overwritten; all others remain. 475 for i in b'1'..=b'8' { 476 // `v.len() > 1`. 477 v[1] = i; 478 assert_eq!( 479 Evaluator::new(v.as_slice(), &mut cache, &mut prev, &mut Vec::new()).get_recall(), 480 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 481 Sign::Plus, 482 BigUint::new(vec![u32::from((b'9' + 1) - i)]) 483 )))) 484 ); 485 } 486 } 487 #[cfg(not(feature = "rand"))] 488 #[test] 489 fn abs() { 490 // Missing closing '|' 491 assert_eq!( 492 Evaluator::new(b"|1", &mut Cache::new(), &mut None, &mut Vec::new()).get_abs(), 493 Err(InvalidAbs(2)) 494 ); 495 assert_eq!( 496 Evaluator::new(b"||1 + 2|", &mut Cache::new(), &mut None, &mut Vec::new()).get_abs(), 497 Err(InvalidAbs(8)) 498 ); 499 assert_eq!( 500 Evaluator::new( 501 b"| 0\t \t |", 502 &mut Cache::new(), 503 &mut None, 504 &mut Vec::new() 505 ) 506 .get_abs(), 507 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 508 Sign::NoSign, 509 BigUint::new(Vec::new()) 510 )))) 511 ); 512 assert_eq!( 513 Evaluator::new(b"| - 5 |", &mut Cache::new(), &mut None, &mut Vec::new()).get_abs(), 514 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 515 Sign::Plus, 516 BigUint::new(vec![5]) 517 )))) 518 ); 519 assert_eq!( 520 Evaluator::new( 521 b"| \t| 2 - 5| * 9 |", 522 &mut Cache::new(), 523 &mut None, 524 &mut Vec::new() 525 ) 526 .get_abs(), 527 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 528 Sign::Plus, 529 BigUint::new(vec![27]) 530 )))) 531 ); 532 // If the input does not start with '|', then it's valid but produces nothing. 533 assert_eq!( 534 Evaluator::new(b" \t|9|", &mut Cache::new(), &mut None, &mut Vec::new()).get_abs(), 535 Ok(None) 536 ); 537 } 538 #[cfg(not(feature = "rand"))] 539 #[test] 540 fn round() { 541 // Missing ',<digit>)' 542 assert_eq!( 543 Evaluator::new(b"round(1", &mut Cache::new(), &mut None, &mut Vec::new()).get_round(), 544 Err(InvalidRound(7)) 545 ); 546 assert_eq!( 547 Evaluator::new(b"round(1,", &mut Cache::new(), &mut None, &mut Vec::new()).get_round(), 548 Err(InvalidRound(8)) 549 ); 550 assert_eq!( 551 Evaluator::new(b"round(1,2", &mut Cache::new(), &mut None, &mut Vec::new()).get_round(), 552 Err(InvalidRound(9)) 553 ); 554 assert_eq!( 555 Evaluator::new( 556 b"round(1,10)", 557 &mut Cache::new(), 558 &mut None, 559 &mut Vec::new() 560 ) 561 .get_round(), 562 Err(InvalidRound(9)) 563 ); 564 assert_eq!( 565 Evaluator::new(b"round(1,a)", &mut Cache::new(), &mut None, &mut Vec::new()).get_round(), 566 Err(InvalidRound(8)) 567 ); 568 assert_eq!( 569 Evaluator::new( 570 b"round(2, 7)", 571 &mut Cache::new(), 572 &mut None, 573 &mut Vec::new() 574 ) 575 .get_round(), 576 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 577 Sign::Plus, 578 BigUint::new(vec![2]) 579 )))) 580 ); 581 assert_eq!( 582 Evaluator::new( 583 b"round(2.677, 1)", 584 &mut Cache::new(), 585 &mut None, 586 &mut Vec::new() 587 ) 588 .get_round(), 589 Ok(Some(Ratio::new( 590 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![27])), 591 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![10])) 592 ))) 593 ); 594 } 595 #[expect(clippy::too_many_lines, reason = "a lot to test")] 596 #[cfg(feature = "rand")] 597 #[test] 598 fn rand() { 599 assert_eq!( 600 Evaluator::new( 601 b"rand(1", 602 &mut Cache::new(), 603 &mut None, 604 &mut Vec::new(), 605 &mut rand::rng() 606 ) 607 .get_rand(), 608 Err(LangErr::InvalidRand(6)) 609 ); 610 assert_eq!( 611 Evaluator::new( 612 b"rand(1,2", 613 &mut Cache::new(), 614 &mut None, 615 &mut Vec::new(), 616 &mut rand::rng() 617 ) 618 .get_rand(), 619 Err(LangErr::InvalidRand(8)) 620 ); 621 assert_eq!( 622 Evaluator::new( 623 b"rand(1/2,3/4)", 624 &mut Cache::new(), 625 &mut None, 626 &mut Vec::new(), 627 &mut rand::rng(), 628 ) 629 .get_rand(), 630 Err(LangErr::RandNoInts(13)) 631 ); 632 assert_eq!( 633 Evaluator::new( 634 b"rand(-100000000000000000000000,-1000000000000000000000)", 635 &mut Cache::new(), 636 &mut None, 637 &mut Vec::new(), 638 &mut rand::rng(), 639 ) 640 .get_rand(), 641 Err(LangErr::RandNoInts(55)) 642 ); 643 assert_eq!( 644 Evaluator::new( 645 b"rand(2/3,1/3)", 646 &mut Cache::new(), 647 &mut None, 648 &mut Vec::new(), 649 &mut rand::rng(), 650 ) 651 .get_rand(), 652 Err(LangErr::RandInvalidArgs(13)) 653 ); 654 // If the input does not start with 'rand(', then it's invalid since get_rand must only be called as the last terminal expression which means whitespace must be consumed already. 655 assert_eq!( 656 Evaluator::new( 657 b" rand(2/3,2)", 658 &mut Cache::new(), 659 &mut None, 660 &mut Vec::new(), 661 &mut rand::rng(), 662 ) 663 .get_rand(), 664 Err(MissingTerm(0)) 665 ); 666 assert!( 667 Evaluator::new( 668 b"rand(2, 7)", 669 &mut Cache::new(), 670 &mut None, 671 &mut Vec::new(), 672 &mut rand::rng() 673 ) 674 .get_rand() 675 .is_ok_and(|r| { 676 let int = r.numer(); 677 int >= &BigInt::from_biguint(Sign::Plus, BigUint::new(vec![2])) 678 && *int <= BigInt::from_biguint(Sign::Plus, BigUint::new(vec![7])) 679 }) 680 ); 681 assert!( 682 Evaluator::new( 683 b"rand()", 684 &mut Cache::new(), 685 &mut None, 686 &mut Vec::new(), 687 &mut rand::rng() 688 ) 689 .get_rand() 690 .is_ok_and(|r| { 691 let int = r.numer(); 692 int >= &BigInt::from(i64::MIN) && *int <= BigInt::from(i64::MAX) 693 }) 694 ); 695 for _ in 0..100u8 { 696 assert!( 697 Evaluator::new( 698 b"rand(2, 2)", 699 &mut Cache::new(), 700 &mut None, 701 &mut Vec::new(), 702 &mut rand::rng() 703 ) 704 .get_rand() 705 .is_ok_and(|r| *r.numer() == BigInt::from_biguint(Sign::Plus, BigUint::new(vec![2]))) 706 ); 707 } 708 } 709 #[expect( 710 clippy::indexing_slicing, 711 clippy::unwrap_used, 712 reason = "comment justifies correctness" 713 )] 714 #[cfg(feature = "rand")] 715 #[test] 716 #[ignore = "slow"] 717 fn rand_uni() { 718 const COUNT: u32 = 999_999; 719 #[expect( 720 clippy::integer_division, 721 clippy::integer_division_remainder_used, 722 reason = "correct" 723 )] 724 const LOWER: u32 = COUNT * 33 / 100; 725 #[expect( 726 clippy::integer_division, 727 clippy::integer_division_remainder_used, 728 reason = "correct" 729 )] 730 const UPPER: u32 = COUNT * 101 / 300; 731 // Test rand on an interval that is not a power of 2 in size. 732 // This causes rand to adjust the interval to enforce uniformity. 733 let mut vals = [0u32; 3]; 734 let mut vec = Vec::new(); 735 let mut cache = Cache::new(); 736 let mut none = None; 737 for _ in 1..COUNT { 738 // We want to `panic` if `rand` does not work correctly. 739 vals[usize::try_from( 740 Evaluator::new( 741 b"rand(-1, 1)", 742 &mut cache, 743 &mut none, 744 &mut vec, 745 &mut rand::rng(), 746 ) 747 .get_rand() 748 .unwrap() 749 .numer() 750 .to_i32() 751 .unwrap() 752 + 1i32, 753 ) 754 .unwrap()] += 1; 755 } 756 // Test that the distribution is within 1% of what is expected. 757 assert_eq!( 758 vals.into_iter().try_fold(false, |_, r| { 759 if (LOWER..=UPPER).contains(&r) { 760 Ok(true) 761 } else { 762 Err(false) 763 } 764 }), 765 Ok(true) 766 ); 767 } 768 #[allow( 769 clippy::allow_attributes, 770 reason = "unwrap_used only fires when rand is not enabled" 771 )] 772 #[allow(clippy::unwrap_used, reason = "comments justify correctness")] 773 #[test] 774 fn term() { 775 #[cfg(not(feature = "rand"))] 776 assert_eq!( 777 Evaluator::new(b"0000.00000", &mut Cache::new(), &mut None, &mut Vec::new()).get_term(), 778 Ok(Ratio::from_integer(BigInt::from_biguint( 779 Sign::NoSign, 780 BigUint::new(Vec::new()) 781 ))) 782 ); 783 #[cfg(not(feature = "rand"))] 784 assert_eq!( 785 Evaluator::new(b"(4)", &mut Cache::new(), &mut None, &mut Vec::new()).get_term(), 786 Ok(Ratio::from_integer(BigInt::from_biguint( 787 Sign::Plus, 788 BigUint::new(vec![4]) 789 ))) 790 ); 791 #[cfg(not(feature = "rand"))] 792 assert_eq!( 793 Evaluator::new( 794 b"round(-2/3,2)", 795 &mut Cache::new(), 796 &mut None, 797 &mut Vec::new() 798 ) 799 .get_term(), 800 Ok(Ratio::new( 801 BigInt::from_biguint(Sign::Minus, BigUint::new(vec![67])), 802 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![100])) 803 )) 804 ); 805 #[cfg(feature = "rand")] 806 drop( 807 Evaluator::new( 808 b"rand()", 809 &mut Cache::new(), 810 &mut None, 811 &mut Vec::new(), 812 &mut rand::rng(), 813 ) 814 .get_term() 815 .unwrap(), 816 ); 817 #[cfg(feature = "rand")] 818 drop( 819 Evaluator::new( 820 b"rand(-13/93, 833)", 821 &mut Cache::new(), 822 &mut None, 823 &mut Vec::new(), 824 &mut rand::rng(), 825 ) 826 .get_term() 827 .unwrap(), 828 ); 829 #[cfg(not(feature = "rand"))] 830 assert_eq!( 831 Evaluator::new(b"rand()", &mut Cache::new(), &mut None, &mut Vec::new()).get_term(), 832 Err(MissingTerm(0)) 833 ); 834 #[cfg(not(feature = "rand"))] 835 assert_eq!( 836 Evaluator::new(b"|4|", &mut Cache::new(), &mut None, &mut Vec::new()).get_term(), 837 Ok(Ratio::from_integer(BigInt::from_biguint( 838 Sign::Plus, 839 BigUint::new(vec![4]) 840 ))) 841 ); 842 // Terminal expressions do no clean up before or after. 843 #[cfg(not(feature = "rand"))] 844 assert_eq!( 845 Evaluator::new(b" 2", &mut Cache::new(), &mut None, &mut Vec::new()).get_term(), 846 Err(MissingTerm(0)) 847 ); 848 #[cfg(not(feature = "rand"))] 849 let mut prev = None; 850 #[cfg(not(feature = "rand"))] 851 let mut cache = Cache::new(); 852 #[cfg(not(feature = "rand"))] 853 { 854 // Quick check that `Ok` is returned. 855 _ = Evaluator::new(b"1\n", &mut cache, &mut prev, &mut Vec::new()) 856 .evaluate() 857 .unwrap(); 858 // Quick check that `Ok` is returned. 859 _ = Evaluator::new(b"s\n", &mut cache, &mut prev, &mut Vec::new()) 860 .evaluate() 861 .unwrap(); 862 } 863 #[cfg(not(feature = "rand"))] 864 assert_eq!( 865 Evaluator::new(b"@", &mut cache, &mut prev, &mut Vec::new()).get_term(), 866 Ok(Ratio::from_integer(BigInt::from_biguint( 867 Sign::Plus, 868 BigUint::new(vec![1]) 869 ))) 870 ); 871 } 872 #[expect(clippy::unwrap_used, reason = "comments justify correctness")] 873 #[cfg(not(feature = "rand"))] 874 #[test] 875 fn factorial() { 876 // Negative integer is not allowed. 877 assert_eq!( 878 Evaluator::new(b"(-1)!", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(), 879 Err(NotNonNegIntFact(5)) 880 ); 881 // Non-integer is not allowed. 882 assert_eq!( 883 Evaluator::new(b"2.5!", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(), 884 Err(NotNonNegIntFact(4)) 885 ); 886 // factorials always become terminal expressions eventually. 887 assert_eq!( 888 Evaluator::new(b"7", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(), 889 Ok(Ratio::from_integer(BigInt::from_biguint( 890 Sign::Plus, 891 BigUint::new(vec![7]) 892 ))) 893 ); 894 assert_eq!( 895 Evaluator::new(b"(7)", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(), 896 Ok(Ratio::from_integer(BigInt::from_biguint( 897 Sign::Plus, 898 BigUint::new(vec![7]) 899 ))) 900 ); 901 assert_eq!( 902 Evaluator::new(b"|7|", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(), 903 Ok(Ratio::from_integer(BigInt::from_biguint( 904 Sign::Plus, 905 BigUint::new(vec![7]) 906 ))) 907 ); 908 let mut prev = None; 909 let mut cache = Cache::new(); 910 // Quick check that `Ok` is returned. 911 _ = Evaluator::new(b"3\n", &mut cache, &mut prev, &mut Vec::new()) 912 .evaluate() 913 .unwrap(); 914 // Quick check that `Ok` is returned. 915 _ = Evaluator::new(b"s\n", &mut cache, &mut prev, &mut Vec::new()) 916 .evaluate() 917 .unwrap(); 918 assert_eq!( 919 Evaluator::new(b"@!", &mut cache, &mut prev, &mut Vec::new()).get_fact(), 920 Ok(Ratio::from_integer(BigInt::from_biguint( 921 Sign::Plus, 922 BigUint::new(vec![6]) 923 ))) 924 ); 925 assert_eq!( 926 Evaluator::new(b"@", &mut cache, &mut prev, &mut Vec::new()).get_fact(), 927 Ok(Ratio::from_integer(BigInt::from_biguint( 928 Sign::Plus, 929 BigUint::new(vec![3]) 930 ))) 931 ); 932 // 0! = 1. 933 assert_eq!( 934 Evaluator::new(b"0.0!", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(), 935 Ok(Ratio::from_integer(BigInt::from_biguint( 936 Sign::Plus, 937 BigUint::new(vec![1]) 938 ))) 939 ); 940 // 1! = 1. 941 assert_eq!( 942 Evaluator::new(b"1!", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(), 943 Ok(Ratio::from_integer(BigInt::from_biguint( 944 Sign::Plus, 945 BigUint::new(vec![1]) 946 ))) 947 ); 948 // 4! = 24, and whitespace is not consumed. 949 assert_eq!( 950 Evaluator::new(b"4! \t", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(), 951 Ok(Ratio::from_integer(BigInt::from_biguint( 952 Sign::Plus, 953 BigUint::new(vec![24]) 954 ))) 955 ); 956 // Factorials can be chained. 957 assert_eq!( 958 Evaluator::new(b"3!! ", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(), 959 Ok(Ratio::from_integer(BigInt::from_biguint( 960 Sign::Plus, 961 BigUint::new(vec![720]) 962 ))) 963 ); 964 // only factorial is consumed. 965 assert_eq!( 966 Evaluator::new(b"2!+3", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(), 967 Ok(Ratio::from_integer(BigInt::from_biguint( 968 Sign::Plus, 969 BigUint::new(vec![2]) 970 ))) 971 ); 972 // Error since leading/trailing whitespace is not consumed by factorial or higher precedence expressions. 973 assert_eq!( 974 Evaluator::new(b" 2!", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(), 975 Err(MissingTerm(0)) 976 ); 977 assert_eq!( 978 Evaluator::new(b"\t2!", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(), 979 Err(MissingTerm(0)) 980 ); 981 // Error since negation is not consumed by factorial or higher precedence expressions. 982 assert_eq!( 983 Evaluator::new(b"-2!", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(), 984 Err(MissingTerm(0)) 985 ); 986 } 987 #[expect( 988 clippy::cognitive_complexity, 989 clippy::too_many_lines, 990 reason = "a lot to test" 991 )] 992 #[cfg(not(feature = "rand"))] 993 #[test] 994 fn exp() { 995 // 1 can be raised to anything and return 1. 996 // Also white space is ignored between operator. 997 assert_eq!( 998 Evaluator::new(b"1 ^\t 0", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 999 Ok(Ratio::from_integer(BigInt::from_biguint( 1000 Sign::Plus, 1001 BigUint::new(vec![1]) 1002 ))) 1003 ); 1004 assert_eq!( 1005 Evaluator::new(b"1^0.5", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 1006 Ok(Ratio::from_integer(BigInt::from_biguint( 1007 Sign::Plus, 1008 BigUint::new(vec![1]) 1009 ))) 1010 ); 1011 assert_eq!( 1012 Evaluator::new(b"1^(-1/2)", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 1013 Ok(Ratio::from_integer(BigInt::from_biguint( 1014 Sign::Plus, 1015 BigUint::new(vec![1]) 1016 ))) 1017 ); 1018 assert_eq!( 1019 Evaluator::new(b"1.0^(-2)", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 1020 Ok(Ratio::from_integer(BigInt::from_biguint( 1021 Sign::Plus, 1022 BigUint::new(vec![1]) 1023 ))) 1024 ); 1025 // 0 can be raised to any non-negative value and will always return 0 unless raised to 0 which will return 1. 1026 assert_eq!( 1027 Evaluator::new(b"0^0", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 1028 Ok(Ratio::from_integer(BigInt::from_biguint( 1029 Sign::Plus, 1030 BigUint::new(vec![1]) 1031 ))) 1032 ); 1033 assert_eq!( 1034 Evaluator::new(b"0^1", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 1035 Ok(Ratio::from_integer(BigInt::from_biguint( 1036 Sign::NoSign, 1037 BigUint::new(vec![0]) 1038 ))) 1039 ); 1040 assert_eq!( 1041 Evaluator::new(b"0^0.5", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 1042 Ok(Ratio::from_integer(BigInt::from_biguint( 1043 Sign::NoSign, 1044 BigUint::new(vec![0]) 1045 ))) 1046 ); 1047 // Anything else can only be raised to integers. 1048 assert_eq!( 1049 Evaluator::new(b"4^0", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 1050 Ok(Ratio::from_integer(BigInt::from_biguint( 1051 Sign::Plus, 1052 BigUint::new(vec![1]) 1053 ))) 1054 ); 1055 assert_eq!( 1056 Evaluator::new(b"4^1", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 1057 Ok(Ratio::from_integer(BigInt::from_biguint( 1058 Sign::Plus, 1059 BigUint::new(vec![4]) 1060 ))) 1061 ); 1062 assert_eq!( 1063 Evaluator::new(b"4^(-2)", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 1064 Ok(Ratio::new( 1065 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1])), 1066 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![16])) 1067 )) 1068 ); 1069 assert_eq!( 1070 Evaluator::new(b"(-4)^0", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 1071 Ok(Ratio::from_integer(BigInt::from_biguint( 1072 Sign::Plus, 1073 BigUint::new(vec![1]) 1074 ))) 1075 ); 1076 assert_eq!( 1077 Evaluator::new(b"(-4)^1", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 1078 Ok(Ratio::from_integer(BigInt::from_biguint( 1079 Sign::Minus, 1080 BigUint::new(vec![4]) 1081 ))) 1082 ); 1083 assert_eq!( 1084 Evaluator::new(b"(-4)^2", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 1085 Ok(Ratio::from_integer(BigInt::from_biguint( 1086 Sign::Plus, 1087 BigUint::new(vec![16]) 1088 ))) 1089 ); 1090 assert_eq!( 1091 Evaluator::new(b"(-4)^(-2)", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 1092 Ok(Ratio::new( 1093 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1])), 1094 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![16])) 1095 )) 1096 ); 1097 assert_eq!( 1098 Evaluator::new(b"(-4)^(-3)", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 1099 Ok(Ratio::new( 1100 BigInt::from_biguint(Sign::Minus, BigUint::new(vec![1])), 1101 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![64])) 1102 )) 1103 ); 1104 assert_eq!( 1105 Evaluator::new(b"(2/3)^0", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 1106 Ok(Ratio::from_integer(BigInt::from_biguint( 1107 Sign::Plus, 1108 BigUint::new(vec![1]) 1109 ))) 1110 ); 1111 assert_eq!( 1112 Evaluator::new(b"(2/3)^(2)", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 1113 Ok(Ratio::new( 1114 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![4])), 1115 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![9])) 1116 )) 1117 ); 1118 assert_eq!( 1119 Evaluator::new(b"(2/3)^(-3)", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 1120 Ok(Ratio::new( 1121 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![27])), 1122 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![8])) 1123 )) 1124 ); 1125 assert_eq!( 1126 Evaluator::new(b"(-2/3)^0", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 1127 Ok(Ratio::from_integer(BigInt::from_biguint( 1128 Sign::Plus, 1129 BigUint::new(vec![1]) 1130 ))) 1131 ); 1132 assert_eq!( 1133 Evaluator::new(b"(-2/3)^(2)", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 1134 Ok(Ratio::new( 1135 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![4])), 1136 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![9])) 1137 )) 1138 ); 1139 assert_eq!( 1140 Evaluator::new(b"(-2/3)^(3)", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 1141 Ok(Ratio::new( 1142 BigInt::from_biguint(Sign::Minus, BigUint::new(vec![8])), 1143 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![27])) 1144 )) 1145 ); 1146 assert_eq!( 1147 Evaluator::new( 1148 b"(-2/3)^(-2)", 1149 &mut Cache::new(), 1150 &mut None, 1151 &mut Vec::new() 1152 ) 1153 .get_exps(), 1154 Ok(Ratio::new( 1155 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![9])), 1156 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![4])) 1157 )) 1158 ); 1159 assert_eq!( 1160 Evaluator::new( 1161 b"(-2/3)^(-3)", 1162 &mut Cache::new(), 1163 &mut None, 1164 &mut Vec::new() 1165 ) 1166 .get_exps(), 1167 Ok(Ratio::new( 1168 BigInt::from_biguint(Sign::Minus, BigUint::new(vec![27])), 1169 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![8])) 1170 )) 1171 ); 1172 assert_eq!( 1173 Evaluator::new( 1174 b"(4/9)^(-1/2)", 1175 &mut Cache::new(), 1176 &mut None, 1177 &mut Vec::new() 1178 ) 1179 .get_exps(), 1180 Ok(Ratio::new( 1181 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![3])), 1182 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![2])) 1183 )) 1184 ); 1185 // Error since 0 cannot be raised to a negative power. 1186 assert_eq!( 1187 Evaluator::new(b"0^(-1)", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 1188 Err(ExpDivByZero(6)) 1189 ); 1190 // Error since anything other than 0 or 1 cannot be raised to a non-integer power or (+/-) 1/2. 1191 assert_eq!( 1192 Evaluator::new(b"2^(1/3)", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 1193 Err(ExpIsNotIntOrOneHalf(7)) 1194 ); 1195 // When exponent is (+/-) 1/2, base has to be the square of a rational number. 1196 assert_eq!( 1197 Evaluator::new(b"2^(1/2)", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 1198 Err(SqrtDoesNotExist(7)) 1199 ); 1200 // exps always become factorials eventually. 1201 assert_eq!( 1202 Evaluator::new(b"3!", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 1203 Ok(Ratio::from_integer(BigInt::from_biguint( 1204 Sign::Plus, 1205 BigUint::new(vec![6]) 1206 ))) 1207 ); 1208 // exponentiation has lower precedence than factorials. 1209 assert_eq!( 1210 Evaluator::new(b"2^3!", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 1211 Ok(Ratio::from_integer(BigInt::from_biguint( 1212 Sign::Plus, 1213 BigUint::new(vec![64]) 1214 ))) 1215 ); 1216 assert_eq!( 1217 Evaluator::new(b"3!^2", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 1218 Ok(Ratio::from_integer(BigInt::from_biguint( 1219 Sign::Plus, 1220 BigUint::new(vec![36]) 1221 ))) 1222 ); 1223 // Error since leading/trailing whitespace is not consumed by exponentiation or higher precedence expressions. 1224 assert_eq!( 1225 Evaluator::new(b" 2^2", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 1226 Err(MissingTerm(0)) 1227 ); 1228 assert_eq!( 1229 Evaluator::new(b"\t2^2", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 1230 Err(MissingTerm(0)) 1231 ); 1232 } 1233 #[cfg(not(feature = "rand"))] 1234 #[test] 1235 fn neg() { 1236 assert_eq!( 1237 Evaluator::new(b"-1", &mut Cache::new(), &mut None, &mut Vec::new()).get_neg(), 1238 Ok(Ratio::from_integer(BigInt::from_biguint( 1239 Sign::Minus, 1240 BigUint::new(vec![1]) 1241 ))) 1242 ); 1243 assert_eq!( 1244 Evaluator::new(b"- \t - 1", &mut Cache::new(), &mut None, &mut Vec::new()).get_neg(), 1245 Ok(Ratio::from_integer(BigInt::from_biguint( 1246 Sign::Plus, 1247 BigUint::new(vec![1]) 1248 ))) 1249 ); 1250 assert_eq!( 1251 Evaluator::new(b"-0", &mut Cache::new(), &mut None, &mut Vec::new()).get_neg(), 1252 Ok(Ratio::from_integer(BigInt::from_biguint( 1253 Sign::NoSign, 1254 BigUint::new(Vec::new()) 1255 ))) 1256 ); 1257 // negation has lower precedence than exponentiation. 1258 assert_eq!( 1259 Evaluator::new(b"-2^2", &mut Cache::new(), &mut None, &mut Vec::new()).get_neg(), 1260 Ok(Ratio::from_integer(BigInt::from_biguint( 1261 Sign::Minus, 1262 BigUint::new(vec![4]) 1263 ))) 1264 ); 1265 // negation always becomes exponentiation eventually. 1266 assert_eq!( 1267 Evaluator::new(b"2.0^2.0", &mut Cache::new(), &mut None, &mut Vec::new()).get_neg(), 1268 Ok(Ratio::from_integer(BigInt::from_biguint( 1269 Sign::Plus, 1270 BigUint::new(vec![4]) 1271 ))) 1272 ); 1273 // Error since leading/trailing whitespace is not consumed by exponentiation or higher precedence expressions. 1274 assert_eq!( 1275 Evaluator::new(b" -2", &mut Cache::new(), &mut None, &mut Vec::new()).get_neg(), 1276 Err(MissingTerm(0)) 1277 ); 1278 assert_eq!( 1279 Evaluator::new(b"\t-2", &mut Cache::new(), &mut None, &mut Vec::new()).get_neg(), 1280 Err(MissingTerm(0)) 1281 ); 1282 } 1283 #[expect( 1284 clippy::cognitive_complexity, 1285 clippy::too_many_lines, 1286 reason = "a lot to test" 1287 )] 1288 #[cfg(not(feature = "rand"))] 1289 #[test] 1290 fn mult() { 1291 assert_eq!( 1292 Evaluator::new(b"2 * 3", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), 1293 Ok(Ratio::from_integer(BigInt::from_biguint( 1294 Sign::Plus, 1295 BigUint::new(vec![6]) 1296 ))) 1297 ); 1298 assert_eq!( 1299 Evaluator::new( 1300 b"-2 * \t 3", 1301 &mut Cache::new(), 1302 &mut None, 1303 &mut Vec::new() 1304 ) 1305 .get_mults(), 1306 Ok(Ratio::from_integer(BigInt::from_biguint( 1307 Sign::Minus, 1308 BigUint::new(vec![6]) 1309 ))) 1310 ); 1311 assert_eq!( 1312 Evaluator::new( 1313 b"2\t * -3.0", 1314 &mut Cache::new(), 1315 &mut None, 1316 &mut Vec::new() 1317 ) 1318 .get_mults(), 1319 Ok(Ratio::from_integer(BigInt::from_biguint( 1320 Sign::Minus, 1321 BigUint::new(vec![6]) 1322 ))) 1323 ); 1324 assert_eq!( 1325 Evaluator::new(b"-2.5*-3.5", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), 1326 Ok(Ratio::new( 1327 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![35])), 1328 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![4])) 1329 )) 1330 ); 1331 assert_eq!( 1332 Evaluator::new(b"4.0\t / 6", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), 1333 Ok(Ratio::new( 1334 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![2])), 1335 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![3])) 1336 )) 1337 ); 1338 assert_eq!( 1339 Evaluator::new(b"6/3", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), 1340 Ok(Ratio::from_integer(BigInt::from_biguint( 1341 Sign::Plus, 1342 BigUint::new(vec![2]) 1343 ))) 1344 ); 1345 assert_eq!( 1346 Evaluator::new(b"-6/3", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), 1347 Ok(Ratio::from_integer(BigInt::from_biguint( 1348 Sign::Minus, 1349 BigUint::new(vec![2]) 1350 ))) 1351 ); 1352 assert_eq!( 1353 Evaluator::new(b"6/-3", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), 1354 Ok(Ratio::from_integer(BigInt::from_biguint( 1355 Sign::Minus, 1356 BigUint::new(vec![2]) 1357 ))) 1358 ); 1359 assert_eq!( 1360 Evaluator::new( 1361 b"- 6 /\t - 3", 1362 &mut Cache::new(), 1363 &mut None, 1364 &mut Vec::new() 1365 ) 1366 .get_mults(), 1367 Ok(Ratio::from_integer(BigInt::from_biguint( 1368 Sign::Plus, 1369 BigUint::new(vec![2]) 1370 ))) 1371 ); 1372 // Number literals are not strictly equivalent to "ratios" as "ratios" don't exist (i.e., 2/3 is not the ratio of 2 to 3 but is the rational number two divided by the rational number 3). 1373 assert!( 1374 Evaluator::new(b"1/1.5", &mut Cache::new(), &mut None, &mut Vec::new()) 1375 .get_mults() 1376 .is_ok_and(|r| { 1377 Evaluator::new(b"1/3/2", &mut Cache::new(), &mut None, &mut Vec::new()) 1378 .get_mults() 1379 .is_ok_and(|r2| r != r2) 1380 }) 1381 ); 1382 assert!( 1383 Evaluator::new(b"1/1.5", &mut Cache::new(), &mut None, &mut Vec::new()) 1384 .get_mults() 1385 .is_ok_and(|r| { 1386 Evaluator::new(b"1/(3/2)", &mut Cache::new(), &mut None, &mut Vec::new()) 1387 .get_mults() 1388 .is_ok_and(|r2| r == r2) 1389 }) 1390 ); 1391 // multiplication always becomes negation eventually. 1392 assert_eq!( 1393 Evaluator::new(b"-2.0", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), 1394 Ok(Ratio::from_integer(BigInt::from_biguint( 1395 Sign::Minus, 1396 BigUint::new(vec![2]) 1397 ))) 1398 ); 1399 // Error since leading/trailing whitespace is not consumed by multiplication or higher precedence expressions. 1400 assert_eq!( 1401 Evaluator::new(b" 2*2", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), 1402 Err(MissingTerm(0)) 1403 ); 1404 assert_eq!( 1405 Evaluator::new(b"\t2/2", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), 1406 Err(MissingTerm(0)) 1407 ); 1408 assert_eq!( 1409 Evaluator::new( 1410 b"4.0\t mod 6", 1411 &mut Cache::new(), 1412 &mut None, 1413 &mut Vec::new() 1414 ) 1415 .get_mults(), 1416 Ok(Ratio::from_integer(BigInt::from_biguint( 1417 Sign::Plus, 1418 BigUint::new(vec![4]) 1419 ),)) 1420 ); 1421 assert_eq!( 1422 Evaluator::new(b"5 mod 3", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), 1423 Ok(Ratio::from_integer(BigInt::from_biguint( 1424 Sign::Plus, 1425 BigUint::new(vec![2]) 1426 ))) 1427 ); 1428 assert_eq!( 1429 Evaluator::new(b"-5 mod 3", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), 1430 Ok(Ratio::from_integer(BigInt::from_biguint( 1431 Sign::Plus, 1432 BigUint::new(vec![1]) 1433 ))) 1434 ); 1435 assert_eq!( 1436 Evaluator::new(b"5 mod -3", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), 1437 Ok(Ratio::from_integer(BigInt::from_biguint( 1438 Sign::Plus, 1439 BigUint::new(vec![2]) 1440 ))) 1441 ); 1442 assert_eq!( 1443 Evaluator::new( 1444 b"-5 mod\t -3", 1445 &mut Cache::new(), 1446 &mut None, 1447 &mut Vec::new() 1448 ) 1449 .get_mults(), 1450 Ok(Ratio::from_integer(BigInt::from_biguint( 1451 Sign::Plus, 1452 BigUint::new(vec![1]) 1453 ))) 1454 ); 1455 // Cannot divide by 0. 1456 assert_eq!( 1457 Evaluator::new(b"2/0", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), 1458 Err(DivByZero(3)) 1459 ); 1460 assert_eq!( 1461 Evaluator::new(b"2 mod 0", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), 1462 Err(ModZero(7)) 1463 ); 1464 // Right and left operands of mod must be integers. 1465 assert_eq!( 1466 Evaluator::new(b"3.2 mod 1", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), 1467 Err(ModIsNotInt(4)) 1468 ); 1469 assert_eq!( 1470 Evaluator::new(b"3 mod 3.2", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), 1471 Err(ModIsNotInt(9)) 1472 ); 1473 // multiplication has lower precedence than exponentiation. 1474 assert_eq!( 1475 Evaluator::new(b"2*2^2", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), 1476 Ok(Ratio::from_integer(BigInt::from_biguint( 1477 Sign::Plus, 1478 BigUint::new(vec![8]) 1479 ))) 1480 ); 1481 assert_eq!( 1482 Evaluator::new(b"8/2^2", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), 1483 Ok(Ratio::from_integer(BigInt::from_biguint( 1484 Sign::Plus, 1485 BigUint::new(vec![2]) 1486 ))) 1487 ); 1488 assert_eq!( 1489 Evaluator::new(b"8 mod 3^2", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), 1490 Ok(Ratio::from_integer(BigInt::from_biguint( 1491 Sign::Plus, 1492 BigUint::new(vec![8]) 1493 ))) 1494 ); 1495 } 1496 #[expect(clippy::too_many_lines, reason = "a lot to test")] 1497 #[cfg(not(feature = "rand"))] 1498 #[test] 1499 fn add() { 1500 assert_eq!( 1501 Evaluator::new(b"2 + 3", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(), 1502 Ok(Ratio::from_integer(BigInt::from_biguint( 1503 Sign::Plus, 1504 BigUint::new(vec![5]) 1505 ))) 1506 ); 1507 assert_eq!( 1508 Evaluator::new(b"-2 + 3", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(), 1509 Ok(Ratio::from_integer(BigInt::from_biguint( 1510 Sign::Plus, 1511 BigUint::new(vec![1]) 1512 ))) 1513 ); 1514 assert_eq!( 1515 Evaluator::new( 1516 b"2 + \t -3.0", 1517 &mut Cache::new(), 1518 &mut None, 1519 &mut Vec::new() 1520 ) 1521 .get_adds(), 1522 Ok(Ratio::from_integer(BigInt::from_biguint( 1523 Sign::Minus, 1524 BigUint::new(vec![1]) 1525 ))) 1526 ); 1527 assert_eq!( 1528 Evaluator::new(b"-2.5+-3.5", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(), 1529 Ok(Ratio::from_integer(BigInt::from_biguint( 1530 Sign::Minus, 1531 BigUint::new(vec![6]) 1532 ))) 1533 ); 1534 assert_eq!( 1535 Evaluator::new(b"4.0\t - 6", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(), 1536 Ok(Ratio::from_integer(BigInt::from_biguint( 1537 Sign::Minus, 1538 BigUint::new(vec![2]) 1539 ))) 1540 ); 1541 assert_eq!( 1542 Evaluator::new(b"6-3", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(), 1543 Ok(Ratio::from_integer(BigInt::from_biguint( 1544 Sign::Plus, 1545 BigUint::new(vec![3]) 1546 ))) 1547 ); 1548 assert_eq!( 1549 Evaluator::new(b"-6-3", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(), 1550 Ok(Ratio::from_integer(BigInt::from_biguint( 1551 Sign::Minus, 1552 BigUint::new(vec![9]) 1553 ))) 1554 ); 1555 assert_eq!( 1556 Evaluator::new(b"6--3", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(), 1557 Ok(Ratio::from_integer(BigInt::from_biguint( 1558 Sign::Plus, 1559 BigUint::new(vec![9]) 1560 ))) 1561 ); 1562 assert_eq!( 1563 Evaluator::new( 1564 b"- 6 -\t - 3", 1565 &mut Cache::new(), 1566 &mut None, 1567 &mut Vec::new() 1568 ) 1569 .get_adds(), 1570 Ok(Ratio::from_integer(BigInt::from_biguint( 1571 Sign::Minus, 1572 BigUint::new(vec![3]) 1573 ))) 1574 ); 1575 // addition always becomes multiplication eventually. 1576 assert_eq!( 1577 Evaluator::new(b"2 * 8", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(), 1578 Ok(Ratio::from_integer(BigInt::from_biguint( 1579 Sign::Plus, 1580 BigUint::new(vec![16]) 1581 ))) 1582 ); 1583 assert_eq!( 1584 Evaluator::new(b"8 /\t 2", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(), 1585 Ok(Ratio::from_integer(BigInt::from_biguint( 1586 Sign::Plus, 1587 BigUint::new(vec![4]) 1588 ))) 1589 ); 1590 // Error since leading/trailing whitespace is not consumed by addition or higher precedence expressions. 1591 assert_eq!( 1592 Evaluator::new(b" 2+2", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(), 1593 Err(MissingTerm(0)) 1594 ); 1595 assert_eq!( 1596 Evaluator::new(b" 2-2", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(), 1597 Err(MissingTerm(0)) 1598 ); 1599 // addition has lower precedence than multiplication. 1600 assert_eq!( 1601 Evaluator::new(b"2+2*2", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(), 1602 Ok(Ratio::from_integer(BigInt::from_biguint( 1603 Sign::Plus, 1604 BigUint::new(vec![6]) 1605 ))) 1606 ); 1607 assert_eq!( 1608 Evaluator::new(b"2+2/2", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(), 1609 Ok(Ratio::from_integer(BigInt::from_biguint( 1610 Sign::Plus, 1611 BigUint::new(vec![3]) 1612 ))) 1613 ); 1614 assert_eq!( 1615 Evaluator::new(b"2-2*2", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(), 1616 Ok(Ratio::from_integer(BigInt::from_biguint( 1617 Sign::Minus, 1618 BigUint::new(vec![2]) 1619 ))) 1620 ); 1621 assert_eq!( 1622 Evaluator::new(b"2-2/2", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(), 1623 Ok(Ratio::from_integer(BigInt::from_biguint( 1624 Sign::Plus, 1625 BigUint::new(vec![1]) 1626 ))) 1627 ); 1628 } 1629 #[cfg(not(feature = "rand"))] 1630 #[test] 1631 fn exit() { 1632 assert_eq!( 1633 Evaluator::new(b" q \n", &mut Cache::new(), &mut None, &mut Vec::new()).evaluate(), 1634 Ok(Exit) 1635 ); 1636 assert_eq!( 1637 Evaluator::new( 1638 b" q \r\n", 1639 &mut Cache::new(), 1640 &mut None, 1641 &mut Vec::new() 1642 ) 1643 .evaluate(), 1644 Ok(Exit) 1645 ); 1646 assert_eq!( 1647 Evaluator::new(b"q\n", &mut Cache::new(), &mut None, &mut Vec::new()).evaluate(), 1648 Ok(Exit) 1649 ); 1650 assert_eq!( 1651 Evaluator::new(b"q\r\n", &mut Cache::new(), &mut None, &mut Vec::new()).evaluate(), 1652 Ok(Exit) 1653 ); 1654 assert_eq!( 1655 Evaluator::new(b"\rq\n", &mut Cache::new(), &mut None, &mut Vec::new()).evaluate(), 1656 Err(MissingTerm(0)) 1657 ); 1658 assert_eq!( 1659 Evaluator::new(b"\tq\n", &mut Cache::new(), &mut None, &mut Vec::new()).evaluate(), 1660 Ok(Exit) 1661 ); 1662 assert_eq!( 1663 Evaluator::new(b"q\n\n", &mut Cache::new(), &mut None, &mut Vec::new()).evaluate(), 1664 Err(InvalidQuit) 1665 ); 1666 assert_eq!( 1667 Evaluator::new(b"\nq\n", &mut Cache::new(), &mut None, &mut Vec::new()).evaluate(), 1668 Err(MissingTerm(0)) 1669 ); 1670 assert_eq!( 1671 Evaluator::new(b"q", &mut Cache::new(), &mut None, &mut Vec::new()).evaluate(), 1672 Ok(Exit) 1673 ); 1674 } 1675 #[expect(clippy::unwrap_used, reason = "comment justifies correctness")] 1676 #[cfg(not(feature = "rand"))] 1677 #[test] 1678 fn store() { 1679 let mut prev = None; 1680 let mut cache = Cache::new(); 1681 // Quick check that `Ok` is returned. 1682 _ = Evaluator::new(b"1\n", &mut cache, &mut prev, &mut Vec::new()) 1683 .evaluate() 1684 .unwrap(); 1685 assert!(cache.is_empty()); 1686 assert_eq!( 1687 Evaluator::new(b"s\n", &mut cache, &mut prev, &mut Vec::new()).evaluate(), 1688 Ok(Store(&Some(Ratio::from_integer(BigInt::from_biguint( 1689 Sign::Plus, 1690 BigUint::new(vec![1]) 1691 ))))) 1692 ); 1693 assert_eq!(cache.len(), 1); 1694 assert_eq!( 1695 Evaluator::new(b"s2\n", &mut Cache::new(), &mut None, &mut Vec::new()).evaluate(), 1696 Err(InvalidStore) 1697 ); 1698 } 1699 #[expect(clippy::too_many_lines, reason = "a lot to test")] 1700 #[cfg(not(feature = "rand"))] 1701 #[test] 1702 fn eval() { 1703 use core::str::FromStr as _; 1704 let mut prev = None; 1705 let mut cache = Cache::new(); 1706 let mut exp = Vec::new(); 1707 assert_eq!( 1708 Evaluator::new(b"1+2\n", &mut cache, &mut prev, &mut exp).evaluate(), 1709 Ok(Eval(&Ratio::from_integer(BigInt::from_biguint( 1710 Sign::Plus, 1711 BigUint::new(vec![3]) 1712 )))) 1713 ); 1714 assert_eq!( 1715 Evaluator::new(b"\t s \n", &mut cache, &mut prev, &mut exp).evaluate(), 1716 Ok(Store(&Some(Ratio::from_integer(BigInt::from_biguint( 1717 Sign::Plus, 1718 BigUint::new(vec![3]) 1719 ))))) 1720 ); 1721 assert_eq!( 1722 Evaluator::new(b"-1/2+2*@\n", &mut cache, &mut prev, &mut exp).evaluate(), 1723 Ok(Eval(&Ratio::new( 1724 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![11])), 1725 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![2])) 1726 ))) 1727 ); 1728 assert_eq!( 1729 Evaluator::new(b"s\n", &mut cache, &mut prev, &mut exp).evaluate(), 1730 Ok(Store(&Some(Ratio::new( 1731 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![11])), 1732 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![2])) 1733 )))) 1734 ); 1735 assert_eq!( 1736 Evaluator::new(b"@^@2!\r\n", &mut cache, &mut prev, &mut exp).evaluate(), 1737 Ok(Eval(&Ratio::new( 1738 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1_771_561])), 1739 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![64])) 1740 ))) 1741 ); 1742 assert_eq!( 1743 Evaluator::new(b"s\n", &mut cache, &mut prev, &mut exp).evaluate(), 1744 Ok(Store(&Some(Ratio::new( 1745 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1_771_561])), 1746 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![64])) 1747 )))) 1748 ); 1749 // Verified with Wolfram Alpha. 1750 assert!( 1751 Evaluator::new( 1752 b" \t 1 + (2 * |(7.98\t - 12/7)|) / 4!^@3!^|1-3|\t \n", 1753 &mut cache, 1754 &mut prev, 1755 &mut exp 1756 ) 1757 .evaluate().is_ok_and(|r| { 1758 Ratio::from_str("2841328814244153299237884950647090899374680152474331/2841328814244153299237884950647090899374680152473600").is_ok_and(|r2| { 1759 r == Eval(&r2) 1760 }) 1761 }) 1762 ); 1763 assert_eq!( 1764 Evaluator::new( 1765 b" \t round(19/6,0)!\t \r\n", 1766 &mut cache, 1767 &mut prev, 1768 &mut exp 1769 ) 1770 .evaluate(), 1771 Ok(Eval(&Ratio::from_integer(BigInt::from_biguint( 1772 Sign::Plus, 1773 BigUint::new(vec![6]) 1774 )))) 1775 ); 1776 assert_eq!( 1777 Evaluator::new( 1778 b" \t 2^round(19/6,0)!\t \r\n", 1779 &mut cache, 1780 &mut prev, 1781 &mut exp 1782 ) 1783 .evaluate(), 1784 Ok(Eval(&Ratio::from_integer(BigInt::from_biguint( 1785 Sign::Plus, 1786 BigUint::new(vec![64]) 1787 )))) 1788 ); 1789 assert_eq!( 1790 Evaluator::new(b"round(19/6,0)^2\t\n", &mut cache, &mut prev, &mut exp).evaluate(), 1791 Ok(Eval(&Ratio::from_integer(BigInt::from_biguint( 1792 Sign::Plus, 1793 BigUint::new(vec![9]) 1794 )))) 1795 ); 1796 // Invalid UTF-8 does not cause a panic!. 1797 assert_eq!( 1798 Evaluator::new(&[255, 255, 255, b'\n'], &mut cache, &mut prev, &mut exp).evaluate(), 1799 Err(MissingTerm(0)) 1800 ); 1801 assert_eq!( 1802 Evaluator::new(&[b'2', 255, b'\n'], &mut cache, &mut prev, &mut exp).evaluate(), 1803 Err(TrailingSyms(1)) 1804 ); 1805 // Exactly one newline is required. 1806 assert_eq!( 1807 Evaluator::new(b"2\n\n", &mut cache, &mut prev, &mut exp).evaluate(), 1808 Err(TrailingSyms(1)) 1809 ); 1810 assert_eq!( 1811 prev, 1812 Some(Ratio::from_integer(BigInt::from_biguint( 1813 Sign::Plus, 1814 BigUint::new(vec![9]) 1815 ))) 1816 ); 1817 assert_eq!( 1818 Evaluator::new(b"\n", &mut cache, &mut prev.clone(), &mut exp).evaluate(), 1819 Ok(Empty(&Some(Ratio::from_integer(BigInt::from_biguint( 1820 Sign::Plus, 1821 BigUint::new(vec![9]) 1822 ))))) 1823 ); 1824 assert_eq!( 1825 prev, 1826 Some(Ratio::from_integer(BigInt::from_biguint( 1827 Sign::Plus, 1828 BigUint::new(vec![9]) 1829 ))) 1830 ); 1831 assert_eq!( 1832 Evaluator::new(b"\r\n", &mut cache, &mut prev.clone(), &mut exp).evaluate(), 1833 Ok(Empty(&prev)) 1834 ); 1835 assert_eq!( 1836 Evaluator::new(&[0u8; 0], &mut cache, &mut prev.clone(), &mut exp).evaluate(), 1837 Ok(Empty(&prev)) 1838 ); 1839 } 1840 #[cfg(feature = "rand")] 1841 #[test] 1842 fn eval_iter() { 1843 struct Reader<'a> { 1844 data: &'a [u8], 1845 err: bool, 1846 } 1847 impl<'a> Reader<'a> { 1848 fn new(data: &'a [u8]) -> Self { 1849 Self { data, err: true } 1850 } 1851 } 1852 impl Read for Reader<'_> { 1853 #[expect(clippy::indexing_slicing, reason = "comment justifies correctness")] 1854 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { 1855 if self.err { 1856 self.err = false; 1857 Err(Error::other("")) 1858 } else { 1859 let len = usize::min(buf.len(), self.data.len()); 1860 // `len <= buf.len()` and `len <= `self.data.len()`. 1861 buf[..len].copy_from_slice(&self.data[..len]); 1862 Ok(len) 1863 } 1864 } 1865 } 1866 impl BufRead for Reader<'_> { 1867 fn fill_buf(&mut self) -> io::Result<&[u8]> { 1868 if self.err { 1869 self.err = false; 1870 Err(Error::other("")) 1871 } else { 1872 Ok(self.data) 1873 } 1874 } 1875 #[expect(clippy::indexing_slicing, reason = "comment justifies correctness")] 1876 fn consume(&mut self, amount: usize) { 1877 // This is just a test, so calling code passing in an invalid `amount` is fine. 1878 self.data = &self.data[amount..]; 1879 } 1880 } 1881 let mut iter = EvalIter::new(Reader::new( 1882 b"1+2\n4\n\nq\n5\ns\nrand() + rand(-139/@, 2984/134)\nab", 1883 )); 1884 assert!( 1885 iter.lend_next() 1886 .is_some_and(|res| res.map_or_else(|e| matches!(e, E::Error(_)), |_| false)) 1887 ); 1888 assert!(iter.lend_next().is_some_and(|res| { 1889 res.is_ok_and(|e| matches!(e, Eval(r) if r.numer().to_i32() == Some(3i32))) 1890 })); 1891 assert!(iter.lend_next().is_some_and(|res| { 1892 res.is_ok_and(|e| matches!(e, Eval(r) if r.numer().to_i32() == Some(4i32))) 1893 })); 1894 assert!(iter.lend_next().is_some_and(|res| { 1895 res.is_ok_and( 1896 |e| matches!(e, Empty(r) if r.as_ref().is_some_and(|val| val.numer().to_i32() == Some(4i32))), 1897 ) 1898 })); 1899 assert!(iter.lend_next().is_none()); 1900 assert!(iter.lend_next().is_some_and(|res| { 1901 res.is_ok_and(|e| matches!(e, Eval(r) if r.numer().to_i32() == Some(5i32))) 1902 })); 1903 assert!(iter.lend_next().is_some_and(|res| { 1904 res.is_ok_and( 1905 |e| matches!(e, Store(r) if r.as_ref().is_some_and(|val| val.numer().to_i32() == Some(5i32))), 1906 ) 1907 })); 1908 assert!( 1909 iter.lend_next() 1910 .is_some_and(|res| res.is_ok_and(|e| matches!(e, Eval(r) if r.is_integer()))) 1911 ); 1912 assert!(iter.lend_next().is_some_and(|res| { 1913 res.is_err_and(|err| matches!(err, E::LangErr(ref e) if matches!(*e, MissingTerm(_)))) 1914 })); 1915 assert!(iter.lend_next().is_none()); 1916 assert!(iter.lend_next().is_none()); 1917 assert!(iter.lend_next().is_none()); 1918 } 1919 #[ignore = "only to silence lint"] 1920 #[cfg(feature = "std")] 1921 #[test] 1922 fn silence_dead_pub_in_binary_when_only_std() { 1923 drop(E::LangErr(MissingTerm(0))); 1924 drop(EvalIter::new(b"")); 1925 }