#![warn(clippy::pedantic)]
use std::collections::HashSet;
use std::io::{stdin, Read};
use nom::bytes::complete::tag;
use nom::character::complete::{anychar, newline, u32};
use nom::combinator::{map, map_opt};
use nom::multi::many1;
use nom::sequence::{preceded, separated_pair, terminated};
type Input<'a> = &'a str;
type IResult<'a, T> = nom::IResult, T>;
type Point = (usize, usize);
fn point(i: Input) -> IResult {
separated_pair(map(u32, |i| i as usize), tag(","), map(u32, |i| i as usize))(i)
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
enum Fold {
X(usize),
Y(usize),
}
fn fold(i: Input) -> IResult {
map_opt(
preceded(
tag("fold along "),
separated_pair(anychar, tag("="), map(u32, |i| i as usize)),
),
|(dimension, position)| match dimension {
'x' => Some(Fold::X(position)),
'y' => Some(Fold::Y(position)),
_ => None,
},
)(i)
}
fn input(i: Input) -> IResult<(Vec, Vec)> {
separated_pair(
many1(terminated(point, newline)),
newline,
many1(terminated(fold, newline)),
)(i)
}
fn do_fold(points: impl Iterator- , fold: Fold) -> HashSet {
let transform = |x, n| if x < n { x } else { n - (x - n) };
points
.map(|(x, y)| match fold {
Fold::X(n) => (transform(x, n), y),
Fold::Y(n) => (x, transform(y, n)),
})
.collect()
}
fn main() {
let mut data = String::new();
stdin().lock().read_to_string(&mut data).unwrap();
let (points, folds) = input(&data).unwrap().1;
let mut points: HashSet<_> = points.into_iter().collect();
let mut folds = folds.into_iter();
points = do_fold(points.into_iter(), folds.next().unwrap());
println!("{}", points.len());
for fold in folds {
points = do_fold(points.into_iter(), fold);
}
for row in 0..=points.iter().map(|&(_, y)| y).max().unwrap() {
for col in 0..=points.iter().map(|&(x, _)| x).max().unwrap() {
if points.contains(&(col, row)) {
print!("#");
} else {
print!(".");
}
}
println!();
}
}