Skip to content

Commit

Permalink
004, 016, 026, 029 (#28)
Browse files Browse the repository at this point in the history
  • Loading branch information
WeetHet authored Aug 21, 2024
1 parent edd3762 commit 6649c49
Show file tree
Hide file tree
Showing 9 changed files with 246 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
name: Test All Dafny Files

on: [push]
on:
push:
branches:
- main
jobs:
test:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v4
- name: Install Nix
uses: cachix/install-nix-action@v20
- name: Test if all dafny files are named correctly
- name: Test that all dafny files are named correctly
run: nix run .#dafny-namecheck
- name: Run Dafny on all files
run: nix run .#dafny-check
23 changes: 23 additions & 0 deletions .github/workflows/test-dafny-new.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Test New Dafny Files

on:
pull_request:
branches:
- main
jobs:
test-changed:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v4
- name: Install Nix
uses: cachix/install-nix-action@v20
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@v45
- name: Test that all dafny files are named correctly
run: nix run .#dafny-namecheck

- name: Run Dafny on new files
env:
ALL_CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }}
run: nix run .#dafny-check-new "${ALL_CHANGED_FILES}"
3 changes: 2 additions & 1 deletion .zed/settings.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{
"tab_size": 2
"tab_size": 2,
"format_on_save": "off"
}
75 changes: 75 additions & 0 deletions 004-mean_absolute_derivation.dfy
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
function sum(s: seq<real>) : real {
if |s| == 0 then 0.0 else s[0] + sum(s[1..])
}

lemma sum_prop(s: seq<real>)
requires |s| > 0
ensures sum(s) == sum(s[..|s| - 1]) + s[ |s| - 1 ]
{
if (|s| > 1) {
assert (s[1..][..|s[1..]| - 1]) == s[1..|s| - 1];
}
}

function abs(x: real) : real
ensures abs(x) >= 0.0
{
if x >= 0.0 then x else -x
}

function mean(s: seq<real>) : real
requires |s| > 0
{
sum(s) / |s| as real
}

method mean_absolute_derivation(numbers: seq<real>) returns (derivation: real)
requires |numbers| > 0
ensures var m := mean(numbers);
derivation == mean(seq(|numbers|, i requires 0 <= i < |numbers| => abs(numbers[i] - m)))
{
var s: real := 0.0;
var i := 0;
while i < |numbers|
invariant 0 <= i <= |numbers|
invariant s == sum(numbers[..i])
{
s := s + numbers[i];
assert sum(numbers[..i + 1]) == sum(numbers[..i]) + numbers[i] by {
assert numbers[..i+1][..i] == numbers[..i];
sum_prop(numbers[..i + 1]);
}
i := i + 1;
}

var m := s / |numbers| as real;
assert numbers[..|numbers|] == numbers;
assert m == mean(numbers);

var t: real := 0.0;
i := 0;

ghost var pref_seq := [];
while i < |numbers|
invariant 0 <= i <= |numbers|
invariant |pref_seq| == i
invariant pref_seq == seq(i, j requires 0 <= j < i => abs(numbers[j] - m))
invariant t == sum(pref_seq[..i])
{
ghost var pre_seq := pref_seq;
assert pre_seq[..|pre_seq|] == pre_seq[..i] == pre_seq;

pref_seq := pref_seq + [abs(numbers[i] - m)];

assert sum(pref_seq[..i + 1]) == sum(pref_seq[..i]) + pref_seq[i] by {
assert pref_seq[..i+1][..i] == pref_seq[..i];
sum_prop(pref_seq[..i + 1]);
}

t := t + abs(numbers[i] - m);
i := i + 1;
}

assert pref_seq[..|pref_seq|] == pref_seq;
derivation := t / |numbers| as real;
}
33 changes: 33 additions & 0 deletions 016-count_distinct_characters.dfy
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
function contains_char(s: string, c: char): bool
decreases |s|
requires forall i :: 0 <= i < |s| ==> 'a' <= s[i] <= 'z' || 'A' <= s[i] <= 'Z'
requires 'a' <= c <= 'z'
{
if |s| == 0 then false else s[0] == c || contains_char(s[1..], c)
}

function upper_char(c: char) : (C: char)
requires 'a' <= c <= 'z'
ensures 'A' <= C <= 'Z'
{ c - 'a' + 'A' }

method count_distinct_characters(s: string) returns (count: int)
requires forall i :: 0 <= i < |s| ==> 'a' <= s[i] <= 'z' || 'A' <= s[i] <= 'Z'
ensures count == |set c | 'a' <= c <= 'z' && contains_char(s, c)|
{
count := 0;
ghost var contained: set<char> := {};
var i := 'a';
while i <= 'z'
invariant 'a' <= i <= ('z' as int + 1) as char
invariant count == |contained|
invariant contained == set c | 'a' <= c < i && contains_char(s, c)
{
if contains_char(s, i) {
count := count + 1;
contained := contained + {i};
}
i := (i as int + 1) as char;
}
assert contained == set c | 'a' <= c <= 'z' && contains_char(s, c);
}
61 changes: 61 additions & 0 deletions 026-remove_duplicates.dfy
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
method remove_duplicates(a: seq<int>) returns (result: seq<int>)
requires forall i :: 0 <= i < |a| ==> count_rec(a, a[i]) >= 1
ensures forall i :: 0 <= i < |result| ==> count_rec(a, result[i]) == 1
ensures forall i :: 0 <= i < |a| ==> (a[i] in result <==> count_rec(a, a[i]) == 1)
{
var res: seq<int> := [];
var i := 0;
while i < |a|
invariant 0 <= i <= |a|
invariant forall j :: 0 <= j < |res| ==> count_rec(a, res[j]) == 1
invariant forall j :: 0 <= j < i ==> (a[j] in res <==> count_rec(a, a[j]) == 1)
invariant forall j :: 0 <= j < |res| ==> res[j] in a[..i]
{
var cnt := count(a, a[i]);
if cnt == 1 {
res := res + [a[i]];
}
i := i + 1;
}
result := res;
}

function count_rec(a: seq<int>, x: int): int {
if |a| == 0 then 0
else count_rec(a[1..], x) + (if a[0] == x then 1 else 0)
}

lemma count_prop(s: seq<int>, x: int)
requires |s| > 0
ensures count_rec(s, x) == count_rec(s[..|s| - 1], x) + if s[|s| - 1] == x then 1 else 0
{
if (|s| > 1) {
assert (s[1..][..|s[1..]| - 1]) == s[1..|s| - 1];
}
}

method count(a: seq<int>, x: int) returns (cnt: int)
ensures cnt == |set i | 0 <= i < |a| && a[i] == x|
ensures cnt == count_rec(a, x)
{
cnt := 0;
ghost var positions: set<int> := {};
var i := 0;
while i < |a|
invariant 0 <= i <= |a|
invariant cnt == |positions|
invariant positions == set k | 0 <= k < i && a[k] == x
invariant cnt == count_rec(a[..i], x)
{
if a[i] == x {
cnt := cnt + 1;
positions := positions + {i};
}
assert count_rec(a[..i + 1], x) == count_rec(a[..i], x) + (if a[i] == x then 1 else 0) by {
assert a[..i+1][..i] == a[..i];
count_prop(a[..i + 1], x);
}
i := i + 1;
}
assert a == a[..|a|];
}
19 changes: 19 additions & 0 deletions 029-filter_by_prefix.dfy
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
method filter_by_prefix(xs: seq<string>, p: string) returns (filtered: seq<string>)
ensures forall i :: 0 <= i < |filtered| ==> starts_with(filtered[i], p)
{
filtered := [];
var i := 0;
while i < |xs|
invariant 0 <= i <= |xs|
invariant forall j :: 0 <= j < |filtered| ==> starts_with(filtered[j], p)
{
if starts_with(xs[i], p) {
filtered := filtered + [xs[i]];
}
i := i + 1;
}
}

function starts_with(s: string, p: string): bool {
|p| == 0 || (|s| != 0 && |s| >= |p| && s[0] == p[0] && starts_with(s[1..], p[1..]))
}
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Current status:
- [x] 1. separate_paren_groups
- [x] 2. truncate
- [x] 3. below_zero
- [ ] 4. mean_absolute_derivation
- [x] 4. mean_absolute_derivation
- [x] 5. intersperse
- [x] 6. parse_nested_parens
- [x] 7. filter_by_substring
Expand All @@ -18,7 +18,7 @@ Current status:
- [x] 13. greatest_common_divisor
- [x] 14. all_prefixes
- [ ] 15. string_sequence - complex strings
- [ ] 16. count_distinct_characters
- [x] 16. count_distinct_characters
- [ ] 17. parse_music - complex strings
- [ ] 18. how_many_times - complex strings
- [ ] 19. sort_numbers - complex strings
Expand All @@ -28,10 +28,10 @@ Current status:
- [x] 23. strlen
- [x] 24. largest_divisor
- [ ] 25. factorize
- [ ] 26. remove_duplicates
- [x] 26. remove_duplicates
- [x] 27. flip_case
- [ ] 28. concatenate
- [ ] 29. filter_by_prefix
- [x] 29. filter_by_prefix
- [x] 30. get_positive
- [x] 31. is_prime
- [ ] 32. poly
Expand All @@ -41,7 +41,7 @@ Current status:
- [ ] 36. fizz_buzz
- [ ] 37. sort_even - sorting
- [ ] 38. encode_cyclic
- [ ] 39. prime_fib
- [ ] 39. ~prime_fib~ -- unbounded loop, probably impossible
- [x] 40. triples_sum_to_zero
- [ ] 41. car_race_collision
- [x] 42. incr_list
Expand Down
23 changes: 23 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
packages = {
dafny-check = pkgs.writeShellScriptBin "dafny-check" ''
DIR=''${1:-.}
file_count=$(find "$DIR" -maxdepth 1 -name "*.dfy" -printf '.' | wc -m | sed 's/ //g')
file_no=0
for f in "$DIR"/*.dfy
Expand All @@ -23,6 +24,28 @@
done
'';

dafny-check-new = pkgs.writeShellScriptBin "dafny-check" ''
file_count=0
echo "New files found:"
for f in $1; do
if [[ $f == *.dfy ]]; then
echo $f
file_count=$((file_count+1))
fi
done
echo "Staring the check"
for f in $1
do
if [[ $f == *.dfy ]]; then
file_no=$((file_no+1))
echo "Running dafny on $(basename "$f") ($file_no/$file_count)"
${pkgs.dafny}/bin/dafny verify --allow-warnings --verification-time-limit 2400 $f || exit 1
fi
done
'';

dafny-namecheck = pkgs.writeShellScriptBin "dafny-namecheck" ''
# Directory to check, use current directory if not specified
DIR=''${1:-.}
Expand Down

0 comments on commit 6649c49

Please sign in to comment.