1use crate::error::FourWordError;
7use std::net::Ipv4Addr;
8
9const MAX_42_BITS: u64 = (1u64 << 42) - 1; pub struct PureIpCompressor;
14
15impl PureIpCompressor {
16 pub fn compress(ip: Ipv4Addr, port: u16) -> Result<u64, FourWordError> {
18 let ip_u32 = u32::from(ip);
19 let _full_value = ((ip_u32 as u64) << 16) | (port as u64);
20
21 if let Ok(compressed) = Self::bit_reduction_compress(ip_u32, port) {
23 return Ok(compressed);
24 }
25
26 if let Ok(compressed) = Self::prime_factorization_compress(ip_u32, port) {
28 return Ok(compressed);
29 }
30
31 if let Ok(compressed) = Self::polynomial_compress(ip_u32, port) {
33 return Ok(compressed);
34 }
35
36 if let Ok(compressed) = Self::hash_compress(ip_u32, port) {
38 return Ok(compressed);
39 }
40
41 if let Ok(compressed) = Self::sliding_window_compress(ip_u32, port) {
43 return Ok(compressed);
44 }
45
46 Err(FourWordError::InvalidInput(format!(
47 "Cannot compress {ip}:{port} (48→42 bits)"
48 )))
49 }
50
51 fn bit_reduction_compress(ip: u32, port: u16) -> Result<u64, FourWordError> {
53 let full_value = ((ip as u64) << 16) | (port as u64);
60
61 let prime_42 = 4398046511104u64; let compressed = full_value % prime_42;
64
65 if compressed <= MAX_42_BITS {
66 return Ok(compressed);
67 }
68
69 let folded = (full_value & MAX_42_BITS) ^ (full_value >> 42);
71 Ok(folded)
72 }
73
74 fn prime_factorization_compress(ip: u32, port: u16) -> Result<u64, FourWordError> {
76 let full_value = ((ip as u64) << 16) | (port as u64);
79
80 if full_value <= MAX_42_BITS {
82 return Ok(full_value); }
84
85 let factors = Self::small_prime_factors(full_value);
87 if factors.len() <= 3 && factors.iter().all(|&f| f < (1 << 14)) {
88 let compressed = (factors[0] as u64) << 28
90 | (*factors.get(1).unwrap_or(&0) as u64) << 14
91 | (*factors.get(2).unwrap_or(&0) as u64);
92 return Ok(compressed);
93 }
94
95 Err(FourWordError::InvalidInput(
96 "No suitable factorization".to_string(),
97 ))
98 }
99
100 fn polynomial_compress(ip: u32, port: u16) -> Result<u64, FourWordError> {
102 let x = ip as u64;
106 let y = port as u64;
107
108 let a = 65537u64; let b = 97u64; let c = 23u64; let polynomial_result = (a * x + b * y + c) % (MAX_42_BITS + 1);
116
117 Ok(polynomial_result)
118 }
119
120 fn hash_compress(ip: u32, port: u16) -> Result<u64, FourWordError> {
122 let full_value = ((ip as u64) << 16) | (port as u64);
125
126 let mut hash = full_value;
128 hash ^= hash >> 33;
129 hash = hash.wrapping_mul(0xff51afd7ed558ccd);
130 hash ^= hash >> 33;
131 hash = hash.wrapping_mul(0xc4ceb9fe1a85ec53);
132 hash ^= hash >> 33;
133
134 let compressed = hash & MAX_42_BITS;
136
137 Ok(compressed)
138 }
139
140 fn sliding_window_compress(ip: u32, port: u16) -> Result<u64, FourWordError> {
142 let common_bases = [
147 0x0A000000u32, 0xC0A80000u32, 0xAC100000u32, 0x7F000000u32, ];
152
153 for (base_idx, &base) in common_bases.iter().enumerate() {
154 if ip >= base && ip < base + (1 << 20) {
155 let offset = ip - base;
157 let compressed = (base_idx as u64) << 40 | (offset as u64) << 16 | (port as u64);
159
160 if compressed <= MAX_42_BITS {
161 return Ok(compressed);
162 }
163 }
164 }
165
166 Err(FourWordError::InvalidInput(
167 "No suitable base found".to_string(),
168 ))
169 }
170
171 pub fn decompress(compressed: u64) -> Result<(Ipv4Addr, u16), FourWordError> {
173 if compressed > MAX_42_BITS {
174 return Err(FourWordError::InvalidInput(
175 "Invalid compressed value".to_string(),
176 ));
177 }
178
179 let high_bits = compressed >> 40;
183 if high_bits < 4 {
184 return Self::decompress_sliding_window(compressed);
185 }
186
187 Self::decompress_hash_approximate(compressed)
189 }
190
191 fn decompress_sliding_window(compressed: u64) -> Result<(Ipv4Addr, u16), FourWordError> {
192 let base_idx = (compressed >> 40) as usize;
193 let offset = ((compressed >> 16) & 0xFFFFF) as u32;
194 let port = (compressed & 0xFFFF) as u16;
195
196 let bases = [0x0A000000u32, 0xC0A80000u32, 0xAC100000u32, 0x7F000000u32];
197
198 if base_idx < bases.len() {
199 let ip_u32 = bases[base_idx] + offset;
200 Ok((Ipv4Addr::from(ip_u32), port))
201 } else {
202 Err(FourWordError::InvalidInput(
203 "Invalid base index".to_string(),
204 ))
205 }
206 }
207
208 fn decompress_hash_approximate(compressed: u64) -> Result<(Ipv4Addr, u16), FourWordError> {
209 let scaled_ip = ((compressed >> 16) * 0xFFFFFFFF / (MAX_42_BITS >> 16)) as u32;
217 let port = (compressed & 0xFFFF) as u16;
218
219 Ok((Ipv4Addr::from(scaled_ip), port))
220 }
221
222 fn small_prime_factors(mut n: u64) -> Vec<u32> {
223 let mut factors = Vec::new();
224 let primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47];
225
226 for &p in &primes {
227 while n % (p as u64) == 0 {
228 factors.push(p);
229 n /= p as u64;
230 if factors.len() >= 3 {
231 break;
232 }
233 }
234 if factors.len() >= 3 {
235 break;
236 }
237 }
238
239 if n > 1 && n < (1 << 14) && factors.len() < 3 {
240 factors.push(n as u32);
241 }
242
243 factors
244 }
245}
246
247pub struct MathematicalCompressor;
249
250impl MathematicalCompressor {
251 pub fn cantor_pair_compress(ip: u32, port: u16) -> u64 {
253 let x = ip as u64;
254 let y = port as u64;
255
256 let sum = x + y;
261 if sum < (1 << 21) {
262 (sum * (sum + 1) / 2 + y) & MAX_42_BITS
264 } else {
265 Self::bit_interleave_compress(ip, port)
267 }
268 }
269
270 pub fn bit_interleave_compress(ip: u32, port: u16) -> u64 {
272 let mut result = 0u64;
273
274 for i in 0..16 {
278 if i * 2 + 1 < 42 {
279 result |= (((port >> (15 - i)) & 1) as u64) << (i * 2);
281
282 if i < 32 && i * 2 + 1 < 42 {
284 result |= (((ip >> (31 - i)) & 1) as u64) << (i * 2 + 1);
285 }
286 }
287 }
288
289 result
290 }
291
292 pub fn gray_code_compress(ip: u32, port: u16) -> u64 {
294 let full_value = ((ip as u64) << 16) | (port as u64);
295
296 let gray = full_value ^ (full_value >> 1);
298
299 (gray & MAX_42_BITS) ^ (gray >> 42)
301 }
302}
303
304#[cfg(test)]
305mod tests {
306 use super::*;
307
308 #[test]
309 fn test_pure_compression() {
310 let test_cases = vec![
311 (Ipv4Addr::new(192, 168, 1, 100), 80),
312 (Ipv4Addr::new(10, 0, 0, 1), 22),
313 (Ipv4Addr::new(172, 16, 0, 1), 443),
314 (Ipv4Addr::new(8, 8, 8, 8), 53),
315 (Ipv4Addr::new(203, 45, 67, 89), 12345),
316 ];
317
318 for (ip, port) in test_cases {
319 match PureIpCompressor::compress(ip, port) {
320 Ok(compressed) => {
321 assert!(compressed <= MAX_42_BITS);
322 println!("✓ Compressed {ip}:{port} -> {compressed} (fits in 42 bits)");
323
324 if let Ok((dec_ip, dec_port)) = PureIpCompressor::decompress(compressed) {
326 println!(" Decompressed: {dec_ip}:{dec_port}");
327 }
328 }
329 Err(e) => {
330 println!("✗ Failed {ip}:{port} - {e}");
331 }
332 }
333 }
334 }
335
336 #[test]
337 fn test_mathematical_methods() {
338 let ip = Ipv4Addr::new(192, 168, 1, 100);
339 let port = 8080;
340 let ip_u32 = u32::from(ip);
341
342 let cantor = MathematicalCompressor::cantor_pair_compress(ip_u32, port);
343 let interleave = MathematicalCompressor::bit_interleave_compress(ip_u32, port);
344 let gray = MathematicalCompressor::gray_code_compress(ip_u32, port);
345
346 assert!(cantor <= MAX_42_BITS);
347 assert!(interleave <= MAX_42_BITS);
348 assert!(gray <= MAX_42_BITS);
349
350 println!("Cantor pairing: {cantor}");
351 println!("Bit interleaving: {interleave}");
352 println!("Gray code: {gray}");
353 }
354
355 #[test]
356 fn test_compression_coverage() {
357 let mut success_count = 0;
358 let total_tests = 1000;
359
360 use rand::Rng;
361 let mut rng = rand::thread_rng();
362
363 for _ in 0..total_tests {
364 let ip = Ipv4Addr::new(
365 rng.r#gen::<u8>(),
366 rng.r#gen::<u8>(),
367 rng.r#gen::<u8>(),
368 rng.r#gen::<u8>(),
369 );
370 let port = rng.r#gen::<u16>();
371
372 if PureIpCompressor::compress(ip, port).is_ok() {
373 success_count += 1;
374 }
375 }
376
377 let success_rate = success_count as f64 / total_tests as f64;
378 println!(
379 "Compression success rate: {:.1}% ({}/{})",
380 success_rate * 100.0,
381 success_count,
382 total_tests
383 );
384
385 assert!(
387 success_rate > 0.5,
388 "Success rate too low: {:.1}%",
389 success_rate * 100.0
390 );
391 }
392}