Skip to content

Commit de9eac2

Browse files
google-labs-jules[bot]Kibnet
authored andcommitted
feat: Implement GetElementsInRange for IntSet
Adds a new public method `GetElementsInRange(int firstElement, int lastElement)` to the `IntSet` class. This method allows you to enumerate integers from the set that fall within the specified range `[min(first, last), max(first, last)]`. The enumeration direction (ascending or descending) is determined by the order of `firstElement` and `lastElement`. Key changes include: - New private helper methods for bidirectional traversal of the internal Card/byte structure (`TraverseRootCardsDirectional`, `TraverseCardLevelDirectional`, `TraverseByteLevelDirectional`, `ProcessBytesDirectional`). - The existing `GetEnumerator()` was updated to use the new ascending directional traversal logic, ensuring consistency. - Efficient pruning logic is implemented at each level of traversal within `GetElementsInRange` to skip blocks of numbers that are outside the target range, using a new `GetBlockRange` helper. - Extensive unit tests have been added to `IntSetTests.cs` to cover various scenarios, including empty sets, ascending/descending ranges, edge cases, gaps in data, negative numbers, and int.MinValue/MaxValue.
1 parent 26e803c commit de9eac2

File tree

2 files changed

+528
-16
lines changed

2 files changed

+528
-16
lines changed

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
}

0 commit comments

Comments
 (0)