1use crate::compat::{format, ToString};
4use crate::interpreter::Interpreter;
5use crate::primitives::numeric_promotion::promote_pair;
6use crate::value::{RuntimeError, Value};
7
8pub fn add_builtin(interp: &mut Interpreter) -> Result<(), RuntimeError> {
12 let b =
13 interp.pop_with_context("'+' requires exactly 2 values on the stack (e.g., '5 3 +')")?;
14 let a =
15 interp.pop_with_context("'+' requires exactly 2 values on the stack (e.g., '5 3 +')")?;
16
17 match (&a, &b) {
19 (Value::String(_), _) | (_, Value::String(_)) => {
20 let str_a = match &a {
21 Value::String(s) => s.as_ref(),
22 _ => &a.to_string(),
23 };
24 let str_b = match &b {
25 Value::String(s) => s.as_ref(),
26 _ => &b.to_string(),
27 };
28 let result = format!("{}{}", str_a, str_b);
29 interp.push(Value::String(result.into()));
30 return Ok(());
31 }
32 _ => {}
33 }
34
35 let (pa, pb) = promote_pair(&a, &b);
37
38 let result = match (&pa, &pb) {
39 (Value::Int32(i1), Value::Int32(i2)) => {
41 match i1.checked_add(*i2) {
42 Some(result) => Value::Int32(result),
43 None => Value::Integer(num_bigint::BigInt::from(*i1) + num_bigint::BigInt::from(*i2)),
45 }
46 }
47 (Value::Integer(i1), Value::Integer(i2)) => Value::Integer(i1 + i2),
48 (Value::Rational(r1), Value::Rational(r2)) => Value::Rational(r1 + r2).demote(),
49 (Value::Number(n1), Value::Number(n2)) => Value::Number(n1 + n2),
50 #[cfg(feature = "complex_numbers")]
51 (Value::GaussianInt(re1, im1), Value::GaussianInt(re2, im2)) => {
52 Value::GaussianInt(re1 + re2, im1 + im2).demote()
53 }
54 #[cfg(feature = "complex_numbers")]
55 (Value::Complex(c1), Value::Complex(c2)) => Value::Complex(c1 + c2),
56 _ => {
57 return Err(RuntimeError::TypeError(format!(
58 "Cannot add {:?} and {:?}",
59 a, b
60 )))
61 }
62 };
63
64 interp.push(result);
65 Ok(())
66}
67
68#[cfg(test)]
69mod tests {
70 use super::*;
71 use crate::value::Value;
72
73 fn setup_interpreter() -> Interpreter {
74 Interpreter::new()
75 }
76
77 #[test]
78 fn test_add_builtin() {
79 let mut interp = setup_interpreter();
80
81 interp.push(Value::Number(3.0));
83 interp.push(Value::Number(5.0));
84 add_builtin(&mut interp).unwrap();
85
86 let result = interp.pop().unwrap();
87 assert!(matches!(result, Value::Number(n) if n == 8.0));
88
89 interp.push(Value::Number(-2.0));
91 interp.push(Value::Number(7.0));
92 add_builtin(&mut interp).unwrap();
93
94 let result = interp.pop().unwrap();
95 assert!(matches!(result, Value::Number(n) if n == 5.0));
96
97 interp.push(Value::Number(0.0));
99 interp.push(Value::Number(42.0));
100 add_builtin(&mut interp).unwrap();
101
102 let result = interp.pop().unwrap();
103 assert!(matches!(result, Value::Number(n) if n == 42.0));
104 }
105
106 #[test]
107 fn test_add_builtin_stack_underflow() {
108 let mut interp = setup_interpreter();
109
110 let result = add_builtin(&mut interp);
112 assert!(matches!(result, Err(RuntimeError::StackUnderflow)));
113
114 interp.push(Value::Number(5.0));
116 let result = add_builtin(&mut interp);
117 assert!(matches!(result, Err(RuntimeError::StackUnderflow)));
118 }
119
120 #[test]
121 fn test_add_builtin_string_concatenation() {
122 let mut interp = setup_interpreter();
123
124 interp.push(Value::String("Hello ".into()));
126 interp.push(Value::String("World".into()));
127 add_builtin(&mut interp).unwrap();
128
129 let result = interp.pop().unwrap();
130 assert!(matches!(result, Value::String(s) if s.as_ref() == "Hello World"));
131
132 interp.push(Value::String("Count: ".into()));
134 interp.push(Value::Number(42.0));
135 add_builtin(&mut interp).unwrap();
136
137 let result = interp.pop().unwrap();
138 assert!(matches!(result, Value::String(s) if s.as_ref() == "Count: 42"));
139
140 interp.push(Value::Number(3.14));
142 interp.push(Value::String(" is pi".into()));
143 add_builtin(&mut interp).unwrap();
144
145 let result = interp.pop().unwrap();
146 assert!(matches!(result, Value::String(s) if s.as_ref() == "3.14 is pi"));
147
148 interp.push(Value::String("Result: ".into()));
150 interp.push(Value::Boolean(true));
151 add_builtin(&mut interp).unwrap();
152
153 let result = interp.pop().unwrap();
154 assert!(matches!(result, Value::String(s) if s.as_ref() == "Result: true"));
155 }
156
157 #[test]
158 fn test_add_builtin_type_error() {
159 let mut interp = setup_interpreter();
160
161 interp.push(Value::Boolean(true));
163 interp.push(Value::Boolean(false));
164 let result = add_builtin(&mut interp);
165 assert!(matches!(result, Err(RuntimeError::TypeError(_))));
166 }
167
168 #[test]
169 fn test_add_builtin_position_aware_error() {
170 use crate::tokenizer::SourcePos;
171 let mut interp = setup_interpreter();
172
173 let pos = SourcePos::new(2, 15, 20); interp.current_pos = Some(pos);
176
177 let result = add_builtin(&mut interp);
179 assert!(result.is_err());
180
181 match result.unwrap_err() {
182 RuntimeError::StackUnderflowAt { pos, context } => {
183 assert_eq!(pos.line, 2);
184 assert_eq!(pos.column, 15);
185 assert_eq!(pos.offset, 20);
186 assert!(context.contains("'+' requires exactly 2 values"));
187 }
188 _ => panic!("Expected StackUnderflowAt error"),
189 }
190
191 interp.push(Value::Number(42.0));
193 let result = add_builtin(&mut interp);
194 assert!(result.is_err());
195
196 match result.unwrap_err() {
197 RuntimeError::StackUnderflowAt { pos, context } => {
198 assert_eq!(pos.line, 2);
199 assert_eq!(pos.column, 15);
200 assert!(context.contains("'+' requires exactly 2 values"));
201 }
202 _ => panic!("Expected StackUnderflowAt error"),
203 }
204 }
205
206 #[test]
207 fn test_demonstrate_formatted_error_output() {
208 use crate::tokenizer::SourcePos;
209 let mut interp = setup_interpreter();
210
211 let pos = SourcePos::new(3, 8, 45); interp.current_pos = Some(pos);
214
215 let result = add_builtin(&mut interp);
217 assert!(result.is_err());
218
219 let error = result.unwrap_err();
221 let formatted_error = format!("{}", error);
222
223 println!("Demo error message: {}", formatted_error);
225
226 assert!(formatted_error.contains("line 3, column 8"));
228 assert!(formatted_error.contains("'+' requires exactly 2 values"));
229 assert!(formatted_error.contains("Stack underflow"));
230 }
231
232 #[test]
235 fn test_add_bigint() {
236 use num_bigint::BigInt;
237 let mut interp = setup_interpreter();
238
239 interp.push(Value::Integer(BigInt::from(123)));
241 interp.push(Value::Integer(BigInt::from(456)));
242 add_builtin(&mut interp).unwrap();
243
244 let result = interp.pop().unwrap();
245 assert!(matches!(result, Value::Integer(i) if i == BigInt::from(579)));
246
247 let large1 = BigInt::parse_bytes(b"123456789012345678901234567890", 10).unwrap();
249 let large2 = BigInt::parse_bytes(b"987654321098765432109876543210", 10).unwrap();
250 let expected = BigInt::parse_bytes(b"1111111110111111111011111111100", 10).unwrap();
251
252 interp.push(Value::Integer(large1));
253 interp.push(Value::Integer(large2));
254 add_builtin(&mut interp).unwrap();
255
256 let result = interp.pop().unwrap();
257 assert!(matches!(result, Value::Integer(i) if i == expected));
258 }
259
260 #[test]
261 fn test_add_rational() {
262 use num_bigint::BigInt;
263 use num_rational::BigRational;
264 let mut interp = setup_interpreter();
265
266 let r1 = BigRational::new(BigInt::from(1), BigInt::from(2));
268 let r2 = BigRational::new(BigInt::from(1), BigInt::from(3));
269 let expected = BigRational::new(BigInt::from(5), BigInt::from(6));
270
271 interp.push(Value::Rational(r1));
272 interp.push(Value::Rational(r2));
273 add_builtin(&mut interp).unwrap();
274
275 let result = interp.pop().unwrap();
276 assert!(matches!(result, Value::Rational(r) if r == expected));
277
278 let r1 = BigRational::new(BigInt::from(1), BigInt::from(3));
280 let r2 = BigRational::new(BigInt::from(1), BigInt::from(3));
281 let expected = BigRational::new(BigInt::from(2), BigInt::from(3));
282
283 interp.push(Value::Rational(r1));
284 interp.push(Value::Rational(r2));
285 add_builtin(&mut interp).unwrap();
286
287 let result = interp.pop().unwrap();
288 assert!(matches!(result, Value::Rational(r) if r == expected));
289 }
290
291 #[test]
292 #[cfg(feature = "complex_numbers")]
293 fn test_add_complex() {
294 use num_complex::Complex64;
295 let mut interp = setup_interpreter();
296
297 let c1 = Complex64::new(3.0, 4.0);
299 let c2 = Complex64::new(1.0, 2.0);
300 let expected = Complex64::new(4.0, 6.0);
301
302 interp.push(Value::Complex(c1));
303 interp.push(Value::Complex(c2));
304 add_builtin(&mut interp).unwrap();
305
306 let result = interp.pop().unwrap();
307 assert!(matches!(result, Value::Complex(c) if c == expected));
308
309 let c1 = Complex64::new(0.0, 5.0);
311 let c2 = Complex64::new(0.0, 3.0);
312 let expected = Complex64::new(0.0, 8.0);
313
314 interp.push(Value::Complex(c1));
315 interp.push(Value::Complex(c2));
316 add_builtin(&mut interp).unwrap();
317
318 let result = interp.pop().unwrap();
319 assert!(matches!(result, Value::Complex(c) if c == expected));
320 }
321
322 #[test]
323 fn test_add_mixed_float_bigint() {
324 use num_bigint::BigInt;
325 let mut interp = setup_interpreter();
326
327 interp.push(Value::Number(5.0));
329 interp.push(Value::Integer(BigInt::from(10)));
330 add_builtin(&mut interp).unwrap();
331
332 let result = interp.pop().unwrap();
333 assert!(matches!(result, Value::Number(n) if n == 15.0));
334
335 interp.push(Value::Integer(BigInt::from(20)));
337 interp.push(Value::Number(7.0));
338 add_builtin(&mut interp).unwrap();
339
340 let result = interp.pop().unwrap();
341 assert!(matches!(result, Value::Number(n) if n == 27.0));
342 }
343
344 #[test]
345 fn test_add_mixed_float_rational() {
346 use num_bigint::BigInt;
347 use num_rational::BigRational;
348 let mut interp = setup_interpreter();
349
350 interp.push(Value::Number(0.5));
352 interp.push(Value::Rational(BigRational::new(BigInt::from(1), BigInt::from(2))));
353 add_builtin(&mut interp).unwrap();
354
355 let result = interp.pop().unwrap();
356 assert!(matches!(result, Value::Number(n) if n == 1.0));
358 }
359
360 #[test]
361 #[cfg(feature = "complex_numbers")]
362 fn test_add_mixed_float_complex() {
363 use num_complex::Complex64;
364 let mut interp = setup_interpreter();
365
366 interp.push(Value::Number(3.0));
368 interp.push(Value::Complex(Complex64::new(2.0, 5.0)));
369 add_builtin(&mut interp).unwrap();
370
371 let result = interp.pop().unwrap();
372 assert!(matches!(result, Value::Complex(c) if c == Complex64::new(5.0, 5.0)));
373 }
374
375 #[test]
376 fn test_add_mixed_bigint_rational() {
377 use num_bigint::BigInt;
378 use num_rational::BigRational;
379 let mut interp = setup_interpreter();
380
381 interp.push(Value::Integer(BigInt::from(2)));
383 interp.push(Value::Rational(BigRational::new(BigInt::from(1), BigInt::from(2))));
384 add_builtin(&mut interp).unwrap();
385
386 let result = interp.pop().unwrap();
387 let expected = BigRational::new(BigInt::from(5), BigInt::from(2));
388 assert!(matches!(result, Value::Rational(r) if r == expected));
389 }
390
391 #[test]
392 #[cfg(feature = "complex_numbers")]
393 fn test_add_mixed_bigint_complex() {
394 use num_bigint::BigInt;
395 use num_complex::Complex64;
396 let mut interp = setup_interpreter();
397
398 interp.push(Value::Integer(BigInt::from(5)));
400 interp.push(Value::Complex(Complex64::new(1.0, 2.0)));
401 add_builtin(&mut interp).unwrap();
402
403 let result = interp.pop().unwrap();
404 assert!(matches!(result, Value::Complex(c) if c == Complex64::new(6.0, 2.0)));
405 }
406
407 #[test]
408 #[cfg(feature = "complex_numbers")]
409 fn test_add_mixed_rational_complex() {
410 use num_bigint::BigInt;
411 use num_complex::Complex64;
412 use num_rational::BigRational;
413 let mut interp = setup_interpreter();
414
415 interp.push(Value::Rational(BigRational::new(BigInt::from(1), BigInt::from(2))));
417 interp.push(Value::Complex(Complex64::new(3.0, 4.0)));
418 add_builtin(&mut interp).unwrap();
419
420 let result = interp.pop().unwrap();
421 assert!(matches!(result, Value::Complex(c) if c == Complex64::new(3.5, 4.0)));
422 }
423
424 #[test]
427 fn test_add_bigint_zero() {
428 use num_bigint::BigInt;
429 let mut interp = setup_interpreter();
430
431 interp.push(Value::Integer(BigInt::from(0)));
433 interp.push(Value::Integer(BigInt::from(0)));
434 add_builtin(&mut interp).unwrap();
435
436 let result = interp.pop().unwrap();
437 assert!(matches!(result, Value::Integer(i) if i == BigInt::from(0)));
438
439 interp.push(Value::Integer(BigInt::from(0)));
441 interp.push(Value::Integer(BigInt::from(42)));
442 add_builtin(&mut interp).unwrap();
443
444 let result = interp.pop().unwrap();
445 assert!(matches!(result, Value::Integer(i) if i == BigInt::from(42)));
446 }
447
448 #[test]
449 fn test_add_bigint_negative() {
450 use num_bigint::BigInt;
451 let mut interp = setup_interpreter();
452
453 interp.push(Value::Integer(BigInt::from(-5)));
455 interp.push(Value::Integer(BigInt::from(-3)));
456 add_builtin(&mut interp).unwrap();
457
458 let result = interp.pop().unwrap();
459 assert!(matches!(result, Value::Integer(i) if i == BigInt::from(-8)));
460
461 interp.push(Value::Integer(BigInt::from(10)));
463 interp.push(Value::Integer(BigInt::from(-7)));
464 add_builtin(&mut interp).unwrap();
465
466 let result = interp.pop().unwrap();
467 assert!(matches!(result, Value::Integer(i) if i == BigInt::from(3)));
468 }
469
470 #[test]
471 fn test_add_rational_zero() {
472 use num_bigint::BigInt;
473 use num_rational::BigRational;
474 let mut interp = setup_interpreter();
475
476 let zero = BigRational::new(BigInt::from(0), BigInt::from(1));
478 interp.push(Value::Rational(zero.clone()));
479 interp.push(Value::Rational(zero.clone()));
480 add_builtin(&mut interp).unwrap();
481
482 let result = interp.pop().unwrap();
483 assert!(matches!(result, Value::Int32(0)));
484
485 let frac = BigRational::new(BigInt::from(3), BigInt::from(4));
487 interp.push(Value::Rational(zero));
488 interp.push(Value::Rational(frac.clone()));
489 add_builtin(&mut interp).unwrap();
490
491 let result = interp.pop().unwrap();
492 assert!(matches!(result, Value::Rational(r) if r == frac));
493 }
494
495 #[test]
496 fn test_add_rational_negative() {
497 use num_bigint::BigInt;
498 use num_rational::BigRational;
499 let mut interp = setup_interpreter();
500
501 let neg_half = BigRational::new(BigInt::from(-1), BigInt::from(2));
503 let pos_half = BigRational::new(BigInt::from(1), BigInt::from(2));
504 interp.push(Value::Rational(neg_half));
505 interp.push(Value::Rational(pos_half));
506 add_builtin(&mut interp).unwrap();
507
508 let result = interp.pop().unwrap();
509 assert!(matches!(result, Value::Int32(0)));
510
511 let r1 = BigRational::new(BigInt::from(-3), BigInt::from(4));
513 let r2 = BigRational::new(BigInt::from(-1), BigInt::from(4));
514 interp.push(Value::Rational(r1));
515 interp.push(Value::Rational(r2));
516 add_builtin(&mut interp).unwrap();
517
518 let result = interp.pop().unwrap();
519 assert!(matches!(result, Value::Int32(-1)));
520 }
521
522 #[test]
523 fn test_add_rational_simplification() {
524 use num_bigint::BigInt;
525 use num_rational::BigRational;
526 let mut interp = setup_interpreter();
527
528 let quarter = BigRational::new(BigInt::from(1), BigInt::from(4));
530 let half = BigRational::new(BigInt::from(1), BigInt::from(2));
531
532 interp.push(Value::Rational(quarter.clone()));
533 interp.push(Value::Rational(quarter));
534 add_builtin(&mut interp).unwrap();
535
536 let result = interp.pop().unwrap();
537 assert!(matches!(result, Value::Rational(r) if r == half));
538 }
539
540 #[test]
541 #[cfg(feature = "complex_numbers")]
542 fn test_add_complex_zero() {
543 use num_complex::Complex64;
544 let mut interp = setup_interpreter();
545
546 let zero = Complex64::new(0.0, 0.0);
548 interp.push(Value::Complex(zero));
549 interp.push(Value::Complex(zero));
550 add_builtin(&mut interp).unwrap();
551
552 let result = interp.pop().unwrap();
553 assert!(matches!(result, Value::Complex(c) if c == zero));
554
555 let c = Complex64::new(3.0, 4.0);
557 interp.push(Value::Complex(zero));
558 interp.push(Value::Complex(c));
559 add_builtin(&mut interp).unwrap();
560
561 let result = interp.pop().unwrap();
562 assert!(matches!(result, Value::Complex(c) if c == Complex64::new(3.0, 4.0)));
563 }
564
565 #[test]
566 #[cfg(feature = "complex_numbers")]
567 fn test_add_complex_negative() {
568 use num_complex::Complex64;
569 let mut interp = setup_interpreter();
570
571 let c1 = Complex64::new(3.0, 4.0);
573 let c2 = Complex64::new(-3.0, -4.0);
574 let zero = Complex64::new(0.0, 0.0);
575
576 interp.push(Value::Complex(c1));
577 interp.push(Value::Complex(c2));
578 add_builtin(&mut interp).unwrap();
579
580 let result = interp.pop().unwrap();
581 assert!(matches!(result, Value::Complex(c) if c == zero));
582
583 let c1 = Complex64::new(-5.0, 2.0);
585 let c2 = Complex64::new(3.0, -1.0);
586 let expected = Complex64::new(-2.0, 1.0);
587
588 interp.push(Value::Complex(c1));
589 interp.push(Value::Complex(c2));
590 add_builtin(&mut interp).unwrap();
591
592 let result = interp.pop().unwrap();
593 assert!(matches!(result, Value::Complex(c) if c == expected));
594 }
595
596 #[test]
597 #[cfg(feature = "complex_numbers")]
598 fn test_add_complex_pure_real() {
599 use num_complex::Complex64;
600 let mut interp = setup_interpreter();
601
602 let c1 = Complex64::new(5.0, 0.0);
604 let c2 = Complex64::new(3.0, 0.0);
605 let expected = Complex64::new(8.0, 0.0);
606
607 interp.push(Value::Complex(c1));
608 interp.push(Value::Complex(c2));
609 add_builtin(&mut interp).unwrap();
610
611 let result = interp.pop().unwrap();
612 assert!(matches!(result, Value::Complex(c) if c == expected));
613 }
614
615 #[test]
616 #[cfg(feature = "complex_numbers")]
617 fn test_add_complex_pure_imaginary() {
618 use num_complex::Complex64;
619 let mut interp = setup_interpreter();
620
621 let c1 = Complex64::new(0.0, 5.0);
623 let c2 = Complex64::new(0.0, 3.0);
624 let expected = Complex64::new(0.0, 8.0);
625
626 interp.push(Value::Complex(c1));
627 interp.push(Value::Complex(c2));
628 add_builtin(&mut interp).unwrap();
629
630 let result = interp.pop().unwrap();
631 assert!(matches!(result, Value::Complex(c) if c == expected));
632 }
633
634 #[test]
635 fn test_add_mixed_float_bigint_fractional() {
636 use num_bigint::BigInt;
637 let mut interp = setup_interpreter();
638
639 interp.push(Value::Number(3.5));
641 interp.push(Value::Integer(BigInt::from(10)));
642 add_builtin(&mut interp).unwrap();
643
644 let result = interp.pop().unwrap();
645 assert!(matches!(result, Value::Number(n) if n == 13.5));
647 }
648
649 #[test]
650 #[cfg(feature = "complex_numbers")]
651 fn test_add_mixed_types_commutativity() {
652 use num_bigint::BigInt;
653 use num_complex::Complex64;
654 let mut interp = setup_interpreter();
655
656 interp.push(Value::Integer(BigInt::from(5)));
659 interp.push(Value::Complex(Complex64::new(1.0, 2.0)));
660 add_builtin(&mut interp).unwrap();
661 let result1 = interp.pop().unwrap();
662
663 interp.push(Value::Complex(Complex64::new(1.0, 2.0)));
665 interp.push(Value::Integer(BigInt::from(5)));
666 add_builtin(&mut interp).unwrap();
667 let result2 = interp.pop().unwrap();
668
669 assert!(matches!(result1, Value::Complex(c) if c == Complex64::new(6.0, 2.0)));
671 assert!(matches!(result2, Value::Complex(c) if c == Complex64::new(6.0, 2.0)));
672 }
673
674 #[test]
675 #[cfg(feature = "complex_numbers")]
676 fn test_add_type_error_with_new_types() {
677 let mut interp = setup_interpreter();
678
679 use num_bigint::BigInt;
681 interp.push(Value::Integer(BigInt::from(5)));
682 interp.push(Value::Atom("foo".into()));
683 let result = add_builtin(&mut interp);
684 assert!(matches!(result, Err(RuntimeError::TypeError(_))));
685
686 use num_rational::BigRational;
688 interp.stack.clear();
689 interp.push(Value::Rational(BigRational::new(BigInt::from(1), BigInt::from(2))));
690 interp.push(Value::Boolean(true));
691 let result = add_builtin(&mut interp);
692 assert!(matches!(result, Err(RuntimeError::TypeError(_))));
693
694 use num_complex::Complex64;
696 interp.stack.clear();
697 interp.push(Value::Complex(Complex64::new(1.0, 2.0)));
698 interp.push(Value::Nil);
699 let result = add_builtin(&mut interp);
700 assert!(matches!(result, Err(RuntimeError::TypeError(_))));
701 }
702
703 #[test]
704 fn test_add_very_large_bigint() {
705 use num_bigint::BigInt;
706 let mut interp = setup_interpreter();
707
708 let large1 = BigInt::parse_bytes(b"99999999999999999999999999999999", 10).unwrap();
710 let large2 = BigInt::parse_bytes(b"11111111111111111111111111111111", 10).unwrap();
711 let expected = BigInt::parse_bytes(b"111111111111111111111111111111110", 10).unwrap();
712
713 interp.push(Value::Integer(large1));
714 interp.push(Value::Integer(large2));
715 add_builtin(&mut interp).unwrap();
716
717 let result = interp.pop().unwrap();
718 assert!(matches!(result, Value::Integer(i) if i == expected));
719 }
720
721 #[test]
722 fn test_add_infinity_and_nan() {
723 let mut interp = setup_interpreter();
724
725 interp.push(Value::Number(f64::INFINITY));
727 interp.push(Value::Number(42.0));
728 add_builtin(&mut interp).unwrap();
729
730 let result = interp.pop().unwrap();
731 assert!(matches!(result, Value::Number(n) if n.is_infinite()));
732
733 interp.push(Value::Number(f64::NAN));
735 interp.push(Value::Number(42.0));
736 add_builtin(&mut interp).unwrap();
737
738 let result = interp.pop().unwrap();
739 assert!(matches!(result, Value::Number(n) if n.is_nan()));
740 }
741
742 #[test]
745 #[cfg(feature = "complex_numbers")]
746 #[cfg(feature = "complex_numbers")]
747 fn test_add_gaussian_int() {
748 use num_bigint::BigInt;
749 let mut interp = setup_interpreter();
750
751 interp.push(Value::GaussianInt(BigInt::from(3), BigInt::from(4)));
753 interp.push(Value::GaussianInt(BigInt::from(1), BigInt::from(2)));
754 add_builtin(&mut interp).unwrap();
755
756 let result = interp.pop().unwrap();
757 assert!(matches!(
758 result,
759 Value::GaussianInt(re, im) if re == BigInt::from(4) && im == BigInt::from(6)
760 ));
761
762 interp.push(Value::GaussianInt(BigInt::from(5), BigInt::from(3)));
764 interp.push(Value::GaussianInt(BigInt::from(2), BigInt::from(-7)));
765 add_builtin(&mut interp).unwrap();
766
767 let result = interp.pop().unwrap();
768 assert!(matches!(
769 result,
770 Value::GaussianInt(re, im) if re == BigInt::from(7) && im == BigInt::from(-4)
771 ));
772 }
773
774 #[test]
775 #[cfg(feature = "complex_numbers")]
776 fn test_add_gaussian_int_zero() {
777 use num_bigint::BigInt;
778 let mut interp = setup_interpreter();
779
780 interp.push(Value::GaussianInt(BigInt::from(0), BigInt::from(0)));
782 interp.push(Value::GaussianInt(BigInt::from(0), BigInt::from(0)));
783 add_builtin(&mut interp).unwrap();
784
785 let result = interp.pop().unwrap();
786 assert!(matches!(result, Value::Int32(0)));
787
788 interp.push(Value::GaussianInt(BigInt::from(0), BigInt::from(0)));
790 interp.push(Value::GaussianInt(BigInt::from(3), BigInt::from(4)));
791 add_builtin(&mut interp).unwrap();
792
793 let result = interp.pop().unwrap();
794 assert!(matches!(
795 result,
796 Value::GaussianInt(re, im) if re == BigInt::from(3) && im == BigInt::from(4)
797 ));
798 }
799
800 #[test]
801 #[cfg(feature = "complex_numbers")]
802 fn test_add_gaussian_int_pure_real() {
803 use num_bigint::BigInt;
804 let mut interp = setup_interpreter();
805
806 interp.push(Value::GaussianInt(BigInt::from(5), BigInt::from(0)));
808 interp.push(Value::GaussianInt(BigInt::from(3), BigInt::from(0)));
809 add_builtin(&mut interp).unwrap();
810
811 let result = interp.pop().unwrap();
812 assert!(matches!(result, Value::Int32(8)));
813 }
814
815 #[test]
816 #[cfg(feature = "complex_numbers")]
817 fn test_add_gaussian_int_pure_imaginary() {
818 use num_bigint::BigInt;
819 let mut interp = setup_interpreter();
820
821 interp.push(Value::GaussianInt(BigInt::from(0), BigInt::from(5)));
823 interp.push(Value::GaussianInt(BigInt::from(0), BigInt::from(3)));
824 add_builtin(&mut interp).unwrap();
825
826 let result = interp.pop().unwrap();
827 assert!(matches!(
828 result,
829 Value::GaussianInt(re, im) if re == BigInt::from(0) && im == BigInt::from(8)
830 ));
831 }
832
833 #[test]
834 #[cfg(feature = "complex_numbers")]
835 fn test_add_gaussian_int_with_bigint() {
836 use num_bigint::BigInt;
837 let mut interp = setup_interpreter();
838
839 interp.push(Value::GaussianInt(BigInt::from(3), BigInt::from(4)));
841 interp.push(Value::Integer(BigInt::from(5)));
842 add_builtin(&mut interp).unwrap();
843
844 let result = interp.pop().unwrap();
845 assert!(matches!(
846 result,
847 Value::GaussianInt(re, im) if re == BigInt::from(8) && im == BigInt::from(4)
848 ));
849
850 interp.push(Value::Integer(BigInt::from(7)));
852 interp.push(Value::GaussianInt(BigInt::from(2), BigInt::from(3)));
853 add_builtin(&mut interp).unwrap();
854
855 let result = interp.pop().unwrap();
856 assert!(matches!(
857 result,
858 Value::GaussianInt(re, im) if re == BigInt::from(9) && im == BigInt::from(3)
859 ));
860 }
861
862 #[test]
863 #[cfg(feature = "complex_numbers")]
864 #[cfg(feature = "complex_numbers")]
865 fn test_add_gaussian_int_with_float_promotes_to_complex() {
866 use num_bigint::BigInt;
867 use num_complex::Complex64;
868 let mut interp = setup_interpreter();
869
870 interp.push(Value::GaussianInt(BigInt::from(3), BigInt::from(4)));
872 interp.push(Value::Number(2.5));
873 add_builtin(&mut interp).unwrap();
874
875 let result = interp.pop().unwrap();
876 assert!(matches!(result, Value::Complex(c) if c == Complex64::new(5.5, 4.0)));
877
878 interp.push(Value::Number(1.5));
880 interp.push(Value::GaussianInt(BigInt::from(2), BigInt::from(3)));
881 add_builtin(&mut interp).unwrap();
882
883 let result = interp.pop().unwrap();
884 assert!(matches!(result, Value::Complex(c) if c == Complex64::new(3.5, 3.0)));
885 }
886
887 #[test]
888 #[cfg(feature = "complex_numbers")]
889 #[cfg(feature = "complex_numbers")]
890 fn test_add_gaussian_int_with_rational_promotes_to_complex() {
891 use num_bigint::BigInt;
892 use num_complex::Complex64;
893 use num_rational::BigRational;
894 let mut interp = setup_interpreter();
895
896 interp.push(Value::GaussianInt(BigInt::from(3), BigInt::from(4)));
898 interp.push(Value::Rational(BigRational::new(BigInt::from(1), BigInt::from(2))));
899 add_builtin(&mut interp).unwrap();
900
901 let result = interp.pop().unwrap();
902 assert!(matches!(result, Value::Complex(c) if c == Complex64::new(3.5, 4.0)));
903
904 interp.push(Value::Rational(BigRational::new(BigInt::from(1), BigInt::from(4))));
906 interp.push(Value::GaussianInt(BigInt::from(2), BigInt::from(5)));
907 add_builtin(&mut interp).unwrap();
908
909 let result = interp.pop().unwrap();
910 assert!(matches!(result, Value::Complex(c) if c == Complex64::new(2.25, 5.0)));
911 }
912
913 #[test]
914 #[cfg(feature = "complex_numbers")]
915 #[cfg(feature = "complex_numbers")]
916 fn test_add_gaussian_int_with_complex() {
917 use num_bigint::BigInt;
918 use num_complex::Complex64;
919 let mut interp = setup_interpreter();
920
921 interp.push(Value::GaussianInt(BigInt::from(3), BigInt::from(4)));
923 interp.push(Value::Complex(Complex64::new(1.5, 2.5)));
924 add_builtin(&mut interp).unwrap();
925
926 let result = interp.pop().unwrap();
927 assert!(matches!(result, Value::Complex(c) if c == Complex64::new(4.5, 6.5)));
928
929 interp.push(Value::Complex(Complex64::new(2.5, 1.5)));
931 interp.push(Value::GaussianInt(BigInt::from(5), BigInt::from(3)));
932 add_builtin(&mut interp).unwrap();
933
934 let result = interp.pop().unwrap();
935 assert!(matches!(result, Value::Complex(c) if c == Complex64::new(7.5, 4.5)));
936 }
937
938 #[test]
939 #[cfg(feature = "complex_numbers")]
940 fn test_add_gaussian_int_large_values() {
941 use num_bigint::BigInt;
942 let mut interp = setup_interpreter();
943
944 let large_re1 = BigInt::parse_bytes(b"123456789012345", 10).unwrap();
946 let large_im1 = BigInt::parse_bytes(b"987654321098765", 10).unwrap();
947 let large_re2 = BigInt::parse_bytes(b"111111111111111", 10).unwrap();
948 let large_im2 = BigInt::parse_bytes(b"222222222222222", 10).unwrap();
949
950 let expected_re = BigInt::parse_bytes(b"234567900123456", 10).unwrap();
951 let expected_im = BigInt::parse_bytes(b"1209876543320987", 10).unwrap();
952
953 interp.push(Value::GaussianInt(large_re1, large_im1));
954 interp.push(Value::GaussianInt(large_re2, large_im2));
955 add_builtin(&mut interp).unwrap();
956
957 let result = interp.pop().unwrap();
958 assert!(matches!(
959 result,
960 Value::GaussianInt(re, im) if re == expected_re && im == expected_im
961 ));
962 }
963
964 #[test]
965 #[cfg(feature = "complex_numbers")]
966 fn test_add_gaussian_int_negative_parts() {
967 use num_bigint::BigInt;
968 let mut interp = setup_interpreter();
969
970 interp.push(Value::GaussianInt(BigInt::from(-3), BigInt::from(4)));
972 interp.push(Value::GaussianInt(BigInt::from(5), BigInt::from(-2)));
973 add_builtin(&mut interp).unwrap();
974
975 let result = interp.pop().unwrap();
976 assert!(matches!(
977 result,
978 Value::GaussianInt(re, im) if re == BigInt::from(2) && im == BigInt::from(2)
979 ));
980
981 interp.push(Value::GaussianInt(BigInt::from(-5), BigInt::from(-3)));
983 interp.push(Value::GaussianInt(BigInt::from(-2), BigInt::from(-7)));
984 add_builtin(&mut interp).unwrap();
985
986 let result = interp.pop().unwrap();
987 assert!(matches!(
988 result,
989 Value::GaussianInt(re, im) if re == BigInt::from(-7) && im == BigInt::from(-10)
990 ));
991 }
992}