1pub const FP_SHIFT: u32 = 16;
18
19pub const FP_ONE: i32 = 1_i32 << FP_SHIFT;
21
22pub const Q16_MAX: i32 = i32::MAX;
24
25pub const Q16_MIN: i32 = i32::MIN;
27
28pub type Q16 = i32;
32
33#[inline(always)]
39pub fn to_q16(v: f32) -> Q16 {
40 (v * 65536.0_f32 + if v >= 0.0 { 0.5 } else { -0.5 }) as i32
41}
42
43#[inline(always)]
45pub fn from_q16(v: Q16) -> f32 {
46 v as f32 / 65536.0_f32
47}
48
49#[inline(always)]
51pub fn from_i16_q16(v: i16) -> Q16 {
52 (v as i32) << 16
53}
54
55#[inline(always)]
57pub fn to_i16_q16(v: Q16) -> i16 {
58 (v >> 16) as i16
59}
60
61#[inline(always)]
63pub fn q31_to_q16(q31: i32) -> Q16 {
64 q31 >> 15
65}
66
67#[inline(always)]
69pub fn q16_to_q31(v: Q16) -> i64 {
70 (v as i64) << 16
71}
72
73#[inline(always)]
79pub fn mul_q16(a: Q16, b: Q16) -> Q16 {
80 ((a as i64 * b as i64) >> 16) as i32
81}
82
83#[inline(always)]
85pub fn mul_n_q16(a: Q16, n: i32) -> Q16 {
86 a.wrapping_mul(n)
87}
88
89#[inline(always)]
91pub fn mul_f_q16(a: Q16, f: f32) -> Q16 {
92 mul_q16(a, to_q16(f))
93}
94
95#[inline(always)]
97pub fn div_q16(a: Q16, b: Q16) -> Q16 {
98 if b == 0 {
99 return 0;
100 }
101 (((a as i64) << 16) / b as i64) as i32
102}
103
104#[inline(always)]
106pub fn div_n_q16(a: Q16, n: i32) -> Q16 {
107 if n == 0 {
108 return 0;
109 }
110 a / n
111}
112
113#[inline(always)]
115pub fn div_f_q16(a: Q16, f: f32) -> Q16 {
116 div_q16(a, to_q16(f))
117}
118
119#[inline(always)]
125pub fn qadd_q16(a: Q16, b: Q16) -> Q16 {
126 (a as i64 + b as i64).clamp(Q16_MIN as i64, Q16_MAX as i64) as i32
127}
128
129#[inline(always)]
131pub fn qsub_q16(a: Q16, b: Q16) -> Q16 {
132 (a as i64 - b as i64).clamp(Q16_MIN as i64, Q16_MAX as i64) as i32
133}
134
135#[inline(always)]
137pub fn abs_q16(a: Q16) -> Q16 {
138 a.abs()
139}
140
141#[inline(always)]
150pub fn lerp_q16(a: Q16, b: Q16, t: i32, denom: i32) -> Q16 {
151 if denom == 0 {
152 return a;
153 }
154 let diff = b as i64 - a as i64;
155 (a as i64 + diff * t as i64 / denom as i64) as i32
156}
157
158#[inline(always)]
160pub fn angle_to_q16(degrees: f32) -> Q16 {
161 to_q16(degrees * core::f32::consts::PI / 180.0)
162}
163
164#[inline(always)]
169pub fn recip_q16(v: Q16) -> Q16 {
170 if v == 0 {
171 return Q16_MAX;
172 }
173 ((1i64 << 32) / v as i64) as i32
174}
175
176#[inline(always)]
182pub fn to_fp(v: f32) -> i32 {
183 to_q16(v)
184}
185
186#[inline(always)]
188pub fn from_fp(v: i32) -> f32 {
189 from_q16(v)
190}
191
192#[inline(always)]
194pub fn mul_fp(a: i32, b: i32) -> i32 {
195 mul_q16(a, b)
196}
197
198#[inline(always)]
200pub fn div_fp(a: i32, b: i32) -> Option<i32> {
201 if b == 0 { None } else { Some(div_q16(a, b)) }
202}
203
204#[derive(Debug, Clone, Copy)]
241pub struct ScanlineInterp {
242 z_cur: u32,
243 z_step: i32,
244 u_cur: u32,
245 u_step: i32,
246 v_cur: u32,
247 v_step: i32,
248}
249
250impl ScanlineInterp {
251 #[inline]
259 pub fn new(
260 z_left: u32,
261 z_right: u32,
262 u_left: u32,
263 u_right: u32,
264 v_left: u32,
265 v_right: u32,
266 span: i32,
267 ) -> Self {
268 let (z_step, u_step, v_step) = if span > 0 {
269 let z_step = ((z_right as i64 - z_left as i64) / span as i64) as i32;
270 let u_step = ((u_right as i64 - u_left as i64) / span as i64) as i32;
271 let v_step = ((v_right as i64 - v_left as i64) / span as i64) as i32;
272 (z_step, u_step, v_step)
273 } else {
274 (0, 0, 0)
275 };
276 Self {
277 z_cur: z_left,
278 z_step,
279 u_cur: u_left,
280 u_step,
281 v_cur: v_left,
282 v_step,
283 }
284 }
285
286 #[inline]
288 pub fn depth_only(z_left: u32, z_right: u32, span: i32) -> Self {
289 Self::new(z_left, z_right, 0, 0, 0, 0, span)
290 }
291
292 #[inline(always)]
294 pub fn z(&self) -> u32 {
295 self.z_cur
296 }
297
298 #[inline(always)]
300 pub fn u(&self) -> u32 {
301 self.u_cur
302 }
303
304 #[inline(always)]
306 pub fn v(&self) -> u32 {
307 self.v_cur
308 }
309
310 #[inline(always)]
312 pub fn step(&mut self) {
313 self.z_cur = self.z_cur.wrapping_add_signed(self.z_step);
314 self.u_cur = self.u_cur.wrapping_add_signed(self.u_step);
315 self.v_cur = self.v_cur.wrapping_add_signed(self.v_step);
316 }
317
318 #[inline]
320 pub fn step_n(&mut self, n: i32) {
321 self.z_cur = self.z_cur.wrapping_add_signed(self.z_step.wrapping_mul(n));
322 self.u_cur = self.u_cur.wrapping_add_signed(self.u_step.wrapping_mul(n));
323 self.v_cur = self.v_cur.wrapping_add_signed(self.v_step.wrapping_mul(n));
324 }
325
326 #[inline(always)]
328 pub fn z_f32(&self) -> f32 {
329 self.z_cur as f32 / 65536.0
330 }
331}
332
333#[cfg(test)]
338mod tests {
339 extern crate std;
340 use super::*;
341
342 #[test]
345 fn roundtrip_f32_q16() {
346 let values = [0.0f32, 0.25, -0.5, 1.0, 3.14159, -100.0, 32767.0];
347 for v in values {
348 let q = to_q16(v);
349 let back = from_q16(q);
350 let lsb = 1.0 / 65536.0_f32;
351 assert!(
352 (back - v).abs() <= lsb,
353 "roundtrip failed for {v}: got {back}"
354 );
355 }
356 }
357
358 #[test]
359 fn roundtrip_i16_q16() {
360 for v in [-32768i16, -1, 0, 1, 100, 32767] {
361 let q = from_i16_q16(v);
362 let back = to_i16_q16(q);
363 assert_eq!(back, v, "i16 roundtrip failed for {v}");
364 }
365 }
366
367 #[test]
368 fn q31_q16_shifts() {
369 let q31_one = 0x7FFF_FFFFi32;
370 let q16 = q31_to_q16(q31_one);
371 let f = from_q16(q16);
372 assert!(
373 (f - 1.0).abs() < 1e-4,
374 "Q31->Q16 should be near 1.0, got {f}"
375 );
376 }
377
378 #[test]
381 fn mul_q16_basic() {
382 let a = to_q16(1.5);
383 let b = to_q16(2.0);
384 let result = from_q16(mul_q16(a, b));
385 assert!((result - 3.0).abs() < 1e-4, "1.5 * 2.0 = {result}");
386 }
387
388 #[test]
389 fn mul_q16_negative() {
390 let a = to_q16(-2.5);
391 let b = to_q16(4.0);
392 let result = from_q16(mul_q16(a, b));
393 assert!((result - (-10.0)).abs() < 1e-4, "-2.5 * 4.0 = {result}");
394 }
395
396 #[test]
397 fn mul_n_q16_integer_scale() {
398 let a = to_q16(3.5);
399 let result = from_q16(mul_n_q16(a, 4));
400 assert!((result - 14.0).abs() < 1e-4, "3.5 * 4 = {result}");
401 }
402
403 #[test]
404 fn mul_f_q16_float_scale() {
405 let a = to_q16(2.0);
406 let result = from_q16(mul_f_q16(a, 1.5));
407 assert!((result - 3.0).abs() < 1e-3, "2.0 * 1.5f = {result}");
408 }
409
410 #[test]
411 fn div_q16_basic() {
412 let result = from_q16(div_q16(to_q16(3.0), to_q16(2.0)));
413 assert!((result - 1.5).abs() < 1e-4, "3.0 / 2.0 = {result}");
414 }
415
416 #[test]
417 fn div_q16_zero_denominator() {
418 let result = div_q16(to_q16(5.0), 0);
419 assert_eq!(result, 0, "division by zero must return 0");
420 }
421
422 #[test]
423 fn div_n_q16_integer_divisor() {
424 let result = from_q16(div_n_q16(to_q16(9.0), 3));
425 assert!((result - 3.0).abs() < 1e-4, "9.0 / 3 = {result}");
426 }
427
428 #[test]
429 fn div_f_q16_float_divisor() {
430 let result = from_q16(div_f_q16(to_q16(6.0), 2.0));
431 assert!((result - 3.0).abs() < 1e-3, "6.0 / 2.0f = {result}");
432 }
433
434 #[test]
437 fn qadd_q16_no_overflow() {
438 assert_eq!(
439 from_q16(qadd_q16(to_q16(1.0), to_q16(2.0))).round() as i32,
440 3
441 );
442 }
443
444 #[test]
445 fn qadd_q16_saturates_at_max() {
446 let result = qadd_q16(Q16_MAX, Q16_MAX);
447 assert_eq!(result, Q16_MAX, "overflow must saturate at Q16_MAX");
448 }
449
450 #[test]
451 fn qsub_q16_saturates_at_min() {
452 let result = qsub_q16(Q16_MIN, Q16_MAX);
453 assert_eq!(result, Q16_MIN, "underflow must saturate at Q16_MIN");
454 }
455
456 #[test]
457 fn abs_q16_positive_unchanged() {
458 let v = to_q16(5.75);
459 assert_eq!(abs_q16(v), v);
460 }
461
462 #[test]
463 fn abs_q16_negated() {
464 let v = to_q16(-3.14);
465 let expected = to_q16(3.14);
466 assert_eq!(abs_q16(v), expected);
467 }
468
469 #[test]
472 fn lerp_q16_midpoint() {
473 let a = to_q16(0.0);
474 let b = to_q16(1.0);
475 let mid = from_q16(lerp_q16(a, b, 1, 2));
476 assert!((mid - 0.5).abs() < 1e-4, "lerp mid = {mid}");
477 }
478
479 #[test]
480 fn lerp_q16_endpoints() {
481 let a = to_q16(10.0);
482 let b = to_q16(20.0);
483 assert_eq!(lerp_q16(a, b, 0, 10), a, "t=0 must return left endpoint");
484 assert_eq!(
485 lerp_q16(a, b, 10, 10),
486 b,
487 "t=denom must return right endpoint"
488 );
489 }
490
491 #[test]
492 fn lerp_q16_zero_denom_returns_left() {
493 let a = to_q16(5.0);
494 let b = to_q16(9.0);
495 assert_eq!(lerp_q16(a, b, 0, 0), a, "zero denom must return left");
496 }
497
498 #[test]
499 fn angle_to_q16_90_degrees() {
500 let q = angle_to_q16(90.0);
501 let rad = from_q16(q);
502 assert!(
503 (rad - core::f32::consts::FRAC_PI_2).abs() < 1e-4,
504 "90 deg should be pi/2, got {rad}"
505 );
506 }
507
508 #[test]
509 fn angle_to_q16_360_degrees() {
510 let q = angle_to_q16(360.0);
511 let rad = from_q16(q);
512 assert!(
513 (rad - 2.0 * core::f32::consts::PI).abs() < 1e-4,
514 "360 deg should be 2*pi, got {rad}"
515 );
516 }
517
518 #[test]
519 fn recip_q16_one() {
520 let result = from_q16(recip_q16(FP_ONE));
521 assert!(
522 (result - 1.0).abs() < 0.01,
523 "recip(1.0) approx 1.0, got {result}"
524 );
525 }
526
527 #[test]
528 fn recip_q16_two() {
529 let result = from_q16(recip_q16(to_q16(2.0)));
530 assert!(
531 (result - 0.5).abs() < 0.01,
532 "recip(2.0) approx 0.5, got {result}"
533 );
534 }
535
536 #[test]
537 fn recip_q16_zero_returns_sentinel() {
538 assert_eq!(
539 recip_q16(0),
540 Q16_MAX,
541 "recip(0) must return Q16_MAX sentinel"
542 );
543 }
544
545 #[test]
548 fn legacy_shims_match_new_api() {
549 let a = to_q16(3.0);
550 let b = to_q16(4.0);
551 assert_eq!(to_fp(3.0), to_q16(3.0));
552 assert_eq!(from_fp(a), from_q16(a));
553 assert_eq!(mul_fp(a, b), mul_q16(a, b));
554 assert_eq!(div_fp(a, b), Some(div_q16(a, b)));
555 assert_eq!(div_fp(a, 0), None);
556 }
557
558 #[test]
561 fn scanline_interp_starts_at_left() {
562 let interp = ScanlineInterp::new(100, 200, 0, 65536, 0, 32768, 10);
563 assert_eq!(interp.z(), 100);
564 assert_eq!(interp.u(), 0);
565 assert_eq!(interp.v(), 0);
566 }
567
568 #[test]
569 fn scanline_interp_step_reaches_right() {
570 let mut interp = ScanlineInterp::depth_only(0, 65536, 10);
573 for _ in 0..10 {
574 interp.step();
575 }
576 let diff = (interp.z() as i64 - 65536i64).abs();
578 assert!(
579 diff <= 10,
580 "z after 10 steps should be within 10 of 65536, got {} (diff={})",
581 interp.z(),
582 diff
583 );
584 }
585
586 #[test]
587 fn scanline_interp_uv_reaches_right() {
588 let mut interp = ScanlineInterp::new(0, 0, 0, 65536, 0, 65536, 8);
589 for _ in 0..8 {
590 interp.step();
591 }
592 let u_err = (interp.u() as i64 - 65536i64).abs();
593 let v_err = (interp.v() as i64 - 65536i64).abs();
594 assert!(
595 u_err <= 1,
596 "u after 8 steps should be 65536 +/- 1, got {}",
597 interp.u()
598 );
599 assert!(
600 v_err <= 1,
601 "v after 8 steps should be 65536 +/- 1, got {}",
602 interp.v()
603 );
604 }
605
606 #[test]
607 fn scanline_interp_zero_span() {
608 let mut interp = ScanlineInterp::new(1000, 9999, 0, 65536, 0, 65536, 0);
609 interp.step();
610 interp.step();
611 assert_eq!(interp.z(), 1000, "zero-span z must not move");
612 assert_eq!(interp.u(), 0, "zero-span u must not move");
613 }
614
615 #[test]
616 fn scanline_interp_step_n() {
617 let mut interp = ScanlineInterp::depth_only(0, 100000, 10);
618 interp.step_n(5);
619 let expected = 50000u32;
620 let diff = (interp.z() as i64 - expected as i64).abs();
621 assert!(
622 diff <= 1,
623 "step_n(5) should land at 50000 +/- 1, got {}",
624 interp.z()
625 );
626 }
627
628 #[test]
629 fn scanline_interp_z_f32() {
630 let interp = ScanlineInterp::depth_only(65536, 65536, 0);
631 assert!((interp.z_f32() - 1.0).abs() < 1e-5, "z_f32 should be 1.0");
632 }
633
634 #[test]
637 fn fixed_roundtrip_is_close() {
638 let values = [0.0f32, 0.25, -0.5, 1.0, 3.14159];
639 for v in values {
640 let fp = to_fp(v);
641 let out = from_fp(fp);
642 assert!((out - v).abs() <= 1.0 / FP_ONE as f32);
643 }
644 }
645
646 #[test]
647 fn fixed_mul_div_behaves_as_expected() {
648 let a = to_fp(1.5);
649 let b = to_fp(2.0);
650 let prod = from_fp(mul_fp(a, b));
651 assert!((prod - 3.0).abs() < 0.001);
652
653 let quot = from_fp(div_fp(to_fp(3.0), to_fp(2.0)).unwrap());
654 assert!((quot - 1.5).abs() < 0.001);
655 }
656
657 #[test]
658 fn fixed_algebraic_identities() {
659 let val = to_fp(123.456);
660 let one = to_fp(1.0);
661 let zero = to_fp(0.0);
662 assert_eq!(mul_fp(val, one), val);
663 assert_eq!(mul_fp(val, zero), zero);
664 assert_eq!(div_fp(val, one), Some(val));
665 assert_eq!(div_fp(val, val), Some(one));
666 assert_eq!(div_fp(val, zero), None);
667 }
668
669 #[test]
670 fn fixed_signed_arithmetic_correctness() {
671 let pos = to_fp(4.25);
672 let neg = to_fp(-2.5);
673 let prod1 = from_fp(mul_fp(pos, neg));
674 assert!((prod1 - (-10.625)).abs() < 0.001);
675 let prod2 = from_fp(mul_fp(neg, neg));
676 assert!((prod2 - 6.25).abs() < 0.001);
677 let quot1 = from_fp(div_fp(pos, neg).unwrap());
678 assert!((quot1 - (-1.7)).abs() < 0.001);
679 }
680
681 #[test]
682 fn fixed_quantization_resolution_bound() {
683 let lsb = 1.0 / (FP_ONE as f32);
684 let val = 100.12345;
685 let fp = to_fp(val);
686 let reconstructed = from_fp(fp);
687 assert!((reconstructed - val).abs() <= lsb);
688 }
689}