Hello. Is code-review requests welcome in this forum? If so I would like some criticisms and feedback for my disjoint sets implementation. The code is as follows:

module dsets;

/// dsets is an implementation of disjoint sets. It is implemented
/// with a simple class. To construct it, you provide the maximum node
/// number.
/// Limitation: CANNOT DISCONNECT TWO NODES

/// If the array is big enough, opt for parallel operations if possible
immutable ulong minimumParallelArrayLength = 10_000;

public class DSets
{
private:
    ulong[] ids, heights;

    bool isRoot(const ulong n) const
    {
        return ids[n] == n;
    }

public:
    /// A `DSets(10)` would create a disjoint set of 0 to 10
    /// inclusive.
    this(in ulong maxN)
    {
        ids.reserve(maxN + 1);
        heights.reserve(maxN + 1);

        foreach (n; 0 .. maxN + 1)
        {
            ids ~= n;
            heights ~= 0;
        }
    }

    /// Returns the root id of a given node
    ulong getRoot(in ulong n) const
    {
        ulong result = n;
        while (ids[result] != result)
            result = ids[ids[result]]; // path compression
        return result;
    }

    /// Connect two nodes. Does not check membership so may throw
    /// errors
    void connect(in ulong n, in ulong m)
    {
        auto nRoot = getRoot(n), mRoot = getRoot(m);
        if (nRoot != mRoot)
        {
            if (heights[nRoot] >= heights[mRoot])
            {
                ids[mRoot] = ids[nRoot];
                heights[nRoot] += heights[mRoot];
            }
            else
            {
                ids[nRoot] = ids[mRoot];
                heights[mRoot] += heights[nRoot];
            }
        }
    }

    /// Check if two given nodes are connected.
    bool connected(in ulong n, in ulong m) const
    {
        return getRoot(n) == getRoot(m);
    }

    /// Returns the number of nodes in the set
    ulong length() const @safe
    {
        return ids.length;
    }

    /// Add a node
    void grow(in ulong nodes = 0)
    {
        auto toAdd = ids[$ - 1];
        foreach (_; 0 .. nodes + 1)
        {
            ids ~= toAdd++;
            heights ~= 0;
        }
    }

    /// Get an array of roots
    ulong[] getRoots() const
    {
        if (ids.length == 0)
            return [];

        // D does not have a standard hash-set. The
        // workaround is to use a hash-table
        bool[ulong] roots;

        // premature optimization, if the array is not huge,
        // do not opt for multithreaded operation
        if (ids.length < 10_000)
        {
            foreach (i, id; ids)
                if (ids[i] == i)
                    if (!(i in roots))
                        roots[i] = true;
        }
        else
        {
            import std.parallelism : parallel;

            foreach (i, id; ids.parallel())
                if (ids[i] == i)
                    if (!(i in roots))
                        roots[i] = true;
        }
        return roots.keys;
    }

    /// Returns all the nodes connected to the given node
    ulong[] getLinkedNodes(in ulong node) const
    {
        // premature optimization, if the array is not huge,
        // do not opt for multithreaded operation
        ulong[] nodes;
        if (ids.length < 10_000)
        {
            foreach (i, _; ids)
                if (this.connected(node, i))
                    nodes ~= i;
        }
        else
        {
            import std.parallelism : parallel;

            foreach (i, _; ids.parallel())
                if (this.connected(node, i))
                    nodes ~= i;
        }
        return nodes;
    }
}

unittest
{
    import std.stdio;

    writefln("Testing %s...", __FILE__);
    scope (success)
        writeln("Success");

    auto forrest = new DSets(9);

    forrest.connect(9, 3);
    forrest.connect(1, 3);
    forrest.connect(9, 0);
    forrest.connect(7, 5);
    forrest.connect(6, 4);
    assert(forrest.connected(1, 0));
    assert(forrest.connected(3, 0));
    assert(!forrest.connected(5, 4));

    foreach (root; forrest.getRoots())
        writeln(root, " ==> ", forrest.getLinkedNodes(root));
}


outupt:

$ dub test -q
Testing source/dsets.d...
6 ==> [4, 6]
7 ==> [5, 7]
2 ==> [2]
1 ==> [0, 1, 3, 9]
8 ==> [8]
Success

Reply via email to