WIP: Adding streaming traversal.
All checks were successful
/ Build and Release Library (push) Successful in 1m32s
All checks were successful
/ Build and Release Library (push) Successful in 1m32s
This commit is contained in:
parent
86c067f3fa
commit
14e33a24f9
6 changed files with 72 additions and 14 deletions
|
|
@ -81,6 +81,11 @@ object Adjacency:
|
|||
*/
|
||||
final val Empty: Adjacency = new Adjacency(Vector.empty)
|
||||
|
||||
/** @return
|
||||
* The single-vertex [[Adjacency]] list.
|
||||
*/
|
||||
final val Single: Adjacency = new Adjacency(Vector(Vector.empty))
|
||||
|
||||
/** Calculate an [[Adjacency]] from some collection of [[Edge]].
|
||||
*
|
||||
* @param numberOfVertices
|
||||
|
|
|
|||
|
|
@ -27,6 +27,13 @@ object UndirectedGraph:
|
|||
/** @return
|
||||
* An empty [[UndirectedGraph]].
|
||||
*/
|
||||
def empty(): UndirectedGraph = new UndirectedGraph(Size.Zero, Adjacency.Empty)
|
||||
final val Empty: UndirectedGraph =
|
||||
new UndirectedGraph(Size.Zero, Adjacency.Empty)
|
||||
|
||||
/** @return
|
||||
* An [[UndirectedGraph]] with a single vertex.
|
||||
*/
|
||||
final val Single: UndirectedGraph =
|
||||
new UndirectedGraph(Size.One, Adjacency.Single)
|
||||
|
||||
end UndirectedGraph
|
||||
|
|
|
|||
|
|
@ -46,13 +46,14 @@ object Digraph:
|
|||
/** @return
|
||||
* An empty [[Digraph]].
|
||||
*/
|
||||
def empty(): Digraph = new Digraph(Size.Zero, Adjacency.Empty, Vector.empty)
|
||||
final val Empty: Digraph =
|
||||
new Digraph(Size.Zero, Adjacency.Empty, Vector.empty)
|
||||
|
||||
/** @return
|
||||
* A [[Digraph]] with a single [[Vertex]] and no edges.
|
||||
* A [[Digraph]] with one vertex.
|
||||
*/
|
||||
def single(): Digraph =
|
||||
new Digraph(Size.One, Adjacency(Vector(Vector.empty)), Vector(Vertex.Zero))
|
||||
final val Single: Digraph =
|
||||
new Digraph(Size.One, Adjacency.Single, Vector(Vertex.Zero))
|
||||
|
||||
def fromEdges(
|
||||
numberOfVertices: Size,
|
||||
|
|
|
|||
|
|
@ -9,14 +9,14 @@ import munit.*
|
|||
class DagTests extends FunSuite:
|
||||
|
||||
test("should validate an empty graph") {
|
||||
Dag.validate(Digraph.empty()) match
|
||||
case Right(dag) => assertEquals(dag, Digraph.empty())
|
||||
Dag.validate(Digraph.Empty) match
|
||||
case Right(dag) => assertEquals(dag, Digraph.Empty)
|
||||
case _ => fail("Expected the empty graph to be validated as a DAG.")
|
||||
}
|
||||
|
||||
test("should validate a graph with one node") {
|
||||
Dag.validate(Digraph.single()) match
|
||||
case Right(dag) => assertEquals(dag, Digraph.single())
|
||||
Dag.validate(Digraph.Single) match
|
||||
case Right(dag) => assertEquals(dag, Digraph.Single)
|
||||
case _ =>
|
||||
fail("Expected a graph with one vertex to be validated as a DAG.")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,11 +14,13 @@ object GraphTraversalFs2:
|
|||
graph: Graph,
|
||||
visit: Vertex => F[Out]
|
||||
): Stream[F, Out] =
|
||||
val state = new DfsState(graph.numberOfVertices)
|
||||
graph
|
||||
.selectRoots()
|
||||
.map(root => pull(graph, state, visit, root).stream.unNoneTerminate)
|
||||
.reduce(_ ++ _)
|
||||
if graph.isEmpty then Stream.empty
|
||||
else
|
||||
val state = new DfsState(graph.numberOfVertices)
|
||||
graph
|
||||
.selectRoots()
|
||||
.map(root => pull(graph, state, visit, root).stream.unNoneTerminate)
|
||||
.reduce(_ ++ _)
|
||||
|
||||
private def pull[F[_]: Sync, Out](
|
||||
graph: Graph,
|
||||
|
|
|
|||
43
modules/fs2/src/test/scala/gs/graph/v0/fs2/Fs2DfsTests.scala
Normal file
43
modules/fs2/src/test/scala/gs/graph/v0/fs2/Fs2DfsTests.scala
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
package gs.graph.v0.fs2
|
||||
|
||||
import cats.effect.IO
|
||||
import cats.effect.unsafe.IORuntime
|
||||
import gs.graph.v0.UndirectedGraph
|
||||
import gs.graph.v0.Vertex
|
||||
import gs.graph.v0.directed.Digraph
|
||||
import munit.*
|
||||
|
||||
class Fs2DfsTests extends FunSuite:
|
||||
given IORuntime = IORuntime.global
|
||||
|
||||
private def iotest(
|
||||
name: String
|
||||
)(
|
||||
body: => IO[Unit]
|
||||
)(
|
||||
using
|
||||
Location
|
||||
): Unit =
|
||||
test(name)(body.unsafeRunSync())
|
||||
|
||||
iotest("(DFS) should return an empty stream for an empty graph") {
|
||||
val s = GraphTraversalFs2.dfs(
|
||||
UndirectedGraph.Empty,
|
||||
_ => IO.raiseError(IllegalStateException("Should not reach this point."))
|
||||
)
|
||||
s.compile.last.map(result => assertEquals(result, None))
|
||||
}
|
||||
|
||||
iotest(
|
||||
"(DFS) should return a stream of one for a graph with a single vertex"
|
||||
) {
|
||||
val s1 = GraphTraversalFs2.dfs(UndirectedGraph.Single, v => IO(v))
|
||||
val s2 = GraphTraversalFs2.dfs(Digraph.Single, v => IO(v))
|
||||
|
||||
for
|
||||
r1 <- s1.compile.toList
|
||||
r2 <- s2.compile.toList
|
||||
yield
|
||||
assertEquals(r1, List(Vertex.Zero))
|
||||
assertEquals(r2, List(Vertex.Zero))
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue