package util 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 import com.typesafe.scalalogging.Logger import com.typesafe.scalalogging.LazyLogging object Util extends LazyLogging { private val r = 6471.00 // km def toRad(num: Double): Double = { val a = num * Pi / 180 a } def getHaversineDistance(c1: Coord, c2: Coord): Double = { val x1 = c1.latitude - c2.latitude val x2 = c1.longitude - c2.longitude val dlat = toRad(x1) val dlon = toRad(x2) val a = pow(sin(dlat / 2), 2) + cos(toRad(c1.latitude)) * cos(toRad((c2.latitude))) * pow(sin(dlon / 2), 2) val c = 2 * atan2(sqrt(a), sqrt(1 - a)) val d = r * c d } def getHaversineDistance(lat: Double, lon: Double): Double = { val x1 = lat val x2 = lon val dlat = toRad(x1) val dlon = toRad(x2) val a = pow(sin(dlat / 2), 2) + cos(toRad(x1)) * cos(toRad((0))) * pow(sin(dlon / 2), 2) val c = 2 * atan2(sqrt(a), sqrt(1 - a)) val d = r * c d } def primTraverse( arr1: Array[Array[Int]], comp: Int, cond: (Int, Int) => Boolean, cb: (Int, Int, Array[Array[Int]], Array[Array[Int]]) => Array[Array[Int]] ): Unit = { val n = arr1.length val selected = Array.ofDim[Boolean](n) val arr2: Array[Array[Int]] = Array.ofDim(n, n) selected(0) = true // val mst: Array[Array[Int]] = Array.ofDim(5, 5) for (_ <- 0 until n - 1) { // var min = 999999 var x = 0 var y = 0 for (i <- 0 until n) { if (selected(i) == true) { for (j <- 0 until n) { if (selected(j) == false && arr1(i)(j) != 0) { if (cond(comp, arr1(i)(j))) { x = i y = j cb(x, y, arr1, arr2) } } } } } // mst(x)(y) = mst(x)(y) selected(y) = true } } 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[T]] = Array.ofDim[T](n, n) for (_ <- 0 until n - 1) { var min = 999999 var x = 0 var y = 0 for (i <- 0 until n) { if (selected(i) == true) { for (j <- 0 until n) { if (selected(j) == false && edges(i)(j) != 0) { if (num.gt(num.fromInt(min), edges(i)(j))) { min = num.toInt(edges(i)(j)) x = i y = j } } } } } // println(s"Edge selected $x - $y : ${edges(x)(y)}") mst(x)(y) = edges(x)(y) mst(y)(x) = edges(x)(y) selected(y) = true } mst } 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]], centroids: IndexedSeq[Int] ): ArrayBuffer[ArrayBuffer[Int]] = { val n = mst.length // val selected: ArrayBuffer[Boolean] = ArrayBuffer.fill(n)(false) val buf: ArrayBuffer[ArrayBuffer[Int]] = ArrayBuffer.fill(n)(ArrayBuffer.empty) // for (_ <- 0 until n -1) { // } for (i <- 0 until n) { for (j <- 0 until n) { if (mst(i)(j) != 0) { // println(s" $i $j = ${mst(i)(j)}") buf(i) += j } } } buf } def findCentroids[T]( mst: Array[Array[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 for (i <- 0 until n) { for (j <- 0 until n) { if (ev.gt(mst(i)(j), ev.fromInt(20)) && mst(i)(j) != 0) { // println(s" $i $j = ${mst(i)(j)}") centroids += i centroids += j removed.append((i, j, mst(i)(j))) mst(i)(j) = ev.zero } } } (mst, centroids.toIndexedSeq, removed.toIndexedSeq) } def DFS[T]( start: 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[T]], visited: Array[Boolean] ): Unit = { visited(start) = true buf += start // print(s"$start ") for (i <- 0 until graph.size) { if (num.gt(graph(start)(i), num.fromInt(0)) && (!visited(i))) { loop(i, graph, visited); } } } loop(start, graph, visited) // println() buf } def DFS( start: Int, graph: Array[Array[Int]], num: Int, cond: (Int, Int) => Boolean ): ArrayBuffer[Int] = { val visited = Array.fill(graph.size)(false) val buf = ArrayBuffer[Int]() def loop( start: Int, graph: Array[Array[Int]], visited: Array[Boolean] ): Unit = { visited(start) = true buf += start // print(s"$start ") for (i <- 0 until graph.size) { if (graph(start)(i) > 0 && cond(graph(start)(i), num) && (!visited(i))) { loop(i, graph, visited); } } } loop(start, graph, visited) // println() buf } def groupClusters[T]( centroids: IndexedSeq[Int], clusters: Map[Int, ArrayBuffer[Int]], removed: IndexedSeq[(Int, Int, T)] )(implicit num: Numeric[T]) = centroids .map(c => { val cluster = clusters(c) val lst = removed .filter(r => { c == r._1 }) .sortWith((x, y) => { num.lt(x._3, y._3) }) .map(l => { (l._2, l._3) }) c -> lst }) .toMap 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 } }