Skip to content

Commit 017cee4

Browse files
committed
Merge branch 'index-encoding'
2 parents e4f7249 + 8fbef6f commit 017cee4

File tree

2 files changed

+513
-0
lines changed

2 files changed

+513
-0
lines changed
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
using NUnit.Framework;
2+
using NUnit.Framework.Interfaces;
3+
using static FrameworkTests.Utils.RunLengthEncodingTests;
4+
5+
namespace FrameworkTests.Utils
6+
{
7+
[TestFixture]
8+
public class RunLengthEncodingRunTests
9+
{
10+
[Test]
11+
[Combinatorial]
12+
public void RunIncludes(
13+
[Values(0, 1, 2, 3)] int start,
14+
[Values(1, 2, 3, 4)] int length)
15+
{
16+
var run = new Run(start, length);
17+
18+
var shouldInclude = Enumerable.Range(start, length).ToArray();
19+
var shouldExclude = new int[]
20+
{
21+
shouldInclude.Min() - 1,
22+
shouldInclude.Max() + 1
23+
};
24+
25+
foreach (var incl in shouldInclude)
26+
{
27+
Assert.That(run.Includes(incl));
28+
}
29+
foreach (var excl in shouldExclude)
30+
{
31+
Assert.That(!run.Includes(excl));
32+
}
33+
}
34+
35+
[Test]
36+
public void RunExpandToInclude()
37+
{
38+
var run = new Run(2, 3);
39+
Assert.That(run.Includes(2));
40+
Assert.That(run.Includes(4));
41+
Assert.That(!run.Includes(5));
42+
43+
Assert.That(run.ExpandToInclude(1), Is.False);
44+
Assert.That(run.ExpandToInclude(2), Is.False);
45+
Assert.That(run.ExpandToInclude(4), Is.False);
46+
Assert.That(run.ExpandToInclude(6), Is.False);
47+
48+
Assert.That(run.ExpandToInclude(5), Is.True);
49+
Assert.That(run.Includes(5));
50+
Assert.That(!run.Includes(6));
51+
}
52+
53+
[Test]
54+
public void RunCanUnsetLastIndex()
55+
{
56+
var run = new Run(0, 3);
57+
Assert.That(run.Includes(2));
58+
var update = run.Unset(2);
59+
Assert.That(!run.Includes(2));
60+
61+
Assert.That(update.NewRuns.Length, Is.EqualTo(0));
62+
Assert.That(update.RemoveRuns.Length, Is.EqualTo(0));
63+
}
64+
65+
[Test]
66+
public void RunCanSplit()
67+
{
68+
var run = new Run(0, 6); // 0, 1, 2, 3, 4, 5
69+
var update = run.Unset(2);
70+
71+
Assert.That(run.Start, Is.EqualTo(0));
72+
Assert.That(run.Length, Is.EqualTo(2)); // 0, 1
73+
Assert.That(!run.Includes(2));
74+
75+
Assert.That(update.NewRuns.Length, Is.EqualTo(1));
76+
Assert.That(update.RemoveRuns.Length, Is.EqualTo(0));
77+
78+
Assert.That(!update.NewRuns[0].Includes(2));
79+
Assert.That(update.NewRuns[0].Start, Is.EqualTo(3));
80+
Assert.That(update.NewRuns[0].Length, Is.EqualTo(3)); // 3, 4, 5
81+
Assert.That(!update.NewRuns[0].Includes(6));
82+
}
83+
84+
[Test]
85+
public void RunReplacesSelfWhenUnsetFirstIndex()
86+
{
87+
var run = new Run(0, 5);
88+
var update = run.Unset(0);
89+
90+
Assert.That(update.NewRuns.Length, Is.EqualTo(1));
91+
Assert.That(update.RemoveRuns.Length, Is.EqualTo(1));
92+
93+
Assert.That(update.RemoveRuns[0], Is.SameAs(run));
94+
Assert.That(update.NewRuns[0].Start, Is.EqualTo(1));
95+
Assert.That(update.NewRuns[0].Length, Is.EqualTo(4));
96+
}
97+
98+
[Test]
99+
public void CanIterateIndices()
100+
{
101+
var run = new Run(2, 4);
102+
var seen = new List<int>();
103+
run.Iterate(i => seen.Add(i));
104+
105+
CollectionAssert.AreEqual(new[] { 2, 3, 4, 5 }, seen);
106+
}
107+
}
108+
109+
public class Run
110+
{
111+
public Run(int start, int length)
112+
{
113+
Start = start;
114+
Length = length;
115+
}
116+
117+
public int Start { get; }
118+
public int Length { get; private set; }
119+
120+
public bool Includes(int index)
121+
{
122+
return index >= Start && index < (Start + Length);
123+
}
124+
125+
public bool ExpandToInclude(int index)
126+
{
127+
if (index == (Start + Length))
128+
{
129+
Length++;
130+
return true;
131+
}
132+
return false;
133+
}
134+
135+
public RunUpdate Unset(int index)
136+
{
137+
if (!Includes(index))
138+
{
139+
return new RunUpdate();
140+
}
141+
142+
if (index == Start)
143+
{
144+
// First index: Replace self with new run at next index, unless empty.
145+
if (Length == 1)
146+
{
147+
return new RunUpdate(Array.Empty<Run>(), new[] { this });
148+
}
149+
return new RunUpdate(
150+
newRuns: new[] { new Run(Start + 1, Length - 1) },
151+
removeRuns: new[] { this }
152+
);
153+
}
154+
155+
if (index == (Start + Length - 1))
156+
{
157+
// Last index: Become one smaller.
158+
Length--;
159+
return new RunUpdate();
160+
}
161+
162+
// Split:
163+
var newRunLength = (Start + Length - 1) - index;
164+
Length = index - Start;
165+
return new RunUpdate(new[] { new Run(index + 1, newRunLength) }, Array.Empty<Run>());
166+
}
167+
168+
public void Iterate(Action<int> action)
169+
{
170+
for (var i = 0; i < Length; i++)
171+
{
172+
action(Start + i);
173+
}
174+
}
175+
}
176+
177+
public class RunUpdate
178+
{
179+
public RunUpdate()
180+
: this(Array.Empty<Run>(), Array.Empty<Run>())
181+
{
182+
}
183+
184+
public RunUpdate(Run[] newRuns, Run[] removeRuns)
185+
{
186+
NewRuns = newRuns;
187+
RemoveRuns = removeRuns;
188+
}
189+
190+
public Run[] NewRuns { get; }
191+
public Run[] RemoveRuns { get; }
192+
}
193+
}

0 commit comments

Comments
 (0)