1use crate::structure::*;
2use algebraeon_nzq::Natural;
3use algebraeon_sets::structure::*;
4use std::{borrow::Cow, fmt};
5
6pub mod quaternion_orders;
7
8#[derive(Debug, Clone)]
18pub struct QuaternionAlgebraStructure<Field: FieldSignature> {
19 base: Field,
20 is_char_2: bool,
21 a: Field::Set,
22 b: Field::Set,
23}
24
25impl<Field: FieldSignature + CharacteristicSignature> QuaternionAlgebraStructure<Field> {
26 pub fn base_field(&self) -> &Field {
27 &self.base
28 }
29
30 pub fn new(base: Field, a: Field::Set, b: Field::Set) -> Self {
31 let is_char_2 = base.characteristic() == Natural::TWO;
32 Self {
33 base,
34 is_char_2,
35 a,
36 b,
37 }
38 }
39}
40
41impl<Field: FieldSignature + ToStringSignature + fmt::Display> fmt::Display
42 for QuaternionAlgebraStructure<Field>
43{
44 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45 write!(
46 f,
47 "Quaternion Algebra ({}, {}) over base field {}",
48 self.base.to_string(&self.a),
49 self.base.to_string(&self.b),
50 self.base
51 )
52 }
53}
54
55#[derive(Debug, Clone, Copy, PartialEq, Eq, CanonicalStructure)]
56#[canonical_structure(eq)]
57pub enum QuaternionAlgebraBasis {
58 R,
59 I,
60 J,
61 K,
62}
63
64impl CountableSetSignature for QuaternionAlgebraBasisCanonicalStructure {
65 fn generate_all_elements(&self) -> impl Iterator<Item = Self::Set> + Clone {
66 vec![
67 QuaternionAlgebraBasis::R,
68 QuaternionAlgebraBasis::I,
69 QuaternionAlgebraBasis::J,
70 QuaternionAlgebraBasis::K,
71 ]
72 .into_iter()
73 }
74}
75
76impl FiniteSetSignature for QuaternionAlgebraBasisCanonicalStructure {
77 fn size(&self) -> usize {
78 4
79 }
80}
81
82#[derive(Debug, Clone)]
83pub struct QuaternionAlgebraElement<Set> {
84 x: Set,
86 y: Set,
87 z: Set,
88 w: Set,
89}
90
91impl<Field: FieldSignature> PartialEq for QuaternionAlgebraStructure<Field> {
92 fn eq(&self, other: &Self) -> bool {
93 self.base == other.base
94 && self.base.equal(&self.a, &other.a)
95 && self.base.equal(&self.b, &other.b)
96 }
97}
98
99impl<Field: FieldSignature> Eq for QuaternionAlgebraStructure<Field> {}
100
101impl<Field: FieldSignature> EqSignature for QuaternionAlgebraStructure<Field> {
102 fn equal(&self, a: &Self::Set, b: &Self::Set) -> bool {
103 self.base.equal(&a.x, &b.x)
104 && self.base.equal(&a.y, &b.y)
105 && self.base.equal(&a.z, &b.z)
106 && self.base.equal(&a.w, &b.w)
107 }
108}
109
110impl<Field: FieldSignature> Signature for QuaternionAlgebraStructure<Field> {}
111
112impl<Field: FieldSignature> SetSignature for QuaternionAlgebraStructure<Field> {
113 type Set = QuaternionAlgebraElement<Field::Set>;
114
115 fn is_element(&self, _x: &Self::Set) -> Result<(), String> {
116 Ok(())
117 }
118}
119
120impl<Field: FieldSignature> RinglikeSpecializationSignature for QuaternionAlgebraStructure<Field> {
121 fn try_ring_restructure(&self) -> Option<impl EqSignature<Set = Self::Set> + RingSignature> {
122 Some(self.clone())
123 }
124}
125
126impl<Field: FieldSignature> ZeroSignature for QuaternionAlgebraStructure<Field> {
127 fn zero(&self) -> Self::Set {
128 QuaternionAlgebraElement {
129 x: self.base.zero(),
130 y: self.base.zero(),
131 z: self.base.zero(),
132 w: self.base.zero(),
133 }
134 }
135}
136
137impl<Field: FieldSignature> AdditionSignature for QuaternionAlgebraStructure<Field> {
138 fn add(&self, a: &Self::Set, b: &Self::Set) -> Self::Set {
139 QuaternionAlgebraElement {
140 x: self.base.add(&a.x, &b.x),
141 y: self.base.add(&a.y, &b.y),
142 z: self.base.add(&a.z, &b.z),
143 w: self.base.add(&a.w, &b.w),
144 }
145 }
146}
147
148impl<Field: FieldSignature> CancellativeAdditionSignature for QuaternionAlgebraStructure<Field> {
149 fn try_sub(&self, a: &Self::Set, b: &Self::Set) -> Option<Self::Set> {
150 Some(self.sub(a, b))
151 }
152}
153
154impl<Field: FieldSignature> TryNegateSignature for QuaternionAlgebraStructure<Field> {
155 fn try_neg(&self, a: &Self::Set) -> Option<Self::Set> {
156 Some(self.neg(a))
157 }
158}
159
160impl<Field: FieldSignature> AdditiveMonoidSignature for QuaternionAlgebraStructure<Field> {}
161
162impl<Field: FieldSignature> AdditiveGroupSignature for QuaternionAlgebraStructure<Field> {
163 fn neg(&self, a: &Self::Set) -> Self::Set {
164 QuaternionAlgebraElement {
165 x: self.base.neg(&a.x),
166 y: self.base.neg(&a.y),
167 z: self.base.neg(&a.z),
168 w: self.base.neg(&a.w),
169 }
170 }
171
172 fn sub(&self, a: &Self::Set, b: &Self::Set) -> Self::Set {
173 QuaternionAlgebraElement {
174 x: self.base.sub(&a.x, &b.x),
175 y: self.base.sub(&a.y, &b.y),
176 z: self.base.sub(&a.z, &b.z),
177 w: self.base.sub(&a.w, &b.w),
178 }
179 }
180}
181
182impl<Field: FieldSignature> OneSignature for QuaternionAlgebraStructure<Field> {
183 fn one(&self) -> Self::Set {
184 QuaternionAlgebraElement {
185 x: self.base.one(),
186 y: self.base.zero(),
187 z: self.base.zero(),
188 w: self.base.zero(),
189 }
190 }
191}
192
193impl<Field: FieldSignature> MultiplicationSignature for QuaternionAlgebraStructure<Field> {
194 fn mul(&self, a: &Self::Set, b: &Self::Set) -> Self::Set {
195 let a_param = &self.a;
196 let b_param = &self.b;
197 let base = &self.base;
198 let ab = base.mul(a_param, b_param);
199
200 if self.is_char_2 {
201 unimplemented!("Quaternion multiplication for char=2 is not implemented yet");
207 } else {
208 let z0 = base.sub(
215 &base.add(
216 &base.add(
217 &base.mul(&a.x, &b.x),
218 &base.mul(&base.mul(&a.y, &b.y), a_param),
219 ),
220 &base.mul(&base.mul(&a.z, &b.z), b_param),
221 ),
222 &base.mul(&base.mul(&a.w, &b.w), &ab),
223 );
224 let z1 = base.sub(
225 &base.add(
226 &base.add(&base.mul(&a.x, &b.y), &base.mul(&a.y, &b.x)),
227 &base.mul(&base.mul(&a.z, &b.w), b_param),
228 ),
229 &base.mul(&base.mul(&a.w, &b.z), b_param),
230 );
231 let z2 = base.add(
232 &base.sub(
233 &base.add(&base.mul(&a.x, &b.z), &base.mul(&a.z, &b.x)),
234 &base.mul(&base.mul(&a.y, &b.w), a_param),
235 ),
236 &base.mul(&base.mul(&a.w, &b.y), a_param),
237 );
238 let z3 = base.add(
239 &base.sub(
240 &base.add(&base.mul(&a.x, &b.w), &base.mul(&a.w, &b.x)),
241 &base.mul(&a.z, &b.y),
242 ),
243 &base.mul(&a.y, &b.z),
244 );
245
246 QuaternionAlgebraElement {
247 x: z0,
248 y: z1,
249 z: z2,
250 w: z3,
251 }
252 }
253 }
254}
255
256impl<Field: FieldSignature> CommutativeMultiplicationSignature
257 for QuaternionAlgebraStructure<Field>
258{
259}
260
261impl<Field: FieldSignature> MultiplicativeMonoidSignature for QuaternionAlgebraStructure<Field> {}
262
263impl<Field: FieldSignature> MultiplicativeAbsorptionMonoidSignature
264 for QuaternionAlgebraStructure<Field>
265{
266}
267
268impl<Field: FieldSignature> LeftDistributiveMultiplicationOverAddition
269 for QuaternionAlgebraStructure<Field>
270{
271}
272
273impl<Field: FieldSignature> RightDistributiveMultiplicationOverAddition
274 for QuaternionAlgebraStructure<Field>
275{
276}
277
278impl<Field: FieldSignature> SemiRingSignature for QuaternionAlgebraStructure<Field> {}
279
280impl<Field: FieldSignature> TryReciprocalSignature for QuaternionAlgebraStructure<Field> {
281 fn try_reciprocal(&self, a: &Self::Set) -> Option<Self::Set> {
282 let n_inv = self.base.try_reciprocal(&self.reduced_norm(a))?;
283 Some(self.scalar_mul(&self.conjugate(a), &n_inv))
284 }
285
286 fn is_unit(&self, a: &Self::Set) -> bool {
287 self.base.is_unit(&self.reduced_norm(a))
288 }
289}
290
291impl<Field: FieldSignature> RingSignature for QuaternionAlgebraStructure<Field> {}
292
293impl<Field: FieldSignature> SemiModuleSignature<Field> for QuaternionAlgebraStructure<Field> {
294 fn ring(&self) -> &Field {
295 &self.base
296 }
297
298 fn scalar_mul(&self, a: &Self::Set, x: &Field::Set) -> Self::Set {
299 let base = &self.base;
300 QuaternionAlgebraElement {
301 x: base.mul(x, &a.x),
302 y: base.mul(x, &a.y),
303 z: base.mul(x, &a.z),
304 w: base.mul(x, &a.w),
305 }
306 }
307}
308
309impl<Field: FieldSignature> AlgebraSignature<Field> for QuaternionAlgebraStructure<Field> {}
310
311impl<Field: FieldSignature> FreeModuleSignature<Field> for QuaternionAlgebraStructure<Field> {
312 type Basis = QuaternionAlgebraBasisCanonicalStructure;
313
314 fn basis_set(&self) -> impl std::borrow::Borrow<Self::Basis> {
315 Self::Basis {}
316 }
317
318 fn to_component<'a>(
319 &self,
320 b: &QuaternionAlgebraBasis,
321 v: &'a Self::Set,
322 ) -> Cow<'a, Field::Set> {
323 Cow::Borrowed(match b {
324 QuaternionAlgebraBasis::R => &v.x,
325 QuaternionAlgebraBasis::I => &v.y,
326 QuaternionAlgebraBasis::J => &v.z,
327 QuaternionAlgebraBasis::K => &v.w,
328 })
329 }
330
331 fn from_component(&self, b: &QuaternionAlgebraBasis, r: &Field::Set) -> Self::Set {
332 let mut v = self.zero();
333 match b {
334 QuaternionAlgebraBasis::R => v.x = r.clone(),
335 QuaternionAlgebraBasis::I => v.y = r.clone(),
336 QuaternionAlgebraBasis::J => v.z = r.clone(),
337 QuaternionAlgebraBasis::K => v.w = r.clone(),
338 }
339 v
340 }
341}
342
343impl<Field: FieldSignature + CharacteristicSignature> CharacteristicSignature
344 for QuaternionAlgebraStructure<Field>
345{
346 fn characteristic(&self) -> Natural {
347 self.base.characteristic()
348 }
349}
350
351impl<Field: CharZeroFieldSignature> CharZeroRingSignature for QuaternionAlgebraStructure<Field> {
352 fn try_to_int(&self, a: &Self::Set) -> Option<algebraeon_nzq::Integer> {
353 if self.base.is_zero(&a.y) && self.base.is_zero(&a.z) && self.base.is_zero(&a.w) {
355 self.base.try_to_int(&a.x)
356 } else {
357 None
358 }
359 }
360}
361
362impl<Field: FieldSignature> QuaternionAlgebraStructure<Field> {
363 pub fn i(&self) -> QuaternionAlgebraElement<Field::Set> {
364 QuaternionAlgebraElement {
365 x: self.base.zero(),
366 y: self.base.one(),
367 z: self.base.zero(),
368 w: self.base.zero(),
369 }
370 }
371
372 pub fn j(&self) -> QuaternionAlgebraElement<Field::Set> {
373 QuaternionAlgebraElement {
374 x: self.base.zero(),
375 y: self.base.zero(),
376 z: self.base.one(),
377 w: self.base.zero(),
378 }
379 }
380
381 pub fn k(&self) -> QuaternionAlgebraElement<Field::Set> {
382 QuaternionAlgebraElement {
383 x: self.base.zero(),
384 y: self.base.zero(),
385 z: self.base.zero(),
386 w: self.base.one(),
387 }
388 }
389
390 pub fn conjugate(
391 &self,
392 a: &QuaternionAlgebraElement<Field::Set>,
393 ) -> QuaternionAlgebraElement<Field::Set> {
394 let base = &self.base;
395 if self.is_char_2 {
396 QuaternionAlgebraElement {
398 x: base.add(&a.x, &a.y),
399 y: a.y.clone(),
400 z: a.z.clone(),
401 w: a.w.clone(),
402 }
403 } else {
404 QuaternionAlgebraElement {
405 x: a.x.clone(),
406 y: base.neg(&a.y),
407 z: base.neg(&a.z),
408 w: base.neg(&a.w),
409 }
410 }
411 }
412
413 pub fn reduced_trace(&self, a: &QuaternionAlgebraElement<Field::Set>) -> Field::Set {
414 if self.is_char_2 {
415 a.y.clone()
417 } else {
418 self.base.add(&a.x, &a.x) }
420 }
421
422 pub fn reduced_norm(&self, a: &QuaternionAlgebraElement<Field::Set>) -> Field::Set {
423 let base = &self.base;
424 let a_param = &self.a;
425 let b_param = &self.b;
426 let ab = base.mul(a_param, b_param);
427
428 if self.is_char_2 {
429 let t2 = base.mul(&a.x, &a.x);
432 let tx = base.mul(&a.x, &a.y);
433 let ax2 = base.mul(a_param, &base.mul(&a.y, &a.y));
434 let by2 = base.mul(b_param, &base.mul(&a.z, &a.z));
435 let byz = base.mul(b_param, &base.mul(&a.z, &a.w));
436 let abz2 = base.mul(&ab, &base.mul(&a.w, &a.w));
437 base.add(
438 &base.add(&base.add(&base.add(&base.add(&t2, &tx), &ax2), &by2), &byz),
439 &abz2,
440 )
441 } else {
442 let term0 = base.mul(&a.x, &a.x);
443 let term1 = base.mul(a_param, &base.mul(&a.y, &a.y));
444 let term2 = base.mul(b_param, &base.mul(&a.z, &a.z));
445 let term3 = base.mul(&ab, &base.mul(&a.w, &a.w));
446
447 base.sub(&base.sub(&base.add(&term0, &term3), &term1), &term2)
448 }
449 }
450}
451
452#[cfg(test)]
453mod tests {
454 use super::*;
455 use algebraeon_nzq::Rational;
456
457 #[test]
458 fn test_add_and_mul() {
459 let h =
461 QuaternionAlgebraStructure::new(Rational::structure(), -Rational::ONE, -Rational::ONE);
462
463 let i = h.i();
464 let j = h.j();
465 let i_plus_j = h.add(&i, &j);
466 let j_plus_i = h.add(&j, &i);
467 let i_times_j = h.mul(&i, &j);
468 let j_times_i = h.mul(&j, &i);
469
470 assert!(h.equal(&i_plus_j, &j_plus_i));
471 assert!(h.equal(&i_times_j, &h.neg(&j_times_i)));
472 }
473
474 #[test]
475 fn test_reduced_norm_from_sage_example() {
476 let h = QuaternionAlgebraStructure::new(
477 Rational::structure(),
478 Rational::from(-5i32),
479 Rational::from(-2i32),
480 );
481
482 assert_eq!(h.reduced_norm(&h.i()), Rational::from(5i32));
483 assert_eq!(h.reduced_norm(&h.j()), Rational::from(2i32));
484
485 let a = QuaternionAlgebraElement {
486 x: Rational::from_integers(1, 3),
487 y: Rational::from_integers(1, 5),
488 z: Rational::from_integers(1, 7),
489 w: Rational::ONE,
490 };
491
492 let expected = Rational::from_integers(22826, 2205);
493 assert_eq!(h.reduced_norm(&a), expected);
494 }
495}