Various fixes related to literals and type inference. Move some utility methods over from optiq.
diff --git a/src/main/java/net/hydromatic/linq4j/expressions/ConstantExpression.java b/src/main/java/net/hydromatic/linq4j/expressions/ConstantExpression.java
index 20a40c1..abf482d 100644
--- a/src/main/java/net/hydromatic/linq4j/expressions/ConstantExpression.java
+++ b/src/main/java/net/hydromatic/linq4j/expressions/ConstantExpression.java
@@ -92,14 +92,18 @@
     final Primitive primitive = Primitive.of(type);
     if (primitive != null) {
       switch (primitive) {
+      case BYTE:
+        return writer.append("(byte)").append(value);
+      case CHAR:
+        return writer.append("(char)").append(value);
+      case SHORT:
+        return writer.append("(short)").append(value);
+      case LONG:
+        return writer.append(value).append("L");
       case FLOAT:
         return writer.append(value).append("F");
       case DOUBLE:
         return writer.append(value).append("D");
-      case LONG:
-        return writer.append(value).append("L");
-      case SHORT:
-        return writer.append("(short)").append(value);
       default:
         return writer.append(value);
       }
diff --git a/src/main/java/net/hydromatic/linq4j/expressions/Expressions.java b/src/main/java/net/hydromatic/linq4j/expressions/Expressions.java
index 9fc0f28..b934271 100644
--- a/src/main/java/net/hydromatic/linq4j/expressions/Expressions.java
+++ b/src/main/java/net/hydromatic/linq4j/expressions/Expressions.java
@@ -1391,15 +1391,79 @@
       type = Boolean.TYPE;
       break;
     default:
-      // curiously, "short + short" has type "int". Who knew?
-      type = left.getType() == short.class || left.getType() == Short.class
-          ? int.class
-          : left.getType();
+      type = larger(left.type, right.type);
       break;
     }
     return new BinaryExpression(binaryType, type, left, right);
   }
 
+  /** Returns an expression to box the value of a primitive expression.
+   * E.g. {@code box(e, Primitive.INT)} returns {@code Integer.valueOf(e)}. */
+  public static Expression box(Expression expression, Primitive primitive) {
+    return call(primitive.boxClass, "valueOf", expression);
+  }
+
+  /** Converts e.g. "anInteger" to "Integer.valueOf(anInteger)". */
+  public static Expression box(Expression expression) {
+    Primitive primitive = Primitive.of(expression.getType());
+    if (primitive == null) {
+      return expression;
+    }
+    return box(expression, primitive);
+  }
+
+  /** Returns an expression to unbox the value of a boxed-primitive expression.
+   * E.g. {@code unbox(e, Primitive.INT)} returns {@code e.intValue()}.
+   * It is assumed that e is of the right box type (or {@link Number})."Value */
+  public static Expression unbox(Expression expression, Primitive primitive) {
+    return call(expression, primitive.primitiveName + "Value");
+  }
+
+  /** Converts e.g. "anInteger" to "anInteger.intValue()". */
+  public static Expression unbox(Expression expression) {
+    Primitive primitive = Primitive.ofBox(expression.getType());
+    if (primitive == null) {
+      return expression;
+    }
+    return unbox(expression, primitive);
+  }
+
+  private Type largest(Type... types) {
+    Type max = types[0];
+    for (int i = 1; i < types.length; i++) {
+      max = larger(max, types[i]);
+    }
+    return max;
+  }
+
+  private static Type larger(Type type0, Type type1) {
+    // curiously, "short + short" has type "int".
+    // similarly, "byte + byte" has type "int".
+    // "byte / long" has type "long".
+    if (type0 == double.class
+        || type0 == Double.class
+        || type1 == double.class
+        || type1 == Double.class)
+    {
+      return double.class;
+    }
+    if (type0 == float.class
+        || type0 == Float.class
+        || type1 == float.class
+        || type1 == Float.class)
+    {
+      return float.class;
+    }
+    if (type0 == long.class
+        || type0 == Long.class
+        || type1 == long.class
+        || type1 == Long.class)
+    {
+      return long.class;
+    }
+    return int.class;
+  }
+
   /**
    * Creates a BinaryExpression, given the left operand, right
    * operand and implementing method, by calling the appropriate
@@ -2801,6 +2865,54 @@
     return new GotoExpression(GotoExpressionKind.Sequence, null, expression);
   }
 
+  /** Combines a list of expressions using AND. Returns TRUE if the list is
+   * empty. */
+  public static Expression foldAnd(List<Expression> conditions) {
+    Expression e = null;
+    for (Expression condition : conditions) {
+      if (condition instanceof ConstantExpression) {
+        if ((Boolean) ((ConstantExpression) condition).value) {
+          continue;
+        } else {
+          return constant(false);
+        }
+      }
+      if (e == null) {
+        e = condition;
+      } else {
+        e = andAlso(e, condition);
+      }
+    }
+    if (e == null) {
+      return constant(true);
+    }
+    return e;
+  }
+
+  /** Combines a list of expressions using OR. Returns FALSE if the list is
+   * empty. */
+  public static Expression foldOr(List<Expression> conditions) {
+    Expression e = null;
+    for (Expression condition : conditions) {
+      if (condition instanceof ConstantExpression) {
+        if ((Boolean) ((ConstantExpression) condition).value) {
+          return constant(true);
+        } else {
+          continue;
+        }
+      }
+      if (e == null) {
+        e = condition;
+      } else {
+        e = orElse(e, condition);
+      }
+    }
+    if (e == null) {
+      return constant(false);
+    }
+    return e;
+  }
+
   /**
    * Creates an empty fluent list.
    */
diff --git a/src/main/java/net/hydromatic/linq4j/expressions/Primitive.java b/src/main/java/net/hydromatic/linq4j/expressions/Primitive.java
index 6df860e..3d4fa2a 100644
--- a/src/main/java/net/hydromatic/linq4j/expressions/Primitive.java
+++ b/src/main/java/net/hydromatic/linq4j/expressions/Primitive.java
@@ -32,25 +32,22 @@
  * (e.g. {@link Integer}).</p>
  */
 public enum Primitive {
-  BOOLEAN(Boolean.TYPE, Boolean.class, 1, true, Boolean.FALSE, Boolean.TRUE),
-  BYTE(Byte.TYPE, Byte.class, 2, true, Byte.MIN_VALUE, Byte.MAX_VALUE),
-  CHAR(Character.TYPE, Character.class, 2, true, Character.MIN_VALUE,
+  BOOLEAN(Boolean.TYPE, Boolean.class, 1, Boolean.FALSE, Boolean.TRUE),
+  BYTE(Byte.TYPE, Byte.class, 2, Byte.MIN_VALUE, Byte.MAX_VALUE),
+  CHAR(Character.TYPE, Character.class, 2, Character.MIN_VALUE,
       Character.MAX_VALUE),
-  SHORT(Short.TYPE, Short.class, 2, true, Short.MIN_VALUE, Short.MAX_VALUE),
-  INT(Integer.TYPE, Integer.class, 2, true, Integer.MIN_VALUE,
-      Integer.MAX_VALUE),
-  LONG(Long.TYPE, Long.class, 2, true, Long.MIN_VALUE, Long.MAX_VALUE),
-  FLOAT(Float.TYPE, Float.class, 3, false, Float.MIN_VALUE, Float.MAX_VALUE),
-  DOUBLE(Double.TYPE, Double.class, 3, false, Double.MIN_VALUE,
-      Double.MAX_VALUE),
-  VOID(Void.TYPE, Void.class, 4, false, null, null),
-  OTHER(null, null, 5, false, null, null);
+  SHORT(Short.TYPE, Short.class, 2, Short.MIN_VALUE, Short.MAX_VALUE),
+  INT(Integer.TYPE, Integer.class, 2, Integer.MIN_VALUE, Integer.MAX_VALUE),
+  LONG(Long.TYPE, Long.class, 2, Long.MIN_VALUE, Long.MAX_VALUE),
+  FLOAT(Float.TYPE, Float.class, 3, Float.MIN_VALUE, Float.MAX_VALUE),
+  DOUBLE(Double.TYPE, Double.class, 3, Double.MIN_VALUE, Double.MAX_VALUE),
+  VOID(Void.TYPE, Void.class, 4, null, null),
+  OTHER(null, null, 5, null, null);
 
   public final Class primitiveClass;
   public final Class boxClass;
   public final String primitiveName; // e.g. "int"
   private final int family;
-  public final boolean fixed;
   public final Object min;
   public final Object max;
 
@@ -71,14 +68,13 @@
     }
   }
 
-  Primitive(Class primitiveClass, Class boxClass, int family, boolean fixed,
-      Object min, Object max) {
+  Primitive(Class primitiveClass, Class boxClass, int family, Object min,
+      Object max) {
     this.primitiveClass = primitiveClass;
     this.family = family;
     this.primitiveName =
         primitiveClass != null ? primitiveClass.getSimpleName() : null;
     this.boxClass = boxClass;
-    this.fixed = fixed;
     this.min = min;
     this.max = max;
   }
@@ -86,8 +82,10 @@
   /**
    * Returns the Primitive object for a given primitive class.
    *
-   * <p>For example, <code>of(Long.TYPE)</code> or <code>of(long.class)</code>
-   * returns {@link #LONG}.
+   * <p>For example, <code>of(long.class)</code> returns {@link #LONG}.
+   * Returns {@code null} when applied to a boxing or other class; for example
+   * <code>of(Long.class)</code> and <code>of(String.class)</code> return
+   * {@code null}.
    */
   public static Primitive of(Type type) {
     //noinspection SuspiciousMethodCalls
@@ -106,6 +104,20 @@
   }
 
   /**
+   * Returns the Primitive object for a given primitive or boxing class.
+   *
+   * <p>For example, <code>ofBoxOr(Long.class)</code> and
+   * <code>ofBoxOr(long.class)</code> both return {@link #LONG}.
+   */
+  public static Primitive ofBoxOr(Type type) {
+    Primitive primitive = of(type);
+    if (primitive == null) {
+      primitive = ofBox(type);
+    }
+    return primitive;
+  }
+
+  /**
    * Returns whether a given type is primitive.
    */
   public static boolean is(Type type) {
@@ -133,6 +145,35 @@
     }
   }
 
+  /** Returns whether this Primitive is a numeric type. */
+  public boolean isNumeric() {
+    // Per Java: Boolean and Character do not extend Number
+    switch (this) {
+    case BYTE:
+    case SHORT:
+    case INT:
+    case LONG:
+    case FLOAT:
+    case DOUBLE:
+      return true;
+    default:
+      return false;
+    }
+  }
+
+  /** Returns whether this Primitive is a fixed-point numeric type. */
+  public boolean isFixedNumeric() {
+    switch (this) {
+    case BYTE:
+    case SHORT:
+    case INT:
+    case LONG:
+      return true;
+    default:
+      return false;
+    }
+  }
+
   /**
    * Converts a primitive type to a boxed type; returns other types
    * unchanged.
diff --git a/src/main/java/net/hydromatic/linq4j/expressions/Types.java b/src/main/java/net/hydromatic/linq4j/expressions/Types.java
index 91e4e2a..ed01dbe 100644
--- a/src/main/java/net/hydromatic/linq4j/expressions/Types.java
+++ b/src/main/java/net/hydromatic/linq4j/expressions/Types.java
@@ -421,18 +421,16 @@
       //   Integer foo(BigDecimal o) {
       //     return o.intValue();
       //   }
-      return Expressions.call(expression,
-          Primitive.ofBox(returnType).primitiveName + "Value");
+      return Expressions.unbox(expression, Primitive.ofBox(returnType));
     }
     if (Primitive.is(returnType) && !Primitive.is(type)) {
       // E.g.
       //   int foo(Object o) {
-      //     return (int) (Integer) o;
+      //     return ((Integer) o).intValue();
       //   }
-      return Expressions.convert_(
-          Expressions.convert_(expression,
-              Types.box(returnType)),
-          returnType);
+      return Expressions.unbox(
+          Expressions.convert_(expression, Types.box(returnType)),
+          Primitive.of(returnType));
     }
     if (!Primitive.is(returnType) && Primitive.is(type)) {
       // E.g.
diff --git a/src/main/java/net/hydromatic/linq4j/function/Functions.java b/src/main/java/net/hydromatic/linq4j/function/Functions.java
index d55157f..d996a68 100644
--- a/src/main/java/net/hydromatic/linq4j/function/Functions.java
+++ b/src/main/java/net/hydromatic/linq4j/function/Functions.java
@@ -67,6 +67,13 @@
   private static final EqualityComparer<Object[]> ARRAY_COMPARER =
       new ArrayEqualityComparer();
 
+  private static final Function1 CONSTANT_NULL_FUNCTION1 =
+      new Function1() {
+        public Object apply(Object s) {
+          return null;
+        }
+      };
+
   @SuppressWarnings("unchecked")
   private static <K, V> Map<K, V> map(K k, V v, Object... rest) {
     final Map<K, V> map = new HashMap<K, V>();
@@ -85,6 +92,21 @@
     return inverseMap;
   }
 
+  /** Returns a 1-parameter function that always returns the same value. */
+  public static <T, R> Function1<T, R> constant(final R r) {
+    return new Function1<T, R>() {
+      public R apply(T s) {
+        return r;
+      }
+    };
+  }
+
+  /** Returns a 1-parameter function that always returns null. */
+  @SuppressWarnings("unchecked")
+  public static <T, R> Function1<T, R> constantNull() {
+    return CONSTANT_NULL_FUNCTION1;
+  }
+
   /**
    * A predicate with one parameter that always returns {@code true}.
    *
diff --git a/src/test/java/net/hydromatic/linq4j/test/ExpressionTest.java b/src/test/java/net/hydromatic/linq4j/test/ExpressionTest.java
index 5e61a4e..9face23 100644
--- a/src/test/java/net/hydromatic/linq4j/test/ExpressionTest.java
+++ b/src/test/java/net/hydromatic/linq4j/test/ExpressionTest.java
@@ -135,6 +135,65 @@
     assertEquals("lo w", s);
   }
 
+  public void testFoldAnd() {
+    // empty list yields true
+    final List<Expression> list0 = Collections.emptyList();
+    assertEquals(
+        "true",
+        Expressions.toString(
+            Expressions.foldAnd(list0)));
+    assertEquals(
+        "false",
+        Expressions.toString(
+            Expressions.foldOr(list0)));
+
+    final List<Expression> list1 =
+        Arrays.asList(
+            Expressions.equal(Expressions.constant(1), Expressions.constant(2)),
+            Expressions.equal(Expressions.constant(3), Expressions.constant(4)),
+            Expressions.constant(true),
+            Expressions.equal(Expressions.constant(5),
+                Expressions.constant(6)));
+    // true is eliminated from AND
+    assertEquals(
+        "1 == 2 && 3 == 4 && 5 == 6",
+        Expressions.toString(
+            Expressions.foldAnd(list1)));
+    // a single true makes OR true
+    assertEquals(
+        "true",
+        Expressions.toString(
+            Expressions.foldOr(list1)));
+
+    final List<Expression> list2 =
+        Collections.<Expression>singletonList(
+            Expressions.constant(true));
+    assertEquals(
+        "true",
+        Expressions.toString(
+            Expressions.foldAnd(list2)));
+    assertEquals(
+        "true",
+        Expressions.toString(
+            Expressions.foldOr(list2)));
+
+    final List<Expression> list3 =
+        Arrays.asList(
+            Expressions.equal(Expressions.constant(1), Expressions.constant(2)),
+            Expressions.constant(false),
+            Expressions.equal(Expressions.constant(5),
+                Expressions.constant(6)));
+    // false causes whole list to be false
+    assertEquals(
+        "false",
+        Expressions.toString(
+            Expressions.foldAnd(list3)));
+    assertEquals(
+        "1 == 2 || 5 == 6",
+        Expressions.toString(
+            Expressions.foldOr(list3)));
+  }
+
   public void testWrite() {
     assertEquals(
         "1 + 2.0F + 3L + Long.valueOf(4L)",
diff --git a/src/test/java/net/hydromatic/linq4j/test/Linq4jTest.java b/src/test/java/net/hydromatic/linq4j/test/Linq4jTest.java
index bc1b94e..2a634ae 100644
--- a/src/test/java/net/hydromatic/linq4j/test/Linq4jTest.java
+++ b/src/test/java/net/hydromatic/linq4j/test/Linq4jTest.java
@@ -1003,6 +1003,50 @@
             .toString());
   }
 
+  public void testList0() {
+    final List<Employee> employees = Arrays.asList(
+        new Employee(100, "Fred", 10),
+        new Employee(110, "Bill", 30),
+        new Employee(120, "Eric", 10),
+        new Employee(130, "Janet", 10));
+    final List<Employee> result = new ArrayList<Employee>();
+    Linq4j.asEnumerable(employees)
+        .where(
+            new Predicate1<Employee>() {
+              public boolean apply(Employee e) {
+                return e.name.contains("e");
+              }
+            })
+        .into(result);
+    assertEquals(
+        "[Employee(name: Fred, deptno:10), Employee(name: Janet, deptno:10)]",
+        result.toString());
+  }
+
+  public void testList() {
+    final List<Employee> employees = Arrays.asList(
+        new Employee(100, "Fred", 10),
+        new Employee(110, "Bill", 30),
+        new Employee(120, "Eric", 10),
+        new Employee(130, "Janet", 10));
+    final Map<Employee, Department> empDepts =
+        new HashMap<Employee, Department>();
+    for (Employee employee : employees) {
+      empDepts.put(employee, depts[(employee.deptno - 10) / 10]);
+    }
+    final List<Grouping<Object, Map.Entry<Employee, Department>>> result =
+        new ArrayList<Grouping<Object, Map.Entry<Employee, Department>>>();
+    Linq4j.asEnumerable(empDepts.entrySet())
+        .groupBy(
+            new Function1<Map.Entry<Employee, Department>, Object>() {
+              public Object apply(Map.Entry<Employee, Department> entry) {
+                return entry.getValue();
+              }
+            })
+        .into(result);
+    assertNotNull(result.toString());
+  }
+
   public static class Employee {
     public final int empno;
     public final String name;
diff --git a/src/test/java/net/hydromatic/linq4j/test/PrimitiveTest.java b/src/test/java/net/hydromatic/linq4j/test/PrimitiveTest.java
index 509f1c5..d0b67b8 100644
--- a/src/test/java/net/hydromatic/linq4j/test/PrimitiveTest.java
+++ b/src/test/java/net/hydromatic/linq4j/test/PrimitiveTest.java
@@ -70,6 +70,20 @@
     assertEquals(boolean[].class, Primitive.box(boolean[].class));
   }
 
+  public void testOfBox() {
+    assertEquals(Primitive.INT, Primitive.ofBox(Integer.class));
+    assertNull(Primitive.ofBox(int.class));
+    assertNull(Primitive.ofBox(String.class));
+    assertNull(Primitive.ofBox(Integer[].class));
+  }
+
+  public void testOfBoxOr() {
+    assertEquals(Primitive.INT, Primitive.ofBox(Integer.class));
+    assertNull(Primitive.ofBox(int.class));
+    assertNull(Primitive.ofBox(String.class));
+    assertNull(Primitive.ofBox(Integer[].class));
+  }
+
   /** Tests the {@link Primitive#number(Number)} method. */
   public void testNumber() {
     Number number = Primitive.SHORT.number(Integer.valueOf(2));