From 065b5ce9ebebb6bf6e4f14ef4a249a442c93db77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Fern=C3=A1ndez=20Serrata?= Date: Mon, 6 May 2024 01:33:48 -0400 Subject: [PATCH] Init --- .clippy.toml | 13 ++++++ .gitignore | 1 + .rustfmt.toml | 3 ++ Cargo.lock | 46 ++++++++++++++++++ Cargo.toml | 25 ++++++++++ src/main.rs | 39 ++++++++++++++++ src/util.rs | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 253 insertions(+) create mode 100644 .clippy.toml create mode 100644 .gitignore create mode 100644 .rustfmt.toml create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/main.rs create mode 100644 src/util.rs diff --git a/.clippy.toml b/.clippy.toml new file mode 100644 index 0000000..1be25d7 --- /dev/null +++ b/.clippy.toml @@ -0,0 +1,13 @@ +single-char-binding-names-threshold = 3 +too-large-for-stack = 256 +cognitive-complexity-threshold = 12 +array-size-threshold = 262144 #2^18 +enum-variant-size-threshold = 128 +too-many-arguments-threshold = 4 +literal-representation-threshold = 16 +too-many-lines-threshold = 64 +trivial-copy-size-limit = 2 +max-include-file-size = 262144 +type-complexity-threshold = 32 +max-trait-bounds = 1 +vec-box-size-threshold = 1024 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/.rustfmt.toml b/.rustfmt.toml new file mode 100644 index 0000000..bb6454d --- /dev/null +++ b/.rustfmt.toml @@ -0,0 +1,3 @@ +hard_tabs = true +max_width = 96 #mean 0x40 0x80 +short_array_element_width_threshold = 8 diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..2a81b23 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,46 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "n82k" +version = "0.1.0" +dependencies = [ + "num-bigint", + "num-integer", +] + +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..1b29d1f --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "n82k" +version = "0.1.0" +edition = "2021" + +[dependencies] +num-bigint = "0.4.4" +num-integer = "0.1.46" + +[lints.clippy] +pedantic = "warn" +nursery = "warn" +unwrap_used = "warn" +float_arithmetic = "forbid" +float_cmp = "forbid" +large_include_file = "forbid" + +[lints.rust] +unsafe_code = "deny" + +[profile.release] +strip = true +lto = true +codegen-units = 1 +panic = "abort" diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..d164efa --- /dev/null +++ b/src/main.rs @@ -0,0 +1,39 @@ +mod util; +#[allow(clippy::wildcard_imports)] +use util::*; + +/// lowest unknown, as of year 2024 +const BASE: Non0U8 = match Non0U8::new(6) { + Some(n) => n, + _ => unreachable!(), +}; + +/// According to [Numberphile](https://youtu.be/LNS1fabDkeA) +/// thousand-digit numerals have already been checked. +const START: u128 = u128::MAX; + +fn main() { + let start = START; + // prevent us from using a trivial value + assert!(start > 1); + + // each bit represents a digit in radix `BASE` + let mut packed_numeral = UN::from(start); + loop { + /* + We must pay the price of conversion, + regardless of representation. + Maybe someone can come up with a clever algorithm + that exploits previously-unpacked values to infer the next? + */ + let n = unpack_as_radix(&packed_numeral, BASE); + // by definition, `BASE` is already checked, + // so no need to include it in the range + if is_0_1_all(&n, BASE) { + println!("{packed_numeral:#x}"); + break; + } + // skip all `n` that match `!is_0_1(n, BASE)` + packed_numeral.inc(); + } +} diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..5b20bbb --- /dev/null +++ b/src/util.rs @@ -0,0 +1,126 @@ +pub use core::num::NonZeroU8 as Non0U8; +pub use num_bigint::BigUint as UN; +pub use num_integer::Integer; + +const MIN_NON_TRIVIAL_BASE: Non0U8 = match Non0U8::new(3) { + Some(n) => n, + _ => unreachable!(), +}; + +/// Interpret the bits of `digits` as digits in base `radix`, +/// and return the numeric value represented by that numeral. +pub fn unpack_as_radix(digits: &UN, radix: Non0U8) -> UN { + let mut out = UN::default(); + let mut pow = UN::from(1u8); + + // LSb + debug_assert_eq!(digits.bit(0), digits.is_odd()); + for i in 0..digits.bits() { + if digits.bit(i) { + out += pow.clone(); + } + pow *= radix.get(); + } + out +} + +/// Checks if `n` can be written in base `radix`, +/// using only zeros and ones. +pub fn is_0_1(mut n: UN, radix: Non0U8) -> bool { + let radix = UN::from(radix.get()); + let n1 = UN::from(1u8); + + // 1 is just "1" in any radix + while n > n1 { + let digit; + (n, digit) = n.div_rem(&radix); + if digit.bits() > 8 { + unreachable!() + } + if digit > n1 { + return false; + } + } + true +} + +/// Checks if `n` can be written using only zeros and ones, +/// in all bases +/// from the minimun non-trivial base (inclusive) +/// to `max_radix` (exclusive). +pub fn is_0_1_all(n: &UN, max_radix: Non0U8) -> bool { + // would `rev` be more optimal? + for radix in MIN_NON_TRIVIAL_BASE.get()..max_radix.get() { + if !is_0_1( + n.clone(), + Non0U8::new(radix).unwrap_or_else(|| unreachable!()), + ) { + return false; + } + } + true +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn unpacker_works() { + assert_eq!( + unpack_as_radix( + &UN::from(0b1010u8), + Non0U8::new(3).unwrap_or_else(|| unreachable!("3 == 0 ???")) + ), + UN::from(27u8 + 3u8) + ); + } + + #[test] + fn checker_happy() { + assert!(is_0_1( + UN::from(3u8), + Non0U8::new(3).unwrap_or_else(|| unreachable!()) + )); + assert!(is_0_1( + UN::from(4u8), + Non0U8::new(3).unwrap_or_else(|| unreachable!()) + )); + assert!(is_0_1_all( + &UN::from(4u8), + Non0U8::new(5).unwrap_or_else(|| unreachable!()) + )); + assert!(is_0_1_all( + &UN::from(82000u32), + Non0U8::new(6).unwrap_or_else(|| unreachable!()) + )); + assert!(is_0_1_all( + &UN::from(82000u32), + Non0U8::new(6).unwrap_or_else(|| unreachable!()) + )); + } + + #[test] + fn checker_sad() { + assert!(!is_0_1( + UN::from(2u8), + Non0U8::new(3).unwrap_or_else(|| unreachable!()) + )); + assert!(!is_0_1( + UN::from(3u8), + Non0U8::new(4).unwrap_or_else(|| unreachable!()) + )); + assert!(!is_0_1_all( + &UN::from(3u8), + Non0U8::new(5).unwrap_or_else(|| unreachable!()) + )); + assert!(!is_0_1_all( + &UN::from(5u8), + Non0U8::new(5).unwrap_or_else(|| unreachable!()) + )); + assert!(!is_0_1_all( + &UN::from(4u8), + Non0U8::new(6).unwrap_or_else(|| unreachable!()) + )); + } +}