Initial commit

This adds the initial contents of the repository.
This commit is contained in:
William Jacobs
2019-03-06 16:46:45 -05:00
parent 7d95d5f991
commit a60eac5b6c
19 changed files with 2770 additions and 2 deletions

View File

@@ -0,0 +1,39 @@
package com.github.btrekkie.connectivity;
/**
* A combining function for taking the augmentations associated with a set of ConnVertices and reducing them to a single
* result. The function is a binary operation for combining two values into one. For example, given vertices with
* augmentations A1, A2, A3, and A4, the combined result may be obtained by computing
* combine(combine(combine(A1, A2), A3), A4). In order for an augmentation result to be meaningful, the combining
* function must be commutative, meaning combine(x, y) is equivalent to combine(y, x), and associative, meaning
* combine(x, combine(y, z)) is equivalent to combine(combine(x, y), z).
*
* If a ConnGraph represents a game map, then one example of an augmentation would be the amount of gold accessible from
* a certain location. Each vertex would be augmented with the amount of gold in that location, and the combining
* function would add the two amounts of gold passed in as arguments. Another example would be the strongest monster
* that can reach a particular location. Each vertex with at least one monster would be augmented with a pointer to the
* strongest monster at that location, and the combining function would return the stronger of the two monsters passed
* in as arguments. A third example would be the number of locations accessible from a given vertex. Each vertex would
* be augmented with the number 1, and the combining function would add the two numbers of vertices passed in as
* arguments.
*
* ConnGraph treats two augmentation values X and Y as interchangeable if they are equal, as in
* X != null ? X.equals(Y) : Y == null. The same holds for two combined augmentation values, and for one combined
* augmentation value and one augmentation value.
*
* See the comments for ConnGraph.
*/
public interface Augmentation {
/**
* Returns the result of combining the specified values into one. Each argument is either the augmentation
* information associated with a vertex, or the result of a previous call to "combine".
*
* Note that a value of null is never passed in to indicate the absence of augmentation information. The fact that
* ConnGraph.getVertexAugmentation, for example, may return null when there is no associated augmentation might lead
* you to believe that a null argument indicates the absence of augmentation information, but again, it does not. A
* null argument can only mean that a vertex is explicitly associated with null augmentation information, due to a
* prior call to ConnGraph.setVertexAugmentation(vertex, null), or that the "combine" method previously returned
* null.
*/
public Object combine(Object value1, Object value2);
}

View File

@@ -0,0 +1,61 @@
package com.github.btrekkie.connectivity;
/**
* Represents an edge in a ConnGraph, at the level of the edge (i.e. at the lowest level i for which G_i contains the
* edge). Every graph edge has exactly one corresponding ConnEdge object, regardless of the number of levels it appears
* in. See the comments for the implementation of ConnGraph.
*
* ConnEdges are stored in the linked lists suggested by EulerTourVertex.graphListHead and
* EulerTourVertex.forestListHead. Each ConnEdge is in two linked lists, so care must be taken when traversing the
* linked lists. prev1 and next1 are the links for the list starting at vertex1.graphListHead or vertex1.forestListHead,
* while prev2 and next2 are the links for vertex2. But the vertex1 and vertex2 fields of a given edge are different
* from the vertex1 and vertex2 fields of the linked edges. For example, the edge after next1 is not necessarily
* next1.next1. It depends on whether next1.vertex1 is the same as vertex1. If next1.vertex1 == vertex1, then the edge
* after next1 is next1.next1, but otherwise, it is next1.next2.
*/
class ConnEdge {
/** The edge's first endpoint (at the same level as the edge). */
public EulerTourVertex vertex1;
/** The edge's second endpoint (at the same level as the edge). */
public EulerTourVertex vertex2;
/**
* The EulerTourEdge object describing the edge's presence in an Euler tour tree, at the same level as the edge, or
* null if the edge is not in the Euler tour forest F_i.
*/
public EulerTourEdge eulerTourEdge;
/**
* The edge preceding this in a linked list of same-level edges adjacent to vertex1, if any. The edge is either part
* of a list of non-forest edges starting with vertex1.graphListHead, or part of a list of forest edges starting
* with vertex1.forestListHead. Note that this list excludes any edges that also appear in lower levels.
*/
public ConnEdge prev1;
/**
* The edge succeeding this in a linked list of same-level edges adjacent to vertex1, if any. The edge is either
* part of a list of non-forest edges starting with vertex1.graphListHead, or part of a list of forest edges
* starting with vertex1.forestListHead. Note that this list excludes any edges that also appear in lower levels.
*/
public ConnEdge next1;
/**
* The edge preceding this in a linked list of same-level edges adjacent to vertex2, if any. The edge is either part
* of a list of non-forest edges starting with vertex2.graphListHead, or part of a list of forest edges starting
* with vertex2.forestListHead. Note that this list excludes any edges that also appear in lower levels.
*/
public ConnEdge prev2;
/**
* The edge succeeding this in a linked list of same-level edges adjacent to vertex2, if any. The edge is either
* part of a list of non-forest edges starting with vertex2.graphListHead, or part of a list of forest edges
* starting with vertex2.forestListHead. Note that this list excludes any edges that also appear in lower levels.
*/
public ConnEdge next2;
public ConnEdge(EulerTourVertex vertex1, EulerTourVertex vertex2) {
this.vertex1 = vertex1;
this.vertex2 = vertex2;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,41 @@
package com.github.btrekkie.connectivity;
import java.util.Random;
/** A vertex in a ConnGraph. See the comments for ConnGraph. */
public class ConnVertex {
/**
* The thread-local random number generator we use by default to set the "hash" field. We could use
* ThreadLocalRandom instead, but ThreadLocalRandom isn't available in Java 6.0 and below.
*/
private static final ThreadLocal<Random> random = new ThreadLocal<Random>() {
@Override
protected Random initialValue() {
return new Random();
}
};
/**
* A randomly generated integer to use as the return value of hashCode(). ConnGraph relies on random hash codes for
* its performance guarantees.
*/
private final int hash;
public ConnVertex() {
hash = random.get().nextInt();
}
/**
* Constructs a new ConnVertex.
* @param random The random number generator to use to produce a random hash code. ConnGraph relies on random hash
* codes for its performance guarantees.
*/
public ConnVertex(Random random) {
hash = random.nextInt();
}
@Override
public int hashCode() {
return hash;
}
}

View File

@@ -0,0 +1,30 @@
package com.github.btrekkie.connectivity;
/**
* The representation of a forest edge in some Euler tour forest F_i at some particular level i. Each forest edge has
* one EulerTourEdge object for each level it appears in. See the comments for the implementation of ConnGraph.
*/
class EulerTourEdge {
/**
* One of the two visits preceding the edge in the Euler tour, in addition to visit2. (The node is at the same level
* as the EulerTourEdge.)
*/
public final EulerTourNode visit1;
/**
* One of the two visits preceding the edge in the Euler tour, in addition to visit1. (The node is at the same level
* as the EulerTourEdge.)
*/
public final EulerTourNode visit2;
/**
* The representation of this edge in the next-higher level. higherEdge is null if this edge is in the highest
* level.
*/
public EulerTourEdge higherEdge;
public EulerTourEdge(EulerTourNode visit1, EulerTourNode visit2) {
this.visit1 = visit1;
this.visit2 = visit2;
}
}

View File

@@ -0,0 +1,112 @@
package com.github.btrekkie.connectivity;
import com.github.btrekkie.red_black_node.RedBlackNode;
/**
* A node in an Euler tour tree for ConnGraph (at some particular level i). See the comments for the implementation of
* ConnGraph.
*/
class EulerTourNode extends RedBlackNode<EulerTourNode> {
/** The dummy leaf node. */
public static final EulerTourNode LEAF = new EulerTourNode(null, null);
/** The vertex this node visits. */
public final EulerTourVertex vertex;
/** The number of nodes in the subtree rooted at this node. */
public int size;
/**
* Whether the subtree rooted at this node contains a node "node" for which
* node.vertex.arbitraryNode == node && node.vertex.graphListHead != null.
*/
public boolean hasGraphEdge;
/**
* Whether the subtree rooted at this node contains a node "node" for which
* node.vertex.arbitraryNode == node && node.vertex.forestListHead != null.
*/
public boolean hasForestEdge;
/**
* The combining function for combining user-provided augmentations. augmentationFunc is null if this node is not in
* the highest level.
*/
public final Augmentation augmentationFunc;
/**
* The combined augmentation for the subtree rooted at this node. This is the result of combining the augmentation
* values node.vertex.augmentation for all nodes "node" in the subtree rooted at this node for which
* node.vertex.arbitraryVisit == node, using augmentationFunc. This is null if hasAugmentation is false.
*/
public Object augmentation;
/**
* Whether the subtree rooted at this node contains at least one augmentation value. This indicates whether there is
* some node "node" in the subtree rooted at this node for which node.vertex.hasAugmentation is true and
* node.vertex.arbitraryVisit == node.
*/
public boolean hasAugmentation;
public EulerTourNode(EulerTourVertex vertex, Augmentation augmentationFunc) {
this.vertex = vertex;
this.augmentationFunc = augmentationFunc;
}
/** Like augment(), but only updates the augmentation fields hasGraphEdge and hasForestEdge. */
public boolean augmentFlags() {
boolean newHasGraphEdge =
left.hasGraphEdge || right.hasGraphEdge || (vertex.arbitraryVisit == this && vertex.graphListHead != null);
boolean newHasForestEdge =
left.hasForestEdge || right.hasForestEdge ||
(vertex.arbitraryVisit == this && vertex.forestListHead != null);
if (newHasGraphEdge == hasGraphEdge && newHasForestEdge == hasForestEdge) {
return false;
} else {
hasGraphEdge = newHasGraphEdge;
hasForestEdge = newHasForestEdge;
return true;
}
}
@Override
public boolean augment() {
int newSize = left.size + right.size + 1;
boolean augmentedFlags = augmentFlags();
Object newAugmentation = null;
boolean newHasAugmentation = false;
if (augmentationFunc != null) {
if (left.hasAugmentation) {
newAugmentation = left.augmentation;
newHasAugmentation = true;
}
if (vertex.hasAugmentation && vertex.arbitraryVisit == this) {
if (newHasAugmentation) {
newAugmentation = augmentationFunc.combine(newAugmentation, vertex.augmentation);
} else {
newAugmentation = vertex.augmentation;
newHasAugmentation = true;
}
}
if (right.hasAugmentation) {
if (newHasAugmentation) {
newAugmentation = augmentationFunc.combine(newAugmentation, right.augmentation);
} else {
newAugmentation = right.augmentation;
newHasAugmentation = true;
}
}
}
if (newSize == size && !augmentedFlags && hasAugmentation == newHasAugmentation &&
(newAugmentation != null ? newAugmentation.equals(augmentation) : augmentation == null)) {
return false;
} else {
size = newSize;
augmentation = newAugmentation;
hasAugmentation = newHasAugmentation;
return true;
}
}
}

View File

@@ -0,0 +1,43 @@
package com.github.btrekkie.connectivity;
/**
* The representation of a ConnVertex at some particular level i. Each vertex has one EulerTourVertex object for each
* level it appears in. Note that different vertices may appear in different numbers of levels, as EulerTourVertex
* objects are only created for lower levels as needed. See the comments for the implementation of ConnGraph.
*/
class EulerTourVertex {
/**
* The representation of this edge in the next-lower level. lowerVertex is null if this is the lowest-level
* representation of this vertex.
*/
public EulerTourVertex lowerVertex;
/**
* The representation of this edge in the next-higher level. This is null if this vertex is in the highest level.
*/
public EulerTourVertex higherVertex;
/**
* An arbitrarily selected visit to the vertex in the Euler tour tree that contains it (at the same level as this).
*/
public EulerTourNode arbitraryVisit;
/**
* The first edge in the linked list of level-i edges that are adjacent to the vertex in G_i, but are not in the
* Euler tour forest F_i, where i is the level of the vertex. Note that this list excludes any edges that also
* appear in lower levels.
*/
public ConnEdge graphListHead;
/**
* The first edge in the linked list of level-i edges adjacent to the vertex that are in F_i, where i is the level
* of the vertex. Note that this list excludes any edges that also appear in lower levels.
*/
public ConnEdge forestListHead;
/** The augmentation associated with this vertex, if any. This is null instead if higherVertex != null. */
public Object augmentation;
/** Whether there is any augmentation associated with this vertex. This is false instead if higherVertex != null. */
public boolean hasAugmentation;
}

View File

@@ -0,0 +1,32 @@
package com.github.btrekkie.connectivity;
import java.util.HashMap;
import java.util.Map;
/**
* Describes a ConnVertex, with respect to a particular ConnGraph. There is exactly one VertexInfo object per vertex in
* a given graph, regardless of how many levels the vertex is in. See the comments for the implementation of ConnGraph.
*/
class VertexInfo {
/** The representation of the vertex in the highest level. */
public EulerTourVertex vertex;
/**
* A map from each ConnVertex adjacent to this vertex to the ConnEdge object for the edge connecting it to this
* vertex. Lookups take O(1) expected time and O(log N / log log N) time with high probability, because "edges" is a
* HashMap, and ConnVertex.hashCode() returns a random integer.
*/
public Map<ConnVertex, ConnEdge> edges = new HashMap<ConnVertex, ConnEdge>();
/**
* The maximum number of entries in "edges" since the last time we "rebuilt" that field. When the number of edges
* drops sufficiently, we rebuild "edges" by copying its contents to a new HashMap. We do this to ensure that
* "edges" uses O(K) space, where K is the number of vertices adjacent to this. (The capacity of a HashMap is not
* automatically reduced as the number of entries decreases, so we have to limit space usage manually.)
*/
public int maxEdgeCountSinceRebuild;
public VertexInfo(EulerTourVertex vertex) {
this.vertex = vertex;
}
}