aboutsummaryrefslogtreecommitdiff
path: root/src/LegacyArithmetic/Double/Proofs/RippleCarryAddSub.v
blob: e703c2e57640b745b5687b66efff29ece2f5ed16 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
Require Import Coq.ZArith.ZArith Coq.micromega.Psatz.
Require Import Crypto.LegacyArithmetic.Interface.
Require Import Crypto.LegacyArithmetic.InterfaceProofs.
Require Import Crypto.LegacyArithmetic.Double.Core.
Require Import Crypto.LegacyArithmetic.Double.Proofs.Decode.
Require Import Crypto.Util.Tuple.
Require Import Crypto.Util.ZUtil.
Require Import Crypto.Util.Tactics.BreakMatch.
Require Import Crypto.Util.Tactics.SimplifyProjections.
Require Import Crypto.Util.Notations.
Require Import Crypto.Util.LetIn.
Require Import Crypto.Util.Prod.
Import Bug5107WorkAround.
Import BoundedRewriteNotations.

Local Coercion Z.of_nat : nat >-> Z.
Local Notation eta x := (fst x, snd x).

Local Open Scope Z_scope.
Local Opaque tuple_decoder.

Lemma ripple_carry_tuple_SS' {T} f k xss yss carry
  : @ripple_carry_tuple T f (S (S k)) xss yss carry
    = dlet xss := xss in
      dlet yss := yss in
      let '(xs, x) := eta xss in
      let '(ys, y) := eta yss in
      dlet addv := (@ripple_carry_tuple _ f (S k) xs ys carry) in
      let '(carry, zs) := eta addv in
      dlet fxy := (f x y carry) in
      let '(carry, z) := eta fxy in
      (carry, (zs, z)).
Proof. reflexivity. Qed.

Lemma ripple_carry_tuple_SS {T} f k xss yss carry
  : @ripple_carry_tuple T f (S (S k)) xss yss carry
    = let '(xs, x) := eta xss in
      let '(ys, y) := eta yss in
      let '(carry, zs) := eta (@ripple_carry_tuple _ f (S k) xs ys carry) in
      let '(carry, z) := eta (f x y carry) in
      (carry, (zs, z)).
Proof.
  rewrite ripple_carry_tuple_SS'.
  eta_expand.
  reflexivity.
Qed.

Lemma carry_is_good (n z0 z1 k : Z)
  : 0 <= n ->
    0 <= k ->
    (z1 + z0 >> k) >> n = (z0 + z1 << k) >> (k + n) /\
    (z0 mod 2 ^ k + ((z1 + z0 >> k) mod 2 ^ n) << k)%Z = (z0 + z1 << k) mod (2 ^ k * 2 ^ n).
Proof.
  intros.
  assert (0 < 2 ^ n) by auto with zarith.
  assert (0 < 2 ^ k) by auto with zarith.
  assert (0 < 2^n * 2^k) by nia.
  autorewrite with Zshift_to_pow push_Zpow.
  rewrite <- (Zmod_small ((z0 mod _) + _) (2^k * 2^n)) by (Z.div_mod_to_quot_rem; nia).
  rewrite <- !Z.mul_mod_distr_r by lia.
  rewrite !(Z.mul_comm (2^k)); pull_Zmod.
  split; [ | apply f_equal2 ];
    Z.div_mod_to_quot_rem; nia.
Qed.
Section carry_sub_is_good.
  Context (n k z0 z1 : Z)
          (Hn : 0 <= n)
          (Hk : 0 <= k)
          (Hz1 : -2^n < z1 < 2^n)
          (Hz0 : -2^k <= z0 < 2^k).

  Lemma carry_sub_is_good_carry
    : ((z1 - if z0 <? 0 then 1 else 0) <? 0) = ((z0 + z1 << k) <? 0).
  Proof using Hk Hz0.
    clear n Hn Hz1.
    assert (0 < 2 ^ k) by auto with zarith.
    autorewrite with Zshift_to_pow.
    repeat match goal with
           | _ => progress break_match
           | [ |- context[?x <? ?y] ] => destruct (x <? y) eqn:?
           | _ => reflexivity
           | _ => progress Z.ltb_to_lt
           | [ |- true = false ] => exfalso
           | [ |- false = true ] => exfalso
           | [ |- False ] => nia
           end.
  Qed.
  Lemma carry_sub_is_good_value
    : (z0 mod 2 ^ k + ((z1 - if z0 <? 0 then 1 else 0) mod 2 ^ n) << k)%Z
      = (z0 + z1 << k) mod (2 ^ k * 2 ^ n).
  Proof using Type*.
    assert (0 < 2 ^ n) by auto with zarith.
    assert (0 < 2 ^ k) by auto with zarith.
    assert (0 < 2^n * 2^k) by nia.
    autorewrite with Zshift_to_pow push_Zpow.
    rewrite <- (Zmod_small ((z0 mod _) + _) (2^k * 2^n)) by (Z.div_mod_to_quot_rem; nia).
    rewrite <- !Z.mul_mod_distr_r by lia.
    rewrite !(Z.mul_comm (2^k)); pull_Zmod.
    apply f_equal2; Z.div_mod_to_quot_rem; break_match; Z.ltb_to_lt; try reflexivity;
      match goal with
      | [ q : Z |- _ = _ :> Z ]
        => first [ cut (q = -1); [ intro; subst; ring | nia ]
                 | cut (q = 0); [ intro; subst; ring | nia ]
                 | cut (q = 1); [ intro; subst; ring | nia ] ]
      end.
  Qed.
End carry_sub_is_good.

Definition carry_is_good_carry n z0 z1 k H0 H1 := proj1 (@carry_is_good n z0 z1 k H0 H1).
Definition carry_is_good_value n z0 z1 k H0 H1 := proj2 (@carry_is_good n z0 z1 k H0 H1).

Section ripple_carry_adc.
  Context {n W} {decode : decoder n W} (adc : add_with_carry W).

  Lemma ripple_carry_adc_SS k xss yss carry
    : ripple_carry_adc (k := S (S k)) adc xss yss carry
      = let '(xs, x) := eta xss in
        let '(ys, y) := eta yss in
        let '(carry, zs) := eta (ripple_carry_adc (k := S k) adc xs ys carry) in
        let '(carry, z) := eta (adc x y carry) in
        (carry, (zs, z)).
  Proof using Type. apply ripple_carry_tuple_SS. Qed.

  Local Opaque Z.of_nat.
  Global Instance ripple_carry_is_add_with_carry {k}
         {isdecode : is_decode decode}
         {is_adc : is_add_with_carry adc}
    : is_add_with_carry (ripple_carry_adc (k := k) adc).
  Proof using Type.
    destruct k as [|k].
    { constructor; simpl; intros; autorewrite with zsimplify; reflexivity. }
    { induction k as [|k IHk].
      { cbv [ripple_carry_adc ripple_carry_tuple to_list].
        constructor; simpl @fst; simpl @snd; intros;
          simpl; pull_decode; reflexivity. }
      { apply Build_is_add_with_carry'; intros x y c.
        assert (0 <= n) by (destruct x; eauto using decode_exponent_nonnegative).
        assert (2^n <> 0) by auto with zarith.
        assert (0 <= S k * n) by nia.
        rewrite !tuple_decoder_S, !ripple_carry_adc_SS by assumption.
        simplify_projections; push_decode; generalize_decode.
        erewrite carry_is_good_carry, carry_is_good_value by lia.
        autorewrite with pull_Zpow push_Zof_nat zsimplify Zshift_to_pow.
        split; apply f_equal2; nia. } }
  Qed.

End ripple_carry_adc.

Hint Extern 2 (@is_add_with_carry _ (tuple ?W ?k) (@tuple_decoder ?n _ ?decode _) (@ripple_carry_adc _ ?adc _))
=> apply (@ripple_carry_is_add_with_carry n W decode adc k) : typeclass_instances.
Hint Resolve (fun n W decode adc isdecode isadc
              => @ripple_carry_is_add_with_carry n W decode adc 2 isdecode isadc
                 : @is_add_with_carry (Z.of_nat 2 * n) (W * W) (@tuple_decoder n W decode 2) (@ripple_carry_adc W adc 2))
  : typeclass_instances.

Section ripple_carry_subc.
  Context {n W} {decode : decoder n W} (subc : sub_with_carry W).

  Lemma ripple_carry_subc_SS k xss yss carry
    : ripple_carry_subc (k := S (S k)) subc xss yss carry
      = let '(xs, x) := eta xss in
        let '(ys, y) := eta yss in
        let '(carry, zs) := eta (ripple_carry_subc (k := S k) subc xs ys carry) in
        let '(carry, z) := eta (subc x y carry) in
        (carry, (zs, z)).
  Proof using Type. apply ripple_carry_tuple_SS. Qed.

  Local Opaque Z.of_nat.
  Global Instance ripple_carry_is_sub_with_carry {k}
         {isdecode : is_decode decode}
         {is_subc : is_sub_with_carry subc}
    : is_sub_with_carry (ripple_carry_subc (k := k) subc).
  Proof using Type.
    destruct k as [|k].
    { constructor; repeat (intros [] || intro); autorewrite with simpl_tuple_decoder zsimplify; reflexivity. }
    { induction k as [|k IHk].
      { cbv [ripple_carry_subc ripple_carry_tuple to_list].
        constructor; simpl @fst; simpl @snd; intros;
          simpl; push_decode; autorewrite with zsimplify; reflexivity. }
      { apply Build_is_sub_with_carry'; intros x y c.
        assert (0 <= n) by (destruct x; eauto using decode_exponent_nonnegative).
        assert (2^n <> 0) by auto with zarith.
        assert (0 <= S k * n) by nia.
        rewrite !tuple_decoder_S, !ripple_carry_subc_SS by assumption.
        simplify_projections; push_decode; generalize_decode.
        erewrite (carry_sub_is_good_carry (S k * n)), carry_sub_is_good_value by (break_match; lia).
        autorewrite with pull_Zpow push_Zof_nat zsimplify Zshift_to_pow.
        split; apply f_equal2; nia. } }
  Qed.

End ripple_carry_subc.

Hint Extern 2 (@is_sub_with_carry _ (tuple ?W ?k) (@tuple_decoder ?n _ ?decode _) (@ripple_carry_subc _ ?subc _))
=> apply (@ripple_carry_is_sub_with_carry n W decode subc k) : typeclass_instances.
Hint Resolve (fun n W decode subc isdecode issubc
              => @ripple_carry_is_sub_with_carry n W decode subc 2 isdecode issubc
                 : @is_sub_with_carry (Z.of_nat 2 * n) (W * W) (@tuple_decoder n W decode 2) (@ripple_carry_subc W subc 2))
  : typeclass_instances.