// Arup Guha
// 4/1/2016 (written in contest)
// Solution to 2016 USACO Gold April Problem: Splitting the Field

import java.util.*;
import java.io.*;

public class split {

	public static void main(String[] args) throws Exception {

		// Read in data.
		BufferedReader stdin = new BufferedReader(new FileReader("split.in"));

        // This is clunky because I can never remember how to write my own comparator and I did this in contest...
		int n = Integer.parseInt(stdin.readLine().trim());
		ptByX[] dimByX = new ptByX[n];
		ptByY[] dimByY = new ptByY[n];
		for (int i=0; i<n; i++) {
			StringTokenizer tok = new StringTokenizer(stdin.readLine());
			int x = Integer.parseInt(tok.nextToken());
			int y = Integer.parseInt(tok.nextToken());
			dimByX[i] = new ptByX(x,y);
			dimByY[i] = new ptByY(x,y);
		}

        // Get bounds (min, max).
		long area = 0;
		long minX = dimByX[0].x, maxX = dimByX[0].x, minY = dimByX[0].y, maxY = dimByX[0].y;
		for (int i=0; i<n; i++) {
			minX = Math.min(dimByX[i].x, minX);
			maxX = Math.max(dimByX[i].x, maxX);
			minY = Math.min(dimByX[i].y, minY);
			maxY = Math.max(dimByX[i].y, maxY);
		}

		// One fence area.
		area = (maxX-minX)*(maxY-minY);

		Arrays.sort(dimByX);
		long bestByX = area;

		IntTree mytree = new IntTree(0,n-1);
		for (int i=0; i<n; i++)
			mytree.change(i,i,dimByX[i].y);

        // Sweep through the x's, this will be vertical cut point between two rectangles.
		for (int i=0; i<n-1; i++) {

			if (dimByX[i].x == dimByX[i+1].x) continue;

            // Returns the corresponding min and max Y's for each range.
			int[] myLeftY = mytree.query(0, i);
			int[] myRightY = mytree.query(i+1, n-1);

            // Calculate the area of these two rectangles and update if necessary.
			long thisA = ((long)(dimByX[i].x-dimByX[0].x))*(myLeftY[1]-myLeftY[0]) + ((long)(dimByX[n-1].x-dimByX[i+1].x))*(myRightY[1]-myRightY[0]);
			bestByX = Math.min(bestByX, thisA);
		}

		/*** Now we do the whole thing over again for y...annoying... ***/
		Arrays.sort(dimByY);
		long bestByY = area;

		mytree = new IntTree(0, n-1);
		for (int i=0; i<n; i++)
			mytree.change(i,i,dimByY[i].x);


        // Now sweep through the y's.
		for (int i=0; i<n-1; i++) {

			if (dimByY[i].y == dimByY[i+1].y) continue;

			int[] myLeftX = mytree.query(0, i);
			int[] myRightX = mytree.query(i+1, n-1);

			long thisA = ((long)(dimByY[i].y-dimByY[0].y))*(myLeftX[1]-myLeftX[0]) + ((long)(dimByY[n-1].y-dimByY[i+1].y))*(myRightX[1]-myRightX[0]);
			bestByY = Math.min(bestByY, thisA);
		}

		// Write result.
		PrintWriter out = new PrintWriter(new FileWriter("split.out"));
		out.println(area-Math.min(bestByX, bestByY));
		out.close();
		stdin.close();
	}

}

class ptByX implements Comparable<ptByX> {

	public int x;
	public int y;

	public ptByX(int myx, int myy) {
		x = myx;
		y = myy;
	}

	public int compareTo(ptByX other) {
		if (this.x != other.x) return this.x - other.x;
		return this.y-other.y;
	}
}


class ptByY implements Comparable<ptByY> {

	public int x;
	public int y;

	public ptByY(int myx, int myy) {
		x = myx;
		y = myy;
	}

	public int compareTo(ptByY other) {
		if (this.y != other.y) return this.y-other.y;
		return this.x-other.x;
	}
}

class IntTree {

	// Stores range for this node.
	public int low;
	public int high;

	// Stores aggregate data for this node.
	public int delta;
	public int min;
	public int max;

	// Pointers to children.
	public IntTree left;
	public IntTree right;

	// Creates an interval tree from myLow to myHigh, inclusive.
	public IntTree(int myLow, int myHigh) {

		low = myLow;
		high = myHigh;
		delta = 0;
		min = 0; /*** Can change ***/
		max = 0;

		// Lowest level.
		if (low == high) {
			left = null;
			right = null;
		}

		// Must create more nodes.
		else {
			int mid = (low+high)/2;
			left = new IntTree(low, mid);
			right = new IntTree(mid+1, high);
		}
	}

	// Propogates a change down to child nodes.
	public void prop() {

		// Recursive case, push down.
		if (left != null) {
			left.delta += delta;	/*** can change ***/
			right.delta += delta;	/*** can change ***/
			delta = 0;
		}
	}

	// Pre-condition: delta is 0.
	public void update() {
		if (left != null) {
			min = Math.min(left.min+left.delta, right.min+right.delta);
			max = Math.max(left.max+left.delta, right.max+right.delta);
		}
	}

	// Changes the values in the range [start, end] by "adding" extra.
	public void change(int start, int end, int extra) {

		// Out of range.
		if (high < start || end < low) return;

		// Push down delta.
		prop();

		// Whole range must be updated.
		if (start <= low && high <= end) {
			delta += extra;		/*** Can change ***/
			update();
			return;
		}

		// Portions of children have to be changed instead.
		left.change(start, end, extra);
		right.change(start, end, extra);
		update();
	}

	public int[] query(int start, int end) {

		// Out of range.
		if (high < start || end < low) {
			int[] res = new int[2];
			res[0] = 1000000000;
			res[1] = 1;
			return  res; /*** Can change ***/
		}
		// Whole range must be returned;
		if (start <= low && high <= end) {
			int[] res = new int[2];
			res[0] = min+delta;
			res[1] = max+delta;
			return res;
		}

		// Get answers from both potions.
		prop();
		int[] leftSide = left.query(start, end);
		int[] rightSide = right.query(start, end);
		update();
		int[] res = new int[2];
		res[0] = Math.min(leftSide[0], rightSide[0]);
		res[1] = Math.max(leftSide[1], rightSide[1]);
		return res;
	}
}
