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