1use alloc::vec::Vec;
2use core::marker::PhantomData;
3use crate::core::ops::{Group, Magma, Monoid, Semigroup, TopologicalGroup};
4use crate::groups::matrix_group::{GroupError, MatrixGroup};
5use crate::topology::manifold::{Atlas, Dim, Manifold};
6use crate::core::scalar::FiniteF64;
7use crate::maps::exp_log::HasExpMap;
8
9#[derive(Debug, Clone, PartialEq, Eq)]
10pub struct Aff<D: Dim> {
11 inner: MatrixGroup,
12 _dim: PhantomData<D>,
13}
14
15#[derive(Debug, Clone, PartialEq, Eq)]
16pub struct AffAlgebra<D: Dim> {
17 inner: MatrixGroup,
18 _dim: PhantomData<D>,
19}
20
21impl<D: Dim> Aff<D> {
22 pub fn from_homogeneous(data: Vec<f64>, dim: D) -> Result<Self, GroupError> {
23 let n = dim.value();
24 let nh = n + 1;
25 if data.len() != nh * nh {
26 return Err(GroupError::DimensionMismatch);
27 }
28 for j in 0..n {
29 if libm::fabs(data[n * nh + j]) > 1e-10 {
30 return Err(GroupError::ConstraintViolated("bottom row must be [0..0,1]"));
31 }
32 }
33 if libm::fabs(data[n * nh + n] - 1.0) > 1e-10 {
34 return Err(GroupError::ConstraintViolated("bottom right must be 1"));
35 }
36 let inner = MatrixGroup::new(data, nh)?;
37 let linear_det: f64 = {
38 let linear: Vec<f64> = (0..n)
39 .flat_map(|i| (0..n).map(move |j| (i, j)))
40 .map(|(i, j)| inner.get(i, j))
41 .collect();
42 MatrixGroup::new(linear, n)
43 .map(|m| m.det())
44 .unwrap_or(0.0)
45 };
46 if libm::fabs(linear_det) < 1e-10 {
47 return Err(GroupError::ConstraintViolated("linear part must be invertible"));
48 }
49 Ok(Self { inner, _dim: PhantomData })
50 }
51
52 pub fn from_parts(linear: Vec<f64>, translation: Vec<f64>, dim: D) -> Result<Self, GroupError> {
53 let n = dim.value();
54 if linear.len() != n * n || translation.len() != n {
55 return Err(GroupError::DimensionMismatch);
56 }
57 let nh = n + 1;
58 let mut data = alloc::vec![0.0f64; nh * nh];
59 for i in 0..n {
60 for j in 0..n {
61 data[i * nh + j] = linear[i * n + j];
62 }
63 data[i * nh + n] = translation[i];
64 }
65 data[n * nh + n] = 1.0;
66 Self::from_homogeneous(data, dim)
67 }
68
69 pub fn identity(dim: D) -> Self {
70 Self { inner: MatrixGroup::identity(dim.value() + 1), _dim: PhantomData }
71 }
72
73 pub fn linear_part(&self, dim: D) -> Vec<f64> {
74 let n = dim.value();
75 (0..n)
76 .flat_map(|i| (0..n).map(move |j| (i, j)))
77 .map(|(i, j)| self.inner.get(i, j))
78 .collect()
79 }
80
81 pub fn translation_part(&self, dim: D) -> Vec<f64> {
82 let n = dim.value();
83 (0..n).map(|i| self.inner.get(i, n)).collect()
84 }
85
86 pub fn to_homogeneous(&self) -> Vec<f64> { self.inner.data() }
87 pub fn n(&self) -> usize { self.inner.n() - 1 }
88}
89
90impl<D: Dim> AffAlgebra<D> {
91 pub fn from_homogeneous(data: Vec<f64>, dim: D) -> Result<Self, GroupError> {
92 let nh = dim.value() + 1;
93 if data.len() != nh * nh {
94 return Err(GroupError::DimensionMismatch);
95 }
96 let inner = MatrixGroup::new(data, nh)?;
97 Ok(Self { inner, _dim: PhantomData })
98 }
99
100 pub fn zero(dim: D) -> Self {
101 let nh = dim.value() + 1;
102 Self {
103 inner: MatrixGroup::new(alloc::vec![0.0; nh * nh], nh).unwrap(),
104 _dim: PhantomData,
105 }
106 }
107
108 pub fn data(&self) -> Vec<f64> { self.inner.data() }
109}
110
111impl<D: Dim> Magma for Aff<D> {
112 fn op(&self, other: &Self) -> Self {
113 Self {
114 inner: self.inner.mul(&other.inner).expect("Aff multiplication must succeed"),
115 _dim: PhantomData,
116 }
117 }
118}
119
120impl<D: Dim> Semigroup for Aff<D> {}
121
122impl<D: Dim> Monoid for Aff<D> {
123 fn identity() -> Self {
124 panic!("Aff identity requires dimension; use Aff::identity(dim)")
125 }
126}
127
128impl<D: Dim> Group for Aff<D> {
129 fn inverse(&self) -> Self {
130 Self {
131 inner: self.inner.inverse().expect("Aff element must be invertible"),
132 _dim: PhantomData,
133 }
134 }
135}
136
137impl<D: Dim> TopologicalGroup for Aff<D> {}
138
139impl<D: Dim> Manifold for Aff<D> {
140 type Scalar = FiniteF64;
141 fn dim(&self) -> usize { let n = self.inner.n() - 1; n * n + n }
142 fn atlas(&self) -> &Atlas<FiniteF64> { unimplemented!("Aff atlas not yet constructed") }
143}
144
145impl<D: Dim> HasExpMap for Aff<D> {
146 type Algebra = AffAlgebra<D>;
147 fn exp(x: &AffAlgebra<D>) -> Self {
148 Self { inner: x.inner.exp(), _dim: PhantomData }
149 }
150 fn log(&self) -> Option<AffAlgebra<D>> {
151 Some(AffAlgebra { inner: self.inner.log()?, _dim: PhantomData })
152 }
153}