From 8594e6b33ce670b0057c8265f984d7de6eb033c2 Mon Sep 17 00:00:00 2001 From: Goro Daoluong Date: Sun, 10 Apr 2022 01:12:21 +0700 Subject: [PATCH 1/2] Fix bad implement, bad data structure CircularBuffer --- DataStructures/Lists/CircularBuffer.cs | 285 ++++++++++++------ .../DataStructuresTests/CircularBufferTest.cs | 95 +++++- 2 files changed, 281 insertions(+), 99 deletions(-) diff --git a/DataStructures/Lists/CircularBuffer.cs b/DataStructures/Lists/CircularBuffer.cs index b49c7534..bb4e16e3 100644 --- a/DataStructures/Lists/CircularBuffer.cs +++ b/DataStructures/Lists/CircularBuffer.cs @@ -3,9 +3,9 @@ using System.Collections.Generic; using System.Linq; -namespace DataStructures.Lists +namespace DataStructures.Lists { - public class CircularBuffer : IEnumerable, ICollection where T : IComparable + public class CircularBuffer : ICollection { private T[] _circularBuffer; private int _end; @@ -15,40 +15,22 @@ public class CircularBuffer : IEnumerable, ICollection where T : ICompa /// /// Returns the length of the buffer /// - public int Length - { - get - { - return _circularBuffer.Length - 1; - } - } + public int Length => _circularBuffer.Length; /// /// Checks if no element is inserted into the buffer /// - public bool IsEmpty - { - get - { - return _count == 0; - } - } + public bool IsEmpty => _count == 0; /// /// Checks if the buffer is filled up /// - public bool IsFilledUp - { - get - { - return ((_end + 1) % _circularBuffer.Length == _start) && !_circularBuffer[_start].Equals(_circularBuffer[_end]); - } - } + public bool IsFilledUp => Count == Length; /// /// Controls whether data should be overridden when it is continously inserted without reading /// - public bool CanOverride + public bool CanOverride { get; } @@ -56,7 +38,7 @@ public bool CanOverride /// /// Initializes a circular buffer with initial length of 10 /// - public CircularBuffer(bool canOverride = true) : this(_defaultBufferLength, canOverride) + public CircularBuffer(bool canOverride = true) : this(_defaultBufferLength, canOverride) { } @@ -64,13 +46,13 @@ public CircularBuffer(bool canOverride = true) : this(_defaultBufferLength, canO /// Initializes a circular buffer with given length /// /// The length of the buffer - public CircularBuffer(int length, bool canOverride = true) + public CircularBuffer(int length, bool canOverride = true) { - if (length < 1) + if (length < 1) { throw new ArgumentOutOfRangeException("length can not be zero or negative"); } - _circularBuffer = new T[length + 1]; + _circularBuffer = new T[length]; _end = 0; _start = 0; CanOverride = canOverride; @@ -80,52 +62,79 @@ public CircularBuffer(int length, bool canOverride = true) /// Writes value to the back of the buffer /// /// value to be added to the buffer - public void Add(T value) + public void Add(T value) { - if (CanOverride==false && IsFilledUp==true) + if (CanOverride == false && IsFilledUp == true) { throw new CircularBufferFullException($"Circular Buffer is filled up. {value} can not be inserted"); } - innerInsert(value); + InnerInsert(value); } // Inserts data into the buffer without checking if it is full - private void innerInsert(T value) + private void InnerInsert(T value) { _circularBuffer[_end] = value; - _end = (_end + 1) % _circularBuffer.Length; - if (_end == _start) + _end = (_end + 1) % Length; + if (IsFilledUp) { - _start = (_start + 1) % _circularBuffer.Length; + _start = _end; + } + else + { + ++_count; } - - // Count should not be greater than the length of the buffer when overriding - _count = _count < Length ? ++_count : _count; } /// /// Reads and removes the value in front of the buffer, and places the next value in front. /// - public T Pop() + public T Pop() { + if (IsEmpty) + { + throw new InvalidOperationException("The Circular Buffer is empty"); + } + var result = _circularBuffer[_start]; - _circularBuffer[_start] = _circularBuffer[_end]; - _start = (_start + 1) % _circularBuffer.Length; - //Count should not go below Zero when poping an empty buffer. - _count = _count > 0 ? --_count : _count; + _circularBuffer[_start] = default; + _start = (_start + 1) % Length; + --_count; return result; } + + + + #region IEnumerable Implementation - public IEnumerator GetEnumerator() + public IEnumerator GetEnumerator() { - for (int i = _start; i < Count; i++) + if (!IsEmpty) { - yield return _circularBuffer[i]; + if (_start < _end) + { + for (int i = _start; i < _end; i++) + { + yield return _circularBuffer[i]; + } + } + else + { + for (int i = _start; i < _circularBuffer.Length; i++) + { + yield return _circularBuffer[i]; + } + + for (int i = 0; i < _end; i++) + { + yield return _circularBuffer[i]; + } + } } } - IEnumerator IEnumerable.GetEnumerator() + IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } @@ -136,51 +145,106 @@ IEnumerator IEnumerable.GetEnumerator() /// /// Returns the number of elements. /// - public int Count - { - get - { - return _count; - } - } + public int Count => _count; /// /// Checks whether this collection is readonly /// - public bool IsReadOnly - { - get - { - return false; - } - } + public bool IsReadOnly => false; /// /// Clears this instance /// - public void Clear() + public void Clear() { _count = 0; _start = 0; _end = 0; - _circularBuffer = new T[Length + 1]; + Array.Clear(_circularBuffer, 0, _circularBuffer.Length); } /// /// Checks whether the buffer contains an item /// - public bool Contains(T item) + public bool Contains(T item) { - return _circularBuffer.Contains(item); + if (IsEmpty) + { + return false; + } + else if (_start < _end) + { + if (item == null) + { + for (int i = _start; i < _end; i++) + { + if (_circularBuffer[i] == null) + { + return true; + } + } + } + else + { + for (int i = _start; i < _end; i++) + { + if (_circularBuffer[i] != null && _circularBuffer[i].Equals(item)) + { + return true; + } + } + } + } + else + { + if (item == null) + { + for (int i = _start; i < _circularBuffer.Length; i++) + { + if (_circularBuffer[i] == null) + { + return true; + } + } + + for (int i = 0; i < _end; i++) + { + if (_circularBuffer[i] == null) + { + return true; + } + } + } + else + { + for (int i = _start; i < _circularBuffer.Length; i++) + { + if (_circularBuffer[i] != null && _circularBuffer[i].Equals(item)) + { + return true; + } + } + + for (int i = 0; i < _end; i++) + { + if (_circularBuffer[i] != null && _circularBuffer[i].Equals(item)) + { + return true; + } + } + } + } + + return false; } /// /// Copies this buffer to an array /// - public void CopyTo(T[] array, int arrayIndex) + public void CopyTo(T[] array, int arrayIndex) { - if (array == null) + if (array == null) { throw new ArgumentNullException("array can not be null"); } - if (array.Length == 0 || arrayIndex >= array.Length || arrayIndex < 0) + if (array.Length == 0 || arrayIndex >= array.Length || arrayIndex < 0) { throw new IndexOutOfRangeException(); } @@ -189,14 +253,14 @@ public void CopyTo(T[] array, int arrayIndex) var enumarator = GetEnumerator(); // Copy elements if there is any in the buffer and if the index is within the valid range - while (arrayIndex < array.Length) + while (arrayIndex < array.Length) { - if (enumarator.MoveNext()) + if (enumarator.MoveNext()) { array[arrayIndex] = enumarator.Current; arrayIndex++; } - else + else { break; } @@ -205,26 +269,73 @@ public void CopyTo(T[] array, int arrayIndex) /// /// Removes an item from the buffer /// - public bool Remove(T item) + public bool Remove(T item) { - if (!IsEmpty && Contains(item)) + if (!IsEmpty && Contains(item)) { - var sourceArray = _circularBuffer.Except(new T[] { item }).ToArray(); - _circularBuffer = new T[Length + 1]; - Array.Copy(sourceArray, _circularBuffer, sourceArray.Length); - - if (!Equals(item,default(T))) + int shiftSpace = 0; + if (_start < _end) { - _end = sourceArray.Length - 1; - _count = sourceArray.Length-1; + for (int i = _start; i < _end; i++) + { + if ((item == null && _circularBuffer[i] ==null) || (_circularBuffer[i] != null && _circularBuffer[i].Equals(item))) + { + shiftSpace++; + } + else + { + if (shiftSpace > 0) + { + _circularBuffer[i - shiftSpace] = _circularBuffer[i]; + } + } + } + _end -= shiftSpace; + _count -= shiftSpace; } - else + else { - _end = sourceArray.Length; - _count = sourceArray.Length; - } + for (int i = _start; i < _circularBuffer.Length; i++) + { + if ((item == null && _circularBuffer[i] == null) || (_circularBuffer[i] != null && _circularBuffer[i].Equals(item))) + { + shiftSpace++; + } + else + { + if (shiftSpace > 0) + { + _circularBuffer[i - shiftSpace] = _circularBuffer[i]; + } + } + } + + for (int i = 0; i < _end; i++) + { + if ((item == null && _circularBuffer[i] == null) || (_circularBuffer[i] != null && _circularBuffer[i].Equals(item))) + { + shiftSpace++; + } + else + { + if (shiftSpace > i) + { + _circularBuffer[_circularBuffer.Length + (i - shiftSpace)] = _circularBuffer[i]; + } + else + { + _circularBuffer[i - shiftSpace] = _circularBuffer[i]; + } + } + } - return true; + _end -= shiftSpace; + if (_end < 0) + { + _end = _circularBuffer.Length + _end; + } + _count -= shiftSpace; + } } return false; @@ -232,9 +343,9 @@ public bool Remove(T item) #endregion } - public class CircularBufferFullException : Exception + public class CircularBufferFullException : Exception { - public CircularBufferFullException(string message) : base(message) + public CircularBufferFullException(string message) : base(message) { } } diff --git a/UnitTest/DataStructuresTests/CircularBufferTest.cs b/UnitTest/DataStructuresTests/CircularBufferTest.cs index efb0bb30..d8754a9d 100644 --- a/UnitTest/DataStructuresTests/CircularBufferTest.cs +++ b/UnitTest/DataStructuresTests/CircularBufferTest.cs @@ -80,19 +80,12 @@ public static void WritesAndReadsValue() var result2 = circularBuffer.Pop(); var result3 = circularBuffer.Pop(); var result4 = circularBuffer.Pop(); - var result5 = circularBuffer.Pop(); - var result6 = circularBuffer.Pop(); - var result7 = circularBuffer.Pop(); - var result8 = circularBuffer.Pop(); Assert.Equal(13, result1); Assert.Equal(43, result2); Assert.Equal(23, result3); Assert.Equal(2, result4); - Assert.Equal(0, result5); - Assert.Equal(0, result6); - Assert.Equal(0, result7); - Assert.Equal(0, result8); + Assert.Throws(()=> circularBuffer.Pop()); } [Fact] @@ -147,7 +140,7 @@ public static void TestingICollectionImplementation() circularBuffer.Add(34); circularBuffer.Add(24); //Testing contains - Assert.True(circularBuffer.Contains(3)); + Assert.Contains(3, circularBuffer); //Testing CopyTo var array = new byte[3]; @@ -160,9 +153,7 @@ public static void TestingICollectionImplementation() Assert.Equal(3, circularBuffer.Count); //Testing clear circularBuffer.Clear(); - Assert.Equal(0, circularBuffer.Pop()); - Assert.Equal(0, circularBuffer.Pop()); - Assert.Equal(0, circularBuffer.Pop()); + Assert.Throws(() => circularBuffer.Pop()); Assert.Empty(circularBuffer); } [Fact] @@ -254,6 +245,86 @@ public static void TestingRemoveMethod() { Assert.Equal("three", stringBuffer.Pop()); Assert.Equal("four", stringBuffer.Pop()); Assert.Equal("five", stringBuffer.Pop()); + + + //Test for removing overrided buffer + circularBuffer = new CircularBuffer(12); + circularBuffer.Add(1); + circularBuffer.Add(0); + circularBuffer.Add(2); + circularBuffer.Add(0); + circularBuffer.Add(3); + circularBuffer.Add(0); + circularBuffer.Add(4); + circularBuffer.Add(0); + circularBuffer.Add(5); + circularBuffer.Add(0); + circularBuffer.Add(6); + circularBuffer.Add(0); + circularBuffer.Add(7); + circularBuffer.Add(0); + circularBuffer.Add(8); + + + circularBuffer.Remove(0); + Assert.Equal(6, circularBuffer.Count); + + Assert.Equal(3, circularBuffer.Pop()); + Assert.Equal(4, circularBuffer.Pop()); + Assert.Equal(5, circularBuffer.Pop()); + Assert.Equal(6, circularBuffer.Pop()); + Assert.Equal(7, circularBuffer.Pop()); + Assert.Equal(8, circularBuffer.Pop()); + } + + [Fact] + public static void TestImplimentEnumerator() + { + Random rnd = new Random(); + int[] values = new int[20]; + for (int i = 0; i < values.Length; i++) + values[i] = rnd.Next(1, 99); + string arrayValue = string.Join(", ", values); + + // buffer write two circles + CircularBuffer buffer = new CircularBuffer(values.Length / 2); + var subvalues = new int[buffer.Length]; + + Array.Copy(values, values.Length - buffer.Length, subvalues, 0, buffer.Length); + + foreach (int value in values) + buffer.Add(value); + + string arraySubValue = string.Join(", ", subvalues); + string bufferValue = string.Join(", ", buffer); + + Assert.Equal(arraySubValue, bufferValue); + + + // buffer not write full circle + buffer = new CircularBuffer(values.Length + 2); + foreach (int value in values) + buffer.Add(value); + + bufferValue = string.Join(", ", buffer); + + Assert.Equal(arrayValue, bufferValue); + + + // buffer write over one circle + buffer = new CircularBuffer(values.Length/2 + 2); + foreach (int value in values) + buffer.Add(value); + + buffer.Pop(); + buffer.Pop(); + + subvalues = new int[10]; + Array.Copy(values, values.Length - 10, subvalues, 0, 10); + arraySubValue = string.Join(", ", subvalues); + bufferValue = string.Join(", ", buffer); + + Assert.Equal(arraySubValue, bufferValue); } } } From 0705e2b067b222e0079784a4e43e771fec4c65e2 Mon Sep 17 00:00:00 2001 From: Goro Daoluong Date: Mon, 11 Apr 2022 02:08:54 +0700 Subject: [PATCH 2/2] Update more test case for CircularBuffer --- UnitTest/DataStructuresTests/CircularBufferTest.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/UnitTest/DataStructuresTests/CircularBufferTest.cs b/UnitTest/DataStructuresTests/CircularBufferTest.cs index d8754a9d..33270ea0 100644 --- a/UnitTest/DataStructuresTests/CircularBufferTest.cs +++ b/UnitTest/DataStructuresTests/CircularBufferTest.cs @@ -288,6 +288,12 @@ public static void TestImplimentEnumerator() // buffer write two circles CircularBuffer buffer = new CircularBuffer(values.Length / 2); + + string bufferValue = string.Join(", ", buffer); + Assert.Empty(bufferValue); + + + var subvalues = new int[buffer.Length]; Array.Copy(values, values.Length - buffer.Length, subvalues, 0, buffer.Length); @@ -296,7 +302,7 @@ public static void TestImplimentEnumerator() buffer.Add(value); string arraySubValue = string.Join(", ", subvalues); - string bufferValue = string.Join(", ", buffer); + bufferValue = string.Join(", ", buffer); Assert.Equal(arraySubValue, bufferValue);