devela/num/frac/wrapper/
impl_frac.rs

1// devela::num::frac::wrapper::impl_frac
2//
3//! implements non-owning fraction-related methods
4//
5// TOC
6// - prim. array | Int array:
7//   - numerator | num
8//   - denominator | den
9//   - is_valid
10//   - is_proper
11//   - is_reduced
12//   - reduce
13//   - gcd
14//   - lcm
15//   - add
16
17#[cfg(doc)]
18use crate::NumError::{self, Overflow};
19#[cfg(_int··)]
20use crate::{Frac, Int, NumResult as Result};
21
22// $i:    the integer type.
23// $self: the fractional self type.
24// $fout: the fractionsl output type.
25// $cap:  the capability feature that enables the given implementation. E.g "_int_i8".
26macro_rules! impl_frac {
27    [] => {
28        impl_frac![
29            i8:"_int_i8", i16:"_int_i16", i32:"_int_i32",
30            i64:"_int_i64", i128:"_int_i128", isize:"_int_isize",
31            u8:"_int_u8", u16:"_int_u16", u32:"_int_u32",
32            u64:"_int_u64", u128:"_int_u128", usize:"_int_usize"
33        ];
34    };
35
36    ($( $i:ty : $cap:literal ),+ ) => {
37        $(
38            impl_frac![@array $i:$cap, [$i; 2], Frac<[$i; 2]>];
39            impl_frac![@int_array $i:$cap, [Int<$i>; 2], Frac<[Int<$i>; 2]>];
40            // impl_frac![@slice $i:$cap, &[$i], [$i; 2]]; // MAYBE
41        )+
42    };
43
44    // both for signed and unsigned
45    (@array $i:ty : $cap:literal, $self:ty, $fout:ty) => { $crate::paste! {
46        #[doc = crate::doc_availability!(feature = $cap)]
47        ///
48        #[doc = "# Fraction related methods for `[" $i "; 2]`\n\n"]
49        #[cfg(feature = $cap )]
50        // #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = $cap)))]
51        impl Frac<$self> {
52            /// Returns the numerator (the first number of the sequence).
53            #[must_use]
54            pub const fn numerator(self) -> $i { self.0[0] }
55            /// Alias of [`numerator`][Self::numerator].
56            #[must_use]
57            pub const fn num(self) -> $i { self.0[0] }
58
59            /// Returns the denominator (the second number of the sequence).
60            #[must_use]
61            pub const fn denominator(self) -> $i { self.0[1] }
62            /// Alias of [`denominator`][Self::denominator].
63            #[must_use]
64            pub const fn den(self) -> $i { self.0[0] }
65
66            /// Retuns `true` if the fraction is valid `(denominator != 0)`.
67            /// # Examples
68            /// ```
69            /// # use devela::Frac;
70            #[doc = "assert![Frac([2_" $i ", 1]).is_valid()];"]
71            #[doc = "assert![!Frac([2_" $i ", 0]).is_valid()];"]
72            /// ```
73            #[must_use]
74            pub const fn is_valid(self) -> bool { self.0[1] != 0 }
75
76            /// Returns `true` if the fraction is proper
77            /// `(numerator.abs() < denominator.abs())`.
78            /// # Examples
79            /// ```
80            /// # use devela::Frac;
81            #[doc = "assert![Frac([2_" $i ", 3]).is_proper()];"]
82            #[doc = "assert![!Frac([3_" $i ", 3]).is_proper()];"]
83            #[doc = "assert![!Frac([4_" $i ", 3]).is_proper()];"]
84            /// ```
85            #[must_use]
86            pub const fn is_proper(self) -> bool { Int(self.0[0]).abs().0 < Int(self.0[1]).abs().0 }
87
88            /// Retuns `true` if the fraction is in the simplest possible form `(gcd() == 1)`.
89            #[must_use]
90            pub const fn is_reduced(self) -> bool { self.gcd() == 1 }
91
92            /// Simplify a fraction.
93            #[must_use]
94            pub const fn reduce(self) -> $fout {
95                let g = self.gcd();
96                Frac([self.0[0] / g, self.0[1] / g])
97            }
98
99            /// Returns the <abbr title="Greatest Common Divisor">GCD</abbr>
100            /// between the numerator and the denominator.
101            #[must_use]
102            pub const fn gcd(self) -> $i { Int(self.0[0]).gcd(self.0[1]).0 }
103
104            /// Returns the <abbr title="Least Common Multiple">LCM</abbr>
105            /// between the numerator and the denominator.
106            /// # Errors
107            /// Could [`Overflow`].
108            pub const fn lcm(self) -> Result<$i> {
109                match Int(self.numerator()).lcm(self.denominator()) {
110                    Ok(res) => Ok(res.0),
111                    Err(e) => Err(e)
112                }
113            }
114
115            /// Adds two fractions.
116            #[allow(clippy::should_implement_trait)]
117            pub fn add(self, other: $self) -> Result<$fout> {
118                let [num1, den1, num2, den2] = [self.0[0], self.0[1], other[0], other[1]];
119                let lcm_denom = match Int(den1).lcm(den2) {
120                    Ok(res) => res.0,
121                    Err(e) => { return Err(e); }
122                };
123                let num = num1 * (lcm_denom / den1) + num2 * (lcm_denom / den2);
124                Ok(Frac([num, lcm_denom]).reduce())
125            }
126        }
127    }};
128
129    (@int_array $i:ty : $cap:literal, $self:ty, $fout:ty) => { $crate::paste! {
130        #[doc = crate::doc_availability!(feature = $cap)]
131        ///
132        #[doc = "# Fraction related methods for `[Int<" $i ">; 2]`\n\n"]
133        #[cfg(feature = $cap )]
134        // #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = $cap)))]
135        impl Frac<$self> {
136            /// Returns the numerator (the first number of the sequence).
137            #[must_use]
138            pub const fn numerator(self) -> Int<$i> { self.0[0] }
139
140            /// Returns the denominator (the second number of the sequence).
141            #[must_use]
142            pub const fn denominator(self) -> Int<$i> { self.0[1] }
143
144            /// Retuns `true` if the fraction is valid `(denominator != 0)`.
145            /// # Examples
146            /// ```
147            /// # use devela::{Frac, Int};
148            #[doc = "assert![Frac([Int(2_" $i "), Int(1)]).is_valid()];"]
149            #[doc = "assert![!Frac([Int(2_" $i "), Int(0)]).is_valid()];"]
150            /// ```
151            #[must_use]
152            pub const fn is_valid(self) -> bool { self.0[1].0 != 0 }
153
154            /// Returns `true` if the fraction is proper
155            /// `(numerator.abs() < denominator.abs())`.
156            /// # Examples
157            /// ```
158            /// # use devela::{Frac, Int};
159            #[doc = "assert![Frac([Int(2_" $i "), Int(3)]).is_proper()];"]
160            #[doc = "assert![!Frac([Int(3_" $i "), Int(3)]).is_proper()];"]
161            #[doc = "assert![!Frac([Int(4_" $i "), Int(3)]).is_proper()];"]
162            /// ```
163            #[must_use]
164            pub const fn is_proper(self) -> bool { self.0[0].abs().0 < self.0[1].abs().0 }
165
166            /// Retuns `true` if the fraction is in the simplest possible form `(gcd() == 1)`.
167            #[must_use]
168            pub const fn is_reduced(self) -> bool { self.gcd().0 == 1 }
169
170            /// Simplify a fraction.
171            #[must_use]
172            pub const fn reduce(self) -> $fout {
173                let g = self.gcd().0;
174                Frac([Int(self.0[0].0 / g), Int(self.0[1].0 / g)])
175            }
176
177            /// Returns the <abbr title="Greatest Common Divisor">GCD</abbr>
178            /// between the numerator and the denominator.
179            #[must_use]
180            pub const fn gcd(self) -> Int<$i> { self.0[0].gcd(self.0[1].0) }
181
182            /// Returns the <abbr title="Least Common Multiple">LCM</abbr>
183            /// between the numerator and the denominator.
184            /// # Errors
185            /// Could [`Overflow`].
186            pub const fn lcm(self) -> Result<Int<$i>> {
187                match self.numerator().lcm(self.denominator().0) {
188                    Ok(res) => Ok(res),
189                    Err(e) => Err(e)
190                }
191            }
192
193            /// Adds two fractions.
194            #[allow(clippy::should_implement_trait)]
195            pub fn add(self, other: $self) -> Result<$fout> {
196                let [num1, den1, num2, den2] = [self.0[0].0, self.0[1].0, other[0].0, other[1].0];
197                let lcm_denom = match Int(den1).lcm(den2) {
198                    Ok(res) => res.0,
199                    Err(e) => { return Err(e); }
200                };
201                let num = num1 * (lcm_denom / den1) + num2 * (lcm_denom / den2);
202                Ok(Frac([Int(num), Int(lcm_denom)]).reduce())
203            }
204        }
205    }};
206}
207impl_frac!();