// 1 + 2 * 3
 
trait Expr {
  def eval(): Int
}
 
case class Atom(x: Int) extends Expr {
  override def eval(): Int = x
}
case class Add(e1: Expr, e2: Expr) extends Expr {
  override def eval(): Int = e1.eval() + e2.eval()
}
case class Mult(e1: Expr, e2: Expr) extends Expr{
  override def eval(): Int = e1.eval() * e2.eval()
}
 
Add(Atom(1),Mult(Atom(2),Atom(3))).eval()
 
 
/*
   - calculeaza valoarea unei expresii
   - group:
     e1 * e2 + e1 * e3 = e1 * (e2 + e3)
 */
def group(e: Expr): Expr =
  e match{
    case Add(Mult(e1,e2),Mult(e3,e4)) =>
      if (e1 == e3) Mult(e1,Add(e2,e4))
      else ???
  }
 
/*
    Liste in Scala
 */
val l1 = List(1,2,3,4)
val l2 = 1 :: 2 :: 3 :: 4 :: Nil
 
l1.head
l2.tail
 
def sum(l: List[Int]):Int =
  l match {
    case Nil => 0
    case x::xs => x + sum(xs)
  }
 
def prod(l: List[Int]):Int =
  l match {
    case Nil => 1
    case x::xs => x * prod(xs)
  }
// valoarea initiala
// op
 
def fold1(b: Int)(op: (Int,Int) => Int)(l: List[Int]): Int =
  l match {
    case Nil => b
    case x :: xs => op(x,fold1(b)(op)(xs))
  }
 
// (x: Int,y: Int) => x - y
val sum1: List[Int] => Int = fold1(0)(_ + _)
 
fold1(100)(_ - _)(List(1,2,3))
 
// 100 - 1 - 2 - 3 (?!) (Nope)
// 1 - (2 - (3 - 100))
// x1 0111p0032(x2 0111p0032(x3 0111p0032b))
 
// ((b @ x1) @ x2) @ x3
 
def foldLeft(b: Int)(op:(Int, Int) => Int)(l: List[Int]): Int = {
  def loop (acc: Int, l: List[Int]): Int =
    l match {
      case Nil => acc
      case x :: xs => loop(op(acc,x), xs)
    }
  loop(b,l)
}
 
List(1,2,3).foldRight(0)(_ + _)
List(1,2,3).foldLeft(0)(_ - _)
 
/*
def reverse(l: List[Int]): List[Int] =
  l.foldLeft(Nil: List[Int])((acc,x) => x :: acc)
*/
 
def reverse(l: List[Int]): List[Int] = {
  def loop(acc: List[Int], l: List[Int]): List[Int] =
    l match {
      case Nil => acc
      case x :: xs => loop(x :: acc, xs)
    }
  loop(Nil,l)
}
reverse(List(1,2,3,4))
/*
reverse:
 ((b < x1) < x2) < x3
  x @ y este "introdu pe y in lista x"
 */
 
/*
Avem un sir, care contine numere si whitespace.
  "12 4 560 3"
 
 Vrem sa realizam un split dupa whitespace al acestui sir:
 "12", "4", "560", "3"
 */
type Sir = List[Char]
 
def split(s: Sir): List[Sir] = {
  def op (c: Char, acc: List[Sir]): List[Sir] =
    acc match {
      case Nil => if (c == ' ') Nil else List(List(c))
      case x :: xs => if (c == ' ') Nil :: acc else (c :: x) :: xs
    }
  s.foldRight(Nil: List[Sir])(op)
}
 
split("12 3 4 5".toList)