/Foundation/CPIndexSet.j

http://github.com/cacaodev/cappuccino · Unknown · 1191 lines · 972 code · 219 blank · 0 comment · 0 complexity · ef000d2f7acea28d36a8cb1473f5e45b MD5 · raw file

  1. /*
  2. * CPIndexSet.j
  3. * Foundation
  4. *
  5. * Created by Francisco Tolmasky.
  6. * Copyright 2008, 280 North, Inc.
  7. *
  8. * This library is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU Lesser General Public
  10. * License as published by the Free Software Foundation; either
  11. * version 2.1 of the License, or (at your option) any later version.
  12. *
  13. * This library is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. * Lesser General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU Lesser General Public
  19. * License along with this library; if not, write to the Free Software
  20. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  21. */
  22. #include "Foundation.h"
  23. @import "CPArray.j"
  24. @import "CPObject.j"
  25. @import "CPRange.j"
  26. /*!
  27. @class CPIndexSet
  28. @ingroup foundation
  29. @brief A collection of unique integers.
  30. Instances of this class are collections of numbers. Each integer can appear
  31. in a collection only once.
  32. */
  33. @implementation CPIndexSet : CPObject
  34. {
  35. unsigned _count;
  36. CPArray _ranges;
  37. }
  38. // Creating an Index Set
  39. /*!
  40. Returns a new empty index set.
  41. */
  42. + (id)indexSet
  43. {
  44. return [[self alloc] init];
  45. }
  46. /*!
  47. Returns a new index set with just one index.
  48. */
  49. + (id)indexSetWithIndex:(int)anIndex
  50. {
  51. return [[self alloc] initWithIndex:anIndex];
  52. }
  53. /*!
  54. Returns a new index set with all the numbers in the specified range.
  55. @param aRange the range of numbers to add to the index set.
  56. */
  57. + (id)indexSetWithIndexesInRange:(CPRange)aRange
  58. {
  59. return [[self alloc] initWithIndexesInRange:aRange];
  60. }
  61. // Initializing and Index Set
  62. - (id)init
  63. {
  64. return [self initWithIndexesInRange:CPMakeRange(0, 0)];
  65. }
  66. /*!
  67. Initializes the index set with a single index.
  68. @return the initialized index set
  69. */
  70. - (id)initWithIndex:(CPInteger)anIndex
  71. {
  72. if (!_IS_NUMERIC(anIndex))
  73. [CPException raise:CPInvalidArgumentException
  74. reason:"Invalid index"];
  75. return [self initWithIndexesInRange:CPMakeRange(anIndex, 1)];
  76. }
  77. /*!
  78. Initializes the index set with numbers from the specified range.
  79. @param aRange the range of numbers to add to the index set
  80. @return the initialized index set
  81. */
  82. - (id)initWithIndexesInRange:(CPRange)aRange
  83. {
  84. if (aRange.location < 0)
  85. [CPException raise:CPInvalidArgumentException reason:"Range " + CPStringFromRange(aRange) + " is out of bounds."];
  86. self = [super init];
  87. if (self)
  88. {
  89. _count = MAX(0, aRange.length);
  90. if (_count > 0)
  91. _ranges = [aRange];
  92. else
  93. _ranges = [];
  94. }
  95. return self;
  96. }
  97. /*!
  98. Initializes the index set with another index set.
  99. @param anIndexSet the index set from which to read the initial index set
  100. @return the initialized index set
  101. */
  102. - (id)initWithIndexSet:(CPIndexSet)anIndexSet
  103. {
  104. self = [super init];
  105. if (self)
  106. {
  107. _count = [anIndexSet count];
  108. _ranges = [];
  109. var otherRanges = anIndexSet._ranges,
  110. otherRangesCount = otherRanges.length;
  111. while (otherRangesCount--)
  112. _ranges[otherRangesCount] = CPMakeRangeCopy(otherRanges[otherRangesCount]);
  113. }
  114. return self;
  115. }
  116. - (BOOL)isEqual:(id)anObject
  117. {
  118. if (self === anObject)
  119. return YES;
  120. if (!anObject || ![anObject isKindOfClass:[CPIndexSet class]])
  121. return NO;
  122. return [self isEqualToIndexSet:anObject];
  123. }
  124. // Querying an Index Set
  125. /*!
  126. Compares the receiver with the provided index set.
  127. @param anIndexSet the index set to compare to
  128. @return \c YES if the receiver and the index set are functionally equivalent
  129. */
  130. - (BOOL)isEqualToIndexSet:(CPIndexSet)anIndexSet
  131. {
  132. if (!anIndexSet)
  133. return NO;
  134. // Comparisons to ourself are always return YES.
  135. if (self === anIndexSet)
  136. return YES;
  137. var rangesCount = _ranges.length,
  138. otherRanges = anIndexSet._ranges;
  139. // If we have a discrepancy in the number of ranges or the number of indexes,
  140. // simply return NO.
  141. if (rangesCount !== otherRanges.length || _count !== anIndexSet._count)
  142. return NO;
  143. while (rangesCount--)
  144. if (!CPEqualRanges(_ranges[rangesCount], otherRanges[rangesCount]))
  145. return NO;
  146. return YES;
  147. }
  148. - (BOOL)isEqual:(id)anObject
  149. {
  150. return self === anObject ||
  151. [anObject isKindOfClass:[self class]] &&
  152. [self isEqualToIndexSet:anObject];
  153. }
  154. /*!
  155. Returns \c YES if the index set contains the specified index.
  156. @param anIndex the index to check for in the set
  157. @return \c YES if \c anIndex is in the receiver index set
  158. */
  159. - (BOOL)containsIndex:(CPInteger)anIndex
  160. {
  161. return positionOfIndex(_ranges, anIndex) !== CPNotFound;
  162. }
  163. /*!
  164. Returns \c YES if the index set contains all the numbers in the specified range.
  165. @param aRange the range of numbers to check for in the index set
  166. */
  167. - (BOOL)containsIndexesInRange:(CPRange)aRange
  168. {
  169. if (aRange.length <= 0)
  170. return NO;
  171. // If we have less total indexes than aRange, we can't possibly contain aRange.
  172. if (_count < aRange.length)
  173. return NO;
  174. // Search for first location
  175. var rangeIndex = positionOfIndex(_ranges, aRange.location);
  176. // If we don't have the first location, then we don't contain aRange.
  177. if (rangeIndex === CPNotFound)
  178. return NO;
  179. var range = _ranges[rangeIndex];
  180. // The intersection must contain all the indexes from the original range.
  181. return CPIntersectionRange(range, aRange).length === aRange.length;
  182. }
  183. /*!
  184. Returns \c YES if the receiving index set contains all the indices in the argument.
  185. @param anIndexSet the set of indices to check for in the receiving index set
  186. */
  187. - (BOOL)containsIndexes:(CPIndexSet)anIndexSet
  188. {
  189. var otherCount = anIndexSet._count;
  190. if (otherCount <= 0)
  191. return YES;
  192. // If we have less total indexes than anIndexSet, we can't possibly contain aRange.
  193. if (_count < otherCount)
  194. return NO;
  195. var otherRanges = anIndexSet._ranges,
  196. otherRangesCount = otherRanges.length;
  197. while (otherRangesCount--)
  198. if (![self containsIndexesInRange:otherRanges[otherRangesCount]])
  199. return NO;
  200. return YES;
  201. }
  202. /*!
  203. Checks if the receiver contains at least one number in \c aRange.
  204. @param aRange the range of numbers to check.
  205. @return \c YES if the receiving index set contains at least one number in the provided range
  206. */
  207. - (BOOL)intersectsIndexesInRange:(CPRange)aRange
  208. {
  209. if (_count <= 0)
  210. return NO;
  211. var lhsRangeIndex = assumedPositionOfIndex(_ranges, aRange.location);
  212. if (FLOOR(lhsRangeIndex) === lhsRangeIndex)
  213. return YES;
  214. var rhsRangeIndex = assumedPositionOfIndex(_ranges, CPMaxRange(aRange) - 1);
  215. if (FLOOR(rhsRangeIndex) === rhsRangeIndex)
  216. return YES;
  217. return lhsRangeIndex !== rhsRangeIndex;
  218. }
  219. /*!
  220. The number of indices in the set
  221. */
  222. - (int)count
  223. {
  224. return _count;
  225. }
  226. // Accessing Indexes
  227. /*!
  228. Return the first index in the set
  229. */
  230. - (CPInteger)firstIndex
  231. {
  232. if (_count > 0)
  233. return _ranges[0].location;
  234. return CPNotFound;
  235. }
  236. /*!
  237. Returns the last index in the set
  238. */
  239. - (CPInteger)lastIndex
  240. {
  241. if (_count > 0)
  242. return CPMaxRange(_ranges[_ranges.length - 1]) - 1;
  243. return CPNotFound;
  244. }
  245. /*!
  246. Returns the first index value in the receiver which is greater than \c anIndex.
  247. @return the closest index or CPNotFound if no match was found
  248. */
  249. - (CPInteger)indexGreaterThanIndex:(CPInteger)anIndex
  250. {
  251. // The first possible index that would satisfy this requirement.
  252. ++anIndex;
  253. // Attempt to find it or something bigger.
  254. var rangeIndex = assumedPositionOfIndex(_ranges, anIndex);
  255. // Nothing at all found?
  256. if (rangeIndex === CPNotFound)
  257. return CPNotFound;
  258. rangeIndex = CEIL(rangeIndex);
  259. if (rangeIndex >= _ranges.length)
  260. return CPNotFound;
  261. var range = _ranges[rangeIndex];
  262. // Check if it's actually in this range.
  263. if (CPLocationInRange(anIndex, range))
  264. return anIndex;
  265. // If not, it must be the first element of this range.
  266. return range.location;
  267. }
  268. /*!
  269. Returns the first index value in the receiver which is less than \c anIndex.
  270. @return the closest index or CPNotFound if no match was found
  271. */
  272. - (CPInteger)indexLessThanIndex:(CPInteger)anIndex
  273. {
  274. // The first possible index that would satisfy this requirement.
  275. --anIndex;
  276. // Attempt to find it or something smaller.
  277. var rangeIndex = assumedPositionOfIndex(_ranges, anIndex);
  278. // Nothing at all found?
  279. if (rangeIndex === CPNotFound)
  280. return CPNotFound;
  281. rangeIndex = FLOOR(rangeIndex);
  282. if (rangeIndex < 0)
  283. return CPNotFound;
  284. var range = _ranges[rangeIndex];
  285. // Check if it's actually in this range.
  286. if (CPLocationInRange(anIndex, range))
  287. return anIndex;
  288. // If not, it must be the first element of this range.
  289. return CPMaxRange(range) - 1;
  290. }
  291. /*!
  292. Returns the first index value in the receiver which is greater than or equal to \c anIndex.
  293. @return the matching index or CPNotFound if no match was found
  294. */
  295. - (CPInteger)indexGreaterThanOrEqualToIndex:(CPInteger)anIndex
  296. {
  297. return [self indexGreaterThanIndex:anIndex - 1];
  298. }
  299. /*!
  300. Returns the first index value in the receiver which is less than or equal to \c anIndex.
  301. @return the matching index or CPNotFound if no match was found
  302. */
  303. - (CPInteger)indexLessThanOrEqualToIndex:(CPInteger)anIndex
  304. {
  305. return [self indexLessThanIndex:anIndex + 1];
  306. }
  307. /*!
  308. Fills up the specified array with numbers from the index set within
  309. the specified range. The method stops filling up the array until the
  310. \c aMaxCount number have been added or the range maximum is reached.
  311. @param anArray the array to fill up
  312. @param aMaxCount the maximum number of numbers to adds
  313. @param aRangePointer the range of indices to add
  314. @return the number of elements added to the array
  315. */
  316. - (CPInteger)getIndexes:(CPArray)anArray maxCount:(CPInteger)aMaxCount inIndexRange:(CPRange)aRange
  317. {
  318. if (!_count || aMaxCount === 0 || aRange && !aRange.length)
  319. {
  320. if (aRange)
  321. aRange.length = 0;
  322. return 0;
  323. }
  324. var total = 0;
  325. if (aRange)
  326. {
  327. var firstIndex = aRange.location,
  328. lastIndex = CPMaxRange(aRange) - 1,
  329. rangeIndex = CEIL(assumedPositionOfIndex(_ranges, firstIndex)),
  330. lastRangeIndex = FLOOR(assumedPositionOfIndex(_ranges, lastIndex));
  331. }
  332. else
  333. {
  334. var firstIndex = [self firstIndex],
  335. lastIndex = [self lastIndex],
  336. rangeIndex = 0,
  337. lastRangeIndex = _ranges.length - 1;
  338. }
  339. while (rangeIndex <= lastRangeIndex)
  340. {
  341. var range = _ranges[rangeIndex],
  342. index = MAX(firstIndex, range.location),
  343. maxRange = MIN(lastIndex + 1, CPMaxRange(range));
  344. for (; index < maxRange; ++index)
  345. {
  346. anArray[total++] = index;
  347. if (total === aMaxCount)
  348. {
  349. // Update aRange if it exists...
  350. if (aRange)
  351. {
  352. aRange.location = index + 1;
  353. aRange.length = lastIndex + 1 - index - 1;
  354. }
  355. return aMaxCount;
  356. }
  357. }
  358. ++rangeIndex;
  359. }
  360. // Update aRange if it exists...
  361. if (aRange)
  362. {
  363. aRange.location = CPNotFound;
  364. aRange.length = 0;
  365. }
  366. return total;
  367. }
  368. - (CPString)description
  369. {
  370. var description = [super description];
  371. if (_count)
  372. {
  373. var index = 0,
  374. count = _ranges.length;
  375. description += "[number of indexes: " + _count + " (in " + count;
  376. if (count === 1)
  377. description += " range), indexes: (";
  378. else
  379. description += " ranges), indexes: (";
  380. for (; index < count; ++index)
  381. {
  382. var range = _ranges[index];
  383. description += range.location;
  384. if (range.length > 1)
  385. description += "-" + (CPMaxRange(range) - 1);
  386. if (index + 1 < count)
  387. description += " ";
  388. }
  389. description += ")]";
  390. }
  391. else
  392. description += "(no indexes)";
  393. return description;
  394. }
  395. - (void)enumerateIndexesUsingBlock:(Function /*(int idx, @ref BOOL stop) */)aFunction
  396. {
  397. [self enumerateIndexesWithOptions:CPEnumerationNormal usingBlock:aFunction];
  398. }
  399. - (void)enumerateIndexesWithOptions:(CPEnumerationOptions)options usingBlock:(Function /*(int idx, @ref BOOL stop)*/)aFunction
  400. {
  401. if (!_count)
  402. return;
  403. [self enumerateIndexesInRange:CPMakeRange(0, CPMaxRange(_ranges[_ranges.length - 1])) options:options usingBlock:aFunction];
  404. }
  405. - (void)enumerateIndexesInRange:(CPRange)enumerationRange options:(CPEnumerationOptions)options usingBlock:(Function /*(int idx, @ref BOOL stop)*/)aFunction
  406. {
  407. if (!_count || CPEmptyRange(enumerationRange))
  408. return;
  409. var shouldStop = NO,
  410. index,
  411. stop,
  412. increment;
  413. if (options & CPEnumerationReverse)
  414. {
  415. index = _ranges.length - 1,
  416. stop = -1,
  417. increment = -1;
  418. }
  419. else
  420. {
  421. index = 0;
  422. stop = _ranges.length;
  423. increment = 1;
  424. }
  425. for (; index !== stop; index += increment)
  426. {
  427. var range = _ranges[index],
  428. rangeIndex,
  429. rangeStop,
  430. rangeIncrement;
  431. if (options & CPEnumerationReverse)
  432. {
  433. rangeIndex = CPMaxRange(range) - 1;
  434. rangeStop = range.location - 1;
  435. rangeIncrement = -1;
  436. }
  437. else
  438. {
  439. rangeIndex = range.location;
  440. rangeStop = CPMaxRange(range);
  441. rangeIncrement = 1;
  442. }
  443. for (; rangeIndex !== rangeStop; rangeIndex += rangeIncrement)
  444. {
  445. if (CPLocationInRange(rangeIndex, enumerationRange))
  446. {
  447. aFunction(rangeIndex, @ref(shouldStop));
  448. if (shouldStop)
  449. return;
  450. }
  451. }
  452. }
  453. }
  454. - (unsigned)indexPassingTest:(Function /*(int anIndex)*/)aPredicate
  455. {
  456. return [self indexWithOptions:CPEnumerationNormal passingTest:aPredicate];
  457. }
  458. - (CPIndexSet)indexesPassingTest:(Function /*(int anIndex)*/)aPredicate
  459. {
  460. return [self indexesWithOptions:CPEnumerationNormal passingTest:aPredicate];
  461. }
  462. - (unsigned)indexWithOptions:(CPEnumerationOptions)anOptions passingTest:(Function /*(int anIndex)*/)aPredicate
  463. {
  464. if (!_count)
  465. return CPNotFound;
  466. return [self indexInRange:CPMakeRange(0, CPMaxRange(_ranges[_ranges.length - 1])) options:anOptions passingTest:aPredicate];
  467. }
  468. - (CPIndexSet)indexesWithOptions:(CPEnumerationOptions)anOptions passingTest:(Function /*(int anIndex)*/)aPredicate
  469. {
  470. if (!_count)
  471. return [CPIndexSet indexSet];
  472. return [self indexesInRange:CPMakeRange(0, CPMaxRange(_ranges[_ranges.length - 1])) options:anOptions passingTest:aPredicate];
  473. }
  474. - (unsigned)indexInRange:(CPRange)aRange options:(CPEnumerationOptions)anOptions passingTest:(Function /*(int anIndex)*/)aPredicate
  475. {
  476. if (!_count || CPEmptyRange(aRange))
  477. return CPNotFound;
  478. var shouldStop = NO,
  479. index,
  480. stop,
  481. increment;
  482. if (anOptions & CPEnumerationReverse)
  483. {
  484. index = _ranges.length - 1,
  485. stop = -1,
  486. increment = -1;
  487. }
  488. else
  489. {
  490. index = 0;
  491. stop = _ranges.length;
  492. increment = 1;
  493. }
  494. for (; index !== stop; index += increment)
  495. {
  496. var range = _ranges[index],
  497. rangeIndex,
  498. rangeStop,
  499. rangeIncrement;
  500. if (anOptions & CPEnumerationReverse)
  501. {
  502. rangeIndex = CPMaxRange(range) - 1;
  503. rangeStop = range.location - 1;
  504. rangeIncrement = -1;
  505. }
  506. else
  507. {
  508. rangeIndex = range.location;
  509. rangeStop = CPMaxRange(range);
  510. rangeIncrement = 1;
  511. }
  512. for (; rangeIndex !== rangeStop; rangeIndex += rangeIncrement)
  513. {
  514. if (CPLocationInRange(rangeIndex, aRange))
  515. {
  516. if (aPredicate(rangeIndex, @ref(shouldStop)))
  517. return rangeIndex;
  518. if (shouldStop)
  519. return CPNotFound;
  520. }
  521. }
  522. }
  523. return CPNotFound;
  524. }
  525. - (CPIndexSet)indexesInRange:(CPRange)aRange options:(CPEnumerationOptions)anOptions passingTest:(Function /*(int anIndex)*/)aPredicate
  526. {
  527. if (!_count || CPEmptyRange(aRange))
  528. return [CPIndexSet indexSet];
  529. var shouldStop = NO,
  530. index,
  531. stop,
  532. increment;
  533. if (anOptions & CPEnumerationReverse)
  534. {
  535. index = _ranges.length - 1,
  536. stop = -1,
  537. increment = -1;
  538. }
  539. else
  540. {
  541. index = 0;
  542. stop = _ranges.length;
  543. increment = 1;
  544. }
  545. var indexesPassingTest = [CPMutableIndexSet indexSet];
  546. for (; index !== stop; index += increment)
  547. {
  548. var range = _ranges[index],
  549. rangeIndex,
  550. rangeStop,
  551. rangeIncrement;
  552. if (anOptions & CPEnumerationReverse)
  553. {
  554. rangeIndex = CPMaxRange(range) - 1;
  555. rangeStop = range.location - 1;
  556. rangeIncrement = -1;
  557. }
  558. else
  559. {
  560. rangeIndex = range.location;
  561. rangeStop = CPMaxRange(range);
  562. rangeIncrement = 1;
  563. }
  564. for (; rangeIndex !== rangeStop; rangeIndex += rangeIncrement)
  565. {
  566. if (CPLocationInRange(rangeIndex, aRange))
  567. {
  568. if (aPredicate(rangeIndex, @ref(shouldStop)))
  569. [indexesPassingTest addIndex:rangeIndex];
  570. if (shouldStop)
  571. return indexesPassingTest;
  572. }
  573. }
  574. }
  575. return indexesPassingTest;
  576. }
  577. @end
  578. @implementation CPIndexSet(CPMutableIndexSet)
  579. // Adding indexes.
  580. /*!
  581. Adds an index to the set.
  582. @param anIndex the index to add
  583. */
  584. - (void)addIndex:(CPInteger)anIndex
  585. {
  586. [self addIndexesInRange:CPMakeRange(anIndex, 1)];
  587. }
  588. /*!
  589. Adds indices to the set
  590. @param anIndexSet a set of indices to add to the receiver
  591. */
  592. - (void)addIndexes:(CPIndexSet)anIndexSet
  593. {
  594. var otherRanges = anIndexSet._ranges,
  595. otherRangesCount = otherRanges.length;
  596. // Simply add each range within anIndexSet.
  597. while (otherRangesCount--)
  598. [self addIndexesInRange:otherRanges[otherRangesCount]];
  599. }
  600. /*!
  601. Adds the range of indices to the set
  602. @param aRange the range of numbers to add as indices to the set
  603. */
  604. - (void)addIndexesInRange:(CPRange)aRange
  605. {
  606. if (aRange.location < 0)
  607. [CPException raise:CPInvalidArgumentException reason:"Range " + CPStringFromRange(aRange) + " is out of bounds."];
  608. // If empty range, bail.
  609. if (aRange.length <= 0)
  610. return;
  611. // If we currently don't have any indexes, this represents our entire set.
  612. if (_count <= 0)
  613. {
  614. _count = aRange.length;
  615. _ranges = [aRange];
  616. return;
  617. }
  618. var rangeCount = _ranges.length,
  619. lhsRangeIndex = assumedPositionOfIndex(_ranges, aRange.location - 1),
  620. lhsRangeIndexCEIL = CEIL(lhsRangeIndex);
  621. if (lhsRangeIndexCEIL === lhsRangeIndex && lhsRangeIndexCEIL < rangeCount)
  622. aRange = CPUnionRange(aRange, _ranges[lhsRangeIndexCEIL]);
  623. var rhsRangeIndex = assumedPositionOfIndex(_ranges, CPMaxRange(aRange)),
  624. rhsRangeIndexFLOOR = FLOOR(rhsRangeIndex);
  625. if (rhsRangeIndexFLOOR === rhsRangeIndex && rhsRangeIndexFLOOR >= 0)
  626. aRange = CPUnionRange(aRange, _ranges[rhsRangeIndexFLOOR]);
  627. var removalCount = rhsRangeIndexFLOOR - lhsRangeIndexCEIL + 1;
  628. if (removalCount === _ranges.length)
  629. {
  630. _ranges = [aRange];
  631. _count = aRange.length;
  632. }
  633. else if (removalCount === 1)
  634. {
  635. if (lhsRangeIndexCEIL < _ranges.length)
  636. _count -= _ranges[lhsRangeIndexCEIL].length;
  637. _count += aRange.length;
  638. _ranges[lhsRangeIndexCEIL] = aRange;
  639. }
  640. else
  641. {
  642. if (removalCount > 0)
  643. {
  644. var removal = lhsRangeIndexCEIL,
  645. lastRemoval = lhsRangeIndexCEIL + removalCount - 1;
  646. for (; removal <= lastRemoval; ++removal)
  647. _count -= _ranges[removal].length;
  648. [_ranges removeObjectsInRange:CPMakeRange(lhsRangeIndexCEIL, removalCount)];
  649. }
  650. [_ranges insertObject:aRange atIndex:lhsRangeIndexCEIL];
  651. _count += aRange.length;
  652. }
  653. }
  654. // Removing Indexes
  655. /*!
  656. Removes an index from the set
  657. @param anIndex the index to remove
  658. */
  659. - (void)removeIndex:(CPInteger)anIndex
  660. {
  661. [self removeIndexesInRange:CPMakeRange(anIndex, 1)];
  662. }
  663. /*!
  664. Removes the indices from the receiving set.
  665. @param anIndexSet the set of indices to remove
  666. from the receiver
  667. */
  668. - (void)removeIndexes:(CPIndexSet)anIndexSet
  669. {
  670. var otherRanges = anIndexSet._ranges,
  671. otherRangesCount = otherRanges.length;
  672. // Simply remove each index from anIndexSet
  673. while (otherRangesCount--)
  674. [self removeIndexesInRange:otherRanges[otherRangesCount]];
  675. }
  676. /*!
  677. Removes all indices from the set
  678. */
  679. - (void)removeAllIndexes
  680. {
  681. _ranges = [];
  682. _count = 0;
  683. }
  684. /*!
  685. Removes the indices in the range from the
  686. set.
  687. @param aRange the range of indices to remove
  688. */
  689. - (void)removeIndexesInRange:(CPRange)aRange
  690. {
  691. // If empty range, bail.
  692. if (aRange.length <= 0)
  693. return;
  694. // If we currently don't have any indexes, there's nothing to remove.
  695. if (_count <= 0)
  696. return;
  697. var rangeCount = _ranges.length,
  698. lhsRangeIndex = assumedPositionOfIndex(_ranges, aRange.location),
  699. lhsRangeIndexCEIL = CEIL(lhsRangeIndex);
  700. // Do we fall on an actual existing range?
  701. if (lhsRangeIndex === lhsRangeIndexCEIL && lhsRangeIndexCEIL < rangeCount)
  702. {
  703. var existingRange = _ranges[lhsRangeIndexCEIL];
  704. // If these ranges don't start in the same place, we have to cull it.
  705. if (aRange.location !== existingRange.location)
  706. {
  707. var maxRange = CPMaxRange(aRange),
  708. existingMaxRange = CPMaxRange(existingRange);
  709. existingRange.length = aRange.location - existingRange.location;
  710. // If this range is internal to the existing range, we have a unique splitting case.
  711. if (maxRange < existingMaxRange)
  712. {
  713. _count -= aRange.length;
  714. [_ranges insertObject:CPMakeRange(maxRange, existingMaxRange - maxRange) atIndex:lhsRangeIndexCEIL + 1];
  715. return;
  716. }
  717. else
  718. {
  719. _count -= existingMaxRange - aRange.location;
  720. lhsRangeIndexCEIL += 1;
  721. }
  722. }
  723. }
  724. var rhsRangeIndex = assumedPositionOfIndex(_ranges, CPMaxRange(aRange) - 1),
  725. rhsRangeIndexFLOOR = FLOOR(rhsRangeIndex);
  726. if (rhsRangeIndex === rhsRangeIndexFLOOR && rhsRangeIndexFLOOR >= 0)
  727. {
  728. var maxRange = CPMaxRange(aRange),
  729. existingRange = _ranges[rhsRangeIndexFLOOR],
  730. existingMaxRange = CPMaxRange(existingRange);
  731. if (maxRange !== existingMaxRange)
  732. {
  733. _count -= maxRange - existingRange.location;
  734. rhsRangeIndexFLOOR -= 1; // This is accounted for, and thus as if we got the previous spot.
  735. existingRange.location = maxRange;
  736. existingRange.length = existingMaxRange - maxRange;
  737. }
  738. }
  739. var removalCount = rhsRangeIndexFLOOR - lhsRangeIndexCEIL + 1;
  740. if (removalCount > 0)
  741. {
  742. var removal = lhsRangeIndexCEIL,
  743. lastRemoval = lhsRangeIndexCEIL + removalCount - 1;
  744. for (; removal <= lastRemoval; ++removal)
  745. _count -= _ranges[removal].length;
  746. [_ranges removeObjectsInRange:CPMakeRange(lhsRangeIndexCEIL, removalCount)];
  747. }
  748. }
  749. // Shifting Index Groups
  750. /*!
  751. Shifts the values of indices left or right by a specified amount.
  752. @param anIndex the index to start the shifting operation from (inclusive)
  753. @param aDelta the amount and direction to shift. A positive value shifts to
  754. the right. A negative value shifts to the left.
  755. */
  756. - (void)shiftIndexesStartingAtIndex:(CPInteger)anIndex by:(int)aDelta
  757. {
  758. if (!_count || aDelta == 0)
  759. return;
  760. // Later indexes have a higher probability of being shifted
  761. // than lower ones, so start at the end and work backwards.
  762. var i = _ranges.length - 1,
  763. shifted = CPMakeRange(CPNotFound, 0);
  764. for (; i >= 0; --i)
  765. {
  766. var range = _ranges[i],
  767. maximum = CPMaxRange(range);
  768. if (anIndex >= maximum)
  769. break;
  770. // If our index is within our range, but not the first index,
  771. // then this range will be split.
  772. if (anIndex > range.location)
  773. {
  774. // Split the range into shift and unshifted.
  775. shifted = CPMakeRange(anIndex + aDelta, maximum - anIndex);
  776. range.length = anIndex - range.location;
  777. // If our delta is positive, then we can simply add the range
  778. // to the array.
  779. if (aDelta > 0)
  780. [_ranges insertObject:shifted atIndex:i + 1];
  781. // If it's negative, it needs to be added properly later.
  782. else if (shifted.location < 0)
  783. {
  784. shifted.length = CPMaxRange(shifted);
  785. shifted.location = 0;
  786. }
  787. // We don't need to continue.
  788. break;
  789. }
  790. // Shift the range, and normalize it if the result is negative.
  791. if ((range.location += aDelta) < 0)
  792. {
  793. _count -= range.length - CPMaxRange(range);
  794. range.length = CPMaxRange(range);
  795. range.location = 0;
  796. }
  797. }
  798. // We need to add the shifted ranges if the delta is negative.
  799. if (aDelta < 0)
  800. {
  801. var j = i + 1,
  802. count = _ranges.length,
  803. shifts = [];
  804. for (; j < count; ++j)
  805. {
  806. [shifts addObject:_ranges[j]];
  807. _count -= _ranges[j].length;
  808. }
  809. if ((j = i + 1) < count)
  810. {
  811. [_ranges removeObjectsInRange:CPMakeRange(j, count - j)];
  812. for (j = 0, count = shifts.length; j < count; ++j)
  813. [self addIndexesInRange:shifts[j]];
  814. }
  815. if (shifted.location != CPNotFound)
  816. [self addIndexesInRange:shifted];
  817. }
  818. }
  819. @end
  820. var CPIndexSetCountKey = @"CPIndexSetCountKey",
  821. CPIndexSetRangeStringsKey = @"CPIndexSetRangeStringsKey";
  822. @implementation CPIndexSet (CPCoding)
  823. /*!
  824. Initializes the index set from a coder.
  825. @param aCoder the coder from which to read the
  826. index set data
  827. @return the initialized index set
  828. */
  829. - (id)initWithCoder:(CPCoder)aCoder
  830. {
  831. self = [super init];
  832. if (self)
  833. {
  834. _count = [aCoder decodeIntForKey:CPIndexSetCountKey];
  835. _ranges = [];
  836. var rangeStrings = [aCoder decodeObjectForKey:CPIndexSetRangeStringsKey],
  837. index = 0,
  838. count = rangeStrings.length;
  839. for (; index < count; ++index)
  840. _ranges.push(CPRangeFromString(rangeStrings[index]));
  841. }
  842. return self;
  843. }
  844. /*!
  845. Writes out the index set to the specified coder.
  846. @param aCoder the coder to which the index set will
  847. be written
  848. */
  849. - (void)encodeWithCoder:(CPCoder)aCoder
  850. {
  851. [aCoder encodeInt:_count forKey:CPIndexSetCountKey];
  852. var index = 0,
  853. count = _ranges.length,
  854. rangeStrings = [];
  855. for (; index < count; ++index)
  856. rangeStrings[index] = CPStringFromRange(_ranges[index]);
  857. [aCoder encodeObject:rangeStrings forKey:CPIndexSetRangeStringsKey];
  858. }
  859. @end
  860. @implementation CPIndexSet (CPCopying)
  861. /*!
  862. Creates a deep copy of the index set. The returned copy
  863. is mutable. The reason for the two copy methods is for
  864. source compatibility with GNUStep code.
  865. @return the index set copy
  866. */
  867. - (id)copy
  868. {
  869. return [[[self class] alloc] initWithIndexSet:self];
  870. }
  871. /*!
  872. Creates a deep copy of the index set. The returned copy
  873. is mutable. The reason for the two copy methods is for
  874. source compatibility with GNUStep code.
  875. @return the index set copy
  876. */
  877. - (id)mutableCopy
  878. {
  879. return [[[self class] alloc] initWithIndexSet:self];
  880. }
  881. @end
  882. /*!
  883. @class CPMutableIndexSet
  884. @ingroup compatibility
  885. This class is an empty of subclass of CPIndexSet.
  886. CPIndexSet already implements mutable methods, and
  887. this class only exists for source compatibility.
  888. */
  889. @implementation CPMutableIndexSet : CPIndexSet
  890. @end
  891. var positionOfIndex = function(ranges, anIndex)
  892. {
  893. var low = 0,
  894. high = ranges.length - 1;
  895. while (low <= high)
  896. {
  897. var middle = FLOOR(low + (high - low) / 2),
  898. range = ranges[middle];
  899. if (anIndex < range.location)
  900. high = middle - 1;
  901. else if (anIndex >= CPMaxRange(range))
  902. low = middle + 1;
  903. else
  904. return middle;
  905. }
  906. return CPNotFound;
  907. };
  908. var assumedPositionOfIndex = function(ranges, anIndex)
  909. {
  910. var count = ranges.length;
  911. if (count <= 0)
  912. return CPNotFound;
  913. var low = 0,
  914. high = count * 2;
  915. while (low <= high)
  916. {
  917. var middle = FLOOR(low + (high - low) / 2),
  918. position = middle / 2,
  919. positionFLOOR = FLOOR(position);
  920. if (position === positionFLOOR)
  921. {
  922. if (positionFLOOR - 1 >= 0 && anIndex < CPMaxRange(ranges[positionFLOOR - 1]))
  923. high = middle - 1;
  924. else if (positionFLOOR < count && anIndex >= ranges[positionFLOOR].location)
  925. low = middle + 1;
  926. else
  927. return positionFLOOR - 0.5;
  928. }
  929. else
  930. {
  931. var range = ranges[positionFLOOR];
  932. if (anIndex < range.location)
  933. high = middle - 1;
  934. else if (anIndex >= CPMaxRange(range))
  935. low = middle + 1;
  936. else
  937. return positionFLOOR;
  938. }
  939. }
  940. return CPNotFound;
  941. };
  942. /*
  943. new old method
  944. X + (id)indexSet;
  945. X + (id)indexSetWithIndex:(unsigned int)value;
  946. X + (id)indexSetWithIndexesInRange:(NSRange)range;
  947. X X - (id)init;
  948. X X - (id)initWithIndex:(unsigned int)value;
  949. X X - (id)initWithIndexesInRange:(NSRange)range; // designated initializer
  950. X X - (id)initWithIndexSet:(NSIndexSet *)indexSet; // designated initializer
  951. X - (BOOL)isEqualToIndexSet:(NSIndexSet *)indexSet;
  952. X X - (unsigned int)count;
  953. X X - (unsigned int)firstIndex;
  954. X X - (unsigned int)lastIndex;
  955. X X - (unsigned int)indexGreaterThanIndex:(unsigned int)value;
  956. X X - (unsigned int)indexLessThanIndex:(unsigned int)value;
  957. X X - (unsigned int)indexGreaterThanOrEqualToIndex:(unsigned int)value;
  958. X X - (unsigned int)indexLessThanOrEqualToIndex:(unsigned int)value;
  959. X - (unsigned int)getIndexes:(unsigned int *)indexBuffer maxCount:(unsigned int)bufferSize inIndexRange:(NSRangePointer)range;
  960. X X - (BOOL)containsIndex:(unsigned int)value;
  961. X X - (BOOL)containsIndexesInRange:(NSRange)range;
  962. X X - (BOOL)containsIndexes:(NSIndexSet *)indexSet;
  963. X X - (BOOL)intersectsIndexesInRange:(NSRange)range;
  964. X X - (void)addIndexes:(NSIndexSet *)indexSet;
  965. X - (void)removeIndexes:(NSIndexSet *)indexSet;
  966. X X - (void)removeAllIndexes;
  967. X - (void)addIndex:(unsigned int)value;
  968. X - (void)removeIndex:(unsigned int)value;
  969. X - (void)addIndexesInRange:(NSRange)range;
  970. X - (void)removeIndexesInRange:(NSRange)range;
  971. - (void)shiftIndexesStartingAtIndex:(CPUInteger)index by:(int)delta;
  972. */