Verifying a function with a static array variable in VST - verifiable-c

I'd like to prove the correctness of this function from libsecp256k1:
static int secp256k1_ctz64_var_debruijn(uint64_t x) {
static const uint8_t debruijn[64] = {
0, 1, 2, 53, 3, 7, 54, 27, 4, 38, 41, 8, 34, 55, 48, 28,
62, 5, 39, 46, 44, 42, 22, 9, 24, 35, 59, 56, 49, 18, 29, 11,
63, 52, 6, 26, 37, 40, 33, 47, 61, 45, 43, 21, 23, 58, 17, 10,
51, 25, 36, 32, 60, 20, 57, 16, 50, 31, 19, 15, 30, 14, 13, 12
};
return debruijn[((x & -x) * 0x022FDD63CC95386D) >> 58];
}
My specification is
Definition secp256k1_ctz64_var_debruijn_spec : ident * funspec :=
DECLARE _secp256k1_ctz64_var_debruijn
WITH a : Z
PRE [ tulong ]
PROP(0 <= a < Int64.modulus)
PARAMS(Vlong (Int64.repr a))
SEP()
POST [ tint ]
PROP()
RETURN(Vint (Int.repr (Z.of_nat (ctz a))))
SEP().
for my own custom implementation of ctz : Z -> nat.
However when I go to prove the associated correctness lemma:
Lemma body_secp256k1_umul128: semax_body Vprog Gprog f_secp256k1_ctz64_var_debruijn secp256k1_ctz64_var_debruijn_spec.
Proof.
start_function.
forward.
I get an error:
Tactic failure: Cannot evaluate right-hand-side expression (sometimes this is caused by missing LOCALs in your precondition) (level 995).
My goal context is
1 goal
Espec : OracleKind
a : Z
Delta_specs : Maps.PTree.t funspec
Delta := abbreviate : tycontext
H : and (Z.le 0 a) (Z.lt a Int64.modulus)
POSTCONDITION := abbreviate : ret_assert
MORE_COMMANDS := abbreviate : statement
______________________________________(1/1)
semax Delta
(PROPx nil
(LOCALx (cons (temp _x (Vlong (Int64.repr a))) nil) (SEPx nil)))
(Ssequence
(Sset _t'1
(Ederef
(Ebinop Oadd (Evar _debruijn (tarray tuchar 64))
(Ebinop Oshr
(Ebinop Omul
(Ebinop Oand (Etempvar _x tulong)
(Eunop Oneg (Etempvar _x tulong) tulong) tulong)
(Econst_long (Int64.repr 157587932685088877) tlong)
tulong) (Econst_int (Int.repr 58) tint) tulong)
(tptr tuchar)) tuchar)) MORE_COMMANDS) POSTCONDITION
Notably the debruijn array and its content are absent from the context. Am I supposed to be adding v_debruijn to my specification in some manner when a function has a local static variable like this?

Though it's defined locally, debruijn is treated as a global variable in the clightgen generated Coq file.
So it seems like modifying your specification in the following way should at least get you started:
Definition secp256k1_ctz64_var_debruijn_spec : ident * funspec :=
DECLARE _secp256k1_ctz64_var_debruijn
WITH a : Z, gv : globals
PRE [ tulong ]
PROP(0 <= a < Int64.modulus)
PARAMS(Vlong (Int64.repr a))
GLOBALS(gv)
SEP(data_at Ers (tarray tuchar 64) (map (fun x => Vint (Int.repr x)) debruijn) (gv _debruijn))
POST [ tint ]
PROP()
RETURN(Vint (Int.repr (Z.of_nat (ctz a))))
SEP(data_at Ers (tarray tuchar 64) (map (fun x => Vint (Int.repr x)) debruijn) (gv _debruijn)).
where debruijn in the above definition is the appropriate list Z associated with your array.

Supplementary to TJ Machado's answer, the data for the debruijn array is stored in gvar_init v_debruijn in a somewhat unusual data structure specialized for static initialization. I haven't found a good canonical way to turn this into a suitable data_at expression, but I did manage to hack something together for my purposes:
Definition debruijn64_array (sh: share) (gv: globals) : mpred :=
Eval cbn in
let
is_all_init_int8 := fix is_all_init_int8 (l : list init_data) :=
match l with
| [] => True
| Init_int8 _ :: l' => is_all_init_int8 l'
| _ => False
end
in let
uninit_int8s := fix uninit_int8s (l: list init_data) :
is_all_init_int8 l -> list int :=
match l with
| [] => fun _ => []
| x :: l' =>
match x with
| Init_int8 i => fun pf => i :: uninit_int8s l' pf
| _ => False_rec (list int)
end
end
in data_at sh (gvar_info v_debruijn)
(map Vint (uninit_int8s (gvar_init v_debruijn) I)) (gv _debruijn).
My final specification is:
Definition secp256k1_ctz64_var_debruijn_spec : ident * funspec :=
DECLARE _secp256k1_ctz64_var_debruijn
WITH a : Z, sh_debruijn : share, gv : globals
PRE [ tulong ]
PROP(0 <= a < Int64.modulus; readable_share sh_debruijn)
PARAMS(Vlong (Int64.repr a))
GLOBALS(gv)
SEP(debruijn64_array sh_debruijn gv)
POST [ tint ]
PROP()
RETURN(Vint (Int.repr (Z.of_nat (ctz a))))
SEP(debruijn64_array sh_debruijn gv).

Related

Why can't I aplly anonymous functions to lists?

So I'm trying to learn Elixir (I have a background o F# and Haskell) and I'm having difficulties understanging what is going on in my code:
fizz_buzz = fn
(0, 0, _) -> "FizzBuzz"
(0, _, _) -> "Fizz"
(_, 0, _) -> "Buzz"
(_, _, c) -> c
end
fizz_buzz_rem = fn n -> fizz_buzz.(rem(n, 3), rem(n, 5), n) end
# This works
IO.puts(fizz_buzz_rem.(10))
IO.puts(fizz_buzz_rem.(11))
IO.puts(fizz_buzz_rem.(12))
IO.puts(fizz_buzz_rem.(13))
IO.puts(fizz_buzz_rem.(14))
IO.puts(fizz_buzz_rem.(15))
IO.puts(fizz_buzz_rem.(16))
IO.puts(fizz_buzz_rem.(17))
IO.puts("----------------")
inputs =
10..17
|> Enum.to_list
# Doesn't work
inputs
|> Enum.map(fizz_buzz_rem)
|> IO.puts
IO.puts("----------------")
# Doesn't work
inputs
|> Enum.map(fn n -> fizz_buzz.(rem(n, 3), rem(n, 5), n) end)
|> IO.puts
IO.puts("----------------")
manual_inputs = [10, 11, 12, 13, 14, 15, 16, 17]
# Doesn't work
manual_inputs
|> Enum.map(fizz_buzz_rem)
|> IO.puts
IO.puts("----------------")
# Doesn't work
manual_inputs
|> Enum.map(fn n -> fizz_buzz.(rem(n, 3), rem(n, 5), n) end)
|> IO.puts
IO.puts("----------------")
# The idiotic way (that doesn't work)
result = [
fizz_buzz_rem.(10),
fizz_buzz_rem.(11),
fizz_buzz_rem.(12),
fizz_buzz_rem.(13),
fizz_buzz_rem.(14),
fizz_buzz_rem.(15),
fizz_buzz_rem.(16),
fizz_buzz_rem.(17),
]
IO.puts result
# ???????????
When I run elixir ex_02.exs the output is:
Buzz
FizzBuzz
Buzz
11
Fizz
13
14
FizzBuzz
16
17
----------------
Buzz
FizzBuzz
----------------
Buzz
FizzBuzz
----------------
Buzz
FizzBuzz
----------------
Buzz
FizzBuzz
So as you can see when I apply the anonymous function to each value individually I get the right answer bu when I try to use ranges, maps and even apply the function to each element of a list manually I end up with the wrong result.
What am I getting wrong about aplying a anonymous function to a list in elixir?
If you use IO.inspect instead of IO.puts, you can see what's going on:
["Buzz", 11, "Fizz", 13, 14, "FizzBuzz", 16, 17]
Your fizzbuzz function returns either a string or an integer, depending on the input. IO.puts treats integers differently depending on whether they're in a list:
iex(1)> IO.puts(65)
65
:ok
iex(2)> IO.puts([65])
A
:ok
So in your code, IO.puts actually prints the control codes corresponding to the integers 11, 13, 14, 16 and 17. In my terminal it shows up as:
Buzz^KFizz^M^NFizzBuzz^P^Q
You can fix this by making your function always return strings:
fizz_buzz = fn
(0, 0, _) -> "FizzBuzz"
(0, _, _) -> "Fizz"
(_, 0, _) -> "Buzz"
(_, _, c) -> "#{c}"
end

Handle DateTime overflow in JULIA

I know that Javascript Date() function can handle date entry overflows. But in Julia I get Error.
Is there any way to handle overflows automatically?
DateTime(2020, 4, 22, 15, 43, 67) # ----> 2020-4-22T15:44:07
DateTime(2020, 12, 31, 23, 59, 60) # ----> 2021-1-1T00:00:00
I find the default behavior of throwing an error useful. If you want to allow overflows you can define your own function for this eg. like this:
julia> MyDateTime(y, m, d, h, mi, s) =
+(DateTime(0), Year(y), Month(m-1), Day(d-1),
Hour(h), Minute(mi), Second(s))
MyDateTime (generic function with 1 method)
julia> MyDateTime(2020, 4, 22, 15, 43, 67) # ----> 2020-4-22T15:44:07
2020-04-22T15:44:07
julia> MyDateTime(2020, 12, 31, 23, 59, 60) # ----> 2021-1-1T00:00:00
2021-01-01T00:00:00
Note that the order of operations matters there - we first advance year, then month, etc. (as e.g. the effect of advancing time by one second may depend on the month, year and day):
julia> MyDateTime(2020, 2, 28, 23, 59, 60)
2020-02-29T00:00:00
julia> MyDateTime(2021, 2, 28, 23, 59, 60)
2021-03-01T00:00:00
(this can get especially tricky if you have very large and invalid values of month, day etc.)

Functional programming functions using 2 lists in D

If I have 2 lists:
list1 = [1, 2, 3, 4];
list2 = [10, 25, 35, 58];
and I want to get a list which has products of corresponding elements of 2 lists;
In Python one can do:
outlist = list(map(lambda a,b: a*b, list1, list2))
However, in D, I know of following method:
import std.stdio;
void main(){
auto list1 = [1, 2, 3, 4];
auto list2 = [10, 25, 35, 58];
int[] outlist;
foreach(i, item; list1){
outlist ~= item*list2[i];
}
writeln(outlist);
}
My questions are:
Q1: Can one keep both lists as argument of foreach?
Q2: How to multiply corresponding elements of 2 lists using map function?
Thanks for your insight.
The key is to use zip to combine the elements of the two lists (or dynamic arrays as they are known in D) to a single array of tuples before applying map or foreach. The tuple elements can be accessed with a zero based index (i.e. a[0] and a[1] in this example).
import std.algorithm.iteration : map;
import std.range : zip;
import std.stdio : writeln;
void main() {
auto list1 = [1, 2, 3, 4];
auto list2 = [10, 25, 35, 58];
// Question #2
auto list3 = zip(list1, list2).map!(a => a[0] * a[1]);
writeln(list3);
// Question #1
typeof(list1) list4;
foreach(a; zip(list1, list2)) {
list4 ~= a[0] * a[1];
}
writeln(list4);
}
The code above prints twice:
[10, 50, 105, 232]
as expected.

Fixing improperly referenced slices in an array replacement

The following go code doesn't compile, because (I believe) there is a mistake around the way pointers are being referenced.
In particular, The error message is
prog.go:13: cannot use append((*x)[:remove], (*x)[remove + 1:]...) (type []int) as type *[]int in assignment
Here is an abstracted and simplified version of the code which results in this error message.
package main
import "fmt"
func main() {
x := &[]int{11, 22, 33, 44, 55, 66, 77, 88, 99}
for i, addr := range *x {
if addr == 22 {
for len(*x) > 5 {
remove := (i + 1) % len(*x)
x = append((*x)[:remove], (*x)[remove+1:]...)
}
break
}
}
fmt.Println(x)
}
You're not using an array here, you're using a slice. Generally, you don't want to handle a pointer to a slice since it can get awkward, and the pointer is needed in very few cases.
To fix your error, dereference x:
*x = append((*x)[:remove], (*x)[remove+1:]...)
But you should probably be using the slice value directly, so that no dereferences are required:
x := []int{11, 22, 33, 44, 55, 66, 77, 88, 99}

TreeForm without overlap

I sometimes run into the problem of labels in TreeForm being unreadable because of overlap. An example is below, can anyone see a way to get rid of overlap?
{{4, 5, 6}, {{{2, 4, 5, 6}, {{{1, 2, 4}, {}}, {{2, 3, 6}, {}}}}, {{4,
5, 6, 8}, {{{4, 7, 8}, {}}, {{6, 8, 9}, {}}}}}} // TreeForm
(source: yaroslavvb.com)
Belisarius' solution helps with overlap, but loses Tooltips, ie compare with
TreeForm[Hold[
GraphPlotHighlight[edges : {((_ -> _) | {_ -> _, _}) ...},
hl : {___} : {}, opts : OptionsPattern[]] :=
Module[{verts, coords, g, sub}, 5]]]
(source: yaroslavvb.com)
Answer update 11/12
I ended up using code below (belisarius' code with a minor fix)
myTreeForm[exp_] :=
Module[{tooltipText, i},
tooltipText =
Cases[Cases[MakeBoxes[TreeForm#exp, StandardForm],
TooltipBox[x__] -> x, 7, Heads -> True],
TagBox[x__, y__] -> DisplayForm[First#{x}], Heads -> True];
i = 0;
TreeForm[exp,
VertexRenderingFunction -> ({Tooltip[
Inset[Rasterize[Text[" " <> ToString##2 <> " "],
Background -> LightBlue], #1], tooltipText[[i++]]]} &)]];
I did this before, but never generalized the result.
rectOffset = {.25,.1};
fontSize = 10
TreeForm[list,
VertexRenderingFunction -> ({White, EdgeForm[Black],
Rectangle[#1 - rectOffset, #1 + rectOffset], Black,
Text[ Style[#2, fontSize], #1]} &)]
Edit With Tooltips
Using a "different approach"
Code is dirty, sorry no time to clean it up right now
rectOffset = {.33, .1};
fontSize = 9;
p = Cases[
Cases[MakeBoxes[TreeForm#list, StandardForm], TooltipBox[x__] -> x,
7, Heads -> True], TagBox[x__, y__] -> DisplayForm[First#{x}],
Heads -> True];
i = 0;
TreeForm[list,
VertexRenderingFunction -> ({White, EdgeForm[Black],
Rectangle[#1 - rectOffset, #1 + rectOffset], Black,
Tooltip[Text[Style[#2, fontSize], #1], p[[i++]]]} &)]
Output
Edit 2
I think this version is better:
Clear["Global`*"];
list = Hold[
GraphPlotHighlight[edges : {((_ -> _) | {_ -> _, _}) ...},
hl : {___} : {}, opts : OptionsPattern[]] :=
Module[{verts, coords, g, sub}, 5]];
myTreeForm[exp_] :=
Module[{ps, tooltipText, i},
ps[text_] := Rasterize[Text[Style[text]], "RasterSize"];
tooltipText =
Cases[Cases[MakeBoxes[TreeForm#list, StandardForm],
TooltipBox[x__] -> x, 7, Heads -> True],
TagBox[x__, y__] -> DisplayForm[First#{x}], Heads -> True];
i = 0;
TreeForm[list,
EdgeRenderingFunction -> ({Red, Line[#1]} &),
VertexRenderingFunction -> ({White, EdgeForm[Black], {}, Black,
Tooltip[
Inset[Rasterize[Text[" " <> ToString##2 <> " "],
Background -> LightBlue], #1], tooltipText[[i++]]]} &)]
];
list // myTreeForm
Output:
Edit 4 ... and last one
Cleaned up code, remove spurious functions and variables that were there just to complicate things:
myTreeForm[list_] := Module[{tooltipText, i},
tooltipText =
Cases[Cases[MakeBoxes[TreeForm#list, StandardForm],
TooltipBox[x__] -> x, 7, Heads -> True],
TagBox[x__, y__] -> DisplayForm[First#{x}], Heads -> True];
i = 0;
TreeForm[list,
VertexRenderingFunction ->
({Tooltip[Inset[Rasterize[Text[" " <> ToString##2 <> " "],
Background -> LightBlue], #1], tooltipText[[i++]]]} &)
]
];
HTH!
It looks as if the option VertexCoordinateRules may be your best hope.

Resources