1use std::sync::Arc;
2
3use fp::{
4 prime::{Binomial, TWO},
5 vector::FpSliceMut,
6};
7use serde::Deserialize;
8use serde_json::Value;
9
10use crate::{
11 algebra::{
12 AdemAlgebra, Algebra, MilnorAlgebra, SteenrodAlgebra,
13 adem_algebra::AdemBasisElement,
14 milnor_algebra::{MilnorBasisElement, PPartEntry},
15 },
16 module::{Module, ZeroModule},
17};
18
19pub struct RealProjectiveSpace<A: Algebra> {
27 algebra: Arc<A>,
28 pub min: i32,
29 pub max: Option<i32>, pub clear_bottom: bool,
31}
32
33impl<A: Algebra> std::fmt::Display for RealProjectiveSpace<A> {
34 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
35 let clear = if self.clear_bottom {
36 " (clear_bottom)"
37 } else {
38 ""
39 };
40
41 if let Some(max) = self.max {
42 write!(f, "RP^{max}_{}{clear}", self.min)
43 } else {
44 write!(f, "RP_{}{clear}", self.min)
45 }
46 }
47}
48
49impl<A: Algebra> PartialEq for RealProjectiveSpace<A> {
50 fn eq(&self, other: &Self) -> bool {
51 self.min == other.min && self.max == other.max
52 }
53}
54
55impl<A: Algebra> Eq for RealProjectiveSpace<A> {}
56
57impl<A: Algebra> Module for RealProjectiveSpace<A>
58where
59 for<'a> &'a A: TryInto<&'a SteenrodAlgebra>,
60{
61 type Algebra = A;
62
63 fn algebra(&self) -> Arc<A> {
64 Arc::clone(&self.algebra)
65 }
66
67 fn min_degree(&self) -> i32 {
68 self.min
69 }
70
71 fn max_computed_degree(&self) -> i32 {
72 i32::MAX
73 }
74
75 fn dimension(&self, degree: i32) -> usize {
76 if degree < self.min {
77 return 0;
78 }
79 if let Some(m) = self.max
80 && degree > m
81 {
82 return 0;
83 }
84
85 if self.clear_bottom
86 && (degree == self.min + 1
87 || degree == self.min + 1 + 1
88 || degree == self.min + 1 + 2
89 || degree == self.min + 1 + 4
90 || degree == self.min + 1 + 8)
91 {
92 return 0;
93 }
94 1
95 }
96
97 fn basis_element_to_string(&self, degree: i32, _idx: usize) -> String {
98 format!("x^{{{degree}}}")
100 }
101
102 fn act_on_basis(
103 &self,
104 mut result: FpSliceMut,
105 coeff: u32,
106 op_degree: i32,
107 op_index: usize,
108 mod_degree: i32,
109 mod_index: usize,
110 ) {
111 assert!(op_index < self.algebra().dimension(op_degree));
112 assert!(mod_index < self.dimension(mod_degree));
113
114 let output_degree = mod_degree + op_degree;
115
116 if op_degree == 0 || coeff == 0 || self.dimension(output_degree) == 0 {
117 return;
118 }
119
120 if match &(&*self.algebra).try_into() {
121 Ok(SteenrodAlgebra::AdemAlgebra(a)) => coef_adem(a, op_degree, op_index, mod_degree),
122 Ok(SteenrodAlgebra::MilnorAlgebra(a)) => {
123 coef_milnor(a, op_degree, op_index, mod_degree)
124 }
125 Err(_) => unreachable!(),
126 } {
127 result.add_basis_element(0, 1);
128 }
129 }
130
131 fn max_degree(&self) -> Option<i32> {
132 self.max
133 }
134}
135
136fn coef_adem(algebra: &AdemAlgebra, op_deg: i32, op_idx: usize, mut j: i32) -> bool {
138 let elt: &AdemBasisElement = algebra.basis_element_from_index(op_deg, op_idx);
139 for i in elt.ps.iter().rev() {
141 let c = if j >= 0 {
142 i32::binomial(TWO, j, *i as i32)
143 } else {
144 i32::binomial(TWO, -j + (*i as i32) - 1, *i as i32)
145 };
146 if c == 0 {
147 return false;
148 }
149 j += *i as i32;
151 }
152 true
153}
154
155fn coef_milnor(algebra: &MilnorAlgebra, op_deg: i32, op_idx: usize, mut mod_degree: i32) -> bool {
156 if mod_degree == 0 {
157 return false;
158 }
159
160 let elt: &MilnorBasisElement = algebra.basis_element_from_index(op_deg, op_idx);
161
162 let sum: PPartEntry = elt.p_part.iter().sum();
163 if mod_degree < 0 {
164 mod_degree = sum as i32 - mod_degree - 1;
165 } else if mod_degree < sum as i32 {
166 return false;
167 }
168
169 let mod_degree = mod_degree as PPartEntry;
170
171 let mut list = Vec::with_capacity(elt.p_part.len() + 1);
172 list.push(mod_degree - sum);
173 list.extend_from_slice(&elt.p_part);
174
175 PPartEntry::multinomial2(&list) == 1
176}
177
178impl<A: Algebra> ZeroModule for RealProjectiveSpace<A>
179where
180 for<'a> &'a A: TryInto<&'a SteenrodAlgebra>,
181{
182 fn zero_module(algebra: Arc<A>, min_degree: i32) -> Self {
183 Self::new(algebra, min_degree, Some(min_degree - 1), false)
184 }
185}
186
187impl<A: Algebra> RealProjectiveSpace<A>
188where
189 for<'a> &'a A: TryInto<&'a SteenrodAlgebra>,
190{
191 pub fn new(algebra: Arc<A>, min: i32, max: Option<i32>, clear_bottom: bool) -> Self {
192 assert_eq!(algebra.prime(), 2);
193 assert!(
194 (&*algebra).try_into().is_ok(),
195 "Real Projective Space only supports Steenrod Algebra"
196 );
197
198 if let Some(max) = max {
199 assert!(max >= min);
200 }
201 Self {
202 algebra,
203 min,
204 max,
205 clear_bottom,
206 }
207 }
208}
209
210#[derive(Deserialize, Debug)]
211struct RPSpec {
212 min: i32,
213 clear_bottom: Option<bool>,
214 max: Option<i32>,
215}
216
217impl<A: Algebra> RealProjectiveSpace<A> {
218 pub fn from_json(algebra: Arc<A>, json: &Value) -> anyhow::Result<Self> {
219 let spec: RPSpec = RPSpec::deserialize(json)?;
220 let clear_bottom = spec.clear_bottom.unwrap_or(false);
221 let mut min = spec.min;
222 if clear_bottom {
223 let x = (spec.min + 1).rem_euclid(8);
224 if x != 0 {
225 min += 8 - x;
226 }
227 }
228
229 Ok(Self {
230 algebra,
231 min,
232 clear_bottom,
233 max: spec.max,
234 })
235 }
236
237 pub fn to_json(&self, json: &mut Value) {
238 json["name"] = Value::String(self.to_string());
239 json["type"] = Value::from("real projective space");
240 json["min"] = Value::from(self.min);
241 if let Some(max) = self.max {
242 json["max"] = Value::from(max);
243 }
244 if self.clear_bottom {
245 json["clear_bottom"] = Value::Bool(true);
246 }
247 }
248}