1use std::alloc::Global;
2use std::cmp::min;
3
4use crate::algorithms::linsolve::smith::determinant_using_pre_smith;
5use crate::algorithms::linsolve::LinSolveRing;
6use crate::algorithms::linsolve::LinSolveRingStore;
7use crate::algorithms::matmul::ComputeInnerProduct;
8use crate::algorithms::poly_factor::FactorPolyField;
9use crate::divisibility::DivisibilityRing;
10use crate::matrix::OwnedMatrix;
11use crate::field::*;
12use crate::pid::PrincipalIdealRing;
13use crate::ring::*;
14use crate::seq::*;
15use crate::homomorphism::*;
16use crate::wrapper::RingElementWrapper;
17use super::field::AsField;
18use super::field::AsFieldBase;
19use super::poly::dense_poly::DensePolyRing;
20use super::poly::{PolyRingStore, PolyRing};
21
22pub mod extension_impl;
27
28pub mod galois_field;
32
33pub mod number_field;
34
35pub mod conway;
39
40pub trait FreeAlgebra: RingExtension {
94
95 type VectorRepresentation<'a>: VectorFn<El<Self::BaseRing>>
96 where Self: 'a;
97
98 fn canonical_gen(&self) -> Self::Element;
102
103 fn rank(&self) -> usize;
107
108 fn wrt_canonical_basis<'a>(&'a self, el: &'a Self::Element) -> Self::VectorRepresentation<'a>;
116
117 fn from_canonical_basis<V>(&self, vec: V) -> Self::Element
125 where V: IntoIterator<Item = El<Self::BaseRing>>,
126 V::IntoIter: DoubleEndedIterator
127 {
128 let mut given_len = 0;
129 let x = self.canonical_gen();
130 let mut result = self.zero();
131 for c in vec.into_iter().rev() {
132 self.mul_assign_ref(&mut result, &x);
133 self.add_assign(&mut result, self.from(c));
134 given_len += 1;
135 }
136 assert_eq!(given_len, self.rank());
137 return result;
138 }
139
140 fn from_canonical_basis_extended<V>(&self, vec: V) -> Self::Element
146 where V: IntoIterator<Item = El<Self::BaseRing>>
147 {
148 default_implementations::from_canonical_basis_extended(self, vec)
149 }
150
151 fn charpoly<P, H>(&self, el: &Self::Element, poly_ring: P, hom: H) -> El<P>
152 where P: RingStore,
153 P::Type: PolyRing,
154 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: LinSolveRing,
155 H: Homomorphism<<Self::BaseRing as RingStore>::Type, <<P::Type as RingExtension>::BaseRing as RingStore>::Type>
156 {
157 default_implementations::charpoly(self, el, poly_ring, hom)
158 }
159
160 fn trace(&self, el: Self::Element) -> El<Self::BaseRing> {
169 let mut current = el;
170 let generator = self.canonical_gen();
171 return self.base_ring().sum((0..self.rank()).map(|i| {
172 let result = self.wrt_canonical_basis(¤t).at(i);
173 self.mul_assign_ref(&mut current, &generator);
174 return result;
175 }));
176 }
177
178 fn discriminant(&self) -> El<Self::BaseRing>
191 where <Self::BaseRing as RingStore>::Type: PrincipalIdealRing
192 {
193 default_implementations::discriminant(self)
194 }
195}
196
197pub trait FreeAlgebraStore: RingStore
201 where Self::Type: FreeAlgebra
202{
203 delegate!{ FreeAlgebra, fn canonical_gen(&self) -> El<Self> }
204 delegate!{ FreeAlgebra, fn rank(&self) -> usize }
205 delegate!{ FreeAlgebra, fn trace(&self, el: El<Self>) -> El<<Self::Type as RingExtension>::BaseRing> }
206
207 fn wrt_canonical_basis<'a>(&'a self, el: &'a El<Self>) -> <Self::Type as FreeAlgebra>::VectorRepresentation<'a> {
211 self.get_ring().wrt_canonical_basis(el)
212 }
213
214 fn from_canonical_basis<V>(&self, vec: V) -> El<Self>
218 where V: IntoIterator<Item = El<<Self::Type as RingExtension>::BaseRing>>,
219 V::IntoIter: DoubleEndedIterator
220 {
221 self.get_ring().from_canonical_basis(vec)
222 }
223
224 fn from_canonical_basis_extended<V>(&self, vec: V) -> El<Self>
228 where V: IntoIterator<Item = El<<Self::Type as RingExtension>::BaseRing>>
229 {
230 self.get_ring().from_canonical_basis_extended(vec)
231 }
232
233 fn generating_poly<P, H>(&self, poly_ring: P, hom: H) -> El<P>
238 where P: PolyRingStore,
239 P::Type: PolyRing,
240 H: Homomorphism<<<Self::Type as RingExtension>::BaseRing as RingStore>::Type, <<P::Type as RingExtension>::BaseRing as RingStore>::Type>
241 {
242 assert!(hom.domain().get_ring() == self.base_ring().get_ring());
243 poly_ring.sub(
244 poly_ring.from_terms([(poly_ring.base_ring().one(), self.rank())].into_iter()),
245 self.poly_repr(&poly_ring, &self.pow(self.canonical_gen(), self.rank()), hom)
246 )
247 }
248
249 fn as_field(self) -> Result<AsField<Self>, Self>
255 where Self::Type: DivisibilityRing,
256 <<Self::Type as RingExtension>::BaseRing as RingStore>::Type: Field + FactorPolyField
257 {
258 let poly_ring = DensePolyRing::new(self.base_ring(), "X");
259 if <_ as FactorPolyField>::factor_poly(&poly_ring, &self.generating_poly(&poly_ring, self.base_ring().identity())).0.len() > 1 {
260 return Err(self);
261 } else {
262 return Ok(RingValue::from(AsFieldBase::promise_is_perfect_field(self)));
263 }
264 }
265
266 fn poly_repr<P, H>(&self, to: P, el: &El<Self>, hom: H) -> El<P>
272 where P: PolyRingStore,
273 P::Type: PolyRing,
274 H: Homomorphism<<<Self::Type as RingExtension>::BaseRing as RingStore>::Type, <<P::Type as RingExtension>::BaseRing as RingStore>::Type>
275 {
276 let coeff_vec = self.wrt_canonical_basis(el);
277 to.from_terms(
278 (0..self.rank()).map(|i| coeff_vec.at(i)).enumerate()
279 .filter(|(_, x)| !self.base_ring().is_zero(x))
280 .map(|(j, x)| (hom.map(x), j))
281 )
282 }
283
284 fn discriminant(&self) -> El<<Self::Type as RingExtension>::BaseRing>
292 where <<Self::Type as RingExtension>::BaseRing as RingStore>::Type: PrincipalIdealRing
293 {
294 self.get_ring().discriminant()
295 }
296
297 fn charpoly<P, H>(&self, el: &El<Self>, poly_ring: P, hom: H) -> El<P>
301 where P: RingStore,
302 P::Type: PolyRing,
303 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: LinSolveRing,
304 H: Homomorphism<<<Self::Type as RingExtension>::BaseRing as RingStore>::Type, <<P::Type as RingExtension>::BaseRing as RingStore>::Type>
305 {
306 self.get_ring().charpoly(el, poly_ring, hom)
307 }
308
309 #[stability::unstable(feature = "enable")]
310 fn with_wrapped_generator<'a, F, const M: usize>(&'a self, f: F) -> [El<Self>; M]
311 where F: FnOnce(&RingElementWrapper<&'a Self>) -> [RingElementWrapper<&'a Self>; M]
312 {
313 let wrapped_indet = RingElementWrapper::new(self, self.canonical_gen());
314 let mut result_it = f(&wrapped_indet).into_iter();
315 return std::array::from_fn(|_| result_it.next().unwrap().unwrap());
316 }
317}
318
319mod default_implementations {
325 use super::*;
326
327 pub(super) fn from_canonical_basis_extended<R, V>(ring: &R, vec: V) -> R::Element
331 where R: ?Sized + FreeAlgebra,
332 V: IntoIterator<Item = El<<R as RingExtension>::BaseRing>>
333 {
334 let mut data = vec.into_iter().collect::<Vec<_>>();
335 let power_of_canonical_gen = ring.mul(
336 ring.from_canonical_basis((1..ring.rank()).map(|_| ring.base_ring().zero()).chain([ring.base_ring().one()].into_iter())),
337 ring.canonical_gen()
338 );
339 let mut current_power = ring.one();
340 return <_ as ComputeInnerProduct>::inner_product(ring, (0..).map_while(|_| {
341 if data.len() == 0 {
342 return None;
343 }
344 let taken_elements = min(data.len(), ring.rank());
345 let chunk = data.drain(..taken_elements).chain((taken_elements..ring.rank()).map(|_| ring.base_ring().zero()));
346 let current = ring.from_canonical_basis(chunk);
347 let result = (current, ring.clone_el(¤t_power));
348 ring.mul_assign_ref(&mut current_power, &power_of_canonical_gen);
349 return Some(result);
350 }));
351 }
352
353 pub(super) fn charpoly<R, P, H>(ring: &R, el: &R::Element, poly_ring: P, hom: H) -> El<P>
357 where R: ?Sized + FreeAlgebra,
358 P: RingStore,
359 P::Type: PolyRing,
360 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: LinSolveRing,
361 H: Homomorphism<<R::BaseRing as RingStore>::Type, <<P::Type as RingExtension>::BaseRing as RingStore>::Type>
362 {
363 assert!(!ring.is_zero(el));
364 let base_ring = hom.codomain();
365 let mut lhs = OwnedMatrix::zero(ring.rank(), ring.rank(), &base_ring);
366 let mut current = ring.one();
367 for j in 0..ring.rank() {
368 let wrt_basis = ring.wrt_canonical_basis(¤t);
369 for i in 0..ring.rank() {
370 *lhs.at_mut(i, j) = hom.map(wrt_basis.at(i));
371 }
372 drop(wrt_basis);
373 ring.mul_assign_ref(&mut current, el);
374 }
375 let mut rhs = OwnedMatrix::zero(ring.rank(), 1, &base_ring);
376 let wrt_basis = ring.wrt_canonical_basis(¤t);
377 for i in 0..ring.rank() {
378 *rhs.at_mut(i, 0) = base_ring.negate(hom.map(wrt_basis.at(i)));
379 }
380 let mut sol = OwnedMatrix::zero(ring.rank(), 1, &base_ring);
381 <_ as LinSolveRingStore>::solve_right(base_ring, lhs.data_mut(), rhs.data_mut(), sol.data_mut()).assert_solved();
382
383 return poly_ring.from_terms((0..ring.rank()).map(|i| (base_ring.clone_el(sol.at(i, 0)), i)).chain([(base_ring.one(), ring.rank())].into_iter()));
384 }
385
386 pub(super) fn discriminant<R>(ring: &R) -> El<R::BaseRing>
390 where R: ?Sized + FreeAlgebra,
391 <R::BaseRing as RingStore>::Type: PrincipalIdealRing
392 {
393 let mut current = ring.one();
394 let generator = ring.canonical_gen();
395 let traces = (0..(2 * ring.rank())).map(|_| {
396 let result = ring.trace(ring.clone_el(¤t));
397 ring.mul_assign_ref(&mut current, &generator);
398 return result;
399 }).collect::<Vec<_>>();
400 let mut matrix = OwnedMatrix::from_fn(ring.rank(), ring.rank(), |i, j| ring.base_ring().clone_el(&traces[i + j]));
401 let result = determinant_using_pre_smith(ring.base_ring(), matrix.data_mut(), Global);
402 return result;
403 }
404}
405
406#[stability::unstable(feature = "enable")]
407pub fn create_multiplication_matrix<R: FreeAlgebraStore>(ring: R, el: &El<R>) -> OwnedMatrix<El<<R::Type as RingExtension>::BaseRing>>
408 where R::Type: FreeAlgebra
409{
410 let mut result = OwnedMatrix::zero(ring.rank(), ring.rank(), ring.base_ring());
411 let mut current = ring.clone_el(el);
412 let gen = ring.canonical_gen();
413 for i in 0..ring.rank() {
414 {
415 let current_basis_repr = ring.wrt_canonical_basis(¤t);
416 for j in 0..ring.rank() {
417 *result.at_mut(j, i) = current_basis_repr.at(j);
418 }
419 }
420 ring.mul_assign_ref(&mut current, &gen);
421 }
422 return result;
423}
424
425impl<R: RingStore> FreeAlgebraStore for R
426 where R::Type: FreeAlgebra
427{}
428
429#[cfg(any(test, feature = "generic_tests"))]
430pub mod generic_tests {
431 use super::*;
432
433 pub fn test_free_algebra_axioms<R: FreeAlgebraStore>(ring: R)
434 where R::Type: FreeAlgebra
435 {
436 let x = ring.canonical_gen();
437 let n = ring.rank();
438
439 let xn_original = ring.pow(ring.clone_el(&x), n);
440 let xn_vec = ring.wrt_canonical_basis(&xn_original);
441 let xn = ring.sum(Iterator::map(0..n, |i| ring.mul(ring.inclusion().map(xn_vec.at(i)), ring.pow(ring.clone_el(&x), i))));
442 assert_el_eq!(ring, xn_original, xn);
443
444 let x_n_1_vec_expected = (0..n).map_fn(|i| if i > 0 {
445 ring.base_ring().add(ring.base_ring().mul(xn_vec.at(n - 1), xn_vec.at(i)), xn_vec.at(i - 1))
446 } else {
447 ring.base_ring().mul(xn_vec.at(n - 1), xn_vec.at(0))
448 });
449 let x_n_1 = ring.pow(ring.clone_el(&x), n + 1);
450 let x_n_1_vec_actual = ring.wrt_canonical_basis(&x_n_1);
451 for i in 0..n {
452 assert_el_eq!(ring.base_ring(), x_n_1_vec_expected.at(i), x_n_1_vec_actual.at(i));
453 }
454
455 for i in (0..ring.rank()).step_by(5) {
457 for j in (1..ring.rank()).step_by(7) {
458 if i == j {
459 continue;
460 }
461 let element = ring.from_canonical_basis((0..n).map(|k| if k == i { ring.base_ring().one() } else if k == j { ring.base_ring().int_hom().map(2) } else { ring.base_ring().zero() }));
462 let expected = ring.add(ring.pow(ring.clone_el(&x), i), ring.int_hom().mul_map(ring.pow(ring.clone_el(&x), j), 2));
463 assert_el_eq!(ring, expected, element);
464 let element_vec = ring.wrt_canonical_basis(&expected);
465 for k in 0..ring.rank() {
466 if k == i {
467 assert_el_eq!(ring.base_ring(), ring.base_ring().one(), element_vec.at(k));
468 } else if k == j {
469 assert_el_eq!(ring.base_ring(), ring.base_ring().int_hom().map(2), element_vec.at(k));
470 } else {
471 assert_el_eq!(ring.base_ring(), ring.base_ring().zero(), element_vec.at(k));
472 }
473 }
474 }
475 }
476 }
477}
478
479#[cfg(test)]
480use crate::primitive_int::StaticRing;
481#[cfg(test)]
482use extension_impl::FreeAlgebraImpl;
483#[cfg(test)]
484use crate::rings::rational::RationalField;
485
486#[test]
487fn test_charpoly() {
488 let ring = FreeAlgebraImpl::new(StaticRing::<i64>::RING, 3, [2, 0, 0]);
489 let poly_ring = DensePolyRing::new(StaticRing::<i64>::RING, "X");
490
491 let [expected] = poly_ring.with_wrapped_indeterminate(|X| [X.pow_ref(3) - 2]);
492 assert_el_eq!(&poly_ring, &expected, default_implementations::charpoly(ring.get_ring(), &ring.canonical_gen(), &poly_ring, &ring.base_ring().identity()));
493
494 let [expected] = poly_ring.with_wrapped_indeterminate(|X| [X.pow_ref(3) - 4]);
495 assert_el_eq!(&poly_ring, &expected, default_implementations::charpoly(ring.get_ring(), &ring.pow(ring.canonical_gen(), 2), &poly_ring, &ring.base_ring().identity()));
496
497 let [expected] = poly_ring.with_wrapped_indeterminate(|X| [X.pow_ref(3) - 6 * X - 6]);
498 assert_el_eq!(&poly_ring, &expected, default_implementations::charpoly(ring.get_ring(), &ring.add(ring.canonical_gen(), ring.pow(ring.canonical_gen(), 2)), &poly_ring, &ring.base_ring().identity()));
499}
500
501#[test]
502fn test_trace() {
503 let ring = FreeAlgebraImpl::new(StaticRing::<i64>::RING, 3, [2, 0, 0]);
504
505 assert_eq!(3, ring.trace(ring.from_canonical_basis([1, 0, 0])));
506 assert_eq!(0, ring.trace(ring.from_canonical_basis([0, 1, 0])));
507 assert_eq!(0, ring.trace(ring.from_canonical_basis([0, 0, 1])));
508 assert_eq!(6, ring.trace(ring.from_canonical_basis([2, 0, 0])));
509 assert_eq!(6, ring.trace(ring.from_canonical_basis([2, 1, 0])));
510 assert_eq!(6, ring.trace(ring.from_canonical_basis([2, 0, 1])));
511}
512
513#[test]
514fn test_discriminant() {
515 let ring = FreeAlgebraImpl::new(StaticRing::<i64>::RING, 3, [2, 0, 0]);
516 assert_eq!(-108, default_implementations::discriminant(ring.get_ring()));
517
518 let ring = FreeAlgebraImpl::new(StaticRing::<i64>::RING, 3, [2, 1, 0]);
519 assert_eq!(-104, default_implementations::discriminant(ring.get_ring()));
520
521 let ring = FreeAlgebraImpl::new(StaticRing::<i64>::RING, 3, [3, 0, 0]);
522 assert_eq!(-243, default_implementations::discriminant(ring.get_ring()));
523
524 let base_ring = DensePolyRing::new(RationalField::new(StaticRing::<i64>::RING), "X");
525 let [f] = base_ring.with_wrapped_indeterminate(|X| [X.pow_ref(3) + 1]);
526 let ring = FreeAlgebraImpl::new(&base_ring, 2, [f, base_ring.zero()]);
527 let [expected] = base_ring.with_wrapped_indeterminate(|X| [4 * X.pow_ref(3) + 4]);
528 assert_el_eq!(&base_ring, expected, default_implementations::discriminant(ring.get_ring()));
529}
530
531#[test]
532fn test_from_canonical_basis_extended() {
533 let ring = FreeAlgebraImpl::new(StaticRing::<i64>::RING, 3, [2]);
534 let actual = default_implementations::from_canonical_basis_extended(ring.get_ring(), [1, 2, 3, 4, 5, 6, 7]);
535 let expected = ring.from_canonical_basis([37, 12, 15]);
536 assert_el_eq!(&ring, expected, actual);
537}