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