proof_engine/number_theory/
gaussian.rs1use glam::{Vec2, Vec3, Vec4};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
7pub struct GaussianInt {
8 pub re: i64,
9 pub im: i64,
10}
11
12impl GaussianInt {
13 pub const ZERO: Self = Self { re: 0, im: 0 };
14 pub const ONE: Self = Self { re: 1, im: 0 };
15 pub const I: Self = Self { re: 0, im: 1 };
16
17 pub fn new(re: i64, im: i64) -> Self {
18 Self { re, im }
19 }
20
21 pub fn norm(self) -> i64 {
23 self.re * self.re + self.im * self.im
24 }
25
26 pub fn conj(self) -> Self {
28 Self { re: self.re, im: -self.im }
29 }
30
31 pub fn is_zero(self) -> bool {
33 self.re == 0 && self.im == 0
34 }
35
36 pub fn is_unit(self) -> bool {
38 self.norm() == 1
39 }
40
41 pub fn div_rem(self, other: Self) -> (Self, Self) {
43 if other.is_zero() {
44 panic!("division by zero in Gaussian integers");
45 }
46 let n = other.norm();
48 let num = self * other.conj();
49 let qr = div_round(num.re, n);
51 let qi = div_round(num.im, n);
52 let q = GaussianInt::new(qr, qi);
53 let r = self - q * other;
54 (q, r)
55 }
56}
57
58fn div_round(a: i64, b: i64) -> i64 {
59 let (d, r) = (a / b, a % b);
61 if 2 * r.abs() > b.abs() {
62 if (a > 0) == (b > 0) { d + 1 } else { d - 1 }
63 } else {
64 d
65 }
66}
67
68impl std::ops::Add for GaussianInt {
69 type Output = Self;
70 fn add(self, rhs: Self) -> Self {
71 Self { re: self.re + rhs.re, im: self.im + rhs.im }
72 }
73}
74
75impl std::ops::Sub for GaussianInt {
76 type Output = Self;
77 fn sub(self, rhs: Self) -> Self {
78 Self { re: self.re - rhs.re, im: self.im - rhs.im }
79 }
80}
81
82impl std::ops::Mul for GaussianInt {
83 type Output = Self;
84 fn mul(self, rhs: Self) -> Self {
85 Self {
86 re: self.re * rhs.re - self.im * rhs.im,
87 im: self.re * rhs.im + self.im * rhs.re,
88 }
89 }
90}
91
92impl std::ops::Neg for GaussianInt {
93 type Output = Self;
94 fn neg(self) -> Self {
95 Self { re: -self.re, im: -self.im }
96 }
97}
98
99impl std::fmt::Display for GaussianInt {
100 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
101 if self.im == 0 {
102 write!(f, "{}", self.re)
103 } else if self.re == 0 {
104 if self.im == 1 {
105 write!(f, "i")
106 } else if self.im == -1 {
107 write!(f, "-i")
108 } else {
109 write!(f, "{}i", self.im)
110 }
111 } else {
112 let sign = if self.im > 0 { "+" } else { "-" };
113 let abs_im = self.im.abs();
114 if abs_im == 1 {
115 write!(f, "{}{}i", self.re, sign)
116 } else {
117 write!(f, "{}{}{}i", self.re, sign, abs_im)
118 }
119 }
120 }
121}
122
123pub fn gcd(mut a: GaussianInt, mut b: GaussianInt) -> GaussianInt {
125 while !b.is_zero() {
126 let (_, r) = a.div_rem(b);
127 a = b;
128 b = r;
129 }
130 normalize(a)
132}
133
134fn normalize(z: GaussianInt) -> GaussianInt {
137 if z.is_zero() {
138 return z;
139 }
140 let units = [
143 GaussianInt::new(1, 0),
144 GaussianInt::new(0, 1),
145 GaussianInt::new(-1, 0),
146 GaussianInt::new(0, -1),
147 ];
148 for &u in &units {
149 let w = z * u;
150 if w.re > 0 && w.im >= 0 {
151 return w;
152 }
153 }
154 for &u in &units {
156 let w = z * u;
157 if w.re >= 0 && w.im >= 0 {
158 return w;
159 }
160 }
161 z
162}
163
164pub fn is_gaussian_prime(z: GaussianInt) -> bool {
166 if z.is_zero() {
167 return false;
168 }
169 let n = z.norm();
170 if n <= 1 {
171 return false; }
173 super::primes::is_prime(n as u64)
177 || (z.im == 0 && {
178 let p = z.re.unsigned_abs();
179 super::primes::is_prime(p) && p % 4 == 3
180 })
181 || (z.re == 0 && {
182 let p = z.im.unsigned_abs();
183 super::primes::is_prime(p) && p % 4 == 3
184 })
185}
186
187pub fn factorize(z: GaussianInt) -> Vec<GaussianInt> {
190 if z.is_zero() || z.is_unit() {
191 return vec![];
192 }
193
194 let mut factors = Vec::new();
195 let mut remaining = z;
196
197 let n = remaining.norm();
199
200 let norm_factors = super::primes::prime_factorization(n as u64);
202
203 for (p, exp) in norm_factors {
204 let p_val = p as i64;
205 if p == 2 {
206 let pi = GaussianInt::new(1, 1);
208 for _ in 0..exp {
209 let (q, r) = remaining.div_rem(pi);
210 if r.is_zero() {
211 factors.push(pi);
212 remaining = q;
213 } else {
214 break;
215 }
216 }
217 } else if p % 4 == 3 {
218 let gp = GaussianInt::new(p_val, 0);
220 let pairs = exp / 2; for _ in 0..pairs {
222 let (q, r) = remaining.div_rem(gp);
223 if r.is_zero() {
224 factors.push(gp);
225 remaining = q;
226 } else {
227 break;
228 }
229 }
230 } else {
231 if let Some((a, b)) = sum_of_two_squares(p) {
234 let pi1 = GaussianInt::new(a as i64, b as i64);
235 let pi2 = pi1.conj();
236 for pi in &[pi1, pi2] {
237 loop {
238 let (q, r) = remaining.div_rem(*pi);
239 if r.is_zero() {
240 factors.push(*pi);
241 remaining = q;
242 } else {
243 break;
244 }
245 }
246 }
247 }
248 }
249 }
250
251 if !remaining.is_unit() && !remaining.is_zero() {
253 factors.push(remaining);
254 }
255
256 factors
257}
258
259fn sum_of_two_squares(p: u64) -> Option<(u64, u64)> {
261 if p == 2 {
262 return Some((1, 1));
263 }
264 if p % 4 != 1 {
265 return None;
266 }
267 let mut r = 2u64;
269 loop {
270 let candidate = super::modular::mod_pow(r, (p - 1) / 4, p);
271 if (candidate as u128 * candidate as u128) % p as u128 == p as u128 - 1 {
272 return fermat_descent(p, candidate);
274 }
275 r += 1;
276 if r >= p {
277 return None;
278 }
279 }
280}
281
282fn fermat_descent(p: u64, mut a: u64) -> Option<(u64, u64)> {
283 let mut b = p;
284 let limit = (p as f64).sqrt() as u64;
285 while a > limit {
286 let t = b % a;
287 b = a;
288 a = t;
289 }
290 let remainder = p - a * a;
291 let b_sq = (remainder as f64).sqrt().round() as u64;
292 if b_sq * b_sq == remainder {
293 Some((a.max(b_sq), a.min(b_sq)))
294 } else {
295 None
296 }
297}
298
299pub struct GaussianLatticeRenderer {
303 pub range: i64,
304 pub origin: Vec3,
305 pub scale: f32,
306}
307
308pub struct LatticeGlyph {
309 pub z: GaussianInt,
310 pub position: Vec3,
311 pub color: Vec4,
312 pub character: char,
313 pub is_prime: bool,
314}
315
316impl GaussianLatticeRenderer {
317 pub fn new(range: i64, origin: Vec3, scale: f32) -> Self {
318 Self { range, origin, scale }
319 }
320
321 pub fn render(&self) -> Vec<LatticeGlyph> {
323 let mut glyphs = Vec::new();
324 for a in -self.range..=self.range {
325 for b in -self.range..=self.range {
326 let z = GaussianInt::new(a, b);
327 let prime = is_gaussian_prime(z);
328 let norm = z.norm() as f32;
329 let max_norm = (2 * self.range * self.range) as f32;
330 let t = (norm / max_norm).min(1.0);
331 let color = if prime {
332 Vec4::new(1.0, 0.2, 0.2, 1.0)
333 } else if z.is_unit() {
334 Vec4::new(1.0, 1.0, 0.0, 1.0)
335 } else {
336 Vec4::new(0.3, 0.3 + t * 0.5, 0.8, 0.6)
337 };
338 let ch = if prime {
339 '*'
340 } else if z.is_zero() {
341 'O'
342 } else if z.is_unit() {
343 'U'
344 } else {
345 '.'
346 };
347 glyphs.push(LatticeGlyph {
348 z,
349 position: self.origin
350 + Vec3::new(a as f32 * self.scale, b as f32 * self.scale, 0.0),
351 color,
352 character: ch,
353 is_prime: prime,
354 });
355 }
356 }
357 glyphs
358 }
359}
360
361#[cfg(test)]
364mod tests {
365 use super::*;
366
367 #[test]
368 fn basic_arithmetic() {
369 let a = GaussianInt::new(3, 4);
370 let b = GaussianInt::new(1, -2);
371 assert_eq!(a + b, GaussianInt::new(4, 2));
372 assert_eq!(a - b, GaussianInt::new(2, 6));
373 assert_eq!(a * b, GaussianInt::new(11, -2));
375 }
376
377 #[test]
378 fn norm_and_conj() {
379 let z = GaussianInt::new(3, 4);
380 assert_eq!(z.norm(), 25);
381 assert_eq!(z.conj(), GaussianInt::new(3, -4));
382 assert_eq!(z * z.conj(), GaussianInt::new(25, 0));
383 }
384
385 #[test]
386 fn units() {
387 assert!(GaussianInt::ONE.is_unit());
388 assert!(GaussianInt::I.is_unit());
389 assert!((-GaussianInt::ONE).is_unit());
390 assert!((-GaussianInt::I).is_unit());
391 assert!(!GaussianInt::new(1, 1).is_unit());
392 }
393
394 #[test]
395 fn div_rem_exact() {
396 let a = GaussianInt::new(11, -2);
397 let b = GaussianInt::new(1, -2);
398 let (q, r) = a.div_rem(b);
399 assert_eq!(a, q * b + r);
400 assert!(r.norm() <= b.norm()); }
402
403 #[test]
404 fn gcd_test() {
405 let a = GaussianInt::new(3, 4);
406 let b = GaussianInt::new(1, 2);
407 let g = gcd(a, b);
408 let (_, r1) = a.div_rem(g);
410 let (_, r2) = b.div_rem(g);
411 assert!(r1.is_zero(), "gcd should divide a, remainder = {:?}", r1);
412 assert!(r2.is_zero(), "gcd should divide b, remainder = {:?}", r2);
413 }
414
415 #[test]
416 fn gaussian_primes() {
417 assert!(is_gaussian_prime(GaussianInt::new(1, 1)));
419 assert!(is_gaussian_prime(GaussianInt::new(3, 0)));
421 assert!(is_gaussian_prime(GaussianInt::new(2, 1)));
423 assert!(!is_gaussian_prime(GaussianInt::new(5, 0)));
425 assert!(!is_gaussian_prime(GaussianInt::ONE));
427 assert!(!is_gaussian_prime(GaussianInt::I));
428 }
429
430 #[test]
431 fn factorize_5() {
432 let z = GaussianInt::new(5, 0);
434 let factors = factorize(z);
435 let product = factors.iter().fold(GaussianInt::ONE, |acc, &f| acc * f);
437 assert_eq!(product.norm(), z.norm());
438 assert!(factors.len() >= 2);
439 }
440
441 #[test]
442 fn factorize_prime_3() {
443 let z = GaussianInt::new(3, 0);
445 let factors = factorize(z);
446 assert_eq!(factors.len(), 1);
447 assert_eq!(factors[0].norm(), 9); }
449
450 #[test]
451 fn display() {
452 assert_eq!(format!("{}", GaussianInt::new(3, 4)), "3+4i");
453 assert_eq!(format!("{}", GaussianInt::new(3, -4)), "3-4i");
454 assert_eq!(format!("{}", GaussianInt::new(0, 1)), "i");
455 assert_eq!(format!("{}", GaussianInt::new(5, 0)), "5");
456 }
457
458 #[test]
459 fn lattice_renderer() {
460 let r = GaussianLatticeRenderer::new(3, Vec3::ZERO, 1.0);
461 let glyphs = r.render();
462 assert_eq!(glyphs.len(), 7 * 7); let prime_count = glyphs.iter().filter(|g| g.is_prime).count();
464 assert!(prime_count > 0);
465 }
466}