Skip to content

Commit 710d0da

Browse files
authored
add nogc tarjan algorithm (#219)
1 parent 2c5e8e5 commit 710d0da

File tree

4 files changed

+109
-30
lines changed

4 files changed

+109
-30
lines changed

source/mir/graph/package.d

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import mir.math.common: optmath;
1717
@optmath:
1818

1919
import mir.series;
20+
import mir.rc.array;
2021
import mir.ndslice.iterator: ChopIterator;
2122

2223
///
@@ -26,6 +27,13 @@ alias Graph(I = uint, J = size_t) = Slice!(GraphIterator!(I, J));
2627
///
2728
alias GraphSeries(T, I = uint, J = size_t) = Series!(T*, GraphIterator!(I, J));
2829

30+
///
31+
alias RCGraphIterator(I = uint, J = size_t) = ChopIterator!(RCI!size_t, RCI!uint);
32+
///
33+
alias RCGraph(I = uint, J = size_t) = Slice!(RCGraphIterator!(I, J));
34+
///
35+
alias RCGraphSeries(T, I = uint, J = size_t) = Series!(RCI!T, RCGraphIterator!(I, J));
36+
2937
/++
3038
Param:
3139
aaGraph = graph that is represented as associative array

source/mir/graph/tarjan.d

Lines changed: 66 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ The implementation is loop based. It does not use recursion and does not have st
3232
Complexity: worst-case `O(|V| + |E|)`.
3333
3434
Params:
35+
RC = nogc mode, refcounted output
3536
graph = components (ndslice) sorted in the direction of traversal of the graph. Each component is an array of indeces.
3637
Returns:
3738
components (ndslice of arrays of indexes)
@@ -44,7 +45,7 @@ See_also:
4445
$(SUBREF utility, graph)
4546
+/
4647
pragma(inline, false)
47-
auto tarjan(G, I = Unqual!(ForeachType!(ForeachType!G)))(G graph)
48+
auto tarjan(bool RC = false, G, I = Unqual!(ForeachType!(ForeachType!G)))(G graph)
4849
if (isUnsigned!I)
4950
{
5051
import mir.utility: min;
@@ -87,35 +88,49 @@ auto tarjan(G, I = Unqual!(ForeachType!(ForeachType!G)))(G graph)
8788
}
8889
}
8990

90-
bool[] onStack = new bool[graph.length];
91-
I[] stack;
92-
IndexNode[] indeces;
93-
LoopNode[] loopStack;
94-
I index;
91+
9592
sizediff_t stackIndex;
9693
sizediff_t backStackIndex = graph.length;
9794
sizediff_t componentBackStackIndex = graph.length + 1;
9895

99-
if (__ctfe)
96+
static if (RC)
10097
{
101-
stack = new I[graph.length];
102-
indeces = new IndexNode[graph.length];
103-
loopStack = new LoopNode[componentBackStackIndex];
98+
import mir.rc.array;
99+
auto onStack = RCArray!bool(graph.length);
100+
auto stack = RCArray!I(graph.length, true);
101+
auto indeces = RCArray!IndexNode(graph.length, true);
102+
auto loopStack = RCArray!LoopNode(componentBackStackIndex, true);
104103
}
105104
else
106105
{
107-
() @trusted {
108-
import std.array: uninitializedArray;
106+
I[] stack;
107+
IndexNode[] indeces;
108+
LoopNode[] loopStack;
109+
110+
bool[] onStack = new bool[graph.length];
111+
if (__ctfe)
112+
{
113+
114+
stack = new I[graph.length];
115+
indeces = new IndexNode[graph.length];
116+
loopStack = new LoopNode[componentBackStackIndex];
117+
}
118+
else
119+
{
120+
() @trusted {
121+
import std.array: uninitializedArray;
109122

110-
stack = uninitializedArray!(I[])(graph.length);
111-
indeces = uninitializedArray!(IndexNode[])(graph.length);
112-
loopStack = uninitializedArray!(LoopNode[])(componentBackStackIndex);
113-
} ();
123+
stack = uninitializedArray!(I[])(graph.length);
124+
indeces = uninitializedArray!(IndexNode[])(graph.length);
125+
loopStack = uninitializedArray!(LoopNode[])(componentBackStackIndex);
126+
} ();
127+
}
114128
}
115129

116130
foreach(ref node; indeces)
117131
node.index = undefined;
118132

133+
I index;
119134
foreach(size_t v; 0u .. graph.length)
120135
{
121136
if (indeces[v].isUndefined)
@@ -191,26 +206,42 @@ auto tarjan(G, I = Unqual!(ForeachType!(ForeachType!G)))(G graph)
191206
}
192207
}
193208

194-
S[] pairwiseIndex;
195-
if (__ctfe)
209+
const indexLength = graph.length + 1 - componentBackStackIndex + 1;
210+
static if (RC)
196211
{
197-
pairwiseIndex = new S[graph.length - componentBackStackIndex + 1];
212+
auto pairwiseIndex = RCArray!S(indexLength, true);
198213
}
199214
else
200215
{
201-
() @trusted {
202-
import std.array: uninitializedArray;
203-
pairwiseIndex = uninitializedArray!(S[])(graph.length + 1 - componentBackStackIndex + 1);
204-
} ();
216+
S[] pairwiseIndex;
217+
if (__ctfe)
218+
{
219+
pairwiseIndex = new S[indexLength];
220+
}
221+
else
222+
{
223+
() @trusted {
224+
import std.array: uninitializedArray;
225+
pairwiseIndex = uninitializedArray!(S[])(indexLength);
226+
} ();
227+
}
205228
}
206-
foreach (i, ref e; loopStack[componentBackStackIndex .. $])
229+
foreach (i, ref e; loopStack[][componentBackStackIndex .. $])
207230
{
208231
pairwiseIndex[i] = e.index;
209232
}
210233
pairwiseIndex[$ - 1] = cast(I) graph.length;
211234

212235
import mir.ndslice.topology: chopped;
213-
return (()@trusted {return stack.ptr; }()).chopped(pairwiseIndex);
236+
static if (RC)
237+
{
238+
import core.lifetime: move;
239+
return chopped(RCI!I(stack.move), pairwiseIndex.asSlice);
240+
}
241+
else
242+
{
243+
return (()@trusted {return stack.ptr; }()).chopped(pairwiseIndex);
244+
}
214245
}
215246

216247
/++
@@ -241,14 +272,21 @@ pure version(mir_test) unittest
241272
"11": [],
242273
].graphSeries;
243274

244-
auto components = gs.data.tarjan;
245275

246-
assert(components == [
276+
static immutable result = [
247277
[0],
248278
[1, 2, 5, 4, 3, 6],
249279
[10],
250280
[7, 8, 9],
251-
[11]]);
281+
[11]];
282+
283+
// chec GC interface
284+
auto components = gs.data.tarjan;
285+
assert(components == result);
286+
// check @nogc interface
287+
// Note: The lambda function is used here to show @nogc mode explicitly.
288+
auto rccomponents = (() @nogc => gs.data.tarjan!true )();
289+
assert(rccomponents == result);
252290
}
253291

254292
/++

source/mir/ndslice/topology.d

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3209,7 +3209,7 @@ in
32093209
assert(b);
32103210
}
32113211
do {
3212-
3212+
import core.lifetime: move;
32133213
sizediff_t length = bounds._lengths[0] <= 1 ? 0 : bounds._lengths[0] - 1;
32143214
static if (hasLength!Sliceable)
32153215
{
@@ -3222,7 +3222,7 @@ do {
32223222
}
32233223
}
32243224

3225-
return typeof(return)([size_t(length)], ChopIterator!(Iterator, Sliceable)(bounds._iterator, sliceable));
3225+
return typeof(return)([size_t(length)], ChopIterator!(Iterator, Sliceable)(bounds._iterator.move, sliceable.move));
32263226
}
32273227

32283228
/// ditto

source/mir/rc/array.d

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,9 @@ Thread safe reference counting iterator.
387387
+/
388388
struct mir_rci(T)
389389
{
390+
import mir.ndslice.slice: Slice;
391+
import mir.ndslice.iterator: IotaIterator;
392+
390393
///
391394
T* _iterator;
392395

@@ -479,6 +482,36 @@ struct mir_rci(T)
479482
return _iterator[index];
480483
}
481484

485+
/// Returns: slice type of `Slice!(IotaIterator!size_t)`
486+
Slice!(IotaIterator!size_t) opSlice(size_t dimension)(size_t i, size_t j) @safe scope const
487+
if (dimension == 0)
488+
in
489+
{
490+
assert(i <= j, "RCI!T.opSlice!0: the left opSlice boundary must be less than or equal to the right bound.");
491+
}
492+
body
493+
{
494+
return typeof(return)(j - i, typeof(return).Iterator(i));
495+
}
496+
497+
/// Returns: ndslice on top of the refcounted iterator
498+
auto opIndex(Slice!(IotaIterator!size_t) slice)
499+
{
500+
import core.lifetime: move;
501+
auto it = this;
502+
it += slice._iterator._index;
503+
return Slice!(RCI!T)(slice.length, it.move);
504+
}
505+
506+
/// ditto
507+
auto opIndex(Slice!(IotaIterator!size_t) slice) const
508+
{
509+
import core.lifetime: move;
510+
auto it = lightConst;
511+
it += slice._iterator._index;
512+
return Slice!(RCI!(const T))(slice.length, it.move);
513+
}
514+
482515
///
483516
void opUnary(string op)() scope
484517
if (op == "--" || op == "++")

0 commit comments

Comments
 (0)