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}