1#![forbid(unsafe_code)]
14
15use std::fmt;
16
17pub mod random;
18
19pub const UNITY: i32 = 0x1_0000;
21pub const TWO: i32 = 0x2_0000;
23pub const MAX_DIMEN: i32 = 0o7777777777;
25pub const INFINITY: i32 = 0o17777777777;
27pub const INF_BAD: i32 = 10000;
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub struct Overflow;
33
34impl fmt::Display for Overflow {
35 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36 f.write_str("arithmetic overflow")
37 }
38}
39
40impl std::error::Error for Overflow {}
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
44pub struct Scaled(pub i32);
45
46impl Scaled {
47 pub const ZERO: Scaled = Scaled(0);
49 pub const MAX: Scaled = Scaled(MAX_DIMEN);
51
52 pub fn to_tex_string(self) -> String {
55 let mut out = String::new();
56 print_scaled(&mut out, self.0);
57 out
58 }
59}
60
61impl fmt::Display for Scaled {
62 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63 let mut s = String::new();
64 print_scaled(&mut s, self.0);
65 f.write_str(&s)
66 }
67}
68
69pub fn half(x: i32) -> i32 {
71 if x & 1 != 0 {
72 (x + 1) / 2
74 } else {
75 x / 2
76 }
77}
78
79pub fn round_decimals(digits: &[u8]) -> i32 {
83 let mut a: i32 = 0;
84 for &d in digits.iter().rev() {
85 a = (a + i32::from(d) * TWO) / 10;
86 }
87 (a + 1) / 2
88}
89
90pub fn print_scaled(out: &mut String, s: i32) {
94 if s == i32::MIN {
95 out.push_str("--32768.0");
99 return;
100 }
101 let mut s = i64::from(s);
102 if s < 0 {
103 out.push('-');
104 s = -s;
105 }
106 let unity = i64::from(UNITY);
107 out.push_str(&(s / unity).to_string());
108 out.push('.');
109 s = 10 * (s % unity) + 5;
110 let mut delta: i64 = 10;
111 loop {
112 if delta > unity {
113 s += 0o100000 - 50000;
115 }
116 let digit = (s / unity) as u8; out.push(char::from(b'0' + digit));
118 s = 10 * (s % unity);
119 delta *= 10;
120 if s <= delta {
121 break;
122 }
123 }
124}
125
126pub fn mult_and_add(mut n: i32, mut x: i32, y: i32, max_answer: i32) -> Result<i32, Overflow> {
129 if n < 0 {
130 x = x.wrapping_neg();
131 n = -n;
132 }
133 if n == 0 {
134 return Ok(y);
135 }
136 let (n64, x64, y64, m) = (
138 i64::from(n),
139 i64::from(x),
140 i64::from(y),
141 i64::from(max_answer),
142 );
143 if x64 <= (m - y64) / n64 && -x64 <= (m + y64) / n64 {
144 Ok((n64 * x64 + y64) as i32)
145 } else {
146 Err(Overflow)
147 }
148}
149
150pub fn nx_plus_y(n: i32, x: i32, y: i32) -> Result<i32, Overflow> {
153 mult_and_add(n, x, y, 0o7777777777)
154}
155
156pub fn mult_integers(n: i32, x: i32) -> Result<i32, Overflow> {
158 mult_and_add(n, x, 0, 0o17777777777)
159}
160
161#[derive(Debug, Clone, Copy, PartialEq, Eq)]
163pub struct DivResult {
164 pub quotient: i32,
166 pub remainder: i32,
168}
169
170pub fn x_over_n(x: i32, n: i32) -> Result<DivResult, Overflow> {
174 if n == 0 {
175 return Err(Overflow);
176 }
177 Ok(x_over_n_unchecked(x, n))
178}
179
180pub fn x_over_n_unchecked(mut x: i32, mut n: i32) -> DivResult {
183 if n == 0 {
184 return DivResult {
185 quotient: 0,
186 remainder: x,
187 };
188 }
189 let mut negative = false;
190 if n < 0 {
191 x = x.wrapping_neg();
192 n = n.wrapping_neg();
193 negative = true;
194 }
195 let (q, mut r) = if x >= 0 {
196 (x / n, x % n)
197 } else {
198 let nx = x.wrapping_neg();
200 (-(nx / n), -(nx % n))
201 };
202 if negative {
203 r = -r;
204 }
205 DivResult {
206 quotient: q,
207 remainder: r,
208 }
209}
210
211pub fn xn_over_d(x: i32, n: i32, d: i32) -> Result<DivResult, Overflow> {
216 const B: i64 = 0o100000; let positive = x >= 0;
218 let x = i64::from(x).abs();
219 let (n, d) = (i64::from(n), i64::from(d));
220 let t = (x % B) * n;
221 let mut u = (x / B) * n + (t / B);
222 let v = (u % d) * B + (t % B);
223 if u / d >= B {
224 return Err(Overflow);
225 }
226 u = B * (u / d) + (v / d);
227 let (q, r) = (u as i32, (v % d) as i32);
228 Ok(if positive {
229 DivResult {
230 quotient: q,
231 remainder: r,
232 }
233 } else {
234 DivResult {
235 quotient: -q,
236 remainder: -r,
237 }
238 })
239}
240
241pub fn badness(t: i32, s: i32) -> i32 {
245 if t == 0 {
246 return 0;
247 }
248 if s <= 0 {
249 return INF_BAD;
250 }
251 let r: i32 = if t <= 7230584 {
252 (t * 297) / s
253 } else if s >= 1663497 {
254 t / (s / 297)
255 } else {
256 t
257 };
258 if r > 1290 {
259 INF_BAD
260 } else {
261 (r * r * r + 0o400000) / 0o1000000
262 }
263}
264
265pub fn add_or_sub(x: i32, mut y: i32, max_answer: i32, negative: bool) -> Result<i32, Overflow> {
268 if negative {
269 y = y.wrapping_neg();
270 }
271 if x >= 0 {
272 if y <= max_answer - x {
273 Ok(x + y)
274 } else {
275 Err(Overflow)
276 }
277 } else if y >= -max_answer - x {
278 Ok(x + y)
279 } else {
280 Err(Overflow)
281 }
282}
283
284pub fn quotient(mut n: i32, mut d: i32) -> Result<i32, Overflow> {
287 if d == 0 {
288 return Err(Overflow);
289 }
290 let mut negative = if d > 0 {
291 false
292 } else {
293 d = -d;
294 true
295 };
296 if n < 0 {
297 n = n.wrapping_neg();
298 negative = !negative;
299 }
300 let mut a = n / d;
301 n -= a * d;
302 d = n - d;
303 if d + n >= 0 {
304 a += 1;
305 }
306 Ok(if negative { -a } else { a })
307}
308
309pub fn fract(mut x: i32, mut n: i32, mut d: i32, max_answer: i32) -> Result<i32, Overflow> {
313 if d == 0 {
314 return Err(Overflow);
315 }
316 let mut a: i32 = 0;
317 let mut negative = if d > 0 {
318 false
319 } else {
320 d = -d;
321 true
322 };
323 if x < 0 {
324 x = x.wrapping_neg();
325 negative = !negative;
326 } else if x == 0 {
327 return Ok(0);
328 }
329 if n < 0 {
330 n = n.wrapping_neg();
331 negative = !negative;
332 }
333 let finish = |a: i32, negative: bool| if negative { -a } else { a };
334 let mut t = n / d;
335 if t > max_answer / x {
336 return Err(Overflow);
337 }
338 a = a.wrapping_add(t * x);
339 n -= t * d;
340 if n == 0 {
341 return Ok(finish(a, negative));
342 }
343 t = x / d;
344 if t > (max_answer - a) / n {
345 return Err(Overflow);
346 }
347 a += t * n;
348 x -= t * d;
349 if x == 0 {
350 return Ok(finish(a, negative));
351 }
352 if x < n {
353 std::mem::swap(&mut x, &mut n);
354 }
355 let mut f: i32 = 0;
357 let mut r: i32 = (d / 2) - d;
358 let h = -r;
359 loop {
360 if n & 1 != 0 {
361 r += x;
362 if r >= 0 {
363 r -= d;
364 f += 1;
365 }
366 }
367 n /= 2;
368 if n == 0 {
369 break;
370 }
371 if x < h {
372 x += x;
373 } else {
374 t = x - d;
375 x += t;
376 f += n;
377 if x < n {
378 if x == 0 {
379 break;
380 }
381 std::mem::swap(&mut x, &mut n);
382 }
383 }
384 }
385 if f > max_answer - a {
386 return Err(Overflow);
387 }
388 a += f;
389 Ok(finish(a, negative))
390}
391
392#[cfg(test)]
393mod tests {
394 use super::*;
395 use proptest::prelude::*;
396
397 #[test]
398 fn print_scaled_examples() {
399 let cases = [
400 (0, "0.0"),
401 (UNITY, "1.0"),
402 (-UNITY, "-1.0"),
403 (UNITY / 2, "0.5"),
404 (1, "0.00002"),
405 (MAX_DIMEN, "16383.99998"),
406 (655360, "10.0"),
407 (26214, "0.4"),
408 (32768 + 1, "0.50002"),
409 (i32::MIN, "--32768.0"),
411 ];
412 for (v, s) in cases {
413 assert_eq!(Scaled(v).to_tex_string(), s, "value {v}");
414 }
415 }
416
417 #[test]
418 fn round_decimals_examples() {
419 assert_eq!(round_decimals(&[5]), 32768);
420 assert_eq!(round_decimals(&[2, 5]), 16384);
421 assert_eq!(round_decimals(&[]), 0);
422 assert_eq!(round_decimals(&[9, 9, 9, 9, 9, 9]), UNITY);
423 }
424
425 #[test]
426 fn half_rounds_up_on_odd() {
427 assert_eq!(half(3), 2);
428 assert_eq!(half(-3), -1);
429 assert_eq!(half(4), 2);
430 assert_eq!(half(-4), -2);
431 }
432
433 #[test]
434 fn badness_examples() {
435 assert_eq!(badness(0, 0), 0);
436 assert_eq!(badness(1, 0), INF_BAD);
437 assert_eq!(badness(UNITY, UNITY), 100);
438 assert_eq!(badness(UNITY, 2 * UNITY), 12);
439 assert_eq!(badness(3 * UNITY, UNITY), 2698);
440 assert_eq!(badness(5 * UNITY, UNITY), INF_BAD);
441 }
442
443 #[test]
444 fn overflow_cases() {
445 assert_eq!(nx_plus_y(2, MAX_DIMEN, 0), Err(Overflow));
446 assert_eq!(x_over_n(5, 0), Err(Overflow));
447 assert_eq!(quotient(5, 0), Err(Overflow));
448 assert_eq!(fract(5, 1, 0, INFINITY), Err(Overflow));
449 assert_eq!(add_or_sub(INFINITY, 1, INFINITY, false), Err(Overflow));
450 }
451
452 proptest! {
453 #[test]
456 fn print_scaled_round_trips(v in -MAX_DIMEN..=MAX_DIMEN) {
457 let s = Scaled(v).to_tex_string();
458 let (neg, body) = match s.strip_prefix('-') {
459 Some(b) => (true, b),
460 None => (false, s.as_str()),
461 };
462 let (int, frac) = body.split_once('.').unwrap();
463 let digits: Vec<u8> = frac.bytes().map(|b| b - b'0').collect();
464 let back = int.parse::<i32>().unwrap() * UNITY + round_decimals(&digits);
465 prop_assert_eq!(if neg { -back } else { back }, v);
466 }
467
468 #[test]
469 fn x_over_n_truncates(x in -MAX_DIMEN..=MAX_DIMEN, n in -100_000i32..100_000) {
470 prop_assume!(n != 0);
471 let r = x_over_n(x, n).unwrap();
472 prop_assert_eq!(r.quotient, x / n);
473 prop_assert_eq!(r.remainder, x % n);
475 }
476
477 #[test]
478 fn xn_over_d_matches_wide(x in -MAX_DIMEN..=MAX_DIMEN, n in 0i32..=0x10000, d in 1i32..=0x10000) {
479 let wide = i64::from(x) * i64::from(n);
480 let q = wide / i64::from(d);
481 match xn_over_d(x, n, d) {
482 Ok(r) => {
483 prop_assert_eq!(i64::from(r.quotient), q);
484 prop_assert_eq!(i64::from(r.remainder), wide % i64::from(d));
485 }
486 Err(Overflow) => prop_assert!(q.abs() >= 1 << 30),
487 }
488 }
489
490 #[test]
491 fn fract_matches_wide(x in -INFINITY..=INFINITY, n in -INFINITY..=INFINITY, d in -INFINITY..=INFINITY) {
492 prop_assume!(d != 0);
493 let (ax, an, ad) = (i128::from(x).abs(), i128::from(n).abs(), i128::from(d).abs());
494 let q = (2 * ax * an + ad) / (2 * ad);
495 let neg = (x < 0) ^ (n < 0) ^ (d < 0);
496 match fract(x, n, d, INFINITY) {
497 Ok(v) => prop_assert_eq!(i128::from(v), if neg && x != 0 { -q } else { q }),
498 Err(Overflow) => prop_assert!(q > i128::from(INFINITY)),
499 }
500 }
501
502 #[test]
503 fn quotient_rounds_half_away(n in -INFINITY..=INFINITY, d in -INFINITY..=INFINITY) {
504 prop_assume!(d != 0);
505 let (an, ad) = (i64::from(n).abs(), i64::from(d).abs());
506 let q = (2 * an + ad) / (2 * ad);
507 let neg = (n < 0) ^ (d < 0);
508 prop_assert_eq!(i64::from(quotient(n, d).unwrap()), if neg { -q } else { q });
509 }
510 }
511}