proof_engine/number_theory/
padic.rs1use glam::{Vec2, Vec3, Vec4};
4
5#[derive(Debug, Clone, PartialEq)]
8pub struct PAdic {
9 pub prime: u64,
10 pub digits: Vec<u64>,
11 pub valuation: i64,
12}
13
14impl PAdic {
15 pub fn from_integer(n: i64, prime: u64, precision: usize) -> Self {
17 assert!(prime >= 2, "prime must be >= 2");
18 if n == 0 {
19 return PAdic {
20 prime,
21 digits: vec![0; precision],
22 valuation: 0,
23 };
24 }
25
26 let p = prime as i64;
27 let mut digits = Vec::with_capacity(precision);
30 let mut val = n;
31
32 if val > 0 {
33 let mut v = 0i64;
35 while val % p == 0 && val != 0 {
36 val /= p;
37 v += 1;
38 }
39 for _ in 0..precision {
40 digits.push((val.rem_euclid(p)) as u64);
41 val = val.div_euclid(p);
42 }
43 PAdic { prime, digits, valuation: v }
44 } else {
45 let mut v = 0i64;
48 let mut abs_val = -val;
49 while abs_val % p == 0 && abs_val != 0 {
50 abs_val /= p;
51 v += 1;
52 }
53 let mut complement = Vec::with_capacity(precision);
56 let mut carry = 0i64;
57 let mut first = true;
58 let mut remaining = abs_val;
59 for _ in 0..precision {
60 let d = remaining.rem_euclid(p);
61 remaining = remaining.div_euclid(p);
62 let comp_digit = if first && d == 0 {
63 first = false;
64 0
65 } else if first {
66 first = false;
67 (p - d) as u64
68 } else {
69 (p - 1 - d - carry) as u64
70 };
71 complement.push(0);
73 }
74 digits.clear();
77 let mut rem = n;
78 for _ in 0..precision {
79 let d = rem.rem_euclid(p) as u64;
80 digits.push(d);
81 rem = rem.div_euclid(p);
82 }
83 PAdic { prime, digits, valuation: v }
84 }
85 }
86
87 pub fn to_string_expansion(&self) -> String {
89 let mut s = String::from("...");
90 for d in self.digits.iter().rev() {
91 s.push_str(&d.to_string());
92 }
93 s
94 }
95
96 pub fn to_f64_approx(&self) -> f64 {
98 let p = self.prime as f64;
99 let mut result = 0.0f64;
100 for (i, &d) in self.digits.iter().enumerate() {
101 result += d as f64 * p.powi((self.valuation + i as i64) as i32);
102 }
103 result
104 }
105
106 pub fn precision(&self) -> usize {
108 self.digits.len()
109 }
110}
111
112pub fn padic_valuation(mut n: i64, p: u64) -> i64 {
114 if n == 0 {
115 return i64::MAX; }
117 let p = p as i64;
118 n = n.abs();
119 let mut v = 0i64;
120 while n % p == 0 {
121 n /= p;
122 v += 1;
123 }
124 v
125}
126
127pub fn padic_norm(n: i64, p: u64) -> f64 {
129 if n == 0 {
130 return 0.0;
131 }
132 let v = padic_valuation(n, p);
133 (p as f64).powi(-v as i32)
134}
135
136pub fn padic_distance(a: i64, b: i64, p: u64) -> f64 {
138 padic_norm(a - b, p)
139}
140
141pub fn add(a: &PAdic, b: &PAdic) -> PAdic {
143 assert_eq!(a.prime, b.prime, "cannot add p-adics with different primes");
144 let p = a.prime;
145 let prec = a.digits.len().min(b.digits.len());
146
147 let min_val = a.valuation.min(b.valuation);
149 let a_shift = (a.valuation - min_val) as usize;
150 let b_shift = (b.valuation - min_val) as usize;
151
152 let total_len = prec + a_shift.max(b_shift);
153 let mut digits = vec![0u64; total_len];
154 let mut carry = 0u64;
155
156 for i in 0..total_len {
157 let da = if i >= a_shift && i - a_shift < a.digits.len() {
158 a.digits[i - a_shift]
159 } else {
160 0
161 };
162 let db = if i >= b_shift && i - b_shift < b.digits.len() {
163 b.digits[i - b_shift]
164 } else {
165 0
166 };
167 let sum = da + db + carry;
168 digits[i] = sum % p;
169 carry = sum / p;
170 }
171
172 digits.truncate(prec);
174
175 let mut val = min_val;
177 while !digits.is_empty() && digits[0] == 0 {
178 digits.remove(0);
179 val += 1;
180 }
181 if digits.is_empty() {
182 digits.push(0);
183 val = 0;
184 }
185
186 PAdic { prime: p, digits, valuation: val }
187}
188
189pub fn multiply(a: &PAdic, b: &PAdic) -> PAdic {
191 assert_eq!(a.prime, b.prime);
192 let p = a.prime;
193 let prec = a.digits.len().min(b.digits.len());
194 let val = a.valuation + b.valuation;
195
196 let mut digits = vec![0u64; prec];
197 for i in 0..a.digits.len().min(prec) {
198 let mut carry = 0u64;
199 for j in 0..b.digits.len().min(prec) {
200 if i + j >= prec {
201 break;
202 }
203 let prod = a.digits[i] * b.digits[j] + digits[i + j] + carry;
204 digits[i + j] = prod % p;
205 carry = prod / p;
206 }
207 }
208
209 PAdic { prime: p, digits, valuation: val }
210}
211
212pub struct PadicRenderer {
218 pub prime: u64,
219 pub depth: usize,
220 pub origin: Vec3,
221 pub scale: f32,
222}
223
224pub struct PadicNode {
225 pub digit: u64,
226 pub level: usize,
227 pub position: Vec3,
228 pub color: Vec4,
229 pub character: char,
230}
231
232impl PadicRenderer {
233 pub fn new(prime: u64, depth: usize, origin: Vec3, scale: f32) -> Self {
234 Self { prime, depth, origin, scale }
235 }
236
237 pub fn render_tree(&self) -> Vec<PadicNode> {
239 let mut nodes = Vec::new();
240 self.build_tree(&mut nodes, 0, 0, 0.0, self.scale);
241 nodes
242 }
243
244 fn build_tree(
245 &self,
246 nodes: &mut Vec<PadicNode>,
247 level: usize,
248 _path_value: u64,
249 x_center: f32,
250 width: f32,
251 ) {
252 if level >= self.depth {
253 return;
254 }
255 let p = self.prime;
256 let child_width = width / p as f32;
257 for d in 0..p {
258 let x = x_center + (d as f32 - (p - 1) as f32 / 2.0) * child_width;
259 let y = -(level as f32) * self.scale * 0.5;
260 let hue = d as f32 / p as f32;
261 nodes.push(PadicNode {
262 digit: d,
263 level,
264 position: self.origin + Vec3::new(x, y, 0.0),
265 color: Vec4::new(hue, 1.0 - hue, 0.5, 1.0),
266 character: std::char::from_digit(d as u32, 36).unwrap_or('?'),
267 });
268 self.build_tree(
269 nodes,
270 level + 1,
271 _path_value * p + d,
272 x,
273 child_width,
274 );
275 }
276 }
277
278 pub fn highlight_path(&self, n: i64) -> Vec<(usize, u64)> {
280 let p = self.prime as i64;
281 let mut path = Vec::new();
282 let mut val = n;
283 for level in 0..self.depth {
284 let d = val.rem_euclid(p) as u64;
285 path.push((level, d));
286 val = val.div_euclid(p);
287 }
288 path
289 }
290}
291
292#[cfg(test)]
295mod tests {
296 use super::*;
297
298 #[test]
299 fn from_integer_basic() {
300 let pa = PAdic::from_integer(25, 5, 5);
301 assert_eq!(pa.prime, 5);
302 assert_eq!(pa.valuation, 2);
303 assert_eq!(pa.digits[0], 1);
305 }
306
307 #[test]
308 fn from_integer_zero() {
309 let pa = PAdic::from_integer(0, 3, 5);
310 assert_eq!(pa.digits, vec![0, 0, 0, 0, 0]);
311 }
312
313 #[test]
314 fn padic_norm_values() {
315 assert!((padic_norm(25, 5) - 0.04).abs() < 1e-12);
317 assert!((padic_norm(12, 3) - 1.0 / 3.0).abs() < 1e-12);
319 assert!((padic_norm(7, 5) - 1.0).abs() < 1e-12);
321 assert_eq!(padic_norm(0, 7), 0.0);
323 }
324
325 #[test]
326 fn padic_distance_values() {
327 assert!((padic_distance(1, 26, 5) - 0.04).abs() < 1e-12);
329 }
330
331 #[test]
332 fn padic_ultrametric() {
333 let a = 3;
335 let b = 8;
336 let c = 18;
337 let p = 5u64;
338 let d_ac = padic_distance(a, c, p);
339 let d_ab = padic_distance(a, b, p);
340 let d_bc = padic_distance(b, c, p);
341 assert!(d_ac <= d_ab.max(d_bc) + 1e-12);
342 }
343
344 #[test]
345 fn add_padics() {
346 let a = PAdic::from_integer(3, 5, 5);
348 let b = PAdic::from_integer(7, 5, 5);
349 let c = add(&a, &b);
350 let val = c.to_f64_approx();
351 assert!((val - 10.0).abs() < 1e-6, "expected 10, got {}", val);
352 }
353
354 #[test]
355 fn multiply_padics() {
356 let a = PAdic::from_integer(3, 5, 5);
358 let b = PAdic::from_integer(4, 5, 5);
359 let c = multiply(&a, &b);
360 let val = c.to_f64_approx();
361 assert!((val - 12.0).abs() < 1e-6, "expected 12, got {}", val);
362 }
363
364 #[test]
365 fn negative_integer() {
366 let pa = PAdic::from_integer(-1, 2, 8);
368 for &d in &pa.digits {
369 assert_eq!(d, 1, "all digits of -1 in 2-adic should be 1");
370 }
371 }
372
373 #[test]
374 fn renderer_tree_size() {
375 let r = PadicRenderer::new(3, 3, Vec3::ZERO, 10.0);
376 let nodes = r.render_tree();
377 assert_eq!(nodes.len(), 3 + 9 + 27);
379 }
380
381 #[test]
382 fn highlight_path() {
383 let r = PadicRenderer::new(5, 4, Vec3::ZERO, 1.0);
384 let path = r.highlight_path(37);
385 assert_eq!(path[0], (0, 2));
387 assert_eq!(path[1], (1, 2));
388 assert_eq!(path[2], (2, 1));
389 assert_eq!(path[3], (3, 0));
390 }
391}