1use super::{Integer, IntegerRef, Rational, RationalRef};
21use num::traits::{One, Zero};
22use num::Integer as NumInteger;
23use std::fmt::{Debug, Display};
24use std::iter::{Product, Sum};
25use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
26
27#[derive(Clone, Copy, PartialEq, Eq, PartialOrd)]
30pub struct Integer64(i64);
31
32impl Debug for Integer64 {
33 #[inline(always)]
34 fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
35 write!(f, "{}", self.0)
36 }
37}
38
39#[cfg(test)]
40impl Display for Integer64 {
41 fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
42 write!(f, "{}", self.0)
43 }
44}
45
46impl Zero for Integer64 {
47 #[inline(always)]
48 fn zero() -> Self {
49 Integer64(i64::zero())
50 }
51 #[inline(always)]
52 fn is_zero(&self) -> bool {
53 self.0.is_zero()
54 }
55}
56impl One for Integer64 {
57 #[inline(always)]
58 fn one() -> Self {
59 Integer64(1)
60 }
61}
62
63impl Add for Integer64 {
64 type Output = Self;
65 #[inline(always)]
66 fn add(self, rhs: Self) -> Self {
67 #[cfg(feature = "checked_i64")]
68 return Integer64(self.0.checked_add(rhs.0).unwrap());
69 #[cfg(not(feature = "checked_i64"))]
70 return Integer64(self.0 + rhs.0);
71 }
72}
73impl Sub for Integer64 {
74 type Output = Self;
75 #[inline(always)]
76 fn sub(self, rhs: Self) -> Self {
77 #[cfg(feature = "checked_i64")]
78 return Integer64(self.0.checked_sub(rhs.0).unwrap());
79 #[cfg(not(feature = "checked_i64"))]
80 return Integer64(self.0 - rhs.0);
81 }
82}
83impl Mul for Integer64 {
84 type Output = Self;
85 #[inline(always)]
86 fn mul(self, rhs: Self) -> Self {
87 #[cfg(feature = "checked_i64")]
88 return Integer64(self.0.checked_mul(rhs.0).unwrap());
89 #[cfg(not(feature = "checked_i64"))]
90 return Integer64(self.0 * rhs.0);
91 }
92}
93
94impl Add<&'_ Integer64> for &'_ Integer64 {
95 type Output = Integer64;
96 #[inline(always)]
97 fn add(self, rhs: &'_ Integer64) -> Integer64 {
98 *self + *rhs
99 }
100}
101impl Sub<&'_ Integer64> for &'_ Integer64 {
102 type Output = Integer64;
103 #[inline(always)]
104 fn sub(self, rhs: &'_ Integer64) -> Integer64 {
105 *self - *rhs
106 }
107}
108impl Mul<&'_ Integer64> for &'_ Integer64 {
109 type Output = Integer64;
110 #[inline(always)]
111 fn mul(self, rhs: &'_ Integer64) -> Integer64 {
112 *self * *rhs
113 }
114}
115
116impl Product for Integer64 {
117 #[inline(always)]
118 fn product<I>(iter: I) -> Self
119 where
120 I: Iterator<Item = Self>,
121 {
122 iter.fold(Self::one(), |acc, x| acc * x)
123 }
124}
125
126impl Integer for Integer64 {
127 #[inline(always)]
128 fn from_usize(i: usize) -> Self {
129 #[cfg(feature = "checked_i64")]
130 return Integer64(i.try_into().unwrap());
131 #[cfg(not(feature = "checked_i64"))]
132 return Integer64(i as i64);
133 }
134
135 #[cfg(test)]
136 fn get_positive_test_values() -> Vec<Self> {
137 let mut result = Vec::new();
138 for i in 0..=30 {
139 result.push(Integer64(1 << i));
140 result.push(Integer64(0x7FFF_FFFF ^ (1 << i)));
141 }
142 result
143 }
144}
145
146impl IntegerRef<Integer64> for &Integer64 {}
147
148#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd)]
151pub struct FixedDecimal9(i64);
152
153impl FixedDecimal9 {
154 const FACTOR: i64 = 1_000_000_000;
155 const FACTOR_I128: i128 = Self::FACTOR as i128;
156}
157
158#[cfg(test)]
159impl FixedDecimal9 {
160 #[inline(always)]
161 pub(crate) fn new(x: i64) -> Self {
162 FixedDecimal9(x)
163 }
164
165 fn from_i64(x: i64) -> Self {
166 FixedDecimal9::from_int(Integer64(x))
167 }
168}
169
170impl Display for FixedDecimal9 {
171 #[inline(always)]
172 fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
173 let sign = if self.0 < 0 { "-" } else { "" };
174 let (i, rem) = self.0.abs().div_rem(&Self::FACTOR);
175 write!(f, "{sign}{i}.{rem:09}")
176 }
177}
178
179impl Zero for FixedDecimal9 {
180 #[inline(always)]
181 fn zero() -> Self {
182 FixedDecimal9(i64::zero())
183 }
184 #[inline(always)]
185 fn is_zero(&self) -> bool {
186 self.0.is_zero()
187 }
188}
189impl One for FixedDecimal9 {
190 #[inline(always)]
191 fn one() -> Self {
192 FixedDecimal9(Self::FACTOR)
193 }
194}
195
196impl Add for FixedDecimal9 {
197 type Output = Self;
198 #[inline(always)]
199 fn add(self, rhs: Self) -> Self {
200 #[cfg(feature = "checked_i64")]
201 return FixedDecimal9(self.0.checked_add(rhs.0).unwrap());
202 #[cfg(not(feature = "checked_i64"))]
203 return FixedDecimal9(self.0 + rhs.0);
204 }
205}
206impl Sub for FixedDecimal9 {
207 type Output = Self;
208 #[inline(always)]
209 fn sub(self, rhs: Self) -> Self {
210 #[cfg(feature = "checked_i64")]
211 return FixedDecimal9(self.0.checked_sub(rhs.0).unwrap());
212 #[cfg(not(feature = "checked_i64"))]
213 return FixedDecimal9(self.0 - rhs.0);
214 }
215}
216impl Mul for FixedDecimal9 {
217 type Output = Self;
218 #[inline(always)]
219 fn mul(self, rhs: Self) -> Self {
220 FixedDecimal9(match self.0.checked_mul(rhs.0) {
221 Some(product) => product / Self::FACTOR,
222 None => {
223 let result: i128 = (self.0 as i128 * rhs.0 as i128) / Self::FACTOR_I128;
224 #[cfg(feature = "checked_i64")]
225 let result: i64 = result.try_into().unwrap();
226 #[cfg(not(feature = "checked_i64"))]
227 let result: i64 = result as i64;
228 result
229 }
230 })
231 }
232}
233impl Mul<Integer64> for FixedDecimal9 {
234 type Output = Self;
235 #[inline(always)]
236 fn mul(self, rhs: Integer64) -> Self {
237 #[cfg(feature = "checked_i64")]
238 return FixedDecimal9(self.0.checked_mul(rhs.0).unwrap());
239 #[cfg(not(feature = "checked_i64"))]
240 return FixedDecimal9(self.0 * rhs.0);
241 }
242}
243impl Div<Integer64> for FixedDecimal9 {
244 type Output = Self;
245 #[inline(always)]
246 fn div(self, rhs: Integer64) -> Self {
247 #[cfg(feature = "checked_i64")]
248 return FixedDecimal9(self.0.checked_div(rhs.0).unwrap());
249 #[cfg(not(feature = "checked_i64"))]
250 return FixedDecimal9(self.0 / rhs.0);
251 }
252}
253
254impl Add<&'_ Self> for FixedDecimal9 {
255 type Output = Self;
256 #[inline(always)]
257 fn add(self, rhs: &'_ Self) -> Self {
258 self + *rhs
259 }
260}
261impl Sub<&'_ Self> for FixedDecimal9 {
262 type Output = Self;
263 #[inline(always)]
264 fn sub(self, rhs: &'_ Self) -> Self {
265 self - *rhs
266 }
267}
268impl Mul<&'_ Self> for FixedDecimal9 {
269 type Output = Self;
270 #[inline(always)]
271 fn mul(self, rhs: &'_ Self) -> Self {
272 self * *rhs
273 }
274}
275
276impl Add<&'_ FixedDecimal9> for &'_ FixedDecimal9 {
277 type Output = FixedDecimal9;
278 #[inline(always)]
279 fn add(self, rhs: &'_ FixedDecimal9) -> FixedDecimal9 {
280 *self + *rhs
281 }
282}
283impl Sub<&'_ FixedDecimal9> for &'_ FixedDecimal9 {
284 type Output = FixedDecimal9;
285 #[inline(always)]
286 fn sub(self, rhs: &'_ FixedDecimal9) -> FixedDecimal9 {
287 *self - *rhs
288 }
289}
290impl Mul<&'_ FixedDecimal9> for &'_ FixedDecimal9 {
291 type Output = FixedDecimal9;
292 #[inline(always)]
293 fn mul(self, rhs: &'_ FixedDecimal9) -> FixedDecimal9 {
294 *self * *rhs
295 }
296}
297impl Mul<&'_ Integer64> for &'_ FixedDecimal9 {
298 type Output = FixedDecimal9;
299 #[inline(always)]
300 fn mul(self, rhs: &'_ Integer64) -> FixedDecimal9 {
301 *self * *rhs
302 }
303}
304impl Div<&'_ Integer64> for &'_ FixedDecimal9 {
305 type Output = FixedDecimal9;
306 #[inline(always)]
307 fn div(self, rhs: &'_ Integer64) -> FixedDecimal9 {
308 *self / *rhs
309 }
310}
311
312impl AddAssign for FixedDecimal9 {
313 #[inline(always)]
314 fn add_assign(&mut self, rhs: Self) {
315 *self = *self + rhs
316 }
317}
318impl SubAssign for FixedDecimal9 {
319 #[inline(always)]
320 fn sub_assign(&mut self, rhs: Self) {
321 *self = *self - rhs
322 }
323}
324impl MulAssign for FixedDecimal9 {
325 #[inline(always)]
326 fn mul_assign(&mut self, rhs: Self) {
327 *self = *self * rhs
328 }
329}
330
331impl AddAssign<&'_ Self> for FixedDecimal9 {
332 #[inline(always)]
333 fn add_assign(&mut self, rhs: &'_ Self) {
334 *self = *self + *rhs
335 }
336}
337impl SubAssign<&'_ Self> for FixedDecimal9 {
338 #[inline(always)]
339 fn sub_assign(&mut self, rhs: &'_ Self) {
340 *self = *self - *rhs
341 }
342}
343impl MulAssign<&'_ Self> for FixedDecimal9 {
344 #[inline(always)]
345 fn mul_assign(&mut self, rhs: &'_ Self) {
346 *self = *self * *rhs
347 }
348}
349impl DivAssign<&'_ Integer64> for FixedDecimal9 {
350 #[inline(always)]
351 fn div_assign(&mut self, rhs: &'_ Integer64) {
352 *self = *self / *rhs
353 }
354}
355
356impl Sum for FixedDecimal9 {
357 #[inline(always)]
358 fn sum<I>(iter: I) -> Self
359 where
360 I: Iterator<Item = Self>,
361 {
362 FixedDecimal9(iter.map(|item| item.0).sum())
363 }
364}
365
366impl Product for FixedDecimal9 {
367 #[inline(always)]
368 fn product<I>(iter: I) -> Self
369 where
370 I: Iterator<Item = Self>,
371 {
372 iter.fold(Self::one(), |acc, x| acc * x)
373 }
374}
375
376impl<'a> Sum<&'a Self> for FixedDecimal9 {
377 #[inline(always)]
378 fn sum<I>(iter: I) -> Self
379 where
380 I: Iterator<Item = &'a Self>,
381 {
382 FixedDecimal9(iter.map(|item| &item.0).sum())
383 }
384}
385
386impl RationalRef<&Integer64, FixedDecimal9> for &FixedDecimal9 {}
387
388impl Rational<Integer64> for FixedDecimal9 {
389 #[inline(always)]
390 fn from_int(i: Integer64) -> Self {
391 #[cfg(feature = "checked_i64")]
392 return FixedDecimal9(i.0.checked_mul(Self::FACTOR).unwrap());
393 #[cfg(not(feature = "checked_i64"))]
394 return FixedDecimal9(i.0 * Self::FACTOR);
395 }
396
397 #[inline(always)]
398 fn ratio_i(num: Integer64, denom: Integer64) -> Self {
399 FixedDecimal9(match num.0.checked_mul(Self::FACTOR) {
400 Some(product) => {
401 #[cfg(feature = "checked_i64")]
402 let result: i64 = product.checked_div(denom.0).unwrap();
403 #[cfg(not(feature = "checked_i64"))]
404 let result: i64 = product / denom.0;
405 result
406 }
407 None => {
408 let product: i128 = num.0 as i128 * Self::FACTOR_I128;
409 #[cfg(feature = "checked_i64")]
410 let result: i64 = product
411 .checked_div(denom.0 as i128)
412 .unwrap()
413 .try_into()
414 .unwrap();
415 #[cfg(not(feature = "checked_i64"))]
416 let result: i64 = (product / denom.0 as i128) as i64;
417 result
418 }
419 })
420 }
421
422 #[inline(always)]
423 fn to_f64(&self) -> f64 {
424 self.0 as f64 / Self::FACTOR as f64
425 }
426
427 #[inline(always)]
428 fn epsilon() -> Self {
429 FixedDecimal9(1)
430 }
431
432 #[inline(always)]
433 fn is_exact() -> bool {
434 false
435 }
436
437 #[inline(always)]
438 fn description() -> &'static str {
439 "fixed-point decimal arithmetic (9 places)"
440 }
441
442 #[inline(always)]
443 fn mul_up(&self, rhs: &Self) -> Self {
444 FixedDecimal9(
445 match self
446 .0
447 .checked_mul(rhs.0)
448 .and_then(|product| product.checked_add(Self::FACTOR - 1))
449 {
450 Some(value) => value / Self::FACTOR,
451 None => {
452 let product: i128 = self.0 as i128 * rhs.0 as i128;
453 #[cfg(feature = "checked_i64")]
454 let result: i64 = (product.checked_add(Self::FACTOR_I128 - 1).unwrap()
455 / Self::FACTOR_I128)
456 .try_into()
457 .unwrap();
458 #[cfg(not(feature = "checked_i64"))]
459 let result: i64 =
460 ((product + Self::FACTOR_I128 - 1) / Self::FACTOR_I128) as i64;
461 result
462 }
463 },
464 )
465 }
466
467 #[inline(always)]
468 fn div_up_as_keep_factor(&self, rhs: &Self) -> Self {
469 FixedDecimal9(
470 match self
471 .0
472 .checked_mul(Self::FACTOR)
473 .and_then(|product| product.checked_add(rhs.0 - 1))
474 {
475 Some(value) => {
476 #[cfg(feature = "checked_i64")]
477 let result: i64 = value.checked_div(rhs.0).unwrap();
478 #[cfg(not(feature = "checked_i64"))]
479 let result: i64 = value / rhs.0;
480 result
481 }
482 None => {
483 let value: i128 = self.0 as i128 * Self::FACTOR_I128 + rhs.0 as i128 - 1;
484 #[cfg(feature = "checked_i64")]
485 let result: i64 = value
486 .checked_div(rhs.0 as i128)
487 .unwrap()
488 .try_into()
489 .unwrap();
490 #[cfg(not(feature = "checked_i64"))]
491 let result: i64 = (value / rhs.0 as i128) as i64;
492 result
493 }
494 },
495 )
496 }
497
498 #[cfg(test)]
499 fn get_positive_test_values() -> Vec<Self> {
500 let mut result = Vec::new();
501 for i in 0..=30 {
502 result.push(FixedDecimal9(1 << i));
503 }
504 for i in 0..=30 {
505 result.push(FixedDecimal9(0x7FFF_FFFF - (1 << i)));
506 }
507 result
508 }
509}
510
511#[cfg(test)]
512mod test {
513 use super::*;
514 use crate::{
515 big_integer_tests, big_numeric_tests, integer_tests, numeric_benchmarks, numeric_tests,
516 };
517
518 integer_tests!(
519 Integer64,
520 testi_values_are_positive,
521 testi_is_zero,
522 testi_zero_is_add_neutral,
523 testi_add_is_commutative,
524 testi_opposite,
525 testi_sub_self,
526 testi_add_sub,
527 testi_sub_add,
528 testi_one_is_mul_neutral,
529 testi_mul_is_commutative,
530 );
531
532 #[cfg(not(any(feature = "checked_i64", overflow_checks)))]
533 big_integer_tests!(
534 Integer64,
535 None,
536 testi_add_is_associative,
537 testi_mul_is_associative,
538 testi_mul_is_distributive,
539 );
540 #[cfg(feature = "checked_i64")]
541 big_integer_tests!(
542 Integer64,
543 None,
544 testi_add_is_associative,
545 testi_mul_is_associative => fail(r"called `Option::unwrap()` on a `None` value"),
546 testi_mul_is_distributive,
547 testi_product => fail(r"called `Option::unwrap()` on a `None` value"),
548 );
549 #[cfg(all(not(feature = "checked_i64"), overflow_checks))]
550 big_integer_tests!(
551 Integer64,
552 None,
553 testi_add_is_associative,
554 testi_mul_is_associative => fail(r"attempt to multiply with overflow"),
555 testi_mul_is_distributive,
556 testi_product => fail(r"attempt to multiply with overflow"),
557 );
558
559 numeric_tests!(
560 Integer64,
561 FixedDecimal9,
562 test_values_are_positive,
563 test_is_exact,
564 test_ratio,
565 test_ratio_invert => fail(r"assertion `left == right` failed: R::ratio(1, a) * a != 1 for 3
566 left: FixedDecimal9(999999999)
567 right: FixedDecimal9(1000000000)"),
568 test_is_zero,
569 test_zero_is_add_neutral,
570 test_add_is_commutative,
571 test_opposite,
572 test_sub_self,
573 test_add_sub,
574 test_sub_add,
575 test_one_is_mul_neutral,
576 test_mul_is_commutative,
577 test_mul_up_is_commutative,
578 test_mul_up_integers,
579 test_mul_up_wrt_mul,
580 test_one_is_div_up_neutral,
581 test_div_up_self,
582 test_mul_div_up => fail(r"assertion `left == right` failed: div_up(a * b, b) != a for 0.000000001, 0.000000001
583 left: FixedDecimal9(0)
584 right: FixedDecimal9(1)"),
585 test_mul_by_int,
586 test_mul_div_by_int,
587 test_references,
588 test_assign,
589 );
590
591 big_numeric_tests!(
592 Integer64,
593 FixedDecimal9,
594 None,
595 test_add_is_associative,
596 test_mul_is_associative => fail(r"assertion `left == right` failed: (a * b) * c != a * (b * c) for 0.000000001, 0.536870912, 2.147483646
597 left: FixedDecimal9(0)
598 right: FixedDecimal9(1)"),
599 test_mul_is_distributive => fail(r"assertion `left == right` failed: a * (b + c) != (a * b) + (a * c) for 0.000000001, 0.134217728, 1.879048191
600 left: FixedDecimal9(2)
601 right: FixedDecimal9(1)"),
602 test_mul_by_int_is_associative,
603 test_mul_by_int_is_distributive,
604 test_div_by_int_is_associative,
605 test_div_by_int_is_distributive => fail(r"assertion `left == right` failed: (a + b) / c != (a / c) + (b / c) for 0.000000001, 0.000000001, 2
606 left: FixedDecimal9(1)
607 right: FixedDecimal9(0)"),
608 test_sum,
609 test_product,
610 );
611
612 numeric_benchmarks!(
613 Integer64,
614 FixedDecimal9,
615 bench_add,
616 bench_sub,
617 bench_mul,
618 bench_div_up,
619 );
620
621 #[test]
622 fn test_description() {
623 assert_eq!(
624 FixedDecimal9::description(),
625 "fixed-point decimal arithmetic (9 places)"
626 );
627 }
628
629 #[test]
630 fn test_display() {
631 assert_eq!(format!("{}", FixedDecimal9::zero()), "0.000000000");
632 assert_eq!(format!("{}", FixedDecimal9::one()), "1.000000000");
633 assert_eq!(format!("{}", FixedDecimal9(0)), "0.000000000");
634 assert_eq!(format!("{}", FixedDecimal9(1)), "0.000000001");
635 assert_eq!(format!("{}", FixedDecimal9(1_000_000_000)), "1.000000000");
636 assert_eq!(format!("{}", FixedDecimal9(1_234_567_890)), "1.234567890");
637 assert_eq!(format!("{}", FixedDecimal9(-1)), "-0.000000001");
638 assert_eq!(format!("{}", FixedDecimal9(-1_000_000_000)), "-1.000000000");
639 }
640
641 #[test]
642 fn test_display_test_values() {
643 #[rustfmt::skip]
644 let expected_displays = [
645 "0.000000001", "0.000000002", "0.000000004", "0.000000008",
646 "0.000000016", "0.000000032", "0.000000064", "0.000000128",
647 "0.000000256", "0.000000512", "0.000001024", "0.000002048",
648 "0.000004096", "0.000008192", "0.000016384", "0.000032768",
649 "0.000065536", "0.000131072", "0.000262144", "0.000524288",
650 "0.001048576", "0.002097152", "0.004194304", "0.008388608",
651 "0.016777216", "0.033554432", "0.067108864", "0.134217728",
652 "0.268435456", "0.536870912", "1.073741824",
653 "2.147483646", "2.147483645", "2.147483643", "2.147483639",
654 "2.147483631", "2.147483615", "2.147483583", "2.147483519",
655 "2.147483391", "2.147483135", "2.147482623", "2.147481599",
656 "2.147479551", "2.147475455", "2.147467263", "2.147450879",
657 "2.147418111", "2.147352575", "2.147221503", "2.146959359",
658 "2.146435071", "2.145386495", "2.143289343", "2.139095039",
659 "2.130706431", "2.113929215", "2.080374783", "2.013265919",
660 "1.879048191", "1.610612735", "1.073741823",
661 ];
662 let actual_displays: Vec<String> = FixedDecimal9::get_positive_test_values()
663 .iter()
664 .map(|x| format!("{x}"))
665 .collect();
666 assert_eq!(actual_displays, expected_displays);
667 }
668
669 #[test]
670 fn test_intermediate_overflow() {
671 assert_eq!(
674 FixedDecimal9::from_i64(1_000) * FixedDecimal9::from_i64(1_000),
675 FixedDecimal9::from_i64(1_000_000)
676 );
677 assert_eq!(
680 FixedDecimal9::from_i64(5) * FixedDecimal9::from_i64(2),
681 FixedDecimal9::from_i64(10)
682 );
683 assert_eq!(
685 FixedDecimal9::ratio(10_000_000_000, 2),
686 FixedDecimal9::from_i64(5_000_000_000)
687 );
688
689 let mut x = FixedDecimal9::from_i64(1_000);
691 x *= FixedDecimal9::from_i64(1_000);
692 assert_eq!(x, FixedDecimal9::from_i64(1_000_000));
693 }
694
695 #[cfg(feature = "checked_i64")]
696 #[test]
697 #[should_panic(expected = "called `Result::unwrap()` on an `Err` value: TryFromIntError(())")]
698 fn test_mul_overflow() {
699 let _ = FixedDecimal9::from_i64(1_000_000) * FixedDecimal9::from_i64(1_000_000);
701 }
702
703 #[cfg(feature = "checked_i64")]
704 #[test]
705 #[should_panic(expected = "called `Result::unwrap()` on an `Err` value: TryFromIntError(())")]
706 fn test_mul_assign_overflow() {
707 let mut x = FixedDecimal9::from_i64(1_000_000);
709 x *= FixedDecimal9::from_i64(1_000_000);
710 }
711
712 #[cfg(not(feature = "checked_i64"))]
713 #[test]
714 fn test_overflow() {
715 assert_eq!(
717 FixedDecimal9::from_i64(1_000_000) * FixedDecimal9::from_i64(1_000_000),
718 FixedDecimal9(3_875_820_019_684_212_736)
719 );
720
721 let mut x = FixedDecimal9::from_i64(1_000_000);
722 x *= FixedDecimal9::from_i64(1_000_000);
723 assert_eq!(x, FixedDecimal9(3_875_820_019_684_212_736));
724 }
725}