Compare commits

..

18 Commits

Author SHA1 Message Date
jonah 195961df1b add README.md 2025-09-16 21:59:57 +02:00
jonah c276a5a45b add challenge scripts 2025-09-16 21:47:44 +02:00
jonah 3556025849 [03_are_you_still_doing_this_by_hand] improve performance by precomputing giant steps 2025-09-16 21:47:42 +02:00
jonah cb876e7427 [08_revisit_everything] fully automate the button 2025-09-16 21:47:41 +02:00
jonah 5e37cfe883 [08_revisit_everything] add solution
Luckily M[30] is easily factorable as it is almost a perfect square.
2025-09-16 21:47:40 +02:00
jonah b2e43654c1 [08_revisit_everything] automate solution of challenges one through seven 2025-09-16 21:47:38 +02:00
jonah d54dfc15ec [08_revisit_everything] add initial implementation 2025-09-16 21:47:37 +02:00
jonah f8563d118c [07_weird_assembly_machine] add solution
This solution takes advantage of the fact that fg is linear in terms of add and sub. The main loop consists of repeated application of fg and subtraction of n. Reordering this expression results in a subtraction between a power of fg applied to x and the sum of powers of fg applied to k successive values. Powers of fg can be computed quickly as fg^64 = id and the sum of powers of fg applied to successive values happens to show a pattern every 192 terms.
2025-09-16 21:47:36 +02:00
jonah 3dbf23547a [07_weird_assembly_machine] reorder main loop to make use of a faster implementation of f(g(x)) 2025-09-16 21:47:34 +02:00
jonah 521d25689a [07_weird_assembly_machine] refactor code 2025-09-16 21:47:33 +02:00
jonah a499dd2629 [07_weird_assembly_machine] add a faster implementation of func0 2025-09-16 21:47:06 +02:00
jonah 6dbaffdd28 [07_weird_assembly_machine] replace i64 with u64 2025-09-16 21:18:24 +02:00
jonah 0416261f09 [07_weird_assembly_machine] add submission logic and deobfuscate code 2025-09-16 21:18:22 +02:00
jonah 6ed1c9b6ae [07_weird_assembly_machine] replace byte array with i64 array 2025-09-16 21:18:21 +02:00
jonah 12f3120b29 [07_weird_assembly_machine] add initial translation from webassembly to rust 2025-09-16 21:18:20 +02:00
jonah 04ac13c45d [06_automation_is_not_enough] add solution 2025-09-16 21:18:16 +02:00
jonah 83dacf81c5 [05_what_the_bf] add solution 2025-09-16 21:18:02 +02:00
jonah 6babd63c0a initial commit
This commit adds basic logic for submitting solutions to the server.
2025-09-16 21:17:57 +02:00
13 changed files with 225 additions and 195 deletions
+1 -3
View File
@@ -6,10 +6,8 @@ edition = "2024"
[dependencies] [dependencies]
anyhow = "1.0.99" anyhow = "1.0.99"
json = "0.12.4" json = "0.12.4"
lazy_static = "1.5.0"
reqwest = { version = "0.12.23", features = ["blocking"] } reqwest = { version = "0.12.23", features = ["blocking"] }
rug = "1.28.0" rug = { version = "1.28.0", features = ["integer"], default-features = false }
rand = "0.9.2"
tqdm = "0.8.0" tqdm = "0.8.0"
sha2 = "0.10.9" sha2 = "0.10.9"
percent-encoding = "2.3.2" percent-encoding = "2.3.2"
+18
View File
@@ -0,0 +1,18 @@
# button
This repository contains code for solving the challenges from [button.qedaka.de](button.qedaka.de). There are eight
challenges in total:
- [01_welcome](./js/01_welcome.js)
- [02_these_numbers_are_big](./js/02_these_numbers_are_big.js)
- [03_are_you_still_doing_this_by_hand](./js/03_are_you_still_doing_this_by_hand.js)
- [04_broken_proof_of_work](./js/04_broken_proof_of_work.js)
- [05_what_the_bf](./js/05_what_the_bf.js)
- [06_automation_is_not_enough](./js/06_automation_is_not_enough.js)
- [07_weird_assembly_machine](./js/07_weird_assembly_machine.js)
- [08_revisit_everything](./js/08_revisit_everything.js)
Challenges one through four are trivial to solve. For challenges five through eight there is one binary each which
demonstrates an approach to a solution.
One additional binary for solving the whole puzzle from start to finish is provided.
+15 -8
View File
@@ -14,6 +14,7 @@ fn main() {
println!("x = {x}"); println!("x = {x}");
} }
/// Tries to find a cycle in [`f_n`] using Brent's algorithm.
fn main_cycle_detection() { fn main_cycle_detection() {
compute_async(|tx| { compute_async(|tx| {
// brent's algorithm // brent's algorithm
@@ -69,20 +70,19 @@ fn main_cycle_detection() {
} }
const A: u128 = 909_090_909_091u128; // (10 * b + 1) / 11 /// The deobfuscated function `f`
const B: u128 = 1_000_000_000_000u128; // 10^12
const C: u128 = 999_999_999_989u128; // b - 11
const D: u128 = 999_999_999_999u128; // b - 1
fn f(mut x: u128) -> u128 { fn f(mut x: u128) -> u128 {
const A: u128 = 909_090_909_091u128; // (10 * b + 1) / 11
const B: u128 = 1_000_000_000_000u128; // 10^12
const C: u128 = 999_999_999_989u128; // b - 11
const D: u128 = 999_999_999_999u128; // b - 1
x = x * A % B; x = x * A % B;
x = (x * C + D) / B; x = (x * C + D) / B;
x x
} }
/// Computes `f(f(...f(X0)...))` (where [`f`] is applied `n` times) using data produces by [`main_cycle_detection`]
const MU: u128 = 0;
const LAM: u128 = 249_999_999_997;
/// Computes `f(f(...f(X0)...))` where `f` is applied `n` times.
fn f_n(n: u128) -> u128 { fn f_n(n: u128) -> u128 {
let n = n % LAM; let n = n % LAM;
@@ -101,6 +101,13 @@ fn f_n(n: u128) -> u128 {
value value
} }
/// The offset of the cycle of [`f_n`] as returned by [`main_cycle_detection`].
const MU: u128 = 0;
/// The length of the cycle of [`f_n`] as returned by [`main_cycle_detection`].
const LAM: u128 = 249_999_999_997;
/// In- and output values of [`f_n`] as returned by [`main_cycle_detection`].
const F_DATA: &[(u128, u128)] = &[ const F_DATA: &[(u128, u128)] = &[
(11538000000, 540610237213), (11538000000, 540610237213),
(11539000000, 525319061659), (11539000000, 525319061659),
+22 -28
View File
@@ -3,38 +3,34 @@
use anyhow::Result; use anyhow::Result;
use std::fs::File; use std::fs::File;
use std::io::{BufRead, BufReader}; use std::io::{BufRead, BufReader};
use lazy_static::lazy_static;
use rug::ops::MulFrom; use rug::ops::MulFrom;
use rug::{Assign, Integer}; use rug::{Assign, Integer};
use std::ops::Add; use std::ops::Add;
use std::str::FromStr; use std::str::FromStr;
use std::sync::LazyLock;
use lib::submit; use lib::submit;
lazy_static!( static N0: LazyLock<Integer> = LazyLock::new(|| Integer::from(1001997000002u128));
static ref N0: Integer = Integer::from(1001997000002u128); static X0: LazyLock<Integer> = LazyLock::new(|| Integer::from_str("13361120425250501347832030920224855036595311511513374827901659942687569213067904382419070310529480239935839308518100143939024253857202176158254361885679515473530816156355117821922648901555956036125537445852483998567339002752976575910942962150").unwrap());
static ref X0: Integer = Integer::from_str("13361120425250501347832030920224855036595311511513374827901659942687569213067904382419070310529480239935839308518100143939024253857202176158254361885679515473530816156355117821922648901555956036125537445852483998567339002752976575910942962150").unwrap();
static ref N1: Integer = Integer::from(1774734677598263u128); static N1: LazyLock<Integer> = LazyLock::new(|| Integer::from(1774734677598263u128));
static ref X1: Integer = Integer::from_str("11593323295292067533341930289979269834079920106030434522240627836294015987043679078861672344892723053626369715841527508395668434915559610809835295347647318767117730544084796074700752732601302352244011354650441946234192592199510139121367920997").unwrap(); static X1: LazyLock<Integer> = LazyLock::new(|| Integer::from_str("11593323295292067533341930289979269834079920106030434522240627836294015987043679078861672344892723053626369715841527508395668434915559610809835295347647318767117730544084796074700752732601302352244011354650441946234192592199510139121367920997").unwrap());
static ref N2: Integer = Integer::from(10000000000000000u128); static M: LazyLock<Integer> = LazyLock::new(|| Integer::from_str("14004392365098131090160062970945115111185775413941111064876648140973294115502980816410773368597517292734034227298996122159833675150497554142801209096513652073059992938078366061434391648276904643753267405058183481162693381822800709938988762923").unwrap());
static ref X2: Integer = Integer::from_str("12451812012967875768280645960359102621677777894853024330321329610110355503343897740935595925071894025774585740051400274576363979250507927352038651542641174781860822343081975931661658111525566916416897092390763978119448659635732847002032508677").unwrap(); static E: LazyLock<Integer> = LazyLock::new(|| Integer::from(65537));
static ref M: Integer = Integer::from_str("14004392365098131090160062970945115111185775413941111064876648140973294115502980816410773368597517292734034227298996122159833675150497554142801209096513652073059992938078366061434391648276904643753267405058183481162693381822800709938988762923").unwrap(); static A: LazyLock<Integer> = LazyLock::new(|| Integer::from_str("1466928606874115117499939299261").unwrap());
static ref E: Integer = Integer::from(65537); static B: LazyLock<Integer> = LazyLock::new(|| Integer::from_str("49119078231137394008451554322").unwrap());
static ref A: Integer = Integer::from_str("1466928606874115117499939299261").unwrap();
static ref B: Integer = Integer::from_str("49119078231137394008451554322").unwrap();
);
fn main() { fn main() {
let f = faulhaber_equation_from_file("res/06_faulhaber_coefficient_65537.log").unwrap(); let f = faulhaber_formula_from_file("res/06_faulhaber_coefficient_65537.log").unwrap();
let n: Integer = Integer::from(10000000000000000u128); let n: Integer = Integer::from(10000000000000000u128);
let x = (X1.clone() + f(n.clone()) - f(N1.clone())).modulo(&M); let x = (X1.clone() + f(n.clone()) - f(N1.clone())).modulo(&M);
println!("{:?}", x); println!("{:?}", x);
println!("{:?}", submit(n, x).unwrap()); println!("{:?}", submit(n, x).unwrap());
// Use the submission server as an oracle to find the `n` at which `x` needs to be doubles.
// let (mut low, mut high) = ( // let (mut low, mut high) = (
// Integer::from(1774734677598262u128), // Integer::from(1774734677598262u128),
// Integer::from(1774734677598263u128), // Integer::from(1774734677598263u128),
@@ -55,19 +51,17 @@ fn main() {
// } // }
} }
fn step(mut n: Integer, mut x: Integer, mut temp1: Integer, mut temp2: Integer) -> (Integer, Integer, Integer, Integer) { /// Computes one step of the challenge.
fn step(mut n: Integer, mut x: Integer) -> (Integer, Integer) {
n += 1; n += 1;
let b: &Integer = &B; let b: &Integer = &B;
// a = pow(n, e, m) // a = pow(n, e, m)
temp1.assign(&n); let a = n.clone().pow_mod(&E, &M).unwrap();
let a = temp1.pow_mod(&E, &M).unwrap();
// if (a % A == b) { // if (a % A == b) {
temp2.assign(&a); if a.clone().modulo(&A).eq(b) {
let c = temp2.modulo(&A);
if c.eq(b) {
// x += x // x += x
x *= 2; x *= 2;
} }
@@ -76,19 +70,18 @@ fn step(mut n: Integer, mut x: Integer, mut temp1: Integer, mut temp2: Integer)
// x %= m // x %= m
x = x.add(&a).modulo(&M); x = x.add(&a).modulo(&M);
(n, x, a, c) (n, x)
} }
fn faulhaber_formula(n: usize) -> impl Fn(Integer) -> Integer {
fn faulhaber_equation(n: usize) -> impl Fn(Integer) -> Integer {
let coefficients = faulhaber_triangle(n); let coefficients = faulhaber_triangle(n);
for (i, coeff) in coefficients.iter().enumerate() { for (i, coeff) in coefficients.iter().enumerate() {
println!("{i} = {coeff}"); println!("{i} = {coeff}");
} }
faulhaber_equation_from_coefficients(coefficients) faulhaber_formula_from_coefficients(coefficients)
} }
fn faulhaber_equation_from_file(path: &str) -> Result<impl Fn(Integer) -> Integer> { fn faulhaber_formula_from_file(path: &str) -> Result<impl Fn(Integer) -> Integer> {
let mut coefficients = Vec::new(); let mut coefficients = Vec::new();
let file = File::open(path)?; let file = File::open(path)?;
@@ -103,10 +96,10 @@ fn faulhaber_equation_from_file(path: &str) -> Result<impl Fn(Integer) -> Intege
sum.modulo_mut(&M); sum.modulo_mut(&M);
println!("{:?}", sum); println!("{:?}", sum);
Ok(faulhaber_equation_from_coefficients(coefficients)) Ok(faulhaber_formula_from_coefficients(coefficients))
} }
fn faulhaber_equation_from_coefficients(coefficients: Vec<Integer>) -> impl Fn(Integer) -> Integer { fn faulhaber_formula_from_coefficients(coefficients: Vec<Integer>) -> impl Fn(Integer) -> Integer {
move |x| { move |x| {
let mut result = Integer::from(0); let mut result = Integer::from(0);
let mut temp = Integer::from(0); let mut temp = Integer::from(0);
@@ -127,6 +120,7 @@ fn faulhaber_equation_from_coefficients(coefficients: Vec<Integer>) -> impl Fn(I
} }
} }
/// Computes the coefficients of the Faulhaber's formula.
fn faulhaber_triangle(n: usize) -> Vec<Integer> { fn faulhaber_triangle(n: usize) -> Vec<Integer> {
let neg1 = Integer::from(-1); let neg1 = Integer::from(-1);
let mut inv = Vec::with_capacity(n + 10); let mut inv = Vec::with_capacity(n + 10);
+2 -2
View File
@@ -1,11 +1,11 @@
use lib::submit; use lib::submit;
use lib::challenges::c7_weird_assembly_machine::f_n; use lib::challenges::c7_weird_assembly_machine::h_n;
const N: u64 = 979607657800000055; const N: u64 = 979607657800000055;
const X: u64 = 10962444957429324784; const X: u64 = 10962444957429324784;
fn main() { fn main() {
let (n, x) = f_n(N, X, 1_000_000_000_000_000_000u64 - N); let (n, x) = h_n(N, X, 1_000_000_000_000_000_000u64 - N);
println!("n = {n}"); println!("n = {n}");
println!("x = {x}"); println!("x = {x}");
submit(n, x).unwrap(); submit(n, x).unwrap();
+14 -16
View File
@@ -1,14 +1,12 @@
#![allow(dead_code)] #![allow(dead_code)]
// You can change the color of your name without changing your name. use std::sync::LazyLock;
use lazy_static::lazy_static;
use lib::submit_with_name; use lib::submit_with_name;
use rug::ops::{DivRounding, Pow}; use rug::ops::DivRounding;
use rug::Integer; use rug::Integer;
use std::str::FromStr; use std::str::FromStr;
lazy_static!( // You can change the color of your name without changing your name.
static ref M: Vec<Integer> = vec![ static M: LazyLock<Vec<Integer>> = LazyLock::new(|| vec![
Integer::from_str("1434008120931012805118743381511953470053626771934922366720474361817092344508461315782084314493561729943207476618986862630642339876124303277669871276872966561372584096756184146218943438497504108129008248947186295903702650936563195517405177267").unwrap(), Integer::from_str("1434008120931012805118743381511953470053626771934922366720474361817092344508461315782084314493561729943207476618986862630642339876124303277669871276872966561372584096756184146218943438497504108129008248947186295903702650936563195517405177267").unwrap(),
Integer::from_str("2279983661166952569412337967792106681853682761140508555222469060830820310854240357968620360297575902950280777825697828457195462861233847516393870602678279643129330544204145040751369969379909048248974150190225328726111083312867874886821021103").unwrap(), Integer::from_str("2279983661166952569412337967792106681853682761140508555222469060830820310854240357968620360297575902950280777825697828457195462861233847516393870602678279643129330544204145040751369969379909048248974150190225328726111083312867874886821021103").unwrap(),
Integer::from_str("2403618920217682814630057320815015451780244245370783386150600183266752801851444036459202928614238339117073052233115617730895326577436837775969446809467396763885076993639257102975158794586432834588823770619549927566204909745661463923650207527").unwrap(), Integer::from_str("2403618920217682814630057320815015451780244245370783386150600183266752801851444036459202928614238339117073052233115617730895326577436837775969446809467396763885076993639257102975158794586432834588823770619549927566204909745661463923650207527").unwrap(),
@@ -51,16 +49,16 @@ lazy_static!(
Integer::from_str("2861034394438211841574954026003099819138101239247590122220350015475642835378420417008259980687528330087930738069620032709655292858712710050922359392413494086067180601383341612168193292772028605966992134213209023470256124404157207490432001519").unwrap(), Integer::from_str("2861034394438211841574954026003099819138101239247590122220350015475642835378420417008259980687528330087930738069620032709655292858712710050922359392413494086067180601383341612168193292772028605966992134213209023470256124404157207490432001519").unwrap(),
Integer::from_str("2192496740130688243467014868961425982524426805724142867736885035473886629020046845749143690992453345556925544037271383038390660805714030629830845497904446979103911346917405936472233104694103049940390437540539269358666128436739707024829933171").unwrap(), Integer::from_str("2192496740130688243467014868961425982524426805724142867736885035473886629020046845749143690992453345556925544037271383038390660805714030629830845497904446979103911346917405936472233104694103049940390437540539269358666128436739707024829933171").unwrap(),
Integer::from_str("4729254672091433598483706976591610165407510022326154887172965802144851945337220063633854456109688411742113721031379206359782182729020553179365019549633309936476652959201812418790254033614677768930428905990768941807243900642422321011756634063").unwrap(), Integer::from_str("4729254672091433598483706976591610165407510022326154887172965802144851945337220063633854456109688411742113721031379206359782182729020553179365019549633309936476652959201812418790254033614677768930428905990768941807243900642422321011756634063").unwrap(),
]; ]);
static ref E: Integer = Integer::from(65537); static E: LazyLock<Integer> = LazyLock::new(|| Integer::from(65537));
static ref E_100: Integer = Integer::from(65537).pow(100);
static ref E_1000: Integer = Integer::from(65537).pow(1000);
static ref P: Integer = Integer::from_str("2134638905903015345595412931439525422327695207079839833349799642610037970639957457078422305821013930668706217301787851463").unwrap(); /// the first prime factor of [`M[30]`](M)
static ref Q: Integer = Integer::from_str("2134638905903015345595412931439525422327695207079839833349799642610037970639957457078422305821013930668706217301787851473").unwrap(); static P: LazyLock<Integer> = LazyLock::new(|| Integer::from_str("2134638905903015345595412931439525422327695207079839833349799642610037970639957457078422305821013930668706217301787851463").unwrap());
static ref PHI: Integer = Integer::from_str("4556683258594822402855414380362620295231673046713752880933967889771646856051745784462468117575177892006513392693685616565666555545842363207724851343853529903674824524238732001458342230627654145983386115997227846421784693186847971068054052064").unwrap(); /// the second prime factor of [`M[30]`](M)
); static Q: LazyLock<Integer> = LazyLock::new(|| Integer::from_str("2134638905903015345595412931439525422327695207079839833349799642610037970639957457078422305821013930668706217301787851473").unwrap());
/// [`φ(M[30])`](M) where `φ` is Euler's totient function
static PHI: LazyLock<Integer> = LazyLock::new(|| Integer::from_str("4556683258594822402855414380362620295231673046713752880933967889771646856051745784462468117575177892006513392693685616565666555545842363207724851343853529903674824524238732001458342230627654145983386115997227846421784693186847971068054052064").unwrap());
const LIMIT: u128 = 100_000_000_000_000_000_000; const LIMIT: u128 = 100_000_000_000_000_000_000;
const NAME: &str = " Jonah"; const NAME: &str = " Jonah";
@@ -68,8 +66,8 @@ const N: u128 = 1000000000000000000;
const X: &str = "3595876711108608978"; const X: &str = "3595876711108608978";
fn main() { fn main() {
assert_eq!(M[30].clone(), P.clone() * Q.clone()); debug_assert_eq!(M[30].clone(), P.clone() * Q.clone());
assert_eq!(PHI.clone(), (P.clone() - 1) * (Q.clone() - 1)); debug_assert_eq!(PHI.clone(), (P.clone() - 1) * (Q.clone() - 1));
let truth = Integer::from(42); let truth = Integer::from(42);
let k = Integer::from(LIMIT - N) * 100; let k = Integer::from(LIMIT - N) * 100;
+1 -1
View File
@@ -9,8 +9,8 @@ fn main() -> ExitCode {
eprintln!("Usage: main NAME"); eprintln!("Usage: main NAME");
return ExitCode::FAILURE; return ExitCode::FAILURE;
} }
let mut name = args[1].clone(); let mut name = args[1].clone();
loop { loop {
println!("\nAttempting to solve for \"{}\"", &name); println!("\nAttempting to solve for \"{}\"", &name);
@@ -1,13 +1,11 @@
use std::sync::LazyLock;
use std::str::FromStr; use std::str::FromStr;
use lazy_static::lazy_static;
use rug::Integer; use rug::Integer;
use rug::integer::Order; use rug::integer::Order;
use sha2::{Sha256, Digest}; use sha2::{Sha256, Digest};
lazy_static!( static M: LazyLock<Integer> = LazyLock::new(|| Integer::from_str("14004392365098131090160062970945115111185775413941111064876648140973294115502980816410773368597517292734034227298996122159833675150497554142801209096513652073059992938078366061434391648276904643753267405058183481162693381822800709938988762923").unwrap());
static ref M: Integer = Integer::from_str("14004392365098131090160062970945115111185775413941111064876648140973294115502980816410773368597517292734034227298996122159833675150497554142801209096513652073059992938078366061434391648276904643753267405058183481162693381822800709938988762923").unwrap(); static E: LazyLock<Integer> = LazyLock::new(|| Integer::from(65537));
static ref E: Integer = Integer::from(65537);
);
pub fn solve(name: &str) -> (u64, Integer) { pub fn solve(name: &str) -> (u64, Integer) {
let mut x = sha256(name); let mut x = sha256(name);
@@ -1,24 +1,44 @@
use lazy_static::lazy_static;
use rug::Integer; use rug::Integer;
use std::collections::HashMap; use std::collections::HashMap;
use std::io::{stdout, Write}; use std::fs::File;
use std::io::Write;
use std::sync::LazyLock;
use tqdm::tqdm; 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; const P: u64 = 12345679943u64;
lazy_static!( /// the precomputed baby steps
static ref BABY_STEPS: Vec<u64> = std::fs::read("res/03_baby_steps.bin").unwrap() static BABY_STEPS: LazyLock<Vec<u64>> = 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) .chunks_exact(8)
.map(|chunk| u64::from_be_bytes(chunk.try_into().unwrap())) .map(|chunk| u64::from_be_bytes(chunk.try_into().unwrap()))
.collect(); .collect()
});
static ref GIANT_STEPS: HashMap<u64, usize> = std::fs::read("res/03_giant_steps.bin").unwrap() /// the precomputed giant steps
static GIANT_STEPS: LazyLock<HashMap<u64, usize>> = 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) .chunks_exact(8)
.map(|chunk| u64::from_be_bytes(chunk.try_into().unwrap())) .map(|chunk| u64::from_be_bytes(chunk.try_into().unwrap()))
.enumerate() .enumerate()
.map(|(idx, step)| (step, idx)) .map(|(idx, step)| (step, idx))
.collect(); .collect()
); });
pub fn solve(n: u64, x: Integer) -> (u64, Integer) { pub fn solve(n: u64, x: Integer) -> (u64, Integer) {
let x = x.modulo(&Integer::from(P - 1)).to_u64().unwrap(); let x = x.modulo(&Integer::from(P - 1)).to_u64().unwrap();
@@ -33,6 +53,8 @@ pub fn solve(n: u64, x: Integer) -> (u64, Integer) {
(n, Integer::from(x)) (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<u64> { fn log42_mod_p(b: u64) -> Option<u64> {
if b % P == 1 { if b % P == 1 {
return Some(0); return Some(0);
@@ -51,36 +73,33 @@ fn log42_mod_p(b: u64) -> Option<u64> {
None None
} }
pub fn precompute() { /// Precomputes the giant steps for use in the Baby-Step-Giant-Step algorithm for a fixed base `g` and modul `m` using a
let g = 42u64; /// stride of `s`.
let m = P; fn precompute_giant_steps(g: u64, m: u64, s: u64, out: &mut impl Write) {
let s = 5341666;
precompute_giant_steps(g, m, s);
precompute_baby_steps(g, m, s);
}
fn precompute_giant_steps(g: u64, m: u64, s: u64) {
let g = g as u128; let g = g as u128;
let m = m as u128; let m = m as u128;
let mut a = 1u128; let mut a = 1u128;
for _ in tqdm(0..s) { for _ in tqdm(0..s) {
stdout().write_all(&(a as u64).to_be_bytes()).unwrap(); out.write_all(&(a as u64).to_be_bytes()).unwrap();
a *= g; a *= g;
a %= m; a %= m;
} }
} }
fn precompute_baby_steps(g: u64, m: u64, s: u64) { /// 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 gs = pow(g, s, m) as u128;
let m= m as u128; let m= m as u128;
let mut a = gs; let mut a = gs;
for _ in tqdm(1..=(m as u64 / s + 1)) { for _ in tqdm(1..=(m as u64 / s + 1)) {
stdout().write_all(&(a as u64).to_be_bytes()).unwrap(); out.write_all(&(a as u64).to_be_bytes()).unwrap();
a *= gs; a *= gs;
a %= m; 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<u64> { fn log(g: u64, b: u64, m: u64) -> Option<u64> {
let g: u128 = g as u128; let g: u128 = g as u128;
let b: u128 = b as u128; let b: u128 = b as u128;
@@ -114,6 +133,7 @@ fn log(g: u64, b: u64, m: u64) -> Option<u64> {
None None
} }
/// Computes the `k`-th power of `a` modulo `m`.
fn pow(a: u64, mut k: u64, m: u64) -> u64 { fn pow(a: u64, mut k: u64, m: u64) -> u64 {
let mut a: u128 = a as u128; let mut a: u128 = a as u128;
let m: u128 = m as u128; let m: u128 = m as u128;
+5 -5
View File
@@ -21,12 +21,12 @@ pub fn solve(n: u64, x: Integer) -> (u64, Integer) {
(LIMIT, x * b % &M) (LIMIT, x * b % &M)
} }
const A: u128 = 909_090_909_091u128; // (10 * b + 1) / 11
const B: u128 = 1_000_000_000_000u128; // 10^12
const C: u128 = 999_999_999_989u128; // b - 11
const D: u128 = 999_999_999_999u128; // b - 1
fn f(mut x: u128) -> u128 { fn f(mut x: u128) -> u128 {
const A: u128 = 909_090_909_091u128; // (10 * b + 1) / 11
const B: u128 = 1_000_000_000_000u128; // 10^12
const C: u128 = 999_999_999_989u128; // b - 11
const D: u128 = 999_999_999_999u128; // b - 1
x = x * A % B; x = x * A % B;
x = (x * C + D) / B; x = (x * C + D) / B;
x x
@@ -1,26 +1,21 @@
use lazy_static::lazy_static;
use rug::Integer; use rug::Integer;
use std::str::FromStr; use std::str::FromStr;
use std::sync::LazyLock;
const START: u64 = 1_000_000_000_000u64; const START: u64 = 1_000_000_000_000u64;
const LIMIT: u64 = 10_000_000_000_000_000u64; const LIMIT: u64 = 10_000_000_000_000_000u64;
lazy_static!( static M: LazyLock<Integer> = LazyLock::new(|| Integer::from_str("14004392365098131090160062970945115111185775413941111064876648140973294115502980816410773368597517292734034227298996122159833675150497554142801209096513652073059992938078366061434391648276904643753267405058183481162693381822800709938988762923").unwrap());
static ref M: Integer = Integer::from_str("14004392365098131090160062970945115111185775413941111064876648140973294115502980816410773368597517292734034227298996122159833675150497554142801209096513652073059992938078366061434391648276904643753267405058183481162693381822800709938988762923").unwrap(); static A: LazyLock<Integer> = LazyLock::new(|| Integer::from_str("10729297455904899337681752672816753703351288544833760635567859176397566160330812285369370751389224534974913042757043771146367160829669925123791471756026119030734890865062863499420767799283504416995775836275660636668989077836006690621539265605").unwrap());
static ref A: Integer = Integer::from_str("10729297455904899337681752672816753703351288544833760635567859176397566160330812285369370751389224534974913042757043771146367160829669925123791471756026119030734890865062863499420767799283504416995775836275660636668989077836006690621539265605").unwrap(); static B: LazyLock<Integer> = LazyLock::new(|| Integer::from_str("4997609466256208183077203585670710326160976219126614124062259398288517298185253986607627790890962248558793881836308854443463332741665631247256917127102588793451033551034614994254514161235462726178612824897626185944163885786520174491419225456").unwrap());
static ref B: Integer = Integer::from_str("4997609466256208183077203585670710326160976219126614124062259398288517298185253986607627790890962248558793881836308854443463332741665631247256917127102588793451033551034614994254514161235462726178612824897626185944163885786520174491419225456").unwrap();
);
pub fn solve(n: u64, x: Integer) -> (u64, Integer) { pub fn solve(n: u64, x: Integer) -> (u64, Integer) {
assert_eq!(n, START); assert_eq!(n, START);
let m: &Integer = &M;
let a: &Integer = &A;
let b: &Integer = &B;
let mut x = x; let mut x = x;
x = (x + a) % m; x = (x + &*A) % &*M;
x = x * 2 % m; x = x * 2 % &*M;
x = (x + b) % m; x = (x + &*B) % &*M;
(LIMIT, x) (LIMIT, x)
} }
+38 -34
View File
@@ -1,11 +1,10 @@
use std::fs::File; use std::fs::File;
use std::io::{BufRead, BufReader}; use std::io::{BufRead, BufReader};
use std::str::FromStr; use std::str::FromStr;
use lazy_static::lazy_static; use std::sync::LazyLock;
use rug::Integer; use rug::Integer;
lazy_static!( static DATA: LazyLock<Vec<u64>> = LazyLock::new(|| {
static ref DATA: Vec<u64> = {
let file = File::open("./res/07_weird_assembly_machine.data"); let file = File::open("./res/07_weird_assembly_machine.data");
let lines = BufReader::new(file.unwrap()).lines(); let lines = BufReader::new(file.unwrap()).lines();
@@ -14,19 +13,21 @@ lazy_static!(
data.push(u64::from_str(&line.unwrap()).unwrap()); data.push(u64::from_str(&line.unwrap()).unwrap());
} }
data data
}; });
);
const LIMIT: u64 = 1_000_000_000_000_000_000u64; const LIMIT: u64 = 1_000_000_000_000_000_000u64;
pub fn solve(n: u64, x: Integer) -> (u64, Integer) { pub fn solve(n: u64, x: Integer) -> (u64, Integer) {
let x = x.to_u64_wrapping(); let x = x.to_u64_wrapping();
let (n, x) = f_n(n, x, LIMIT - n); let (n, x) = h_n(n, x, LIMIT - n);
(n, Integer::from(x)) (n, Integer::from(x))
} }
fn h(x: u64, n: u64) -> u64 {
g(sub(f(x), n))
}
pub fn f_n(mut n: u64, mut x: u64, k: u64) -> (u64, u64) { pub fn h_n(mut n: u64, mut x: u64, k: u64) -> (u64, u64) {
n += 1; n += 1;
x = sub(f(x), n); x = sub(f(x), n);
@@ -45,6 +46,28 @@ pub fn f_n(mut n: u64, mut x: u64, k: u64) -> (u64, u64) {
(n, x) (n, x)
} }
fn f(x: u64) -> u64 {
evaluate(x, &DATA[..128])
}
fn g(x: u64) -> u64 {
evaluate(x, &DATA[128..])
}
/// Quickly computes [`f`](f)([`g`](g)(x)).
fn fg(x: u64) -> u64 {
!x.rotate_left(17)
}
/// Computes [`fg`](fg)(...[`fg`](fg)(x)...) where [`fg`] is applied `n` times.
fn fg_n(mut x: u64, n: u64) -> u64 {
for _ in 0..n%64 {
x = fg(x);
}
x
}
/// Computes the sum from `i = 1` to `k` over `fg^(k-i)(n + i)`.
fn fg_sum(n: u64, k: u64) -> u64 { fn fg_sum(n: u64, k: u64) -> u64 {
let mut sum = 0u64; let mut sum = 0u64;
for i in 1..=k { for i in 1..=k {
@@ -53,6 +76,7 @@ fn fg_sum(n: u64, k: u64) -> u64 {
sum sum
} }
/// Quickly computes `fg_sum` when `k` is divisible by `192`.
fn fast_fg_sum(n: u64, k: u64) -> u64 { fn fast_fg_sum(n: u64, k: u64) -> u64 {
debug_assert!(k % 192 == 0); debug_assert!(k % 192 == 0);
let mut result = fg_sum(n, 0); let mut result = fg_sum(n, 0);
@@ -60,12 +84,13 @@ fn fast_fg_sum(n: u64, k: u64) -> u64 {
result result
} }
fn f(x: u64) -> u64 { /// Evaluates a polynomial over `GF(2)[X] / (X^64 + 1)`
evaluate(x, &DATA[..128], &DATA[..128]) fn evaluate(x: u64, data: &[u64]) -> u64 {
} let mut out = 0;
for i in 0..data.len() {
fn g(x: u64) -> u64 { out = xor_mul(out, x) ^ data[i];
evaluate(x, &DATA[128..], &DATA[128..]) }
out
} }
fn add(a: u64, b: u64) -> u64 { fn add(a: u64, b: u64) -> u64 {
@@ -80,27 +105,6 @@ fn sub(x: u64, mut n: u64) -> u64 {
x.wrapping_sub(n) x.wrapping_sub(n)
} }
fn fg(x: u64) -> u64 {
!x.rotate_left(17)
}
fn fg_n(mut x: u64, n: u64) -> u64 {
for _ in 0..n%64 {
x = fg(x);
}
x
}
fn evaluate(x: u64, data_even: &[u64], data_odd: &[u64]) -> u64 {
let data = if x.count_ones() % 2 == 0 { data_even } else { data_odd };
let mut out = 0;
for i in 0..data.len() {
out = xor_mul(out, x) ^ data[i];
}
out
}
#[cfg(not(all(target_arch = "x86_64", target_feature = "pclmulqdq")))] #[cfg(not(all(target_arch = "x86_64", target_feature = "pclmulqdq")))]
fn xor_mul(a: u64, mut b: u64) -> u64 { fn xor_mul(a: u64, mut b: u64) -> u64 {
let mut x: u64 = 0; let mut x: u64 = 0;
+9 -11
View File
@@ -1,26 +1,24 @@
use rug::ops::DivRounding; use rug::ops::DivRounding;
use rug::Integer; use rug::Integer;
use std::str::FromStr; use std::str::FromStr;
use std::sync::LazyLock;
const PHI: &str = "4556683258594822402855414380362620295231673046713752880933967889771646856051745784462468117575177892006513392693685616565666555545842363207724851343853529903674824524238732001458342230627654145983386115997227846421784693186847971068054052064"; static PHI: LazyLock<Integer> = LazyLock::new(|| Integer::from_str("4556683258594822402855414380362620295231673046713752880933967889771646856051745784462468117575177892006513392693685616565666555545842363207724851343853529903674824524238732001458342230627654145983386115997227846421784693186847971068054052064").unwrap());
const M: &str = "4556683258594822402855414380362620295231673046713752880933967889771646856051745784462468117575177892006513392693685616569935833357648393898915677206732580748330214938398411668157941515847730087263301030154072458063812554524260405671629754999"; static M: LazyLock<Integer> = LazyLock::new(|| Integer::from_str("4556683258594822402855414380362620295231673046713752880933967889771646856051745784462468117575177892006513392693685616569935833357648393898915677206732580748330214938398411668157941515847730087263301030154072458063812554524260405671629754999").unwrap());
const E: u64 = 65537; static E: LazyLock<Integer> = LazyLock::new(|| Integer::from(65537));
static TRUTH: LazyLock<Integer> = LazyLock::new(|| Integer::from(42));
const LIMIT: u128 = 100_000_000_000_000_000_000; const LIMIT: u128 = 100_000_000_000_000_000_000;
pub fn solve(n: u64, x: Integer) -> Result<(u128, Integer), u32> { pub fn solve(n: u64, x: Integer) -> Result<(u128, Integer), u32> {
let truth = Integer::from(42);
let phi = Integer::from_str(PHI).unwrap();
let m = Integer::from_str(M).unwrap();
let k = Integer::from(LIMIT - n as u128) * 100; let k = Integer::from(LIMIT - n as u128) * 100;
let e = Integer::from(E).pow_mod(&k, &phi).unwrap(); let e = E.clone().pow_mod(&k, &PHI).unwrap();
let w: u32 = x.clone().modulo(&truth).try_into().unwrap(); let w: u32 = x.clone().modulo(&TRUTH).try_into().unwrap();
if w != 30 { if w != 30 {
return Err(w); return Err(w);
} }
let mut y = x.clone().div_floor(&truth); let mut y = x.clone().div_floor(&*TRUTH);
y = y.pow_mod(&e, &m).unwrap(); y = y.pow_mod(&e, &M).unwrap();
let x: Integer = y * 42 + w; let x: Integer = y * 42 + w;
Ok((LIMIT, x)) Ok((LIMIT, x))