1 /******************************************************************************* 2 3 Collection of utilities to modify arrays and buffers in-place. 4 5 All functions in this module only work on mutable arguments and modify them 6 in place. New memory must never be allocated. 7 8 Based on `tango.core.Array` module from Tango library. 9 10 Copyright: 11 Copyright (C) 2005-2006 Sean Kelly. 12 Some parts copyright (c) 2009-2016 dunnhumby Germany GmbH. 13 All rights reserved. 14 15 License: 16 Tango Dual License: 3-Clause BSD License / Academic Free License v3.0. 17 See LICENSE_TANGO.txt for details. 18 19 *******************************************************************************/ 20 21 module ocean.core.array.Mutation; 22 23 24 import ocean.transition; 25 26 import core.stdc.string; // memmove, memset 27 import core.stdc.math; // fabs; 28 29 import ocean.meta.traits.Basic; 30 import ocean.meta.types.Function; 31 import ocean.meta.AliasSeq; 32 import ocean.core.Buffer; 33 import ocean.core.array.DefaultPredicates; 34 import ocean.core.Verify; 35 36 version (UnitTest) 37 { 38 import ocean.core.Test; 39 import ocean.text.convert.Formatter; 40 } 41 42 /******************************************************************************* 43 44 Performs a linear scan of complete array, replacing occurrences of 45 specified element with the new element. Comparisons will be performed 46 using the supplied predicate or '==' if none is supplied. 47 48 Params: 49 array = The array to scan. 50 element = The pattern to match. 51 new_element = The value to substitute. 52 pred = The evaluation predicate, which should return true if e1 is 53 equal to e2 and false if not. This predicate may be any 54 callable type. 55 56 Returns: 57 The number of elements replaced. 58 59 *******************************************************************************/ 60 61 size_t replace ( T, Pred = DefaultPredicates.IsEqual!(T) ) 62 ( T[] array, in T element, T new_element, Pred pred = Pred.init ) 63 { 64 static assert( isCallableType!(Pred) ); 65 66 size_t cnt = 0; 67 68 foreach( size_t pos, ref T cur; array ) 69 { 70 if( pred( cur, element ) ) 71 { 72 cur = new_element; 73 ++cnt; 74 } 75 } 76 77 return cnt; 78 } 79 80 /// 81 unittest 82 { 83 auto array = "abbbbc".dup; 84 test!("==")(replace(array, 'b', 'x'), 4); 85 test!("==")(array, "axxxxc"); 86 } 87 88 unittest 89 { 90 test!("==")( replace( "gbbbi".dup, 'a', 'b' ), 0 ); 91 test!("==")( replace( "gbbbi".dup, 'g', 'h' ), 1 ); 92 test!("==")( replace( "gbbbi".dup, 'b', 'c' ), 3 ); 93 test!("==")( replace( "gbbbi".dup, 'i', 'j' ), 1 ); 94 test!("==")( replace( "gbbbi".dup, 'd', 'e' ), 0 ); 95 } 96 97 /// ditto 98 size_t replace ( T, Pred = DefaultPredicates.IsEqual!(T) ) 99 ( ref Buffer!(T) array, in T element, T new_element, Pred pred = Pred.init ) 100 { 101 return replace(array[0..array.length], element, new_element, pred); 102 } 103 104 /// 105 unittest 106 { 107 auto buffer = createBuffer("abbbbc"); 108 test!("==")(replace(buffer, 'b', 'x'), 4); 109 test!("==")(buffer[], "axxxxc"); 110 } 111 112 /******************************************************************************* 113 114 Performs a linear scan of the complete array, replacing elements where pred 115 returns true. 116 117 Params: 118 array = The array to scan. 119 new_element = The value to substitute. 120 pred = The evaluation predicate, which should return true if the 121 element is a valid match and false if not. This predicate 122 may be any callable type. 123 124 Returns: 125 The number of elements replaced. 126 127 *******************************************************************************/ 128 129 size_t replaceIf ( T, Pred ) ( T[] array, T new_element, Pred pred ) 130 { 131 static assert( isCallableType!(Pred) ); 132 133 size_t cnt = 0; 134 135 foreach( size_t pos, ref T cur; array ) 136 { 137 if( pred( cur ) ) 138 { 139 cur = new_element; 140 ++cnt; 141 } 142 } 143 return cnt; 144 } 145 146 /// 147 unittest 148 { 149 auto array = "abbc".dup; 150 test!("==")(replaceIf(array, 'x', (char c) { return c > 'a'; }), 3); 151 test!("==")(array, "axxx"); 152 } 153 154 /// ditto 155 size_t replaceIf ( T, Pred ) ( ref Buffer!(T) array, T new_element, Pred pred ) 156 { 157 return replaceIf(array[0..array.length], new_element, pred); 158 } 159 160 /// 161 unittest 162 { 163 auto buffer = createBuffer("abbc"); 164 test!("==")(replaceIf(buffer, 'x', (char c) { return c > 'a'; }), 3); 165 test!("==")(buffer[], "axxx"); 166 } 167 168 unittest 169 { 170 test!("==")( replaceIf( "gbbbi".dup, 'b', ( char c ) { return c == 'a'; } ), 0 ); 171 test!("==")( replaceIf( "gbbbi".dup, 'h', ( char c ) { return c == 'g'; } ), 1 ); 172 test!("==")( replaceIf( "gbbbi".dup, 'c', ( char c ) { return c == 'b'; } ), 3 ); 173 test!("==")( replaceIf( "gbbbi".dup, 'j', ( char c ) { return c == 'i'; } ), 1 ); 174 test!("==")( replaceIf( "gbbbi".dup, 'e', ( char c ) { return c == 'd'; } ), 0 ); 175 } 176 177 /******************************************************************************* 178 179 Performs a linear scan of complete array, moving all items matching 180 element to the end of the sequence. The relative order of items not 181 matching the matching element will be preserved. Comparisons will be 182 performed using the supplied predicate or '==' if none is supplied. 183 184 Params: 185 array = The array to scan. This parameter is not marked 'ref' 186 to allow temporary slices to be modified. As array is not resized 187 in any way, omitting the 'ref' qualifier has no effect on the 188 result of this operation, even though it may be viewed as a 189 side-effect. 190 element = The element value to look for 191 pred = The evaluation predicate, which should return true if e1 is 192 equal to e2 and false if not. This predicate may be any 193 callable type. 194 195 Returns: 196 The number of elements that do not match element. 197 198 *******************************************************************************/ 199 200 size_t moveToEnd ( T, Pred = DefaultPredicates.IsEqual!(T) ) 201 ( T[] array, in T element, Pred pred = Pred.init ) 202 { 203 static assert( isCallableType!(Pred) ); 204 205 // NOTE: Indexes are passed instead of references because DMD does 206 // not inline the reference-based version. 207 void exch( size_t p1, size_t p2 ) 208 { 209 T t = array[p1]; 210 array[p1] = array[p2]; 211 array[p2] = t; 212 } 213 214 size_t cnt = 0; 215 216 for( size_t pos = 0, len = array.length; pos < len; ++pos ) 217 { 218 if( pred( array[pos], element ) ) 219 ++cnt; 220 else 221 exch( pos, pos - cnt ); 222 } 223 return array.length - cnt; 224 } 225 226 /// ditto 227 size_t moveToEnd ( T, Pred = DefaultPredicates.IsEqual!(T) ) 228 ( ref Buffer!(T) array, in T element, Pred pred = Pred.init ) 229 { 230 return moveToEnd(array[0..array.length], element, pred); 231 } 232 233 /// 234 unittest 235 { 236 auto buffer = createBuffer("abbcc"); 237 test!("==")(moveToEnd(buffer, 'b'), 3); 238 test!("==")(buffer[], "accbb"); 239 } 240 241 unittest 242 { 243 static void testOne ( cstring _array, 244 char element, size_t num, int line = __LINE__ ) 245 { 246 auto array = _array.dup; 247 auto t = new NamedTest(format("moveToEnd.testOne:{}", line)); 248 t.test!("==")(moveToEnd(array, element), num); 249 foreach (pos, cur; array) 250 t.test(pos < num ? cur != element : cur == element); 251 } 252 253 testOne("abcdefghij", 'x', 10); 254 testOne("xabcdefghi", 'x', 9); 255 testOne("abcdefghix", 'x', 9); 256 testOne("abxxcdefgh", 'x', 8); 257 testOne("xaxbcdxxex", 'x', 5); 258 } 259 260 /******************************************************************************* 261 262 Performs a linear scan of complete array, removing all elements that satisfy 263 pred from the sequence. The relative order of elements that do not satisfy 264 pred will be preserved. 265 266 Params: 267 array = The array to scan. This parameter is not marked 'ref' 268 to allow temporary slices to be modified. As array is not resized 269 in any way, omitting the 'ref' qualifier has no effect on the 270 result of this operation, even though it may be viewed as a 271 side-effect. 272 pred = The evaluation predicate, which should return true if the 273 element satisfies the condition and false if not. This 274 predicate may be any callable type. 275 276 Returns: 277 The number of elements that do not satisfy pred. 278 279 *******************************************************************************/ 280 281 size_t removeIf ( T, Pred ) ( T[] array, Pred pred ) 282 { 283 static assert( isCallableType!(Pred) ); 284 285 // NOTE: Indexes are passed instead of references because DMD does 286 // not inline the reference-based version. 287 void exch( size_t p1, size_t p2 ) 288 { 289 T t = array[p1]; 290 array[p1] = array[p2]; 291 array[p2] = t; 292 } 293 294 size_t cnt = 0; 295 296 for( size_t pos = 0, len = array.length; pos < len; ++pos ) 297 { 298 if( pred( array[pos] ) ) 299 ++cnt; 300 else 301 exch( pos, pos - cnt ); 302 } 303 return array.length - cnt; 304 } 305 306 /// 307 unittest 308 { 309 auto array = "abbcc".dup; 310 test!("==")(removeIf(array, (char c) { return c == 'b'; }), 3); 311 test!("==")(array, "accbb"); 312 } 313 314 /// ditto 315 size_t removeIf ( T, Pred ) ( ref Buffer!(T) array, Pred pred ) 316 { 317 return removeIf(array[0..array.length], pred); 318 } 319 320 /// 321 unittest 322 { 323 auto buffer = createBuffer("abbcc"); 324 test!("==")(removeIf(buffer, (char c) { return c == 'b'; }), 3); 325 test!("==")(buffer[], "accbb"); 326 } 327 328 unittest 329 { 330 static void testOne ( cstring _array, 331 scope bool delegate(char) dg, size_t num, int line = __LINE__ ) 332 { 333 auto array = _array.dup; 334 auto t = new NamedTest(format("removeIf.testOne:{}", line)); 335 t.test!("==")(removeIf( array, dg ), num); 336 foreach (pos, cur; array) 337 t.test(pos < num ? !dg( cur ) : dg( cur )); 338 } 339 340 testOne("abcdefghij", ( char c ) { return c == 'x'; }, 10); 341 testOne("xabcdefghi", ( char c ) { return c == 'x'; }, 9); 342 testOne("abcdefghix", ( char c ) { return c == 'x'; }, 9); 343 testOne("abxxcdefgh", ( char c ) { return c == 'x'; }, 8); 344 testOne("xaxbcdxxex", ( char c ) { return c == 'x'; }, 5); 345 } 346 347 /******************************************************************************* 348 349 Performs a linear scan of complete array, moving all but the first element 350 of each consecutive group of duplicate elements to the end of the sequence. 351 The relative order of all remaining elements will be preserved. Comparisons 352 will be performed using the supplied predicate or '==' if none is supplied. 353 354 Params: 355 array = The array to scan. This parameter is not marked 'ref' 356 to allow temporary slices to be modified. As array is not resized 357 in any way, omitting the 'ref' qualifier has no effect on the 358 result of this operation, even though it may be viewed as a 359 side-effect. 360 pred = The evaluation predicate, which should return true if e1 is 361 equal to e2 and false if not. This predicate may be any 362 callable type. 363 364 Returns: 365 The number of distinct sub-sequences in array. 366 367 *******************************************************************************/ 368 369 size_t distinct ( T, Pred = DefaultPredicates.IsEqual!(T) ) 370 ( T[] array, Pred pred = Pred.init ) 371 { 372 static assert( isCallableType!(Pred) ); 373 374 // NOTE: Indexes are passed instead of references because DMD does 375 // not inline the reference-based version. 376 void exch( size_t p1, size_t p2 ) 377 { 378 T t = array[p1]; 379 array[p1] = array[p2]; 380 array[p2] = t; 381 } 382 383 if( array.length < 2 ) 384 return array.length; 385 386 size_t cnt = 0; 387 T element = array[0]; 388 389 for( size_t pos = 1, len = array.length; pos < len; ++pos ) 390 { 391 if( pred( array[pos], element ) ) 392 ++cnt; 393 else 394 { 395 element = array[pos]; 396 exch( pos, pos - cnt ); 397 } 398 } 399 return array.length - cnt; 400 } 401 402 /// 403 unittest 404 { 405 auto array = "aabbcdd".dup; 406 auto last = distinct(array); 407 test!("==")(array[0 .. last], "abcd"); 408 } 409 410 /// ditto 411 size_t distinct ( T, Pred = DefaultPredicates.IsEqual!(T) ) 412 ( ref Buffer!(T) array, Pred pred = Pred.init ) 413 { 414 return distinct(array[0..array.length], pred); 415 } 416 417 /// 418 unittest 419 { 420 auto buffer = createBuffer("aabbcdd"); 421 auto last = distinct(buffer); 422 test!("==")(buffer[0 .. last], "abcd"); 423 } 424 425 unittest 426 { 427 test!("==")(distinct("a".dup), 1); 428 429 static void testOne ( cstring _array, 430 cstring expected, int line = __LINE__ ) 431 { 432 auto array = _array.dup; 433 auto t = new NamedTest(format("distinct.testOne:{}", line)); 434 t.test!("==")(distinct(array), expected.length); 435 t.test!("==")(array[0 .. expected.length], expected); 436 } 437 438 testOne("abcdefghij", "abcdefghij"); 439 testOne("aabcdefghi", "abcdefghi"); 440 testOne("bcdefghijj", "bcdefghij"); 441 testOne("abccdefghi", "abcdefghi"); 442 testOne("abccdddefg", "abcdefg"); 443 } 444 445 /******************************************************************************* 446 447 Partitions array such that all elements that satisfy pred will be placed 448 before the elements that do not satisfy pred. The algorithm is not 449 required to be stable. 450 451 Params: 452 array = The array to partition. This parameter is not marked 'ref' 453 to allow temporary slices to be sorted. As array is not resized 454 in any way, omitting the 'ref' qualifier has no effect on 455 the result of this operation, even though it may be viewed 456 as a side-effect. 457 pred = The evaluation predicate, which should return true if the 458 element satisfies the condition and false if not. This 459 predicate may be any callable type. 460 461 Returns: 462 The number of elements that satisfy pred. 463 464 *******************************************************************************/ 465 466 size_t partition ( T, Pred ) ( T[] array, Pred pred ) 467 { 468 static assert( isCallableType!(Pred ) ); 469 470 // NOTE: Indexes are passed instead of references because DMD does 471 // not inline the reference-based version. 472 void exch( size_t p1, size_t p2 ) 473 { 474 T t = array[p1]; 475 array[p1] = array[p2]; 476 array[p2] = t; 477 } 478 479 if( array.length == 0 ) 480 return 0; 481 482 size_t l = 0, 483 r = array.length, 484 i = l, 485 j = r - 1; 486 487 while( true ) 488 { 489 while( i < r && pred( array[i] ) ) 490 ++i; 491 while( j > l && !pred( array[j] ) ) 492 --j; 493 if( i >= j ) 494 break; 495 exch( i++, j-- ); 496 } 497 return i; 498 } 499 500 /// 501 unittest 502 { 503 auto array = "af242df56s2".dup; 504 test!("==")(partition(array, (char c) { return c >= '0' && c <= '9'; }), 6); 505 } 506 507 /// ditto 508 size_t partition ( T, Pred ) ( ref Buffer!(T) array, Pred pred ) 509 { 510 return partition(array[0..array.length], pred); 511 } 512 513 /// 514 unittest 515 { 516 auto buffer = createBuffer("af242df56s2"); 517 test!("==")(partition(buffer, (char c) { return c >= '0' && c <= '9'; }), 6); 518 } 519 520 unittest 521 { 522 test!("==")(partition("".dup, (char c) { return true; }), 0); 523 524 static void testOne ( cstring _array, 525 scope bool delegate(char) dg, size_t num, int line = __LINE__ ) 526 { 527 auto array = _array.dup; 528 auto t = new NamedTest(format("partition.testOne:{}", line)); 529 t.test!("==")(partition(array, dg), num); 530 for ( size_t pos = 0; pos < array.length; ++pos ) 531 t.test( pos < num ? dg( array[pos] ) : !dg( array[pos] ) ); 532 } 533 534 testOne("abcdefg".dup, ( char c ) { return c < 'a'; }, 0); 535 testOne("gfedcba".dup, ( char c ) { return c < 'a'; }, 0); 536 testOne("abcdefg".dup, ( char c ) { return c < 'h'; }, 7); 537 testOne("gfedcba".dup, ( char c ) { return c < 'h'; }, 7); 538 testOne("abcdefg".dup, ( char c ) { return c < 'd'; }, 3); 539 testOne("gfedcba".dup, ( char c ) { return c < 'd'; }, 3); 540 testOne("bbdaabc".dup, ( char c ) { return c < 'c'; }, 5); 541 testOne("f".dup, ( char c ) { return c == 'f'; }, 1); 542 } 543 544 /******************************************************************************* 545 546 Sorts array using the supplied predicate or '<' if none is supplied. The 547 algorithm is not required to be stable. The current implementation is 548 based on quicksort, but uses a three-way partitioning scheme to improve 549 performance for ranges containing duplicate values (Bentley and McIlroy, 550 1993). 551 552 Params: 553 array = The array to sort. This parameter is not marked 'ref' to 554 allow temporary slices to be sorted. As array is not resized 555 in any way, omitting the 'ref' qualifier has no effect on 556 the result of this operation, even though it may be viewed 557 as a side-effect. 558 pred = The evaluation predicate, which should return true if e1 is 559 less than e2 and false if not. This predicate may be any 560 callable type. 561 562 *******************************************************************************/ 563 564 T[] sort ( T, Pred = DefaultPredicates.IsLess!(T) ) 565 ( T[] array, Pred pred = Pred.init ) 566 { 567 static assert( isCallableType!(Pred ) ); 568 569 bool equiv( T p1, T p2 ) 570 { 571 return !pred( p1, p2 ) && !pred( p2, p1 ); 572 } 573 574 // NOTE: Indexes are passed instead of references because DMD does 575 // not inline the reference-based version. 576 void exch( size_t p1, size_t p2 ) 577 { 578 T t = array[p1]; 579 array[p1] = array[p2]; 580 array[p2] = t; 581 } 582 583 // NOTE: This algorithm operates on the inclusive range [l .. r]. 584 void insertionSort( size_t l, size_t r ) 585 { 586 for( size_t i = r; i > l; --i ) 587 { 588 // swap the min element to array[0] to act as a sentinel 589 if( pred( array[i], array[i - 1] ) ) 590 exch( i, i - 1 ); 591 } 592 for( size_t i = l + 2; i <= r; ++i ) 593 { 594 size_t j = i; 595 T v = array[i]; 596 597 // don't need to test (j != l) because of the sentinel 598 while( pred( v, array[j - 1] ) ) 599 { 600 array[j] = array[j - 1]; 601 j--; 602 } 603 array[j] = v; 604 } 605 } 606 607 size_t medianOf( size_t l, size_t m, size_t r ) 608 { 609 if( pred( array[m], array[l] ) ) 610 { 611 if( pred( array[r], array[m] ) ) 612 return m; 613 else 614 { 615 if( pred( array[r], array[l] ) ) 616 return r; 617 else 618 return l; 619 } 620 } 621 else 622 { 623 if( pred( array[r], array[m] ) ) 624 { 625 if( pred( array[r], array[l] ) ) 626 return l; 627 else 628 return r; 629 } 630 else 631 return m; 632 } 633 } 634 635 // NOTE: This algorithm operates on the inclusive range [l .. r]. 636 void quicksort( size_t l, size_t r, size_t d ) 637 { 638 if( r <= l ) 639 return; 640 641 // HEURISTIC: Use insertion sort for sufficiently small arrays. 642 enum { MIN_LENGTH = 80 } 643 if( r - l < MIN_LENGTH ) 644 return insertionSort( l, r ); 645 646 // HEURISTIC: Use the median-of-3 value as a pivot. Swap this 647 // into r so quicksort remains untouched. 648 exch( r, medianOf( l, l + (r - l) / 2, r ) ); 649 650 // This implementation of quicksort improves upon the classic 651 // algorithm by partitioning the array into three parts, one 652 // each for keys smaller than, equal to, and larger than the 653 // partitioning element, v: 654 // 655 // |--less than v--|--equal to v--|--greater than v--[v] 656 // l j i r 657 // 658 // This approach sorts ranges containing duplicate elements 659 // more quickly. During processing, the following situation 660 // is maintained: 661 // 662 // |--equal--|--less--|--[###]--|--greater--|--equal--[v] 663 // l p i j q r 664 // 665 // Please note that this implementation varies from the typical 666 // algorithm by replacing the use of signed index values with 667 // unsigned values. 668 669 T v = array[r]; 670 size_t i = l, 671 j = r, 672 p = l, 673 q = r; 674 675 while( true ) 676 { 677 while( pred( array[i], v ) ) 678 ++i; 679 while( pred( v, array[--j] ) ) 680 if( j == l ) break; 681 if( i >= j ) 682 break; 683 exch( i, j ); 684 if( equiv( array[i], v ) ) 685 exch( p++, i ); 686 if( equiv( v, array[j] ) ) 687 exch( --q, j ); 688 ++i; 689 } 690 exch( i, r ); 691 if( p < i ) 692 { 693 j = i - 1; 694 for( size_t k = l; k < p; k++, j-- ) 695 exch( k, j ); 696 quicksort( l, j, d ); 697 } 698 if( ++i < q ) 699 { 700 for( size_t k = r - 1; k >= q; k--, i++ ) 701 exch( k, i ); 702 quicksort( i, r, d ); 703 } 704 } 705 706 size_t maxDepth( size_t x ) 707 { 708 size_t d = 0; 709 710 do 711 { 712 ++d; 713 x /= 2; 714 } while( x > 1 ); 715 return d * 2; // same as "floor( log( x ) / log( 2 ) ) * 2" 716 } 717 718 if( array.length > 1 ) 719 { 720 quicksort( 0, array.length - 1, maxDepth( array.length ) ); 721 } 722 723 return array; 724 } 725 726 unittest 727 { 728 static void testOne ( cstring _array, int line = __LINE__ ) 729 { 730 auto array = _array.dup; 731 auto t = new NamedTest(format("sort.testOne:{}", line)); 732 sort( array ); 733 for ( ptrdiff_t i = 0; i + 1 < array.length; ++ i ) 734 t.test!(">=")(array[i+1], array[i]); 735 } 736 737 testOne(""); 738 testOne("a"); 739 testOne("mkcvalsidivjoaisjdvmzlksvdjioawmdsvmsdfefewv"); 740 testOne("asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf"); 741 testOne("the quick brown fox jumped over the lazy dog"); 742 testOne("abcdefghijklmnopqrstuvwxyz"); 743 testOne("zyxwvutsrqponmlkjihgfedcba"); 744 745 auto longer = new char[500]; 746 foreach (i, ref c; longer) 747 c = cast(char) (i % 7); 748 testOne(longer); 749 750 auto very_long = new char[100_000]; 751 foreach (i, ref c; very_long) 752 c = cast(char) (i % 34); 753 testOne(very_long); 754 } 755 756 // Test for static arrays 757 unittest 758 { 759 char[2][] arr = [ "EN", "FR", "DE" ]; 760 sort(arr); 761 test!("==")(arr[0], "DE"); 762 test!("==")(arr[1], "EN"); 763 test!("==")(arr[2], "FR"); 764 reverse(arr); 765 test!("==")(arr[2], "DE"); 766 test!("==")(arr[1], "EN"); 767 test!("==")(arr[0], "FR"); 768 } 769 770 /******************************************************************************* 771 772 Swaps elements of argument array so they all come in reverse order 773 774 Params: 775 array = array slice to reverse in-place 776 777 Returns: 778 slice of the argument 779 780 *******************************************************************************/ 781 782 T[] reverse (T) (T[] array) 783 { 784 for (ptrdiff_t i = 0; i < array.length / 2; ++i) 785 { 786 auto tmp = array[i]; 787 array[i] = array[$-i-1]; 788 array[$-i-1] = tmp; 789 } 790 791 return array; 792 } 793 794 /// 795 unittest 796 { 797 test (reverse((int[]).init) == (int[]).init); 798 test (reverse([1, 2, 3]) == [3, 2, 1]); 799 test (reverse([1, 2, 3, 4]) == [4, 3, 2, 1]); 800 } 801 802 //////////////////////////////////////////////////////////////////////////////// 803 // Functions below originally come from ocean.core.Array // 804 //////////////////////////////////////////////////////////////////////////////// 805 806 /******************************************************************************* 807 808 Copies the contents of one element of arrays to another, starting at 809 dest[start], setting the length of the destination array first. 810 Note that start may be greater than the initial length of dest; dest will 811 then be extended appropriately. 812 813 Template params: 814 func = function name for static assertion messages 815 816 Params: 817 dest = reference to the destination array 818 arrays = arrays to copy; a null parameter has the same effect as an 819 empty array. 820 821 Returns: 822 dest 823 824 TODO: Could be made public but must then not rely on elements to be arrays. 825 826 *******************************************************************************/ 827 828 public DE[] append ( DE, T... ) ( ref Buffer!(DE) dest, T arrays ) 829 { 830 size_t appended_length = 0; 831 foreach (array; arrays) 832 appended_length += array.length; 833 dest.reserve(dest.length + appended_length); 834 835 foreach (array; arrays) 836 dest ~= array; 837 838 return dest[]; 839 } 840 841 public DE[] append ( DE, T... ) ( ref DE[] dest, T arrays ) 842 { 843 return append( *(cast(Buffer!(DE)*) &dest), arrays); 844 } 845 846 /// 847 unittest 848 { 849 auto buffer = createBuffer("zero"); 850 append(buffer, "one", "two", "three"); 851 test!("==")(buffer[], "zeroonetwothree"); 852 } 853 854 /******************************************************************************* 855 856 Concatenates a list of arrays into a destination array. The function results 857 in at most a single memory allocation, if the destination array is too small 858 to contain the concatenation results. 859 860 The destination array is passed as a reference, so its length can be 861 modified in-place as required. This avoids any per-element memory 862 allocation, which the normal ~ operator suffers from. 863 864 Template params: 865 T = type of array element 866 867 Params: 868 dest = reference to the destination array 869 arrays = variadic list of arrays to concatenate 870 871 Returns: 872 dest 873 874 ********************************************************************************/ 875 876 public DE[] concat ( DE, T... ) ( ref Buffer!(DE) dest, T arrays ) 877 { 878 dest.reset(); 879 return append(dest, arrays); 880 } 881 882 /// 883 unittest 884 { 885 Buffer!(char) dest; 886 concat(dest, "hello "[], "world"[]); 887 test!("==")(dest[], "hello world"); 888 } 889 890 891 public DE[] concat ( DE, T... ) ( ref DE[] dest, T arrays ) 892 { 893 return concat(*(cast(Buffer!(DE)*) &dest), arrays); 894 } 895 896 unittest 897 { 898 mstring dest; 899 concat(dest, "hello "[], "world"[]); 900 test!("==")(dest, "hello world"); 901 } 902 903 /******************************************************************************* 904 905 Copies the contents of one array to another, setting the length of the 906 destination array first. 907 908 This function is provided as a shorthand for this common operation. 909 910 Template params: 911 T = type of array element 912 913 Params: 914 dest = reference to the destination array 915 array = array to copy; null has the same effect as an empty array 916 917 Returns: 918 dest 919 920 *******************************************************************************/ 921 922 public T[] copy ( T, T2 ) ( ref Buffer!(T) dest, T2[] src ) 923 { 924 static assert (is(typeof({ dest[] = src[]; })), 925 "Type " ~ T2.stringof ~ " cannot be assigned to " ~ T.stringof); 926 927 dest.length = src.length; 928 dest[] = src[]; 929 return dest[]; 930 } 931 932 /// 933 unittest 934 { 935 Buffer!(char) dest; 936 cstring src = "hello"; 937 copy(dest, src); 938 test!("==")(dest[], "hello"[]); 939 } 940 941 unittest 942 { 943 Buffer!(cstring) dest; 944 mstring[] src = [ "Hello".dup, "World".dup ]; 945 copy(dest, src); 946 // Doesn't compile in D2... 947 //test!("==")(dest[], src); 948 } 949 950 public T[] copy ( T, T2 ) ( ref T[] dest, T2[] src ) 951 { 952 return copy(*(cast(Buffer!(T)*) &dest), src); 953 } 954 955 unittest 956 { 957 mstring dest; 958 cstring src = "hello"; 959 copy(dest, src); 960 test!("==")(dest[], "hello"[]); 961 } 962 963 /******************************************************************************* 964 965 Copies the contents of src to dest, increasing dest.length if required. 966 Since dest.length will not be decreased, dest will contain tailing garbage 967 if src.length < dest.length. 968 969 Template params: 970 T = type of array element 971 972 Params: 973 dest = reference to the destination array 974 array = array to copy; null has the same effect as an empty array 975 976 Returns: 977 slice to copied elements in dest 978 979 *******************************************************************************/ 980 981 public T[] copyExtend ( T, T2 ) ( ref Buffer!(T) dest, T2[] src ) 982 { 983 static assert (is(typeof({ dest[] = src[]; })), 984 "Type " ~ T2.stringof ~ " cannot be assigned to " ~ T.stringof); 985 986 if (dest.length < src.length) 987 dest.length = src.length; 988 dest[0 .. src.length] = src[]; 989 return dest[0 .. src.length]; 990 } 991 992 /// 993 unittest 994 { 995 auto dst = createBuffer("aa"); 996 copyExtend(dst, "bbbb"); 997 test!("==")(dst[], "bbbb"); 998 copyExtend(dst, "ccc"); 999 test!("==")(dst[], "cccb"); 1000 } 1001 1002 public T[] copyExtend ( T, T2 ) ( ref T[] dest, T2[] src ) 1003 { 1004 return copyExtend(*(cast(Buffer!(T)*) &dest), src); 1005 } 1006 1007 unittest 1008 { 1009 auto dst = "aa".dup; 1010 copyExtend(dst, "bbbb"); 1011 test!("==")(dst[], "bbbb"); 1012 copyExtend(dst, "ccc"); 1013 test!("==")(dst[], "cccb"); 1014 } 1015 1016 /******************************************************************************* 1017 1018 Appends an element to a list of arrays, and copies the contents of the 1019 passed source array into the new element, setting the length of the 1020 destination array first. 1021 1022 This function is provided as a shorthand for this common operation. 1023 1024 Template params: 1025 T = type of array element 1026 1027 Params: 1028 dest = reference to the destination array list 1029 array = array to copy 1030 1031 Returns: 1032 dest 1033 1034 *******************************************************************************/ 1035 1036 public void appendCopy ( T, T2 ) ( ref Buffer!(T)[] dest, T2[] src ) 1037 { 1038 dest.length = dest.length + 1; 1039 copy(dest[dest.length - 1], src); 1040 } 1041 1042 /// 1043 unittest 1044 { 1045 Buffer!(char)[] dest; 1046 cstring src = "hello"; 1047 appendCopy(dest, src); 1048 test!("==")(dest[0][], "hello"); 1049 } 1050 1051 unittest 1052 { 1053 Buffer!(cstring)[] dest; 1054 mstring[] src = [ "Hello".dup, "World".dup ]; 1055 appendCopy(dest, src); 1056 // Doesn't compile in D2... 1057 //test!("==")(dest[0][], src); 1058 } 1059 1060 1061 public void appendCopy ( T, T2 ) ( ref T[][] dest, T2[] src ) 1062 { 1063 appendCopy(*(cast(Buffer!(T)[]*) &dest), src); 1064 } 1065 1066 /// 1067 unittest 1068 { 1069 mstring[] dest; 1070 cstring src = "hello"; 1071 appendCopy(dest, src); 1072 test!("==")(dest[0][], "hello"); 1073 } 1074 1075 /******************************************************************************* 1076 1077 Removes and returns (via the 'popped' out parameter) the last element in an 1078 array. If the provided array is empty, the function returns false. 1079 1080 Template params: 1081 T = type of array element 1082 1083 Params: 1084 buffer = buffer to pop an element from 1085 popped = popped element (if array contains > 0 elements) 1086 1087 Returns: 1088 true if an element was popped 1089 1090 *******************************************************************************/ 1091 1092 public bool pop ( T ) ( ref Buffer!(T) buffer, out T popped ) 1093 { 1094 if (buffer.length == 0) 1095 return false; 1096 1097 popped = *buffer[buffer.length - 1]; 1098 buffer.length = buffer.length - 1; 1099 return true; 1100 } 1101 1102 /// 1103 unittest 1104 { 1105 auto buffer = createBuffer("something"); 1106 char elem; 1107 test(pop(buffer, elem)); 1108 test!("==")(buffer[], "somethin"[]); 1109 test!("==")(elem, 'g'); 1110 } 1111 1112 public bool pop ( T ) ( ref T[] array, out T popped ) 1113 { 1114 return pop (* cast(Buffer!(T)*) &array, popped); 1115 } 1116 1117 unittest 1118 { 1119 auto buffer = "something".dup; 1120 char elem; 1121 test(pop(buffer, elem)); 1122 test!("==")(buffer, "somethin"[]); 1123 test!("==")(elem, 'g'); 1124 } 1125 1126 /******************************************************************************* 1127 1128 Removes elements from the middle of a buffer, maintaining the order of the 1129 remaining elements by shifting them left using memmove. 1130 1131 Template params: 1132 T = type of buffer element 1133 1134 Params: 1135 buffer = buffer to remove from 1136 index = position in buffer from which to remove elements 1137 remove_elems = number of elements to remove, defaults to one 1138 1139 Returns: 1140 slice of buffer 1141 1142 *******************************************************************************/ 1143 1144 public T[] removeShift ( T ) ( ref Buffer!(T) buffer, size_t index, 1145 size_t remove_elems = 1 ) 1146 { 1147 if ( remove_elems == 0 ) 1148 return buffer[]; 1149 1150 verify(index < buffer.length, "removeShift: index is >= buffer length"); 1151 verify(index + remove_elems - 1 < buffer.length, 1152 "removeShift: end is >= buffer length"); 1153 1154 auto end = index + remove_elems - 1; 1155 auto shift_elems = (buffer.length - end) - 1; 1156 1157 if ( shift_elems ) 1158 { 1159 // shift after elements to the left 1160 void* src = buffer[].ptr + end + 1; 1161 void* dst = buffer[].ptr + index; 1162 size_t num = T.sizeof * shift_elems; 1163 1164 memmove(dst, src, num); 1165 } 1166 1167 // adjust buffer length 1168 buffer.length = buffer.length - remove_elems; 1169 return buffer[]; 1170 } 1171 1172 /// 1173 unittest 1174 { 1175 auto arr = createBuffer("something"); 1176 removeShift(arr, 3, 4); 1177 test!("==")(arr[], "somng"[]); 1178 } 1179 1180 public T[] removeShift ( T ) ( ref T[] buffer, size_t index, 1181 size_t remove_elems = 1 ) 1182 { 1183 return removeShift(*cast(Buffer!(T)*) &buffer, index, remove_elems); 1184 } 1185 1186 unittest 1187 { 1188 mstring arr = "something".dup; 1189 removeShift(arr, 3, 4); 1190 test!("==")(arr, "somng"[]); 1191 } 1192 1193 /******************************************************************************* 1194 1195 Inserts elements into the middle of a buffer, maintaining the order of the 1196 existing elements by shifting them right using memmove. 1197 1198 Template params: 1199 T = type of buffer element 1200 1201 Params: 1202 buffer = buffer to insert into 1203 index = position in buffer at which to insert new elements 1204 insert_elems = number of elements to insert, defaults to one 1205 1206 Returns: 1207 slice of buffer 1208 1209 *******************************************************************************/ 1210 1211 public T[] insertShift ( T ) ( ref Buffer!(T) buffer, size_t index, 1212 size_t insert_elems = 1) 1213 { 1214 verify(index <= buffer.length, "insertShift: index is > buffer length"); 1215 1216 if ( insert_elems == 0 ) 1217 return buffer[]; 1218 1219 auto shift_elems = buffer.length - index; 1220 1221 // adjust buffer length 1222 buffer.length = buffer.length + insert_elems; 1223 1224 // shift required elements one place to the right 1225 if ( shift_elems ) 1226 { 1227 void* src = buffer[].ptr + index; 1228 void* dst = buffer[].ptr + index + insert_elems; 1229 size_t num = T.sizeof * shift_elems; 1230 memmove(dst, src, num); 1231 } 1232 1233 return buffer[]; 1234 } 1235 1236 /// 1237 unittest 1238 { 1239 auto arr = createBuffer("something"); 1240 insertShift(arr, 2, 2); 1241 test!("==")(arr[], "somemething"); 1242 } 1243 1244 public T[] insertShift ( T ) ( ref T[] buffer, size_t index, 1245 size_t insert_elems = 1) 1246 { 1247 return insertShift(* cast(Buffer!(T)*) &buffer, index, insert_elems); 1248 } 1249 1250 /******************************************************************************* 1251 1252 Sorts array and removes all value duplicates. 1253 1254 Template params: 1255 T = type of array element 1256 sort = true: do array.sort first; false: array is already sorted 1257 1258 Params: 1259 array = array to clean from duplicate values 1260 1261 Returns: 1262 result 1263 1264 *******************************************************************************/ 1265 1266 public T[] uniq ( T, bool sort = true ) ( T[] array ) 1267 { 1268 if (array.length) 1269 { 1270 size_t n = 0; 1271 1272 static if (sort) 1273 { 1274 .sort(array); 1275 } 1276 1277 T item = array[n]; 1278 1279 foreach (element; array) 1280 { 1281 if (element != item) 1282 { 1283 array[++n] = element; 1284 item = element; 1285 } 1286 } 1287 1288 return array[0 .. n + 1]; 1289 } 1290 else 1291 { 1292 return array; 1293 } 1294 } 1295 1296 /// 1297 unittest 1298 { 1299 int[] arr = [ 42, 43, 43, 42, 2 ]; 1300 auto slice = uniq(arr); 1301 test!("==")(slice, [ 2, 42, 43 ]); 1302 test!("==")(arr, [ 2, 42, 43, 43, 43 ]); 1303 } 1304 1305 unittest 1306 { 1307 auto buffer = createBuffer([ 42, 43, 43, 42, 2 ]); 1308 auto slice = uniq(buffer[]); 1309 test!("==")(slice, [ 2, 42, 43 ][]); 1310 test!("==")(buffer[], [ 2, 42, 43, 43, 43 ][]); 1311 } 1312 1313 /******************************************************************************* 1314 1315 Sorts array and checks if it contains at least one duplicate. 1316 1317 Template params: 1318 T = type of array element 1319 sort = true: do array.sort first; false: array is already sorted 1320 1321 Params: 1322 array = array to clean from duplicate values 1323 1324 Returns: 1325 true if array contains a duplicate or false if not. Returns false if 1326 array is empty. 1327 1328 *******************************************************************************/ 1329 1330 public bool containsDuplicate ( T, bool sort = true ) ( T[] array ) 1331 { 1332 return !!findDuplicates!(T, sort)( 1333 array, 1334 delegate int(ref size_t index, ref T element) {return true;} 1335 ); 1336 } 1337 1338 /******************************************************************************* 1339 1340 Sorts array and iterates over each array element that compares equal to the 1341 previous element. 1342 1343 To just check for the existence of duplicates it's recommended to make 1344 found() return true (or some other value different from 0) to stop the 1345 iteration after the first duplicate. 1346 1347 To assert array has no duplicates or throw an exception if it has, put the 1348 `assert(false)` or `throw ...` in `found()`: 1349 --- 1350 int[] array; 1351 1352 findDuplicates(array, 1353 (ref size_t index, ref int element) 1354 { 1355 throw new Exception("array contains duplicates"); 1356 return 0; // pacify the compiler 1357 }); 1358 --- 1359 1360 Template params: 1361 T = type of array element 1362 sort = true: do array.sort first; false: array is already sorted 1363 1364 Params: 1365 array = array to clean from duplicate values 1366 found = `foreach`/`opApply()` style delegate, called with the index and 1367 the value of each array element that is equal to the previous 1368 element, returns 0 to continue or a value different from 0 to 1369 stop iteration. 1370 1371 Returns: 1372 - 0 if no duplicates were found so `found()` was not called or 1373 - 0 if `found()` returned 0 on each call or 1374 - the non-zero value returned by `found()` on the last call. 1375 1376 *******************************************************************************/ 1377 1378 public int findDuplicates ( T, bool sort = true ) 1379 ( T[] array, scope int delegate ( ref size_t index, ref T element ) found ) 1380 { 1381 if (array.length) 1382 { 1383 static if (sort) 1384 { 1385 .sort(array); 1386 } 1387 1388 foreach (i, ref element; array[1 .. $]) 1389 { 1390 if (element == array[i]) 1391 { 1392 auto j = i + 1; 1393 if (int x = found(j, element)) 1394 { 1395 return x; 1396 } 1397 } 1398 } 1399 } 1400 1401 return 0; 1402 } 1403 1404 unittest 1405 { 1406 uint n_iterations, n_duplicates; 1407 1408 struct Found 1409 { 1410 int value; 1411 size_t index; 1412 } 1413 1414 Found[8] found; 1415 int[8] array; 1416 alias findDuplicates!(typeof(array[0]), false) fd; 1417 1418 int found_cb ( ref size_t index, ref int element ) 1419 in 1420 { 1421 test(n_iterations); 1422 } 1423 body 1424 { 1425 test(index); 1426 test(index < array.length); 1427 test(array[index] == array[index - 1]); 1428 found[n_duplicates++] = Found(element, index); 1429 return !--n_iterations; 1430 } 1431 1432 array[] = 2; 1433 1434 test(containsDuplicate(array)); 1435 1436 for (uint i = 1; i < array.length; i++) 1437 { 1438 n_iterations = i; 1439 n_duplicates = 0; 1440 int ret = fd(array, &found_cb); 1441 test(ret); 1442 test(n_duplicates == i); 1443 } 1444 1445 n_iterations = array.length; 1446 n_duplicates = 0; 1447 { 1448 int ret = fd(array, &found_cb); 1449 test(!ret); 1450 } 1451 test(n_duplicates == array.length - 1); 1452 1453 array[] = [2, 3, 5, 7, 11, 13, 17, 19]; 1454 1455 test(!containsDuplicate(array)); 1456 1457 n_duplicates = 0; 1458 1459 for (uint i = 1; i <= array.length; i++) 1460 { 1461 n_iterations = i; 1462 int ret = fd(array, &found_cb); 1463 test(!ret); 1464 test(!n_duplicates); 1465 } 1466 1467 n_iterations = array.length; 1468 array[] = 2; 1469 { 1470 n_duplicates = 0; 1471 int ret = fd(array[0 .. 0], &found_cb); 1472 test(!ret); 1473 test(!n_duplicates); 1474 ret = fd(array[0 .. 1], &found_cb); 1475 test(!ret); 1476 test(!n_duplicates); 1477 ret = fd(array[0 .. 2], &found_cb); 1478 test(!ret); 1479 test(n_duplicates == 1); 1480 } 1481 } 1482 1483 /******************************************************************************* 1484 1485 Moves all elements from array which match the exclusion criterum 1486 represented by exclude to the back of array so that the elements that do not 1487 match this criterium are in the front. 1488 1489 array is modified in-place, the order of the elements may change. 1490 1491 exclude is expected to be callable (function or delegate), accepting exactly 1492 one T argument and returning an integer (bool, (u)int, (u)short or (u)long). 1493 It is called with the element in question and should return true if that 1494 element should moved to the back or false if to the front. 1495 1496 Params: 1497 array = array to move values matching the exclusion criterum to the 1498 back 1499 exclude = returns true if the element matches the exclusion criterium 1500 1501 Returns: 1502 the index of the first excluded elements in array. This element and all 1503 following ones matched the exclusion criterum; all elements before it 1504 did not match. 1505 0 indicates that all elements matched the exclusion criterium 1506 and array.length that none matched. 1507 1508 This allows the calling code to keep only the non-excluded items 1509 by calling: `arr.length = filterInPlace(arr, filterFunc);` 1510 1511 Out: 1512 The returned index is at most array.length. 1513 1514 *******************************************************************************/ 1515 1516 public size_t filterInPlace ( T, Exclude ) ( T[] array, Exclude exclude ) 1517 out (end) 1518 { 1519 assert(end <= array.length, "result index out of bounds"); 1520 } 1521 body 1522 { 1523 static assert ( 1524 isCallableType!(Exclude), 1525 "exclude is expected to be callable, not \"" ~ Exclude.stringof ~ '"' 1526 ); 1527 1528 static assert( 1529 is(ParametersOf!(Exclude) == AliasSeq!(T)), 1530 "exclude is expected to accept one " ~ T.stringof ~ " argument" 1531 ); 1532 1533 static assert( 1534 is(ReturnTypeOf!(Exclude) : long), 1535 "the return type of exclude is expected to be an integer type" 1536 ); 1537 1538 return filterInPlaceCore( 1539 array.length, 1540 (size_t i) 1541 { 1542 return !!exclude(array[i]); 1543 }, 1544 (size_t i, size_t j) 1545 { 1546 typeid(T).swap(&array[i], &array[j]); 1547 } 1548 ); 1549 } 1550 1551 unittest 1552 { 1553 auto arr = "something".dup; 1554 filterInPlace(arr, (char c) { return c / 2; }); 1555 } 1556 1557 /******************************************************************************* 1558 1559 Moves all elements in an array which match the exclusion criterum 1560 represented by exclude to the back of array so that the elements that do not 1561 match this criterium are in the front. 1562 1563 array is modified in-place, the order of the elements may change. 1564 1565 exclude is called with the index of the element in question and should 1566 return true if array[index] should moved to the back or false if to the 1567 front. At the time exclude is called, the order of the array elements may 1568 have changed so exclude should index the same array instance this function 1569 is working on (i.e. not a copy). 1570 1571 Params: 1572 length = array length 1573 exclude = returns true if array)[index] matches the exclusion 1574 criterium 1575 swap = swaps array[i] and array[j] 1576 1577 Returns: 1578 the index of the first excluded elements in the array. This element 1579 and all following ones matched the exclusion criterum; all elements 1580 before it did not match. 1581 length indicates that all elements matched the exclusion criterium and 1582 0 that none matched. 1583 1584 *******************************************************************************/ 1585 1586 private size_t filterInPlaceCore ( size_t length, 1587 scope bool delegate ( size_t index ) exclude, 1588 scope void delegate ( size_t i, size_t j ) swap ) 1589 out (end) 1590 { 1591 assert(end <= length, "result length out of bounds"); 1592 } 1593 body 1594 { 1595 for (size_t i = 0; i < length; i++) 1596 { 1597 if (exclude(i)) 1598 { 1599 length--; 1600 1601 while (length > i) 1602 { 1603 if (exclude(length)) 1604 { 1605 length--; 1606 } 1607 else 1608 { 1609 swap(i, length); 1610 break; 1611 } 1612 } 1613 } 1614 } 1615 1616 return length; 1617 } 1618 1619 /******************************************************************************/ 1620 1621 unittest 1622 { 1623 uint[] array = [2, 3, 5, 8, 13, 21, 34, 55, 89, 144]; 1624 size_t end; 1625 1626 /*************************************************************************** 1627 1628 Returns true if array[0 .. end] contains n or false if not. 1629 1630 ***************************************************************************/ 1631 1632 bool inIncluded ( uint n ) 1633 { 1634 foreach (element; array[0 .. end]) 1635 { 1636 if (element == n) return true; 1637 } 1638 1639 return false; 1640 } 1641 1642 /*************************************************************************** 1643 1644 Returns true if array[end .. $] contains n or false if not. 1645 1646 ***************************************************************************/ 1647 1648 bool inExcluded ( uint n ) 1649 { 1650 foreach (element; array[end .. $]) 1651 { 1652 if (element == n) return true; 1653 } 1654 1655 return false; 1656 } 1657 1658 /*************************************************************************** 1659 1660 Returns true n is even or false if n is odd. 1661 1662 ***************************************************************************/ 1663 1664 bool even ( uint n ) 1665 { 1666 return !(n & 1); 1667 } 1668 1669 end = .filterInPlace(array, &even); 1670 test(end == 6); 1671 test(inIncluded(3)); 1672 test(inIncluded(5)); 1673 test(inIncluded(13)); 1674 test(inIncluded(21)); 1675 test(inIncluded(55)); 1676 test(inIncluded(89)); 1677 test(inExcluded(2)); 1678 test(inExcluded(8)); 1679 test(inExcluded(34)); 1680 test(inExcluded(144)); 1681 1682 array = [2, 4, 6]; 1683 end = .filterInPlace(array, &even); 1684 test(!end); 1685 test(inExcluded(2)); 1686 test(inExcluded(4)); 1687 test(inExcluded(6)); 1688 1689 array = [8]; 1690 end = .filterInPlace(array, &even); 1691 test(!end); 1692 test(array[end] == 8); 1693 1694 array = [12345]; 1695 end = .filterInPlace(array, &even); 1696 test(end == array.length); 1697 test(array[0] == 12345); 1698 1699 array = [1, 2, 4, 6]; 1700 end = .filterInPlace(array, &even); 1701 test(end == 1); 1702 test(array[0] == 1); 1703 test(inExcluded(2)); 1704 test(inExcluded(4)); 1705 test(inExcluded(6)); 1706 1707 array = [1, 3, 5, 7]; 1708 end = .filterInPlace(array, &even); 1709 test(end == array.length); 1710 test(inIncluded(1)); 1711 test(inIncluded(3)); 1712 test(inIncluded(5)); 1713 test(inIncluded(7)); 1714 1715 array = [1, 2, 5, 7]; 1716 end = .filterInPlace(array, &even); 1717 test(end == 3); 1718 test(inIncluded(1)); 1719 test(inIncluded(5)); 1720 test(inIncluded(7)); 1721 test(inExcluded(2)); 1722 } 1723 1724 /******************************************************************************* 1725 1726 Shuffles the elements of array in-place. 1727 1728 Params: 1729 array = array with elements to shuffle 1730 rand = random number generator whose generated numbers must have range 1731 ]-1..0] or [0..1[, will be invoked array.length - 1 times 1732 1733 Returns: 1734 shuffled array 1735 1736 *******************************************************************************/ 1737 1738 public T[] shuffle ( T ) ( T[] array, lazy double rand ) 1739 { 1740 auto result = shuffle( 1741 array, 1742 (size_t i) { return cast(size_t) (fabs(rand) * (i + 1)); } 1743 ); 1744 return result; 1745 } 1746 1747 /// 1748 unittest 1749 { 1750 int[] arr = [ 1, 2, 3, 4 ]; 1751 auto random_generator = () { return 0.42; }; // not proven by the dice roll 1752 shuffle(arr, random_generator()); 1753 } 1754 1755 /******************************************************************************* 1756 1757 Shuffles the elements of array in-place. 1758 1759 Params: 1760 array = array with elements to shuffle 1761 new_index = returns the new index for the array element whose index is 1762 currently i. i is guaranteed to be in the range 1763 [1 .. array.length - 1]; the returned index should be in the 1764 range [0 .. i] and must be in range [0 .. array.length - 1]. 1765 1766 Returns: 1767 shuffled array 1768 1769 *******************************************************************************/ 1770 1771 public T[] shuffle ( T ) ( T[] array, scope size_t delegate ( size_t i ) new_index ) 1772 { 1773 for (auto i = array.length? array.length - 1 : 0; i; i--) 1774 { 1775 auto j = new_index(i); 1776 auto tmp = array[i]; 1777 array[i] = array[j]; 1778 array[j] = tmp; 1779 } 1780 1781 return array; 1782 } 1783 1784 /// 1785 unittest 1786 { 1787 int[] arr = [ 1, 2, 3, 4 ]; 1788 int[] orig = arr.dup; 1789 auto modified = shuffle(arr, (size_t i) { return i; }); 1790 test!("==")(modified, orig); 1791 } 1792 1793 /****************************************************************************** 1794 1795 Resets each elements of array to its initial value. 1796 1797 T.init must consist only of zero bytes. 1798 1799 Params: 1800 array = array to clear elements 1801 1802 Returns: 1803 array with cleared elements 1804 1805 ******************************************************************************/ 1806 1807 public T[] clear ( T ) ( T[] array ) 1808 { 1809 verify(isClearable!(T), T.stringof ~ ".init contains a non-zero byte so " ~ 1810 (T[]).stringof ~ " cannot be simply cleared"); 1811 1812 memset(array.ptr, 0, array.length * array[0].sizeof); 1813 1814 return array; 1815 } 1816 1817 unittest 1818 { 1819 auto arr = [ 1, 2, 3 ]; 1820 clear(arr); 1821 } 1822 1823 /****************************************************************************** 1824 1825 Checks if T.init consists only of zero bytes so that a T[] array can be 1826 cleared by clear(). 1827 1828 Returns: 1829 true if a T[] array can be cleared by clear() or false if not. 1830 1831 ******************************************************************************/ 1832 1833 bool isClearable ( T ) ( ) 1834 { 1835 static immutable size_t n = T.sizeof; 1836 1837 T init; 1838 1839 ubyte[n] zero_data; 1840 1841 return (cast (void*) &init)[0 .. n] == zero_data; 1842 } 1843 1844 unittest 1845 { 1846 auto x = isClearable!(double); 1847 } 1848 1849 /******************************************************************************* 1850 1851 Selects the kth element of an array as defined by the given predicate. 1852 1853 Notes: 1854 -> Uses the Quickselect selection algorithm 1855 (http://en.wikipedia.org/wiki/Quickselect) 1856 -> Typically, Quickselect is used to select the kth smallest element 1857 of an array, but this function can also be used to select elements 1858 ordered in any fashion, as defined by the given predicate. 1859 -> The array elements may be reordered during the selection process. 1860 -> The following would be true if the selection happens successfully 1861 (i.e. if no exception is thrown): 1862 * the kth element in the array as defined by the given 1863 predicate would have been moved to index 'k'. 1864 * All elements before index 'k' are guaranteed to be passed by 1865 the predicate compared to element 'k' (i.e. the predicate 1866 would return true) whereas all elements after index 'k' are 1867 guaranteed to not be passed by the predicate compared to 1868 element 'k' (i.e. the predicate would return false). This 1869 means that, if the kth smallest element is being selected, 1870 then all elements before the kth smallest element would be 1871 lesser than it and all elements after the kth smallest 1872 element would be greater than or equal to it. However, no 1873 guarantees are made about the sorting order of the elements 1874 before/after the kth smallest element. In other words, 1875 unordered partial sorting of the array will take place. 1876 -> The result is defined only if all values in the array are ordered 1877 (unordered values like floating-point NaNs could result in 1878 undefined behaviour). 1879 1880 Params: 1881 arr = the array being worked upon. 1882 k = the order statistic being looked for (starting from zero). In 1883 particular, there should be 'k' elements in the array for which 1884 the predicate will return true. 1885 pred = predicate used for array element comparisons. Takes two array 1886 elements to be compared and returns true/false based upon the 1887 comparison. 1888 (defaults to a comparison using "<" if no predicate is given) 1889 1890 Returns: 1891 the index of the kth element in the array as defined by the ordering 1892 predicate. 1893 1894 Throws: 1895 new Exception if input array is empty. 1896 1897 *******************************************************************************/ 1898 1899 size_t select ( T, Pred = DefaultPredicates.IsLess!(T) ) 1900 ( T[] arr, size_t k, Pred pred = Pred.init ) 1901 { 1902 /* For best results, i.e. to achieve O(n) performance from 1903 * Quickselect, it is recommended to choose a random initial pivot 1904 * value. But for now, for the sake of simplicity, the selection 1905 * function is called with its default value of zero which causes 1906 * the first value in the array to be treated as the initial pivot 1907 * value. */ 1908 1909 quickselect(arr, k, pred); 1910 1911 if ( k >= arr.length ) 1912 { 1913 k = arr.length - 1; 1914 } 1915 1916 return k; 1917 } 1918 1919 /******************************************************************************* 1920 1921 Same as `select` with two changes: 1922 1923 1. An additional input parameter: 1924 initial_pivot_index = the array index at which the first pivot 1925 value can be found. In theory, the pivot 1926 value can be chosen at random, but 1927 making an intelligent first guess close 1928 to the expected kth smallest (or 1929 largest) element is a good idea as that 1930 reduces the number of iterations needed 1931 to close-in on the target value 1932 (defaults to zero). 1933 2. The return value: 1934 returns the kth element in the array as defined by the 1935 ordering predicate. 1936 1937 *******************************************************************************/ 1938 1939 T quickselect ( T, Pred = DefaultPredicates.IsLess!(T) ) 1940 ( T[] arr, size_t k, Pred pred = Pred.init, size_t initial_pivot_index = 0 ) 1941 { 1942 static assert (isCallableType!(Pred)); 1943 1944 /* 1945 * Partitions a range of elements within an array based on a pivot 1946 * value. 1947 * 1948 * At the end of the partition function in a typical Quickselect 1949 * example, the range of elements being worked upon would be 1950 * modified such that all elements before the pivot element will be 1951 * smaller than it, while all elements after the pivot element will 1952 * be greater than it. This function is capable of this typical 1953 * behaviour as well as the opposite behaviour depending upon the 1954 * given predicate. 1955 * 1956 * Params: 1957 * arr = array being worked upon. 1958 * left = leftmost index of the range of elements to 1959 * partition. 1960 * right = rightmost index of the range of elements to 1961 * partition. 1962 * pivot_index = index of the pivot value to be used. 1963 * pred = predicate used for array element comparisons. 1964 * Takes two array elements to be compared and 1965 * returns true/false based upon the comparison. 1966 * 1967 * Returns: 1968 * the new index to which the pivot value has moved. 1969 */ 1970 1971 static size_t partition( T[] arr, size_t left, size_t right, 1972 size_t pivot_index, Pred pred ) 1973 { 1974 if( left >= right ) 1975 { 1976 return left; 1977 } 1978 1979 if( pivot_index < left || pivot_index > right ) 1980 { 1981 pivot_index = left; 1982 } 1983 1984 auto pivot_value = arr[pivot_index]; 1985 1986 // Move the pivot value to the last index in the current range. 1987 1988 if( pivot_index != right ) 1989 { 1990 /* Note that the following line will result in a 1991 * compile-time error in D1 if 'Elem' is a static array. 1992 * This is because D1 does not allow assignment to a static 1993 * array. */ 1994 1995 arr[pivot_index] = arr[right]; 1996 arr[right] = pivot_value; 1997 } 1998 1999 auto store_index = left; 2000 2001 for( auto i = left; i < right; ++i ) 2002 { 2003 if( pred(arr[i], pivot_value) ) 2004 { 2005 if( i != store_index ) 2006 { 2007 typeid(T).swap(&arr[i], &arr[store_index]); 2008 } 2009 2010 ++store_index; 2011 } 2012 } 2013 2014 // Move the pivot value from the last index to its rightful 2015 // place. 2016 2017 if( store_index != right ) 2018 { 2019 arr[right] = arr[store_index]; 2020 arr[store_index] = pivot_value; 2021 } 2022 2023 return store_index; 2024 } 2025 2026 if( arr.length == 0 ) 2027 { 2028 throw new Exception("Zero-length arrays are not supported"); 2029 } 2030 2031 if( arr.length == 1 ) 2032 { 2033 return arr[0]; 2034 } 2035 2036 /* The initial pivot index must be a valid array index. */ 2037 2038 if( initial_pivot_index >= arr.length ) 2039 { 2040 initial_pivot_index = 0; 2041 } 2042 2043 /* One cannot have "the fifth largest element in an array" if the 2044 * array contains only three elements. In such a case, 'k' is set to 2045 * the largest valid index of the array. */ 2046 2047 if( k >= arr.length ) 2048 { 2049 k = arr.length - 1; 2050 } 2051 2052 /* 2053 * Important Note: 2054 * --------------- 2055 * This function uses 'left' and 'right' markers to define a 2056 * range of elements in the array being searched. At first 2057 * glance, this may seem unnecessary as D's slicing operations 2058 * could be used to inspect a range of elements in an array. 2059 * However, the QuickSelect algorithm works in such a way that 2060 * the kth element in the array will move to index 'k' of the 2061 * original array at the end of computation. This makes it 2062 * necessary for us to maintain the original length of the 2063 * array, and not use slices. 2064 */ 2065 2066 /* The selection process works on a constantly reducing range of 2067 * elements within the given array, with the search range being 2068 * halved in each iteration. To begin with, the range is set to the 2069 * entire array. */ 2070 2071 size_t left = 0; 2072 size_t right = arr.length - 1; 2073 2074 size_t pivot_index = initial_pivot_index; 2075 2076 while( 1 ) 2077 { 2078 pivot_index = partition(arr, left, right, pivot_index, pred); 2079 2080 if( pivot_index == k ) 2081 { 2082 /* The kth element in the array is the current pivot. */ 2083 2084 break; 2085 } 2086 else if( pivot_index > k ) 2087 { 2088 /* The kth element is before the current pivot, so restrict 2089 * further searching to the first half of the current range 2090 * by shortening the range from the right. */ 2091 2092 right = pivot_index - 1; 2093 pivot_index = right; 2094 } 2095 else 2096 { 2097 /* The kth element is after the current pivot, so restrict 2098 * further searching to the second half of the current range 2099 * by shortening the range from the left. */ 2100 2101 left = pivot_index + 1; 2102 pivot_index = left; 2103 } 2104 } 2105 2106 return arr[pivot_index]; 2107 } 2108 2109 version (UnitTest) 2110 { 2111 void verifySelect ( T, istring file = __FILE__, int line = __LINE__ ) 2112 ( T[] buf, size_t k, T expected_value ) 2113 { 2114 auto t = new NamedTest(file ~ ":" ~ line.stringof); 2115 auto kth_element_index = select(buf, k); 2116 2117 t.test!("<")(kth_element_index, buf.length); 2118 t.test!("==")(buf[kth_element_index], expected_value); 2119 2120 /* Confirm that unordered partial sorting of the array has also 2121 * happened as expected. */ 2122 2123 if( k > buf.length ) 2124 k = buf.length; 2125 2126 foreach (cur; buf[0 .. k]) 2127 t.test!("<=")(cur, expected_value); 2128 2129 foreach (cur; buf[k .. $]) 2130 test!(">=")(cur, expected_value); 2131 } 2132 2133 } 2134 2135 unittest 2136 { 2137 testThrown!(Exception)(select((int[]).init, 0)); 2138 2139 verifySelect("efedcaabca".dup, 5, 'c'); 2140 2141 verifySelect(['x'], 0, 'x'); 2142 verifySelect([42], 10, 42); 2143 2144 verifySelect([7, 3, 4, 1, 9], 0, 1); 2145 verifySelect([7, 3, 4, 1, 9], 1, 3); 2146 verifySelect([7, 3, 4, 1, 9], 2, 4); 2147 verifySelect([7, 3, 4, 1, 9], 3, 7); 2148 verifySelect([7, 3, 4, 1, 9], 4, 9); 2149 verifySelect([7, 3, 4, 1, 9], 5, 9); 2150 2151 verifySelect([7.4, 3.57, 4.2, 1.23, 3.56], 1, 3.56); 2152 2153 struct Person 2154 { 2155 istring name; 2156 int age; 2157 } 2158 2159 Person a = Person("Gautam", 18); 2160 Person b = Person("Tom", 52); 2161 Person c = Person("George", 53); 2162 Person d = Person("Arnold", 67); 2163 2164 bool person_age_predicate( Person p1, Person p2 ) 2165 { 2166 return p1.age < p2.age; 2167 } 2168 2169 Person[] buf = [a, b, c, d]; 2170 2171 auto kth_element_index = select(buf, 0, &person_age_predicate); 2172 test!("==")(buf[kth_element_index].name, "Gautam"); 2173 2174 kth_element_index = select(buf, 2, &person_age_predicate); 2175 test!("==")(buf[kth_element_index].name, "George"); 2176 2177 test!("==")(quickselect([7, 3, 4, 1, 9][], 1), 3); 2178 2179 bool greater_than_predicate( int a, int b ) 2180 { 2181 return a > b; 2182 } 2183 2184 test!("==")(quickselect([7, 3, 4, 1, 9][], 1, &greater_than_predicate), 7); 2185 test!("==")(quickselect([7, 3, 4, 1][], 1, &greater_than_predicate, 2), 4); 2186 }