From af883704993e914a9c481691cd8f2b0361321e74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A1s=20B=20Nagy?= <20251272+BNAndras@users.noreply.github.com> Date: Mon, 4 Mar 2024 22:25:40 -0800 Subject: [PATCH 1/2] Add circular-buffer --- config.json | 10 +- .../circular-buffer/.docs/instructions.md | 58 ++++++++++ .../circular-buffer/.meta/config.json | 19 ++++ .../circular-buffer/.meta/example.vim | 54 +++++++++ .../practice/circular-buffer/.meta/tests.toml | 52 +++++++++ .../circular-buffer/circular_buffer.vader | 105 ++++++++++++++++++ .../circular-buffer/circular_buffer.vim | 6 + 7 files changed, 303 insertions(+), 1 deletion(-) create mode 100644 exercises/practice/circular-buffer/.docs/instructions.md create mode 100644 exercises/practice/circular-buffer/.meta/config.json create mode 100644 exercises/practice/circular-buffer/.meta/example.vim create mode 100644 exercises/practice/circular-buffer/.meta/tests.toml create mode 100644 exercises/practice/circular-buffer/circular_buffer.vader create mode 100644 exercises/practice/circular-buffer/circular_buffer.vim diff --git a/config.json b/config.json index 6625d39..0fdbe68 100644 --- a/config.json +++ b/config.json @@ -543,6 +543,14 @@ "strings" ] }, + { + "slug": "circular-buffer", + "name": "Circular Buffer", + "uuid": "75477d21-a1cb-4ed8-8be9-0c9260cbecf9", + "practices": [], + "prerequisites": [], + "difficulty": 4 + }, { "slug": "custom-set", "name": "Custom Set", @@ -571,7 +579,7 @@ "practices": [], "prerequisites": [], "difficulty": 4 - }, + }, { "slug": "nucleotide-count", "name": "Nucleotide Count", diff --git a/exercises/practice/circular-buffer/.docs/instructions.md b/exercises/practice/circular-buffer/.docs/instructions.md new file mode 100644 index 0000000..2ba1fda --- /dev/null +++ b/exercises/practice/circular-buffer/.docs/instructions.md @@ -0,0 +1,58 @@ +# Instructions + +A circular buffer, cyclic buffer or ring buffer is a data structure that uses a single, fixed-size buffer as if it were connected end-to-end. + +A circular buffer first starts empty and of some predefined length. +For example, this is a 7-element buffer: + +```text +[ ][ ][ ][ ][ ][ ][ ] +``` + +Assume that a 1 is written into the middle of the buffer (exact starting location does not matter in a circular buffer): + +```text +[ ][ ][ ][1][ ][ ][ ] +``` + +Then assume that two more elements are added — 2 & 3 — which get appended after the 1: + +```text +[ ][ ][ ][1][2][3][ ] +``` + +If two elements are then removed from the buffer, the oldest values inside the buffer are removed. +The two elements removed, in this case, are 1 & 2, leaving the buffer with just a 3: + +```text +[ ][ ][ ][ ][ ][3][ ] +``` + +If the buffer has 7 elements then it is completely full: + +```text +[5][6][7][8][9][3][4] +``` + +When the buffer is full an error will be raised, alerting the client that further writes are blocked until a slot becomes free. + +When the buffer is full, the client can opt to overwrite the oldest data with a forced write. +In this case, two more elements — A & B — are added and they overwrite the 3 & 4: + +```text +[5][6][7][8][9][A][B] +``` + +3 & 4 have been replaced by A & B making 5 now the oldest data in the buffer. +Finally, if two elements are removed then what would be returned is 5 & 6 yielding the buffer: + +```text +[ ][ ][7][8][9][A][B] +``` + +Because there is space available, if the client again uses overwrite to store C & D then the space where 5 & 6 were stored previously will be used not the location of 7 & 8. +7 is still the oldest element and the buffer is once again full. + +```text +[C][D][7][8][9][A][B] +``` diff --git a/exercises/practice/circular-buffer/.meta/config.json b/exercises/practice/circular-buffer/.meta/config.json new file mode 100644 index 0000000..e0da7a7 --- /dev/null +++ b/exercises/practice/circular-buffer/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "circular_buffer.vim" + ], + "test": [ + "circular_buffer.vader" + ], + "example": [ + ".meta/example.vim" + ] + }, + "blurb": "A data structure that uses a single, fixed-size buffer as if it were connected end-to-end.", + "source": "Wikipedia", + "source_url": "https://en.wikipedia.org/wiki/Circular_buffer" +} diff --git a/exercises/practice/circular-buffer/.meta/example.vim b/exercises/practice/circular-buffer/.meta/example.vim new file mode 100644 index 0000000..12ddc78 --- /dev/null +++ b/exercises/practice/circular-buffer/.meta/example.vim @@ -0,0 +1,54 @@ +" +" Returns a dictionary representing a circular buffer with a set capacity +" +function! CircularBuffer(capacity) abort + return { + \ 'capacity': a:capacity, + \ 'size': 0, + \ 'data': repeat([v:null], a:capacity), + \ 'readIndex': 0, + \ 'writeIndex': 0, + \ 'Read': funcref('s:Read'), + \ 'Write': funcref('s:Write'), + \ 'Overwrite': funcref('s:Overwrite'), + \ 'Clear': funcref('s:Clear') + \ } +endfunction + +function! s:Read() dict + if self.size == 0 + throw 'Empty buffer' + endif + + let l:value = self.data[self.readIndex] + let self.readIndex = (self.readIndex + 1) % self.capacity + let self.size -= 1 + + return l:value +endfunction + +function! s:Write(value) dict + if self.size >= self.capacity + throw 'Full buffer' + endif + + let self.data[self.writeIndex] = a:value + let self.writeIndex = (self.writeIndex + 1) % self.capacity + let self.size += 1 +endfunction + +function! s:Overwrite(value) dict + if self.size == self.capacity + call self.Read() + endif + + call self.Write(a:value) +endfunction + +function! s:Clear() dict + let l:capacity = len(self.data) + let self.data = repeat([v:null], l:capacity) + let self.readIndex = 0 + let self.writeIndex = 0 + let self.size = 0 +endfunction diff --git a/exercises/practice/circular-buffer/.meta/tests.toml b/exercises/practice/circular-buffer/.meta/tests.toml new file mode 100644 index 0000000..0fb3143 --- /dev/null +++ b/exercises/practice/circular-buffer/.meta/tests.toml @@ -0,0 +1,52 @@ +# This is an auto-generated file. +# +# Regenerating this file via `configlet sync` will: +# - Recreate every `description` key/value pair +# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications +# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) +# - Preserve any other key/value pair +# +# As user-added comments (using the # character) will be removed when this file +# is regenerated, comments can be added via a `comment` key. + +[28268ed4-4ff3-45f3-820e-895b44d53dfa] +description = "reading empty buffer should fail" + +[2e6db04a-58a1-425d-ade8-ac30b5f318f3] +description = "can read an item just written" + +[90741fe8-a448-45ce-be2b-de009a24c144] +description = "each item may only be read once" + +[be0e62d5-da9c-47a8-b037-5db21827baa7] +description = "items are read in the order they are written" + +[2af22046-3e44-4235-bfe6-05ba60439d38] +description = "full buffer can't be written to" + +[547d192c-bbf0-4369-b8fa-fc37e71f2393] +description = "a read frees up capacity for another write" + +[04a56659-3a81-4113-816b-6ecb659b4471] +description = "read position is maintained even across multiple writes" + +[60c3a19a-81a7-43d7-bb0a-f07242b1111f] +description = "items cleared out of buffer can't be read" + +[45f3ae89-3470-49f3-b50e-362e4b330a59] +description = "clear frees up capacity for another write" + +[e1ac5170-a026-4725-bfbe-0cf332eddecd] +description = "clear does nothing on empty buffer" + +[9c2d4f26-3ec7-453f-a895-7e7ff8ae7b5b] +description = "overwrite acts like write on non-full buffer" + +[880f916b-5039-475c-bd5c-83463c36a147] +description = "overwrite replaces the oldest item on full buffer" + +[bfecab5b-aca1-4fab-a2b0-cd4af2b053c3] +description = "overwrite replaces the oldest item remaining in buffer following a read" + +[9cebe63a-c405-437b-8b62-e3fdc1ecec5a] +description = "initial clear does not affect wrapping around" diff --git a/exercises/practice/circular-buffer/circular_buffer.vader b/exercises/practice/circular-buffer/circular_buffer.vader new file mode 100644 index 0000000..9844ec2 --- /dev/null +++ b/exercises/practice/circular-buffer/circular_buffer.vader @@ -0,0 +1,105 @@ + +Execute (reading empty buffer should fail): + let g:buffer = CircularBuffer(1) + AssertThrows call g:buffer.Read() + AssertEqual "Empty buffer", g:vader_exception + +Execute (can read an item just written): + let g:buffer = CircularBuffer(1) + call g:buffer.Write(1) + AssertEqual g:buffer.Read(), 1 + +Execute (each item may only be read once): + let g:buffer = CircularBuffer(1) + call g:buffer.Write(1) + AssertEqual g:buffer.Read(), 1 + AssertThrows call g:buffer.Read() + AssertEqual "Empty buffer", g:vader_exception + +Execute (items are read in the order they are written): + let g:buffer = CircularBuffer(2) + call g:buffer.Write(1) + call g:buffer.Write(2) + AssertEqual g:buffer.Read(), 1 + AssertEqual g:buffer.Read(), 2 + +Execute (full buffer can't be written to): + let g:buffer = CircularBuffer(1) + call g:buffer.Write(1) + AssertThrows call g:buffer.Write(2) + AssertEqual "Full buffer", g:vader_exception + +Execute (a read frees up capacity for another write): + let g:buffer = CircularBuffer(1) + call g:buffer.Write(1) + AssertEqual g:buffer.Read(), 1 + call g:buffer.Write(2) + AssertEqual g:buffer.Read(), 2 + +Execute (read position is maintained even across multiple writes): + let g:buffer = CircularBuffer(3) + call g:buffer.Write(1) + call g:buffer.Write(2) + AssertEqual g:buffer.Read(), 1 + call g:buffer.Write(3) + AssertEqual g:buffer.Read(), 2 + AssertEqual g:buffer.Read(), 3 + +Execute (items cleared out of buffer can't be read): + let g:buffer = CircularBuffer(1) + call g:buffer.Write(1) + call g:buffer.Clear() + AssertThrows call g:buffer.Read() + AssertEqual "Empty buffer", g:vader_exception + +Execute (clear frees up capacity for another write): + let g:buffer = CircularBuffer(1) + call g:buffer.Write(1) + call g:buffer.Clear() + call g:buffer.Write(2) + AssertEqual g:buffer.Read(), 2 + +Execute (clear does nothing on empty buffer): + let g:buffer = CircularBuffer(1) + call g:buffer.Clear() + call g:buffer.Write(1) + AssertEqual g:buffer.Read(), 1 + +Execute (overwrite acts like write on non-full buffer): + let g:buffer = CircularBuffer(2) + call g:buffer.Write(1) + call g:buffer.Overwrite(2) + AssertEqual g:buffer.Read(), 1 + AssertEqual g:buffer.Read(), 2 + +Execute (overwrite replaces the oldest item on full buffer): + let g:buffer = CircularBuffer(2) + call g:buffer.Write(1) + call g:buffer.Write(2) + call g:buffer.Overwrite(3) + AssertEqual g:buffer.Read(), 2 + AssertEqual g:buffer.Read(), 3 + +Execute (overwrite replaces the oldest item remaining in buffer following a read): + let g:buffer = CircularBuffer(3) + call g:buffer.Write(1) + call g:buffer.Write(2) + call g:buffer.Write(3) + AssertEqual g:buffer.Read(), 1 + call g:buffer.Write(4) + call g:buffer.Overwrite(5) + AssertEqual g:buffer.Read(), 3 + AssertEqual g:buffer.Read(), 4 + AssertEqual g:buffer.Read(), 5 + +Execute (initial clear does not affect wrapping around): + let g:buffer = CircularBuffer(2) + call g:buffer.Clear() + call g:buffer.Write(1) + call g:buffer.Write(2) + call g:buffer.Overwrite(3) + call g:buffer.Overwrite(4) + AssertEqual g:buffer.Read(), 3 + AssertEqual g:buffer.Read(), 4 + AssertThrows call g:buffer.Read() + AssertEqual "Empty buffer", g:vader_exception \ No newline at end of file diff --git a/exercises/practice/circular-buffer/circular_buffer.vim b/exercises/practice/circular-buffer/circular_buffer.vim new file mode 100644 index 0000000..d9e7000 --- /dev/null +++ b/exercises/practice/circular-buffer/circular_buffer.vim @@ -0,0 +1,6 @@ +" +" Returns a dictionary representing a circular buffer with a set capacity +" +function! CircularBuffer(capacity) abort + " your code goes here +endfunction From d18aa5b5b4155c07a0bf960a348ff314ebf54168 Mon Sep 17 00:00:00 2001 From: Victor Goff Date: Tue, 5 Mar 2024 11:04:14 -0500 Subject: [PATCH 2/2] EOL --- exercises/practice/circular-buffer/circular_buffer.vader | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/exercises/practice/circular-buffer/circular_buffer.vader b/exercises/practice/circular-buffer/circular_buffer.vader index 9844ec2..d9e409c 100644 --- a/exercises/practice/circular-buffer/circular_buffer.vader +++ b/exercises/practice/circular-buffer/circular_buffer.vader @@ -102,4 +102,5 @@ Execute (initial clear does not affect wrapping around): AssertEqual g:buffer.Read(), 3 AssertEqual g:buffer.Read(), 4 AssertThrows call g:buffer.Read() - AssertEqual "Empty buffer", g:vader_exception \ No newline at end of file + AssertEqual "Empty buffer", g:vader_exception + \ No newline at end of file