1use crate::expression::{DivExpression, MulExpression};
6use crate::fixed::{Fixed, PromoteAddSub, PromoteMul};
7use crate::num_traits::{
8 self, Bounded, CheckedDiv, SaturatingAdd, SaturatingMul, SaturatingSub, Zero,
9};
10use crate::utility::{clamp_cast, round_half_to_even};
11
12mod private {
13 pub trait Sealed {}
14 impl Sealed for i8 {}
15 impl Sealed for i16 {}
16 impl Sealed for i32 {}
17 impl Sealed for i64 {}
18 impl Sealed for u8 {}
19 impl Sealed for u16 {}
20 impl Sealed for u32 {}
21 impl Sealed for u64 {}
22}
23
24pub trait FastInt:
25 private::Sealed + Copy + num_traits::NumCast + num_traits::Bounded + PartialOrd + num_traits::Zero
26{
27 type Intermediate: num_traits::PrimInt
28 + num_traits::Bounded
29 + num_traits::SaturatingAdd
30 + num_traits::SaturatingSub
31 + num_traits::SaturatingMul
32 + num_traits::CheckedDiv
33 + num_traits::NumCast;
34}
35
36impl FastInt for i8 {
37 type Intermediate = i64;
38}
39impl FastInt for i16 {
40 type Intermediate = i64;
41}
42impl FastInt for i32 {
43 type Intermediate = i64;
44}
45impl FastInt for i64 {
46 type Intermediate = i64;
47}
48
49impl FastInt for u8 {
50 type Intermediate = u64;
51}
52impl FastInt for u16 {
53 type Intermediate = u64;
54}
55impl FastInt for u32 {
56 type Intermediate = u64;
57}
58impl FastInt for u64 {
59 type Intermediate = u64;
60}
61
62#[inline]
63pub(crate) fn saturating_shl<I>(val: I, bits: usize) -> I
64where
65 I: num_traits::PrimInt + num_traits::Bounded + num_traits::SaturatingMul,
66{
67 if bits == 0 || val == I::zero() {
68 return val;
69 }
70 let integer_bits = core::mem::size_of::<I>() * 8;
71 let limit = if I::min_value() < I::zero() { integer_bits - 1 } else { integer_bits };
72 if bits >= limit {
73 if I::min_value() < I::zero() {
74 return if val > I::zero() { I::max_value() } else { I::min_value() };
76 } else {
77 return I::max_value();
79 }
80 }
81 let shift_factor = I::one() << bits;
82 val.saturating_mul(&shift_factor)
83}
84
85#[inline]
86pub(crate) fn div_power_of_two<I>(val: I, bits: usize) -> I
87where
88 I: num_traits::PrimInt + num_traits::Bounded,
89{
90 let integer_bits = core::mem::size_of::<I>() * 8;
91 let limit = if I::min_value() < I::zero() { integer_bits - 1 } else { integer_bits };
92 if bits == integer_bits - 1 && val == I::min_value() && I::min_value() < I::zero() {
93 I::zero().saturating_sub(&I::one())
94 } else if bits >= limit {
95 I::zero()
96 } else {
97 val / (I::one() << bits)
98 }
99}
100
101#[repr(transparent)]
116#[derive(Clone, Copy, Default, Hash)]
117#[allow(clippy::derived_hash_with_manual_eq)]
118pub struct FastFixed<I, const FRAC: usize>(pub Fixed<I, FRAC>);
119
120impl<I: PartialEq + Copy, const FRAC: usize> PartialEq for FastFixed<I, FRAC> {
121 #[inline]
122 fn eq(&self, other: &Self) -> bool {
123 self.0.raw_value() == other.0.raw_value()
124 }
125}
126
127impl<I: Eq + Copy, const FRAC: usize> Eq for FastFixed<I, FRAC> {}
128
129impl<I: PartialOrd + Copy, const FRAC: usize> PartialOrd for FastFixed<I, FRAC> {
130 #[inline]
131 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
132 self.0.raw_value().partial_cmp(&other.0.raw_value())
133 }
134}
135
136impl<I: Ord + Copy, const FRAC: usize> Ord for FastFixed<I, FRAC> {
137 #[inline]
138 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
139 self.0.raw_value().cmp(&other.0.raw_value())
140 }
141}
142
143impl<I, const FRAC: usize> core::fmt::Debug for FastFixed<I, FRAC>
144where
145 I: Into<i128> + Copy + core::fmt::Debug,
146{
147 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
148 let s = crate::string::String::new(self.0, crate::string::Mode::Dec, None);
149 write!(f, "FastFixed(raw: {:?}, value: {})", self.0.raw_value(), s)
150 }
151}
152
153impl<I: Copy, const FRAC: usize> FastFixed<I, FRAC> {
154 pub const fn from_raw(value: I) -> Self {
156 Self(Fixed::from_raw(value))
157 }
158
159 pub const fn raw_value(&self) -> I {
161 self.0.raw_value()
162 }
163
164 pub const fn into_fixed(self) -> Fixed<I, FRAC> {
166 self.0
167 }
168}
169
170impl<I: Bounded + num_traits::NumCast + Copy, const FRAC: usize> FastFixed<I, FRAC> {
171 pub fn min() -> Self {
173 Self(Fixed::min())
174 }
175
176 pub fn max() -> Self {
178 Self(Fixed::max())
179 }
180}
181
182impl<I: Copy, const FRAC: usize> From<Fixed<I, FRAC>> for FastFixed<I, FRAC> {
183 fn from(val: Fixed<I, FRAC>) -> Self {
184 Self(val)
185 }
186}
187
188impl<I: Copy, const FRAC: usize> From<FastFixed<I, FRAC>> for Fixed<I, FRAC> {
189 fn from(val: FastFixed<I, FRAC>) -> Self {
190 val.0
191 }
192}
193
194impl<I, const FRAC: usize> core::ops::Neg for FastFixed<I, FRAC>
196where
197 I: num_traits::Signed
198 + num_traits::Bounded
199 + num_traits::Zero
200 + PartialOrd
201 + num_traits::NumCast
202 + Copy
203 + FastInt,
204{
205 type Output = Self;
206
207 fn neg(self) -> Self::Output {
208 type Intermediate<I> = <I as FastInt>::Intermediate;
209 let val_64: Intermediate<I> = clamp_cast(self.0.raw_value());
210 let negated = Intermediate::<I>::zero().saturating_sub(&val_64);
211 Self(Fixed::from_raw(clamp_cast(negated)))
212 }
213}
214
215impl<T, const FRAC: usize, U, const FRAC_R: usize> core::ops::Add<FastFixed<U, FRAC_R>>
217 for FastFixed<T, FRAC>
218where
219 T: PromoteAddSub<U> + num_traits::Zero + PartialOrd + num_traits::NumCast + Copy,
220 U: num_traits::Zero + PartialOrd + num_traits::NumCast + Copy,
221 <T as PromoteAddSub<U>>::Output: FastInt,
222{
223 type Output = FastFixed<<T as PromoteAddSub<U>>::Output, FRAC>;
224
225 fn add(self, rhs: FastFixed<U, FRAC_R>) -> Self::Output {
226 type Out<T, U> = <T as PromoteAddSub<U>>::Output;
227 type I64<T, U> = <Out<T, U> as FastInt>::Intermediate;
228
229 let val_l: I64<T, U> = clamp_cast(self.0.raw_value());
230 let val_r: I64<T, U> = clamp_cast(rhs.0.raw_value());
231
232 let sum = if FRAC > FRAC_R {
234 let delta = FRAC - FRAC_R;
235 let val_r_shifted = saturating_shl(val_r, delta);
236 val_l.saturating_add(&val_r_shifted)
237 } else if FRAC < FRAC_R {
238 let delta = FRAC_R - FRAC;
239 let val_l_shifted = saturating_shl(val_l, delta);
240 let sum_aligned = val_l_shifted.saturating_add(&val_r);
241 let rounded = round_half_to_even::<I64<T, U>>(sum_aligned, delta);
242 div_power_of_two(rounded, delta)
243 } else {
244 val_l.saturating_add(&val_r)
245 };
246
247 FastFixed(Fixed::from_raw(clamp_cast(sum)))
248 }
249}
250
251impl<T, const FRAC: usize, U, const FRAC_R: usize> core::ops::Sub<FastFixed<U, FRAC_R>>
253 for FastFixed<T, FRAC>
254where
255 T: PromoteAddSub<U> + num_traits::Zero + PartialOrd + num_traits::NumCast + Copy,
256 U: num_traits::Zero + PartialOrd + num_traits::NumCast + Copy,
257 <T as PromoteAddSub<U>>::Output: FastInt,
258{
259 type Output = FastFixed<<T as PromoteAddSub<U>>::Output, FRAC>;
260
261 fn sub(self, rhs: FastFixed<U, FRAC_R>) -> Self::Output {
262 type Out<T, U> = <T as PromoteAddSub<U>>::Output;
263 type I64<T, U> = <Out<T, U> as FastInt>::Intermediate;
264
265 let val_l: I64<T, U> = clamp_cast(self.0.raw_value());
266 let val_r: I64<T, U> = clamp_cast(rhs.0.raw_value());
267
268 let diff = if FRAC > FRAC_R {
270 let delta = FRAC - FRAC_R;
271 let val_r_shifted = saturating_shl(val_r, delta);
272 val_l.saturating_sub(&val_r_shifted)
273 } else if FRAC < FRAC_R {
274 let delta = FRAC_R - FRAC;
275 let val_l_shifted = saturating_shl(val_l, delta);
276 let diff_aligned = val_l_shifted.saturating_sub(&val_r);
277 let rounded = round_half_to_even::<I64<T, U>>(diff_aligned, delta);
278 div_power_of_two(rounded, delta)
279 } else {
280 val_l.saturating_sub(&val_r)
281 };
282
283 FastFixed(Fixed::from_raw(clamp_cast(diff)))
284 }
285}
286
287impl<T, const FL: usize, U, const FR: usize> core::ops::Mul<FastFixed<U, FR>> for FastFixed<T, FL> {
289 type Output = MulExpression<FastFixed<T, FL>, FastFixed<U, FR>>;
290
291 fn mul(self, rhs: FastFixed<U, FR>) -> Self::Output {
292 MulExpression { lhs: self, rhs }
293 }
294}
295
296pub trait FastExpression: Copy {
307 type Int: num_traits::PrimInt
309 + Bounded
310 + SaturatingAdd
311 + SaturatingSub
312 + SaturatingMul
313 + num_traits::NumCast
314 + crate::num_traits::FixedInt;
315
316 const FRAC: usize;
318
319 fn evaluate_raw(self) -> Self::Int;
321
322 fn evaluate<I, const FRAC_OUT: usize>(self) -> FastFixed<I, FRAC_OUT>
325 where
326 I: Bounded + num_traits::NumCast + Copy,
327 {
328 FastFixed(Fixed::from_raw(clamp_cast(rescale(self.evaluate_raw(), Self::FRAC, FRAC_OUT))))
329 }
330}
331
332#[inline]
335fn rescale<I>(value: I, src_frac: usize, dst_frac: usize) -> I
336where
337 I: num_traits::PrimInt + Bounded + SaturatingMul + crate::num_traits::FixedInt,
338{
339 if src_frac > dst_frac {
340 let delta = src_frac - dst_frac;
341 div_power_of_two(round_half_to_even::<I>(value, delta), delta)
342 } else if src_frac < dst_frac {
343 saturating_shl(value, dst_frac - src_frac)
344 } else {
345 value
346 }
347}
348
349impl<T, const FL: usize, U, const FR: usize> FastExpression
350 for MulExpression<FastFixed<T, FL>, FastFixed<U, FR>>
351where
352 T: PromoteMul<U> + num_traits::Zero + PartialOrd + num_traits::NumCast + Copy,
353 U: num_traits::Zero + PartialOrd + num_traits::NumCast + Copy,
354 <T as PromoteMul<U>>::Output: FastInt,
355{
356 type Int = <<T as PromoteMul<U>>::Output as FastInt>::Intermediate;
357 const FRAC: usize = FL + FR;
358
359 fn evaluate_raw(self) -> Self::Int {
360 let val_l: Self::Int = clamp_cast(self.lhs.0.raw_value());
361 let val_r: Self::Int = clamp_cast(self.rhs.0.raw_value());
362 val_l.saturating_mul(&val_r)
363 }
364}
365
366#[derive(Clone, Copy)]
371pub struct SumExpression<L, R> {
372 lhs: L,
373 rhs: R,
374 subtract: bool,
375}
376
377impl<L, R> FastExpression for SumExpression<L, R>
378where
379 L: FastExpression,
380 R: FastExpression<Int = L::Int>,
381{
382 type Int = L::Int;
383 const FRAC: usize = if L::FRAC < R::FRAC { L::FRAC } else { R::FRAC };
384
385 fn evaluate_raw(self) -> Self::Int {
386 let lhs = rescale(self.lhs.evaluate_raw(), L::FRAC, Self::FRAC);
387 let rhs = rescale(self.rhs.evaluate_raw(), R::FRAC, Self::FRAC);
388 if self.subtract { lhs.saturating_sub(&rhs) } else { lhs.saturating_add(&rhs) }
389 }
390}
391
392impl<I, const FRAC_OUT: usize, L, R> From<MulExpression<L, R>> for FastFixed<I, FRAC_OUT>
394where
395 I: Bounded + num_traits::NumCast + Copy,
396 MulExpression<L, R>: FastExpression,
397{
398 fn from(expr: MulExpression<L, R>) -> Self {
399 expr.evaluate()
400 }
401}
402
403impl<I, const FRAC_OUT: usize, L, R> From<SumExpression<L, R>> for FastFixed<I, FRAC_OUT>
404where
405 I: Bounded + num_traits::NumCast + Copy,
406 SumExpression<L, R>: FastExpression,
407{
408 fn from(expr: SumExpression<L, R>) -> Self {
409 expr.evaluate()
410 }
411}
412
413impl<I, const FRAC: usize, E> PartialEq<E> for FastFixed<I, FRAC>
416where
417 I: Bounded + num_traits::NumCast + PartialEq + Copy,
418 E: FastExpression,
419{
420 fn eq(&self, other: &E) -> bool {
421 *self == other.evaluate::<I, FRAC>()
422 }
423}
424
425impl<I, const FRAC: usize, E> PartialOrd<E> for FastFixed<I, FRAC>
426where
427 I: Bounded + num_traits::NumCast + PartialOrd + Copy,
428 E: FastExpression,
429{
430 fn partial_cmp(&self, other: &E) -> Option<core::cmp::Ordering> {
431 self.partial_cmp(&other.evaluate::<I, FRAC>())
432 }
433}
434
435macro_rules! impl_fast_fixed_expression_add_sub {
441 ($Trait:ident, $method:ident, $op:tt) => {
442 impl<I, const FRAC: usize, E> core::ops::$Trait<E> for FastFixed<I, FRAC>
443 where
444 I: Bounded + num_traits::NumCast + Copy,
445 E: FastExpression,
446 FastFixed<I, FRAC>: core::ops::$Trait<FastFixed<I, FRAC>>,
447 {
448 type Output = <FastFixed<I, FRAC> as core::ops::$Trait<FastFixed<I, FRAC>>>::Output;
449
450 fn $method(self, rhs: E) -> Self::Output {
451 self $op rhs.evaluate::<I, FRAC>()
452 }
453 }
454 };
455}
456
457impl_fast_fixed_expression_add_sub!(Add, add, +);
458impl_fast_fixed_expression_add_sub!(Sub, sub, -);
459
460macro_rules! impl_fast_expression_ops {
464 ($Expr:ident) => {
465 impl<I, const FRAC: usize, L, R> PartialEq<FastFixed<I, FRAC>> for $Expr<L, R>
466 where
467 I: Bounded + num_traits::NumCast + PartialEq + Copy,
468 $Expr<L, R>: FastExpression,
469 {
470 fn eq(&self, other: &FastFixed<I, FRAC>) -> bool {
471 self.evaluate::<I, FRAC>() == *other
472 }
473 }
474
475 impl<I, const FRAC: usize, L, R> PartialOrd<FastFixed<I, FRAC>> for $Expr<L, R>
476 where
477 I: Bounded + num_traits::NumCast + PartialOrd + Copy,
478 $Expr<L, R>: FastExpression,
479 {
480 fn partial_cmp(&self, other: &FastFixed<I, FRAC>) -> Option<core::cmp::Ordering> {
481 self.evaluate::<I, FRAC>().partial_cmp(other)
482 }
483 }
484
485 impl_fast_expression_ops!(@add_sub $Expr, Add, add, +, false);
486 impl_fast_expression_ops!(@add_sub $Expr, Sub, sub, -, true);
487 };
488 (@add_sub $Expr:ident, $Trait:ident, $method:ident, $op:tt, $subtract:expr) => {
489 impl<I, const FRAC: usize, L, R> core::ops::$Trait<FastFixed<I, FRAC>> for $Expr<L, R>
490 where
491 I: Bounded + num_traits::NumCast + Copy,
492 $Expr<L, R>: FastExpression,
493 FastFixed<I, FRAC>: core::ops::$Trait<FastFixed<I, FRAC>>,
494 {
495 type Output = <FastFixed<I, FRAC> as core::ops::$Trait<FastFixed<I, FRAC>>>::Output;
496
497 fn $method(self, rhs: FastFixed<I, FRAC>) -> Self::Output {
498 self.evaluate::<I, FRAC>() $op rhs
499 }
500 }
501
502 impl<L, R, E> core::ops::$Trait<E> for $Expr<L, R>
503 where
504 $Expr<L, R>: FastExpression,
505 E: FastExpression,
506 {
507 type Output = SumExpression<$Expr<L, R>, E>;
508
509 fn $method(self, rhs: E) -> Self::Output {
510 SumExpression { lhs: self, rhs, subtract: $subtract }
511 }
512 }
513 };
514}
515
516impl_fast_expression_ops!(MulExpression);
517impl_fast_expression_ops!(SumExpression);
518
519impl<R, const FRAC_OUT: usize> FastFixed<R, FRAC_OUT>
521where
522 R: Bounded + num_traits::NumCast + Copy,
523{
524 pub fn from_fixed<OtherI: Copy, const OTHER_FRAC: usize>(
530 other: FastFixed<OtherI, OTHER_FRAC>,
531 ) -> Self
532 where
533 crate::fixed_format::Value<OtherI, OTHER_FRAC>:
534 crate::fixed_format::ConvertValue<R, FRAC_OUT>,
535 {
536 FastFixed(Fixed::from_fixed(other.0))
537 }
538}
539
540impl<IL, const FL: usize, IR, const FR: usize> core::ops::Div<FastFixed<IR, FR>>
542 for FastFixed<IL, FL>
543{
544 type Output = DivExpression<FastFixed<IL, FL>, FastFixed<IR, FR>>;
545
546 fn div(self, rhs: FastFixed<IR, FR>) -> Self::Output {
547 DivExpression { numerator: self, denominator: rhs }
548 }
549}
550
551impl<IL, const FL: usize, IR, const FR: usize> DivExpression<FastFixed<IL, FL>, FastFixed<IR, FR>> {
552 pub fn evaluate<I, const TARGET_FRAC: usize>(self) -> FastFixed<I, TARGET_FRAC>
558 where
559 FastFixed<I, TARGET_FRAC>: From<Self>,
560 {
561 FastFixed::from(self)
562 }
563}
564
565impl<I, const TARGET_FRAC: usize, IL, const FL: usize, IR, const FR: usize>
567 From<DivExpression<FastFixed<IL, FL>, FastFixed<IR, FR>>> for FastFixed<I, TARGET_FRAC>
568where
569 I: num_traits::Bounded + num_traits::NumCast + Copy + FastInt,
570 IL: num_traits::Zero + PartialOrd + num_traits::NumCast + Copy,
571 IR: num_traits::Zero + PartialOrd + num_traits::NumCast + Copy,
572{
573 fn from(expr: DivExpression<FastFixed<IL, FL>, FastFixed<IR, FR>>) -> Self {
574 type I64<I> = <I as FastInt>::Intermediate;
575 let num_raw: I64<I> = clamp_cast(expr.numerator.0.raw_value());
576 let denom_raw: I64<I> = clamp_cast(expr.denominator.0.raw_value());
577
578 let dst_frac = TARGET_FRAC + FR;
579
580 let num_scaled = if FL > dst_frac {
581 let delta = FL - dst_frac;
582 let rounded = round_half_to_even::<I64<I>>(num_raw, delta);
583 div_power_of_two(rounded, delta)
584 } else if FL < dst_frac {
585 let delta = dst_frac - FL;
586 saturating_shl(num_raw, delta)
587 } else {
588 num_raw
589 };
590
591 if denom_raw == I64::<I>::zero() {
592 panic!("Division by zero in DivExpression");
593 }
594 let quotient = if let Some(q) = num_scaled.checked_div(&denom_raw) {
595 q
596 } else {
597 I64::<I>::max_value()
598 };
599 FastFixed(Fixed::from_raw(clamp_cast(quotient)))
600 }
601}
602
603impl<I, const TARGET_FRAC: usize, IL, const FL: usize, IR, const FR: usize>
604 From<DivExpression<Fixed<IL, FL>, Fixed<IR, FR>>> for FastFixed<I, TARGET_FRAC>
605where
606 FastFixed<I, TARGET_FRAC>: From<DivExpression<FastFixed<IL, FL>, FastFixed<IR, FR>>>,
607{
608 fn from(expr: DivExpression<Fixed<IL, FL>, Fixed<IR, FR>>) -> Self {
609 Self::from(DivExpression {
610 numerator: FastFixed(expr.numerator),
611 denominator: FastFixed(expr.denominator),
612 })
613 }
614}
615
616impl<I, const FRAC: usize, U, const FRAC_R: usize> core::ops::AddAssign<FastFixed<U, FRAC_R>>
618 for FastFixed<I, FRAC>
619where
620 I: PromoteAddSub<U>
621 + num_traits::Zero
622 + PartialOrd
623 + num_traits::Bounded
624 + num_traits::NumCast
625 + Copy
626 + FastInt
627 + TryFrom<<I as PromoteAddSub<U>>::Output>,
628 <I as PromoteAddSub<U>>::Output: FastInt,
629 U: num_traits::Zero + PartialOrd + num_traits::NumCast + Copy,
630 FastFixed<I, FRAC>: core::ops::Add<
631 FastFixed<U, FRAC_R>,
632 Output = FastFixed<<I as PromoteAddSub<U>>::Output, FRAC>,
633 >,
634{
635 fn add_assign(&mut self, rhs: FastFixed<U, FRAC_R>) {
636 let sum = *self + rhs;
637 self.0 = Fixed::from_raw(clamp_cast(sum.0.raw_value()));
638 }
639}
640
641impl<I, const FRAC: usize, U, const FRAC_R: usize> core::ops::SubAssign<FastFixed<U, FRAC_R>>
642 for FastFixed<I, FRAC>
643where
644 I: PromoteAddSub<U>
645 + num_traits::Zero
646 + PartialOrd
647 + num_traits::Bounded
648 + num_traits::NumCast
649 + Copy
650 + FastInt
651 + TryFrom<<I as PromoteAddSub<U>>::Output>,
652 <I as PromoteAddSub<U>>::Output: FastInt,
653 U: num_traits::Zero + PartialOrd + num_traits::NumCast + Copy,
654 FastFixed<I, FRAC>: core::ops::Sub<
655 FastFixed<U, FRAC_R>,
656 Output = FastFixed<<I as PromoteAddSub<U>>::Output, FRAC>,
657 >,
658{
659 fn sub_assign(&mut self, rhs: FastFixed<U, FRAC_R>) {
660 let diff = *self - rhs;
661 self.0 = Fixed::from_raw(clamp_cast(diff.0.raw_value()));
662 }
663}
664
665impl<I, const FRAC: usize> core::ops::MulAssign<FastFixed<I, FRAC>> for FastFixed<I, FRAC>
666where
667 I: PromoteMul<I>
668 + num_traits::Zero
669 + PartialOrd
670 + num_traits::Bounded
671 + num_traits::NumCast
672 + Copy
673 + FastInt
674 + TryFrom<<I as PromoteMul<I>>::Output>,
675 <I as PromoteMul<I>>::Output: FastInt,
676 FastFixed<I, FRAC>:
677 core::ops::Mul<FastFixed<I, FRAC>, Output = FastFixed<<I as PromoteMul<I>>::Output, FRAC>>,
678{
679 fn mul_assign(&mut self, rhs: Self) {
680 let prod = *self * rhs;
681 self.0 = Fixed::from_raw(clamp_cast(prod.0.raw_value()));
682 }
683}
684
685impl<I, const FRAC: usize> core::ops::DivAssign<FastFixed<I, FRAC>> for FastFixed<I, FRAC>
686where
687 Self: Copy,
688 I: num_traits::Zero + PartialOrd,
689 FastFixed<I, FRAC>: From<DivExpression<Self, Self>>,
690{
691 fn div_assign(&mut self, rhs: Self) {
692 *self = Self::from(DivExpression { numerator: *self, denominator: rhs });
693 }
694}
695
696impl<I, const FRAC: usize> core::fmt::Display for FastFixed<I, FRAC>
698where
699 I: Into<i128> + Copy,
700{
701 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
702 let s = crate::string::String::new(self.0, crate::string::Mode::Dec, f.precision());
703 write!(f, "{}", s)
704 }
705}
706
707impl<I, const FRAC: usize> core::fmt::LowerHex for FastFixed<I, FRAC>
708where
709 I: Into<i128> + Copy,
710{
711 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
712 let s = crate::string::String::new(self.0, crate::string::Mode::Hex, Some(0));
713 write!(f, "{}", s)
714 }
715}
716
717macro_rules! impl_fast_fixed_integer_comparison {
719 ($T:ty, $Int:ty) => {
720 impl<const FRAC: usize> PartialEq<$Int> for FastFixed<$T, FRAC>
721 where
722 Fixed<$T, FRAC>: PartialEq<$Int>,
723 {
724 fn eq(&self, other: &$Int) -> bool {
725 self.0 == *other
726 }
727 }
728
729 impl<const FRAC: usize> PartialEq<FastFixed<$T, FRAC>> for $Int
730 where
731 Fixed<$T, FRAC>: PartialEq<$Int>,
732 {
733 fn eq(&self, other: &FastFixed<$T, FRAC>) -> bool {
734 other.0 == *self
735 }
736 }
737
738 impl<const FRAC: usize> PartialOrd<$Int> for FastFixed<$T, FRAC>
739 where
740 Fixed<$T, FRAC>: PartialOrd<$Int>,
741 {
742 fn partial_cmp(&self, other: &$Int) -> Option<core::cmp::Ordering> {
743 self.0.partial_cmp(other)
744 }
745 }
746
747 impl<const FRAC: usize> PartialOrd<FastFixed<$T, FRAC>> for $Int
748 where
749 Fixed<$T, FRAC>: PartialOrd<$Int>,
750 {
751 fn partial_cmp(&self, other: &FastFixed<$T, FRAC>) -> Option<core::cmp::Ordering> {
752 other.0.partial_cmp(self).map(|o| o.reverse())
753 }
754 }
755 };
756}
757
758macro_rules! gen_fast_fixed_integer_comparisons {
759 ($label:ident: [$($T:ty),*], $Int_list:tt) => {
760 $(
761 gen_fast_fixed_integer_comparisons!(@one $T, $Int_list);
762 )*
763 };
764 (@one $T:ty, [$($Int:ty),*]) => {
765 $(
766 impl_fast_fixed_integer_comparison!($T, $Int);
767 )*
768 };
769}
770
771gen_fast_fixed_integer_comparisons!(signed: [i8, i16, i32, i64], [i8, i16, i32, i64]);
772gen_fast_fixed_integer_comparisons!(unsigned: [u8, u16, u32, u64], [u8, u16, u32, u64]);
773gen_fast_fixed_integer_comparisons!(mixed: [i8, i16, i32, i64], [u8, u16, u32, u64]);
774gen_fast_fixed_integer_comparisons!(mixed: [u8, u16, u32, u64], [i8, i16, i32, i64]);
775
776macro_rules! impl_fast_fixed_integer_arithmetic {
778 ($T:ty, $Int:ty) => {
779 impl<const FRAC: usize> core::ops::Add<$Int> for FastFixed<$T, FRAC>
780 where
781 $T: PromoteAddSub<$Int> + num_traits::NumCast + Copy,
782 $Int: num_traits::NumCast + Copy,
783 <$T as PromoteAddSub<$Int>>::Output:
784 num_traits::Bounded + num_traits::NumCast + Copy + FastInt,
785 {
786 type Output = FastFixed<<$T as PromoteAddSub<$Int>>::Output, FRAC>;
787
788 fn add(self, rhs: $Int) -> Self::Output {
789 type Out = <$T as PromoteAddSub<$Int>>::Output;
790 type I64 = <Out as FastInt>::Intermediate;
791 let val_l: I64 = clamp_cast(self.0.raw_value());
792 let val_r: I64 = clamp_cast(rhs);
793 let val_r_shifted = saturating_shl(val_r, FRAC);
794 let sum = val_l.saturating_add(val_r_shifted);
795 FastFixed(Fixed::from_raw(clamp_cast(sum)))
796 }
797 }
798
799 impl<const FRAC: usize> core::ops::Add<FastFixed<$T, FRAC>> for $Int
800 where
801 $T: PromoteAddSub<$Int> + num_traits::NumCast + Copy,
802 $Int: num_traits::NumCast + Copy,
803 <$T as PromoteAddSub<$Int>>::Output:
804 num_traits::Bounded + num_traits::NumCast + Copy + FastInt,
805 {
806 type Output = FastFixed<<$T as PromoteAddSub<$Int>>::Output, FRAC>;
807
808 fn add(self, rhs: FastFixed<$T, FRAC>) -> Self::Output {
809 type Out = <$T as PromoteAddSub<$Int>>::Output;
810 type I64 = <Out as FastInt>::Intermediate;
811 let val_l: I64 = clamp_cast(self);
812 let val_r: I64 = clamp_cast(rhs.0.raw_value());
813 let val_l_shifted = saturating_shl(val_l, FRAC);
814 let sum = val_l_shifted.saturating_add(val_r);
815 FastFixed(Fixed::from_raw(clamp_cast(sum)))
816 }
817 }
818
819 impl<const FRAC: usize> core::ops::Sub<$Int> for FastFixed<$T, FRAC>
820 where
821 $T: PromoteAddSub<$Int> + num_traits::NumCast + Copy,
822 $Int: num_traits::NumCast + Copy,
823 <$T as PromoteAddSub<$Int>>::Output:
824 num_traits::Bounded + num_traits::NumCast + Copy + FastInt,
825 {
826 type Output = FastFixed<<$T as PromoteAddSub<$Int>>::Output, FRAC>;
827
828 fn sub(self, rhs: $Int) -> Self::Output {
829 type Out = <$T as PromoteAddSub<$Int>>::Output;
830 type I64 = <Out as FastInt>::Intermediate;
831 let val_l: I64 = clamp_cast(self.0.raw_value());
832 let val_r: I64 = clamp_cast(rhs);
833 let val_r_shifted = saturating_shl(val_r, FRAC);
834 let diff = val_l.saturating_sub(val_r_shifted);
835 FastFixed(Fixed::from_raw(clamp_cast(diff)))
836 }
837 }
838
839 impl<const FRAC: usize> core::ops::Sub<FastFixed<$T, FRAC>> for $Int
840 where
841 $T: PromoteAddSub<$Int> + num_traits::NumCast + Copy,
842 $Int: num_traits::NumCast + Copy,
843 <$T as PromoteAddSub<$Int>>::Output:
844 num_traits::Bounded + num_traits::NumCast + Copy + FastInt,
845 {
846 type Output = FastFixed<<$T as PromoteAddSub<$Int>>::Output, FRAC>;
847
848 fn sub(self, rhs: FastFixed<$T, FRAC>) -> Self::Output {
849 type Out = <$T as PromoteAddSub<$Int>>::Output;
850 type I64 = <Out as FastInt>::Intermediate;
851 let val_l: I64 = clamp_cast(self);
852 let val_r: I64 = clamp_cast(rhs.0.raw_value());
853 let val_l_shifted = saturating_shl(val_l, FRAC);
854 let diff = val_l_shifted.saturating_sub(val_r);
855 FastFixed(Fixed::from_raw(clamp_cast(diff)))
856 }
857 }
858
859 impl<const FRAC: usize> core::ops::Mul<$Int> for FastFixed<$T, FRAC>
860 where
861 $T: PromoteMul<$Int> + num_traits::Zero + PartialOrd + num_traits::NumCast + Copy,
862 $Int: num_traits::Zero + PartialOrd + num_traits::NumCast + Copy,
863 <$T as PromoteMul<$Int>>::Output:
864 num_traits::Bounded + num_traits::NumCast + Copy + FastInt,
865 {
866 type Output = FastFixed<<$T as PromoteMul<$Int>>::Output, FRAC>;
867
868 fn mul(self, rhs: $Int) -> Self::Output {
869 type Out = <$T as PromoteMul<$Int>>::Output;
870 type I64 = <Out as FastInt>::Intermediate;
871 let val_l: I64 = clamp_cast(self.0.raw_value());
872 let val_r: I64 = clamp_cast(rhs);
873 let prod = val_l.saturating_mul(val_r);
874 FastFixed(Fixed::from_raw(clamp_cast(prod)))
875 }
876 }
877
878 impl<const FRAC: usize> core::ops::Mul<FastFixed<$T, FRAC>> for $Int
879 where
880 $T: PromoteMul<$Int> + num_traits::Zero + PartialOrd + num_traits::NumCast + Copy,
881 $Int: num_traits::Zero + PartialOrd + num_traits::NumCast + Copy,
882 <$T as PromoteMul<$Int>>::Output:
883 num_traits::Bounded + num_traits::NumCast + Copy + FastInt,
884 {
885 type Output = FastFixed<<$T as PromoteMul<$Int>>::Output, FRAC>;
886
887 fn mul(self, rhs: FastFixed<$T, FRAC>) -> Self::Output {
888 rhs.mul(self)
889 }
890 }
891
892 impl<const FRAC: usize> core::ops::AddAssign<$Int> for FastFixed<$T, FRAC>
893 where
894 $T: PromoteAddSub<$Int>
895 + num_traits::Zero
896 + PartialOrd
897 + num_traits::Bounded
898 + num_traits::NumCast
899 + Copy
900 + FastInt,
901 <$T as PromoteAddSub<$Int>>::Output: FastInt,
902 $Int: num_traits::Zero + PartialOrd + num_traits::NumCast + Copy,
903 FastFixed<$T, FRAC>:
904 core::ops::Add<$Int, Output = FastFixed<<$T as PromoteAddSub<$Int>>::Output, FRAC>>,
905 {
906 fn add_assign(&mut self, rhs: $Int) {
907 let sum = *self + rhs;
908 self.0 = Fixed::from_raw(clamp_cast(sum.0.raw_value()));
909 }
910 }
911
912 impl<const FRAC: usize> core::ops::SubAssign<$Int> for FastFixed<$T, FRAC>
913 where
914 $T: PromoteAddSub<$Int>
915 + num_traits::Zero
916 + PartialOrd
917 + num_traits::Bounded
918 + num_traits::NumCast
919 + Copy
920 + FastInt,
921 <$T as PromoteAddSub<$Int>>::Output: FastInt,
922 $Int: num_traits::Zero + PartialOrd + num_traits::NumCast + Copy,
923 FastFixed<$T, FRAC>:
924 core::ops::Sub<$Int, Output = FastFixed<<$T as PromoteAddSub<$Int>>::Output, FRAC>>,
925 {
926 fn sub_assign(&mut self, rhs: $Int) {
927 let diff = *self - rhs;
928 self.0 = Fixed::from_raw(clamp_cast(diff.0.raw_value()));
929 }
930 }
931
932 impl<const FRAC: usize> core::ops::MulAssign<$Int> for FastFixed<$T, FRAC>
933 where
934 $T: PromoteMul<$Int>
935 + num_traits::Zero
936 + PartialOrd
937 + num_traits::Bounded
938 + num_traits::NumCast
939 + Copy
940 + FastInt,
941 <$T as PromoteMul<$Int>>::Output: FastInt,
942 $Int: num_traits::Zero + PartialOrd + num_traits::NumCast + Copy,
943 FastFixed<$T, FRAC>:
944 core::ops::Mul<$Int, Output = FastFixed<<$T as PromoteMul<$Int>>::Output, FRAC>>,
945 {
946 fn mul_assign(&mut self, rhs: $Int) {
947 let prod = *self * rhs;
948 self.0 = Fixed::from_raw(clamp_cast(prod.0.raw_value()));
949 }
950 }
951
952 impl<const FRAC: usize> core::ops::Div<$Int> for FastFixed<$T, FRAC> {
953 type Output = DivExpression<FastFixed<$T, FRAC>, FastFixed<$Int, 0>>;
954
955 fn div(self, rhs: $Int) -> Self::Output {
956 DivExpression { numerator: self, denominator: FastFixed(Fixed::from_raw(rhs)) }
957 }
958 }
959
960 impl<const FRAC: usize> core::ops::DivAssign<$Int> for FastFixed<$T, FRAC>
961 where
962 Self: Copy,
963 FastFixed<$T, FRAC>: From<DivExpression<FastFixed<$T, FRAC>, FastFixed<$Int, 0>>>,
964 {
965 fn div_assign(&mut self, rhs: $Int) {
966 *self = (*self / rhs).into();
967 }
968 }
969 };
970}
971
972macro_rules! gen_fast_fixed_integer_arithmetic {
973 ($label:ident: [$($T:ty),*], $Int_list:tt) => {
974 $(
975 gen_fast_fixed_integer_arithmetic!(@one $T, $Int_list);
976 )*
977 };
978 (@one $T:ty, [$($Int:ty),*]) => {
979 $(
980 impl_fast_fixed_integer_arithmetic!($T, $Int);
981 )*
982 };
983}
984
985gen_fast_fixed_integer_arithmetic!(signed: [i8, i16, i32, i64], [i8, i16, i32, i64]);
986gen_fast_fixed_integer_arithmetic!(unsigned: [u8, u16, u32, u64], [u8, u16, u32, u64]);
987gen_fast_fixed_integer_arithmetic!(mixed: [i8, i16, i32, i64], [u8, u16, u32, u64]);
988gen_fast_fixed_integer_arithmetic!(mixed: [u8, u16, u32, u64], [i8, i16, i32, i64]);