ctcore/primitive/
fixed_real.rs1use 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}