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 FRAC: usize, U, const FRAC_R: usize> core::ops::AddAssign<FastFixed<U, FRAC_R>>
605 for FastFixed<I, FRAC>
606where
607 I: PromoteAddSub<U>
608 + num_traits::Zero
609 + PartialOrd
610 + num_traits::Bounded
611 + num_traits::NumCast
612 + Copy
613 + FastInt
614 + TryFrom<<I as PromoteAddSub<U>>::Output>,
615 <I as PromoteAddSub<U>>::Output: FastInt,
616 U: num_traits::Zero + PartialOrd + num_traits::NumCast + Copy,
617 FastFixed<I, FRAC>: core::ops::Add<
618 FastFixed<U, FRAC_R>,
619 Output = FastFixed<<I as PromoteAddSub<U>>::Output, FRAC>,
620 >,
621{
622 fn add_assign(&mut self, rhs: FastFixed<U, FRAC_R>) {
623 let sum = *self + rhs;
624 self.0 = Fixed::from_raw(clamp_cast(sum.0.raw_value()));
625 }
626}
627
628impl<I, const FRAC: usize, U, const FRAC_R: usize> core::ops::SubAssign<FastFixed<U, FRAC_R>>
629 for FastFixed<I, FRAC>
630where
631 I: PromoteAddSub<U>
632 + num_traits::Zero
633 + PartialOrd
634 + num_traits::Bounded
635 + num_traits::NumCast
636 + Copy
637 + FastInt
638 + TryFrom<<I as PromoteAddSub<U>>::Output>,
639 <I as PromoteAddSub<U>>::Output: FastInt,
640 U: num_traits::Zero + PartialOrd + num_traits::NumCast + Copy,
641 FastFixed<I, FRAC>: core::ops::Sub<
642 FastFixed<U, FRAC_R>,
643 Output = FastFixed<<I as PromoteAddSub<U>>::Output, FRAC>,
644 >,
645{
646 fn sub_assign(&mut self, rhs: FastFixed<U, FRAC_R>) {
647 let diff = *self - rhs;
648 self.0 = Fixed::from_raw(clamp_cast(diff.0.raw_value()));
649 }
650}
651
652impl<I, const FRAC: usize> core::ops::MulAssign<FastFixed<I, FRAC>> for FastFixed<I, FRAC>
653where
654 I: PromoteMul<I>
655 + num_traits::Zero
656 + PartialOrd
657 + num_traits::Bounded
658 + num_traits::NumCast
659 + Copy
660 + FastInt
661 + TryFrom<<I as PromoteMul<I>>::Output>,
662 <I as PromoteMul<I>>::Output: FastInt,
663 FastFixed<I, FRAC>:
664 core::ops::Mul<FastFixed<I, FRAC>, Output = FastFixed<<I as PromoteMul<I>>::Output, FRAC>>,
665{
666 fn mul_assign(&mut self, rhs: Self) {
667 let prod = *self * rhs;
668 self.0 = Fixed::from_raw(clamp_cast(prod.0.raw_value()));
669 }
670}
671
672impl<I, const FRAC: usize> core::ops::DivAssign<FastFixed<I, FRAC>> for FastFixed<I, FRAC>
673where
674 Self: Copy,
675 I: num_traits::Zero + PartialOrd,
676 FastFixed<I, FRAC>: From<DivExpression<Self, Self>>,
677{
678 fn div_assign(&mut self, rhs: Self) {
679 *self = Self::from(DivExpression { numerator: *self, denominator: rhs });
680 }
681}
682
683impl<I, const FRAC: usize> core::fmt::Display for FastFixed<I, FRAC>
685where
686 I: Into<i128> + Copy,
687{
688 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
689 let s = crate::string::String::new(self.0, crate::string::Mode::Dec, f.precision());
690 write!(f, "{}", s)
691 }
692}
693
694impl<I, const FRAC: usize> core::fmt::LowerHex for FastFixed<I, FRAC>
695where
696 I: Into<i128> + Copy,
697{
698 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
699 let s = crate::string::String::new(self.0, crate::string::Mode::Hex, Some(0));
700 write!(f, "{}", s)
701 }
702}
703
704macro_rules! impl_fast_fixed_integer_comparison {
706 ($T:ty, $Int:ty) => {
707 impl<const FRAC: usize> PartialEq<$Int> for FastFixed<$T, FRAC>
708 where
709 Fixed<$T, FRAC>: PartialEq<$Int>,
710 {
711 fn eq(&self, other: &$Int) -> bool {
712 self.0 == *other
713 }
714 }
715
716 impl<const FRAC: usize> PartialEq<FastFixed<$T, FRAC>> for $Int
717 where
718 Fixed<$T, FRAC>: PartialEq<$Int>,
719 {
720 fn eq(&self, other: &FastFixed<$T, FRAC>) -> bool {
721 other.0 == *self
722 }
723 }
724
725 impl<const FRAC: usize> PartialOrd<$Int> for FastFixed<$T, FRAC>
726 where
727 Fixed<$T, FRAC>: PartialOrd<$Int>,
728 {
729 fn partial_cmp(&self, other: &$Int) -> Option<core::cmp::Ordering> {
730 self.0.partial_cmp(other)
731 }
732 }
733
734 impl<const FRAC: usize> PartialOrd<FastFixed<$T, FRAC>> for $Int
735 where
736 Fixed<$T, FRAC>: PartialOrd<$Int>,
737 {
738 fn partial_cmp(&self, other: &FastFixed<$T, FRAC>) -> Option<core::cmp::Ordering> {
739 other.0.partial_cmp(self).map(|o| o.reverse())
740 }
741 }
742 };
743}
744
745macro_rules! gen_fast_fixed_integer_comparisons {
746 ($label:ident: [$($T:ty),*], $Int_list:tt) => {
747 $(
748 gen_fast_fixed_integer_comparisons!(@one $T, $Int_list);
749 )*
750 };
751 (@one $T:ty, [$($Int:ty),*]) => {
752 $(
753 impl_fast_fixed_integer_comparison!($T, $Int);
754 )*
755 };
756}
757
758gen_fast_fixed_integer_comparisons!(signed: [i8, i16, i32, i64], [i8, i16, i32, i64]);
759gen_fast_fixed_integer_comparisons!(unsigned: [u8, u16, u32, u64], [u8, u16, u32, u64]);
760gen_fast_fixed_integer_comparisons!(mixed: [i8, i16, i32, i64], [u8, u16, u32, u64]);
761gen_fast_fixed_integer_comparisons!(mixed: [u8, u16, u32, u64], [i8, i16, i32, i64]);
762
763macro_rules! impl_fast_fixed_integer_arithmetic {
765 ($T:ty, $Int:ty) => {
766 impl<const FRAC: usize> core::ops::Add<$Int> for FastFixed<$T, FRAC>
767 where
768 $T: PromoteAddSub<$Int> + num_traits::NumCast + Copy,
769 $Int: num_traits::NumCast + Copy,
770 <$T as PromoteAddSub<$Int>>::Output:
771 num_traits::Bounded + num_traits::NumCast + Copy + FastInt,
772 {
773 type Output = FastFixed<<$T as PromoteAddSub<$Int>>::Output, FRAC>;
774
775 fn add(self, rhs: $Int) -> Self::Output {
776 type Out = <$T as PromoteAddSub<$Int>>::Output;
777 type I64 = <Out as FastInt>::Intermediate;
778 let val_l: I64 = clamp_cast(self.0.raw_value());
779 let val_r: I64 = clamp_cast(rhs);
780 let val_r_shifted = saturating_shl(val_r, FRAC);
781 let sum = val_l.saturating_add(val_r_shifted);
782 FastFixed(Fixed::from_raw(clamp_cast(sum)))
783 }
784 }
785
786 impl<const FRAC: usize> core::ops::Add<FastFixed<$T, FRAC>> for $Int
787 where
788 $T: PromoteAddSub<$Int> + num_traits::NumCast + Copy,
789 $Int: num_traits::NumCast + Copy,
790 <$T as PromoteAddSub<$Int>>::Output:
791 num_traits::Bounded + num_traits::NumCast + Copy + FastInt,
792 {
793 type Output = FastFixed<<$T as PromoteAddSub<$Int>>::Output, FRAC>;
794
795 fn add(self, rhs: FastFixed<$T, FRAC>) -> Self::Output {
796 type Out = <$T as PromoteAddSub<$Int>>::Output;
797 type I64 = <Out as FastInt>::Intermediate;
798 let val_l: I64 = clamp_cast(self);
799 let val_r: I64 = clamp_cast(rhs.0.raw_value());
800 let val_l_shifted = saturating_shl(val_l, FRAC);
801 let sum = val_l_shifted.saturating_add(val_r);
802 FastFixed(Fixed::from_raw(clamp_cast(sum)))
803 }
804 }
805
806 impl<const FRAC: usize> core::ops::Sub<$Int> for FastFixed<$T, FRAC>
807 where
808 $T: PromoteAddSub<$Int> + num_traits::NumCast + Copy,
809 $Int: num_traits::NumCast + Copy,
810 <$T as PromoteAddSub<$Int>>::Output:
811 num_traits::Bounded + num_traits::NumCast + Copy + FastInt,
812 {
813 type Output = FastFixed<<$T as PromoteAddSub<$Int>>::Output, FRAC>;
814
815 fn sub(self, rhs: $Int) -> Self::Output {
816 type Out = <$T as PromoteAddSub<$Int>>::Output;
817 type I64 = <Out as FastInt>::Intermediate;
818 let val_l: I64 = clamp_cast(self.0.raw_value());
819 let val_r: I64 = clamp_cast(rhs);
820 let val_r_shifted = saturating_shl(val_r, FRAC);
821 let diff = val_l.saturating_sub(val_r_shifted);
822 FastFixed(Fixed::from_raw(clamp_cast(diff)))
823 }
824 }
825
826 impl<const FRAC: usize> core::ops::Sub<FastFixed<$T, FRAC>> for $Int
827 where
828 $T: PromoteAddSub<$Int> + num_traits::NumCast + Copy,
829 $Int: num_traits::NumCast + Copy,
830 <$T as PromoteAddSub<$Int>>::Output:
831 num_traits::Bounded + num_traits::NumCast + Copy + FastInt,
832 {
833 type Output = FastFixed<<$T as PromoteAddSub<$Int>>::Output, FRAC>;
834
835 fn sub(self, rhs: FastFixed<$T, FRAC>) -> Self::Output {
836 type Out = <$T as PromoteAddSub<$Int>>::Output;
837 type I64 = <Out as FastInt>::Intermediate;
838 let val_l: I64 = clamp_cast(self);
839 let val_r: I64 = clamp_cast(rhs.0.raw_value());
840 let val_l_shifted = saturating_shl(val_l, FRAC);
841 let diff = val_l_shifted.saturating_sub(val_r);
842 FastFixed(Fixed::from_raw(clamp_cast(diff)))
843 }
844 }
845
846 impl<const FRAC: usize> core::ops::Mul<$Int> for FastFixed<$T, FRAC>
847 where
848 $T: PromoteMul<$Int> + num_traits::Zero + PartialOrd + num_traits::NumCast + Copy,
849 $Int: num_traits::Zero + PartialOrd + num_traits::NumCast + Copy,
850 <$T as PromoteMul<$Int>>::Output:
851 num_traits::Bounded + num_traits::NumCast + Copy + FastInt,
852 {
853 type Output = FastFixed<<$T as PromoteMul<$Int>>::Output, FRAC>;
854
855 fn mul(self, rhs: $Int) -> Self::Output {
856 type Out = <$T as PromoteMul<$Int>>::Output;
857 type I64 = <Out as FastInt>::Intermediate;
858 let val_l: I64 = clamp_cast(self.0.raw_value());
859 let val_r: I64 = clamp_cast(rhs);
860 let prod = val_l.saturating_mul(val_r);
861 FastFixed(Fixed::from_raw(clamp_cast(prod)))
862 }
863 }
864
865 impl<const FRAC: usize> core::ops::Mul<FastFixed<$T, FRAC>> for $Int
866 where
867 $T: PromoteMul<$Int> + num_traits::Zero + PartialOrd + num_traits::NumCast + Copy,
868 $Int: num_traits::Zero + PartialOrd + num_traits::NumCast + Copy,
869 <$T as PromoteMul<$Int>>::Output:
870 num_traits::Bounded + num_traits::NumCast + Copy + FastInt,
871 {
872 type Output = FastFixed<<$T as PromoteMul<$Int>>::Output, FRAC>;
873
874 fn mul(self, rhs: FastFixed<$T, FRAC>) -> Self::Output {
875 rhs.mul(self)
876 }
877 }
878
879 impl<const FRAC: usize> core::ops::AddAssign<$Int> for FastFixed<$T, FRAC>
880 where
881 $T: PromoteAddSub<$Int>
882 + num_traits::Zero
883 + PartialOrd
884 + num_traits::Bounded
885 + num_traits::NumCast
886 + Copy
887 + FastInt,
888 <$T as PromoteAddSub<$Int>>::Output: FastInt,
889 $Int: num_traits::Zero + PartialOrd + num_traits::NumCast + Copy,
890 FastFixed<$T, FRAC>:
891 core::ops::Add<$Int, Output = FastFixed<<$T as PromoteAddSub<$Int>>::Output, FRAC>>,
892 {
893 fn add_assign(&mut self, rhs: $Int) {
894 let sum = *self + rhs;
895 self.0 = Fixed::from_raw(clamp_cast(sum.0.raw_value()));
896 }
897 }
898
899 impl<const FRAC: usize> core::ops::SubAssign<$Int> for FastFixed<$T, FRAC>
900 where
901 $T: PromoteAddSub<$Int>
902 + num_traits::Zero
903 + PartialOrd
904 + num_traits::Bounded
905 + num_traits::NumCast
906 + Copy
907 + FastInt,
908 <$T as PromoteAddSub<$Int>>::Output: FastInt,
909 $Int: num_traits::Zero + PartialOrd + num_traits::NumCast + Copy,
910 FastFixed<$T, FRAC>:
911 core::ops::Sub<$Int, Output = FastFixed<<$T as PromoteAddSub<$Int>>::Output, FRAC>>,
912 {
913 fn sub_assign(&mut self, rhs: $Int) {
914 let diff = *self - rhs;
915 self.0 = Fixed::from_raw(clamp_cast(diff.0.raw_value()));
916 }
917 }
918
919 impl<const FRAC: usize> core::ops::MulAssign<$Int> for FastFixed<$T, FRAC>
920 where
921 $T: PromoteMul<$Int>
922 + num_traits::Zero
923 + PartialOrd
924 + num_traits::Bounded
925 + num_traits::NumCast
926 + Copy
927 + FastInt,
928 <$T as PromoteMul<$Int>>::Output: FastInt,
929 $Int: num_traits::Zero + PartialOrd + num_traits::NumCast + Copy,
930 FastFixed<$T, FRAC>:
931 core::ops::Mul<$Int, Output = FastFixed<<$T as PromoteMul<$Int>>::Output, FRAC>>,
932 {
933 fn mul_assign(&mut self, rhs: $Int) {
934 let prod = *self * rhs;
935 self.0 = Fixed::from_raw(clamp_cast(prod.0.raw_value()));
936 }
937 }
938
939 impl<const FRAC: usize> core::ops::Div<$Int> for FastFixed<$T, FRAC> {
940 type Output = DivExpression<FastFixed<$T, FRAC>, FastFixed<$Int, 0>>;
941
942 fn div(self, rhs: $Int) -> Self::Output {
943 DivExpression { numerator: self, denominator: FastFixed(Fixed::from_raw(rhs)) }
944 }
945 }
946
947 impl<const FRAC: usize> core::ops::DivAssign<$Int> for FastFixed<$T, FRAC>
948 where
949 Self: Copy,
950 FastFixed<$T, FRAC>: From<DivExpression<FastFixed<$T, FRAC>, FastFixed<$Int, 0>>>,
951 {
952 fn div_assign(&mut self, rhs: $Int) {
953 *self = (*self / rhs).into();
954 }
955 }
956 };
957}
958
959macro_rules! gen_fast_fixed_integer_arithmetic {
960 ($label:ident: [$($T:ty),*], $Int_list:tt) => {
961 $(
962 gen_fast_fixed_integer_arithmetic!(@one $T, $Int_list);
963 )*
964 };
965 (@one $T:ty, [$($Int:ty),*]) => {
966 $(
967 impl_fast_fixed_integer_arithmetic!($T, $Int);
968 )*
969 };
970}
971
972gen_fast_fixed_integer_arithmetic!(signed: [i8, i16, i32, i64], [i8, i16, i32, i64]);
973gen_fast_fixed_integer_arithmetic!(unsigned: [u8, u16, u32, u64], [u8, u16, u32, u64]);
974gen_fast_fixed_integer_arithmetic!(mixed: [i8, i16, i32, i64], [u8, u16, u32, u64]);
975gen_fast_fixed_integer_arithmetic!(mixed: [u8, u16, u32, u64], [i8, i16, i32, i64]);