1use crate::{
12 discrete::Z,
13 impl_lie_group_via_quotient,
14 traits::{Interval, Tensor},
15};
16use core::marker::PhantomData;
17
18use crate::traits::{Chart, Euclidean, Group, LieGroup, Quotient, Real, Smooth};
19use num_traits::{Euclid, NumCast, One, Zero, real::Real as _};
20
21#[derive(Copy, Clone, Debug, PartialEq)]
24pub struct S1<V: Euclidean + From<[<V as Tensor>::F; 1]>>(V);
25
26impl<V: Euclidean<F: Real> + From<[V::F; 1]>> Interval for S1<V> {
27 type R = V::F;
28
29 fn interval_squared(&self, other: &Self) -> V::F {
30 self.to_local(other).unwrap().norm_squared()
31 }
32}
33
34impl<V: Euclidean<F: Real> + From<[<V as Tensor>::F; 1]>> Quotient<V, Z<V>, V> for S1<V> {
35 fn new(g: V) -> Self {
36 let one = V::F::one();
37 let mut d = g[0].rem_euclid(&one);
38 if d == one {
39 d = V::F::zero();
41 }
42 Self([d].into())
43 }
44
45 fn lift(&self) -> V {
46 let half = V::F::one() / (V::F::one() + V::F::one());
49 let mut d = self.0[0];
50 if d > half {
51 d = d - V::F::one();
52 }
53 [d].into()
54 }
55
56 fn embed(h: Z<V>) -> V {
57 [<V::F as NumCast>::from(h.0).unwrap()].into()
58 }
59}
60
61impl_lie_group_via_quotient!(S1<V>, V, Z<V>, V, V: Euclidean + From<[<V as Tensor>::F; 1]>);
62
63#[derive(Debug, Copy, Clone, PartialEq)]
65pub struct Torus<I: Euclidean + From<[I::F; 1]> + From<[V::F; 1]>, V: Euclidean + From<[I::F; 2]>>(
66 S1<I>,
67 S1<I>,
68 PhantomData<V>,
69);
70
71impl<I: ICompatible<V>, V: VCompatible<I>> Torus<I, V> {
72 pub fn new(a: S1<I>, b: S1<I>) -> Self {
74 Self(a, b, PhantomData)
75 }
76}
77
78impl<I: ICompatible<V>, V: VCompatible<I>> Interval for Torus<I, V>
79where
80 I::F: From<V::F>,
81{
82 type R = I::F;
83
84 fn interval_squared(&self, other: &Self) -> I::F {
85 self.to_local(other).unwrap().norm_squared().into()
86 }
87}
88
89impl<I: ICompatible<V>, V: VCompatible<I>> Group for Torus<I, V> {
90 fn identity() -> Self {
91 Self::new(S1::identity(), S1::identity())
92 }
93
94 fn compose(&self, other: &Self) -> Self {
95 Self::new(self.0.compose(&other.0), self.1.compose(&other.1))
96 }
97
98 fn inverse(&self) -> Self {
99 Self::new(self.0.inverse(), self.1.inverse())
100 }
101}
102
103impl<I: ICompatible<V>, V: VCompatible<I>> LieGroup<V> for Torus<I, V> {
104 fn identity_exp(v: V) -> Self {
105 let v0 = [v[0]].into();
106 let v1 = [v[1]].into();
107 Self::new(S1::identity_exp(v0), S1::identity_exp(v1))
108 }
109
110 fn identity_log(p: &Self) -> Option<V> {
111 let a = S1::identity_log(&p.0)?;
112 let b = S1::identity_log(&p.1)?;
113
114 Some([a[0], b[0]].into())
115 }
116}
117
118pub trait ICompatible<V: Euclidean<F: Real + Send + Sync> + From<[Self::F; 2]>>:
122 Euclidean<F: Real + From<V::F>> + From<[Self::F; 1]> + From<[V::F; 1]> + 'static + Send + Sync
123{
124}
125
126pub trait VCompatible<I: Euclidean<F: Real + From<Self::F>> + From<[I::F; 1]> + From<[Self::F; 1]>>:
129 Euclidean<F: Real + Send + Sync> + From<[I::F; 2]> + 'static + Send + Sync
130{
131}
132
133impl<
134 I: Euclidean<F: Real + From<V::F>> + From<[I::F; 1]> + From<[V::F; 1]> + 'static + Send + Sync,
135 V: Euclidean<F: Real + Send + Sync> + From<[I::F; 2]> + 'static + Send + Sync,
136> ICompatible<V> for I
137{
138}
139
140impl<
141 I: Euclidean<F: Real + From<V::F>> + From<[I::F; 1]> + From<[V::F; 1]> + 'static + Send + Sync,
142 V: Euclidean<F: Real + Send + Sync> + From<[I::F; 2]> + 'static + Send + Sync,
143> VCompatible<I> for V
144{
145}
146
147#[derive(Debug, Copy, Clone, PartialEq)]
152pub struct KleinBottle<I: ICompatible<V>, V: VCompatible<I>>(S1<I>, S1<I>, PhantomData<V>);
153
154impl<I: ICompatible<V>, V: VCompatible<I>> KleinBottle<I, V> {
155 pub fn new(a: S1<I>, b: S1<I>) -> Self {
157 Self(a, b, PhantomData)
158 }
159}
160
161impl<I: ICompatible<V>, V: VCompatible<I>> Smooth<V> for KleinBottle<I, V> {
162 type Global = Self;
163
164 fn exp(&self, v: V) -> Self {
165 let (x, y) = self.coords();
166 let vx: I::F = v[0].into();
167 let vy: I::F = v[1].into();
168 Self::from_cover(x + vx, y + vy)
169 }
170
171 fn log(&self, other: &Self) -> Option<V> {
172 let one = I::F::one();
173 let two = one + one;
174 let (sx, sy) = self.coords();
175 let (ox, oy) = other.coords();
176 let mut best: Option<(I::F, I::F)> = None;
177 let mut best_sq = I::F::zero();
178
179 for n in [-one, I::F::zero(), one] {
180 let n_odd = n.rem_euclid(&two) != I::F::zero();
181 let base_ox = if n_odd { -ox } else { ox };
187 for m in [-one, I::F::zero(), one] {
188 let cx = base_ox + m;
189 let cy = oy + n;
190 let dx = cx - sx;
191 let dy = cy - sy;
192 let sq = dx * dx + dy * dy;
193 if best.is_none() || sq < best_sq {
194 best = Some((dx, dy));
195 best_sq = sq;
196 }
197 }
198 }
199 best.map(|(dx, dy)| [dx, dy].into())
200 }
201}
202
203impl<I: ICompatible<V>, V: VCompatible<I>> KleinBottle<I, V> {
204 fn from_cover(x: I::F, y: I::F) -> Self {
215 let one = I::F::one();
216 let two = one + one;
217 let ky = y.round(); let y_red = y - ky; let ky_parity_odd = ky.rem_euclid(&two) != I::F::zero();
221 let x_oriented = if ky_parity_odd { -x } else { x };
222
223 Self(
226 S1::new([x_oriented].into()),
227 S1::new([y_red].into()),
228 PhantomData,
229 )
230 }
231
232 fn coords(&self) -> (I::F, I::F) {
233 (self.0.lift()[0], self.1.lift()[0])
234 }
235}
236
237impl<I: ICompatible<V>, V: VCompatible<I>> Interval for KleinBottle<I, V> {
238 type R = I::F;
239
240 fn interval_squared(&self, other: &Self) -> I::F {
241 self.to_local(other).unwrap().norm_squared().into()
242 }
243}
244
245#[cfg(feature = "simplicial")]
246mod simplicial {
247 use super::*;
248 use crate::{
249 impl_tangent_bundle_via_bounded,
250 traits::simplicial::{Bounded, BuildNodes, NerveComplexParameters},
251 };
252 use std::vec::Vec;
253 #[derive(Debug, Copy, Clone)]
255 pub struct TorusCover<I: ICompatible<V>, V: VCompatible<I>>(Torus<I, V>);
256
257 impl<I: ICompatible<V>, V: VCompatible<I>> From<Torus<I, V>> for TorusCover<I, V> {
258 fn from(value: Torus<I, V>) -> Self {
259 Self(value)
260 }
261 }
262
263 impl<I: ICompatible<V>, V: VCompatible<I>> AsRef<Torus<I, V>> for TorusCover<I, V> {
264 fn as_ref(&self) -> &Torus<I, V> {
265 &self.0
266 }
267 }
268
269 const S: usize = 4;
270
271 impl<I: ICompatible<V>, V: VCompatible<I>> Bounded<Torus<I, V>, Torus<I, V>, V>
272 for TorusCover<I, V>
273 {
274 fn sdf(&self, v: &V) -> <V as Tensor>::F {
275 let to = |x| <V::F as NumCast>::from(x).unwrap();
276 v.norm() - (to(2).sqrt() + to(2)) / to(4 * S)
277 }
278 }
279
280 impl_tangent_bundle_via_bounded!(
281 TorusCover<I, V>,
282 Torus<I, V>,
283 Torus<I, V>,
284 V,
285 I: ICompatible<V>,
286 V: VCompatible<I>
287 );
288
289 impl<I: ICompatible<V>, V: VCompatible<I>> BuildNodes<TorusCover<I, V>> for TorusCover<I, V> {
290 fn build_nodes() -> Vec<Self> {
291 let to = |x| <I::F as NumCast>::from(x).unwrap();
292 let s = to(S);
293 let offset = to(1) / (to(2) * s);
294
295 (0..S)
296 .flat_map(|y| (0..S).map(move |x| (x, y)))
297 .map(|(x, y)| {
298 Torus::new(
299 S1([offset + to(x) / s].into()),
300 S1([offset + to(y) / s].into()),
301 )
302 .into()
303 })
304 .collect()
305 }
306 }
307
308 impl<I: ICompatible<V>, V: VCompatible<I>>
309 NerveComplexParameters<Torus<I, V>, V, Torus<I, V>, TorusCover<I, V>> for TorusCover<I, V>
310 {
311 }
312
313 #[derive(Debug, Copy, Clone)]
315 pub struct KleinBottleCover<I: ICompatible<V>, V: VCompatible<I>>(KleinBottle<I, V>);
316
317 impl<I: ICompatible<V>, V: VCompatible<I>> From<KleinBottle<I, V>> for KleinBottleCover<I, V> {
318 fn from(value: KleinBottle<I, V>) -> Self {
319 Self(value)
320 }
321 }
322
323 impl<I: ICompatible<V>, V: VCompatible<I>> AsRef<KleinBottle<I, V>> for KleinBottleCover<I, V> {
324 fn as_ref(&self) -> &KleinBottle<I, V> {
325 &self.0
326 }
327 }
328
329 impl<I: ICompatible<V>, V: VCompatible<I>> Bounded<KleinBottle<I, V>, KleinBottle<I, V>, V>
330 for KleinBottleCover<I, V>
331 {
332 fn sdf(&self, v: &V) -> <V as Tensor>::F {
333 let to = |x| <V::F as NumCast>::from(x).unwrap();
334 v.norm() - (to(2).sqrt() + to(2)) / to(4 * S)
335 }
336 }
337
338 impl_tangent_bundle_via_bounded!(
339 KleinBottleCover<I, V>,
340 KleinBottle<I, V>,
341 KleinBottle<I, V>,
342 V,
343 I: ICompatible<V>, V: VCompatible<I>
344 );
345
346 impl<I: ICompatible<V>, V: VCompatible<I>> BuildNodes<KleinBottleCover<I, V>>
347 for KleinBottleCover<I, V>
348 {
349 fn build_nodes() -> Vec<Self> {
350 let to = |x| <I::F as NumCast>::from(x).unwrap();
351 let s = to(S);
352 let offset = to(1) / (to(2) * s);
353
354 (0..S)
355 .flat_map(|y| (0..S).map(move |x| (x, y)))
356 .map(|(x, y)| {
357 KleinBottle::new(
358 S1([offset + to(x) / s].into()),
359 S1([offset + to(y) / s].into()),
360 )
361 .into()
362 })
363 .collect()
364 }
365 }
366
367 impl<I: ICompatible<V>, V: VCompatible<I>>
368 NerveComplexParameters<KleinBottle<I, V>, V, KleinBottle<I, V>, KleinBottleCover<I, V>>
369 for KleinBottleCover<I, V>
370 {
371 }
372
373 #[derive(Debug, Clone)]
379 pub struct MyopicTorus<I: ICompatible<V>, V: VCompatible<I>>(pub Torus<I, V>);
380
381 impl<I: ICompatible<V>, V: VCompatible<I>> MyopicTorus<I, V> {
382 pub fn s() -> usize {
384 8
385 }
386
387 fn radius() -> V::F {
388 (V::F::one() + V::F::one()) / <V::F as NumCast>::from(Self::s()).unwrap()
390 }
391 }
392
393 impl<I: ICompatible<V>, V: VCompatible<I>> AsRef<Torus<I, V>> for MyopicTorus<I, V> {
394 fn as_ref(&self) -> &Torus<I, V> {
395 &self.0
396 }
397 }
398
399 impl<I: ICompatible<V>, V: VCompatible<I>> From<Torus<I, V>> for MyopicTorus<I, V> {
400 fn from(value: Torus<I, V>) -> Self {
401 Self(value)
402 }
403 }
404
405 impl<I: ICompatible<V>, V: VCompatible<I>> Bounded<Torus<I, V>, Torus<I, V>, V>
406 for MyopicTorus<I, V>
407 {
408 fn sdf(&self, v: &V) -> <V as Tensor>::F {
409 v.norm() - Self::radius()
410 }
411 }
412
413 impl_tangent_bundle_via_bounded!(
414 MyopicTorus<I, V>,
415 Torus<I, V>,
416 Torus<I, V>,
417 V,
418 I: ICompatible<V>, V: VCompatible<I>
419 );
420
421 #[derive(Debug, Clone)]
423 pub struct MyopicTorusCover<I: ICompatible<V>, V: VCompatible<I>>(MyopicTorus<I, V>);
424
425 impl<I: ICompatible<V>, V: VCompatible<I>> AsRef<MyopicTorus<I, V>> for MyopicTorusCover<I, V> {
426 fn as_ref(&self) -> &MyopicTorus<I, V> {
427 &self.0
428 }
429 }
430
431 impl<I: ICompatible<V>, V: VCompatible<I>> From<MyopicTorus<I, V>> for MyopicTorusCover<I, V> {
432 fn from(value: MyopicTorus<I, V>) -> Self {
433 Self(value)
434 }
435 }
436
437 impl<I: ICompatible<V>, V: VCompatible<I>> Bounded<MyopicTorus<I, V>, Torus<I, V>, V>
438 for MyopicTorusCover<I, V>
439 {
440 fn sdf(&self, v: &V) -> <V as Tensor>::F {
441 let to = |x| <V::F as NumCast>::from(x).unwrap();
442 v.norm() - (to(2).sqrt() + to(2)) / to(4 * MyopicTorus::<I, V>::s())
443 }
444 }
445
446 impl_tangent_bundle_via_bounded!(
447 MyopicTorusCover<I, V>,
448 MyopicTorus<I, V>,
449 Torus<I, V>,
450 V,
451 I: ICompatible<V>, V: VCompatible<I>
452 );
453
454 impl<I: ICompatible<V>, V: VCompatible<I>> BuildNodes<MyopicTorusCover<I, V>>
455 for MyopicTorusCover<I, V>
456 {
457 fn build_nodes() -> Vec<Self> {
458 let to = |x| <I::F as NumCast>::from(x).unwrap();
459 let s_usize = MyopicTorus::<I, V>::s();
460 let s = to(s_usize);
461 let offset = to(1) / (to(2) * s);
462
463 (0..s_usize)
464 .flat_map(|y| (0..s_usize).map(move |x| (x, y)))
465 .map(|(x, y)| {
466 MyopicTorus(Torus::new(
467 S1([offset + to(x) / s].into()),
468 S1([offset + to(y) / s].into()),
469 ))
470 .into()
471 })
472 .collect()
473 }
474 }
475
476 impl<I: ICompatible<V>, V: VCompatible<I>>
477 NerveComplexParameters<Torus<I, V>, V, MyopicTorus<I, V>, MyopicTorusCover<I, V>>
478 for MyopicTorusCover<I, V>
479 {
480 fn overestimation_bound() -> Option<(V::F, V::F)> {
481 let to = |x| <V::F as NumCast>::from(x).unwrap();
482 let s = to(MyopicTorus::<I, V>::s());
483 let kappa = (to(4) - to(2) * to(2).sqrt()).sqrt();
485 let delta_s = to(2).sqrt() / (to(2) * s);
487 Some((kappa, (V::F::one() + kappa) * to(2) * delta_s))
488 }
489 }
490}
491
492#[cfg(feature = "simplicial")]
493pub use simplicial::*;