use std::fs::File;
use std::io::{ self, BufRead, BufReader };

type Grid = Vec<Vec<u8>>;
const N: usize = 20;

fn read_grid_lines() -> io::Lines<BufReader<File>> {
    let file = File::open("grid.txt").unwrap();
    return io::BufReader::new(file).lines();
}

fn get_grid() -> Grid {
    let lines = read_grid_lines();
    let mut grid: Grid = Vec::new();
    for line in lines {
        let s = line.unwrap();
        let ns: Vec<_>  = s.split(" ")
                    .map(|x| x.parse::<u8>().unwrap())
                    .collect();
        grid.push(ns);
    }
    return grid;
}

fn g(grid: &Grid, i: usize, j: usize) -> u32 {
    return u32::from(grid.get(i).unwrap().get(j).unwrap().clone());
}

fn best_h(grid: &Grid) -> u32 {
    let mut best_pr = 1;
    for i in 0..N {
        for j in 0..N-3 {
            let a = g(&grid, i, j);
            let b = g(&grid, i, j + 1);
            let c = g(&grid, i, j + 2);
            let d = g(&grid, i, j + 3);
            let pr = a * b * c * d;
            if pr > best_pr { best_pr = pr; }
        }
    }
    return best_pr;
}

fn best_v(grid: &Grid) -> u32 {
    let mut best_pr = 1;
    for i in 0..N-3 {
        for j in 0..N {
            let a = g(&grid, i, j);
            let b = g(&grid, i + 1, j);
            let c = g(&grid, i + 2, j);
            let d = g(&grid, i + 3, j);
            let pr = a * b * c * d;
            if pr > best_pr { best_pr = pr; }
        }
    }
    return best_pr;
}

fn best_dr(grid: &Grid) -> u32 {
    let mut best_pr = 1;
    for i in 0..N-3 {
        for j in 0..N-3 {
            let a = g(&grid, i, j);
            let b = g(&grid, i + 1, j + 1);
            let c = g(&grid, i + 2, j + 2);
            let d = g(&grid, i + 3, j + 3);
            let pr = a * b * c * d;
            if pr > best_pr { best_pr = pr; }
        }
    }
    return best_pr;
}

fn best_dl(grid: &Grid) -> u32 {
    let mut best_pr = 1;
    for i in 3..N {
        for j in 0..N-3 {
            let a = g(&grid, i, j);
            let b = g(&grid, i - 1, j + 1);
            let c = g(&grid, i - 2, j + 2);
            let d = g(&grid, i - 3, j + 3);
            let pr = a * b * c * d;
            if pr > best_pr { best_pr = pr; }
        }
    }
    return best_pr;
}

fn best(grid: &Grid) -> u32 {
    let a = best_h(&grid);
    let b = best_v(&grid);
    let c = best_dr(&grid);
    let d = best_dl(&grid);

    println!("{} {} {} {}", a, b, c, d);

    return best_h(&grid).max(best_v(&grid)).max(best_dr(&grid)).max(best_dl(grid));
}

fn main() {
    let grid = get_grid();
    let n = best(&grid);

    println!("{}", n);
}