Support undirected graphs.
All checks were successful
/ Build and Release Library (push) Successful in 1m19s

This commit is contained in:
Pat Garrity 2025-12-11 07:29:33 -06:00
parent 2dcb9f7c26
commit 4788240666
Signed by: pfm
GPG key ID: 5CA5D21BAB7F3A76
6 changed files with 145 additions and 52 deletions

View file

@ -55,6 +55,8 @@ end Graph
object Graph: object Graph:
/** The empty graph. Contains no vertices and no edges.
*/
object Empty extends Graph: object Empty extends Graph:
override def numberOfVertices: Size = Size.Zero override def numberOfVertices: Size = Size.Zero

View file

@ -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

View file

@ -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

View file

@ -13,32 +13,8 @@ final class DataDag[A] private (
n: Size, n: Size,
a: Adjacency, a: Adjacency,
r: Vector[Vertex] r: Vector[Vertex]
) extends Dag(n, a, r): ) extends Dag(n, a, r)
with AnyGraphWithData[A]
/** 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
object DataDag: object DataDag:

View file

@ -13,32 +13,8 @@ final class DataDigraph[A] private (
n: Size, n: Size,
a: Adjacency, a: Adjacency,
r: Vector[Vertex] r: Vector[Vertex]
) extends Digraph(n, a, r): ) extends Digraph(n, a, r)
with AnyGraphWithData[A]
/** 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
object DataDigraph: object DataDigraph:

View file

@ -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
)