| 1 |
/**Some extra ranges that were not included in std.range. |
|---|
| 2 |
* |
|---|
| 3 |
* Author: David Simcha*/ |
|---|
| 4 |
/* |
|---|
| 5 |
* You may use this software under your choice of either of the following |
|---|
| 6 |
* licenses. YOU NEED ONLY OBEY THE TERMS OF EXACTLY ONE OF THE TWO LICENSES. |
|---|
| 7 |
* IF YOU CHOOSE TO USE THE PHOBOS LICENSE, YOU DO NOT NEED TO OBEY THE TERMS OF |
|---|
| 8 |
* THE BSD LICENSE. IF YOU CHOOSE TO USE THE BSD LICENSE, YOU DO NOT NEED |
|---|
| 9 |
* TO OBEY THE TERMS OF THE PHOBOS LICENSE. IF YOU ARE A LAWYER LOOKING FOR |
|---|
| 10 |
* LOOPHOLES AND RIDICULOUSLY NON-EXISTENT AMBIGUITIES IN THE PREVIOUS STATEMENT, |
|---|
| 11 |
* GET A LIFE. |
|---|
| 12 |
* |
|---|
| 13 |
* ---------------------Phobos License: --------------------------------------- |
|---|
| 14 |
* |
|---|
| 15 |
* Copyright (C) 2009 by David Simcha. |
|---|
| 16 |
* |
|---|
| 17 |
* This software is provided 'as-is', without any express or implied |
|---|
| 18 |
* warranty. In no event will the authors be held liable for any damages |
|---|
| 19 |
* arising from the use of this software. |
|---|
| 20 |
* |
|---|
| 21 |
* Permission is granted to anyone to use this software for any purpose, |
|---|
| 22 |
* including commercial applications, and to alter it and redistribute it |
|---|
| 23 |
* freely, in both source and binary form, subject to the following |
|---|
| 24 |
* restrictions: |
|---|
| 25 |
* |
|---|
| 26 |
* o The origin of this software must not be misrepresented; you must not |
|---|
| 27 |
* claim that you wrote the original software. If you use this software |
|---|
| 28 |
* in a product, an acknowledgment in the product documentation would be |
|---|
| 29 |
* appreciated but is not required. |
|---|
| 30 |
* o Altered source versions must be plainly marked as such, and must not |
|---|
| 31 |
* be misrepresented as being the original software. |
|---|
| 32 |
* o This notice may not be removed or altered from any source |
|---|
| 33 |
* distribution. |
|---|
| 34 |
* |
|---|
| 35 |
* --------------------BSD License: ----------------------------------------- |
|---|
| 36 |
* |
|---|
| 37 |
* Copyright (c) 2009, David Simcha |
|---|
| 38 |
* All rights reserved. |
|---|
| 39 |
* |
|---|
| 40 |
* Redistribution and use in source and binary forms, with or without |
|---|
| 41 |
* modification, are permitted provided that the following conditions are met: |
|---|
| 42 |
* |
|---|
| 43 |
* * Redistributions of source code must retain the above copyright |
|---|
| 44 |
* notice, this list of conditions and the following disclaimer. |
|---|
| 45 |
* |
|---|
| 46 |
* * Redistributions in binary form must reproduce the above copyright |
|---|
| 47 |
* notice, this list of conditions and the following disclaimer in the |
|---|
| 48 |
* documentation and/or other materials provided with the distribution. |
|---|
| 49 |
* |
|---|
| 50 |
* * Neither the name of the authors nor the |
|---|
| 51 |
* names of its contributors may be used to endorse or promote products |
|---|
| 52 |
* derived from this software without specific prior written permission. |
|---|
| 53 |
* |
|---|
| 54 |
* THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY |
|---|
| 55 |
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|---|
| 56 |
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
|---|
| 57 |
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY |
|---|
| 58 |
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
|---|
| 59 |
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
|---|
| 60 |
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
|---|
| 61 |
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|---|
| 62 |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
|---|
| 63 |
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|---|
| 64 |
*/ |
|---|
| 65 |
module rangeextra; |
|---|
| 66 |
|
|---|
| 67 |
import std.range, std.traits, std.typetuple, std.typecons, core.memory, |
|---|
| 68 |
std.array, std.functional; |
|---|
| 69 |
|
|---|
| 70 |
version(unittest) { |
|---|
| 71 |
import std.algorithm, std.conv, std.stdio;void main(){} |
|---|
| 72 |
} |
|---|
| 73 |
|
|---|
| 74 |
template hasLength(T) { |
|---|
| 75 |
enum bool hasLength = is(typeof(T.init.length)) || |
|---|
| 76 |
is(typeof(T.init.length())); |
|---|
| 77 |
} |
|---|
| 78 |
|
|---|
| 79 |
/**Tests whether T can be iterated over using foreach. This is a superset |
|---|
| 80 |
* of isInputRange, as it also accepts things that use opApply, builtin |
|---|
| 81 |
* arrays, builtin associative arrays, etc. Useful when all you need is |
|---|
| 82 |
* lowest common denominator iteration functionality and you don't care about |
|---|
| 83 |
* more advanced range features.*/ |
|---|
| 84 |
template isIterable(T) |
|---|
| 85 |
{ |
|---|
| 86 |
static if (is(typeof({foreach(elem; T.init) {}}))) { |
|---|
| 87 |
enum bool isIterable = true; |
|---|
| 88 |
} else { |
|---|
| 89 |
enum bool isIterable = false; |
|---|
| 90 |
} |
|---|
| 91 |
} |
|---|
| 92 |
|
|---|
| 93 |
unittest { |
|---|
| 94 |
struct Foo { // For testing opApply. |
|---|
| 95 |
// For testing. |
|---|
| 96 |
|
|---|
| 97 |
int opApply(int delegate(ref uint) dg) { assert(0); } |
|---|
| 98 |
} |
|---|
| 99 |
|
|---|
| 100 |
static assert(isIterable!(uint[])); |
|---|
| 101 |
static assert(!isIterable!(uint)); |
|---|
| 102 |
static assert(isIterable!(Foo)); |
|---|
| 103 |
static assert(isIterable!(uint[string])); |
|---|
| 104 |
static assert(isIterable!(Chain!(uint[], uint[]))); |
|---|
| 105 |
} |
|---|
| 106 |
|
|---|
| 107 |
/**Determine the iterable type of any iterable object, regardless of whether |
|---|
| 108 |
* it uses ranges, opApply, etc. This is typeof(elem) if one does |
|---|
| 109 |
* foreach(elem; T.init) {}.*/ |
|---|
| 110 |
template IterType(T) { |
|---|
| 111 |
alias ReturnType!( |
|---|
| 112 |
{ |
|---|
| 113 |
foreach(elem; T.init) { |
|---|
| 114 |
return elem; |
|---|
| 115 |
} |
|---|
| 116 |
}) IterType; |
|---|
| 117 |
} |
|---|
| 118 |
|
|---|
| 119 |
unittest { |
|---|
| 120 |
struct Foo { // For testing opApply. |
|---|
| 121 |
// For testing. |
|---|
| 122 |
|
|---|
| 123 |
int opApply(int delegate(ref uint) dg) { assert(0); } |
|---|
| 124 |
} |
|---|
| 125 |
|
|---|
| 126 |
static assert(is(IterType!(uint[]) == uint)); |
|---|
| 127 |
static assert(is(IterType!(Foo) == uint)); |
|---|
| 128 |
static assert(is(IterType!(uint[string]) == uint)); |
|---|
| 129 |
static assert(is(IterType!(Chain!(uint[], uint[])) == uint)); |
|---|
| 130 |
} |
|---|
| 131 |
|
|---|
| 132 |
/// |
|---|
| 133 |
struct Reindex(alias lambda, T) |
|---|
| 134 |
if(isRandomAccessRange!(T)) { |
|---|
| 135 |
private: |
|---|
| 136 |
alias unaryFun!(lambda) reindexTempl; |
|---|
| 137 |
|
|---|
| 138 |
static if(is(ParameterTypeTuple!(reindexTempl))) { |
|---|
| 139 |
alias ParameterTypeTuple!(reindexTempl)[0] I; |
|---|
| 140 |
alias reindexTempl reindexFun; |
|---|
| 141 |
} else { |
|---|
| 142 |
alias reindexTempl!(size_t) reindexFun; |
|---|
| 143 |
alias size_t I; |
|---|
| 144 |
} |
|---|
| 145 |
|
|---|
| 146 |
alias ReturnType!(reindexFun) R; |
|---|
| 147 |
static assert(is(typeof(range[R.init])), "Cannot index a " ~ T.stringof ~ |
|---|
| 148 |
" with a " ~ R.stringof ~ "."); |
|---|
| 149 |
alias ElementType!(T) E; |
|---|
| 150 |
|
|---|
| 151 |
public: |
|---|
| 152 |
T range; |
|---|
| 153 |
alias range this; |
|---|
| 154 |
|
|---|
| 155 |
static if(hasAssignableElements!(T)) { |
|---|
| 156 |
ref E opIndex(I index) { |
|---|
| 157 |
return range[reindexFun(index)]; |
|---|
| 158 |
} |
|---|
| 159 |
|
|---|
| 160 |
auto opIndexAssign(E val, I index) { |
|---|
| 161 |
return range[reindexFun(index)] = val; |
|---|
| 162 |
} |
|---|
| 163 |
} else { |
|---|
| 164 |
E opIndex(I index) { |
|---|
| 165 |
return range[reindexFun(index)]; |
|---|
| 166 |
} |
|---|
| 167 |
} |
|---|
| 168 |
|
|---|
| 169 |
static if(is(typeof(range[R.init..R.init]) == T)) { |
|---|
| 170 |
typeof(this) opSlice(I index1, I index2) { |
|---|
| 171 |
return typeof(this) |
|---|
| 172 |
(range[reindexFun(index1)..reindexFun(index2)]); |
|---|
| 173 |
} |
|---|
| 174 |
} |
|---|
| 175 |
|
|---|
| 176 |
static if(is(typeof(range[R.init..R.init] = E.init))) { |
|---|
| 177 |
void opSliceAssign(E val, I index1, I index2) { |
|---|
| 178 |
range[reindexFun(index1)..reindexFun(index2)] = val; |
|---|
| 179 |
} |
|---|
| 180 |
} |
|---|
| 181 |
|
|---|
| 182 |
static if(is(typeof(range[R.init..R.init] = T.init))) { |
|---|
| 183 |
void opSliceAssign(T otherRange, I index1, I index2) { |
|---|
| 184 |
return range[reindexFun(index1)..reindexFun(index2)] = otherRange; |
|---|
| 185 |
} |
|---|
| 186 |
} |
|---|
| 187 |
} |
|---|
| 188 |
|
|---|
| 189 |
/**Reindex a random-access range by translating its indices via the function |
|---|
| 190 |
* lambda. All properties and methods of the underlying range that do not |
|---|
| 191 |
* involve indexing are forwarded to the underlying range using alias this. |
|---|
| 192 |
* |
|---|
| 193 |
* Examples: |
|---|
| 194 |
* --- |
|---|
| 195 |
* // An array indexed by only even numbers. |
|---|
| 196 |
* auto evenArray = reindex!"a / 2"([0,2,4,6,8,10].dup); |
|---|
| 197 |
* for(uint i = 0; i <= 10; i += 2) { |
|---|
| 198 |
* assert(evenArray[i] == i); |
|---|
| 199 |
* } |
|---|
| 200 |
* |
|---|
| 201 |
* // A one-indexed array. |
|---|
| 202 |
* auto oneArray = reindex!"a - 1"([1,2,3,4,5].dup); |
|---|
| 203 |
* for(uint i = 1; i <= 5; i++) { |
|---|
| 204 |
* assert(oneArray[i] == i); |
|---|
| 205 |
* } |
|---|
| 206 |
* --- |
|---|
| 207 |
*/ |
|---|
| 208 |
Reindex!(lambda, T) reindex(alias lambda, T)(T range) { |
|---|
| 209 |
return Reindex!(lambda, T)(range); |
|---|
| 210 |
} |
|---|
| 211 |
|
|---|
| 212 |
unittest { |
|---|
| 213 |
auto oneArray = reindex!"a - 1"([1,2,3,4,5].dup); |
|---|
| 214 |
|
|---|
| 215 |
for(uint i = 1; i <= 5; i++) { |
|---|
| 216 |
assert(oneArray[i] == i); |
|---|
| 217 |
} |
|---|
| 218 |
|
|---|
| 219 |
oneArray[1..3] = 1; |
|---|
| 220 |
assert(oneArray[1] == 1); |
|---|
| 221 |
assert(oneArray[2] == 1); |
|---|
| 222 |
assert(oneArray[3] == 3); |
|---|
| 223 |
|
|---|
| 224 |
auto last3 = oneArray[3..6]; |
|---|
| 225 |
assert(last3[3] == 5); |
|---|
| 226 |
|
|---|
| 227 |
auto evenArray = reindex!"a / 2"([0,2,4,6,8,10].dup); |
|---|
| 228 |
for(uint i = 0; i <= 10; i += 2) { |
|---|
| 229 |
assert(evenArray[i] == i); |
|---|
| 230 |
} |
|---|
| 231 |
writeln("Passed Reindex test."); |
|---|
| 232 |
} |
|---|
| 233 |
|
|---|
| 234 |
/// |
|---|
| 235 |
struct Rotated(T) |
|---|
| 236 |
if(isRandomAccessRange!(T) && hasLength!(T)) { |
|---|
| 237 |
T range; |
|---|
| 238 |
private size_t startFrom; |
|---|
| 239 |
private size_t nIter; // For iteration. |
|---|
| 240 |
alias ElementType!(T) E; |
|---|
| 241 |
|
|---|
| 242 |
void setStart(size_t start) { |
|---|
| 243 |
startFrom = start; |
|---|
| 244 |
} |
|---|
| 245 |
|
|---|
| 246 |
size_t length() { |
|---|
| 247 |
return range.length; |
|---|
| 248 |
} |
|---|
| 249 |
|
|---|
| 250 |
static if(hasAssignableElements!(T)) { |
|---|
| 251 |
ref E opIndex(size_t index) { |
|---|
| 252 |
return range[ startFrom + index < length ? |
|---|
| 253 |
startFrom + index : |
|---|
| 254 |
startFrom + index - length]; |
|---|
| 255 |
} |
|---|
| 256 |
|
|---|
| 257 |
auto opIndexAssign(E val, size_t index) { |
|---|
| 258 |
return (startFrom + index < length) ? |
|---|
| 259 |
(range[startFrom + index] = val) : |
|---|
| 260 |
(range[startFrom + index - length] = val); |
|---|
| 261 |
} |
|---|
| 262 |
} else { |
|---|
| 263 |
E opIndex(size_t index) { |
|---|
| 264 |
return range[ startFrom + index < length ? |
|---|
| 265 |
startFrom + index : |
|---|
| 266 |
startFrom + index - length]; |
|---|
| 267 |
} |
|---|
| 268 |
} |
|---|
| 269 |
|
|---|
| 270 |
E front() { |
|---|
| 271 |
return this[0]; |
|---|
| 272 |
} |
|---|
| 273 |
|
|---|
| 274 |
void popFront() { |
|---|
| 275 |
startFrom++; |
|---|
| 276 |
nIter++; |
|---|
| 277 |
} |
|---|
| 278 |
|
|---|
| 279 |
bool empty() { |
|---|
| 280 |
return nIter == length; |
|---|
| 281 |
} |
|---|
| 282 |
|
|---|
| 283 |
void put(E elem) { |
|---|
| 284 |
startFrom = (startFrom == length - 1) ? 0 : startFrom + 1; |
|---|
| 285 |
immutable pos = startFrom == 0 ? length - 1 : startFrom - 1; |
|---|
| 286 |
range[pos] = elem; |
|---|
| 287 |
} |
|---|
| 288 |
} |
|---|
| 289 |
|
|---|
| 290 |
/**Gives a rotated view of a random access range without modifying the |
|---|
| 291 |
* underlying range. Also funtions as an output range. Given an underlying |
|---|
| 292 |
* range of length L, a Rotated range remembers the last L elements inserted |
|---|
| 293 |
* via its put() interface, in the order that they were inserted. Insertions |
|---|
| 294 |
* are O(1), but to the user of the API, the effect is similar to shifting |
|---|
| 295 |
* an array on each insertion. |
|---|
| 296 |
* |
|---|
| 297 |
* Examples: |
|---|
| 298 |
* --- |
|---|
| 299 |
* uint[] arr = [5,6,7,8,9]; |
|---|
| 300 |
* auto rot = rotated(arr, 2); |
|---|
| 301 |
* assert(rot[0] == 7); |
|---|
| 302 |
* assert(rot[3] == 5); |
|---|
| 303 |
* assert(rot.length == 5); |
|---|
| 304 |
* |
|---|
| 305 |
* rot.put(0); |
|---|
| 306 |
* rot.put(1); |
|---|
| 307 |
* rot.put(2); |
|---|
| 308 |
* rot.put(3); |
|---|
| 309 |
* rot.put(4); |
|---|
| 310 |
* foreach(i; 0..5) { |
|---|
| 311 |
* assert(rot[i] == i); |
|---|
| 312 |
* } |
|---|
| 313 |
* --- |
|---|
| 314 |
*/ |
|---|
| 315 |
Rotated!(T) rotated(T)(T range, size_t startPos = 0) |
|---|
| 316 |
if(isRandomAccessRange!(T) && hasLength!(T)) |
|---|
| 317 |
in { |
|---|
| 318 |
assert(startPos < range.length); |
|---|
| 319 |
} body { |
|---|
| 320 |
alias Rotated!(T) t; |
|---|
| 321 |
return t(range, startPos, 0); |
|---|
| 322 |
} |
|---|
| 323 |
|
|---|
| 324 |
unittest { |
|---|
| 325 |
uint[] arr = [5,6,7,8,9]; |
|---|
| 326 |
auto rot = rotated(arr, 2); |
|---|
| 327 |
assert(rot[0] == 7); |
|---|
| 328 |
assert(rot[1] == 8); |
|---|
| 329 |
assert(rot[2] == 9); |
|---|
| 330 |
assert(rot[3] == 5); |
|---|
| 331 |
assert(rot[4] == 6); |
|---|
| 332 |
assert(rot.length == 5); |
|---|
| 333 |
|
|---|
| 334 |
rot.put(0); |
|---|
| 335 |
rot.put(1); |
|---|
| 336 |
rot.put(2); |
|---|
| 337 |
rot.put(3); |
|---|
| 338 |
rot.put(4); |
|---|
| 339 |
foreach(i; 0..5) { |
|---|
| 340 |
assert(rot[i] == i); |
|---|
| 341 |
} |
|---|
| 342 |
|
|---|
| 343 |
writeln("Passed rotated unittest."); |
|---|
| 344 |
} |
|---|
| 345 |
|
|---|
| 346 |
/**A struct wrapper for a static array, to get around the fact that static |
|---|
| 347 |
* arrays can't be returned from functions. Alias this is used to forward |
|---|
| 348 |
* all operations to the underlying static array.*/ |
|---|
| 349 |
struct StaticArray(E, size_t N) { |
|---|
| 350 |
/// |
|---|
| 351 |
E[N] values; |
|---|
| 352 |
alias values this; |
|---|
| 353 |
} |
|---|
| 354 |
|
|---|
| 355 |
/// |
|---|
| 356 |
struct Comb(size_t N, T) |
|---|
| 357 |
if(isForwardRange!(T)){ |
|---|
| 358 |
private: |
|---|
| 359 |
alias ElementType!(T) E; |
|---|
| 360 |
T[N] ranges = void; |
|---|
| 361 |
bool _empty; |
|---|
| 362 |
|
|---|
| 363 |
void popAll(size_t startFrom) { |
|---|
| 364 |
if(startFrom == size_t.max) { |
|---|
| 365 |
_empty = true; |
|---|
| 366 |
return; |
|---|
| 367 |
} |
|---|
| 368 |
ranges[startFrom].popFront; |
|---|
| 369 |
if(ranges[startFrom].empty) { |
|---|
| 370 |
return popAll(startFrom - 1); |
|---|
| 371 |
} |
|---|
| 372 |
foreach(i; startFrom + 1..N) { |
|---|
| 373 |
ranges[i] = ranges[i - 1]; |
|---|
| 374 |
ranges[i].popFront; |
|---|
| 375 |
if(ranges[i].empty) { |
|---|
| 376 |
return popAll(startFrom - 1); |
|---|
| 377 |
} |
|---|
| 378 |
} |
|---|
| 379 |
} |
|---|
| 380 |
|
|---|
| 381 |
|
|---|
| 382 |
public: |
|---|
| 383 |
this(T range) { |
|---|
| 384 |
static if(N > 0) { |
|---|
| 385 |
ranges[0] = range; |
|---|
| 386 |
foreach(i; 1..N) { |
|---|
| 387 |
ranges[i] = ranges[i - 1]; |
|---|
| 388 |
ranges[i].popFront; |
|---|
| 389 |
} |
|---|
| 390 |
} |
|---|
| 391 |
} |
|---|
| 392 |
|
|---|
| 393 |
StaticArray!(E, N) front() { |
|---|
| 394 |
StaticArray!(E, N) ret; |
|---|
| 395 |
foreach(i, range; ranges) { |
|---|
| 396 |
ret[i] = range.front; |
|---|
| 397 |
} |
|---|
| 398 |
return ret; |
|---|
| 399 |
} |
|---|
| 400 |
|
|---|
| 401 |
|
|---|
| 402 |
void popFront() { |
|---|
| 403 |
static if(N > 0) { |
|---|
| 404 |
popAll(N - 1); |
|---|
| 405 |
} |
|---|
| 406 |
} |
|---|
| 407 |
|
|---|
| 408 |
static if(N > 0) { |
|---|
| 409 |
bool empty() { |
|---|
| 410 |
return _empty; |
|---|
| 411 |
} |
|---|
| 412 |
} else { |
|---|
| 413 |
enum bool empty = true; |
|---|
| 414 |
} |
|---|
| 415 |
} |
|---|
| 416 |
|
|---|
| 417 |
/**A struct to iterate over all possible unordered combinations of elements |
|---|
| 418 |
* of size N, in a forward range. This struct is itself a forward range. |
|---|
| 419 |
* Each call to front() returns a StaticArray of size N, containing the |
|---|
| 420 |
* relevant elements of the range. |
|---|
| 421 |
* |
|---|
| 422 |
* Examples: |
|---|
| 423 |
* --- |
|---|
| 424 |
* auto foo = map!(to!(uint, string))(cast(string[]) ["1", "2", "3"]); |
|---|
| 425 |
* auto c = comb!2(foo); |
|---|
| 426 |
* assert(c.front == [1,2]); |
|---|
| 427 |
* c.popFront; |
|---|
| 428 |
* assert(c.front == [1,3]); |
|---|
| 429 |
* c.popFront; |
|---|
| 430 |
* assert(c.front == [2,3]); |
|---|
| 431 |
* c.popFront; |
|---|
| 432 |
* assert(c.empty); |
|---|
| 433 |
* --- |
|---|
| 434 |
*/ |
|---|
| 435 |
Comb!(N, T) comb(uint N, T)(T range) |
|---|
| 436 |
if(isForwardRange!(T)) { |
|---|
| 437 |
return Comb!(N, T)(range); |
|---|
| 438 |
} |
|---|
| 439 |
|
|---|
| 440 |
unittest { |
|---|
| 441 |
auto foo = map!(to!(uint, string))(cast(string[]) ["1", "2", "3"]); |
|---|
| 442 |
auto c = comb!2(foo); |
|---|
| 443 |
assert(c.front == [1,2]); |
|---|
| 444 |
c.popFront; |
|---|
| 445 |
assert(c.front == [1,3]); |
|---|
| 446 |
c.popFront; |
|---|
| 447 |
assert(c.front == [2,3]); |
|---|
| 448 |
c.popFront; |
|---|
| 449 |
assert(c.empty); |
|---|
| 450 |
writeln("Passed comb unittest."); |
|---|
| 451 |
} |
|---|
| 452 |
|
|---|
| 453 |
template ElementsTuple(T...) { |
|---|
| 454 |
static if(T.length == 1) { |
|---|
| 455 |
alias TypeTuple!(Unqual!(ElementType!(T[0]))) ElementsTuple; |
|---|
| 456 |
} else { |
|---|
| 457 |
alias TypeTuple!(Unqual!(ElementType!(T[0])), ElementsTuple!(T[1..$])) |
|---|
| 458 |
ElementsTuple; |
|---|
| 459 |
} |
|---|
| 460 |
} |
|---|
| 461 |
|
|---|
| 462 |
template SameElemTypes(T...) { |
|---|
| 463 |
static if(T.length == 1) { |
|---|
| 464 |
enum bool SameElemTypes = true; |
|---|
| 465 |
} else { |
|---|
| 466 |
enum bool SameElemTypes = |
|---|
| 467 |
is(Unqual!(ElementType!(T[0])) == Unqual!(ElementType!(T[1]))) && |
|---|
| 468 |
SameElemTypes!(T[1..$]); |
|---|
| 469 |
} |
|---|
| 470 |
} |
|---|
| 471 |
|
|---|
| 472 |
template UnqualAll(T...) { |
|---|
| 473 |
static if(T.length == 1) { |
|---|
| 474 |
alias TypeTuple!(Unqual!(T[0])) UnqualAll; |
|---|
| 475 |
} else { |
|---|
| 476 |
alias TypeTuple!(Unqual!(T[0]), UnqualAll!(T[1..$])) UnqualAll; |
|---|
| 477 |
} |
|---|
| 478 |
} |
|---|
| 479 |
|
|---|
| 480 |
/// |
|---|
| 481 |
struct Broadcast(T...) |
|---|
| 482 |
if(allSatisfy!(isForwardRange, T)) { |
|---|
| 483 |
private : |
|---|
| 484 |
UnqualAll!T starts; |
|---|
| 485 |
UnqualAll!T ranges; |
|---|
| 486 |
|
|---|
| 487 |
void popFrontImpl(uint index)() { |
|---|
| 488 |
ranges[index].popFront; |
|---|
| 489 |
if(ranges[index].empty) { |
|---|
| 490 |
static if(index == 0) { |
|---|
| 491 |
return; |
|---|
| 492 |
} else { |
|---|
| 493 |
ranges[index] = starts[index]; |
|---|
| 494 |
return popFrontImpl!(index - 1)(); |
|---|
| 495 |
} |
|---|
| 496 |
} |
|---|
| 497 |
} |
|---|
| 498 |
|
|---|
| 499 |
public: |
|---|
| 500 |
this(T args) { |
|---|
| 501 |
starts = args; |
|---|
| 502 |
ranges = args; |
|---|
| 503 |
} |
|---|
| 504 |
|
|---|
| 505 |
void popFront() { |
|---|
| 506 |
popFrontImpl!(T.length - 1)(); |
|---|
| 507 |
} |
|---|
| 508 |
|
|---|
| 509 |
static if(SameElemTypes!(T)) { |
|---|
| 510 |
StaticArray!(Unqual!(ElementType!(T[0])), T.length) front() { |
|---|
| 511 |
typeof(return) ret; |
|---|
| 512 |
foreach(ti, range; ranges) { |
|---|
| 513 |
ret[ti] = range.front; |
|---|
| 514 |
} |
|---|
| 515 |
return ret; |
|---|
| 516 |
} |
|---|
| 517 |
} else { |
|---|
| 518 |
Tuple!(ElementsTuple!(T)) front() { |
|---|
| 519 |
ElementsTuple!(T) ret; |
|---|
| 520 |
foreach(ti, range; ranges) { |
|---|
| 521 |
ret[ti] = range.front; |
|---|
| 522 |
} |
|---|
| 523 |
return tuple(ret); |
|---|
| 524 |
} |
|---|
| 525 |
} |
|---|
| 526 |
|
|---|
| 527 |
bool empty() { |
|---|
| 528 |
return ranges[0].empty; |
|---|
| 529 |
} |
|---|
| 530 |
} |
|---|
| 531 |
|
|---|
| 532 |
/**Given a set of forward ranges, iterate over all possible tuples composed of |
|---|
| 533 |
* [range1Element, range2Element, ..., rangeNElement]. Calling front() |
|---|
| 534 |
* returns a StaticArray if the element types of all of the ranges are |
|---|
| 535 |
* identical and a std.typecons.Tuple otherwise. |
|---|
| 536 |
* |
|---|
| 537 |
* Examples: |
|---|
| 538 |
* --- |
|---|
| 539 |
* // Iterate over all possible RNA codons. |
|---|
| 540 |
* immutable bases = "ACGU"; |
|---|
| 541 |
* auto codonRange = broadcast(bases, bases, bases); |
|---|
| 542 |
* assert(codonRange.front == "AAA"); |
|---|
| 543 |
* codonRange.popFront; |
|---|
| 544 |
* assert(codonRange.front == "AAC"); |
|---|
| 545 |
* --- |
|---|
| 546 |
*/ |
|---|
| 547 |
Broadcast!(T) broadcast(T...)(T ranges) { |
|---|
| 548 |
return Broadcast!(T)(ranges); |
|---|
| 549 |
} |
|---|
| 550 |
|
|---|
| 551 |
unittest { |
|---|
| 552 |
immutable letters = "AB"; |
|---|
| 553 |
auto n = broadcast(letters, letters, letters); |
|---|
| 554 |
assert(n.front == "AAA"); |
|---|
| 555 |
n.popFront; |
|---|
| 556 |
assert(n.front == "AAB"); |
|---|
| 557 |
n.popFront; |
|---|
| 558 |
assert(n.front == "ABA"); |
|---|
| 559 |
n.popFront; |
|---|
| 560 |
assert(n.front == "ABB"); |
|---|
| 561 |
n.popFront; |
|---|
| 562 |
assert(n.front == "BAA"); |
|---|
| 563 |
n.popFront; |
|---|
| 564 |
assert(n.front == "BAB"); |
|---|
| 565 |
n.popFront; |
|---|
| 566 |
assert(n.front == "BBA"); |
|---|
| 567 |
n.popFront; |
|---|
| 568 |
assert(n.front == "BBB"); |
|---|
| 569 |
n.popFront; |
|---|
| 570 |
assert(n.empty); |
|---|
| 571 |
writeln("Passed Broadcast unittest."); |
|---|
| 572 |
} |
|---|
| 573 |
|
|---|
| 574 |
template AppendableImpl(T, U) { |
|---|
| 575 |
T[] data; |
|---|
| 576 |
U elem; |
|---|
| 577 |
enum bool ret = is(typeof(data ~= elem)); |
|---|
| 578 |
} |
|---|
| 579 |
|
|---|
| 580 |
template Appendable(T, U) { |
|---|
| 581 |
enum bool Appendable = AppendableImpl!(T, U).ret; |
|---|
| 582 |
} |
|---|
| 583 |
|
|---|
| 584 |
/**An array with a capacity field. It's based on D's builtin arrays and |
|---|
| 585 |
* forwards everything except appending and resizing to the underlying builtin |
|---|
| 586 |
* array using alias this. Therefore, the compile time interface and semantics |
|---|
| 587 |
* of a TNew are the same as those of a builtin array, modulo a few bugs in |
|---|
| 588 |
* alias this. |
|---|
| 589 |
* |
|---|
| 590 |
* Examples: |
|---|
| 591 |
* --- |
|---|
| 592 |
* TNew!uint ab, ab2; |
|---|
| 593 |
* foreach(i; 0..5) { |
|---|
| 594 |
* ab ~= i; |
|---|
| 595 |
* ab2 ~= [i]; |
|---|
| 596 |
* } |
|---|
| 597 |
* assert(ab == cast(uint[]) [0,1,2,3,4]); |
|---|
| 598 |
* assert(ab2 == cast(uint[]) [0,1,2,3,4]); |
|---|
| 599 |
* --- |
|---|
| 600 |
*/ |
|---|
| 601 |
struct TNew(T) { |
|---|
| 602 |
/**Access underlying array directly. Useful for getting around some |
|---|
| 603 |
* alias this bugs.*/ |
|---|
| 604 |
T[] arr; |
|---|
| 605 |
alias arr this; |
|---|
| 606 |
private size_t capacity = 0; |
|---|
| 607 |
|
|---|
| 608 |
|
|---|
| 609 |
private void updateCapacity() { |
|---|
| 610 |
capacity = GC.sizeOf(cast(void*) arr.ptr) / T.sizeof; |
|---|
| 611 |
} |
|---|
| 612 |
|
|---|
| 613 |
/// |
|---|
| 614 |
this(T[] newData) { |
|---|
| 615 |
arr = newData; |
|---|
| 616 |
} |
|---|
| 617 |
|
|---|
| 618 |
/**Append a single element.*/ |
|---|
| 619 |
typeof(this) opCatAssign(U)(U elem) |
|---|
| 620 |
if(Appendable!(T, U) && (Appendable!(T, U[]) || !isArray!(U))) { |
|---|
| 621 |
if(arr.length < capacity) { |
|---|
| 622 |
// Casting away const to change the last element of the array. |
|---|
| 623 |
// This is very dangerous, but since this struct is supposed to |
|---|
| 624 |
// faithfully replicate the compile time interface of builtin |
|---|
| 625 |
// arrays, that means replicating the bugs, too. |
|---|
| 626 |
*(cast(Unqual!(T)*) arr.ptr + arr.length) = elem; |
|---|
| 627 |
*(cast(size_t*) &arr) += 1; |
|---|
| 628 |
} else { |
|---|
| 629 |
arr ~= elem; |
|---|
| 630 |
updateCapacity(); |
|---|
| 631 |
} |
|---|
| 632 |
return this; |
|---|
| 633 |
} |
|---|
| 634 |
|
|---|
| 635 |
/**Append an array.*/ |
|---|
| 636 |
typeof(this) opCatAssign(U)(U elems) |
|---|
| 637 |
if(Appendable!(T, U) && !(Appendable!(T, U[]) || !isArray!(U))) { |
|---|
| 638 |
if((arr.length + elems.length) <= capacity) { |
|---|
| 639 |
// Again, casting away const to change the last element of the array. |
|---|
| 640 |
auto mutData = (cast(Unqual!(T)*) arr.ptr) |
|---|
| 641 |
[0..arr.length + elems.length]; |
|---|
| 642 |
mutData[arr.length..$] = cast(typeof(mutData)) elems[]; |
|---|
| 643 |
arr = cast(typeof(arr)) mutData; |
|---|
| 644 |
} else { |
|---|
| 645 |
arr ~= elems; |
|---|
| 646 |
updateCapacity(); |
|---|
| 647 |
} |
|---|
| 648 |
return this; |
|---|
| 649 |
} |
|---|
| 650 |
|
|---|
| 651 |
/// |
|---|
| 652 |
typeof(this) opAssign(T[] newData) { |
|---|
| 653 |
if(arr.ptr !is newData.ptr) { |
|---|
| 654 |
capacity = 0; |
|---|
| 655 |
} |
|---|
| 656 |
arr = newData; |
|---|
| 657 |
return this; |
|---|
| 658 |
} |
|---|
| 659 |
|
|---|
| 660 |
/// |
|---|
| 661 |
size_t length(size_t newLen) { |
|---|
| 662 |
if(newLen <= arr.length) { |
|---|
| 663 |
return arr.length = newLen; |
|---|
| 664 |
} |
|---|
| 665 |
if(newLen <= capacity) { |
|---|
| 666 |
auto ptr = cast(Unqual!(T)*) arr.ptr; |
|---|
| 667 |
ptr[arr.length..newLen] = T.init; |
|---|
| 668 |
*(cast(size_t*) &arr) = newLen; |
|---|
| 669 |
} else { |
|---|
| 670 |
arr.length = newLen; |
|---|
| 671 |
updateCapacity(); |
|---|
| 672 |
} |
|---|
| 673 |
return newLen; |
|---|
| 674 |
} |
|---|
| 675 |
|
|---|
| 676 |
/**Make the capacity big enough to hold at least nElem elements |
|---|
| 677 |
* without further GC activity.*/ |
|---|
| 678 |
void reserve(size_t nElem) { |
|---|
| 679 |
if(nElem <= arr.length || nElem <= capacity) { |
|---|
| 680 |
return; |
|---|
| 681 |
} |
|---|
| 682 |
updateCapacity(); |
|---|
| 683 |
if(nElem <= capacity) { |
|---|
| 684 |
return; |
|---|
| 685 |
} |
|---|
| 686 |
if(typeid(T).flags & 1) { |
|---|
| 687 |
arr = (cast(T*) GC.realloc(cast(void*) arr.ptr, nElem * T.sizeof)) |
|---|
| 688 |
[0..arr.length]; |
|---|
| 689 |
} else { |
|---|
| 690 |
arr = (cast(T*) GC.realloc(cast(void*) arr.ptr, nElem * T.sizeof, |
|---|
| 691 |
GC.BlkAttr.NO_SCAN))[0..arr.length]; |
|---|
| 692 |
} |
|---|
| 693 |
updateCapacity(); |
|---|
| 694 |
} |
|---|
| 695 |
|
|---|
| 696 |
// The following is quick and dirty fixes for stuff that's really bugs in |
|---|
| 697 |
// DMD, specifically in alias this. The appropriate bug reports have been |
|---|
| 698 |
// filed and these are just temporary kludges. They don't need ddoc |
|---|
| 699 |
// b/c they should already be expected to work via alias this. |
|---|
| 700 |
|
|---|
| 701 |
// Bugzilla 2889 |
|---|
| 702 |
size_t length() { |
|---|
| 703 |
return arr.length; |
|---|
| 704 |
} |
|---|
| 705 |
|
|---|
| 706 |
// -------------------------Bug 2778------------------------------- |
|---|
| 707 |
T front() { |
|---|
| 708 |
return arr.front; |
|---|
| 709 |
} |
|---|
| 710 |
|
|---|
| 711 |
void popFront() { |
|---|
| 712 |
return arr.popFront; |
|---|
| 713 |
} |
|---|
| 714 |
|
|---|
| 715 |
bool empty() { |
|---|
| 716 |
return arr.empty; |
|---|
| 717 |
} |
|---|
| 718 |
|
|---|
| 719 |
T back() { |
|---|
| 720 |
return arr.back; |
|---|
| 721 |
} |
|---|
| 722 |
|
|---|
| 723 |
void popBack() { |
|---|
| 724 |
return arr.popBack; |
|---|
| 725 |
} |
|---|
| 726 |
|
|---|
| 727 |
// ---------------------------------------------------------------- |
|---|
| 728 |
} |
|---|
| 729 |
|
|---|
| 730 |
/**Array builder literals.*/ |
|---|
| 731 |
TNew!(T) tNew(T)(T[] data) { |
|---|
| 732 |
TNew!(T) ret; |
|---|
| 733 |
ret = data; |
|---|
| 734 |
return ret; |
|---|
| 735 |
} |
|---|
| 736 |
|
|---|
| 737 |
unittest { |
|---|
| 738 |
TNew!uint ab, ab2; |
|---|
| 739 |
foreach(i; 0..5) { |
|---|
| 740 |
ab ~= i; |
|---|
| 741 |
ab2 ~= [i]; |
|---|
| 742 |
} |
|---|
| 743 |
assert(ab == cast(uint[]) [0,1,2,3,4]); |
|---|
| 744 |
assert(ab2 == cast(uint[]) [0,1,2,3,4]); |
|---|
| 745 |
writeln("Passed TNew unittest."); |
|---|
| 746 |
} |
|---|