/*
 * Magic state distillation and computation
 */
include "stdgates.inc";

const int[32] buffer_size = 6;  // size of magic state buffer

// Y-basis measurement
def ymeasure(qubit q) -> bit {
  s q;
  h q;
  return measure q;
}

/*
 * Distillation subroutine takes 10 |H> magic states
 * and 3 scratch qubits that will be reinitialized.
 * The first two input magic states are the outputs.
 * The subroutine returns a success bit that is true
 * on success and false otherwise (see arXiv:1811.00566).
 */
def distill(qubit[10] magic, qubit[3] scratch) -> bool {
  bit temp;
  bit[3] checks;
  // Encode two magic states in the [[4,2,2]] code
  reset scratch[0: 1];
  h scratch[1];
  cx scratch[1], magic[0];
  cx magic[1], scratch[0];
  cx magic[0], scratch[0];
  cx scratch[1], magic[1];
  // Body of distillation circuit
  cy magic[2], scratch[0];
  h magic[1];
  temp = ymeasure(magic[2]);
  if(temp == 1) { ry(-pi / 2) scratch[0]; }
  reset scratch[2];
  h scratch[2];
  cz scratch[2], scratch[0];
  cy magic[3], scratch[0];
  temp = ymeasure(magic[3]);
  if(temp==0) { ry(pi / 2) scratch[0]; }
  h scratch[0];
  s scratch[0];
  cy magic[4], scratch[1];
  temp = ymeasure(magic[4]);
  if(temp==1) { ry(-pi / 2) scratch[1]; }
  cz scratch[3], scratch[2];
  cy magic[5], scratch[1];
  temp = ymeasure(magic[5]);
  if(temp==0) { ry(pi / 2) scratch[1]; }
  cy scratch[0], magic[1];
  inv @ s scratch[1];
  cz scratch[0], scratch[1];
  h scratch[0];
  cy scratch[1], magic[1];
  cy magic[6], scratch[0];
  temp = ymeasure(magic[6]);
  if(temp == 1) { ry(-pi / 2) scratch[0]; }
  cz scratch[2], scratch[1];
  cz scratch[2], scratch[0];
  cy magic[7], scratch[0];
  temp = ymeasure(magic[7]);
  if(temp == 0) ry(pi / 2) scratch[0];
  cy magic[8], scratch[1];
  temp = ymeasure(magic[8]);
  if(temp==1) { ry(-pi / 2) scratch[1]; }
  cz scratch[2], scratch[1];
  cy magic[9], scratch[1];
  temp = ymeasure(magic[9]);
  if(temp == 0) { ry(pi / 2) scratch[1]; }
  h scratch[2];
  // Decode [[4,2,2]] code
  cx magic[0], scratch[0];
  cx scratch[1], magic[1];
  cx magic[1], scratch[0];
  cx scratch[1], magic[0];
  h scratch[1];
  checks = measure scratch;
  success = ~(bool(checks[0]) | bool(checks[1]) | bool(checks[2]));
  return success;
}

// Repeat level-0 distillation until success
def rus_level_0(qubit[10] magic, qubit[3] scratch) {
  bool success;
  while(~success) {
    reset magic;
    ry(pi / 4) magic;
    success = distill(magic, scratch);
  }
}

/*
 * Run two levels of 10:2 magic state distillation.
 * Both levels have two distillations running in parallel.
 * The output pairs from the first level are separated and
 * input to different second levels distillation circuits
 * because a failure in a first level circuit can lead to
 * errors on both outputs.
 * Put the requested even number of copies into the buffer.
 */
def distill_and_buffer(int[32] num, qubit[33] work, qubit[buffer_size] buffer) {
  int[32] index;
  bit success_0;
  bit success_1;
  let magic_lvl0 = work[0: 9];
  let magic_lvl1_0 = work[10: 19];
  let magic_lvl1_1 = work[20: 29];
  let scratch = work[30: 32];

  // Run first-level circuits until 10 successes,
  // storing the outputs for use in the second level
  for uint i in [0: 9] {
    rus_level_0 magic_lvl0, scratch;
    swap magic_lvl0[0], magic_lvl1_0[i];
    swap magic_lvl0[1], magic_lvl1_1[i];
  }

  // Run two second level circuits simultaneously
  success_0 = distill(magic_lvl1_0, scratch);
  success_1 = distill(magic_lvl1_1, scratch);

  // Move usable magic states into the buffer register
  if(success_0 && index < buffer_size) {
    swap magic_lvl1_0[0: 1], buffer[index: index + 1];
    index += 2;
  }
  if(success_1 && index < buffer_size) {
    swap magic_lvl1_1[0: 1], buffer[index: index + 1];
    index += 2;
  }
}

// Apply Ry(pi/4) to a qubit by consuming a magic state
// from the magic state buffer at address "addr"
def Ty(int[32] addr, qubit q, qubit[buffer_size] buffer) {
  bit outcome;
  cy buffer[addr], q;
  outcome = ymeasure(buffer[addr]);
  if(outcome == 1) ry(pi / 2) q;
}

qubit[33] workspace;
qubit[buffer_size] buffer;

qubit[5] q;
bit[5] c;
int[32] address;

// initialize
reset workspace;
reset buffer;
reset q;

distill_and_buffer(buffer_size) workspace, buffer;

// Consume magic states to apply some gates ...
h q[0];
cx q[0], q[1];
Ty(address) q[0], buffer;
address += 1;
cx q[0], q[1];
Ty(address) q[1], buffer;
address += 1;

// In principle each Ty gate can execute as soon as the magic
// state is available at the address in the buffer register.

// We can continue alternating state distillation and computation
// to refill and empty a circular buffer.
