package main

import (
	"time"
	"container/heap"
)

/**
 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 *
 * lfu design
 * @param operators int整型二维数组 ops
 * @param k int整型 the k
 * @return int整型一维数组
 */
type entry struct {
    key int
    val int
    updatedAt time.Time
}

type LfuEntry struct {
    entry entry
    count int
    index int
}

type mypriority []*LfuEntry
func (p mypriority) Len() int {
    return len(p)
}
func (p mypriority) Swap(i, j int) {
    p[i], p[j] = p[j], p[i]
    p[i].index = i
    p[j].index = j
}
func (p mypriority) Less(i, j int) bool {
    if p[i].count == p[j].count {
        return p[i].entry.updatedAt.Before(p[j].entry.updatedAt)
    }
    return p[i].count < p[j].count
}
func (p *mypriority) Push(x interface{}) {
    lfuEntry := x.(*LfuEntry)
    lfuEntry.index = p.Len()
    (*p) = append((*p), lfuEntry)
}
func (p *mypriority) Pop() interface{} {
    oldp := *p
    n := len(oldp)
    entry := oldp[n-1]
    oldp[n-1] = nil
    newp := oldp[:n-1]
    // 1 2 3 4 5 -> 2 3 4 5(索引 1 2 3 4) 更新为(0 1 2 3) 
    for i := 0; i < len(newp); i++ {
        newp[i].index = i
    }
    *p = newp
    return entry
}

type LFUCache struct {
    maxCacheSize int
    nSize        int
    m            map[int]*LfuEntry
    priority     *mypriority
}

func NewLFUCache(cachesize int) LFUCache {
    priority := make(mypriority, 0)
    return LFUCache{
        maxCacheSize: cachesize,
        m: make(map[int]*LfuEntry),
        priority: &priority,
    }
}

func NewLFUEntry(key, val int) *LfuEntry {
    return &LfuEntry{
        entry: entry{
            key: key,
            val: val,
            updatedAt: time.Now(),
        },
    }
}
func (l *LFUCache) Get(key int) int {
    if lfuEntry, ok := l.m[key]; ok {
        lfuEntry.touch()
        heap.Fix(l.priority, lfuEntry.index)
        return lfuEntry.entry.val
    }
    return -1
}

func (l *LFUCache) Put(key, val int) {
    if lfuEntry, ok := l.m[key]; ok {
        lfuEntry.touch()
        // index 节点 count 发生变化,调整堆
        heap.Fix(l.priority, lfuEntry.index)
        lfuEntry.entry.val = val
    } else {
        newLfuEntry := NewLFUEntry(key, val)
        newLfuEntry.touch()
        if l.nSize == l.maxCacheSize {
            l.Remove()
        }
        l.nSize++
        l.m[key] = newLfuEntry
        heap.Push(l.priority, newLfuEntry)
    }
}

func (l *LFUCache) Remove() {
        lfuEntry := heap.Pop(l.priority).(*LfuEntry)
        delete(l.m, lfuEntry.entry.key)
        l.nSize--
}

func (l *LfuEntry) touch() {
    l.count++
    l.entry.updatedAt = time.Now()
}

func LFU( operators [][]int ,  k int ) []int {
    // write code here
    lfu, ans := NewLFUCache(k), []int{}
    for _, operator := range operators {
        if operator[0] == 1 {
            lfu.Put(operator[1], operator[2])
        } else {
            ans = append(ans, lfu.Get(operator[1]))
        }
    }
    return ans
}