1 ///
2 module eerange.base;
3 
4 @safe:
5 
6 ///
7 struct EachWithEachOtherRangeBase
8 {
9     @nogc:
10 
11     package size_t srcLength;
12 
13     ///
14     this(size_t srcLen) pure nothrow
15     {
16         srcLength = srcLen;
17     }
18 
19     ///
20     size_t length() pure const nothrow
21     {
22         const len = srcLength;
23 
24         return (len * len - len) / 2;
25     }
26 
27     private static struct Coords
28     {
29         size_t x;
30         size_t y;
31     }
32 
33     private Coords coordsInSquare(size_t idx) pure
34     {
35         return Coords(
36             idx % srcLength,
37             idx / srcLength
38         );
39     }
40 
41     ///
42     size_t[2] opIndex(size_t idx) pure
43     {
44         version(D_NoBoundsChecks){}
45         else
46         {
47             import core.exception: RangeError;
48 
49             if(idx >= length)
50                 throw new RangeError;
51         }
52 
53         Coords coords = coordsInSquare(idx);
54 
55         import std.traits;
56         static assert(isMutable!(typeof(coords)));
57 
58         if(coords.x <= coords.y) // under diagonal line?
59         {
60             const latestIdx = srcLength - 1;
61 
62             // Mirroring coords
63             coords.x = latestIdx - coords.x;
64             coords.y = latestIdx - coords.y - 1; // shifted above diagonal
65         }
66 
67         return [coords.x, coords.y];
68     }
69 }
70 
71 unittest
72 {
73     import std.parallelism;
74 
75     enum srcLen = 4;
76 
77     auto r = EachWithEachOtherRangeBase(srcLen);
78 
79     size_t cnt;
80 
81     foreach(i; 0 .. r.length)
82     {
83         auto pair = r[i];
84 
85         cnt++;
86     }
87 
88     assert(cnt == 6);
89 }