1 /// "Each with each other" random range
2 module eerange.randomrange;
3 
4 import eerange.base;
5 
6 @safe:
7 
8 ///
9 struct EachWithEachOtherRandomAccessRange
10 {
11     @nogc:
12 
13     EachWithEachOtherRangeBase base;
14 
15     private size_t fwdIdx;
16     private ptrdiff_t backIdx;
17     private size_t sliceStart; /// slice index start
18     private size_t sliceEnd; ///  slice index end
19 
20     ///
21     this(size_t srcLen, size_t _sliceStart = 0, size_t _sliceEnd = 0) pure nothrow
22     {
23         base = EachWithEachOtherRangeBase(srcLen);
24 
25         sliceStart = _sliceStart;
26         sliceEnd = _sliceEnd ? _sliceEnd : base.length;
27 
28         assert(sliceStart <= sliceEnd);
29         //~ import std.conv: to;
30         //~ assert(sliceStart <= sliceEnd, "srcLen="~srcLen.to!string~" sliceStart="~sliceStart.to!string~" sliceEnd="~sliceEnd.to!string);
31 
32         fwdIdx = sliceStart;
33         backIdx = sliceEnd - 1;
34     }
35 
36     ///
37     size_t length() pure const nothrow
38     {
39         return sliceEnd - sliceStart;
40     }
41 
42     ///
43     size_t[2] opIndex(size_t idx) pure
44     {
45         return base.opIndex(sliceStart + idx);
46     }
47 
48     ///
49     bool empty() const @nogc
50     {
51         return fwdIdx >= sliceEnd || backIdx < sliceStart;
52     }
53 
54     private void checkEmpty()
55     {
56         version(D_NoBoundsChecks){}
57         else
58         {
59             import core.exception: RangeError;
60 
61             if(empty)
62                 throw new RangeError;
63         }
64     }
65 
66     auto front() {
67         checkEmpty();
68 
69         return base.opIndex(fwdIdx);
70     }
71 
72     auto back() {
73         checkEmpty();
74 
75         return base.opIndex(backIdx);
76     }
77 
78     ///
79     void popFront() { fwdIdx++; }
80 
81     ///
82     void popBack() { backIdx--; }
83 
84     ///
85     EachWithEachOtherRandomAccessRange save() { return this; }
86 
87     ///
88     EachWithEachOtherRandomAccessRange opSlice(size_t from, size_t to)
89     {
90         return EachWithEachOtherRandomAccessRange(base.srcLength, sliceStart + from, sliceStart + to);
91     }
92 
93     ///
94     size_t opDollar(size_t pos)()
95     if(pos == 0)
96     {
97         return length;
98     }
99 }
100 
101 unittest
102 {
103     import std.range.primitives;
104     import std.traits;
105 
106     alias R = EachWithEachOtherRandomAccessRange;
107 
108     static assert(hasLength!R);
109     static assert(is(typeof(lvalueOf!R[1]) == ElementType!R));
110     static assert(isInputRange!R);
111     static assert(!isNarrowString!R);
112     static assert(is(ReturnType!((R r) => r.save) == R));
113     static assert(isForwardRange!R);
114     static assert(isBidirectionalRange!R);
115     static assert(isRandomAccessRange!R);
116 }
117 
118 ///
119 alias eweo = EachWithEachOtherRandomAccessRange;
120 
121 @trusted unittest
122 {
123     import std.parallelism;
124     import std.format;
125 
126     enum ubyte testSize = 100;
127 
128     auto eeRandom = eweo(testSize);
129     auto slice1 = eeRandom[0 .. 50];
130     auto slice2 = eeRandom[50 .. $];
131 
132     auto randomRes1 = taskPool.amap!("a[0]", "a[1]")(slice1);
133     auto randomRes2 = taskPool.amap!("a[0]", "a[1]")(slice2);
134 
135     size_t[testSize] cnt;
136 
137     foreach(r; randomRes1)
138     {
139         cnt[r[0]]++;
140         cnt[r[1]]++;
141     }
142 
143     foreach(r; randomRes2)
144     {
145         cnt[r[0]]++;
146         cnt[r[1]]++;
147     }
148 
149     foreach(i, r; cnt)
150         assert(r == testSize-1, format("%d %d", i, r));
151 }
152 
153 unittest
154 {
155     auto eeRandom = eweo(4);
156 
157     auto slice = eeRandom[0 .. $];
158 
159     assert(slice.length == eeRandom.length);
160 }
161 
162 unittest
163 {
164     auto zero = eweo(0);
165 
166     assert(zero.empty);
167 
168     auto single = eweo(1);
169 
170     assert(single.empty);
171 }