fixed_analytics/ops/
circular.rs1use crate::error::{Error, Result};
4use crate::kernel::{circular_rotation, circular_vectoring, cordic_scale_factor};
5use crate::traits::CordicNumber;
6
7#[must_use]
9pub fn sin_cos<T: CordicNumber>(angle: T) -> (T, T) {
10 let pi = T::pi();
11 let frac_pi_2 = T::frac_pi_2();
12 let zero = T::zero();
13
14 let mut reduced = angle;
16 let two_pi = pi + pi;
17 let mut i = 0;
18 while reduced > pi && i < 64 {
19 reduced -= two_pi;
20 i += 1;
21 }
22 i = 0;
23 while reduced < -pi && i < 64 {
24 reduced += two_pi;
25 i += 1;
26 }
27
28 let (reduced, negate) = if reduced > frac_pi_2 {
30 (reduced - pi, true)
31 } else if reduced < -frac_pi_2 {
32 (reduced + pi, true)
33 } else {
34 (reduced, false)
35 };
36
37 let inv_gain = cordic_scale_factor();
39 let (cos_val, sin_val, _) = circular_rotation(inv_gain, zero, reduced);
40
41 if negate {
42 (-sin_val, -cos_val)
43 } else {
44 (sin_val, cos_val)
45 }
46}
47
48#[inline]
50#[must_use]
51pub fn sin<T: CordicNumber>(angle: T) -> T {
52 sin_cos(angle).0
53}
54
55#[inline]
57#[must_use]
58pub fn cos<T: CordicNumber>(angle: T) -> T {
59 sin_cos(angle).1
60}
61
62#[must_use]
64pub fn tan<T: CordicNumber>(angle: T) -> T {
65 let (s, c) = sin_cos(angle);
66 s.div(c)
67}
68
69#[must_use = "returns the arcsine result which should be handled"]
78#[allow(clippy::missing_panics_doc)] pub fn asin<T: CordicNumber>(x: T) -> Result<T> {
80 let one = T::one();
81 let neg_one = -one;
82
83 if x > one || x < neg_one {
84 return Err(Error::domain("asin", "value in range [-1, 1]"));
85 }
86
87 if x == one {
89 return Ok(T::frac_pi_2());
90 }
91 if x == neg_one {
92 return Ok(-T::frac_pi_2());
93 }
94 if x == T::zero() {
95 return Ok(T::zero());
96 }
97
98 let x_sq = x.saturating_mul(x);
101 let one_minus_x_sq = one.saturating_sub(x_sq);
102 #[allow(clippy::expect_used)] let sqrt_term =
105 crate::ops::algebraic::sqrt(one_minus_x_sq).expect("x in [-1,1] guarantees 1-x² ≥ 0");
106
107 if sqrt_term < T::from_i1f63(0x0001_0000_0000_0000) {
109 return if x.is_positive() {
111 Ok(T::frac_pi_2())
112 } else {
113 Ok(-T::frac_pi_2())
114 };
115 }
116
117 Ok(atan(x.div(sqrt_term)))
118}
119
120#[must_use = "returns the arccosine result which should be handled"]
125pub fn acos<T: CordicNumber>(x: T) -> Result<T> {
126 asin(x).map(|a| T::frac_pi_2() - a)
128}
129
130#[must_use]
132pub fn atan<T: CordicNumber>(x: T) -> T {
133 let zero = T::zero();
134 let one = T::one();
135
136 if x == zero {
138 return zero;
139 }
140
141 let abs_x = x.abs();
144 if abs_x > one {
145 let recip = one.div(x);
146 let atan_recip = circular_vectoring(one, recip, zero).2;
147
148 if x.is_positive() {
149 T::frac_pi_2() - atan_recip
150 } else {
151 -T::frac_pi_2() - atan_recip
152 }
153 } else {
154 circular_vectoring(one, x, zero).2
156 }
157}
158
159#[must_use]
161pub fn atan2<T: CordicNumber>(y: T, x: T) -> T {
162 let zero = T::zero();
163 let pi = T::pi();
164 let frac_pi_2 = T::frac_pi_2();
165
166 if x == zero {
168 return if y.is_negative() {
169 -frac_pi_2
170 } else if y == zero {
171 zero } else {
173 frac_pi_2
174 };
175 }
176
177 if y == zero {
178 return if x.is_negative() { pi } else { zero };
179 }
180
181 let (_, _, base_angle) = circular_vectoring(x.abs(), y.abs(), zero);
184
185 match (x.is_negative(), y.is_negative()) {
187 (false, false) => base_angle,
189 (false, true) => -base_angle,
191 (true, false) => pi - base_angle,
193 (true, true) => base_angle - pi,
195 }
196}