From 57d1dee00b84c1b41c17b415a3e7859e21c8029f Mon Sep 17 00:00:00 2001 From: didier amyot Date: Mon, 9 Mar 2015 20:58:37 -0700 Subject: [PATCH] first commit --- README.md | 6 ++ lrucache/README.md | 4 ++ lrucache/lrucache.go | 60 ++++++++++++++++ lrucache/lrucache_test.go | 118 ++++++++++++++++++++++++++++++++ sorting/README.md | 4 ++ sorting/streams.go | 133 ++++++++++++++++++++++++++++++++++++ sorting/streams_test.go | 140 ++++++++++++++++++++++++++++++++++++++ 7 files changed, 465 insertions(+) create mode 100644 README.md create mode 100644 lrucache/README.md create mode 100644 lrucache/lrucache.go create mode 100644 lrucache/lrucache_test.go create mode 100644 sorting/README.md create mode 100644 sorting/streams.go create mode 100644 sorting/streams_test.go diff --git a/README.md b/README.md new file mode 100644 index 0000000..78747df --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +## The questions I screwed + +Questions (and their answers) I screwed up during interviews[^1]. + + + diff --git a/lrucache/README.md b/lrucache/README.md new file mode 100644 index 0000000..a2e7027 --- /dev/null +++ b/lrucache/README.md @@ -0,0 +1,4 @@ +## Question + +Implement a *Least Recently Used* (lru) cache + diff --git a/lrucache/lrucache.go b/lrucache/lrucache.go new file mode 100644 index 0000000..3b0c4b0 --- /dev/null +++ b/lrucache/lrucache.go @@ -0,0 +1,60 @@ +package lrucache + +import ( + "container/list" +) + +type Creator func(key string) interface{} + +type cacheKey struct { + Value interface{} + DoubleLinkedListNode *list.Element +} + +type LRUCache struct { + cache map[string]*cacheKey + ddl *list.List + creator Creator + length int +} + +func New(creator Creator, length int) *LRUCache { + return &LRUCache{ + cache: make(map[string]*cacheKey), + creator: creator, + ddl: new(list.List).Init(), + length: length, + } +} + +func (lru *LRUCache) add(key string, val interface{}) interface{} { + lru.ddl.PushFront(key) + lru.cache[key] = &cacheKey{val, lru.ddl.Front()} + return val +} + +func (lru *LRUCache) Size() int { + return len(lru.cache) +} + +func (lru *LRUCache) removeExtraKeys() { + if lru.ddl.Len() < lru.length { + return + } + for e := lru.ddl.Back(); lru.ddl.Len() > lru.length; { + delete(lru.cache, e.Value.(string)) + be := e.Prev() + lru.ddl.Remove(e) + e = be + } +} + +func (lru *LRUCache) Get(key string) interface{} { + if val, ok := lru.cache[key]; ok { + lru.ddl.MoveToFront(val.DoubleLinkedListNode) + return val.Value + } + ret := lru.add(key, lru.creator(key)) + lru.removeExtraKeys() + return ret +} diff --git a/lrucache/lrucache_test.go b/lrucache/lrucache_test.go new file mode 100644 index 0000000..ddb3a56 --- /dev/null +++ b/lrucache/lrucache_test.go @@ -0,0 +1,118 @@ +// Example of an lru implementation using double linked lists (not thread safe) +package lrucache + +import ( + "container/list" + "errors" + "strconv" + "testing" +) + +func mockCreator(key string) interface{} { + return key +} + +func doubleLinkedListContains(l *list.List, elm string) bool { + for node := l.Front(); node != nil; node = node.Next() { + if node.Value.(string) == elm { + return true + } + } + return false +} + +func toString(a interface{}) string { + if i, ok := a.(string); ok { + return i + } + panic("cannot convert in string") +} + +func TestAdd(t *testing.T) { + lru := New(mockCreator, 10) + if lru.add("key", "val") != "val" { + t.Error("must returns the value passed") + } + + if toString(lru.ddl.Front().Value) != "key" { + t.Error("key must be ther first item after adding it") + } + if val, ok := lru.cache["key"]; !ok { + t.Error("key not in cache") + } else if toString(val.Value) != "val" { + t.Error("bad value associated to key") + } + +} + +func TestRemoveExtraKeys(t *testing.T) { + lru := New(mockCreator, 4) + for i := 0; i < 10; i++ { + lru.add(strconv.Itoa(i), strconv.Itoa(i)) + } + if lru.Size() != 10 { + t.Error("bad initial length") + } + lru.removeExtraKeys() + if lru.Size() != 4 { + t.Error("bad final length. should be 4 but have ", lru.Size()) + } + if lru.ddl.Len() != 4 { + t.Error("double linked list has incorrect length") + } +} + +func checkLRUCacheState(lru *LRUCache, expected ...string) error { + expectedSize := len(expected) + if len(lru.cache) != expectedSize { + return errors.New("cache of wrong size") + } + + if lru.ddl.Len() != expectedSize { + return errors.New("double linked list of wrong size") + } + for _, e := range expected { + if _, ok := lru.cache[e]; !ok { + return errors.New("key " + e + " not in cache") + } + if !doubleLinkedListContains(lru.ddl, e) { + return errors.New("key " + e + " not in double linked list") + } + } + return nil +} + +func TestGet(t *testing.T) { + lru := New(mockCreator, 2) + if v := lru.Get("0"); v != "0" { + t.Error() + } + if err := checkLRUCacheState(lru, "0"); err != nil { + t.Error(err) + return + } + + lru.Get("1") + if err := checkLRUCacheState(lru, "1", "0"); err != nil { + t.Error(err) + return + } + + lru.Get("2") + if err := checkLRUCacheState(lru, "2", "1"); err != nil { + t.Error(err) + return + } + + lru.Get("1") + if err := checkLRUCacheState(lru, "1", "2"); err != nil { + t.Error(err) + return + } + + lru.Get("3") + if err := checkLRUCacheState(lru, "3", "1"); err != nil { + t.Error(err) + return + } +} diff --git a/sorting/README.md b/sorting/README.md new file mode 100644 index 0000000..ccf259e --- /dev/null +++ b/sorting/README.md @@ -0,0 +1,4 @@ +## Question + +You have *n* streams of sorted object, create a function that will merge them in a +sorted steam. diff --git a/sorting/streams.go b/sorting/streams.go new file mode 100644 index 0000000..4e8067d --- /dev/null +++ b/sorting/streams.go @@ -0,0 +1,133 @@ +package sorting + +import "fmt" + +var _ = fmt.Print + +type IsGreaterer interface { + // return true iff "this" is strictly greater than e + IsGreater(e IsGreaterer) bool +} + +type alwaysBigger struct{} + +func (a *alwaysBigger) IsGreater(e IsGreaterer) bool { + return e != nil +} + +type Stream chan IsGreaterer + +type streamOutput struct { + Val IsGreaterer + Idx int +} + +func readFirstElementOfAllTheStreams(streams []Stream) []IsGreaterer { + values := make([]IsGreaterer, len(streams)) + ch := make(chan *streamOutput) + for i, _ := range values { + go func(idx int) { + v := <-streams[idx] + ch <- &streamOutput{v, idx} + + }(i) + } + + i := 0 + for streamOutput := range ch { + values[streamOutput.Idx] = streamOutput.Val + i++ + if i == len(streams) { + return values + } + } + return values +} + +func allStreamsButOne(streams []Stream, idx int) []Stream { + ret := make([]Stream, len(streams)-1) + for i, s := range streams { + if i < idx { + ret[i] = s + } else if i > idx { + ret[i-1] = s + } + } + return ret +} + +func prependToStream(streams Stream, toPrepend IsGreaterer) Stream { + ret := make(Stream) + go func() { + ret <- toPrepend + for e := range streams { + ret <- e + } + close(ret) + }() + return ret +} + +func prependValueToEachStream(streams []Stream, values []IsGreaterer) []Stream { + ret := make([]Stream, len(streams)) + for i := 0; i < len(streams); i++ { + ret[i] = prependToStream(streams[i], values[i]) + + } + return ret +} + +func getSmallerStreamWithSecondSmallestElementWithAllTheOthersGreaterStreams(streams []Stream) (Stream, IsGreaterer, []Stream) { + firstElements := readFirstElementOfAllTheStreams(streams) + streams = prependValueToEachStream(streams, firstElements) + + var smallestStreamIndex int = -1 + var smallestItem IsGreaterer = &alwaysBigger{} + var secondSmallestItem IsGreaterer = &alwaysBigger{} + + for i, e := range firstElements { + if smallestItem.IsGreater(e) { + smallestItem = e + smallestStreamIndex = i + } else if secondSmallestItem.IsGreater(e) { + secondSmallestItem = e + } + } + if _, ok := secondSmallestItem.(*alwaysBigger); ok { + return streams[smallestStreamIndex], nil, []Stream{} + } + return streams[smallestStreamIndex], secondSmallestItem, allStreamsButOne(streams, smallestStreamIndex) +} + +func sortStreamInChannel(o Stream, streams []Stream) { + if len(streams) == 1 { + for e := range streams[0] { + o <- e + } + close(o) + return + } + smallestStream, secondSmallestItem, greaterStreams := getSmallerStreamWithSecondSmallestElementWithAllTheOthersGreaterStreams(streams) + if secondSmallestItem == nil { + sortStreamInChannel(o, append(greaterStreams, smallestStream)) + return + + } + for elm := range smallestStream { + if !elm.IsGreater(secondSmallestItem) { + o <- elm + } else { + smallestStream = prependToStream(smallestStream, elm) + sortStreamInChannel(o, append(greaterStreams, smallestStream)) + return + } + } + sortStreamInChannel(o, greaterStreams) +} + +// Takes sorted streams as input and return the merged result stream +func SortStreams(streams ...Stream) Stream { + ret := make(Stream) + go sortStreamInChannel(ret, streams) + return ret +} diff --git a/sorting/streams_test.go b/sorting/streams_test.go new file mode 100644 index 0000000..1570469 --- /dev/null +++ b/sorting/streams_test.go @@ -0,0 +1,140 @@ +package sorting + +import ( + "reflect" + "testing" +) + +type MockIsGreaterer int + +func (i MockIsGreaterer) IsGreater(e IsGreaterer) bool { + return i > e.(MockIsGreaterer) +} + +func streamEqual(stream Stream, expected ...int) bool { + i := 0 + for v := range stream { + if i >= len(expected) { + return false + } + if MockIsGreaterer(expected[i]) != v { + return false + } + i++ + } + return i == len(expected) + +} + +func CreateStream(values ...int) Stream { + s := make(Stream) + go func() { + for _, val := range values { + s <- MockIsGreaterer(val) + } + close(s) + }() + + return s +} + +func TestReadFirstElementOfAllTheStreams(t *testing.T) { + s1 := CreateStream(0, 1) + arr := readFirstElementOfAllTheStreams([]Stream{s1}) + if !reflect.DeepEqual(arr, []IsGreaterer{MockIsGreaterer(0)}) { + t.Error(arr) + } + + if next := <-s1; next != MockIsGreaterer(1) { + t.Error("read too many element in the list") + return + } + + s1 = CreateStream(0, 1, 2) + s2 := CreateStream(5, 6, 7) + s3 := CreateStream(90, 91, 92) + arr = readFirstElementOfAllTheStreams([]Stream{s1, s2, s3}) + if !reflect.DeepEqual(arr, []IsGreaterer{MockIsGreaterer(0), MockIsGreaterer(5), MockIsGreaterer(90)}) { + t.Error(arr) + } +} + +func TestAllStreamsButOne(t *testing.T) { + streams := []Stream{CreateStream(0, 10, 1), CreateStream(1, 10, 1), CreateStream(2, 10, 1)} + + res := allStreamsButOne(streams, 1) + if len(res) != 2 { + t.Error() + } + if e := <-res[0]; e != MockIsGreaterer(0) { + t.Error() + } + if e := <-res[1]; e != MockIsGreaterer(2) { + t.Error() + } +} + +func TestGetSmallerStreamWithSecondSmallestElementWithAllTheOthersGreaterStreams(t *testing.T) { + streams := []Stream{CreateStream(0, 1), CreateStream(3, 4), CreateStream(7, 8)} + stream, e, streams := getSmallerStreamWithSecondSmallestElementWithAllTheOthersGreaterStreams(streams) + var _ = stream + _ = streams + if e != MockIsGreaterer(3) { + t.Error("getSmallerStreamWithSecondSmallestElementWithAllTheOthersGreaterStreams does not return the second smallest value", e) + } + + if v := <-stream; v != MockIsGreaterer(0) { + t.Error("return streams is not the smalest", v) + } + + if len(streams) != 2 { + t.Error("bad number of returned streams") + } + + if len(streams) != 2 { + t.Error("bad number of returned streams") + } +} + +func TestSortStreams(t *testing.T) { + if s := SortStreams(CreateStream(0, 1, 2)); !streamEqual(s, 0, 1, 2) { + t.Error() + } + + if s := SortStreams(CreateStream(0, 1), CreateStream(2, 3)); !streamEqual(s, 0, 1, 2, 3) { + t.Error() + } + + if s := SortStreams(CreateStream(0), CreateStream(2)); !streamEqual(s, 0, 2) { + t.Error() + } + + if s := SortStreams(CreateStream(0, 5), CreateStream(2)); !streamEqual(s, 0, 2, 5) { + t.Error() + } + + if s := SortStreams(CreateStream(0, 5), CreateStream(2, 7)); !streamEqual(s, 0, 2, 5, 7) { + t.Error() + } + + if s := SortStreams(CreateStream(0, 5), CreateStream(2, 7, 9, 10, 12)); !streamEqual(s, 0, 2, 5, 7, 9, 10, 12) { + t.Error() + } + + if s := SortStreams(CreateStream(), CreateStream(2, 7)); !streamEqual(s, 2, 7) { + t.Error() + } + + if s := SortStreams(CreateStream(), CreateStream(2, 7), CreateStream(3, 8)); !streamEqual(s, 2, 3, 7, 8) { + t.Error() + } + + if s := SortStreams(CreateStream()); !streamEqual(s) { + t.Error() + } + + if s := SortStreams(CreateStream(1, 2), CreateStream(2, 3)); !streamEqual(s, 1, 2, 2, 3) { + t.Error() + } + +}