logicaffeine_proof/
period.rs1use crate::factor::{gcd, modpow};
17use logicaffeine_base::BigInt;
18use std::collections::HashMap;
19
20#[inline]
21fn i(x: i64) -> BigInt {
22 BigInt::from_i64(x)
23}
24
25#[inline]
26fn rem_pos(a: &BigInt, n: &BigInt) -> BigInt {
27 let r = a.div_rem(n).map(|(_, r)| r).unwrap_or_else(|| a.clone());
28 if r.is_negative() {
29 r.add(n)
30 } else {
31 r
32 }
33}
34
35#[inline]
36fn mulmod(a: &BigInt, b: &BigInt, n: &BigInt) -> BigInt {
37 rem_pos(&a.mul(b), n)
38}
39
40#[inline]
42fn key(x: &BigInt) -> Vec<u8> {
43 x.to_le_bytes().1
44}
45
46fn distinct_primes(mut r: u64) -> Vec<u64> {
48 let mut ps = Vec::new();
49 let mut d = 2u64;
50 while d * d <= r {
51 if r % d == 0 {
52 ps.push(d);
53 while r % d == 0 {
54 r /= d;
55 }
56 }
57 d += 1;
58 }
59 if r > 1 {
60 ps.push(r);
61 }
62 ps
63}
64
65fn reduce_to_order(a: &BigInt, mut r: u64, n: &BigInt) -> u64 {
68 let one = i(1);
69 for p in distinct_primes(r) {
70 while r % p == 0 && modpow(a, &i((r / p) as i64), n) == one {
71 r /= p;
72 }
73 }
74 r
75}
76
77pub fn multiplicative_order(a: &BigInt, n: &BigInt, bound: u64) -> Option<u64> {
81 let one = i(1);
82 if gcd(a, n) != one {
83 return None;
84 }
85 let a = rem_pos(a, n);
86 if a == one {
87 return Some(1);
88 }
89 let m = (bound as f64).sqrt() as u64 + 1;
91
92 let mut baby: HashMap<Vec<u8>, u64> = HashMap::new();
94 let mut cur = one.clone();
95 for j in 0..m {
96 if j > 0 && cur == one {
97 return Some(j);
98 }
99 baby.entry(key(&cur)).or_insert(j);
100 cur = mulmod(&cur, &a, n);
101 }
102 let giant = cur; let mut gk = giant.clone();
105 for k in 1..=m {
106 if let Some(&idx) = baby.get(&key(&gk)) {
107 let cand = k * m - idx;
108 if cand > 0 {
109 return Some(reduce_to_order(&a, cand, n));
110 }
111 }
112 gk = mulmod(&gk, &giant, n);
113 }
114 None
115}
116
117pub fn factor_from_order(a: &BigInt, r: u64, n: &BigInt) -> Option<BigInt> {
121 if r % 2 != 0 {
122 return None;
123 }
124 let one = i(1);
125 let root = modpow(a, &i((r / 2) as i64), n); if root == one || root == n.sub(&one) {
127 return None; }
129 for cand in [root.sub(&one), root.add(&one)] {
130 let g = gcd(&cand, n);
131 if g != one && g != *n {
132 return Some(g);
133 }
134 }
135 None
136}
137
138pub fn factor_via_order(n: &BigInt, tries: usize, bound: u64, seed: u64) -> Option<BigInt> {
143 let one = i(1);
144 if !n.is_odd() {
145 return Some(i(2));
146 }
147 for t in 0..tries {
148 let a_u = 2 + seed.wrapping_add(t as u64).wrapping_mul(2_654_435_761) % 1_000_000;
149 let a = rem_pos(&i(a_u as i64), n);
150 if a == one || a.is_zero() {
151 continue;
152 }
153 let g = gcd(&a, n);
155 if g != one && g != *n {
156 return Some(g);
157 }
158 if let Some(r) = multiplicative_order(&a, n, bound) {
159 if let Some(f) = factor_from_order(&a, r, n) {
160 return Some(f);
161 }
162 }
163 }
164 None
165}
166
167#[cfg(test)]
168mod tests {
169 use super::*;
170
171 fn big(s: &str) -> BigInt {
172 BigInt::parse_decimal(s).unwrap()
173 }
174
175 fn brute_order(a: u64, n: u64) -> Option<u64> {
177 if gcd(&i(a as i64), &i(n as i64)) != i(1) {
178 return None;
179 }
180 let mut cur = 1u64 % n;
181 for r in 1..=n {
182 cur = (cur * a) % n;
183 if cur == 1 {
184 return Some(r);
185 }
186 }
187 None
188 }
189
190 #[test]
191 fn order_matches_brute_force_exhaustively() {
192 for n in 2..=200u64 {
194 for a in 2..n {
195 let bsgs = multiplicative_order(&i(a as i64), &i(n as i64), 400);
196 assert_eq!(bsgs, brute_order(a, n), "ord_{n}({a}) mismatch");
197 }
198 }
199 }
200
201 #[test]
202 fn factor_from_order_splits_a_semiprime() {
203 let n = i(15);
205 let r = multiplicative_order(&i(2), &n, 100).unwrap();
206 assert_eq!(r, 4, "ord_15(2) = 4");
207 let f = factor_from_order(&i(2), r, &n).expect("even order with a nontrivial root splits N");
208 assert!(f == i(3) || f == i(5), "recovers a real factor, got {f:?}");
209 }
210
211 #[test]
212 fn factor_via_order_is_the_classical_shor_shell() {
213 for (p, q) in [(11u64, 13u64), (17, 19), (101, 103), (211, 223)] {
215 let n = i((p * q) as i64);
216 let f = factor_via_order(&n, 50, (p * q) as u64, 42).expect("order route finds a factor");
217 assert!(f != i(1) && f != n, "nontrivial");
218 assert!(n.div_rem(&f).unwrap().1.is_zero(), "and it divides N = {}·{}", p, q);
219 }
220 }
221
222 #[test]
223 fn order_declines_when_base_shares_a_factor() {
224 assert_eq!(multiplicative_order(&i(6), &i(15), 100), None, "6 shares 3 with 15");
226 assert_eq!(multiplicative_order(&i(10), &i(15), 100), None, "10 shares 5 with 15");
227 }
228}