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     Template params:
1039         T = type of array element
1040 
1041     Params:
1042         a = array A
1043         b = array B
1044 
1045     Returns:
1046         false if there is any item of array B, which is not an item of array A
1047 
1048     In:
1049         See `bsearch` for input constraints
1050 
1051 *******************************************************************************/
1052 
1053 public bool bsubset ( T ) ( T[] a, T[] b )
1054 {
1055     if ( a.length == 0 )
1056         return b.length == 0;
1057 
1058     foreach ( ref T item; b )
1059     {
1060         if ( !bcontains(a, item) )
1061             return false;
1062     }
1063 
1064     return true;
1065 }
1066 
1067 unittest
1068 {
1069     test(bsubset!(int)([], []));
1070     test(!bsubset!(int)([], [0]));
1071     test(bsubset!(int)([0], []));
1072     test(bsubset([0], [0]));
1073     test(!bsubset([0], [1]));
1074     test(bsubset([0, 1], [1]));
1075     test(bsubset([0, 1], [0, 1, 1]));
1076 }
1077 
1078 /*******************************************************************************
1079 
1080     Searches a sorted array for the specified element. The array is assumed to
1081     be pre-sorted in ascending order, the search will not work properly if it
1082     is not. If T is a class or struct, comparison is performed using T.opCmp().
1083     Otherwise, elements of T are compared using ">" and ">=" or, if T is
1084     compatible to size_t (which includes ssize_t, the signed version of size_t),
1085     by calculating the difference.
1086 
1087     Template params:
1088         T = type of array element
1089 
1090     Params:
1091         array = array to search
1092         match = element to search for
1093 
1094     Returns:
1095         true if the element was found in the array
1096 
1097     In:
1098         See `bsearch` for input constraints
1099 
1100 *******************************************************************************/
1101 
1102 public bool bcontains ( T ) ( T[] array, T match )
1103 {
1104     size_t position;
1105 
1106     return bsearch(array, match, position);
1107 }
1108 
1109 unittest
1110 {
1111     auto arr = [ 1, 2, 4, 6, 20, 100, 240 ];
1112 
1113     test(bcontains(arr, 6));
1114     test(!bcontains(arr, 0));
1115     test(!bcontains(arr, 300));
1116     test(!bcontains(arr, 10));
1117 }
1118 
1119 /*******************************************************************************
1120 
1121     Searches a sorted array for the specified element or for the insert
1122     position of the element. The array is assumed to be pre-sorted in ascending
1123     order, the search will not work properly if it is not.
1124     If T is a class or struct, comparison is performed using T.opCmp().
1125     Otherwise, elements of T are compared using ">" and ">=" or, if T is
1126     compatible to size_t (which includes ssize_t, the signed version of size_t),
1127     by calculating the difference.
1128 
1129     Template params:
1130         T = type of array element
1131 
1132     Params:
1133         array = array to search
1134         match = element to search for
1135         position = out value, value depends on whether the element was found:
1136 
1137             1. If found, the position at which element was found is output.
1138 
1139             2. If not found, the position at which the element could be inserted
1140                is output, as follows:
1141 
1142                * A value of 0 means that the element is smaller than all
1143                  elements in the array, and would need to be inserted at the
1144                  beginning of the array, and all other elements shifted to the
1145                  right.
1146                * A value of array.length means that the element is larger than
1147                  all elements in the array, and would need to be appended to the
1148                  end of the array.
1149                * A value of > 0 and < array.length means that the element would
1150                  need to be inserted at the specified position, and all elements
1151                  of index >= the specified position shifted to the right.
1152 
1153     Returns:
1154         true if the element was found in the array
1155 
1156     In:
1157         array.length must be at most ssize_t.max (int.max if size_t is uint or
1158         long.max if size_t is ulong). TODO: Remove this restriction by
1159         rephrasing the implementation in bsearchCustom().
1160 
1161 *******************************************************************************/
1162 
1163 public bool bsearch ( T ) ( T[] array, T match, out size_t position )
1164 out (found)
1165 {
1166     if (found)
1167     {
1168         assert (position < array.length);
1169     }
1170     else
1171     {
1172         assert (position <= array.length);
1173     }
1174 }
1175 do
1176 {
1177     return bsearchCustom(
1178         array.length,
1179         delegate ssize_t ( size_t i )
1180         {
1181             static if (is (T : size_t)) // will also be true if T is ssize_t
1182             {
1183                 // If T is unsigned, check if cast (ssize_t) (0 - 1) == -1.
1184                 // TODO: Is this behaviour guaranteed? If so, remove the
1185                 // check.
1186 
1187                 static if (T.min == 0)
1188                 {
1189                     static assert (cast (ssize_t) (T.min - cast (T) 1) == -1,
1190                                    "bsearch: 0 - 1 != -1 for type " ~ T.stringof);
1191                 }
1192 
1193                 return match - array[i];
1194             }
1195             else static if (is (T == class) || is (T == struct))
1196             {
1197                 return match.opCmp(array[i]);
1198             }
1199             else
1200             {
1201                 return (match >= array[i])? (match > array[i]) : -1;
1202             }
1203         },
1204         position
1205     );
1206 }
1207 
1208 unittest
1209 {
1210     auto arr = [ 1, 2, 4, 6, 20, 100, 240 ];
1211     size_t pos;
1212     bool found = bsearch(arr, 6, pos);
1213 }
1214 
1215 /*******************************************************************************
1216 
1217     Searches a sorted array for an element or an insert position for an element.
1218     The array is assumed to be pre-sorted according to cmp.
1219 
1220     Params:
1221         array_length = length of array to search
1222         cmp       = comparison callback delegate, should return
1223                     * a positive value if the array element at index i compares
1224                       greater than the element to search for,
1225                     * a negative value if the array element at index i compares
1226                       less than the element to search for,
1227                     * 0 if if the array element at index i compares equal to
1228                       the element to search for.
1229         position  = out value, value depends on whether the element was found:
1230 
1231             1. If found, the position at which element was found is output.
1232 
1233             2. If not found, the position at which the element could be inserted
1234                is output, as follows:
1235 
1236                * A value of 0 means that the element is smaller than all
1237                  elements in the array, and would need to be inserted at the
1238                  beginning of the array, and all other elements shifted to the
1239                  right.
1240                * A value of array.length means that the element is larger than
1241                  all elements in the array, and would need to be appended to the
1242                  end of the array.
1243                * A value of > 0 and < array.length means that the element would
1244                  need to be inserted at the specified position, and all elements
1245                  of index >= the specified position shifted to the right.
1246 
1247     Returns:
1248         true if the element was found in the array
1249 
1250     In:
1251         array_length must be at most ssize_t.max (int.max if size_t is uint or
1252         long.max if size_t is ulong). TODO: Remove this restriction by
1253         rephrasing the implementation so that min/max cannot be less than 0.
1254 
1255 *******************************************************************************/
1256 
1257 public bool bsearchCustom ( size_t array_length, scope ssize_t delegate ( size_t i ) cmp, out size_t position )
1258 out (found)
1259 {
1260     if (found)
1261     {
1262         assert (position < array_length);
1263     }
1264     else
1265     {
1266         assert (position <= array_length);
1267     }
1268 }
1269 do
1270 {
1271     verify(cast (ssize_t) array_length >= 0,
1272         "bsearchCustom: array_length integer overflow (maximum is " ~
1273         ssize_t.stringof ~ ".max = " ~ ssize_t.max.stringof ~ ')');
1274 
1275     if ( array_length == 0 )
1276     {
1277         return false;
1278     }
1279 
1280     ssize_t min = 0;
1281     ssize_t max = array_length - 1;
1282 
1283     ssize_t c = cmp(position = (min + max) / 2);
1284 
1285     while ( min <= max && c )
1286     {
1287         if ( c < 0 ) // match < array[position]
1288         {
1289             max = position - 1;
1290         }
1291         else        // match > array[position]
1292         {
1293             min = position + 1;
1294         }
1295 
1296         c = cmp(position = (min + max) / 2);
1297     }
1298 
1299     position += c > 0;
1300 
1301     return !c;
1302 }
1303 
1304 /*******************************************************************************
1305 
1306     Performs a parallel linear scan of setA and setB from [0 .. N$(RP)
1307     where N = min(setA.length, setB.length), returning true if setA
1308     includes all elements in setB and false if not.  Both setA and setB are
1309     required to be sorted, and duplicates in setB require an equal number of
1310     duplicates in setA.  Comparisons will be performed using the supplied
1311     predicate or '<' if none is supplied.
1312 
1313     Params:
1314         setA = The sorted array to evaluate.
1315         setB = The sorted array to match against.
1316         pred = The evaluation predicate, which should return true if e1 is
1317            less than e2 and false if not.  This predicate may be any
1318        callable type.
1319 
1320     Returns:
1321         True if setA includes all elements in setB, false if not.
1322 
1323 *******************************************************************************/
1324 
1325 bool includes ( T,  Pred = DefaultPredicates.IsLess!(T) )
1326     ( in T[] setA, in T[] setB, Pred pred = Pred.init )
1327 {
1328     static assert( isCallableType!(Pred ) );
1329 
1330     size_t  posA = 0,
1331             posB = 0;
1332 
1333     while( posA < setA.length && posB < setB.length )
1334     {
1335         if( pred( setB[posB], setA[posA] ) )
1336             return false;
1337         else if( pred( setA[posA], setB[posB] ) )
1338             ++posA;
1339         else
1340             ++posA, ++posB;
1341     }
1342     return posB == setB.length;
1343 }
1344 
1345 ///
1346 unittest
1347 {
1348     test(includes("abcdefg", "cde"));
1349 }
1350 
1351 unittest
1352 {
1353     test( includes( "abcdefg"[], "a"[] ) );
1354     test( includes( "abcdefg"[], "g"[] ) );
1355     test( includes( "abcdefg"[], "d"[] ) );
1356     test( includes( "abcdefg"[], "abcdefg"[] ) );
1357     test( includes( "aaaabbbcdddefgg"[], "abbbcdefg"[] ) );
1358 
1359     test( !includes( "abcdefg"[], "aaabcdefg"[] ) );
1360     test( !includes( "abcdefg"[], "abcdefggg"[] ) );
1361     test( !includes( "abbbcdefg"[], "abbbbcdefg"[] ) );
1362 }
1363 
1364 /*******************************************************************************
1365 
1366     Check if the given array starts with the given prefix
1367 
1368     Template Params:
1369         T = The type of the array element
1370 
1371     Params:
1372         arr    = The array to be tested
1373         prefix = The prefix to test for
1374 
1375     Returns:
1376         True if the array starts with the prefix, false otherwise
1377 
1378 *******************************************************************************/
1379 
1380 bool startsWith ( T ) ( in T[] arr, in T[] prefix )
1381 {
1382     // https://issues.dlang.org/show_bug.cgi?id=17917
1383     // WORKAROUND: with -O -cov compiler doesn't early return from
1384     // && expression, thus the code here is manually split into two
1385     // conditions
1386     if (arr.length < prefix.length)
1387         return false;
1388     return arr[0..prefix.length] == prefix[];
1389 }
1390 
1391 unittest
1392 {
1393     test( startsWith("abcd", "abc"));
1394     test( startsWith("abcd", "abcd"));
1395     test(!startsWith("ab", "abc"));
1396     test( startsWith("ab", ""));
1397     test(!startsWith("", "xx"));
1398 
1399     test( startsWith([1,2,3,4], [1,2,3]));
1400     test( startsWith([1,2,3,4], [1,2,3,4]));
1401     test(!startsWith([1,2], [1,2,3]));
1402     test( startsWith([1,2], (int[]).init));
1403     test(!startsWith((int[]).init, [1,2]));
1404 }
1405 
1406 /*******************************************************************************
1407 
1408     Check if the given array ends with the given suffix
1409 
1410     Template Params:
1411         T = The type of the array element
1412 
1413     Params:
1414         arr    = The array to be tested
1415         suffix = The suffix to test for
1416 
1417     Returns:
1418         True if the array ends with the suffix, false otherwise
1419 
1420 *******************************************************************************/
1421 
1422 bool endsWith ( T ) ( in T[] arr, in T[] suffix )
1423 {
1424     // https://issues.dlang.org/show_bug.cgi?id=17917
1425     // WORKAROUND: with -O -cov compiler doesn't early return from
1426     // && expression, thus the code here is manually split into two
1427     // conditions
1428     if (arr.length < suffix.length)
1429         return false;
1430     return arr[$ - suffix.length .. $] == suffix[];
1431 }
1432 
1433 unittest
1434 {
1435     test( endsWith("abcd", "bcd"));
1436     test( endsWith("abcd", "abcd"));
1437     test(!endsWith("ab", "abc"));
1438     test( endsWith("ab", ""));
1439     test(!endsWith("", "xx"));
1440 
1441     test( endsWith([1,2,3,4], [2,3,4]));
1442     test( endsWith([1,2,3,4], [1,2,3,4]));
1443     test(!endsWith([1,2], [1,2,3]));
1444     test( endsWith([1,2], (int[]).init));
1445     test(!endsWith((int[]).init, [1,2]));
1446 }
1447 
1448 /*******************************************************************************
1449 
1450     Remove the given prefix from the given array.
1451 
1452     Template Params:
1453         T = The type of the array element
1454 
1455     Params:
1456         arr    = The array from which the prefix is to be removed
1457         prefix = The prefix to remove
1458 
1459     Returns:
1460         A slice without the prefix if successful, the original array otherwise
1461 
1462 *******************************************************************************/
1463 
1464 public T1[] removePrefix ( T1, T2 ) ( T1[] arr, in T2[] prefix )
1465 {
1466     return ((arr.length >= prefix.length) && (startsWith(arr, prefix))
1467                 ? arr[prefix.length .. $]
1468                 : arr);
1469 }
1470 
1471 unittest
1472 {
1473     test(removePrefix("abcd", "abc") == "d");
1474     test(removePrefix("abcd", "abcd") == "");
1475     test(removePrefix("abcd", "abcde") == "abcd");
1476     test(removePrefix("abcd", "") == "abcd");
1477     test(removePrefix("", "xx") == "");
1478     test("abcd".removePrefix("abc") == "d");
1479     test("abcd".removePrefix("abcd") == "");
1480     test("abcd".removePrefix("abcde") == "abcd");
1481     test("abcd".removePrefix("") == "abcd");
1482     test("".removePrefix("xx") == "");
1483 
1484     test(removePrefix([1,2,3,4], [1,2,3]) == [ 4 ]);
1485     test(removePrefix([1,2,3,4], [1,2,3,4]) == cast(int[]) null);
1486     test(removePrefix([1,2], [1,2,3]) == [ 1, 2 ]);
1487     test(removePrefix([1,2], (int[]).init) == [ 1, 2 ]);
1488     test(removePrefix((int[]).init, [1,2]) == cast(int[]) null);
1489 }
1490 
1491 /*******************************************************************************
1492 
1493     Remove the given suffix from the given array.
1494 
1495     Template Params:
1496         T = The type of the array element
1497 
1498     Params:
1499         arr    = The array from which the suffix is to be removed
1500         suffix = The suffix to remove
1501 
1502     Returns:
1503         A slice without the suffix if successful, the original array otherwise
1504 
1505 *******************************************************************************/
1506 
1507 public T1[] removeSuffix ( T1, T2 ) ( T1[] arr, in T2[] suffix )
1508 {
1509     return ((arr.length >= suffix.length) && (endsWith(arr, suffix))
1510                 ? arr[0 .. $-suffix.length]
1511                 : arr);
1512 }
1513 
1514 unittest
1515 {
1516     test(removeSuffix("abcd", "cd") == "ab");
1517     test(removeSuffix("abcd", "abcd") == "");
1518     test(removeSuffix("abcd", "abcde") == "abcd");
1519     test(removeSuffix("abcd", "") == "abcd");
1520     test(removeSuffix("", "xx") == "");
1521     test("abcd".removeSuffix("cd") == "ab");
1522     test("abcd".removeSuffix("abcd") == "");
1523     test("abcd".removeSuffix("abcde") == "abcd");
1524     test("abcd".removeSuffix("") == "abcd");
1525     test("".removeSuffix("xx") == "");
1526 
1527     test(removeSuffix([1,2,3,4], [2,3,4]) == [ 1 ]);
1528     test(removeSuffix([1,2,3,4], [1,2,3,4]) == cast(int[]) null);
1529     test(removeSuffix([1,2], [1,2,3]) == [ 1, 2 ]);
1530     test(removeSuffix([1,2], (int[]).init) == [ 1, 2 ]);
1531     test(removeSuffix((int[]).init, [1,2]) == cast(int[]) null);
1532 }