#![warn(clippy::pedantic)]
use std::{
    convert::Infallible,
    io::{stdin, BufRead},
    str::FromStr,
};
use vector2d::Vector2D;

#[derive(Copy, Clone, Debug)]
struct Line {
    start: Vector2D<usize>,
    end: Vector2D<usize>,
}

impl Line {
    pub fn is_straight(&self) -> bool {
        self.start.x == self.end.x || self.start.y == self.end.y
    }

    pub fn expand(&self) -> impl Iterator<Item = Vector2D<usize>> {
        LinePoints::new(self)
    }
}

impl FromStr for Line {
    type Err = Infallible;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let parts: Vec<_> = s.split(' ').collect();
        let start: Vec<_> = parts[0].split(',').map(|n| n.parse().unwrap()).collect();
        let end: Vec<_> = parts[2].split(',').map(|n| n.parse().unwrap()).collect();

        Ok(Line {
            start: Vector2D::new(start[0], start[1]),
            end: Vector2D::new(end[0], end[1]),
        })
    }
}

struct LinePoints {
    pos: Vector2D<isize>,
    step: Vector2D<isize>,
    points_left: usize,
}

impl LinePoints {
    fn new(line: &Line) -> Self {
        let start = line.start.as_isizes();
        let end = line.end.as_isizes();
        let delta = end - start;
        let step = Vector2D::new(delta.x.signum(), delta.y.signum());

        let len = usize::max(delta.x.unsigned_abs(), delta.y.unsigned_abs());

        Self {
            pos: start,
            step,
            points_left: len + 1,
        }
    }
}

impl Iterator for LinePoints {
    type Item = Vector2D<usize>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.points_left == 0 {
            return None;
        }

        let current = self.pos;
        self.pos += self.step;
        self.points_left -= 1;
        Some(current.as_usizes())
    }
}

fn count_overlaps<'a>(it: impl IntoIterator<Item = &'a Line>) -> usize {
    let points: Vec<(usize, usize)> = it
        .into_iter()
        .flat_map(Line::expand)
        .map(Into::into)
        .collect();

    let (max_x, max_y) = points.iter().fold((0, 0), |(acc_x, acc_y), &(x, y)| {
        (usize::max(acc_x, x), usize::max(acc_y, y))
    });

    let mut field = vec![vec![0_usize; max_y + 1]; max_x + 1];

    let mut hits = 0;
    for (x, y) in points {
        let point = &mut field[x][y];
        if *point == 1 {
            hits += 1;
        }
        *point += 1;
    }
    hits
}

fn main() {
    let lines: Vec<Line> = stdin()
        .lock()
        .lines()
        .map(|s| s.unwrap().parse().unwrap())
        .collect();

    println!(
        "{}",
        count_overlaps(lines.iter().filter(|line| line.is_straight()))
    );
    println!("{}", count_overlaps(&lines));
}
