1use crate::error::{IffError, Result};
9use serde::{Deserialize, Serialize};
10use std::fmt;
11use std::ops::{Add, Sub, Mul, Div, Neg};
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
15#[repr(transparent)]
16pub struct Fixed(i32);
17
18impl Fixed {
19 pub const FRAC_BITS: u32 = 16;
21
22 pub const SCALE: i32 = 1 << Self::FRAC_BITS;
24
25 pub const ZERO: Fixed = Fixed(0);
27
28 pub const ONE: Fixed = Fixed(Self::SCALE);
30
31 pub const NEG_ONE: Fixed = Fixed(-Self::SCALE);
33
34 pub const HALF: Fixed = Fixed(Self::SCALE / 2);
36
37 pub const PI: Fixed = Fixed(205887); pub const TWO_PI: Fixed = Fixed(411775); #[inline]
45 pub const fn from_raw(val: i32) -> Self {
46 Fixed(val)
47 }
48
49 #[inline]
51 pub const fn raw(self) -> i32 {
52 self.0
53 }
54
55 #[inline]
57 pub const fn from_int(val: i32) -> Self {
58 Fixed(val * Self::SCALE)
59 }
60
61 #[inline]
63 pub const fn to_int(self) -> i32 {
64 self.0 / Self::SCALE
65 }
66
67 #[inline]
69 pub const fn to_int_round(self) -> i32 {
70 (self.0 + Self::SCALE / 2) / Self::SCALE
71 }
72
73 #[inline]
75 pub fn from_f32(val: f32) -> Self {
76 Fixed((val * Self::SCALE as f32) as i32)
77 }
78
79 #[inline]
81 pub fn to_f32(self) -> f32 {
82 self.0 as f32 / Self::SCALE as f32
83 }
84
85 #[inline]
87 pub const fn abs(self) -> Self {
88 Fixed(self.0.abs())
89 }
90
91 #[inline]
93 pub const fn floor(self) -> Self {
94 Fixed(self.0 & !((1 << Self::FRAC_BITS) - 1))
95 }
96
97 #[inline]
99 pub const fn ceil(self) -> Self {
100 let mask = (1 << Self::FRAC_BITS) - 1;
101 if self.0 & mask == 0 {
102 Fixed(self.0)
103 } else {
104 Fixed((self.0 | mask) + 1)
105 }
106 }
107
108 #[inline]
110 pub const fn fract(self) -> Self {
111 Fixed(self.0 & ((1 << Self::FRAC_BITS) - 1))
112 }
113
114 #[inline]
116 pub fn mul_checked(self, rhs: Fixed) -> Result<Fixed> {
117 let product = (self.0 as i64) * (rhs.0 as i64);
118 let result = (product >> Self::FRAC_BITS) as i32;
119
120 if (product >> Self::FRAC_BITS) != result as i64 {
122 return Err(IffError::FixedPointOverflow {
123 operation: format!("{} * {}", self.to_f32(), rhs.to_f32()),
124 });
125 }
126
127 Ok(Fixed(result))
128 }
129
130 #[inline]
132 pub fn div_checked(self, rhs: Fixed) -> Result<Fixed> {
133 if rhs.0 == 0 {
134 return Err(IffError::FixedPointOverflow {
135 operation: "division by zero".to_string(),
136 });
137 }
138
139 let dividend = (self.0 as i64) << Self::FRAC_BITS;
140 let result = (dividend / rhs.0 as i64) as i32;
141
142 Ok(Fixed(result))
143 }
144
145 pub fn sqrt(self) -> Result<Fixed> {
147 if self.0 < 0 {
148 return Err(IffError::FixedPointOverflow {
149 operation: "sqrt of negative number".to_string(),
150 });
151 }
152
153 if self.0 == 0 {
154 return Ok(Fixed::ZERO);
155 }
156
157 let mut x = Fixed(self.0 >> 1);
159
160 for _ in 0..10 {
162 let x_div = self.div_checked(x)?;
163 let x_new = (x + x_div).0 >> 1;
164 if (x_new - x.0).abs() <= 1 {
165 break;
166 }
167 x = Fixed(x_new);
168 }
169
170 Ok(x)
171 }
172
173 pub fn sin_small(self) -> Fixed {
176 let x = self.0;
178 let x2 = ((x as i64 * x as i64) >> Self::FRAC_BITS) as i32;
179 let x3 = ((x2 as i64 * x as i64) >> Self::FRAC_BITS) as i32;
180 let x5 = ((x3 as i64 * x2 as i64) >> Self::FRAC_BITS) as i32;
181
182 let term1 = x;
183 let term2 = x3 / 6;
184 let term3 = x5 / 120;
185
186 Fixed(term1 - term2 + term3)
187 }
188
189 pub fn cos_small(self) -> Fixed {
191 let x = self.0;
193 let x2 = ((x as i64 * x as i64) >> Self::FRAC_BITS) as i32;
194 let x4 = ((x2 as i64 * x2 as i64) >> Self::FRAC_BITS) as i32;
195
196 let term1 = Self::SCALE;
197 let term2 = x2 / 2;
198 let term3 = x4 / 24;
199
200 Fixed(term1 - term2 + term3)
201 }
202
203 pub fn sin(self) -> Fixed {
205 let two_pi = Self::TWO_PI.0;
207 let mut x = self.0 % two_pi;
208 if x > Self::PI.0 {
209 x -= two_pi;
210 } else if x < -Self::PI.0 {
211 x += two_pi;
212 }
213
214 Fixed(x).sin_small()
215 }
216
217 pub fn cos(self) -> Fixed {
219 (self + Fixed(Self::PI.0 / 2)).sin()
221 }
222
223 #[inline]
225 pub const fn min(self, other: Fixed) -> Fixed {
226 if self.0 < other.0 {
227 self
228 } else {
229 other
230 }
231 }
232
233 #[inline]
235 pub const fn max(self, other: Fixed) -> Fixed {
236 if self.0 > other.0 {
237 self
238 } else {
239 other
240 }
241 }
242
243 #[inline]
245 pub const fn clamp(self, min: Fixed, max: Fixed) -> Fixed {
246 if self.0 < min.0 {
247 min
248 } else if self.0 > max.0 {
249 max
250 } else {
251 self
252 }
253 }
254}
255
256impl Add for Fixed {
257 type Output = Fixed;
258
259 #[inline]
260 fn add(self, rhs: Fixed) -> Fixed {
261 Fixed(self.0.wrapping_add(rhs.0))
262 }
263}
264
265impl Sub for Fixed {
266 type Output = Fixed;
267
268 #[inline]
269 fn sub(self, rhs: Fixed) -> Fixed {
270 Fixed(self.0.wrapping_sub(rhs.0))
271 }
272}
273
274impl Mul for Fixed {
275 type Output = Fixed;
276
277 #[inline]
278 fn mul(self, rhs: Fixed) -> Fixed {
279 let product = (self.0 as i64) * (rhs.0 as i64);
280 Fixed((product >> Self::FRAC_BITS) as i32)
281 }
282}
283
284impl Div for Fixed {
285 type Output = Fixed;
286
287 #[inline]
288 fn div(self, rhs: Fixed) -> Fixed {
289 let dividend = (self.0 as i64) << Self::FRAC_BITS;
290 Fixed((dividend / rhs.0 as i64) as i32)
291 }
292}
293
294impl Neg for Fixed {
295 type Output = Fixed;
296
297 #[inline]
298 fn neg(self) -> Fixed {
299 Fixed(self.0.wrapping_neg())
300 }
301}
302
303impl fmt::Display for Fixed {
304 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
305 write!(f, "{:.6}", self.to_f32())
306 }
307}
308
309impl From<i32> for Fixed {
310 fn from(val: i32) -> Self {
311 Fixed::from_int(val)
312 }
313}
314
315impl From<u16> for Fixed {
316 fn from(val: u16) -> Self {
317 Fixed::from_int(val as i32)
318 }
319}
320
321#[cfg(test)]
322mod tests {
323 use super::*;
324 use approx::assert_relative_eq;
325
326 #[test]
327 fn test_constants() {
328 assert_eq!(Fixed::ZERO.to_f32(), 0.0);
329 assert_eq!(Fixed::ONE.to_f32(), 1.0);
330 assert_eq!(Fixed::NEG_ONE.to_f32(), -1.0);
331 assert_eq!(Fixed::HALF.to_f32(), 0.5);
332 assert_relative_eq!(Fixed::PI.to_f32(), std::f32::consts::PI, epsilon = 0.001);
333 }
334
335 #[test]
336 fn test_from_int() {
337 assert_eq!(Fixed::from_int(5).to_int(), 5);
338 assert_eq!(Fixed::from_int(-3).to_int(), -3);
339 assert_eq!(Fixed::from_int(0).to_int(), 0);
340 }
341
342 #[test]
343 fn test_arithmetic() {
344 let a = Fixed::from_int(3);
345 let b = Fixed::from_int(2);
346
347 assert_eq!((a + b).to_int(), 5);
348 assert_eq!((a - b).to_int(), 1);
349 assert_eq!((a * b).to_int(), 6);
350 assert_eq!((a / b).to_int(), 1); }
352
353 #[test]
354 fn test_fractional() {
355 let a = Fixed::from_f32(3.5);
356 let b = Fixed::from_f32(2.25);
357
358 assert_relative_eq!((a + b).to_f32(), 5.75, epsilon = 0.01);
359 assert_relative_eq!((a - b).to_f32(), 1.25, epsilon = 0.01);
360 assert_relative_eq!((a * b).to_f32(), 7.875, epsilon = 0.01);
361 }
362
363 #[test]
364 fn test_sqrt() {
365 let tests = vec![
366 (4.0, 2.0),
367 (9.0, 3.0),
368 (2.0, 1.414),
369 (0.25, 0.5),
370 ];
371
372 for (input, expected) in tests {
373 let result = Fixed::from_f32(input).sqrt().unwrap();
374 assert_relative_eq!(result.to_f32(), expected, epsilon = 0.01);
375 }
376 }
377
378 #[test]
379 fn test_trig() {
380 let angles = vec![0.0, 0.1, 0.3, 0.5];
382
383 for angle in angles {
384 let sin_result = Fixed::from_f32(angle).sin().to_f32();
385 let cos_result = Fixed::from_f32(angle).cos().to_f32();
386
387 assert_relative_eq!(sin_result, angle.sin(), epsilon = 0.15);
388 assert_relative_eq!(cos_result, angle.cos(), epsilon = 0.15);
389 }
390 }
391
392 #[test]
393 fn test_floor_ceil() {
394 let a = Fixed::from_f32(3.7);
395 assert_eq!(a.floor().to_int(), 3);
396 assert_eq!(a.ceil().to_int(), 4);
397
398 let b = Fixed::from_f32(-2.3);
399 assert_eq!(b.floor().to_int(), -3);
400 assert_eq!(b.ceil().to_int(), -2);
401 }
402
403 #[test]
404 fn test_min_max_clamp() {
405 let a = Fixed::from_int(5);
406 let b = Fixed::from_int(10);
407
408 assert_eq!(a.min(b), a);
409 assert_eq!(a.max(b), b);
410
411 let c = Fixed::from_int(7);
412 assert_eq!(c.clamp(a, b), c);
413
414 let d = Fixed::from_int(3);
415 assert_eq!(d.clamp(a, b), a);
416
417 let e = Fixed::from_int(15);
418 assert_eq!(e.clamp(a, b), b);
419 }
420
421 #[test]
422 fn test_determinism() {
423 let a = Fixed::from_int(7);
425 let b = Fixed::from_int(3);
426
427 for _ in 0..100 {
428 assert_eq!((a * b).raw(), (a * b).raw());
429 assert_eq!((a / b).raw(), (a / b).raw());
430 }
431 }
432}