diff --git a/Foundation/CPIndexSet.j b/Foundation/CPIndexSet.j index c0a13c3d4..c7210c8b7 100644 --- a/Foundation/CPIndexSet.j +++ b/Foundation/CPIndexSet.j @@ -549,6 +549,150 @@ } } +- (unsigned)indexPassingTest:(Function /*(int anIndex)*/)aPredicate +{ + return [self indexWithOptions:CPEnumerationNormal passingTest:aPredicate]; +} + +- (CPIndexSet)indexesPassingTest:(Function /*(int anIndex)*/)aPredicate +{ + return [self indexesWithOptions:CPEnumerationNormal passingTest:aPredicate]; +} + +- (unsigned)indexWithOptions:(CPEnumerationOptions)anOptions passingTest:(Function /*(int anIndex)*/)aPredicate +{ + if (!_count) + return CPNotFound; + + return [self indexInRange:CPMakeRange(0, _CPMaxRange(_ranges[_ranges.length - 1])) options:anOptions passingTest:aPredicate]; +} + +- (CPIndexSet)indexesWithOptions:(CPEnumerationOptions)anOptions passingTest:(Function /*(int anIndex)*/)aPredicate +{ + if (!_count) + return [CPIndexSet indexSet]; + + return [self indexesInRange:CPMakeRange(0, _CPMaxRange(_ranges[_ranges.length - 1])) options:anOptions passingTest:aPredicate]; +} + +- (unsigned)indexInRange:(CPRange)aRange options:(CPEnumerationOptions)anOptions passingTest:(Function /*(int anIndex)*/)aPredicate +{ + if (!_count || CPEmptyRange(aRange)) + return CPNotFound; + + var shouldStop = NO, + index, + stop, + increment; + + if (anOptions & CPEnumerationReverse) + { + index = _ranges.length - 1, + stop = -1, + increment = -1; + } + else + { + index = 0; + stop = _ranges.length; + increment = 1; + } + + for (; index !== stop; index += increment) + { + var range = _ranges[index]; + + var rangeIndex, + rangeStop, + rangeIncrement; + + if (anOptions & CPEnumerationReverse) + { + rangeIndex = _CPMaxRange(range) - 1; + rangeStop = range.location - 1; + rangeIncrement = -1; + } + else + { + rangeIndex = range.location; + rangeStop = _CPMaxRange(range); + rangeIncrement = 1; + } + + for (; rangeIndex !== rangeStop; rangeIndex += rangeIncrement) + { + if (CPLocationInRange(rangeIndex, aRange)) + { + if (aPredicate(rangeIndex, AT_REF(shouldStop))) + return rangeIndex; + + if (shouldStop) + return CPNotFound; + } + } + } + return CPNotFound; +} + +- (CPIndexSet)indexesInRange:(CPRange)aRange options:(CPEnumerationOptions)anOptions passingTest:(Function /*(int anIndex)*/)aPredicate +{ + if (!_count || CPEmptyRange(aRange)) + return [CPIndexSet indexSet]; + + var shouldStop = NO, + index, + stop, + increment; + + if (anOptions & CPEnumerationReverse) + { + index = _ranges.length - 1, + stop = -1, + increment = -1; + } + else + { + index = 0; + stop = _ranges.length; + increment = 1; + } + + var indexesPassingTest = [CPMutableIndexSet indexSet]; + for (; index !== stop; index += increment) + { + var range = _ranges[index]; + + var rangeIndex, + rangeStop, + rangeIncrement; + + if (anOptions & CPEnumerationReverse) + { + rangeIndex = _CPMaxRange(range) - 1; + rangeStop = range.location - 1; + rangeIncrement = -1; + } + else + { + rangeIndex = range.location; + rangeStop = _CPMaxRange(range); + rangeIncrement = 1; + } + + for (; rangeIndex !== rangeStop; rangeIndex += rangeIncrement) + { + if (CPLocationInRange(rangeIndex, aRange)) + { + if (aPredicate(rangeIndex, AT_REF(shouldStop))) + [indexesPassingTest addIndex:rangeIndex]; + + if (shouldStop) + return indexesPassingTest; + } + } + } + return indexesPassingTest; +} @end diff --git a/Tests/Foundation/CPIndexSetTest.j b/Tests/Foundation/CPIndexSetTest.j index 654116773..fe1831a3d 100644 --- a/Tests/Foundation/CPIndexSetTest.j +++ b/Tests/Foundation/CPIndexSetTest.j @@ -543,6 +543,112 @@ function descriptionWithoutEntity(aString) [self assert:[0, 3] equals:visitedIndexes message:"enumeration should stop after 2 results"]; } +- (void)testIndexPassingTest +{ + var set0 = [CPMutableIndexSet indexSet], + index = [set0 indexPassingTest:function(anIndex) + { + return anIndex % 2 === 0; + }]; + [self assertTrue:index === CPNotFound message:"must be equal to CPNotFound, was " + index]; + + [set0 addIndexesInRange:CPMakeRange(1, 10)]; + index = [set0 indexPassingTest:function(anIndex) + { + return anIndex % 2 === 0; + }]; + [self assertTrue:index === 2 message:"index must be equal to 2"]; + + index = [set0 indexPassingTest:function(anIndex) + { + return anIndex === 1000; + }]; + [self assertTrue:index === CPNotFound message:"must be equal to CPNotFound, was " + index]; +} + +- (void)testIndexesPassingTest +{ + var set0 = [CPMutableIndexSet indexSet], + set1 = [CPMutableIndexSet indexSet], + indexes = [set0 indexesPassingTest:function(anIndex) + { + return anIndex % 2 === 0; + }]; + + [self assertTrue:[indexes isEqualToIndexSet:set1] message:"must be equal to " + [set1 description] + ", was " + [indexes description]]; + + [set0 addIndexesInRange:CPMakeRange(1, 10)]; + [set1 addIndex:2]; + [set1 addIndex:4]; + [set1 addIndex:6]; + [set1 addIndex:8]; + [set1 addIndex:10]; + indexes = [set0 indexesPassingTest:function(anIndex) + { + return anIndex % 2 === 0; + }]; + [self assertTrue:[indexes isEqualToIndexSet:set1] message:"must be equal to " + [set1 description] + ", was " + [indexes description]]; +} + +- (void)testIndexPassingTest_options +{ + var set0 = [CPMutableIndexSet indexSetWithIndexesInRange:CPMakeRange(1, 10)], + index = [set0 indexWithOptions:CPEnumerationReverse + passingTest:function(anIndex) + { + return anIndex % 2 === 0; + }]; + [self assertTrue:index === 10 message:"index must be equal to 10"]; +} + +- (void)testIndexesPassingTest_options +{ + var set0 = [CPMutableIndexSet indexSetWithIndexesInRange:CPMakeRange(1, 5)], + set1 = [CPMutableIndexSet indexSet], + visitedIndexes = [], + indexes = [set0 indexesWithOptions:CPEnumerationReverse + passingTest:function(anIndex) + { + visitedIndexes.push(anIndex); + return anIndex % 2 === 0; + }]; + [set1 addIndex:2]; + [set1 addIndex:4]; + + [self assertTrue:[indexes isEqualToIndexSet:set1] message:"must be equal to " + [set1 description] + ", was " + [indexes description]]; + [self assert:[5, 4, 3, 2, 1] equals:visitedIndexes message:"enumeration should be done in reverse"]; +} + +- (void)testIndexPassingTest_options_range +{ + var set0 = [CPMutableIndexSet indexSetWithIndexesInRange:CPMakeRange(1, 10)], + index = [set0 indexInRange:CPMakeRange(3, 4) + options:CPEnumerationReverse + passingTest:function(anIndex) + { + return anIndex % 2 === 0; + }]; + [self assertTrue:index === 6 message:"index must be equal to 6"]; +} + +- (void)testIndexesPassingTest_options_range +{ + var set0 = [CPMutableIndexSet indexSetWithIndexesInRange:CPMakeRange(1, 5)], + set1 = [CPMutableIndexSet indexSet], + visitedIndexes = [], + indexes = [set0 indexesInRange:CPMakeRange(3, 4) + options:CPEnumerationReverse + passingTest:function(anIndex) + { + visitedIndexes.push(anIndex); + return anIndex % 2 === 0; + }]; + [set1 addIndex:4]; + + [self assertTrue:[indexes isEqualToIndexSet:set1] message:"must be equal to " + [set1 description] + ", was " + [indexes description]]; + [self assert:[5, 4, 3] equals:visitedIndexes message:"enumeration should be done in reverse"]; +} + - (void)tearDown { _set = nil;