From 69a0c3ce63337fb6ff448da9456d70d8f88a59c0 Mon Sep 17 00:00:00 2001 From: Rohan Sircar Date: Sat, 11 Apr 2020 17:24:16 +0530 Subject: [PATCH] Command lines args and railway pattern --- Input.txt | 20 ++++ build.sbt | 10 +- src/main/scala/HHCSim.scala | 31 ++++++ src/main/scala/Main.scala | 71 +++++++++---- src/main/scala/config/Config.scala | 11 ++ src/main/scala/util/Util.scala | 155 +++++++++++++++++++---------- 6 files changed, 220 insertions(+), 78 deletions(-) create mode 100644 Input.txt create mode 100644 src/main/scala/HHCSim.scala create mode 100644 src/main/scala/config/Config.scala diff --git a/Input.txt b/Input.txt new file mode 100644 index 0000000..fdafaf5 --- /dev/null +++ b/Input.txt @@ -0,0 +1,20 @@ +9.46, 0.90, 82.88 +1.79, 7.90, 21.65 +5.90, 2.98, 2.66 +7.70, 9.18, 88.91 +2.07, 9.57, 48.59 +0.61, 3.38, 33.63 +3.80, 1.31, 7.02 +6.50, 1.13, 42.29 +8.53, 0.56, 32.25 +3.20, 8.90, 5.74 +7.13, 8.38, 99.00 +2.40, 3.40, 34.93 +0.34, 6.58, 55.81 +4.26, 0.94, 72.11 +9.42, 7.50, 23.83 +7.12, 4.67, 55.61 +9.13, 9.82, 62.38 +1.60, 3.12, 67.76 +7.39, 7.44, 87.71 +3.41, 9.76, 11.80 diff --git a/build.sbt b/build.sbt index e6948e5..0c19773 100644 --- a/build.sbt +++ b/build.sbt @@ -1,19 +1,15 @@ name := "scala-hhc" -version := "1.0" +version := "0.0.1" scalaVersion := "2.13.1" - resolvers ++= Seq( Resolver.sonatypeRepo("releases"), Resolver.sonatypeRepo("snapshots") ) - -// https://mvnrepository.com/artifact/org.projectlombok/lombok -libraryDependencies += "org.projectlombok" % "lombok" % "1.18.10" % "provided" libraryDependencies ++= Seq( "org.typelevel" %% "cats-core" % "2.0.0", "com.softwaremill.quicklens" %% "quicklens" % "1.4.12", "com.softwaremill.macwire" %% "macros" % "2.3.3", - "me.shadaj" %% "scalapy-numpy" % "0.1.0+5-ad550211" -) \ No newline at end of file + "org.rogach" %% "scallop" % "3.4.0" +) diff --git a/src/main/scala/HHCSim.scala b/src/main/scala/HHCSim.scala new file mode 100644 index 0000000..17911c1 --- /dev/null +++ b/src/main/scala/HHCSim.scala @@ -0,0 +1,31 @@ +import model.Customer +import util._ +class HHCSim( + private val epsilonMax: Int, + private val iterations: Int, + // private val customers: IndexedSeq[Customer], + private val WLDMax: Float +) { + // private val graph: Array[Array[T]] + def go( + customers: Either[String, IndexedSeq[Customer]] + ): String Either Map[Int, IndexedSeq[(Int, Double)]] = + customers + .map(Util.formAdjMatrix) + .map(edges => Util.mstUsingPrims(edges)) + .map(mst => Util.findCentroids(mst)) + .map( + e => + e match { + case (mst, centroids, removed) => + Util.findClusters(mst, centroids, removed) + } + ) + .map( + e => + e match { + case (centroids, clusters, removed) => + Util.groupClusters(centroids, clusters, removed) + } + ) +} diff --git a/src/main/scala/Main.scala b/src/main/scala/Main.scala index 42e9ab9..e24e002 100644 --- a/src/main/scala/Main.scala +++ b/src/main/scala/Main.scala @@ -1,11 +1,40 @@ import model.Coord import model.Customer import model.HHCEdge -import scala.collection.mutable._ +import scala.collection.mutable.ArrayBuffer import util.Util +import config.Conf +import java.io.File object Main { def main(args: Array[String]): Unit = { + val conf = new Conf(args.toIndexedSeq) + + val bufferedSource = io.Source.fromFile(conf.infile()) + + val customers = Util.getCustomers(bufferedSource) + bufferedSource.close() + // customers.map(println(_)) + + // val edges3 = customers.map(Util.formAdjMatrix) + + // val fnl2 = go(customers) + + // fnl2 match { + // case Left(value) => println(value) + // case Right(value) => println(value) + // } + + // edges3 match { + // case Right(e) => + // e.foreach { d => + // d.foreach(g => print(f"$g%.2f, ")) + // println() + // } + // case Left(e) => + // println(s"Oops, an error occured. The error message was: $e") + // } + val coord1 = Coord(3, 4) println(s"Distance from origin = ${coord1.distance}") @@ -128,14 +157,14 @@ object Main { // 0, 0, 51, 0 , 31 // 0, 0, 0 , 0 , 0 - val (centr, removed) = Util.findCentroids(mst) + val (mst2, centr, removed) = Util.findCentroids(mst) // val (centr2, eds2) = Util.findCentroids(edges2) println() println(s"Centroids: \n$centr") println(s"Removed: \n$removed") - val clust = Util.findClusters(mst, centr) + val (_, clust, _) = Util.findClusters(mst, centr, removed) val adjList = Util.makeAdjacencyList(edges, centr) println(s"Clusters:") @@ -156,28 +185,28 @@ object Main { // Output // // Initial graph: - // 0, 9, 75, 0, 0, - // 9, 0, 95, 19, 42, - // 75, 95, 0, 51, 66, - // 0, 19, 51, 0, 31, - // 0, 42, 66, 31, 0, - // MST: - // 0, 9, 0, 0, 0, - // 9, 0, 0, 19, 0, - // 0, 0, 0, 51, 0, - // 0, 19, 51, 0, 31, - // 0, 0, 0, 31, 0, - - // Centroids: + // 0, 9, 75, 0, 0, + // 9, 0, 95, 19, 42, + // 75, 95, 0, 51, 66, + // 0, 19, 51, 0, 31, + // 0, 42, 66, 31, 0, + // MST: + // 0, 9, 0, 0, 0, + // 9, 0, 0, 19, 0, + // 0, 0, 0, 51, 0, + // 0, 19, 51, 0, 31, + // 0, 0, 0, 31, 0, + + // Centroids: // Vector(2, 3, 4) - // Removed: + // Removed: // Vector((2,3,51), (3,2,51), (3,4,31), (4,3,31)) // Clusters: - // 2: -> 2 - // 3: -> 3 -> 1 -> 0 - // 4: -> 4 + // 2: -> 2 + // 3: -> 3 -> 1 -> 0 + // 4: -> 4 - // Final cluster groups: + // Final cluster groups: // Map(2 -> Vector((3,51)), 3 -> Vector((4,31), (2,51)), 4 -> Vector((3,31))) } } diff --git a/src/main/scala/config/Config.scala b/src/main/scala/config/Config.scala new file mode 100644 index 0000000..0846831 --- /dev/null +++ b/src/main/scala/config/Config.scala @@ -0,0 +1,11 @@ +package config + +import org.rogach.scallop._ + +class Conf(arguments: Seq[String]) extends ScallopConf(arguments) { + val infile = + opt[String](default = Some("Input.txt"), descr = "Input file") + val outfile = + opt[String](default = Some("Output.txt"), descr = "Output file") + verify() +} diff --git a/src/main/scala/util/Util.scala b/src/main/scala/util/Util.scala index 99ebb6b..606c3a2 100644 --- a/src/main/scala/util/Util.scala +++ b/src/main/scala/util/Util.scala @@ -4,6 +4,9 @@ import model.Coord import scala.math._ import scala.collection.mutable.ArrayBuffer import scala.collection.mutable +import model.Customer +import scala.io.BufferedSource +import scala.reflect.ClassTag object Util { private val r = 6471.00 // km @@ -85,16 +88,16 @@ object Util { selected(y) = true } } - def mstUsingPrims( - edges: Array[Array[Int]] - ): Array[Array[Int]] = { + + def mstUsingPrims[T: ClassTag]( + edges: Array[Array[T]] + )(implicit num: Numeric[T]): Array[Array[T]] = { val n = edges.length val selected: ArrayBuffer[Boolean] = ArrayBuffer.fill(n)(false) selected(0) = true - val mst: Array[Array[Int]] = Array.ofDim(n, n) - + val mst: Array[Array[T]] = Array.ofDim[T](n, n) for (_ <- 0 until n - 1) { var min = 999999 @@ -104,8 +107,8 @@ object Util { if (selected(i) == true) { for (j <- 0 until n) { if (selected(j) == false && edges(i)(j) != 0) { - if (min > edges(i)(j)) { - min = edges(i)(j) + if (num.gt(num.fromInt(min), edges(i)(j))) { + min = num.toInt(edges(i)(j)) x = i y = j } @@ -122,19 +125,21 @@ object Util { mst } - def findClusters( - mst: Array[Array[Int]], - centroids: IndexedSeq[Int] - ): Map[Int, ArrayBuffer[Int]] = { - val n = mst.length - val x: Map[Int, ArrayBuffer[Int]] = centroids - .map(d => { - val y = DFS(d, mst) - y(0) -> y - }) - .toMap - x - } + def findClusters[T]( + mst: Array[Array[T]], + centroids: IndexedSeq[Int], + removed: IndexedSeq[(Int, Int, T)] + )(implicit num: Numeric[T]) = + ( + centroids, + centroids + .map(d => { + val y = DFS(d, mst) + y(0) -> y + }) + .toMap, + removed + ) def makeAdjacencyList( mst: Array[Array[Int]], @@ -160,7 +165,9 @@ object Util { def findCentroids[T]( mst: Array[Array[T]] - )(implicit ev: Numeric[T]): (IndexedSeq[Int], IndexedSeq[(Int, Int, T)]) = { + )( + implicit ev: Numeric[T] + ): (Array[Array[T]], IndexedSeq[Int], IndexedSeq[(Int, Int, T)]) = { val n = mst.length val centroids: mutable.Set[Int] = mutable.Set.empty val removed: ArrayBuffer[(Int, Int, T)] = ArrayBuffer.empty @@ -175,37 +182,26 @@ object Util { } } } - (centroids.toIndexedSeq, removed.toIndexedSeq) + (mst, centroids.toIndexedSeq, removed.toIndexedSeq) } - // def DFS(start: Int, graph: Array[Array[Int]], visited: Array[Boolean]): Unit = { - // // if(start == 0) { - // // visited = Array.fill(graph.size)(false) - // // } - // visited(start) = true - - // println(s"$start ") - - // for(i <- 0 until graph.size) { - // if (graph(start)(i) > 0 && graph(start)(i) < 20 && (!visited(i))) { - // DFS(i, graph, visited); - // } - // } - // } - // val visited = Array.fill(mst.size)(false) - def DFS( + def DFS[T]( start: Int, - graph: Array[Array[Int]] - ): ArrayBuffer[Int] = { + graph: Array[Array[T]] + )(implicit num: Numeric[T]): ArrayBuffer[Int] = { val visited = Array.fill(graph.size)(false) val buf = ArrayBuffer[Int]() - def loop(start: Int, graph: Array[Array[Int]], visited: Array[Boolean]) { + def loop( + start: Int, + graph: Array[Array[T]], + visited: Array[Boolean] + ): Unit = { visited(start) = true buf += start // print(s"$start ") for (i <- 0 until graph.size) { - if (graph(start)(i) > 0 && (!visited(i))) { + if (num.gt(graph(start)(i), num.fromInt(0)) && (!visited(i))) { loop(i, graph, visited); } } @@ -224,7 +220,11 @@ object Util { ): ArrayBuffer[Int] = { val visited = Array.fill(graph.size)(false) val buf = ArrayBuffer[Int]() - def loop(start: Int, graph: Array[Array[Int]], visited: Array[Boolean]) { + def loop( + start: Int, + graph: Array[Array[Int]], + visited: Array[Boolean] + ): Unit = { visited(start) = true buf += start // print(s"$start ") @@ -241,12 +241,12 @@ object Util { buf } - def groupClusters( + def groupClusters[T]( centroids: IndexedSeq[Int], clusters: Map[Int, ArrayBuffer[Int]], - removed: IndexedSeq[(Int, Int, Int)] - ) = { - val groups = centroids + removed: IndexedSeq[(Int, Int, T)] + )(implicit num: Numeric[T]) = + centroids .map(c => { val cluster = clusters(c) val lst = removed @@ -254,15 +254,70 @@ object Util { c == r._1 }) .sortWith((x, y) => { - x._3 < y._3 + num.lt(x._3, y._3) }) .map(l => { - (l._2,l._3) + (l._2, l._3) }) c -> lst }) .toMap - groups + + def getCustomers( + infile: BufferedSource + ): String Either IndexedSeq[Customer] = { + var customers: String Either IndexedSeq[Customer] = Right(IndexedSeq.empty) + val it = infile.getLines + @annotation.tailrec + def loop( + lst: IndexedSeq[Customer], + iter: Iterator[String] + ): String Either IndexedSeq[Customer] = { + if (!iter.hasNext) + return Right(lst) + val line = iter.next + val arr = line.split(",").map(_.trim) + arr match { + case Array(latitude, longitude, workLoad) => { + val cust = + Customer( + Coord(latitude.toDouble, longitude.toDouble), + workLoad.toFloat + ) + loop(lst :+ cust, iter) + } + case _ => + Left( + "Error reading customers from" + + s" file - ${arr.mkString(", ")}" + ) + } + } + try { + customers = loop(IndexedSeq.empty, it) + } catch { + case e: NumberFormatException => + customers = Left( + s"Expected number but received string ${e.getMessage()}" + ) + case e: NullPointerException => + customers = Left("Input was null") + } + customers + } + + def formAdjMatrix(customers: IndexedSeq[Customer]): Array[Array[Double]] = { + val n = customers.length + val edges: Array[Array[Double]] = Array.ofDim(n, n) + for (i <- 0 until n) { + for (j <- i until n) { + val weight = + getHaversineDistance(customers(i).location, customers(j).location) + edges(i)(j) = weight + edges(j)(i) = weight + } + } + edges } }