// Copyright (C) 2010 by Yan Huang <yhuang@virginia.edu>

package YaoGC;

import java.math.*;

import Cipher.*;
import sprout.oram.PID;
import sprout.oram.TID;

public abstract class T1_FF0_O_2_1 extends SimpleCircuit_2_1 {
	public T1_FF0_O_2_1() {
		super("T1_FF0_O_2_1");
	}

	public static T1_FF0_O_2_1 newInstance() {
		if (Circuit.isForGarbling)
			return new G_T1_FF0_O_2_1();
		else
			return new E_T1_FF0_O_2_1();
	}

	private int output(int l, int r) {
		int o;
		if (l == 0) {
			if (r == 0)
				o = 1;
			else
				o = 0;
		} else {
			if (r == 0)
				o = 0;
			else
				o = 0;
		}
		return o;
	}

	protected void compute() {
		int left = inputWires[0].value;
		int right = inputWires[1].value;

		outputWires[0].value = output(left, right);
	}

	protected void fillTruthTable() {
		Wire inWireL = inputWires[0];
		Wire inWireR = inputWires[1];
		Wire outWire = outputWires[0];

		BigInteger[] labelL = { inWireL.lbl, Wire.conjugate(inWireL.lbl) };
		if (inWireL.invd == true) {
			BigInteger tmp = labelL[0];
			labelL[0] = labelL[1];
			labelL[1] = tmp;
		}

		BigInteger[] labelR = { inWireR.lbl, Wire.conjugate(inWireR.lbl) };
		if (inWireR.invd == true) {
			BigInteger tmp = labelR[0];
			labelR[0] = labelR[1];
			labelR[1] = tmp;
		}

		int k = outWire.serialNum;

		gtt = new BigInteger[2][2];

		int cL = inWireL.lbl.testBit(0) ? 1 : 0;
		int cR = inWireR.lbl.testBit(0) ? 1 : 0;

		BigInteger[] lb = new BigInteger[2];
		timing.stopwatch[PID.sha1][TID.offline].start();
		lb[output(cL, cR)] = Cipher.encrypt(labelL[cL], labelR[cR], k, BigInteger.ZERO);
		timing.stopwatch[PID.sha1][TID.offline].stop();
		lb[1 - output(cL, cR)] = Wire.conjugate(lb[output(cL, cR)]);
		outWire.lbl = lb[0];

		gtt[0 ^ cL][0 ^ cR] = lb[1];
		gtt[0 ^ cL][1 ^ cR] = lb[0];
		gtt[1 ^ cL][0 ^ cR] = lb[0];
		gtt[1 ^ cL][1 ^ cR] = lb[0];

		int lsb = lb[0].testBit(0) ? 1 : 0;
		if (outputWires[0].outBitEncPair != null) {
			timing.stopwatch[PID.sha1][TID.offline].start();
			outputWires[0].outBitEncPair[lsb] = Cipher.encrypt(k, lb[0], 0);
			outputWires[0].outBitEncPair[1 - lsb] = Cipher.encrypt(k, lb[1], 1);
			timing.stopwatch[PID.sha1][TID.offline].stop();
		}
	}

	protected boolean shortCut() {
		if (inputWires[0].value == 1) {
			outputWires[0].value = 0;
			return true;
		}

		if (inputWires[1].value == 1) {
			outputWires[0].value = 0;
			return true;
		}

		return false;
	}

	protected boolean collapse() {
		Wire inWireL = inputWires[0];
		Wire inWireR = inputWires[1];
		Wire outWire = outputWires[0];

		if (inWireL.lbl.equals(inWireR.lbl)) {
			if (inWireL.invd == inWireR.invd) {
				outWire.invd = inWireL.invd;
				outWire.setLabel(inWireL.lbl);
			} else {
				outWire.invd = false;
				outWire.value = 0;
			}

			return true;
		}

		return false;
	}
}
