001: package gnu.xquery.util;
002:
003: import gnu.bytecode.*;
004: import gnu.mapping.*;
005: import gnu.expr.*;
006: import gnu.xml.TextUtils;
007: import gnu.kawa.xml.*;
008: import gnu.kawa.functions.*;
009: import java.math.*;
010: import gnu.math.*;
011:
012: public class ArithOp extends Procedure1or2 implements CanInline,
013: Inlineable {
014: char op;
015:
016: static final BigInteger TEN = BigInteger.valueOf(10);
017:
018: public static final ArithOp add = new ArithOp("+", '+', 2);
019: public static final ArithOp sub = new ArithOp("-", '-', 2);
020: public static final ArithOp mul = new ArithOp("*", '*', 2);
021: public static final ArithOp div = new ArithOp("div", 'd', 2);
022: public static final ArithOp idiv = new ArithOp("idiv", 'i', 2);
023: public static final ArithOp mod = new ArithOp("mod", 'm', 2);
024: public static final ArithOp plus = new ArithOp("+", 'P', 1);
025: public static final ArithOp minus = new ArithOp("-", 'M', 1);
026:
027: ArithOp(String name, char op, int nargs) {
028: super (name);
029: this .op = op;
030: }
031:
032: public Object apply1(Object arg1) throws java.lang.Throwable {
033: if (arg1 == Values.empty || arg1 == null)
034: return arg1;
035: if (arg1 instanceof KNode || arg1 instanceof UntypedAtomic)
036: arg1 = XDataType.doubleType.valueOf(TextUtils
037: .stringValue(arg1));
038: switch (op) {
039: case 'P':
040: return AddOp.apply2(1, gnu.math.IntNum.zero(), arg1);
041: case 'M':
042: int code1 = Arithmetic.classifyValue(arg1);
043: switch (code1) {
044: case Arithmetic.FLOAT_CODE:
045: return XDataType.makeFloat(-Arithmetic.asFloat(arg1));
046: case Arithmetic.DOUBLE_CODE:
047: return XDataType.makeDouble(-Arithmetic.asDouble(arg1));
048: default:
049: if (arg1 instanceof Numeric)
050: return ((Numeric) arg1).neg();
051: return AddOp.apply2(-1, gnu.math.IntNum.zero(), arg1);
052: }
053: }
054: throw new UnsupportedOperationException(getName());
055: }
056:
057: public static BigDecimal div(BigDecimal d1, BigDecimal d2) {
058: /* #ifdef JAVA5 */
059: // return d1.divide(d2, MathContext.DECIMAL128);
060: /* #else */
061: BigDecimal d = d1.divide(d2, 18, BigDecimal.ROUND_HALF_EVEN);
062: /* #ifdef JAVA2 */
063: BigInteger unscaled = d.unscaledValue();
064: if (unscaled.signum() == 0)
065: return BigDecimal.valueOf(0);
066: int sc = 0;
067: while (sc < 18) {
068: BigInteger[] divmod = unscaled.divideAndRemainder(TEN);
069: if (divmod[1].signum() != 0)
070: break;
071: sc++;
072: unscaled = divmod[0];
073: }
074: return sc == 0 ? d : new BigDecimal(unscaled, d.scale() - sc);
075: /* #endif */
076: /* #endif */
077: }
078:
079: public Object apply2(Object arg1, Object arg2)
080: throws java.lang.Throwable {
081: if (arg1 == Values.empty || arg1 == null)
082: return arg1;
083: if (arg2 == Values.empty || arg2 == null)
084: return arg2;
085: if (arg1 instanceof KNode || arg1 instanceof UntypedAtomic)
086: arg1 = XDataType.doubleType.valueOf(TextUtils
087: .stringValue(arg1));
088: if (arg2 instanceof KNode || arg2 instanceof UntypedAtomic)
089: arg2 = XDataType.doubleType.valueOf(TextUtils
090: .stringValue(arg2));
091: switch (op) {
092: case '+':
093: return AddOp.apply2(1, arg1, arg2);
094: case '-':
095: return AddOp.apply2(-1, arg1, arg2);
096: case '*':
097: return MultiplyOp.$St.apply2(arg1, arg2);
098: }
099: int code1 = Arithmetic.classifyValue(arg1);
100: int code2 = Arithmetic.classifyValue(arg2);
101: int code = code1 < code2 ? code2 : code1;
102: switch (op) {
103: case 'd': // 'div'
104: if (code1 < 0 || code2 < 0)
105: break;
106: else if (code <= Arithmetic.RATNUM_CODE) {
107: BigDecimal d1 = (BigDecimal) XDataType.decimalType
108: .cast(arg1);
109: // OR: d1 = Arithmetic.asBigDecimal(arg1);
110: BigDecimal d2 = (BigDecimal) XDataType.decimalType
111: .cast(arg2);
112: // OR: d2 = Arithmetic.asBigDecimal(arg2);
113: return div(d1, d2);
114: } else if (code == Arithmetic.FLOAT_CODE) {
115: return new Float(((Number) arg1).floatValue()
116: / ((Number) arg2).floatValue());
117: } else if (code == Arithmetic.DOUBLE_CODE) {
118: return new Double(((Number) arg1).doubleValue()
119: / ((Number) arg2).doubleValue());
120: } else if (arg1 instanceof Duration
121: && arg2 instanceof Duration) {
122: Duration dur1 = (Duration) arg1;
123: Duration dur2 = (Duration) arg2;
124: if (dur1.unit() == Unit.second
125: && dur2.unit() == Unit.second) {
126: long s1 = dur1.getTotalSeconds();
127: long s2 = dur2.getTotalSeconds();
128: int n1 = dur1.getNanoSecondsOnly();
129: int n2 = dur2.getNanoSecondsOnly();
130: BigDecimal sec1 = TimeUtils
131: .secondsBigDecimalFromDuration(s1, n1);
132: BigDecimal sec2 = TimeUtils
133: .secondsBigDecimalFromDuration(s2, n2);
134: return div(sec1, sec2);
135: }
136: if (dur1.unit() == Unit.month
137: && dur2.unit() == Unit.month) {
138: BigDecimal m1 = BigDecimal.valueOf(dur1
139: .getTotalMonths());
140: BigDecimal m2 = BigDecimal.valueOf(dur2
141: .getTotalMonths());
142: return div(m1, m2);
143: }
144: throw new ArithmeticException(
145: "divide of incompatible durations");
146: } else if (code >= 0)
147: return Arithmetic.asNumeric(arg1).div(
148: Arithmetic.asNumeric(arg2));
149: case 'i': // 'idiv'
150: if (code1 < 0 || code2 < 0)
151: break;
152: else if (code <= Arithmetic.INTNUM_CODE) {
153: IntNum i1 = Arithmetic.asIntNum(arg1);
154: IntNum i2 = Arithmetic.asIntNum(arg2);
155: return IntNum.quotient(i1, i2);
156: } else if (code <= Arithmetic.RATNUM_CODE) {
157: BigDecimal d1 = (BigDecimal) XDataType.decimalType
158: .cast(arg1);
159: // OR: d1 = Arithmetic.asBigDecimal(arg1);
160: BigDecimal d2 = (BigDecimal) XDataType.decimalType
161: .cast(arg2);
162: // OR: d2 = Arithmetic.asBigDecimal(arg2);
163: return Arithmetic.asIntNum(d1.divide(d2, 0,
164: BigDecimal.ROUND_DOWN));
165: } else if (code <= Arithmetic.FLOAT_CODE) {
166: float f = ((Number) arg1).floatValue()
167: / ((Number) arg2).floatValue();
168: return RealNum.toExactInt((double) f, RealNum.TRUNCATE);
169: } else {
170: double d = ((Number) arg1).doubleValue()
171: / ((Number) arg2).doubleValue();
172: return RealNum.toExactInt(d, RealNum.TRUNCATE);
173: }
174: case 'm': // 'mod'
175: if (code1 < 0 || code2 < 0)
176: break;
177: else if (code <= Arithmetic.INTNUM_CODE) {
178: IntNum i1 = Arithmetic.asIntNum(arg1);
179: IntNum i2 = Arithmetic.asIntNum(arg2);
180: return IntNum.remainder(i1, i2);
181: } else if (code <= Arithmetic.RATNUM_CODE) {
182: return sub.apply2(arg1, mul.apply2(idiv.apply2(arg1,
183: arg2), arg2));
184: } else if (code <= Arithmetic.FLOAT_CODE) {
185: float f1 = Arithmetic.asFloat(arg1);
186: float f2 = Arithmetic.asFloat(arg2);
187: return gnu.kawa.xml.XDataType.makeFloat(f1 % f2);
188: } else if (code <= Arithmetic.FLONUM_CODE) {
189: double d1 = Arithmetic.asDouble(arg1);
190: double d2 = Arithmetic.asDouble(arg2);
191: double d = d1 % d2;
192: if (code == Arithmetic.FLONUM_CODE)
193: return DFloNum.make(d);
194: else
195: return gnu.kawa.xml.XDataType.makeDouble(d);
196: }
197: }
198: throw new UnsupportedOperationException(getName());
199: }
200:
201: public Expression inline(ApplyExp exp, ExpWalker walker) {
202: // FUTURE
203: return exp;
204: }
205:
206: public void compile(ApplyExp exp, Compilation comp, Target target) {
207: // FUTURE
208: ApplyExp.compile(exp, comp, target);
209: }
210:
211: public Type getReturnType(Expression[] args) {
212: return Type.pointer_type;
213: }
214: }
|