class CompileState(object):
    def __init__(self):
        self.vars = {}
        self.cells = set()
        self.currentCell = 0

    def requestVar(self,name):
        try:
            return self.vars[name]
        except:
            cell = 0
            while cell in self.cells:
                cell+=1
            self.vars[name] = cell
            self.cells.add(cell)
            return cell

    def releaseVar(self,name):
        try:
            cell = self.vars[name]
        except KeyError:
            return False

        del self.vars[name]
        self.cells.remove(cell)

    def requestTempVar(self):
        vid = object()
        cell = self.requestVar(vid)
        return vid, cell

    def incrementCell(self):
        self.currentCell += 1

    def decrementCell(self):
        self.currentCell -= 1

class Command(object):
    def __str__(self):
        return self.__class__.__name__

    def compile(self,state):
        raise NotImplementedError


class SimpleRepeatedCommand(Command):
    def __init__(self,repeats,command):
        self.repeats = repeats
        self.command = command

    def compile(self,state):
        s = ""
        for i in range(self.repeats):
            s += self.command.compile(state)
        return s

    def __str__(self):
        return "Repeat(%d)(%s)" % (self.repeats, str(self.command))

class CompositeCommand(Command):
    def __init__(self,commands):
        self.commands = commands

    def compile(self,state):
        s=""
        for command in self.commands:
            s+=command.compile(state)
        return s

    def __str__(self):
        return "[" + ",".join([ str(command) for command in self.commands]) + "]"

class SimpleIncrement(Command):
    def compile(self,state):
        return '+'

class SimpleDecrement(Command):
    def compile(self,state):
        return '-'

class SimpleNextCell(Command):
    def compile(self,state):
        state.incrementCell()
        return '>'

class SimplePrevCell(Command):
    def compile(self,state):
        state.decrementCell()
        return '<'

class SimpleOutput(Command):
    def compile(self,state):
        return '.'

class SimpleInput(Command):
    def compile(self,state):
        return ','

class SimpleLoopBegin(Command):
    def compile(self,state):
        return '['

class SimpleLoopEnd(Command):
    def compile(self,state):
        return ']'

class SimpleLoop(Command):
    def __init__(self,command):
        self.command = command

    def compile(self,state):
        return CompositeCommand([SimpleLoopBegin(), self.command, SimpleLoopEnd()]).compile(state)

class SelectVariable(Command):
    def __init__(self,variable):
        self.variable = variable

    def compile(self,state):
        d = state.requestVar(self.variable) - state.currentCell
        if d < 0:
            return SimpleRepeatedCommand(-d,SimplePrevCell()).compile(state)
        elif d > 0:
            return SimpleRepeatedCommand(d,SimpleNextCell()).compile(state)
        else:
            return ""

class ClearCell(Command):
    def compile(self,state):
        return SimpleLoop(SimpleDecrement()).compile(state)

class WriteConstant(Command):
    def __init__(self,const):
        self.const = const

    def compile(self,state):
        return CompositeCommand([ClearCell(),SimpleRepeatedCommand(self.const, SimpleIncrement())]).compile(state)

class SetConstant(Command):
    def __init__(self,variable,const):
        self.variable = variable
        self.const = const

    def compile(self,state):
        return CompositeCommand([SelectVariable(self.variable),WriteConstant(self.const)]).compile(state)

SetVariable = SetConstant

class Move(Command):
    def __init__(self, source, dest):
       self.source = source
       self.dest = dest

    def compile(self,state):
        return CompositeCommand([
            SelectVariable(self.dest),
            ClearCell(),
            SimpleAddition(self.source,self.dest)
        ]).compile(state)

class Copy(Command):
    def __init__(self,source,dest):
        self.source = source
        self.dest = dest

    def compile(self,state):
        tmp, _ = state.requestTempVar()
        s = CompositeCommand([
            SelectVariable(self.dest),
            ClearCell(),
            SelectVariable(tmp),
            ClearCell(),
            SelectVariable(self.source),
            SimpleLoop(CompositeCommand([
                SelectVariable(self.dest),
                SimpleIncrement(),
                SelectVariable(tmp),
                SimpleIncrement(),
                SelectVariable(self.source),
                SimpleDecrement()
            ])),
            Move(tmp,self.source)
        ]).compile(state)
        state.releaseVar(tmp)
        return s

class SimpleAddition(Command):
    def __init__(self, source, dest):
        self.source = source
        self.dest = dest

    def compile(self,state):
        return CompositeCommand([
            SelectVariable(self.source),
            SimpleLoop(CompositeCommand([
                SelectVariable(self.dest),
                SimpleIncrement(),
                SelectVariable(self.source),
                SimpleDecrement(),
            ]))
        ]).compile(state)

class Addition2(Command):
    def __init__(self,source,dest):
        self.source = source
        self.dest = dest

    def compile(self,state):
        tmp, _ = state.requestTempVar()
        s = CompositeCommand([
            Copy(self.source,tmp),
            SimpleAddition(tmp,self.dest)
        ]).compile(state)
        state.releaseVar(tmp)
        return s

class Condition(Command):
    def __init__(self,condition,command):
        self.condition = condition
        self.command = command

    def compile(self,state):
        return CompositeCommand([
            SelectVariable(self.condition),
            SimpleLoop(CompositeCommand([
                self.command,
                SelectVariable(self.condition),
                ClearCell(),
            ])),
        ]).compile(state)

class ConditionKeep(Command):
    def __init__(self,condition,command):
        self.condition = condition
        self.command = command

    def compile(self,state):
        tmp, _ = state.requestTempVar()
        s = CompositeCommand([
            Copy(self.condition,tmp),
            Condition(tmp,self.command)
        ]).compile(state)
        state.releaseVar(tmp)
        return s

class Booleanize(Command):
    def __init__(self,variable):
        self.variable = variable

    def compile(self,state):
        tmp, _ = state.requestTempVar()
        s = CompositeCommand([
            SelectVariable(tmp),
            ClearCell(),
            Condition(self.variable,CompositeCommand([
                SelectVariable(tmp),
                SimpleIncrement()
            ])),
            Move(tmp,self.variable)
        ]).compile(state)
        state.releaseVar(tmp)
        return s

class Not(Command):
    def __init__(self,variable):
        self.variable = variable

    def compile(self,state):
        tmp, _ = state.requestTempVar()
        s = CompositeCommand([
            SelectVariable(tmp),
            WriteConstant(1),
            Condition(self.variable,CompositeCommand([
                SelectVariable(tmp),
                SimpleDecrement()
            ])),
            Move(tmp,self.variable)
        ]).compile(state)
        state.releaseVar(tmp)
        return s

class And(Command):
    def __init__(self,source,dest):
        self.source = source
        self.dest = dest

    def compile(self,state):
        return ConditionKeep(self.dest, Move(self.source,self.dest)).compile(state)

class Or(Command):
    def __init__(self,source,dest):
        self.source = source
        self.dest = dest

    def compile(self,state):
        return CompositeCommand([
            SimpleAddition(self.source, self.dest),
            SelectVariable(self.dest),
            Booleanize(self.dest)
        ]).compile(state)

if __name__ == "__main__":
    c = CompositeCommand([
        SetVariable("a",2),
        SetVariable("b",5),
        Addition2("b","a"),
        Copy("a","tmp"),
        Or("tmp","b"),
        ConditionKeep("tmp",CompositeCommand([
            SelectVariable("b"),
            SimpleIncrement()
        ])),
        Not("tmp"),
        Condition("tmp",CompositeCommand([
            SelectVariable("b"),
            SimpleDecrement()
        ]))
    ])

    print(c.compile(CompileState()))