Polygon Points

type Ll = i64;
type Pll = (Ll, Ll);

fn cross(x1: Ll, y1: Ll, x2: Ll, y2: Ll) -> Ll {
    x1 * y2 - x2 * y1
}

pub fn polygon_area(pts: &[Pll]) -> Ll {
    let mut ats = 0;
    for i in 2..pts.len() {
        ats += cross(
            pts[i].0 - pts[0].0,
            pts[i].1 - pts[0].1,
            pts[i - 1].0 - pts[0].0,
            pts[i - 1].1 - pts[0].1,
        );
    }
    Ll::abs(ats / 2)
}

fn gcd(mut a: Ll, mut b: Ll) -> Ll {
    while b != 0 {
        let temp = b;
        b = a % b;
        a = temp;
    }
    a
}

fn boundary(pts: &[Pll]) -> Ll {
    let mut ats = pts.len() as Ll;
    for i in 0..pts.len() {
        let deltax = pts[i].0 - pts[(i + 1) % pts.len()].0;
        let deltay = pts[i].1 - pts[(i + 1) % pts.len()].1;
        ats += Ll::abs(gcd(deltax, deltay)) - 1;
    }
    ats
}

pub fn lattice_points(pts: &[Pll]) -> Ll {
    let bounds = boundary(pts);
    let area = polygon_area(pts);
    area + 1 - bounds / 2
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_calculate_cross() {
        assert_eq!(cross(1, 2, 3, 4), 4 - 3 * 2);
    }

    #[test]
    fn test_polygon_3_coordinates() {
        let pts = vec![(0, 0), (0, 3), (4, 0)];
        assert_eq!(polygon_area(&pts), 6);
    }

    #[test]
    fn test_polygon_4_coordinates() {
        let pts = vec![(0, 0), (0, 2), (2, 2), (2, 0)];
        assert_eq!(polygon_area(&pts), 4);
    }

    #[test]
    fn test_gcd_multiple_of_common_factor() {
        assert_eq!(gcd(14, 28), 14);
    }

    #[test]
    fn test_boundary() {
        let pts = vec![(0, 0), (0, 3), (0, 4), (2, 2)];
        assert_eq!(boundary(&pts), 8);
    }

    #[test]
    fn test_lattice_points() {
        let pts = vec![(1, 1), (5, 1), (5, 4)];
        let result = lattice_points(&pts);
        assert_eq!(result, 3);
    }
}