Skip to main content

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}