/*
 * 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.awk.nano.value;

import java.math.BigInteger;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

import net.morilib.awk.nano.AwkArithmeticException;
import net.morilib.awk.nano.AwkCastException;
import net.morilib.awk.nano.namespace.AwkNamespace;

/**
 * awkium値の基底クラスです。
 * 
 * @author MORIGUCHI, Yuichiro 2013/03
 */
public abstract class AwkValue {

	private static final BigInteger MINB =
		BigInteger.valueOf(Integer.MIN_VALUE);
	private static final BigInteger MAXB =
		BigInteger.valueOf(Integer.MAX_VALUE);

	/**
	 * 真の値です。
	 */
	public static final AwkValue TRUE = AwkInteger.valueOf(1);

	/**
	 * 偽の値です。
	 */
	public static final AwkValue FALSE = AwkInteger.valueOf(0);

	/**
	 * IOエラーの値です。
	 */
	public static final AwkValue IOERROR = AwkInteger.valueOf(-1);

	/**
	 * 整数に変換します。
	 * 
	 * @return 整数
	 */
	public BigInteger toInteger() {
//		throw new AwkCastException(this + " is not a number");
		return BigInteger.ZERO;
	}

	/**
	 * int型の整数に変換します。<br />
	 * int型であらわされないときは例外を返します。
	 * 
	 * @return int型の整数
	 * @throws AwkArithmeticException
	 */
	public int toIntExact() {
		if(!isIntValue()) {
			throw new AwkArithmeticException(
					"the integer is not small");
		}
		return toInteger().intValue();
	}

	/**
	 * 浮動小数に変換します。
	 * 
	 * @return 浮動小数
	 */
	public double toFloat() {
//		throw new AwkCastException(this + " is not a number");
		return 0.0;
	}

	/**
	 * 虚数部を得ます。
	 * 
	 * @return 浮動小数
	 */
	public double toImaginary() {
		return 0.0;
	}

	/**
	 * 文字列に変換します。
	 * 
	 * @param ns 名前空間
	 * @return 文字列
	 */
	public abstract String toString(AwkNamespace ns);

	/**
	 * 文字列に変換します。
	 * 
	 * @return 文字列
	 */
	public String toString() {
		return toString(null);
	}

	/**
	 * booleanに変換します。
	 * 
	 * @return 0のときfalse、そうでないときtrue
	 */
	public abstract boolean toBoolean(AwkNamespace ns);

	/**
	 * 正規表現に変換します。
	 * 
	 * @return 正規表現
	 */
	public Pattern toRegex(AwkNamespace ns) {
		if(ns.isIgnoreCase()) {
			return Pattern.compile(
					toString(ns), Pattern.CASE_INSENSITIVE);
		} else {
			return Pattern.compile(toString(ns));
		}
	}

	/**
	 * Javaオブジェクトに変換します。
	 * 
	 * @return Javaオブジェクト
	 */
	public abstract Object toObject();

	/**
	 * 配列の値を参照します。
	 * 
	 * @param v 添字
	 * @return 配列の値
	 */
	public AwkValue referArray(String v) {
		throw new AwkCastException(this + " is not an array");
	}

	/**
	 * 配列の値をおきます。
	 * 
	 * @param k 添字
	 * @param v 値
	 * @return 配列の前の値
	 */
	public AwkValue putArray(String k, AwkValue v) {
		throw new AwkCastException(this + " is not an array");
	}

	/**
	 * 配列の添字があるときtrueを得ます。
	 * 
	 * @param k 添字
	 * @return
	 */
	public boolean contains(String k) {
		throw new AwkCastException(this + " is not an array");
	}

	/**
	 * 添字に相当する配列を削除します。
	 * 
	 * @param v 添字
	 * @return 配列の前の値
	 */
	public AwkValue deleteArray(String v) {
		throw new AwkCastException(this + " is not an array");
	}

	/**
	 * 配列の添字の集合を取得します。
	 * 
	 * @return 添字の集合
	 */
	public Set<String> keys() {
		throw new AwkCastException(this + " is not an array");
	}

	/**
	 * 配列の値の集合を取得します。
	 * 
	 * @return 値の集合
	 */
	public Collection<AwkValue> values() {
		throw new AwkCastException(this + " is not an array");
	}

	/**
	 * 配列の添字のイテレータを取得します。
	 * 
	 * @return 添字のイテレータ
	 */
	public Iterator<AwkValue> iterator() {
		throw new AwkCastException(this + " is not an iterator");
	}

	/**
	 * 配列の添字と値の集合を取得します。
	 * 
	 * @return 添字と値の集合
	 */
	public Set<Map.Entry<String, AwkValue>> entrySet() {
		throw new AwkCastException(this + " is not an array");
	}

	/**
	 * 配列の添字と値のMapを取得します。
	 * 
	 * @return 添字と値のMap
	 */
	public Map<String, AwkValue> map() {
		throw new AwkCastException(this + " is not an array");
	}

	/**
	 * 配列をクリアします。
	 */
	public void clear() {
		throw new AwkCastException(this + " is not an array");
	}

	/**
	 * 配列のサイズを得ます。
	 * 
	 * @return サイズ
	 */
	public int size() {
		return 1;
	}

	/**
	 * 整数のときtrueを得ます。
	 * 
	 * @return
	 */
	public abstract boolean isInteger();

	/**
	 * 浮動小数のときtrueを得ます。
	 * 
	 * @return
	 */
	public abstract boolean isFloat();

	/**
	 * 実数のときtrueを得ます。
	 * 
	 * @return
	 */
	public abstract boolean isReal();

	/**
	 * 型が整数のときtrueを得ます。
	 * 
	 * @return
	 */
	public abstract boolean isIntegerValue();

	/**
	 * 型がint型整数のときtrueを得ます。
	 * 
	 * @return
	 */
	public boolean isIntValue() {
		return (isIntegerValue() &&
				toInteger().compareTo(MINB) > 0 &&
				toInteger().compareTo(MAXB) < 0);
	}

	/**
	 * 型が浮動小数のときtrueを得ます。
	 * 
	 * @return
	 */
	public abstract boolean isFloatValue();

	/**
	 * 型が実数のときtrueを得ます。
	 * 
	 * @return
	 */
	public abstract boolean isRealValue();

	/**
	 * 正数のときtrueを得ます。
	 * 
	 * @return
	 */
	public abstract boolean isPositiveValue();

	/**
	 * 0のときtrueを得ます。
	 * 
	 * @return
	 */
	public abstract boolean isZeroValue();

	/**
	 * 負数のときtrueを得ます。
	 * 
	 * @return
	 */
	public abstract boolean isNegativeValue();

	/**
	 * 文字列のときtrueを得ます。
	 * 
	 * @return
	 */
	public abstract boolean isString();

	/**
	 * 正規表現のときtrueを得ます。
	 * 
	 * @return
	 */
	public abstract boolean isRegex();

	/**
	 * 
	 * @return
	 */
	public abstract boolean isFunction();

	/**
	 * 空のときtrueを得ます。
	 * 
	 * @return
	 */
	public boolean isEmpty() {
		return false;
	}

	/**
	 * 配列のときtrueを得ます。
	 * 
	 * @return
	 */
	public boolean isArray() {
		return false;
	}

	/**
	 * 名前空間を得ます。
	 * 
	 * @return 名前空間
	 */
	public AwkNamespace getNamespace() {
		return null;
	}

	/**
	 * 値を比較します。
	 * 
	 * @param v  比較する値
	 * @param ns 名前空間
	 * @return このオブジェクトが大きいときは1、小さいときは-1、等しいときは0
	 */
	public abstract int compareTo(AwkValue v, AwkNamespace ns);

	/**
	 * 文字列を可能ならば数値に変換します。
	 * 
	 * @param s 文字列
	 * @return 値
	 */
	public static AwkValue cast(String s) {
		try {
			return AwkInteger.valueOf(new BigInteger(s));
		} catch(NumberFormatException e1) {
			try {
				return AwkFloat.valueOf(Double.parseDouble(s));
			} catch(NumberFormatException e2) {
				return AwkString.valueOf(s);
			}
		}
	}

	/**
	 * 複素数のときにtrueを得ます。
	 * 
	 * @return 複素数のときtrue
	 */
	public boolean isComplex() {
		return false;
	}

	/**
	 * Not a numberのときにtrueを得ます。
	 * 
	 * @return 複素数のときtrue
	 */
	public boolean isNaN() {
		return false;
	}

	/**
	 * 値が1のときtrueを得ます。
	 * 
	 * @return 値が1のときtrue
	 */
	public boolean isOne() {
		return (toImaginary() == 0.0 &&
				(toInteger().equals(BigInteger.ONE) ||
						toFloat() == 1.0));
	}

	/**
	 * 値が数のときtrueを得ます。
	 * 
	 * @return 値が数のときtrue
	 */
	public boolean isNumber() {
		return false;
	}

	/**
	 * 値を足します。
	 * 
	 * @param r1 値
	 * @param r2 値
	 * @return 結果
	 */
	public static AwkValue add(AwkValue r1, AwkValue r2) {
		AwkArray r0;
		String s;

		if(r1 instanceof AwkUndefined) {
			return r2;
		} else if(r2 instanceof AwkUndefined) {
			return r1;
		} else if(r1.isArray() && r2.isArray()) {
			r0 = new AwkArray(r1);
			for(Map.Entry<String, AwkValue> e : r2.entrySet()) {
				s = e.getKey();
				if(r1.contains(s)) {
					r0.putArray(s,
							add(r1.referArray(s), e.getValue()));
				} else {
					r0.putArray(s, e.getValue());
				}
			}
			return r0;
		} else if(r1.isArray()) {
			r0 = new AwkArray();
			for(Map.Entry<String, AwkValue> z : r1.entrySet()) {
				s = z.getKey();
				r0.putArray(s, add(z.getValue(), r2));
			}
			return r0;
		} else if(r2.isArray()) {
			r0 = new AwkArray();
			for(Map.Entry<String, AwkValue> z : r2.entrySet()) {
				s = z.getKey();
				r0.putArray(s, add(r1, z.getValue()));
			}
			return r0;
		} else if(r1.isInteger() && r2.isInteger()) {
			return AwkInteger.valueOf(
					r1.toInteger().add(r2.toInteger()));
		} else {
			return AwkFloat.valueOf(r1.toFloat() + r2.toFloat());
		}
	}

	/**
	 * 値を引きます。
	 * 
	 * @param r1 値
	 * @param r2 値
	 * @return 結果
	 */
	public static AwkValue sub(AwkValue r1, AwkValue r2) {
		AwkArray r0;
		String s;

		if(r1 instanceof AwkUndefined) {
			return sub(AwkInteger.ZERO, r2);
		} else if(r2 instanceof AwkUndefined) {
			return sub(r1, AwkInteger.ZERO);
		} else if(r1.isArray() && r2.isArray()) {
			r0 = new AwkArray(r1);
			for(Map.Entry<String, AwkValue> e : r2.entrySet()) {
				s = e.getKey();
				if(r1.contains(s)) {
					r0.putArray(s,
							sub(r1.referArray(s), e.getValue()));
				} else {
					r0.putArray(s, neg(e.getValue()));
				}
			}
			return r0;
		} else if(r1.isArray()) {
			r0 = new AwkArray();
			for(Map.Entry<String, AwkValue> z : r1.entrySet()) {
				s = z.getKey();
				r0.putArray(s, sub(z.getValue(), r2));
			}
			return r0;
		} else if(r2.isArray()) {
			r0 = new AwkArray();
			for(Map.Entry<String, AwkValue> z : r2.entrySet()) {
				s = z.getKey();
				r0.putArray(s, sub(r1, z.getValue()));
			}
			return r0;
		} else if(r1.isInteger() && r2.isInteger()) {
			return AwkInteger.valueOf(
					r1.toInteger().subtract(r2.toInteger()));
		} else {
			return AwkFloat.valueOf(r1.toFloat() - r2.toFloat());
		}
	}

	/**
	 * 値を掛けます。
	 * 
	 * @param r1 値
	 * @param r2 値
	 * @return 結果
	 */
	public static AwkValue mul(AwkValue r1, AwkValue r2) {
		AwkArray r0;
		String s;

		if(r1 instanceof AwkUndefined) {
			return mul(AwkInteger.ZERO, r2);
		} else if(r2 instanceof AwkUndefined) {
			return mul(r1, AwkInteger.ZERO);
		} else if(r1.isArray() && r2.isArray()) {
			r0 = new AwkArray(r1);
			for(Map.Entry<String, AwkValue> z : r2.entrySet()) {
				s = z.getKey();
				if(r1.contains(s)) {
					r0.putArray(s,
							mul(r1.referArray(s), z.getValue()));
				} else if(z.getValue().isFloat()) {
					r0.putArray(s, AwkFloat.valueOf(0.0));
				} else {
					r0.putArray(s, AwkInteger.ZERO);
				}
			}

			for(Map.Entry<String, AwkValue> z : r1.entrySet()) {
				s = z.getKey();
				if(!r2.contains(s)) {
					r0.putArray(s, AwkInteger.ZERO);
				}
			}
			return r0;
		} else if(r1.isArray()) {
			r0 = new AwkArray();
			for(Map.Entry<String, AwkValue> z : r1.entrySet()) {
				s = z.getKey();
				r0.putArray(s, mul(z.getValue(), r2));
			}
			return r0;
		} else if(r2.isArray()) {
			r0 = new AwkArray();
			for(Map.Entry<String, AwkValue> z : r2.entrySet()) {
				s = z.getKey();
				r0.putArray(s, mul(r1, z.getValue()));
			}
			return r0;
		} else if(r1.isInteger() && r2.isInteger()) {
			return AwkInteger.valueOf(
					r1.toInteger().multiply(r2.toInteger()));
		} else {
			return AwkFloat.valueOf(r1.toFloat() * r2.toFloat());
		}
	}

	/**
	 * 値を割ります。
	 * 
	 * @param r1 値
	 * @param r2 値
	 * @return 結果
	 */
	public static AwkValue div(AwkValue r1, AwkValue r2) {
		BigInteger e, f;
		BigInteger[] g;
		AwkArray r0;
		String s;

		if(r1 instanceof AwkUndefined) {
			return div(AwkInteger.ZERO, r2);
		} else if(r2 instanceof AwkUndefined) {
			return div(r1, AwkInteger.ZERO);
		} else if(r1.isArray() && r2.isArray()) {
			r0 = new AwkArray(r1);
			for(Map.Entry<String, AwkValue> z : r2.entrySet()) {
				s = z.getKey();
				if(r1.contains(s)) {
					r0.putArray(s,
							div(r1.referArray(s), z.getValue()));
				} else if(z.getValue().isFloat()) {
					r0.putArray(s, AwkFloat.valueOf(0.0));
				} else {
					r0.putArray(s, AwkInteger.ZERO);
				}
			}

			for(Map.Entry<String, AwkValue> z : r1.entrySet()) {
				s = z.getKey();
				if(r2.contains(s)) {
					// do nothing
				} else if(z.getValue().isPositiveValue()) {
					r0.putArray(s, AwkFloat.valueOf(
							Double.POSITIVE_INFINITY));
				} else {
					r0.putArray(s, AwkFloat.valueOf(
							Double.NEGATIVE_INFINITY));
				}
			}
			return r0;
		} else if(r1.isArray()) {
			r0 = new AwkArray();
			for(Map.Entry<String, AwkValue> z : r1.entrySet()) {
				s = z.getKey();
				r0.putArray(s, div(z.getValue(), r2));
			}
			return r0;
		} else if(r2.isArray()) {
			r0 = new AwkArray();
			for(Map.Entry<String, AwkValue> z : r2.entrySet()) {
				s = z.getKey();
				r0.putArray(s, div(r1, z.getValue()));
			}
			return r0;
		} else if(r1.isInteger() && r2.isInteger()) {
			try {
				e = r1.toInteger();
				f = r2.toInteger();
				g = e.divideAndRemainder(f);
				if(g[1].signum() == 0) {
					return AwkInteger.valueOf(g[0]);
				}
			} catch(ArithmeticException z) {
				throw new AwkArithmeticException(z.getMessage());
			}
		} else if(r1.isFloat() || r2.isFloat()) {
			return AwkFloat.valueOf(r1.toFloat() / r2.toFloat());
		}
		return AwkFloat.valueOf(r1.toFloat() / r2.toFloat());
	}

	/**
	 * 値のべき乗をとります。
	 * 
	 * @param r1 値
	 * @param r2 値
	 * @return 結果
	 */
	public static AwkValue pow(AwkValue r1, AwkValue r2) {
//		BigInteger f;
		AwkArray r0;
		String s;

		if(r1 instanceof AwkUndefined && r2 instanceof AwkUndefined) {
			throw new ArithmeticException();
		} else if(r1 instanceof AwkUndefined) {
			return pow(AwkInteger.ZERO, r2);
		} else if(r2 instanceof AwkUndefined) {
			return pow(r1, AwkInteger.ZERO);
		} else if(r1.isArray() && r2.isArray()) {
			r0 = new AwkArray(r1);
			for(Map.Entry<String, AwkValue> z : r2.entrySet()) {
				s = z.getKey();
				if(r1.contains(s)) {
					r0.putArray(s,
							pow(r1.referArray(s), z.getValue()));
				} else {
					r0.putArray(s, pow(AwkInteger.ZERO, z.getValue()));
				}
			}

			for(Map.Entry<String, AwkValue> z : r1.entrySet()) {
				s = z.getKey();
				if(!r2.contains(s)) {
					r0.putArray(s, pow(z.getValue(), AwkInteger.ZERO));
				}
			}
			return r0;
		} else if(r1.isArray()) {
			r0 = new AwkArray();
			for(Map.Entry<String, AwkValue> z : r1.entrySet()) {
				s = z.getKey();
				r0.putArray(s, pow(z.getValue(), r2));
			}
			return r0;
		} else if(r2.isArray()) {
			r0 = new AwkArray();
			for(Map.Entry<String, AwkValue> z : r2.entrySet()) {
				s = z.getKey();
				r0.putArray(s, pow(r1, z.getValue()));
			}
			return r0;
		}
		return AwkFloat.valueOf(Math.pow(r1.toFloat(), r2.toFloat()));
	}

	/**
	 * 値の符号を変えます。
	 * 
	 * @param v 値
	 * @return 結果
	 */
	public static AwkValue neg(AwkValue v) {
		AwkArray r0;
		String s;

		if(v.isArray()) {
			r0 = new AwkArray();
			for(Map.Entry<String, AwkValue> z : v.entrySet()) {
				s = z.getKey();
				r0.putArray(s, neg(z.getValue()));
			}
			return r0;
		} else if(v.isInteger()) {
			return AwkInteger.valueOf(v.toInteger().negate());
		}
		return AwkFloat.valueOf(-v.toFloat());
	}

	/**
	 * 値を文字列連結します。
	 * 
	 * @param r1 値
	 * @param r2 値
	 * @param ns 名前空間
	 * @return 結果
	 */
	public static AwkValue concat(AwkValue r1, AwkValue r2,
			AwkNamespace ns) {
		AwkArray r0;
		String s;

		if(r1 instanceof AwkUndefined) {
			return r2;
		} else if(r2 instanceof AwkUndefined) {
			return r1;
		} else if(r1.isArray() && r2.isArray()) {
			r0 = new AwkArray(r1);
			for(Map.Entry<String, AwkValue> e : r2.entrySet()) {
				s = e.getKey();
				if(r1.contains(s)) {
					r0.putArray(s,
							concat(r1.referArray(s),
									e.getValue(), ns));
				} else {
					r0.putArray(s, e.getValue());
				}
			}
			return r0;
		} else if(r1.isArray()) {
			r0 = new AwkArray();
			for(Map.Entry<String, AwkValue> z : r1.entrySet()) {
				s = z.getKey();
				r0.putArray(s, concat(z.getValue(), r2, ns));
			}
			return r0;
		} else if(r2.isArray()) {
			r0 = new AwkArray();
			for(Map.Entry<String, AwkValue> z : r2.entrySet()) {
				s = z.getKey();
				r0.putArray(s, concat(r1, z.getValue(), ns));
			}
			return r0;
		} else {
			return AwkString.valueOf(
					r1.toString(ns) + r2.toString(ns));
		}
	}

	/**
	 * 値が等しいかを調べます。
	 * 
	 * @param r1 値
	 * @param r2 値
	 * @param ns 名前空間
	 * @return 結果
	 */
	public static AwkValue equals(AwkValue r1, AwkValue r2,
			AwkNamespace ns) {
		AwkArray a;
		AwkValue x;
		String s;

		if(r1 instanceof AwkUndefined) {
			a = new AwkArray();
			if(r2.isArray()) {
				for(Map.Entry<String, AwkValue> z : r2.entrySet()) {
					a.putArray(z.getKey(), AwkInteger.booleanOf(
							z.getValue().isEmpty()));
				}
				return a;
			} else {
				return AwkInteger.booleanOf(r2.isEmpty());
			}
		} else if(r2 instanceof AwkUndefined) {
			a = new AwkArray();
			if(r1.isArray()) {
				for(Map.Entry<String, AwkValue> z : r1.entrySet()) {
					a.putArray(z.getKey(), AwkInteger.booleanOf(
							z.getValue().isEmpty()));
				}
				return a;
			} else {
				return AwkInteger.booleanOf(r1.isEmpty());
			}
		} else if(r1.isArray() && r2.isArray()) {
			a = new AwkArray();
			for(Map.Entry<String, AwkValue> z : r2.entrySet()) {
				s = z.getKey();
				if(r1.contains(s)) {
					x = equals(r1.referArray(s), z.getValue(), ns);
					a.putArray(s, AwkInteger.booleanOf(
							x.toBoolean(ns)));
				} else {
					a.putArray(s, AwkInteger.booleanOf(
							z.getValue().isEmpty()));
				}
			}

			for(Map.Entry<String, AwkValue> z : r1.entrySet()) {
				s = z.getKey();
				if(!r2.contains(s)) {
					a.putArray(s, AwkInteger.booleanOf(
							z.getValue().isEmpty()));
				}
			}
			return a;
		} else if(r1.isArray()) {
			a = new AwkArray();
			for(Map.Entry<String, AwkValue> z : r1.entrySet()) {
				s = z.getKey();
				x = equals(r1.referArray(s), r2, ns);
				a.putArray(s, AwkInteger.booleanOf(x.toBoolean(ns)));
			}
			return a;
		} else if(r2.isArray()) {
			a = new AwkArray();
			for(Map.Entry<String, AwkValue> z : r2.entrySet()) {
				s = z.getKey();
				x = equals(r1, r2.referArray(s), ns);
				a.putArray(s, AwkInteger.booleanOf(x.toBoolean(ns)));
			}
			return a;
		} else {
			return AwkInteger.booleanOf(r1.compareTo(r2, ns) == 0);
		}
	}

	/**
	 * 値が等しくないかを調べます。
	 * 
	 * @param r1 値
	 * @param r2 値
	 * @param ns 名前空間
	 * @return 結果
	 */
	public static AwkValue ne(AwkValue r1, AwkValue r2,
			AwkNamespace ns) {
		AwkArray a;
		AwkValue x;
		String s;

		if(r1 instanceof AwkUndefined) {
			a = new AwkArray();
			if(r2.isArray()) {
				for(Map.Entry<String, AwkValue> z : r2.entrySet()) {
					a.putArray(z.getKey(), AwkInteger.booleanOf(
							!z.getValue().isEmpty()));
				}
				return a;
			} else {
				return AwkInteger.booleanOf(!r2.isEmpty());
			}
		} else if(r2 instanceof AwkUndefined) {
			a = new AwkArray();
			if(r1.isArray()) {
				for(Map.Entry<String, AwkValue> z : r1.entrySet()) {
					a.putArray(z.getKey(), AwkInteger.booleanOf(
							!z.getValue().isEmpty()));
				}
				return a;
			} else {
				return AwkInteger.booleanOf(!r1.isEmpty());
			}
		} else if(r1.isArray() && r2.isArray()) {
			a = new AwkArray();
			for(Map.Entry<String, AwkValue> z : r2.entrySet()) {
				s = z.getKey();
				if(r1.contains(s)) {
					x = ne(r1.referArray(s), z.getValue(), ns);
					a.putArray(s, AwkInteger.booleanOf(
							x.toBoolean(ns)));
				} else {
					a.putArray(s, AwkInteger.booleanOf(
							!z.getValue().isEmpty()));
				}
			}

			for(Map.Entry<String, AwkValue> z : r1.entrySet()) {
				s = z.getKey();
				if(!r2.contains(s)) {
					a.putArray(s, AwkInteger.booleanOf(
							!z.getValue().isEmpty()));
				}
			}
			return a;
		} else if(r1.isArray()) {
			a = new AwkArray();
			for(Map.Entry<String, AwkValue> z : r1.entrySet()) {
				s = z.getKey();
				x = ne(r1.referArray(s), r2, ns);
				a.putArray(s, AwkInteger.booleanOf(x.toBoolean(ns)));
			}
			return a;
		} else if(r2.isArray()) {
			a = new AwkArray();
			for(Map.Entry<String, AwkValue> z : r2.entrySet()) {
				s = z.getKey();
				x = ne(r1, r2.referArray(s), ns);
				a.putArray(s, AwkInteger.booleanOf(x.toBoolean(ns)));
			}
			return a;
		} else {
			return AwkInteger.booleanOf(r1.compareTo(r2, ns) != 0);
		}
	}

	/**
	 * 値が小さいかを調べます。
	 * 
	 * @param r1 値
	 * @param r2 値
	 * @param ns 名前空間
	 * @return 結果
	 */
	public static AwkValue lt(AwkValue r1, AwkValue r2,
			AwkNamespace ns) {
		AwkArray a;
		AwkValue x;
		String s;

		if(r1 instanceof AwkUndefined) {
			return lt(AwkInteger.ZERO, r2, ns);
		} else if(r2 instanceof AwkUndefined) {
			return lt(r1, AwkInteger.ZERO, ns);
		} else if(r1.isArray() && r2.isArray()) {
			a = new AwkArray();
			for(Map.Entry<String, AwkValue> z : r2.entrySet()) {
				s = z.getKey();
				if(r1.contains(s)) {
					x = lt(r1.referArray(s), z.getValue(), ns);
					a.putArray(s, AwkInteger.booleanOf(
							x.toBoolean(ns)));
				} else {
					a.putArray(s, AwkInteger.booleanOf(
							z.getValue().isPositiveValue()));
				}
			}

			for(Map.Entry<String, AwkValue> z : r1.entrySet()) {
				s = z.getKey();
				if(!r2.contains(s)) {
					a.putArray(s, AwkInteger.booleanOf(
							z.getValue().isNegativeValue()));
				}
			}
			return a;
		} else if(r1.isArray()) {
			a = new AwkArray();
			for(Map.Entry<String, AwkValue> z : r1.entrySet()) {
				s = z.getKey();
				x = lt(r1.referArray(s), r2, ns);
				a.putArray(s, AwkInteger.booleanOf(x.toBoolean(ns)));
			}
			return a;
		} else if(r2.isArray()) {
			a = new AwkArray();
			for(Map.Entry<String, AwkValue> z : r2.entrySet()) {
				s = z.getKey();
				x = lt(r1, r2.referArray(s), ns);
				a.putArray(s, AwkInteger.booleanOf(x.toBoolean(ns)));
			}
			return a;
		} else {
			return AwkInteger.booleanOf(r1.compareTo(r2, ns) < 0);
		}
	}

	/**
	 * 値が大きくないかを調べます。
	 * 
	 * @param r1 値
	 * @param r2 値
	 * @param ns 名前空間
	 * @return 結果
	 */
	public static AwkValue le(AwkValue r1, AwkValue r2,
			AwkNamespace ns) {
		AwkArray a;
		AwkValue x;
		String s;

		if(r1 instanceof AwkUndefined) {
			return le(AwkInteger.ZERO, r2, ns);
		} else if(r2 instanceof AwkUndefined) {
			return le(r1, AwkInteger.ZERO, ns);
		} else if(r1.isArray() && r2.isArray()) {
			a = new AwkArray();
			for(Map.Entry<String, AwkValue> z : r2.entrySet()) {
				s = z.getKey();
				if(r1.contains(s)) {
					x = le(r1.referArray(s), z.getValue(), ns);
					a.putArray(s, AwkInteger.booleanOf(
							x.toBoolean(ns)));
				} else {
					a.putArray(s, AwkInteger.booleanOf(
							!z.getValue().isNegativeValue()));
				}
			}

			for(Map.Entry<String, AwkValue> z : r1.entrySet()) {
				s = z.getKey();
				if(!r2.contains(s)) {
					a.putArray(s, AwkInteger.booleanOf(
							!z.getValue().isPositiveValue()));
				}
			}
			return a;
		} else if(r1.isArray()) {
			a = new AwkArray();
			for(Map.Entry<String, AwkValue> z : r1.entrySet()) {
				s = z.getKey();
				x = le(r1.referArray(s), r2, ns);
				a.putArray(s, AwkInteger.booleanOf(x.toBoolean(ns)));
			}
			return a;
		} else if(r2.isArray()) {
			a = new AwkArray();
			for(Map.Entry<String, AwkValue> z : r2.entrySet()) {
				s = z.getKey();
				x = le(r1, r2.referArray(s), ns);
				a.putArray(s, AwkInteger.booleanOf(x.toBoolean(ns)));
			}
			return a;
		} else {
			return AwkInteger.booleanOf(r1.compareTo(r2, ns) <= 0);
		}
	}

	/**
	 * 値が大きいかを調べます。
	 * 
	 * @param r1 値
	 * @param r2 値
	 * @param ns 名前空間
	 * @return 結果
	 */
	public static AwkValue gt(AwkValue r1, AwkValue r2,
			AwkNamespace ns) {
		AwkArray a;
		AwkValue x;
		String s;

		if(r1 instanceof AwkUndefined) {
			return gt(AwkInteger.ZERO, r2, ns);
		} else if(r2 instanceof AwkUndefined) {
			return gt(r1, AwkInteger.ZERO, ns);
		} else if(r1.isArray() && r2.isArray()) {
			a = new AwkArray();
			for(Map.Entry<String, AwkValue> z : r2.entrySet()) {
				s = z.getKey();
				if(r1.contains(s)) {
					x = gt(r1.referArray(s), z.getValue(), ns);
					a.putArray(s, AwkInteger.booleanOf(
							x.toBoolean(ns)));
				} else {
					a.putArray(s, AwkInteger.booleanOf(
							z.getValue().isNegativeValue()));
				}
			}

			for(Map.Entry<String, AwkValue> z : r1.entrySet()) {
				s = z.getKey();
				if(!r2.contains(s)) {
					a.putArray(s, AwkInteger.booleanOf(
							z.getValue().isPositiveValue()));
				}
			}
			return a;
		} else if(r1.isArray()) {
			a = new AwkArray();
			for(Map.Entry<String, AwkValue> z : r1.entrySet()) {
				s = z.getKey();
				x = gt(r1.referArray(s), r2, ns);
				a.putArray(s, AwkInteger.booleanOf(x.toBoolean(ns)));
			}
			return a;
		} else if(r2.isArray()) {
			a = new AwkArray();
			for(Map.Entry<String, AwkValue> z : r2.entrySet()) {
				s = z.getKey();
				x = gt(r1, r2.referArray(s), ns);
				a.putArray(s, AwkInteger.booleanOf(x.toBoolean(ns)));
			}
			return a;
		} else {
			return AwkInteger.booleanOf(r1.compareTo(r2, ns) > 0);
		}
	}

	/**
	 * 値が小さくないかを調べます。
	 * 
	 * @param r1 値
	 * @param r2 値
	 * @param ns 名前空間
	 * @return 結果
	 */
	public static AwkValue ge(AwkValue r1, AwkValue r2,
			AwkNamespace ns) {
		AwkArray a;
		AwkValue x;
		String s;

		if(r1 instanceof AwkUndefined) {
			return ge(AwkInteger.ZERO, r2, ns);
		} else if(r2 instanceof AwkUndefined) {
			return ge(r1, AwkInteger.ZERO, ns);
		} else if(r1.isArray() && r2.isArray()) {
			a = new AwkArray();
			for(Map.Entry<String, AwkValue> z : r2.entrySet()) {
				s = z.getKey();
				if(r1.contains(s)) {
					x = ge(r1.referArray(s), z.getValue(), ns);
					a.putArray(s, AwkInteger.booleanOf(
							x.toBoolean(ns)));
				} else {
					a.putArray(s, AwkInteger.booleanOf(
							!z.getValue().isPositiveValue()));
				}
			}

			for(Map.Entry<String, AwkValue> z : r1.entrySet()) {
				s = z.getKey();
				if(!r2.contains(s)) {
					a.putArray(s, AwkInteger.booleanOf(
							!z.getValue().isNegativeValue()));
				}
			}
			return a;
		} else if(r1.isArray()) {
			a = new AwkArray();
			for(Map.Entry<String, AwkValue> z : r1.entrySet()) {
				s = z.getKey();
				x = ge(r1.referArray(s), r2, ns);
				a.putArray(s, AwkInteger.booleanOf(x.toBoolean(ns)));
			}
			return a;
		} else if(r2.isArray()) {
			a = new AwkArray();
			for(Map.Entry<String, AwkValue> z : r2.entrySet()) {
				s = z.getKey();
				x = ge(r1, r2.referArray(s), ns);
				a.putArray(s, AwkInteger.booleanOf(x.toBoolean(ns)));
			}
			return a;
		} else {
			return AwkInteger.booleanOf(r1.compareTo(r2, ns) >= 0);
		}
	}

}
