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 }