ctcore/primitive/
fixed_integer.rs

1use crate::{
2    build::{Builder, Error, Target},
3    text::{Digit, NonZeroDigit, Printable},
4};
5
6use super::Natural;
7
8#[derive(Debug, PartialEq)]
9pub enum FixedInteger<const I: usize> {
10    Zero,
11    Positive(Natural),
12    Negative(Natural),
13}
14
15impl<const I: usize> FixedInteger<I> {
16    pub fn start() -> impl Builder<Product = FixedInteger<I>> {
17        FixedIntegerBuilder::<I>::Pad(0)
18    }
19
20    pub fn from_int(int: i32) -> Option<Self> {
21        if int < 0 {
22            let natural = Natural::from_int(int.abs() as u32)?;
23
24            if 1 + natural.len() > I {
25                None
26            } else {
27                Some(Self::Negative(natural))
28            }
29        } else if int == 0 {
30            Some(Self::Zero)
31        } else {
32            let natural = Natural::from_int(int as u32)?;
33
34            if natural.len() > I {
35                None
36            } else {
37                Some(Self::Positive(natural))
38            }
39        }
40    }
41}
42
43#[derive(Debug, PartialEq)]
44pub enum FixedIntegerBuilder<const I: usize> {
45    Pad(usize),
46    Minus(usize),
47    Negative(usize, Natural),
48    Positive(usize, Natural),
49}
50
51impl<const I: usize> FixedIntegerBuilder<I> {
52    pub fn new() -> Self {
53        Self::Pad(0)
54    }
55}
56
57impl<const I: usize> Builder for FixedIntegerBuilder<I> {
58    type Product = FixedInteger<I>;
59
60    fn push(
61        self,
62        printable: Printable,
63    ) -> Result<Target<Self::Product, Self>, Error> {
64        match self {
65            Self::Pad(padding) => {
66                if padding + 1 == I {
67                    match printable {
68                        Printable::D0 => {
69                            Ok(Target::Product(FixedInteger::Zero))
70                        }
71                        printable => {
72                            match NonZeroDigit::from_printable(printable) {
73                                Some(non_zero) => {
74                                    Ok(Target::Product(FixedInteger::Positive(
75                                        Natural::new(non_zero),
76                                    )))
77                                }
78                                None => Err(Error::digit()),
79                            }
80                        }
81                    }
82                } else {
83                    match printable {
84                        Printable::Space => {
85                            Ok(Target::Builder(Self::Pad(padding + 1)))
86                        }
87                        Printable::Minus => {
88                            Ok(Target::Builder(Self::Minus(padding)))
89                        }
90                        printable => {
91                            match NonZeroDigit::from_printable(printable) {
92                                Some(non_zero) => Ok(Target::Builder(
93                                    FixedIntegerBuilder::Positive(
94                                        padding,
95                                        Natural::new(non_zero),
96                                    ),
97                                )),
98                                None => Err(Error::integer_leading()),
99                            }
100                        }
101                    }
102                }
103            }
104            Self::Minus(padding) => {
105                match NonZeroDigit::from_printable(printable) {
106                    Some(non_zero) => {
107                        let natural = Natural::new(non_zero);
108
109                        if padding + 2 == I {
110                            Ok(Target::Product(FixedInteger::Negative(natural)))
111                        } else {
112                            Ok(Target::Builder(Self::Negative(
113                                padding, natural,
114                            )))
115                        }
116                    }
117                    None => Err(Error::non_zero_digit()),
118                }
119            }
120            Self::Negative(padding, mut natural) => {
121                match Digit::from_printable(printable) {
122                    Some(digit) => {
123                        natural.push(digit);
124                        if 1 + padding + natural.len() == I {
125                            Ok(Target::Product(FixedInteger::Negative(natural)))
126                        } else {
127                            Ok(Target::Builder(Self::Negative(
128                                padding, natural,
129                            )))
130                        }
131                    }
132                    None => Err(Error::digit()),
133                }
134            }
135            Self::Positive(padding, mut natural) => {
136                match Digit::from_printable(printable) {
137                    Some(digit) => {
138                        natural.push(digit);
139
140                        if padding + natural.len() == I {
141                            Ok(Target::Product(FixedInteger::Positive(natural)))
142                        } else {
143                            Ok(Target::Builder(Self::Positive(
144                                padding, natural,
145                            )))
146                        }
147                    }
148                    None => Err(Error::digit()),
149                }
150            }
151        }
152    }
153
154    fn done(self) -> Option<Self::Product> {
155        match self {
156            Self::Negative(padding, natural) => {
157                if padding + 1 + natural.len() == I {
158                    Some(FixedInteger::Negative(natural))
159                } else {
160                    None
161                }
162            }
163            Self::Positive(padding, natural) => {
164                if padding + natural.len() == I {
165                    Some(FixedInteger::Positive(natural))
166                } else {
167                    None
168                }
169            }
170            _ => None,
171        }
172    }
173}
174
175#[cfg(test)]
176mod from_int {
177    use super::*;
178    use pretty_assertions::assert_eq;
179
180    #[test]
181    fn zero() {
182        assert_eq!(FixedInteger::<2>::from_int(0), Some(FixedInteger::Zero))
183    }
184
185    #[test]
186    fn negative_over_limit() {
187        assert_eq!(FixedInteger::<2>::from_int(-42), None)
188    }
189
190    #[test]
191    fn negative_at_limit() {
192        assert_eq!(
193            FixedInteger::<3>::from_int(-42),
194            Some(FixedInteger::Negative(Natural::from_int(42).unwrap()))
195        )
196    }
197
198    #[test]
199    fn positive_at_limit() {
200        assert_eq!(
201            FixedInteger::<2>::from_int(42),
202            Some(FixedInteger::Positive(Natural::from_int(42).unwrap()))
203        )
204    }
205
206    #[test]
207    fn positive_over_limit() {
208        assert_eq!(FixedInteger::<1>::from_int(42), None)
209    }
210}
211
212#[cfg(test)]
213mod builder_push {
214    use super::*;
215    use pretty_assertions::assert_eq;
216
217    #[test]
218    fn pad_limit_space() {
219        let builder = FixedIntegerBuilder::<1>::Pad(0);
220
221        assert_eq!(builder.push(Printable::Space), Err(Error::digit()))
222    }
223
224    #[test]
225    fn pad_limit_zero() {
226        let builder = FixedIntegerBuilder::<1>::Pad(0);
227
228        assert_eq!(
229            builder.push(Printable::D0),
230            Ok(Target::Product(FixedInteger::<1>::Zero))
231        )
232    }
233
234    #[test]
235    fn pad_limit_non_zero() {
236        let builder = FixedIntegerBuilder::<1>::Pad(0);
237
238        assert_eq!(
239            builder.push(Printable::D1),
240            Ok(Target::Product(FixedInteger::<1>::Positive(Natural::new(
241                NonZeroDigit::D1
242            ))))
243        )
244    }
245
246    #[test]
247    fn pad_not_limit_space() {
248        let builder = FixedIntegerBuilder::<2>::Pad(0);
249
250        assert_eq!(
251            builder.push(Printable::Space),
252            Ok(Target::Builder(FixedIntegerBuilder::<2>::Pad(1)))
253        )
254    }
255
256    #[test]
257    fn pad_not_limit_minus() {
258        let builder = FixedIntegerBuilder::<2>::Pad(0);
259
260        assert_eq!(
261            builder.push(Printable::Minus),
262            Ok(Target::Builder(FixedIntegerBuilder::<2>::Minus(0)))
263        )
264    }
265
266    #[test]
267    fn pad_not_limit_zero() {
268        let builder = FixedIntegerBuilder::<2>::Pad(0);
269
270        assert_eq!(builder.push(Printable::D0), Err(Error::integer_leading()))
271    }
272
273    #[test]
274    fn pad_not_limit_non_zero() {
275        let builder = FixedIntegerBuilder::<2>::Pad(0);
276
277        assert_eq!(
278            builder.push(Printable::D1),
279            Ok(Target::Builder(FixedIntegerBuilder::<2>::Positive(
280                0,
281                Natural::new(NonZeroDigit::D1)
282            )))
283        )
284    }
285
286    #[test]
287    fn minus_minus() {
288        let builder = FixedIntegerBuilder::<2>::Minus(0);
289
290        assert_eq!(builder.push(Printable::Minus), Err(Error::non_zero_digit()))
291    }
292
293    #[test]
294    fn minus_zero() {
295        let builder = FixedIntegerBuilder::<2>::Minus(0);
296
297        assert_eq!(builder.push(Printable::Minus), Err(Error::non_zero_digit()))
298    }
299
300    #[test]
301    fn minus_not_limit_non_zero() {
302        let builder = FixedIntegerBuilder::<3>::Minus(0);
303
304        assert_eq!(
305            builder.push(Printable::D1),
306            Ok(Target::Builder(FixedIntegerBuilder::<3>::Negative(
307                0,
308                Natural::new(NonZeroDigit::D1)
309            )))
310        )
311    }
312
313    #[test]
314    fn minus_limit_non_zero() {
315        let builder = FixedIntegerBuilder::<2>::Minus(0);
316
317        assert_eq!(
318            builder.push(Printable::D1),
319            Ok(Target::Product(FixedInteger::Negative(Natural::new(
320                NonZeroDigit::D1
321            ))))
322        )
323    }
324
325    #[test]
326    fn negative_non_digit() {
327        let builder = FixedIntegerBuilder::<3>::Negative(
328            0,
329            Natural::new(NonZeroDigit::D1),
330        );
331
332        assert_eq!(builder.push(Printable::Space), Err(Error::digit()))
333    }
334
335    #[test]
336    fn negative_limit_digit() {
337        let builder = FixedIntegerBuilder::<3>::Negative(
338            0,
339            Natural::new(NonZeroDigit::D4),
340        );
341
342        assert_eq!(
343            builder.push(Printable::D2),
344            Ok(Target::Product(FixedInteger::Negative(
345                Natural::from_int(42).unwrap()
346            )))
347        )
348    }
349
350    #[test]
351    fn negative_not_limit_digit() {
352        let builder = FixedIntegerBuilder::<4>::Negative(
353            0,
354            Natural::new(NonZeroDigit::D4),
355        );
356
357        assert_eq!(
358            builder.push(Printable::D2),
359            Ok(Target::Builder(FixedIntegerBuilder::Negative(
360                0,
361                Natural::from_int(42).unwrap()
362            )))
363        )
364    }
365
366    #[test]
367    fn positive_non_digit() {
368        let builder = FixedIntegerBuilder::<2>::Positive(
369            0,
370            Natural::new(NonZeroDigit::D4),
371        );
372
373        assert_eq!(builder.push(Printable::Space), Err(Error::digit()))
374    }
375
376    #[test]
377    fn positive_limit_digit() {
378        let builder = FixedIntegerBuilder::<2>::Positive(
379            0,
380            Natural::new(NonZeroDigit::D4),
381        );
382
383        assert_eq!(
384            builder.push(Printable::D2),
385            Ok(Target::Product(FixedInteger::Positive(
386                Natural::from_int(42).unwrap()
387            )))
388        )
389    }
390
391    #[test]
392    fn positive_not_limit_digit() {
393        let builder = FixedIntegerBuilder::<3>::Positive(
394            0,
395            Natural::new(NonZeroDigit::D4),
396        );
397
398        assert_eq!(
399            builder.push(Printable::D2),
400            Ok(Target::Builder(FixedIntegerBuilder::Positive(
401                0,
402                Natural::from_int(42).unwrap()
403            )))
404        )
405    }
406}
407
408#[cfg(test)]
409mod builder_done {
410    use super::*;
411    use pretty_assertions::assert_eq;
412
413    #[test]
414    fn postitive_not_done() {
415        let builder = FixedIntegerBuilder::<3>::Positive(
416            0,
417            Natural::new(NonZeroDigit::D4),
418        );
419
420        assert_eq!(builder.done(), None)
421    }
422
423    #[test]
424    fn positive_done() {
425        let builder = FixedIntegerBuilder::<1>::Positive(
426            0,
427            Natural::new(NonZeroDigit::D1),
428        );
429
430        assert_eq!(
431            builder.done(),
432            Some(FixedInteger::Positive(Natural::from_int(1).unwrap()))
433        )
434    }
435
436    #[test]
437    fn negative_not_done() {
438        let builder = FixedIntegerBuilder::<3>::Negative(
439            0,
440            Natural::new(NonZeroDigit::D4),
441        );
442
443        assert_eq!(builder.done(), None)
444    }
445
446    #[test]
447    fn negative_done() {
448        let builder = FixedIntegerBuilder::<2>::Negative(
449            0,
450            Natural::new(NonZeroDigit::D1),
451        );
452
453        assert_eq!(
454            builder.done(),
455            Some(FixedInteger::Negative(Natural::from_int(1).unwrap()))
456        )
457    }
458
459    #[test]
460    fn positive_not_done() {
461        let builder = FixedIntegerBuilder::<3>::Positive(
462            0,
463            Natural::new(NonZeroDigit::D4),
464        );
465
466        assert_eq!(builder.done(), None)
467    }
468}