1 /**
2 License: public domain
3 Authors: Simon Bürger
4 */
5 
6 module jive.priorityarray;
7 
8 import std.algorithm;
9 import std.range;
10 import std.functional;
11 import jive.array;
12 import jive.internal;
13 
14 /**
15  * Array-like structure with fast access to the smallest element.
16  *
17  * Implemented using a segment tree.
18  * TODO: more array-like operations (e.g. pushBack, popBack, maybe iteration)
19  */
20 struct PriorityArray(V, alias _pred = "a < b")
21 {
22 	// NOTE: seg[0] is unused, all unused are set to -1. length of seg is always an even power of 2
23 	private Array!int seg;
24 	private Array!V data;
25 
26 	mixin PredicateHelper!(_pred, V);
27 
28 	static if(dynamicPred)
29 	{
30 		/** constructor for given size */
31 		this(_pred p, size_t size)
32 		{
33 			this.pred = p;
34 
35 			data.resize(size);
36 
37 			size_t sizeRounded = 2;
38 			while(sizeRounded < size)
39 				sizeRounded *= 2;
40 			seg.resize(sizeRounded, -1);
41 			for(int i = 0; i < length/2; ++i)
42 				seg[seg.length/2 + i] = i*2;
43 			for(int i = cast(int)seg.length/2-1; i > 0; --i)
44 				seg[i] = seg[2*i];
45 		}
46 
47 		/** constructor taking a range */
48 		this(Stuff)(_pred p, Stuff data)
49 			if(isInputRange!Stuff && is(ElementType!Stuff:V) && hasLength!Stuff)
50 		{
51 			this(p, data.length);
52 			size_t i = 0;
53 			foreach(x; data)
54 				this[i++] = x;
55 		}
56 	}
57 	else
58 	{
59 		/** constructor for given size */
60 		this(size_t size)
61 		{
62 			data.resize(size);
63 
64 			size_t sizeRounded = 2;
65 			while(sizeRounded < size)
66 				sizeRounded *= 2;
67 			seg.resize(sizeRounded, -1);
68 			for(int i = 0; i < length/2; ++i)
69 				seg[seg.length/2 + i] = i*2;
70 			for(int i = cast(int)seg.length/2-1; i > 0; --i)
71 				seg[i] = seg[2*i];
72 		}
73 
74 		/** constructor taking a range */
75 		this(Stuff)(Stuff data)
76 			if(isInputRange!Stuff && is(ElementType!Stuff:V) && hasLength!Stuff)
77 		{
78 			this(data.length);
79 			size_t i = 0;
80 			foreach(x; data)
81 				this[i++] = x;
82 		}
83 	}
84 
85 	/** number of elements in the array */
86 	size_t length() const @property
87 	{
88 		return data.length;
89 	}
90 
91 	/** ditto */
92 	size_t opDollar() const @property
93 	{
94 		return data.length;
95 	}
96 
97 	/**
98 	 * Allocated heap memory in bytes.
99 	 * This is recursive if V has a `.memUsage` property.
100 	 */
101 	size_t memUsage() const pure nothrow @property @trusted
102 	{
103 		return seg.memUsage + data.memUsage;
104 	}
105 
106 	/** read-only access to the i'th element */
107 	inout(V) opIndex(size_t i) inout
108 	{
109 		return data[i];
110 	}
111 
112 	/** read-only access to all elements */
113 	const(V)[] opSlice() const
114 	{
115 		return data[];
116 	}
117 
118 	/** set element at i to value v ( O(log n) ) */
119 	void opIndexAssign(V v, size_t _i)
120 	{
121 		data[_i] = move(v); // this performs the bounds check, so that the conversion to int is fine
122 		int i = cast(int)_i;
123 
124 		int k = cast(int)i/2 + cast(int)seg.length/2;
125 
126 		if((i^1) < length && pred(data[i^1], data[i]))
127 			seg[k] = i^1;
128 		else
129 			seg[k] = i;
130 
131 		for(; k != 1; k /= 2)
132 			if(seg[k^1] != -1 && pred(data[seg[k^1]], data[seg[k]]))
133 				seg[k/2] = seg[k^1];
134 			else
135 				seg[k/2] = seg[k];
136 	}
137 
138 	/** returns index of smallest element ( O(1) ) */
139 	size_t minIndex() const
140 	{
141 		return seg[1];
142 	}
143 
144 	/** returns smallest element ( O(1) ) */
145 	ref const(V) min() const
146 	{
147 		return data[seg[1]];
148 	}
149 }
150 
151 ///
152 /+@nogc+/ nothrow pure @safe unittest
153 {
154 	auto a = PriorityArray!int([7,9,2,3,4,1,6,5,8,0]);
155 	assert(a[] == [7,9,2,3,4,1,6,5,8,0]);
156 
157 	assert(a.minIndex == 9);
158 	assert(a.min == 0);
159 
160 	a[9] = 100;
161 
162 	assert(a.minIndex == 5);
163 	assert(a.min == 1);
164 
165 	a[2] = -3;
166 
167 	assert(a.minIndex == 2);
168 	assert(a.min == -3);
169 }
170 
171 
172 /// custom predicate
173 unittest
174 {
175 	struct Cmp
176 	{
177 		bool opCall(int a, int b)
178 		{
179 			return a > b;
180 		}
181 	}
182 
183 	// The function Cmp.opCall is not static. Therefore the constructor of
184 	// PriorityArray takes an instance of Cmp
185 	auto a = PriorityArray!(int, Cmp)(Cmp.init, [-7,-9,-2,-3,-4,-1,-6,-5,-8,-0]);
186 	assert(a[] == [-7,-9,-2,-3,-4,-1,-6,-5,-8,-0]);
187 
188 	assert(a.minIndex == 9);
189 	assert(a.min == -0);
190 
191 	a[9] = -100;
192 
193 	assert(a.minIndex == 5);
194 	assert(a.min == -1);
195 
196 	a[2] = 3;
197 
198 	assert(a.minIndex == 2);
199 	assert(a.min == 3);
200 }