1 module dkh.container.stack;
2 
3 import dkh.container.stackpayload;
4 
5 /// Stack
6 struct Stack(T) {
7     import core.exception : RangeError;
8 
9     alias Payload = StackPayload!T;
10     private Payload* p;
11 
12     import std.traits : isImplicitlyConvertible;
13     import std.range : ElementType, isInputRange, hasLength;
14     /// Stack(1, 2, 3)
15     this(U)(U[] values...) if (isImplicitlyConvertible!(U, T)) {
16         p = new Payload();
17         p.reserve(values.length);
18         foreach (v; values) this ~= v;
19     }
20     /// Stack(iota(3))
21     this(Range)(Range r)
22     if (isInputRange!Range &&
23     isImplicitlyConvertible!(ElementType!Range, T) &&
24     !is(Range == T[])) {
25         p = new Payload();
26         static if (hasLength!Range) p.reserve(r.length);
27         foreach (v; r) this ~= v;
28     }
29 
30     @property bool empty() const { return (!p || p.empty); } ///
31     @property size_t length() const { return (p ? p.length : 0); } ///
32     alias opDollar = length; /// ditto
33     @property inout(T)[] data() inout { return (!p) ? [] : p.data; } ///
34 
35     ref inout(T) opIndex(size_t i) inout {
36         assert(!empty, "Stack.opIndex: Stack is empty");
37         return (*p)[i];
38     } ///
39     ref inout(T) front() inout { return this[0]; } ///
40     ref inout(T) back() inout { return this[$-1]; } ///
41     
42     void clear() { if (p) p.clear(); } ///
43 
44     void insertBack(T item) {
45         if (!p) p = new Payload();
46         p.insertBack(item);
47     } ///
48     alias opOpAssign(string op : "~") = insertBack; /// ditto
49     alias stableInsertBack = insertBack; /// ditto
50     void removeBack() {
51         assert(!empty, "Stack.removeBack: Stack is empty");
52         p.removeBack();
53     } ///
54     alias stableRemoveBack = removeBack; /// ditto
55 
56     /// Random-access range
57     alias Range = RangeT!(StackPayload!T);
58     alias ConstRange = RangeT!(const StackPayload!T); /// ditto
59     alias ImmutableRange = RangeT!(immutable StackPayload!T); /// ditto
60 
61     size_t[2] opSlice(size_t dim : 0)(size_t start, size_t end) const {
62         assert(start <= end && end <= length);
63         return [start, end];
64     } ///
65     Range opIndex(size_t[2] rng) { return Range(p, rng[0], rng[1]); } /// Get slice
66     ConstRange opIndex(size_t[2] rng) const { return ConstRange(p, rng[0], rng[1]); } /// ditto
67     ImmutableRange opIndex(size_t[2] rng) immutable { return ImmutableRange(p, rng[0], rng[1]); } /// ditto
68     auto opIndex() inout { return this[0..$]; } /// ditto
69 
70     static struct RangeT(QualifiedPayload) {
71         alias A = QualifiedPayload;
72         import std.traits : CopyTypeQualifiers;
73         alias E = CopyTypeQualifiers!(A, T);
74         A *p;
75         size_t l, r;
76 
77         @property bool empty() const { return r <= l; }
78         @property size_t length() const { return r - l; }
79         alias opDollar = length;
80 
81         @property auto save() { return this; }
82         
83         ref inout(E) opIndex(size_t i) inout {
84             version(assert) if (empty) throw new RangeError();
85             return (*p)[l+i];
86         }
87         @property ref inout(E) front() inout { return this[0]; }
88         @property ref inout(E) back() inout { return this[$-1]; }
89 
90         void popFront() {
91             version(assert) if (empty) throw new RangeError();
92             l++;
93         }
94         void popBack() {
95             version(assert) if (empty) throw new RangeError();
96             r--;
97         }
98         
99         size_t[2] opSlice(size_t dim : 0)(size_t start, size_t end) const {
100             assert(start <= end && end <= length);
101             return [start, end];
102         }
103         auto opIndex(size_t[2] rng) { return RangeT(p, l+rng[0], l+rng[1]); }
104         auto opIndex(size_t[2] rng) const { return RangeT!(const A)(p, l+rng[0], l+rng[1]); }
105         auto opIndex(size_t[2] rng) immutable { return RangeT!(immutable A)(p, l+rng[0], l+rng[1]); }
106         auto opIndex() inout { return this[0..$]; }
107     } 
108 }
109 
110 ///
111 unittest {
112     import std.algorithm : equal;
113     import std.range.primitives : isRandomAccessRange;
114     import std.container.util : make;
115     auto q = Stack!int();
116 
117     assert(equal(q[], new int[0]));
118     q.insertBack(1);
119     assert(equal(q[], [1]));
120     q.insertBack(2);
121     assert(equal(q[], [1, 2]));
122     q.insertBack(3);
123     assert(equal(q[], [1, 2, 3]));
124     q.removeBack();
125     assert(equal(q[], [1, 2]));
126     q.insertBack(4);
127     assert(equal(q[], [1, 2, 4]));
128     q.removeBack();
129     assert(equal(q[], [1, 2]));
130 }
131 
132 unittest {
133     import std.range;
134     auto q1 = Stack!int(1, 2, 3);
135     auto q2 = Stack!int(iota(3));
136 }
137 
138 unittest {
139     import std.algorithm : equal;
140     auto q = Stack!int(1, 2, 3, 4, 5);
141     assert(equal(q[1..4], [2, 3, 4]));
142     assert(q[1..4][1] == 3);
143     const auto rng = q[1..4];
144     assert(rng.front == 2 && rng.back == 4);
145     assert(equal(rng[0..3], [2, 3, 4]));
146     assert(equal(rng[], [2, 3, 4]));
147 }
148 
149 unittest {
150     import std.range : isRandomAccessRange;
151     static assert(isRandomAccessRange!(Stack!int.Range));
152     static assert(isRandomAccessRange!(Stack!int.ConstRange));
153     static assert(isRandomAccessRange!(Stack!int.ImmutableRange));
154 }