Skip to main content

chinese_numerals/
midscale.rs

1use crate::{characters::*, ChineseNumeralBase, MyriadScaleInt, Sign, Signed};
2
3/// Mid-scale integers (中数).
4///
5/// 「中数者,万万变之。若言万万曰亿,万万亿曰兆,万万兆曰京也。」
6#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug, Default)]
7pub struct MidScaleInt {
8    pub(super) sign: Sign,
9    pub(super) data: u128,
10}
11
12impl MidScaleInt {
13    /// Generates a new non-positive mid-scale integer from given absolute value.
14    ///
15    /// There is no way to generate Chinese numerals by `From` trait from negative primitive numbers less than [`i128::MIN`]. This associated function provides a way to generate them from the given absolute value less than or equal to [`u128::MAX`]. This crate also provides struct [`MidScaleBigInt`] for integers with absolute value larger than `u128::MAX`.
16    pub fn new_non_pos(abs: u128) -> Self {
17        if abs == 0 {
18            Self::default()
19        } else {
20            Self {
21                sign: Sign::Neg,
22                data: abs,
23            }
24        }
25    }
26}
27
28impl ChineseNumeralBase for MidScaleInt {
29    fn to_chars(&self) -> Vec<NumChar> {
30        let mut chars = Vec::new();
31        let mut num = *self.data();
32        let mut prev_rem = 1000_0000;
33
34        // u128 uses up to NUM_CHARS[17] = Gai (垓) for mid-scale numerals
35        for exp in 13..=17 {
36            let rem = num % 1_0000_0000;
37            num /= 1_0000_0000;
38
39            if rem > 0 {
40                if !chars.is_empty() && prev_rem < 1000_0000 {
41                    chars.push(NUM_CHARS[0]);
42                }
43                if exp > 13 {
44                    chars.push(NUM_CHARS[exp]);
45                }
46                let myriad = MyriadScaleInt::from(rem);
47                let mut node = myriad.to_chars();
48                chars.append(&mut node);
49            }
50            prev_rem = rem;
51        }
52        chars
53    }
54
55    fn to_chars_trimmed(&self) -> Vec<NumChar> {
56        let mut chars = self.to_chars();
57        let mut data = *self.data();
58        while data >= 1_0000 {
59            data /= 1_0000;
60        }
61        if data >= 10 && data <= 19 {
62            let one = chars.pop();
63            debug_assert_eq!(one, Some(NumChar::One));
64        }
65        chars
66    }
67}
68
69#[cfg(feature = "bigint")]
70use num_bigint::BigUint;
71#[cfg(feature = "bigint")]
72use num_integer::Integer;
73#[cfg(feature = "bigint")]
74use num_traits::{ToPrimitive, Zero};
75
76/// Mid-scale big integers (中数).
77///
78/// Use it by turning on feature "bigint". It uses [`BigUint`](num_bigint::BigUint) to store the absolute value. Therefore, all integers that can be expressed in mid-scale are included.
79#[cfg(feature = "bigint")]
80#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug, Default)]
81pub struct MidScaleBigInt {
82    pub(super) sign: Sign,
83    pub(super) data: BigUint,
84}
85
86#[cfg(feature = "bigint")]
87impl MidScaleBigInt {
88    pub(super) const MAX_ABS_ARR: &'static [u32] = &[
89        4294967295, 4294967295, 2701131775, 807615852, 3882706566, 3057181734, 745289159,
90        4056365773, 462339630, 20,
91    ];
92
93    /// The maximum integer can be expressed in mid-scale.
94    pub fn max_value() -> Self {
95        Self {
96            sign: Sign::Pos,
97            data: BigUint::from_slice(Self::MAX_ABS_ARR),
98        }
99    }
100
101    /// The minimum integer can be expressed in mid-scale.
102    pub fn min_value() -> Self {
103        Self {
104            sign: Sign::Neg,
105            data: BigUint::from_slice(Self::MAX_ABS_ARR),
106        }
107    }
108}
109
110#[cfg(feature = "bigint")]
111impl ChineseNumeralBase for MidScaleBigInt {
112    fn to_chars(&self) -> Vec<NumChar> {
113        let mut chars = Vec::new();
114        let mut num = self.data().to_owned();
115        let mut prev_rem = BigUint::new(vec![1000_0000]);
116        let lim = BigUint::new(vec![1000_0000]);
117        let div = BigUint::new(vec![1_0000_0000]);
118
119        for exp in 13..=23 {
120            let (_, rem) = num.div_rem(&div);
121            num /= &div;
122
123            if rem > BigUint::zero() {
124                if !chars.is_empty() && prev_rem < lim {
125                    chars.push(NUM_CHARS[0]);
126                }
127                if exp > 13 {
128                    chars.push(NUM_CHARS[exp]);
129                }
130                let rem = rem.to_u32().unwrap();
131                let myriad = MyriadScaleInt::from(rem);
132                let mut node = myriad.to_chars();
133                chars.append(&mut node);
134            }
135            prev_rem = rem;
136        }
137        chars
138    }
139
140    fn to_chars_trimmed(&self) -> Vec<NumChar> {
141        let mut chars = self.to_chars();
142        let mut data = self.data().to_owned();
143        let div = BigUint::new(vec![1_0000]);
144        let ten = BigUint::new(vec![10]);
145        let nineteen = BigUint::new(vec![19]);
146        while data >= div {
147            data /= &div;
148        }
149        if data >= ten && data <= nineteen {
150            let one = chars.pop();
151            debug_assert_eq!(one, Some(NumChar::One));
152        }
153        chars
154    }
155}