1 #!/usr/bin/env rdmd-dev-module 2 3 /** Extensions to std.algorithm. 4 Copyright: Per Nordlöw 2014-. 5 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 6 Authors: $(WEB Per Nordlöw) 7 */ 8 module algorithm_ex; 9 10 /* version = print; */ 11 12 import std.algorithm : min, max; 13 import std.traits : isArray, Unqual, isIntegral, CommonType, isIterable, isStaticArray, isFloatingPoint, arity, isSomeString, isSomeChar, isExpressionTuple; 14 import std.range : ElementType, isInputRange, isForwardRange, isBidirectionalRange, isRandomAccessRange, front; 15 import traits_ex : allSameType; 16 import std.functional : unaryFun, binaryFun; 17 import std.algorithm.searching : find; 18 19 version(print) import dbg; 20 21 auto typestringof(T)(in T a) { return T.stringof; } 22 23 import std.range : dropOne; 24 alias tail = dropOne; 25 26 /** Returns: First Argument (element of $(D a)) whose implicit conversion to 27 bool is true. 28 29 Similar to behaviour of or operator in dynamic languages such as Lisp's (or 30 a...) and Python's a or .... 31 32 TODO Is inout Conversion!T the correct return value? 33 34 NOTE: Lazy parameters are currently marked as throw which makes it 35 impossible to mark either() as nothrow. Issue at 36 https://issues.dlang.org/show_bug.cgi?id=12647 37 */ 38 CommonType!Ts either(Ts...)(lazy Ts a) 39 if (a.length >= 1) 40 { 41 auto a0 = a[0](); // evaluate only once 42 static if (Ts.length == 1) 43 { 44 return a0; 45 } 46 else 47 { 48 return a0 ? a0 : either(a[1 .. $]); // recurse 49 } 50 } 51 52 T either_r(T)(lazy T a) 53 { 54 return a; 55 } 56 57 CommonType!(A, Bs) either_r(A, Bs...)(lazy A a, lazy Bs bs) 58 if (bs.length >= 1) 59 { 60 auto a_ = a(); 61 return a_ ? a_ : either_r(bs); 62 } 63 64 /** This overload enables, when possible, lvalue return. 65 TODO should we limit this template to a.length >= 2 66 */ 67 auto ref either(Ts...)(ref Ts a) 68 if (a.length >= 1 && allSameType!Ts) 69 { 70 static if (Ts.length == 1) 71 { 72 return a[0]; 73 } 74 else 75 { 76 return a[0] ? a[0] : either(a[1 .. $]); // recurse 77 } 78 } 79 @safe pure /*TODO nothrow*/ unittest 80 { 81 immutable p = 1, q = 2; 82 auto pq = either(p, q); 83 assert(pq == 1); 84 85 assert(either(3) == 3); 86 assert(either(3, 4) == 3); 87 assert(either(0, 4) == 4); 88 assert(either(0, 0) == 0); 89 assert(either(``, `a`) == ``); 90 string s = null; 91 assert(either(s, `a`) == `a`); 92 assert(either(`a`, ``) == `a`); 93 immutable a2 = [1, 2]; 94 assert(either(a2) == a2); 95 assert(either([0, 1], [1, 2]) == [0, 1]); 96 assert(either([0, 1], [1]) == [0, 1]); 97 assert(either(`a`, `b`) == `a`); 98 99 int x = 1, y = 2; 100 either(x, y) = 3; 101 assert(x == 3); 102 assert(y == 2); 103 } 104 105 /** Returns: Last Argument if all arguments implicitly bool-convert to true 106 otherwise CommonType!T.init. 107 108 Similar to behaviour and operator in dynamic languages such as of Lisp's 109 (and a...) and Python's a and .... 110 111 TODO Is inout Conversion!T the correct return value? 112 */ 113 CommonType!T every(T...)(lazy T a) 114 if (T.length >= 1) 115 { 116 auto a0 = a[0](); // evaluate only once 117 static if (T.length == 1) 118 { 119 return a0; 120 } 121 else 122 { 123 return a0 ? every(a[1 .. $]) : CommonType!T.init; // recurse 124 } 125 } 126 127 @safe pure /*TODO nothrow*/ unittest 128 { 129 assert(every(3) == 3); 130 assert(every(3, 4) == 4); 131 assert(every(0, 4) == 0); 132 assert(every(0, 0) == 0); 133 assert(every([0, 1], [1, 2]) == [1, 2]); 134 assert(every([0, 1], [1]) == [1]); 135 assert(every(`a`, `b`) == `b`); 136 assert(every(``, `b`) == `b`); 137 assert(every(cast(string)null, `b`) == cast(string)null); 138 } 139 140 version(none) // WARNING disabled because I don't see any use of this for. 141 { 142 /** This overload enables, when possible, lvalue return. 143 */ 144 auto ref every(T...)(ref T a) 145 if (T.length >= 1 && allSameType!T) 146 { 147 static if (T.length == 1) 148 { 149 return a[0]; 150 } 151 else 152 { 153 return a[0] ? every(a[1 .. $]) : a[0]; // recurse 154 } 155 } 156 157 unittest 158 { 159 immutable p = 1, q = 2; 160 assert(every(p, q) == 2); 161 162 int x = 1, y = 2; 163 every(x, y) = 3; 164 assert(x == 1); 165 assert(y == 3); 166 } 167 } 168 169 /** Evaluate all $(D parts) possibly digesting $(D whole). 170 If all values of $(D parts) implicitly convert to bool true return the 171 values as an array, otherwise restore whole and return null. 172 */ 173 CommonType!T[] tryEvery(S, T...)(ref S whole, 174 lazy T parts) 175 if (T.length >= 1) 176 { 177 auto wholeBackup = whole; 178 bool all = true; 179 alias R = typeof(return); 180 R results; 181 foreach (result; parts) // evaluate each part 182 { 183 if (result) 184 { 185 results ~= result; 186 } 187 else 188 { 189 all = false; 190 break; 191 } 192 } 193 if (all) 194 { 195 return results; // ok that whole has been changed in caller scope 196 } 197 else 198 { 199 whole = wholeBackup; // restore whole in caller scope if any failed 200 return R.init; 201 } 202 } 203 204 unittest 205 { 206 auto whole = `xyz`; 207 import std.algorithm: skipOver; 208 209 assert(whole.tryEvery(whole.skipOver('x'), 210 whole.skipOver('z')) == []); // failing match 211 assert(whole == `xyz`); // should restore whole 212 213 assert(whole.tryEvery(whole.skipOver('x'), 214 whole.skipOver('y'), 215 whole.skipOver('w')) == []); // failing match 216 assert(whole == `xyz`); // should restore whole 217 218 assert(whole.tryEvery(whole.skipOver('x'), 219 whole.skipOver('y')) == [true, true]); // successful match 220 assert(whole == `z`); // should digest matching part 221 } 222 223 import std.traits : isInstanceOf; 224 225 /** Returns: Minimum Element in $(D range). 226 See also: http://forum.dlang.org/thread/jufggxqwzhlsmhshtnfj@forum.dlang.org?page=3 227 */ 228 auto minElement(alias F = min, R)(in R range) 229 if (isInputRange!R) 230 { 231 import std.algorithm : reduce; 232 import std.range : SortedRange; 233 enum M = ElementType!R.max; 234 235 // TODO Expose predFun in SortedRange so we can check with static if (R.predFun == binaryFun!`a < b`) 236 // TODO or add members minElement and maxElement to SortedRange that wraps this logic. 237 238 // static if (isInstanceOf!(SortedRange, R)) 239 // { 240 // return r.empty ? M : r.front; 241 // } 242 // else static if (isInstanceOf!(SortedRange, R)) 243 // { 244 // return r.empty ? M : r.back; // TODO if R has member back (R.Range isBidirectionalRange) 245 // } 246 // else 247 // { 248 // return reduce!F(M, range); 249 // } 250 return reduce!F(M, range); 251 } 252 alias smallest = minElement; 253 unittest { assert([4, 1, 2, 3].minElement == 1); } 254 255 /** Returns: Maximum Element in X. 256 See also: http://forum.dlang.org/thread/jufggxqwzhlsmhshtnfj@forum.dlang.org?page=3 257 */ 258 auto maxElement(alias F = max, R)(in R range) 259 if (isInputRange!R) 260 { 261 import std.algorithm : reduce; 262 return reduce!F(ElementType!R.min, range); 263 } 264 alias largest = maxElement; 265 unittest { assert([4, 1, 2, 3].maxElement == 4); } 266 267 /** Returns: Minmum and Maximum Element in X. */ 268 auto minmaxElement(alias F = min, alias G = max, R)(in R range) 269 if (isInputRange!R) 270 { 271 import std.typecons : tuple; 272 import std.algorithm : reduce; 273 return reduce!(F, G)(tuple(Unqual!(ElementType!R).max, 274 Unqual!(ElementType!R).min), range); 275 } 276 unittest { assert([1, 2, 3].minmaxElement == tuple(1, 3)); } 277 278 import std.typecons : Nullable; 279 280 /** Returns: true iff $(D a) has a value containing meaningful information. 281 */ 282 bool hasContents(T)(in T a) // @safe @nogc pure nothrow 283 { 284 static if (isInstanceOf!(Nullable, T)) 285 return !a.isNull; 286 else static if (isArray!T || isSomeString!T) 287 return cast(bool)a.length; // see: http://stackoverflow.com/questions/18563414/empty-string-should-implicit-convert-to-bool-true/18566334?noredirect=1#18566334 288 else 289 return cast(bool)a; 290 } 291 292 /** Reset $(D a) to its default value. 293 See also: std.typecons.Nullable.nullify 294 */ 295 auto ref reset(T)(ref T a) @trusted // pure nothrow 296 { 297 static if (isInstanceOf!(Nullable, T)) 298 a.nullify(); 299 else 300 return a = T.init; 301 } 302 unittest { 303 int x = 42; 304 x.reset; 305 assert(x == x.init); 306 } 307 308 unittest 309 { 310 import std.typecons : Nullable; 311 auto n = Nullable!(size_t, 312 size_t.max)(); 313 import predicates : isUntouched; 314 assert(n.isUntouched); 315 n = 0; 316 assert(!n.isUntouched); 317 assert(n == 0); 318 n.reset; 319 assert(n.isUntouched); 320 } 321 322 import std.typecons : Tuple, tuple; 323 324 /** Find $(D needles) In Order in $(D haystack). */ 325 auto findInOrder(alias pred = `a == b`, 326 alias finder = find, 327 R, 328 E...)(R haystack, 329 E needles) /* @trusted pure nothrow */ 330 { 331 import std.range : empty; 332 auto hit = haystack; // reference 333 foreach (needle; needles) // for every needle in order 334 { 335 hit = finder!pred(hit, needle); 336 if (hit.empty) 337 { 338 break; 339 } 340 } 341 return hit; 342 } 343 unittest 344 { 345 import std.range : empty; 346 assert(`a b c`.findInOrder(`a`, `b`, `c`)); 347 assert(`b a`.findInOrder(`a`, `b`).empty); 348 } 349 350 /** Returns: Slice Overlap of $(D a) and $(D b) in order given by arguments. 351 */ 352 inout(T[]) overlapInOrder(T)(inout(T[]) a, 353 inout(T[]) b) /* @trusted pure nothrow */ 354 { 355 if (a.ptr <= b.ptr && // if a-start lies at or before b-start 356 b.ptr < a.ptr + a.length) // if b-start lies before b-end 357 { 358 import std.algorithm: min, max; 359 const low = max(a.ptr, b.ptr) - a.ptr; 360 const n = min(b.length, 361 (b.ptr - a.ptr + 1)); // overlap length 362 return a[low..low + n]; 363 } 364 else 365 { 366 return []; 367 } 368 } 369 370 /** Returns: Slice Overlap of $(D a) and $(D b) in any order. 371 Deprecated by: std.array.overlap 372 */ 373 inout(T[]) overlap(T)(inout(T[]) a, 374 inout(T[]) b) /* @safe pure nothrow */ 375 { 376 if (inout(T[]) ab = overlapInOrder(a, b)) 377 { 378 return ab; 379 } 380 else if (inout(T[]) ba = overlapInOrder(b, a)) 381 { 382 return ba; 383 } 384 else 385 { 386 return []; 387 } 388 } 389 390 unittest 391 { 392 auto x = [-11_111, 11, 22, 333_333]; 393 const y = [-22_222, 441, 555, 66]; 394 395 assert(!overlap(x, y)); 396 assert(!overlap(y, x)); 397 398 auto x01 = x[0..1]; 399 auto x12 = x[1..2]; 400 auto x23 = x[2..3]; 401 402 // sub-ranges should overlap completely 403 assert(overlap(x, x12) == x12); 404 assert(overlap(x, x01) == x01); 405 assert(overlap(x, x23) == x23); 406 // and commutate f(a,b) == f(b,a) 407 assert(overlap(x01, x) == x01); 408 assert(overlap(x12, x) == x12); 409 assert(overlap(x23, x) == x23); 410 } 411 412 /** Helper for overlap(). 413 Copied from std.array with simplified return expression. 414 */ 415 bool overlaps(T)(const(T)[] r1, const(T)[] r2) @trusted // pure nothrow 416 { 417 alias U = inout(T); 418 static U* max(U* a, U* b) nothrow { return a > b ? a : b; } 419 static U* min(U* a, U* b) nothrow { return a < b ? a : b; } 420 421 auto b = max(r1.ptr, r2.ptr); 422 auto e = min(r1.ptr + r1.length, 423 r2.ptr + r2.length); 424 return b < e; 425 } 426 427 /** Returns: If range is a palindrome larger than $(D minLength). 428 See also: http://forum.dlang.org/thread/dlfeiszyweafpjiocplf@forum.dlang.org#post-vpzuaqxvtdpzpeuorxdl:40forum.dlang.org 429 See also: https://stackoverflow.com/questions/21849580/equality-operator-in-favour-of-std-range-equal 430 TODO: Test graphemes in `string` and `wstring`. 431 */ 432 bool isSymmetric(R)(R range, size_t minLength = 0) // TODO good value for minLength? 433 if (isBidirectionalRange!(R)) 434 { 435 static if (isRandomAccessRange!R) // arrays excluding `char[]` and `wchar[]` 436 { 437 if (range.length < minLength) { return false; } 438 } 439 size_t i = 0; 440 import std.range : empty; 441 while (!range.empty) 442 { 443 import std.range.primitives: front, back, popFront, popBack; 444 if (range.front != range.back) return false; 445 range.popFront(); i++; 446 if (range.empty) break; 447 range.popBack(); i++; 448 } 449 return i >= minLength; 450 } 451 452 unittest 453 { 454 assert(`dallassallad`.isSymmetric); 455 assert(!`ab`.isSymmetric); 456 assert(`a`.isSymmetric); 457 assert(`åäå`.isSymmetric); 458 assert(`áá`.isSymmetric); 459 assert(`åäå`.isSymmetric(3)); 460 assert(!`åäå`.isSymmetric(4)); 461 assert(``.isSymmetric); 462 assert([1, 2, 2, 1].isSymmetric); 463 assert(![1, 2, 2, 1].isSymmetric(5)); 464 } 465 alias isPalindrome = isSymmetric; 466 467 import traits_ex : areEquable; 468 469 /** Return true if $(D s1) is an Anagram of $(D s2). 470 Equal arguments are not considered to be an anagrams of each other. 471 472 TODO Is there a faster way of calculating anagrams? 473 TODO Allow const input 474 TODO Move to std.algorithm.sorting. 475 TODO Should requirement isInputRange be relaxed? 476 477 Note that implementations in http://rosettacode.org/wiki/Anagrams#D doesn't 478 correctly handle multi-byte encoded characters in string and wstring. 479 */ 480 auto isAnagramOf(R1, R2)(R1 r1, R2 r2) // TODO nothrow 481 if (isInputRange!R1 && 482 isInputRange!R2 && 483 areEquable!(ElementType!R1, 484 ElementType!R2)) 485 { 486 const sortLimit = 0; 487 import std.range : empty; 488 if (r1.empty || r2.empty) { return false; } 489 if (r1.length + r2.length < sortLimit) 490 { 491 import std.algorithm.comparison : equal; 492 import sort_ex : sorted; 493 return equal(r1.sorted, 494 r2.sorted); 495 } 496 else 497 { 498 alias E1 = ElementType!R1; 499 alias E2 = ElementType!R2; 500 alias C = CommonType!(E1, E2); 501 502 alias T = Tuple!(size_t, // R1 histogram bin count 503 size_t); // R2 histogram bin count 504 505 import std.traits : isNarrowString; 506 import std.utf : byUTF; 507 508 // TODO functionize 509 static if (isNarrowString!R1) 510 auto s1 = r1.byUTF!dchar; 511 else 512 auto s1 = r1; 513 514 static if (isNarrowString!R2) 515 auto s2 = r2.byUTF!dchar; 516 else 517 auto s2 = r2; 518 519 // histogram 520 T[C] hist; // TODO use non-GC-allocating AA 521 522 // create histograms 523 foreach (const ref e1; s1) 524 { 525 // TODO functionize to hist.initOrUpdate(e1, T(0,1), (ref AA aa){ aa[0] += 1; }) 526 if (auto hit = e1 in hist) 527 (*hit)[0] += 1; 528 else 529 hist[e1] = T(1, 0); 530 } 531 foreach (const ref e2; s2) 532 { 533 // TODO functionize to hist.initOrUpdate(e2, T(0,1), (ref AA aa){ aa[1] += 1; }) 534 if (auto hit = e2 in hist) 535 (*hit)[1] += 1; 536 else 537 hist[e2] = T(0, 1); 538 } 539 540 // check histograms 541 foreach (const ref e; hist) // TODO nothrow 542 { 543 if (e[0] != e[1]) 544 { 545 return false; 546 } 547 } 548 return true; 549 } 550 } 551 alias isPermutationOf = isAnagramOf; // TODO Only define isAnagramOf for strings? 552 553 /// 554 @safe pure unittest // TODO nothrow 555 { 556 assert([1, 2, 3, 4, 5].isPermutationOf([1, 2, 4, 5, 3])); 557 assert(![1, 2, 3, 4, 5].isPermutationOf([1, 4, 5, 3])); 558 } 559 560 /// 561 @safe pure unittest 562 { 563 assert(!``w.isAnagramOf(``)); 564 assert(`äöå`w.isAnagramOf(`åäö`)); 565 assert(`äöå`.isAnagramOf(`åäö`w)); 566 assert(`äöå`w.isAnagramOf(`åäö`w)); 567 568 assert(`äöå`d.isAnagramOf(`åäö`)); 569 assert(`äöå`.isAnagramOf(`åäö`d)); 570 assert(`äöå`d.isAnagramOf(`åäö`d)); 571 } 572 573 /// 574 @safe pure unittest 575 { 576 assert(`äöå`.isAnagramOf(`åäö`)); 577 assert(!`äöå`.isAnagramOf(`xyz`)); 578 assert(!`äöå`.isAnagramOf(``)); 579 assert(!``.isAnagramOf(`åäö`)); 580 } 581 @safe pure unittest 582 { 583 assert(`xyzxyzxyzxyzxyzxyzxyzxyz`.isAnagramOf(`xyzxyzxyzxyzxyzxyzxyzxyz`)); 584 assert(`xyzxyzxyzxyzxyzxyzxyzxyz`.isAnagramOf(`xxyyzzxxyyzzxxyyzzxxyyzz`)); 585 } 586 587 /// 588 @safe pure unittest 589 { 590 import std.conv: to; 591 592 auto x = `äöå`.to!(dchar[]); 593 594 auto y = sort(x); 595 alias Y = typeof(y); 596 597 const z = `åäö`; 598 599 assert(y.isAnagramOf(z)); 600 assert(z.isAnagramOf(y)); 601 } 602 603 /* ref Unqual!T unqual(T)(in T x) pure nothrow if isStuct!T { return cast(Unqual!T)x; } */ 604 /* unittest { */ 605 /* const int x; */ 606 /* unqual(x) = 1; */ 607 /* } */ 608 609 enum Reduction 610 { 611 forwardDifference, 612 backwardDifference, 613 sum, 614 } 615 616 /** Generalized Windowed Reduce. 617 See also: https://stackoverflow.com/questions/21004944/forward-difference-algorithm 618 See also: http://forum.dlang.org/thread/ujouqtqeehkegmtaxebg@forum.dlang.org#post-lczzsypupcfigttghkwx:40forum.dlang.org 619 See also: http://rosettacode.org/wiki/Forward_difference#D 620 */ 621 auto ref windowedReduce(Reduction reduction = Reduction.forwardDifference, R)(R range) 622 if (isInputRange!R) 623 { 624 import std.algorithm.iteration: map; 625 import std.range: zip, dropOne; 626 auto ref op(T)(T a, T b) @safe pure nothrow 627 { 628 static if (reduction == Reduction.forwardDifference) return b - a; // TODO final static switch 629 else static if (reduction == Reduction.backwardDifference) return a - b; 630 else static if (reduction == Reduction.sum) return a + b; 631 } 632 return range.zip(range.dropOne).map!(a => op(a[0], a[1])); // a is a tuple here 633 } 634 // NOTE: Disabled for now because this solution cannot be made nothrow 635 /* auto ref windowedReduce(Reduction reduction = Reduction.forwardDifference, uint N = 0, R)(R range) */ 636 /* @safe pure /\* nothrow *\/ if (isInputRange!R) */ 637 /* { */ 638 /* auto ref helper(R range) @safe pure /\* nothrow *\/ { */ 639 /* import std.algorithm.iteration: map; */ 640 /* import std.range: zip, dropOne; */ 641 /* // Note: that a[0] and a[1] indexes Zip tuple */ 642 /* static if (reduction == Reduction.forwardDifference) */ 643 /* return range.zip(range.dropOne).map!(a => a[1] - a[0]); */ 644 /* static if (reduction == Reduction.backwardDifference) */ 645 /* return range.zip(range.dropOne).map!(a => a[0] - a[1]); */ 646 /* static if (reduction == Reduction.sum) */ 647 /* return range.zip(range.dropOne).map!(a => a[0] + a[1]); */ 648 /* } */ 649 /* static if (N != 0) { */ 650 /* return windowedReduce!(reduction, N - 1)(helper(range)); */ 651 /* } else { */ 652 /* return helper(range); */ 653 /* } */ 654 /* } */ 655 656 /* unittest { */ 657 /* import std.range: front; */ 658 /* dln([1].windowedReduce!(Reduction.forwardDifference)); */ 659 /* dln([1, 22].windowedReduce!(Reduction.forwardDifference)); */ 660 /* dln([1, 22, 333].windowedReduce!(Reduction.forwardDifference)); */ 661 /* } */ 662 663 unittest 664 { 665 import std.datetime: Clock, SysTime, Duration; 666 import std.algorithm.iteration: map; 667 SysTime[] times; 668 const n = 4; 669 foreach (i; 0..n) 670 times ~= Clock.currTime; 671 version(print) dln(times); 672 auto spans = times.windowedReduce!(Reduction.forwardDifference); 673 version(print) dln(spans); 674 // dln(*(cast(ulong*)&(spans.front))); 675 version(print) dln(Duration.sizeof); 676 } 677 678 @safe pure unittest 679 { 680 immutable i = [1, 4, 9, 17]; 681 import std.algorithm: equal; 682 assert(i.windowedReduce!(Reduction.forwardDifference).equal ([+3, +5, +8])); 683 assert(i.windowedReduce!(Reduction.backwardDifference).equal([-3, -5, -8])); 684 assert(i.windowedReduce!(Reduction.sum).equal ([+5, +13, +26])); 685 assert([1].windowedReduce.empty); 686 version(print) dln(i.windowedReduce!(Reduction.sum)); 687 } 688 689 /* TODO Assert that ElementType!R only value semantics. */ 690 auto ref packBitParallelRunLengths(R)(in R x) 691 if (isInputRange!R) 692 { 693 import std.bitmanip: BitArray; 694 import core.bitop: bt; 695 alias E = ElementType!R; // element type 696 enum nBits = 8*E.sizeof; 697 698 BitArray[nBits] runs; 699 700 // allocate runs 701 foreach (ref run; runs) 702 { 703 run.length = x.length; 704 } 705 706 /* string toString() @property @trusted const { */ 707 /* typeof(return) y; */ 708 /* import std.conv: to; */ 709 /* foreach (run; runs) { */ 710 /* y ~= run.to!string ~ "\n"; */ 711 /* } */ 712 /* return y; */ 713 /* } */ 714 715 /* size_t[nBits] counts; */ 716 717 import bitset: BitSet; 718 foreach (eltIx, elt; x) 719 { 720 /* BitSet!nBits bits; */ 721 foreach (bitIndex; 0..nBits) 722 { 723 import bitop_ex: getBit; 724 runs[bitIndex][eltIx] = elt.getBit(bitIndex); 725 } 726 } 727 return runs; 728 } 729 alias packBPRL = packBitParallelRunLengths; 730 731 pure unittest 732 { 733 /* import backtrace.backtrace; */ 734 /* import std.stdio: stderr; */ 735 /* backtrace.backtrace.install(stderr); */ 736 const x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 737 const xPacked = x.packBitParallelRunLengths; 738 version(print) dln(xPacked); 739 } 740 741 /** Compute Forward Difference of $(D range). 742 743 TODO Is there a difference between whether R r is immutable, const or 744 mutable? 745 746 TODO If r contains only one element return empty range. 747 748 See also: https://stackoverflow.com/questions/21004944/forward-difference-algorithm 749 See also: http://forum.dlang.org/thread/ujouqtqeehkegmtaxebg@forum.dlang.org#post-lczzsypupcfigttghkwx:40forum.dlang.org 750 See also: http://rosettacode.org/wiki/Forward_difference#D 751 */ 752 auto forwardDifference(R)(R r) 753 if (isInputRange!R) 754 { 755 import std.range: front, empty, popFront, dropOne; 756 757 struct ForwardDifference 758 { 759 R _range; 760 alias E = ElementType!R; // Input ElementType 761 alias D = typeof(_range.front - _range.front); // Element Difference Type. TODO Use this as ElementType of range 762 D _front; 763 bool _initialized = false; 764 765 this(R range) in { assert(!range.empty); } 766 body 767 { 768 auto tmp = range; 769 if (tmp.dropOne.empty) // TODO This may be an unneccesary cost but is practical to remove extra logic 770 _range = R.init; // return empty range 771 else 772 _range = range; // store range internally (by reference) 773 } 774 775 @property: 776 auto ref front() 777 { 778 if (!_initialized) { popFront(); } 779 return _front; 780 } 781 auto ref moveFront() 782 { 783 popFront(); 784 return _front; 785 } 786 void popFront() 787 { 788 if (empty is false) 789 { 790 _initialized = true; 791 E rf = _range.front; 792 _range.popFront(); 793 if (_range.empty is false) 794 { 795 _front = _range.front - rf; 796 } 797 } 798 } 799 bool empty() 800 { 801 return _range.empty; 802 } 803 } 804 805 return ForwardDifference(r); 806 } 807 808 unittest 809 { 810 import msgpack; 811 import std.array: array; 812 813 auto x = [long.max, 0, 1]; 814 auto y = x.forwardDifference; 815 816 version(print) dln(y); 817 version(print) dln(y.pack); 818 version(print) dln(y.array.pack); 819 } 820 821 import std.traits: isCallable, ReturnType, arity, ParameterTypeTuple; 822 import traits_ex: arityMin0; 823 824 /** Create Range of Elements Generated by $(D fun). 825 826 Use for example to generate random instances of return value of fun. 827 828 TODO I believe we need arityMin, arityMax trait here 829 */ 830 auto apply(alias fun, N)(N n) 831 if (// TODO isCallable!fun && 832 arityMin0!fun && 833 !is(ReturnType!fun == void) && 834 isIntegral!N) 835 { 836 import std.range: iota; 837 import std.algorithm.iteration: map; 838 return n.iota.map!(n => fun); 839 } 840 841 unittest 842 { 843 import std.datetime: Clock, SysTime, Duration; 844 import std.algorithm.iteration: map; 845 import std.array: array; 846 const n = 3; 847 auto times = n.apply!(Clock.currTime).array; 848 version(print) dln(times); 849 auto spans = times.forwardDifference; 850 version(print) dln(spans); 851 } 852 853 /** In Place Ordering (in Sorted Order) of all Elements $(D t). 854 See also: https://stackoverflow.com/questions/21102646/in-place-ordering-of-elements/ 855 See also: http://forum.dlang.org/thread/eweortsmcmibppmvtriw@forum.dlang.org#post-eweortsmcmibppmvtriw:40forum.dlang.org 856 */ 857 void orderInPlace(T...)(ref T t) @trusted 858 { 859 import std.algorithm: sort, swap; 860 static if (t.length == 2) 861 { 862 if (t[0] > t[1]) 863 { 864 swap(t[0], t[1]); 865 } 866 } 867 else 868 { // generic version 869 T[0][T.length] buffer; // static buffer to capture elements 870 foreach (idx, a; t) 871 buffer[idx] = a; 872 auto sorted = sort(buffer[]); 873 foreach (idx, a; t) 874 t[idx] = sorted[idx]; 875 } 876 } 877 unittest { 878 auto x = 2, y = 1; 879 orderInPlace(x, y); 880 assert(x == 1); 881 assert(y == 2); 882 } 883 unittest { 884 auto x = 3, y = 1, z = 2; 885 orderInPlace(x, y, z); 886 assert(x == 1); 887 assert(y == 2); 888 assert(z == 3); 889 } 890 891 import std.algorithm: SwapStrategy; 892 893 /** Allow Static Arrays to be sorted without []. 894 See also: http://forum.dlang.org/thread/jhzurojjnlkatjdgcfhg@forum.dlang.org 895 */ 896 template sort(alias less = `a < b`, SwapStrategy ss = SwapStrategy.unstable) 897 { 898 import std.algorithm: stdSort = sort; 899 auto sort(Arr)(ref Arr arr) 900 if (isStaticArray!Arr) 901 { 902 return stdSort!(less, ss)(arr[]); 903 } 904 auto sort(Range)(Range r) 905 if (!isStaticArray!Range) 906 { 907 return stdSort!(less, ss)(r); 908 } 909 } 910 unittest 911 { 912 int[5] a = [ 9, 5, 1, 7, 3 ]; 913 int[] b = [ 4, 2, 1, 6, 3 ]; 914 sort(a); 915 sort(b); 916 } 917 918 /** Stable Variant of Quick Sort. 919 See also: http://forum.dlang.org/thread/gjuvmrypvxeebvztszpr@forum.dlang.org 920 */ 921 auto ref stableSort(T)(auto ref T a) pure 922 if (isRandomAccessRange!T) 923 { 924 if (a.length >= 2) 925 { 926 import std.algorithm: partition3, sort; 927 auto parts = partition3(a, a[$ / 2]); // mid element as pivot 928 parts[0].sort(); 929 parts[2].sort(); 930 } 931 return a; 932 } 933 934 unittest 935 { 936 import random_ex: randInPlace; 937 const n = 2^^16; 938 auto a = new int[n]; 939 a.randInPlace; 940 auto b = a.dup; 941 a[].stableSort; 942 import std.algorithm: sort; 943 sort(b); 944 assert(a == b); 945 } 946 947 /** Execute Expression $(D expr) the same way $(D n) times. */ 948 void doTimes(uint n, lazy void expr) 949 { 950 while (n--) expr(); 951 } 952 953 /** Execute Expression $(D expr) $(I inline) the same way $(D n) times. 954 $(D n) must be a constant known at compile time. 955 */ 956 void doTimes(uint n)(lazy void expr) 957 { 958 import range_ex: iota; 959 foreach (i; iota!(0, n)) expr(); 960 } 961 962 unittest 963 { 964 int i = 0; 965 doTimes!4(i++); 966 assert(i == 4); 967 } 968 969 alias loop = doTimes; 970 alias doN = doTimes; 971 alias repeat = doTimes; 972 973 /** Execute Expression $(D action) the same way $(D n) times. */ 974 void times(alias action, N)(N n) 975 if (isCallable!action && 976 isIntegral!N && 977 arity!action <= 1) 978 { 979 static if (arity!action == 1 && // if one argument and 980 isIntegral!(ParameterTypeTuple!action[0])) // its an integer 981 { 982 foreach (i; 0 .. n) 983 action(i); // use it as action input 984 } 985 else 986 { 987 foreach (i; 0 .. n) 988 action(); 989 } 990 } 991 992 unittest 993 { 994 enum n = 10; 995 int sum = 0; 996 10.times!({ sum++; }); 997 assert(sum == n); 998 } 999 1000 private string genNaryFun(string fun, V...)() @safe pure 1001 { 1002 string code; 1003 import std.string: format; 1004 foreach (n, v; V) 1005 code ~= "alias values[%d] %s;".format(n, cast(char)('a'+n)); 1006 code ~= `return ` ~ fun ~ `;`; 1007 return code; 1008 } 1009 template naryFun(string fun) 1010 { 1011 auto naryFun(V...)(V values) 1012 { 1013 mixin(genNaryFun!(fun, V)); 1014 } 1015 } 1016 unittest { 1017 alias test = naryFun!`a + b + c`; 1018 assert(test(1, 2, 3) == 6); 1019 } 1020 1021 import std.typetuple : allSatisfy; 1022 1023 /** Zip $(D ranges) together with operation $(D fun). 1024 TODO Remove when Issue 8715 is fixed providing zipWith 1025 */ 1026 auto zipWith(alias fun, Ranges...)(Ranges ranges) 1027 if (Ranges.length >= 2 && 1028 allSatisfy!(isInputRange, Ranges)) 1029 { 1030 import std.range: zip; 1031 import std.algorithm.iteration: map; 1032 static if (ranges.length < 2) 1033 static assert(false, `Need at least 2 range arguments.`); 1034 else static if (ranges.length == 2) 1035 return zip(ranges).map!(a => binaryFun!fun(a.expand)); 1036 else 1037 return zip(ranges).map!(a => naryFun!fun(a.expand)); 1038 // return zip(ranges).map!(a => fun(a.expand)); 1039 } 1040 unittest { 1041 auto x = [1, 2, 3]; 1042 import std.array: array; 1043 assert(zipWith!`a+b`(x, x).array == [2, 4, 6]); 1044 assert(zipWith!((a, b) => a + b)(x, x).array == [2, 4, 6]); 1045 assert(zipWith!`a+b+c`(x, x, x).array == [3, 6, 9]); 1046 } 1047 1048 auto zipWith(fun, StoppingPolicy, Ranges...)(StoppingPolicy sp, 1049 Ranges ranges) 1050 if (Ranges.length && 1051 allSatisfy!(isInputRange, Ranges)) 1052 { 1053 import std.range: zip; 1054 import std.algorithm.iteration: map; 1055 return zip(sp, ranges).map!fun; 1056 } 1057 1058 /** Pair. TODO std.typecons */ 1059 alias Pair(T, U) = Tuple!(T, U); 1060 /** Instantiator for $(D Pair). */ 1061 auto pair(T, U)(in T t, in U u) { return Pair!(T, U)(t, u); } 1062 1063 /** Triple. TODO std.typecons */ 1064 alias Triple(T, U, V) = Tuple!(T, U, V); 1065 /** Instantiator for $(D Triple). */ 1066 auto triple(T, U, V)(in T t, in U u, in V v) { return Triple!(T, U, V)(t, u, v); } 1067 1068 /** Quadruple. TODO std.typecons */ 1069 alias Quadruple(T, U, V, W) = Tuple!(T, U, V, W); 1070 /** Instantiator for $(D Quadruple). */ 1071 auto quadruple(T, U, V, W)(in T t, in U u, in V v, in W w) { return Quadruple!(T, U, V, W)(t, u, v, w); } 1072 1073 /** Limit/Span (Min,Max) Pair. 1074 Todo: Decide on either Span, MinMax or Limits 1075 See also: https://stackoverflow.com/questions/21241878/generic-span-type-in-phobos 1076 */ 1077 struct Limits(T) 1078 { 1079 import std.algorithm: min, max; 1080 1081 @property @safe pure: 1082 1083 /** Expand Limits to include $(D a). */ 1084 auto ref include(in T a) nothrow { 1085 _lims[0] = min(_lims[0], a); 1086 _lims[1] = max(_lims[1], a); 1087 return this; 1088 } 1089 alias expand = include; 1090 1091 auto ref reset() nothrow { 1092 _lims[0] = T.max; 1093 _lims[1] = T.min; 1094 } 1095 1096 string toString() const { 1097 import std.conv: to; 1098 return (`[` ~ to!string(_lims[0]) ~ 1099 `...` ~ to!string(_lims[1]) ~ `]`) ; 1100 } 1101 1102 auto _lims = tuple(T.max, T.min); 1103 1104 alias _lims this; 1105 } 1106 1107 auto limits(T)() { return Limits!T(); } 1108 unittest { 1109 /* import std.file: SysTime; */ 1110 /* SysTime st; */ 1111 Limits!int x; 1112 x.expand(-10); 1113 x.expand(10); 1114 assert(x[0] == -10); 1115 assert(x[1] == +10); 1116 version(print) dln(x); 1117 } 1118 1119 /* import rational: Rational; */ 1120 1121 /* template getTypeString(T) { */ 1122 /* static if (is(T == Rational)) */ 1123 /* string getTypeString(T)() @safe pure nothrow { */ 1124 /* return x`211A`; */ 1125 /* } */ 1126 /* } */ 1127 /* unittest { */ 1128 /* import rational: Rational; */ 1129 /* dln(getTypeString!Rational); */ 1130 /* } */ 1131 1132 /** Check if $(D a) and $(D b) are colinear. */ 1133 bool areColinear(T)(T a, T b) 1134 { 1135 // a and b are colinear if a.x / a.y == b.x / b.y 1136 // We can avoid the division by multiplying out. 1137 return a.x * b.y == a.y * b.x; 1138 } 1139 1140 /* /\** TODO Remove when each is standard in Phobos. *\/ */ 1141 /* void each(R)(R range, delegate x) @safe pure /\* nothrow *\/ if (isInputRange!R) { */ 1142 /* foreach (ref elt; range) { */ 1143 /* x(elt); */ 1144 /* } */ 1145 /* } */ 1146 /* unittest { */ 1147 /* version(print) [1, 2, 3, 4].each(a => dln(a)); */ 1148 /* } */ 1149 1150 enum isIntLike(T) = is(typeof({T t = 0; t = t+t;})); // More if needed 1151 1152 /** $(LUCKY Fibonacci) Numbers (Infinite Range). 1153 See also: http://forum.dlang.org/thread/dqlrfoxzsppylcgljyyf@forum.dlang.org#post-mailman.1072.1350619455.5162.digitalmars-d-learn:40puremagic.com 1154 See also: https://www.reddit.com/r/programming/comments/rif9x/uniform_function_call_syntax_for_the_d/ 1155 */ 1156 auto fibonacci(T = int)(T nth = 0) 1157 if (isIntLike!T) 1158 { 1159 struct Fibonacci 1160 { 1161 T a, b; 1162 T front() { return b; } 1163 void popFront() 1164 { 1165 T c = a+b; 1166 a = b; 1167 b = c; 1168 } 1169 bool empty() const { return false; } 1170 } 1171 return nth == 0 ? Fibonacci(0, 1) : Fibonacci(1, 1); 1172 } 1173 1174 unittest 1175 { 1176 import std.range: take; 1177 import std.algorithm: equal; 1178 assert(fibonacci.take(10).equal([1, 1, 2, 3, 5, 8, 13, 21, 34, 55])); 1179 assert(1.fibonacci.take(9).equal([1, 2, 3, 5, 8, 13, 21, 34, 55])); 1180 } 1181 1182 /** Expand Static $(D array) into a parameter arguments (AliasSeq!). 1183 See also: http://forum.dlang.org/thread/hwellpcaomwbpnpofzlx@forum.dlang.org?page=1 1184 */ 1185 template expand(alias array, size_t idx = 0) 1186 if (isStaticArray!(typeof(array))) 1187 { 1188 @property ref delay() { return array[idx]; } 1189 static if (idx + 1 < array.length) 1190 { 1191 import std.meta : AliasSeq; 1192 alias expand = AliasSeq!(delay, expand!(array, idx + 1)); 1193 } 1194 else 1195 { 1196 alias expand = delay; 1197 } 1198 } 1199 1200 unittest 1201 { 1202 static void foo(int a, int b, int c) 1203 { 1204 import std.stdio: writefln; 1205 version(print) writefln("a: %s, b: %s, c: %s", a, b, c); 1206 } 1207 int[3] arr = [1, 2, 3]; 1208 foo(expand!arr); 1209 } 1210 1211 /** Python Style To-String-Conversion Alias. */ 1212 string str(T)(in T a) @safe pure 1213 { 1214 import std.conv: to; 1215 return to!string(a); 1216 } 1217 1218 /** Python Style Length Alias. */ 1219 auto len(T)(in T a) 1220 { 1221 return a.length; 1222 } 1223 1224 unittest 1225 { 1226 import std.algorithm.iteration: map; 1227 import std.array: array; 1228 assert(([42].map!str).array == [`42`]); 1229 } 1230 1231 import std.range: InputRange, OutputRange; 1232 alias Source = InputRange; // nicer alias 1233 alias Sink = OutputRange; // nicer alias 1234 1235 /* belongs to std.range */ 1236 import std.range: cycle, retro; 1237 import std.functional: compose; 1238 alias retroCycle = compose!(cycle, retro); 1239 1240 import std.traits: isAggregateType, hasMember; 1241 1242 /** Generic Member Setter. 1243 See also: http://forum.dlang.org/thread/fdjkijrtduraaajlxxne@forum.dlang.org 1244 */ 1245 auto ref T set(string member, T, U)(auto ref T a, in U value) 1246 if (isAggregateType!T && 1247 hasMember!(T, member)) 1248 { 1249 __traits(getMember, a, member) = value; 1250 return a; 1251 } 1252 1253 unittest 1254 { 1255 class C { int x, y, z, w; } 1256 auto c = new C().set!`x`(11).set!`w`(44); 1257 assert(c.x == 11); 1258 assert(c.w == 44); 1259 } 1260 1261 /* Check if $(D part) is part of $(D whole). 1262 See also: http://forum.dlang.org/thread/ls9dbk$jkq$1@digitalmars.com 1263 TODO Standardize name and remove alises. 1264 TODO Use partOf if generalized to InputRange. 1265 */ 1266 bool sliceOf(T)(in T[] part, 1267 in T[] whole) 1268 { 1269 return (whole.ptr <= part.ptr && 1270 part.ptr + part.length <= 1271 whole.ptr + whole.length); 1272 } 1273 alias containedIn = sliceOf; 1274 alias partOf = sliceOf; 1275 alias coveredBy = sliceOf; 1276 alias includedIn = sliceOf; 1277 1278 /* See also: http://forum.dlang.org/thread/cjpplpzdzebfxhyqtskw@forum.dlang.org#post-cjpplpzdzebfxhyqtskw:40forum.dlang.org */ 1279 auto dropWhile(alias pred = `a == b`, R, E)(R range, E element) 1280 if (isInputRange!R && 1281 is (typeof(binaryFun!pred(range.front, element)) : bool)) 1282 { 1283 alias predFun = binaryFun!pred; 1284 return range.find!(a => !predFun(a, element)); 1285 } 1286 alias dropAllOf = dropWhile; 1287 alias stripFront = dropWhile; 1288 alias lstrip = dropWhile; // Python style 1289 1290 import std.algorithm.searching: until; 1291 alias takeUntil = until; 1292 1293 alias dropUntil = find; 1294 1295 unittest 1296 { 1297 assert([1, 2, 3].dropWhile(1) == [2, 3]); 1298 assert([1, 1, 1, 2, 3].dropWhile(1) == [2, 3]); 1299 assert([1, 2, 3].dropWhile(2) == [1, 2, 3]); 1300 assert(`aabc`.dropWhile('a') == `bc`); // TODO Remove restriction on cast to dchar 1301 } 1302 1303 /* See also: http://forum.dlang.org/thread/cjpplpzdzebfxhyqtskw@forum.dlang.org#post-cjpplpzdzebfxhyqtskw:40forum.dlang.org */ 1304 auto takeWhile(alias pred = `a == b`, R, E)(R range, E element) 1305 if (isInputRange!R && 1306 is (typeof(binaryFun!pred(range.front, element)) : bool)) 1307 { 1308 import std.algorithm: until; 1309 alias predFun = binaryFun!pred; 1310 return range.until!(a => !predFun(a, element)); 1311 } 1312 alias takeAllOf = takeWhile; 1313 1314 unittest 1315 { 1316 import std.algorithm: equal; 1317 assert(equal([1, 1, 2, 3].takeWhile(1), 1318 [1, 1])); 1319 } 1320 1321 /** Simpler variant of Phobos' $(D split). */ 1322 auto split(alias pred, R)(R haystack) 1323 if (isForwardRange!R) 1324 { 1325 import std.range : empty; 1326 static if (isSomeString!R || 1327 isRandomAccessRange!R) 1328 { 1329 auto balance = find!pred(haystack); 1330 immutable pos1 = haystack.length - balance.length; 1331 immutable pos2 = balance.empty ? pos1 : pos1 + 1; 1332 return tuple(haystack[0 .. pos1], 1333 haystack[pos1 .. pos2], 1334 haystack[pos2 .. haystack.length]); 1335 } 1336 else 1337 { 1338 auto original = haystack.save; 1339 auto h = haystack.save; 1340 size_t pos1, pos2; 1341 while (!h.empty) 1342 { 1343 if (unaryFun!pred(h.front)) 1344 { 1345 h.popFront(); 1346 ++pos2; 1347 } 1348 else 1349 { 1350 haystack.popFront(); 1351 h = haystack.save; 1352 pos2 = ++pos1; 1353 } 1354 } 1355 return tuple(takeExactly(original, pos1), 1356 takeExactly(haystack, pos2 - pos1), 1357 h); 1358 } 1359 } 1360 1361 unittest 1362 { 1363 import std.algorithm: equal; 1364 import std.ascii: isDigit; 1365 assert(`aa1bb`.split!(a => a.isDigit) == tuple(`aa`, `1`, `bb`)); 1366 assert(`aa1`.split!(a => a.isDigit) == tuple(`aa`, `1`, ``)); 1367 assert(`1bb`.split!(a => a.isDigit) == tuple(``, `1`, `bb`)); 1368 } 1369 1370 /** Simpler variant of Phobos' $(D splitBefore). */ 1371 auto splitBefore(alias pred, R)(R haystack) 1372 if (isForwardRange!R) 1373 { 1374 static if (isSomeString!R || 1375 sRandomAccessRange!R) 1376 { 1377 auto balance = find!pred(haystack); 1378 immutable pos = haystack.length - balance.length; 1379 return tuple(haystack[0 .. pos], 1380 haystack[pos .. haystack.length]); 1381 } 1382 else 1383 { 1384 auto original = haystack.save; 1385 auto h = haystack.save; 1386 size_t pos; 1387 import std.range : empty; 1388 while (!h.empty) 1389 { 1390 if (unaryFun!pred(h.front)) 1391 { 1392 h.popFront(); 1393 } 1394 else 1395 { 1396 haystack.popFront(); 1397 h = haystack.save; 1398 ++pos; 1399 } 1400 } 1401 return tuple(takeExactly(original, pos), 1402 haystack); 1403 } 1404 } 1405 1406 unittest 1407 { 1408 import std.algorithm: equal; 1409 import std.ascii: isDigit; 1410 assert(`11ab`.splitBefore!(a => !a.isDigit) == tuple(`11`, `ab`)); 1411 assert(`ab`.splitBefore!(a => !a.isDigit) == tuple(``, `ab`)); 1412 } 1413 1414 auto splitAfter(alias pred, R)(R haystack) 1415 if (isForwardRange!R) 1416 { 1417 static if (isSomeString!R || isRandomAccessRange!R) 1418 { 1419 import std.range : empty; 1420 auto balance = find!pred(haystack); 1421 immutable pos = balance.empty ? 0 : haystack.length - balance.length + 1; 1422 return tuple(haystack[0 .. pos], haystack[pos .. haystack.length]); 1423 } 1424 else 1425 { 1426 static assert(false, `How to implement this?`); 1427 // import std.range : empty; 1428 /* auto original = haystack.save; */ 1429 /* auto h = haystack.save; */ 1430 /* size_t pos1, pos2; */ 1431 /* while (!n.empty) */ 1432 /* { */ 1433 /* if (h.empty) */ 1434 /* { */ 1435 /* // Failed search */ 1436 /* return tuple(takeExactly(original, 0), original); */ 1437 /* } */ 1438 /* if (binaryFun!pred(h.front, n.front)) */ 1439 /* { */ 1440 /* h.popFront(); */ 1441 /* n.popFront(); */ 1442 /* ++pos2; */ 1443 /* } */ 1444 /* else */ 1445 /* { */ 1446 /* haystack.popFront(); */ 1447 /* n = needle.save; */ 1448 /* h = haystack.save; */ 1449 /* pos2 = ++pos1; */ 1450 /* } */ 1451 /* } */ 1452 /* return tuple(takeExactly(original, pos2), h); */ 1453 } 1454 } 1455 1456 unittest 1457 { 1458 import std.algorithm: equal; 1459 import std.ascii: isDigit; 1460 assert(`aa1bb`.splitAfter!(a => a.isDigit) == tuple(`aa1`, `bb`)); 1461 assert(`aa1`.splitAfter!(a => a.isDigit) == tuple(`aa1`, ``)); 1462 } 1463 1464 auto moveUntil(alias pred, R)(ref R r) 1465 if (isInputRange!R) 1466 { 1467 auto split = r.splitBefore!pred; 1468 r = split[1]; 1469 return split[0]; 1470 } 1471 1472 unittest 1473 { 1474 auto r = `xxx111`; 1475 auto id = r.moveUntil!(a => a == '1'); 1476 assert(id == `xxx`); 1477 assert(r == `111`); 1478 } 1479 1480 auto moveWhile(alias pred, R)(ref R r) 1481 if (isInputRange!R) 1482 { 1483 return r.moveUntil!(a => !pred(a)); 1484 } 1485 1486 unittest 1487 { 1488 auto r = `xxx111`; 1489 auto id = r.moveWhile!(a => a == 'x'); 1490 assert(id == `xxx`); 1491 assert(r == `111`); 1492 } 1493 1494 /** Variant of $(D findSplitBefore) that destructively pops everything up to, 1495 not including, $(D needle) from $(D haystack). 1496 */ 1497 auto findPopBefore(alias pred = `a == b`, R1, R2)(ref R1 haystack, 1498 R2 needle) 1499 if (isForwardRange!R1 && 1500 isForwardRange!R2) 1501 { 1502 import std.range : empty; 1503 if (haystack.empty || needle.empty) 1504 { 1505 return R1.init; // TODO correct? 1506 } 1507 import std.algorithm.searching : findSplitBefore; 1508 if (auto split = findSplitBefore!pred(haystack, needle)) // TODO If which case are empty and what return value should they lead to? 1509 { 1510 haystack = split[1]; 1511 return split[0]; 1512 } 1513 else 1514 { 1515 return R1.init; // TODO correct? 1516 } 1517 } 1518 1519 unittest 1520 { 1521 auto haystack = `xy`; 1522 auto needle = `z`; 1523 auto pop = haystack.findPopBefore(needle); 1524 assert(pop == `xy`); 1525 } 1526 1527 unittest 1528 { 1529 auto haystack = `xyz`; 1530 auto needle = `y`; 1531 auto pop = haystack.findPopBefore(needle); 1532 assert(pop == `x`); 1533 assert(haystack == `yz`); 1534 } 1535 1536 /** Variant of $(D findSplitAfter) that destructively pops everything up to, 1537 including, $(D needle) from $(D haystack). 1538 */ 1539 auto findPopAfter(alias pred = `a == b`, R1, R2)(ref R1 haystack, 1540 R2 needle) 1541 if (isForwardRange!R1 && 1542 isForwardRange!R2) 1543 { 1544 import std.range : empty; 1545 if (haystack.empty || needle.empty) 1546 { 1547 return R1.init; // TODO correct? 1548 } 1549 import std.algorithm.searching : findSplitAfter; 1550 auto split = findSplitAfter!pred(haystack, needle);// TODO use new interface to findSplitAfter 1551 if (split[0].empty) 1552 { 1553 return R1.init; // TODO correct? 1554 } 1555 else 1556 { 1557 haystack = split[1]; 1558 return split[0]; 1559 } 1560 } 1561 1562 unittest 1563 { 1564 auto source = `xyz`; 1565 auto haystack = source; 1566 auto needle = `y`; 1567 auto pop = haystack.findPopAfter(needle); 1568 assert(pop == `xy`); 1569 assert(haystack == `z`); 1570 } 1571 1572 unittest 1573 { 1574 auto source = `xy`; 1575 auto haystack = source; 1576 auto needle = `z`; 1577 auto pop = haystack.findPopAfter(needle); 1578 assert(pop is null); 1579 assert(!pop); 1580 assert(haystack == source); 1581 } 1582 1583 /** Find First Occurrence any of $(D needles) in $(D haystack). 1584 Like to std.algorithm.find but takes an array of needles as argument instead 1585 of a variadic list of key needle arguments. 1586 Return found range plus index into needles starting at 1 upon. 1587 */ 1588 Tuple!(R, size_t) findFirstOfAnyInOrder(alias pred = `a == b`, R)(R haystack, const R[] needles) 1589 { 1590 import std.algorithm: find; 1591 switch (needles.length) 1592 { 1593 case 1: 1594 import std.range : empty; 1595 auto hit = haystack.find(needles[0]); 1596 return tuple(hit, hit.empty ? 0UL : 1UL); 1597 case 2: 1598 return haystack.find(needles[0], 1599 needles[1]); 1600 case 3: 1601 return haystack.find(needles[0], 1602 needles[1], 1603 needles[2]); 1604 case 4: 1605 return haystack.find(needles[0], 1606 needles[1], 1607 needles[2], 1608 needles[3]); 1609 case 5: 1610 return haystack.find(needles[0], 1611 needles[1], 1612 needles[2], 1613 needles[3], 1614 needles[4]); 1615 case 6: 1616 return haystack.find(needles[0], 1617 needles[1], 1618 needles[2], 1619 needles[3], 1620 needles[4], 1621 needles[5]); 1622 case 7: 1623 return haystack.find(needles[0], 1624 needles[1], 1625 needles[2], 1626 needles[3], 1627 needles[4], 1628 needles[5], 1629 needles[6]); 1630 case 8: 1631 return haystack.find(needles[0], 1632 needles[1], 1633 needles[2], 1634 needles[3], 1635 needles[4], 1636 needles[5], 1637 needles[6], 1638 needles[7]); 1639 default: 1640 import std.conv: to; 1641 assert(false, `Too many keys ` ~ needles.length.to!string); 1642 } 1643 } 1644 1645 unittest 1646 { 1647 assert(`abc`.findFirstOfAnyInOrder([`x`]) == tuple(``, 0UL)); 1648 assert(`abc`.findFirstOfAnyInOrder([`a`]) == tuple(`abc`, 1UL)); 1649 assert(`abc`.findFirstOfAnyInOrder([`c`]) == tuple(`c`, 1UL)); 1650 assert(`abc`.findFirstOfAnyInOrder([`a`, `b`]) == tuple(`abc`, 1UL)); 1651 assert(`abc`.findFirstOfAnyInOrder([`a`, `b`]) == tuple(`abc`, 1UL)); 1652 assert(`abc`.findFirstOfAnyInOrder([`x`, `b`]) == tuple(`bc`, 2UL)); 1653 } 1654 1655 Tuple!(R, size_t)[] findAllOfAnyInOrder(alias pred = `a == b`, R)(R haystack, R[] needles) 1656 { 1657 // TODO Return some clever lazy range that calls each possible haystack.findFirstOfAnyInOrder(needles); 1658 return typeof(return).init; 1659 } 1660 1661 /** Return true if all arguments $(D args) are strictly ordered, 1662 that is args[0] < args[1] < args[2] < ... . 1663 TODO: CT-variant 1664 See also: http://forum.dlang.org/thread/wzsdhzycwqyrvqmmttix@forum.dlang.org?page=2#post-vprvhifglfegnlvzqmjj:40forum.dlang.org 1665 */ 1666 bool areStrictlyOrdered(Ts...)(Ts args) 1667 if (args.length >= 2 && 1668 haveCommonType!Ts) 1669 { 1670 foreach (i, arg; args[1..$]) 1671 if (args[i] >= arg) return false; 1672 return true; 1673 } 1674 1675 unittest 1676 { 1677 static assert(!__traits(compiles, areStrictlyOrdered())); 1678 static assert(!__traits(compiles, areStrictlyOrdered(1))); 1679 assert(areStrictlyOrdered(1, 2, 3)); 1680 assert(!areStrictlyOrdered(1, 3, 2)); 1681 assert(!areStrictlyOrdered(1, 2, 2)); 1682 assert(areStrictlyOrdered('a', 'b', 'c')); 1683 } 1684 1685 /** Return true if all arguments $(D args) are unstrictly ordered, 1686 that is args[0] <= args[1] <= args[2] <= ... . 1687 TODO: CT-variant 1688 See also: http://forum.dlang.org/thread/wzsdhzycwqyrvqmmttix@forum.dlang.org?page=2#post-vprvhifglfegnlvzqmjj:40forum.dlang.org 1689 */ 1690 bool areUnstrictlyOrdered(Ts...)(Ts args) 1691 if (args.length >= 2 && 1692 haveCommonType!Ts) 1693 { 1694 foreach (i, arg; args[1..$]) 1695 if (args[i] > arg) return false; 1696 return true; 1697 } 1698 1699 unittest 1700 { 1701 static assert(!__traits(compiles, areUnstrictlyOrdered())); 1702 static assert(!__traits(compiles, areUnstrictlyOrdered(1))); 1703 assert(areUnstrictlyOrdered(1, 2, 2, 3)); 1704 assert(!areUnstrictlyOrdered(1, 3, 2)); 1705 assert(areUnstrictlyOrdered('a', 'b', 'c')); 1706 } 1707 1708 import core.checkedint: addu, subu, mulu; 1709 1710 alias sadd = addu; 1711 alias ssub = subu; 1712 alias smul = mulu; 1713 1714 /** Append Arguments $(args) to $(D data). 1715 TODO Add support for other Random Access Ranges such as std.container.Array 1716 See also: http://forum.dlang.org/thread/mevnosveagdiswkxtbrv@forum.dlang.org?page=1 1717 */ 1718 ref R append(R, Args...)(ref R data, 1719 auto ref Args args) 1720 if (args.length >= 1) 1721 { 1722 alias E = ElementType!R; 1723 1724 import std.traits : isAssignable; 1725 enum isElementType(U) = isAssignable!(E, U); 1726 1727 import std.meta : allSatisfy; 1728 1729 static if (args.length == 1) 1730 { 1731 data ~= args[0]; 1732 } 1733 else static if (isRandomAccessRange!R && 1734 allSatisfy!(isElementType, Args)) 1735 { 1736 data.length += args.length; 1737 foreach (i, arg; args) 1738 { 1739 data[$ - args.length + i] = arg; 1740 } 1741 } 1742 else 1743 { 1744 static size_t estimateLength(Args args) 1745 { 1746 size_t result; 1747 import std.traits : isArray; 1748 foreach (arg; args) 1749 { 1750 alias A = typeof(arg); 1751 import std.range : hasLength; 1752 static if (isArray!A && 1753 is(E == ElementType!A) && 1754 hasLength!A) 1755 { 1756 result += arg.length; 1757 } 1758 else 1759 { 1760 result += 1; 1761 } 1762 } 1763 // import std.stdio; 1764 // writeln(args, ` : `, result); 1765 return result; 1766 } 1767 1768 import std.range: appender; 1769 auto app = appender!(R)(data); 1770 1771 app.reserve(data.length + estimateLength(args)); 1772 1773 foreach (arg; args) 1774 { 1775 app.put(arg); 1776 } 1777 data = app.data; 1778 } 1779 1780 return data; 1781 } 1782 1783 unittest 1784 { 1785 int[] data; 1786 import std.range: only, iota; 1787 1788 data.append(-1, 0, only(1, 2, 3), iota(4, 9)); 1789 assert(data == [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8]); 1790 1791 data.append(9, 10); 1792 assert(data == [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); 1793 1794 data.append([11, 12], [13, 14]); 1795 assert(data == [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]); 1796 1797 // int[3] d; 1798 // data.append(d, d); 1799 1800 static assert(!__traits(compiles, { data.append(); })); 1801 } 1802 1803 unittest 1804 { 1805 import std.container: Array; 1806 import std.algorithm: equal; 1807 1808 Array!int data; 1809 1810 data.append(-1); 1811 assert(equal(data[], [-1])); 1812 1813 static assert(!__traits(compiles, { data.append(); })); 1814 } 1815 1816 import std.algorithm: aggregate; 1817 1818 /** Distinct Elements of $(D r). 1819 1820 See also: http://forum.dlang.org/thread/jufggxqwzhlsmhshtnfj@forum.dlang.org?page=2 1821 See also: http://dpaste.dzfl.pl/7b4b37b490a7 1822 */ 1823 auto distinct(R)(R r) 1824 if (isInputRange!(Unqual!R)) 1825 { 1826 import std.traits: ForeachType; 1827 bool[ForeachType!R] seen; // TODO Use containers.hashset.HashSet 1828 import std.algorithm.iteration: filter; 1829 return r.filter!((k) 1830 { 1831 if (k !in seen) 1832 return false; 1833 else 1834 return seen[k] = true; 1835 }); 1836 } 1837 1838 // unittest 1839 // { 1840 // immutable first = [1, 0, 2, 1, 3]; 1841 // immutable second = [1,5,6]; 1842 // import std.algorithm.comparison: equal; 1843 // import std.range: chain, take; 1844 // assert(equal(first.chain(second).distinct.take(5), 1845 // [1, 0, 2, 3, 5])); 1846 // } 1847 1848 T[n] s(T, size_t n)(auto ref T[n] values) @property @safe pure nothrow 1849 { 1850 return values; 1851 } 1852 1853 @safe pure nothrow unittest 1854 { 1855 enum n = 3; 1856 auto a = [1, 2, 3]; 1857 auto x = [1, 2, 3].s; 1858 static assert(is(typeof(x) == int[3])); 1859 static assert(is(typeof([1, 2, 3].s) == int[3])); 1860 assert([1, 2, 3].s[2] == 3); 1861 } 1862 1863 /** 1864 */ 1865 auto staticArray() @property @safe 1866 { 1867 static struct _staticArray 1868 { 1869 T[n] s(T, size_t n)(auto ref T[n] values) @safe @property { return values; } 1870 1871 T[0][n] opIndex(size_t n = T.length, T...)(T items) 1872 { 1873 typeof(return) arr; 1874 foreach (index,item; items) 1875 arr[index] = item; 1876 1877 return (values) { return values; }(arr);//s!(T[0], n)(arr); 1878 } 1879 } 1880 return _staticArray(); 1881 } 1882 1883 /** 1884 */ 1885 bool isAmong(alias pred = (a, b) => a == b, 1886 Value, 1887 Values...)(Value value, 1888 Values values) 1889 if (Values.length != 0) 1890 { 1891 import std.algorithm.comparison : among; 1892 return value.among!pred(values) != 0; 1893 } 1894 1895 @safe pure nothrow @nogc unittest 1896 { 1897 assert(`b`.isAmong(`a`, `b`)); 1898 assert(!`c`.isAmong(`a`, `b`)); 1899 } 1900 1901 import std.traits : isExpressionTuple; 1902 import traits_ex : haveCommonType; 1903 1904 /** 1905 */ 1906 template isAmong(values...) 1907 if (isExpressionTuple!values) 1908 { 1909 bool isAmong(Value)(Value value) 1910 if (haveCommonType!(Value, values)) 1911 { 1912 switch (value) 1913 { 1914 foreach (uint i, v; values) 1915 case v: 1916 return true; 1917 default: 1918 return false; 1919 } 1920 } 1921 } 1922 1923 /// Ditto 1924 @safe pure nothrow @nogc unittest 1925 { 1926 assert(`b`.isAmong!(`a`, `b`)); 1927 assert(!`c`.isAmong!(`a`, `b`)); 1928 } 1929 1930 import std.algorithm.setops : cartesianProduct; 1931 1932 /** More descriptive alias. 1933 */ 1934 alias elementCombinations = cartesianProduct; 1935 1936 /** Reset all members in aggregate instance $(D c). 1937 See also: http://forum.dlang.org/post/ckitmpguywfitgadfpkv@forum.dlang.org 1938 See also: http://forum.dlang.org/post/fbs8b5$5bu$1@digitalmars.com 1939 */ 1940 void resetAllMembers(T)(T c) 1941 if (is(T == class)) 1942 { 1943 foreach (ref m; c.tupleof) 1944 { 1945 import std.traits : isMutable; 1946 alias M = typeof(m); 1947 static if (isMutable!M) 1948 { 1949 m = M.init; 1950 } 1951 } 1952 } 1953 1954 unittest 1955 { 1956 class C 1957 { 1958 this (int a, int b, string c) 1959 { 1960 this.a = a; 1961 this.b = b; 1962 this.c = c; 1963 } 1964 int a; int b; string c; 1965 } 1966 void f(C c) 1967 { 1968 c.resetAllMembers(); 1969 } 1970 auto c = new C(1, 2, "3"); 1971 assert(c.a == 1); 1972 assert(c.b == 2); 1973 assert(c.c == "3"); 1974 f(c); 1975 assert(c.a == 0); 1976 assert(c.b == 0); 1977 assert(c.c == null); 1978 } 1979 1980 /** Returns: `true` if `r` contains strictly values that are strictly increase 1981 with the increment `step`. 1982 See also: http://forum.dlang.org/post/mqjyhvqxepgfljpkxvmd@forum.dlang.org 1983 */ 1984 bool isLinearRamp(R)(R r, size_t step = 1) 1985 if (isInputRange!R && 1986 isIntegral!(ElementType!R)) 1987 { 1988 import std.algorithm : findAdjacent; 1989 import std.range : empty; 1990 return r.findAdjacent!((a, b) => a + step != b).empty; 1991 } 1992 1993 bool hasHoles(R)(R r) 1994 if (isInputRange!R && 1995 isIntegral!(ElementType!R)) 1996 { 1997 return !r.isLinearRamp; 1998 } 1999 2000 @safe pure nothrow unittest 2001 { 2002 assert([1].isLinearRamp); 2003 assert([1, 2, 3].isLinearRamp); 2004 assert(![1, 1].isLinearRamp); 2005 assert(![1, 2, 1].isLinearRamp); 2006 assert(![1, 2, 4].isLinearRamp); 2007 } 2008 2009 /** Check if `r` counts to exactly `exactCount` elements. 2010 */ 2011 bool countsExactly(R)(R r, size_t exactCount) @("complexity", "O(exactCount)") 2012 if (isInputRange!R) 2013 { 2014 import std.range : hasLength; 2015 static if (hasLength!R) 2016 { 2017 return r.length == exactCount; 2018 } 2019 else 2020 { 2021 size_t n; 2022 import std.range : empty; 2023 while (!r.empty) 2024 { 2025 r.popFront; 2026 if (++n > exactCount) { return false; } 2027 } 2028 return n == exactCount; 2029 } 2030 } 2031 2032 /** Check if `r` counts to at least `minCount` elements. 2033 */ 2034 bool countsAtLeast(R)(R r, size_t minCount) @("complexity", "O(minCount)") 2035 if (isInputRange!R) 2036 { 2037 import std.range : hasLength; 2038 static if (hasLength!R) 2039 { 2040 return r.length >= minCount; 2041 } 2042 else 2043 { 2044 size_t n; 2045 import std.range : empty; 2046 while (!r.empty) 2047 { 2048 r.popFront; 2049 if (++n >= minCount) { return true; } 2050 } 2051 return false; 2052 } 2053 } 2054 2055 /** Check if `r` counts to at most `maxCount` elements. 2056 */ 2057 bool countsAtMost(R)(R r, size_t maxCount) @("complexity", "O(maxCount)") 2058 if (isInputRange!R) 2059 { 2060 import std.range : hasLength; 2061 static if (hasLength!R) 2062 { 2063 return r.length <= maxCount; 2064 } 2065 else 2066 { 2067 size_t n; 2068 import std.range : empty; 2069 while (!r.empty) 2070 { 2071 r.popFront; 2072 if (++n > maxCount) { return false; } 2073 } 2074 return true; 2075 } 2076 } 2077 2078 /// 2079 @safe pure nothrow unittest 2080 { 2081 static void test(R)(R x) if (isInputRange!R) 2082 { 2083 import std.algorithm : count; 2084 const n = x.count; 2085 2086 // below 2087 foreach (const i; 0 .. n) 2088 { 2089 assert(x.countsAtLeast(i)); 2090 assert(!x.countsExactly(i)); 2091 assert(!x.countsAtMost(i)); 2092 } 2093 2094 // at 2095 assert(x.countsAtLeast(n)); 2096 assert(x.countsExactly(n)); 2097 assert(x.countsAtMost(n)); 2098 2099 // above 2100 foreach (const i; n + 1 .. n + 10) 2101 { 2102 assert(!x.countsAtLeast(i)); 2103 assert(!x.countsExactly(i)); 2104 assert(x.countsAtMost(i)); 2105 } 2106 } 2107 2108 import std.algorithm : filter; 2109 import std.range : iota; 2110 import std.array : array; 2111 2112 test(3.iota.filter!(x => true)); 2113 test(3.iota.array); 2114 } 2115 2116 /** This function returns the index of the `value` if it exist among `values`, 2117 `size_t.max` otherwise. 2118 */ 2119 size_t binarySearch(T, U)(const T[] values, in U value) 2120 if (is(typeof(T[0].init == U.init))) // TODO SortedRange support 2121 { 2122 // value is not in the array if the array is empty 2123 if (values.length == 0) { return size_t.max; } 2124 2125 immutable mid = values.length / 2; // mid offset 2126 if (value == values[mid]) 2127 { 2128 return mid; // direct hit 2129 } 2130 else if (value < values[mid]) 2131 { 2132 return binarySearch(values[0 .. mid], value); // recurse left 2133 } 2134 else 2135 { 2136 auto index = binarySearch(values[mid + 1 .. $], value); // recurse right 2137 if (index != size_t.max) 2138 { 2139 index += mid + 1; // adjust the index; it is 0-based in the right-hand side slice. 2140 } 2141 return index; 2142 } 2143 }