
package polarDust.calculator;

/*
 * Copyright (c) 1996 Sorin Lazareanu, All Rights Reserved.
 *
 * Permission to use, copy, modify, and distribute this software
 * and its documentation for NON-COMMERCIAL purposes and without
 * fee is hereby granted provided that this copyright notice
 * appears in all copies.
 *
 */

/**
 *
 * This class is an equivalent of a microprocessor in a calculator (minus the
 * display logic). It executes commands and is capable of sending messages
 * to a display unit (panel).
 * Logic organization:<br>
 * 2 number registers (double): R1, R2,
 * 1 memory register (double): RM.
 *
 * This class can be further broken down into:<br>
 * a Command Control Unit (CCU),<br>
 * an Arithmetic and Logic Unit (ALU) and<br>
 * Registers
 *
 * <br><br><a href=../source/polarDust/calculator/Processor01.java>
 * The source code.</a>
 *
 * @version 0.9, 1996.06.04
 * @author Sorin Lazareanu
 */

public class Processor01 implements CalcCPU, CalcConstants {
	boolean bDigits;
	boolean bDecimalPoint;
	boolean bUnary;
	double dReg1, dReg2, dRegM;
	int iOpReg, iDecPlaces = 0;
	long lDecimalFactor = 1;
	CalcDisplay display;

	/**
	* Creates a new Processor01 object. Uses a display.
	* @see CalcDisplay
	*/
	public Processor01(CalcDisplay aDisplay) {
		display = aDisplay;
		c();
	}

	/**
	* Performs the calculations for a digit input.
	* @param iDigit 0...9
	*/
	public void digit(int iDigit) {
		double dBufferReg = dReg1;
		if (!display.isError()) {
			if (!bDigits) {
				dReg2 = dReg1;
				dBufferReg = 0d;
				opRegAssign(iOpReg);
			};
			if (bDecimalPoint) {
				if (lDecimalFactor <= display.maxDigitsFactor()) {
					if (dBufferReg >= 0d)
						dBufferReg = dBufferReg + iDigit/(double)lDecimalFactor;
					else
						dBufferReg = dBufferReg - iDigit/(double)lDecimalFactor;
					lDecimalFactor *= 10;
					iDecPlaces += 1;
				};
			}
			else {
				if (dBufferReg >= 0d) dBufferReg = dBufferReg * 10d + iDigit;
				else dBufferReg = dBufferReg * 10d - iDigit;
				iDecPlaces = 0;
			};
			reg1ShowWithDecimals(dBufferReg, iDecPlaces);
			bDigits = true;
			bDecimalPoint = bDecimalPoint;
			bUnary = false;
		}
	}

	/**
	* Inputs a decimal point.
	*/
	public void dot() {
		if (!display.isError()) {
			if (!bDecimalPoint) {
				lDecimalFactor = 10;
				iDecPlaces = 0;
			}
			bDigits = bDigits;
			bDecimalPoint = true;
			bUnary = false;
		}
	}

	/**
	* Toggles the sign of the current number (R1 = -R1).
	*/
	public void sgn() {
		if (!display.isError()) {
			if (bDecimalPoint && iDecPlaces >= 1) {
				reg1ShowWithDecimals(-dReg1, iDecPlaces);
			}
			else {
				reg1Show(-dReg1);
			}
		}
	}

	/**
	* Clear all registers.
	*/
	public void c() { mc(); reg1Show(0d); ce(); iOpReg = NUL; }

	/**
	* Memory register (RM) clear.
	*/
	public void mc() { regMAssign(0d); }

	/**
	* Enter or result (=sign ).
	*/
	public void equ() { result(EQU); }

	/**
	* Percent operator.
	* <br><br>For more information see <a href=../source/polarDust/calculator/Processor01.java>
	* the source code </a> method result.
	*/	
	public void prc() { result(PRC); }

	/**
	* Clear entry (R1 = 0).
	*/
	public void ce() { unary(CE); }

	/**
	* Memory read.
	*/
	public void mr() { unary(MR); }

	/**
	* Add to memory.
	*/
	public void madd() { unary(MADD); }

	/**
	* Substract from memory.
	*/
	public void msub() { unary(MSUB); }

	/**
	* Swap registers (R1 with R2).
	*/
	public void swap() { unary(SWAP); }

	/**
	* R1 = 1 / R1
	*/
	public void inv() { unary(INV); }

	/**
	* R1 = 3.14156...
	*/
	public void pi() { unary(VPI); }

	/**
	* R1 = 2.71828...
	*/
	public void e() { unary(VE); }

	/**
	* R1 = ln(R1)
	*/
	public void ln() { unary(LN); }

	/**
	* R1 = log(R1)
	*/
	public void log() { unary(LOG); }

	/**
	* R1 = e^R1
	*/
	public void exp() { unary(EXP); }

	/**
	* R1 = square_root(R1)
	*/
	public void sqrt() { unary(SQRT); }

	/**
	* R1 = R1 * R1
	*/
	public void sqr() { unary(SQR); }

	/**
	* R1 = sin(R1)
	*/
	public void sin() { unary(SIN); }

	/**
	* R1 = arcsin(R1)
	*/ 
	public void asin() { unary(ASIN); }

	/**
	* R1 = cos(R1)
	*/
	public void cos() { unary(COS); }

	/**
	* R1 = arccos(R1)
	*/
	public void acos() { unary(ACOS); }

	/**
	* R1 = tan(R1)
	*/
	public void tan() { unary(TAN); }

	/**
	* R1 = arctan(R1)
	*/
	public void atan() { unary(ATAN); }

	/**
	* R1 = R1 + R2
	*/
	public void add() { binary(ADD); }

	/**
	* R1 = R2 - R1
	*/
	public void sub() { binary(SUB); }

	/**
	* R1 = R1 * R2
	*/
	public void mul() { binary(MUL); }

	/**
	* R1 = R2 / R1
	*/
	public void div() { binary(DIV); }

	/**
	* R1 = R2 ^ R1
	*/
	public void pow() { binary(POW); }

	protected void unary(int iCurrentOp) {
		if (!display.isError()) {
			switch (iCurrentOp) {
			case CE: reg1Show(0d); break;
			case MR: reg1Show(dRegM); break;
			case VPI: reg1Show(Math.PI); break;
			case VE: reg1Show(Math.E); break;
			case SWAP:
				double dTemp;
				dTemp = dReg1;
				reg1Show(dReg2);
				dReg2 = dTemp;
				break;
			case SQRT: reg1Show(Math.sqrt(dReg1)); break;
			case INV: reg1Show(1d / dReg1); break;
			case LN: reg1Show(Math.log(dReg1)); break;
			case LOG: reg1Show(Math.log(dReg1) / Math.log(10)); break;
			case EXP: reg1Show(Math.exp(dReg1)); break;
			case SQR: reg1Show(dReg1 * dReg1); break;
			case SIN: reg1Show(Math.sin(toRad(dReg1))); break;
			case ASIN: reg1Show(toArc(Math.asin(toRad(dReg1)))); break;
			case COS: reg1Show(Math.cos(toRad(dReg1))); break;
			case ACOS: reg1Show(toArc(Math.acos(toRad(dReg1)))); break;
			case TAN: reg1Show(Math.tan(toRad(dReg1))); break;
			case ATAN: reg1Show(toArc(Math.atan(toRad(dReg1)))); break;
			case MADD: regMAssign(dRegM + dReg1); break;
			case MSUB: regMAssign(dRegM - dReg1);
			};
			bDigits = false;
			bDecimalPoint = false;
			bUnary = true;
		}
	}

	protected void binary(int iNextOp){
		if (!display.isError()) {
			if (bDigits || bUnary) {
				reg1Assign(dReg1);
				computeBinary();
				dReg2 = dReg1;
			}
			iOpReg = iNextOp;
			bDigits = false;
			bDecimalPoint = false;
			bUnary = false;
	 	}
	}

	protected void result(int iNextOp){
		if (!display.isError()) {
			if (!bDigits) opRegAssign(iOpReg);
			reg1Assign(dReg1);
			opRegAssign(iNextOp);
			switch (iNextOp) {
			case PRC:
				switch (iOpReg) {
				case MUL: reg1Show(dReg2 * dReg1 / 100); break;
				case DIV: reg1Show(dReg2 / (dReg1 / 100)); break;
				case ADD: reg1Show(dReg2 * (1 + dReg1 / 100)); break;
				case SUB: reg1Show(dReg2 * (1 - dReg1 / 100));
				}
				break;
			case EQU:
				computeBinary();
				break;
			}
		}
		if (!display.isError()) reg1Assign(dReg1);
		iOpReg = NUL;
		bDigits = false;
		bDecimalPoint = false;
		bUnary = false;
	 }

	private void computeBinary() {
		switch (iOpReg) {
		case MUL: reg1Show(dReg1 * dReg2); break;
		case DIV: reg1Show(dReg2 / dReg1); break;
		case ADD: reg1Show(dReg1 + dReg2); break;
		case SUB: reg1Show(dReg2 - dReg1); break;
		case POW: reg1Show(Math.pow(dReg2, dReg1));
	   	}
	}

	private double toRad(double dArg) {
		return dArg;
	}

	private double toArc(double dArg) {
		return dArg;
	}

	private protected void regMAssign(double dArg) {
		dRegM = display.showM(dArg);
	 }

	private protected void reg1ShowWithDecimals(double dArg, int iDecimals) {
		dReg1 = display.showWithDecimals(dArg, iDecimals);
	}

	private protected void reg1AssignWithDecimals(double dArg, int iDecimals) {
		dReg1 = display.assignWithDecimals(dArg, iDecimals);
	}

	private protected void reg1Show(double dArg) {
	 	dReg1 = display.show(dArg);
	}

	private protected void reg1Assign(double dArg) {
	 	dReg1 = display.assign(dArg);
	}

	private protected void opRegAssign(int iOperator) {
		Integer oper = new Integer(iOperator);

		if (CalcHashtable.containsKey(oper))
			display.showOp((String)CalcHashtable.get(oper));
	}

}


