summaryrefslogtreecommitdiff
path: root/src/main.rs
blob: 8be934df96adce52274ba813df4fbc31dc91e2d1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
use std::ffi::OsString;
use std::process::ExitCode;

// bnum seems to be slightly faster than ruint,
// and 3x faster than uint.
type UInt = bnum::types::U256;

fn is_choose_r(mut k: UInt, r: u32) -> bool {
	// multiply k by r! to avoid future divisions
	for i in 1..=r {
		k *= UInt::from(i);
	}
	let k = k; // no morge changing k
	let log = k.ilog2();
	let mut hi = UInt::from(1u8) << (log / r + 2);
	let mut lo = UInt::from(r + 1);
	'outer: while lo < hi {
		let mid: UInt = (lo + hi) / UInt::from(2u8);
		let mut i = mid;
		let mut mid_falling_r = mid;
		for _ in 1..r {
			i -= UInt::from(1u8);
			mid_falling_r *= i;
			if mid_falling_r > k {
				hi = mid;
				continue 'outer;
			}
		}
		if mid_falling_r < k {
			lo = mid + UInt::from(1u8);
		} else {
			return true;
		}
	}
	false
}

fn superscript(number: &str) -> String {
	number
		.chars()
		.map(|c| match c {
			'0' => '⁰',
			'1' => '¹',
			'2' => '²',
			'3' => '³',
			'4' => '⁴',
			'5' => '⁵',
			'6' => '⁶',
			'7' => '⁷',
			'8' => '⁸',
			'9' => '⁹',
			_ => c,
		})
		.collect()
}

fn main() -> ExitCode {
	let args: Vec<OsString> = std::env::args_os().collect();
	if args.len() > 2 {
		eprintln!("Please provide at most 1 argument (power of 10 to search up to).");
		return ExitCode::FAILURE;
	}
	let power_of_10: Option<usize> = match args.get(1) {
		Some(s) => s.clone().into_string().ok().and_then(|x| x.parse().ok()),
		None => Some(35),
	};
	let Some(power_of_10) = power_of_10 else {
		eprintln!("Argument must be a nonnegative integer");
		return ExitCode::FAILURE;
	};
	if power_of_10 > usize::MAX / 4
		|| (power_of_10 as f64 * f64::log2(10.0)) as usize + 10 > size_of::<UInt>() * 8
	{
		eprintln!("Power of 10 is too large for integer type. You will have to increase the size of UInt in the source code.");
		return ExitCode::FAILURE;
	}

	let mut pascal_row = [UInt::from(0u8); 500];
	let mut range = pascal_row.len();
	println!(
		"searching up to 10{}",
		superscript(&format!("{power_of_10}"))
	);
	let limit = UInt::from(10u8).pow(power_of_10 as u32);
	let mut numbers: Vec<UInt> = Vec::new();
	pascal_row[0] = UInt::from(1u8);
	for row in 1u128.. {
		for i in (1..range).rev() {
			pascal_row[i] += pascal_row[i - 1];
			if i > 4 && i as u128 <= row / 2 {
				if is_choose_r(pascal_row[i], 2)
					|| is_choose_r(pascal_row[i], 3)
					|| is_choose_r(pascal_row[i], 4) {
					println!("{}",pascal_row[i]);
				}
				numbers.push(pascal_row[i]);
			}
			if pascal_row[i] > limit {
				range = i;
			}
		}
		if range <= 4 {
			break;
		}
	}
	println!(
		"memory needed = {}MiB",
		(numbers.len() * size_of::<UInt>()) >> 20
	);
	numbers.sort();
	let mut prev = UInt::from(0u8);
	let mut occurrences = 0;
	for n in numbers {
		if n == prev {
			occurrences += 1;
		} else if occurrences > 0 {
			if occurrences > 1 {
				println!("{prev}: {occurrences}");
			}
			occurrences = 1;
		}
		prev = n;
	}
	ExitCode::SUCCESS
}