diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9658366..9cf89aa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -129,3 +129,47 @@ jobs: - name: expand.py tests run: bash ./.github/workflows/test-expand.sh + + verify: + name: Verify + runs-on: ubuntu-20.04 + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup `1.47.0-x86_64-unknown-linux-gnu` + uses: actions-rs/toolchain@v1 + with: + toolchain: 1.47.0-x86_64-unknown-linux-gnu + override: true + profile: minimal + + - name: Setup Python 3.9 + uses: actions/setup-python@v2 + with: + python-version: '3.9' + + - name: Install `oj` + run: pip install online-judge-tools + + - name: cargo-build + uses: actions-rs/cargo@v1 + with: + command: build + args: --release --examples + + - name: Verify + run: | + NAMES=( + convolution_mod + static_range_sum + sum_of_floor_of_linear + unionfind + ) + for name in "${NAMES[@]}"; do + oj d "https://judge.yosupo.jp/problem/$name" -ad "/tmp/$name" + done + for name in "${NAMES[@]}"; do + oj t -d "/tmp/$name" -t 10 -c "./target/release/examples/library-checker-${name//_/-}" --judge-command ~/.cache/online-judge-tools/library-checker-problems/*/"$name"/checker + done diff --git a/Cargo.toml b/Cargo.toml index 5348943..463b124 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,5 +15,7 @@ publish = false [dependencies] [dev-dependencies] +input = { path = "./crates/input" } proconio = "=0.3.6" +proconio-derive = "0.2.1" rand = "0.7.3" diff --git a/crates/input/Cargo.toml b/crates/input/Cargo.toml new file mode 100644 index 0000000..feab978 --- /dev/null +++ b/crates/input/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "input" +version = "0.1.0" +authors = ["rust-lang-ja Developers"] +edition = "2018" +description = "Provides an `input!` macro for the `examples`." +license = "CC0-1.0" +publish = false + +[dependencies] diff --git a/crates/input/src/lib.rs b/crates/input/src/lib.rs new file mode 100644 index 0000000..ea7cd6a --- /dev/null +++ b/crates/input/src/lib.rs @@ -0,0 +1,96 @@ +//! A simple `input!` macro with minimal functionality. +//! +//! ```no_run +//! #[macro_use] +//! extern crate input as _; +//! +//! fn main() { +//! input! { +//! a: [u64], +//! } +//! } +//! ``` + +use std::{ + fmt, + io::{self, Read}, + str::{FromStr, SplitAsciiWhitespace}, +}; + +#[macro_export] +macro_rules! input { + ($($tt:tt)*) => { + let mut __scanner = $crate::Scanner::new().unwrap(); + $crate::input_inner!(@scanner(__scanner), @tts($($tt)*)); + ::std::mem::drop(__scanner); + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! input_inner { + (@scanner($scanner:ident), @tts()) => {}; + (@scanner($scanner:ident), @tts(mut $single_tt_pat:tt : $readable:tt)) => { + let mut $single_tt_pat = $crate::read!(from $scanner { $readable }); + }; + (@scanner($scanner:ident), @tts($single_tt_pat:tt : $readable:tt)) => { + let $single_tt_pat = $crate::read!(from $scanner { $readable }); + }; + (@scanner($scanner:ident), @tts(mut $single_tt_pat:tt : $readable:tt, $($rest:tt)*)) => { + $crate::input_inner!(@scanner($scanner), @tts(mut $single_tt_pat: $readable)); + $crate::input_inner!(@scanner($scanner), @tts($($rest)*)); + }; + (@scanner($scanner:ident), @tts($single_tt_pat:tt : $readable:tt, $($rest:tt)*)) => { + $crate::input_inner!(@scanner($scanner), @tts($single_tt_pat: $readable)); + $crate::input_inner!(@scanner($scanner), @tts($($rest)*)); + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! read { + (from $scanner:ident { [$tt:tt] }) => { + $crate::read!(from $scanner { [$tt; $crate::read!(from $scanner { usize })] }) + }; + (from $scanner:ident { [$tt:tt; $n:expr] }) => { + (0..$n).map(|_| $crate::read!(from $scanner { $tt })).collect::>() + }; + (from $scanner:ident { ($($tt:tt),+) }) => { + ($($crate::read!(from $scanner { $tt })),*) + }; + (from $scanner:ident { $ty:ty }) => { + $scanner.parse::<$ty>() + }; +} + +#[doc(hidden)] +pub struct Scanner { + words: SplitAsciiWhitespace<'static>, +} + +impl Scanner { + pub fn new() -> io::Result { + let mut buf = String::with_capacity(1024); + io::stdin().read_to_string(&mut buf)?; + let words = Box::leak(buf.into_boxed_str()).split_ascii_whitespace(); + Ok(Self { words }) + } + + /// Parses the next word. + /// + /// # Panics + /// + /// Panics if: + /// + /// - reached the end of input. + /// - the word is not successfully parsed. + pub fn parse(&mut self) -> T + where + T: FromStr, + T::Err: fmt::Display, + { + let word = self.words.next().expect("reached the end of input"); + word.parse() + .unwrap_or_else(|e| panic!("could not parse {:?}: {}", word, e)) + } +} diff --git a/examples/library-checker-convolution-mod.rs b/examples/library-checker-convolution-mod.rs new file mode 100644 index 0000000..9e179b0 --- /dev/null +++ b/examples/library-checker-convolution-mod.rs @@ -0,0 +1,27 @@ +#[macro_use] +extern crate input as _; + +use ac_library_rs::{convolution, modint::ModInt998244353 as Mint}; +use std::fmt; + +fn main() { + input! { + n: usize, + m: usize, + a: [Mint; n], + b: [Mint; m], + } + + print_oneline(convolution::convolution(&a, &b)); +} + +fn print_oneline, T: fmt::Display>(values: I) { + println!( + "{}", + values + .into_iter() + .map(|v| v.to_string()) + .collect::>() + .join(" "), + ) +} diff --git a/examples/library-checker-static-range-sum.rs b/examples/library-checker-static-range-sum.rs new file mode 100644 index 0000000..a4a5d8b --- /dev/null +++ b/examples/library-checker-static-range-sum.rs @@ -0,0 +1,25 @@ +#[macro_use] +extern crate input as _; +#[macro_use] +extern crate proconio_derive as _; + +use ac_library_rs::fenwicktree::FenwickTree; + +#[allow(clippy::needless_collect)] +#[fastout] +fn main() { + input! { + n: usize, + q: usize, + r#as: [u64; n], + lrs: [(usize, usize); q], + } + + let mut fenwick = FenwickTree::new(n, 0); + for (i, a) in r#as.into_iter().enumerate() { + fenwick.add(i, a); + } + for (l, r) in lrs { + println!("{}", fenwick.sum(l, r)); + } +} diff --git a/examples/library-checker-sum-of-floor-of-linear.rs b/examples/library-checker-sum-of-floor-of-linear.rs new file mode 100644 index 0000000..78c3ca6 --- /dev/null +++ b/examples/library-checker-sum-of-floor-of-linear.rs @@ -0,0 +1,17 @@ +#[macro_use] +extern crate input as _; +#[macro_use] +extern crate proconio_derive as _; + +use ac_library_rs::math; + +#[fastout] +fn main() { + input! { + nmabs: [(i64, i64, i64, i64)], + } + + for (n, m, a, b) in nmabs { + println!("{}", math::floor_sum(n, m, a, b)); + } +} diff --git a/examples/library-checker-unionfind.rs b/examples/library-checker-unionfind.rs new file mode 100644 index 0000000..b6e1081 --- /dev/null +++ b/examples/library-checker-unionfind.rs @@ -0,0 +1,22 @@ +#[macro_use] +extern crate input as _; + +use ac_library_rs::dsu::Dsu; + +fn main() { + input! { + n: usize, + queries: [(u8, usize, usize)], + } + + let mut dsu = Dsu::new(n); + for (kind, u, v) in queries { + match kind { + 0 => { + dsu.merge(u, v); + } + 1 => println!("{}", u8::from(dsu.same(u, v))), + _ => unreachable!(), + } + } +}