Skip to content

Commit e0439ba

Browse files
authored
Merge pull request #4 from Kibnet/feature/intset-range-enumeration
feat: Implement GetElementsInRange for IntSet
2 parents 238d0fd + 98ecc84 commit e0439ba

File tree

6 files changed

+714
-31
lines changed

6 files changed

+714
-31
lines changed

.github/workflows/ci-tests.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name: CI Tests
2+
3+
on:
4+
push:
5+
branches: ['**'] # Test on push to all branches
6+
pull_request:
7+
branches: ['**'] # Test on PR to all branches (can remove type filter or adjust as needed)
8+
9+
jobs:
10+
test: # Renamed job for clarity
11+
12+
runs-on: ubuntu-latest
13+
14+
steps:
15+
- uses: actions/checkout@v4
16+
- name: Setup .NET
17+
uses: actions/setup-dotnet@v4
18+
with:
19+
dotnet-version: 9.0.x
20+
- name: Restore dependencies
21+
run: dotnet restore
22+
- name: Build
23+
run: dotnet build --no-restore
24+
- name: Test
25+
run: dotnet test --no-build --verbosity normal
Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,19 @@
1-
name: NuGet Generation
1+
name: Manual NuGet Publication
22

33
on:
4-
push:
5-
branches: [ master ]
6-
pull_request:
7-
types: [closed]
8-
branches: [ master ]
4+
workflow_dispatch: # Allows manual triggering from the GitHub Actions UI
95

106
jobs:
11-
build:
12-
7+
publish:
138
runs-on: ubuntu-latest
149

1510
steps:
16-
- uses: actions/checkout@v2
17-
- name: Setup .NET Core
18-
uses: actions/setup-dotnet@v1
11+
- uses: actions/checkout@v4
12+
- name: Setup .NET
13+
uses: actions/setup-dotnet@v4
1914
with:
20-
dotnet-version: 3.1.202
21-
- name: Install dependencies
15+
dotnet-version: 9.0.x
16+
- name: Restore dependencies
2217
run: dotnet restore
2318
- name: Pack solution
2419
run: dotnet pack --configuration Release -o out --no-restore

IntSet.sln

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
3-
# Visual Studio Version 16
4-
VisualStudioVersion = 16.0.29613.14
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.14.36109.1 d17.14
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IntSet", "src\IntSet\IntSet.csproj", "{AE63B664-F383-48F8-8EEE-70FCB2169AF7}"
77
EndProject
88
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IntSet.Tests", "src\IntSet.Tests\IntSet.Tests.csproj", "{175DFCC8-9221-4D95-81D3-42C7252227D0}"
99
EndProject
10+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Элементы решения", "Элементы решения", "{754FC069-D67B-A9D7-50A1-8D1CA196D8F1}"
11+
ProjectSection(SolutionItems) = preProject
12+
.github\workflows\ci-tests.yml = .github\workflows\ci-tests.yml
13+
.github\workflows\manual-publish.yml = .github\workflows\manual-publish.yml
14+
EndProjectSection
15+
EndProject
1016
Global
1117
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1218
Debug|Any CPU = Debug|Any CPU

src/IntSet.Tests/IntSetTests.cs

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11

22
using System;
33
using System.Collections;
4+
using System.Collections.Generic;
45
using System.Linq;
56
using Xunit;
67

@@ -450,5 +451,159 @@ public void CopyTo_MatchExactSpace_CopiesAll()
450451
set.CopyTo(dst, 2);
451452
Assert.Equal(new[] { 0, 0, 1, 2, 3 }, dst);
452453
}
454+
455+
#region GetElementsInRange Tests
456+
457+
[Fact]
458+
public void GetElementsInRange_EmptySet()
459+
{
460+
var set = new Kibnet.IntSet();
461+
Assert.Equal(new List<int>(), set.GetElementsInRange(1, 10).ToList());
462+
Assert.Equal(new List<int>(), set.GetElementsInRange(10, 1).ToList());
463+
}
464+
465+
[Fact]
466+
public void GetElementsInRange_PopulatedSet_Ascending()
467+
{
468+
var set = new Kibnet.IntSet(new[] { 1, 5, 10, 15, 20, 25 });
469+
Assert.Equal(new List<int> { 1, 5, 10, 15, 20, 25 }, set.GetElementsInRange(1, 25).ToList());
470+
Assert.Equal(new List<int> { 10, 15, 20 }, set.GetElementsInRange(10, 20).ToList());
471+
Assert.Equal(new List<int> { 1, 5 }, set.GetElementsInRange(0, 5).ToList());
472+
Assert.Equal(new List<int> { 20, 25 }, set.GetElementsInRange(20, 30).ToList());
473+
Assert.Equal(new List<int>(), set.GetElementsInRange(11, 14).ToList());
474+
Assert.Equal(new List<int> { 1, 5, 10, 15, 20, 25 }, set.GetElementsInRange(0, 30).ToList());
475+
Assert.Equal(new List<int> { 5, 10, 15 }, set.GetElementsInRange(5, 15).ToList());
476+
}
477+
478+
[Fact]
479+
public void GetElementsInRange_PopulatedSet_Descending()
480+
{
481+
var set = new Kibnet.IntSet(new[] { 1, 5, 10, 15, 20, 25 });
482+
Assert.Equal(new List<int> { 25, 20, 15, 10, 5, 1 }, set.GetElementsInRange(25, 1).ToList());
483+
Assert.Equal(new List<int> { 20, 15, 10 }, set.GetElementsInRange(20, 10).ToList());
484+
Assert.Equal(new List<int> { 25, 20 }, set.GetElementsInRange(30, 20).ToList());
485+
Assert.Equal(new List<int> { 5, 1 }, set.GetElementsInRange(5, 0).ToList());
486+
Assert.Equal(new List<int>(), set.GetElementsInRange(14, 11).ToList());
487+
Assert.Equal(new List<int> { 25, 20, 15, 10, 5, 1 }, set.GetElementsInRange(30, 0).ToList());
488+
Assert.Equal(new List<int> { 15, 10, 5 }, set.GetElementsInRange(15, 5).ToList());
489+
}
490+
491+
[Fact]
492+
public void GetElementsInRange_PopulatedSet_SingleElementRange()
493+
{
494+
var set = new Kibnet.IntSet(new[] { 1, 5, 10, 15, 20 });
495+
Assert.Equal(new List<int> { 10 }, set.GetElementsInRange(10, 10).ToList());
496+
Assert.Equal(new List<int>(), set.GetElementsInRange(11, 11).ToList());
497+
Assert.Equal(new List<int> { 1 }, set.GetElementsInRange(1, 1).ToList());
498+
Assert.Equal(new List<int> { 20 }, set.GetElementsInRange(20, 20).ToList());
499+
}
500+
501+
[Fact]
502+
public void GetElementsInRange_ComplexScenarios_Gaps()
503+
{
504+
var set = new Kibnet.IntSet(new[] { 1, 2, 3, 10, 11, 12, 20, 21, 22 });
505+
Assert.Equal(new List<int> { 3, 10, 11 }, set.GetElementsInRange(3, 11).ToList());
506+
Assert.Equal(new List<int> { 11, 10, 3 }, set.GetElementsInRange(11, 3).ToList());
507+
Assert.Equal(new List<int> { 3, 10 }, set.GetElementsInRange(3, 10).ToList()); // Test case name was 'Asc across gap'
508+
Assert.Equal(new List<int> { 10, 3 }, set.GetElementsInRange(10, 3).ToList()); // Test case name was 'Desc across gap'
509+
}
510+
511+
[Fact]
512+
public void GetElementsInRange_ComplexScenarios_NegativeNumbers()
513+
{
514+
var negSet = new Kibnet.IntSet(new[] { -10, -5, 0, 5, 10 });
515+
Assert.Equal(new List<int> { -5, 0, 5 }, negSet.GetElementsInRange(-5, 5).ToList());
516+
Assert.Equal(new List<int> { 5, 0, -5 }, negSet.GetElementsInRange(5, -5).ToList());
517+
Assert.Equal(new List<int> { -10, -5 }, negSet.GetElementsInRange(-15, -5).ToList());
518+
Assert.Equal(new List<int> { -5, -10 }, negSet.GetElementsInRange(-5, -15).ToList());
519+
Assert.Equal(new List<int> { -10 }, negSet.GetElementsInRange(-10, -10).ToList());
520+
Assert.Equal(new List<int> { 0 }, negSet.GetElementsInRange(0, 0).ToList());
521+
Assert.Equal(new List<int> { 10 }, negSet.GetElementsInRange(10, 10).ToList());
522+
}
523+
524+
[Fact]
525+
public void GetElementsInRange_FullRange_MinMaxInt()
526+
{
527+
var set = new Kibnet.IntSet(new[] { int.MinValue, 0, int.MaxValue });
528+
Assert.Equal(new List<int> { int.MinValue, 0, int.MaxValue }, set.GetElementsInRange(int.MinValue, int.MaxValue).ToList());
529+
Assert.Equal(new List<int> { int.MaxValue, 0, int.MinValue }, set.GetElementsInRange(int.MaxValue, int.MinValue).ToList());
530+
}
531+
532+
[Fact]
533+
public void GetElementsInRange_LargeNumbers()
534+
{
535+
var set = new Kibnet.IntSet(new[] { 1000000, 2000000, 3000000, int.MaxValue - 5, int.MaxValue });
536+
Assert.Equal(new List<int> { 1000000, 2000000 }, set.GetElementsInRange(1000000, 2500000).ToList());
537+
Assert.Equal(new List<int> { 2000000, 1000000 }, set.GetElementsInRange(2500000, 1000000).ToList());
538+
Assert.Equal(new List<int> { int.MaxValue - 5, int.MaxValue }, set.GetElementsInRange(int.MaxValue - 10, int.MaxValue).ToList());
539+
Assert.Equal(new List<int> { int.MaxValue, int.MaxValue -5 }, set.GetElementsInRange(int.MaxValue, int.MaxValue - 10).ToList());
540+
}
541+
542+
[Fact]
543+
public void GetElementsInRange_RangeOutsidePopulatedData()
544+
{
545+
var set = new Kibnet.IntSet(new[] { 10, 20, 30 });
546+
Assert.Equal(new List<int>(), set.GetElementsInRange(1, 5).ToList()); // Range before
547+
Assert.Equal(new List<int>(), set.GetElementsInRange(5, 1).ToList());
548+
Assert.Equal(new List<int>(), set.GetElementsInRange(35, 40).ToList()); // Range after
549+
Assert.Equal(new List<int>(), set.GetElementsInRange(40, 35).ToList());
550+
Assert.Equal(new List<int>(), set.GetElementsInRange(22, 28).ToList()); // Range between elements
551+
Assert.Equal(new List<int>(), set.GetElementsInRange(28, 22).ToList());
552+
}
553+
554+
[Fact]
555+
public void GetElementsInRange_SingleElementInSet_MatchingRange()
556+
{
557+
var set = new Kibnet.IntSet(new[] { 42 });
558+
Assert.Equal(new List<int> { 42 }, set.GetElementsInRange(42, 42).ToList());
559+
Assert.Equal(new List<int> { 42 }, set.GetElementsInRange(40, 45).ToList());
560+
Assert.Equal(new List<int> { 42 }, set.GetElementsInRange(45, 40).ToList());
561+
}
562+
563+
[Fact]
564+
public void GetElementsInRange_SingleElementInSet_NonMatchingRange()
565+
{
566+
var set = new Kibnet.IntSet(new[] { 42 });
567+
Assert.Equal(new List<int>(), set.GetElementsInRange(10, 20).ToList());
568+
Assert.Equal(new List<int>(), set.GetElementsInRange(50, 60).ToList());
569+
}
570+
571+
// Test with a set that forces traversal through multiple levels of cards
572+
[Fact]
573+
public void GetElementsInRange_DeepTraversal()
574+
{
575+
// These numbers are chosen to likely span different i0, i1, i2, i3 blocks
576+
var data = new List<int> { 0, 1, 2,
577+
(1 << 8) + 5, (1 << 8) + 10, // Different i3
578+
(1 << 14) + 7, (1 << 14) + 15, // Different i2
579+
(1 << 20) + 3, (1 << 20) + 9, // Different i1
580+
(1 << 26) + 100, (1 << 26) + 200, // Different i0
581+
int.MaxValue - 10, int.MaxValue -1, int.MaxValue};
582+
var set = new Kibnet.IntSet(data);
583+
584+
var expectedAsc = data.Where(x => x >= ((1 << 8) + 5) && x <= ((1 << 26) + 100)).OrderBy(x => x).ToList();
585+
Assert.Equal(expectedAsc, set.GetElementsInRange((1 << 8) + 5, (1 << 26) + 100).ToList());
586+
587+
var expectedDesc = data.Where(x => x >= ((1 << 14) + 7) && x <= ((1 << 20) + 9)).OrderByDescending(x => x).ToList();
588+
Assert.Equal(expectedDesc, set.GetElementsInRange((1 << 20) + 9, (1 << 14) + 7).ToList());
589+
590+
// Range including int.MaxValue
591+
Assert.Equal(new List<int> { (1 << 26) + 200, int.MaxValue - 10, int.MaxValue -1, int.MaxValue }, set.GetElementsInRange((1 << 26) + 150, int.MaxValue).ToList());
592+
Assert.Equal(new List<int> { int.MaxValue, int.MaxValue -1, int.MaxValue - 10, (1 << 26) + 200 }, set.GetElementsInRange(int.MaxValue, (1 << 26) + 150).ToList());
593+
594+
// Range including int.MinValue (if 0 is considered MinValue for positive range)
595+
// If IntSet can store negative numbers, this test would be more relevant with actual int.MinValue
596+
var dataWithNeg = new List<int>(data);
597+
dataWithNeg.Add(int.MinValue);
598+
dataWithNeg.Add(int.MinValue+1);
599+
var setWithNeg = new Kibnet.IntSet(dataWithNeg);
600+
dataWithNeg.Sort(); // For expected list
601+
602+
Assert.Equal(dataWithNeg.Where(x => x >= int.MinValue && x <= 2).ToList(), setWithNeg.GetElementsInRange(int.MinValue, 2).ToList());
603+
Assert.Equal(dataWithNeg.Where(x => x >= int.MinValue && x <= 2).OrderByDescending(x=>x).ToList(), setWithNeg.GetElementsInRange(2, int.MinValue).ToList());
604+
605+
}
606+
607+
#endregion
453608
}
454609
}

src/IntSet.Tests/StrykerTests.cs

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,87 @@ public void SetEquals_DifferentOrderButSameElements_ReturnsTrue()
195195
Assert.True(a.SetEquals(b));
196196
}
197197

198+
[Fact]
199+
public void CheckUniqueAndUnfoundElements_EmptySet_ReturnsCorrectCounts()
200+
{
201+
var set = new IntSet();
202+
var result = set.IsSubsetOf(new[] { 1, 2 });
203+
Assert.Equal(true, result);
204+
}
205+
206+
[Fact]
207+
public void CheckUniqueAndUnfoundElements_MixedElements_ReturnsCorrectCounts()
208+
{
209+
var set = new IntSet(new[] { 1, 2, 3 });
210+
var result = set.IsSubsetOf(new[] { 2, 3, 4, 5 });
211+
Assert.Equal(false, result);
212+
}
213+
214+
[Fact]
215+
public void ContainsAllElements_EmptyEnumerable_ReturnsTrue()
216+
{
217+
var set = new IntSet(new[] { 1, 2, 3 });
218+
Assert.True(set.IsSupersetOf(Enumerable.Empty<int>()));
219+
}
220+
221+
[Fact]
222+
public void ContainsAllElements_MixedElements_ReturnsFalse()
223+
{
224+
var set = new IntSet(new[] { 1, 2, 3 });
225+
Assert.False(set.IsSupersetOf(new[] { 2, 3, 4 }));
226+
}
227+
228+
[Fact]
229+
public void IntersectWithIntSet_EmptySet_DoesNotChange()
230+
{
231+
var set = new IntSet(new[] { 1, 2, 3 });
232+
var other = new IntSet();
233+
set.IntersectWith(other);
234+
Assert.Empty(set);
235+
}
236+
237+
[Fact]
238+
public void IntersectWithIntSet_PartialIntersection_RetainsCommonElements()
239+
{
240+
var set = new IntSet(new[] { 1, 2, 3 });
241+
var other = new IntSet(new[] { 2, 3, 4 });
242+
set.IntersectWith(other);
243+
Assert.Equal(new[] { 2, 3 }, set.OrderBy(x => x));
244+
}
245+
246+
[Fact]
247+
public void IntersectWithEnumerable_EmptyEnumerable_DoesNotChange()
248+
{
249+
var set = new IntSet(new[] { 1, 2, 3 });
250+
set.IntersectWith(Enumerable.Empty<int>());
251+
Assert.Equal(Enumerable.Empty<int>(), set.OrderBy(x => x));
252+
}
253+
254+
[Fact]
255+
public void IntersectWithEnumerable_PartialIntersection_RetainsCommonElements()
256+
{
257+
var set = new IntSet(new[] { 1, 2, 3 });
258+
var other = new[] { 2, 3, 4 };
259+
set.IntersectWith(other);
260+
Assert.Equal(new[] { 2, 3 }, set.OrderBy(x => x));
261+
}
262+
263+
[Fact]
264+
public void Constructor_FullSet_CreatesFullSet()
265+
{
266+
var set = new IntSet(false, true);
267+
Assert.Equal(uint.MaxValue, set.LongCount);
268+
Assert.True(set.root.Full);
269+
Assert.Null(set.root.Cards);
270+
}
271+
272+
[Fact]
273+
public void Constructor_EmptyCollection_DoesNotThrow()
274+
{
275+
var set = new IntSet(new int[0]);
276+
Assert.Empty(set);
277+
}
278+
198279
[Fact]
199280
public void SetEquals_DifferentCounts_ReturnsFalse()
200281
{
@@ -328,6 +409,58 @@ public void CheckFull_NonFull_DoesNotClearBytes()
328409
Assert.NotNull(set.root.Cards[0].Cards[0].Cards[0].Cards[0].Bytes);
329410
}
330411

412+
[Fact]
413+
public void Remove_CascadeRemoval_RemovesAllLevels()
414+
{
415+
var set = new IntSet();
416+
set.Add(1000000); // This will create a deep card structure
417+
Assert.True(set.Contains(1000000));
418+
Assert.True(set.Remove(1000000));
419+
Assert.False(set.Contains(1000000));
420+
Assert.Equal(0, set.Count);
421+
}
422+
423+
[Fact]
424+
public void LargeSetOperations_ReturnsCorrectCounts()
425+
{
426+
var set = new IntSet();
427+
for (int i = 0; i < 1000; i++)
428+
{
429+
set.Add(i * 2); // Add even numbers
430+
}
431+
432+
var otherSet = new IntSet(Enumerable.Range(0, 1000));
433+
Assert.Equal(1000, set.Count); // 1000 even numbers
434+
Assert.Equal(1000, otherSet.Count); // 1000 numbers
435+
436+
set.IntersectWith(otherSet);
437+
Assert.Equal(500, set.Count); // 500 even numbers
438+
}
439+
440+
[Fact]
441+
public void BoundaryValuesOperations_WorkCorrectly()
442+
{
443+
var set = new IntSet();
444+
445+
// Test with max int value
446+
set.Add(int.MaxValue);
447+
Assert.True(set.Contains(int.MaxValue));
448+
Assert.True(set.Remove(int.MaxValue));
449+
Assert.False(set.Contains(int.MaxValue));
450+
451+
// Test with min int value
452+
set.Add(int.MinValue);
453+
Assert.True(set.Contains(int.MinValue));
454+
Assert.True(set.Remove(int.MinValue));
455+
Assert.False(set.Contains(int.MinValue));
456+
457+
// Test with positive boundary
458+
set.Add(16383); // Last index in first card
459+
Assert.True(set.Contains(16383));
460+
Assert.True(set.Remove(16383));
461+
Assert.False(set.Contains(16383));
462+
}
463+
331464
[Fact]
332465
public void UnionWith_AddsAllElements()
333466
{

0 commit comments

Comments
 (0)