pub mod macros {
pub use crate::{iota, fatal, puts};
}
mod colors {
pub const CLR: &str = "\x1b[22;39m";
pub const BOLD_RED: &str = "\x1b[1;91m";
}
#[macro_export]
macro_rules! iota {
($n:expr) => {{
(0..$n).collect::<Vec<_>>()
}};
($n:expr, $f:expr) => {{
(0..$n).map($f).collect::<Vec<_>>()
}};
}
#[macro_export]
macro_rules! fatal {
($($t:tt)*) => {{
eprintln!(
"{}fatal error{}: {}",
crate::colors::BOLD_RED,
crate::colors::CLR,
format!($($t)*)
);
std::process::exit(-1);
}};
($($t:tt)*, $p:expr) => {{
eprintln!(
"{}fatal error{}: {}",
crate::colors::BOLD_RED,
crate::colors::CLR,
format!($($t)*)
);
std::process::exit($p);
}};
}
#[macro_export]
macro_rules! puts {
($($t:tt)*) => {{
use std::io::Write;
print!($($t)*);
match std::io::stdout().flush() {
Ok(_) => (),
Err(e) => fatal!("can't flush stdout: {e}"),
}
}};
}
#[allow(dead_code)]
pub mod com {
pub fn ceil_div(x: usize, y: usize) -> usize {
(x as f64 / y as f64).ceil() as usize
}
pub fn rev<T>(x: &mut [T]) {
let l = x.len() - 1;
for i in (0..ceil_div(l, 2)).into_iter() {
x.swap(i, l - i);
}
}
pub fn basename<T>(x: T) -> String
where
String: From<T>
{
let x = String::from(x);
let v: Vec<_> = x.split("/").collect();
(if v.len() > 0 { v[v.len() - 1] } else { v[0] }).to_string()
}
pub fn dirname<T>(x: T) -> String
where
String: From<T>
{
let x = String::from(x);
let mut v: Vec<_> = x.split("/").collect();
v.pop();
if v.len() > 1 {
v.join("/")
} else {
String::new()
}
}
fn trim_<T>(s: T, c: char, rev: bool) -> String
where
String: From<T>,
{
let mut s = {
let s = String::from(s);
if rev {
s.chars().rev().collect::<String>()
} else {
s
}
};
let mut i = s.chars().rev().collect::<Vec<_>>();
let mut i = i.iter_mut();
while let Some(x) = i.next() {
if *x == c {
let _ = s.pop();
} else {
break;
}
}
if rev {
s.chars().rev().collect::<String>()
} else {
s
}
}
pub fn trim_left<T>(s: T, c: char) -> String
where
String: From<T>,
{
trim_(s, c, true)
}
pub fn trim_right<T>(s: T, c: char) -> String
where
String: From<T>,
{
trim_(s, c, false)
}
pub fn i64_to_bool(x: i64) -> bool {
if x == 0 { false } else { true }
}
pub fn bool_to_i64(x: bool) -> i64 {
if x { 1 } else { 0 }
}
pub fn input(p: &str) -> String {
let mut b = String::new();
let stdin = std::io::stdin();
puts!("{p}");
match stdin.read_line(&mut b) {
Ok(x) => x,
Err(e) => fatal!("failed to read line from stdin: {e}"),
};
b.trim().to_string()
}
pub fn input_or<T>(p: &str, x: T) -> String
where
String: From<T>,
{
let i = input(p);
if i == String::new() || is_all_space(&mut i.chars()) {
String::from(x)
} else {
i
}
}
fn is_all_space(x: &mut std::str::Chars) -> bool {
x.all(|x| match x {
' ' | '\t' | '\n' => true,
_ => false,
})
}
}
#[test]
fn rev_even_len() {
let mut x = vec![1, 2, 3, 4];
com::rev(&mut x);
assert_eq!(x, vec![4, 3, 2, 1]);
}
#[test]
fn rev_odd_len() {
let mut x = iota!(5, |x| x+1);
com::rev(&mut x);
assert_eq!(
x,
iota!(5, |x| x+1)
.into_iter()
.rev()
.collect::<Vec<_>>()
);
}
#[test]
fn iota_map() {
let x = iota!(5, |x| x+1);
assert_eq!(x, iota!(5).into_iter().map(|x| x+1).collect::<Vec<_>>())
}
#[test]
fn basename() {
assert_eq!(com::basename("/home/skye"), "skye");
assert_eq!(com::basename("foo"), "foo");
}
#[test]
fn dirname() {
assert_eq!(com::dirname("/home/skye"), "/home");
assert_eq!(com::dirname("/home"), "");
}
#[test]
fn trim() {
assert_eq!(com::trim_right("foo ", ' '), "foo");
assert_eq!(com::trim_left(" foo", ' '), "foo");
}