diff --git a/res/03_baby_steps.bin b/res/03_baby_steps.bin new file mode 100644 index 0000000..df238dd Binary files /dev/null and b/res/03_baby_steps.bin differ diff --git a/res/03_giant_steps.bin b/res/03_giant_steps.bin new file mode 100644 index 0000000..c2753e0 Binary files /dev/null and b/res/03_giant_steps.bin differ diff --git a/src/lib/challenges/c3_are_you_still_doing_this_by_hand.rs b/src/lib/challenges/c3_are_you_still_doing_this_by_hand.rs index ae07a25..f95d884 100644 --- a/src/lib/challenges/c3_are_you_still_doing_this_by_hand.rs +++ b/src/lib/challenges/c3_are_you_still_doing_this_by_hand.rs @@ -1,23 +1,104 @@ use rug::Integer; use std::collections::HashMap; +use std::fs::File; +use std::io::Write; +use std::sync::LazyLock; use tqdm::tqdm; +/// the base of the discrete logarithm +const G: u64 = 42; +/// the stride value of the Baby-Step-Giant-Step algorithm, controlling the space-time-tradeoff +const S: u64 = 5341666; /// the modul of the discrete logarithm const P: u64 = 12345679943u64; +/// the precomputed baby steps +static BABY_STEPS: LazyLock> = LazyLock::new(|| { + let path = "res/03_baby_steps.bin"; + if !std::fs::exists(path).unwrap() { + precompute_baby_steps(G, P, S, &mut File::create(path).unwrap()); + } + + std::fs::read(path).unwrap() + .chunks_exact(8) + .map(|chunk| u64::from_be_bytes(chunk.try_into().unwrap())) + .collect() +}); + +/// the precomputed giant steps +static GIANT_STEPS: LazyLock> = LazyLock::new(|| { + let path = "res/03_giant_steps.bin"; + if !std::fs::exists(path).unwrap() { + precompute_giant_steps(G, P, S, &mut File::create(path).unwrap()); + } + + std::fs::read(path).unwrap() + .chunks_exact(8) + .map(|chunk| u64::from_be_bytes(chunk.try_into().unwrap())) + .enumerate() + .map(|(idx, step)| (step, idx)) + .collect() +}); + pub fn solve(n: u64, x: Integer) -> (u64, Integer) { let x = x.modulo(&Integer::from(P - 1)).to_u64().unwrap(); let mut n = n; let mut x = x.clone(); - for _ in tqdm(0..10_000) { + for _ in 0..10_000 { n += 1; - x = log(42, 1 + (x + n) % (P - 1), P).unwrap(); + x = log42_mod_p(1 + (x + n) % (P - 1)).unwrap(); } (n, Integer::from(x)) } +/// Quickly computes the discrete logarithm of `b` to the base [`G`] modulo [`M`] using the Baby-Step-Giant-Step +/// algorithm with precomputed steps +fn log42_mod_p(b: u64) -> Option { + if b % P == 1 { + return Some(0); + } + + let inv = pow(b, P - 2, P) as u128; + let s = GIANT_STEPS.len(); + for j in 1..=BABY_STEPS.len() { + let baby_step = BABY_STEPS[j - 1] as u128; + let search = ((baby_step * inv) % P as u128) as u64; + if let Some(i) = GIANT_STEPS.get(&search) { + return Some((j * s - i) as u64); + } + } + + None +} + +/// Precomputes the giant steps for use in the Baby-Step-Giant-Step algorithm for a fixed base `g` and modul `m` using a +/// stride of `s`. +fn precompute_giant_steps(g: u64, m: u64, s: u64, out: &mut impl Write) { + let g = g as u128; + let m = m as u128; + let mut a = 1u128; + for _ in tqdm(0..s) { + out.write_all(&(a as u64).to_be_bytes()).unwrap(); + a *= g; + a %= m; + } +} + +/// Precomputes the baby steps for use in the Baby-Step-Giant-Step algorithm for a fixed base `g` and modul `m` using a +/// stride of `s`. +fn precompute_baby_steps(g: u64, m: u64, s: u64, out: &mut impl Write) { + let gs = pow(g, s, m) as u128; + let m= m as u128; + let mut a = gs; + for _ in tqdm(1..=(m as u64 / s + 1)) { + out.write_all(&(a as u64).to_be_bytes()).unwrap(); + a *= gs; + a %= m; + } +} + /// Computes the discrete logarithm of `b` to the base `g` modulo `m` using the Baby-Step-Giant-Step algorithm. fn log(g: u64, b: u64, m: u64) -> Option { let g: u128 = g as u128;