main.rs
9.6 kB · rust · 348 lines
1use num_bigint::BigUint;2use std::env;3use std::time::Instant;45// DIGITS67fn ok_small(mut n: u128, base: u128, top: u128) -> bool {8 while n > 0 {9 if n % base > top {10 return false;11 }12 n /= base;13 }14 true15}1617fn ok_big(n: &BigUint, base: u32, top: u8) -> bool {18 n.to_radix_le(base).iter().all(|&d| d <= top)19}2021fn pow_big(base: u32, e: usize) -> BigUint {22 BigUint::from(base).pow(e as u32)23}2425// SEARCH2627struct Thin {28 cap: BigUint,29 top5: u8,30 top7: u8,31 p3: Vec<BigUint>,32 p5: Vec<BigUint>,33 p7: Vec<BigUint>,34 cut5: Vec<usize>,35 cut7: Vec<usize>,36 hits: Vec<BigUint>,37 nodes: u64,38}3940impl Thin {41 fn new(cap: BigUint, top5: u8, top7: u8) -> Thin {42 let mut p3 = vec![BigUint::from(1u32)];43 while *p3.last().unwrap() < cap {44 let next = p3.last().unwrap() * 3u32;45 p3.push(next);46 }47 let l = p3.len() - 1;48 let one = BigUint::from(1u32);49 let rest: Vec<BigUint> = (0..=l).map(|k| (&p3[k] - &one) / 2u32).collect();50 let top = &rest[l];51 let mut p5 = vec![BigUint::from(1u32)];52 while p5.last().unwrap() <= top {53 let next = p5.last().unwrap() * 5u32;54 p5.push(next);55 }56 let mut p7 = vec![BigUint::from(1u32)];57 while p7.last().unwrap() <= top {58 let next = p7.last().unwrap() * 7u32;59 p7.push(next);60 }61 let cut5: Vec<usize> = rest62 .iter()63 .map(|r| p5.iter().position(|q| q > r).unwrap())64 .collect();65 let cut7: Vec<usize> = rest66 .iter()67 .map(|r| p7.iter().position(|q| q > r).unwrap())68 .collect();69 Thin {70 cap,71 top5,72 top7,73 p3,74 p5,75 p7,76 cut5,77 cut7,78 hits: Vec::new(),79 nodes: 0,80 }81 }8283 fn walk(&mut self, k: usize, v: BigUint) {84 self.nodes += 1;85 if k == 0 {86 if ok_big(&v, 5, self.top5) && ok_big(&v, 7, self.top7) {87 self.hits.push(v);88 }89 return;90 }91 let one = BigUint::from(1u32);92 let h5 = &v / &self.p5[self.cut5[k]];93 if !ok_big(&h5, 5, self.top5) && !ok_big(&(&h5 + &one), 5, self.top5) {94 return;95 }96 let h7 = &v / &self.p7[self.cut7[k]];97 if !ok_big(&h7, 7, self.top7) && !ok_big(&(&h7 + &one), 7, self.top7) {98 return;99 }100 let w = &v + &self.p3[k - 1];101 self.walk(k - 1, v);102 if w < self.cap {103 self.walk(k - 1, w);104 }105 }106107 fn run(mut self) -> (Vec<BigUint>, u64, usize) {108 let l = self.p3.len() - 1;109 self.walk(l, BigUint::from(0u32));110 (self.hits, self.nodes, l)111 }112}113114fn members(cap: BigUint, top5: u8, top7: u8) -> Vec<BigUint> {115 Thin::new(cap, top5, top7).run().0116}117118fn scan(cap: u128, top5: u128, top7: u128) -> Vec<BigUint> {119 let mut out = Vec::new();120 let mut n = 0u128;121 while n < cap {122 if ok_small(n, 3, 1) && ok_small(n, 5, top5) && ok_small(n, 7, top7) {123 out.push(BigUint::from(n));124 }125 n += 1;126 }127 out128}129130fn shown(v: &[BigUint]) -> Vec<String> {131 v.iter().map(|x| x.to_string()).collect()132}133134// OEIS CONTROL135136const A030979: [u128; 23] = [137 0,138 1,139 10,140 756,141 757,142 3160,143 3186,144 3187,145 3250,146 7560,147 7561,148 7651,149 20007,150 59548377,151 59548401,152 45773612811,153 45775397187,154 237617431723407,155 24991943420078301,156 24991943420078302,157 24991943420078307,158 24991943715007536,159 24991943715007537,160];161162fn a030979() -> Vec<BigUint> {163 A030979.iter().map(|&x| BigUint::from(x)).collect()164}165166// BUDGETS167168fn dim(base: f64, alphabet: f64) -> f64 {169 alphabet.ln() / base.ln()170}171172fn up(x: f64) -> f64 {173 (x * 1e6).ceil() / 1e6174}175176fn near(x: f64) -> f64 {177 (x * 1e6).round() / 1e6178}179180fn down(x: f64) -> f64 {181 (x * 1e6).floor() / 1e6182}183184fn budget() {185 let d3 = dim(3.0, 2.0);186 let d5 = dim(5.0, 3.0);187 let d7 = dim(7.0, 3.0);188 let d7w = dim(7.0, 4.0);189 println!("dim log_3 2 {:.6}", near(d3));190 println!("dim log_5 3 {:.6}", near(d5));191 println!("dim log_7 3 {:.6}", near(d7));192 println!("dim log_7 4 {:.6}", near(d7w));193 println!("pair 3 5 {:.6}", up(d3 + d5 - 1.0));194 println!("pair 3 7 {:.6}", up(d3 + d7 - 1.0));195 println!("pair 5 7 {:.6}", up(d5 + d7 - 1.0));196 println!("triple 3 5 7 {:.6}", up(d3 + d5 + d7 - 2.0));197 println!("triple 3 5 7 wide {:.6}", up(d3 + d5 + d7w - 2.0));198 println!("erdos 3 5 {:.6}", 1.0 / 2.0 + 2.0 / 4.0);199 println!("erdos 3 7 {:.6}", 1.0 / 2.0 + 2.0 / 6.0);200 println!("erdos 5 7 {:.6}", 2.0 / 4.0 + 2.0 / 6.0);201 println!("erdos 3 7 wide {:.6}", 1.0 / 2.0 + 3.0 / 6.0);202 println!("erdos 5 7 wide {:.6}", 2.0 / 4.0 + 3.0 / 6.0);203}204205// VERBS206207fn control() {208 let cap = BigUint::from(100_000_000u128);209 let thin = members(cap.clone(), 2, 2);210 let brute = scan(100_000_000u128, 2, 2);211 println!("thin below 10^8 pruned {:?}", shown(&thin));212 println!("thin agrees with the scan {}", thin == brute);213 let wide = members(cap, 2, 3);214 let wide_brute = scan(100_000_000u128, 2, 3);215 println!("wide below 10^8 pruned {:?}", shown(&wide));216 println!("wide agrees with the scan {}", wide == wide_brute);217 let far = BigUint::from(*A030979.last().unwrap() + 1);218 let wide_far = members(far, 2, 3);219 println!("wide terms below the last A030979 term {}", wide_far.len());220 println!("wide rebuilds A030979 {}", wide_far == a030979());221 println!(222 "thin sits inside wide {}",223 thin.iter().all(|x| wide.contains(x))224 );225}226227fn report(base: u32, exp: usize, top7: u8, name: &str) {228 let cap = pow_big(base, exp);229 let t0 = Instant::now();230 let shownice = cap.to_string();231 let (hits, nodes, l) = Thin::new(cap, 2, top7).run();232 if shownice.len() <= 40 {233 println!("{} height {}^{} = {}", name, base, exp, shownice);234 } else {235 println!(236 "{} height {}^{} of {} decimal digits",237 name,238 base,239 exp,240 shownice.len()241 );242 }243 println!("{} powers of three {}", name, l);244 println!("{} nodes {}", name, nodes);245 println!("{} count {}", name, hits.len());246 if hits.len() <= 16 {247 println!("{} members {:?}", name, shown(&hits));248 } else {249 println!("{} largest {}", name, hits.last().unwrap());250 }251 let e = (hits.len() as f64).ln() / (exp as f64 * (base as f64).ln());252 println!("{} effective exponent {:.6}", name, down(e));253 println!("{} seconds {:.2}", name, t0.elapsed().as_secs_f64());254}255256fn main() {257 let args: Vec<String> = env::args().collect();258 let verb = args.get(1).map(|s| s.as_str()).unwrap_or("budget");259 let n: usize = args.get(2).map(|s| s.parse().unwrap()).unwrap_or(0);260 let arg = move || n;261 match verb {262 "budget" => budget(),263 "control" => control(),264 "reach" => big(move || report(3, arg(), 2, "thin")),265 "seven" => big(move || report(7, arg(), 2, "thin")),266 "wide" => big(move || report(3, arg(), 3, "wide")),267 "ten" => big(move || report(10, arg(), 3, "wide")),268 _ => println!("verbs budget control reach seven wide ten"),269 }270}271272fn big<F: FnOnce() + Send + 'static>(f: F) {273 std::thread::Builder::new()274 .stack_size(1 << 30)275 .spawn(f)276 .unwrap()277 .join()278 .unwrap();279}280281#[cfg(test)]282mod tests {283 use super::*;284285 fn list(v: &[u128]) -> Vec<BigUint> {286 v.iter().map(|&x| BigUint::from(x)).collect()287 }288289 #[test]290 fn the_thin_set_below_seven_to_the_seventeen() {291 let cap = pow_big(7, 17);292 assert_eq!(cap.to_string(), "232630513987207");293 assert_eq!(members(cap, 2, 2), list(&[0, 1, 3186, 3187, 20007]));294 }295296 #[test]297 fn the_thin_set_gains_no_member_by_three_to_the_two_hundred() {298 assert_eq!(299 members(pow_big(3, 200), 2, 2),300 list(&[0, 1, 3186, 3187, 20007])301 );302 }303304 #[test]305 fn the_pruned_walk_agrees_with_a_direct_scan() {306 let cap = 3u128.pow(15);307 assert_eq!(members(BigUint::from(cap), 2, 2), scan(cap, 2, 2));308 assert_eq!(members(BigUint::from(cap), 2, 3), scan(cap, 2, 3));309 }310311 #[test]312 fn the_wide_variant_rebuilds_a030979() {313 let far = BigUint::from(*A030979.last().unwrap() + 1);314 assert_eq!(members(far, 2, 3), a030979());315 }316317 #[test]318 fn every_member_passes_all_three_digit_rules() {319 for n in members(pow_big(3, 120), 2, 2) {320 assert!(ok_big(&n, 3, 1));321 assert!(ok_big(&n, 5, 2));322 assert!(ok_big(&n, 7, 2));323 }324 }325326 #[test]327 fn the_pair_budgets_are_positive_and_the_triple_budget_is_not() {328 let d3 = dim(3.0, 2.0);329 let d5 = dim(5.0, 3.0);330 let d7 = dim(7.0, 3.0);331 assert_eq!(format!("{:.6}", up(d3 + d5 - 1.0)), "0.313536");332 assert_eq!(format!("{:.6}", up(d3 + d7 - 1.0)), "0.195505");333 assert_eq!(format!("{:.6}", up(d5 + d7 - 1.0)), "0.247182");334 assert_eq!(format!("{:.6}", up(d3 + d5 + d7 - 2.0)), "-0.121889");335 assert_eq!(336 format!("{:.6}", up(d3 + d5 + dim(7.0, 4.0) - 2.0)),337 "0.025951"338 );339 }340341 #[test]342 fn the_dimensions_print_to_six_places() {343 assert_eq!(format!("{:.6}", near(dim(3.0, 2.0))), "0.630930");344 assert_eq!(format!("{:.6}", near(dim(5.0, 3.0))), "0.682606");345 assert_eq!(format!("{:.6}", near(dim(7.0, 3.0))), "0.564575");346 assert_eq!(format!("{:.6}", near(dim(7.0, 4.0))), "0.712414");347 }348}