1 /// "Each with each other" random range
2 module eerange.base;
3 
4 @safe:
5 
6 ///
7 @nogc struct EachWithEachOtherRangeBase(R)
8 {
9     private R srcRange;
10 
11     ///
12     this(R r)
13     {
14         srcRange = r;
15     }
16 
17     ///
18     size_t length() const pure
19     {
20         const len = srcRange.length;
21 
22         return (len * len - len) / 2;
23     }
24 
25     private static struct Coords
26     {
27         size_t x;
28         size_t y;
29     }
30 
31     private Coords coordsInSquare(size_t idx) const pure
32     {
33         return Coords(
34             idx % srcRange.length,
35             idx / srcRange.length
36         );
37     }
38 
39     import std.range.primitives: ElementType;
40 
41     private alias T = ElementType!R;
42 
43     private T[2] getElemBySquareCoords(Coords c) const pure
44     {
45         return [srcRange[c.x], srcRange[c.y]];
46     }
47 
48     ///
49     T[2] opIndex(size_t idx) const pure
50     {
51         assert(idx < length);
52 
53         Coords coords = coordsInSquare(idx);
54 
55         if(coords.x <= coords.y) // under diagonal line?
56         {
57             const latestIdx = srcRange.length - 1;
58 
59             // Mirroring coords
60             coords.x = latestIdx - coords.x;
61             coords.y = latestIdx - coords.y - 1; // shifted above diagonal
62         }
63 
64         return getElemBySquareCoords(coords);
65     }
66 }
67 
68 unittest
69 {
70     import std.parallelism;
71 
72     int[] arr = [100, 200, 300, 400];
73 
74     auto r = EachWithEachOtherRangeBase!(int[])(arr);
75 
76     size_t cnt;
77 
78     foreach(i; 0 .. r.length)
79     {
80         auto pair = r[i];
81 
82         cnt++;
83     }
84 
85     assert(cnt == 6);
86 }