diff --git a/modules/core/src/main/scala/gs/graph/v0/Graph.scala b/modules/core/src/main/scala/gs/graph/v0/Graph.scala index 52f1f40..5522598 100644 --- a/modules/core/src/main/scala/gs/graph/v0/Graph.scala +++ b/modules/core/src/main/scala/gs/graph/v0/Graph.scala @@ -55,6 +55,8 @@ end Graph object Graph: + /** The empty graph. Contains no vertices and no edges. + */ object Empty extends Graph: override def numberOfVertices: Size = Size.Zero diff --git a/modules/core/src/main/scala/gs/graph/v0/UndirectedGraph.scala b/modules/core/src/main/scala/gs/graph/v0/UndirectedGraph.scala new file mode 100644 index 0000000..f0cf3c7 --- /dev/null +++ b/modules/core/src/main/scala/gs/graph/v0/UndirectedGraph.scala @@ -0,0 +1,26 @@ +package gs.graph.v0 + +/** Undirected implementation of [[Graph]]. + * + * @param numberOfVertices + * The number of vertices present in this graph. + * @param adjacency + * The [[Adjacency]] which describes this graph. + */ +class UndirectedGraph( + val numberOfVertices: Size, + val adjacency: Adjacency +) extends Graph: + /** @inheritDocs + */ + final override val disposition: GraphDisposition = GraphDisposition.Undirected +end UndirectedGraph + +object UndirectedGraph: + + /** @return + * An empty [[UndirectedGraph]]. + */ + def empty(): UndirectedGraph = new UndirectedGraph(Size.Zero, Adjacency.Empty) + +end UndirectedGraph diff --git a/modules/core/src/main/scala/gs/graph/v0/data/AnyGraphWithData.scala b/modules/core/src/main/scala/gs/graph/v0/data/AnyGraphWithData.scala new file mode 100644 index 0000000..bfb6839 --- /dev/null +++ b/modules/core/src/main/scala/gs/graph/v0/data/AnyGraphWithData.scala @@ -0,0 +1,56 @@ +package gs.graph.v0.data + +import gs.graph.v0.Adjacency +import gs.graph.v0.GraphDisposition +import gs.graph.v0.GraphException +import gs.graph.v0.Size +import gs.graph.v0.Vertex + +/** Trait that describes _any_ graph (undirected, directed, or other + * specializations of those such as DAGs) that is correlated with data. + */ +trait AnyGraphWithData[A]: + /** @return + * The data stored by each [[gs.graph.v0.Vertex]] in this graph. + */ + def data: Vector[A] + + /** @return + * The number of vertices present in this graph. + */ + def numberOfVertices: Size + + /** @return + * The [[Adjacency]] that describes this graph. + */ + def adjacency: Adjacency + + /** @return + * The underlying disposition of this graph. + */ + def disposition: GraphDisposition + + /** Retrieve the data associated with some [[Vertex]]. + * + * This implementation throws an exception if the input [[Vertex]] is out of + * range for this graph. + * + * @param vertex + * The input [[Vertex]]. + * @return + * The data associated with the input [[Vertex]]. + */ + def dataAtUnsafe(vertex: Vertex): A = + if vertex < numberOfVertices then data.apply(vertex.ordinal) + else throw GraphException.VertexExceedsGraphBounds(vertex, numberOfVertices) + + /** Retrieve the data associated with some [[Vertex]]. + * + * @param vertex + * The input [[Vertex]]. + * @return + * The data associated with the input [[Vertex]], or `None` if the + * [[Vertex]] is out of range for this graph. + */ + def dataAt(vertex: Vertex): Option[A] = + if vertex < numberOfVertices then Some(data.apply(vertex.ordinal)) else None diff --git a/modules/core/src/main/scala/gs/graph/v0/data/DataDag.scala b/modules/core/src/main/scala/gs/graph/v0/data/DataDag.scala index 70f8128..2899a5c 100644 --- a/modules/core/src/main/scala/gs/graph/v0/data/DataDag.scala +++ b/modules/core/src/main/scala/gs/graph/v0/data/DataDag.scala @@ -13,32 +13,8 @@ final class DataDag[A] private ( n: Size, a: Adjacency, r: Vector[Vertex] -) extends Dag(n, a, r): - - /** Retrieve the data associated with some [[Vertex]]. - * - * This implementation throws an exception if the input [[Vertex]] is out of - * range for this graph. - * - * @param vertex - * The input [[Vertex]]. - * @return - * The data associated with the input [[Vertex]]. - */ - def dataAtUnsafe(vertex: Vertex): A = - if vertex < n then data.apply(vertex.ordinal) - else throw GraphException.VertexExceedsGraphBounds(vertex, n) - - /** Retrieve the data associated with some [[Vertex]]. - * - * @param vertex - * The input [[Vertex]]. - * @return - * The data associated with the input [[Vertex]], or `None` if the - * [[Vertex]] is out of range for this graph. - */ - def dataAt(vertex: Vertex): Option[A] = - if vertex < n then Some(data.apply(vertex.ordinal)) else None +) extends Dag(n, a, r) + with AnyGraphWithData[A] object DataDag: diff --git a/modules/core/src/main/scala/gs/graph/v0/data/DataDigraph.scala b/modules/core/src/main/scala/gs/graph/v0/data/DataDigraph.scala index a138d4d..42e3e41 100644 --- a/modules/core/src/main/scala/gs/graph/v0/data/DataDigraph.scala +++ b/modules/core/src/main/scala/gs/graph/v0/data/DataDigraph.scala @@ -13,32 +13,8 @@ final class DataDigraph[A] private ( n: Size, a: Adjacency, r: Vector[Vertex] -) extends Digraph(n, a, r): - - /** Retrieve the data associated with some [[Vertex]]. - * - * This implementation throws an exception if the input [[Vertex]] is out of - * range for this graph. - * - * @param vertex - * The input [[Vertex]]. - * @return - * The data associated with the input [[Vertex]]. - */ - def dataAtUnsafe(vertex: Vertex): A = - if vertex < n then data.apply(vertex.ordinal) - else throw GraphException.VertexExceedsGraphBounds(vertex, n) - - /** Retrieve the data associated with some [[Vertex]]. - * - * @param vertex - * The input [[Vertex]]. - * @return - * The data associated with the input [[Vertex]], or `None` if the - * [[Vertex]] is out of range for this graph. - */ - def dataAt(vertex: Vertex): Option[A] = - if vertex < n then Some(data.apply(vertex.ordinal)) else None +) extends Digraph(n, a, r) + with AnyGraphWithData[A] object DataDigraph: diff --git a/modules/core/src/main/scala/gs/graph/v0/data/UndirectedDataGraph.scala b/modules/core/src/main/scala/gs/graph/v0/data/UndirectedDataGraph.scala new file mode 100644 index 0000000..85f9641 --- /dev/null +++ b/modules/core/src/main/scala/gs/graph/v0/data/UndirectedDataGraph.scala @@ -0,0 +1,57 @@ +package gs.graph.v0.data + +import gs.graph.v0.Adjacency +import gs.graph.v0.GraphException +import gs.graph.v0.Size +import gs.graph.v0.UndirectedGraph + +/** Specialization of [[UndirectedGraph]] that associates _data_ with _each + * [[Vertex]]_. + */ +final class UndirectedDataGraph[A] private ( + val data: Vector[A], + n: Size, + a: Adjacency +) extends UndirectedGraph(n, a) + with AnyGraphWithData[A] + +object UndirectedDataGraph: + + /** @return + * An empty [[UndirectedGraph]] with no data. + */ + def empty[A]: UndirectedDataGraph[A] = + new UndirectedDataGraph[A]( + Vector.empty, + Size.Zero, + Adjacency.Empty + ) + + /** Instantiate a new [[UndirectedDataGraph]] given an input + * [[UndirectedGraph]] and some data. + * + * Throws an exception if the input data vector does not exactly match the + * number of nodes in the graph -- each node MUST have a data element. + * + * @param graph + * The input [[UndirectedGraph]]. + * @param data + * The data to associate with the [[UndirectedGraph]]. + * @return + * The new [[UndirectedDataGraph]] instance. + */ + def fromUndirectedGraph[A]( + graph: UndirectedGraph, + data: Vector[A] + ): UndirectedDataGraph[A] = + if graph.numberOfVertices != Size.fromVector(data) then + throw GraphException.DataGraphMismatch( + graph.numberOfVertices, + data.length + ) + else + new UndirectedDataGraph( + data, + graph.numberOfVertices, + graph.adjacency + )