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!();