module Mul

  use int.Int
  use mach.int.Int32
  use import mach.int.UInt64GMP as Limb
  use int.Power
  use ref.Ref
  use mach.c.C
  use array.Array
  use map.Map
  use types.Types
  use lemmas.Lemmas
  use util.Util
  use add.Add

  (** `wmpn_mul_1 r x sz y` multiplies `x[0..sz-1]` by the limb `y` and
       writes the n least significant limbs in `r`, and returns the most
       significant limb. It corresponds to `mpn_mul_1`. *)
  let wmpn_mul_1 (r x:t) (sz:int32) (y:limb) : limb
    requires { valid x sz }
    requires { valid r sz }
    requires { writable r }
    ensures { value r sz + (power radix sz) * result
                = value x sz * y }
    ensures { forall j. (j < offset r \/ offset r + sz <= j)
              -> (pelts r)[j] = old (pelts r)[j] }
    writes { r.data.elts }
  =
    let ref cl = 0 in
    let ref ul = 0 in
    let ref n = sz in
    let ref up = C.incr x 0 in
    let ref rp = C.incr r 0 in
    let ghost ref i : int32 = 0 in
    while n <> 0 do
      invariant { 0 <= n <= sz }
      invariant { i = sz - n }
      invariant { value r i + (power radix i) * cl =
                  value x i * y }
      invariant { rp.offset = r.offset + i }
      invariant { plength rp = plength r }
      invariant { rp.min = r.min }
      invariant { rp.max = r.max }
      invariant { writable rp }
      invariant { pelts rp = pelts r }
      invariant { up.offset = x.offset + i }
      invariant { plength up = plength x }
      invariant { up.min = x.min }
      invariant { up.max = x.max }
      invariant { pelts up = pelts x }
      invariant { forall j. (j < offset r \/ offset r + sz <= j)
                -> (pelts r)[j] = old (pelts r)[j] }
      variant { n }
      label StartLoop in
      ul <- C.get up;
      up <- C.incr up 1;
      let l, h = mul_double ul y in
      assert { h < radix - 1
               by ul * y <= (radix - 1) * (radix - 1)
               so radix * h <= ul * y };
      let lpl = add_mod l cl in
      begin ensures { lpl + radix * cl = ul * y + (cl at StartLoop) }
        cl <- (if lpl < cl then 1 else 0) + h;
      end;
      value_sub_update_no_change (pelts r) (r.offset + int32'int i)
                                 r.offset (r.offset + int32'int i) lpl;
      C.set rp lpl;
      assert { value r i = value r i at StartLoop };
      assert { (pelts r)[offset r + i] = lpl };
      value_tail r i;
      value_tail x i;
      assert { value x (i+1) = value x i + power radix i * ul };
      assert { value x (i+1) * y = value x i * y + power radix i * (ul * y) };
      assert { value r (i+1) + power radix (i+1) * cl = value x (i+1) * y };
      rp <- C.incr rp 1;
      n <- n-1;
      i <- i+1;
    done;
    cl


  (** `wmpn_addmul_1 r x sz y` multiplies `(x, sz)` by `y`, adds the `sz`
      least significant limbs to `(r, sz)` and writes the result in `(r, sz)`.
      Returns the most significant limb of the product plus the carry
      of the addition. Corresponds to `mpn_addmul_1`.*)
  let wmpn_addmul_1 (r x:t) (sz: int32) (y:limb) : limb
    requires { valid x sz }
    requires { valid r sz }
    requires { writable r }
    ensures { value r sz + (power radix sz) * result
            = value (old r) sz
              + value x sz * y }
    writes { r.data.elts }
    ensures { forall j. (j < r.offset \/ r.offset + sz <= j) ->
              (pelts r)[j] = (pelts (old r))[j] }
  =
    let ref ul = 0 in
    let ref rl = 0 in
    let ref cl = 0 in
    let ref n = sz in
    let ghost ref i = 0 in
    let ref rp = C.incr r 0 in
    let ref up = C.incr x 0 in
    while n <> 0 do
      invariant { 0 <= n <= sz }
      invariant { i = sz - n }
      invariant { value r i + (power radix i) * cl
                  = value (old r) i
                    + value x i * y }
      invariant { (rp).offset = r.offset + i }
      invariant { rp.min = r.min }
      invariant { rp.max = r.max }
      invariant { writable rp }
      invariant { pelts rp = pelts r }
      invariant { up.offset = x.offset + i }
      invariant { plength up = plength x }
      invariant { up.min = x.min }
      invariant { up.max = x.max }
      invariant { pelts up = pelts x }
      invariant { forall j. r.offset + i <= j < r.offset + sz ->
                 (pelts (old r)) [j] = (pelts r)[j]  }
      invariant { forall j. j < r.offset \/ r.offset + sz <= j ->
                 (pelts r)[j] = (pelts (old r))[j] }
      variant { sz - i }
      label StartLoop in
      ul <- C.get up;
      up <- C.incr up 1;
      let l, h = mul_double ul y in
      let ref lpl = add_mod l cl in
      assert { h < radix - 1
               by ul * y <= (radix - 1) * (radix - 1)
               so radix * h <= ul * y };
      begin ensures { lpl + radix * cl = ul * y + (cl at StartLoop) }
        cl <- (if lpl < cl then 1 else 0) + h;
      end;
      assert { cl = radix - 1 -> lpl = 0
               by ul * y <= (radix - 1) * (radix - 1)
               so cl at StartLoop <= radix - 1
               so lpl + radix * cl
                  = ul * y + (cl at StartLoop) = radix * (radix - 1) };
      rl <- C.get rp;
      assert { rl = (pelts (old r))[r.offset + i] };
      lpl <- add_mod rl lpl;
      begin ensures { lpl + radix * cl = ul * y + (cl at StartLoop) + rl }
        cl <- (if lpl < rl then 1 else 0) + cl;
      end;
      value_sub_update_no_change (pelts r) (r.offset + int32'int i)
                                 r.offset (r.offset + int32'int i) lpl;
      C.set rp lpl;
      assert { value r i = value r i at StartLoop };
      value_tail r i;
      value_tail x i;
      assert { value (old r) (i+1)
               = value (old r) i + power radix i * rl };
      assert { value x (i+1) = value x i + power radix i * ul };
      assert { value r (i+1) = value r i + power radix i * lpl };
      assert { value x (i+1) * y = value x i * y + power radix i * (ul * y) };
      assert { value r (i+1) + power radix (i+1) * cl =
               value (old r) (i+1) + value x (i+1) * y };
      rp <- C.incr rp 1;
      n <- n-1;
      i <- i+1;
    done;
    cl

 let wmpn_addmul_n (r x y:t) (sz:int32) : limb
    requires { sz > 0 }
    requires { valid x sz }
    requires { valid y sz }
    requires { valid r (sz + sz) }
    requires { writable r }
    writes { r.data.elts }
    ensures { value r (sz + sz)
                + power radix (sz + sz) * result
              = value (old r) (sz + sz)
                + value x sz * value y sz }
  = [@vc:do_not_keep_trace] (* traceability info breaks the proof *)
    let ref rp = C.incr r 0 in
    let ref vp = C.incr y 0 in
    let ref lr = 0 in
    let ref c = 0 in
    let ref vn = sz in
    let ghost ref i = 0 in
    while vn <> 0 do
      invariant { 0 <= i <= sz }
      invariant { i = sz - vn }
      invariant { value r (i + sz)
                    + (power radix (i + sz)) * c
                  = value (old r) (i + sz)
                    + value x sz * value y i }
      invariant { rp.offset = r.offset + i }
      invariant { rp.min = r.min }
      invariant { rp.max = r.max }
      invariant { writable rp }
      invariant { pelts rp = pelts r }
      invariant { plength rp = plength r }
      invariant { vp.offset = y.offset + i }
      invariant { plength vp = plength y }
      invariant { vp.min = y.min }
      invariant { vp.max = y.max }
      invariant { pelts vp = pelts y }
      invariant { 0 <= c <= 1 }
      invariant { forall j. rp.offset + sz <= j ->
                 (pelts (old r)) [j] = (pelts r)[j] }
      variant { sz - i }
      label StartLoop in
      value_concat r i (i+sz);
      assert { value rp sz
               = value_sub (pelts r) (r.offset + i) (r.offset + (i + sz)) };
      let ghost ly = pure { (pelts y)[y.offset + i] } in
      let c' =  wmpn_addmul_1 rp x sz (C.get vp) in
      assert { forall j. rp.offset + sz <= j ->
                 (pelts (old r)) [j] = (pelts r)[j]
                 by (pelts r)[j]
                 = (pelts rp)[j]
                 = (pelts rp)[j] at StartLoop
                 = (pelts (old r))[j]};
      assert { value rp sz + power radix sz * c'
              = value (rp at StartLoop) sz + value x sz * ly };
      assert { MapEq.map_eq_sub (pelts r) (pelts r at StartLoop)
                r.offset rp.offset
                by rp.offset = r.offset + i
                so forall j. r.offset <= j < rp.offset
                   ->
                   (j < rp.offset
                    so (pelts rp)[j] = (pelts rp at StartLoop)[j]
                         = (pelts r at StartLoop)[j]) };
      lr <- get_ofs rp sz;
      assert { lr = (pelts (old r))[(old r).offset + (i + sz)] };
      let (res, carry) = add_with_carry c' lr c in
      label BeforeCarry in
      value_sub_update_no_change (pelts r) (rp.offset + p2i sz)
                        r.offset  (r.offset + p2i i) res;
      set_ofs rp sz res;
      assert { value rp sz = value (rp at BeforeCarry) sz };
      c <- carry;
      assert { value r i = value (r at BeforeCarry) i
               = value (r at StartLoop) i};
      value_tail r (i+sz);
      value_tail y i;
      value_tail (pure { old r }) (i+sz);
      assert { value (old r) ((i+sz)+1)
               = value (old r) (i+sz) + power radix (i+sz) * lr };
      assert { (pelts r)[r.offset + (i + sz)] = res };
      value_concat r i (i+sz);
      assert { value_sub (pelts r) (r.offset + i) (r.offset+(i+sz))
               = value rp sz };
      assert { value r (i + sz + 1) =
               value r i + power radix i * (value rp sz) + power radix (i + sz) * res };
      assert { value x sz * value y (i+1)
               = value x sz * value y i + power radix i * (value x sz * ly) };
             (* nonlinear *)
      assert { value r (i + sz + 1)
                    + (power radix (i + sz + 1)) * c
                  = value (old r) (i + sz + 1)
                    + value x sz * value y (i + 1) };
      i <- i + 1;
      rp <- C.incr rp 1;
      vp <- C.incr vp 1;
      vn <- vn - 1;
      assert { forall j. rp.offset + sz <= j ->
                 (pelts (old r)) [j] = (pelts r)[j] };
    done;
    c

  (** In-place variant of wmpn_mul_1 *)
  let wmpn_mul_1_in_place (x:t) (sz:int32) (y:limb) : limb
    requires { valid x sz }
    requires { writable x }
    ensures  { value x sz + (power radix sz) * result
               = old value x sz * y }
    ensures  { forall j. (j < offset x \/ offset x + sz <= j)
              -> (pelts x)[j] = old (pelts x)[j] }
    writes   { x.data.elts }
  =
    let ghost ox = pure { x } in
    let ref cl = 0 in
    let ref ul = 0 in
    let ref n = sz in
    let ref up = C.incr x 0 in
    let ghost ref i : int32 = 0 in
    while n <> 0 do
      invariant { 0 <= n <= sz }
      invariant { i = sz - n }
      invariant { value x i + (power radix i) * cl =
                  value ox i * y }
      invariant { up.offset = x.offset + i }
      invariant { plength up = plength x }
      invariant { up.min = x.min }
      invariant { up.max = x.max }
      invariant { pelts up = pelts x }
      invariant { writable up }
      invariant { forall j. (j < offset x \/ offset x + i <= j)
                -> (pelts x)[j] = (pelts ox)[j] }
      variant { n }
      label StartLoop in
      ul <- C.get up;
      let l, h = mul_double ul y in
      assert { h < radix - 1
               by ul * y <= (radix - 1) * (radix - 1)
               so radix * h <= ul * y };
      let lpl = add_mod l cl in
      begin ensures { lpl + radix * cl = ul * y + (cl at StartLoop) }
        cl <- (if lpl < cl then 1 else 0) + h;
      end;
      value_sub_update_no_change (pelts x) (x.offset + int32'int i)
                                 x.offset (x.offset + int32'int i) lpl;
      C.set up lpl;
      assert { value x i = value x i at StartLoop };
      assert { (pelts x)[offset x + i] = lpl };
      value_tail x i;
      value_tail ox i;
      assert { value ox (i+1) = value ox i + power radix i * ul };
      assert { value ox (i+1) * y = value ox i * y + power radix i * (ul * y) };
      assert { value x (i+1) + power radix (i+1) * cl = value ox (i+1) * y };
      up <- C.incr up 1;
      n <- n-1;
      i <- i+1;
    done;
    cl

  (** `wmpn_submul_1 r x sz y` multiplies `(x, sz)` by `y`, subtracts the `sz`
      least significant limbs from `(r, sz)` and writes the result in `(r, sz)`.
      Returns the most significant limb of the product plus the borrow
      of the subtraction. Corresponds to `mpn_submul_1`.*)
  let wmpn_submul_1 (r x:t) (sz:int32) (y:limb):limb
    requires { valid x sz }
    requires { valid r sz }
    requires { writable r }
    ensures { value r sz - (power radix sz) * result
            = value (old r) sz
              - value x sz * y }
    writes { r.data.elts }
    ensures { forall j. j < r.offset \/ r.offset + sz <= j ->
              (pelts r)[j] = (pelts (old r))[j] }
  =
    let ref ul = 0 in
    let ref rl = 0 in
    let ref cl = 0 in
    let ref n = sz in
    let ghost ref i = 0 in
    let ref rp = C.incr r 0 in
    let ref up = C.incr x 0 in
    while n <> 0 do
      invariant { 0 <= n <= sz }
      invariant { i = sz - n }
      invariant { value r i - (power radix i) * cl
                  = value (old r) i
                    - value x i * y }
      invariant { (rp).offset = r.offset + i }
      invariant { rp.min = r.min }
      invariant { rp.max = r.max }
      invariant { writable rp }
      invariant { pelts rp = pelts r }
      invariant { up.offset = x.offset + i }
      invariant { plength up = plength x }
      invariant { up.min = x.min }
      invariant { up.max = x.max }
      invariant { pelts up = pelts x }
      invariant { forall j. r.offset + i <= j < r.offset + sz ->
                 (pelts (old r)) [j] = (pelts r)[j]  }
      invariant { forall j. j < r.offset \/ r.offset + sz <= j ->
                 (pelts r)[j] = (pelts (old r))[j] }
      variant { sz - i }
      label StartLoop in
      ul <- C.get up;
      up <- C.incr up 1;
      let l, h = mul_double ul y in
      let ref lpl = add_mod l cl in
      assert { h < radix - 1
               by ul * y <= (radix - 1) * (radix - 1)
               so radix * h <= ul * y };
      begin ensures { lpl + radix * cl = ul * y + (cl at StartLoop) }
        cl <- (if lpl < cl then 1 else 0) + h;
      end;
      assert { cl = radix - 1 -> lpl = 0
               by ul * y <= (radix - 1) * (radix - 1)
               so cl at StartLoop <= radix - 1
               so lpl + radix * cl
                  = ul * y + (cl at StartLoop) = radix * (radix - 1) };
      rl <- C.get rp;
      assert { rl = (pelts (old r))[r.offset + i] };
      lpl <- sub_mod rl lpl;
      begin ensures { lpl - radix * cl = rl - ul * y - (cl at StartLoop) }
        cl <- (if lpl > rl then 1 else 0) + cl;
      end;
      value_sub_update_no_change (pelts r) (r.offset + int32'int i)
                                 r.offset (r.offset + int32'int i) lpl;
      C.set rp lpl;
      assert { value r i = value r i at StartLoop };
      value_tail r i;
      value_tail x i;
      assert { value (old r) (i+1)
               = value (old r) i + power radix i * rl };
      assert { value x (i+1) = value x i + power radix i * ul };
      assert { value r (i+1) = value r i + power radix i * lpl };
      assert { value x (i+1) * y = value x i * y + power radix i * (ul * y) };
      assert { value r (i+1) - power radix (i+1) * cl =
               value (old r) (i+1) - value x (i+1) * y };
      rp <- C.incr rp 1;
      n <- n-1;
      i <- i+1;
    done;
    cl

 let wmpn_addmul_2 (r x:t) (sz:int32) (y:t) : limb
    requires { sz > 0 }
    requires { valid x sz }
    requires { valid y 2 }
    requires { valid r (sz + 2) }
    requires { writable r }
    writes   { r.data.elts }
    ensures  { value r (sz + 1)
                 + power radix (sz + 1) * result
               = value (old r) sz
                 + value x sz * value y 2 }
    ensures  { forall j. (j < r.offset \/ r.offset + sz + 1 <= j) ->
               (pelts r)[j] = (pelts (old r))[j] }
  =
    let ghost or = pure { r } in
    let y0 = C.get y in
    let rn = wmpn_addmul_1 r x sz y0 in
    value_sub_update_no_change (pelts r) (offset r + int32'int sz) (offset r)
                               (offset r + int32'int sz) rn;
    C.set_ofs r sz rn;
    value_tail r sz;
    assert { value r (sz + 1) = value or sz + value x sz * y0 };
    value_concat r 1 (sz + 1);
    let r1 = C.incr r 1 in
    assert { value r (sz + 1) = value r 1 + radix * value r1 sz };
    let y1 = C.get_ofs y 1 in
    assert { value y 2 = y0 + radix * y1 };
    label A2 in
    let c = wmpn_addmul_1 r1 x sz y1 in
    value_concat r 1 (sz + 1);
    value_sub_frame (pelts r) (pure { pelts r at A2 })
                    (offset r) (offset r + 1);
    assert { value r1 sz + power radix sz * c
             = value r1 sz at A2 + value x sz * y1 };
    assert { value r (sz + 1) + power radix (sz + 1) * c
             = value or sz + value x sz * value y 2
             by value r (sz + 1) + power radix (sz + 1) * c
                = value r 1 + radix * value r1 sz + power radix (sz + 1) * c
                = value r 1 + radix * (value r1 sz + power radix sz * c)
                = value r 1 + radix * (value r1 sz at A2 + value x sz * y1)
                = value r 1 at A2 + radix * value r1 sz at A2
                  + radix * (value x sz * y1)
                = value r (sz + 1) at A2 + radix * (value x sz * y1)
                = value or sz + value x sz * y0 + radix * value x sz * y1
                = value or sz + value x sz * (y0 + radix * y1)
                = value or sz + value x sz * value y 2 };
    assert { forall j. (j < r.offset \/ r.offset + sz + 1 <= j) ->
             (pelts r)[j] = (pelts (old r))[j]
             by (pelts r)[j] = (pelts r)[j] at A2 = (pelts (old r))[j] };
    c

end

module Mul_basecase

  use int.Int
  use mach.int.Int32
  use import mach.int.UInt64GMP as Limb
  use int.Power
  use ref.Ref
  use mach.c.C
  use array.Array
  use map.Map
  use types.Types
  use lemmas.Lemmas
  use util.Util
  use add.Add
  use Mul

  (** `wmpn_mul_basecase r x sx y sy` multiplies `(x, sx)` and `(y,sy)` and writes
  the result in `(r, sx+sy)`. `sx` must be greater than or equal to
  `sy`. Corresponds to `mpn_mul`. *)
  let wmpn_mul_basecase (r x:t) (sx:int32) (y:t) (sy:int32) : unit
    requires { 0 < sy <= sx }
    requires { valid x sx }
    requires { valid y sy }
    requires { valid r (sy + sx) }
    requires { writable r }
    writes   { r.data.elts }
    ensures  { value r (sy + sx) = value x sx * value y sy }
    ensures { forall j. (j < offset r \/ offset r + (sy + sx) <= j)
              -> (pelts r)[j] = old (pelts r)[j] }
    (*ensures  { result = (pelts r)[r.offset + sx + sy - 1] }*)
  =
    let c = wmpn_mul_1 r x sx (C.get y) in
    value_sub_update_no_change (pelts r) (r.offset + p2i sx)
                               r.offset (r.offset + p2i sx - 1) c;
    set_ofs r sx c;
    value_sub_tail (pelts r) r.offset (r.offset + p2i sx);
    assert { value r (sx + 1) = value x sx * value y 1
             by (pelts y)[offset y] = value y 1
             so value r sx + power radix sx * c = value x sx * value y 1 };
    let ref rp = C.incr r 1 in
    let ref vp = C.incr y 1 in
    let ghost ref i = 1 in
    let ref vn = sy - 1 in
    while vn >= 2 do
      invariant { 1 <= i <= sy }
      invariant { i = sy - vn }
      invariant { value r (i + sx) = value x sx * value y i }
      invariant { rp.offset = r.offset + i }
      invariant { plength rp = plength r }
      invariant { rp.min = r.min }
      invariant { rp.max = r.max }
      invariant { pelts rp = pelts r }
      invariant { writable rp }
      invariant { vp.offset = y.offset + i }
      invariant { plength vp = plength y }
      invariant { vp.min = y.min }
      invariant { vp.max = y.max }
      invariant { pelts vp = pelts y }
      invariant { forall j. (j < offset r \/ offset r + (sy + sx) <= j)
                -> (pelts r)[j] = old (pelts r)[j] }
      variant { vn }
      label StartLoop in
      value_concat r i (i + sx);
      assert { value rp sx =
               value_sub (pelts r) (r.offset + i) (r.offset + (i + sx)) };
      let res =  wmpn_addmul_2 rp x sx vp in
      assert { value rp (sx+1) + power radix (sx+1) * res
              = value (rp at StartLoop) sx + value x sx * value vp 2 };
      assert { MapEq.map_eq_sub (pelts r) (pelts r at StartLoop)
                r.offset rp.offset
                by rp.offset = r.offset + i
                so forall j. r.offset <= j < rp.offset
                   ->
                   (j < rp.offset
                    so (pelts rp)[j] = (pelts rp at StartLoop)[j]
                         = (pelts r at StartLoop)[j]) };
      label BeforeCarry in
      value_sub_update_no_change (pelts r) (rp.offset + p2i sx + 1)
                        r.offset  (r.offset + p2i i) res;
      set_ofs rp (sx+1) res;
      assert { value rp (sx+1) = value (rp at BeforeCarry) (sx+1) };
      assert { value r i = value (r at BeforeCarry) i
             = value (r at StartLoop) i };
      value_tail r (i + sx + 1);
      value_tail rp (sx + 1);
      value_concat y i (i+2);
      assert { value vp 2
               = value_sub (pelts y) (y.offset + i) (y.offset + (i+2)) };
      value_concat r i (i + sx + 2);
(*      assert { value_sub (pelts r) (r.offset + i) (r.offset + (i + sx + 1))
               = value rp (sx + 1) };
      assert { (pelts r)[r.offset + (i + sx + 1)] = res };*)
      assert { value rp (sx + 2) = value (rp at StartLoop) sx
               + value x sx * value vp 2 };
      assert { value x sx * value y (i+2)
               = value x sx * value y i
                 + power radix i * (value x sx * value vp 2) };
      assert { value r (i + sx + 2) = value x sx * value y (i+2)
               by value r (i + sx + 2)
                  = value r i
                    + power radix i * (value_sub (pelts r) (offset r + i)
                                                 (offset r + i + sx + 2))
                  = value r i + power radix i * value rp (sx + 2)
                  = value r i
                    + power radix i
                      * (value (rp at StartLoop) sx + value x sx * value vp 2)
                  = value (r at StartLoop) i
                    + power radix i
                      * (value (rp at StartLoop) sx + value x sx * value vp 2)
                  = value (r at StartLoop) i
                    + power radix i * value (rp at StartLoop) sx
                    + power radix i * (value x sx * value vp 2)
                  = value (r at StartLoop) (sx + i)
                    + power radix i * (value x sx * value vp 2)
                  = value x sx * value y i
                    + power radix i * value x sx * value vp 2
                  = value x sx * (value y i + power radix i * value vp 2)
                  = value x sx * value y (i+2) };
      i <- i + 2;
      rp <- C.incr rp 2;
      vp <- C.incr vp 2;
      vn <- vn - 2;
    done;
    while vn >= 1 do
      invariant { 1 <= i <= sy }
      invariant { i = sy - vn }
      invariant { value r (i + sx) = value x sx * value y i }
      invariant { rp.offset = r.offset + i }
      invariant { plength rp = plength r }
      invariant { rp.min = r.min }
      invariant { rp.max = r.max }
      invariant { pelts rp = pelts r }
      invariant { writable rp }
      invariant { vp.offset = y.offset + i }
      invariant { plength vp = plength y }
      invariant { vp.min = y.min }
      invariant { vp.max = y.max }
      invariant { pelts vp = pelts y }
      invariant { forall j. (j < offset r \/ offset r + (sy + sx) <= j)
                -> (pelts r)[j] = old (pelts r)[j] }
      variant { vn }
      label StartLoop in
      value_concat r i (i + sx);
      assert { value rp sx =
               value_sub (pelts r) (r.offset + i) (r.offset + (i + sx)) };
      let ghost ly = pure { (pelts y)[y.offset + i] } in
      let res =  wmpn_addmul_1 rp x sx (C.get vp) in
      assert { value rp sx + power radix sx * res
              = value (rp at StartLoop) sx + value x sx * ly };
      assert { MapEq.map_eq_sub (pelts r) (pelts r at StartLoop)
                r.offset rp.offset
                by rp.offset = r.offset + i
                so forall j. r.offset <= j < rp.offset
                   ->
                   (j < rp.offset
                    so (pelts rp)[j] = (pelts rp at StartLoop)[j]
                         = (pelts r at StartLoop)[j]) };
      label BeforeCarry in
      value_sub_update_no_change (pelts r) (rp.offset + p2i sx)
                        r.offset  (r.offset + p2i i) res;
      set_ofs rp sx res;
      assert { value rp sx = value (rp at BeforeCarry) sx };
      assert { value r i = value (r at BeforeCarry) i
             = value (r at StartLoop) i };
      value_tail r (i + sx);
      value_tail y i;
      value_concat r i (i+sx);
      assert { value_sub (pelts r) (r.offset + i) (r.offset+(i+sx))
               = value rp sx };
      assert { (pelts r)[r.offset + (i+sx)] = res };
      assert { value x sx * value y (i+1)
               = value x sx * value y i
                 + power radix i * (value x sx * ly) };
             (*nonlinear*)
      assert { value r (i + sx + 1) = value x sx * value y (i+1) };
      i <- i + 1;
      rp <- C.incr rp 1;
      vp <- C.incr vp 1;
      vn <- vn - 1;
    done


end