commit 0e372df756b492ef43a3d2061ed1cb88bc086f1f Author: hal8174 Date: Sat Sep 21 22:26:15 2024 +0200 Initial commit. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..b9762b6 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,175 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "bitexperimentation" +version = "0.1.0" +dependencies = [ + "quickcheck", + "quickcheck_macros", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "env_logger" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.158" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quickcheck" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6" +dependencies = [ + "env_logger", + "log", + "rand", +] + +[[package]] +name = "quickcheck_macros" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b22a693222d716a9587786f37ac3f6b4faedb5b80c23914e7303ff5a1d8016e9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "regex" +version = "1.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..04e7b13 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "bitexperimentation" +version = "0.1.0" +edition = "2021" + +[dependencies] + + +[dev-dependencies] +quickcheck = "1" +quickcheck_macros = "1" diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..5d9aff1 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,156 @@ +#[allow(non_snake_case)] +fn L(k: u8) -> u64 { + let mut s = 0; + for i in 0..64u8.div_ceil(k) { + s |= 1 << (i * k); + } + s +} + +#[allow(non_snake_case)] +fn H(k: u8) -> u64 { + L(k) << (k - 1) +} + +fn sideways_addition(x: u64) -> u64 { + let s = x - ((x & 0xAAAAAAAAAAAAAAAA) >> 1); + // 0xA = 0b1010 + let s = (s & 0x3333333333333333) + ((s >> 2) & 0x3333333333333333); + // 0x3 = 0b0011 + let s = (s + (s >> 4)) & 0x0F0F0F0F0F0F0F0F; + u64::wrapping_mul(s, L(8)) >> 56 +} + +fn less_equal_k_signed(x: u64, y: u64, k: u8) -> u64 { + let s = (y | H(k)) - (x & !H(k)); + let s = s ^ x ^ y; + let s = s | (x & !y); + let s = s & !(!x & y); + s & H(k) +} + +fn less_equal_k_unsigned(x: u64, y: u64, k: u8) -> u64 { + let s = (y | H(k)) - (x & !H(k)); + let s = s | x ^ y; + let s = s ^ (x & !y); + s & H(k) +} + +fn grater_than_0_k_unsigned(x: u64, k: u8) -> u64 { + (((x | H(k)) - L(k)) | x) & H(k) +} + +fn select64(x: u64, r: u64) -> u64 { + // Same as in sideways addition + let s = x - ((x & 0xAAAAAAAAAAAAAAAA) >> 1); + // 0xA = 0b1010 + let s = (s & 0x3333333333333333) + ((s >> 2) & 0x3333333333333333); + // 0x3 = 0b0011 + let s = ((s + (s >> 4)) & 0x0F0F0F0F0F0F0F0F).wrapping_mul(L(8)); + + // Select byte + // b: byte index << 3 + let b = + (less_equal_k_signed(s, r.wrapping_mul(L(8)), 8) >> 7).wrapping_mul(L(8)) >> 53 & (!0x7); + if b == 64 { + return 72; + } + + // extract selected byte + let l = r - (((s << 8) >> b) & 0xFF); + + // select index in byte + let s = (grater_than_0_k_unsigned((x >> b & 0xFF).wrapping_mul(L(8)) & 0x8040201008040201, 8) + >> 7) + .wrapping_mul(L(8)); + + b + ((less_equal_k_signed(s, l.wrapping_mul(L(8)), 8) >> 7).wrapping_mul(L(8)) >> 56) +} + +fn trivial_select(data: u64, count: u8) -> u8 { + let mut l = 0; + let mut r = 63; + + while l < r { + let m = (l + r) / 2; + + let shift = 64 - m - 1; + let shifted = data << shift; + + let local_count = shifted.count_ones(); + + // dbg!(l, r, m, shift, local_count, r); + + if local_count < count as u32 { + l = m + 1; + } else { + r = m; + } + } + l as u8 +} + +fn main() { + dbg!(select64(3, 1)); + dbg!(trivial_select(3, 3)); +} + +#[cfg(test)] +mod test { + use quickcheck_macros::quickcheck; + + use crate::*; + + #[quickcheck] + fn qc_test_sideways_addition(x: u64) -> bool { + x.count_ones() as u64 == sideways_addition(x) + } + + #[quickcheck] + fn qc_less_equal_8_signed(x: u64, y: u64) -> bool { + let s = less_equal_k_signed(x, y, 8).to_be_bytes(); + + s.iter() + .zip(x.to_be_bytes().iter().zip(y.to_be_bytes().iter())) + .all(|(s, (&x, &y))| match s { + 0x00 => (x as i8) > (y as i8), + 0x80 => (x as i8) <= (y as i8), + _ => false, + }) + } + + #[quickcheck] + fn qc_less_equal_k_unsigned(x: u64, y: u64) -> bool { + let s = less_equal_k_unsigned(x, y, 8).to_be_bytes(); + + s.iter() + .zip(x.to_be_bytes().iter().zip(y.to_be_bytes().iter())) + .all(|(s, (&x, &y))| match s { + 0x00 => x > y, + 0x80 => x <= y, + _ => false, + }) + } + + #[quickcheck] + fn qc_grater_than_0_k_unsigned(x: u64) -> bool { + let s = grater_than_0_k_unsigned(x, 8).to_be_bytes(); + s.iter().zip(x.to_be_bytes().iter()).all(|(a, b)| match a { + 0x00 => b == &0, + 0x80 => b > &0, + _ => false, + }) + } + + #[quickcheck] + fn qc_select64(x: u64, r: u8) -> bool { + let r = r.clamp(1, 64); + let s = select64(x, r as u64); + + if x.count_ones() <= r as u32 { + s == 72 + } else { + trivial_select(x, r + 1) as u64 == s + } + } +}