Command lines args and railway pattern

This commit is contained in:
Rohan Sircar 2020-04-11 17:24:16 +05:30
parent a6e0377816
commit 69a0c3ce63
6 changed files with 219 additions and 77 deletions

20
Input.txt Normal file
View File

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

View File

@ -1,19 +1,15 @@
name := "scala-hhc" name := "scala-hhc"
version := "1.0" version := "0.0.1"
scalaVersion := "2.13.1" scalaVersion := "2.13.1"
resolvers ++= Seq( resolvers ++= Seq(
Resolver.sonatypeRepo("releases"), Resolver.sonatypeRepo("releases"),
Resolver.sonatypeRepo("snapshots") Resolver.sonatypeRepo("snapshots")
) )
// https://mvnrepository.com/artifact/org.projectlombok/lombok
libraryDependencies += "org.projectlombok" % "lombok" % "1.18.10" % "provided"
libraryDependencies ++= Seq( libraryDependencies ++= Seq(
"org.typelevel" %% "cats-core" % "2.0.0", "org.typelevel" %% "cats-core" % "2.0.0",
"com.softwaremill.quicklens" %% "quicklens" % "1.4.12", "com.softwaremill.quicklens" %% "quicklens" % "1.4.12",
"com.softwaremill.macwire" %% "macros" % "2.3.3", "com.softwaremill.macwire" %% "macros" % "2.3.3",
"me.shadaj" %% "scalapy-numpy" % "0.1.0+5-ad550211" "org.rogach" %% "scallop" % "3.4.0"
) )

View File

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

View File

@ -1,11 +1,40 @@
import model.Coord import model.Coord
import model.Customer import model.Customer
import model.HHCEdge import model.HHCEdge
import scala.collection.mutable._ import scala.collection.mutable.ArrayBuffer
import util.Util import util.Util
import config.Conf
import java.io.File
object Main { object Main {
def main(args: Array[String]): Unit = { 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) val coord1 = Coord(3, 4)
println(s"Distance from origin = ${coord1.distance}") println(s"Distance from origin = ${coord1.distance}")
@ -128,14 +157,14 @@ object Main {
// 0, 0, 51, 0 , 31 // 0, 0, 51, 0 , 31
// 0, 0, 0 , 0 , 0 // 0, 0, 0 , 0 , 0
val (centr, removed) = Util.findCentroids(mst) val (mst2, centr, removed) = Util.findCentroids(mst)
// val (centr2, eds2) = Util.findCentroids(edges2) // val (centr2, eds2) = Util.findCentroids(edges2)
println() println()
println(s"Centroids: \n$centr") println(s"Centroids: \n$centr")
println(s"Removed: \n$removed") println(s"Removed: \n$removed")
val clust = Util.findClusters(mst, centr) val (_, clust, _) = Util.findClusters(mst, centr, removed)
val adjList = Util.makeAdjacencyList(edges, centr) val adjList = Util.makeAdjacencyList(edges, centr)
println(s"Clusters:") println(s"Clusters:")

View File

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

View File

@ -4,6 +4,9 @@ import model.Coord
import scala.math._ import scala.math._
import scala.collection.mutable.ArrayBuffer import scala.collection.mutable.ArrayBuffer
import scala.collection.mutable import scala.collection.mutable
import model.Customer
import scala.io.BufferedSource
import scala.reflect.ClassTag
object Util { object Util {
private val r = 6471.00 // km private val r = 6471.00 // km
@ -85,16 +88,16 @@ object Util {
selected(y) = true selected(y) = true
} }
} }
def mstUsingPrims(
edges: Array[Array[Int]] def mstUsingPrims[T: ClassTag](
): Array[Array[Int]] = { edges: Array[Array[T]]
)(implicit num: Numeric[T]): Array[Array[T]] = {
val n = edges.length val n = edges.length
val selected: ArrayBuffer[Boolean] = ArrayBuffer.fill(n)(false) val selected: ArrayBuffer[Boolean] = ArrayBuffer.fill(n)(false)
selected(0) = true 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) { for (_ <- 0 until n - 1) {
var min = 999999 var min = 999999
@ -104,8 +107,8 @@ object Util {
if (selected(i) == true) { if (selected(i) == true) {
for (j <- 0 until n) { for (j <- 0 until n) {
if (selected(j) == false && edges(i)(j) != 0) { if (selected(j) == false && edges(i)(j) != 0) {
if (min > edges(i)(j)) { if (num.gt(num.fromInt(min), edges(i)(j))) {
min = edges(i)(j) min = num.toInt(edges(i)(j))
x = i x = i
y = j y = j
} }
@ -122,19 +125,21 @@ object Util {
mst mst
} }
def findClusters( def findClusters[T](
mst: Array[Array[Int]], mst: Array[Array[T]],
centroids: IndexedSeq[Int] centroids: IndexedSeq[Int],
): Map[Int, ArrayBuffer[Int]] = { removed: IndexedSeq[(Int, Int, T)]
val n = mst.length )(implicit num: Numeric[T]) =
val x: Map[Int, ArrayBuffer[Int]] = centroids (
centroids,
centroids
.map(d => { .map(d => {
val y = DFS(d, mst) val y = DFS(d, mst)
y(0) -> y y(0) -> y
}) })
.toMap .toMap,
x removed
} )
def makeAdjacencyList( def makeAdjacencyList(
mst: Array[Array[Int]], mst: Array[Array[Int]],
@ -160,7 +165,9 @@ object Util {
def findCentroids[T]( def findCentroids[T](
mst: Array[Array[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 n = mst.length
val centroids: mutable.Set[Int] = mutable.Set.empty val centroids: mutable.Set[Int] = mutable.Set.empty
val removed: ArrayBuffer[(Int, Int, T)] = ArrayBuffer.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 = { def DFS[T](
// // 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(
start: Int, start: Int,
graph: Array[Array[Int]] graph: Array[Array[T]]
): ArrayBuffer[Int] = { )(implicit num: Numeric[T]): ArrayBuffer[Int] = {
val visited = Array.fill(graph.size)(false) val visited = Array.fill(graph.size)(false)
val buf = ArrayBuffer[Int]() 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 visited(start) = true
buf += start buf += start
// print(s"$start ") // print(s"$start ")
for (i <- 0 until graph.size) { 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); loop(i, graph, visited);
} }
} }
@ -224,7 +220,11 @@ object Util {
): ArrayBuffer[Int] = { ): ArrayBuffer[Int] = {
val visited = Array.fill(graph.size)(false) val visited = Array.fill(graph.size)(false)
val buf = ArrayBuffer[Int]() 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 visited(start) = true
buf += start buf += start
// print(s"$start ") // print(s"$start ")
@ -241,12 +241,12 @@ object Util {
buf buf
} }
def groupClusters( def groupClusters[T](
centroids: IndexedSeq[Int], centroids: IndexedSeq[Int],
clusters: Map[Int, ArrayBuffer[Int]], clusters: Map[Int, ArrayBuffer[Int]],
removed: IndexedSeq[(Int, Int, Int)] removed: IndexedSeq[(Int, Int, T)]
) = { )(implicit num: Numeric[T]) =
val groups = centroids centroids
.map(c => { .map(c => {
val cluster = clusters(c) val cluster = clusters(c)
val lst = removed val lst = removed
@ -254,15 +254,70 @@ object Util {
c == r._1 c == r._1
}) })
.sortWith((x, y) => { .sortWith((x, y) => {
x._3 < y._3 num.lt(x._3, y._3)
}) })
.map(l => { .map(l => {
(l._2,l._3) (l._2, l._3)
}) })
c -> lst c -> lst
}) })
.toMap .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
} }
} }