1#![feature(let_chains)]
2use lazy_static::lazy_static;
9
10const EPSILON: f64 = 1e-17;
11const SIXTEEN: f64 = 16.0;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
14pub struct Location {
15 pub offset: usize,
16 pub length: usize,
17}
18
19pub struct PiIterator {
20 offset: usize,
21 digits: Vec<u8>,
22}
23
24impl PiIterator {
25 pub fn new() -> PiIterator {
26 PiIterator {
27 offset: 0,
28 digits: vec![],
29 }
30 }
31
32 pub fn from(offset: usize) -> PiIterator {
33 PiIterator {
34 offset,
35 digits: vec![],
36 }
37 }
38
39 pub fn refill(&mut self) {
40 self.digits = pi_digits(self.offset);
41 self.digits.reverse();
42 self.offset += self.digits.len();
43 }
44}
45
46impl Iterator for PiIterator {
47 type Item = u8;
48
49 fn next(&mut self) -> Option<Self::Item> {
50 if self.digits.is_empty() {
51 self.refill();
52 }
53 self.digits.pop()
54 }
55}
56
57pub fn nibbles(msg: String) -> Vec<u8> {
58 msg.bytes().flat_map(|b| [b / 16, b & 0x0F]).collect()
59}
60
61pub fn compress(msg: &[u8], limit: usize) -> Vec<Location> {
62 let mut locs = vec![];
63
64 let mut index = 0;
65 while index < msg.len() {
66 let m = find_pi_match(&msg[index..], limit).expect("should find some match");
67 index += m.length;
68 locs.push(m);
69 }
70
71 locs
72}
73
74pub fn find_pi_match(msg: &[u8], limit: usize) -> Option<Location> {
75 let mut best_match: Option<Location> = None;
76 let mut offset = 0;
77
78 'outer: while offset < limit {
79 let mut pi_digits = PiIterator::from(offset);
80
81 while let Some(b) = pi_digits.next()
82 && b != msg[0]
83 {
84 offset += 1;
85 if offset >= limit {
86 break 'outer;
87 }
88 }
89
90 let length = pi_digits
91 .zip(msg.iter().skip(1))
92 .take_while(|(a, &b)| *a == b)
93 .count() + 1;
94
95 if length == msg.len() {
96 return Some(Location { offset, length });
97 } else if let Some(m) = &best_match {
98 if length > m.length {
99 best_match = Some(Location { offset, length });
100 }
101 } else {
102 best_match = Some(Location { offset, length });
103 }
104
105 offset += 1;
106 }
107
108 best_match
109}
110
111pub fn pi_digits(id: usize) -> Vec<u8> {
112 let s1 = series(1, id as i64);
113 let s2 = series(4, id as i64);
114 let s3 = series(5, id as i64);
115 let s4 = series(6, id as i64);
116 let pid = 4. * s1 - 2. * s2 - s3 - s4;
117 let pid = pid - pid.floor() + 1.;
118
119 to_hex(pid)
120}
121
122pub fn to_hex(x: f64) -> Vec<u8> {
123 let mut chx = vec![0; 8];
124
125 let mut y = x.abs();
126
127 for i in 0..8 {
128 y = 16.0 * (y - y.floor());
129 chx[i] = y as u8;
130 }
131
132 chx
133}
134
135pub fn series(m: i64, id: i64) -> f64 {
136 let mut s: f64 = 0.0;
137
138 for k in 0..id {
139 let ak = (8 * k + m) as f64;
140 let p = (id - k) as f64;
141 let t = expm(p, ak);
142 s = s + t / ak;
143 s = s - f64::floor(s);
144 }
145
146 for k in id..=(id + 100) {
147 let ak = (8 * k + m) as f64;
148 let t = SIXTEEN.powf((id - k) as f64) / ak;
149 if t < EPSILON {
150 break;
151 }
152
153 s = s + t;
154 s = s - s.floor();
155 }
156
157 s
158}
159
160const NUM_POWERS: usize = 25;
161lazy_static! {
162 static ref TWO_TO_THE: [f64; NUM_POWERS] = {
163 let mut t = [0.0; NUM_POWERS];
164
165 t[0] = 1.0;
166 for i in 1..NUM_POWERS {
167 t[i] = t[i - 1] * 2.;
168 }
169
170 t
171 };
172}
173
174pub fn expm(p: f64, ak: f64) -> f64 {
175 if ak == 1.0 {
176 return 0.0;
177 }
178
179 let mut i = 0;
180 for idx in 1..NUM_POWERS {
181 if TWO_TO_THE[idx] > p {
182 i = idx;
183 break;
184 }
185 }
186 let mut pt = TWO_TO_THE[i - 1];
187
188 let mut p1 = p;
189 let mut r: f64 = 1.0;
190
191 for _ in 1..=i {
192 if p1 >= pt {
193 r = 16.0 * r;
194 r = r - (r / ak).floor() * ak;
195 p1 = p1 - pt;
196 }
197 pt = 0.5 * pt;
198 if pt >= 1.0 {
199 r = r * r;
200 r = r - (r / ak).floor() * ak;
201 }
202 }
203
204 r
205}