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