diff --git a/README.md b/README.md index 303ac95..70639a1 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ The CSES environment is defined [here](https://cses.fi/howto/). At time of writi `cargo install` should get you started. -## running a problem +## running a problem Since only single-file submissions are accepted on CSES, everything gets stored in the `src/bin` directory per [Cargo conventions](https://doc.rust-lang.org/cargo/reference/cargo-targets.html?highlight=src%2Fbin#binaries). You can then run your file with `cargo run --bin < `. (Add the `--release` flag if testing for performance.) @@ -23,6 +23,10 @@ To save yourself from having to write the same I/O code for every single problem ## design decisions - Competitive programming in general de-emphasizes error handling, particularly regarding stdin and stdout. It's always assumed that stdin will match the constraints, focus on speed and clarity over safety here. In a real world application, obviously don't neglect safety. + - All entries on CSES use ASCII, so you can optimize slightly by skipping Rust's UTF-8 checks. DON'T do this in a real-world problem. + - Since CSES does not seem to have interactive problems (from what I've seen), and since input size doesn't seem to go over 2MB, you can read all stdin at once to minimize I/O calls. Additionally, you can get away with just parsing generic whitespace instead of handling newlines as a special case; you can predict what the next line will be either from the problem statement or from the first "tokens" parsed, so there's never really a reason to read line-by-line. In a real-world application, you'll probably be processing potentially arbitrary files, so you'd probably want to make use of one of the `BufRead` trait's functions instead. + - Similarly, you definitely don't want to be calling `unwrapped_unchecked()` at any point in a real-world application during I/O. Handle the `Err` from the Result properly. + - In certain cases, you can allocate huge arrays on the stack, though be warned that you have a limit of 2MB! - Strong typing and powerful compilers are great at catching errors as you're writing the code, instead of after you've run the code. - Be concise in how much code is written. Minimal expressions and statements have an appealing aesthetic on their own. - General goal is to be among the fastest applications, but not _the_ fastest. **NOTE**: in proper competitive programming environments, you really should try to be the fastest (in both code completion speed and execution speed). @@ -31,6 +35,10 @@ To save yourself from having to write the same I/O code for every single problem - I try to use functional-style programming (using Rust iterator patterns) as much as possible, but sometimes imperative programming (or, rarely, OO programming) is just the best way to solve a problem. - Don't cheat with lookup tables (or small lookup tables which could potentially be invalidated by a new submission under the listed constraints). Obviously, there are many problems where that's the fastest case, and in a real-world application you would of course use the pre-computed values. But where's the fun in that? +## linting + +`cargo clippy -- -Wclippy::pedantic` should catch all of the important lints. + ## testing Run `cargo test`, all files should have basic unit tests by default. Note that successful test execution does not guarantee an accepted code submission on CSES, as there's a maximum time limit (usually 1 second) and memory usage. @@ -42,3 +50,4 @@ Note that these tests are NOT representative of the actual test cases on CSES - ## Credits - [EbTech](https://github.com/EbTech/rust-algorithms/commit/6198cf16f667859ca60babb4b2264b9b9d039ade) : scanner boilerplate, well-designed algorithm implementations +- [The Rust Performance Book](https://nnethercote.github.io/perf-book/introduction.html) - some great tips on how to improve performance diff --git a/src/bin/0_cses_template.rs b/src/bin/0_cses_template.rs index adc2e48..f0b73b8 100644 --- a/src/bin/0_cses_template.rs +++ b/src/bin/0_cses_template.rs @@ -1,18 +1,26 @@ // I/O boilerplate // -pub struct UnsafeScanner { - reader: R, +pub struct UnsafeScanner { + // not actually dead code, needed for buf_iter to work + #[allow(dead_code)] buf_str: Vec, buf_iter: std::str::SplitAsciiWhitespace<'static>, } -impl UnsafeScanner { - pub fn new(reader: R) -> Self { - Self { - reader, - buf_str: vec![], - buf_iter: "".split_ascii_whitespace(), +impl UnsafeScanner { + pub fn new(mut reader: R) -> Self { + let mut buf_str = vec![]; + unsafe { + reader.read_to_end(&mut buf_str).unwrap_unchecked(); } + let buf_iter = unsafe { + let slice = std::str::from_utf8_unchecked(&buf_str); + std::mem::transmute(slice.split_ascii_whitespace()) + }; + // optional memory clear + buf_str.clear(); + + Self { buf_str, buf_iter } } /// Use "turbofish" syntax `token::()` to select data type of next token. @@ -20,18 +28,12 @@ impl UnsafeScanner { /// # Panics /// Panics if there's an I/O error or if the token cannot be parsed as T. pub fn token(&mut self) -> T { - loop { - if let Some(token) = self.buf_iter.next() { - return token.parse().ok().expect("Failed parse"); - } - self.buf_str.clear(); - self.reader - .read_until(b'\n', &mut self.buf_str) - .expect("Failed read"); - self.buf_iter = unsafe { - let slice = std::str::from_utf8_unchecked(&self.buf_str); - std::mem::transmute(slice.split_ascii_whitespace()) - } + unsafe { + self.buf_iter + .next() + .unwrap_unchecked() + .parse() + .unwrap_unchecked() } } } @@ -53,7 +55,7 @@ impl UnsafeScanner { ///
    ///
  • −106 ≤ A,B ≤ 106
  • ///
-fn solve(scan: &mut UnsafeScanner, out: &mut W) { +fn solve(mut scan: UnsafeScanner, out: &mut W) { let a = scan.token::(); let b = scan.token::(); writeln!(out, "{}", a + b).ok(); @@ -62,9 +64,9 @@ fn solve(scan: &mut UnsafeScanner, ou // entrypoints // fn main() { - let mut scan = UnsafeScanner::new(std::io::stdin().lock()); + let scan = UnsafeScanner::new(std::io::stdin().lock()); let mut out = std::io::BufWriter::new(std::io::stdout().lock()); - solve(&mut scan, &mut out); + solve(scan, &mut out); } #[cfg(test)] @@ -72,9 +74,9 @@ mod test { use super::*; fn test(input: &[u8], target: &[u8]) { - let mut scan = UnsafeScanner::new(input); + let scan = UnsafeScanner::new(input); let mut out = Vec::with_capacity(target.len()); - solve(&mut scan, &mut out); + solve(scan, &mut out); assert_eq!(out, target); } diff --git a/src/bin/dynamic_coin_combinations_1.rs b/src/bin/dynamic_coin_combinations_1.rs index 4d15853..5d3ee34 100644 --- a/src/bin/dynamic_coin_combinations_1.rs +++ b/src/bin/dynamic_coin_combinations_1.rs @@ -1,18 +1,26 @@ // I/O boilerplate // -pub struct UnsafeScanner { - reader: R, +pub struct UnsafeScanner { + // not actually dead code, needed for buf_iter to work + #[allow(dead_code)] buf_str: Vec, buf_iter: std::str::SplitAsciiWhitespace<'static>, } -impl UnsafeScanner { - pub fn new(reader: R) -> Self { - Self { - reader, - buf_str: vec![], - buf_iter: "".split_ascii_whitespace(), +impl UnsafeScanner { + pub fn new(mut reader: R) -> Self { + let mut buf_str = vec![]; + unsafe { + reader.read_to_end(&mut buf_str).unwrap_unchecked(); } + let buf_iter = unsafe { + let slice = std::str::from_utf8_unchecked(&buf_str); + std::mem::transmute(slice.split_ascii_whitespace()) + }; + // optional memory clear + buf_str.clear(); + + Self { buf_str, buf_iter } } /// Use "turbofish" syntax `token::()` to select data type of next token. @@ -20,18 +28,12 @@ impl UnsafeScanner { /// # Panics /// Panics if there's an I/O error or if the token cannot be parsed as T. pub fn token(&mut self) -> T { - loop { - if let Some(token) = self.buf_iter.next() { - return token.parse().ok().expect("Failed parse"); - } - self.buf_str.clear(); - self.reader - .read_until(b'\n', &mut self.buf_str) - .expect("Failed read"); - self.buf_iter = unsafe { - let slice = std::str::from_utf8_unchecked(&self.buf_str); - std::mem::transmute(slice.split_ascii_whitespace()) - } + unsafe { + self.buf_iter + .next() + .unwrap_unchecked() + .parse() + .unwrap_unchecked() } } } @@ -71,7 +73,7 @@ const MODULO: u64 = 1_000_000_007; ///
  • 1 ≤ x ≤ 106
  • ///
  • 1 ≤ ci ≤ 106
  • /// -fn solve(scan: &mut UnsafeScanner, out: &mut W) { +fn solve(mut scan: UnsafeScanner, out: &mut W) { let capacity: u8 = scan.token(); let target: usize = scan.token(); let mut coins: Vec = (0..capacity).map(|_| scan.token::()).collect(); @@ -94,9 +96,9 @@ fn solve(scan: &mut UnsafeScanner, ou // entrypoints // fn main() { - let mut scan = UnsafeScanner::new(std::io::stdin().lock()); + let scan = UnsafeScanner::new(std::io::stdin().lock()); let mut out = std::io::BufWriter::new(std::io::stdout().lock()); - solve(&mut scan, &mut out); + solve(scan, &mut out); } #[cfg(test)] @@ -104,9 +106,9 @@ mod test { use super::*; fn test(input: &[u8], target: &[u8]) { - let mut scan = UnsafeScanner::new(input); + let scan = UnsafeScanner::new(input); let mut out = Vec::with_capacity(target.len()); - solve(&mut scan, &mut out); + solve(scan, &mut out); assert_eq!(out, target); } diff --git a/src/bin/dynamic_dice_combinations.rs b/src/bin/dynamic_dice_combinations.rs index 5f99e8f..cd5ee4c 100644 --- a/src/bin/dynamic_dice_combinations.rs +++ b/src/bin/dynamic_dice_combinations.rs @@ -1,18 +1,26 @@ // I/O boilerplate // -pub struct UnsafeScanner { - reader: R, +pub struct UnsafeScanner { + // not actually dead code, needed for buf_iter to work + #[allow(dead_code)] buf_str: Vec, buf_iter: std::str::SplitAsciiWhitespace<'static>, } -impl UnsafeScanner { - pub fn new(reader: R) -> Self { - Self { - reader, - buf_str: vec![], - buf_iter: "".split_ascii_whitespace(), +impl UnsafeScanner { + pub fn new(mut reader: R) -> Self { + let mut buf_str = vec![]; + unsafe { + reader.read_to_end(&mut buf_str).unwrap_unchecked(); } + let buf_iter = unsafe { + let slice = std::str::from_utf8_unchecked(&buf_str); + std::mem::transmute(slice.split_ascii_whitespace()) + }; + // optional memory clear + buf_str.clear(); + + Self { buf_str, buf_iter } } /// Use "turbofish" syntax `token::()` to select data type of next token. @@ -20,18 +28,12 @@ impl UnsafeScanner { /// # Panics /// Panics if there's an I/O error or if the token cannot be parsed as T. pub fn token(&mut self) -> T { - loop { - if let Some(token) = self.buf_iter.next() { - return token.parse().ok().expect("Failed parse"); - } - self.buf_str.clear(); - self.reader - .read_until(b'\n', &mut self.buf_str) - .expect("Failed read"); - self.buf_iter = unsafe { - let slice = std::str::from_utf8_unchecked(&self.buf_str); - std::mem::transmute(slice.split_ascii_whitespace()) - } + unsafe { + self.buf_iter + .next() + .unwrap_unchecked() + .parse() + .unwrap_unchecked() } } } @@ -65,7 +67,7 @@ type Matrix = [[usize; 6]; 6]; ///
      ///
    • 1 ≤ n ≤ 106
    • ///
    -fn solve(scan: &mut UnsafeScanner, out: &mut W) { +fn solve(mut scan: UnsafeScanner, out: &mut W) { let mut exponent = scan.token::(); let mut base: Matrix = [ @@ -112,9 +114,9 @@ fn multiply_matrix(a: &Matrix, b: &Matrix) -> Matrix { // entrypoints // fn main() { - let mut scan = UnsafeScanner::new(std::io::stdin().lock()); + let scan = UnsafeScanner::new(std::io::stdin().lock()); let mut out = std::io::BufWriter::new(std::io::stdout().lock()); - solve(&mut scan, &mut out); + solve(scan, &mut out); } #[cfg(test)] @@ -122,9 +124,9 @@ mod test { use super::*; fn test(input: &[u8], target: &[u8]) { - let mut scan = UnsafeScanner::new(input); + let scan = UnsafeScanner::new(input); let mut out = Vec::with_capacity(target.len()); - solve(&mut scan, &mut out); + solve(scan, &mut out); assert_eq!(out, target); } diff --git a/src/bin/dynamic_minimizing_coins.rs b/src/bin/dynamic_minimizing_coins.rs index 77b3308..fb67158 100644 --- a/src/bin/dynamic_minimizing_coins.rs +++ b/src/bin/dynamic_minimizing_coins.rs @@ -1,18 +1,26 @@ // I/O boilerplate // -pub struct UnsafeScanner { - reader: R, +pub struct UnsafeScanner { + // not actually dead code, needed for buf_iter to work + #[allow(dead_code)] buf_str: Vec, buf_iter: std::str::SplitAsciiWhitespace<'static>, } -impl UnsafeScanner { - pub fn new(reader: R) -> Self { - Self { - reader, - buf_str: vec![], - buf_iter: "".split_ascii_whitespace(), +impl UnsafeScanner { + pub fn new(mut reader: R) -> Self { + let mut buf_str = vec![]; + unsafe { + reader.read_to_end(&mut buf_str).unwrap_unchecked(); } + let buf_iter = unsafe { + let slice = std::str::from_utf8_unchecked(&buf_str); + std::mem::transmute(slice.split_ascii_whitespace()) + }; + // optional memory clear + buf_str.clear(); + + Self { buf_str, buf_iter } } /// Use "turbofish" syntax `token::()` to select data type of next token. @@ -20,18 +28,12 @@ impl UnsafeScanner { /// # Panics /// Panics if there's an I/O error or if the token cannot be parsed as T. pub fn token(&mut self) -> T { - loop { - if let Some(token) = self.buf_iter.next() { - return token.parse().ok().expect("Failed parse"); - } - self.buf_str.clear(); - self.reader - .read_until(b'\n', &mut self.buf_str) - .expect("Failed read"); - self.buf_iter = unsafe { - let slice = std::str::from_utf8_unchecked(&self.buf_str); - std::mem::transmute(slice.split_ascii_whitespace()) - } + unsafe { + self.buf_iter + .next() + .unwrap_unchecked() + .parse() + .unwrap_unchecked() } } } @@ -61,7 +63,7 @@ const MODULO: u32 = 1_000_000_007; ///
  • 1 ≤ x ≤ 106
  • ///
  • 1 ≤ ci ≤ 106
  • /// -fn solve(scan: &mut UnsafeScanner, out: &mut W) { +fn solve(mut scan: UnsafeScanner, out: &mut W) { let capacity: u8 = scan.token(); let target: usize = scan.token(); @@ -84,9 +86,9 @@ fn solve(scan: &mut UnsafeScanner, ou // entrypoints // fn main() { - let mut scan = UnsafeScanner::new(std::io::stdin().lock()); + let scan = UnsafeScanner::new(std::io::stdin().lock()); let mut out = std::io::BufWriter::new(std::io::stdout().lock()); - solve(&mut scan, &mut out); + solve(scan, &mut out); } #[cfg(test)] @@ -94,9 +96,9 @@ mod test { use super::*; fn test(input: &[u8], target: &[u8]) { - let mut scan = UnsafeScanner::new(input); + let scan = UnsafeScanner::new(input); let mut out = Vec::with_capacity(target.len()); - solve(&mut scan, &mut out); + solve(scan, &mut out); assert_eq!(out, target); } diff --git a/src/bin/dynamic_removing_digits.rs b/src/bin/dynamic_removing_digits.rs index 82dfec2..18f615d 100644 --- a/src/bin/dynamic_removing_digits.rs +++ b/src/bin/dynamic_removing_digits.rs @@ -1,18 +1,26 @@ // I/O boilerplate // -pub struct UnsafeScanner { - reader: R, +pub struct UnsafeScanner { + // not actually dead code, needed for buf_iter to work + #[allow(dead_code)] buf_str: Vec, buf_iter: std::str::SplitAsciiWhitespace<'static>, } -impl UnsafeScanner { - pub fn new(reader: R) -> Self { - Self { - reader, - buf_str: vec![], - buf_iter: "".split_ascii_whitespace(), +impl UnsafeScanner { + pub fn new(mut reader: R) -> Self { + let mut buf_str = vec![]; + unsafe { + reader.read_to_end(&mut buf_str).unwrap_unchecked(); } + let buf_iter = unsafe { + let slice = std::str::from_utf8_unchecked(&buf_str); + std::mem::transmute(slice.split_ascii_whitespace()) + }; + // optional memory clear + buf_str.clear(); + + Self { buf_str, buf_iter } } /// Use "turbofish" syntax `token::()` to select data type of next token. @@ -20,18 +28,12 @@ impl UnsafeScanner { /// # Panics /// Panics if there's an I/O error or if the token cannot be parsed as T. pub fn token(&mut self) -> T { - loop { - if let Some(token) = self.buf_iter.next() { - return token.parse().ok().expect("Failed parse"); - } - self.buf_str.clear(); - self.reader - .read_until(b'\n', &mut self.buf_str) - .expect("Failed read"); - self.buf_iter = unsafe { - let slice = std::str::from_utf8_unchecked(&self.buf_str); - std::mem::transmute(slice.split_ascii_whitespace()) - } + unsafe { + self.buf_iter + .next() + .unwrap_unchecked() + .parse() + .unwrap_unchecked() } } } @@ -58,7 +60,7 @@ impl UnsafeScanner { /// /// For "27", one optimal solution is 27→20→18→10→9→0. /// -fn solve(scan: &mut UnsafeScanner, out: &mut W) { +fn solve(mut scan: UnsafeScanner, out: &mut W) { let mut target = scan.token::(); let mut counter = 0_u32; @@ -81,9 +83,9 @@ fn solve(scan: &mut UnsafeScanner, ou // entrypoints // fn main() { - let mut scan = UnsafeScanner::new(std::io::stdin().lock()); + let scan = UnsafeScanner::new(std::io::stdin().lock()); let mut out = std::io::BufWriter::new(std::io::stdout().lock()); - solve(&mut scan, &mut out); + solve(scan, &mut out); } #[cfg(test)] @@ -91,9 +93,9 @@ mod test { use super::*; fn test(input: &[u8], target: &[u8]) { - let mut scan = UnsafeScanner::new(input); + let scan = UnsafeScanner::new(input); let mut out = Vec::with_capacity(target.len()); - solve(&mut scan, &mut out); + solve(scan, &mut out); assert_eq!(out, target); } diff --git a/src/bin/graph_counting_rooms.rs b/src/bin/graph_counting_rooms.rs index 6e689e3..4cdb149 100644 --- a/src/bin/graph_counting_rooms.rs +++ b/src/bin/graph_counting_rooms.rs @@ -1,18 +1,26 @@ // I/O boilerplate // -pub struct UnsafeScanner { - reader: R, +pub struct UnsafeScanner { + // not actually dead code, needed for buf_iter to work + #[allow(dead_code)] buf_str: Vec, buf_iter: std::str::SplitAsciiWhitespace<'static>, } -impl UnsafeScanner { - pub fn new(reader: R) -> Self { - Self { - reader, - buf_str: vec![], - buf_iter: "".split_ascii_whitespace(), +impl UnsafeScanner { + pub fn new(mut reader: R) -> Self { + let mut buf_str = vec![]; + unsafe { + reader.read_to_end(&mut buf_str).unwrap_unchecked(); } + let buf_iter = unsafe { + let slice = std::str::from_utf8_unchecked(&buf_str); + std::mem::transmute(slice.split_ascii_whitespace()) + }; + // optional memory clear + buf_str.clear(); + + Self { buf_str, buf_iter } } /// Use "turbofish" syntax `token::()` to select data type of next token. @@ -20,18 +28,12 @@ impl UnsafeScanner { /// # Panics /// Panics if there's an I/O error or if the token cannot be parsed as T. pub fn token(&mut self) -> T { - loop { - if let Some(token) = self.buf_iter.next() { - return token.parse().ok().expect("Failed parse"); - } - self.buf_str.clear(); - self.reader - .read_until(b'\n', &mut self.buf_str) - .expect("Failed read"); - self.buf_iter = unsafe { - let slice = std::str::from_utf8_unchecked(&self.buf_str); - std::mem::transmute(slice.split_ascii_whitespace()) - } + unsafe { + self.buf_iter + .next() + .unwrap_unchecked() + .parse() + .unwrap_unchecked() } } } @@ -55,7 +57,7 @@ impl UnsafeScanner { ///
      ///
    • 1 ≤ n,m ≤ 1000
    • ///
    -fn solve(scan: &mut UnsafeScanner, out: &mut W) { +fn solve(mut scan: UnsafeScanner, out: &mut W) { let height: usize = scan.token(); let width: usize = scan.token(); @@ -125,9 +127,9 @@ fn solve(scan: &mut UnsafeScanner, ou // entrypoints // fn main() { - let mut scan = UnsafeScanner::new(std::io::stdin().lock()); + let scan = UnsafeScanner::new(std::io::stdin().lock()); let mut out = std::io::BufWriter::new(std::io::stdout().lock()); - solve(&mut scan, &mut out); + solve(scan, &mut out); } #[cfg(test)] @@ -135,9 +137,9 @@ mod test { use super::*; fn test(input: &[u8], target: &[u8]) { - let mut scan = UnsafeScanner::new(input); + let scan = UnsafeScanner::new(input); let mut out = Vec::with_capacity(target.len()); - solve(&mut scan, &mut out); + solve(scan, &mut out); assert_eq!(out, target); } diff --git a/src/bin/intro_bit_strings.rs b/src/bin/intro_bit_strings.rs index 9510589..fd3a648 100644 --- a/src/bin/intro_bit_strings.rs +++ b/src/bin/intro_bit_strings.rs @@ -1,18 +1,26 @@ // I/O boilerplate // -pub struct UnsafeScanner { - reader: R, +pub struct UnsafeScanner { + // not actually dead code, needed for buf_iter to work + #[allow(dead_code)] buf_str: Vec, buf_iter: std::str::SplitAsciiWhitespace<'static>, } -impl UnsafeScanner { - pub fn new(reader: R) -> Self { - Self { - reader, - buf_str: vec![], - buf_iter: "".split_ascii_whitespace(), +impl UnsafeScanner { + pub fn new(mut reader: R) -> Self { + let mut buf_str = vec![]; + unsafe { + reader.read_to_end(&mut buf_str).unwrap_unchecked(); } + let buf_iter = unsafe { + let slice = std::str::from_utf8_unchecked(&buf_str); + std::mem::transmute(slice.split_ascii_whitespace()) + }; + // optional memory clear + buf_str.clear(); + + Self { buf_str, buf_iter } } /// Use "turbofish" syntax `token::()` to select data type of next token. @@ -20,18 +28,12 @@ impl UnsafeScanner { /// # Panics /// Panics if there's an I/O error or if the token cannot be parsed as T. pub fn token(&mut self) -> T { - loop { - if let Some(token) = self.buf_iter.next() { - return token.parse().ok().expect("Failed parse"); - } - self.buf_str.clear(); - self.reader - .read_until(b'\n', &mut self.buf_str) - .expect("Failed read"); - self.buf_iter = unsafe { - let slice = std::str::from_utf8_unchecked(&self.buf_str); - std::mem::transmute(slice.split_ascii_whitespace()) - } + unsafe { + self.buf_iter + .next() + .unwrap_unchecked() + .parse() + .unwrap_unchecked() } } } @@ -57,7 +59,7 @@ const MODULO: u64 = 1_000_000_007; ///
      ///
    • 1 ≤ n ≤ 106
    • ///
    -fn solve(scan: &mut UnsafeScanner, out: &mut W) { +fn solve(mut scan: UnsafeScanner, out: &mut W) { let mut exponent = scan.token::(); let mut goal: u64 = 1; @@ -77,9 +79,9 @@ fn solve(scan: &mut UnsafeScanner, ou // entrypoints // fn main() { - let mut scan = UnsafeScanner::new(std::io::stdin().lock()); + let scan = UnsafeScanner::new(std::io::stdin().lock()); let mut out = std::io::BufWriter::new(std::io::stdout().lock()); - solve(&mut scan, &mut out); + solve(scan, &mut out); } #[cfg(test)] @@ -87,9 +89,9 @@ mod test { use super::*; fn test(input: &[u8], target: &[u8]) { - let mut scan = UnsafeScanner::new(input); + let scan = UnsafeScanner::new(input); let mut out = Vec::with_capacity(target.len()); - solve(&mut scan, &mut out); + solve(scan, &mut out); assert_eq!(out, target); } diff --git a/src/bin/intro_chessboard_and_queens.rs b/src/bin/intro_chessboard_and_queens.rs index fed359e..4b5563b 100644 --- a/src/bin/intro_chessboard_and_queens.rs +++ b/src/bin/intro_chessboard_and_queens.rs @@ -1,18 +1,26 @@ // I/O boilerplate // -pub struct UnsafeScanner { - reader: R, +pub struct UnsafeScanner { + // not actually dead code, needed for buf_iter to work + #[allow(dead_code)] buf_str: Vec, buf_iter: std::str::SplitAsciiWhitespace<'static>, } -impl UnsafeScanner { - pub fn new(reader: R) -> Self { - Self { - reader, - buf_str: vec![], - buf_iter: "".split_ascii_whitespace(), +impl UnsafeScanner { + pub fn new(mut reader: R) -> Self { + let mut buf_str = vec![]; + unsafe { + reader.read_to_end(&mut buf_str).unwrap_unchecked(); } + let buf_iter = unsafe { + let slice = std::str::from_utf8_unchecked(&buf_str); + std::mem::transmute(slice.split_ascii_whitespace()) + }; + // optional memory clear + buf_str.clear(); + + Self { buf_str, buf_iter } } /// Use "turbofish" syntax `token::()` to select data type of next token. @@ -20,18 +28,12 @@ impl UnsafeScanner { /// # Panics /// Panics if there's an I/O error or if the token cannot be parsed as T. pub fn token(&mut self) -> T { - loop { - if let Some(token) = self.buf_iter.next() { - return token.parse().ok().expect("Failed parse"); - } - self.buf_str.clear(); - self.reader - .read_until(b'\n', &mut self.buf_str) - .expect("Failed read"); - self.buf_iter = unsafe { - let slice = std::str::from_utf8_unchecked(&self.buf_str); - std::mem::transmute(slice.split_ascii_whitespace()) - } + unsafe { + self.buf_iter + .next() + .unwrap_unchecked() + .parse() + .unwrap_unchecked() } } } @@ -51,7 +53,7 @@ const COLUMNS: usize = 8; /// Output /// /// Print one integer: the number of ways you can place the queens. -fn solve(scan: &mut UnsafeScanner, out: &mut W) { +fn solve(mut scan: UnsafeScanner, out: &mut W) { let mut chessboard = [0; COLUMNS * COLUMNS]; for (idx, row) in (0..COLUMNS).map(|idx| (idx, scan.token::().into_bytes())) { chessboard[COLUMNS * idx..COLUMNS * (idx + 1)].copy_from_slice(&row); @@ -117,9 +119,9 @@ fn recurse( // entrypoints // fn main() { - let mut scan = UnsafeScanner::new(std::io::stdin().lock()); + let scan = UnsafeScanner::new(std::io::stdin().lock()); let mut out = std::io::BufWriter::new(std::io::stdout().lock()); - solve(&mut scan, &mut out); + solve(scan, &mut out); } #[cfg(test)] @@ -127,9 +129,9 @@ mod test { use super::*; fn test(input: &[u8], target: &[u8]) { - let mut scan = UnsafeScanner::new(input); + let scan = UnsafeScanner::new(input); let mut out = Vec::with_capacity(target.len()); - solve(&mut scan, &mut out); + solve(scan, &mut out); assert_eq!(out, target); } diff --git a/src/bin/intro_coin_piles.rs b/src/bin/intro_coin_piles.rs index 9884a2a..a166dd6 100644 --- a/src/bin/intro_coin_piles.rs +++ b/src/bin/intro_coin_piles.rs @@ -1,18 +1,26 @@ // I/O boilerplate // -pub struct UnsafeScanner { - reader: R, +pub struct UnsafeScanner { + // not actually dead code, needed for buf_iter to work + #[allow(dead_code)] buf_str: Vec, buf_iter: std::str::SplitAsciiWhitespace<'static>, } -impl UnsafeScanner { - pub fn new(reader: R) -> Self { - Self { - reader, - buf_str: vec![], - buf_iter: "".split_ascii_whitespace(), +impl UnsafeScanner { + pub fn new(mut reader: R) -> Self { + let mut buf_str = vec![]; + unsafe { + reader.read_to_end(&mut buf_str).unwrap_unchecked(); } + let buf_iter = unsafe { + let slice = std::str::from_utf8_unchecked(&buf_str); + std::mem::transmute(slice.split_ascii_whitespace()) + }; + // optional memory clear + buf_str.clear(); + + Self { buf_str, buf_iter } } /// Use "turbofish" syntax `token::()` to select data type of next token. @@ -20,18 +28,12 @@ impl UnsafeScanner { /// # Panics /// Panics if there's an I/O error or if the token cannot be parsed as T. pub fn token(&mut self) -> T { - loop { - if let Some(token) = self.buf_iter.next() { - return token.parse().ok().expect("Failed parse"); - } - self.buf_str.clear(); - self.reader - .read_until(b'\n', &mut self.buf_str) - .expect("Failed read"); - self.buf_iter = unsafe { - let slice = std::str::from_utf8_unchecked(&self.buf_str); - std::mem::transmute(slice.split_ascii_whitespace()) - } + unsafe { + self.buf_iter + .next() + .unwrap_unchecked() + .parse() + .unwrap_unchecked() } } } @@ -58,7 +60,7 @@ impl UnsafeScanner { ///
  • 1 ≤ t ≤ 105
  • ///
  • 0 ≤ a,b ≤ 109
  • /// -fn solve(scan: &mut UnsafeScanner, out: &mut W) { +fn solve(mut scan: UnsafeScanner, out: &mut W) { let tests = scan.token::(); for _ in 0..tests { @@ -77,9 +79,9 @@ fn solve(scan: &mut UnsafeScanner, ou // entrypoints // fn main() { - let mut scan = UnsafeScanner::new(std::io::stdin().lock()); + let scan = UnsafeScanner::new(std::io::stdin().lock()); let mut out = std::io::BufWriter::new(std::io::stdout().lock()); - solve(&mut scan, &mut out); + solve(scan, &mut out); } #[cfg(test)] @@ -87,9 +89,9 @@ mod test { use super::*; fn test(input: &[u8], target: &[u8]) { - let mut scan = UnsafeScanner::new(input); + let scan = UnsafeScanner::new(input); let mut out = Vec::with_capacity(target.len()); - solve(&mut scan, &mut out); + solve(scan, &mut out); assert_eq!(out, target); } diff --git a/src/bin/intro_creating_strings.rs b/src/bin/intro_creating_strings.rs index 1e6552b..58aa61e 100644 --- a/src/bin/intro_creating_strings.rs +++ b/src/bin/intro_creating_strings.rs @@ -1,18 +1,26 @@ // I/O boilerplate // -pub struct UnsafeScanner { - reader: R, +pub struct UnsafeScanner { + // not actually dead code, needed for buf_iter to work + #[allow(dead_code)] buf_str: Vec, buf_iter: std::str::SplitAsciiWhitespace<'static>, } -impl UnsafeScanner { - pub fn new(reader: R) -> Self { - Self { - reader, - buf_str: vec![], - buf_iter: "".split_ascii_whitespace(), +impl UnsafeScanner { + pub fn new(mut reader: R) -> Self { + let mut buf_str = vec![]; + unsafe { + reader.read_to_end(&mut buf_str).unwrap_unchecked(); } + let buf_iter = unsafe { + let slice = std::str::from_utf8_unchecked(&buf_str); + std::mem::transmute(slice.split_ascii_whitespace()) + }; + // optional memory clear + buf_str.clear(); + + Self { buf_str, buf_iter } } /// Use "turbofish" syntax `token::()` to select data type of next token. @@ -20,18 +28,12 @@ impl UnsafeScanner { /// # Panics /// Panics if there's an I/O error or if the token cannot be parsed as T. pub fn token(&mut self) -> T { - loop { - if let Some(token) = self.buf_iter.next() { - return token.parse().ok().expect("Failed parse"); - } - self.buf_str.clear(); - self.reader - .read_until(b'\n', &mut self.buf_str) - .expect("Failed read"); - self.buf_iter = unsafe { - let slice = std::str::from_utf8_unchecked(&self.buf_str); - std::mem::transmute(slice.split_ascii_whitespace()) - } + unsafe { + self.buf_iter + .next() + .unwrap_unchecked() + .parse() + .unwrap_unchecked() } } } @@ -56,7 +58,7 @@ const MAX_CHARS: usize = 9 * 8 * 7 * 6 * 5 * 4 * 3 * 2; ///
      ///
    • 1 ≤ n ≤ 8
    • ///
    -fn solve(scan: &mut UnsafeScanner, out: &mut W) { +fn solve(mut scan: UnsafeScanner, out: &mut W) { let mut string = scan.token::().into_bytes(); string.sort_unstable(); @@ -98,9 +100,9 @@ fn next_permutation(slice: &mut [T]) -> bool { // entrypoints // fn main() { - let mut scan = UnsafeScanner::new(std::io::stdin().lock()); + let scan = UnsafeScanner::new(std::io::stdin().lock()); let mut out = std::io::BufWriter::new(std::io::stdout().lock()); - solve(&mut scan, &mut out); + solve(scan, &mut out); } #[cfg(test)] @@ -108,9 +110,9 @@ mod test { use super::*; fn test(input: &[u8], target: &[u8]) { - let mut scan = UnsafeScanner::new(input); + let scan = UnsafeScanner::new(input); let mut out = Vec::with_capacity(target.len()); - solve(&mut scan, &mut out); + solve(scan, &mut out); assert_eq!(out, target); } diff --git a/src/bin/intro_gray_code.rs b/src/bin/intro_gray_code.rs index 8262ce8..e0b6ae9 100644 --- a/src/bin/intro_gray_code.rs +++ b/src/bin/intro_gray_code.rs @@ -1,18 +1,26 @@ // I/O boilerplate // -pub struct UnsafeScanner { - reader: R, +pub struct UnsafeScanner { + // not actually dead code, needed for buf_iter to work + #[allow(dead_code)] buf_str: Vec, buf_iter: std::str::SplitAsciiWhitespace<'static>, } -impl UnsafeScanner { - pub fn new(reader: R) -> Self { - Self { - reader, - buf_str: vec![], - buf_iter: "".split_ascii_whitespace(), +impl UnsafeScanner { + pub fn new(mut reader: R) -> Self { + let mut buf_str = vec![]; + unsafe { + reader.read_to_end(&mut buf_str).unwrap_unchecked(); } + let buf_iter = unsafe { + let slice = std::str::from_utf8_unchecked(&buf_str); + std::mem::transmute(slice.split_ascii_whitespace()) + }; + // optional memory clear + buf_str.clear(); + + Self { buf_str, buf_iter } } /// Use "turbofish" syntax `token::()` to select data type of next token. @@ -20,18 +28,12 @@ impl UnsafeScanner { /// # Panics /// Panics if there's an I/O error or if the token cannot be parsed as T. pub fn token(&mut self) -> T { - loop { - if let Some(token) = self.buf_iter.next() { - return token.parse().ok().expect("Failed parse"); - } - self.buf_str.clear(); - self.reader - .read_until(b'\n', &mut self.buf_str) - .expect("Failed read"); - self.buf_iter = unsafe { - let slice = std::str::from_utf8_unchecked(&self.buf_str); - std::mem::transmute(slice.split_ascii_whitespace()) - } + unsafe { + self.buf_iter + .next() + .unwrap_unchecked() + .parse() + .unwrap_unchecked() } } } @@ -55,7 +57,7 @@ impl UnsafeScanner { ///
      ///
    • 1 ≤ A,B ≤ 16
    • ///
    -fn solve(scan: &mut UnsafeScanner, out: &mut W) { +fn solve(mut scan: UnsafeScanner, out: &mut W) { let exponent = scan.token::(); for num in 0..(1 << exponent) { @@ -66,9 +68,9 @@ fn solve(scan: &mut UnsafeScanner, ou // entrypoints // fn main() { - let mut scan = UnsafeScanner::new(std::io::stdin().lock()); + let scan = UnsafeScanner::new(std::io::stdin().lock()); let mut out = std::io::BufWriter::new(std::io::stdout().lock()); - solve(&mut scan, &mut out); + solve(scan, &mut out); } #[cfg(test)] @@ -76,9 +78,9 @@ mod test { use super::*; fn test(input: &[u8], target: &[u8]) { - let mut scan = UnsafeScanner::new(input); + let scan = UnsafeScanner::new(input); let mut out = Vec::with_capacity(target.len()); - solve(&mut scan, &mut out); + solve(scan, &mut out); assert_eq!(out, target); } diff --git a/src/bin/intro_increasing_array.rs b/src/bin/intro_increasing_array.rs index c887208..b04d044 100644 --- a/src/bin/intro_increasing_array.rs +++ b/src/bin/intro_increasing_array.rs @@ -1,18 +1,26 @@ // I/O boilerplate // -pub struct UnsafeScanner { - reader: R, +pub struct UnsafeScanner { + // not actually dead code, needed for buf_iter to work + #[allow(dead_code)] buf_str: Vec, buf_iter: std::str::SplitAsciiWhitespace<'static>, } -impl UnsafeScanner { - pub fn new(reader: R) -> Self { - Self { - reader, - buf_str: vec![], - buf_iter: "".split_ascii_whitespace(), +impl UnsafeScanner { + pub fn new(mut reader: R) -> Self { + let mut buf_str = vec![]; + unsafe { + reader.read_to_end(&mut buf_str).unwrap_unchecked(); } + let buf_iter = unsafe { + let slice = std::str::from_utf8_unchecked(&buf_str); + std::mem::transmute(slice.split_ascii_whitespace()) + }; + // optional memory clear + buf_str.clear(); + + Self { buf_str, buf_iter } } /// Use "turbofish" syntax `token::()` to select data type of next token. @@ -20,18 +28,12 @@ impl UnsafeScanner { /// # Panics /// Panics if there's an I/O error or if the token cannot be parsed as T. pub fn token(&mut self) -> T { - loop { - if let Some(token) = self.buf_iter.next() { - return token.parse().ok().expect("Failed parse"); - } - self.buf_str.clear(); - self.reader - .read_until(b'\n', &mut self.buf_str) - .expect("Failed read"); - self.buf_iter = unsafe { - let slice = std::str::from_utf8_unchecked(&self.buf_str); - std::mem::transmute(slice.split_ascii_whitespace()) - } + unsafe { + self.buf_iter + .next() + .unwrap_unchecked() + .parse() + .unwrap_unchecked() } } } @@ -58,7 +60,7 @@ impl UnsafeScanner { ///
  • 1 ≤ n ≤ 2 * 105
  • ///
  • 1 ≤ xi ≤ 109
  • /// -fn solve(scan: &mut UnsafeScanner, out: &mut W) { +fn solve(mut scan: UnsafeScanner, out: &mut W) { let size = scan.token::(); // we have to eagerly evaluate the first token of row 2 let first = scan.token::(); @@ -83,9 +85,9 @@ fn solve(scan: &mut UnsafeScanner, ou // entrypoints // fn main() { - let mut scan = UnsafeScanner::new(std::io::stdin().lock()); + let scan = UnsafeScanner::new(std::io::stdin().lock()); let mut out = std::io::BufWriter::new(std::io::stdout().lock()); - solve(&mut scan, &mut out); + solve(scan, &mut out); } #[cfg(test)] @@ -93,9 +95,9 @@ mod test { use super::*; fn test(input: &[u8], target: &[u8]) { - let mut scan = UnsafeScanner::new(input); + let scan = UnsafeScanner::new(input); let mut out = Vec::with_capacity(target.len()); - solve(&mut scan, &mut out); + solve(scan, &mut out); assert_eq!(out, target); } diff --git a/src/bin/intro_missing_number.rs b/src/bin/intro_missing_number.rs index ff1ccda..f541372 100644 --- a/src/bin/intro_missing_number.rs +++ b/src/bin/intro_missing_number.rs @@ -1,18 +1,26 @@ // I/O boilerplate // -pub struct UnsafeScanner { - reader: R, +pub struct UnsafeScanner { + // not actually dead code, needed for buf_iter to work + #[allow(dead_code)] buf_str: Vec, buf_iter: std::str::SplitAsciiWhitespace<'static>, } -impl UnsafeScanner { - pub fn new(reader: R) -> Self { - Self { - reader, - buf_str: vec![], - buf_iter: "".split_ascii_whitespace(), +impl UnsafeScanner { + pub fn new(mut reader: R) -> Self { + let mut buf_str = vec![]; + unsafe { + reader.read_to_end(&mut buf_str).unwrap_unchecked(); } + let buf_iter = unsafe { + let slice = std::str::from_utf8_unchecked(&buf_str); + std::mem::transmute(slice.split_ascii_whitespace()) + }; + // optional memory clear + buf_str.clear(); + + Self { buf_str, buf_iter } } /// Use "turbofish" syntax `token::()` to select data type of next token. @@ -20,18 +28,12 @@ impl UnsafeScanner { /// # Panics /// Panics if there's an I/O error or if the token cannot be parsed as T. pub fn token(&mut self) -> T { - loop { - if let Some(token) = self.buf_iter.next() { - return token.parse().ok().expect("Failed parse"); - } - self.buf_str.clear(); - self.reader - .read_until(b'\n', &mut self.buf_str) - .expect("Failed read"); - self.buf_iter = unsafe { - let slice = std::str::from_utf8_unchecked(&self.buf_str); - std::mem::transmute(slice.split_ascii_whitespace()) - } + unsafe { + self.buf_iter + .next() + .unwrap_unchecked() + .parse() + .unwrap_unchecked() } } } @@ -55,7 +57,7 @@ impl UnsafeScanner { ///
      ///
    • 2 ≤ n ≤ 2 * 105
    • ///
    -fn solve(scan: &mut UnsafeScanner, out: &mut W) { +fn solve(mut scan: UnsafeScanner, out: &mut W) { let upper_bound = scan.token::(); writeln!( out, @@ -68,9 +70,9 @@ fn solve(scan: &mut UnsafeScanner, ou // entrypoints // fn main() { - let mut scan = UnsafeScanner::new(std::io::stdin().lock()); + let scan = UnsafeScanner::new(std::io::stdin().lock()); let mut out = std::io::BufWriter::new(std::io::stdout().lock()); - solve(&mut scan, &mut out); + solve(scan, &mut out); } #[cfg(test)] @@ -78,9 +80,9 @@ mod test { use super::*; fn test(input: &[u8], target: &[u8]) { - let mut scan = UnsafeScanner::new(input); + let scan = UnsafeScanner::new(input); let mut out = Vec::with_capacity(target.len()); - solve(&mut scan, &mut out); + solve(scan, &mut out); assert_eq!(out, target); } diff --git a/src/bin/intro_number_spiral.rs b/src/bin/intro_number_spiral.rs index ebad275..9ef86e0 100644 --- a/src/bin/intro_number_spiral.rs +++ b/src/bin/intro_number_spiral.rs @@ -1,18 +1,26 @@ // I/O boilerplate // -pub struct UnsafeScanner { - reader: R, +pub struct UnsafeScanner { + // not actually dead code, needed for buf_iter to work + #[allow(dead_code)] buf_str: Vec, buf_iter: std::str::SplitAsciiWhitespace<'static>, } -impl UnsafeScanner { - pub fn new(reader: R) -> Self { - Self { - reader, - buf_str: vec![], - buf_iter: "".split_ascii_whitespace(), +impl UnsafeScanner { + pub fn new(mut reader: R) -> Self { + let mut buf_str = vec![]; + unsafe { + reader.read_to_end(&mut buf_str).unwrap_unchecked(); } + let buf_iter = unsafe { + let slice = std::str::from_utf8_unchecked(&buf_str); + std::mem::transmute(slice.split_ascii_whitespace()) + }; + // optional memory clear + buf_str.clear(); + + Self { buf_str, buf_iter } } /// Use "turbofish" syntax `token::()` to select data type of next token. @@ -20,18 +28,12 @@ impl UnsafeScanner { /// # Panics /// Panics if there's an I/O error or if the token cannot be parsed as T. pub fn token(&mut self) -> T { - loop { - if let Some(token) = self.buf_iter.next() { - return token.parse().ok().expect("Failed parse"); - } - self.buf_str.clear(); - self.reader - .read_until(b'\n', &mut self.buf_str) - .expect("Failed read"); - self.buf_iter = unsafe { - let slice = std::str::from_utf8_unchecked(&self.buf_str); - std::mem::transmute(slice.split_ascii_whitespace()) - } + unsafe { + self.buf_iter + .next() + .unwrap_unchecked() + .parse() + .unwrap_unchecked() } } } @@ -66,7 +68,7 @@ impl UnsafeScanner { ///
  • 1 ≤ t ≤ 105
  • ///
  • 1 ≤ y,x ≤ 109
  • /// -fn solve(scan: &mut UnsafeScanner, out: &mut W) { +fn solve(mut scan: UnsafeScanner, out: &mut W) { let t = scan.token::(); for _ in 0..t { @@ -92,9 +94,9 @@ fn solve(scan: &mut UnsafeScanner, ou // entrypoints // fn main() { - let mut scan = UnsafeScanner::new(std::io::stdin().lock()); + let scan = UnsafeScanner::new(std::io::stdin().lock()); let mut out = std::io::BufWriter::new(std::io::stdout().lock()); - solve(&mut scan, &mut out); + solve(scan, &mut out); } #[cfg(test)] @@ -102,9 +104,9 @@ mod test { use super::*; fn test(input: &[u8], target: &[u8]) { - let mut scan = UnsafeScanner::new(input); + let scan = UnsafeScanner::new(input); let mut out = Vec::with_capacity(target.len()); - solve(&mut scan, &mut out); + solve(scan, &mut out); assert_eq!(out, target); } diff --git a/src/bin/intro_palindrome_reorder.rs b/src/bin/intro_palindrome_reorder.rs index 19ade48..19d9e10 100644 --- a/src/bin/intro_palindrome_reorder.rs +++ b/src/bin/intro_palindrome_reorder.rs @@ -1,18 +1,26 @@ // I/O boilerplate // -pub struct UnsafeScanner { - reader: R, +pub struct UnsafeScanner { + // not actually dead code, needed for buf_iter to work + #[allow(dead_code)] buf_str: Vec, buf_iter: std::str::SplitAsciiWhitespace<'static>, } -impl UnsafeScanner { - pub fn new(reader: R) -> Self { - Self { - reader, - buf_str: vec![], - buf_iter: "".split_ascii_whitespace(), +impl UnsafeScanner { + pub fn new(mut reader: R) -> Self { + let mut buf_str = vec![]; + unsafe { + reader.read_to_end(&mut buf_str).unwrap_unchecked(); } + let buf_iter = unsafe { + let slice = std::str::from_utf8_unchecked(&buf_str); + std::mem::transmute(slice.split_ascii_whitespace()) + }; + // optional memory clear + buf_str.clear(); + + Self { buf_str, buf_iter } } /// Use "turbofish" syntax `token::()` to select data type of next token. @@ -20,18 +28,12 @@ impl UnsafeScanner { /// # Panics /// Panics if there's an I/O error or if the token cannot be parsed as T. pub fn token(&mut self) -> T { - loop { - if let Some(token) = self.buf_iter.next() { - return token.parse().ok().expect("Failed parse"); - } - self.buf_str.clear(); - self.reader - .read_until(b'\n', &mut self.buf_str) - .expect("Failed read"); - self.buf_iter = unsafe { - let slice = std::str::from_utf8_unchecked(&self.buf_str); - std::mem::transmute(slice.split_ascii_whitespace()) - } + unsafe { + self.buf_iter + .next() + .unwrap_unchecked() + .parse() + .unwrap_unchecked() } } } @@ -53,7 +55,7 @@ impl UnsafeScanner { ///
      ///
    • 1 ≤ n ≤ 106
    • ///
    -fn solve(scan: &mut UnsafeScanner, out: &mut W) { +fn solve(mut scan: UnsafeScanner, out: &mut W) { let mut token = scan.token::().into_bytes(); let mut counter = [0_u32; 26]; @@ -95,9 +97,9 @@ fn solve(scan: &mut UnsafeScanner, ou // entrypoints // fn main() { - let mut scan = UnsafeScanner::new(std::io::stdin().lock()); + let scan = UnsafeScanner::new(std::io::stdin().lock()); let mut out = std::io::BufWriter::new(std::io::stdout().lock()); - solve(&mut scan, &mut out); + solve(scan, &mut out); } #[cfg(test)] @@ -105,9 +107,9 @@ mod test { use super::*; fn test(input: &[u8], target: &[u8]) { - let mut scan = UnsafeScanner::new(input); + let scan = UnsafeScanner::new(input); let mut out = Vec::with_capacity(target.len()); - solve(&mut scan, &mut out); + solve(scan, &mut out); assert_eq!(out, target); } diff --git a/src/bin/intro_permutations.rs b/src/bin/intro_permutations.rs index cc7b586..c4486e5 100644 --- a/src/bin/intro_permutations.rs +++ b/src/bin/intro_permutations.rs @@ -1,18 +1,26 @@ // I/O boilerplate // -pub struct UnsafeScanner { - reader: R, +pub struct UnsafeScanner { + // not actually dead code, needed for buf_iter to work + #[allow(dead_code)] buf_str: Vec, buf_iter: std::str::SplitAsciiWhitespace<'static>, } -impl UnsafeScanner { - pub fn new(reader: R) -> Self { - Self { - reader, - buf_str: vec![], - buf_iter: "".split_ascii_whitespace(), +impl UnsafeScanner { + pub fn new(mut reader: R) -> Self { + let mut buf_str = vec![]; + unsafe { + reader.read_to_end(&mut buf_str).unwrap_unchecked(); } + let buf_iter = unsafe { + let slice = std::str::from_utf8_unchecked(&buf_str); + std::mem::transmute(slice.split_ascii_whitespace()) + }; + // optional memory clear + buf_str.clear(); + + Self { buf_str, buf_iter } } /// Use "turbofish" syntax `token::()` to select data type of next token. @@ -20,18 +28,12 @@ impl UnsafeScanner { /// # Panics /// Panics if there's an I/O error or if the token cannot be parsed as T. pub fn token(&mut self) -> T { - loop { - if let Some(token) = self.buf_iter.next() { - return token.parse().ok().expect("Failed parse"); - } - self.buf_str.clear(); - self.reader - .read_until(b'\n', &mut self.buf_str) - .expect("Failed read"); - self.buf_iter = unsafe { - let slice = std::str::from_utf8_unchecked(&self.buf_str); - std::mem::transmute(slice.split_ascii_whitespace()) - } + unsafe { + self.buf_iter + .next() + .unwrap_unchecked() + .parse() + .unwrap_unchecked() } } } @@ -55,7 +57,7 @@ impl UnsafeScanner { ///
      ///
    • 1 ≤ n ≤ 106
    • ///
    -fn solve(scan: &mut UnsafeScanner, out: &mut W) { +fn solve(mut scan: UnsafeScanner, out: &mut W) { let num = scan.token::(); match num { @@ -74,9 +76,9 @@ fn solve(scan: &mut UnsafeScanner, ou // entrypoints // fn main() { - let mut scan = UnsafeScanner::new(std::io::stdin().lock()); + let scan = UnsafeScanner::new(std::io::stdin().lock()); let mut out = std::io::BufWriter::new(std::io::stdout().lock()); - solve(&mut scan, &mut out); + solve(scan, &mut out); } #[cfg(test)] @@ -84,9 +86,9 @@ mod test { use super::*; fn test(input: &[u8], target: &[u8]) { - let mut scan = UnsafeScanner::new(input); + let scan = UnsafeScanner::new(input); let mut out = Vec::with_capacity(target.len()); - solve(&mut scan, &mut out); + solve(scan, &mut out); assert_eq!(out, target); } diff --git a/src/bin/intro_repetitions.rs b/src/bin/intro_repetitions.rs index 3cab2f9..b7e73df 100644 --- a/src/bin/intro_repetitions.rs +++ b/src/bin/intro_repetitions.rs @@ -1,18 +1,26 @@ // I/O boilerplate // -pub struct UnsafeScanner { - reader: R, +pub struct UnsafeScanner { + // not actually dead code, needed for buf_iter to work + #[allow(dead_code)] buf_str: Vec, buf_iter: std::str::SplitAsciiWhitespace<'static>, } -impl UnsafeScanner { - pub fn new(reader: R) -> Self { - Self { - reader, - buf_str: vec![], - buf_iter: "".split_ascii_whitespace(), +impl UnsafeScanner { + pub fn new(mut reader: R) -> Self { + let mut buf_str = vec![]; + unsafe { + reader.read_to_end(&mut buf_str).unwrap_unchecked(); } + let buf_iter = unsafe { + let slice = std::str::from_utf8_unchecked(&buf_str); + std::mem::transmute(slice.split_ascii_whitespace()) + }; + // optional memory clear + buf_str.clear(); + + Self { buf_str, buf_iter } } /// Use "turbofish" syntax `token::()` to select data type of next token. @@ -20,18 +28,12 @@ impl UnsafeScanner { /// # Panics /// Panics if there's an I/O error or if the token cannot be parsed as T. pub fn token(&mut self) -> T { - loop { - if let Some(token) = self.buf_iter.next() { - return token.parse().ok().expect("Failed parse"); - } - self.buf_str.clear(); - self.reader - .read_until(b'\n', &mut self.buf_str) - .expect("Failed read"); - self.buf_iter = unsafe { - let slice = std::str::from_utf8_unchecked(&self.buf_str); - std::mem::transmute(slice.split_ascii_whitespace()) - } + unsafe { + self.buf_iter + .next() + .unwrap_unchecked() + .parse() + .unwrap_unchecked() } } } @@ -53,7 +55,7 @@ impl UnsafeScanner { ///
      ///
    • 1 ≤ n ≤ 106
    • ///
    -fn solve(scan: &mut UnsafeScanner, out: &mut W) { +fn solve(mut scan: UnsafeScanner, out: &mut W) { let token = scan.token::().into_bytes(); writeln!( @@ -77,9 +79,9 @@ fn solve(scan: &mut UnsafeScanner, ou // entrypoints // fn main() { - let mut scan = UnsafeScanner::new(std::io::stdin().lock()); + let scan = UnsafeScanner::new(std::io::stdin().lock()); let mut out = std::io::BufWriter::new(std::io::stdout().lock()); - solve(&mut scan, &mut out); + solve(scan, &mut out); } #[cfg(test)] @@ -87,9 +89,9 @@ mod test { use super::*; fn test(input: &[u8], target: &[u8]) { - let mut scan = UnsafeScanner::new(input); + let scan = UnsafeScanner::new(input); let mut out = Vec::with_capacity(target.len()); - solve(&mut scan, &mut out); + solve(scan, &mut out); assert_eq!(out, target); } diff --git a/src/bin/intro_tower_of_hanoi.rs b/src/bin/intro_tower_of_hanoi.rs index 63fc9d0..1ba363d 100644 --- a/src/bin/intro_tower_of_hanoi.rs +++ b/src/bin/intro_tower_of_hanoi.rs @@ -1,18 +1,26 @@ // I/O boilerplate // -pub struct UnsafeScanner { - reader: R, +pub struct UnsafeScanner { + // not actually dead code, needed for buf_iter to work + #[allow(dead_code)] buf_str: Vec, buf_iter: std::str::SplitAsciiWhitespace<'static>, } -impl UnsafeScanner { - pub fn new(reader: R) -> Self { - Self { - reader, - buf_str: vec![], - buf_iter: "".split_ascii_whitespace(), +impl UnsafeScanner { + pub fn new(mut reader: R) -> Self { + let mut buf_str = vec![]; + unsafe { + reader.read_to_end(&mut buf_str).unwrap_unchecked(); } + let buf_iter = unsafe { + let slice = std::str::from_utf8_unchecked(&buf_str); + std::mem::transmute(slice.split_ascii_whitespace()) + }; + // optional memory clear + buf_str.clear(); + + Self { buf_str, buf_iter } } /// Use "turbofish" syntax `token::()` to select data type of next token. @@ -20,18 +28,12 @@ impl UnsafeScanner { /// # Panics /// Panics if there's an I/O error or if the token cannot be parsed as T. pub fn token(&mut self) -> T { - loop { - if let Some(token) = self.buf_iter.next() { - return token.parse().ok().expect("Failed parse"); - } - self.buf_str.clear(); - self.reader - .read_until(b'\n', &mut self.buf_str) - .expect("Failed read"); - self.buf_iter = unsafe { - let slice = std::str::from_utf8_unchecked(&self.buf_str); - std::mem::transmute(slice.split_ascii_whitespace()) - } + unsafe { + self.buf_iter + .next() + .unwrap_unchecked() + .parse() + .unwrap_unchecked() } } } @@ -59,7 +61,7 @@ impl UnsafeScanner { ///
      ///
    • 1 ≤ n ≤ 16
    • ///
    -fn solve(scan: &mut UnsafeScanner, out: &mut W) { +fn solve(mut scan: UnsafeScanner, out: &mut W) { let discs = scan.token::(); writeln!(out, "{}", (1 << discs) - 1).ok(); @@ -80,9 +82,9 @@ fn recurse(out: &mut W, from: u8, to: u8, swap: u8, disc: u8) // entrypoints // fn main() { - let mut scan = UnsafeScanner::new(std::io::stdin().lock()); + let scan = UnsafeScanner::new(std::io::stdin().lock()); let mut out = std::io::BufWriter::new(std::io::stdout().lock()); - solve(&mut scan, &mut out); + solve(scan, &mut out); } #[cfg(test)] @@ -90,9 +92,9 @@ mod test { use super::*; fn test(input: &[u8], target: &[u8]) { - let mut scan = UnsafeScanner::new(input); + let scan = UnsafeScanner::new(input); let mut out = Vec::with_capacity(target.len()); - solve(&mut scan, &mut out); + solve(scan, &mut out); assert_eq!(out, target); } diff --git a/src/bin/intro_trailing_zeros.rs b/src/bin/intro_trailing_zeros.rs index 43eaf06..5422cb8 100644 --- a/src/bin/intro_trailing_zeros.rs +++ b/src/bin/intro_trailing_zeros.rs @@ -1,18 +1,26 @@ // I/O boilerplate // -pub struct UnsafeScanner { - reader: R, +pub struct UnsafeScanner { + // not actually dead code, needed for buf_iter to work + #[allow(dead_code)] buf_str: Vec, buf_iter: std::str::SplitAsciiWhitespace<'static>, } -impl UnsafeScanner { - pub fn new(reader: R) -> Self { - Self { - reader, - buf_str: vec![], - buf_iter: "".split_ascii_whitespace(), +impl UnsafeScanner { + pub fn new(mut reader: R) -> Self { + let mut buf_str = vec![]; + unsafe { + reader.read_to_end(&mut buf_str).unwrap_unchecked(); } + let buf_iter = unsafe { + let slice = std::str::from_utf8_unchecked(&buf_str); + std::mem::transmute(slice.split_ascii_whitespace()) + }; + // optional memory clear + buf_str.clear(); + + Self { buf_str, buf_iter } } /// Use "turbofish" syntax `token::()` to select data type of next token. @@ -20,18 +28,12 @@ impl UnsafeScanner { /// # Panics /// Panics if there's an I/O error or if the token cannot be parsed as T. pub fn token(&mut self) -> T { - loop { - if let Some(token) = self.buf_iter.next() { - return token.parse().ok().expect("Failed parse"); - } - self.buf_str.clear(); - self.reader - .read_until(b'\n', &mut self.buf_str) - .expect("Failed read"); - self.buf_iter = unsafe { - let slice = std::str::from_utf8_unchecked(&self.buf_str); - std::mem::transmute(slice.split_ascii_whitespace()) - } + unsafe { + self.buf_iter + .next() + .unwrap_unchecked() + .parse() + .unwrap_unchecked() } } } @@ -55,7 +57,7 @@ impl UnsafeScanner { ///
      ///
    • 1 ≤ n ≤ 109
    • ///
    -fn solve(scan: &mut UnsafeScanner, out: &mut W) { +fn solve(mut scan: UnsafeScanner, out: &mut W) { let mut token = scan.token::(); let mut answer = 0; @@ -70,9 +72,9 @@ fn solve(scan: &mut UnsafeScanner, ou // entrypoints // fn main() { - let mut scan = UnsafeScanner::new(std::io::stdin().lock()); + let scan = UnsafeScanner::new(std::io::stdin().lock()); let mut out = std::io::BufWriter::new(std::io::stdout().lock()); - solve(&mut scan, &mut out); + solve(scan, &mut out); } #[cfg(test)] @@ -80,9 +82,9 @@ mod test { use super::*; fn test(input: &[u8], target: &[u8]) { - let mut scan = UnsafeScanner::new(input); + let scan = UnsafeScanner::new(input); let mut out = Vec::with_capacity(target.len()); - solve(&mut scan, &mut out); + solve(scan, &mut out); assert_eq!(out, target); } diff --git a/src/bin/intro_two_knights.rs b/src/bin/intro_two_knights.rs index 1f6c6ad..886b6b4 100644 --- a/src/bin/intro_two_knights.rs +++ b/src/bin/intro_two_knights.rs @@ -1,18 +1,26 @@ // I/O boilerplate // -pub struct UnsafeScanner { - reader: R, +pub struct UnsafeScanner { + // not actually dead code, needed for buf_iter to work + #[allow(dead_code)] buf_str: Vec, buf_iter: std::str::SplitAsciiWhitespace<'static>, } -impl UnsafeScanner { - pub fn new(reader: R) -> Self { - Self { - reader, - buf_str: vec![], - buf_iter: "".split_ascii_whitespace(), +impl UnsafeScanner { + pub fn new(mut reader: R) -> Self { + let mut buf_str = vec![]; + unsafe { + reader.read_to_end(&mut buf_str).unwrap_unchecked(); } + let buf_iter = unsafe { + let slice = std::str::from_utf8_unchecked(&buf_str); + std::mem::transmute(slice.split_ascii_whitespace()) + }; + // optional memory clear + buf_str.clear(); + + Self { buf_str, buf_iter } } /// Use "turbofish" syntax `token::()` to select data type of next token. @@ -20,18 +28,12 @@ impl UnsafeScanner { /// # Panics /// Panics if there's an I/O error or if the token cannot be parsed as T. pub fn token(&mut self) -> T { - loop { - if let Some(token) = self.buf_iter.next() { - return token.parse().ok().expect("Failed parse"); - } - self.buf_str.clear(); - self.reader - .read_until(b'\n', &mut self.buf_str) - .expect("Failed read"); - self.buf_iter = unsafe { - let slice = std::str::from_utf8_unchecked(&self.buf_str); - std::mem::transmute(slice.split_ascii_whitespace()) - } + unsafe { + self.buf_iter + .next() + .unwrap_unchecked() + .parse() + .unwrap_unchecked() } } } @@ -53,7 +55,7 @@ impl UnsafeScanner { ///
      ///
    • 1 ≤ n ≤ 10000
    • ///
    -fn solve(scan: &mut UnsafeScanner, out: &mut W) { +fn solve(mut scan: UnsafeScanner, out: &mut W) { let token = scan.token::(); for rows in 1..=token { @@ -81,9 +83,9 @@ fn solve(scan: &mut UnsafeScanner, ou // entrypoints // fn main() { - let mut scan = UnsafeScanner::new(std::io::stdin().lock()); + let scan = UnsafeScanner::new(std::io::stdin().lock()); let mut out = std::io::BufWriter::new(std::io::stdout().lock()); - solve(&mut scan, &mut out); + solve(scan, &mut out); } #[cfg(test)] @@ -91,9 +93,9 @@ mod test { use super::*; fn test(input: &[u8], target: &[u8]) { - let mut scan = UnsafeScanner::new(input); + let scan = UnsafeScanner::new(input); let mut out = Vec::with_capacity(target.len()); - solve(&mut scan, &mut out); + solve(scan, &mut out); assert_eq!(out, target); } diff --git a/src/bin/intro_two_sets.rs b/src/bin/intro_two_sets.rs index 647d720..2ba0e7e 100644 --- a/src/bin/intro_two_sets.rs +++ b/src/bin/intro_two_sets.rs @@ -1,18 +1,26 @@ // I/O boilerplate // -pub struct UnsafeScanner { - reader: R, +pub struct UnsafeScanner { + // not actually dead code, needed for buf_iter to work + #[allow(dead_code)] buf_str: Vec, buf_iter: std::str::SplitAsciiWhitespace<'static>, } -impl UnsafeScanner { - pub fn new(reader: R) -> Self { - Self { - reader, - buf_str: vec![], - buf_iter: "".split_ascii_whitespace(), +impl UnsafeScanner { + pub fn new(mut reader: R) -> Self { + let mut buf_str = vec![]; + unsafe { + reader.read_to_end(&mut buf_str).unwrap_unchecked(); } + let buf_iter = unsafe { + let slice = std::str::from_utf8_unchecked(&buf_str); + std::mem::transmute(slice.split_ascii_whitespace()) + }; + // optional memory clear + buf_str.clear(); + + Self { buf_str, buf_iter } } /// Use "turbofish" syntax `token::()` to select data type of next token. @@ -20,18 +28,12 @@ impl UnsafeScanner { /// # Panics /// Panics if there's an I/O error or if the token cannot be parsed as T. pub fn token(&mut self) -> T { - loop { - if let Some(token) = self.buf_iter.next() { - return token.parse().ok().expect("Failed parse"); - } - self.buf_str.clear(); - self.reader - .read_until(b'\n', &mut self.buf_str) - .expect("Failed read"); - self.buf_iter = unsafe { - let slice = std::str::from_utf8_unchecked(&self.buf_str); - std::mem::transmute(slice.split_ascii_whitespace()) - } + unsafe { + self.buf_iter + .next() + .unwrap_unchecked() + .parse() + .unwrap_unchecked() } } } @@ -55,7 +57,7 @@ impl UnsafeScanner { ///
      ///
    • 1 ≤ n ≤ 106
    • ///
    -fn solve(scan: &mut UnsafeScanner, out: &mut W) { +fn solve(mut scan: UnsafeScanner, out: &mut W) { let token = scan.token::(); match token & 3 { @@ -111,9 +113,9 @@ fn solve(scan: &mut UnsafeScanner, ou // entrypoints // fn main() { - let mut scan = UnsafeScanner::new(std::io::stdin().lock()); + let scan = UnsafeScanner::new(std::io::stdin().lock()); let mut out = std::io::BufWriter::new(std::io::stdout().lock()); - solve(&mut scan, &mut out); + solve(scan, &mut out); } #[cfg(test)] @@ -121,9 +123,9 @@ mod test { use super::*; fn test(input: &[u8], target: &[u8]) { - let mut scan = UnsafeScanner::new(input); + let scan = UnsafeScanner::new(input); let mut out = Vec::with_capacity(target.len()); - solve(&mut scan, &mut out); + solve(scan, &mut out); assert_eq!(out, target); } diff --git a/src/bin/intro_weird_algorithm.rs b/src/bin/intro_weird_algorithm.rs index 5f6c8de..62463d5 100644 --- a/src/bin/intro_weird_algorithm.rs +++ b/src/bin/intro_weird_algorithm.rs @@ -1,18 +1,26 @@ // I/O boilerplate // -pub struct UnsafeScanner { - reader: R, +pub struct UnsafeScanner { + // not actually dead code, needed for buf_iter to work + #[allow(dead_code)] buf_str: Vec, buf_iter: std::str::SplitAsciiWhitespace<'static>, } -impl UnsafeScanner { - pub fn new(reader: R) -> Self { - Self { - reader, - buf_str: vec![], - buf_iter: "".split_ascii_whitespace(), +impl UnsafeScanner { + pub fn new(mut reader: R) -> Self { + let mut buf_str = vec![]; + unsafe { + reader.read_to_end(&mut buf_str).unwrap_unchecked(); } + let buf_iter = unsafe { + let slice = std::str::from_utf8_unchecked(&buf_str); + std::mem::transmute(slice.split_ascii_whitespace()) + }; + // optional memory clear + buf_str.clear(); + + Self { buf_str, buf_iter } } /// Use "turbofish" syntax `token::()` to select data type of next token. @@ -20,18 +28,12 @@ impl UnsafeScanner { /// # Panics /// Panics if there's an I/O error or if the token cannot be parsed as T. pub fn token(&mut self) -> T { - loop { - if let Some(token) = self.buf_iter.next() { - return token.parse().ok().expect("Failed parse"); - } - self.buf_str.clear(); - self.reader - .read_until(b'\n', &mut self.buf_str) - .expect("Failed read"); - self.buf_iter = unsafe { - let slice = std::str::from_utf8_unchecked(&self.buf_str); - std::mem::transmute(slice.split_ascii_whitespace()) - } + unsafe { + self.buf_iter + .next() + .unwrap_unchecked() + .parse() + .unwrap_unchecked() } } } @@ -57,7 +59,7 @@ impl UnsafeScanner { ///
      ///
    • 1 ≤ n ≤ 106
    • ///
    -fn solve(scan: &mut UnsafeScanner, out: &mut W) { +fn solve(mut scan: UnsafeScanner, out: &mut W) { let mut token = scan.token::(); while token != 1 { @@ -74,9 +76,9 @@ fn solve(scan: &mut UnsafeScanner, ou // entrypoints // fn main() { - let mut scan = UnsafeScanner::new(std::io::stdin().lock()); + let scan = UnsafeScanner::new(std::io::stdin().lock()); let mut out = std::io::BufWriter::new(std::io::stdout().lock()); - solve(&mut scan, &mut out); + solve(scan, &mut out); } #[cfg(test)] @@ -84,9 +86,9 @@ mod test { use super::*; fn test(input: &[u8], target: &[u8]) { - let mut scan = UnsafeScanner::new(input); + let scan = UnsafeScanner::new(input); let mut out = Vec::with_capacity(target.len()); - solve(&mut scan, &mut out); + solve(scan, &mut out); assert_eq!(out, target); } diff --git a/src/bin/search_apartments.rs b/src/bin/search_apartments.rs index 33b02e4..4c73bda 100644 --- a/src/bin/search_apartments.rs +++ b/src/bin/search_apartments.rs @@ -1,18 +1,26 @@ // I/O boilerplate // -pub struct UnsafeScanner { - reader: R, +pub struct UnsafeScanner { + // not actually dead code, needed for buf_iter to work + #[allow(dead_code)] buf_str: Vec, buf_iter: std::str::SplitAsciiWhitespace<'static>, } -impl UnsafeScanner { - pub fn new(reader: R) -> Self { - Self { - reader, - buf_str: vec![], - buf_iter: "".split_ascii_whitespace(), +impl UnsafeScanner { + pub fn new(mut reader: R) -> Self { + let mut buf_str = vec![]; + unsafe { + reader.read_to_end(&mut buf_str).unwrap_unchecked(); } + let buf_iter = unsafe { + let slice = std::str::from_utf8_unchecked(&buf_str); + std::mem::transmute(slice.split_ascii_whitespace()) + }; + // optional memory clear + buf_str.clear(); + + Self { buf_str, buf_iter } } /// Use "turbofish" syntax `token::()` to select data type of next token. @@ -20,18 +28,12 @@ impl UnsafeScanner { /// # Panics /// Panics if there's an I/O error or if the token cannot be parsed as T. pub fn token(&mut self) -> T { - loop { - if let Some(token) = self.buf_iter.next() { - return token.parse().ok().expect("Failed parse"); - } - self.buf_str.clear(); - self.reader - .read_until(b'\n', &mut self.buf_str) - .expect("Failed read"); - self.buf_iter = unsafe { - let slice = std::str::from_utf8_unchecked(&self.buf_str); - std::mem::transmute(slice.split_ascii_whitespace()) - } + unsafe { + self.buf_iter + .next() + .unwrap_unchecked() + .parse() + .unwrap_unchecked() } } } @@ -61,7 +63,7 @@ impl UnsafeScanner { ///
  • 0 ≤ k ≤ 109
  • ///
  • 1 ≤ ai , bi ≤ 109
  • /// -fn solve(scan: &mut UnsafeScanner, out: &mut W) { +fn solve(mut scan: UnsafeScanner, out: &mut W) { let n: u32 = scan.token(); let m: u32 = scan.token(); let k: i32 = scan.token(); @@ -105,9 +107,9 @@ fn solve(scan: &mut UnsafeScanner, ou // entrypoints // fn main() { - let mut scan = UnsafeScanner::new(std::io::stdin().lock()); + let scan = UnsafeScanner::new(std::io::stdin().lock()); let mut out = std::io::BufWriter::new(std::io::stdout().lock()); - solve(&mut scan, &mut out); + solve(scan, &mut out); } #[cfg(test)] @@ -115,9 +117,9 @@ mod test { use super::*; fn test(input: &[u8], target: &[u8]) { - let mut scan = UnsafeScanner::new(input); + let scan = UnsafeScanner::new(input); let mut out = Vec::with_capacity(target.len()); - solve(&mut scan, &mut out); + solve(scan, &mut out); assert_eq!(out, target); } diff --git a/src/bin/search_distinct_numbers.rs b/src/bin/search_distinct_numbers.rs index 7658e24..af57d66 100644 --- a/src/bin/search_distinct_numbers.rs +++ b/src/bin/search_distinct_numbers.rs @@ -61,19 +61,27 @@ impl Hasher for NoHashHasher { // I/O boilerplate // -pub struct UnsafeScanner { - reader: R, +pub struct UnsafeScanner { + // not actually dead code, needed for buf_iter to work + #[allow(dead_code)] buf_str: Vec, buf_iter: std::str::SplitAsciiWhitespace<'static>, } -impl UnsafeScanner { - pub fn new(reader: R) -> Self { - Self { - reader, - buf_str: vec![], - buf_iter: "".split_ascii_whitespace(), +impl UnsafeScanner { + pub fn new(mut reader: R) -> Self { + let mut buf_str = vec![]; + unsafe { + reader.read_to_end(&mut buf_str).unwrap_unchecked(); } + let buf_iter = unsafe { + let slice = std::str::from_utf8_unchecked(&buf_str); + std::mem::transmute(slice.split_ascii_whitespace()) + }; + // optional memory clear + buf_str.clear(); + + Self { buf_str, buf_iter } } /// Use "turbofish" syntax `token::()` to select data type of next token. @@ -81,18 +89,12 @@ impl UnsafeScanner { /// # Panics /// Panics if there's an I/O error or if the token cannot be parsed as T. pub fn token(&mut self) -> T { - loop { - if let Some(token) = self.buf_iter.next() { - return token.parse().ok().expect("Failed parse"); - } - self.buf_str.clear(); - self.reader - .read_until(b'\n', &mut self.buf_str) - .expect("Failed read"); - self.buf_iter = unsafe { - let slice = std::str::from_utf8_unchecked(&self.buf_str); - std::mem::transmute(slice.split_ascii_whitespace()) - } + unsafe { + self.buf_iter + .next() + .unwrap_unchecked() + .parse() + .unwrap_unchecked() } } } @@ -117,7 +119,7 @@ impl UnsafeScanner { ///
  • 1 ≤ n ≤ 2 * 105
  • ///
  • 1 ≤ xi ≤ 109
  • /// -fn solve(scan: &mut UnsafeScanner, out: &mut W) { +fn solve(mut scan: UnsafeScanner, out: &mut W) { let n: usize = scan.token(); writeln!( @@ -134,9 +136,9 @@ fn solve(scan: &mut UnsafeScanner, ou // entrypoints // fn main() { - let mut scan = UnsafeScanner::new(std::io::stdin().lock()); + let scan = UnsafeScanner::new(std::io::stdin().lock()); let mut out = std::io::BufWriter::new(std::io::stdout().lock()); - solve(&mut scan, &mut out); + solve(scan, &mut out); } #[cfg(test)] @@ -144,9 +146,9 @@ mod test { use super::*; fn test(input: &[u8], target: &[u8]) { - let mut scan = UnsafeScanner::new(input); + let scan = UnsafeScanner::new(input); let mut out = Vec::with_capacity(target.len()); - solve(&mut scan, &mut out); + solve(scan, &mut out); assert_eq!(out, target); } diff --git a/src/bin/search_maximum_subarray_sum.rs b/src/bin/search_maximum_subarray_sum.rs index c2017a6..77f327d 100644 --- a/src/bin/search_maximum_subarray_sum.rs +++ b/src/bin/search_maximum_subarray_sum.rs @@ -1,18 +1,26 @@ // I/O boilerplate // -pub struct UnsafeScanner { - reader: R, +pub struct UnsafeScanner { + // not actually dead code, needed for buf_iter to work + #[allow(dead_code)] buf_str: Vec, buf_iter: std::str::SplitAsciiWhitespace<'static>, } -impl UnsafeScanner { - pub fn new(reader: R) -> Self { - Self { - reader, - buf_str: vec![], - buf_iter: "".split_ascii_whitespace(), +impl UnsafeScanner { + pub fn new(mut reader: R) -> Self { + let mut buf_str = vec![]; + unsafe { + reader.read_to_end(&mut buf_str).unwrap_unchecked(); } + let buf_iter = unsafe { + let slice = std::str::from_utf8_unchecked(&buf_str); + std::mem::transmute(slice.split_ascii_whitespace()) + }; + // optional memory clear + buf_str.clear(); + + Self { buf_str, buf_iter } } /// Use "turbofish" syntax `token::()` to select data type of next token. @@ -20,18 +28,12 @@ impl UnsafeScanner { /// # Panics /// Panics if there's an I/O error or if the token cannot be parsed as T. pub fn token(&mut self) -> T { - loop { - if let Some(token) = self.buf_iter.next() { - return token.parse().ok().expect("Failed parse"); - } - self.buf_str.clear(); - self.reader - .read_until(b'\n', &mut self.buf_str) - .expect("Failed read"); - self.buf_iter = unsafe { - let slice = std::str::from_utf8_unchecked(&self.buf_str); - std::mem::transmute(slice.split_ascii_whitespace()) - } + unsafe { + self.buf_iter + .next() + .unwrap_unchecked() + .parse() + .unwrap_unchecked() } } } @@ -56,7 +58,7 @@ impl UnsafeScanner { ///
  • 1 ≤ n ≤ 2 * 105
  • ///
  • −109 ≤ xi ≤ 109
  • /// -fn solve(scan: &mut UnsafeScanner, out: &mut W) { +fn solve(mut scan: UnsafeScanner, out: &mut W) { let size = scan.token::(); let base = scan.token::(); writeln!( @@ -76,9 +78,9 @@ fn solve(scan: &mut UnsafeScanner, ou // entrypoints // fn main() { - let mut scan = UnsafeScanner::new(std::io::stdin().lock()); + let scan = UnsafeScanner::new(std::io::stdin().lock()); let mut out = std::io::BufWriter::new(std::io::stdout().lock()); - solve(&mut scan, &mut out); + solve(scan, &mut out); } #[cfg(test)] @@ -86,9 +88,9 @@ mod test { use super::*; fn test(input: &[u8], target: &[u8]) { - let mut scan = UnsafeScanner::new(input); + let scan = UnsafeScanner::new(input); let mut out = Vec::with_capacity(target.len()); - solve(&mut scan, &mut out); + solve(scan, &mut out); assert_eq!(out, target); }