aoclib_rs/
lib.rs

1pub mod binary_search;
2pub mod dijkstra;
3pub mod dir;
4pub mod iter;
5pub mod matrix;
6pub mod option_min_max;
7pub mod point;
8pub mod trie;
9
10use std::{
11    fmt,
12    fs::{File, read_to_string},
13    io::BufWriter,
14    ops,
15    str::FromStr,
16};
17
18use num_traits::Zero;
19
20#[macro_export]
21macro_rules! printwriteln {
22    ($writer:expr, $fmt:literal) => {
23        {
24            println!($fmt);
25            writeln!($writer, $fmt)
26        }
27    };
28    ($writer:expr, $fmt:literal, $($args:expr),+) => {
29        {
30            println!($fmt, $($args),+);
31            writeln!($writer, $fmt, $($args),+)
32        }
33    };
34}
35
36pub fn pad<T: Clone + Copy>(contents: &Vec<&[T]>, padding: usize, default: T) -> Vec<Vec<T>> {
37    let mut r = Vec::with_capacity(contents.len());
38    let mut prefix = vec![vec![default; contents[0].len() + padding * 2]; padding];
39    r.append(&mut prefix);
40
41    for line in contents {
42        let prefix = vec![default; padding];
43        let middle = line.to_vec();
44        let suffix = vec![default; padding];
45
46        r.push(vec![prefix, middle, suffix].into_iter().flatten().collect());
47    }
48
49    let mut suffix = vec![vec![default; contents[0].len() + padding * 2]; padding];
50    r.append(&mut suffix);
51
52    r
53}
54
55// TODO: having 2 exactly identical functions is stupid. Find a way to fix this.
56pub fn pad_vec<T: Clone + Copy>(contents: &Vec<Vec<T>>, padding: usize, default: T) -> Vec<Vec<T>> {
57    let mut r = Vec::with_capacity(contents.len());
58    let mut prefix = vec![vec![default; contents[0].len() + padding * 2]; padding];
59    r.append(&mut prefix);
60
61    for line in contents {
62        let prefix = vec![default; padding];
63        let middle = line.to_vec();
64        let suffix = vec![default; padding];
65
66        r.push(vec![prefix, middle, suffix].into_iter().flatten().collect());
67    }
68
69    let mut suffix = vec![vec![default; contents[0].len() + padding * 2]; padding];
70    r.append(&mut suffix);
71
72    r
73}
74
75pub fn prep_io(contents: &mut String, day: u8) -> anyhow::Result<(BufWriter<File>, Vec<&str>)> {
76    *contents = read_to_string(format!("inputs/{:02}.txt", day))?;
77    let contents: Vec<&str> = contents.trim().lines().collect();
78
79    let write_file = File::create(format!("outputs/{:02}.txt", day))?;
80    let writer = BufWriter::new(write_file);
81
82    Ok((writer, contents))
83}
84
85pub fn split_and_parse<T: FromStr>(s: &str, delim: &str) -> anyhow::Result<Vec<T>>
86where
87    <T as FromStr>::Err: std::error::Error + Send + Sync + 'static,
88{
89    let mut v = Vec::new();
90    for n in s.split(delim) {
91        v.push(n.parse()?);
92    }
93
94    Ok(v)
95}
96
97/// Adds generic type to usize. Can panic if the values are outside the range of the given types.
98///
99/// ```
100/// use aoclib_rs::usize_plus_i;
101///
102/// let i: i64 = 23;
103/// assert_eq!(usize_plus_i(5, i), 28);
104///
105/// let i: i64 = -2;
106/// assert_eq!(usize_plus_i(5, i), 3);
107///
108/// // let i: i64 = -8;
109/// // usize_plus_i(5, i);
110/// // panics: -3 is an invalid usize
111///
112/// // let i: i8 = 5;
113/// // usize_plus_i(12345, i);
114/// // panics: 12345 is an invalid i8
115/// ```
116pub fn usize_plus_i<T>(u: usize, i: T) -> usize
117where
118    T: TryFrom<usize, Error: fmt::Debug> + ops::Add<Output = T>,
119    usize: TryFrom<T, Error: fmt::Debug>,
120{
121    usize::try_from(T::try_from(u).unwrap() + i).unwrap()
122}
123
124pub fn u8_to_string(c: u8) -> String {
125    String::from_utf8(vec![c]).unwrap()
126}
127
128// this function is pretty silly and new code should probably just use s.chars().
129pub fn split_by_char(s: &str) -> Vec<&str> {
130    s.split("").filter(|c| !c.is_empty()).collect()
131}
132
133/// Given 2-dimensional data and a predicate, `position_2d()` returns the position (ie, the 2D
134/// indices) of where the predicate first returned `true`, or `None` if the predicate never returns
135/// `true`. The input data is interpreted as follows:
136///
137/// `vec![vec![1, 2, 3], vec![4, 5, 6]]` is
138///
139/// 1 2 3
140/// 4 5 6
141///
142/// That is, the indices of the `3` are `(2, 0)` - in `(x, y)` form.
143///
144/// Iteration order is left-to-right, top-to-bottom.
145///
146/// ```
147/// let v = vec![vec![1, 2, 3], vec![4, 5, 6]];
148/// assert_eq!(aoclib_rs::position_2d(&v, |&i| i == 3), Some((2, 0)));
149/// ```
150pub fn position_2d<T, P>(v: &[Vec<T>], mut predicate: P) -> Option<(usize, usize)>
151where
152    P: FnMut(&T) -> bool,
153{
154    for (y, row) in v.iter().enumerate() {
155        if let Some(x) = row.iter().position(&mut predicate) {
156            return Some((x, y));
157        }
158    }
159
160    None
161}
162
163// Euclidean Algorithm https://en.wikipedia.org/wiki/Greatest_common_divisor#Euclidean_algorithm
164/// Greatest common divisor. Behaviour when calling with negative numbers can be unintuitive; refer
165/// to examples below.
166///
167/// ```
168/// assert_eq!(aoclib_rs::gcd(8, 12), 4);
169/// assert_eq!(aoclib_rs::gcd(-8, -12), -4);
170/// assert_eq!(aoclib_rs::gcd(-8, 12), 4);
171/// assert_eq!(aoclib_rs::gcd(8, -12), -4);
172/// ```
173pub fn gcd<T>(a: T, b: T) -> T
174where
175    T: Copy + ops::Rem<Output = T> + PartialEq + Zero,
176{
177    if b == T::zero() {
178        return a;
179    }
180
181    gcd(b, a % b)
182}
183
184/// Increments a "selector" slice of booleans. Essentially treats the slice as a binary number and
185/// increments it. Returns `true` if the input is already all true and doesn't increment. Returns
186/// `false` and increments otherwise.
187///
188/// ```
189/// use aoclib_rs::inc_selector;
190///
191/// let mut v = [true, false, false, true, true];
192/// assert_eq!(inc_selector(&mut v), false);
193/// assert_eq!(v, [true, false, true, false, false]);
194///
195/// let mut v = [true, true, true, true, true];
196/// assert_eq!(inc_selector(&mut v), true);
197/// assert_eq!(v, [true, true, true, true, true]);
198/// ```
199pub fn inc_selector(v: &mut [bool]) -> bool {
200    if v.iter().map(|&b| if b { 1 } else { 0 }).sum::<usize>() == v.len() {
201        return true;
202    }
203
204    let mut last = v.len() - 1;
205    loop {
206        v[last] = !v[last];
207
208        if v[last] {
209            break;
210        }
211
212        match last.checked_sub(1) {
213            None => break,
214            Some(l) => last = l,
215        }
216    }
217
218    false
219}
220
221/// Essentially a short-circuiting version of `Iterator::fold()` (see
222/// <https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.fold>). The only difference
223/// between this and `Iterator::fold()` is that the closure in this function returns two values: the
224/// accumulator value and a boolean. If the boolean returned is `true`, `fold_while()` will continue
225/// iterating, otherwise it will terminate early.
226///
227/// ```
228/// assert_eq!(
229///     // Sums items, but exits after item `3`.
230///     aoclib_rs::fold_while([1, 2, 3, 4, 5].iter(), 0, |acc, &item| (
231///         acc + item,
232///         item != 3
233///     )),
234///     6
235/// );
236/// ```
237pub fn fold_while<I, B, F>(it: I, init: B, mut f: F) -> B
238where
239    I: Iterator,
240    F: FnMut(B, I::Item) -> (B, bool),
241{
242    let mut b = init;
243    for v in it {
244        let (new_b, should_continue) = f(b, v);
245        b = new_b;
246        if !should_continue {
247            break;
248        }
249    }
250    b
251}
252
253/// Get the absolute value for generic types.
254///
255/// ```
256/// use aoclib_rs::abs;
257/// assert_eq!(abs(3), 3);
258/// assert_eq!(abs(0), 0);
259/// assert_eq!(abs(-5), 5);
260/// assert_eq!(abs(3.0), 3.0);
261/// assert_eq!(abs(0.0), 0.0);
262/// assert_eq!(abs(-5.0), 5.0);
263/// ```
264pub fn abs<T>(i: T) -> T
265where
266    T: PartialOrd + ops::Sub<Output = T> + Zero,
267{
268    if i < T::zero() { T::zero() - i } else { i }
269}