1 // Written in the D programming language. 2 /** 3 This is a submodule of $(MREF std, algorithm). 4 It contains generic _comparison algorithms. 5 6 $(SCRIPT inhibitQuickIndex = 1;) 7 $(BOOKTABLE Cheat Sheet, 8 $(TR $(TH Function Name) $(TH Description)) 9 $(T2 among, 10 Checks if a value is among a set of values, e.g. 11 $(D if (v.among(1, 2, 3)) // `v` is 1, 2 or 3)) 12 $(T2 castSwitch, 13 $(D (new A()).castSwitch((A a)=>1,(B b)=>2)) returns $(D 1).) 14 $(T2 clamp, 15 $(D clamp(1, 3, 6)) returns $(D 3). $(D clamp(4, 3, 6)) returns $(D 4).) 16 $(T2 cmp, 17 $(D cmp("abc", "abcd")) is $(D -1), $(D cmp("abc", "aba")) is $(D 1), 18 and $(D cmp("abc", "abc")) is $(D 0).) 19 $(T2 either, 20 Return first parameter $(D p) that passes an $(D if (p)) test, e.g. 21 $(D either(0, 42, 43)) returns $(D 42).) 22 $(T2 equal, 23 Compares ranges for element-by-element equality, e.g. 24 $(D equal([1, 2, 3], [1.0, 2.0, 3.0])) returns $(D true).) 25 $(T2 isPermutation, 26 $(D isPermutation([1, 2], [2, 1])) returns $(D true).) 27 $(T2 isSameLength, 28 $(D isSameLength([1, 2, 3], [4, 5, 6])) returns $(D true).) 29 $(T2 levenshteinDistance, 30 $(D levenshteinDistance("kitten", "sitting")) returns $(D 3) by using 31 the $(LINK2 https://en.wikipedia.org/wiki/Levenshtein_distance, 32 Levenshtein distance _algorithm).) 33 $(T2 levenshteinDistanceAndPath, 34 $(D levenshteinDistanceAndPath("kitten", "sitting")) returns 35 $(D tuple(3, "snnnsni")) by using the 36 $(LINK2 https://en.wikipedia.org/wiki/Levenshtein_distance, 37 Levenshtein distance _algorithm).) 38 $(T2 max, 39 $(D max(3, 4, 2)) returns $(D 4).) 40 $(T2 min, 41 $(D min(3, 4, 2)) returns $(D 2).) 42 $(T2 mismatch, 43 $(D mismatch("oh hi", "ohayo")) returns $(D tuple(" hi", "ayo")).) 44 $(T2 predSwitch, 45 $(D 2.predSwitch(1, "one", 2, "two", 3, "three")) returns $(D "two").) 46 ) 47 48 Copyright: Andrei Alexandrescu 2008-. 49 50 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). 51 52 Authors: $(HTTP erdani.com, Andrei Alexandrescu) 53 54 Source: $(PHOBOSSRC std/algorithm/_comparison.d) 55 56 Macros: 57 T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) 58 */ 59 module std.algorithm.comparison; 60 61 // FIXME 62 import std.functional; // : unaryFun, binaryFun; 63 import std.range.primitives; 64 import std.traits; 65 // FIXME 66 import std.meta : allSatisfy; 67 import std.typecons; // : tuple, Tuple, Flag, Yes; 68 69 /** 70 Find $(D value) _among $(D values), returning the 1-based index 71 of the first matching value in $(D values), or $(D 0) if $(D value) 72 is not _among $(D values). The predicate $(D pred) is used to 73 compare values, and uses equality by default. 74 75 Params: 76 pred = The predicate used to compare the values. 77 value = The value to search for. 78 values = The values to compare the value to. 79 80 Returns: 81 0 if value was not found among the values, otherwise the index of the 82 found value plus one is returned. 83 84 See_Also: 85 $(REF_ALTTEXT find, find, std,algorithm,searching) and $(REF_ALTTEXT canFind, canFind, std,algorithm,searching) for finding a value in a 86 range. 87 */ 88 uint among(alias pred = (a, b) => a == b, Value, Values...) 89 (Value value, Values values) 90 if (Values.length != 0) 91 { 92 foreach (uint i, ref v; values) 93 { 94 import std.functional : binaryFun; 95 if (binaryFun!pred(value, v)) return i + 1; 96 } 97 return 0; 98 } 99 100 /// Ditto 101 template among(values...) 102 if (isExpressionTuple!values) 103 { 104 uint among(Value)(Value value) 105 if (!is(CommonType!(Value, values) == void)) 106 { 107 switch (value) 108 { 109 foreach (uint i, v; values) 110 case v: 111 return i + 1; 112 default: 113 return 0; 114 } 115 } 116 } 117 118 /// 119 @safe unittest 120 { 121 assert(3.among(1, 42, 24, 3, 2)); 122 123 if (auto pos = "bar".among("foo", "bar", "baz")) 124 assert(pos == 2); 125 else 126 assert(false); 127 128 // 42 is larger than 24 129 assert(42.among!((lhs, rhs) => lhs > rhs)(43, 24, 100) == 2); 130 } 131 132 /** 133 Alternatively, $(D values) can be passed at compile-time, allowing for a more 134 efficient search, but one that only supports matching on equality: 135 */ 136 @safe unittest 137 { 138 assert(3.among!(2, 3, 4)); 139 assert("bar".among!("foo", "bar", "baz") == 2); 140 } 141 142 @safe unittest 143 { 144 import std.meta : AliasSeq; 145 146 if (auto pos = 3.among(1, 2, 3)) 147 assert(pos == 3); 148 else 149 assert(false); 150 assert(!4.among(1, 2, 3)); 151 152 auto position = "hello".among("hello", "world"); 153 assert(position); 154 assert(position == 1); 155 156 alias values = AliasSeq!("foo", "bar", "baz"); 157 auto arr = [values]; 158 assert(arr[0 .. "foo".among(values)] == ["foo"]); 159 assert(arr[0 .. "bar".among(values)] == ["foo", "bar"]); 160 assert(arr[0 .. "baz".among(values)] == arr); 161 assert("foobar".among(values) == 0); 162 163 if (auto pos = 3.among!(1, 2, 3)) 164 assert(pos == 3); 165 else 166 assert(false); 167 assert(!4.among!(1, 2, 3)); 168 169 position = "hello".among!("hello", "world"); 170 assert(position); 171 assert(position == 1); 172 173 static assert(!__traits(compiles, "a".among!("a", 42))); 174 static assert(!__traits(compiles, (Object.init).among!(42, "a"))); 175 } 176 177 // Used in castSwitch to find the first choice that overshadows the last choice 178 // in a tuple. 179 private template indexOfFirstOvershadowingChoiceOnLast(choices...) 180 { 181 alias firstParameterTypes = Parameters!(choices[0]); 182 alias lastParameterTypes = Parameters!(choices[$ - 1]); 183 184 static if (lastParameterTypes.length == 0) 185 { 186 // If the last is null-typed choice, check if the first is null-typed. 187 enum isOvershadowing = firstParameterTypes.length == 0; 188 } 189 else static if (firstParameterTypes.length == 1) 190 { 191 // If the both first and last are not null-typed, check for overshadowing. 192 enum isOvershadowing = 193 is(firstParameterTypes[0] == Object) // Object overshadows all other classes!(this is needed for interfaces) 194 || is(lastParameterTypes[0] : firstParameterTypes[0]); 195 } 196 else 197 { 198 // If the first is null typed and the last is not - the is no overshadowing. 199 enum isOvershadowing = false; 200 } 201 202 static if (isOvershadowing) 203 { 204 enum indexOfFirstOvershadowingChoiceOnLast = 0; 205 } 206 else 207 { 208 enum indexOfFirstOvershadowingChoiceOnLast = 209 1 + indexOfFirstOvershadowingChoiceOnLast!(choices[1..$]); 210 } 211 } 212 213 /** 214 Executes and returns one of a collection of handlers based on the type of the 215 switch object. 216 217 The first choice that $(D switchObject) can be casted to the type 218 of argument it accepts will be called with $(D switchObject) casted to that 219 type, and the value it'll return will be returned by $(D castSwitch). 220 221 If a choice's return type is void, the choice must throw an exception, unless 222 all the choices are void. In that case, castSwitch itself will return void. 223 224 Throws: If none of the choice matches, a $(D SwitchError) will be thrown. $(D 225 SwitchError) will also be thrown if not all the choices are void and a void 226 choice was executed without throwing anything. 227 228 Params: 229 choices = The $(D choices) needs to be composed of function or delegate 230 handlers that accept one argument. There can also be a choice that 231 accepts zero arguments. That choice will be invoked if the $(D 232 switchObject) is null. 233 switchObject = the object against which the tests are being made. 234 235 Returns: 236 The value of the selected choice. 237 238 Note: $(D castSwitch) can only be used with object types. 239 */ 240 auto castSwitch(choices...)(Object switchObject) 241 { 242 import core.exception : SwitchError; 243 import std.format : format; 244 245 // Check to see if all handlers return void. 246 enum areAllHandlersVoidResult = { 247 bool result = true; 248 foreach (index, choice; choices) 249 { 250 result &= is(ReturnType!choice == void); 251 } 252 return result; 253 }(); 254 255 if (switchObject !is null) 256 { 257 258 // Checking for exact matches: 259 const classInfo = typeid(switchObject); 260 foreach (index, choice; choices) 261 { 262 static assert(isCallable!choice, 263 "A choice handler must be callable"); 264 265 alias choiceParameterTypes = Parameters!choice; 266 static assert(choiceParameterTypes.length <= 1, 267 "A choice handler can not have more than one argument."); 268 269 static if (choiceParameterTypes.length == 1) 270 { 271 alias CastClass = choiceParameterTypes[0]; 272 static assert(is(CastClass == class) || is(CastClass == interface), 273 "A choice handler can have only class or interface typed argument."); 274 275 // Check for overshadowing: 276 immutable indexOfOvershadowingChoice = 277 indexOfFirstOvershadowingChoiceOnLast!(choices[0 .. index + 1]); 278 static assert(indexOfOvershadowingChoice == index, 279 "choice number %d(type %s) is overshadowed by choice number %d(type %s)".format( 280 index + 1, CastClass.stringof, indexOfOvershadowingChoice + 1, 281 Parameters!(choices[indexOfOvershadowingChoice])[0].stringof)); 282 283 if (classInfo == typeid(CastClass)) 284 { 285 static if (is(ReturnType!(choice) == void)) 286 { 287 choice(cast(CastClass) switchObject); 288 static if (areAllHandlersVoidResult) 289 { 290 return; 291 } 292 else 293 { 294 throw new SwitchError("Handlers that return void should throw"); 295 } 296 } 297 else 298 { 299 return choice(cast(CastClass) switchObject); 300 } 301 } 302 } 303 } 304 305 // Checking for derived matches: 306 foreach (choice; choices) 307 { 308 alias choiceParameterTypes = Parameters!choice; 309 static if (choiceParameterTypes.length == 1) 310 { 311 if (auto castedObject = cast(choiceParameterTypes[0]) switchObject) 312 { 313 static if (is(ReturnType!(choice) == void)) 314 { 315 choice(castedObject); 316 static if (areAllHandlersVoidResult) 317 { 318 return; 319 } 320 else 321 { 322 throw new SwitchError("Handlers that return void should throw"); 323 } 324 } 325 else 326 { 327 return choice(castedObject); 328 } 329 } 330 } 331 } 332 } 333 else // If switchObject is null: 334 { 335 // Checking for null matches: 336 foreach (index, choice; choices) 337 { 338 static if (Parameters!(choice).length == 0) 339 { 340 immutable indexOfOvershadowingChoice = 341 indexOfFirstOvershadowingChoiceOnLast!(choices[0 .. index + 1]); 342 343 // Check for overshadowing: 344 static assert(indexOfOvershadowingChoice == index, 345 "choice number %d(null reference) is overshadowed by choice number %d(null reference)".format( 346 index + 1, indexOfOvershadowingChoice + 1)); 347 348 if (switchObject is null) 349 { 350 static if (is(ReturnType!(choice) == void)) 351 { 352 choice(); 353 static if (areAllHandlersVoidResult) 354 { 355 return; 356 } 357 else 358 { 359 throw new SwitchError("Handlers that return void should throw"); 360 } 361 } 362 else 363 { 364 return choice(); 365 } 366 } 367 } 368 } 369 } 370 371 // In case nothing matched: 372 throw new SwitchError("Input not matched by any choice"); 373 } 374 375 /// 376 @system unittest 377 { 378 import std.algorithm.iteration : map; 379 import std.format : format; 380 381 class A 382 { 383 int a; 384 this(int a) {this.a = a;} 385 @property int i() { return a; } 386 } 387 interface I { } 388 class B : I { } 389 390 Object[] arr = [new A(1), new B(), null]; 391 392 auto results = arr.map!(castSwitch!( 393 (A a) => "A with a value of %d".format(a.a), 394 (I i) => "derived from I", 395 () => "null reference", 396 ))(); 397 398 // A is handled directly: 399 assert(results[0] == "A with a value of 1"); 400 // B has no handler - it is handled by the handler of I: 401 assert(results[1] == "derived from I"); 402 // null is handled by the null handler: 403 assert(results[2] == "null reference"); 404 } 405 406 /// Using with void handlers: 407 @system unittest 408 { 409 import std.exception : assertThrown; 410 411 class A { } 412 class B { } 413 // Void handlers are allowed if they throw: 414 assertThrown!Exception( 415 new B().castSwitch!( 416 (A a) => 1, 417 (B d) { throw new Exception("B is not allowed!"); } 418 )() 419 ); 420 421 // Void handlers are also allowed if all the handlers are void: 422 new A().castSwitch!( 423 (A a) { }, 424 (B b) { assert(false); }, 425 )(); 426 } 427 428 @system unittest 429 { 430 import core.exception : SwitchError; 431 import std.exception : assertThrown; 432 433 interface I { } 434 class A : I { } 435 class B { } 436 437 // Nothing matches: 438 assertThrown!SwitchError((new A()).castSwitch!( 439 (B b) => 1, 440 () => 2, 441 )()); 442 443 // Choices with multiple arguments are not allowed: 444 static assert(!__traits(compiles, 445 (new A()).castSwitch!( 446 (A a, B b) => 0, 447 )())); 448 449 // Only callable handlers allowed: 450 static assert(!__traits(compiles, 451 (new A()).castSwitch!( 452 1234, 453 )())); 454 455 // Only object arguments allowed: 456 static assert(!__traits(compiles, 457 (new A()).castSwitch!( 458 (int x) => 0, 459 )())); 460 461 // Object overshadows regular classes: 462 static assert(!__traits(compiles, 463 (new A()).castSwitch!( 464 (Object o) => 0, 465 (A a) => 1, 466 )())); 467 468 // Object overshadows interfaces: 469 static assert(!__traits(compiles, 470 (new A()).castSwitch!( 471 (Object o) => 0, 472 (I i) => 1, 473 )())); 474 475 // No multiple null handlers allowed: 476 static assert(!__traits(compiles, 477 (new A()).castSwitch!( 478 () => 0, 479 () => 1, 480 )())); 481 482 // No non-throwing void handlers allowed(when there are non-void handlers): 483 assertThrown!SwitchError((new A()).castSwitch!( 484 (A a) {}, 485 (B b) => 2, 486 )()); 487 488 // All-void handlers work for the null case: 489 null.castSwitch!( 490 (Object o) { assert(false); }, 491 () { }, 492 )(); 493 494 // Throwing void handlers work for the null case: 495 assertThrown!Exception(null.castSwitch!( 496 (Object o) => 1, 497 () { throw new Exception("null"); }, 498 )()); 499 } 500 501 @system unittest 502 { 503 interface I { } 504 class B : I { } 505 class C : I { } 506 507 assert((new B()).castSwitch!( 508 (B b) => "class B", 509 (I i) => "derived from I", 510 ) == "class B"); 511 512 assert((new C()).castSwitch!( 513 (B b) => "class B", 514 (I i) => "derived from I", 515 ) == "derived from I"); 516 } 517 518 /** Clamps a value into the given bounds. 519 520 This functions is equivalent to $(D max(lower, min(upper,val))). 521 522 Params: 523 val = The value to _clamp. 524 lower = The _lower bound of the _clamp. 525 upper = The _upper bound of the _clamp. 526 527 Returns: 528 Returns $(D val), if it is between $(D lower) and $(D upper). 529 Otherwise returns the nearest of the two. 530 531 */ 532 auto clamp(T1, T2, T3)(T1 val, T2 lower, T3 upper) 533 in 534 { 535 import std.functional : greaterThan; 536 assert(!lower.greaterThan(upper)); 537 } 538 body 539 { 540 return max(lower, min(upper, val)); 541 } 542 543 /// 544 @safe unittest 545 { 546 assert(clamp(2, 1, 3) == 2); 547 assert(clamp(0, 1, 3) == 1); 548 assert(clamp(4, 1, 3) == 3); 549 550 assert(clamp(1, 1, 1) == 1); 551 552 assert(clamp(5, -1, 2u) == 2); 553 } 554 555 @safe unittest 556 { 557 int a = 1; 558 short b = 6; 559 double c = 2; 560 static assert(is(typeof(clamp(c,a,b)) == double)); 561 assert(clamp(c, a, b) == c); 562 assert(clamp(a-c, a, b) == a); 563 assert(clamp(b+c, a, b) == b); 564 // mixed sign 565 a = -5; 566 uint f = 5; 567 static assert(is(typeof(clamp(f, a, b)) == int)); 568 assert(clamp(f, a, b) == f); 569 // similar type deduction for (u)long 570 static assert(is(typeof(clamp(-1L, -2L, 2UL)) == long)); 571 572 // user-defined types 573 import std.datetime : Date; 574 assert(clamp(Date(1982, 1, 4), Date(1012, 12, 21), Date(2012, 12, 21)) == Date(1982, 1, 4)); 575 assert(clamp(Date(1982, 1, 4), Date.min, Date.max) == Date(1982, 1, 4)); 576 // UFCS style 577 assert(Date(1982, 1, 4).clamp(Date.min, Date.max) == Date(1982, 1, 4)); 578 579 } 580 581 // cmp 582 /********************************** 583 Performs three-way lexicographical comparison on two 584 $(REF_ALTTEXT input ranges, isInputRange, std,range,primitives) 585 according to predicate $(D pred). Iterating $(D r1) and $(D r2) in 586 lockstep, $(D cmp) compares each element $(D e1) of $(D r1) with the 587 corresponding element $(D e2) in $(D r2). If one of the ranges has been 588 finished, $(D cmp) returns a negative value if $(D r1) has fewer 589 elements than $(D r2), a positive value if $(D r1) has more elements 590 than $(D r2), and $(D 0) if the ranges have the same number of 591 elements. 592 593 If the ranges are strings, $(D cmp) performs UTF decoding 594 appropriately and compares the ranges one code point at a time. 595 596 Params: 597 pred = The predicate used for comparison. 598 r1 = The first range. 599 r2 = The second range. 600 601 Returns: 602 0 if both ranges compare equal. -1 if the first differing element of $(D 603 r1) is less than the corresponding element of $(D r2) according to $(D 604 pred). 1 if the first differing element of $(D r2) is less than the 605 corresponding element of $(D r1) according to $(D pred). 606 607 */ 608 int cmp(alias pred = "a < b", R1, R2)(R1 r1, R2 r2) 609 if (isInputRange!R1 && isInputRange!R2 && !(isSomeString!R1 && isSomeString!R2)) 610 { 611 for (;; r1.popFront(), r2.popFront()) 612 { 613 if (r1.empty) return -cast(int)!r2.empty; 614 if (r2.empty) return !r1.empty; 615 auto a = r1.front, b = r2.front; 616 if (binaryFun!pred(a, b)) return -1; 617 if (binaryFun!pred(b, a)) return 1; 618 } 619 } 620 621 /// ditto 622 int cmp(alias pred = "a < b", R1, R2)(R1 r1, R2 r2) 623 if (isSomeString!R1 && isSomeString!R2) 624 { 625 import core.stdc.string : memcmp; 626 import std.utf : decode; 627 628 static if (is(typeof(pred) : string)) 629 enum isLessThan = pred == "a < b"; 630 else 631 enum isLessThan = false; 632 633 // For speed only 634 static int threeWay(size_t a, size_t b) 635 { 636 static if (size_t.sizeof == int.sizeof && isLessThan) 637 return a - b; 638 else 639 return binaryFun!pred(b, a) ? 1 : binaryFun!pred(a, b) ? -1 : 0; 640 } 641 // For speed only 642 // @@@BUG@@@ overloading should be allowed for nested functions 643 static int threeWayInt(int a, int b) 644 { 645 static if (isLessThan) 646 return a - b; 647 else 648 return binaryFun!pred(b, a) ? 1 : binaryFun!pred(a, b) ? -1 : 0; 649 } 650 651 static if (typeof(r1[0]).sizeof == typeof(r2[0]).sizeof && isLessThan) 652 { 653 static if (typeof(r1[0]).sizeof == 1) 654 { 655 immutable len = min(r1.length, r2.length); 656 immutable result = __ctfe ? 657 { 658 foreach (i; 0 .. len) 659 { 660 if (r1[i] != r2[i]) 661 return threeWayInt(r1[i], r2[i]); 662 } 663 return 0; 664 }() 665 : () @trusted { return memcmp(r1.ptr, r2.ptr, len); }(); 666 if (result) return result; 667 } 668 else 669 { 670 auto p1 = r1.ptr, p2 = r2.ptr, 671 pEnd = p1 + min(r1.length, r2.length); 672 for (; p1 != pEnd; ++p1, ++p2) 673 { 674 if (*p1 != *p2) return threeWayInt(cast(int) *p1, cast(int) *p2); 675 } 676 } 677 return threeWay(r1.length, r2.length); 678 } 679 else 680 { 681 for (size_t i1, i2;;) 682 { 683 if (i1 == r1.length) return threeWay(i2, r2.length); 684 if (i2 == r2.length) return threeWay(r1.length, i1); 685 immutable c1 = decode(r1, i1), 686 c2 = decode(r2, i2); 687 if (c1 != c2) return threeWayInt(cast(int) c1, cast(int) c2); 688 } 689 } 690 } 691 692 /// 693 @safe unittest 694 { 695 int result; 696 697 result = cmp("abc", "abc"); 698 assert(result == 0); 699 result = cmp("", ""); 700 assert(result == 0); 701 result = cmp("abc", "abcd"); 702 assert(result < 0); 703 result = cmp("abcd", "abc"); 704 assert(result > 0); 705 result = cmp("abc"d, "abd"); 706 assert(result < 0); 707 result = cmp("bbc", "abc"w); 708 assert(result > 0); 709 result = cmp("aaa", "aaaa"d); 710 assert(result < 0); 711 result = cmp("aaaa", "aaa"d); 712 assert(result > 0); 713 result = cmp("aaa", "aaa"d); 714 assert(result == 0); 715 result = cmp(cast(int[])[], cast(int[])[]); 716 assert(result == 0); 717 result = cmp([1, 2, 3], [1, 2, 3]); 718 assert(result == 0); 719 result = cmp([1, 3, 2], [1, 2, 3]); 720 assert(result > 0); 721 result = cmp([1, 2, 3], [1L, 2, 3, 4]); 722 assert(result < 0); 723 result = cmp([1L, 2, 3], [1, 2]); 724 assert(result > 0); 725 } 726 727 // equal 728 /** 729 Compares two ranges for equality, as defined by predicate $(D pred) 730 (which is $(D ==) by default). 731 */ 732 template equal(alias pred = "a == b") 733 { 734 enum isEmptyRange(R) = 735 isInputRange!R && __traits(compiles, {static assert(R.empty);}); 736 737 enum hasFixedLength(T) = hasLength!T || isNarrowString!T; 738 739 /++ 740 Compares two ranges for equality. The ranges may have 741 different element types, as long as $(D pred(r1.front, r2.front)) 742 evaluates to $(D bool). 743 Performs $(BIGOH min(r1.length, r2.length)) evaluations of $(D pred). 744 745 Params: 746 r1 = The first range to be compared. 747 r2 = The second range to be compared. 748 749 Returns: 750 $(D true) if and only if the two ranges compare _equal element 751 for element, according to binary predicate $(D pred). 752 753 See_Also: 754 $(HTTP sgi.com/tech/stl/_equal.html, STL's _equal) 755 +/ 756 bool equal(Range1, Range2)(Range1 r1, Range2 r2) 757 if (isInputRange!Range1 && isInputRange!Range2 && 758 is(typeof(binaryFun!pred(r1.front, r2.front)))) 759 { 760 static assert(!(isInfinite!Range1 && isInfinite!Range2), 761 "Both ranges are known to be infinite"); 762 763 //No pred calls necessary 764 static if (isEmptyRange!Range1 || isEmptyRange!Range2) 765 { 766 return r1.empty && r2.empty; 767 } 768 else static if ((isInfinite!Range1 && hasFixedLength!Range2) || 769 (hasFixedLength!Range1 && isInfinite!Range2)) 770 { 771 return false; 772 } 773 //Detect default pred and compatible dynamic array 774 else static if (is(typeof(pred) == string) && pred == "a == b" && 775 isArray!Range1 && isArray!Range2 && is(typeof(r1 == r2))) 776 { 777 return r1 == r2; 778 } 779 // if one of the arguments is a string and the other isn't, then auto-decoding 780 // can be avoided if they have the same ElementEncodingType 781 else static if (is(typeof(pred) == string) && pred == "a == b" && 782 isAutodecodableString!Range1 != isAutodecodableString!Range2 && 783 is(ElementEncodingType!Range1 == ElementEncodingType!Range2)) 784 { 785 import std.utf : byCodeUnit; 786 787 static if (isAutodecodableString!Range1) 788 { 789 return equal(r1.byCodeUnit, r2); 790 } 791 else 792 { 793 return equal(r2.byCodeUnit, r1); 794 } 795 } 796 //Try a fast implementation when the ranges have comparable lengths 797 else static if (hasLength!Range1 && hasLength!Range2 && is(typeof(r1.length == r2.length))) 798 { 799 immutable len1 = r1.length; 800 immutable len2 = r2.length; 801 if (len1 != len2) return false; //Short circuit return 802 803 //Lengths are the same, so we need to do an actual comparison 804 //Good news is we can squeeze out a bit of performance by not checking if r2 is empty 805 for (; !r1.empty; r1.popFront(), r2.popFront()) 806 { 807 if (!binaryFun!(pred)(r1.front, r2.front)) return false; 808 } 809 return true; 810 } 811 else 812 { 813 //Generic case, we have to walk both ranges making sure neither is empty 814 for (; !r1.empty; r1.popFront(), r2.popFront()) 815 { 816 if (r2.empty) return false; 817 if (!binaryFun!(pred)(r1.front, r2.front)) return false; 818 } 819 static if (!isInfinite!Range1) 820 return r2.empty; 821 } 822 } 823 } 824 825 /// 826 @safe unittest 827 { 828 import std.algorithm.comparison : equal; 829 import std.math : approxEqual; 830 831 int[] a = [ 1, 2, 4, 3 ]; 832 assert(!equal(a, a[1..$])); 833 assert(equal(a, a)); 834 assert(equal!((a, b) => a == b)(a, a)); 835 836 // different types 837 double[] b = [ 1.0, 2, 4, 3]; 838 assert(!equal(a, b[1..$])); 839 assert(equal(a, b)); 840 841 // predicated: ensure that two vectors are approximately equal 842 double[] c = [ 1.005, 2, 4, 3]; 843 assert(equal!approxEqual(b, c)); 844 } 845 846 /++ 847 Tip: $(D equal) can itself be used as a predicate to other functions. 848 This can be very useful when the element type of a range is itself a 849 range. In particular, $(D equal) can be its own predicate, allowing 850 range of range (of range...) comparisons. 851 +/ 852 @safe unittest 853 { 854 import std.algorithm.comparison : equal; 855 import std.range : iota, chunks; 856 assert(equal!(equal!equal)( 857 [[[0, 1], [2, 3]], [[4, 5], [6, 7]]], 858 iota(0, 8).chunks(2).chunks(2) 859 )); 860 } 861 862 @safe unittest 863 { 864 import std.algorithm.iteration : map; 865 import std.internal.test.dummyrange : ReferenceForwardRange, 866 ReferenceInputRange, ReferenceInfiniteForwardRange; 867 import std.math : approxEqual; 868 869 // various strings 870 assert(equal("æøå", "æøå")); //UTF8 vs UTF8 871 assert(!equal("???", "æøå")); //UTF8 vs UTF8 872 assert(equal("æøå"w, "æøå"d)); //UTF16 vs UTF32 873 assert(!equal("???"w, "æøå"d));//UTF16 vs UTF32 874 assert(equal("æøå"d, "æøå"d)); //UTF32 vs UTF32 875 assert(!equal("???"d, "æøå"d));//UTF32 vs UTF32 876 assert(!equal("hello", "world")); 877 878 // same strings, but "explicit non default" comparison (to test the non optimized array comparison) 879 assert( equal!("a == b")("æøå", "æøå")); //UTF8 vs UTF8 880 assert(!equal!("a == b")("???", "æøå")); //UTF8 vs UTF8 881 assert( equal!("a == b")("æøå"w, "æøå"d)); //UTF16 vs UTF32 882 assert(!equal!("a == b")("???"w, "æøå"d));//UTF16 vs UTF32 883 assert( equal!("a == b")("æøå"d, "æøå"d)); //UTF32 vs UTF32 884 assert(!equal!("a == b")("???"d, "æøå"d));//UTF32 vs UTF32 885 assert(!equal!("a == b")("hello", "world")); 886 887 //Array of string 888 assert(equal(["hello", "world"], ["hello", "world"])); 889 assert(!equal(["hello", "world"], ["hello"])); 890 assert(!equal(["hello", "world"], ["hello", "Bob!"])); 891 892 //Should not compile, because "string == dstring" is illegal 893 static assert(!is(typeof(equal(["hello", "world"], ["hello"d, "world"d])))); 894 //However, arrays of non-matching string can be compared using equal!equal. Neat-o! 895 equal!equal(["hello", "world"], ["hello"d, "world"d]); 896 897 //Tests, with more fancy map ranges 898 int[] a = [ 1, 2, 4, 3 ]; 899 assert(equal([2, 4, 8, 6], map!"a*2"(a))); 900 double[] b = [ 1.0, 2, 4, 3]; 901 double[] c = [ 1.005, 2, 4, 3]; 902 assert(equal!approxEqual(map!"a*2"(b), map!"a*2"(c))); 903 assert(!equal([2, 4, 1, 3], map!"a*2"(a))); 904 assert(!equal([2, 4, 1], map!"a*2"(a))); 905 assert(!equal!approxEqual(map!"a*3"(b), map!"a*2"(c))); 906 907 //Tests with some fancy reference ranges. 908 ReferenceInputRange!int cir = new ReferenceInputRange!int([1, 2, 4, 3]); 909 ReferenceForwardRange!int cfr = new ReferenceForwardRange!int([1, 2, 4, 3]); 910 assert(equal(cir, a)); 911 cir = new ReferenceInputRange!int([1, 2, 4, 3]); 912 assert(equal(cir, cfr.save)); 913 assert(equal(cfr.save, cfr.save)); 914 cir = new ReferenceInputRange!int([1, 2, 8, 1]); 915 assert(!equal(cir, cfr)); 916 917 //Test with an infinite range 918 auto ifr = new ReferenceInfiniteForwardRange!int; 919 assert(!equal(a, ifr)); 920 assert(!equal(ifr, a)); 921 //Test InputRange without length 922 assert(!equal(ifr, cir)); 923 assert(!equal(cir, ifr)); 924 } 925 926 @safe pure unittest 927 { 928 import std.utf : byChar, byWchar, byDchar; 929 930 assert(equal("æøå".byChar, "æøå")); 931 assert(equal("æøå", "æøå".byChar)); 932 assert(equal("æøå".byWchar, "æøå"w)); 933 assert(equal("æøå"w, "æøå".byWchar)); 934 assert(equal("æøå".byDchar, "æøå"d)); 935 assert(equal("æøå"d, "æøå".byDchar)); 936 } 937 938 @safe pure unittest 939 { 940 struct R(bool _empty) { 941 enum empty = _empty; 942 @property char front(){assert(0);} 943 void popFront(){assert(0);} 944 } 945 alias I = R!false; 946 static assert(!__traits(compiles, I().equal(I()))); 947 // strings have fixed length so don't need to compare elements 948 assert(!I().equal("foo")); 949 assert(!"bar".equal(I())); 950 951 alias E = R!true; 952 assert(E().equal(E())); 953 assert(E().equal("")); 954 assert("".equal(E())); 955 assert(!E().equal("foo")); 956 assert(!"bar".equal(E())); 957 } 958 959 // MaxType 960 private template MaxType(T...) 961 if (T.length >= 1) 962 { 963 static if (T.length == 1) 964 { 965 alias MaxType = T[0]; 966 } 967 else static if (T.length == 2) 968 { 969 static if (!is(typeof(T[0].min))) 970 alias MaxType = CommonType!T; 971 else static if (T[1].max > T[0].max) 972 alias MaxType = T[1]; 973 else 974 alias MaxType = T[0]; 975 } 976 else 977 { 978 alias MaxType = MaxType!(MaxType!(T[0 .. ($+1)/2]), MaxType!(T[($+1)/2 .. $])); 979 } 980 } 981 982 // levenshteinDistance 983 /** 984 Encodes $(HTTP realityinteractive.com/rgrzywinski/archives/000249.html, 985 edit operations) necessary to transform one sequence into 986 another. Given sequences $(D s) (source) and $(D t) (target), a 987 sequence of $(D EditOp) encodes the steps that need to be taken to 988 convert $(D s) into $(D t). For example, if $(D s = "cat") and $(D 989 "cars"), the minimal sequence that transforms $(D s) into $(D t) is: 990 skip two characters, replace 't' with 'r', and insert an 's'. Working 991 with edit operations is useful in applications such as spell-checkers 992 (to find the closest word to a given misspelled word), approximate 993 searches, diff-style programs that compute the difference between 994 files, efficient encoding of patches, DNA sequence analysis, and 995 plagiarism detection. 996 */ 997 998 enum EditOp : char 999 { 1000 /** Current items are equal; no editing is necessary. */ 1001 none = 'n', 1002 /** Substitute current item in target with current item in source. */ 1003 substitute = 's', 1004 /** Insert current item from the source into the target. */ 1005 insert = 'i', 1006 /** Remove current item from the target. */ 1007 remove = 'r' 1008 } 1009 1010 /// 1011 @safe unittest 1012 { 1013 with(EditOp) 1014 { 1015 assert(levenshteinDistanceAndPath("foo", "foobar")[1] == [none, none, none, insert, insert, insert]); 1016 assert(levenshteinDistanceAndPath("banana", "fazan")[1] == [substitute, none, substitute, none, none, remove]); 1017 } 1018 } 1019 1020 private struct Levenshtein(Range, alias equals, CostType = size_t) 1021 { 1022 EditOp[] path() 1023 { 1024 import std.algorithm.mutation : reverse; 1025 1026 EditOp[] result; 1027 size_t i = rows - 1, j = cols - 1; 1028 // restore the path 1029 while (i || j) 1030 { 1031 auto cIns = j == 0 ? CostType.max : matrix(i,j - 1); 1032 auto cDel = i == 0 ? CostType.max : matrix(i - 1,j); 1033 auto cSub = i == 0 || j == 0 1034 ? CostType.max 1035 : matrix(i - 1,j - 1); 1036 switch (min_index(cSub, cIns, cDel)) 1037 { 1038 case 0: 1039 result ~= matrix(i - 1,j - 1) == matrix(i,j) 1040 ? EditOp.none 1041 : EditOp.substitute; 1042 --i; 1043 --j; 1044 break; 1045 case 1: 1046 result ~= EditOp.insert; 1047 --j; 1048 break; 1049 default: 1050 result ~= EditOp.remove; 1051 --i; 1052 break; 1053 } 1054 } 1055 reverse(result); 1056 return result; 1057 } 1058 1059 ~this() { 1060 FreeMatrix(); 1061 } 1062 1063 private: 1064 CostType _deletionIncrement = 1, 1065 _insertionIncrement = 1, 1066 _substitutionIncrement = 1; 1067 CostType[] _matrix; 1068 size_t rows, cols; 1069 1070 // Treat _matrix as a rectangular array 1071 ref CostType matrix(size_t row, size_t col) { return _matrix[row * cols + col]; } 1072 1073 void AllocMatrix(size_t r, size_t c) @trusted { 1074 import core.checkedint : mulu; 1075 bool overflow; 1076 const rc = mulu(r, c, overflow); 1077 if (overflow) assert(0); 1078 rows = r; 1079 cols = c; 1080 if (_matrix.length < rc) 1081 { 1082 import core.exception : onOutOfMemoryError; 1083 import core.stdc.stdlib : realloc; 1084 const nbytes = mulu(rc, _matrix[0].sizeof, overflow); 1085 if (overflow) assert(0); 1086 auto m = cast(CostType *) realloc(_matrix.ptr, nbytes); 1087 if (!m) 1088 onOutOfMemoryError(); 1089 _matrix = m[0 .. r * c]; 1090 InitMatrix(); 1091 } 1092 } 1093 1094 void FreeMatrix() @trusted { 1095 import core.stdc.stdlib : free; 1096 1097 free(_matrix.ptr); 1098 _matrix = null; 1099 } 1100 1101 void InitMatrix() { 1102 foreach (r; 0 .. rows) 1103 matrix(r,0) = r * _deletionIncrement; 1104 foreach (c; 0 .. cols) 1105 matrix(0,c) = c * _insertionIncrement; 1106 } 1107 1108 static uint min_index(CostType i0, CostType i1, CostType i2) 1109 { 1110 if (i0 <= i1) 1111 { 1112 return i0 <= i2 ? 0 : 2; 1113 } 1114 else 1115 { 1116 return i1 <= i2 ? 1 : 2; 1117 } 1118 } 1119 1120 CostType distanceWithPath(Range s, Range t) 1121 { 1122 auto slen = walkLength(s.save), tlen = walkLength(t.save); 1123 AllocMatrix(slen + 1, tlen + 1); 1124 foreach (i; 1 .. rows) 1125 { 1126 auto sfront = s.front; 1127 auto tt = t.save; 1128 foreach (j; 1 .. cols) 1129 { 1130 auto cSub = matrix(i - 1,j - 1) 1131 + (equals(sfront, tt.front) ? 0 : _substitutionIncrement); 1132 tt.popFront(); 1133 auto cIns = matrix(i,j - 1) + _insertionIncrement; 1134 auto cDel = matrix(i - 1,j) + _deletionIncrement; 1135 switch (min_index(cSub, cIns, cDel)) 1136 { 1137 case 0: 1138 matrix(i,j) = cSub; 1139 break; 1140 case 1: 1141 matrix(i,j) = cIns; 1142 break; 1143 default: 1144 matrix(i,j) = cDel; 1145 break; 1146 } 1147 } 1148 s.popFront(); 1149 } 1150 return matrix(slen,tlen); 1151 } 1152 1153 CostType distanceLowMem(Range s, Range t, CostType slen, CostType tlen) 1154 { 1155 CostType lastdiag, olddiag; 1156 AllocMatrix(slen + 1, 1); 1157 foreach (y; 1 .. slen + 1) 1158 { 1159 _matrix[y] = y; 1160 } 1161 foreach (x; 1 .. tlen + 1) 1162 { 1163 auto tfront = t.front; 1164 auto ss = s.save; 1165 _matrix[0] = x; 1166 lastdiag = x - 1; 1167 foreach (y; 1 .. rows) 1168 { 1169 olddiag = _matrix[y]; 1170 auto cSub = lastdiag + (equals(ss.front, tfront) ? 0 : _substitutionIncrement); 1171 ss.popFront(); 1172 auto cIns = _matrix[y - 1] + _insertionIncrement; 1173 auto cDel = _matrix[y] + _deletionIncrement; 1174 switch (min_index(cSub, cIns, cDel)) 1175 { 1176 case 0: 1177 _matrix[y] = cSub; 1178 break; 1179 case 1: 1180 _matrix[y] = cIns; 1181 break; 1182 default: 1183 _matrix[y] = cDel; 1184 break; 1185 } 1186 lastdiag = olddiag; 1187 } 1188 t.popFront(); 1189 } 1190 return _matrix[slen]; 1191 } 1192 } 1193 1194 /** 1195 Returns the $(HTTP wikipedia.org/wiki/Levenshtein_distance, Levenshtein 1196 distance) between $(D s) and $(D t). The Levenshtein distance computes 1197 the minimal amount of edit operations necessary to transform $(D s) 1198 into $(D t). Performs $(BIGOH s.length * t.length) evaluations of $(D 1199 equals) and occupies $(BIGOH s.length * t.length) storage. 1200 1201 Params: 1202 equals = The binary predicate to compare the elements of the two ranges. 1203 s = The original range. 1204 t = The transformation target 1205 1206 Returns: 1207 The minimal number of edits to transform s into t. 1208 1209 Does not allocate GC memory. 1210 */ 1211 size_t levenshteinDistance(alias equals = (a,b) => a == b, Range1, Range2) 1212 (Range1 s, Range2 t) 1213 if (isForwardRange!(Range1) && isForwardRange!(Range2)) 1214 { 1215 alias eq = binaryFun!(equals); 1216 1217 for (;;) 1218 { 1219 if (s.empty) return t.walkLength; 1220 if (t.empty) return s.walkLength; 1221 if (eq(s.front, t.front)) 1222 { 1223 s.popFront(); 1224 t.popFront(); 1225 continue; 1226 } 1227 static if (isBidirectionalRange!(Range1) && isBidirectionalRange!(Range2)) 1228 { 1229 if (eq(s.back, t.back)) 1230 { 1231 s.popBack(); 1232 t.popBack(); 1233 continue; 1234 } 1235 } 1236 break; 1237 } 1238 1239 auto slen = walkLength(s.save); 1240 auto tlen = walkLength(t.save); 1241 1242 if (slen == 1 && tlen == 1) 1243 { 1244 return eq(s.front, t.front) ? 0 : 1; 1245 } 1246 1247 if (slen > tlen) 1248 { 1249 Levenshtein!(Range1, eq, size_t) lev; 1250 return lev.distanceLowMem(s, t, slen, tlen); 1251 } 1252 else 1253 { 1254 Levenshtein!(Range2, eq, size_t) lev; 1255 return lev.distanceLowMem(t, s, tlen, slen); 1256 } 1257 } 1258 1259 /// 1260 @safe unittest 1261 { 1262 import std.algorithm.iteration : filter; 1263 import std.uni : toUpper; 1264 1265 assert(levenshteinDistance("cat", "rat") == 1); 1266 assert(levenshteinDistance("parks", "spark") == 2); 1267 assert(levenshteinDistance("abcde", "abcde") == 0); 1268 assert(levenshteinDistance("abcde", "abCde") == 1); 1269 assert(levenshteinDistance("kitten", "sitting") == 3); 1270 assert(levenshteinDistance!((a, b) => toUpper(a) == toUpper(b)) 1271 ("parks", "SPARK") == 2); 1272 assert(levenshteinDistance("parks".filter!"true", "spark".filter!"true") == 2); 1273 assert(levenshteinDistance("ID", "I♥D") == 1); 1274 } 1275 1276 @safe @nogc nothrow unittest 1277 { 1278 assert(levenshteinDistance("cat"d, "rat"d) == 1); 1279 } 1280 1281 /// ditto 1282 size_t levenshteinDistance(alias equals = (a,b) => a == b, Range1, Range2) 1283 (auto ref Range1 s, auto ref Range2 t) 1284 if (isConvertibleToString!Range1 || isConvertibleToString!Range2) 1285 { 1286 import std.meta : staticMap; 1287 alias Types = staticMap!(convertToString, Range1, Range2); 1288 return levenshteinDistance!(equals, Types)(s, t); 1289 } 1290 1291 @safe unittest 1292 { 1293 static struct S { string s; alias s this; } 1294 assert(levenshteinDistance(S("cat"), S("rat")) == 1); 1295 assert(levenshteinDistance("cat", S("rat")) == 1); 1296 assert(levenshteinDistance(S("cat"), "rat") == 1); 1297 } 1298 1299 @safe @nogc nothrow unittest 1300 { 1301 static struct S { dstring s; alias s this; } 1302 assert(levenshteinDistance(S("cat"d), S("rat"d)) == 1); 1303 assert(levenshteinDistance("cat"d, S("rat"d)) == 1); 1304 assert(levenshteinDistance(S("cat"d), "rat"d) == 1); 1305 } 1306 1307 /** 1308 Returns the Levenshtein distance and the edit path between $(D s) and 1309 $(D t). 1310 1311 Params: 1312 equals = The binary predicate to compare the elements of the two ranges. 1313 s = The original range. 1314 t = The transformation target 1315 1316 Returns: 1317 Tuple with the first element being the minimal amount of edits to transform s into t and 1318 the second element being the sequence of edits to effect this transformation. 1319 1320 Allocates GC memory for the returned EditOp[] array. 1321 */ 1322 Tuple!(size_t, EditOp[]) 1323 levenshteinDistanceAndPath(alias equals = (a,b) => a == b, Range1, Range2) 1324 (Range1 s, Range2 t) 1325 if (isForwardRange!(Range1) && isForwardRange!(Range2)) 1326 { 1327 Levenshtein!(Range1, binaryFun!(equals)) lev; 1328 auto d = lev.distanceWithPath(s, t); 1329 return tuple(d, lev.path()); 1330 } 1331 1332 /// 1333 @safe unittest 1334 { 1335 string a = "Saturday", b = "Sundays"; 1336 auto p = levenshteinDistanceAndPath(a, b); 1337 assert(p[0] == 4); 1338 assert(equal(p[1], "nrrnsnnni")); 1339 } 1340 1341 @safe unittest 1342 { 1343 assert(levenshteinDistance("a", "a") == 0); 1344 assert(levenshteinDistance("a", "b") == 1); 1345 assert(levenshteinDistance("aa", "ab") == 1); 1346 assert(levenshteinDistance("aa", "abc") == 2); 1347 assert(levenshteinDistance("Saturday", "Sunday") == 3); 1348 assert(levenshteinDistance("kitten", "sitting") == 3); 1349 } 1350 1351 /// ditto 1352 Tuple!(size_t, EditOp[]) 1353 levenshteinDistanceAndPath(alias equals = (a,b) => a == b, Range1, Range2) 1354 (auto ref Range1 s, auto ref Range2 t) 1355 if (isConvertibleToString!Range1 || isConvertibleToString!Range2) 1356 { 1357 import std.meta : staticMap; 1358 alias Types = staticMap!(convertToString, Range1, Range2); 1359 return levenshteinDistanceAndPath!(equals, Types)(s, t); 1360 } 1361 1362 @safe unittest 1363 { 1364 static struct S { string s; alias s this; } 1365 assert(levenshteinDistanceAndPath(S("cat"), S("rat"))[0] == 1); 1366 assert(levenshteinDistanceAndPath("cat", S("rat"))[0] == 1); 1367 assert(levenshteinDistanceAndPath(S("cat"), "rat")[0] == 1); 1368 } 1369 1370 // max 1371 /** 1372 Iterates the passed arguments and return the maximum value. 1373 1374 Params: 1375 args = The values to select the maximum from. At least two arguments must 1376 be passed. 1377 1378 Returns: 1379 The maximum of the passed-in args. The type of the returned value is 1380 the type among the passed arguments that is able to store the largest value. 1381 1382 See_Also: 1383 $(REF maxElement, std,algorithm,searching) 1384 */ 1385 MaxType!T max(T...)(T args) 1386 if (T.length >= 2) 1387 { 1388 //Get "a" 1389 static if (T.length <= 2) 1390 alias a = args[0]; 1391 else 1392 auto a = max(args[0 .. ($+1)/2]); 1393 alias T0 = typeof(a); 1394 1395 //Get "b" 1396 static if (T.length <= 3) 1397 alias b = args[$-1]; 1398 else 1399 auto b = max(args[($+1)/2 .. $]); 1400 alias T1 = typeof(b); 1401 1402 import std.algorithm.internal : algoFormat; 1403 static assert(is(typeof(a < b)), 1404 algoFormat("Invalid arguments: Cannot compare types %s and %s.", T0.stringof, T1.stringof)); 1405 1406 //Do the "max" proper with a and b 1407 import std.functional : lessThan; 1408 immutable chooseB = lessThan!(T0, T1)(a, b); 1409 return cast(typeof(return)) (chooseB ? b : a); 1410 } 1411 1412 /// 1413 @safe unittest 1414 { 1415 int a = 5; 1416 short b = 6; 1417 double c = 2; 1418 auto d = max(a, b); 1419 assert(is(typeof(d) == int)); 1420 assert(d == 6); 1421 auto e = min(a, b, c); 1422 assert(is(typeof(e) == double)); 1423 assert(e == 2); 1424 } 1425 1426 @safe unittest 1427 { 1428 int a = 5; 1429 short b = 6; 1430 double c = 2; 1431 auto d = max(a, b); 1432 static assert(is(typeof(d) == int)); 1433 assert(d == 6); 1434 auto e = max(a, b, c); 1435 static assert(is(typeof(e) == double)); 1436 assert(e == 6); 1437 // mixed sign 1438 a = -5; 1439 uint f = 5; 1440 static assert(is(typeof(max(a, f)) == uint)); 1441 assert(max(a, f) == 5); 1442 1443 //Test user-defined types 1444 import std.datetime : Date; 1445 assert(max(Date(2012, 12, 21), Date(1982, 1, 4)) == Date(2012, 12, 21)); 1446 assert(max(Date(1982, 1, 4), Date(2012, 12, 21)) == Date(2012, 12, 21)); 1447 assert(max(Date(1982, 1, 4), Date.min) == Date(1982, 1, 4)); 1448 assert(max(Date.min, Date(1982, 1, 4)) == Date(1982, 1, 4)); 1449 assert(max(Date(1982, 1, 4), Date.max) == Date.max); 1450 assert(max(Date.max, Date(1982, 1, 4)) == Date.max); 1451 assert(max(Date.min, Date.max) == Date.max); 1452 assert(max(Date.max, Date.min) == Date.max); 1453 } 1454 1455 // MinType 1456 private template MinType(T...) 1457 if (T.length >= 1) 1458 { 1459 static if (T.length == 1) 1460 { 1461 alias MinType = T[0]; 1462 } 1463 else static if (T.length == 2) 1464 { 1465 static if (!is(typeof(T[0].min))) 1466 alias MinType = CommonType!T; 1467 else 1468 { 1469 enum hasMostNegative = is(typeof(mostNegative!(T[0]))) && 1470 is(typeof(mostNegative!(T[1]))); 1471 static if (hasMostNegative && mostNegative!(T[1]) < mostNegative!(T[0])) 1472 alias MinType = T[1]; 1473 else static if (hasMostNegative && mostNegative!(T[1]) > mostNegative!(T[0])) 1474 alias MinType = T[0]; 1475 else static if (T[1].max < T[0].max) 1476 alias MinType = T[1]; 1477 else 1478 alias MinType = T[0]; 1479 } 1480 } 1481 else 1482 { 1483 alias MinType = MinType!(MinType!(T[0 .. ($+1)/2]), MinType!(T[($+1)/2 .. $])); 1484 } 1485 } 1486 1487 // min 1488 /** 1489 Iterates the passed arguments and returns the minimum value. 1490 1491 Params: args = The values to select the minimum from. At least two arguments 1492 must be passed, and they must be comparable with `<`. 1493 Returns: The minimum of the passed-in values. 1494 See_Also: 1495 $(REF minElement, std,algorithm,searching) 1496 */ 1497 MinType!T min(T...)(T args) 1498 if (T.length >= 2) 1499 { 1500 //Get "a" 1501 static if (T.length <= 2) 1502 alias a = args[0]; 1503 else 1504 auto a = min(args[0 .. ($+1)/2]); 1505 alias T0 = typeof(a); 1506 1507 //Get "b" 1508 static if (T.length <= 3) 1509 alias b = args[$-1]; 1510 else 1511 auto b = min(args[($+1)/2 .. $]); 1512 alias T1 = typeof(b); 1513 1514 import std.algorithm.internal : algoFormat; 1515 static assert(is(typeof(a < b)), 1516 algoFormat("Invalid arguments: Cannot compare types %s and %s.", T0.stringof, T1.stringof)); 1517 1518 //Do the "min" proper with a and b 1519 import std.functional : lessThan; 1520 immutable chooseA = lessThan!(T0, T1)(a, b); 1521 return cast(typeof(return)) (chooseA ? a : b); 1522 } 1523 1524 /// 1525 @safe unittest 1526 { 1527 int a = 5; 1528 short b = 6; 1529 double c = 2; 1530 auto d = min(a, b); 1531 static assert(is(typeof(d) == int)); 1532 assert(d == 5); 1533 auto e = min(a, b, c); 1534 static assert(is(typeof(e) == double)); 1535 assert(e == 2); 1536 1537 // With arguments of mixed signedness, the return type is the one that can 1538 // store the lowest values. 1539 a = -10; 1540 uint f = 10; 1541 static assert(is(typeof(min(a, f)) == int)); 1542 assert(min(a, f) == -10); 1543 1544 // User-defined types that support comparison with < are supported. 1545 import std.datetime; 1546 assert(min(Date(2012, 12, 21), Date(1982, 1, 4)) == Date(1982, 1, 4)); 1547 assert(min(Date(1982, 1, 4), Date(2012, 12, 21)) == Date(1982, 1, 4)); 1548 assert(min(Date(1982, 1, 4), Date.min) == Date.min); 1549 assert(min(Date.min, Date(1982, 1, 4)) == Date.min); 1550 assert(min(Date(1982, 1, 4), Date.max) == Date(1982, 1, 4)); 1551 assert(min(Date.max, Date(1982, 1, 4)) == Date(1982, 1, 4)); 1552 assert(min(Date.min, Date.max) == Date.min); 1553 assert(min(Date.max, Date.min) == Date.min); 1554 } 1555 1556 // mismatch 1557 /** 1558 Sequentially compares elements in $(D r1) and $(D r2) in lockstep, and 1559 stops at the first mismatch (according to $(D pred), by default 1560 equality). Returns a tuple with the reduced ranges that start with the 1561 two mismatched values. Performs $(BIGOH min(r1.length, r2.length)) 1562 evaluations of $(D pred). 1563 1564 See_Also: 1565 $(HTTP sgi.com/tech/stl/_mismatch.html, STL's _mismatch) 1566 */ 1567 Tuple!(Range1, Range2) 1568 mismatch(alias pred = "a == b", Range1, Range2)(Range1 r1, Range2 r2) 1569 if (isInputRange!(Range1) && isInputRange!(Range2)) 1570 { 1571 for (; !r1.empty && !r2.empty; r1.popFront(), r2.popFront()) 1572 { 1573 if (!binaryFun!(pred)(r1.front, r2.front)) break; 1574 } 1575 return tuple(r1, r2); 1576 } 1577 1578 /// 1579 @safe unittest 1580 { 1581 int[] x = [ 1, 5, 2, 7, 4, 3 ]; 1582 double[] y = [ 1.0, 5, 2, 7.3, 4, 8 ]; 1583 auto m = mismatch(x, y); 1584 assert(m[0] == x[3 .. $]); 1585 assert(m[1] == y[3 .. $]); 1586 } 1587 1588 @safe unittest 1589 { 1590 int[] a = [ 1, 2, 3 ]; 1591 int[] b = [ 1, 2, 4, 5 ]; 1592 auto mm = mismatch(a, b); 1593 assert(mm[0] == [3]); 1594 assert(mm[1] == [4, 5]); 1595 } 1596 1597 /** 1598 Returns one of a collection of expressions based on the value of the switch 1599 expression. 1600 1601 $(D choices) needs to be composed of pairs of test expressions and return 1602 expressions. Each test-expression is compared with $(D switchExpression) using 1603 $(D pred)($(D switchExpression) is the first argument) and if that yields true 1604 - the return expression is returned. 1605 1606 Both the test and the return expressions are lazily evaluated. 1607 1608 Params: 1609 1610 switchExpression = The first argument for the predicate. 1611 1612 choices = Pairs of test expressions and return expressions. The test 1613 expressions will be the second argument for the predicate, and the return 1614 expression will be returned if the predicate yields true with $(D 1615 switchExpression) and the test expression as arguments. May also have a 1616 default return expression, that needs to be the last expression without a test 1617 expression before it. A return expression may be of void type only if it 1618 always throws. 1619 1620 Returns: The return expression associated with the first test expression that 1621 made the predicate yield true, or the default return expression if no test 1622 expression matched. 1623 1624 Throws: If there is no default return expression and the predicate does not 1625 yield true with any test expression - $(D SwitchError) is thrown. $(D 1626 SwitchError) is also thrown if a void return expression was executed without 1627 throwing anything. 1628 */ 1629 auto predSwitch(alias pred = "a == b", T, R ...)(T switchExpression, lazy R choices) 1630 { 1631 import core.exception : SwitchError; 1632 alias predicate = binaryFun!(pred); 1633 1634 foreach (index, ChoiceType; R) 1635 { 1636 //The even places in `choices` are for the predicate. 1637 static if (index % 2 == 1) 1638 { 1639 if (predicate(switchExpression, choices[index - 1]())) 1640 { 1641 static if (is(typeof(choices[index]()) == void)) 1642 { 1643 choices[index](); 1644 throw new SwitchError("Choices that return void should throw"); 1645 } 1646 else 1647 { 1648 return choices[index](); 1649 } 1650 } 1651 } 1652 } 1653 1654 //In case nothing matched: 1655 static if (R.length % 2 == 1) //If there is a default return expression: 1656 { 1657 static if (is(typeof(choices[$ - 1]()) == void)) 1658 { 1659 choices[$ - 1](); 1660 throw new SwitchError("Choices that return void should throw"); 1661 } 1662 else 1663 { 1664 return choices[$ - 1](); 1665 } 1666 } 1667 else //If there is no default return expression: 1668 { 1669 throw new SwitchError("Input not matched by any pattern"); 1670 } 1671 } 1672 1673 /// 1674 @safe unittest 1675 { 1676 string res = 2.predSwitch!"a < b"( 1677 1, "less than 1", 1678 5, "less than 5", 1679 10, "less than 10", 1680 "greater or equal to 10"); 1681 1682 assert(res == "less than 5"); 1683 1684 //The arguments are lazy, which allows us to use predSwitch to create 1685 //recursive functions: 1686 int factorial(int n) 1687 { 1688 return n.predSwitch!"a <= b"( 1689 -1, {throw new Exception("Can not calculate n! for n < 0");}(), 1690 0, 1, // 0! = 1 1691 n * factorial(n - 1) // n! = n * (n - 1)! for n >= 0 1692 ); 1693 } 1694 assert(factorial(3) == 6); 1695 1696 //Void return expressions are allowed if they always throw: 1697 import std.exception : assertThrown; 1698 assertThrown!Exception(factorial(-9)); 1699 } 1700 1701 @system unittest 1702 { 1703 import core.exception : SwitchError; 1704 import std.exception : assertThrown; 1705 1706 //Nothing matches - with default return expression: 1707 assert(20.predSwitch!"a < b"( 1708 1, "less than 1", 1709 5, "less than 5", 1710 10, "less than 10", 1711 "greater or equal to 10") == "greater or equal to 10"); 1712 1713 //Nothing matches - without default return expression: 1714 assertThrown!SwitchError(20.predSwitch!"a < b"( 1715 1, "less than 1", 1716 5, "less than 5", 1717 10, "less than 10", 1718 )); 1719 1720 //Using the default predicate: 1721 assert(2.predSwitch( 1722 1, "one", 1723 2, "two", 1724 3, "three", 1725 ) == "two"); 1726 1727 //Void return expressions must always throw: 1728 assertThrown!SwitchError(1.predSwitch( 1729 0, "zero", 1730 1, {}(), //A void return expression that doesn't throw 1731 2, "two", 1732 )); 1733 } 1734 1735 /** 1736 Checks if the two ranges have the same number of elements. This function is 1737 optimized to always take advantage of the $(D length) member of either range 1738 if it exists. 1739 1740 If both ranges have a length member, this function is $(BIGOH 1). Otherwise, 1741 this function is $(BIGOH min(r1.length, r2.length)). 1742 1743 Params: 1744 r1 = a finite $(REF_ALTTEXT input range, isInputRange, std,range,primitives) 1745 r2 = a finite $(REF_ALTTEXT input range, isInputRange, std,range,primitives) 1746 1747 Returns: 1748 $(D true) if both ranges have the same length, $(D false) otherwise. 1749 */ 1750 bool isSameLength(Range1, Range2)(Range1 r1, Range2 r2) 1751 if (isInputRange!Range1 && 1752 isInputRange!Range2 && 1753 !isInfinite!Range1 && 1754 !isInfinite!Range2) 1755 { 1756 static if (hasLength!(Range1) && hasLength!(Range2)) 1757 { 1758 return r1.length == r2.length; 1759 } 1760 else static if (hasLength!(Range1) && !hasLength!(Range2)) 1761 { 1762 size_t length; 1763 1764 while (!r2.empty) 1765 { 1766 r2.popFront; 1767 1768 if (++length > r1.length) 1769 { 1770 return false; 1771 } 1772 } 1773 1774 return !(length < r1.length); 1775 } 1776 else static if (!hasLength!(Range1) && hasLength!(Range2)) 1777 { 1778 size_t length; 1779 1780 while (!r1.empty) 1781 { 1782 r1.popFront; 1783 1784 if (++length > r2.length) 1785 { 1786 return false; 1787 } 1788 } 1789 1790 return !(length < r2.length); 1791 } 1792 else 1793 { 1794 while (!r1.empty) 1795 { 1796 if (r2.empty) 1797 { 1798 return false; 1799 } 1800 1801 r1.popFront; 1802 r2.popFront; 1803 } 1804 1805 return r2.empty; 1806 } 1807 } 1808 1809 /// 1810 @safe nothrow pure unittest 1811 { 1812 assert(isSameLength([1, 2, 3], [4, 5, 6])); 1813 assert(isSameLength([0.3, 90.4, 23.7, 119.2], [42.6, 23.6, 95.5, 6.3])); 1814 assert(isSameLength("abc", "xyz")); 1815 1816 int[] a; 1817 int[] b; 1818 assert(isSameLength(a, b)); 1819 1820 assert(!isSameLength([1, 2, 3], [4, 5])); 1821 assert(!isSameLength([0.3, 90.4, 23.7], [42.6, 23.6, 95.5, 6.3])); 1822 assert(!isSameLength("abcd", "xyz")); 1823 } 1824 1825 // Test CTFE 1826 @safe pure unittest 1827 { 1828 enum result1 = isSameLength([1, 2, 3], [4, 5, 6]); 1829 static assert(result1); 1830 1831 enum result2 = isSameLength([0.3, 90.4, 23.7], [42.6, 23.6, 95.5, 6.3]); 1832 static assert(!result2); 1833 } 1834 1835 @safe nothrow pure unittest 1836 { 1837 import std.internal.test.dummyrange; 1838 1839 auto r1 = new ReferenceInputRange!int([1, 2, 3]); 1840 auto r2 = new ReferenceInputRange!int([4, 5, 6]); 1841 assert(isSameLength(r1, r2)); 1842 1843 auto r3 = new ReferenceInputRange!int([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); 1844 DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Input) r4; 1845 assert(isSameLength(r3, r4)); 1846 1847 DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Input) r5; 1848 auto r6 = new ReferenceInputRange!int([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); 1849 assert(isSameLength(r5, r6)); 1850 1851 auto r7 = new ReferenceInputRange!int([1, 2]); 1852 auto r8 = new ReferenceInputRange!int([4, 5, 6]); 1853 assert(!isSameLength(r7, r8)); 1854 1855 auto r9 = new ReferenceInputRange!int([1, 2, 3, 4, 5, 6, 7, 8]); 1856 DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Input) r10; 1857 assert(!isSameLength(r9, r10)); 1858 1859 DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Input) r11; 1860 auto r12 = new ReferenceInputRange!int([1, 2, 3, 4, 5, 6, 7, 8]); 1861 assert(!isSameLength(r11, r12)); 1862 } 1863 1864 /// For convenience 1865 alias AllocateGC = Flag!"allocateGC"; 1866 1867 /** 1868 Checks if both ranges are permutations of each other. 1869 1870 This function can allocate if the $(D Yes.allocateGC) flag is passed. This has 1871 the benefit of have better complexity than the $(D Yes.allocateGC) option. However, 1872 this option is only available for ranges whose equality can be determined via each 1873 element's $(D toHash) method. If customized equality is needed, then the $(D pred) 1874 template parameter can be passed, and the function will automatically switch to 1875 the non-allocating algorithm. See $(REF binaryFun, std,functional) for more details on 1876 how to define $(D pred). 1877 1878 Non-allocating forward range option: $(BIGOH n^2) 1879 Non-allocating forward range option with custom $(D pred): $(BIGOH n^2) 1880 Allocating forward range option: amortized $(BIGOH r1.length) + $(BIGOH r2.length) 1881 1882 Params: 1883 pred = an optional parameter to change how equality is defined 1884 allocate_gc = $(D Yes.allocateGC)/$(D No.allocateGC) 1885 r1 = A finite $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) 1886 r2 = A finite $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) 1887 1888 Returns: 1889 $(D true) if all of the elements in $(D r1) appear the same number of times in $(D r2). 1890 Otherwise, returns $(D false). 1891 */ 1892 1893 bool isPermutation(AllocateGC allocate_gc, Range1, Range2) 1894 (Range1 r1, Range2 r2) 1895 if (allocate_gc == Yes.allocateGC && 1896 isForwardRange!Range1 && 1897 isForwardRange!Range2 && 1898 !isInfinite!Range1 && 1899 !isInfinite!Range2) 1900 { 1901 alias E1 = Unqual!(ElementType!Range1); 1902 alias E2 = Unqual!(ElementType!Range2); 1903 1904 if (!isSameLength(r1.save, r2.save)) 1905 { 1906 return false; 1907 } 1908 1909 // Skip the elements at the beginning where r1.front == r2.front, 1910 // they are in the same order and don't need to be counted. 1911 while (!r1.empty && !r2.empty && r1.front == r2.front) 1912 { 1913 r1.popFront(); 1914 r2.popFront(); 1915 } 1916 1917 if (r1.empty && r2.empty) 1918 { 1919 return true; 1920 } 1921 1922 int[CommonType!(E1, E2)] counts; 1923 1924 foreach (item; r1) 1925 { 1926 ++counts[item]; 1927 } 1928 1929 foreach (item; r2) 1930 { 1931 if (--counts[item] < 0) 1932 { 1933 return false; 1934 } 1935 } 1936 1937 return true; 1938 } 1939 1940 /// ditto 1941 bool isPermutation(alias pred = "a == b", Range1, Range2) 1942 (Range1 r1, Range2 r2) 1943 if (is(typeof(binaryFun!(pred))) && 1944 isForwardRange!Range1 && 1945 isForwardRange!Range2 && 1946 !isInfinite!Range1 && 1947 !isInfinite!Range2) 1948 { 1949 import std.algorithm.searching : count; 1950 1951 alias predEquals = binaryFun!(pred); 1952 alias E1 = Unqual!(ElementType!Range1); 1953 alias E2 = Unqual!(ElementType!Range2); 1954 1955 if (!isSameLength(r1.save, r2.save)) 1956 { 1957 return false; 1958 } 1959 1960 // Skip the elements at the beginning where r1.front == r2.front, 1961 // they are in the same order and don't need to be counted. 1962 while (!r1.empty && !r2.empty && predEquals(r1.front, r2.front)) 1963 { 1964 r1.popFront(); 1965 r2.popFront(); 1966 } 1967 1968 if (r1.empty && r2.empty) 1969 { 1970 return true; 1971 } 1972 1973 size_t r1_count; 1974 size_t r2_count; 1975 1976 // At each element item, when computing the count of item, scan it while 1977 // also keeping track of the scanning index. If the first occurrence 1978 // of item in the scanning loop has an index smaller than the current index, 1979 // then you know that the element has been seen before 1980 size_t index; 1981 outloop: for (auto r1s1 = r1.save; !r1s1.empty; r1s1.popFront, index++) 1982 { 1983 auto item = r1s1.front; 1984 r1_count = 0; 1985 r2_count = 0; 1986 1987 size_t i; 1988 for (auto r1s2 = r1.save; !r1s2.empty; r1s2.popFront, i++) 1989 { 1990 auto e = r1s2.front; 1991 if (predEquals(e, item) && i < index) 1992 { 1993 continue outloop; 1994 } 1995 else if (predEquals(e, item)) 1996 { 1997 ++r1_count; 1998 } 1999 } 2000 2001 r2_count = r2.save.count!pred(item); 2002 2003 if (r1_count != r2_count) 2004 { 2005 return false; 2006 } 2007 } 2008 2009 return true; 2010 } 2011 2012 /// 2013 @safe pure unittest 2014 { 2015 import std.typecons : Yes; 2016 2017 assert(isPermutation([1, 2, 3], [3, 2, 1])); 2018 assert(isPermutation([1.1, 2.3, 3.5], [2.3, 3.5, 1.1])); 2019 assert(isPermutation("abc", "bca")); 2020 2021 assert(!isPermutation([1, 2], [3, 4])); 2022 assert(!isPermutation([1, 1, 2, 3], [1, 2, 2, 3])); 2023 assert(!isPermutation([1, 1], [1, 1, 1])); 2024 2025 // Faster, but allocates GC handled memory 2026 assert(isPermutation!(Yes.allocateGC)([1.1, 2.3, 3.5], [2.3, 3.5, 1.1])); 2027 assert(!isPermutation!(Yes.allocateGC)([1, 2], [3, 4])); 2028 } 2029 2030 // Test @nogc inference 2031 @safe @nogc pure unittest 2032 { 2033 static immutable arr1 = [1, 2, 3]; 2034 static immutable arr2 = [3, 2, 1]; 2035 assert(isPermutation(arr1, arr2)); 2036 2037 static immutable arr3 = [1, 1, 2, 3]; 2038 static immutable arr4 = [1, 2, 2, 3]; 2039 assert(!isPermutation(arr3, arr4)); 2040 } 2041 2042 @safe pure unittest 2043 { 2044 import std.internal.test.dummyrange; 2045 2046 auto r1 = new ReferenceForwardRange!int([1, 2, 3, 4]); 2047 auto r2 = new ReferenceForwardRange!int([1, 2, 4, 3]); 2048 assert(isPermutation(r1, r2)); 2049 2050 auto r3 = new ReferenceForwardRange!int([1, 2, 3, 4]); 2051 auto r4 = new ReferenceForwardRange!int([4, 2, 1, 3]); 2052 assert(isPermutation!(Yes.allocateGC)(r3, r4)); 2053 2054 auto r5 = new ReferenceForwardRange!int([1, 2, 3]); 2055 auto r6 = new ReferenceForwardRange!int([4, 2, 1, 3]); 2056 assert(!isPermutation(r5, r6)); 2057 2058 auto r7 = new ReferenceForwardRange!int([4, 2, 1, 3]); 2059 auto r8 = new ReferenceForwardRange!int([1, 2, 3]); 2060 assert(!isPermutation!(Yes.allocateGC)(r7, r8)); 2061 2062 DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random) r9; 2063 DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random) r10; 2064 assert(isPermutation(r9, r10)); 2065 2066 DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random) r11; 2067 DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random) r12; 2068 assert(isPermutation!(Yes.allocateGC)(r11, r12)); 2069 2070 alias mytuple = Tuple!(int, int); 2071 2072 assert(isPermutation!"a[0] == b[0]"( 2073 [mytuple(1, 4), mytuple(2, 5)], 2074 [mytuple(2, 3), mytuple(1, 2)] 2075 )); 2076 } 2077 2078 /** 2079 Get the _first argument `a` that passes an `if (unaryFun!pred(a))` test. If 2080 no argument passes the test, return the last argument. 2081 2082 Similar to behaviour of the `or` operator in dynamic languages such as Lisp's 2083 `(or ...)` and Python's `a or b or ...` except that the last argument is 2084 returned upon no match. 2085 2086 Simplifies logic, for instance, in parsing rules where a set of alternative 2087 matchers are tried. The _first one that matches returns it match result, 2088 typically as an abstract syntax tree (AST). 2089 2090 Bugs: 2091 Lazy parameters are currently, too restrictively, inferred by DMD to 2092 always throw even though they don't need to be. This makes it impossible to 2093 currently mark `either` as `nothrow`. See issue at $(BUGZILLA 12647). 2094 2095 Returns: 2096 The _first argument that passes the test `pred`. 2097 */ 2098 CommonType!(T, Ts) either(alias pred = a => a, T, Ts...)(T first, lazy Ts alternatives) 2099 if (alternatives.length >= 1 && 2100 !is(CommonType!(T, Ts) == void) && 2101 allSatisfy!(ifTestable, T, Ts)) 2102 { 2103 alias predFun = unaryFun!pred; 2104 2105 if (predFun(first)) return first; 2106 2107 foreach (e; alternatives[0 .. $ - 1]) 2108 if (predFun(e)) return e; 2109 2110 return alternatives[$ - 1]; 2111 } 2112 2113 /// 2114 @safe pure unittest 2115 { 2116 const a = 1; 2117 const b = 2; 2118 auto ab = either(a, b); 2119 static assert(is(typeof(ab) == const(int))); 2120 assert(ab == a); 2121 2122 auto c = 2; 2123 const d = 3; 2124 auto cd = either!(a => a == 3)(c, d); // use predicate 2125 static assert(is(typeof(cd) == int)); 2126 assert(cd == d); 2127 2128 auto e = 0; 2129 const f = 2; 2130 auto ef = either(e, f); 2131 static assert(is(typeof(ef) == int)); 2132 assert(ef == f); 2133 2134 immutable p = 1; 2135 immutable q = 2; 2136 auto pq = either(p, q); 2137 static assert(is(typeof(pq) == immutable(int))); 2138 assert(pq == p); 2139 2140 assert(either(3, 4) == 3); 2141 assert(either(0, 4) == 4); 2142 assert(either(0, 0) == 0); 2143 assert(either("", "a") == ""); 2144 2145 string r = null; 2146 assert(either(r, "a") == "a"); 2147 assert(either("a", "") == "a"); 2148 2149 immutable s = [1, 2]; 2150 assert(either(s, s) == s); 2151 2152 assert(either([0, 1], [1, 2]) == [0, 1]); 2153 assert(either([0, 1], [1]) == [0, 1]); 2154 assert(either("a", "b") == "a"); 2155 2156 static assert(!__traits(compiles, either(1, "a"))); 2157 static assert(!__traits(compiles, either(1.0, "a"))); 2158 static assert(!__traits(compiles, either('a', "a"))); 2159 } 2160