1 /** Array container(s) with optional sortedness via template-parameter
2  * `Ordering` and optional use of GC via `useGCAllocation`.
3  *
4  * See_Also: https://en.wikipedia.org/wiki/Sorted_array
5  *
6  * TODO: UniqueArray!(const(E)) where E has indirections
7  *
8  * TODO: Support for constructing from r-value range (container) of non-copyable
9  * elements.
10  *
11  * TODO: Add some way to implement lazy sorting either for the whole array (via
12  * bool flag) or completeSort at a certain offset (extra member).
13  *
14  * TODO: Replace ` = void` with construction or emplace
15  *
16  * TODO: Break out common logic into private `DynamicArray` and reuse with `alias
17  * this` to express StandardArray, SortedArray, SortedSetArray
18  *
19  * TODO: Use std.array.insertInPlace in insert()?
20  * TODO: Use std.array.replaceInPlace?
21  *
22  * TODO: Use `std.algorithm.mutation.move` and `std.range.primitives.moveAt`
23  * when moving internal sub-slices
24  *
25  * TODO: Add `c.insertAfter(r, x)` where `c` is a collection, `r` is a range
26  * previously extracted from `c`, and `x` is a value convertible to
27  * collection's element type. See_Also:
28  * https://forum.dlang.org/post/n3qq6e$2bis$1@digitalmars.com
29  *
30  * TODO: replace qcmeman with std.experimental.allocator parameter defaulting to
31  * `Mallocator`
32  *
33  * TODO: use `integer_sorting.radixSort` when element type `isSigned` or `isUnsigned` and
34  * above or below a certain threshold calculated by my benchmarks
35  *
36  * TODO: Remove explicit moves when DMD std.algorithm.mutation.move calls these
37  * members for us (if they exist)
38  *
39  * See_Also: https://forum.dlang.org/post/rcufngzosbbhkvevryyd@forum.dlang.org
40  */
41 module nxt.generic_array;
42 
43 version(none):					// TODO: enable
44 
45 /// Array element ordering.
46 enum Ordering
47 {
48     unsorted, // unsorted array
49     sortedValues, // sorted array with possibly duplicate values
50     sortedUniqueSet, // sorted array with unique values
51 }
52 
53 /// Is `true` iff `ordering` is sorted.
54 enum isOrdered(Ordering ordering) = ordering != Ordering.unsorted;
55 
56 version(unittest)
57 {
58     import std.algorithm.iteration : map, filter;
59     import std.algorithm.comparison : equal;
60     import std.conv : to;
61     import std.meta : AliasSeq;
62     import core.internal.traits : Unqual;
63     import nxt.dbgio : dump;
64     import nxt.array_help : s;
65 }
66 
67 import nxt.container.traits : ContainerElementType;
68 
69 /// Is `true` iff `C` is an instance of an `Array` container.
70 template isArrayContainer(C)
71 {
72     enum isArrayContainer = is(C == Array!(_), _);
73 }
74 
75 /// Semantics of copy construction and copy assignment.
76 enum Assignment
77 {
78     disabled,           /// for reference counting use `std.typecons.RefCounted`. for safe slicing use `borrown`
79     move,               /// only move construction allowed
80     copy                /// always copy (often not the desirable)
81 }
82 
83 /** Array of value types `E` with optional sortedness/ordering.
84  *
85  * Always `@safe pure nothrow @nogc` when possible.
86  *
87  * `Assignment` either
88  * - is disabled
89  * - does Rust-style move, or
90  * - does C++-style copying
91  *
92  * Params:
93  * GCAllocation = `true` iff `GC.malloc` is used for store allocation,
94  * otherwise C's `{m,ce,re}alloc()` is used.
95  */
96 private struct Array(E,
97                      // TODO: merge these flags into one to reduce template bloat
98                      Assignment assignment = Assignment.disabled,
99                      Ordering ordering = Ordering.unsorted,
100                      bool useGCAllocation = false,
101                      Capacity = size_t, // see also https://github.com/izabera/s
102                      alias less = "a < b") // TODO: move out of this definition and support only for the case when `ordering` is not `Ordering.unsorted`
103 if (is(Capacity == ulong) ||           // 3 64-bit words
104     is(Capacity == uint))              // 2 64-bit words
105 {
106     import core.internal.traits : hasElaborateDestructor;
107     import core.lifetime : emplace, move, moveEmplace;
108     import std.algorithm.mutation : moveEmplaceAll;
109     import std.range.primitives : isInputRange, isInfinite, ElementType;
110     import std.traits : isIterable, isAssignable, Unqual, isArray, isScalarType, hasIndirections, TemplateOf;
111     import std.functional : binaryFun;
112     import std.meta : allSatisfy;
113 
114     import nxt.qcmeman : malloc, calloc, realloc, free, gc_addRange, gc_removeRange;
115 
116     private template shouldAddGCRange(T)
117     {
118         import std.traits : hasIndirections;
119         enum shouldAddGCRange = hasIndirections!T && !is(T == Array!(_), _); // TODO: unify to container_traits.shouldAddGCRange
120     }
121 
122     /// Mutable element type.
123     private alias MutableE = Unqual!E;
124 
125     /// Template for type of `this`.
126     private alias ThisTemplate = TemplateOf!(typeof(this));
127 
128     /// Same type as this but with mutable element type.
129     private alias MutableThis = ThisTemplate!(MutableE, assignment, ordering, useGCAllocation, Capacity, less);
130 
131     static if (useGCAllocation || // either we asked for allocation
132                shouldAddGCRange!E) // or we need GC ranges
133         import core.memory : GC;
134 
135     /// Is `true` iff `Array` can be interpreted as a narrow D `string` or `wstring`.
136     private enum isNarrowString = (is(MutableE == char) ||
137                                    is(MutableE == wchar));
138 
139     static if (isOrdered!ordering)
140         static assert(!isNarrowString, "A narrow string cannot be an ordered array because it's not random access'");
141 
142     alias comp = binaryFun!less; //< comparison
143 
144     /// Create a empty array.
145     // this(typeof(null)) nothrow
146     // {
147     //     version(showCtors) dump("ENTERING: smallCapacity:", smallCapacity, " @",  __PRETTY_FUNCTION__);
148     //     // nothing needed, rely on default initialization of data members
149     // }
150 
151     /// Returns: an array of length `initialLength` with all elements default-initialized to `ElementType.init`.
152     static typeof(this) withLength(size_t initialLength) @trusted
153     {
154         version(showCtors) dump("ENTERING: smallCapacity:", smallCapacity, " @",  __PRETTY_FUNCTION__);
155 
156         debug { typeof(return) that; }
157         else { typeof(return) that = void; }
158 
159         // TODO: functionize:
160         if (initialLength > smallCapacity)
161             emplace!Large(&that._large, initialLength, initialLength, true); // no elements so we need to zero
162         else
163             that._small.length = cast(ubyte)initialLength;
164 
165         version(showCtors) dump("EXITING: ", __PRETTY_FUNCTION__);
166         return that;
167     }
168 
169     /// Returns: an array with initial capacity `initialCapacity`.
170     static typeof(this) withCapacity(size_t initialCapacity) @trusted
171     {
172         version(showCtors) dump("ENTERING: smallCapacity:", smallCapacity, " @",  __PRETTY_FUNCTION__);
173 
174         debug { typeof(return) that; }
175         else { typeof(return) that = void; }
176 
177         if (initialCapacity > smallCapacity)
178             emplace!Large(&that._large, initialCapacity, 0, false);
179         else
180             that._small.length = 0;
181 
182         version(showCtors) dump("EXITING: ", __PRETTY_FUNCTION__);
183         return that;
184     }
185 
186     /// Returns: an array of one element `element`.
187     static typeof(this) withElement(E element) @trusted
188     {
189         version(showCtors) dump("ENTERING: smallCapacity:", smallCapacity, " @",  __PRETTY_FUNCTION__);
190 
191         debug { typeof(return) that; }
192         else { typeof(return) that = void; }
193 
194         // TODO: functionize:
195         enum initialLength = 1;
196         if (initialLength > smallCapacity)
197             emplace!Large(&that._large, initialLength, initialLength, false);
198         else
199             emplace!Small(&that._small, initialLength, false);
200 
201         // move element
202         static if (__traits(isPOD, E))
203             that._mptr[0] = element;
204         else static if (!shouldAddGCRange!E)
205             moveEmplace(*cast(MutableE*)&element, // TODO: can we prevent this cast?
206                         that._mptr[0]); // safe to cast away constness when no indirections
207         else
208             moveEmplace(element, that._mptr[0]); // TODO: remove `move` when compiler does it for us
209 
210         return that;
211     }
212 
213     /// Returns: an array of `Us.length` number of elements set to `elements`.
214     static typeof(this) withElements(Us...)(Us elements) @trusted
215     {
216         version(showCtors) dump("ENTERING: smallCapacity:", smallCapacity, " @",  __PRETTY_FUNCTION__);
217 
218         debug { typeof(return) that; }
219         else { typeof(return) that = void; }
220 
221         // TODO: functionize:
222         enum initialLength = Us.length;
223         if (initialLength > smallCapacity)
224             emplace!Large(&that._large, initialLength, initialLength, false);
225         else
226             emplace!Small(&that._small, initialLength, false);
227 
228         // move elements
229         foreach (immutable i, ref element; elements)
230         {
231             static if (!shouldAddGCRange!E)
232                 moveEmplace(*cast(MutableE*)&element,
233                             that._mptr[i]); // safe to cast away constness when no indirections
234             else
235                 moveEmplace(element, that._mptr[i]); // TODO: remove `move` when compiler does it for us
236         }
237 
238         static if (isOrdered!ordering)
239             that.sortElements!comp();
240 
241         return that;
242     }
243 
244     static if (assignment == Assignment.copy)
245     {
246         /// Copy construction.
247         this(this) @trusted
248         {
249             version(showCtors) dump("Copy ctor: ", typeof(this).stringof);
250             if (isLarge)        // only large case needs special treatment
251             {
252                 auto rhs_storePtr = _large.ptr; // save store pointer
253                 _large.setCapacity(this.length); // pack by default
254                 // _large.length already copied
255                 _large.ptr = allocate(this.length, false);
256                 foreach (immutable i; 0 .. this.length)
257                     _large.ptr[i] = rhs_storePtr[i];
258             }
259         }
260 
261         /// Copy assignment.
262         void opAssign(typeof(this) rhs) @trusted
263         {
264             version(showCtors) dump("Copy assign: ", typeof(this).stringof);
265             // self-assignment may happen when assigning derefenced pointer
266             if (isLarge)        // large = ...
267             {
268                 if (rhs.isLarge) // large = large
269                 {
270                     // TODO: functionize to Large.opAssign(Large rhs):
271                     if (_large.ptr != rhs._large.ptr) // if not self assignment
272                     {
273                         _large.length = rhs._large.length;
274                         reserve(rhs._large.length);
275                         foreach (immutable i; 0 .. rhs._large.length)
276                             _large.ptr[i] = rhs._large.ptr[i];
277                     }
278                 }
279                 else            // large = small
280                 {
281                     {            // make it small
282                         clear(); // clear large storage
283                         _large.isLarge = false; // TODO: needed?
284                     }
285                     _small = rhs._small; // small
286                 }
287             }
288             else                // small = ...
289             {
290                 if (rhs.isLarge) // small = large
291                 {
292                     {            // make it large
293                         clear(); // clear small storage
294                         _large.isLarge = true; // TODO: needed?
295                     }
296                     // TODO: functionize to Large.opAssign(Large rhs):
297                     if (_large.ptr != rhs._large.ptr) // if not self assignment
298                     {
299                         _large.length = rhs._large.length;
300                         reserve(rhs._large.length);
301                         foreach (immutable i; 0 .. rhs._large.length)
302                             _large.ptr[i] = rhs._large.ptr[i];
303                     }
304                 }
305                 else            // small = small
306                 {
307                     _small = rhs._small;
308                 }
309             }
310         }
311     }
312     else static if (assignment == Assignment.disabled)
313     {
314         @disable this(this);
315     }
316     else static if (assignment == Assignment.move)
317     {
318         /// Copy ctor moves.
319         this(typeof(this) rhs) @trusted
320         {
321             version(showCtors) dump("Copying: ", typeof(this).stringof);
322             assert(!isBorrowed);
323             moveEmplace(rhs, this); // TODO: remove `move` when compiler does it for us
324         }
325 
326         /// Assignment moves.
327         void opAssign(typeof(this) rhs) @trusted
328         {
329             assert(!isBorrowed);
330             import std.algorith.mutation : move;
331             move(rhs, this);  // TODO: remove `move` when compiler does it for us
332         }
333     }
334 
335     static if (__traits(isCopyable, E))
336     {
337         /// Returns: shallow duplicate of `this`.
338         @property MutableThis dup() const @trusted // `MutableThis` mimics behaviour of `dup` for builtin D arrays
339         {
340             debug { typeof(return) that; }
341             else { typeof(return) that = void; }
342             if (isLarge)
343             {
344                 emplace!(that.Large)(&that._large, _large.length, _large.length, false);
345                 foreach (immutable i; 0 .. _large.length)
346                 {
347                     // TODO: is there a more standardized way of solving this other than this hacky cast?
348                     that._large.ptr[i] = (cast(E*)_large.ptr)[i];
349                 }
350             }
351             else
352             {
353                 emplace!(that.Small)(&that._small, _small.length, false);
354                 // TODO: is there a more standardized way of solving this other than this hacky cast?
355                 that._small.elms[0 .. _small.length] = (cast(E[_small.elms.length])_small.elms)[0 .. _small.length]; // copy elements
356             }
357             return that;
358         }
359     }
360 
361     bool opEquals(in typeof(this) rhs) const @trusted
362     {
363         static if (__traits(isCopyable, E))
364             return this[] == rhs[]; // TODO: fix DMD to make this work for non-copyable aswell
365         else
366         {
367             if (this.length != rhs.length)
368 				return false;
369             foreach (immutable i; 0 .. this.length)
370                 if (this.ptr[i] != rhs.ptr[i])
371 					return false;
372             return true;
373         }
374     }
375 
376     /// Compare with range `R` with comparable element type.
377     bool opEquals(R)(R rhs) const
378 	if (isInputRange!R && !isInfinite!R)
379     {
380 		pragma(inline, true);
381         return opSlice.equal(rhs);
382     }
383 
384     /// Calculate D associative array (AA) key hash.
385     hash_t toHash() const nothrow @trusted // cannot currently be template-lazy
386     {
387         pragma(msg, "WARNING: using toHash() when we should use toDigest instead");
388         import core.internal.hash : hashOf;
389         static if (__traits(isCopyable, E))
390             return this.length ^ hashOf(slice());
391         else
392         {
393             typeof(return) hash = this.length;
394             foreach (immutable i; 0 .. this.length)
395                 hash ^= this.ptr[i].hashOf;
396             return hash;
397         }
398     }
399 
400     /** Construct from InputRange `values`.
401         If `values` are sorted `assumeSortedParameter` is `true`.
402 
403         TODO: Have `assumeSortedParameter` only when `isOrdered!ordering` is true
404      */
405     this(R)(R values, bool assumeSortedParameter = false) @trusted @("complexity", "O(n*log(n))")
406 	if (isIterable!R)
407     {
408         version(showCtors) dump("ENTERING: smallCapacity:", smallCapacity, " @",  __PRETTY_FUNCTION__);
409 
410         // append new data
411         import std.range.primitives : hasLength;
412         static if (hasLength!R)
413         {
414             // TODO: choose large or small depending on values.length
415             _small.isLarge = false;
416             _small.length = 0;
417 
418             reserve(values.length); // fast reserve
419             setOnlyLength(values.length);
420 
421             // static if (__traits(isRef))
422             // {
423             //     // TODO: dup elements
424             // }
425             // else
426             // {
427             //     // TODO: move elements
428             // }
429 
430             size_t i = 0;
431             foreach (ref value; move(values)) // TODO: remove `move` when compiler does it for us
432             {
433                 // TODO: functionize:
434                 static if (__traits(isPOD, typeof(value)))
435                     _mptr[i++] = value;
436                 else
437                     moveEmplace(value, _mptr[i++]);
438             }
439         }
440         else
441         {
442             // always start small
443             _small.isLarge = false;
444             _small.length = 0;
445 
446             size_t i = 0;
447             foreach (ref value; move(values)) // TODO: remove `move` when compiler does it for us
448             {
449                 reserve(i + 1); // slower reserve
450                 // TODO: functionize:
451                 static if (__traits(isPOD, typeof(value)))
452                     _mptr[i++] = value;
453                 else
454                     moveEmplace(value, _mptr[i++]);
455                 setOnlyLength(i); // must be set here because correct length is needed in reserve call above in this same scope
456             }
457         }
458 
459         if (!assumeSortedParameter)
460         {
461             static if (isOrdered!ordering)
462                 sortElements!comp();
463             static if (ordering == Ordering.sortedUniqueSet)
464             {
465                 import std.algorithm.iteration : uniq;
466                 size_t j = 0;
467                 foreach (ref e; uniq(slice))
468                 {
469                     auto ePtr = &e;
470                     const separate = ePtr != &_mptr[j];
471                     if (separate)
472                         move(*cast(MutableE*)ePtr, _mptr[j]);
473                     ++j;
474                 }
475                 shrinkTo(j);
476             }
477         }
478 
479         version(showCtors) dump("EXITING: ", __PRETTY_FUNCTION__);
480     }
481 
482     /// Sort all elements in-place regardless of `ordering`.
483     private void sortElements(alias comp_)() @trusted
484     {
485         import std.algorithm.sorting : sort;
486         sort!comp_(_mptr[0 .. length]);
487     }
488 
489     /// Reserve room for `newCapacity`.
490     size_t reserve(in size_t newCapacity) pure @trusted
491 	in(!isBorrowed)
492     {
493         if (newCapacity <= capacity)
494             return capacity;
495         if (isLarge)
496         {
497             static if (shouldAddGCRange!E)
498                 gc_removeRange(_mptr);
499             static if (__VERSION__ >= 2098) import std.math.algebraic : nextPow2; else import std.math : nextPow2;
500             reallocateLargeStoreAndSetCapacity(newCapacity.nextPow2);
501             static if (shouldAddGCRange!E)
502                 gc_addRange(_mptr, _large.capacity * E.sizeof);
503         }
504         else
505         {
506             if (newCapacity > smallCapacity) // convert to large
507             {
508                 auto tempLarge = Large(newCapacity, length, false);
509 
510                 // move small to temporary large
511                 foreach (immutable i; 0 .. length)
512                     moveEmplace(_small._mptr[i],
513                                 tempLarge._mptr[i]);
514 
515                 static if (hasElaborateDestructor!E)
516                     destroyElements();
517 
518                 // TODO: functionize:
519                 {               // make this large
520                     moveEmplace(tempLarge, _large);
521                 }
522 
523                 assert(isLarge);
524             }
525             else
526             {
527                 // staying small
528             }
529         }
530 		return capacity;
531     }
532 
533     /// Pack/Compress storage.
534     void compress() pure @trusted
535 	in(!isBorrowed)
536     {
537         if (isLarge)
538         {
539             if (this.length)
540             {
541                 if (this.length <= smallCapacity)
542                 {
543                     Small tempSmall = Small(length, false);
544 
545                     // move elements to temporary small. TODO: make moveEmplaceAll work on char[],char[] and use
546                     foreach (immutable i; 0 .. length)
547                         moveEmplace(_large._mptr[i],
548                                     tempSmall._mptr[i]);
549 
550                     // free existing large data
551                     static if (shouldAddGCRange!E)
552                         gc_removeRange(_mptr);
553 
554                     static if (useGCAllocation)
555                         GC.free(_mptr);
556                     else
557                         free(_mptr);
558 
559                     moveEmplace(tempSmall, _small);
560                 }
561                 else
562                 {
563                     if (_large.capacity != this.length)
564                     {
565                         static if (shouldAddGCRange!E)
566                             gc_removeRange(_mptr);
567                         reallocateLargeStoreAndSetCapacity(this.length);
568                         static if (shouldAddGCRange!E)
569                             gc_addRange(_mptr, _large.capacity * E.sizeof);
570                     }
571                 }
572             }
573             else                // if empty
574             {
575                 // free data
576                 static if (shouldAddGCRange!E)
577                     gc_removeRange(_mptr);
578 
579                 static if (useGCAllocation)
580                     GC.free(_mptr);
581                 else
582                     free(_mptr);
583 
584                 _large.capacity = 0;
585                 _large.ptr = null;
586             }
587         }
588     }
589     /// ditto
590     alias pack = compress;
591 
592     /// Reallocate storage. TODO: move to Large.reallocateAndSetCapacity
593     private void reallocateLargeStoreAndSetCapacity(in size_t newCapacity) pure @trusted
594     {
595         version(D_Coverage) {} else pragma(inline, true);
596         _large.setCapacity(newCapacity);
597         static if (useGCAllocation)
598             _large.ptr = cast(E*)GC.realloc(_mptr, E.sizeof * _large.capacity);
599         else                    // @nogc
600         {
601             _large.ptr = cast(E*)realloc(_mptr, E.sizeof * _large.capacity);
602             assert(_large.ptr, "Reallocation failed");
603         }
604     }
605 
606     /// Destruct.
607     ~this() nothrow @trusted @nogc
608 	in(!isBorrowed)
609     {
610         pragma(inline, true);
611         if (isLarge)
612             debug assert(_large.ptr != _ptrMagic, "Double free."); // trigger fault for double frees
613         release();
614         if (isLarge)
615             debug _large.ptr = _ptrMagic; // tag as freed
616     }
617 
618     /// Empty.
619     void clear() @nogc
620 	in(!isBorrowed)
621     {
622         pragma(inline, true);
623         release();
624         resetInternalData();
625     }
626     /// ditto
627     void opAssign(typeof(null))
628     {
629 		pragma(inline, true);
630         clear();
631     }
632 
633     /// Destroy elements.
634     static if (hasElaborateDestructor!E)
635     {
636         private void destroyElements() @trusted
637         {
638             foreach (immutable i; 0 .. this.length) // TODO: move to a common place
639                 .destroy(_mptr[i]);
640         }
641     }
642 
643     /// Release internal store.
644     private void release() @trusted @nogc
645     {
646         static if (hasElaborateDestructor!E)
647             destroyElements();
648         if (isLarge)
649         {
650             static if (shouldAddGCRange!E)
651                 gc_removeRange(_large.ptr);
652 
653             static if (useGCAllocation)
654                 GC.free(_large.ptr);
655             else                // @nogc
656             {
657                 static if (!shouldAddGCRange!E)
658                     free(cast(MutableE*)_large.ptr); // safe to case away constness
659                 else
660                     free(_large.ptr);
661             }
662         }
663     }
664 
665     /// Reset internal data.
666     private void resetInternalData() @trusted pure @nogc
667     {
668 		pragma(inline, true);
669         if (isLarge)
670         {
671             _large.ptr = null;
672             _large.length = 0;
673             _large.capacity = 0;
674         }
675         else
676             _small.length = 0; // fast discardal
677     }
678 
679     /// Is `true` if `U` can be assigned to the element type `E` of `this`.
680     enum isElementAssignable(U) = isAssignable!(E, U);
681 
682     /** Removal doesn't need to care about ordering. */
683     ContainerElementType!(typeof(this), E) removeAt(size_t index) @trusted @("complexity", "O(length)")
684 	in(!isBorrowed)
685     in(index < this.length)
686     {
687         auto value = move(_mptr[index]);
688 
689         // TODO: use this instead:
690         // immutable si = index + 1;   // source index
691         // immutable ti = index;       // target index
692         // immutable restLength = this.length - (index + 1);
693         // moveEmplaceAll(_mptr[si .. si + restLength],
694         //                _mptr[ti .. ti + restLength]);
695 
696         foreach (immutable i; 0 .. this.length - (index + 1)) // each element index that needs to be moved
697         {
698             immutable si = index + i + 1; // source index
699             immutable ti = index + i; // target index
700             moveEmplace(_mptr[si], // TODO: remove `move` when compiler does it for us
701                         _mptr[ti]);
702         }
703 
704         decOnlyLength();
705         return move(value); // TODO: remove `move` when compiler does it for us
706     }
707 
708     /** Removal doesn't need to care about ordering. */
709     ContainerElementType!(typeof(this), E) popFront() @trusted @("complexity", "O(length)")
710     {
711 		pragma(inline, true);
712         return removeAt(0);
713     }
714 
715     /** Removal doesn't need to care about ordering. */
716     void popBack() @("complexity", "O(1)")
717 	in(!isBorrowed)
718     in(!empty)
719     {
720 		pragma(inline, true);
721         decOnlyLength();
722     }
723 
724     /** Pop back element and return it. */
725     E takeBack() @trusted
726 	in(!isBorrowed)
727 	in(!empty)
728     {
729 		pragma(inline, true);
730         decOnlyLength();
731         // TODO: functionize:
732         static if (__traits(isPOD, E))
733             return _mptr[this.length]; // no move needed
734         else
735             return move(_mptr[this.length]); // move is indeed need here
736     }
737 
738     /** Pop last `count` back elements. */
739     void popBackN(size_t count) @("complexity", "O(1)")
740 	in(!isBorrowed)
741     {
742 		pragma(inline, true);
743         shrinkTo(this.length - count);
744     }
745 
746     static if (!isOrdered!ordering) // for unsorted arrays
747     {
748         /// Push back (append) `values`.
749         pragma(inline) void insertBack(Us...)(Us values) @trusted @("complexity", "O(1)")
750 		if (values.length >= 1 &&
751 			allSatisfy!(isElementAssignable, Us))
752 		in(!isBorrowed)
753         {
754             immutable newLength = this.length + values.length;
755             reserve(newLength);
756             foreach (immutable i, ref value; values) // `ref` so we can `move`
757             {
758                 // TODO: functionize:
759                 static if (__traits(isPOD, typeof(value)))
760                     _mptr[this.length + i] = value;
761                 else
762                     moveEmplace(*cast(MutableE*)&value, _mptr[this.length + i]);
763             }
764             setOnlyLength(this.length + values.length);
765         }
766 
767         /// ditto
768         void insertBack(R)(R values) @("complexity", "O(values.length)") @trusted
769 		if (isInputRange!R && !isInfinite!R &&
770 			!(isArray!R) &&
771 			!(isArrayContainer!R) &&
772 			isElementAssignable!(ElementType!R))
773 		in(!isBorrowed)
774         {
775             import std.range.primitives : hasLength;
776             static if (hasLength!R)
777             {
778                 const nextLength = this.length + values.length;
779                 reserve(nextLength);
780                 size_t i = 0;
781                 foreach (ref value; values) // `ref` so we can `move`
782                 {
783                     // TODO: functionize:
784                     static if (__traits(isPOD, typeof(value)))
785                         _mptr[this.length + i] = value;
786                     else
787                         moveEmplace(*cast(Mutable!E*)&value, _mptr[this.length + i]);
788                     ++i;
789                 }
790                 setOnlyLength(nextLength);
791             }
792             else
793             {
794                 foreach (ref value; values) // `ref` so we can `move`
795                     insertBack(value);
796             }
797         }
798 
799         /// ditto.
800         void insertBack(A)(A values) @trusted @("complexity", "O(values.length)")
801 		if (isArray!A &&
802 			(is(MutableE == Unqual!(typeof(A.init[0]))) || // for narrow strings
803 			 isElementAssignable!(ElementType!A)))
804 		in(!isBorrowed)
805         {
806             static if (is(A == immutable(E)[]))
807             {
808                 // immutable array cannot overlap with mutable array container
809                 // data; no need to check for overlap with `overlaps()`
810                 reserve(this.length + values.length);
811                 _mptr[this.length .. this.length + values.length] = values[];
812                 setOnlyLength(this.length + values.length);
813             }
814             else
815             {
816                 import nxt.overlapping : overlaps;
817                 if (this.ptr == values[].ptr) // called for instances as: `this ~= this`
818                 {
819                     reserve(2*this.length);
820                     foreach (immutable i; 0 .. this.length)
821                         _mptr[this.length + i] = ptr[i]; // needs copying
822                     setOnlyLength(2 * this.length);
823                 }
824                 else if (overlaps(this[], values[]))
825                     assert(0, `TODO: Handle overlapping arrays`);
826                 else
827                 {
828                     reserve(this.length + values.length);
829                     static if (is(MutableE == Unqual!(ElementType!A))) // TODO: also when `E[]` is `A[]`
830                         _mptr[this.length .. this.length + values.length] = values[];
831                     else
832                         foreach (immutable i, ref value; values)
833                             _mptr[this.length + i] = value;
834                     setOnlyLength(this.length + values.length);
835                 }
836             }
837         }
838 
839         /// ditto.
840         void insertBack(A)(in A values) @trusted @("complexity", "O(values.length)") // TODO: `in` parameter qualifier doesn't work here. Compiler bug?
841 		if (isArrayContainer!A &&
842 			(is(MutableE == Unqual!(typeof(A.init[0]))) || // for narrow strings
843 			 isElementAssignable!(ElementType!A)))
844         {
845 			pragma(inline, true);
846             insertBack(values[]);
847         }
848         alias put = insertBack;   // OutputRange support
849 
850 
851         // NOTE these separate overloads of opOpAssign are needed because one
852         // `const ref`-parameter-overload doesn't work because of compiler bug
853         // with: `this(this) @disable`
854         void opOpAssign(string op, Us...)(Us values)
855 		if (op == "~" &&
856 			values.length >= 1 &&
857 			allSatisfy!(isElementAssignable, Us))
858 		in(!isBorrowed)
859         {
860 			pragma(inline, true);
861             insertBack(move(values)); // TODO: remove `move` when compiler does it for us
862             // static if (values.length == 1)
863             // {
864             //     import std.traits : hasIndirections;
865             //     static if (hasIndirections!(Us[0]))
866             //     {
867             //         insertBack(move(values)); // TODO: remove `move` when compiler does it for us
868             //     }
869             //     else
870             //     {
871             //         insertBack(move(cast(Unqual!(Us[0]))values[0])); // TODO: remove `move` when compiler does it for us
872             //     }
873             // }
874             // else
875             // {
876             //     insertBack(move(values)); // TODO: remove `move` when compiler does it for us
877             // }
878         }
879 
880         void opOpAssign(string op, R)(R values)
881 		if (op == "~" &&
882 			isInputRange!R &&
883 			!isInfinite!R &&
884 			allSatisfy!(isElementAssignable, ElementType!R))
885         in(!isBorrowed)
886         {
887 			pragma(inline, true);
888             // TODO: use move(values)
889             insertBack(values); // TODO: remove `move` when compiler does it for us
890         }
891 
892         void opOpAssign(string op, A)(ref A values)
893 		if (op == "~" &&
894 			isArrayContainer!A &&
895 			isElementAssignable!(ElementType!A))
896         in(!isBorrowed)
897         {
898 			pragma(inline, true);
899             insertBack(values);
900         }
901     }
902 
903     import nxt.searching_ex : containsStoreIndex; // TODO: this is redundant but elides rdmd dependency error for array_ex.d
904 
905     static if (isOrdered!ordering)
906     {
907         import std.range : SearchPolicy, assumeSorted;
908 
909         /// Returns: `true` iff this contains `value`.
910         bool contains(U)(U value) const @nogc @("complexity", "O(log(length))")
911         {
912             return this[].contains(value); // reuse `SortedRange.contains`
913         }
914 
915         /** Wrapper for `std.range.SortedRange.lowerBound` when this `ordering` is sorted. */
916         auto lowerBound(SearchPolicy sp = SearchPolicy.binarySearch, U)(U e) inout @("complexity", "O(log(length))")
917         {
918             return this[].lowerBound!sp(e); // reuse `SortedRange.lowerBound`
919         }
920 
921         /** Wrapper for `std.range.SortedRange.upperBound` when this `ordering` is sorted. */
922         auto upperBound(SearchPolicy sp = SearchPolicy.binarySearch, U)(U e) inout @("complexity", "O(log(length))")
923         {
924             return this[].upperBound!sp(e); // reuse `SortedRange.upperBound`
925         }
926 
927         static if (ordering == Ordering.sortedUniqueSet)
928         {
929             /** Inserts several `values` into `this` ordered set.
930 
931                 Returns: `bool`-array with same length as `values`, where i:th
932                 `bool` value is set if `value[i]` wasn't previously in `this`.
933             */
934             bool[Us.length] insertMany(SearchPolicy sp = SearchPolicy.binarySearch, Us...)(Us values) @("complexity", "O(length)")
935                 if (values.length >= 1 &&
936                     allSatisfy!(isElementAssignable, Us))
937             in
938             {
939                 assert(!isBorrowed);
940 
941                 // assert no duplicates in `values`
942                 import std.range.primitives : empty;
943                 import std.algorithm.searching : findAdjacent;
944                 import std.algorithm.sorting : sort;
945 
946                 // TODO: functionize or use other interface in pushing `values`
947                 import std.traits : CommonType;
948                 CommonType!Us[Us.length] valuesArray;
949                 foreach (immutable i, const ref value; values)
950                     valuesArray[i] = value;
951                 assert(sort(valuesArray[]).findAdjacent.empty,
952                        "Parameter `values` must not contain duplicate elements");
953             }
954             do
955             {
956                 static if (values.length == 1) // faster because `contains()` followed by `completeSort()` searches array twice
957                 {
958                     import nxt.searching_ex : containsStoreIndex;
959                     size_t index;
960                     if (slice.assumeSorted!comp.containsStoreIndex!sp(values, index)) // faster than `completeSort` for single value
961                         return [false];
962                     else
963                     {
964                         insertAtIndexHelper(index, values);
965                         return [true];
966                     }
967                 }
968                 else
969                 {
970                     import std.algorithm.sorting : completeSort;
971 
972                     debug { typeof(return) hits; }
973                     else  { typeof(return) hits = void; }
974 
975                     size_t expandedLength = 0;
976                     immutable initialLength = this.length;
977                     foreach (immutable i, ref value; values)
978                     {
979                         // TODO: reuse completeSort with uniqueness handling?
980                         static if (values.length == 1)
981                         {
982                             // TODO: reuse single parameter overload linearUniqueInsert() and return
983                         }
984                         else
985                         {
986                             // TODO: reuse completeSort with uniqueness handling?
987                         }
988                         hits[i] = !this[0 .. initialLength].contains(value);
989                         if (hits[i])
990                         {
991                             insertBackHelper(value); // NOTE: append but don't yet sort
992                             ++expandedLength;
993                         }
994                     }
995 
996                     if (expandedLength != 0)
997                     {
998                         immutable ix = this.length - expandedLength;
999                         // TODO: use `_mptr` here instead and functionize to @trusted helper function
1000                         completeSort!comp(slice[0 .. ix].assumeSorted!comp,
1001                                           slice[ix .. this.length]);
1002                     }
1003                     return hits;
1004                 }
1005             }
1006         }
1007         else static if (ordering == Ordering.sortedValues)
1008         {
1009             /** Inserts `values`. */
1010             void insertMany(SearchPolicy sp = SearchPolicy.binarySearch, Us...)(Us values) @("complexity", "O(log(length))")
1011 			if (values.length >= 1 &&
1012 				allSatisfy!(isElementAssignable, Us))
1013             in(!isBorrowed)
1014             {
1015                 // TODO: add optimization for values.length == 2
1016                 static if (values.length == 1)
1017                 {
1018                     import nxt.searching_ex : containsStoreIndex;
1019                     size_t index;
1020                     if (!slice.assumeSorted!comp.containsStoreIndex!sp(values, index)) // faster than `completeSort` for single value
1021                         insertAtIndexHelper(index, values);
1022                 }
1023                 else
1024                 {
1025                     insertBackHelper(values); // simpler because duplicates are allowed
1026                     immutable ix = this.length - values.length;
1027                     import std.algorithm.sorting : completeSort;
1028                     completeSort!comp(_mptr[0 .. ix].assumeSorted!comp,
1029                                       _mptr[ix .. this.length]);
1030                 }
1031             }
1032         }
1033     }
1034     else
1035     {
1036         /** Insert element(s) `values` at array offset `index`. */
1037         void insertAtIndex(Us...)(size_t index, Us values) @("complexity", "O(length)")
1038 		if (values.length >= 1 &&
1039 			allSatisfy!(isElementAssignable, Us))
1040         in(!isBorrowed)
1041         {
1042 			pragma(inline, true)
1043             insertAtIndexHelper(index, values);
1044         }
1045 
1046         /** Insert element(s) `values` at the beginning. */
1047         void pushFront(Us...)(Us values) @("complexity", "O(length)")
1048 		if (values.length >= 1 &&
1049 			allSatisfy!(isElementAssignable, Us))
1050         {
1051 			pragma(inline, true);
1052             insertAtIndex(0, values);
1053         }
1054 
1055         alias prepend = pushFront;
1056 
1057         import nxt.traits_ex : isComparable;
1058         static if (__traits(isCopyable, E) &&
1059                    !is(E == char) &&
1060                    !is(E == wchar) &&
1061                    isComparable!E)
1062         {
1063             /// Returns: a sorted array copy.
1064             Array!(E, assignment, ordering.sortedValues, useGCAllocation, Capacity, less_) toSortedArray(alias less_ = "a < b")() const
1065             {
1066                 return typeof(return)(slice);
1067             }
1068             /// Returns: a sorted set array copy.
1069             Array!(E, assignment, ordering.sortedUniqueSet, useGCAllocation, Capacity, less_) toSortedSetArray(alias less_ = "a < b")() const
1070             {
1071                 return typeof(return)(slice);
1072             }
1073         }
1074     }
1075 
1076     /** Helper function used externally for unsorted and internally for sorted. */
1077     private void insertAtIndexHelper(Us...)(size_t index, Us values) @trusted @("complexity", "O(length)")
1078     {
1079         reserve(this.length + values.length);
1080 
1081         // TODO: factor this to robustCopy. It uses copy when no overlaps (my algorithm_em), iteration otherwise
1082         enum usePhobosCopy = false;
1083         static if (usePhobosCopy)
1084         {
1085             // TODO: why does this fail?
1086             import std.algorithm.mutation : copy;
1087             copy(ptr[index ..
1088                      this.length],        // source
1089                  _mptr[index + values.length ..
1090                        this.length + values.length]); // target
1091         }
1092         else
1093         {
1094             // move second part in reverse
1095             // TODO: functionize move
1096             foreach (immutable i; 0 .. this.length - index) // each element index that needs to be moved
1097             {
1098                 immutable si = this.length - 1 - i; // source index
1099                 immutable ti = si + values.length; // target index
1100                 _mptr[ti] = ptr[si]; // TODO: move construct?
1101             }
1102         }
1103 
1104         // set new values
1105         foreach (immutable i, ref value; values)
1106             ptr[index + i] = value; // TODO: use range algorithm instead?
1107 
1108         setOnlyLength(this.length + values.length);
1109     }
1110 
1111     private void insertBackHelper(Us...)(Us values) @trusted
1112 	@("complexity", "O(1)")
1113     {
1114         const newLength = this.length + values.length;
1115         reserve(newLength);
1116         foreach (immutable i, ref value; values)
1117         {
1118             // TODO: functionize:
1119             static if (__traits(isPOD, typeof(value)))
1120                 _mptr[this.length + i] = value;
1121             else
1122                 moveEmplace(*cast(MutableE*)&value, _mptr[this.length + i]);
1123         }
1124         setOnlyLength(newLength);
1125     }
1126 
1127     @property @("complexity", "O(1)")
1128 
1129     pragma(inline, true):
1130 
1131     /// ditto
1132     static if (isOrdered!ordering)
1133     {
1134         const pure: // indexing and slicing must be `const` when ordered
1135 
1136         /// Slice operator must be const when ordered.
1137         auto opSlice() return scope @trusted  // TODO: remove @trusted?
1138         {
1139             static if (is(E == class))
1140                 // TODO: remove this workaround when else branch works for classes
1141                 return (cast(E[])slice).assumeSorted!comp;
1142             else
1143                 return (cast(const(E)[])slice).assumeSorted!comp;
1144         }
1145         /// ditto
1146         auto opSlice(this This)(size_t i, size_t j) return scope @trusted // TODO: remove @trusted?
1147         {
1148             import std.range : assumeSorted;
1149             return (cast(const(E)[])slice[i .. j]).assumeSorted!comp;
1150         }
1151         private alias This = typeof(this);
1152 
1153         /// Index operator must be const to preserve ordering.
1154         ref const(E) opIndex(size_t i) return scope @nogc @trusted in(i < this.length) => ptr[i];
1155 
1156         /// Get front element (as constant reference to preserve ordering).
1157         ref const(E) front() return scope @nogc @trusted in(!empty) => ptr[0];
1158 
1159         /// Get back element (as constant reference to preserve ordering).
1160         ref const(E) back() return scope @nogc @trusted in(!empty) => ptr[this.length - 1];
1161     }
1162     else
1163     {
1164         /// Set length to `newLength`.
1165         @property void length(size_t newLength)
1166         {
1167             if (newLength < length)
1168                 shrinkTo(newLength);
1169             else
1170             {
1171                 reserve(newLength);
1172                 setOnlyLength(newLength);
1173             }
1174         }
1175 
1176         @nogc:
1177 
1178         /// Index assign operator.
1179         ref E opIndexAssign(V)(V value, size_t i) @trusted return scope
1180         {
1181             assert(!isBorrowed);
1182             assert(i < this.length);
1183             static if (isScalarType!E)
1184                 ptr[i] = value;
1185             else
1186                 move(*(cast(MutableE*)(&value)), _mptr[i]); // TODO: is this correct?
1187             return ptr[i];
1188         }
1189 
1190         /// Slice assign operator.
1191         static if (__traits(isCopyable, E))
1192         {
1193             void opSliceAssign(V)(V value, size_t i, size_t j) @trusted return scope
1194 			in(!isBorrowed)
1195             in(i <= j)
1196             in(j <= this.length)
1197             {
1198                 foreach (immutable i; 0 .. this.length)
1199                     ptr[i] = value;
1200             }
1201         }
1202 
1203         pure inout: // indexing and slicing has mutable access only when unordered
1204 
1205         /// Slice operator.
1206         inout(E)[] opSlice() return => this.opSlice(0, this.length);
1207         /// ditto
1208         inout(E)[] opSlice(size_t i, size_t j) @trusted return scope
1209 		in(i <= j)
1210 		in(j <= this.length)
1211         {
1212             return ptr[i .. j];
1213         }
1214 
1215         @trusted:
1216 
1217         /// Index operator.
1218         ref inout(E) opIndex(size_t i) return scope in(i < this.length) => ptr[i];
1219 
1220         /// Get front element reference.
1221         ref inout(E) front() return scope in(!empty) => ptr[0];
1222 
1223         /// Get back element reference.
1224         ref inout(E) back() return scope in(!empty) => ptr[this.length - 1];
1225     }
1226 
1227     alias data = opSlice;   // `std.array.Appender` compatibility
1228 
1229     // static if (__traits(isCopyable, E))
1230     // {
1231     //     string toString() const @property @trusted pure
1232     //     {
1233     //         import std.array : Appender;
1234     //         import std.conv : to;
1235     //         Appender!string s = "[";
1236     //         foreach (immutable i; 0 .. this.length)
1237     //         {
1238     //             if (i) { s.put(','); }
1239     //             s.put(ptr[i].to!string);
1240     //         }
1241     //         s.put("]");
1242     //         return s.data;
1243     //     }
1244     // }
1245 
1246     pure:
1247 
1248     /** Allocate heap regionwith `newCapacity` number of elements of type `E`.
1249         If `zero` is `true` they will be zero-initialized.
1250     */
1251     private static MutableE* allocate(in size_t newCapacity, bool zero = false)
1252     {
1253         typeof(return) ptr = null;
1254         static if (useGCAllocation)
1255         {
1256             if (zero) { ptr = cast(typeof(return))GC.calloc(newCapacity, E.sizeof); }
1257             else      { ptr = cast(typeof(return))GC.malloc(newCapacity * E.sizeof); }
1258         }
1259         else                    // @nogc
1260         {
1261             if (zero) { ptr = cast(typeof(return))calloc(newCapacity, E.sizeof); }
1262             else      { ptr = cast(typeof(return))malloc(newCapacity * E.sizeof); }
1263             assert(ptr, "Allocation failed");
1264         }
1265         static if (shouldAddGCRange!E)
1266             gc_addRange(ptr, newCapacity * E.sizeof);
1267         return ptr;
1268     }
1269 
1270     @nogc:
1271 
1272     /// Check if empty.
1273     bool empty() const @property { return this.length == 0; }
1274 
1275     /// Get length.
1276     size_t length() const @trusted
1277     {
1278         if (isLarge)
1279             return _large.length;
1280         else
1281             return _small.length;
1282     }
1283     alias opDollar = length;    /// ditto
1284 
1285     /// Decrease only length.
1286     private void decOnlyLength() @trusted
1287     {
1288         if (isLarge)
1289         {
1290             assert(_large.length);
1291             _large.length = _large.length - 1;
1292         }
1293         else
1294         {
1295             assert(_small.length);
1296             _small.length = cast(SmallLengthType)(_small.length - 1);
1297         }
1298     }
1299 
1300     /// Set only length.
1301     private void setOnlyLength(size_t newLength) @trusted
1302     {
1303         if (isLarge)
1304             _large.length = newLength; // TODO: compress?
1305         else
1306         {
1307             assert(newLength <= SmallLengthType.max);
1308             _small.length = cast(SmallLengthType)newLength;
1309         }
1310     }
1311 
1312     /// Get reserved capacity of store.
1313     size_t capacity() const @trusted
1314     {
1315         if (isLarge)
1316             return _large.capacity;
1317         else
1318             return smallCapacity;
1319     }
1320 
1321     /** Shrink length to `newLength`.
1322      *
1323      * If `newLength` >= `length` operation has no effect.
1324      */
1325     pragma(inline)
1326     void shrinkTo(size_t newLength)
1327     {
1328         assert(!isBorrowed);
1329         if (newLength < length)
1330         {
1331             static if (hasElaborateDestructor!E)
1332                 foreach (immutable i; newLength .. length)
1333                     .destroy(_mptr[i]);
1334             setOnlyLength(newLength);
1335         }
1336     }
1337 
1338     /// Get internal pointer.
1339     inout(E*) ptr() inout @trusted return scope // array access is @trusted
1340     {
1341         // TODO: Use cast(ET[])?: alias ET = ContainerElementType!(typeof(this), E);
1342         if (isLarge)
1343             return _large.ptr;
1344         else
1345             return _small.elms.ptr;
1346     }
1347 
1348     /// Get internal pointer to mutable content. Doesn't need to be qualified with `scope`.
1349     private MutableE* _mptr()
1350         const return scope
1351     {
1352         if (isLarge)
1353             return _large._mptr;
1354         else
1355             return _small._mptr;
1356     }
1357 
1358     /// Get internal slice.
1359     private auto slice() inout @trusted return scope
1360     {
1361         return ptr[0 .. this.length];
1362     }
1363 
1364     /** Magic pointer value used to detect double calls to `free`.
1365 
1366         Cannot conflict with return value from `malloc` because the least
1367         significant bit is set (when the value ends with a one).
1368     */
1369     debug private enum _ptrMagic = cast(E*)0x0C6F3C6c0f3a8471;
1370 
1371     /// Returns: `true` if `this` currently uses large array storage.
1372     bool isLarge() const @trusted // trusted access to anonymous union
1373     {
1374         assert(_large.isLarge == _small.isLarge); // must always be same
1375         return _large.isLarge;
1376     }
1377 
1378     /// Returns: `true` if `this` currently uses small (packed) array storage.
1379     bool isSmall() const { return !isLarge; }
1380 
1381     private
1382     {
1383         private alias SmallLengthType = ubyte;
1384 
1385         private enum largeSmallLengthDifference = Large.sizeof - SmallLengthType.sizeof;
1386         private enum smallCapacity = largeSmallLengthDifference / E.sizeof;
1387         private enum smallPadSize = largeSmallLengthDifference - smallCapacity*E.sizeof;
1388     }
1389 
1390     /** Tag `this` borrowed.
1391         Used by wrapper logic in owned.d and borrowed.d
1392     */
1393     void tagAsBorrowed() @trusted
1394     {
1395         if (isLarge) { _large.isBorrowed = true; }
1396         else         { _small.isBorrowed = true; }
1397     }
1398 
1399     /** Tag `this` as not borrowed.
1400         Used by wrapper logic in owned.d and borrowed.d
1401     */
1402     void untagAsNotBorrowed() @trusted
1403     {
1404         if (isLarge) { _large.isBorrowed = false; }
1405         else         { _small.isBorrowed = false; }
1406     }
1407 
1408     /// Returns: `true` if this is borrowed
1409     bool isBorrowed() @trusted
1410     {
1411         if (isLarge) { return _large.isBorrowed; }
1412         else         { return _small.isBorrowed; }
1413     }
1414 
1415 private:                        // data
1416     enum useAlignedTaggedPointer = false; // TODO: make this work when true
1417     static struct Large
1418     {
1419         static if (useAlignedTaggedPointer)
1420         {
1421             private enum lengthMax = Capacity.max;
1422             version(LittleEndian) // see: http://forum.dlang.org/posting/zifyahfohbwavwkwbgmw
1423             {
1424                 import std.bitmanip : taggedPointer;
1425                 mixin(taggedPointer!(uint*, "_uintptr", // GC-allocated store pointer. See_Also: http://forum.dlang.org/post/iubialncuhahhxsfvbbg@forum.dlang.org
1426                                      bool, "isLarge", 1, // bit 0
1427                                      bool, "isBorrowed", 1, // bit 1
1428                           ));
1429 
1430                 pragma(inline, true):
1431                 @property void ptr(E* c)
1432                 {
1433                     assert((cast(ulong)c & 0b11) == 0);
1434                     _uintptr = cast(uint*)c;
1435                 }
1436 
1437                 @property inout(E)* ptr() inout
1438                 {
1439                     return cast(E*)_uintptr;
1440                 }
1441 
1442                 Capacity capacity;  // store capacity
1443                 Capacity length;  // store length
1444             }
1445             else
1446             {
1447                 static assert(0, "BigEndian support and test");
1448             }
1449         }
1450         else
1451         {
1452             private enum lengthBits = 8*Capacity.sizeof - 2;
1453             private enum lengthMax = 2^^lengthBits - 1;
1454 
1455             static if (useGCAllocation)
1456                 E* ptr; // GC-allocated store pointer. See_Also: http://forum.dlang.org/post/iubialncuhahhxsfvbbg@forum.dlang.org
1457             else
1458                 @nogc E* ptr;   // non-GC-allocated store pointer
1459 
1460             Capacity capacity;  // store capacity
1461 
1462             import std.bitmanip : bitfields; // TODO: replace with own logic cause this mixin costs compilation speed
1463             mixin(bitfields!(Capacity, "length", lengthBits,
1464                              bool, "isBorrowed", 1,
1465                              bool, "isLarge", 1,
1466                       ));
1467         }
1468 
1469         pragma(inline)
1470         this(size_t initialCapacity, size_t initialLength, bool zero)
1471         {
1472             assert(initialCapacity <= lengthMax);
1473             assert(initialLength <= lengthMax);
1474 
1475             setCapacity(initialCapacity);
1476             this.capacity = cast(Capacity)initialCapacity;
1477             this.ptr = allocate(initialCapacity, zero);
1478             this.length = initialLength;
1479             this.isLarge = true;
1480             this.isBorrowed = false;
1481         }
1482 
1483         pragma(inline, true):
1484 
1485         void setCapacity(in size_t newCapacity)
1486         {
1487             assert(newCapacity <= capacity.max);
1488             capacity = cast(Capacity)newCapacity;
1489         }
1490 
1491         MutableE* _mptr() const @trusted
1492         {
1493             return cast(typeof(return))ptr;
1494         }
1495     }
1496 
1497     /// Small string storage.
1498     static struct Small
1499     {
1500         enum capacity = smallCapacity;
1501         private enum lengthBits = 8*SmallLengthType.sizeof - 2;
1502         private enum lengthMax = 2^^lengthBits - 1;
1503 
1504         import std.bitmanip : bitfields; // TODO: replace with own logic cause this mixin costs compilation speed
1505         static if (useAlignedTaggedPointer)
1506         {
1507             mixin(bitfields!(bool, "isLarge", 1, // defaults to false
1508                              bool, "isBorrowed", 1, // default to false
1509                              SmallLengthType, "length", lengthBits,
1510                       ));
1511             static if (smallPadSize) { ubyte[smallPadSize] _ignoredPadding; }
1512             E[capacity] elms;
1513         }
1514         else
1515         {
1516             E[capacity] elms;
1517             static if (smallPadSize) { ubyte[smallPadSize] _ignoredPadding; }
1518             mixin(bitfields!(SmallLengthType, "length", lengthBits,
1519                              bool, "isBorrowed", 1, // default to false
1520                              bool, "isLarge", 1, // defaults to false
1521                              ));
1522         }
1523 
1524         this(size_t initialLength, bool zero)
1525 		in(initialLength <= lengthMax)
1526         {
1527             this.length = cast(SmallLengthType)initialLength;
1528             this.isLarge = false;
1529             this.isBorrowed = false;
1530             if (zero)
1531                 elms[] = E.init;
1532         }
1533 
1534         pragma(inline, true)
1535         MutableE* _mptr() const @trusted
1536         {
1537             return cast(typeof(return))(elms.ptr);
1538         }
1539     }
1540 
1541     static assert(Large.sizeof == Small.sizeof);
1542 
1543     static if (E.sizeof == 1 &&
1544                size_t.sizeof == 8 && // 64-bit
1545                Small.capacity == 23)
1546     {
1547         static assert(Large.sizeof == 24);
1548         static assert(Small.sizeof == 24);
1549     }
1550 
1551     union
1552     {
1553         Large _large;            // indirected storage
1554         Small _small;            // non-indirected storage
1555     }
1556 }
1557 
1558 import std.traits : isDynamicArray;
1559 
1560 /** Return an instance of `R` with capacity `capacity`. */
1561 R make_withCapacity(R)(in size_t capacity)
1562 if (__traits(hasMember, R, "withCapacity"))
1563 {
1564     return R.withCapacity(capacity);
1565 }
1566 /// ditto
1567 R make_withCapacity(R)(in size_t capacity)
1568 if (isDynamicArray!R)
1569 {
1570     R r;
1571     // See http://forum.dlang.org/post/nupffaitocqjlamffuqi@forum.dlang.org
1572     r.reserve(capacity);
1573     return r;
1574 }
1575 
1576 ///
1577 @safe pure nothrow unittest
1578 {
1579     immutable capacity = 10;
1580     auto x = capacity.make_withCapacity!(int[]);
1581     assert(x.capacity >= capacity);
1582 }
1583 
1584 /** Return an instance of `R` of length `length`. */
1585 R make_withLength(R)(size_t length)
1586 if (__traits(hasMember, R, "withLength"))
1587 {
1588     return R.withLength(length);
1589 }
1590 /// ditto
1591 R make_withLength(R)(size_t length)
1592 if (isDynamicArray!R)
1593 {
1594     R r;
1595     r.length = length;
1596     return r;
1597 }
1598 
1599 /** Return an instance of `R` containing a single element `e`. */
1600 R withElementMake(R)(typeof(R.init[0]) e)
1601 if (__traits(hasMember, R, "withElement"))
1602 {
1603     return R.withElement(e);
1604 }
1605 /// ditto
1606 R withElementMake(R)(typeof(R.init[0]) e)
1607 if (isDynamicArray!R)
1608 {
1609     return [e];
1610 }
1611 
1612 alias UniqueArray(E, bool useGCAllocation = false) = Array!(E, Assignment.disabled, Ordering.unsorted, useGCAllocation, size_t, "a < b");
1613 alias CopyingArray(E, bool useGCAllocation = false) = Array!(E, Assignment.copy, Ordering.unsorted, useGCAllocation, size_t, "a < b");
1614 
1615 alias SortedCopyingArray(E, bool useGCAllocation = false, alias less = "a < b") = Array!(E, Assignment.copy, Ordering.sortedValues, useGCAllocation, size_t, less);
1616 alias SortedSetCopyingArray(E, bool useGCAllocation = false, alias less = "a < b") = Array!(E, Assignment.copy, Ordering.sortedUniqueSet, useGCAllocation, size_t, less);
1617 
1618 alias SortedUniqueArray(E, bool useGCAllocation = false, alias less = "a < b") = Array!(E, Assignment.disabled, Ordering.sortedValues, useGCAllocation, size_t, less);
1619 alias SortedSetUniqueArray(E, bool useGCAllocation = false, alias less = "a < b") = Array!(E, Assignment.disabled, Ordering.sortedUniqueSet, useGCAllocation, size_t, less);
1620 
1621 // string aliases
1622 alias UniqueString(bool useGCAllocation = false) = Array!(char,  Assignment.disabled, Ordering.unsorted, useGCAllocation, size_t, "a < b");
1623 alias CopyingString(bool useGCAllocation = false) = Array!(char,  Assignment.copy, Ordering.unsorted, useGCAllocation, size_t, "a < b");
1624 alias UniqueWString(bool useGCAllocation = false) = Array!(wchar, Assignment.disabled, Ordering.unsorted, useGCAllocation, size_t, "a < b");
1625 alias CopyingWString(bool useGCAllocation = false) = Array!(wchar, Assignment.copy, Ordering.unsorted, useGCAllocation, size_t, "a < b");
1626 alias UniqueDString(bool useGCAllocation = false) = Array!(dchar, Assignment.disabled, Ordering.unsorted, useGCAllocation, size_t, "a < b");
1627 alias CopyingDString(bool useGCAllocation = false) = Array!(dchar, Assignment.copy, Ordering.unsorted, useGCAllocation, size_t, "a < b");
1628 
1629 ///
1630 @safe pure unittest
1631 {
1632     auto c = UniqueString!false();
1633     auto w = UniqueWString!false();
1634     auto d = UniqueDString!false();
1635 }
1636 
1637 ///
1638 @safe pure unittest
1639 {
1640     auto c = CopyingString!false();
1641     auto w = CopyingWString!false();
1642     auto d = CopyingDString!false();
1643 }
1644 
1645 ///
1646 version(none)
1647 @safe pure unittest
1648 {
1649     import std.conv : to;
1650     foreach (assignment; AliasSeq!(Assignment.disabled, Assignment.copy))
1651     {
1652         foreach (Ch; AliasSeq!(char, wchar, dchar))
1653         {
1654             alias Str = Array!(Ch, assignment);
1655             Str str_as = Str.withElement('a');
1656             Str str_as2 = 'a'.withElementMake!Str;
1657             Str str_as3 = 'a'.withElementMake!(Ch[]);
1658             assert(str_as == str_as2);
1659             assert(str_as2 == str_as3);
1660             str_as ~= Ch('_');
1661             assert(str_as[].equal("a_"));
1662         }
1663     }
1664 }
1665 
1666 static void tester(Ordering ordering, bool supportGC, alias less)()
1667 {
1668     import std.functional : binaryFun;
1669     import std.range : iota, chain, repeat, only, ElementType;
1670     import std.algorithm : filter, map;
1671     import std.algorithm.sorting : isSorted, sort;
1672     import std.exception : assertThrown, assertNotThrown;
1673     import core.internal.traits : Unqual;
1674 
1675     enum assignment = Assignment.copy;
1676     alias comp = binaryFun!less; //< comparison
1677 
1678     alias E = int;
1679 
1680     {
1681         alias A = SortedUniqueArray!E;
1682         auto x = A.withElements(0, 3, 2, 1);
1683         assert(x[].equal([0, 1, 2, 3].s[]));
1684     }
1685 
1686     foreach (Ch; AliasSeq!(char, wchar, dchar))
1687     {
1688         static if (!isOrdered!ordering || // either not ordered
1689                    is(Ch == dchar))       // or not a not a narrow string
1690         {
1691             alias Str = Array!(Ch, assignment, ordering, supportGC, size_t, less);
1692             auto y = Str.withElements('a', 'b', 'c');
1693             static assert(is(Unqual!(ElementType!Str) == Ch));
1694             y = Str.init;
1695 
1696             const(Ch)[] xs;
1697             {
1698                 // immutable
1699                 immutable x = Str.withElements('a', 'b', 'c');
1700                 static if (!isOrdered!ordering)
1701                     xs = x[];       // TODO: should fail with DIP-1000 scope
1702             }
1703         }
1704     }
1705 
1706     foreach (Ch; AliasSeq!(char))
1707     {
1708         static if (!isOrdered!ordering)
1709         {
1710             alias Str = Array!(Ch, assignment, ordering, supportGC, size_t, less);
1711             auto str = Str.withElements('a', 'b', 'c');
1712 
1713             static if (isOrdered!ordering)
1714                 static assert(is(Unqual!(ElementType!Str) == Ch));
1715             else
1716             {
1717                 static assert(is(ElementType!Str == Ch));
1718                 assert(str[] == `abc`); // TODO: this fails for wchar and dchar
1719             }
1720         }
1721     }
1722 
1723     {
1724         alias A = Array!(int, assignment, ordering, supportGC, size_t, less);
1725         foreach (immutable n; [0, 1, 2, 3, 4, 5])
1726         {
1727             assert(A.withLength(n).isSmall);
1728         }
1729         assert(!(A.withLength(6).isSmall));
1730         assert((A.withLength(6).isLarge));
1731     }
1732 
1733     // test move construction
1734     {
1735         immutable maxLength = 1024;
1736         foreach (immutable n; 0 .. maxLength)
1737         {
1738             auto x = Array!(E, assignment, ordering, supportGC, size_t, less).withLength(n);
1739 
1740             // test resize
1741             static if (!isOrdered!ordering)
1742             {
1743                 assert(x.length == n);
1744                 x.length = n + 1;
1745                 assert(x.length == n + 1);
1746                 x.length = n;
1747             }
1748 
1749             const ptr = x.ptr;
1750             immutable capacity = x.capacity;
1751             assert(x.length == n);
1752 
1753             import std.algorithm.mutation : move;
1754             auto y = Array!(E, assignment, ordering, supportGC, size_t, less)();
1755             move(x, y);
1756 
1757             assert(x.length == 0);
1758             assert(x.capacity == x.smallCapacity);
1759 
1760             assert(y.length == n);
1761             assert(y.capacity == capacity);
1762         }
1763     }
1764 
1765     foreach (immutable n; chain(0.only, iota(0, 10).map!(x => 2^^x)))
1766     {
1767         import std.array : array;
1768         import std.range : radial;
1769 
1770         immutable zi = cast(int)0; // zero index
1771         immutable ni = cast(int)n; // number index
1772 
1773         auto fw = iota(zi, ni); // 0, 1, 2, ..., n-1
1774 
1775         auto bw = fw.array.radial;
1776 
1777         Array!(E, assignment, ordering, supportGC, size_t, less) ss0 = bw; // reversed
1778         static assert(is(Unqual!(ElementType!(typeof(ss0))) == E));
1779         static assert(is(typeof(ss0) == Array!(_), _));
1780         assert(ss0.length == n);
1781 
1782         static if (isOrdered!ordering)
1783         {
1784             if (!ss0.empty) { assert(ss0[0] == ss0[0]); } // trigger use of opindex
1785             assert(ss0[].equal(fw.array.sort!comp));
1786             assert(ss0[].isSorted!comp);
1787         }
1788 
1789         Array!(E, assignment, ordering, supportGC, size_t, less) ss1 = fw; // ordinary
1790         assert(ss1.length == n);
1791 
1792         static if (isOrdered!ordering)
1793         {
1794             assert(ss1[].equal(fw.array.sort!comp));
1795             assert(ss1[].isSorted!comp);
1796         }
1797 
1798         Array!(E, assignment, ordering, supportGC, size_t, less) ss2 = fw.filter!(x => x & 1);
1799         assert(ss2.length == n/2);
1800 
1801         static if (isOrdered!ordering)
1802         {
1803             assert(ss2[].equal(fw.filter!(x => x & 1).array.sort!comp));
1804             assert(ss2[].isSorted!comp);
1805         }
1806 
1807         auto ssA = Array!(E, assignment, ordering, supportGC, size_t, less).withLength(0);
1808         static if (isOrdered!ordering)
1809         {
1810             static if (less == "a < b")
1811             {
1812                 alias A = Array!(E, assignment, ordering, supportGC, size_t, less);
1813                 const A x = [1, 2, 3, 4, 5, 6];
1814                 assert(x.front == 1);
1815                 assert(x.back == 6);
1816                 assert(x.lowerBound(3).equal([1, 2].s[]));
1817                 assert(x.upperBound(3).equal([4, 5, 6].s[]));
1818                 assert(x[].equal(x[])); // already sorted
1819             }
1820 
1821             foreach (i; bw)
1822             {
1823                 static if (ordering == Ordering.sortedUniqueSet)
1824                 {
1825                     assert(ssA.insertMany(i)[].equal([true].s[]));
1826                     assert(ssA.insertMany(i)[].equal([false].s[]));
1827                 }
1828                 else
1829                 {
1830                     ssA.insertMany(i);
1831                 }
1832             }
1833             assert(ssA[].equal(sort!comp(fw.array)));
1834 
1835             auto ssB = Array!(E, assignment, ordering, supportGC, size_t, less).withLength(0);
1836             static if (ordering == Ordering.sortedUniqueSet)
1837             {
1838                 assert(ssB.insertMany(1, 7, 4, 9)[].equal(true.repeat(4)));
1839                 assert(ssB.insertMany(3, 6, 8, 5, 1, 9)[].equal([true, true, true, true, false, false].s[]));
1840                 assert(ssB.insertMany(3, 0, 2, 10, 11, 5)[].equal([false, true, true, true, true, false].s[]));
1841                 assert(ssB.insertMany(0, 2, 10, 11)[].equal(false.repeat(4))); // false becuse already inserted
1842                 assert(ssB.capacity == 16);
1843             }
1844             else
1845             {
1846                 ssB.insertMany(1, 7, 4, 9);
1847                 ssB.insertMany(3, 6, 8, 5);
1848                 ssB.insertMany(0, 2, 10, 11);
1849                 assert(ssB.capacity == 16);
1850             }
1851 
1852             auto ssI = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11].sort!comp; // values
1853             immutable ssO = [12, 13]; // values not range
1854 
1855             assert(ssB[].equal(ssI));
1856 
1857             foreach (s; ssI) { assert(ssB.contains(s)); }
1858             foreach (s; ssO) { assert(!ssB.contains(s)); }
1859 
1860             ssB.compress();
1861             assert(ssB.capacity == 12);
1862         }
1863         else
1864         {
1865             {
1866                 alias A = Array!(E, assignment, ordering, supportGC);
1867                 A x = [1, 2, 3];
1868                 x ~= x;
1869                 assert(x[].equal([1, 2, 3,
1870                                   1, 2, 3].s[]));
1871                 x ~= x[];
1872                 assert(x[].equal([1, 2, 3, 1, 2, 3,
1873                                   1, 2, 3, 1, 2, 3].s[]));
1874             }
1875 
1876             ssA ~= 3;
1877             ssA ~= 2;
1878             ssA ~= 1;
1879             assert(ssA[].equal([3, 2, 1].s[]));
1880 
1881             ssA.compress();
1882 
1883             // popBack
1884             ssA[0] = 1;
1885             ssA[1] = 2;
1886             assert(ssA[].equal([1, 2, 1].s[]));
1887             assert(!ssA.empty);
1888             assert(ssA.front == 1);
1889             assert(ssA.back == 1);
1890 
1891             assertNotThrown(ssA.popBack());
1892             assert(ssA[].equal([1, 2].s[]));
1893             assert(!ssA.empty);
1894             assert(ssA.front == 1);
1895             assert(ssA.back == 2);
1896 
1897             assertNotThrown(ssA.popBack());
1898             assert(ssA[].equal([1].s[]));
1899             assert(!ssA.empty);
1900             assert(ssA.front == 1);
1901             assert(ssA.back == 1);
1902 
1903             assertNotThrown(ssA.popBack());
1904             assert(ssA.length == 0);
1905             assert(ssA.empty);
1906             assert(ssA.capacity != 0);
1907 
1908             ssA.compress();
1909             assert(ssA.length == 0);
1910             assert(ssA.empty);
1911 
1912             // insertAt
1913             ssA ~= 1;
1914             ssA ~= 2;
1915             ssA ~= 3;
1916             ssA ~= 4;
1917             ssA ~= 5;
1918             ssA ~= 6;
1919             ssA ~= 7;
1920             ssA ~= 8;
1921             assert(ssA[].equal([1, 2, 3, 4, 5, 6, 7, 8].s[]));
1922             ssA.insertAtIndex(3, 100, 101);
1923             assert(ssA[].equal([1, 2, 3, 100, 101, 4, 5, 6, 7, 8].s[]));
1924             assertNotThrown(ssA.popFront());
1925             assert(ssA[].equal([2, 3, 100, 101, 4, 5, 6, 7, 8].s[]));
1926             assertNotThrown(ssA.popFront());
1927             assert(ssA[].equal([3, 100, 101, 4, 5, 6, 7, 8].s[]));
1928             assertNotThrown(ssA.popFront());
1929             assert(ssA[].equal([100, 101, 4, 5, 6, 7, 8].s[]));
1930             assertNotThrown(ssA.popFront());
1931             assertNotThrown(ssA.popFront());
1932             assertNotThrown(ssA.popFront());
1933             assertNotThrown(ssA.popFront());
1934             assertNotThrown(ssA.popFront());
1935             assertNotThrown(ssA.popFront());
1936             assertNotThrown(ssA.popFront());
1937             assert(ssA.empty);
1938             ssA.compress();
1939 
1940             // removeAt
1941             ssA ~= 1;
1942             ssA ~= 2;
1943             ssA ~= 3;
1944             ssA ~= 4;
1945             ssA ~= 5;
1946             assertNotThrown(ssA.removeAt(2));
1947             assert(ssA[].equal([1, 2, 4, 5].s[]));
1948 
1949             // insertBack and assignment from slice
1950             auto ssB = Array!(E, assignment, ordering, supportGC, size_t, less).withLength(0);
1951             ssB.insertBack([1, 2, 3, 4, 5].s[]);
1952             ssB.insertBack([6, 7]);
1953             assert(ssB[].equal([1, 2, 3, 4, 5, 6, 7].s[]));
1954             assert(ssB.takeBack() == 7);
1955             assert(ssB.takeBack() == 6);
1956             assert(ssB.takeBack() == 5);
1957             assert(ssB.takeBack() == 4);
1958             assert(ssB.takeBack() == 3);
1959             assert(ssB.takeBack() == 2);
1960             assert(ssB.takeBack() == 1);
1961             assert(ssB.empty);
1962 
1963             // insertBack(Array)
1964             {
1965                 immutable s = [1, 2, 3];
1966                 Array!(E, assignment, ordering, supportGC, size_t, less) s1 = s;
1967                 Array!(E, assignment, ordering, supportGC, size_t, less) s2 = s1[];
1968                 assert(s1[].equal(s));
1969                 s1 ~= s1;
1970                 assert(s1[].equal(chain(s, s)));
1971                 s1 ~= s2;
1972                 assert(s1[].equal(chain(s, s, s)));
1973             }
1974 
1975             // immutable ss_ = Array!(E, assignment, ordering, supportGC, size_t, less)(null);
1976             // assert(ss_.empty);
1977 
1978             auto ssC = Array!(E, assignment, ordering, supportGC, size_t, less).withLength(0);
1979             immutable int[5] i5_ = [1, 2, 3, 4, 5];
1980             immutable(int)[] i5 = i5_[];
1981             ssC.insertBack(i5);
1982             assert(i5 == [1, 2, 3, 4, 5].s[]);
1983             assert(ssC[].equal(i5));
1984 
1985             auto ssCc = ssC;    // copy it
1986             assert(ssCc[].equal(i5));
1987 
1988             ssC.shrinkTo(4);
1989             assert(ssC[].equal([1, 2, 3, 4].s[]));
1990 
1991             ssC.shrinkTo(3);
1992             assert(ssC[].equal([1, 2, 3].s[]));
1993 
1994             ssC.shrinkTo(2);
1995             assert(ssC[].equal([1, 2].s[]));
1996 
1997             ssC.shrinkTo(1);
1998             assert(ssC[].equal([1].s[]));
1999 
2000             ssC.shrinkTo(0);
2001             assert(ssC[].length == 0);
2002             assert(ssC.empty);
2003 
2004             ssC.insertBack(i5);
2005             ssC.popBackN(3);
2006             assert(ssC[].equal([1, 2].s[]));
2007 
2008             auto ssD = ssC;
2009             ssC.clear();
2010             assert(ssC.empty);
2011 
2012             assert(!ssD.empty);
2013             ssD = null;
2014             assert(ssD.empty);
2015             assert(ssD == typeof(ssD).init);
2016 
2017             assert(ssCc[].equal(i5));
2018 
2019             ssCc = ssCc;   // self assignment
2020         }
2021     }
2022 }
2023 
2024 /// disabled copying
2025 @safe pure nothrow @nogc unittest
2026 {
2027     alias E = ubyte;
2028     alias A = Array!(E, Assignment.disabled, Ordering.unsorted, false, size_t, "a < b");
2029     A a;
2030     immutable n = ubyte.max;
2031     size_t i = 0;
2032     foreach (ubyte e; 0 .. n)
2033     {
2034         a ~= e;
2035         // assert(a.back == e.to!E);
2036         assert(a.length == i + 1);
2037         ++i;
2038     }
2039     const b = a.dup;
2040     static assert(is(typeof(a) == Unqual!(typeof(b))));
2041     assert(b.length == a.length);
2042     assert(a !is b);
2043     assert(a == b);
2044 }
2045 
2046 /// disabled copying
2047 @safe pure nothrow unittest
2048 {
2049     alias E = string;
2050 
2051     alias A = Array!(E, Assignment.disabled, Ordering.unsorted, false, size_t, "a < b");
2052     A a;
2053     immutable n = 100_000;
2054     size_t i = 0;
2055     foreach (const ref e; 0 .. n)
2056     {
2057         a ~= e.to!E;
2058         // assert(a.back == e.to!E);
2059         assert(a.length == i + 1);
2060         ++i;
2061     }
2062     const b = a.dup;
2063     static assert(is(typeof(a) == Unqual!(typeof(b))));
2064     assert(b.length == a.length);
2065     assert(a !is b);
2066     assert(a == b);
2067 }
2068 
2069 /// disabled copying
2070 @safe pure nothrow unittest
2071 {
2072     alias E = string;
2073     alias A = Array!(E, Assignment.disabled, Ordering.unsorted, false, size_t, "a < b");
2074     A a;
2075     immutable n = 100_000;
2076     size_t i = 0;
2077     foreach (const ref e; 0 .. n)
2078     {
2079         a ~= e.to!E;
2080         assert(a.length == i + 1);
2081         ++i;
2082     }
2083     const b = a.dup;
2084     static assert(is(typeof(a) == Unqual!(typeof(b))));
2085     assert(a[] == b[]);
2086 }
2087 
2088 /// disabled copying
2089 @safe pure nothrow @nogc unittest
2090 {
2091     import std.traits : isAssignable;
2092 
2093     alias E = string;
2094     alias A = Array!E;
2095     static assert(!__traits(isCopyable, A));
2096 
2097     alias CA = CopyingArray!E;
2098     static assert(__traits(isCopyable, CA));
2099 
2100     // import std.traits : isRvalueAssignable, isLvalueAssignable;
2101     // static assert(isRvalueAssignable!(A));
2102     // static assert(isLvalueAssignable!(A));
2103 
2104     static assert(!isAssignable!(A));
2105 
2106     // import std.range.primitives : hasSlicing;
2107     // TODO: make this evaluate to `true`
2108     // static assert(hasSlicing!A);
2109 
2110     alias AA = Array!A;
2111 
2112     AA aa;
2113     A a;
2114     a ~= "string";
2115     aa ~= A.init;
2116 
2117     assert(aa == aa);
2118     assert(AA.withLength(3) == AA.withLength(3));
2119     assert(AA.withCapacity(3) == AA.withCapacity(3));
2120     assert(AA.withLength(3).length == 3);
2121     assert(aa != AA.init);
2122 }
2123 
2124 ///
2125 @safe pure nothrow @nogc unittest
2126 {
2127     alias E = int;
2128     alias A = Array!E;
2129     A a;
2130     import std.range : iota;
2131     import std.container.util : make;
2132     foreach (n; 0 .. 100)
2133     {
2134         const e = iota(0, n).make!Array;
2135         assert(e[].equal(iota(0, n)));
2136     }
2137 }
2138 
2139 version(unittest)
2140 {
2141     import std.traits : EnumMembers;
2142 }
2143 
2144 /// use GC
2145 pure nothrow unittest
2146 {
2147     foreach (ordering; EnumMembers!Ordering)
2148     {
2149         tester!(ordering, true, "a < b"); // use GC
2150         tester!(ordering, true, "a > b"); // use GC
2151     }
2152 }
2153 
2154 /// don't use GC
2155 pure nothrow /+TODO: @nogc+/ unittest
2156 {
2157     foreach (ordering; EnumMembers!Ordering)
2158     {
2159         tester!(ordering, false, "a < b"); // don't use GC
2160         tester!(ordering, false, "a > b"); // don't use GC
2161     }
2162 }
2163 
2164 ///
2165 @safe pure nothrow unittest
2166 {
2167     alias E = int;
2168     alias A = Array!E;
2169     A[string] map;
2170     map["a"] = A.init;
2171     map["B"] = A.withLength(42);
2172 
2173     auto aPtr = "a" in map;
2174     assert(aPtr);
2175     assert(A.init == *aPtr);
2176     assert(*aPtr == A.init);
2177 
2178     assert("z" !in map);
2179     auto zPtr = "z" in map;
2180     assert(!zPtr);
2181 }
2182 
2183 /// test withElement and withElements
2184 @safe pure nothrow @nogc unittest
2185 {
2186     import std.algorithm.mutation : move;
2187     import std.range.primitives : ElementType;
2188 
2189     alias A = Array!int;
2190     alias AA = Array!A;
2191     alias AAA = Array!AA;
2192 
2193     foreach (A_; AliasSeq!(A, AA, AAA))
2194     {
2195         alias E = ElementType!A_;
2196         A_ x = A_.withElement(E.init);
2197         A_ y = A_.withElements(E.init, E.init);
2198         assert(x.length == 1);
2199         assert(y.length == 2);
2200         immutable n = 100;
2201         foreach (_; 0 .. n)
2202         {
2203             auto e = E.init;
2204             x ~= move(e);
2205             y ~= E.init;
2206         }
2207         foreach (_; 0 .. n)
2208         {
2209             assert(x.takeBack() == E.init);
2210             assert(y.takeBack() == E.init);
2211         }
2212         assert(x.length == 1);
2213         assert(y.length == 2);
2214 
2215         import std.algorithm : swap;
2216         swap(x, y);
2217         assert(x.length == 2);
2218         assert(y.length == 1);
2219 
2220         swap(x[0], y[0]);
2221     }
2222 
2223 }
2224 
2225 /// assert same behaviour of `dup` as for builtin arrays
2226 @safe pure nothrow unittest
2227 {
2228     struct Vec { int x, y; }
2229     class Db { int* _ptr; }
2230     struct Node { int x; class Db; }
2231     // struct Node1 { const(int) x; class Db; }
2232     foreach (E; AliasSeq!(int, const(int), Vec, Node// , Node1
2233                  ))
2234     {
2235         alias DA = E[];         // builtin D array/slice
2236         immutable DA da = [E.init]; // construct from array
2237         auto daCopy = da.dup;   // duplicate
2238         daCopy[] = E.init;   // opSliceAssign
2239 
2240         alias CA = Array!E;         // container array
2241         immutable ca = CA.withElement(E.init);
2242 
2243         auto caCopy = ca.dup;
2244 
2245         import std.traits : hasIndirections;
2246         static if (!hasIndirections!E)
2247         {
2248             const(E)[2] x = [E.init, E.init];
2249             // TODO: caCopy ~= E.init;
2250             caCopy ~= x[];
2251             assert(caCopy.length == 3);
2252             assert(caCopy[1 .. $] == x[]);
2253         }
2254 
2255         // should have same element type
2256         static assert(is(typeof(caCopy[0]) ==
2257                          typeof(daCopy[0])));
2258 
2259     }
2260 }
2261 
2262 /// array as AA key type
2263 @safe pure nothrow unittest
2264 {
2265     struct E { int x, y; }
2266     foreach (A; AliasSeq!(Array!E,
2267                           CopyingArray!E))
2268     {
2269         int[A] x;
2270         immutable n = 100;
2271         foreach (immutable i; 0 .. n)
2272         {
2273             assert(x.length == i);
2274             assert(A.withElement(E(i, 2*i)) !in x);
2275             x[A.withElement(E(i, 2*i))] = 42;
2276             assert(x.length == i + 1);
2277             auto a = A.withElement(E(i, 2*i));
2278             import std.traits : isCopyable;
2279             static if (__traits(isCopyable, A))
2280             {
2281                 // TODO: why do these fail when `A` is uncopyable?
2282                 assert(a in x);
2283                 assert(A.withElement(E(i, 2*i)) in x);
2284                 assert(x[A.withElement(E(i, 2*i))] == 42);
2285             }
2286         }
2287     }
2288 }
2289 
2290 /// init and append to empty array as AA value type
2291 @safe pure nothrow unittest
2292 {
2293     alias Key = string;
2294     alias A = Array!int;
2295 
2296     A[Key] x;
2297 
2298     assert("a" !in x);
2299 
2300     x["a"] = A.init;            // if this init is removed..
2301     x["a"] ~= 42;               // ..then this fails
2302 
2303     assert(x["a"] == A.withElement(42));
2304 }
2305 
2306 /// compress
2307 @safe pure nothrow @nogc unittest
2308 {
2309     alias A = Array!string;
2310     A a;
2311 
2312     a.compress();
2313 
2314     a ~= "a";
2315     a ~= "b";
2316     a ~= "c";
2317 
2318     assert(a.length == 3);
2319     assert(a.capacity == 4);
2320 
2321     a.compress();
2322 
2323     assert(a.capacity == a.length);
2324 }
2325 
2326 ///
2327 @safe pure nothrow @nogc unittest
2328 {
2329     alias Key = UniqueArray!char;
2330     alias Value = UniqueArray!int;
2331     struct E
2332     {
2333         Key key;
2334         Value value;
2335         E dup() @safe pure nothrow @nogc
2336         {
2337             return E(key.dup, value.dup);
2338         }
2339     }
2340     E e;
2341     e.key = Key.withElement('a');
2342     e.value = Value.withElement(42);
2343 
2344     auto f = e.dup;
2345     assert(e == f);
2346 
2347     e.key = Key.withElement('b');
2348     assert(e != f);
2349 
2350     e.key = Key.withElement('a');
2351     assert(e == f);
2352 
2353     e.value = Value.withElement(43);
2354     assert(e != f);
2355 
2356     e.value = Value.withElement(42);
2357     assert(e == f);
2358 
2359 }
2360 
2361 /// append to empty to array as AA value type
2362 @safe pure nothrow @nogc unittest
2363 {
2364     import std.exception: assertThrown;
2365     import core.exception : RangeError;
2366 
2367     alias Key = string;
2368     alias A = Array!int;
2369 
2370     A[Key] x;
2371     // assertThrown!RangeError({ x["a"] ~= 42; }); // TODO: make this work
2372 }
2373 
2374 /// map array of uncopyable
2375 @safe pure nothrow unittest
2376 {
2377     import std.range.primitives : isInputRange;
2378     import std.array : array;
2379 
2380     alias A = UniqueArray!int;
2381     auto y = A.init[].map!(_ => _^^2).array;
2382 
2383     A z = y.dup;                // check that dup returns same type
2384     z = A.init;
2385     const w = [0, 1].s;
2386     z.insertBack(w[]);
2387     assert(z[].equal(w[]));
2388 }
2389 
2390 ///
2391 version(none)
2392 @safe pure nothrow @nogc unittest
2393 {
2394     alias A = UniqueArray!int;
2395     A x;
2396     const y = [0, 1, 2, 3].s;
2397 
2398     x.insertBack(y[]);
2399     assert(x[].equal(y[]));
2400 
2401     x.clear();
2402     x.insertBack(y[].map!(_ => _^^2)); // rhs has length (`hasLength`)
2403     assert(x[].equal(y[].map!(_ => _^^2)));
2404 
2405     x.clear();
2406     x.insertBack(y[].filter!(_ => _ & 1)); // rhs has no length (`!hasLength`)
2407     assert(x[].equal(y[].filter!(_ => _ & 1)));
2408 }
2409 
2410 /// collection
2411 /*@safe*/ pure nothrow @nogc unittest // TODO: make @safe when collect has been made safe
2412 {
2413     import std.range : iota, isOutputRange;
2414     import nxt.algorithm_ex : collect;
2415 
2416     alias E = int;
2417     alias A = Array!E;
2418 
2419     immutable n = 100;
2420     static assert(isOutputRange!(A, E));
2421 
2422     assert((0.iota(n).collect!A)[].equal(0.iota(n)));
2423 }
2424 
2425 /// map array of uncopyable
2426 @safe pure nothrow @nogc unittest
2427 {
2428     foreach (AT; AliasSeq!(SortedUniqueArray,
2429                            SortedSetUniqueArray))
2430     {
2431         alias A = AT!int;
2432         A a;
2433         A b = a.dup;
2434     }
2435 }
2436 
2437 /// init and append to empty array as AA value type
2438 @safe pure nothrow @nogc unittest
2439 {
2440     alias A = Array!int;
2441 
2442     const x = A.withElements(0, 1, 3, 0, 2, 1, 3);
2443 
2444     assert(x.toSortedArray == [0, 0, 1, 1, 2, 3, 3].s[]);
2445     assert(x.toSortedSetArray == [0, 1, 2, 3].s[]);
2446 
2447     assert(x.toSortedArray!"a > b" == [3, 3, 2, 1, 1, 0, 0].s[]);
2448     assert(x.toSortedSetArray!"a > b" == [3, 2, 1, 0].s[]);
2449 }
2450 
2451 /** Return `data` appended with arguments `args`.
2452 
2453     If `data` is an r-value it's modified and returned, otherwise a copy is made
2454     and returned.
2455  */
2456 C append(C, Args...)(auto ref C data,
2457                      auto ref Args args)
2458 if (args.length >= 1)    // TODO: trait: when `C` is a container supporting `insertBack`
2459 {
2460     static if (__traits(isRef, data)) // `data` is an r-value
2461         C mutableData = data.dup;
2462     else                        // `data` is an l-value
2463         alias mutableData = data;
2464     // TODO: use `mutableData.insertBack(args);` instead
2465     foreach (ref arg; args)
2466         mutableData.insertBack(arg);
2467     import std.algorithm.mutation : move;
2468     return move(mutableData);
2469 }
2470 
2471 /// append
2472 @safe pure nothrow @nogc unittest
2473 {
2474     alias Str = UniqueString!false;
2475 
2476     assert(Str(`a`).append('b', 'c')[] == `abc`);
2477     assert(Str(`a`).append(`b`, `c`)[] == `abc`);
2478 
2479     const Str x = Str(`a`).append('b', 'c'); // is moved
2480     assert(x[] == `abc`);
2481 
2482     Str y = `x`;
2483     Str z = y.append('y', 'z', `w`); // needs dup
2484     assert(y.ptr != z.ptr);
2485     assert(z[] == `xyzw`);
2486 }
2487 
2488 version(unittest)
2489 {
2490     private static struct SS
2491     {
2492         @disable this(this);
2493         int x;
2494     }
2495 }
2496 
2497 /// uncopyable elements
2498 @safe pure nothrow @nogc unittest
2499 {
2500     alias A = UniqueArray!SS;
2501     A x;
2502     x ~= SS.init;
2503     // TODO: x.insertBack(A.init);
2504 }
2505 
2506 // TODO: implement?
2507 // T opBinary(string op, R, Args...)(R lhs,
2508 //                                   auto ref Args args)
2509 // {
2510 //     return append(lhs, rhs);
2511 // }
2512 // @safe pure nothrow @nogc unittest
2513 // {
2514 //     alias S = UniqueString!false;
2515 //     // TODO
2516 //     // const S x = S(`a`) ~ 'b';
2517 //     // assert(x[] == `abc`);
2518 // }
2519 
2520 /// See_Also: http://forum.dlang.org/post/omfm56$28nu$1@digitalmars.com
2521 version(none)
2522 @safe pure nothrow unittest
2523 {
2524     import std.range.primitives : ElementType;
2525     import std.array : Appender;
2526 
2527     struct S
2528     {
2529         string src;
2530         S[] subs;
2531     }
2532 
2533     struct T
2534     {
2535         string src;
2536         Appender!(T[]) subs;
2537     }
2538 
2539     static assert(is(ElementType!(S[]) == S));
2540     static assert(is(ElementType!(T[]) == void)); // TODO: forward-reference bug: should be `T`
2541 
2542     S s;
2543     s.subs ~= S.init;
2544 
2545     T t;
2546     // t.subs ~= T.init;
2547     // t.subs.put(T.init);
2548 
2549     // struct U
2550     // {
2551     //     string src;
2552     //     UniqueArray!U subs;
2553     // }
2554     // U u;
2555 }
2556 
2557 /// class element
2558 @safe pure nothrow unittest
2559 {
2560     class Zing
2561     {
2562         void* raw;
2563     }
2564     class Edge : Zing
2565     {
2566         Zing[] actors;
2567     }
2568 
2569     foreach (AT; AliasSeq!(UniqueArray,
2570                            CopyingArray,
2571                            SortedCopyingArray,
2572                            SortedSetCopyingArray,
2573                            SortedUniqueArray,
2574                            SortedSetUniqueArray))
2575     {
2576         alias A = AT!int;
2577         A a;
2578     }
2579 }