1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 /**
6  * This module defines primitives for working with ranges.
7  *
8  * Copyright: Eugene Wissner 2017-2020.
9  * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
10  *                  Mozilla Public License, v. 2.0).
11  * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
12  * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/range/primitive.d,
13  *                 tanya/range/primitive.d)
14  */
15 module tanya.range.primitive;
16 
17 import std.algorithm.comparison;
18 import tanya.memory.lifetime;
19 import tanya.meta.trait;
20 import tanya.meta.transform;
21 import tanya.range.array;
22 
23 /**
24  * Returns the element type of the range $(D_PARAM R).
25  *
26  * Element type is the return type of such primitives like
27  * $(D_INLINECODE R.front) and (D_INLINECODE R.back) or the array base type.
28  * If $(D_PARAM R) is not a range, its element type is $(D_KEYWORD void).
29  *
30  * If $(D_PARAM R) is a string, $(D_PSYMBOL ElementType) doesn't distinguish
31  * between narrow and wide strings, it just returns the base type of the
32  * underlying array ($(D_KEYWORD char), $(D_KEYWORD wchar) or
33  * $(D_KEYWORD dchar)).
34  *
35  * Params:
36  *  R = Range type.
37  *
38  * Returns: Element type of the range $(D_PARAM R).
39  */
40 template ElementType(R)
41 {
42     static if (is(R U : U[]))
43     {
44         alias ElementType = U;
45     }
46     else static if (isInputRange!R)
47     {
48         alias ElementType = ReturnType!((R r) => r.front());
49     }
50     else
51     {
52         alias ElementType = void;
53     }
54 }
55 
56 /**
57  * Detects whether $(D_PARAM R) has a length property.
58  *
59  * $(D_PARAM R) does not have to be a range to support the length.
60  *
61  * Length mustn't be a $(D_KEYWORD @property) or a function, it can be a member
62  * variable or $(D_KEYWORD enum). But its type (or the type returned by the
63  * appropriate function) should be $(D_KEYWORD size_t), otherwise
64  * $(D_PSYMBOL hasLength) is $(D_KEYWORD false).
65  *
66  * All dynamic arrays except $(D_KEYWORD void)-arrays have length.
67  *
68  * Params:
69  *  R = A type.
70  *
71  * Returns: $(D_KEYWORD true) if $(D_PARAM R) has a length property,
72  *          $(D_KEYWORD false) otherwise.
73  *
74  * See_Also: $(D_PSYMBOL isInfinite).
75  */
76 enum bool hasLength(R) = is(ReturnType!((R r) => r.length) == size_t);
77 
78 ///
79 @nogc nothrow pure @safe unittest
80 {
81     static assert(hasLength!(char[]));
82     static assert(hasLength!(int[]));
83     static assert(hasLength!(const(int)[]));
84 
85     struct A
86     {
87         enum size_t length = 1;
88     }
89     static assert(hasLength!(A));
90 
91     struct B
92     {
93         @property size_t length() const @nogc nothrow pure @safe
94         {
95             return 0;
96         }
97     }
98     static assert(hasLength!(B));
99 
100     struct C
101     {
102         @property const(size_t) length() const @nogc nothrow pure @safe
103         {
104             return 0;
105         }
106     }
107     static assert(!hasLength!C);
108 }
109 
110 /**
111  * Determines whether $(D_PARAM R) is a forward range with slicing support
112  * ($(D_INLINECODE R[i .. j])).
113  *
114  * For finite ranges, the result of `opSlice()` must be of the same type as the
115  * original range. If the range defines opDollar, it must support subtraction.
116  *
117  * For infinite ranges, the result of `opSlice()` must be of the same type as
118  * the original range only if it defines `opDollar()`. Otherwise it can be any
119  * forward range.
120  *
121  * For both finite and infinite ranges, the result of `opSlice()` must have
122  * length.
123  *
124  * Params:
125  *  R = The type to be tested.
126  *
127  * Returns: $(D_KEYWORD true) if $(D_PARAM R) supports slicing,
128  *          $(D_KEYWORD false) otherwise.
129  */
130 template hasSlicing(R)
131 {
132     private enum bool hasDollar = is(typeof((R r) => r[0 .. $]));
133     private enum bool subDollar = !hasDollar
134                                || isInfinite!R
135                                || is(ReturnType!((R r) => r[0 .. $ - 1]) == R);
136 
137     static if (isForwardRange!R
138             && is(ReturnType!((R r) => r[0 .. 0]) T)
139             && (!hasDollar || is(ReturnType!((R r) => r[0 .. $]) == R))
140             && subDollar
141             && isForwardRange!(ReturnType!((ref R r) => r[0 .. 0])))
142     {
143         enum bool hasSlicing = (is(T == R) || isInfinite!R)
144                             && hasLength!T;
145     }
146     else
147     {
148         enum bool hasSlicing = false;
149     }
150 }
151 
152 ///
153 @nogc nothrow pure @safe unittest
154 {
155     static assert(hasSlicing!(int[]));
156     static assert(hasSlicing!(const(int)[]));
157     static assert(hasSlicing!(dstring));
158     static assert(hasSlicing!(string));
159     static assert(!hasSlicing!(const int[]));
160     static assert(!hasSlicing!(void[]));
161 
162     struct A
163     {
164         int front() @nogc nothrow pure @safe
165         {
166             return 0;
167         }
168         void popFront() @nogc nothrow pure @safe
169         {
170         }
171         bool empty() const  @nogc nothrow pure @safe
172         {
173             return false;
174         }
175         typeof(this) save() @nogc nothrow pure @safe
176         {
177             return this;
178         }
179         @property size_t length() const @nogc nothrow pure @safe
180         {
181             return 0;
182         }
183         typeof(this) opSlice(const size_t i, const size_t j)
184         pure nothrow  @safe @nogc
185         {
186             return this;
187         }
188     }
189     static assert(hasSlicing!A);
190 
191     struct B
192     {
193         struct Dollar
194         {
195         }
196         int front() @nogc nothrow pure @safe
197         {
198             return 0;
199         }
200         void popFront() @nogc nothrow pure @safe
201         {
202         }
203         bool empty() const @nogc nothrow pure @safe
204         {
205             return false;
206         }
207         typeof(this) save() @nogc nothrow pure @safe
208         {
209             return this;
210         }
211         @property size_t length() const @nogc nothrow pure @safe
212         {
213             return 0;
214         }
215         @property Dollar opDollar() const @nogc nothrow pure @safe
216         {
217             return Dollar();
218         }
219         typeof(this) opSlice(const size_t i, const Dollar j)
220         pure nothrow  @safe @nogc
221         {
222             return this;
223         }
224     }
225     static assert(!hasSlicing!B);
226 
227     struct C
228     {
229         int front() @nogc nothrow pure @safe
230         {
231             return 0;
232         }
233         void popFront() @nogc nothrow pure @safe
234         {
235         }
236         enum bool empty = false;
237         typeof(this) save() @nogc nothrow pure @safe
238         {
239             return this;
240         }
241         typeof(this) opSlice(const size_t i, const size_t j)
242         pure nothrow  @safe @nogc
243         {
244             return this;
245         }
246     }
247     static assert(!hasSlicing!C);
248 
249     struct D
250     {
251         struct Range
252         {
253             int front() @nogc nothrow pure @safe
254             {
255                 return 0;
256             }
257             void popFront() @nogc nothrow pure @safe
258             {
259             }
260             bool empty() const @nogc nothrow pure @safe
261             {
262                 return true;
263             }
264             typeof(this) save() @nogc nothrow pure @safe
265             {
266                 return this;
267             }
268             @property size_t length() const @nogc nothrow pure @safe
269             {
270                 return 0;
271             }
272         }
273         int front() @nogc nothrow pure @safe
274         {
275             return 0;
276         }
277         void popFront() @nogc nothrow pure @safe
278         {
279         }
280         enum bool empty = false;
281         typeof(this) save() @nogc nothrow pure @safe
282         {
283             return this;
284         }
285         Range opSlice(const size_t i, const size_t j)
286         pure nothrow  @safe @nogc
287         {
288             return Range();
289         }
290     }
291     static assert(hasSlicing!D);
292 }
293 
294 private template isDynamicArrayRange(R)
295 {
296     static if (is(R E : E[]))
297     {
298         enum bool isDynamicArrayRange = !is(E == void);
299     }
300     else
301     {
302         enum bool isDynamicArrayRange = false;
303     }
304 }
305 
306 private struct Primitive(Candidate, string primitive)
307 {
308     auto ref returnType(ref Candidate candidate)
309     {
310         mixin("return candidate." ~ primitive ~ ";");
311     }
312 
313     alias ReturnType = .ReturnType!returnType;
314     static assert(!is(ReturnType == void));
315 
316     enum uint attributes = functionAttributes!returnType
317                          & FunctionAttribute.ref_;
318 
319     bool opEquals(That)(That) const
320     {
321         return is(ReturnType == That.ReturnType)
322             && attributes == That.attributes;
323     }
324 }
325 
326 /**
327  * Determines whether $(D_PARAM R) is an input range.
328  *
329  * An input range should define following primitives:
330  *
331  * $(UL
332  *  $(LI front)
333  *  $(LI empty)
334  *  $(LI popFront)
335  * )
336  *
337  * Params:
338  *  R = The type to be tested.
339  *
340  * Returns: $(D_KEYWORD true) if $(D_PARAM R) is an input range,
341  *          $(D_KEYWORD false) otherwise.
342  */
343 template isInputRange(R)
344 {
345     static if (is(Primitive!(R, "front()") U)
346             && is(ReturnType!((R r) => r.empty) == bool)
347             && is(typeof(R.popFront())))
348     {
349         enum bool isInputRange = true;
350     }
351     else
352     {
353         enum bool isInputRange = isDynamicArrayRange!R;
354     }
355 }
356 
357 ///
358 @nogc nothrow pure @safe unittest
359 {
360     static struct Range
361     {
362         void popFront() @nogc nothrow pure @safe
363         {
364         }
365 
366         int front() @nogc nothrow pure @safe
367         {
368             return 0;
369         }
370 
371         bool empty() const @nogc nothrow pure @safe
372         {
373             return true;
374         }
375     }
376     static assert(isInputRange!Range);
377     static assert(isInputRange!(int[]));
378     static assert(!isInputRange!(void[]));
379 }
380 
381 /**
382  * Determines whether $(D_PARAM R) is a forward range.
383  *
384  * A forward range is an input range that also defines:
385  *
386  * $(UL
387  *  $(LI save)
388  * )
389  *
390  * Params:
391  *  R = The type to be tested.
392  *
393  * Returns: $(D_KEYWORD true) if $(D_PARAM R) is a forward range,
394  *          $(D_KEYWORD false) otherwise.
395  *
396  * See_Also: $(D_PSYMBOL isInputRange).
397  */
398 template isForwardRange(R)
399 {
400     static if (is(ReturnType!((R r) => r.save()) U))
401     {
402         enum bool isForwardRange = isInputRange!R && is(U == R);
403     }
404     else
405     {
406         enum bool isForwardRange = false;
407     }
408 }
409 
410 ///
411 @nogc nothrow pure @safe unittest
412 {
413     static struct Range
414     {
415         void popFront() @nogc nothrow pure @safe
416         {
417         }
418 
419         int front() @nogc nothrow pure @safe
420         {
421             return 0;
422         }
423 
424         bool empty() const @nogc nothrow pure @safe
425         {
426             return true;
427         }
428 
429         typeof(this) save() @nogc nothrow pure @safe
430         {
431             return this;
432         }
433     }
434     static assert(isForwardRange!Range);
435     static assert(isForwardRange!(int[]));
436     static assert(!isForwardRange!(void[]));
437 }
438 
439 /**
440  * Determines whether $(D_PARAM R) is a bidirectional range.
441  *
442  * A bidirectional range is a forward range that also defines:
443  *
444  * $(UL
445  *  $(LI back)
446  *  $(LI popBack)
447  * )
448  *
449  * Params:
450  *  R = The type to be tested.
451  *
452  * Returns: $(D_KEYWORD true) if $(D_PARAM R) is a bidirectional range,
453  *          $(D_KEYWORD false) otherwise.
454  *
455  * See_Also: $(D_PSYMBOL isForwardRange).
456  */
457 template isBidirectionalRange(R)
458 {
459     static if (is(Primitive!(R, "back()") U)
460             && is(typeof(R.popBack())))
461     {
462         enum bool isBidirectionalRange = isForwardRange!R
463                                       && (U() == Primitive!(R, "front()")());
464     }
465     else
466     {
467         enum bool isBidirectionalRange = isDynamicArrayRange!R;
468     }
469 }
470 
471 ///
472 @nogc nothrow pure @safe unittest
473 {
474     static struct Range
475     {
476         void popFront() @nogc nothrow pure @safe
477         {
478         }
479 
480         void popBack() @nogc nothrow pure @safe
481         {
482         }
483 
484         @property int front() @nogc nothrow pure @safe
485         {
486             return 0;
487         }
488 
489         @property int back() @nogc nothrow pure @safe
490         {
491             return 0;
492         }
493 
494         bool empty() const @nogc nothrow pure @safe
495         {
496             return true;
497         }
498 
499         Range save() @nogc nothrow pure @safe
500         {
501             return this;
502         }
503     }
504     static assert(isBidirectionalRange!Range);
505     static assert(isBidirectionalRange!(int[]));
506     static assert(!isBidirectionalRange!(void[]));
507 }
508 
509 /**
510  * Determines whether $(D_PARAM R) is a random-access range.
511  *
512  * A random-access range is a range that allows random access to its
513  * elements by index using $(D_INLINECODE [])-operator (defined with
514  * $(D_INLINECODE opIndex())). Further a random access range should
515  * have a length or be infinite.
516  *
517  * Params:
518  *  R = The type to be tested.
519  *
520  * Returns: $(D_KEYWORD true) if $(D_PARAM R) is a random-access range,
521  *          $(D_KEYWORD false) otherwise.
522  *
523  * See_Also: $(D_PSYMBOL isInfinite),
524  *           $(D_PSYMBOL hasLength).
525  *
526  * Note: This definition differs from `std.range.primitives.isRandomAccessRange`
527  *  in the D standard library in that it does not also require $(D_PARAM R) to
528  *  be a forward range and a bidirectional range. Those properties may be tested
529  *  separately with $(D_PSYMBOL isForwardRange) and
530  *  $(D_PSYMBOL isBidirectionalRange).
531  */
532 template isRandomAccessRange(R)
533 {
534     static if (is(Primitive!(R, "opIndex(size_t.init)") U))
535     {
536         enum bool isRandomAccessRange = isInputRange!R
537                                      && (hasLength!R || isInfinite!R)
538                                      && (U() == Primitive!(R, "front()")());
539     }
540     else
541     {
542         enum bool isRandomAccessRange = isDynamicArrayRange!R;
543     }
544 }
545 
546 ///
547 @nogc nothrow pure @safe unittest
548 {
549     static struct A
550     {
551         void popFront() @nogc nothrow pure @safe
552         {
553         }
554 
555         @property int front() @nogc nothrow pure @safe
556         {
557             return 0;
558         }
559 
560         bool empty() const @nogc nothrow pure @safe
561         {
562             return true;
563         }
564 
565         int opIndex(size_t) @nogc nothrow pure @safe
566         {
567             return 0;
568         }
569 
570         size_t length() const @nogc nothrow pure @safe
571         {
572             return 0;
573         }
574     }
575     static assert(isRandomAccessRange!A);
576     static assert(isRandomAccessRange!(int[]));
577     static assert(!isRandomAccessRange!(void[]));
578 
579     static struct B
580     {
581         void popFront() @nogc nothrow pure @safe
582         {
583         }
584 
585         @property int front() @nogc nothrow pure @safe
586         {
587             return 0;
588         }
589 
590         enum bool empty = false;
591 
592         int opIndex(const size_t pos) @nogc nothrow pure @safe
593         {
594             return 0;
595         }
596     }
597     static assert(isRandomAccessRange!B);
598 }
599 
600 /**
601  * Puts $(D_PARAM e) into the $(D_PARAM range).
602  *
603  * $(D_PSYMBOL R) should be an output range for $(D_PARAM E), i.e. at least one
604  * of the following conditions should met:
605  *
606  * $(OL
607  *  $(LI $(D_PARAM e) can be put into $(D_PARAM range) using
608  *       $(D_INLINECODE range(e))
609  *  $(LI $(D_PARAM e) can be assigned to $(D_INLINECODE range.front))
610  *  )
611  * )
612  *
613  * The method to put $(D_PARAM e) into $(D_PARAM range) is chosen based on the
614  * order specified above.
615  *
616  * If $(D_PARAM E) is an input range and $(D_PARAM R) is an output range for
617  * its elements as well, use $(D_PSYMBOL tanya.algorithm.mutation.copy)
618  * instead.
619  *
620  * $(D_PARAM range) is advanced after putting an element into it if it is an
621  * input range that doesn't define a `put`-method.
622  *
623  * Params:
624  *  R     = Target range type.
625  *  E     = Source element type.
626  *  range = Target range.
627  *  e     = Source element.
628  *
629  * See_Also: $(D_PSYMBOL isOutputRange).
630  */
631 void put(R, E)(ref R range, auto ref E e)
632 {
633     static if (is(typeof((R r, E e) => r(e))))
634     {
635         range(e);
636     }
637     else static if (isInputRange!R
638                  && is(typeof((R r, E e) => r.front = e)))
639     {
640         range.front = e;
641         range.popFront();
642     }
643     else
644     {
645         static assert(false, R.stringof ~ " is not an output range for "
646                            ~ E.stringof);
647     }
648 }
649 
650 ///
651 @nogc nothrow pure @safe unittest
652 {
653     int[2] actual;
654     auto slice = actual[];
655 
656     put(slice, 2);
657     assert(actual == [2, 0]);
658 }
659 
660 ///
661 @nogc nothrow pure @safe unittest
662 {
663     static struct OpCall
664     {
665         int e;
666 
667         void opCall(int e)
668         {
669             this.e = e;
670         }
671     }
672     OpCall oc;
673     put(oc, 2);
674     assert(oc.e == 2);
675 }
676 
677 /**
678  * Determines whether $(D_PARAM R) is an output range for the elemens of type
679  * $(D_PARAM E).
680  *
681  * If $(D_PARAM R) is an output range for the elements of type $(D_PARAM E)
682  * if an element `e` of type $(D_PARAM E) can be put into the range instance
683  * `r` in one of the following ways:
684  *
685  * $(TABLE
686  *  $(TR
687  *      $(TH Code)
688  *      $(TH Scenario)
689  *  )
690  *  $(TR
691  *      $(TD r(e))
692  *      $(TD $(D_PARAM R) defines `opCall` for $(D_PARAM E).)
693  *  )
694  *  $(TR
695  *      $(TD r.front = e)
696  *      $(TD $(D_PARAM R) is an input range with assignable elements of type
697  *           $(D_PARAM E).)
698  *  )
699  * )
700  *
701  * Output ranges don't have element type (so $(D_PSYMBOL ElementType) returns
702  * $(D_KEYWORD void) when applied to an output range). It is because an output
703  * range can support puting differently typed elements into it.
704  *
705  * Params:
706  *  R = The type to be tested.
707  *  E = Element type should be tested for.
708  *
709  * Returns: $(D_KEYWORD true) if $(D_PARAM R) is an output range for the
710  *          elements of the type $(D_PARAM E), $(D_KEYWORD false) otherwise.
711  *
712  * See_Also: $(D_PSYMBOL put).
713  */
714 template isOutputRange(R, E)
715 {
716     static if (is(typeof((R r, E e) => put(r, e))))
717     {
718         enum bool isOutputRange = true;
719     }
720     else static if (isInputRange!E)
721     {
722         pragma(msg, "Deprecation. An input range whose element type is "
723                   ~ "supported by the output range isn't considered itself to "
724                   ~ "be a source for such an output range. Don't rely on this "
725                   ~ "behavior and use tanya.algorithm.copy() to write one "
726                   ~ "range into another one.");
727         alias ET = ElementType!E;
728         enum bool isOutputRange = is(typeof((R r, ET e) => put(r, e)));
729     }
730     else
731     {
732         enum bool isOutputRange = false;
733     }
734 }
735 
736 ///
737 @nogc nothrow pure @safe unittest
738 {
739     static struct R1
740     {
741         void opCall(int) @nogc nothrow pure @safe
742         {
743         }
744     }
745     static assert(isOutputRange!(R1, int));
746 
747     static struct R2
748     {
749         int value;
750 
751         void popFront() @nogc nothrow pure @safe
752         {
753         }
754 
755         ref int front() @nogc nothrow pure @safe
756         {
757             return value;
758         }
759 
760         bool empty() const @nogc nothrow pure @safe
761         {
762             return true;
763         }
764     }
765     static assert(isOutputRange!(R2, int));
766 
767     static struct R3
768     {
769         void popFront() @nogc nothrow pure @safe
770         {
771         }
772 
773         int front() @nogc nothrow pure @safe
774         {
775             return 0;
776         }
777 
778         bool empty() const @nogc nothrow pure @safe
779         {
780             return true;
781         }
782     }
783     static assert(!isOutputRange!(R3, int));
784 }
785 
786 /**
787  * Determines whether $(D_PARAM R) is an infinite range.
788  *
789  * An infinite range is an input range whose `empty` member is defined as
790  * $(D_KEYWORD enum) which is always $(D_KEYWORD false).
791  *
792  * Params:
793  *  R = A type.
794  *
795  * Returns: $(D_KEYWORD true) if $(D_PARAM R) is an infinite range,
796  *          $(D_KEYWORD false) otherwise.
797  */
798 template isInfinite(R)
799 {
800     static if (isInputRange!R && is(typeof({enum bool e = R.empty;})))
801     {
802         enum bool isInfinite = R.empty == false;
803     }
804     else
805     {
806         enum bool isInfinite = false;
807     }
808 }
809 
810 ///
811 @nogc nothrow pure @safe unittest
812 {
813     static assert(!isInfinite!int);
814 
815     static struct NotRange
816     {
817         enum bool empty = false;
818     }
819     static assert(!isInfinite!NotRange);
820 
821     static struct InfiniteRange
822     {
823         void popFront() @nogc nothrow pure @safe
824         {
825         }
826         @property int front() @nogc nothrow pure @safe
827         {
828             return 0;
829         }
830         enum bool empty = false;
831     }
832     static assert(isInfinite!InfiniteRange);
833 
834     static struct InputRange
835     {
836         void popFront() @nogc nothrow pure @safe
837         {
838         }
839         @property int front() @nogc nothrow pure @safe
840         {
841             return 0;
842         }
843         @property bool empty() const @nogc nothrow pure @safe
844         {
845             return false;
846         }
847     }
848     static assert(!isInfinite!InputRange);
849 }
850 
851 /**
852  * Removes exactly $(D_PARAM count) first elements from the input range
853  * $(D_PARAM range).
854  *
855  * $(D_PARAM R) must have length or be infinite.
856  *
857  * Params:
858  *  R     = Range type.
859  *  range = Some input range.
860  *  count = Number of elements to remove.
861  *
862  * See_Also: $(D_PSYMBOL popBackExactly),
863  *           $(D_PSYMBOL popFrontN),
864  *           $(D_PSYMBOL isInputRange),
865  *           $(D_PSYMBOL hasLength),
866  *           $(D_PSYMBOL isInfinite).
867  *
868  * Precondition: If $(D_PARAM R) has length, it must be less than or equal to
869  *               $(D_PARAM count).
870  */
871 void popFrontExactly(R)(ref R range, size_t count)
872 if (isInputRange!R && (hasLength!R || isInfinite!R))
873 in
874 {
875     static if (hasLength!R)
876     {
877         assert(count <= range.length);
878     }
879 }
880 do
881 {
882     static if (hasSlicing!R)
883     {
884         range = range[count .. $];
885     }
886     else
887     {
888         while (count-- != 0)
889         {
890             range.popFront();
891         }
892     }
893 }
894 
895 ///
896 @nogc nothrow pure @safe unittest
897 {
898     int[5] a = [1, 2, 3, 4, 5];
899     auto slice = a[];
900 
901     popFrontExactly(slice, 3);
902     assert(slice.length == 2);
903     assert(slice[0] == 4);
904     assert(slice[$ - 1] == 5);
905 
906     popFrontExactly(slice, 2);
907     assert(slice.length == 0);
908 }
909 
910 /**
911  * Removes exactly $(D_PARAM count) last elements from the bidirectional range
912  * $(D_PARAM range).
913  *
914  * $(D_PARAM R) must have length or be infinite.
915  *
916  * Params:
917  *  R     = Range type.
918  *  range = Some bidirectional range.
919  *  count = Number of elements to remove.
920  *
921  * See_Also: $(D_PSYMBOL popFrontExactly),
922  *           $(D_PSYMBOL popBackN),
923  *           $(D_PSYMBOL isBidirectionalRange),
924  *           $(D_PSYMBOL hasLength),
925  *           $(D_PSYMBOL isInfinite).
926  *
927  * Precondition: If $(D_PARAM R) has length, it must be less than or equal to
928  *               $(D_PARAM count).
929  */
930 void popBackExactly(R)(ref R range, size_t count)
931 if (isBidirectionalRange!R && (hasLength!R || isInfinite!R))
932 in
933 {
934     static if (hasLength!R)
935     {
936         assert(count <= range.length);
937     }
938 }
939 do
940 {
941     static if (hasSlicing!R)
942     {
943         range = range[0 .. $ - count];
944     }
945     else
946     {
947         while (count-- != 0)
948         {
949             range.popBack();
950         }
951     }
952 }
953 
954 ///
955 @nogc nothrow pure @safe unittest
956 {
957     int[5] a = [1, 2, 3, 4, 5];
958     auto slice = a[];
959 
960     popBackExactly(slice, 3);
961     assert(slice.length == 2);
962     assert(slice[0] == 1);
963     assert(slice[$ - 1] == 2);
964 
965     popBackExactly(slice, 2);
966     assert(slice.length == 0);
967 }
968 
969 /**
970  * Removes maximum $(D_PARAM count) first elements from the input range
971  * $(D_PARAM range).
972  *
973  * Params:
974  *  R     = Range type.
975  *  range = Some input range.
976  *  count = Number of elements to remove.
977  *
978  * See_Also: $(D_PSYMBOL popBackN),
979  *           $(D_PSYMBOL popFrontExactly),
980  *           $(D_PSYMBOL isInputRange).
981  */
982 void popFrontN(R)(ref R range, size_t count)
983 if (isInputRange!R)
984 {
985     static if (hasLength!R && hasSlicing!R)
986     {
987         range = range[min(count, range.length) .. $];
988     }
989     else static if (hasLength!R)
990     {
991         size_t length = min(count, range.length);
992         while (length--)
993         {
994             range.popFront();
995         }
996     }
997     else
998     {
999         while (count-- != 0 && !range.empty)
1000         {
1001             range.popFront();
1002         }
1003     }
1004 }
1005 
1006 ///
1007 @nogc nothrow pure @safe unittest
1008 {
1009     int[5] a = [1, 2, 3, 4, 5];
1010     auto slice = a[];
1011 
1012     popFrontN(slice, 3);
1013     assert(slice.length == 2);
1014     assert(slice[0] == 4);
1015     assert(slice[$ - 1] == 5);
1016 
1017     popFrontN(slice, 20);
1018     assert(slice.length == 0);
1019 }
1020 
1021 /**
1022  * Removes maximum $(D_PARAM count) last elements from the bidirectional range
1023  * $(D_PARAM range).
1024  *
1025  * Params:
1026  *  R     = Range type.
1027  *  range = Some bidirectional range.
1028  *  count = Number of elements to remove.
1029  *
1030  * See_Also: $(D_PSYMBOL popFrontN),
1031  *           $(D_PSYMBOL popBackExactly),
1032  *           $(D_PSYMBOL isBidirectionalRange).
1033  */
1034 void popBackN(R)(ref R range, size_t count)
1035 if (isBidirectionalRange!R)
1036 {
1037     static if (hasLength!R && hasSlicing!R)
1038     {
1039         range = range[0 .. $ - min(count, range.length)];
1040     }
1041     else static if (hasLength!R)
1042     {
1043         size_t length = min(count, range.length);
1044         while (length--)
1045         {
1046             range.popBack();
1047         }
1048     }
1049     else
1050     {
1051         while (count-- != 0 && !range.empty)
1052         {
1053             range.popBack();
1054         }
1055     }
1056 }
1057 
1058 ///
1059 @nogc nothrow pure @safe unittest
1060 {
1061     int[5] a = [1, 2, 3, 4, 5];
1062     auto slice = a[];
1063 
1064     popBackN(slice, 3);
1065     assert(slice.length == 2);
1066     assert(slice[0] == 1);
1067     assert(slice[$ - 1] == 2);
1068 
1069     popBackN(slice, 20);
1070     assert(slice.length == 0);
1071 }
1072 
1073 /**
1074  * Moves the front element of an input range.
1075  *
1076  * The front element is left in a valid but unspecified state.
1077  * $(D_PSYMBOL moveFront) doesn't advances the range, so `popFront` should be
1078  * probably called after this function.
1079  *
1080  * Params:
1081  *  R     = Type of the range.
1082  *  range = Input range.
1083  *
1084  * Returns: The front element of the $(D_PSYMBOL range).
1085  *
1086  * See_Also: $(D_PSYMBOL move).
1087  */
1088 ElementType!R moveFront(R)(R range)
1089 if (isInputRange!R)
1090 {
1091     static if (!hasElaborateCopyConstructor!(ElementType!R))
1092     {
1093         return range.front;
1094     }
1095     else static if (is(typeof(((ref ElementType!R e) => e)(range.front))))
1096     {
1097         return move(range.front);
1098     }
1099     else
1100     {
1101         static assert(false, "Front element cannot be moved");
1102     }
1103 }
1104 
1105 ///
1106 @nogc nothrow pure @safe unittest
1107 {
1108     // Has elements without a postblit constructor.
1109     int[2] a = 5;
1110 
1111     assert(moveFront(a[]) == 5);
1112 }
1113 
1114 /**
1115  * Moves the back element of a bidirectional range.
1116  *
1117  * The back element is left in a valid but unspecified state.
1118  * $(D_PSYMBOL moveBack) doesn't advances the range, so `popBack` should be
1119  * probably called after this function.
1120  *
1121  * Params:
1122  *  R     = Type of the range.
1123  *  range = Bidirectional range.
1124  *
1125  * Returns: The back element of the $(D_PSYMBOL range).
1126  *
1127  * See_Also: $(D_PSYMBOL move).
1128  */
1129 ElementType!R moveBack(R)(R range)
1130 if (isBidirectionalRange!R)
1131 {
1132     static if (!hasElaborateCopyConstructor!(ElementType!R))
1133     {
1134         return range.back;
1135     }
1136     else static if (is(typeof(((ref ElementType!R e) => e)(range.back))))
1137     {
1138         return move(range.back);
1139     }
1140     else
1141     {
1142         static assert(false, "Back element cannot be moved");
1143     }
1144 }
1145 
1146 ///
1147 @nogc nothrow pure @safe unittest
1148 {
1149     // Has elements without a postblit constructor.
1150     int[2] a = 5;
1151 
1152     assert(moveBack(a[]) == 5);
1153 }
1154 
1155 /**
1156  * Moves the element at the position $(D_PARAM n) out of the range.
1157  *
1158  * The moved element is left in a valid but unspecified state.
1159  *
1160  * Params:
1161  *  R     = Range type.
1162  *  range = Random-access range.
1163  *  n     = Element position.
1164  *
1165  * Returns: The element at the position $(D_PARAM n).
1166  *
1167  * See_Also: $(D_PSYMBOL move).
1168  */
1169 ElementType!R moveAt(R)(R range, size_t n)
1170 if (isRandomAccessRange!R)
1171 {
1172     static if (!hasElaborateCopyConstructor!(ElementType!R))
1173     {
1174         return range[n];
1175     }
1176     else static if (is(typeof(((ref ElementType!R e) => e)(range[0]))))
1177     {
1178         return move(range[n]);
1179     }
1180     else
1181     {
1182         static assert(false, "Random element cannot be moved");
1183     }
1184 }
1185 
1186 ///
1187 @nogc nothrow pure @safe unittest
1188 {
1189     // Has elements without a postblit constructor.
1190     int[3] a = 5;
1191 
1192     assert(moveAt(a[], 1) == 5);
1193 }
1194 
1195 /**
1196  * Determines whether $(D_PSYMBOL R) is a range containing mobile elements,
1197  * i.e. elements that can be moved out of the range.
1198  *
1199  * Having mobile elements means for an input range to support
1200  * $(D_PSYMBOL moveFront), for a bidirectional range - both,
1201  * $(D_PSYMBOL moveFront) and $(D_PSYMBOL moveBack), for a random-access
1202  * range - $(D_PSYMBOL moveFront) and $(D_PSYMBOL moveAt).
1203  *
1204  * Params:
1205  *  R = Range type.
1206  *
1207  * Returns: $(D_KEYWORD true) if $(D_PSYMBOL R) has mobile elements,
1208  *          $(D_KEYWORD false) otherwise.
1209  *
1210  * See_Also: $(D_PSYMBOL moveFront), $(D_PSYMBOL moveBack),
1211  *           $(D_PSYMBOL moveAt).
1212  */
1213 template hasMobileElements(R)
1214 {
1215     static if (isRandomAccessRange!R)
1216     {
1217         enum bool hasMobileElements = is(typeof((R r) => moveFront(r)))
1218                                    && is(typeof((R r) => moveAt(r, 0)));
1219     }
1220     else static if (isBidirectionalRange!R)
1221     {
1222         enum bool hasMobileElements = is(typeof((R r) => moveFront(r)))
1223                                    && is(typeof((R r) => moveBack(r)));
1224     }
1225     else
1226     {
1227         enum bool hasMobileElements = is(typeof((R r) => moveFront(r)));
1228     }
1229 }
1230 
1231 ///
1232 @nogc nothrow pure @safe unittest
1233 {
1234     static assert(hasMobileElements!(int[]));
1235 }
1236 
1237 ///
1238 @nogc nothrow pure @safe unittest
1239 {
1240     static struct Element
1241     {
1242         this(this) @nogc nothrow pure @safe
1243         {
1244         }
1245     }
1246 
1247     static struct R1
1248     {
1249         enum bool empty = false;
1250 
1251         Element front() @nogc nothrow pure @safe
1252         {
1253             return Element();
1254         }
1255 
1256         void popFront() @nogc nothrow pure @safe
1257         {
1258         }
1259     }
1260     static assert(!hasMobileElements!R1);
1261 
1262     static struct R2
1263     {
1264         enum bool empty = false;
1265         private Element front_;
1266 
1267         ref Element front() @nogc nothrow pure @safe
1268         {
1269             return front_;
1270         }
1271 
1272         void popFront() @nogc nothrow pure @safe
1273         {
1274         }
1275     }
1276     static assert(hasMobileElements!R2);
1277 }
1278 
1279 /**
1280  * Determines whether $(D_PARAM R) provides access to its elements by
1281  * reference.
1282  *
1283  * Params:
1284  *  R = Range type.
1285  *
1286  * Returns: $(D_KEYWORD true) if $(D_PARAM R) has lvalue elements,
1287  *          $(D_KEYWORD false) otherwise.
1288  */
1289 template hasLvalueElements(R)
1290 {
1291     private alias refDg = (ref ElementType!R e) => &e;
1292 
1293     static if (isRandomAccessRange!R)
1294     {
1295         enum bool hasLvalueElements = is(typeof(refDg(R.init.front)))
1296                                    && is(typeof(refDg(R.init[0])));
1297     }
1298     else static if (isBidirectionalRange!R)
1299     {
1300         enum bool hasLvalueElements = is(typeof(refDg(R.init.front)))
1301                                    && is(typeof(refDg(R.init.back)));
1302     }
1303     else
1304     {
1305         enum bool hasLvalueElements = is(typeof(refDg(R.init.front)));
1306     }
1307 }
1308 
1309 ///
1310 @nogc nothrow pure @safe unittest
1311 {
1312     static struct R1
1313     {
1314         enum bool empty = false;
1315 
1316         int front() @nogc nothrow pure @safe
1317         {
1318             return 5;
1319         }
1320 
1321         void popFront() @nogc nothrow pure @safe
1322         {
1323         }
1324     }
1325     static assert(!hasLvalueElements!R1);
1326 
1327     static struct R2
1328     {
1329         int element;
1330         enum bool empty = false;
1331 
1332         ref const(int) front() const @nogc nothrow pure @safe
1333         {
1334             return element;
1335         }
1336 
1337         void popFront() @nogc nothrow pure @safe
1338         {
1339         }
1340 
1341         ref const(int) opIndex(size_t) const @nogc nothrow pure @safe
1342         {
1343             return element;
1344         }
1345     }
1346     static assert(hasLvalueElements!R2);
1347 }
1348 
1349 /**
1350  * Determines whether the elements of $(D_PARAM R) are assignable.
1351  *
1352  * Params:
1353  *  R = Range type.
1354  *
1355  * Returns: $(D_KEYWORD true) if the elements of $(D_PARAM R) are assignable
1356  *          $(D_KEYWORD false) otherwise.
1357  */
1358 template hasAssignableElements(R)
1359 {
1360     static if (isRandomAccessRange!R)
1361     {
1362         enum bool assignable = is(typeof({R.init.front = R.init.front;}))
1363                             && is(typeof({R.init[0] = R.init[0];}));
1364     }
1365     else static if (isBidirectionalRange!R)
1366     {
1367         enum bool assignable = is(typeof({R.init.front = R.init.front;}))
1368                             && is(typeof({R.init.back = R.init.back;}));
1369     }
1370     else
1371     {
1372         enum bool assignable = is(typeof({R.init.front = R.init.front;}));
1373     }
1374     enum bool hasAssignableElements = assignable;
1375 }
1376 
1377 ///
1378 @nogc nothrow pure @safe unittest
1379 {
1380     static struct R1
1381     {
1382         int element;
1383         enum bool empty = false;
1384 
1385         ref int front() @nogc nothrow pure @safe
1386         {
1387             return element;
1388         }
1389         alias back = front;
1390 
1391         void popFront() @nogc nothrow pure @safe
1392         {
1393         }
1394         alias popBack = popFront;
1395 
1396         R1 save() const @nogc nothrow pure @safe
1397         {
1398             return this;
1399         }
1400     }
1401     static assert(hasAssignableElements!R1);
1402 
1403     static struct R2
1404     {
1405         int element;
1406         enum bool empty = false;
1407 
1408         ref const(int) front() const @nogc nothrow pure @safe
1409         {
1410             return element;
1411         }
1412         alias back = front;
1413 
1414         void popFront() @nogc nothrow pure @safe
1415         {
1416         }
1417         alias popBack = popFront;
1418 
1419         R2 save() const @nogc nothrow pure @safe
1420         {
1421             return this;
1422         }
1423     }
1424     static assert(!hasAssignableElements!R2);
1425 }
1426 
1427 /**
1428  * Determines whether the elements of $(D_PSYMBOL R) can be swapped with
1429  * $(D_PSYMBOL swap).
1430  *
1431  * Params:
1432  *  R = Range type.
1433  *
1434  * Returns: $(D_KEYWORD true) if $(D_PARAM R) has swappable elements,
1435  *          $(D_KEYWORD false) otherwise.
1436  */
1437 template hasSwappableElements(R)
1438 {
1439     static if (isRandomAccessRange!R)
1440     {
1441         enum bool hasSwappableElements = is(typeof(swap(R.init.front, R.init.front)))
1442                                       && is(typeof(swap(R.init[0], R.init[0])));
1443     }
1444     else static if (isBidirectionalRange!R)
1445     {
1446         enum bool hasSwappableElements = is(typeof(swap(R.init.front, R.init.front)))
1447                                       && is(typeof(swap(R.init.back, R.init.back)));
1448     }
1449     else
1450     {
1451         enum bool hasSwappableElements = is(typeof(swap(R.init.front, R.init.front)));
1452     }
1453 }
1454 
1455 ///
1456 @nogc nothrow pure @safe unittest
1457 {
1458     static struct R1
1459     {
1460         int element;
1461         enum bool empty = false;
1462 
1463         ref int front() @nogc nothrow pure @safe
1464         {
1465             return element;
1466         }
1467         alias back = front;
1468 
1469         void popFront() @nogc nothrow pure @safe
1470         {
1471         }
1472         alias popBack = popFront;
1473 
1474         R1 save() const @nogc nothrow pure @safe
1475         {
1476             return this;
1477         }
1478     }
1479     static assert(hasSwappableElements!R1);
1480 
1481     static struct R2
1482     {
1483         int element;
1484         enum bool empty = false;
1485 
1486         int front() const @nogc nothrow pure @safe
1487         {
1488             return element;
1489         }
1490         alias back = front;
1491 
1492         void popFront() @nogc nothrow pure @safe
1493         {
1494         }
1495         alias popBack = popFront;
1496 
1497         R2 save() const @nogc nothrow pure @safe
1498         {
1499             return this;
1500         }
1501     }
1502     static assert(!hasSwappableElements!R2);
1503 }
1504 
1505 /**
1506  * Determines whether `r1.front` and `r2.front` point to the same element.
1507  *
1508  * Params:
1509  *  r1 = First range.
1510  *  r2 = Second range.
1511  *
1512  * Returns: $(D_KEYWORD true) if $(D_PARAM r1) and $(D_PARAM r2) have the same
1513  *          head, $(D_KEYWORD false) otherwise.
1514  */
1515 bool sameHead(Range)(Range r1, Range r2) @trusted
1516 if (isInputRange!Range && hasLvalueElements!Range)
1517 {
1518     return &r1.front is &r2.front;
1519 }
1520 
1521 ///
1522 @nogc nothrow pure @safe unittest
1523 {
1524     const int[2] array;
1525 
1526     auto r1 = array[];
1527     auto r2 = array[];
1528 
1529     assert(sameHead(r1, r2));
1530 }
1531 
1532 ///
1533 @nogc nothrow pure @safe unittest
1534 {
1535     const int[2] array;
1536 
1537     auto r1 = array[];
1538     auto r2 = array[1 .. $];
1539 
1540     assert(!sameHead(r1, r2));
1541 }
1542 
1543 /**
1544  * Returns the first element and advances the range.
1545  *
1546  * If $(D_PARAM range) has lvalue elements, then $(D_PSYMBOL getAndPopFront)
1547  * returns by reference, otherwise the returned element is copied.
1548  *
1549  * Params:
1550  *  R     = Input range type.
1551  *  range = Input range.
1552  *
1553  * Returns: Front range element.
1554  *
1555  * See_Also: $(D_PSYMBOL getAndPopBack).
1556  */
1557 ElementType!R getAndPopFront(R)(ref R range)
1558 if (isInputRange!R)
1559 in
1560 {
1561     assert(!range.empty);
1562 }
1563 do
1564 {
1565     static if (hasLvalueElements!R)
1566     {
1567         if (false)
1568         {
1569             // This code is removed by the compiler but ensures that
1570             // this function isn't @safe if range.front isn't @safe.
1571             auto _ = range.front();
1572         }
1573         auto el = (() @trusted => &range.front())();
1574     }
1575     else
1576     {
1577         auto el = range.front;
1578     }
1579     range.popFront();
1580     static if (hasLvalueElements!R)
1581     {
1582         return *el;
1583     }
1584     else
1585     {
1586         return el;
1587     }
1588 }
1589 
1590 ///
1591 @nogc nothrow pure @safe unittest
1592 {
1593     int[3] array = [1, 2, 3];
1594     auto slice = array[];
1595 
1596     assert(getAndPopFront(slice) == 1);
1597     assert(slice.length == 2);
1598 }
1599 
1600 /**
1601  * Returns the last element and removes it from the range.
1602  *
1603  * If $(D_PARAM range) has lvalue elements, then $(D_PSYMBOL getAndPopBack)
1604  * returns by reference, otherwise the returned element is copied.
1605  *
1606  * Params:
1607  *  R     = Bidirectional range type.
1608  *  range = Bidirectional range.
1609  *
1610  * Returns: Last range element.
1611  *
1612  * See_Also: $(D_PSYMBOL getAndPopFront).
1613  */
1614 auto ref getAndPopBack(R)(ref R range)
1615 if (isBidirectionalRange!R)
1616 in
1617 {
1618     assert(!range.empty);
1619 }
1620 do
1621 {
1622     static if (hasLvalueElements!R)
1623     {
1624         if (false)
1625         {
1626             // This code is removed by the compiler but ensures that
1627             // this function isn't @safe if range.back isn't @safe.
1628             auto _ = range.back();
1629         }
1630         auto el = (() @trusted => &range.back())();
1631     }
1632     else
1633     {
1634         auto el = range.back;
1635     }
1636     range.popBack();
1637     static if (hasLvalueElements!R)
1638     {
1639         return *el;
1640     }
1641     else
1642     {
1643         return el;
1644     }
1645 }
1646 
1647 ///
1648 @nogc nothrow pure @trusted unittest
1649 {
1650     int[3] array = [1, 2, 3];
1651     auto slice = array[];
1652 
1653     assert(getAndPopBack(slice) == 3);
1654     assert(slice.length == 2);
1655 }