scratch_test_value/arithmetic/subtraction.rs
1use crate::{
2 QuirkSink, SNumber, SValue,
3 quirks::{SNumberToFloatQ, SValueToNumberQ},
4};
5
6/// A subtraction of two integers would overflow/underflow so they're treated as
7/// floating point numbers for subtraction, a float is then produces instead of an int.
8///
9/// Generated by [`SNumber::q_sub_numbers`]
10pub struct IntegerSubWouldFailQ(());
11
12impl SValue {
13 /// See [`SNumber::q_sub_numbers`]
14 pub fn q_sub_numbers<Q>(&self, other: &SValue, sink: &mut Q) -> SValue
15 where
16 Q: QuirkSink<SValueToNumberQ>
17 + QuirkSink<SNumberToFloatQ>
18 + QuirkSink<IntegerSubWouldFailQ>,
19 {
20 self.q_as_number(sink)
21 .q_sub_numbers(&other.q_as_number(sink), sink)
22 .svalue()
23 }
24}
25
26impl SNumber {
27 /// If both numbers are finite integers numbers are subtracted as integers
28 /// if the result fits into an [`i64`].
29 ///
30 /// Otherwise, the below table applys. p and q are arbitraty finite floats.
31 ///
32 /// (line - column is shown in cell)
33 ///
34 /// | `-` | | `NaN` | `+∞` | `-∞` | +q | -q | `+0` | `-0` |
35 /// | ----- | -- | ----- | ----- | ----- | ------- | ------- | ----- | ----- |
36 /// | | | | | | | | | |
37 /// | `NaN` | | `0` | `-∞` | `+∞` | -q | +q | `-0` | `+0` |
38 /// | `+∞` | | `+∞` | `NaN` | `+∞` | `+∞` | `+∞` | `+∞` | `-∞` |
39 /// | `-∞` | | `-∞` | `-∞` | `NaN` | `-∞` | `-∞` | `-∞` | `-∞` |
40 /// | +p | | +p | `-∞` | `+∞` | +p-(+q) | +p-(-q) | +p | +p |
41 /// | -p | | -p | `-∞` | `+∞` | -p-(+q) | -p-(-q) | -p | -p |
42 /// | `+0` | | `+0` | `-∞` | `+∞` | -q | +q | `0` | `0` |
43 /// | `-0` | | `-0` | `-∞` | `+∞` | -q | +q | `0` | `0` |
44 pub fn q_sub_numbers<Q>(&self, other: &SNumber, sink: &mut Q) -> SNumber
45 where
46 Q: QuirkSink<SNumberToFloatQ> + QuirkSink<IntegerSubWouldFailQ>,
47 {
48 if let (SNumber::Int(a), SNumber::Int(b)) = (self, other) {
49 if let Some(diff) = a.checked_sub(*b) {
50 return SNumber::Int(diff);
51 } else {
52 sink.put(IntegerSubWouldFailQ(()));
53 }
54 }
55
56 let a = self.q_as_float(sink);
57 let b = other.q_as_float(sink);
58
59 // NaN is treated as 0
60 if a.is_nan() {
61 return if b.is_nan() {
62 SNumber::Int(0)
63 } else {
64 SNumber::Float(-b)
65 };
66 }
67 if b.is_nan() {
68 return SNumber::Float(a);
69 }
70
71 SNumber::Float(a - b)
72 }
73}