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