├── ConstantTimeLfuCache ├── README.md └── lfu_cache_in_go │ ├── main_test.go │ └── main.go └── README.md /ConstantTimeLfuCache/README.md: -------------------------------------------------------------------------------- 1 | [An O(1) Algorithm for implementing the LFU Cache Eviction Scheme -- K. Shah, A. Mitra, D. Matani](https://github.com/papers-we-love/papers-we-love/blob/d70df4f6301e97c32465e4161703d04416aff061/caching/a-constant-algorithm-for-implementing-the-lfu-cache-eviction-scheme.pdf) 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Papers I Read 2 | 3 | I'm looking to expand my horizons and learn more and one of the ways I've heard to 4 | do that is by simply reading research papers. Considering how I haven't read any papers 5 | since that time I had to read *one* for an info retrieval class, I figured this is a good 6 | way to do it. 7 | 8 | I'm really looking for somewhere to put all this code, and open sourcing it is fun. 9 | 10 | The papers are just taken off of [papers we love](https://github.com/papers-we-love/papers-we-love) and implemented here. The goal is to try 11 | pulling it off in a bunch of languages, but even after my first one (which might be over-complicated) 12 | I'm starting to question how realistic that is. 13 | 14 | # License 15 | 16 | MIT 17 | -------------------------------------------------------------------------------- /ConstantTimeLfuCache/lfu_cache_in_go/main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | func TestAddingAndRemovingAnElementFromFrequencyNode(t *testing.T) { 9 | fn := NewFrequencyNode(1) 10 | assert.Equal(t, 0, fn.Len()) 11 | fn.Add("hello") 12 | assert.Equal(t, 1, fn.Len()) 13 | } 14 | 15 | func TestRemovingNonExistentElementFromFrequencyNode(t *testing.T) { 16 | fn := NewFrequencyNode(1) 17 | fn.Add("hello") 18 | fn.Remove("Hello") 19 | assert.Equal(t, 1, fn.Len()) 20 | fn.Remove("hello") 21 | assert.Equal(t, 0, fn.Len()) 22 | fn.Remove("hello") 23 | assert.Equal(t, 0, fn.Len()) 24 | } 25 | 26 | func TestInitializingACache(t *testing.T) { 27 | cache := NewLfuCache() 28 | head := cache.Head() 29 | fn := head.Value.(*FrequencyNode) 30 | assert.Equal(t, 0, fn.Value) 31 | assert.Equal(t, 0, fn.Len()) 32 | assert.Nil(t, head.Next()) 33 | assert.Nil(t, head.Prev()) 34 | } 35 | 36 | func TestGettingANewNodeForTheCache(t *testing.T) { 37 | cache := NewLfuCache() 38 | head := cache.Head() 39 | node, err := cache.GetNewNode(1, head) 40 | fn := node.Value.(*FrequencyNode) 41 | assert.Nil(t, err) 42 | assert.Equal(t, 1, fn.Value) 43 | assert.Equal(t, head, node.Prev()) 44 | assert.Nil(t, node.Next()) 45 | } 46 | 47 | func TestRemovingANodeFromTheCache(t *testing.T) { 48 | cache := NewLfuCache() 49 | assert.Equal(t, 0, cache.FreqListLen()) 50 | node, err := cache.GetNewNode(1, cache.Head()) 51 | assert.Nil(t, err) 52 | assert.Equal(t, 1, cache.FreqListLen()) 53 | cache.DeleteNode(node) 54 | assert.Equal(t, 0, cache.FreqListLen()) 55 | } 56 | 57 | func TestFetchingDataForAKeyThatDoesNotExist(t *testing.T) { 58 | cache := NewLfuCache() 59 | data, err := cache.Fetch("hello") 60 | assert.Nil(t, data) 61 | assert.Equal(t, "'hello' does not exist", err.Error()) 62 | } 63 | 64 | func TestInsertingDataIntoTheCache(t *testing.T) { 65 | cache := NewLfuCache() 66 | cache.Store("hello", "world") 67 | actual, _ := cache.Fetch("hello") 68 | assert.Equal(t, "world", actual) 69 | } 70 | 71 | func TestInsertingIntoCacheStoresItemInCorrectLocation(t *testing.T) { 72 | cache := NewLfuCache() 73 | cache.Store("hello", "world") 74 | freq := cache.FreqNodeFor(1) 75 | assert.Equal(t, 1, freq.Value) 76 | assert.Equal(t, 1, freq.Len()) 77 | assert.True(t, freq.Contains("hello")) 78 | } 79 | 80 | func TestInsertingSameValueTwiceReturnsAnError(t *testing.T) { 81 | cache := NewLfuCache() 82 | err := cache.Store("hello", "world") 83 | assert.Nil(t, err) 84 | err = cache.Store("hello", "world") 85 | assert.NotNil(t, err) 86 | assert.Equal(t, "Data already in cache", err.Error()) 87 | } 88 | -------------------------------------------------------------------------------- /ConstantTimeLfuCache/lfu_cache_in_go/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "container/list" 5 | "errors" 6 | "fmt" 7 | ) 8 | 9 | type FrequencyNode struct { 10 | Value int 11 | elements map[string]bool 12 | count int 13 | } 14 | 15 | func NewFrequencyNode(value int) *FrequencyNode { 16 | elements := make(map[string]bool) 17 | return &FrequencyNode{value, elements, 0} 18 | } 19 | 20 | func (f *FrequencyNode) Len() int { 21 | return f.count 22 | } 23 | 24 | func (f *FrequencyNode) Contains(key string) bool { 25 | return f.elements[key] 26 | } 27 | 28 | func (f *FrequencyNode) Add(key string) { 29 | f.elements[key] = true 30 | f.count++ 31 | } 32 | 33 | func (f *FrequencyNode) Remove(key string) { 34 | if f.elements[key] { 35 | f.count-- 36 | } 37 | f.elements[key] = false 38 | } 39 | 40 | type CacheItem struct { 41 | Data interface{} 42 | Parent *list.Element 43 | } 44 | 45 | func NewCacheItem(data interface{}, parent *list.Element) *CacheItem { 46 | return &CacheItem{data, parent} 47 | } 48 | 49 | type LfuCache struct { 50 | dataLookup map[string]*CacheItem 51 | frequencyHead *list.Element 52 | freqList *list.List 53 | } 54 | 55 | func NewLfuCache() LfuCache { 56 | cache := LfuCache{ 57 | dataLookup: make(map[string]*CacheItem), 58 | freqList: list.New(), 59 | } 60 | cache.frequencyHead = cache.freqList.PushFront(NewFrequencyNode(0)) 61 | return cache 62 | } 63 | 64 | func (l LfuCache) Head() *list.Element { 65 | return l.frequencyHead 66 | } 67 | 68 | func (l LfuCache) FreqListLen() int { 69 | return l.freqList.Len() - 1 // ignore the first node 70 | } 71 | 72 | func (l LfuCache) GetNewNode(value int, parent *list.Element) (*list.Element, error) { 73 | if parent == nil { 74 | return nil, errors.New("Cannot insert node because parent is nil") 75 | } 76 | node := l.freqList.InsertAfter(NewFrequencyNode(value), parent) 77 | return node, nil 78 | } 79 | 80 | func (l LfuCache) DeleteNode(node *list.Element) { 81 | l.freqList.Remove(node) 82 | } 83 | 84 | func (l LfuCache) FreqNodeFor(value int) *FrequencyNode { 85 | node := l.frequencyHead 86 | for i := value; i > 0; i-- { 87 | if node.Next() == nil { 88 | break 89 | } 90 | node = node.Next() 91 | } 92 | 93 | return node.Value.(*FrequencyNode) 94 | } 95 | 96 | func (l LfuCache) Fetch(key string) (data interface{}, err error) { 97 | var freq, nextFreq *FrequencyNode 98 | var node, nextNode *list.Element 99 | elem := l.dataLookup[key] 100 | if elem == nil { 101 | return nil, errors.New(fmt.Sprintf("'%s' does not exist", key)) 102 | } 103 | node = elem.Parent 104 | freq = node.Value.(*FrequencyNode) 105 | if nextNode = node.Next(); nextNode != nil { 106 | nextFreq = nextNode.Value.(*FrequencyNode) 107 | } 108 | 109 | if nextNode == nil || nextFreq.Value != freq.Value+1 { 110 | nextNode, err = l.GetNewNode(freq.Value+1, node) 111 | if err != nil { 112 | return nil, err 113 | } 114 | nextFreq = nextNode.Value.(*FrequencyNode) 115 | } 116 | 117 | nextFreq.Add(key) 118 | elem.Parent = nextNode 119 | 120 | freq.Remove(key) 121 | if freq.Len() == 0 { 122 | l.DeleteNode(node) 123 | } 124 | return elem.Data, nil 125 | } 126 | 127 | func (l LfuCache) Store(key string, value interface{}) error { 128 | var node *list.Element 129 | var freq *FrequencyNode 130 | 131 | if l.dataLookup[key] != nil { 132 | return errors.New("Data already in cache") 133 | } 134 | 135 | node = l.frequencyHead.Next() 136 | if node != nil { 137 | freq = node.Value.(*FrequencyNode) 138 | } 139 | 140 | if node == nil || freq.Value != 1 { 141 | node, _ = l.GetNewNode(1, l.frequencyHead) 142 | freq = node.Value.(*FrequencyNode) 143 | } 144 | 145 | freq.Add(key) 146 | l.dataLookup[key] = NewCacheItem(value, node) 147 | return nil 148 | } 149 | --------------------------------------------------------------------------------