001: package gnu.kawa.functions;
002:
003: import gnu.math.*;
004: import gnu.mapping.*;
005: import gnu.bytecode.*;
006: import gnu.expr.*;
007: import java.math.*;
008:
009: /** This implements the numeric comparison relations: <, <=, etc. */
010:
011: public class NumberCompare extends ProcedureN implements CanInline,
012: Inlineable {
013: Language language;
014:
015: // Return codes from Numeric.compare:
016: static final int RESULT_GRT = 1;
017: static final int RESULT_EQU = 0;
018: static final int RESULT_LSS = -1;
019: static final int RESULT_NAN = -2;
020: static final int RESULT_NEQ = -3;
021:
022: // One flag bit for each of the above RESULT_XXX codes:
023: public static final int TRUE_IF_GRT = 1 << (RESULT_GRT + 3);
024: public static final int TRUE_IF_EQU = 1 << (RESULT_EQU + 3);
025: public static final int TRUE_IF_LSS = 1 << (RESULT_LSS + 3);
026: public static final int TRUE_IF_NAN = 1 << (RESULT_NAN + 3);
027: public static final int TRUE_IF_NEQ = 1 << (RESULT_NEQ + 3);
028: int flags;
029:
030: public int numArgs() {
031: return (-1 << 12) | 2;
032: }
033:
034: public static boolean $Eq(Object arg1, Object arg2) {
035: return apply2(TRUE_IF_EQU, arg1, arg2);
036: }
037:
038: public static boolean $Gr(Object arg1, Object arg2) {
039: return apply2(TRUE_IF_GRT, arg1, arg2);
040: }
041:
042: public static boolean $Gr$Eq(Object arg1, Object arg2) {
043: return apply2(TRUE_IF_GRT | TRUE_IF_EQU, arg1, arg2);
044: }
045:
046: public static boolean $Ls(Object arg1, Object arg2) {
047: return apply2(TRUE_IF_LSS, arg1, arg2);
048: }
049:
050: public static boolean $Ls$Eq(Object arg1, Object arg2) {
051: return apply2(TRUE_IF_LSS | TRUE_IF_EQU, arg1, arg2);
052: }
053:
054: public static boolean $Eq$V(Object arg1, Object arg2, Object arg3,
055: Object[] rest) {
056: return ($Eq(arg1, arg2) && $Eq(arg2, arg3) && (rest.length == 0 || ($Eq(
057: arg3, rest[0]) && applyN(TRUE_IF_EQU, rest))));
058: }
059:
060: public static boolean $Gr$V(Object arg1, Object arg2, Object arg3,
061: Object[] rest) {
062: return ($Gr(arg1, arg2) && $Gr(arg2, arg3) && (rest.length == 0 || ($Gr(
063: arg3, rest[0]) && applyN(TRUE_IF_GRT, rest))));
064: }
065:
066: public static boolean $Gr$Eq$V(Object arg1, Object arg2,
067: Object arg3, Object[] rest) {
068: return ($Gr$Eq(arg1, arg2) && $Gr$Eq(arg2, arg3) && (rest.length == 0 || ($Gr$Eq(
069: arg3, rest[0]) && applyN(TRUE_IF_GRT | TRUE_IF_EQU,
070: rest))));
071: }
072:
073: public static boolean $Ls$V(Object arg1, Object arg2, Object arg3,
074: Object[] rest) {
075: return ($Ls(arg1, arg2) && $Ls(arg2, arg3) && (rest.length == 0 || ($Ls(
076: arg3, rest[0]) && applyN(TRUE_IF_LSS, rest))));
077: }
078:
079: public static boolean $Ls$Eq$V(Object arg1, Object arg2,
080: Object arg3, Object[] rest) {
081: return ($Ls$Eq(arg1, arg2) && $Ls$Eq(arg2, arg3) && (rest.length == 0 || ($Ls$Eq(
082: arg3, rest[0]) && applyN(TRUE_IF_LSS | TRUE_IF_EQU,
083: rest))));
084: }
085:
086: public static NumberCompare make(Language language, String name,
087: int flags) {
088: NumberCompare proc = new NumberCompare();
089: proc.language = language;
090: proc.setName(name);
091: proc.flags = flags;
092: return proc;
093: }
094:
095: protected final Language getLanguage() {
096: return language;
097: }
098:
099: public Object apply2(Object arg1, Object arg2) {
100: return getLanguage().booleanObject(apply2(flags, arg1, arg2));
101: }
102:
103: static public boolean apply2(int flags, Object arg1, Object arg2) {
104: return ((1 << (3 + compare(arg1, arg2, true))) & flags) != 0;
105: }
106:
107: public static boolean checkCompareCode(int code, int flags) {
108: return ((1 << (3 + code)) & flags) != 0;
109: }
110:
111: static public boolean applyWithPromotion(int flags, Object arg1,
112: Object arg2) {
113: return checkCompareCode(compare(arg1, arg2, false), flags);
114: }
115:
116: /** Compare two numbers.
117: * @param exact true if we should compare exact/inexact numbers exactly
118: * (by converting the inexact number to exact), or inexactly (by
119: * "promoting" the exact to inexact) (as required for XQuery).
120: * @return 1 if {@code arg1>arg2}; 0 if {@code arg1==arg2};
121: * -1 if {@code arg1<arg2}; -2 if either is {@code NaN};
122: * -3 if not comparable (either is not a number). */
123: static public int compare(Object arg1, Object arg2, boolean exact) {
124: int code1 = Arithmetic.classifyValue(arg1);
125: int code2 = Arithmetic.classifyValue(arg2);
126: return compare(arg1, code1, arg2, code2, exact);
127: }
128:
129: static public int compare(Object arg1, int code1, Object arg2,
130: int code2, boolean exact) {
131: if (code1 < 0 || code2 < 0)
132: return -3;
133: int code = code1 < code2 ? code2 : code1;
134: int comp; // A Numeric.compare return code: -1, 0, 1, or rarely: -2, or -3.
135: switch (code) {
136: case Arithmetic.INT_CODE:
137: int i1 = Arithmetic.asInt(arg1);
138: int i2 = Arithmetic.asInt(arg2);
139: comp = i1 < i2 ? -1 : i1 > i2 ? 1 : 0;
140: break;
141: case Arithmetic.LONG_CODE:
142: long l1 = Arithmetic.asLong(arg1);
143: long l2 = Arithmetic.asLong(arg2);
144: comp = l1 < l2 ? -1 : l1 > l2 ? 1 : 0;
145: break;
146: case Arithmetic.BIGINTEGER_CODE:
147: BigInteger bi1 = Arithmetic.asBigInteger(arg1);
148: BigInteger bi2 = Arithmetic.asBigInteger(arg2);
149: comp = bi1.compareTo(bi2);
150: break;
151: case Arithmetic.INTNUM_CODE:
152: comp = IntNum.compare(Arithmetic.asIntNum(arg1), Arithmetic
153: .asIntNum(arg2));
154: break;
155: case Arithmetic.BIGDECIMAL_CODE:
156: BigDecimal bd1 = Arithmetic.asBigDecimal(arg1);
157: BigDecimal bd2 = Arithmetic.asBigDecimal(arg2);
158: comp = bd1.compareTo(bd2);
159: break;
160: case Arithmetic.RATNUM_CODE:
161: comp = RatNum.compare(Arithmetic.asRatNum(arg1), Arithmetic
162: .asRatNum(arg2));
163: break;
164: case Arithmetic.FLOAT_CODE:
165: if (!exact
166: || (code1 > Arithmetic.RATNUM_CODE && code2 > Arithmetic.RATNUM_CODE)) {
167: float f1 = Arithmetic.asFloat(arg1);
168: float f2 = Arithmetic.asFloat(arg2);
169: comp = f1 > f2 ? 1 : f1 < f2 ? -1 : f1 == f2 ? 0 : -2;
170: break;
171: }
172: // else fall through, to handle exact-inexact comparison
173: case Arithmetic.DOUBLE_CODE:
174: case Arithmetic.FLONUM_CODE:
175: if (!exact
176: || (code1 > Arithmetic.RATNUM_CODE && code2 > Arithmetic.RATNUM_CODE)) {
177: double d1 = Arithmetic.asDouble(arg1);
178: double d2 = Arithmetic.asDouble(arg2);
179: comp = d1 > d2 ? 1 : d1 < d2 ? -1 : d1 == d2 ? 0 : -2;
180: break;
181: }
182: // else fall through, to handle exact-inexact comparison
183: default:
184: Numeric num1 = Arithmetic.asNumeric(arg1);
185: Numeric num2 = Arithmetic.asNumeric(arg2);
186: comp = ((Numeric) num1).compare(num2);
187: }
188: return comp;
189: }
190:
191: static boolean applyN(int flags, Object[] args) {
192: // if (args.length < 2)
193: // throw new WrongArguments(this.name(),2,"(< x1 x2 ...)");
194: for (int i = 0; i < args.length - 1; i++) {
195: Object arg1 = args[i];
196: Object arg2 = args[i + 1];
197: if (!apply2(flags, arg1, arg2))
198: return false;
199: }
200: return true;
201: }
202:
203: public Object applyN(Object[] args) {
204: // if (args.length < 2)
205: // throw new WrongArguments(this.name(),2,"(< x1 x2 ...)");
206: return getLanguage().booleanObject(applyN(flags, args));
207: }
208:
209: public Expression inline(ApplyExp exp, ExpWalker walker) {
210: Expression folded = exp.inlineIfConstant(this , walker);
211: if (folded != exp)
212: return folded;
213: return exp;
214: }
215:
216: public void compile(ApplyExp exp, Compilation comp, Target target) {
217: Expression[] args = exp.getArgs();
218: if (args.length == 2) {
219: Expression arg0 = args[0];
220: Expression arg1 = args[1];
221: int kind0 = classify(arg0);
222: int kind1 = classify(arg1);
223: CodeAttr code = comp.getCode();
224: if (kind0 >= RealNum_KIND && kind1 >= RealNum_KIND
225: // Don't optimize if both operands are fractions.
226: && (kind0 != RealNum_KIND || kind1 != RealNum_KIND)) {
227: if (!(target instanceof ConditionalTarget)) {
228: IfExp.compile(exp, QuoteExp.trueExp,
229: QuoteExp.falseExp, comp, target);
230: return;
231: }
232: int mask = flags;
233: if (mask == TRUE_IF_NEQ)
234: mask = TRUE_IF_GRT | TRUE_IF_LSS;
235: if (kind0 >= IntNum_KIND && kind1 >= IntNum_KIND
236: && (kind0 < long_KIND || kind1 < long_KIND)) {
237: Type[] ctypes = new Type[2];
238: ctypes[0] = AddOp.typeIntNum;
239: if (kind1 >= long_KIND) {
240: ctypes[1] = Type.long_type;
241: } else if (kind0 >= long_KIND
242: // Simple check to avoid re-ordering side-effects.
243: && (arg0 instanceof QuoteExp
244: || arg1 instanceof QuoteExp
245: || arg0 instanceof ReferenceExp || arg1 instanceof ReferenceExp)) {
246: ctypes[1] = Type.long_type;
247: args = new Expression[2];
248: args[0] = arg1;
249: args[1] = arg0;
250: if (mask != TRUE_IF_EQU
251: && mask != TRUE_IF_GRT + TRUE_IF_LSS)
252: mask ^= TRUE_IF_GRT | TRUE_IF_LSS;
253: } else
254: ctypes[1] = AddOp.typeIntNum;
255: Method cmeth = AddOp.typeIntNum.getDeclaredMethod(
256: "compare", ctypes);
257: PrimProcedure compare = new PrimProcedure(cmeth);
258: arg0 = new ApplyExp(compare, args);
259: arg1 = new QuoteExp(IntNum.zero());
260: kind0 = kind1 = int_KIND;
261: }
262: Type commonType;
263: if (kind0 >= int_KIND && kind1 >= int_KIND)
264: commonType = Type.int_type;
265: else if (kind0 >= long_KIND && kind1 >= long_KIND)
266: commonType = Type.long_type;
267: else
268: commonType = Type.double_type;
269: StackTarget subTarget = new StackTarget(commonType);
270: ConditionalTarget ctarget = (ConditionalTarget) target;
271:
272: int opcode;
273: if (arg0 instanceof QuoteExp
274: && !(arg1 instanceof QuoteExp)) {
275: Expression tmp = arg1;
276: arg1 = arg0;
277: arg0 = tmp;
278: if (mask != TRUE_IF_EQU
279: && mask != TRUE_IF_GRT + TRUE_IF_LSS)
280: mask ^= TRUE_IF_GRT | TRUE_IF_LSS;
281: }
282: Label label1 = ctarget.trueBranchComesFirst ? ctarget.ifFalse
283: : ctarget.ifTrue;
284: if (ctarget.trueBranchComesFirst)
285: mask ^= TRUE_IF_GRT | TRUE_IF_LSS | TRUE_IF_EQU;
286: switch (mask) {
287: case TRUE_IF_GRT:
288: opcode = 157 /*ifgt*/;
289: break;
290: case TRUE_IF_EQU:
291: opcode = 153 /*ifeq*/;
292: break;
293: case TRUE_IF_LSS:
294: opcode = 155 /*iflt*/;
295: break;
296: case TRUE_IF_GRT | TRUE_IF_LSS:
297: opcode = 154 /*ifne*/;
298: break;
299: case TRUE_IF_GRT | TRUE_IF_EQU:
300: opcode = 156 /*ifge*/;
301: break;
302: case TRUE_IF_LSS | TRUE_IF_EQU:
303: opcode = 158 /*ifle*/;
304: break;
305: default:
306: opcode = 0;
307: }
308: arg0.compile(comp, subTarget);
309: Object value;
310: if (kind0 >= int_KIND
311: && kind1 >= int_KIND
312: && arg1 instanceof QuoteExp
313: && (value = ((QuoteExp) arg1).getValue()) instanceof IntNum
314: && ((IntNum) value).isZero()) {
315: code.emitGotoIfCompare1(label1, opcode);
316: } else {
317: arg1.compile(comp, subTarget);
318: code.emitGotoIfCompare2(label1, opcode);
319: }
320: ctarget.emitGotoFirstBranch(code);
321: return;
322: }
323: }
324: ApplyExp.compile(exp, comp, target);
325: }
326:
327: // Return a code indicate type of number:
328: private static final int Unknown_KIND = 0; // unknown or invalid type
329: private static final int Number_KIND = 1; // java.lang.Number - not used
330: private static final int Numeric_KIND = 2; // gnu.math.Numeric
331: private static final int RealNum_KIND = 3; // exact or unknown real
332: private static final int double_KIND = 4; // inexact real (double or DFloNum)
333: private static final int IntNum_KIND = 5; // gnu.math.IntNum
334: private static final int long_KIND = 6; // long
335: private static final int int_KIND = 7; // int
336:
337: static int classify(Expression exp) {
338: Type type = exp.getType();
339: int kind = classify(type);
340: Object value;
341: if (kind == IntNum_KIND
342: && exp instanceof QuoteExp
343: && (value = ((QuoteExp) exp).getValue()) instanceof IntNum) {
344: int ilength = ((IntNum) value).intLength();
345: if (ilength < 32)
346: return int_KIND;
347: if (ilength < 64)
348: return long_KIND;
349: }
350: return kind;
351: }
352:
353: static int classify(Type type) {
354: if (type instanceof PrimType) {
355: char sig = type.getSignature().charAt(0);
356: if (sig == 'V' || sig == 'Z' || sig == 'C')
357: return Unknown_KIND;
358: if (sig == 'D' || sig == 'F')
359: return double_KIND;
360: if (sig == 'J')
361: return long_KIND;
362: return int_KIND;
363: }
364: if (type.isSubtype(AddOp.typeIntNum))
365: return IntNum_KIND;
366: if (type.isSubtype(AddOp.typeDFloNum))
367: return double_KIND;
368: if (type.isSubtype(AddOp.typeRealNum))
369: return RealNum_KIND;
370: if (type.isSubtype(AddOp.typeNumeric))
371: return Numeric_KIND;
372: return Unknown_KIND;
373: }
374:
375: public Type getReturnType(Expression[] args) {
376: return Compilation.scmBooleanType;
377: }
378: }
|