/data/iterable.d

http://github.com/wilkie/djehuty · D · 828 lines · 653 code · 108 blank · 67 comment · 95 complexity · 38dadcbcfaaf0ad79c19d42c382617fe MD5 · raw file

  1. module data.iterable;
  2. import djehuty;
  3. import io.console;
  4. // Description: This template resolves to true when the type T is
  5. // either an array or a class that inherits from Iterable.
  6. template IsIterableImpl(int idx = 0, Base, T...) {
  7. static if (idx == T.length) {
  8. // Go to the base class and start over...
  9. const bool IsIterableImpl = IsIterable!(Super!(Base));
  10. }
  11. else static if (IsInterface!(T[idx])) {
  12. // Check each interface for whether it is Iterable
  13. static if (T[idx].stringof == "Iterable") {
  14. const bool IsIterableImpl = true;
  15. }
  16. else {
  17. const bool IsIterableImpl = IsIterableImpl!(idx+1, Base, T);
  18. }
  19. }
  20. else {
  21. // Well... this doesn't make sense... give up!
  22. const bool IsIterableImpl = false;
  23. }
  24. }
  25. template IsIterable(T) {
  26. static if (IsArray!(T)) {
  27. // T is an array, so it is iterable
  28. const bool IsIterable = true;
  29. }
  30. else static if (is(T == Object)) {
  31. // Cannot be iterable if T is Object
  32. const bool IsIterable = false;
  33. }
  34. else static if (IsInterface!(T)) {
  35. static if (T.stringof == "Iterable") {
  36. const bool IsIterable = true;
  37. }
  38. else {
  39. const bool IsIterable = false;
  40. }
  41. }
  42. else static if (IsClass!(T)) {
  43. // It is some class... we should read its interfaces
  44. const bool IsIterable = IsIterableImpl!(0, T, Interfaces!(T));
  45. }
  46. else {
  47. // It is some other type...
  48. const bool IsIterable = false;
  49. }
  50. }
  51. // Description: Returns Iterable!(T) for any class that inherits Iterable.
  52. template BaseIterable(T) {
  53. static if (IsClass!(T)) {
  54. static if (is(T == Object)) {
  55. alias T BaseIterable;
  56. }
  57. else {
  58. alias BaseIterable!(Super!(T)) BaseIterable;
  59. }
  60. }
  61. static if (IsInterface!(T)) {
  62. alias T BaseIterable;
  63. }
  64. else {
  65. alias T BaseIterable;
  66. }
  67. }
  68. // Description: This template resolves to the base type for any class that
  69. // inherits Iterable or any array. That is int[][] will return int.
  70. // And List!(List!(int)) will return int.
  71. template BaseIterableType(T) {
  72. static if (IsIterable!(IterableType!(T))) {
  73. alias BaseIterableType!(IterableType!(T)) BaseIterableType;
  74. }
  75. else {
  76. alias IterableType!(T) BaseIterableType;
  77. }
  78. }
  79. // Description: This template resolves to the iterated type for any class that
  80. // inherits Iterable or any array. That is int[][] will return int[].
  81. // And List!(List!(int)) will return List!(int). int[] will return int.
  82. template IterableType(T) {
  83. static if (IsIterable!(T)) {
  84. static if (IsArray!(T)) {
  85. alias ArrayType!(T) IterableType;
  86. }
  87. else {
  88. alias T.BaseType IterableType;
  89. }
  90. }
  91. else {
  92. alias T IterableType;
  93. }
  94. }
  95. interface Iterable(T) {
  96. private alias T BaseType;
  97. template add(R) {
  98. void add(R item);
  99. }
  100. template addAt(R) {
  101. void addAt(R item, size_t idx);
  102. }
  103. T remove();
  104. T removeAt(size_t idx);
  105. T peek();
  106. T peekAt(size_t idx);
  107. template set(R) {
  108. void set(R value);
  109. }
  110. template setAt(R) {
  111. void setAt(size_t idx, R value);
  112. }
  113. template apply(R, S) {
  114. void apply(R delegate(S) func);
  115. }
  116. template contains(R) {
  117. bool contains(R value);
  118. }
  119. bool empty();
  120. void clear();
  121. T[] array();
  122. Iterable!(T) dup();
  123. Iterable!(T) slice(size_t start, size_t end);
  124. Iterable!(T) reverse();
  125. size_t length();
  126. T opIndex(size_t i1);
  127. template opIndexAssign(R) {
  128. size_t opIndexAssign(R value, size_t i1);
  129. }
  130. int opApply(int delegate(ref T) loopFunc);
  131. int opApply(int delegate(ref size_t, ref T) loopFunc);
  132. int opApplyReverse(int delegate(ref T) loopFunc);
  133. int opApplyReverse(int delegate(ref size_t, ref T) loopFunc);
  134. Iterable!(T) opCat(T[] list);
  135. Iterable!(T) opCat(Iterable!(T) list);
  136. Iterable!(T) opCat(T item);
  137. void opCatAssign(T[] list);
  138. void opCatAssign(Iterable!(T) list);
  139. void opCatAssign(T item);
  140. }
  141. int[] range(int start, int end) {
  142. int[] ret = new int[end-start];
  143. for (int i = start, o; i < end; i++, o++) {
  144. ret[o] = i;
  145. }
  146. return ret;
  147. }
  148. template filter(T, S) {
  149. static assert(IsIterable!(T), "filter: " ~ T.stringof ~ " is not iterable.");
  150. T filter(bool delegate(S) pred, T list) {
  151. T ret;
  152. static if (!IsArray!(T)) {
  153. ret = new T;
  154. }
  155. foreach(item; list) {
  156. if (pred(item)) {
  157. ret ~= item;
  158. }
  159. }
  160. return ret;
  161. }
  162. }
  163. template filter(T, S) {
  164. static assert(IsIterable!(T), "filter: " ~ T.stringof ~ " is not iterable.");
  165. T filter(bool function(S) pred, T list) {
  166. T ret;
  167. static if (!IsArray!(T)) {
  168. ret = new T;
  169. }
  170. foreach(item; list) {
  171. if (pred(item)) {
  172. ret ~= item;
  173. }
  174. }
  175. return ret;
  176. }
  177. }
  178. template count(T, S) {
  179. static assert(IsIterable!(T), "count: " ~ T.stringof ~ " is not iterable.");
  180. size_t count(bool delegate(S) pred, T list) {
  181. size_t acc = 0;
  182. foreach(item; list) {
  183. if (pred(item)) {
  184. acc++;
  185. }
  186. }
  187. return acc;
  188. }
  189. }
  190. template count(T, S) {
  191. static assert(IsIterable!(T), "count: " ~ T.stringof ~ " is not iterable.");
  192. size_t count(bool function(S) pred, T list) {
  193. size_t acc = 0;
  194. foreach(item; list) {
  195. if (pred(item)) {
  196. acc++;
  197. }
  198. }
  199. return acc;
  200. }
  201. }
  202. template filterNot(T, S) {
  203. static assert(IsIterable!(T), "filterNot: " ~ T.stringof ~ " is not iterable.");
  204. T filterNot(bool delegate(S) pred, T list) {
  205. T ret;
  206. static if (!IsArray!(T)) {
  207. ret = new T;
  208. }
  209. foreach(item; list) {
  210. if (!pred(item)) {
  211. ret ~= item;
  212. }
  213. }
  214. return ret;
  215. }
  216. }
  217. template filterNot(T, S) {
  218. static assert(IsIterable!(T), "filterNot: " ~ T.stringof ~ " is not iterable.");
  219. T filterNot(bool function(S) pred, T list) {
  220. T ret;
  221. static if (!IsArray!(T)) {
  222. ret = new T;
  223. }
  224. foreach(item; list) {
  225. if (!pred(item)) {
  226. ret ~= item;
  227. }
  228. }
  229. return ret;
  230. }
  231. }
  232. template map(T, R, S) {
  233. static assert(IsIterable!(T), "map: " ~ T.stringof ~ " is not iterable.");
  234. S[] map(S delegate(R) func, T list) {
  235. S[] ret;
  236. ret = new S[list.length];
  237. foreach(size_t i, R item; list) {
  238. ret[i] = func(item);
  239. }
  240. return ret;
  241. }
  242. }
  243. template map(T, R, S) {
  244. static assert(IsIterable!(T), "map: " ~ T.stringof ~ " is not iterable.");
  245. S[] map(S function(R) func, T list) {
  246. S[] ret;
  247. ret = new S[list.length];
  248. foreach(uint i, item; list) {
  249. ret[i] = func(item);
  250. }
  251. return ret;
  252. }
  253. }
  254. template foldl(T, R, S) {
  255. static assert(IsIterable!(T), "foldl: " ~ T.stringof ~ " is not iterable.");
  256. S foldl(S delegate(R, R) func, T list) {
  257. if (list.length == 1) {
  258. return cast(S)list[0];
  259. }
  260. S acc = func(list[0], list[1]);
  261. foreach(item; list[2..list.length]) {
  262. acc = func(acc, item);
  263. }
  264. return acc;
  265. }
  266. }
  267. template foldl(T, R, S) {
  268. static assert(IsIterable!(T), "foldl: " ~ T.stringof ~ " is not iterable.");
  269. S foldl(S function(R, R) func, T list) {
  270. if (list.length == 1) {
  271. return cast(S)list[0];
  272. }
  273. S acc = func(list[0], list[1]);
  274. foreach(item; list[2..list.length]) {
  275. acc = func(acc, item);
  276. }
  277. return acc;
  278. }
  279. }
  280. template foldr(T, R, S) {
  281. static assert(IsIterable!(T), "foldr: " ~ T.stringof ~ " is not iterable.");
  282. S foldr(S delegate(R, R) func, T list) {
  283. if (list.length == 1) {
  284. return cast(S)list[0];
  285. }
  286. S acc = func(list[list.length-2], list[list.length-1]);
  287. foreach_reverse(item; list[0..list.length-2]) {
  288. acc = func(item, acc);
  289. }
  290. return acc;
  291. }
  292. }
  293. template foldr(T, R, S) {
  294. static assert(IsIterable!(T), "foldr: " ~ T.stringof ~ " is not iterable.");
  295. S foldr(S function(R, R) func, T list) {
  296. if (list.length == 1) {
  297. return cast(S)list[0];
  298. }
  299. S acc = func(list[list.length-2], list[list.length-1]);
  300. foreach_reverse(item; list[0..list.length-2]) {
  301. acc = func(item, acc);
  302. }
  303. return acc;
  304. }
  305. }
  306. template member(T, S) {
  307. static assert(IsIterable!(T), "member: " ~ T.stringof ~ " is not iterable.");
  308. T member(S value, T list) {
  309. foreach(size_t i, S item; list) {
  310. if (value == item) {
  311. return cast(T)(list[i..list.length]);
  312. }
  313. }
  314. return null;
  315. }
  316. }
  317. template remove(T, S) {
  318. static assert(IsIterable!(T), "remove: " ~ T.stringof ~ " is not iterable.");
  319. T remove(S value, T list) {
  320. foreach(uint i, item; list) {
  321. if (value == item) {
  322. return cast(T)(list[0..i] ~ list[i+1..list.length]);
  323. }
  324. }
  325. return list;
  326. }
  327. }
  328. template remove(T, S, R, Q) {
  329. static assert(IsIterable!(T), "remove: " ~ T.stringof ~ " is not iterable.");
  330. T remove(S value, T list, bool delegate(R, Q) equalFunc) {
  331. foreach(uint i, item; list) {
  332. if (equalFunc(value, item)) {
  333. return cast(T)(list[0..i] ~ list[i+1..list.length]);
  334. }
  335. }
  336. return list;
  337. }
  338. }
  339. template car(T) {
  340. static assert(IsIterable!(T), "car: " ~ T.stringof ~ " is not iterable.");
  341. IterableType!(T) car(T list) {
  342. return list[0];
  343. }
  344. }
  345. template cdr(T) {
  346. static assert(IsIterable!(T), "cdr: " ~ T.stringof ~ " is not iterable.");
  347. T cdr(T list) {
  348. return list[1..list.length];
  349. }
  350. }
  351. template cadr(T) {
  352. static assert(IsIterable!(T), "cadr: " ~ T.stringof ~ " is not iterable.");
  353. IterableType!(T) cadr(T list) {
  354. return car(cdr(list));
  355. }
  356. }
  357. template caar(T) {
  358. static assert(IsIterable!(T), "caar: " ~ T.stringof ~ " is not iterable.");
  359. static assert(IsIterable!(IterableType!(T)), "caar: " ~ T.stringof ~ "'s inner type (" ~ IterableType!(T).stringof ~ ") is not iterable.");
  360. IterableType!(IterableType!(T)) caar(T list) {
  361. return car(car(list));
  362. }
  363. }
  364. template first(T) {
  365. static assert(IsIterable!(T), "first: " ~ T.stringof ~ " is not iterable.");
  366. IterableType!(T) first(T list) {
  367. return list[0];
  368. }
  369. }
  370. template second(T) {
  371. static assert(IsIterable!(T), "second: " ~ T.stringof ~ " is not iterable.");
  372. IterableType!(T) second(T list) {
  373. return list[1];
  374. }
  375. }
  376. template third(T) {
  377. static assert(IsIterable!(T), "third: " ~ T.stringof ~ " is not iterable.");
  378. IterableType!(T) third(T[] list) {
  379. return list[2];
  380. }
  381. }
  382. template fourth(T) {
  383. static assert(IsIterable!(T), "fourth: " ~ T.stringof ~ " is not iterable.");
  384. IterableType!(T) fourth(T list) {
  385. return list[3];
  386. }
  387. }
  388. template fifth(T) {
  389. static assert(IsIterable!(T), "fifth: " ~ T.stringof ~ " is not iterable.");
  390. IterableType!(T) fifth(T list) {
  391. return list[4];
  392. }
  393. }
  394. template sixth(T) {
  395. static assert(IsIterable!(T), "sixth: " ~ T.stringof ~ " is not iterable.");
  396. IterableType!(T) sixth(T list) {
  397. return list[5];
  398. }
  399. }
  400. template seventh(T) {
  401. static assert(IsIterable!(T), "seventh: " ~ T.stringof ~ " is not iterable.");
  402. IterableType!(T) seventh(T list) {
  403. return list[6];
  404. }
  405. }
  406. template eighth(T) {
  407. static assert(IsIterable!(T), "eighth: " ~ T.stringof ~ " is not iterable.");
  408. IterableType!(T) eighth(T list) {
  409. return list[7];
  410. }
  411. }
  412. template ninth(T) {
  413. static assert(IsIterable!(T), "ninth: " ~ T.stringof ~ " is not iterable.");
  414. IterableType!(T) ninth(T list) {
  415. return list[8];
  416. }
  417. }
  418. template tenth(T) {
  419. static assert(IsIterable!(T), "tenth: " ~ T.stringof ~ " is not iterable.");
  420. IterableType!(T) tenth(T list) {
  421. return list[9];
  422. }
  423. }
  424. template last(T) {
  425. static assert(IsIterable!(T), "last: " ~ T.stringof ~ " is not iterable.");
  426. IterableType!(T) last(T list) {
  427. return list[list.length-1];
  428. }
  429. }
  430. template rest(T) {
  431. static assert(IsIterable!(T), "rest: " ~ T.stringof ~ " is not iterable.");
  432. T rest(T list) {
  433. return list[1..list.length];
  434. }
  435. }
  436. template drop(T) {
  437. static assert(IsIterable!(T), "drop: " ~ T.stringof ~ " is not iterable.");
  438. T drop(T list, uint num) {
  439. return list[num..list.length];
  440. }
  441. }
  442. template dropRight(T) {
  443. static assert(IsIterable!(T), "dropRight: " ~ T.stringof ~ " is not iterable.");
  444. T dropRight(T list, uint num) {
  445. return list[0..list.length-num];
  446. }
  447. }
  448. template take(T) {
  449. static assert(IsIterable!(T), "take: " ~ T.stringof ~ " is not iterable.");
  450. T take(T list, uint num) {
  451. return list[0..num];
  452. }
  453. }
  454. template takeRight(T) {
  455. static assert(IsIterable!(T), "takeRight: " ~ T.stringof ~ " is not iterable.");
  456. T takeRight(T list, uint num) {
  457. return list[list.length-num..list.length];
  458. }
  459. }
  460. template flatten(T) {
  461. static assert(IsIterable!(T), "flatten: " ~ T.stringof ~ " is not iterable.");
  462. BaseIterableType!(T)[] flatten(T list) {
  463. static if (IsIterable!(IterableType!(T))) {
  464. // recursive
  465. BaseIterableType!(T)[] ret;
  466. foreach(sublist; list) {
  467. ret ~= flatten(sublist);
  468. }
  469. return ret;
  470. }
  471. else {
  472. // base case
  473. BaseIterableType!(T)[] ret;
  474. foreach(item; list) {
  475. ret ~= item;
  476. }
  477. return ret;
  478. }
  479. }
  480. }
  481. template argmin(T, R, S) {
  482. static assert(IsIterable!(T), "argmin: " ~ T.stringof ~ " is not iterable.");
  483. IterableType!(T) argmin(S delegate(R) func, T list) {
  484. IterableType!(T) min;
  485. uint idx = uint.max;
  486. foreach(uint i, item; list) {
  487. S cur = func(item);
  488. if (idx == uint.max || cur < min) {
  489. idx = i;
  490. min = item;
  491. }
  492. }
  493. return min;
  494. }
  495. }
  496. template argmin(T, R, S) {
  497. static assert(IsIterable!(T), "argmin: " ~ T.stringof ~ " is not iterable.");
  498. IterableType!(T) argmin(S function(R) func, T list) {
  499. IterableType!(T) min;
  500. uint idx = uint.max;
  501. foreach(uint i, item; list) {
  502. S cur = func(item);
  503. if (idx == uint.max || cur < min) {
  504. idx = i;
  505. min = item;
  506. }
  507. }
  508. return min;
  509. }
  510. }
  511. template argmax(T, R, S) {
  512. static assert(IsIterable!(T), "argmax: " ~ T.stringof ~ " is not iterable.");
  513. IterableType!(T) argmax(S delegate(R) func, T list) {
  514. IterableType!(T) max;
  515. uint idx = uint.max;
  516. foreach(uint i, item; list) {
  517. S cur = func(item);
  518. if (idx == uint.max || cur > max) {
  519. idx = i;
  520. max = item;
  521. }
  522. }
  523. return max;
  524. }
  525. }
  526. template argmax(T, R, S) {
  527. static assert(IsIterable!(T), "argmax: " ~ T.stringof ~ " is not iterable.");
  528. IterableType!(T) argmax(S function(R) func, T list) {
  529. IterableType!(T) max;
  530. uint idx = uint.max;
  531. foreach(uint i, item; list) {
  532. S cur = func(item);
  533. if (idx == uint.max || cur > max) {
  534. idx = i;
  535. max = item;
  536. }
  537. }
  538. return max;
  539. }
  540. }
  541. template makeList(T) {
  542. T[] makeList(uint amt, T item) {
  543. T[] ret = new T[amt];
  544. ret[] = item;
  545. return ret;
  546. }
  547. }
  548. template addBetween(T, S) {
  549. T addBetween(T list, S val) {
  550. T ret;
  551. static if (!IsArray!(T)) {
  552. ret = new T;
  553. }
  554. for (uint i; i < list.length - 1; i++) {
  555. ret ~= [list[i], val];
  556. }
  557. ret ~= [list[list.length-1]];
  558. return ret;
  559. }
  560. }
  561. // Description: This function will rotate an array by shifting the contents in place.
  562. // list: An Iterable type.
  563. // amount: The amount to rotate to the left. To rotate to the right, specify a negative value.
  564. // Returns: Simply returns the reference to the input.
  565. template rotate(T) {
  566. static assert(IsIterable!(T), "rotate: " ~ T.stringof ~ " is not iterable.");
  567. IterableType!(T)[] rotate(T list, int amount) {
  568. if (list.length == 0) {
  569. return list;
  570. }
  571. amount %= list.length;
  572. if (amount == 0) {
  573. return list;
  574. }
  575. IterableType!(T) tmp;
  576. size_t iterations = 1;
  577. if ((list.length % amount) == 0) {
  578. // Will require multiple passes
  579. iterations = amount;
  580. }
  581. // Figure out the number of swaps per iteration
  582. size_t maxSwaps = list.length / iterations;
  583. maxSwaps--; // account for final swap outside of loop
  584. while(iterations > 0) {
  585. size_t swapWith;
  586. size_t swapIndex = iterations-1;
  587. tmp = list[swapIndex];
  588. for (size_t i = 0; i < maxSwaps; i++) {
  589. swapWith = (swapIndex + amount) % list.length;
  590. list[swapIndex] = list[swapWith];
  591. swapIndex = swapWith;
  592. }
  593. list[swapIndex] = tmp;
  594. iterations--;
  595. }
  596. return list;
  597. }
  598. }
  599. // Description: This function will reverse the contents of the array in place. It will respect unicode
  600. // encoding in terms of grouping combining marks for utf encoded strings.
  601. // list: An Iterable type.
  602. // Returns: Simply returns the reference to the input.
  603. template reverse(T) {
  604. static assert(IsIterable!(T), "reverse: " ~ T.stringof ~ " is not iterable.");
  605. IterableType!(T)[] reverse(T list) {
  606. static if (IsCharType!(IterableType!(T))) {
  607. // We are reversing a unicode string
  608. return unicode_reverse(list);
  609. }
  610. else {
  611. // Normal reverse
  612. size_t front, end;
  613. // Go from front to end, and swap each index
  614. // Note: Since the array passed has a length measured in a factor
  615. // of elementSize, we need to account for that when we treat
  616. // it is a byte array.
  617. if (list.length == 0) {
  618. return list;
  619. }
  620. front = 0;
  621. end = list.length-1;
  622. while(front < end) {
  623. IterableType!(T) tmp = list[front];
  624. list[front] = list[end];
  625. list[end] = tmp;
  626. front++;
  627. end--;
  628. }
  629. return list;
  630. }
  631. }
  632. }
  633. // This internal function will reverse a unicode string of any flavor.
  634. private template unicode_reverse(T) {
  635. T[] unicode_reverse(T[] a) {
  636. // for all elements in 'a'
  637. // A: Identify two substrings to swap
  638. // B: Swap
  639. size_t frontIndex = 0;
  640. size_t frontLength = 0;
  641. size_t endIndex = a.length;
  642. size_t endLength = 0;
  643. size_t swapLength = 0;
  644. int swapRotate = 0;
  645. // Two sections will swap:
  646. // - a[frontIndex..frontIndex+frontLength]
  647. // - a[endIndex..endIndex+endLength]
  648. // If the two sections do not completely match up, it passes
  649. // responsibility to the next iteration to move the remaining elements.
  650. // Like so:
  651. // [0][1][ ][ ][ ][ ][2][3][4]
  652. // \- frontIndex
  653. // |----| frontLength
  654. // \- endIndex
  655. // |-------| endLength
  656. // Will become:
  657. // [2][3][ ][ ][ ][ ][4][0][1]
  658. // |-------------| next iteration
  659. // \- endIndex
  660. // |-| - endLength
  661. // The next iteration will move the remaining elements to the front during
  662. // the swap phase. This is done to avoid phase shifting an array and to
  663. // promote a O(1) space complexity.
  664. while(frontIndex <= endIndex) {
  665. // Find length of next substring to swap
  666. if (frontLength == 0) {
  667. frontLength = 1;
  668. while(!Unicode.isStartChar(a[frontIndex+frontLength])
  669. || Unicode.isDeadChar(a[frontIndex+frontLength..$])) {
  670. if (frontIndex + frontLength == endIndex) {
  671. // Do not count it if it is within the endIndex,
  672. // This would mean the combining marks have been swapped onto another character
  673. break;
  674. }
  675. frontLength++;
  676. }
  677. }
  678. // Find length of next substring from end to swap with
  679. if (endLength == 0) {
  680. endIndex--;
  681. while(!Unicode.isStartChar(a[endIndex])
  682. || Unicode.isDeadChar(a[endIndex..$])) {
  683. endIndex--;
  684. endLength++;
  685. }
  686. endLength++;
  687. }
  688. if (frontIndex + frontLength > endIndex) {
  689. // We do not have to move the middle substring
  690. break;
  691. }
  692. // Figure out the number to swap (lowest length)
  693. if (frontLength < endLength) {
  694. swapLength = frontLength;
  695. swapRotate = cast(int)(swapLength);
  696. // Shift end
  697. a[endIndex..endIndex+endLength].rotate(swapRotate);
  698. }
  699. else {
  700. swapLength = endLength;
  701. swapRotate = cast(int)(cast(long)frontLength - cast(long)swapLength);
  702. // Shift front
  703. a[frontIndex..frontIndex+frontLength].rotate(swapRotate);
  704. }
  705. // xor swap front and end
  706. for(size_t swapCount = 0; swapCount < swapLength; swapCount++) {
  707. size_t index1 = frontIndex + swapCount;
  708. size_t index2 = endIndex + endLength - swapLength + swapCount;
  709. a[index1] ^= a[index2];
  710. a[index2] ^= a[index1];
  711. a[index1] ^= a[index2];
  712. }
  713. // Move to next position in string
  714. frontIndex += swapLength;
  715. frontLength -= swapLength;
  716. endLength -= swapLength;
  717. }
  718. return a;
  719. }
  720. }