Day 13
This commit is contained in:
75
13-shuttle_search/Cargo.lock
generated
Normal file
75
13-shuttle_search/Cargo.lock
generated
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
[[package]]
|
||||||
|
name = "anyhow"
|
||||||
|
version = "1.0.38"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chinese_remainder"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.24"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-xid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shuttle_search"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"chinese_remainder",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "1.0.60"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-xid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "1.0.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "76cc616c6abf8c8928e2fdcc0dbfab37175edd8fb49a4641066ad1364fdab146"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "1.0.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9be73a2caec27583d0046ef3796c3794f868a5bc813db689eed00c7631275cd1"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-xid"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
||||||
@@ -7,3 +7,6 @@ edition = "2018"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
anyhow = "1.0.38"
|
||||||
|
chinese_remainder = { path = "../chinese_remainder/" }
|
||||||
|
thiserror = "1.0.23"
|
||||||
|
|||||||
2
13-shuttle_search/data/input.txt
Normal file
2
13-shuttle_search/data/input.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
1014511
|
||||||
|
17,x,x,x,x,x,x,41,x,x,x,x,x,x,x,x,x,643,x,x,x,x,x,x,x,23,x,x,x,x,13,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,29,x,433,x,x,x,x,x,37,x,x,x,x,x,x,x,x,x,x,x,x,19
|
||||||
@@ -1,3 +1,97 @@
|
|||||||
fn main() {
|
use anyhow::Result;
|
||||||
println!("Hello, world!");
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
struct ProblemStatement {
|
||||||
|
departure: u64,
|
||||||
|
lines: Vec<u64>,
|
||||||
|
}
|
||||||
|
fn main() {
|
||||||
|
let input = include_str!("../data/input.txt");
|
||||||
|
println!("Part 1: {}", step1(parse_input(input).unwrap()));
|
||||||
|
println!("Part 2: {}", step2(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_input(i: &str) -> Result<ProblemStatement> {
|
||||||
|
let mut lines = i.lines();
|
||||||
|
Ok(ProblemStatement {
|
||||||
|
departure: lines
|
||||||
|
.next()
|
||||||
|
.ok_or_else(|| anyhow::anyhow!("Missing departure"))?
|
||||||
|
.parse::<u64>()?,
|
||||||
|
lines: lines
|
||||||
|
.next()
|
||||||
|
.unwrap()
|
||||||
|
.split(',')
|
||||||
|
.filter(|&c| c != "x")
|
||||||
|
.map(|t| t.parse::<u64>().unwrap())
|
||||||
|
.collect(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn step1(p: ProblemStatement) -> u64 {
|
||||||
|
let r = p
|
||||||
|
.lines
|
||||||
|
.iter()
|
||||||
|
.map(|&l| (l - p.departure % l, l))
|
||||||
|
.min()
|
||||||
|
.unwrap();
|
||||||
|
r.0 * r.1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn step2(input: &str) -> i64 {
|
||||||
|
let (remainders, moduli): (Vec<i64>, Vec<i64>) = input.lines().nth(1)
|
||||||
|
.unwrap()
|
||||||
|
.split(',')
|
||||||
|
.enumerate()
|
||||||
|
.filter_map(
|
||||||
|
|(num, lineno)| {
|
||||||
|
let line = str::parse::<i64>(lineno).ok()?;
|
||||||
|
Some((line - num as i64, line))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.unzip();
|
||||||
|
chinese_remainder::chinese_remainder(&remainders, &moduli).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
static INPUT: &str = r#"939
|
||||||
|
7,13,x,x,59,x,31,19"#;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse() {
|
||||||
|
assert_eq!(
|
||||||
|
parse_input(INPUT).unwrap(),
|
||||||
|
ProblemStatement {
|
||||||
|
departure: 939,
|
||||||
|
lines: vec![7, 13, 59, 31, 19],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_step1() {
|
||||||
|
assert_eq!(step1(parse_input(INPUT).unwrap()), 295)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_step2() {
|
||||||
|
let v_rep = vec![
|
||||||
|
(INPUT, 1068781),
|
||||||
|
("0\n17,x,13,19", 3417),
|
||||||
|
("0\n67,7,59,61", 754018),
|
||||||
|
("0\n67,x,7,59,61", 779210),
|
||||||
|
("0\n67,7,x,59,61", 1261476),
|
||||||
|
("0\n1789,37,47,1889", 1202161486),
|
||||||
|
];
|
||||||
|
assert_eq!(
|
||||||
|
v_rep
|
||||||
|
.iter()
|
||||||
|
.map(|(i, _)| (*i, step2(i)))
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
v_rep,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
5
chinese_remainder/Cargo.lock
generated
Normal file
5
chinese_remainder/Cargo.lock
generated
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
[[package]]
|
||||||
|
name = "chinese_remainder"
|
||||||
|
version = "0.1.0"
|
||||||
9
chinese_remainder/Cargo.toml
Normal file
9
chinese_remainder/Cargo.toml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
[package]
|
||||||
|
name = "chinese_remainder"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Guilhem MARION <gmarion@netc.fr>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
64
chinese_remainder/src/lib.rs
Normal file
64
chinese_remainder/src/lib.rs
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
/// Represents the GCD computed as part of the Extended Euclidean Algorithm.
|
||||||
|
/// This invariant should always be valid : ax + by = gcd
|
||||||
|
#[derive(Debug,PartialEq)]
|
||||||
|
pub struct EGCD {
|
||||||
|
pub a: i64,
|
||||||
|
pub b: i64,
|
||||||
|
pub x: i64,
|
||||||
|
pub y: i64,
|
||||||
|
pub gcd: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Computes the Extended Euclidean Algorithm's solution to finding a & b's GCD
|
||||||
|
/// Additionally to the standard Euclidean Algorithm, it provides x and y so
|
||||||
|
/// that ax + by = GCD
|
||||||
|
pub fn egcd(a: i64, b: i64) -> EGCD {
|
||||||
|
// If b == 0, then a == gcd(a,b)
|
||||||
|
// Then gcd = 1 * a + 0 * b follows
|
||||||
|
if b == 0 {
|
||||||
|
EGCD {
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
x: 1,
|
||||||
|
// Anything other than 0 gives a valid solution but
|
||||||
|
// With different end x and y
|
||||||
|
y: 0,
|
||||||
|
gcd: a,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let rec = egcd(b, a%b);
|
||||||
|
EGCD {
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
x: rec.y,
|
||||||
|
y: rec.x - rec.y * (a / b),
|
||||||
|
gcd: rec.gcd,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Solves the chinese remainder problem, stated as follows:
|
||||||
|
/// ```noexec
|
||||||
|
/// for i in residues.len(),
|
||||||
|
/// (chinese_remainder(residues, moduli) - residues[i]) / moduli[i] == 0
|
||||||
|
/// ```
|
||||||
|
pub fn chinese_remainder(residues: &[i64], moduli: &[i64]) -> Option<i64> {
|
||||||
|
// TODO: Filter out 0s in moduli (meaning big_n is 0 -> div by 0)
|
||||||
|
let big_n: i64 = moduli.iter().product();
|
||||||
|
Some(moduli.iter()
|
||||||
|
.map(|&ni| {
|
||||||
|
egcd(ni, big_n / ni)
|
||||||
|
})
|
||||||
|
.zip(residues)
|
||||||
|
.map(|(egcd,ai)| {
|
||||||
|
if egcd.gcd != 1 {
|
||||||
|
// Fail in case moduli are not co-prime
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(ai * egcd.y * egcd.b)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.sum::<Option<i64>>()?
|
||||||
|
.rem_euclid(big_n)) // Sum on Option<_> returns None if the Iter
|
||||||
|
// contains a None
|
||||||
|
}
|
||||||
36
chinese_remainder/tests/tests.rs
Normal file
36
chinese_remainder/tests/tests.rs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use chinese_remainder::{EGCD, egcd, chinese_remainder};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_egcd() {
|
||||||
|
assert_eq!(
|
||||||
|
egcd(25, 7),
|
||||||
|
EGCD {
|
||||||
|
a: 25,
|
||||||
|
b: 7,
|
||||||
|
x: 2,
|
||||||
|
y: -7,
|
||||||
|
gcd: 1,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_cr1() {
|
||||||
|
assert_eq!(
|
||||||
|
chinese_remainder(&[2, 3, 2], &[3, 5, 7]), Some(23));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cr2() {
|
||||||
|
assert_eq!(
|
||||||
|
chinese_remainder(&[11,22,19], &[10,4,9]), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cr3() {
|
||||||
|
assert_eq!(
|
||||||
|
chinese_remainder(&[10,4,12], &[11,12,13]), Some(1000));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user