feat: accept search limit as CLI argument

Allow users to override the default HCN search limit by passing one optional
positive integer argument. Bad input now exits with status 2 and prints a clear
message instead of silently using a fixed range.

Keep the existing one-billion default when no argument is provided, so current
usage remains unchanged.

Test Plan:
- cargo clippy
- cargo clippy --benches
- cargo clippy --tests
- cargo test

Refs: N/A
This commit is contained in:
2026-04-26 13:48:19 +02:00
parent e36cecd2d3
commit 8222199a7d
+56 -4
View File
@@ -1,6 +1,6 @@
use std::time::Instant;
use std::{env, process, time::Instant};
const SEARCH_LIMIT: u64 = 1_000_000_000;
const DEFAULT_SEARCH_LIMIT: u64 = 1_000_000_000;
const FIRST_PRIMES: &[u64] = &[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31];
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
@@ -108,9 +108,36 @@ fn hcn_records(limit: u64) -> Vec<Candidate> {
records
}
fn parse_search_limit(args: &[String]) -> Result<u64, String> {
let program = args.first().map_or("hcn", String::as_str);
match args.len() {
0 | 1 => Ok(DEFAULT_SEARCH_LIMIT),
2 => {
let raw_limit = &args[1];
let limit = raw_limit
.parse::<u64>()
.map_err(|err| format!("invalid search limit {raw_limit:?}: {err}"))?;
if limit == 0 {
return Err("search limit must be greater than zero".to_owned());
}
Ok(limit)
}
_ => Err(format!("usage: {program} [SEARCH_LIMIT]")),
}
}
fn main() {
let args = env::args().collect::<Vec<_>>();
let search_limit = parse_search_limit(&args).unwrap_or_else(|err| {
eprintln!("{err}");
process::exit(2);
});
let start = Instant::now();
let records = hcn_records(SEARCH_LIMIT);
let records = hcn_records(search_limit);
for Candidate { nr, divisors } in records {
println!("{nr}: {divisors} ({:?} since start)", start.elapsed());
@@ -119,7 +146,7 @@ fn main() {
#[cfg(test)]
mod tests {
use super::{Candidate, hcn_records, max_exponent};
use super::{Candidate, DEFAULT_SEARCH_LIMIT, hcn_records, max_exponent, parse_search_limit};
fn count_divisors(mut nr: u64) -> u64 {
let mut divisor_count = 1;
@@ -233,4 +260,29 @@ mod tests {
fn matches_brute_force_records_up_to_ten_thousand() {
assert_eq!(hcn_records(10_000), brute_force_hcn_records(10_000));
}
#[test]
fn parses_default_search_limit_without_arg() {
assert_eq!(
parse_search_limit(&["hcn".to_owned()]),
Ok(DEFAULT_SEARCH_LIMIT)
);
}
#[test]
fn parses_search_limit_arg() {
assert_eq!(
parse_search_limit(&["hcn".to_owned(), "42000".to_owned()]),
Ok(42_000)
);
}
#[test]
fn rejects_invalid_search_limit_arg() {
assert!(parse_search_limit(&["hcn".to_owned(), "nope".to_owned()]).is_err());
assert!(parse_search_limit(&["hcn".to_owned(), "0".to_owned()]).is_err());
assert!(
parse_search_limit(&["hcn".to_owned(), "100".to_owned(), "200".to_owned()]).is_err()
);
}
}