From 2de9a2e5e50692620af23ad53e01f8440c8f9b6a Mon Sep 17 00:00:00 2001 From: Stanislav Alekseev <43210583+WeetHet@users.noreply.github.com> Date: Fri, 9 Aug 2024 14:26:04 +0300 Subject: [PATCH] 96, 97, 100, 104, 110, 118, 122, 127 (#8) --- .gitignore | 1 + 096-count_up_to.dfy | 27 +++++++++++++ 097-multiply.dfy | 14 +++++++ 100-make_a_pile.dfy | 22 +++++++++++ 104-unique_digits.dfy | 59 +++++++++++++++++++++++++++++ 110-exchange.dfy | 24 ++++++++++++ 118-get_closest_vowel.dfy | 37 ++++++++++++++++++ 122-add_elements.dfy | 79 +++++++++++++++++++++++++++++++++++++++ 127-intersection.dfy | 50 +++++++++++++++++++++++++ README.md | 14 +++---- 10 files changed, 320 insertions(+), 7 deletions(-) create mode 100644 096-count_up_to.dfy create mode 100644 097-multiply.dfy create mode 100644 100-make_a_pile.dfy create mode 100644 104-unique_digits.dfy create mode 100644 110-exchange.dfy create mode 100644 118-get_closest_vowel.dfy create mode 100644 122-add_elements.dfy create mode 100644 127-intersection.dfy diff --git a/.gitignore b/.gitignore index 79b5594..c687487 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ **/.DS_Store +.vscode \ No newline at end of file diff --git a/096-count_up_to.dfy b/096-count_up_to.dfy new file mode 100644 index 0000000..c428815 --- /dev/null +++ b/096-count_up_to.dfy @@ -0,0 +1,27 @@ +predicate IsPrime(n: int) +{ + n > 1 && forall k :: 2 <= k < n ==> n % k != 0 +} + +method CountUpTo(n: int) returns (primes: seq) + requires n >= 0 + ensures forall i :: 0 <= i < |primes| ==> IsPrime(primes[i]) + ensures forall i :: 0 <= i < |primes| ==> primes[i] < n + ensures forall p :: 2 <= p < n && IsPrime(p) <==> p in primes +{ + primes := []; + if n <= 2 { return; } + var i := 2; + while i < n + invariant 2 <= i <= n + invariant forall j :: 0 <= j < |primes| ==> IsPrime(primes[j]) + invariant forall j :: 0 <= j < |primes| ==> 2 <= primes[j] < i + invariant forall p :: 2 <= p < i && IsPrime(p) <==> p in primes + invariant forall j, k :: 0 <= j < k < |primes| ==> primes[j] < primes[k] + { + if IsPrime(i) { + primes := primes + [i]; + } + i := i + 1; + } +} \ No newline at end of file diff --git a/097-multiply.dfy b/097-multiply.dfy new file mode 100644 index 0000000..c5dadca --- /dev/null +++ b/097-multiply.dfy @@ -0,0 +1,14 @@ +function last_digit(n: int): int + ensures n >= 0 ==> last_digit(n) == n % 10 + ensures n < 0 ==> last_digit(n) == (-n) % 10 +{ + if n < 0 then (-n) % 10 else n % 10 +} + +method multiply(a: int, b: int) returns (c: int) + requires a >= 0 + requires b >= 0 + ensures c == last_digit(a) * last_digit(b) +{ + c := last_digit(a) * last_digit(b); +} \ No newline at end of file diff --git a/100-make_a_pile.dfy b/100-make_a_pile.dfy new file mode 100644 index 0000000..df8cb87 --- /dev/null +++ b/100-make_a_pile.dfy @@ -0,0 +1,22 @@ +method make_a_pile(n: int) returns (pile: seq) + requires n >= 0 + ensures |pile| == n + ensures forall i :: 1 <= i < n ==> pile[i] == pile[i - 1] + 2 + ensures n > 0 ==> pile[0] == n +{ + pile := []; + if n == 0 { + return; + } + pile := [n]; + var i := 1; + while i < n + invariant 0 <= i <= n + invariant |pile| == i + invariant forall j :: 1 <= j < i ==> pile[j] == pile[j - 1] + 2 + invariant n > 0 ==> pile[0] == n + { + pile := pile + [pile[i - 1] + 2]; + i := i + 1; + } +} \ No newline at end of file diff --git a/104-unique_digits.dfy b/104-unique_digits.dfy new file mode 100644 index 0000000..febd9b1 --- /dev/null +++ b/104-unique_digits.dfy @@ -0,0 +1,59 @@ +predicate HasNoEvenDigit(n: int) + decreases n +{ + n >= 0 && (n < 10 || (n % 10 % 2 != 0 && HasNoEvenDigit(n / 10))) +} + +method UniqueDigits(x: seq) returns (result: seq) + ensures forall i :: 0 <= i < |result| ==> HasNoEvenDigit(result[i]) + ensures forall i, j :: 0 <= i < j < |result| ==> result[i] <= result[j] + ensures forall e :: e in x && HasNoEvenDigit(e) ==> e in result + ensures forall e :: e in result ==> e in x +{ + result := []; + + for i := 0 to |x| + invariant forall j :: 0 <= j < |result| ==> HasNoEvenDigit(result[j]) + invariant forall e :: e in result ==> e in x[..i] + invariant forall e :: e in x[..i] && HasNoEvenDigit(e) ==> e in result + { + if HasNoEvenDigit(x[i]) { + result := result + [x[i]]; + } + } + + ghost var unsorted := result; + + var i := 0; + while i < |result| + invariant 0 <= i <= |result| + invariant forall j, k :: 0 <= j < k < i ==> result[j] <= result[k] + invariant multiset(unsorted) == multiset(result) + invariant forall j :: 0 <= j < i ==> forall k :: i <= k < |result| ==> result[j] <= result[k] + invariant forall e :: e in result ==> HasNoEvenDigit(e) + invariant forall e :: e in result ==> e in x + { + var minIndex := i; + var j := i + 1; + while j < |result| + invariant i <= minIndex < j <= |result| + invariant forall k :: i <= k < j ==> result[minIndex] <= result[k] + { + if result[j] < result[minIndex] { + minIndex := j; + } + j := j + 1; + } + if minIndex != i { + var temp := result[i]; + result := result[i := result[minIndex]][minIndex := temp]; + } + i := i + 1; + } + + assert forall e :: e in result ==> HasNoEvenDigit(e); + assert forall e :: e in result ==> e in x; + assert forall e :: e in x && HasNoEvenDigit(e) ==> e in result by { + assert forall e :: e in unsorted ==> e in multiset(result); + } +} \ No newline at end of file diff --git a/110-exchange.dfy b/110-exchange.dfy new file mode 100644 index 0000000..c4d922a --- /dev/null +++ b/110-exchange.dfy @@ -0,0 +1,24 @@ +predicate IsEven(n: int) +{ + n % 2 == 0 +} + +function CountEvens(lst: seq): nat +{ + if |lst| == 0 then 0 + else (if IsEven(lst[0]) then 1 else 0) + CountEvens(lst[1..]) +} + +method Exchange(lst1: seq, lst2: seq) returns (result: string) + requires |lst1| > 0 && |lst2| > 0 + ensures result == "YES" || result == "NO" + ensures result == "YES" ==> CountEvens(lst1) + CountEvens(lst2) >= |lst1| + ensures result == "NO" ==> CountEvens(lst1) + CountEvens(lst2) < |lst1| +{ + var totalEvens := CountEvens(lst1) + CountEvens(lst2); + if totalEvens >= |lst1| { + result := "YES"; + } else { + result := "NO"; + } +} \ No newline at end of file diff --git a/118-get_closest_vowel.dfy b/118-get_closest_vowel.dfy new file mode 100644 index 0000000..68babba --- /dev/null +++ b/118-get_closest_vowel.dfy @@ -0,0 +1,37 @@ +predicate IsVowel(c: char) +{ + c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u' || + c == 'A' || c == 'E' || c == 'I' || c == 'O' || c == 'U' +} + +predicate IsConsonant(c: char) +{ + ('A' <= c <= 'Z' || 'a' <= c <= 'z') && !IsVowel(c) +} + +method get_closest_vowel(word: string) returns (result: string) + requires forall i :: 0 <= i < |word| ==> ('A' <= word[i] <= 'Z' || 'a' <= word[i] <= 'z') + ensures |result| <= 1 + ensures |result| == 1 ==> IsVowel(result[0]) + ensures |result| == 1 ==> exists i {:trigger word[i]} :: + 1 <= i && i + 1 < |word| + && IsVowel(word[i]) && IsConsonant(word[i - 1]) && IsConsonant(word[i + 1]) + && (forall j :: i < j < |word| - 1 ==> !IsVowel(word[j]) || !IsConsonant(word[j - 1]) || !IsConsonant(word[j + 1])) +{ + if |word| < 3 { + return ""; + } + + var i := |word| - 2; + while i > 0 + invariant 0 <= i <= |word| - 2 + invariant forall j :: i < j < |word| - 1 ==> !IsVowel(word[j]) || !IsConsonant(word[j - 1]) || !IsConsonant(word[j + 1]) + { + if IsVowel(word[i]) && IsConsonant(word[i - 1]) && IsConsonant(word[i + 1]) { + return [word[i]]; + } + i := i - 1; + } + + return ""; +} \ No newline at end of file diff --git a/122-add_elements.dfy b/122-add_elements.dfy new file mode 100644 index 0000000..d20f099 --- /dev/null +++ b/122-add_elements.dfy @@ -0,0 +1,79 @@ +function select_at_most_two_digits_rec(arr: seq): seq + requires |arr| >= 0 && |arr| <= 100 +{ + if |arr| == 0 then [] + else if 0 <= arr[0] < 100 then [arr[0]] + select_at_most_two_digits_rec(arr[1..]) + else select_at_most_two_digits_rec(arr[1..]) +} + +lemma select_prop(s: seq) + requires |s| > 0 && |s| <= 100 + ensures select_at_most_two_digits_rec(s) == select_at_most_two_digits_rec(s[..|s| - 1]) + if 0 <= s[|s| - 1] < 100 then [s[|s| - 1]] else [] +{ + if (|s| > 1) { + assert (s[1..][..|s[1..]| - 1]) == s[1..|s| - 1]; + } +} + +method select_at_most_two_digits(arr: seq) returns (result: seq) + requires |arr| > 0 && |arr| <= 100 + ensures forall i :: 0 <= i < |result| ==> 0 <= result[i] < 100 + ensures forall i :: 0 <= i < |result| ==> result[i] in arr + ensures result == select_at_most_two_digits_rec(arr) +{ + result := []; + var i := 0; + while i < |arr| + invariant 0 <= i <= |arr| + invariant forall j :: 0 <= j < |result| ==> result[j] in arr + invariant forall j :: 0 <= j < |result| ==> 0 <= result[j] < 100 + invariant |result| <= i + invariant result == select_at_most_two_digits_rec(arr[..i]) + { + if 0 <= arr[i] < 100 { + result := result + [arr[i]]; + } + assert select_at_most_two_digits_rec(arr[..i + 1]) == select_at_most_two_digits_rec(arr[..i]) + if 0 <= arr[i] < 100 then [arr[i]] else [] by { + assert arr[..i+1][..i] == arr[..i]; + select_prop(arr[..i + 1]); + } + i := i + 1; + } + assert arr[..|arr|] == arr; +} + +function sum(s: seq) : int { + if |s| == 0 then 0 else s[0] + sum(s[1..]) +} + +lemma sum_prop(s: seq) + 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]; + } +} + +method SumElementsWithAtMostTwoDigits(arr: seq, k: int) returns (s: int) + requires |arr| > 0 && |arr| <= 100 + requires 1 <= k <= |arr| + ensures var two_digits := select_at_most_two_digits_rec(arr[..k]); + s == sum(two_digits) +{ + var two_digits := select_at_most_two_digits(arr[..k]); + s := 0; + var i := 0; + while i < |two_digits| + invariant 0 <= i <= |two_digits| + invariant s == sum(two_digits[..i]) + { + s := s + two_digits[i]; + assert sum(two_digits[..i + 1]) == sum(two_digits[..i]) + two_digits[i] by { + assert two_digits[..i+1][..i] == two_digits[..i]; + sum_prop(two_digits[..i + 1]); + } + i := i + 1; + } + assert two_digits[..|two_digits|] == two_digits; +} \ No newline at end of file diff --git a/127-intersection.dfy b/127-intersection.dfy new file mode 100644 index 0000000..471f807 --- /dev/null +++ b/127-intersection.dfy @@ -0,0 +1,50 @@ +predicate IsPrime(n: nat) +{ + n > 1 && + forall k :: 2 <= k < n ==> n % k != 0 +} + +function min(a: int, b: int): int +{ + if a <= b then a else b +} + +function max(a: int, b: int): int +{ + if a >= b then a else b +} + +method Intersection(start1: int, end1: int, start2: int, end2: int) returns (result: string) + requires start1 <= end1 && start2 <= end2 + ensures result == "YES" || result == "NO" + ensures result == "YES" <==> + (max(start1, start2) <= min(end1, end2) && + IsPrime((min(end1, end2) - max(start1, start2) + 1) as nat)) +{ + var intersectionStart := max(start1, start2); + var intersectionEnd := min(end1, end2); + + if intersectionStart <= intersectionEnd { + var length := (intersectionEnd - intersectionStart + 1) as nat; + if IsPrime(length) { + return "YES"; + } + } + + return "NO"; +} + +method {:test} Main() +{ + var result1 := Intersection(1, 2, 2, 3); + assert result1 == "NO"; + + var result2 := Intersection(-1, 1, 0, 4); + // The intersection is [0, 1], which has length 2, a prime number + assert result2 == "YES"; + + var result3 := Intersection(-3, -1, -5, 5); + assert result3 == "YES"; + + print "All tests passed!\n"; +} \ No newline at end of file diff --git a/README.md b/README.md index 44d199c..c09fc68 100644 --- a/README.md +++ b/README.md @@ -98,11 +98,11 @@ Current status: - [ ] 93. encode - [ ] 94. skjkasdkd - [x] 95. check_dict_case -- [ ] 96. count_up_to -- [ ] 97. multiply +- [x] 96. count_up_to +- [x] 97. multiply - [ ] 98. count_upper - [ ] 99. closest_integer -- [ ] 100. make_a_pile +- [x] 100. make_a_pile - [ ] 101. words_string - [x] 102. choose_num - [ ] 103. rounded_avg @@ -112,7 +112,7 @@ Current status: - [ ] 107. even_odd_palindrome - [ ] 108. count_nums - [ ] 109. move_one_ball -- [ ] 110. exchange +- [x] 110. exchange - [ ] 111. histogram - [ ] 112. reverse_delete - [ ] 113. odd_count @@ -120,16 +120,16 @@ Current status: - [ ] 115. max_fill - [ ] 116. sort_array - [ ] 117. select_words -- [ ] 118. get_closest_vowel +- [x] 118. get_closest_vowel - [ ] 119. match_parens - [ ] 120. maximum - [ ] 121. solution -- [ ] 122. add_elements +- [x] 122. add_elements - [ ] 123. get_odd_collatz - [ ] 124. valid_date - [ ] 125. split_words - [ ] 126. is_sorted -- [ ] 127. intersection +- [x] 127. intersection - [ ] 128. prod_signs - [ ] 129. minPath - [x] 130. tri