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