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