package object abacus {
import scala.annotation.tailrec
import lib._
// Abacus instructions
sealed abstract class AInst {
def print : String
}
case class Inc(n: Int) extends AInst {
override def print = "Inc(" + n.toString + ")\n"
}
case class Dec(n: Int, l: Int) extends AInst {
override def print = "Dec(" + n.toString + "," + l.toString + ")\n"
}
case class Goto(l: Int) extends AInst {
override def print = "Goto(" + l.toString + ")\n"
}
type AProg = List[AInst]
type Regs = Map[Int, Int]
// Abacus configurations
case class AConfig(s: Int, regs: Regs)
// Abacus machines
case class Abacus(p: AProg) {
def print = p.foldLeft[String]("")(_ + _.print)
//simple composition
def ++ (that: Abacus) = Abacus(p ::: that.p)
def :+ (that: Abacus) = this ++ that.shift(p.length, -1)
def shift(offset: Int, jump: Int) = Abacus(p.map(_ match {
case Inc(n) => Inc(n)
case Dec(n, l) => if (l == jump) Dec(n, l) else Dec(n, l + offset)
case Goto(l) => if (l == jump) Goto(l) else Goto(l + offset)
}))
def adjust(old_jump: Int, jump: Int) = Abacus(p.map(_ match {
case Inc(n) => Inc(n)
case Dec(n, l) => if (l == old_jump) Dec(n, jump) else Dec(n, l)
case Goto(l) => if (l == old_jump) Goto(jump) else Goto(l)
}))
def step(cf: AConfig) : AConfig = nth_of(p, cf.s) match {
case None => cf
case Some(Inc(n)) => AConfig(cf.s + 1, cf.regs + (n -> (dget(cf.regs, n) + 1)))
case Some(Dec(n, l)) => {
if (dget(cf.regs, n) == 0) AConfig(l, cf.regs)
else AConfig(cf.s + 1, cf.regs + (n -> (dget(cf.regs, n) - 1)))
}
case Some(Goto(l)) => AConfig(l, cf.regs)
}
def steps(cf: AConfig, n: Int) : AConfig = n match {
case 0 => cf
case n => steps(step(cf), n - 1)
}
def steps(regs: Regs, n: Int) : AConfig = steps(AConfig(0, regs), n)
@tailrec
final def run(cf: AConfig) : AConfig = {
if (cf.s >= p.length || cf.s < 0) cf else run(step(cf))
}
def run(regs: Regs): Regs = run(AConfig(0, regs)).regs
}
// some syntactical convenience
object Abacus {
def apply(is: AInst*) : Abacus = Abacus(is.toList)
}
}