1 // Written in the D programming language. 2 /** 3 This is a submodule of $(MREF std, algorithm). 4 It contains generic algorithms that implement set operations. 5 6 The functions $(LREF multiwayMerge), $(LREF multiwayUnion), $(LREF setDifference), 7 $(LREF setIntersection), $(LREF setSymmetricDifference) expect a range of sorted 8 ranges as input. 9 10 All algorithms are generalized to accept as input not only sets but also 11 $(LINK2 https://en.wikipedia.org/wiki/Multiset, multisets). Each algorithm 12 documents behaviour in the presence of duplicated inputs. 13 14 $(SCRIPT inhibitQuickIndex = 1;) 15 $(BOOKTABLE Cheat Sheet, 16 $(TR $(TH Function Name) $(TH Description)) 17 $(T2 cartesianProduct, 18 Computes Cartesian product of two ranges.) 19 $(T2 largestPartialIntersection, 20 Copies out the values that occur most frequently in a range of ranges.) 21 $(T2 largestPartialIntersectionWeighted, 22 Copies out the values that occur most frequently (multiplied by 23 per-value weights) in a range of ranges.) 24 $(T2 multiwayMerge, 25 Merges a range of sorted ranges.) 26 $(T2 multiwayUnion, 27 Computes the union of a range of sorted ranges.) 28 $(T2 setDifference, 29 Lazily computes the set difference of two sorted ranges.) 30 $(T2 setIntersection, 31 Lazily computes the intersection of two or more sorted ranges.) 32 $(T2 setSymmetricDifference, 33 Lazily computes the symmetric set difference of two sorted ranges.) 34 ) 35 36 Copyright: Andrei Alexandrescu 2008-. 37 38 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). 39 40 Authors: $(HTTP erdani.com, Andrei Alexandrescu) 41 42 Source: $(PHOBOSSRC std/algorithm/setops.d) 43 44 Macros: 45 T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) 46 */ 47 module std.algorithm.setops; 48 49 import std.range.primitives; 50 51 import std.functional : unaryFun, binaryFun; 52 import std.traits; 53 import std.meta : AliasSeq, staticMap, allSatisfy, anySatisfy; 54 55 import std.algorithm.sorting : Merge; 56 import std.typecons : No; 57 58 // cartesianProduct 59 /** 60 Lazily computes the Cartesian product of two or more ranges. The product is a 61 range of tuples of elements from each respective range. 62 63 The conditions for the two-range case are as follows: 64 65 If both ranges are finite, then one must be (at least) a 66 $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) and the 67 other an $(REF_ALTTEXT input range, isInputRange, std,range,primitives). 68 69 If one range is infinite and the other finite, then the finite range must 70 be a forward range, and the infinite range can be an input range. 71 72 If both ranges are infinite, then both must be forward ranges. 73 74 When there are more than two ranges, the above conditions apply to each 75 adjacent pair of ranges. 76 77 Params: 78 range1 = The first range 79 range2 = The second range 80 ranges = Two or more non-infinite forward ranges 81 otherRanges = Zero or more non-infinite forward ranges 82 83 Returns: 84 A forward range of $(REF Tuple, std,typecons) representing elements of the 85 cartesian product of the given ranges. 86 */ 87 auto cartesianProduct(R1, R2)(R1 range1, R2 range2) 88 if (!allSatisfy!(isForwardRange, R1, R2) || 89 anySatisfy!(isInfinite, R1, R2)) 90 { 91 import std.algorithm.iteration : map, joiner; 92 93 static if (isInfinite!R1 && isInfinite!R2) 94 { 95 static if (isForwardRange!R1 && isForwardRange!R2) 96 { 97 import std.range : zip, repeat, take, chain, sequence; 98 99 // This algorithm traverses the cartesian product by alternately 100 // covering the right and bottom edges of an increasing square area 101 // over the infinite table of combinations. This schedule allows us 102 // to require only forward ranges. 103 return zip(sequence!"n"(cast(size_t) 0), range1.save, range2.save, 104 repeat(range1), repeat(range2)) 105 .map!(function(a) => chain( 106 zip(repeat(a[1]), take(a[4].save, a[0])), 107 zip(take(a[3].save, a[0]+1), repeat(a[2])) 108 ))() 109 .joiner(); 110 } 111 else static assert(0, "cartesianProduct of infinite ranges requires "~ 112 "forward ranges"); 113 } 114 else static if (isInputRange!R1 && isForwardRange!R2 && !isInfinite!R2) 115 { 116 import std.range : zip, repeat; 117 return joiner(map!((ElementType!R1 a) => zip(repeat(a), range2.save)) 118 (range1)); 119 } 120 else static if (isInputRange!R2 && isForwardRange!R1 && !isInfinite!R1) 121 { 122 import std.range : zip, repeat; 123 return joiner(map!((ElementType!R2 a) => zip(range1.save, repeat(a))) 124 (range2)); 125 } 126 else static assert(0, "cartesianProduct involving finite ranges must "~ 127 "have at least one finite forward range"); 128 } 129 130 /// 131 @safe unittest 132 { 133 import std.algorithm.searching : canFind; 134 import std.range; 135 import std.typecons : tuple; 136 137 auto N = sequence!"n"(0); // the range of natural numbers 138 auto N2 = cartesianProduct(N, N); // the range of all pairs of natural numbers 139 140 // Various arbitrary number pairs can be found in the range in finite time. 141 assert(canFind(N2, tuple(0, 0))); 142 assert(canFind(N2, tuple(123, 321))); 143 assert(canFind(N2, tuple(11, 35))); 144 assert(canFind(N2, tuple(279, 172))); 145 } 146 147 /// 148 @safe unittest 149 { 150 import std.algorithm.searching : canFind; 151 import std.typecons : tuple; 152 153 auto B = [ 1, 2, 3 ]; 154 auto C = [ 4, 5, 6 ]; 155 auto BC = cartesianProduct(B, C); 156 157 foreach (n; [[1, 4], [2, 4], [3, 4], [1, 5], [2, 5], [3, 5], [1, 6], 158 [2, 6], [3, 6]]) 159 { 160 assert(canFind(BC, tuple(n[0], n[1]))); 161 } 162 } 163 164 @safe unittest 165 { 166 // Test cartesian product of two infinite ranges 167 import std.algorithm.searching : canFind; 168 import std.range; 169 import std.typecons : tuple; 170 171 auto Even = sequence!"2*n"(0); 172 auto Odd = sequence!"2*n+1"(0); 173 auto EvenOdd = cartesianProduct(Even, Odd); 174 175 foreach (pair; [[0, 1], [2, 1], [0, 3], [2, 3], [4, 1], [4, 3], [0, 5], 176 [2, 5], [4, 5], [6, 1], [6, 3], [6, 5]]) 177 { 178 assert(canFind(EvenOdd, tuple(pair[0], pair[1]))); 179 } 180 181 // This should terminate in finite time 182 assert(canFind(EvenOdd, tuple(124, 73))); 183 assert(canFind(EvenOdd, tuple(0, 97))); 184 assert(canFind(EvenOdd, tuple(42, 1))); 185 } 186 187 @safe unittest 188 { 189 // Test cartesian product of an infinite input range and a finite forward 190 // range. 191 import std.algorithm.searching : canFind; 192 import std.range; 193 import std.typecons : tuple; 194 195 auto N = sequence!"n"(0); 196 auto M = [100, 200, 300]; 197 auto NM = cartesianProduct(N,M); 198 199 foreach (pair; [[0, 100], [0, 200], [0, 300], [1, 100], [1, 200], [1, 300], 200 [2, 100], [2, 200], [2, 300], [3, 100], [3, 200], 201 [3, 300]]) 202 { 203 assert(canFind(NM, tuple(pair[0], pair[1]))); 204 } 205 206 // We can't solve the halting problem, so we can only check a finite 207 // initial segment here. 208 assert(!canFind(NM.take(100), tuple(100, 0))); 209 assert(!canFind(NM.take(100), tuple(1, 1))); 210 assert(!canFind(NM.take(100), tuple(100, 200))); 211 212 auto MN = cartesianProduct(M,N); 213 foreach (pair; [[100, 0], [200, 0], [300, 0], [100, 1], [200, 1], [300, 1], 214 [100, 2], [200, 2], [300, 2], [100, 3], [200, 3], 215 [300, 3]]) 216 { 217 assert(canFind(MN, tuple(pair[0], pair[1]))); 218 } 219 220 // We can't solve the halting problem, so we can only check a finite 221 // initial segment here. 222 assert(!canFind(MN.take(100), tuple(0, 100))); 223 assert(!canFind(MN.take(100), tuple(0, 1))); 224 assert(!canFind(MN.take(100), tuple(100, 200))); 225 } 226 227 @safe unittest 228 { 229 import std.algorithm.searching : canFind; 230 import std.typecons : tuple; 231 232 // Test cartesian product of two finite ranges. 233 auto X = [1, 2, 3]; 234 auto Y = [4, 5, 6]; 235 auto XY = cartesianProduct(X, Y); 236 auto Expected = [[1, 4], [1, 5], [1, 6], [2, 4], [2, 5], [2, 6], [3, 4], 237 [3, 5], [3, 6]]; 238 239 // Verify Expected ⊆ XY 240 foreach (pair; Expected) 241 { 242 assert(canFind(XY, tuple(pair[0], pair[1]))); 243 } 244 245 // Verify XY ⊆ Expected 246 foreach (pair; XY) 247 { 248 assert(canFind(Expected, [pair[0], pair[1]])); 249 } 250 251 // And therefore, by set comprehension, XY == Expected 252 } 253 254 @safe unittest 255 { 256 import std.algorithm.comparison : equal; 257 import std.algorithm.iteration : map; 258 import std.algorithm.searching : canFind; 259 import std.typecons : tuple; 260 261 import std.range; 262 auto N = sequence!"n"(0); 263 264 // To force the template to fall to the second case, we wrap N in a struct 265 // that doesn't allow bidirectional access. 266 struct FwdRangeWrapper(R) 267 { 268 R impl; 269 270 // Input range API 271 @property auto front() { return impl.front; } 272 void popFront() { impl.popFront(); } 273 static if (isInfinite!R) 274 enum empty = false; 275 else 276 @property bool empty() { return impl.empty; } 277 278 // Forward range API 279 @property auto save() { return typeof(this)(impl.save); } 280 } 281 auto fwdWrap(R)(R range) { return FwdRangeWrapper!R(range); } 282 283 // General test: two infinite bidirectional ranges 284 auto N2 = cartesianProduct(N, N); 285 286 assert(canFind(N2, tuple(0, 0))); 287 assert(canFind(N2, tuple(123, 321))); 288 assert(canFind(N2, tuple(11, 35))); 289 assert(canFind(N2, tuple(279, 172))); 290 291 // Test first case: forward range with bidirectional range 292 auto fwdN = fwdWrap(N); 293 auto N2_a = cartesianProduct(fwdN, N); 294 295 assert(canFind(N2_a, tuple(0, 0))); 296 assert(canFind(N2_a, tuple(123, 321))); 297 assert(canFind(N2_a, tuple(11, 35))); 298 assert(canFind(N2_a, tuple(279, 172))); 299 300 // Test second case: bidirectional range with forward range 301 auto N2_b = cartesianProduct(N, fwdN); 302 303 assert(canFind(N2_b, tuple(0, 0))); 304 assert(canFind(N2_b, tuple(123, 321))); 305 assert(canFind(N2_b, tuple(11, 35))); 306 assert(canFind(N2_b, tuple(279, 172))); 307 308 // Test third case: finite forward range with (infinite) input range 309 static struct InpRangeWrapper(R) 310 { 311 R impl; 312 313 // Input range API 314 @property auto front() { return impl.front; } 315 void popFront() { impl.popFront(); } 316 static if (isInfinite!R) 317 enum empty = false; 318 else 319 @property bool empty() { return impl.empty; } 320 } 321 auto inpWrap(R)(R r) { return InpRangeWrapper!R(r); } 322 323 auto inpN = inpWrap(N); 324 auto B = [ 1, 2, 3 ]; 325 auto fwdB = fwdWrap(B); 326 auto BN = cartesianProduct(fwdB, inpN); 327 328 assert(equal(map!"[a[0],a[1]]"(BN.take(10)), [[1, 0], [2, 0], [3, 0], 329 [1, 1], [2, 1], [3, 1], [1, 2], [2, 2], [3, 2], [1, 3]])); 330 331 // Test fourth case: (infinite) input range with finite forward range 332 auto NB = cartesianProduct(inpN, fwdB); 333 334 assert(equal(map!"[a[0],a[1]]"(NB.take(10)), [[0, 1], [0, 2], [0, 3], 335 [1, 1], [1, 2], [1, 3], [2, 1], [2, 2], [2, 3], [3, 1]])); 336 337 // General finite range case 338 auto C = [ 4, 5, 6 ]; 339 auto BC = cartesianProduct(B, C); 340 341 foreach (n; [[1, 4], [2, 4], [3, 4], [1, 5], [2, 5], [3, 5], [1, 6], 342 [2, 6], [3, 6]]) 343 { 344 assert(canFind(BC, tuple(n[0], n[1]))); 345 } 346 } 347 348 // https://issues.dlang.org/show_bug.cgi?id=13091 349 pure nothrow @safe @nogc unittest 350 { 351 int[1] a = [1]; 352 foreach (t; cartesianProduct(a[], a[])) {} 353 } 354 355 /// ditto 356 auto cartesianProduct(RR...)(RR ranges) 357 if (ranges.length >= 2 && 358 allSatisfy!(isForwardRange, RR) && 359 !anySatisfy!(isInfinite, RR)) 360 { 361 // This overload uses a much less template-heavy implementation when 362 // all ranges are finite forward ranges, which is the most common use 363 // case, so that we don't run out of resources too quickly. 364 // 365 // For infinite ranges or non-forward ranges, we fall back to the old 366 // implementation which expands an exponential number of templates. 367 import std.typecons : tuple; 368 369 static struct Result 370 { 371 RR ranges; 372 RR current; 373 bool empty = true; 374 375 this(RR _ranges) 376 { 377 ranges = _ranges; 378 empty = false; 379 foreach (i, r; ranges) 380 { 381 current[i] = r.save; 382 if (current[i].empty) 383 empty = true; 384 } 385 } 386 @property auto front() 387 { 388 import std.algorithm.internal : algoFormat; 389 import std.range : iota; 390 return mixin(algoFormat("tuple(%(current[%d].front%|,%))", 391 iota(0, current.length))); 392 } 393 void popFront() scope 394 { 395 foreach_reverse (i, ref r; current) 396 { 397 r.popFront(); 398 if (!r.empty) break; 399 400 static if (i == 0) 401 empty = true; 402 else 403 r = ranges[i].save; // rollover 404 } 405 } 406 @property Result save() return scope 407 { 408 Result copy = this; 409 foreach (i, r; ranges) 410 { 411 copy.ranges[i] = ranges[i].save; 412 copy.current[i] = current[i].save; 413 } 414 return copy; 415 } 416 } 417 static assert(isForwardRange!Result, Result.stringof ~ " must be a forward" 418 ~ " range"); 419 420 return Result(ranges); 421 } 422 423 // cartesian product of empty ranges should be empty 424 // https://issues.dlang.org/show_bug.cgi?id=10693 425 @safe unittest 426 { 427 int[] a, b, c, d, e; 428 auto cprod = cartesianProduct(a,b,c,d,e); 429 assert(cprod.empty); 430 foreach (_; cprod) {} // should not crash 431 432 // Test case where only one of the ranges is empty: the result should still 433 // be empty. 434 int[] p=[1], q=[]; 435 auto cprod2 = cartesianProduct(p,p,p,q,p); 436 assert(cprod2.empty); 437 foreach (_; cprod2) {} // should not crash 438 } 439 440 @safe unittest 441 { 442 // .init value of cartesianProduct should be empty 443 auto cprod = cartesianProduct([0,0], [1,1], [2,2]); 444 assert(!cprod.empty); 445 assert(cprod.init.empty); 446 } 447 448 // https://issues.dlang.org/show_bug.cgi?id=13393 449 @safe unittest 450 { 451 assert(!cartesianProduct([0],[0],[0]).save.empty); 452 } 453 454 /// ditto 455 auto cartesianProduct(R1, R2, RR...)(R1 range1, R2 range2, RR otherRanges) 456 if (!allSatisfy!(isForwardRange, R1, R2, RR) || 457 anySatisfy!(isInfinite, R1, R2, RR)) 458 { 459 /* We implement the n-ary cartesian product by recursively invoking the 460 * binary cartesian product. To make the resulting range nicer, we denest 461 * one level of tuples so that a ternary cartesian product, for example, 462 * returns 3-element tuples instead of nested 2-element tuples. 463 */ 464 import std.algorithm.internal : algoFormat; 465 import std.algorithm.iteration : map; 466 import std.range : iota; 467 468 enum string denest = algoFormat("tuple(a[0], %(a[1][%d]%|,%))", 469 iota(0, otherRanges.length+1)); 470 return map!denest( 471 cartesianProduct(range1, cartesianProduct(range2, otherRanges)) 472 ); 473 } 474 475 @safe unittest 476 { 477 import std.algorithm.searching : canFind; 478 import std.range; 479 import std.typecons : tuple, Tuple; 480 481 auto N = sequence!"n"(0); 482 auto N3 = cartesianProduct(N, N, N); 483 484 // Check that tuples are properly denested 485 assert(is(ElementType!(typeof(N3)) == Tuple!(size_t,size_t,size_t))); 486 487 assert(canFind(N3, tuple(0, 27, 7))); 488 assert(canFind(N3, tuple(50, 23, 11))); 489 assert(canFind(N3, tuple(9, 3, 0))); 490 } 491 492 @safe unittest 493 { 494 import std.algorithm.searching : canFind; 495 import std.range; 496 import std.typecons : tuple, Tuple; 497 498 auto N = sequence!"n"(0); 499 auto N4 = cartesianProduct(N, N, N, N); 500 501 // Check that tuples are properly denested 502 assert(is(ElementType!(typeof(N4)) == Tuple!(size_t,size_t,size_t,size_t))); 503 504 assert(canFind(N4, tuple(1, 2, 3, 4))); 505 assert(canFind(N4, tuple(4, 3, 2, 1))); 506 assert(canFind(N4, tuple(10, 3, 1, 2))); 507 } 508 509 // https://issues.dlang.org/show_bug.cgi?id=9878 510 /// 511 @safe unittest 512 { 513 import std.algorithm.comparison : equal; 514 import std.typecons : tuple; 515 516 auto A = [ 1, 2, 3 ]; 517 auto B = [ 'a', 'b', 'c' ]; 518 auto C = [ "x", "y", "z" ]; 519 auto ABC = cartesianProduct(A, B, C); 520 521 assert(ABC.equal([ 522 tuple(1, 'a', "x"), tuple(1, 'a', "y"), tuple(1, 'a', "z"), 523 tuple(1, 'b', "x"), tuple(1, 'b', "y"), tuple(1, 'b', "z"), 524 tuple(1, 'c', "x"), tuple(1, 'c', "y"), tuple(1, 'c', "z"), 525 tuple(2, 'a', "x"), tuple(2, 'a', "y"), tuple(2, 'a', "z"), 526 tuple(2, 'b', "x"), tuple(2, 'b', "y"), tuple(2, 'b', "z"), 527 tuple(2, 'c', "x"), tuple(2, 'c', "y"), tuple(2, 'c', "z"), 528 tuple(3, 'a', "x"), tuple(3, 'a', "y"), tuple(3, 'a', "z"), 529 tuple(3, 'b', "x"), tuple(3, 'b', "y"), tuple(3, 'b', "z"), 530 tuple(3, 'c', "x"), tuple(3, 'c', "y"), tuple(3, 'c', "z") 531 ])); 532 } 533 534 pure @safe nothrow @nogc unittest 535 { 536 import std.range.primitives : isForwardRange; 537 int[2] A = [1,2]; 538 auto C = cartesianProduct(A[], A[], A[]); 539 assert(isForwardRange!(typeof(C))); 540 541 C.popFront(); 542 auto front1 = C.front; 543 auto D = C.save; 544 C.popFront(); 545 assert(D.front == front1); 546 } 547 548 // https://issues.dlang.org/show_bug.cgi?id=13935 549 @safe unittest 550 { 551 import std.algorithm.iteration : map; 552 auto seq = [1, 2].map!(x => x); 553 foreach (pair; cartesianProduct(seq, seq)) {} 554 } 555 556 @system unittest 557 { 558 import std.algorithm.comparison : equal; 559 import std.typecons : tuple; 560 561 static struct SystemRange 562 { 563 int[] data; 564 565 int front() @system @property inout 566 { 567 return data[0]; 568 } 569 570 bool empty() @system @property inout 571 { 572 return data.length == 0; 573 } 574 575 void popFront() @system 576 { 577 data = data[1 .. $]; 578 } 579 580 SystemRange save() @system 581 { 582 return this; 583 } 584 } 585 586 assert(SystemRange([1, 2]).cartesianProduct(SystemRange([3, 4])) 587 .equal([tuple(1, 3), tuple(1, 4), tuple(2, 3), tuple(2, 4)])); 588 } 589 590 // largestPartialIntersection 591 /** 592 Given a range of sorted $(REF_ALTTEXT forward ranges, isForwardRange, std,range,primitives) 593 `ror`, copies to `tgt` the elements that are common to most ranges, along with their number 594 of occurrences. All ranges in `ror` are assumed to be sorted by $(D 595 less). Only the most frequent `tgt.length` elements are returned. 596 597 Params: 598 less = The predicate the ranges are sorted by. 599 ror = A range of forward ranges sorted by `less`. 600 tgt = The target range to copy common elements to. 601 sorted = Whether the elements copied should be in sorted order. 602 603 The function `largestPartialIntersection` is useful for 604 e.g. searching an $(LINK2 https://en.wikipedia.org/wiki/Inverted_index, 605 inverted index) for the documents most 606 likely to contain some terms of interest. The complexity of the search 607 is $(BIGOH n * log(tgt.length)), where `n` is the sum of lengths of 608 all input ranges. This approach is faster than keeping an associative 609 array of the occurrences and then selecting its top items, and also 610 requires less memory (`largestPartialIntersection` builds its 611 result directly in `tgt` and requires no extra memory). 612 613 If at least one of the ranges is a multiset, then all occurences 614 of a duplicate element are taken into account. The result is 615 equivalent to merging all ranges and picking the most frequent 616 `tgt.length` elements. 617 618 Warning: Because `largestPartialIntersection` does not allocate 619 extra memory, it will leave `ror` modified. Namely, $(D 620 largestPartialIntersection) assumes ownership of `ror` and 621 discretionarily swaps and advances elements of it. If you want $(D 622 ror) to preserve its contents after the call, you may want to pass a 623 duplicate to `largestPartialIntersection` (and perhaps cache the 624 duplicate in between calls). 625 */ 626 void largestPartialIntersection 627 (alias less = "a < b", RangeOfRanges, Range) 628 (RangeOfRanges ror, Range tgt, SortOutput sorted = No.sortOutput) 629 { 630 struct UnitWeights 631 { 632 static int opIndex(ElementType!(ElementType!RangeOfRanges)) { return 1; } 633 } 634 return largestPartialIntersectionWeighted!less(ror, tgt, UnitWeights(), 635 sorted); 636 } 637 638 /// 639 @system unittest 640 { 641 import std.typecons : tuple, Tuple; 642 643 // Figure which number can be found in most arrays of the set of 644 // arrays below. 645 double[][] a = 646 [ 647 [ 1, 4, 7, 8 ], 648 [ 1, 7 ], 649 [ 1, 7, 8], 650 [ 4 ], 651 [ 7 ], 652 ]; 653 auto b = new Tuple!(double, uint)[1]; 654 // it will modify the input range, hence we need to create a duplicate 655 largestPartialIntersection(a.dup, b); 656 // First member is the item, second is the occurrence count 657 assert(b[0] == tuple(7.0, 4u)); 658 // 7.0 occurs in 4 out of 5 inputs, more than any other number 659 660 // If more of the top-frequent numbers are needed, just create a larger 661 // tgt range 662 auto c = new Tuple!(double, uint)[2]; 663 largestPartialIntersection(a, c); 664 assert(c[0] == tuple(1.0, 3u)); 665 // 1.0 occurs in 3 inputs 666 667 // multiset 668 double[][] x = 669 [ 670 [1, 1, 1, 1, 4, 7, 8], 671 [1, 7], 672 [1, 7, 8], 673 [4, 7], 674 [7] 675 ]; 676 auto y = new Tuple!(double, uint)[2]; 677 largestPartialIntersection(x.dup, y); 678 // 7.0 occurs 5 times 679 assert(y[0] == tuple(7.0, 5u)); 680 // 1.0 occurs 6 times 681 assert(y[1] == tuple(1.0, 6u)); 682 } 683 684 import std.algorithm.sorting : SortOutput; // FIXME 685 686 // largestPartialIntersectionWeighted 687 /** 688 Similar to `largestPartialIntersection`, but associates a weight 689 with each distinct element in the intersection. 690 691 If at least one of the ranges is a multiset, then all occurences 692 of a duplicate element are taken into account. The result 693 is equivalent to merging all input ranges and picking the highest 694 `tgt.length`, weight-based ranking elements. 695 696 Params: 697 less = The predicate the ranges are sorted by. 698 ror = A range of $(REF_ALTTEXT forward ranges, isForwardRange, std,range,primitives) 699 sorted by `less`. 700 tgt = The target range to copy common elements to. 701 weights = An associative array mapping elements to weights. 702 sorted = Whether the elements copied should be in sorted order. 703 704 */ 705 void largestPartialIntersectionWeighted 706 (alias less = "a < b", RangeOfRanges, Range, WeightsAA) 707 (RangeOfRanges ror, Range tgt, WeightsAA weights, SortOutput sorted = No.sortOutput) 708 { 709 import std.algorithm.iteration : group; 710 import std.algorithm.sorting : topNCopy; 711 712 if (tgt.empty) return; 713 alias InfoType = ElementType!Range; 714 bool heapComp(InfoType a, InfoType b) 715 { 716 return weights[a[0]] * a[1] > weights[b[0]] * b[1]; 717 } 718 topNCopy!heapComp(group(multiwayMerge!less(ror)), tgt, sorted); 719 } 720 721 /// 722 @system unittest 723 { 724 import std.typecons : tuple, Tuple; 725 726 // Figure which number can be found in most arrays of the set of 727 // arrays below, with specific per-element weights 728 double[][] a = 729 [ 730 [ 1, 4, 7, 8 ], 731 [ 1, 7 ], 732 [ 1, 7, 8], 733 [ 4 ], 734 [ 7 ], 735 ]; 736 auto b = new Tuple!(double, uint)[1]; 737 double[double] weights = [ 1:1.2, 4:2.3, 7:1.1, 8:1.1 ]; 738 largestPartialIntersectionWeighted(a, b, weights); 739 // First member is the item, second is the occurrence count 740 assert(b[0] == tuple(4.0, 2u)); 741 // 4.0 occurs 2 times -> 4.6 (2 * 2.3) 742 // 7.0 occurs 3 times -> 4.4 (3 * 1.1) 743 744 // multiset 745 double[][] x = 746 [ 747 [ 1, 1, 1, 4, 7, 8 ], 748 [ 1, 7 ], 749 [ 1, 7, 8], 750 [ 4 ], 751 [ 7 ], 752 ]; 753 auto y = new Tuple!(double, uint)[1]; 754 largestPartialIntersectionWeighted(x, y, weights); 755 assert(y[0] == tuple(1.0, 5u)); 756 // 1.0 occurs 5 times -> 1.2 * 5 = 6 757 } 758 759 @system unittest 760 { 761 import std.conv : text; 762 import std.typecons : tuple, Tuple, Yes; 763 764 double[][] a = 765 [ 766 [ 1, 4, 7, 8 ], 767 [ 1, 7 ], 768 [ 1, 7, 8], 769 [ 4 ], 770 [ 7 ], 771 ]; 772 auto b = new Tuple!(double, uint)[2]; 773 largestPartialIntersection(a, b, Yes.sortOutput); 774 assert(b == [ tuple(7.0, 4u), tuple(1.0, 3u) ][], text(b)); 775 assert(a[0].empty); 776 } 777 778 @system unittest 779 { 780 import std.conv : text; 781 import std.typecons : tuple, Tuple, Yes; 782 783 string[][] a = 784 [ 785 [ "1", "4", "7", "8" ], 786 [ "1", "7" ], 787 [ "1", "7", "8"], 788 [ "4" ], 789 [ "7" ], 790 ]; 791 auto b = new Tuple!(string, uint)[2]; 792 largestPartialIntersection(a, b, Yes.sortOutput); 793 assert(b == [ tuple("7", 4u), tuple("1", 3u) ][], text(b)); 794 } 795 796 @system unittest 797 { 798 import std.typecons : tuple, Tuple; 799 800 // Figure which number can be found in most arrays of the set of 801 // arrays below, with specific per-element weights 802 double[][] a = 803 [ 804 [ 1, 4, 7, 8 ], 805 [ 1, 7 ], 806 [ 1, 7, 8], 807 [ 4 ], 808 [ 7 ], 809 ]; 810 auto b = new Tuple!(double, uint)[1]; 811 double[double] weights = [ 1:1.2, 4:2.3, 7:1.1, 8:1.1 ]; 812 largestPartialIntersectionWeighted(a, b, weights); 813 // First member is the item, second is the occurrence count 814 assert(b[0] == tuple(4.0, 2u)); 815 } 816 817 @system unittest 818 { 819 import std.container : Array; 820 import std.typecons : Tuple; 821 822 alias T = Tuple!(uint, uint); 823 const Array!T arrayOne = Array!T( [ T(1,2), T(3,4) ] ); 824 const Array!T arrayTwo = Array!T([ T(1,2), T(3,4) ] ); 825 826 assert(arrayOne == arrayTwo); 827 } 828 829 // MultiwayMerge 830 /** 831 Merges multiple sets. The input sets are passed as a 832 range of ranges and each is assumed to be sorted by $(D 833 less). Computation is done lazily, one union element at a time. The 834 complexity of one `popFront` operation is $(BIGOH 835 log(ror.length)). However, the length of `ror` decreases as ranges 836 in it are exhausted, so the complexity of a full pass through $(D 837 MultiwayMerge) is dependent on the distribution of the lengths of ranges 838 contained within `ror`. If all ranges have the same length `n` 839 (worst case scenario), the complexity of a full pass through $(D 840 MultiwayMerge) is $(BIGOH n * ror.length * log(ror.length)), i.e., $(D 841 log(ror.length)) times worse than just spanning all ranges in 842 turn. The output comes sorted (unstably) by `less`. 843 844 The length of the resulting range is the sum of all lengths of 845 the ranges passed as input. This means that all elements (duplicates 846 included) are transferred to the resulting range. 847 848 For backward compatibility, `multiwayMerge` is available under 849 the name `nWayUnion` and `MultiwayMerge` under the name of `NWayUnion` . 850 Future code should use `multiwayMerge` and `MultiwayMerge` as `nWayUnion` 851 and `NWayUnion` will be deprecated. 852 853 Params: 854 less = Predicate the given ranges are sorted by. 855 ror = A range of ranges sorted by `less` to compute the union for. 856 857 Returns: 858 A range of the union of the ranges in `ror`. 859 860 Warning: Because `MultiwayMerge` does not allocate extra memory, it 861 will leave `ror` modified. Namely, `MultiwayMerge` assumes ownership 862 of `ror` and discretionarily swaps and advances elements of it. If 863 you want `ror` to preserve its contents after the call, you may 864 want to pass a duplicate to `MultiwayMerge` (and perhaps cache the 865 duplicate in between calls). 866 867 See_Also: $(REF merge, std,algorithm,sorting) for an analogous function that 868 takes a static number of ranges of possibly disparate types. 869 */ 870 struct MultiwayMerge(alias less, RangeOfRanges) 871 { 872 import std.container : BinaryHeap; 873 874 private alias ElementType = .ElementType!(.ElementType!RangeOfRanges); 875 private alias comp = binaryFun!less; 876 private RangeOfRanges _ror; 877 878 /// 879 static bool compFront(.ElementType!RangeOfRanges a, 880 .ElementType!RangeOfRanges b) 881 { 882 // revert comparison order so we get the smallest elements first 883 return comp(b.front, a.front); 884 } 885 private BinaryHeap!(RangeOfRanges, compFront) _heap; 886 887 /// 888 this(RangeOfRanges ror) 889 { 890 import std.algorithm.mutation : remove, SwapStrategy; 891 892 // Preemptively get rid of all empty ranges in the input 893 // No need for stability either 894 _ror = remove!("a.empty", SwapStrategy.unstable)(ror); 895 //Build the heap across the range 896 _heap.acquire(_ror); 897 } 898 899 /// 900 @property bool empty() { return _ror.empty; } 901 902 /// 903 @property auto ref front() 904 { 905 return _heap.front.front; 906 } 907 908 /// 909 void popFront() 910 { 911 _heap.removeFront(); 912 // let's look at the guy just popped 913 _ror.back.popFront(); 914 if (_ror.back.empty) 915 { 916 _ror.popBack(); 917 // nothing else to do: the empty range is not in the 918 // heap and not in _ror 919 return; 920 } 921 // Put the popped range back in the heap 922 const bool worked = _heap.conditionalInsert(_ror.back); 923 assert(worked, "Failed to insert item into heap"); 924 } 925 } 926 927 /// Ditto 928 MultiwayMerge!(less, RangeOfRanges) multiwayMerge 929 (alias less = "a < b", RangeOfRanges) 930 (RangeOfRanges ror) 931 { 932 return typeof(return)(ror); 933 } 934 935 /// 936 @system unittest 937 { 938 import std.algorithm.comparison : equal; 939 940 double[][] a = 941 [ 942 [ 1, 4, 7, 8 ], 943 [ 1, 7 ], 944 [ 1, 7, 8], 945 [ 4 ], 946 [ 7 ], 947 ]; 948 auto witness = [ 949 1, 1, 1, 4, 4, 7, 7, 7, 7, 8, 8 950 ]; 951 assert(equal(multiwayMerge(a), witness)); 952 953 double[][] b = 954 [ 955 // range with duplicates 956 [ 1, 1, 4, 7, 8 ], 957 [ 7 ], 958 [ 1, 7, 8], 959 [ 4 ], 960 [ 7 ], 961 ]; 962 // duplicates are propagated to the resulting range 963 assert(equal(multiwayMerge(b), witness)); 964 } 965 966 alias nWayUnion = multiwayMerge; 967 alias NWayUnion = MultiwayMerge; 968 969 /** 970 Computes the union of multiple ranges. The 971 $(REF_ALTTEXT input ranges, isInputRange, std,range,primitives) are passed 972 as a range of ranges and each is assumed to be sorted by $(D 973 less). Computation is done lazily, one union element at a time. 974 `multiwayUnion(ror)` is functionally equivalent to `multiwayMerge(ror).uniq`. 975 976 "The output of multiwayUnion has no duplicates even when its inputs contain duplicates." 977 978 Params: 979 less = Predicate the given ranges are sorted by. 980 ror = A range of ranges sorted by `less` to compute the intersection for. 981 982 Returns: 983 A range of the union of the ranges in `ror`. 984 985 See also: $(LREF multiwayMerge) 986 */ 987 auto multiwayUnion(alias less = "a < b", RangeOfRanges)(RangeOfRanges ror) 988 { 989 import std.algorithm.iteration : uniq; 990 import std.functional : not; 991 return ror.multiwayMerge!(less).uniq!(not!less); 992 } 993 994 /// 995 @system unittest 996 { 997 import std.algorithm.comparison : equal; 998 999 // sets 1000 double[][] a = 1001 [ 1002 [ 1, 4, 7, 8 ], 1003 [ 1, 7 ], 1004 [ 1, 7, 8], 1005 [ 4 ], 1006 [ 7 ], 1007 ]; 1008 1009 auto witness = [1, 4, 7, 8]; 1010 assert(equal(multiwayUnion(a), witness)); 1011 1012 // multisets 1013 double[][] b = 1014 [ 1015 [ 1, 1, 1, 4, 7, 8 ], 1016 [ 1, 7 ], 1017 [ 1, 7, 7, 8], 1018 [ 4 ], 1019 [ 7 ], 1020 ]; 1021 assert(equal(multiwayUnion(b), witness)); 1022 1023 double[][] c = 1024 [ 1025 [9, 8, 8, 8, 7, 6], 1026 [9, 8, 6], 1027 [9, 8, 5] 1028 ]; 1029 auto witness2 = [9, 8, 7, 6, 5]; 1030 assert(equal(multiwayUnion!"a > b"(c), witness2)); 1031 } 1032 1033 /** 1034 Lazily computes the difference of `r1` and `r2`. The two ranges 1035 are assumed to be sorted by `less`. The element types of the two 1036 ranges must have a common type. 1037 1038 1039 In the case of multisets, considering that element `a` appears `x` 1040 times in `r1` and `y` times and `r2`, the number of occurences 1041 of `a` in the resulting range is going to be `x-y` if x > y or 0 otherwise. 1042 1043 Params: 1044 less = Predicate the given ranges are sorted by. 1045 r1 = The first range. 1046 r2 = The range to subtract from `r1`. 1047 1048 Returns: 1049 A range of the difference of `r1` and `r2`. 1050 1051 See_also: $(LREF setSymmetricDifference) 1052 */ 1053 struct SetDifference(alias less = "a < b", R1, R2) 1054 if (isInputRange!(R1) && isInputRange!(R2)) 1055 { 1056 private: 1057 R1 r1; 1058 R2 r2; 1059 alias comp = binaryFun!(less); 1060 1061 void adjustPosition() 1062 { 1063 while (!r1.empty) 1064 { 1065 if (r2.empty || comp(r1.front, r2.front)) break; 1066 if (comp(r2.front, r1.front)) 1067 { 1068 r2.popFront(); 1069 } 1070 else 1071 { 1072 // both are equal 1073 r1.popFront(); 1074 r2.popFront(); 1075 } 1076 } 1077 } 1078 1079 public: 1080 /// 1081 this(R1 r1, R2 r2) 1082 { 1083 this.r1 = r1; 1084 this.r2 = r2; 1085 // position to the first element 1086 adjustPosition(); 1087 } 1088 1089 /// 1090 void popFront() 1091 { 1092 r1.popFront(); 1093 adjustPosition(); 1094 } 1095 1096 /// 1097 @property auto ref front() 1098 { 1099 assert(!empty, "Can not get front of empty SetDifference"); 1100 return r1.front; 1101 } 1102 1103 static if (isForwardRange!R1 && isForwardRange!R2) 1104 { 1105 /// 1106 @property typeof(this) save() 1107 { 1108 auto ret = this; 1109 ret.r1 = r1.save; 1110 ret.r2 = r2.save; 1111 return ret; 1112 } 1113 } 1114 1115 /// 1116 @property bool empty() { return r1.empty; } 1117 } 1118 1119 /// Ditto 1120 SetDifference!(less, R1, R2) setDifference(alias less = "a < b", R1, R2) 1121 (R1 r1, R2 r2) 1122 { 1123 return typeof(return)(r1, r2); 1124 } 1125 1126 /// 1127 @safe unittest 1128 { 1129 import std.algorithm.comparison : equal; 1130 import std.range.primitives : isForwardRange; 1131 1132 //sets 1133 int[] a = [ 1, 2, 4, 5, 7, 9 ]; 1134 int[] b = [ 0, 1, 2, 4, 7, 8 ]; 1135 assert(equal(setDifference(a, b), [5, 9])); 1136 static assert(isForwardRange!(typeof(setDifference(a, b)))); 1137 1138 // multisets 1139 int[] x = [1, 1, 1, 2, 3]; 1140 int[] y = [1, 1, 2, 4, 5]; 1141 auto r = setDifference(x, y); 1142 assert(equal(r, [1, 3])); 1143 assert(setDifference(r, x).empty); 1144 } 1145 1146 // https://issues.dlang.org/show_bug.cgi?id=10460 1147 @safe unittest 1148 { 1149 import std.algorithm.comparison : equal; 1150 1151 int[] a = [1, 2, 3, 4, 5]; 1152 int[] b = [2, 4]; 1153 foreach (ref e; setDifference(a, b)) 1154 e = 0; 1155 assert(equal(a, [0, 2, 0, 4, 0])); 1156 } 1157 1158 /** 1159 Lazily computes the intersection of two or more 1160 $(REF_ALTTEXT input ranges, isInputRange, std,range,primitives) 1161 `ranges`. The ranges are assumed to be sorted by `less`. The element 1162 types of the ranges must have a common type. 1163 1164 In the case of multisets, the range with the minimum number of 1165 occurences of a given element, propagates the number of 1166 occurences of this element to the resulting range. 1167 1168 Params: 1169 less = Predicate the given ranges are sorted by. 1170 ranges = The ranges to compute the intersection for. 1171 1172 Returns: 1173 A range containing the intersection of the given ranges. 1174 */ 1175 struct SetIntersection(alias less = "a < b", Rs...) 1176 if (Rs.length >= 2 && allSatisfy!(isInputRange, Rs) && 1177 !is(CommonType!(staticMap!(ElementType, Rs)) == void)) 1178 { 1179 private: 1180 Rs _input; 1181 alias comp = binaryFun!less; 1182 alias ElementType = CommonType!(staticMap!(.ElementType, Rs)); 1183 1184 // Positions to the first elements that are all equal 1185 void adjustPosition() 1186 { 1187 if (empty) return; 1188 1189 size_t done = Rs.length; 1190 static if (Rs.length > 1) while (true) 1191 { 1192 foreach (i, ref r; _input) 1193 { 1194 alias next = _input[(i + 1) % Rs.length]; 1195 1196 if (comp(next.front, r.front)) 1197 { 1198 do 1199 { 1200 next.popFront(); 1201 if (next.empty) return; 1202 } while (comp(next.front, r.front)); 1203 done = Rs.length; 1204 } 1205 if (--done == 0) return; 1206 } 1207 } 1208 } 1209 1210 public: 1211 /// 1212 this(Rs input) 1213 { 1214 this._input = input; 1215 // position to the first element 1216 adjustPosition(); 1217 } 1218 1219 /// 1220 @property bool empty() 1221 { 1222 foreach (ref r; _input) 1223 { 1224 if (r.empty) return true; 1225 } 1226 return false; 1227 } 1228 1229 /// 1230 void popFront() 1231 { 1232 assert(!empty, "Can not popFront of empty SetIntersection"); 1233 static if (Rs.length > 1) foreach (i, ref r; _input) 1234 { 1235 alias next = _input[(i + 1) % Rs.length]; 1236 assert(!comp(r.front, next.front), "Set elements must not" 1237 ~ " contradict the less predicate"); 1238 } 1239 1240 foreach (ref r; _input) 1241 { 1242 r.popFront(); 1243 } 1244 adjustPosition(); 1245 } 1246 1247 /// 1248 @property ElementType front() 1249 { 1250 assert(!empty, "Can not get front of empty SetIntersection"); 1251 return _input[0].front; 1252 } 1253 1254 static if (allSatisfy!(isForwardRange, Rs)) 1255 { 1256 /// 1257 @property SetIntersection save() 1258 { 1259 auto ret = this; 1260 foreach (i, ref r; _input) 1261 { 1262 ret._input[i] = r.save; 1263 } 1264 return ret; 1265 } 1266 } 1267 } 1268 1269 /// Ditto 1270 SetIntersection!(less, Rs) setIntersection(alias less = "a < b", Rs...)(Rs ranges) 1271 if (Rs.length >= 2 && allSatisfy!(isInputRange, Rs) && 1272 !is(CommonType!(staticMap!(ElementType, Rs)) == void)) 1273 { 1274 return typeof(return)(ranges); 1275 } 1276 1277 /// 1278 @safe unittest 1279 { 1280 import std.algorithm.comparison : equal; 1281 1282 // sets 1283 int[] a = [ 1, 2, 4, 5, 7, 9 ]; 1284 int[] b = [ 0, 1, 2, 4, 7, 8 ]; 1285 int[] c = [ 0, 1, 4, 5, 7, 8 ]; 1286 assert(equal(setIntersection(a, a), a)); 1287 assert(equal(setIntersection(a, b), [1, 2, 4, 7])); 1288 assert(equal(setIntersection(a, b, c), [1, 4, 7])); 1289 1290 // multisets 1291 int[] d = [ 1, 1, 2, 2, 7, 7 ]; 1292 int[] e = [ 1, 1, 1, 7]; 1293 assert(equal(setIntersection(a, d), [1, 2, 7])); 1294 assert(equal(setIntersection(d, e), [1, 1, 7])); 1295 } 1296 1297 @safe unittest 1298 { 1299 import std.algorithm.comparison : equal; 1300 import std.algorithm.iteration : filter; 1301 1302 int[] a = [ 1, 2, 4, 5, 7, 9 ]; 1303 int[] b = [ 0, 1, 2, 4, 7, 8 ]; 1304 int[] c = [ 0, 1, 4, 5, 7, 8 ]; 1305 int[] d = [ 1, 3, 4 ]; 1306 int[] e = [ 4, 5 ]; 1307 1308 assert(equal(setIntersection(a, a), a)); 1309 assert(equal(setIntersection(a, a, a), a)); 1310 assert(equal(setIntersection(a, b), [1, 2, 4, 7])); 1311 assert(equal(setIntersection(a, b, c), [1, 4, 7])); 1312 assert(equal(setIntersection(a, b, c, d), [1, 4])); 1313 assert(equal(setIntersection(a, b, c, d, e), [4])); 1314 1315 auto inpA = a.filter!(_ => true), inpB = b.filter!(_ => true); 1316 auto inpC = c.filter!(_ => true), inpD = d.filter!(_ => true); 1317 assert(equal(setIntersection(inpA, inpB, inpC, inpD), [1, 4])); 1318 1319 assert(equal(setIntersection(a, b, b, a), [1, 2, 4, 7])); 1320 assert(equal(setIntersection(a, c, b), [1, 4, 7])); 1321 assert(equal(setIntersection(b, a, c), [1, 4, 7])); 1322 assert(equal(setIntersection(b, c, a), [1, 4, 7])); 1323 assert(equal(setIntersection(c, a, b), [1, 4, 7])); 1324 assert(equal(setIntersection(c, b, a), [1, 4, 7])); 1325 } 1326 1327 /** 1328 Lazily computes the symmetric difference of `r1` and `r2`, 1329 i.e. the elements that are present in exactly one of `r1` and $(D 1330 r2). The two ranges are assumed to be sorted by `less`, and the 1331 output is also sorted by `less`. The element types of the two 1332 ranges must have a common type. 1333 1334 If both ranges are sets (without duplicated elements), the resulting 1335 range is going to be a set. If at least one of the ranges is a multiset, 1336 the number of occurences of an element `x` in the resulting range is `abs(a-b)` 1337 where `a` is the number of occurences of `x` in `r1`, `b` is the number of 1338 occurences of `x` in `r2`, and `abs` is the absolute value. 1339 1340 If both arguments are ranges of L-values of the same type then 1341 `SetSymmetricDifference` will also be a range of L-values of 1342 that type. 1343 1344 Params: 1345 less = Predicate the given ranges are sorted by. 1346 r1 = The first range. 1347 r2 = The second range. 1348 1349 Returns: 1350 A range of the symmetric difference between `r1` and `r2`. 1351 1352 See_also: $(LREF setDifference) 1353 */ 1354 struct SetSymmetricDifference(alias less = "a < b", R1, R2) 1355 if (isInputRange!(R1) && isInputRange!(R2)) 1356 { 1357 private: 1358 R1 r1; 1359 R2 r2; 1360 //bool usingR2; 1361 alias comp = binaryFun!(less); 1362 1363 void adjustPosition() 1364 { 1365 while (!r1.empty && !r2.empty) 1366 { 1367 if (comp(r1.front, r2.front) || comp(r2.front, r1.front)) 1368 { 1369 break; 1370 } 1371 // equal, pop both 1372 r1.popFront(); 1373 r2.popFront(); 1374 } 1375 } 1376 1377 public: 1378 /// 1379 this(R1 r1, R2 r2) 1380 { 1381 this.r1 = r1; 1382 this.r2 = r2; 1383 // position to the first element 1384 adjustPosition(); 1385 } 1386 1387 /// 1388 void popFront() 1389 { 1390 assert(!empty, "Can not popFront of empty SetSymmetricDifference"); 1391 if (r1.empty) r2.popFront(); 1392 else if (r2.empty) r1.popFront(); 1393 else 1394 { 1395 // neither is empty 1396 if (comp(r1.front, r2.front)) 1397 { 1398 r1.popFront(); 1399 } 1400 else 1401 { 1402 assert(comp(r2.front, r1.front), "Elements of R1 and R2" 1403 ~ " must be different"); 1404 r2.popFront(); 1405 } 1406 } 1407 adjustPosition(); 1408 } 1409 1410 /// 1411 @property auto ref front() 1412 { 1413 assert(!empty, "Can not get the front of an empty" 1414 ~ " SetSymmetricDifference"); 1415 immutable chooseR1 = r2.empty || !r1.empty && comp(r1.front, r2.front); 1416 assert(chooseR1 || r1.empty || comp(r2.front, r1.front), "Failed to" 1417 ~ " get appropriate front"); 1418 return chooseR1 ? r1.front : r2.front; 1419 } 1420 1421 static if (isForwardRange!R1 && isForwardRange!R2) 1422 { 1423 /// 1424 @property typeof(this) save() 1425 { 1426 auto ret = this; 1427 ret.r1 = r1.save; 1428 ret.r2 = r2.save; 1429 return ret; 1430 } 1431 } 1432 1433 /// 1434 ref auto opSlice() { return this; } 1435 1436 /// 1437 @property bool empty() { return r1.empty && r2.empty; } 1438 } 1439 1440 /// Ditto 1441 SetSymmetricDifference!(less, R1, R2) 1442 setSymmetricDifference(alias less = "a < b", R1, R2) 1443 (R1 r1, R2 r2) 1444 { 1445 return typeof(return)(r1, r2); 1446 } 1447 1448 /// 1449 @safe unittest 1450 { 1451 import std.algorithm.comparison : equal; 1452 import std.range.primitives : isForwardRange; 1453 1454 // sets 1455 int[] a = [ 1, 2, 4, 5, 7, 9 ]; 1456 int[] b = [ 0, 1, 2, 4, 7, 8 ]; 1457 assert(equal(setSymmetricDifference(a, b), [0, 5, 8, 9][])); 1458 static assert(isForwardRange!(typeof(setSymmetricDifference(a, b)))); 1459 1460 //mutisets 1461 int[] c = [1, 1, 1, 1, 2, 2, 2, 4, 5, 6]; 1462 int[] d = [1, 1, 2, 2, 2, 2, 4, 7, 9]; 1463 assert(equal(setSymmetricDifference(c, d), setSymmetricDifference(d, c))); 1464 assert(equal(setSymmetricDifference(c, d), [1, 1, 2, 5, 6, 7, 9])); 1465 } 1466 1467 // https://issues.dlang.org/show_bug.cgi?id=10460 1468 @safe unittest 1469 { 1470 import std.algorithm.comparison : equal; 1471 1472 int[] a = [1, 2]; 1473 double[] b = [2.0, 3.0]; 1474 int[] c = [2, 3]; 1475 1476 alias R1 = typeof(setSymmetricDifference(a, b)); 1477 static assert(is(ElementType!R1 == double)); 1478 static assert(!hasLvalueElements!R1); 1479 1480 alias R2 = typeof(setSymmetricDifference(a, c)); 1481 static assert(is(ElementType!R2 == int)); 1482 static assert(hasLvalueElements!R2); 1483 1484 assert(equal(setSymmetricDifference(a, b), [1.0, 3.0])); 1485 assert(equal(setSymmetricDifference(a, c), [1, 3])); 1486 } 1487 1488 /++ 1489 TODO: once SetUnion got deprecated we can provide the usual definition 1490 (= merge + filter after uniqs) 1491 See: https://github.com/dlang/phobos/pull/4249 1492 /** 1493 Lazily computes the union of two or more ranges `rs`. The ranges 1494 are assumed to be sorted by `less`. Elements in the output are 1495 unique. The element types of all ranges must have a common type. 1496 1497 Params: 1498 less = Predicate the given ranges are sorted by. 1499 rs = The ranges to compute the union for. 1500 1501 Returns: 1502 A range containing the unique union of the given ranges. 1503 1504 See_Also: 1505 $(REF merge, std,algorithm,sorting) 1506 */ 1507 auto setUnion(alias less = "a < b", Rs...) 1508 (Rs rs) 1509 { 1510 import std.algorithm.iteration : uniq; 1511 import std.algorithm.sorting : merge; 1512 return merge!(less, Rs)(rs).uniq; 1513 } 1514 1515 /// 1516 @safe pure nothrow unittest 1517 /// 1518 { 1519 import std.algorithm.comparison : equal; 1520 1521 int[] a = [1, 3, 5]; 1522 int[] b = [2, 3, 4]; 1523 assert(a.setUnion(b).equal([1, 2, 3, 4, 5])); 1524 } 1525 1526 @safe pure nothrow unittest 1527 { 1528 import std.algorithm.comparison : equal; 1529 1530 int[] a = [ 1, 2, 4, 5, 7, 9 ]; 1531 int[] b = [ 0, 1, 2, 4, 7, 8 ]; 1532 double[] c = [ 10.5 ]; 1533 1534 assert(equal(setUnion(a, b), [0, 1, 2, 4, 5, 7, 8, 9][])); 1535 assert(equal(setUnion(a, c, b), 1536 [0, 1, 2, 4, 5, 7, 8, 9, 10.5][])); 1537 } 1538 1539 @safe unittest 1540 { 1541 // save 1542 import std.range : dropOne; 1543 int[] a = [0, 1, 2]; 1544 int[] b = [0, 3]; 1545 auto arr = a.setUnion(b); 1546 assert(arr.front == 0); 1547 assert(arr.save.dropOne.front == 1); 1548 assert(arr.front == 0); 1549 } 1550 1551 @nogc @safe pure nothrow unittest 1552 { 1553 import std.algorithm.comparison : equal; 1554 1555 static immutable a = [1, 3, 5]; 1556 static immutable b = [2, 4]; 1557 static immutable r = [1, 2, 3, 4, 5]; 1558 assert(a.setUnion(b).equal(r)); 1559 } 1560 1561 @safe pure nothrow unittest 1562 { 1563 import std.algorithm.comparison : equal; 1564 import std.internal.test.dummyrange; 1565 import std.range : iota; 1566 1567 auto dummyResult1 = [1, 1.5, 2, 3, 4, 5, 5.5, 6, 7, 8, 9, 10]; 1568 auto dummyResult2 = iota(1, 11); 1569 foreach (DummyType; AllDummyRanges) 1570 { 1571 DummyType d; 1572 assert(d.setUnion([1, 1.5, 5.5]).equal(dummyResult1)); 1573 assert(d.setUnion(d).equal(dummyResult2)); 1574 } 1575 } 1576 ++/