diff --git a/.cargo/config b/.cargo/config index 20f0bd6..cf13702 100644 --- a/.cargo/config +++ b/.cargo/config @@ -1,3 +1,3 @@ [alias] dep-tests = ["run", "--manifest-path", "./tools/dep-tests/Cargo.toml", "--"] -test-with-generated-opts = ["run", "--manifest-path", "./tools/test-with-generated-opts/Cargo.toml", "--"] +test-examples = ["run", "--manifest-path", "./tools/test-examples/Cargo.toml", "--"] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6a5339f..b2528fa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,20 +43,25 @@ jobs: command: fmt args: --manifest-path ./tools/dep-tests/Cargo.toml -- --check - - name: '`cargo fmt --manifest-path ./tools/test-with-generated-opts/Cargo.toml -- --check`' + - name: '`cargo fmt --manifest-path ./tools/test-examples/Cargo.toml -- --check`' uses: actions-rs/cargo@v1 with: command: fmt - args: --manifest-path ./tools/test-with-generated-opts/Cargo.toml -- --check + args: --manifest-path ./tools/test-examples/Cargo.toml -- --check - test-with-generated-opts: - name: test-with-generated-opts + test-examples: + name: test-examples runs-on: ubuntu-18.04 steps: - name: Checkout uses: actions/checkout@v1 + - name: setup-python + uses: actions/setup-python@v1 + with: + python-version: '3.8' + - name: rust-toolchain uses: actions-rs/toolchain@v1 with: @@ -70,11 +75,11 @@ jobs: command: install args: --git https://github.com/rust-lang-ja/atcoder-rustc-dep-option-generator - - name: '`cargo clippy --all-features --manifest-path ./tools/test-with-generated-opts/Cargo.toml -- -D warnings`' + - name: '`cargo clippy --all-features --manifest-path ./tools/test-examples/Cargo.toml -- -D warnings`' uses: actions-rs/cargo@v1 with: command: clippy - args: --all-features --manifest-path ./tools/test-with-generated-opts/Cargo.toml -- -D warnings + args: --all-features --manifest-path ./tools/test-examples/Cargo.toml -- -D warnings - name: '`cargo build --all-features --release`' uses: actions-rs/cargo@v1 @@ -82,13 +87,10 @@ jobs: command: build args: --all-features --release - - name: '`chmod -R a=rX-w ./target`' - run: chmod -R a=rX-w ./target - - - name: '`cargo test-with-generated-opts`' + - name: '`cargo test-examples`' uses: actions-rs/cargo@v1 with: - command: test-with-generated-opts + command: test-examples build: strategy: @@ -176,17 +178,17 @@ jobs: override: true profile: default - - name: '`cargo clippy ${{ matrix.features }} -- -D warnings`' + - name: '`cargo clippy --all-targets --profile test ${{ matrix.features }} -- -D warnings`' uses: actions-rs/cargo@v1 with: command: clippy - args: ${{ matrix.features }} -- -D warnings + args: --all-targets --profile test ${{ matrix.features }} -- -D warnings - - name: '`cargo test ${{ matrix.features }} --no-fail-fast`' + - name: '`cargo test --all-targets ${{ matrix.features }} --no-fail-fast`' uses: actions-rs/cargo@v1 with: command: test - args: ${{ matrix.features }} --no-fail-fast + args: --all-targets ${{ matrix.features }} --no-fail-fast - name: '`cargo run --release`' uses: actions-rs/cargo@v1 diff --git a/.gitignore b/.gitignore index 211ee2e..83c0e8e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ /tools/dep-tests/Cargo.lock -/tools/test-with-generated-opts/Cargo.lock +/tools/test-examples/Cargo.lock **/target/ **/*.rs.bk **/*~ diff --git a/examples/abc054-c.rs b/examples/abc054-c.rs index 63d8f99..4f46ce8 100644 --- a/examples/abc054-c.rs +++ b/examples/abc054-c.rs @@ -1,55 +1,21 @@ // https://atcoder.jp/contests/abc054/tasks/abc054_c -use petgraph::csr::Csr; -use petgraph::Undirected; +use itertools::Itertools as _; +use petgraph::graph::UnGraph; +use proconio::input; +use proconio::marker::Usize1; fn main() { - // use std::io::{self, Read as _}; - // - // let mut input = "".to_owned(); - // io::stdin().read_to_string(&mut input).unwrap(); - // let mut input = input.split_whitespace(); - // macro_rules! read { - // ([$t:tt; $n:expr]) => { - // (0..$n).map(|_| read!($t)).collect::>() - // }; - // (($($t:tt),+)) => { - // ($(read!($t)),*) - // }; - // (_1based) => { - // read!(usize) - 1 - // }; - // (_bytes) => { - // read!(String).into_bytes() - // }; - // ($ty:ty) => { - // input.next().unwrap().parse::<$ty>().unwrap() - // }; - // } - // - // let (n, m) = read!((usize, usize)); - // let mut abs = read!([(_1based, _1based); m]); - - use proconio::input; - use proconio::marker::Usize1; - input! { n: usize, - m: usize, - mut abs: [(Usize1, Usize1); m], + abs: [(Usize1, Usize1)], } - abs.sort(); - let mut g = Csr::<(), (), Undirected, usize>::with_nodes(n); - for (a, b) in abs { - g.add_edge(a, b, ()); - } - let mut ans = 0; - let mut es = (0..n).collect::>(); - permutohedron::heap_recursive(&mut es, |es| { - if es[0] == 0 && es.windows(2).all(|w| g.contains_edge(w[0], w[1])) { - ans += 1; - } - }); + let graph = UnGraph::<(), (), usize>::from_edges(abs); + let ans = graph + .node_indices() + .permutations(n) + .filter(|p| p[0].index() == 0 && p.windows(2).all(|w| graph.contains_edge(w[0], w[1]))) + .count(); println!("{}", ans); } diff --git a/examples/abc057-b-naive.rs b/examples/abc057-b-naive.rs new file mode 100644 index 0000000..fc85f3d --- /dev/null +++ b/examples/abc057-b-naive.rs @@ -0,0 +1,32 @@ +// https://atcoder.jp/contests/abc057/tasks/abc057_b + +use std::io::{self, Read}; + +fn main() { + let mut input = read_to_static(io::stdin()).split_whitespace(); + macro_rules! read { + ([$tt:tt]) => (read!([$tt; read!(usize)])); + ([$tt:tt; $n:expr]) => ((0..$n).map(|_| read!($tt)).collect::>()); + (($($tt:tt),+)) => (($(read!($tt)),*)); + ($ty:ty) => (input.next().unwrap().parse::<$ty>().unwrap()); + } + + let (n, m) = read!((usize, usize)); + let (abs, cds) = read!(([(i64, i64); n], [(i64, i64); m])); + + for (a, b) in abs { + let j = (0..m) + .min_by_key(|&j| { + let (c, d) = cds[j]; + (a - c).abs() + (b - d).abs() + }) + .unwrap(); + println!("{}", j + 1); + } +} + +fn read_to_static(mut source: impl Read) -> &'static str { + let mut input = "".to_owned(); + source.read_to_string(&mut input).unwrap(); + Box::leak(input.into_boxed_str()) +} diff --git a/examples/abc057-b-proconio.rs b/examples/abc057-b-proconio.rs new file mode 100644 index 0000000..7f652e8 --- /dev/null +++ b/examples/abc057-b-proconio.rs @@ -0,0 +1,22 @@ +// https://atcoder.jp/contests/abc057/tasks/abc057_b + +use proconio::input; + +fn main() { + input! { + n: usize, + m: usize, + abs: [(i64, i64); n], + cds: [(i64, i64); m], + } + + for (a, b) in abs { + let j = (0..m) + .min_by_key(|&j| { + let (c, d) = cds[j]; + (a - c).abs() + (b - d).abs() + }) + .unwrap(); + println!("{}", j + 1); + } +} diff --git a/examples/abc057-b-text-io.rs b/examples/abc057-b-text-io.rs new file mode 100644 index 0000000..2f22337 --- /dev/null +++ b/examples/abc057-b-text-io.rs @@ -0,0 +1,26 @@ +// https://atcoder.jp/contests/abc057/tasks/abc057_b + +#![allow(clippy::many_single_char_names, clippy::try_err)] + +use text_io::{read, try_read, try_scan}; + +fn main() { + let n: usize = read!(); + let m: usize = read!(); + let abs = (0..n) + .map(|_| (read!(), read!())) + .collect::>(); + let cds = (0..m) + .map(|_| (read!(), read!())) + .collect::>(); + + for (a, b) in abs { + let j = (0..m) + .min_by_key(|&j| { + let (c, d) = cds[j]; + (a - c).abs() + (b - d).abs() + }) + .unwrap(); + println!("{}", j + 1); + } +} diff --git a/examples/abc057-b-whiteread.rs b/examples/abc057-b-whiteread.rs new file mode 100644 index 0000000..4f7b8c8 --- /dev/null +++ b/examples/abc057-b-whiteread.rs @@ -0,0 +1,21 @@ +// https://atcoder.jp/contests/abc057/tasks/abc057_b + +use whiteread::Reader; + +fn main() { + let mut rdr = Reader::from_stdin_naive(); + + let (n, m) = rdr.p::<(usize, usize)>(); + let abs = (0..n).map(|_| rdr.p()).collect::>(); + let cds = (0..m).map(|_| rdr.p()).collect::>(); + + for (a, b) in abs { + let j = (0..m) + .min_by_key(|&j| { + let (c, d) = cds[j]; + (a - c).abs() + (b - d).abs() + }) + .unwrap(); + println!("{}", j + 1); + } +} diff --git a/examples/abc073-d.rs b/examples/abc073-d.rs new file mode 100644 index 0000000..5f3d456 --- /dev/null +++ b/examples/abc073-d.rs @@ -0,0 +1,50 @@ +// https://atcoder.jp/contests/abc073/tasks/abc073_d + +use itertools::Itertools as _; +use num::traits::One; +use petgraph::graph::{IndexType, NodeIndex, UnGraph}; +use proconio::input; +use proconio::source::{Readable, Source}; + +use std::collections::HashMap; +use std::io::BufRead; +use std::marker::PhantomData; +use std::ops::Sub; + +fn main() { + input! { + _n: usize, + m: usize, + r: usize, + rs: [NodeIndex1; r], + abcs: [(NodeIndex1, NodeIndex1, u32); m], + } + + let graph = UnGraph::<(), u32>::from_edges(abcs); + + let dijkstra = rs + .iter() + .map(|&r| { + let dijkstra = petgraph::algo::dijkstra(&graph, r, None, |e| *e.weight()); + (r, dijkstra) + }) + .collect::>(); + + let ans = rs + .into_iter() + .permutations(r) + .map(|rs| rs.windows(2).map(|w| dijkstra[&w[0]][&w[1]]).sum::()) + .min() + .unwrap(); + println!("{}", ans); +} + +struct NodeIndex1(PhantomData Ix>); + +impl + One + Sub> Readable for NodeIndex1 { + type Output = NodeIndex; + + fn read>(source: &mut S) -> NodeIndex { + NodeIndex::from(Ix::read(source) - Ix::one()) + } +} diff --git a/examples/abc084-d.rs b/examples/abc084-d.rs deleted file mode 100644 index 2a06584..0000000 --- a/examples/abc084-d.rs +++ /dev/null @@ -1,52 +0,0 @@ -// https://atcoder.jp/contests/abc084/tasks/abc084_d - -use itertools_num::ItertoolsNum as _; -use primal::Sieve; - -#[proconio::fastout] -fn main() { - // use std::io::{self, Read as _}; - // - // let mut input = "".to_owned(); - // io::stdin().read_to_string(&mut input).unwrap(); - // let mut input = input.split_whitespace(); - // macro_rules! read { - // ([$t:tt; $n:expr]) => { - // (0..$n).map(|_| read!($t)).collect::>() - // }; - // (($($t:tt),+)) => { - // ($(read!($t)),*) - // }; - // (_1based) => { - // read!(usize) - 1 - // }; - // (_bytes) => { - // read!(String).into_bytes() - // }; - // ($ty:ty) => { - // input.next().unwrap().parse::<$ty>().unwrap() - // }; - // } - // - // let q = read!(usize); - // let lrs = read!([(usize, usize); q]); - - use proconio::input; - - input! { - q: usize, - lrs: [(usize, usize); q], - } - - // サンプルケースでしか試してないので嘘かもしれない。 - - let hi = lrs.iter().map(|&(_, r)| r).max().unwrap(); - let sieve = Sieve::new(hi); - let nums = (0..=hi) - .map(|k| u32::from(sieve.is_prime(k) && sieve.is_prime((k + 1) / 2))) - .cumsum() - .collect::>(); - for (l, r) in lrs { - println!("{}", nums[r] - nums[l - 1]); - } -} diff --git a/examples/abc118-b-naive.rs b/examples/abc118-b-naive.rs new file mode 100644 index 0000000..925c6fa --- /dev/null +++ b/examples/abc118-b-naive.rs @@ -0,0 +1,33 @@ +// https://atcoder.jp/contests/abc118/tasks/abc118_b + +use std::io::{self, Read}; +use std::ops::{BitAnd, BitOr}; + +fn main() { + let mut input = read_to_static(io::stdin()).split_whitespace(); + macro_rules! read { + ([$tt:tt]) => (read!([$tt; read!(usize)])); + ([$tt:tt; $n:expr]) => ((0..$n).map(|_| read!($tt)).collect::>()); + (($($tt:tt),+)) => (($(read!($tt)),*)); + ($ty:ty) => (input.next().unwrap().parse::<$ty>().unwrap()); + ({ Usize1 }) => { + read!(usize) - 1 + }; + } + + let (n, _) = read!((usize, usize)); + let a = read!([[{ Usize1 }]; n]); + + let ans = a + .into_iter() + .map(|row| row.into_iter().map(|k| 1 << k).fold(0, BitOr::bitor)) + .fold(usize::max_value(), BitAnd::bitand) + .count_ones(); + println!("{}", ans); +} + +fn read_to_static(mut source: impl Read) -> &'static str { + let mut input = "".to_owned(); + source.read_to_string(&mut input).unwrap(); + Box::leak(input.into_boxed_str()) +} diff --git a/examples/abc118-b-proconio.rs b/examples/abc118-b-proconio.rs new file mode 100644 index 0000000..722a1e4 --- /dev/null +++ b/examples/abc118-b-proconio.rs @@ -0,0 +1,21 @@ +// https://atcoder.jp/contests/abc118/tasks/abc118_b + +use proconio::input; +use proconio::marker::Usize1; + +use std::ops::{BitAnd, BitOr}; + +fn main() { + input! { + n: usize, + _m: usize, + a: [[Usize1]; n], + } + + let ans = a + .into_iter() + .map(|row| row.into_iter().map(|k| 1 << k).fold(0, BitOr::bitor)) + .fold(usize::max_value(), BitAnd::bitand) + .count_ones(); + println!("{}", ans); +} diff --git a/examples/abc118-b-text-io.rs b/examples/abc118-b-text-io.rs new file mode 100644 index 0000000..2cef31d --- /dev/null +++ b/examples/abc118-b-text-io.rs @@ -0,0 +1,27 @@ +// https://atcoder.jp/contests/abc118/tasks/abc118_b + +use text_io::{read, try_read, try_scan}; + +use std::ops::{BitAnd, BitOr}; + +#[allow(clippy::try_err)] +fn main() { + let (n, _): (usize, usize) = (read!(), read!()); + let a = (0..n) + .map(|_| { + (0..read!()) + .map(|_| { + let a: usize = read!(); + a - 1 + }) + .collect() + }) + .collect::>>(); + + let ans = a + .into_iter() + .map(|row| row.into_iter().map(|k| 1 << k).fold(0, BitOr::bitor)) + .fold(usize::max_value(), BitAnd::bitand) + .count_ones(); + println!("{}", ans); +} diff --git a/examples/abc118-b-whiteread.rs b/examples/abc118-b-whiteread.rs new file mode 100644 index 0000000..f784580 --- /dev/null +++ b/examples/abc118-b-whiteread.rs @@ -0,0 +1,23 @@ +// https://atcoder.jp/contests/abc118/tasks/abc118_b + +use whiteread::Reader; + +use std::ops::{BitAnd, BitOr}; + +fn main() { + let mut rdr = Reader::from_stdin_naive(); + let (n, _) = rdr.p::<(usize, usize)>(); + let a = (0..n) + .map(|_| { + let k = rdr.p::(); + (0..k).map(|_| rdr.p::() - 1).collect() + }) + .collect::>>(); + + let ans = a + .into_iter() + .map(|row| row.into_iter().map(|k| 1 << k).fold(0, BitOr::bitor)) + .fold(usize::max_value(), BitAnd::bitand) + .count_ones(); + println!("{}", ans); +} diff --git a/examples/abc120-d.rs b/examples/abc120-d.rs deleted file mode 100644 index 92e33ee..0000000 --- a/examples/abc120-d.rs +++ /dev/null @@ -1,56 +0,0 @@ -// https://atcoder.jp/contests/abc120/tasks/abc120_d - -use union_find::{QuickFindUf, UnionBySize, UnionFind as _}; - -#[proconio::fastout] -fn main() { - // use std::io::{self, Read as _}; - // - // let mut input = "".to_owned(); - // io::stdin().read_to_string(&mut input).unwrap(); - // let mut input = input.split_whitespace(); - // macro_rules! read { - // ([$t:tt; $n:expr]) => { - // (0..$n).map(|_| read!($t)).collect::>() - // }; - // (($($t:tt),+)) => { - // ($(read!($t)),*) - // }; - // (_1based) => { - // read!(usize) - 1 - // }; - // (_bytes) => { - // read!(String).into_bytes() - // }; - // ($ty:ty) => { - // input.next().unwrap().parse::<$ty>().unwrap() - // }; - // } - // - // let (n, m) = (read!((usize, usize))); - // let abs = read!([(_1based, _1based); m]); - - use proconio::input; - use proconio::marker::Usize1; - - input! { - n: usize, - m: usize, - abs: [(Usize1, Usize1); m], - } - - let mut u = QuickFindUf::::new(n); - let mut k = n * (n - 1) / 2; - let mut r = vec![k]; - r.extend(abs.into_iter().rev().map(|(a, b)| { - let p = u.get(a).size() * u.get(b).size(); - if u.union(a, b) { - k -= p; - } - k - })); - assert_eq!(r.pop(), Some(0)); - for r in r.into_iter().rev() { - println!("{}", r); - } -} diff --git a/examples/abc121-b-naive.rs b/examples/abc121-b-naive.rs new file mode 100644 index 0000000..33b4177 --- /dev/null +++ b/examples/abc121-b-naive.rs @@ -0,0 +1,29 @@ +// https://atcoder.jp/contests/abc121/tasks/abc121_b + +use std::io::{self, Read}; + +#[allow(clippy::many_single_char_names)] +fn main() { + let mut input = read_to_static(io::stdin()).split_whitespace(); + macro_rules! read { + ([$tt:tt]) => (read!([$tt; read!(usize)])); + ([$tt:tt; $n:expr]) => ((0..$n).map(|_| read!($tt)).collect::>()); + (($($tt:tt),+)) => (($(read!($tt)),*)); + ($ty:ty) => (input.next().unwrap().parse::<$ty>().unwrap()); + } + + let (n, m, c) = read!((usize, usize, i32)); + let (b, a) = read!(([i32; m], [[i32; m]; n])); + + let ans = a + .into_iter() + .filter(|a| a.iter().zip(&b).map(|(a, b)| a * b).sum::() + c > 0) + .count(); + println!("{}", ans); +} + +fn read_to_static(mut source: impl Read) -> &'static str { + let mut input = "".to_owned(); + source.read_to_string(&mut input).unwrap(); + Box::leak(input.into_boxed_str()) +} diff --git a/examples/abc121-b-proconio.rs b/examples/abc121-b-proconio.rs new file mode 100644 index 0000000..f4e8de7 --- /dev/null +++ b/examples/abc121-b-proconio.rs @@ -0,0 +1,19 @@ +// https://atcoder.jp/contests/abc121/tasks/abc121_b + +use proconio::input; + +fn main() { + input! { + n: usize, + m: usize, + c: i32, + b: [i32; m], + a: [[i32; m]; n], + } + + let ans = a + .into_iter() + .filter(|a| a.iter().zip(&b).map(|(a, b)| a * b).sum::() + c > 0) + .count(); + println!("{}", ans); +} diff --git a/examples/abc121-b-text-io.rs b/examples/abc121-b-text-io.rs new file mode 100644 index 0000000..3c7ab2d --- /dev/null +++ b/examples/abc121-b-text-io.rs @@ -0,0 +1,18 @@ +// https://atcoder.jp/contests/abc121/tasks/abc121_b + +use text_io::{read, try_read, try_scan}; + +#[allow(clippy::many_single_char_names, clippy::try_err)] +fn main() { + let (n, m, c): (usize, usize, i32) = (read!(), read!(), read!()); + let b = (0..m).map(|_| read!()).collect::>(); + let a = (0..n) + .map(|_| (0..m).map(|_| read!()).collect()) + .collect::>>(); + + let ans = a + .into_iter() + .filter(|a| a.iter().zip(&b).map(|(a, b)| a * b).sum::() + c > 0) + .count(); + println!("{}", ans); +} diff --git a/examples/abc121-b-whiteread.rs b/examples/abc121-b-whiteread.rs new file mode 100644 index 0000000..7648be9 --- /dev/null +++ b/examples/abc121-b-whiteread.rs @@ -0,0 +1,19 @@ +// https://atcoder.jp/contests/abc121/tasks/abc121_b + +use whiteread::Reader; + +fn main() { + let mut rdr = Reader::from_stdin_naive(); + + let (n, _, c) = rdr.p::<(usize, usize, i32)>(); + let b = rdr.line::>().unwrap(); + let a = (0..n) + .map(|_| rdr.line().unwrap()) + .collect::>>(); + + let ans = a + .into_iter() + .filter(|a| a.iter().zip(&b).map(|(a, b)| a * b).sum::() + c > 0) + .count(); + println!("{}", ans); +} diff --git a/examples/abc129-f.rs b/examples/abc129-f.rs new file mode 100644 index 0000000..53e5d8b --- /dev/null +++ b/examples/abc129-f.rs @@ -0,0 +1,118 @@ +// https://atcoder.jp/contests/abc129/tasks/abc129_f + +use ndarray::{array, Array2, LinalgScalar}; +use num::{PrimInt, Unsigned}; +use num_derive::{One, Zero}; +use proconio::input; +use proconio::source::{Readable, Source}; + +use std::cell::Cell; +use std::io::BufRead; +use std::ops::{Add, Div, Mul, Sub}; +use std::{cmp, fmt}; + +fn main() { + input! { + l: u64, + a: u64, + b: u64, + _m: Mod, + } + + let count = |d| -> _ { + let count = + |above: u64| cmp::min(above.saturating_sub(a + 1) / b + u64::from(b < above), l); + count(10u64.pow(d)) - count(10u64.pow(d - 1)) + }; + + let ans = (1..=18).fold(array![[Z::new(0), Z::new(a), Z::new(1)]], |acc, d| { + acc.dot( + &array![ + [Z::new(10u64.pow(d)), Z::new(0), Z::new(0)], + [Z::new(1), Z::new(1), Z::new(0)], + [Z::new(0), Z::new(b), Z::new(1)], + ] + .matrix_power(count(d)), + ) + })[(0, 0)]; + println!("{}", ans); +} + +trait Array2Ext { + fn matrix_power(&self, exp: E) -> Self; +} + +impl Array2Ext for Array2 { + fn matrix_power(&self, exp: E) -> Self { + let (mut base, mut exp, mut acc) = (self.clone(), exp, Self::eye(self.nrows())); + while exp > E::zero() { + if (exp & E::one()) == E::one() { + acc = acc.dot(&base); + } + exp = exp / (E::one() + E::one()); + base = base.dot(&base); + } + acc + } +} + +thread_local! { + static MOD: Cell = Cell::new(0); +} + +enum Mod {} + +impl Readable for Mod { + type Output = (); + + fn read>(source: &mut S) { + MOD.with(|cell| cell.set(u64::read(source))); + } +} + +#[derive(Zero, One, Debug, Clone, Copy)] +struct Z(u64); + +impl Z { + fn new(val: u64) -> Self { + Self(val % MOD.with(Cell::get)) + } +} + +impl fmt::Display for Z { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(&self.0, fmt) + } +} + +impl Add for Z { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + Self::new(self.0 + rhs.0) + } +} + +impl Sub for Z { + type Output = Self; + + fn sub(self, _: Self) -> Self { + unreachable!("should not be performed") + } +} + +impl Mul for Z { + type Output = Self; + + fn mul(self, rhs: Self) -> Self { + Self::new(self.0 * rhs.0) + } +} + +impl Div for Z { + type Output = Self; + + fn div(self, _: Self) -> Self { + unreachable!("should not be performed") + } +} diff --git a/examples/abc141-c.rs b/examples/abc141-c.rs new file mode 100644 index 0000000..5cc7bc8 --- /dev/null +++ b/examples/abc141-c.rs @@ -0,0 +1,22 @@ +// https://atcoder.jp/contests/abc141/tasks/abc141_c + +use proconio::marker::Usize1; +use proconio::{fastout, input}; + +#[fastout] +fn main() { + input! { + n: usize, + k: usize, + q: usize, + a: [Usize1; q], + } + + let mut correct = vec![0; n]; + a.into_iter().for_each(|a| correct[a] += 1); + + for correct in correct { + let p = k + correct > q; + println!("{}", if p { "Yes" } else { "No" }); + } +} diff --git a/examples/abc142-c.rs b/examples/abc142-c.rs new file mode 100644 index 0000000..c92f705 --- /dev/null +++ b/examples/abc142-c.rs @@ -0,0 +1,15 @@ +// https://atcoder.jp/contests/abc142/tasks/abc142_c + +use itertools::Itertools as _; +use proconio::input; +use proconio::marker::Isize1; +use superslice::Ext2 as _; + +fn main() { + input! { + mut a: [Isize1], + } + + a.invert_permutation(); + println!("{}", a.iter().map(|a| a + 1).format(" ")); +} diff --git a/examples/abc142-d.rs b/examples/abc142-d.rs deleted file mode 100644 index a8fdb4e..0000000 --- a/examples/abc142-d.rs +++ /dev/null @@ -1,53 +0,0 @@ -// https://atcoder.jp/contests/abc142/tasks/abc142_d - -use primal::Sieve; - -use std::cmp::max; -use std::collections::HashSet; - -fn main() { - // use std::io::{self, Read as _}; - // - // let mut input = "".to_owned(); - // io::stdin().read_to_string(&mut input).unwrap(); - // let mut input = input.split_whitespace(); - // macro_rules! read { - // ([$t:tt; $n:expr]) => { - // (0..$n).map(|_| read!($t)).collect::>() - // }; - // (($($t:tt),+)) => { - // ($(read!($t)),*) - // }; - // (_1based) => { - // read!(usize) - 1 - // }; - // (_bytes) => { - // read!(String).into_bytes() - // }; - // ($ty:ty) => { - // input.next().unwrap().parse::<$ty>().unwrap() - // }; - // } - // - // let (n, m) = read!((usize, usize)); - - use proconio::input; - - input! { - a: usize, - b: usize, - } - - // サンプルケースでしか試してないので嘘かもしれない。 - - let sieve = Sieve::new(num_integer::sqrt(max(a, b))); - let bases = |k| -> HashSet<_> { - sieve - .factor(k) - .unwrap() - .into_iter() - .map(|(p, _)| p) - .collect() - }; - println!("{}", (&bases(a) & &bases(b)).len() + 1); -} diff --git a/examples/abc144-d.rs b/examples/abc144-d.rs new file mode 100644 index 0000000..532d589 --- /dev/null +++ b/examples/abc144-d.rs @@ -0,0 +1,21 @@ +// https://atcoder.jp/contests/abc144/tasks/abc144_d + +use proconio::input; + +use std::f64::consts::PI; + +fn main() { + input! { + a: f64, + b: f64, + x: f64, + } + + let ans = 180.0 / PI + * if x >= (a.powi(2) * b) / 2.0 { + libm::atan2(2.0 * (a.powi(2) * b - x), a.powi(3)) + } else { + PI / 2.0 - libm::atan2(2.0 * x, a * b.powi(2)) + }; + println!("{}", ans); +} diff --git a/examples/abc150-d.rs b/examples/abc150-d.rs new file mode 100644 index 0000000..136d828 --- /dev/null +++ b/examples/abc150-d.rs @@ -0,0 +1,21 @@ +// https://atcoder.jp/contests/abc150/tasks/abc150_d + +use itertools::Itertools as _; +use proconio::input; + +fn main() { + input! { + n: usize, + m: usize, + a: [usize; n], + } + + if !a.iter().copied().map(usize::trailing_zeros).all_equal() { + println!("0"); + return; + } + + let x0 = a.into_iter().fold(1, num::integer::lcm) / 2; + let ans = (m + x0) / (2 * x0); + println!("{}", ans); +} diff --git a/examples/abc151-d.rs b/examples/abc151-d.rs new file mode 100644 index 0000000..a5a0e75 --- /dev/null +++ b/examples/abc151-d.rs @@ -0,0 +1,58 @@ +// https://atcoder.jp/contests/abc151/tasks/abc151_d + +use ndarray::Array; +use proconio::input; +use proconio::marker::Bytes; +use smallvec::SmallVec; + +use std::{iter, mem}; + +fn main() { + input! { + h: usize, + w: usize, + sss: [Bytes; h], + } + + let maze = Array::from_shape_vec((h, w), itertools::concat(sss)) + .unwrap() + .map(|&c| c == b'.'); + + let neighbors = Array::from_shape_fn((h, w), |(i, j)| { + let mut neighbors = SmallVec::<[_; 4]>::new(); + macro_rules! push((if $cond:expr => $pos:expr) => { + if $cond && maze[$pos] { + neighbors.push($pos); + } + }); + push!(if 0 < i => (i - 1, j)); + push!(if i < h - 1 => (i + 1, j)); + push!(if 0 < j => (i, j - 1)); + push!(if j < w - 1 => (i, j + 1)); + neighbors + }); + + let ans = (0..h) + .flat_map(|i| (0..w).map(move |j| (i, j))) + .filter(|&p| maze[p]) + .map(|start| { + let mut queue = vec![start]; + let mut unvisited = maze.clone(); + unvisited[start] = false; + + iter::repeat(()) + .take_while(|_| { + queue = queue + .iter() + .flat_map(|&p| &neighbors[p]) + .copied() + .filter(|&p| mem::replace(&mut unvisited[p], false)) + .collect(); + !queue.is_empty() + }) + .count() + }) + .max() + .unwrap(); + println!("{}", ans); +} diff --git a/examples/apg4b-a.rs b/examples/apg4b-a.rs index ef23780..6391d14 100644 --- a/examples/apg4b-a.rs +++ b/examples/apg4b-a.rs @@ -1,16 +1,11 @@ -use aho_corasick as _; +// https://atcoder.jp/contests/APG4b/tasks/APG4b_a + use alga as _; -use approx as _; use ascii as _; use bitset_fixed as _; -use defmac as _; -use derive_more as _; -use derive_new as _; use either as _; -use euclid as _; use fixedbitset as _; use getrandom as _; -use if_chain as _; use im_rc as _; use indexmap as _; use itertools as _; @@ -18,12 +13,8 @@ use itertools_num as _; use lazy_static as _; use libm as _; use maplit as _; -#[allow(unused_imports)] -use matches::matches as _; // https://github.com/rust-lang/rust/issues/66518 -use modtype as _; use nalgebra as _; use ndarray as _; -use nom as _; use num as _; use num_bigint as _; use num_complex as _; @@ -35,10 +26,6 @@ use num_traits as _; use ordered_float as _; use permutohedron as _; use petgraph as _; -use primal as _; -use primal_check as _; -use primal_estimate as _; -use primal_sieve as _; use proconio as _; use rand as _; use rand_chacha as _; @@ -49,18 +36,10 @@ use rand_pcg as _; use regex as _; use rustc_hash as _; use smallvec as _; -use strsim as _; use superslice as _; -use take_mut as _; use text_io as _; -use union_find as _; use whiteread as _; -#[cfg(feature = "jemalloc-ctl")] -use jemalloc_ctl as _; -#[cfg(feature = "jemallocator")] -use jemallocator as _; - fn main() { println!("Hello, world!"); } diff --git a/examples/apg4b-ex25.rs b/examples/apg4b-ex25.rs index a19484e..b489293 100644 --- a/examples/apg4b-ex25.rs +++ b/examples/apg4b-ex25.rs @@ -2,45 +2,13 @@ use fixedbitset::FixedBitSet; use itertools::Itertools as _; +use proconio::input; +#[allow(clippy::many_single_char_names)] fn main() { - // use std::io::{self, Read as _}; - // - // let mut input = "".to_owned(); - // io::stdin().read_to_string(&mut input).unwrap(); - // let mut input = input.split_whitespace(); - // macro_rules! read { - // ([$t:tt; $n:expr]) => { - // (0..$n).map(|_| read!($t)).collect::>() - // }; - // (($($t:tt),+)) => { - // ($(read!($t)),*) - // }; - // (_1based) => { - // read!(usize) - 1 - // }; - // (_bytes) => { - // read!(String).into_bytes() - // }; - // ($ty:ty) => { - // input.next().unwrap().parse::<$ty>().unwrap() - // }; - // } - // - // let n = read!(usize); - // let a = read!([usize; n]); - // let m = read!(usize); - // let b = read!([usize; m]); - // let arg0 = read!(String); - // let args = read!([usize; if arg0 == "subtract" { 1 } else { 0 }]); - - use proconio::input; - input! { - n: usize, - a: [usize; n], - m: usize, - b: [usize; m], + a: [usize], + b: [usize], arg0: String, args: [usize; if arg0 == "subtract" { 1 } else { 0 }], } @@ -75,7 +43,6 @@ fn symmetric_diff(a: &FixedBitSet, b: &FixedBitSet) -> FixedBitSet { } fn subtract(mut a: FixedBitSet, x: usize) -> FixedBitSet { - // > xは存在することが保証される。 a.set(x, false); a } diff --git a/examples/apg4b-ex26.rs b/examples/apg4b-ex26.rs deleted file mode 100644 index ecc0c9a..0000000 --- a/examples/apg4b-ex26.rs +++ /dev/null @@ -1,235 +0,0 @@ -// https://atcoder.jp/contests/APG4b/tasks/APG4b_bw - -use itertools::Itertools as _; -use maplit::hashmap; -use matches::matches; - -use std::collections::HashMap; -use std::io::{self, Read as _}; -use std::str::FromStr; - -fn main() { - let mut input = "".to_owned(); - io::stdin().read_to_string(&mut input).unwrap(); - let mut env = hashmap!(); - for line in input.lines().skip(1) { - line.parse::().unwrap().eval(&mut env); - } -} - -#[derive(Debug)] -enum Stmt { - DeclInt(char, IntExpr), - PrintInt(IntExpr), - DeclVec(char, VecExpr), - PrintVec(VecExpr), -} - -impl Stmt { - fn eval(&self, env: &mut HashMap) { - match self { - Self::DeclInt(name, expr) => { - env.insert(*name, Val::Int(expr.eval(env))); - } - Self::PrintInt(expr) => println!("{}", expr.eval(env)), - Self::DeclVec(name, expr) => { - env.insert(*name, Val::Vec(expr.eval(env))); - } - Self::PrintVec(expr) => println!("[ {} ]", expr.eval(env).iter().format(" ")), - } - } -} - -impl FromStr for Stmt { - type Err = (String, usize, nom::error::ErrorKind); - - fn from_str(input: &str) -> Result { - use nom::branch::alt; - use nom::bytes::complete::{tag, take_while_m_n}; - use nom::character::complete::{char, one_of, space0, space1}; - use nom::combinator::{map, map_res}; - use nom::multi::{fold_many0, separated_list}; - use nom::sequence::{pair, preceded, tuple}; - use nom::IResult; - - fn decl_int(input: &str) -> IResult<&str, Stmt> { - let (input, _) = space0(input)?; - let (input, _) = tag("int")(input)?; - let (input, _) = space1(input)?; - let (input, name) = var_name(input)?; - let (input, _) = space0(input)?; - let (input, _) = tag("=")(input)?; - let (input, _) = space0(input)?; - let (input, expr) = int_expr(input)?; - let (input, _) = space0(input)?; - let (input, _) = char(';')(input)?; - Ok((input, Stmt::DeclInt(name, expr))) - } - - fn print_int(input: &str) -> IResult<&str, Stmt> { - let (input, _) = space0(input)?; - let (input, _) = tag("print_int")(input)?; - let (input, _) = space1(input)?; - let (input, expr) = int_expr(input)?; - let (input, _) = space0(input)?; - let (input, _) = tag(";")(input)?; - Ok((input, Stmt::PrintInt(expr))) - } - - fn decl_vec(input: &str) -> IResult<&str, Stmt> { - let (input, _) = space0(input)?; - let (input, _) = tag("vec")(input)?; - let (input, _) = space1(input)?; - let (input, name) = var_name(input)?; - let (input, _) = space0(input)?; - let (input, _) = char('=')(input)?; - let (input, _) = space0(input)?; - let (input, val) = vec_expr(input)?; - let (input, _) = space0(input)?; - let (input, _) = char(';')(input)?; - Ok((input, Stmt::DeclVec(name, val))) - } - - fn print_vec(input: &str) -> IResult<&str, Stmt> { - let (input, _) = space0(input)?; - let (input, _) = tag("print_vec")(input)?; - let (input, _) = space1(input)?; - let (input, val) = vec_expr(input)?; - let (input, _) = space0(input)?; - let (input, _) = char(';')(input)?; - Ok((input, Stmt::PrintVec(val))) - } - - fn int_expr(input: &str) -> IResult<&str, IntExpr> { - let (input, expr) = int_term(input)?; - fold_many0( - preceded(space0, pair(one_of("+-"), preceded(space0, int_term))), - expr, - |expr, (op, term)| match op { - '+' => IntExpr::Add(Box::new(expr), Box::new(term)), - '-' => IntExpr::Sub(Box::new(expr), Box::new(term)), - _ => unreachable!(), - }, - )(input) - } - - fn int_term(input: &str) -> IResult<&str, IntExpr> { - let (input, _) = space0(input)?; - alt(( - map(var_name, IntExpr::Var), - map( - take_while_m_n::<_, &str, _>(1, 1, |c| matches!(c, '0'..='9')), - |s| IntExpr::Lit(s.parse().unwrap()), - ), - ))(input) - } - - fn vec_expr(input: &str) -> IResult<&str, VecExpr> { - let (input, expr) = vec_term(input)?; - fold_many0( - preceded(space0, pair(one_of("+-"), preceded(space0, vec_term))), - expr, - |expr, (op, term)| match op { - '+' => VecExpr::Add(Box::new(expr), Box::new(term)), - '-' => VecExpr::Sub(Box::new(expr), Box::new(term)), - _ => unreachable!(), - }, - )(input) - } - - fn vec_term(input: &str) -> IResult<&str, VecExpr> { - let (input, _) = space0(input)?; - alt((map(var_name, VecExpr::Var), |input| { - let (input, _) = char('[')(input)?; - let (input, _) = space0(input)?; - let (input, vec) = - separated_list(tuple((space0, char(','), space0)), int_expr)(input)?; - let (input, _) = space0(input)?; - let (input, _) = char(']')(input)?; - Ok((input, VecExpr::Lit(vec))) - }))(input) - } - - fn var_name(input: &str) -> IResult<&str, char> { - map_res(take_while_m_n(1, 1, |c| matches!(c, 'a'..='z')), str::parse)(input) - } - - decl_int(input) - .or_else(|_| print_int(input)) - .or_else(|_| decl_vec(input)) - .or_else(|_| print_vec(input)) - .map(|(_, stmt)| stmt) - .map_err(|err| match err { - nom::Err::Incomplete(_) => unreachable!(), - nom::Err::Error((s, k)) | nom::Err::Failure((s, k)) => { - (input.to_owned(), input.len() - s.len(), k) - } - }) - } -} - -#[derive(Debug)] -enum Val { - Int(i32), - Vec(Vec), -} - -impl Val { - fn unwrap_int(&self) -> i32 { - match self { - Self::Int(n) => *n, - Self::Vec(_) => panic!(), - } - } - - fn unwrap_vec(&self) -> &[i32] { - match self { - Self::Int(_) => panic!(), - Self::Vec(vec) => vec, - } - } -} - -#[derive(Debug, Clone)] -enum IntExpr { - Lit(i32), - Var(char), - Add(Box, Box), - Sub(Box, Box), -} - -impl IntExpr { - fn eval(&self, env: &HashMap) -> i32 { - match self { - Self::Lit(n) => *n, - Self::Var(s) => env[s].unwrap_int(), - Self::Add(l, r) => l.eval(env) + r.eval(env), - Self::Sub(l, r) => l.eval(env) - r.eval(env), - } - } -} - -#[derive(Debug, Clone)] -enum VecExpr { - Lit(Vec), - Var(char), - Add(Box, Box), - Sub(Box, Box), -} - -impl VecExpr { - fn eval(&self, env: &HashMap) -> Vec { - match self { - Self::Lit(v) => v.iter().map(|x| x.eval(env)).collect(), - Self::Var(s) => env[s].unwrap_vec().to_owned(), - Self::Add(l, r) => { - let (l, r) = (l.eval(env), r.eval(env)); - l.into_iter().zip_eq(r).map(|(l, r)| l + r).collect() - } - Self::Sub(l, r) => { - let (l, r) = (l.eval(env), r.eval(env)); - l.into_iter().zip_eq(r).map(|(l, r)| l - r).collect() - } - } - } -} diff --git a/examples/arc065-c.rs b/examples/arc065-c.rs index 46eb475..fc8dfb2 100644 --- a/examples/arc065-c.rs +++ b/examples/arc065-c.rs @@ -1,38 +1,11 @@ // https://atcoder.jp/contests/arc065/tasks/arc065_a use lazy_static::lazy_static; +use proconio::input; +use proconio::marker::Bytes; use regex::bytes::Regex; -#[proconio::fastout] fn main() { - // use std::io::{self, Read as _}; - // - // let mut input = "".to_owned(); - // io::stdin().read_to_string(&mut input).unwrap(); - // let mut input = input.split_whitespace(); - // macro_rules! read { - // ([$t:tt; $n:expr]) => { - // (0..$n).map(|_| read!($t)).collect::>() - // }; - // (($($t:tt),+)) => { - // ($(read!($t)),*) - // }; - // (_1based) => { - // read!(usize) - 1 - // }; - // (_bytes) => { - // read!(String).into_bytes() - // }; - // ($ty:ty) => { - // input.next().unwrap().parse::<$ty>().unwrap() - // }; - // } - // - // let s = read!(_bytes); - - use proconio::input; - use proconio::marker::Bytes; - input! { s: Bytes, } diff --git a/examples/arc084-c.rs b/examples/arc084-c.rs new file mode 100644 index 0000000..8120651 --- /dev/null +++ b/examples/arc084-c.rs @@ -0,0 +1,21 @@ +// https://atcoder.jp/contests/arc084/tasks/arc084_a + +use proconio::input; +use superslice::Ext as _; + +fn main() { + input! { + n: usize, + mut a: [u32; n], + b: [u32; n], + mut c: [u32; n], + } + + a.sort(); + c.sort(); + let ans = b + .into_iter() + .map(|b| a.lower_bound(&b) * (n - c.upper_bound(&b))) + .sum::(); + println!("{}", ans); +} diff --git a/examples/atc001-b.rs b/examples/atc001-b.rs index f630b23..5caa617 100644 --- a/examples/atc001-b.rs +++ b/examples/atc001-b.rs @@ -1,44 +1,21 @@ -use petgraph::unionfind::UnionFind; +// https://atcoder.jp/contests/atc001/tasks/unionfind_a -#[proconio::fastout] -fn main() { - // use std::io::{self, Read as _}; - // - // let mut input = "".to_owned(); - // io::stdin().read_to_string(&mut input).unwrap(); - // let mut input = input.split_whitespace(); - // macro_rules! read { - // ([$t:tt; $n:expr]) => { - // (0..$n).map(|_| read!($t)).collect::>() - // }; - // (($($t:tt),+)) => { - // ($(read!($t)),*) - // }; - // (_1based) => { - // read!(usize) - 1 - // }; - // (_bytes) => { - // read!(String).into_bytes() - // }; - // ($ty:ty) => { - // input.next().unwrap().parse::<$ty>().unwrap() - // }; - // } - // - // let (n, q) = read!((usize, usize)); - // let pabs = read!([(u8, usize, usize); q]); +use petgraph::unionfind::UnionFind; +use proconio::source::{Readable, Source}; +use proconio::{fastout, input}; - use proconio::input; +use std::io::BufRead; +#[fastout] +fn main() { input! { n: usize, - q: usize, - pabs: [(u8, usize, usize); q], + pabs: [(ZeroOne, usize, usize)], } let mut uf = UnionFind::new(n); for (p, a, b) in pabs { - if p == 1 { + if p { let same = uf.find(a) == uf.find(b); println!("{}", if same { "Yes" } else { "No" }); } else { @@ -46,3 +23,13 @@ fn main() { } } } + +enum ZeroOne {} + +impl Readable for ZeroOne { + type Output = bool; + + fn read>(source: &mut S) -> bool { + u32::read(source) == 1 + } +} diff --git a/examples/atc002-b.rs b/examples/atc002-b.rs index f2a2a7b..25a4b09 100644 --- a/examples/atc002-b.rs +++ b/examples/atc002-b.rs @@ -1,24 +1,9 @@ // https://atcoder.jp/contests/atc002/tasks/atc002_b use num::BigUint; +use proconio::input; -#[proconio::fastout] fn main() { - // use defmac::defmac; - // - // use std::io::{self, Read as _}; - // - // let mut input = "".to_owned(); - // io::stdin().read_to_string(&mut input).unwrap(); - // let mut input = input.split_whitespace(); - // defmac!(read => input.next().unwrap().parse().unwrap()); - // - // let n: BigUint = read!(); - // let m: BigUint = read!(); - // let p: BigUint = read!(); - - use proconio::input; - input! { n: BigUint, m: BigUint, diff --git a/examples/practice-a-naive.rs b/examples/practice-a-naive.rs new file mode 100644 index 0000000..c16b51c --- /dev/null +++ b/examples/practice-a-naive.rs @@ -0,0 +1,14 @@ +// https://atcoder.jp/contests/practice/tasks/practice_1 + +use std::io::{self, Read as _}; + +fn main() { + let mut input = "".to_owned(); + io::stdin().read_to_string(&mut input).unwrap(); + let mut input = input.split_whitespace(); + macro_rules! read(() => (input.next().unwrap().parse().unwrap())); + + let (a, b, c, s): (u32, u32, u32, String) = (read!(), read!(), read!(), read!()); + + println!("{} {}", a + b + c, s); +} diff --git a/examples/practice-a-proconio.rs b/examples/practice-a-proconio.rs new file mode 100644 index 0000000..d7b635e --- /dev/null +++ b/examples/practice-a-proconio.rs @@ -0,0 +1,14 @@ +// https://atcoder.jp/contests/practice/tasks/practice_1 + +use proconio::input; + +fn main() { + input! { + a: u32, + b: u32, + c: u32, + s: String, + } + + println!("{} {}", a + b + c, s); +} diff --git a/examples/practice-a-text-io.rs b/examples/practice-a-text-io.rs new file mode 100644 index 0000000..cc39bde --- /dev/null +++ b/examples/practice-a-text-io.rs @@ -0,0 +1,10 @@ +// https://atcoder.jp/contests/practice/tasks/practice_1 + +use text_io::{read, try_read, try_scan}; + +#[allow(clippy::many_single_char_names, clippy::try_err)] +fn main() { + let (a, b, c, s): (u32, u32, u32, String) = (read!(), read!(), read!(), read!()); + + println!("{} {}", a + b + c, s); +} diff --git a/examples/practice-a-whiteread.rs b/examples/practice-a-whiteread.rs new file mode 100644 index 0000000..16b4031 --- /dev/null +++ b/examples/practice-a-whiteread.rs @@ -0,0 +1,11 @@ +// https://atcoder.jp/contests/practice/tasks/practice_1 + +use whiteread::Reader; + +fn main() { + let mut rdr = Reader::from_stdin_naive(); + + let (a, b, c, s) = rdr.p::<(u32, u32, u32, String)>(); + + println!("{} {}", a + b + c, s); +} diff --git a/examples/practice-a.rs b/examples/practice-a.rs deleted file mode 100644 index 21a65b4..0000000 --- a/examples/practice-a.rs +++ /dev/null @@ -1,26 +0,0 @@ -// https://atcoder.jp/contests/practice/tasks/practice_1 - -#[proconio::fastout] -fn main() { - // use defmac::defmac; - // - // use std::io::{self, Read as _}; - // - // let mut input = "".to_owned(); - // io::stdin().read_to_string(&mut input).unwrap(); - // let mut input = input.split_whitespace(); - // defmac!(read => input.next().unwrap().parse().unwrap()); - // - // let (a, b, c, s): (u32, u32, u32, String) = (read!(), read!(), read!(), read!()); - - use proconio::input; - - input! { - a: u32, - b: u32, - c: u32, - s: String, - } - - println!("{} {}", a + b + c, s); -} diff --git a/examples/practice-b-naive.rs b/examples/practice-b-naive.rs new file mode 100644 index 0000000..e937181 --- /dev/null +++ b/examples/practice-b-naive.rs @@ -0,0 +1,108 @@ +// https://atcoder.jp/contests/practice/tasks/practice_2 + +use maplit::hashset; + +use std::{io, str}; + +fn main() { + fn read_line() -> String { + let mut input = "".to_owned(); + io::stdin().read_line(&mut input).unwrap(); + input + } + + fn query(l: u8, r: u8) -> bool { + println!("? {} {}", l as char, r as char); + read_line() == "<\n" + } + + let n = { + let input = read_line(); + let mut input = input.split_whitespace(); + macro_rules! read(($ty:ty) => (input.next().unwrap().parse::<$ty>().unwrap())); + read!(u32) + }; + + let ans = match n { + 26 => on_26(query), + 5 => on_5(query), + _ => unreachable!(), + }; + println!("! {}", str::from_utf8(&ans).unwrap()); +} + +fn on_26(mut query: impl FnMut(u8, u8) -> bool) -> Vec { + (b'B'..=b'Z').fold(vec![b'A'], |balls, ball| insort(balls, ball, &mut query)) +} + +#[allow(clippy::many_single_char_names)] +fn on_5(mut query: impl FnMut(u8, u8) -> bool) -> Vec { + let (r, s, t, u) = { + let (q1, q2) = (query(b'A', b'B'), query(b'C', b'D')); + let (light1, heavy1) = if q1 { (b'A', b'B') } else { (b'B', b'A') }; + let (light2, heavy2) = if q2 { (b'C', b'D') } else { (b'D', b'C') }; + let q3 = query(light1, light2); + if q3 { + (light1, heavy1, light2, heavy2) + } else { + (light2, heavy2, light1, heavy1) + } + }; + + let v = (&hashset!(b'A', b'B', b'C', b'D', b'E') - &hashset!(r, s, t, u)) + .into_iter() + .next() + .unwrap(); + + let q4 = query(t, v); + if q4 { + let q5 = query(u, v); + let (min_uv, max_uv) = if q5 { (u, v) } else { (v, u) }; + itertools::chain(vec![r], insort(vec![t, min_uv, max_uv], s, &mut query)).collect() + } else { + let q5 = query(r, v); + if q5 { + itertools::chain(vec![r], insort(vec![v, t, u], s, &mut query)).collect() + } else { + itertools::chain(vec![v, r], insort(vec![t, u], s, &mut query)).collect() + } + } +} + +fn insort(mut balls: Vec, ball: u8, mut query: impl FnMut(u8, u8) -> bool) -> Vec { + let (mut min, mut max) = (0, balls.len()); + while min < max { + let mid = (min + max) / 2; + if query(balls[mid], ball) { + min = mid + 1; + } else { + max = mid + }; + } + balls.insert(min, ball); + balls +} + +#[cfg(test)] +mod tests { + use itertools::Itertools as _; + + use std::str; + + #[test] + fn on_5() { + for balls in (b'A'..=b'E').permutations(5) { + let mut queries = 0; + let ans = super::on_5(|l, r| { + queries += 1; + let wl = balls.iter().position(|&b| b == l).unwrap(); + let wr = balls.iter().position(|&b| b == r).unwrap(); + wl < wr + }); + let ans = str::from_utf8(&ans).unwrap(); + let balls = str::from_utf8(&balls).unwrap(); + assert_eq!(ans, balls); + assert!(queries <= 7); + } + } +} diff --git a/examples/practice-b-proconio.rs b/examples/practice-b-proconio.rs new file mode 100644 index 0000000..47f2d9b --- /dev/null +++ b/examples/practice-b-proconio.rs @@ -0,0 +1,109 @@ +// https://atcoder.jp/contests/practice/tasks/practice_2 + +use maplit::hashset; +use proconio::input; +use proconio::source::line::LineSource; + +use std::{io, str}; + +fn main() { + let stdin = io::stdin(); + let mut stdin = stdin.lock(); + + input! { + from LineSource::new(&mut stdin), + n: u32, + } + + let query = |l: u8, r: u8| -> _ { + println!("? {} {}", l as char, r as char); + input! { + from LineSource::new(&mut stdin), + c: char, + } + c == '<' + }; + + let ans = match n { + 26 => on_26(query), + 5 => on_5(query), + _ => unreachable!(), + }; + println!("! {}", str::from_utf8(&ans).unwrap()); +} + +fn on_26(mut query: impl FnMut(u8, u8) -> bool) -> Vec { + (b'B'..=b'Z').fold(vec![b'A'], |balls, ball| insort(balls, ball, &mut query)) +} + +#[allow(clippy::many_single_char_names)] +fn on_5(mut query: impl FnMut(u8, u8) -> bool) -> Vec { + let (r, s, t, u) = { + let (q1, q2) = (query(b'A', b'B'), query(b'C', b'D')); + let (light1, heavy1) = if q1 { (b'A', b'B') } else { (b'B', b'A') }; + let (light2, heavy2) = if q2 { (b'C', b'D') } else { (b'D', b'C') }; + let q3 = query(light1, light2); + if q3 { + (light1, heavy1, light2, heavy2) + } else { + (light2, heavy2, light1, heavy1) + } + }; + + let v = (&hashset!(b'A', b'B', b'C', b'D', b'E') - &hashset!(r, s, t, u)) + .into_iter() + .next() + .unwrap(); + + let q4 = query(t, v); + if q4 { + let q5 = query(u, v); + let (min_uv, max_uv) = if q5 { (u, v) } else { (v, u) }; + itertools::chain(vec![r], insort(vec![t, min_uv, max_uv], s, &mut query)).collect() + } else { + let q5 = query(r, v); + if q5 { + itertools::chain(vec![r], insort(vec![v, t, u], s, &mut query)).collect() + } else { + itertools::chain(vec![v, r], insort(vec![t, u], s, &mut query)).collect() + } + } +} + +fn insort(mut balls: Vec, ball: u8, mut query: impl FnMut(u8, u8) -> bool) -> Vec { + let (mut min, mut max) = (0, balls.len()); + while min < max { + let mid = (min + max) / 2; + if query(balls[mid], ball) { + min = mid + 1; + } else { + max = mid + }; + } + balls.insert(min, ball); + balls +} + +#[cfg(test)] +mod tests { + use itertools::Itertools as _; + + use std::str; + + #[test] + fn on_5() { + for balls in (b'A'..=b'E').permutations(5) { + let mut queries = 0; + let ans = super::on_5(|l, r| { + queries += 1; + let wl = balls.iter().position(|&b| b == l).unwrap(); + let wr = balls.iter().position(|&b| b == r).unwrap(); + wl < wr + }); + let ans = str::from_utf8(&ans).unwrap(); + let balls = str::from_utf8(&balls).unwrap(); + assert_eq!(ans, balls); + assert!(queries <= 7); + } + } +} diff --git a/examples/practice-b-text-io.rs b/examples/practice-b-text-io.rs new file mode 100644 index 0000000..df3960e --- /dev/null +++ b/examples/practice-b-text-io.rs @@ -0,0 +1,108 @@ +// https://atcoder.jp/contests/practice/tasks/practice_2 + +use maplit::hashset; +use text_io::{read, try_read, try_scan}; + +use std::{io, str}; + +#[allow(clippy::try_err)] +fn main() { + fn read_line() -> String { + let mut input = "".to_owned(); + io::stdin().read_line(&mut input).unwrap(); + input + } + + let n: u32 = { + let line = read_line(); + read!("{}", line.bytes()) + }; + + let query = |l: u8, r: u8| -> _ { + println!("? {} {}", l as char, r as char); + read_line() == "<\n" + }; + + let ans = match n { + 26 => on_26(query), + 5 => on_5(query), + _ => unreachable!(), + }; + println!("! {}", str::from_utf8(&ans).unwrap()); +} + +fn on_26(mut query: impl FnMut(u8, u8) -> bool) -> Vec { + (b'B'..=b'Z').fold(vec![b'A'], |balls, ball| insort(balls, ball, &mut query)) +} + +#[allow(clippy::many_single_char_names)] +fn on_5(mut query: impl FnMut(u8, u8) -> bool) -> Vec { + let (r, s, t, u) = { + let (q1, q2) = (query(b'A', b'B'), query(b'C', b'D')); + let (light1, heavy1) = if q1 { (b'A', b'B') } else { (b'B', b'A') }; + let (light2, heavy2) = if q2 { (b'C', b'D') } else { (b'D', b'C') }; + let q3 = query(light1, light2); + if q3 { + (light1, heavy1, light2, heavy2) + } else { + (light2, heavy2, light1, heavy1) + } + }; + + let v = (&hashset!(b'A', b'B', b'C', b'D', b'E') - &hashset!(r, s, t, u)) + .into_iter() + .next() + .unwrap(); + + let q4 = query(t, v); + if q4 { + let q5 = query(u, v); + let (min_uv, max_uv) = if q5 { (u, v) } else { (v, u) }; + itertools::chain(vec![r], insort(vec![t, min_uv, max_uv], s, &mut query)).collect() + } else { + let q5 = query(r, v); + if q5 { + itertools::chain(vec![r], insort(vec![v, t, u], s, &mut query)).collect() + } else { + itertools::chain(vec![v, r], insort(vec![t, u], s, &mut query)).collect() + } + } +} + +fn insort(mut balls: Vec, ball: u8, mut query: impl FnMut(u8, u8) -> bool) -> Vec { + let (mut min, mut max) = (0, balls.len()); + while min < max { + let mid = (min + max) / 2; + if query(balls[mid], ball) { + min = mid + 1; + } else { + max = mid + }; + } + balls.insert(min, ball); + balls +} + +#[cfg(test)] +mod tests { + use itertools::Itertools as _; + + use std::str; + + #[test] + fn on_5() { + for balls in (b'A'..=b'E').permutations(5) { + let mut queries = 0; + let ans = super::on_5(|l, r| { + queries += 1; + let wl = balls.iter().position(|&b| b == l).unwrap(); + let wr = balls.iter().position(|&b| b == r).unwrap(); + wl < wr + }); + let ans = str::from_utf8(&ans).unwrap(); + let balls = str::from_utf8(&balls).unwrap(); + assert_eq!(ans, balls); + assert!(queries <= 7); + } + } +} diff --git a/examples/practice-b-whiteread.rs b/examples/practice-b-whiteread.rs new file mode 100644 index 0000000..0f93295 --- /dev/null +++ b/examples/practice-b-whiteread.rs @@ -0,0 +1,100 @@ +// https://atcoder.jp/contests/practice/tasks/practice_2 + +use maplit::hashset; +use whiteread::Reader; + +use std::str; + +fn main() { + let mut rdr = Reader::from_stdin_naive(); + + let (n, _) = rdr.line::<(u32, u32)>().unwrap(); + + let query = |l: u8, r: u8| -> _ { + println!("? {} {}", l as char, r as char); + rdr.p::() == '<' + }; + + let ans = match n { + 26 => on_26(query), + 5 => on_5(query), + _ => unreachable!(), + }; + println!("! {}", str::from_utf8(&ans).unwrap()); +} + +fn on_26(mut query: impl FnMut(u8, u8) -> bool) -> Vec { + (b'B'..=b'Z').fold(vec![b'A'], |balls, ball| insort(balls, ball, &mut query)) +} + +#[allow(clippy::many_single_char_names)] +fn on_5(mut query: impl FnMut(u8, u8) -> bool) -> Vec { + let (r, s, t, u) = { + let (q1, q2) = (query(b'A', b'B'), query(b'C', b'D')); + let (light1, heavy1) = if q1 { (b'A', b'B') } else { (b'B', b'A') }; + let (light2, heavy2) = if q2 { (b'C', b'D') } else { (b'D', b'C') }; + let q3 = query(light1, light2); + if q3 { + (light1, heavy1, light2, heavy2) + } else { + (light2, heavy2, light1, heavy1) + } + }; + + let v = (&hashset!(b'A', b'B', b'C', b'D', b'E') - &hashset!(r, s, t, u)) + .into_iter() + .next() + .unwrap(); + + let q4 = query(t, v); + if q4 { + let q5 = query(u, v); + let (min_uv, max_uv) = if q5 { (u, v) } else { (v, u) }; + itertools::chain(vec![r], insort(vec![t, min_uv, max_uv], s, &mut query)).collect() + } else { + let q5 = query(r, v); + if q5 { + itertools::chain(vec![r], insort(vec![v, t, u], s, &mut query)).collect() + } else { + itertools::chain(vec![v, r], insort(vec![t, u], s, &mut query)).collect() + } + } +} + +fn insort(mut balls: Vec, ball: u8, mut query: impl FnMut(u8, u8) -> bool) -> Vec { + let (mut min, mut max) = (0, balls.len()); + while min < max { + let mid = (min + max) / 2; + if query(balls[mid], ball) { + min = mid + 1; + } else { + max = mid + }; + } + balls.insert(min, ball); + balls +} + +#[cfg(test)] +mod tests { + use itertools::Itertools as _; + + use std::str; + + #[test] + fn on_5() { + for balls in (b'A'..=b'E').permutations(5) { + let mut queries = 0; + let ans = super::on_5(|l, r| { + queries += 1; + let wl = balls.iter().position(|&b| b == l).unwrap(); + let wr = balls.iter().position(|&b| b == r).unwrap(); + wl < wr + }); + let ans = str::from_utf8(&ans).unwrap(); + let balls = str::from_utf8(&balls).unwrap(); + assert_eq!(ans, balls); + assert!(queries <= 7); + } + } +} diff --git a/examples/sumitrust2019-c.rs b/examples/sumitrust2019-c.rs new file mode 100644 index 0000000..2c3be0c --- /dev/null +++ b/examples/sumitrust2019-c.rs @@ -0,0 +1,20 @@ +// https://atcoder.jp/contests/sumitrust2019/tasks/sumitb2019_c + +use fixedbitset::FixedBitSet; +use proconio::input; + +fn main() { + input! { + x: usize, + } + + let mut dp = FixedBitSet::with_capacity(x + 105); + dp.insert(0); + for i in 0..=x.saturating_sub(100) { + if dp[i] { + // `insert_range` does not accept `RangeInclusive`. + dp.insert_range(i + 100..i + 106); + } + } + println!("{}", u32::from(dp[x])); +} diff --git a/examples/testers/practice-b.py b/examples/testers/practice-b.py new file mode 100644 index 0000000..00a5ffb --- /dev/null +++ b/examples/testers/practice-b.py @@ -0,0 +1,58 @@ +import itertools +import random +import string +from argparse import ArgumentParser +from subprocess import Popen, PIPE +from typing import List + + +def main() -> None: + parser = ArgumentParser() + parser.add_argument('bin') + + binary = parser.parse_args().bin + + for _ in range(100): + judge(binary, ''.join(random.sample(string.ascii_uppercase, 26))) + + for balls in itertools.permutations(string.ascii_uppercase[:5]): + judge(binary, ''.join(balls)) + + +def judge(binary: str, balls: str) -> None: + n = len(balls) + q = 7 if n == 5 else 100 + + with Popen([binary], stdin=PIPE, stdout=PIPE) as proc: + def read_words() -> List[str]: + return proc.stdout.readline().decode('utf-8').split() + + def on_query(c1: str, c2: str) -> None: + reply = '<' if balls.index(c1) < balls.index(c2) else '>' + proc.stdin.write(f'{reply}\n'.encode('utf-8')) + proc.stdin.flush() + + def on_answer(ans: str) -> None: + if ans != balls: + raise Exception('wrong answer') + + proc.stdin.write(f'{n} {q}\n'.encode('utf-8')) + proc.stdin.flush() + + for _ in range(q): + words = read_words() + if len(words) == 3 and words[0] == '?': + on_query(words[1], words[2]) + elif len(words) == 2 and words[0] == '!': + return on_answer(words[1]) + else: + raise Exception('invalid') + else: + words = read_words() + if len(words) == 2 and words[0] == '!': + return on_answer(words[1]) + raise Exception('answer me') + + +if __name__ == '__main__': + main() diff --git a/examples/tests.ron b/examples/tests.ron deleted file mode 100644 index c96e7e5..0000000 --- a/examples/tests.ron +++ /dev/null @@ -1,51 +0,0 @@ -// 手元でDropboxのテストケース(ある場合)で確認し、Gitに含めるのはそのうちサンプルのみ。 -// CI上でもサンプルだけテストする。(Dropboxから引っ張るのも面倒だし何より誰のアカウントを使うかという問題がある) - -( - tests: { - "apg4b-a": ( - name: "APG4b: A - 1.00.はじめに", - matching: ExactWhole, - ), - "apg4b-ex25": ( - name: "APG4b: EX25 - 集合の操作 / 3.05", - matching: ExactWords, - ), - "apg4b-ex26": ( - name: "APG4b: EX26 - 電卓を作ろう3 / 3.06", - matching: ExactWords, - ), - "practice-a": ( - name: "practice contest: A - Welcome to AtCoder", - matching: ExactWords, - ), - "atc001-b": ( - name: "ATC001: B - Union Find", - matching: ExactWords, - ), - "atc002-b": ( - name: "ATC002: B - n^p mod m", - matching: ExactWords, - ), - "arc065-c": ( - name: "ABC049 / ARC065: C - 白昼夢 / Daydream", - matching: ExactWords, - ), - "abc054-c": ( - name: "ABC045: C - One-stroke Path", - matching: ExactWords, - ), - "abc084-d": ( - name: "ABC084: D - 2017-like Number", - matching: ExactWords, - ), - "abc120-d": ( - name: "ABC120: D - Decayed Bridges", - matching: ExactWords, - ), - "abc142-d": ( - name: "ABC124: D - Disjoint Set of Common Divisors", - matching: ExactWords, - ), - } -) diff --git a/examples/testsets/abc054-c/in/sample_01.txt b/examples/testsets/abc054-c/in/sample_01.txt deleted file mode 100644 index bc88773..0000000 --- a/examples/testsets/abc054-c/in/sample_01.txt +++ /dev/null @@ -1,4 +0,0 @@ -3 3 -1 2 -1 3 -2 3 diff --git a/examples/testsets/abc054-c/in/sample_02.txt b/examples/testsets/abc054-c/in/sample_02.txt deleted file mode 100644 index 052f07a..0000000 --- a/examples/testsets/abc054-c/in/sample_02.txt +++ /dev/null @@ -1,8 +0,0 @@ -7 7 -1 3 -2 7 -3 4 -4 5 -4 6 -5 6 -6 7 diff --git a/examples/testsets/abc054-c/out/sample_01.txt b/examples/testsets/abc054-c/out/sample_01.txt deleted file mode 100644 index 0cfbf08..0000000 --- a/examples/testsets/abc054-c/out/sample_01.txt +++ /dev/null @@ -1 +0,0 @@ -2 diff --git a/examples/testsets/abc054-c/out/sample_02.txt b/examples/testsets/abc054-c/out/sample_02.txt deleted file mode 100644 index d00491f..0000000 --- a/examples/testsets/abc054-c/out/sample_02.txt +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/examples/testsets/abc084-d/in/sample_01.txt b/examples/testsets/abc084-d/in/sample_01.txt deleted file mode 100644 index 6990404..0000000 --- a/examples/testsets/abc084-d/in/sample_01.txt +++ /dev/null @@ -1,2 +0,0 @@ -1 -3 7 diff --git a/examples/testsets/abc084-d/in/sample_02.txt b/examples/testsets/abc084-d/in/sample_02.txt deleted file mode 100644 index 988378b..0000000 --- a/examples/testsets/abc084-d/in/sample_02.txt +++ /dev/null @@ -1,5 +0,0 @@ -4 -13 13 -7 11 -7 11 -2017 2017 diff --git a/examples/testsets/abc084-d/in/sample_03.txt b/examples/testsets/abc084-d/in/sample_03.txt deleted file mode 100644 index da66f27..0000000 --- a/examples/testsets/abc084-d/in/sample_03.txt +++ /dev/null @@ -1,7 +0,0 @@ -6 -1 53 -13 91 -37 55 -19 51 -73 91 -13 49 diff --git a/examples/testsets/abc084-d/out/sample_01.txt b/examples/testsets/abc084-d/out/sample_01.txt deleted file mode 100644 index 0cfbf08..0000000 --- a/examples/testsets/abc084-d/out/sample_01.txt +++ /dev/null @@ -1 +0,0 @@ -2 diff --git a/examples/testsets/abc084-d/out/sample_02.txt b/examples/testsets/abc084-d/out/sample_02.txt deleted file mode 100644 index 680eb50..0000000 --- a/examples/testsets/abc084-d/out/sample_02.txt +++ /dev/null @@ -1,4 +0,0 @@ -1 -0 -0 -1 diff --git a/examples/testsets/abc084-d/out/sample_03.txt b/examples/testsets/abc084-d/out/sample_03.txt deleted file mode 100644 index 10cd9ca..0000000 --- a/examples/testsets/abc084-d/out/sample_03.txt +++ /dev/null @@ -1,6 +0,0 @@ -4 -4 -1 -1 -1 -2 diff --git a/examples/testsets/abc120-d/in/sample_01.txt b/examples/testsets/abc120-d/in/sample_01.txt deleted file mode 100644 index 8881ee2..0000000 --- a/examples/testsets/abc120-d/in/sample_01.txt +++ /dev/null @@ -1,6 +0,0 @@ -4 5 -1 2 -3 4 -1 3 -2 3 -1 4 diff --git a/examples/testsets/abc120-d/in/sample_02.txt b/examples/testsets/abc120-d/in/sample_02.txt deleted file mode 100644 index 23a7d58..0000000 --- a/examples/testsets/abc120-d/in/sample_02.txt +++ /dev/null @@ -1,6 +0,0 @@ -6 5 -2 3 -1 2 -5 6 -3 4 -4 5 diff --git a/examples/testsets/abc120-d/in/sample_03.txt b/examples/testsets/abc120-d/in/sample_03.txt deleted file mode 100644 index c9fa102..0000000 --- a/examples/testsets/abc120-d/in/sample_03.txt +++ /dev/null @@ -1,2 +0,0 @@ -2 1 -1 2 diff --git a/examples/testsets/abc120-d/out/sample_01.txt b/examples/testsets/abc120-d/out/sample_01.txt deleted file mode 100644 index ce56183..0000000 --- a/examples/testsets/abc120-d/out/sample_01.txt +++ /dev/null @@ -1,5 +0,0 @@ -0 -0 -4 -5 -6 diff --git a/examples/testsets/abc120-d/out/sample_02.txt b/examples/testsets/abc120-d/out/sample_02.txt deleted file mode 100644 index 83dd8d4..0000000 --- a/examples/testsets/abc120-d/out/sample_02.txt +++ /dev/null @@ -1,5 +0,0 @@ -8 -9 -12 -14 -15 diff --git a/examples/testsets/abc120-d/out/sample_03.txt b/examples/testsets/abc120-d/out/sample_03.txt deleted file mode 100644 index d00491f..0000000 --- a/examples/testsets/abc120-d/out/sample_03.txt +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/examples/testsets/abc142-d/in/sample_01.txt b/examples/testsets/abc142-d/in/sample_01.txt deleted file mode 100644 index f86db24..0000000 --- a/examples/testsets/abc142-d/in/sample_01.txt +++ /dev/null @@ -1 +0,0 @@ -12 18 diff --git a/examples/testsets/abc142-d/in/sample_02.txt b/examples/testsets/abc142-d/in/sample_02.txt deleted file mode 100644 index 2b55e52..0000000 --- a/examples/testsets/abc142-d/in/sample_02.txt +++ /dev/null @@ -1 +0,0 @@ -420 660 diff --git a/examples/testsets/abc142-d/in/sample_03.txt b/examples/testsets/abc142-d/in/sample_03.txt deleted file mode 100644 index 381ada1..0000000 --- a/examples/testsets/abc142-d/in/sample_03.txt +++ /dev/null @@ -1 +0,0 @@ -1 2019 diff --git a/examples/testsets/abc142-d/out/sample_01.txt b/examples/testsets/abc142-d/out/sample_01.txt deleted file mode 100644 index 00750ed..0000000 --- a/examples/testsets/abc142-d/out/sample_01.txt +++ /dev/null @@ -1 +0,0 @@ -3 diff --git a/examples/testsets/abc142-d/out/sample_02.txt b/examples/testsets/abc142-d/out/sample_02.txt deleted file mode 100644 index b8626c4..0000000 --- a/examples/testsets/abc142-d/out/sample_02.txt +++ /dev/null @@ -1 +0,0 @@ -4 diff --git a/examples/testsets/abc142-d/out/sample_03.txt b/examples/testsets/abc142-d/out/sample_03.txt deleted file mode 100644 index d00491f..0000000 --- a/examples/testsets/abc142-d/out/sample_03.txt +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/examples/testsets/apg4b-a/in/sample_01.txt b/examples/testsets/apg4b-a/in/sample_01.txt deleted file mode 100644 index e69de29..0000000 diff --git a/examples/testsets/apg4b-a/out/sample_01.txt b/examples/testsets/apg4b-a/out/sample_01.txt deleted file mode 100644 index af5626b..0000000 --- a/examples/testsets/apg4b-a/out/sample_01.txt +++ /dev/null @@ -1 +0,0 @@ -Hello, world! diff --git a/examples/testsets/apg4b-ex25/in/sample_01.txt b/examples/testsets/apg4b-ex25/in/sample_01.txt deleted file mode 100644 index 72d1a5d..0000000 --- a/examples/testsets/apg4b-ex25/in/sample_01.txt +++ /dev/null @@ -1,5 +0,0 @@ -3 -0 1 2 -3 -1 2 3 -intersection diff --git a/examples/testsets/apg4b-ex25/in/sample_02.txt b/examples/testsets/apg4b-ex25/in/sample_02.txt deleted file mode 100644 index 975f08a..0000000 --- a/examples/testsets/apg4b-ex25/in/sample_02.txt +++ /dev/null @@ -1,5 +0,0 @@ -3 -0 1 2 -3 -1 2 3 -union_set diff --git a/examples/testsets/apg4b-ex25/in/sample_03.txt b/examples/testsets/apg4b-ex25/in/sample_03.txt deleted file mode 100644 index cc0e508..0000000 --- a/examples/testsets/apg4b-ex25/in/sample_03.txt +++ /dev/null @@ -1,5 +0,0 @@ -3 -0 1 2 -3 -1 2 3 -symmetric_diff diff --git a/examples/testsets/apg4b-ex25/in/sample_04.txt b/examples/testsets/apg4b-ex25/in/sample_04.txt deleted file mode 100644 index c5ee91b..0000000 --- a/examples/testsets/apg4b-ex25/in/sample_04.txt +++ /dev/null @@ -1,5 +0,0 @@ -3 -0 1 2 -3 -1 2 3 -subtract 2 diff --git a/examples/testsets/apg4b-ex25/in/sample_05.txt b/examples/testsets/apg4b-ex25/in/sample_05.txt deleted file mode 100644 index 354e615..0000000 --- a/examples/testsets/apg4b-ex25/in/sample_05.txt +++ /dev/null @@ -1,5 +0,0 @@ -3 -0 1 49 -3 -1 2 3 -increment diff --git a/examples/testsets/apg4b-ex25/in/sample_06.txt b/examples/testsets/apg4b-ex25/in/sample_06.txt deleted file mode 100644 index 8d4d22c..0000000 --- a/examples/testsets/apg4b-ex25/in/sample_06.txt +++ /dev/null @@ -1,5 +0,0 @@ -3 -0 1 49 -3 -1 2 3 -decrement diff --git a/examples/testsets/apg4b-ex25/out/sample_01.txt b/examples/testsets/apg4b-ex25/out/sample_01.txt deleted file mode 100644 index 8d04f96..0000000 --- a/examples/testsets/apg4b-ex25/out/sample_01.txt +++ /dev/null @@ -1 +0,0 @@ -1 2 diff --git a/examples/testsets/apg4b-ex25/out/sample_02.txt b/examples/testsets/apg4b-ex25/out/sample_02.txt deleted file mode 100644 index fd15fe1..0000000 --- a/examples/testsets/apg4b-ex25/out/sample_02.txt +++ /dev/null @@ -1 +0,0 @@ -0 1 2 3 diff --git a/examples/testsets/apg4b-ex25/out/sample_03.txt b/examples/testsets/apg4b-ex25/out/sample_03.txt deleted file mode 100644 index 65c9b94..0000000 --- a/examples/testsets/apg4b-ex25/out/sample_03.txt +++ /dev/null @@ -1 +0,0 @@ -0 3 diff --git a/examples/testsets/apg4b-ex25/out/sample_04.txt b/examples/testsets/apg4b-ex25/out/sample_04.txt deleted file mode 100644 index 6e8183b..0000000 --- a/examples/testsets/apg4b-ex25/out/sample_04.txt +++ /dev/null @@ -1 +0,0 @@ -0 1 diff --git a/examples/testsets/apg4b-ex25/out/sample_05.txt b/examples/testsets/apg4b-ex25/out/sample_05.txt deleted file mode 100644 index 63fb4bf..0000000 --- a/examples/testsets/apg4b-ex25/out/sample_05.txt +++ /dev/null @@ -1 +0,0 @@ -0 1 2 diff --git a/examples/testsets/apg4b-ex25/out/sample_06.txt b/examples/testsets/apg4b-ex25/out/sample_06.txt deleted file mode 100644 index 03ebe79..0000000 --- a/examples/testsets/apg4b-ex25/out/sample_06.txt +++ /dev/null @@ -1 +0,0 @@ -0 48 49 diff --git a/examples/testsets/apg4b-ex26/in/sample_01.txt b/examples/testsets/apg4b-ex26/in/sample_01.txt deleted file mode 100644 index c73dcea..0000000 --- a/examples/testsets/apg4b-ex26/in/sample_01.txt +++ /dev/null @@ -1,5 +0,0 @@ -4 -int x = 1 + 2 ; -print_int x + 3 ; -vec a = [ 1 , 2 , x ] ; -print_vec a + [ 4 , 5 , 6 ] ; diff --git a/examples/testsets/apg4b-ex26/in/sample_02.txt b/examples/testsets/apg4b-ex26/in/sample_02.txt deleted file mode 100644 index 52f5052..0000000 --- a/examples/testsets/apg4b-ex26/in/sample_02.txt +++ /dev/null @@ -1,3 +0,0 @@ -2 -print_int 1 - 2 ; -print_vec [ 1 , 2 , 3 ] - [ 3 , 2 , 1 ] ; diff --git a/examples/testsets/apg4b-ex26/in/sample_03.txt b/examples/testsets/apg4b-ex26/in/sample_03.txt deleted file mode 100644 index 9698270..0000000 --- a/examples/testsets/apg4b-ex26/in/sample_03.txt +++ /dev/null @@ -1,2 +0,0 @@ -1 -print_int 5 ; diff --git a/examples/testsets/apg4b-ex26/in/sample_04.txt b/examples/testsets/apg4b-ex26/in/sample_04.txt deleted file mode 100644 index 58ee2d6..0000000 --- a/examples/testsets/apg4b-ex26/in/sample_04.txt +++ /dev/null @@ -1,2 +0,0 @@ -1 -print_vec [ 1 , 2 ] ; diff --git a/examples/testsets/apg4b-ex26/in/sample_05.txt b/examples/testsets/apg4b-ex26/in/sample_05.txt deleted file mode 100644 index 0f3f7a4..0000000 --- a/examples/testsets/apg4b-ex26/in/sample_05.txt +++ /dev/null @@ -1,3 +0,0 @@ -2 -int x = 1 ; -print_int x ; diff --git a/examples/testsets/apg4b-ex26/in/sample_06.txt b/examples/testsets/apg4b-ex26/in/sample_06.txt deleted file mode 100644 index bca6173..0000000 --- a/examples/testsets/apg4b-ex26/in/sample_06.txt +++ /dev/null @@ -1,3 +0,0 @@ -2 -vec a = [ 3 , 4 ] ; -print_vec a ; diff --git a/examples/testsets/apg4b-ex26/in/sample_07.txt b/examples/testsets/apg4b-ex26/in/sample_07.txt deleted file mode 100644 index edbcbb1..0000000 --- a/examples/testsets/apg4b-ex26/in/sample_07.txt +++ /dev/null @@ -1,5 +0,0 @@ -4 -int x = 1 ; -int y = 2 ; -int z = 3 ; -print_int x + y + z ; diff --git a/examples/testsets/apg4b-ex26/in/sample_08.txt b/examples/testsets/apg4b-ex26/in/sample_08.txt deleted file mode 100644 index 62b13df..0000000 --- a/examples/testsets/apg4b-ex26/in/sample_08.txt +++ /dev/null @@ -1,5 +0,0 @@ -4 -vec a = [ 1 , 2 , 3 ] ; -vec b = [ 4 , 5 , 6 ] ; -vec c = [ 7 , 8 , 9 ] ; -print_vec a + b + c ; diff --git a/examples/testsets/apg4b-ex26/in/sample_09.txt b/examples/testsets/apg4b-ex26/in/sample_09.txt deleted file mode 100644 index ba24ea5..0000000 --- a/examples/testsets/apg4b-ex26/in/sample_09.txt +++ /dev/null @@ -1,7 +0,0 @@ -6 -vec a = [ 1 , 2 ] ; -vec b = a + [ 3 , 4 ] ; -vec c = a - [ 5 , 6 ] ; -print_vec a ; -print_vec b ; -print_vec c ; diff --git a/examples/testsets/apg4b-ex26/out/sample_01.txt b/examples/testsets/apg4b-ex26/out/sample_01.txt deleted file mode 100644 index e21af7e..0000000 --- a/examples/testsets/apg4b-ex26/out/sample_01.txt +++ /dev/null @@ -1,2 +0,0 @@ -6 -[ 5 7 9 ] diff --git a/examples/testsets/apg4b-ex26/out/sample_02.txt b/examples/testsets/apg4b-ex26/out/sample_02.txt deleted file mode 100644 index 22e4cf0..0000000 --- a/examples/testsets/apg4b-ex26/out/sample_02.txt +++ /dev/null @@ -1,2 +0,0 @@ --1 -[ -2 0 2 ] diff --git a/examples/testsets/apg4b-ex26/out/sample_03.txt b/examples/testsets/apg4b-ex26/out/sample_03.txt deleted file mode 100644 index 7ed6ff8..0000000 --- a/examples/testsets/apg4b-ex26/out/sample_03.txt +++ /dev/null @@ -1 +0,0 @@ -5 diff --git a/examples/testsets/apg4b-ex26/out/sample_04.txt b/examples/testsets/apg4b-ex26/out/sample_04.txt deleted file mode 100644 index b4a1e66..0000000 --- a/examples/testsets/apg4b-ex26/out/sample_04.txt +++ /dev/null @@ -1 +0,0 @@ -[ 1 2 ] diff --git a/examples/testsets/apg4b-ex26/out/sample_05.txt b/examples/testsets/apg4b-ex26/out/sample_05.txt deleted file mode 100644 index d00491f..0000000 --- a/examples/testsets/apg4b-ex26/out/sample_05.txt +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/examples/testsets/apg4b-ex26/out/sample_06.txt b/examples/testsets/apg4b-ex26/out/sample_06.txt deleted file mode 100644 index b6f6fec..0000000 --- a/examples/testsets/apg4b-ex26/out/sample_06.txt +++ /dev/null @@ -1 +0,0 @@ -[ 3 4 ] diff --git a/examples/testsets/apg4b-ex26/out/sample_07.txt b/examples/testsets/apg4b-ex26/out/sample_07.txt deleted file mode 100644 index 1e8b314..0000000 --- a/examples/testsets/apg4b-ex26/out/sample_07.txt +++ /dev/null @@ -1 +0,0 @@ -6 diff --git a/examples/testsets/apg4b-ex26/out/sample_08.txt b/examples/testsets/apg4b-ex26/out/sample_08.txt deleted file mode 100644 index b77f265..0000000 --- a/examples/testsets/apg4b-ex26/out/sample_08.txt +++ /dev/null @@ -1 +0,0 @@ -[ 12 15 18 ] diff --git a/examples/testsets/apg4b-ex26/out/sample_09.txt b/examples/testsets/apg4b-ex26/out/sample_09.txt deleted file mode 100644 index 20db499..0000000 --- a/examples/testsets/apg4b-ex26/out/sample_09.txt +++ /dev/null @@ -1,3 +0,0 @@ -[ 1 2 ] -[ 4 6 ] -[ -4 -4 ] diff --git a/examples/testsets/arc065-c/in/subtask0_0.txt b/examples/testsets/arc065-c/in/subtask0_0.txt deleted file mode 100644 index b2f36c5..0000000 --- a/examples/testsets/arc065-c/in/subtask0_0.txt +++ /dev/null @@ -1 +0,0 @@ -erasedream diff --git a/examples/testsets/arc065-c/in/subtask0_1.txt b/examples/testsets/arc065-c/in/subtask0_1.txt deleted file mode 100644 index afebb48..0000000 --- a/examples/testsets/arc065-c/in/subtask0_1.txt +++ /dev/null @@ -1 +0,0 @@ -dreameraser diff --git a/examples/testsets/arc065-c/in/subtask0_2.txt b/examples/testsets/arc065-c/in/subtask0_2.txt deleted file mode 100644 index 621f453..0000000 --- a/examples/testsets/arc065-c/in/subtask0_2.txt +++ /dev/null @@ -1 +0,0 @@ -dreamerer diff --git a/examples/testsets/arc065-c/out/subtask0_0.txt b/examples/testsets/arc065-c/out/subtask0_0.txt deleted file mode 100644 index f033a50..0000000 --- a/examples/testsets/arc065-c/out/subtask0_0.txt +++ /dev/null @@ -1 +0,0 @@ -YES diff --git a/examples/testsets/arc065-c/out/subtask0_1.txt b/examples/testsets/arc065-c/out/subtask0_1.txt deleted file mode 100644 index f033a50..0000000 --- a/examples/testsets/arc065-c/out/subtask0_1.txt +++ /dev/null @@ -1 +0,0 @@ -YES diff --git a/examples/testsets/arc065-c/out/subtask0_2.txt b/examples/testsets/arc065-c/out/subtask0_2.txt deleted file mode 100644 index 5e35d1b..0000000 --- a/examples/testsets/arc065-c/out/subtask0_2.txt +++ /dev/null @@ -1 +0,0 @@ -NO diff --git a/examples/testsets/atc001-b/in/00_sample_01.txt b/examples/testsets/atc001-b/in/00_sample_01.txt deleted file mode 100644 index 632a776..0000000 --- a/examples/testsets/atc001-b/in/00_sample_01.txt +++ /dev/null @@ -1,10 +0,0 @@ -8 9 -0 1 2 -0 3 2 -1 1 3 -1 1 4 -0 2 4 -1 4 1 -0 4 2 -0 0 0 -1 0 0 diff --git a/examples/testsets/atc001-b/out/00_sample_01.txt b/examples/testsets/atc001-b/out/00_sample_01.txt deleted file mode 100644 index df9be86..0000000 --- a/examples/testsets/atc001-b/out/00_sample_01.txt +++ /dev/null @@ -1,4 +0,0 @@ -Yes -No -Yes -Yes diff --git a/examples/testsets/atc002-b/in/sample_01.txt b/examples/testsets/atc002-b/in/sample_01.txt deleted file mode 100644 index 2065346..0000000 --- a/examples/testsets/atc002-b/in/sample_01.txt +++ /dev/null @@ -1 +0,0 @@ -12 15 7 diff --git a/examples/testsets/atc002-b/in/sample_02.txt b/examples/testsets/atc002-b/in/sample_02.txt deleted file mode 100644 index 0903e42..0000000 --- a/examples/testsets/atc002-b/in/sample_02.txt +++ /dev/null @@ -1 +0,0 @@ -123456789 234567894 6574837563712 diff --git a/examples/testsets/atc002-b/out/sample_01.txt b/examples/testsets/atc002-b/out/sample_01.txt deleted file mode 100644 index 00750ed..0000000 --- a/examples/testsets/atc002-b/out/sample_01.txt +++ /dev/null @@ -1 +0,0 @@ -3 diff --git a/examples/testsets/atc002-b/out/sample_02.txt b/examples/testsets/atc002-b/out/sample_02.txt deleted file mode 100644 index 7ecdf4c..0000000 --- a/examples/testsets/atc002-b/out/sample_02.txt +++ /dev/null @@ -1 +0,0 @@ -120678297 diff --git a/examples/testsets/practice-a/in/00_sample_1.txt b/examples/testsets/practice-a/in/00_sample_1.txt deleted file mode 100644 index b75af29..0000000 --- a/examples/testsets/practice-a/in/00_sample_1.txt +++ /dev/null @@ -1,3 +0,0 @@ -1 -2 3 -test diff --git a/examples/testsets/practice-a/in/00_sample_2.txt b/examples/testsets/practice-a/in/00_sample_2.txt deleted file mode 100644 index a4250ca..0000000 --- a/examples/testsets/practice-a/in/00_sample_2.txt +++ /dev/null @@ -1,3 +0,0 @@ -72 -128 256 -myonmyon diff --git a/examples/testsets/practice-a/out/00_sample_1.txt b/examples/testsets/practice-a/out/00_sample_1.txt deleted file mode 100644 index 58fffac..0000000 --- a/examples/testsets/practice-a/out/00_sample_1.txt +++ /dev/null @@ -1 +0,0 @@ -6 test diff --git a/examples/testsets/practice-a/out/00_sample_2.txt b/examples/testsets/practice-a/out/00_sample_2.txt deleted file mode 100644 index a5c645d..0000000 --- a/examples/testsets/practice-a/out/00_sample_2.txt +++ /dev/null @@ -1 +0,0 @@ -456 myonmyon diff --git a/test-examples.toml b/test-examples.toml new file mode 100644 index 0000000..9d0b69c --- /dev/null +++ b/test-examples.toml @@ -0,0 +1,253 @@ +# Dropboxからのダウンロードは行なわない。(面倒なのと誰のアカウントを使うかという問題がある) + +testcases = "./target/test-examples/testcases/{problem}" + +[examples.abc054-c] +type = "Normal" +name = "ABC054: C - One-stroke Path" +url = "https://atcoder.jp/contests/abc054/tasks/abc054_c" +matching = "Words" +meta = { using = ["itertools", "petgraph", "proconio"] } + +[examples.abc057-b-naive] +type = "Normal" +name = "ABC057: B - Checkpoints" +url = "https://atcoder.jp/contests/abc057/tasks/abc057_b" +matching = "Words" +meta = { using = [] } # 下3つと比較するため + +[examples.abc057-b-proconio] +type = "Normal" +name = "ABC057: B - Checkpoints" +url = "https://atcoder.jp/contests/abc057/tasks/abc057_b" +matching = "Words" +meta = { using = ["proconio"] } + +[examples.abc057-b-text-io] +type = "Normal" +name = "ABC057: B - Checkpoints" +url = "https://atcoder.jp/contests/abc057/tasks/abc057_b" +matching = "Words" +meta = { using = ["text_io"] } + +[examples.abc057-b-whiteread] +type = "Normal" +name = "ABC057: B - Checkpoints" +url = "https://atcoder.jp/contests/abc057/tasks/abc057_b" +matching = "Words" +meta = { using = ["whiteread"] } + +[examples.abc073-d] +type = "Normal" +name = "ABC073: D - joisino's travel" +url = "https://atcoder.jp/contests/abc073/tasks/abc073_d" +matching = "Words" +meta = { using = ["itertools", "num", "petgraph", "proconio"] } + +[examples.abc118-b-naive] +type = "Normal" +name = "ABC118: B - Foods Loved by Everyone" +url = "https://atcoder.jp/contests/abc118/tasks/abc118_b" +matching = "Words" +meta = { using = [] } # 下3つと比較するため + +[examples.abc118-b-proconio] +type = "Normal" +name = "ABC118: B - Foods Loved by Everyone" +url = "https://atcoder.jp/contests/abc118/tasks/abc118_b" +matching = "Words" +meta = { using = ["proconio"] } + +[examples.abc118-b-text-io] +type = "Normal" +name = "ABC118: B - Foods Loved by Everyone" +url = "https://atcoder.jp/contests/abc118/tasks/abc118_b" +matching = "Words" +meta = { using = ["text-io"] } + +[examples.abc118-b-whiteread] +type = "Normal" +name = "ABC118: B - Foods Loved by Everyone" +url = "https://atcoder.jp/contests/abc118/tasks/abc118_b" +matching = "Words" +meta = { using = ["whiteread"] } + +[examples.abc121-b-naive] +type = "Normal" +name = "ABC121: B - Can you solve this?" +url = "https://atcoder.jp/contests/abc121/tasks/abc121_b" +matching = "Words" +meta = { using = [] } # 下3つと比較するため + +[examples.abc121-b-proconio] +type = "Normal" +name = "ABC121: B - Can you solve this?" +url = "https://atcoder.jp/contests/abc121/tasks/abc121_b" +matching = "Words" +meta = { using = ["proconio"] } + +[examples.abc121-b-text-io] +type = "Normal" +name = "ABC121: B - Can you solve this?" +url = "https://atcoder.jp/contests/abc121/tasks/abc121_b" +matching = "Words" +meta = { using = ["text_io"] } + +[examples.abc121-b-whiteread] +type = "Normal" +name = "ABC121: B - Can you solve this?" +url = "https://atcoder.jp/contests/abc121/tasks/abc121_b" +matching = "Words" +meta = { using = ["whiteread"] } + +[examples.abc129-f] +type = "Normal" +name = "ABC129: F - Takahashi's Basics in Education and Learning" +url = "https://atcoder.jp/contests/abc129/tasks/abc129_f" +matching = "Words" +meta = { using = ["ndarray", "num", "num-derive", "proconio"] } + +[examples.abc141-c] +type = "Normal" +name = "ABC141: C - Attack Survival" +url = "https://atcoder.jp/contests/abc141/tasks/abc141_c" +matching = "Words" +meta = { using = ["proconio"] } + +[examples.abc142-c] +type = "Normal" +name = "ABC142: C - Go to School" +url = "https://atcoder.jp/contests/abc142/tasks/abc142_c" +matching = "Words" +meta = { using = ["itertools", "proconio", "superslice"] } + +[examples.abc144-d] +type = "Normal" +name = "ABC144: D - Water Bottle" +url = "https://atcoder.jp/contests/abc144/tasks/abc144_d" +matching = { FloatOr = { abs = 1e-6, rel = 1e-6 } } +meta = { using = ["libm", "proconio"] } + +[examples.abc150-d] +type = "Normal" +name = "ABC150: D - Semi Common Multiple" +url = "https://atcoder.jp/contests/abc150/tasks/abc150_d" +matching = "Words" +meta = { using = ["itertools", "num", "proconio"] } + +[examples.abc151-d] +type = "Normal" +name = "ABC151: D - Maze Master" +url = "https://atcoder.jp/contests/abc151/tasks/abc151_d" +matching = "Words" +meta = { using = ["itertools", "ndarray", "proconio", "smallvec"] } + +[examples.apg4b-a] +type = "Normal" +name = "APG4b: A - 1.00.はじめに" +url = "https://atcoder.jp/contests/APG4b/tasks/APG4b_a" +matching = "Exact" +alt_testcases = [{ in = "", out = "Hello, world!\n" }] +meta = { using = ["alga", "ascii", "bitset-fixed", "either", "fixedbitset", "getrandom", "im-rc", "indexmap", "itertools", "itertools-num", "lazy_static", "libm", "maplit", "nalgebra", "ndarray", "num", "num-bigint", "num-complex", "num-derive", "num-integer", "num-iter", "num-rational", "num-traits", "ordered-float", "permutohedron", "petgraph", "proconio", "rand", "rand_chacha", "rand_core", "rand_distr", "rand_hc", "rand_pcg", "regex", "rustc-hash", "smallvec", "superslice", "text_io", "whiteread"] } + +[examples.apg4b-ex25] +type = "Normal" +name = "APG4b: EX25 - 集合の操作 / 3.05" +url = "https://atcoder.jp/contests/APG4b/tasks/APG4b_bx" +matching = "Words" +meta = { using = ["fixedbitset", "itertools", "proconio"] } + +[examples.arc065-c] +type = "Normal" +name = "ABC049 / ARC065: C - 白昼夢 / Daydream" +url = "https://atcoder.jp/contests/arc065/tasks/arc065_a" +matching = "Words" +meta = { using = ["lazy_static", "proconio", "regex"] } + +[examples.arc084-c] +type = "Normal" +name = "ABC077 / ARC084: C - Snuke Festival" +url = "https://atcoder.jp/contests/arc084/tasks/arc084_a" +matching = "Words" +meta = { using = ["proconio", "superslice"] } + +[examples.atc001-b] +type = "Normal" +name = "ATC001: B - Union Find" +url = "https://atcoder.jp/contests/atc001/tasks/unionfind_a" +matching = "Words" +meta = { using = ["petgraph", "proconio"] } + +[examples.atc002-b] +type = "Normal" +name = "ATC002: B - n^p mod m" +url = "https://atcoder.jp/contests/atc002/tasks/atc002_b" +matching = "Words" +meta = { using = ["num", "proconio"] } + +[examples.practice-a-naive] +type = "Normal" +name = "practice contest: A - Welcome to AtCoder" +url = "https://atcoder.jp/contests/practice/tasks/practice_1" +matching = "Exact" +alt_testcases = [{ in = "1\n2 3\ntest", out = "6 test\n" }, { in = "72\n128 256\nmyonmyon", out = "456 myonmyon\n" }] +meta = { using = [] } # 下3つと比較するため + +[examples.practice-a-proconio] +type = "Normal" +name = "practice contest: A - Welcome to AtCoder" +url = "https://atcoder.jp/contests/practice/tasks/practice_1" +matching = "Exact" +alt_testcases = [{ in = "1\n2 3\ntest", out = "6 test\n" }, { in = "72\n128 256\nmyonmyon", out = "456 myonmyon\n" }] +meta = { using = ["proconio"] } + +[examples.practice-a-text-io] +type = "Normal" +name = "practice contest: A - Welcome to AtCoder" +url = "https://atcoder.jp/contests/practice/tasks/practice_1" +matching = "Exact" +alt_testcases = [{ in = "1\n2 3\ntest", out = "6 test\n" }, { in = "72\n128 256\nmyonmyon", out = "456 myonmyon\n" }] +meta = { using = ["text-io"] } + +[examples.practice-a-whiteread] +type = "Normal" +name = "practice contest: A - Welcome to AtCoder" +url = "https://atcoder.jp/contests/practice/tasks/practice_1" +matching = "Exact" +alt_testcases = [{ in = "1\n2 3\ntest", out = "6 test\n" }, { in = "72\n128 256\nmyonmyon", out = "456 myonmyon\n" }] +meta = { using = ["whiteread"] } + +[examples.practice-b-naive] +type = "Special" +name = "practice contest: B - Interactive Sorting" +url = "https://atcoder.jp/contests/practice/tasks/practice_2" +tester = ["python", "./examples/testers/practice-b.py", "{bin}"] +meta = { using = ["itertools", "maplit"] } + +[examples.practice-b-proconio] +type = "Special" +name = "practice contest: B - Interactive Sorting" +url = "https://atcoder.jp/contests/practice/tasks/practice_2" +tester = ["python", "./examples/testers/practice-b.py", "{bin}"] +meta = { using = ["itertools", "maplit", "proconio"] } + +[examples.practice-b-text-io] +type = "Special" +name = "practice contest: B - Interactive Sorting" +url = "https://atcoder.jp/contests/practice/tasks/practice_2" +tester = ["python", "./examples/testers/practice-b.py", "{bin}"] +meta = { using = ["itertools", "maplit", "text-io"] } + +[examples.practice-b-whiteread] +type = "Special" +name = "practice contest: B - Interactive Sorting" +url = "https://atcoder.jp/contests/practice/tasks/practice_2" +tester = ["python", "./examples/testers/practice-b.py", "{bin}"] +meta = { using = ["itertools", "maplit", "whiteread"] } + +[examples.sumitrust2019-c] +type = "Normal" +name = "Sumitomo Mitsui Trust Bank Programming Contest 2019: C - 100 to 105" +url = "https://atcoder.jp/contests/sumitrust2019/tasks/sumitb2019_c" +matching = "Words" +meta = { using = ["fixedbitset", "proconio"] } diff --git a/tests/test_num_derive.rs b/tests/test_num_derive.rs index ac5a14b..fb7d81f 100644 --- a/tests/test_num_derive.rs +++ b/tests/test_num_derive.rs @@ -18,6 +18,7 @@ use num_derive::{FromPrimitive, Num, NumCast, NumOps, One, ToPrimitive, Zero}; )] struct Weight(i32); +#[allow(clippy::eq_op)] #[test] fn check_ops() { let w1 = Weight(7); diff --git a/tools/test-examples/Cargo.toml b/tools/test-examples/Cargo.toml new file mode 100644 index 0000000..35512c4 --- /dev/null +++ b/tools/test-examples/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "test-examples" +version = "0.0.0" +edition = "2018" +publish = false +description = "Test the examples" + +[dependencies] +anyhow = "1.0.26" +approx = "0.3.2" +env_logger = "0.7.1" +indexmap = { version = "1.3.1", features = ["serde-1"] } +itertools = "0.8.2" +log = "0.4.8" +maplit = "1.0.2" +nom = "5.1.0" +once_cell = "1.3.1" +regex = "1.3.3" +scraper = "0.11.0" +serde = { version = "1.0.104", features = ["derive"] } +serde_json = "1.0.45" +shell-escape = "0.1.4" +structopt = "0.3.8" +toml = "0.5.6" +ureq = "0.11.3" +url = { version = "2.1.1", features = ["serde"] } +which = { version = "3.1.0", default-features = false } +either = "1.5.3" +fallible-iterator = "0.2.0" diff --git a/tools/test-examples/src/main.rs b/tools/test-examples/src/main.rs new file mode 100644 index 0000000..100c6a2 --- /dev/null +++ b/tools/test-examples/src/main.rs @@ -0,0 +1,726 @@ +use anyhow::{anyhow, ensure, Context as _}; +use approx::{abs_diff_eq, relative_eq}; +use either::Either; +use env_logger::fmt::Color; +use fallible_iterator::FallibleIterator as _; +use indexmap::IndexMap; +use itertools::Itertools as _; +use log::{info, Level, LevelFilter}; +use maplit::hashmap; +use once_cell::sync::Lazy; +use regex::Regex; +use scraper::{Html, Selector}; +use serde::de::DeserializeOwned; +use serde::{Deserialize, Deserializer}; +use structopt::StructOpt; +use url::Url; + +use std::collections::{BTreeMap, BTreeSet, HashMap}; +use std::ffi::{OsStr, OsString}; +use std::io::{self, Read as _, Write as _}; +use std::ops::Deref; +use std::path::{Path, PathBuf}; +use std::process::{Command, Stdio}; +use std::str::FromStr; +use std::time::Instant; +use std::{env, f64, fs}; + +#[derive(StructOpt, Debug)] +struct Opt { + #[structopt( + long, + value_name("PATH"), + default_value("./test-examples.toml"), + help("Path to the config") + )] + config: PathBuf, +} + +fn main() -> anyhow::Result<()> { + let Opt { config } = Opt::from_args(); + + env_logger::builder() + .format(|buf, record| { + let mut style = buf.style(); + let mut write_with_style = |color, bold, intense, value| -> _ { + let value = style + .set_color(color) + .set_bold(bold) + .set_intense(intense) + .value(value); + write!(buf, "{}", value) + }; + write_with_style(Color::Black, false, true, "[")?; + match record.level() { + Level::Info => write_with_style(Color::Cyan, true, false, "INFO"), + Level::Warn => write_with_style(Color::Yellow, true, false, "WARN"), + Level::Error => write_with_style(Color::Red, true, false, "ERROR"), + _ => unreachable!(), + }?; + write_with_style(Color::Black, false, true, "]")?; + writeln!(buf, " {}", record.args()) + }) + .filter_module("test_examples", LevelFilter::Info) + .init(); + + let config = read_toml::<_, Config>(config)?; + + scrape_sample_cases(&config)?; + cargo_build_examples_release()?; + + let tests = config + .examples + .iter() + .map(|(slug, example)| { + let bin = Path::new(".") + .join("target") + .join("release") + .join("examples") + .join(slug); + + match example { + Example::Normal(Normal { + base: Base { name, url }, + matching, + alt_testcases, + }) => { + let testcases = if let Some(alt_testcases) = alt_testcases { + alt_testcases + .iter() + .enumerate() + .map(|(i, c)| { + ((i + 1).to_string().into(), (c.r#in.clone(), c.out.clone())) + }) + .collect() + } else { + load_testcases(&config.testcases.expand_path(slug)?)? + }; + Ok(Either::Left((name, url, bin, *matching, testcases))) + } + Example::Special(Special { + base: Base { name, url }, + tester, + }) => { + let tester = tester + .iter() + .map(|t| t.expand_as_arg(&bin)) + .collect::>>()?; + Ok(Either::Right((name, url, bin, tester))) + } + } + }) + .collect::>>()?; + + for test in tests { + match test { + Either::Left((name, url, bin, matching, testcases)) => { + normal_test(&name, &url, matching, &testcases, &bin)? + } + Either::Right((name, url, bin, tester)) => special_test(&name, &url, &tester, &bin)?, + } + } + Ok(()) +} + +fn scrape_sample_cases(config: &Config) -> anyhow::Result<()> { + for (slug, example) in &config.examples { + let dst_dir = config.testcases.expand_path(slug)?; + if example.requires_sample_cases() && !dst_dir.exists() { + let samples = get_html(&example.url())?.extract_samples()?; + save_testcases(&dst_dir, &samples)?; + } + } + Ok(()) +} + +fn get_html(url: &Url) -> anyhow::Result { + static USER_AGENT: &str = "test-examples "; + + info!("GET: {}", url); + + let res = ureq::get(url.as_ref()).set("User-Agent", USER_AGENT).call(); + + if let Some(err) = res.synthetic_error() { + let mut err = err as &dyn std::error::Error; + let mut displays = vec![err.to_string()]; + while let Some(source) = err.source() { + displays.push(source.to_string()); + err = source; + } + let mut displays = displays.into_iter().rev(); + let cause = anyhow!("{}", displays.next().unwrap()); + return Err(displays.fold(cause, |err, display| err.context(display))); + } + + info!("{} {}", res.status(), res.status_text()); + ensure!(res.status() == 200, "expected 200"); + let text = res.into_string()?; + Ok(Html::parse_document(&text)) +} + +trait HtmlExt { + fn extract_samples(&self) -> anyhow::Result>; +} + +impl HtmlExt for Html { + fn extract_samples(&self) -> anyhow::Result> { + fn extract_samples( + this: &Html, + selector_for_header: &'static Selector, + selector_for_content: &'static Selector, + re_input: &'static Regex, + re_output: &'static Regex, + ) -> Option> { + macro_rules! static_selector { + ($s:expr $(,)?) => {{ + static SELECTOR: Lazy = Lazy::new(|| Selector::parse($s).unwrap()); + &*SELECTOR + }}; + } + + macro_rules! guard { + ($p:expr $(,)?) => { + if !$p { + return None; + } + }; + } + + let task_statement = this + .select(static_selector!("#task-statement")) + .exactly_one() + .ok() + .or_else(|| { + this.select(static_selector!( + r#"div[id="task-statement"] > div[id="task-statement"]"#, + )) + .exactly_one() + .ok() + })?; + + let mut ins = BTreeMap::::new(); + let mut outs = BTreeMap::::new(); + let mut next = None; + let selector = selector_for_header.or(selector_for_content); + for elem_ref in task_statement.select(&selector) { + if elem_ref.value().name() == "h3" { + let text = elem_ref.text().join(""); + if let Some(caps) = re_input.captures(&text) { + next = Some((true, parse_possibly_zenkaku(&caps[1]).ok()?)); + } else if let Some(caps) = re_output.captures(&text) { + next = Some((false, parse_possibly_zenkaku(&caps[1]).ok()?)); + } + } else if ["pre", "section"].contains(&elem_ref.value().name()) { + if let Some((is_input, n)) = next { + let text = elem_ref.text().join(""); + if is_input { + ins.insert(n, text); + } else { + outs.insert(n, text); + } + } + next = None; + } + } + + let mut samples = ins + .into_iter() + .flat_map(|(idx, input)| outs.remove(&idx).map(|output| (input, output))) + .collect::>(); + + for (input, output) in &mut samples { + for s in &mut [input, output] { + if !(s.is_empty() || s.ends_with('\n')) { + s.push('\n'); + } + guard!(is_valid_text(s)); + } + } + + (!samples.is_empty()).then_(samples) + } + + fn parse_possibly_zenkaku(s: &str) -> Result { + s.parse().or_else(|err| { + if s.chars().all(|c| '0' <= c && c <= '9') { + s.chars() + .map(|c| { + char::from((u32::from(c) - u32::from('0') + u32::from('0')) as u8) + }) + .collect::() + .parse() + } else { + Err(err) + } + }) + } + + fn is_valid_text(s: &str) -> bool { + s == "\n" + || ![' ', '\n'].iter().any(|&c| s.starts_with(c)) + && s.chars().all(|c| { + c.is_ascii() && (c.is_ascii_whitespace() == [' ', '\n'].contains(&c)) + }) + } + + trait SelectorExt { + fn or(&self, other: &Self) -> Self; + } + + impl SelectorExt for Selector { + fn or(&self, other: &Self) -> Self { + let mut acc = self.clone(); + acc.selectors.extend(other.selectors.clone()); + acc + } + } + + macro_rules! lazy_regex { + ($s:expr $(,)?) => { + Lazy::new(|| Regex::new($s).unwrap()) + }; + } + + macro_rules! lazy_selector { + ($s:expr $(,)?) => { + Lazy::new(|| Selector::parse($s).unwrap()) + }; + } + + static IN_JA: Lazy = lazy_regex!(r"\A[\s\n]*入力例\s*(\d{1,2})[.\n]*\z"); + static OUT_JA: Lazy = lazy_regex!(r"\A[\s\n]*出力例\s*(\d{1,2})[.\n]*\z"); + static IN_EN: Lazy = lazy_regex!(r"\ASample Input\s?([0-9]{1,2}).*\z"); + static OUT_EN: Lazy = lazy_regex!(r"\ASample Output\s?([0-9]{1,2}).*\z"); + + // Current style (Japanese) + static P1_HEAD: Lazy = + lazy_selector!("span.lang > span.lang-ja > div.part > section > h3"); + static P1_CONTENT: Lazy = + lazy_selector!("span.lang > span.lang-ja > div.part > section > pre"); + // Current style (English) + static P2_HEAD: Lazy = + lazy_selector!("span.lang > span.lang-en > div.part > section > h3"); + static P2_CONTENT: Lazy = + lazy_selector!("span.lang>span.lang-en>div.part>section>pre"); + // ARC019..ARC057 \ {ARC019/C, ARC046/D, ARC050, ARC052/{A, C}, ARC053, ARC055}, + // ABC007..ABC040 \ {ABC036}, ATC001, ATC002 + static P3_HEAD: Lazy = lazy_selector!("div.part > section > h3"); + static P3_CONTENT: Lazy = lazy_selector!("div.part > section > pre"); + // ARC002..ARC018, ARC019/C, ABC001..ABC006 + static P4_HEAD: Lazy = lazy_selector!("div.part > h3,pre"); + static P4_CONTENT: Lazy = lazy_selector!("div.part > section > pre"); + // ARC001, dwacon2018-final/{A, B} + static P5_HEAD: Lazy = lazy_selector!("h3,pre"); + static P5_CONTENT: Lazy = lazy_selector!("section > pre"); + // ARC046/D, ARC050, ARC052/{A, C}, ARC053, ARC055, ABC036, ABC041 + static P6_HEAD: Lazy = lazy_selector!("section > h3"); + static P6_CONTENT: Lazy = lazy_selector!("section > pre"); + // ABC034 + static P7_HEAD: Lazy = lazy_selector!("span.lang > span.lang-ja > section > h3"); + static P7_CONTENT: Lazy = + lazy_selector!("span.lang > span.lang-ja > section > pre"); + // practice contest (Japanese) + static P8_HEAD: Lazy = lazy_selector!("span.lang > span.lang-ja > div.part > h3"); + static P8_CONTENT: Lazy = + lazy_selector!("span.lang > span.lang-ja > div.part > section > pre"); + + extract_samples(self, &P1_HEAD, &P1_CONTENT, &IN_JA, &OUT_JA) + .or_else(|| extract_samples(self, &P2_HEAD, &P2_CONTENT, &IN_EN, &OUT_EN)) + .or_else(|| extract_samples(self, &P3_HEAD, &P3_CONTENT, &IN_JA, &OUT_JA)) + .or_else(|| extract_samples(self, &P4_HEAD, &P4_CONTENT, &IN_JA, &OUT_JA)) + .or_else(|| extract_samples(self, &P5_HEAD, &P5_CONTENT, &IN_JA, &OUT_JA)) + .or_else(|| extract_samples(self, &P6_HEAD, &P6_CONTENT, &IN_JA, &OUT_JA)) + .or_else(|| extract_samples(self, &P7_HEAD, &P7_CONTENT, &IN_JA, &OUT_JA)) + .or_else(|| extract_samples(self, &P8_HEAD, &P8_CONTENT, &IN_JA, &OUT_JA)) + .ok_or_else(|| anyhow!("Failed to scrape")) + } +} + +fn save_testcases(dir: &Path, cases: &[(String, String)]) -> anyhow::Result<()> { + let contents = cases + .iter() + .enumerate() + .flat_map(|(idx, (input, output))| { + let file_name = format!("{}.txt", idx + 1); + let input = (dir.join("in").join(&file_name), input); + let output = (dir.join("out").join(file_name), output); + vec![input, output] + }) + .collect::>(); + + for (path, contents) in contents { + let parent = path.parent().expect("should not be root or empty"); + if !parent.exists() { + create_dir_all(parent)?; + } + write(&path, contents)?; + info!("Wrote {}", path.display()); + } + Ok(()) +} + +fn load_testcases(dir: &Path) -> anyhow::Result> { + let find_files = |dir_file_name: &str| -> _ { + let dir = dir.join(dir_file_name); + (|| -> _ { + fs::read_dir(&dir)? + .flat_map(|entry| { + entry + .map(|entry| { + let path = entry.path(); + (path.extension() == Some("txt".as_ref())).then_with_(|| { + (path.file_stem().unwrap_or_default().to_owned(), path) + }) + }) + .transpose() + }) + .collect::>>() + })() + .with_context(|| format!("Failed to read {}", dir.display())) + }; + + let (ins, mut outs) = (find_files("in")?, find_files("out")?); + ins.into_iter() + .flat_map(|(stem, input)| outs.remove(&stem).map(|output| (stem, input, output))) + .map(|(stem, input, output)| { + let (input, output) = (read_to_string(input)?, read_to_string(output)?); + Ok((stem, (input, output))) + }) + .collect() +} + +fn cargo_build_examples_release() -> anyhow::Result<()> { + fn run_command, S2: AsRef, I: IntoIterator>( + program: S1, + args: I, + ) -> anyhow::Result<()> { + let program = program.as_ref(); + let args = args.into_iter().collect::>(); + + info!( + "Running `{}{}`", + shell_escape::escape(program.to_string_lossy()), + args.iter() + .map(AsRef::as_ref) + .map(OsStr::to_string_lossy) + .map(shell_escape::escape) + .format_with("", |s, f| f(&format_args!(" {}", s))), + ); + + let status = Command::new(program).args(&args).status()?; + if !status.success() { + return Err(anyhow!("{}: {}", program.to_string_lossy(), status)); + } + Ok(()) + } + + run_command( + env::var_os("CARGO").unwrap_or_else(|| "cargo".into()), + &["build", "--examples", "--release"], + ) +} + +fn normal_test( + task_name: &str, + url: &Url, + matching: Matching, + testcases: &BTreeMap, + binary: &Path, +) -> anyhow::Result<()> { + info!("Testing {}", binary.display()); + info!(" Name: {:?}", task_name); + info!(" URL: {}", url); + + for (case_name, (input, expected)) in testcases { + let start = Instant::now(); + + let mut child = Command::new(binary) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::inherit()) + .spawn() + .with_context(|| format!("Failed to execute {}", binary.display()))?; + + child.stdin.as_mut().unwrap().write_all(input.as_ref())?; + child.stdin.take(); + let actual = { + let mut actual = "".to_owned(); + child + .stdout + .as_mut() + .unwrap() + .read_to_string(&mut actual) + .with_context(|| format!("{} outputted invalid UTF-8", binary.display()))?; + actual + }; + let status = child.wait()?; + let stop = Instant::now(); + + let time = (stop - start).as_millis(); + let verdict = if status.success() && matching.accepts(&expected, &actual) { + "AC" + } else if status.success() { + "WA" + } else { + "RE" + }; + info!("{:?}: {} in {}ms", case_name, verdict, time); + if verdict != "AC" { + return Err(anyhow!("Test failed")); + } + } + Ok(()) +} + +fn special_test(task_name: &str, url: &Url, tester: &[OsString], bin: &Path) -> anyhow::Result<()> { + info!("Testing {}", bin.display()); + info!(" Name: {:?}", task_name); + info!(" URL: {}", url); + info!(" Arguments: {:?}", tester); + + let start = Instant::now(); + let arg0 = tester.get(0).map(Deref::deref).unwrap_or_default(); + let status = Command::new(arg0) + .args(&tester[1..]) + .status() + .with_context(|| format!("Failed to execute {}", arg0.to_string_lossy()))?; + let stop = Instant::now(); + let time = (stop - start).as_millis(); + let verdict = if status.success() { "AC" } else { "WA" }; + + info!("{} in {}ms", verdict, time); + if verdict != "AC" { + return Err(anyhow!("Test failed")); + } + Ok(()) +} + +fn read_to_string(path: impl AsRef) -> anyhow::Result { + let path = path.as_ref(); + fs::read_to_string(path).with_context(|| format!("Failed to read {}", path.display())) +} + +fn read_toml, T: DeserializeOwned>(path: P) -> anyhow::Result { + let path = path.as_ref(); + fs::read_to_string(path) + .map_err(anyhow::Error::from) + .and_then(|s| toml::from_str(&s).map_err(Into::into)) + .with_context(|| format!("Failed to read {}", path.display())) +} + +fn write(path: impl AsRef, contents: impl AsRef) -> anyhow::Result<()> { + let (path, contents) = (path.as_ref(), contents.as_ref()); + fs::write(path, contents).with_context(|| format!("Failed to write {}", path.display())) +} + +fn create_dir_all(path: impl AsRef) -> anyhow::Result<()> { + let path = path.as_ref(); + fs::create_dir_all(path).with_context(|| format!("Failed to create {}", path.display())) +} + +trait BoolExt { + /// + fn then_(self, t: T) -> Option; + + /// + fn then_with_(self, f: F) -> Option + where + F: FnOnce() -> T; +} + +impl BoolExt for bool { + fn then_(self, t: T) -> Option { + if self { + Some(t) + } else { + None + } + } + + fn then_with_(self, f: F) -> Option + where + F: FnOnce() -> T, + { + if self { + Some(f()) + } else { + None + } + } +} + +#[derive(Debug, Deserialize)] +struct Config { + testcases: Template, + examples: IndexMap, +} + +#[derive(Debug)] +struct Template(Vec); + +impl Template { + fn expand(&self, vars: &HashMap<&str, &OsStr>) -> anyhow::Result { + let args = self.0.iter().map(|token| match token { + TemplateToken::Brace(name) => vars.get(&**name).copied().ok_or_else(|| { + anyhow!( + "Undefined variable {:?} (expected {:?})", + name, + vars.keys().collect::>(), + ) + }), + TemplateToken::Plain(plain) => Ok(plain.as_ref()), + }); + fallible_iterator::convert(args).fold(OsString::new(), |mut acc, arg| { + acc.push(arg); + Ok(acc) + }) + } + + fn expand_path(&self, slug: &str) -> anyhow::Result { + let vars = hashmap!("problem" => slug.as_ref()); + self.expand(&vars).map(Into::into) + } + + fn expand_as_arg(&self, bin: &Path) -> anyhow::Result { + let vars = hashmap!("bin" => bin.as_ref()); + self.expand(&vars) + } +} + +impl<'de> Deserialize<'de> for Template { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + use nom::branch::alt; + use nom::bytes::complete::take_while1; + use nom::character::complete::{alphanumeric1, char, space0}; + use nom::multi::many0; + use nom::IResult; + + fn tokens(input: &str) -> IResult<&str, Vec> { + many0(alt((brace, plain)))(input) + } + + fn brace(input: &str) -> IResult<&str, TemplateToken> { + let (input, _) = char('{')(input)?; + let (input, _) = space0(input)?; + let (input, name) = alphanumeric1(input)?; + let (input, _) = space0(input)?; + let (input, _) = char('}')(input)?; + Ok((input, TemplateToken::Brace(name.to_owned()))) + } + + fn plain(input: &str) -> IResult<&str, TemplateToken> { + let (input, plain) = take_while1(|c| !['{', '}'].contains(&c))(input)?; + Ok((input, TemplateToken::Plain(plain.to_owned()))) + } + + let input = String::deserialize(deserializer)?; + let (_, tokens) = tokens(&input).map_err(|err| match err { + nom::Err::Incomplete(_) => unreachable!(), + nom::Err::Error((s, k)) | nom::Err::Failure((s, k)) => serde::de::Error::custom( + format!("{:?} at {}: {:?}", input, input.len() - s.len(), k), + ), + })?; + Ok(Self(tokens)) + } +} + +#[derive(Debug)] +enum TemplateToken { + Brace(String), + Plain(String), +} + +#[derive(Debug, Deserialize)] +#[serde(tag = "type")] +enum Example { + Normal(Normal), + Special(Special), +} + +impl Example { + fn url(&self) -> &Url { + match self { + Self::Normal(this) => &this.base.url, + Self::Special(this) => &this.base.url, + } + } + + fn requires_sample_cases(&self) -> bool { + match self { + Self::Normal(this) => this.alt_testcases.is_none(), + Self::Special(_) => false, + } + } +} + +#[derive(Debug, Deserialize)] +struct Normal { + #[serde(flatten)] + base: Base, + matching: Matching, + alt_testcases: Option>, +} + +#[derive(Debug, Deserialize)] +struct Special { + #[serde(flatten)] + base: Base, + tester: Vec