Skip to content

Commit f69e7fa

Browse files
committed
Добавлен класс LimitedQueue<T> и тесты для него
Введен новый класс LimitedQueue<T>, реализующий ограниченную очередь с фиксированным размером буфера и поддержкой кругового буфера. Реализованы методы для добавления, удаления, просмотра элементов, преобразования в массив и очистки очереди. Добавлены свойства для проверки состояния очереди (пустая/полная). Добавлены тесты для проверки функциональности: - Корректность работы методов `Enqueue`, `Dequeue`, `Peek`, `ToArray`, `Clear`. - Поведение при переполнении (перезапись старых элементов). - Проверка состояний `IsEmpty` и `IsFull`. - Генерация исключений на пустой очереди. - Корректность работы после многократных оберток индексов. - Проверка конструктора с коллекцией и перечисления через `IEnumerable`.
1 parent 16228e3 commit f69e7fa

2 files changed

Lines changed: 273 additions & 0 deletions

File tree

MathCore/LimitedQueue.cs

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
using System.Collections;
2+
3+
namespace MathCore;
4+
5+
/// <summary>Ограниченная очередь с фиксированным размером буфера, использующая круговой буфер для перезаписи старых элементов при переполнении.</summary>
6+
public class LimitedQueue<T>(int Length) : IEnumerable<T>
7+
{
8+
private readonly T[] _Items = new T[Length];
9+
private int _Head;
10+
private int _Tail;
11+
private int _Count;
12+
13+
/// <summary>Создает ограниченную очередь из коллекции элементов, ограничивая размер буфера.</summary>
14+
/// <param name="Items">Коллекция элементов для инициализации очереди.</param>
15+
/// <param name="Length">Максимальный размер буфера.</param>
16+
public LimitedQueue(IEnumerable<T> Items, int Length) : this(Length)
17+
{
18+
foreach (var item in Items)
19+
Enqueue(item);
20+
}
21+
22+
/// <summary>Количество элементов в очереди.</summary>
23+
public int Count => _Count;
24+
25+
/// <summary>Проверяет, пуста ли очередь.</summary>
26+
public bool IsEmpty => _Count == 0;
27+
28+
/// <summary>Проверяет, полна ли очередь.</summary>
29+
public bool IsFull => _Count == _Items.Length;
30+
31+
/// <summary>Добавляет элемент в очередь, перезаписывая старый при переполнении.</summary>
32+
/// <param name="Item">Элемент для добавления.</param>
33+
public void Enqueue(T Item)
34+
{
35+
_Items[_Tail] = Item;
36+
37+
_Tail = (_Tail + 1) % _Items.Length;
38+
39+
if (_Count < _Items.Length)
40+
_Count++;
41+
else
42+
_Head = (_Head + 1) % _Items.Length;
43+
}
44+
45+
/// <summary>Удаляет и возвращает элемент из начала очереди.</summary>
46+
/// <returns>Элемент из начала очереди.</returns>
47+
/// <exception cref="InvalidOperationException">Если очередь пуста.</exception>
48+
public T Dequeue()
49+
{
50+
if (_Count == 0)
51+
throw new InvalidOperationException("Очередь пуста.");
52+
53+
var item = _Items[_Head];
54+
55+
_Head = (_Head + 1) % _Items.Length;
56+
_Count--;
57+
58+
return item;
59+
}
60+
61+
/// <summary>Возвращает элемент из начала очереди без удаления.</summary>
62+
/// <returns>Элемент из начала очереди.</returns>
63+
/// <exception cref="InvalidOperationException">Если очередь пуста.</exception>
64+
public T Peek()
65+
{
66+
if (_Count == 0)
67+
throw new InvalidOperationException("Очередь пуста.");
68+
69+
return _Items[_Head];
70+
}
71+
72+
/// <summary>Преобразует очередь в массив.</summary>
73+
/// <returns>Массив элементов очереди.</returns>
74+
public T[] ToArray()
75+
{
76+
var array = new T[_Count];
77+
for (var i = 0; i < _Count; i++)
78+
array[i] = _Items[(_Head + i) % _Items.Length];
79+
return array;
80+
}
81+
82+
/// <summary>Очищает очередь.</summary>
83+
public void Clear()
84+
{
85+
_Head = 0;
86+
_Tail = 0;
87+
_Count = 0;
88+
}
89+
90+
/// <inheritdoc />
91+
public IEnumerator<T> GetEnumerator()
92+
{
93+
for (var i = 0; i < _Count; i++)
94+
yield return _Items[(_Head + i) % _Items.Length];
95+
}
96+
97+
/// <inheritdoc />
98+
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
99+
}
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
// file: Tests/MathCore.Tests/LimitedQueueTests.cs
2+
namespace MathCore.Tests;
3+
4+
/// <summary>Тесты для ограниченной очереди</summary>
5+
[TestClass]
6+
public class LimitedQueueTests
7+
{
8+
/// <summary>Очередь сохраняет порядок при добавлении без переполнения</summary>
9+
[TestMethod]
10+
public void Enqueue_Dequeue_Preserves_Order_Without_Overflow()
11+
{
12+
var q = new LimitedQueue<int>(5);
13+
14+
for (var i = 1; i <= 5; i++) q.Enqueue(i);
15+
16+
Assert.HasCount(5, q);
17+
Assert.IsTrue(q.IsFull);
18+
Assert.IsFalse(q.IsEmpty);
19+
20+
for (var i = 1; i <= 5; i++)
21+
Assert.AreEqual(i, q.Dequeue());
22+
23+
Assert.IsEmpty(q);
24+
Assert.IsTrue(q.IsEmpty);
25+
Assert.IsFalse(q.IsFull);
26+
}
27+
28+
/// <summary>При переполнении переписывается самый старый элемент</summary>
29+
[TestMethod]
30+
public void Enqueue_Overflow_Rewrites_Oldest()
31+
{
32+
var q = new LimitedQueue<int>(3);
33+
34+
q.Enqueue(1);
35+
q.Enqueue(2);
36+
q.Enqueue(3);
37+
Assert.IsTrue(q.IsFull);
38+
Assert.HasCount(3, q);
39+
40+
q.Enqueue(4); // ожидаем, что 1 будет вытеснен, останутся 2,3,4
41+
42+
Assert.HasCount(3, q);
43+
CollectionAssert.AreEqual(new[] { 2, 3, 4 }, q.ToArray());
44+
45+
Assert.AreEqual(2, q.Dequeue());
46+
Assert.AreEqual(3, q.Dequeue());
47+
Assert.AreEqual(4, q.Dequeue());
48+
Assert.IsTrue(q.IsEmpty);
49+
}
50+
51+
/// <summary>Peek возвращает первый элемент без удаления</summary>
52+
[TestMethod]
53+
public void Peek_Returns_Front_Without_Removing()
54+
{
55+
var q = new LimitedQueue<int>(3);
56+
q.Enqueue(10);
57+
q.Enqueue(20);
58+
59+
Assert.AreEqual(10, q.Peek());
60+
Assert.HasCount(2, q);
61+
Assert.AreEqual(10, q.Dequeue());
62+
Assert.AreEqual(20, q.Dequeue());
63+
}
64+
65+
/// <summary>ToArray возвращает снимок в корректном порядке</summary>
66+
[TestMethod]
67+
public void ToArray_Returns_Correct_Order_With_Wrap()
68+
{
69+
var q = new LimitedQueue<int>(3);
70+
71+
q.Enqueue(1);
72+
q.Enqueue(2);
73+
q.Enqueue(3);
74+
_ = q.Dequeue(); // удаляем 1
75+
q.Enqueue(4); // очередь: 2,3,4
76+
77+
CollectionAssert.AreEqual(new[] { 2, 3, 4 }, q.ToArray());
78+
}
79+
80+
/// <summary>Очистка приводит очередь к пустому состоянию</summary>
81+
[TestMethod]
82+
public void Clear_Empties_Queue()
83+
{
84+
var q = new LimitedQueue<int>(2);
85+
q.Enqueue(7);
86+
q.Enqueue(8);
87+
88+
q.Clear();
89+
90+
Assert.IsEmpty(q);
91+
Assert.IsTrue(q.IsEmpty);
92+
Assert.IsFalse(q.IsFull);
93+
CollectionAssert.AreEqual(Array.Empty<int>(), q.ToArray());
94+
}
95+
96+
/// <summary>Перечисление возвращает элементы в корректном порядке</summary>
97+
[TestMethod]
98+
public void Enumeration_Yields_In_Order()
99+
{
100+
var q = new LimitedQueue<int>(3);
101+
102+
q.Enqueue(1);
103+
q.Enqueue(2);
104+
q.Enqueue(3);
105+
_ = q.Dequeue(); // удаляем 1
106+
q.Enqueue(4); // очередь: 2,3,4
107+
108+
var items = q.ToArray(); // снимок для сравнения
109+
var iterated = q.ToList();
110+
111+
CollectionAssert.AreEqual(items, iterated);
112+
}
113+
114+
/// <summary>Конструктор из коллекции заполняет буфер и отбрасывает самые старые при избытке</summary>
115+
[TestMethod]
116+
public void Construct_From_Enumerable_Trims_To_Capacity()
117+
{
118+
var source = Enumerable.Range(1, 5).ToArray();
119+
var q = new LimitedQueue<int>(source, 3);
120+
121+
Assert.HasCount(3, q);
122+
CollectionAssert.AreEqual(new[] { 3, 4, 5 }, q.ToArray());
123+
}
124+
125+
/// <summary>Dequeue и Peek на пустой очереди генерируют исключение</summary>
126+
[TestMethod]
127+
public void Dequeue_And_Peek_On_Empty_Throw()
128+
{
129+
var q = new LimitedQueue<int>(2);
130+
131+
Assert.ThrowsExactly<InvalidOperationException>(() => q.Dequeue());
132+
Assert.ThrowsExactly<InvalidOperationException>(() => q.Peek());
133+
}
134+
135+
/// <summary>Состояния IsFull и IsEmpty корректно меняются при операциях</summary>
136+
[TestMethod]
137+
public void State_Flags_Are_Correct()
138+
{
139+
var q = new LimitedQueue<int>(2);
140+
141+
Assert.IsTrue(q.IsEmpty);
142+
Assert.IsFalse(q.IsFull);
143+
144+
q.Enqueue(1);
145+
Assert.IsFalse(q.IsEmpty);
146+
Assert.IsFalse(q.IsFull);
147+
148+
q.Enqueue(2);
149+
Assert.IsTrue(q.IsFull);
150+
151+
_ = q.Dequeue();
152+
Assert.IsFalse(q.IsFull);
153+
154+
q.Enqueue(3);
155+
Assert.IsTrue(q.IsFull);
156+
}
157+
158+
/// <summary>Длительная работа с множественными обёртками индексов корректна</summary>
159+
[TestMethod]
160+
public void Works_Correctly_After_Many_Wraps()
161+
{
162+
var q = new LimitedQueue<int>(4);
163+
164+
for (var i = 1; i <= 20; i++)
165+
q.Enqueue(i); // должны остаться последние 4: 17,18,19,20
166+
167+
CollectionAssert.AreEqual(new[] { 17, 18, 19, 20 }, q.ToArray());
168+
169+
for (var i = 17; i <= 20; i++)
170+
Assert.AreEqual(i, q.Dequeue());
171+
172+
Assert.IsTrue(q.IsEmpty);
173+
}
174+
}

0 commit comments

Comments
 (0)