public static Varnode simplify(PcodeOp pcodeOp, Varnode[] values, AddressFactory addrFactory,
			TaskMonitor monitor) throws CancelledException {

		SequenceNumber seq = pcodeOp.getSeqnum();
		Varnode output = pcodeOp.getOutput();
		Varnode result = null;

		switch (pcodeOp.getOpcode()) {

			case PcodeOp.COPY:		        // Copy one operand to another
				result = values[0];
				break;

			case PcodeOp.INT_EQUAL:	        // Return TRUE if operand1 == operand2
				if (values[0].equals(values[1])) {
					result = new Varnode(addrFactory.getConstantAddress(1), 1);
				}
				else if (values[0].isConstant() && values[1].isConstant()) {
					int b =
						(getUnsignedOffset(values[0], values[0].getSize()) == getUnsignedOffset(
							values[1], values[1].getSize())) ? 1 : 0;
					result = new Varnode(addrFactory.getConstantAddress(b), 1);
				}
				break;

			case PcodeOp.INT_NOTEQUAL:	    // Return TRUE if operand1 != operand2
				if (values[0].equals(values[1])) {
					result = new Varnode(addrFactory.getConstantAddress(0), 1);
				}
				else if (values[0].isConstant() && values[1].isConstant()) {
					int b =
						(getUnsignedOffset(values[0], values[0].getSize()) != getUnsignedOffset(
							values[1], values[1].getSize())) ? 1 : 0;
					result = new Varnode(addrFactory.getConstantAddress(b), 1);
				}
				break;

			case PcodeOp.INT_SLESS:         // Return TRUE if signed op1 < signed op2
				if (values[0].isConstant() && values[1].isConstant()) {
					int b = (getSignedOffset(values[0]) < getSignedOffset(values[1])) ? 1 : 0;
					result = new Varnode(addrFactory.getConstantAddress(b), 1);
				}
				break;

			case PcodeOp.INT_SLESSEQUAL:	// Return TRUE if signed op1 <= signed op2
				if (values[0].isConstant() && values[1].isConstant()) {
					int b = (getSignedOffset(values[0]) <= getSignedOffset(values[1])) ? 1 : 0;
					result = new Varnode(addrFactory.getConstantAddress(b), 1);
				}
				break;

			case PcodeOp.INT_LESS:			// Return TRUE if unsigned op1 < unsigned op2
				// Also indicates borrow on unsigned substraction
				if (values[0].isConstant() && values[1].isConstant()) {
					int b =
						(getUnsignedOffset(values[0], values[0].getSize()) < getUnsignedOffset(
							values[1], values[1].getSize())) ? 1 : 0;
					result = new Varnode(addrFactory.getConstantAddress(b), 1);
				}
				break;

			case PcodeOp.INT_LESSEQUAL:		// Return TRUE if unsigned op1 <= unsigned op2
				if (values[0].isConstant() && values[1].isConstant()) {
					int b =
						(getUnsignedOffset(values[0], values[0].getSize()) <= getUnsignedOffset(
							values[1], values[1].getSize())) ? 1 : 0;
					result = new Varnode(addrFactory.getConstantAddress(b), 1);
				}
				break;

			case PcodeOp.INT_ZEXT:			// Zero extend operand 
				if (values[0].isConstant()) {
					result =
						new Varnode(addrFactory.getConstantAddress(getUnsignedOffset(values[0],
							values[0].getSize())), output.getSize());
				}
				break;

			case PcodeOp.INT_SEXT:			// Sign extend operand
				if (values[0].isConstant()) {
					long signedOffset = getSignedOffset(values[0]) & VALUE_MASK[output.getSize()];
					result =
						new Varnode(addrFactory.getConstantAddress(signedOffset), output.getSize());
				}
				break;

			case PcodeOp.INT_ADD:			// Unsigned addition of operands of same size
				if (values[0].isConstant()) {
					VarnodeOperation op = flipInputs(pcodeOp, values);
					pcodeOp = op.getPCodeOp();
				}
				if (values[1].isConstant()) {
					if (values[0].isConstant()) {
						long signedOffset =
							(getSignedOffset(values[1]) + getSignedOffset(values[0])) &
								VALUE_MASK[output.getSize()];
						result =
							new Varnode(addrFactory.getConstantAddress(signedOffset),
								output.getSize());
					}
					else if (values[1].getOffset() == 0) {
						result = values[0];
					}
					else if (values[0] instanceof VarnodeOperation) {
						//result = combineIntAddOffset(pcodeOp, (VarnodeOperation)values[0], getSignedOffset(values[1], output.getSize()), monitor);
						result =
							pushDownIntAddOffset((VarnodeOperation) values[0],
								getSignedOffset(values[1]), addrFactory, monitor);
					}
				}
				break;

			case PcodeOp.INT_SUB:			// Unsigned subtraction of operands of same size 
				if (values[0].equals(values[1])) {
					result = new Varnode(addrFactory.getConstantAddress(0), output.getSize());
				}
				else if (values[1].isConstant()) {
					if (values[0].isConstant()) {
						long signedOffset =
							(getSignedOffset(values[0]) - getSignedOffset(values[1])) &
								VALUE_MASK[output.getSize()];
						result =
							new Varnode(addrFactory.getConstantAddress(signedOffset),
								output.getSize());
					}
					else if (values[1].getOffset() == 0) {
						result = values[0];
					}
					else if (values[0] instanceof VarnodeOperation) {
						//result = combineIntAddOffset(pcodeOp, (VarnodeOperation)values[0], -getSignedOffset(values[1], output.getSize()), monitor);
						result =
							pushDownIntAddOffset((VarnodeOperation) values[0],
								-getSignedOffset(values[1]), addrFactory, monitor);
					}
				}
				break;

			case PcodeOp.INT_CARRY:       	// TRUE if adding two operands has overflow (carry)
				return eillimnateCarryOp(pcodeOp, values, addrFactory, monitor);

			case PcodeOp.INT_SCARRY:  		// TRUE if carry in signed addition of 2 ops 

// TODO: Implement constant case

			case PcodeOp.INT_SBORROW:  		// TRUE if borrow in signed subtraction of 2 ops 
				if ((values[1].isConstant() && values[1].getOffset() == 0) ||
					(values[0].isConstant() && values[0].getOffset() == 0)) {
					result = new Varnode(addrFactory.getConstantAddress(0), 1);
				}
				break;

			case PcodeOp.INT_2COMP:   		// Twos complement (for subtracting) of operand 
				if (values[0].isConstant()) {
					result =
						new Varnode(addrFactory.getConstantAddress(-values[0].getOffset()),
							output.getSize());
				}
				break;

			case PcodeOp.INT_NEGATE:
				if (values[0].isConstant()) {
					result =
						new Varnode(addrFactory.getConstantAddress(~values[0].getOffset()),
							output.getSize());
				}
				break;

			case PcodeOp.INT_XOR:			// Exclusive OR of two operands of same size 
				if (values[0].equals(values[1])) {
					// Anything XOR'd with itself is Zero
					result = new Varnode(addrFactory.getConstantAddress(0), output.getSize());
				}
				else if (values[1].isConstant() && values[0].isConstant()) {
					result =
						new Varnode(addrFactory.getConstantAddress(values[0].getOffset() ^
							values[1].getOffset()), output.getSize());
				}
				else if (values[0].isConstant()) {
					if (values[0].getOffset() == 0) {
						result = values[1];
					}
					else {
						VarnodeOperation op = flipInputs(pcodeOp, values);
						result = combineLogicOrOperation(op, addrFactory);
					}
				}
				else if (values[1].isConstant()) {
					if (values[1].getOffset() == 0) {
						result = values[0];
					}
					else {
						VarnodeOperation op = new VarnodeOperation(pcodeOp, values);
						result = combineLogicOrOperation(op, addrFactory);
					}
				}
				break;

			case PcodeOp.INT_AND:
				if ((values[1].isConstant() && values[1].getOffset() == 0) ||
					(values[0].isConstant() && values[0].getOffset() == 0)) {
					// Zero AND'd with anything is Zero
					result = new Varnode(addrFactory.getConstantAddress(0), output.getSize());
				}
				else if (values[1].isConstant() && values[0].isConstant()) {
					result =
						new Varnode(addrFactory.getConstantAddress(values[0].getOffset() &
							values[1].getOffset()), output.getSize());
				}
				else if (values[0].isConstant()) {
					result =
						simplifyWithIntAndMask(seq, values[1],
							getUnsignedOffset(values[0], output.getSize()), addrFactory, monitor);
				}
				else if (values[1].isConstant()) {
					result =
						simplifyWithIntAndMask(seq, values[0],
							getUnsignedOffset(values[1], output.getSize()), addrFactory, monitor);
				}
				break;

			case PcodeOp.INT_OR:
				if (values[0].equals(values[1])) {
					result = values[0];
				}
				else if (values[1].isConstant() && values[0].isConstant()) {
					result =
						new Varnode(addrFactory.getConstantAddress(values[0].getOffset() |
							values[1].getOffset()), output.getSize());
				}
				else if (values[0].isConstant()) {
					if (values[0].getOffset() == 0) {
						result = values[1];
					}
					else {
						VarnodeOperation op = flipInputs(pcodeOp, values);
						result = combineLogicOrOperation(op, addrFactory);
					}
				}
				else if (values[1].isConstant()) {
					if (values[1].getOffset() == 0) {
						result = values[0];
					}
					else {
						VarnodeOperation op = new VarnodeOperation(pcodeOp, values);
						result = combineLogicOrOperation(op, addrFactory);
					}
				}
				break;
			case PcodeOp.INT_LEFT:			// Left shift
				if (values[1].isConstant() && values[1].getOffset() < 64) {
					if (values[0].isConstant()) {
						result =
							new Varnode(
								addrFactory.getConstantAddress(values[0].getOffset() << values[1].getOffset()),
								output.getSize());
					}
					else {
						result = combineDoubleShiftOperation(pcodeOp, values, addrFactory);
					}
				}

				break;
			case PcodeOp.INT_RIGHT:	        // Right shift zero fill 
				if (values[1].isConstant() && values[1].getOffset() < 64) {
					if (values[0].isConstant()) {
						result =
							new Varnode(
								addrFactory.getConstantAddress(values[0].getOffset() >>> values[1].getOffset()),
								output.getSize());
					}
					else {
						result = combineDoubleShiftOperation(pcodeOp, values, addrFactory);
					}
				}
				break;

			case PcodeOp.INT_SRIGHT:       	// Signed right shift 
				if (values[1].isConstant() && values[0].isConstant() && values[1].getOffset() < 64) {
					result =
						new Varnode(
							addrFactory.getConstantAddress(values[0].getOffset() >> values[1].getOffset()),
							output.getSize());
				}
				break;

			case PcodeOp.INT_MULT:			// Integer multiplication 
				if (values[1].isConstant() && values[0].isConstant()) {
					result =
						new Varnode(addrFactory.getConstantAddress(values[0].getOffset() *
							values[1].getOffset()), output.getSize());
				}
				break;

			case PcodeOp.INT_DIV:			// Unsigned integer division
				if (values[1].isConstant() && values[0].isConstant()) {
					if (values[1].getOffset() == 0) {
						Msg.warn(ResultsState.class,
							"Divide by zero encounteerd during emulation at " +
								pcodeOp.getSeqnum().getTarget());
					}
					else {
						result =
							new Varnode(
								addrFactory.getConstantAddress(getUnsignedOffset(values[0],
									output.getSize()) /
									getUnsignedOffset(values[1], output.getSize())),
								output.getSize());
					}
				}
				break;

			case PcodeOp.INT_SDIV:			// Signed integer division
				if (values[1].isConstant() && values[0].isConstant()) {
					if (values[1].getOffset() == 0) {
						Msg.warn(ResultsState.class,
							"Divide by zero encounteerd during emulation at " +
								pcodeOp.getSeqnum().getTarget());
					}
					else {
						result =
							new Varnode(addrFactory.getConstantAddress(values[0].getOffset() /
								values[1].getOffset()), output.getSize());
					}
				}
				break;

			case PcodeOp.INT_REM:			// Unsigned mod (remainder)
				if (values[1].isConstant() && values[0].isConstant()) {
					if (values[1].getOffset() == 0) {
						Msg.warn(ResultsState.class,
							"Divide by zero encounteerd during emulation at " +
								pcodeOp.getSeqnum().getTarget());
					}
					else {
						result =
							new Varnode(
								addrFactory.getConstantAddress(getUnsignedOffset(values[0],
									output.getSize()) %
									getUnsignedOffset(values[1], output.getSize())),
								output.getSize());
					}
				}
				break;

			case PcodeOp.INT_SREM:			// Signed mod (remainder)
				if (values[1].isConstant() && values[0].isConstant()) {
					if (values[1].getOffset() == 0) {
						Msg.warn(ResultsState.class,
							"Divide by zero encounteerd during emulation at " +
								pcodeOp.getSeqnum().getTarget());
					}
					else {
						result =
							new Varnode(addrFactory.getConstantAddress(values[0].getOffset() %
								values[1].getOffset()), output.getSize());
					}
				}
				break;

			case PcodeOp.BOOL_NEGATE:		// Boolean negate or not
				if (values[0].isConstant()) {
					int b = values[0].getOffset() != 0 ? 0 : 1;
					result = new Varnode(addrFactory.getConstantAddress(b), 1);
				}
				else if (values[0] instanceof VarnodeOperation) {
					VarnodeOperation opVal = (VarnodeOperation) values[0];
					if (opVal.getPCodeOp().getOpcode() == PcodeOp.BOOL_NEGATE) {
						result = opVal.getInputValues()[0]; // double-negative case
					}
				}
				break;

			case PcodeOp.BOOL_XOR:			// Boolean xor
				if (values[0].equals(values[1])) {
					result = new Varnode(addrFactory.getConstantAddress(0), 1);
				}
				else if (values[1].isConstant() && values[0].isConstant()) {
					boolean v0 = (values[0].getOffset() != 0);
					boolean v1 = (values[1].getOffset() != 0);
					int b = (v0 != v1) ? 1 : 0;
					result = new Varnode(addrFactory.getConstantAddress(b), 1);
				}
				break;

			case PcodeOp.BOOL_AND:			// Boolean and (&&)
				if (values[0].isConstant()) {
					if (values[0].getOffset() == 0) {
						result = new Varnode(addrFactory.getConstantAddress(0), 1);
					}
					else if (values[1].isConstant()) {
						result =
							new Varnode(
								addrFactory.getConstantAddress(values[1].getOffset() == 0 ? 0 : 1),
								1);
					}
					else {
						result = values[1];
					}
				}
				else if (values[1].isConstant()) {
					if (values[1].getOffset() == 0) {
						result = new Varnode(addrFactory.getConstantAddress(0), 1);
					}
					else {
						result = values[0];
					}
				}
				break;

			case PcodeOp.BOOL_OR:			// Boolean or (||)
				if (values[0].isConstant() && values[0].getOffset() != 0) {
					result = new Varnode(addrFactory.getConstantAddress(1), 1);
				}
				else if (values[1].isConstant() && values[1].getOffset() != 0) {
					result = new Varnode(addrFactory.getConstantAddress(1), 1);
				}
				else if (values[1].isConstant() && values[0].isConstant()) {
					// both values are zero
					result = new Varnode(addrFactory.getConstantAddress(0), 1);
				}
				break;

			case PcodeOp.SUBPIECE:   		// Output is a subpiece of input0, input1=offset into input0
				if (values[0].isConstant()) {
					long val = getUnsignedOffset(values[0], values[0].getSize());
					val = (val >> (8 * values[1].getOffset())) & VALUE_MASK[output.getSize()];
					result = new Varnode(addrFactory.getConstantAddress(val), output.getSize());
				}
				else if (values[1].getOffset() == 0) {
					if (values[0].getSize() == output.getSize()) {
						// unnecessary SUBPIECE
						result = values[0];
					}
					else if (values[0] instanceof VarnodeOperation) {
						VarnodeOperation inputOp = (VarnodeOperation) values[0];
						PcodeOp inputPcodeOp = inputOp.getPCodeOp();
						Varnode[] subInputValues = inputOp.getInputValues();
						if (inputPcodeOp.getOpcode() == PcodeOp.INT_ZEXT &&
							subInputValues[0].getSize() == output.getSize()) {
							// Combine SUBPIECE and ZEXT
							result = subInputValues[0];
						}
					}
				}
				break;

			// floating point instructions:  No floating point data format is specified here,
			// although the exact operation of these instructions obviously depends on the
			// format.  For simulation, a "mode" variable specifying the floating point format
			// will be necessary.
			case PcodeOp.FLOAT_EQUAL:       // Return TRUE if operand1 == operand2    
			case PcodeOp.FLOAT_NOTEQUAL:	// Return TRUE if operand1 != operand2    
			case PcodeOp.FLOAT_LESS:   		// Return TRUE if op1 < op2 
			case PcodeOp.FLOAT_LESSEQUAL:	// Return TRUE if op1 <= op2
			case PcodeOp.FLOAT_NAN:			// Return TRUE if neither op1 is NaN 

			case PcodeOp.FLOAT_ADD:         // float addition
			case PcodeOp.FLOAT_DIV:         // float division
			case PcodeOp.FLOAT_MULT:        // float multiplication
			case PcodeOp.FLOAT_SUB:         // float subtraction
			case PcodeOp.FLOAT_NEG:         // float negation
			case PcodeOp.FLOAT_ABS:         // float absolute value
			case PcodeOp.FLOAT_SQRT:        // float square root

			case PcodeOp.FLOAT_INT2FLOAT:   // convert int type to float type
			case PcodeOp.FLOAT_FLOAT2FLOAT: // convert between float sizes
			case PcodeOp.FLOAT_TRUNC:       // round towards zero
			case PcodeOp.FLOAT_CEIL:        // round towards +infinity
			case PcodeOp.FLOAT_FLOOR:       // round towards -infinity
			case PcodeOp.FLOAT_ROUND:       // round towards nearest

		}

		if (result == null) {
			VarnodeOperation op = new VarnodeOperation(pcodeOp, values);
			op.setSimplified(true);
			result = op;
		}
		return result;
	}
Generate simplified operation
@param pcodeOp pcode operation
@param values values associated with pcodeOp inputs
@return operation output result or simplification of an operation.
Ghidra/Features/Base/src/main/java/ghidra/util/state/ResultsState.java