import java.io.File

import scala.collection.mutable.HashMap

object Freq {
  
  def fileContents(file: String): List[String] = {
    val f = new java.io.File(file)
    try {
      scala.io.Source.fromFile(f).getLines().toList
    } catch {
      case e: java.io.FileNotFoundException => {
        println("WTF! No such file exists")
        List()
      }
    }
  }

  def fileChars(file: String): List[Char] = {
    for (line <- fileContents(file); char <- line) yield char
  }

  def freq[A](xs: Iterable[A]): HashMap[A, Int] = {
    val freqMap = new HashMap[A, Int]
    for (x <- xs) {
      freqMap(x) = 1 + freqMap.getOrElse(x, 0)
    }
    freqMap
  }

  def apply[A](xs: Iterable[A]) = freq(xs)
  
  def show[A](m: HashMap[A, Int], size: Int) = {
    for ((k, v) <- m) { println(k + " : " + "#" * (v / size)) }
  }

  def main(args: Array[String]) { 
    val str   = fileChars(args(0))
    val freqs = freq(str)
    show(freqs, 1)
  }

}




object FileString { 
  
  def apply(file: File): String = 
    scala.io.Source.fromFile(file).toList.mkString("")

  def apply(fileName: String): String = 
    apply(new File(fileName))

}

object Queries {

  case class Album (title: String, artist: String)
  case class Movie (title: String, actors: List[String])
  case class Novel (title: String, author: String)


  val novels = List( Novel ("Skinny Legs and All", "Tom Robbins")
                   , Novel ("Breakfast of Champions", "Kurt Vonnegut")
                   , Novel ("An Object of Beauty", "Steve Martin")
                   , Novel ("Sophie's Choice", "William Styron")
                   , Novel ("The Other", "Tom Tryon")
                   )

  val albums = List( Album ("Rare Bird Alert", "Steve Martin")
                   , Album ("Paper Airplane", "Alison Krauss and Union Station")
                   , Album ("Earl Scruggs and Friends", "Earl Scruggs")
                   )

  val movies = List( Movie ("Airplane", List ("Leslie Nielsen", "Robert Stack"))
                   , Movie ("Young Frankenstein", List ("Gene Wilder", "Madeline Kahn"))
                   , Movie ("The Pink Panther", List ("Steve Martin", "Jean Reno"))
                   )
                            
  val tripleThreats = for (n <- novels;
                           a <- albums;
                           m <- movies;
                           if (n.author == a.artist && m.actors.contains (n.author)))
                      yield (n.author + " starred in " + m.title + ","
                                      + " wrote " + n.title + ","
                                      + " and performed " + a.title)
}

object Queens {
  
  type XY = (Int, Int)

  def eqRow(q1: XY, q2: XY) = q1._1 == q2._1
  def eqCol(q1: XY, q2: XY) = q1._2 == q2._2
  def eqDia(q1: XY, q2: XY) = (q1._1 - q2._1).abs == (q1._2 - q2._2).abs

  def canKill(q1: XY, q2: XY) = 
    eqRow(q1, q2) || eqCol(q1, q2) || eqDia(q1, q2)

  def isSafe(queen: (Int, Int), others: List[(Int, Int)]) = 
    others forall (!canKill(_, queen))

  def placeQueens(columns: Int, row: Int) : Iterator[List[(Int, Int)]] = { 
    if (row == 0) 
      Iterator(List())
    else 
      for { queens <- placeQueens(columns, row - 1)
            column <- 1 to columns 
            queen   = (row, column)
            if isSafe(queen, queens)
      } yield (queen :: queens)
  }

  def apply(n: Int) =
    placeQueens(n, n)

  /*********************************************************************/
  /******************** Stuff for Printing Solutions *******************/
  /*********************************************************************/
  
  def renderRow(cols: Int, col: Int) = 
    ("O" * (col - 1)) + "X" + ("O" * (cols - col))

  def render(qs: List[XY]): String = {
    val cols = qs.length
    qs.sortWith(_._1 > _._1).map(x => renderRow(cols, x._2)).mkString("\n")
  }

  def apply(n: Int, numSols: Int) { 
    for (sol <- placeQueens(n, n) take numSols) {
      println("********  Solution **********") 
      println(render(sol))
      println("*****************************") 
    }
  }

}

object Sudoku {
  
  type XY    = (Int, Int)
  type Board = Map[XY, Int]

  /****************************************************************/
  /************ Reading in a Board  *******************************/
  /****************************************************************/
 
  def readVal(v: String): Option[Int] = 
    try Some(v.trim.toInt) catch { case _ => None }

  def read(str: String) : Board = {
    var board : Board = Map()
    val lines = str.lines.toList
    for ((y, ly)  <- (0 until 9) zip lines;
         vals      = ly.split(",").map(readVal(_)); 
         (x, vo)  <- (0 until 9) zip vals) 
    { vo.foreach(v => { board += (x, y) -> v }) }
    board
  }

  /****************************************************************/
  /************ Writing out a Board *******************************/
  /****************************************************************/

  def xyString(b: Board, x: Int, y: Int): String = 
    b.get(x, y) match {
      case Some(v) => v.toString
      case None    => " "
    }

  def yString(b: Board, y: Int): String = 
    (0 until 9).map(xyString(b ,_ : Int, y)).mkString(", ")

  def bString(b: Board): String =
    (0 until 9).map(yString(b, _)).mkString("\n")

  /****************************************************************/
  /************ Points related to a given XY **********************/ 
  /****************************************************************/
 
  def pointsOfGrid(x: Int, y: Int) = {
    val (x0, y0) = ((x/3) * 3,  (y/3) * 3) 
    for (px <- x0 until x0 + 3 ;
         py <- y0 until y0 + 3 )
    yield (px, py)
  }

  def pointsOfCol(x: Int) =
    for (y <- 0 until 9) yield (x, y)

  def pointsOfRow(y: Int) = 
    for (x <- 0 until 9) yield (x, y)

  /****************************************************************/
  /************ Candidate Values for a given XY *******************/
  /****************************************************************/
 
  def alreadyTaken(x: Int, y: Int, b: Board) =
    for (p <- pointsOfCol(x) ++ 
              pointsOfRow(y) ++ 
              pointsOfGrid(x, y)
         if b.isDefinedAt(p))
      yield b(p)

  def candidates (b: Board) =
    for { x <- 0 until 9 ;
          y <- 0 until 9 ;
          if (!b.isDefinedAt((x,y))) ;
          xy   = (x, y) ;
          vals = (0 until 9) diff (alreadyTaken(x,y,b)) }
    yield (xy, vals)
  
  /****************************************************************/
  /************ Recursive Search for a Solution *******************/
  /****************************************************************/

  case class SolvedException(val b: Board) extends Exception

  // Raises SolvedException(b) if solution exists
  // Returns ()                otherwise 
  def solve1 (b: Board) {
    val blanks = candidates(b)
    if (blanks.isEmpty) { 
      throw new SolvedException(b)
    }
    val (xy, vals) = blanks minBy (_._2.length)
    for (v <- vals) { 
      solve1(b + (xy -> v))
    }
  }

  def solve(b: Board) : Iterator[Board] = {
    val blanks = candidates(b)
    if (blanks.isEmpty) Iterator(b)  
    else { 
      val (xy, choices) = blanks minBy (_._2.length)
      for (choice <- choices.toIterator; 
           bFull  <- solve(b + (xy -> choice)))
      yield bFull
    }
  }
  /****************************************************************/
  /************ Running the Solver ********************************/ 
  /****************************************************************/

  def apply(s: String, n: Int) {
    val b0 = read(s)
    println("\n********** Trying to solve **************\n")
    print(bString(b0))

    for (bf <- solve(b0) take n){
      println("\n\n********** Hooray! Solution Found :) **********\n")
      print(bString(bf))
    }
    println("\n\n********** No more solutions! ***************\n")
  }

  def apply(s: String) { 
    try { 
      val b0 = read(s) 
      println("\n********** Trying to solve **************\n")
      print(bString(b0))
      solve1(b0)
      println("\n*** Oh No! There is no solution :( ******\n")
    } catch {
      case SolvedException(b) => {
        println("\n\n********** Hooray! Solution Found :) **********\n")
        print(bString(b)) 
        println("\n***********************************************\n")
      }
      case e => println("Unexpected Exception: " + e.toString)
    }
  }

  def apply(f: File, n: Int) { Sudoku(FileString(f), n) }
  def apply(f: File)         { Sudoku(FileString(f))    }

  //$ scala Sudoku sudoku1.txt
  //scala> Sudoku(new File("sudoku1.txt"))
  def main(args: Array[String]) { Sudoku(FileString(args(0))) }

  /****************************************************************/
  /************ Some Test Puzzles *********************************/ 
  /****************************************************************/


  val puzzle1 = List ("5, 3,  ,  , 7,  ,  ,  ,  "
                    , "6,  ,  , 1, 0, 5,  ,  ,  "
                    , " , 0, 8,  ,  ,  ,  , 6,  "
                    , "8,  ,  ,  , 6,  ,  ,  , 3"
                    , "4,  ,  , 8,  , 3,  ,  , 1"
                    , "7,  ,  ,  , 2,  ,  ,  , 6"
                    , " , 6,  ,  ,  ,  , 2, 8,  "
                    , " ,  ,  , 4, 1, 0,  ,  , 5"
                    , " ,  ,  ,  , 8,  ,  , 7, 0")
                .mkString("\n")


  val puzzle = Map ((0, 0) -> 5, (1, 0) -> 3, (4, 0) -> 7,
                    (0, 1) -> 6, (3, 1) -> 1, (4, 1) -> 0,
                    (5, 1) -> 5, (1, 2) -> 0, (2, 2) -> 8,
                    (7, 2) -> 6, (0, 3) -> 8, (4, 3) -> 6,
                    (8, 3) -> 3, (0, 4) -> 4, (3, 4) -> 8,
                    (5, 4) -> 3, (8, 4) -> 1, (0, 5) -> 7,
                    (4, 5) -> 2, (8, 5) -> 6, (1, 6) -> 6,
                    (6, 6) -> 2, (7, 6) -> 8, (3, 7) -> 4,
                    (4, 7) -> 1, (5, 7) -> 0, (8, 7) -> 5,
                    (4, 8) -> 8, (7, 8) -> 7, (8, 8) -> 0)
  
}
