func pow(base: Int = 10, _ n: Int) -> Int {
    precondition( n >= 0 )
    switch n {
    case 0: return 1
    case 1: return base
    case _ where n.isMultiple(of: 2):
        return pow(base: base * base, n / 2)
    default:
        return base * pow(base: base * base, n / 2)
    }
}

public protocol IO: class {
    func input() -> Int
    func output(_ value: Int)
}

public class IntCode {
    var memory: [Int]

    var pc = 0
    var base = 0

    var io: IO

    public init(program: [Int], io: IO) {
        self.memory = program
        self.io = io
    }

    func address(_ index: Int) -> Int {
        let mode = (memory[pc] / pow(2 + index - 1)) % 10

        switch mode {
        case 0: return memory[pc + index]
        case 1: return pc + index
        case 2: return memory[pc + index] + base
        default: preconditionFailure("Unknown mode \(mode)")
        }
    }

    func get(_ index: Int) -> Int {
        let addr = address(index)
        return addr < memory.count ? memory[addr] : 0
    }

    func put(_ index: Int, value: Int) {

        let addr = address(index)

        let toAdd = addr - memory.count + 1
        if toAdd > 0 {
            memory.append(contentsOf: Array(repeating: 0, count: toAdd))
        }

        memory[addr] = value
    }


    public func run() {
        loop: while(true) {
            let opcode = memory[pc] % 100

            switch opcode {
            case 1:
                put(3, value: get(1) + get(2))
                pc += 4

            case 2:
                put(3, value: get(1) * get(2))
                pc += 4

            case 3:
                put(1, value: io.input())
                pc += 2

            case 4:
                io.output(get(1))
                pc += 2

            case 5:
                if get(1) != 0 {
                    pc = get(2)
                } else {
                    pc += 3
                }

            case 6:
                if get(1) == 0 {
                    pc = get(2)
                } else {
                    pc += 3
                }

            case 7:
                put(3, value: get(1) < get(2) ? 1 : 0)
                pc += 4

            case 8:
                put(3, value: get(1) == get(2) ? 1 : 0)
                pc += 4

            case 9:
                base += get(1)
                pc += 2

            case 99:
                break loop

            default:
                preconditionFailure("Unknown opcode \(opcode)")

            }
        }
    }
}