1use std::cmp::Ordering;
2use std::fmt;
3use std::ops::{Add, Div, Mul, Neg, Sub};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7#[repr(u32)]
8pub enum Rounding {
9 Zero = 0,
11 Inf = 1,
13 Down = 2,
15 Up = 3,
17 NearInf = 5,
19}
20
21pub const ROUND_PASS_MINMAX: u32 = 8192;
23
24#[derive(Clone, Copy, Default)]
29pub struct Rational {
30 pub num: i32,
31 pub den: i32,
32}
33
34impl Rational {
35 pub const fn new(num: i32, den: i32) -> Self {
37 Self { num, den }
38 }
39
40 pub fn to_f64(self) -> f64 {
42 self.num as f64 / self.den as f64
43 }
44
45 pub const fn invert(self) -> Self {
47 Self {
48 num: self.den,
49 den: self.num,
50 }
51 }
52
53 pub fn reduce(&mut self, max: i64) -> bool {
56 let (num, den, exact) = reduce(self.num as i64, self.den as i64, max);
57 self.num = num;
58 self.den = den;
59 exact
60 }
61}
62
63impl fmt::Debug for Rational {
64 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65 write!(f, "{}/{}", self.num, self.den)
66 }
67}
68
69impl fmt::Display for Rational {
70 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71 write!(f, "{}/{}", self.num, self.den)
72 }
73}
74
75impl PartialEq for Rational {
76 fn eq(&self, other: &Self) -> bool {
77 cmp_q(*self, *other) == Some(Ordering::Equal)
78 }
79}
80
81impl Eq for Rational {}
82
83impl PartialOrd for Rational {
84 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
85 cmp_q(*self, *other)
86 }
87}
88
89fn cmp_q(a: Rational, b: Rational) -> Option<Ordering> {
92 let tmp = a.num as i64 * b.den as i64 - b.num as i64 * a.den as i64;
93
94 if tmp != 0 {
95 let v = ((tmp ^ a.den as i64 ^ b.den as i64) >> 63) | 1;
98 if v > 0 {
99 Some(Ordering::Greater)
100 } else {
101 Some(Ordering::Less)
102 }
103 } else if b.den != 0 && a.den != 0 {
104 Some(Ordering::Equal)
105 } else if a.num != 0 && b.num != 0 {
106 let cmp = (a.num >> 31) - (b.num >> 31);
107 match cmp.cmp(&0) {
108 Ordering::Less => Some(Ordering::Less),
109 Ordering::Greater => Some(Ordering::Greater),
110 Ordering::Equal => Some(Ordering::Equal),
111 }
112 } else {
113 None }
115}
116
117#[allow(unknown_lints, clippy::manual_checked_ops)]
120pub fn reduce(num: i64, den: i64, max: i64) -> (i32, i32, bool) {
121 let (mut a0n, mut a0d): (u64, u64) = (0, 1);
125 let (mut a1n, mut a1d): (u64, u64) = (1, 0);
126 let sign = (num < 0) ^ (den < 0);
127 let g = gcd(num.unsigned_abs(), den.unsigned_abs());
128 let max = max as u64;
129
130 let mut num = num.unsigned_abs();
131 let mut den = den.unsigned_abs();
132
133 if g != 0 {
134 num /= g;
135 den /= g;
136 }
137
138 if num <= max && den <= max {
141 a1n = num;
142 a1d = den;
143 den = 0;
144 }
145
146 while den != 0 {
147 let x = num / den;
148 let next_den = num - den * x;
149 let a2n = x * a1n + a0n;
150 let a2d = x * a1d + a0d;
151
152 if a2n > max || a2d > max {
153 let mut x = x;
156 if a1n != 0 {
157 x = (max - a0n) / a1n;
158 }
159 if a1d != 0 {
160 x = x.min((max - a0d) / a1d);
161 }
162
163 if den * (2 * x * a1d + a0d) > num * a1d {
167 a1n = x * a1n + a0n;
168 a1d = x * a1d + a0d;
169 }
170 break;
171 }
172
173 a0n = a1n;
174 a0d = a1d;
175 a1n = a2n;
176 a1d = a2d;
177 num = den;
178 den = next_den;
179 }
180
181 let dst_num = if sign { -(a1n as i32) } else { a1n as i32 };
184 let dst_den = a1d as i32;
185 (dst_num, dst_den, den == 0)
186}
187
188pub fn gcd(mut a: u64, mut b: u64) -> u64 {
190 if a == 0 {
191 return b;
192 }
193 if b == 0 {
194 return a;
195 }
196 let za = a.trailing_zeros();
197 let zb = b.trailing_zeros();
198 let k = za.min(zb);
199 a >>= za;
200 b >>= zb;
201 while a != b {
202 if a > b {
203 std::mem::swap(&mut a, &mut b);
204 }
205 b -= a;
206 b >>= b.trailing_zeros();
207 }
208 a << k
209}
210
211pub fn rescale_rnd(a: i64, b: i64, c: i64, rnd: Rounding, pass_minmax: bool) -> i64 {
215 if c <= 0 || b < 0 {
216 return i64::MIN;
217 }
218
219 if pass_minmax && (a == i64::MIN || a == i64::MAX) {
220 return a;
221 }
222
223 if a < 0 {
224 let neg_rnd = match rnd {
225 Rounding::Down => Rounding::Up,
226 Rounding::Up => Rounding::Down,
227 Rounding::Inf => Rounding::Inf,
228 other => other,
229 };
230 let abs_a = if a == i64::MIN { i64::MAX } else { -a };
231 return (rescale_rnd(abs_a, b, c, neg_rnd, false) as u64).wrapping_neg() as i64;
232 }
233
234 let r: i64 = match rnd {
235 Rounding::NearInf => c / 2,
236 Rounding::Inf | Rounding::Up => c - 1,
237 _ => 0,
238 };
239
240 if b <= i32::MAX as i64 && c <= i32::MAX as i64 {
241 if a <= i32::MAX as i64 {
242 return (a * b + r) / c;
243 }
244 let ad = a / c;
245 let a2 = (a % c * b + r) / c;
246 if ad >= i32::MAX as i64 && b != 0 && ad > (i64::MAX - a2) / b {
247 return i64::MIN;
248 }
249 return ad * b + a2;
250 }
251
252 let product = a as u128 * b as u128 + r as u128;
254 let result = product / c as u128;
255 if result > i64::MAX as u128 {
256 return i64::MIN;
257 }
258 result as i64
259}
260
261pub fn rescale(a: i64, b: i64, c: i64) -> i64 {
264 rescale_rnd(a, b, c, Rounding::NearInf, false)
265}
266
267pub fn rescale_q_rnd(a: i64, bq: Rational, cq: Rational, rnd: Rounding, pass_minmax: bool) -> i64 {
270 let b = bq.num as i64 * cq.den as i64;
271 let c = cq.num as i64 * bq.den as i64;
272 rescale_rnd(a, b, c, rnd, pass_minmax)
273}
274
275pub fn rescale_q(a: i64, bq: Rational, cq: Rational) -> i64 {
278 rescale_q_rnd(a, bq, cq, Rounding::NearInf, false)
279}
280
281impl Neg for Rational {
282 type Output = Self;
283 fn neg(self) -> Self {
284 Self {
286 num: self.num.wrapping_neg(),
287 den: self.den,
288 }
289 }
290}
291
292impl Add for Rational {
293 type Output = Self;
294 fn add(self, rhs: Self) -> Self {
295 let num = self.num as i64 * rhs.den as i64 + rhs.num as i64 * self.den as i64;
296 let den = self.den as i64 * rhs.den as i64;
297 let (n, d, _) = reduce(num, den, i32::MAX as i64);
298 Self { num: n, den: d }
299 }
300}
301
302impl Sub for Rational {
303 type Output = Self;
304 fn sub(self, rhs: Self) -> Self {
305 self + (-rhs)
306 }
307}
308
309impl Mul for Rational {
310 type Output = Self;
311 fn mul(self, rhs: Self) -> Self {
312 let num = self.num as i64 * rhs.num as i64;
313 let den = self.den as i64 * rhs.den as i64;
314 let (n, d, _) = reduce(num, den, i32::MAX as i64);
315 Self { num: n, den: d }
316 }
317}
318
319impl Div for Rational {
320 type Output = Self;
321 #[allow(clippy::suspicious_arithmetic_impl)]
322 fn div(self, rhs: Self) -> Self {
323 self * rhs.invert()
324 }
325}
326
327#[cfg(test)]
328mod tests {
329 use super::*;
330
331 #[test]
332 fn test_rational_basics() {
333 let r = Rational::new(1, 2);
334 assert_eq!(r.to_f64(), 0.5);
335 assert_eq!(r.invert(), Rational::new(2, 1));
336 }
337
338 #[test]
339 fn test_rational_arithmetic() {
340 let a = Rational::new(1, 3);
341 let b = Rational::new(1, 6);
342 let sum = a + b;
343 assert_eq!(sum, Rational::new(1, 2));
344
345 let product = Rational::new(2, 3) * Rational::new(3, 4);
346 assert_eq!(product, Rational::new(1, 2));
347 }
348
349 #[test]
350 fn test_rescale_rnd() {
351 assert_eq!(rescale(3, 1, 2), 2); assert_eq!(rescale_rnd(3, 1, 2, Rounding::Zero, false), 1);
354 assert_eq!(rescale_rnd(3, 1, 2, Rounding::Up, false), 2);
355 assert_eq!(rescale_rnd(3, 1, 2, Rounding::Down, false), 1);
356 }
357
358 #[test]
359 fn test_rescale_q() {
360 let tb1 = Rational::new(1, 48000);
361 let tb2 = Rational::new(1, 1000);
362 let result = rescale_q(48000, tb1, tb2);
364 assert_eq!(result, 1000);
365 }
366
367 #[test]
368 fn test_gcd() {
369 assert_eq!(gcd(12, 8), 4);
370 assert_eq!(gcd(0, 5), 5);
371 assert_eq!(gcd(5, 0), 5);
372 assert_eq!(gcd(0, 0), 0);
373 assert_eq!(gcd(7, 13), 1);
374 }
375
376 #[test]
377 fn test_reduce() {
378 let (n, d, exact) = reduce(6, 4, i32::MAX as i64);
379 assert!(exact);
380 assert_eq!(n, 3);
381 assert_eq!(d, 2);
382 }
383
384 #[test]
385 fn test_pass_minmax() {
386 assert_eq!(
387 rescale_rnd(i64::MIN, 1, 2, Rounding::NearInf, true),
388 i64::MIN
389 );
390 assert_eq!(
391 rescale_rnd(i64::MAX, 1, 2, Rounding::NearInf, true),
392 i64::MAX
393 );
394 }
395}