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