diff --git a/cp-demo/Product.scala b/cp-demo/Product.scala
index 74b3d7980fd4693458a260038cc82084d5eef0e8..b06c633a6db31b1e6bc5fbed4f4a1ea72a1c10b3 100644
--- a/cp-demo/Product.scala
+++ b/cp-demo/Product.scala
@@ -7,6 +7,13 @@ object Product {
     val c2 = ((x: Int) => x > 41).c
 
     val c3 = c1 product1 c2
+
+    val c4 = ((a: Boolean, b: Int) => a == b > 0).c
+    val c5 = ((i: Int, j: Int) => i > j).c
+
+    val c6 = c4 product2 c5
+
     println(c3.solve)
+    println(c6.solve)
   }
 }
diff --git a/src/cp/Terms.scala b/src/cp/Terms.scala
index 41e8912d2e6d70fcf9122da251a4cbed88523652..760f07b35f5ec5517c22686f7cfb5acaec63cf35 100644
--- a/src/cp/Terms.scala
+++ b/src/cp/Terms.scala
@@ -143,6 +143,15 @@ object Terms {
   }
   */
 
+  trait Term0[R] extends Term[Unit,R] with Function0[R] {
+    val convertingFunction = converterOf(this).exprSeq2scala0 _
+    type t2c = (Term0[R]) => Term0[Boolean]
+    val scalaFunction : () => R
+    lazy val evaluator : (Seq[Expr]) => R = (s : Seq[Expr]) => scalaFunction()
+
+    override def apply() : R = scalaFunction()
+  }
+
   /** GENERATED CODE: */
   /** Type aliases for terms with boolean and integer range */
   type Constraint[T] = Term[T,Boolean]
@@ -157,7 +166,7 @@ object Terms {
   type Constraint8[T1,T2,T3,T4,T5,T6,T7,T8] = Term8[T1,T2,T3,T4,T5,T6,T7,T8,Boolean]
   type Constraint9[T1,T2,T3,T4,T5,T6,T7,T8,T9] = Term9[T1,T2,T3,T4,T5,T6,T7,T8,T9,Boolean]
   type IntTerm[T] = Term[T,Int]
-  type IntTerm0[T1] = Term0[Int]
+  type IntTerm0 = Term0[Int]
   type IntTerm1[T1] = Term1[T1,Int]
   type IntTerm2[T1,T2] = Term2[T1,T2,Int]
   type IntTerm3[T1,T2,T3] = Term3[T1,T2,T3,Int]
@@ -168,15 +177,6 @@ object Terms {
   type IntTerm8[T1,T2,T3,T4,T5,T6,T7,T8] = Term8[T1,T2,T3,T4,T5,T6,T7,T8,Int]
   type IntTerm9[T1,T2,T3,T4,T5,T6,T7,T8,T9] = Term9[T1,T2,T3,T4,T5,T6,T7,T8,T9,Int]
   
-  trait Term0[R] extends Term[Unit,R] with Function0[R] {
-    val convertingFunction = converterOf(this).exprSeq2scala0 _
-    type t2c = (Term0[R]) => Term0[Boolean]
-    val scalaFunction : () => R
-    lazy val evaluator : (Seq[Expr]) => R = (s : Seq[Expr]) => scalaFunction()
-
-    override def apply() : R = scalaFunction()
-  }
-
   trait Term1[T1,R] extends Term[(T1),R] with Function1[T1,R] {
     val convertingFunction = converterOf(this).exprSeq2scala1[T1] _
     type t2c = (Term1[T1,R]) => Term1[T1,Boolean]
@@ -190,9 +190,6 @@ object Terms {
   
     def &&(other : Term1[T1,Boolean])(implicit asBoolean : (R) => Boolean) : Term1[T1,Boolean] = 
       Term1(this.program, And(this.expr, other.expr), if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : T1) => this.scalaFunction(x_0) && other.scalaFunction(x_0), this.types, this.converter, this.lVars ++ other.lVars)
-
-    def +(other : Term1[T1,Int])(implicit isInt : R =:= Int) : Term1[T1,Int] = 
-      Term1(this.program, Plus(this.expr, other.expr), if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : T1) => this.scalaFunction(x_0) + other.scalaFunction(x_0), this.types, this.converter, this.lVars ++ other.lVars)
   
     def unary_!(implicit asBoolean : (R) => Boolean) : Term1[T1,Boolean] = 
       Term1(this.program, Not(this.expr), if (this.scalaFunction == null) null else (x_0 : T1) => ! this.scalaFunction(x_0), this.types, this.converter, this.lVars)
@@ -251,10 +248,52 @@ object Terms {
       val (newExpr, newTypes) = Terms.compose(other, this, 0, 9, 1)
       Term9(this.program, newExpr, if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : A1, x_1 : A2, x_2 : A3, x_3 : A4, x_4 : A5, x_5 : A6, x_6 : A7, x_7 : A8, x_8 : A9) => this.scalaFunction(other.scalaFunction(x_0, x_1, x_2, x_3, x_4, x_5, x_6, x_7, x_8)), newTypes, this.converter, this.lVars ++ other.lVars)
     }
-
+  
     def product1[A1](other: Term1[A1,Boolean])(implicit isBoolean: R =:= Boolean): Term2[T1,A1,Boolean] = {
       val (newExpr, newTypes) = Terms.product(this, other)
-      Term2(this.program, newExpr, if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0: T1, x_1: A1) => this.scalaFunction(x_0) && other.scalaFunction(x_1), newTypes, this.converter, this.lVars ++ other.lVars)
+      Term2(this.program, newExpr, if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : T1, x_1 : A1) => this.scalaFunction(x_0) && other.scalaFunction(x_1), newTypes, this.converter, this.lVars ++ other.lVars)
+    }
+    
+    
+    def product2[A1,A2](other: Term2[A1,A2,Boolean])(implicit isBoolean: R =:= Boolean): Term3[T1,A1,A2,Boolean] = {
+      val (newExpr, newTypes) = Terms.product(this, other)
+      Term3(this.program, newExpr, if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : T1, x_1 : A1, x_2 : A2) => this.scalaFunction(x_0) && other.scalaFunction(x_1, x_2), newTypes, this.converter, this.lVars ++ other.lVars)
+    }
+    
+    
+    def product3[A1,A2,A3](other: Term3[A1,A2,A3,Boolean])(implicit isBoolean: R =:= Boolean): Term4[T1,A1,A2,A3,Boolean] = {
+      val (newExpr, newTypes) = Terms.product(this, other)
+      Term4(this.program, newExpr, if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : T1, x_1 : A1, x_2 : A2, x_3 : A3) => this.scalaFunction(x_0) && other.scalaFunction(x_1, x_2, x_3), newTypes, this.converter, this.lVars ++ other.lVars)
+    }
+    
+    
+    def product4[A1,A2,A3,A4](other: Term4[A1,A2,A3,A4,Boolean])(implicit isBoolean: R =:= Boolean): Term5[T1,A1,A2,A3,A4,Boolean] = {
+      val (newExpr, newTypes) = Terms.product(this, other)
+      Term5(this.program, newExpr, if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : T1, x_1 : A1, x_2 : A2, x_3 : A3, x_4 : A4) => this.scalaFunction(x_0) && other.scalaFunction(x_1, x_2, x_3, x_4), newTypes, this.converter, this.lVars ++ other.lVars)
+    }
+    
+    
+    def product5[A1,A2,A3,A4,A5](other: Term5[A1,A2,A3,A4,A5,Boolean])(implicit isBoolean: R =:= Boolean): Term6[T1,A1,A2,A3,A4,A5,Boolean] = {
+      val (newExpr, newTypes) = Terms.product(this, other)
+      Term6(this.program, newExpr, if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : T1, x_1 : A1, x_2 : A2, x_3 : A3, x_4 : A4, x_5 : A5) => this.scalaFunction(x_0) && other.scalaFunction(x_1, x_2, x_3, x_4, x_5), newTypes, this.converter, this.lVars ++ other.lVars)
+    }
+    
+    
+    def product6[A1,A2,A3,A4,A5,A6](other: Term6[A1,A2,A3,A4,A5,A6,Boolean])(implicit isBoolean: R =:= Boolean): Term7[T1,A1,A2,A3,A4,A5,A6,Boolean] = {
+      val (newExpr, newTypes) = Terms.product(this, other)
+      Term7(this.program, newExpr, if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : T1, x_1 : A1, x_2 : A2, x_3 : A3, x_4 : A4, x_5 : A5, x_6 : A6) => this.scalaFunction(x_0) && other.scalaFunction(x_1, x_2, x_3, x_4, x_5, x_6), newTypes, this.converter, this.lVars ++ other.lVars)
+    }
+    
+    
+    def product7[A1,A2,A3,A4,A5,A6,A7](other: Term7[A1,A2,A3,A4,A5,A6,A7,Boolean])(implicit isBoolean: R =:= Boolean): Term8[T1,A1,A2,A3,A4,A5,A6,A7,Boolean] = {
+      val (newExpr, newTypes) = Terms.product(this, other)
+      Term8(this.program, newExpr, if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : T1, x_1 : A1, x_2 : A2, x_3 : A3, x_4 : A4, x_5 : A5, x_6 : A6, x_7 : A7) => this.scalaFunction(x_0) && other.scalaFunction(x_1, x_2, x_3, x_4, x_5, x_6, x_7), newTypes, this.converter, this.lVars ++ other.lVars)
+    }
+    
+    
+    def product8[A1,A2,A3,A4,A5,A6,A7,A8](other: Term8[A1,A2,A3,A4,A5,A6,A7,A8,Boolean])(implicit isBoolean: R =:= Boolean): Term9[T1,A1,A2,A3,A4,A5,A6,A7,A8,Boolean] = {
+      val (newExpr, newTypes) = Terms.product(this, other)
+      Term9(this.program, newExpr, if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : T1, x_1 : A1, x_2 : A2, x_3 : A3, x_4 : A4, x_5 : A5, x_6 : A6, x_7 : A7, x_8 : A8) => this.scalaFunction(x_0) && other.scalaFunction(x_1, x_2, x_3, x_4, x_5, x_6, x_7, x_8), newTypes, this.converter, this.lVars ++ other.lVars)
     }
   }
   
@@ -364,6 +403,47 @@ object Terms {
       val (newExpr, newTypes) = Terms.compose(other, this, 1, 8, 2)
       Term9(this.program, newExpr, if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : T1, x_1 : A1, x_2 : A2, x_3 : A3, x_4 : A4, x_5 : A5, x_6 : A6, x_7 : A7, x_8 : A8) => this.scalaFunction(x_0, other.scalaFunction(x_1, x_2, x_3, x_4, x_5, x_6, x_7, x_8)), newTypes, this.converter, this.lVars ++ other.lVars)
     }
+  
+    def product1[A1](other: Term1[A1,Boolean])(implicit isBoolean: R =:= Boolean): Term3[T1,T2,A1,Boolean] = {
+      val (newExpr, newTypes) = Terms.product(this, other)
+      Term3(this.program, newExpr, if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : T1, x_1 : T2, x_2 : A1) => this.scalaFunction(x_0, x_1) && other.scalaFunction(x_2), newTypes, this.converter, this.lVars ++ other.lVars)
+    }
+    
+    
+    def product2[A1,A2](other: Term2[A1,A2,Boolean])(implicit isBoolean: R =:= Boolean): Term4[T1,T2,A1,A2,Boolean] = {
+      val (newExpr, newTypes) = Terms.product(this, other)
+      Term4(this.program, newExpr, if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : T1, x_1 : T2, x_2 : A1, x_3 : A2) => this.scalaFunction(x_0, x_1) && other.scalaFunction(x_2, x_3), newTypes, this.converter, this.lVars ++ other.lVars)
+    }
+    
+    
+    def product3[A1,A2,A3](other: Term3[A1,A2,A3,Boolean])(implicit isBoolean: R =:= Boolean): Term5[T1,T2,A1,A2,A3,Boolean] = {
+      val (newExpr, newTypes) = Terms.product(this, other)
+      Term5(this.program, newExpr, if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : T1, x_1 : T2, x_2 : A1, x_3 : A2, x_4 : A3) => this.scalaFunction(x_0, x_1) && other.scalaFunction(x_2, x_3, x_4), newTypes, this.converter, this.lVars ++ other.lVars)
+    }
+    
+    
+    def product4[A1,A2,A3,A4](other: Term4[A1,A2,A3,A4,Boolean])(implicit isBoolean: R =:= Boolean): Term6[T1,T2,A1,A2,A3,A4,Boolean] = {
+      val (newExpr, newTypes) = Terms.product(this, other)
+      Term6(this.program, newExpr, if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : T1, x_1 : T2, x_2 : A1, x_3 : A2, x_4 : A3, x_5 : A4) => this.scalaFunction(x_0, x_1) && other.scalaFunction(x_2, x_3, x_4, x_5), newTypes, this.converter, this.lVars ++ other.lVars)
+    }
+    
+    
+    def product5[A1,A2,A3,A4,A5](other: Term5[A1,A2,A3,A4,A5,Boolean])(implicit isBoolean: R =:= Boolean): Term7[T1,T2,A1,A2,A3,A4,A5,Boolean] = {
+      val (newExpr, newTypes) = Terms.product(this, other)
+      Term7(this.program, newExpr, if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : T1, x_1 : T2, x_2 : A1, x_3 : A2, x_4 : A3, x_5 : A4, x_6 : A5) => this.scalaFunction(x_0, x_1) && other.scalaFunction(x_2, x_3, x_4, x_5, x_6), newTypes, this.converter, this.lVars ++ other.lVars)
+    }
+    
+    
+    def product6[A1,A2,A3,A4,A5,A6](other: Term6[A1,A2,A3,A4,A5,A6,Boolean])(implicit isBoolean: R =:= Boolean): Term8[T1,T2,A1,A2,A3,A4,A5,A6,Boolean] = {
+      val (newExpr, newTypes) = Terms.product(this, other)
+      Term8(this.program, newExpr, if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : T1, x_1 : T2, x_2 : A1, x_3 : A2, x_4 : A3, x_5 : A4, x_6 : A5, x_7 : A6) => this.scalaFunction(x_0, x_1) && other.scalaFunction(x_2, x_3, x_4, x_5, x_6, x_7), newTypes, this.converter, this.lVars ++ other.lVars)
+    }
+    
+    
+    def product7[A1,A2,A3,A4,A5,A6,A7](other: Term7[A1,A2,A3,A4,A5,A6,A7,Boolean])(implicit isBoolean: R =:= Boolean): Term9[T1,T2,A1,A2,A3,A4,A5,A6,A7,Boolean] = {
+      val (newExpr, newTypes) = Terms.product(this, other)
+      Term9(this.program, newExpr, if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : T1, x_1 : T2, x_2 : A1, x_3 : A2, x_4 : A3, x_5 : A4, x_6 : A5, x_7 : A6, x_8 : A7) => this.scalaFunction(x_0, x_1) && other.scalaFunction(x_2, x_3, x_4, x_5, x_6, x_7, x_8), newTypes, this.converter, this.lVars ++ other.lVars)
+    }
   }
   
   trait Term3[T1,T2,T3,R] extends Term[(T1,T2,T3),R] with Function3[T1,T2,T3,R] {
@@ -497,6 +577,41 @@ object Terms {
       val (newExpr, newTypes) = Terms.compose(other, this, 2, 7, 3)
       Term9(this.program, newExpr, if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : T1, x_1 : T2, x_2 : A1, x_3 : A2, x_4 : A3, x_5 : A4, x_6 : A5, x_7 : A6, x_8 : A7) => this.scalaFunction(x_0, x_1, other.scalaFunction(x_2, x_3, x_4, x_5, x_6, x_7, x_8)), newTypes, this.converter, this.lVars ++ other.lVars)
     }
+  
+    def product1[A1](other: Term1[A1,Boolean])(implicit isBoolean: R =:= Boolean): Term4[T1,T2,T3,A1,Boolean] = {
+      val (newExpr, newTypes) = Terms.product(this, other)
+      Term4(this.program, newExpr, if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : T1, x_1 : T2, x_2 : T3, x_3 : A1) => this.scalaFunction(x_0, x_1, x_2) && other.scalaFunction(x_3), newTypes, this.converter, this.lVars ++ other.lVars)
+    }
+    
+    
+    def product2[A1,A2](other: Term2[A1,A2,Boolean])(implicit isBoolean: R =:= Boolean): Term5[T1,T2,T3,A1,A2,Boolean] = {
+      val (newExpr, newTypes) = Terms.product(this, other)
+      Term5(this.program, newExpr, if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : T1, x_1 : T2, x_2 : T3, x_3 : A1, x_4 : A2) => this.scalaFunction(x_0, x_1, x_2) && other.scalaFunction(x_3, x_4), newTypes, this.converter, this.lVars ++ other.lVars)
+    }
+    
+    
+    def product3[A1,A2,A3](other: Term3[A1,A2,A3,Boolean])(implicit isBoolean: R =:= Boolean): Term6[T1,T2,T3,A1,A2,A3,Boolean] = {
+      val (newExpr, newTypes) = Terms.product(this, other)
+      Term6(this.program, newExpr, if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : T1, x_1 : T2, x_2 : T3, x_3 : A1, x_4 : A2, x_5 : A3) => this.scalaFunction(x_0, x_1, x_2) && other.scalaFunction(x_3, x_4, x_5), newTypes, this.converter, this.lVars ++ other.lVars)
+    }
+    
+    
+    def product4[A1,A2,A3,A4](other: Term4[A1,A2,A3,A4,Boolean])(implicit isBoolean: R =:= Boolean): Term7[T1,T2,T3,A1,A2,A3,A4,Boolean] = {
+      val (newExpr, newTypes) = Terms.product(this, other)
+      Term7(this.program, newExpr, if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : T1, x_1 : T2, x_2 : T3, x_3 : A1, x_4 : A2, x_5 : A3, x_6 : A4) => this.scalaFunction(x_0, x_1, x_2) && other.scalaFunction(x_3, x_4, x_5, x_6), newTypes, this.converter, this.lVars ++ other.lVars)
+    }
+    
+    
+    def product5[A1,A2,A3,A4,A5](other: Term5[A1,A2,A3,A4,A5,Boolean])(implicit isBoolean: R =:= Boolean): Term8[T1,T2,T3,A1,A2,A3,A4,A5,Boolean] = {
+      val (newExpr, newTypes) = Terms.product(this, other)
+      Term8(this.program, newExpr, if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : T1, x_1 : T2, x_2 : T3, x_3 : A1, x_4 : A2, x_5 : A3, x_6 : A4, x_7 : A5) => this.scalaFunction(x_0, x_1, x_2) && other.scalaFunction(x_3, x_4, x_5, x_6, x_7), newTypes, this.converter, this.lVars ++ other.lVars)
+    }
+    
+    
+    def product6[A1,A2,A3,A4,A5,A6](other: Term6[A1,A2,A3,A4,A5,A6,Boolean])(implicit isBoolean: R =:= Boolean): Term9[T1,T2,T3,A1,A2,A3,A4,A5,A6,Boolean] = {
+      val (newExpr, newTypes) = Terms.product(this, other)
+      Term9(this.program, newExpr, if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : T1, x_1 : T2, x_2 : T3, x_3 : A1, x_4 : A2, x_5 : A3, x_6 : A4, x_7 : A5, x_8 : A6) => this.scalaFunction(x_0, x_1, x_2) && other.scalaFunction(x_3, x_4, x_5, x_6, x_7, x_8), newTypes, this.converter, this.lVars ++ other.lVars)
+    }
   }
   
   trait Term4[T1,T2,T3,T4,R] extends Term[(T1,T2,T3,T4),R] with Function4[T1,T2,T3,T4,R] {
@@ -645,6 +760,35 @@ object Terms {
       val (newExpr, newTypes) = Terms.compose(other, this, 3, 6, 4)
       Term9(this.program, newExpr, if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : T1, x_1 : T2, x_2 : T3, x_3 : A1, x_4 : A2, x_5 : A3, x_6 : A4, x_7 : A5, x_8 : A6) => this.scalaFunction(x_0, x_1, x_2, other.scalaFunction(x_3, x_4, x_5, x_6, x_7, x_8)), newTypes, this.converter, this.lVars ++ other.lVars)
     }
+  
+    def product1[A1](other: Term1[A1,Boolean])(implicit isBoolean: R =:= Boolean): Term5[T1,T2,T3,T4,A1,Boolean] = {
+      val (newExpr, newTypes) = Terms.product(this, other)
+      Term5(this.program, newExpr, if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : T1, x_1 : T2, x_2 : T3, x_3 : T4, x_4 : A1) => this.scalaFunction(x_0, x_1, x_2, x_3) && other.scalaFunction(x_4), newTypes, this.converter, this.lVars ++ other.lVars)
+    }
+    
+    
+    def product2[A1,A2](other: Term2[A1,A2,Boolean])(implicit isBoolean: R =:= Boolean): Term6[T1,T2,T3,T4,A1,A2,Boolean] = {
+      val (newExpr, newTypes) = Terms.product(this, other)
+      Term6(this.program, newExpr, if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : T1, x_1 : T2, x_2 : T3, x_3 : T4, x_4 : A1, x_5 : A2) => this.scalaFunction(x_0, x_1, x_2, x_3) && other.scalaFunction(x_4, x_5), newTypes, this.converter, this.lVars ++ other.lVars)
+    }
+    
+    
+    def product3[A1,A2,A3](other: Term3[A1,A2,A3,Boolean])(implicit isBoolean: R =:= Boolean): Term7[T1,T2,T3,T4,A1,A2,A3,Boolean] = {
+      val (newExpr, newTypes) = Terms.product(this, other)
+      Term7(this.program, newExpr, if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : T1, x_1 : T2, x_2 : T3, x_3 : T4, x_4 : A1, x_5 : A2, x_6 : A3) => this.scalaFunction(x_0, x_1, x_2, x_3) && other.scalaFunction(x_4, x_5, x_6), newTypes, this.converter, this.lVars ++ other.lVars)
+    }
+    
+    
+    def product4[A1,A2,A3,A4](other: Term4[A1,A2,A3,A4,Boolean])(implicit isBoolean: R =:= Boolean): Term8[T1,T2,T3,T4,A1,A2,A3,A4,Boolean] = {
+      val (newExpr, newTypes) = Terms.product(this, other)
+      Term8(this.program, newExpr, if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : T1, x_1 : T2, x_2 : T3, x_3 : T4, x_4 : A1, x_5 : A2, x_6 : A3, x_7 : A4) => this.scalaFunction(x_0, x_1, x_2, x_3) && other.scalaFunction(x_4, x_5, x_6, x_7), newTypes, this.converter, this.lVars ++ other.lVars)
+    }
+    
+    
+    def product5[A1,A2,A3,A4,A5](other: Term5[A1,A2,A3,A4,A5,Boolean])(implicit isBoolean: R =:= Boolean): Term9[T1,T2,T3,T4,A1,A2,A3,A4,A5,Boolean] = {
+      val (newExpr, newTypes) = Terms.product(this, other)
+      Term9(this.program, newExpr, if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : T1, x_1 : T2, x_2 : T3, x_3 : T4, x_4 : A1, x_5 : A2, x_6 : A3, x_7 : A4, x_8 : A5) => this.scalaFunction(x_0, x_1, x_2, x_3) && other.scalaFunction(x_4, x_5, x_6, x_7, x_8), newTypes, this.converter, this.lVars ++ other.lVars)
+    }
   }
   
   trait Term5[T1,T2,T3,T4,T5,R] extends Term[(T1,T2,T3,T4,T5),R] with Function5[T1,T2,T3,T4,T5,R] {
@@ -798,6 +942,29 @@ object Terms {
       val (newExpr, newTypes) = Terms.compose(other, this, 4, 5, 5)
       Term9(this.program, newExpr, if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : T1, x_1 : T2, x_2 : T3, x_3 : T4, x_4 : A1, x_5 : A2, x_6 : A3, x_7 : A4, x_8 : A5) => this.scalaFunction(x_0, x_1, x_2, x_3, other.scalaFunction(x_4, x_5, x_6, x_7, x_8)), newTypes, this.converter, this.lVars ++ other.lVars)
     }
+  
+    def product1[A1](other: Term1[A1,Boolean])(implicit isBoolean: R =:= Boolean): Term6[T1,T2,T3,T4,T5,A1,Boolean] = {
+      val (newExpr, newTypes) = Terms.product(this, other)
+      Term6(this.program, newExpr, if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : T1, x_1 : T2, x_2 : T3, x_3 : T4, x_4 : T5, x_5 : A1) => this.scalaFunction(x_0, x_1, x_2, x_3, x_4) && other.scalaFunction(x_5), newTypes, this.converter, this.lVars ++ other.lVars)
+    }
+    
+    
+    def product2[A1,A2](other: Term2[A1,A2,Boolean])(implicit isBoolean: R =:= Boolean): Term7[T1,T2,T3,T4,T5,A1,A2,Boolean] = {
+      val (newExpr, newTypes) = Terms.product(this, other)
+      Term7(this.program, newExpr, if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : T1, x_1 : T2, x_2 : T3, x_3 : T4, x_4 : T5, x_5 : A1, x_6 : A2) => this.scalaFunction(x_0, x_1, x_2, x_3, x_4) && other.scalaFunction(x_5, x_6), newTypes, this.converter, this.lVars ++ other.lVars)
+    }
+    
+    
+    def product3[A1,A2,A3](other: Term3[A1,A2,A3,Boolean])(implicit isBoolean: R =:= Boolean): Term8[T1,T2,T3,T4,T5,A1,A2,A3,Boolean] = {
+      val (newExpr, newTypes) = Terms.product(this, other)
+      Term8(this.program, newExpr, if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : T1, x_1 : T2, x_2 : T3, x_3 : T4, x_4 : T5, x_5 : A1, x_6 : A2, x_7 : A3) => this.scalaFunction(x_0, x_1, x_2, x_3, x_4) && other.scalaFunction(x_5, x_6, x_7), newTypes, this.converter, this.lVars ++ other.lVars)
+    }
+    
+    
+    def product4[A1,A2,A3,A4](other: Term4[A1,A2,A3,A4,Boolean])(implicit isBoolean: R =:= Boolean): Term9[T1,T2,T3,T4,T5,A1,A2,A3,A4,Boolean] = {
+      val (newExpr, newTypes) = Terms.product(this, other)
+      Term9(this.program, newExpr, if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : T1, x_1 : T2, x_2 : T3, x_3 : T4, x_4 : T5, x_5 : A1, x_6 : A2, x_7 : A3, x_8 : A4) => this.scalaFunction(x_0, x_1, x_2, x_3, x_4) && other.scalaFunction(x_5, x_6, x_7, x_8), newTypes, this.converter, this.lVars ++ other.lVars)
+    }
   }
   
   trait Term6[T1,T2,T3,T4,T5,T6,R] extends Term[(T1,T2,T3,T4,T5,T6),R] with Function6[T1,T2,T3,T4,T5,T6,R] {
@@ -946,6 +1113,23 @@ object Terms {
       val (newExpr, newTypes) = Terms.compose(other, this, 5, 4, 6)
       Term9(this.program, newExpr, if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : T1, x_1 : T2, x_2 : T3, x_3 : T4, x_4 : T5, x_5 : A1, x_6 : A2, x_7 : A3, x_8 : A4) => this.scalaFunction(x_0, x_1, x_2, x_3, x_4, other.scalaFunction(x_5, x_6, x_7, x_8)), newTypes, this.converter, this.lVars ++ other.lVars)
     }
+  
+    def product1[A1](other: Term1[A1,Boolean])(implicit isBoolean: R =:= Boolean): Term7[T1,T2,T3,T4,T5,T6,A1,Boolean] = {
+      val (newExpr, newTypes) = Terms.product(this, other)
+      Term7(this.program, newExpr, if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : T1, x_1 : T2, x_2 : T3, x_3 : T4, x_4 : T5, x_5 : T6, x_6 : A1) => this.scalaFunction(x_0, x_1, x_2, x_3, x_4, x_5) && other.scalaFunction(x_6), newTypes, this.converter, this.lVars ++ other.lVars)
+    }
+    
+    
+    def product2[A1,A2](other: Term2[A1,A2,Boolean])(implicit isBoolean: R =:= Boolean): Term8[T1,T2,T3,T4,T5,T6,A1,A2,Boolean] = {
+      val (newExpr, newTypes) = Terms.product(this, other)
+      Term8(this.program, newExpr, if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : T1, x_1 : T2, x_2 : T3, x_3 : T4, x_4 : T5, x_5 : T6, x_6 : A1, x_7 : A2) => this.scalaFunction(x_0, x_1, x_2, x_3, x_4, x_5) && other.scalaFunction(x_6, x_7), newTypes, this.converter, this.lVars ++ other.lVars)
+    }
+    
+    
+    def product3[A1,A2,A3](other: Term3[A1,A2,A3,Boolean])(implicit isBoolean: R =:= Boolean): Term9[T1,T2,T3,T4,T5,T6,A1,A2,A3,Boolean] = {
+      val (newExpr, newTypes) = Terms.product(this, other)
+      Term9(this.program, newExpr, if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : T1, x_1 : T2, x_2 : T3, x_3 : T4, x_4 : T5, x_5 : T6, x_6 : A1, x_7 : A2, x_8 : A3) => this.scalaFunction(x_0, x_1, x_2, x_3, x_4, x_5) && other.scalaFunction(x_6, x_7, x_8), newTypes, this.converter, this.lVars ++ other.lVars)
+    }
   }
   
   trait Term7[T1,T2,T3,T4,T5,T6,T7,R] extends Term[(T1,T2,T3,T4,T5,T6,T7),R] with Function7[T1,T2,T3,T4,T5,T6,T7,R] {
@@ -1079,6 +1263,17 @@ object Terms {
       val (newExpr, newTypes) = Terms.compose(other, this, 6, 3, 7)
       Term9(this.program, newExpr, if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : T1, x_1 : T2, x_2 : T3, x_3 : T4, x_4 : T5, x_5 : T6, x_6 : A1, x_7 : A2, x_8 : A3) => this.scalaFunction(x_0, x_1, x_2, x_3, x_4, x_5, other.scalaFunction(x_6, x_7, x_8)), newTypes, this.converter, this.lVars ++ other.lVars)
     }
+  
+    def product1[A1](other: Term1[A1,Boolean])(implicit isBoolean: R =:= Boolean): Term8[T1,T2,T3,T4,T5,T6,T7,A1,Boolean] = {
+      val (newExpr, newTypes) = Terms.product(this, other)
+      Term8(this.program, newExpr, if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : T1, x_1 : T2, x_2 : T3, x_3 : T4, x_4 : T5, x_5 : T6, x_6 : T7, x_7 : A1) => this.scalaFunction(x_0, x_1, x_2, x_3, x_4, x_5, x_6) && other.scalaFunction(x_7), newTypes, this.converter, this.lVars ++ other.lVars)
+    }
+    
+    
+    def product2[A1,A2](other: Term2[A1,A2,Boolean])(implicit isBoolean: R =:= Boolean): Term9[T1,T2,T3,T4,T5,T6,T7,A1,A2,Boolean] = {
+      val (newExpr, newTypes) = Terms.product(this, other)
+      Term9(this.program, newExpr, if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : T1, x_1 : T2, x_2 : T3, x_3 : T4, x_4 : T5, x_5 : T6, x_6 : T7, x_7 : A1, x_8 : A2) => this.scalaFunction(x_0, x_1, x_2, x_3, x_4, x_5, x_6) && other.scalaFunction(x_7, x_8), newTypes, this.converter, this.lVars ++ other.lVars)
+    }
   }
   
   trait Term8[T1,T2,T3,T4,T5,T6,T7,T8,R] extends Term[(T1,T2,T3,T4,T5,T6,T7,T8),R] with Function8[T1,T2,T3,T4,T5,T6,T7,T8,R] {
@@ -1187,6 +1382,11 @@ object Terms {
       val (newExpr, newTypes) = Terms.compose(other, this, 7, 2, 8)
       Term9(this.program, newExpr, if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : T1, x_1 : T2, x_2 : T3, x_3 : T4, x_4 : T5, x_5 : T6, x_6 : T7, x_7 : A1, x_8 : A2) => this.scalaFunction(x_0, x_1, x_2, x_3, x_4, x_5, x_6, other.scalaFunction(x_7, x_8)), newTypes, this.converter, this.lVars ++ other.lVars)
     }
+  
+    def product1[A1](other: Term1[A1,Boolean])(implicit isBoolean: R =:= Boolean): Term9[T1,T2,T3,T4,T5,T6,T7,T8,A1,Boolean] = {
+      val (newExpr, newTypes) = Terms.product(this, other)
+      Term9(this.program, newExpr, if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : T1, x_1 : T2, x_2 : T3, x_3 : T4, x_4 : T5, x_5 : T6, x_6 : T7, x_7 : T8, x_8 : A1) => this.scalaFunction(x_0, x_1, x_2, x_3, x_4, x_5, x_6, x_7) && other.scalaFunction(x_8), newTypes, this.converter, this.lVars ++ other.lVars)
+    }
   }
   
   trait Term9[T1,T2,T3,T4,T5,T6,T7,T8,T9,R] extends Term[(T1,T2,T3,T4,T5,T6,T7,T8,T9),R] with Function9[T1,T2,T3,T4,T5,T6,T7,T8,T9,R] {
@@ -1260,20 +1460,8 @@ object Terms {
       val (newExpr, newTypes) = Terms.compose(other, this, 8, 1, 9)
       Term9(this.program, newExpr, if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : T1, x_1 : T2, x_2 : T3, x_3 : T4, x_4 : T5, x_5 : T6, x_6 : T7, x_7 : T8, x_8 : A1) => this.scalaFunction(x_0, x_1, x_2, x_3, x_4, x_5, x_6, x_7, other.scalaFunction(x_8)), newTypes, this.converter, this.lVars ++ other.lVars)
     }
-  }
   
-  object Term0 {
-    def apply[R](conv : Converter, serializedProg : Serialized, serializedInputVars: Serialized, serializedLVars: Serialized, serializedOutputVars : Serialized, serializedExpr : Serialized, inputVarValues : Seq[Expr], lVarValues : Seq[Expr], lVars : Seq[L[_]], scalaExpr : () => R) = {
-      val (converter, program, expr, types) = Term.processArgs(conv, serializedProg, serializedInputVars, serializedLVars, serializedOutputVars, serializedExpr, inputVarValues, lVarValues)
-      new Term[Unit,R](program, expr, types, converter, lVars.toSet) with Term0[R] {
-        val scalaFunction = scalaExpr
-      }
-    }
     
-    def apply[R](program : Program, expr : Expr, scalaExpr : () => R, types : Seq[TypeTree], converter : Converter, lVars: Set[L[_]]) =
-      new Term[Unit,R](program, expr, types, converter, lVars) with Term0[R] {
-        val scalaFunction = scalaExpr
-      }
   }
   
   object Term1 {
diff --git a/src/cp/Utils.scala b/src/cp/Utils.scala
index 10973783e637ac8bb6deede8c4793428688e8f3f..072150ff99110aca9ef9f26bd4b88e4741d1873f 100644
--- a/src/cp/Utils.scala
+++ b/src/cp/Utils.scala
@@ -68,6 +68,45 @@ object Utils {
           }
         }).flatten.mkString("\n\n")
 
+        val productMethods = (for (otherArity <- 1 to (maxArity - arity)) yield {
+          val productArity = arity + otherArity
+
+          val thisParams = (1 to arity).map("T" + _)
+
+          val otherParams = (1 to otherArity).map("A" + _)
+          val otherParamsString = otherParams.mkString(",")
+
+          val otherTermParams = otherParams ++ Seq("Boolean")
+          val otherTermParamsString = otherTermParams.mkString(",")
+
+          val resultParams = thisParams ++ otherParams
+          val resultTermParams = resultParams ++ Seq("Boolean")
+          val resultTermParamsString = resultTermParams.mkString(",")
+
+          val scalaFunctionParams = resultParams.zipWithIndex.map{ case (p, i) => "x_%d : %s" format (i, p) }
+          val scalaFunctionParamsString = scalaFunctionParams.mkString(", ")
+
+          val thisScalaFunctionArgs = (0 until arity).map("x_" + _)
+          val thisScalaFunctionArgsString = thisScalaFunctionArgs.mkString(", ")
+
+          val otherScalaFunctionArgs = (arity until productArity).map("x_" + _)
+          val otherScalaFunctionArgsString = otherScalaFunctionArgs.mkString(", ")
+
+          val thisApplication = "this.scalaFunction(%s)" format (thisScalaFunctionArgsString)
+          val otherApplication = "other.scalaFunction(%s)" format (otherScalaFunctionArgsString)
+
+          val scalaFunction = "(%s) => %s && %s" format (scalaFunctionParamsString, thisApplication, otherApplication)
+
+          val methodString =
+"""def product%d[%s](other: Term%d[%s])(implicit isBoolean: R =:= Boolean): Term%d[%s] = {
+  val (newExpr, newTypes) = Terms.product(this, other)
+  Term%d(this.program, newExpr, if (this.scalaFunction == null || other.scalaFunction == null) null else %s, newTypes, this.converter, this.lVars ++ other.lVars)
+}
+""" format (otherArity, otherParamsString, otherArity, otherTermParamsString, productArity, resultTermParamsString, productArity, scalaFunction)
+
+          methodString
+        }).mkString("\n\n")
+
         val (applyParams, applyArgs) = traitArgParams.zipWithIndex.map{ case (p, i) => ("x_%d : %s" format (i, p), "x_%d" format (i)) }.unzip
 
         val evaluatorArgs = traitArgParams.zipWithIndex.map{ case (p, i) => "converter.expr2scala(s(%d)).asInstanceOf[%s]" format (i, p) }
@@ -90,6 +129,8 @@ object Utils {
 
 %s
 
+%s
+
 %s
 }""" format (traitName, termClassParamTuple, "R", arity, traitParams.mkString(","), 
   arity, traitArgParamsString, 
@@ -97,7 +138,7 @@ object Utils {
   termClassParamTuple, "R", 
   evaluatorArgs.mkString(","),
   applyParams.mkString(", "), applyArgs.mkString(", "), 
-  indent(orMethod), indent(andMethod), indent(notMethod), indent(minimizingMethod), indent(lazySolveMethod), indent(composeMethods))
+  indent(orMethod), indent(andMethod), indent(notMethod), indent(minimizingMethod), indent(lazySolveMethod), indent(composeMethods), indent(productMethods))
         
         termTraitString
       }
@@ -169,11 +210,11 @@ object Utils {
       booleanTerms = "type Constraint[T] = Term[T,Boolean]" :: booleanTerms
       intTerms = "type IntTerm[T] = Term[T,Int]" :: intTerms
 
-      for (arity <- 1 to maxArity) {
+      for (arity <- 0 to maxArity) {
         val params = (1 to arity) map ("T" + _)
         val paramWithBooleanString = (params ++ Seq("Boolean")).mkString("[", ",", "]")
         val paramWithIntString = (params ++ Seq("Int")).mkString("[", ",", "]")
-        val paramString = params.mkString("[", ",", "]")
+        val paramString = if (arity == 0) "" else params.mkString("[", ",", "]")
         val boolType = "type Constraint%d%s = Term%d%s" format (arity, paramString, arity, paramWithBooleanString)
         val intType = "type IntTerm%d%s = Term%d%s" format (arity, paramString, arity, paramWithIntString)