diff --git a/calculator/heap.go b/calculator/heap.go new file mode 100644 index 0000000..3e5928f --- /dev/null +++ b/calculator/heap.go @@ -0,0 +1,28 @@ +package calculator + +type LargestNumbersHeap []int + +func (biggestNumbers LargestNumbersHeap) Less(first, second int) bool { + // pop the smallest number + return biggestNumbers[first] < biggestNumbers[second] +} + +func (biggestNumbers LargestNumbersHeap) Len() int { + return len(biggestNumbers) +} + +func (biggestNumbers LargestNumbersHeap) Swap(first, second int) { + biggestNumbers[first], biggestNumbers[second] = biggestNumbers[second], biggestNumbers[first] +} + +func (biggestNumbers *LargestNumbersHeap) Push(x interface{}) { + *biggestNumbers = append(*biggestNumbers, x.(int)) +} + +func (biggestNumbers *LargestNumbersHeap) Pop() interface{} { + oldNumbers := *biggestNumbers + arrayLength := len(oldNumbers) + number := oldNumbers[arrayLength-1] + *biggestNumbers = oldNumbers[0 : arrayLength-1] + return number +} diff --git a/calculator/largestnumbers.go b/calculator/largestnumbers.go new file mode 100644 index 0000000..30cb87a --- /dev/null +++ b/calculator/largestnumbers.go @@ -0,0 +1,51 @@ +package calculator + +import ( + "bufio" + "container/heap" + "fmt" + "os" + "strconv" +) + +func calculateIncomingNumber(numbers *LargestNumbersHeap, newNumber int) *LargestNumbersHeap { + smallest := heap.Pop(numbers).(int) + + if smallest < newNumber { + heap.Push(numbers, newNumber) + } else { + heap.Push(numbers, smallest) + } + + return numbers +} + +func CalculateLargestNumbers(filePath string, topX int) ([]int, error) { + if topX <= 0 { + return []int{}, nil + } + + file, err := os.Open(filePath) + if err != nil { + return []int{}, fmt.Errorf("Could not open file %s with error %w", filePath, err) + } + scanner := bufio.NewScanner(file) + + largestNumbersHeap := LargestNumbersHeap{} + + for scanner.Scan() { + text := scanner.Text() + number, err := strconv.Atoi(text) + if err != nil { + return []int{}, fmt.Errorf("fail to parse text %s with error %w", text, err) + } + if len(largestNumbersHeap) < topX { + largestNumbersHeap = append(largestNumbersHeap, number) + heap.Init(&largestNumbersHeap) + } else { + calculateIncomingNumber(&largestNumbersHeap, number) + } + } + heap.Init(&largestNumbersHeap) + return largestNumbersHeap, nil +} diff --git a/calculator/largestnumbers_test.go b/calculator/largestnumbers_test.go new file mode 100644 index 0000000..03e8118 --- /dev/null +++ b/calculator/largestnumbers_test.go @@ -0,0 +1,96 @@ +package calculator + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestCalculateLargestNumbers(t *testing.T) { + type args struct { + filePath string + topX int + } + tests := []struct { + name string + args args + want []int + wantErr bool + }{ + { + name: "Should return top numbers", + args: args{ + filePath: "../test1.txt", + topX: 3, + }, + want: []int{0, 2, 5}, + wantErr: false, + }, + { + name: "Should throw errors", + args: args{ + filePath: "../test4.txt", + topX: 3, + }, + want: nil, + wantErr: true, + }, + { + name: "Should handle duplicate numbers well", + args: args{ + filePath: "../test3.txt", + topX: 4, + }, + want: []int{23435, 38949, 11111, 11111}, + wantErr: false, + }, + { + name: "Should return all numbers when topX greater than length of array", + args: args{ + filePath: "../test2.txt", + topX: 5, + }, + want: []int{1, 9, 3, 2}, + wantErr: false, + }, + { + name: "Should return 0 when topX is less then 1", + args: args{ + filePath: "../test2.txt", + topX: 0, + }, + want: []int{}, + wantErr: false, + }, + { + name: "Should return 0 when topX is less then 1", + args: args{ + filePath: "../test2.txt", + topX: -1, + }, + want: []int{}, + wantErr: false, + }, + { + name: "Should throw error when fail to parse file", + args: args{ + filePath: "../not_exist.txt", + topX: 3, + }, + want: nil, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := CalculateLargestNumbers(tt.args.filePath, tt.args.topX) + if (err != nil) != tt.wantErr { + t.Errorf("CalculateLargestNumbers() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !assert.ElementsMatch(t, got, tt.want) { + t.Errorf("test CalculateLargestNumbers() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/go.mod b/go.mod index 0326f7a..a3838de 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,11 @@ module github.com/wahyd4/mryum go 1.17 + +require github.com/stretchr/testify v1.7.0 + +require ( + github.com/davecgh/go-spew v1.1.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..b380ae4 --- /dev/null +++ b/go.sum @@ -0,0 +1,10 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go index ecfba05..2499d53 100644 --- a/main.go +++ b/main.go @@ -1,7 +1,22 @@ package main -import "fmt" +import ( + "fmt" + "os" + + "github.com/wahyd4/mryum/calculator" +) + +const ( + filePath = "./testx3.txt" + topNumber = 4 +) func main() { - fmt.Println("hello Mr. Yum") + topNumbers, err := calculator.CalculateLargestNumbers(filePath, topNumber) + if err != nil { + fmt.Println("Oops: " + err.Error()) + os.Exit(1) + } + fmt.Printf("Top %d numbers are: %v", topNumber, topNumbers) } diff --git a/test1.txt b/test1.txt new file mode 100644 index 0000000..8e46e93 --- /dev/null +++ b/test1.txt @@ -0,0 +1,6 @@ +0 +0 +0 +2 +0 +5 diff --git a/test2.txt b/test2.txt new file mode 100644 index 0000000..badcfa6 --- /dev/null +++ b/test2.txt @@ -0,0 +1,4 @@ +1 +9 +3 +2 diff --git a/test3.txt b/test3.txt new file mode 100644 index 0000000..3c0fd36 --- /dev/null +++ b/test3.txt @@ -0,0 +1,14 @@ +7 +1 +122 +23435 +3345 +11111 +38949 +4213 +5233 +561 +11111 +6 +1 +3 diff --git a/test4.txt b/test4.txt new file mode 100644 index 0000000..18fd93a --- /dev/null +++ b/test4.txt @@ -0,0 +1,6 @@ +1 +s +3 +2&&^ +3 +4