Skip to content

Commit

Permalink
Init
Browse files Browse the repository at this point in the history
  • Loading branch information
Ricardo Fernández Serrata committed May 6, 2024
0 parents commit 065b5ce
Show file tree
Hide file tree
Showing 7 changed files with 253 additions and 0 deletions.
13 changes: 13 additions & 0 deletions .clippy.toml
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/target
3 changes: 3 additions & 0 deletions .rustfmt.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
hard_tabs = true
max_width = 96 #mean 0x40 0x80
short_array_element_width_threshold = 8
46 changes: 46 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
39 changes: 39 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -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();
}
}
126 changes: 126 additions & 0 deletions src/util.rs
Original file line number Diff line number Diff line change
@@ -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!())
));
}
}

0 comments on commit 065b5ce

Please sign in to comment.