use std::cmp::max;

fn main() {
    let (r, w) = (std::io::stdin(), std::io::stdout());
    let mut sc = IO::new(r.lock(), w.lock());
    let h: usize = sc.read();
    let w: usize = sc.read();
    let map: Vec<Vec<char>> = (0..h).map(|_| sc.chars()).collect();

    let min_ans = max(h, w);

    let mut board = vec![vec![false; w - 1]; h - 1];
    for i in 0..(h - 1) {
        for j in 0..(w - 1) {
            let mut count = 0;
            for k in 0..2 {
                for l in 0..2 {
                    if map[i + k][j + l] == '#' {
                        count += 1;
                    }
                }
            }
            board[i][j] = count % 2 == 0;
        }
    }

    let max_area = max_rectangle::maximize(&board);
    println!("{}", max(max_area, min_ans));
}

pub mod max_rectangle {
    use std::cmp;
    use std::collections::VecDeque;

    fn calc(hist: &[usize]) -> usize {
        let n = hist.len();
        let mut ans = 0;
        let mut q: VecDeque<(usize, usize)> = VecDeque::new();

        for (i, &hist) in hist.iter().enumerate() {
            let mut reachable_min = i;
            while let Some((pos, height)) = q.pop_front() {
                if height <= hist {
                    q.push_front((pos, height));
                    break;
                }
                reachable_min = pos;
                ans = cmp::max(ans, (i - reachable_min + 1) * (height + 1));
            }

            if q.is_empty() || q.iter().next().unwrap().1 < hist {
                q.push_front((reachable_min, hist));
            }
        }
        while let Some((pos, height)) = q.pop_front() {
            ans = cmp::max(ans, (n - pos + 1) * (height + 1));
        }
        ans
    }

    pub fn maximize(map: &[Vec<bool>]) -> usize {
        let h = map.len();
        let w = map[0].len();
        let mut hist = vec![vec![0; w]; h];
        for i in 0..h {
            for j in 0..w {
                if !map[i][j] {
                    continue;
                }
                if i == 0 {
                    hist[i][j] = 1;
                } else {
                    hist[i][j] = hist[i - 1][j] + 1;
                }
            }
        }

        let mut ans = 0;
        for hist in hist.iter() {
            ans = cmp::max(ans, calc(hist));
        }
        ans
    }
}

pub struct IO<R, W: std::io::Write>(R, std::io::BufWriter<W>);

impl<R: std::io::Read, W: std::io::Write> IO<R, W> {
    pub fn new(r: R, w: W) -> Self {
        Self(r, std::io::BufWriter::new(w))
    }
    pub fn write<S: ToString>(&mut self, s: S) {
        use std::io::Write;
        self.1.write(s.to_string().as_bytes()).unwrap();
    }
    pub fn read<T: std::str::FromStr>(&mut self) -> T {
        use std::io::Read;
        let buf = self
            .0
            .by_ref()
            .bytes()
            .map(|b| b.unwrap())
            .skip_while(|&b| b == b' ' || b == b'\n' || b == b'\r' || b == b'\t')
            .take_while(|&b| b != b' ' && b != b'\n' && b != b'\r' && b != b'\t')
            .collect::<Vec<_>>();
        unsafe { std::str::from_utf8_unchecked(&buf) }
            .parse()
            .ok()
            .expect("Parse error.")
    }
    pub fn vec<T: std::str::FromStr>(&mut self, n: usize) -> Vec<T> {
        (0..n).map(|_| self.read()).collect()
    }
    pub fn chars(&mut self) -> Vec<char> {
        self.read::<String>().chars().collect()
    }
}


Measured memory_bytes: 53200