<p>Given an array of <code>points</code> where <code>points[i] = [x<sub>i</sub>, y<sub>i</sub>]</code> represents a point on the <strong>X-Y</strong> plane and an integer <code>k</code>, return the <code>k</code> closest points to the origin <code>(0, 0)</code>.</p>

<p>The distance between two points on the <strong>X-Y</strong> plane is the Euclidean distance (i.e., <code>&radic;(x<sub>1</sub> - x<sub>2</sub>)<sup>2</sup> + (y<sub>1</sub> - y<sub>2</sub>)<sup>2</sup></code>).</p>

<p>You may return the answer in <strong>any order</strong>. The answer is <strong>guaranteed</strong> to be <strong>unique</strong> (except for the order that it is in).</p>

<p>&nbsp;</p>
<p><strong class="example">Example 1:</strong></p>
<img alt="" src="https://assets.leetcode.com/uploads/2021/03/03/closestplane1.jpg" style="width: 400px; height: 400px;" />
<pre>
<strong>Input:</strong> points = [[1,3],[-2,2]], k = 1
<strong>Output:</strong> [[-2,2]]
<strong>Explanation:</strong>
The distance between (1, 3) and the origin is sqrt(10).
The distance between (-2, 2) and the origin is sqrt(8).
Since sqrt(8) &lt; sqrt(10), (-2, 2) is closer to the origin.
We only want the closest k = 1 points from the origin, so the answer is just [[-2,2]].
</pre>

<p><strong class="example">Example 2:</strong></p>

<pre>
<strong>Input:</strong> points = [[3,3],[5,-1],[-2,4]], k = 2
<strong>Output:</strong> [[3,3],[-2,4]]
<strong>Explanation:</strong> The answer [[-2,4],[3,3]] would also be accepted.
</pre>

<p>&nbsp;</p>
<p><strong>Constraints:</strong></p>

<ul>
	<li><code>1 &lt;= k &lt;= points.length &lt;= 10<sup>4</sup></code></li>
	<li><code>-10<sup>4</sup> &lt;= x<sub>i</sub>, y<sub>i</sub> &lt;= 10<sup>4</sup></code></li>
</ul>

# class Solution:
#     def kClosest(self, points: List[List[int]], k: int) -> List[List[int]]:
#         max_hq = []

#         for p in points: 
#             x, y = p[0], p[1]
#             heapq.heappush(max_hq, (-(x**2 + y**2), x, y))
#             if len(max_hq) > k: 
#                 heapq.heappop(max_hq)

#         res = []

#         for _, x, y in max_hq: 
#             res.append([x, y])

#         return res


#"""
# Max Heap: reuse the default min heap
# Time Complexity: O(Nlog(K))
# Space Complexity: O(K)

# by default, it is min heap
import heapq
class Solution:
    def kClosest(self, points: List[List[int]], k: int) -> List[List[int]]:
        mx_hq = []
        res = []
        
        for i, p in enumerate(points): 
            d = p[0]**2 + p[1]**2
            if len(mx_hq) < k: 
                heapq.heappush(mx_hq, (-d, i))
            else: 
                heapq.heappushpop(mx_hq, (-d, i))

        while mx_hq: 
            pq = heapq.heappop(mx_hq)
            res.append([points[pq[1]][0],points[pq[1]][1]])
            
        return res
#"""


#"""
import heapq
class Solution:
    def kClosest(self, points, K):
        return heapq.nsmallest(K, points, key = lambda p: p[0]**2 + p[1]**2)
        #return sorted(points, key = lambda p: p[0]**2 + p[1]**2)[0:K] 
        #return heapq.nsmallest(K, points, lambda [x,y]: x * x + y * y) # fail to work in python3
#"""

  
"""
from heapq import heapreplace, heapify
class Solution(object):
    def kClosest(self, pairs: List[List[int]], k: int) -> List[List[int]]:
        nums = []
        self.getDist(pairs, nums)
        heap = nums[:k]
        heapq.heapify(heap)
        
        for i in range(k, len(nums)):
            if -nums[i][0] < -heap[0][0]:
                heapq.heapreplace(heap, nums[i])
        
        res = []
        for num, x, y in heap:
            res.append([x, y])
        return res
        
    def getDist(self, pairs, nums):
        for pair in pairs:
            x, y = pair
            dist = (x ** 2 + y ** 2) ** 0.5
            nums.append((-dist, x, y))
"""

"""
class Solution(object):
    def kClosest(self, points: List[List[int]], K: int) -> List[List[int]]:
        return heapq.nsmallest(K, points, key = lambda point: point[0]**2 + point[1]**2)
        #return sorted(points, key = lambda point : point[0]**2 + point[1]**2)[:K]
"""

'''
class Solution(object):
    def kClosest(self, points: List[List[int]], K: int) -> List[List[int]]:
        class NodeDist():
            def __init__(self, point: List[List[int]]): 
                self.point = point
            def __lt__(self, other: List[List[int]]):
                return self.point[0]**2 + self.point[1]**2 < other.point[0]**2 + other.point[1]**2
                
        minhp = []
        for p in points: 
            heapq.heappush(minhp, NodeDist(p))
            
        #return [minhp[i].point for i in range(K)] # this one cannot work
        return [heapq.heappop(minhp).point for _ in range(K)]
'''
        
'''
# TC: O(NlogN)
# heaplify the distance, but attach the index to it
import heapq
class Solution:
    def kClosest(self, points: List[List[int]], K: int) -> List[List[int]]:
        distance = []
        for i in points:
            heapq.heappush(distance,(i[0]**2+i[1]**2,i))
        K_points = []
        for i in range(K):
            K_points.append(heapq.heappop(distance)[1])
        return K_points
'''
      
'''
# max heap: 
# only keep the k smalleast, the pop the larger ones
import heapq
class Solution:
    def kClosest(self, points: List[List[int]], K: int) -> List[List[int]]:
        distance = []
        for p in points:
            if len(distance) <= K-1:
                heapq.heappush(distance,(-p[0]**2-p[1]**2,p))
            else:
                if -p[0]**2-p[1]**2 > distance[0][0]:
                    heapq.heapreplace(distance, (-p[0]**2-p[1]**2,p))
                    #heapq.heappop(distance)
                    #heapq.heappush(distance,(-p[0]**2-p[1]**2,p))
        res = []
        for i in range (K):
            res.append(heapq.heappop(distance)[1])
        return res
'''
      
'''
# TC: O(NlogK)
# we the max heap to emit the max distance,  and keep the heapq size less or equal than K
# distance q with distance and the point index, 
import heapq

class Solution:
    def kClosest(self, points: List[List[int]], K: int) -> List[List[int]]:
        distance = [] # list of tuple of distance and the node index
        
        # for p in points:
        for i in range(len(points)):
            if len(distance) <= K-1:
                heapq.heappush(distance,(-points[i][0]**2 - points[i][1]**2, i))
            else:
                heapq.heappushpop(distance, (-points[i][0]**2 - points[i][1]**2, i))
                    
        return [[points[d[1]][0],points[d[1]][1]] for d in distance] # can be in any order
                    
        
        res = []
        for i in range (K):
            res.append(points[heapq.heappop(distance)[1]])
        return res
        
'''
       


"""
class Solution:
    def kClosest(self, points: List[List[int]], k: int) -> List[List[int]]:
        mx_hq = []
        res_idx = []
        res = []
        
        for i, p in enumerate(points): 
            d = p[0]**2 + p[1]**2
            if len(mx_hq) < k: 
                heapq.heappush(mx_hq, (-d, i))
            else: 
                heapq.heappushpop(mx_hq, (-d, i))

        while mx_hq: 
            pq = heapq.heappop(mx_hq)
            res_idx.append(pq[1])

        for i in range(len(res_idx) - 1, -1, -1): 
            res.append([points[res_idx[i]][0],points[res_idx[i]][1]])
            
        return res

"""
                
22200