| 1 | module main |
| 2 | |
| 3 | import benchmark |
| 4 | import math |
| 5 | import math.big |
| 6 | import os |
| 7 | import strconv |
| 8 | |
| 9 | // Regression benchmark for issue #21319. |
| 10 | // It mirrors the external edigits workload without printing every line, |
| 11 | // so the timing stays focused on `math.big`. |
| 12 | const default_n = 250_001 |
| 13 | const one = big.one_int |
| 14 | const ten = big.c10 |
| 15 | |
| 16 | fn main() { |
| 17 | mut n := default_n |
| 18 | if os.args.len > 1 { |
| 19 | n = strconv.atoi(os.args[1]) or { default_n } |
| 20 | } |
| 21 | mut clock := benchmark.start() |
| 22 | k := binary_search(n) |
| 23 | clock.measure('k=${k}') |
| 24 | mut p, q := sum_terms(0, k - 1) |
| 25 | clock.measure('sum_terms p.bit_len=${p.bit_len()} q.bit_len=${q.bit_len()}') |
| 26 | p += q |
| 27 | a := ten.pow(u32(n - 1)) |
| 28 | clock.measure('pow 10^${n - 1}') |
| 29 | answer := p * a / q |
| 30 | clock.measure('division answer.bit_len=${answer.bit_len()}') |
| 31 | s := answer.str() |
| 32 | clock.measure('str digits=${s.len}') |
| 33 | if s.len != n { |
| 34 | eprintln('unexpected digit count: got ${s.len}, want ${n}') |
| 35 | return |
| 36 | } |
| 37 | println(last_line(s, n)) |
| 38 | } |
| 39 | |
| 40 | fn sum_terms(a int, b int) (big.Integer, big.Integer) { |
| 41 | if b == a + 1 { |
| 42 | return one, big.integer_from_int(b) |
| 43 | } |
| 44 | mid := (a + b) / 2 |
| 45 | p_left, q_left := sum_terms(a, mid) |
| 46 | p_right, q_right := sum_terms(mid, b) |
| 47 | return p_left * q_right + p_right, q_left * q_right |
| 48 | } |
| 49 | |
| 50 | fn binary_search(n int) int { |
| 51 | mut a := 0 |
| 52 | mut b := 1 |
| 53 | for !test_k(n, b) { |
| 54 | a = b |
| 55 | b *= 2 |
| 56 | } |
| 57 | for b - a > 1 { |
| 58 | m := (a + b) / 2 |
| 59 | if test_k(n, m) { |
| 60 | b = m |
| 61 | } else { |
| 62 | a = m |
| 63 | } |
| 64 | } |
| 65 | return b |
| 66 | } |
| 67 | |
| 68 | fn test_k(n int, k int) bool { |
| 69 | if k < 0 { |
| 70 | return false |
| 71 | } |
| 72 | ln_k_factorial := k * (math.log(k) - 1) + 0.5 * math.log(math.tau) |
| 73 | log_10_k_factorial := ln_k_factorial / math.ln10 |
| 74 | return log_10_k_factorial >= n + 50 |
| 75 | } |
| 76 | |
| 77 | fn last_line(s string, n int) string { |
| 78 | chunk_len := if n % 10 == 0 { 10 } else { n % 10 } |
| 79 | start := n - chunk_len |
| 80 | mut line := s[start..n] |
| 81 | for line.len < 10 { |
| 82 | line += ' ' |
| 83 | } |
| 84 | return '${line}\t:${n}' |
| 85 | } |
| 86 | |