1 ------------------------------------------------------------------------------
3 -- GNAT RUN-TIME COMPONENTS --
5 -- S Y S T E M . E X P _ M O D --
9 -- Copyright (C) 1992-2023, Free Software Foundation, Inc. --
11 -- GNAT is free software; you can redistribute it and/or modify it under --
12 -- terms of the GNU General Public License as published by the Free Soft- --
13 -- ware Foundation; either version 3, or (at your option) any later ver- --
14 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE. --
18 -- As a special exception under Section 7 of GPL version 3, you are granted --
19 -- additional permissions described in the GCC Runtime Library Exception, --
20 -- version 3.1, as published by the Free Software Foundation. --
22 -- You should have received a copy of the GNU General Public License and --
23 -- a copy of the GCC Runtime Library Exception along with this program; --
24 -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
25 -- <http://www.gnu.org/licenses/>. --
27 -- GNAT was originally developed by the GNAT team at New York University. --
28 -- Extensive contributions were provided by Ada Core Technologies Inc. --
30 ------------------------------------------------------------------------------
32 -- Preconditions, postconditions, ghost code, loop invariants and assertions
33 -- in this unit are meant for analysis only, not for run-time checking, as it
34 -- would be too costly otherwise. This is enforced by setting the assertion
37 pragma Assertion_Policy
(Pre
=> Ignore
,
40 Loop_Invariant
=> Ignore
,
43 with Ada
.Numerics
.Big_Numbers
.Big_Integers_Ghost
;
44 use Ada
.Numerics
.Big_Numbers
.Big_Integers_Ghost
;
46 package body System
.Exp_Mod
49 use System
.Unsigned_Types
;
53 procedure Lemma_Add_Mod
(X
, Y
: Big_Natural
; B
: Big_Positive
)
56 Post
=> (X
+ Y
) mod B
= ((X
mod B
) + (Y
mod B
)) mod B
;
58 procedure Lemma_Exp_Expand
(A
: Big_Integer
; Exp
: Natural)
62 (if Exp
rem 2 = 0 then
63 A
** Exp
= A
** (Exp
/ 2) * A
** (Exp
/ 2)
65 A
** Exp
= A
** (Exp
/ 2) * A
** (Exp
/ 2) * A
);
67 procedure Lemma_Exp_Mod
(A
: Big_Natural
; Exp
: Natural; B
: Big_Positive
)
70 Subprogram_Variant
=> (Decreases
=> Exp
),
71 Post
=> ((A
mod B
) ** Exp
) mod B
= (A
** Exp
) mod B
;
73 procedure Lemma_Mod_Ident
(A
: Big_Natural
; B
: Big_Positive
)
79 procedure Lemma_Mod_Mod
(A
: Big_Integer
; B
: Big_Positive
)
82 Post
=> A
mod B
mod B
= A
mod B
;
84 procedure Lemma_Mult_Div
(X
: Big_Natural
; Y
: Big_Positive
)
87 Post
=> X
* Y
/ Y
= X
;
89 procedure Lemma_Mult_Mod
(X
, Y
: Big_Natural
; B
: Big_Positive
)
92 -- The following subprogram variant can be added as soon as supported
93 -- Subprogram_Variant => (Decreases => Y),
94 Post
=> (X
* Y
) mod B
= ((X
mod B
) * (Y
mod B
)) mod B
;
96 -----------------------------
97 -- Local lemma null bodies --
98 -----------------------------
100 procedure Lemma_Mod_Ident
(A
: Big_Natural
; B
: Big_Positive
) is null;
101 procedure Lemma_Mod_Mod
(A
: Big_Integer
; B
: Big_Positive
) is null;
102 procedure Lemma_Mult_Div
(X
: Big_Natural
; Y
: Big_Positive
) is null;
108 procedure Lemma_Add_Mod
(X
, Y
: Big_Natural
; B
: Big_Positive
) is
110 procedure Lemma_Euclidean_Mod
(Q
, F
, R
: Big_Natural
) with
112 Post
=> (Q
* F
+ R
) mod F
= R
mod F
,
113 Subprogram_Variant
=> (Decreases
=> Q
);
115 -------------------------
116 -- Lemma_Euclidean_Mod --
117 -------------------------
119 procedure Lemma_Euclidean_Mod
(Q
, F
, R
: Big_Natural
) is
122 Lemma_Euclidean_Mod
(Q
- 1, F
, R
);
124 end Lemma_Euclidean_Mod
;
128 Left
: constant Big_Natural
:= (X
+ Y
) mod B
;
129 Right
: constant Big_Natural
:= ((X
mod B
) + (Y
mod B
)) mod B
;
130 XQuot
: constant Big_Natural
:= X
/ B
;
131 YQuot
: constant Big_Natural
:= Y
/ B
;
132 AQuot
: constant Big_Natural
:= (X
mod B
+ Y
mod B
) / B
;
134 if Y
/= 0 and B
> 1 then
135 pragma Assert
(X
= XQuot
* B
+ X
mod B
);
136 pragma Assert
(Y
= YQuot
* B
+ Y
mod B
);
138 (Left
= ((XQuot
+ YQuot
) * B
+ X
mod B
+ Y
mod B
) mod B
);
139 pragma Assert
(X
mod B
+ Y
mod B
= AQuot
* B
+ Right
);
140 pragma Assert
(Left
= ((XQuot
+ YQuot
+ AQuot
) * B
+ Right
) mod B
);
141 Lemma_Euclidean_Mod
(XQuot
+ YQuot
+ AQuot
, B
, Right
);
142 pragma Assert
(Left
= (Right
mod B
));
143 pragma Assert
(Left
= Right
);
147 ----------------------
148 -- Lemma_Exp_Expand --
149 ----------------------
151 procedure Lemma_Exp_Expand
(A
: Big_Integer
; Exp
: Natural) is
153 if Exp
rem 2 = 0 then
154 pragma Assert
(Exp
= Exp
/ 2 + Exp
/ 2);
156 pragma Assert
(Exp
= Exp
/ 2 + Exp
/ 2 + 1);
157 pragma Assert
(A
** Exp
= A
** (Exp
/ 2) * A
** (Exp
/ 2 + 1));
158 pragma Assert
(A
** (Exp
/ 2 + 1) = A
** (Exp
/ 2) * A
);
159 pragma Assert
(A
** Exp
= A
** (Exp
/ 2) * A
** (Exp
/ 2) * A
);
161 end Lemma_Exp_Expand
;
167 procedure Lemma_Exp_Mod
(A
: Big_Natural
; Exp
: Natural; B
: Big_Positive
)
172 Left
: constant Big_Integer
:= ((A
mod B
) ** Exp
) mod B
;
173 Right
: constant Big_Integer
:= (A
** Exp
) mod B
;
175 Lemma_Mult_Mod
(A
mod B
, (A
mod B
) ** (Exp
- 1), B
);
176 Lemma_Mod_Mod
(A
, B
);
177 Lemma_Exp_Mod
(A
, Exp
- 1, B
);
178 Lemma_Mult_Mod
(A
, A
** (Exp
- 1), B
);
180 ((A
mod B
) * (A
mod B
) ** (Exp
- 1) = (A
mod B
) ** Exp
);
181 pragma Assert
(A
* A
** (Exp
- 1) = A
** Exp
);
182 pragma Assert
(Left
= Right
);
191 procedure Lemma_Mult_Mod
(X
, Y
: Big_Natural
; B
: Big_Positive
) is
192 Left
: constant Big_Natural
:= (X
* Y
) mod B
;
193 Right
: constant Big_Natural
:= ((X
mod B
) * (Y
mod B
)) mod B
;
195 if Y
/= 0 and B
> 1 then
196 Lemma_Add_Mod
(X
* (Y
- 1), X
, B
);
197 Lemma_Mult_Mod
(X
, Y
- 1, B
);
198 Lemma_Mod_Mod
(X
, B
);
199 Lemma_Add_Mod
((X
mod B
) * ((Y
- 1) mod B
), X
mod B
, B
);
200 Lemma_Add_Mod
(Y
- 1, 1, B
);
201 pragma Assert
(((Y
- 1) mod B
+ 1) mod B
= Y
mod B
);
202 if (Y
- 1) mod B
+ 1 < B
then
203 Lemma_Mod_Ident
((Y
- 1) mod B
+ 1, B
);
204 Lemma_Mod_Mod
((X
mod B
) * (Y
mod B
), B
);
205 pragma Assert
(Left
= Right
);
207 pragma Assert
(Y
mod B
= 0);
208 pragma Assert
(Y
/ B
* B
= Y
);
209 pragma Assert
((X
* Y
) mod B
= (X
* Y
) - (X
* Y
) / B
* B
);
211 ((X
* Y
) mod B
= (X
* Y
) - (X
* (Y
/ B
) * B
) / B
* B
);
212 Lemma_Mult_Div
(X
* (Y
/ B
), B
);
213 pragma Assert
(Left
= 0);
214 pragma Assert
(Left
= Right
);
226 Right
: Natural) return Unsigned
228 Result
: Unsigned
:= 1;
229 Factor
: Unsigned
:= Left
;
230 Exp
: Natural := Right
;
232 function Mult
(X
, Y
: Unsigned
) return Unsigned
is
233 (Unsigned
(Long_Long_Unsigned
(X
) * Long_Long_Unsigned
(Y
)
234 mod Long_Long_Unsigned
(Modulus
)))
237 -- Modular multiplication. Note that we can't take advantage of the
238 -- compiler's circuit, because the modulus is not known statically.
240 -- Local ghost variables, functions and lemmas
242 M
: constant Big_Positive
:= Big
(Modulus
) with Ghost
;
244 function Equal_Modulo
(X
, Y
: Big_Integer
) return Boolean is
250 procedure Lemma_Mult
(X
, Y
: Unsigned
)
253 Post
=> Big
(Mult
(X
, Y
)) = (Big
(X
) * Big
(Y
)) mod M
254 and then Big
(Mult
(X
, Y
)) < M
;
256 procedure Lemma_Mult
(X
, Y
: Unsigned
) is null;
258 Rest
: Big_Integer
with Ghost
;
259 -- Ghost variable to hold Factor**Exp between Exp and Factor updates
262 pragma Assert
(Modulus
/= 1);
264 -- We use the standard logarithmic approach, Exp gets shifted right
265 -- testing successive low order bits and Factor is the value of the
266 -- base raised to the next power of 2.
268 -- Note: it is not worth special casing the cases of base values -1,0,+1
269 -- since the expander does this when the base is a literal, and other
270 -- cases will be extremely rare.
274 pragma Loop_Invariant
(Exp
> 0);
275 pragma Loop_Invariant
(Result
< Modulus
);
276 pragma Loop_Invariant
(Equal_Modulo
277 (Big
(Result
) * Big
(Factor
) ** Exp
, Big
(Left
) ** Right
));
278 pragma Loop_Variant
(Decreases
=> Exp
);
280 if Exp
rem 2 /= 0 then
283 = Big
(Factor
) * Big
(Factor
) ** (Exp
- 1));
284 pragma Assert
(Equal_Modulo
285 ((Big
(Result
) * Big
(Factor
)) * Big
(Factor
) ** (Exp
- 1),
286 Big
(Left
) ** Right
));
287 pragma Assert
(Big
(Factor
) >= 0);
288 Lemma_Mult_Mod
(Big
(Result
) * Big
(Factor
),
289 Big
(Factor
) ** (Exp
- 1),
291 Lemma_Mult
(Result
, Factor
);
293 Result
:= Mult
(Result
, Factor
);
295 Lemma_Mod_Ident
(Big
(Result
), Big
(Modulus
));
296 Lemma_Mod_Mod
(Big
(Factor
) ** (Exp
- 1), Big
(Modulus
));
297 Lemma_Mult_Mod
(Big
(Result
),
298 Big
(Factor
) ** (Exp
- 1),
300 pragma Assert
(Equal_Modulo
301 (Big
(Result
) * Big
(Factor
) ** (Exp
- 1),
302 Big
(Left
) ** Right
));
303 Lemma_Exp_Expand
(Big
(Factor
), Exp
- 1);
304 pragma Assert
(Exp
/ 2 = (Exp
- 1) / 2);
307 Lemma_Exp_Expand
(Big
(Factor
), Exp
);
312 Rest
:= Big
(Factor
) ** Exp
;
313 pragma Assert
(Equal_Modulo
314 (Big
(Result
) * (Rest
* Rest
), Big
(Left
) ** Right
));
315 Lemma_Exp_Mod
(Big
(Factor
) * Big
(Factor
), Exp
, Big
(Modulus
));
317 ((Big
(Factor
) * Big
(Factor
)) ** Exp
= Rest
* Rest
);
318 pragma Assert
(Equal_Modulo
319 ((Big
(Factor
) * Big
(Factor
)) ** Exp
,
321 Lemma_Mult
(Factor
, Factor
);
323 Factor
:= Mult
(Factor
, Factor
);
325 Lemma_Mod_Mod
(Rest
* Rest
, Big
(Modulus
));
326 Lemma_Mod_Ident
(Big
(Result
), Big
(Modulus
));
327 Lemma_Mult_Mod
(Big
(Result
), Rest
* Rest
, Big
(Modulus
));
328 pragma Assert
(Big
(Factor
) >= 0);
329 Lemma_Mult_Mod
(Big
(Result
), Big
(Factor
) ** Exp
,
331 pragma Assert
(Equal_Modulo
332 (Big
(Result
) * Big
(Factor
) ** Exp
, Big
(Left
) ** Right
));
335 pragma Assert
(Big
(Result
) = Big
(Left
) ** Right
mod Big
(Modulus
));