func main() {
	app := cli.NewApp()
	app.Action = func(ctx *cli.Context) {
		prog := ctx.GlobalString("prog")
		if prog == "" {
			log.Fatal(
				error_.New("--prog (or -p) required!"),
			)
		}
		file, err := os.Open(prog)
		if err != nil {
			log.Fatal(err)
		}
		jsonDec := json.NewDecoder(file)
		var machineDesc machine.MachineDesc
		err = jsonDec.Decode(&machineDesc)
		if err != nil {
			log.Fatal(err)
		}
		machine, err := machine.NewMachine(&machineDesc)
		if err != nil {
			log.Fatal(err)
		}
		machine.Run()
	}
	app.Flags = []cli.Flag{
		cli.StringFlag{
			"prog,p",
			"",
			"Path to the turing machine \"program\" to run.",
		},
	}
	app.Name = "turing-machine"
	app.Usage = "Turing machinez tho."
	app.Version = "0.0.1"
	err := app.Run(os.Args)
	if err != nil {
		log.Fatal(err)
	}
}
func NewMachine(machineDesc *MachineDesc) (*Machine, error) {
	alphabet := make(map[Sym]bool)
	for i := range machineDesc.Alphabet {
		alphabet[machineDesc.Alphabet[i]] = true
	}
	states := make(map[State]bool)
	for i := range machineDesc.States {
		states[machineDesc.States[i]] = true
	}
	states[StateValHalt] = true
	for i := range machineDesc.InputSyms {
		if !alphabet[machineDesc.InputSyms[i]] {
			return nil, error_.New(
				fmt.Sprintf("<machine> Input symbol \"%s\" isn't in the alphabet!", machineDesc.InputSyms[i]),
			)
		}
	}
	machine := Machine{
		conf: &Conf{
			currState:   machineDesc.InitState,
			currTapePos: uint(0),
		},
		tape:     NewTape(machineDesc.InputSyms),
		transMap: make(map[MachineTransMapKey]MachineTransMapVal),
	}
	for i := range machineDesc.TransMap {
		if !states[machineDesc.TransMap[i].Map.State] {
			return nil, error_.New(
				fmt.Sprintf("<machine> Transition map output state \"%s\" isn't in the set of finite states!", machineDesc.TransMap[i].Map.State),
			)
		}
		if !alphabet[machineDesc.TransMap[i].Map.Sym] {
			return nil, error_.New(
				fmt.Sprintf("<machine> Transition map output symbol \"%s\" isn't in the alphabet!", machineDesc.TransMap[i].Map.Sym),
			)
		}
		if !states[machineDesc.TransMap[i].State] {
			return nil, error_.New(
				fmt.Sprintf("<machine> Transition map input state \"%s\" isn't in the set of finite states!", machineDesc.TransMap[i].State),
			)
		}
		if !alphabet[machineDesc.TransMap[i].Sym] {
			return nil, error_.New(
				fmt.Sprintf("<machine> Transition map input symbol \"%s\" isn't in the alphabet!", machineDesc.TransMap[i].Sym),
			)
		}
		switch machineDesc.TransMap[i].Map.Dir {
		case DirMovLeft, DirMovRight, DirStay:
			break
		default:
			return nil, error_.New(
				fmt.Sprintf("<machine> \"%s\" isn't a valid direction!", machineDesc.TransMap[i].Map.Dir),
			)
		}
		machine.transMap[MachineTransMapKey{
			state: machineDesc.TransMap[i].State,
			sym:   machineDesc.TransMap[i].Sym,
		}] = MachineTransMapVal{
			dir:   machineDesc.TransMap[i].Map.Dir,
			state: machineDesc.TransMap[i].Map.State,
			sym:   machineDesc.TransMap[i].Map.Sym,
		}
	}
	return &machine, nil
}