1 /** Array-only overloads of Phobos algorithms. 2 * 3 * Functions are when possible `@safe pure nothrow @nogc`. 4 * Haystack parameter is when possible and relevant `scope return inout(T)[]` and DIP-1000-compliant. 5 * Needle parameter is either `scope const(T)[]` or `scope const T[]`. 6 * 7 * Provides more than twice as fast compilation for `char`-arrays (`string`s). 8 * 9 * See_Also: https://forum.dlang.org/post/sjirukypxmmcgdmqbcpe@forum.dlang.org 10 * See_Also: https://forum.dlang.org/thread/ybamybeakxwxwleebnwb@forum.dlang.org?page=1 11 * 12 * TODO: Merge into separate array-specializations of Phobos algorithms for less template bloat in Phobos. 13 * 14 * TODO: Move to nxt/algorithm/searching_ex.d 15 */ 16 module nxt.array_algorithm; 17 18 // version = unittestAsBetterC; // Run_As: dmd -betterC -unittest -run $(__FILE__).d 19 20 /** Array-specialization of `startsWith` with default predicate. 21 * 22 * See_Also: https://d.godbolt.org/z/ejEmrK 23 */ 24 bool startsWith(T)(scope const T[] haystack, 25 scope const T[] needle) @trusted 26 { 27 if (haystack.length < needle.length) 28 return false; 29 return haystack.ptr[0 .. needle.length] == needle; 30 } 31 /// ditto 32 bool startsWith(T)(scope const T[] haystack, 33 scope const T needle) @trusted 34 { 35 static if (is(T == char)) 36 assert(needle < 128); // See_Also: https://forum.dlang.org/post/sjirukypxmmcgdmqbcpe@forum.dlang.org 37 if (haystack.length == 0) 38 return false; 39 return haystack.ptr[0] == needle; 40 } 41 42 /// 43 @safe pure nothrow @nogc unittest 44 { 45 const x = "beta version"; 46 assert(x.startsWith("beta")); 47 assert(x.startsWith('b')); 48 assert(!x.startsWith("_")); 49 } 50 51 /** Array-specialization of `all` with element needle. */ 52 bool all(T)(scope const T[] haystack, 53 scope const T needle) @trusted 54 { 55 static if (is(T == char)) 56 assert(needle < 128); // See_Also: https://forum.dlang.org/post/sjirukypxmmcgdmqbcpe@forum.dlang.org 57 foreach (const offset; 0 .. haystack.length) 58 if (haystack.ptr[offset] != needle) 59 return false; 60 return true; 61 } 62 63 /// 64 @safe pure nothrow @nogc unittest 65 { 66 assert("".all('a')); // matches behaviour of `std.algorithm.searching.any` 67 assert("aaa".all('a')); 68 assert(!"aa_".all('a')); 69 } 70 71 /** Array-specialization of `any` with element needle. */ 72 bool any(T)(scope const T[] haystack, 73 scope const T needle) @trusted 74 { 75 static if (is(T == char)) 76 assert(needle < 128); // See_Also: https://forum.dlang.org/post/sjirukypxmmcgdmqbcpe@forum.dlang.org 77 foreach (const offset; 0 .. haystack.length) 78 if (haystack.ptr[offset] == needle) 79 return true; 80 return false; 81 } 82 83 /// 84 @safe pure nothrow @nogc unittest 85 { 86 assert(!"".any('a')); // matches behaviour of `std.algorithm.searching.any` 87 assert("aaa".any('a')); 88 assert("aa_".any('a')); 89 assert(!"_".any('a')); 90 } 91 92 /** Array-specialization of `endsWith` with default predicate. */ 93 bool endsWith(T)(scope const T[] haystack, 94 scope const T[] needle) @trusted 95 { 96 if (haystack.length < needle.length) 97 return false; 98 return haystack.ptr[haystack.length - needle.length .. haystack.length] == needle; 99 } 100 /// ditto 101 bool endsWith(T)(scope const T[] haystack, 102 scope const T needle) @trusted 103 { 104 static if (is(T == char)) 105 assert(needle < 128); // See_Also: https://forum.dlang.org/post/sjirukypxmmcgdmqbcpe@forum.dlang.org 106 if (haystack.length == 0) 107 return false; 108 return haystack.ptr[haystack.length - 1] == needle; 109 } 110 111 /// 112 @safe pure nothrow @nogc unittest 113 { 114 const x = "beta version"; 115 assert(x.endsWith("version")); 116 assert(x.endsWith('n')); 117 assert(!x.startsWith("_")); 118 } 119 120 bool endsWithEither(T)(scope const T[] haystack, 121 scope const T[][] needles) @trusted 122 { 123 foreach (const needle; needles) 124 if (haystack.endsWith(needle)) // TODO: optimize 125 return true; 126 return false; 127 } 128 /// ditto 129 bool endsWithEither(T)(scope const T[] haystack, 130 scope const T[] needles) @trusted 131 { 132 foreach (const needle; needles) 133 if (haystack.endsWith(needle)) // TODO: optimize 134 return true; 135 return false; 136 } 137 138 /// 139 @safe pure nothrow @nogc unittest 140 { 141 const x = "beta version"; 142 assert(x.endsWithEither(["version", ""])); 143 assert(x.endsWithEither(['n', ' '])); 144 assert(x.endsWithEither("n ")); 145 } 146 147 /** Array-specialization of `findSkip` with default predicate. 148 */ 149 auto findSkip(T)(scope ref inout(T)[] haystack, 150 scope const T[] needle) @trusted 151 { 152 const index = haystack.indexOf(needle); 153 if (index != -1) 154 { 155 haystack = haystack.ptr[index + needle.length .. haystack.length]; 156 return true; 157 } 158 return false; 159 } 160 /// ditto 161 auto findSkip(T)(scope ref inout(T)[] haystack, 162 scope const T needle) @trusted 163 { 164 const index = haystack.indexOf(needle); 165 if (index != -1) 166 { 167 haystack = haystack.ptr[index + 1 .. haystack.length]; 168 return true; 169 } 170 return false; 171 } 172 173 /// 174 @safe pure nothrow @nogc unittest 175 { 176 const auto x = "abc"; 177 { 178 string y = x; 179 const bool ok = y.findSkip("_"); 180 assert(!ok); 181 assert(y is x); 182 } 183 { 184 string y = x; 185 const bool ok = y.findSkip("a"); 186 assert(ok); 187 assert(y == x[1 .. $]); 188 } 189 { 190 string y = x; 191 const bool ok = y.findSkip("c"); 192 assert(ok); 193 assert(y is x[$ .. $]); 194 } 195 char[] f()() @safe pure nothrow { char[1] x = "_"; return x[].findSkip(" "); } 196 static if (hasPreviewDIP1000) static assert(!__traits(compiles, { auto _ = f(); })); 197 } 198 199 /// 200 @safe pure nothrow @nogc unittest 201 { 202 const auto x = "abc"; 203 { 204 string y = x; 205 const bool ok = y.findSkip('_'); 206 assert(!ok); 207 assert(y is x); 208 } 209 { 210 string y = x; 211 const bool ok = y.findSkip('a'); 212 assert(ok); 213 assert(y == x[1 .. $]); 214 } 215 { 216 string y = x; 217 const bool ok = y.findSkip('c'); 218 assert(ok); 219 assert(y is x[$ .. $]); 220 } 221 char[] f()() @safe pure nothrow { char[1] x = "_"; return x[].findSkip(' '); } 222 static if (hasPreviewDIP1000) static assert(!__traits(compiles, { auto _ = f(); })); 223 } 224 225 /** Array-specialization of `findSkip` with default predicate that finds the last skip. 226 */ 227 auto findLastSkip(T)(scope ref inout(T)[] haystack, 228 scope const T[] needle) @trusted 229 { 230 const index = haystack.lastIndexOf(needle); 231 if (index != -1) 232 { 233 haystack = haystack.ptr[index + needle.length .. haystack.length]; 234 return true; 235 } 236 return false; 237 } 238 /// 239 auto findLastSkip(T)(scope ref inout(T)[] haystack, 240 scope const T needle) @trusted 241 { 242 const index = haystack.lastIndexOf(needle); 243 if (index != -1) 244 { 245 haystack = haystack.ptr[index + 1 .. haystack.length]; 246 return true; 247 } 248 return false; 249 } 250 251 /// 252 @safe pure nothrow @nogc unittest 253 { 254 const auto x = "abacc"; 255 { 256 string y = x; 257 const bool ok = y.findLastSkip("_"); 258 assert(!ok); 259 assert(y is x); 260 } 261 { 262 string y = x; 263 const bool ok = y.findLastSkip("a"); 264 assert(ok); 265 assert(y == x[3 .. $]); 266 } 267 { 268 string y = x; 269 const bool ok = y.findLastSkip("c"); 270 assert(ok); 271 assert(y is x[$ .. $]); 272 } 273 char[] f()() @safe pure nothrow { char[1] x = "_"; return x[].findLastSkip(" "); } 274 static if (hasPreviewDIP1000) static assert(!__traits(compiles, { auto _ = f(); })); 275 } 276 277 /// 278 @safe pure nothrow @nogc unittest 279 { 280 const auto x = "abacc"; 281 { 282 string y = x; 283 const bool ok = y.findLastSkip('_'); 284 assert(!ok); 285 assert(y is x); 286 } 287 { 288 string y = x; 289 const bool ok = y.findLastSkip('a'); 290 assert(ok); 291 assert(y == x[3 .. $]); 292 } 293 { 294 string y = x; 295 const bool ok = y.findLastSkip('c'); 296 assert(ok); 297 assert(y is x[$ .. $]); 298 } 299 char[] f()() @safe pure nothrow { char[1] x = "_"; return x[].findLastSkip(' '); } 300 static if (hasPreviewDIP1000) static assert(!__traits(compiles, { auto _ = f(); })); 301 } 302 303 /** Array-specialization of `skipOver` with default predicate. 304 * 305 * See_Also: https://forum.dlang.org/post/dhxwgtaubzbmjaqjmnmq@forum.dlang.org 306 */ 307 bool skipOver(T)(scope ref inout(T)[] haystack, 308 scope const T[] needle) @trusted 309 { 310 if (!startsWith(haystack, needle)) 311 return false; 312 haystack = haystack.ptr[needle.length .. haystack.length]; 313 return true; 314 } 315 /// ditto 316 bool skipOver(T)(scope ref inout(T)[] haystack, 317 scope const T needle) @trusted 318 { 319 if (!startsWith(haystack, needle)) 320 return false; 321 haystack = haystack.ptr[1 .. haystack.length]; 322 return true; 323 } 324 325 /// 326 @safe pure nothrow @nogc unittest 327 { 328 string x = "beta version"; 329 assert(x.skipOver("beta")); 330 assert(x == " version"); 331 assert(x.skipOver(' ')); 332 assert(x == "version"); 333 } 334 335 /// constness of haystack and needle 336 @safe pure nothrow @nogc unittest 337 { 338 { 339 const(char)[] haystack; 340 string needle; 341 assert(haystack.skipOver(needle)); 342 } 343 { 344 const(char)[] haystack; 345 const(char)[] needle; 346 assert(haystack.skipOver(needle)); 347 } 348 { 349 const(char)[] haystack; 350 char[] needle; 351 assert(haystack.skipOver(needle)); 352 } 353 } 354 355 /** Array-specialization of `skipOverBack` with default predicate. 356 * 357 * See_Also: https://forum.dlang.org/post/dhxwgtaubzbmjaqjmnmq@forum.dlang.org 358 */ 359 bool skipOverBack(T)(scope ref inout(T)[] haystack, 360 scope const T[] needle) @trusted 361 { 362 if (!endsWith(haystack, needle)) 363 return false; 364 haystack = haystack.ptr[0 .. haystack.length - needle.length]; 365 return true; 366 } 367 /// ditto 368 bool skipOverBack(T)(scope ref inout(T)[] haystack, 369 scope const T needle) @trusted 370 { 371 if (!endsWith(haystack, needle)) 372 return false; 373 haystack = haystack.ptr[0 .. haystack.length - 1]; 374 return true; 375 } 376 377 /// 378 @safe pure nothrow @nogc unittest 379 { 380 string x = "beta version"; 381 assert(x.skipOverBack(" version")); 382 assert(x == "beta"); 383 assert(x.skipOverBack('a')); 384 assert(x == "bet"); 385 } 386 387 bool skipOverAround(T)(scope ref inout(T)[] haystack, 388 scope const T[] needleFront, 389 scope const T[] needleBack) @trusted 390 { 391 if (!startsWith(haystack, needleFront) || 392 !endsWith(haystack, needleBack)) 393 return false; 394 haystack = haystack.ptr[needleFront.length .. haystack.length - needleBack.length]; 395 return true; 396 } 397 /// ditto 398 bool skipOverAround(T)(scope ref inout(T)[] haystack, 399 scope const T needleFront, 400 scope const T needleBack) @trusted 401 { 402 if (!startsWith(haystack, needleFront) || 403 !endsWith(haystack, needleBack)) 404 return false; 405 haystack = haystack.ptr[1 .. haystack.length - 1]; 406 return true; 407 } 408 409 /// 410 @safe pure nothrow @nogc unittest 411 { 412 string x = "alpha beta_gamma"; 413 assert(x.skipOverAround("alpha", "gamma")); 414 assert(x == " beta_"); 415 assert(x.skipOverAround(' ', '_')); 416 assert(x == "beta"); 417 } 418 419 /** Array-specialization of `stripLeft` with default predicate. 420 * 421 * See_Also: https://forum.dlang.org/post/dhxwgtaubzbmjaqjmnmq@forum.dlang.org 422 */ 423 inout(T)[] stripLeft(T)(scope return inout(T)[] haystack, 424 scope const T needle) @trusted 425 { 426 static if (is(T == char)) 427 assert(needle < 128); // See_Also: https://forum.dlang.org/post/sjirukypxmmcgdmqbcpe@forum.dlang.org 428 size_t offset = 0; 429 while (offset != haystack.length && 430 haystack.ptr[offset] == needle) // TODO: elide range-check 431 offset += 1; 432 return haystack.ptr[offset .. haystack.length]; 433 } 434 /// ditto 435 inout(char)[] stripLeft()(scope return inout(char)[] haystack) @safe pure nothrow @nogc /* template-lazy */ 436 { 437 return haystack.stripLeft(' '); 438 } 439 440 /// 441 @safe pure nothrow @nogc unittest 442 { 443 assert("beta".stripLeft(' ') == "beta"); 444 assert(" beta".stripLeft(' ') == "beta"); 445 assert(" beta".stripLeft(' ') == "beta"); 446 assert(" beta".stripLeft(' ') == "beta"); 447 assert(" beta".stripLeft() == "beta"); 448 assert(" _ beta _ ".stripLeft(' ') == "_ beta _ "); 449 assert(" _ beta _ ".stripLeft(' ') == "_ beta _ "); 450 451 char[] f()() @safe pure nothrow { char[1] x = "_"; return x[].stripLeft(' '); } 452 static if (hasPreviewDIP1000) static assert(!__traits(compiles, { auto _ = f(); })); 453 } 454 455 /** Array-specialization of `stripRight` with default predicate. 456 * 457 * See_Also: https://forum.dlang.org/post/dhxwgtaubzbmjaqjmnmq@forum.dlang.org 458 */ 459 inout(T)[] stripRight(T)(scope return inout(T)[] haystack, 460 scope const T needle) @trusted 461 { 462 static if (is(T == char)) 463 assert(needle < 128); // See_Also: https://forum.dlang.org/post/sjirukypxmmcgdmqbcpe@forum.dlang.org 464 size_t offset = haystack.length; 465 while (offset != 0 && 466 haystack.ptr[offset - 1] == needle) // TODO: elide range-check 467 offset -= 1; 468 return haystack.ptr[0 .. offset]; 469 } 470 /// ditto 471 inout(char)[] stripRight()(scope return inout(char)[] haystack) @safe pure nothrow @nogc /* template-lazy */ 472 { 473 return haystack.stripRight(' '); 474 } 475 476 /// 477 @safe pure nothrow @nogc unittest 478 { 479 assert("beta".stripRight(' ') == "beta"); 480 assert("beta ".stripRight(' ') == "beta"); 481 assert("beta ".stripRight(' ') == "beta"); 482 assert("beta ".stripRight(' ') == "beta"); 483 assert("beta ".stripRight() == "beta"); 484 assert(" _ beta _ ".stripRight(' ') == " _ beta _"); 485 assert(" _ beta _ ".stripRight(' ') == " _ beta _"); 486 487 char[] f()() @safe pure nothrow { char[1] x = "_"; return x[].stripRight(' '); } 488 static if (hasPreviewDIP1000) static assert(!__traits(compiles, { auto _ = f(); })); 489 } 490 491 /** Array-specialization of `strip` with default predicate. 492 * 493 * See_Also: https://forum.dlang.org/post/dhxwgtaubzbmjaqjmnmq@forum.dlang.org 494 */ 495 inout(T)[] strip(T)(scope return inout(T)[] haystack, 496 scope const T needle) @trusted 497 { 498 static if (is(T == char)) 499 assert(needle < 128); // See_Also: https://forum.dlang.org/post/sjirukypxmmcgdmqbcpe@forum.dlang.org 500 501 size_t leftOffset = 0; 502 while (leftOffset != haystack.length && 503 haystack.ptr[leftOffset] == needle) // TODO: elide range-check 504 leftOffset += 1; 505 506 size_t rightOffset = haystack.length; 507 while (rightOffset != leftOffset && 508 haystack.ptr[rightOffset - 1] == needle) // TODO: elide range-check 509 rightOffset -= 1; 510 511 return haystack.ptr[leftOffset .. rightOffset]; 512 } 513 /// ditto 514 inout(char)[] strip()(scope return inout(char)[] haystack) @safe pure nothrow @nogc /* template-lazy */ 515 { 516 return haystack.strip(' '); 517 } 518 519 /// 520 @safe pure nothrow @nogc unittest 521 { 522 assert("beta".strip(' ') == "beta"); 523 assert(" beta ".strip(' ') == "beta"); 524 assert(" beta ".strip(' ') == "beta"); 525 assert(" beta ".strip(' ') == "beta"); 526 assert(" _ beta _ ".strip(' ') == "_ beta _"); 527 assert(" _ beta _ ".strip(' ') == "_ beta _"); 528 529 char[] f()() @safe pure nothrow { char[1] x = "_"; return x[].strip(' '); } 530 static if (hasPreviewDIP1000) static assert(!__traits(compiles, { auto _ = f(); })); 531 } 532 533 /// 534 @safe pure nothrow @nogc unittest 535 { 536 const ubyte[3] x = [0, 42, 0]; 537 assert(x.strip(0) == x[1 .. 2]); 538 } 539 540 /** Array-specialization of `count` with default predicate. 541 * 542 * TODO: Add optimized implementation for needles with length >= 543 * `largeNeedleLength` with no repeat of elements. 544 * 545 * TODO: reuse `return haystack.indexOf(needle) != -1` in both overloads 546 */ 547 bool canFind(T)(scope const T[] haystack, 548 scope const T[] needle) @trusted 549 in(needle.length, "Cannot count occurrences of an empty range") 550 { 551 // enum largeNeedleLength = 4; 552 if (haystack.length < needle.length) 553 return false; 554 foreach (const offset; 0 .. haystack.length - needle.length + 1) 555 if (haystack.ptr[offset .. offset + needle.length] == needle) 556 return true; 557 return false; 558 } 559 /// ditto 560 bool canFind(T)(scope const T[] haystack, 561 scope const T needle) @trusted 562 { 563 static if (is(T == char)) 564 assert(needle < 128); // See_Also: https://forum.dlang.org/post/sjirukypxmmcgdmqbcpe@forum.dlang.org 565 if (haystack.length == 0) 566 return false; 567 foreach (const ref element; haystack) 568 if (element == needle) 569 return true; 570 return false; 571 } 572 573 /// 574 @safe pure nothrow @nogc unittest 575 { 576 assert(!"".canFind("_")); 577 assert(!"a".canFind("_")); 578 assert("a".canFind("a")); 579 assert(!"a".canFind("ab")); 580 assert("ab".canFind("a")); 581 assert("ab".canFind("b")); 582 assert("ab".canFind("ab")); 583 assert(!"a".canFind("ab")); 584 assert(!"b".canFind("ab")); 585 } 586 587 /// 588 @safe pure nothrow @nogc unittest 589 { 590 assert(!"".canFind('_')); 591 assert(!"a".canFind('_')); 592 assert("a".canFind('a')); 593 assert("a".canFind('a')); 594 assert("ab".canFind('a')); 595 assert("ab".canFind('b')); 596 } 597 598 /** Array-specialization of `count` with default predicate. 599 */ 600 size_t count(T)(scope const T[] haystack, 601 scope const T[] needle) @trusted 602 { 603 assert(needle.length, "Cannot count occurrences of an empty range"); 604 size_t result = 0; 605 if (haystack.length < needle.length) 606 return false; 607 foreach (const offset; 0 .. haystack.length - needle.length + 1) 608 result += haystack.ptr[offset .. offset + needle.length] == needle ? 1 : 0; 609 return result; 610 } 611 /// ditto 612 size_t count(T)(scope const T[] haystack, 613 scope const T needle) 614 { 615 static if (is(T == char)) 616 assert(needle < 128); // See_Also: https://forum.dlang.org/post/sjirukypxmmcgdmqbcpe@forum.dlang.org 617 size_t result; 618 foreach (const ref element; haystack) 619 result += element == needle ? 1 : 0; 620 return result; 621 } 622 623 /// 624 @safe pure nothrow @nogc unittest 625 { 626 // import std.algorithm.searching : count; 627 assert("".count("_") == 0); 628 assert("".count(" ") == 0); 629 assert(" ".count(" ") == 1); 630 assert("abc_abc".count("a") == 2); 631 assert("abc_abc".count("abc") == 2); 632 assert("_a_a_".count("_") == 3); 633 assert("_aaa_".count("a") == 3); 634 // assert("".count("") == 0); 635 // assert("_a_a_".count("") == 5); 636 } 637 638 /// 639 @safe pure nothrow @nogc unittest 640 { 641 assert("".count('_') == 0); 642 assert("abc_abc".count('a') == 2); 643 assert("_abc_abc_".count('_') == 3); 644 } 645 646 /** Array-specialization of `count` with default predicate and no needle. 647 */ 648 size_t count(T)(scope const T[] haystack) => haystack.length; 649 650 /// 651 @safe pure nothrow @nogc unittest 652 { 653 assert("abc_abc".count == 7); 654 } 655 656 /** Array-specialization of `indexOf` with default predicate. 657 * 658 * TODO: Add optimized implementation for needles with length >= 659 * `largeNeedleLength` with no repeat of elements. 660 */ 661 ptrdiff_t indexOf(T)(scope inout(T)[] haystack, 662 scope const(T)[] needle) @trusted 663 { 664 // enum largeNeedleLength = 4; 665 if (haystack.length < needle.length) 666 return -1; 667 foreach (const offset; 0 .. haystack.length - needle.length + 1) 668 if (haystack.ptr[offset .. offset + needle.length] == needle) 669 return offset; 670 return -1; 671 } 672 /// ditto 673 ptrdiff_t indexOf(T)(scope inout(T)[] haystack, 674 scope const T needle) 675 { 676 static if (is(T == char)) 677 assert(needle < 128); // See_Also: https://forum.dlang.org/post/sjirukypxmmcgdmqbcpe@forum.dlang.org 678 foreach (const offset, const ref element; haystack) 679 if (element == needle) 680 return offset; 681 return -1; 682 } 683 684 /// 685 @safe pure nothrow @nogc unittest 686 { 687 assert("_abc_abc_".indexOf("abc") == 1); 688 assert("__abc_".indexOf("abc") == 2); 689 assert("a".indexOf("a") == 0); 690 assert("abc".indexOf("abc") == 0); 691 assert("_".indexOf("a") == -1); 692 assert("_".indexOf("__") == -1); 693 assert("__".indexOf("a") == -1); 694 } 695 696 /// 697 @safe pure nothrow @nogc unittest 698 { 699 assert("_".indexOf('a') == -1); 700 assert("a".indexOf('a') == 0); 701 assert("_a".indexOf('a') == 1); 702 assert("__a".indexOf('a') == 2); 703 } 704 705 /// ditto 706 ptrdiff_t indexOfEither(T)(scope inout(T)[] haystack, 707 scope const T[] needles) 708 { 709 if (needles.length == 0) 710 return -1; 711 foreach (const offset, const ref element; haystack) 712 foreach (const needle; needles) 713 if (element == needle) 714 return offset; 715 return -1; 716 } 717 718 /// 719 @safe pure nothrow @nogc unittest 720 { 721 assert("_".indexOfEither("a") == -1); 722 assert("_a".indexOfEither("a") == 1); 723 assert("_a".indexOfEither("ab") == 1); 724 assert("_b".indexOfEither("ab") == 1); 725 assert("_b".indexOfEither("_") == 0); 726 assert("_b".indexOfEither("xy") == -1); 727 } 728 729 /** Array-specialization of `lastIndexOf` with default predicate. 730 */ 731 ptrdiff_t lastIndexOf(T)(scope inout(T)[] haystack, 732 scope const(T)[] needle) @trusted 733 { 734 if (haystack.length < needle.length) 735 return -1; 736 foreach_reverse (const offset; 0 .. haystack.length - needle.length + 1) 737 if (haystack.ptr[offset .. offset + needle.length] == needle) 738 return offset; 739 return -1; 740 } 741 /// ditto 742 ptrdiff_t lastIndexOf(T)(scope inout(T)[] haystack, 743 scope const T needle) 744 { 745 static if (is(T == char)) 746 assert(needle < 128); // See_Also: https://forum.dlang.org/post/sjirukypxmmcgdmqbcpe@forum.dlang.org 747 foreach_reverse (const offset, const ref element; haystack) 748 if (element == needle) 749 return offset; 750 return -1; 751 } 752 753 /// 754 @safe pure nothrow @nogc unittest 755 { 756 assert("_abc_abc_".lastIndexOf("abc") == 5); 757 assert("__abc_".lastIndexOf("abc") == 2); 758 assert("a".lastIndexOf("a") == 0); 759 assert("aa".lastIndexOf("a") == 1); 760 assert("abc".lastIndexOf("abc") == 0); 761 assert("_".lastIndexOf("a") == -1); 762 assert("_".lastIndexOf("__") == -1); 763 assert("__".lastIndexOf("a") == -1); 764 } 765 766 /// 767 @safe pure nothrow @nogc unittest 768 { 769 assert("_".lastIndexOf('a') == -1); 770 assert("a".lastIndexOf('a') == 0); 771 assert("_a".lastIndexOf('a') == 1); 772 assert("__a".lastIndexOf('a') == 2); 773 assert("a__a".lastIndexOf('a') == 3); 774 } 775 776 /** Array-specialization of `findSplit` with default predicate. 777 * 778 * See_Also: https://forum.dlang.org/post/dhxwgtaubzbmjaqjmnmq@forum.dlang.org 779 * See_Also: https://forum.dlang.org/post/zhgajqdhybtbufeiiofp@forum.dlang.org 780 */ 781 auto findSplit(T)(scope return inout(T)[] haystack, 782 scope const(T)[] needle) 783 { 784 static struct Result // NOTE `static` qualifier is needed for `inout` to propagate correctly 785 { 786 private T[] _haystack; 787 private size_t _offset; // hit offset 788 private size_t _length; // hit length 789 pragma(inline, true): 790 inout(T)[] pre() @trusted inout => _haystack.ptr[0 .. _offset]; 791 inout(T)[] separator() @trusted inout => _haystack.ptr[_offset .. _offset + _length]; 792 inout(T)[] post() @trusted inout => _haystack.ptr[_offset + _length .. _haystack.length]; 793 bool opCast(T : bool)() @safe const => _haystack.length != _offset; 794 } 795 796 assert(needle.length, "Cannot find occurrence of an empty range"); 797 const index = haystack.indexOf(needle); 798 if (index >= 0) 799 return inout(Result)(haystack, index, needle.length); 800 return inout(Result)(haystack, haystack.length, 0); // miss 801 } 802 /// ditto 803 auto findSplit(T)(scope return inout(T)[] haystack, 804 scope const T needle) 805 { 806 static struct Result // NOTE `static` qualifier is needed for `inout` to propagate correctly 807 { 808 private T[] _haystack; 809 private size_t _offset; // hit offset 810 pragma(inline, true): 811 inout(T)[] pre() @trusted inout => _haystack.ptr[0 .. _offset]; 812 inout(T)[] separator() @trusted inout => !empty ? _haystack.ptr[_offset .. _offset + 1] : _haystack[$ .. $]; 813 inout(T)[] post() @trusted inout => !empty ? _haystack.ptr[_offset + 1 .. _haystack.length] : _haystack[$ .. $]; 814 bool opCast(T : bool)() const => !empty; 815 private bool empty() const @property => _haystack.length == _offset; 816 } 817 818 static if (is(T == char)) 819 assert(needle < 128); // See_Also: https://forum.dlang.org/post/sjirukypxmmcgdmqbcpe@forum.dlang.org 820 const index = haystack.indexOf(needle); 821 if (index >= 0) 822 return inout(Result)(haystack, index); 823 return inout(Result)(haystack, haystack.length); 824 } 825 826 /// 827 @safe pure nothrow @nogc unittest 828 { 829 const h = "a**b"; 830 const r = h.findSplit("**"); 831 assert(r); 832 assert(r.pre is h[0 .. 1]); 833 assert(r.separator is h[1 .. 3]); 834 assert(r.post is h[3 .. 4]); 835 836 auto f()() @safe pure nothrow { char[1] x = "_"; return x[].findSplit(" "); } 837 static if (hasPreviewDIP1000) static assert(!__traits(compiles, { auto _ = f(); })); 838 } 839 840 /// 841 @safe pure nothrow @nogc unittest 842 { 843 const h = "a**b"; 844 const r = h.findSplit("_"); 845 static assert(r.sizeof == 2 * 2 * size_t.sizeof); 846 assert(!r); 847 assert(r.pre is h); 848 assert(r.separator is h[$ .. $]); 849 assert(r.post is h[$ .. $]); 850 } 851 852 /// 853 version(none) 854 @safe pure nothrow @nogc unittest 855 { 856 import std.algorithm.searching : findSplit; 857 const h = "a**b"; 858 const r = h.findSplit("_"); 859 static assert(r.sizeof == 3 * 2 * size_t.sizeof); 860 assert(!r); 861 assert(r[0] is h); 862 assert(r[1] is h[$ .. $]); 863 assert(r[2] is h[$ .. $]); 864 } 865 866 /// 867 @safe pure nothrow @nogc unittest 868 { 869 const r = "a*b".findSplit('*'); 870 static assert(r.sizeof == 3 * size_t.sizeof); 871 assert(r); 872 assert(r.pre == "a"); 873 assert(r.separator == "*"); 874 assert(r.post == "b"); 875 876 auto f()() @safe pure nothrow { char[1] x = "_"; return x[].findSplit(' '); } 877 static if (hasPreviewDIP1000) static assert(!__traits(compiles, { auto _ = f(); })); 878 } 879 880 /// DIP-1000 scope analysis 881 version(none) // TODO. enable 882 @safe pure nothrow @nogc unittest 883 { 884 char[] f() @safe pure nothrow 885 { 886 char[3] haystack = "a*b"; 887 auto r = haystack[].findSplit('*'); 888 static assert(is(typeof(r.pre()) == char[])); 889 return r.pre(); // TODO: this should fail 890 } 891 f(); 892 } 893 894 /** Array-specialization of `findLastSplit` with default predicate. 895 */ 896 auto findLastSplit(T)(scope return inout(T)[] haystack, 897 scope const(T)[] needle) 898 { 899 static struct Result // NOTE `static` qualifier is needed for `inout` to propagate correctly 900 { 901 private T[] _haystack; 902 private size_t _offset; // hit offset 903 private size_t _length; // hit length 904 pragma(inline, true): 905 inout(T)[] pre() @trusted inout => _haystack.ptr[0 .. _offset]; 906 inout(T)[] separator() @trusted inout => _haystack.ptr[_offset .. _offset + _length]; 907 inout(T)[] post() @trusted inout => _haystack.ptr[_offset + _length .. _haystack.length]; 908 bool opCast(T : bool)() @safe const => _haystack.length != _offset; 909 } 910 911 assert(needle.length, "Cannot find occurrence of an empty range"); 912 const index = haystack.lastIndexOf(needle); 913 if (index >= 0) 914 return inout(Result)(haystack, index, needle.length); 915 return inout(Result)(haystack, haystack.length, 0); // miss 916 } 917 /// ditto 918 auto findLastSplit(T)(scope return inout(T)[] haystack, 919 scope const T needle) 920 { 921 static struct Result // NOTE `static` qualifier is needed for `inout` to propagate correctly 922 { 923 private T[] _haystack; 924 private size_t _offset; // hit offset 925 pragma(inline, true): 926 inout(T)[] pre() @trusted inout => _haystack.ptr[0 .. _offset]; 927 inout(T)[] separator() @trusted inout => !empty ? _haystack.ptr[_offset .. _offset + 1] : _haystack[$ .. $]; 928 inout(T)[] post() @trusted inout => !empty ? _haystack.ptr[_offset + 1 .. _haystack.length] : _haystack[$ .. $]; 929 bool opCast(T : bool)() const => !empty; 930 private bool empty() const @property => _haystack.length == _offset; 931 } 932 933 static if (is(T == char)) 934 assert(needle < 128); // See_Also: https://forum.dlang.org/post/sjirukypxmmcgdmqbcpe@forum.dlang.org 935 const index = haystack.lastIndexOf(needle); 936 if (index >= 0) 937 return inout(Result)(haystack, index); 938 return inout(Result)(haystack, haystack.length); 939 } 940 941 /// 942 @safe pure nothrow @nogc unittest 943 { 944 const h = "a**b**c"; 945 const r = h.findLastSplit("**"); 946 assert(r); 947 assert(r.pre is h[0 .. 4]); 948 assert(r.separator is h[4 .. 6]); 949 assert(r.post is h[6 .. 7]); 950 951 auto f()() @safe pure nothrow { char[1] x = "_"; return x[].findLastSplit(" "); } 952 static if (hasPreviewDIP1000) static assert(!__traits(compiles, { auto _ = f(); })); 953 } 954 955 /// 956 @safe pure nothrow @nogc unittest 957 { 958 const h = "a**b**c"; 959 const r = h.findLastSplit("_"); 960 static assert(r.sizeof == 2 * 2 * size_t.sizeof); 961 assert(!r); 962 assert(r.pre is h); 963 assert(r.separator is h[$ .. $]); 964 assert(r.post is h[$ .. $]); 965 } 966 967 /// 968 @safe pure nothrow @nogc unittest 969 { 970 const r = "a*b*c".findLastSplit('*'); 971 static assert(r.sizeof == 3 * size_t.sizeof); 972 assert(r); 973 assert(r.pre == "a*b"); 974 assert(r.separator == "*"); 975 assert(r.post == "c"); 976 977 auto f()() @safe pure nothrow { char[1] x = "_"; return x[].findLastSplit(' '); } 978 static if (hasPreviewDIP1000) static assert(!__traits(compiles, { auto _ = f(); })); 979 } 980 981 /// DIP-1000 scope analysis 982 version(none) // TODO: enable 983 @safe pure nothrow @nogc unittest 984 { 985 char[] f() @safe pure nothrow 986 { 987 char[3] haystack = "a*b"; 988 auto r = haystack[].findLastSplit('*'); 989 static assert(is(typeof(r.pre()) == char[])); 990 return r.pre(); // TODO: this should fail 991 } 992 f(); 993 } 994 995 /** Array-specialization of `findSplitBefore` with default predicate. 996 * 997 * See_Also: https://forum.dlang.org/post/dhxwgtaubzbmjaqjmnmq@forum.dlang.org 998 * See_Also: https://forum.dlang.org/post/zhgajqdhybtbufeiiofp@forum.dlang.org 999 */ 1000 auto findSplitBefore(T)(scope return inout(T)[] haystack, 1001 scope const T needle) 1002 { 1003 static struct Result // NOTE `static` qualifier is needed for `inout` to propagate correctly 1004 { 1005 private T[] _haystack; 1006 private size_t _offset; 1007 pragma(inline, true): 1008 inout(T)[] pre() @trusted inout => _haystack.ptr[0 .. _offset]; 1009 inout(T)[] post() @trusted inout => !empty ? _haystack.ptr[_offset .. _haystack.length] : _haystack[$ .. $]; 1010 bool opCast(T : bool)() const => !empty; 1011 private bool empty() const @property => _haystack.length == _offset; 1012 } 1013 1014 static if (is(T == char)) 1015 assert(needle < 128); // See_Also: https://forum.dlang.org/post/sjirukypxmmcgdmqbcpe@forum.dlang.org 1016 foreach (const offset, const ref element; haystack) 1017 if (element == needle) 1018 return inout(Result)(haystack, offset); 1019 return inout(Result)(haystack, haystack.length); 1020 } 1021 1022 /// 1023 @safe pure nothrow @nogc unittest 1024 { 1025 char[] haystack; 1026 auto r = haystack.findSplitBefore('_'); 1027 static assert(is(typeof(r.pre()) == char[])); 1028 static assert(is(typeof(r.post()) == char[])); 1029 assert(!r); 1030 assert(!r.pre); 1031 assert(!r.post); 1032 1033 auto f()() @safe pure nothrow { char[1] x = "_"; return x[].findSplitBefore(' '); } 1034 static if (hasPreviewDIP1000) static assert(!__traits(compiles, { auto _ = f(); })); 1035 } 1036 1037 /// 1038 @safe pure nothrow @nogc unittest 1039 { 1040 const(char)[] haystack; 1041 auto r = haystack.findSplitBefore('_'); 1042 static assert(is(typeof(r.pre()) == const(char)[])); 1043 static assert(is(typeof(r.post()) == const(char)[])); 1044 assert(!r); 1045 assert(!r.pre); 1046 assert(!r.post); 1047 } 1048 1049 /// 1050 @safe pure nothrow @nogc unittest 1051 { 1052 const r = "a*b".findSplitBefore('*'); 1053 assert(r); 1054 assert(r.pre == "a"); 1055 assert(r.post == "*b"); 1056 } 1057 1058 /// 1059 @safe pure nothrow @nogc unittest 1060 { 1061 const r = "a*b".findSplitBefore('_'); 1062 assert(!r); 1063 assert(r.pre == "a*b"); 1064 assert(r.post == ""); 1065 } 1066 1067 /** Array-specialization of `findSplitBefore` with explicit needle-only predicate `needlePred`. 1068 * 1069 * See_Also: https://forum.dlang.org/post/dhxwgtaubzbmjaqjmnmq@forum.dlang.org 1070 * See_Also: https://forum.dlang.org/post/zhgajqdhybtbufeiiofp@forum.dlang.org 1071 */ 1072 auto findSplitBefore(alias needlePred, T)(scope return inout(T)[] haystack) 1073 { 1074 static struct Result // NOTE `static` qualifier is needed for `inout` to propagate correctly 1075 { 1076 private T[] _haystack; 1077 private size_t _offset; 1078 pragma(inline, true): 1079 inout(T)[] pre() @trusted inout => _haystack.ptr[0 .. _offset]; 1080 inout(T)[] post() @trusted inout => !empty ? _haystack.ptr[_offset .. _haystack.length] : _haystack[$ .. $]; 1081 bool opCast(T : bool)() const => !empty; 1082 private bool empty() const @property => _haystack.length == _offset; 1083 } 1084 1085 foreach (const offset, const ref element; haystack) 1086 if (needlePred(element)) 1087 return inout(Result)(haystack, offset); 1088 return inout(Result)(haystack, haystack.length); 1089 } 1090 1091 /// 1092 @safe pure nothrow @nogc unittest 1093 { 1094 char[] haystack; 1095 auto r = haystack.findSplitBefore!(_ => _ == '_'); 1096 static assert(is(typeof(r.pre()) == char[])); 1097 static assert(is(typeof(r.post()) == char[])); 1098 assert(!r); 1099 assert(!r.pre); 1100 assert(!r.post); 1101 1102 auto f()() @safe pure nothrow { char[1] x = "_"; return x[].findSplitBefore!(_ => _ == ' '); } 1103 static if (hasPreviewDIP1000) static assert(!__traits(compiles, { auto _ = f(); })); 1104 } 1105 1106 /// 1107 @safe pure nothrow @nogc unittest 1108 { 1109 const(char)[] haystack; 1110 auto r = haystack.findSplitBefore!(_ => _ == '_'); 1111 static assert(is(typeof(r.pre()) == const(char)[])); 1112 static assert(is(typeof(r.post()) == const(char)[])); 1113 assert(!r); 1114 assert(!r.pre); 1115 assert(!r.post); 1116 } 1117 1118 /// 1119 @safe pure nothrow @nogc unittest 1120 { 1121 const r = "a*b".findSplitBefore!(_ => _ == '*'); 1122 assert(r); 1123 assert(r.pre == "a"); 1124 assert(r.post == "*b"); 1125 } 1126 1127 /// 1128 @safe pure nothrow @nogc unittest 1129 { 1130 const r = "a*b".findSplitBefore!(_ => _ == '*' || _ == '+'); 1131 assert(r); 1132 assert(r.pre == "a"); 1133 assert(r.post == "*b"); 1134 } 1135 1136 /// 1137 @safe pure nothrow @nogc unittest 1138 { 1139 const r = "a+b".findSplitBefore!(_ => _ == '*' || _ == '+'); 1140 assert(r); 1141 assert(r.pre == "a"); 1142 assert(r.post == "+b"); 1143 } 1144 1145 /// 1146 @safe pure nothrow @nogc unittest 1147 { 1148 const r = "a*b".findSplitBefore!(_ => _ == '_'); 1149 assert(!r); 1150 assert(r.pre == "a*b"); 1151 assert(r.post == ""); 1152 } 1153 1154 /** Array-specialization of `findSplitAfter` with default predicate. 1155 * 1156 * See_Also: https://forum.dlang.org/post/dhxwgtaubzbmjaqjmnmq@forum.dlang.org 1157 * See_Also: https://forum.dlang.org/post/zhgajqdhybtbufeiiofp@forum.dlang.org 1158 */ 1159 auto findSplitAfter(T)(scope return inout(T)[] haystack, 1160 scope const T needle) @trusted 1161 { 1162 static struct Result // NOTE `static` qualifier is needed for `inout` to propagate correctly 1163 { 1164 private T[] _haystack; 1165 private size_t _offset; 1166 pragma(inline, true): 1167 inout(T)[] pre() @trusted inout => !empty ? _haystack.ptr[0 .. _offset + 1] : _haystack[$ .. $]; 1168 inout(T)[] post() @trusted inout => !empty ? _haystack.ptr[_offset + 1 .. _haystack.length] : _haystack[0 .. $]; 1169 bool opCast(T : bool)() const => !empty; 1170 private bool empty() const @property => _haystack.length == _offset; 1171 } 1172 1173 static if (is(T == char)) 1174 assert(needle < 128); // See_Also: https://forum.dlang.org/post/sjirukypxmmcgdmqbcpe@forum.dlang.org 1175 foreach (const offset, const ref element; haystack) 1176 if (element == needle) 1177 return inout(Result)(haystack, offset); 1178 return inout(Result)(haystack, haystack.length); 1179 } 1180 1181 /// 1182 @safe pure nothrow @nogc unittest 1183 { 1184 char[] haystack; 1185 auto r = haystack.findSplitAfter('_'); 1186 static assert(is(typeof(r.pre()) == char[])); 1187 static assert(is(typeof(r.post()) == char[])); 1188 assert(!r); 1189 assert(!r.pre); 1190 assert(!r.post); 1191 1192 auto f()() @safe pure nothrow { char[1] x = "_"; return x[].findSplitAfter(' '); } 1193 static if (hasPreviewDIP1000) static assert(!__traits(compiles, { auto _ = f(); })); 1194 } 1195 1196 /// 1197 @safe pure nothrow @nogc unittest 1198 { 1199 const(char)[] haystack; 1200 auto r = haystack.findSplitAfter('_'); 1201 static assert(is(typeof(r.pre()) == const(char)[])); 1202 static assert(is(typeof(r.post()) == const(char)[])); 1203 assert(!r); 1204 assert(!r.pre); 1205 assert(!r.post); 1206 } 1207 1208 /// 1209 @safe pure nothrow @nogc unittest 1210 { 1211 auto r = "a*b".findSplitAfter('*'); 1212 static assert(is(typeof(r.pre()) == string)); 1213 static assert(is(typeof(r.post()) == string)); 1214 assert(r); 1215 assert(r.pre == "a*"); 1216 assert(r.post == "b"); 1217 } 1218 1219 /** Array-specialization of `findLastSplitAfter` with default predicate. 1220 * 1221 * See_Also: https://forum.dlang.org/post/dhxwgtaubzbmjaqjmnmq@forum.dlang.org 1222 * See_Also: https://forum.dlang.org/post/zhgajqdhybtbufeiiofp@forum.dlang.org 1223 */ 1224 auto findLastSplitAfter(T)(scope return inout(T)[] haystack, 1225 scope const T needle) @trusted 1226 { 1227 static struct Result // NOTE `static` qualifier is needed for `inout` to propagate correctly 1228 { 1229 private T[] _haystack; 1230 private size_t _offset; 1231 pragma(inline, true): 1232 inout(T)[] pre() @trusted inout => !empty ? _haystack.ptr[0 .. _offset + 1] : _haystack[$ .. $]; 1233 inout(T)[] post() @trusted inout => !empty ? _haystack.ptr[_offset + 1 .. _haystack.length] : _haystack[0 .. $]; 1234 bool opCast(T : bool)() const => !empty; 1235 private bool empty() const @property => _haystack.length == _offset; 1236 } 1237 1238 static if (is(T == char)) 1239 assert(needle < 128); // See_Also: https://forum.dlang.org/post/sjirukypxmmcgdmqbcpe@forum.dlang.org 1240 const index = haystack.lastIndexOf(needle); 1241 if (index >= 0) 1242 return inout(Result)(haystack, index); 1243 return inout(Result)(haystack, haystack.length); // miss 1244 } 1245 1246 /// 1247 @safe pure nothrow @nogc unittest 1248 { 1249 char[] haystack; 1250 auto r = haystack.findLastSplitAfter('_'); 1251 static assert(is(typeof(r.pre()) == char[])); 1252 static assert(is(typeof(r.post()) == char[])); 1253 assert(!r); 1254 assert(!r.pre); 1255 assert(!r.post); 1256 1257 auto f()() @safe pure nothrow { char[1] x = "_"; return x[].findLastSplitAfter(' '); } 1258 static if (hasPreviewDIP1000) static assert(!__traits(compiles, { auto _ = f(); })); 1259 } 1260 1261 /// 1262 @safe pure nothrow @nogc unittest 1263 { 1264 const(char)[] haystack; 1265 auto r = haystack.findLastSplitAfter('_'); 1266 static assert(is(typeof(r.pre()) == const(char)[])); 1267 static assert(is(typeof(r.post()) == const(char)[])); 1268 assert(!r); 1269 assert(!r.pre); 1270 assert(!r.post); 1271 } 1272 1273 /// 1274 @safe pure nothrow @nogc unittest 1275 { 1276 auto r = "a*b*c".findLastSplitAfter('*'); 1277 static assert(is(typeof(r.pre()) == string)); 1278 static assert(is(typeof(r.post()) == string)); 1279 assert(r); 1280 assert(r.pre == "a*b*"); 1281 assert(r.post == "c"); 1282 } 1283 1284 import std.range.primitives : isInputRange, isInfinite, ElementType; 1285 1286 // Use until `std.algorithm.comparison.equal` supports uncopyable parameters. 1287 bool equal(T, U)(scope T a, scope U b) 1288 if (!(isInfinite!T && 1289 isInfinite!U)) 1290 { 1291 static if ((is(T == TE[m], TE, size_t m)) && 1292 (is(U == UE[n], UE, size_t n)) && 1293 is(typeof(TE.init == UE.init) : bool)) // static array 1294 { 1295 static if (m != n) 1296 return false; 1297 else 1298 { 1299 foreach (const i; 0 .. m) 1300 if (a[i] != b[i]) 1301 return false; 1302 return true; 1303 } 1304 } 1305 else static if ((is(T == TA[], TA)) && 1306 (is(U == UA[], UA)) && 1307 is(typeof(TA.init == UA.init) : bool)) // dynamic array 1308 { 1309 if (a.length != b.length) 1310 return false; 1311 const n = a.length; 1312 foreach (const i; 0 .. n) 1313 if (a[i] != b[i]) 1314 return false; 1315 return true; 1316 } 1317 else static if (is(typeof(ElementType!T.init == ElementType!U.init) : bool)) 1318 { 1319 static if (is(typeof(a[size_t.init]))) 1320 { 1321 import std.algorithm.mutation : move; 1322 size_t i = 0; 1323 foreach (const ref be; move(b)) 1324 if (a[i++] != be) 1325 return false; 1326 return true; 1327 } 1328 else static if (is(typeof(b[size_t.init]))) 1329 { 1330 import std.algorithm.mutation : move; 1331 size_t i = 0; 1332 foreach (const ref ae; move(a)) 1333 if (ae != b[i++]) 1334 return false; 1335 return true; 1336 } 1337 else 1338 { 1339 while (true) 1340 { 1341 if (a.empty()) 1342 return b.empty(); 1343 if (b.empty()) 1344 return a.empty(); 1345 if (a.front != b.front) 1346 return false; 1347 a.popFront(); 1348 b.popFront(); 1349 } 1350 } 1351 } 1352 else 1353 static assert(0, "Cannot compare " ~ T.stringof ~ "with" ~ U.stringof); 1354 } 1355 /// ditto 1356 bool equal(T, U)(scope const(T)[] a, scope const(U)[] b) 1357 if (is(typeof(T.init == U.init) : bool)) 1358 { 1359 } 1360 1361 /// dynamic arrays 1362 @safe pure nothrow @nogc unittest 1363 { 1364 assert(!equal([1, 2 ].s[], [1, 2, 3].s[])); 1365 assert(!equal([1, 2, 3].s[], [1, 2, ].s[])); 1366 assert( equal([1, 2, 3].s[], [1, 2, 3].s[])); 1367 } 1368 1369 /// static arrays 1370 @safe pure nothrow @nogc unittest 1371 { 1372 assert(!equal([1, 2 ].s, [1, 2, 3].s)); 1373 assert(!equal([1, 2, 3].s, [1, 2, ].s)); 1374 assert( equal([1, 2, 3].s, [1, 2, 3].s)); 1375 } 1376 1377 version(unittest) 1378 { 1379 import nxt.dip_traits : hasPreviewDIP1000; 1380 import nxt.array_help : s; 1381 } 1382 1383 // See_Also: https://dlang.org/spec/betterc.html#unittests 1384 version(unittestAsBetterC) 1385 extern(C) void main() 1386 { 1387 static foreach (u; __traits(getUnitTests, __traits(parent, main))) 1388 { 1389 u(); 1390 } 1391 }