From 726d2f08ffe31c34203ea261f0376b10a82ac89c Mon Sep 17 00:00:00 2001 From: Peter Empen Date: Fri, 11 Oct 2024 12:56:12 +0200 Subject: [PATCH 01/34] split off coreTestScala3 from matrix workflow --- .github/workflows/ci.yml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6383cdf5..4e0154db 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,7 +2,7 @@ name: CI on: [ push, pull_request ] jobs: - build: + cross-build: strategy: fail-fast: false matrix: @@ -23,4 +23,16 @@ jobs: - name: Test run: | sbt ++${{ matrix.scala }} ${{ matrix.project }}/test scalafmtCheckAll + others: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: JDK with SBT caching + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + cache: 'sbt' + - name: Scala 3 Rewrite + run: | sbt "project coreTestScala3" test Test/scalafmt From 1a9976e55ba63dd17ee36182df253675a20bab0d Mon Sep 17 00:00:00 2001 From: Peter Empen Date: Tue, 15 Oct 2024 16:36:30 +0200 Subject: [PATCH 02/34] replace 'return' --- .../scala/collection/mutable/ExtHashSet.scala | 64 ++++++------ .../scala/scala/util/compat/Boundary.scala | 30 ++++++ .../ForeachBasedDetachingIterable.scala | 84 +++++++++------- .../collection/GraphTraversalImpl.scala | 27 +++--- .../scalax/collection/TraverserImpl.scala | 97 ++++++++++--------- .../collection/generator/RandomGraph.scala | 34 +++---- .../collection/immutable/SortedArraySet.scala | 31 +++--- .../scalax/collection/mutable/EqHash.scala | 13 +-- .../scalax/collection/mutable/ExtBitSet.scala | 38 ++++---- .../collection/mutable/SimpleArraySet.scala | 41 ++++---- 10 files changed, 259 insertions(+), 200 deletions(-) create mode 100644 core/src/main/scala/scala/util/compat/Boundary.scala diff --git a/core/src/main/scala/scala/collection/mutable/ExtHashSet.scala b/core/src/main/scala/scala/collection/mutable/ExtHashSet.scala index b67fdbdc..ff0ea192 100644 --- a/core/src/main/scala/scala/collection/mutable/ExtHashSet.scala +++ b/core/src/main/scala/scala/collection/mutable/ExtHashSet.scala @@ -9,7 +9,7 @@ * See the NOTICE file distributed with this work for * additional information regarding copyright ownership. */ -/* Extended version of https://github.com/scala/scala/blob/2.13.x/src/library/scala/collection/mutable/ExtHashSet.scala +/* Extended version of https://github.com/scala/scala/blob/2.13.x/src/library/scala/collection/mutable/HashSet.scala */ package scala.collection @@ -105,11 +105,9 @@ final class ExtHashSet[A](initialCapacity: Int, loadFactor: Double) } } - override def subtractAll(xs: IterableOnce[A]): this.type = { - if (size == 0) { - return this - } - + override def subtractAll(xs: IterableOnce[A]): this.type = + if (size == 0) this + else xs match { case hs: immutable.HashSet[A] => hs.foreachWithHashWhile { (k, h) => @@ -119,15 +117,13 @@ final class ExtHashSet[A](initialCapacity: Int, loadFactor: Double) this case hs: ExtHashSet[A] => val iter = hs.nodeIterator - while (iter.hasNext) { + while (iter.hasNext && size > 0) { val next = iter.next() remove(next.key, next.hash) - if (size == 0) return this } this case _ => super.subtractAll(xs) } - } /** Adds an element to this set * @@ -139,21 +135,29 @@ final class ExtHashSet[A](initialCapacity: Int, loadFactor: Double) table(idx) match { case null => table(idx) = new Node(elem, hash, null) + contentSize += 1 + true case old => var prev: Node[A] = null var n = old - while((n ne null) && n.hash <= hash) { - if(n.hash == hash && elem == n.key) return false - prev = n - n = n.next + var duplicate = false + while((n ne null) && !duplicate && n.hash <= hash) { + if(n.hash == hash && elem == n.key) duplicate = true + else { + prev = n + n = n.next + } + } + if (duplicate) false + else { + if (prev eq null) + table(idx) = new Node(elem, hash, old) + else + prev.next = new Node(elem, hash, prev.next) + contentSize += 1 + true } - if(prev eq null) - table(idx) = new Node(elem, hash, old) - else - prev.next = new Node(elem, hash, prev.next) } - contentSize += 1 - true } private[this] def remove(elem: A, hash: Int): Boolean = { @@ -169,16 +173,18 @@ final class ExtHashSet[A](initialCapacity: Int, loadFactor: Double) // find an element that matches var prev = nd var next = nd.next - while((next ne null) && next.hash <= hash) { + var found = false + while((next ne null) && !found && next.hash <= hash) { if(next.hash == hash && next.key == elem) { prev.next = next.next contentSize -= 1 - return true + found = true + } else { + prev = next + next = next.next } - prev = next - next = next.next } - false + found } } @@ -194,12 +200,16 @@ final class ExtHashSet[A](initialCapacity: Int, loadFactor: Double) def hasNext: Boolean = { if(node ne null) true else { - while(i < len) { + var found = false + while(!found && i < len) { val n = table(i) i += 1 - if(n ne null) { node = n; return true } + if(n ne null) { + node = n + found = true + } } - false + found } } diff --git a/core/src/main/scala/scala/util/compat/Boundary.scala b/core/src/main/scala/scala/util/compat/Boundary.scala new file mode 100644 index 00000000..e2e812f4 --- /dev/null +++ b/core/src/main/scala/scala/util/compat/Boundary.scala @@ -0,0 +1,30 @@ +package scala.util.compat + +/** Used for cross compilation, this is close to Scala 3's scala.util.boundary that does not work for Scala 2.13. + */ +final class Boundary { + import Boundary.* + + private val label = new Boundary.Label + + def break[T](value: T): Nothing = throw new Break(label, value) + + def apply[T](body: (T => Nothing) => T): T = + try body(break[T]) + catch { + case ex: Break[T] @unchecked => + if (ex.label eq label) ex.value + else throw ex + } +} + +object Boundary { + def boundary: Boundary = new Boundary + + final private class Break[T] private[Boundary] (val label: Label, val value: T) + extends RuntimeException( + /*message*/ null, /*cause*/ null, /*enableSuppression=*/ false, /*writableStackTrace*/ false + ) + + final private class Label +} diff --git a/core/src/main/scala/scalax/collection/ForeachBasedDetachingIterable.scala b/core/src/main/scala/scalax/collection/ForeachBasedDetachingIterable.scala index 044d96aa..43281b85 100644 --- a/core/src/main/scala/scalax/collection/ForeachBasedDetachingIterable.scala +++ b/core/src/main/scala/scalax/collection/ForeachBasedDetachingIterable.scala @@ -14,7 +14,8 @@ package scalax.collection import scala.collection.immutable.VectorBuilder import scala.collection.{Factory, Iterable, IterableFactory} -import scala.util.chaining._ +import scala.util.compat.Boundary.boundary +import scala.util.chaining.* /** Substitute for Scala 2.12 `Traversable` to continue support for collections that cannot implement `hasNext`/`next` easily. * The methods of Scala 2.13's `IterableOnce` are implemented in terms of `foreach`. @@ -70,25 +71,29 @@ trait ForeachBasedDetachingIterable[+A] extends Iterable[A] { final override def head: A = headOption.get - final override def headOption: Option[A] = { - for (x <- this) return Some(x) - None - } + final override def headOption: Option[A] = + boundary { break => + for (x <- this) + break(Some(x)) + None + } - final override def find(p: A => Boolean): Option[A] = { - for (x <- this) - if (p(x)) return Some(x) - None - } + final override def find(p: A => Boolean): Option[A] = + boundary { break => + for (x <- this) + if (p(x)) break(Some(x)) + None + } - final override def collectFirst[B](pf: PartialFunction[A, B]): Option[B] = { - val sentinel: A => Any = _ => this - for (x <- this) { - val r = pf.applyOrElse(x, sentinel) - if (r.asInstanceOf[AnyRef] ne sentinel) return Some(r.asInstanceOf[B]) + final override def collectFirst[B](pf: PartialFunction[A, B]): Option[B] = + boundary { break => + val sentinel: A => Any = _ => this + for (x <- this) { + val r = pf.applyOrElse(x, sentinel) + if (r.asInstanceOf[AnyRef] ne sentinel) break(Some(r.asInstanceOf[B])) + } + None } - None - } // Subcollections @@ -110,22 +115,25 @@ trait ForeachBasedDetachingIterable[+A] extends Iterable[A] { final override def slice(from: Int, until: Int): CC[A] = math.max(from, 0) pipe { from => def block(b: Builder[A]): Unit = - if (until > from) { - var i = 0 - for (x <- this) { - if (i >= from) b += x - i += 1 - if (i >= until) return + if (until > from) + boundary[Unit] { break => + var i = 0 + for (x <- this) { + if (i >= from) b += x + i += 1 + if (i >= until) break(()) + } } - } withBuilder(block) } final override def takeWhile(p: A => Boolean): CC[A] = { def block(b: Builder[A]): Unit = - for (x <- this) { - if (!p(x)) return - b += x + boundary[Unit] { break => + for (x <- this) { + if (!p(x)) break(()) + b += x + } } withBuilder(block) } @@ -168,17 +176,19 @@ trait ForeachBasedDetachingIterable[+A] extends Iterable[A] { // Element Conditions - final override def forall(p: A => Boolean): Boolean = { - for (x <- this) - if (!p(x)) return false - true - } + final override def forall(p: A => Boolean): Boolean = + boundary { break => + for (x <- this) + if (!p(x)) break(false) + true + } - final override def exists(p: A => Boolean): Boolean = { - for (x <- this) - if (p(x)) return true - false - } + final override def exists(p: A => Boolean): Boolean = + boundary { break => + for (x <- this) + if (p(x)) break(true) + false + } final override def count(p: A => Boolean): Int = { var i = 0 diff --git a/core/src/main/scala/scalax/collection/GraphTraversalImpl.scala b/core/src/main/scala/scalax/collection/GraphTraversalImpl.scala index 354dc74a..d38b4ce9 100644 --- a/core/src/main/scala/scalax/collection/GraphTraversalImpl.scala +++ b/core/src/main/scala/scalax/collection/GraphTraversalImpl.scala @@ -3,6 +3,8 @@ package scalax.collection import scala.annotation.{switch, tailrec} import scala.collection.{AbstractIterable, EqSetFacade, IndexedSeq, Seq} import scala.collection.mutable.{ArrayBuffer, Buffer, Map => MMap, Stack} +import scala.util.compat.Boundary.boundary + import scalax.collection.generic.Edge import scalax.collection.mutable.{EqHashMap, EqHashSet} @@ -288,20 +290,21 @@ trait GraphTraversalImpl[N, E <: Edge[N]] extends GraphTraversal[N, E] with Trav def findCycle[U](implicit visitor: InnerElem => U = Visitor.empty): Option[Cycle] = if (order == 0) None - else { - val traverser = innerElemTraverser - withHandles(2) { handles => - implicit val visitedHandle: State.Handle = handles(0) - for (node <- nodes if !node.visited && subgraphNodes(node)) { - val nodeTraverser: InnerElemTraverserImpl = - traverser.withRoot(node) // TODO not sure why this declaration is needed - val res = nodeTraverser.Runner(noNode, visitor).dfsWGB(handles) - if (res.isDefined) - return cycle(res, subgraphEdges) + else + boundary { break => + val traverser = innerElemTraverser + withHandles(2) { handles => + implicit val visitedHandle: State.Handle = handles(0) + for (node <- nodes if !node.visited && subgraphNodes(node)) { + val nodeTraverser: InnerElemTraverserImpl = + traverser.withRoot(node) // TODO not sure why this declaration is needed + val res = nodeTraverser.Runner(noNode, visitor).dfsWGB(handles) + if (res.isDefined) + break(cycle(res, subgraphEdges)) + } } + None } - None - } final def topologicalSort[U](implicit visitor: InnerElem => U = Visitor.empty): TopologicalSort = innerElemTraverser diff --git a/core/src/main/scala/scalax/collection/TraverserImpl.scala b/core/src/main/scala/scalax/collection/TraverserImpl.scala index 2f2f3a8e..4acca87d 100644 --- a/core/src/main/scala/scalax/collection/TraverserImpl.scala +++ b/core/src/main/scala/scalax/collection/TraverserImpl.scala @@ -4,6 +4,7 @@ import scala.annotation.{switch, tailrec} import scala.collection.{FilterableSet, FilteredSet} import scala.collection.mutable.{ArrayBuffer, Map => MMap, PriorityQueue, Queue, Stack} import scala.reflect.ClassTag +import scala.util.compat.Boundary.boundary import scalax.collection.generic.Edge import scalax.collection.immutable.SortedArraySet @@ -425,22 +426,24 @@ trait TraverserImpl[N, E <: Edge[N]] { } visit(root) - while (q.nonEmpty) { - val Element(prevNode, prevDepth, cumWeight) = q.dequeue() - if (prevDepth < untilDepth) { - depth = prevDepth + 1 - for (n <- filteredNodes(prevNode, nonVisited, cumWeight, false)) { - visit(n) - if (stopAt(n, nodeCnt, depth)) return Some(n) - q enqueue Element( - n, - depth, - maxWeight.fold(ifEmpty = 0d)(w => cumWeight + minWeight(prevNode, n, cumWeight)) - ) + boundary { break => + while (q.nonEmpty) { + val Element(prevNode, prevDepth, cumWeight) = q.dequeue() + if (prevDepth < untilDepth) { + depth = prevDepth + 1 + for (n <- filteredNodes(prevNode, nonVisited, cumWeight, false)) { + visit(n) + if (stopAt(n, nodeCnt, depth)) break(Some(n)) + q enqueue Element( + n, + depth, + maxWeight.fold(ifEmpty = 0d)(w => cumWeight + minWeight(prevNode, n, cumWeight)) + ) + } } } + None } - None } @inline protected[collection] def dfs[U](maybeHandle: Option[Handle] = None): Option[NodeT] = @@ -697,41 +700,45 @@ trait TraverserImpl[N, E <: Edge[N]] { def mixedCycle(blackSuccessors: Iterable[NodeT]): Option[(NodeT, Stack[CycleStackElem])] = withHandle() { handle => - val visitedBlackHandle = Some(handle) - for (n <- blackSuccessors) - thisImpl - .withRoot(n) - .withSubgraph(n => subgraphNodes(n) && !isWhite(n) && (n ne current), subgraphEdges) - .pathUntil_(isGray, maybeHandle = visitedBlackHandle) - .foreach { missingPath => - val start = missingPath.endNode - val shortenedPath = { - var found = false - path takeWhile { case CycleStackElem(n, _) => - if (n eq start) { - found = true - true - } else !found - } - } - if ( - mustContain.forall { n => - missingPath.nodes.exists(_ eq n) || - shortenedPath.exists { case CycleStackElem(pn, _) => pn eq n } + boundary { break => + val visitedBlackHandle = Some(handle) + for (n <- blackSuccessors) + thisImpl + .withRoot(n) + .withSubgraph(n => subgraphNodes(n) && !isWhite(n) && (n ne current), subgraphEdges) + .pathUntil_(isGray, maybeHandle = visitedBlackHandle) + .foreach { missingPath => + val start = missingPath.endNode + val shortenedPath = { + var found = false + path takeWhile { case CycleStackElem(n, _) => + if (n eq start) { + found = true + true + } else !found + } } - ) { - missingPath.foldLeft((missingPath.nodes.head connectionsWith shortenedPath.head.node).head) { - case (edge, inner: InnerNode) => - val n = inner.asInstanceOf[NodeT] - if (isBlack(n)) - shortenedPath.push(new CycleStackElem(n, Set(edge))) - edge - case (_, InnerEdge(e, _)) => e + if ( + mustContain.forall { n => + missingPath.nodes.exists(_ eq n) || + shortenedPath.exists { case CycleStackElem(pn, _) => pn eq n } + } + ) { + missingPath.foldLeft( + (missingPath.nodes.head connectionsWith shortenedPath.head.node).head + ) { + case (edge, inner: InnerNode) => + val n = inner.asInstanceOf[NodeT] + if (isBlack(n)) + shortenedPath.push(new CycleStackElem(n, Set(edge))) + edge + case (_, InnerEdge(e, _)) => e + } + break(Some(start, shortenedPath)) } - return Some(start, shortenedPath) } - } - None + None + } } def cycle(graySuccessors: Iterable[NodeT]): Option[(NodeT, Stack[CycleStackElem])] = diff --git a/core/src/main/scala/scalax/collection/generator/RandomGraph.scala b/core/src/main/scala/scalax/collection/generator/RandomGraph.scala index afd675d9..f5984e41 100644 --- a/core/src/main/scala/scalax/collection/generator/RandomGraph.scala +++ b/core/src/main/scala/scalax/collection/generator/RandomGraph.scala @@ -1,13 +1,14 @@ package scalax.collection package generator +import scala.annotation.tailrec import scala.collection.mutable.{ArrayBuffer, Set => MSet} import scala.util.Random import scala.reflect.ClassTag import scalax.collection.config.GraphConfig -import scalax.collection.edges._ -import scalax.collection.generic._ +import scalax.collection.edges.* +import scalax.collection.generic.* /* TODO L import edge.WBase.{WEdgeCompanion, WHyperEdgeCompanion} import edge.LBase.{LEdgeCompanion, LHyperEdgeCompanion} @@ -87,27 +88,20 @@ class RandomGraph[N, E <: Edge[N], G[X, Y <: Edge[X]] <: AnyGraph[X, Y] with Gra } private def addExact[A](nrToAdd: Int, add: => Boolean, infiniteMsg: String, gentle: Boolean): Boolean = { - val checkInfiniteAt = math.pow(math.log(nrToAdd), 2).ceil.toInt + 10 - val infiniteFactor = 5 + val assumeInfiniteAt = (nrToAdd * 3) + math.log(nrToAdd * 10).ceil.toInt - var added, lastAdded = 0 - var trials = 0 - while (added < nrToAdd) { - if (add) added += 1 + @tailrec def loop(added: Int, missed: Int): Boolean = + if (added < nrToAdd) + if (missed > assumeInfiniteAt) { + traceln(s"gentle=$gentle, added=$added, missed=$missed, ") - trials += 1 - if (trials == checkInfiniteAt) { - if ((added - lastAdded) * infiniteFactor < trials) { - traceln(s"gentle=$gentle trials=$trials, lastAdded=$lastAdded, added=$added") - - if (gentle) return false + if (gentle) false else throw new IllegalArgumentException(infiniteMsg) - } - trials = 0 - lastAdded = added - } - } - true + } else if (add) loop(added + 1, missed) + else loop(added, missed + 1) + else true + + loop(0, 0) } final protected[RandomGraph] class Degrees { diff --git a/core/src/main/scala/scalax/collection/immutable/SortedArraySet.scala b/core/src/main/scala/scalax/collection/immutable/SortedArraySet.scala index 614f6959..fe96a208 100644 --- a/core/src/main/scala/scalax/collection/immutable/SortedArraySet.scala +++ b/core/src/main/scala/scalax/collection/immutable/SortedArraySet.scala @@ -65,23 +65,24 @@ class SortedArraySet[A](array: Array[A] = new Array[AnyRef](0).asInstanceOf[Arra if (found == -1) None else Some(found) } - def rangeImpl(from: Option[A], until: Option[A]): SortedArraySet[A] = { - if (size == 0 || from == None && until == None) return this - val idxFrom = from flatMap (search(_, ordering.lt)) getOrElse 0 - val idxTill = (until flatMap (e => - search(e, ordering.lt) orElse ( - if (ordering.gt(e, array(size - 1))) Some(size) - else Some(-1) - ) - ) getOrElse size) - 1 - if (idxFrom > idxTill) empty + def rangeImpl(from: Option[A], until: Option[A]): SortedArraySet[A] = + if (size == 0 || from == None && until == None) this else { - val newSize = idxTill - idxFrom + 1 - val newArr: Array[AnyRef] = new Array(newSize) - arraycopy(array, idxFrom, newArr, 0, newSize) - new SortedArraySet(newArr.asInstanceOf[Array[A]]) + val idxFrom = from flatMap (search(_, ordering.lt)) getOrElse 0 + val idxTill = (until flatMap (e => + search(e, ordering.lt) orElse ( + if (ordering.gt(e, array(size - 1))) Some(size) + else Some(-1) + ) + ) getOrElse size) - 1 + if (idxFrom > idxTill) empty + else { + val newSize = idxTill - idxFrom + 1 + val newArr: Array[AnyRef] = new Array(newSize) + arraycopy(array, idxFrom, newArr, 0, newSize) + new SortedArraySet(newArr.asInstanceOf[Array[A]]) + } } - } def find(elem: A): Option[A] = { val i = array.indexOf(elem) diff --git a/core/src/main/scala/scalax/collection/mutable/EqHash.scala b/core/src/main/scala/scalax/collection/mutable/EqHash.scala index 4ab3af21..28c57fb5 100644 --- a/core/src/main/scala/scalax/collection/mutable/EqHash.scala +++ b/core/src/main/scala/scalax/collection/mutable/EqHash.scala @@ -2,6 +2,7 @@ package scalax.collection.mutable import scala.collection.Util.nextPositivePowerOfTwo import scala.collection.mutable.Growable +import scala.util.compat.Boundary.boundary trait EqHash[A, C <: EqHash[A, C]] { this: IterableOnce[A] with Growable[A] with Equals => @@ -45,15 +46,15 @@ trait EqHash[A, C <: EqHash[A, C]] { else nextPositivePowerOfTwo(min) } - protected def index(maskedKey: AnyRef, keyHash: Int, tabLength: Int): Int = { + protected def index(maskedKey: AnyRef, keyHash: Int, tabLength: Int): Int = boundary { break => val tab = table var i = keyHash while (true) { val item = tab(i) if (item eq maskedKey) - return i + break(i) else if (item eq null) - return ~i + break(~i) else i = nextKeyIndex(i, tabLength) } @@ -120,14 +121,14 @@ trait EqHash[A, C <: EqHash[A, C]] { private[this] var lastReturnedIndex = -1 private[this] var indexValid = false - def hasNext: Boolean = { + def hasNext: Boolean = boundary { break => val s = step var i = index while (i < len) { if (tab(i) ne null) { index = i indexValid = true - return true + break(true) } i += s } @@ -141,7 +142,7 @@ trait EqHash[A, C <: EqHash[A, C]] { indexValid = false lastReturnedIndex = index index += step - return lastReturnedIndex + lastReturnedIndex } } diff --git a/core/src/main/scala/scalax/collection/mutable/ExtBitSet.scala b/core/src/main/scala/scalax/collection/mutable/ExtBitSet.scala index 93bd1ffd..016e590f 100644 --- a/core/src/main/scala/scalax/collection/mutable/ExtBitSet.scala +++ b/core/src/main/scala/scalax/collection/mutable/ExtBitSet.scala @@ -1,10 +1,12 @@ package scalax.collection package mutable +import java.lang.Long.{bitCount, lowestOneBit => jLowestOneBit, toBinaryString} +import scala.annotation.tailrec import scala.collection.mutable.BitSet import State.Handle -import ExtBitSet._ +import ExtBitSet.* final protected[collection] class ExtBitSet(words: Array[Long]) extends BitSet(words) { def this(initWords: Int = incrWords) = this(new Array[Long](initWords)) @@ -22,11 +24,11 @@ final protected[collection] class ExtBitSet(words: Array[Long]) extends BitSet(w /** All bits of all words. */ override def toString = - (elems map (w => "%64s".format(java.lang.Long.toBinaryString(w)).replace(' ', '0'))).mkString(" ") + (elems map (w => "%64s".format(toBinaryString(w)).replace(' ', '0'))).mkString(" ") /** Summary of the words each of which formatted as :. */ def summary: String = - elems.zipWithIndex.map(zWord => "%2d:%2d" format (zWord._2, java.lang.Long.bitCount(zWord._1))) mkString " " + elems.zipWithIndex.map(zWord => "%2d:%2d" format (zWord._2, bitCount(zWord._1))) mkString " " @inline def apply(idx: Int, mask: Long): Boolean = (word(idx) & mask) != 0 @@ -64,23 +66,25 @@ final protected[collection] class ExtBitSet(words: Array[Long]) extends BitSet(w } def onOrFindUnset(other: ExtBitSet): Option[Handle] = { - var idx = 0 - while (idx < nwords) { - val or = elems(idx) | other.word(idx) - if (or == ~0) idx += 1 - else return Some(new Handle(idx, java.lang.Long.lowestOneBit(~or))) - } - None + @tailrec def loop(idx: Int): Option[Handle] = + if (idx < nwords) { + val or = elems(idx) | other.word(idx) + if (or == ~0) loop(idx + 1) + else Some(new Handle(idx, jLowestOneBit(~or))) + } else None + + loop(0) } def lowestOneBit: Option[Handle] = { - var idx = 0 - while (idx < nwords) { - val bit = java.lang.Long.lowestOneBit(elems(idx)) - if (bit == 0) idx += 1 - else return Some(new Handle(idx, bit)) - } - None + @tailrec def loop(idx: Int): Option[Handle] = + if (idx < nwords) { + val bit = jLowestOneBit(elems(idx)) + if (bit == 0) loop(idx + 1) + else Some(new Handle(idx, bit)) + } else None + + loop(0) } private def expand(mustHaveIdx: Int): Unit = { diff --git a/core/src/main/scala/scalax/collection/mutable/SimpleArraySet.scala b/core/src/main/scala/scalax/collection/mutable/SimpleArraySet.scala index 4d041b19..3f9f3e58 100644 --- a/core/src/main/scala/scalax/collection/mutable/SimpleArraySet.scala +++ b/core/src/main/scala/scalax/collection/mutable/SimpleArraySet.scala @@ -1,6 +1,7 @@ package scalax.collection package mutable +import scala.annotation.tailrec import scala.collection.mutable.{ExtHashSet, GrowableBuilder} import scala.collection.{IterableFactory, IterableFactoryDefaults, SortedSet, StrictOptimizedIterableOps} import scala.util.Random @@ -60,11 +61,9 @@ final class SimpleArraySet[A](override val hints: ArraySet.Hints) protected[collection] def +=!(elem: A): this.type = { if (isHash) hashSet add elem + else if (nextFree == capacity && resizedToHash) + add(elem) else { - if (nextFree == capacity) - if (resizedToHash) { - add(elem); return this - } arr(nextFree) = elem nextFree += 1 } @@ -128,20 +127,24 @@ final class SimpleArraySet[A](override val hints: ArraySet.Hints) resizeArray(capacity, nextFree) protected def indexOf[B](elem: B, pred: (A, B) => Boolean): Int = { - var i = 0 - while (i < nextFree) - if (pred(arr(i), elem)) return i - else i += 1 - -1 + @tailrec def loop(i: Int): Int = + if (i < nextFree) + if (pred(arr(i), elem)) i + else loop(i + 1) + else -1 + + loop(0) } /* Optimized 'arr contains c'. */ protected def indexOf(elem: A): Int = { - var i = 0 - while (i < nextFree) - if (arr(i) == elem) return i - else i += 1 - -1 + @tailrec def loop(i: Int): Int = + if (i < nextFree) + if (arr(i) == elem) i + else loop(i + 1) + else -1 + + loop(0) } override def contains(elem: A): Boolean = @@ -157,14 +160,10 @@ final class SimpleArraySet[A](override val hints: ArraySet.Hints) override def add(elem: A): Boolean = if (isHash) hashSet add elem + else if (nextFree == capacity && resizedToHash) + add(elem) + else if (indexOf(elem) >= 0) false else { - if (nextFree == capacity) - if (resizedToHash) - return add(elem) - var i = 0 - while (i < nextFree) - if (arr(i) == elem) return false - else i += 1 arr(nextFree) = elem nextFree += 1 true From b08180b77f4cf954820713675462ccda5b86a295 Mon Sep 17 00:00:00 2001 From: Peter Empen Date: Tue, 15 Oct 2024 18:03:26 +0200 Subject: [PATCH 03/34] add 'uses: sbt/setup-sbt@v1' to workflow jobs --- .github/workflows/ci.yml | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4e0154db..9acfd9f4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,25 +1,25 @@ name: CI on: [ push, pull_request ] - jobs: cross-build: strategy: fail-fast: false matrix: - scala: [ "2.13.15", "3.3.0" ] - project: [ "core", "dot", "json" ] + scala: [ '2.13.15', '3.3.0' ] + project: [ core, dot, json ] exclude: - - scala: "3.3.0" - project: "json" + - scala: '3.3.0' + project: json runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: JDK with SBT caching uses: actions/setup-java@v4 with: - java-version: '17' - distribution: 'temurin' - cache: 'sbt' + java-version: 17 + distribution: temurin + cache: sbt + - uses: sbt/setup-sbt@v1 - name: Test run: | sbt ++${{ matrix.scala }} ${{ matrix.project }}/test scalafmtCheckAll @@ -30,9 +30,10 @@ jobs: - name: JDK with SBT caching uses: actions/setup-java@v4 with: - java-version: '17' - distribution: 'temurin' - cache: 'sbt' + java-version: 17 + distribution: temurin + cache: sbt + - uses: sbt/setup-sbt@v1 - name: Scala 3 Rewrite run: | sbt "project coreTestScala3" test Test/scalafmt From ed53fb55581c9119270e7faf7807a90746ca97c1 Mon Sep 17 00:00:00 2001 From: Peter Empen Date: Sat, 9 Nov 2024 14:45:42 +0100 Subject: [PATCH 04/34] use Stack's reverseIterator --- .../scalax/collection/GraphTraversal.scala | 8 +- .../collection/GraphTraversalImpl.scala | 84 ++++++++----------- .../scalax/collection/TraverserImpl.scala | 4 +- 3 files changed, 41 insertions(+), 55 deletions(-) diff --git a/core/src/main/scala/scalax/collection/GraphTraversal.scala b/core/src/main/scala/scalax/collection/GraphTraversal.scala index 8e564750..3bd4add9 100644 --- a/core/src/main/scala/scalax/collection/GraphTraversal.scala +++ b/core/src/main/scala/scalax/collection/GraphTraversal.scala @@ -512,11 +512,14 @@ trait GraphTraversal[N, E <: Edge[N]] extends GraphBase[N, E, GraphTraversal] { * returns also `true` if the elements of `that` cycle can be shifted and optionally * reversed such that their elements have the same order. For instance, given * - * `c1 = Cycle(1-2-3-1)`, `c2 = Cycle(2-3-1-2)` and `c3 = Cycle(2-1-3-2)` + * `c1 = Cycle(1-2-3-1)` + * `c2 = Cycle(2-3-1-2)` + * `c3 = Cycle(2-1-3-2)` * * the following expressions hold: * - * `c1 != c2`, `c1 != c3` but `c1 sameAs c2` and `c1 sameAs c3`. + * `c1 != c2`, `c1 != c3` but + * `c1 sameAs c2` and `c1 sameAs c3`. */ final def sameAs(that: GraphTraversal[N, E]#Cycle): Boolean = this == that || (that match { @@ -1428,7 +1431,6 @@ trait GraphTraversal[N, E <: Edge[N]] extends GraphBase[N, E, GraphTraversal] { * the number of consecutive child visits before siblings are visited for DFS. * `0` - the default - indicates that the traversal should have * an unlimited depth. - * @author Peter Empen */ object GraphTraversal { diff --git a/core/src/main/scala/scalax/collection/GraphTraversalImpl.scala b/core/src/main/scala/scalax/collection/GraphTraversalImpl.scala index d38b4ce9..80e477c8 100644 --- a/core/src/main/scala/scalax/collection/GraphTraversalImpl.scala +++ b/core/src/main/scala/scalax/collection/GraphTraversalImpl.scala @@ -1,7 +1,7 @@ package scalax.collection import scala.annotation.{switch, tailrec} -import scala.collection.{AbstractIterable, EqSetFacade, IndexedSeq, Seq} +import scala.collection.{mutable, AbstractIterable, EqSetFacade, IndexedSeq, Seq} import scala.collection.mutable.{ArrayBuffer, Buffer, Map => MMap, Stack} import scala.util.compat.Boundary.boundary @@ -29,7 +29,7 @@ trait GraphTraversalImpl[N, E <: Edge[N]] extends GraphTraversal[N, E] with Trav ): Option[Cycle] = maybeStart map { start => new AnyEdgeLazyCycle( - new ReverseStackTraversable[DfsElem](stack, None, Array[Option[DfsElem]](None, Some(DfsElem(start)))), + new CycleNodes[DfsElem](stack, None, prefix = None, postfix = DfsElem(start)), edgeFilter ) } @@ -37,14 +37,18 @@ trait GraphTraversalImpl[N, E <: Edge[N]] extends GraphTraversal[N, E] with Trav final protected def cycle(results: Option[(NodeT, Stack[CycleStackElem])], edgeFilter: EdgePredicate): Option[Cycle] = results match { case Some((start, stack)) => - val reverse = new ReverseStackTraversable[CycleStackElem]( - stack, - Some((elem: CycleStackElem) => elem.node ne start), - Array.fill[Option[CycleStackElem]](2)(Some(CycleStackElem(start))) - ) + val iterable = { + val affix = CycleStackElem(start) + new CycleNodes[CycleStackElem]( + stack, + Some((elem: CycleStackElem) => elem.node ne start), + Some(affix), + affix + ) + } Some( - if (thisGraph.isDirected) new AnyEdgeLazyCycle(reverse, edgeFilter) - else new MultiEdgeLazyCycle(reverse, edgeFilter) + if (thisGraph.isDirected) new AnyEdgeLazyCycle(iterable, edgeFilter) + else new MultiEdgeLazyCycle(iterable, edgeFilter) ) case _ => None } @@ -641,59 +645,38 @@ trait GraphTraversalImpl[N, E <: Edge[N]] extends GraphTraversal[N, E] with Trav ): OuterNodeDownUpTraverser = OuterNodeDownUpTraverserImpl(root, parameters, subgraphNodes, subgraphEdges, ordering, maxWeight) - /** Efficient reverse `foreach` overcoming `Stack`'s deficiency not to overwrite `reverseIterator`. - */ - // TODO is this still needed? Stack now _does_ override `reverseIterator`. - final protected class ReverseStackTraversable[S <: NodeElement]( - s: IndexedSeq[S], - takeWhile: Option[S => Boolean] = None, - enclosed: Array[Option[S]] = Array[Option[S]](None, None) + final protected class CycleNodes[S <: NodeElement]( + stack: mutable.Stack[S], + strip: Option[S => Boolean] = None, + prefix: Option[S], + postfix: S ) extends Iterable[NodeT] { override def iterator = source.map(_.node).iterator final override protected def className = "Nodes" - private[this] var _size: Option[Int] = None - @inline override val size: Int = _size getOrElse super.size - - @inline override def last: NodeT = enclosed(1).fold(ifEmpty = s.head.node)(_.node) - - // TODO unreachable? - def reverse: Iterable[NodeT] = new AbstractIterable[NodeT] { - override def iterator: Iterator[NodeT] = ??? - /* TODO replace foreach with iterator - def foreach[U](f: NodeT => U): Unit = { - def fT(elem: S): Unit = f(elem.node) - def end(i: Int): Unit = enclosed(i) foreach fT - end(1) - s foreach fT - end(0) - } - */ + private val skipCount: Int = strip.fold(ifEmpty = 1) { pred => + val it = stack.reverseIterator + var i = 1 + while (it.hasNext && pred(it.next())) i += 1 + i } - private lazy val upper: Int = takeWhile.fold(ifEmpty = s.size) { pred => - var i = s.size - 1 - while (i >= 0 && pred(s(i))) i -= 1 - if (i < 0) 0 else i - } + @inline override val size: Int = stack.size - skipCount + prefix.fold(0)(_ => 1) + 1 + @inline override val last: NodeT = postfix.node - private[GraphTraversalImpl] lazy val source: Iterable[S] = new AbstractIterable[S] { + private[GraphTraversalImpl] def source: Iterable[S] = new AbstractIterable[S] { override def iterator = { val buffer = ArrayBuffer[S]() foreach(buffer += _) buffer.iterator } + override def foreach[U](f: S => U): Unit = { - enclosed(0) foreach f - var i = upper - while (i > 0) { - i -= 1 - f(s(i)) - } - enclosed(1) foreach f - if (_size.isEmpty) _size = Some(upper + enclosed.count(_.isDefined)) + prefix foreach f + stack.reverseIterator.drop(skipCount) foreach f + f(postfix) } } } @@ -737,7 +720,8 @@ trait GraphTraversalImpl[N, E <: Edge[N]] extends GraphTraversal[N, E] with Trav that.toArray[AnyGraph#InnerElem].sameElements(toArray[InnerElem]) case _ => false } - override def hashCode: Int = nodes.## + 27 * edges.## + + final override def hashCode: Int = nodes.## + 27 * edges.## } /** `LazyPath` with deferred edges selection. @@ -788,7 +772,7 @@ trait GraphTraversalImpl[N, E <: Edge[N]] extends GraphTraversal[N, E] with Trav /** `LazyPath` with edge selection such that there exists no duplicate edge in the path. */ protected class MultiEdgeLazyPath( - override val nodes: ReverseStackTraversable[CycleStackElem], + override val nodes: CycleNodes[CycleStackElem], edgeFilter: EdgePredicate ) extends LazyPath(nodes) { @@ -829,7 +813,7 @@ trait GraphTraversalImpl[N, E <: Edge[N]] extends GraphTraversal[N, E] with Trav with Cycle protected class MultiEdgeLazyCycle( - override val nodes: ReverseStackTraversable[CycleStackElem], + override val nodes: CycleNodes[CycleStackElem], edgeFilter: EdgePredicate ) extends MultiEdgeLazyPath(nodes, edgeFilter) with Cycle diff --git a/core/src/main/scala/scalax/collection/TraverserImpl.scala b/core/src/main/scala/scalax/collection/TraverserImpl.scala index 4acca87d..637f70b0 100644 --- a/core/src/main/scala/scalax/collection/TraverserImpl.scala +++ b/core/src/main/scala/scalax/collection/TraverserImpl.scala @@ -44,9 +44,9 @@ trait TraverserImpl[N, E <: Edge[N]] { ): Option[Path] = requireSuccessors { Runner[U](pred, visitor).dfsStack() match { - case (target, path) => + case (target, stack) => target map { _ => - new AnyEdgeLazyPath(new ReverseStackTraversable[DfsInformer.Element](path), subgraphEdges) + new AnyEdgeLazyPath(Iterable.from(stack.reverseIterator.map(_.node)), subgraphEdges) } } } From a6ae26af5062f08ee480e0a607eeea4d7fcdb992 Mon Sep 17 00:00:00 2001 From: Peter Empen Date: Sat, 9 Nov 2024 14:56:53 +0100 Subject: [PATCH 05/34] scaladoc --- .../scalax/collection/GraphTraversal.scala | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/core/src/main/scala/scalax/collection/GraphTraversal.scala b/core/src/main/scala/scalax/collection/GraphTraversal.scala index 3bd4add9..dc306ea1 100644 --- a/core/src/main/scala/scalax/collection/GraphTraversal.scala +++ b/core/src/main/scala/scalax/collection/GraphTraversal.scala @@ -176,10 +176,11 @@ trait GraphTraversal[N, E <: Edge[N]] extends GraphBase[N, E, GraphTraversal] { } /** Layers of a topological order of a graph or of an isolated graph component. - * The layers of a topological sort can roughly be defined as follows: - * a. layer 0 contains all nodes having no predecessors, - * a. layer n contains those nodes that have only predecessors in ancestor layers - * with at least one of them contained in layer n - 1 + * + * The layers of a topological sort can roughly be defined as follows: + * a. layer 0 contains all nodes having no predecessors + * a. layer n contains those nodes that have only predecessors in ancestor layers + * with at least one of them contained in layer n - 1 * @tparam A one of `NodeT`, `N` */ final class LayeredTopologicalOrder[+A] protected[collection] ( @@ -478,9 +479,9 @@ trait GraphTraversal[N, E <: Edge[N]] extends GraphBase[N, E, GraphTraversal] { /** Represents a cycle in this graph listing the nodes and connecting edges on it * with the following syntax: - * + * {{{ * `cycle ::= ''start-end-node'' { ''edge'' ''node'' } ''edge'' ''start-end-node''` - * + * }}} * All nodes and edges on the path are distinct except the start and end nodes that * are equal. A cycle contains at least a start node followed by any number of * consecutive pairs of an edge and a node and the end node equaling to the start node. @@ -511,15 +512,16 @@ trait GraphTraversal[N, E <: Edge[N]] extends GraphBase[N, E, GraphTraversal] { * only if the cycles contain the same elements in the same order, this comparison * returns also `true` if the elements of `that` cycle can be shifted and optionally * reversed such that their elements have the same order. For instance, given - * - * `c1 = Cycle(1-2-3-1)` - * `c2 = Cycle(2-3-1-2)` - * `c3 = Cycle(2-1-3-2)` - * + * {{{ + * c1 = Cycle(1-2-3-1) + * c2 = Cycle(2-3-1-2) + * c3 = Cycle(2-1-3-2) + * }}} * the following expressions hold: - * + * {{{ * `c1 != c2`, `c1 != c3` but - * `c1 sameAs c2` and `c1 sameAs c3`. + * `c1 sameAs c2` and `c1 sameAs c3` + * }}} */ final def sameAs(that: GraphTraversal[N, E]#Cycle): Boolean = this == that || (that match { From 9752665c18ea50aaafd6e1bb462c178292e7e257 Mon Sep 17 00:00:00 2001 From: Peter Empen Date: Sat, 1 Feb 2025 09:57:26 +0100 Subject: [PATCH 06/34] add cycle test --- core/src/test/scala/scalax/collection/CycleSpec.scala | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/core/src/test/scala/scalax/collection/CycleSpec.scala b/core/src/test/scala/scalax/collection/CycleSpec.scala index 5413290b..511d8ea9 100644 --- a/core/src/test/scala/scalax/collection/CycleSpec.scala +++ b/core/src/test/scala/scalax/collection/CycleSpec.scala @@ -127,6 +127,16 @@ private class Cycle[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E, C } } + def `cycles may be compared by 'sameAs'`(): Unit = { + val g1 = factory(1 ~> 2, 2 ~> 1).asAnyGraph + (g1 get 1 findCycle, g1 get 2 findCycle) match { + case (Some(c1), Some(c2)) => + c1.startNode shouldNot be (c2.startNode) + c1 sameAs c2 shouldBe true + case (x, y) => fail(s"Cycles expected, got ($x, $y ) instead.") + } + } + def `the cycle returned by 'findCycleContaining' contains the expected nodes`: Unit = { withGraph(acyclic_1) { g => g.findCycleContaining(g get 1) should be(None) From 570e8e68742e0ba94ad4deb554a72162fb3a2a45 Mon Sep 17 00:00:00 2001 From: Peter Empen Date: Sat, 1 Feb 2025 10:16:15 +0100 Subject: [PATCH 07/34] scalafmt --- .../src/main/scala/scalax/collection/GraphTraversalImpl.scala | 4 ++-- core/src/test/scala/scalax/collection/CycleSpec.scala | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/scala/scalax/collection/GraphTraversalImpl.scala b/core/src/main/scala/scalax/collection/GraphTraversalImpl.scala index 80e477c8..2c316a62 100644 --- a/core/src/main/scala/scalax/collection/GraphTraversalImpl.scala +++ b/core/src/main/scala/scalax/collection/GraphTraversalImpl.scala @@ -663,8 +663,8 @@ trait GraphTraversalImpl[N, E <: Edge[N]] extends GraphTraversal[N, E] with Trav i } - @inline override val size: Int = stack.size - skipCount + prefix.fold(0)(_ => 1) + 1 - @inline override val last: NodeT = postfix.node + @inline override val size: Int = stack.size - skipCount + prefix.fold(0)(_ => 1) + 1 + @inline override val last: NodeT = postfix.node private[GraphTraversalImpl] def source: Iterable[S] = new AbstractIterable[S] { override def iterator = { diff --git a/core/src/test/scala/scalax/collection/CycleSpec.scala b/core/src/test/scala/scalax/collection/CycleSpec.scala index 511d8ea9..7aaf574f 100644 --- a/core/src/test/scala/scalax/collection/CycleSpec.scala +++ b/core/src/test/scala/scalax/collection/CycleSpec.scala @@ -131,7 +131,7 @@ private class Cycle[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E, C val g1 = factory(1 ~> 2, 2 ~> 1).asAnyGraph (g1 get 1 findCycle, g1 get 2 findCycle) match { case (Some(c1), Some(c2)) => - c1.startNode shouldNot be (c2.startNode) + c1.startNode shouldNot be(c2.startNode) c1 sameAs c2 shouldBe true case (x, y) => fail(s"Cycles expected, got ($x, $y ) instead.") } From 951e939beb5d69c9cb8090737f24e6f59a962291 Mon Sep 17 00:00:00 2001 From: Marcus Date: Thu, 27 Feb 2025 09:11:11 +0100 Subject: [PATCH 08/34] GraphLike: override hashCode. (#357) --- core/src/main/scala/scalax/collection/AnyGraph.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/src/main/scala/scalax/collection/AnyGraph.scala b/core/src/main/scala/scalax/collection/AnyGraph.scala index cc218b52..32811531 100644 --- a/core/src/main/scala/scalax/collection/AnyGraph.scala +++ b/core/src/main/scala/scalax/collection/AnyGraph.scala @@ -88,6 +88,8 @@ trait GraphLike[N, E <: Edge[N], +CC[X, Y <: Edge[X]] <: GraphLike[X, Y, CC] wit false } + override def hashCode(): Int = this.nodes.toOuter.## + 31 * this.edges.toOuter.## + type NodeT <: GraphInnerNode trait GraphInnerNode extends BaseInnerNode with TraverserInnerNode { this: NodeT => From c9ecd0079cc58171162f58d526a2ed22b5e81eb1 Mon Sep 17 00:00:00 2001 From: Peter Empen Date: Fri, 28 Feb 2025 17:53:21 +0100 Subject: [PATCH 09/34] test and optimize Graph.hashCode --- .../main/scala/scalax/collection/AnyGraph.scala | 8 +++++++- .../scala/scalax/collection/EqualitySpec.scala | 17 ++++++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/core/src/main/scala/scalax/collection/AnyGraph.scala b/core/src/main/scala/scalax/collection/AnyGraph.scala index 32811531..35556d3c 100644 --- a/core/src/main/scala/scalax/collection/AnyGraph.scala +++ b/core/src/main/scala/scalax/collection/AnyGraph.scala @@ -88,7 +88,13 @@ trait GraphLike[N, E <: Edge[N], +CC[X, Y <: Edge[X]] <: GraphLike[X, Y, CC] wit false } - override def hashCode(): Int = this.nodes.toOuter.## + 31 * this.edges.toOuter.## + override def hashCode: Int = { + import scala.util.hashing.MurmurHash3.{finalizeHash, mix, mixLast, productSeed} + var h = productSeed + h = mix(h, this.nodes.##) + h = mixLast(h, this.edges.##) + finalizeHash(h, 2) + } type NodeT <: GraphInnerNode trait GraphInnerNode extends BaseInnerNode with TraverserInnerNode { this: NodeT => diff --git a/core/src/test/scala/scalax/collection/EqualitySpec.scala b/core/src/test/scala/scalax/collection/EqualitySpec.scala index df0d472c..0986b3e0 100644 --- a/core/src/test/scala/scalax/collection/EqualitySpec.scala +++ b/core/src/test/scala/scalax/collection/EqualitySpec.scala @@ -20,12 +20,13 @@ private class Equality[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E val factory: GenericGraphCoreFactory[CC] ) extends RefSpec with Matchers { + info(factory.getClass.getPackage.toString) private val seq_1_3 = Seq(1, 3) private val gInt_1_3 = factory(seq_1_3.toOuterElems[DiEdge[Int]]: _*) private val gString_A = factory("A") - def `Eq ` : Unit = { + def `Graph equals ` : Unit = { factory[Int, Nothing]() shouldEqual factory[Int, DiEdge]() gInt_1_3 shouldEqual factory(1, 3) gString_A shouldEqual factory("A") @@ -36,6 +37,20 @@ private class Equality[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E gInt_1_3 shouldEqual immutable.Graph(1) + 3 } + + def `Graph hashCode`(): Unit = { + factory(1).hashCode shouldBe factory(1).hashCode + factory(1).hashCode shouldNot be(factory(2).hashCode) + + factory(1 ~> 2).hashCode shouldBe factory(1 ~> 2).hashCode + factory(1 ~> 2).hashCode shouldNot be(factory(2 ~> 1).hashCode) + + factory(1 ~ 2).hashCode shouldBe factory(1 ~ 2).hashCode + factory(1 ~ 2).hashCode shouldNot be(factory(1 ~ 3).hashCode) + + factory(1 ~ 2, 2 ~ 3).hashCode shouldBe factory(1 ~ 2, 2 ~ 3).hashCode + factory(1 ~ 2, 2 ~ 3).hashCode shouldNot be(factory(1 ~ 2, 3 ~ 3).hashCode) + } } private class EqualityMixed extends RefSpec with Matchers { From aa8357dd1e3de18e01219be29b8de657816dae80 Mon Sep 17 00:00:00 2001 From: Peter Empen Date: Sun, 2 Mar 2025 19:18:44 +0100 Subject: [PATCH 10/34] diSuccessor includes self if loop exists --- .../scala/scalax/collection/GraphBase.scala | 26 ++++++----- .../scalax/collection/GraphTraversal.scala | 2 +- .../scalax/collection/TraverserImpl.scala | 6 +-- .../immutable/AdjacencyListBase.scala | 46 +++++++++++-------- .../immutable/AdjacencyListGraph.scala | 11 ++--- .../scalax/collection/immutable/EqSet.scala | 4 +- .../mutable/AdjacencyListGraph.scala | 12 +++-- .../scalax/collection/EditingHyperSpec.scala | 12 +++-- .../scala/scalax/collection/EditingSpec.scala | 31 ++++++++++--- .../scalax/collection/EqualitySpec.scala | 2 +- .../scalax/collection/EditingHyperSpec.scala | 14 ++++-- .../scala/scalax/collection/EditingSpec.scala | 31 ++++++++++--- .../scalax/collection/EqualitySpec.scala | 17 ++++++- 13 files changed, 144 insertions(+), 70 deletions(-) diff --git a/core/src/main/scala/scalax/collection/GraphBase.scala b/core/src/main/scala/scalax/collection/GraphBase.scala index 10885bb3..314f0b7b 100644 --- a/core/src/main/scala/scalax/collection/GraphBase.scala +++ b/core/src/main/scala/scalax/collection/GraphBase.scala @@ -108,9 +108,9 @@ trait GraphBase[N, E <: Edge[N], +CC[X, Y <: Edge[X]] <: GraphBase[X, Y, CC]] */ def isIndependentOf(that: NodeT): Boolean - /** All direct successors of this node, also called ''successor set'' or - * ''open out-neighborhood'': target nodes of directed incident edges and / or - * adjacent nodes of undirected incident edges excluding this node. + /** All direct successors of this node, also called ''successor set'': + * target nodes of directed incident edges and / or adjacent nodes of undirected incident edges. + * This node itself is also included if a loop exists. * @return set of all direct successors of this node. */ def diSuccessors: Set[NodeT] @@ -118,10 +118,12 @@ trait GraphBase[N, E <: Edge[N], +CC[X, Y <: Edge[X]] <: GraphBase[X, Y, CC]] /** Whether this node has any successors. */ def hasSuccessors: Boolean - protected[collection] def addDiSuccessors(edge: EdgeT, add: NodeT => Unit): Unit + protected[collection] def addOutNeighbors(edge: EdgeT, add: NodeT => Unit): Unit - /** Synonym for `diSuccessors`. */ - @inline final def outNeighbors: Set[NodeT] = diSuccessors + /** Like `diSuccessors` except that this node is excluded even if a loop exists. + * Also called ''open out-neighborhood'' + */ + def outNeighbors: Set[NodeT] /** All direct predecessors of this node, also called ''predecessor set'' or * ''open in-neighborhood'': source nodes of directed incident edges and / or @@ -133,7 +135,7 @@ trait GraphBase[N, E <: Edge[N], +CC[X, Y <: Edge[X]] <: GraphBase[X, Y, CC]] /** Whether this node has any predecessors. */ def hasPredecessors: Boolean - protected[collection] def addDiPredecessors(edge: EdgeT, add: NodeT => Unit): Unit + protected[collection] def addInNeighbors(edge: EdgeT, add: NodeT => Unit): Unit /** Synonym for `diPredecessors`. */ @inline final def inNeighbors = diPredecessors @@ -251,10 +253,10 @@ trait GraphBase[N, E <: Edge[N], +CC[X, Y <: Edge[X]] <: GraphBase[X, Y, CC]] def apply(node: N) = newNode(node) def unapply(n: NodeT) = Some(n) - @inline final protected[collection] def addDiSuccessors(node: NodeT, edge: EdgeT, add: NodeT => Unit): Unit = - node.addDiSuccessors(edge, add) - @inline final protected[collection] def addDiPredecessors(node: NodeT, edge: EdgeT, add: NodeT => Unit): Unit = - node.addDiPredecessors(edge, add) + @inline final protected[collection] def addOutNeighbors(node: NodeT, edge: EdgeT, add: NodeT => Unit): Unit = + node.addOutNeighbors(edge, add) + @inline final protected[collection] def addInNeighbors(node: NodeT, edge: EdgeT, add: NodeT => Unit): Unit = + node.addInNeighbors(edge, add) @inline final protected[collection] def addNeighbors(node: NodeT, edge: EdgeT, add: NodeT => Unit): Unit = node.addNeighbors(edge, add) @@ -350,7 +352,7 @@ trait GraphBase[N, E <: Edge[N], +CC[X, Y <: Edge[X]] <: GraphBase[X, Y, CC]] def adjacencyListsToString: String = (for (n <- this) - yield n.outer.toString + ": " + ((for (a <- n.diSuccessors) yield a.outer) mkString ",")) mkString "\n" + yield n.outer.toString + ": " + ((for (a <- n.outNeighbors) yield a.outer) mkString ",")) mkString "\n" def draw(random: Random): NodeT diff --git a/core/src/main/scala/scalax/collection/GraphTraversal.scala b/core/src/main/scala/scalax/collection/GraphTraversal.scala index dc306ea1..e4cae2f2 100644 --- a/core/src/main/scala/scalax/collection/GraphTraversal.scala +++ b/core/src/main/scala/scalax/collection/GraphTraversal.scala @@ -561,7 +561,7 @@ trait GraphTraversal[N, E <: Edge[N]] extends GraphBase[N, E, GraphTraversal] { */ def isComplete = { val orderLessOne = order - 1 - nodes forall (_.diSuccessors.size == orderLessOne) + nodes forall (_.outNeighbors.size == orderLessOne) } /** An arbitrary edge between `from` and `to` that is available most efficiently. diff --git a/core/src/main/scala/scalax/collection/TraverserImpl.scala b/core/src/main/scala/scalax/collection/TraverserImpl.scala index 637f70b0..95118cbd 100644 --- a/core/src/main/scala/scalax/collection/TraverserImpl.scala +++ b/core/src/main/scala/scalax/collection/TraverserImpl.scala @@ -99,8 +99,8 @@ trait TraverserImpl[N, E <: Edge[N]] { final protected class Runner[U] private (stopAt: StopCondition, visitor: A => U) { private[this] val addMethod = parameters.direction match { - case Successors => Node.addDiSuccessors _ - case Predecessors => Node.addDiPredecessors _ + case Successors => Node.addOutNeighbors _ + case Predecessors => Node.addInNeighbors _ case AnyConnected => Node.addNeighbors _ } @@ -279,7 +279,7 @@ trait TraverserImpl[N, E <: Edge[N]] { if (withEdgeFiltering) filtered(node, nodeFilter, filteredEdges(node.outgoing, cumWeight), reverse) else { - val succ = node.diSuccessors + val succ = node.outNeighbors filtered(succ, succ.size, nodeFilter, reverse) } diff --git a/core/src/main/scala/scalax/collection/immutable/AdjacencyListBase.scala b/core/src/main/scala/scalax/collection/immutable/AdjacencyListBase.scala index 2c1226d1..a82d997c 100644 --- a/core/src/main/scala/scalax/collection/immutable/AdjacencyListBase.scala +++ b/core/src/main/scala/scalax/collection/immutable/AdjacencyListBase.scala @@ -2,10 +2,12 @@ package scalax.collection package immutable import java.io.{ObjectInputStream, ObjectOutputStream} + import scala.annotation.unchecked.{uncheckedVariance => uV} import scala.collection.{AbstractIterable, AbstractIterator, EqSetFacade} import scala.collection.mutable.{ArrayBuffer, ExtHashSet} import scala.util.Random + import scalax.collection.generic.Edge import scalax.collection.AnyGraph import scalax.collection.mutable.{ArraySet, EqHashMap, EqHashSet} @@ -30,49 +32,53 @@ trait AdjacencyListBase[N, E <: Edge[N], +CC[X, Y <: Edge[X]] <: GraphLike[X, Y, def edges: ArraySet[EdgeT] @inline final protected def nodeEqThis = (n: NodeT) => n eq this - protected[collection] object Adj extends Serializable { // lazy adjacents + + protected[collection] object Lazy extends Serializable { @transient protected[collection] var _aHook: Option[(NodeT, EdgeT)] = _ - final def aHook: Option[(NodeT, EdgeT)] = { - if (_aHook eq null) diSucc + @transient private var outN: EqHashMap[NodeT, EdgeT] = _ + + def aHook: Option[(NodeT, EdgeT)] = { + if (_aHook eq null) outNeighborsToSomeEdge _aHook } - @transient private var _diSucc: EqHashMap[NodeT, EdgeT] = _ - final def diSucc: EqHashMap[NodeT, EdgeT] = + def outNeighborsToSomeEdge: EqHashMap[NodeT, EdgeT] = if (edges eq null) new EqHashMap[NodeT, EdgeT] else { - if (_diSucc eq null) { + if (outN eq null) { val m = new EqHashMap[NodeT, EdgeT](edges.size) _aHook = None edges foreach { e => - if (e.matches(nodeEqThis, nodeEqThis) && aHook.isEmpty) + if (aHook.isEmpty && e.matches(nodeEqThis, nodeEqThis)) _aHook = Some(thisNode -> e) - addDiSuccessors(e, (n: NodeT) => m put (n, e)) + addOutNeighbors(e, (n: NodeT) => m put (n, e)) } - _diSucc = m + outN = m } - _diSucc + outN } + + def diSuccessorsToSomeEdge: EqHashMap[NodeT, EdgeT] = + aHook.fold(outNeighborsToSomeEdge)(outNeighborsToSomeEdge.clone += _) } - import Adj._ + import Lazy._ final def connectionsWith(other: NodeT) = edges withSetFilter (_.isAt(other)) - final def hasOnlyHooks = diSucc.isEmpty && aHook.isDefined + final def hasOnlyHooks = outNeighbors.isEmpty && aHook.isDefined final def hook: Option[EdgeT] = aHook map (_._2) - final def isDirectPredecessorOf(that: NodeT): Boolean = - diSucc contains that + final def isDirectPredecessorOf(that: NodeT): Boolean = diSuccessors contains that final def isIndependentOf(that: NodeT): Boolean = if (this eq that) edges forall (_.nonLooping) else edges forall (!_.isAt((_: NodeT) eq that)) - final def hasSuccessors: Boolean = diSuccessors exists (_ ne this) + final def hasSuccessors: Boolean = diSuccessors.nonEmpty - final protected[collection] def addDiSuccessors(edge: EdgeT, add: (NodeT) => Unit): Unit = { + final protected[collection] def addOutNeighbors(edge: EdgeT, add: (NodeT) => Unit): Unit = { val filter = if (edge.isHyperEdge && edge.isDirected) edge.hasSource((_: NodeT) eq this) else true @@ -82,14 +88,14 @@ trait AdjacencyListBase[N, E <: Edge[N], +CC[X, Y <: Edge[X]] <: GraphLike[X, Y, final def diPredecessors: Set[NodeT] = { val m = new EqHashMap[NodeT, EdgeT](edges.size) edges foreach { e => - addDiPredecessors(e, (n: NodeT) => m put (n, e)) + addInNeighbors(e, (n: NodeT) => m put (n, e)) } new EqSet(m) } final def hasPredecessors: Boolean = edges exists (_.hasSource((n: NodeT) => n ne this)) - final protected[collection] def addDiPredecessors(edge: EdgeT, add: NodeT => Unit): Unit = + final protected[collection] def addInNeighbors(edge: EdgeT, add: NodeT => Unit): Unit = edge.sources foreach (n => if (n ne this) add(n)) final def neighbors: Set[NodeT] = { @@ -105,6 +111,7 @@ trait AdjacencyListBase[N, E <: Edge[N], +CC[X, Y <: Edge[X]] <: GraphLike[X, Y, if (e.isDirected) e.hasSource((_: NodeT) eq this) else true ) + @inline private[this] def isOutgoingTo(e: EdgeT, to: NodeT): Boolean = if (e.isDirected) e matches ((_: NodeT) eq this, (_: NodeT) eq to) @@ -115,12 +122,13 @@ trait AdjacencyListBase[N, E <: Edge[N], +CC[X, Y <: Edge[X]] <: GraphLike[X, Y, final def findOutgoingTo(to: NodeT): Option[EdgeT] = if (to eq this) aHook map (_._2) - else diSucc get to + else outNeighborsToSomeEdge get to final def incoming = edges withSetFilter (e => if (e.isDirected) e.hasTarget((_: NodeT) eq this) else true ) + @inline final private[this] def isIncomingFrom(e: EdgeT, from: NodeT): Boolean = if (e.isDirected) e matches ((_: NodeT) eq from, (_: NodeT) eq this) diff --git a/core/src/main/scala/scalax/collection/immutable/AdjacencyListGraph.scala b/core/src/main/scala/scalax/collection/immutable/AdjacencyListGraph.scala index ef386b03..a172b1cf 100644 --- a/core/src/main/scala/scalax/collection/immutable/AdjacencyListGraph.scala +++ b/core/src/main/scala/scalax/collection/immutable/AdjacencyListGraph.scala @@ -19,13 +19,12 @@ trait AdjacencyListGraph[N, E <: Edge[N], +CC[X, Y <: Edge[X]] <: AdjacencyListG abstract class InnerNodeImpl(val outer: N, hints: ArraySet.Hints) extends NodeBase with AdjacendyListBaseInnerNode { this: NodeT => - final override val edges: ArraySet[EdgeT] = ArraySet.emptyWithHints[EdgeT](hints) - @transient protected var _diSuccessors: immutable.EqSet[NodeT] = _ + final override val edges: ArraySet[EdgeT] = ArraySet.emptyWithHints[EdgeT](hints) + + final def outNeighbors: Set[NodeT] = new immutable.EqSet(Lazy.outNeighborsToSomeEdge) + + final def diSuccessors: Set[NodeT] = new immutable.EqSet(Lazy.diSuccessorsToSomeEdge) - final def diSuccessors: Set[NodeT] = { - if (_diSuccessors eq null) _diSuccessors = new immutable.EqSet(Adj.diSucc) - _diSuccessors - } } type NodeSetT = AdjacencyListNodeSet diff --git a/core/src/main/scala/scalax/collection/immutable/EqSet.scala b/core/src/main/scala/scalax/collection/immutable/EqSet.scala index 7f8772f2..5220d22d 100644 --- a/core/src/main/scala/scalax/collection/immutable/EqSet.scala +++ b/core/src/main/scala/scalax/collection/immutable/EqSet.scala @@ -5,9 +5,9 @@ import scala.collection.immutable.Set import scalax.collection.mutable.EqHashMap /** Wrapper class mimicking a `scala.collection.immutable.Set` - * without copying the contents of the underlying `EqHashMap`. + * without copying the contents of the underlying `EqHashMap`. * - * @define ON Creates a new `Set` as an O(N) operation + * @define ON Creates a new `Set` as an O(N) operation */ final class EqSet[K <: AnyRef](map: EqHashMap[K, _]) extends Set[K] { diff --git a/core/src/main/scala/scalax/collection/mutable/AdjacencyListGraph.scala b/core/src/main/scala/scalax/collection/mutable/AdjacencyListGraph.scala index a37f6284..d58881bd 100644 --- a/core/src/main/scala/scalax/collection/mutable/AdjacencyListGraph.scala +++ b/core/src/main/scala/scalax/collection/mutable/AdjacencyListGraph.scala @@ -21,7 +21,7 @@ trait AdjacencyListGraph[N, E <: Edge[N], +CC[X, Y <: Edge[X]] <: AdjacencyListG with AdjacendyListBaseInnerNode { this: NodeT => final override val edges: ArraySet[EdgeT] = ArraySet.emptyWithHints[EdgeT](hints) - import Adj._ + import Lazy._ final override protected[collection] def add(edge: EdgeT): Boolean = if (super.add(edge)) { @@ -38,10 +38,12 @@ trait AdjacencyListGraph[N, E <: Edge[N], +CC[X, Y <: Edge[X]] <: AdjacencyListG final protected def addDiSuccOrHook(edge: EdgeT): Unit = { if (edge.matches(nodeEqThis, nodeEqThis) && aHook.isEmpty) _aHook = Some(this -> edge) - addDiSuccessors(edge, (n: NodeT) => diSucc put (n, edge)) + addOutNeighbors(edge, (n: NodeT) => outNeighborsToSomeEdge put (n, edge)) } - final def diSuccessors: Set[NodeT] = new immutable.EqSet(diSucc) + final def diSuccessors: Set[NodeT] = new immutable.EqSet(diSuccessorsToSomeEdge) + + final def outNeighbors: Set[NodeT] = new immutable.EqSet(outNeighborsToSomeEdge) protected[collection] def remove(edge: EdgeT): Boolean = if (edges.remove(edge)) { @@ -53,7 +55,9 @@ trait AdjacencyListGraph[N, E <: Edge[N], +CC[X, Y <: Edge[X]] <: AdjacencyListG def onNonLooping(): Unit = edge.targets foreach (t => edges .find((e: EdgeT) => e.hasTarget((n: NodeT) => n eq t)) - .fold[Unit](ifEmpty = diSucc remove t)((e: EdgeT) => if (e hasSource this) diSucc put (t, e)) + .fold[Unit](ifEmpty = outNeighborsToSomeEdge remove t)((e: EdgeT) => + if (e hasSource this) outNeighborsToSomeEdge put (t, e) + ) ) if (edge.isHyperEdge) diff --git a/core/src/test/scala/scalax/collection/EditingHyperSpec.scala b/core/src/test/scala/scalax/collection/EditingHyperSpec.scala index 48d6ae52..01b911d3 100644 --- a/core/src/test/scala/scalax/collection/EditingHyperSpec.scala +++ b/core/src/test/scala/scalax/collection/EditingHyperSpec.scala @@ -96,7 +96,8 @@ private class EditingHyper[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[ object `diSuccessors ` { def `for DiHyper`: Unit = { - (hDi get 1).diSuccessors shouldEqual Set(2, 3, 4, 5, 9) + (hDi get 1).outNeighbors shouldEqual Set(2, 3, 4, 5, 9) + (hDi get 1).diSuccessors shouldEqual Set(1, 2, 3, 4, 5, 9) (hDi get 2).diSuccessors shouldEqual Set.empty (hDi get 5).diSuccessors shouldEqual Set.empty } @@ -149,12 +150,14 @@ private class EditingHyperMutable extends RefSpec with Matchers { val (n1, n2) = (g get 1, g get 2) n2.diSuccessors shouldBe empty - n1.diSuccessors should be(Set(2, 3)) + n1.outNeighbors should be(Set(2, 3)) + n1.diSuccessors should be(Set(1, 2, 3)) n1 findOutgoingTo n1 should be(Some(_1_to_1_2)) g subtractOne _1_to_2_3 g shouldEqual Graph(_1_to_1_2, 3) - n1.diSuccessors should be(Set(2)) + n1.outNeighbors should be(Set(2)) + n1.diSuccessors should be(Set(1, 2)) n1 findOutgoingTo n1 should be(Some(_1_to_1_2)) g subtractOne 2 @@ -164,7 +167,8 @@ private class EditingHyperMutable extends RefSpec with Matchers { g += _1_to_1_2 g shouldEqual Graph(_1_to_1_2, 3) - n1.diSuccessors should be(Set(2)) + n1.outNeighbors should be(Set(2)) + n1.diSuccessors should be(Set(1, 2)) n1 findOutgoingTo n1 should be(Some(_1_to_1_2)) } } diff --git a/core/src/test/scala/scalax/collection/EditingSpec.scala b/core/src/test/scala/scalax/collection/EditingSpec.scala index fb871e3a..2c1d589f 100644 --- a/core/src/test/scala/scalax/collection/EditingSpec.scala +++ b/core/src/test/scala/scalax/collection/EditingSpec.scala @@ -137,7 +137,7 @@ private class EditingMutable extends RefSpec with Matchers { g.clear(); directed(false) } - def `serve 'diSuccessors' when directed`: Unit = { + def `serve 'diSuccessors', 'outNeighbors' when directed`: Unit = { val (one, two, oneOne, oneTwo) = (1, 2, 1 ~> 1, 1 ~> 2) val g = Graph(oneOne, oneTwo, one ~> 3, one ~> 4) val (n1, n2) = (g get one, g get two) @@ -145,19 +145,23 @@ private class EditingMutable extends RefSpec with Matchers { g subtractOne 1 ~> 4 // Graph(oneOne, oneTwo, one~>3) n2.diSuccessors shouldBe empty - n1.diSuccessors.map(_.outer) shouldBe Set(two, 3) + n1.diSuccessors.map(_.outer) shouldBe Set(one, two, 3) + n1.outNeighbors.map(_.outer) shouldBe Set(two, 3) n1 findOutgoingTo n1 should be(Some(e11)) g subtractOne oneTwo // Graph(oneOne, one~>3) - n1.diSuccessors should be(Set(3)) + n1.diSuccessors should be(Set(one, 3)) + n1.outNeighbors should be(Set(3)) n1 findOutgoingTo n1 should be(Some(e11)) g subtractOne oneOne // Graph(one~>3) n1.diSuccessors should be(Set(3)) + n1.outNeighbors should be(Set(3)) n1 findOutgoingTo n1 should be(None) g ++= (edges = List(oneOne, oneTwo)) // Graph(oneOne, oneTwo, one~>3) - n1.diSuccessors should be(Set(two, 3)) + n1.diSuccessors should be(Set(one, two, 3)) + n1.outNeighbors should be(Set(two, 3)) n1 findOutgoingTo n1 should be(Some(e11)) } @@ -319,13 +323,28 @@ private class Editing[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E, private val gDi = factory(1 ~> 1, 1 ~> 2, 1 ~> 3, 1 ~> 4) private val gMixed = factory[Int, AnyEdge](1 ~> 2, 2 ~> 3, 4 ~ 3) + object `outNeighbors ` { + def `for UnDi`: Unit = { + (gUnDi get 1).outNeighbors should be(Set(2, 3, 4)) + (gUnDi get 2).outNeighbors should be(Set(1)) + } + def `for Di`: Unit = { + (gDi get 1).outNeighbors should be(Set(2, 3, 4)) + (gDi get 2).outNeighbors should be(Set.empty) + } + def `for mixed`: Unit = { + (gMixed get 2).outNeighbors should be(Set(3)) + (gMixed get 3).outNeighbors should be(Set(4)) + } + } + object `diSuccessors ` { def `for UnDi`: Unit = { - (gUnDi get 1).diSuccessors should be(Set(2, 3, 4)) + (gUnDi get 1).diSuccessors should be(Set(1, 2, 3, 4)) (gUnDi get 2).diSuccessors should be(Set(1)) } def `for Di`: Unit = { - (gDi get 1).diSuccessors should be(Set(2, 3, 4)) + (gDi get 1).diSuccessors should be(Set(1, 2, 3, 4)) (gDi get 2).diSuccessors should be(Set.empty) } def `for mixed`: Unit = { diff --git a/core/src/test/scala/scalax/collection/EqualitySpec.scala b/core/src/test/scala/scalax/collection/EqualitySpec.scala index 0986b3e0..6f1f97c5 100644 --- a/core/src/test/scala/scalax/collection/EqualitySpec.scala +++ b/core/src/test/scala/scalax/collection/EqualitySpec.scala @@ -26,7 +26,7 @@ private class Equality[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E private val gInt_1_3 = factory(seq_1_3.toOuterElems[DiEdge[Int]]: _*) private val gString_A = factory("A") - def `Graph equals ` : Unit = { + def `Graph equals`: Unit = { factory[Int, Nothing]() shouldEqual factory[Int, DiEdge]() gInt_1_3 shouldEqual factory(1, 3) gString_A shouldEqual factory("A") diff --git a/coreTestScala3/src/test/scala/scalax/collection/EditingHyperSpec.scala b/coreTestScala3/src/test/scala/scalax/collection/EditingHyperSpec.scala index ac12f855..d18eb17a 100644 --- a/coreTestScala3/src/test/scala/scalax/collection/EditingHyperSpec.scala +++ b/coreTestScala3/src/test/scala/scalax/collection/EditingHyperSpec.scala @@ -95,7 +95,8 @@ private class EditingHyper[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[ object `diSuccessors ` { def `for DiHyper`: Unit = { - (hDi get 1).diSuccessors shouldEqual Set(2, 3, 4, 5, 9) + (hDi get 1).outNeighbors shouldEqual Set(2, 3, 4, 5, 9) + (hDi get 1).diSuccessors shouldEqual Set(1, 2, 3, 4, 5, 9) (hDi get 2).diSuccessors shouldEqual Set.empty (hDi get 5).diSuccessors shouldEqual Set.empty } @@ -123,7 +124,7 @@ private class EditingHyper[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[ } def `match directed hyperedge`: Unit = { - val count = 4 + val count = 3 val sources = OneOrMore.fromUnsafe(List.tabulate(count - 1)(_ + 1)) val target = one(count) val diHyper = sources ~~> target @@ -148,12 +149,14 @@ private class EditingHyperMutable extends RefSpec with Matchers { val (n1, n2) = (g get 1, g get 2) n2.diSuccessors shouldBe empty - n1.diSuccessors should be(Set(2, 3)) + n1.outNeighbors should be(Set(2, 3)) + n1.diSuccessors should be(Set(1, 2, 3)) n1 findOutgoingTo n1 should be(Some(_1_to_1_2)) g subtractOne _1_to_2_3 g shouldEqual Graph(_1_to_1_2, 3) - n1.diSuccessors should be(Set(2)) + n1.outNeighbors should be(Set(2)) + n1.diSuccessors should be(Set(1, 2)) n1 findOutgoingTo n1 should be(Some(_1_to_1_2)) g subtractOne 2 @@ -163,7 +166,8 @@ private class EditingHyperMutable extends RefSpec with Matchers { g += _1_to_1_2 g shouldEqual Graph(_1_to_1_2, 3) - n1.diSuccessors should be(Set(2)) + n1.outNeighbors should be(Set(2)) + n1.diSuccessors should be(Set(1, 2)) n1 findOutgoingTo n1 should be(Some(_1_to_1_2)) } } diff --git a/coreTestScala3/src/test/scala/scalax/collection/EditingSpec.scala b/coreTestScala3/src/test/scala/scalax/collection/EditingSpec.scala index fb871e3a..2c1d589f 100644 --- a/coreTestScala3/src/test/scala/scalax/collection/EditingSpec.scala +++ b/coreTestScala3/src/test/scala/scalax/collection/EditingSpec.scala @@ -137,7 +137,7 @@ private class EditingMutable extends RefSpec with Matchers { g.clear(); directed(false) } - def `serve 'diSuccessors' when directed`: Unit = { + def `serve 'diSuccessors', 'outNeighbors' when directed`: Unit = { val (one, two, oneOne, oneTwo) = (1, 2, 1 ~> 1, 1 ~> 2) val g = Graph(oneOne, oneTwo, one ~> 3, one ~> 4) val (n1, n2) = (g get one, g get two) @@ -145,19 +145,23 @@ private class EditingMutable extends RefSpec with Matchers { g subtractOne 1 ~> 4 // Graph(oneOne, oneTwo, one~>3) n2.diSuccessors shouldBe empty - n1.diSuccessors.map(_.outer) shouldBe Set(two, 3) + n1.diSuccessors.map(_.outer) shouldBe Set(one, two, 3) + n1.outNeighbors.map(_.outer) shouldBe Set(two, 3) n1 findOutgoingTo n1 should be(Some(e11)) g subtractOne oneTwo // Graph(oneOne, one~>3) - n1.diSuccessors should be(Set(3)) + n1.diSuccessors should be(Set(one, 3)) + n1.outNeighbors should be(Set(3)) n1 findOutgoingTo n1 should be(Some(e11)) g subtractOne oneOne // Graph(one~>3) n1.diSuccessors should be(Set(3)) + n1.outNeighbors should be(Set(3)) n1 findOutgoingTo n1 should be(None) g ++= (edges = List(oneOne, oneTwo)) // Graph(oneOne, oneTwo, one~>3) - n1.diSuccessors should be(Set(two, 3)) + n1.diSuccessors should be(Set(one, two, 3)) + n1.outNeighbors should be(Set(two, 3)) n1 findOutgoingTo n1 should be(Some(e11)) } @@ -319,13 +323,28 @@ private class Editing[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E, private val gDi = factory(1 ~> 1, 1 ~> 2, 1 ~> 3, 1 ~> 4) private val gMixed = factory[Int, AnyEdge](1 ~> 2, 2 ~> 3, 4 ~ 3) + object `outNeighbors ` { + def `for UnDi`: Unit = { + (gUnDi get 1).outNeighbors should be(Set(2, 3, 4)) + (gUnDi get 2).outNeighbors should be(Set(1)) + } + def `for Di`: Unit = { + (gDi get 1).outNeighbors should be(Set(2, 3, 4)) + (gDi get 2).outNeighbors should be(Set.empty) + } + def `for mixed`: Unit = { + (gMixed get 2).outNeighbors should be(Set(3)) + (gMixed get 3).outNeighbors should be(Set(4)) + } + } + object `diSuccessors ` { def `for UnDi`: Unit = { - (gUnDi get 1).diSuccessors should be(Set(2, 3, 4)) + (gUnDi get 1).diSuccessors should be(Set(1, 2, 3, 4)) (gUnDi get 2).diSuccessors should be(Set(1)) } def `for Di`: Unit = { - (gDi get 1).diSuccessors should be(Set(2, 3, 4)) + (gDi get 1).diSuccessors should be(Set(1, 2, 3, 4)) (gDi get 2).diSuccessors should be(Set.empty) } def `for mixed`: Unit = { diff --git a/coreTestScala3/src/test/scala/scalax/collection/EqualitySpec.scala b/coreTestScala3/src/test/scala/scalax/collection/EqualitySpec.scala index 78021002..f0db8edb 100644 --- a/coreTestScala3/src/test/scala/scalax/collection/EqualitySpec.scala +++ b/coreTestScala3/src/test/scala/scalax/collection/EqualitySpec.scala @@ -18,12 +18,13 @@ private class Equality[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E val factory: GenericGraphCoreFactory[CC] ) extends RefSpec with Matchers { + info(factory.getClass.getPackage.toString) private val seq_1_3 = Seq(1, 3) private val gInt_1_3 = factory(seq_1_3.toOuterElems[DiEdge[Int]]: _*) private val gString_A = factory("A") - def `Eq ` : Unit = { + def `Graph equals`: Unit = { factory[Int, Nothing]() shouldEqual factory[Int, DiEdge]() gInt_1_3 shouldEqual factory(1, 3) gString_A shouldEqual factory("A") @@ -34,6 +35,20 @@ private class Equality[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E gInt_1_3 shouldEqual immutable.Graph(1) + 3 } + + def `Graph hashCode`(): Unit = { + factory(1).hashCode shouldBe factory(1).hashCode + factory(1).hashCode shouldNot be(factory(2).hashCode) + + factory(1 ~> 2).hashCode shouldBe factory(1 ~> 2).hashCode + factory(1 ~> 2).hashCode shouldNot be(factory(2 ~> 1).hashCode) + + factory(1 ~ 2).hashCode shouldBe factory(1 ~ 2).hashCode + factory(1 ~ 2).hashCode shouldNot be(factory(1 ~ 3).hashCode) + + factory(1 ~ 2, 2 ~ 3).hashCode shouldBe factory(1 ~ 2, 2 ~ 3).hashCode + factory(1 ~ 2, 2 ~ 3).hashCode shouldNot be(factory(1 ~ 2, 3 ~ 3).hashCode) + } } private class EqualityMixed extends RefSpec with Matchers { From 3fc76d717236cb88ccddf2d6e047b881a2f502bf Mon Sep 17 00:00:00 2001 From: Peter Empen Date: Mon, 3 Mar 2025 13:19:57 +0100 Subject: [PATCH 11/34] optimize 'Lazy' --- .../immutable/AdjacencyListBase.scala | 23 +++++++++++-------- .../immutable/AdjacencyListGraph.scala | 5 ---- .../mutable/AdjacencyListGraph.scala | 8 ++----- 3 files changed, 16 insertions(+), 20 deletions(-) diff --git a/core/src/main/scala/scalax/collection/immutable/AdjacencyListBase.scala b/core/src/main/scala/scalax/collection/immutable/AdjacencyListBase.scala index a82d997c..da909e90 100644 --- a/core/src/main/scala/scalax/collection/immutable/AdjacencyListBase.scala +++ b/core/src/main/scala/scalax/collection/immutable/AdjacencyListBase.scala @@ -34,10 +34,10 @@ trait AdjacencyListBase[N, E <: Edge[N], +CC[X, Y <: Edge[X]] <: GraphLike[X, Y, @inline final protected def nodeEqThis = (n: NodeT) => n eq this protected[collection] object Lazy extends Serializable { - @transient protected[collection] var _aHook: Option[(NodeT, EdgeT)] = _ - @transient private var outN: EqHashMap[NodeT, EdgeT] = _ + @transient protected[collection] var _aHook: Option[EdgeT] = _ + @transient private var outN: EqHashMap[NodeT, EdgeT] = _ - def aHook: Option[(NodeT, EdgeT)] = { + def aHook: Option[EdgeT] = { if (_aHook eq null) outNeighborsToSomeEdge _aHook } @@ -51,7 +51,7 @@ trait AdjacencyListBase[N, E <: Edge[N], +CC[X, Y <: Edge[X]] <: GraphLike[X, Y, _aHook = None edges foreach { e => if (aHook.isEmpty && e.matches(nodeEqThis, nodeEqThis)) - _aHook = Some(thisNode -> e) + _aHook = Some(e) addOutNeighbors(e, (n: NodeT) => m put (n, e)) } outN = m @@ -59,16 +59,21 @@ trait AdjacencyListBase[N, E <: Edge[N], +CC[X, Y <: Edge[X]] <: GraphLike[X, Y, outN } - def diSuccessorsToSomeEdge: EqHashMap[NodeT, EdgeT] = - aHook.fold(outNeighborsToSomeEdge)(outNeighborsToSomeEdge.clone += _) + def diSuccessors: Set[NodeT] = new EqSetFacade( + aHook.fold(outNeighborsToSomeEdge.keys)(_ => Iterable.single(thisNode) ++ outNeighborsToSomeEdge.keys) + ) } - import Lazy._ + import Lazy.{aHook, outNeighborsToSomeEdge} final def connectionsWith(other: NodeT) = edges withSetFilter (_.isAt(other)) final def hasOnlyHooks = outNeighbors.isEmpty && aHook.isDefined - final def hook: Option[EdgeT] = aHook map (_._2) + final def hook: Option[EdgeT] = aHook + + final def outNeighbors: Set[NodeT] = new immutable.EqSet(outNeighborsToSomeEdge) + + final def diSuccessors: Set[NodeT] = Lazy.diSuccessors final def isDirectPredecessorOf(that: NodeT): Boolean = diSuccessors contains that @@ -121,7 +126,7 @@ trait AdjacencyListBase[N, E <: Edge[N], +CC[X, Y <: Edge[X]] <: GraphLike[X, Y, final def outgoingTo(to: NodeT) = edges withSetFilter (isOutgoingTo(_, to)) final def findOutgoingTo(to: NodeT): Option[EdgeT] = - if (to eq this) aHook map (_._2) + if (to eq this) aHook else outNeighborsToSomeEdge get to final def incoming = edges withSetFilter (e => diff --git a/core/src/main/scala/scalax/collection/immutable/AdjacencyListGraph.scala b/core/src/main/scala/scalax/collection/immutable/AdjacencyListGraph.scala index a172b1cf..5b695aad 100644 --- a/core/src/main/scala/scalax/collection/immutable/AdjacencyListGraph.scala +++ b/core/src/main/scala/scalax/collection/immutable/AdjacencyListGraph.scala @@ -20,11 +20,6 @@ trait AdjacencyListGraph[N, E <: Edge[N], +CC[X, Y <: Edge[X]] <: AdjacencyListG this: NodeT => final override val edges: ArraySet[EdgeT] = ArraySet.emptyWithHints[EdgeT](hints) - - final def outNeighbors: Set[NodeT] = new immutable.EqSet(Lazy.outNeighborsToSomeEdge) - - final def diSuccessors: Set[NodeT] = new immutable.EqSet(Lazy.diSuccessorsToSomeEdge) - } type NodeSetT = AdjacencyListNodeSet diff --git a/core/src/main/scala/scalax/collection/mutable/AdjacencyListGraph.scala b/core/src/main/scala/scalax/collection/mutable/AdjacencyListGraph.scala index d58881bd..5d9b70d3 100644 --- a/core/src/main/scala/scalax/collection/mutable/AdjacencyListGraph.scala +++ b/core/src/main/scala/scalax/collection/mutable/AdjacencyListGraph.scala @@ -37,20 +37,16 @@ trait AdjacencyListGraph[N, E <: Edge[N], +CC[X, Y <: Edge[X]] <: AdjacencyListG final protected def addDiSuccOrHook(edge: EdgeT): Unit = { if (edge.matches(nodeEqThis, nodeEqThis) && aHook.isEmpty) - _aHook = Some(this -> edge) + _aHook = Some(edge) addOutNeighbors(edge, (n: NodeT) => outNeighborsToSomeEdge put (n, edge)) } - final def diSuccessors: Set[NodeT] = new immutable.EqSet(diSuccessorsToSomeEdge) - - final def outNeighbors: Set[NodeT] = new immutable.EqSet(outNeighborsToSomeEdge) - protected[collection] def remove(edge: EdgeT): Boolean = if (edges.remove(edge)) { if (selfGraph.edges.initialized) { def onLooping(): Unit = - edges.find((e: EdgeT) => e.isLooping).fold(ifEmpty = _aHook = None)((e: EdgeT) => _aHook = Some(this -> e)) + edges.find((e: EdgeT) => e.isLooping).fold(ifEmpty = _aHook = None)((e: EdgeT) => _aHook = Some(e)) def onNonLooping(): Unit = edge.targets foreach (t => edges From 5b0d927cc301060a4c088498b997fd292efbfb7f Mon Sep 17 00:00:00 2001 From: Peter Empen Date: Sat, 8 Mar 2025 18:19:29 +0100 Subject: [PATCH 12/34] diPredecessors include self if loop exists --- .../main/scala/scala/collection/Facades.scala | 1 + .../scala/scalax/collection/GraphBase.scala | 15 +++++++----- .../scalax/collection/TraverserImpl.scala | 2 +- .../immutable/AdjacencyListBase.scala | 22 ++++++++++++++---- .../scalax/collection/EditingHyperSpec.scala | 7 +++--- .../scala/scalax/collection/EditingSpec.scala | 23 ++++++++++++------- .../collection/TopologicalSortSpec.scala | 2 +- .../scalax/collection/EditingHyperSpec.scala | 7 +++--- .../scala/scalax/collection/EditingSpec.scala | 23 ++++++++++++------- .../collection/TopologicalSortSpec.scala | 2 +- 10 files changed, 68 insertions(+), 36 deletions(-) diff --git a/core/src/main/scala/scala/collection/Facades.scala b/core/src/main/scala/scala/collection/Facades.scala index 4828dc73..e1e4f4e9 100644 --- a/core/src/main/scala/scala/collection/Facades.scala +++ b/core/src/main/scala/scala/collection/Facades.scala @@ -32,6 +32,7 @@ final class EqSetFacade[A <: AnyRef](i: Iterable[A]) extends immutable.Set[A] { def incl(elem: A) = i.toSet - elem def excl(elem: A) = i.toSet + elem + override def knownSize: Int = i.knownSize override def size: Int = i.size override def contains(elem: A) = i exists (_ eq elem) } diff --git a/core/src/main/scala/scalax/collection/GraphBase.scala b/core/src/main/scala/scalax/collection/GraphBase.scala index 314f0b7b..33e7a0d2 100644 --- a/core/src/main/scala/scalax/collection/GraphBase.scala +++ b/core/src/main/scala/scalax/collection/GraphBase.scala @@ -121,13 +121,13 @@ trait GraphBase[N, E <: Edge[N], +CC[X, Y <: Edge[X]] <: GraphBase[X, Y, CC]] protected[collection] def addOutNeighbors(edge: EdgeT, add: NodeT => Unit): Unit /** Like `diSuccessors` except that this node is excluded even if a loop exists. - * Also called ''open out-neighborhood'' + * Also called ''open out-neighborhood''. */ def outNeighbors: Set[NodeT] - /** All direct predecessors of this node, also called ''predecessor set'' or - * ''open in-neighborhood'': source nodes of directed incident edges and / or - * adjacent nodes of undirected incident edges excluding this node. + /** All direct predecessors of this node, also called ''predecessor set'': + * source nodes of directed incident edges and / or adjacent nodes of undirected incident edges. + * This node itself is also included if a loop exists. * @return set of all direct predecessors of this node. */ def diPredecessors: Set[NodeT] @@ -137,14 +137,17 @@ trait GraphBase[N, E <: Edge[N], +CC[X, Y <: Edge[X]] <: GraphBase[X, Y, CC]] protected[collection] def addInNeighbors(edge: EdgeT, add: NodeT => Unit): Unit - /** Synonym for `diPredecessors`. */ - @inline final def inNeighbors = diPredecessors + /** Like `diPredecessors` except that this node is excluded even if a loop exists. + * Also called ''open in-neighborhood''. + */ + def inNeighbors: Set[NodeT] /** All adjacent nodes (direct successors and predecessors) of this node, * also called ''open neighborhood'' excluding this node. * @return set of all neighbors. */ def neighbors: Set[NodeT] + protected[collection] def addNeighbors(edge: EdgeT, add: NodeT => Unit): Unit /** All edges outgoing from this node. diff --git a/core/src/main/scala/scalax/collection/TraverserImpl.scala b/core/src/main/scala/scalax/collection/TraverserImpl.scala index 95118cbd..114c1ffe 100644 --- a/core/src/main/scala/scalax/collection/TraverserImpl.scala +++ b/core/src/main/scala/scalax/collection/TraverserImpl.scala @@ -292,7 +292,7 @@ trait TraverserImpl[N, E <: Edge[N]] { if (withEdgeFiltering) filtered(node, nodeFilter, filteredEdges(node.incoming, cumWeight), reverse) else - filtered(node.diPredecessors, -estimatedNrOfNodes(node), nodeFilter, reverse) + filtered(node.inNeighbors, -estimatedNrOfNodes(node), nodeFilter, reverse) private[this] def filteredNeighbors( node: NodeT, diff --git a/core/src/main/scala/scalax/collection/immutable/AdjacencyListBase.scala b/core/src/main/scala/scalax/collection/immutable/AdjacencyListBase.scala index da909e90..b49f8525 100644 --- a/core/src/main/scala/scalax/collection/immutable/AdjacencyListBase.scala +++ b/core/src/main/scala/scalax/collection/immutable/AdjacencyListBase.scala @@ -91,17 +91,29 @@ trait AdjacencyListBase[N, E <: Edge[N], +CC[X, Y <: Edge[X]] <: GraphLike[X, Y, } final def diPredecessors: Set[NodeT] = { - val m = new EqHashMap[NodeT, EdgeT](edges.size) + val m = new EqHashSet[NodeT](edges.size) + var selfAdded = false edges foreach { e => - addInNeighbors(e, (n: NodeT) => m put (n, e)) + addInNeighbors(e, m += _) + if (!selfAdded) + if (e.matches(nodeEqThis, nodeEqThis)) { + m += thisNode + selfAdded = true + } } - new EqSet(m) + new EqSetFacade(m) } final def hasPredecessors: Boolean = edges exists (_.hasSource((n: NodeT) => n ne this)) - final protected[collection] def addInNeighbors(edge: EdgeT, add: NodeT => Unit): Unit = - edge.sources foreach (n => if (n ne this) add(n)) + @inline final protected[collection] def addInNeighbors(edge: EdgeT, add: NodeT => Unit): Unit = + edge withSources (n => if (n ne this) add(n)) + + def inNeighbors: Set[NodeT] = { + val m = new EqHashSet[NodeT](edges.size) + edges foreach (addInNeighbors(_, m += _)) + new EqSetFacade(m) + } final def neighbors: Set[NodeT] = { val m = new EqHashSet[NodeT](edges.size) diff --git a/core/src/test/scala/scalax/collection/EditingHyperSpec.scala b/core/src/test/scala/scalax/collection/EditingHyperSpec.scala index 01b911d3..b0009409 100644 --- a/core/src/test/scala/scalax/collection/EditingHyperSpec.scala +++ b/core/src/test/scala/scalax/collection/EditingHyperSpec.scala @@ -94,7 +94,7 @@ private class EditingHyper[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[ single ~~> more(4, 9) ) - object `diSuccessors ` { + object `diSuccessors, outNeighbors` { def `for DiHyper`: Unit = { (hDi get 1).outNeighbors shouldEqual Set(2, 3, 4, 5, 9) (hDi get 1).diSuccessors shouldEqual Set(1, 2, 3, 4, 5, 9) @@ -103,9 +103,10 @@ private class EditingHyper[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[ } } - object `diPredecessors ` { + object `diPredecessors, inNeighbors` { def `for DiHyper`: Unit = { - (hDi get 1).diPredecessors should be(Set.empty) + (hDi get 1).inNeighbors should be(Set.empty) + (hDi get 1).diPredecessors should be(Set(1)) (hDi get 2).diPredecessors should be(Set(1)) (hDi get 5).diPredecessors should be(Set(1)) } diff --git a/core/src/test/scala/scalax/collection/EditingSpec.scala b/core/src/test/scala/scalax/collection/EditingSpec.scala index 2c1d589f..eb4630d3 100644 --- a/core/src/test/scala/scalax/collection/EditingSpec.scala +++ b/core/src/test/scala/scalax/collection/EditingSpec.scala @@ -353,19 +353,26 @@ private class Editing[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E, } } - object `diPredecessors ` { - def `for UnDi`: Unit = { - (gUnDi get 1).diPredecessors should be(Set(2, 3, 4)) - (gUnDi get 2).diSuccessors should be(Set(1)) + object `inNeighbors ` { + def `for UnDi`: Unit = + (gUnDi get 1).inNeighbors should be(Set(2, 3, 4)) + def `for Di`: Unit = { + (gDi get 1).inNeighbors should be(Set.empty) + (gDi get 2).inNeighbors should be(Set(1)) } + def `for mixed`: Unit = + (gMixed get 2).diPredecessors should be(Set(1)) + } + + object `diPredecessors ` { + def `for UnDi`: Unit = + (gUnDi get 1).diPredecessors should be(Set(1, 2, 3, 4)) def `for Di`: Unit = { - (gDi get 1).diPredecessors should be(Set.empty) + (gDi get 1).diPredecessors should be(Set(1)) (gDi get 2).diPredecessors should be(Set(1)) } - def `for mixed`: Unit = { + def `for mixed`: Unit = (gMixed get 2).diPredecessors should be(Set(1)) - (gMixed get 3).diSuccessors should be(Set(4)) - } } object `neighbors ` { diff --git a/core/src/test/scala/scalax/collection/TopologicalSortSpec.scala b/core/src/test/scala/scalax/collection/TopologicalSortSpec.scala index 225e2338..f7dd9a04 100644 --- a/core/src/test/scala/scalax/collection/TopologicalSortSpec.scala +++ b/core/src/test/scala/scalax/collection/TopologicalSortSpec.scala @@ -49,7 +49,7 @@ final private class TopologicalSort[G[N, E <: Edge[N]] <: AnyGraph[N, E] with Gr def checkOrder(seq: OrderedInnerNodes, ignorePredecessorsOf: Option[graph.NodeT]): Unit = seq.foldLeft(predecessors(ignorePredecessorsOf)) { (allowedPredecessors, innerNode) => - if (!innerNode.diPredecessors.forall(allowedPredecessors.contains)) + if (!innerNode.inNeighbors.forall(allowedPredecessors.contains)) fail(s"$innerNode is misplaced in $seq") allowedPredecessors + innerNode } diff --git a/coreTestScala3/src/test/scala/scalax/collection/EditingHyperSpec.scala b/coreTestScala3/src/test/scala/scalax/collection/EditingHyperSpec.scala index d18eb17a..b262686a 100644 --- a/coreTestScala3/src/test/scala/scalax/collection/EditingHyperSpec.scala +++ b/coreTestScala3/src/test/scala/scalax/collection/EditingHyperSpec.scala @@ -93,7 +93,7 @@ private class EditingHyper[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[ single ~~> more(4, 9) ) - object `diSuccessors ` { + object `diSuccessors, outNeighbors` { def `for DiHyper`: Unit = { (hDi get 1).outNeighbors shouldEqual Set(2, 3, 4, 5, 9) (hDi get 1).diSuccessors shouldEqual Set(1, 2, 3, 4, 5, 9) @@ -102,9 +102,10 @@ private class EditingHyper[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[ } } - object `diPredecessors ` { + object `diPredecessors, inNeighbors` { def `for DiHyper`: Unit = { - (hDi get 1).diPredecessors should be(Set.empty) + (hDi get 1).inNeighbors should be(Set.empty) + (hDi get 1).diPredecessors should be(Set(1)) (hDi get 2).diPredecessors should be(Set(1)) (hDi get 5).diPredecessors should be(Set(1)) } diff --git a/coreTestScala3/src/test/scala/scalax/collection/EditingSpec.scala b/coreTestScala3/src/test/scala/scalax/collection/EditingSpec.scala index 2c1d589f..eb4630d3 100644 --- a/coreTestScala3/src/test/scala/scalax/collection/EditingSpec.scala +++ b/coreTestScala3/src/test/scala/scalax/collection/EditingSpec.scala @@ -353,19 +353,26 @@ private class Editing[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E, } } - object `diPredecessors ` { - def `for UnDi`: Unit = { - (gUnDi get 1).diPredecessors should be(Set(2, 3, 4)) - (gUnDi get 2).diSuccessors should be(Set(1)) + object `inNeighbors ` { + def `for UnDi`: Unit = + (gUnDi get 1).inNeighbors should be(Set(2, 3, 4)) + def `for Di`: Unit = { + (gDi get 1).inNeighbors should be(Set.empty) + (gDi get 2).inNeighbors should be(Set(1)) } + def `for mixed`: Unit = + (gMixed get 2).diPredecessors should be(Set(1)) + } + + object `diPredecessors ` { + def `for UnDi`: Unit = + (gUnDi get 1).diPredecessors should be(Set(1, 2, 3, 4)) def `for Di`: Unit = { - (gDi get 1).diPredecessors should be(Set.empty) + (gDi get 1).diPredecessors should be(Set(1)) (gDi get 2).diPredecessors should be(Set(1)) } - def `for mixed`: Unit = { + def `for mixed`: Unit = (gMixed get 2).diPredecessors should be(Set(1)) - (gMixed get 3).diSuccessors should be(Set(4)) - } } object `neighbors ` { diff --git a/coreTestScala3/src/test/scala/scalax/collection/TopologicalSortSpec.scala b/coreTestScala3/src/test/scala/scalax/collection/TopologicalSortSpec.scala index 1f2a2318..23f3ff8b 100644 --- a/coreTestScala3/src/test/scala/scalax/collection/TopologicalSortSpec.scala +++ b/coreTestScala3/src/test/scala/scalax/collection/TopologicalSortSpec.scala @@ -49,7 +49,7 @@ final private class TopologicalSort[G[N, E <: Edge[N]] <: AnyGraph[N, E] with Gr def checkOrder(seq: OrderedInnerNodes, ignorePredecessorsOf: Option[graph.NodeT]): Unit = seq.foldLeft(predecessors(ignorePredecessorsOf)) { (allowedPredecessors, innerNode) => - if (!innerNode.diPredecessors.forall(allowedPredecessors.contains)) + if (!innerNode.inNeighbors.forall(allowedPredecessors.contains)) fail(s"$innerNode is misplaced in $seq") allowedPredecessors + innerNode } From 5d1003bbac9be25183d6bc3c5e3f61bbff1431ca Mon Sep 17 00:00:00 2001 From: Scala Steward <43047562+scala-steward@users.noreply.github.com> Date: Sat, 8 Mar 2025 20:13:30 +0100 Subject: [PATCH 13/34] Update scala-library to 2.13.16 (#350) --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9acfd9f4..a7e28b74 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,7 +5,7 @@ jobs: strategy: fail-fast: false matrix: - scala: [ '2.13.15', '3.3.0' ] + scala: [ '2.13.16', '3.3.0' ] project: [ core, dot, json ] exclude: - scala: '3.3.0' From 677ccd89c0193b3b938463c06e6d33e1610e45ef Mon Sep 17 00:00:00 2001 From: Scala Steward <43047562+scala-steward@users.noreply.github.com> Date: Sat, 8 Mar 2025 20:15:06 +0100 Subject: [PATCH 14/34] Update sbt-pgp to 2.3.1 (#345) --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index 9a000078..174660e6 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -4,4 +4,4 @@ addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.16.0") addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.5.5") addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.3.2") addSbtPlugin("org.portable-scala" % "sbt-scala-native-crossproject" % "1.3.2") -addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.2.1") +addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.3.1") From 329874e02752e48af5819c5f75f34142fdc3f108 Mon Sep 17 00:00:00 2001 From: Scala Steward <43047562+scala-steward@users.noreply.github.com> Date: Sat, 8 Mar 2025 20:17:15 +0100 Subject: [PATCH 15/34] Update scala3-library to 3.3.5 (#355) Co-authored-by: Peter Empen --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a7e28b74..c1f74e70 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,10 +5,10 @@ jobs: strategy: fail-fast: false matrix: - scala: [ '2.13.16', '3.3.0' ] + scala: [ '2.13.15', '3.3.5' ] project: [ core, dot, json ] exclude: - - scala: '3.3.0' + - scala: '3.3.5' project: json runs-on: ubuntu-latest steps: From 2da5bacdcaacf90368ce424f9fbb93648261b26d Mon Sep 17 00:00:00 2001 From: Vince Date: Sat, 8 Mar 2025 20:18:41 +0100 Subject: [PATCH 16/34] fix name of member that's equivalent when negated (#342) --- core/src/main/scala/scalax/collection/generic/edgeBase.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/scala/scalax/collection/generic/edgeBase.scala b/core/src/main/scala/scalax/collection/generic/edgeBase.scala index d6e675ea..6c97b2fa 100644 --- a/core/src/main/scala/scalax/collection/generic/edgeBase.scala +++ b/core/src/main/scala/scalax/collection/generic/edgeBase.scala @@ -61,7 +61,7 @@ sealed trait Edge[+N] extends Equals { */ def isLooping: Boolean - /** Same as `! looping`. */ + /** Same as `! isLooping`. */ @inline final def nonLooping: Boolean = !isLooping /** The weight of this edge with a default of 1. From 80f09b44c54f6ea8cc02ee66d1bcd261a9a828a4 Mon Sep 17 00:00:00 2001 From: Vince Date: Sat, 8 Mar 2025 20:19:47 +0100 Subject: [PATCH 17/34] better error message when random graph order is non-positive; close #340 (#341) --- .../main/scala/scalax/collection/generator/RandomGraph.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/scala/scalax/collection/generator/RandomGraph.scala b/core/src/main/scala/scalax/collection/generator/RandomGraph.scala index f5984e41..e36ccfb8 100644 --- a/core/src/main/scala/scalax/collection/generator/RandomGraph.scala +++ b/core/src/main/scala/scalax/collection/generator/RandomGraph.scala @@ -41,7 +41,7 @@ class RandomGraph[N, E <: Edge[N], G[X, Y <: Edge[X]] <: AnyGraph[X, Y] with Gra weightFactory: Option[() => Long] = None, labelFactory: Option[() => Any] = None )(implicit nodeTag: ClassTag[N]) { - require(order > 0) + require(order > 0, s"Requested random graph order must be strictly positive, not $order") if (connected) require(nodeDegree.min >= 2) implicit val graphConfig: GraphConfig = graphCompanion.defaultConfig From e848bb6f031e4f8088e7eaa5987387ea386a080a Mon Sep 17 00:00:00 2001 From: Scala Steward <43047562+scala-steward@users.noreply.github.com> Date: Sun, 9 Mar 2025 09:40:22 +0100 Subject: [PATCH 18/34] Update sbt-scalafmt to 2.5.4 (#351) --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index 174660e6..4ca4a752 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,4 +1,4 @@ -addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.2") +addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.4") addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.12.1") addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.16.0") addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.5.5") From fa9ee6daa1eba90d6d138a9cf6be105871aea7cb Mon Sep 17 00:00:00 2001 From: Scala Steward <43047562+scala-steward@users.noreply.github.com> Date: Sun, 9 Mar 2025 09:40:52 +0100 Subject: [PATCH 19/34] Update sbt-scalajs to 1.18.2 (#353) --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index 4ca4a752..cc0bc281 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,6 +1,6 @@ addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.4") addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.12.1") -addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.16.0") +addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.18.2") addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.5.5") addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.3.2") addSbtPlugin("org.portable-scala" % "sbt-scala-native-crossproject" % "1.3.2") From bd2d468e535674700c1c150ab833e5b33a03a484 Mon Sep 17 00:00:00 2001 From: Scala Steward <43047562+scala-steward@users.noreply.github.com> Date: Sun, 9 Mar 2025 09:44:45 +0100 Subject: [PATCH 20/34] Update scalafmt-core to 3.8.6 (#354) * Update scalafmt-core to 3.8.6 * Reformat with scalafmt 3.8.6 Executed command: scalafmt --non-interactive * Add 'Reformat with scalafmt 3.8.6' to .git-blame-ignore-revs --------- Co-authored-by: Peter Empen --- .git-blame-ignore-revs | 3 ++ .scalafmt.conf | 2 +- .../collection/constrained/ConstraintOp.scala | 14 +++---- .../constrained/mutable/GraphLike.scala | 8 ++-- .../scala/scalax/collection/GraphBase.scala | 2 +- .../scalax/collection/EditingTypedSpec.scala | 2 +- .../scalax/collection/EditingTypedSpec.scala | 2 +- .../src/test/scala/demo/EditingDemoSpec.scala | 14 +++---- .../scalax/collection/EditingHyperSpec.scala | 2 +- .../scala/scalax/collection/EditingSpec.scala | 40 +++++++++---------- .../scalax/collection/EqualityHyperSpec.scala | 4 +- .../scala/scalax/collection/SetOpsSpec.scala | 14 +++---- .../collection/mutable/ArraySetSpec.scala | 2 +- .../src/test/scala/demo/EditingDemoSpec.scala | 14 +++---- .../scalax/collection/EditingHyperSpec.scala | 2 +- .../scala/scalax/collection/EditingSpec.scala | 40 +++++++++---------- .../scalax/collection/EditingTypedSpec.scala | 2 +- .../scalax/collection/EqualityHyperSpec.scala | 4 +- .../scala/scalax/collection/SetOpsSpec.scala | 14 +++---- .../collection/mutable/ArraySetSpec.scala | 2 +- .../scalax/collection/centrality/Katz.scala | 3 +- 21 files changed, 95 insertions(+), 95 deletions(-) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 7bac7092..a6d22c7f 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -6,3 +6,6 @@ c8ea894cd1f40ce3cc10f61a05be3b4c9ff11516 # Scala Steward: Reformat with scalafmt 3.8.2 643a4d6a4a8c546f5dba789f9c81961d944f051c + +# Scala Steward: Reformat with scalafmt 3.8.6 +832cfd7b56fad7cdf1d4976c19ee1a527487f842 diff --git a/.scalafmt.conf b/.scalafmt.conf index 5472f6b8..8511b0ae 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -1,4 +1,4 @@ -version=3.8.2 +version=3.8.6 runner.dialect = scala213 align.preset = more diff --git a/constrained/src/main/scala/scalax/collection/constrained/ConstraintOp.scala b/constrained/src/main/scala/scalax/collection/constrained/ConstraintOp.scala index 29b6ad56..5f1efed3 100644 --- a/constrained/src/main/scala/scalax/collection/constrained/ConstraintOp.scala +++ b/constrained/src/main/scala/scalax/collection/constrained/ConstraintOp.scala @@ -60,14 +60,12 @@ class ConstraintBinaryOp[N, E <: Edge[N], G <: Graph[N, E]]( case Or => rightFollowUp } if (followUp == Abort) PreCheckResult(Abort) - else { - if (rightDone) { - leftResult match { - case r: PreCheckResults => r += (right, rightResult) - case _ => new PreCheckResults(min(leftFollowUp, rightFollowUp), left, leftResult) += (right, rightResult) - } - } else leftResult - } + else if (rightDone) + leftResult match { + case r: PreCheckResults => r += (right, rightResult) + case _ => new PreCheckResults(min(leftFollowUp, rightFollowUp), left, leftResult) += (right, rightResult) + } + else leftResult } protected def eval[V <: ConstraintViolation](left: Either[V, G], right: => Either[V, G]): Either[V, G] = diff --git a/constrained/src/main/scala/scalax/collection/constrained/mutable/GraphLike.scala b/constrained/src/main/scala/scalax/collection/constrained/mutable/GraphLike.scala index 988bd041..a04df7af 100644 --- a/constrained/src/main/scala/scalax/collection/constrained/mutable/GraphLike.scala +++ b/constrained/src/main/scala/scalax/collection/constrained/mutable/GraphLike.scala @@ -32,7 +32,7 @@ trait GraphLike[N, E <: Edge[N], +CC[X, Y[+X] <: EdgeLikeIn[X]] <: GraphLike[X, case Complete => Right(remove) case PostCheck => val incidentEdges = node.edges.toIterable - if (remove) { + if (remove) postSubtract(selfGraph, Set(node), Set.empty[E[N]], preCheckResult).fold( failure => { withoutChecks { @@ -43,7 +43,7 @@ trait GraphLike[N, E <: Edge[N], +CC[X, Y[+X] <: EdgeLikeIn[X]] <: GraphLike[X, }, _ => Right(true) ) - } else Right(false) + else Right(false) case Abort => Left(preCheckResult) } } @@ -98,7 +98,7 @@ trait GraphLike[N, E <: Edge[N], +CC[X, Y[+X] <: EdgeLikeIn[X]] <: GraphLike[X, if (preCheckResult.abort) Some(preCheckResult) else { add - if (preCheckResult.postCheck) { + if (preCheckResult.postCheck) postAdd(this, newNodes, newEdges, preCheckResult).fold( failure => { withoutChecks { @@ -109,7 +109,7 @@ trait GraphLike[N, E <: Edge[N], +CC[X, Y[+X] <: EdgeLikeIn[X]] <: GraphLike[X, }, _ => None ) - } else None + else None } } (elems match { diff --git a/core/src/main/scala/scalax/collection/GraphBase.scala b/core/src/main/scala/scalax/collection/GraphBase.scala index 33e7a0d2..51f18a30 100644 --- a/core/src/main/scala/scalax/collection/GraphBase.scala +++ b/core/src/main/scala/scalax/collection/GraphBase.scala @@ -70,7 +70,7 @@ trait GraphBase[N, E <: Edge[N], +CC[X, Y <: Edge[X]] <: GraphBase[X, Y, CC]] @inline final def isCustomEdgeFilter(f: EdgePredicate) = f ne anyEdge type NodeT <: BaseInnerNode with Serializable - trait Node extends Serializable + trait Node extends Serializable trait BaseInnerNode extends Node with InnerNode { /** All edges at this node - commonly denoted as E(v). diff --git a/core/src/test/scala-2/scalax/collection/EditingTypedSpec.scala b/core/src/test/scala-2/scalax/collection/EditingTypedSpec.scala index 901bcbb3..c0603d14 100644 --- a/core/src/test/scala-2/scalax/collection/EditingTypedSpec.scala +++ b/core/src/test/scala-2/scalax/collection/EditingTypedSpec.scala @@ -94,7 +94,7 @@ private class EditingTyped[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[ madrid ~> rio :++ (flightNo, outer.departures, outer.duration) shouldBe outer } - def `extractor ` : Unit = { + def `extractor `: Unit = { val g = typedFactory.empty.asAnyGraph g.nodes foreach { case g.InnerNode(inner, Airport(code)) => diff --git a/core/src/test/scala-3/scalax/collection/EditingTypedSpec.scala b/core/src/test/scala-3/scalax/collection/EditingTypedSpec.scala index 7ca909af..e080d440 100644 --- a/core/src/test/scala-3/scalax/collection/EditingTypedSpec.scala +++ b/core/src/test/scala-3/scalax/collection/EditingTypedSpec.scala @@ -94,7 +94,7 @@ private class EditingTyped[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[ madrid ~> rio :++ (flightNo, outer.departures, outer.duration) shouldBe outer } - def `extractor ` : Unit = { + def `extractor `: Unit = { val g = typedFactory.empty.asAnyGraph g.nodes foreach { case g.InnerNode(inner, Airport(code)) => diff --git a/core/src/test/scala/demo/EditingDemoSpec.scala b/core/src/test/scala/demo/EditingDemoSpec.scala index da246edc..8df0121c 100644 --- a/core/src/test/scala/demo/EditingDemoSpec.scala +++ b/core/src/test/scala/demo/EditingDemoSpec.scala @@ -39,7 +39,7 @@ final class EditingDemoSpec extends RefSpec with Matchers { (Graph[Int, AnyEdge](1, 2 ~ 3) += 3 ~> 1) shouldBe Graph[Int, AnyEdge](1, 2 ~ 3, 3 ~> 1) } - def `equality ` : Unit = { + def `equality `: Unit = { val g = immutable.Graph(1 ~ 2) (g get 1).outer shouldBe 1 g get 1 ~ 2 shouldBe 2 ~ 1 @@ -47,7 +47,7 @@ final class EditingDemoSpec extends RefSpec with Matchers { g get 1 ~ 2 should not be 2 ~ 2 } - def `union, diff, intersect ` : Unit = { + def `union, diff, intersect `: Unit = { import immutable.Graph val g = Graph(1 ~ 2, 2 ~ 3, 2 ~ 4, 3 ~ 5, 4 ~ 5) val h = Graph(3 ~ 4, 3 ~ 5, 4 ~ 6, 5 ~ 6) @@ -58,7 +58,7 @@ final class EditingDemoSpec extends RefSpec with Matchers { g & h shouldBe Graph(4, 3 ~ 5) } - def `endpoints ` : Unit = { + def `endpoints `: Unit = { val uE = 3 ~ 4 // UnDiEdge[Int] uE.node1 * uE.node2 shouldBe 12 @@ -80,7 +80,7 @@ final class EditingDemoSpec extends RefSpec with Matchers { hE.ends.iterator.sum shouldBe 26 } - def `neighbors ` : Unit = { + def `neighbors `: Unit = { import immutable.Graph val g = Graph[Int, AnyEdge](0, 1 ~ 3, 3 ~> 2) @@ -96,7 +96,7 @@ final class EditingDemoSpec extends RefSpec with Matchers { n(3) findOutgoingTo n(2) shouldBe Some(e(3 ~> 2)) } - def `querying ` : Unit = { + def `querying `: Unit = { import immutable.Graph val g = Graph[Int, AnyEdge](2 ~> 3, 3 ~ 1, 5) @@ -107,7 +107,7 @@ final class EditingDemoSpec extends RefSpec with Matchers { g.edges filter (_ contains 4) shouldBe Symbol("empty") } - def `measuring ` : Unit = { + def `measuring `: Unit = { import immutable.Graph import scalax.collection.edges.labeled._ val g = Graph[Int, AnyEdge]( @@ -129,7 +129,7 @@ final class EditingDemoSpec extends RefSpec with Matchers { g.degreeNodesMap(degreeFilter = _ > 3) should contain only (4 -> Set(3, 4)) } - def `classifying ` : Unit = { + def `classifying `: Unit = { import immutable.Graph val g = Graph(1, 2 ~> 3) g.isConnected shouldBe false diff --git a/core/src/test/scala/scalax/collection/EditingHyperSpec.scala b/core/src/test/scala/scalax/collection/EditingHyperSpec.scala index b0009409..13347851 100644 --- a/core/src/test/scala/scalax/collection/EditingHyperSpec.scala +++ b/core/src/test/scala/scalax/collection/EditingHyperSpec.scala @@ -77,7 +77,7 @@ private class EditingHyper[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[ g.contains(h) shouldBe true } - def `isHyper ` : Unit = { + def `isHyper `: Unit = { def test(g: CC[Int, AnyHyperEdge[Int]], expected: Boolean): Unit = g.isHyper should be(expected) test(factory.from[Int, AnyHyperEdge](List(1 ~> 2, 1 ~~ 2 ~~ 3)), true) diff --git a/core/src/test/scala/scalax/collection/EditingSpec.scala b/core/src/test/scala/scalax/collection/EditingSpec.scala index eb4630d3..423ed917 100644 --- a/core/src/test/scala/scalax/collection/EditingSpec.scala +++ b/core/src/test/scala/scalax/collection/EditingSpec.scala @@ -48,7 +48,7 @@ class EditingImmutable extends RefSpec with Matchers { val gString_A = Graph[String, AnyEdge]("A") - def `- ` : Unit = { + def `- `: Unit = { val g_1 = gString_A - "B" g_1.order should be(1) @@ -63,13 +63,13 @@ class EditingImmutable extends RefSpec with Matchers { h - 2 should be(Graph(1, 3)) } - def `-- ` : Unit = { + def `-- `: Unit = { val g = Graph(1, 2 ~ 3, 3 ~ 4) g -- (List(2), List(3 ~ 3)) should be(Graph(1, 3 ~ 4)) g -- (List(2), List(3 ~ 4)) should be(Graph(1, 3, 4)) } - def `+ String ` : Unit = { + def `+ String `: Unit = { val g = gString_A + "B" g.elementCount shouldBe 2 g.nodes should contain("A") @@ -106,7 +106,7 @@ private class EditingMutable extends RefSpec with Matchers { g should be(Symbol("empty")) } - def `+ String ` : Unit = { + def `+ String `: Unit = { val g = Graph("A") addOne "B" g.elementCount shouldBe 2 g.contains("A") shouldBe true @@ -119,7 +119,7 @@ private class EditingMutable extends RefSpec with Matchers { h.elementCount should be(3) } - def `serve -= properly (2)` : Unit = { + def `serve -= properly (2)`: Unit = { val g = Graph(1 ~ 2, 2 ~ 3) g subtractOne 2 should be(Graph(1, 3)) g.size should be(0) @@ -187,13 +187,13 @@ private class Editing[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E, private val gString_A = factory("A") object `graph editing` { - def `empty ` : Unit = { + def `empty `: Unit = { val eg = factory.empty[Nothing, Nothing] eg shouldBe empty eg should have size 0 } - def `apply ` : Unit = { + def `apply `: Unit = { gInt_1_3 should not be empty gInt_1_3.order should be(2) gInt_1_3(0) shouldBe false @@ -209,14 +209,14 @@ private class Editing[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E, factory(N1() ~> N2(), N1() ~> N1()): CC[Node, DiEdge[Node]] // should typeCheck } - def `isDirected ` : Unit = { + def `isDirected `: Unit = { def directed(g: CC[Int, AnyEdge[Int]], expected: Boolean): Unit = g.isDirected should be(expected) directed(factory(1 ~ 2), false) directed(factory(1 ~> 2), true) } - def `from ` : Unit = { + def `from `: Unit = { val (n_start, n_end) = (11, 20) val nodes = List.range(n_start, n_end) val edges = List[DiEdge[Int]](14 ~> 16, 16 ~> 18, 18 ~> 20, 20 ~> 22) @@ -225,17 +225,17 @@ private class Editing[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E, g.edges.size should be(edges.size) } - def `contains ` : Unit = { + def `contains `: Unit = { seq_1_3 foreach (n => gInt_1_3 contains n should be(true)) gInt_1_3.iterator.next() shouldBe a[gInt_1_3.InnerNode] } - def `toString ` : Unit = { + def `toString `: Unit = { gInt_1_3.toString shouldBe "Graph(NodeSet(1, 3), EdgeSet())" gString_A.toString shouldBe """Graph(NodeSet(A), EdgeSet())""" } - def `render ` : Unit = { + def `render `: Unit = { import ToString._ gInt_1_3.render(SetElemsOnSeparateLines()) shouldBe """Graph( @@ -262,7 +262,7 @@ private class Editing[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E, | 1 ~ 2)""".stripMargin } - def `from inner ` : Unit = { + def `from inner `: Unit = { val gn = factory(2, 3) factory.from[Int, Nothing](gn.nodes.outerIterable, Nil) should equal(gn) @@ -271,7 +271,7 @@ private class Editing[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E, factory.from(g.edges.outerIterable) should equal(g) } - def `NodeSet ` : Unit = { + def `NodeSet `: Unit = { val o = Vector.range(0, 4) val g = factory(o(1) ~ o(2), o(2) ~ o(3)) val n = o map (g.nodes find _ getOrElse g.nodes.head) @@ -290,7 +290,7 @@ private class Editing[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E, restored.find(_ == n(1)).get.edges should have size 1 } - def `EdgeAssoc ` : Unit = { + def `EdgeAssoc `: Unit = { val e = 1 ~ 2 e shouldBe an[UnDiEdge[_]] @@ -393,13 +393,13 @@ private class Editing[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E, n(1) findOutgoingTo n(1) should be(Some(1 ~> 1)) } - def `degree ` : Unit = { + def `degree `: Unit = { val g = factory(1 ~ 1, 1 ~ 2, 1 ~ 3, 1 ~ 4) (g get 1).degree should be(5) (g get 2).degree should be(1) } - def `incoming ` : Unit = { + def `incoming `: Unit = { val uEdges = Seq(1 ~ 1, 1 ~ 2, 1 ~ 3, 1 ~ 4) val g = factory(uEdges(0), uEdges(1), uEdges(2), uEdges(3)) (g get 1).incoming should be(uEdges.toSet) @@ -417,7 +417,7 @@ private class Editing[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E, (g get 1 ~ 2).adjacents should be(Set[AnyEdge[Int]](1 ~> 3, 1 ~ 5, 2 ~ 3)) } - def `filter ` : Unit = { + def `filter `: Unit = { val g: AnyGraph[Int, DiEdge[Int]] = factory(2 ~> 3, 3 ~> 1, 5) g filter (_ > 1) should be(factory(2 ~> 3, 5)) g filter (_ < 2) should be(factory(1)) @@ -427,7 +427,7 @@ private class Editing[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E, g filter (nodeP = _ <= 3, edgeP = _ contains 2) should be(factory(1, 2 ~> 3)) } - def `match ` : Unit = { + def `match `: Unit = { val di = 1 ~> 2 (di match { case DiEdge(src, _) => src }) should be(1) (di match { case src ~> trg => src + trg }) should be(3) @@ -437,7 +437,7 @@ private class Editing[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E, (unDi match { case n1 ~ n2 => n1 + n2 }) should be(3) } - def `foldLeft, foldLeftOuter ` : Unit = { + def `foldLeft, foldLeftOuter `: Unit = { val g = factory(1 ~> 2, 2 ~> 3, 7) val sumOfNodes = 13 diff --git a/core/src/test/scala/scalax/collection/EqualityHyperSpec.scala b/core/src/test/scala/scalax/collection/EqualityHyperSpec.scala index 367a5623..27872dff 100644 --- a/core/src/test/scala/scalax/collection/EqualityHyperSpec.scala +++ b/core/src/test/scala/scalax/collection/EqualityHyperSpec.scala @@ -6,7 +6,7 @@ import scalax.collection.generic.AbstractDiHyperEdge class EqualityHyperSpec extends RefSpec with Matchers { - def `hyperedges, bag like ` : Unit = { + def `hyperedges, bag like `: Unit = { import scalax.collection.hyperedges._ val nodes = List('A', 'B', 'C', 'C') @@ -19,7 +19,7 @@ class EqualityHyperSpec extends RefSpec with Matchers { hEdge.node(i) shouldBe nodes(i) } - def `hyperedges, ordered ` : Unit = { + def `hyperedges, ordered `: Unit = { import scalax.collection.hyperedges.ordered._ val nodes = List('A', 'B', 'C', 'C') diff --git a/core/src/test/scala/scalax/collection/SetOpsSpec.scala b/core/src/test/scala/scalax/collection/SetOpsSpec.scala index 916a4dae..313297ab 100644 --- a/core/src/test/scala/scalax/collection/SetOpsSpec.scala +++ b/core/src/test/scala/scalax/collection/SetOpsSpec.scala @@ -46,7 +46,7 @@ private class SetOps[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E, with IntelliJ[CC] with Visualizer { - def `concat ` : Unit = { + def `concat `: Unit = { factory(1 ~ 2).asAnyGraph concat List(1 ~ 2) factory(1 ~ 2).asAnyGraph concat List(1 ~> 2) factory(1 ~ 2).asAnyGraph ++ List(1 ~ 2) @@ -58,13 +58,13 @@ private class SetOps[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E, factory(1 ~ 2).asAnyGraph ++ (List('x'), List('a' ~ 'b')) } - def `union ` : Unit = + def `union `: Unit = g union h shouldEqual Expected.g_union_h - def `difference ` : Unit = + def `difference `: Unit = g diff h shouldEqual Expected.g_diff_h - def `intersection ` : Unit = { + def `intersection `: Unit = { val expected = factory(3 ~ 5, 4) withGraph(g intersect h)(_ shouldEqual expected) withGraph(g & h)(_ shouldEqual expected) @@ -81,17 +81,17 @@ private class SetOpsMutable extends RefSpec with Matchers with SetOpExamples[mut private val iH = immutable.Graph.from(hEdges) - def `unionInPlace ` : Unit = { + def `unionInPlace `: Unit = { (g |= h) shouldEqual Expected.g_union_h (g |= iH) shouldEqual Expected.g_union_h } - def `--= ` : Unit = { + def `--= `: Unit = { (g --= h) shouldEqual Expected.g_diff_h (g --= iH) shouldEqual Expected.g_diff_h } - def `&= ` : Unit = { + def `&= `: Unit = { val expected = factory(3 ~ 5, 4) (g &= h) shouldEqual expected diff --git a/core/src/test/scala/scalax/collection/mutable/ArraySetSpec.scala b/core/src/test/scala/scalax/collection/mutable/ArraySetSpec.scala index 437ffbf9..7d501581 100644 --- a/core/src/test/scala/scalax/collection/mutable/ArraySetSpec.scala +++ b/core/src/test/scala/scalax/collection/mutable/ArraySetSpec.scala @@ -114,7 +114,7 @@ class ArraySetSpec extends RefSpec with Matchers { sorted.range(-10, -4) shouldBe SortedArraySet.empty[Int] } - def `supports ++` : Unit = { + def `supports ++`: Unit = { val a = ArraySet.empty[Int] val b = ArraySet(1) val c = ArraySet(2) diff --git a/coreTestScala3/src/test/scala/demo/EditingDemoSpec.scala b/coreTestScala3/src/test/scala/demo/EditingDemoSpec.scala index aa62dccf..356ff279 100644 --- a/coreTestScala3/src/test/scala/demo/EditingDemoSpec.scala +++ b/coreTestScala3/src/test/scala/demo/EditingDemoSpec.scala @@ -39,7 +39,7 @@ final class EditingDemoSpec extends RefSpec with Matchers { (Graph[Int, AnyEdge](1, 2 ~ 3) += 3 ~> 1) shouldBe Graph[Int, AnyEdge](1, 2 ~ 3, 3 ~> 1) } - def `equality ` : Unit = { + def `equality `: Unit = { val g = immutable.Graph(1 ~ 2) (g get 1).outer shouldBe 1 g get 1 ~ 2 shouldBe 2 ~ 1 @@ -47,7 +47,7 @@ final class EditingDemoSpec extends RefSpec with Matchers { g get 1 ~ 2 should not be 2 ~ 2 } - def `union, diff, intersect ` : Unit = { + def `union, diff, intersect `: Unit = { import immutable.Graph val g = Graph(1 ~ 2, 2 ~ 3, 2 ~ 4, 3 ~ 5, 4 ~ 5) val h = Graph(3 ~ 4, 3 ~ 5, 4 ~ 6, 5 ~ 6) @@ -58,7 +58,7 @@ final class EditingDemoSpec extends RefSpec with Matchers { g & h shouldBe Graph(4, 3 ~ 5) } - def `endpoints ` : Unit = { + def `endpoints `: Unit = { val uE = 3 ~ 4 // UnDiEdge[Int] uE.node1 * uE.node2 shouldBe 12 @@ -80,7 +80,7 @@ final class EditingDemoSpec extends RefSpec with Matchers { hE.ends.iterator.sum shouldBe 26 } - def `neighbors ` : Unit = { + def `neighbors `: Unit = { import immutable.Graph val g = Graph[Int, AnyEdge](0, 1 ~ 3, 3 ~> 2) @@ -96,7 +96,7 @@ final class EditingDemoSpec extends RefSpec with Matchers { n(3) findOutgoingTo n(2) shouldBe Some(e(3 ~> 2)) } - def `querying ` : Unit = { + def `querying `: Unit = { import immutable.Graph val g = Graph[Int, AnyEdge](2 ~> 3, 3 ~ 1, 5) @@ -107,7 +107,7 @@ final class EditingDemoSpec extends RefSpec with Matchers { g.edges filter (_ contains 4) shouldBe Symbol("empty") } - def `measuring ` : Unit = { + def `measuring `: Unit = { import immutable.Graph import scalax.collection.edges.labeled.* val g = Graph[Int, AnyEdge]( @@ -129,7 +129,7 @@ final class EditingDemoSpec extends RefSpec with Matchers { g.degreeNodesMap(degreeFilter = _ > 3) should contain only (4 -> Set(3, 4)) } - def `classifying ` : Unit = { + def `classifying `: Unit = { import immutable.Graph val g = Graph(1, 2 ~> 3) g.isConnected shouldBe false diff --git a/coreTestScala3/src/test/scala/scalax/collection/EditingHyperSpec.scala b/coreTestScala3/src/test/scala/scalax/collection/EditingHyperSpec.scala index b262686a..f745accc 100644 --- a/coreTestScala3/src/test/scala/scalax/collection/EditingHyperSpec.scala +++ b/coreTestScala3/src/test/scala/scalax/collection/EditingHyperSpec.scala @@ -76,7 +76,7 @@ private class EditingHyper[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[ g.contains(h) shouldBe true } - def `isHyper ` : Unit = { + def `isHyper `: Unit = { def test(g: CC[Int, AnyHyperEdge[Int]], expected: Boolean): Unit = g.isHyper should be(expected) test(factory.from[Int, AnyHyperEdge](List(1 ~> 2, 1 ~~ 2 ~~ 3)), true) diff --git a/coreTestScala3/src/test/scala/scalax/collection/EditingSpec.scala b/coreTestScala3/src/test/scala/scalax/collection/EditingSpec.scala index eb4630d3..423ed917 100644 --- a/coreTestScala3/src/test/scala/scalax/collection/EditingSpec.scala +++ b/coreTestScala3/src/test/scala/scalax/collection/EditingSpec.scala @@ -48,7 +48,7 @@ class EditingImmutable extends RefSpec with Matchers { val gString_A = Graph[String, AnyEdge]("A") - def `- ` : Unit = { + def `- `: Unit = { val g_1 = gString_A - "B" g_1.order should be(1) @@ -63,13 +63,13 @@ class EditingImmutable extends RefSpec with Matchers { h - 2 should be(Graph(1, 3)) } - def `-- ` : Unit = { + def `-- `: Unit = { val g = Graph(1, 2 ~ 3, 3 ~ 4) g -- (List(2), List(3 ~ 3)) should be(Graph(1, 3 ~ 4)) g -- (List(2), List(3 ~ 4)) should be(Graph(1, 3, 4)) } - def `+ String ` : Unit = { + def `+ String `: Unit = { val g = gString_A + "B" g.elementCount shouldBe 2 g.nodes should contain("A") @@ -106,7 +106,7 @@ private class EditingMutable extends RefSpec with Matchers { g should be(Symbol("empty")) } - def `+ String ` : Unit = { + def `+ String `: Unit = { val g = Graph("A") addOne "B" g.elementCount shouldBe 2 g.contains("A") shouldBe true @@ -119,7 +119,7 @@ private class EditingMutable extends RefSpec with Matchers { h.elementCount should be(3) } - def `serve -= properly (2)` : Unit = { + def `serve -= properly (2)`: Unit = { val g = Graph(1 ~ 2, 2 ~ 3) g subtractOne 2 should be(Graph(1, 3)) g.size should be(0) @@ -187,13 +187,13 @@ private class Editing[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E, private val gString_A = factory("A") object `graph editing` { - def `empty ` : Unit = { + def `empty `: Unit = { val eg = factory.empty[Nothing, Nothing] eg shouldBe empty eg should have size 0 } - def `apply ` : Unit = { + def `apply `: Unit = { gInt_1_3 should not be empty gInt_1_3.order should be(2) gInt_1_3(0) shouldBe false @@ -209,14 +209,14 @@ private class Editing[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E, factory(N1() ~> N2(), N1() ~> N1()): CC[Node, DiEdge[Node]] // should typeCheck } - def `isDirected ` : Unit = { + def `isDirected `: Unit = { def directed(g: CC[Int, AnyEdge[Int]], expected: Boolean): Unit = g.isDirected should be(expected) directed(factory(1 ~ 2), false) directed(factory(1 ~> 2), true) } - def `from ` : Unit = { + def `from `: Unit = { val (n_start, n_end) = (11, 20) val nodes = List.range(n_start, n_end) val edges = List[DiEdge[Int]](14 ~> 16, 16 ~> 18, 18 ~> 20, 20 ~> 22) @@ -225,17 +225,17 @@ private class Editing[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E, g.edges.size should be(edges.size) } - def `contains ` : Unit = { + def `contains `: Unit = { seq_1_3 foreach (n => gInt_1_3 contains n should be(true)) gInt_1_3.iterator.next() shouldBe a[gInt_1_3.InnerNode] } - def `toString ` : Unit = { + def `toString `: Unit = { gInt_1_3.toString shouldBe "Graph(NodeSet(1, 3), EdgeSet())" gString_A.toString shouldBe """Graph(NodeSet(A), EdgeSet())""" } - def `render ` : Unit = { + def `render `: Unit = { import ToString._ gInt_1_3.render(SetElemsOnSeparateLines()) shouldBe """Graph( @@ -262,7 +262,7 @@ private class Editing[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E, | 1 ~ 2)""".stripMargin } - def `from inner ` : Unit = { + def `from inner `: Unit = { val gn = factory(2, 3) factory.from[Int, Nothing](gn.nodes.outerIterable, Nil) should equal(gn) @@ -271,7 +271,7 @@ private class Editing[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E, factory.from(g.edges.outerIterable) should equal(g) } - def `NodeSet ` : Unit = { + def `NodeSet `: Unit = { val o = Vector.range(0, 4) val g = factory(o(1) ~ o(2), o(2) ~ o(3)) val n = o map (g.nodes find _ getOrElse g.nodes.head) @@ -290,7 +290,7 @@ private class Editing[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E, restored.find(_ == n(1)).get.edges should have size 1 } - def `EdgeAssoc ` : Unit = { + def `EdgeAssoc `: Unit = { val e = 1 ~ 2 e shouldBe an[UnDiEdge[_]] @@ -393,13 +393,13 @@ private class Editing[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E, n(1) findOutgoingTo n(1) should be(Some(1 ~> 1)) } - def `degree ` : Unit = { + def `degree `: Unit = { val g = factory(1 ~ 1, 1 ~ 2, 1 ~ 3, 1 ~ 4) (g get 1).degree should be(5) (g get 2).degree should be(1) } - def `incoming ` : Unit = { + def `incoming `: Unit = { val uEdges = Seq(1 ~ 1, 1 ~ 2, 1 ~ 3, 1 ~ 4) val g = factory(uEdges(0), uEdges(1), uEdges(2), uEdges(3)) (g get 1).incoming should be(uEdges.toSet) @@ -417,7 +417,7 @@ private class Editing[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E, (g get 1 ~ 2).adjacents should be(Set[AnyEdge[Int]](1 ~> 3, 1 ~ 5, 2 ~ 3)) } - def `filter ` : Unit = { + def `filter `: Unit = { val g: AnyGraph[Int, DiEdge[Int]] = factory(2 ~> 3, 3 ~> 1, 5) g filter (_ > 1) should be(factory(2 ~> 3, 5)) g filter (_ < 2) should be(factory(1)) @@ -427,7 +427,7 @@ private class Editing[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E, g filter (nodeP = _ <= 3, edgeP = _ contains 2) should be(factory(1, 2 ~> 3)) } - def `match ` : Unit = { + def `match `: Unit = { val di = 1 ~> 2 (di match { case DiEdge(src, _) => src }) should be(1) (di match { case src ~> trg => src + trg }) should be(3) @@ -437,7 +437,7 @@ private class Editing[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E, (unDi match { case n1 ~ n2 => n1 + n2 }) should be(3) } - def `foldLeft, foldLeftOuter ` : Unit = { + def `foldLeft, foldLeftOuter `: Unit = { val g = factory(1 ~> 2, 2 ~> 3, 7) val sumOfNodes = 13 diff --git a/coreTestScala3/src/test/scala/scalax/collection/EditingTypedSpec.scala b/coreTestScala3/src/test/scala/scalax/collection/EditingTypedSpec.scala index 7ca909af..e080d440 100644 --- a/coreTestScala3/src/test/scala/scalax/collection/EditingTypedSpec.scala +++ b/coreTestScala3/src/test/scala/scalax/collection/EditingTypedSpec.scala @@ -94,7 +94,7 @@ private class EditingTyped[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[ madrid ~> rio :++ (flightNo, outer.departures, outer.duration) shouldBe outer } - def `extractor ` : Unit = { + def `extractor `: Unit = { val g = typedFactory.empty.asAnyGraph g.nodes foreach { case g.InnerNode(inner, Airport(code)) => diff --git a/coreTestScala3/src/test/scala/scalax/collection/EqualityHyperSpec.scala b/coreTestScala3/src/test/scala/scalax/collection/EqualityHyperSpec.scala index ba8e6e1b..d1fcbd75 100644 --- a/coreTestScala3/src/test/scala/scalax/collection/EqualityHyperSpec.scala +++ b/coreTestScala3/src/test/scala/scalax/collection/EqualityHyperSpec.scala @@ -6,7 +6,7 @@ import scalax.collection.generic.AbstractDiHyperEdge class EqualityHyperSpec extends RefSpec with Matchers { - def `hyperedges, bag like ` : Unit = { + def `hyperedges, bag like `: Unit = { import scalax.collection.hyperedges.* val nodes = List('A', 'B', 'C', 'C') @@ -19,7 +19,7 @@ class EqualityHyperSpec extends RefSpec with Matchers { hEdge.node(i) shouldBe nodes(i) } - def `hyperedges, ordered ` : Unit = { + def `hyperedges, ordered `: Unit = { import scalax.collection.hyperedges.ordered.* val nodes = List('A', 'B', 'C', 'C') diff --git a/coreTestScala3/src/test/scala/scalax/collection/SetOpsSpec.scala b/coreTestScala3/src/test/scala/scalax/collection/SetOpsSpec.scala index d6739bb0..f65609f4 100644 --- a/coreTestScala3/src/test/scala/scalax/collection/SetOpsSpec.scala +++ b/coreTestScala3/src/test/scala/scalax/collection/SetOpsSpec.scala @@ -46,7 +46,7 @@ private class SetOps[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E, with IntelliJ[CC] with Visualizer { - def `concat ` : Unit = { + def `concat `: Unit = { factory(1 ~ 2).asAnyGraph concat List(1 ~ 2) factory(1 ~ 2).asAnyGraph concat List(1 ~> 2) factory(1 ~ 2).asAnyGraph ++ List(1 ~ 2) @@ -58,13 +58,13 @@ private class SetOps[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E, factory(1 ~ 2).asAnyGraph ++ (List('x'), List('a' ~ 'b')) } - def `union ` : Unit = + def `union `: Unit = g union h shouldEqual Expected.g_union_h - def `difference ` : Unit = + def `difference `: Unit = g diff h shouldEqual Expected.g_diff_h - def `intersection ` : Unit = { + def `intersection `: Unit = { val expected = factory(3 ~ 5, 4) withGraph(g intersect h)(_ shouldEqual expected) withGraph(g & h)(_ shouldEqual expected) @@ -81,17 +81,17 @@ private class SetOpsMutable extends RefSpec with Matchers with SetOpExamples[mut private val iH = immutable.Graph.from(hEdges) - def `unionInPlace ` : Unit = { + def `unionInPlace `: Unit = { (g |= h) shouldEqual Expected.g_union_h (g |= iH) shouldEqual Expected.g_union_h } - def `--= ` : Unit = { + def `--= `: Unit = { (g --= h) shouldEqual Expected.g_diff_h (g --= iH) shouldEqual Expected.g_diff_h } - def `&= ` : Unit = { + def `&= `: Unit = { val expected = factory(3 ~ 5, 4) (g &= h) shouldEqual expected diff --git a/coreTestScala3/src/test/scala/scalax/collection/mutable/ArraySetSpec.scala b/coreTestScala3/src/test/scala/scalax/collection/mutable/ArraySetSpec.scala index 437ffbf9..7d501581 100644 --- a/coreTestScala3/src/test/scala/scalax/collection/mutable/ArraySetSpec.scala +++ b/coreTestScala3/src/test/scala/scalax/collection/mutable/ArraySetSpec.scala @@ -114,7 +114,7 @@ class ArraySetSpec extends RefSpec with Matchers { sorted.range(-10, -4) shouldBe SortedArraySet.empty[Int] } - def `supports ++` : Unit = { + def `supports ++`: Unit = { val a = ArraySet.empty[Int] val b = ArraySet(1) val c = ArraySet(2) diff --git a/misc/src/main/scala/scalax/collection/centrality/Katz.scala b/misc/src/main/scala/scalax/collection/centrality/Katz.scala index 8cfb2449..e76204da 100644 --- a/misc/src/main/scala/scalax/collection/centrality/Katz.scala +++ b/misc/src/main/scala/scalax/collection/centrality/Katz.scala @@ -41,11 +41,10 @@ object Katz { import g.ExtendedNodeVisitor var weight = 0f - n.innerNodeTraverser.withMaxDepth(maxDepth) foreach { + n.innerNodeTraverser.withMaxDepth(maxDepth) foreach ExtendedNodeVisitor { (node, count, depth, informer) => weight += degrees(node.asInstanceOf[G#NodeT]) * Factor(depth) } - } weightBuilder += ((n.asInstanceOf[G#NodeT], weight)) } From b936cf74dee6544e79421164f27d30e7ae910b8e Mon Sep 17 00:00:00 2001 From: Scala Steward <43047562+scala-steward@users.noreply.github.com> Date: Sun, 9 Mar 2025 09:54:22 +0100 Subject: [PATCH 21/34] Update sbt-scalafix to 0.14.2 (#356) Co-authored-by: Peter Empen --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index cc0bc281..9136934e 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,5 +1,5 @@ addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.4") -addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.12.1") +addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.14.2") addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.18.2") addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.5.5") addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.3.2") From af4a2bef40c357327413e96a3768ba2b6ccd0c72 Mon Sep 17 00:00:00 2001 From: Scala Steward <43047562+scala-steward@users.noreply.github.com> Date: Sun, 9 Mar 2025 09:58:04 +0100 Subject: [PATCH 22/34] Update sbt-scala-native to 0.5.7 (#358) Co-authored-by: Peter Empen --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index 9136934e..8c88d18b 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,7 +1,7 @@ addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.4") addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.14.2") addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.18.2") -addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.5.5") +addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.5.7") addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.3.2") addSbtPlugin("org.portable-scala" % "sbt-scala-native-crossproject" % "1.3.2") addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.3.1") From 991a49e8fbab12be7b1b7db5e350651a28ec4990 Mon Sep 17 00:00:00 2001 From: Scala Steward <43047562+scala-steward@users.noreply.github.com> Date: Sun, 9 Mar 2025 09:58:54 +0100 Subject: [PATCH 23/34] Update sbt, scripted-plugin to 1.10.10 (#360) --- project/build.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/build.properties b/project/build.properties index 0b699c30..e97b2722 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.10.2 +sbt.version=1.10.10 From 97e014bacabc7eee566785b1933ce41e87dc445f Mon Sep 17 00:00:00 2001 From: Peter Empen Date: Sun, 9 Mar 2025 10:21:56 +0100 Subject: [PATCH 24/34] Update README.md --- README.md | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index a44419ca..7ae1edf1 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ This is the source code repository and issue tracker for [Graph for Scala](http://www.scala-graph.org). [Questions or any feedback](https://groups.google.com/forum/#!forum/scala-graph) are appreciated. -Please use GitHub issues for proven issues or enhancement requests but not for questions. +Please use GitHub issues for proven issues or enhancement requests, not for questions. You are also welcome as a co-contributor. @@ -13,22 +13,15 @@ Peter ## Branches -**1.x** started in 2011. It has evolved by paying high attention to version compatibility. +**1.x** started in 2011, is now superseeded by 2.x. It evolved by paying high attention to version compatibility. -**2.x** started in 2019 to make some significant improvements that also need new, simplified signatures. -New features include +**2.x**, the **default** branch, started in 2019 to make some significant improvements that also need new, simplified signatures. +The new features include * multiple sources for directed hyperedges -* easy edge class definition using case classes thus easy definition of ADTs of edges +* easy edge class definition using case classes, thus easy definition of ADTs for edges * improved functional graph handling. -Shipment is due shortly after Scala 2.13.11 is published because -this fix will help to further simplify user code. +2.0 was released in May 2023. -## Roadmap - -* Complete migration of random graphs for 2.x. -* Investigate whether and how the JSON module should be retained and migrated to 2.x or dropped. Migrate it if needed. -* Remove internal State by possibly also changing node references to IDs. -* Add support for algorithms by enrichment that use fluent properties. -* Reimplement immutable graphs to be based on a persistent data structure. +**persistent** opened in January 2024 with the aim of implementing a persistent data structure. From a54c42503660e06157d4ac32d308979775990dbc Mon Sep 17 00:00:00 2001 From: Peter Empen Date: Sun, 9 Mar 2025 10:23:18 +0100 Subject: [PATCH 25/34] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7ae1edf1..fc7891fb 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Peter **1.x** started in 2011, is now superseeded by 2.x. It evolved by paying high attention to version compatibility. -**2.x**, the **default** branch, started in 2019 to make some significant improvements that also need new, simplified signatures. +**2.x**, the **default** branch, started in 2019 to make some significant improvements that also needed new, simplified signatures. The new features include * multiple sources for directed hyperedges From afb3e28be8c4d44f17b40206e5d91e2b9fb842a3 Mon Sep 17 00:00:00 2001 From: Peter Empen Date: Wed, 26 Mar 2025 10:20:17 +0100 Subject: [PATCH 26/34] bump version --- project/Version.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/project/Version.scala b/project/Version.scala index 54704f62..e3235238 100644 --- a/project/Version.scala +++ b/project/Version.scala @@ -1,5 +1,5 @@ object Version { - val compiler_2_13 = "2.13.15" + val compiler_2_13 = "2.13.16" val compiler_3 = "3.5.1" val compiler_3_fallback = "3.3.0" @@ -11,8 +11,8 @@ object Version { private val minor = 0 private def version(patch: Int) = s"$major.$minor.$patch$snapshot" - val highest = version(2) - val core = version(2) + val highest = version(3) + val core = version(3) val dot = version(0) val json = version(0) } From e6fb23e649ff277106d62b030c8ed6400d308deb Mon Sep 17 00:00:00 2001 From: Peter Empen Date: Wed, 26 Mar 2025 18:15:16 +0100 Subject: [PATCH 27/34] resolve warning 'named argument is deprecated for infix syntax' --- .../main/scala/scalax/collection/AnyGraph.scala | 16 +++++++--------- .../scalax/collection/mutable/Growable.scala | 12 +++++++++--- .../scala/scalax/collection/EditingSpec.scala | 8 ++++---- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/core/src/main/scala/scalax/collection/AnyGraph.scala b/core/src/main/scala/scalax/collection/AnyGraph.scala index 35556d3c..aaa78684 100644 --- a/core/src/main/scala/scalax/collection/AnyGraph.scala +++ b/core/src/main/scala/scalax/collection/AnyGraph.scala @@ -402,7 +402,7 @@ trait GraphLike[N, E <: Edge[N], +CC[X, Y <: Edge[X]] <: GraphLike[X, Y, CC] wit nodes foreach { n => val newNodes = fNode(n) nMap put (n, newNodes) - b ++= newNodes + b addNodes newNodes } (nMap, b) } @@ -521,7 +521,7 @@ trait GraphLike[N, E <: Edge[N], +CC[X, Y <: Edge[X]] <: GraphLike[X, Y, CC] wit flatMapNodes[NN, EC](fNode) match { case (nMap, builder) => edges foreach { case e @ InnerEdge(AnyEdge(n1: NodeT @unchecked, n2: NodeT @unchecked), _) => - builder ++= (edges = fEdge(e, nMap(n1), nMap(n2))) + builder addEdges fEdge(e, nMap(n1), nMap(n2)) } builder.result } @@ -546,7 +546,7 @@ trait GraphLike[N, E <: Edge[N], +CC[X, Y <: Edge[X]] <: GraphLike[X, Y, CC] wit case e @ InnerEdge(AnyEdge(n1: NodeT @unchecked, n2: NodeT @unchecked), _) => val nn1s = nMap(n1) val nn2s = nMap(n2) - builder ++= (edges = fEdge.fold(fHyperEdge(e, nn1s ++ nn2s))(_(e, nn1s, nn2s))) + builder addEdges fEdge.fold(fHyperEdge(e, nn1s ++ nn2s))(_(e, nn1s, nn2s)) case e @ InnerEdge( AnyDiHyperEdge(sources: OneOrMore[NodeT @unchecked], targets: OneOrMore[NodeT @unchecked]), @@ -554,12 +554,10 @@ trait GraphLike[N, E <: Edge[N], +CC[X, Y <: Edge[X]] <: GraphLike[X, Y, CC] wit ) => val newSources = sources.flatMap(nMap) val newTargets = targets.flatMap(nMap) - builder ++= (edges = - fDiHyperEdge.fold(fHyperEdge(e, newSources ++ newTargets))(_(e, newSources, newTargets)) - ) + builder addEdges fDiHyperEdge.fold(fHyperEdge(e, newSources ++ newTargets))(_(e, newSources, newTargets)) case e @ InnerEdge(AnyHyperEdge(ends: Several[NodeT @unchecked]), _) => - builder ++= (edges = fHyperEdge(e, ends.flatMap(nMap))) + builder addEdges fHyperEdge(e, ends.flatMap(nMap)) } builder.result } @@ -582,7 +580,7 @@ trait GraphLike[N, E <: Edge[N], +CC[X, Y <: Edge[X]] <: GraphLike[X, Y, CC] wit case e @ InnerEdge(AnyEdge(n1: NodeT @unchecked, n2: NodeT @unchecked), _) => val nn1s = nMap(n1) val nn2s = nMap(n2) - builder ++= (edges = fEdge.fold(fDiHyperEdge(e, nn1s, nn2s))(_(e, nn1s, nn2s))) + builder addEdges fEdge.fold(fDiHyperEdge(e, nn1s, nn2s))(_(e, nn1s, nn2s)) case e @ InnerEdge( AnyDiHyperEdge(sources: OneOrMore[NodeT @unchecked], targets: OneOrMore[NodeT @unchecked]), @@ -590,7 +588,7 @@ trait GraphLike[N, E <: Edge[N], +CC[X, Y <: Edge[X]] <: GraphLike[X, Y, CC] wit ) => val newSources = sources.flatMap(nMap) val newTargets = targets.flatMap(nMap) - builder ++= (edges = fDiHyperEdge(e, newSources, newTargets)) + builder addEdges fDiHyperEdge(e, newSources, newTargets) } builder.result } diff --git a/core/src/main/scala/scalax/collection/mutable/Growable.scala b/core/src/main/scala/scalax/collection/mutable/Growable.scala index 73fd02e8..7f1f31b6 100644 --- a/core/src/main/scala/scalax/collection/mutable/Growable.scala +++ b/core/src/main/scala/scalax/collection/mutable/Growable.scala @@ -39,7 +39,6 @@ trait Growable[-N, -E <: Edge[N @uV]] { } /** Adds all elements produced by `outer` to this graph. - * For a graph see also `unionInPlace`. */ def addAll(xs: Iterable[OuterElem[N, E]]): this.type = { xs foreach addOuter; this } @@ -47,11 +46,18 @@ trait Growable[-N, -E <: Edge[N @uV]] { def ++=(xs: Iterable[OuterElem[N, E]]): this.type = { xs foreach addOuter; this } /** Adds all passed nodes and edges to this graph. - * For a mutable Graph see also `unionInPlace`. */ - def ++=(nodes: Iterable[N] = Nil, edges: Iterable[E @uV] = Nil): this.type = { + def addAll(nodes: Iterable[N], edges: Iterable[E @uV]): this.type = { nodes foreach addOne edges foreach += this } + + /** Adds all `nodes` to this graph. + */ + def addNodes(nodes: Iterable[N]): this.type = { nodes foreach addOne; this } + + /** Adds all `edges` to this graph. Nodes being ends of `edges` are also added if not yet present. + */ + def addEdges(edges: Iterable[E]): this.type = { edges foreach +=; this } } diff --git a/core/src/test/scala/scalax/collection/EditingSpec.scala b/core/src/test/scala/scalax/collection/EditingSpec.scala index 423ed917..0ba47541 100644 --- a/core/src/test/scala/scalax/collection/EditingSpec.scala +++ b/core/src/test/scala/scalax/collection/EditingSpec.scala @@ -159,7 +159,7 @@ private class EditingMutable extends RefSpec with Matchers { n1.outNeighbors should be(Set(3)) n1 findOutgoingTo n1 should be(None) - g ++= (edges = List(oneOne, oneTwo)) // Graph(oneOne, oneTwo, one~>3) + g addEdges List(oneOne, oneTwo) // Graph(oneOne, oneTwo, one~>3) n1.diSuccessors should be(Set(one, two, 3)) n1.outNeighbors should be(Set(two, 3)) n1 findOutgoingTo n1 should be(Some(e11)) @@ -167,7 +167,7 @@ private class EditingMutable extends RefSpec with Matchers { def `serve ++=, unionInPlace`: Unit = { val (gBefore, gAfter) = (Graph(1, 2 ~ 3), Graph(0, 1 ~ 2, 2 ~ 3)) - (gBefore ++= (0 :: Nil, List(1 ~ 2, 2 ~ 3))) should equal(gAfter) + (gBefore addAll (0 :: Nil, List(1 ~ 2, 2 ~ 3))) should equal(gAfter) (gBefore |= Graph(0, 1 ~ 2)) should equal(gAfter) (gBefore |= Graph[Int, UnDiEdge](0) |= Graph(1 ~ 2)) should equal(gAfter) } @@ -423,8 +423,8 @@ private class Editing[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E, g filter (_ < 2) should be(factory(1)) g filter (_ < 2) should be(factory(1)) g filter (_ >= 2) should be(factory(2 ~> 3, 5)) - g filter (edgeP = _.node1.outer == 2) should be(factory(1, 5, 2 ~> 3)) - g filter (nodeP = _ <= 3, edgeP = _ contains 2) should be(factory(1, 2 ~> 3)) + g.filter(edgeP = _.node1.outer == 2) should be(factory(1, 5, 2 ~> 3)) + g.filter(nodeP = _ <= 3, edgeP = _ contains 2) should be(factory(1, 2 ~> 3)) } def `match `: Unit = { From 0d18bae666a1d7a5aac1543686160e28f4ab14ef Mon Sep 17 00:00:00 2001 From: Peter Empen Date: Sun, 22 Jun 2025 14:24:43 +0200 Subject: [PATCH 28/34] Scala 3.3.5 --- .github/workflows/ci.yml | 2 +- build.sbt | 2 +- core/src/main/scala/scalax/collection/GraphTraversal.scala | 2 -- .../scala/scalax/collection/immutable/AdjacencyListBase.scala | 1 - project/Version.scala | 4 +--- 5 files changed, 3 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c1f74e70..80f2e6b7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,7 +5,7 @@ jobs: strategy: fail-fast: false matrix: - scala: [ '2.13.15', '3.3.5' ] + scala: [ '2.13.16', '3.3.5' ] project: [ core, dot, json ] exclude: - scala: '3.3.5' diff --git a/build.sbt b/build.sbt index ea96e671..82aec67f 100644 --- a/build.sbt +++ b/build.sbt @@ -73,7 +73,7 @@ val unusedImports = "-Wunused:imports" lazy val defaultSettings_cross = Defaults.coreDefaultSettings ++ Seq( scalaVersion := Version.compiler_2_13, - crossScalaVersions := Seq(Version.compiler_2_13, Version.compiler_3_fallback) + crossScalaVersions := Seq(Version.compiler_2_13, Version.compiler_3) ) ++ defaultSettings ++ defaultTestLibSettings diff --git a/core/src/main/scala/scalax/collection/GraphTraversal.scala b/core/src/main/scala/scalax/collection/GraphTraversal.scala index e4cae2f2..c69e967e 100644 --- a/core/src/main/scala/scalax/collection/GraphTraversal.scala +++ b/core/src/main/scala/scalax/collection/GraphTraversal.scala @@ -885,8 +885,6 @@ trait GraphTraversal[N, E <: Edge[N]] extends GraphBase[N, E, GraphTraversal] { abstract protected class TraverserMethods[A, +CC <: TraverserMethods[A, CC]] extends FluentProperties[CC] { this: CC with Properties => - def root: NodeT - protected def nodeVisitor[U](f: A => U): (NodeT) => U protected def edgeVisitor[U](f: A => U): (EdgeT) => U diff --git a/core/src/main/scala/scalax/collection/immutable/AdjacencyListBase.scala b/core/src/main/scala/scalax/collection/immutable/AdjacencyListBase.scala index b49f8525..79b090c9 100644 --- a/core/src/main/scala/scalax/collection/immutable/AdjacencyListBase.scala +++ b/core/src/main/scala/scalax/collection/immutable/AdjacencyListBase.scala @@ -9,7 +9,6 @@ import scala.collection.mutable.{ArrayBuffer, ExtHashSet} import scala.util.Random import scalax.collection.generic.Edge -import scalax.collection.AnyGraph import scalax.collection.mutable.{ArraySet, EqHashMap, EqHashSet} import scalax.collection.config.{AdjacencyListArrayConfig, GraphConfig} diff --git a/project/Version.scala b/project/Version.scala index e3235238..c0377446 100644 --- a/project/Version.scala +++ b/project/Version.scala @@ -1,8 +1,6 @@ object Version { val compiler_2_13 = "2.13.16" - val compiler_3 = "3.5.1" - - val compiler_3_fallback = "3.3.0" + val compiler_3 = "3.3.5" private val isSnapshot = false private def snapshot = if (isSnapshot) "-SNAPSHOT" else "" From 85856ec4bdde06394cdd8c840042dd729aaf2482 Mon Sep 17 00:00:00 2001 From: Peter Empen Date: Sun, 22 Jun 2025 21:03:35 +0200 Subject: [PATCH 29/34] json 2.0.3 --- project/Version.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Version.scala b/project/Version.scala index c0377446..7e291bed 100644 --- a/project/Version.scala +++ b/project/Version.scala @@ -12,5 +12,5 @@ object Version { val highest = version(3) val core = version(3) val dot = version(0) - val json = version(0) + val json = version(3) } From d3e1e9c1337d14e0859ee5f4bef7409fcbb3ef30 Mon Sep 17 00:00:00 2001 From: Peter Empen Date: Tue, 24 Jun 2025 13:57:00 +0200 Subject: [PATCH 30/34] drop module coreTestScala3 --- .github/workflows/ci.yml | 14 - build.sbt | 15 - .../src/test/scala/WastelandSpec.scala | 89 --- .../src/test/scala/demo/EdgeADTDemo.scala | 72 --- .../src/test/scala/demo/EditingDemoSpec.scala | 147 ----- .../test/scala/demo/EnrichingDemoSpec.scala | 43 -- .../src/test/scala/demo/FlightDemoSpec.scala | 97 --- .../src/test/scala/demo/GraphTestDemo.scala | 181 ------ .../src/test/scala/demo/HyperADTDemo.scala | 155 ----- .../test/scala/demo/InspectingDemoSpec.scala | 146 ----- .../scala/demo/TransformingDemoSpec.scala | 131 ----- .../test/scala/demo/TraversingDemoSpec.scala | 206 ------- .../src/test/scala/demo/subset.scala | 88 --- .../collection/mutable/ExtHashSetSpec.scala | 154 ----- .../scalax/collection/ConfigWrapper.scala | 24 - .../scalax/collection/ConnectivitySpec.scala | 181 ------ .../scala/scalax/collection/CycleSpec.scala | 393 ------------- .../test/scala/scalax/collection/Data.scala | 114 ---- .../scala/scalax/collection/DegreeSpec.scala | 191 ------ .../scalax/collection/EditingHyperSpec.scala | 175 ------ .../collection/EditingLabeledHyperSpec.scala | 550 ------------------ .../collection/EditingLabeledSpec.scala | 308 ---------- .../scala/scalax/collection/EditingSpec.scala | 462 --------------- .../collection/EditingTypedHyperSpec.scala | 5 - .../scalax/collection/EditingTypedSpec.scala | 140 ----- .../scalax/collection/EqualityHyperSpec.scala | 75 --- .../scalax/collection/EqualitySpec.scala | 73 --- .../scala/scalax/collection/IntelliJ.scala | 13 - .../scalax/collection/MappingHyperSpec.scala | 143 ----- .../scala/scalax/collection/MappingSpec.scala | 123 ---- .../collection/MappingTypedHyperSpec.scala | 18 - .../scalax/collection/MappingTypedSpec.scala | 157 ----- .../scalax/collection/PathBuilderSpec.scala | 60 -- .../scalax/collection/SerializableSpec.scala | 378 ------------ .../scala/scalax/collection/SetOpsSpec.scala | 100 ---- .../scala/scalax/collection/StateSpec.scala | 127 ---- .../collection/TopologicalSortSpec.scala | 273 --------- .../scalax/collection/TraversalSpec.scala | 382 ------------ .../collection/generator/GraphGenSpec.scala | 82 --- .../generator/RandomGraphSpec.scala | 122 ---- .../collection/labeled/TraversalSpec.scala | 299 ---------- .../collection/labeled/aviation/Airport.scala | 5 - .../collection/labeled/aviation/Flight.scala | 47 -- .../collection/labeled/aviation/package.scala | 46 -- .../collection/mutable/ArraySetSpec.scala | 160 ----- .../collection/visualization/Visualizer.scala | 54 -- .../scala/scalax/time/MicroBenchmark.scala | 135 ----- .../scalax/time/MicroBenchmarkTest.scala | 73 --- 48 files changed, 7026 deletions(-) delete mode 100644 coreTestScala3/src/test/scala/WastelandSpec.scala delete mode 100644 coreTestScala3/src/test/scala/demo/EdgeADTDemo.scala delete mode 100644 coreTestScala3/src/test/scala/demo/EditingDemoSpec.scala delete mode 100644 coreTestScala3/src/test/scala/demo/EnrichingDemoSpec.scala delete mode 100644 coreTestScala3/src/test/scala/demo/FlightDemoSpec.scala delete mode 100644 coreTestScala3/src/test/scala/demo/GraphTestDemo.scala delete mode 100644 coreTestScala3/src/test/scala/demo/HyperADTDemo.scala delete mode 100644 coreTestScala3/src/test/scala/demo/InspectingDemoSpec.scala delete mode 100644 coreTestScala3/src/test/scala/demo/TransformingDemoSpec.scala delete mode 100644 coreTestScala3/src/test/scala/demo/TraversingDemoSpec.scala delete mode 100644 coreTestScala3/src/test/scala/demo/subset.scala delete mode 100644 coreTestScala3/src/test/scala/scala/collection/mutable/ExtHashSetSpec.scala delete mode 100644 coreTestScala3/src/test/scala/scalax/collection/ConfigWrapper.scala delete mode 100644 coreTestScala3/src/test/scala/scalax/collection/ConnectivitySpec.scala delete mode 100644 coreTestScala3/src/test/scala/scalax/collection/CycleSpec.scala delete mode 100644 coreTestScala3/src/test/scala/scalax/collection/Data.scala delete mode 100644 coreTestScala3/src/test/scala/scalax/collection/DegreeSpec.scala delete mode 100644 coreTestScala3/src/test/scala/scalax/collection/EditingHyperSpec.scala delete mode 100644 coreTestScala3/src/test/scala/scalax/collection/EditingLabeledHyperSpec.scala delete mode 100644 coreTestScala3/src/test/scala/scalax/collection/EditingLabeledSpec.scala delete mode 100644 coreTestScala3/src/test/scala/scalax/collection/EditingSpec.scala delete mode 100644 coreTestScala3/src/test/scala/scalax/collection/EditingTypedHyperSpec.scala delete mode 100644 coreTestScala3/src/test/scala/scalax/collection/EditingTypedSpec.scala delete mode 100644 coreTestScala3/src/test/scala/scalax/collection/EqualityHyperSpec.scala delete mode 100644 coreTestScala3/src/test/scala/scalax/collection/EqualitySpec.scala delete mode 100644 coreTestScala3/src/test/scala/scalax/collection/IntelliJ.scala delete mode 100644 coreTestScala3/src/test/scala/scalax/collection/MappingHyperSpec.scala delete mode 100644 coreTestScala3/src/test/scala/scalax/collection/MappingSpec.scala delete mode 100644 coreTestScala3/src/test/scala/scalax/collection/MappingTypedHyperSpec.scala delete mode 100644 coreTestScala3/src/test/scala/scalax/collection/MappingTypedSpec.scala delete mode 100644 coreTestScala3/src/test/scala/scalax/collection/PathBuilderSpec.scala delete mode 100644 coreTestScala3/src/test/scala/scalax/collection/SerializableSpec.scala delete mode 100644 coreTestScala3/src/test/scala/scalax/collection/SetOpsSpec.scala delete mode 100644 coreTestScala3/src/test/scala/scalax/collection/StateSpec.scala delete mode 100644 coreTestScala3/src/test/scala/scalax/collection/TopologicalSortSpec.scala delete mode 100644 coreTestScala3/src/test/scala/scalax/collection/TraversalSpec.scala delete mode 100644 coreTestScala3/src/test/scala/scalax/collection/generator/GraphGenSpec.scala delete mode 100644 coreTestScala3/src/test/scala/scalax/collection/generator/RandomGraphSpec.scala delete mode 100644 coreTestScala3/src/test/scala/scalax/collection/labeled/TraversalSpec.scala delete mode 100644 coreTestScala3/src/test/scala/scalax/collection/labeled/aviation/Airport.scala delete mode 100644 coreTestScala3/src/test/scala/scalax/collection/labeled/aviation/Flight.scala delete mode 100644 coreTestScala3/src/test/scala/scalax/collection/labeled/aviation/package.scala delete mode 100644 coreTestScala3/src/test/scala/scalax/collection/mutable/ArraySetSpec.scala delete mode 100644 coreTestScala3/src/test/scala/scalax/collection/visualization/Visualizer.scala delete mode 100644 coreTestScala3/src/test/scala/scalax/time/MicroBenchmark.scala delete mode 100644 coreTestScala3/src/test/scala/scalax/time/MicroBenchmarkTest.scala diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 80f2e6b7..e626d91b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,17 +23,3 @@ jobs: - name: Test run: | sbt ++${{ matrix.scala }} ${{ matrix.project }}/test scalafmtCheckAll - others: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: JDK with SBT caching - uses: actions/setup-java@v4 - with: - java-version: 17 - distribution: temurin - cache: sbt - - uses: sbt/setup-sbt@v1 - - name: Scala 3 Rewrite - run: | - sbt "project coreTestScala3" test Test/scalafmt diff --git a/build.sbt b/build.sbt index 82aec67f..d700e959 100644 --- a/build.sbt +++ b/build.sbt @@ -29,21 +29,6 @@ lazy val core = crossProject(JSPlatform, JVMPlatform, NativePlatform) ) ) -lazy val coreTestScala3 = project - .in(file("coreTestScala3")) - .dependsOn(core.jvm) - .settings( - defaultSettings_3 ++ Seq( - libraryDependencies ++= Seq( - "org.scalatest" %% "scalatest" % "3.2.19" % Test, - "org.scalatestplus" %% "scalacheck-1-18" % "3.2.19.0" % Test exclude ( - "org.scalacheck", - "scalacheck_3" - ) - ) - ) - ) - lazy val dot = crossProject(JSPlatform, JVMPlatform, NativePlatform) .withoutSuffixFor(JVMPlatform) .crossType(CrossType.Pure) diff --git a/coreTestScala3/src/test/scala/WastelandSpec.scala b/coreTestScala3/src/test/scala/WastelandSpec.scala deleted file mode 100644 index 9f74519c..00000000 --- a/coreTestScala3/src/test/scala/WastelandSpec.scala +++ /dev/null @@ -1,89 +0,0 @@ -import scala.annotation.tailrec - -import scalax.collection.OneOrMore -import scalax.collection.generic.AbstractDiHyperEdge -import scalax.collection.immutable.Graph - -import org.scalatest.matchers.should.Matchers -import org.scalatest.refspec.RefSpec - -/** Directed hyperedge to represent nodes of the network https://adventofcode.com/2023/day/8. - * Each line of the input file like `AAA = (BBB, CCC)` corresponds to one instance. - */ -case class Fork(source: String, leftTarget: String, rightTarget: String) - extends AbstractDiHyperEdge[String]( - sources = OneOrMore.one(source), - targets = OneOrMore(leftTarget, rightTarget) - ) - -object Fork: - def hook(source: String) = Fork(source, source, source) - -type Wasteland = Graph[String, Fork] - -enum Direction: - case Left, Right - -/** The claim is not to be complete but to demonstrate the use-case specific graph walk. - */ -def solve(wasteland: Wasteland, startingFork: String, instructions: Iterable[Direction]): Either[String, Int] = - @tailrec def traverse( - current: wasteland.NodeT, - instructions: Iterator[Direction], - count: Int - ): Option[Int] = - /** Uses inner node `fork` to navigate to the next node. Thus we can avoid repeated node lookup. - */ - def target(fork: wasteland.NodeT, direction: Direction): Option[wasteland.NodeT] = - fork.outgoing.headOption.map { e => - import Direction._ - direction match - case Left => e.targets(0) - case Right => e.targets(1) - } - - instructions.nextOption match - case Some(instruction) => - target(current, instruction) match - case Some(fork) => traverse(fork, instructions, count + 1) - case None => None - case None => Some(count) - - wasteland.find(startingFork) match - case Some(root) => - traverse(root, instructions.iterator, 0) match - case Some(count) => Right(count) - case None => Left("Node without successors.") - case None => - Left(s"Fork '$startingFork' does not exist in wasteland.") - -final class WastelandSpec extends RefSpec with Matchers: - - /** @see diagram https://github.com/scala-graph/scala-graph/issues/300#issuecomment-1854583980 - */ - val wasteland: Wasteland = Graph.from( - nodes = Nil, - edges = List( - Fork("A", "B", "C"), - Fork("B", "D", "E"), - Fork.hook("D"), - Fork.hook("E"), - Fork("C", "Z", "G"), - Fork.hook("Z"), - Fork.hook("G") - ) - ) - - def instructions(chars: String): Iterable[Direction] = - import Direction._ - chars.flatMap { - case 'L' | 'l' => Left :: Nil - case 'R' | 'r' => Right :: Nil - case _ => Nil - } - - def `start at A`: Unit = - solve(wasteland, "A", instructions("LR")) shouldBe Right(2) - - def `pass some non-existing fork`: Unit = - solve(wasteland, "/", instructions("LR")).isLeft shouldBe true diff --git a/coreTestScala3/src/test/scala/demo/EdgeADTDemo.scala b/coreTestScala3/src/test/scala/demo/EdgeADTDemo.scala deleted file mode 100644 index 32f1c072..00000000 --- a/coreTestScala3/src/test/scala/demo/EdgeADTDemo.scala +++ /dev/null @@ -1,72 +0,0 @@ -package demo - -import scalax.collection.OneOrMore -import scalax.collection.OneOrMore.one -import scalax.collection.generic.{AbstractDiEdge, AbstractUnDiEdge, AnyEdge, MultiEdge} -import scalax.collection.immutable.{Graph, TypedGraphFactory} - -/** Demonstrates a Graph with nodes representing Persons and edges representing Relations between Persons. - */ -object EdgeADTDemo { - // node type, which could also be an ADT - case class Person(name: String) - - // edge ADT - sealed trait Relation extends AnyEdge[Person] - - /** `abstract class`es facilitating more concise case class definitions. - * `ExtendedKey` is needed to allow for multiple Relations between the same Persons. - */ - sealed abstract protected class DiRelation(from: Person, to: Person, discriminator: AnyRef) - extends AbstractDiEdge(from, to) - with MultiEdge - with Relation { - def extendKeyBy: OneOrMore[Any] = one(discriminator) - } - sealed abstract protected class UnDiRelation(person_1: Person, person_2: Person, discriminator: AnyRef) - extends AbstractUnDiEdge(person_1, person_2) - with MultiEdge - with Relation { - def extendKeyBy: OneOrMore[Any] = one(discriminator) - } - - final case class Parent(offspring: Person, parent: Person) extends DiRelation(offspring, parent, Parent) - final case class Siblings(one: Person, another: Person) extends UnDiRelation(one, another, Siblings) - final case class Friends(one: Person, another: Person) extends UnDiRelation(one, another, Friends) - - val kate = Person("Kate") - val john = Person("John") - val mike = Person("Mike") - - type People = Graph[Person, Relation] - object People extends TypedGraphFactory[Person, Relation] - - // create empty Graph - val empty = People.empty - - // populate Graph by Iterable[Relation] - val people: People = People.from( - List( - Parent(kate, mike), - Friends(kate, john), - Siblings(kate, john) - ) - ) - - val edgesInWords = - people.edges.outerIterator map { - case Friends(Person(name_1), Person(name_2)) => s"$name_1 and $name_2 are friends" - case Siblings(Person(name_1), Person(name_2)) => s"$name_1 and $name_2 are siblings" - case Parent(Person(offspring), Person(parent)) => s"$parent is parent of $offspring" - } - - // populate Graph by Iterable[People] and Iterable[Relation] - val p1 = People.from( - nodes = List(kate, john, mike), - edges = List(Parent(kate, mike), Friends(kate, john)) - ) - - // populate Graph by repeated Peoples and Relations - import People.OuterImplicits.* - val p2 = People(kate, Parent(kate, mike), Friends(kate, john), Siblings(kate, john)) -} diff --git a/coreTestScala3/src/test/scala/demo/EditingDemoSpec.scala b/coreTestScala3/src/test/scala/demo/EditingDemoSpec.scala deleted file mode 100644 index 356ff279..00000000 --- a/coreTestScala3/src/test/scala/demo/EditingDemoSpec.scala +++ /dev/null @@ -1,147 +0,0 @@ -package demo - -import scala.collection.SortedSet - -import org.scalatest.matchers.should.Matchers -import org.scalatest.refspec.RefSpec - -import scalax.collection.OuterImplicits.* -import scalax.collection.edges.* -import scalax.collection.generic.* -import scalax.collection.{immutable, mutable} - -/** Includes the examples given on [[http://www.scala-graph.org/guides/core-operations.html - * Graph Operations]]. - */ -final class EditingDemoSpec extends RefSpec with Matchers { - - object `demonstrating ` { - - def `Add and Subtract Elements`(): Unit = { - import immutable.Graph - val g = Graph(1, 2 ~ 3) // Graph(NodeSet(1, 2, 3), EdgeSet(2 ~ 3)) - g + 1 shouldBe g - g + 0 shouldBe Graph(0, 1, 2 ~ 3) - "g + 1.2" shouldNot compile // error: overloaded method... - g + 0 ~ 1 shouldBe Graph(0, 1, 0 ~ 1, 2 ~ 3) - g ++ List(1 ~ 2, 2 ~ 3) shouldBe Graph(1 ~ 2, 2 ~ 3) - g ++ (0 :: Nil, 1 ~ 2 :: 2 ~ 3 :: Nil) shouldBe Graph(0, 1, 1 ~ 2, 2 ~ 3) - g - 0 shouldBe g - g - 1 shouldBe Graph(2 ~ 3) - g - 2 shouldBe Graph(1, 3) - g - 2 ~ 3 shouldBe Graph(1, 2, 3) - g -- (2 :: Nil, 3 ~ 3 :: Nil) shouldBe Graph(1, 3) - } - - def `Add Elements to mutable.Graph`(): Unit = { - import mutable.Graph - (Graph(1, 2 ~ 3) += 0) shouldBe Graph(0, 1, 2 ~ 3) - (Graph[Int, AnyEdge](1, 2 ~ 3) += 3 ~> 1) shouldBe Graph[Int, AnyEdge](1, 2 ~ 3, 3 ~> 1) - } - - def `equality `: Unit = { - val g = immutable.Graph(1 ~ 2) - (g get 1).outer shouldBe 1 - g get 1 ~ 2 shouldBe 2 ~ 1 - g get 1 ~ 2 eq 2 ~ 1 shouldBe false - g get 1 ~ 2 should not be 2 ~ 2 - } - - def `union, diff, intersect `: Unit = { - import immutable.Graph - val g = Graph(1 ~ 2, 2 ~ 3, 2 ~ 4, 3 ~ 5, 4 ~ 5) - val h = Graph(3 ~ 4, 3 ~ 5, 4 ~ 6, 5 ~ 6) - - g union h shouldBe Graph(1 ~ 2, 2 ~ 3, 2 ~ 4, 3 ~ 5, 4 ~ 5, 3 ~ 4, 4 ~ 6, 5 ~ 6) - g diff h shouldBe Graph(1 ~ 2) - g intersect h shouldBe Graph(4, 3 ~ 5) - g & h shouldBe Graph(4, 3 ~ 5) - } - - def `endpoints `: Unit = { - val uE = 3 ~ 4 // UnDiEdge[Int] - - uE.node1 * uE.node2 shouldBe 12 - uE.ends.iterator.product shouldBe 12 - (uE match { - case n ~ m => n * m - }) shouldBe 12 - - val dE = 1 ~> 2 // DiEdge[Int] - dE.source - dE.target shouldBe -1 - uE.arity == dE.arity shouldBe true - (dE match { - case s ~> t => s - t - }) shouldBe -1 - - import scalax.collection.hyperedges.* - val hE = 1 ~~ 2 ~~ 11 ~~ 12 // HyperEdge[Int] - hE.node(hE.arity - 1) shouldBe 12 - hE.ends.iterator.sum shouldBe 26 - } - - def `neighbors `: Unit = { - import immutable.Graph - val g = Graph[Int, AnyEdge](0, 1 ~ 3, 3 ~> 2) - - def n(outer: Int): g.NodeT = g get outer - - def e(outer: AnyEdge[Int]): g.EdgeT = g get outer - - n(0).diSuccessors shouldBe Set.empty[g.NodeT] - n(2).diSuccessors.isEmpty shouldBe true - n(3).diSuccessors shouldBe Set(n(1), n(2)) - n(3).diPredecessors shouldBe Set(n(1)) - n(2).incoming shouldBe Set(e(3 ~> 2)) - n(3) findOutgoingTo n(2) shouldBe Some(e(3 ~> 2)) - } - - def `querying `: Unit = { - import immutable.Graph - val g = Graph[Int, AnyEdge](2 ~> 3, 3 ~ 1, 5) - - def n(outer: Int): g.NodeT = g get outer - - g.nodes filter (_ > 2) shouldBe Set(n(5), n(3)) - g.nodes filter (_.degree > 1) shouldBe Set(n(3)) - g.edges filter (_ contains 4) shouldBe Symbol("empty") - } - - def `measuring `: Unit = { - import immutable.Graph - import scalax.collection.edges.labeled.* - val g = Graph[Int, AnyEdge]( - 1 ~ 2 % 4, - 2 ~ 3 % 2, - 1 ~> 3 % 5, - 1 ~ 5 % 3, - 3 ~ 5 % 2, - 3 ~ 4 % 1, - 4 ~> 4 % 1, - 4 ~> 5 % 0 - ) - g.order shouldBe 5 - g.size shouldBe 8 - g.totalDegree shouldBe 16 - g.degreeSet shouldBe SortedSet(4, 3, 2) - g.degreeNodeSeq(g.InDegree) shouldBe List((4, 3), (3, 5), (2, 1), (2, 2), (2, 4)) - g.degreeNodesMap should contain only (2 -> Set(2), 3 -> Set(5, 1), 4 -> Set(3, 4)) - g.degreeNodesMap(degreeFilter = _ > 3) should contain only (4 -> Set(3, 4)) - } - - def `classifying `: Unit = { - import immutable.Graph - val g = Graph(1, 2 ~> 3) - g.isConnected shouldBe false - (g + 2 ~> 1).isConnected shouldBe true - (g get 2).findConnected(_.outer == 3) shouldBe Some(3) - g.isCyclic shouldBe false - (g + 3 ~> 2).isCyclic shouldBe true - g.isComplete shouldBe false - (g ++ List(1 ~> 2, 1 ~> 3, 2 ~> 1, 3 ~> 1, 3 ~> 2)).isComplete shouldBe true - g.isDirected shouldBe true - g.isHyper shouldBe false - g.isMulti shouldBe false - } - } -} diff --git a/coreTestScala3/src/test/scala/demo/EnrichingDemoSpec.scala b/coreTestScala3/src/test/scala/demo/EnrichingDemoSpec.scala deleted file mode 100644 index bdd622e0..00000000 --- a/coreTestScala3/src/test/scala/demo/EnrichingDemoSpec.scala +++ /dev/null @@ -1,43 +0,0 @@ -package demo - -import org.scalatest.matchers.should.Matchers -import org.scalatest.refspec.RefSpec - -import scalax.collection.OuterImplicits.* -import scalax.collection.edges.* -import scalax.collection.generic.* -import scalax.collection.immutable.Graph - -class EnrichingDemoSpec extends RefSpec with Matchers { - - object `demonstrating how to` { - - def `enrich graphs`: Unit = { - implicit class ExtGraph[N, E <: Edge[N]](protected val g: Graph[N, E]) { - def foo: String = "bar" - } - Graph(1 ~ 2).foo shouldBe "bar" - } - - def `enrich directed graphs`: Unit = { - implicit class ExtGraph[N, E <: AnyDiEdge[N]](protected val g: Graph[N, E]) { - def foo: String = "bar" - } - Graph(1 ~> 2).foo shouldBe "bar" - "Graph(1 ~ 2).foo" shouldNot typeCheck - } - - def `enrich inner nodes`: Unit = { - // works for any Graph due to projection type - implicit class ExtInnerNode[N, E <: Edge[N]](node: Graph[N, E]#NodeT) { - def outOverInDegree: Int = node.outDegree - node.inDegree - } - - Graph(1 ~> 2).nodes foreach { - case n if n.outer == 1 => n.outOverInDegree shouldBe 1 - case n if n.outer == 2 => n.outOverInDegree shouldBe -1 - case _ => fail() - } - } - } -} diff --git a/coreTestScala3/src/test/scala/demo/FlightDemoSpec.scala b/coreTestScala3/src/test/scala/demo/FlightDemoSpec.scala deleted file mode 100644 index f1a1d3d3..00000000 --- a/coreTestScala3/src/test/scala/demo/FlightDemoSpec.scala +++ /dev/null @@ -1,97 +0,0 @@ -package demo - -import java.time.DayOfWeek.* -import java.time.{DayOfWeek, LocalTime} - -import scala.concurrent.duration.* - -import org.scalatest.matchers.should.Matchers -import org.scalatest.refspec.RefSpec -import scalax.collection.edges.* - -class FlightDemoSpec extends RefSpec with Matchers { - - object `demonstrating how to` { - - def `work with the Flight typed edge`: Unit = { - import FlightDemoSpec.* - import scalax.collection.labeled.aviation.* - - def expectedStringRepresentation( - from: Airport, - to: Airport, - flightNo: String, - times: List[(DayOfWeek, LocalTime)], - duration: FiniteDuration - ) = - s"$from ~> $to :++ ($flightNo, $times, $duration)" - - /* look up a node and examine outgoing edges by using the prefix extractor - */ - g.find(hamburg).map { ham => - ham.diSuccessors shouldBe Set(amsterdam, london) - ham.outgoing.map { case g.InnerEdge(_, flight @ Flight(from, to, flightNo, times, duration)) => - flight.toString shouldBe expectedStringRepresentation(from, to, flightNo, times, duration) - } - } - - /* same but using the infix extractors `:~>` and `++:` that were added optionally - */ - g.find(hamburg).map { ham => - ham.diSuccessors shouldBe Set(amsterdam, london) - ham.outgoing.map { case g.InnerEdge(_, flight @ from :~> to ++: ((flightNo, times, duration))) => - flight.toString shouldBe expectedStringRepresentation(from, to, flightNo, times, duration) - } - } - } - } -} - -object FlightDemoSpec { - import scalax.collection.labeled.aviation.* - import scalax.collection.labeled.aviation.immutable.* - import scalax.collection.labeled.aviation.immutable.FlightGraph.OuterImplicits.* - - val amsterdam = Airport("AMS") - val hamburg = Airport("HAM") - val frankfurt = Airport("FRA") - val newYork = Airport("JFK") - val london = Airport("LHR") - val mexico = Airport("MEX") - - /* construct the Graph by supplying edges - */ - val g = FlightGraph( - hamburg ~> amsterdam :++ ( - "KL 1776", - List( - MONDAY -> LocalTime.of(17, 50), - SATURDAY -> LocalTime.of(17, 40) - ), - 50.minutes - ), - hamburg ~> london :++ ( - "BA 967", - List( - TUESDAY -> LocalTime.of(8, 20), - SATURDAY -> LocalTime.of(8, 20) - ), - 1.hour + 10.minutes - ), - london ~> newYork :++ ( - "UA 921", - List( - THURSDAY -> LocalTime.of(18, 0) - ), - 5.hours + 40.minutes - ), - newYork ~> mexico :++ ( - "VB 101", - List( - TUESDAY -> LocalTime.of(14, 10), - SUNDAY -> LocalTime.of(14, 20) - ), - 4.hours + 25.minutes - ) - ) -} diff --git a/coreTestScala3/src/test/scala/demo/GraphTestDemo.scala b/coreTestScala3/src/test/scala/demo/GraphTestDemo.scala deleted file mode 100644 index b3b94de9..00000000 --- a/coreTestScala3/src/test/scala/demo/GraphTestDemo.scala +++ /dev/null @@ -1,181 +0,0 @@ -package demo - -import scalax.collection.edges.* -import scalax.collection.mutable.Graph - -/** Includes the examples given on [[http://www.scala-graph.org/guides/test.html - * Test Utilities]]. - */ -object GraphTestDemo extends App { - - import scalax.collection.generator.* - - object PersonData { - val firstNames = Vector("Alen", "Alice", "Bob", "Jack", "Jim", "Joe", "Kate", "Leo", "Tim", "Tom") - val firstNamesSize = firstNames.size - - val surnames = Vector("Bell", "Brown", "Clark", "Cox", "King", "Lee", "Moore", "Ross", "Smith", "Wong") - val surnamesSize = surnames.size - - def order = firstNamesSize * surnamesSize / 10 - def degrees = new NodeDegreeRange(2, order / 2) - val maxYearOfBirth = 2010 - } - - object RG { - // working with random graph generators ----------------------------------------------- - - // obtaining generators for graphs with predefined metrics - val predefined = RandomGraph.tinyConnectedIntDi(Graph) - val tinyGraph = predefined.draw // Graph[Int,DiEdge] - - // setting individual graph metrics while keeping metrics constraints in mind - object sparse_1000_Int extends RandomGraph.IntFactory { - val order = 1000 - val nodeDegrees = NodeDegreeRange(1, 10) - override def connected = false - } - val randomSparse = RandomGraph.fromMetrics[Int, UnDiEdge[Int], Graph](Graph, sparse_1000_Int, Set(UnDiEdge)) - val sparseGraph = randomSparse.draw // Graph[Int,UnDiEdge] - - // TODO obtain generators for graphs with typed edges - - case class Person(name: String, yearOfBirth: Int) - object Person { - import PersonData.* - private val r = new scala.util.Random - - def drawName: String = { - def drawFirstName: String = firstNames(r.nextInt(firstNamesSize)) - def drawSurame: String = surnames(r.nextInt(surnamesSize)) - - s"$drawFirstName, $drawSurame" - } - - def drawYearOfBirth = maxYearOfBirth - r.nextInt(100) - } - - val randomMixedGraph = - RandomGraph.fromMetrics[Person, UnDiEdge[Person], Graph]( - Graph, - new RandomGraph.Metrics[Person] { - val order = PersonData.order - val nodeDegrees = PersonData.degrees - def nodeGen: Person = Person(Person.drawName, Person.drawYearOfBirth) - }, - Set(UnDiEdge) // TODO LDiEdge - ) - val mixedGraph = randomMixedGraph.draw - /* - println(mixedGraph) - Graph( - Person(Alice, Smith,1967), - Person(Kate, Ross,1921), - Person(Leo, Bell,2008), - Person(Leo, Smith,1983), - ..., - Person(Alice, Smith,1967)~>Person(Kate, Ross,1921) 'C, - Person(Leo, Bell,2008)~Person(Leo, Smith,1983), - ... - ) - */ - } - - object GG { - import org.scalacheck.{Arbitrary, Gen} - import Arbitrary.arbitrary - import org.scalacheck.Prop.forAll - - // working with org.scalacheck.Arbitrary graphs --------------------------------------- - - // obtaining Arbitrary instances for graphs with predefined metrics - type IntDiGraph = Graph[Int, DiEdge[Int]] - implicit val arbitraryTinyGraph: Arbitrary[Graph[Int, DiEdge[Int]]] = GraphGen.tinyConnectedIntDi[Graph](Graph) - - val properTiny = forAll(arbitrary[IntDiGraph]) { (g: IntDiGraph) => - g.order == GraphGen.TinyInt.order - } - properTiny.check() - - // setting individual graph metrics for Arbitrary instances - // while keeping metrics constraints in mind - object Sparse_1000_Int extends GraphGen.Metrics[Int] { - val order = 1000 - val nodeDegrees = NodeDegreeRange(1, 10) - def nodeGen: Gen[Int] = Gen.choose(0, 10 * order) - override def connected = false - } - - type IntUnDiGraph = Graph[Int, UnDiEdge[Int]] - implicit val arbitrarySparseGraph: Arbitrary[Graph[Int, UnDiEdge[Int]]] = Arbitrary { - GraphGen.fromMetrics[Int, UnDiEdge[Int], Graph](Graph, Sparse_1000_Int, Set(UnDiEdge)).apply - } - - val properSparse = forAll(arbitrary[IntUnDiGraph]) { (g: IntUnDiGraph) => - g.order == Sparse_1000_Int.order - } - properSparse.check() - - // TODO obtain Arbitrary instances to generate graphs with typed edges - - case class Person(name: String, yearOfBirth: Int) - object Person { - import PersonData.* - - def firstNameGen: Gen[String] = Gen.oneOf(firstNames) - def surameGen: Gen[String] = Gen.oneOf(surnames) - - def nameGen: Gen[String] = Gen.resultOf((firstName: String, surname: String) => s"$firstName, $surname")( - Arbitrary(firstNameGen), - Arbitrary(surameGen) - ) - - def yearOfBirthGen: Gen[Int] = Gen.choose(maxYearOfBirth - 100, maxYearOfBirth) - } - - object MixedMetrics extends GraphGen.Metrics[Person] { - val order = PersonData.order - val nodeDegrees = PersonData.degrees - def nodeGen: Gen[Person] = Gen.resultOf((name: String, year: Int) => Person(name, year))( - Arbitrary(Person.nameGen), - Arbitrary(Person.yearOfBirthGen) - ) - } - - type Mixed = Graph[Person, UnDiEdge[Person]] - implicit val arbitraryMixedGraph: Arbitrary[Graph[Person, UnDiEdge[Person]]] = Arbitrary { - GraphGen - .fromMetrics[Person, UnDiEdge[Person], Graph](Graph, MixedMetrics, Set(UnDiEdge /*, TODO LDiEdge*/ )) - .apply - } - - val properMixedGraph = forAll(arbitrary[Mixed]) { (g: Mixed) => - g.order == MixedMetrics.order - } - properMixedGraph.check() - // println(arbitraryMixedGraph.arbitrary.sample) - - // Integrating with ScalaTest, limiting the minimum # of successful tests - import org.scalatest.matchers.should.Matchers - import org.scalatest.refspec.RefSpec - import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks - - class TGraphGenTest extends RefSpec with Matchers with ScalaCheckPropertyChecks { - - implicit val config: PropertyCheckConfiguration = - PropertyCheckConfiguration(minSuccessful = 5, maxDiscardedFactor = 1.0) - - object `generated Tiny graph` { - implicit val arbitraryTinyGraph: Arbitrary[Graph[Int, DiEdge[Int]]] = GraphGen.tinyConnectedIntDi[Graph](Graph) - - def `should conform to tiny metrics`: Unit = - forAll(arbitrary[IntDiGraph]) { (g: IntDiGraph) => - g.order should equal(GraphGen.TinyInt.order) - } - } - } - } - - RG - GG -} diff --git a/coreTestScala3/src/test/scala/demo/HyperADTDemo.scala b/coreTestScala3/src/test/scala/demo/HyperADTDemo.scala deleted file mode 100644 index 160d32e8..00000000 --- a/coreTestScala3/src/test/scala/demo/HyperADTDemo.scala +++ /dev/null @@ -1,155 +0,0 @@ -package demo - -import scala.util.chaining.* - -import scalax.collection.OneOrMore -import scalax.collection.OneOrMore.one -import scalax.collection.generic.* -import scalax.collection.immutable.{Graph, TypedGraphFactory} - -/** Demonstrates a largely typesafe Hypergraph for SQL schemas with - * - `Node`'s representing tables and columns and - * - `Connection`s representing - * - table-column relationships and - * - foreign keys. - */ -object HyperADTDemo extends App { - sealed trait DataType - case object StringType extends DataType - case object NumericType extends DataType - - sealed trait Node - case class Table(name: String) extends Node - case class Column(table: Table, name: String, `type`: DataType) extends Node - - sealed trait Connection extends AnyDiHyperEdge[Node] - - /** Ensures by type safety that - * - columns cannot be connected to a table multiple times - * - primary key columns are an ordered subset of the table's columns - * - foreign key columns are an ordered subset of the table's columns - * - foreign keys are connected with the primary key of the child table - * TODO try to also ensure that - * - only columns of the same table definition may be added to indexes/foreign keys - * - column names are unique with respect to the table - * - sets are not empty whenever appropriate - * - the # of foreign key columns must correspond to the # of primary key columns in the child table - * - `CS` of ForeignKey is different from `S` - */ - final case class TableContext[S <: Set[Column] with Singleton]( - table: Table, - columns: Superset[Column, S], - primaryKeyColumns: OrderedSubset[Column, S] - ) { outer => - - case class TableColumn private[TableContext] (table: outer.table.type, column: Column) - extends AbstractDiEdge(table: Table, column) - with DiEdgeToString - with Connection - - protected case class PrimaryKey private (table: outer.table.type, columns: OrderedSubset[Column, S]) - extends AbstractDiHyperEdge(one(table: Table), OneOrMore.fromUnsafe(columns)) - with MultiEdge - with Connection { - def extendKeyBy: OneOrMore[Any] = PrimaryKey.edgeKeyExtension - } - - case object PrimaryKey { - def apply(): PrimaryKey = PrimaryKey(table, primaryKeyColumns) - private val edgeKeyExtension = one(PrimaryKey.toString) - } - - def columnEdges: List[TableColumn] = - columns.set pipe (set => - set.iterator.map { column => - TableColumn(table, column) - }.toList - ) - - def primaryKeyEdge: PrimaryKey = PrimaryKey() - - def requiredEdges: List[Connection] = primaryKeyEdge +: columnEdges - - case class Index[CS <: Set[Column] with Singleton] private ( - table: outer.table.type, - columns: OrderedSubset[Column, S] - ) extends AbstractDiHyperEdge(one(table: Table), OneOrMore.fromUnsafe(columns)) - with MultiEdge - with Connection { - def extendKeyBy: OneOrMore[Any] = Index.edgeKeyExtension - } - - case object Index { - def apply[CS <: Set[Column] with Singleton]( - columns: OrderedSubset[Column, S] - ): Index[CS] = - Index(table, columns) - - private val edgeKeyExtension = one(this.toString) - } - - case class ForeignKey[CS <: Set[Column] with Singleton] private ( - table: outer.table.type, - columns: OrderedSubset[Column, S], - childTable: TableContext[CS] - ) extends AbstractDiHyperEdge(one(table: Table), OneOrMore.fromUnsafe(columns)) - with MultiEdge - with Connection { - def extendKeyBy: OneOrMore[Any] = ForeignKey.edgeKeyExtension - } - - case object ForeignKey { - def apply[CS <: Set[Column] with Singleton]( - columns: OrderedSubset[Column, S], - childTable: TableContext[CS] - ): ForeignKey[CS] = - ForeignKey(table, columns, childTable) - - private val edgeKeyExtension = one(this.toString) - } - } - - abstract class TableDef(val tableName: String) { - final val table = Table(tableName) - val columns: Set[Column] - val primaryKeyColumns: OrderedSubset[Column, columns.type] - - final def column(name: String, `type`: DataType): Column = Column(table, name, `type`) - final lazy val context: TableContext[columns.type] = TableContext(table, Superset(columns), primaryKeyColumns) - final def requiredEdges: List[Connection] = context.requiredEdges - - final def index[C <: Set[Column] with Singleton](columns: Column*) = - context.Index(OrderedSubset(this.columns)(columns: _*)) - - final def foreignKey[C <: Set[Column] with Singleton](childTable: TableContext[C])(columns: Column*) = - context.ForeignKey(OrderedSubset(this.columns)(columns: _*), childTable) - } - - object Address extends TableDef("address") { - val name = column("name", StringType) - val countryCode = column("countryCode", StringType) - val columns = Set(name, countryCode) - val primaryKeyColumns = OrderedSubset(columns)(name) - } - - object Country extends TableDef("country") { - val name = column("name", StringType) - val countryCode = column("countryCode", StringType) - val columns = Set(name, countryCode) - val primaryKeyColumns = OrderedSubset(columns)(countryCode) - } - - type SqlSchema = Graph[Node, Connection] - object SqlSchema extends TypedGraphFactory[Node, Connection] - - val schema = SqlSchema.from( - Address.requiredEdges - ++: Country.requiredEdges - ++: List( - Address.foreignKey(childTable = Country.context)(Address.countryCode), - Country.index(Country.countryCode) - ) - ) - - // TODO some tasks based on `schema` -} diff --git a/coreTestScala3/src/test/scala/demo/InspectingDemoSpec.scala b/coreTestScala3/src/test/scala/demo/InspectingDemoSpec.scala deleted file mode 100644 index d7f50dd6..00000000 --- a/coreTestScala3/src/test/scala/demo/InspectingDemoSpec.scala +++ /dev/null @@ -1,146 +0,0 @@ -package demo - -import org.scalatest.matchers.should.Matchers -import org.scalatest.refspec.RefSpec - -import scalax.collection.OuterImplicits.* -import scalax.collection.{OneOrMore, Several} -import scalax.collection.edges.* -import scalax.collection.generic.AnyEdge -import scalax.collection.hyperedges.* -import scalax.collection.mutable.Graph - -class InspectingDemoSpec extends RefSpec with Matchers { - - object `demonstrating ` { - - def `Iterate over Elements`(): Unit = { - val g = Graph(2 ~ 3, 3 ~ 1) - g.toString // "Graph(NodeSet(1, 2, 3), EdgeSet(2 ~ 3, 3 ~ 1))" - g.nodes mkString ", " // "1, 2, 3" - g.edges mkString ", " // "2 ~ 3, 3 ~ 1" - g.outerIterator - .map { - case g.OuterNode(n) => n.toString - case g.OuterEdge(e) => e.toString - } - .mkString(", ") // 1, 2, 3, 2 ~ 3, 3 ~ 1 - } - - def `Look up Nodes and Edges`(): Unit = { - val g = Graph(1 ~ 2) - g find 1 // Option[g.NodeT] = Some(1) - g find 3 // Option[g.NodeT] = None - g get 1 // g.NodeT = 1 - a[NoSuchElementException] should be thrownBy { - g get 3 - } // NoSuchElementException - g find 1 ~ 2 // Option[g.EdgeT] = Some(1 ~ 2) - g.nodes find (_.outer == 1) // Option[g.NodeT] = 1 - g addAndGet 3 // g.NodeT = 3 - } - - def `Inspect Edge Ends`(): Unit = { - val unDiEdge = 3 ~ 4 // UnDiEdge[Int] - unDiEdge.node1 * unDiEdge.node2 // Int = 12 - unDiEdge.ends.iterator.product // Int = 12 - unDiEdge match { - case n ~ m => n * m // Int = 12 - } - - val diEdge = 1 ~> 2 // DiEdge[Int] - unDiEdge.arity == diEdge.arity // true - diEdge.source - diEdge.target // Int = -1 - diEdge match { - case s ~> t => s - t // Int = -1 - } - - val hyperEdge = 1 ~~ 2 ~~ 11 ~~ 12 // HyperEdge[Int] - hyperEdge.ends.get(hyperEdge.arity - 1) // Int = 12 - hyperEdge.ends.iterator.sum // Int = 26 - hyperEdge match { - case ~~(Several.Seq(n1, n2, _*)) => n1 - n2 // Int = -1 - } - - val diHyperEdge = OneOrMore(1, 2) ~~> OneOrMore(5, 6) - diHyperEdge.sources.iterator.sum // Int = 3 - diHyperEdge.targets.iterator.sum // Int = 11 - diHyperEdge match { - case OneOrMore.Seq(_, s2, _*) ~~> OneOrMore(t1, _) => s2 * t1 // Int = 10 - } - } - - def `Inspect Neighbors and Incident Edges`(): Unit = { - val g = Graph[Int, AnyEdge](0, 1 ~ 3, 3 ~> 2) - - def n(outer: Int): g.NodeT = g get outer - - n(0).diSuccessors // Set[g.NodeT] = Set() - n(2).diSuccessors // Set[g.NodeT] = Set() - n(3).diSuccessors // Set[g.NodeT] = Set(1, 2) - n(3).diPredecessors // Set[g.NodeT] = Set(1) - n(2).incoming // Set[g.EdgeT] = Set(3 ~> 2) - } - } - - def `Query the Node or Edge Set`(): Unit = { - val g = Graph[Int, AnyEdge](2 ~> 3, 3 ~ 1, 5) - - g.nodes.filter(_.outer > 2) // Set[g.NodeT] = Set(3, 5) - g.nodes.filter(_.degree > 1) // Set[g.NodeT] = Set(3) - g.edges.filter(_.adjacents.isEmpty) // Set[g.EdgeT] = Set() - - g.outerIterator - .filter { - case g.OuterNode(n) => n > 1 - case g.OuterEdge(AnyEdge(n1, n2)) => n1 > 1 && n2 > 1 - } - .map { - case g.OuterNode(n) => n - case g.OuterEdge(e) => e - } - .toList // List[Any] = List(2, 3, 5, 2 ~> 3) - - g.iterator.filter { - case g.InnerNode(innerN, _) => innerN.degree > 1 - case g.InnerEdge(_, outerE) => outerE.isDirected - }.mkString // String = 3, 2 ~> 3 - } - - def `Compute Union, Difference and Intersection`(): Unit = { - val g = Graph(1 ~ 2, 2 ~ 3, 2 ~ 4, 3 ~ 5, 4 ~ 5) - val h = Graph(3 ~ 4, 3 ~ 5, 4 ~ 6, 5 ~ 6) - g union h // Graph(1 ~ 2, 2 ~ 3, 2 ~ 4, 3 ~ 5, 4 ~ 5, 3 ~ 4, 4 ~ 6, 5 ~ 6) - g diff h // Graph(1 ~ 2) - g intersect h // Graph(4, 3 ~ 5) - g &= h // Graph(4, 3 ~ 5), mutated instance - } - - def `Measure your Graph`(): Unit = { - val g = Graph[Int, AnyEdge](1 ~ 2, 2 ~ 3, 1 ~> 3, 1 ~ 5, 3 ~ 5, 3 ~ 4, 4 ~> 4, 4 ~> 5) - - g.order // Int = 5 - g.size // Int = 8 - g.elementCount // Int = 13 - g.totalDegree // Int = 16 - g.degreeSet // TreeSet(4, 3, 2) - g.degreeNodeSeq(g.InDegree) // List((4, 3), (3, 5), (2, 1), (2, 2), (2, 4)) - g.degreeNodesMap // Map(2->Set(2), 3->Set(5, 1), 4->Set(3, 4)) - g.degreeNodesMap(degreeFilter = _ > 3) // Map(4 -> Set(3, 4)) - } - - def `Classify your Graph`(): Unit = { - val g = Graph(1, 2 ~> 3) - - g.isConnected // false - (g addOne 2 ~> 1).isConnected // true - (g get 2).findConnected(_.outer == 3) // Some(3) - g.isCyclic // false - (g addOne 3 ~> 2).isCyclic // true - g.isComplete // false - (g ++ List(1 ~> 2, 1 ~> 3, 2 ~> 1, 3 ~> 1, 3 ~> 2)).isComplete // true - g.isDirected // true - g.isHyper // false - g.isMulti // false - } -} diff --git a/coreTestScala3/src/test/scala/demo/TransformingDemoSpec.scala b/coreTestScala3/src/test/scala/demo/TransformingDemoSpec.scala deleted file mode 100644 index 50896dac..00000000 --- a/coreTestScala3/src/test/scala/demo/TransformingDemoSpec.scala +++ /dev/null @@ -1,131 +0,0 @@ -package demo - -import scala.concurrent.duration.* - -import org.scalatest.matchers.should.Matchers -import org.scalatest.refspec.RefSpec - -import scalax.collection.OuterImplicits.* -import scalax.collection.edges.* -import scalax.collection.generic.AnyEdge -import scalax.collection.immutable.Graph - -final class TransformingDemoSpec extends RefSpec with Matchers { - - object `demonstrating ` { - def `Filter your graph`(): Unit = { - val g = Graph[Int, AnyEdge](2 ~> 3, 3 ~ 1, 5) - - g filter (nodeP = _ >= 2) should ===(Graph(2, 3, 5, 2 ~> 3)) - g filter (edgeP = _.isDirected) should ===(Graph(1, 5, 2, 3, 2 ~> 3)) - g filter (nodeP = _ >= 2, edgeP = _.isUndirected) should ===(Graph(2, 3, 5)) - } - - def `Fold your graph`(): Unit = { - val g = Graph(1 ~> 2, 1 ~> 3, 4) - - g.foldLeft(Graph.empty[Int, DiEdge[Int]])( - opNode = { - case (cum, g.InnerNode(n, _)) if n.isIsolated => cum + n - case (cum, _) => cum - }, - opEdge = { - case (cum, g.InnerEdge(e, _)) if cum.size % 2 == 0 => cum + e - case (cum, _) => cum - } - ) should (be(Graph(4, 1 ~> 3)) or be(Graph(4, 1 ~> 2))) // Graph(NodeSet(1, 2, 4), EdgeSet(1 ~> 2)) - } - - def `Map your generic graph`(): Unit = - Graph(1 ~ 2).map( - fNode = _.outer + 1, - fEdge = (n1: Int, n2: Int) => n1 ~> n2 - ) shouldEqual Graph(2 ~> 3) - - def `Flat-map your generic graph`: Unit = - Graph(1 ~ 2).flatMap( - fNode = _.outer + 1 :: Nil, - fEdge = (n1s: Seq[Int], n2s: Seq[Int]) => - (n1s, n2s) match { - case (Seq(n1, _*), Seq(n2, _*)) => List(n1 ~> n2, n2 ~> n1) - } - ) shouldEqual Graph(2 ~> 3, 3 ~> 2) - - def `Map your typed graph`(): Unit = { - import FlightDemoSpec.* - import scalax.collection.labeled.aviation.* - - val hamReplacedByFra = - g.mapBound((airport: g.NodeT) => if (airport == hamburg) frankfurt else airport) - - hamReplacedByFra should have size g.size - hamReplacedByFra.nodes should not contain hamburg - - def hamInvolved(flight: Flight): Boolean = - flight.departure == hamburg || - flight.destination == hamburg - def replaced(flight: Flight) = flight.copy( - departure = if (flight.departure == hamburg) frankfurt else flight.departure, - destination = if (flight.destination == hamburg) frankfurt else flight.destination - ) - g.edges.outerIterable.filter(hamInvolved) foreach { originalFlight => - hamReplacedByFra.edges.outerIterable should contain(replaced(originalFlight)) - } - - g.map( - fNode = (airport: g.NodeT) => airport.outer.toString, - fEdge = (from: String, to: String) => DiEdge(from, to) - ) shouldEqual Graph.from( - g.edges.outerIterable.map(flight => DiEdge(flight.departure.toString, flight.destination.toString)) - ) - } - - def `Flat-map your typed graph`(): Unit = { - import FlightDemoSpec.* - import scalax.collection.labeled.aviation.* - - def ham(flight: Flight): Boolean = flight.departure == hamburg || flight.destination == hamburg - def shortHam(flight: Flight): Boolean = ham(flight) && flight.duration < 1.hour - - val shortHamReplacedByFra = - g.flatMapBound( - fNode = (airport: g.NodeT) => (if (airport == hamburg) frankfurt else airport.outer) :: Nil, - fEdge = (edge: g.EdgeT, newDepartures: Seq[Airport], newDestinations: Seq[Airport]) => { - val outer = edge.outer - if (shortHam(outer)) - outer.copy(departure = newDepartures.head, destination = newDestinations.head) :: Nil - else if (ham(outer)) Nil - else outer :: Nil - } - ) - - val longHamCount = g.edges.outerIterable.count(f => ham(f) && !shortHam(f)) - shortHamReplacedByFra should have size g.size - longHamCount - shortHamReplacedByFra.nodes should not contain hamburg - - def replaced(flight: Flight) = flight.copy( - departure = if (flight.departure == hamburg) frankfurt else flight.departure, - destination = if (flight.destination == hamburg) frankfurt else flight.destination - ) - g.edges.outerIterable.filter(shortHam) foreach { originalFlight => - shortHamReplacedByFra.edges.outerIterable should contain(replaced(originalFlight)) - } - - g.flatMap( - fNode = (airport: g.NodeT) => { - def existing = airport.outer.toString - if (airport == hamburg) existing :: frankfurt.toString :: Nil - else existing :: Nil - }, - fEdge = (edge: g.EdgeT, from: Seq[String], to: Seq[String]) => { - val outer = edge.outer - if (shortHam(outer)) { - def replaced(a: Airport) = (if (a == hamburg) frankfurt else a).toString - DiEdge(replaced(outer.departure), replaced(outer.destination)) :: Nil - } else if (ham(outer)) Nil - else DiEdge(from.head, to.head) :: Nil - } - ) - } - } -} diff --git a/coreTestScala3/src/test/scala/demo/TraversingDemoSpec.scala b/coreTestScala3/src/test/scala/demo/TraversingDemoSpec.scala deleted file mode 100644 index f48f1336..00000000 --- a/coreTestScala3/src/test/scala/demo/TraversingDemoSpec.scala +++ /dev/null @@ -1,206 +0,0 @@ -package demo - -import org.scalatest.matchers.should.Matchers -import org.scalatest.refspec.RefSpec - -import scalax.collection.{OuterElem, OuterNode} -import scalax.collection.OuterImplicits.* -import scalax.collection.edges.* -import scalax.collection.edges.labeled.* -import scalax.collection.generic.AnyEdge -import scalax.collection.immutable.Graph - -/** Includes the examples given on [[http://www.scala-graph.org/guides/core-traversing.html - * Traversing Graphs]]. - */ -final class TraversingDemoSpec extends RefSpec with Matchers { - - type Outer = OuterElem[Int, AnyEdge[Int]] - val g = Graph[Int, AnyEdge](1 ~ 2 % 4, 2 ~ 3 % 2, 1 ~> 3 % 5, 1 ~ 5 % 3, 3 ~ 5 % 2, 3 ~ 4 % 1, 4 ~> 4 % 1, 4 ~> 5 % 0) - def n(outer: Int): g.NodeT = g get outer - - object `demonstrating ` { - def `traversals for a result`: Unit = { - def validatePath(p: g.Path, sample: List[Outer]): Unit = { - def toN(p: Outer): Int = p match { - case OuterNode(n) => n - case _ => throw new IllegalArgumentException - } - p.isValid shouldBe true - p.startNode.outer == toN(sample.head) shouldBe true - p.endNode.outer == toN(sample.last) shouldBe true - } - - n(1) findSuccessor (_.outDegree > 3) shouldBe None - n(1) findSuccessor (_.outDegree >= 3) should be(Some(3)) - n(4) findSuccessor (_.edges forall (!_.isDirected)) should be(Some(2)) - n(4) isPredecessorOf n(1) shouldBe true - validatePath( - (n(1) pathTo n(4)).get, - List[Outer](1, 1 ~> 3 % 5, 3, 3 ~ 4 % 1, 4) - ) - validatePath( - (n(1) pathUntil (_.outDegree >= 3)).get, - List[Outer](1, 1 ~> 3 % 5, 3) - ) - val maybeP = n(3) shortestPathTo n(1) - val p = maybeP.get - validatePath(p, List[Outer](3, 3 ~ 4 % 1, 4, 4 ~> 5 % 0, 5, 1 ~ 5 % 3, 1)) - p.nodes.toList should be(List(3, 4, 5, 1)) - p.weight shouldBe 4 - - def negWeight(e: g.EdgeT): Double = 5.5f - e.weight - val maybeNegP = n(3).shortestPathTo(n(1), negWeight) - val negP = maybeNegP.get - validatePath(negP, List[Outer](3, 2 ~ 3 % 2, 2, 1 ~> 2 % 4, 1)) - negP.nodes.toList shouldBe List(3, 2, 1) - negP.weight shouldBe 6 - - val maybeSubgraphP1 = n(4).withSubgraph(nodes = _ < 4) pathTo n(2) - validatePath(maybeSubgraphP1.get, List[Outer](4, 3 ~ 4 % 1, 3, 2 ~ 3 % 2, 2)) - maybeSubgraphP1.map(_.nodes).get.toList shouldBe List(4, 3, 2) - - val maybeSubgraphP2 = n(4).withSubgraph(edges = _.weight != 2) pathTo n(2) - validatePath(maybeSubgraphP2.get, List[Outer](4, 4 ~> 5 % 0, 5, 1 ~ 5 % 3, 1, 1 ~ 2 % 4, 2)) - maybeSubgraphP2.map(_.nodes).get.toList shouldBe List(4, 5, 1, 2) - } - - def `cycle detection`: Unit = { - val g = Graph(1 ~> 2, 1 ~> 3, 2 ~> 3, 3 ~> 4, 4 ~> 2) - val fc1 = g.findCycle - fc1.get.sameElements(List(2, 2 ~> 3, 3, 3 ~> 4, 4, 4 ~> 2, 2)) shouldBe true - val fc2 = (g get 4).findCycle - fc2.get.sameElements(List(4, 4 ~> 2, 2, 2 ~> 3, 3, 3 ~> 4, 4)) shouldBe true - for { - c1 <- fc1 - c2 <- fc2 - } yield c1 == c2 shouldBe false - for { - c1 <- fc1 - c2 <- fc2 - } yield c1 sameAs c2 shouldBe true - } - - def `ordered traversal`: Unit = { - val root = 1 - val g = Graph(root ~> 4 % 2, root ~> 2 % 5, root ~> 3 % 4, 3 ~> 6 % 4, 3 ~> 5 % 5, 3 ~> 7 % 2) - - def edgeOrdering = g.EdgeOrdering(g.BaseInnerEdge.WeightOrdering.reverse.compare _) - val traverser = (g get root).outerNodeTraverser.withOrdering(edgeOrdering) - - traverser.toList should equal(List(1 to 7: _*)) - } - - def `traversers with fluent properties`: Unit = { - val g = Graph(1 ~> 2 % 1, 1 ~> 3 % 2, 2 ~> 3 % 3, 3 ~> 4 % 1) - val n1 = g get 1 - - n1.outerNodeTraverser.sum shouldBe 10 - g.outerNodeTraverser(n1).sum shouldBe 10 - n1.outerNodeTraverser.withMaxDepth(1).sum shouldBe 6 - - n1.innerEdgeTraverser.map(_.weight).sum shouldBe 7 - - n1.innerElemTraverser - .filter { - case g.InnerNode(n, _) => n.degree > 1 - case g.InnerEdge(_, e) => e.weight > 1 - } - .map { - case g.InnerNode(_, n) => n - case g.InnerEdge(_, e) => e - } - .toSet shouldBe Set(1, 2, 3, 1 ~> 3 % 2, 2 ~> 3 % 3) - } - - def `DownUp traverser`: Unit = { - import scala.collection.mutable.ArrayBuffer - - val root = "A" - val g = Graph(root ~> "B1", root ~> "B2") - val innerRoot = g get root - val result = innerRoot.innerNodeDownUpTraverser.foldLeft(ArrayBuffer.empty[String]) { (buf, param) => - param match { - case (down, node) => - if (down) buf += (if (node eq innerRoot) "(" else "[") += node.toString - else buf += (if (node eq innerRoot) ")" else "]") - } - } - result.fold("")(_ + _) should (be("(A[B1][B2])") or - be("(A[B2][B1])")) - } - - def `extended traverser`: Unit = { - val g = Graph(1 ~> 2, 1 ~> 3, 2 ~> 3, 3 ~> 4, 4 ~> 2) - - import g.ExtendedNodeVisitor - - type ValDepth = (Int, Int) - var info = List.empty[ValDepth] - (g get 1).innerNodeTraverser.foreach { - ExtendedNodeVisitor((node, _, depth, _) => info :+= (node.outer, depth)) - } - info.sortWith((a: ValDepth, b: ValDepth) => - a._1 < b._1 || - a._1 == b._1 && a._2 < b._2 - ) should be(List((1, 0), (2, 1), (3, 1), (4, 2))) - } - - def `cycle detection for side effect`: Unit = { - val g = Graph(1 ~> 2, 1 ~> 3, 2 ~> 3, 3 ~> 4, 4 ~> 2) - - var center: Option[g.NodeT] = None - val maybeCycle = (g get 4).findCycle(n => - center = center match { - case s @ Some(c) => if (n.degree > c.degree) Some(n) else s - case None => Some(n) - } - ) - maybeCycle.map(_.sameElements(List(2, 2 ~> 3, 3, 3 ~> 4, 4, 4 ~> 2, 2))) shouldBe Some(true) - center shouldBe Some(2) - } - - def `weak component traverser`: Unit = { - val componentEdges = { - def edges(i: Int) = List(i ~> (i + 1), i ~> (i + 2), (i + 1) ~> (i + 2)) - (edges(1), edges(5)) - } - val disconnected = Graph.from(edges = componentEdges._1 ++ componentEdges._2) - val sums = - for (component <- disconnected.componentTraverser()) - yield component.nodes.foldLeft(0)((cum, n) => cum + n.outer) - sums should be(List(6, 18)) - - val anyNode = disconnected.nodes.draw(new util.Random) - anyNode.weakComponent.nodes should have size componentEdges._1.size - } - - def `strong component traverser`: Unit = { - type G = Graph[Char, DiEdge[Char]] - val sccExpected: (G, G) = ( - Graph('a' ~> 'b', 'b' ~> 'c', 'c' ~> 'd', 'd' ~> 'a', 'd' ~> 'e', 'c' ~> 'e', 'e' ~> 'c'), - Graph('f' ~> 'g', 'g' ~> 'f', 'g' ~> 'h', 'h' ~> 'j', 'j' ~> 'i', 'i' ~> 'g', 'i' ~> 'f', 'f' ~> 'i') - ) - val connected = (sccExpected._1 union sccExpected._2) concat List('e' ~> 'f') - val scc = connected.strongComponentTraverser().map(_.to(Graph)) - scc.toSet should be(Set(sccExpected._1, sccExpected._2)) - - val startAt = sccExpected._2.nodes.head - startAt.strongComponents should have size 1 - startAt.innerNodeTraverser.strongComponents(_ => ()) - } - - def `path builder`: Unit = { - val builder = g.newPathBuilder(n(1)) - builder += n(3) += n(4) - builder.result().toOuterElems.toList shouldBe List[Outer](1, 1 ~> 3 % 5.0, 3, 3 ~ 4 % 1.0, 4) - - builder.clear() - builder += n(4) += n(3) - builder.result().toOuterElems.toList shouldBe List[Outer](1, 1 ~> 3 % 5.0, 3) - - builder.clear() - builder add n(4) shouldBe false - } - } -} diff --git a/coreTestScala3/src/test/scala/demo/subset.scala b/coreTestScala3/src/test/scala/demo/subset.scala deleted file mode 100644 index 47bd2336..00000000 --- a/coreTestScala3/src/test/scala/demo/subset.scala +++ /dev/null @@ -1,88 +0,0 @@ -package demo - -import scala.collection.immutable.ArraySeq -import scala.reflect.ClassTag -import scala.util.chaining.* - -protected trait SubsetOps[A, +S <: Set[A] with Singleton] extends Set[A] { - def superset: S - def set: Set[A] - - def contains(elem: A): Boolean = set(elem) - def iterator: Iterator[A] = set.iterator - override def toString: String = s"${getClass.getSimpleName}(${set mkString ", "}) out of ${superset.size}" -} - -/** Includes a `superset` and a `set` guaranteeing that `set` is a subset of `superset`. - * - * @tparam A type of the elements of both `superset` and `set` - * @tparam S singleton type denoting `superset` - */ -case class Subset[A, +S <: Set[A] with Singleton] private (superset: S, set: Set[A]) extends SubsetOps[A, S] { - - def incl(elem: A): Subset[A, S] = - if (superset(elem) && !set(elem)) Subset(superset, set incl elem) - else this - - def excl(elem: A): Subset[A, S] = Subset(superset, set excl elem) -} - -object Subset { - def apply[A](superset: Set[A])(elems: A*): Subset[A, superset.type] = - new Subset(superset, elems.iterator.filter(superset.apply).toSet) - - def byFilter[A](superset: Set[A])(predicate: A => Boolean): Subset[A, superset.type] = - new Subset(superset, superset.filter(predicate)) -} - -/** Includes a `superset`, a `seq` and a `set` guaranteeing that `set` is a subset of `superset` and - * `seq` has the same elements as `set` ordered on creation time. - * - * @tparam A type of the elements of both `superset` and `set` - * @tparam S singleton type denoting `superset` - */ -case class OrderedSubset[A, +S <: Set[A] with Singleton] private (superset: S, ordered: ArraySeq[A], set: Set[A]) - extends SubsetOps[A, S] { - - def incl(elem: A): OrderedSubset[A, S] = - if (superset(elem) && !set(elem)) - OrderedSubset(superset, ordered appended elem, set incl elem) - else this - - def excl(elem: A): OrderedSubset[A, S] = - ordered.indexOf(elem) pipe { idx => - if (idx == -1) this - else OrderedSubset(superset, ordered.patch(idx, elem :: Nil, 1), set excl elem) - } -} - -object OrderedSubset { - def apply[A](superset: Set[A])(elems: A*)(implicit tag: ClassTag[A]): OrderedSubset[A, superset.type] = - elems.foldLeft((ArraySeq.empty[A], Set.empty[A])) { - case (cum @ (_, set), elem) if !superset(elem) || set(elem) => cum - case ((seq, set), elem) => (seq appended elem, set incl elem) - } match { - case (seq, set) => new OrderedSubset(superset, seq, set) - } -} - -/** Includes a `superset` and a single element `set` guaranteeing that `set` is a subset of `superset`. - * - * @tparam A type of the elements of both `superset` and `set` - * @tparam S singleton type denoting `superset` - */ -case class OneOf[A, +S <: Set[A] with Singleton] private (superset: S, one: A) { - override def toString: String = s"One($one) out of ${superset.size}" -} - -object OneOf { - def apply[A](superset: Set[A])(elem: A): Option[OneOf[A, superset.type]] = - if (superset(elem)) Some(new OneOf(superset, elem)) - else None -} - -/** Wraps an `immutable.Set` by also capturing the underlying set as a singleton type. - * Useful if you need to enforce `Superset` and `Subset` instances to use the same superset singleton. - * Gets probably obsolete in Scala 3. - */ -case class Superset[A, +S <: Set[A] with Singleton](set: Set[A] with S) diff --git a/coreTestScala3/src/test/scala/scala/collection/mutable/ExtHashSetSpec.scala b/coreTestScala3/src/test/scala/scala/collection/mutable/ExtHashSetSpec.scala deleted file mode 100644 index 27f1ca8a..00000000 --- a/coreTestScala3/src/test/scala/scala/collection/mutable/ExtHashSetSpec.scala +++ /dev/null @@ -1,154 +0,0 @@ -package scala.collection.mutable - -import scala.collection.immutable -import scala.math.abs -import scala.util.Random -import scala.util.chaining.* - -import org.scalatest.matchers.should.Matchers -import org.scalatest.refspec.RefSpec - -class ExtHashSetSpec extends RefSpec with Matchers { - - object `ExtHashSet works properly in that it` { - import ExtHashSetSpec.* - - def `draws random elements with uniform distribution if buckets are of equal length`: Unit = { - val size = 16 - val set = ExtHashSet(1 to size: _*) - nonEmptyBuckets(set).length should ===(size) - - val nrProbesPerElement = 100 - val nrProbes = size * nrProbesPerElement - val frequencies = sortedProbeFrequencies(set, nrProbes, Some(0)) - val maxDeviation_% = 20 - range(frequencies) should be < nrProbesPerElement * (2 * maxDeviation_%) / 100 - } - - def `draws random elements with near-uniform distribution if buckets are of different length`: Unit = { - val set = { - val maxSize = 16 - val r = new Random - r.setSeed(1150) - ExtHashSet.fill(maxSize)(r.nextInt(maxSize * 4)) - } - - val buckets = nonEmptyBuckets(set) - buckets.map(_.length) pipe { bucketLengths => - bucketLengths.max - bucketLengths.min should be > 0 - } - - val bucketLengthOfElement: Int Map Int = { - val m = Map.empty[Int, Int] - buckets foreach { bucket => - val len = bucket.length - bucket foreach (v => m update (v, len)) - } - m - } - val nrProbesPerElement = 100 - val nrProbes = set.size * nrProbesPerElement - val frequencies = sortedProbeFrequencies(set, nrProbes, Some(77)) - val maxDeviationForBucketLength_% = Map(1 -> 25, 2 -> 50) - frequencies foreach { case ProbeFrequency(element, count) => - abs(nrProbesPerElement - count) should be < maxDeviationForBucketLength_%(bucketLengthOfElement(element)) - } - } - - def `finds elements by predicate`: Unit = { - case class C(value: Int) { - override def hashCode(): Int = 0 - } - val s = ExtHashSet(C(1), C(2)) - s.findElem(C(3), (i: C, j: C) => i.value == j.value - 1) should ===(C(2)) - } - - def `is able to upsert elements`: Unit = { - val elem = new MutableElem(1, 0) - val set = ExtHashSet(elem) - - val mutation = new MutableElem(1, 1) - mutation should ===(elem) - - set.upsert(mutation) should be(false) - set should (have size 1 and contain(mutation)) - - set.upsert(new MutableElem(2, 0)) should be(true) - set should have size 2 - } - - def `iterates over hashCodes`: Unit = { - case class C(value: Int) { - override def hashCode(): Int = value - } - val set = ExtHashSet(C(1), C(2)) - set.hashCodeIterator(3).toList should have size 0 - val elems = set.hashCodeIterator(1).toList - elems should have size 1 - elems.head should be(C(1)) - } - - def `iterates over duplicate hashCodes`: Unit = { - case class C(i: Int, j: Int) { - override def hashCode = i.## - } - val multi = ExtHashSet(C(1, 0), C(1, 1), C(2, 2), C(1, 3), C(2, 0)) - for (i <- 0 to 2) { - val elems = multi.hashCodeIterator(i.##).toList - elems should have size (multi count (_.i == i)) - } - } - } -} - -object ExtHashSetSpec { - - private def nonEmptyBuckets(set: ExtHashSet[Int]) = set.dump filter (_.nonEmpty) - - protected case class ProbeFrequency[A](probe: A, count: Int) - - private def sortedProbeFrequencies( - set: ExtHashSet[Int], - nrProbes: Int, - seed: Option[Long] - ): List[ProbeFrequency[Int]] = { - val r = new Random - seed foreach r.setSeed - Array - .fill(nrProbes)(set draw r) - .groupBy(identity) - .map { case (k, v: Array[Int]) => - ProbeFrequency(k, v.length) - } - .toList - .sortBy { case ProbeFrequency(_, v) => - v - } - } - - private def range(sortedFrequencies: List[ProbeFrequency[Int]]): Int = - (sortedFrequencies.head, sortedFrequencies.last) match { - case (ProbeFrequency(_, lowest), ProbeFrequency(_, highest)) => highest - lowest - } - - class MutableElem(val a: Int, var b: Int) { - override def hashCode(): Int = a.## - - override def equals(other: Any): Boolean = other match { - case that: MutableElem => a == that.a - case _ => false - } - - override def toString: String = s"M($a, $b)" - } - - /* some support to play on the console: - - val set = ExtHashSet(0, 1, 7, 10, 12, 81, 82, 83) - set.dump foreach println - - Range(10, 2000, 100) foreach { i => - println(sortedProbeFrequencies(set, i)) - } - */ -} diff --git a/coreTestScala3/src/test/scala/scalax/collection/ConfigWrapper.scala b/coreTestScala3/src/test/scala/scalax/collection/ConfigWrapper.scala deleted file mode 100644 index 44d0e5f0..00000000 --- a/coreTestScala3/src/test/scala/scalax/collection/ConfigWrapper.scala +++ /dev/null @@ -1,24 +0,0 @@ -package scalax.collection - -import scalax.collection.config.GraphConfig -import scalax.collection.generic.{Edge, GenericGraphFactory} - -/** Enables to transparently pass `GraphCompanion` objects with non-default configuration parameters to specs. - */ -trait ConfigWrapper[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E, CC]] { - val companion: GenericGraphFactory[CC] - implicit val config: GraphConfig - - def empty[N, E <: Edge[N]]: CC[N, E] = companion.empty[N, E] - - def apply[N, E[X] <: Edge[X]](elems: OuterElem[N, E[N]]*)(implicit config: GraphConfig): CC[N, E[N]] = - companion(elems: _*) - - def from[N, E <: Edge[N]](nodes: collection.Iterable[N], edges: collection.Iterable[E])(implicit - config: GraphConfig - ): CC[N, E] = - companion.from[N, E](nodes, edges) - - def from[N, E[X] <: Edge[X]](edges: collection.Iterable[E[N]]): CC[N, E[N]] = - companion.from[N, E](edges) -} diff --git a/coreTestScala3/src/test/scala/scalax/collection/ConnectivitySpec.scala b/coreTestScala3/src/test/scala/scalax/collection/ConnectivitySpec.scala deleted file mode 100644 index ef7b8b2b..00000000 --- a/coreTestScala3/src/test/scala/scalax/collection/ConnectivitySpec.scala +++ /dev/null @@ -1,181 +0,0 @@ -package scalax.collection - -import scala.util.Random - -import org.scalatest.* -import org.scalatest.matchers.should.Matchers -import org.scalatest.refspec.RefSpec -import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks -import scalax.collection.OuterImplicits.* -import scalax.collection.edges.* -import scalax.collection.generator.* -import scalax.collection.generator.RandomGraph.IntFactory -import scalax.collection.generic.* -import scalax.collection.visualization.Visualizer - -class ConnectivitySpec - extends Suites( - new Connectivity[immutable.Graph](immutable.Graph), - new Connectivity[mutable.Graph](mutable.Graph) - ) - -final class Connectivity[G[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E, G]]( - val factory: GenericGraphCoreFactory[G] -) extends RefSpec - with Matchers - with ScalaCheckPropertyChecks - with IntelliJ[G] - with Visualizer { - - implicit val config: PropertyCheckConfiguration = - PropertyCheckConfiguration(minSuccessful = 5, maxDiscardedFactor = 1.0) - - object `In a weakly connected diGraph` { - import Data.elementsOfDi_1 - val g = factory(elementsOfDi_1: _*).asAnyGraph - - def `there exists no pair of mutually reachable nodes`: Unit = - withGraph(g) { - _.nodes.toList.combinations(2) foreach { - case List(a, b) => List(a pathTo b, b pathTo a) should contain(None) - case _ => fail() - } - } - - def `evaluating strong components from any node yields single-node components`: Unit = - withGraph(g) { - _.nodes foreach { n => - val components = n.innerNodeTraverser.strongComponents - components foreach (_.nodes should have size 1) - } - } - - def `evaluating all strong components yields a component for every node`: Unit = - withGraph(g) { g => - g.strongComponentTraverser().size shouldBe g.order - } - } - - object `Having two strong components` { - // see example on https://de.wikipedia.org/wiki/Algorithmus_von_Tarjan_zur_Bestimmung_starker_Zusammenhangskomponenten - val sccExpected = Vector[G[Char, DiEdge[Char]]]( - factory('a' ~> 'b', 'b' ~> 'c', 'c' ~> 'd', 'd' ~> 'a', 'd' ~> 'e', 'c' ~> 'e', 'e' ~> 'c'), - factory('f' ~> 'g', 'g' ~> 'f', 'g' ~> 'h', 'h' ~> 'j', 'j' ~> 'i', 'i' ~> 'g', 'i' ~> 'f', 'f' ~> 'i') - ) - val sscExpectedAny: Vector[AnyGraph[Char, DiEdge[Char]]] = sccExpected // annotated for IntelliJ - val sccExpectedG = sccExpected - - assert(sccExpected.size == 2) - assert(sscExpectedAny(0).intersect(sscExpectedAny(1)).isEmpty) - - def `each is detected as such`: Unit = - sscExpectedAny.foreach { g => - withGraph(g) { - _.strongComponentTraverser() should have size 1 - } - } - - def `connected by a diEdge yields a graph with the very same two strong components`: Unit = { - val r = new Random - val union = - sscExpectedAny.foldLeft(factory.empty[Char, DiEdge[Char]].asAnyGraph)((r, g) => g union r) - val connectors = { - def pickNode(index: Int) = sscExpectedAny(index).nodes.draw(r).outer - for (i <- 1 to 10) yield pickNode(0) ~> pickNode(1) - } - connectors foreach { connector => - val connected = union concat List(connector) - def check(scc: Iterable[connected.Component], expectedSize: Int): Unit = { - scc should have size expectedSize - scc foreach { sc => - withGraph(sc.to(factory).asAnyGraph) { g => - sccExpected should contain(g) - sc.frontierEdges should have size 1 - } - } - } - - check(connected.strongComponentTraverser().toVector, 2) - - val start = connected.nodes.draw(r) - check( - start.innerNodeTraverser.strongComponents.toVector, - if (sccExpected(0).asAnyGraph contains start) 2 else 1 - ) - } - } - } - - object `Having two weak components` { - def `weak components are detected, fix #57`: Unit = - withGraph(factory(11 ~> 12, 13 ~> 14)) { - _.componentTraverser() should have size 2 - } - } - - object `Having a bigger graph` { - val g = { - val gOrder = 1000 - val random = RandomGraph.diGraph( - factory, - new IntFactory { - val order = gOrder - val nodeDegrees = NodeDegreeRange(gOrder / 10, gOrder / 4) - } - ) - random.draw - }.asAnyGraph - - val strongComponents = g.strongComponentTraverser().toVector - - def `no stack overflow occurs`: Unit = - withGraph(g)(_ => strongComponents shouldBe a[Vector[_]]) - - def `strong components are complete`: Unit = - withGraph(g) { _ => - strongComponents.foldLeft(Set.empty[g.NodeT])((cum, sc) => cum ++ sc.nodes) shouldBe g.nodes - } - - def `strong components are proper`: Unit = - withGraph(g) { _ => - val maxProbes = 10 - val arbitraryNodes: Vector[Set[g.NodeT]] = strongComponents map { sc => - val nodes = sc.nodes - if (nodes.size <= maxProbes) nodes - else { - val every = nodes.size / maxProbes - nodes.zipWithIndex withFilter { case (_, i) => i % every == 0 } map (_._1) - } - } - arbitraryNodes foreach { nodes => - def checkBiConnected(n1: g.NodeT, n2: g.NodeT) = - if (n1 ne n2) { - n1 pathTo n2 shouldBe defined - n2 pathTo n1 shouldBe defined - } - - nodes.sliding(2) foreach { pairOrSingle => - pairOrSingle.toList match { - case List(n1, n2) => checkBiConnected(n1, n2) - case n :: Nil => checkBiConnected(n, nodes.head) - case _ => fail() - } - } - } - arbitraryNodes.sliding(2) foreach { pairOrSingle => - def checkNonBiConnected(ns1: Set[g.NodeT], ns2: Set[g.NodeT]): Unit = - if (ns1 ne ns2) - ns1 zip ns2 foreach { case (n1, n2) => - (n1 pathTo n2) shouldBe defined - (n2 pathTo n1) should not be defined - } - - pairOrSingle.toList match { - case List(ns1, ns2) => checkNonBiConnected(ns1, ns2) - case ns :: Nil => checkNonBiConnected(ns, arbitraryNodes.head) - case _ => fail() - } - } - } - } -} diff --git a/coreTestScala3/src/test/scala/scalax/collection/CycleSpec.scala b/coreTestScala3/src/test/scala/scalax/collection/CycleSpec.scala deleted file mode 100644 index 3153ff91..00000000 --- a/coreTestScala3/src/test/scala/scalax/collection/CycleSpec.scala +++ /dev/null @@ -1,393 +0,0 @@ -package scalax.collection - -import language.postfixOps - -import scalax.collection.OuterImplicits.* -import scalax.collection.edges.* -import scalax.collection.generic.* -import scalax.collection.visualization.Visualizer - -import org.scalatest.matchers.should.Matchers -import org.scalatest.refspec.RefSpec -import org.scalatest.Suites -import org.scalatest.matchers.{MatchResult, Matcher} - -import scala.util.chaining.scalaUtilChainingOps - -class CycleSpec extends Suites(new Cycle[immutable.Graph](immutable.Graph), new Cycle[mutable.Graph](mutable.Graph)) - -private trait CycleMatcher[N, E <: Edge[N]] { - protected type C = AnyGraph[N, E]#Cycle - - def haveOneNodeSequenceOf(expected: Seq[N]*): Matcher[Option[C]] = - Matcher { (ns: Option[C]) => - val found: Seq[N] = ns match { - case None => Seq() - case Some(path) => path.nodes.toSeq.map(_.outer).toList - } - def msg(key: String): String = - s"""$found equals to $key of - |${expected mkString ", "} in - |${ns.get.containingGraph}. - """.stripMargin - MatchResult(expected contains found, msg("none"), msg("one")) - } - - def beValid: Matcher[Option[C]] = - Matcher { (c: Option[C]) => - def msg(key: String): String = s"$c $key valid" - MatchResult(c.get.isValid, msg("is not"), msg("is")) - } -} - -private class Cycle[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E, CC]]( - val factory: GenericGraphCoreFactory[CC] -) extends RefSpec - with Matchers - with IntelliJ[CC] - with Visualizer { - - object `given some directed graphs` extends CycleMatcher[Int, DiEdge[Int]] { - private val acyclic_1 = factory(1 ~> 2, 1 ~> 3, 2 ~> 3, 3 ~> 4).asAnyGraph - private val acyclic_2 = - factory(1 ~> 2, 1 ~> 3, 1 ~> 4, 1 ~> 5, 2 ~> 3, 3 ~> 7, 7 ~> 4, 7 ~> 8, 4 ~> 5, 5 ~> 6).asAnyGraph - private val ( - (cyclic_1, cyclicEdge_1), - (cyclic_21, cyclicEdge_21), - (cyclic_22, cyclicEdge_22) - ) = { - def makeCyclic(acyclic: AnyGraph[Int, DiEdge[Int]], byEdge: DiEdge[Int]) = { - val cyclic = acyclic concat List(byEdge) - (cyclic, cyclic get byEdge) - } - ( - makeCyclic(acyclic_1, 4 ~> 2), - makeCyclic(acyclic_2, 8 ~> 3), - makeCyclic(acyclic_2, 6 ~> 1) - ) - } - - def `the cycle returned by 'findCycle' contains the expected nodes`: Unit = { - withGraph(acyclic_1) { g => - (g get 1 findCycle) should be(None) - } - withGraph(cyclic_1) { g => - (g get 2 findCycle) should haveOneNodeSequenceOf(Seq(2, 3, 4, 2)) - } - - withGraph(acyclic_2) { g => - (g get 1 findCycle) should be(None) - } - withGraph(cyclic_21) { g => - (g get 1 findCycle) should haveOneNodeSequenceOf(Seq(3, 7, 8, 3)) - } - withGraph(cyclic_22) { g => - def n(outer: Int) = g get outer - - n(1).findCycle should haveOneNodeSequenceOf( - Seq(1, 5, 6, 1), - Seq(1, 4, 5, 6, 1), - Seq(1, 3, 7, 4, 5, 6, 1), - Seq(1, 2, 3, 7, 4, 5, 6, 1) - ) - n(4).findCycle should haveOneNodeSequenceOf( - Seq(5, 6, 1, 5), - Seq(4, 5, 6, 1, 4), - Seq(4, 5, 6, 1, 3, 7, 4), - Seq(4, 5, 6, 1, 2, 3, 7, 4) - ) - } - - val g = { - var i, j = 0 - factory.fill(5) { i += 1; j = i + 1; i ~> j } - }.asAnyGraph - - def fromEachNode[N, E <: Edge[N]](noCycles: Set[N], cycle: AnyGraph[N, E]#Cycle): Unit = - withGraph(cycle.nodes.head.containingGraph) { - _.nodes foreach { n => - val found = n.findCycle - if (noCycles contains n.outer) found should be(None) - else (found.get sameAs cycle) should be(true) - } - } - - withGraph(g concat List(4 ~> 2)) { g => - val cycle = g get 3 findCycle - - cycle should haveOneNodeSequenceOf(Seq(3, 4, 2, 3)) - fromEachNode(Set(5, 6), cycle get) - } - - withGraph(g concat List(5 ~> 2)) { g => - val cycle = g get 3 findCycle - - cycle should haveOneNodeSequenceOf(Seq(3, 4, 5, 2, 3)) - fromEachNode(Set(6), cycle get) - } - } - - def `the cycle returned by 'findCycleContaining' contains the expected nodes`: Unit = { - withGraph(acyclic_1) { g => - g.findCycleContaining(g get 1) should be(None) - } - withGraph(cyclic_1) { g => - g.findCycleContaining(g get 2) should haveOneNodeSequenceOf(Seq(2, 3, 4, 2)) - } - withGraph(acyclic_2) { g => - g.findCycleContaining(g get 1) should be(None) - } - withGraph(cyclic_21) { g => - def n(outer: Int) = g get outer - - g.findCycleContaining(n(1)) should be(None) - g.findCycleContaining(n(3)) should haveOneNodeSequenceOf(Seq(3, 7, 8, 3)) - } - withGraph(cyclic_22) { g => - def n(outer: Int) = g get outer - - g.findCycleContaining(n(1)) should haveOneNodeSequenceOf( - Seq(1, 5, 6, 1), - Seq(1, 4, 5, 6, 1), - Seq(1, 3, 7, 4, 5, 6, 1), - Seq(1, 2, 3, 7, 4, 5, 6, 1) - ) - g.findCycleContaining(n(4)) should haveOneNodeSequenceOf( - Seq(4, 5, 6, 1, 4), - Seq(4, 5, 6, 1, 3, 7, 4), - Seq(4, 5, 6, 1, 2, 3, 7, 4) - ) - g.findCycleContaining(n(3)) should haveOneNodeSequenceOf(Seq(3, 7, 4, 5, 6, 1, 3), Seq(3, 7, 4, 5, 6, 1, 2, 3)) - g.findCycleContaining(n(2)) should haveOneNodeSequenceOf(Seq(2, 3, 7, 4, 5, 6, 1, 2)) - } - } - - def `the cycle returned by 'partOfCycle' combined with fluent properties contains the expected nodes`: Unit = - withGraph(cyclic_22) { g => - def n(outer: Int) = g get outer - - n(1).withSubgraph(nodes = _.outer != 3).partOfCycle() should haveOneNodeSequenceOf( - Seq(1, 5, 6, 1), - Seq(1, 4, 5, 6, 1) - ) - n(4).withSubgraph(nodes = _.outer != 3).partOfCycle() should haveOneNodeSequenceOf(Seq(4, 5, 6, 1, 4)) - n(2).withSubgraph(nodes = _.outer != 3).partOfCycle() should be(None) - } - - def `the cycle returned by 'findCycle' contains the expected edges`: Unit = { - withGraph(acyclic_1)(_.findCycle should be(None)) - withGraph(cyclic_1)(_.findCycle.get.edges should contain(cyclicEdge_1)) - withGraph(acyclic_2)(_.findCycle should be(None)) - withGraph(cyclic_21)(_.findCycle.get.edges should contain(cyclicEdge_21)) - withGraph(cyclic_22)(_.findCycle.get.edges should contain(cyclicEdge_22)) - } - - def `the cycle returned by 'findCycleContaining' contains the expected edges`: Unit = { - withGraph(cyclic_1) { g => - g.findCycleContaining(g get 2).get.edges should contain(cyclicEdge_1) - } - withGraph(cyclic_21) { g => - g.findCycleContaining(g get 3).get.edges should contain(cyclicEdge_21) - } - withGraph(cyclic_22) { g => - g.findCycleContaining(g get 1).get.edges should contain(cyclicEdge_22) - } - } - - def `'isCyclic' returns the expected result`: Unit = { - withGraph(acyclic_1)(_.isAcyclic shouldBe true) - withGraph(cyclic_1)(_.isCyclic shouldBe true) - withGraph(acyclic_2)(_.isAcyclic shouldBe true) - withGraph(cyclic_21)(_.isCyclic shouldBe true) - withGraph(cyclic_22)(_.isCyclic shouldBe true) - } - - def `they are cyclic if they contain a self loop #76`: Unit = { - val loop = 1 ~> 1 - withGraph(acyclic_1 concat List(loop))(_.isCyclic shouldBe true) - withGraph(factory(loop)) { g => - g.findCycle should (be(defined) and beValid) - g.findCycleContaining(g get 1) should (be(defined) and beValid) - } - } - } - - object `given some undirected graphs` extends CycleMatcher[Int, UnDiEdge[Int]] { - private val unDiAcyclic_1 = factory(1 ~ 2, 2 ~ 3).asAnyGraph - private val unDiCyclic_1 = unDiAcyclic_1 concat List(1 ~ 3) - - private val unDiAcyclic_2 = factory(1 ~ 2, 1 ~ 3, 2 ~ 4, 2 ~ 5).asAnyGraph - private val unDiCyclic_21 = unDiAcyclic_2 concat List(3 ~ 5) - private val unDiCyclic_22 = unDiAcyclic_2 concat List(3 ~ 6, 6 ~ 7, 7 ~ 4) - - def `the cycle returned by 'findCycle' contains the expected nodes`: Unit = { - withGraph(unDiAcyclic_1) { g => - (g get 1 findCycle) shouldBe None - } - withGraph(unDiCyclic_1) { g => - (g get 2 findCycle) should haveOneNodeSequenceOf(Seq(2, 3, 1, 2), Seq(2, 1, 3, 2)) - } - withGraph(unDiAcyclic_2) { g => - (g get 1 findCycle) should be(None) - } - withGraph(unDiCyclic_21) { g => - (g get 1 findCycle) should haveOneNodeSequenceOf(Seq(1, 3, 5, 2, 1), Seq(1, 2, 5, 3, 1)) - } - withGraph(unDiCyclic_22) { g => - (g get 3 findCycle) should haveOneNodeSequenceOf(Seq(3, 1, 2, 4, 7, 6, 3), Seq(3, 6, 7, 4, 2, 1, 3)) - } - } - - def `the cycle returned by 'findCycleContaining' contains the expected nodes`: Unit = { - withGraph(unDiAcyclic_1) { g => - g.findCycleContaining(g get 1) should be(None) - } - withGraph(unDiCyclic_1) { g => - g.findCycleContaining(g get 2) should haveOneNodeSequenceOf(Seq(2, 3, 1, 2), Seq(2, 1, 3, 2)) - } - withGraph(unDiAcyclic_2) { g => - g.findCycleContaining(g get 1) should be(None) - } - withGraph(unDiCyclic_21) { g => - def n(outer: Int) = g get outer - - g.findCycleContaining(n(1)) should haveOneNodeSequenceOf(Seq(1, 3, 5, 2, 1), Seq(1, 2, 5, 3, 1)) - g.findCycleContaining(n(4)) should be(None) - } - withGraph(unDiCyclic_22) { g => - def n(outer: Int) = g get outer - - g.findCycleContaining(n(3)) should haveOneNodeSequenceOf(Seq(3, 1, 2, 4, 7, 6, 3), Seq(3, 6, 7, 4, 2, 1, 3)) - g.findCycleContaining(n(5)) should be(None) - } - } - def `the cycle returned by 'partOfCycle' combined with fluent properties contains the expected nodes`: Unit = { - withGraph(unDiCyclic_21) { g => - (g get 1).withSubgraph(nodes = _.outer != 2).partOfCycle() should be(None) - } - withGraph(unDiCyclic_22) { g => - (g get 3).withSubgraph(nodes = _.outer != 2).partOfCycle() should be(None) - } - } - } - - object `given an undirected multigraph` { - import scalax.collection.edges.multilabeled._ - val (e1, e2) = (1 ~ 2 %% 0, 1 ~ 2 %% 1) - - val g = factory(e1, e2).asAnyGraph - - def `the cycle returned by 'findCycle' contains the expected edges`: Unit = { - val c = (g get 1).findCycle - c shouldBe defined - c.get.edges should (be(List(e1, e2)) or be(List(e2, e1))) - } - - def `the cycle returned by 'findCycleContaining' contains the expected edges`: Unit = { - val c = g.findCycleContaining(g get 1) - c shouldBe defined - c.get.edges should (be(List(e1, e2)) or - be(List(e2, e1))) - } - } - - object `given some mixed graphs` extends CycleMatcher[Int, AnyEdge[Int]] { - - private val mixed = factory.from(Data.elementsOfMixed_1).asAnyGraph - - def `'findCycle' finds a cycle following any route`: Unit = { - withGraph( - factory[Int, AnyEdge]( - 1 ~> 3, - 3 ~> 4, - 3 ~> 20, - 20 ~> 21, - 1 ~> 10, - 10 ~ 11, - 11 ~ 12, - 12 ~ 13, - 12 ~> 3, - 20 ~> 10 - ).asAnyGraph - ) { g => - g.findCycle pipe { cycle => - cycle should (be(defined) and beValid) - cycle foreach (_.nodes foreach { n => - g.innerNodeTraverser(n).findCycle should (be(defined) and beValid) - }) - } - (g get 13).innerNodeTraverser.withOrdering(g.NodeOrdering((a, b) => b.outer - a.outer)).findCycle should (be( - defined - ) and beValid) - } - withGraph(mixed.filterNot(_.outer == 5, _.outer == 4 ~> 4)) { g => - (g get 1).findCycle should haveOneNodeSequenceOf(Seq(1, 3, 2, 1)) - } - } - - def `the cycle returned by 'findCycleContaining' contains the expected nodes`: Unit = - withGraph(mixed) { g => - def n(outer: Int) = g get outer - - g.findCycleContaining(n(2)) should haveOneNodeSequenceOf( - Seq(2, 1, 3, 2), - Seq(2, 3, 4, 5, 1, 2), - Seq(2, 1, 5, 3, 2), - Seq(2, 3, 5, 1, 2) - ) - g.findCycleContaining(n(1)) should haveOneNodeSequenceOf( - Seq(1, 3, 2, 1), - Seq(1, 3, 5, 1), - Seq(1, 2, 3, 5, 1), - Seq(1, 5, 3, 2, 1), - Seq(1, 2, 3, 4, 5, 1), - Seq(1, 3, 4, 5, 1) - ) - g.findCycleContaining(n(4)) should haveOneNodeSequenceOf( - Seq(4, 4), - Seq(4, 5, 3, 4), - Seq(4, 5, 1, 2, 3, 4), - Seq(4, 5, 1, 3, 4) - ) - } - - def `the cycle returned by 'partOfCycle' combined with fluent properties contains the expected nodes`: Unit = - withGraph(mixed) { g => - def n(outer: Int) = g get outer - - n(2).withSubgraph(edges = _ != DiEdge(1, 3)).partOfCycle should haveOneNodeSequenceOf( - Seq(2, 3, 4, 5, 1, 2), - Seq(2, 1, 5, 3, 2), - Seq(2, 3, 5, 1, 2) - ) - n(2) - .withSubgraph(nodes = _.outer != 5) - .withOrdering(g.BaseInnerEdge.WeightOrdering) - .partOfCycle should haveOneNodeSequenceOf(Seq(2, 1, 3, 2)) - - n(1).withSubgraph(nodes = _.outer != 5).partOfCycle should haveOneNodeSequenceOf(Seq(1, 3, 2, 1)) - } - - private val cycleEdges = List(1 ~> 2, 1 ~ 2) - private val g = factory.from[Int, AnyEdge](2 ~ 3 +: cycleEdges).asAnyGraph - - def `the cycle returned by 'findCycle' contains the expected edges`: Unit = - withGraph(g) { g => - g.size should be(3) - g.nodes foreach { n => - val c = n.findCycle - (n, c.isDefined) should be((n, true)) - c.get.edges should (be(cycleEdges) or be(cycleEdges.reverse)) - } - } - - def `the cycle returned by 'findCycleContaining' contains the expected edges`: Unit = - withGraph(g) { g => - g.nodes.filterNot(_.outer == 3) foreach { n => - val c = g.findCycleContaining(g get n) - (n, c.isDefined) should be((n, true)) - c.get.edges should (be(cycleEdges) or be(cycleEdges.reverse)) - } - } - } -} diff --git a/coreTestScala3/src/test/scala/scalax/collection/Data.scala b/coreTestScala3/src/test/scala/scalax/collection/Data.scala deleted file mode 100644 index e95b9c34..00000000 --- a/coreTestScala3/src/test/scala/scalax/collection/Data.scala +++ /dev/null @@ -1,114 +0,0 @@ -package scalax.collection - -import scala.annotation.tailrec -import scala.collection.BuildFrom -import scala.util.Random.shuffle - -import scalax.collection.generic._ -import scalax.collection.edges._ -import scalax.collection.edges.labeled._ - -abstract class TGraph[N, E <: Edge[N], G[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E, G]]( - val g: G[N, E] -) { - def node(outer: N): g.NodeT = g get outer - def n(outer: N): g.NodeT = node(outer) - def edge(outer: E): g.EdgeT = g get outer - def e(outer: E): g.EdgeT = edge(outer) -} - -/** The Graph for Scala representation of graph pictures located in `scala/test/doc`. - */ -object Data { -// WDi-1.jpg without weights - val elementsOfDi_1 = List( - 1 ~> 2, - 2 ~> 3, - 4 ~> 3, - 3 ~> 5, - 1 ~> 5, - 1 ~> 3 - ) - - // WUnDi-1.jpg without weights - val elementsOfMixed_1 = List[AnyEdge[Int]]( - 1 ~ 2, - 2 ~ 3, - 1 ~> 3, - 1 ~ 5, - 3 ~ 5, - 3 ~ 4, - 4 ~> 4, - 4 ~> 5 - ) - - // WUnDi-2.jpg without weights - val elementsOfMixed_2 = List[AnyEdge[Int]]( - 1 ~ 2, - 2 ~ 3, - 1 ~> 3, - 1 ~ 3, - 1 ~> 2, - 2 ~ 2 - ) - -// WDi-1.jpg - val elementsOfWDi_1 = List( - 1 ~> 2 % 4, - 2 ~> 3 % 40, - 4 ~> 3 % 7, - 3 ~> 5 % 50, - 1 ~> 5 % 40, - 1 ~> 3 % 2 - ) - -// WUnDi-1.jpg - val elementsOfWMixed_1 = List[AnyEdge[Int]]( - 1 ~ 2 % 4, - 2 ~ 3 % 2, - 1 ~> 3 % 5, - 1 ~ 5 % 3, - 3 ~ 5 % 2, - 3 ~ 4 % 1, - 4 ~> 4 % 1, - 4 ~> 5 % 0 - ) - -// WUnDi-2.jpg - val elementsOfWMixed_2 = List[AnyEdge[Int]]( - 1 ~ 2 % 4, - 2 ~ 3 % -1, - 1 ~> 3 % 5, - 1 ~ 3 % 4, - 1 ~> 2 % 3, - 2 ~ 2 % 1 - ) - - def shuffleNotEqual[A, C <: IterableOnce[A]](xs: IterableOnce[A])(implicit bf: BuildFrom[xs.type, A, C]): C = - xs match { - case _: Iterable[A] => - @tailrec def loop(): C = { - val shuffled = shuffle(xs) - if (shuffled == xs) loop() - else shuffled - } - loop() - - case it: Iterator[A] => - val source = it.toBuffer - @tailrec def loop(): C = { - val shuffled = shuffle(source) - if (shuffled == source) loop() - else shuffled.iterator.asInstanceOf[C] - } - loop() - } - - def shuffleNotEqual[N, C[X] <: OneOrMore[X]](o: C[N]): OneOrMore[N] = o match { - case OneOrMore(_, tail) if tail.isEmpty => throw new IllegalArgumentException("Cannot shuffle single element.") - case more => OneOrMore.fromUnsafe(shuffleNotEqual(more.iterator)) - } - - def shuffleNotEqual[N](s: Several[N]): Several[N] = - Several.fromUnsafe(shuffleNotEqual(s.iterator)) -} diff --git a/coreTestScala3/src/test/scala/scalax/collection/DegreeSpec.scala b/coreTestScala3/src/test/scala/scalax/collection/DegreeSpec.scala deleted file mode 100644 index 785b75a3..00000000 --- a/coreTestScala3/src/test/scala/scalax/collection/DegreeSpec.scala +++ /dev/null @@ -1,191 +0,0 @@ -package scalax.collection - -import scala.collection.{SortedMap, SortedSet} - -import org.scalatest.* -import org.scalatest.matchers.should.Matchers -import org.scalatest.refspec.RefSpec -import scalax.collection.edges.* -import scalax.collection.generic.* -import scalax.collection.visualization.Visualizer - -class DegreeSpec - extends Suites( - new Degree[immutable.Graph](immutable.Graph), - new Degree[mutable.Graph](mutable.Graph) - ) - -class Degree[CC[N, E <: Edge[N]] <: GraphLike[N, E, CC] with AnyGraph[N, E]](val factory: GenericGraphCoreFactory[CC]) - extends RefSpec - with Matchers - with Visualizer - with IntelliJ[CC] { - - private val emptyG: AnyGraph[Int, DiEdge[Int]] = factory.empty[Int, DiEdge[Int]].asAnyGraph - - abstract class TestGraph[N, E <: Edge[N]](override val g: CC[N, E]) extends TGraph(g) { - def degree(outer: N) = node(outer).degree - val nodeDegrees: List[(g.NodeT, Int)] = g.nodes.iterator.map(n => (n, n.degree)).toList - val degrees: List[Int] = nodeDegrees map (_._2) - } - - import Data.* - object UnDi_1 extends TestGraph[Int, AnyEdge[Int]](factory.from(elementsOfMixed_1)) { - val expectedDegreeSeq = Seq(4, 4, 3, 3, 2) - val expectedDegreeSet = SortedSet(4, 3, 2) - val expectedDegreeNodeSeq = Seq((4, node(4)), (4, node(3)), (3, node(5)), (3, node(1)), (2, node(2))) - val expectedDegreeNodesMap = SortedMap((4, Set(node(3), node(4))), (3, Set(node(1), node(5))), (2, Set(node(2)))) - val expectedDegreeCount = SortedMap((4, 2), (3, 2), (2, 1)) - val expectedInDegreeNodeSeq = Seq((4, node(3)), (3, node(5)), (2, node(4)), (2, node(2)), (2, node(1))) - val expectedDegreeGT3NodesMap = SortedMap((4, Set(node(3), node(4)))) - } - object UnDi_2 extends TestGraph[Int, AnyEdge[Int]](factory.from(elementsOfMixed_2)) { - val expectedDegreeSeq = Seq(5, 4, 3) - val expectedDegreeSet = SortedSet(5, 4, 3) - val expectedDegreeNodeSeq = Seq((5, node(2)), (4, node(1)), (3, node(3))) - val expectedDegreeNodesMap = SortedMap((5, Set(node(2))), (4, Set(node(1))), (3, Set(node(3)))) - val expectedDegreeCount = SortedMap((5, 1), (4, 1), (3, 1)) - } - - object `Degrees are calculated properly` { - def `for nodes`: Unit = { - { - import UnDi_1.* - withGraph(g.asAnyGraph) { _ => - degree(1) should be(3) - degree(2) should be(2) - degree(3) should be(4) - degree(4) should be(4) - degree(5) should be(3) - } - } - { - import UnDi_2.* - withGraph(g.asAnyGraph) { _ => - degree(1) should be(4) - degree(2) should be(5) - degree(3) should be(3) - } - } - } - - def `for total graph`: Unit = { - emptyG.totalDegree shouldBe 0; - { - import UnDi_1.* - withGraph(g.asAnyGraph)(_.totalDegree shouldBe degrees.sum) - } - { - import UnDi_2.* - withGraph(g.asAnyGraph)(_.totalDegree shouldBe degrees.sum) - } - } - } - - object `Degree statistics are calculated properly for` { - def `minimum degree`: Unit = { - emptyG.minDegree should be(0); - { - import UnDi_1.* - withGraph(g.asAnyGraph)(_.minDegree should be(degrees.min)) - } - { - import UnDi_2.* - withGraph(g.asAnyGraph)(_.minDegree should be(degrees.min)) - } - } - - def `maximum degree`: Unit = { - emptyG.maxDegree should be(0); - { - import UnDi_1.* - withGraph(g.asAnyGraph)(_.maxDegree should be(degrees.max)) - } - { - import UnDi_2.* - withGraph(g.asAnyGraph)(_.maxDegree should be(degrees.max)) - } - } - - def `sequence of degrees`: Unit = { - emptyG.degreeSeq should be(Seq.empty); - { - import UnDi_1.* - withGraph(g.asAnyGraph)(_.degreeSeq should be(expectedDegreeSeq)) - } - { - import UnDi_2.* - withGraph(g.asAnyGraph)(_.degreeSeq should be(expectedDegreeSeq)) - } - } - - def `set of degrees`: Unit = { - emptyG.degreeSet should be(Set.empty); - { - import UnDi_1.* - withGraph(g.asAnyGraph)(_.degreeSet should be(expectedDegreeSet)) - } - { - import UnDi_2.* - withGraph(g.asAnyGraph)(_.degreeSet should be(expectedDegreeSet)) - } - } - - def `sequence of nodes sorted by degree`: Unit = { - emptyG.degreeNodeSeq should be(Seq.empty); - { - import UnDi_1.* - withGraph(g.asAnyGraph) { g => - val ord = new Ordering[g.DegreeNodeSeqEntry] { - def compare(a: g.DegreeNodeSeqEntry, b: g.DegreeNodeSeqEntry) = { - def sortKey(e: g.DegreeNodeSeqEntry) = 100 * e._1 + e._2 - - sortKey(b) compare sortKey(a) - } - } - - val ds = g.degreeNodeSeq - val dsSorted = ds.toList sorted ord - dsSorted should be(expectedDegreeNodeSeq) - - val ids = g.degreeNodeSeq(g.InDegree) - val idsSorted = ids.toList sorted ord - idsSorted should be(expectedInDegreeNodeSeq) - } - } - { - import UnDi_2.* - withGraph(g.asAnyGraph)(_.degreeNodeSeq should be(expectedDegreeNodeSeq)) - } - } - - def `map of nodes by degree`: Unit = { - emptyG.degreeNodesMap should be(Map.empty); - { - import UnDi_1.* - withGraph(g.asAnyGraph) { g => - g.degreeNodesMap should be(expectedDegreeNodesMap) - g.degreeNodesMap(degreeFilter = _ > 3) should be(expectedDegreeGT3NodesMap) - } - } - { - import UnDi_2.* - withGraph(g.asAnyGraph) { g => - g.degreeNodesMap should be(expectedDegreeNodesMap) - } - } - } - - def `map of degree by node`: Unit = { - emptyG.degreeCount should be(Map.empty); - { - import UnDi_1.* - withGraph(g.asAnyGraph)(_.degreeCount should be(expectedDegreeCount)) - } - { - import UnDi_2.* - withGraph(g.asAnyGraph)(_.degreeCount should be(expectedDegreeCount)) - } - } - } -} diff --git a/coreTestScala3/src/test/scala/scalax/collection/EditingHyperSpec.scala b/coreTestScala3/src/test/scala/scalax/collection/EditingHyperSpec.scala deleted file mode 100644 index f745accc..00000000 --- a/coreTestScala3/src/test/scala/scalax/collection/EditingHyperSpec.scala +++ /dev/null @@ -1,175 +0,0 @@ -package scalax.collection - -import org.scalatest.Suites -import org.scalatest.matchers.should.Matchers -import org.scalatest.refspec.RefSpec -import scalax.collection.OneOrMore.{more, one} -import scalax.collection.OuterImplicits.* -import scalax.collection.edges.* -import scalax.collection.generic.* -import scalax.collection.hyperedges.* - -/** Editing any kind of hypergraph with unlabeled edges including mixed and multigraphs. - */ -class EditingHyperSpec - extends Suites( - new EditingHyper[immutable.Graph](immutable.Graph), - new EditingHyper[mutable.Graph](mutable.Graph), - new EditingHyperMutable - ) - -private class EditingHyper[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E, CC]]( - val factory: GenericGraphCoreFactory[CC] -) extends RefSpec - with Matchers { - - object `hypergraph editing` { - - def `create HyperEdge`: Unit = { - "HyperEdge(List(1))" shouldNot compile - "HyperEdge(List(1): _*)" shouldNot compile - - HyperEdge.from(List(1)) shouldBe None - an[IllegalArgumentException] shouldBe thrownBy { - HyperEdge.fromUnsafe(List(1)) - } - - val h = HyperEdge(1, 2, 3) - 1 ~~ 2 ~~ 3 shouldEqual h - h.arity shouldBe 3 - h.toString shouldBe "1 ~~ 2 ~~ 3" - - val g = factory[Int, AnyHyperEdge](1, h, 1 ~ 2) - g.nodes should have size 3 - g.edges should have size 2 - g.elementCount should be(5) - g.contains(h) should be(true) - } - - def `create DiHyperEdge`: Unit = { - "DiHyperEdge(List(1), List(1)): DiHyperEdge[Int]" shouldNot compile - "DiHyperEdge(List(1): _*)(): DiHyperEdge[Int]" shouldNot compile - - DiHyperEdge.from(List(1), Nil) shouldBe None - an[IllegalArgumentException] shouldBe thrownBy { - DiHyperEdge.fromUnsafe(List(1), Nil) - } - - val sources = more(1, 2) - val targets = more(2, 3) - val h = DiHyperEdge(sources, targets) - - DiHyperEdge.from(List(1, 2), List(2, 3)) shouldEqual Some(h) - DiHyperEdge.from(List(1, 2), Nil) shouldEqual None - sources ~~> targets shouldEqual h - h.arity shouldBe sources.size + targets.size - h.toString shouldBe "{1, 2} ~~> {2, 3}" - - single ~~> targets shouldEqual DiHyperEdge(single, targets) - sources ~~> single shouldEqual DiHyperEdge(sources, single) - single ~~> single shouldEqual DiHyperEdge(1)(1) - - val g = factory[Int, AnyHyperEdge](1, h, 1 ~ 2) - g.nodes should have size 3 - g.edges should have size 2 - g.elementCount shouldBe 5 - g.contains(h) shouldBe true - } - - def `isHyper `: Unit = { - def test(g: CC[Int, AnyHyperEdge[Int]], expected: Boolean): Unit = g.isHyper should be(expected) - - test(factory.from[Int, AnyHyperEdge](List(1 ~> 2, 1 ~~ 2 ~~ 3)), true) - test(factory.from[Int, AnyHyperEdge](1 ~ 2 :: Nil), false) - test(factory.from[Int, AnyHyperEdge](1 ~> 2 :: Nil), false) - } - } - - val single = one(1) - val hDi = factory( - single ~~> more(1, 5), - single ~~> more(2, 5), - single ~~> more(3, 5), - single ~~> more(4, 9) - ) - - object `diSuccessors, outNeighbors` { - def `for DiHyper`: Unit = { - (hDi get 1).outNeighbors shouldEqual Set(2, 3, 4, 5, 9) - (hDi get 1).diSuccessors shouldEqual Set(1, 2, 3, 4, 5, 9) - (hDi get 2).diSuccessors shouldEqual Set.empty - (hDi get 5).diSuccessors shouldEqual Set.empty - } - } - - object `diPredecessors, inNeighbors` { - def `for DiHyper`: Unit = { - (hDi get 1).inNeighbors should be(Set.empty) - (hDi get 1).diPredecessors should be(Set(1)) - (hDi get 2).diPredecessors should be(Set(1)) - (hDi get 5).diPredecessors should be(Set(1)) - } - } - - object `neighbors ` { - def `for DiHyper`: Unit = { - (hDi get 1).neighbors should be(Set(2, 3, 4, 5, 9)) - (hDi get 2).neighbors should be(Set(1, 5)) - (hDi get 5).neighbors should be(Set(1, 2, 3)) - } - } - - def `match hyperedge`: Unit = { - val HyperEdge(Several.Seq(n1, n2, n3)) = 1 ~~ 2 ~~ 3 - n1 + n2 + n3 shouldBe 6 - } - - def `match directed hyperedge`: Unit = { - val count = 3 - val sources = OneOrMore.fromUnsafe(List.tabulate(count - 1)(_ + 1)) - val target = one(count) - val diHyper = sources ~~> target - - val OneOrMore.Seq(s1, _*) ~~> (t @ OneOrMore.Seq(c)) = diHyper: @unchecked - s1 shouldBe sources.head - c shouldBe count - t shouldBe target - } -} - -private class EditingHyperMutable extends RefSpec with Matchers { - import mutable.Graph - - object `mutable graphs with labeled edges` { - def `'diSuccessors' when directed hypergraph`: Unit = { - val (_1_to_1_2, _1_to_2_3) = ( - one(1) ~~> more(1, 2), - one(1) ~~> more(2, 3) - ) - val g = Graph(_1_to_1_2, _1_to_2_3) - val (n1, n2) = (g get 1, g get 2) - - n2.diSuccessors shouldBe empty - n1.outNeighbors should be(Set(2, 3)) - n1.diSuccessors should be(Set(1, 2, 3)) - n1 findOutgoingTo n1 should be(Some(_1_to_1_2)) - - g subtractOne _1_to_2_3 - g shouldEqual Graph(_1_to_1_2, 3) - n1.outNeighbors should be(Set(2)) - n1.diSuccessors should be(Set(1, 2)) - n1 findOutgoingTo n1 should be(Some(_1_to_1_2)) - - g subtractOne 2 - g shouldEqual Graph(1, 3) - n1.diSuccessors shouldBe empty - n1 findOutgoingTo n1 should be(None) - - g += _1_to_1_2 - g shouldEqual Graph(_1_to_1_2, 3) - n1.outNeighbors should be(Set(2)) - n1.diSuccessors should be(Set(1, 2)) - n1 findOutgoingTo n1 should be(Some(_1_to_1_2)) - } - } -} diff --git a/coreTestScala3/src/test/scala/scalax/collection/EditingLabeledHyperSpec.scala b/coreTestScala3/src/test/scala/scalax/collection/EditingLabeledHyperSpec.scala deleted file mode 100644 index 5543bb3a..00000000 --- a/coreTestScala3/src/test/scala/scalax/collection/EditingLabeledHyperSpec.scala +++ /dev/null @@ -1,550 +0,0 @@ -package scalax.collection - -import org.scalatest.Suites -import org.scalatest.matchers.should.Matchers -import org.scalatest.refspec.RefSpec -import scalax.collection.Data.shuffleNotEqual -import scalax.collection.OneOrMore.more -import scalax.collection.generic.{Edge, GenericGraphCoreFactory} - -/** Editing hypergraphs with labeled edges including support for multi-hypergraphs. - */ -class EditingLabeledHyperSpec - extends Suites( - new LabeledHyperEdges, - new EditingLabeledHyperEdges[immutable.Graph](immutable.Graph), - new EditingLabeledHyperEdges[mutable.Graph](mutable.Graph) - ) - -object EditingLabeledHyperSpec { - object Labeled { - import hyperedges.labeled.* - - case class MyHyperEdge[+N](override val ends: Several[N], label: Int) - extends LHyperEdge[N, Int](ends) - with GenericHyperEdgeMapper[MyHyperEdge] { - def map[N](ends: Several[N]): MyHyperEdge[N] = copy(ends) - } - - val ends = Several('a', 'b', 'c') - val label = 1 - val hE = MyHyperEdge(ends, label) - } - - object OrderedLabeled { - import hyperedges.ordered.labeled.* - - case class MyHyperEdge[+N](override val ends: Several[N], label: Int) - extends LHyperEdge[N, Int](ends) - with GenericHyperEdgeMapper[MyHyperEdge] { - def map[N](ends: Several[N]): MyHyperEdge[N] = copy(ends) - } - - val ends = Labeled.ends - val label = Labeled.label - val hE = MyHyperEdge(ends, label) - } - - object LabeledDi { - import hyperedges.labeled.* - - case class MyDiHyperEdge[+N](override val sources: OneOrMore[N], override val targets: OneOrMore[N], label: Int) - extends LDiHyperEdge[N, Int](sources, targets) - with GenericDiHyperEdgeMapper[MyDiHyperEdge] { - def map[N](sources: OneOrMore[N], targets: OneOrMore[N]): MyDiHyperEdge[N] = copy(sources, targets) - } - - val sources = more('a', 'b') - val targets = more('c', 'd') - val label = 1 - val diHE = MyDiHyperEdge(sources, targets, label) - } - - object OrderedLabeledDi { - import hyperedges.ordered.labeled.* - - case class MyDiHyperEdge[+N](override val sources: OneOrMore[N], override val targets: OneOrMore[N], label: Int) - extends LDiHyperEdge[N, Int](sources, targets) - with GenericDiHyperEdgeMapper[MyDiHyperEdge] { - def map[N](sources: OneOrMore[N], targets: OneOrMore[N]): MyDiHyperEdge[N] = copy(sources, targets) - } - - val sources = more('a', 'b', 'x') - val targets = more('c', 'd') - val label = 1 - val diHE = MyDiHyperEdge(sources, targets, label) - } - - object MultiLabeled { - import hyperedges.multilabeled.* - - case class MyHyperEdge[+N](override val ends: Several[N], label: Int) - extends LHyperEdge[N, Int](ends) - with GenericHyperEdgeMapper[MyHyperEdge] { - def map[N](ends: Several[N]): MyHyperEdge[N] = copy(ends) - } - - val ends = Labeled.ends - val label = Labeled.label - val hE = MyHyperEdge(ends, label) - } - - object MultiOrderedLabeled { - import hyperedges.ordered.multilabeled.* - - case class MyHyperEdge[+N](override val ends: Several[N], label: Int) - extends LHyperEdge[N, Int](ends) - with GenericHyperEdgeMapper[MyHyperEdge] { - def map[N](ends: Several[N]): MyHyperEdge[N] = copy(ends) - } - - val ends = Labeled.ends - val label = Labeled.label - val hE = MyHyperEdge(ends, label) - } - - object MultiLabeledDi { - import hyperedges.multilabeled.* - - case class MyDiHyperEdge[+N](override val sources: OneOrMore[N], override val targets: OneOrMore[N], label: Int) - extends LDiHyperEdge[N, Int](sources, targets) - with GenericDiHyperEdgeMapper[MyDiHyperEdge] { - def map[N](sources: OneOrMore[N], targets: OneOrMore[N]): MyDiHyperEdge[N] = copy(sources, targets) - } - - val sources = LabeledDi.sources - val targets = LabeledDi.targets - val label = LabeledDi.label - val diHE = MyDiHyperEdge(sources, targets, label) - } - - object MultiOrderedLabeledDi { - import hyperedges.ordered.multilabeled.* - - case class MyDiHyperEdge[+N](override val sources: OneOrMore[N], override val targets: OneOrMore[N], label: Int) - extends LDiHyperEdge[N, Int](sources, targets) - with GenericDiHyperEdgeMapper[MyDiHyperEdge] { - def map[N](sources: OneOrMore[N], targets: OneOrMore[N]): MyDiHyperEdge[N] = copy(sources, targets) - } - - val sources = OrderedLabeledDi.sources - val targets = OrderedLabeledDi.targets - val label = OrderedLabeledDi.label - val diHE = MyDiHyperEdge(sources, targets, label) - } - - def endsToString(ends: OneOrMore[_]) = ends.iterator.mkString("{", ", ", "}") -} - -private class LabeledHyperEdges extends RefSpec with Matchers { - import EditingLabeledHyperSpec.endsToString - - object `a generic undirected, mappable labeled hyperedge` { - import EditingLabeledHyperSpec.Labeled.* - import hyperedges.* - import hyperedges.labeled.* - - def `meets toString convention`: Unit = - hE.toString shouldBe s"${ends.iterator mkString " ~~ "} :+ 1" - - def `meets equality rules`: Unit = { - hE.copy(label = 9) shouldEqual hE - hE shouldEqual HyperEdge(hE.ends) - hE shouldEqual HyperEdge(shuffleNotEqual(hE.ends)) - } - - def `supports infix construction`: Unit = { - implicit class MyInfixConstructor[N](val hyperedge: HyperEdge[N]) - extends LHyperEdgeInfixConstructor[N, Int, MyHyperEdge](MyHyperEdge.apply) - 'a' ~~ 'b' ~~ 'c' :+ 1 shouldEqual hE - } - - def `supports infix extraction`: Unit = { - val +: = MyHyperEdge - - hE match { - case ends +: label => - val reconstructed = MyHyperEdge(ends, label) - "reconstructed: MyHyperEdge[Char]" should compile - reconstructed shouldEqual hE - } - hE match { - case Several.Seq(n1, n2, n3) +: label => - val reconstructed = MyHyperEdge(Several(n1, n2, n3 :: Nil), label) - "reconstructed: MyHyperEdge[Char]" should compile - reconstructed shouldEqual hE - case _ +: _ => fail() - } - } - } - - object `generic undirected, mappable, ordered labeled hyperedge` { - import EditingLabeledHyperSpec.OrderedLabeled.* - import hyperedges.* - import hyperedges.ordered.labeled.* - - def `meets toString convention`: Unit = - hE.toString shouldBe s"${ends.iterator mkString " ~~ "} :+ 1" - - def `meets equality rules`: Unit = { - hE.copy(label = 9) shouldEqual hE - hE.copy(ends = shuffleNotEqual(hE.ends)) shouldNot equal(hE) - hE shouldNot equal(HyperEdge(hE.ends)) - } - - def `supports infix construction`: Unit = { - implicit class MyInfixConstructor[N](val hyperedge: HyperEdge[N]) - extends LHyperEdgeInfixConstructor[N, Int, MyHyperEdge](MyHyperEdge.apply) - 'a' ~~ 'b' ~~ 'c' :+ 1 shouldEqual hE - } - - def `supports infix extraction`: Unit = { - val +: = MyHyperEdge - - hE match { - case ends +: label => - val reconstructed = MyHyperEdge(ends, label) - "reconstructed: MyHyperEdge[Char]" should compile - reconstructed shouldEqual hE - } - hE match { - case Several.Seq(n1, n2, n3) +: label => - val reconstructed = MyHyperEdge(Several(n1, n2, n3 :: Nil), label) - "reconstructed: MyHyperEdge[Char]" should compile - reconstructed shouldEqual hE - case _ +: _ => fail() - } - } - } - - object `generic directed, mappable labeled hyperedge` { - import EditingLabeledHyperSpec.LabeledDi.* - import hyperedges.* - import hyperedges.labeled.* - - def `meets toString convention`: Unit = - diHE.toString shouldBe s"${endsToString(sources)} ~~> ${endsToString(targets)} :+ $label" - - def `meets equality rules`: Unit = { - diHE.copy(label = 9) shouldEqual diHE - diHE shouldEqual DiHyperEdge(diHE.sources, diHE.targets) - diHE shouldEqual DiHyperEdge(shuffleNotEqual(diHE.sources), shuffleNotEqual(diHE.targets)) - } - - def `supports infix construction`: Unit = { - implicit class MyInfixConstructor[N](val diHyperedge: DiHyperEdge[N]) - extends LDiHyperEdgeInfixConstructor[N, Int, MyDiHyperEdge](MyDiHyperEdge.apply) - sources ~~> targets :+ 1 shouldEqual diHE - } - - def `supports infix extraction`: Unit = { - import generic.{UnapplyGenericHyperLabel, UnapplyGenericLabeledDiHyperEdge} - object :~~> extends UnapplyGenericLabeledDiHyperEdge[MyDiHyperEdge, Int] - object +: extends UnapplyGenericHyperLabel[Int] - - diHE match { - case sources :~~> targets +: label => - val reconstructed = MyDiHyperEdge(sources, targets, label) - "reconstructed: MyDiHyperEdge[Char]" should compile - reconstructed shouldEqual diHE - } - diHE match { - case OneOrMore.Seq(s1, s2, _*) :~~> OneOrMore.Seq(t1, t2, _*) +: label => - val reconstructed = MyDiHyperEdge( - more(s1, s2), - more(t1, t2), - label - ) - "reconstructed: MyDiHyperEdge[Char]" should compile - reconstructed shouldEqual diHE - case _ :~~> _ +: _ => fail() - } - } - } - - object `generic directed, mappable, ordered labeled hyperedge` { - import EditingLabeledHyperSpec.OrderedLabeledDi.* - import hyperedges.* - import hyperedges.ordered.labeled.* - - def `meets toString convention`: Unit = - diHE.toString shouldBe s"${endsToString(sources)} ~~> ${endsToString(targets)} :+ $label" - - def `meets equality rules`: Unit = { - diHE.copy(label = 9) shouldEqual diHE - diHE shouldNot equal(DiHyperEdge(diHE.sources, diHE.targets)) - diHE shouldNot equal(DiHyperEdge(shuffleNotEqual(diHE.sources), shuffleNotEqual(diHE.targets))) - } - - def `supports infix construction`: Unit = { - implicit class MyInfixConstructor[N](val diHyperedge: DiHyperEdge[N]) - extends LDiHyperEdgeInfixConstructor[N, Int, MyDiHyperEdge](MyDiHyperEdge.apply) - sources ~~> targets :+ 1 shouldEqual diHE - } - - def `supports infix extraction`: Unit = { - import generic.{UnapplyGenericHyperLabel, UnapplyGenericLabeledDiHyperEdge} - object :~~> extends UnapplyGenericLabeledDiHyperEdge[MyDiHyperEdge, Int] - object +: extends UnapplyGenericHyperLabel[Int] - - diHE match { - case sources :~~> targets +: label => - val reconstructed = MyDiHyperEdge(sources, targets, label) - "reconstructed: MyDiHyperEdge[Char]" should compile - reconstructed shouldEqual diHE - } - diHE match { - case OneOrMore.Seq(s1, s2, sRest @ _*) :~~> OneOrMore.Seq(t1, t2, tRest @ _*) +: label => - val reconstructed = MyDiHyperEdge( - more(s1, s2, sRest: _*), - more(t1, t2, tRest: _*), - label - ) - "reconstructed: MyDiHyperEdge[Char]" should compile - reconstructed shouldEqual diHE - case _ :~~> _ +: _ => fail() - } - } - } - - object `a generic undirected, mappable multilabeled hyperedge` { - import EditingLabeledHyperSpec.MultiLabeled.* - import hyperedges.* - import hyperedges.multilabeled.* - - def `meets toString convention`: Unit = - hE.toString shouldBe s"${ends.iterator mkString " ~~ "} :++ 1" - - def `meets equality rules`: Unit = { - hE.copy(label = 9) shouldNot equal(hE) - hE shouldNot equal(HyperEdge(hE.ends)) - } - - def `supports infix construction`: Unit = { - implicit class MyInfixConstructor[N](val hyperedge: HyperEdge[N]) - extends LHyperEdgeInfixConstructor[N, Int, MyHyperEdge](MyHyperEdge.apply) - 'a' ~~ 'b' ~~ 'c' :++ 1 shouldEqual hE - } - - def `supports infix extraction`: Unit = { - val ++ = MyHyperEdge - - hE match { - case ends ++ label => - val reconstructed = MyHyperEdge(ends, label) - "reconstructed: MyHyperEdge[Char]" should compile - reconstructed shouldEqual hE - } - hE match { - case Several.Seq(n1, n2, n3) ++ label => - val reconstructed = MyHyperEdge(Several(n1, n2, n3 :: Nil), label) - "reconstructed: MyHyperEdge[Char]" should compile - reconstructed shouldEqual hE - case _ ++ _ => fail() - } - } - } - - object `generic undirected, mappable, ordered multilabeled hyperedge` { - import EditingLabeledHyperSpec.MultiOrderedLabeled.* - import hyperedges.* - import hyperedges.ordered.multilabeled.* - - def `meets toString convention`: Unit = - hE.toString shouldBe s"${ends.iterator mkString " ~~ "} :++ 1" - - def `meets equality rules`: Unit = { - hE.copy(label = 9) shouldNot equal(hE) - hE.copy(ends = shuffleNotEqual(hE.ends)) shouldNot equal(hE) - hE shouldNot equal(HyperEdge(hE.ends)) - } - - def `supports infix construction`: Unit = { - implicit class MyInfixConstructor[N](val hyperedge: HyperEdge[N]) - extends LHyperEdgeInfixConstructor[N, Int, MyHyperEdge](MyHyperEdge.apply) - 'a' ~~ 'b' ~~ 'c' :++ 1 shouldEqual hE - } - - def `supports infix extraction`: Unit = { - val ++ = MyHyperEdge - - hE match { - case ends ++ label => - val reconstructed = MyHyperEdge(ends, label) - "reconstructed: MyHyperEdge[Char]" should compile - reconstructed shouldEqual hE - } - hE match { - case Several.Seq(n1, n2, n3) ++ label => - val reconstructed = MyHyperEdge(Several(n1, n2, n3 :: Nil), label) - "reconstructed: MyHyperEdge[Char]" should compile - reconstructed shouldEqual hE - case _ ++ _ => fail() - } - } - } - - object `generic directed, mappable multilabeled hyperedge` { - import EditingLabeledHyperSpec.MultiLabeledDi.* - import hyperedges.* - import hyperedges.multilabeled.* - - def `meets toString convention`: Unit = - diHE.toString shouldBe s"${endsToString(sources)} ~~> ${endsToString(targets)} :++ $label" - - def `meets equality rules`: Unit = { - diHE.copy(label = 9) shouldNot equal(diHE) - diHE shouldNot equal(DiHyperEdge(diHE.sources, diHE.targets)) - } - - def `supports infix construction`: Unit = { - implicit class MyInfixConstructor[N](val diHyperedge: DiHyperEdge[N]) - extends LDiHyperEdgeInfixConstructor[N, Int, MyDiHyperEdge](MyDiHyperEdge.apply) - sources ~~> targets :++ 1 shouldEqual diHE - } - - def `supports infix extraction`: Unit = { - import generic.{UnapplyGenericHyperLabel, UnapplyGenericLabeledDiHyperEdge} - object :~~> extends UnapplyGenericLabeledDiHyperEdge[MyDiHyperEdge, Int] - object ++: extends UnapplyGenericHyperLabel[Int] - - diHE match { - case sources :~~> targets ++: label => - val reconstructed = MyDiHyperEdge(sources, targets, label) - "reconstructed: MyDiHyperEdge[Char]" should compile - reconstructed shouldEqual diHE - } - diHE match { - case OneOrMore.Seq(s1, s2, _*) :~~> OneOrMore.Seq(t1, t2, _*) ++: label => - val reconstructed = MyDiHyperEdge( - more(s1, s2), - more(t1, t2), - label - ) - "reconstructed: MyDiHyperEdge[Char]" should compile - reconstructed shouldEqual diHE - case _ :~~> _ ++: _ => fail() - } - } - } - - object `generic directed, mappable, ordered multilabeled hyperedge` { - import EditingLabeledHyperSpec.MultiOrderedLabeledDi.* - import hyperedges.* - import hyperedges.ordered.multilabeled.* - - def `meets toString convention`: Unit = - diHE.toString shouldBe s"${endsToString(sources)} ~~> ${endsToString(targets)} :++ $label" - - def `meets equality rules`: Unit = { - diHE.copy(label = 9) shouldNot equal(diHE) - diHE.copy(shuffleNotEqual(diHE.sources), shuffleNotEqual(diHE.targets)) shouldNot equal(diHE) - diHE shouldNot equal(DiHyperEdge(diHE.sources, diHE.targets)) - } - - def `supports infix construction`: Unit = { - implicit class MyInfixConstructor[N](val diHyperedge: DiHyperEdge[N]) - extends LDiHyperEdgeInfixConstructor[N, Int, MyDiHyperEdge](MyDiHyperEdge.apply) - sources ~~> targets :++ 1 shouldEqual diHE - } - - def `supports infix extraction`: Unit = { - import generic.{UnapplyGenericHyperLabel, UnapplyGenericLabeledDiHyperEdge} - object :~~> extends UnapplyGenericLabeledDiHyperEdge[MyDiHyperEdge, Int] - object ++: extends UnapplyGenericHyperLabel[Int] - - diHE match { - case sources :~~> targets ++: label => - val reconstructed = MyDiHyperEdge(sources, targets, label) - "reconstructed: MyDiHyperEdge[Char]" should compile - reconstructed shouldEqual diHE - } - diHE match { - case OneOrMore.Seq(s1, s2, sRest @ _*) :~~> OneOrMore.Seq(t1, t2, tRest @ _*) ++: label => - val reconstructed = MyDiHyperEdge( - more(s1, s2, sRest: _*), - more(t1, t2, tRest: _*), - label - ) - "reconstructed: MyDiHyperEdge[Char]" should compile - reconstructed shouldEqual diHE - case _ :~~> _ ++: _ => fail() - } - } - } -} - -private class EditingLabeledHyperEdges[G[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E, G]]( - val factory: GenericGraphCoreFactory[G] -) extends RefSpec - with Matchers { - - object `a generic undirected, mappable labeled edge` { - import EditingLabeledHyperSpec.Labeled.* - - def `is mappable`: Unit = { - val g = factory.from(hE :: Nil) - g.map(_.toString).edges.toOuter.head shouldBe hE.copy(ends = hE.ends.map(_.toString)) - } - } - - object `generic undirected, mappable, ordered labeled edge` { - import EditingLabeledHyperSpec.OrderedLabeled.* - - def `is mappable`: Unit = { - val g = factory.from(hE :: Nil) - g.map(_.toString).edges.toOuter.head shouldBe hE.copy(ends = hE.ends.map(_.toString)) - } - } - - object `generic directed, mappable labeled edge` { - import EditingLabeledHyperSpec.LabeledDi.* - - def `is mappable`: Unit = { - val g = factory.from(diHE :: Nil) - g.map(_.toString).edges.toOuter.head shouldBe diHE - .copy(diHE.sources.map(_.toString), diHE.targets.map(_.toString)) - } - } - - object `generic directed, mappable, ordered labeled edge` { - import EditingLabeledHyperSpec.OrderedLabeledDi.* - - def `is mappable`: Unit = { - val g = factory.from(diHE :: Nil) - g.map(_.toString).edges.toOuter.head shouldBe diHE - .copy(diHE.sources.map(_.toString), diHE.targets.map(_.toString)) - } - } -} - -/* TODO -private class EditingLabeledHyperMutable extends RefSpec with Matchers { - object `mutable graphs with labeled edges` { - def `satisfy labeled directed hyperedege equality` { - import edge.Implicits._ - import edge.LHyperEdge - - type StringLabel = String - val outerLabels = Seq("A", "BC", "CDE") - val g = mutable.Graph(1 ~ 2 ~ 3, (2 ~+# 3) (outerLabels(0))) - - implicit val factory = LHyperEdge - (g +~+= (3, 4, 5)) (outerLabels(1)) - g should have('order(5), 'size(3)) - g.addLEdge(4, 5, 6)(outerLabels(2)) should be(true) - g should have('order(6), 'size(4)) - - val innerLabels: collection.mutable.Set[_ >: StringLabel] = - g.edges filter (_.isLabeled) map (_.label) - innerLabels should have size (outerLabels.size) - /* - innerLabels forall (outerLabels contains _) should be (true) - * https://groups.google.com/forum/?fromgroups=#!searchin/scala-internals/both$20method/scala-internals/nPZY2EMtDvY/PivCCtyRM_IJ - * https://issues.scala-lang.org/browse/SI-5330 - */ - (innerLabels: Iterable[Any]) forall (outerLabels contains _) should be(true) - } - } -} - */ diff --git a/coreTestScala3/src/test/scala/scalax/collection/EditingLabeledSpec.scala b/coreTestScala3/src/test/scala/scalax/collection/EditingLabeledSpec.scala deleted file mode 100644 index f4f6f166..00000000 --- a/coreTestScala3/src/test/scala/scalax/collection/EditingLabeledSpec.scala +++ /dev/null @@ -1,308 +0,0 @@ -package scalax.collection - -import org.scalatest.Suites -import org.scalatest.matchers.should.Matchers -import org.scalatest.refspec.RefSpec -import scalax.collection.edges.{DiEdge, UnDiEdge} -import scalax.collection.generic.{Edge, GenericGraphCoreFactory} - -/** Editing non-hypergraphs with labeled edges, in particular, editing multigraphs. - */ -class EditingLabeledSpec - extends Suites( - new LabeledEdges, - new EditingLabeledEdges[immutable.Graph](immutable.Graph), - new EditingLabeledEdges[mutable.Graph](mutable.Graph) - ) - -private class LabeledEdges extends RefSpec with Matchers { - - def `toString of labeled edge`: Unit = { - import edges.labeled.* - - WUnDiEdge('a', 'b', 2).toString shouldBe "a ~ b % 2.0" - WUnDiEdge("A", "B", 3).toString shouldBe "A ~ B % 3.0" - } - - def `toString of multilabeled edge`: Unit = { - import edges.multilabeled.* - - WUnDiEdge('a', 'b', 2).toString shouldBe "a ~ b %% 2.0" - WUnDiEdge("A", "B", 3).toString shouldBe "A ~ B %% 3.0" - } - - def `mixed infix constructors`: Unit = { - import edges.UnDiEdgeImplicits - import edges.labeled.* - import edges.multilabeled.* - - 1 ~ 2 % 3.2 shouldBe a[edges.labeled.WUnDiEdge[_]] - 1 ~ 2 %% 3.2 shouldBe a[edges.multilabeled.WUnDiEdge[_]] - } - - def `mixed infix extractors`: Unit = { - import edges.UnDiEdgeImplicits - import edges.labeled.* - import edges.multilabeled.* - - 1 ~ 2 % 3.2 match { - case n1 :~ n2 % w => (n1, n2, w) shouldBe (1, 2, 3.2) - } - 1 ~ 2 %% 3.2 match { - case n1 ::~ n2 %% w => (n1, n2, w) shouldBe (1, 2, 3.2) - } - } -} - -private class EditingLabeledEdges[G[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E, G]]( - val factory: GenericGraphCoreFactory[G] -) extends RefSpec - with Matchers { - - def `generic directed labeled edge`: Unit = { - import edges.labeled.LDiEdge - - case class MyEdge[+N](source: N, target: N, label: String) extends LDiEdge[N, String] - - val e = MyEdge(1, 2, "") - e shouldEqual DiEdge(1, 2) - - val g = factory.from(e :: Nil) - "g.map(_.toString)" shouldNot compile - } - - def `generic directed, mappable labeled edge`: Unit = { - import edges.labeled.* - - case class MyEdge[+N](source: N, target: N, label: String) - extends LDiEdge[N, String] - with GenericEdgeMapper[MyEdge] { - def map[N](source: N, target: N) = copy(source, target) - } - - val e = MyEdge(1, 2, "") - e shouldEqual DiEdge(1, 2) - - val g = factory.from(e :: Nil) - g.map(_.toString).edges.toOuter.head shouldBe MyEdge("1", "2", "") - - import edges.DiEdgeImplicits - implicit class MyInfixConstructor[N](val edge: DiEdge[N]) - extends LDiEdgeInfixConstructor[N, String, MyEdge](MyEdge.apply) - 1 ~> 2 :+ "" shouldEqual e - - import generic.{UnapplyGenericLabel, UnapplyGenericLabeledEdge} - object :~> extends UnapplyGenericLabeledEdge[MyEdge, String] - object +: extends UnapplyGenericLabel[String] - - e match { - case n1 :~> n2 +: label => - val reconstructed = MyEdge(n1, n2, label) - "reconstructed: MyEdge[Int]" should compile - reconstructed shouldEqual e - } - } - - def `generic undirected labeled edge`: Unit = { - import edges.labeled.LUnDiEdge - - case class MyEdge[+N](source: N, target: N, label: String) extends LUnDiEdge[N, String] - - val e = MyEdge(1, 2, "") - e shouldEqual UnDiEdge(1, 2) - - val g = factory.from(e :: Nil) - "g.map(_.toString)" shouldNot compile - } - - def `generic undirected, mappable labeled edge`: Unit = { - import edges.labeled.* - - case class MyEdge[+N](source: N, target: N, label: String) - extends LUnDiEdge[N, String] - with GenericEdgeMapper[MyEdge] { - def map[N](node_1: N, node_2: N) = copy(node_1, node_2) - } - - val e = MyEdge(1, 2, "") - e shouldEqual UnDiEdge(1, 2) - - val g = factory.from(e :: Nil) - g.map(_.toString).edges.toOuter.head shouldBe MyEdge("1", "2", "") - - import edges.UnDiEdgeImplicits - implicit class MyInfixConstructor[N](val edge: UnDiEdge[N]) - extends LUnDiEdgeInfixConstructor[N, String, MyEdge](MyEdge.apply) - 1 ~ 2 :+ "" shouldEqual e - - import generic.{UnapplyGenericLabel, UnapplyGenericLabeledEdge} - object :~ extends UnapplyGenericLabeledEdge[MyEdge, String] - object +: extends UnapplyGenericLabel[String] - - e match { - case n1 :~ n2 +: label => - val reconstructed = MyEdge(n1, n2, label) - "reconstructed: MyEdge[Int]" should compile - reconstructed shouldEqual e - } - } - - def `generic directed labeled multiedge`: Unit = { - import edges.multilabeled.LDiEdge - - case class MyEdge[+N](source: N, target: N, label: String) extends LDiEdge[N, String] - - val e = MyEdge(1, 2, "") - e shouldNot equal(DiEdge(1, 2)) - - val g = factory.from(e :: Nil) - "g.map(_.toString)" shouldNot compile - } - - def `generic directed, mappable labeled multiedge`: Unit = { - import edges.multilabeled.* - - case class MyEdge[+N](source: N, target: N, label: String) - extends LDiEdge[N, String] - with GenericEdgeMapper[MyEdge] { - def map[N](source: N, target: N) = copy(source, target) - } - - val e = MyEdge(1, 2, "") - e shouldNot equal(DiEdge(1, 2)) - - val g = factory.from(e :: Nil) - g.map(_.toString).edges.toOuter.head shouldBe MyEdge("1", "2", "") - - import edges.DiEdgeImplicits - implicit class MyInfixConstructor[N](val edge: DiEdge[N]) - extends LDiEdgeInfixConstructor[N, String, MyEdge](MyEdge.apply) - 1 ~> 2 :++ "" shouldEqual e - - import generic.{UnapplyGenericLabel, UnapplyGenericLabeledEdge} - object :~> extends UnapplyGenericLabeledEdge[MyEdge, String] - object ++: extends UnapplyGenericLabel[String] - - e match { - case n1 :~> n2 ++: label => - val reconstructed = MyEdge(n1, n2, label) - "reconstructed: MyEdge[Int]" should compile - reconstructed shouldEqual e - } - } - - def `generic undirected labeled multiedge`: Unit = { - import edges.multilabeled.LUnDiEdge - - case class MyEdge[+N](source: N, target: N, label: String) extends LUnDiEdge[N, String] - - val e = MyEdge(1, 2, "") - e shouldNot equal(UnDiEdge(1, 2)) - - val g = factory.from(e :: Nil) - "g.map(_.toString)" shouldNot compile - } - - def `generic undirected, mappable labeled multiedge`: Unit = { - import edges.multilabeled.* - - case class MyEdge[+N](source: N, target: N, label: String) - extends LUnDiEdge[N, String] - with GenericEdgeMapper[MyEdge] { - def map[N](node_1: N, node_2: N) = copy(node_1, node_2) - } - - val e = MyEdge(1, 2, "") - e shouldNot equal(UnDiEdge(1, 2)) - - val g = factory.from(e :: Nil) - g.map(_.toString).edges.toOuter.head shouldBe MyEdge("1", "2", "") - - import edges.UnDiEdgeImplicits - implicit class MyInfixConstructor[N](val edge: UnDiEdge[N]) - extends LUnDiEdgeInfixConstructor[N, String, MyEdge](MyEdge.apply) - 1 ~ 2 :++ "" shouldEqual e - - import generic.{UnapplyGenericLabel, UnapplyGenericLabeledEdge} - object :~ extends UnapplyGenericLabeledEdge[MyEdge, String] - object ++: extends UnapplyGenericLabel[String] - - e match { - case n1 :~ n2 ++: label => - val reconstructed = MyEdge(n1, n2, label) - "reconstructed: MyEdge[Int]" should compile - reconstructed shouldEqual e - } - } - - /* TODO - def `isMulti ` { - import edge.WkDiEdge - def multi(g: CC[Int, UnDiEdge], expected: Boolean): Unit = g.isMulti should be(expected) - val (wDi_1, wDi_2) = (WkDiEdge(1, 2)(0), WkDiEdge(1, 2)(1)) - - multi(factory(1 ~ 2), false) - multi(factory(1 ~ 2, 1 ~> 2), false) - multi(factory(wDi_1, wDi_2), true) - } - - def `isDirected ` { - def directed(g: CC[Int, UnDiEdge], expected: Boolean): Unit = g.isDirected should be(expected) - val wDi = edge.WDiEdge(1, 2)(0) - - factory(wDi).isDirected should be(true) - directed(factory(wDi), true) - directed(factory(0 ~> 1, wDi), true) - } - */ -} - -private class EditingLabeledMutable extends RefSpec with Matchers { - object `mutable graphs with labeled edges` { // TODO - /* - def `satisfy labeled edege equality` { - import edge.Implicits._ - import edge.LDiEdge - - type StringLabel = Option[String] - val str = "A" - val label: StringLabel = Some(str) - val g = mutable.Graph(2 ~ 3, (2 ~+# 3) (label)) - g should have('order(2), 'size(2)) - - import edge.LBase.{LEdgeImplicits} - object StringLabelImplicit extends LEdgeImplicits[StringLabel] - import StringLabelImplicit._ - for (e <- g.edges if e.isLabeled) { - e.isDefined should be(true) - e.get should be(str) - } - - type ListLabel = List[Int] - implicit val factory = LDiEdge - val listLabel = List(1, 0, 1) - g.addLEdge(3, 4)(listLabel) should be(true) - g should have('order(3), 'size(3)) - val findAdded = g.edges find (3 ~> 4) - findAdded should be('isDefined) - val added: g.EdgeT = findAdded.get - added.directed should be(true) - added.count(_ > 0) should be(List(1, 0, 1).count(_ > 0)) - } - - def `are upsertable` { - import edge.LDiEdge - val (label, modLabel) = ("A", "B") - val g = mutable.Graph(LDiEdge(1, 2)(label), LDiEdge(2, 3)(label)) - - g.edges foreach { - _.edge match { - case LDiEdge(s, t, l) => g upsert (LDiEdge(s.value, t.value)(modLabel)) - } - } - g should have('size (2)) - g.edges foreach { _.label should be(modLabel) } - } - */ - } -} diff --git a/coreTestScala3/src/test/scala/scalax/collection/EditingSpec.scala b/coreTestScala3/src/test/scala/scalax/collection/EditingSpec.scala deleted file mode 100644 index 423ed917..00000000 --- a/coreTestScala3/src/test/scala/scalax/collection/EditingSpec.scala +++ /dev/null @@ -1,462 +0,0 @@ -package scalax.collection - -import org.scalatest.matchers.should.Matchers -import org.scalatest.Suites -import org.scalatest.refspec.RefSpec -import scalax.collection.edges._ -import scalax.collection.generic._ -import scalax.collection.OuterImplicits._ -import scalax.collection.config.GraphConfig - -/** Editing any kind of non-hypergraph with unlabeled edges including mixed graphs. - */ -class EditingSpec - extends Suites( - new EditingEdges, - new Editing[immutable.Graph](new ConfigWrapper[immutable.Graph] { - val companion = immutable.Graph - val config = immutable.Graph.defaultConfig - }), - new Editing[mutable.Graph](new ConfigWrapper[mutable.Graph] { - val companion = mutable.Graph - val config = mutable.Graph.defaultConfig - }), - new EditingImmutable, - new EditingMutable - ) - -private class EditingEdges extends RefSpec with Matchers { - - def `toString of edges`: Unit = { - DiEdge(1, 2).toString shouldBe "1 ~> 2" - UnDiEdge(1, 2).toString shouldBe "1 ~ 2" - } -} - -class EditingImmutable extends RefSpec with Matchers { - private val Graph = immutable.Graph - - Graph.from(1 ~ 2 :: Nil) - - object `graphs ` { - def `+ Int`: Unit = { - val g = Graph(1, 2 ~ 3) - g + 1 should be(g) - g + 0 should be(Graph(0, 1, 2, 3, 2 ~ 3)) - g + 0 ~ 1 should be(Graph(0, 1, 2, 3, 0 ~ 1, 2 ~ 3)) - } - - val gString_A = Graph[String, AnyEdge]("A") - - def `- `: Unit = { - val g_1 = gString_A - "B" - g_1.order should be(1) - - val g = gString_A - "A" - g.nodes shouldNot contain("A") - g should have size 0 - g shouldBe empty - - val h = Graph(1, 2, 2 ~ 3) - h - 0 should be(h) - h - 1 should be(Graph(2, 2 ~ 3)) - h - 2 should be(Graph(1, 3)) - } - - def `-- `: Unit = { - val g = Graph(1, 2 ~ 3, 3 ~ 4) - g -- (List(2), List(3 ~ 3)) should be(Graph(1, 3 ~ 4)) - g -- (List(2), List(3 ~ 4)) should be(Graph(1, 3, 4)) - } - - def `+ String `: Unit = { - val g = gString_A + "B" - g.elementCount shouldBe 2 - g.nodes should contain("A") - g.nodes should contain("B") - - val hString_A = Graph[String, UnDiEdge]("A") - val h = hString_A + "A" ~ "C" - h.nodes should have size 2 - h.edges should have size 1 - h.elementCount should be(3) - } - } -} - -private class EditingMutable extends RefSpec with Matchers { - private val Graph = mutable.Graph - - object `mutable graphs` { - def `serve += properly`: Unit = { - val g = Graph[Int, Nothing](1, 3) - g addOne 2 - g.order should be(3) - for (i <- 1 to 3) - g.nodes should contain(i) - } - - def `serve -= properly`: Unit = { - val g = Graph(1, 2, 2 ~ 3, 4) - g remove 1 should be(true) - g should be(Graph(2 ~ 3, 4)) - g remove 5 should be(false) - g subtractOne 2 should be(Graph(3, 4)) - g.clear() - g should be(Symbol("empty")) - } - - def `+ String `: Unit = { - val g = Graph("A") addOne "B" - g.elementCount shouldBe 2 - g.contains("A") shouldBe true - g.contains("B") shouldBe true - - val hString_A = Graph[String, UnDiEdge]("A") - val h = hString_A += "A" ~ "C" - h.nodes should have size 2 - h.edges should have size 1 - h.elementCount should be(3) - } - - def `serve -= properly (2)`: Unit = { - val g = Graph(1 ~ 2, 2 ~ 3) - g subtractOne 2 should be(Graph(1, 3)) - g.size should be(0) - } - - def `serve 'directed' properly`: Unit = { - val (di, unDi) = (1 ~> 2, 2 ~ 3) - val g = Graph[Int, AnyEdge](unDi) - def directed(expected: Boolean): Unit = g.isDirected should be(expected) - - directed(false) - g.clear(); directed(false) - g += di; directed(true) - g += unDi; directed(false) - g.clear(); directed(false) - } - - def `serve 'diSuccessors', 'outNeighbors' when directed`: Unit = { - val (one, two, oneOne, oneTwo) = (1, 2, 1 ~> 1, 1 ~> 2) - val g = Graph(oneOne, oneTwo, one ~> 3, one ~> 4) - val (n1, n2) = (g get one, g get two) - val e11 = g get oneOne - - g subtractOne 1 ~> 4 // Graph(oneOne, oneTwo, one~>3) - n2.diSuccessors shouldBe empty - n1.diSuccessors.map(_.outer) shouldBe Set(one, two, 3) - n1.outNeighbors.map(_.outer) shouldBe Set(two, 3) - n1 findOutgoingTo n1 should be(Some(e11)) - - g subtractOne oneTwo // Graph(oneOne, one~>3) - n1.diSuccessors should be(Set(one, 3)) - n1.outNeighbors should be(Set(3)) - n1 findOutgoingTo n1 should be(Some(e11)) - - g subtractOne oneOne // Graph(one~>3) - n1.diSuccessors should be(Set(3)) - n1.outNeighbors should be(Set(3)) - n1 findOutgoingTo n1 should be(None) - - g ++= (edges = List(oneOne, oneTwo)) // Graph(oneOne, oneTwo, one~>3) - n1.diSuccessors should be(Set(one, two, 3)) - n1.outNeighbors should be(Set(two, 3)) - n1 findOutgoingTo n1 should be(Some(e11)) - } - - def `serve ++=, unionInPlace`: Unit = { - val (gBefore, gAfter) = (Graph(1, 2 ~ 3), Graph(0, 1 ~ 2, 2 ~ 3)) - (gBefore ++= (0 :: Nil, List(1 ~ 2, 2 ~ 3))) should equal(gAfter) - (gBefore |= Graph(0, 1 ~ 2)) should equal(gAfter) - (gBefore |= Graph[Int, UnDiEdge](0) |= Graph(1 ~ 2)) should equal(gAfter) - } - } -} - -private class Editing[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E, CC]](val factory: ConfigWrapper[CC]) - extends RefSpec - with Matchers { - - info("factory = " + factory.companion.getClass) - - implicit private val config: GraphConfig = factory.config - - private val seq_1_3 = Seq(1, 3) - private val gInt_1_3 = factory.from(nodes = seq_1_3, Nil) - private val gString_A = factory("A") - - object `graph editing` { - def `empty `: Unit = { - val eg = factory.empty[Nothing, Nothing] - eg shouldBe empty - eg should have size 0 - } - - def `apply `: Unit = { - gInt_1_3 should not be empty - gInt_1_3.order should be(2) - gInt_1_3(0) shouldBe false - gInt_1_3(1) shouldBe true - gInt_1_3(2) shouldBe false - gInt_1_3(3) shouldBe true - } - - def `nodes of ADT fixes #40`: Unit = { - sealed trait Node - case class N1() extends Node - case class N2() extends Node - factory(N1() ~> N2(), N1() ~> N1()): CC[Node, DiEdge[Node]] // should typeCheck - } - - def `isDirected `: Unit = { - def directed(g: CC[Int, AnyEdge[Int]], expected: Boolean): Unit = g.isDirected should be(expected) - - directed(factory(1 ~ 2), false) - directed(factory(1 ~> 2), true) - } - - def `from `: Unit = { - val (n_start, n_end) = (11, 20) - val nodes = List.range(n_start, n_end) - val edges = List[DiEdge[Int]](14 ~> 16, 16 ~> 18, 18 ~> 20, 20 ~> 22) - val g = factory.from(nodes, edges) - g.nodes.size should be(nodes.size + 2) - g.edges.size should be(edges.size) - } - - def `contains `: Unit = { - seq_1_3 foreach (n => gInt_1_3 contains n should be(true)) - gInt_1_3.iterator.next() shouldBe a[gInt_1_3.InnerNode] - } - - def `toString `: Unit = { - gInt_1_3.toString shouldBe "Graph(NodeSet(1, 3), EdgeSet())" - gString_A.toString shouldBe """Graph(NodeSet(A), EdgeSet())""" - } - - def `render `: Unit = { - import ToString._ - gInt_1_3.render(SetElemsOnSeparateLines()) shouldBe - """Graph( - | NodeSet( - | 1, - | 3), - | EdgeSet())""".stripMargin - val g = factory(1 ~ 2) - g.render(SetsOnSeparateLines()) shouldBe - """Graph( - | NodeSet(1, 2), - | EdgeSet(1 ~ 2))""".stripMargin - g.render(SetElemsOnSeparateLines()) shouldBe - """Graph( - | NodeSet( - | 1, - | 2), - | EdgeSet( - | 1 ~ 2))""".stripMargin - g.render(SetElemsOnSeparateLines(), withInnerPrefix = false) shouldBe - """Graph( - | 1, - | 2, - | 1 ~ 2)""".stripMargin - } - - def `from inner `: Unit = { - val gn = factory(2, 3) - factory.from[Int, Nothing](gn.nodes.outerIterable, Nil) should equal(gn) - - val g = factory(2 ~ 3) - factory(g.edges.head) should equal(g) - factory.from(g.edges.outerIterable) should equal(g) - } - - def `NodeSet `: Unit = { - val o = Vector.range(0, 4) - val g = factory(o(1) ~ o(2), o(2) ~ o(3)) - val n = o map (g.nodes find _ getOrElse g.nodes.head) - - val less = g.nodes - n(3) - g.order shouldBe 3 - less should have size 2 - less should contain(n(1)) - less.find(_ == n(1)).get.edges should have size 1 - less should contain(n(2)) - less.find(_ == n(2)).get.edges should have size 2 - - val restored = less.toSet + n(3) - restored should have size 3 - restored should contain(n(3)) - restored.find(_ == n(1)).get.edges should have size 1 - } - - def `EdgeAssoc `: Unit = { - val e = 1 ~ 2 - e shouldBe an[UnDiEdge[_]] - - val g = factory(3 ~ 4) - g.edges should contain(3 ~ 4) - - val d = 1 ~> 2 - d shouldBe a[DiEdge[_]] - d.source should be(1) - d.target should be(2) - factory[Int, AnyEdge](1, d, 1 ~ 4).nodes should have size 3 - } - - def `concat, ++, union`: Unit = { - val diEdge = 1 ~> 2 - factory.empty[Int, DiEdge[Int]] ++ List(diEdge) shouldBe factory.from(diEdge :: Nil) - - val g = gString_A.concat[String, Edge[String]](List("B", "C"), Nil) - g.elementCount shouldEqual 3 - 'A' to 'C' map (_.toString) foreach (g.contains(_) shouldEqual true) - - val (gBefore, gAfter) = (factory(1, 2 ~ 3), factory(0, 1 ~ 2, 2 ~ 3)) - gBefore ++ (edges = List(1 ~ 2, 2 ~ 3), isolatedNodes = List(0)) shouldEqual gAfter - - gBefore union factory(0, 1 ~ 2) shouldEqual gAfter - gBefore union factory[Int, UnDiEdge](0) union factory(1 ~ 2) shouldEqual gAfter - } - - private val gUnDi = factory(1 ~ 1, 1 ~ 2, 1 ~ 3, 1 ~ 4) - private val gDi = factory(1 ~> 1, 1 ~> 2, 1 ~> 3, 1 ~> 4) - private val gMixed = factory[Int, AnyEdge](1 ~> 2, 2 ~> 3, 4 ~ 3) - - object `outNeighbors ` { - def `for UnDi`: Unit = { - (gUnDi get 1).outNeighbors should be(Set(2, 3, 4)) - (gUnDi get 2).outNeighbors should be(Set(1)) - } - def `for Di`: Unit = { - (gDi get 1).outNeighbors should be(Set(2, 3, 4)) - (gDi get 2).outNeighbors should be(Set.empty) - } - def `for mixed`: Unit = { - (gMixed get 2).outNeighbors should be(Set(3)) - (gMixed get 3).outNeighbors should be(Set(4)) - } - } - - object `diSuccessors ` { - def `for UnDi`: Unit = { - (gUnDi get 1).diSuccessors should be(Set(1, 2, 3, 4)) - (gUnDi get 2).diSuccessors should be(Set(1)) - } - def `for Di`: Unit = { - (gDi get 1).diSuccessors should be(Set(1, 2, 3, 4)) - (gDi get 2).diSuccessors should be(Set.empty) - } - def `for mixed`: Unit = { - (gMixed get 2).diSuccessors should be(Set(3)) - (gMixed get 3).diSuccessors should be(Set(4)) - } - } - - object `inNeighbors ` { - def `for UnDi`: Unit = - (gUnDi get 1).inNeighbors should be(Set(2, 3, 4)) - def `for Di`: Unit = { - (gDi get 1).inNeighbors should be(Set.empty) - (gDi get 2).inNeighbors should be(Set(1)) - } - def `for mixed`: Unit = - (gMixed get 2).diPredecessors should be(Set(1)) - } - - object `diPredecessors ` { - def `for UnDi`: Unit = - (gUnDi get 1).diPredecessors should be(Set(1, 2, 3, 4)) - def `for Di`: Unit = { - (gDi get 1).diPredecessors should be(Set(1)) - (gDi get 2).diPredecessors should be(Set(1)) - } - def `for mixed`: Unit = - (gMixed get 2).diPredecessors should be(Set(1)) - } - - object `neighbors ` { - def `for UnDi`: Unit = { - (gUnDi get 1).neighbors should be(Set(2, 3, 4)) - (gUnDi get 2).neighbors should be(Set(1)) - } - def `for Di`: Unit = { - (gDi get 1).neighbors should be(Set(2, 3, 4)) - (gDi get 2).neighbors should be(Set(1)) - } - } - - def `findOutgoingTo Di`: Unit = { - val g = factory(1 ~> 1, 1 ~> 2, 2 ~> 1) - def n(i: Int) = g get i - n(1) findOutgoingTo n(2) should be(Some(1 ~> 2)) - n(1) findOutgoingTo n(1) should be(Some(1 ~> 1)) - } - - def `degree `: Unit = { - val g = factory(1 ~ 1, 1 ~ 2, 1 ~ 3, 1 ~ 4) - (g get 1).degree should be(5) - (g get 2).degree should be(1) - } - - def `incoming `: Unit = { - val uEdges = Seq(1 ~ 1, 1 ~ 2, 1 ~ 3, 1 ~ 4) - val g = factory(uEdges(0), uEdges(1), uEdges(2), uEdges(3)) - (g get 1).incoming should be(uEdges.toSet) - (g get 2).incoming should be(Set(uEdges(1))) - - val dEdges = Seq(1 ~> 1, 1 ~> 2, 1 ~> 3, 1 ~> 4) - val h = factory(dEdges(0), dEdges(1), dEdges(2), dEdges(3)) - (h get 1).incoming should be(Set(dEdges(0))) - (h get 2).incoming should be(Set(dEdges(1))) - } - - def `edgeAdjacents UnDi`: Unit = { - val g = factory[Int, AnyEdge](1 ~ 2, 2 ~ 3, 1 ~> 3, 1 ~ 5, 3 ~ 5, 3 ~ 4, 4 ~> 4, 4 ~> 5) - (g get 4 ~> 4).adjacents should be(Set[AnyEdge[Int]](3 ~ 4, 4 ~> 5)) - (g get 1 ~ 2).adjacents should be(Set[AnyEdge[Int]](1 ~> 3, 1 ~ 5, 2 ~ 3)) - } - - def `filter `: Unit = { - val g: AnyGraph[Int, DiEdge[Int]] = factory(2 ~> 3, 3 ~> 1, 5) - g filter (_ > 1) should be(factory(2 ~> 3, 5)) - g filter (_ < 2) should be(factory(1)) - g filter (_ < 2) should be(factory(1)) - g filter (_ >= 2) should be(factory(2 ~> 3, 5)) - g filter (edgeP = _.node1.outer == 2) should be(factory(1, 5, 2 ~> 3)) - g filter (nodeP = _ <= 3, edgeP = _ contains 2) should be(factory(1, 2 ~> 3)) - } - - def `match `: Unit = { - val di = 1 ~> 2 - (di match { case DiEdge(src, _) => src }) should be(1) - (di match { case src ~> trg => src + trg }) should be(3) - - val unDi = 1 ~ 2 - (unDi match { case UnDiEdge(n1, _) => n1 }) should be(1) - (unDi match { case n1 ~ n2 => n1 + n2 }) should be(3) - } - - def `foldLeft, foldLeftOuter `: Unit = { - val g = factory(1 ~> 2, 2 ~> 3, 7) - - val sumOfNodes = 13 - g.nodes.foldLeft(0)((cum, n) => cum + n.outer) shouldBe sumOfNodes - g.nodes.foldLeftOuter(0)((cum, n) => cum + n) shouldBe sumOfNodes - - val edgeProducts = 2 + 6 - g.edges.foldLeft(0)((cum, e) => cum + e.outer.source * e.outer.target) shouldBe edgeProducts - g.edges.foldLeftOuter(0)((cum, e) => cum + e.source * e.target) shouldBe edgeProducts - - val expected = sumOfNodes + edgeProducts - g.foldLeft(0)( - (cum, n) => cum + n.outer, - (cum, e) => cum + e.outer.source * e.outer.target - ) shouldBe expected - g.foldLeftOuter(0)( - (cum, n) => cum + n, - (cum, e) => cum + e.source * e.target - ) shouldBe expected - } - } -} diff --git a/coreTestScala3/src/test/scala/scalax/collection/EditingTypedHyperSpec.scala b/coreTestScala3/src/test/scala/scalax/collection/EditingTypedHyperSpec.scala deleted file mode 100644 index 8f88e43d..00000000 --- a/coreTestScala3/src/test/scala/scalax/collection/EditingTypedHyperSpec.scala +++ /dev/null @@ -1,5 +0,0 @@ -package scalax.collection - -/** Editing any kind of typed hypergraph including mixed and multigraphs. - */ -class EditingTypedHyperSpec // TODO extends Suites() diff --git a/coreTestScala3/src/test/scala/scalax/collection/EditingTypedSpec.scala b/coreTestScala3/src/test/scala/scalax/collection/EditingTypedSpec.scala deleted file mode 100644 index e080d440..00000000 --- a/coreTestScala3/src/test/scala/scalax/collection/EditingTypedSpec.scala +++ /dev/null @@ -1,140 +0,0 @@ -package scalax.collection - -import java.time.DayOfWeek.* -import java.time.LocalTime - -import scala.concurrent.duration.* - -import org.scalatest.Suites -import org.scalatest.matchers.should.Matchers -import org.scalatest.refspec.RefSpec -import scalax.collection.edges.DiEdge -import scalax.collection.generic.* -import scalax.collection.labeled.aviation.* - -class EditingTypedSpec - extends Suites( - new EditingTypedEdges, - new EditingTyped(immutable.FlightGraph, scalax.collection.immutable.Graph), - new EditingTyped(mutable.FlightGraph, scalax.collection.mutable.Graph) - ) - -private object Samples { - val (madrid, rio) = (Airport("MAD"), Airport("GIG")) - val flightNo = "IB 8711" - val outer = Flight( - madrid, - rio, - flightNo, - List( - TUESDAY -> LocalTime.of(8, 20), - SATURDAY -> LocalTime.of(8, 20) - ), - 12.hour + 30.minutes - ) -} - -private class EditingTypedEdges extends RefSpec with Matchers { - import Samples.* - - def `toString of typed edge`: Unit = - outer.toString should startWith(s"$madrid ~> $rio :++ ") -} - -private class EditingTyped[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E, CC]]( - typedFactory: TypedGraphCoreFactory[Airport, Flight, CC], - genericFactory: GenericGraphCoreFactory[CC] -) extends RefSpec - with Matchers - with IntelliJ[CC] { - import Samples.* - - object `Custom edge 'Flight'` { - def `edge methods`: Unit = { - - val g = typedFactory.from(Nil, edges = outer :: Nil).asAnyGraph - - val e = g.edges.head - e.ends.head.getClass should be(g.nodes.head.getClass) - e.departure shouldBe madrid - e.destination shouldBe rio - e.flightNo shouldBe flightNo - e shouldBe outer - e.## should be(outer.##) - - val eqFlight = Flight(madrid, rio, flightNo, Nil, outer.duration + 1.minute) - e shouldBe eqFlight - e.## should be(eqFlight.##) - - val neFlight = Flight(madrid, rio, flightNo + "x", outer.departures, outer.duration) - e should not be neFlight - e.## should not be neFlight.## - } - - def `edge equality`: Unit = { - val outer_1 = Flight(madrid, rio, flightNo, outer.departures, outer.duration + 1.minute) - val outer_2 = Flight(madrid, rio, flightNo + "x", outer.departures, outer.duration + 1.minute) - - outer_1 should not equal outer_2 - - val g = typedFactory.from(outer_1 :: outer_2 :: Nil).asAnyGraph - g.edges.toList match { - case inner_1 :: inner_2 :: Nil => - inner_1 should not equal inner_2 - inner_1 should equal(outer_1) - case _ => fail() - } - - outer shouldNot equal(DiEdge(madrid, rio)) - DiEdge(madrid, rio) shouldNot equal(outer) - } - - def `infix constructor`: Unit = { - import scalax.collection.edges.DiEdgeImplicits - madrid ~> rio :++ (flightNo, outer.departures, outer.duration) shouldBe outer - } - - def `extractor `: Unit = { - val g = typedFactory.empty.asAnyGraph - - g.nodes foreach { case g.InnerNode(inner, Airport(code)) => - code -> inner.outDegree - } - g.nodes.outerIterator foreach { case Airport(code) => - code - } - g.edges foreach { case g.InnerEdge(g.InnerDiEdge(source, _), Flight(_, _, no, _, _)) => - no -> source.outDegree - } - g.edges.outerIterator foreach { case Flight(from, to, no, _, _) => - (from.code, to.code, no) - } - } - - def `concat adding a generic edge`: Unit = { - val g = typedFactory.empty.asAnyGraph - - g ++ List(outer) shouldBe typedFactory.from(outer :: Nil) - "g ++ List(outer): AnyFlightGraph" should compile - - val diEdge = DiEdge(outer.source, outer.target) - val widened = g ++ List(diEdge) - "widened: AnyFlightGraph" shouldNot compile - "widened: AnyGraph[Airport, DiEdge[Airport]]" shouldNot compile - "widened: AnyGraph[Airport, AnyDiEdge[Airport] with EdgeMapper with DiEdgeToString]" should compile - } - - def `concat adding a typed edge`: Unit = { - val g = genericFactory.empty[Airport, DiEdge[Airport]].asAnyGraph - - val diEdge = DiEdge(outer.source, outer.target) - g ++ List(diEdge) shouldBe genericFactory.from(diEdge :: Nil) - "g ++ List(diEdge): AnyGraph[Airport, DiEdge[Airport]]" should compile - - val widened = g ++ List(outer) - "widened: AnyFlightGraph" shouldNot compile - "widened: AnyGraph[Airport, DiEdge[Airport]]" shouldNot compile - "widened: AnyGraph[Airport, AnyDiEdge[Airport] with EdgeMapper with DiEdgeToString]" should compile - } - } -} diff --git a/coreTestScala3/src/test/scala/scalax/collection/EqualityHyperSpec.scala b/coreTestScala3/src/test/scala/scalax/collection/EqualityHyperSpec.scala deleted file mode 100644 index d1fcbd75..00000000 --- a/coreTestScala3/src/test/scala/scalax/collection/EqualityHyperSpec.scala +++ /dev/null @@ -1,75 +0,0 @@ -package scalax.collection - -import org.scalatest.matchers.should.Matchers -import org.scalatest.refspec.RefSpec -import scalax.collection.generic.AbstractDiHyperEdge - -class EqualityHyperSpec extends RefSpec with Matchers { - - def `hyperedges, bag like `: Unit = { - import scalax.collection.hyperedges.* - - val nodes = List('A', 'B', 'C', 'C') - val hEdge = HyperEdge.fromUnsafe(nodes) - hEdge shouldEqual 'A' ~~ 'B' ~~ 'C' ~~ 'C' - hEdge shouldEqual 'C' ~~ 'C' ~~ 'B' ~~ 'A' - - hEdge.arity shouldBe nodes.size - for (i <- nodes.indices) - hEdge.node(i) shouldBe nodes(i) - } - - def `hyperedges, ordered `: Unit = { - import scalax.collection.hyperedges.ordered.* - - val nodes = List('A', 'B', 'C', 'C') - val hEdge = HyperEdge.fromUnsafe(nodes) - hEdge shouldEqual 'A' ~~ 'B' ~~ 'C' ~~ 'C' - hEdge shouldNot equal('C' ~~ 'C' ~~ 'B' ~~ 'A') - - hEdge.arity shouldBe nodes.size - for (i <- nodes.indices) - hEdge.node(i) shouldBe nodes(i) - } - - def `directed hyperedges, bag like`: Unit = { - import scalax.collection.hyperedges.* - - val sources = OneOrMore('A', 'B', 'C') - val targets = OneOrMore('D', 'D', 'E') - val dhEdge = DiHyperEdge(sources, targets) - dhEdge shouldEqual sources ~~> targets - dhEdge shouldEqual sources.reverse ~~> targets.reverse - - dhEdge.ends.toList should contain theSameElementsAs (sources ++ targets).toList - - val sourcesSize = sources.size - dhEdge.arity shouldBe sourcesSize + targets.size - - checkIndices(sources, dhEdge) - checkIndices(targets, dhEdge, sourcesSize) - } - - def `directed hyperedges, ordered`: Unit = { - import scalax.collection.hyperedges.ordered.* - - val sources = OneOrMore('A', 'B', 'C') - val targets = OneOrMore('D', 'D', 'E') - val dhEdge = DiHyperEdge(sources, targets) - dhEdge shouldEqual sources ~~> targets - dhEdge shouldNot equal(sources.reverse ~~> targets.reverse) - - dhEdge.ends.toList should contain theSameElementsAs (sources ++ targets).toList - - val sourcesSize = sources.size - dhEdge.arity shouldBe sourcesSize + targets.size - - checkIndices(sources, dhEdge) - checkIndices(targets, dhEdge, sourcesSize) - } - - private def checkIndices(s: OneOrMore[_], dhEdge: AbstractDiHyperEdge[_], plus: Int = 0): Unit = { - val list = s.iterator.toList - for (i <- list.indices) dhEdge.node(i + plus) shouldBe list(i) - } -} diff --git a/coreTestScala3/src/test/scala/scalax/collection/EqualitySpec.scala b/coreTestScala3/src/test/scala/scalax/collection/EqualitySpec.scala deleted file mode 100644 index f0db8edb..00000000 --- a/coreTestScala3/src/test/scala/scalax/collection/EqualitySpec.scala +++ /dev/null @@ -1,73 +0,0 @@ -package scalax.collection - -import org.scalatest.Suites -import org.scalatest.matchers.should.Matchers -import org.scalatest.refspec.RefSpec -import scalax.collection.OuterImplicits.* -import scalax.collection.edges.* -import scalax.collection.generic.{Edge, GenericGraphCoreFactory} - -class EqualitySpec - extends Suites( - new Equality[immutable.Graph](immutable.Graph), - new Equality[mutable.Graph](mutable.Graph), - new EqualityMixed - ) - -private class Equality[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E, CC]]( - val factory: GenericGraphCoreFactory[CC] -) extends RefSpec - with Matchers { - info(factory.getClass.getPackage.toString) - - private val seq_1_3 = Seq(1, 3) - private val gInt_1_3 = factory(seq_1_3.toOuterElems[DiEdge[Int]]: _*) - private val gString_A = factory("A") - - def `Graph equals`: Unit = { - factory[Int, Nothing]() shouldEqual factory[Int, DiEdge]() - gInt_1_3 shouldEqual factory(1, 3) - gString_A shouldEqual factory("A") - - factory() shouldNot be(factory(1)) - gInt_1_3 shouldNot be(factory(2, 3)) - gString_A shouldNot be(factory("B")) - - gInt_1_3 shouldEqual immutable.Graph(1) + 3 - } - - def `Graph hashCode`(): Unit = { - factory(1).hashCode shouldBe factory(1).hashCode - factory(1).hashCode shouldNot be(factory(2).hashCode) - - factory(1 ~> 2).hashCode shouldBe factory(1 ~> 2).hashCode - factory(1 ~> 2).hashCode shouldNot be(factory(2 ~> 1).hashCode) - - factory(1 ~ 2).hashCode shouldBe factory(1 ~ 2).hashCode - factory(1 ~ 2).hashCode shouldNot be(factory(1 ~ 3).hashCode) - - factory(1 ~ 2, 2 ~ 3).hashCode shouldBe factory(1 ~ 2, 2 ~ 3).hashCode - factory(1 ~ 2, 2 ~ 3).hashCode shouldNot be(factory(1 ~ 2, 3 ~ 3).hashCode) - } -} - -private class EqualityMixed extends RefSpec with Matchers { - - val oEdgesG = List(1 ~ 2, 2 ~ 3, 2 ~ 4, 3 ~ 5, 4 ~ 5) - val oEdgesH = List(3 ~ 4, 3 ~ 5, 4 ~ 6, 5 ~ 6) - - val (iFactory, mFactory) = (immutable.Graph, mutable.Graph) - - def initG = (iFactory(oEdgesG: _*), mFactory(oEdgesG: _*)) - def initH = (iFactory(oEdgesH: _*), mFactory(oEdgesH: _*)) - - object `equals works properly` { - def `over immutable and mutable graphs`: Unit = { - val (iG, mG) = initG - iG should ===(mG) - - val (iH, mH) = initH - iH should ===(mH) - } - } -} diff --git a/coreTestScala3/src/test/scala/scalax/collection/IntelliJ.scala b/coreTestScala3/src/test/scala/scalax/collection/IntelliJ.scala deleted file mode 100644 index dee95102..00000000 --- a/coreTestScala3/src/test/scala/scalax/collection/IntelliJ.scala +++ /dev/null @@ -1,13 +0,0 @@ -package scalax.collection - -import scalax.collection.generic.Edge - -trait IntelliJ[C[N, E <: Edge[N]] <: GraphLike[N, E, C] with AnyGraph[N, E]] { - - /** Moves lots of false positive IntelliJ errors on the higher kinded type parameter `C` to this single point. - * Using this kind of IDE correction is not recommended in production code. - */ - implicit class ForIntelliJ[N, E <: Edge[N]](val g: C[N, E]) { - def asAnyGraph: AnyGraph[N, E] = g - } -} diff --git a/coreTestScala3/src/test/scala/scalax/collection/MappingHyperSpec.scala b/coreTestScala3/src/test/scala/scalax/collection/MappingHyperSpec.scala deleted file mode 100644 index 6ab8a51f..00000000 --- a/coreTestScala3/src/test/scala/scalax/collection/MappingHyperSpec.scala +++ /dev/null @@ -1,143 +0,0 @@ -package scalax.collection - -import org.scalatest.matchers.should.Matchers -import org.scalatest.refspec.RefSpec - -import scalax.collection.OneOrMore.{more, one} -import scalax.collection.OuterImplicits.* -import scalax.collection.hyperedges.* -import scalax.collection.immutable.Graph - -class MappingHyperSpec extends RefSpec with Matchers { - - object `mapping a generic hypergraph you can` { - val g = Graph( - 1 ~~ 2 ~~ 3, - 3 ~~ 4 ~~ 1 - ) - - def `map by identity`: Unit = - g.map(identity) shouldEqual g - - def `map nodes`: Unit = - g.map(_.toString) shouldEqual Graph("1" ~~ "2" ~~ "3", "3" ~~ "4" ~~ "1") - - def `map both nodes and edges`: Unit = - g.mapHyper( - fNode = _.outer + 1, // increment node values - fHyperEdge = (newEnds: Several[Int]) => newEnds(0) ~~ newEnds(1) // add only the first two ends - ) shouldEqual Graph(2 ~~ 3, 4 ~~ 5) - } - - object `mapping a generic directed hypergraph you can` { - val g = Graph( - more(1, 2) ~~> one(3), - one(3) ~~> more(4, 1) - ) - - def `map by identity`: Unit = - g.map(identity) shouldEqual g - - def `map nodes`: Unit = - g.map(_.toString) shouldEqual Graph(more("1", "2") ~~> one("3"), one("3") ~~> more("4", "1")) - - def `map both nodes and edges`: Unit = - g.mapDiHyper( - fNode = _.outer + 1, // increment node values - fDiHyperEdge = - // keep newSources as sources, add newSources to targets - (newSources: OneOrMore[Int], newTargets: OneOrMore[Int]) => newSources ~~> (newTargets ++ newSources) - ) shouldEqual Graph( - more(2, 3) ~~> more(4, 2, 3), - one(4) ~~> more(5, 2, 4) - ) - } - - object `flat-mapping a generic hypergraph you can` { - val g = Graph( - 1 ~~ 2 ~~ 3, - 3 ~~ 4 ~~ 1 - ) - - def `map elements, change node type and edge cardinality`: Unit = - g.flatMapHyper( - fNode = (n: g.NodeT) => - /* nodes will be mapped to - 1 -> 2 - 2 -> 2, -2 - 3 -> 4 - 4 -> 4, -4 - */ - if (n.outer % 2 == 0) n.toString :: (-n).toString :: Nil - else (n.outer + 1).toString :: Nil, - - // `fromUnsafe` is fine here because we know that `fNode` returns at least one mapped node - fHyperEdge = (nn: Seq[String]) => HyperEdge.fromUnsafe(nn) :: Nil - ) shouldEqual Graph( - "-2" ~~ "2" ~~ "2" ~~ "4", - "-4" ~~ "2" ~~ "4" ~~ "4" - ) - - def `change the graph structure`: Unit = - g.flatMapHyper( - fNode = (n: g.NodeT) => (n.outer + 1) :: Nil, - fHyperEdge = (e: g.EdgeT, nn: Seq[Int]) => - nn match { - case Seq(nn1, nn2, nn3) => - nn1 ~~ nn2 ~~ nn3 ~~ e.ends.iterator.map(_.degree).max :: - nn1 ~~ nn2 ~~ -e.ends.size :: - Nil - case _ => Nil - }, - fDiHyperEdge = None, - fEdge = None - ) shouldEqual Graph( - 2 ~~ 3 ~~ 4 ~~ 2, - 2 ~~ 3 ~~ -3, - 4 ~~ 5 ~~ 2 ~~ 2, - 4 ~~ 5 ~~ -3 - ) - } - - object `flat-mapping a generic directed hypergraph you can` { - val g = Graph( - more(1, 2) ~~> one(3), - one(3) ~~> more(4, 1) - ) - - def `map elements, change node type and edge cardinality`: Unit = - g.flatMapDiHyper( - fNode = (n: g.NodeT) => - /* nodes will be mapped to - 1 -> 2 - 2 -> 2, -2 - 3 -> 4 - 4 -> 4, -4 - */ - if (n.outer % 2 == 0) n.toString :: (-n).toString :: Nil - else (n.outer + 1).toString :: Nil, - fDiHyperEdge = - // `fromUnsafe` is fine here because above `fNode` returns at least one mapped node - (newSources: Seq[String], newTargets: Seq[String]) => DiHyperEdge.fromUnsafe(newSources, newTargets) :: Nil - ) shouldEqual Graph( - more("-2", "2", "2") ~~> one("4"), - one("4") ~~> more("4", "-4", "2") - ) - - def `change the graph structure`: Unit = - g.flatMapDiHyper( - fNode = (n: g.NodeT) => (n.outer + 1) :: Nil, - fDiHyperEdge = (e: g.EdgeT, newSources: Seq[Int], newTargets: Seq[Int]) => - // `fromUnsafe` is fine here because above `fNode` always returns exactly one mapped node - DiHyperEdge.fromUnsafe(newSources, newTargets ++ e.targets.iterator.map(_.outDegree)) :: - DiHyperEdge.fromUnsafe(newTargets, newSources :+ e.sources.size) :: - Nil, - fEdge = None - ) shouldEqual Graph( - more(2, 3) ~~> more(4, 1), - one(4) ~~> more(2, 3, 2), - one(4) ~~> more(5, 2, 0, 1), - more(5, 2) ~~> more(4, 1) - ) - } -} diff --git a/coreTestScala3/src/test/scala/scalax/collection/MappingSpec.scala b/coreTestScala3/src/test/scala/scalax/collection/MappingSpec.scala deleted file mode 100644 index 9ff7afd4..00000000 --- a/coreTestScala3/src/test/scala/scalax/collection/MappingSpec.scala +++ /dev/null @@ -1,123 +0,0 @@ -package scalax.collection - -import org.scalatest.matchers.should.Matchers -import org.scalatest.refspec.RefSpec - -import scalax.collection.OuterImplicits.* -import scalax.collection.edges.* -import scalax.collection.immutable.Graph - -class MappingSpec extends RefSpec with Matchers { - - object `mapping a generic undirected graph you can` { - private val edge = 1 ~ 2 - private val originalG = Graph(edge) - - private def increment(n: originalG.NodeT) = n.outer + 1 - - def `create another graph`: Unit = { - val g = originalG map increment - - g shouldBe a[Graph[Int, UnDiEdge[Int]] @unchecked] - g.nodes.head.outer shouldBe an[Integer] - g.edges.head shouldBe an[g.InnerUnDiEdge] - (g.edges.head.outer: UnDiEdge[Int]) shouldBe an[UnDiEdge[_]] - } - - def `map by nodes`: Unit = { - val g = originalG map increment - - originalG.nodes zip g.nodes.outerIterator foreach { case (original, mapped) => - increment(original) shouldBe mapped - } - g.edges.head shouldBe UnDiEdge(2, 3) - } - - def `change the node type`: Unit = { - val g = originalG map (_.toString) - - g.nodes.head.outer shouldBe a[String] - (g.edges.head.outer: UnDiEdge[String]) shouldBe an[UnDiEdge[_]] - g.edges.head.outer shouldBe (edge.node1.toString ~ edge.node2.toString) - } - - def `change the edge type`: Unit = { - val g = originalG.map(increment, (n1: Int, n2: Int) => n1 ~> n2) - - (g: Graph[Int, DiEdge[Int]]) shouldEqual Graph((edge.node1 + 1) ~> (edge.node2 + 1)) - g.isDirected shouldBe true - } - - def `inspect the edges to be mapped`: Unit = { - val g = - originalG.map( - increment, - (e: originalG.EdgeT, n1: Int, _: Int) => n1 ~> (e.weight.toInt + 2) - ) - (g: Graph[Int, DiEdge[Int]]) shouldEqual Graph(2 ~> 3) - } - - def `change edge ends`: Unit = { - val g = originalG.map(_.outer + 1, (n1: Int, _: Int) => n1 ~> 7) - (g: Graph[Int, DiEdge[Int]]) shouldEqual Graph(3, 2 ~> 7) - } - } - - object `flat-mapping a generic undirected graph you can` { - private val edge = 1 ~ 2 - private val originalG = Graph(edge) - - def increment(n: Graph[Int, UnDiEdge[Int]]#NodeT): List[Int] = n.outer + 1 :: Nil - - def `change nodes`: Unit = { - val g = originalG flatMap increment - - originalG.nodes zip g.nodes.outerIterator foreach { case (original, mapped) => - increment(original).head shouldBe mapped - } - g.edges.head shouldBe UnDiEdge(2, 3) - } - - def `add nodes`: Unit = { - val g = originalG flatMap (n => List(n.outer, -n.outer)) - g shouldBe Graph(edge, -edge.node1, -edge.node2) - } - - def `change the node type`: Unit = { - val g = originalG flatMap (n => List(n.outer.toString, (-n.outer).toString)) - g shouldBe Graph(edge.node1.toString ~ edge.node2.toString, (-edge.node1).toString, (-edge.node2).toString) - } - - def `change the edge type`: Unit = { - val g = originalG.flatMap( - fNode = increment, - fEdge = (n1s: Seq[Int], n2s: Seq[Int]) => - (n1s, n2s) match { - case (Seq(n1, _*), Seq(n2, _*)) => List(n1 ~> n2, n2 ~> n1) - } - ) - (g: Graph[Int, DiEdge[Int]]) shouldEqual Graph(2 ~> 3, 3 ~> 2) - g.isDirected shouldBe true - } - - def `change edge ends and structure`: Unit = { - val source = Graph(1 ~ 2, 3 ~ 3) - val g: Graph[Int, DiEdge[Int]] = - source.flatMap( - fNode = increment, - fEdge = (e: source.EdgeT, n1s: Seq[Int], n2s: Seq[Int]) => - if (e.isLooping) Nil - else - (n1s, n2s) match { - case (Seq(n1, _*), Seq(n2, _*)) => - List( - n1 ~> (e.weight.toInt + 10), - n2 ~> (e.weight.toInt + 10) - ) - } - ) - g.nodes.outerIterable should contain theSameElementsAs List(2, 3, 4, 11) - g.edges.outerIterable should contain theSameElementsAs List(2 ~> 11, 3 ~> 11) - } - } -} diff --git a/coreTestScala3/src/test/scala/scalax/collection/MappingTypedHyperSpec.scala b/coreTestScala3/src/test/scala/scalax/collection/MappingTypedHyperSpec.scala deleted file mode 100644 index 5bfde4f6..00000000 --- a/coreTestScala3/src/test/scala/scalax/collection/MappingTypedHyperSpec.scala +++ /dev/null @@ -1,18 +0,0 @@ -package scalax.collection - -import org.scalatest.Suites -import org.scalatest.matchers.should.Matchers -import org.scalatest.refspec.RefSpec - -import scalax.collection.generic.{Edge, GenericGraphCoreFactory} - -class MappingTypedHyperSpec - extends Suites( - new MappingTypedHyper[immutable.Graph](immutable.Graph), - new MappingTypedHyper[mutable.Graph](mutable.Graph) - ) - -private class MappingTypedHyper[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E, CC]]( - val factory: GenericGraphCoreFactory[CC] -) extends RefSpec - with Matchers {} diff --git a/coreTestScala3/src/test/scala/scalax/collection/MappingTypedSpec.scala b/coreTestScala3/src/test/scala/scalax/collection/MappingTypedSpec.scala deleted file mode 100644 index 2853df38..00000000 --- a/coreTestScala3/src/test/scala/scalax/collection/MappingTypedSpec.scala +++ /dev/null @@ -1,157 +0,0 @@ -package scalax.collection - -import scala.language.implicitConversions -import scala.util.chaining.* - -import org.scalatest.matchers.should.Matchers -import org.scalatest.refspec.RefSpec -import scalax.collection.edges.* -import scalax.collection.generic.* -import scalax.collection.immutable.{Graph, TypedGraphFactory} - -class MappingTypedSpec extends RefSpec with Matchers { - - object `mapping a typed graph you can` { - import MappingTypedSpec.* - import TGraph.OuterImplicits.* - - private val a_1 = A(1) - private val b_0_0 = B(0, 0) - - def `map nodes`: Unit = - TGraph(Connector(a_1, b_0_0)) pipe { g => - g.mapBound { (n: g.NodeT) => - n match { - case g.InnerNode(_, a: A) => a.copy(a.a + 1) - case g.InnerNode(_, b: B) => b.copy(b.a + 1, b.b + 1) - } - } shouldEqual TGraph(Connector(A(2), B(1, 1))) - } - - def `downcast nodes`: Unit = - TGraph(Connector(a_1, b_0_0)) pipe { g => - g.mapBound(_ => b_0_0) shouldEqual TGraph(Connector(b_0_0, b_0_0)) - } - - def `not upcast nodes when only passing an node mapper`: Unit = - TGraph(AConnector(a_1, a_1)) pipe { _ => - "g.map(_ => b_0_0): Graph[B, Edge]" shouldNot compile - "g.map(_.toString)" shouldNot compile - } - - def `upcast nodes to another typed edge within the ADT`: Unit = - TGraph(AConnector(a_1, a_1)) pipe { g => - g.mapBound[Node, Connector](_ => b_0_0, Connector.apply) pipe { mapped => - mapped.edges.head.outer shouldEqual Connector(b_0_0, b_0_0) - } - } - - def `upcast nodes to any type if a generic edge mapper is passed`: Unit = - TGraph(AConnector(a_1, a_1)) pipe { g => - def stringify(a: A): String = s"""string-$a""" - - g.map(n => stringify(n.outer), UnDiEdge[String] _) pipe { mapped => - mapped.size shouldBe 1 - mapped.edges.head.outer shouldBe (stringify(A(1)) ~ stringify(A(1))) - } - } - } - - object `flat-mapping a typed graph you can` { - - import MappingTypedSpec.* - import TGraph.OuterImplicits.* - - private val a_1 = A(1) - private val b_0_0 = B(0, 0) - - private def incr(a: A) = a.copy(a.a + 1) - - private val g = TGraph( - Connector(A(1), B(0, 0)), - AConnector(A(1), A(2)) - ) - - def `change and add nodes`: Unit = - g.flatMapBound { (n: g.NodeT) => - n match { - case g.InnerNode(_, a: A) => incr(a) :: a :: Nil - case g.InnerNode(_, _) => Nil - } - } shouldEqual TGraph(AConnector(A(1), A(2)), A(3)) - - def `downcast nodes`: Unit = - TGraph(Connector(a_1, b_0_0)) pipe { g => - g.flatMapBound(_ => b_0_0 :: Nil) shouldEqual TGraph(Connector(b_0_0, b_0_0)) - } - - def `not upcast nodes when only passing an node mapper`: Unit = - TGraph(AConnector(a_1, a_1)) pipe { _ => - "g.flatMap(_ => b_0_0): Graph[B, Edge]" shouldNot compile - "g.flatMap(_.toString)" shouldNot compile - } - - def `upcast and change structure within the ADT`: Unit = - TGraph(AConnector(A(1), A(2))) pipe { g => - g.flatMapBound[Node, Connector]( - fNode = n => List(incr(n.outer), b_0_0), - fEdge = (n1s: Seq[Node], n2s: Seq[Node]) => - List( - Connector(n2s.head, n1s.head), - Connector(n2s.head, b_0_0) - ) - ) pipe { mapped => - mapped.nodes.outerIterable should contain theSameElementsAs List(A(2), A(3), b_0_0) - mapped.edges.outerIterable should contain theSameElementsAs List( - Connector(A(3), A(2)), - Connector(A(3), b_0_0) - ) - } - } - - def `upcast and change structure to any type if a generic edge mapper is passed`: Unit = - TGraph(AConnector(A(1), A(2))) pipe { g => - def stringify(n: Any): String = s"""string-$n""" - g.flatMap( - fNode = n => List(stringify(incr(n.outer)), "B"), - fEdge = (n1s: Seq[String], n2s: Seq[String]) => - List( - n2s.head ~ n1s.head, - n2s.head ~ "B" - ) - ) pipe { (mapped: Graph[String, UnDiEdge[String]]) => - mapped.nodes.outerIterable should contain theSameElementsAs List(stringify(A(2)), stringify(A(3)), "B") - mapped.edges.outerIterable should contain theSameElementsAs List( - stringify(A(3)) ~ stringify(A(2)), - stringify(A(3)) ~ "B" - ) - } - } - } -} - -private object MappingTypedSpec { - private trait Node - private case class A(a: Int) extends Node - private case class B(a: Int, b: Int) extends Node - - sealed private trait Edge extends AnyDiEdge[Node] with PartialMapper - - private case class Connector(override val source: Node, override val target: Node) - extends AbstractDiEdge[Node](source, target) - with PartialEdgeMapper[Connector] - with Edge { - def map[N]: PartialFunction[(N, N), Connector] = { case (s: Node, t: Node) => copy(s, t) } - } - - private case class AConnector(override val source: A, override val target: A) - extends AbstractDiEdge[A](source, target) - with PartialEdgeMapper[AConnector] - with Edge { - def map[N]: PartialFunction[(N, N), AConnector] = { case (s: A, t: A) => copy(s, t) } - } - - private object TGraph extends TypedGraphFactory[Node, Edge] - - implicit private def aConnectorToOuterEdge(e: AConnector): OuterEdge[A, AConnector] = OuterEdge(e) -} diff --git a/coreTestScala3/src/test/scala/scalax/collection/PathBuilderSpec.scala b/coreTestScala3/src/test/scala/scalax/collection/PathBuilderSpec.scala deleted file mode 100644 index 50890be1..00000000 --- a/coreTestScala3/src/test/scala/scalax/collection/PathBuilderSpec.scala +++ /dev/null @@ -1,60 +0,0 @@ -package scalax.collection - -import org.scalatest.flatspec.AnyFlatSpec -import org.scalatest.matchers.should.Matchers -import scalax.collection.edges.* -import scalax.collection.generic.* -import scalax.collection.immutable.Graph - -protected trait WalkBehaviors { - this: AnyFlatSpec with Matchers => - - import UnDi_1.* - - protected def walk(builder: => g.WalkBuilder): Unit = { - it should "accept neighbors" in { builder add n(3) shouldBe true } - it should "refuse non-neighbors" in { builder add n(4) shouldBe false } - it should "accept outgoing edge" in { builder add (g get 1 ~ 2) shouldBe true } - it should "refuse non-outgoing edge" in { builder add (g get 2 ~ 3) shouldBe false } - } -} - -class PathBuilderSpec extends AnyFlatSpec with WalkBehaviors with Matchers { - - import UnDi_1.* - - def walkBuilder = g.newWalkBuilder(n(1)) - def pathBuilder = g.newPathBuilder(n(1)) - - "A WalkBuilder" should behave like walk(walkBuilder) - - "A WalkBuilder" should "yield the expected Walk" in { - val walk = (walkBuilder += n(3) += n(5) += n(1) += n(2)).result() - walk.nodes.toList shouldBe List(1, 3, 5, 1, 2) - walk.edges.toList shouldBe List(1 ~> 3, 3 ~ 5, 5 ~ 1, 1 ~ 2) - } - - "A PathBuilder" should behave like walk(pathBuilder) - - "A PathBuilder" should "refuse duplicate nodes" in { - (pathBuilder += n(2)) add n(1) shouldBe false - (pathBuilder += n(2) += n(3)) add n(2) shouldBe false - } - - "A PathBuilder" should "refuse duplicate edges" in { - (pathBuilder += n(2) += n(3)) add e(2 ~ 3) shouldBe false - } - - "PathBuilder result" should "discard a terminating edge" in { - (pathBuilder += n(2) += e(2 ~ 3)).result().edges should have size 1 - } - - it should "yield the expected Path" in { - val path = (pathBuilder += n(3) += n(5)).result() - path.nodes.toList shouldBe List(1, 3, 5) - path.edges.toList shouldBe List(1 ~> 3, 3 ~ 5) - } -} - -// The single graph used for this test -protected object UnDi_1 extends TGraph[Int, AnyEdge[Int], Graph](Graph.from(Data.elementsOfMixed_1)) diff --git a/coreTestScala3/src/test/scala/scalax/collection/SerializableSpec.scala b/coreTestScala3/src/test/scala/scalax/collection/SerializableSpec.scala deleted file mode 100644 index 86e7c0c6..00000000 --- a/coreTestScala3/src/test/scala/scalax/collection/SerializableSpec.scala +++ /dev/null @@ -1,378 +0,0 @@ -package scalax.collection - -import java.io.* - -import scala.util.{Failure, Success, Try} - -import org.scalatest.* -import org.scalatest.flatspec.AnyFlatSpec -import org.scalatest.matchers.should.Matchers - -import scalax.collection.OuterImplicits.* -import scalax.collection.edges.* -import scalax.collection.generic.{AnyEdge, Edge, GenericGraphCoreFactory} -import scalax.collection.visualization.Visualizer - -class SerializableSpec - extends Suites( - new TSerializable[immutable.Graph](immutable.Graph), - new TSerializable[mutable.Graph](mutable.Graph) - ) - -/** Tests standard java serialization. - */ -final private class TSerializable[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E, CC]]( - val factory: GenericGraphCoreFactory[CC] -) extends AnyFlatSpec - with Matchers - with BeforeAndAfterEach - with IntelliJ[CC] - with Visualizer { - private val factoryName = factory.getClass.getName - private val isImmutableTest = factoryName contains "immutable" - - s"Tests based on $factoryName" should "start" in {} - - trait GraphStore { - protected trait ExecMethods { - def save[N, E <: Edge[N]](g: CC[N, E]): Unit - def restore[N, E <: Edge[N]]: CC[N, E] - } - protected def newTest: ExecMethods - def test[N, E <: Edge[N]](g: CC[N, E]): CC[N, E] = { - val exec = newTest - exec.save[N, E](g) - val r = exec.restore[N, E] - withGraph(r.asAnyGraph)(_ should equal(g)) - r - } - } - - /** to save and restore graphs to/from files */ - object GraphFile extends GraphStore { - protected class Exec(filename: String) extends ExecMethods { - import FileSerialization.* - def save[N, E <: Edge[N]](g: CC[N, E]): Unit = write(g, filename) recover { case e => - fail(s"Couldn't write $g: $e") - } - - def restore[N, E <: Edge[N]]: CC[N, E] = read(filename).recover { case e => - fail(s"Couldn't read graph: $e") - }.get - } - private val tmpDir = System.getProperty("java.io.tmpdir") - private var cnt = 0 - protected def newTest: Exec = { - cnt += 1 - new Exec( // include the name of the test method - s"$tmpDir${File.separator}${s"${this.getClass.getSimpleName.init}.${if (isImmutableTest) "i" else "m"}-${testNames.toArray.apply(cnt)}"}.ser" - ) - } - } - - /** to save and restore graphs to/from byte arrays */ - object GraphByteArray extends GraphStore { - protected class Exec extends ExecMethods { - import ByteArraySerialization.* - private var _saved: Array[Byte] = _ - - def save[N, E <: Edge[N]](g: CC[N, E]): Unit = write(g) match { - case Success(s) => _saved = s - case Failure(e) => fail("Couldn't write: " + g, e) - } - - def restore[N, E <: Edge[N]]: CC[N, E] = read[CC[N, E]](_saved) match { - case Success(s) => s - case Failure(e) => fail("Couldn't read graph", e) - } - } - protected def newTest: Exec = new Exec - } - - /** normally we test with byte arrays but may be set to GraphFile instead */ - private lazy val store: GraphStore = GraphByteArray - - private val work = "be serializable" - - "An empty graph" should work in { - val g = factory.empty[Nothing, Nothing] - store.test[Nothing, Nothing](g) - } - - "A graph of type [Int,Nothing]" should work in { - val g = factory[Int, Nothing](-1, 1, 2) - store.test[Int, Nothing](g) - } - - "A graph of type [Int,UnDiEdge]" should work in { - val g = factory[Int, AnyEdge](-1 ~ 1, 2 ~> 1) - store.test[Int, AnyEdge[Int]](g) - } - - "A graph of type [String,UnDiEdge]" should work in { - val g = factory[String, AnyEdge]("a" ~ "b", "b" ~> "c") - store.test[String, AnyEdge[String]](g) - } - - "A graph of type [Int,DiEdge]" should work in { - import Data.elementsOfDi_1 - val g = factory(elementsOfDi_1: _*) - store.test[Int, DiEdge[Int]](g) - } - - /* TODO L - "A graph of [MyNode,WLDiEdge]" should work in { - import edge.WLDiEdge - - val (a1, a2, b1, b2) = ("a1", "a2", "b1", "b2") - val (n1, n2) = (MyNode(List(a1, b1)), MyNode(List(a2, b2))) - val label = MyLabel("abab") - val e = WLDiEdge(n1, n2)(11, label) - - { - /* if MyNode is declared as an inner class, it is not serializable; - * so we assert first, that both the node class and the label class are serializable - */ - val bos = new ByteArrayOutputStream - val out = new ObjectOutputStream(bos) - out writeObject n1 - out writeObject label - bos.close - } - - given(factory(e)) { g => - val back = store.test[MyNode, WLDiEdge](g) - - back.size should be(1) - - val inner_1 = back get n1 - inner_1.diSuccessors should have size 1 - inner_1.diSuccessors.head should be(n2) - - val backEdge = back.edges.head - backEdge.source.s should be(List(a1, b1)) - backEdge.target.s should be(List(a2, b2)) - backEdge.label should be(label) - } - } - */ - - "After calling diSuccessors the graph" should work in { - import Data.elementsOfDi_1 - val g = factory(elementsOfDi_1: _*) - g.nodes.head.diSuccessors - store.test[Int, DiEdge[Int]](g) - } - - "After calling pathTo the graph" should work in { - import Data.elementsOfDi_1 - val g = factory(elementsOfDi_1: _*) - g.nodes.head.diSuccessors - val n = g.nodes.head - n.pathTo(n) - store.test[Int, DiEdge[Int]](g) - } - - "A deserialized graph" should "be traversable" in { - import Data.elementsOfDi_1 - val g = factory(elementsOfDi_1: _*) - val back = store.test[Int, DiEdge[Int]](g) - withGraph(g.asAnyGraph) { g => - def op(g: AnyGraph[Int, DiEdge[Int]]): Int = g.nodes.head.outerNodeTraverser.size - op(back.asAnyGraph) shouldBe op(g) - } - } - - "A deserialized graph" should "have the same successors" in { - import Data.elementsOfDi_1 - val g = factory(elementsOfDi_1: _*) - - def outerSuccessors(g: AnyGraph[Int, DiEdge[Int]]) = - g.nodes map (innerNode => innerNode.outer -> innerNode.diSuccessors.map(_.outer)) - - val diSuccBefore = outerSuccessors(g.asAnyGraph) - val back = store.test[Int, DiEdge[Int]](g) - withGraph(g.asAnyGraph) { g => - outerSuccessors(back.asAnyGraph) shouldBe diSuccBefore - } - } - - trait EdgeStore { - def save[N, E <: Edge[N]](e: Iterable[OuterElem[N, E]]): Unit - def restore[N, E <: Edge[N]]: Iterable[OuterElem[N, E]] - def test[N, E <: Edge[N]](e: Iterable[OuterElem[N, E]]): Iterable[OuterElem[N, E]] = { - save[N, E](e) - val r = restore[N, E] - r should be(e) - r - } - } - - class EdgeByteArray extends EdgeStore { - import ByteArraySerialization.* - private var _saved: Array[Byte] = _ - - def save[N, E <: Edge[N]](it: Iterable[OuterElem[N, E]]): Unit = write(it) match { - case Success(s) => _saved = s - case Failure(e) => fail(s"Couldn't write '$it': $e") - } - - def restore[N, E <: Edge[N]]: Iterable[OuterElem[N, E]] = - readWithCustomClassLoader[Iterable[OuterElem[N, E]]](_saved) match { - case Success(s) => s - case Failure(e) => fail(s"Couldn't read iterable: $e") - } - } - - "A graph of [Int, AnyEdge[Int]]" should work in { - import Data.elementsOfMixed_2 - withGraph(factory.from(elementsOfMixed_2).asAnyGraph) { _ => - (new EdgeByteArray).test[Int, AnyEdge[Int]](elementsOfMixed_2) - } - } -} - -object ByteArraySerialization { - def write(obj: AnyRef, initSize: Int = 8000): Try[Array[Byte]] = { - val bos = new ByteArrayOutputStream(initSize) - val out = new ObjectOutputStream(bos) - Try { - out writeObject obj - out.close() - bos.toByteArray - } recoverWith { case e => - out.close() - Failure[Array[Byte]](e) - } - } - - def readWithCustomClassLoader[A](from: Array[Byte]): Try[A] = - read[A](new CustomObjectInputStream(new ByteArrayInputStream(from), classOf[SerializableSpec].getClassLoader)) - - def read[A](from: Array[Byte]): Try[A] = - read(new ObjectInputStream(new ByteArrayInputStream(from))) - - def read[A](in: ObjectInputStream): Try[A] = - Try { - val read = in.readObject - in.close() - read.asInstanceOf[A] - } recoverWith { case e => - in.close() - Failure[A](e) - } - - // resolves ClassNotFoundException - private class CustomObjectInputStream(in: InputStream, classLoader: ClassLoader) extends ObjectInputStream(in) { - import java.lang.reflect.Proxy - import java.lang.reflect.{InvocationHandler, Method} - - override def resolveClass(cd: ObjectStreamClass): Class[_] = - try - classLoader.loadClass(cd.getName) - catch { - case cnf: ClassNotFoundException => - super.resolveClass(cd) - } - override def resolveProxyClass(interfaces: Array[String]): Class[_] = { - val classInterfaces = interfaces map classLoader.loadClass - try - Proxy.newProxyInstance(classLoader, classInterfaces, new DummyInvocationHandler).getClass - catch { - case e: ClassNotFoundException => - super.resolveProxyClass(interfaces) - } - } - - private class DummyInvocationHandler extends InvocationHandler { - def invoke(proxy: AnyRef, method: Method, args: Array[AnyRef]): AnyRef = - throw new NotImplementedError(s"${getClass.getSimpleName} should never be called.") - } - } -} - -object FileSerialization { - def write(obj: AnyRef, filename: String): Try[File] = write(obj, new File(filename)) - def write(obj: AnyRef, file: File): Try[File] = { - var out: ObjectOutputStream = null - Try { - out = new ObjectOutputStream(new FileOutputStream(file)) - out writeObject obj - out.close() - file - } recoverWith { case e => - if (out ne null) out.close() - Failure[File](e) - } - } - def read[A](filename: String): Try[A] = read(new File(filename)) - def read[A](file: File): Try[A] = { - var in: ObjectInputStream = null - Try { - in = new ObjectInputStream(new FileInputStream(file)) - val read = in.readObject - in.close() - read.asInstanceOf[A] - } recoverWith { case e => - if (in ne null) in.close() - Failure[A](e) - } - } -} - -// to be serializable, the following classes must be defined at top level -case class MyNode(s: List[String]) extends Serializable -case class MyLabel(s: String) extends Serializable - -// examining https://issues.scala-lang.org/browse/SI-5773?jql=text%20~%20%22%40transient%22 -protected object ScalaObjectSerialization extends App { - - private trait Base { - trait Inner { - def d: String - def v: String - } - def inner: Inner - } - - private object MyObject extends Base with Serializable { - @transient // ok: no serialization takes place - object Inner extends super.Inner { - def d = "MyDef" - val v = "MyVal" - } - def inner = Inner - } - - private trait MyTrait extends Base with Serializable { - @transient // !!! ignored so Serializable needs to be mixed in - object Inner extends super.Inner with Serializable { - def d = "MyDef" - val v = "MyVal" - } - val inner = Inner - } - - private class MyClass extends Base with Serializable { - @transient // !!! same as MyTrait - object Inner extends super.Inner with Serializable { - def d = "MyDef" - val v = "MyVal" - } - val inner = Inner - } - - import ByteArraySerialization.* - private def test[A <: Base](my: A): Unit = - write(my) flatMap { saved => - println(s"saved (${saved.length} bytes)=${new String(saved)}") - println(s" contains MyVal=${new String(saved) contains "MyVal"}") - read[A](saved) - } map (my => println(s" okDef=${my.inner.d}, okVal=${my.inner.v}")) recover { case e => - println(s"serialization of $my failed with $e") - } - - test(MyObject) - test(new MyTrait {}) - test(new MyClass) -} diff --git a/coreTestScala3/src/test/scala/scalax/collection/SetOpsSpec.scala b/coreTestScala3/src/test/scala/scalax/collection/SetOpsSpec.scala deleted file mode 100644 index f65609f4..00000000 --- a/coreTestScala3/src/test/scala/scalax/collection/SetOpsSpec.scala +++ /dev/null @@ -1,100 +0,0 @@ -package scalax.collection - -import scala.util.chaining.* - -import org.scalatest.Suites -import org.scalatest.matchers.should.Matchers -import org.scalatest.refspec.RefSpec - -import scalax.collection.OuterImplicits.* -import scalax.collection.edges.* -import scalax.collection.generic.{Edge, GenericGraphCoreFactory} -import scalax.collection.visualization.Visualizer - -class SetOpsSpec - extends Suites( - new SetOps[immutable.Graph](immutable.Graph), - new SetOps[mutable.Graph](mutable.Graph), - new SetOpsImmutable, - new SetOpsMutable - ) - -protected trait SetOpExamples[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E, CC]] { - - protected def factory: GenericGraphCoreFactory[CC] - - protected val gEdges = Set(1 ~ 2, 2 ~ 3, 2 ~ 4, 3 ~ 5, 4 ~ 5) - protected val hEdges = Set(3 ~ 4, 3 ~ 5, 4 ~ 6, 5 ~ 6) - - protected def g = factory.from(gEdges) - protected def h = factory.from(hEdges) - - protected object Expected { - val g_union_h = factory.from(gEdges ++ hEdges) - val g_diff_h = - g.nodes.toOuter -- h.nodes.toOuter pipe { nDiff => - factory.from(nDiff, gEdges ++ hEdges filter { case n1 ~ n2 => nDiff(n1) && nDiff(n2) }) - } - } -} - -private class SetOps[CC[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E, CC]]( - val factory: GenericGraphCoreFactory[CC] -) extends RefSpec - with Matchers - with SetOpExamples[CC] - with IntelliJ[CC] - with Visualizer { - - def `concat `: Unit = { - factory(1 ~ 2).asAnyGraph concat List(1 ~ 2) - factory(1 ~ 2).asAnyGraph concat List(1 ~> 2) - factory(1 ~ 2).asAnyGraph ++ List(1 ~ 2) - - factory(1 ~ 2) concat List("a" ~ "b"): AnyGraph[Any, UnDiEdge[Any]] - factory(1 ~ 2) concat (List('x'), List("a" ~ "b")): AnyGraph[Any, UnDiEdge[Any]] - - factory(1 ~ 2).asAnyGraph concat (List('x'), List('a' ~ 'b')) - factory(1 ~ 2).asAnyGraph ++ (List('x'), List('a' ~ 'b')) - } - - def `union `: Unit = - g union h shouldEqual Expected.g_union_h - - def `difference `: Unit = - g diff h shouldEqual Expected.g_diff_h - - def `intersection `: Unit = { - val expected = factory(3 ~ 5, 4) - withGraph(g intersect h)(_ shouldEqual expected) - withGraph(g & h)(_ shouldEqual expected) - } -} - -private class SetOpsImmutable extends RefSpec with Matchers with SetOpExamples[immutable.Graph] { - protected val factory = immutable.Graph - -} - -private class SetOpsMutable extends RefSpec with Matchers with SetOpExamples[mutable.Graph] { - protected val factory = mutable.Graph - - private val iH = immutable.Graph.from(hEdges) - - def `unionInPlace `: Unit = { - (g |= h) shouldEqual Expected.g_union_h - (g |= iH) shouldEqual Expected.g_union_h - } - - def `--= `: Unit = { - (g --= h) shouldEqual Expected.g_diff_h - (g --= iH) shouldEqual Expected.g_diff_h - } - - def `&= `: Unit = { - val expected = factory(3 ~ 5, 4) - - (g &= h) shouldEqual expected - (g &= iH) shouldEqual expected - } -} diff --git a/coreTestScala3/src/test/scala/scalax/collection/StateSpec.scala b/coreTestScala3/src/test/scala/scalax/collection/StateSpec.scala deleted file mode 100644 index b8376560..00000000 --- a/coreTestScala3/src/test/scala/scalax/collection/StateSpec.scala +++ /dev/null @@ -1,127 +0,0 @@ -package scalax.collection - -import scala.collection.mutable.Map as MutableMap -import scala.concurrent.ExecutionContext.Implicits.* -import scala.concurrent.duration.* -import scala.concurrent.{Await, Future} -import scala.language.postfixOps -import scala.util.Random - -import org.scalatest.Inspectors.* -import org.scalatest.matchers.should.Matchers -import org.scalatest.refspec.RefSpec -import scalax.collection.OuterImplicits.* -import scalax.collection.edges.* -import scalax.collection.generator.{NodeDegreeRange, RandomGraph} -import scalax.collection.immutable.Graph - -/** Ensure that stateful data handling used for traversals is thread-safe. - */ -class StateSpec extends RefSpec with Matchers { - val g = Graph.from(Data.elementsOfMixed_2) - - // a sample traversal with recursive calls in its node visitor - def countNodes(recursion: Int = 0): Int = { - assert(recursion >= 0) - var nrNodes = 0 - g.nodes.head.innerNodeTraverser foreach { _ => - nrNodes += ( - if (recursion == 0) 1 - else countNodes(recursion - 1) - ) - } - nrNodes - } - - val nrNodesExpected = g.order - val aLotOfTimes = 162 // at least 2 times State.nrOfFlagWordBits - - def dump: Unit = - println(State.dump(g)) - - object `Single-threaded shared state proves robust` { - def `when looping`: Unit = - for (i <- 1 to aLotOfTimes) - countNodes() should be(nrNodesExpected) - - def `when looping at visited nodes`: Unit = - g.nodes.head.innerNodeTraverser foreach (_ => `when looping`) - - def `when called recursively`: Unit = { - val depth = 5 - countNodes(depth) should be(math.pow(3, depth) * nrNodesExpected) - } - - def `when called deep-recursively`: Unit = { - val recurseAt = g.nodes.head - def countNodesDeep(recursion: Int): Int = { - assert(recursion >= 0) - var nrNodes = 0 - g.nodes.head.innerNodeTraverser foreach (n => - nrNodes += ( - // if (n eq recurseAt) println(State.dump(recurseAt).summary) - if (recursion == 0) 0 - else if (n eq recurseAt) countNodesDeep(recursion - 1) - else 1 - ) - ) - nrNodes - } - for (i <- 1 to 2) countNodesDeep(aLotOfTimes) - } - - def `when cleaned up after lots of unconnected traversals`: Unit = { - val order = 5000 - val r = new Random(10 * order) - - def intNodeFactory = r.nextInt() - val g = - new RandomGraph[Int, DiEdge[Int], Graph]( - Graph, - order, - intNodeFactory, - NodeDegreeRange(0, 2), - Set(DiEdge), - false - ).draw - - val rootNodes = List.fill(50)(g.nodes.draw(r)) - for { - node <- g.nodes - root <- rootNodes - } node.hasSuccessor(root) - } - } - - object `Multi-threaded shared state proves robust` { - def `when traversing by futures`: Unit = { - val traversals = Future.sequence( - for (i <- 1 to aLotOfTimes) - yield Future(countNodes()) - ) - - // statistics map with key = nrOfNodesCounted, value = frequency - val stat = MutableMap.empty[Int, Int] - - Await.result(traversals, 1 seconds) foreach { cnt => - stat += cnt -> (stat.getOrElse(cnt, 0) + 1) - } - - // each traversal must yield the same result - stat should be(Map(nrNodesExpected -> aLotOfTimes)) - } - - def `when tested under stress fixing #34`: Unit = { - import Data.* - object g extends TGraph[Int, DiEdge[Int], Graph](Graph(elementsOfDi_1: _*)) - def n(outer: Int) = g.node(outer) - val (n1, n2) = (n(2), n(5)) - - val times = 200000 - def run: Future[Seq[Boolean]] = Future.sequence((1 to times) map (_ => Future(n1 pathTo n2) map (_.nonEmpty))) - val bulks: Future[Seq[Boolean]] = Future.sequence((1 to 3) map (_ => run)) map (_.flatten) - - forAll(Await.result(bulks, 50.seconds))(_ shouldBe true) - } - } -} diff --git a/coreTestScala3/src/test/scala/scalax/collection/TopologicalSortSpec.scala b/coreTestScala3/src/test/scala/scalax/collection/TopologicalSortSpec.scala deleted file mode 100644 index 23f3ff8b..00000000 --- a/coreTestScala3/src/test/scala/scalax/collection/TopologicalSortSpec.scala +++ /dev/null @@ -1,273 +0,0 @@ -package scalax.collection - -import org.scalatest.* -import org.scalatest.matchers.should.Matchers -import org.scalatest.refspec.RefSpec -import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks - -import scalax.collection.GraphTraversal.* -import scalax.collection.GraphTraversal.Parameters.* -import scalax.collection.OuterImplicits.* -import scalax.collection.edges.* -import scalax.collection.generic.{Edge, GenericGraphCoreFactory} -import scalax.collection.visualization.Visualizer - -class TopologicalSortSpec - extends Suites( - new TopologicalSort[immutable.Graph](immutable.Graph), - new TopologicalSort[mutable.Graph](mutable.Graph) - ) - -final private class TopologicalSort[G[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E, G]]( - val factory: GenericGraphCoreFactory[G] -) extends RefSpec - with Matchers - with ScalaCheckPropertyChecks - with IntelliJ[G] - with Visualizer { - - private object Topo { - - class Checker[N, E <: Edge[N]](val graph: AnyGraph[N, E]) { - - def checkOuterNodes(seq: Iterable[N]): Unit = - checkInnerNodes(seq map (graph get _)) - - type OrderedInnerNodes = Iterable[graph.NodeT] - - def checkInnerNodes( - seq: OrderedInnerNodes, - root: Option[graph.NodeT] = None, - ignorePredecessors: Boolean = false - ): Unit = { - def predecessors(maybeRoot: Option[graph.NodeT]): Set[graph.NodeT] = - maybeRoot.fold( - ifEmpty = Set.empty[graph.NodeT] - ) { root => - root.innerNodeTraverser().withParameters(Dfs(Predecessors)).toSet - root - } - - def checkOrder(seq: OrderedInnerNodes, ignorePredecessorsOf: Option[graph.NodeT]): Unit = - seq.foldLeft(predecessors(ignorePredecessorsOf)) { (allowedPredecessors, innerNode) => - if (!innerNode.inNeighbors.forall(allowedPredecessors.contains)) - fail(s"$innerNode is misplaced in $seq") - allowedPredecessors + innerNode - } - - def checkCompleteness( - seq: OrderedInnerNodes, - maybeRoot: Option[graph.NodeT], - ignorePredecessors: Boolean - ): Unit = { - val expected = maybeRoot.fold( - ifEmpty = graph.nodes.toSet - ) { root => - root.innerNodeTraverser().withParameters(Dfs(AnyConnected)).toSet -- - (if (ignorePredecessors) predecessors(maybeRoot) else Nil) - } - val set = seq.toSet - if (set != expected) - fail( - s"Ordering is incomplete when root=$maybeRoot and ignorePredecessors=$ignorePredecessors: expected $expected but was $set." - ) - } - - checkOrder(seq, if (ignorePredecessors) root else None) - checkCompleteness(seq, root, ignorePredecessors) - } - } - - def unexpectedCycle[N, E <: Edge[N]](cycleNode: AnyGraph[N, E]#TopologicalSortFailure): Nothing = - fail(s"Unexpected cycle with candidate cycle nodes $cycleNode") - - def unexpectedRight[N, E <: Edge[N]](order: AnyGraph[N, E]#TopologicalOrder[_]): Nothing = - fail(s"Cycle expected but topological order ${order.toLayered} found") - - def checkCycleHint( - g: AnyGraph[Int, DiEdge[Int]] - )(hint: g.TopologicalSortFailure, expectedDefined: Boolean): Unit = - (hint.candidateCycleNodes, expectedDefined) match { - case (ns, true) if ns.nonEmpty => - hint.cycle orElse fail(s"Cycle containing any of $ns expected but none found.") - case (ns, false) if ns.nonEmpty => fail(s"Unexpected cycle node hints $ns found.") - case (_, true) => fail(s"Non-empty cycle node hint expected.") - case (_, false) => - } - } - - def `empty graph`(): Unit = - withGraph(factory.empty[Int, DiEdge[Int]].asAnyGraph) { - _.topologicalSort.fold( - Topo.unexpectedCycle, - _ shouldBe empty - ) - } - - def `daily activities`(): Unit = { - - object Activities { - val (coffee, coding, inspiration, shopping, sleeping, supper, gaming) = - ("coffee", "coding", "inspiration", "shopping", "sleeping", "supper", "gaming") - val (making_music, driving_to_work, driving_home, listening_to_music) = - ("making music", "driving to work", "driving home", "listening to music") - } - import Activities.* - - val typicalDay = factory[String, DiEdge]( - coffee ~> coding, - inspiration ~> coding, - shopping ~> coffee, - coding ~> sleeping, - supper ~> sleeping, - gaming ~> sleeping, - making_music ~> sleeping, - inspiration ~> making_music, - shopping ~> supper, - driving_home ~> supper, - driving_home ~> sleeping, - coding ~> driving_home, - driving_to_work ~> coding, - driving_to_work ~> driving_home, - driving_home ~> gaming, - listening_to_music - ).asAnyGraph - - withGraph(typicalDay) { - _.topologicalSort.fold( - Topo.unexpectedCycle, - order => - new Topo.Checker(typicalDay) { - checkOuterNodes(order.toOuter) - } - ) - } - } - - def `connected graph`(): Unit = { - val someOuter @ n0 :: n1 :: n5 :: Nil = 0 :: 1 :: 5 :: Nil: @unchecked - val connected = factory[Int, DiEdge](n0 ~> n1, 2 ~> 4, 2 ~> n5, n0 ~> 3, n1 ~> 4, 4 ~> 3).asAnyGraph - withGraph(connected) { g => - g.isMulti shouldBe false - g.topologicalSort.fold( - Topo.unexpectedCycle, - order => - new Topo.Checker(connected) { - checkOuterNodes(order.toOuter) - for { - outer <- someOuter - inner = graph get outer - ignorePredecessors <- Array(false, true) - } - inner - .topologicalSort(ignorePredecessors) - .fold(Topo.unexpectedCycle, order => checkInnerNodes(order, Some(inner), ignorePredecessors)) - } - ) - } - } - - def `multi graph`(): Unit = { - import scalax.collection.edges.multilabeled.* - - val g = factory(1 ~> 2 %% 0, 1 ~> 2 %% 1).asAnyGraph - - g.topologicalSort.fold( - Topo.unexpectedCycle, - order => - new Topo.Checker(g) { - checkOuterNodes(order.toOuter) - } - ) - } - - def `unconnected graph`(): Unit = { - val expectedLayer_0 @ _1 :: _3 :: Nil = List(1, 3): @unchecked - val expectedLayer_1 @ _2 :: _4 :: Nil = List(2, 4): @unchecked - withGraph(factory(_1 ~> _2, _3 ~> _4)) { - _.topologicalSort.fold( - Topo.unexpectedCycle, - _.toLayered.toOuter.toList match { - case (layer_0 :: layer_1 :: Nil) => - layer_0._2.toList.sorted should be(expectedLayer_0) - layer_1._2.toList.sorted should be(expectedLayer_1) - case _ => fail() - } - ) - } - } - - def `minimal cyclic graph`(): Unit = - withGraph(factory(1 ~> 2, 2 ~> 1)) { g => - g.topologicalSort.fold( - Topo.checkCycleHint(g)(_, expectedDefined = false), - Topo.unexpectedRight - ) - } - - def `cyclic graph #68`(): Unit = - withGraph(factory(0 ~> 7, 4 ~> 7, 7 ~> 3, 3 ~> 4, 0 ~> 5)) { g => - g.topologicalSort.fold( - Topo.checkCycleHint(g)(_, expectedDefined = true), - Topo.unexpectedRight - ) - } - - def `cyclic graphs #264`(): Unit = - withGraph( - factory( - 111 ~> 2, - 2 ~> 111, - 111 ~> 33 - ) ++ (for { - i <- Range.inclusive(33, 230, step = 10) - j = i + 10 - } yield i ~> j) - ) { g => - g.topologicalSort.fold( - Topo.checkCycleHint(g)(_, expectedDefined = false), - Topo.unexpectedRight - ) - } - - def `cyclic unconnected graph`(): Unit = - withGraph(factory(11100 ~> 2, 6 ~> 7, 2 ~> 11100, 3 ~> 4)) { g => - g.topologicalSort.fold( - Topo.checkCycleHint(g)(_, expectedDefined = false), - Topo.unexpectedRight - ) - } - - def `cyclic unconnected graph by component`(): Unit = - withGraph(factory(11100 ~> 2, 6 ~> 7, 2 ~> 11100, 3 ~> 4)) { g => - val r = g.topologicalSortByComponent - r.size shouldBe 3 - r.count(_.isLeft) shouldBe 1 - } - - def `proper cycle node out of multiple hints`(): Unit = - withGraph(factory(0 ~> 11, 0 ~> 20, 1 ~> 20, 11 ~> 20, 11 ~> 30, 30 ~> 11)) { g => - g.topologicalSort.fold( - Topo.checkCycleHint(g)(_, expectedDefined = true), - Topo.unexpectedRight - ) - } - - def `with filtered edges #104`(): Unit = { - import scalax.collection.edges.labeled.* - - withGraph(factory(1 ~> 3 % 1, 1 ~> 2 % 2, 2 ~> 3 % 1)) { g => - val n1 = g get 1 - n1.topologicalSort().isRight shouldBe true - - n1.withSubgraph(edges = _.weight == 1) - .topologicalSort() - .fold( - Topo.unexpectedCycle, - order => - new Topo.Checker(g) { - checkOuterNodes(order.toOuter) - } - ) - } - } -} diff --git a/coreTestScala3/src/test/scala/scalax/collection/TraversalSpec.scala b/coreTestScala3/src/test/scala/scalax/collection/TraversalSpec.scala deleted file mode 100644 index d23c34eb..00000000 --- a/coreTestScala3/src/test/scala/scalax/collection/TraversalSpec.scala +++ /dev/null @@ -1,382 +0,0 @@ -package scalax.collection - -import scala.collection.mutable.ListBuffer - -import org.scalatest.* -import org.scalatest.matchers.should.Matchers -import org.scalatest.refspec.RefSpec -import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks - -import scalax.collection.GraphTraversal.* -import scalax.collection.OuterImplicits.* -import scalax.collection.edges.* -import scalax.collection.generic.{Edge, GenericGraphCoreFactory} -import scalax.collection.visualization.Visualizer - -class TraversalSpec - extends Suites( - new Traversal[immutable.Graph](immutable.Graph), - new Traversal[mutable.Graph](mutable.Graph) - ) - -/** This class contains tests for graph traversals to be run for Graph instances created - * by the Graph factory and passed to the constructor. - * It allows the same tests to be run for mutable and immutable Graphs. - */ -final private class Traversal[G[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E, G]]( - val factory: GenericGraphCoreFactory[G] -) extends RefSpec - with Matchers - with ScalaCheckPropertyChecks - with IntelliJ[G] - with Visualizer { - - implicit val config: PropertyCheckConfiguration = - PropertyCheckConfiguration(minSuccessful = 5, maxDiscardedFactor = 1.0) - - val predecessors = Parameters(direction = Predecessors) - val anyConnected = Parameters(direction = AnyConnected) - - def `find successors in a tiny graph`: Unit = - withGraph(factory(1 ~> 2).asAnyGraph) { g => - val (n1, n2) = (g get 1, g get 2) - - List(1, 3) foreach { i => - n1 findSuccessor (_.outer == i) shouldBe empty - } - n2 findSuccessor (_.outer == 1) shouldBe empty - n1 findSuccessor (_.outer == 2) shouldBe Some(n2) - } - - def `find predecessors in a tiny graph`: Unit = - withGraph(factory(1 ~> 2)) { g => - val (n1, n2) = (g get 1, g get 2) - - 1 to 3 foreach { i => - n1 findPredecessor (_.outer == i) shouldBe empty - } - val predecessor = n2 findPredecessor (_.outer == 1) - predecessor shouldBe Some(n1) - } - - def `find connected nodes by predicate in a tiny graph`: Unit = - withGraph(factory(1 ~> 2)) { g => - val (n1, n2) = (g get 1, g get 2) - - List(1, 3) foreach { i => - n1 findConnected (_.outer == i) shouldBe empty - } - n1 findConnected (_.outer == 2) shouldBe Some(n2) - n2 findConnected (_.outer == 1) shouldBe Some(n1) - } - - import Data.* - object Di_1 extends TGraph(factory.from(elementsOfDi_1)) - object UnDi_1 extends TGraph(factory.from(elementsOfMixed_1)) - - def `find successors in a mid-size graph`: Unit = - withGraph(Di_1.g.asAnyGraph) { g => - def n(outer: Int) = g.get(outer) - - List(0, 3, 7) foreach { i => - n(3) findSuccessor (_.outer == i) shouldBe empty - } - n(2) findSuccessor (_.outer == 5) shouldBe Some(5) - n(3) findSuccessor (_.outer > 4) shouldBe Some(5) - } - - def `find predecessors in a mid-size graph`: Unit = - withGraph(Di_1.g.asAnyGraph) { g => - def n(outer: Int) = g.get(outer) - - List(0, 3, 5) foreach { i => - n(3) findPredecessor (_.outer == i) shouldBe empty - } - n(3) findPredecessor (_.outer == 4) shouldBe Some(4) - n(3) findPredecessor (_ > 2) shouldBe Some(4) - } - - def `find connected nodes by predicate`: Unit = - withGraph(Di_1.g.asAnyGraph) { g => - def n(outer: Int) = g get outer - - List(0, 3) foreach { i => - n(3) findConnected (_.outer == i) shouldBe empty - } - n(2) findConnected (_.outer == 4) shouldBe Some(4) - n(3) findConnected (_.outer > 3) should (be(Some(4)) or be(Some(5))) - } - - def `find path to a successor`: Unit = - withGraph(factory(1, 2 ~ 3, 3 ~ 4, 5 ~ 6, 6 ~ 1)) { g => - val n1 = g get 1 - n1 pathUntil (_ == n1) shouldBe None - - val n2 = g get 2 - n2 pathUntil (_ == n1) shouldBe None - - val n5 = g get 5 - val n6 = g get 6 - val expected = List(n5, n6, n1) - val r5 = n5 pathUntil (_ < 4) - r5 shouldBe defined - val p5 = r5.get - p5.nodes.toList shouldBe expected - - p5.size shouldBe (expected.size + (expected.size - 1)) - p5.length shouldBe (expected.size - 1) - } - - def `find path to a successor in a minimalistic graph`: Unit = - withGraph(factory(0 ~ 1, 1 ~ 2)) { g => - def n(outer: Int) = g get outer - for (i <- 0 to 2) - (n(0) pathTo n(i)).get.length shouldBe i - } - - def `assert fix_110409 of shortestPathTo`: Unit = - withGraph(factory(0 ~ 1, 1 ~ 2, 2 ~ 3)) { g => - def n(outer: Int) = g get outer - (n(0) shortestPathTo n(0)).get.length shouldBe 0 - (n(0) shortestPathTo n(3)).get.nodes.toList shouldBe List(0, 1, 2, 3) - (n(1) shortestPathTo n(3)).get.nodes.toList shouldBe List(1, 2, 3) - } - - def `traverser to graph`: Unit = - withGraph(Di_1.g.asAnyGraph) { g => - def innerNode(outer: Int) = g get outer - - innerNode(1).outerNodeTraverser.to(factory) should equal(factory(1 ~> 2, 2 ~> 3, 3 ~> 5, 1 ~> 5, 1 ~> 3)) - - innerNode(2).outerNodeTraverser(anyConnected).to(factory) should equal( - factory(1 ~> 2, 2 ~> 3, 4 ~> 3, 3 ~> 5, 1 ~> 5, 1 ~> 3) - ) - - innerNode(3).outerNodeTraverser(predecessors).to(factory) should equal(factory(4 ~> 3, 1 ~> 3, 2 ~> 3, 1 ~> 2)) - } - - def `traverser with an extended visitor`: Unit = - withGraph(UnDi_1.g.asAnyGraph) { g => - import g.ExtendedNodeVisitor - def n(outer: Int) = g get outer - - var lastCount = 0 - n(1).innerNodeTraverser.withKind(DepthFirst) foreach - ExtendedNodeVisitor { (node, count, depth, _) => - count shouldBe (lastCount + 1) - lastCount += 1 - - node.outer match { - case 1 => depth shouldBe 0 - case 2 => depth should (be(1) or be(2) or be(3)) - case 3 => depth should (be(1) or be(2)) - case 4 => depth should (be(2) or be(3)) - case 5 => depth should (be > 0 and be < 5) - } - } - } - - def `traverser withMaxDepth`: Unit = { - import Data.* - object UnDi_1 extends TGraph(factory.from(elementsOfMixed_1)) { - val expectedSumAll = 15 - val expectedSumLayer1 = 12 - val expectedSumLayer2 = 15 - val expectedSumAllExclGt4 = 10 - val expectedSumLayer2ExclGt4 = 9 - } - import UnDi_1.* - withGraph(g.asAnyGraph) { g => - def n(outer: Int) = g get outer - - val bfs_4 = n(4).outerNodeTraverser - bfs_4.sum shouldBe expectedSumAll - bfs_4.withMaxDepth(1).sum shouldBe expectedSumLayer1 - bfs_4.withMaxDepth(2).sum shouldBe expectedSumLayer2 - - val dfs_4 = bfs_4.withKind(DepthFirst) - dfs_4.withMaxDepth(1).sum shouldBe expectedSumLayer1 - dfs_4.withMaxDepth(2).sum shouldBe expectedSumLayer2 - - val sub_4 = bfs_4.withSubgraph(nodes = _ <= 4) - sub_4.sum shouldBe expectedSumAllExclGt4 - sub_4.withMaxDepth(2).sum shouldBe expectedSumLayer2ExclGt4 - sub_4.withKind(DepthFirst).sum shouldBe expectedSumAllExclGt4 - } - } - - def `DownUp traverser`: Unit = - withGraph(Di_1.g.asAnyGraph) { g => - def innerNode(outer: Int) = g get outer - var stack = List.empty[Int] - - innerNode(4).innerNodeDownUpTraverser foreach { case (down, node) => - if (down) stack = node.outer +: stack - else { - stack.head shouldBe node.outer - stack = stack.tail - } - } - stack shouldBe empty - } - - def `DownUp traverser for computing braces`: Unit = { - val root = "A" - withGraph(factory(root ~> "B1", root ~> "B2")) { g => - val innerRoot = g get root - val result = innerRoot.innerNodeDownUpTraverser.foldLeft(ListBuffer.empty[String]) { (buf, param) => - param match { - case (down, node) => - if (down) buf += (if (node eq innerRoot) "(" else "[") += node.toString - else buf += (if (node eq innerRoot) ")" else "]") - } - } - result.foldLeft("")(_ + _) should ( - be("(A[B1][B2])") or - be("(A[B2][B1])") - ) - } - } - - def `DownUp traverser for computing sums`: Unit = { - abstract class Elem(val name: String) { - def balance: Int - } - case class Node(override val name: String) extends Elem(name) { - var sum: Int = 0 - def balance = sum - } - case class Leaf(override val name: String, override val balance: Int) extends Elem(name) - - val root = Node("R") - val (nA, nB, nBA) = (Node("A"), Node("B"), Node("BA")) - - withGraph( - factory[Elem, DiEdge]( - root ~> nA, - root ~> nB, - nA ~> Leaf("LA1", 1), - nA ~> Leaf("LA2", 2), - nB ~> Leaf("B1", 3), - nB ~> nBA, - nBA ~> Leaf("BA1", 10), - nBA ~> Leaf("BA2", 11), - nBA ~> Leaf("BA3", 12) - ).asAnyGraph - ) { g => - (g get root).innerNodeDownUpTraverser foreach { case (down, node) => - if (!down) - node.outer match { - case n: Node => n.sum = node.diSuccessors.foldLeft(0)(_ + _.balance) - case _ => - } - } - val expected = Map(root -> 39, nA -> 3, nB -> 36, nBA -> 33) - g.nodes foreach { - _.outer match { - case n: Node => n.balance shouldBe (expected(n)) - case _ => - } - } - } - } - - def `traverser withDirection`: Unit = { - // https://groups.google.com/forum/?fromgroups=#!topic/scala-internals/9NMPfU4xdhU - object DDi_1 extends TGraph(factory.from(elementsOfDi_1)) { - val expectedSumSuccessorsOf_4 = 12 - val expectedSumPredecessorsOf_4 = 4 - val expectedSumSuccessorsOf_2 = 10 - val expectedSumPredecessorsOf_2 = 3 - val expectedSumAnyConnected = 15 - - val expectedSumLayer1SuccessorsOf_2 = 5 - val expectedSumLayer1PredecessorsOf_2 = 3 - val expectedSumLayer1AnyConnectedWith_2 = 6 - } - import DDi_1.* - withGraph(DDi_1.g.asAnyGraph) { g => - def n(outer: Int) = g get outer - - val maxDepth_1 = Parameters(maxDepth = 1) - - n(4).outerNodeTraverser.sum shouldBe expectedSumSuccessorsOf_4 - n(4).outerNodeTraverser(predecessors).sum shouldBe expectedSumPredecessorsOf_4 - - n(2).outerNodeTraverser.sum shouldBe expectedSumSuccessorsOf_2 - n(2).outerNodeTraverser(predecessors).sum shouldBe expectedSumPredecessorsOf_2 - n(2).outerNodeTraverser(anyConnected).sum shouldBe expectedSumAnyConnected - - n(2).outerNodeTraverser(maxDepth_1).sum shouldBe expectedSumLayer1SuccessorsOf_2 - n(2).outerNodeTraverser(maxDepth_1.withDirection(Predecessors)).sum shouldBe expectedSumLayer1PredecessorsOf_2 - - n(2).outerNodeTraverser(maxDepth_1.withDirection(AnyConnected)).sum shouldBe - expectedSumLayer1AnyConnectedWith_2 - - an[IllegalArgumentException] should be thrownBy { - n(2).innerNodeTraverser(anyConnected) pathTo n(2) - } - an[IllegalArgumentException] should be thrownBy { - n(2).innerNodeTraverser(anyConnected) shortestPathTo n(2) - } - } - } - - def `traverser withOrdering for nodes`: Unit = - withGraph( - factory( - 0 ~> 4, - 0 ~> 2, - 0 ~> 3, - 0 ~> 1, - 1 ~> 13, - 1 ~> 11, - 1 ~> 12, - 2 ~> 22, - 2 ~> 21, - 2 ~> 23, - 3 ~> 32, - 3 ~> 33, - 3 ~> 31, - 4 ~> 42, - 4 ~> 41, - 4 ~> 43 - ) - ) { g => - val root = g get 0 - val nodeOrdering = g.NodeOrdering(Ordering.Int.compare(_, _)) - - val orderedTraverser = root.outerNodeTraverser.withOrdering(nodeOrdering) - orderedTraverser.toList shouldBe ( - List(0 to 4: _*) ++ - List(11 to 13: _*) ++ List(21 to 23: _*) ++ - List(31 to 33: _*) ++ List(41 to 43: _*) - ) - - orderedTraverser.withKind(DepthFirst).toList shouldBe ( - 0 :: - List(1) ::: List(11 to 13: _*) ::: List(2) ::: List(21 to 23: _*) ::: - List(3) ::: List(31 to 33: _*) ::: List(4) ::: List(41 to 43: _*) - ) - } - - def `map Traverser result`: Unit = - withGraph(Di_1.g.asAnyGraph) { g => - val t = g.nodes.head.outerNodeTraverser - t map (_ + 1) shouldBe (t.toList map (_ + 1)) - } - - def `traverser for inner elements`: Unit = - withGraph(Di_1.g.asAnyGraph) { g => - val t = g.nodes.head.innerElemTraverser - - def nodePred(n: g.NodeT) = n.degree > 1 - def edgePred(e: g.EdgeT) = e.ends forall nodePred - - val nodes = t collect { case g.InnerNode(n, _) if nodePred(n) => n } - val edges = t collect { case g.InnerEdge(e, _) if edgePred(e) => e } - - nodes.toSet shouldBe (g.nodes filter nodePred) - edges.toSet shouldBe (g.edges filter edgePred) - } -} diff --git a/coreTestScala3/src/test/scala/scalax/collection/generator/GraphGenSpec.scala b/coreTestScala3/src/test/scala/scalax/collection/generator/GraphGenSpec.scala deleted file mode 100644 index df28a088..00000000 --- a/coreTestScala3/src/test/scala/scalax/collection/generator/GraphGenSpec.scala +++ /dev/null @@ -1,82 +0,0 @@ -package scalax.collection.generator - -import org.scalacheck.{Arbitrary, Gen} -import org.scalacheck.Arbitrary.arbitrary -import org.scalatest.matchers.should.Matchers -import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks -import org.scalatest.refspec.RefSpec - -import scalax.collection.edges.* -import scalax.collection.immutable.Graph - -class GraphGenSpec extends RefSpec with Matchers with ScalaCheckPropertyChecks { - - final val minSuccessful = 5 - implicit val config: PropertyCheckConfiguration = - PropertyCheckConfiguration(minSuccessful = minSuccessful, maxDiscardedFactor = 1.0) - - object `nr of minimum successful tests` { - def `should be met`: Unit = { - var count = 0 - forAll { (i: Int) => - count += 1 - } - count should be(minSuccessful) - } - } - - object `outer node set` { - val order = 5 - implicit val arbitraryOuterNodes: Arbitrary[Set[Int]] = - new GraphGen[Int, DiEdge[Int], Graph]( - Graph, - order, - Gen.choose(0, 10 * order), - NodeDegreeRange(1, 4), - Set(DiEdge) - ).outerNodeSet - - def `should conform to the passed size`: Unit = - forAll(arbitrary[Set[Int]]) { (outerNodes: Set[Int]) => - outerNodes should have size order - } - } - - type IntDiGraph = Graph[Int, DiEdge[Int]] - - def checkMetrics(g: IntDiGraph, metrics: GraphGen.Metrics[Int]): Unit = { - import metrics._ - - val degrees = g.degreeSeq - val tolerableMaxExceed: Int = if (g.isHyper) 8 else 1 - - g.order should be(order) - g.isConnected should be(connected) - - degrees.min should be >= (nodeDegrees.min) - degrees.max should be <= (nodeDegrees.max + tolerableMaxExceed) - - g.totalDegree should ( - be >= (expectedTotalDegree - maxDegreeDeviation) and - be <= (expectedTotalDegree + maxDegreeDeviation) - ) - } - - object `tiny connected graph of [Int,DiEdge]` { - implicit val arbitraryGraph: Arbitrary[Graph[Int, DiEdge[Int]]] = GraphGen.tinyConnectedIntDi[Graph](Graph) - - def `should conform to tiny metrics`: Unit = - forAll(arbitrary[IntDiGraph]) { (g: IntDiGraph) => - checkMetrics(g, GraphGen.TinyInt) - } - } - - object `small connected graph of [Int,DiEdge]` { - implicit val arbitraryGraph: Arbitrary[Graph[Int, DiEdge[Int]]] = GraphGen.smallConnectedIntDi[Graph](Graph) - - def `should conform to small metrics`: Unit = - forAll(arbitrary[IntDiGraph]) { (g: IntDiGraph) => - checkMetrics(g, GraphGen.SmallInt) - } - } -} diff --git a/coreTestScala3/src/test/scala/scalax/collection/generator/RandomGraphSpec.scala b/coreTestScala3/src/test/scala/scalax/collection/generator/RandomGraphSpec.scala deleted file mode 100644 index c1889fb1..00000000 --- a/coreTestScala3/src/test/scala/scalax/collection/generator/RandomGraphSpec.scala +++ /dev/null @@ -1,122 +0,0 @@ -package scalax.collection.generator - -import scala.reflect.ClassTag - -import org.scalatest.Ignore -import org.scalatest.matchers.should.Matchers -import org.scalatest.refspec.RefSpec - -import scalax.collection.AnyGraph -import scalax.collection.edges.* -import scalax.collection.generic.{Edge, EdgeCompanionBase, GenericGraphFactory} -import scalax.collection.GraphLike -import scalax.collection.immutable.Graph -import scalax.collection.mutable.{Graph => MGraph} - -class RandomGraphSpec extends RefSpec with Matchers { - - import RandomGraph._ - val normal = new IntFactory { - val order = 1000 - val nodeDegrees = NodeDegreeRange(4, 15) - } - - /** Creates a `RandomGraph` generator that produces a graph - * with a constant order, constant `NodeDegreeRange` and a single edge type. - */ - def generator[N, E <: Edge[N], G[X, Y <: Edge[X]] <: AnyGraph[X, Y] with GraphLike[X, Y, G]]( - edgeCompanion: EdgeCompanionBase, - gCompanion: GenericGraphFactory[G], - connected: Boolean - )(implicit nodeTag: ClassTag[N], metrics: Metrics[N]) = - new RandomGraph[N, E, G]( - gCompanion, - metrics.order, - metrics.nodeGen, - metrics.nodeDegrees, - Set(edgeCompanion), - connected - ) - - def checkOrder(g: AnyGraph[Int, DiEdge[Int]])(implicit metrics: Metrics[Int]): Unit = - g.order should be(metrics.order) - - def checkSize(g: AnyGraph[Int, DiEdge[Int]])(implicit metrics: Metrics[Int]): Unit = { - import metrics._ - val totalDegree = g.totalDegree - val deviation = totalDegree - expectedTotalDegree - if (false) - println(f""" total degree=$totalDegree, - | isDense=$isDense, - | maxDev=$maxDegreeDeviation, - | deviation=$deviation, - | (${100f * deviation / totalDegree}%2.2f percent)""".stripMargin.linesIterator.mkString) - totalDegree should (be >= (expectedTotalDegree - maxDegreeDeviation) and - be <= (expectedTotalDegree + maxDegreeDeviation)) - } - - object `disconnected random graph` { - def `should have expected size`: Unit = { - implicit val metrics: Metrics[Int] = normal - val g = generator[Int, DiEdge[Int], Graph](DiEdge, Graph, false).draw - checkOrder(g) - checkSize(g) - } - } - - object `connected random graph` { - def `should have expected size`: Unit = { - implicit lazy val metrics: Metrics[Int] = normal - val g = generator[Int, DiEdge[Int], MGraph](DiEdge, MGraph, true).draw - checkOrder(g) - checkSize(g) - } - } - - object `dense random graph` { - @Ignore def `should have expected size`: Unit = { - implicit val dense: Metrics[Int] = new IntFactory { - val order = 100 - val nodeDegrees = NodeDegreeRange(55, 90) - } - dense.isDense shouldBe true - val g = generator[Int, DiEdge[Int], Graph](DiEdge, Graph, true).draw - - checkOrder(g) - checkSize(g) - } - } - - /* TODO L - object `default weighted random graph edges` { - def `should have distinct weights`: Unit = { - implicit val metrics: Metrics[Int] = RandomGraph.TinyInt - val g = generator[Int, WDiEdge[Int], Graph](WDiEdge, Graph, true).draw - val weights = MSet.empty[Long] ++ (g.edges map (_.weight)) - weights.size should be(g.size) - } - } - - object `default labeled random graph edges` { - def `should have distinct labels`: Unit = { - implicit val metrics: Metrics[Int] = RandomGraph.SmallInt - val g = generator[Int, LDiEdge, Graph](LDiEdge, Graph, true).draw - val labels = MSet.empty[Any] ++ (g.edges map (_.label)) - labels.size should be(g.size) - } - } - */ - - object `huge graph` { - @Ignore def `should have expected size`: Unit = { - implicit val huge: Metrics[Int] = new IntFactory { - val order = 100000 - val nodeDegrees = normal.nodeDegrees - } - val g = generator[Int, DiEdge[Int], MGraph](DiEdge, MGraph, true).draw - // TODO should be fast enough - checkOrder(g) - checkSize(g) - } - } -} diff --git a/coreTestScala3/src/test/scala/scalax/collection/labeled/TraversalSpec.scala b/coreTestScala3/src/test/scala/scalax/collection/labeled/TraversalSpec.scala deleted file mode 100644 index ae5953b3..00000000 --- a/coreTestScala3/src/test/scala/scalax/collection/labeled/TraversalSpec.scala +++ /dev/null @@ -1,299 +0,0 @@ -package scalax.collection.labeled - -import scala.concurrent.duration.* - -//import scala.util.Random -//import org.scalacheck.Arbitrary.arbitrary -//import org.scalacheck.* -import org.scalatest.* -import org.scalatest.matchers.should.Matchers -import org.scalatest.refspec.RefSpec -import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks - -import scalax.collection.Data.* -import scalax.collection.OuterImplicits.* -//import generator.GraphGen -import scalax.collection.generic.{AnyEdge, Edge, GenericGraphCoreFactory} -import scalax.collection.edges.* -import scalax.collection.edges.labeled.* -import scalax.collection.edges.multilabeled.* -import scalax.collection.GraphTraversal.* -import scalax.collection.visualization.Visualizer -import scalax.collection.{immutable, mutable, AnyGraph, GraphLike, IntelliJ, MSet, TGraph} - -import scala.collection.mutable.ListBuffer - -class TraversalSpec - extends Suites( - new Traversal[immutable.Graph](immutable.Graph), - new Traversal[mutable.Graph](mutable.Graph) - ) - -final private class Traversal[G[N, E <: Edge[N]] <: AnyGraph[N, E] with GraphLike[N, E, G]]( - val factory: GenericGraphCoreFactory[G] -) extends RefSpec - with Matchers - with ScalaCheckPropertyChecks - with IntelliJ[G] - with Visualizer { - - implicit val config: PropertyCheckConfiguration = - PropertyCheckConfiguration(minSuccessful = 5, maxDiscardedFactor = 1.0) - - val predecessors = Parameters(direction = Predecessors) - val anyConnected = Parameters(direction = AnyConnected) - - def `assert bug 9 of shortestPathTo is fixed`: Unit = - withGraph(factory(0 ~> 1 % 3, 0 ~> 2 % 4, 1 ~> 3 % 3, 2 ~> 3 % 1)) { g => - def n(outer: Int) = g get outer - (n(0) shortestPathTo n(3)).get.nodes.toList should be(List(0, 2, 3)) - } - - def `shortestPathTo in WDi_1`: Unit = - withGraph(factory(elementsOfWDi_1: _*)) { g => - def n(outer: Int) = g get outer - - n(5) shortestPathTo n(4) shouldBe None - n(5) shortestPathTo n(1) shouldBe None - n(3) shortestPathTo n(1) shouldBe None - - (n(1) shortestPathTo n(3)).get.nodes.toList shouldBe List(1, 3) - (n(4) shortestPathTo n(5)).get.nodes.toList shouldBe List(4, 3, 5) - (n(1) shortestPathTo n(5)).get.nodes.toList shouldBe List(1, 5) - } - - def `shortestPathTo in WDi_1 using Float`: Unit = - withGraph(factory(elementsOfWDi_1: _*)) { g => - def n(outer: Int) = g get outer - - def weight(e: g.EdgeT): Float = 0.5f + e.weight.toFloat - def reverseWeight(e: g.EdgeT): Double = 41 - e.weight - - n(5) shortestPathTo (n(4), weight) shouldBe empty - - (n(1) shortestPathTo (n(3), weight)).get.nodes.to(LazyList) should contain theSameElementsInOrderAs Array(1, 3) - (n(1) shortestPathTo (n(3), reverseWeight)).get.nodes.to(LazyList) should contain theSameElementsInOrderAs Array( - 1, - 2, - 3 - ) - } - - def `shortestPathTo in WUnDi_1`: Unit = - withGraph(factory(elementsOfWMixed_1: _*)) { g => - def shortestPathNodes(from: Int, to: Int): LazyList[g.NodeT] = { - def n(value: Int): g.NodeT = g get value - - val path = n(from) shortestPathTo n(to) - path shouldBe defined - path.get.nodes.to(LazyList) - } - shortestPathNodes(2, 5) should contain theSameElementsInOrderAs Array(2, 3, 4, 5) - shortestPathNodes(4, 5) should contain theSameElementsInOrderAs Array(4, 5) - shortestPathNodes(1, 3) should (contain theSameElementsInOrderAs (Array(1, 3)) or - contain theSameElementsInOrderAs (Array(1, 5, 3))) - shortestPathNodes(5, 4) should contain theSameElementsInOrderAs Array(5, 3, 4) - shortestPathNodes(3, 1) should contain theSameElementsInOrderAs Array(3, 4, 5, 1) - } - - def `shortestPathTo withMaxDepth`: Unit = - withGraph(factory(elementsOfWMixed_1: _*)) { g => - def n(value: Int): g.NodeT = g get value - - n(2).innerNodeTraverser.withMaxDepth(2).shortestPathTo(n(5)).get.nodes.toList should be(List(2, 3, 5)) - } - - def `shortestPathTo withMaxWeight`: Unit = - withGraph(factory(elementsOfWMixed_1: _*)) { g => - def n(value: Int): g.NodeT = g get value - - val t = n(2).innerNodeTraverser - t.withMaxWeight(3).shortestPathTo(n(5)) shouldBe defined - t.withMaxWeight(2).shortestPathTo(n(5)) shouldBe empty - } - - // see diagram WUnDi-2.jpg - val eUnDi_2 = List[AnyEdge[Int]](1 ~ 2 %% 4, 2 ~ 3 %% -1, 1 ~> 3 %% 5, 1 ~ 3 %% 4, 1 ~> 2 %% 3, 2 ~ 2 %% 1) - val gUnDi_2 = - factory.from[Int, AnyEdge[Int]](Set.empty, eUnDi_2).asAnyGraph - - def `shortestPathTo in UnDi_2`: Unit = - withGraph(gUnDi_2) { g => - def n(value: Int) = g get value - - val p1_3 = n(1).shortestPathTo(n(3)).get - p1_3.nodes.toList should be(List(1, 2, 3)) - p1_3.edges.toList should be(List(eUnDi_2(4), eUnDi_2(1))) - - val p2_1 = (n(2) shortestPathTo n(1)).get - p2_1.nodes.toList should be(List(2, 3, 1)) - p2_1.edges.toList should be(List(eUnDi_2(1), eUnDi_2(3))) - - val p3_1 = (n(3) shortestPathTo n(1)).get - p3_1.nodes.toList should be(List(3, 2, 1)) - p3_1.edges.toList should be(List(eUnDi_2(1), eUnDi_2(0))) - - val p3_3 = (n(3) shortestPathTo n(3)).get - p3_3.nodes.toList should be(List(3)) - p3_3.edges.toList shouldBe empty - } - - def `traverser withSubgraph`: Unit = - withGraph(gUnDi_2) { g => - def n(value: Int) = g get value - - val p2_1_nNE3 = n(2).withSubgraph(nodes = _.outer != 3).pathTo(n(1)).get - p2_1_nNE3.nodes.toList should be(List(2, 1)) - p2_1_nNE3.edges.toList should be(List(2 ~ 1 %% 4)) - - val p1_3_wGT4 = n(1).withSubgraph(edges = _.weight > 4).pathTo(n(3)).get - p1_3_wGT4.nodes.toList should be(List(1, 3)) - p1_3_wGT4.edges.toList should be(List(eUnDi_2(2))) - - val p1_3_wLT4 = n(1).withSubgraph(edges = _.weight < 4).pathTo(n(3)).get - p1_3_wLT4.nodes.toList should be(List(1, 2, 3)) - p1_3_wLT4.edges.toList should be(List(eUnDi_2(4), eUnDi_2(1))) - } - - object `traverser withMaxWeight` { - object WMixed_1 extends TGraph[Int, AnyEdge[Int], G](factory.from(elementsOfWMixed_1)) - import WMixed_1._ - - private def check(kind: Kind): Unit = - List[Long](Long.MaxValue, 5, 4, 3, 2, 1, 0).map(max => n(1).withKind(kind).withMaxWeight(max).size) shouldBe - List(5, 4, 3, 2, 1, 1, 1) - - def `calling DepthFirst`: Unit = withGraph(WMixed_1.g) { _ => - check(DepthFirst) - } - def `calling BreadthFirst`: Unit = withGraph(WMixed_1.g) { _ => - check(BreadthFirst) - } - } - - def `traverser to graph`: Unit = { - object Di_1 extends TGraph(factory(elementsOfDi_1: _*)) - withGraph(Di_1.g) { g => - def innerNode(outer: Int) = g get outer - - innerNode(1).outerNodeTraverser.to(factory) should equal(factory(1 ~> 2, 2 ~> 3, 3 ~> 5, 1 ~> 5, 1 ~> 3)) - - innerNode(2).outerNodeTraverser(anyConnected).to(factory) should equal( - factory(1 ~> 2, 2 ~> 3, 4 ~> 3, 3 ~> 5, 1 ~> 5, 1 ~> 3) - ) - - innerNode(3).outerNodeTraverser(predecessors).to(factory) should equal(factory(4 ~> 3, 1 ~> 3, 2 ~> 3, 1 ~> 2)) - } - } - - def `traverser with a visitor`: Unit = - withGraph(gUnDi_2) { g => - def n(value: Int) = g get value - - val nodes = ListBuffer[g.NodeT]() - val edges = ListBuffer[g.EdgeT]() - val traverser = n(2).innerElemTraverser.withSubgraph(nodes = _.outer != 3) - traverser.pathTo(n(1)) { - case n: g.InnerNode => nodes += n.asNodeT - case e: g.InnerEdge => edges += e.asEdgeT - } - - nodes shouldBe List(n(2), n(1)) - edges.toList.sorted(g.BaseInnerEdge.WeightOrdering) shouldBe List(eUnDi_2(1), eUnDi_2(5), eUnDi_2(0)) - } - - def `shortestPathTo in the flight example graph`: Unit = { - import scalax.collection.labeled.aviation._ - - val (jfc, lhr, dme, svx, fra, prg) = - (Airport("JFC"), Airport("LHR"), Airport("DME"), Airport("SVX"), Airport("FRA"), Airport("PRG")) - - val flights: List[Flight] = - List( - jfc ~> dme :++ ("UN 2222", Nil, 8.hours + 50.minutes), - dme ~> svx :++ ("UN 109", Nil, 2.hours + 15.minutes), - jfc ~> lhr :++ ("BA 174", Nil, 6.hours + 50.minutes), - jfc ~> fra :++ ("LH 400", Nil, 8.hours + 20.minutes), - jfc ~> fra :++ ("UA 8840", Nil, 7.hours + 35.minutes), - lhr ~> dme :++ ("BA 872", Nil, 4.hours), - lhr ~> dme :++ ("SU 242", Nil, 3.hours + 50.minutes), - lhr ~> fra :++ ("LH 903", Nil, 1.hours + 35.minutes), - lhr ~> prg :++ ("BA 860", Nil, 2.hours), - fra ~> lhr :++ ("LH 920", Nil, 1.hours + 35.minutes), - fra ~> dme :++ ("LH 1444", Nil, 3.hours + 10.minutes), - fra ~> svx :++ ("LH 1480", Nil, 4.hours + 35.minutes), - prg ~> svx :++ ("U6 902", Nil, 4.hours + 25.minutes) - ) - - def flight(flightNo: String) = flights.find(_.flightNo == flightNo).get - - val g = factory.from[Airport, Flight](Set.empty, flights) - - withGraph(g.asAnyGraph) { g => - val shp1 = (g get jfc).withSubgraph(edges = _.airline != "UN") shortestPathTo (g get dme) - shp1.get.nodes.toList should be(List(jfc, lhr, dme)) - shp1.get.edges.toList should be(List(flight("BA 174"), flight("SU 242"))) - - val shp2 = (g get lhr).withSubgraph(edges = _.airline != "SU") shortestPathTo (g get svx) - shp2.get.edges.toList should be(List(flight("LH 903"), flight("LH 1480"))) - - val shp3 = (g get dme).withSubgraph(nodes = _ != fra) shortestPathTo (g get jfc) - shp3 should be(None) - - val shp4 = (g get jfc).withSubgraph(nodes = _ != dme) shortestPathTo (g get svx) - shp4.get.nodes.toList should be(List(jfc, fra, svx)) - shp4.get.edges.toList should be(List(flight("UA 8840"), flight("LH 1480"))) - - val visited = MSet[g.EdgeT]() - (g get jfc).innerEdgeTraverser.shortestPathTo(g get lhr) { (e: g.EdgeT) => - visited += e - } - val visitedSorted = visited.toList.sortWith((a: g.EdgeT, b: g.EdgeT) => a.flightNo < b.flightNo) - visitedSorted.sameElements( - List(flight("BA 174"), flight("LH 400"), flight("UA 8840"), flight("UN 2222")) - ) should be(true) - } - } - - def `traverser withOrdering for edges`: Unit = { - val outerEdges = List( - 1 ~> 4 % 2, - 1 ~> 2 % 5, - 1 ~> 3 % 4, - 3 ~> 6 % 4, - 3 ~> 5 % 5, - 3 ~> 7 % 2 - ) - withGraph(factory(outerEdges: _*)) { g => - val root = g get 1 - - def edgeOrdering = g EdgeOrdering (g.BaseInnerEdge.WeightOrdering.reverse.compare _) - - val orderedTraverser = root.outerNodeTraverser.withOrdering(edgeOrdering) - orderedTraverser.toList should be(List(1 to 7: _*)) - orderedTraverser.withKind(DepthFirst).toList should be(List(1, 2, 3, 5, 6, 7, 4)) - } - } - - /* TODO GraphGen - def `shortest path exists if path exists`: Unit = { - implicit val arbitraryWDiGraph = Arbitrary { - import GraphGen.SmallInt._ - new GraphGen[Int, WDiEdge[Int], G](factory, order, nodeGen, nodeDegrees, Set(WDiEdge), connected).apply - } - val r = new Random - - forAll(arbitrary[G[Int, WDiEdge]]) { - given(_) { g => - def drawNode = g.nodes.draw(r) - - val (n1, n2) = (drawNode, drawNode) - val path = n1 pathTo n2 - val shortestPath = n1 shortestPathTo n2 - - path.isDefined should equal(shortestPath.isDefined) - } - } - } - */ -} diff --git a/coreTestScala3/src/test/scala/scalax/collection/labeled/aviation/Airport.scala b/coreTestScala3/src/test/scala/scalax/collection/labeled/aviation/Airport.scala deleted file mode 100644 index 6f5b765f..00000000 --- a/coreTestScala3/src/test/scala/scalax/collection/labeled/aviation/Airport.scala +++ /dev/null @@ -1,5 +0,0 @@ -package scalax.collection.labeled.aviation - -case class Airport(code: String) { - override def toString: String = code // leave out Airport-prefix -} diff --git a/coreTestScala3/src/test/scala/scalax/collection/labeled/aviation/Flight.scala b/coreTestScala3/src/test/scala/scalax/collection/labeled/aviation/Flight.scala deleted file mode 100644 index 9797daa5..00000000 --- a/coreTestScala3/src/test/scala/scalax/collection/labeled/aviation/Flight.scala +++ /dev/null @@ -1,47 +0,0 @@ -package scalax.collection.labeled.aviation - -import scalax.collection.OneOrMore -import scalax.collection.generic.{AbstractDiEdge, LDiEdgeToString, MultiEdge, MultiLEdgeToString, PartialEdgeMapper} - -import java.time.{DayOfWeek, LocalTime} -import scala.concurrent.duration.FiniteDuration - -/** A labeled edge to represent flights between airports. - * - * Defines a custom edge type that can be used for a flight route map. - * - * A `Flight` has several attributes like `departure` and `duration`. - * To enable multiple flights between airports, the key is extended by `flightNo`. - * - * @see EditingTypedSpec.scala. - * - * @param departure The departure airport - * @param destination The destination airport - * @param flightNo The flight Id as a key attribute consisting of the airline short and a flight number of that airline. - * @param departures daytime of departure - * @param duration of flight - */ -case class Flight( - departure: Airport, - destination: Airport, - flightNo: String, - departures: List[(DayOfWeek, LocalTime)], - duration: FiniteDuration -) extends AbstractDiEdge[Airport](departure, destination) - with MultiEdge - with LDiEdgeToString - with MultiLEdgeToString - with PartialEdgeMapper[Flight] { - - override def weight: Double = duration.toMinutes.toDouble - - def airline: String = flightNo.takeWhile(_.isLetter) - - override def extendKeyBy: OneOrMore[String] = OneOrMore(flightNo) - - override protected def labelToString: String = s"($flightNo, $departures, $duration)" - - override def map[N]: PartialFunction[(N, N), Flight] = { case (from: Airport, to: Airport) => - copy(from, to) - } -} diff --git a/coreTestScala3/src/test/scala/scalax/collection/labeled/aviation/package.scala b/coreTestScala3/src/test/scala/scalax/collection/labeled/aviation/package.scala deleted file mode 100644 index 4505306c..00000000 --- a/coreTestScala3/src/test/scala/scalax/collection/labeled/aviation/package.scala +++ /dev/null @@ -1,46 +0,0 @@ -package scalax.collection.labeled - -import scalax.collection.AnyGraph -import scalax.collection.edges.DiEdge -import scalax.collection.generic.{UnapplyLabel, UnapplyLabeledEdge} - -import java.time.{DayOfWeek, LocalTime} -import scala.concurrent.duration._ - -/** Convenience infix constructors and extractors for the `Flight` edge. - */ -package object aviation { - - type AnyFlightGraph = AnyGraph[Airport, Flight] - - object immutable { - import scalax.collection.immutable.{Graph, TypedGraphFactory} - - type FlightGraph = Graph[Airport, Flight] - object FlightGraph extends TypedGraphFactory[Airport, Flight] - } - - object mutable { - import scalax.collection.mutable.{Graph, TypedGraphFactory} - - type FlightGraph = Graph[Airport, Flight] - object FlightGraph extends TypedGraphFactory[Airport, Flight] - } - - /** Optionally facilitate infix constructors like `airportA ~> airportB :++ (flightNo, departures, duration)` - */ - implicit class InfixFlightConstructor(val e: DiEdge[Airport]) extends AnyVal { - - def :++(flightNo: String, departures: List[(DayOfWeek, LocalTime)], duration: FiniteDuration) = - Flight(e.source, e.target, flightNo, departures, duration) - } - - type Labels = (String, List[(DayOfWeek, LocalTime)], FiniteDuration) - - /** Optionally allow for pattern matching like `airportA :~> airportB ++: (flightNo, departures, duration)` - */ - object :~> extends UnapplyLabeledEdge[Airport, Flight, Labels] { - protected def label(edge: Flight): Labels = (edge.flightNo, edge.departures, edge.duration) - } - object ++: extends UnapplyLabel[Airport, Labels] -} diff --git a/coreTestScala3/src/test/scala/scalax/collection/mutable/ArraySetSpec.scala b/coreTestScala3/src/test/scala/scalax/collection/mutable/ArraySetSpec.scala deleted file mode 100644 index 7d501581..00000000 --- a/coreTestScala3/src/test/scala/scalax/collection/mutable/ArraySetSpec.scala +++ /dev/null @@ -1,160 +0,0 @@ -package scalax.collection.mutable - -import scala.collection.mutable.{Set => MutableSet} -import scala.util.chaining._ - -import scalax.collection.immutable.SortedArraySet - -import org.scalatest.matchers.should.Matchers -import org.scalatest.refspec.RefSpec - -class ArraySetSpec extends RefSpec with Matchers { - - implicit val hints: ArraySet.Hints = ArraySet.Hints( - initialCapacity = 4, - capacityIncrement = 4, - hashTableThreshold = 12, - compactUpToUsed = 100 - ) - - private class IntSequence { - private var i = 1 - def draw(): Int = i tap (_ => i += 1) - } - - object `ArraySet ` { - def `can grow`: Unit = { - val arr = ArraySet.emptyWithHints[Int] - arr.capacity shouldBe hints.initialCapacity - - val integers = new IntSequence - def add(numberOfAdditions: Int, expectedCapacity: Int): Unit = - for (i <- 0 until numberOfAdditions) { - arr += integers.draw() - arr.capacity shouldBe expectedCapacity - } - - var toAdd, nextCapacity = hints.initialCapacity - while (nextCapacity <= hints.hashTableThreshold) { - add(toAdd, nextCapacity) - nextCapacity += hints.capacityIncrement - toAdd = hints.capacityIncrement - } - add(1, 0) - arr.isArray shouldBe false - } - - def `may be compacted`: Unit = { - val integers = new IntSequence - val toAdd = hints.initialCapacity + 1 - val arr = ArraySet.emptyWithHints[Int] ++= - (for (i <- 1 to toAdd) yield integers.draw()) - arr.compact() - arr.capacity shouldBe toAdd - } - - def `may be configured to be represented solely by a HashSet`: Unit = { - val edges = new IntSequence - val arr = ArraySet.emptyWithHints[Int](ArraySet.Hints.HashOnly) - def check(): Unit = { - arr.isArray shouldBe false - arr.capacity shouldBe 0 - } - check() - - arr += edges.draw() - check() - - arr.compact() - check() - } - - def `supports hints`: Unit = { - val edges = new IntSequence - val arr = ArraySet.emptyWithHints[Int](ArraySet.Hints(0, 4, 8, 0)) - arr += edges.draw() - arr.capacity shouldBe 4 - } - - def `supports hints properly when filtered`: Unit = { - val integers = new IntSequence - type E = Int - val arr = ArraySet.emptyWithHints[E] - val size = hints.initialCapacity + 1 - for (i <- 1 to size) arr += integers.draw() - - val taken = arr take size - taken.isInstanceOf[ArraySet[_]] shouldBe true - taken should have size size - - def setInterface[A](set: MutableSet[A], n: Int): MutableSet[A] = set take n - setInterface(arr, size - 1).isInstanceOf[ArraySet[_]] shouldBe true - - val filtered0 = arr filter (_ => false) - filtered0.isInstanceOf[ArraySet[_]] shouldBe true - taken should have size size - filtered0.hints.initialCapacity should equal(arr.size) - - for (i <- 1 to hints.capacityIncrement) arr += integers.draw() - val filteredEven = arr filter (_ % 2 == 0) - filteredEven.hints.initialCapacity should equal(arr.size) - } - - def `is sortable`: Unit = { - val sorted = ArraySet(3, 6, 0, -3).sorted - - sorted.isInstanceOf[SortedArraySet[_]] shouldBe true - sorted.filter(_ < 0).isInstanceOf[SortedArraySet[_]] shouldBe true - sorted.toList shouldBe List(-3, 0, 3, 6) - sorted.rangeFrom(1) shouldBe SortedArraySet(3, 6) - sorted.rangeUntil(0) shouldBe SortedArraySet(-3) - sorted.range(-10, 10) shouldBe sorted - sorted.range(-10, 1) shouldBe SortedArraySet(-3, 0) - sorted.range(-10, -3) shouldBe SortedArraySet.empty[Int] - sorted.range(-10, -4) shouldBe SortedArraySet.empty[Int] - } - - def `supports ++`: Unit = { - val a = ArraySet.empty[Int] - val b = ArraySet(1) - val c = ArraySet(2) - - a.clone shouldBe empty - a ++ b shouldBe b - b ++ c shouldBe (b.toSet ++ c.toSet) - } - - object `supports upsert` { - case class Mutable(key: Int)(var i: Int = 0) - - def upsert(setSize: Int): Unit = { - val integers = new IntSequence - val pos = 1 - pos shouldBe <(setSize) - - val arr = ArraySet.emptyWithHints[Mutable] ++= - (for (i <- 1 to setSize) yield Mutable(integers.draw())()) - arr should have size setSize - - def mutable = arr.drop(pos).head - val newI = mutable.i + 1 - val toUpsert = Mutable(mutable.key)(newI) - toUpsert should equal(mutable) - - val inserted = arr.upsert(toUpsert) - inserted shouldBe false - mutable.i shouldBe newI - - arr.size shouldBe setSize - arr.upsert(Mutable(integers.draw())()) should ===(true) - arr.size shouldBe (setSize + 1) - } - - def `when represented by an Array`: Unit = - upsert(hints.hashTableThreshold - 3) - - def `when represented by a HashSet`: Unit = - upsert(hints.hashTableThreshold + 3) - } - } -} diff --git a/coreTestScala3/src/test/scala/scalax/collection/visualization/Visualizer.scala b/coreTestScala3/src/test/scala/scalax/collection/visualization/Visualizer.scala deleted file mode 100644 index 806d76dd..00000000 --- a/coreTestScala3/src/test/scala/scalax/collection/visualization/Visualizer.scala +++ /dev/null @@ -1,54 +0,0 @@ -package scalax.collection.visualization - -import org.scalatest.exceptions.TestFailedException - -import scalax.collection.AnyGraph -import scalax.collection.ToString.* -import scalax.collection.generic.Edge - -/** Scalatest support for graph visualization in case of failures. - * - * Drawing is commented out because - * - org.gephi with all it's dependencies is rather heavy - * - managing Gephi releases proved cumbersome over time - * - there was no frequent usage - * - permission to write files needs be additionally configured in the CI. - * - * However it's intended to add a more lightweight drawing implementation in future. - */ -trait Visualizer /*extends Drawable*/ { - - final def withGraph[N, E <: Edge[N]](graph: AnyGraph[N, E])(test: AnyGraph[N, E] => Unit): Unit = { - - def reThrow(ex: TestFailedException, messageExtension: String) = - throw ex.modifyMessage(_.map { testMessage => - s"""$testMessage - |------------ given ------------ - |$messageExtension - |-------------------------------""".stripMargin - }) - - try test(graph) - catch { - case ex: TestFailedException => - /* - makeImage( - graph, - path = "log/", - name = (ex.failedCodeFileName match { - case Some(fileName) => fileName - case None => "failed_test" - }) + (ex.failedCodeLineNumber match { - case Some(number) => "_line" + number.toString - case None => "" - }) + ".png" - ) match { - case Success(f) => reThrow(ex, s"The graph image is available at file://${f.getAbsolutePath}") - case Failure(e) => reThrow(ex, s"Graph image generation failed with `${e.getMessage}`.") - } - */ - val small = graph.edges.size < 10 && graph.edges.toString.length < 100 - reThrow(ex, graph.render(if (small) SetsOnSeparateLines() else SetElemsOnSeparateLines())) - } - } -} diff --git a/coreTestScala3/src/test/scala/scalax/time/MicroBenchmark.scala b/coreTestScala3/src/test/scala/scalax/time/MicroBenchmark.scala deleted file mode 100644 index 23319304..00000000 --- a/coreTestScala3/src/test/scala/scalax/time/MicroBenchmark.scala +++ /dev/null @@ -1,135 +0,0 @@ -package scalax.time - -import scala.collection.mutable.ArrayBuffer -import scala.language.implicitConversions - -/** Provides lightweight syntax for simple time measurement and the comparison of results. - * Not aimed at sophisticated JVM benchmarking. - * - * Use `time`, `measure` or `measureAll` to get absolute times. - * Use `relativeTime` or `relativeTimes` to get relative times. - * Except `time`, all methods return `Result` that is comprised of the computation result(s) and elapsed times. - * `warmUp` is the number of repeated computations prior to any measurement. - * `repetitions` is the number of the repeated computation that will be measured. - */ -object MicroBenchmark { - import scala.math.BigInt.* - import scala.math.Numeric - - implicit final class NanoSecond(val value: Long) extends AnyVal { - def relativeTo(decimals: Int = 2)(that: NanoSecond): Float = - round(this.value.toFloat / that.value.toFloat, decimals) - } - - // allows to call functions requiring implicit Numeric such as sum - implicit object NanoSecondNumeric extends Numeric[NanoSecond] { - implicit def nanoSecondToLong(ns: NanoSecond): Long = ns.value - val num = Numeric.LongIsIntegral - def plus(x: NanoSecond, y: NanoSecond) = num.plus(x, y) - def minus(x: NanoSecond, y: NanoSecond) = num.minus(x, y) - def times(x: NanoSecond, y: NanoSecond) = num.times(x, y) - def negate(x: NanoSecond) = num.negate(x) - def fromInt(x: Int) = x.toLong - def toInt(x: NanoSecond) = x.value.toInt - def toLong(x: NanoSecond) = x.value.toLong - def toFloat(x: NanoSecond) = x.value.toFloat - def toDouble(x: NanoSecond) = x.value.toDouble - def compare(x: NanoSecond, y: NanoSecond) = num.compare(x, y) - def parseString(str: String) = throw new UnsupportedOperationException - } - - sealed abstract class MeasurementResult[A](result: A) { - def mediumNanoSecs: Long - def relativeTo(decimals: Int = 2)(that: MeasurementResult[A]) = - this.mediumNanoSecs.relativeTo(decimals)(that.mediumNanoSecs) - protected def toStringPrefix: String - protected def optToStringParams = "" - override def toString = s"$toStringPrefix($mediumNanoSecs ns, $result$optToStringParams)" - } - - case class SingleResult[A](nanoSecs: Long, result: A) extends MeasurementResult(result) { - def mediumNanoSecs = nanoSecs - protected def toStringPrefix = "Result" - } - - case class Result[A](result: A, times: ArrayBuffer[Long] = ArrayBuffer.empty) extends MeasurementResult(result) { - def this(result: A, firstNanoSecs: Long) = this(result, ArrayBuffer(firstNanoSecs)) - def mediumNanoSecs: Long = times.sum / times.size - def +=(nanoSecs: Long): this.type = { this.times += nanoSecs; this } - def nanoSecs = times.iterator - protected def toStringPrefix = "Results" - override protected def optToStringParams = s""", {${nanoSecs mkString " "}}""" - } - - case class Results[A](list: List[Result[A]]) { - def relativeTimes(decimals: Int = 2): List[Float] = { - val mA = list.head - 1f :: (list.tail map (r => r.relativeTo(decimals)(mA))) - } - } - - private def requireEq[A](r1: A, r2: A): Unit = require( - r1 == r2, - s"'$r1' != '$r2' but blocks are expected to return the same result. Otherwise set requireEqualResults to false." - ) - - def once[A](block: => A): SingleResult[A] = { - val start = System.nanoTime - val res = block - val end = System.nanoTime - SingleResult(end - start, res) - } - - private def once[A](byName: ByName[A]): SingleResult[A] = once(byName()) - - def measure[A](warmUp: Int = 1, repetitions: Int = 1)(block: => A): Result[A] = - measureAll[A](warmUp, repetitions)(block).list.head - - def time[A](warmUp: Int = 1, repetitions: Int = 1)(block: => A): Long = - measure(warmUp, repetitions)(block).mediumNanoSecs - - private def round(float: Float, decimals: Int) = { - val fact = (10 pow decimals).toInt - (float * fact).floor / fact - } - - // relation of elapsed times b : a - def relativeTime[A](warmUp: Int = 1, repetitions: Int = 1, decimals: Int = 2, requireEqualResults: Boolean = true)( - a: => A, - b: => A - ): Float = - measureAll[A](warmUp, repetitions, requireEqualResults)(a, b).list match { - case mA :: mB :: Nil => mB.relativeTo(decimals)(mA) - case x => throw new MatchError(x) - } - - final class ByName[+A](x: => A) { def apply(): A = x } - implicit def toHolder[A](block: => A): ByName[A] = new ByName(block) - - def measureAll[A](warmUp: Int = 1, repetitions: Int = 1, requireEqualResults: Boolean = true)( - blocks: ByName[A]* - ): Results[A] = { - require(repetitions > 0, "'repetitions' must be positive") - for (i <- 1 to warmUp) blocks foreach once - - val results: Array[Result[A]] = (blocks map once map (r => new Result(r.result, r.nanoSecs))).toArray - val zippedBlocks = blocks.zipWithIndex - for { - i <- 1 until repetitions - zB <- zippedBlocks - } { - val (b, j) = zB - once(b) match { - case SingleResult(t, r) => - results(j) += t - if (requireEqualResults) requireEq(r, results(j).result) - } - } - Results(results.toList) - } - - def relativeTimes[A](warmUp: Int = 1, repetitions: Int = 1, decimals: Int = 2, requireEqualResults: Boolean = true)( - blocks: ByName[A]* - ): Seq[Float] = - measureAll[A](warmUp, repetitions, requireEqualResults)(blocks: _*).relativeTimes(decimals) -} diff --git a/coreTestScala3/src/test/scala/scalax/time/MicroBenchmarkTest.scala b/coreTestScala3/src/test/scala/scalax/time/MicroBenchmarkTest.scala deleted file mode 100644 index 6517c303..00000000 --- a/coreTestScala3/src/test/scala/scalax/time/MicroBenchmarkTest.scala +++ /dev/null @@ -1,73 +0,0 @@ -package scalax.time - -import scala.collection.immutable.ArraySeq -import scala.collection.mutable - -import org.scalactic.Equality -import org.scalatest.matchers.should.Matchers -import org.scalatest.refspec.RefSpec - -/* Intentionally excluded from SBT tests by means of a class name suffix different from "Spec". - */ -class MicroBenchmarkTest extends RefSpec with Matchers { - - import MicroBenchmark.* - - object `relativeTimes() reflects` { - def `relative execution times`: Unit = { - val r = 1 to 20 - val relTimes = relativeTimes(warmUp = 2)( - r.toList.sorted, - r.toList.sorted.toArray.toList.sorted, - r.toList.sorted.toSet.toArray.toList.sorted - ) - relTimes sliding 2 foreach { - case a :: b :: _ => a should be < b - case x => throw new MatchError(x) - } - } - } - - class FloatTolerance(maxDeviation: Float) extends Equality[Float] { - private def eq(a: Float, b: Float): Boolean = if (a > b) a < b * maxDeviation else a > b / maxDeviation - def areEqual(a: Float, b: Any) = b match { - case f: Float => eq(a, f) - case i: Int => eq(a, i.toFloat) - } - } - - object `relativeTime() roughly reflects` { - def `O(N) complexity of List.size`: Unit = { - def fill(size: Int): (Int, List[Int]) = (size, List.fill(size)(0)) - val (small, big) = (fill(100), fill(1000)) - - implicit val tolerance = new FloatTolerance(4f) - val expected = big._1.toFloat / small._1.toFloat - val results = measureAll(warmUp = 5, repetitions = 10)(small._2.size == small._1, big._2.size == big._1) - val actual = results.relativeTimes()(1) - - actual should ===(expected) - } - } - - def `traversing immutable.Set takes marginally longer than mutable.Set`: Unit = { - val size = 10000 - val seq = ArraySeq.tabulate(size)(identity) - val sum = seq.sum - val imm = Set(seq: _*) - val m = mutable.Set(seq: _*) - - relativeTime(repetitions = 6)(m.sum == sum, imm.sum == sum) should be > 1.05f - } - - /* assumption not correct - def `traversing mutable.Set takes longer than mutable.BitSet`: Unit = { - val size = 10000 - val seq = ArraySeq.tabulate(size)(_ % (size / 10)) - val s = Set(seq: _*) - val b = mutable.BitSet(seq: _*) - - relativeTime(warmUp = 20, repetitions = 6)(b.sum, s.sum) should be > 1.1f - } - */ -} From 58d68e4661d0db8c0bf535d7da88d85ea36c4882 Mon Sep 17 00:00:00 2001 From: Scala Steward <43047562+scala-steward@users.noreply.github.com> Date: Tue, 24 Jun 2025 17:15:40 +0200 Subject: [PATCH 31/34] Update sbt, scripted-plugin to 1.10.11 (#363) --- project/build.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/build.properties b/project/build.properties index e97b2722..cc68b53f 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.10.10 +sbt.version=1.10.11 From 8428a24fe9c1e0bb7f3feedb31268421df0c8d30 Mon Sep 17 00:00:00 2001 From: Scala Steward <43047562+scala-steward@users.noreply.github.com> Date: Tue, 24 Jun 2025 17:16:14 +0200 Subject: [PATCH 32/34] Update sbt-scalajs to 1.19.0 (#364) --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index 8c88d18b..6b5ff60c 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,6 +1,6 @@ addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.4") addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.14.2") -addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.18.2") +addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.19.0") addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.5.7") addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.3.2") addSbtPlugin("org.portable-scala" % "sbt-scala-native-crossproject" % "1.3.2") From 2affd0bc41cf589c8499c2afa9732ebd8a8b7498 Mon Sep 17 00:00:00 2001 From: Scala Steward <43047562+scala-steward@users.noreply.github.com> Date: Tue, 24 Jun 2025 17:35:40 +0200 Subject: [PATCH 33/34] Update sbt-scalafix to 0.14.3 (#367) Co-authored-by: Peter Empen --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index 6b5ff60c..5841460a 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,5 +1,5 @@ addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.4") -addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.14.2") +addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.14.3") addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.19.0") addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.5.7") addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.3.2") From 8fcdc34c8d55104f8672a725c8df893554c7b513 Mon Sep 17 00:00:00 2001 From: Scala Steward <43047562+scala-steward@users.noreply.github.com> Date: Tue, 24 Jun 2025 17:40:50 +0200 Subject: [PATCH 34/34] Update sbt-scala-native to 0.5.8 (#369) Co-authored-by: Peter Empen --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index 5841460a..a150d90d 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,7 +1,7 @@ addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.4") addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.14.3") addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.19.0") -addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.5.7") +addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.5.8") addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.3.2") addSbtPlugin("org.portable-scala" % "sbt-scala-native-crossproject" % "1.3.2") addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.3.1")