#![warn(clippy::pedantic)]
use std::{
    cmp::{max, min},
    io::{stdin, BufRead},
    ops::RangeInclusive,
};

use nom::{
    branch::alt,
    bytes::complete::tag,
    character::complete::i64,
    combinator::map,
    error::ParseError,
    sequence::{pair, preceded, separated_pair, terminated, tuple},
};

#[derive(Clone, Debug, PartialEq, Eq)]
struct Cuboid {
    x: RangeInclusive<i64>,
    y: RangeInclusive<i64>,
    z: RangeInclusive<i64>,
}

fn range_len(range: &RangeInclusive<i64>) -> u64 {
    (range.end() - range.start() + 1).try_into().unwrap_or(0)
}

fn range_intersect(a: &RangeInclusive<i64>, b: &RangeInclusive<i64>) -> RangeInclusive<i64> {
    max(*a.start(), *b.start())..=min(*a.end(), *b.end())
}

impl Cuboid {
    pub fn cubes(&self) -> u64 {
        range_len(&self.x) * range_len(&self.y) * range_len(&self.z)
    }

    pub fn intersect(&self, other: &Cuboid) -> Option<Cuboid> {
        let x = range_intersect(&self.x, &other.x);
        let y = range_intersect(&self.y, &other.y);
        let z = range_intersect(&self.z, &other.z);

        if x.is_empty() || y.is_empty() || z.is_empty() {
            None
        } else {
            Some(Self { x, y, z })
        }
    }

    pub fn in_small_range(&self) -> bool {
        let check = |r: &RangeInclusive<i64>| -50 <= *r.start() && *r.end() <= 50;
        check(&self.x) && check(&self.y) && check(&self.z)
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum CubeState {
    Off,
    On,
}

#[derive(Clone, Debug, PartialEq, Eq)]
struct CuboidSpec {
    state: CubeState,
    cuboid: Cuboid,
}

impl CuboidSpec {
    fn parse<'input, E: ParseError<&'input str>>(
        input: &'input str,
    ) -> nom::IResult<&'input str, Self, E> {
        let dimension_range = |dim| {
            map(
                preceded(
                    pair(tag(dim), tag("=")),
                    separated_pair(i64, tag(".."), i64),
                ),
                |(start, end)| start..=end,
            )
        };

        map(
            separated_pair(
                alt((
                    map(tag("off"), |_| CubeState::Off),
                    map(tag("on"), |_| CubeState::On),
                )),
                tag(" "),
                map(
                    tuple((
                        terminated(dimension_range("x"), tag(",")),
                        terminated(dimension_range("y"), tag(",")),
                        dimension_range("z"),
                    )),
                    |(x, y, z)| Cuboid { x, y, z },
                ),
            ),
            |(state, cuboid)| Self { state, cuboid },
        )(input)
    }
}

fn count_intersecting(first: &Cuboid, rest: impl IntoIterator<Item = Cuboid>) -> u64 {
    let intersections = rest.into_iter().filter_map(|c| c.intersect(first));
    count_union(intersections, Entry::Use)
}

enum Entry {
    Ignore(Cuboid),
    Use(Cuboid),
}

fn count_union<T>(cuboids: impl IntoIterator<Item = T>, get_cuboid: impl Fn(T) -> Entry) -> u64 {
    cuboids
        .into_iter()
        .scan(Vec::new(), |previous, x| match get_cuboid(x) {
            Entry::Ignore(c) => {
                previous.push(c);
                Some(0)
            }
            Entry::Use(c) => {
                let count = c.cubes() - count_intersecting(&c, previous.iter().cloned());
                previous.push(c);
                Some(count)
            }
        })
        .sum()
}

fn main() {
    let mut small_range = None;
    let mut lit_cubes: u64 = 0;
    let mut placed_cuboids: Vec<CuboidSpec> = Vec::new();

    for line in stdin().lock().lines() {
        let spec = CuboidSpec::parse::<()>(&line.unwrap()).unwrap().1;

        if !spec.cuboid.in_small_range() {
            small_range.get_or_insert(lit_cubes);
        }

        // all overlaps with previous cuboids
        let intersections = placed_cuboids.iter().cloned().filter_map(|mut s| {
            s.cuboid = s.cuboid.intersect(&spec.cuboid)?;
            Some(s)
        });

        // count the cubes that overlap with currently-ON cubes
        let overlap = count_union(intersections.rev(), |s| {
            (if s.state == CubeState::On {
                Entry::Use
            } else {
                Entry::Ignore
            })(s.cuboid)
        });

        if let CubeState::On = spec.state {
            let size = spec.cuboid.cubes();
            lit_cubes += size;
        };
        lit_cubes -= overlap;

        placed_cuboids.push(spec);
    }

    println!("{}", small_range.unwrap());
    println!("{lit_cubes}");
}
