1 ------------------------------------------------------------------------------
3 -- GNAT RUN-TIME COMPONENTS --
5 -- S Y S T E M . I M A G E _ I --
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 with Ada
.Numerics
.Big_Numbers
.Big_Integers_Ghost
;
33 use Ada
.Numerics
.Big_Numbers
.Big_Integers_Ghost
;
37 package body System
.Image_I
is
39 -- Ghost code, loop invariants and assertions in this unit are meant for
40 -- analysis only, not for run-time checking, as it would be too costly
41 -- otherwise. This is enforced by setting the assertion policy to Ignore.
43 pragma Assertion_Policy
(Ghost
=> Ignore
,
44 Loop_Invariant
=> Ignore
,
46 Assert_And_Cut
=> Ignore
,
49 Subprogram_Variant
=> Ignore
);
51 subtype Non_Positive
is Int
range Int
'First .. 0;
53 function Uns_Of_Non_Positive
(T
: Non_Positive
) return Uns
is
54 (if T
= Int
'First then Uns
(Int
'Last) + 1 else Uns
(-T
));
61 Pre
=> P
< Integer'Last
62 and then S
'Last < Integer'Last
63 and then S
'First <= P
+ 1
64 and then S
'First <= S
'Last
65 and then P
<= S
'Last - Unsigned_Width_Ghost
+ 1,
66 Post
=> S
(S
'First .. P
'Old) = S
'Old (S
'First .. P
'Old)
67 and then P
in P
'Old + 1 .. S
'Last
68 and then UP
.Only_Decimal_Ghost
(S
, From
=> P
'Old + 1, To
=> P
)
69 and then UP
.Scan_Based_Number_Ghost
(S
, From
=> P
'Old + 1, To
=> P
)
70 = UP
.Wrap_Option
(Uns_Of_Non_Positive
(T
));
71 -- Set digits of absolute value of T, which is zero or negative. We work
72 -- with the negative of the value so that the largest negative number is
73 -- not a special case.
75 package Unsigned_Conversion
is new Unsigned_Conversions
(Int
=> Uns
);
77 function Big
(Arg
: Uns
) return Big_Integer
renames
78 Unsigned_Conversion
.To_Big_Integer
;
80 function From_Big
(Arg
: Big_Integer
) return Uns
renames
81 Unsigned_Conversion
.From_Big_Integer
;
83 Big_10
: constant Big_Integer
:= Big
(10) with Ghost
;
89 procedure Lemma_Non_Zero
(X
: Uns
)
95 procedure Lemma_Div_Commutation
(X
, Y
: Uns
)
99 Post
=> Big
(X
) / Big
(Y
) = Big
(X
/ Y
);
101 procedure Lemma_Div_Twice
(X
: Big_Natural
; Y
, Z
: Big_Positive
)
104 Post
=> X
/ Y
/ Z
= X
/ (Y
* Z
);
106 ---------------------------
107 -- Lemma_Div_Commutation --
108 ---------------------------
110 procedure Lemma_Non_Zero
(X
: Uns
) is null;
111 procedure Lemma_Div_Commutation
(X
, Y
: Uns
) is null;
113 ---------------------
114 -- Lemma_Div_Twice --
115 ---------------------
117 procedure Lemma_Div_Twice
(X
: Big_Natural
; Y
, Z
: Big_Positive
) is
118 XY
: constant Big_Natural
:= X
/ Y
;
119 YZ
: constant Big_Natural
:= Y
* Z
;
120 XYZ
: constant Big_Natural
:= X
/ Y
/ Z
;
121 R
: constant Big_Natural
:= (XY
rem Z
) * Y
+ (X
rem Y
);
123 pragma Assert
(X
= XY
* Y
+ (X
rem Y
));
124 pragma Assert
(XY
= XY
/ Z
* Z
+ (XY
rem Z
));
125 pragma Assert
(X
= XYZ
* YZ
+ R
);
126 pragma Assert
((XY
rem Z
) * Y
<= (Z
- 1) * Y
);
127 pragma Assert
(R
<= YZ
- 1);
128 pragma Assert
(X
/ YZ
= (XYZ
* YZ
+ R
) / YZ
);
129 pragma Assert
(X
/ YZ
= XYZ
+ R
/ YZ
);
136 procedure Image_Integer
141 pragma Assert
(S
'First = 1);
143 procedure Prove_Value_Integer
147 and then S
'Last < Integer'Last
148 and then P
in 2 .. S
'Last
149 and then S
(1) in ' ' |
'-'
150 and then (S
(1) = '-') = (V
< 0)
151 and then UP
.Only_Decimal_Ghost
(S
, From
=> 2, To
=> P
)
152 and then UP
.Scan_Based_Number_Ghost
(S
, From
=> 2, To
=> P
)
153 = UP
.Wrap_Option
(IP
.Abs_Uns_Of_Int
(V
)),
154 Post
=> not System
.Val_Spec
.Only_Space_Ghost
(S
, 1, P
)
155 and then IP
.Is_Integer_Ghost
(S
(1 .. P
))
156 and then IP
.Is_Value_Integer_Ghost
(S
(1 .. P
), V
);
157 -- Ghost lemma to prove the value of Value_Integer from the value of
158 -- Scan_Based_Number_Ghost and the sign on a decimal string.
160 -------------------------
161 -- Prove_Value_Integer --
162 -------------------------
164 procedure Prove_Value_Integer
is
165 Str
: constant String := S
(1 .. P
);
167 pragma Assert
(Str
'First = 1);
168 pragma Assert
(Str
(2) /= ' ');
170 (UP
.Only_Decimal_Ghost
(Str
, From
=> 2, To
=> P
));
171 UP
.Prove_Scan_Based_Number_Ghost_Eq
(S
, Str
, From
=> 2, To
=> P
);
173 (UP
.Scan_Based_Number_Ghost
(Str
, From
=> 2, To
=> P
)
174 = UP
.Wrap_Option
(IP
.Abs_Uns_Of_Int
(V
)));
175 IP
.Prove_Scan_Only_Decimal_Ghost
(Str
, V
);
176 end Prove_Value_Integer
;
178 -- Start of processing for Image_Integer
182 pragma Annotate
(CodePeer
, False_Positive
, "test always false",
183 "V can be positive");
186 pragma Assert
(P
< S
'Last);
190 pragma Assert
(P
< S
'Last - 1);
194 P_Prev
: constant Integer := P
with Ghost
;
195 Offset
: constant Positive := (if V
>= 0 then 1 else 2) with Ghost
;
197 Set_Image_Integer
(V
, S
, P
);
199 pragma Assert
(P_Prev
+ Offset
= 2);
201 pragma Assert
(if V
>= 0 then S
(1) = ' ');
202 pragma Assert
(S
(1) in ' ' |
'-');
216 Nb_Digits
: Natural := 0;
217 Value
: Non_Positive
:= T
;
219 -- Local ghost variables
221 Pow
: Big_Positive
:= 1 with Ghost
;
222 S_Init
: constant String := S
with Ghost
;
223 Uns_T
: constant Uns
:= Uns_Of_Non_Positive
(T
) with Ghost
;
224 Uns_Value
: Uns
:= Uns_Of_Non_Positive
(Value
) with Ghost
;
225 Prev_Value
: Uns
with Ghost
;
226 Prev_S
: String := S
with Ghost
;
228 -- Local ghost lemmas
230 procedure Prove_Character_Val
(RU
: Uns
; RI
: Non_Positive
)
233 Post
=> RU
rem 10 in 0 .. 9
234 and then -(RI
rem 10) in 0 .. 9
235 and then Character'Val (48 + RU
rem 10) in '0' .. '9'
236 and then Character'Val (48 - RI
rem 10) in '0' .. '9';
237 -- Ghost lemma to prove the value of a character corresponding to the
240 procedure Prove_Euclidian
(Val
, Quot
, Rest
: Uns
)
243 Pre
=> Quot
= Val
/ 10
244 and then Rest
= Val
rem 10,
245 Post
=> Uns
'Last - Rest
>= 10 * Quot
and then Val
= 10 * Quot
+ Rest
;
246 -- Ghost lemma to prove the relation between the quotient/remainder of
247 -- division by 10 and the initial value.
249 procedure Prove_Hexa_To_Unsigned_Ghost
(RU
: Uns
; RI
: Int
)
253 and then RI
in 0 .. 9,
254 Post
=> UP
.Hexa_To_Unsigned_Ghost
255 (Character'Val (48 + RU
)) = RU
256 and then UP
.Hexa_To_Unsigned_Ghost
257 (Character'Val (48 + RI
)) = Uns
(RI
);
258 -- Ghost lemma to prove that Hexa_To_Unsigned_Ghost returns the source
259 -- figure when applied to the corresponding character.
261 procedure Prove_Scan_Iter
263 V
, Prev_V
, Res
: Uns
;
268 S
'First = Prev_S
'First and then S
'Last = Prev_S
'Last
269 and then S
'Last < Natural'Last and then
270 Max
in S
'Range and then P
in S
'First .. Max
and then
271 (for all I
in P
+ 1 .. Max
=> Prev_S
(I
) in '0' .. '9')
272 and then (for all I
in P
+ 1 .. Max
=> Prev_S
(I
) = S
(I
))
273 and then S
(P
) in '0' .. '9'
274 and then V
<= Uns
'Last / 10
275 and then Uns
'Last - UP
.Hexa_To_Unsigned_Ghost
(S
(P
))
278 V
* 10 + UP
.Hexa_To_Unsigned_Ghost
(S
(P
))
280 (if P
= Max
then Prev_V
= Res
281 else UP
.Scan_Based_Number_Ghost
286 Acc
=> Prev_V
) = UP
.Wrap_Option
(Res
)),
288 (for all I
in P
.. Max
=> S
(I
) in '0' .. '9')
289 and then UP
.Scan_Based_Number_Ghost
294 Acc
=> V
) = UP
.Wrap_Option
(Res
);
295 -- Ghost lemma to prove that Scan_Based_Number_Ghost is preserved
296 -- through an iteration of the loop.
298 procedure Prove_Uns_Of_Non_Positive_Value
301 Pre
=> Uns_Value
= Uns_Of_Non_Positive
(Value
),
302 Post
=> Uns_Value
/ 10 = Uns_Of_Non_Positive
(Value
/ 10)
303 and then Uns_Value
rem 10 = Uns_Of_Non_Positive
(Value
rem 10);
304 -- Ghost lemma to prove that the relation between Value and its unsigned
305 -- version is preserved.
307 -----------------------------
308 -- Local lemma null bodies --
309 -----------------------------
311 procedure Prove_Character_Val
(RU
: Uns
; RI
: Non_Positive
) is null;
312 procedure Prove_Euclidian
(Val
, Quot
, Rest
: Uns
) is null;
313 procedure Prove_Hexa_To_Unsigned_Ghost
(RU
: Uns
; RI
: Int
) is null;
314 procedure Prove_Uns_Of_Non_Positive_Value
is null;
316 ---------------------
317 -- Prove_Scan_Iter --
318 ---------------------
320 procedure Prove_Scan_Iter
322 V
, Prev_V
, Res
: Uns
;
325 pragma Unreferenced
(Res
);
327 UP
.Lemma_Scan_Based_Number_Ghost_Step
334 UP
.Prove_Scan_Based_Number_Ghost_Eq
335 (Prev_S
, S
, P
+ 1, Max
, 10, Prev_V
);
337 UP
.Lemma_Scan_Based_Number_Ghost_Base
346 -- Start of processing for Set_Digits
349 pragma Assert
(P
>= S
'First - 1 and P
< S
'Last);
350 -- No check is done since, as documented in the Set_Image_Integer
351 -- specification, the caller guarantees that S is long enough to
354 -- First we compute the number of characters needed for representing
357 Lemma_Div_Commutation
(Uns_Of_Non_Positive
(Value
), 10);
358 Lemma_Div_Twice
(Big
(Uns_Of_Non_Positive
(T
)),
359 Big_10
** Nb_Digits
, Big_10
);
360 Prove_Uns_Of_Non_Positive_Value
;
363 Nb_Digits
:= Nb_Digits
+ 1;
365 Uns_Value
:= Uns_Value
/ 10;
368 pragma Loop_Invariant
(Uns_Value
= Uns_Of_Non_Positive
(Value
));
369 pragma Loop_Invariant
(Nb_Digits
in 1 .. Unsigned_Width_Ghost
- 1);
370 pragma Loop_Invariant
(Pow
= Big_10
** Nb_Digits
);
371 pragma Loop_Invariant
(Big
(Uns_Value
) = Big
(Uns_T
) / Pow
);
372 pragma Loop_Variant
(Increases
=> Value
);
376 Lemma_Non_Zero
(Uns_Value
);
377 pragma Assert
(Pow
<= Big
(Uns
'Last));
381 Uns_Value
:= Uns_Of_Non_Positive
(T
);
384 pragma Assert
(Uns_Value
= From_Big
(Big
(Uns_T
) / Big_10
** 0));
386 -- We now populate digits from the end of the string to the beginning
387 for J
in reverse 1 .. Nb_Digits
loop
388 Lemma_Div_Commutation
(Uns_Value
, 10);
389 Lemma_Div_Twice
(Big
(Uns_T
), Big_10
** (Nb_Digits
- J
), Big_10
);
390 Prove_Character_Val
(Uns_Value
, Value
);
391 Prove_Hexa_To_Unsigned_Ghost
(Uns_Value
rem 10, -(Value
rem 10));
392 Prove_Uns_Of_Non_Positive_Value
;
394 Prev_Value
:= Uns_Value
;
397 Uns_Value
:= Uns_Value
/ 10;
399 S
(P
+ J
) := Character'Val (48 - (Value
rem 10));
405 Rest
=> UP
.Hexa_To_Unsigned_Ghost
(S
(P
+ J
)));
408 (S
, Prev_S
, Uns_Value
, Prev_Value
, Uns_T
, P
+ J
, P
+ Nb_Digits
);
410 pragma Loop_Invariant
(Uns_Value
= Uns_Of_Non_Positive
(Value
));
411 pragma Loop_Invariant
(Uns_Value
<= Uns
'Last / 10);
412 pragma Loop_Invariant
413 (for all K
in S
'First .. P
=> S
(K
) = S_Init
(K
));
414 pragma Loop_Invariant
415 (UP
.Only_Decimal_Ghost
(S
, P
+ J
, P
+ Nb_Digits
));
416 pragma Loop_Invariant
417 (for all K
in P
+ J
.. P
+ Nb_Digits
=> S
(K
) in '0' .. '9');
418 pragma Loop_Invariant
(Pow
= Big_10
** (Nb_Digits
- J
+ 1));
419 pragma Loop_Invariant
(Big
(Uns_Value
) = Big
(Uns_T
) / Pow
);
420 pragma Loop_Invariant
421 (UP
.Scan_Based_Number_Ghost
427 = UP
.Wrap_Option
(Uns_T
));
430 pragma Assert
(Big
(Uns_Value
) = Big
(Uns_T
) / Big_10
** (Nb_Digits
));
431 pragma Assert
(Uns_Value
= 0);
433 (UP
.Scan_Based_Number_Ghost
439 = UP
.Wrap_Option
(Uns_T
));
444 -----------------------
445 -- Set_Image_Integer --
446 -----------------------
448 procedure Set_Image_Integer
455 Set_Digits
(-V
, S
, P
);
458 pragma Assert
(P
>= S
'First - 1 and P
< S
'Last);
459 -- No check is done since, as documented in the specification,
460 -- the caller guarantees that S is long enough to hold the result.
463 Set_Digits
(V
, S
, P
);
465 end Set_Image_Integer
;