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