/*
 * Copyright 2013 Yuichiro Moriguchi
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.morilib.dc;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import net.morilib.dc.number.PiMachinSeries;

/**
 * 
 *
 *
 * @author MORIGUCHI, Yuichiro 2013/07/14
 */
public final class DcInterpreter {

	static interface DcC extends DcCommand<DcModel> {
		// nothing
	}

	static final char SCALE = (char)0xff03;
	static final char IBASE = (char)0xff04;

	static final DcC PRINT = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) {
			Object o;

			o = env.pop();
			env.println(o);
			env.push(o);
		}

	};

	static final DcC PRINT2 = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) {
			Object o;

			o = env.pop();
			env.print(o);
		}

	};

	static final DcC PRINT_BYTE = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) throws IOException {
			Object o;

			o = env.pop();
			env.printAsByte(o);
		}

	};

	static final DcC PRINT_STACK = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) {
			env.printStackTrace();
		}

	};

	static final DcC ADD = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) {
			BigDecimal a, b;

			b = (BigDecimal)env.pop();
			a = (BigDecimal)env.pop();
			env.push(a.add(b));
		}

	};

	static final DcC SUB = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) {
			BigDecimal a, b;

			b = (BigDecimal)env.pop();
			a = (BigDecimal)env.pop();
			env.push(a.subtract(b));
		}

	};

	static final DcC MUL = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) {
			BigDecimal a, b;
//			int s;

			b = (BigDecimal)env.pop();
			a = (BigDecimal)env.pop();
//			s = Math.max(a.scale(), b.scale());
//			env.push(a.multiply(b).setScale(s, env.getRoundingMode()));
			env.push(a.multiply(b));
		}

	};

	static final DcC DIV = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) {
			BigDecimal a, b;

			b = ((BigDecimal)env.pop()).setScale(
					env.getScale(), env.getRoundingMode());
			a = ((BigDecimal)env.pop()).setScale(
					env.getScale(), env.getRoundingMode());
			parseDiv(p, env, a, b);
		}

	};

	static final DcC REM = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) {
			BigDecimal a, b;

			b = ((BigDecimal)env.pop()).setScale(
					env.getScale(), env.getRoundingMode());
			a = ((BigDecimal)env.pop()).setScale(
					env.getScale(), env.getRoundingMode());
			parseMod(p, env, a, b);
		}

	};

	static final DcC MOD = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) {
			BigDecimal a, b, d;

			b = (BigDecimal)env.pop();
			a = (BigDecimal)env.pop();
			if((d = a.remainder(b)).signum() < 0) {
				d = d.add(b);
			}
			env.push(d);
		}

	};

	static final DcC DIV_REM = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) {
			BigDecimal a, b;

			b = ((BigDecimal)env.pop()).setScale(
					env.getScale(), env.getRoundingMode());
			a = ((BigDecimal)env.pop()).setScale(
					env.getScale(), env.getRoundingMode());
			parseDiv(p, env, a, b);
			parseMod(p, env, a, b);
		}

	};

	static final DcC POW = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) {
			BigDecimal a, b;
			int z;

			b = (BigDecimal)env.pop();
			a = (BigDecimal)env.pop();
			if((z = b.intValue()) < 0) {
				env.pushConst(1);
				env.push(a.pow(-z));
				b = (BigDecimal)env.pop();
				a = (BigDecimal)env.pop();
				env.push(a.divide(b, env.getRoundingMode()));
			} else {
				env.push(a.pow(z));
			}
		}

	};

	static final DcC POW_MOD = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) {
			BigDecimal a, b, c;
			int z;

			c = (BigDecimal)env.pop();
			b = (BigDecimal)env.pop();
			a = (BigDecimal)env.pop();
			if((z = b.intValue()) < 0) {
				throw new ArithmeticException();
			} else if(z == 0) {
				env.pushConst(1);
			} else if(z == 1) {
				env.push(a);
			} else {
				for(int i = 2; i < z; i++) {
					a = a.multiply(a).remainder(c);
				}
				env.push(a);
			}
		}

	};

	static final DcC SQRT = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) {
			BigDecimal a;

			a = (BigDecimal)env.pop();
			env.push(DcMath.sqrt(a.setScale(env.getScale())));
		}

	};

	static final DcC CLEAR = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) {
			env.clear();
		}

	};

	static final DcC DUP = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) {
			env.duplicate();
		}

	};

	static final DcC EXCHANGE = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) {
			env.exchange();
		}

	};

	static final DcC SETREG = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) throws IOException {
			Object o;
			int c, x;

			if((c = rd.read()) < 0) {
				throw new DcSyntaxException();
			}

			o = env.pop();
			if(c == SCALE && o instanceof BigDecimal) {
				x = ((BigDecimal)o).intValue();
				if(x >= 0)  env.setScale(x);
			} else if(c == 'π') {
				throw new DcSyntaxException();
			} else {
				env.set(c, o);
			}
		}

	};

	static final DcC GETREG = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) throws IOException {
			int c;

			if((c = rd.read()) < 0) {
				throw new DcSyntaxException();
			} else if(c == SCALE) {
				env.pushConst(env.getScale());
			} else if(c == 'π') {
				env.push(PiMachinSeries.getPi(env.getScale(),
						env.getRoundingMode()));
			} else {
				env.push(env.peek(c));
			}
		}

	};

	static final DcC PUSHREG = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) throws IOException {
			Object o;
			int c;

			if((c = rd.read()) < 0) {
				throw new DcSyntaxException();
			}
			o = env.pop();
			env.push(c, o);
		}

	};

	static final DcC POPREG = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) throws IOException {
			int c;

			if((c = rd.read()) < 0) {
				throw new DcSyntaxException();
			}
			env.push(env.pop(c));
		}

	};

	static final DcC SET_I_RADIX = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) {
			BigDecimal d;

			d = (BigDecimal)env.pop();
			env.setInputRadix(d.intValueExact());
		}

	};

	static final DcC GET_I_RADIX = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) {
			env.pushConst(env.getInputRadix());
		}

	};

	static final DcC SET_O_RADIX = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) {
			BigDecimal d;

			d = (BigDecimal)env.pop();
			env.setOutputRadix(d.intValueExact());
		}

	};

	static final DcC GET_O_RADIX = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) {
			env.pushConst(env.getOutputRadix());
		}

	};

	static final DcC SET_SCALE = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) {
			BigDecimal d;

			d = (BigDecimal)env.pop();
			env.setScale(d.intValueExact());
		}

	};

	static final DcC GET_SCALE = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) {
			env.pushConst(env.getScale());
		}

	};

	static final DcC CHR_STR = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) {
			Object o;

			if((o = env.pop()) instanceof BigDecimal) {
				env.push(String.format("%c",
						(char)((BigDecimal)o).intValue()));
			} else if(o instanceof String && !o.equals("")) {
				env.push(((String)o).subSequence(0, 1));
			} else {
				env.push(o);
			}
		}

	};

	static final DcC EXEC_MACRO = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) throws IOException {
			Object o;

			o = env.pop();
//			rd.pushOrSetStringAsReader(o.toString());
			rd.pushStringAsReader(o.toString());
		}

	};

	static final DcC EXEC_LT = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) throws IOException {
			BigDecimal a, b;
			String m;
			int c;

			if((c = rd.read()) < 0) {
				throw new DcSyntaxException();
			}
			m = env.peek(c).toString();
			b = (BigDecimal)env.pop();
			a = (BigDecimal)env.pop();
			if(a.compareTo(b) < 0) {
//				rd.pushOrSetStringAsReader(m);
				rd.pushStringAsReader(m);
			}
		}

	};

	static final DcC EXEC_GE = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) throws IOException {
			BigDecimal a, b;
			String m;
			int c;

			if((c = rd.read()) < 0) {
				throw new DcSyntaxException();
			}
			m = env.peek(c).toString();
			b = (BigDecimal)env.pop();
			a = (BigDecimal)env.pop();
			if(a.compareTo(b) >= 0) {
//				rd.pushOrSetStringAsReader(m);
				rd.pushStringAsReader(m);
			}
		}

	};

	static final DcC EXEC_GT = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) throws IOException {
			BigDecimal a, b;
			String m;
			int c;

			if((c = rd.read()) < 0) {
				throw new DcSyntaxException();
			}
			m = env.peek(c).toString();
			b = (BigDecimal)env.pop();
			a = (BigDecimal)env.pop();
			if(a.compareTo(b) > 0) {
//				rd.pushOrSetStringAsReader(m);
				rd.pushStringAsReader(m);
			}
		}

	};

	static final DcC EXEC_LE = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) throws IOException {
			BigDecimal a, b;
			String m;
			int c;

			if((c = rd.read()) < 0) {
				throw new DcSyntaxException();
			}
			m = env.peek(c).toString();
			b = (BigDecimal)env.pop();
			a = (BigDecimal)env.pop();
			if(a.compareTo(b) <= 0) {
//				rd.pushOrSetStringAsReader(m);
				rd.pushStringAsReader(m);
			}
		}

	};

	static final DcC EXEC_EQ = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) throws IOException {
			BigDecimal a, b;
			String m;
			int c;

			if((c = rd.read()) < 0) {
				throw new DcSyntaxException();
			}
			m = env.peek(c).toString();
			b = (BigDecimal)env.pop();
			a = (BigDecimal)env.pop();
			if(a.compareTo(b) == 0) {
//				rd.pushOrSetStringAsReader(m);
				rd.pushStringAsReader(m);
			}
		}

	};

	static final DcC EXEC_NE = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) throws IOException {
			BigDecimal a, b;
			String m;
			int c;

			if((c = rd.read()) < 0) {
				throw new DcSyntaxException();
			}
			m = env.peek(c).toString();
			b = (BigDecimal)env.pop();
			a = (BigDecimal)env.pop();
			if(a.compareTo(b) != 0) {
//				rd.pushOrSetStringAsReader(m);
				rd.pushStringAsReader(m);
			}
		}

	};

	static final DcC READ_MACRO = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) throws IOException {
			Object o;

			o = env.readLine();
//			rd.pushOrSetStringAsReader(o.toString());
			rd.pushStringAsReader(o.toString());
		}

	};

	static final DcC QUIT = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) throws IOException {
			if(rd.getLevel() > 0) {
				rd.pop();
			} else {
				throw new DcQuitSignalException();
			}
		}

	};

	static final DcC QUIT_N = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) throws IOException {
			int n;

			n = ((BigDecimal)env.pop()).intValueExact();
//			if(rd.peek().isEof())  n--;
			for(int i = 0; i < n && rd.getLevel() >= 0; i++) {
				rd.pop();
			}
		}

	};

	static final DcC LENGTH = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) {
			BigDecimal d;
			Object o;
			int s;

			if((o = env.pop()) instanceof BigDecimal) {
				d = (BigDecimal)o;
				s = Math.abs(d.stripTrailingZeros().precision());
				env.pushConst(s);
			} else {
				env.pushConst(o.toString().length());
			}
		}

	};

	static final DcC LENGTH_2 = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) {
			BigDecimal d;
			Object o;

			if((o = env.pop()) instanceof BigDecimal) {
				d = (BigDecimal)o;
				env.pushConst(d.stripTrailingZeros().scale());
			} else {
				env.pushConst(0);
			}
		}

	};

	static final DcC DEPTH = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) {
			int d = env.getDepthOfStack();

			env.pushConst(d);
		}

	};

	static final DcC SETROUND = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) {
			switch(((BigDecimal)env.pop()).intValue()) {
			case -1:
				env.setRoundingMode(RoundingMode.DOWN);
				return;
			case  1:
				env.setRoundingMode(RoundingMode.UP);
				return;
			case  0:
				env.setRoundingMode(RoundingMode.HALF_UP);
				return;
			case -2:
				env.setRoundingMode(RoundingMode.FLOOR);
				return;
			case  2:
				env.setRoundingMode(RoundingMode.CEILING);
				return;
			case -3:
				env.setRoundingMode(RoundingMode.HALF_DOWN);
				return;
			case  3:
				env.setRoundingMode(RoundingMode.HALF_EVEN);
				return;
			}
		}

	};

	static final DcC L_NOT = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) {
			BigDecimal b;

			b = (BigDecimal)env.pop();
			env.pushConst(b.signum() != 0 ? 0 : 1);
		}

	};

	static final DcC L_AND = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) {
			BigDecimal a, b;

			b = (BigDecimal)env.pop();
			a = (BigDecimal)env.pop();
			env.pushConst(a.signum() != 0 && b.signum() != 0 ? 1 : 0);
		}

	};

	static final DcC L_OR = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) {
			BigDecimal a, b;

			b = (BigDecimal)env.pop();
			a = (BigDecimal)env.pop();
			env.pushConst(a.signum() != 0 || b.signum() != 0 ? 1 : 0);
		}

	};

	static final DcC L_LT = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) {
			BigDecimal a, b;

			b = (BigDecimal)env.pop();
			a = (BigDecimal)env.pop();
			env.pushConst(a.compareTo(b) < 0 ? 1 : 0);
		}

	};

	static final DcC L_LE = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) {
			BigDecimal a, b;

			b = (BigDecimal)env.pop();
			a = (BigDecimal)env.pop();
			env.pushConst(a.compareTo(b) <= 0 ? 1 : 0);
		}

	};

	static final DcC L_GT = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) {
			BigDecimal a, b;

			b = (BigDecimal)env.pop();
			a = (BigDecimal)env.pop();
			env.pushConst(a.compareTo(b) > 0 ? 1 : 0);
		}

	};

	static final DcC L_GE = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) {
			BigDecimal a, b;

			b = (BigDecimal)env.pop();
			a = (BigDecimal)env.pop();
			env.pushConst(a.compareTo(b) >= 0 ? 1 : 0);
		}

	};

	static final DcC L_EQ = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) {
			BigDecimal a, b;

			b = (BigDecimal)env.pop();
			a = (BigDecimal)env.pop();
			env.pushConst(a.compareTo(b) == 0 ? 1 : 0);
		}

	};

	static final DcC L_NE = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) {
			BigDecimal a, b;

			b = (BigDecimal)env.pop();
			a = (BigDecimal)env.pop();
			env.pushConst(a.compareTo(b) != 0 ? 1 : 0);
		}

	};

	static final DcC EXEC_TRI = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) throws IOException {
			BigDecimal a;
			String b, c;

			c = (String)env.pop();
			b = (String)env.pop();
			a = (BigDecimal)env.pop();
			if(a.signum() != 0) {
//				rd.pushOrSetStringAsReader(b);
				rd.pushStringAsReader(b);
			} else {
//				rd.pushOrSetStringAsReader(c);
				rd.pushStringAsReader(c);
			}
		}

	};

	static final DcC EXEC_FOR = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) throws IOException {
			String a, b, c;

			c = (String)env.pop();
			b = (String)env.pop();
			a = (String)env.pop();
			rd.pushStringAsReader(a);
			while(((BigDecimal)env.pop()).signum() != 0) {
				rd.pushStringAsReader(b);
				if(((BigDecimal)env.pop()).signum() > 0) {
					break;
				}
				rd.pushStringAsReader(c);
				rd.pushStringAsReader(a);
			}
		}

	};

	static final DcC HALT = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) throws IOException {
			throw new DcQuitSignalException();
		}

	};

	static final DcC POP = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) throws IOException {
			env.pop();
		}

	};

	static final DcC PRINTALL = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) throws IOException {
			List<String> l = new ArrayList<String>();
			int x;

			x = ((BigDecimal)env.pop()).intValue();
			for(int i = 0; i < x; i++)  l.add(env.pop().toString());
			for(int i = l.size() - 1; i >= 0; i--) {
				env.print(l.get(i));
			}
		}

	};

	static final DcC IRR = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) throws IOException {
			double y = Double.POSITIVE_INFINITY;
			double[] d, f;
			BigDecimal a;
			int x;

			x = ((BigDecimal)env.pop()).intValue();
			d = new double[x];
			for(int i = 0; i < x; i++) {
				d[i] = ((BigDecimal)env.pop()).doubleValue();
			}

			f = Polynomial1Solver.solveAll(d);
			for(int i = 0; i < f.length; i++) {
				if(Math.abs(f[i] - 1) < y) {
					y = Math.abs(f[i] - 1);
				}
			}

			if(Double.isInfinite(y)) {
				throw new ArithmeticException();
			}
			a = BigDecimal.valueOf(y);
			a = a.setScale(2, env.getRoundingMode());
			env.push(a);
		}

	};

	static final DcC TENTOTHE = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) throws IOException {
			int x;

			x = ((BigDecimal)env.pop()).intValue();
			env.push(BigDecimal.ONE.scaleByPowerOfTen(x));
		}

	};

	static final DcC ERROR = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) throws IOException {
			throw new ArithmeticException(env.pop().toString());
		}

	};

	static final DcC EXEC_MACRO2 = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) throws IOException {
			Object o;

			o = env.pop();
			rd.pushOrSetStringAsReader(o.toString());
		}

	};

	static final DcC EXEC_TRI2 = new DcC() {

		public void parse(DcParser<DcModel> p, DcModel env,
				DcReader rd, boolean tail) throws IOException {
			BigDecimal a;
			String b, c;

			c = (String)env.pop();
			b = (String)env.pop();
			a = (BigDecimal)env.pop();
			if(a.signum() != 0) {
				rd.pushOrSetStringAsReader(b);
			} else {
				rd.pushOrSetStringAsReader(c);
			}
		}

	};

	//
	private static void parseDiv(DcParser<DcModel> p, DcModel env,
			BigDecimal a, BigDecimal b) {
		BigDecimal d;

		d = a.divide(b, env.getRoundingMode());
		env.push(d.stripTrailingZeros());
	}

	//
	private static void parseMod(DcParser<DcModel> p, DcModel env,
			BigDecimal a, BigDecimal b) {
		BigDecimal d, x, y, q, r, t, z;
		int s;

//		env.push(a.remainder(b));
		x = a.setScale(env.getScale(), env.getRoundingMode());
		y = b.setScale(env.getScale(), env.getRoundingMode());
		d = x.divide(y, env.getRoundingMode());
		s = Math.max(env.getScale() + b.scale(), a.scale());
		q = a.setScale(s, env.getRoundingMode());
		r = d.setScale(s, env.getRoundingMode());
		t = b.setScale(s, env.getRoundingMode());
		z = q.subtract(r.multiply(t));
		env.push(z.stripTrailingZeros().setScale(env.getScale()));
	}

	/**
	 * 
	 * @return
	 */
	public static Map<Character, DcCommand<DcModel>> getInstance() {
		Map<Character, DcCommand<DcModel>> r;

		r = new HashMap<Character, DcCommand<DcModel>>();
		r.put('p', PRINT);
		r.put('n', PRINT2);
		r.put('P', PRINT_BYTE);
		r.put('f', PRINT_STACK);
		r.put('+', ADD);
		r.put('-', SUB);
		r.put('*', MUL);
		r.put('/', DIV);
		r.put('%', REM);
		r.put('~', DIV_REM);
		r.put('^', POW);
		r.put('|', POW_MOD);
		r.put('v', SQRT);
		r.put('c', CLEAR);
		r.put('d', DUP);
		r.put('r', EXCHANGE);
		r.put('s', SETREG);
		r.put('l', GETREG);
		r.put('S', PUSHREG);
		r.put('L', POPREG);
		r.put('i', SET_I_RADIX);
		r.put('I', GET_I_RADIX);
		r.put('o', SET_O_RADIX);
		r.put('O', GET_O_RADIX);
		r.put('k', SET_SCALE);
		r.put('K', GET_SCALE);
		r.put('a', CHR_STR);
		r.put('x', EXEC_MACRO);
		r.put('<', EXEC_LT);
		r.put('>', EXEC_GT);
		r.put('=', EXEC_EQ);
		r.put('小', EXEC_LE);
		r.put('大', EXEC_GE);
		r.put('不', EXEC_NE);
		r.put('?', READ_MACRO);
		r.put('q', QUIT);
		r.put('Q', QUIT_N);
		r.put('Z', LENGTH);
		r.put('X', LENGTH_2);
		r.put('z', DEPTH);

		r.put('R', SETROUND);
		r.put('N', L_NOT);
		r.put('＆', L_AND);
		r.put('｜', L_OR);
		r.put('＜', L_LT);
		r.put('≦', L_LE);
		r.put('＞', L_GT);
		r.put('≧', L_GE);
		r.put('＝', L_EQ);
		r.put('≠', L_NE);
		r.put('？', EXEC_TRI);
		r.put('迄', EXEC_FOR);
		r.put('刷', PRINTALL);
		r.put('停', HALT);
		r.put('捨', POP);
		r.put('利', IRR);
		r.put('万', TENTOTHE);
		r.put('誤', ERROR);
		r.put('法', MOD);
		r.put('走', EXEC_MACRO2);
		r.put('岐', EXEC_TRI2);
		return Collections.unmodifiableMap(r);
	}
}
