diff --git a/library/lang/StrOps.scala b/library/lang/StrOps.scala
index 631c2e3ad95b6c5bd967bc66b50ceb006f81bb5c..1276a06f3fa67f32746dade7332ea75e4de5f720 100644
--- a/library/lang/StrOps.scala
+++ b/library/lang/StrOps.scala
@@ -12,6 +12,14 @@ object StrOps {
   def concat(a: String, b: String): String = {
     a + b
   }
+  @ignore
+  def bigLength(s: String): BigInt = {
+    BigInt(s.length)
+  }
+  @ignore
+  def bigSubstring(s: String, start: BigInt, end: BigInt): String = {
+    s.substring(start.toInt, end.toInt)
+  }
   @internal @library
   def escape(s: String): String = s // Wrong definition, but it will eventually use StringEscapeUtils.escapeJava(s) at parsing and compile time.
 }
\ No newline at end of file
diff --git a/library/lang/package.scala b/library/lang/package.scala
index 823788a8883306c265ea33c193478ea2c3950aff..c10c025828309a313abb3e02d4d40737737192d4 100644
--- a/library/lang/package.scala
+++ b/library/lang/package.scala
@@ -68,6 +68,13 @@ package object lang {
       (res: A) => byExample(input, res)
     }
   }
+  
+  implicit class StringDecorations(val underlying: String) {
+    @ignore
+    def bigLength = BigInt(underlying.length)
+    @ignore
+    def bigsubstring(start: BigInt, end: BigInt): String = underlying.substring(start.toInt, end.toInt)
+  }
 
   @ignore
   object BigInt {
diff --git a/library/theories/String.scala b/library/theories/String.scala
index ca2b9027df5a9628ab3140416897ee6274904ff9..7455015784f45fb8e8b0e8d9f239b426be4ce0a1 100644
--- a/library/theories/String.scala
+++ b/library/theories/String.scala
@@ -9,6 +9,11 @@ sealed abstract class String {
     case StringCons(_, tail) => 1 + tail.size
     case StringNil() => BigInt(0)
   }) ensuring (_ >= 0)
+  
+  def sizeI: Int = this match {
+    case StringCons(_, tail) => 1 + tail.length
+    case StringNil() => 0
+  }
 
   def concat(that: String): String = this match {
     case StringCons(head, tail) => StringCons(head, tail concat that)
@@ -24,8 +29,19 @@ sealed abstract class String {
     case StringCons(head, tail) if i > 0 => tail drop (i - 1)
     case _ => this
   }
+  
+  def takeI(i: Int): String = this match {
+    case StringCons(head, tail) if i > 0 => StringCons(head, tail takeI (i - 1))
+    case _ => StringNil()
+  }
+
+  def dropI(i: Int): String = this match {
+    case StringCons(head, tail) if i > 0 => tail dropI (i - 1)
+    case _ => this
+  }
 
   def slice(from: BigInt, to: BigInt): String = drop(from).take(to - from)
+  def sliceI(from: Int, to: Int): String = dropI(from).takeI(to - from)
 }
 
 case class StringCons(head: Char, tail: String) extends String
diff --git a/src/main/java/leon/codegen/runtime/StrOps.java b/src/main/java/leon/codegen/runtime/StrOps.java
index 26a95aa7c1a039175accd4b4e810a91b5fd37e73..9f97e37a535b230386095fe0f37ad62d1ec59bf7 100644
--- a/src/main/java/leon/codegen/runtime/StrOps.java
+++ b/src/main/java/leon/codegen/runtime/StrOps.java
@@ -8,6 +8,14 @@ public class StrOps {
 	public static String concat(String a, String b) {
 		return a + b;
 	}
+	
+	public static BigInt bigLength(String a) {
+		return new BigInt(a.length() + "");
+	}
+	
+	public static String bigSubstring(String s, BigInt start, BigInt end) {
+		return s.substring(Integer.parseInt(start.toString()), Integer.parseInt(end.toString()));
+	}
 
 	public static String bigIntToString(BigInt a) {
 		return a.toString();
diff --git a/src/main/scala/leon/codegen/CodeGeneration.scala b/src/main/scala/leon/codegen/CodeGeneration.scala
index c352b45e35e830fac493cf9f51b4bd8db7ce9df3..199fd866942e3700f08da56413330ee192714ee6 100644
--- a/src/main/scala/leon/codegen/CodeGeneration.scala
+++ b/src/main/scala/leon/codegen/CodeGeneration.scala
@@ -872,7 +872,11 @@ trait CodeGeneration {
         
       case StringLength(a) =>
         mkExpr(a, ch)
-        ch << InvokeSpecial(JavaStringClass, "length", s"()I")
+        ch << InvokeVirtual(JavaStringClass, "length", s"()I")
+        
+      case StringBigLength(a) =>
+        mkExpr(a, ch)
+        ch << InvokeStatic(StrOpsClass, "bigLength", s"(L$JavaStringClass;)L$BigIntClass;")
         
       case Int32ToString(a) =>
         mkExpr(a, ch)
@@ -894,7 +898,13 @@ trait CodeGeneration {
         mkExpr(a, ch)
         mkExpr(start, ch)
         mkExpr(end, ch)
-        ch << InvokeSpecial(JavaStringClass, "substring", s"(L$JavaStringClass;II)L$JavaStringClass;")
+        ch << InvokeVirtual(JavaStringClass, "substring", s"(II)L$JavaStringClass;")
+      
+      case BigSubString(a, start, end) =>
+        mkExpr(a, ch)
+        mkExpr(start, ch)
+        mkExpr(end, ch)
+        ch << InvokeStatic(StrOpsClass, "bigSubstring", s"(L$JavaStringClass;II)L$JavaStringClass;")
         
       // Arithmetic
       case Plus(l, r) =>
diff --git a/src/main/scala/leon/evaluators/RecursiveEvaluator.scala b/src/main/scala/leon/evaluators/RecursiveEvaluator.scala
index 348f06fb4411a096fbfc7e64fd37b1729b98fae6..a709de91fdf5af88ff64fa9393dd0a25e2d122dc 100644
--- a/src/main/scala/leon/evaluators/RecursiveEvaluator.scala
+++ b/src/main/scala/leon/evaluators/RecursiveEvaluator.scala
@@ -285,11 +285,20 @@ abstract class RecursiveEvaluator(ctx: LeonContext, prog: Program, val bank: Eva
       case StringLiteral(a) => IntLiteral(a.length)
       case res => throw EvalError(typeErrorMsg(res, Int32Type))
     }
+    case StringBigLength(a) => e(a) match {
+      case StringLiteral(a) => InfiniteIntegerLiteral(a.length)
+      case res => throw EvalError(typeErrorMsg(res, IntegerType))
+    }
     case SubString(a, start, end) => (e(a), e(start), e(end)) match {
       case (StringLiteral(a), IntLiteral(b), IntLiteral(c))  =>
         StringLiteral(a.substring(b, c))
       case res => throw EvalError(typeErrorMsg(res._1, StringType))
     }
+    case BigSubString(a, start, end) => (e(a), e(start), e(end)) match {
+      case (StringLiteral(a), InfiniteIntegerLiteral(b), InfiniteIntegerLiteral(c))  =>
+        StringLiteral(a.substring(b.toInt, c.toInt))
+      case res => throw EvalError(typeErrorMsg(res._1, StringType))
+    }
     case Int32ToString(a) => e(a) match {
       case IntLiteral(i) => StringLiteral(i.toString)
       case res =>  throw EvalError(typeErrorMsg(res, Int32Type))
diff --git a/src/main/scala/leon/frontends/scalac/CodeExtraction.scala b/src/main/scala/leon/frontends/scalac/CodeExtraction.scala
index c586444732e0ce7283ebcd72ee9c343861b5a2b0..9c3e4b393792474d8cfcf4e3229d3aeacf1e749c 100644
--- a/src/main/scala/leon/frontends/scalac/CodeExtraction.scala
+++ b/src/main/scala/leon/frontends/scalac/CodeExtraction.scala
@@ -1728,10 +1728,16 @@ trait CodeExtraction extends ASTExtractors {
               StringConcat(converter(a1), a2)
             case (IsTyped(a1, StringType), "length", List()) =>
               StringLength(a1)
+            case (IsTyped(a1, StringType), "bigLength", List()) =>
+              StringBigLength(a1)
             case (IsTyped(a1, StringType), "substring", List(IsTyped(start, Int32Type))) =>
               SubString(a1, start, StringLength(a1))
             case (IsTyped(a1, StringType), "substring", List(IsTyped(start, Int32Type), IsTyped(end, Int32Type))) =>
               SubString(a1, start, end)
+            case (IsTyped(a1, StringType), "bigSubstring", List(IsTyped(start, IntegerType))) =>
+              BigSubString(a1, start, StringBigLength(a1))
+            case (IsTyped(a1, StringType), "bigSubstring", List(IsTyped(start, IntegerType), IsTyped(end, IntegerType))) =>
+              BigSubString(a1, start, end)
 
             //BigInt methods
             case (IsTyped(a1, IntegerType), "+", List(IsTyped(a2, IntegerType))) =>
diff --git a/src/main/scala/leon/purescala/ExprOps.scala b/src/main/scala/leon/purescala/ExprOps.scala
index 2f63ac23b3a8402125cbbf37affec4617f6bc450..c0a219c4ddb18ec73945c718d0af5551c44d5c09 100644
--- a/src/main/scala/leon/purescala/ExprOps.scala
+++ b/src/main/scala/leon/purescala/ExprOps.scala
@@ -1102,8 +1102,10 @@ object ExprOps extends GenTreeOps[Expr] {
       case StringConcat(StringLiteral(""), b) => b
       case StringConcat(b, StringLiteral("")) => b
       case StringConcat(StringLiteral(a), StringLiteral(b)) => StringLiteral(a + b)
-      case StringLength(StringLiteral(a)) => InfiniteIntegerLiteral(a.length)
-      case SubString(StringLiteral(a), InfiniteIntegerLiteral(start), InfiniteIntegerLiteral(end)) => StringLiteral(a.substring(start.toInt, end.toInt))
+      case StringLength(StringLiteral(a)) => IntLiteral(a.length)
+      case StringBigLength(StringLiteral(a)) => InfiniteIntegerLiteral(a.length)
+      case SubString(StringLiteral(a), IntLiteral(start), IntLiteral(end)) => StringLiteral(a.substring(start.toInt, end.toInt))
+      case BigSubString(StringLiteral(a), InfiniteIntegerLiteral(start), InfiniteIntegerLiteral(end)) => StringLiteral(a.substring(start.toInt, end.toInt))
       case _ => expr
     }).copiedFrom(expr)
     simplify0(expr)
diff --git a/src/main/scala/leon/purescala/Expressions.scala b/src/main/scala/leon/purescala/Expressions.scala
index adeee57f345239423a93b2f9b7abadbf0e08795b..6620ebb6ad5149742cae3369470c2091776bf9dd 100644
--- a/src/main/scala/leon/purescala/Expressions.scala
+++ b/src/main/scala/leon/purescala/Expressions.scala
@@ -599,6 +599,16 @@ object Expressions {
       else Untyped
     }
   }
+  /** $encodingof `lhs.subString(start, end)` for strings */
+  case class BigSubString(expr: Expr, start: Expr, end: Expr) extends Expr {
+    val getType = {
+      val ext = expr.getType
+      val st = start.getType
+      val et = end.getType
+      if (ext == StringType && st == IntegerType && et == IntegerType) StringType
+      else Untyped
+    }
+  }
   /** $encodingof `lhs.length` for strings */
   case class StringLength(expr: Expr) extends Expr {
     val getType = {
@@ -606,6 +616,13 @@ object Expressions {
       else Untyped
     }
   }
+  /** $encodingof `lhs.length` for strings */
+  case class StringBigLength(expr: Expr) extends Expr {
+    val getType = {
+      if (expr.getType == StringType) IntegerType
+      else Untyped
+    }
+  }
 
   /* Integer arithmetic */
 
diff --git a/src/main/scala/leon/purescala/Extractors.scala b/src/main/scala/leon/purescala/Extractors.scala
index 42cc07526982d45e56c034ea7b9b14f4c4c72e44..36b0887dc14fa86623357b1c2157a75b30b3f49e 100644
--- a/src/main/scala/leon/purescala/Extractors.scala
+++ b/src/main/scala/leon/purescala/Extractors.scala
@@ -40,6 +40,8 @@ object Extractors {
         Some((Seq(t), (es: Seq[Expr]) => BVNot(es.head)))
       case StringLength(t) =>
         Some((Seq(t), (es: Seq[Expr]) => StringLength(es.head)))
+      case StringBigLength(t) =>
+        Some((Seq(t), (es: Seq[Expr]) => StringBigLength(es.head)))
       case Int32ToString(t) =>
         Some((Seq(t), (es: Seq[Expr]) => Int32ToString(es.head)))
       case BooleanToString(t) =>
@@ -196,6 +198,7 @@ object Extractors {
       case And(args) => Some((args, es => And(es)))
       case Or(args) => Some((args, es => Or(es)))
       case SubString(t1, a, b) => Some((t1::a::b::Nil, es => SubString(es(0), es(1), es(2))))
+      case BigSubString(t1, a, b) => Some((t1::a::b::Nil, es => BigSubString(es(0), es(1), es(2))))
       case FiniteSet(els, base) =>
         Some((els.toSeq, els => FiniteSet(els.toSet, base)))
       case FiniteBag(els, base) =>
diff --git a/src/main/scala/leon/purescala/PrettyPrinter.scala b/src/main/scala/leon/purescala/PrettyPrinter.scala
index e822cae941b8554b4b3b2f4825a0b3d683930fb1..bda0e061258e8f542c21e0fe4f40e5df369984aa 100644
--- a/src/main/scala/leon/purescala/PrettyPrinter.scala
+++ b/src/main/scala/leon/purescala/PrettyPrinter.scala
@@ -190,7 +190,9 @@ class PrettyPrinter(opts: PrinterOptions,
       case StringConcat(lhs, rhs) => optP { p"$lhs + $rhs" }
     
       case SubString(expr, start, end) => p"$expr.substring($start, $end)"
+      case BigSubString(expr, start, end) => p"$expr.bigSubstring($start, $end)"
       case StringLength(expr)          => p"$expr.length"
+      case StringBigLength(expr)       => p"$expr.bigLength"
 
       case IntLiteral(v)        => p"$v"
       case InfiniteIntegerLiteral(v) => p"$v"
diff --git a/src/main/scala/leon/solvers/smtlib/SMTLIBCVC4Target.scala b/src/main/scala/leon/solvers/smtlib/SMTLIBCVC4Target.scala
index 04763b1b593df46c358651e28f6dfbd905fd4269..bd1ef544856123a9a940179f432b6b235c404ab8 100644
--- a/src/main/scala/leon/solvers/smtlib/SMTLIBCVC4Target.scala
+++ b/src/main/scala/leon/solvers/smtlib/SMTLIBCVC4Target.scala
@@ -109,9 +109,13 @@ trait SMTLIBCVC4Target extends SMTLIBTarget {
       case (SString(v), Some(StringType)) =>
         StringLiteral(v)
         
-      case (Strings.Length(a), _) =>
+      case (Strings.Length(a), Some(Int32Type)) =>
         val aa = fromSMT(a)
         StringLength(aa)
+        
+      case (Strings.Length(a), Some(IntegerType)) =>
+        val aa = fromSMT(a)
+        StringBigLength(aa)
 
       case (Strings.Concat(a, b, c @ _*), _) =>
         val aa = fromSMT(a)
@@ -125,8 +129,14 @@ trait SMTLIBCVC4Target extends SMTLIBTarget {
         val tt = fromSMT(start)
         val oo = fromSMT(offset)
         oo match {
-          case Minus(otherEnd, `tt`) => SubString(ss, tt, otherEnd)
-          case _ => SubString(ss, tt, Plus(tt, oo))
+          case BVMinus(otherEnd, `tt`) => SubString(ss, tt, otherEnd)
+          case Minus(otherEnd, `tt`) => BigSubString(ss, tt, otherEnd)
+          case _ => 
+            if(tt.getType == IntegerType) {
+              BigSubString(ss, tt, Plus(tt, oo))
+            } else {
+              SubString(ss, tt, BVPlus(tt, oo))
+            }
         }
         
       case (Strings.At(a, b), _) => fromSMT(Strings.Substring(a, b, SNumeral(1)))
@@ -164,10 +174,14 @@ trait SMTLIBCVC4Target extends SMTLIBTarget {
         declareSort(StringType)
         Strings.StringLit(v)
     case StringLength(a)           => Strings.Length(toSMT(a))
+    case StringBigLength(a)        => Strings.Length(toSMT(a))
     case StringConcat(a, b)        => Strings.Concat(toSMT(a), toSMT(b))
-    case SubString(a, start, Plus(start2, length)) if start == start2  =>
+    case SubString(a, start, BVPlus(start2, length)) if start == start2  =>
                                       Strings.Substring(toSMT(a),toSMT(start),toSMT(length))
     case SubString(a, start, end)  => Strings.Substring(toSMT(a),toSMT(start),toSMT(Minus(end, start)))
+    case BigSubString(a, start, Plus(start2, length)) if start == start2  =>
+                                      Strings.Substring(toSMT(a),toSMT(start),toSMT(length))
+    case BigSubString(a, start, end)  => Strings.Substring(toSMT(a),toSMT(start),toSMT(Minus(end, start)))
     case _ =>
       super.toSMT(e)
   }
diff --git a/src/main/scala/leon/solvers/theories/StringEncoder.scala b/src/main/scala/leon/solvers/theories/StringEncoder.scala
index 02293fab327e7a79f1bb682c5fca65857a45bfd8..1b7538cd86b5da2a073807abf4e18f8d33e14d3b 100644
--- a/src/main/scala/leon/solvers/theories/StringEncoder.scala
+++ b/src/main/scala/leon/solvers/theories/StringEncoder.scala
@@ -22,6 +22,11 @@ class StringEncoder(ctx: LeonContext, p: Program) extends TheoryEncoder {
   val Drop   = p.library.lookupUnique[FunDef]("leon.theories.String.drop").typed
   val Slice  = p.library.lookupUnique[FunDef]("leon.theories.String.slice").typed
   val Concat = p.library.lookupUnique[FunDef]("leon.theories.String.concat").typed
+
+  val SizeI   = p.library.lookupUnique[FunDef]("leon.theories.String.sizeI").typed
+  val TakeI   = p.library.lookupUnique[FunDef]("leon.theories.String.takeI").typed
+  val DropI   = p.library.lookupUnique[FunDef]("leon.theories.String.dropI").typed
+  val SliceI  = p.library.lookupUnique[FunDef]("leon.theories.String.sliceI").typed
   
   val FromInt      = p.library.lookupUnique[FunDef]("leon.theories.String.fromInt").typed
   val FromChar     = p.library.lookupUnique[FunDef]("leon.theories.String.fromChar").typed
@@ -46,13 +51,19 @@ class StringEncoder(ctx: LeonContext, p: Program) extends TheoryEncoder {
     override def transformExpr(e: Expr)(implicit binders: Map[Identifier, Identifier]): Option[Expr] = e match {
       case StringLiteral(v)          =>
         Some(convertFromString(v))
-      case StringLength(a)           =>
+      case StringBigLength(a)           =>
         Some(FunctionInvocation(Size, Seq(transform(a))).copiedFrom(e))
+      case StringLength(a)           =>
+        Some(FunctionInvocation(SizeI, Seq(transform(a))).copiedFrom(e))
       case StringConcat(a, b)        =>
         Some(FunctionInvocation(Concat, Seq(transform(a), transform(b))).copiedFrom(e))
       case SubString(a, start, Plus(start2, length)) if start == start2  =>
-        Some(FunctionInvocation(Take, Seq(FunctionInvocation(Drop, Seq(transform(a), transform(start))), transform(length))).copiedFrom(e))
+        Some(FunctionInvocation(TakeI, Seq(FunctionInvocation(DropI, Seq(transform(a), transform(start))), transform(length))).copiedFrom(e))
       case SubString(a, start, end)  => 
+        Some(FunctionInvocation(SliceI, Seq(transform(a), transform(start), transform(end))).copiedFrom(e))
+      case BigSubString(a, start, Plus(start2, length)) if start == start2  =>
+        Some(FunctionInvocation(Take, Seq(FunctionInvocation(Drop, Seq(transform(a), transform(start))), transform(length))).copiedFrom(e))
+      case BigSubString(a, start, end)  => 
         Some(FunctionInvocation(Slice, Seq(transform(a), transform(start), transform(end))).copiedFrom(e))
       case Int32ToString(a) => 
         Some(FunctionInvocation(FromInt, Seq(transform(a))).copiedFrom(e))
@@ -85,20 +96,32 @@ class StringEncoder(ctx: LeonContext, p: Program) extends TheoryEncoder {
     override def transformExpr(e: Expr)(implicit binders: Map[Identifier, Identifier]): Option[Expr] = e match {
       case cc @ CaseClass(cct, args) if TypeOps.isSubtypeOf(cct, String)=>
         Some(StringLiteral(convertToString(cc)).copiedFrom(cc))
-      case FunctionInvocation(Size, Seq(a)) =>
+      case FunctionInvocation(SizeI, Seq(a)) =>
         Some(StringLength(transform(a)).copiedFrom(e))
+      case FunctionInvocation(Size, Seq(a)) =>
+        Some(StringBigLength(transform(a)).copiedFrom(e))
       case FunctionInvocation(Concat, Seq(a, b)) =>
         Some(StringConcat(transform(a), transform(b)).copiedFrom(e))
-      case FunctionInvocation(Slice, Seq(a, from, to)) =>
+      case FunctionInvocation(SliceI, Seq(a, from, to)) =>
         Some(SubString(transform(a), transform(from), transform(to)).copiedFrom(e))
-      case FunctionInvocation(Take, Seq(FunctionInvocation(Drop, Seq(a, start)), length)) =>
+      case FunctionInvocation(Slice, Seq(a, from, to)) =>
+        Some(BigSubString(transform(a), transform(from), transform(to)).copiedFrom(e))
+      case FunctionInvocation(TakeI, Seq(FunctionInvocation(DropI, Seq(a, start)), length)) =>
         val rstart = transform(start)
         Some(SubString(transform(a), rstart, plus(rstart, transform(length))).copiedFrom(e))
+      case FunctionInvocation(Take, Seq(FunctionInvocation(Drop, Seq(a, start)), length)) =>
+        val rstart = transform(start)
+        Some(BigSubString(transform(a), rstart, plus(rstart, transform(length))).copiedFrom(e))
+      case FunctionInvocation(TakeI, Seq(a, length)) =>
+        Some(SubString(transform(a), IntLiteral(0), transform(length)).copiedFrom(e))
       case FunctionInvocation(Take, Seq(a, length)) =>
-        Some(SubString(transform(a), InfiniteIntegerLiteral(0), transform(length)).copiedFrom(e))
-      case FunctionInvocation(Drop, Seq(a, count)) =>
+        Some(BigSubString(transform(a), InfiniteIntegerLiteral(0), transform(length)).copiedFrom(e))
+      case FunctionInvocation(DropI, Seq(a, count)) =>
         val ra = transform(a)
         Some(SubString(ra, transform(count), StringLength(ra)).copiedFrom(e))
+      case FunctionInvocation(Drop, Seq(a, count)) =>
+        val ra = transform(a)
+        Some(BigSubString(ra, transform(count), StringBigLength(ra)).copiedFrom(e))
       case FunctionInvocation(FromInt, Seq(a)) =>
         Some(Int32ToString(transform(a)).copiedFrom(e))
       case FunctionInvocation(FromBigInt, Seq(a)) =>