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