import lib._
import turing._
import abacus._
import recs._
import comp1._
import comp2._


// Turing machine examples
val TMCopy = TM((WBk, 5), (R, 2), (R, 3), (R, 2), (WOc, 3), 
                (L, 4), (L, 4), (L, 5), (R, 11), (R, 6), 
                (R, 7), (WBk, 6), (R, 7), (R, 8), (WOc, 9), 
                (R, 8), (L, 10), (L, 9), (L, 10), (L, 5), 
                (L, 0), (R, 12), (WOc, 13), (L, 14), (R, 12), 
                (R, 12), (L, 15), (WBk, 14), (R, 0), (L, 15))

/*
println("TMCopy:    " + (TMCopy.run(Tape(3))))
println("TMfindnth: " + (TMFindnth(3).run(Tape(1,2,3,4,5))))
println("TMMopup:   " + (TMMopup(3).run(Tape(1,2,3,4,5))))

println("TMCopyMop: " + ((TMCopy :+ TMMopup(0))))
println("TMCopyMop: " + ((TMCopy :+ TMMopup(1)).run(Tape(3))))
*/

// Abacus machine examples
def Copy(in: Int, out: Int, jump: Int) = 
  Abacus(Dec(in, jump), Inc(out), Goto(0)) 

def Plus(m: Int, n: Int, tmp: Int, jump: Int) =
  Abacus(Dec(m, 4), Inc(n), Inc(tmp), Goto(0), Dec(tmp, jump), Inc(m), Goto(4))

def Mult(in1: Int, in2: Int, out: Int, tmp: Int, jump: Int) = 
  Abacus(Dec(in1, jump)) ++ Plus(in2, out, tmp, -1).shift(1, -1).adjust(-1, 0)

def Expo(in1: Int, in2: Int, out: Int, tmp1: Int, tmp2: Int, jump: Int) = {
  Abacus(Inc(out), Dec(in1, jump)) ++ 
  Mult(out, in2, tmp2, tmp1, -1).shift(2, -1).adjust(-1, 10) ++
  Copy(tmp2, out, -1).shift(10, -1).adjust(-1, 1)
}

println("Copy 3:     " + (Copy(0, 1, -1).run(Map(0 -> 3, 1 -> 0))))
println("Plus 3 + 4: " + (Plus(0, 1, 2, -1).run(Map(0 -> 3, 1 -> 4, 2 -> 0))))
println("Mult 3 * 5: " + (Mult(0, 1, 2, 3, -1).run(Map(0 -> 3, 1 -> 5, 2 -> 0, 3 -> 0))))
println("Expo 3 ^ 4: " + (Expo(0, 1, 2, 3, 4, -1).run(Map(0 -> 4, 1 -> 3, 2 -> 0, 3 -> 0, 4 -> 0))))

/*
// Abacus-to-TM translation examples
println("Compiled Copy 3:     " + toTM(Copy(0, 1, Int.MaxValue).p).run(Tape(3,0,0)))
println("Compiled Plus 3 + 4: " + toTM(Plus(0, 1, 2, Int.MaxValue).p).run(Tape(3,4,0,0)))
println("Compiled Mult 3 * 5: " + toTM(Mult(0, 1, 2, 3, Int.MaxValue).p).run(Tape(3,5,0,0)))
println("Compiled Expo 3 ^ 4: " + toTM(Expo(0, 1, 2, 3, 4, 10000).p).run(Tape(3,4,0,0,0)))
*/
 
// Recursive function examples 
println("Const 8:   " + Const(8).eval(67))
println("Add 3 4:   " + Add.eval(3, 4))
println("Mult 3 4:  " + recs.Mult.eval(3, 4))
println("Power 2 3: " + Power.eval(2, 3))
println("Fact 5:    " + Fact.eval(5))
println("Pred 9:    " + Pred.eval(9))
println("Pred 0:    " + Pred.eval(0))
println("Minus 6 2: " + Minus.eval(6, 2))
println("Minus 6 8: " + Minus.eval(6, 8))


println("Sign 8:    " + Sign.eval(8))
println("Sign 0:    " + Sign.eval(0))
println("Not 0:     " + Not.eval(0))
println("Not 6:     " + Not.eval(6))
println("Eq 4 4:    " + Eq.eval(4, 4))
println("Eq 4 6:    " + Eq.eval(4, 6))
println("Eq 6 4:    " + Eq.eval(6, 4))
println("NotEq 4 4: " + Noteq.eval(4, 4))
println("NotEq 4 6: " + Noteq.eval(4, 6))
println("NotEq 6 4: " + Noteq.eval(6, 4))
println("Conj 0 6:  " + Conj.eval(0, 6))
println("Conj 6 4:  " + Conj.eval(6, 4))
println("Conj 0 0:  " + Conj.eval(0, 0))
println("Disj 0 6:  " + Disj.eval(0, 6))
println("Disj 6 4:  " + Disj.eval(6, 4))
println("Disj 0 0:  " + Disj.eval(0, 0))
println("Imp  6 1:  " + Imp.eval(6, 1))
println("Imp  6 4:  " + Imp.eval(6, 4))
println("Imp  1 0:  " + Imp.eval(1, 0))
println("Ifz  0 1 2:" + Ifz.eval(0, 1, 2))
println("Ifz  1 1 2:" + Ifz.eval(1, 1, 2))
println("If   0 1 2:" + If.eval(0, 1, 2))
println("If   1 1 2:" + If.eval(1, 1, 2))


println("Less 4 4:  " + Less.eval(4, 4))
println("Less 4 6:  " + Less.eval(4, 6))
println("Less 6 4:  " + Less.eval(6, 4))
println("Le 4 4:    " + recs.Le.eval(4, 4))
println("Le 4 6:    " + recs.Le.eval(4, 6))
println("Le 6 4:    " + recs.Le.eval(6, 4))

println("Sigma1 Add 2 3 -> 12: " + Sigma1(Add).eval(2, 3))
println("Accum1 Add 2 3 -> 60:  " + Accum1(Add).eval(2,3))
println("Accum1 Mult 2 3 -> 0:  " + Accum1(recs.Mult).eval(2,3))
println("Accum1 (Id(2, 1)) 2 3 -> 27:  " + Accum1(Id(2, 1)).eval(2,3))
println("Accum2 (Id(3, 1)) 2 3 3 -> 27:  " + Accum2(Id(3, 1)).eval(2,3,3))
println("Accum3 (Id(4, 1)) 2 3 3 3 -> 27:  " + Accum3(Id(4, 1)).eval(2,3,3,3))
println("All1 Add 2 0 -> 0:  " + All1(Add).eval(2,0))
println("All1 Add 2 1 -> 1:  " + All1(Add).eval(2,1))
println("All1_less Add 3 0 -> 0:  " + All1_less(Add).eval(2,0))
println("All1_less Add 3 1 -> 1:  " + All1_less(Add).eval(2,1))
println("All2_less (Id 3 1) 2 3 0 -> 1:  " + All2_less(Id(3, 1)).eval(2,3,0))
println("All2_less (Id 3 0) 2 3 0 -> 0:  " + All2_less(Id(3, 0)).eval(2,3,0))
println("Ex1 Add 2 0 -> 1:       " + Ex1(Add).eval(2,0))
println("Ex2 Id(3,1) 2 1 0 -> 1: " + Ex2(Id(3, 1)).eval(2,1,0))
println("Ex2 Id(3,2) 2 1 0 -> 0: " + Ex2(Id(3, 2)).eval(2,1,0))

println("Quo 6 4 -> 1:   " + Quo.eval(6, 4))
println("Quo 13 4 -> 3:  " + Quo.eval(13, 4))

println("Triangle 0 - 5: " + (0 until 5).map(Triangle.eval(_)).mkString(","))
println("MaxTriangle 10 -> 4 " + MaxTriangle.eval(10))

println("Penc 1 2 -> 7: " + Penc.eval(1, 2))
println("Pdec1 7 -> 1:  " + Pdec1.eval(7))
println("Pdec2 7 -> 2:  " + Pdec2.eval(7))
println("Enclen 0 .. 10: " + (0 until 10).map(Enclen.eval(_)))

println("Size of UF -> 140843: " + UF.size)


// compilation of rec to abacus tests
def test_comp2(f: Rec, ns: Int*) = {
  val (abc_f, _) = compile_rec(f)
  val abc_map = (0 until ns.length).zip(ns).toMap[Int, Int]
  //val start = System.nanoTime()
  //val res = (abc_f.run(abc_map))(arity)
  //val end = System.nanoTime()
  //val time = (end - start)/1.0e9
  //("Result: " + res + "  length: " + abc_f.p.length + " time: " + "%.5f".format(time))
  ("Length: " + abc_f.p.length)
}


println("S(3)          " + test_comp2(S, 3))
println("Const(1)      " + test_comp2(Const(1), 0))
println("Const(10)     " + test_comp2(Const(10), 0))
println("Add(69, 30)   " + test_comp2(Add, 69, 30))
println("Mult(13, 9)   " + test_comp2(recs.Mult, 13, 9))
println("Power(3, 4)   " + test_comp2(Power, 3, 4))
println("Minus(30, 4)  " + test_comp2(Minus, 30, 4))
println("Fact(5)       " + test_comp2(Fact, 5))
//println("UF(0)         " + test_comp2(UF, 0))

def test_comp1(f: Rec, ns: Int*) = {
  val (abc_f, _) = compile_rec(f)
  println("Abacus Length: " + abc_f.p.length)
  val tm = toTM(abc_f.p) :+ TMMopup(f.arity)
  //val start = System.nanoTime()
  //val res = tm.run(Tape(ns.toList))
  //val end = System.nanoTime()
  //val time = (end - start)/1.0e9
  //("length: " + tm.p.length + " tape: " + arity + " time: " + "%.5f".format(time) + "\nResult: " + res)
  ("Length: " + abc_f.p.length + " " + tm.p.length)
}

//println("")
//println("S(3)          " + test_comp1(S, 3))
//println("Const(10)     " + test_comp1(Const(10), 0))
//println("Add(6, 3)     " + test_comp1(Add, 6, 3))
//println("Mult(4, 5)    " + test_comp1(recs.Mult, 4, 5))
//println("Fact(4)       " + test_comp1(Fact, 4))
println("UF(0)         " + test_comp1(UF, 0))

