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