algebraeon_rings/natural/factorization/
structure.rs1use crate::natural::factorization::factor_nat;
2use crate::natural::factorization::primes::is_prime_nat;
3use crate::structure::{
4 Factored, FactoringMonoidSignature, FactoringStructure, MetaFactoringMonoid,
5 MultiplicationSignature, MultiplicativeMonoidSignature, SemiRingSignature,
6 UniqueFactorizationMonoidSignature,
7};
8use algebraeon_nzq::traits::ModPow;
9use algebraeon_nzq::{Natural, NaturalCanonicalStructure, gcd};
10use algebraeon_sets::structure::{BorrowedStructure, MetaType, SetSignature, ToStringSignature};
11use itertools::Itertools;
12
13impl UniqueFactorizationMonoidSignature for NaturalCanonicalStructure {
14 type FactoredExponent = NaturalCanonicalStructure;
15
16 fn factorization_exponents(&self) -> &Self::FactoredExponent {
17 Natural::structure_ref()
18 }
19
20 fn into_factorization_exponents(self) -> Self::FactoredExponent {
21 Natural::structure()
22 }
23
24 fn try_is_irreducible(&self, a: &Self::Set) -> Option<bool> {
25 Some(is_prime_nat(a))
26 }
27
28 fn factorization_pow(&self, a: &Self::Set, k: &Natural) -> Self::Set {
29 self.nat_pow(a, k)
30 }
31}
32
33impl FactoringMonoidSignature for NaturalCanonicalStructure {
34 fn is_irreducible(&self, a: &Self::Set) -> bool {
35 is_prime_nat(a)
36 }
37
38 fn factor_unchecked(
39 &self,
40 a: &Self::Set,
41 ) -> Factored<Self::Set, <Self::FactoredExponent as SetSignature>::Set> {
42 factor_nat(a.clone())
43 }
44}
45
46impl<
47 ObjectB: BorrowedStructure<NaturalCanonicalStructure>,
48 ExponentB: BorrowedStructure<NaturalCanonicalStructure>,
49> FactoringStructure<NaturalCanonicalStructure, ObjectB, NaturalCanonicalStructure, ExponentB>
50{
51 pub fn euler_totient(&self, a: &Factored<Natural, Natural>) -> Natural {
52 #[cfg(debug_assertions)]
53 self.is_element(a).unwrap();
54 match a {
55 Factored::Zero => {
56 self.objects().from_nat(&Natural::TWO)
58 }
59 Factored::NonZero(a) => {
60 let mut prod = a.unit().clone();
61 for (p, k) in a.powers() {
62 debug_assert!(p != &Natural::ZERO);
63 debug_assert!(k != &Natural::ZERO);
64 self.objects().mul_mut(
65 &mut prod,
66 &((p - &Natural::ONE) * p.pow(&(k - &Natural::ONE))),
67 );
68 }
69 prod
70 }
71 }
72 }
73}
74
75#[derive(Debug, Clone, Copy, PartialEq, Eq)]
76pub enum IsPrimitiveRootResult {
77 NonUnit,
78 No,
79 Yes,
80}
81
82impl<
83 PowersB: BorrowedStructure<NaturalCanonicalStructure>,
84 ExponentB: BorrowedStructure<NaturalCanonicalStructure>,
85> FactoringStructure<NaturalCanonicalStructure, PowersB, NaturalCanonicalStructure, ExponentB>
86{
87 pub fn is_primitive_root(
89 &self,
90 x: &Natural,
91 n_factored: &Factored<Natural, Natural>,
92 ) -> IsPrimitiveRootResult {
93 #[cfg(debug_assertions)]
94 self.is_element(n_factored).unwrap();
95
96 let factorizations = Natural::structure_ref().factorizations();
97 let n = factorizations.expand(n_factored);
98 if gcd(x.clone(), n.clone()) != Natural::ONE {
99 IsPrimitiveRootResult::NonUnit
100 } else {
101 let phi_n = factorizations.euler_totient(n_factored);
102 let x_mod_n = x % &n;
103 for p in phi_n.clone().factor().distinct_irreducibles().unwrap() {
104 if (&x_mod_n).mod_pow(&phi_n / p, &n) == Natural::ONE {
105 return IsPrimitiveRootResult::No;
106 }
107 }
108 IsPrimitiveRootResult::Yes
109 }
110 }
111}
112
113impl<
114 PowersB: BorrowedStructure<NaturalCanonicalStructure>,
115 ExponentB: BorrowedStructure<NaturalCanonicalStructure>,
116> ToStringSignature
117 for FactoringStructure<NaturalCanonicalStructure, PowersB, NaturalCanonicalStructure, ExponentB>
118{
119 fn to_string(&self, elem: &Self::Set) -> String {
120 use std::fmt::Write;
121 let mut f = String::new();
122 if let Some(powers) = elem.powers() {
123 if powers.is_empty() {
124 write!(f, "1").unwrap();
125 } else {
126 for (i, (p, k)) in powers
127 .iter()
128 .sorted_by_cached_key(|(p, _k)| (*p).clone())
129 .enumerate()
130 {
131 if i != 0 {
132 write!(f, " × ").unwrap();
133 }
134 write!(f, "{}", p).unwrap();
135 if k != &Natural::ONE {
136 write!(f, "^").unwrap();
137 write!(f, "{}", k).unwrap();
138 }
139 }
140 }
141 } else {
142 write!(f, "0").unwrap();
143 }
144 f
145 }
146}