From 977ea251d08005f9dc44104d3f0e2664b767bd78 Mon Sep 17 00:00:00 2001 From: Abhijit Sarkar Date: Thu, 16 Jan 2025 10:50:01 -0800 Subject: [PATCH] Simplify graph tests by implicitly converting tuples to edges --- graph/test/src/P83Spec.scala | 12 ++++---- graph/test/src/P84Spec.scala | 14 +++++---- graph/test/src/P85Spec.scala | 58 ++++++++++++++++++++---------------- graph/test/src/P86Spec.scala | 6 ++-- graph/test/src/Util.scala | 7 +++++ 5 files changed, 59 insertions(+), 38 deletions(-) create mode 100644 graph/test/src/Util.scala diff --git a/graph/test/src/P83Spec.scala b/graph/test/src/P83Spec.scala index 60f0a76..d312986 100644 --- a/graph/test/src/P83Spec.scala +++ b/graph/test/src/P83Spec.scala @@ -4,10 +4,12 @@ import org.scalatest.funspec.AnyFunSpec import org.scalatest.matchers.should.Matchers.* import P83.spanningTrees import org.scalactic.Equality +import graph.Util.given +import scala.language.implicitConversions class P83Spec extends AnyFunSpec: it("construct all spanning trees"): - val data = List( + val data: List[(List[Char], List[Edge[Char, Nothing]])] = List( ( List('a', 'b', 'c'), List(('a', 'b'), ('b', 'c'), ('a', 'c')) @@ -30,16 +32,16 @@ class P83Spec extends AnyFunSpec: ) ) - val edgeEq = new Equality[(Char, Char)]: - def areEqual(a: (Char, Char), b: Any): Boolean = + val edgeEq = new Equality[Edge[Char, Nothing]]: + def areEqual(a: Edge[Char, Nothing], b: Any): Boolean = if b.isInstanceOf[List[?]] then b.asInstanceOf[List[Char]] match - case u :: v :: Nil => (u, v) == a || (v, u) == a + case u :: v :: Nil => (u, v) == (a.u, a.v) || (v, u) == (a.u, a.v) case _ => false else false data.foreach { (vertices, edges) => - val g = Graph.buildUG(vertices, edges.map((u, v) => Edge(u, v, None))) + val g = Graph.buildUG(vertices, edges) val st = g.spanningTrees val n = g.vertices.size st.foreach { t => diff --git a/graph/test/src/P84Spec.scala b/graph/test/src/P84Spec.scala index 9577a37..d7b6a15 100644 --- a/graph/test/src/P84Spec.scala +++ b/graph/test/src/P84Spec.scala @@ -4,10 +4,12 @@ import org.scalatest.funspec.AnyFunSpec import org.scalatest.matchers.should.Matchers.* import P84.minimalSpanningTree import org.scalactic.Equality +import graph.Util.given +import scala.language.implicitConversions class P84Spec extends AnyFunSpec: it("construct the minimal spanning tree"): - val data = List( + val data: List[(List[Char], List[Edge[Char, Int]], Int)] = List( ( List('a', 'b', 'c'), List(('a', 'b', 1), ('b', 'c', 2), ('a', 'c', 3)), @@ -32,15 +34,15 @@ class P84Spec extends AnyFunSpec: ) ) - val edgeEq = new Equality[(Char, Char, Int)]: - def areEqual(a: (Char, Char, Int), b: Any): Boolean = + val edgeEq = new Equality[Edge[Char, Int]]: + def areEqual(a: Edge[Char, Int], b: Any): Boolean = if b.isInstanceOf[Edge[?, ?]] then - val e = b.asInstanceOf[Edge[Char, Int]] - (a._1, a._2) == (e.u, e.v) || (a._1, a._2) == (e.v, e.u) + b.asInstanceOf[Edge[Char, Int]] match + case Edge(u, v, _) => (u, v) == (a.u, a.v) || (v, u) == (a.u, a.v) else false data.foreach { (vertices, edges, cost) => - val g = Graph.buildUG(vertices, edges.map((u, v, d) => Edge(u, v, Some(d)))) + val g = Graph.buildUG(vertices, edges) val mst = g.minimalSpanningTree mst.foreach(e => (edges should contain(e))(edgeEq)) mst.foldLeft(0)((s, e) => s + e.data.getOrElse(0)) shouldBe cost diff --git a/graph/test/src/P85Spec.scala b/graph/test/src/P85Spec.scala index 77eef9e..2c9316e 100644 --- a/graph/test/src/P85Spec.scala +++ b/graph/test/src/P85Spec.scala @@ -3,27 +3,29 @@ package graph import org.scalatest.funspec.AnyFunSpec import org.scalatest.matchers.should.Matchers.shouldBe import P85.isIsomorphicTo +import graph.Util.given +import scala.language.implicitConversions class P85Spec extends AnyFunSpec: it("graph isomorphism"): - val v1 = List('a', 'b') - val e1 = List(('a', 'b')) - val v2 = List(5, 7) - val e2 = List((5, 7)) - val g1 = Graph.buildUG(v1, e1.map((u, v) => Edge(u, v, None))) - val g2 = Graph.buildUG(v2, e2.map((u, v) => Edge(u, v, None))) + val v1 = List('a', 'b') + val e1: List[Edge[Char, Nothing]] = List(('a', 'b')) + val v2 = List(5, 7) + val e2: List[Edge[Int, Nothing]] = List((5, 7)) + val g1 = Graph.buildUG(v1, e1) + val g2 = Graph.buildUG(v2, e2) g1.isIsomorphicTo(g2) shouldBe true // format: off val v3 = (1 to 8).toList - val e3 = + val e3: List[Edge[Int, Nothing]] = List( (1, 5), (1, 6), (1, 7), (2, 5), (2, 6), (2, 8), (3, 5), (3, 7), (3, 8), (4, 6), (4, 7), (4, 8) ) - val e4 = + val e4: List[Edge[Int, Nothing]] = List( (1, 2), (1, 4), (1, 5), (6, 2), (6, 5), (6, 7), (8, 4), (8, 5), @@ -31,30 +33,35 @@ class P85Spec extends AnyFunSpec: ) // format: on - val g3 = Graph.buildUG(v3, e3.map((u, v) => Edge(u, v, None))) - val g4 = Graph.buildUG(v3, e4.map((u, v) => Edge(u, v, None))) + val g3 = Graph.buildUG(v3, e3) + val g4 = Graph.buildUG(v3, e4) g3.isIsomorphicTo(g4) shouldBe true val v5 = ('a' to 'e').toList - val e5 = List(('a', 'b'), ('a', 'c'), ('a', 'e'), ('b', 'c'), ('c', 'd'), ('d', 'e')) + val e5: List[Edge[Char, Nothing]] = + List(('a', 'b'), ('a', 'c'), ('a', 'e'), ('b', 'c'), ('c', 'd'), ('d', 'e')) val v6 = ('A' to 'E').toList - val e6 = List(('A', 'D'), ('A', 'E'), ('B', 'C'), ('B', 'D'), ('B', 'E'), ('C', 'D')) + val e6: List[Edge[Char, Nothing]] = + List(('A', 'D'), ('A', 'E'), ('B', 'C'), ('B', 'D'), ('B', 'E'), ('C', 'D')) - val g5 = Graph.buildUG(v5, e5.map((u, v) => Edge(u, v, None))) - val g6 = Graph.buildUG(v6, e6.map((u, v) => Edge(u, v, None))) + val g5 = Graph.buildUG(v5, e5) + val g6 = Graph.buildUG(v6, e6) g5.isIsomorphicTo(g6) shouldBe true val v7 = List('α', 'β', 'γ', 'δ', 'ε') - val e7 = List(('α', 'β'), ('α', 'γ'), ('α', 'ε'), ('β', 'δ'), ('γ', 'δ'), ('δ', 'ε')) - val g7 = Graph.buildUG(v7, e7.map((u, v) => Edge(u, v, None))) + val e7: List[Edge[Char, Nothing]] = + List(('α', 'β'), ('α', 'γ'), ('α', 'ε'), ('β', 'δ'), ('γ', 'δ'), ('δ', 'ε')) + val g7 = Graph.buildUG(v7, e7) g5.isIsomorphicTo(g7) shouldBe false g6.isIsomorphicTo(g7) shouldBe false val v8 = ('a' to 'f').toList - val e8 = List(('a', 'b'), ('a', 'd'), ('b', 'c'), ('c', 'f'), ('d', 'e'), ('e', 'f')) - val e9 = List(('a', 'd'), ('a', 'e'), ('b', 'd'), ('b', 'f'), ('c', 'e'), ('c', 'f')) - val g8 = Graph.buildUG(v8, e8.map((u, v) => Edge(u, v, None))) - val g9 = Graph.buildUG(v8, e9.map((u, v) => Edge(u, v, None))) + val e8: List[Edge[Char, Nothing]] = + List(('a', 'b'), ('a', 'd'), ('b', 'c'), ('c', 'f'), ('d', 'e'), ('e', 'f')) + val e9: List[Edge[Char, Nothing]] = + List(('a', 'd'), ('a', 'e'), ('b', 'd'), ('b', 'f'), ('c', 'e'), ('c', 'f')) + val g8 = Graph.buildUG(v8, e8) + val g9 = Graph.buildUG(v8, e9) g8.isIsomorphicTo(g9) shouldBe true // G(v8 e8) and G(va ea) are not isomorphic but the algorithm can't determine that. @@ -62,9 +69,10 @@ class P85Spec extends AnyFunSpec: // val ea = [(1, 3), (1, 5), (2, 4), (2, 6), (3, 5), (4, 6)] val v10 = ('A' to 'D').toList - val e10 = List(('A', 'B'), ('A', 'C'), ('A', 'D'), ('B', 'C'), ('B', 'D'), ('C', 'D')) - val v11 = (1 to 4).toList - val e11 = List((1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)) - val g10 = Graph.buildUG(v10, e10.map((u, v) => Edge(u, v, None))) - val g11 = Graph.buildUG(v11, e11.map((u, v) => Edge(u, v, None))) + val e10: List[Edge[Char, Nothing]] = + List(('A', 'B'), ('A', 'C'), ('A', 'D'), ('B', 'C'), ('B', 'D'), ('C', 'D')) + val v11 = (1 to 4).toList + val e11: List[Edge[Int, Nothing]] = List((1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)) + val g10 = Graph.buildUG(v10, e10) + val g11 = Graph.buildUG(v11, e11) g10.isIsomorphicTo(g11) shouldBe true diff --git a/graph/test/src/P86Spec.scala b/graph/test/src/P86Spec.scala index 7518b9e..58116a2 100644 --- a/graph/test/src/P86Spec.scala +++ b/graph/test/src/P86Spec.scala @@ -3,10 +3,12 @@ package graph import org.scalatest.funspec.AnyFunSpec import org.scalatest.matchers.should.Matchers.* import P86.colorNodes +import graph.Util.given +import scala.language.implicitConversions class P86Spec extends AnyFunSpec: it("graph coloration"): - val data = List( + val data: List[(List[Char], List[Edge[Char, Nothing]])] = List( ( ('a' to 'j').toList, List( @@ -50,7 +52,7 @@ class P86Spec extends AnyFunSpec: ) data.foreach { (vertices, edges) => - val g = Graph.buildUG(vertices, edges.map((u, v) => Edge(u, v, None))) + val g = Graph.buildUG(vertices, edges) val clrMap = g.colorNodes.toMap vertices.foreach { u => g.neighbors(u).map(v => clrMap(v._1)) should not contain (clrMap(u)) diff --git a/graph/test/src/Util.scala b/graph/test/src/Util.scala new file mode 100644 index 0000000..6bf8c41 --- /dev/null +++ b/graph/test/src/Util.scala @@ -0,0 +1,7 @@ +package graph + +object Util: + given [A, B] => Conversion[Tuple, Edge[A, B]] = + case (u, v) => Edge(u.asInstanceOf[A], v.asInstanceOf[A], None) + case (u, v, d) => Edge(u.asInstanceOf[A], v.asInstanceOf[A], Some(d.asInstanceOf[B])) + case _ => throw IllegalArgumentException(s"unsupported tuple")