retrofire_core/math/
space.rs1use core::fmt::{Debug, Formatter};
6use core::iter::zip;
7use core::marker::PhantomData;
8
9use crate::math::vary::{Iter, Vary, ZDiv};
10
11pub trait Affine: Sized {
15 type Space;
17 type Diff: Linear;
20
21 const DIM: usize;
23
24 fn add(&self, diff: &Self::Diff) -> Self;
28
29 fn sub(&self, other: &Self) -> Self::Diff;
33
34 fn combine<S: Copy, const N: usize>(
42 weights: &[S; N],
43 points: &[Self; N],
44 ) -> Self
45 where
46 Self: Clone,
47 Self::Diff: Linear<Scalar = S>,
48 {
49 const { assert!(N != 0) }
50
51 let p0 = &points[0]; zip(&weights[1..], &points[1..])
53 .fold(p0.clone(), |res, (w, q)| res.add(&q.sub(p0).mul(*w)))
54 }
55}
56
57pub trait Linear: Affine<Diff = Self> {
68 type Scalar: Sized;
70
71 fn zero() -> Self;
73
74 fn neg(&self) -> Self {
76 Self::zero().sub(self)
77 }
78
79 fn mul(&self, scalar: Self::Scalar) -> Self;
92}
93
94#[derive(Copy, Clone, Default, Eq, PartialEq)]
98pub struct Real<const DIM: usize, Basis = ()>(PhantomData<Basis>);
99
100#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
105pub struct Proj3;
106
107#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
108pub struct Hom<const DIM: usize, Basis = ()>(PhantomData<Basis>);
109
110impl Affine for f32 {
111 type Space = ();
112 type Diff = Self;
113 const DIM: usize = 1;
114
115 fn add(&self, other: &f32) -> f32 {
116 self + other
117 }
118 fn sub(&self, other: &f32) -> f32 {
119 self - other
120 }
121}
122
123impl Linear for f32 {
124 type Scalar = f32;
125
126 fn zero() -> f32 {
127 0.0
128 }
129 fn mul(&self, rhs: f32) -> f32 {
130 self * rhs
131 }
132}
133
134impl Affine for i32 {
135 type Space = ();
136 type Diff = Self;
137 const DIM: usize = 1;
138
139 fn add(&self, rhs: &i32) -> i32 {
140 self + rhs
141 }
142 fn sub(&self, rhs: &i32) -> i32 {
143 self - rhs
144 }
145}
146
147impl Linear for i32 {
148 type Scalar = Self;
149
150 fn zero() -> i32 {
151 0
152 }
153 fn mul(&self, rhs: i32) -> i32 {
154 self * rhs
155 }
156}
157
158impl Affine for u32 {
159 type Space = ();
160 type Diff = i32;
161 const DIM: usize = 1;
162
163 fn add(&self, rhs: &i32) -> u32 {
164 let (res, o) = self.overflowing_add_signed(*rhs);
165 debug_assert!(!o, "overflow adding {rhs}_i32 to {self}_u32");
166 res
167 }
168
169 fn sub(&self, rhs: &u32) -> i32 {
170 let diff = *self as i64 - *rhs as i64;
171 debug_assert!(
172 i32::try_from(diff).is_ok(),
173 "overflow subtracting {rhs}_u32 from {self}_u32"
174 );
175 diff as i32
176 }
177}
178
179impl<V: Clone> Vary for V
180where
181 Self: Affine<Diff: Linear<Scalar = f32> + Clone> + ZDiv,
182{
183 type Iter = Iter<Self>;
184 type Diff = <Self as Affine>::Diff;
185
186 #[inline]
187 fn vary(self, step: Self::Diff, n: Option<u32>) -> Self::Iter {
188 Iter { val: self, step, n }
189 }
190
191 fn dv_dt(&self, other: &Self, recip_dt: f32) -> Self::Diff {
192 other.sub(self).mul(recip_dt)
193 }
194
195 #[inline]
197 fn step(&self, delta: &Self::Diff) -> Self {
198 self.add(delta)
199 }
200}
201
202impl<const DIM: usize, B> Debug for Real<DIM, B>
203where
204 B: Debug + Default,
205{
206 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
207 const DIMS: [&str; 4] = ["", "²", "³", "⁴"];
208 let b = B::default();
209 if let Some(dim) = DIMS.get(DIM - 1) {
210 write!(f, "ℝ{dim}<{b:?}>")
211 } else {
212 write!(f, "ℝ^{DIM}<{b:?}>")
213 }
214 }
215}
216
217#[cfg(test)]
218mod tests {
219 use super::*;
220
221 mod f32 {
222 use super::*;
223
224 #[test]
225 fn affine_ops() {
226 assert_eq!(f32::DIM, 1);
227
228 assert_eq!(1_f32.add(&2_f32), 3_f32);
229 assert_eq!(3_f32.add(&-2_f32), 1_f32);
230
231 assert_eq!(3_f32.sub(&2_f32), 1_f32);
232 assert_eq!(1_f32.sub(&4_f32), -3_f32);
233 }
234
235 #[test]
236 fn linear_ops() {
237 assert_eq!(f32::zero(), 0.0);
238
239 assert_eq!(2_f32.neg(), -2_f32);
240 assert_eq!(-3_f32.neg(), 3_f32);
241
242 assert_eq!(3_f32.mul(2_f32), 6_f32);
243 assert_eq!(3_f32.mul(0.5_f32), 1.5_f32);
244 assert_eq!(3_f32.mul(-2_f32), -6_f32);
245 }
246 }
247
248 mod i32 {
249 use super::*;
250
251 #[test]
252 fn affine_ops() {
253 assert_eq!(i32::DIM, 1);
254
255 assert_eq!(1_i32.add(&2_i32), 3_i32);
256 assert_eq!(2_i32.add(&-3_i32), -1_i32);
257
258 assert_eq!(3_i32.sub(&2_i32), 1_i32);
259 assert_eq!(3_i32.sub(&4_i32), -1_i32);
260 }
261
262 #[test]
263 fn linear_ops() {
264 assert_eq!(i32::zero(), 0);
265
266 assert_eq!(2_i32.neg(), -2_i32);
267 assert_eq!(-3_i32.neg(), 3_i32);
268
269 assert_eq!(3_i32.mul(2_i32), 6_i32);
270 assert_eq!(2_i32.mul(-3_i32), -6_i32);
271 }
272 }
273
274 mod u32 {
275 use super::*;
276
277 #[test]
278 fn affine_ops() {
279 assert_eq!(u32::DIM, 1);
280
281 assert_eq!(1_u32.add(&2_i32), 3_u32);
282 assert_eq!(3_u32.add(&-2_i32), 1_u32);
283
284 assert_eq!(3_u32.sub(&2_u32), 1_i32);
285 assert_eq!(3_u32.sub(&4_u32), -1_i32);
286 }
287
288 #[test]
289 #[should_panic]
290 fn affine_add_underflow_should_panic() {
291 _ = 3_u32.add(&-4_i32);
292 }
293
294 #[test]
295 #[should_panic]
296 fn affine_add_overflow_should_panic() {
297 _ = (u32::MAX / 2 + 2).add(&i32::MAX);
298 }
299
300 #[test]
301 #[should_panic]
302 fn affine_sub_underflow_should_panic() {
303 _ = 3_u32.sub(&u32::MAX);
304 }
305
306 #[test]
307 #[should_panic]
308 fn affine_sub_overflow_should_panic() {
309 _ = u32::MAX.sub(&1_u32);
310 }
311 }
312}