1use std::alloc::Allocator;
2use std::alloc::Global;
3use std::cmp::min;
4
5use crate::algorithms::int_bisect::bisect_floor;
6use crate::algorithms::linsolve::smith::determinant_using_pre_smith;
7use crate::algorithms::linsolve::*;
8use crate::rings::poly::*;
9use crate::algorithms::matmul::ComputeInnerProduct;
10use crate::divisibility::*;
11use crate::homomorphism::Homomorphism;
12use crate::matrix::OwnedMatrix;
13use crate::pid::PrincipalIdealRing;
14use crate::primitive_int::StaticRing;
15use crate::ring::*;
16use crate::rings::extension::*;
17use crate::seq::*;
18use crate::rings::poly::PolyRing;
19
20#[stability::unstable(feature = "enable")]
24pub fn from_canonical_basis_extended<R, V>(ring: &R, vec: V) -> R::Element
25 where R: ?Sized + FreeAlgebra,
26 V: IntoIterator<Item = El<<R as RingExtension>::BaseRing>>
27{
28 let mut data = vec.into_iter().collect::<Vec<_>>();
29 let power_of_canonical_gen = ring.mul(
30 ring.from_canonical_basis((1..ring.rank()).map(|_| ring.base_ring().zero()).chain([ring.base_ring().one()].into_iter())),
31 ring.canonical_gen()
32 );
33 let mut current_power = ring.one();
34 return <_ as ComputeInnerProduct>::inner_product(ring, (0..).map_while(|_| {
35 if data.len() == 0 {
36 return None;
37 }
38 let taken_elements = min(data.len(), ring.rank());
39 let chunk = data.drain(..taken_elements).chain((taken_elements..ring.rank()).map(|_| ring.base_ring().zero()));
40 let current = ring.from_canonical_basis(chunk);
41 let result = (current, ring.clone_el(¤t_power));
42 ring.mul_assign_ref(&mut current_power, &power_of_canonical_gen);
43 return Some(result);
44 }));
45}
46
47#[stability::unstable(feature = "enable")]
48pub fn charpoly<R, P, H>(ring: &R, el: &R::Element, poly_ring: P, hom: H) -> El<P>
49 where R: ?Sized + FreeAlgebra,
50 P: RingStore,
51 P::Type: PolyRing,
52 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: LinSolveRing,
53 H: Homomorphism<<R::BaseRing as RingStore>::Type, <<P::Type as RingExtension>::BaseRing as RingStore>::Type>
54{
55 let minpoly = minpoly(ring, el, &poly_ring, hom);
56 let power = StaticRing::<i64>::RING.checked_div(&(ring.rank() as i64), &(poly_ring.degree(&minpoly).unwrap() as i64)).unwrap() as usize;
57 return poly_ring.pow(minpoly, power);
58}
59
60#[stability::unstable(feature = "enable")]
61pub fn minpoly<R, P, H>(ring: &R, el: &R::Element, poly_ring: P, hom: H) -> El<P>
62 where R: ?Sized + FreeAlgebra,
63 P: RingStore,
64 P::Type: PolyRing,
65 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: LinSolveRing,
66 H: Homomorphism<<R::BaseRing as RingStore>::Type, <<P::Type as RingExtension>::BaseRing as RingStore>::Type>
67{
68 assert!(!ring.is_zero(el));
69 let base_ring = hom.codomain();
70
71 let mut result = None;
72 _ = bisect_floor(StaticRing::<i64>::RING, 1, ring.rank() as i64, |d| {
73 let d = *d as usize;
74 let mut lhs = OwnedMatrix::zero(ring.rank(), d, &base_ring);
75 let mut current = ring.one();
76 for j in 0..d {
77 let wrt_basis = ring.wrt_canonical_basis(¤t);
78 for i in 0..ring.rank() {
79 *lhs.at_mut(i, j) = hom.map(wrt_basis.at(i));
80 }
81 drop(wrt_basis);
82 ring.mul_assign_ref(&mut current, el);
83 }
84 let mut rhs = OwnedMatrix::zero(ring.rank(), 1, &base_ring);
85 let wrt_basis = ring.wrt_canonical_basis(¤t);
86 for i in 0..ring.rank() {
87 *rhs.at_mut(i, 0) = base_ring.negate(hom.map(wrt_basis.at(i)));
88 }
89 let mut sol = OwnedMatrix::zero(d, 1, &base_ring);
90 let sol_poly = |sol: &OwnedMatrix<El<<P::Type as RingExtension>::BaseRing>>| poly_ring.from_terms((0..d).map(|i| (base_ring.clone_el(sol.at(i, 0)), i)).chain([(base_ring.one(), d)].into_iter()));
91 match <_ as LinSolveRingStore>::solve_right(base_ring, lhs.data_mut(), rhs.data_mut(), sol.data_mut()) {
92 SolveResult::NoSolution => {
93 -1
94 },
95 SolveResult::FoundUniqueSolution => {
96 result = Some(sol_poly(&sol));
97 0
98 },
99 SolveResult::FoundSomeSolution => {
100 result = Some(sol_poly(&sol));
101 1
102 }
103 }
104 });
105
106 return result.unwrap();
107}
108
109#[stability::unstable(feature = "enable")]
113pub fn discriminant<R>(ring: &R) -> El<R::BaseRing>
114 where R: ?Sized + FreeAlgebra,
115 <R::BaseRing as RingStore>::Type: PrincipalIdealRing
116{
117 let mut current = ring.one();
118 let generator = ring.canonical_gen();
119 let traces = (0..(2 * ring.rank())).map(|_| {
120 let result = ring.trace(ring.clone_el(¤t));
121 ring.mul_assign_ref(&mut current, &generator);
122 return result;
123 }).collect::<Vec<_>>();
124 let mut matrix = OwnedMatrix::from_fn(ring.rank(), ring.rank(), |i, j| ring.base_ring().clone_el(&traces[i + j]));
125 let result = determinant_using_pre_smith(ring.base_ring(), matrix.data_mut(), Global);
126 return result;
127}
128
129#[stability::unstable(feature = "enable")]
130pub fn create_multiplication_matrix<R: RingStore, A: Allocator>(ring: R, el: &El<R>, allocator: A) -> OwnedMatrix<El<<R::Type as RingExtension>::BaseRing>, A>
131 where R::Type: FreeAlgebra
132{
133 let mut result = OwnedMatrix::zero_in(ring.rank(), ring.rank(), ring.base_ring(), allocator);
134 let mut current = ring.clone_el(el);
135 let g = ring.canonical_gen();
136 for i in 0..ring.rank() {
137 {
138 let current_basis_repr = ring.wrt_canonical_basis(¤t);
139 for j in 0..ring.rank() {
140 *result.at_mut(j, i) = current_basis_repr.at(j);
141 }
142 }
143 ring.mul_assign_ref(&mut current, &g);
144 }
145 return result;
146}
147
148#[cfg(test)]
149use crate::rings::extension::extension_impl::FreeAlgebraImpl;
150#[cfg(test)]
151use crate::rings::rational::RationalField;
152#[cfg(test)]
153use crate::rings::poly::dense_poly::DensePolyRing;
154
155#[test]
156fn test_charpoly() {
157 let ring = FreeAlgebraImpl::new(StaticRing::<i64>::RING, 3, [2]);
158 let poly_ring = DensePolyRing::new(StaticRing::<i64>::RING, "X");
159
160 let [expected] = poly_ring.with_wrapped_indeterminate(|X| [X.pow_ref(3) - 2]);
161 assert_el_eq!(&poly_ring, &expected, charpoly(ring.get_ring(), &ring.canonical_gen(), &poly_ring, &ring.base_ring().identity()));
162
163 let [expected] = poly_ring.with_wrapped_indeterminate(|X| [X.pow_ref(3) - 4]);
164 assert_el_eq!(&poly_ring, &expected, charpoly(ring.get_ring(), &ring.pow(ring.canonical_gen(), 2), &poly_ring, &ring.base_ring().identity()));
165
166 let [expected] = poly_ring.with_wrapped_indeterminate(|X| [X.pow_ref(3) - 6 * X - 6]);
167 assert_el_eq!(&poly_ring, &expected, charpoly(ring.get_ring(), &ring.add(ring.canonical_gen(), ring.pow(ring.canonical_gen(), 2)), &poly_ring, &ring.base_ring().identity()));
168
169 let ring = FreeAlgebraImpl::new(StaticRing::<i64>::RING, 4, [2]);
170 let poly_ring = DensePolyRing::new(StaticRing::<i64>::RING, "X");
171
172 let [expected] = poly_ring.with_wrapped_indeterminate(|X| [X.pow_ref(4) - 2]);
173 assert_el_eq!(&poly_ring, &expected, charpoly(ring.get_ring(), &ring.canonical_gen(), &poly_ring, &ring.base_ring().identity()));
174
175 let [expected] = poly_ring.with_wrapped_indeterminate(|X| [X.pow_ref(4) - 4 * X.pow_ref(2) + 4]);
176 assert_el_eq!(&poly_ring, &expected, charpoly(ring.get_ring(), &ring.pow(ring.canonical_gen(), 2), &poly_ring, &ring.base_ring().identity()));
177}
178
179#[test]
180fn test_minpoly() {
181 let ring = FreeAlgebraImpl::new(StaticRing::<i64>::RING, 6, [2]);
182 let poly_ring = DensePolyRing::new(StaticRing::<i64>::RING, "X");
183
184 let [expected] = poly_ring.with_wrapped_indeterminate(|X| [X.pow_ref(6) - 2]);
185 assert_el_eq!(&poly_ring, &expected, minpoly(ring.get_ring(), &ring.canonical_gen(), &poly_ring, &ring.base_ring().identity()));
186
187 let [expected] = poly_ring.with_wrapped_indeterminate(|X| [X.pow_ref(3) - 2]);
188 assert_el_eq!(&poly_ring, &expected, minpoly(ring.get_ring(), &ring.pow(ring.canonical_gen(), 2), &poly_ring, &ring.base_ring().identity()));
189
190 let [expected] = poly_ring.with_wrapped_indeterminate(|X| [X.pow_ref(2) - 2 * X - 1]);
191 assert_el_eq!(&poly_ring, &expected, minpoly(ring.get_ring(), &ring.add(ring.one(), ring.pow(ring.canonical_gen(), 3)), &poly_ring, &ring.base_ring().identity()));
192}
193
194#[test]
195fn test_trace() {
196 let ring = FreeAlgebraImpl::new(StaticRing::<i64>::RING, 3, [2, 0, 0]);
197
198 assert_eq!(3, ring.trace(ring.from_canonical_basis([1, 0, 0])));
199 assert_eq!(0, ring.trace(ring.from_canonical_basis([0, 1, 0])));
200 assert_eq!(0, ring.trace(ring.from_canonical_basis([0, 0, 1])));
201 assert_eq!(6, ring.trace(ring.from_canonical_basis([2, 0, 0])));
202 assert_eq!(6, ring.trace(ring.from_canonical_basis([2, 1, 0])));
203 assert_eq!(6, ring.trace(ring.from_canonical_basis([2, 0, 1])));
204}
205
206#[test]
207fn test_discriminant() {
208 let ring = FreeAlgebraImpl::new(StaticRing::<i64>::RING, 3, [2, 0, 0]);
209 assert_eq!(-108, discriminant(ring.get_ring()));
210
211 let ring = FreeAlgebraImpl::new(StaticRing::<i64>::RING, 3, [2, 1, 0]);
212 assert_eq!(-104, discriminant(ring.get_ring()));
213
214 let ring = FreeAlgebraImpl::new(StaticRing::<i64>::RING, 3, [3, 0, 0]);
215 assert_eq!(-243, discriminant(ring.get_ring()));
216
217 let base_ring = DensePolyRing::new(RationalField::new(StaticRing::<i64>::RING), "X");
218 let [f] = base_ring.with_wrapped_indeterminate(|X| [X.pow_ref(3) + 1]);
219 let ring = FreeAlgebraImpl::new(&base_ring, 2, [f, base_ring.zero()]);
220 let [expected] = base_ring.with_wrapped_indeterminate(|X| [4 * X.pow_ref(3) + 4]);
221 assert_el_eq!(&base_ring, expected, discriminant(ring.get_ring()));
222}
223
224#[test]
225fn test_from_canonical_basis_extended() {
226 let ring = FreeAlgebraImpl::new(StaticRing::<i64>::RING, 3, [2]);
227 let actual = from_canonical_basis_extended(ring.get_ring(), [1, 2, 3, 4, 5, 6, 7]);
228 let expected = ring.from_canonical_basis([37, 12, 15]);
229 assert_el_eq!(&ring, expected, actual);
230}