Given a set of points in the xy-plane, determine the minimum area of a rectangle formed from these points, with sides parallel to the x and y axes.
If there isn't any rectangle, return 0.
 

Example 1:
Input: [[1,1],[1,3],[3,1],[3,3],[2,2]]
Output: 4


Example 2:
Input: [[1,1],[1,3],[3,1],[3,3],[4,1],[4,3]]
Output: 2

 
Note:

1 <= points.length <= 500
0 <= points[i][0] <= 40000
0 <= points[i][1] <= 40000
All points are distinct.

Difficulty: interview

Starter code:

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        

Solutions:
class Solution:
    def minAreaRect(self, points):
        pSet, ans = set(map(tuple, points)), float('inf')
        for i, (p1x, p1y) in enumerate(points):
            for (p2x, p2y) in points[i+1:]:
                if (p1x != p2x) and (p1y != p2y) and ((p1x, p2y) in pSet) and ((p2x, p1y) in pSet):
                    ans = min(ans, abs((p1x - p2x) * (p1y - p2y)))
        return ans if ans < float('inf') else 0
    
class Solution:
    def minAreaRect(self, points):
        X = list(zip(*points))
        if len(set(X[0])) == 1 or len(set(X[1])) == 1: return 0

        pSet, ans = set(map(tuple, points)), float('inf')
        for i, (p1x, p1y) in enumerate(points):
            for (p2x, p2y) in points[i+1:]:
                area = abs((p2x-p1x)*(p2y-p1y))
                if area > ans or area == 0:
                    continue
                if (p1x, p2y) in pSet and (p2x, p1y) in pSet:
                    ans = area
        return ans if ans < float('inf') else 0
    
from collections import defaultdict
class Solution:
    def minAreaRect(self, points):
        X = list(zip(*points))
        Nr, Nc = len(set(X[0])), len(set(X[1]))
        if Nr == 1 or Nc == 1: return 0

        points.sort()
        columns = defaultdict(list)
        for r, c in points:
            columns[r].append(c)
        ans, lastc = float('inf'), dict()
        for r, cols in list(columns.items()):
            for i, c1 in enumerate(cols):
                for c2 in cols[i+1:]:
                    if (c1, c2) in lastc:
                        area = (r - lastc[(c1, c2)]) * (c2 - c1)
                        #ans = min(ans, area)
                        if area < ans: ans = area
                    lastc[(c1, c2)] = r
        return ans if ans < float('inf') else 0
    
from collections import defaultdict
class Solution:
    def minAreaRect(self, points):
        X = list(zip(*points))
        Nr, Nc = len(set(X[0])), len(set(X[1]))
        if Nr == 1 or Nc == 1: return 0
        if Nr < Nc:
            points = [[c, r] for (r, c) in points]
            return self.minAreaRect(points)
    
        points.sort()
        columns = defaultdict(list)
        for r, c in points:
            columns[r].append(c)
        ans, lastc = float('inf'), dict()
        for r, cols in list(columns.items()):
            for i, c1 in enumerate(cols):
                for c2 in cols[i+1:]:
                    if (c1, c2) in lastc:
                        area = (r - lastc[(c1, c2)]) * (c2 - c1)
                        #ans = min(ans, area)
                        if area < ans: ans = area
                    lastc[(c1, c2)] = r
        return ans if ans < float('inf') else 0
    
from collections import defaultdict
class Solution:
    def minAreaRect(self, points):
        d = defaultdict(set)
        rset, cset, N, ans = set(), set(), len(points), float('inf')
        for r, c in points:
            rset.add(r)
            cset.add(c)
        Nr, Nc = len(rset), len(cset)
        if Nr == 1 or Nc == 1:
            return 0
        elif Nr < Nc:
            for r, c in points:
                d[r].add(c)
        else:
            for r, c in points:
                d[c].add(r)

        A = sorted(d.keys())
        for i, r1 in enumerate(A):
            cols1 = d[r1]
            for r2 in A[i+1:]:
                cols2 = d[r2]
                s = sorted(cols1 & cols2)
                for c1, c2 in zip(s[:-1], s[1:]):
                    area = (r1 - r2) * (c1 - c2)
                    #ans = min(ans, area)
                    if area < ans: ans = area
        return ans if ans < float('inf') else 0


from collections import defaultdict
class Solution:
    def minAreaRect(self, points):
        d = defaultdict(set)
        rset, cset, N, ans = set(), set(), len(points), float('inf')
        for r, c in points:
            rset.add(r)
            cset.add(c)
        Nr, Nc = len(rset), len(cset)
        if Nr == 1 or Nc == 1:
            return 0
        elif Nr < Nc:
            for r, c in points:
                d[r].add(c)
        else:
            for r, c in points:
                d[c].add(r)

        A = sorted(d.keys())
        for i, r1 in enumerate(A):
            cols1 = d[r1]
            for r2 in A[i+1:]:
                cols2 = d[r2]
                s = sorted(cols1 & cols2)
                for c1, c2 in zip(s[:-1], s[1:]):
                    area = abs((r1 - r2) * (c1 - c2))
                    ans = min(ans, area)
        return ans if ans < float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        n = len(points)
        # count of each distinct x axis point
        countx = len(set(x for x, y in points))
        # count of each distinct y axixs point
        county = len(set(y for x, y in points))
        
         # if either same as n, no points for that axis
        if countx == n or county == n:
            return 0
        
        container = defaultdict(list)
        
        # add y val to x list in container if x > y, opposite if otherwise
        if countx > county:
            for x, y in points:
                container[x].append(y)
        else:
            for x, y in points:
                container[y].append(x)
        
        # seen set/dict
        lastx = {}
        ans = float('inf')
        
        for x in sorted(container):
            container[x].sort()
            # loop for the len of x's list in container
            for y in range(len(container[x])):
                # loop curr num/index
                for z in range(y):
                    # get from container
                    y1, y2 = container[x][z], container[x][y]
                    
                    #seen before, reset ans to min of itself and last seen x, curr y
                    if (y1, y2) in lastx:
                        ans = min(ans, (x - lastx[y1, y2]) * abs(y2 - y1))
                    # put curr x in seen
                    lastx[y1,y2] = x
        
        return ans if ans < float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        \"\"\"
        maxRow = {(col pair): largest row index}
        
        for every col pair, if in maxRow, update globalMin, update maxRow
        
        O(m * n^2)
        \"\"\"
        grid = {}       # {row: [cols with 1]}
        for x, y in points:
            grid[x] = grid.get(x, []) + [y]
        for value in grid.values():
            value.sort()
        
        minArea = float(\"inf\")
        maxRow = {}
        for row in sorted(grid.keys()):
            n = len(grid[row])
            for i in range(n):
                for j in range(i + 1, n):
                    pair = (grid[row][i], grid[row][j])
                    if pair in maxRow:
                        rowPre = maxRow[pair]
                        minArea = min(minArea, (row - rowPre) * (pair[1] - pair[0]))
                    maxRow[pair] = row
        return minArea if minArea != float(\"inf\") else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        points.sort(key = lambda x:(x[0], x[1]))
        ver = collections.defaultdict(list)
        hor = collections.defaultdict(list)
        seen = set()
        res = float(\"inf\")
        for x, y in points:
            if len(ver[x]) > 0 and len(hor[y]) > 0:
                for i in ver[x][::-1]:
                    for j in hor[y][::-1]:
                        if (j, i) in seen:
                            res = min(res, (x - j) * (y - i))
            seen.add((x, y))
            ver[x].append(y)
            hor[y].append(x)
        if res != float(\"inf\"):
            return res
        else:
            return 0



class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        
        cols = collections.defaultdict(list)
        
        for x, y in points:
            cols[x].append(y)
        
        min_ = float(\"inf\")
        pre_x = {}
        
        for x2 in sorted(cols):
            cols[x2] = sorted(cols[x2])
            for j in range(len(cols[x2])):
                for i in range(j):
                    y1 = cols[x2][i]
                    y2 = cols[x2][j]
                    if (y1,y2) in pre_x:
                        delta_x = x2 - pre_x[y1,y2]
                        delta_y = y2 - y1
                        area =  delta_x * delta_y
                        min_ = min(min_, area)
                    
                    pre_x[y1,y2] = x2
        
        return min_ if min_ < float(\"inf\") else 0
                        
                        

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        d_x = collections.defaultdict(set)
        d_y = collections.defaultdict(set)
        for x, y in points: 
            d_x[x].add(y)
            d_y[y].add(x)
        area = sys.maxsize            
        for x, y in points:
            for yy in d_x[x]: # find same x (point above), larger y
                if yy <= y: continue
                for xx in d_y[y]: # find same y (point right), larget x
                    if xx > x and yy in d_x[xx]:
                        area = min(area, (xx-x)*(yy-y))
        return area if area != sys.maxsize else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        columns = collections.defaultdict(list)
        for x, y in points:
            columns[x].append(y)
        lastx = {}
        ans = float('inf')

        for x in sorted(columns):
            column = columns[x]
            column.sort()
            for j, y2 in enumerate(column):
                for i in range(j):
                    y1 = column[i]
                    if (y1, y2) in lastx:
                        ans = min(ans, (x - lastx[y1,y2]) * (y2 - y1))
                    lastx[y1, y2] = x
        return ans if ans < float('inf') else 0

import collections

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        p = collections.defaultdict(list)
        for x, y in points:
            p[x].append(y)
        
        lastx = {}
        res = float('inf')
        for x in sorted(p):
            p[x].sort()
            for i in range(len(p[x])):
                for j in range(i):
                    y1, y2 = p[x][j], p[x][i]
                    if (y1, y2) in lastx:
                        res = min(res, (x - lastx[y1, y2]) * (y2 - y1))
                    lastx[y1, y2] = x
        
        return res if res < float('inf') else 0

import collections
class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        sameX = collections.defaultdict(list)
        for x, y in points:
            sameX[x].append(y)
        lastX = {}
        minArea = float('inf')
        for x in sorted(sameX):
            sameX[x].sort()
            for i in range(len(sameX[x])):
                for j in range(i):
                    y1, y2 = sameX[x][j], sameX[x][i]
                    if (y1, y2) in lastX:
                        minArea = min(minArea, (y2-y1) * (x - lastX[y1, y2]))
                    lastX[y1, y2] = x
        return minArea if minArea != float('inf') else 0


import numpy as np
import collections
class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        col = collections.defaultdict(list)
        lastx = {}
        ans = float('inf')
        for x,y in points:
            col[x].append(y)
        for x,col_item in sorted(col.items()):
            col_item = sorted(col_item)
            for i in range(len(col_item)):
                y2 = col_item[i]
                for j in range(i):
                    y1 = col_item[j]
                    x1 = lastx.get((y1,y2))
                    if x1!=None:
                        ans = min(ans,(x-x1)*(y2-y1))
                    lastx[(y1,y2)] = x
                    
        if ans<float('inf'):
            return ans
        else:
            return 0

        


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        columns = defaultdict(list)
        
        points = sorted(points, key = lambda x: (x[0], x[1]))
        
        for x, y in points:
            columns[x].append(y)
                
        most_recent = {}
        min_area = float('inf')
        
        #for x in sorted(columns):
        for x in columns:
            #column = sorted(columns[x])
            column = columns[x]
            
            for index, y2 in enumerate(column):
                for y1 in column[:index]:
                    if (y1, y2) in most_recent:
                        min_area = min(min_area, (y2 - y1) * (x - most_recent[(y1, y2)]))
                                       
                    most_recent[(y1, y2)] = x
        
        return min_area if min_area < float('inf') else 0


class Solution:
    def minAreaRect(self, points):
        n = len(points)
        nx = len(set(x for x, y in points))
        ny = len(set(y for x, y in points))
        if nx == n or ny == n:
            return 0

        p = collections.defaultdict(list)
        if nx > ny:
            for x, y in points:
                p[x].append(y)
        else:
            for x, y in points:
                p[y].append(x)

        lastx = {}
        res = float('inf')
        for x in sorted(p):
            p[x].sort()
            for i in range(len(p[x])):
                for j in range(i):
                    y1, y2 = p[x][j], p[x][i]
                    if (y1, y2) in lastx:
                        res = min(res, (x - lastx[y1, y2]) * abs(y2 - y1))
                    lastx[y1, y2] = x
        return res if res < float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        if not points: return 0
        cache_x = defaultdict(set)
        cache_y = defaultdict(set)
        cache = set()
        for i, p in enumerate(points):
            cache_x[p[0]].add(i)
            cache_y[p[1]].add(i)
            cache.add((p[0], p[1]))
        
        res = float('inf')
        for i, (x0, y0) in enumerate(points):
            cache_x[x0].remove(i)
            cache_y[y0].remove(i)
            cache.remove((x0,y0))
            for ix in cache_y[y0]:
                for iy in cache_x[x0]:
                    x1, y1 = points[ix][0], points[iy][1]
                    if (x1, y1) in cache: 
                        res = min(res, abs(x1-x0)*abs(y1-y0))
        return 0 if res==float('inf') else res
            


import collections
class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        s= set(map(tuple,points))
        dx = collections.defaultdict(list)
        dy = collections.defaultdict(list)
        for item in points:
            dx[item[0]].append(item[1])
            dy[item[1]].append(item[0])
        
        res = float(\"inf\")
        for x in sorted(dx.keys()):
            for i in range(len(dx[x])):
                y1 = dx[x][i]
                for j in range(i+1,len(dx[x])):
                    y2 = dx[x][j]
                    for x1 in dy[y2]:
                        if x1<=x:
                            continue
                        if (x1,y1) in s:
                            res = min(res,abs(x1-x)*abs(y1-y2))
        if res ==float(\"inf\"):
            return 0
        return res 

from collections import defaultdict

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        x_dict = defaultdict(set)
        
        # x coordinate as key, y coordiante as value
        for x, y in points:
            x_dict[x].add(y)
            
        x_vals = list(x_dict.keys())
        best = float(\"inf\")
        
        # loop through combinations
        for i in range(1, len(x_vals)):
            for j in range(i):
                matching_y = []
                
                # find all matching y values, keep track of largest and smallest
                for y in x_dict[x_vals[i]]: 
                    if y in x_dict[x_vals[j]]:
                        matching_y.append(y)
                        
                # if found two distinct matching y values, can make rectangle
                if len(matching_y) >= 2:
                    smallest_diff = get_smallest_diff(matching_y)
                    best = min(best, smallest_diff * abs(x_vals[i] - x_vals[j]))
                
        return 0 if best == float(\"inf\") else best
    
def get_smallest_diff(array):
    array.sort()
    smallest = float(\"inf\")
    
    for i in range(len(array) - 1):
        smallest = min(smallest, array[i + 1] - array[i])
        
    return smallest

class Solution(object):
    def minAreaRect(self, points):
        columns = collections.defaultdict(list)
        for x, y in points:
            columns[x].append(y)
        lastx = {}
        ans = float('inf')

        for x in sorted(columns):
            column = columns[x]
            column.sort()
            for j, y2 in enumerate(column):
                for i in range(j):
                    y1 = column[i]
                    if (y1, y2) in lastx:
                        ans = min(ans, (x - lastx[y1,y2]) * (y2 - y1))
                    lastx[y1, y2] = x
        return ans if ans < float('inf') else 0


from collections import defaultdict

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        x_groups = defaultdict(list)
        for pnt in points:
            x_groups[pnt[0]].append(pnt[1])
            
        pair2x = dict()
        
        min_area = float('inf')
        for x in sorted(x_groups):
            group = x_groups[x]
            group.sort()
            
            for i in range(len(group)):
                for j in range(i + 1, len(group)):
                    last_x = pair2x.get((group[i], group[j]))
                    if last_x is not None:
                        min_area = min(min_area, (x - last_x) * (group[j] - group[i]))
                    pair2x[group[i], group[j]] = x
                    
                    
        return min_area if min_area < float('inf') else 0

class Solution(object):
    def minAreaRect(self, points):
        columns = collections.defaultdict(list)
        for x, y in points:
            columns[x].append(y)
        lastx = {}
        ans = float('inf')

        for x in sorted(columns):
            column = columns[x]
            column.sort()
            for j, y2 in enumerate(column):
                for i in range(j):
                    y1 = column[i]
                    if (y1, y2) in lastx:
                        ans = min(ans, (x - lastx[y1,y2]) * (y2 - y1))
                    lastx[y1, y2] = x
        return ans if ans < float('inf') else 0

class Solution:
    def minAreaRect(self, points):
        seen, ans = set(map(tuple, points)), float('inf')
        for i, (p1x, p1y) in enumerate(points):
            for (p2x, p2y) in points[i+1:]:
                if (p1x != p2x) and (p1y != p2y):
                    area = abs((p2x-p1x)*(p2y-p1y))
                    if area > ans or area == 0:
                        continue
                    if (p1x, p2y) in seen and (p2x, p1y) in seen:
                        ans = area
        return ans if ans < float('inf') else 0

class Solution:
     def minAreaRect(self, points):
        columns = collections.defaultdict(list)
        for x, y in points:
            columns[x].append(y)
        lastx = {}
        ans = float('inf')

        for x in sorted(columns):
            column = columns[x]
            column.sort()
            for j, y2 in enumerate(column):
                for i in range(j):
                    y1 = column[i]
                    if (y1, y2) in lastx:
                        ans = min(ans, (x - lastx[y1,y2]) * (y2 - y1))
                    lastx[y1, y2] = x
        return ans if ans < float('inf') else 0


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        points.sort(key = lambda x: (x[0], x[1]))
        # print(points)
        mx_y = defaultdict(list)
        for x, y in points:
            mx_y[x].append(y)
        
        # print(mx_y)
        res = float('inf')
        seenY_pairs = {}
        
        for x in mx_y:
            if len(mx_y[x])<2: continue
            for i in range(len(mx_y[x])-1):
                y1 = mx_y[x][i]
                for j in range(i+1, len(mx_y[x])):
                    y2 = mx_y[x][j]
                    
                    if (y1, y2) in seenY_pairs:
                        width = x - seenY_pairs[(y1, y2)]
                        height = y2 - y1
                        res = min(res, width*height)
                    seenY_pairs[(y1, y2)] = x
        
        if res == float('inf'):
            return 0
        else:
            return res
        
# [[1,1],[1,3],[3,1],[3,3],[2,2]]
# [[1,1],[1,3],[3,1],[3,3],[4,1],[4,3]]


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        n = len(points)
        nx = len(set(x for x, y in points))
        ny = len(set(y for x, y in points))
        if nx == n or ny == n:
            return 0

        p = collections.defaultdict(list)
        if nx > ny:
            for x, y in points:
                p[x].append(y)
        else:
            for x, y in points:
                p[y].append(x)

        lastx = {}
        res = float('inf')
        for x in sorted(p):
            p[x].sort()
            for i in range(len(p[x])):
                for j in range(i):
                    y1, y2 = p[x][j], p[x][i]
                    if (y1, y2) in lastx:
                        res = min(res, (x - lastx[y1, y2]) * abs(y2 - y1))
                    lastx[y1, y2] = x
        return res if res < float('inf') else 0
                
            


from collections import defaultdict
class Solution:
\tdef minAreaRect(self, points) -> int:
\t\tif(len(points) < 2):
\t\t\treturn 0

\t\tpoints = set(map(tuple,points))
\t\txMap = defaultdict(list)
\t\tyMap = defaultdict(list)

\t\tfor point in points:
\t\t\tx = point[0]
\t\t\ty = point[1]
\t\t\t
\t\t\txMap[x].append(y)

\t\t\tyMap[y].append(x)

\t\tresult = float('inf')
\t\tfor x1 in xMap.keys():
\t\t\tfor i in range(len(xMap[x1])):
\t\t\t\ty1 = xMap[x1][i]
\t\t\t\tfor j in range(i+1, len(xMap[x1])):
\t\t\t\t\ty2 = xMap[x1][j]
\t\t\t\t\tfor x2 in yMap[y2]:
\t\t\t\t\t\tif(x1 < x2 and ((x2,y1) in points)):
\t\t\t\t\t\t\tresult = min(result,abs((y2-y1)*(x2-x1)))
\t\tif(result == float('inf')):
\t\t\treturn 0
\t\treturn result

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        hx = dict()
        hy = dict()
        
        for i, (x, y) in enumerate(points):
            hx.setdefault(x, set())
            hy.setdefault(y, set())
            
            hx[x].add(y)
            hy[y].add(x)
        
        res = float(\"inf\")
        
        
        for xa, ys in hy.items():
            for ya in ys:
                for xb in hx[ya]:
                    if xa < xb:
                        for yb in ys:
                            if yb < ya and yb in hy[xb]:
                                res = min(res, abs(xa - xb) * abs(ya - yb))
            
        return res if res < float(\"inf\") else 0
            

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:


        s = set(map(tuple, points))
        dx = collections.defaultdict(list)
        dy = collections.defaultdict(list)
        for x,y in points:
            dx[x].append(y)
            dy[y].append(x)

        res = float('inf')
        for x in sorted(dx.keys()): # 1. find x in dx
            for i in range(len(dx[x])): # 2. find y1, y2 in dx[x]
                y1 = dx[x][i]
                for j in range(i+1, len(dx[x])):
                    y2 = dx[x][j]
                    for x1 in dy[y2]: # 3. find x1 in dy[y2]
                        # always make x in the left side
                        if x1 <= x: continue
                        if (x1, y1) in s: # 4. judeg (x1,y1) in points
                            res = min(res, abs(x-x1) * abs(y1-y2))

        return res if res != float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        
        h = defaultdict(deque)
        v = defaultdict(deque)
        p = set([(x, y) for x, y in points])

        for x, y in points:
            h[x].append(y)
            v[y].append(x)
        
        minArea = float('inf')
        for x, y in points:
            for y1 in h[x]:
                if y1 != y:
                    for x1 in v[y]:
                        if x1 != x:
                            if (x1, y1) in p:
                                minArea = min(minArea, abs(y1 - y) * abs(x1 - x))
            if h[x]:
                h[x].popleft()
            if v[y]:
                v[y].popleft()

        if minArea == float('inf'):
            return 0
        return minArea


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        record = {}
        points.sort(key = lambda x: [x[0], x[1]])
        res = float('inf')
        temp = [points[0][1]]
        #print(record, points)
        for i in range(1, len(points)):
            if points[i][0] == points[i-1][0]:
                temp.append(points[i][1])
            else:
                if len(temp) >= 2:
                    for j in range(len(temp)-1):
                        for k in range(j+1, len(temp)):
                            if (temp[j], temp[k]) in record:
                                res = min(res, (points[i-1][0]-record[(temp[j], temp[k])])*(temp[k]-temp[j]))
                            record[(temp[j], temp[k])] = points[i-1][0]
                temp = [points[i][1]]
        #print(temp,record)
        if len(temp) >= 2:
            for j in range(len(temp)-1):
                for k in range(j+1, len(temp)):
                    if (temp[j], temp[k]) in record:
                        res = min(res, (points[-1][0]-record[(temp[j], temp[k])])*(temp[k]-temp[j]))
        
        if res == float('inf'):
            return 0
        return res


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        
        col = defaultdict(list)
        
        for x, y in points:
            col[x].append(y)
        
        res = float('inf')
        
        pre = defaultdict(int)
        for x in sorted(col):
            col[x].sort()
            for i in range(len(col[x]) - 1):
                for j in range(i + 1, len(col[x])):
                    if (col[x][i], col[x][j]) in pre:
                        res = min(res, (col[x][j] - col[x][i]) * (x - pre[(col[x][i], col[x][j])]))
                    pre[(col[x][i], col[x][j])] = x
        
        return (res if res != float('inf') else 0)

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        columns = defaultdict(list)
        lastx = {}
        ans = sys.maxsize
        
        for x, y in points:
            columns[x].append(y)
        
        for x in sorted(columns):
            y_list = columns[x]
            y_list.sort()
            for i, y1 in enumerate(y_list):
                for j in range(i):
                    y2 = y_list[j]
                    
                    if (y1, y2) in lastx:
                        ans = min(ans, (x - lastx[(y1,y2)]) * (y1-y2))
                    
                    lastx[(y1,y2)] = x
        
        return ans if ans < sys.maxsize else 0
        
        
        '''
        seen = set()
        ans = sys.maxsize
        
        for x1, y1 in points:
            for x2, y2 in seen:
                if (x1, y2) in seen and (x2, y1) in seen:
                    area = abs(x1-x2) * abs(y1-y2)
                    ans = min(ans, area)
            
            seen.add((x1,y1))
        
        return ans if ans < sys.maxsize else 0
        '''


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        sorted_points = {}
        for point in points:
            if point[0] not in sorted_points:
                sorted_points[point[0]] = []
            sorted_points[point[0]].append(point)
        sorted_col = sorted(sorted_points.keys())
        
        min_area = None
        seen = {} # set((row1, row2)) -> col
        for col in sorted_col:
            sorted_points[col] = sorted(sorted_points[col])
            for idx1 in range(len(sorted_points[col])):
                for idx2 in range(idx1 + 1, len(sorted_points[col])):
                    point1 = sorted_points[col][idx1]
                    point2 = sorted_points[col][idx2]
                    if (point1[1], point2[1]) in seen:
                        temp = (point2[1] - point1[1]) * (col - seen[(point1[1], point2[1])])
                        min_area = min(min_area, temp) if min_area is not None else temp
                    seen[(point1[1], point2[1])] = col
        
        # print(seen)
        
        return min_area if min_area is not None else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        columns = defaultdict(list)
        for x, y in points:
            columns[x].append(y)

        seen, result = {}, float('inf')

        for x2 in sorted(columns):
            column = columns[x2]
            column.sort()
            for j, y2 in enumerate(column):
                for i in range(j):
                    y1 = column[i]
                    if (y1, y2) in seen:
                        result = min(result, (x2 - seen[y1,y2]) * (y2 - y1))
                    seen[y1, y2] = x2
        return result if result != float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        n = len(points)
        nx = len(set(x for x, _ in points))
        ny = len(set(y for _, y in points))
        if nx == n and ny == n:
            return False
        
        p = collections.defaultdict(list)
        if nx > ny:
            for x, y in points:
                p[x].append(y)
        else:
            for x, y in points:
                p[y].append(x)
        
        last_x = {}
        area = float('inf')
        
        for x in sorted(p):
            p[x].sort()
            for i in range(len(p[x])):
                for j in range(i):
                    y1, y2 = p[x][j], p[x][i]
                    
                    if (y1, y2) in last_x:
                        area = min(area, (x - last_x[(y1, y2)]) * (y2 - y1))
                    last_x[(y1, y2)] = x
        return area if area < float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        xtoy = collections.defaultdict(set)
        for x, y in points:
            xtoy[x].add(y)
            
        minarea = sys.maxsize
        for x1 in xtoy:
            for x2 in xtoy:
                if x1 >= x2:
                    continue 
                commony = list(xtoy[x1].intersection(xtoy[x2]))
                if len(commony) < 2:
                    continue 
                commony.sort()
                width = x2 - x1 
                height = commony[1] - commony[0]
                for i in range(2, len(commony)):
                    height = min(height, commony[i] - commony[i-1])
                minarea = min(minarea, height * width)
        return minarea if minarea != sys.maxsize else 0 


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        
        
        columns = collections.defaultdict(list)
        for x, y in points:
            columns[x].append(y)
        lastx = {}
        ans = float('inf')

        for x in sorted(columns):
            column = columns[x]
            column.sort()
            for j, y2 in enumerate(column):
                for i in range(j):
                    y1 = column[i]
                    if (y1, y2) in lastx:
                        ans = min(ans, (x - lastx[y1,y2]) * (y2 - y1))
                    lastx[y1, y2] = x
        return ans if ans < float('inf') else 0
    
    
        
        # S = set(map(tuple, points))
        # ans = float('inf')
        # for j, p2 in enumerate(points):
        #     for i in range(j):
        #         p1 = points[i]
        #         if (p1[0] != p2[0] and p1[1] != p2[1] and
        #                 (p1[0], p2[1]) in S and (p2[0], p1[1]) in S):
        #             ans = min(ans, abs(p2[0] - p1[0]) * abs(p2[1] - p1[1]))
        # return ans if ans < float('inf') else 0        

        
        
        
#         min_area = float('inf')
#         same_y = {}
#         h_segments = {}
                
#         for i in range(len(points)  - 1):
#             for j in range(i + 1 , len(points)):
#                 if points[i][1] == points[j][1]:
#                     key = (min(points[i][0] , points[j][0]) , max(points[i][0] , points[j][0]) )
#                     if key not in h_segments:
#                         h_segments[key] = []
#                     h_segments[key].append(points[i][1])
                                    
#         for key in h_segments:
#             width = key[1] - key[0]
            
#             val = h_segments[key]
#             if len(val) > 1:
#                 for i in range(len(val) - 1):
#                     for j in range(i, len(val)):
#                         height = abs(val[i] - val[j])
#                         if  width > 0 and  height > 0:
#                             min_area = min(min_area , width * height)
        
#         if min_area == float('inf'):
#             return 0
                
#         return min_area


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        if not points:
            return 0
        
        ret = float('inf')
        
        r_to_c = collections.defaultdict(list)
        
        for r, c in points:
            r_to_c[r].append(c)
            
        seen = {}
        
        for bottom in sorted(r_to_c.keys()):
            cols = r_to_c[bottom]
            cols.sort()
            
            for right_i in range(len(cols)):
                for left_i in range(right_i):
                    left = cols[left_i]
                    right = cols[right_i]
                    
                    if (left, right) in seen:
                        top = seen[(left, right)]
                        ret = min(ret, (bottom - top) * (right - left))
                    
                    seen[(left, right)] = bottom
                    
        
        
        
        if ret == float('inf'):
            return 0
        return ret

class Solution:
    def minAreaRect(self, points):
        s = set(map(tuple, points))
        dx = collections.defaultdict(list)
        dy = collections.defaultdict(list)
        for x,y in points:
            dx[x].append(y)
            dy[y].append(x)

        res = float('inf')
        for x in sorted(dx.keys()): # 1. find x in dx
            for i in range(len(dx[x])): # 2. find y1, y2 in dx[x]
                y1 = dx[x][i]
                for j in range(i+1, len(dx[x])):
                    y2 = dx[x][j]
                    for x1 in dy[y2]: # 3. find x1 in dy[y2]
                        # always make x in the left side
                        if x1 <= x: continue
                        if (x1, y1) in s: # 4. judeg (x1,y1) in points
                            res = min(res, abs(x-x1) * abs(y1-y2))

        return res if res != float('inf') else 0

from itertools import combinations

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        area = float('inf')
        by_x = {}
        visited = {}
        for point in points:
            by_x[point[0]] = []
        for point in points:
            by_x[point[0]].append(point[1])
            
        for x in sorted(by_x):
            for y1, y2 in combinations(sorted(by_x[x]), 2):
                if (y1, y2) in visited:
                    area = min(area, abs(x - visited[(y1, y2)]) * abs(y2 - y1))
                visited[(y1, y2)] = x
            
        return area if area != float('inf') else 0

from collections import defaultdict
from itertools import combinations
import sys

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        coordMap = defaultdict(set)
        for x, y in points:
            coordMap[x].add(y)
        ans = sys.maxsize
        for x1, x2 in combinations(list(coordMap.keys()), 2):
            ylist = coordMap[x1].intersection(coordMap[x2])
            if len(ylist) < 2:
                continue
            ylist = sorted(list(ylist))
            idx = min(list(range(len(ylist)-1)), key=lambda i: ylist[i+1] - ylist[i])
            h = ylist[idx+1] - ylist[idx]
            ans = min(ans, (abs(x2-x1))*h)
        return ans if ans != sys.maxsize else 0
                    
        


import collections

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        
        columns = {}
        for x,y in points:
            if x not in columns:
                columns[x] = []
            columns[x].append(y)
        
        lastx = {}
        ans = float('inf')
        
        for x in sorted(columns.keys()):
            column = columns[x]
            column.sort()
            for i,v in enumerate(column):
                for j in range(i):
                    val = column[j]
                    if (val, v) in lastx:
                        ans = min(ans, (x-lastx[val, v])*(v-val))
                    lastx[val, v] = x
                    
        return ans if ans < float('inf') else 0


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        columns = collections.defaultdict(list)
        for x, y in points:
            columns[x].append(y)
        ans = float('inf')
        lastx = dict()
        for x in sorted(columns):
            column = columns[x]
            column.sort()
            for i,y2 in enumerate(column):
                for y1 in column[:i]:
                    if (y1,y2) in lastx:
                        ans = min(ans, (x-lastx[y1,y2]) * abs(y2-y1))
                    lastx[y1,y2] = x
        return ans if ans < float('inf') else 0
                


from collections import defaultdict
class Solution:
    def minAreaRect(self, points):
        points.sort()
        d, ans = defaultdict(set), float('inf')
        for r, c in points:
            d[r].add(c)

        A = list(d.keys())
        for i, r1 in enumerate(A):
            cols1 = d[r1]
            for r2 in A[i+1:]:
                cols2 = d[r2]
                s = sorted(cols1 & cols2)
                for c1, c2 in zip(s[:-1], s[1:]):
                    area = ((r1 - r2) * (c1 - c2))
                    if area < ans: ans = area
        return ans if ans < float('inf') else 0

# from collections import defaultdict
# class Solution:
#     def minAreaRect(self, points):
#         d = defaultdict(set)
#         rset, cset, N, ans = set(), set(), len(points), float('inf')
#         for r, c in points:
#             rset.add(r)
#             cset.add(c)
#         Nr, Nc = len(rset), len(cset)
#         if Nr == N or Nc == N:
#             return 0
#         elif Nr < Nc:
#             for r, c in points:
#                 d[r].add(c)
#         else:
#             for r, c in points:
#                 d[c].add(r)
# 
#         A = sorted(d.keys())
#         for i, r1 in enumerate(A):
#             cols1 = d[r1]
#             for r2 in A[i+1:]:
#                 cols2 = d[r2]
#                 s = sorted(cols1 & cols2)
#                 for c1, c2 in zip(s[:-1], s[1:]):
#                     area = abs((r1 - r2) * (c1 - c2))
#                     ans = min(ans, area)
#         return ans if ans < float('inf') else 0


from collections import defaultdict 
class Solution:
    def minAreaRect(self, points):
        d, ans = defaultdict(set), float('inf')
        for r, c in points:
            d[r].add(c)
         
        #A = sorted(d.keys())
        A = list(d.keys())
        for i, r1 in enumerate(A):
            cols1 = d[r1]
            for r2 in A[i+1:]:
                cols2 = d[r2]
                s = sorted(cols1 & cols2)
                for c1, c2 in zip(s[:-1], s[1:]):
                    area = abs((r1 - r2) * (c1 - c2))
                    if area < ans: ans = area
        return ans if ans < float('inf') else 0

import collections as clc
import itertools as it


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        xs = set()
        ys = set()
        for x, y in points:
            xs.add(x)
            ys.add(y)
        ys_at = clc.defaultdict(list)
        if len(xs) > len(ys):
            for x, y in points:
                ys_at[x].append(y)
        else:
            for x, y in points:
                ys_at[y].append(x)
        latest_x = {}
        ans = float(\"inf\")
        for x in sorted(ys_at):
            for y1, y2 in it.combinations(ys_at[x], 2):
                y1, y2 = sorted([y1, y2])
                if (y1, y2) in latest_x:
                    ans = min(ans, (x - latest_x[y1, y2]) * (y2 - y1))
                latest_x[y1, y2] = x
        return ans if ans != float(\"inf\") else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        def compute_table(points, axis=0):
            table = {}
            for point in points:
                key, value = point[axis], point[1-axis]
                if key in table:
                    table[key].append(value)
                else:
                    table[key] = [value]
            return table

        points.sort()
        x_2_y = compute_table(points, 0)
        y_2_x = compute_table(points, 1)
        min_area = float('inf')
        hashed = set([tuple(point) for point in points])
        for point in points:
            x, y = point
            y_candidates = [_y for _y in x_2_y[x] if _y > y]
            x_candidates = [_x for _x in y_2_x[y] if _x > x]
            for c_y in y_candidates:
                for c_x in x_candidates:
                    if (c_x, c_y) in hashed:
                        min_area = min(min_area, abs(c_y - y) * abs(c_x - x))
        # no rectangles
        if min_area == float('inf'):
            return 0
        return min_area


from collections import defaultdict 
class Solution:
    def minAreaRect(self, points):
        d, ans = defaultdict(set), float('inf')
        for r, c in points:
            d[r].add(c)
         
        A = sorted(d.keys())
        for i, r1 in enumerate(A):
            cols1 = d[r1]
            for r2 in A[i+1:]:
                cols2 = d[r2]
                s = sorted(cols1 & cols2)
                for c1, c2 in zip(s[:-1], s[1:]):
                    area = abs((r1 - r2) * (c1 - c2))
                    if area < ans: ans = area
        return ans if ans < float('inf') else 0

from collections import defaultdict 
class Solution:
    def minAreaRect(self, points):
        d, ans = defaultdict(set), float('inf')
        for r, c in points:
            d[r].add(c)
         
        A = sorted(d.keys())
        for i, r1 in enumerate(A):
            cols1 = d[r1]
            for r2 in A[i+1:]:
                cols2 = d[r2]
                s = sorted(cols1 & cols2)
                for c1, c2 in zip(s[:-1], s[1:]):
                    area = abs((r1 - r2) * (c1 - c2))
                    ans = min(ans, area)
        return ans if ans < float('inf') else 0

from collections import defaultdict
from itertools import combinations
import sys

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        coordMap = defaultdict(list)
        visited = {}
        for x, y in points:
            coordMap[x].append(y)
        ans = sys.maxsize
        for x in sorted(coordMap):
            for y1, y2 in combinations(sorted(coordMap[x]), 2):
                if (y1, y2) in visited:
                    ans = min(ans, (x-visited[y1, y2])*abs(y1-y2))
                visited[y1, y2] = x
        return ans if ans != sys.maxsize else 0
                
                
                    
        


from collections import defaultdict
class Solution:
    def minAreaRect(self, points):
        d, ans = defaultdict(set), float('inf')
        for r, c in points:
            d[r].add(c)

        A = list(d.keys())
        for i, r1 in enumerate(A):
            cols1 = d[r1]
            for r2 in A[i+1:]:
                cols2 = d[r2]
                s = sorted(cols1 & cols2)
                for c1, c2 in zip(s[:-1], s[1:]):
                    area = abs((r1 - r2) * (c1 - c2))
                    if area < ans: ans = area
        return ans if ans < float('inf') else 0

# from collections import defaultdict
# class Solution:
#     def minAreaRect(self, points):
#         d = defaultdict(set)
#         rset, cset, N, ans = set(), set(), len(points), float('inf')
#         for r, c in points:
#             rset.add(r)
#             cset.add(c)
#         Nr, Nc = len(rset), len(cset)
#         if Nr == N or Nc == N:
#             return 0
#         elif Nr < Nc:
#             for r, c in points:
#                 d[r].add(c)
#         else:
#             for r, c in points:
#                 d[c].add(r)
# 
#         A = sorted(d.keys())
#         for i, r1 in enumerate(A):
#             cols1 = d[r1]
#             for r2 in A[i+1:]:
#                 cols2 = d[r2]
#                 s = sorted(cols1 & cols2)
#                 for c1, c2 in zip(s[:-1], s[1:]):
#                     area = abs((r1 - r2) * (c1 - c2))
#                     ans = min(ans, area)
#         return ans if ans < float('inf') else 0


from collections import defaultdict

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        if len(points) < 4:
            return 0
        #x1,y2  x2,y2
        #x1,y1  x2,y1
        
        xp = defaultdict(set)
        yp = defaultdict(set)
        for x, y in points:
            xp[x].add(y)
            yp[y].add(x)
        
        a_min = None
        for x1, ys in xp.items():
            if len(ys) < 2:
                continue
            for y1 in ys:
                for y2 in ys:
                    if y1 >= y2:
                        continue
                    for x2 in yp.get(y2, []):
                        if x1 >= x2:
                            continue
                        if y1 in xp[x2]:
                            area = (x2 - x1) * (y2 - y1)
                            if a_min is None:
                                a_min = area
                            else:
                                a_min = min(area, a_min)
        if a_min is None:
            return 0
        return a_min

import math

from collections import defaultdict

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        horizontal_lines = defaultdict(set)
        vertical_lines   = defaultdict(set)
        
        for x, y in points:
            vertical_lines[x].add(y)
            horizontal_lines[y].add(x)
        
        min_area = math.inf 
        for x, y in points:
            for next_y in vertical_lines[x]:
                if next_y > y:
                    for intersect_x in horizontal_lines[y] & horizontal_lines[next_y]:
                        if intersect_x < x:
                            min_area = min((x - intersect_x) * (next_y - y), min_area)
                            
        if min_area == math.inf:
            return 0
        else:
            return min_area

from collections import defaultdict
class Solution:
    # O(n ^ 2) Time | O(n) Space
    def minAreaRect(self, points: List[List[int]]) -> int:
        rows = defaultdict(list)
        for i, j in points:
            rows[i].append(j)
        result = float('inf')
        lastRow = {}
        for row in sorted(rows.keys()):
            cols = (rows[row])
            cols.sort()
            for j, col2 in enumerate(cols):
                for i in range(j):
                    col1 = cols[i]
                    if (col1, col2) in lastRow:
                        result = min(result, ((row - lastRow[(col1, col2)]) * (col2 - col1)))
                    lastRow[(col1, col2)] = row
        return result if result != float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        # sort by x // N log(N)
        # [y0, y1] -> (min dx, last) // log(N)

        lines = self.find_lines(points)
        min_area = 0
        d = {}
        for x, ys in lines:
            if ys in d:
                px = d[ys]
                curr_min_area = abs(x - px) * abs(ys[0] - ys[1])
                min_area = min(min_area, curr_min_area) if min_area > 0 else curr_min_area
            d[ys] = x
        return min_area
        
    def find_lines(self, points: List[List[int]]) -> List[Tuple[int, Tuple[int, int]]]:
        ps = defaultdict(list)
        for x, y in sorted(points):
            ps[x].append(y)
        lines = []
        for x, ys in ps.items():
            for i, y0 in enumerate(ys):
                for y1 in ys[i+1:]:
                    lines.append((x, (y0, y1)))
        return lines

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        n = len(points)
        nx = len(set(x for x, y in points))
        ny = len(set(y for x, y in points))
        if nx == n or ny == n:
            return 0

        p = collections.defaultdict(list)
        if nx > ny:
            for x, y in points:
                p[x].append(y)
        else:
            for x, y in points:
                p[y].append(x)

        lastx = {}
        res = float('inf')
        for x in sorted(p):
            p[x].sort()
            for i in range(len(p[x])):
                for j in range(i):
                    y1, y2 = p[x][j], p[x][i]
                    if (y1, y2) in lastx:
                        res = min(res, (x - lastx[y1, y2]) * abs(y2 - y1))
                    lastx[y1, y2] = x
        return res if res < float('inf') else 0


from collections import defaultdict

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        
        points.sort(key = lambda x: (x[0], x[1]))
        
        heights = defaultdict(list)
        for point in points:
            x, h = point
            heights[x].append(h)
            
        segments = {}
        keys = list(heights.keys())
        keys.sort()
        min_area = float('inf')
        
        for x in keys:    
            heights_x = heights[x]
            if len(heights_x) < 2:
                continue
                
            # print(x, heights_x)
            
            for i in range(len(heights_x)-1):
                for j in range(i+1, len(heights_x)):
                    pair = (heights_x[i], heights_x[j])
                    if pair not in segments:
                        segments[pair] = [None, x]
                    else:
                        height_diff = heights_x[j] - heights_x[i]
                        width_diff = x  - segments[pair][1]
                        area = height_diff * width_diff
                        min_area = min(min_area, area)
                        segments[pair] = [segments[pair][1], x]
            
        if min_area == float('inf'):
            return 0
        else:
            return min_area

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        x_dict = defaultdict(set)
        for p in points:
            x_dict[p[0]].add(p[1])

        min_area = float(\"inf\")
        x = sorted(list(x_dict.keys()))
        for i in range(len(x)):
            for j in range(i+1, len(x)):
                y = sorted(list(x_dict[x[i]] & x_dict[x[j]]))
                if len(y)>1:
                    min_height = min(y[k]-y[k-1] for k in range(1, len(y)))
                    min_area = min(min_area, (x[j]-x[i])*min_height)
        
        if min_area == float(\"inf\"):
            return 0
        
        return min_area
    
                    
                
                

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        points.sort()
        xs = collections.defaultdict(list)
        ys = collections.defaultdict(list)
        for x, y in points:
            xs[x].append(y)
            ys[y].append(x)
        result = math.inf
        for x0, ylist in xs.items():
            for y1, y2 in itertools.combinations(ylist, 2):
                xlist1, xlist2 = ys[y1], ys[y2]
                m, n = len(xlist1), len(xlist2)
                i = bisect.bisect(xlist1, x0)
                if i == m:
                    continue
                j = bisect.bisect(xlist2, x0)
                if j == n:
                    continue
                while i < m and j < n:
                    if xlist1[i] < xlist2[j]:
                        i += 1
                    elif xlist1[i] > xlist2[j]:
                        j += 1
                    else:
                        result = min(result, (xlist1[i] - x0) * (y2 - y1))
                        break
        return 0 if math.isinf(result) else  result

import collections
class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        if not points:
            return 0
        dic = collections.defaultdict(list)
        for x,y in points:
            dic[x].append(y)
        
        res = float('inf')
        store = {}
        for x in sorted(dic):
            col = sorted(dic[x])
            for i, y1 in enumerate(col):
                for j in range(i):
                    if (y1,col[j]) not in store:
                        store[(y1,col[j])] = x
                    else:
                        res = min(res, (y1-col[j])*(x-store[(y1,col[j])]))
                        store[(y1,col[j])] = x
        return res if res != float('inf') else 0


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        table = {}
        mark = []
        best = 0
        for i in points:
            if i[0] not in table:
                table[i[0]] = [[i[1]],[]]
            else:
                if i[1] not in table[i[0]][0]:
                    for j in table[i[0]][0]:
                        if j < i[1]:
                            table[i[0]][1].append((j, i[1]))
                        else:
                            table[i[0]][1].append((i[1],j))
                    table[i[0]][0].append(i[1])
                    if len(table[i[0]][0]) == 2:
                        mark.append(i[0])
        g = {}
        if len(mark) < 2:
            return 0
        for i in mark:
            p = table[i]
            for j in p[1]:
                if j in g:
                    for k in g[j]:
                        temp = abs((k - i) * (j[1] - j[0]))
                        if best == 0 or temp < best:
                            best = temp
                    g[j].append(i)
                else:
                    g[j] = [i]
        return best

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        n = len(points)
        if n < 4:
          return 0
        s = set([tuple(i) for i in points])
        d = {}
        for i in points:
          if i[0] in d:
            d[i[0]].append(i[1])
          else:
            d[i[0]] = [i[1]]
        # print(d)
        for i in list(d.keys()):
          if len(d[i]) == 1:
            d.pop(i)
        # print(d)
        dkeys = list(d.keys())
        minarea = float('inf')
        for i in range(len(dkeys)-1):
          if len(points[i]) == 1:
            continue

          for x in range(len(d[dkeys[i]])-1):
            for y in range(x+1, len(d[dkeys[i]])):
              for j in range(i+1, len(dkeys)):
                # print(i, j, d[dkeys[i]], x, y, (dkeys[j], d[dkeys[i]][x]), (dkeys[j], d[dkeys[i]][y]))
                if (dkeys[j], d[dkeys[i]][x]) in s and (dkeys[j], d[dkeys[i]][y]) in s:
                  # found a rectangle
                  # print('test1', dkeys[i], dkeys[j], d[dkeys[i]][x], d[dkeys[i]][y])
                  minarea = min(minarea, abs(dkeys[i]-dkeys[j])*abs(d[dkeys[i]][x]-d[dkeys[i]][y]))
        
        if minarea == float('inf'):
          return 0
        return minarea

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        if len(points)<4:
            return
        minimum=float(\"inf\")
        length=defaultdict(list)
        for x,y in points:
            length[x].append(y)
         
        print(length)
        breadth={}
        
        for x in sorted(length):
            values=length[x]
            print(values)
            values.sort()
            if len(values)<2:
                continue
            for i in range(0,len(values)-1):
                for j in range(i+1,len(values)):
                    if (values[i],values[j]) in breadth:
                        minimum=min(minimum,(x-breadth[(values[i],values[j])])*(values[j]-values[i]))
                        
                    breadth[(values[i],values[j])]=x
        
        if minimum==float(\"inf\"):
            return 0
        else:
            return minimum
        

class Solution:
    def minAreaRect(self, points):
        seen, ans = set(map(tuple, points)), float('inf')
        for i, (p1x, p1y) in enumerate(points):
            for (p2x, p2y) in points[i+1:]:
                area = abs((p2x-p1x)*(p2y-p1y))
                if area > ans or area == 0:
                    continue
                if (p1x, p2y) in seen and (p2x, p1y) in seen:
                    ans = area
        return ans if ans < float('inf') else 0

class Solution(object):
    def minAreaRect(self, points):
        \"\"\"
        :type points: List[List[int]]
        :rtype: int
        \"\"\"
        seen = set()
        res = float('inf')
        # for point, compare with all other points that we've seen so far, a rectangle can only be formed when the two points are diagonal to each other and the other points forming the other two corners also exist. If the points are parallel you don't know if you can form a rectangle yet. If there exists two other points that form a rectangle with them you will run into them both in the future so no worries to attempt to find them right away.
        for (x1, y1) in points:
            for (x2, y2) in seen:
                if (x1, y2) in seen and (x2, y1) in seen:
                    area = abs(x2-x1) * abs(y2-y1)
                    if area < res:
                        res = area
            seen.add((x1, y1))
        return res if res != float('inf') else 0


class Solution(object):
    def minAreaRect(self, points):
        seen = set()
        res = float('inf')
        for x1, y1 in points:
            for x2, y2 in seen:
                if (x1, y2) in seen and (x2, y1) in seen:
                    area = abs(x1 - x2) * abs(y1 - y2)
                    if area and area < res:
                        res = area
            seen.add((x1, y1))
        return res if res < float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        n = len(points)
        nx = len(set(x for x, _ in points))
        ny = len(set(y for _, y in points))
        
        if n == nx or n == ny:
            return 0
        
        p = collections.defaultdict(list)
        if nx > ny:
            for x, y in points:
                p[x].append(y)
        else:
            for x, y in points:
                p[y].append(x)
        
        last_x = {}
        res = float('inf')
        
        for x in sorted(p):
            p[x].sort()
            for i in range(len(p[x])):
                for j in range(i):
                    y1, y2 = p[x][j], p[x][i]
                    
                    if (y1, y2) in last_x:
                        res = min(res, (y2 - y1) * (x - last_x[(y1, y2)]))
                    last_x[(y1, y2)] = x
        
        return res if res < float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        n = len(points)
        nx, ny = len(set(x for x, _ in points)), len(set(y for _, y in points))
        
        if nx == n or ny == n:
            return 0
        p = collections.defaultdict(list)
        if nx > ny:
            for x, y in points:
                p[x].append(y)
        else:
            for x, y in points:
                p[y].append(x)
        
        res = float('inf')
        dic_last = {}
        for x1, x2 in itertools.combinations(p, 2):
            if (len(p[x1]) < 2 or len(p[x2]) < 2):
                continue
            for y1, y2 in itertools.combinations( sorted(set(p[x1]) & set(p[x2])), 2 ):
                res = min(res, abs(x1-x2) * (y2 - y1))
        # for x in sorted(p):
        #     # print(x)
        #     p[x].sort()
        #     for y1, y2 in itertools.combinations(p[x], 2):
        #         if (y1, y2) in dic_last:
        #             res = min(res, abs( x - dic_last[y1, y2]) * abs(y1 -y2))
        #             # if res == 2:
        #             #     print(x, dic_last[y1, y2],  y1, y2)
        #         dic_last[y1, y2] = x
        return res if res < float('inf') else 0
        
        # n = len(points)
        # nx, ny = len(set(x for x, y in points)), len(set(y for x, y in points))
        # if nx == n or ny == n:
        #     return 0
        
        # p = collections.defaultdict(list)
        # if nx > ny:
        #     for x, y in points:
        #         p[x].append(y)
        # else:
        #     for x, y in points:
        #         p[y].append(x)
        # res = float('inf')
        # dic = {}
        # for x in sorted(p):
        #     p[x].sort()
        #     for y1, y2 in itertools.combinations(p[x], 2):
        #         if (y1, y2) in dic:
        #             res = min(res, abs(x - dic[y1, y2]) * abs(y1 - y2) )
        #         dic[y1, y2] = x
        # return res if res < float('inf') else 0
#         dic_x = {}
        
#         n = len(points)
#         nx, ny = len( set(x for x, y in points) ), len( set(y for x, y in points) )
#         if nx == n or ny == n:
#             return 0
        
#         p = collections.defaultdict(list)
#         if nx > ny:
#             for x, y in points:
#                 p[x].append(y)
#         else:
#             for x, y in points:
#                 p[y].append(x)
#         res = float('inf')
#         for x in sorted(p):
#             p[x].sort()
            
#             for y1, y2 in itertools.combinations(p[x], 2):
#                 # y1, y2 = p[x][i], p[x][j]
#                 if (y1, y2) in dic_x:
#                     res = min(res, abs(x - dic_x[y1, y2]) * abs(y1 -y2))
#                 dic_x[y1, y2] = x
#         return res if res < float('inf') else 0
#         seen = set()
        
#         res = float('inf')
#         for x1, y1 in points:
#             for x2, y2 in seen:
#                 if (x1, y2) in seen and (x2, y1) in seen:
#                     area = abs(x1 - x2) * abs(y1 - y2)
#                     if area and res > area:
#                         res = area
#                     # res = min(res, )
#             seen.add((x1, y1))
#         return res if res < float('inf') else 0


from collections import defaultdict
import sys

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        coordMap = defaultdict(set)
        for x, y in points:
            coordMap[x].add(y)
        xlist = sorted(list(coordMap.keys()))
        ans = sys.maxsize
        for i in range(1, len(xlist)):
            for j in range(len(xlist)-i):
                ylist = coordMap[xlist[j]].intersection(coordMap[xlist[j+i]])
                if len(ylist) < 2:
                    continue
                ylist = sorted(list(ylist))
                idx = min(list(range(len(ylist)-1)), key=lambda i: ylist[i+1]-ylist[i])
                h = ylist[idx+1]-ylist[idx]
                ans = min(ans, (xlist[j+i]-xlist[j])*h)
        return ans if ans != sys.maxsize else 0
                    
        


class Solution(object):
    def minAreaRect(self, points):
        \"\"\"
        :type points: List[List[int]]
        :rtype: int
        \"\"\"
        seen = set()
        res = float('inf')
        # for point, compare with all other points that we've seen so far, a rectangle can only be formed when the two points are diagonal to each other and the other points forming the other two corners also exist. If the points are parallel you don't know if you can form a rectangle yet. If there exists two other points that form a rectangle with them you will run into them both in the future so no worries to attempt to find them right away.
        for (x1, y1) in points:
            # all points are distinct so you are guaranteed to not run into (x1, y1)
            for (x2, y2) in seen:
                if (x1, y2) in seen and (x2, y1) in seen:
                    area = abs(x2-x1) * abs(y2-y1)
                    if area < res:
                        res = area
            seen.add((x1, y1))
        return res if res != float('inf') else 0

class Solution:
    def minAreaRect(self, points):
        pSet, ans = set(map(tuple, points)), float('inf')
        for i, (p1x, p1y) in enumerate(points):
            for (p2x, p2y) in points[i+1:]:
                area = abs((p2x-p1x)*(p2y-p1y))
                if area > ans or area == 0:
                    continue
                if (p1x, p2y) in pSet and (p2x, p1y) in pSet:
                    ans = area
        return ans if ans < float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        seen = set()
        result = float('inf')
        for x1, y1 in points:
            for x2, y2 in seen:
                if (x1, y2) in seen and (x2, y1) in seen:
                    area = abs(x1 - x2) * abs(y1 - y2)
                    if area and area < result:
                        result = area
            seen.add((x1, y1))
            
        return result if result != float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
#         res = 2**31
#         s = {(p[0], p[1]) for p in points}
        
#         for i, p1 in enumerate(points):
#             for j, p2 in enumerate(points[i + 1:]):
#                 if p1[0] != p2[0] and p1[1] != p2[1]:
#                     if (p1[0], p2[1]) in s and (p2[0], p1[1]) in s:
#                         res = min(res, abs(p1[1] - p2[1]) * abs(p1[0] - p2[0]))
#         res = res if res < 2**31 else 0
#         return res
        
        seen = set()
        res = float('inf')
        for x1, y1 in points:
            for x2, y2 in seen:
                if (x1, y2) in seen and (x2, y1) in seen:
                    area = abs(x1 - x2) * abs(y1 - y2)
                    if area and area < res:
                        res = area
            seen.add((x1, y1))
        return res if res < float('inf') else 0

                
                


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        points.sort()
        points_set = set([tuple(point) for point in points])
        smallest = float('inf')
        for i, (x1, y1) in enumerate(points):
            for j, (x2, y2) in enumerate(points[i:]):
                if x1 < x2 and y1 < y2 and (x1, y2) in points_set and (x2, y1) in points_set:
                    area = (x2 - x1) * (y2 - y1)
                    smallest = min(smallest, area)
        return smallest if smallest != float('inf') else 0


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        seen = set()
        res = float('inf')
        for x1, y1 in points:
            for x2, y2 in seen:
                if (x1, y2) in seen and (x2, y1) in seen:
                    area = abs(x1 - x2) * abs(y1 - y2)
                    if area and area < res:
                        res = area
            seen.add((x1, y1))
        return res if res < float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        points.sort()
        points_set = set([tuple(point) for point in points])
        smallest = float('inf')
        for i, (x1, y1) in enumerate(points):
            for j, (x2, y2) in enumerate(points[i:], i):
                if x1 < x2 and y1 < y2 and (x1, y2) in points_set and (x2, y1) in points_set:
                    area = (x2 - x1) * (y2 - y1)
                    smallest = min(smallest, area)
        return smallest if smallest != float('inf') else 0


class Solution(object):
    def minAreaRect(self, points):
        n = len(points)
        nx = len(set(x for x, y in points))
        ny = len(set(y for x, y in points))
        if nx == n or ny == n:
            return 0

        p = collections.defaultdict(list)
        if nx > ny:
            for x, y in points:
                p[x].append(y)
        else:
            for x, y in points:
                p[y].append(x)

        lastx = {}
        res = float('inf')
        for x in sorted(p):
            p[x].sort()
            for i in range(len(p[x])):
                for j in range(i):
                    y1, y2 = p[x][j], p[x][i]
                    if (y1, y2) in lastx:
                        res = min(res, (x - lastx[y1, y2]) * abs(y2 - y1))
                    lastx[y1, y2] = x
        return res if res < float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        import math
        from collections import defaultdict
        row_m = defaultdict(list)
        
        for x, y in points:
            row_m[x].append(y)
            
        sol = float(\"inf\")
        # print(rows)
        pairs = {}
        
        for x in sorted(row_m):
            row = row_m[x]
            for i in range(len(row)):                
                for j in range(i + 1, len(row)):
                    
                    candidate = tuple(sorted([row[i], row[j]]))
                    if candidate in pairs:
                        sol = min(
                            sol,
                            abs(pairs[candidate] - x) * abs(row[i] - row[j]),
                        )                        
                    
                    pairs[candidate] = x
        # print(pairs)
        if sol == float('inf'):
            return 0
        return sol
                
            
            

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        point_set = set()
        points.sort()
        for point in points:
            point_set.add((point[0], point[1]))
        
        min_area = math.inf
        N = len(points)
        for i in range(N):
            start_point = points[i]
            for j in range(i+1,N):
                end_point = points[j]
                if end_point[0] > start_point[0] and end_point[1] > start_point[1]:
                    area = (end_point[0]-start_point[0])*(end_point[1]-start_point[1])
                    if area < min_area and \\
                       (start_point[0], end_point[1]) in point_set and \\
                       (end_point[0], start_point[1]) in point_set:
                        min_area = area
                
        return min_area if min_area != math.inf else 0
            
        

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
       
        # pointsSet = set([(x, y) for x, y in points])
        # minv = float('inf')
        # for i in range(len(points)): 
        #     for j in range(i + 1, len(points)):
        #         if points[i][0] != points[j][0] and points[i][1] != points[j][1] and  (points[i][0], points[j][1]) in pointsSet and (points[j][0], points[i][1]) in pointsSet:
        #             minv = min(minv, abs(points[i][0] - points[j][0])*abs(points[i][1] - points[j][1]))
        #             if minv == 1: print(points[i], points[j])
        # return minv if minv != float('inf') else 0
    
        
        seen = set()
        minv = float('inf')
        for x, y in points:
            for a, b in seen:
                if x != a and y != b and (x, b) in seen and (a, y) in seen:
                    minv = min(minv, abs(a - x)*abs(y - b))
            seen.add((x,y))
        return  minv if minv != float('inf') else 0
                    


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        seen = set()
        output = float('inf')
        
        for x1, y1 in points:
            for x2, y2 in seen:
                if (x1, y2) in seen and (x2, y1) in seen:
                    area = abs(x1 - x2) * abs(y1 - y2)
                    if area > 0 and area < output:
                        output = area
            seen.add((x1,y1))
        return output if output < float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        res = inf = float('inf')
        seen = set()
        n = len(points)
        for x1, y1 in points:
            for x2, y2 in seen:
                if (x1, y2) in seen and (x2, y1) in seen:
                    res = min(res, abs((x2-x1)*(y2-y1)))
            seen.add((x1, y1))
        return res if res != inf else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        if len(points) < 4:
            return 0
        
        area = float('inf')
        seen = set()
        for x1, y1 in points:
            for x2, y2 in seen:
                if x1 != x2 and y1 != y2:
                    if (x1, y2) in seen and (x2, y1) in seen:
                        l, w = abs(x1 - x2), abs(y1 - y2)
                        area = min(area, l*w)
                        
            seen.add((x1, y1))
        
        return 0 if area == float('inf') else area

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        seen = set()
        res = float('inf')
        for x1, y1 in points:
            for x2, y2 in seen:
                if (x1, y2) in seen and (x2, y1) in seen:
                    area = abs(x1 - x2) * abs(y1 - y2)
                    if area and area < res:
                        res = area
            seen.add((x1, y1))
        return res if res < float('inf') else 0


import sys
class Solution:
    def minAreaRect(self, points):
        seen = set()
        res = float('inf')
        for x1, y1 in points:
            for x2, y2 in seen:
                if (x1, y2) in seen and (x2, y1) in seen:
                    area = abs(x1 - x2) * abs(y1 - y2)
                    if area and area < res:
                        res = area
            seen.add((x1, y1))
        return res if res < float('inf') else 0

class Solution(object):
     def minAreaRect(self, points):
        seen = set()
        res = float('inf')
        for x1, y1 in points:
            for x2, y2 in seen:
                if (x1, y2) in seen and (x2, y1) in seen:
                    area = abs(x1 - x2) * abs(y1 - y2)
                    if area and area < res:
                        res = area
            seen.add((x1, y1))
        return res if res < float('inf') else 0

class Solution(object):
    def minAreaRect(self, points: List[List[int]]) -> int:
        n = len(points)
        setx = set()
        sety = set()
        for x, y in points:
            setx.add(x)
            sety.add(y)
        nx = len(setx)
        ny = len(sety)

        if nx == n or ny == n:
            return 0
        p = collections.defaultdict(list)
        if nx > ny:
            for x, y in points:
                p[x].append(y)
        else:
            for x, y in points:
                p[y].append(x)

        res = float('inf')
        dic_last = {}
        for x in sorted(p):
            p[x].sort()
            for y1, y2 in itertools.combinations(p[x], 2):
                if (y1, y2) in dic_last:
                    res = min(res, (x - dic_last[y1, y2]) * (y2-y1))
                dic_last[y1, y2] = x
        return res if res < float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        seen = set()
        ans = float('inf')
        
        for x1,y1 in points:
            for x2,y2 in seen:
                if (x1,y2) in seen and (x2,y1) in seen:
                    ans = min(ans, abs(x1-x2)*abs(y1-y2) )
            
            seen.add((x1,y1))
            
        return(ans if ans!=float(inf) else 0)
            


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        '''
        Input: [[1,1],[1,3],[3,1],[3,3],[2,2]]
        visited = {(1,1), (1,3)}
        '''
        visited = set()
        output = float('inf')
        for x1, y1 in points:
            for x2, y2 in visited:
                if (x1, y2) in visited and (x2, y1) in visited:
                    temp = abs(x2 - x1) * abs(y2 - y1)
                    if temp < output:
                        output = temp
            visited.add((x1, y1))
        return output if output != float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        # count by diagonal, Time O(n^2); space O(n)
        seen = set()
        res = float('inf')
        for x1, y1 in points:
            for x2, y2 in seen:
                if (x1, y2) in seen and (x2, y1) in seen:
                    area = abs(x1 - x2) * abs(y1 - y2)
                    res = min(res, area)
                    
            seen.add((x1, y1))
        return res if res != float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        res = inf = float('inf')
        seen = set()
        n = len(points)
        for x1, y1 in set(map(tuple, points)):
            for x2, y2 in seen:
                if (x1, y2) in seen and (x2, y1) in seen:
                    res = min(res, abs((x2-x1)*(y2-y1)))
            seen.add((x1, y1))
        return res if res != inf else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        if len(points) < 4:
            return 0
        
        area = float('inf')
        seen = set()
        for x1, y1 in points:
            for x2, y2 in seen:
                if (x1, y2) in seen and (x2, y1) in seen:
                    l, w = abs(x1 - x2), abs(y1 - y2)
                    area = min(area, l*w)
                        
            seen.add((x1, y1))
        
        return area if area < float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        seen = set()
        res = float('inf')
        for (x1, y1) in points:
            for (x2, y2) in seen:
                if (x1, y2) in seen and (x2, y1) in seen:
                    res = min(res, abs(x2-x1) * abs(y2-y1))
            seen.add((x1, y1))
        return res if res != float('inf') else 0


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        min_area = float('inf')
        seen = set()
        
        poi = []
        for x, y in points:
            poi.append((x, y))
        
        for x1, y1 in points:
            for x2, y2 in seen:
                if (x1, y2) in seen and (x2, y1) in seen:
                    area = abs(x1 - x2) * abs(y1 - y2)
                    if area and area < min_area:
                        min_area = area

            seen.add((x1, y1))
                    
        return min_area if min_area < float('inf') else 0
            


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        seen = set()
        min_area = math.inf
        for x1, y1 in points:
            for x2, y2 in seen:
                if (x1, y2) in seen and (x2, y1) in seen and (x1, y2) != (x2, y1):
                    area = abs(x2 - x1) * abs(y2 - y1)
                    if area < min_area:
                        min_area = area
            seen.add((x1, y1))
        return min_area if min_area < math.inf else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        if len(points) < 4:
            return 0
        
        seen = set()
        minarea = sys.maxsize
        for x1, y1 in points:
            for x2, y2 in seen:
                if (x1,y2) in seen and (x2,y1) in seen:
                    minarea = min(minarea, abs(x2-x1)*abs(y2-y1))
            seen.add((x1,y1))
        return minarea if minarea < sys.maxsize else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        
        res = float('inf') # initiate as inf
        lookup = set()
        for x1, y1 in points:
            for x2, y2 in lookup:
                if (x1, y2) in lookup and (x2, y1) in lookup: # if all in the lookup
                    res = min(res, abs(x1-x2) * abs(y1-y2)) # check the min area
            # add to lookup for the outer loop
            lookup.add((x1, y1))
        return res if res != float('inf') else 0
    
    
        ys_with_same_x = defaultdict(list)
        for x, y in points:
            ys_with_same_x[x].append(y)
        
        pair_of_ys_history = {}
        res = float('inf')
        for x, ys in sorted(ys_with_same_x.items()):
            ys.sort()
            for i, y1 in enumerate(ys):
                for j in range(i):
                    y2 = ys[j]
                    if (y1, y2) in pair_of_ys_history:
                        res = min(res, (x - pair_of_ys_history[(y1,y2)]) * (y1-y2) )
                    pair_of_ys_history[(y1,y2)] = x
        if res == float('inf'):
            return 0
        else:
            return res


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        n = len(points)
        nx = len(set(x for x, y in points))
        ny = len(set(y for x, y in points))
        
        p = collections.defaultdict(list)
        lastseen = {}
        if nx > ny:
            for x, y in points:
                p[x].append(y)
        else:
            for x, y in points:
                p[y].append(x)
                
        minarea =float(\"inf\")     
        for x in sorted(p):
            p[x].sort()
            for i in range(len(p[x])):
                for j in range(i):
                    y1,y2 = p[x][i], p[x][j]
                    if (y1,y2) in lastseen:
                        area = abs(y1-y2) * abs(x-lastseen[(y1,y2)])
                        minarea = min(area,minarea)
                    lastseen[(y1,y2)] = x
        #print(lastseen)
        return minarea if minarea < 10**21 else 0

from collections import defaultdict
class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        seen = set()
        res = float('inf')
        for x1, y1 in points:
            for x2, y2 in seen:
                if (x1,y2) in seen and (x2,y1) in seen:
                    # find the rectangle
                    res = min(res, abs(x1-x2)*abs(y1-y2))
            seen.add((x1,y1))
            
        return res if res < float('inf') else 0
                    
                


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        point_set = set((x, y) for x, y in points)
        min_rec = math.inf
        for x1, y1 in points:
            for x2, y2 in points:
                if x1 < x2 and y1 > y2:
                    if (x1, y2) in point_set and (x2, y1) in point_set:
                        min_rec = min(min_rec, (x2 - x1) * (y1 - y2))
        return 0 if min_rec == math.inf else min_rec

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        if len(points) < 4:
            return 0
        
        area = float('inf')
        seen = set()
        for x1, y1 in points:
            for x2, y2 in seen:
                if (x1, y2) in seen and (x2, y1) in seen:
                    l, w = abs(x1 - x2), abs(y1 - y2)
                    area = min(area, l*w)
                        
            seen.add((x1, y1))
        
        return 0 if area == float('inf') else area

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        # N = 500 is really loose.
        # Just check all pairs of points.
        # Time O(N^2) , 1000ms ~ 1200ms
        
        seen = set()
        minA = float('inf')
        for x1, y1 in points:
            for x2, y2 in seen:
                if (x1,y2) in seen and (x2,y1) in seen:
                    area = abs(x1-x2)*abs(y1-y2)
                    minA = min(minA, area)
            seen.add((x1,y1))
        return minA if minA<float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        
        
        # Rectangle (x0, y0), (x1, y1), (x0, y1), (x1, y0)
        min_area = float('inf')
        seen = set()
        
        
        for x0, y0 in points: 
            for x1, y1 in seen: 
                if (x0, y1) in seen and (x1, y0) in seen: 
                    current_area = (abs(y1-y0))*(abs(x1-x0))
                    # print(current_area)
                    min_area = min(min_area, current_area)
            seen.add((x0, y0))
        

        
        return min_area if min_area != float('inf') else 0 
    
    
    
    
    
    
        #     .       .
        #         .
        #     .       .
        # .


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        
        def smallestinterval(l):
            rst = float('inf')
            for i in range(1, len(l)):
                if l[i] - l[i-1] < rst:
                    rst = l[i] - l[i-1]
            return rst
            
        xloc = defaultdict(set)
#        yloc = defaultdict(set())
        for p in points:
            xloc[p[0]].add(p[1])
#            yloc[p[1]].add(p[0])
        minarea = float('inf')
        xkeys = list(xloc.keys())
        for x1 in xkeys:
            for x2 in xkeys:
                if x1 != x2:
                    sharedy = xloc[x1] & xloc[x2]
                    if len(sharedy)> 1:
                        ally = list(sharedy)
                        ally.sort()
                        smallinterval = smallestinterval(ally)
                        if smallinterval * abs(x1 - x2) < minarea:
                            minarea = smallinterval * abs(x1-x2)
        if minarea == float('inf'):
            return 0
        return minarea
        


from collections import defaultdict
class Solution:
    def minAreaRect(self, points):
        X = list(zip(*points))
        Nr, Nc = len(set(X[0])), len(set(X[1]))
        if Nr == 1 or Nc == 1: return 0
        if Nr < Nc:
            points = [[c, r] for (r, c) in points]
            return self.minAreaRect(points)

        points.sort()
        columns = defaultdict(list)
        for r, c in points:
            columns[r].append(c)
        ans, lastc = float('inf'), dict()
        for r, cols in columns.items():
            for i, c1 in enumerate(cols):
                for c2 in cols[i+1:]:
                    if (c1, c2) in lastc:
                        area = (r - lastc[(c1, c2)]) * (c2 - c1)
                        #ans = min(ans, area)
                        if area < ans: ans = area
                    lastc[(c1, c2)] = r
        return ans if ans < float('inf') else 0

class Solution(object):
    def minAreaRect(self, points: List[List[int]]) -> int:
        n = len(points)
        setx = set()
        sety = set()
        for x, y in points:
            setx.add(x)
            sety.add(y)
        nx = len(setx)
        ny = len(sety)

        if nx == n or ny == n:
            return 0
        p = collections.defaultdict(list)
        if nx > ny:
            for x, y in points:
                p[x].append(y)
        else:
            for x, y in points:
                p[y].append(x)

        res = float('inf')
        dic_last = {}
        for x in sorted(p):
            column = p[x]
            column.sort()
            for y1, y2 in itertools.combinations(column, 2):
                if (y1, y2) in dic_last:
                    res = min(res, (x - dic_last[y1, y2]) * (y2 -y1))
                dic_last[y1, y2] = x
        return res if res < float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        n = len(points)
        min_area = math.inf
        seen = set()
        
        for x0, y0 in points:
            for x1, y1 in seen:
                if (x0, y1) in seen and (x1, y0) in seen:
                    area = abs(x0 - x1) * abs(y0 - y1)
                    if area > 0:
                        min_area = min(min_area, area)
            seen.add((x0, y0))
        
        return 0 if min_area == math.inf else min_area

from collections import defaultdict

class Solution:
    
    def minAreaRect(self, points: List[List[int]]) -> int:
        area = float('inf')
        gx = defaultdict(list)
        gy = defaultdict(list)
        
        for p in points:
            [x1, y1] = p
            
            if x1 in gx and y1 in gy:
                for y2 in gx[x1]:
                    if y2 in gy:
                        for x2 in gy[y2]:
                            if x2 in gy[y1]:
                                area = min(area, abs(x1-x2) * abs(y1-y2))
            gx[x1].append(y1)
            gy[y1].append(x1)
            
        return area if area < float('inf') else 0
                    
                    
                


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        if len(points) < 4:
            return 0
        group_x_coords = collections.defaultdict(list)
        group_y_coords = collections.defaultdict(set)
        
        # Form the grouping
        for point in points:
            group_x_coords[point[0]].append(point[1])
            group_y_coords[point[1]].add(point[0])
        
        
        # Pick element from x coords
        # Then pick another element from it
        # Do the intersection on the group_y_coords 
        # Find the maximum in the intersection
        min_area = float('inf')
        for key in group_x_coords.keys():
            if len(group_x_coords[key]) < 2:
                continue
            for i in range(len(group_x_coords[key])):
                for j in range(i + 1, len(group_x_coords[key])):
                    y_val_1 = group_x_coords[key][i]
                    y_val_2 = group_x_coords[key][j]
                    # Get the max rectangle
                    common_x_coords = group_y_coords[y_val_1] & group_y_coords[y_val_2]
                    if len(common_x_coords) > 1:
                        y_diff = abs(y_val_1 - y_val_2)
                        # Need to find number closest to current
                        x_diff = float('inf')
                        for ele in common_x_coords:
                            if ele == key:
                                continue
                            if abs(ele - key) < x_diff:
                                x_diff = abs(ele - key)
                        area = x_diff * y_diff
                        if area < min_area:
                            min_area = area
        if min_area == float('inf'):
            return 0
        return min_area

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        rows = collections.defaultdict(list)
        
        points.sort()
        
        
        seen = set()
        
        result = float('inf')
        
        for x1, y1 in points:
            for x2, y2 in seen:
                if x1 < x2 and y1 < y2:
                    continue
                    
                if (x1, y2) in seen and (x2, y1) in seen :
                    
                    area = abs(x1 - x2) * abs(y1 - y2)
                    if area and area < result:
                        result = area
            seen.add((x1, y1))
        
        return result if result < float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        # idea, use lookup set to store the point that has seen before. for (x1,y1) in points, for (x2,y2) in lookup, check whether (x1,y2) and (x2, y1) in lookup. Time O(n^2), Space O(n).
        res = float('inf') # initiate as inf
        lookup = set()
        for x1, y1 in points:
            for x2, y2 in lookup:
                if (x1, y2) in lookup and (x2, y1) in lookup: # if all in the lookup
                    res = min(res, abs(x1-x2) * abs(y1-y2)) # check the min area
            # add to lookup for the outer loop
            lookup.add((x1, y1))
        return res if res != float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        globalMinArea = float('inf')
        a = set()
        for x,y in points:
            for x1,y1 in a:
                if (x,y1) in a and (x1,y) in a:
                    currentMinArea = abs(x-x1)*abs(y-y1)
                    globalMinArea = min(globalMinArea, currentMinArea)
            a.add((x,y))
        return globalMinArea if globalMinArea<float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        seen = set()
        res = float('inf')
        for point in points:                                                    
            x, y = point[0], point[1]            
            for _x, _y in seen:
                if (x, _y) in seen and (_x, y) in seen:
                    res = min(res, abs(x - _x) * abs(y - _y))
            seen.add((x, y))
            
        if res == float('inf'):
            return 0
        return res
    
    ''' mine
    class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
                
        Y = dict()
        X = dict()                
        for point in points:
            x, y = point[0], point[1]
            if x not in X:
                X[x] = set()
            X[x].add(y)
            
            if y not in Y:
                Y[y] = set()
            Y[y].add(x)
        
                
        _min = float('inf')
        for point in points:            
            x, y = point[0], point[1]
            for _y in X[x]:
                if _y > y:
                    for _x in Y[_y]:
                        if _x > x and _x in Y[y]:
                            h = abs(_y - y)
                            w = abs(_x - x)                    
                            if h * w != 0:
                                _min = min(_min, h * w)
                        
        if _min == float('inf'):
            return 0
        
        return _min
            
    
    '''

            
            
'''
    def minAreaRect(self, points):
        n = len(points)
        nx = len(set(x for x, y in points))
        ny = len(set(y for x, y in points))
        if nx == n or ny == n:
            return 0

        p = collections.defaultdict(list)
        if nx > ny:
            for x, y in points:
                p[x].append(y)
        else:
            for x, y in points:
                p[y].append(x)

        lastx = {}
        res = float('inf')
        for x in sorted(p):
            p[x].sort()
            for i in range(len(p[x])):
                for j in range(i):
                    y1, y2 = p[x][j], p[x][i]
                    if (y1, y2) in lastx:
                        res = min(res, (x - lastx[y1, y2]) * abs(y2 - y1))
                    lastx[y1, y2] = x
        return res if res < float('inf') else 0

'''
        
        
            
            


class Solution(object):
    def minAreaRect(self, points):
        min_area = sys.maxsize
        points_table = set()
        for x, y in points:
            points_table.add((x,y))
        for x1, y1 in points:
            for x2, y2 in points:
                if (x1 > x2) and (y1 > y2):
                    if (x1, y2) in points_table and (x2, y1) in points_table:
                        area = abs(x1 -  x2) * abs(y1 - y2)
                        if area:
                            min_area = min(area, min_area)
        return 0 if min_area == sys.maxsize else min_area

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        hs = set()
        N = len(points)
        res = float('inf')
        
        for i in range(N):
            x1, y1 = points[i]
            for x2, y2 in hs:
                if (x1, y2) in hs and (x2, y1) in hs:
                    res = min(res, abs(x1-x2)*abs(y1-y2))
            
            hs.add((x1, y1))
            
        return res if res!=float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        ans=float('inf')
        s=set()
        for i,j in points:
            for x,y in s:
                if (i,y) in s and (x,j) in s:
                    ans=min(ans,abs(i-x)*abs(j-y))
            s.add((i,j))
        return ans if ans!=float('inf') else 0


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        \"\"\"
        :type points: List[List[int]]
        :rtype: int
        \"\"\"
        min_area = sys.maxsize
        points_table = set()
        
        for x, y in points:
            points_table.add((x,y))
            
        for x1, y1 in points:
            for x2, y2 in points:
                if x1 > x2 and y1 > y2: # Skip looking at same point
                    if (x1, y2) in points_table and (x2, y1) in points_table:
                        area = abs(x1 -  x2) * abs(y1 - y2)
                        if area:
                            min_area = min(area, min_area)
                        
        return 0 if min_area == sys.maxsize else min_area       

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        res = float('inf')
        lookup = set()
        for x1, y1 in points:
            for x2, y2 in lookup:
                if (x1, y2) in lookup and (x2, y1) in lookup:
                    res = min(res, abs(x2 - x1) * abs(y2 - y1))
            lookup.add((x1, y1))
        return res if res != float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        #
        #  0123456
        # 0
        # 1 * **
        # 2 
        # 3 * **
        # 4
        seen = set()
        best = 0
        
        for p1 in points:
            x1, y1 = p1
            for p2 in seen:
                x2, y2 = p2
                if (x1, y2) in seen and (x2, y1) in seen:
                    area = abs(x2 - x1) * abs(y2 - y1)
                    if best == 0:
                        best = area
                    else:
                        best = min(best, area)
            seen.add((x1, y1))
        return best


from collections import defaultdict
class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        pts = set([(x,y) for x, y in points])
        min_area = float(\"inf\")
        for x1, y1 in points:
            for x2, y2 in points:
                if x1 > x2 and y1 > y2 and (x1, y2) in pts and (x2, y1) in pts:
                    min_area = min(abs(x1-x2)*abs(y1-y2), min_area)
        return min_area if min_area != float(\"inf\") else 0
                
            
        

class Solution(object):
        def minAreaRect(self, points):
            \"\"\"
            :type points: List[List[int]]
            :rtype: int
            \"\"\"
            min_area = sys.maxsize
            points_table = set()

            for x, y in points:
                points_table.add((x,y))

            for x1, y1 in points:
                for x2, y2 in points:
                    if (x1 > x2) and (y1 > y2): 
                        # Skip looking at same point
                        if ((x1, y2) in points_table) and ((x2, y1) in points_table):
                            area = abs(x1 -  x2) * abs(y1 - y2)
                            if area:
                                min_area = min(area, min_area)

            return 0 if min_area == sys.maxsize else min_area

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        min_area = sys.maxsize
        points_table = set()
        for x, y in points:
            points_table.add((x,y))
        for x1, y1 in points:
            for x2, y2 in points:
                if x1 > x2 and y1 > y2: # Skip looking at same point
                    if (x1, y2) in points_table and (x2, y1) in points_table:
                        area = abs(x1 -  x2) * abs(y1 - y2)
                        if area:
                            min_area = min(area, min_area)
                        
        return 0 if min_area == sys.maxsize else min_area        

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        # count by diagonal, Time O(n); space O(n)
        seen = set()
        res = float('inf')
        for x1, y1 in points:
            for x2, y2 in seen:
                if (x1, y2) in seen and (x2, y1) in seen:
                    area = abs(x1 - x2) * abs(y1 - y2)
                    res = min(res, area)
            
            seen.add((x1, y1))
        
        return res if res < float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:

        min_area = float('inf')
        points_table = set()
        
        for x, y in points:
            points_table.add((x,y))
            
        for x1, y1 in points:
            for x2, y2 in points:
                if x1 > x2 and y1 > y2: 
                    if (x1, y2) in points_table and (x2, y1) in points_table:
                        area = abs(x1 -  x2) * abs(y1 - y2)
                        if area:
                            min_area = min(area, min_area)
                        
        return 0 if min_area == float('inf') else min_area

from collections import defaultdict
class Solution:
    def minAreaRect(self, points):
        setr, setc = set(), set()
        for r, c in points:
            setr.add(r)
            setc.add(c)
        if len(points) in (len(setr), len(setc)): return 0
        
        
        columns = defaultdict(list)
        for r, c in points:
            columns[r].append(c)
        ans, lastc = float('inf'), dict()
        for r in sorted(columns):
            cols = sorted(columns[r])
            for j, y2 in enumerate(cols):
                for i, y1 in enumerate(cols[:j]):
                    if (y1, y2) in lastc:
                        ans = min(ans, (r - lastc[(y1, y2)]) * (y2 - y1))
                    lastc[(y1, y2)] = r
        return ans if ans < float('inf') else 0


                              
class Solution:
    def minAreaRect(self, points):
        X = list(zip(*points))
        if len(points) in (len(set(X[0])), len(set(X[1]))): return 0
        seen, ans = set(map(tuple, points)), float('inf')
        for i, (p1x, p1y) in enumerate(points):
            for (p2x, p2y) in points[i+1:]:
                if (p1x != p2x) and (p1y != p2y) and ((p1x, p2y) in seen) and ((p2x, p1y) in seen):
                    ans = min(ans, abs((p1x-p2x)*(p1y-p2y)))
        return ans if ans < float('inf') else 0


\"\"\"
In order to form a rectangle, you need four points all positioned at 90 degrees to each other.

In this approach, we store all given points in a set.

Then iteratively go through all the points in two loops (x1, y1) and (x2, y2) while checking if (x1, y2) and (x2, y1) are also valid points. If so, we found a rectangle.

We calculate the area of this rectangle. If this area is smaller than the minimum area seen so far, make it the minimum area.
\"\"\"
class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        min_area = sys.maxsize
        points_table = set()
        
        for x, y in points:
            points_table.add((x,y))
            
        for x1, y1 in points:
            for x2, y2 in points:
                if x1 > x2 and y1 > y2: # Skip looking at same point
                    if (x1, y2) in points_table and (x2, y1) in points_table:
                        area = abs(x1 -  x2) * abs(y1 - y2)
                        if area:
                            min_area = min(area, min_area)
                        
        return 0 if min_area == sys.maxsize else min_area

class Solution(object):
    def minAreaRect(self, points: List[List[int]]) -> int:
        n = len(points)
        setx = set()
        sety = set()
        for x, y in points:
            setx.add(x)
            sety.add(y)
        nx = len(setx)
        ny = len(sety)

        if nx == n or ny == n:
            return 0
        p = collections.defaultdict(list)
        if nx > ny:
            for x, y in points:
                p[x].append(y)
        else:
            for x, y in points:
                p[y].append(x)

        res = float('inf')
        dic_last = {}
        for x in sorted(p):
            p[x].sort()
            for j, y2 in enumerate(p[x]):
                for y1 in p[x][:j]:
                    if (y1, y2) in dic_last:
                        res = min(res, (x - dic_last[y1, y2]) * (y2-y1))
                    dic_last[y1, y2] = x
        return res if res < float('inf') else 0

class Solution:
    def minAreaRect(self, points):
        min_area = float('inf')
        points_table = set()
        
        for x, y in points:
            points_table.add((x,y))
            
        for x1, y1 in points:
            for x2, y2 in points:
                if x1 > x2 and y1 > y2: # Skip looking at same point
                    if (x1, y2) in points_table and (x2, y1) in points_table:
                        area = abs(x1 -  x2) * abs(y1 - y2)
                        if area:
                            min_area = min(area, min_area)
                        
        return 0 if min_area == float('inf') else min_area


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        point_set = set((x, y) for x, y in points)
        min_rec = math.inf
        points.sort()
        for idx1 in range(len(points)):
            for idx2 in range(idx1 + 1, len(points)):
                x1, y1 = points[idx1]
                x2, y2 = points[idx2]
                # O(N^2)
                if x1 < x2 and y1 > y2:
                    if (x1, y2) in point_set and (x2, y1) in point_set:
                        min_rec = min(min_rec, (x2 - x1) * (y1 - y2)) #O(1)
        return 0 if min_rec == math.inf else min_rec

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        ans = float(\"inf\")
        point_set = set()
        for p in points:
            point_set.add((p[0],p[1]))
        for x1, y1 in point_set:
            for x2, y2 in point_set:
                if x1 > x2 and y1 > y2: # make sure no zero 
                    if (x1,y2) in point_set and (x2, y1) in point_set:
                        ans = min(abs((x1 - x2)) * abs((y1 - y2)), ans)
        return ans if ans != float(\"inf\") else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        points.sort()
        points = map(tuple, points)
        xdict, ydict = collections.defaultdict(list), collections.defaultdict(list)
        pset = set()
        res = float(\"inf\")
        for point in points:
            xdict[point[0]].append(point)
            ydict[point[1]].append(point)
            pset.add(point)
        for x1 in xdict.keys():
            if len(xdict[x1]) == 1:
                continue
            for i in range(len(xdict[x1]) - 1):
                p1 = xdict[x1][i]
                for j in range(i + 1, len(xdict[x1])):
                    p2 = xdict[x1][j]
                    for p3 in ydict[p1[1]]:
                        if p3 != p1:
                            if (p3[0], p2[1]) in pset:
                                res = min(res, abs((p3[0] - p1[0]) * (p2[1] - p1[1])))
        return res if res != float(\"inf\") else 0

\"\"\"
Inputs
    points (List[List[int]]): x y locations of points
Outputs
    int: minimum area of a rectangle
Notes
    - if no rectangle possible, output 0

Examples
    
    Example 1
        
        Input: [[1,1],[1,3],[3,1],[3,3],[2,2]]
        Output: 4
    
    Example 2

        Input: [[1,1],[1,3],[3,1],[3,3],[4,1],[4,3]]
        Output: 2

Ideas
    - distance doesn't tell us if points are parallel or not
    
    - keep track of points at common x and y locations
        const_x: {1: index 0, index 1}
        const_y: {1: index 0, index 2}
        
    - sorting points may help us
    
    - area calc
        + dx * dy
    
    - find two points at same x, and different y
        + then find another two points at different x and same y
            x1, y1
            x2, y2
            x1 = x2
        + then find one point at same y1, different x
            x3, y3
            
            y1 = y3
            
        + then find one point at same y2, different x
            
            x4, y4
            
            x3 = x4
            y2 = y4
    
    - checking for points will be easier with set or hash
        O(1) checks.
        + we need the (x,y) values to verify this

\"\"\"

from collections import defaultdict

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        \"\"\"
        
        - aggreate points by common x or y coordinate
        - 
        
        \"\"\"
        
        ans = float('inf')
        
        ''' Aggregate points by common location '''
        const_x = defaultdict(list)
        
        for point in points:
            x, y = point
            const_x[x].append(y)
        
        ''' Iterate over constant x pairs'''
        
        prev_x = {}  # store x location for pairs of points
        
        for x in sorted(const_x):
            n: int = len(const_x[x])
                
            # Sort by y value
            const_x[x].sort()
            
            for ix in range(n):
                for jx in range(ix + 1, n):
                
                    y1 = const_x[x][ix]  # (x1, y1) smaller y
                    y2 = const_x[x][jx]  # (x1, y2) larger y
                    
                    dy = y2 - y1
                    
                    # Find points at the same y and different x from current
                    # but same x as each other
                    if (y1, y2) in prev_x:
                        dx = x - prev_x[(y1, y2)]
                        ans = min(ans, dx * dy)
                    
                    # Save the x location for every pair of y points
                    prev_x[(y1, y2)] = x
                     
        
        if ans != float('inf'):
            return ans
        else:
            return 0
        

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        points.sort()
        points_set = set([tuple(point) for point in points])
        smallest = float('inf')
        for i in range(len(points) - 1):
            for j in range(i + 1, len(points)):
                x1, y1 = points[i]
                x2, y2 = points[j]
                if x1 < x2 and y1 < y2 and (x1, y2) in points_set and (x2, y1) in points_set:
                    area = (x2 - x1) * (y2 - y1)
                    smallest = min(smallest, area)
        return smallest if smallest != float('inf') else 0

from collections import defaultdict
class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        pts = set([(x,y) for x, y in points])
        min_area = float(\"inf\")
        for x1, y1 in points:
            for x2, y2 in points:
                if x1 < x2 and y1 < y2 and (x1, y2) in pts and (x2, y1) in pts:
                    min_area = min(abs(x1-x2)*abs(y1-y2), min_area)
        return min_area if min_area != float(\"inf\") else 0
                
            
        

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        seen = set()
        for point in points:
            seen.add((point[0], point[1]))
        min_area = float('inf')
        for x1, y1 in points:
            for x2, y2 in points:
                if x1 > x2 and y1 > y2:
                    if (x1, y2) in seen and (x2, y1) in seen:
                        area = abs(y2 - y1) * abs(x2 - x1)
                        min_area = min(min_area, area)
        return 0 if min_area == float('inf') else min_area

import math

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        points = set(map(tuple, points))
        
        min_area = math.inf
        
        for start_x, start_y in points:
            for diag_x, diag_y in points:
                if diag_y < start_y and diag_x > start_x and (diag_x, start_y) in points and (start_x, diag_y) in points:
                    min_area = min(min_area, (diag_x - start_x) * (start_y - diag_y))
                    
        if min_area == math.inf:
            return 0
        else:
            return min_area
                    
        


'''
1. When we see new x point, we establish base point x as baseX.
2. For every baseX, there will be multiple y points (y_for_baseX array)
3. If y was registered in previous base(for base in y_for_baseX), we update res.
  - In other words, we update res if (y, base) seen before at another x.
4. We also update x value of seen[base][y]
5. After processing, we append y to bases array.
6. Return res
'''

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        \"\"\"
        :type points: List[List[int]]
        :rtype: int
        \"\"\"
        min_area = float('inf')
        points_table = set()
        
        for x, y in points:
            points_table.add((x,y))
            
        for x1, y1 in points:
            for x2, y2 in points:
                if x1 > x2 and y1 > y2: # skip dupes
                    if (x1, y2) in points_table and (x2, y1) in points_table:
                        area = abs(x1 - x2) * abs(y1 - y2)
                        if area: min_area = min(area, min_area)
                        
        return 0 if min_area == float('inf') else min_area

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        
        n = len(points)
        nx = len(set(x for x, y in points))
        ny = len(set(y for x, y in points))
        if nx == n or ny == n:
            return 0

        p = collections.defaultdict(list)   # this is my 1st thought, should be a dictionary of lists. 
    # Here is the right way to define the dictionary.
    # This is one of the KEY step shorten the time. If define in the opposite way, short dictionary but long lists, ~5 fold slower.
        if nx > ny:
            for x, y in points:
                p[x].append(y)
        else:
            for x, y in points:
                p[y].append(x)  # if using this define, long dictionary, each list is short.

        lastx = {}
        res = float('inf')

        for x in sorted(p):
            p[x].sort() # the dictionary p and the list in p must be sorted here, because it was unknown x or y should be the keys for the dictionary. 
            for i in range(len(p[x])):
                for j in range(i):
                    y1, y2 = p[x][j], p[x][i] # fix one dimension, find possible pairs for the other dimention.
                    #Record the fixed dimension as value, the pair as the key.
                    if (y1, y2) in lastx:  
                        res = min(res, (x - lastx[y1, y2]) * abs(y2 - y1)) # if the pari appeared before, calculate an area, compare with the current min.
                    lastx[y1, y2] = x # no matter the pair appeared before or not, record the new one. 
                    # This is because the dictionary is sorted, the last must be the lowest.
        return res if res < float('inf') else 0  # simple version.


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        lookup = set()
        for x, y in points:
            lookup.add((x, y))
        
        res = float(\"inf\")
        for x1, y1 in points:              ##在points里面遍历！！定下两个点就可以确定一个矩形,
            for x2, y2 in points:
                if x1 > x2 and y1 > y2:  ##去掉之前看过的元素！！
                    if (x1, y2) in lookup and (x2, y1) in lookup:
                  
                        res = min(res, abs(x1 - x2) * abs(y1 - y2))
        
        if res < float(\"inf\"):
            return res
        else:
            return 0
            

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        ans = float(\"inf\")
        point_set = set()
        for p in points:
            point_set.add((p[0],p[1]))
        for x1, y1 in point_set:
            for x2, y2 in point_set:
                if x1 > x2 and y1 > y2:
                    if (x1,y2) in point_set and (x2, y1) in point_set:
                        ans = min(abs((x1 - x2)) * abs((y1 - y2)), ans)
        return ans if ans !=  float(\"inf\") else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        point_table = set()
        for x,y in points:
            point_table.add((x,y))
        minarea = float('inf')
        for x1, y1 in points:
            for x2, y2 in points:
                if x1>x2 and y1>y2:
                    if (x1,y2) in point_table and (x2,y1) in point_table:
                        area = abs(x1-x2)*abs(y1-y2)
                        if area:
                            area = min(area, minarea)
                            minarea = area
        return minarea if minarea<float('inf') else 0


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        min_area = sys.maxsize
        points_table = set()
        
        for x, y in points:
            points_table.add((x,y))
            
        print(points)
            
        for x1, y1 in points:
            for x2, y2 in points:
                if x1 > x2 and y1 > y2: # Skip looking at same point
                    if (x1, y2) in points_table and (x2, y1) in points_table:
                        area = abs(x1 -  x2) * abs(y1 - y2)
                        if area:
                            min_area = min(area, min_area)
                        
        return 0 if min_area == sys.maxsize else min_area

# +------------------------------+
# |                              |
# |                              |
# |                              |
# |    +-----------------+       |
# |    |                 |       |
# | +---------+          |       |
# | |  |      |          |       |
# | |  +-----------------+       |
# | +---------+                  |
# |           (x1, y2)           |   (x2, y2)
# |              +-------------------+
# |              |               |   |
# |              |               |   |
# +------------------------------+   |
#                |                   |
#                |                   |
#                +-------------------+
#             (x1, y1)               (x2, y1)
            

from collections import defaultdict


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        
        # stores all points into a set
        points_set = set()
        
        for x, y in points:
            points_set.add((x, y))
        
        min_area = float('inf')
        
        # iterate through all the points nested way
        # for each point, compare with all other points
        for x1, y1 in points:
            for x2, y2 in points:
                # skip looking at the same point
                if x2 > x1 and y2 > y1:
                    # looking for two other points for form a rec
                    # see diag above
                    if (x2, y1) in points_set and (x1, y2) in points_set:
                        area = abs(x1 - x2) * abs(y1 - y2)
                        min_area = min(min_area, area)
                    
                        
        return min_area if min_area != float('inf') else 0


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        min_area = sys.maxsize
        points_table = set()

        for x, y in points:
            points_table.add((x,y))

        for x1, y1, in points:
            for x2, y2 in points:
                if x1 > x2 and y1 > y2: # Skip looking at same point
                    if (x1, y2) in points_table and (x2, y1) in points_table:
                            area = abs(x1 -  x2) * abs(y1 - y2)
                            if area:
                                min_area = min(area, min_area)

        return 0 if min_area == sys.maxsize else min_area

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        ys = {}
        for x, y in points:
            if y not in ys:
                ys[y] = set()
            ys[y].add(x)                
        result = -1            
        for y1 in ys:
            for y2 in ys:
                if y1 == y2:
                    continue
                points = ys[y1].intersection(ys[y2])                      
                if len(points) < 2:
                    continue
                points = list(points)                    
                points.sort()
                x1 = points[0]
                for i in range(1, len(points)):
                    x2 = points[i]
                    area = (x2-x1) * abs(y2 - y1)
                    if result < 0:
                        result = area
                    else:
                        result = min(area, result)
                    x1 = x2                        
                        
        return result if result >= 0 else 0                                


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        n = len(points)
        min_area = math.inf
        seen = set()
        
        for i, (x0, y0) in enumerate(points):
            for j in range(i):
                x1, y1 = points[j]
                if (x0, y1) in seen and (x1, y0) in seen:
                    area = abs(x0 - x1) * abs(y0 - y1)
                    if area > 0:
                        min_area = min(min_area, area)
            seen.add((x0, y0))
        
        return 0 if min_area == math.inf else min_area

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        #Parallel with x, y -> find all point on same x, and same y that can form parallel edge
        
        def calculateArea(x1,x2,y1,y2):
            return abs(y2-y1) * abs(x2-x1)
        if not points or len(points) < 4: return 0
        
        visited = set()
        area = float('inf')
        for x1,y1 in points:
            for x2,y2 in visited:
                if (x1,y2) in visited and (x2,y1) in visited:
                    area = min(area, calculateArea(x1,x2,y1,y2))
            
            visited.add((x1,y1))
        
        return area if area != float('inf') else 0
        #Skip those that have less than len 2 (cant form a line)
        
        #  (1,5)        (6,5)       (x1,y2)         (x2,y2)
        #                       or 
        #  (1,2)        (6,2)       (x1,y1)         (x2,y1)

        # Rectangle would be
        


from collections import defaultdict
from math import inf


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        byrow = defaultdict(set) # row: {col,}
        bycol = defaultdict(list) # col: [row,]
        for x, y in points:
            byrow[y].add(x)
            bycol[x].append(y)
            
        area = inf
        for x1, ys in list(bycol.items()):
            if len(ys) < 2:
                continue
            for j, y1 in enumerate(ys):
                for y2 in ys[j+1:]:
                    lens = [abs(x1-x2) for x2 in byrow[y1] & byrow[y2] - {x1}]
                    if lens:
                        area = min(area, min(lens)*abs(y1-y2))
        return area if area < inf else 0


from collections import defaultdict
class Solution:
    def minAreaRect(self, points):
        X = list(zip(*points))
        if len(set(X[0])) == 1 or len(set(X[1])) == 1: return 0
        
        points.sort()
        columns = defaultdict(list)
        for r, c in points:
            columns[r].append(c)
        ans, lastc = float('inf'), dict()
        for r, cols in columns.items():
            for i, c1 in enumerate(cols):
                for c2 in cols[i+1:]:
                    if (c1, c2) in lastc:
                        area = (r - lastc[(c1, c2)]) * (c2 - c1)
                        #ans = min(ans, area)
                        if area < ans: ans = area
                    lastc[(c1, c2)] = r
        return ans if ans < float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        pointsets = set()
        for point in points:
            pointsets.add((point[0], point[1]))
        ans = float(\"inf\")
        # consider two points at diagonal
        # (x1, y1) at lower left, (x2, y2) at upper right
        # since sides are parallel to x and y axes
        # then we should also find (x1, y2) and (x2, y1)
        # if found, we can form a rectangle
        for x1, y1 in points:
            for x2, y2 in points:
                if x2>x1 and y2>y1:
                    if (x2, y1) in pointsets and (x1, y2) in pointsets:
                        area = abs(x1-x2)*abs(y1-y2)
                        ans = min(area, ans)
        return ans if ans < float(\"inf\") else 0

class Solution(object):
    def minAreaRect(self, points):
        \"\"\"
        :type points: List[List[int]]
        :rtype: int
        \"\"\"
        min_area = sys.maxsize
        points_table = set()

        for x, y in points:
            points_table.add((x, y))

        for x1, y1 in points:
            for x2, y2 in points:
                if x1 > x2 and y1 > y2:  # Skip looking at same point
                    if (x1, y2) in points_table and (x2, y1) in points_table:
                        area = abs(x1 - x2) * abs(y1 - y2)
                        if area:
                            min_area = min(area, min_area)

        return 0 if min_area == sys.maxsize else min_area


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        min_area = float('inf')
        points_table = set()
        
        for x, y in points:
            points_table.add((x,y))
            
        for x1, y1 in points:
            for x2, y2 in points:
                if x1 > x2 and y1 > y2:
                    if (x1, y2) in points_table and (x2, y1) in points_table:
                        area = abs(x1 -  x2) * abs(y1 - y2)
                        if area: 
                            min_area = min(area, min_area)     
        return 0 if min_area == float('inf') else min_area

from collections import defaultdict
class Solution:
    def minAreaRect(self, points):
        setr, setc = set(), set()
        for r, c in points:
            setr.add(r)
            setc.add(c)
        if len(points) in (len(setr), len(setc)): return 0
        
        
        columns = defaultdict(list)
        for r, c in points:
            columns[r].append(c)
        ans, lastc = float('inf'), dict()
        for r in sorted(columns):
            cols = sorted(columns[r])
            for j, y2 in enumerate(cols):
                for i, y1 in enumerate(cols[:j]):
                    if (y1, y2) in lastc:
                        ans = min(ans, (r - lastc[(y1, y2)]) * (y2 - y1))
                    lastc[(y1, y2)] = r
        return ans if ans < float('inf') else 0


                              
class Solution:
    def minAreaRect(self, points):
        seen, ans = set(map(tuple, points)), float('inf')
        for i, (p1x, p1y) in enumerate(points):
            for (p2x, p2y) in points[i+1:]:
                if (p1x != p2x) and (p1y != p2y) and ((p1x, p2y) in seen) and ((p2x, p1y) in seen):
                    ans = min(ans, abs((p1x-p2x)*(p1y-p2y)))
        return ans if ans < float('inf') else 0


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        min_area = float(\"inf\")
        points_table = set()
        for p in points:
            points_table.add(tuple(p))
        
        for x1, y1 in points:
            for x2, y2 in points:
                if x2 > x1 and y2 > y1:
                    if (x1, y2) in points_table and (x2, y1) in points_table:
                        area = abs(x1 - x2) * abs(y1 - y2)
                        if area:
                            min_area = min(min_area, area)
        return 0 if min_area == float(\"inf\") else min_area

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        saw = set()
        res = float('inf')
        for x, y in points:
            for dx, dy in saw:
                p1 = (dx, y)
                p2 = (x, dy)
                if p1 in saw and p2 in saw and dx != x and dy != y:
                    res = min(res, abs(dx - x) * abs(dy - y))
            saw.add((x, y))
        return res if res != float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        if len(points) < 4:
            return 0
        # watch for degenerate cases of 4 same points or getting a line
        # if points weren't distinct, can convert to set and then back to list
        # for every x, need list of y's (set)
        # for every y, need list of x's (set)
        # then, O(4) to find a rectangle
        # x_vals: {1: [1, 3], 3: [3, 1], 2: [2]}
        # y_vals: {1, [1, 3], 3: [3, 1], 2: [2]}
        
        # Then, to check, loop through all x's and see if we can get all 4 points for each; if so, calculate area
        
        # Runtime: O(n^3)
        # x_vals = defaultdict(set)
        # y_vals = defaultdict(set)
        # for p in points:
        #     x_vals[p[0]].add(p[1])
        #     y_vals[p[1]].add(p[0])
        # # print(x_vals)
        # # print(y_vals)
        # min_area = 40000*40000 + 1
        # for x, y_coords in x_vals.items():
        #     if len(y_coords) == 1:
        #         continue # can't make a rectangle if it's the only point on that x = x line
        #     y_list = list(y_coords)
        #     for i, y_1 in enumerate(y_list):
        #         for j, y_2 in enumerate(y_list[i+1:]):
        #             # we have points (x, y_1) and (x, y_2)
        #             # now need to check if same value in both y_vals[y_1] and y_vals[y_2]
        #             # if so, we have a rectangle!
        #             common_x_2 = y_vals[y_1].intersection(y_vals[y_2])
        #             common_x_2.remove(x) # avoid degenerate case of a line
        #             for x_2 in list(common_x_2):
        #                 # print(x, x_2, y_1, y_2)
        #                 min_area = min(min_area, abs(x_2 - x) * abs(y_2 - y_1))
        # if min_area == 40000*40000 + 1:
        #     return 0
        # return min_area
    
        # Improved O(n^2) solution that's a lot simpler too:
        min_area = 40000*40000 + 1
        seen_points = set()
        for x,y in points:
            seen_points.add((x, y))
        # print(seen_points)
        for p1_x, p1_y in points:
            for p2_x, p2_y in points:
                if p1_x > p2_x and p1_y > p2_y:
                    if (p1_x, p2_y) in seen_points and (p2_x, p1_y) in seen_points:
                        area = abs(p2_x - p1_x) * abs(p2_y - p1_y)
                        if area > 0:
                            min_area = min(min_area, area)
        if min_area == 40000*40000 + 1:
            return 0
        return min_area
            


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        table = {}
        mark = []
        best = 0
        for i in points:
            if i[0] not in table:
                table[i[0]] = [[i[1]],[]]
            else:
                if i[1] not in table[i[0]][0]:
                    for j in table[i[0]][0]:
                        if j < i[1]:
                            table[i[0]][1].append((j, i[1]))
                        else:
                            table[i[0]][1].append((i[1],j))
                    table[i[0]][0].append(i[1])
                    if len(table[i[0]][0]) == 2:
                        mark.append(i[0])
        g = {}
        print(mark)
        for i in mark:
            p = table[i]
            for j in p[1]:
                if j in g:
                    for k in g[j]:
                        temp = abs((k - i) * (j[1] - j[0]))
                        if best == 0 or temp < best:
                            best = temp
                    g[j].append(i)
                else:
                    g[j] = [i]
        return best

class Solution:
    def minAreaRect(self, points):
        pSet, ans = set(map(tuple, points)), float('inf')
        for i, (p1x, p1y) in enumerate(points):
            for (p2x, p2y) in points[i+1:]:
                if (p1x != p2x) and (p1y != p2y) and ((p1x, p2y) in pSet) and ((p2x, p1y) in pSet):
                    ans = min(ans, abs((p1x - p2x) * (p1y - p2y)))
        return ans if ans < float('inf') else 0


   #     if Nr < Nc:
   #         points = [[c, r] for (r, c) in points]
   #         return self.minAreaRect(points)

from collections import defaultdict
class Solution:
    def minAreaRect(self, points):
        X = list(zip(*points))
        Nr, Nc = len(set(X[0])), len(set(X[1]))
        if Nr == 1 or Nc == 1: return 0

        points.sort()
        columns = defaultdict(list)
        for r, c in points:
            columns[r].append(c)
        ans, lastc = float('inf'), dict()
        for r, cols in columns.items():
            for i, c1 in enumerate(cols):
                for c2 in cols[i+1:]:
                    if (c1, c2) in lastc:
                        area = (r - lastc[(c1, c2)]) * (c2 - c1)
                        #ans = min(ans, area)
                        if area < ans: ans = area
                    lastc[(c1, c2)] = r
        return ans if ans < float('inf') else 0

from collections import defaultdict
class Solution:
    def minAreaRect(self, points):
        setr, setc = set(), set()
        for r, c in points:
            setr.add(r)
            setc.add(c)
        if len(points) in (len(setr), len(setc)): return 0
        
        
        columns = defaultdict(list)
        for r, c in points:
            columns[r].append(c)
        ans, lastc = float('inf'), dict()
        for r in sorted(columns):
            cols = sorted(columns[r])
            for j, y2 in enumerate(cols):
                for i, y1 in enumerate(cols[:j]):
                    if (y1, y2) in lastc:
                        ans = min(ans, (r - lastc[(y1, y2)]) * (y2 - y1))
                    lastc[(y1, y2)] = r
        return ans if ans < float('inf') else 0


                              
class Solution:
    def minAreaRect(self, points):
        X = list(zip(*points))
        if len(points) in (len(set(X[0])), len(set(X[1]))): return 0
        seen, ans = set(map(tuple, points)), float('inf')
        points.sort()
        for i, (p1x, p1y) in enumerate(points):
            for (p2x, p2y) in points[i+1:]:
                area = abs((p2x-p1x)*(p2y-p1y))
                if area > ans or area == 0:
                    continue
                if (p1x, p2y) in seen and (p2x, p1y) in seen:
                    ans = area
        return ans if ans < float('inf') else 0
    
class Solution:
    def minAreaRect(self, points):
        seen, ans = set(map(tuple, points)), float('inf')
        for i, (p1x, p1y) in enumerate(points):
            for (p2x, p2y) in points[i+1:]:
                if (p1x != p2x) and (p1y != p2y) and ((p1x, p2y) in seen) and ((p2x, p1y) in seen):
                    ans = min(ans, abs((p1x - p2x) * (p1y - p2y)))
        return ans if ans < float('inf') else 0


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        min_area = float('inf')
        points_set = set()
        for x, y in points:
            points_set.add((x,y))
        for (x1, y1) in points_set:
            for (x2, y2) in points_set:
                if x1 > x2 and y1 > y2 and (x1, y2) in points_set and (x2, y1) in points_set:
                        
                        area = abs(x1 - x2) * abs(y1 - y2)
                        min_area = min(min_area, area)
        return min_area if min_area < float('inf') else 0
                        


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        s = set(tuple(p) for p in points)
        
        area = math.inf
        for i, (x1, y1) in enumerate(points):
            for x2, y2 in points[i:]:
                if x1==x2 or y1==y2:
                    continue
                    
                if (x2, y1) not in s or (x1, y2) not in s:
                    continue
                    
                area = min(area, abs((x2-x1)*(y2-y1)))
                
        return area if area != math.inf else 0

# +------------------------------+
# |                              |
# |                              |
# |                              |
# |    +-----------------+       |
# |    |                 |       |
# | +---------+          |       |
# | |  |      |          |       |
# | |  +-----------------+       |
# | +---------+                  |
# |           (x1, y2)           |   (x2, y2)
# |              +-------------------+
# |              |               |   |
# |              |               |   |
# +------------------------------+   |
#                |                   |
#                |                   |
#                +-------------------+
#             (x1, y1)               (x2, y1)
            

from collections import defaultdict


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        
        # stores points seen before into a set
        points_set = set()
        
        for x, y in points:
            points_set.add((x, y))
        
        min_area = float('inf')
        
        # iterate through all the points nested way
        for x1, y1 in points:
            for x2, y2 in points:
                if x2 > x1 and y2 > y1:
                    if (x2, y1) in points_set and (x1, y2) in points_set:
                        area = abs(x1 - x2) * abs(y1 - y2)
                        min_area = min(min_area, area)
                    
                        
        return min_area if min_area != float('inf') else 0


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        dx = defaultdict(list)
        for x, y in points:
            dx[x].append(y)
        lastx = defaultdict(list)
        ans = float('inf')
        
        for x in sorted(dx):
            col = dx[x]
            col.sort()
            for i in range(len(col)-1):
                for j in range(i+1, len(col)):
                    y1 = col[i]
                    y2 = col[j]
                    if (y1,y2) in lastx:
                        for xs in lastx[y1,y2]:
                            ans = min(ans, (abs(y2-y1))*(abs(x-xs)))
                    lastx[y1,y2].append(x)
        return ans if ans<float('inf') else 0
            
            
        


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        \"\"\"
        Time complexity: O(n^2)
        Space complexity: O(n)
        \"\"\"
        # idea, use lookup set to store the point that has been seen before. for (x1,y1) in points, for (x2,y2) in lookup, check whether (x1,y2) and (x2, y1) in lookup. 
        
        min_area = float('inf') 
        lookup = set()
        
        
        for x1, y1 in points:
            for x2, y2 in lookup:
                if (x1, y2) in lookup and (x2, y1) in lookup:
                    min_area = min(min_area, abs(x1-x2) * abs(y1-y2)) 
                    
            # add to lookup for the outer loop
            lookup.add((x1, y1))
            
            
        if min_area != float('inf'):
            return min_area
        else:
            return 0

class Solution:
    def minAreaRect(self, points):
        pSet, ans = set(map(tuple, points)), float('inf')
        for i, (p1x, p1y) in enumerate(points):
            for (p2x, p2y) in points[i+1:]:
                if (p1x != p2x) and (p1y != p2y) and ((p1x, p2y) in pSet) and ((p2x, p1y) in pSet):
                    ans = min(ans, abs((p1x - p2x) * (p1y - p2y)))
        return ans if ans < float('inf') else 0
    


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        '''
        columns = defaultdict(list)
        
        for x, y in points:
            columns[x].append(y)
        
        result = float('inf')
        lastx = {}
        
        for x in sorted(columns):
            column_list = columns[x]
            column_list.sort()
            
            for j, y2 in enumerate(column_list):
                for i in range(j):
                    y1 = column_list[i]
                    
                    if (y1, y2) in lastx:
                        result = min(result, (x-lastx[y1,y2]) * (y2-y1))
                    lastx[y1,y2] = x
        
        return result if result < float('inf') else 0
        '''
        
        seen = set()
        ans = sys.maxsize
        
        for x1, y1 in points:
            for x2, y2 in seen:
                if (x1, y2) in seen and (x2, y1) in seen:
                    area = abs(x1-x2) * abs(y1-y2)
                    ans = min(ans, area)
            
            seen.add((x1,y1))
        
        return ans if ans < sys.maxsize else 0
                    


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        counter = Counter(list(map(tuple, points)))
        points = list(counter)
        result = float('inf')
        for i, (x, y) in enumerate(points):
            for j in range(i + 1, len(points)):
                x_, y_ = points[j]
                if x != x_ and y != y_:
                    area = abs((x_ - x) * (y_ - y))
                    if area < result and (x, y_) in counter and (x_, y) in counter:
                        if not area:
                            return 0
                        result = area
                elif counter[(x, y)] == 2 and counter[(x_, y_)] == 2:
                        return 0
        return 0 if result == float('inf') else result


from collections import defaultdict
class Solution:
    def minAreaRect(self, points):
        X = list(zip(*points))
        if len(set(X[0])) == 1 or len(set(X[1])) == 1: return 0

        points.sort()
        columns = defaultdict(list)
        for r, c in points:
            columns[r].append(c)
        ans, lastc = float('inf'), dict()
        for r, cols in list(columns.items()):
            for i, c1 in enumerate(cols):
                for c2 in cols[i+1:]:
                    if (c1, c2) in lastc:
                        area = (r - lastc[(c1, c2)]) * (c2 - c1)
                        #ans = min(ans, area)
                        if area < ans: ans = area
                    lastc[(c1, c2)] = r
        return ans if ans < float('inf') else 0

# from collections import defaultdict
# class Solution:
#     def minAreaRect(self, points):
#         d = defaultdict(set)
#         rset, cset, N, ans = set(), set(), len(points), float('inf')
#         for r, c in points:
#             rset.add(r)
#             cset.add(c)
#         Nr, Nc = len(rset), len(cset)
#         if Nr == N or Nc == N:
#             return 0
#         elif Nr < Nc:
#             for r, c in points:
#                 d[r].add(c)
#         else:
#             for r, c in points:
#                 d[c].add(r)
# 
#         A = sorted(d.keys())
#         for i, r1 in enumerate(A):
#             cols1 = d[r1]
#             for r2 in A[i+1:]:
#                 cols2 = d[r2]
#                 s = sorted(cols1 & cols2)
#                 for c1, c2 in zip(s[:-1], s[1:]):
#                     area = abs((r1 - r2) * (c1 - c2))
#                     ans = min(ans, area)
#         return ans if ans < float('inf') else 0


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        point_set = set(map(tuple,points))
        min_area = float('inf')
        for i, (x1, y1) in enumerate(points):
            for x2, y2 in points[:i]:
                if x1 != x2 and y1 != y2:
                    if (x1, y2) in point_set and (x2, y1) in point_set:
                        min_area = min(min_area, abs((x2 - x1) * (y2 - y1)))
        return min_area if min_area != float('inf') else 0


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        appeared = set()
        res = math.inf
        for x1, y1 in points:
            for x2, y2 in appeared:
                if (x1, y2) in appeared and (x2, y1) in appeared:
                    area = abs(x1-x2) * abs(y1-y2)
                    if area and area < res:
                        res = area
            appeared.add((x1, y1))
        return 0 if res == math.inf else res

class Solution:
    
    # O(n^2), O(n)
    def minAreaRect(self, points: List[List[int]]) -> int:
        # O(nlogn), O(1)
        points.sort()
        
        cols = defaultdict(list)
        for p in points:
            cols[p[0]].append(p[1])
        
        last_x = {}
        ans = float('inf')
        for x in sorted(cols):
            for i in range(len(cols[x])):
                for j in range(i + 1, len(cols[x])):
                    if (cols[x][i], cols[x][j]) in last_x:
                        ans = min(ans, (cols[x][j] - cols[x][i]) * (x - last_x[(cols[x][i], cols[x][j])]))
                    last_x[(cols[x][i], cols[x][j])] = x
                    
        return ans if ans < float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        if not points:
            return 0
        
        xMap = defaultdict(set)
        yMap = defaultdict(int)
        
        for x,y in points:
            yMap[y] += 1
            xMap[x].add(y)
        
        
        
        xUnique = list(xMap.keys())
        xUnique.sort()
        
        res = float(\"inf\")
        for i in range(len(xUnique)):
            for j in range(i+1, len(xUnique)):
                x1 = xUnique[i]
                x2 = xUnique[j]
                
                values = xMap[x1].intersection(xMap[x2])
                
                values = list(sorted(values))
            
                minDiff = float(\"inf\")
                for k in range(1, len(values)):
                    minDiff = min(minDiff, values[k] - values[k-1])
                
                if minDiff == float(\"inf\"):
                    continue
                
                res = min(res, (x2 - x1) * minDiff)
        
        if res == float(\"inf\"):
            return 0
        
        return res
                
                
        
        
        

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        n = len(points)
        nx, ny = len(set(x for x, _ in points)), len(set(y for _, y in points))
        
        if nx == n or ny == n:
            return 0
        p = collections.defaultdict(list)
        if nx > ny:
            for x, y in points:
                p[x].append(y)
        else:
            for x, y in points:
                p[y].append(x)
        
        res = float('inf')
        dic_last = {}
        for x1, x2 in itertools.combinations(p, 2):
            if (len(p[x1]) < 2 or len(p[x2]) < 2):
                continue
            l1, l2 = sorted(p[x1]), sorted(p[x2])
            i = j = 0
            y = []
            while i < len(l1) and j < len(l2):
                v = max(l1[i], l2[j])
                if l1[i] < v:
                    i += 1
                elif l2[j] < v:
                    j += 1
                else:
                    y += v,
                    i, j = i + 1, j + 1
            
            for y1, y2 in itertools.combinations( y , 2 ):
                res = min(res, abs(x1-x2) * (y2 - y1))
        # for x in sorted(p):
        #     # print(x)
        #     p[x].sort()
        #     for y1, y2 in itertools.combinations(p[x], 2):
        #         if (y1, y2) in dic_last:
        #             res = min(res, abs( x - dic_last[y1, y2]) * abs(y1 -y2))
        #             # if res == 2:
        #             #     print(x, dic_last[y1, y2],  y1, y2)
        #         dic_last[y1, y2] = x
        return res if res < float('inf') else 0
        
        # n = len(points)
        # nx, ny = len(set(x for x, y in points)), len(set(y for x, y in points))
        # if nx == n or ny == n:
        #     return 0
        
        # p = collections.defaultdict(list)
        # if nx > ny:
        #     for x, y in points:
        #         p[x].append(y)
        # else:
        #     for x, y in points:
        #         p[y].append(x)
        # res = float('inf')
        # dic = {}
        # for x in sorted(p):
        #     p[x].sort()
        #     for y1, y2 in itertools.combinations(p[x], 2):
        #         if (y1, y2) in dic:
        #             res = min(res, abs(x - dic[y1, y2]) * abs(y1 - y2) )
        #         dic[y1, y2] = x
        # return res if res < float('inf') else 0
#         dic_x = {}
        
#         n = len(points)
#         nx, ny = len( set(x for x, y in points) ), len( set(y for x, y in points) )
#         if nx == n or ny == n:
#             return 0
        
#         p = collections.defaultdict(list)
#         if nx > ny:
#             for x, y in points:
#                 p[x].append(y)
#         else:
#             for x, y in points:
#                 p[y].append(x)
#         res = float('inf')
#         for x in sorted(p):
#             p[x].sort()
            
#             for y1, y2 in itertools.combinations(p[x], 2):
#                 # y1, y2 = p[x][i], p[x][j]
#                 if (y1, y2) in dic_x:
#                     res = min(res, abs(x - dic_x[y1, y2]) * abs(y1 -y2))
#                 dic_x[y1, y2] = x
#         return res if res < float('inf') else 0
#         seen = set()
        
#         res = float('inf')
#         for x1, y1 in points:
#             for x2, y2 in seen:
#                 if (x1, y2) in seen and (x2, y1) in seen:
#                     area = abs(x1 - x2) * abs(y1 - y2)
#                     if area and res > area:
#                         res = area
#                     # res = min(res, )
#             seen.add((x1, y1))
#         return res if res < float('inf') else 0


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        length = len(points)
        if length <= 3:
            return 0
        
        sets = set([])
        for point in points:
            sets.add(tuple(point))
        
        min_area = float('inf')
        for i in range(length - 1):
            x1, y1 = points[i]
            for j in range(i + 1, length):              
                x2, y2 = points[j]
                #print(x1, y1, x2, y2)
                if x1 == x2 or y1 == y2:
                    continue
                #print((x1, y2) in sets and (x2, y1) in sets)   
                if (x1, y2) in sets and (x2, y1) in sets:
                    area = abs(x1 - x2) * abs(y1 - y2)
                    #print(area)
                    if area < min_area:
                        min_area = area
                        
        if min_area == float('inf'):
            return 0
        return min_area
        
                    
                
                
        


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        seen=set()
        res=float('inf')
        for x1,y1 in points:
            for x2,y2 in seen:
                if (x1,y2) in seen and (x2,y1) in seen:
                    area=abs(x1-x2)*abs(y1-y2)
                    if area and area <res:
                        res=area
            seen.add((x1,y1))
        return res if res<float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        dictY=collections.defaultdict(list)
        for i, (x, y) in enumerate(points):
            dictY[y].append(i)
        
        y2Xpair=collections.defaultdict(set)
        for y in dictY.keys():
            if len(dictY[y])<=1:
                continue
                
            for p1, p2 in itertools.combinations(dictY[y], 2):
                x1, _, x2, _ = points[p1] + points[p2]
                y2Xpair[y].add((min(x1,x2), max(x1,x2)))  
            
        res=float('inf')
        for (y1,p1), (y2, p2) in itertools.combinations(y2Xpair.items(), 2):
            h=abs(y1-y2)
            for x1, x2 in p1&p2:
                res=min(res, (x2-x1)*h)
        
             
        return 0 if res==float('inf') else res

from math import inf
class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        if len(points) == 0:
            return 0
        
        N, min_area = len(points), inf
        d = set()
        for i in range(N):
            d.add(tuple(points[i]))
        #print(d)
        for i in range(N):
            for j in range(i+1, N, 1):
                x1, y1 = points[i]
                x2, y2 = points[j]
                if (x1 > x2 and y1 > y2) or (x2 > x1 and y2 > y1):
                    if (x1, y2) in d and (x2, y1) in d:
                        min_area = min(min_area, (x2 - x1) * (y2 - y1))
        
        if min_area == inf:
            return 0
        else:
            return min_area

class Solution:
    def minAreaRect(self, points):
        pSet, ans = set(map(tuple, points)), float('inf')
        for i, (p1x, p1y) in enumerate(points):
            for (p2x, p2y) in points[i+1:]:
                if (p1x != p2x) and (p1y != p2y) and ((p1x, p2y) in pSet) and ((p2x, p1y) in pSet):
                    ans = min(ans, abs((p1x - p2x) * (p1y - p2y)))
        return ans if ans < float('inf') else 0
    
class Solution:
    def minAreaRect(self, points):
        X = list(zip(*points))
        if len(set(X[0])) == 1 or len(set(X[1])) == 1: return 0

        pSet, ans = set(map(tuple, points)), float('inf')
        for i, (p1x, p1y) in enumerate(points):
            for (p2x, p2y) in points[i+1:]:
                area = abs((p2x-p1x)*(p2y-p1y))
                if area > ans or area == 0:
                    continue
                if (p1x, p2y) in pSet and (p2x, p1y) in pSet:
                    ans = area
        return ans if ans < float('inf') else 0
    
from collections import defaultdict
class Solution:
    def minAreaRect(self, points):
        X = list(zip(*points))
        Nr, Nc = len(set(X[0])), len(set(X[1]))
        if Nr == 1 or Nc == 1: return 0

        points.sort()
        columns = defaultdict(list)
        for r, c in points:
            columns[r].append(c)
        ans, lastc = float('inf'), dict()
        for r, cols in list(columns.items()):
            for i, c1 in enumerate(cols):
                for c2 in cols[i+1:]:
                    if (c1, c2) in lastc:
                        area = (r - lastc[(c1, c2)]) * (c2 - c1)
                        #ans = min(ans, area)
                        if area < ans: ans = area
                    lastc[(c1, c2)] = r
        return ans if ans < float('inf') else 0
    
    


from itertools import product


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        
        # create the row and col dicts
        if not points or not points[0]:
            return 0
        
        m, n = len(points), len(points[0])
        
        d_row, d_col = {}, {}
        
        for r, c in points:
            d_row[r] = d_row.get(r, []) + [c]
            d_col[c] = d_col.get(c, []) + [r]
            
        # while iterating through the list, check if there are:
        # 1. if there's another point in the same col
        # 2. if there's another point in the same row
        # 3. if there's point that can make a rectangle with points from 2, 3
        # only consider points to the down/right
        
        for r in d_row:
            d_row[r] = sorted(d_row[r])
            
        for c in d_col:
            d_col[c] = sorted(d_col[c])
        
        area = float('inf')
        for r, c, in points:
            c2s = [x for x in d_row[r] if x > c]
            r2s = [x for x in d_col[c] if x > r]
            if not c2s or not r2s:
                continue
                
            for r2, c2 in product(r2s, c2s):
                if c2 in d_row[r2]:
                    area = min(area, (r2 - r) * (c2 - c))
                    
        return area if area < float('inf') else 0


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        from collections import defaultdict
        
        x_dic = defaultdict(list)
        y_dic = defaultdict(list)
        
        area = float('inf')
        
        for x, y in points:
            x_dic[x].append(y)
            y_dic[y].append(x)
            
            for _y in x_dic[x]:
                if _y != y and _y in y_dic:
                    for _x in y_dic[_y]:
                        if _x != x and _x in x_dic and y in x_dic[_x]:
                            area = min(area, abs(x - _x) * abs(y - _y))
        
        return area if area < float('inf') else 0
        


from collections import defaultdict
class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
      
        
#   Assume one point(x1,y1) as bottom left and other (x2,y2) as top right point of a diagnol now search for   
        points=set([(i[0],i[1]) for i in points ])
        
        mini=float(\"INF\")
        # points.sort(key=lambda x:x[0])
        for point1 in points:
            for point2 in points: 
                x1,y1=point1
                x2,y2=point2
                
                if (x2>x1) and (y2>y1) and (x1,y2) in points and (x2,y1) in points:
                    # print(x1,y1,x2,y2)
                    mini=min(mini,(y2-y1)*(x2-x1)) 
            
        return mini if not mini==float(\"INF\") else 0
                
        
        

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        x_memo = collections.defaultdict(set)
        for x, y in points:
            x_memo[x].add(y)
        x_lst = sorted(list(x_memo.keys()))
        res = math.inf
        for i in range(len(x_lst)):
            for j in range(i+1, len(x_lst)):
                dx = x_lst[j]-x_lst[i]
                # print (dx)
                y_common = x_memo[x_lst[i]].intersection(x_memo[x_lst[j]])
                if len(y_common)<2:
                    continue
                y_common = sorted(list(y_common))
                for k in range(len(y_common)-1):
                    res = min(res, dx*(y_common[k+1]-y_common[k]))
        if math.isfinite(res):
            return res
        else:
            return 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        p = collections.defaultdict(set)
        for x, y in points:
            p[x].add(y)
        xs = sorted(p.keys())
        minv = float('inf')
        for i in range(len(xs) - 1):
            for j in range(i + 1, len(xs)):
                le = xs[j] - xs[i]
                if le > minv:
                    break
                potential = sorted(p[xs[i]].intersection(p[xs[j]]))
                #print(le, potential)
                v = float('inf')
                for j in range(len(potential) - 1):
                    v = min(v, potential[j + 1] - potential[j])
                minv = min(minv, v * le)
        return minv if minv < float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:


        s = set(map(tuple, points))
        dx = collections.defaultdict(list)
        dy = collections.defaultdict(list)
        for x,y in points:
            dx[x].append(y)
            dy[y].append(x)

        res = float('inf')
        for x in sorted(dx.keys()): # 1. find x in dx
            for i in range(len(dx[x])): # 2. find y1, y2 in dx[x]
                y1 = dx[x][i]
                for j in range(i+1, len(dx[x])):
                    y2 = dx[x][j]
                    for x1 in set(dy[y2])&set(dy[y1]): # 3. find x1 in dy[y2]
                        # always make x in the left side
                        if x1 <= x: continue
                      
                        res = min(res, abs(x-x1) * abs(y1-y2))

        return res if res != float('inf') else 0

'''
939. Minimum Area Rectangle.  Medium

Given a set of points in the xy-plane, 
determine the minimum area of a rectangle
formed from these points, with sides parallel
to the x and y axes.

If there isn't any rectangle, return 0.

Example 1:
Input: [[1,1],[1,3],[3,1],[3,3],[2,2]]
Output: 4

Example 2:
Input: [[1,1],[1,3],[3,1],[3,3],[4,1],[4,3]]
Output: 2

Note:
1 <= points.length <= 500
0 <= points[i][0] <= 40000
0 <= points[i][1] <= 40000
All points are distinct.

Accepted
55,377
Submissions
107,280

NOTE: Clarify: rectangle formed by ALL these points (convex hull)
            v. rectangle formed by ANY four distinct points
'''
class Solution:
    def minAreaRectangularHull(self, points: List[List[int]]) -> int:
        area = 0
        if points:
            minx, miny = points[0]
            maxx, maxy = points[0]
            for x, y in points:
                minx = min(minx, x)
                miny = min(miny, y)
                maxx = max(maxx, x)
                maxy = max(maxy, y)
            area = (maxx - minx)*(maxy - miny)
        return area
                

    def minAreaRect(self, points):
        '''
        Runtime: 1720 ms, faster than 36.22% of Python3 online submissions for Minimum Area Rectangle.
        Memory Usage: 14.4 MB, less than 17.19% of Python3 online submissions for Minimum Area Rectangle.
        '''
        S = set(map(tuple, points))
        ans = float('inf')
        for k, (xB, yB) in enumerate(points):
            for j in range(k):
                xA, yA = points[j]
                if (xA != xB and yA != yB and (xA, yB) in S and (xB, yA) in S):
                    ans = min(ans, abs((xB - xA) * (yB - yA)))
        return ans if ans < float('inf') else 0


class Solution:
    def minAreaRect(self, points):
        \"\"\"
        * loose criteria, just check all pairs of points.
        * set is faster than list
        \"\"\"
        seen = set()
        res = float('inf')
        for x1, y1 in points:
            for x2, y2 in seen:
                if (x1, y2) in seen and (x2, y1) in seen:
                    area = abs(x1 - x2) * abs(y1 - y2)
                    res = min(res, area)
                    # if area and area < res:
                    #     res = area
            seen.add((x1, y1))
        return res if res < float('inf') else 0
    
        ### TLE
        # res = float('inf')
        # for i, (x1, y1) in enumerate(points):
        #     for x2, y2 in points[i:]:
        #         if [x1, y2] in points and [x2, y1] in points:
        #             area = abs(x1 - x2) * abs(y1 - y2)
        #             if area and area < res:
        #                 res = area
        #     #seen.add((x1, y1))
        # return res if res < float('inf') else 0
    


        

from collections import defaultdict

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        xs = defaultdict(list)
        ys = defaultdict(list)
        hm = defaultdict(bool)

        for point in points:
            xs[point[0]] += [point]
            ys[point[1]] += [point]
            hm[tuple(point)] = True 

        min_area = float(\"inf\")
        for p1 in points:
            # get all points on the same y as p1 that are 
            same_y = [(x,y) for x,y in ys[p1[1]] if x > p1[0]]
            same_x = [(x,y) for x,y in xs[p1[0]] if y > p1[1]]

            for p2 in same_y:
                for p3 in same_x:
                    p4 = (p2[0], p3[1])
                    if hm[p4]:
                        dx = abs(p2[0]-p1[0])
                        dy = abs(p3[1]-p1[1])
                        min_area = min(min_area, dx*dy)

        return min_area if min_area < float(\"inf\") else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        seen = set()
        output = float('inf')
        
        for x1, y1 in points:
            for x2, y2 in seen:
                if (x1, y2) in seen and (x2, y1) in seen:
                    area = abs(x1-x2) * abs(y1-y2)
                    if area > 0 and area < output:
                        output = area
            seen.add((x1,y1))
        return output if output < float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        if len(points) <= 1:
            return 0
        points.sort(key=lambda a: a[0] * 10 ** 6 + a[1])
        min_array = float(\"inf\")
        point_dict = {}
        current_layer = -1
        current_points = []
        for y, x in points:
            if y != current_layer:
                for old_x in current_points:
                    point_dict.setdefault(old_x, set()).add(current_layer)
                current_layer = y
                current_points = []
            if x in point_dict:
                for previous_x in current_points:
                    valid_y = point_dict[x].intersection(point_dict[previous_x])
                    if valid_y:
                        min_array = min(min_array, (y - max(valid_y)) * (x - previous_x))
                current_points.append(x)
            else:
                point_dict.setdefault(x, set()).add(y)
        if min_array == float(\"inf\"):
            return 0
        else:
            return min_array

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
      #  if points == [] or points == [[]] or points == None or len(points) < 4:
      #      return 0
        
        seen = set()
        res = float('inf')
        for x1, y1 in points:
            for x2, y2 in seen:
                if (x1, y2) in seen and (x2, y1) in seen:
                    area = abs(x1 - x2) * abs(y1 - y2)
                    if area and area < res:
                        res = area
            seen.add((x1, y1))
        
        if res != float('inf'):
            return res
        else: 
            return 0

            
        
        


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        \"\"\"
        horizontal line if yi == yj
        veritcal line if xi == xj
        
        valid rectangle:
        - p1, p2, p3, p4 such that
        \"\"\"
        
        min_area = float('inf')
        
        pts = {(x,y) for x,y in points}
        
        for x1,y1 in pts:
            for x2,y2 in pts:
                if x2 > x1 and y2 > y1: #first pt is bottom left vertex, second pt is upper right vertex
                    if (x1, y2) in pts and (x2, y1) in pts:
                        min_area = min(min_area, abs(x2-x1) * abs(y2-y1))
                        
        if min_area == float('inf'):
            return 0
        return min_area

'''
939. Minimum Area Rectangle.  Medium

Given a set of points in the xy-plane, 
determine the minimum area of a rectangle
formed from these points, with sides parallel
to the x and y axes.

If there isn't any rectangle, return 0.

Example 1:
Input: [[1,1],[1,3],[3,1],[3,3],[2,2]]
Output: 4

Example 2:
Input: [[1,1],[1,3],[3,1],[3,3],[4,1],[4,3]]
Output: 2

Note:
1 <= points.length <= 500
0 <= points[i][0] <= 40000
0 <= points[i][1] <= 40000
All points are distinct.

Accepted
55,377
Submissions
107,280

NOTE: Clarify: rectangle formed by ALL these points (convex hull)
            v. rectangle formed by ANY four distinct points
'''
class Solution:
    def minAreaRectangularHull(self, points: List[List[int]]) -> int:
        area = 0
        if points:
            minx, miny = points[0]
            maxx, maxy = points[0]
            for x, y in points:
                minx = min(minx, x)
                miny = min(miny, y)
                maxx = max(maxx, x)
                maxy = max(maxy, y)
            area = (maxx - minx)*(maxy - miny)
        return area
                

    def minAreaRect(self, points):
        '''
        Runtime: 1628 ms, faster than 38.40% of Python3 online submissions for Minimum Area Rectangle.
        Memory Usage: 14.4 MB, less than 17.19% of Python3 online submissions for Minimum Area Rectangle.
        '''
        S = set(map(tuple, points))
        ans = float('inf')
        for k, (xB, yB) in enumerate(points):
            for j in range(k):
                xA, yA = points[j]
                if (xA != xB and yA != yB and (xA, yB) in S and (xB, yA) in S):
                    ans = min(ans, abs((xB - xA) * (yB - yA)))
        return ans if ans < float('inf') else 0


from collections import defaultdict
class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        # similar=[]
        
        # for point in points:
        #     x,y=point
        #     if x==y:
        #         similar.append((x,y))
        points_set=set([(i[0],i[1]) for i in points ])
        
        mini=float(\"INF\")
        # points.sort(key=lambda x:x[0])
        for point1 in points:
            for point2 in points: 
                x1,y1=point1
                x2,y2=point2
                
                if (x2>x1) and (y2>y1) and (x1,y2) in points_set and (x2,y1) in points_set:
                    # print(x1,y1,x2,y2)
                    mini=min(mini,(y2-y1)*(x2-x1)) 
            
        return mini if not mini==float(\"INF\") else 0
                
        
        

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        mx = defaultdict(set)
        for x, y in points: mx[x].add(y)
        ans, n = float('inf'), len(points)
        for i in range(n-1):
            x1, y1 = points[i]
            for j in range(i+1, n):                
                x2, y2 = points[j]
                if x2 == x1 or y2 == y1 or y2 not in mx[x1] or y1 not in mx[x2]: continue                    
                ans = min(abs(x2-x1)*abs(y2-y1), ans)
        return 0 if ans == float('inf') else ans

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        counter = Counter(list(map(tuple, points)))
        if any(count >= 4 for count in list(counter.values())):
            return 0
        points = list(counter)
        result = float('inf')
        for i, (x, y) in enumerate(points):
            for j in range(i + 1, len(points)):
                x_, y_ = points[j]
                if x != x_ and y != y_:
                    area = abs((x_ - x) * (y_ - y))
                    if area < result and (x, y_) in counter and (x_, y) in counter:
                        if not area:
                            return 0
                        result = area
                elif counter[(x, y)] == 2 and counter[(x_, y_)] == 2:
                        return 0
        return 0 if result == float('inf') else result


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        
        # hash set
        points = { (x, y) for x, y in points}
        min_area = float('inf')
        
        for x1, y1 in points:
            for x2, y2 in points:
                if x1 == x2 or y1 == y2:
                    continue
                if abs((x1 - x2) * (y1 - y2)) > min_area:
                    # Don't lookup
                    continue
                if (x1, y2) in points and (x2, y1) in points:
                    min_area = min(min_area, abs((x1 - x2) * (y1 - y2)))
                        
        if min_area == float('inf'):
            return 0
        return min_area

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        if not points: return 0
        x_lines, y_lines = {}, {}
        for x, y in points:
            x_lines[x] = x_lines.get(x, set()) | {y}
            y_lines[y] = y_lines.get(y, set()) | {x}
        # print(x_lines, y_lines)
        res = float('inf')
        for x, y_s in x_lines.items():
            if len(y_s) <2: continue
            # print(x, y_s)
            y_s = list(y_s)
            for i in range(len(y_s)):
                for j in range(i+1, len(y_s)):
                    y1, y2 = y_s[i], y_s[j]
                    inter = y_lines[y1].intersection(y_lines[y2])
                    # print(inter)
                    if len(inter) <= 1: continue
                    for x2 in inter:
                        if x2 == x: continue 
                        res = min(res, abs(y1-y2)*abs(x-x2))
        return res if res != float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        if len(points) < 4:
            return 0
        # watch for degenerate cases of 4 same points or getting a line
        # if points weren't distinct, can convert to set and then back to list
        # for every x, need list of y's (set)
        # for every y, need list of x's (set)
        # then, O(4) to find a rectangle
        # x_vals: {1: [1, 3], 3: [3, 1], 2: [2]}
        # y_vals: {1, [1, 3], 3: [3, 1], 2: [2]}
        
        # Then, to check, loop through all x's and see if we can get all 4 points for each; if so, calculate area
        x_vals = defaultdict(set)
        y_vals = defaultdict(set)
        for p in points:
            x_vals[p[0]].add(p[1])
            y_vals[p[1]].add(p[0])
        # print(x_vals)
        # print(y_vals)
        min_area = 40000*40000 + 1
        for x, y_coords in list(x_vals.items()):
            if len(y_coords) == 1:
                continue # can't make a rectangle if it's the only point on that x = x line
            y_list = list(y_coords)
            for i, y_1 in enumerate(y_list):
                for j, y_2 in enumerate(y_list[i+1:]):
                    # we have points (x, y_1) and (x, y_2)
                    # now need to check if same value in both y_vals[y_1] and y_vals[y_2]
                    # if so, we have a rectangle!
                    common_x_2 = y_vals[y_1].intersection(y_vals[y_2])
                    common_x_2.remove(x)
                    for x_2 in list(common_x_2):
                        # print(x, x_2, y_1, y_2)
                        min_area = min(min_area, abs(x_2 - x) * abs(y_2 - y_1))
        if min_area == 40000*40000 + 1:
            return 0
        return min_area



class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        
        point_set = set([tuple(x) for x in points])
            
        min_size = float('inf')
        n = len(points)
        for i in range(n):
            x1, y1 = points[i]
            for j in range(i+1, n):
                x2, y2 = points[j]
                if x1 == x2 or y1 == y2:
                    continue
                
                if (x1, y2) in point_set and (x2, y1) in point_set:
                    size = (y2-y1)*(x2-x1)
                    min_size = min(min_size, abs(size))
                    continue
                    
        if min_size == float('inf'):
            return 0
        return min_size
                


class Solution:
    def minAreaRect(self, points):
        pSet, ans = set(map(tuple, points)), float('inf')
        for i, (p1x, p1y) in enumerate(points):
            for (p2x, p2y) in points[i+1:]:
                if (p1x != p2x) and (p1y != p2y) and ((p1x, p2y) in pSet) and ((p2x, p1y) in pSet):
                    ans = min(ans, abs((p1x - p2x) * (p1y - p2y)))
        return ans if ans < float('inf') else 0
    
class Solution:
    def minAreaRect(self, points):
        X = list(zip(*points))
        if len(set(X[0])) == 1 or len(set(X[1])) == 1: return 0

        pSet, ans = set(map(tuple, points)), float('inf')
        for i, (p1x, p1y) in enumerate(points):
            for (p2x, p2y) in points[i+1:]:
                area = abs((p2x-p1x)*(p2y-p1y))
                if area > ans or area == 0:
                    continue
                if (p1x, p2y) in pSet and (p2x, p1y) in pSet:
                    ans = area
        return ans if ans < float('inf') else 0
    
from collections import defaultdict
class Solution:
    def minAreaRect(self, points):
        X = list(zip(*points))
        Nr, Nc = len(set(X[0])), len(set(X[1]))
        if Nr == 1 or Nc == 1: return 0

        points.sort()
        columns = defaultdict(list)
        for r, c in points:
            columns[r].append(c)
        ans, lastc = float('inf'), dict()
        for r, cols in list(columns.items()):
            for i, c1 in enumerate(cols):
                for c2 in cols[i+1:]:
                    if (c1, c2) in lastc:
                        area = (r - lastc[(c1, c2)]) * (c2 - c1)
                        #ans = min(ans, area)
                        if area < ans: ans = area
                    lastc[(c1, c2)] = r
        return ans if ans < float('inf') else 0


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        m = set()
        for point in points:
            m.add(tuple(point))
        res = 0x3fffffff
        for i in range(len(points)):
            x1, y1 = points[i]
            for j in range(i + 1, len(points)):
                x2, y2 = points[j]
                if x1 != x2 and y1 != y2:
                    if (x1, y2) in m and (x2, y1) in m:
                        res = min(res, abs((y1 - y2) * (x1 - x2)))
        res = 0 if res == 0x3fffffff else res
        return res

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        seen = set()
        res = float('inf')
        for point in points:                                                    
            x, y = point[0], point[1]            
            for _x, _y in seen:
                if (x, _y) in seen and (_x, y) in seen:
                    res = min(res, abs(x - _x) * abs(y - _y))
            seen.add((x, y))
            
        if res == float('inf'):
            return 0
        return res
                

            
            
'''
    def minAreaRect(self, points):
        n = len(points)
        nx = len(set(x for x, y in points))
        ny = len(set(y for x, y in points))
        if nx == n or ny == n:
            return 0

        p = collections.defaultdict(list)
        if nx > ny:
            for x, y in points:
                p[x].append(y)
        else:
            for x, y in points:
                p[y].append(x)

        lastx = {}
        res = float('inf')
        for x in sorted(p):
            p[x].sort()
            for i in range(len(p[x])):
                for j in range(i):
                    y1, y2 = p[x][j], p[x][i]
                    if (y1, y2) in lastx:
                        res = min(res, (x - lastx[y1, y2]) * abs(y2 - y1))
                    lastx[y1, y2] = x
        return res if res < float('inf') else 0

'''
        
        
            
            


class Solution:
    def minAreaRect(self, points):
        \"\"\"
        :type points: List[List[int]]
        :rtype: int
        In order to form a rectangle, you need four points all positioned at 90 degrees to each other.
        In this approach, we store all given points in a set.Then iteratively go through all the points in two loops (x1, y1) and (x2, y2) while checking if (x1, y2) and (x2, y1) are also valid points. If so, we found a rectangle.We calculate the area of this rectangle. If this area is smaller than the minimum area seen so far, make it the minimum area.
        
        \"\"\"
        min_area = sys.maxsize
        points_table = set()
        
        for x, y in points:
            points_table.add((x,y))
            
        for x1, y1 in points:
            for x2, y2 in points:
                if x1 > x2 and y1 > y2:
                    # Skip looking at same point
                    if (x1, y2) in points_table and (x2, y1) in points_table:
                        area = abs(x1 -  x2) * abs(y1 - y2)
                        if area:
                            min_area = min(area, min_area)
                        
        return 0 if min_area == sys.maxsize else min_area

'''
939. Minimum Area Rectangle.  Medium

Given a set of points in the xy-plane, 
determine the minimum area of a rectangle
formed from these points, with sides parallel
to the x and y axes.

If there isn't any rectangle, return 0.

Example 1:
Input: [[1,1],[1,3],[3,1],[3,3],[2,2]]
Output: 4

Example 2:
Input: [[1,1],[1,3],[3,1],[3,3],[4,1],[4,3]]
Output: 2

Note:
1 <= points.length <= 500
0 <= points[i][0] <= 40000
0 <= points[i][1] <= 40000
All points are distinct.

Accepted
55,377
Submissions
107,280

NOTE: Clarify: rectangle formed by ALL these points (convex hull)
            v. rectangle formed by ANY four distinct points
'''
class Solution:
    def minAreaRectangularHull(self, points: List[List[int]]) -> int:
        area = 0
        if points:
            minx, miny = points[0]
            maxx, maxy = points[0]
            for x, y in points:
                minx = min(minx, x)
                miny = min(miny, y)
                maxx = max(maxx, x)
                maxy = max(maxy, y)
            area = (maxx - minx)*(maxy - miny)
        return area
                

    def minAreaRect(self, points):
        S = set(map(tuple, points))
        ans = float('inf')
        for k, (xB, yB) in enumerate(points):
            for j in range(k):
                xA, yA = points[j]
                if (xA != xB and yA != yB and (xA, yB) in S and (xB, yA) in S):
                    ans = min(ans, abs(xB - xA) * abs(yB - yA))
        return ans if ans < float('inf') else 0


from collections import defaultdict

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        if not points:
            return 0
        
        mapper = defaultdict(set)

        for point in points:
            mapper[point[1]].add(point[0])
            
        result = float('inf')
        
        for y1, x_values1 in list(mapper.items()):
            
            for y2, x_values2 in list(mapper.items()):
                if y1 == y2:
                    continue
                intersect = x_values1.intersection(x_values2)
                
                if len(intersect) < 2:
                    continue
                
                x_min_diff = self.min_diff(list(intersect))
                
                temp = x_min_diff * abs(y1-y2)
                if temp < result:
                    result = temp
        return result if result != float('inf') else 0
    
    def min_diff(self, given: list) -> int:
        result = float('inf')
        
        for i in range(len(given)):
            for j in range(i+1, len(given)):
                diff = abs(given[i] - given[j])
                if diff < result:
                    result = diff
        return result
                
                        
        
            
            
        
            
            


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        point_set = set(map(tuple, points))
        
        min_area = float('inf')
        
        for i, p1 in enumerate(points):
            for j in range(i):
                p2 = points[j]
                potential_area = abs(p1[0]-p2[0]) * abs(p1[1]-p2[1])
                
                if potential_area == 0 or potential_area > min_area:
                    continue
                    
                if (p1[0],p2[1]) in point_set and (p2[0],p1[1]) in point_set:
                    
                    min_area = min(potential_area, min_area)     
                    
        return 0 if min_area == float('inf') else min_area

from collections import defaultdict
class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        
        ref_x = defaultdict(list)
        ref_y = defaultdict(list)
        
        for point in points:
            x, y = point[0], point[1]
            ref_x[x].append(y)
            ref_y[y].append(x)
            
        print(\"ref_x={}\".format(ref_x))
        print(\"ref_y={}\".format(ref_y))
        
        ans = float('inf')
        for x in ref_x.keys():
            if len(ref_x[x]) < 2:
                continue
            
            ys = sorted(ref_x[x])
            for i in range(len(ys)-1):
                first_y = ys[i]
                if len(ref_y[first_y]) < 2:
                    continue
                for j in range(i+1, len(ys)):
                    second_y = ys[j]
                    if len(ref_y[second_y]) < 2:
                        continue
                    
                    second_xs = list(set(ref_y[first_y]).intersection(set(ref_y[second_y])))
                    if not second_xs:
                        continue
                    
                    for second_x in second_xs:
                        #print(\"first_x={} second_x={} first_y={} second_y={} res={}\".format(x, second_x, first_y, second_y, abs(x-second_x)*abs(first_y-second_y)))
                        if x != second_x and first_y != second_y:
                            ans = min(ans, abs(x-second_x)*abs(first_y-second_y))
                        
        return ans if ans < float('inf') else 0
                
        

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        points_set = set((r,c) for r,c in points)
        m = 2 ** 32
        for i in range(len(points)):
            x1,y1 = points[i]
            for j in range(i, len(points)):
                x2,y2 = points[j]
                if x2 == x1 or y2 == y1:
                    continue
                if (x2,y1) not in points_set:
                    continue
                if (x1,y2) not in points_set:
                    continue
                area = abs(x1-x2) * abs(y1 - y2)
                m = min(m,area)
        if m == 2**32:
            return 0
        else:
            return m

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        st = set()
        
        for x,y in points:
            st.add((x,y))
            
        minarea = float('Inf')
        for i, p1 in enumerate(points):
            for p2 in points[:i]:
                x1,y1 = p1
                x2,y2 = p2
                
                if x1 != x2 and y1 != y2 and (x1,y2) in st and (x2,y1) in st:
                    minarea = min(minarea, abs(x1-x2)*abs(y1-y2))
                    
        return minarea if minarea < float('Inf') else 0

class Solution:
    def minAreaRect(self, a: List[List[int]]) -> int:
        mx = defaultdict(set)
        for x, y in a:
            mx[x].add(y)
        ans, n = float('inf'), len(a)
        # print(mx)
        for i in range(n-1):
            x1, y1 = a[i]
            for j in range(i+1, n):                
                x2, y2 = a[j]
                if x2 == x1 or y2 == y1 or y2 not in mx[x1] or y1 not in mx[x2]:
                    continue                    
                ans = min(abs(x2-x1)*abs(y2-y1), ans)
        return 0 if ans == float('inf') else ans

from collections import defaultdict
from bisect import insort
class Solution:
    def minAreaRect(self, points):
        X = list(zip(*points))
        if len(set(X[0])) == 1 or len(set(X[1])) == 1: return 0 
        points.sort()
        columns = defaultdict(list)
        for r, c in points:
            insort(columns[r], c)
        ans, lastc = float('inf'), dict()
        for r, cols in columns.items():
            for i, c1 in enumerate(cols):
                for c2 in cols[i+1:]:
                    if (c1, c2) in lastc:
                        area = abs((r - lastc[(c1, c2)]) * (c2 - c1))
                        #ans = min(ans, area)
                        if area < ans: ans = area
                    lastc[(c1, c2)] = r
        return ans if ans < float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        dic = collections.defaultdict(set)
        for x, y in points:
            dic[x].add(y)
        res = float('inf')
        n = len(points)
        for i in range(n):
            x1, y1 = points[i]
            for j in range(i + 1, n):
                x2, y2 = points[j]
                if x1 == x2 or y1 == y2 or y2 not in dic[x1] or y1 not in dic[x2]:
                    continue
                else:
                    res = min(res, abs(x1 - x2) * abs(y1 - y2))
        return res if res != float('inf') else 0


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        points = sorted(points, key=lambda x: (x[0], x[1]))
        self.res = float('inf')
        self.cache = defaultdict(list)
        
        x_cache = defaultdict(list)
        
        for x,y in points:
            for y2 in x_cache[x]:
                if y2 < y:
                    for x_other in self.cache[(y, y2)]:
                        self.res = min(self.res, abs(x_other-x) * abs(y2-y))
                    self.cache[(y, y2)].append(x)
            x_cache[x].append(y)
        if self.res == float('inf'):
            self.res = 0
        return self.res

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        maps = set(map(tuple, points))
        points.sort()
        n = len(points)
        result = math.inf
        for i in range(n):
            x1, y1 = points[i]
            for j in range(i + 1, n):
                x2, y2 = points[j]
                if x2 == x1 or y1 == y2: continue
                if (x1, y2) in maps and (x2, y1) in maps:
                    result = min(result, (x2 - x1) * abs(y2 - y1))
        return 0 if math.isinf(result) else result

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        \"\"\"
        |
        |  *       *   
        |       *
        |  *       *
        |_______________
        
        
        
        
        \"\"\"
        
        min_x = float('inf')
        max_x = float('-inf')
        
        points_dict = dict()
        left_sides = dict()
        
        for point in points:
            min_x = min(min_x, point[0])
            max_x = max(max_x, point[0])
        
            if point[0] not in points_dict:
                points_dict[point[0]] = [point]
            else:
                points_dict[point[0]].append(point)
        
        min_area = float('inf')
        
        
        for x in range(min_x, max_x + 1):
            if x in points_dict:
                for point in points_dict[x]:
                    for point2 in points_dict[x]:
                        if (point[1], point2[1]) in left_sides:
                            min_area = min(min_area, abs(point[1] - point2[1]) * abs(point[0] - left_sides[point[1], point2[1]]))

                        if point != point2:
                            left_sides[point[1], point2[1]] = x
        
        return 0 if min_area == float('inf') else min_area

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        dic = collections.defaultdict(set)
        for x, y in points:
            dic[x].add(y)
        res = float('inf')
        n = len(points)
        for i in range(n):
            x1, y1 = points[i]
            for j in range(i + 1, n):
                x2, y2 = points[j]
                if x1 == x2 or y1 == y2 or y2 not in dic[x1] or y1 not in dic[x2]:
                    continue
                else:
                    res = min(res, abs(x1 - x2) * abs(y1 - y2))
        return res if res != float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        res = sys.maxsize
        diag = collections.defaultdict(set)
        for i, j in points:
            diag[i].add(j)
        
        for i in range(len(points)):
            a1, a2 = points[i]
            for j in range(len(points)):
                b1, b2 = points[j]
                if not (b1 > a1 and b2 > a2):
                    continue
                if b2 in diag[a1] and a2 in diag[b1]:
                    # print(\"?\")
                    res = min(res, abs(a1 - b1) * abs(a2 - b2))
        
        
        return res if res != sys.maxsize else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        def intersection(lst1, lst2): 
            return list(set(lst1) & set(lst2)) 
  
        col=defaultdict(list)
#        row=defaultdict(list)
        ans=float('inf')
        for point in points:
            col[point[0]].append(point[1])
#            row[point[1]].append(point[0])
        cols=list(col.keys())
        for i1 in range(len(cols)):
            for i2 in range(i1+1,len(cols)):
                col1=cols[i1]
                col2=cols[i2]
                il=sorted(intersection(col[col1],col[col2]))
                for i in range(1,len(il)):
                    ans=min(ans,(il[i]-il[i-1])*abs(col2-col1))
        return ans if ans<float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        minArea = float('inf')
        pointsSet = set()
        for x, y in points:
            pointsSet.add((x, y))
        
            
        for x1, y1 in points:
            for x2, y2 in points:
                if x1 > x2 and y1 > y2:
                    if (x1, y2) in pointsSet and (x2, y1) in pointsSet:
                        area = abs(x1-x2) * abs(y1-y2)
                        if area:
                            minArea = min(minArea, area)
                            
        return minArea if minArea != float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        area = float(inf)
        dt = set()
        for x, y in points:
            dt.add((x,y))
        
        for i in range(len(points)):
            x, y = points[i]
            for j in range(i+1, len(points)):
                h, v = points[j]
                if h == x or v == y:
                    continue
                if (x,v) in dt and (h, y) in dt:
                    area = min(area, abs(y-v)*abs(h-x))
        
        return area if area != float('inf') else 0

import itertools
class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        if not points:
            return 0
        ybucket = defaultdict(list)
        for x, y in points:
            ybucket[y].append(x)

        for k, v in ybucket.items():
            if len(v) >= 2:
                ybucket[k] = sorted(v)

        ys = [k for k in ybucket.keys() if len(ybucket[k]) >= 2]
        ans = float('inf')
        for y1, y2 in itertools.combinations(ys, 2):
            row1 = set(ybucket[y1])
            row2 = ybucket[y2]
            dy = abs(y2-y1)
            xlast = None            
            for x in row2:
                if x in row1:
                    if xlast is not None:
                        ans = min(ans, abs(x-xlast)*dy)
                    xlast = x

        return ans if ans < float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        xs = collections.defaultdict(set)
        ys = collections.defaultdict(set)
        for point in points:
            x, y = point[0], point[1]
            xs[x].add(y)
            ys[y].add(x)
        area = 40000 ** 2 + 1
        for x in xs:
            for y1, y2 in itertools.product(xs[x], xs[x]):
                if y1 <= y2:
                    continue
                #print(f'x={x}, y1={y1}, y2={y2}')
                A = set(xx for xx in ys[y1] & ys[y2] if xx < x)
                #print('A=', A)
                if len(A) > 0:
                    area = min(area, (y1 - y2) * (x - max(A)))
        return 0 if area == 40000 ** 2 + 1 else area

\"\"\"
https://www.youtube.com/watch?v=pFgBZFKJ2Co
fast forward to 17minutes

Group the points by x coordinates, so that we have columns of points. Then, for every pair of points in a column (with coordinates (x,y1) and (x,y2)), check for the smallest rectangle with this pair of points as the rightmost edge. We can do this by keeping memory of what pairs of points we've seen before.

columns = {
    1: 1, 3
    3: 1, 3
    4: 1, 4
    
}

column = [1,3]
\"\"\"
class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        # dictionary that stores a list of x coordinates for each vertical edge (y1, y2) where y1 < y2
        x_coord = collections.defaultdict(list) 
        points.sort(key = lambda x: (x[0], x[1])) # For making traversal more convenient
        min_area = float('inf')
        for i in range(len(points)):
            for j in range(i+1, len(points)):
                x1, y1 = points[i]
                x2, y2 = points[j]
                if x1 == x2: # This is a vertical edge.
                    if (y1, y2) in x_coord: # We have seen this vertical edge in the past.
                        x_other = x_coord[(y1, y2)][-1] # Recall: we traverse points in the order of inceasing x
                        min_area = min(min_area, (y2-y1) * (x1 - x_other)) # Update min area
                    x_coord[(y1, y2)].append(x1)
        return min_area if min_area!=float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        ptmap = {}
        min_area = float('inf')
        for x1,y1 in points:
            for x2,y2 in ptmap:
                if ((x1,y2) in ptmap) and ((x2,y1) in ptmap):
                    area = abs(x2-x1) * abs(y2-y1)
                    min_area  = min(area,min_area)
            ptmap[(x1,y1)] = (x1,y1)
        return min_area if min_area!=float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        pointrec=dict()
        for x,y in points:
            if not x in pointrec:
                pointrec[x]=dict()
            pointrec[x][y]=1
        area=16*10**8
        for i in range(len(points)-1):
            for j in range(i+1,len(points)):
                a=abs((points[i][0]-points[j][0])*(points[i][1]-points[j][1]))
                if a==0:
                    continue
                if a<area and (points[j][1] in pointrec[points[i][0]]) and (points[i][1] in pointrec[points[j][0]]):
                    area=a
        if area==16*10**8:
            area=0
        return area

MAX_AREA = 40000 * 40000
class Solution:
\tdef minAreaRect(self, points: List[List[int]]) -> int:
\t\tpoints_set = set()
\t\tfor x, y in points:
\t\t\tpoints_set.add((x,y))
\t\tmin_area = MAX_AREA
\t\tn = len(points)
\t\tfor i in range(n-1):
\t\t\tx1,y1 = points[i]
\t\t\tfor j in range(i+1, n):
\t\t\t\tx2,y2 = points[j]
\t\t\t\tif x1 == x2 or y1 == y2:
\t\t\t\t\tcontinue
\t\t\t\tif (x1, y2) in points_set and (x2,y1) in points_set:
\t\t\t\t\tmin_area = min(min_area, abs(x2 - x1) * abs(y2 - y1))

\t\treturn 0 if min_area == MAX_AREA else min_area


from collections import defaultdict
from itertools import combinations

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        
        mPoints = set( (x, y) for x, y in points )
        area = float('inf')
        
        for (x1, y1), (x2, y2) in combinations(points, 2):
            if x1 != x2 and y1 != y2 and (x1, y2) in mPoints and (x2, y1) in mPoints:
                area = min(area, abs(x1-x2)*abs(y1-y2))
        return area if area < float('inf') else 0
                
            
        
        


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        counter = Counter(list(map(tuple, points)))
        if any(count >= 4 for count in list(counter.values())):
            return 0
        points = list(counter)
        result = float('inf')
        for i, (x, y) in enumerate(points):
            for j in range(i + 1, len(points)):
                x_, y_ = points[j]
                area = abs((x_ - x) * (y_ - y))
                if x != x_ and y != y_:
                    if area < result and (x, y_) in counter and (x_, y) in counter:
                        result = area
                        if not result:
                            return 0
                elif counter[(x, y)] == 2 and counter[(x_, y_)] == 2:
                        return 0
        return 0 if result == float('inf') else result


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        xd = collections.defaultdict(set)
        
        for p in points:
            xd[p[0]].add(p[1])
        
        yd = collections.defaultdict(list)
        
        for x, v in list(xd.items()):
            if len(v) < 2:
                continue
                
            ys = list(v)
            
            for i in range(len(ys)):
                for j in range(i+1, len(ys)):
                    miny, maxy = min(ys[i], ys[j]), max(ys[i], ys[j])
                    yd[(miny, maxy)].append(x)
                    
        res = float('inf')
        
        for ypair, xlist in list(yd.items()):
            if len(xlist) < 2:
                continue
            
            xlist = sorted(xlist)
            h = ypair[1] - ypair[0]
            
            for i in range(len(xlist)-1):
                res = min(res, h * (xlist[i+1] - xlist[i]))
        
        return res if res != float('inf') else 0
                
            
             
            
        


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        d = collections.defaultdict(set)
        for i in range(len(points)):
            x0, y0 = points[i]
            for j in range(i+1, len(points)):
                x1, y1 = points[j]
                if x0 == x1:
                    d[x0].add(tuple(sorted([y0,y1])))
        seen = set()
        minA = float('inf')
        for x0, lines0 in d.items(): 
            seen.add(x0)
            for x1, lines1 in d.items():
                if x1 not in seen:
                    for l in lines0:
                        if l in lines1:
                            minA = min(minA, abs(x0-x1)*(l[1]-l[0]))
        return minA if minA<float('inf') else 0

from collections import defaultdict
from bisect import insort
class Solution:
    def minAreaRect(self, points):
        X = list(zip(*points))
        if len(set(X[0])) == 1 or len(set(X[1])) == 1: return 0

        points.sort()
        columns = defaultdict(list)
        for r, c in points:
            insort(columns[r], c)
        ans, lastc = float('inf'), dict()
        for r, cols in columns.items():
            for i, c1 in enumerate(cols):
                for c2 in cols[i+1:]:
                    if (c1, c2) in lastc:
                        ans = min(ans, (r - lastc[(c1, c2)]) * (c2 - c1))
                    lastc[(c1, c2)] = r
        return ans if ans < float('inf') else 0

class Solution:
    def minAreaRect(self, a: List[List[int]]) -> int:
        mx = defaultdict(set)

        for x, y in a: mx[x].add(y)

        ans, n = float('inf'), len(a)
        for i in range(n-1):
            x1, y1 = a[i]
            for j in range(i+1, n):
                x2, y2 = a[j]

                if x2 == x1 or y2 == y1 or y2 not in mx[x1] or y1 not in mx[x2]: continue
                ans = min(abs(x2-x1)*abs(y2-y1), ans)

        return 0 if ans == float('inf') else ans


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        x_to_points = defaultdict(lambda: [])
        y_to_points = defaultdict(lambda: set())
        for [x, y] in points:
            x_to_points[x].append((x, y))
            y_to_points[y].add((x, y))
            
        min_rect = float('inf')
        for x in sorted(x_to_points.keys()):
            xpoints = x_to_points[x]
            if len(xpoints) < 2:
                continue
            
            for x1_index in range(len(xpoints)):
                for x2_index in range(x1_index + 1, len(xpoints)):
                    x1, y1 = xpoints[x1_index]
                    x2, y2 = xpoints[x2_index]
                    for x3, y3 in y_to_points[y1]:     
                        if x3 == x2:
                            continue
                        if (x3, y2) in y_to_points[y2]:
                            min_rect = min(min_rect, abs(y2-y1)*abs(x3-x2))
            
        
        return 0 if min_rect  == float('inf') else min_rect


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        res = inf = float('inf')
        points_set = set(tuple(i) for i in points)
        points = list(points_set)
        n = len(points)
        for i in range(n):
            for j in range(i+1, n):
                x1, y1 = points[i]
                x2, y2 = points[j]
                if x1 != x2 and y1 != y2 and (x1, y2) in points_set and (x2, y1) in points_set:
                    res = min(res, abs((x2-x1)*(y2-y1)))
        return res if res != inf else 0

from collections import defaultdict
class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        dctyx=defaultdict(set) #key: y, value:x
        dctxy=defaultdict(set)
        xlist=[]
        ylist=[]
        for x,y in points:
            dctyx[y].add(x)
            dctxy[x].add(y)
        ans=inf
        for i in range(len(points)):
            for j in range(i+1,len(points)):
                x1,y1=points[i]
                x2,y2=points[j]
                if not (x1==x2 or y1==y2):
                    if y2 in dctxy[x1] and x2 in dctyx[y1]:
                        ans=min(ans,abs(x1-x2)*abs(y1-y2))
        return ans if ans!=inf else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        if len(points) < 4:
            return 0
        # watch for degenerate cases of 4 same points or getting a line
        # if points weren't distinct, can convert to set and then back to list
        # for every x, need list of y's (set)
        # for every y, need list of x's (set)
        # then, O(4) to find a rectangle
        # x_vals: {1: [1, 3], 3: [3, 1], 2: [2]}
        # y_vals: {1, [1, 3], 3: [3, 1], 2: [2]}
        
        # Then, to check, loop through all x's and see if we can get all 4 points for each; if so, calculate area
        
        # Runtime: O(n^3)
        # x_vals = defaultdict(set)
        # y_vals = defaultdict(set)
        # for p in points:
        #     x_vals[p[0]].add(p[1])
        #     y_vals[p[1]].add(p[0])
        # # print(x_vals)
        # # print(y_vals)
        # min_area = 40000*40000 + 1
        # for x, y_coords in x_vals.items():
        #     if len(y_coords) == 1:
        #         continue # can't make a rectangle if it's the only point on that x = x line
        #     y_list = list(y_coords)
        #     for i, y_1 in enumerate(y_list):
        #         for j, y_2 in enumerate(y_list[i+1:]):
        #             # we have points (x, y_1) and (x, y_2)
        #             # now need to check if same value in both y_vals[y_1] and y_vals[y_2]
        #             # if so, we have a rectangle!
        #             common_x_2 = y_vals[y_1].intersection(y_vals[y_2])
        #             common_x_2.remove(x) # avoid degenerate case of a line
        #             for x_2 in list(common_x_2):
        #                 # print(x, x_2, y_1, y_2)
        #                 min_area = min(min_area, abs(x_2 - x) * abs(y_2 - y_1))
        # if min_area == 40000*40000 + 1:
        #     return 0
        # return min_area
    
        # Improved O(n^2) solution that's a lot simpler too:
        x_coord = collections.defaultdict(list) # dictionary that stores a list of x coordinates for each vertical edge (y1, y2) where y1 < y2
        points.sort(key = lambda x: (x[0], x[1])) # For making traversal more convenient
        min_area = float('inf')
        for i in range(len(points)):
            for j in range(i+1, len(points)):
                x1, y1 = points[i]
                x2, y2 = points[j]
                if x1 == x2: # This is a vertical edge.
                    if (y1, y2) in x_coord: # We have seen this vertical edge in the past.
                        x_other = x_coord[(y1, y2)][-1] # Recall: we traverse points in the order of inceasing x
                        min_area = min(min_area, (y2-y1) * (x1 - x_other)) # Update min area
                    x_coord[(y1, y2)].append(x1)
        return min_area if min_area!=float('inf') else 0


from itertools import combinations

class Solution:
    def minAreaRect(self, points) -> int:
        point_x = {}
        point_y = {}
        result = float('inf')
        for i in points:
            if point_x.get(i[0]):
                point_x[i[0]].add(i[1])
            else:
                point_x[i[0]] = set([i[1]])
            if point_y.get(i[1]):
                point_y[i[1]].add(i[0])
            else:
                point_y[i[1]] = set([i[0]])
        for i in point_x:
            if len(point_x[i]) < 2:
                continue
            for y1, y2 in combinations(point_x[i], 2):
                inter = sorted(point_y[y1].intersection(point_y[y2]))
                if len(inter) >= 2:
                    result = min(min([inter[j] - inter[j - 1] for j in range(1, len(inter))]) * abs(y1 - y2), result)

        if result == float('inf'):
            result = 0
        return result

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        coord = {(x, y) for x, y in points}
        n = len(points)
        min_area = sys.maxsize
        for i in range(n):
            for j in range(i+1, n):
                x1, y1 = points[i]
                x2, y2 = points[j]
                if x1 == x2 or y1 == y2: # Not diagonal of rectangle. It's just straight line so rect area would be 0 then
                    continue
                if (x1, y2) in coord and (x2, y1) in coord:
                    min_area = min(min_area, abs((x1-x2)*(y1-y2)))
                    # print(x1, x2, y1, y2, min_area)
                
        return min_area if min_area!=sys.maxsize else 0
    
#     math.sqrt((y1-y2)**2 + (x1-x2)**2), min_area


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        s = set()
        for a,b in points:
            s.add((a,b))
        n = len(points)
        res = float('inf')
        for i in range(n):
            for j in range(i+1,n):
                
                a,b = points[i]
                c,d = points[j]
                if a!=c and b!=d:
                    if (a,d) in s and (c,b) in s: 
                        res = min(res, abs((a-c)*(b-d)))
        return res if res < float('inf') else 0


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        counter = Counter(list(map(tuple, points)))
        if any(count >= 4 for count in list(counter.values())):
            return 0
        points = list(counter)
        result = float('inf')
        for i, (x, y) in enumerate(points):
            for j in range(i + 1, len(points)):
                x_, y_ = points[j]
                if x != x_ and y != y_:
                    if (x, y_) in counter and (x_, y) in counter:
                        result = min(result, abs((x_ - x) * (y_ - y)))
                        if not result:
                            break
        return 0 if result == float('inf') else result


from collections import defaultdict
class Solution:
    def minAreaRect(self, points):
        X = list(zip(*points))
        if len(set(X[0])) == 1 or len(set(X[1])) == 1: return 0
        points.sort()
        d, ans = defaultdict(set), float('inf')
        for r, c in points:
            d[r].add(c)

        A = list(d.keys())
        for i, r1 in enumerate(A):
            cols1 = d[r1]
            for r2 in A[i+1:]:
                cols2 = d[r2]
                s = sorted(cols1 & cols2)
                for c1, c2 in zip(s[:-1], s[1:]):
                    area = ((r1 - r2) * (c1 - c2))
                    if area < ans: ans = area
        return ans if ans < float('inf') else 0

# from collections import defaultdict
# class Solution:
#     def minAreaRect(self, points):
#         d = defaultdict(set)
#         rset, cset, N, ans = set(), set(), len(points), float('inf')
#         for r, c in points:
#             rset.add(r)
#             cset.add(c)
#         Nr, Nc = len(rset), len(cset)
#         if Nr == N or Nc == N:
#             return 0
#         elif Nr < Nc:
#             for r, c in points:
#                 d[r].add(c)
#         else:
#             for r, c in points:
#                 d[c].add(r)
# 
#         A = sorted(d.keys())
#         for i, r1 in enumerate(A):
#             cols1 = d[r1]
#             for r2 in A[i+1:]:
#                 cols2 = d[r2]
#                 s = sorted(cols1 & cols2)
#                 for c1, c2 in zip(s[:-1], s[1:]):
#                     area = abs((r1 - r2) * (c1 - c2))
#                     ans = min(ans, area)
#         return ans if ans < float('inf') else 0


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        '''
        Count by diagonal - approach 2 in solution
        
        https://leetcode.com/problems/minimum-area-rectangle/discuss/240341/Python-O(n2)-easy-to-understand.-Good-for-beginners
        '''
        
        if not points:
            return 0
        
        points_table = set()
        for x, y in points:
            points_table.add((x,y))
        
        min_area = float('inf')
        for i in range(len(points) - 1):
            for j in range(i+1, len(points)):
                x1, y1 = points[i]
                x2, y2 = points[j]
                '''
                check for diagonal only
                the points coul;d be on same vertical line, we need to ignore those
                '''
                if x1 != x2 and y1 != y2:
                    if (x1, y2) in points_table and (x2, y1) in points_table:
                        min_area = min(min_area, abs(x1 - x2) * abs(y1 - y2))
        
        return 0 if min_area == float('inf') else min_area


from collections import defaultdict
import math
class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        d = dict()
        for x, y in points:
            if x not in d:
                d[x] = set()
            d[x].add(y)
            
        ans = float(\"inf\")
        
        for i in range(len(points)):
            for j in range(i + 1, len(points)):
                x1, y1 = points[i]
                x2, y2 = points[j]
                if x1 == x2 or y1 == y2:
                    continue
                if y2 in d[x1] and y1 in d[x2]:
                    ans = min(ans, abs((x2 - x1)) * abs((y2 - y1)))
        return 0 if ans == float(\"inf\") else ans
                    
                
                    
            
        

MAX_VAL = 10 ** 9 + 7
class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        points_set = set()
        for x,y in points:
            points_set.add((x,y))
        
        n = len(points)
        min_area = MAX_VAL
        
        for i in range(n-1):
            for j in range(i +1, n):
                x1, y1 = points[i]
                x2, y2 = points[j]
                
                if x1 != x2 and y1 != y2 and (x2, y1) in points_set and (x1, y2) in points_set:
                    curr_area = abs((x1 - x2) * (y1 - y2))
                    min_area = min(min_area, curr_area)
        
        return 0 if min_area == MAX_VAL else min_area

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        
        pointset = set([(x,y) for x,y in points])
        minArea = math.inf
        
        for i in range(len(points)):
            for j in range(i+1, len(points)):
                x1, y1 = points[i]
                x2, y2 = points[j]
                if x1 != x2 and y1!=y2 and (x1, y2) in pointset and (x2, y1) in pointset:
                    area = abs((x1-x2)*(y1-y2))
                    minArea = min(area, minArea)
        
        return minArea if minArea < math.inf else 0
        
        
        # x     x    x
        #    x       x
        # x     x
#         x          x


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        import math
        from collections import defaultdict
        row_m = defaultdict(set)
        rows = set()
        for x, y in points:
            row_m[x].add(y)
            rows.add(x)
        
        rows = sorted(rows)
        l = len(rows)
        sol = math.inf
        # print(rows)
        for i in range(l):
            x1 = rows[i]
            for j in range(i + 1, l):                
                x2 = rows[j]
                
                # print(\"x1\", x1, \"x2\", x2)
                row1 = row_m[x1]
                row2 = row_m[x2]
                
                intersect = sorted(set(row1) & set(row2))
                
                if len(intersect) < 1:
                    continue
                
                smallest = math.inf
                for i, point in enumerate(intersect[1:], start=1):
                    # print(\"point\", point, i, intersect[i - 1])
                    smallest = min(
                        smallest,
                        point - intersect[i - 1]
                    )
                # print(\"smallest\", smallest, intersect)
                sol = min(
                    sol,
                    smallest * (x2 - x1),
                )
                
        if sol is math.inf:
            return 0
        return sol
                
            


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        n = len(points)
        nx = len(set(x for x, y in points))
        ny = len(set(y for x, y in points))
        if nx == n or ny == n:
            return 0
        res = 2**31
        s = {(p[0], p[1]) for p in points}
        
        for i, p1 in enumerate(points):
            for j, p2 in enumerate(points[i + 1:]):
                if p1[0] != p2[0] and p1[1] != p2[1]:
                    if (p1[0], p2[1]) in s and (p2[0], p1[1]) in s:
                        res = min(res, abs(p1[1] - p2[1]) * abs(p1[0] - p2[0]))
        res = res if res < 2**31 else 0
        return res


#         p = collections.defaultdict(list)
#         if nx > ny:
#             for x, y in points:
#                 p[x].append(y)
#         else:
#             for x, y in points:
#                 p[y].append(x)

#         lastx = {}
#         res = float('inf')
#         for x in sorted(p):
#             p[x].sort()
#             for i in range(len(p[x])):
#                 for j in range(i):
#                     y1, y2 = p[x][j], p[x][i]
#                     if (y1, y2) in lastx:
#                         res = min(res, (x - lastx[y1, y2]) * abs(y2 - y1))
#                     lastx[y1, y2] = x
#         return res if res < float('inf') else 0
                
                


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        rows = defaultdict(set)
        cols = defaultdict(set)
        for i,point in enumerate(points):
            rows[point[0]].add(tuple(point))
            cols[point[1]].add(tuple(point))
            
        minArea = 99999999999
        for point in points:
            rowPoints = rows[point[0]]
            colPoints = cols[point[1]]
            for iP in rowPoints:
                for jP in colPoints:
                    # print(point,iP,jP, (jP[0],iP[1]))
                    if iP[1] > point[1] and jP[0] > point[0] and (jP[0],iP[1]) in rows[jP[0]]:
                        minArea = min(minArea,(iP[1]-point[1])*(jP[0]-point[0]))
                        # print(minArea)
                        
        if minArea == 99999999999:
            return 0
        
        return minArea

from collections import defaultdict
class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        pts = set([(x,y) for x, y in points])
        min_area = float(\"inf\")
        for i in range(len(points)-1):
            for j in range(i+1, len(points)):
                x1, y1 = points[i]
                x2, y2 = points[j]
                if x1 != x2 and y1 != y2 and (x1, y2) in pts and (x2, y1) in pts:
                    min_area = min(abs(x1-x2)*abs(y1-y2), min_area)
        return min_area if min_area != float(\"inf\") else 0
                
            
        

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        point_set = set()
        area = 0
        for i1, j1 in points:
            for i2, j2 in points:
                if i1 != i2 and j1 != j2:
                    if (i1, j2) in point_set and (i2, j1) in point_set:
                        if area == 0:
                            area = abs(i1 - i2) * abs(j1 - j2)
                        else:
                            area = min(area, abs(i1 - i2) * abs(j1 - j2))
            point_set.add((i1, j1))    
        return area


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        n = len(points)
        point_set = {(x, y) for x, y in points}
        min_area = float(\"inf\")
        
        for i in range(n-1):
            for j in range(i+1, n):
                x0, y0 = points[i]
                x1, y1 = points[j]
                
                if x0 != x1 and y0 != y1:
                    if (x0, y1) in point_set and (x1, y0) in point_set:
                        area = abs((x1-x0)*(y1-y0))
                        min_area = min(area, min_area)
                    
        return min_area if min_area < float(\"inf\") else 0

# time: O(n^2)
# space: O(n)

from collections import defaultdict
from bisect import insort
class Solution:
    def minAreaRect(self, points):
        setr, setc = set(), set()
        for r, c in points:
            setr.add(r)
            setc.add(c)
        if len(points) in (len(setr), len(setc)): return 0
        
        points.sort()
        columns = defaultdict(list)
        for r, c in points:
            insort(columns[r], c)
        ans, lastc = float('inf'), dict()
        for r, cols in list(columns.items()):
            for j, y2 in enumerate(cols):
                for i, y1 in enumerate(cols[:j]):
                    if (y1, y2) in lastc:
                        ans = min(ans, (r - lastc[(y1, y2)]) * (y2 - y1))
                    lastc[(y1, y2)] = r
        return ans if ans < float('inf') else 0


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        d = collections.defaultdict(set)
        for i in range(len(points)):
            x0, y0 = points[i]
            for j in range(i+1, len(points)):
                x1, y1 = points[j]
                if x0 == x1:
                    d[x0].add(tuple(sorted([y0,y1]))) # sorted ys since later we will use it to match
        seen = set()
        minA = float('inf')
        for x0, lines0 in d.items(): 
            seen.add(x0)
            for x1, lines1 in d.items():
                if x1 not in seen:
                    for l in lines0:
                        if l in lines1: # matching ys, that's why we sort it earlier
                            minA = min(minA, abs(x0-x1)*(l[1]-l[0]))
        return minA if minA<float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        '''
        Count by diagonal - approach 2 in solution
        
        https://leetcode.com/problems/minimum-area-rectangle/discuss/240341/Python-O(n2)-easy-to-understand.-Good-for-beginners
        '''
        
        if not points:
            return 0
        
        points_table = set()
        for x, y in points:
            points_table.add((x,y))
        
        min_area = float('inf')
        for i in range(len(points) - 1):
            for j in range(i+1, len(points)):
                x1, y1 = points[i]
                x2, y2 = points[j]
                '''
                check for diagonal only
                '''
                if x1 != x2 and y1 != y2:
                    if (x1, y2) in points_table and (x2, y1) in points_table:
                        min_area = min(min_area, abs(x1 - x2) * abs(y1 - y2))
        
        return 0 if min_area == float('inf') else min_area


\"\"\"
总体思路是，enum所有点pair，把他们单做diagnal上的两个点，这样我们只要在points中找有没有另外两个点就好了。
注意这里我们直接把拿出来的点当做左上和右下，为什么可以这么做？因为我们要穷举所有的点pair，所以肯定能找到一个这样的点pair
\"\"\"
class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        hashset = set()
        # for lookup
        for p in points:
            hashset.add((p[0], p[1]))
        
        ans = sys.maxsize
        # we enumerate all diagnal points
        for i in range(len(points)-1):
            for j in range(i+1, len(points)):
                x1, y1 = points[i]
                x2, y2 = points[j]
                if x1 == x2 or y1 == y2: # we need diagnal points
                    continue
                if (x1, y2) in hashset and (x2, y1) in hashset:
                    ans = min(ans, abs((x2 - x1) * (y1 - y2)))
        return 0 if ans == sys.maxsize else ans            
                
                
        

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        import math
        from collections import defaultdict
        row_m = defaultdict(set)
        rows = set()
        for x, y in points:
            row_m[x].add(y)
            rows.add(x)
        
        rows = sorted(rows)
        l = len(rows)
        sol = math.inf
        print(rows)
        for i in range(l):
            x1 = rows[i]
            for j in range(i + 1, l):                
                x2 = rows[j]
                
                # print(\"x1\", x1, \"x2\", x2)
                row1 = row_m[x1]
                row2 = row_m[x2]
                
                intersect = sorted(set(row1) & set(row2))
                
                if len(intersect) < 1:
                    continue
                
                smallest = math.inf
                for i, point in enumerate(intersect[1:], start=1):
                    # print(\"point\", point, i, intersect[i - 1])
                    smallest = min(
                        smallest,
                        point - intersect[i - 1]
                    )
                # print(\"smallest\", smallest, intersect)
                sol = min(
                    sol,
                    smallest * (x2 - x1),
                )
                
        if sol is math.inf:
            return 0
        return sol
                
            


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        data = set()
        res = 2 ** 31 -1
        
        for x1, y1 in points:
            for x2, y2 in data:
                if (x1, y2) in data and (x2, y1) in data:
                    res = min(res, abs(x1 - x2) * abs(y1 - y2))
            data.add((x1, y1))
        return res if res < 2 ** 31 -1 else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        seen = set()
        res = float('inf')
        for point in points:                                                    
            x, y = point[0], point[1]            
            for _x, _y in seen:
                if (x, _y) in seen and (_x, y) in seen:
                    res = min(res, abs(x - _x) * abs(y - _y))
            seen.add((x, y))
            
        if res == float('inf'):
            return 0
        return res
                

            
            
            
        
        
            
            


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        xAxis = {}
        yAxis = {}
        minArea = None
        
        for point in points:
            if xAxis.get(point[0]):
                xAxis[point[0]].append(point[1])
            else:
                xAxis[point[0]] = [point[1]]
                
            if yAxis.get(point[1]):
                yAxis[point[1]].append(point[0])
            else:
                yAxis[point[1]] = [point[0]]
                
        # Treat every point as bottom left corner then try to find valid points
        for point in points:
            topLeft = xAxis[point[0]]
            bottomRight = yAxis[point[1]]
            
            for tlPoint in topLeft:
                if tlPoint == point[1]:
                    continue
                
                possibleTopRight = yAxis[tlPoint]
                topRight = [value for value in possibleTopRight if value in bottomRight and value != point[0]]

                for trPoint in topRight:
                    area = abs((trPoint - point[0]) * (tlPoint - point[1]))

                    if area != 0 and (minArea == None or area < minArea):
                        minArea = area      
                        
            xAxis[point[0]].remove(point[1])
            yAxis[point[1]].remove(point[0])
            
        return 0 if minArea == None else minArea

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        seen = set()
        res = float('inf')
        for x1,y1 in points:
            for x2,y2 in seen:
                if (x1,y2) in seen and (x2,y1) in seen:
                   
                    area = abs(x1-x2)*abs(y2-y1)
                    #print(area)
                    if area>0 and area < res:
                        res = area
            seen.add((x1,y1))
        #print(seen)
        return res if res < float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        points.sort()
        xs = collections.defaultdict(list)
        ys = collections.defaultdict(list)
        for x, y in points:
            xs[x].append(y)
            ys[y].append(x)
        result = math.inf
        for x0, ylist in xs.items():
            for i in range(len(ylist)):
                y1 = ylist[i]
                xlist1 = ys[y1]
                m = len(xlist1)
                ii = bisect.bisect(xlist1, x0)
                if ii == m:
                    continue
                for j in range(i + 1, len(ylist)):
                    y2 = ylist[j]
                    xlist2 = ys[y2]
                    n = len(xlist2)
                    jj = bisect.bisect(xlist2, x0)
                    if jj == n:
                        continue
                    i1 = ii
                    while i1 < m and jj < n:
                        if xlist1[i1] < xlist2[jj]:
                            i1 += 1
                        elif xlist1[i1] > xlist2[jj]:
                            jj += 1
                        else:
                            result = min(result, (xlist1[i1] - x0) * (y2 - y1))
                            break
        return 0 if math.isinf(result) else  result

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        xys = set()
        for point in points:
            xys.add((point[0], point[1]))
            
        sq_min = float('inf')
        for i, pointi in enumerate(points):
            xi, yi = pointi[0], pointi[1]
            for j in range(i+1, len(points)):
                xj, yj = points[j][0], points[j][1]
                if xi != xj and yi != yj and (xi, yj) in xys and (xj, yi) in xys:
                    sq_min = min(sq_min, abs((xi-xj)*(yi-yj)))
        
        return sq_min if sq_min != float('inf') else 0

class Solution(object):
    def minAreaRect(self, points):
        # idea, use lookup set to store the point that has seen before. for (x1,y1) in points, for (x2,y2) in lookup, check whether (x1,y2) and (x2, y1) in lookup. Time O(n^2), Space O(n).
        res = float('inf') # initiate as inf
        lookup = set()
        for x1, y1 in points:
            for x2, y2 in lookup:
                if (x1, y2) in lookup and (x2, y1) in lookup: # if all in the lookup
                    res = min(res, abs(x1-x2) * abs(y1-y2)) # check the min area
            # add to lookup for the outer loop
            lookup.add((x1, y1))
        return res if res != float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        points.sort()
        point_set = set([tuple(point) for point in points])
        min_area = math.inf
        N = len(points)
        for i in range(N):
            start_point = points[i]
            for j in range(i+1,N):
                end_point = points[j]
                if end_point[0] > start_point[0] and end_point[1] > start_point[1]:
                    area = (end_point[0]-start_point[0])*(end_point[1]-start_point[1])
                    if area < min_area and \\
                       (start_point[0], end_point[1]) in point_set and \\
                       (end_point[0], start_point[1]) in point_set:
                        min_area = area
                
        return min_area if min_area != math.inf else 0
            
        

\"\"\"
solution 2: O(N^2)
我们choose two diagnol points to iterate - then check if other two diagonal points in p_set
\"\"\"
class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        points.sort(key = lambda x: (x[0], x[1]))
        p_set = set(map(tuple, points))     # 变成set, 这样查找更快

        # 注意我们choose two diagnol points to iterate
        min_area = float(\"inf\")
        for i in range(len(points) - 1):
            for j in range(i + 1, len(points)):
                x1, y1 = points[i]
                x2, y2 = points[j]
                if x1 == x2 or y1 == y2:    # diagonal元素必须不能平行于x-axes or y-axes
                    continue
                x3, y3 = x2, y1
                x4, y4 = x1, y2
                if (x3, y3) in p_set and (x4, y4) in p_set:
                    min_area = min(min_area, abs(x1 - x2) * abs(y1 - y2))

        return 0 if min_area == float(\"inf\") else min_area

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        # group points by x coord, for each pari of points in column,
        # check for the smallest rectangle with this pair of points as the right edge
        # Time Coplexity: O(N^2) N is the number of points
        # columns = collections.defaultdict(list)
        # for x, y in points:
        #     columns[x].append(y)
        # last_x = {}
        # res = float('inf')
        # for x in sorted(columns):
        #     column = columns[x]
        #     column.sort()
        #     for j, y2 in enumerate(column):
        #         for i in range(j):
        #             y1 = column[i]
        #             if (y1, y2) in last_x:
        #                 res = min(res, (y2 - y1) * (x - last_x[(y1, y2)]))
        #             last_x[(y1, y2)] = x
        # return res if res < float('inf') else 0
        
        # assume each pair of points form the diagnal of the rectangle, check if other points
        # exist in the set, if so, we have a candidate
        # Time Coplexity: O(N^2)
        s = set([(x, y) for x, y in points])
        res = float('inf')
        for i, point1 in enumerate(points):
            for j in range(i):
                point2 = points[j]
                x1, y1 = point1
                x2, y2 = point2
                # two point can't form diagnal if they are on the same line
                if x1 != x2 and y1 != y2 and (x1, y2) in s and (x2, y1) in s:
                    res = min(res, abs(x1 - x2) * abs(y1 - y2))
        return res if res < float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        pointsSet = set([(x, y) for x, y in points])
        points = list(pointsSet)
        numberOfPoints = len(points)
        minimumArea = float('inf')
        for i in range(numberOfPoints):
            for j in range(i + 1, numberOfPoints):
                x1, y1 = points[i]
                x2, y2 = points[j]
                if x1 != x2 and y1 != y2 and (x1, y2) in pointsSet and (x2, y1) in pointsSet:
                    minimumArea = min(minimumArea, abs(x2 - x1) * abs(y2 - y1))
        return 0 if minimumArea == float('inf') else minimumArea 

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        
        pointSet = set()
        
        for dx, dy in points:
            pointSet.add((dx, dy))
        minArea = float('inf')
        for i in range(len(points)):
            x1, y1 = points[i][0], points[i][1]

            for j in range(i + 1, len(points)):
                x2, y2 = points[j][0], points[j][1]
                if x1 != x2 and y1 != y2:
                    if (x2, y1) in  pointSet and (x1, y2) in pointSet:
                        
                        minArea = min(minArea,  abs(x2- x1) * abs(y2 - y1))

        
        return minArea if minArea < float('inf') else 0
                


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        y_in_x = defaultdict(list)
        x_in_vline = defaultdict(list)
        ans = float(\"inf\")
        for x,y in points:
            if len(y_in_x[x]) >= 1 :
                for pre_y in y_in_x[x]:
                    a,b = max(pre_y,y) , min(pre_y,y)
                    if len(x_in_vline[(a,b)]) >= 1:
                        for pre_x in x_in_vline[(a,b)]:
                            ans = min(ans,(a-b)*abs(x-pre_x))
                    x_in_vline[(a,b)].append(x)
            y_in_x[x].append(y)
        
        #print(y_in_x)
        #print(x_in_vline)
        return ans if ans < float(\"inf\") else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        res = float('inf')
        pointSet = set([tuple(p) for p in points])
        for i in range(len(points)):
            for j in range(i+1, len(points)):
                x1, y1 = points[i]
                x2, y2 = points[j]
                if x1 == x2 or y1 == y2:
                    continue
                if (x1,y2) in pointSet and (x2,y1) in pointSet:
                    res = min(res, abs(x1-x2)*abs(y1-y2))
        if res == float('inf'):
            return 0
        return res

                        


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        # store y coordinates at each x
        # for each y1 y2 pairs at each x from left to right
        # find the last x that has y1 and y2, and calculate area
        n = len(points)
        nx = len(set(x for x, y in points))
        ny = len(set(y for x, y in points))
        if nx == n or ny == n:
            return 0
        lastx = {}
        everyx = collections.defaultdict(list)
        min_area = math.inf
        for x, y in points:
            everyx[x].append(y)
        for x in sorted(everyx):
            everyy = sorted(everyx[x])
            for i, y1 in enumerate(everyy):
                for j in range(i):
                    y2 = everyy[j]
                    if (y1, y2) in lastx:
                        area = abs(y2-y1) * abs(x - lastx[y1, y2])
                        min_area = min(area, min_area)
                    lastx[y1,y2] = x
        return min_area if min_area < math.inf else 0


\"\"\"
solution 2: O(N^2)
我们choose two diagnol points to iterate - then check if other two diagonal points in p_set
\"\"\"
class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        p_set = set(map(tuple, points))     # 变成set, 这样查找更快

        # 注意我们choose two diagnol points to iterate
        min_area = float(\"inf\")
        for i in range(len(points) - 1):
            for j in range(i + 1, len(points)):
                x1, y1 = points[i]
                x2, y2 = points[j]
                if x1 == x2 or y1 == y2:    # diagonal元素必须不能平行于x-axes or y-axes
                    continue
                x3, y3 = x2, y1
                x4, y4 = x1, y2
                if (x3, y3) in p_set and (x4, y4) in p_set:
                    min_area = min(min_area, abs(x1 - x2) * abs(y1 - y2))

        return 0 if min_area == float(\"inf\") else min_area

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        if not points or len(points) <= 3:
            return 0
        visited = set()
        res = float('Inf')
        for x1, y1 in points:
            for x2, y2 in visited:
                if (x1, y2) in visited and (x2, y1) in visited:
                    area = abs(x1-x2)*abs(y1-y2)
                    if area and area <= res:
                        res = area
            visited.add((x1, y1))
        return res if res != float('Inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        
        max_area = float(\"inf\")
        seen = set()
        for x1, y1 in points:
            for x2, y2 in points:
                if (x1, y2) in seen and (x2, y1) in seen:
                    area = abs(x2 - x1) * abs(y2 - y1)
                    if area != 0:
                        max_area = min(max_area, area)
            seen.add((x1, y1))
        return max_area if max_area < float(\"inf\") else 0 

from collections import defaultdict


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        # (1,1), (1,3), (3, 1), (3,3)
        # (1,1), (1,3), (3,1), (3,3) X (3,1), (3,3), (4,1), (4,3)
        
        point_set = set()
        for x, y in points:
            point_set.add((x, y))
            
        min_area = float('inf')
        for i, (x1, y1) in enumerate(points):
            for (x2, y2) in points[i+1:]:
                if x1 == x2 or y1 == y2:
                    continue

                if (x1, y2) in point_set and (x2, y1) in point_set:
                    min_area = min(min_area, abs((x1-x2) * (y1-y2)))
            
        return min_area if min_area != float('inf') else 0
            
        #x1, y1    x2, y1
        #x1, y2    x2, y2
        
        #3, 3      
        #3,4       4, 4


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        
        point_dict = {}
        point_list = []
        
        for i in points:
            point_dict[(i[0],i[1])] = 1
            point_list.append((i[0],i[1]))
            
        print(point_dict)
        print(point_list)
        
        ans = math.inf
        for i in range(0,len(points)):
            for j in range(i,len(points)):
                ix, iy = point_list[i]
                jx, jy = point_list[j]
                if ix==jx or iy==jy:
                    continue
                if ((ix,jy) in point_dict) and ((jx,iy) in point_dict):
                    ans = min(ans,abs(ix-jx)*abs(iy-jy))

        if ans == math.inf:
            return 0
        return ans

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        points_set = set(map(tuple, points))
        result = float(\"inf\")
        for j, p2 in enumerate(points):
            for i in range(j):
                p1 = points[i]
                if p2[0] != p1[0] and p2[1] != p1[1] and (p1[0], p2[1]) in points_set and (p2[0], p1[1]) in points_set:
                    result = min(result, abs((p2[0] - p1[0]) * (p2[1] - p1[1])))
        return result if result != float(\"inf\") else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        y_in_x = defaultdict(list)
        x_in_vline = defaultdict(list)
        ans = float(\"inf\")
        for x,y in points:
            if len(y_in_x[x]) >= 1 :
                for pre_y in y_in_x[x]:
                    a,b = max(pre_y,y) , min(pre_y,y)
                    if len(x_in_vline[(a,b)]) >= 1:
                        for pre_x in x_in_vline[(a,b)]:
                            ans = min(ans,(a-b)*abs(x-pre_x))
                    x_in_vline[(a,b)].append(x)
            y_in_x[x].append(y)
        

        return ans if ans < float(\"inf\") else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        
        # hash set
        points = { (x, y) for x, y in points}
        min_area = float('inf')
        
        for x1, y1 in points:
            for x2, y2 in points:
                if x1 == x2 or y1 == y2:
                    continue
                x3, y3, x4, y4 = x1, y2, x2, y1
                
                theoretical_area = abs((x1 - x2) * (y1 - y2))
                if theoretical_area > min_area:
                    # Don't lookup
                    continue
                
                if (x3, y3) in points and (x4, y4) in points:
                    min_area = min(min_area, abs((x1 - x2) * (y1 - y2)))
                        
        if min_area == float('inf'):
            return 0
        return min_area

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        coords = {}
        
        for x, y in points:
            if x not in coords:
                coords[x] = {}
                 
            coords[x][y] = True
        
        area = None
        for x1 in coords:
            for y1 in coords[x1]:
                for y2 in coords[x1]:
                    if y1 == y2:
                        continue
                           
                    for x2 in coords:
                        if x1 == x2:
                            continue
                            
                        if y1 in coords[x2] and y2 in coords[x2]:
                            current = abs(x1 - x2) * abs(y1 - y2)
                            
                            if area == None or current < area:
                                area = current
                                
        if area == None:
            return 0
        
        return area
                        
                    
                        


import math
import itertools
import collections

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        x_index = defaultdict(set)
        y_index = defaultdict(set)
        
        for x,y in points:
            x_index[x].add(y)
            y_index[y].add(x)
        
        rects = []
        
        for p1,p2 in itertools.combinations(points, 2):
            x1,y1 = p1
            x2,y2 = p2
            
            if x1 == x2 or y1 == y2:
                continue
                
            if y1 in x_index[x2] and x1 in y_index[y2]:
                rects.append([p1,p2])
        
        if len(rects) == 0:
            return 0
        
        ret = math.inf
        for ((x1, y1), (x2, y2)) in rects:
            area = abs((x1 - x2) * (y1 - y2))
            if area < ret:
                ret = area
        
        return ret
            


from collections import defaultdict
class Solution:
    def minAreaRect(self, points):
        X = list(zip(*points))
        if len(set(X[0])) == 1 or len(set(X[1])) == 1: return 0
        
        d, ans = defaultdict(set), float('inf')
        for r, c in points:
            d[r].add(c)

        A = list(d.keys())
        for i, r1 in enumerate(A):
            cols1 = d[r1]
            for r2 in A[i+1:]:
                cols2 = d[r2]
                s = sorted(cols1 & cols2)
                for c1, c2 in zip(s[:-1], s[1:]):
                    area = abs((r1 - r2) * (c1 - c2))
                    ans = min(ans, area)
        return ans if ans < float('inf') else 0


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        min_area = float(\"inf\")
        points_table = set((x,y) for x, y in points)
        
        for (x1, y1) in points:
            for (x2, y2) in points:
                if x1 > x2 and y1 > y2:
                    if (x1, y2) in points_table and (x2, y1) in points_table:
                        area = abs(x2-x1) * abs(y2-y1)
                        if area:
                            min_area = min(min_area, area)
        return min_area if min_area != float(\"inf\") else 0
                

        
        
        

\"\"\"
solution 2: O(N^2)
我们choose two diagnol points to iterate - then check if other two diagonal elements in p_set
\"\"\"
class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        # points.sort(key = lambda x: (x[0], x[1]))
        p_set = set(map(tuple, points))

        # 注意我们choose two diagnol points to iterate
        min_area = float(\"inf\")
        for i in range(len(points) - 1):
            for j in range(i + 1, len(points)):
                x1, y1 = points[i]
                x2, y2 = points[j]
                if x1 == x2 or y1 == y2:    # diagonal元素必须不能平行于x-axes or y-axes
                    continue
                x3, y3 = x2, y1
                x4, y4 = x1, y2
                if (x3, y3) in p_set and (x4, y4) in p_set:
                    min_area = min(min_area, abs(x1 - x2) * abs(y1 - y2))

        return 0 if min_area == float(\"inf\") else min_area

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        # you need 4 points to make a rectangle
        # first dedupe and add elements to a set
        # for every x,y - iterate the remaining elements, idenitfy the new x,y is greater than current x,y
        # also check if there is x1,y2 and x2,y1 is present in the set:
        # then find the area, and process the minimum area
        
        pointset = set()
        
        for x, y in points:
            pointset.add((x,y))
        minarea = float('inf')    
        
        for x1, y1 in pointset:
            for x2, y2 in pointset:
                if x1>x2 and y1>y2 and (x1,y2) in pointset and (x2,y1) in pointset:
                    area = abs(x1-x2) * abs(y1-y2)
                    minarea = min(minarea, area)
                    
                    
        if minarea == float('inf'):
            return 0
        else:
            return minarea
            
                        
                        
            
                        
                        
                    
                    
        
        
        
        


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        S = set(map(tuple, points))
        ans = float('inf')
        for j, p2 in enumerate(points):
            for i in range(j):
                p1 = points[i]
                if (p1[0] != p2[0] and p1[1] != p2[1] and
                        (p1[0], p2[1]) in S and (p2[0], p1[1]) in S):
                    ans = min(ans, abs(p2[0] - p1[0]) * abs(p2[1] - p1[1]))
        return ans if ans < float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        n = len(points)
        min_area = float(\"inf\")
        point_set = {(x, y) for x, y in points}
        
        for i in range(n-1):
            for j in range(i+1, n):
                x1, y1 = points[i]
                x2, y2 = points[j]
                
                if x1 != x2 and y1 != y2 and (x1, y2) in point_set and (x2, y1) in point_set:
                    min_area = min(min_area, abs((y2-y1)*(x2-x1)))
                    
        return min_area if min_area < float(\"inf\") else 0
                

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        pointsSet = set()
        
        for x, y in points:
            pointsSet.add((x, y))
        
        minArea = float(\"inf\")
        for idx, point in enumerate(points):
            x1 = point[0]
            y1 = point[1]
            for x2, y2 in points[idx:]:
                if x1 != x2 and y1 != y2:
                    if (x1, y2) in pointsSet and (x2, y1) in pointsSet:
                        curtArea = abs(x1 - x2) * abs(y1 - y2)
                        minArea = min(curtArea, minArea)
        if minArea == float(\"inf\"):
            return 0
        return minArea
            

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        
        point_set = set([tuple(p) for p in points])
        
        min_area = float('inf')
        
        for idx, p1 in enumerate(points):
            for idx2 in range(idx, len(points)):
                p2 = points[idx2]
                
                if p1[0] != p2[0] and p1[1] != p2[1]:
                    # search for other points
                    p3 = (p1[0], p2[1])
                    p4 = (p2[0], p1[1])
                    
                    if p3 in point_set and p4 in point_set:
                        # calc area
                        dx = abs(p1[0] - p2[0])
                        dy = abs(p1[1] - p2[1])
                        area = dx * dy
                        if area < min_area:
                            min_area = area
        return min_area if min_area != float('inf') else 0
                
            


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        min_area = 999999999999999999
        points = [(l[0], l[1]) for l in points]
        s_points = set(points)
        for p1 in points:
            for p2 in points:
                x1, y1 = p1
                x2, y2 = p2
                if x1 == x2 or y1 == y2:
                    continue
                area = abs(x2 - x1) * abs(y2 - y1)
                if area < min_area:
                    if (x1, y2) in s_points and (x2, y1) in s_points:
                            min_area = area
        if min_area is 999999999999999999:
            return 0
        return min_area


# store column values for every x in a dictionary, find diagonal points
# time - O(n*n), space - O(n)


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        
        columns = defaultdict(set)
        minArea = float(\"inf\")
        
        for pt in points:
            columns[pt[0]].add(pt[1])

        for i,pt1 in enumerate(points):
            for j,pt2 in enumerate(points[i:]):
                if pt1[0]==pt2[0] or pt1[1]==pt2[1]:
                    continue
                
                # if y point of diagonal in column[x of point1] and y of point1 in column[x point of diagonal]
                if pt2[1] in columns[pt1[0]] and pt1[1] in columns[pt2[0]]:
                    
                    curArea = abs(pt2[1]-pt1[1])*abs(pt2[0]-pt1[0])
                    minArea = min(minArea, curArea)
                    
               
        
        return minArea if minArea!=float(\"inf\") else 0 

       
            

from collections import defaultdict
class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        p_set = set(map(tuple, points))
        
        res = float('inf')
        for j, p2 in enumerate(points):
            for i in range(j):
                p1 = points[i]

                if p1[0] != p2[0] and p1[1] != p2[1] and (p1[0], p2[1]) in p_set and (p2[0], p1[1]) in p_set:
                    area = abs(p1[0]-p2[0]) * abs(p1[1]-p2[1])
                    res = min(area, res)

        return 0 if res == float('inf') else res


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        from collections import defaultdict
        
        x_set, y_set = set(), set()
        for x, y in points:
            x_set.add(x)
            y_set.add(y)
        
        if len(x_set) == len(points) or len(y_set) == len(points):
            return 0
        
        # using a hash map to record {x:[y]}
        columns = defaultdict(list)
        for x, y in points:
            columns[x].append(y)
        
        visited = {}
        res = float('inf')
        
        # sort hash map, for every 2 ys in the current column, calculate area
        for col in sorted(columns):
            num_y = columns[col]
            num_y.sort()
            
            for i in range(len(num_y)):
                for j in range(i):
                    y1 = num_y[i]
                    y2 = num_y[j]
                    
                    if (y1, y2) in visited:
                        col_prev = visited[(y1, y2)]
                        area = (y1 - y2) * (col - col_prev)
                        res = min(res, area)
                    
                    visited[(y1, y2)] = col
        
        return res if res != float('inf') else 0


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        pointset = {tuple(i) for i in points}
        ans = float('inf')
        area = lambda x,y: abs((x[0]-y[0])*(x[1]-y[1]))
        
        for A, B in combinations(points, 2):
            if A[0] == B[0] or A[1] == B[1]: continue
            if (A[1]-B[1])//(A[0]-B[0]) >= 0: continue

            C = (A[0], B[1])
            D = (B[0], A[1])

            if C in pointset and D in pointset:
                ans = min(ans, area(A, B))

        return ans if ans != float('inf') else 0
                        


import sys
class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        S = set([(x,y) for x,y in points])
        mini = sys.maxsize
        for i,p1 in enumerate(points):
            for j in range(i):
                p2 = points[j]
                if (p1[0]!=p2[0]) and (p1[1]!=p2[1]) and (p1[0],p2[1]) in S and (p2[0], p1[1]) in S:
                    mini = min(mini, abs((p1[1]-p2[1])*(p1[0]-p2[0])))
        return mini if mini != sys.maxsize else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        if len(points) < 4:
            return 0
        ans = float('inf')

        s = set(map(tuple, points))
        for i, p2 in enumerate(points):
            for j in range(i):
                p1 = points[j]
                if (p2[0] != p1[0]) and (p2[1] != p1[1]) and ((p1[0], p2[1]) in s) and ((p2[0], p1[1]) in s):
                    ans = min(ans, (abs(p2[0] - p1[0]) * abs(p2[1] - p1[1])))
        return ans if ans < float('inf') else 0
                


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        points_set = set([(point[0], point[1]) for point in points])
        min_area = float(\"inf\")
        for i, point1 in enumerate(points):
            for j in range(i):
                point2 = points[j]
                if point1[0] != point2[0] and point1[1] != point2[1] and (point1[0], point2[1]) in points_set and (point2[0], point1[1]) in points_set:
                    min_area = min(min_area, abs(point2[0]-point1[0])*abs(point2[1]-point1[1]))
        return min_area if min_area < float('inf') else 0
                    
                
            
        

class Solution:
    def minAreaRectangularHull(self, points: List[List[int]]) -> int:
        area = 0
        if points:
            minx, miny = points[0]
            maxx, maxy = points[0]
            for x, y in points:
                minx = min(minx, x)
                miny = min(miny, y)
                maxx = max(maxx, x)
                maxy = max(maxy, y)
            area = (maxx - minx)*(maxy - miny)
        return area
                

    def minAreaRect(self, points):
        S = set(map(tuple, points))
        ans = float('inf')
        for j, p2 in enumerate(points):
            for i in range(j):
                p1 = points[i]
                if (p1[0] != p2[0] and p1[1] != p2[1] and
                        (p1[0], p2[1]) in S and (p2[0], p1[1]) in S):
                    ans = min(ans, abs(p2[0] - p1[0]) * abs(p2[1] - p1[1]))
        return ans if ans < float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        ref = set(map(tuple, points))
        minArea = float('inf')
        curArea = 0
        for i, p2 in enumerate(points):
            for j in range(i):
                p1 = points[j]

                if (p1[0] != p2[0] and p1[1] != p2[1]) and (p1[0], p2[1]) in ref and (p2[0], p1[1]) in ref:
                    curArea = abs(p2[0] - p1[0]) * abs(p2[1] - p1[1])
                    minArea = min(minArea, curArea)
        return minArea if minArea != float('inf') else 0



class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        
        S = set(map(tuple, points))
        ans = float('inf')
        for j, p2 in enumerate(points):
            for i in range(j):
                p1 = points[i]
                if (p1[0] != p2[0] and p1[1] != p2[1] and
                        (p1[0], p2[1]) in S and (p2[0], p1[1]) in S):
                    ans = min(ans, abs(p2[0] - p1[0]) * abs(p2[1] - p1[1]))
        return ans if ans < float('inf') else 0
        
        res = float('inf') # initiate as inf
        lookup = set()
        for x1, y1 in points:
            for x2, y2 in lookup:
                if (x1, y2) in lookup and (x2, y1) in lookup: # if all in the lookup
                    res = min(res, abs(x1-x2) * abs(y1-y2)) # check the min area
            # add to lookup for the outer loop
            lookup.add((x1, y1))
        return res if res != float('inf') else 0
    
    
        ys_with_same_x = defaultdict(list)
        for x, y in points:
            ys_with_same_x[x].append(y)
        
        pair_of_ys_history = {}
        res = float('inf')
        for x, ys in sorted(ys_with_same_x.items()):
            ys.sort()
            for i, y1 in enumerate(ys):
                for j in range(i):
                    y2 = ys[j]
                    if (y1, y2) in pair_of_ys_history:
                        res = min(res, (x - pair_of_ys_history[(y1,y2)]) * (y1-y2) )
                    pair_of_ys_history[(y1,y2)] = x
        if res == float('inf'):
            return 0
        else:
            return res


class Solution(object):
    def minAreaRect(self, points):
        S = set(map(tuple, points))
        ans = float('inf')
        for j, p2 in enumerate(points):
            for i in range(j):
                p1 = points[i]
                if (p1[0] != p2[0] and p1[1] != p2[1] and
                        (p1[0], p2[1]) in S and (p2[0], p1[1]) in S):
                    ans = min(ans, abs(p2[0] - p1[0]) * abs(p2[1] - p1[1]))
        return ans if ans < float('inf') else 0

from collections import defaultdict
class Solution:
    def minAreaRect(self, points):
        setr, setc = set(), set()
        for r, c in points:
            setr.add(r)
            setc.add(c)
        if len(points) in (len(setr), len(setc)): return 0
        
        
        columns = defaultdict(list)
        for r, c in points:
            columns[r].append(c)
        ans, lastc = float('inf'), dict()
        for r in sorted(columns):
            cols = sorted(columns[r])
            for j, y2 in enumerate(cols):
                for i, y1 in enumerate(cols[:j]):
                    if (y1, y2) in lastc:
                        ans = min(ans, (r - lastc[(y1, y2)]) * (y2 - y1))
                    lastc[(y1, y2)] = r
        return ans if ans < float('inf') else 0

class Solution:
    def minAreaRect(self, points):
        S = set(map(tuple, points))
        ans = float('inf')
        for j, p2 in enumerate(points):
            for i, p1 in enumerate(points[:j]):
                if (p1[0] != p2[0] and p1[1] != p2[1] and
                        (p1[0], p2[1]) in S and (p2[0], p1[1]) in S):
                    ans = min(ans, abs(p2[0] - p1[0]) * abs(p2[1] - p1[1]))
        return ans if ans < float('inf') else 0



class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        
        columns = defaultdict(set)
        minArea = float(\"inf\")
        
        for pt in points:
            columns[pt[0]].add(pt[1])

        for i,pt1 in enumerate(points):
            for j,pt2 in enumerate(points[i:]):
                if pt1[0]==pt2[0] or pt1[1]==pt2[1]:
                    continue
        
                if pt2[1] in columns[pt1[0]] and pt1[1] in columns[pt2[0]]:
                    
                    curArea = abs(pt2[1]-pt1[1])*abs(pt2[0]-pt1[0])
                    minArea = min(minArea, curArea)
                    
               
        
        return minArea if minArea!=float(\"inf\") else 0 

        # S = set(map(tuple, points))
        # ans = float('inf')
        # for j, p2 in enumerate(points):
        #     for i in range(j):
        #         p1 = points[i]
        #         if (p1[0] != p2[0] and p1[1] != p2[1] and
        #                 (p1[0], p2[1]) in S and (p2[0], p1[1]) in S):
        #             ans = min(ans, abs(p2[0] - p1[0]) * abs(p2[1] - p1[1]))
        # return ans if ans < float('inf') else 0
        
            

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        n = len(points)
        nx = len(set(x for x, y in points))
        ny = len(set(y for x, y in points))
        if nx == n or ny == n:
            return 0

        p = collections.defaultdict(list)
        if nx > ny:
            for a, b in points:
                p[a].append(b)
        else:
            for a, b in points:
                p[a].append(b)

        lastx = {}
        res = float('inf')
        for x in sorted(p):
            p[x].sort()
            for i in range(len(p[x])):
                for j in range(i):
                    y1, y2 = p[x][j], p[x][i]
                    if (y1, y2) in lastx:
                        res = min(res, (x - lastx[y1, y2]) * abs(y2 - y1))
                    lastx[y1, y2] = x
        return res if res < float('inf') else 0


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        n = len(points)
        if n == 1:
            return 0
        S = set(map(tuple, points))
        ans = float('inf')
        for j, p2 in enumerate(points):
            for i in range(j):
                p1 = points[i]
                if (p1[0] != p2[0] and p1[1] != p2[1] and
                        (p1[0], p2[1]) in S and (p2[0], p1[1]) in S):
                    ans = min(ans, abs(p2[0] - p1[0]) * abs(p2[1] - p1[1]))
        return ans if ans < float('inf') else 0


'''
We can group them in group of X's, using dictionary

So we loop through all Y's in xs and keep track of the X of last seen pair of Y. We can hash Y's and keep dictionary of pair. Each time we find pair that already exist, we document their solution. 

Complexity:
So if there are N points and each column has K points. So there are N/K columns. We loop through each column, for each column we loop through k points twice. So N/K * K * K so = Nk

O(Nk) but K could be N so O(N^2)
O(N)
'''
class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        xD = collections.defaultdict(list)
        for x, y in points:
            xD[x].append(y)
        
        sol = math.inf
        lastSeen = collections.defaultdict(int)
        for x in sorted(xD.keys()):
            for i1 in range(len(xD[x])):
                for i2 in range(i1 + 1, len(xD[x])):
                    
                    pair1,pair2, diff = (xD[x][i1], xD[x][i2]), (xD[x][i2], xD[x][i1]),abs(xD[x][i1]-xD[x][i2])
                    if pair1 in lastSeen or pair2 in lastSeen:
                        sol = min(diff * (x-lastSeen[pair1]), sol)
                    lastSeen[pair1], lastSeen[pair2] = x,x
        return sol if sol != math.inf else 0
                        
                        
                
                        


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        if len(points) < 4:
            return 0
        group_x_coords = collections.defaultdict(list)
        group_y_coords = collections.defaultdict(set)
        
        # Form the grouping
        for point in points:
            group_x_coords[point[0]].append(point[1])
            group_y_coords[point[1]].add(point[0])
        
        
        # Pick element from x coords
        # Then pick another element from it
        # Do the intersection on the group_y_coords 
        # Find the maximum in the intersection
        min_area = float('inf')
        for key in group_x_coords.keys():
            if len(group_x_coords[key]) < 2:
                continue
            for i in range(len(group_x_coords[key])):
                for j in range(i + 1, len(group_x_coords[key])):
                    y_val_1 = group_x_coords[key][i]
                    y_val_2 = group_x_coords[key][j]
                    # print(y_val)
                    # Get the max rectangle
                    common_x_coords = group_y_coords[y_val_1] & group_y_coords[y_val_2]
                    if len(common_x_coords) > 1:
                        y_diff = abs(y_val_1 - y_val_2)
                        # Need to find number closest to current
                        x_diff = min([abs(ele - key) for ele in common_x_coords if ele != key])
                        area = x_diff * y_diff
                        if area < min_area:
                            min_area = area
        if min_area == float('inf'):
            return 0
        return min_area

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        # you need 4 points to make a rectangle
        # first dedupe and add elements to a set
        # for every x,y - iterate the remaining elements, idenitfy the new x,y is greater than current x,y
        # also check if there is x1,y2 and x2,y1 is present in the set:
        # then find the area, and process the minimum area
        
        pointset = set()
        minarea = float('inf')
        
        for x, y in points:
            pointset.add((x,y))
            
        
        for x1,y1 in pointset:
            for x2, y2 in pointset:
                if x1>x2 and y1>y2 and (x1,y2) in pointset and (x2,y1) in pointset:
                    area = abs(x1-x2) * abs(y1-y2)
                    minarea = min(area, minarea)
            
        if minarea == float('inf'):
            return 0
        else:
            return minarea
                        
                        
            
                        
                        
                    
                    
        
        
        
        


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        visited = set()
        res = float(\"inf\")
        for x1, y1 in points:
            for x2, y2 in visited:
                if (x1, y2) in visited and (x2, y1) in visited:
                    res = min(res, abs(x2 - x1) * abs(y2 - y1))
                    
            visited.add((x1, y1))
        return res if res != float(\"inf\") else 0
                

'''
939. Minimum Area Rectangle.  Medium

Given a set of points in the xy-plane, 
determine the minimum area of a rectangle
formed from these points, with sides parallel
to the x and y axes.

If there isn't any rectangle, return 0.

Example 1:
Input: [[1,1],[1,3],[3,1],[3,3],[2,2]]
Output: 4

Example 2:
Input: [[1,1],[1,3],[3,1],[3,3],[4,1],[4,3]]
Output: 2

Note:
1 <= points.length <= 500
0 <= points[i][0] <= 40000
0 <= points[i][1] <= 40000
All points are distinct.

Accepted
55,377
Submissions
107,280

NOTE: Clarify: rectangle formed by ALL these points (convex hull)
            v. rectangle formed by ANY four distinct points
'''
class Solution:
    def minAreaRectangularHull(self, points: List[List[int]]) -> int:
        area = 0
        if points:
            minx, miny = points[0]
            maxx, maxy = points[0]
            for x, y in points:
                minx = min(minx, x)
                miny = min(miny, y)
                maxx = max(maxx, x)
                maxy = max(maxy, y)
            area = (maxx - minx)*(maxy - miny)
        return area
                

    def minAreaRect(self, points):
        S = set(map(tuple, points))
        ans = float('inf')
        for j, p2 in enumerate(points):
            for i in range(j):
                p1 = points[i]
                if (p1[0] != p2[0] and p1[1] != p2[1] and
                        (p1[0], p2[1]) in S and (p2[0], p1[1]) in S):
                    ans = min(ans, abs(p2[0] - p1[0]) * abs(p2[1] - p1[1]))
        return ans if ans < float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        res = 2**31
        s = {(p[0], p[1]) for p in points}
        
        for i, p1 in enumerate(points):
            for j, p2 in enumerate(points[i + 1:]):
                if p1[0] != p2[0] and p1[1] != p2[1]:
                    if (p1[0], p2[1]) in s and (p2[0], p1[1]) in s:
                        res = min(res, abs(p1[1] - p2[1]) * abs(p1[0] - p2[0]))
        res = res if res < 2**31 else 0
        return res
                
                


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        counter = Counter(list(map(tuple, points)))
        if any(count >= 4 for count in list(counter.values())):
            return 0
        points = list(counter)
        result = float('inf')
        for i, (x, y) in enumerate(points):
            for j in range(i + 1, len(points)):
                x_, y_ = points[j]
                if x != x_ and y != y_:
                    if (x, y_) in counter and (x_, y) in counter:
                        result = min(result, abs(x - x_) * abs(y - y_))
                        if not result:
                            break
        return 0 if result == float('inf') else result


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        point_set = set()
        points.sort()
        for point in points:
            point_set.add((point[0], point[1]))
        
        def getArea(point1: List[int], point2: List[int]) -> int:
            if point2[0] <= point1[0] or point2[1] <= point1[1]:
                return 0
            
            if (point1[0], point2[1]) in point_set and (point2[0], point1[1]) in point_set:
                return (point2[0]-point1[0])*(point2[1]-point1[1])
            return 0
        
        min_area = math.inf
        N = len(points)
        for i in range(N):
            start_point = points[i]
            for j in range(i+1,N):
                area = getArea(start_point, points[j])
                if area > 0:
                    min_area = min(min_area, area)
                
        return min_area if min_area != math.inf else 0
            


class Solution:
    def minAreaRect(self, points):
        pSet, ans = set(map(tuple, points)), float('inf')
        for i, (p1x, p1y) in enumerate(points):
            for (p2x, p2y) in points[i+1:]:
                if (p1x != p2x) and (p1y != p2y) and ((p1x, p2y) in pSet) and ((p2x, p1y) in pSet):
                    ans = min(ans, abs((p1x - p2x) * (p1y - p2y)))
        return ans if ans < float('inf') else 0
    
class Solution:
    def minAreaRect(self, points):
        X = list(zip(*points))
        if len(set(X[0])) == 1 or len(set(X[1])) == 1: return 0

        pSet, ans = set(map(tuple, points)), float('inf')
        for i, (p1x, p1y) in enumerate(points):
            for (p2x, p2y) in points[i+1:]:
                area = abs((p2x-p1x)*(p2y-p1y))
                if area > ans or area == 0:
                    continue
                if (p1x, p2y) in pSet and (p2x, p1y) in pSet:
                    ans = area
        return ans if ans < float('inf') else 0
    
from collections import defaultdict
class Solution:
    def minAreaRect(self, points):
        X = list(zip(*points))
        Nr, Nc = len(set(X[0])), len(set(X[1]))
        if Nr == 1 or Nc == 1: return 0

        points.sort()
        columns = defaultdict(list)
        for r, c in points:
            columns[r].append(c)
        ans, lastc = float('inf'), dict()
        for r, cols in list(columns.items()):
            for i, c1 in enumerate(cols):
                for c2 in cols[i+1:]:
                    if (c1, c2) in lastc:
                        area = (r - lastc[(c1, c2)]) * (c2 - c1)
                        #ans = min(ans, area)
                        if area < ans: ans = area
                    lastc[(c1, c2)] = r
        return ans if ans < float('inf') else 0
    
from collections import defaultdict
class Solution:
    def minAreaRect(self, points):
        X = list(zip(*points))
        Nr, Nc = len(set(X[0])), len(set(X[1]))
        if Nr == 1 or Nc == 1: return 0
        if Nr < Nc:
            points = [[c, r] for (r, c) in points]
            return self.minAreaRect(points)
    
        points.sort()
        columns = defaultdict(list)
        for r, c in points:
            columns[r].append(c)
        ans, lastc = float('inf'), dict()
        for r, cols in list(columns.items()):
            for i, c1 in enumerate(cols):
                for c2 in cols[i+1:]:
                    if (c1, c2) in lastc:
                        area = (r - lastc[(c1, c2)]) * (c2 - c1)
                        #ans = min(ans, area)
                        if area < ans: ans = area
                    lastc[(c1, c2)] = r
        return ans if ans < float('inf') else 0
    
    


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        horizontal = collections.defaultdict(set)
        vertical = collections.defaultdict(set)
        for x,y in points:
            horizontal[y].add(x)
            vertical[x].add(y)
        xlist = [x for x in vertical if len(vertical[x])>1]
        ans = float('inf')
        for i in range(len(xlist)-1):
            x1 = xlist[i]
            for j in range(i+1,len(xlist)):
                x2 = xlist[j]
                cand = []
                for y in  vertical[x1]:
                    if y in  vertical[x2]:
                        cand.append(y)
                if len(cand)>=2:
                    cand.sort()
                    for i in range(len(cand)-1):
                        ans = min(ans, abs(x2-x1)*(cand[i+1]-cand[i]) )
        return ans if ans < float('inf') else 0


class Solution:
#     def minAreaRect(self, points: List[List[int]]) -> int:
#         columns = collections.defaultdict(list)
#         for x, y in points:
#             columns[x].append(y)
#         lastx = {}
#         ans = float('inf')
        
#         for x in sorted(columns):
#             column = columns[x]
#             column.sort()
#             for index_j, y2 in enumerate(column):
#                 for index_i in range(index_j):
#                     y1 = column[index_i]
#                     if (y1, y2) in lastx:
#                         ans = min(ans, (x - lastx[(y1, y2)])* (y2 - y1))
#                     lastx[(y1, y2)] = x
#         return ans if ans < float('inf') else 0

    def minAreaRect(self, points):
        S = set(map(tuple, points))
        ans = float('inf')
        for index_j, p2 in enumerate(points):
            for index_i in range(index_j):
                p1 = points[index_i]
                if p1[0] != p2[0] and p1[1] != p2[1]:
                    if (p1[0], p2[1]) in S and (p2[0], p1[1]) in S:
                        ans = min(ans, abs((p1[0] - p2[0]) * (p1[1] - p2[1])))
            
        return ans if ans < float('inf') else 0

from collections import defaultdict
class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        S = set(map(tuple, points))
        area = float(\"inf\")
        points.sort()
        for i in range(len(points)):
            for j in range(i):
                p0, p1 = points[i], points[j]
                if p0[0] == p1[0] or p0[1] == p1[1]: continue
                if (p1[0], p0[1]) in S and (p0[0], p1[1]) in S:
                    area = min(area, abs((p1[1]-p0[1]) * (p1[0]-p0[0])))
        return area if area != float(\"inf\") else 0
            

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        marea=float(\"inf\")
        pd={}
        for p in points:
            pd[(p[0],p[1])]=True
        for p1 in points:
            for p2 in points:
                if p1!=p2 and p2[0]>p1[0] and p2[1]>p1[1]:
                    if (p1[0],p2[1]) in pd and (p2[0],p1[1]) in pd:
                        marea=min(marea, (p2[0]-p1[0])*(p2[1]-p1[1]) )
        return marea if marea!=float(\"inf\") else 0
        

from collections import defaultdict
class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        res = float('inf')
        past_points = set()
        for x1, y1 in points:
            for x2, y2 in past_points:
                if x1 != x2 and y1 != y2:
                    if (x1, y2) in past_points and (x2, y1) in past_points:
                        area = abs(x1 - x2) * abs(y1 - y2)
                        res = min(res, area)
            past_points.add((x1, y1))
        # for cur_point in points:
        #     for dia_point in points:
        #         if cur_point[0] != dia_point[0] and cur_point[1] != dia_point[1]:
        #             if (cur_point[0], dia_point[1]) in past_points \\
        #             and (dia_point[0], cur_point[1]) in past_points:
        #                 new_area = abs(cur_point[0]-dia_point[0]) * abs(cur_point[1]-dia_point[1])
        #                 print(new_area)
        #                 res = min(res, new_area)
        #     past_points.add((cur_point[0], cur_point[1]))
        return res if res!=float('inf') else 0




from itertools import combinations

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        points_s = set(map(tuple,points))
        
        ans = float(\"inf\")
        for p, q in combinations(points, 2):
            if p[0] != q[0] and p[1] != q[1]:
                if (p[0], q[1]) in points_s and (q[0], p[1]) in points_s:
                    ans = min(ans, abs(p[0] - q[0]) * abs(p[1] - q[1]))
                    
        return ans if ans < float(\"inf\") else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        point_set = set()
        x_map, y_map = defaultdict(set), defaultdict(set)
        for x, y in points:
            point_set.add((x, y))
            x_map[x].add((x, y))
            y_map[y].add((x, y))
        
        min_rec = math.inf
        def dfs(points:list):
            nonlocal x_map, y_map, point_set, min_rec
            if len(points) not in (1, 2, 3):
                return 
            if len(points) == 1:
                x, y = points[0]
                for point in x_map[x]:
                    if point[1] < y:
                        points.append(point)
                        dfs(points)
                        points.pop()
            elif len(points) == 2:
                x, y = points[0]
                for point in y_map[y]:
                    if point[0] > x:
                        points.append(point)
                        dfs(points)
                        points.pop()
            else:
                x2, y1 = points[2][0], points[1][1]
                if (x2, y1) in point_set:
                    x1, y2 = points[1][0], points[2][1]
                    min_rec = min(min_rec, (x2 - x1) * (y2 - y1))
        for point in point_set:
            dfs([point])
        if min_rec != math.inf:
            return min_rec
        else:
            return 0


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        
        # 针对每一组点, check 有没有另外两个点可以组成一个长方形
        # (1, 2) 和 (4, 3), 检查有没有 (1, 3) 和 (4, 2)
        
        points_set = set(map(tuple, points))
        min_area = float('inf')
        
        for idx, p2 in enumerate(points):
            for i in range(idx):
                p1 = points[i]
                if p1[0] != p2[0] and p1[1] != p2[1] and (p1[0], p2[1]) in points_set and (p2[0], p1[1]) in points_set:
                    area = abs( (p2[0]-p1[0])*(p2[1] - p1[1]) )
                    min_area = min(min_area, area)
        
        return min_area if min_area < float('inf') else 0
        
        


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        seen, bases, baseX, res = collections.defaultdict(dict), [], -1, float(\"inf\")
        for x, y in sorted(points):
            if x != baseX:
                baseX, bases = x, []
            for base in bases:
                if y in seen[base]:
                    res = min(res, (x - seen[base][y]) * (y - base))
                seen[base][y] = x
            bases.append(y)
        return res if res < float(\"inf\") else 0

from collections import defaultdict

class Solution:
    
    def minAreaRect(self, points: List[List[int]]) -> int:
        \"\"\"
        A rectangle consists of four points:
        A ---- B
        |      |
        D ---- C
        
        Point B = [Cx, Ay]
        Point D = [Ax, Cy]

        \"\"\"

        point_set = set(map(tuple, points))
        minArea = float('inf')

        for i, point_a in enumerate(points):
            for point_c in points[i:]:

                # Make sure A and C have different x and y position
                # otherwise the rectangle will be of 0 width/height.
                if point_a[0] == point_c[0] or point_a[1] == point_c[1]:
                    continue

                point_b = (point_c[0], point_a[1])
                point_d = (point_a[0], point_c[1])
                
                if point_b in point_set and point_d in point_set:
                    minArea = min(minArea,
                                  abs(point_a[0] - point_b[0])*abs(point_a[1] - point_d[1]))

        if minArea == float('inf'):
            return 0
        else:
            return minArea

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        pointset = {tuple(i) for i in points}
        ans = float('inf')
        area = lambda x,y: abs((x[0]-y[0])*(x[1]-y[1]))
        
        for A, B in combinations(points, 2):
            if A[0] == B[0] or A[1] == B[1]: continue
            if (A[1]-B[1])//(A[0]-B[0]) >= 0: continue

            if (A[0], B[1]) in pointset and (B[0], A[1]) in pointset:
                ans = min(ans, area(A, B))

        return ans if ans != float('inf') else 0
                        


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        point_set = {(x, y) for x, y in points}
        n = len(points)
        result = float('inf')
        for i in range(1, n):
            for j in range(i):
                p1 = points[i]
                p2 = points[j]
                if p1[0] != p2[0] and p1[1] != p2[1] \\
                    and (p1[0], p2[1]) in point_set \\
                    and (p2[0], p1[1]) in point_set:
                    result = min( result, abs((p2[0]-p1[0]) * (p2[1]-p1[1])) )
        
        return result if result != float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:

        points = sorted(points)

        points_set = set([tuple(p) for p in points])

        min_area = 999999999999

        has_rectangle = False

        for i in range(len(points)):
            for j in range(i+1,len(points)):
                x1 = points[i][0]
                y1 = points[i][1]

                x2 = points[j][0]
                y2 = points[j][1]

                # check (x1, y2) and (x2, y1)

                if x1==x2:
                    continue

                if y1==y2:
                    continue

                if (x1, y2) not in points_set:
                    continue

                if (x2, y1) not in points_set:
                    continue

                area = abs((x1-x2)*(y1-y2))

                has_rectangle = True
                    
                if area<min_area:
                    min_area = area
                    
        if not has_rectangle:
            return 0
        else:
            return min_area                


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        '''
        Exactly two row values, exactly two column values such that
        we minimize (row2 - row1) * (col2 - col1)
        '''
        S = set(map(tuple, points))
        ans = float('inf')
        for j, p2 in enumerate(points):
            for i in range(j):
                p1 = points[i]
                if (p1[0] != p2[0] and p1[1] != p2[1] and
                        (p1[0], p2[1]) in S and (p2[0], p1[1]) in S):
                    ans = min(ans, abs(p2[0] - p1[0]) * abs(p2[1] - p1[1]))
        return ans if ans < float('inf') else 0
                


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        
        
        S = set(map(tuple, points))
        ans = float('inf')
        for j, p2 in enumerate(points):
            for i in range(j):
                p1 = points[i]
                if (p1[0] != p2[0] and p1[1] != p2[1] and
                        (p1[0], p2[1]) in S and (p2[0], p1[1]) in S):
                    ans = min(ans, abs(p2[0] - p1[0]) * abs(p2[1] - p1[1]))
        return ans if ans < float('inf') else 0
    
    
        
        columns = collections.defaultdict(list)
        for x, y in points:
            columns[x].append(y)
        lastx = {}
        ans = float('inf')

        for x in sorted(columns):
            column = columns[x]
            column.sort()
            for j, y2 in enumerate(column):
                for i in range(j):
                    y1 = column[i]
                    if (y1, y2) in lastx:
                        ans = min(ans, (x - lastx[y1,y2]) * (y2 - y1))
                    lastx[y1, y2] = x
        return ans if ans < float('inf') else 0


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        
        
#         columns = collections.defaultdict(list)
#         for x, y in points:
#             columns[x].append(y)
#         lastx = {}
#         ans = float('inf')

#         for x in sorted(columns):
#             column = columns[x]
#             column.sort()
#             for j, y2 in enumerate(column):
#                 for i in range(j):
#                     y1 = column[i]
#                     if (y1, y2) in lastx:
#                         ans = min(ans, (x - lastx[y1,y2]) * (y2 - y1))
#                     lastx[y1, y2] = x
#         return ans if ans < float('inf') else 0
    
    
        
        S = set(map(tuple, points))
        ans = float('inf')
        for j, p2 in enumerate(points):
            for i in range(j):
                p1 = points[i]
                if (p1[0] != p2[0] and p1[1] != p2[1] and
                        (p1[0], p2[1]) in S and (p2[0], p1[1]) in S):
                    ans = min(ans, abs(p2[0] - p1[0]) * abs(p2[1] - p1[1]))
        return ans if ans < float('inf') else 0        

        
        
        
#         min_area = float('inf')
#         same_y = {}
#         h_segments = {}
                
#         for i in range(len(points)  - 1):
#             for j in range(i + 1 , len(points)):
#                 if points[i][1] == points[j][1]:
#                     key = (min(points[i][0] , points[j][0]) , max(points[i][0] , points[j][0]) )
#                     if key not in h_segments:
#                         h_segments[key] = []
#                     h_segments[key].append(points[i][1])
                                    
#         for key in h_segments:
#             width = key[1] - key[0]
            
#             val = h_segments[key]
#             if len(val) > 1:
#                 for i in range(len(val) - 1):
#                     for j in range(i, len(val)):
#                         height = abs(val[i] - val[j])
#                         if  width > 0 and  height > 0:
#                             min_area = min(min_area , width * height)
        
#         if min_area == float('inf'):
#             return 0
                
#         return min_area


from collections import defaultdict
class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        
        
        ss=set([tuple(ele) for ele in points])
        ans=float('inf')
        
        for i in range(len(points)-1):
            for j in range(i+1,len(points)):
                x1,y1=points[i]
                x2,y2=points[j]
                if (x2,y1) in ss and (x1,y2) in ss and x1!=x2 and y1!=y2:
                    ans=min(ans,abs((x1-x2)*(y1-y2)))
        return (0 if ans==float('inf') else ans)


from collections import defaultdict
class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        point_set = set(map(tuple, points)) 
        result = float(\"inf\")
        for k, p2 in enumerate(points):
            for i in range(k):
                p1 = points[i]
                if p1[0] != p2[0] and p1[1] != p2[1] and (p1[0], p2[1]) in point_set and (p2[0], p1[1]) in point_set:
                    area = abs(p1[0] - p2[0]) * abs(p1[1] - p2[1])
                    result = min(result, area)
                    
        return result if result != float(\"inf\") else 0
        
        

from collections import defaultdict
class Solution:
    def minAreaRect(self, points):
        X = list(zip(*points))
        Nr, Nc = len(set(X[0])), len(set(X[1]))
        if Nr == 1 or Nc == 1: return 0
        if Nr > Nc:
            points = [[c, r] for (r, c) in points]
            return self.minAreaRect(points)

        points.sort()
        columns = defaultdict(list)
        for r, c in points:
            columns[r].append(c)
        ans, lastc = float('inf'), dict()
        for r, cols in list(columns.items()):
            for i, c1 in enumerate(cols):
                for c2 in cols[i+1:]:
                    if (c1, c2) in lastc:
                        area = (r - lastc[(c1, c2)]) * (c2 - c1)
                        #ans = min(ans, area)
                        if area < ans: ans = area
                    lastc[(c1, c2)] = r
        return ans if ans < float('inf') else 0

# from collections import defaultdict
# class Solution:
#     def minAreaRect(self, points):
#         d = defaultdict(set)
#         rset, cset, N, ans = set(), set(), len(points), float('inf')
#         for r, c in points:
#             rset.add(r)
#             cset.add(c)
#         Nr, Nc = len(rset), len(cset)
#         if Nr == N or Nc == N:
#             return 0
#         elif Nr < Nc:
#             for r, c in points:
#                 d[r].add(c)
#         else:
#             for r, c in points:
#                 d[c].add(r)
# 
#         A = sorted(d.keys())
#         for i, r1 in enumerate(A):
#             cols1 = d[r1]
#             for r2 in A[i+1:]:
#                 cols2 = d[r2]
#                 s = sorted(cols1 & cols2)
#                 for c1, c2 in zip(s[:-1], s[1:]):
#                     area = abs((r1 - r2) * (c1 - c2))
#                     ans = min(ans, area)
#         return ans if ans < float('inf') else 0


from math import floor
from math import ceil
from math import sqrt
from collections import deque
import numpy
from _collections import deque
#from _ast import Num # LC doesn't like this
from heapq import *
from typing import List
import random

MOD = int(1e9 + 7)
BASE = 256


class Solution:

    def minAreaRect(self, points: List[List[int]]) -> int:
        s = set()
        for p in points:
            s.add((p[0], p[1]))

        N = len(points)
        ans = 0
        for i in range(N):
            p1 = points[i]
            for j in range(i + 1, N):
                p2 = points[j]
                if p1[0] == p2[0] or p1[1] == p2[1]:
                    continue
                newA = abs(p1[0] - p2[0]) * abs(p1[1] - p2[1])
                if (ans == 0 or
                        newA < ans) and (p1[0], p2[1]) in s and (p2[0],
                                                                 p1[1]) in s:
                    ans = newA
        return ans

\"\"\"
l = [[1, 1], [1, 2]]
S = Solution()
l = [[1, 1], [1, 3], [3, 1], [3, 3], [2, 2]]
l = [[1, 1], [1, 3], [3, 1], [3, 3], [4, 1], [4, 3]]
print(S.minAreaRect(l))
\"\"\"

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        points.sort()
        min_area = 40000**2+1
        m = len(points)
        # set_points = set()
        # for i in points:
        #     set_points.add(i)
        # print(set_points)
        points_table = set()

        for x, y in points:
            points_table.add((x,y))
        for i in range(m):
            for j in range(i+1,m):
                a = points[i]
                b = points[j]
                if a[0]!=b[0] and a[1]!=b[1]:
                    if ((a[0],b[1]) in points_table) and ((b[0],a[1]) in points_table):
                        z=abs((b[0]-a[0])*(b[1]-a[1]))
                        min_area = min(min_area, z)
        if min_area == 40000**2+1:
            min_area = 0
        return min_area

from collections import defaultdict
class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        S = set(map(tuple, points))
        area = float(\"inf\")
        for i in range(len(points)):
            for j in range(i):
                p0, p1 = points[i], points[j]
                if p0[0] == p1[0] or p0[1] == p1[1]: continue
                if (p1[0], p0[1]) in S and (p0[0], p1[1]) in S:
                    area = min(area, abs((p1[1]-p0[1]) * (p1[0]-p0[0])))
        return area if area != float(\"inf\") else 0
            

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        if not points:
            return 0
        
        pointsLookup = set(map(tuple, points))
        minArea = math.inf

        for i in range(0, len(points)-1):
            p1 = points[i]
            for j in range(i+1, len(points)):
                p2 = points[j]
                # Find two potential diagonal points and see if other points of the rectangle
                # are present. If so, we've a rectangle.
                if p1[0] != p2[0] and p1[1] != p2[1] \\
                    and (p1[0], p2[1]) in pointsLookup and (p2[0], p1[1]) in pointsLookup:
                    area = abs(p1[0] - p2[0]) * abs(p1[1] - p2[1])
                    minArea = min(minArea, area)

        return minArea if minArea < math.inf else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        
  
        S = set(map(tuple, points))
        ans = float('inf')
        
        for j, p2 in enumerate(points):
            for i in range(j):
                p1 = points[i]
                if (p1[0] != p2[0] and p1[1] != p2[1] and
                        (p1[0], p2[1]) in S and (p2[0], p1[1]) in S):
                    ans = min(ans, abs(p2[0] - p1[0]) * abs(p2[1] - p1[1]))
        
        return ans if ans < float('inf') else 0
            
        
        


from collections import defaultdict

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        # Sort by Column
        # Time  complexity: O(N^2)
        # Space complexity: O(N)
        # columns = defaultdict(list)
        # for x, y in points:
        #     columns[x].append(y)
        # lastx, ans = {}, float(\"inf\")

        # for x in sorted(columns):
        #     column = columns[x]
        #     column.sort()
        #     for j, y2 in enumerate(column):
        #         for i in range(j):
        #             y1 = column[i]
        #             if (y1, y2) in lastx:
        #                 ans = min(ans, (x - lastx[y1, y2]) * (y2 - y1))
        #             lastx[y1, y2] = x

        # return ans if ans < float(\"inf\") else 0


        # Count by Diagonal
        # For each pair of points in the array, consider them to be the long diagonal of a potential rectangle. We can check if all 4 points are there using a Set.
        # Time  complexity: O(N^2)
        # Space complexity: O(N)
        S = set(map(tuple, points))
        ans = float(\"inf\")
        for j, p2 in enumerate(points):
            for i in range(j):
                p1 = points[i]
                if p1[0] != p2[0] and p1[1] != p2[1] and \\
                    (p1[0], p2[1]) in S and (p2[0], p1[1]) in S:
                    ans = min(ans, abs(p2[0] - p1[0]) * abs(p2[1] - p1[1]))
        return ans if ans < float(\"inf\") else 0
        

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        d = set(map(tuple, points))
        
        min_square = sys.maxsize
        for i, x in enumerate(points):
            for j in range(i, len(points)):
                y = points[j]            
                if x[0] != y[0] and x[1] != y[1] and (x[0], y[1]) in d and (y[0], x[1]) in d:
                    min_square = min(min_square, abs(x[0]-y[0])*abs(x[1]-y[1]))
            
        return min_square if min_square != sys.maxsize else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        S = set(map(tuple, points))
        minArea = float('inf')
        
        for i, p1 in enumerate(points):
            for j in range(i):
                p2 = points[j]
                
                if p1[0] == p2[0] or p1[1] == p2[1]:
                    continue
                
                if (p1[0], p2[1]) in S and \\
                   (p2[0], p1[1]) in S:
                    area = abs(p2[0] - p1[0]) * abs(p2[1] - p1[1])
                    minArea = min(minArea, area)
        
        if minArea == float('inf'):
            return 0
        return minArea

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        all_points = set([(point[0],point[1]) for point in points])
        min_area = float('inf')
        for idx,point1 in enumerate(points):
            for idx,point2 in enumerate(points[idx+1:],start=idx):
                if point1[0] != point2[0] and point1[1] != point2[1] and (point1[0],point2[1]) in all_points and (point2[0],point1[1]) in all_points:
                    min_area = min(min_area, abs(point1[0]-point2[0])*abs(point1[1]-point2[1]))
        return min_area if min_area != float('inf') else 0

import itertools
class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        if not points:
            return 0
        ybucket = defaultdict(list)
        for x, y in points:
            ybucket[y].append(x)

        for k, v in ybucket.items():
            if len(v) >= 2:
                ybucket[k] = sorted(v)

        ys = [k for k in ybucket.keys() if len(ybucket[k]) >= 2]
        ans = 2**31-1
        for y1, y2 in itertools.combinations(ys, 2):
            row1 = set(ybucket[y1])
            row2 = ybucket[y2]
            for idx, x in enumerate(row2):
                if x in row1:
                    print(x)
                    xlast = x
                    idx_last = idx
                    break
            else:
                continue
     
            dy = abs(y2-y1)
            for x in row2[idx_last+1:]:
                if x in row1:
                    ans = min(ans, abs(x-xlast)*dy)
                    xlast = x

        return ans if ans < 2**31-1 else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        ans = float('inf')
            
        s = {tuple(point) for point in points}
        for i in range(len(points) - 1):
            for j in range(i + 1, len(points)):
                p1, p2 = points[i], points[j]
                if p1[0] == p2[0] or p1[1] == p2[1]:
                    continue
                if (p1[0], p2[1]) in s and (p2[0], p1[1]) in s:
                    ans = min(ans, abs(p1[0] - p2[0]) * abs(p1[1] - p2[1]))
        
        return ans if ans != float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        hash_set = set(map(tuple, points))
        min_ = float('inf')

            
        for j, p2 in enumerate(points):
            for i in range(j):
                p1 = points[i]
                x1,y1 = p2[0],p2[1]
                x2,y2 = p1[0],p1[1]
                if x1 != x2 and y1 != y2:
                    if (x2, y1) in hash_set and (x1,y2) in hash_set:
                        min_ = min(min_, abs(x1-x2)*abs(y1-y2))
        
        if min_ == float('inf'):
            return 0
        
        return min_

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        if not points or len(points) < 4:
            return 0
        
        pointsSet = set()
        
        minArea = float(\"inf\")
        for x1, y1 in points:
            for x2, y2 in pointsSet:
                if (x1,y2) in pointsSet and (x2, y1) in pointsSet:
                    minArea = min(minArea, self.getArea(x1,y1,x2,y2))
            pointsSet.add((x1, y1))
        
        return 0 if minArea == float(\"inf\") else minArea
                    
                
    
    def getArea(self, x1, y1, x2, y2) -> int:
        x = abs(x2 - x1)
        y = abs(y2 - y1)
        return x * y

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        S = set(map(tuple,points))
        minarea = float('inf')
        for j,p2 in enumerate(points):
            for i in range(j):
                p1 = points[i]
                if p1[0] != p2[0] and p1[1] != p2[1] and (p1[0],p2[1]) in S and (p2[0],p1[1]) in S:
                    minarea = min(minarea,abs(p2[0]-p1[0]) * abs(p1[1]-p2[1]))
                
                
        return minarea if minarea < float('inf') else 0


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        res = float('inf')
        dic = collections.defaultdict(set)
        for p in points:
            dic[p[0]].add(p[1])
        for i in range(len(points)):
            for j in range(len(points)):
                p1,p2=points[i],points[j]
                if p1[0]>=p2[0] or p1[1]>=p2[1]:
                    continue
                if p1[1] in dic[p2[0]] and p2[1] in dic[p1[0]]:
                    res = min(res,(p2[0]-p1[0])*(p2[1]-p1[1]))
        return res if res!=float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        
        area = float('inf')
        
        points_set = {tuple(p) for p in points}
        
        for i in range(len(points) - 1):
            for j in range(i + 1, len(points)):
                (x1, y1) = points[i]
                (x2, y2) = points[j]
                
                if x1 != x2 and y1!=y2:
                    if (x2, y1) in points_set and (x1, y2) in points_set:
                        area = min(abs(y2-y1) * abs(x2-x1), area)
        
        return area if area  < float('inf') else 0
                        


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        _set = {(x, y) for x, y in points}
        res = math.inf
        for p1, p2 in combinations(points, 2):
            arr = [p1, p2]
            arr.sort()
            (x1, y1), (x2, y2) = arr
            l, w = y2 - y1, x2 - x1
            if l > 0 and w > 0 and (x1, y1 + l) in _set and (x1 + w, y1) in _set:
                res = min(res, l * w)
    
        return 0 if math.isinf(res) else res

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        pointSet = set()
        for x, y in points:
            pointSet.add((x, y))
        length = len(points)
        minArea = float('inf')
        for i in range(length):
            for j in range(i, length):
                p1 = points[i]
                p2 = points[j]
                if p1[0] != p2[0] and p1[1] != p2[1]:
                    if (p1[0], p2[1]) in pointSet and (p2[0], p1[1]) in pointSet:
                        minArea = min(minArea, abs(p1[0] - p2[0]) * abs(p1[1] - p2[1]))
        if minArea == float('inf'):
            return 0
        return minArea

class Solution:
\tdef minAreaRect(self, points: List[List[int]]) -> int:
\t\tpoints.sort(key = lambda x:(x[0], x[1]))
\t\tver = collections.defaultdict(list)
\t\thor = collections.defaultdict(list)
\t\tseen = set()
\t\tres = float(\"inf\")
\t\tfor x, y in points:            
\t\t\tif len(ver[x]) > 0 and len(hor[y]) > 0:
\t\t\t\tfor i in ver[x][::-1]:
\t\t\t\t\tfor j in hor[y][::-1]:
\t\t\t\t\t\tif (j, i) in seen:
\t\t\t\t\t\t\tres = min(res, (x - j) * (y - i))
\t\t\tseen.add((x, y))
\t\t\tver[x].append(y)
\t\t\thor[y].append(x)
\t\tif res != float(\"inf\"):
\t\t\treturn res
\t\telse:
\t\t\treturn 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        point_set = set(map(tuple, points)) 
        result = float(\"inf\")
        for k, p2 in enumerate(points):
            for i in range(k+1, len(points)):
                p1 = points[i]
                if p1[0] != p2[0] and p1[1] != p2[1] and (p1[0], p2[1]) in point_set and (p2[0], p1[1]) in point_set:
                    area = abs(p1[0] - p2[0]) * abs(p1[1] - p2[1])
                    result = min(result, area)
                    
        return result if result != float(\"inf\") else 0


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        points.sort()
        n = len(points)
        result = math.inf
        seen = set()
        for i in range(n):
            x1, y1 = points[i]
            for j in range(i + 1, n):
                x2, y2 = points[j]
                if x2 == x1: continue
                a = (x1, y2, x2, y1)
                if a in seen:
                    result = min(result, (x2 - x1) * abs(y2 - y1))
                seen.add((x1, y1, x2, y2))
        return 0 if math.isinf(result) else result

from collections import defaultdict

class Solution:
    
    def computeArea(self, point_a, point_b, point_c, point_d):
        width = abs(point_a[0] - point_b[0])
        height = abs(point_a[1] - point_d[1])
        return width*height
    
    def minAreaRect(self, points: List[List[int]]) -> int:
        \"\"\"
        A rectangle consists of four points:
        A ---- B
        |      |
        D ---- C
        
        Point B = [Cx, Ay]
        Point D = [Ax, Cy]

        \"\"\"

        point_set = set(map(tuple, points))
            
        minArea = None
        for i, point_a in enumerate(points):
            for point_c in points[i:]:

                # Make sure A and C have different x and y position
                # otherwise the rectangle will be of 0 width/height.
                if point_a[0] == point_c[0] or point_a[1] == point_c[1]:
                    continue

                point_b = (point_c[0], point_a[1])
                point_d = (point_a[0], point_c[1])
                
                if point_b in point_set and point_d in point_set:
                    area = self.computeArea(point_a, point_b, point_c, point_d)
                    if area > 0 and minArea is None or area < minArea:
                        minArea = area

        if minArea is None:
            return 0
        else:
            return minArea

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        ans = float('inf')
        seen = set(map(tuple, points))
        points.sort()
        for i in range(len(points)):
            for j in range(i):
                x1, y1 = points[i][0], points[i][1]
                x2, y2 = points[j][0], points[j][1]
                                
                if x1 != x2 and y1!= y2 and (x1, y2) in seen and (x2, y1) in seen:
                    ans = min(ans, (x1-x2)*abs(y2-y1))

        return ans if ans != float('inf') else 0


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        s = set(map(tuple, points))
        res = float('inf')
        for i in range(len(points)-1):
            for j in range(i+1,len(points)):
                x1 = points[i][0]
                y1 = points[i][1]
                x2 = points[j][0]
                y2 = points[j][1]
                if x1!=x2 and y1!=y2 and (x1,y2) in s and (x2,y1) in s:
                    res = min (res,abs((y2-y1)*(x2-x1)))
        return res if res!=float('inf') else 0


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        s = set()
        for point in points:
            x = point[0]
            y = point[1]
            s.add((x,y))
        res = float('inf')
        for i in range(len(points)-1):
            for j in range(i+1,len(points)):
                x1 = points[i][0]
                y1 = points[i][1]
                x2 = points[j][0]
                y2 = points[j][1]
                if x1==x2 or y1==y2:
                    continue
                if (x1,y2) in s and (x2,y1) in s:
                    res = min (res,abs((y2-y1)*(x2-x1)))
        return res if res!=float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        S = set(map(tuple, points))
        print(S)
        ans = float('inf')
        for j, p2 in enumerate(points):
            print((j,p2))
            for i in range(j):
                p1 = points[i]
                if (p1[0] != p2[0] and p1[1] != p2[1] and
                        (p1[0], p2[1]) in S and (p2[0], p1[1]) in S):
                    ans = min(ans, abs(p2[0] - p1[0]) * abs(p2[1] - p1[1]))
        return ans if ans < float('inf') else 0


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        p_set = set([(x, y) for x, y in points])
        
        res = float('inf')
        for i in range(len(points)):
            for j in range(i+1, len(points)):
                p1 = points[i]
                p2 = points[j]
                if p1[0] == p2[0] or p1[1] == p2[1]:
                    continue
                up = (p1[0], p2[1])
                left = (p2[0], p1[1])
                if up in p_set and left in p_set:
                    area = abs(p1[0]-p2[0])*abs(p1[1]-p2[1])
                    res = min(res, area)
        
        return res if res < float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        pointset = {tuple(i) for i in points}
        ans = float('inf')
        area = lambda x,y: abs((x[0]-y[0])*(x[1]-y[1]))
        
        for i, A in enumerate(points):
            for j in range(i):
                B = points[j]
                if A[0] == B[0] or A[1] == B[1]: continue
                #if (A[1]-B[1])//(A[0]-B[0]) >= 0: continue

                if (A[0], B[1]) in pointset and (B[0], A[1]) in pointset:
                    ans = min(ans, area(A, B))

        return ans if ans != float('inf') else 0
                        


'''

    When we see new x point, we establish base point x as baseX.
    For every baseX, there will be multiple y points (y_for_baseX array)
    If y was registered in previous base(for base in y_for_baseX), we update res.
        In other words, we update res if (y, base) seen before at another x.
    We also update x value of seen[base][y]
    After processing, we append y to bases array.
    Return res

'''

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        base_x_to_y = collections.defaultdict(dict)
        y_for_base_x = []
        base_x = -1
        min_area = float('inf')
        for x, y in sorted(points):
            if x != base_x:
                base_x = x
                y_for_base_x = []
            for prev_base_x in y_for_base_x:
                if y in base_x_to_y[prev_base_x]:
                    w = x - base_x_to_y[prev_base_x][y]
                    h = y - prev_base_x
                    area = w * h
                    min_area = min(min_area, area)
                base_x_to_y[prev_base_x][y] = x
            y_for_base_x.append(y)
        return min_area if min_area < float('inf') else 0

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        
class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        points = [Point(x, y) for [x, y] in points]
        dic_x = collections.defaultdict(set)
        dic_y = collections.defaultdict(set)
        for p in points:
            dic_x[p.x].add(p.y)
            dic_y[p.y].add(p.x)
        
        area = math.inf
        for (p1, p2) in itertools.combinations(points, 2):
            if p1.x == p2.x or p1.y == p2.y:
                continue
            if p2.y in dic_x[p1.x] and p2.x in dic_y[p1.y]:
                area = min(area, abs(p2.x-p1.x) * abs(p2.y-p1.y))
        return area if area != math.inf else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
#         # N = 500 is really loose.
#         # Just check all pairs of points.
#         # Time O(N^2) , 1000ms ~ 1200ms
        
#         seen = set()
#         minA = float('inf')
#         for x1, y1 in points:
#             for x2, y2 in seen:
#                 if (x1,y2) in seen and (x2,y1) in seen:
#                     area = abs(x1-x2)*abs(y1-y2)
#                     minA = min(minA, area)
#             seen.add((x1,y1))
#         return minA if minA<float('inf') else 0
    
        # Below is a bit more ugly solution but is easier to understand
        # time is O(N^2)
        # similar question #963. Minimum Area Rectangle II
        minA = float('inf')
        cache = {(x,y) for x, y in points} # hash all points for quicker lookup
        for i in range(len(points)):
            x1, y1 = points[i]
            for j in range(i+1, len(points)): # for every possible pair in points
                x2, y2 = points[j]
                if (x1,y2) in cache and (x2,y1) in cache:
                    area = abs(x1-x2)*abs(y1-y2)
                    if area != 0:
                        minA = min(minA, area)
        return minA if minA<float('inf') else 0
                
                
                
                
                
                
                


from collections import defaultdict
from math import inf


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        byrow = defaultdict(set) # row: {col,}
        bycol = defaultdict(set) # col: {row,}
        for x, y in points:
            byrow[y].add(x)
            bycol[x].add(y)
            
        area = inf
        for x1, ys in list(bycol.items()):
            if len(ys) < 2:
                continue
            for y1 in ys:
                for y2 in ys - {y1}:
                    for x2 in byrow[y1] & byrow[y2] - {x1}:
                        area = min(area, abs(x1-x2)*abs(y1-y2))
        return area if area < inf else 0


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        n = len(points)
        point_set = {(x, y) for x, y in points}
        min_area = math.inf
        
        for i in range(n):
            x0, y0 = points[i]
            for j in range(i + 1, n):
                x1, y1 = points[j]
                if (x0, y1) in point_set and (x1, y0) in point_set:
                    area = abs(x0 - x1) * abs(y0 - y1)
                    if area > 0:
                        min_area = min(min_area, area)
        
        return 0 if min_area == math.inf else min_area

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        counter = Counter(list(map(tuple, points)))
        if any(count >= 4 for count in list(counter.values())):
            return 0
        points = list(counter)
        result = float('inf')
        for i, (x, y) in enumerate(points):
            for j in range(i + 1, len(points)):
                x_, y_ = points[j]
                if x != x_ and y != y_:
                    if (x, y_) in counter and (x_, y) in counter:
                        result = min(result, abs((x_ - x) * (y_ - y)))
                        if not result:
                            return 0
                elif counter[(x, y)] == 2 and counter[(x_, y_)] == 2:
                        return 0
        return 0 if result == float('inf') else result


from collections import defaultdict
class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        
        
        ss={}
        for ele in points:
            ss[tuple(ele)]=1
            
        ans=float('inf')
        for i in range(len(points)-1):
            for j in range(i+1,len(points)):
                x1,y1=points[i]
                x2,y2=points[j]
                if (x2,y1) in ss and (x1,y2) in ss and x1!=x2 and y1!=y2:
                    ans=min(ans,abs((x1-x2)*(y1-y2)))
        return (0 if ans==float('inf') else ans)


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        
        st = set()
        
        min_area = float('inf')
        
        for item in points:
            st.add((item[0], item[1]))
            
        for i in range(len(points)-1):
            for j in range(i+1,len(points)):
                x1,y1 = points[i][0], points[i][1]
                
                x2,y2 = points[j][0], points[j][1]
                
                if x1 == x2 or y1 ==y2:
                    continue
                
                if (x1,y2) in st and (x2, y1) in st:
                    curr_area = abs((y2-y1) * (x2-x1))
                    min_area = min(min_area, curr_area)
        
        if min_area == float('inf'):
            return 0
        
        return min_area

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        points_set = set([tuple(v) for v in points])
        min_area = None
        for i, x1y1 in enumerate(points):
            for x2y2 in points[i:]:
                if x1y1[0] == x2y2[0] or x1y1[1] == x2y2[1]:
                    continue
                x1y2 = tuple([x1y1[0], x2y2[1]])
                x2y1 = tuple([x2y2[0], x1y1[1]])
                if x1y2 in points_set and x2y1 in points_set:
                    area = abs((x1y1[0]-x2y2[0]) * (x1y1[1]-x2y2[1]))
                    # print(abs(area))
                    if min_area is None or area < min_area:
                        min_area=area
        return min_area if min_area else 0
        


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        # for every pair of points, check if other diagonal is in points
        pointset = set([tuple(x) for x in points])
        res = 2 ** 32
        for i in range(len(points)):
            for j in range(i+1, len(points)):
                p1 = points[i]
                p2 = points[j]
                if p1[0] != p2[0] and p1[1] != p2[1]: # check points aren't on the same line
                    if (p1[0],p2[1]) in pointset and (p2[0], p1[1]) in pointset: # check other diag is in points
                        res = min(res, abs(p1[0] - p2[0]) * abs(p1[1] - p2[1])) # update res
        return res if res < 2 ** 32 else 0
                


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        
        
        hashset = set()
        
        for x, y in points:
            hashset.add((x, y))
        
        area = float('inf')
        for i in range(len(points)):
            for j in range(len(points)):
                x1, y1 = points[i]
                x2, y2 = points[j]
                
                if x1 >= x2 or y1 >= y2:
                    continue
                
                if (x1, y2) in hashset and (x2, y1) in hashset:
                    area = min(area, (y2-y1)*(x2-x1))
                
        return area if area < float('inf') else 0

'''
1. When we see new x point, we establish base point x as baseX.
2. For every baseX, there will be multiple y points (y_for_baseX array)
3. If y was registered in previous base(for base in y_for_baseX), we update res.
  - In other words, we update res if (y, base) seen before at another x.
4. We also update x value of seen[base][y]
5. After processing, we append y to bases array.
6. Return res
'''

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        base_x_to_y = collections.defaultdict(dict)
        y_for_base_x = []
        base_x = -1
        min_area = float('inf')
        for x, y in sorted(points):
            if x != base_x: # 1
                base_x = x # 1
                y_for_base_x = [] # 2
            for prev_base_x in y_for_base_x: # 3
                if y in base_x_to_y[prev_base_x]: # 3
                    w = x - base_x_to_y[prev_base_x][y]
                    h = y - prev_base_x
                    area = w * h
                    min_area = min(min_area, area) # 3
                base_x_to_y[prev_base_x][y] = x # 4
            y_for_base_x.append(y) # 5
        return min_area if min_area < float('inf') else 0 # 6

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        
        
#         columns = collections.defaultdict(list)
#         for x, y in points:
#             columns[x].append(y)
#         lastx = {}
#         ans = float('inf')

#         for x in sorted(columns):
#             column = columns[x]
#             column.sort()
#             for j, y2 in enumerate(column):
#                 for i in range(j):
#                     y1 = column[i]
#                     if (y1, y2) in lastx:
#                         ans = min(ans, (x - lastx[y1,y2]) * (y2 - y1))
#                     lastx[y1, y2] = x
#         return ans if ans < float('inf') else 0
    
    
        
        # S = set(map(tuple, points))
        S = {tuple(p) for p in points}
        
        result = float('inf')
        
        for j in range(len(points)):
            for i in range(j):
                p1 = points[i]
                p2 = points[j]
                if (p1[0] != p2[0] and p1[1] != p2[1] and
                        (p1[0], p2[1]) in S and (p2[0], p1[1]) in S):
                    result = min(result, abs(p2[0] - p1[0]) * abs(p2[1] - p1[1]))
        
        return result if result < float('inf') else 0        

        
        
        
#         min_area = float('inf')
#         same_y = {}
#         h_segments = {}
                
#         for i in range(len(points)  - 1):
#             for j in range(i + 1 , len(points)):
#                 if points[i][1] == points[j][1]:
#                     key = (min(points[i][0] , points[j][0]) , max(points[i][0] , points[j][0]) )
#                     if key not in h_segments:
#                         h_segments[key] = []
#                     h_segments[key].append(points[i][1])
                                    
#         for key in h_segments:
#             width = key[1] - key[0]
            
#             val = h_segments[key]
#             if len(val) > 1:
#                 for i in range(len(val) - 1):
#                     for j in range(i, len(val)):
#                         height = abs(val[i] - val[j])
#                         if  width > 0 and  height > 0:
#                             min_area = min(min_area , width * height)
        
#         if min_area == float('inf'):
#             return 0
                
#         return min_area


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        x = 0; y = 1
        points_set = set()
        for point in points:
            points_set.add((point[x], point[y]))
        if len(points_set) < 4: return 0
        best = None
        for i in range(len(points) - 1):
            for j in range(i + 1, len(points)):
                p1 = points[i]
                p2 = points[j]
                if p1[x] == p2[x] or p1[y] == p2[y]:
                    continue
                if (p1[x],p2[y]) in points_set and (p2[x],p1[y]) in points_set:
                    area = abs(p2[x]-p1[x])*abs(p2[y]-p1[y])
                    best = area if best is None else min(best, area)
        return 0 if not best else best

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
#         # N = 500 is really loose.
#         # Just check all pairs of points.
#         # Time O(N^2) , 1000ms ~ 1200ms
        
#         seen = set()
#         minA = float('inf')
#         for x1, y1 in points:
#             for x2, y2 in seen:
#                 if (x1,y2) in seen and (x2,y1) in seen:
#                     area = abs(x1-x2)*abs(y1-y2)
#                     minA = min(minA, area)
#             seen.add((x1,y1))
#         return minA if minA<float('inf') else 0
    
        # Below is a bit more ugly solution but is easier to understand
        # time is O(N^2)
        # similar question #963. Minimum Area Rectangle II
        cache = set()
        minA = float('inf')
        for x, y in points:
            cache.add((x,y)) # hash all points for quicker lookup
        for i in range(len(points)):
            x1, y1 = points[i]
            for j in range(i+1, len(points)): # for every possible pair in points
                x2, y2 = points[j]
                if (x1,y2) in cache and (x2,y1) in cache:
                    area = abs(x1-x2)*abs(y1-y2)
                    if area != 0:
                        minA = min(minA, area)
        return minA if minA<float('inf') else 0
                
                
                
                
                
                
                


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        setPts = set(map(tuple,points))
        ans = sys.maxsize
        for p in range(len(points)):
            for i in range(p):
                p1 = points[p]
                p2 = points[i]
                if p1[0] != p2[0] and p1[1] != p2[1] and \\
                ((p1[0],p2[1]) in setPts and (p2[0],p1[1]) in setPts):
                    ans = min(ans,abs(p1[0]-p2[0]) * abs(p1[1]-p2[1]))
                        
        return ans if ans < sys.maxsize else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        pointsSet = set(map(tuple, points))
        pointsLength = len(points)
        minArea = float('inf')
        for i in range(pointsLength):
            for j in range(i + 1, pointsLength):
                p1, p2 = points[i], points[j]
                if p1[0] != p2[0] and p1[1] != p2[1] and (p1[0], p2[1]) in pointsSet and (p2[0], p1[1]) in pointsSet:
                    minArea = min(minArea, abs(p2[0] - p1[0]) * abs(p2[1] - p1[1]))
        return minArea if minArea < float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        pointsSet = set(tuple(point) for point in points)
        minarea = sys.maxsize
        for i in range(len(points)):
            for j in range(i+1,len(points)):
                x_i = points[i][0]
                y_i = points[i][1]
                x_j = points[j][0]
                y_j = points[j][1]
                if x_i!=x_j and y_i!=y_j and (x_i,y_j) in pointsSet and (x_j,y_i) in pointsSet:
                    minarea = min(minarea,abs(x_j-x_i)*abs(y_j-y_i))
        return minarea if minarea!=sys.maxsize else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
#         # N = 500 is really loose.
#         # Just check all pairs of points.
#         # Time O(N^2) , 1000ms ~ 1200ms
        
#         seen = set()
#         minA = float('inf')
#         for x1, y1 in points:
#             for x2, y2 in seen:
#                 if (x1,y2) in seen and (x2,y1) in seen:
#                     area = abs(x1-x2)*abs(y1-y2)
#                     minA = min(minA, area)
#             seen.add((x1,y1))
#         return minA if minA<float('inf') else 0
    
        # Below is a bit more ugly solution but is easier to understand
        cache = set()
        minA = float('inf')
        for x, y in points:
            cache.add((x,y)) # hash all points for quicker lookup
        for i in range(len(points)):
            x1, y1 = points[i]
            for j in range(i+1, len(points)): # for every possible pair in points
                x2, y2 = points[j]
                if (x1,y2) in cache and (x2,y1) in cache:
                    area = abs(x1-x2)*abs(y1-y2)
                    if area != 0:
                        minA = min(minA, area)
        return minA if minA<float('inf') else 0
                
                
                
                
                
                
                


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        grid = {}
        for x, y in points:
            grid[x] = grid.get(x, []) + [y]
        
        for row in grid.values():
            row.sort()
            
        globalMin = float(\"inf\")
        for row1 in grid:
            for row2 in grid:
                if row1 == row2:
                    continue
                i = j = 0
                preIdx = -1
                while i < len(grid[row1]) and j < len(grid[row2]):
                    if grid[row1][i] < grid[row2][j]:
                        i += 1
                    elif grid[row1][i] > grid[row2][j]:
                        j += 1
                    else:
                        if preIdx != -1:
                            globalMin = min(globalMin, abs(row2 - row1)*(grid[row1][i] - preIdx))
                        preIdx = grid[row1][i]
                        i += 1
                        j += 1
                        
        if globalMin == float(\"inf\"):
            return 0
        return globalMin

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
#         # N = 500 is really loose.
#         # Just check all pairs of points.
#         # Time O(N^2) , 1000ms ~ 1200ms
        
#         seen = set()
#         minA = float('inf')
#         for x1, y1 in points:
#             for x2, y2 in seen:
#                 if (x1,y2) in seen and (x2,y1) in seen:
#                     area = abs(x1-x2)*abs(y1-y2)
#                     minA = min(minA, area)
#             seen.add((x1,y1))
#         return minA if minA<float('inf') else 0
    
        # Below is a bit more ugly solution but is easier to understand
        # time is O(N^2)
        cache = set()
        minA = float('inf')
        for x, y in points:
            cache.add((x,y)) # hash all points for quicker lookup
        for i in range(len(points)):
            x1, y1 = points[i]
            for j in range(i+1, len(points)): # for every possible pair in points
                x2, y2 = points[j]
                if (x1,y2) in cache and (x2,y1) in cache:
                    area = abs(x1-x2)*abs(y1-y2)
                    if area != 0:
                        minA = min(minA, area)
        return minA if minA<float('inf') else 0
                
                
                
                
                
                
                


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        
        min_area = sys.maxsize
        
        point_set = set([(p[0], p[1]) for p in points])
        
        for i in range(len(points)):
            
            for j in range(i + 1, len(points)):
                
                p1 = points[i]
                
                p2 = points[j]
                
                if p1[0] != p2[0] and p1[1] != p2[1]:
                    
                    p3 = (p1[0], p2[1])
                    
                    p4 = (p2[0], p1[1])
                    
                    if p3 in point_set and p4 in point_set:
                        
                        min_area = min(min_area, abs((p1[0] - p2[0]) * (p1[1] - p2[1])))
                                    
        return min_area if min_area != sys.maxsize else 0
                    
                    
                    
        
        


class Solution:
    def minAreaRect(self, points):
        seen, bases, baseX, res = collections.defaultdict(dict), [], -1, float(\"inf\")
        for x, y in sorted(points):
            if x != baseX:
                baseX, bases = x, []
            for base in bases:
                if y in seen[base]:
                    res = min(res, (x - seen[base][y]) * (y - base))
                seen[base][y] = x
            bases.append(y)
        return res if res < float(\"inf\") else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        S = {(point[0], point[1]) for point in points}
        minArea = math.inf
        for i,point1 in enumerate(points):
            for j in range(i):
                point2 = points[j]
                x1, x2, y1, y2 = point1[0], point2[0], point1[1], point2[1]
                diagPoints = (x1 != x2) and (y1 != y2)
                if diagPoints and (x1, y2) in S and (x2, y1) in S:
                    minArea = min(minArea, abs(x1 - x2) * abs(y1 - y2))
        return minArea if minArea != math.inf else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        by_x = defaultdict(set)
        by_y = defaultdict(set)
        for x, y in points:
            by_x[x].add(y)
            by_y[y].add(x)
        min_area = float(\"inf\")
        for x, ys in by_x.items():
            if len(ys) < 2:
                continue
            for y1 in ys:
                for y2 in ys:
                    if y1 == y2:
                        continue
                    valid_xs = by_y[y1] & by_y[y2]
                    for other_x in valid_xs:
                        if other_x == x:
                            continue
                        min_area = min(min_area, abs(x - other_x) * abs(y1 - y2))
        if min_area == float(\"inf\"):
            return 0
        else:
            return min_area

class Solution:
    def minAreaRect(self, z: List[List[int]]) -> int:
        
        
        
        x=defaultdict(set)
        y=defaultdict(set)
        z.sort()
        #se= set(tuple(i) for i in z)
        for a,b in z:
            x[a].add(b)
            y[b].add(a)
        ans=inf
        l=len(z)
        for k in range(l):
            a,b=z[k]
            for m in range(k+1,l):
                c,d=z[m]
                if a<c:break
                for j in y[b].intersection(y[d]):
                    if j!=a:
                        ans= min(ans, abs(b-d)*abs(a-j))
        return ans if ans!=inf else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        x_grouped = collections.defaultdict(set)

        for x, y in points:
            x_grouped[x].add(y)

        res = float(\"inf\")
        for i in range(len(points)):
            for j in range(i+1, len(points)):
                x1, y1 = points[i]
                x2, y2 = points[j]

                if y2 in x_grouped[x1] and y1 in x_grouped[x2]:
                    width = abs(x1 - x2)
                    height = abs(y1 - y2)
                    if width != 0 and height != 0:
                        res = min(res, width * height)

        if res < float(\"inf\"): return res
        return 0

\"\"\"
sol1 hashmap 存 x:[y1,y2,...], 寻找对角线diagonal上的两点，再检查是否存在剩下的两点
timeO(n^2) spaceO(n^2)
\"\"\"
class Solution:
    \"\"\"sol1 diagonal+hashmap timeO(n^2) spaceO(n^2)\"\"\"
    def minAreaRect(self, points: List[List[int]]) -> int:
        x2y = collections.defaultdict(set)
        minArea = float('inf')
        
        for x,y in points: # hashmap <x, {y1,y2,...}>
            x2y[x].add(y)
        
        # find diagnal (x1,y1) -> (x2,y2)
        for x1,y1 in points:
            for x2,y2 in points:
                if x1==x2 or y1==y2: continue # in the same row/column, cannot form retangle
                elif y2 in x2y[x1] and y1 in x2y[x2]:
                    minArea = min(minArea, abs((x1-x2)*(y1-y2)) )
                
        if minArea<float('inf'): return minArea
        return 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        pointSet = set(map(tuple, points))
        n = len(points)
        minArea = float('inf')
        for i in range(n-1):
            for j in range(i+1, n):
                diag1, diag2 = points[i], points[j]
                if diag2[0] == diag1[0] or diag2[1] == diag1[1]:    continue
                if (diag2[0], diag1[1]) not in pointSet:    continue
                if (diag1[0], diag2[1]) not in pointSet:    continue
                minArea = min(minArea, abs(diag2[0]-diag1[0])*abs(diag2[1]-diag1[1]))
        return minArea if minArea < float('inf') else 0



class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        S = {(point[0], point[1]) for point in points}
        minArea = math.inf
        for i,point1 in enumerate(points):
            for j in range(i):
                point2 = points[j]
                x1, x2, y1, y2 = point1[0], point2[0], point1[1], point2[1]
                diagPoints = (x1 != x2) and (y1 != y2)
                if diagPoints and (x1, y2) in S and (x2, y1) in S:
                    minArea = min(minArea, abs(x1 - x2) * abs(y1 - y2))
        return minArea if minArea != math.inf else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        hash_set = set(map(tuple, points))
        min_ = float('inf')

            
        for j, p2 in enumerate(points):
            for i in range(j):
                p1 = points[i]
                if p1[0] != p2[0] and p1[1] != p2[1]:
                    if (p1[0], p2[1]) in hash_set and (p2[0],p1[1]) in hash_set:
                        min_ = min(min_, abs(p2[0]-p1[0])*abs(p2[1]-p1[1]))
        
        if min_ == float('inf'):
            return 0
        
        return min_

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        S = set((p[0], p[1]) for p in points)
        ans = float('inf')
        for i in range(len(points)):
          for j in range(i):
            p1 = points[i]
            p2 = points[j]
            if p1[0] == p2[0] or p1[1] == p2[1]:
              continue
            if (p1[0], p2[1]) in S and (p2[0], p1[1]) in S:
              ans = min(ans, abs(p2[0] - p1[0]) * abs(p2[1] - p1[1]))
        return ans if ans < float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        x_grouped, y_grouped = collections.defaultdict(set), collections.defaultdict(set)

        for x, y in points:
            x_grouped[x].add(y)

        res = float(\"inf\")
        for i in range(len(points)):
            for j in range(i+1, len(points)):
                x1, y1 = points[i]
                x2, y2 = points[j]

                if y2 in x_grouped[x1] and y1 in x_grouped[x2]:
                    width = abs(x1 - x2)
                    height = abs(y1 - y2)
                    if width != 0 and height != 0:
                        res = min(res, width * height)

        if res < float(\"inf\"): return res
        return 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        x_axes = {}
        y_axes = {}
        
        for point in points:
            if point[0] not in x_axes:
                x_axes[point[0]] = set()
            if point[1] not in y_axes:
                y_axes[point[1]] = set()
            x_axes[point[0]].add(point[1])
            y_axes[point[1]].add(point[0])
        
        def findMinArea(point, x_axes, y_axes):
            # traverse x axes, fix y axis
            possible_x = y_axes[point[1]]
            possible_y = x_axes[point[0]]
            # print(point, possible_x, possible_y)
            min_area = None
            for x in possible_x:
                if x == point[0]:
                    continue
                for y in possible_y:
                    if y == point[1]:
                        continue
                    if x in y_axes[y]:
                        area = abs((x-point[0])*(y-point[1]))
                        # print(area)
                        min_area = area if min_area is None else min(area, min_area)
            return min_area
        
        min_area = None
        for point in points:
            area = findMinArea(point, x_axes, y_axes)
            if area is not None:
                min_area = area if min_area is None else min(min_area, area)
        
        return min_area if min_area else 0


import sys
class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        minArea = sys.maxsize
        
        pointSet = set()
        for point in points:
            pointSet.add((point[0], point[1]))
        
        #print(pointSet)
        for i in range(0, len(points)):
            for j in range(i+1, len(points)):
                point1 = points[i]
                point2 = points[j]
                x1, y1 = point1[0], point1[1]
                x2, y2 = point2[0], point2[1]
                
                # same point
                if x1 == x2 or y1 == y2:
                    continue
                
                if (x1, y2) in pointSet and (x2, y1) in pointSet:
                    
                    area = abs(x2-x1) * abs(y2-y1)
                    minArea = min(minArea, area)
                    
        if minArea == sys.maxsize:
            return 0
        return minArea

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        def hashing(i,j,p=40001):
            return i*p+j
        board = set()
        for x,y in points:
            board.add(hashing(x,y))
        ans = float('inf')
        print(board)
        for i in range(len(points)):
            for j in range(i+1,len(points)):
                tlx,tly = points[i]
                brx,bry = points[j]
                if tlx==brx or tly==bry:   continue
                if hashing(tlx,bry) in board and hashing(brx,tly) in board:
                
                    ans = min(ans,abs((brx-tlx)*(bry-tly)))
        if ans < float('inf'):  return ans
        else:   return 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        y2x = collections.defaultdict(list)
        for x,y in points:
            y2x[y].append(x)
        res = float('inf')
        ys = list(y2x.keys())
        for y1 in ys:
            for y2 in ys:
                if y1!=y2:
                    x1List = y2x[y1]
                    x2Set = y2x[y2]
                    canList = []
                    for x1 in x1List:
                        if x1 in x2Set:
                            canList.append(x1)
                    for m in canList:
                        for n in canList:
                            if m!=n:
                                res = min(res, abs(m-n)*abs(y1-y2))
        if res == float('inf'):
            return 0
        return res

                        


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        n = len(points)
        if n < 4:
            return 0
        
        ptSet = set([(x,y) for x,y in points])
        area = math.inf
        for i in range(n):
            for j in range(i):
                if points[i][0] != points[j][0] and points[i][1] != points[j][1]:
                    if (points[i][0], points[j][1]) in ptSet and (points[j][0], points[i][1]) in ptSet:
                        area = min(area, abs(points[i][0] - points[j][0]) * abs(points[i][1] - points[j][1]))
        
        return area if area < math.inf else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        points.sort()
        n = len(points)
        pmap = {(i, j) for i, j in points}
        area = float('inf')
        for i in range(n):
            for j in range(i+1, n):
                x1 = points[i][0]
                y1 = points[i][1]
                x2 = points[j][0]
                y2 = points[j][1]
                if x1 != x2 and y2 != y1 and x1 != y2 and x2 != y1:
                    if (x1, y2) in pmap and (x2, y1) in pmap:
                        area = min(area, abs(x1-x2) * abs(y2-y1))
        
        return 0 if area == float('inf') else area


class Solution(object):
    def minAreaRect(self, points):
        \"\"\"
        :type points: List[List[int]]
        :rtype: int
        \"\"\"
        min_area = sys.maxsize
        points_table = set()
        
        for x, y in points:
            points_table.add((x,y))
            
        for p1,p2 in itertools.combinations(points_table,2):
               # print (p1,p2)
                x1,y1 = p1
                x2,y2 = p2 
               # if x1 > x2 and y1 > y2: # Skip looking at same point
                if 1:   
                    if (x1, y2) in points_table and (x2, y1) in points_table:
                       # print (\"wew\",p1,p2)
                        area = abs(x1 -  x2) * abs(y1 - y2)
                        if area:
                            min_area = min(area, min_area)
                        
        return 0 if min_area == sys.maxsize else min_area

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        area = float('inf')
        x_order = collections.defaultdict(list)
        y_order = collections.defaultdict(list)
        for p in points:
            x_order[p[0]].append(p[1])
            y_order[p[1]].append(p[0])
        
        xs = sorted(list(x_order.items()), key = lambda x:x[0])
        for col in xs:
            if len(col[1]) < 2:
                continue
            
            for i in range(len(col[1])-1):
                j = i + 1
                while j < len(col[1]):
                    common_x = list(set(y_order[col[1][i]]) & set(y_order[col[1][j]]))
                    # remove the side of the col we hold now
                    common_x.remove(col[0])
                    if common_x:
                        for x in common_x:
                            a = (abs(col[0]-x)*abs(col[1][i] - col[1][j]))
                            area = min(area, a)
                    j += 1
        return area if area < float('inf') else 0


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        n = len(points)
        if n < 4:
          return 0
        s = set([tuple(i) for i in points])
        # print(s)
        minarea = float('inf')
        for i in range(n-1):
          for j in range(i+1, n):
            if points[i][0] != points[j][0] and points[i][1] != points[j][1]:  # not in same line horizontally and vertically
              if (points[j][0], points[i][1]) in s and (points[i][0], points[j][1]) in s:
                # print(i, j)
                minarea = min(minarea, abs(points[i][0] - points[j][0]) * abs(points[i][1] - points[j][1]))

        if minarea == float('inf'):
          return 0
        return minarea

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        if len(points) < 4:
            return 0
        
        area = float('inf')
        corners = set([tuple(x) for x in points])
        for i in range(len(points)):
            for j in range(i + 1, len(points)):
                if points[i][0] != points[j][0] and points[i][1] != points[j][1]:
                    if (points[i][0], points[j][1]) in corners and (points[j][0], points[i][1]) in corners:
                        l, w = abs(points[i][0] - points[j][0]), abs(points[i][1] - points[j][1])
                        area = min(area, l*w)
                    # corners.add(tuple(points[i]))
                    # corners.add(tuple(points[j]))
        
        return 0 if area == float('inf') else area

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
       
        pointsSet = set([(x, y) for x, y in points])
        minv = float('inf')
        for i in range(len(points)): 
            for j in range(i + 1, len(points)):
                if points[i][0] != points[j][0] and points[i][1] != points[j][1] and  (points[i][0], points[j][1]) in pointsSet and (points[j][0], points[i][1]) in pointsSet:
                    minv = min(minv, abs(points[i][0] - points[j][0])*abs(points[i][1] - points[j][1]))
                    if minv == 1: print((points[i], points[j]))
        return minv if minv != float('inf') else 0


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        # Let's make a set of points
        # With two nested loops, find points (x1, y1) and (x2, y2) and use those to see if (x1,y2) and (x2,y1) are in the sets
        points_set = {tuple(p) for p in points}
        res = float('inf')
        for p1 in points:
            for p2 in points:
                # Find diagonal points
                if p1[0] != p2[0] and p1[1] != p2[1]:
                    area = abs(p1[0] - p2[0]) * abs(p1[1] - p2[1])
                    if area < res:
                        if ((p1[0], p2[1]) in points_set and (p2[0], p1[1]) in points_set):
                            res = area
        return 0 if res == float('inf') else res        



class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        pts = set()
        for p in points:
            pts.add((p[0], p[1]))
        ans = float('inf')
        flag = False
        P = len(points)
        for i in range(P):
            for j in range(i+1, P):
                if points[i][0] == points[j][0] or points[i][1] == points[j][1]:
                    continue
                else:
                    dx1 = points[i][0]
                    dy1 = points[j][1]
                    dx2 = points[j][0]
                    dy2 = points[i][1]
                    if (dx1, dy1) in pts and (dx2, dy2) in pts:
                        area = abs(dy1-dy2)*abs(dx1-dx2)
                        ans = min(area, ans)
                        flag = True
        return ans if flag else 0


class Solution:
    def minAreaRect(self, points):
        pSet, ans = set(map(tuple, points)), float('inf')
        for i, (p1x, p1y) in enumerate(points):
            for (p2x, p2y) in points[i+1:]:
                if (p1x != p2x) and (p1y != p2y) and ((p1x, p2y) in pSet) and ((p2x, p1y) in pSet):
                    ans = min(ans, abs((p1x - p2x) * (p1y - p2y)))
        return ans if ans < float('inf') else 0
    
class Solution:
    def minAreaRect(self, points):
        X = list(zip(*points))
        if len(set(X[0])) == 1 or len(set(X[1])) == 1: return 0

        pSet, ans = set(map(tuple, points)), float('inf')
        for i, (p1x, p1y) in enumerate(points):
            for (p2x, p2y) in points[i+1:]:
                area = abs((p2x-p1x)*(p2y-p1y))
                if area > ans or area == 0:
                    continue
                if (p1x, p2y) in pSet and (p2x, p1y) in pSet:
                    ans = area
        return ans if ans < float('inf') else 0
    
from collections import defaultdict
class Solution:
    def minAreaRect(self, points):
        X = list(zip(*points))
        Nr, Nc = len(set(X[0])), len(set(X[1]))
        if Nr == 1 or Nc == 1: return 0

        points.sort()
        columns = defaultdict(list)
        for r, c in points:
            columns[r].append(c)
        ans, lastc = float('inf'), dict()
        for r, cols in list(columns.items()):
            for i, c1 in enumerate(cols):
                for c2 in cols[i+1:]:
                    if (c1, c2) in lastc:
                        area = (r - lastc[(c1, c2)]) * (c2 - c1)
                        #ans = min(ans, area)
                        if area < ans: ans = area
                    lastc[(c1, c2)] = r
        return ans if ans < float('inf') else 0
    
from collections import defaultdict
class Solution:
    def minAreaRect(self, points):
        X = list(zip(*points))
        Nr, Nc = len(set(X[0])), len(set(X[1]))
        if Nr == 1 or Nc == 1: return 0
        if Nr < Nc:
            points = [[c, r] for (r, c) in points]
            return self.minAreaRect(points)
    
        points.sort()
        columns = defaultdict(list)
        for r, c in points:
            columns[r].append(c)
        ans, lastc = float('inf'), dict()
        for r, cols in list(columns.items()):
            for i, c1 in enumerate(cols):
                for c2 in cols[i+1:]:
                    if (c1, c2) in lastc:
                        area = (r - lastc[(c1, c2)]) * (c2 - c1)
                        #ans = min(ans, area)
                        if area < ans: ans = area
                    lastc[(c1, c2)] = r
        return ans if ans < float('inf') else 0
    



class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        hor = defaultdict(list)
        ver = defaultdict(list)
        seen = set()
        area = float(\"inf\")
        for point in points:
            x, y = point
            if len(hor[y]) > 0 and len(ver[x]) > 0:
                for i in ver[x]:
                    for j in hor[y]:
                        if (j, i) in seen:
                            area = min(area, abs((x-j)*(y-i)))
            seen.add((x, y))
            ver[x].append(y)
            hor[y].append(x)
        if area != float('inf'):
            return area
        else:
            return 0

from collections import defaultdict
class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        points_by_x = defaultdict(set)
        # points_by_y = defaultdict(set)
        for point in points:
            points_by_x[point[0]].add(point[1])
            # points_by_y[point[1]].add(point[0])
        
        
        xs_to_delete = []
        for x, ys in points_by_x.items():
            if len(ys) < 2:
                xs_to_delete.append(x)
        for x in xs_to_delete:
            del points_by_x[x] 
        
        \"\"\"
        ys_to_delete = []
        for y, xs in points_by_y.items():
            if len(xs) < 2:
                ys_to_delete.append(y)
        [del points_by_y[y] for y in ys_to_delete]
        \"\"\"
        # [ys.sort() for x, ys in points_by_x.items()]
        # [xs.sort() for y, xs in points_by_y.items()]
        
        minarea = float('inf')
        for x1, ys1 in points_by_x.items():
            for x2, ys2 in points_by_x.items():
                if x1 == x2:
                    continue
                xdiff = abs(x2-x1)
                if xdiff > minarea:
                    continue
                ys_common = sorted(ys1.intersection(ys2))
                if len(ys_common) < 2:
                    continue
                ydiff = min(y2-y1 for y1, y2 in zip(ys_common[:-1], ys_common[1:]))
                area = xdiff*ydiff
                if area < minarea:
                    minarea = area
        return 0 if minarea == float('inf') else minarea
        

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        points.sort(key = lambda x:(x[0], x[1]))
        print(points)
        ver = collections.defaultdict(list)
        hor = collections.defaultdict(list)
        seen = set()
        res = float(\"inf\")
        for x, y in points:            
            if len(ver[x]) > 0 and len(hor[y]) > 0:
                for i in ver[x][::-1]:
                    for j in hor[y][::-1]:
                        if (j, i) in seen:
                            res = min(res, (x - j) * (y - i))
            seen.add((x, y))
            ver[x].append(y)
            hor[y].append(x)
        if res != float(\"inf\"):
            return res
        else:
            return 0
        
        #1 0 1
        #0 1 0
        #1 1 1

from collections import defaultdict
class Solution:
    def minAreaRect(self, points):
        points.sort()
        columns = defaultdict(list)
        for r, c in points:
            columns[r].append(c)
        ans, lastc = float('inf'), dict()
        for r, cols in columns.items():
            for i, c1 in enumerate(cols):
                for c2 in cols[i+1:]:
                    if (c1, c2) in lastc:
                        area = (r - lastc[(c1, c2)]) * (c2 - c1)
                        #ans = min(ans, area)
                        if area < ans: ans = area
                    lastc[(c1, c2)] = r
        return ans if ans < float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        len_x = {x for x, y in points}
        len_y = {y for x, y in points}
        
        if len(len_x) == len(points) or len(len_y) == len(points):
            return 0
        
        memo = collections.defaultdict(list)
        if len_x > len_y:
            for x, y in points:
                memo[x].append(y)
        else:
            for x, y in points:
                memo[y].append(x)
        
        res = float('inf')
        memo2 = {}
        for i in sorted(memo):
            memo[i].sort()
            for j in range(len(memo[i])):
                for k in range(j+1, len(memo[i])):
                    if (memo[i][j], memo[i][k]) in memo2:
                        res = min(res, ((memo[i][k]-memo[i][j]) * abs(memo2[(memo[i][j], memo[i][k])] - i)))
                    
                    memo2[memo[i][j], memo[i][k]] = i
        
        return res if res < float('inf') else 0
    
    
#         n = len(points)
#         nx = len(set(x for x, y in points))
#         ny = len(set(y for x, y in points))
#         if nx == n or ny == n:
#             return 0

#         p = collections.defaultdict(list)
#         if nx > ny:
#             for x, y in points:
#                 p[x].append(y)
#         else:
#             for x, y in points:
#                 p[y].append(x)

#         lastx = {}
#         res = float('inf')
#         for x in sorted(p):
#             p[x].sort()
#             for i in range(len(p[x])):
#                 for j in range(i):
#                     y1, y2 = p[x][j], p[x][i]
#                     if (y1, y2) in lastx:
#                         res = min(res, (x - lastx[y1, y2]) * abs(y2 - y1))
#                     lastx[y1, y2] = x
#         return res if res < float('inf') else 0


from collections import defaultdict
from bisect import insort
class Solution:
    def minAreaRect(self, points):
        points.sort()
        columns = defaultdict(list)
        for r, c in points:
            insort(columns[r], c)
        ans, lastc = float('inf'), dict()
        for r, cols in columns.items():
            for i, c1 in enumerate(cols):
                for c2 in cols[i+1:]:
                    if (c1, c2) in lastc:
                        area = (r - lastc[(c1, c2)]) * (c2 - c1)
                        #ans = min(ans, area)
                        if area < ans: ans = area
                    lastc[(c1, c2)] = r
        return ans if ans < float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        columns = collections.defaultdict(list)
        for x, y in points:
            columns[x].append(y)
        ans = float(\"inf\")
        seen = {}
        for x in sorted(columns):
            col = sorted(columns[x])
            for j, y2 in enumerate(col):
                for i in range(j):
                    y1 = col[i]
                    key = (y1, y2)
                    if key in seen:
                        ans = min(ans, (x - seen[key]) * (y2-y1))
                    seen[key] = x
        return ans if ans < float('inf') else 0

from collections import defaultdict
class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        columns = defaultdict(list)
        for x, y in points:
            columns[x].append(y)
        seen = {}
        min_area = float(\"inf\")
        for col, ys in sorted(columns.items()):
            ys = sorted(ys)
            for i, y1 in enumerate(ys):
                for y2 in ys[i+1:]:
                    if (y1, y2) in seen:
                        #we have a rectangle
                        x1 = seen[(y1, y2)]
                        area = (col -x1) * (y2-y1)
                        min_area = min(min_area, area)
                        
                        
                    seen[(y1, y2)] = col
        return 0 if min_area == float(\"inf\") else min_area
                    
        

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        points.sort()
        xs = collections.defaultdict(list)
        for x, y in points:
            xs[x].append(y)
        seen = {}
        result = math.inf
        for x, ylist in xs.items():
            for ys in itertools.combinations(ylist, 2):
                if ys in seen:
                    result = min(result, (x - seen[ys]) * (ys[1] - ys[0]))
                seen[ys] = x
        return 0 if math.isinf(result) else  result

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        n = len(points)
        nx = len(set(x for x, y in points))
        ny = len(set(y for x, y in points))
        if nx == n or ny == n:
            return 0

        p = collections.defaultdict(list)
        if nx > ny:
            for x, y in points:
                p[x].append(y)
        else:
            for x, y in points:
                p[y].append(x)

        lastx = {}
        res = float('inf')
        for x in sorted(p):
            p[x].sort()
            for i in range(len(p[x])):
                for j in range(i):
                    y1, y2 = p[x][j], p[x][i]
                    if (y1, y2) in lastx:
                        res = min(res, (x - lastx[y1, y2]) * abs(y2 - y1))
                    lastx[y1, y2] = x
        return res if res < float('inf') else 0

class Solution(object):
    def minAreaRect(self, points):
        y_group=collections.defaultdict(list)
        for x, y in points:
            y_group[x].append(y)
            
        visited={}
        area=inf
        
        for x in sorted(y_group):
            Y=y_group[x]
            Y.sort()
            for i in range(len(Y)):
                y1= Y[i]
                for j in range(i):
                    y2=Y[j]
                    if (y1, y2) in visited:
                        area=min((x - visited[(y1, y2)])* (y1 - y2), area)
                    visited[(y1, y2)]=x
        return area if area<inf else 0
                    


class Solution(object):
    def minAreaRect(self, points):
        columns = collections.defaultdict(list)
        for x, y in points:
            columns[x].append(y)
        lastx = {}
        ans = float('inf')

        for x in sorted(columns):
            column = columns[x]
            column.sort()
            for j, y2 in enumerate(column):
                for i in range(j):
                    y1 = column[i]
                    if (y1, y2) in lastx:
                        ans = min(ans, (x - lastx[y1,y2]) * (y2 - y1))
                    lastx[y1, y2] = x
        return ans if ans < float('inf') else 0
# class Solution:
#     def minAreaRect(self, points: List[List[int]]) -> int:
#         d_x = collections.defaultdict(set)
#         d_y = collections.defaultdict(set)
#         for x, y in points: 
#             d_x[x].add(y)
#             d_y[y].add(x)
#         area = sys.maxsize            
#         for x, y in points:
#             for yy in d_x[x]: # find same x (point above), larger y
#                 if yy <= y: continue
#                 for xx in d_y[y]: # find same y (point right), larget x
#                     if xx > x and yy in d_x[xx]:
#                         area = min(area, (xx-x)*(yy-y))
#         return area if area != sys.maxsize else 0


from collections import defaultdict
class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        columns = defaultdict(list)
        for x, y in points:
            columns[x].append(y)
        seen = {}
        min_area = float(\"inf\")
        for col, ys in sorted(columns.items()):
            print(col, ys)
            ys = sorted(ys)
            for i, y1 in enumerate(ys):
                for y2 in ys[i+1:]:
                    if (y1, y2) in seen:
                        #we have a rectangle
                        x1 = seen[(y1, y2)]
                        area = (col -x1) * (y2-y1)
                        min_area = min(min_area, area)
                        
                        
                    seen[(y1, y2)] = col
        return 0 if min_area == float(\"inf\") else min_area
                    
        

\"\"\"
https://www.youtube.com/watch?v=pFgBZFKJ2Co
fast forward to 17minutes

Group the points by x coordinates, so that we have columns of points. Then, for every pair of points in a column (with coordinates (x,y1) and (x,y2)), check for the smallest rectangle with this pair of points as the rightmost edge. We can do this by keeping memory of what pairs of points we've seen before.

Input: [[1,1],[1,3],[3,1],[3,3],[4,1],[4,3]]
Output: 2

columns = {
  # x: y1, y1
    1: 1, 3
    3: 1, 3
    4: 1, 3
}

lastx {
    (1,3) : 1 -> 3      # (y1, y2) : x axis
}
            i
            y2
column = [1,3]
            j
            y1 
\"\"\"
class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        if len(points) <= 3: return 0
        columns = collections.defaultdict(list)
        for x,y in points:
            columns[x].append(y)
        lastx = {}
        ans = float('inf')
        
        for x in sorted(columns):
            column = columns[x]
            column.sort()
            for i, y2 in enumerate(column):
                for j in range(i):
                    y1 = column[j]
                    if (y1, y2) in lastx:
                        area = (x - lastx[y1, y2]) * (y2 - y1)
                        ans = min(ans, area)
                    lastx[y1, y2] = x
        
        return ans if ans < float('inf') else 0
            

'''
939. Minimum Area Rectangle.  Medium

Given a set of points in the xy-plane, 
determine the minimum area of a rectangle
formed from these points, with sides parallel
to the x and y axes.

If there isn't any rectangle, return 0.

Example 1:
Input: [[1,1],[1,3],[3,1],[3,3],[2,2]]
Output: 4

Example 2:
Input: [[1,1],[1,3],[3,1],[3,3],[4,1],[4,3]]
Output: 2

Note:
1 <= points.length <= 500
0 <= points[i][0] <= 40000
0 <= points[i][1] <= 40000
All points are distinct.

Accepted
55,377
Submissions
107,280

NOTE: Clarify: rectangle formed by ALL these points (convex hull)
            v. rectangle formed by ANY four distinct points
'''
class Solution:
    def minAreaRectangularHull(self, points: List[List[int]]) -> int:
        area = 0
        if points:
            minx, miny = points[0]
            maxx, maxy = points[0]
            for x, y in points:
                minx = min(minx, x)
                miny = min(miny, y)
                maxx = max(maxx, x)
                maxy = max(maxy, y)
            area = (maxx - minx)*(maxy - miny)
        return area
                

    def minAreaRect(self, points):
        '''
        Runtime: 1628 ms, faster than 38.40% of Python3 online submissions for Minimum Area Rectangle.
        Memory Usage: 14.4 MB, less than 17.19% of Python3 online submissions for Minimum Area Rectangle.
        '''
        S = set(map(tuple, points))
        ans = float('inf')
        for k, (xB, yB) in enumerate(points):
            for j in range(k):
                xA, yA = points[j]
                if (xA != xB and yA != yB and (xA, yB) in S and (xB, yA) in S):
                    ans = min(ans, abs((xB - xA) * (yB - yA)))
        return ans if ans < float('inf') else 0
    

class Solution:
    '''
    Runtime: 688 ms, faster than 88.20% of Python3 online submissions for Minimum Area Rectangle.
    Memory Usage: 31.7 MB, less than 17.19% of Python3 online submissions for Minimum Area Rectangle.
    '''
    def minAreaRect(self, points):
        columns = collections.defaultdict(set)
        for x, y in points:
            columns[x].add(y)
        lastx = {}
        ans = float('inf')

        for x in sorted(columns):
            column = sorted(columns[x])
            for j, y2 in enumerate(column):
                for i in range(j):
                    y1 = column[i]
                    if (y1, y2) in lastx:
                        ans = min(ans, (x - lastx[y1,y2]) * (y2 - y1))
                    lastx[y1, y2] = x
        return ans if ans < float('inf') else 0


from collections import defaultdict

class Solution(object):
    def minAreaRect(self, points):
        columns = collections.defaultdict(list)
        for x, y in points:
            columns[x].append(y)
        lastx = {}
        ans = float('inf')

        for x in sorted(columns):
            column = columns[x]
            column.sort()
            for j, y2 in enumerate(column):
                for i in range(j):
                    y1 = column[i]
                    if (y1, y2) in lastx:
                        ans = min(ans, (x - lastx[y1,y2]) * (y2 - y1))
                    lastx[y1, y2] = x
        return ans if ans < float('inf') else 0

'''
939. Minimum Area Rectangle.  Medium

Given a set of points in the xy-plane, 
determine the minimum area of a rectangle
formed from these points, with sides parallel
to the x and y axes.

If there isn't any rectangle, return 0.

Example 1:
Input: [[1,1],[1,3],[3,1],[3,3],[2,2]]
Output: 4

Example 2:
Input: [[1,1],[1,3],[3,1],[3,3],[4,1],[4,3]]
Output: 2

Note:
1 <= points.length <= 500
0 <= points[i][0] <= 40000
0 <= points[i][1] <= 40000
All points are distinct.

Accepted
55,377
Submissions
107,280

NOTE: Clarify: rectangle formed by ALL these points (convex hull)
            v. rectangle formed by ANY four distinct points
'''
class Solution:
    def minAreaRectangularHull(self, points: List[List[int]]) -> int:
        area = 0
        if points:
            minx, miny = points[0]
            maxx, maxy = points[0]
            for x, y in points:
                minx = min(minx, x)
                miny = min(miny, y)
                maxx = max(maxx, x)
                maxy = max(maxy, y)
            area = (maxx - minx)*(maxy - miny)
        return area
                

    def minAreaRect(self, points):
        '''
        Runtime: 1628 ms, faster than 38.40% of Python3 online submissions for Minimum Area Rectangle.
        Memory Usage: 14.4 MB, less than 17.19% of Python3 online submissions for Minimum Area Rectangle.
        '''
        S = set(map(tuple, points))
        ans = float('inf')
        for k, (xB, yB) in enumerate(points):
            for j in range(k):
                xA, yA = points[j]
                if (xA != xB and yA != yB and (xA, yB) in S and (xB, yA) in S):
                    ans = min(ans, abs((xB - xA) * (yB - yA)))
        return ans if ans < float('inf') else 0
    

class Solution:
    '''
    Runtime: 688 ms, faster than 88.20% of Python3 online submissions for Minimum Area Rectangle.
    Memory Usage: 31.7 MB, less than 17.19% of Python3 online submissions for Minimum Area Rectangle.
    '''
    def minAreaRect(self, points):
        columns = collections.defaultdict(list)
        for x, y in points:
            columns[x].append(y)
        lastx = {}
        ans = float('inf')

        for x in sorted(columns):
            column = sorted(columns[x])
            # column.sort()
            for j, y2 in enumerate(column):
                for i in range(j):
                    y1 = column[i]
                    if (y1, y2) in lastx:
                        ans = min(ans, (x - lastx[y1,y2]) * (y2 - y1))
                    lastx[y1, y2] = x
        return ans if ans < float('inf') else 0


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        columns=collections.defaultdict(list)
        for x,y in points:
            columns[x].append(y)
        
        lastx={}
        ans=float('inf')
        for x in sorted(columns):
            column=columns[x]
            column.sort()
            for j, y2 in enumerate(column):
                for i in range(j):
                    y1=column[i]
                    if (y1,y2) in lastx:
                        ans=min(ans,(x-lastx[y1,y2])*(y2-y1))
                    lastx[y1,y2]=x
            
        return ans if ans<float('inf') else 0

'''
939. Minimum Area Rectangle.  Medium

Given a set of points in the xy-plane,
determine the minimum area of a rectangle
formed from these points, with sides parallel
to the x and y axes.

If there isn't any rectangle, return 0.

Example 1:
Input: [[1, 1], [1, 3], [3, 1], [3, 3], [2, 2]]
Output: 4

Example 2:
Input: [[1, 1], [1, 3], [3, 1], [3, 3], [4, 1], [4, 3]]
Output: 2

Note:
1 <= points.length <= 500
0 <= points[j][0] <= 40000
0 <= points[j][1] <= 40000
All points are distinct.

Accepted 55,377 / 107,280 submissions.

NOTE: Clarify: rectangle formed by ALL these points (convex hull)
            v. rectangle formed by ANY four distinct points
'''
###############################################################################

class SolutionDefDictSort:      # Faster
    '''
    Runtime: 664 ms, faster than 90.46% of Python3 online submissions for Minimum Area Rectangle.
    Memory Usage: 31.7 MB, less than 20.13% of Python3 online submissions for Minimum Area Rectangle.
    '''
    def minAreaRect(self, points):
        columns = collections.defaultdict(set)
        for x, y in points:
            columns[x].add(y)
        lastx = {}
        ans = float('inf')

        for x in sorted(columns):
            column = sorted(columns[x])
            for j, y2 in enumerate(column):
                for i in range(j):
                    y1 = column[i]
                    if (y1, y2) in lastx:
                        ans = min(ans, (x - lastx[y1,y2]) * (y2 - y1))
                    lastx[y1, y2] = x
        return ans if ans < float('inf') else 0

###############################################################################

class SolutionTupleSet:         # Slower
    '''
    Runtime: 1628 ms, faster than 38.40% in Python3.
    Memory Usage: 14.4 MB, less than 17.19% in Python3.
    '''

    def minAreaRect(self, points):
        S = set(map(tuple, points))
        ans = float('inf')
        for k, (xB, yB) in enumerate(points):
            for j in range(k):
                xA, yA = points[j]
                if (xA != xB and yA != yB and (xA, yB) in S and (xB, yA) in S):
                    ans = min(ans, abs((xB - xA) * (yB - yA)))
        return ans if ans < float('inf') else 0


    def minAreaRectangularHull(self, points: List[List[int]]) -> int:
        '''
        NOTE: Not what the question was intended to meant!
        '''
        area = 0
        if points:
            minx, miny = points[0]
            maxx, maxy = points[0]
            for x, y in points:
                minx = min(minx, x)
                miny = min(miny, y)
                maxx = max(maxx, x)
                maxy = max(maxy, y)
            area = (maxx - minx)*(maxy - miny)
        return area

###############################################################################

class Solution:
    pass

Solution = SolutionTupleSet
Solution = SolutionDefDictSort


'''
939. Minimum Area Rectangle.  Medium

Given a set of points in the xy-plane, 
determine the minimum area of a rectangle
formed from these points, with sides parallel
to the x and y axes.

If there isn't any rectangle, return 0.

Example 1:
Input: [[1,1],[1,3],[3,1],[3,3],[2,2]]
Output: 4

Example 2:
Input: [[1,1],[1,3],[3,1],[3,3],[4,1],[4,3]]
Output: 2

Note:
1 <= points.length <= 500
0 <= points[i][0] <= 40000
0 <= points[i][1] <= 40000
All points are distinct.

Accepted
55,377
Submissions
107,280

NOTE: Clarify: rectangle formed by ALL these points (convex hull)
            v. rectangle formed by ANY four distinct points
'''
class Solution:
    def minAreaRectangularHull(self, points: List[List[int]]) -> int:
        area = 0
        if points:
            minx, miny = points[0]
            maxx, maxy = points[0]
            for x, y in points:
                minx = min(minx, x)
                miny = min(miny, y)
                maxx = max(maxx, x)
                maxy = max(maxy, y)
            area = (maxx - minx)*(maxy - miny)
        return area
                

    def minAreaRect(self, points):
        '''
        Runtime: 1628 ms, faster than 38.40% of Python3 online submissions for Minimum Area Rectangle.
        Memory Usage: 14.4 MB, less than 17.19% of Python3 online submissions for Minimum Area Rectangle.
        '''
        S = set(map(tuple, points))
        ans = float('inf')
        for k, (xB, yB) in enumerate(points):
            for j in range(k):
                xA, yA = points[j]
                if (xA != xB and yA != yB and (xA, yB) in S and (xB, yA) in S):
                    ans = min(ans, abs((xB - xA) * (yB - yA)))
        return ans if ans < float('inf') else 0
    
class Solution(object):
    def minAreaRect(self, points):
        columns = collections.defaultdict(list)
        for x, y in points:
            columns[x].append(y)
        lastx = {}
        ans = float('inf')

        for x in sorted(columns):
            column = columns[x]
            column.sort()
            for j, y2 in enumerate(column):
                for i in range(j):
                    y1 = column[i]
                    if (y1, y2) in lastx:
                        ans = min(ans, (x - lastx[y1,y2]) * (y2 - y1))
                    lastx[y1, y2] = x
        return ans if ans < float('inf') else 0


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        columns = collections.defaultdict(list)

        for x, y in points:
            columns[x].append(y)

        lastx = {}
        ans = float(\"inf\")

        for x in sorted(columns):
            column = columns[x]
            column.sort()

            for j, y2 in enumerate(column):
                for i in range(j):
                    y1 = column[i]
                    if (y1, y2) in lastx:
                        ans = min(ans, (x-lastx[y1, y2]) * (y2-y1))

                    lastx[y1, y2] = x

        return ans if ans < float(\"inf\") else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        # store y coordinates at each x
        # for each y1 y2 pairs at each x from left to right
        # find the last x that has y1 and y2, and calculate area
        
        lastx = {}
        everyx = collections.defaultdict(list)
        min_area = math.inf
        for x, y in points:
            everyx[x].append(y)
        for x in sorted(everyx):
            everyy = sorted(everyx[x])
            for i, y1 in enumerate(everyy):
                for j in range(i):
                    y2 = everyy[j]
                    if (y1, y2) in lastx:
                        area = abs(y2-y1) * abs(x - lastx[y1, y2])
                        min_area = min(area, min_area)
                    lastx[y1,y2] = x
        return min_area if min_area < math.inf else 0


from collections import defaultdict
from bisect import insort
class Solution:
    def minAreaRect(self, points):
       # setr, setc = set(), set()
       # for r, c in points:
       #     setr.add(r)
       #     setc.add(c)
       # if len(points) in (len(setr), len(setc)): return 0
        
        points.sort()
        columns = defaultdict(list)
        for r, c in points:
            insort(columns[r], c)
        ans, lastc = float('inf'), dict()
        for r, cols in list(columns.items()):
            #cols = sorted(columns[r])
            for j, y2 in enumerate(cols):
                for i, y1 in enumerate(cols[:j]):
                    if (y1, y2) in lastc:
                        ans = min(ans, (r - lastc[(y1, y2)]) * (y2 - y1))
                    lastc[(y1, y2)] = r
        return ans if ans < float('inf') else 0


class Solution:
    def minAreaRect(self, points):
        X = list(zip(*points))
        if 1 in (len(set(X[0])), len(set(X[1]))): return 0
        seen, ans = set(map(tuple, points)), float('inf')
        for i, (p1x, p1y) in enumerate(points):
            for (p2x, p2y) in points[i+1:]:
                if (p1x != p2x) and (p1y != p2y):
                    area = abs((p2x-p1x)*(p2y-p1y))
                    if area > ans or area == 0:
                        continue
                    if (p1x, p2y) in seen and (p2x, p1y) in seen:
                        ans = area
        return ans if ans < float('inf') else 0

\"\"\"
https://www.youtube.com/watch?v=pFgBZFKJ2Co
fast forward to 17minutes

Group the points by x coordinates, so that we have columns of points. Then, for every pair of points in a column (with coordinates (x,y1) and (x,y2)), check for the smallest rectangle with this pair of points as the rightmost edge. We can do this by keeping memory of what pairs of points we've seen before.

columns = {
    1: 1, 3
    3: 1, 3
    4: 1, 4
    
}

column = [1,3]
\"\"\"
class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        if len(points) <= 3: return 0
        columns = collections.defaultdict(list)
        for x, y in points:
            columns[x].append(y)
        lastx = {}
        ans = float('inf')

        for x in sorted(columns):
            column = columns[x]
            column.sort()
            for i, y2 in enumerate(column):
                for j in range(i):
                    y1 = column[j]
                    if (y1, y2) in lastx:
                        area = (x - lastx[y1,y2]) * (y2 - y1)
                        ans = min(ans, area)
                    lastx[y1, y2] = x
        return ans if ans < float('inf') else 0

from collections import defaultdict

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        result = float(\"inf\")
        found = False
        
        point_by_x = defaultdict(set)
        
        for x, y in points:
            point_by_x[x].add(y)
            
        y_pair_to_x = dict()
        sorted_x = sorted(list(point_by_x.keys()))

        for x in sorted_x:
            y_list = sorted(list(point_by_x[x]))
            
            for i in range(len(y_list)):
                for j in range(i+1, len(y_list)):
                    v1, v2 = y_list[i], y_list[j]
                    if (v1, v2) in y_pair_to_x:
                        width = v2 - v1
                        height = x - y_pair_to_x[(v1, v2)]
                        result = min(result, width * height)
                        found = True
                    y_pair_to_x[(v1, v2)] = x
                    
        if not found:
            return 0
        return result
                    
        
        
        

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        
        points_dict = collections.defaultdict(list)
        for x, y in points:
            points_dict[x].append(y)
        points_dict = dict(sorted(list(points_dict.items()), key=lambda x:x[0]))
        seen_ys = dict()
        
        result = float('inf')
        
        for x in list(points_dict.keys()):
            ys = sorted(points_dict[x])
            print((x, ys))
            
            for i, y2 in enumerate(ys):
                
                for j in range(i):
                    y1 = ys[j]
                
                    if (y1, y2) in seen_ys:
                        result = min(result, (y2-y1)*(x-seen_ys[(y1, y2)]))

                    seen_ys[(y1, y2)] = x
        return result if result < float('inf') else 0


from collections import defaultdict

class Solution(object):
    def minAreaRect(self, points):
        x_points = defaultdict(list)
        for x,y in points:
            x_points[x].append(y)
        min_area = float('inf')
        vert_lines = {}
        sort_by_x = sorted(x_points)
        for x in sort_by_x:
            y_list = x_points[x]
            y_list.sort()
            for i in range(len(y_list)):
                y2 = y_list[i]
                for j in range(i):
                    y1 = y_list[j]
                    if (y1,y2) in vert_lines:
                        x2 = vert_lines[(y1,y2)]
                        min_area = min(min_area, abs(x - x2)* abs(y1-y2))
                    vert_lines[(y1,y2)] = x
        return min_area if min_area != float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        collection = collections.defaultdict(list)
        for x, y in points:
            collection[x].append(y)
        result = float('inf')
        previousx = {}
        for x in sorted(collection):
            column = collection[x]
            column.sort()
            for j, y2 in enumerate(column):
                
                for i in range(j):
                    
                    y1 = column[i]
                    if (y1, y2) in previousx:
                        result = min(result, ((x-previousx[y1,y2]) * (y2-y1)))
                                     
                    previousx[y1,y2] = x
                                    
        if result < float('inf'):
               return result
        return 0
                                
            


from collections import defaultdict
class Solution:
    def minAreaRect(self, points):
        setr, setc = set(), set()
        for r, c in points:
            setr.add(r)
            setc.add(c)
        if len(points) in (len(setr), len(setc)): return 0
        
        
        columns = defaultdict(list)
        for r, c in points:
            columns[r].append(c)
        ans, lastc = float('inf'), dict()
        for r in sorted(columns):
            cols = sorted(columns[r])
            for j, y2 in enumerate(cols):
                for i, y1 in enumerate(cols[:j]):
                    if (y1, y2) in lastc:
                        ans = min(ans, (r - lastc[(y1, y2)]) * (y2 - y1))
                    lastc[(y1, y2)] = r
        return ans if ans < float('inf') else 0


                              
class Solution:
    def minAreaRect(self, points):
        X = list(zip(*points))
        if len(points) in (len(set(X[0])), len(set(X[1]))): return 0
        seen, ans = set(map(tuple, points)), float('inf')
        points.sort()
        for i, (p1x, p1y) in enumerate(points):
            for (p2x, p2y) in points[i+1:]:
                area = abs((p2x-p1x)*(p2y-p1y))
                if area > ans or area == 0:
                    continue
                if (p1x, p2y) in seen and (p2x, p1y) in seen:
                    ans = area
        return ans if ans < float('inf') else 0


class Solution:
    def minAreaRect(self, points):
        X = list(zip(*points))
        if len(set(X[0])) == 1 or len(set(X[1])) == 1: return 0
        
        seen, ans = set(map(tuple, points)), float('inf')
        for i, (p1x, p1y) in enumerate(points):
            for (p2x, p2y) in points[i+1:]:
                if (p1x != p2x) and (p1y != p2y):
                    area = abs((p2x-p1x)*(p2y-p1y))
                    if area > ans or area == 0:
                        continue
                    if (p1x, p2y) in seen and (p2x, p1y) in seen:
                        ans = area
        return ans if ans < float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        
        def smallestinterval(l):
            rst = float('inf')
            for i in range(1, len(l)):
                if l[i] - l[i-1] < rst:
                    rst = l[i] - l[i-1]
            return rst
            
        xloc = defaultdict(set)
#        yloc = defaultdict(set())
        for p in points:
            xloc[p[0]].add(p[1])
#            yloc[p[1]].add(p[0])
        minarea = float('inf')
        xkeys = list(xloc.keys())
        for x1 in range(len(xkeys)):
            for x2 in range(x1):
                if x1 != x2:
                    sharedy = xloc[xkeys[x1]] & xloc[xkeys[x2]]
                    if len(sharedy)> 1:
                        ally = list(sharedy)
                        ally.sort()
                        smallinterval = smallestinterval(ally)
                        if smallinterval * abs(xkeys[x1] - xkeys[x2]) < minarea:
                            minarea = smallinterval * abs(xkeys[x1]-xkeys[x2])
        if minarea == float('inf'):
            return 0
        return minarea
        


class Solution:
    def minAreaRect(self, points):
        X = list(zip(*points))
        if len(set(X[0])) == 1 or len(set(X[1])) == 1: return 0
        
        seen, ans = set(map(tuple, points)), float('inf')
        for i, (p1x, p1y) in enumerate(points):
            for (p2x, p2y) in points[i+1:]:
                area = abs((p2x-p1x)*(p2y-p1y))
                if area > ans or area == 0:
                    continue
                if (p1x, p2y) in seen and (p2x, p1y) in seen:
                    ans = area
        return ans if ans < float('inf') else 0

class Solution:
    def minAreaRect(self, points):
        X = list(zip(*points))
        if len(set(X[0])) == 1 or len(set(X[1])) == 1: return 0
        seen, ans = set(map(tuple, points)), float('inf')
        for i, (p1x, p1y) in enumerate(points):
            for (p2x, p2y) in points[i+1:]:
                if (p1x != p2x) and (p1y != p2y):
                    area = abs((p2x-p1x)*(p2y-p1y))
                    if area > ans or area == 0:
                        continue
                    if (p1x, p2y) in seen and (p2x, p1y) in seen:
                        ans = area
        return ans if ans < float('inf') else 0

class Solution:
    def minAreaRect(self, points):
        pSet, ans = set(map(tuple, points)), float('inf')
        for i, (p1x, p1y) in enumerate(points):
            for (p2x, p2y) in points[i+1:]:
                if (p1x != p2x) and (p1y != p2y) and ((p1x, p2y) in pSet) and ((p2x, p1y) in pSet):
                    ans = min(ans, abs((p1x - p2x) * (p1y - p2y)))
        return ans if ans < float('inf') else 0
    
class Solution:
    def minAreaRect(self, points):
        X = list(zip(*points))
        if len(set(X[0])) == 1 or len(set(X[1])) == 1: return 0

        pSet, ans = set(map(tuple, points)), float('inf')
        for i, (p1x, p1y) in enumerate(points):
            for (p2x, p2y) in points[i+1:]:
                area = abs((p2x-p1x)*(p2y-p1y))
                if area > ans or area == 0:
                    continue
                if (p1x, p2y) in pSet and (p2x, p1y) in pSet:
                    ans = area
        return ans if ans < float('inf') else 0
    


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        
        def smallestinterval(l):
            rst = float('inf')
            for i in range(1, len(l)):
                if l[i] - l[i-1] < rst:
                    rst = l[i] - l[i-1]
            return rst
            
        xloc = defaultdict(set)
        for p in points:
            xloc[p[1]].add(p[0])
        minarea = float('inf')
        xkeys = list(xloc.keys())
        for x1 in range(len(xkeys)):
            for x2 in range(x1):
                if x1 != x2:
                    sharedy = xloc[xkeys[x1]] & xloc[xkeys[x2]]
                    if len(sharedy)> 1:
                        ally = list(sharedy)
                        ally.sort()
                        smallinterval = smallestinterval(ally)
                        if smallinterval * abs(xkeys[x1] - xkeys[x2]) < minarea:
                            minarea = smallinterval * abs(xkeys[x1]-xkeys[x2])
        if minarea == float('inf'):
            return 0
        return minarea
        


from collections import defaultdict
class Solution:
    def minAreaRect(self, points):
        setr, setc = set(), set()
        for r, c in points:
            setr.add(r)
            setc.add(c)
        if len(points) in (len(setr), len(setc)): return 0
        
        
        columns = defaultdict(list)
        for r, c in points:
            columns[r].append(c)
        ans, lastc = float('inf'), dict()
        for r in sorted(columns):
            cols = sorted(columns[r])
            for j, y2 in enumerate(cols):
                for i, y1 in enumerate(cols[:j]):
                    if (y1, y2) in lastc:
                        ans = min(ans, (r - lastc[(y1, y2)]) * (y2 - y1))
                    lastc[(y1, y2)] = r
        return ans if ans < float('inf') else 0


                              
class Solution:
    def minAreaRect(self, points):
        X = list(zip(*points))
        if len(points) in (len(set(X[0])), len(set(X[1]))): return 0
        seen, ans = set(map(tuple, points)), float('inf')
        for i, (p1x, p1y) in enumerate(points):
            for (p2x, p2y) in points[i+1:]:
                area = abs((p2x-p1x)*(p2y-p1y))
                if area > ans or area == 0:
                    continue
                if (p1x, p2y) in seen and (p2x, p1y) in seen:
                    ans = area
        return ans if ans < float('inf') else 0


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        n = len(points)
        nx, ny = len(set(x for x, _ in points)), len(set(y for _, y in points))
        
        if nx == n or ny == n:
            return 0
        p = collections.defaultdict(list)
        if nx > ny:
            for x, y in points:
                p[x].append(y)
        else:
            for x, y in points:
                p[y].append(x)
        
        res = float('inf')
        dic_last = {}
        for x in sorted(p):
            # print(x)
            p[x].sort()
            for y1, y2 in itertools.combinations(p[x], 2):
                if (y1, y2) in dic_last:
                    res = min(res, abs( x - dic_last[y1, y2]) * abs(y1 -y2))
                    # if res == 2:
                    #     print(x, dic_last[y1, y2],  y1, y2)
                dic_last[y1, y2] = x
        return res if res < float('inf') else 0
        
        # n = len(points)
        # nx, ny = len(set(x for x, y in points)), len(set(y for x, y in points))
        # if nx == n or ny == n:
        #     return 0
        
        # p = collections.defaultdict(list)
        # if nx > ny:
        #     for x, y in points:
        #         p[x].append(y)
        # else:
        #     for x, y in points:
        #         p[y].append(x)
        # res = float('inf')
        # dic = {}
        # for x in sorted(p):
        #     p[x].sort()
        #     for y1, y2 in itertools.combinations(p[x], 2):
        #         if (y1, y2) in dic:
        #             res = min(res, abs(x - dic[y1, y2]) * abs(y1 - y2) )
        #         dic[y1, y2] = x
        # return res if res < float('inf') else 0
#         dic_x = {}
        
#         n = len(points)
#         nx, ny = len( set(x for x, y in points) ), len( set(y for x, y in points) )
#         if nx == n or ny == n:
#             return 0
        
#         p = collections.defaultdict(list)
#         if nx > ny:
#             for x, y in points:
#                 p[x].append(y)
#         else:
#             for x, y in points:
#                 p[y].append(x)
#         res = float('inf')
#         for x in sorted(p):
#             p[x].sort()
            
#             for y1, y2 in itertools.combinations(p[x], 2):
#                 # y1, y2 = p[x][i], p[x][j]
#                 if (y1, y2) in dic_x:
#                     res = min(res, abs(x - dic_x[y1, y2]) * abs(y1 -y2))
#                 dic_x[y1, y2] = x
#         return res if res < float('inf') else 0
#         seen = set()
        
#         res = float('inf')
#         for x1, y1 in points:
#             for x2, y2 in seen:
#                 if (x1, y2) in seen and (x2, y1) in seen:
#                     area = abs(x1 - x2) * abs(y1 - y2)
#                     if area and res > area:
#                         res = area
#                     # res = min(res, )
#             seen.add((x1, y1))
#         return res if res < float('inf') else 0


from collections import defaultdict

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        n = len(points)
        if n < 4:
            return 0
        
        d = defaultdict(set)
        for (x, y) in points:
            d[x].add(y)
                
        xs = sorted(d.keys())
        # print(d, xs)
        ans = float('inf')    
        
        exist = False
        for i in range(len(xs) - 1):
            x1 = xs[i]
            ys1 = d[x1]
            for j in range(i+1, len(xs)):
                x2 = xs[j]
                ys2 = d[x2]
                ys = sorted(ys1 & ys2)
                # print(x1, x2, ys)
                if len(ys) >= 2:
                    exist = True
                    minHeight = ys[1] - ys[0]
                    for j in range(2, len(ys)):
                        minHeight = min(minHeight, ys[j] - ys[j-1])
                    ans = min(ans, minHeight * (x2-x1))
            
        return 0 if not exist else ans

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        columns = collections.defaultdict(list)
        res = float('inf')
        
        for x,y in points:
            columns[x].append(y)
        y_pairs = dict()
        for x in sorted(columns):
            ys = sorted(columns[x])
            # print('ys',ys,x)
            for i in range(len(ys)-1):
                for j in range(i+1,len(ys)):
                    if (ys[i], ys[j]) in y_pairs:
                        res = min(res, (x - y_pairs[(ys[i], ys[j])]) * (ys[j] - ys[i]))
                    y_pairs[(ys[i],ys[j])] = x
        return res if res != float('inf') else 0


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        
        x_to_ys = collections.defaultdict(set)
        for px, py in points:
            x_to_ys[px].add(py)
            
        min_area = float(\"inf\")
        xs = sorted(list(x_to_ys.keys()))
        for i, x1 in enumerate(xs):
            for j in range(i+1, len(xs)):
                x2 = xs[j]
                if x2-x1>min_area:
                    break
                common_ys = x_to_ys[x1] & x_to_ys[x2]
                if len(common_ys)>=2:
                    common_ys = sorted(list(common_ys))
                    min_y_diff = min(y-common_ys[iy] 
                                     for iy, y in enumerate(common_ys[1:]))
                    min_area = min(min_area, min_y_diff*(x2-x1))
                
        if min_area==float(\"inf\"):
            return 0
        else:
            return min_area

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        res = float('inf')
        xs, ys = collections.defaultdict(set), collections.defaultdict(set)
        for x1, y1 in points:
            for x2 in xs[y1]:
                for y2 in ys[x1]:
                    if x2 in xs[y2]:
                        res = min(res, abs(x2 - x1) * abs(y2 - y1))
            xs[y1].add(x1)
            ys[x1].add(y1)
        return res if res < float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        result = float('inf')
        columns = collections.defaultdict(lambda: [])
        for x, y in points:
            columns[x].append(y)
            
        print('columns is: ' + str(columns))
            
        # Map from (y1, y2) pair to x
        lastSeen = {}
        for x in sorted(columns.keys()):
            column = sorted(columns[x])
            for i in range(len(column)):
                y1 = column[i]
                for j in range(i+1, len(column)):
                    if y1 != column[j]:
                        y2 = column[j]
                        if (y1, y2) in lastSeen:
                            prev_x = lastSeen[(y1, y2)]
                            result = min(result, (y2-y1)*(x-prev_x))
                        lastSeen[(y1, y2)] = x
        return 0 if result == float('inf') else result

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        if not points:
            return 0
        
        x_map= {}
        points.sort(key=lambda x: x)
        area = float('inf')
        
        y_map = {}
        
        for point in points:
            if point[0] in x_map:
                x_map[point[0]].append(point[1])
                continue
            x_map[point[0]] = [point[1]]
            
        for x in x_map:
            x_map[x].sort()
            for j in range(0, len(x_map[x])):
                y2 = x_map[x][j]
                for k in range(0, j):
                    y1 =x_map[x][k]
                    if (y1, y2) in y_map:
                        area = min(area, (y2-y1) * (x - y_map[(y1, y2)]))
                    y_map[(y1, y2)] = x
        return area if area!=float('inf') else 0
                    
                
        
        


class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        \"\"\"
        | x   x x
        |   x x x
        | x   x    x1:y1,y3
        |________  x2:y2
                   x3:y1,y2,y3
                   x4:y2,y3
        \"\"\"
        minarea=float('inf')
        d = defaultdict(set)
        for x,y in points:
            d[x].add(y)
        sortx=sorted(d)
        
        #print(d)
        for i in range(len(sortx)):
            for j in range(i+1, len(sortx)):
                ys= d[sortx[i]] & d[sortx[j]]
                if len(ys)>1:
                    ylist = list(ys)
                    ylist.sort()
                    ylen =min([abs(ii-jj) for ii, jj in zip(ylist, ylist[1:])])
                    
                    minarea = min(minarea, abs(sortx[i]-sortx[j])*ylen)
                
        return minarea if minarea!=float('inf') else 0

class Solution:
    def minAreaRect(self, points: List[List[int]]) -> int:
        # [[1,1],[2,2],[1,3],[3,1],[3,3],[4,1],[4,3]]
        # vsides = {1: [1,3], 2:[2], 3:[1, 3], 4:[1,3]}
        # slengths = {(1,3):1}
        vsides = dict()
        for x, y in points:
            if x in vsides:
                vsides[x].append(y)
            else:
                vsides[x] = [y]
        slengths = dict()
        min_area = float('inf')
        xs = list(vsides.keys())
        xs.sort()
        for x in xs:
            vs = vsides[x]
            vs.sort()
            for i in range(len(vs) - 1):
                for j in range(i + 1, len(vs)):
                    if (vs[i], vs[j]) in slengths:
                        area = abs( (x - slengths[(vs[i], vs[j])]) * (vs[j] - vs[i]) )
                        min_area = min(min_area, area)
                    slengths[(vs[i], vs[j])] = x
        return min_area if min_area != float('inf') else 0