Skip to main content

jay/
dtype.rs

1//! Element types. The set is deliberately small; nothing here may assume it
2//! stays small.
3
4#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5pub enum DType {
6    Bool,
7    I64,
8    /// An arbitrary-precision integer (J's "extended", `123x`).
9    Ext,
10    /// An exact ratio of two arbitrary-precision integers (J `1r3`).
11    Rat,
12    F64,
13    /// A complex number, held as an interleaved `[re, im]` pair.
14    Complex,
15    Char,
16    /// A box: every element is itself an array (J `<`, APL `⊂`).
17    Box,
18}
19
20impl DType {
21    pub fn name(self) -> &'static str {
22        match self {
23            DType::Bool => "boolean",
24            DType::I64 => "integer",
25            DType::Ext => "extended",
26            DType::Rat => "rational",
27            DType::F64 => "float",
28            DType::Complex => "complex",
29            DType::Char => "character",
30            DType::Box => "boxed",
31        }
32    }
33
34    pub fn is_numeric(self) -> bool {
35        matches!(
36            self,
37            DType::Bool | DType::I64 | DType::Ext | DType::Rat | DType::F64 | DType::Complex
38        )
39    }
40
41    /// True for the two types that never round.
42    pub fn is_exact(self) -> bool {
43        matches!(self, DType::Ext | DType::Rat)
44    }
45
46    /// Common type two numeric operands widen to. None if incompatible.
47    ///
48    /// The order is J's numeric tower: an exact type sits above the machine
49    /// integers and below the floats, so `1x + 1r2` stays exact while
50    /// `1x + 1.5` rounds.
51    pub fn promote(a: DType, b: DType) -> Option<DType> {
52        use DType::*;
53        match (a, b) {
54            (Box, Box) => Some(Box),
55            (Box, _) | (_, Box) => None,
56            (Char, Char) => Some(Char),
57            (Char, _) | (_, Char) => None,
58            (Complex, _) | (_, Complex) => Some(Complex),
59            (F64, _) | (_, F64) => Some(F64),
60            (Rat, _) | (_, Rat) => Some(Rat),
61            (Ext, _) | (_, Ext) => Some(Ext),
62            (I64, _) | (_, I64) => Some(I64),
63            (Bool, Bool) => Some(Bool),
64        }
65    }
66}
67
68#[cfg(test)]
69mod tests {
70    use super::DType::*;
71
72    #[test]
73    fn the_numeric_tower_climbs_bool_integer_extended_rational_float_complex() {
74        let tower = [Bool, I64, Ext, Rat, F64, Complex];
75        for (i, &a) in tower.iter().enumerate() {
76            for (j, &b) in tower.iter().enumerate() {
77                let want = tower[i.max(j)];
78                // Two booleans are the one pair that stays boolean.
79                assert_eq!(super::DType::promote(a, b), Some(want), "{a:?} with {b:?}");
80            }
81        }
82    }
83
84    #[test]
85    fn characters_and_boxes_mix_with_nothing() {
86        assert_eq!(super::DType::promote(Char, Ext), None);
87        assert_eq!(super::DType::promote(Box, Rat), None);
88    }
89}