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