embedded_charts/math/
backends.rs

1//! Math backend implementations for different numeric types and feature configurations.
2
3use super::traits::MathBackend;
4
5/// Floating-point backend using micromath
6#[cfg(feature = "floating-point")]
7pub struct FloatingPointBackend;
8
9#[cfg(feature = "floating-point")]
10impl MathBackend<f32> for FloatingPointBackend {
11    #[inline]
12    fn sqrt(&self, x: f32) -> f32 {
13        micromath::F32Ext::sqrt(x)
14    }
15
16    #[inline]
17    fn abs(&self, x: f32) -> f32 {
18        if x < 0.0 {
19            -x
20        } else {
21            x
22        }
23    }
24
25    #[inline]
26    fn min(&self, a: f32, b: f32) -> f32 {
27        if a < b {
28            a
29        } else {
30            b
31        }
32    }
33
34    #[inline]
35    fn max(&self, a: f32, b: f32) -> f32 {
36        if a > b {
37            a
38        } else {
39            b
40        }
41    }
42
43    #[inline]
44    fn floor(&self, x: f32) -> f32 {
45        micromath::F32Ext::floor(x)
46    }
47
48    #[inline]
49    fn ceil(&self, x: f32) -> f32 {
50        micromath::F32Ext::ceil(x)
51    }
52
53    #[inline]
54    fn pow(&self, x: f32, y: f32) -> f32 {
55        micromath::F32Ext::powf(x, y)
56    }
57
58    #[inline]
59    fn ln(&self, x: f32) -> f32 {
60        micromath::F32Ext::ln(x)
61    }
62
63    #[inline]
64    fn log10(&self, x: f32) -> f32 {
65        micromath::F32Ext::log10(x)
66    }
67
68    #[inline]
69    fn sin(&self, x: f32) -> f32 {
70        micromath::F32Ext::sin(x)
71    }
72
73    #[inline]
74    fn cos(&self, x: f32) -> f32 {
75        micromath::F32Ext::cos(x)
76    }
77
78    #[inline]
79    fn tan(&self, x: f32) -> f32 {
80        micromath::F32Ext::tan(x)
81    }
82
83    #[inline]
84    fn to_radians(&self, degrees: f32) -> f32 {
85        degrees * (core::f32::consts::PI / 180.0)
86    }
87
88    #[inline]
89    fn to_degrees(&self, radians: f32) -> f32 {
90        radians * (180.0 / core::f32::consts::PI)
91    }
92
93    #[inline]
94    fn atan2(&self, y: f32, x: f32) -> f32 {
95        micromath::F32Ext::atan2(y, x)
96    }
97}
98
99/// Libm backend for floating-point operations
100#[cfg(feature = "libm-math")]
101pub struct LibmBackend;
102
103#[cfg(feature = "libm-math")]
104impl MathBackend<f32> for LibmBackend {
105    #[inline]
106    fn sqrt(&self, x: f32) -> f32 {
107        libm::sqrtf(x)
108    }
109
110    #[inline]
111    fn abs(&self, x: f32) -> f32 {
112        libm::fabsf(x)
113    }
114
115    #[inline]
116    fn min(&self, a: f32, b: f32) -> f32 {
117        libm::fminf(a, b)
118    }
119
120    #[inline]
121    fn max(&self, a: f32, b: f32) -> f32 {
122        libm::fmaxf(a, b)
123    }
124
125    #[inline]
126    fn floor(&self, x: f32) -> f32 {
127        libm::floorf(x)
128    }
129
130    #[inline]
131    fn ceil(&self, x: f32) -> f32 {
132        libm::ceilf(x)
133    }
134
135    #[inline]
136    fn pow(&self, x: f32, y: f32) -> f32 {
137        libm::powf(x, y)
138    }
139
140    #[inline]
141    fn ln(&self, x: f32) -> f32 {
142        libm::logf(x)
143    }
144
145    #[inline]
146    fn log10(&self, x: f32) -> f32 {
147        libm::log10f(x)
148    }
149
150    #[inline]
151    fn sin(&self, x: f32) -> f32 {
152        libm::sinf(x)
153    }
154
155    #[inline]
156    fn cos(&self, x: f32) -> f32 {
157        libm::cosf(x)
158    }
159
160    #[inline]
161    fn tan(&self, x: f32) -> f32 {
162        libm::tanf(x)
163    }
164
165    #[inline]
166    fn to_radians(&self, degrees: f32) -> f32 {
167        degrees * (core::f32::consts::PI / 180.0)
168    }
169
170    #[inline]
171    fn to_degrees(&self, radians: f32) -> f32 {
172        radians * (180.0 / core::f32::consts::PI)
173    }
174
175    #[inline]
176    fn atan2(&self, y: f32, x: f32) -> f32 {
177        libm::atan2f(y, x)
178    }
179}
180
181/// Fixed-point backend using the fixed crate
182#[cfg(any(feature = "fixed-point", feature = "cordic-math"))]
183pub struct FixedPointBackend;
184
185#[cfg(any(feature = "fixed-point", feature = "cordic-math"))]
186impl MathBackend<fixed::types::I16F16> for FixedPointBackend {
187    #[inline]
188    fn sqrt(&self, x: fixed::types::I16F16) -> fixed::types::I16F16 {
189        // Simple Newton-Raphson method for square root
190        if x <= fixed::types::I16F16::ZERO {
191            return fixed::types::I16F16::ZERO;
192        }
193
194        let mut guess = x / 2;
195        for _ in 0..8 {
196            // 8 iterations should be sufficient for I16F16
197            let new_guess = (guess + x / guess) / 2;
198            if (new_guess - guess).abs() < fixed::types::I16F16::from_num(0.001) {
199                break;
200            }
201            guess = new_guess;
202        }
203        guess
204    }
205
206    #[inline]
207    fn abs(&self, x: fixed::types::I16F16) -> fixed::types::I16F16 {
208        x.abs()
209    }
210
211    #[inline]
212    fn min(&self, a: fixed::types::I16F16, b: fixed::types::I16F16) -> fixed::types::I16F16 {
213        if a < b {
214            a
215        } else {
216            b
217        }
218    }
219
220    #[inline]
221    fn max(&self, a: fixed::types::I16F16, b: fixed::types::I16F16) -> fixed::types::I16F16 {
222        if a > b {
223            a
224        } else {
225            b
226        }
227    }
228
229    #[inline]
230    fn floor(&self, x: fixed::types::I16F16) -> fixed::types::I16F16 {
231        x.floor()
232    }
233
234    #[inline]
235    fn ceil(&self, x: fixed::types::I16F16) -> fixed::types::I16F16 {
236        x.ceil()
237    }
238
239    #[inline]
240    fn pow(&self, x: fixed::types::I16F16, y: fixed::types::I16F16) -> fixed::types::I16F16 {
241        // Simple integer power for fixed-point
242        if y == fixed::types::I16F16::ZERO {
243            return fixed::types::I16F16::ONE;
244        }
245
246        let y_int: i32 = y.to_num();
247        if y_int < 0 {
248            return fixed::types::I16F16::ONE / self.pow(x, -y);
249        }
250
251        let mut result = fixed::types::I16F16::ONE;
252        let mut base = x;
253        let mut exp = y_int as u32;
254
255        while exp > 0 {
256            if exp & 1 == 1 {
257                result *= base;
258            }
259            base = base * base;
260            exp >>= 1;
261        }
262        result
263    }
264
265    #[inline]
266    fn ln(&self, x: fixed::types::I16F16) -> fixed::types::I16F16 {
267        // Simplified natural log approximation for fixed-point
268        if x <= fixed::types::I16F16::ZERO {
269            return fixed::types::I16F16::from_num(-100.0); // Approximate -infinity
270        }
271
272        // Use Taylor series approximation around x=1
273        let one = fixed::types::I16F16::ONE;
274        let t = x - one;
275
276        // ln(1+t) ≈ t - t²/2 + t³/3 - t⁴/4 + ...
277        let t2 = t * t;
278        let t3 = t2 * t;
279        let t4 = t3 * t;
280
281        t - t2 / 2 + t3 / 3 - t4 / 4
282    }
283
284    #[inline]
285    fn log10(&self, x: fixed::types::I16F16) -> fixed::types::I16F16 {
286        // log10(x) = ln(x) / ln(10)
287        let ln_x = self.ln(x);
288        let ln_10 = fixed::types::I16F16::from_num(core::f32::consts::LN_10); // ln(10)
289        ln_x / ln_10
290    }
291
292    #[inline]
293    fn sin(&self, x: fixed::types::I16F16) -> fixed::types::I16F16 {
294        // Taylor series approximation for sine
295        let pi = fixed::types::I16F16::from_num(core::f32::consts::PI);
296        let two_pi = pi * 2;
297
298        // Normalize angle to [-π, π]
299        let mut angle = x;
300        while angle > pi {
301            angle -= two_pi;
302        }
303        while angle < -pi {
304            angle += two_pi;
305        }
306
307        // sin(x) ≈ x - x³/6 + x⁵/120 - x⁷/5040
308        let x2 = angle * angle;
309        let x3 = x2 * angle;
310        let x5 = x3 * x2;
311        let x7 = x5 * x2;
312
313        angle - x3 / 6 + x5 / 120 - x7 / 5040
314    }
315
316    #[inline]
317    fn cos(&self, x: fixed::types::I16F16) -> fixed::types::I16F16 {
318        // cos(x) = sin(x + π/2)
319        let pi_2 = fixed::types::I16F16::from_num(core::f32::consts::FRAC_PI_2); // π/2
320        self.sin(x + pi_2)
321    }
322
323    #[inline]
324    fn tan(&self, x: fixed::types::I16F16) -> fixed::types::I16F16 {
325        // tan(x) = sin(x) / cos(x)
326        let sin_x = self.sin(x);
327        let cos_x = self.cos(x);
328
329        if cos_x.abs() < fixed::types::I16F16::from_num(0.001) {
330            // Avoid division by zero
331            if sin_x >= fixed::types::I16F16::ZERO {
332                fixed::types::I16F16::from_num(100.0) // Approximate +infinity
333            } else {
334                fixed::types::I16F16::from_num(-100.0) // Approximate -infinity
335            }
336        } else {
337            sin_x / cos_x
338        }
339    }
340
341    #[inline]
342    fn to_radians(&self, degrees: fixed::types::I16F16) -> fixed::types::I16F16 {
343        let pi = fixed::types::I16F16::from_num(core::f32::consts::PI);
344        degrees * pi / fixed::types::I16F16::from_num(180.0)
345    }
346
347    #[inline]
348    fn to_degrees(&self, radians: fixed::types::I16F16) -> fixed::types::I16F16 {
349        let pi = fixed::types::I16F16::from_num(core::f32::consts::PI);
350        radians * fixed::types::I16F16::from_num(180.0) / pi
351    }
352
353    #[inline]
354    fn atan2(&self, y: fixed::types::I16F16, x: fixed::types::I16F16) -> fixed::types::I16F16 {
355        // Implement atan2 using atan approximation
356        let zero = fixed::types::I16F16::ZERO;
357        let pi = fixed::types::I16F16::from_num(core::f32::consts::PI);
358        let pi_2 = pi / 2;
359
360        if x == zero {
361            if y > zero {
362                return pi_2;
363            } else if y < zero {
364                return -pi_2;
365            } else {
366                return zero; // undefined, but return 0
367            }
368        }
369
370        // Use atan(y/x) and adjust for quadrant
371        let ratio = y / x;
372        let atan_ratio = self.atan_approx(ratio);
373
374        if x > zero {
375            atan_ratio
376        } else if y >= zero {
377            atan_ratio + pi
378        } else {
379            atan_ratio - pi
380        }
381    }
382}
383
384#[cfg(any(feature = "fixed-point", feature = "cordic-math"))]
385impl FixedPointBackend {
386    /// Approximate atan using polynomial approximation
387    #[allow(clippy::only_used_in_recursion)]
388    fn atan_approx(&self, x: fixed::types::I16F16) -> fixed::types::I16F16 {
389        let abs_x = x.abs();
390        let pi_4 = fixed::types::I16F16::from_num(core::f32::consts::FRAC_PI_4); // π/4
391
392        // For |x| <= 1, use: atan(x) ≈ x - x³/3 + x⁵/5 - x⁷/7
393        if abs_x <= fixed::types::I16F16::ONE {
394            let x2 = x * x;
395            let x3 = x2 * x;
396            let x5 = x3 * x2;
397            let x7 = x5 * x2;
398
399            x - x3 / 3 + x5 / 5 - x7 / 7
400        } else {
401            // For |x| > 1, use: atan(x) = π/2 - atan(1/x)
402            let inv_x = fixed::types::I16F16::ONE / x;
403            let atan_inv = self.atan_approx(inv_x);
404
405            if x > fixed::types::I16F16::ZERO {
406                pi_4 * 2 - atan_inv
407            } else {
408                -pi_4 * 2 - atan_inv
409            }
410        }
411    }
412}
413
414/// Integer-only backend for the most constrained environments
415#[cfg(feature = "integer-math")]
416pub struct IntegerBackend;
417
418#[cfg(feature = "integer-math")]
419impl MathBackend<i32> for IntegerBackend {
420    #[inline]
421    fn sqrt(&self, x: i32) -> i32 {
422        if x <= 0 {
423            return 0;
424        }
425
426        // Integer square root using binary search
427        let mut left = 0i32;
428        let mut right = x;
429        let mut result = 0i32;
430
431        while left <= right {
432            let mid = left + (right - left) / 2;
433            let mid_squared = mid.saturating_mul(mid);
434
435            if mid_squared == x {
436                return mid;
437            } else if mid_squared < x {
438                left = mid + 1;
439                result = mid;
440            } else {
441                right = mid - 1;
442            }
443        }
444
445        result
446    }
447
448    #[inline]
449    fn abs(&self, x: i32) -> i32 {
450        x.abs()
451    }
452
453    #[inline]
454    fn min(&self, a: i32, b: i32) -> i32 {
455        if a < b {
456            a
457        } else {
458            b
459        }
460    }
461
462    #[inline]
463    fn max(&self, a: i32, b: i32) -> i32 {
464        if a > b {
465            a
466        } else {
467            b
468        }
469    }
470
471    #[inline]
472    fn floor(&self, x: i32) -> i32 {
473        x // Integers are already "floored"
474    }
475
476    #[inline]
477    fn ceil(&self, x: i32) -> i32 {
478        x // Integers are already "ceiled"
479    }
480
481    #[inline]
482    fn pow(&self, x: i32, y: i32) -> i32 {
483        if y == 0 {
484            return 1;
485        }
486        if y < 0 {
487            return 0; // Integer division would result in 0 for most cases
488        }
489
490        let mut result = 1i32;
491        let mut base = x;
492        let mut exp = y as u32;
493
494        while exp > 0 {
495            if exp & 1 == 1 {
496                result = result.saturating_mul(base);
497            }
498            base = base.saturating_mul(base);
499            exp >>= 1;
500        }
501        result
502    }
503
504    #[inline]
505    fn ln(&self, x: i32) -> i32 {
506        // Very rough integer approximation of natural log
507        if x <= 0 {
508            return -1000; // Approximate -infinity
509        }
510
511        // Simple lookup table for small values
512        match x {
513            1 => 0,
514            2..=3 => 693,    // ln(2) * 1000 ≈ 693
515            4..=7 => 1386,   // ln(4) * 1000 ≈ 1386
516            8..=15 => 2079,  // ln(8) * 1000 ≈ 2079
517            16..=31 => 2772, // ln(16) * 1000 ≈ 2772
518            _ => {
519                // For larger values, use bit length approximation
520                let bit_len = 32 - x.leading_zeros() as i32;
521                bit_len * 693 // Approximate ln(2^n) = n * ln(2)
522            }
523        }
524    }
525
526    #[inline]
527    fn log10(&self, x: i32) -> i32 {
528        // log10(x) = ln(x) / ln(10)
529        let ln_x = self.ln(x);
530        ln_x * 1000 / 2303 // ln(10) * 1000 ≈ 2303
531    }
532
533    #[inline]
534    fn sin(&self, x: i32) -> i32 {
535        // Very rough integer sine approximation using lookup table
536        // Input is assumed to be in milliradians (radians * 1000)
537        let pi_1000 = 3142; // π * 1000
538        let two_pi_1000 = 6284; // 2π * 1000
539
540        // Normalize to [0, 2π)
541        let mut angle = x % two_pi_1000;
542        if angle < 0 {
543            angle += two_pi_1000;
544        }
545
546        // Simple piecewise linear approximation
547        if angle <= pi_1000 / 2 {
548            // First quadrant: sin increases from 0 to 1
549            (angle * 1000) / (pi_1000 / 2)
550        } else if angle <= pi_1000 {
551            // Second quadrant: sin decreases from 1 to 0
552            ((pi_1000 - angle) * 1000) / (pi_1000 / 2)
553        } else if angle <= 3 * pi_1000 / 2 {
554            // Third quadrant: sin decreases from 0 to -1
555            -((angle - pi_1000) * 1000) / (pi_1000 / 2)
556        } else {
557            // Fourth quadrant: sin increases from -1 to 0
558            -((two_pi_1000 - angle) * 1000) / (pi_1000 / 2)
559        }
560    }
561
562    #[inline]
563    fn cos(&self, x: i32) -> i32 {
564        // cos(x) = sin(x + π/2)
565        let pi_2_1000 = 1571; // π/2 * 1000
566        self.sin(x + pi_2_1000)
567    }
568
569    #[inline]
570    fn tan(&self, x: i32) -> i32 {
571        let sin_x = self.sin(x);
572        let cos_x = self.cos(x);
573
574        if cos_x.abs() < 10 {
575            // Avoid division by very small numbers
576            if sin_x >= 0 {
577                100000 // Large positive number
578            } else {
579                -100000 // Large negative number
580            }
581        } else {
582            (sin_x * 1000) / cos_x
583        }
584    }
585
586    #[inline]
587    fn to_radians(&self, degrees: i32) -> i32 {
588        // Convert degrees to milliradians
589        (degrees * 3142) / (180 * 1000)
590    }
591
592    #[inline]
593    fn to_degrees(&self, radians: i32) -> i32 {
594        // Convert milliradians to degrees
595        (radians * 180 * 1000) / 3142
596    }
597
598    #[inline]
599    fn atan2(&self, y: i32, x: i32) -> i32 {
600        // Integer atan2 implementation
601        // Returns angle in milliradians (radians * 1000)
602        let pi_1000 = 3142; // π * 1000
603        let pi_2_1000 = 1571; // π/2 * 1000
604
605        if x == 0 {
606            if y > 0 {
607                return pi_2_1000;
608            } else if y < 0 {
609                return -pi_2_1000;
610            } else {
611                return 0; // undefined, but return 0
612            }
613        }
614
615        // Simple quadrant-based approximation
616        let abs_y = y.abs();
617        let abs_x = x.abs();
618
619        // Use a simple lookup table approach for basic angles
620        let angle = if abs_x >= abs_y {
621            // More horizontal than vertical
622            (abs_y * pi_2_1000) / abs_x / 2 // Rough approximation
623        } else {
624            // More vertical than horizontal
625            pi_2_1000 - (abs_x * pi_2_1000) / abs_y / 2
626        };
627
628        // Adjust for quadrant
629        if x > 0 && y >= 0 {
630            angle // First quadrant
631        } else if x <= 0 && y > 0 {
632            pi_1000 - angle // Second quadrant
633        } else if x < 0 && y <= 0 {
634            -pi_1000 + angle // Third quadrant
635        } else {
636            -angle // Fourth quadrant
637        }
638    }
639}
640
641/// CORDIC backend for trigonometric functions
642#[cfg(feature = "cordic-math")]
643pub struct CordicBackend;
644
645#[cfg(feature = "cordic-math")]
646impl MathBackend<fixed::types::I16F16> for CordicBackend {
647    #[inline]
648    fn sqrt(&self, x: fixed::types::I16F16) -> fixed::types::I16F16 {
649        // Use the fixed-point backend for non-trig functions
650        let fixed_backend = FixedPointBackend;
651        fixed_backend.sqrt(x)
652    }
653
654    #[inline]
655    fn abs(&self, x: fixed::types::I16F16) -> fixed::types::I16F16 {
656        x.abs()
657    }
658
659    #[inline]
660    fn min(&self, a: fixed::types::I16F16, b: fixed::types::I16F16) -> fixed::types::I16F16 {
661        if a < b {
662            a
663        } else {
664            b
665        }
666    }
667
668    #[inline]
669    fn max(&self, a: fixed::types::I16F16, b: fixed::types::I16F16) -> fixed::types::I16F16 {
670        if a > b {
671            a
672        } else {
673            b
674        }
675    }
676
677    #[inline]
678    fn floor(&self, x: fixed::types::I16F16) -> fixed::types::I16F16 {
679        x.floor()
680    }
681
682    #[inline]
683    fn ceil(&self, x: fixed::types::I16F16) -> fixed::types::I16F16 {
684        x.ceil()
685    }
686
687    #[inline]
688    fn pow(&self, x: fixed::types::I16F16, y: fixed::types::I16F16) -> fixed::types::I16F16 {
689        let fixed_backend = FixedPointBackend;
690        fixed_backend.pow(x, y)
691    }
692
693    #[inline]
694    fn ln(&self, x: fixed::types::I16F16) -> fixed::types::I16F16 {
695        let fixed_backend = FixedPointBackend;
696        fixed_backend.ln(x)
697    }
698
699    #[inline]
700    fn log10(&self, x: fixed::types::I16F16) -> fixed::types::I16F16 {
701        let fixed_backend = FixedPointBackend;
702        fixed_backend.log10(x)
703    }
704
705    #[inline]
706    fn sin(&self, x: fixed::types::I16F16) -> fixed::types::I16F16 {
707        // Use CORDIC for trigonometric functions
708        let (sin_val, _cos_val) = cordic::sin_cos(x);
709        sin_val
710    }
711
712    #[inline]
713    fn cos(&self, x: fixed::types::I16F16) -> fixed::types::I16F16 {
714        let (_sin_val, cos_val) = cordic::sin_cos(x);
715        cos_val
716    }
717
718    #[inline]
719    fn tan(&self, x: fixed::types::I16F16) -> fixed::types::I16F16 {
720        let sin_x = self.sin(x);
721        let cos_x = self.cos(x);
722
723        if cos_x.abs() < fixed::types::I16F16::from_num(0.001) {
724            if sin_x >= fixed::types::I16F16::ZERO {
725                fixed::types::I16F16::from_num(100.0)
726            } else {
727                fixed::types::I16F16::from_num(-100.0)
728            }
729        } else {
730            sin_x / cos_x
731        }
732    }
733
734    #[inline]
735    fn to_radians(&self, degrees: fixed::types::I16F16) -> fixed::types::I16F16 {
736        let pi = fixed::types::I16F16::from_num(core::f32::consts::PI);
737        degrees * pi / fixed::types::I16F16::from_num(180.0)
738    }
739
740    #[inline]
741    fn to_degrees(&self, radians: fixed::types::I16F16) -> fixed::types::I16F16 {
742        let pi = fixed::types::I16F16::from_num(core::f32::consts::PI);
743        radians * fixed::types::I16F16::from_num(180.0) / pi
744    }
745
746    #[inline]
747    fn atan2(&self, y: fixed::types::I16F16, x: fixed::types::I16F16) -> fixed::types::I16F16 {
748        // Use the fixed-point backend for atan2
749        let fixed_backend = FixedPointBackend;
750        fixed_backend.atan2(y, x)
751    }
752}
753
754// Default backend selection with priority order
755#[cfg(feature = "floating-point")]
756pub use self::FloatingPointBackend as DefaultBackend;
757
758#[cfg(all(feature = "libm-math", not(feature = "floating-point")))]
759pub use self::LibmBackend as DefaultBackend;
760
761#[cfg(all(
762    feature = "fixed-point",
763    not(any(feature = "floating-point", feature = "libm-math"))
764))]
765pub use self::FixedPointBackend as DefaultBackend;
766
767#[cfg(all(
768    feature = "cordic-math",
769    not(any(
770        feature = "floating-point",
771        feature = "libm-math",
772        feature = "fixed-point"
773    ))
774))]
775pub use self::CordicBackend as DefaultBackend;
776
777#[cfg(all(
778    feature = "integer-math",
779    not(any(
780        feature = "floating-point",
781        feature = "libm-math",
782        feature = "fixed-point",
783        feature = "cordic-math"
784    ))
785))]
786pub use self::IntegerBackend as DefaultBackend;
787
788// Final fallback for when no features are explicitly enabled
789#[cfg(not(any(
790    feature = "floating-point",
791    feature = "libm-math",
792    feature = "fixed-point",
793    feature = "cordic-math",
794    feature = "integer-math"
795)))]
796/// Fallback math backend that provides basic implementations without external dependencies
797pub struct FallbackBackend;
798
799#[cfg(not(any(
800    feature = "floating-point",
801    feature = "libm-math",
802    feature = "fixed-point",
803    feature = "cordic-math",
804    feature = "integer-math"
805)))]
806impl MathBackend<f32> for FallbackBackend {
807    fn sqrt(&self, x: f32) -> f32 {
808        // Basic integer square root approximation
809        if x <= 0.0 {
810            return 0.0;
811        }
812        let x_int = x as i32;
813        if x_int <= 0 {
814            return 0.0;
815        }
816
817        // Simple integer sqrt using binary search
818        let mut left = 0i32;
819        let mut right = x_int;
820        let mut result = 0i32;
821
822        while left <= right {
823            let mid = left + (right - left) / 2;
824            let mid_squared = mid.saturating_mul(mid);
825
826            if mid_squared <= x_int {
827                result = mid;
828                left = mid + 1;
829            } else {
830                right = mid - 1;
831            }
832        }
833        result as f32
834    }
835    fn abs(&self, x: f32) -> f32 {
836        if x < 0.0 {
837            -x
838        } else {
839            x
840        }
841    }
842    fn min(&self, a: f32, b: f32) -> f32 {
843        if a < b {
844            a
845        } else {
846            b
847        }
848    }
849    fn max(&self, a: f32, b: f32) -> f32 {
850        if a > b {
851            a
852        } else {
853            b
854        }
855    }
856    fn floor(&self, x: f32) -> f32 {
857        (x as i32) as f32
858    }
859    fn ceil(&self, x: f32) -> f32 {
860        let int_part = x as i32;
861        if x > int_part as f32 {
862            (int_part + 1) as f32
863        } else {
864            int_part as f32
865        }
866    }
867    fn pow(&self, x: f32, y: f32) -> f32 {
868        // Simple integer power approximation
869        if y == 0.0 {
870            return 1.0;
871        }
872        if y == 1.0 {
873            return x;
874        }
875        if y == 2.0 {
876            return x * x;
877        }
878        // For other powers, return x for simplicity in fallback
879        x
880    }
881    fn ln(&self, _x: f32) -> f32 {
882        0.0
883    } // Stub implementation
884    fn log10(&self, _x: f32) -> f32 {
885        0.0
886    } // Stub implementation
887    fn sin(&self, _x: f32) -> f32 {
888        0.0
889    } // Stub implementation
890    fn cos(&self, _x: f32) -> f32 {
891        1.0
892    } // Stub implementation
893    fn tan(&self, _x: f32) -> f32 {
894        0.0
895    } // Stub implementation
896    fn atan2(&self, _y: f32, _x: f32) -> f32 {
897        0.0
898    } // Stub implementation
899    fn to_radians(&self, degrees: f32) -> f32 {
900        degrees * 0.017453292
901    } // Simple approximation
902    fn to_degrees(&self, radians: f32) -> f32 {
903        radians * 57.29578
904    } // Simple approximation
905}
906
907#[cfg(not(any(
908    feature = "floating-point",
909    feature = "libm-math",
910    feature = "fixed-point",
911    feature = "cordic-math",
912    feature = "integer-math"
913)))]
914pub use self::FallbackBackend as DefaultBackend;