Some minor improvements to construction.
This commit is contained in:
parent
eeb8a7400b
commit
80284f8013
9 changed files with 160 additions and 38 deletions
|
|
@ -8,7 +8,7 @@ import scala.collection.mutable.ListBuffer
|
|||
* the corresponding vector is a "to" [[Vertex]] -- there are edges _from_ some
|
||||
* vertex _to_ another vertex.
|
||||
*/
|
||||
final class Adjacency(val neighbors: Vector[Vector[Vertex]]):
|
||||
final class Adjacency private (val neighbors: Vector[Vector[Vertex]]):
|
||||
/** Get the vector of [[Vertex]] that receive a connection _from_ the input
|
||||
* [[Vertex]].
|
||||
*
|
||||
|
|
@ -118,8 +118,7 @@ object Adjacency:
|
|||
*/
|
||||
final val Single: Adjacency = new Adjacency(Vector(Vector.empty))
|
||||
|
||||
/** Calculate an [[Adjacency]] from some collection of [[Edge]], where those
|
||||
* edges are assumed to be directed.
|
||||
/** Calculate an [[Adjacency]] from some collection of [[Edge]].
|
||||
*
|
||||
* @param numberOfVertices
|
||||
* The number of [[Vertex]] (`N`) in this graph.
|
||||
|
|
@ -128,14 +127,44 @@ object Adjacency:
|
|||
* @return
|
||||
* The calculated [[Adjacency]].
|
||||
*/
|
||||
def fromDirectedEdges(
|
||||
def fromEdges(
|
||||
numberOfVertices: Size,
|
||||
edges: Iterable[Edge]
|
||||
): Adjacency =
|
||||
val buffs = Vector.fill(numberOfVertices.value)(ListBuffer.empty[Vertex])
|
||||
val _ = edges.foreach { edge =>
|
||||
if edge.v1 >= numberOfVertices || edge.v2 >= numberOfVertices then
|
||||
throw new IllegalArgumentException(
|
||||
s"Edge (${edge.v1}, ${edge.v2}) is out of bounds. Maximum vertex value is ${numberOfVertices.value - 1}"
|
||||
)
|
||||
else
|
||||
val _ = buffs(edge.from.ordinal).addOne(edge.to)
|
||||
}
|
||||
new Adjacency(buffs.map(_.distinct.toVector))
|
||||
|
||||
/** Calculate an [[Adjacency]] from some collection of [[Edge]].
|
||||
*
|
||||
* @param edges
|
||||
* The collection of [[Edge]] present in this graph.
|
||||
* @return
|
||||
* The calculated [[Adjacency]].
|
||||
*/
|
||||
def fromEdges(
|
||||
edges: Iterable[Edge]
|
||||
): Adjacency =
|
||||
val numberOfVertices = findMaximumVertex(edges)
|
||||
val buffs = Vector.fill(numberOfVertices.value)(ListBuffer.empty[Vertex])
|
||||
val _ = edges.foreach { edge =>
|
||||
val _ = buffs(edge.from.ordinal).addOne(edge.to)
|
||||
}
|
||||
new Adjacency(buffs.map(_.distinct.toVector))
|
||||
|
||||
private def findMaximumVertex(edges: Iterable[Edge]): Vertex =
|
||||
var maximum = Vertex.Zero
|
||||
edges.foreach { edge =>
|
||||
if edge.max > maximum then maximum = edge.max
|
||||
else ()
|
||||
}
|
||||
maximum
|
||||
|
||||
end Adjacency
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ final class Edge private (
|
|||
val v2: Vertex
|
||||
):
|
||||
|
||||
def max: Vertex = if v1 >= v2 then v1 else v2
|
||||
|
||||
/** When considering this edge as _directed_, this function returns the
|
||||
* [[Vertex]] that is the beginning of the connection.
|
||||
*
|
||||
|
|
@ -103,4 +105,14 @@ object Edge:
|
|||
v2: Int
|
||||
): Edge = Edge(Vertex(v1), Vertex(v2))
|
||||
|
||||
/** Instantiate a list of Edge.
|
||||
*
|
||||
* @param edges
|
||||
* The list of Edge.
|
||||
* @return
|
||||
* The captured list of Edge.
|
||||
*/
|
||||
def list(edges: (Int, Int)*): List[Edge] =
|
||||
edges.map(e => apply(e._1, e._2)).toList
|
||||
|
||||
end Edge
|
||||
|
|
|
|||
|
|
@ -58,3 +58,41 @@ trait Graph:
|
|||
case _ => false
|
||||
|
||||
end Graph
|
||||
|
||||
object Graph:
|
||||
|
||||
/** Construct a new [[UndirectedGraph]] from the given [[Edge]].
|
||||
*
|
||||
* @param numberOfVertices
|
||||
* The number of vertices in this [[Graph]].
|
||||
* @param edges
|
||||
* The [[Edge]] in this graph.
|
||||
* @return
|
||||
* The new [[UndirectedGraph]].
|
||||
*/
|
||||
def undirected(
|
||||
numberOfVertices: Size,
|
||||
edges: (Int, Int)*
|
||||
): UndirectedGraph =
|
||||
new UndirectedGraph(
|
||||
numberOfVertices,
|
||||
Adjacency.fromEdges(numberOfVertices, Edge.list(edges*))
|
||||
)
|
||||
|
||||
/** Construct a new [[UndirectedGraph]] from the given [[Edge]].
|
||||
*
|
||||
* @param edges
|
||||
* The [[Edge]] in this graph.
|
||||
* @return
|
||||
* The new [[UndirectedGraph]].
|
||||
*/
|
||||
def undirected(
|
||||
edges: (Int, Int)*
|
||||
): UndirectedGraph =
|
||||
val adj = Adjacency.fromEdges(Edge.list(edges*))
|
||||
new UndirectedGraph(
|
||||
adj.numberOfVertices,
|
||||
adj
|
||||
)
|
||||
|
||||
end Graph
|
||||
|
|
|
|||
|
|
@ -8,6 +8,11 @@ package gs.graph.v0
|
|||
*/
|
||||
final class Vertex private (val ordinal: Int) extends Ordered[Vertex]:
|
||||
|
||||
/** @return
|
||||
* The value (ordinal) of the Vertex.
|
||||
*/
|
||||
def value: Int = ordinal
|
||||
|
||||
/** @inheritDocs
|
||||
*/
|
||||
override def compare(that: Vertex): Int =
|
||||
|
|
@ -37,6 +42,16 @@ final class Vertex private (val ordinal: Int) extends Ordered[Vertex]:
|
|||
*/
|
||||
infix def <(value: Int): Boolean = ordinal < value
|
||||
|
||||
/** Is the ordinal of this vertex less than or equal to some integer value?
|
||||
*
|
||||
* @param value
|
||||
* The integer value.
|
||||
* @return
|
||||
* True if the ordinal is less than or equal to the integer value. False
|
||||
* otherwise.
|
||||
*/
|
||||
infix def <=(value: Int): Boolean = ordinal <= value
|
||||
|
||||
/** Is the ordinal of this vertex greater than some integer value?
|
||||
*
|
||||
* @param value
|
||||
|
|
@ -46,6 +61,16 @@ final class Vertex private (val ordinal: Int) extends Ordered[Vertex]:
|
|||
*/
|
||||
infix def >(value: Int): Boolean = ordinal > value
|
||||
|
||||
/** Is the ordinal of this vertex greater than or equal to some integer value?
|
||||
*
|
||||
* @param value
|
||||
* The integer value.
|
||||
* @return
|
||||
* True if the ordinal is greater than or equal to the integer value. False
|
||||
* otherwise.
|
||||
*/
|
||||
infix def >=(value: Int): Boolean = ordinal >= value
|
||||
|
||||
/** Is the ordinal of this vertex less than some [[Size]] value?
|
||||
*
|
||||
* @param value
|
||||
|
|
@ -55,6 +80,16 @@ final class Vertex private (val ordinal: Int) extends Ordered[Vertex]:
|
|||
*/
|
||||
infix def <(size: Size): Boolean = ordinal < size.value
|
||||
|
||||
/** Is the ordinal of this vertex less than or equal to some [[Size]] value?
|
||||
*
|
||||
* @param value
|
||||
* The [[Size]] value.
|
||||
* @return
|
||||
* True if the ordinal is less than or equal to the [[Size]] value. False
|
||||
* otherwise.
|
||||
*/
|
||||
infix def <=(size: Size): Boolean = ordinal <= size.value
|
||||
|
||||
/** Is the ordinal of this vertex greater than some [[Size]] value?
|
||||
*
|
||||
* @param value
|
||||
|
|
@ -64,6 +99,17 @@ final class Vertex private (val ordinal: Int) extends Ordered[Vertex]:
|
|||
*/
|
||||
infix def >(size: Size): Boolean = ordinal > size.value
|
||||
|
||||
/** Is the ordinal of this vertex greater than or equal to some [[Size]]
|
||||
* value?
|
||||
*
|
||||
* @param value
|
||||
* The [[Size]] value.
|
||||
* @return
|
||||
* True if the ordinal is greater than or equal to the [[Size]] value.
|
||||
* False otherwise.
|
||||
*/
|
||||
infix def >=(size: Size): Boolean = ordinal >= size.value
|
||||
|
||||
object Vertex:
|
||||
|
||||
/** The fixed value 0 expressed as a [[Vertex]].
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ object Digraph:
|
|||
): Digraph =
|
||||
new Digraph(
|
||||
numberOfVertices = numberOfVertices,
|
||||
adjacency = Adjacency.fromDirectedEdges(numberOfVertices, edges),
|
||||
adjacency = Adjacency.fromEdges(numberOfVertices, edges),
|
||||
roots = findRootsForDirectedEdges(numberOfVertices, edges)
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ object SingleRootDigraph:
|
|||
if root < numberOfVertices then
|
||||
new SingleRootDigraph(
|
||||
numberOfVertices,
|
||||
Adjacency.fromDirectedEdges(numberOfVertices, edges),
|
||||
Adjacency.fromEdges(numberOfVertices, edges),
|
||||
root
|
||||
)
|
||||
else throw GraphException.RootOutOfBounds(root, numberOfVertices)
|
||||
|
|
@ -86,7 +86,7 @@ object SingleRootDigraph:
|
|||
Some(
|
||||
new SingleRootDigraph(
|
||||
numberOfVertices,
|
||||
Adjacency.fromDirectedEdges(numberOfVertices, edges),
|
||||
Adjacency.fromEdges(numberOfVertices, edges),
|
||||
roots(0)
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -5,17 +5,17 @@ class AdjacencyTests extends munit.FunSuite:
|
|||
test("should provide incoming connections") {
|
||||
val N = Size(7)
|
||||
val vs = (0 until N.value).map(Vertex(_)).toArray
|
||||
val E = List(
|
||||
Edge(vs(0), vs(1)),
|
||||
Edge(vs(0), vs(2)),
|
||||
Edge(vs(0), vs(3)),
|
||||
Edge(vs(1), vs(4)),
|
||||
Edge(vs(2), vs(4)),
|
||||
Edge(vs(3), vs(4)),
|
||||
Edge(vs(3), vs(5)),
|
||||
Edge(vs(4), vs(6))
|
||||
val E = Edge.list(
|
||||
0 -> 1,
|
||||
0 -> 2,
|
||||
0 -> 3,
|
||||
1 -> 4,
|
||||
2 -> 4,
|
||||
3 -> 4,
|
||||
3 -> 5,
|
||||
4 -> 6
|
||||
)
|
||||
val A = Adjacency.fromDirectedEdges(N, E)
|
||||
val A = Adjacency.fromEdges(N, E)
|
||||
|
||||
assertEquals(A.incoming(vs(0)), Vector.empty)
|
||||
assertEquals(A.incoming(vs(1)), Vector(vs(0)))
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ class EdgeTests extends FunSuite:
|
|||
val v3 = Vertex(3)
|
||||
val edge1 = Edge(v1, v2)
|
||||
val edge2 = Edge(v1 -> v2)
|
||||
val edge3 = new Edge(v3, v1)
|
||||
val edge3 = Edge(v3, v1)
|
||||
assertEquals(edge1, edge2)
|
||||
assertNotEquals(edge1, edge3)
|
||||
assertEquals(edge1.toString(), s"($v1, $v2)")
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package gs.graph.v0.directed
|
|||
import gs.graph.v0.Adjacency
|
||||
import gs.graph.v0.Edge
|
||||
import gs.graph.v0.Size
|
||||
import gs.graph.v0.Vertex
|
||||
import munit.*
|
||||
|
||||
class DagTests extends FunSuite:
|
||||
|
|
@ -23,20 +22,19 @@ class DagTests extends FunSuite:
|
|||
|
||||
test("should validate a single-root graph") {
|
||||
val size = Size(8)
|
||||
val vs = (0 until size.value).map(Vertex(_))
|
||||
val digraph: Digraph = Digraph.fromAdjacency(
|
||||
Adjacency.fromDirectedEdges(
|
||||
Adjacency.fromEdges(
|
||||
numberOfVertices = size,
|
||||
edges = Seq(
|
||||
Edge(vs(0) -> vs(1)),
|
||||
Edge(vs(0) -> vs(2)),
|
||||
Edge(vs(0) -> vs(3)),
|
||||
Edge(vs(1) -> vs(4)),
|
||||
Edge(vs(2) -> vs(4)),
|
||||
Edge(vs(3) -> vs(5)),
|
||||
Edge(vs(4) -> vs(6)),
|
||||
Edge(vs(5) -> vs(6)),
|
||||
Edge(vs(6) -> vs(7))
|
||||
edges = Edge.list(
|
||||
0 -> 1,
|
||||
0 -> 2,
|
||||
0 -> 3,
|
||||
1 -> 4,
|
||||
2 -> 4,
|
||||
3 -> 5,
|
||||
4 -> 6,
|
||||
5 -> 6,
|
||||
6 -> 7
|
||||
)
|
||||
)
|
||||
)
|
||||
|
|
@ -48,15 +46,14 @@ class DagTests extends FunSuite:
|
|||
|
||||
test("should NOT validate a single-root digraph with a cycle") {
|
||||
val size = Size(4)
|
||||
val vs = (0 until size.value).map(Vertex(_))
|
||||
val digraph: Digraph = Digraph.fromAdjacency(
|
||||
Adjacency.fromDirectedEdges(
|
||||
Adjacency.fromEdges(
|
||||
numberOfVertices = size,
|
||||
edges = Seq(
|
||||
Edge(vs(0) -> vs(1)),
|
||||
Edge(vs(1) -> vs(2)),
|
||||
Edge(vs(2) -> vs(3)),
|
||||
Edge(vs(3) -> vs(1))
|
||||
edges = Edge.list(
|
||||
0 -> 1,
|
||||
1 -> 2,
|
||||
2 -> 3,
|
||||
3 -> 1
|
||||
)
|
||||
)
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue