Skip to content

Commit e352e5c

Browse files
committed
Moves code to file
1 parent 2cbe030 commit e352e5c

File tree

4 files changed

+334
-329
lines changed

4 files changed

+334
-329
lines changed
Lines changed: 334 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,334 @@
1+
namespace FrameworkTests.Utils
2+
{
3+
public class Run
4+
{
5+
public Run(int start, int length)
6+
{
7+
Start = start;
8+
Length = length;
9+
}
10+
11+
public int Start { get; }
12+
public int Length { get; private set; }
13+
14+
public bool Includes(int index)
15+
{
16+
return index >= Start && index < (Start + Length);
17+
}
18+
19+
public RunUpdate ExpandToInclude(int index)
20+
{
21+
if (Includes(index)) throw new Exception("Run already includes this index. Run: {ToString()} index: {index}");
22+
if (index == (Start + Length))
23+
{
24+
Length++;
25+
return new RunUpdate();
26+
}
27+
if (index == (Start - 1))
28+
{
29+
return new RunUpdate(
30+
newRuns: [new Run(Start - 1, Length + 1)],
31+
removeRuns: [this]
32+
);
33+
}
34+
throw new Exception($"Run cannot expand to include index. Run: {ToString()} index: {index}");
35+
}
36+
37+
public RunUpdate Unset(int index)
38+
{
39+
if (!Includes(index))
40+
{
41+
return new RunUpdate();
42+
}
43+
44+
if (index == Start)
45+
{
46+
// First index: Replace self with new run at next index, unless empty.
47+
if (Length == 1)
48+
{
49+
return new RunUpdate(
50+
newRuns: Array.Empty<Run>(),
51+
removeRuns: [this]
52+
);
53+
}
54+
return new RunUpdate(
55+
newRuns: [new Run(Start + 1, Length - 1)],
56+
removeRuns: [this]
57+
);
58+
}
59+
60+
if (index == (Start + Length - 1))
61+
{
62+
// Last index: Become one smaller.
63+
Length--;
64+
return new RunUpdate();
65+
}
66+
67+
// Split:
68+
var newRunLength = (Start + Length - 1) - index;
69+
Length = index - Start;
70+
return new RunUpdate(
71+
newRuns: [new Run(index + 1, newRunLength)],
72+
removeRuns: Array.Empty<Run>()
73+
);
74+
}
75+
76+
public void Iterate(Action<int> action)
77+
{
78+
for (var i = 0; i < Length; i++)
79+
{
80+
action(Start + i);
81+
}
82+
}
83+
84+
public override string ToString()
85+
{
86+
return $"[{Start},{Length}]";
87+
}
88+
89+
public override bool Equals(object? obj)
90+
{
91+
return obj is Run run &&
92+
Start == run.Start &&
93+
Length == run.Length;
94+
}
95+
96+
public override int GetHashCode()
97+
{
98+
return HashCode.Combine(Start, Length);
99+
}
100+
101+
public static bool operator ==(Run? obj1, Run? obj2)
102+
{
103+
if (ReferenceEquals(obj1, obj2)) return true;
104+
if (ReferenceEquals(obj1, null)) return false;
105+
if (ReferenceEquals(obj2, null)) return false;
106+
return obj1.Equals(obj2);
107+
}
108+
public static bool operator !=(Run? obj1, Run? obj2) => !(obj1 == obj2);
109+
}
110+
111+
public class RunUpdate
112+
{
113+
public RunUpdate()
114+
: this(Array.Empty<Run>(), Array.Empty<Run>())
115+
{
116+
}
117+
118+
public RunUpdate(Run[] newRuns, Run[] removeRuns)
119+
{
120+
NewRuns = newRuns;
121+
RemoveRuns = removeRuns;
122+
}
123+
124+
public Run[] NewRuns { get; }
125+
public Run[] RemoveRuns { get; }
126+
}
127+
128+
public partial class IndexSet
129+
{
130+
private readonly SortedList<int, Run> runs = new SortedList<int, Run>();
131+
132+
public IndexSet()
133+
{
134+
}
135+
136+
public IndexSet(int[] indices)
137+
{
138+
foreach (var i in indices) Set(i);
139+
}
140+
141+
public static IndexSet FromRunLengthEncoded(int[] rle)
142+
{
143+
var set = new IndexSet();
144+
for (var i = 0; i < rle.Length; i += 2)
145+
{
146+
var start = rle[i];
147+
var length = rle[i + 1];
148+
set.runs.Add(start, new Run(start, length));
149+
}
150+
151+
return set;
152+
}
153+
154+
public bool IsSet(int index)
155+
{
156+
if (runs.ContainsKey(index)) return true;
157+
158+
var run = GetRunAt(index);
159+
if (run == null) return false;
160+
return true;
161+
}
162+
163+
public void Set(int index)
164+
{
165+
if (IsSet(index)) return;
166+
167+
var runBefore = GetRunAt(index - 1);
168+
var runAfter = GetRunExact(index + 1);
169+
170+
if (runBefore == null)
171+
{
172+
if (runAfter == null)
173+
{
174+
CreateNewRun(index);
175+
}
176+
else
177+
{
178+
HandleUpdate(runAfter.ExpandToInclude(index));
179+
}
180+
}
181+
else
182+
{
183+
if (runAfter == null)
184+
{
185+
HandleUpdate(runBefore.ExpandToInclude(index));
186+
}
187+
else
188+
{
189+
// new index will connect runBefore with runAfter. We merge!
190+
HandleUpdate(new RunUpdate(
191+
newRuns: [new Run(runBefore.Start, runBefore.Length + 1 + runAfter.Length)],
192+
removeRuns: [runBefore, runAfter]
193+
));
194+
}
195+
}
196+
}
197+
198+
public void Unset(int index)
199+
{
200+
if (runs.ContainsKey(index))
201+
{
202+
HandleUpdate(runs[index].Unset(index));
203+
}
204+
else
205+
{
206+
var run = GetRunAt(index);
207+
if (run == null) return;
208+
HandleUpdate(run.Unset(index));
209+
}
210+
}
211+
212+
public void Iterate(Action<int> onIndex)
213+
{
214+
foreach (var run in runs.Values)
215+
{
216+
run.Iterate(onIndex);
217+
}
218+
}
219+
220+
public int[] RunLengthEncoded()
221+
{
222+
return Encode().ToArray();
223+
}
224+
225+
public override string ToString()
226+
{
227+
return string.Join("&", runs.Select(r => r.ToString()).ToArray());
228+
}
229+
230+
private IEnumerable<int> Encode()
231+
{
232+
foreach (var pair in runs)
233+
{
234+
yield return pair.Value.Start;
235+
yield return pair.Value.Length;
236+
}
237+
}
238+
239+
private Run? GetRunAt(int index)
240+
{
241+
foreach (var run in runs.Values)
242+
{
243+
if (run.Includes(index)) return run;
244+
}
245+
return null;
246+
}
247+
248+
private Run? GetRunExact(int index)
249+
{
250+
if (runs.ContainsKey(index)) return runs[index];
251+
return null;
252+
}
253+
254+
private void HandleUpdate(RunUpdate runUpdate)
255+
{
256+
foreach (var removeRun in runUpdate.RemoveRuns) runs.Remove(removeRun.Start);
257+
foreach (var newRun in runUpdate.NewRuns) runs.Add(newRun.Start, newRun);
258+
}
259+
260+
private void CreateNewRun(int index)
261+
{
262+
if (runs.ContainsKey(index + 1))
263+
{
264+
var length = runs[index + 1].Length + 1;
265+
runs.Add(index, new Run(index, length));
266+
runs.Remove(index + 1);
267+
}
268+
else
269+
{
270+
runs.Add(index, new Run(index, 1));
271+
}
272+
}
273+
}
274+
275+
public partial class IndexSet
276+
{
277+
public IndexSet Overlap(IndexSet other)
278+
{
279+
var result = new IndexSet();
280+
Iterate(i =>
281+
{
282+
if (other.IsSet(i)) result.Set(i);
283+
});
284+
return result;
285+
}
286+
287+
public IndexSet Merge(IndexSet other)
288+
{
289+
var result = new IndexSet();
290+
Iterate(result.Set);
291+
other.Iterate(result.Set);
292+
return result;
293+
}
294+
295+
public IndexSet Without(IndexSet other)
296+
{
297+
var result = new IndexSet();
298+
Iterate(i =>
299+
{
300+
if (!other.IsSet(i)) result.Set(i);
301+
});
302+
return result;
303+
}
304+
305+
public override bool Equals(object? obj)
306+
{
307+
if (obj is IndexSet set)
308+
{
309+
if (set.runs.Count != runs.Count) return false;
310+
foreach (var pair in runs)
311+
{
312+
if (!set.runs.ContainsKey(pair.Key)) return false;
313+
if (set.runs[pair.Key] != pair.Value) return false;
314+
}
315+
return true;
316+
}
317+
return false;
318+
}
319+
320+
public override int GetHashCode()
321+
{
322+
return HashCode.Combine(runs);
323+
}
324+
325+
public static bool operator ==(IndexSet? obj1, IndexSet? obj2)
326+
{
327+
if (ReferenceEquals(obj1, obj2)) return true;
328+
if (ReferenceEquals(obj1, null)) return false;
329+
if (ReferenceEquals(obj2, null)) return false;
330+
return obj1.Equals(obj2);
331+
}
332+
public static bool operator !=(IndexSet? obj1, IndexSet? obj2) => !(obj1 == obj2);
333+
}
334+
}

Tests/FrameworkTests/Utils/RunLengthEncodingLogicalTests.cs

Lines changed: 0 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -91,64 +91,4 @@ public void Without()
9191
Assert.That(set, Is.EqualTo(expectedSet));
9292
}
9393
}
94-
95-
public partial class IndexSet
96-
{
97-
public IndexSet Overlap(IndexSet other)
98-
{
99-
var result = new IndexSet();
100-
Iterate(i =>
101-
{
102-
if (other.IsSet(i)) result.Set(i);
103-
});
104-
return result;
105-
}
106-
107-
public IndexSet Merge(IndexSet other)
108-
{
109-
var result = new IndexSet();
110-
Iterate(result.Set);
111-
other.Iterate(result.Set);
112-
return result;
113-
}
114-
115-
public IndexSet Without(IndexSet other)
116-
{
117-
var result = new IndexSet();
118-
Iterate(i =>
119-
{
120-
if (!other.IsSet(i)) result.Set(i);
121-
});
122-
return result;
123-
}
124-
125-
public override bool Equals(object? obj)
126-
{
127-
if (obj is IndexSet set)
128-
{
129-
if (set.runs.Count != runs.Count) return false;
130-
foreach (var pair in runs)
131-
{
132-
if (!set.runs.ContainsKey(pair.Key)) return false;
133-
if (set.runs[pair.Key] != pair.Value) return false;
134-
}
135-
return true;
136-
}
137-
return false;
138-
}
139-
140-
public override int GetHashCode()
141-
{
142-
return HashCode.Combine(runs);
143-
}
144-
145-
public static bool operator ==(IndexSet? obj1, IndexSet? obj2)
146-
{
147-
if (ReferenceEquals(obj1, obj2)) return true;
148-
if (ReferenceEquals(obj1, null)) return false;
149-
if (ReferenceEquals(obj2, null)) return false;
150-
return obj1.Equals(obj2);
151-
}
152-
public static bool operator !=(IndexSet? obj1, IndexSet? obj2) => !(obj1 == obj2);
153-
}
15494
}

0 commit comments

Comments
 (0)