From 6c242698a941e26883f139f411390d6d3aaabd86 Mon Sep 17 00:00:00 2001 From: Anthony Green Date: Fri, 9 May 2014 03:04:57 +0100 Subject: [PATCH] Add Circular Buffer exercism --- EXERCISES.txt | 1 + circular-buffer/circular_buffer_test.rb | 108 ++++++++++++++++++++++++ circular-buffer/example.rb | 69 +++++++++++++++ 3 files changed, 178 insertions(+) create mode 100644 circular-buffer/circular_buffer_test.rb create mode 100644 circular-buffer/example.rb diff --git a/EXERCISES.txt b/EXERCISES.txt index b1f5eb7b53..cb73c675ea 100644 --- a/EXERCISES.txt +++ b/EXERCISES.txt @@ -13,6 +13,7 @@ proverb space-age house roman-numerals +circular-buffer clock prime-factors strain diff --git a/circular-buffer/circular_buffer_test.rb b/circular-buffer/circular_buffer_test.rb new file mode 100644 index 0000000000..6f08f345f7 --- /dev/null +++ b/circular-buffer/circular_buffer_test.rb @@ -0,0 +1,108 @@ +require 'minitest/autorun' +require_relative 'circular_buffer' + +class CircularBufferTest < MiniTest::Unit::TestCase + + def test_read_empty_buffer_throws_buffer_empty_exception + buffer = CircularBuffer.new(1) + assert_raises(CircularBuffer::BufferEmptyException) { buffer.read } + end + + def test_write_and_read_back_one_item + skip + buffer = CircularBuffer.new(1) + buffer.write '1' + assert_equal '1', buffer.read + assert_raises(CircularBuffer::BufferEmptyException) { buffer.read } + end + + def test_write_and_read_back_multiple_items + skip + buffer = CircularBuffer.new(2) + buffer.write '1' + buffer.write '2' + assert_equal '1', buffer.read + assert_equal '2', buffer.read + assert_raises(CircularBuffer::BufferEmptyException) { buffer.read } + end + + def test_clearing_buffer + skip + buffer = CircularBuffer.new(3) + (1..3).each { |i| buffer.write String(i) } + buffer.clear + assert_raises(CircularBuffer::BufferEmptyException) { buffer.read } + buffer.write '1' + buffer.write '2' + assert_equal '1', buffer.read + buffer.write '3' + assert_equal '2', buffer.read + end + + def test_alternate_write_and_read + skip + buffer = CircularBuffer.new(2) + buffer.write '1' + assert_equal '1', buffer.read + buffer.write '2' + assert_equal '2', buffer.read + end + + def test_reads_back_oldest_item + skip + buffer = CircularBuffer.new(3) + buffer.write '1' + buffer.write '2' + buffer.read + buffer.write '3' + assert_equal '2', buffer.read + assert_equal '3', buffer.read + end + + def test_writes_of_nil_should_not_occupy_buffer + skip + buffer = CircularBuffer.new(5) + buffer.write nil + (1..3).each { |i| buffer.write String(i) } + assert_equal '1', buffer.read + end + + def test_writing_to_a_full_buffer_throws_an_exception + skip + buffer = CircularBuffer.new(2) + buffer.write '1' + buffer.write '2' + assert_raises(CircularBuffer::BufferFullException) { buffer.write 'A' } + end + + def test_overwriting_oldest_item_in_a_full_buffer + skip + buffer = CircularBuffer.new(2) + buffer.write '1' + buffer.write '2' + buffer.write! 'A' + assert_equal '2', buffer.read + assert_equal 'A', buffer.read + assert_raises(CircularBuffer::BufferEmptyException) { buffer.read } + end + + def test_alternate_read_and_write_into_buffer_overflow + skip + buffer = CircularBuffer.new(5) + (1..3).each { |i| buffer.write String(i) } + buffer.read + buffer.read + buffer.write '4' + buffer.read + (5..8).each { |i| buffer.write String(i) } + buffer.write! 'A' + buffer.write! 'B' + (6..8).each do |i| + assert_equal String(i), buffer.read + end + assert_equal 'A', buffer.read + assert_equal 'B', buffer.read + assert_raises(CircularBuffer::BufferEmptyException) { buffer.read } + end + +end diff --git a/circular-buffer/example.rb b/circular-buffer/example.rb new file mode 100644 index 0000000000..ae4d7c1a4a --- /dev/null +++ b/circular-buffer/example.rb @@ -0,0 +1,69 @@ +class CircularBuffer + + def initialize(capacity) + @capacity = capacity + @write_point = @read_point = 0 + @buffer = Array.new(capacity, nil) + end + + def write data + update_buffer(data) do + raise BufferFullException if full? + @buffer[@write_point] = data + end + end + + def write! data + update_buffer(data) do + @buffer[@write_point] = data + if overwriting? + update_read_point + end + end + end + + def read + raise BufferEmptyException if empty? + data = @buffer[@read_point] + @buffer[@read_point] = nil + update_read_point + data + end + + def clear + @buffer = Array.new(@capacity) + end + + private + + def update_buffer item + return if item.nil? + yield if block_given? + update_write_point + end + + def update_read_point + @read_point = (@read_point + 1) % @capacity + end + + def update_write_point + @write_point = (@write_point + 1) % @capacity + end + + def overwriting? + (full? && @write_point == @read_point) + end + + def empty? + @buffer.compact.empty? + end + + def full? + @buffer.all? + end + + class BufferFullException < Exception; end + + class BufferEmptyException < Exception; end + +end \ No newline at end of file