1use crate::cell::*;
16use crate::machine::Machine;
17
18#[derive(Debug, Clone, Copy, PartialEq)]
20pub enum ArithValue {
21 Int(i64),
22 Float(f64),
23}
24
25fn overflow(m: &mut Machine, operation: &str) {
28 let ctx = format!("Arithmetic error: integer overflow in {operation}");
29 crate::errors::evaluation(m, "int_overflow", &ctx);
30}
31
32fn zero_divisor(m: &mut Machine, label: &str) {
33 let ctx = format!("Division by zero ({label})");
34 crate::errors::evaluation(m, "zero_divisor", &ctx);
35}
36
37fn int_args_required(m: &mut Machine, op: &str) {
42 let culprit = make_atom(m.atoms.intern("member"));
43 let ctx = format!("{op} requires integer arguments");
44 crate::errors::type_error(m, "integer", culprit, &ctx);
45}
46
47fn shift_undefined(m: &mut Machine, op: &str) {
48 let ctx = format!("Shift {op} requires a non-negative count in [0, 64)");
49 crate::errors::evaluation(m, "undefined", &ctx);
50}
51
52fn check_float(m: &mut Machine, f: f64) -> Result<ArithValue, ()> {
54 if f.is_nan() {
55 crate::errors::evaluation(m, "undefined", "Arithmetic error: NaN result");
56 Err(())
57 } else if f.is_infinite() {
58 crate::errors::evaluation(m, "float_overflow", "Arithmetic error: Infinity result");
59 Err(())
60 } else {
61 Ok(ArithValue::Float(f))
62 }
63}
64
65fn as_f64(v: ArithValue) -> f64 {
66 match v {
67 ArithValue::Int(n) => n as f64,
68 ArithValue::Float(f) => f,
69 }
70}
71
72pub fn arith_lt(a: ArithValue, b: ArithValue) -> bool {
75 use ArithValue::*;
76 match (a, b) {
77 (Int(a), Int(b)) => a < b,
78 (Float(a), Float(b)) => a < b,
79 (Int(a), Float(b)) => (a as f64) < b,
80 (Float(a), Int(b)) => a < (b as f64),
81 }
82}
83
84pub fn arith_gt(a: ArithValue, b: ArithValue) -> bool {
85 arith_lt(b, a)
86}
87
88pub fn arith_eq(a: ArithValue, b: ArithValue) -> bool {
89 use ArithValue::*;
90 match (a, b) {
91 (Int(a), Int(b)) => a == b,
92 (Float(a), Float(b)) => a == b,
93 (Int(a), Float(b)) => (a as f64) == b,
94 (Float(a), Int(b)) => a == (b as f64),
95 }
96}
97
98#[allow(clippy::result_unit_err)]
105pub fn eval(m: &mut Machine, expr: Word) -> Result<ArithValue, ()> {
106 let w = m.deref(expr);
107 match tag_of(w) {
108 TAG_INT => Ok(ArithValue::Int(int_value(w))),
109 TAG_BIG => Ok(ArithValue::Int(m.heap[payload(w) as usize] as i64)),
110 TAG_FLT => Ok(ArithValue::Float(f64::from_bits(
111 m.heap[payload(w) as usize],
112 ))),
113 TAG_REF => {
114 let ctx = format!("Arithmetic error: unbound variable _{}", payload(w));
115 crate::errors::instantiation(m, &ctx);
116 Err(())
117 }
118 TAG_ATOM | TAG_LST => {
119 crate::errors::type_error(m, "evaluable", w, "Cannot evaluate as arithmetic");
122 Err(())
123 }
124 TAG_STR => eval_struct(m, w),
125 _ => unreachable!("bad tag in arith eval"),
126 }
127}
128
129fn eval_struct(m: &mut Machine, w: Word) -> Result<ArithValue, ()> {
130 let idx = payload(w) as usize;
131 let (functor, arity) = unpack_functor(m.heap[idx]);
132 let name = m.atoms.resolve(functor).to_string();
133 let a0 = m.heap[idx + 1];
135 match (name.as_str(), arity) {
136 ("+", 2) => {
137 let (a, b) = bin(m, idx)?;
138 add(m, a, b)
139 }
140 ("-", 2) => {
141 let (a, b) = bin(m, idx)?;
142 sub(m, a, b)
143 }
144 ("*", 2) => {
145 let (a, b) = bin(m, idx)?;
146 mul(m, a, b)
147 }
148 ("/", 2) => {
149 let (a, b) = bin(m, idx)?;
150 div(m, a, b)
151 }
152 ("//", 2) => {
153 let (a, b) = bin(m, idx)?;
154 int_div(m, a, b)
155 }
156 ("mod", 2) => {
157 let (a, b) = bin(m, idx)?;
158 modulo(m, a, b)
159 }
160 ("rem", 2) => {
161 let (a, b) = bin(m, idx)?;
162 rem(m, a, b)
163 }
164 ("**", 2) => {
165 let (a, b) = bin(m, idx)?;
166 pow_float(m, a, b)
167 }
168 ("^", 2) => {
169 let (a, b) = bin(m, idx)?;
170 pow(m, a, b)
171 }
172 ("<<", 2) => {
173 let (a, b) = bin(m, idx)?;
174 shl(m, a, b)
175 }
176 (">>", 2) => {
177 let (a, b) = bin(m, idx)?;
178 shr(m, a, b)
179 }
180 ("/\\", 2) => {
181 let (a, b) = bin(m, idx)?;
182 bit_and(m, a, b)
183 }
184 ("\\/", 2) => {
185 let (a, b) = bin(m, idx)?;
186 bit_or(m, a, b)
187 }
188 ("xor", 2) => {
189 let (a, b) = bin(m, idx)?;
190 bit_xor(m, a, b)
191 }
192 ("div", 2) => {
193 let (a, b) = bin(m, idx)?;
194 div_floor(m, a, b)
195 }
196 ("min", 2) => {
197 let (a, b) = bin(m, idx)?;
198 Ok(if arith_lt(a, b) { a } else { b })
199 }
200 ("max", 2) => {
201 let (a, b) = bin(m, idx)?;
202 Ok(if arith_lt(a, b) { b } else { a })
203 }
204 ("-", 1) => {
205 let a = eval(m, a0)?;
206 neg(m, a)
207 }
208 ("abs", 1) => {
209 let a = eval(m, a0)?;
210 abs(m, a)
211 }
212 ("sign", 1) => {
213 let a = eval(m, a0)?;
214 Ok(sign(a))
215 }
216 _ => {
217 let slash = m.atoms.intern("/");
219 let name_atom = make_atom(m.atoms.intern(&name));
220 let pi = m.heap.len();
221 m.heap.push(pack_functor(slash, 2));
222 m.heap.push(name_atom);
223 m.heap.push(make_int(arity as i64));
224 let culprit = make(TAG_STR, pi as u64);
225 let ctx = format!("Unknown arithmetic operator: {name}/{arity}");
226 crate::errors::type_error(m, "evaluable", culprit, &ctx);
227 Err(())
228 }
229 }
230}
231
232fn bin(m: &mut Machine, idx: usize) -> Result<(ArithValue, ArithValue), ()> {
234 let a = eval(m, m.heap[idx + 1])?;
235 let b = eval(m, m.heap[idx + 2])?;
236 Ok((a, b))
237}
238
239fn add(m: &mut Machine, a: ArithValue, b: ArithValue) -> Result<ArithValue, ()> {
242 use ArithValue::*;
243 match (a, b) {
244 (Int(a), Int(b)) => a
245 .checked_add(b)
246 .map(Int)
247 .ok_or_else(|| overflow(m, "addition")),
248 (Float(a), Float(b)) => check_float(m, a + b),
249 (Int(a), Float(b)) => check_float(m, a as f64 + b),
250 (Float(a), Int(b)) => check_float(m, a + b as f64),
251 }
252}
253
254fn sub(m: &mut Machine, a: ArithValue, b: ArithValue) -> Result<ArithValue, ()> {
255 use ArithValue::*;
256 match (a, b) {
257 (Int(a), Int(b)) => a
258 .checked_sub(b)
259 .map(Int)
260 .ok_or_else(|| overflow(m, "subtraction")),
261 (Float(a), Float(b)) => check_float(m, a - b),
262 (Int(a), Float(b)) => check_float(m, a as f64 - b),
263 (Float(a), Int(b)) => check_float(m, a - b as f64),
264 }
265}
266
267fn mul(m: &mut Machine, a: ArithValue, b: ArithValue) -> Result<ArithValue, ()> {
268 use ArithValue::*;
269 match (a, b) {
270 (Int(a), Int(b)) => a
271 .checked_mul(b)
272 .map(Int)
273 .ok_or_else(|| overflow(m, "multiplication")),
274 (Float(a), Float(b)) => check_float(m, a * b),
275 (Int(a), Float(b)) => check_float(m, a as f64 * b),
276 (Float(a), Int(b)) => check_float(m, a * b as f64),
277 }
278}
279
280fn div(m: &mut Machine, a: ArithValue, b: ArithValue) -> Result<ArithValue, ()> {
281 use ArithValue::*;
282 match (a, b) {
284 (_, Int(0)) => {
285 zero_divisor(m, "float division");
286 Err(())
287 }
288 (_, Float(0.0)) => {
289 zero_divisor(m, "float division");
290 Err(())
291 }
292 (Int(a), Int(b)) => check_float(m, a as f64 / b as f64),
293 (Float(a), Float(b)) => check_float(m, a / b),
294 (Int(a), Float(b)) => check_float(m, a as f64 / b),
295 (Float(a), Int(b)) => check_float(m, a / b as f64),
296 }
297}
298
299fn modulo(m: &mut Machine, a: ArithValue, b: ArithValue) -> Result<ArithValue, ()> {
300 use ArithValue::*;
301 match (a, b) {
302 (Int(_), Int(0)) => {
303 zero_divisor(m, "modulo");
304 Err(())
305 }
306 (Int(_), Int(i64::MIN)) => {
307 overflow(m, "mod");
308 Err(())
309 }
310 (Int(a), Int(b)) => {
311 let r = a.rem_euclid(b.abs());
313 if b < 0 && r != 0 {
314 Ok(Int(r - b.abs()))
315 } else {
316 Ok(Int(r))
317 }
318 }
319 _ => {
320 int_args_required(m, "mod");
321 Err(())
322 }
323 }
324}
325
326fn rem(m: &mut Machine, a: ArithValue, b: ArithValue) -> Result<ArithValue, ()> {
327 use ArithValue::*;
328 match (a, b) {
329 (Int(_), Int(0)) => {
330 zero_divisor(m, "remainder");
331 Err(())
332 }
333 (Int(a), Int(b)) => a.checked_rem(b).map(Int).ok_or_else(|| overflow(m, "rem")),
334 _ => {
335 int_args_required(m, "rem");
336 Err(())
337 }
338 }
339}
340
341fn int_div(m: &mut Machine, a: ArithValue, b: ArithValue) -> Result<ArithValue, ()> {
342 use ArithValue::*;
343 match (a, b) {
344 (Int(_), Int(0)) => {
345 zero_divisor(m, "integer division");
346 Err(())
347 }
348 (Int(a), Int(b)) => a
349 .checked_div(b)
350 .map(Int)
351 .ok_or_else(|| overflow(m, "division")),
352 _ => {
353 int_args_required(m, "//");
354 Err(())
355 }
356 }
357}
358
359fn div_floor(m: &mut Machine, a: ArithValue, b: ArithValue) -> Result<ArithValue, ()> {
360 use ArithValue::*;
361 match (a, b) {
362 (Int(_), Int(0)) => {
363 zero_divisor(m, "floor division");
364 Err(())
365 }
366 (Int(a), Int(b)) => {
367 let q = match a.checked_div(b) {
368 Some(q) => q,
369 None => {
370 overflow(m, "floor division");
371 return Err(());
372 }
373 };
374 let r = match a.checked_rem(b) {
375 Some(r) => r,
376 None => {
377 overflow(m, "floor division");
378 return Err(());
379 }
380 };
381 if r != 0 && (r < 0) != (b < 0) {
382 q.checked_sub(1)
383 .map(Int)
384 .ok_or_else(|| overflow(m, "floor division"))
385 } else {
386 Ok(Int(q))
387 }
388 }
389 _ => {
390 int_args_required(m, "div");
391 Err(())
392 }
393 }
394}
395
396fn pow_float(m: &mut Machine, a: ArithValue, b: ArithValue) -> Result<ArithValue, ()> {
397 check_float(m, as_f64(a).powf(as_f64(b)))
398}
399
400fn pow(m: &mut Machine, a: ArithValue, b: ArithValue) -> Result<ArithValue, ()> {
401 use ArithValue::*;
402 match (a, b) {
403 (Int(base), Int(exp)) if exp >= 0 => {
404 let exp_u32 = match u32::try_from(exp) {
405 Ok(e) => e,
406 Err(_) => {
407 overflow(m, "integer power");
408 return Err(());
409 }
410 };
411 base.checked_pow(exp_u32)
412 .map(Int)
413 .ok_or_else(|| overflow(m, "integer power"))
414 }
415 _ => check_float(m, as_f64(a).powf(as_f64(b))),
416 }
417}
418
419fn shl(m: &mut Machine, a: ArithValue, b: ArithValue) -> Result<ArithValue, ()> {
420 use ArithValue::*;
421 match (a, b) {
422 (Int(v), Int(n)) => {
423 let bits = match u32::try_from(n) {
424 Ok(b) => b,
425 Err(_) => {
426 shift_undefined(m, "<<");
427 return Err(());
428 }
429 };
430 if bits >= 64 {
431 shift_undefined(m, "<<");
432 return Err(());
433 }
434 v.checked_shl(bits)
435 .map(Int)
436 .ok_or_else(|| overflow(m, "shift_left"))
437 }
438 _ => {
439 int_args_required(m, "<<");
440 Err(())
441 }
442 }
443}
444
445fn shr(m: &mut Machine, a: ArithValue, b: ArithValue) -> Result<ArithValue, ()> {
446 use ArithValue::*;
447 match (a, b) {
448 (Int(v), Int(n)) => {
449 let bits = match u32::try_from(n) {
450 Ok(b) => b,
451 Err(_) => {
452 shift_undefined(m, ">>");
453 return Err(());
454 }
455 };
456 if bits >= 64 {
457 shift_undefined(m, ">>");
458 return Err(());
459 }
460 v.checked_shr(bits)
461 .map(Int)
462 .ok_or_else(|| overflow(m, "shift_right"))
463 }
464 _ => {
465 int_args_required(m, ">>");
466 Err(())
467 }
468 }
469}
470
471fn bit_and(m: &mut Machine, a: ArithValue, b: ArithValue) -> Result<ArithValue, ()> {
472 use ArithValue::*;
473 match (a, b) {
474 (Int(a), Int(b)) => Ok(Int(a & b)),
475 _ => {
476 int_args_required(m, "/\\");
477 Err(())
478 }
479 }
480}
481
482fn bit_or(m: &mut Machine, a: ArithValue, b: ArithValue) -> Result<ArithValue, ()> {
483 use ArithValue::*;
484 match (a, b) {
485 (Int(a), Int(b)) => Ok(Int(a | b)),
486 _ => {
487 int_args_required(m, "\\/");
488 Err(())
489 }
490 }
491}
492
493fn bit_xor(m: &mut Machine, a: ArithValue, b: ArithValue) -> Result<ArithValue, ()> {
494 use ArithValue::*;
495 match (a, b) {
496 (Int(a), Int(b)) => Ok(Int(a ^ b)),
497 _ => {
498 int_args_required(m, "xor");
499 Err(())
500 }
501 }
502}
503
504fn neg(m: &mut Machine, a: ArithValue) -> Result<ArithValue, ()> {
507 use ArithValue::*;
508 match a {
509 Int(n) => n
510 .checked_neg()
511 .map(Int)
512 .ok_or_else(|| overflow(m, "negation")),
513 Float(f) => check_float(m, -f),
514 }
515}
516
517fn abs(m: &mut Machine, a: ArithValue) -> Result<ArithValue, ()> {
518 use ArithValue::*;
519 match a {
520 Int(n) => n.checked_abs().map(Int).ok_or_else(|| overflow(m, "abs")),
521 Float(f) => check_float(m, f.abs()),
522 }
523}
524
525fn sign(a: ArithValue) -> ArithValue {
526 match a {
527 ArithValue::Int(n) => ArithValue::Int(n.signum()),
528 ArithValue::Float(f) => ArithValue::Float(f.signum()),
529 }
530}
531
532#[cfg(test)]
533mod tests {
534 use super::*;
535 use plg_shared::StringInterner;
536
537 fn machine() -> Box<Machine> {
538 Machine::new(StringInterner::new(), Vec::new())
539 }
540
541 fn bin_str(m: &mut Machine, op: &str, a: Word, b: Word) -> Word {
543 let f = m.atoms.intern(op);
544 let idx = m.heap.len();
545 m.heap.push(pack_functor(f, 2));
546 m.heap.push(a);
547 m.heap.push(b);
548 make(TAG_STR, idx as u64)
549 }
550
551 fn un_str(m: &mut Machine, op: &str, a: Word) -> Word {
552 let f = m.atoms.intern(op);
553 let idx = m.heap.len();
554 m.heap.push(pack_functor(f, 1));
555 m.heap.push(a);
556 make(TAG_STR, idx as u64)
557 }
558
559 fn flt(m: &mut Machine, f: f64) -> Word {
560 let idx = m.heap.len();
561 m.heap.push(f.to_bits());
562 make(TAG_FLT, idx as u64)
563 }
564
565 fn msg(m: &Machine) -> &str {
566 m.error.as_ref().unwrap().message.as_str()
567 }
568
569 #[test]
570 fn happy_paths() {
571 let mut m = machine();
572 let e = bin_str(&mut m, "+", make_int(2), make_int(3));
573 assert_eq!(eval(&mut m, e), Ok(ArithValue::Int(5)));
574
575 let e = bin_str(&mut m, "*", make_int(4), make_int(5));
576 assert_eq!(eval(&mut m, e), Ok(ArithValue::Int(20)));
577
578 let two = flt(&mut m, 2.0);
580 let e = bin_str(&mut m, "+", two, make_int(3));
581 assert_eq!(eval(&mut m, e), Ok(ArithValue::Float(5.0)));
582
583 let e = bin_str(&mut m, "**", make_int(2), make_int(3));
585 assert_eq!(eval(&mut m, e), Ok(ArithValue::Float(8.0)));
586
587 let e = bin_str(&mut m, "^", make_int(2), make_int(3));
589 assert_eq!(eval(&mut m, e), Ok(ArithValue::Int(8)));
590
591 let e = bin_str(&mut m, "mod", make_int(10), make_int(-3));
593 assert_eq!(eval(&mut m, e), Ok(ArithValue::Int(-2)));
594 let e = bin_str(&mut m, "mod", make_int(-10), make_int(3));
595 assert_eq!(eval(&mut m, e), Ok(ArithValue::Int(2)));
596
597 let e = bin_str(&mut m, "div", make_int(10), make_int(-3));
599 assert_eq!(eval(&mut m, e), Ok(ArithValue::Int(-4)));
600
601 let e = un_str(&mut m, "abs", make_int(-5));
602 assert_eq!(eval(&mut m, e), Ok(ArithValue::Int(5)));
603 let e = un_str(&mut m, "sign", make_int(-5));
604 assert_eq!(eval(&mut m, e), Ok(ArithValue::Int(-1)));
605 let e = un_str(&mut m, "-", make_int(3));
606 assert_eq!(eval(&mut m, e), Ok(ArithValue::Int(-3)));
607
608 let e = bin_str(&mut m, "/\\", make_int(5), make_int(3));
609 assert_eq!(eval(&mut m, e), Ok(ArithValue::Int(1)));
610 let e = bin_str(&mut m, "xor", make_int(3), make_int(5));
611 assert_eq!(eval(&mut m, e), Ok(ArithValue::Int(6)));
612 let e = bin_str(&mut m, "<<", make_int(5), make_int(1));
613 assert_eq!(eval(&mut m, e), Ok(ArithValue::Int(10)));
614
615 let two = flt(&mut m, 2.0);
617 let e = bin_str(&mut m, "max", make_int(1), two);
618 assert_eq!(eval(&mut m, e), Ok(ArithValue::Float(2.0)));
619 let two = flt(&mut m, 2.0);
620 let e = bin_str(&mut m, "min", make_int(1), two);
621 assert_eq!(eval(&mut m, e), Ok(ArithValue::Int(1)));
622 }
623
624 #[test]
625 fn err_zero_divisors() {
626 let cases = [
627 ("//", "integer division"),
628 ("mod", "modulo"),
629 ("rem", "remainder"),
630 ("div", "floor division"),
631 ];
632 for (op, label) in cases {
633 let mut m = machine();
634 let e = bin_str(&mut m, op, make_int(1), make_int(0));
635 assert!(eval(&mut m, e).is_err());
636 assert_eq!(
637 msg(&m),
638 format!("error(evaluation_error(zero_divisor), Division by zero ({label}))")
639 );
640 }
641 let mut m = machine();
643 let e = bin_str(&mut m, "/", make_int(1), make_int(0));
644 assert!(eval(&mut m, e).is_err());
645 assert_eq!(
646 msg(&m),
647 "error(evaluation_error(zero_divisor), Division by zero (float division))"
648 );
649 }
650
651 #[test]
652 fn err_int_overflow() {
653 let mut m = machine();
658 let e = bin_str(&mut m, "*", make_int(INT_MAX), make_int(INT_MAX));
659 assert!(eval(&mut m, e).is_err());
660 assert_eq!(
661 msg(&m),
662 "error(evaluation_error(int_overflow), Arithmetic error: integer overflow in multiplication)"
663 );
664
665 let mut m = machine();
669 let e = bin_str(&mut m, "+", make_int(INT_MAX), make_int(INT_MAX));
670 assert_eq!(eval(&mut m, e), Ok(ArithValue::Int(INT_MAX + INT_MAX)));
672 }
673
674 #[test]
675 fn err_type_evaluable_atom_and_compound() {
676 let mut m = machine();
677 let foo = m.atoms.intern("foo");
678 assert!(eval(&mut m, make_atom(foo)).is_err());
679 assert_eq!(
680 msg(&m),
681 "error(type_error(evaluable, foo), Cannot evaluate as arithmetic)"
682 );
683
684 let mut m = machine();
685 let e = un_str(&mut m, "foo", make_int(1));
686 assert!(eval(&mut m, e).is_err());
687 assert_eq!(
688 msg(&m),
689 "error(type_error(evaluable, /(foo, 1)), Unknown arithmetic operator: foo/1)"
690 );
691 }
692
693 #[test]
694 fn err_instantiation() {
695 let mut m = machine();
696 let v = m.new_var();
697 assert!(eval(&mut m, v).is_err());
698 let idx = payload(v);
700 assert_eq!(
701 msg(&m),
702 format!("error(instantiation_error, Arithmetic error: unbound variable _{idx})")
703 );
704 }
705
706 #[test]
707 fn err_nan_and_infinity() {
708 let mut m = machine();
710 let a = flt(&mut m, 0.0);
711 let b = flt(&mut m, 0.0);
712 let e = bin_str(&mut m, "/", a, b);
713 assert!(eval(&mut m, e).is_err());
714 assert_eq!(
715 msg(&m),
716 "error(evaluation_error(zero_divisor), Division by zero (float division))"
717 );
718
719 let mut m = machine();
722 let big = flt(&mut m, 1.0e308);
723 let ten = flt(&mut m, 10.0);
724 let e = bin_str(&mut m, "*", big, ten);
725 assert!(eval(&mut m, e).is_err());
726 assert_eq!(
727 msg(&m),
728 "error(evaluation_error(float_overflow), Arithmetic error: Infinity result)"
729 );
730
731 let mut m = machine();
733 let nan = flt(&mut m, f64::NAN);
734 let one = flt(&mut m, 1.0);
735 let e = bin_str(&mut m, "+", nan, one);
736 assert!(eval(&mut m, e).is_err());
737 assert_eq!(
738 msg(&m),
739 "error(evaluation_error(undefined), Arithmetic error: NaN result)"
740 );
741 }
742
743 #[test]
744 fn err_int_args_required() {
745 let mut m = machine();
746 let two = flt(&mut m, 2.0);
747 let e = bin_str(&mut m, "mod", make_int(5), two);
748 assert!(eval(&mut m, e).is_err());
749 assert_eq!(
750 msg(&m),
751 "error(type_error(integer, member), mod requires integer arguments)"
752 );
753 }
754
755 #[test]
756 fn err_shift_undefined() {
757 let mut m = machine();
758 let e = bin_str(&mut m, "<<", make_int(1), make_int(64));
759 assert!(eval(&mut m, e).is_err());
760 assert_eq!(
761 msg(&m),
762 "error(evaluation_error(undefined), Shift << requires a non-negative count in [0, 64))"
763 );
764 }
765
766 #[test]
767 fn mixed_comparison_helpers() {
768 assert!(arith_eq(ArithValue::Int(1), ArithValue::Float(1.0)));
770 assert!(!arith_lt(ArithValue::Float(1.0), ArithValue::Int(1)));
771 assert!(arith_lt(ArithValue::Int(1), ArithValue::Int(2)));
772 assert!(arith_gt(ArithValue::Float(2.0), ArithValue::Int(1)));
773 }
774}