1use crate::ratio::{Exact, Ratio, Round};
6use zr::static_assert;
7
8pub struct Saturate;
9#[allow(non_upper_case_globals)]
10impl Saturate {
11 pub const No: bool = false;
12 pub const Yes: bool = true;
13}
14
15#[repr(C)]
53#[derive(Debug, Clone, Copy, PartialEq, Eq)]
54pub struct Transform {
55 a_offset: i64,
56 b_offset: i64,
57 ratio: Ratio,
58}
59
60static_assert!(core::mem::size_of::<Transform>() == 24);
61static_assert!(core::mem::align_of::<Transform>() == 8);
62
63impl Default for Transform {
65 fn default() -> Self {
66 Transform { a_offset: 0, b_offset: 0, ratio: Ratio::default() }
67 }
68}
69
70impl Transform {
72 pub fn new(a_offset: i64, b_offset: i64, ratio: Ratio) -> Self {
74 Transform { a_offset, b_offset, ratio }
75 }
76
77 pub fn new_linear(ratio: Ratio) -> Self {
79 Transform { a_offset: 0, b_offset: 0, ratio }
80 }
81
82 pub fn invertible(&self) -> bool {
83 self.ratio.invertible()
84 }
85
86 pub fn a_offset(&self) -> i64 {
87 self.a_offset
88 }
89
90 pub fn b_offset(&self) -> i64 {
91 self.b_offset
92 }
93
94 pub fn ratio(&self) -> Ratio {
95 self.ratio
96 }
97
98 pub fn numerator(&self) -> u32 {
99 self.ratio.numerator()
100 }
101
102 pub fn denominator(&self) -> u32 {
103 self.ratio.denominator()
104 }
105
106 pub fn inverse(&self) -> Self {
108 Transform { a_offset: self.b_offset, b_offset: self.a_offset, ratio: self.ratio.inverse() }
109 }
110
111 pub fn apply_static<const SATURATE: bool>(
113 a_offset: i64,
114 b_offset: i64,
115 ratio: Ratio,
116 val: i64,
117 ) -> i64 {
118 if SATURATE {
119 let sub = val.saturating_sub(a_offset);
120 let scaled = ratio.scale::<{ Round::DOWN }>(sub);
121 scaled.saturating_add(b_offset)
122 } else {
123 let sub = val.wrapping_sub(a_offset);
128 let scaled = ratio.scale::<{ Round::DOWN }>(sub);
129 scaled.wrapping_add(b_offset)
130 }
131 }
132
133 pub fn apply_inverse_static<const SATURATE: bool>(
135 a_offset: i64,
136 b_offset: i64,
137 ratio: Ratio,
138 val: i64,
139 ) -> i64 {
140 Self::apply_static::<SATURATE>(b_offset, a_offset, ratio.inverse(), val)
141 }
142
143 pub fn apply<const SATURATE: bool>(&self, val: i64) -> i64 {
145 Self::apply_static::<SATURATE>(self.a_offset, self.b_offset, self.ratio, val)
146 }
147
148 pub fn apply_inverse<const SATURATE: bool>(&self, val: i64) -> i64 {
150 debug_assert!(self.ratio.denominator() != 0);
151 Self::apply_inverse_static::<SATURATE>(self.a_offset, self.b_offset, self.ratio, val)
152 }
153
154 pub fn compose(bc: &Transform, ab: &Transform, exact: Exact) -> Transform {
164 Transform {
165 a_offset: ab.a_offset,
166 b_offset: bc.apply::<{ Saturate::Yes }>(ab.b_offset),
167 ratio: Ratio::product(ab.ratio, bc.ratio, exact),
168 }
169 }
170}
171
172impl core::ops::Mul for Transform {
178 type Output = Self;
179 fn mul(self, rhs: Self) -> Self::Output {
180 Transform::compose(&self, &rhs, Exact::Yes)
181 }
182}
183
184#[cfg(test)]
185mod tests {
186 use super::*;
187
188 #[test]
189 fn test_construction() {
190 let t = Transform::default();
191 assert_eq!(t.a_offset(), 0);
192 assert_eq!(t.b_offset(), 0);
193 assert_eq!(t.numerator(), 1);
194 assert_eq!(t.denominator(), 1);
195
196 struct TestVector {
197 a_offset: i64,
198 b_offset: i64,
199 n: u32,
200 d: u32,
201 }
202
203 let valid_vectors = [
204 TestVector { a_offset: 12345, b_offset: 98764, n: 3, d: 2 },
205 TestVector { a_offset: -12345, b_offset: 98764, n: 247, d: 931 },
206 TestVector { a_offset: -12345, b_offset: -98764, n: 48000, d: 44100 },
207 TestVector { a_offset: 12345, b_offset: -98764, n: 1000007, d: 1000000 },
208 TestVector { a_offset: 12345, b_offset: 98764, n: 0, d: 1000000 },
209 ];
210
211 for v in &valid_vectors {
212 let ratio = Ratio::new(v.n, v.d);
213
214 let t_linear = Transform::new_linear(ratio);
215 assert_eq!(t_linear.a_offset(), 0);
216 assert_eq!(t_linear.b_offset(), 0);
217 assert_eq!(t_linear.numerator(), ratio.numerator());
218 assert_eq!(t_linear.denominator(), ratio.denominator());
219
220 let t_affine = Transform::new(v.a_offset, v.b_offset, ratio);
221 assert_eq!(t_affine.a_offset(), v.a_offset);
222 assert_eq!(t_affine.b_offset(), v.b_offset);
223 assert_eq!(t_affine.numerator(), ratio.numerator());
224 assert_eq!(t_affine.denominator(), ratio.denominator());
225 }
226 }
227
228 #[test]
229 fn test_inverse() {
230 struct TestVector {
231 a_offset: i64,
232 b_offset: i64,
233 n: u32,
234 d: u32,
235 }
236
237 let test_vectors = [
238 TestVector { a_offset: 12345, b_offset: 98764, n: 3, d: 2 },
239 TestVector { a_offset: -12345, b_offset: 98764, n: 247, d: 931 },
240 TestVector { a_offset: -12345, b_offset: -98764, n: 48000, d: 44100 },
241 TestVector { a_offset: 12345, b_offset: -98764, n: 1000007, d: 1000000 },
242 ];
243
244 for v in &test_vectors {
245 let ratio = Ratio::new(v.n, v.d);
246 let t = Transform::new(v.a_offset, v.b_offset, ratio);
247
248 if t.invertible() {
249 let res = t.inverse();
250 assert_eq!(t.a_offset(), res.b_offset());
251 assert_eq!(t.b_offset(), res.a_offset());
252 assert_eq!(t.numerator(), res.denominator());
253 assert_eq!(t.denominator(), res.numerator());
254 assert_eq!(t.ratio().inverse().numerator(), res.ratio().numerator());
255 assert_eq!(t.ratio().inverse().denominator(), res.ratio().denominator());
256 }
257 }
258
259 let t_non_inv = Transform::new(12345, 98764, Ratio::new(0, 1000000));
260 assert!(!t_non_inv.invertible());
261 }
262
263 #[test]
264 fn test_apply() {
265 struct TestVector {
266 a_offset: i64,
267 b_offset: i64,
268 n: u32,
269 d: u32,
270 val: i64,
271 expected: i64,
272 expect_ovfl: bool,
273 }
274
275 let test_vectors = [
276 TestVector {
277 a_offset: 0,
278 b_offset: 0,
279 n: 1,
280 d: 1,
281 val: 12345,
282 expected: 12345,
283 expect_ovfl: false,
284 },
285 TestVector {
286 a_offset: 50,
287 b_offset: 0,
288 n: 1,
289 d: 1,
290 val: 12345,
291 expected: 12295,
292 expect_ovfl: false,
293 },
294 TestVector {
295 a_offset: 0,
296 b_offset: -50,
297 n: 1,
298 d: 1,
299 val: 12345,
300 expected: 12295,
301 expect_ovfl: false,
302 },
303 TestVector {
304 a_offset: 50,
305 b_offset: -50,
306 n: 1,
307 d: 1,
308 val: 12345,
309 expected: 12245,
310 expect_ovfl: false,
311 },
312 TestVector {
313 a_offset: 50,
314 b_offset: 50,
315 n: 1,
316 d: 1,
317 val: 12345,
318 expected: 12345,
319 expect_ovfl: false,
320 },
321 TestVector {
322 a_offset: 0,
323 b_offset: 0,
324 n: 48000,
325 d: 44100,
326 val: 12345,
327 expected: 13436,
328 expect_ovfl: false,
329 },
330 TestVector {
331 a_offset: 50,
332 b_offset: 0,
333 n: 48000,
334 d: 44100,
335 val: 12345,
336 expected: 13382,
337 expect_ovfl: false,
338 },
339 TestVector {
340 a_offset: 0,
341 b_offset: -54,
342 n: 48000,
343 d: 44100,
344 val: 12345,
345 expected: 13382,
346 expect_ovfl: false,
347 },
348 TestVector {
349 a_offset: 50,
350 b_offset: -54,
351 n: 48000,
352 d: 44100,
353 val: 12345,
354 expected: 13328,
355 expect_ovfl: false,
356 },
357 TestVector {
358 a_offset: 50,
359 b_offset: 54,
360 n: 48000,
361 d: 44100,
362 val: 12345,
363 expected: 13436,
364 expect_ovfl: false,
365 },
366 TestVector {
367 a_offset: -100,
368 b_offset: -17,
369 n: 1,
370 d: 1,
371 val: i64::MAX - 1,
372 expected: i64::MAX - 17,
373 expect_ovfl: true,
374 },
375 TestVector {
376 a_offset: 100,
377 b_offset: 17,
378 n: 1,
379 d: 1,
380 val: i64::MIN + 1,
381 expected: i64::MIN + 17,
382 expect_ovfl: true,
383 },
384 TestVector {
385 a_offset: 0,
386 b_offset: -17,
387 n: 3,
388 d: 1,
389 val: i64::MAX / 2,
390 expected: i64::MAX - 17,
391 expect_ovfl: true,
392 },
393 TestVector {
394 a_offset: 0,
395 b_offset: 17,
396 n: 3,
397 d: 1,
398 val: i64::MIN / 2,
399 expected: i64::MIN + 17,
400 expect_ovfl: true,
401 },
402 TestVector {
403 a_offset: 0,
404 b_offset: 17,
405 n: 1,
406 d: 1,
407 val: i64::MAX - 10,
408 expected: i64::MAX,
409 expect_ovfl: true,
410 },
411 TestVector {
412 a_offset: 0,
413 b_offset: -17,
414 n: 1,
415 d: 1,
416 val: i64::MIN + 10,
417 expected: i64::MIN,
418 expect_ovfl: true,
419 },
420 ];
421
422 for v in &test_vectors {
423 let t = Transform::new(v.a_offset, v.b_offset, Ratio::new(v.n, v.d));
424
425 let res_sat_static = Transform::apply_static::<{ Saturate::Yes }>(
426 t.a_offset(),
427 t.b_offset(),
428 t.ratio(),
429 v.val,
430 );
431 assert_eq!(res_sat_static, v.expected);
432
433 if !v.expect_ovfl {
434 let res_nosat_static = Transform::apply_static::<{ Saturate::No }>(
435 t.a_offset(),
436 t.b_offset(),
437 t.ratio(),
438 v.val,
439 );
440 assert_eq!(res_nosat_static, v.expected);
441 }
442
443 let res_sat_obj = t.apply::<{ Saturate::Yes }>(v.val);
444 assert_eq!(res_sat_obj, v.expected);
445
446 if !v.expect_ovfl {
447 let res_nosat_obj = t.apply::<{ Saturate::No }>(v.val);
448 assert_eq!(res_nosat_obj, v.expected);
449 }
450
451 if t.invertible() {
452 let t_inv = t.inverse();
453
454 let res_sat_inv_static = Transform::apply_inverse_static::<{ Saturate::Yes }>(
455 t_inv.a_offset(),
456 t_inv.b_offset(),
457 t_inv.ratio(),
458 v.val,
459 );
460 assert_eq!(res_sat_inv_static, v.expected);
461
462 if !v.expect_ovfl {
463 let res_nosat_inv_static = Transform::apply_inverse_static::<{ Saturate::No }>(
464 t_inv.a_offset(),
465 t_inv.b_offset(),
466 t_inv.ratio(),
467 v.val,
468 );
469 assert_eq!(res_nosat_inv_static, v.expected);
470 }
471
472 let res_sat_inv_obj = t_inv.apply_inverse::<{ Saturate::Yes }>(v.val);
473 assert_eq!(res_sat_inv_obj, v.expected);
474
475 if !v.expect_ovfl {
476 let res_nosat_inv_obj = t_inv.apply_inverse::<{ Saturate::No }>(v.val);
477 assert_eq!(res_nosat_inv_obj, v.expected);
478 }
479 }
480 }
481 }
482
483 #[test]
484 fn test_compose() {
485 struct TestVector {
486 ab: Transform,
487 bc: Transform,
488 ac: Transform,
489 is_exact: Exact,
490 }
491
492 let test_vectors = [
493 TestVector {
494 ab: Transform::new(0, 0, Ratio::new(1, 1)),
495 bc: Transform::new(0, 0, Ratio::new(1, 1)),
496 ac: Transform::new(0, 0, Ratio::new(1, 1)),
497 is_exact: Exact::Yes,
498 },
499 TestVector {
500 ab: Transform::new(0, 0, Ratio::new(1, 1)),
501 bc: Transform::new(12345, 98765, Ratio::new(17, 7)),
502 ac: Transform::new(0, 68784, Ratio::new(17, 7)),
503 is_exact: Exact::Yes,
504 },
505 TestVector {
506 ab: Transform::new(12345, 98765, Ratio::new(17, 7)),
507 bc: Transform::new(0, 0, Ratio::new(1, 1)),
508 ac: Transform::new(12345, 98765, Ratio::new(17, 7)),
509 is_exact: Exact::Yes,
510 },
511 TestVector {
512 ab: Transform::new(34327, 86539, Ratio::new(1000007, 1000000)),
513 bc: Transform::new(728376, -34265, Ratio::new(48000, 44100)),
514 ac: Transform::new(34327, -732864, Ratio::new(1000007, 918750)),
515 is_exact: Exact::Yes,
516 },
517 TestVector {
518 ab: Transform::new(0, i64::MAX - 5, Ratio::new(1, 1)),
519 bc: Transform::new(-100, 0, Ratio::new(1, 1)),
520 ac: Transform::new(0, i64::MAX, Ratio::new(1, 1)),
521 is_exact: Exact::Yes,
522 },
523 TestVector {
524 ab: Transform::new(0, i64::MIN + 5, Ratio::new(1, 1)),
525 bc: Transform::new(100, 0, Ratio::new(1, 1)),
526 ac: Transform::new(0, i64::MIN, Ratio::new(1, 1)),
527 is_exact: Exact::Yes,
528 },
529 TestVector {
530 ab: Transform::new(0, 100, Ratio::new(1, 1)),
531 bc: Transform::new(0, i64::MAX - 5, Ratio::new(1, 1)),
532 ac: Transform::new(0, i64::MAX, Ratio::new(1, 1)),
533 is_exact: Exact::Yes,
534 },
535 TestVector {
536 ab: Transform::new(0, -100, Ratio::new(1, 1)),
537 bc: Transform::new(0, i64::MIN + 5, Ratio::new(1, 1)),
538 ac: Transform::new(0, i64::MIN, Ratio::new(1, 1)),
539 is_exact: Exact::Yes,
540 },
541 TestVector {
542 ab: Transform::new(0, 0, Ratio::new(3465653567, 2327655023)),
543 bc: Transform::new(0, 0, Ratio::new(1291540343, 3698423317)),
544 ac: Transform::new(0, 0, Ratio::new(317609835, 610852072)),
545 is_exact: Exact::No,
546 },
547 TestVector {
548 ab: Transform::new(0, 20, Ratio::new(3465653567, 2327655023)),
549 bc: Transform::new(-3698423317 + 20, 5, Ratio::new(1291540343, 3698423317)),
550 ac: Transform::new(0, 1291540343 + 5, Ratio::new(317609835, 610852072)),
551 is_exact: Exact::No,
552 },
553 ];
554
555 for v in &test_vectors {
556 if v.is_exact == Exact::Yes {
557 let res_static = Transform::compose(&v.bc, &v.ab, Exact::Yes);
558 assert_eq!(res_static, v.ac);
559
560 let res_op = v.bc * v.ab;
561 assert_eq!(res_op, v.ac);
562 }
563
564 let res_inexact = Transform::compose(&v.bc, &v.ab, Exact::No);
565 assert_eq!(res_inexact, v.ac);
566 }
567 }
568}