From ca096a3f0f7c5039e8a815f369be51e5cf7cb441 Mon Sep 17 00:00:00 2001 From: kzrnm Date: Mon, 25 Sep 2023 19:57:17 +0900 Subject: [PATCH] Add Deque.Grow(int capacity) --- CHANGELOG.md | 3 + Source/ac-library-csharp/STL/Deque.cs | 64 ++++++++----- Test/ac-library-csharp.Test/STL/DequeTest.cs | 94 +++++++++++++++++--- 3 files changed, 128 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2417423..7c5a67c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Added +- Add `Deque.Grow(int capacity)` + ### Changed - Fix empty `Deque.GetEnumerator()` diff --git a/Source/ac-library-csharp/STL/Deque.cs b/Source/ac-library-csharp/STL/Deque.cs index e7848d3..495b028 100644 --- a/Source/ac-library-csharp/STL/Deque.cs +++ b/Source/ac-library-csharp/STL/Deque.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; +using System.Numerics; using System.Runtime.CompilerServices; using AtCoder.Internal; @@ -14,7 +15,6 @@ namespace AtCoder [DebuggerDisplay("Count = {" + nameof(Count) + "}")] public class Deque : IEnumerable, IReadOnlyCollection, ICollection { - [EditorBrowsable(Never)] public T[] data; @@ -30,14 +30,8 @@ public class Deque : IEnumerable, IReadOnlyCollection, ICollection public Deque() : this(1) { } public Deque(int capacity) { - capacity = -#if NET7_0_OR_GREATER - (int)System.Numerics.BitOperations.RoundUpToPowerOf2((uint)capacity + 1u); -#else - 1 << (InternalBit.CeilPow2(capacity + 1)); -#endif - data = new T[capacity]; - mask = capacity - 1; + data = Array.Empty(); + Grow(capacity); } public int Count => (tail - head) & mask; @@ -64,28 +58,58 @@ public T PopLast() [MethodImpl(256)] public void AddFirst(T item) { - data[head = (head - 1) & mask] = item; - if (head == tail) Resize(); + var nxt = (head - 1) & mask; + if (nxt == tail) + { + Grow(); + nxt = (head - 1) & mask; + } + data[nxt] = item; + head = nxt; } [MethodImpl(256)] public void AddLast(T item) { + var nxt = (tail + 1) & mask; + if (head == nxt) + { + Grow(); + nxt = (tail + 1) & mask; + } data[tail] = item; - tail = (tail + 1) & mask; - if (head == tail) Resize(); + tail = nxt; } [EditorBrowsable(Never)] - public void Resize() + public void Grow() => Grow(Math.Max(mask << 1, 0b11)); + + [EditorBrowsable(Never)] + public void Grow(int capacity) { - var oldSize = data.Length; - var newArray = new T[oldSize << 1]; + capacity = +#if NET7_0_OR_GREATER + (int)BitOperations.RoundUpToPowerOf2((uint)capacity + 1u); +#else + 1 << (InternalBit.CeilPow2(capacity + 1)); +#endif + Debug.Assert(BitOperations.PopCount((uint)capacity) == 1); + if (capacity <= data.Length) return; + var oldSize = Count; + var newArray = new T[capacity]; + if (head <= tail) + { + Array.Copy(data, head, newArray, 0, oldSize); + } + else + { + var hsize = data.Length - head; + Debug.Assert(hsize + tail == oldSize); + Array.Copy(data, head, newArray, 0, hsize); + Array.Copy(data, 0, newArray, hsize, tail); + } - var hsize = oldSize - head; - Array.Copy(data, head, newArray, 0, hsize); - Array.Copy(data, 0, newArray, hsize, tail); data = newArray; - mask = data.Length - 1; + mask = capacity - 1; head = 0; tail = oldSize; } diff --git a/Test/ac-library-csharp.Test/STL/DequeTest.cs b/Test/ac-library-csharp.Test/STL/DequeTest.cs index 585fb5f..37e61f2 100644 --- a/Test/ac-library-csharp.Test/STL/DequeTest.cs +++ b/Test/ac-library-csharp.Test/STL/DequeTest.cs @@ -58,23 +58,27 @@ void AddFirst(int num) { deque.AddFirst(num); list.AddFirst(num); + deque.Should().Equal(list); } void AddLast(int num) { deque.AddLast(num); list.AddLast(num); + deque.Should().Equal(list); } void PopFirst() { deque.PopFirst(); list.RemoveFirst(); + deque.Should().Equal(list); } void PopLast() { deque.PopLast(); list.RemoveLast(); + deque.Should().Equal(list); } @@ -85,21 +89,24 @@ void PopLast() switch (type) { - case 0: - AddFirst(mt.Next()); - break; - case 1: - AddLast(mt.Next()); - break; - case 2: - PopFirst(); - break; - case 3: - PopLast(); - break; + case 0: AddFirst(mt.Next()); break; + case 1: AddLast(mt.Next()); break; + case 2: PopFirst(); break; + case 3: PopLast(); break; } + } - deque.Should().Equal(list); + for (int q = 0; q < 10000; q++) + { + var type = mt.Next(6); + if (deque.Count == 0) type %= 2; + switch (type) + { + case 0: AddFirst(mt.Next()); break; + case 1: AddLast(mt.Next()); break; + case 2: case 4: PopFirst(); break; + case 3: case 5: PopLast(); break; + } } } @@ -223,5 +230,66 @@ public void Enumerate() deque.Should().Equal(new[] { 1, 2, 3, 4, 5 }); deque.Reversed().Should().Equal(new[] { 5, 4, 3, 2, 1 }); } + + [Fact] + public void Grow() + { + { + var deque = new Deque { 1, }; + deque.Should().Equal(new[] { 1, }); + deque.Grow(6); + deque.data.Should().HaveCount(8); + deque.Should().Equal(new[] { 1, }); + } + { + var deque = new Deque { 1, 2, 3, }; + deque.Should().Equal(new[] { 1, 2, 3, }); + deque.Grow(6); + deque.data.Should().HaveCount(8); + deque.Should().Equal(new[] { 1, 2, 3, }); + } + { + var deque = new Deque { 1, 2, 3, }; + deque.Should().Equal(new[] { 1, 2, 3, }); + deque.PopFirst(); + deque.Grow(6); + deque.data.Should().HaveCount(8); + deque.Should().Equal(new[] { 2, 3, }); + } + { + var deque = new Deque { 1, 2, 3, }; + deque.Should().Equal(new[] { 1, 2, 3, }); + deque.PopFirst(); + deque.PopFirst(); + deque.AddLast(-1); + deque.AddLast(-2); + deque.PopFirst(); + deque.Grow(6); + deque.data.Should().HaveCount(8); + deque.Should().Equal(new[] { -1, -2, }); + } + { + var deque = new Deque { 1, 2, 3, }; + deque.Should().Equal(new[] { 1, 2, 3, }); + deque.PopFirst(); + deque.PopFirst(); + deque.PopFirst(); + deque.Should().BeEmpty(); + deque.Grow(6); + deque.data.Should().HaveCount(8); + deque.Should().BeEmpty(); + } + { + var deque = new Deque { 1, 2, 3, }; + deque.Should().Equal(new[] { 1, 2, 3, }); + deque.PopLast(); + deque.PopFirst(); + deque.PopFirst(); + deque.Should().BeEmpty(); + deque.Grow(6); + deque.data.Should().HaveCount(8); + deque.Should().BeEmpty(); + } + } } }