1 /******************************************************************************* 2 3 Collection of utilities to search within arrays and buffers. 4 5 All functions in this module must never mutate their arguments and must 6 never allocate new memory each call. Some may keep internal static GC 7 buffers if required by algorithm. 8 9 Based on `tango.core.Array` module from Tango library. 10 11 Copyright: 12 Copyright (C) 2005-2006 Sean Kelly. 13 Some parts copyright (c) 2009-2016 dunnhumby Germany GmbH. 14 All rights reserved. 15 16 License: 17 Tango Dual License: 3-Clause BSD License / Academic Free License v3.0. 18 See LICENSE_TANGO.txt for details. 19 20 21 *******************************************************************************/ 22 23 module ocean.core.array.Search; 24 25 26 import ocean.transition; 27 28 import ocean.stdc.posix.sys.types; // ssize_t; 29 30 import ocean.meta.traits.Basic; 31 import ocean.core.Buffer; 32 import ocean.core.array.DefaultPredicates; 33 import ocean.core.Verify; 34 35 version (UnitTest) 36 { 37 import ocean.core.Test; 38 } 39 40 /******************************************************************************* 41 42 Linear search in an array 43 44 Performs a linear scan of haystack from [0 .. haystack.length$(RP), returning 45 the index of the first element matching needle, or haystack.length if no match 46 was found. Comparisons will be performed using the supplied predicate 47 or '==' if none is supplied. 48 49 Params: 50 haystack = The array to search. 51 needle = The needletern to search for, either sub-array or element 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 callable 54 type. 55 56 Returns: 57 The index of the first match or haystack.length if no match was found. 58 59 *******************************************************************************/ 60 61 size_t find ( T, Pred = DefaultPredicates.IsEqual!(T) ) 62 ( in T[] haystack, in T needle, Pred pred = Pred.init ) 63 { 64 static assert (isCallableType!(Pred)); 65 66 foreach ( pos, cur; haystack ) 67 { 68 if( pred( cur, needle ) ) 69 return pos; 70 } 71 72 return haystack.length; 73 } 74 75 /// 76 unittest 77 { 78 test!("==")(find( "abc", 'b' ), 1); 79 } 80 81 unittest 82 { 83 test!("==")(find( "", 'a' ), 0); 84 test!("==")(find( "abc", 'a' ), 0); 85 test!("==")(find( "abc", 'b' ), 1); 86 test!("==")(find( "abc", 'c' ), 2); 87 test!("==")(find( "abc", 'd' ), 3); 88 } 89 90 /// ditto 91 size_t find ( T, Pred = DefaultPredicates.IsEqual!(T) ) 92 ( in T[] haystack, in T[] needle, Pred pred = Pred.init ) 93 { 94 static assert (isCallableType!(Pred)); 95 96 if( haystack.length == 0 || 97 needle.length == 0 || 98 haystack.length < needle.length ) 99 { 100 return haystack.length; 101 } 102 103 size_t end = haystack.length - needle.length + 1; 104 105 for( size_t pos = 0; pos < end; ++pos ) 106 { 107 if( pred( haystack[pos], needle[0] ) ) 108 { 109 size_t mat = 0; 110 111 do 112 { 113 if( ++mat >= needle.length ) 114 return pos - needle.length + 1; 115 ++pos; 116 assert (pos < haystack.length); 117 } while( pred( haystack[pos], needle[mat] ) ); 118 pos -= mat; 119 } 120 } 121 return haystack.length; 122 } 123 124 /// 125 unittest 126 { 127 test!("==")(find( "abc", "bc" ), 1); 128 test!("==")(find( "abcd", "cc" ), 4); 129 } 130 131 /// ditto 132 size_t find ( T, Pred = DefaultPredicates.IsEqual!(T) ) 133 ( ref Buffer!(T) haystack, in T[] needle, Pred pred = Pred.init ) 134 { 135 return find(haystack[], needle, pred); 136 } 137 138 /// 139 unittest 140 { 141 auto buffer = createBuffer([ 1, 2, 3 ]); 142 test!("==")(find(buffer, [ 2, 3 ]), 1); 143 } 144 145 /// ditto 146 size_t find ( T, Pred = DefaultPredicates.IsEqual!(T) ) 147 ( ref Buffer!(T) haystack, in T needle, Pred pred = Pred.init ) 148 { 149 return find(haystack[], needle, pred); 150 } 151 152 /// 153 unittest 154 { 155 auto buffer = createBuffer([ 1, 2, 3 ]); 156 test!("==")(find(buffer, 3), 2); 157 } 158 159 unittest 160 { 161 // null parameters 162 test!("==")(find( "", ""[] ), 0); 163 test!("==")(find( "a", ""[] ), 1); 164 test!("==")(find( "", "a"[] ), 0); 165 166 // exact match 167 test!("==")(find( "abc", "abc"[] ), 0); 168 169 // simple substring match 170 test!("==")(find( "abc", "a"[] ), 0); 171 test!("==")(find( "abca", "a"[] ), 0); 172 test!("==")(find( "abc", "b"[] ), 1); 173 test!("==")(find( "abc", "c"[] ), 2); 174 test!("==")(find( "abc", "d"[] ), 3); 175 176 // multi-char substring match 177 test!("==")(find( "abc", "ab"[] ), 0); 178 test!("==")(find( "abcab", "ab"[] ), 0); 179 test!("==")(find( "abc", "bc"[] ), 1); 180 test!("==")(find( "abc", "ac"[] ), 3); 181 test!("==")(find( "abrabracadabra", "abracadabra"[] ), 3); 182 183 // different qualifiers 184 mstring s = "abcd".dup; 185 test!("==")(find(s, "bc"[]), 1); 186 187 // custom predicate 188 bool foo(char a, char b) 189 { 190 return a == 'x'; 191 } 192 193 test!("==")(find( "abcdxa", 'b', &foo ), 4); 194 test!("==")(find( "abcdxa", "b"[], &foo ), 4); 195 } 196 197 /******************************************************************************* 198 199 Performs a linear scan of haystack from $(LP)haystack.length .. 0], returning 200 the index of the first element matching needle, or haystack.length if no match 201 was found. Comparisons will be performed using the supplied predicate 202 or '==' if none is supplied. 203 204 Params: 205 haystac = The array to search. 206 needle = The needletern to search for. 207 pred = The evaluation predicate, which should return true if e1 is 208 equal to e2 and false if not. This predicate may be any 209 callable type. 210 211 Returns: 212 The index of the first match or haystack.length if no match was found. 213 214 *******************************************************************************/ 215 216 size_t rfind (T, Pred = DefaultPredicates.IsEqual!(T)) 217 ( in T[] haystack, in T needle, Pred pred = Pred.init ) 218 { 219 static assert ( isCallableType!(Pred) ); 220 221 if( haystack.length == 0 ) 222 return haystack.length; 223 224 size_t pos = haystack.length; 225 226 do 227 { 228 if( pred( haystack[--pos], needle ) ) 229 return pos; 230 } while( pos > 0 ); 231 return haystack.length; 232 } 233 234 /// 235 unittest 236 { 237 test!("==")(rfind([ 1, 2, 3 ], 1), 0); 238 } 239 240 unittest 241 { 242 // rfind element 243 test!("==")( rfind( "", 'a' ), 0 ); 244 test!("==")( rfind( "abc", 'a' ), 0 ); 245 test!("==")( rfind( "abc", 'b' ), 1 ); 246 test!("==")( rfind( "abc", 'c' ), 2 ); 247 test!("==")( rfind( "abc", 'd' ), 3 ); 248 } 249 250 /// ditto 251 size_t rfind ( T, Pred = DefaultPredicates.IsEqual!(T) ) 252 ( in T[] haystack, in T[] needle, Pred pred = Pred.init ) 253 { 254 static assert (isCallableType!(Pred) ); 255 256 if( haystack.length == 0 || 257 needle.length == 0 || 258 haystack.length < needle.length ) 259 { 260 return haystack.length; 261 } 262 263 size_t pos = haystack.length - needle.length + 1; 264 265 do 266 { 267 if( pred( haystack[--pos], needle[0] ) ) 268 { 269 size_t mat = 0; 270 271 do 272 { 273 if( ++mat >= needle.length ) 274 return pos - needle.length + 1; 275 ++pos; 276 assert (pos < haystack.length); 277 } while( pred( haystack[pos], needle[mat] ) ); 278 pos -= mat; 279 } 280 } while( pos > 0 ); 281 return haystack.length; 282 } 283 284 /// 285 unittest 286 { 287 test!("==")(rfind([ 1, 2, 3 ], [ 1, 2 ]), 0); 288 } 289 290 /// ditto 291 size_t rfind ( T, Pred = DefaultPredicates.IsEqual!(T) ) 292 ( ref Buffer!(T) haystack, in T[] needle, Pred pred = Pred.init ) 293 { 294 return rfind(haystack[], needle, pred); 295 } 296 297 /// 298 unittest 299 { 300 auto buffer = createBuffer([ 1, 2, 3 ]); 301 test!("==")(rfind(buffer, 1), 0); 302 } 303 304 /// ditto 305 size_t rfind ( T, Pred = DefaultPredicates.IsEqual!(T) ) 306 ( ref Buffer!(T) haystack, in T needle, Pred pred = Pred.init ) 307 { 308 return rfind(haystack[], needle, pred); 309 } 310 311 /// 312 unittest 313 { 314 auto buffer = createBuffer([ 1, 2, 3 ]); 315 test!("==")(rfind(buffer, [ 2, 3 ]), 1); 316 } 317 318 unittest 319 { 320 // null parameters 321 test!("==")(rfind( "", ""[] ), 0 ); 322 test!("==")(rfind( "a", ""[] ), 1 ); 323 test!("==")(rfind( "", "a"[] ), 0 ); 324 325 // exact match 326 test!("==")(rfind( "abc", "abc"[] ), 0 ); 327 328 // simple substring match 329 test!("==")(rfind( "abc", "a"[] ), 0 ); 330 test!("==")(rfind( "abca", "a"[] ), 3 ); 331 test!("==")(rfind( "abc", "b"[] ), 1 ); 332 test!("==")(rfind( "abc", "c"[] ), 2 ); 333 test!("==")(rfind( "abc", "d"[] ), 3 ); 334 335 // multi-char substring match 336 test!("==")(rfind( "abc", "ab"[] ), 0 ); 337 test!("==")(rfind( "abcab", "ab"[] ), 3 ); 338 test!("==")(rfind( "abc", "bc"[] ), 1 ); 339 test!("==")(rfind( "abc", "ac"[] ), 3 ); 340 test!("==")(rfind( "abracadabrabra", "abracadabra"[] ), 0 ); 341 342 // custom predicate 343 bool foo(char a, char b) 344 { 345 return a == 'x'; 346 } 347 348 test!("==")(rfind( "axcdxa", 'b', &foo ), 4 ); 349 test!("==")(rfind( "axcdxa", "b"[], &foo ), 4 ); 350 351 } 352 353 /******************************************************************************* 354 355 Performs a linear scan of haystack from [0 .. haystack.length$(RP), returning 356 the index of the first element matching needle, or haystack.length if no match 357 was found. Comparisons will be performed using the supplied predicate 358 or '==' if none is supplied. 359 360 This function uses the KMP algorithm and offers O(M+N) performance but 361 must allocate a temporary buffer of size needle.sizeof to do so. If it is 362 available on the target system, alloca will be used for the allocation, 363 otherwise a standard dynamic memory allocation will occur. 364 365 Params: 366 haystack = The array to search. 367 needle = The pattern to search for. 368 pred = The evaluation predicate, which should return true if e1 is 369 equal to e2 and false if not. This predicate may be any 370 callable type. 371 372 Returns: 373 The index of the first match or haystack.length if no match was found. 374 375 *******************************************************************************/ 376 377 size_t kfind (T, Pred = DefaultPredicates.IsEqual!(T)) 378 ( in T[] haystack, in T needle, Pred pred = Pred.init ) 379 { 380 return find(haystack, needle, pred); 381 } 382 383 /// 384 unittest 385 { 386 test!("==")(kfind( "abc", 'b' ), 1); 387 } 388 389 /// ditto 390 size_t kfind (T, Pred = DefaultPredicates.IsEqual!(T)) 391 ( in T[] haystack, in T[] needle, Pred pred = Pred.init ) 392 { 393 static assert (isCallableType!(Pred)); 394 395 if( haystack.length == 0 || 396 needle.length == 0 || 397 haystack.length < needle.length ) 398 { 399 return haystack.length; 400 } 401 402 static Buffer!(size_t) func; 403 func.length = needle.length + 1; 404 405 func[0] = 0; 406 407 // building prefix-function 408 for( size_t m = 0, i = 1 ; i < needle.length ; ++i ) 409 { 410 while( ( m > 0 ) && !pred( needle[m], needle[i] ) ) 411 m = *func[m - 1]; 412 if( pred( needle[m], needle[i] ) ) 413 ++m; 414 func[i] = m; 415 } 416 417 // searching 418 for( size_t m = 0, i = 0; i < haystack.length; ++i ) 419 { 420 while( ( m > 0 ) && !pred( needle[m], haystack[i] ) ) 421 m = *func[m - 1]; 422 if( pred( needle[m], haystack[i] ) ) 423 { 424 ++m; 425 if( m == needle.length ) 426 { 427 return i - needle.length + 1; 428 } 429 } 430 } 431 432 return haystack.length; 433 } 434 435 /// 436 unittest 437 { 438 test!("==")( kfind( "abc", "a" ), 0 ); 439 } 440 441 /// ditto 442 size_t kfind ( T, Pred = DefaultPredicates.IsEqual!(T) ) 443 ( ref Buffer!(T) haystack, in T[] needle, Pred pred = Pred.init ) 444 { 445 return kfind(haystack[], needle[0..needle.length], pred); 446 } 447 448 /// 449 unittest 450 { 451 auto buffer = createBuffer([ 1, 2, 3 ]); 452 test!("==")(kfind(buffer, 1), 0); 453 } 454 455 /// ditto 456 size_t kfind ( T, Pred = DefaultPredicates.IsEqual!(T) ) 457 ( ref Buffer!(T) haystack, in T needle, Pred pred = Pred.init ) 458 { 459 return kfind(haystack[], needle, pred); 460 } 461 462 /// 463 unittest 464 { 465 auto buffer = createBuffer([ 1, 2, 3 ]); 466 test!("==")(kfind(buffer, [ 2, 3 ]), 1); 467 } 468 469 unittest 470 { 471 // find element 472 test!("==")( kfind( ""[], 'a' ), 0 ); 473 test!("==")( kfind( "abc"[], 'a' ), 0 ); 474 test!("==")( kfind( "abc"[], 'b' ), 1 ); 475 test!("==")( kfind( "abc"[], 'c' ), 2 ); 476 test!("==")( kfind( "abc"[], 'd' ), 3 ); 477 478 // null parameters 479 test!("==")( kfind( ""[], ""[] ), 0 ); 480 test!("==")( kfind( "a"[], ""[] ), 1 ); 481 test!("==")( kfind( ""[], "a"[] ), 0 ); 482 483 // exact match 484 test!("==")( kfind( "abc"[], "abc"[] ), 0 ); 485 486 // simple substring match 487 test!("==")( kfind( "abc"[], "a"[] ), 0 ); 488 test!("==")( kfind( "abca"[], "a"[] ), 0 ); 489 test!("==")( kfind( "abc"[], "b"[] ), 1 ); 490 test!("==")( kfind( "abc"[], "c"[] ), 2 ); 491 test!("==")( kfind( "abc"[], "d"[] ), 3 ); 492 493 // multi-char substring match 494 test!("==")( kfind( "abc"[], "ab"[] ), 0 ); 495 test!("==")( kfind( "abcab"[], "ab"[] ), 0 ); 496 test!("==")( kfind( "abc"[], "bc"[] ), 1 ); 497 test!("==")( kfind( "abc"[], "ac"[] ), 3 ); 498 test!("==")( kfind( "abrabracadabra"[], "abracadabra"[] ), 3 ); 499 } 500 501 /******************************************************************************* 502 503 Performs a linear scan of haystack from [0 .. haystack.length$(RP), returning 504 the index of the first element where pred returns true. 505 506 Params: 507 haystack = The array to search. 508 pred = The evaluation predicate, which should return true if the 509 element is a valid match and false if not. This predicate 510 may be any callable type. 511 512 Returns: 513 The index of the first match or haystack.length if no match was found. 514 515 *******************************************************************************/ 516 517 size_t findIf ( T, Pred ) ( in T[] haystack, Pred pred ) 518 { 519 static assert( isCallableType!(Pred) ); 520 521 foreach( pos, cur; haystack ) 522 { 523 if( pred( cur ) ) 524 return pos; 525 } 526 return haystack.length; 527 } 528 529 /// 530 unittest 531 { 532 test!("==")(findIf("bcecg", ( char c ) { return c == 'a'; }), 5); 533 } 534 535 unittest 536 { 537 struct S 538 { 539 mstring err; 540 int x; 541 } 542 543 S[] sarr; 544 findIf(sarr, ( in S s ) { return s.x == 5; }); 545 } 546 547 /// ditto 548 size_t findIf ( T, Pred ) ( ref Buffer!(T) haystack, Pred pred ) 549 { 550 return findIf(haystack[], pred); 551 } 552 553 /// 554 unittest 555 { 556 auto buffer = createBuffer("bcecg"); 557 test!("==")(findIf(buffer, ( char c ) { return c == 'a'; }), 5); 558 } 559 560 unittest 561 { 562 test!("==")( findIf( "bcecg"[], ( char c ) { return c == 'a'; } ), 5 ); 563 test!("==")( findIf( "bcecg"[], ( char c ) { return c == 'b'; } ), 0 ); 564 test!("==")( findIf( "bcecg"[], ( char c ) { return c == 'c'; } ), 1 ); 565 test!("==")( findIf( "bcecg"[], ( char c ) { return c == 'd'; } ), 5 ); 566 test!("==")( findIf( "bcecg"[], ( char c ) { return c == 'g'; } ), 4 ); 567 test!("==")( findIf( "bcecg"[], ( char c ) { return c == 'h'; } ), 5 ); 568 } 569 570 /******************************************************************************* 571 572 Performs a linear scan of haystack from $(LP)haystack.length .. 0], returning 573 the index of the first element where pred returns true. 574 575 Params: 576 haystack = The array to search. 577 pred = The evaluation predicate, which should return true if the 578 element is a valid match and false if not. This predicate 579 may be any callable type. 580 581 Returns: 582 The index of the first match or haystack.length if no match was found. 583 584 *******************************************************************************/ 585 586 size_t rfindIf ( T, Pred ) ( in T[] haystack, Pred pred ) 587 { 588 static assert( isCallableType!(Pred) ); 589 590 if( haystack.length == 0 ) 591 return haystack.length; 592 593 size_t pos = haystack.length; 594 595 do 596 { 597 if( pred( haystack[--pos] ) ) 598 return pos; 599 } while( pos > 0 ); 600 return haystack.length; 601 } 602 603 /// 604 unittest 605 { 606 test!("==")(rfindIf("bcecg", ( char c ) { return c == 'a'; }), 5); 607 } 608 609 /// ditto 610 size_t rfindIf ( T, Pred ) ( ref Buffer!(T) haystack, Pred pred ) 611 { 612 return rfindIf(haystack[], pred); 613 } 614 615 /// 616 unittest 617 { 618 auto buffer = createBuffer("bcecg"); 619 test!("==")(rfindIf(buffer, ( char c ) { return c == 'a'; }), 5); 620 } 621 622 unittest 623 { 624 test!("==")(rfindIf("", ( char c ) { return c == 'a'; }), 0); 625 test!("==")(rfindIf("bcecg", ( char c ) { return c == 'a'; } ), 5); 626 test!("==")(rfindIf("bcecg", ( char c ) { return c == 'b'; } ), 0); 627 test!("==")(rfindIf("bcecg", ( char c ) { return c == 'c'; } ), 3); 628 test!("==")(rfindIf("bcecg", ( char c ) { return c == 'd'; } ), 5); 629 test!("==")(rfindIf("bcecg", ( char c ) { return c == 'g'; } ), 4); 630 test!("==")(rfindIf("bcecg", ( char c ) { return c == 'h'; } ), 5); 631 } 632 633 /******************************************************************************* 634 635 Performs a linear scan of haystack from [0 .. haystack.length$(RP), returning 636 the index of the first element that compares equal to the next element 637 in the sequence. Comparisons will be performed using the supplied 638 predicate or '==' if none is supplied. 639 640 Params: 641 haystack = The array to scan. 642 pred = The evaluation predicate, which should return true if e1 is 643 equal to e2 and false if not. This predicate may be any 644 callable type. 645 646 Returns: 647 The index of the first match or haystack.length if no match was found. 648 649 *******************************************************************************/ 650 651 size_t findAdj( T, Pred = DefaultPredicates.IsEqual!(T) ) 652 ( in T[] haystack, Pred pred = Pred.init ) 653 { 654 static assert( isCallableType!(Pred) ); 655 656 if( haystack.length < 2 ) 657 return haystack.length; 658 659 Unqual!(T) sav = haystack[0]; 660 661 foreach( size_t pos, T cur; haystack[1 .. $] ) 662 { 663 if( pred( cur, sav ) ) 664 return pos; 665 sav = cur; 666 } 667 return haystack.length; 668 } 669 670 /// 671 unittest 672 { 673 test!("==")(findAdj("abcddef"), 3); 674 } 675 676 /// ditto 677 size_t findAdj( T, Pred = DefaultPredicates.IsEqual!(T) ) 678 ( ref Buffer!(T) haystack, Pred pred = Pred.init ) 679 { 680 return findAdj(haystack[], pred); 681 } 682 683 /// 684 unittest 685 { 686 auto buffer = createBuffer("abcddef"); 687 test!("==")(findAdj(buffer), 3); 688 } 689 690 unittest 691 { 692 test!("==")(findAdj(""), 0); 693 test!("==")(findAdj("aabcdef"), 0); 694 test!("==")(findAdj("abcdeff"), 5); 695 test!("==")(findAdj("abcdefg"), 7); 696 } 697 698 /******************************************************************************* 699 700 Performs a linear scan of haystack from [0 .. haystack.length$(RP), returning 701 true if an element matching needle is found. Comparisons will be performed 702 using the supplied predicate or '==' if none is supplied. 703 704 Params: 705 haystack = The array to search. 706 needle = The pattern to search for. 707 pred = The evaluation predicate, which should return true if e1 is 708 equal to e2 and false if not. This predicate may be any 709 callable type. 710 711 Returns: 712 True if an element equivalent to needle is found, false if not. 713 714 *******************************************************************************/ 715 716 equals_t contains ( T, Pred = DefaultPredicates.IsEqual!(T) ) 717 ( in T[] haystack, in T needle, Pred pred = Pred.init ) 718 { 719 return find(haystack, needle, pred) != haystack.length; 720 } 721 722 /// 723 unittest 724 { 725 test( contains("abc", 'a')); 726 test(!contains("abc", 'd')); 727 } 728 729 /// ditto 730 equals_t contains ( T, Pred = DefaultPredicates.IsEqual!(T) ) 731 ( in T[] haystack, in T[] needle, Pred pred = Pred.init ) 732 { 733 return find(haystack, needle, pred) != haystack.length; 734 } 735 736 /// 737 unittest 738 { 739 test( contains("abc", "a")); 740 test(!contains("abc", "d")); 741 } 742 743 /// ditto 744 equals_t contains ( T, Pred = DefaultPredicates.IsEqual!(T) ) 745 ( ref Buffer!(T) haystack, in T needle, Pred pred = Pred.init ) 746 { 747 return find(haystack, needle, pred) != haystack.length; 748 } 749 750 /// 751 unittest 752 { 753 auto buffer = createBuffer("abc"); 754 test( contains(buffer, 'a')); 755 test(!contains(buffer, 'd')); 756 } 757 758 /// ditto 759 equals_t contains ( T, Pred = DefaultPredicates.IsEqual!(T) ) 760 ( ref Buffer!(T) haystack, in T[] needle, Pred pred = Pred.init ) 761 { 762 return find(haystack, needle, pred) != haystack.length; 763 } 764 765 /// 766 unittest 767 { 768 auto buffer = createBuffer("abc"); 769 test( contains(buffer, "a")); 770 test(!contains(buffer, "d")); 771 } 772 773 unittest 774 { 775 mixin (Typedef!(hash_t, "Hash")); 776 auto id = cast(Hash) 42; 777 Hash[] arr = [ cast(Hash) 1, cast(Hash) 2, cast(Hash) 3 ]; 778 auto result = contains(arr, id); 779 } 780 781 unittest 782 { 783 // find element 784 test(!contains( ""[], 'a' )); 785 test( contains( "abc"[], 'a' )); 786 test( contains( "abc"[], 'b' )); 787 test( contains( "abc"[], 'c' )); 788 test(!contains( "abc"[], 'd' )); 789 790 // null parameters 791 test(!contains( ""[], ""[] )); 792 test(!contains( "a"[], ""[] )); 793 test(!contains( ""[], "a"[] )); 794 795 // exact match 796 test( contains( "abc"[], "abc"[] )); 797 798 // simple substring match 799 test( contains( "abc"[], "a"[] )); 800 test( contains( "abca"[], "a"[] )); 801 test( contains( "abc"[], "b"[] )); 802 test( contains( "abc"[], "c"[] )); 803 test(!contains( "abc"[], "d"[] )); 804 805 // multi-char substring match 806 test( contains( "abc"[], "ab"[] )); 807 test( contains( "abcab"[], "ab"[] )); 808 test( contains( "abc"[], "bc"[] )); 809 test(!contains( "abc"[], "ac"[] )); 810 test( contains( "abrabracadabra"[], "abracadabra"[] )); 811 } 812 813 /******************************************************************************* 814 815 Performs a parallel linear scan of arr and arr_against from [0 .. N$(RP) 816 where N = min(arr.length, arr_against.length), returning the index of 817 the first element in arr which does not match the corresponding element 818 in arr_against or N if no mismatch occurs. Comparisons will be performed 819 using the supplied predicate or '==' if none is supplied. 820 821 If the input arrays are not sorted in the same way, or they have different 822 number of duplicate items, see `bsubset`. 823 824 Params: 825 arr = The array to evaluate. 826 arr_against = The array to match against. 827 pred = The evaluation predicate, which should return true if e1 828 is equal to e2 and false if not. This predicate may be any 829 callable type. 830 831 Returns: 832 The index of the first mismatch or N if the first N elements of arr 833 and arr_against match, where 834 N = min$(LP)arr.length, arr_against.length$(RP). 835 836 *******************************************************************************/ 837 838 size_t mismatch ( T, Pred = DefaultPredicates.IsEqual!(T) ) 839 ( in T[] arr, in T[] arr_against, Pred pred = Pred.init ) 840 { 841 static assert( isCallableType!(Pred) ); 842 843 size_t posA = 0, 844 posB = 0; 845 846 while( posA < arr.length && posB < arr_against.length ) 847 { 848 if( !pred( arr_against[posB], arr[posA] ) ) 849 break; 850 ++posA, ++posB; 851 } 852 return posA; 853 } 854 855 /// 856 unittest 857 { 858 // result must not change from swapping argument order: 859 test!("==")(mismatch("abcxefg", "abcdefg"), 3); 860 test!("==")(mismatch("abcdefg", "abcxefg"), 3); 861 } 862 863 /// ditto 864 size_t mismatch ( T, Pred = DefaultPredicates.IsEqual!(T) ) 865 ( ref Buffer!(T) arr, ref Buffer!(T) arr_against, Pred pred = Pred.init ) 866 { 867 return mismatch(arr[0..arr.length], 868 arr_against[0..arr_against.length], pred); 869 } 870 871 /// 872 unittest 873 { 874 // result must not change from swapping argument order: 875 auto buffer1 = createBuffer("abcxefg"); 876 auto buffer2 = createBuffer("abcdefg"); 877 test!("==")( mismatch(buffer1, buffer2), 3 ); 878 test!("==")( mismatch(buffer2, buffer1), 3 ); 879 } 880 881 unittest 882 { 883 test!("==")( mismatch( "a"[], "abcdefg"[] ), 1 ); 884 test!("==")( mismatch( "abcdefg"[], "a"[] ), 1 ); 885 886 test!("==")( mismatch( "x"[], "abcdefg"[] ), 0 ); 887 test!("==")( mismatch( "abcdefg"[], "x"[] ), 0 ); 888 889 test!("==")( mismatch( "xbcdefg"[], "abcdefg"[] ), 0 ); 890 test!("==")( mismatch( "abcdefg"[], "xbcdefg"[] ), 0 ); 891 892 test!("==")( mismatch( "abcxefg"[], "abcdefg"[] ), 3 ); 893 test!("==")( mismatch( "abcdefg"[], "abcxefg"[] ), 3 ); 894 895 test!("==")( mismatch( "abcdefx"[], "abcdefg"[] ), 6 ); 896 test!("==")( mismatch( "abcdefg"[], "abcdefx"[] ), 6 ); 897 } 898 899 /****************************************************************************** 900 901 Performs a linear scan of haystack from [0 .. haystack.length$(RP), returning 902 a count of the number of elements matching needle. Comparisons will be 903 performed using the supplied predicate or '==' if none is supplied. 904 905 Params: 906 haystack = The array to scan. 907 needle = The pattern to match. 908 pred = The evaluation predicate, which should return true if e1 is 909 equal to e2 and false if not. This predicate may be any 910 callable type. 911 912 Returns: 913 The number of elements matching needle. 914 915 ******************************************************************************/ 916 917 size_t count ( T, Pred = DefaultPredicates.IsEqual!(T) ) 918 ( in T[] haystack, in T needle, Pred pred = Pred.init ) 919 { 920 static assert( isCallableType!(Pred) ); 921 922 size_t cnt = 0; 923 924 foreach( size_t pos, T cur; haystack ) 925 { 926 if( pred( cur, needle ) ) 927 ++cnt; 928 } 929 return cnt; 930 } 931 932 /// 933 unittest 934 { 935 test!("==")(count("gbbbi", 'b'), 3); 936 } 937 938 /// ditto 939 size_t count ( T, Pred = DefaultPredicates.IsEqual!(T) ) 940 ( ref Buffer!(T) haystack, in T needle, Pred pred = Pred.init ) 941 { 942 return count(haystack[], needle, pred); 943 } 944 945 /// 946 unittest 947 { 948 auto buffer = createBuffer("gbbbi"); 949 test!("==")(count(buffer, 'b'), 3); 950 } 951 952 unittest 953 { 954 test!("==")( count( "gbbbi"[], 'a' ), 0 ); 955 test!("==")( count( "gbbbi"[], 'g' ), 1 ); 956 test!("==")( count( "gbbbi"[], 'b' ), 3 ); 957 test!("==")( count( "gbbbi"[], 'i' ), 1 ); 958 test!("==")( count( "gbbbi"[], 'd' ), 0 ); 959 } 960 961 /******************************************************************************* 962 963 Performs a linear scan of haystack from [0 .. haystack.length$(RP), returning 964 a count of the number of elements where pred returns true. 965 966 Params: 967 haystack = The array to scan. 968 pred = The evaluation predicate, which should return true if the 969 element is a valid match and false if not. This predicate 970 may be any callable type. 971 972 Returns: 973 The number of elements where pred returns true. 974 975 *******************************************************************************/ 976 977 size_t countIf ( T, Pred = DefaultPredicates.IsEqual!(T) ) 978 ( in T[] haystack, Pred pred = Pred.init ) 979 { 980 static assert( isCallableType!(Pred) ); 981 982 size_t cnt = 0; 983 984 foreach( size_t pos, T cur; haystack ) 985 { 986 if( pred( cur ) ) 987 ++cnt; 988 } 989 return cnt; 990 } 991 992 /// 993 unittest 994 { 995 test!("==")(countIf("gbbbi", ( char c ) { return c == 'b'; }), 3); 996 } 997 998 size_t countIf ( T, Pred = DefaultPredicates.IsEqual!(T) ) 999 ( ref Buffer!(T) haystack, Pred pred = Pred.init ) 1000 { 1001 return countIf(haystack[], pred); 1002 } 1003 1004 /// 1005 unittest 1006 { 1007 auto buffer = createBuffer("gbbbi"); 1008 test!("==")(countIf(buffer, ( char c ) { return c == 'b'; }), 3); 1009 } 1010 1011 unittest 1012 { 1013 test!("==")( countIf( "gbbbi"[], ( char c ) { return c == 'a'; } ), 0 ); 1014 test!("==")( countIf( "gbbbi"[], ( char c ) { return c == 'g'; } ), 1 ); 1015 test!("==")( countIf( "gbbbi"[], ( char c ) { return c == 'b'; } ), 3 ); 1016 test!("==")( countIf( "gbbbi"[], ( char c ) { return c == 'i'; } ), 1 ); 1017 test!("==")( countIf( "gbbbi"[], ( char c ) { return c == 'd'; } ), 0 ); 1018 } 1019 1020 /******************************************************************************* 1021 1022 Checks if array B is a subset of array A. Array A is assumed to be 1023 pre-sorted in ascending order. Array B doesn't need to be sorted and might 1024 have duplicate elements. It checks if array A contains each item of array B 1025 using binary search. If T is a class or struct, comparison is performed 1026 using T.opCmp(). Otherwise, elements of T are compared using ">" and ">=" 1027 or, if T is compatible to size_t (which includes ssize_t, the signed version 1028 of size_t), by calculating the difference. 1029 1030 If both of the arrays are sorted in the same way and don't contain different 1031 number of duplicate items, see `mismatch`. 1032 1033 Template params: 1034 T = type of array element 1035 1036 Params: 1037 a = array A 1038 b = array B 1039 1040 Returns: 1041 false if there is any item of array B, which is not an item of array A 1042 1043 In: 1044 See `bsearch` for input constraints 1045 1046 *******************************************************************************/ 1047 1048 public bool bsubset ( T ) ( T[] a, T[] b ) 1049 { 1050 if ( a.length == 0 ) 1051 return b.length == 0; 1052 1053 foreach ( ref T item; b ) 1054 { 1055 if ( !bcontains(a, item) ) 1056 return false; 1057 } 1058 1059 return true; 1060 } 1061 1062 unittest 1063 { 1064 test(bsubset!(int)([], [])); 1065 test(!bsubset!(int)([], [0])); 1066 test(bsubset!(int)([0], [])); 1067 test(bsubset([0], [0])); 1068 test(!bsubset([0], [1])); 1069 test(bsubset([0, 1], [1])); 1070 test(bsubset([0, 1], [0, 1, 1])); 1071 } 1072 1073 /******************************************************************************* 1074 1075 Searches a sorted array for the specified element. The array is assumed to 1076 be pre-sorted in ascending order, the search will not work properly if it 1077 is not. If T is a class or struct, comparison is performed using T.opCmp(). 1078 Otherwise, elements of T are compared using ">" and ">=" or, if T is 1079 compatible to size_t (which includes ssize_t, the signed version of size_t), 1080 by calculating the difference. 1081 1082 Template params: 1083 T = type of array element 1084 1085 Params: 1086 array = array to search 1087 match = element to search for 1088 1089 Returns: 1090 true if the element was found in the array 1091 1092 In: 1093 See `bsearch` for input constraints 1094 1095 *******************************************************************************/ 1096 1097 public bool bcontains ( T ) ( T[] array, T match ) 1098 { 1099 size_t position; 1100 1101 return bsearch(array, match, position); 1102 } 1103 1104 unittest 1105 { 1106 auto arr = [ 1, 2, 4, 6, 20, 100, 240 ]; 1107 1108 test(bcontains(arr, 6)); 1109 test(!bcontains(arr, 0)); 1110 test(!bcontains(arr, 300)); 1111 test(!bcontains(arr, 10)); 1112 } 1113 1114 /******************************************************************************* 1115 1116 Searches a sorted array for the specified element or for the insert 1117 position of the element. The array is assumed to be pre-sorted in ascending 1118 order, the search will not work properly if it is not. 1119 If T is a class or struct, comparison is performed using T.opCmp(). 1120 Otherwise, elements of T are compared using ">" and ">=" or, if T is 1121 compatible to size_t (which includes ssize_t, the signed version of size_t), 1122 by calculating the difference. 1123 1124 Template params: 1125 T = type of array element 1126 1127 Params: 1128 array = array to search 1129 match = element to search for 1130 position = out value, value depends on whether the element was found: 1131 1132 1. If found, the position at which element was found is output. 1133 1134 2. If not found, the position at which the element could be inserted 1135 is output, as follows: 1136 1137 * A value of 0 means that the element is smaller than all 1138 elements in the array, and would need to be inserted at the 1139 beginning of the array, and all other elements shifted to the 1140 right. 1141 * A value of array.length means that the element is larger than 1142 all elements in the array, and would need to be appended to the 1143 end of the array. 1144 * A value of > 0 and < array.length means that the element would 1145 need to be inserted at the specified position, and all elements 1146 of index >= the specified position shifted to the right. 1147 1148 Returns: 1149 true if the element was found in the array 1150 1151 In: 1152 array.length must be at most ssize_t.max (int.max if size_t is uint or 1153 long.max if size_t is ulong). TODO: Remove this restriction by 1154 rephrasing the implementation in bsearchCustom(). 1155 1156 *******************************************************************************/ 1157 1158 public bool bsearch ( T ) ( T[] array, T match, out size_t position ) 1159 out (found) 1160 { 1161 if (found) 1162 { 1163 assert (position < array.length); 1164 } 1165 else 1166 { 1167 assert (position <= array.length); 1168 } 1169 } 1170 body 1171 { 1172 return bsearchCustom( 1173 array.length, 1174 delegate ssize_t ( size_t i ) 1175 { 1176 static if (is (T : size_t)) // will also be true if T is ssize_t 1177 { 1178 // If T is unsigned, check if cast (ssize_t) (0 - 1) == -1. 1179 // TODO: Is this behaviour guaranteed? If so, remove the 1180 // check. 1181 1182 static if (T.min == 0) 1183 { 1184 static assert (cast (ssize_t) (T.min - cast (T) 1) == -1, 1185 "bsearch: 0 - 1 != -1 for type " ~ T.stringof); 1186 } 1187 1188 return match - array[i]; 1189 } 1190 else static if (is (T == class) || is (T == struct)) 1191 { 1192 return match.opCmp(array[i]); 1193 } 1194 else 1195 { 1196 return (match >= array[i])? (match > array[i]) : -1; 1197 } 1198 }, 1199 position 1200 ); 1201 } 1202 1203 unittest 1204 { 1205 auto arr = [ 1, 2, 4, 6, 20, 100, 240 ]; 1206 size_t pos; 1207 bool found = bsearch(arr, 6, pos); 1208 } 1209 1210 /******************************************************************************* 1211 1212 Searches a sorted array for an element or an insert position for an element. 1213 The array is assumed to be pre-sorted according to cmp. 1214 1215 Params: 1216 array_length = length of array to search 1217 cmp = comparison callback delegate, should return 1218 * a positive value if the array element at index i compares 1219 greater than the element to search for, 1220 * a negative value if the array element at index i compares 1221 less than the element to search for, 1222 * 0 if if the array element at index i compares equal to 1223 the element to search for. 1224 position = out value, value depends on whether the element was found: 1225 1226 1. If found, the position at which element was found is output. 1227 1228 2. If not found, the position at which the element could be inserted 1229 is output, as follows: 1230 1231 * A value of 0 means that the element is smaller than all 1232 elements in the array, and would need to be inserted at the 1233 beginning of the array, and all other elements shifted to the 1234 right. 1235 * A value of array.length means that the element is larger than 1236 all elements in the array, and would need to be appended to the 1237 end of the array. 1238 * A value of > 0 and < array.length means that the element would 1239 need to be inserted at the specified position, and all elements 1240 of index >= the specified position shifted to the right. 1241 1242 Returns: 1243 true if the element was found in the array 1244 1245 In: 1246 array_length must be at most ssize_t.max (int.max if size_t is uint or 1247 long.max if size_t is ulong). TODO: Remove this restriction by 1248 rephrasing the implementation so that min/max cannot be less than 0. 1249 1250 *******************************************************************************/ 1251 1252 public bool bsearchCustom ( size_t array_length, scope ssize_t delegate ( size_t i ) cmp, out size_t position ) 1253 out (found) 1254 { 1255 if (found) 1256 { 1257 assert (position < array_length); 1258 } 1259 else 1260 { 1261 assert (position <= array_length); 1262 } 1263 } 1264 body 1265 { 1266 verify(cast (ssize_t) array_length >= 0, 1267 "bsearchCustom: array_length integer overflow (maximum is " ~ 1268 ssize_t.stringof ~ ".max = " ~ ssize_t.max.stringof ~ ')'); 1269 1270 if ( array_length == 0 ) 1271 { 1272 return false; 1273 } 1274 1275 ssize_t min = 0; 1276 ssize_t max = array_length - 1; 1277 1278 ssize_t c = cmp(position = (min + max) / 2); 1279 1280 while ( min <= max && c ) 1281 { 1282 if ( c < 0 ) // match < array[position] 1283 { 1284 max = position - 1; 1285 } 1286 else // match > array[position] 1287 { 1288 min = position + 1; 1289 } 1290 1291 c = cmp(position = (min + max) / 2); 1292 } 1293 1294 position += c > 0; 1295 1296 return !c; 1297 } 1298 1299 /******************************************************************************* 1300 1301 Performs a parallel linear scan of setA and setB from [0 .. N$(RP) 1302 where N = min(setA.length, setB.length), returning true if setA 1303 includes all elements in setB and false if not. Both setA and setB are 1304 required to be sorted, and duplicates in setB require an equal number of 1305 duplicates in setA. Comparisons will be performed using the supplied 1306 predicate or '<' if none is supplied. 1307 1308 Params: 1309 setA = The sorted array to evaluate. 1310 setB = The sorted array to match against. 1311 pred = The evaluation predicate, which should return true if e1 is 1312 less than e2 and false if not. This predicate may be any 1313 callable type. 1314 1315 Returns: 1316 True if setA includes all elements in setB, false if not. 1317 1318 *******************************************************************************/ 1319 1320 bool includes ( T, Pred = DefaultPredicates.IsLess!(T) ) 1321 ( in T[] setA, in T[] setB, Pred pred = Pred.init ) 1322 { 1323 static assert( isCallableType!(Pred ) ); 1324 1325 size_t posA = 0, 1326 posB = 0; 1327 1328 while( posA < setA.length && posB < setB.length ) 1329 { 1330 if( pred( setB[posB], setA[posA] ) ) 1331 return false; 1332 else if( pred( setA[posA], setB[posB] ) ) 1333 ++posA; 1334 else 1335 ++posA, ++posB; 1336 } 1337 return posB == setB.length; 1338 } 1339 1340 /// 1341 unittest 1342 { 1343 test(includes("abcdefg", "cde")); 1344 } 1345 1346 unittest 1347 { 1348 test( includes( "abcdefg"[], "a"[] ) ); 1349 test( includes( "abcdefg"[], "g"[] ) ); 1350 test( includes( "abcdefg"[], "d"[] ) ); 1351 test( includes( "abcdefg"[], "abcdefg"[] ) ); 1352 test( includes( "aaaabbbcdddefgg"[], "abbbcdefg"[] ) ); 1353 1354 test( !includes( "abcdefg"[], "aaabcdefg"[] ) ); 1355 test( !includes( "abcdefg"[], "abcdefggg"[] ) ); 1356 test( !includes( "abbbcdefg"[], "abbbbcdefg"[] ) ); 1357 } 1358 1359 /******************************************************************************* 1360 1361 Check if the given array starts with the given prefix 1362 1363 Template Params: 1364 T = The type of the array element 1365 1366 Params: 1367 arr = The array to be tested 1368 prefix = The prefix to test for 1369 1370 Returns: 1371 True if the array starts with the prefix, false otherwise 1372 1373 *******************************************************************************/ 1374 1375 bool startsWith ( T ) ( in T[] arr, in T[] prefix ) 1376 { 1377 // https://issues.dlang.org/show_bug.cgi?id=17917 1378 // WORKAROUND: with -O -cov compiler doesn't early return from 1379 // && expression, thus the code here is manually split into two 1380 // conditions 1381 if (arr.length < prefix.length) 1382 return false; 1383 return arr[0..prefix.length] == prefix[]; 1384 } 1385 1386 unittest 1387 { 1388 test( startsWith("abcd", "abc")); 1389 test( startsWith("abcd", "abcd")); 1390 test(!startsWith("ab", "abc")); 1391 test( startsWith("ab", "")); 1392 test(!startsWith("", "xx")); 1393 1394 test( startsWith([1,2,3,4], [1,2,3])); 1395 test( startsWith([1,2,3,4], [1,2,3,4])); 1396 test(!startsWith([1,2], [1,2,3])); 1397 test( startsWith([1,2], (int[]).init)); 1398 test(!startsWith((int[]).init, [1,2])); 1399 } 1400 1401 /******************************************************************************* 1402 1403 Check if the given array ends with the given suffix 1404 1405 Template Params: 1406 T = The type of the array element 1407 1408 Params: 1409 arr = The array to be tested 1410 suffix = The suffix to test for 1411 1412 Returns: 1413 True if the array ends with the suffix, false otherwise 1414 1415 *******************************************************************************/ 1416 1417 bool endsWith ( T ) ( in T[] arr, in T[] suffix ) 1418 { 1419 // https://issues.dlang.org/show_bug.cgi?id=17917 1420 // WORKAROUND: with -O -cov compiler doesn't early return from 1421 // && expression, thus the code here is manually split into two 1422 // conditions 1423 if (arr.length < suffix.length) 1424 return false; 1425 return arr[$ - suffix.length .. $] == suffix[]; 1426 } 1427 1428 unittest 1429 { 1430 test( endsWith("abcd", "bcd")); 1431 test( endsWith("abcd", "abcd")); 1432 test(!endsWith("ab", "abc")); 1433 test( endsWith("ab", "")); 1434 test(!endsWith("", "xx")); 1435 1436 test( endsWith([1,2,3,4], [2,3,4])); 1437 test( endsWith([1,2,3,4], [1,2,3,4])); 1438 test(!endsWith([1,2], [1,2,3])); 1439 test( endsWith([1,2], (int[]).init)); 1440 test(!endsWith((int[]).init, [1,2])); 1441 } 1442 1443 /******************************************************************************* 1444 1445 Remove the given prefix from the given array. 1446 1447 Template Params: 1448 T = The type of the array element 1449 1450 Params: 1451 arr = The array from which the prefix is to be removed 1452 prefix = The prefix to remove 1453 1454 Returns: 1455 A slice without the prefix if successful, the original array otherwise 1456 1457 *******************************************************************************/ 1458 1459 public T1[] removePrefix ( T1, T2 ) ( T1[] arr, in T2[] prefix ) 1460 { 1461 return ((arr.length >= prefix.length) && (startsWith(arr, prefix)) 1462 ? arr[prefix.length .. $] 1463 : arr); 1464 } 1465 1466 unittest 1467 { 1468 test(removePrefix("abcd", "abc") == "d"); 1469 test(removePrefix("abcd", "abcd") == ""); 1470 test(removePrefix("abcd", "abcde") == "abcd"); 1471 test(removePrefix("abcd", "") == "abcd"); 1472 test(removePrefix("", "xx") == ""); 1473 test("abcd".removePrefix("abc") == "d"); 1474 test("abcd".removePrefix("abcd") == ""); 1475 test("abcd".removePrefix("abcde") == "abcd"); 1476 test("abcd".removePrefix("") == "abcd"); 1477 test("".removePrefix("xx") == ""); 1478 1479 test(removePrefix([1,2,3,4], [1,2,3]) == [ 4 ]); 1480 test(removePrefix([1,2,3,4], [1,2,3,4]) == cast(int[]) null); 1481 test(removePrefix([1,2], [1,2,3]) == [ 1, 2 ]); 1482 test(removePrefix([1,2], (int[]).init) == [ 1, 2 ]); 1483 test(removePrefix((int[]).init, [1,2]) == cast(int[]) null); 1484 } 1485 1486 /******************************************************************************* 1487 1488 Remove the given suffix from the given array. 1489 1490 Template Params: 1491 T = The type of the array element 1492 1493 Params: 1494 arr = The array from which the suffix is to be removed 1495 suffix = The suffix to remove 1496 1497 Returns: 1498 A slice without the suffix if successful, the original array otherwise 1499 1500 *******************************************************************************/ 1501 1502 public T1[] removeSuffix ( T1, T2 ) ( T1[] arr, in T2[] suffix ) 1503 { 1504 return ((arr.length >= suffix.length) && (endsWith(arr, suffix)) 1505 ? arr[0 .. $-suffix.length] 1506 : arr); 1507 } 1508 1509 unittest 1510 { 1511 test(removeSuffix("abcd", "cd") == "ab"); 1512 test(removeSuffix("abcd", "abcd") == ""); 1513 test(removeSuffix("abcd", "abcde") == "abcd"); 1514 test(removeSuffix("abcd", "") == "abcd"); 1515 test(removeSuffix("", "xx") == ""); 1516 test("abcd".removeSuffix("cd") == "ab"); 1517 test("abcd".removeSuffix("abcd") == ""); 1518 test("abcd".removeSuffix("abcde") == "abcd"); 1519 test("abcd".removeSuffix("") == "abcd"); 1520 test("".removeSuffix("xx") == ""); 1521 1522 test(removeSuffix([1,2,3,4], [2,3,4]) == [ 1 ]); 1523 test(removeSuffix([1,2,3,4], [1,2,3,4]) == cast(int[]) null); 1524 test(removeSuffix([1,2], [1,2,3]) == [ 1, 2 ]); 1525 test(removeSuffix([1,2], (int[]).init) == [ 1, 2 ]); 1526 test(removeSuffix((int[]).init, [1,2]) == cast(int[]) null); 1527 } 1528