Source: builder/builderGraph.js

/**
 * Constructs a BuilderGraph with the specified set of hosts.
 * 
 * @classdesc
 * 
 * A BuilderGraph represents a user-defined motif. It is not the result of a
 * search for a motif, but rather it defines the motif structure that should be
 * searched for during a motif search. BuilderGraphs are so named because they
 * are the product of the user-facing graph builder. BuilderGraphs contain
 * {@link BuilderNode}s
 * 
 * @constructor
 * @extends AbstractGraph
 * @param {Array<String>} hosts The initial set of hosts
 */
function BuilderGraph(hosts, hostConstraints) {
    AbstractGraph.call(this);

    /** @private */
    this.hostSet = {};

    for (var i = 0; i < hosts.length; i++) {
        this.addHost(hosts[i], hostConstraints[i]);
    }
}

// BuilderGraph extends Graph
BuilderGraph.prototype = Object.create(AbstractGraph.prototype);
BuilderGraph.prototype.constructor = BuilderGraph;

/**
 * Adds a new host to the BuilderGraph. This method will also create the head
 * and tail nodes for that host. If the provided host name is already a valid
 * host in the BuilderGraph, this method does nothing
 * 
 * @param {String} host The name of the new host.
 */
BuilderGraph.prototype.addHost = function(host, hostConstraint) {
    if (!!this.hostSet[host]) {
        return;
    }

    this.hosts.push(host);
    this.hostSet[host] = true;

    var head = new BuilderNode();
    head.isHeadInner = true;
    head.host = host;
    head.graph = this;
    head.hasHostConstraint = hostConstraint;

    var tail = new BuilderNode();
    tail.isTailInner = true;
    tail.host = host;
    tail.graph = this;
    tail.hasHostConstraint = hostConstraint;

    head.prev = null;
    head.next = tail;

    tail.prev = head;
    tail.next = null;

    this.hostToHead[host] = head;
    this.hostToTail[host] = tail;
};

BuilderGraph.prototype.toVectorTimestamps = function() {

    var nodeToVT = {};
    var orderedNodes = [];

    for (var i = 0; i < this.hosts.length; i++) {
        var host = this.hosts[i];
        var curr = this.getHead(host).getNext();
        var num = 1;

        while (!curr.isTail()) {
            orderedNodes.push(curr);
            var clock = {};
            clock[host] = num++;
            nodeToVT[curr.getId()] = new VectorTimestamp(clock, host);
            curr = curr.getNext();
        }
    }

    this.getNodesTopologicallySorted().forEach(function(node) {
        var nodeVT = nodeToVT[node.getId()];
        node.getChildren().forEach(function(child) {
            nodeToVT[child.getId()] = nodeToVT[child.getId()].update(nodeVT);
        });
        if (!node.getNext().isTail()) {
            nodeToVT[node.getNext().getId()] = nodeToVT[node.getNext().getId()].update(nodeVT);
        }
    });

    return orderedNodes.map(function(node) {
        return nodeToVT[node.getId()];
    });
};

BuilderGraph.fromVectorTimestamps = function(vectorTimestamps, hostConstraints) {
    
    var logEvents = vectorTimestamps.map(function(vt) {
        return new LogEvent("", vt, 0, {});
    });

    var modelGraph = null;

    try {
        modelGraph = new ModelGraph(logEvents);
    }
    catch (exception) {
        if (exception.constructor != Exception) {
            throw exception;
        }
        throw new Exception("The JSON describing the structure of the motif is invalid.", true);
    }

    var hosts = modelGraph.getHosts();
    var newGraph = new BuilderGraph(hosts, hostConstraints);

    var oldToNewNode = {};

    hosts.forEach(function(host) {
        oldToNewNode[modelGraph.getHead(host).getId()] = newGraph.getHead(host);
    });

    modelGraph.getNodesTopologicallySorted().forEach(function(node) {
        var newNode = new BuilderNode();
        oldToNewNode[node.getPrev().getId()].insertNext(newNode);
        oldToNewNode[node.getId()] = newNode;

        node.getParents().forEach(function(parent) {
            newNode.addParent(oldToNewNode[parent.getId()]);
        });

        var index = hosts.indexOf(newNode.getHost());
        newNode.setHasHostConstraint(hostConstraints[index]);
    });

    return newGraph;

};