diff --git a/README.md b/README.md index 450c65a..ad8a13b 100644 --- a/README.md +++ b/README.md @@ -340,6 +340,7 @@ problems from * [Day 350](https://github.com/vaskoz/dailycodingproblem-go/issues/695) * [Day 352](https://github.com/vaskoz/dailycodingproblem-go/issues/699) * [Day 353](https://github.com/vaskoz/dailycodingproblem-go/issues/701) +* [Day 356](https://github.com/vaskoz/dailycodingproblem-go/issues/705) * [Day 359](https://github.com/vaskoz/dailycodingproblem-go/issues/708) * [Day 360](https://github.com/vaskoz/dailycodingproblem-go/issues/709) * [Day 361](https://github.com/vaskoz/dailycodingproblem-go/issues/712) diff --git a/day356/problem.go b/day356/problem.go new file mode 100644 index 0000000..bceab3b --- /dev/null +++ b/day356/problem.go @@ -0,0 +1,102 @@ +package day356 + +import "errors" + +const fixedArraySize = 10 + +var errEmpty = errors.New("queue is empty") +var errFull = errors.New("queue is full") + +// ErrEmpty is the error returned if queue is empty. +func ErrEmpty() error { + return errEmpty +} + +// ErrFull is the error returned if queue is full. +func ErrFull() error { + return errFull +} + +// NewQueueFLA returns a new instance of QueueFLA +// with a given capacity. +func NewQueueFLA(capacity int) QueueFLA { + slots := capacity / fixedArraySize + if capacity%fixedArraySize != 0 { + slots++ + } + return &queueFLA{ + set: make([][fixedArraySize]interface{}, slots), + size: 0, + capacity: capacity, + frontSlot: -1, + frontPos: -1, + backSlot: -1, + backPos: -1, + } +} + +// QueueFLA is a queue implemented using a set +// of fixed-length arrays. +// In this implementation, the fixed array size is 10. +type QueueFLA interface { + Enqueue(interface{}) error + Dequeue() (interface{}, error) + Size() int +} + +type queueFLA struct { + set [][fixedArraySize]interface{} + size, capacity int + frontSlot, frontPos int + backSlot, backPos int +} + +// Enqueue attempts to add an item to the queue if there is room. +func (q *queueFLA) Enqueue(item interface{}) error { + if q.size == q.capacity { + return errFull + } + q.size++ + if q.frontSlot == -1 && q.frontPos == -1 { + q.frontSlot++ + q.frontPos++ + q.set[q.frontSlot][q.frontPos] = item + q.backSlot++ + q.backPos++ + } else { + q.backPos++ + if q.backPos == fixedArraySize { + q.backPos = 0 + q.backSlot++ + if q.backSlot == len(q.set) { + q.backSlot = 0 + } + } + q.set[q.backSlot][q.backPos] = item + } + return nil +} + +// Dequeue attempts to remove an item from the queue if +// the queue is not empty. +func (q *queueFLA) Dequeue() (interface{}, error) { + if q.size == 0 { + return nil, errEmpty + } + q.size-- + res := q.set[q.frontSlot][q.frontPos] + q.frontPos++ + if q.frontPos == fixedArraySize { + q.frontPos = 0 + q.frontSlot++ + if q.frontSlot == len(q.set) { + q.frontSlot = 0 + } + } + return res, nil +} + +// Size returns the number of elements currently in the queue. +func (q *queueFLA) Size() int { + return q.size +} diff --git a/day356/problem_test.go b/day356/problem_test.go new file mode 100644 index 0000000..e8f73be --- /dev/null +++ b/day356/problem_test.go @@ -0,0 +1,81 @@ +package day356 + +import "testing" + +type action struct { + act string + data interface{} + err error +} + +// nolint +var testcases = []struct { + actions []action +}{ + { + []action{ + {"E", 100, nil}, {"S", 1, nil}, {"D", 100, nil}, + {"D", nil, ErrEmpty()}, + }, + }, + { + []action{ + {"E", 100, nil}, {"E", 200, nil}, {"E", 300, nil}, {"E", 400, nil}, {"E", 500, nil}, + {"S", 5, nil}, {"E", 1000, ErrFull()}, + {"D", 100, nil}, {"D", 200, nil}, + {"S", 3, nil}, + {"D", 300, nil}, {"D", 400, nil}, + {"S", 1, nil}, + {"E", 600, nil}, {"E", 700, nil}, {"E", 800, nil}, {"E", 900, nil}, + {"E", 1000, ErrFull()}, + {"D", 500, nil}, {"D", 600, nil}, {"D", 700, nil}, {"D", 800, nil}, + {"S", 1, nil}, + {"E", 1000, nil}, {"E", 1100, nil}, {"E", 1200, nil}, {"E", 1300, nil}, + {"S", 5, nil}, + {"D", 900, nil}, {"D", 1000, nil}, {"D", 1100, nil}, {"D", 1200, nil}, {"D", 1300, nil}, + {"S", 0, nil}, + {"D", nil, ErrEmpty()}, + }, + }, +} + +func TestQueueFLA(t *testing.T) { + t.Parallel() + for _, tc := range testcases { + q := NewQueueFLA(5) + for _, action := range tc.actions { + switch action.act { + case "E": + if err := q.Enqueue(action.data); err != action.err { + t.Errorf("Expected %v, got %v", action.err, err) + } + case "D": + if item, err := q.Dequeue(); item != action.data || err != action.err { + t.Errorf("Expected (%v,%v), got (%v,%v)", action.data, action.err, item, err) + } + case "S": + if s := q.Size(); s != action.data { + t.Errorf("Expected a size of %d, got %d", action.data, s) + } + } + } + } +} + +func BenchmarkQueueFLA(b *testing.B) { + for i := 0; i < b.N; i++ { + for _, tc := range testcases { + q := NewQueueFLA(5) + for _, action := range tc.actions { + switch action.act { + case "E": + q.Enqueue(action.data) // nolint + case "D": + q.Dequeue() // nolint + case "S": + q.Size() + } + } + } + } +}