ctcore/primitive/
fixed_real.rs

1use crate::{
2    build::{Builder, Error, Target},
3    text::{Digit, Printable},
4};
5
6use super::{fixed_integer::FixedIntegerBuilder, FixedInteger};
7
8#[derive(Debug, PartialEq)]
9pub struct FixedReal<const I: usize, const F: usize>(
10    FixedInteger<I>,
11    Vec<Digit>,
12);
13
14impl<const I: usize, const F: usize> FixedReal<I, F> {
15    pub fn start() -> impl Builder<Product = FixedReal<I, F>> {
16        FixedRealBuilder::IntegerPart(FixedIntegerBuilder::new())
17    }
18}
19
20#[derive(Debug, PartialEq)]
21enum FixedRealBuilder<const I: usize, const F: usize> {
22    IntegerPart(FixedIntegerBuilder<I>),
23    Decimal(FixedInteger<I>),
24    FractionalPart(FixedInteger<I>, Vec<Digit>),
25}
26
27impl<const I: usize, const F: usize> Builder for FixedRealBuilder<I, F> {
28    type Product = FixedReal<I, F>;
29
30    fn push(
31        self,
32        printable: Printable,
33    ) -> Result<Target<Self::Product, Self>, Error> {
34        match self {
35            Self::IntegerPart(builder) => match builder.push(printable)? {
36                Target::Builder(builder) => {
37                    Ok(Target::Builder(Self::IntegerPart(builder)))
38                }
39                Target::Product(fixed_integer) => {
40                    Ok(Target::Builder(Self::Decimal(fixed_integer)))
41                }
42            },
43            Self::Decimal(fixed_integer) => {
44                if printable == Printable::Dot {
45                    Ok(Target::Builder(Self::FractionalPart(
46                        fixed_integer,
47                        Vec::new(),
48                    )))
49                } else {
50                    Err(Error::Character(vec![Printable::Dot]))
51                }
52            }
53            Self::FractionalPart(fixed_integer, mut digits) => {
54                match Digit::from_printable(printable) {
55                    Some(digit) => {
56                        digits.push(digit);
57
58                        if digits.len() == F {
59                            Ok(Target::Product(FixedReal(
60                                fixed_integer,
61                                digits,
62                            )))
63                        } else {
64                            Ok(Target::Builder(Self::FractionalPart(
65                                fixed_integer,
66                                digits,
67                            )))
68                        }
69                    }
70                    None => Err(Error::digit()),
71                }
72            }
73        }
74    }
75
76    fn done(self) -> Option<Self::Product> {
77        match self {
78            Self::FractionalPart(fixed_integer, digits) => {
79                if digits.len() == F {
80                    Some(FixedReal(fixed_integer, digits))
81                } else {
82                    None
83                }
84            }
85            _ => None,
86        }
87    }
88}
89
90#[cfg(test)]
91mod builder_push {
92    use crate::primitive::Natural;
93    use pretty_assertions::assert_eq;
94
95    use super::*;
96
97    #[test]
98    fn integer_part_not_limit_non_zero() {
99        let builder = FixedRealBuilder::<2, 3>::IntegerPart(
100            FixedIntegerBuilder::<2>::Pad(0),
101        );
102
103        assert_eq!(
104            builder.push(Printable::D4),
105            Ok(Target::Builder(FixedRealBuilder::IntegerPart(
106                FixedIntegerBuilder::Positive(0, Natural::from_int(4).unwrap())
107            )))
108        )
109    }
110
111    #[test]
112    fn integer_part_limit_digit() {
113        let builder = FixedRealBuilder::<1, 3>::IntegerPart(
114            FixedIntegerBuilder::<1>::Pad(0),
115        );
116
117        assert_eq!(
118            builder.push(Printable::D4),
119            Ok(Target::Builder(FixedRealBuilder::Decimal(
120                FixedInteger::from_int(4).unwrap()
121            )))
122        )
123    }
124
125    #[test]
126    fn decimal_non_dot() {
127        let builder = FixedRealBuilder::<1, 3>::Decimal(
128            FixedInteger::from_int(2).unwrap(),
129        );
130
131        assert_eq!(
132            builder.push(Printable::Space),
133            Err(Error::Character(vec![Printable::Dot]))
134        )
135    }
136
137    #[test]
138    fn decimal_dot() {
139        let builder = FixedRealBuilder::<1, 3>::Decimal(
140            FixedInteger::from_int(2).unwrap(),
141        );
142
143        assert_eq!(
144            builder.push(Printable::Dot),
145            Ok(Target::Builder(FixedRealBuilder::FractionalPart(
146                FixedInteger::from_int(2).unwrap(),
147                vec![]
148            )))
149        )
150    }
151
152    #[test]
153    fn fractional_non_digit() {
154        let builder = FixedRealBuilder::<1, 3>::FractionalPart(
155            FixedInteger::from_int(2).unwrap(),
156            vec![],
157        );
158
159        assert_eq!(builder.push(Printable::X), Err(Error::digit()))
160    }
161
162    #[test]
163    fn fractional_limit_digit() {
164        let builder = FixedRealBuilder::<1, 1>::FractionalPart(
165            FixedInteger::from_int(4).unwrap(),
166            vec![],
167        );
168
169        assert_eq!(
170            builder.push(Printable::D2),
171            Ok(Target::Product(FixedReal(
172                FixedInteger::from_int(4).unwrap(),
173                vec![Digit::d2()]
174            )))
175        )
176    }
177
178    #[test]
179    fn fractional_not_limit_digit() {
180        let builder = FixedRealBuilder::<1, 2>::FractionalPart(
181            FixedInteger::from_int(4).unwrap(),
182            vec![],
183        );
184
185        assert_eq!(
186            builder.push(Printable::D2),
187            Ok(Target::Builder(FixedRealBuilder::FractionalPart(
188                FixedInteger::from_int(4).unwrap(),
189                vec![Digit::d2()]
190            )))
191        )
192    }
193}
194
195#[cfg(test)]
196mod builder_done {
197    use super::*;
198    use pretty_assertions::assert_eq;
199
200    #[test]
201    fn fractional_not_done() {
202        let builder = FixedRealBuilder::<1, 2>::FractionalPart(
203            FixedInteger::from_int(4).unwrap(),
204            vec![],
205        );
206
207        assert_eq!(builder.done(), None)
208    }
209
210    #[test]
211    fn fractional_done() {
212        let builder = FixedRealBuilder::<1, 1>::FractionalPart(
213            FixedInteger::from_int(4).unwrap(),
214            vec![Digit::d2()],
215        );
216
217        assert_eq!(
218            builder.done(),
219            Some(FixedReal(
220                FixedInteger::from_int(4).unwrap(),
221                vec![Digit::d2()]
222            ))
223        )
224    }
225
226    #[test]
227    fn non_fractional() {
228        let builder = FixedRealBuilder::<2, 3>::IntegerPart(
229            FixedIntegerBuilder::<2>::Pad(0),
230        );
231
232        assert_eq!(builder.done(), None)
233    }
234}