const N: usize = 200000;
const Q: usize = 200000;
const V: u64 = 10000000000000000000;

const ZERO: usize = N - 1;
const SCRATCH: usize = 10;
const OPSCRATCH: usize = 20;
const TWOPOW: usize = 1000;
const MASK: usize = 1100;

const A: u64 = 0x65c816;
const B: u64 = 6502;

fn main() {
	let mut st = State::new(A, B);
	st.generate_two_pows();
	st.generate_masks();

	let abin = 100;
	let bbin = 200;
	st.to_binary(0, 32, abin);
	st.to_binary(1, 32, bbin);

	for i in 0..32 {
		for j in 0..32 {
			st.and(abin + i, bbin + j, 3000 + i * 100 + i + j);
		}
	}

	for i in 0..32 {
		st.binary_add(3000 + i * 100, 10000 + i * 100, 10000 + (i + 1) * 100);
	}

	st.to_decimal(10000 + 32 * 100, 2);

	debug_assert!(st.query_size() <= Q);
	debug_assert!(st.get(2) == A * B);

	st.print();
}

struct State {
	query: usize,
	array: Vec<u64>,
	output: String
}

impl State {
	fn new(a: u64, b: u64) -> Self {
		let mut array = vec![0; N];
		array[0] = a;
		array[1] = b;
		State { query: 0, array, output: String::new() }
	}

	fn slice(&self, l: usize, r: usize) -> &[u64] {
		&self.array[l..r]
	}

	fn get(&self, index: usize) -> u64 {
		self.array[index]
	}

	fn print(&self) {
		println!("{}", self.query);
		println!("{}", self.output);
	}

	fn query_size(&self) -> usize {
		self.query
	}

	fn generate_two_pows(&mut self) {
		self.or(0, 1, TWOPOW);
		for i in 0..63 {
			self.add(TWOPOW + i, TWOPOW + i, TWOPOW + i + 1);
		}
	}

	fn generate_masks(&mut self) {
		for i in 0..63 {
			self.add(TWOPOW + i, MASK + i, MASK + i + 1);
		}
	}

	fn add(&mut self, i: usize, j: usize, dst: usize) {
		self.output += &format!("+ {} {} {}\n", i, j, dst);
		self.array[dst] = self.array[i].saturating_add(self.array[j]);
		debug_assert!(self.array[dst] <= V);
		self.query += 1;
	}

	fn lt(&mut self, i: usize, j: usize, dst: usize) {
		self.output += &format!("< {} {} {}\n", i, j, dst);
		self.array[dst] = if self.array[i] < self.array[j] { 1 } else { 0 };
		self.query += 1;
	}

	fn stz(&mut self, dst: usize) {
		self.lt(ZERO, ZERO, dst);
	}

	fn shl(&mut self, src: usize, shift: usize) {
		for _ in 0..shift {
			self.add(src, src, src);
		}
	}

	fn or(&mut self, i: usize, j: usize, dst: usize) {
		self.lt(ZERO, i, OPSCRATCH);
		self.lt(ZERO, j, OPSCRATCH + 1);
		self.add(OPSCRATCH, OPSCRATCH + 1, OPSCRATCH + 2);
		self.lt(ZERO, OPSCRATCH + 2, dst);
	}

	fn and(&mut self, i: usize, j: usize, dst: usize) {
		self.lt(ZERO, i, OPSCRATCH);
		self.lt(ZERO, j, OPSCRATCH + 1);
		self.add(OPSCRATCH, OPSCRATCH + 1, OPSCRATCH + 2);
		self.lt(TWOPOW + 0, OPSCRATCH + 2, dst);
	}

	fn to_binary(&mut self, src: usize, bit_range: usize, dst: usize) {
		self.stz(SCRATCH + 1);
		for i in (0..bit_range).rev() {
			self.add(SCRATCH + 1, MASK + i, SCRATCH);
			self.lt(SCRATCH, src, dst + i);
			self.lt(SCRATCH, src, SCRATCH + 2);
			self.shl(SCRATCH + 2, i);
			self.add(SCRATCH + 2, SCRATCH + 1, SCRATCH + 1);
		}
	}

	fn binary_add(&mut self, src: usize, operand: usize, dst: usize) {
		self.stz(SCRATCH);
		for i in 0..64 {
			self.add(src + i, operand + i, dst + i);
			self.add(dst + i, SCRATCH, dst + i);
			self.lt(TWOPOW, dst + i, SCRATCH);

			self.lt(MASK, dst + i, SCRATCH + 1);
			self.lt(dst + i, TWOPOW + 1, SCRATCH + 2);
			self.and(SCRATCH + 1, SCRATCH + 2, SCRATCH + 1);
			self.lt(TWOPOW + 1, dst + i, SCRATCH + 3);
			self.lt(dst + i, TWOPOW + 2, SCRATCH + 4);
			self.and(SCRATCH + 3, SCRATCH + 4, SCRATCH + 3);

			self.or(SCRATCH + 1, SCRATCH + 3, dst + i);
		}
	}

	fn to_decimal(&mut self, src: usize, dst: usize) {
		self.stz(dst);
		for i in (0..64).rev() {
			self.shl(dst, 1);
			self.add(src + i, dst, dst);
		}
	}
}


Measured memory_bytes: 2876