-
Notifications
You must be signed in to change notification settings - Fork 25
Quick sort and related Arrays utilities #58
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 28 commits
2e62624
00e8667
fd12d32
d8f8cee
57e35ae
ac104b2
9af98ed
aab9de9
513d165
f44ea6f
71fe474
41faf37
33a43a5
5c8f06f
7da871a
62b9346
e8e5919
bb009fb
6d57e55
9038f2a
a2edede
1df340f
850e3d4
34e380a
730278a
f1f31ad
92a19bd
bc018cb
ae832a4
a508b99
03af996
467f587
9688843
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,374 @@ | ||||||
| // RUN: %dafny /compile:0 "%s" | ||||||
|
|
||||||
| /******************************************************************************* | ||||||
| * Copyright by the contributors to the Dafny Project | ||||||
| * SPDX-License-Identifier: MIT | ||||||
| *******************************************************************************/ | ||||||
|
|
||||||
| include "../Sets/Sets.dfy" | ||||||
| include "../../Comparison.dfy" | ||||||
|
|
||||||
| module Arrays{ | ||||||
|
|
||||||
| import opened Sets | ||||||
| import opened Comparison | ||||||
| import opened Wrappers | ||||||
|
|
||||||
| trait Sorter<T> { | ||||||
| ghost const C := Comparison(Cmp) | ||||||
|
|
||||||
| function method Cmp(t0: T, t1: T): Cmp | ||||||
|
|
||||||
| twostate predicate Identical(arr: array<T>, lo: int, hi: int) | ||||||
|
||||||
| requires 0 <= lo <= hi <= arr.Length | ||||||
| reads arr | ||||||
| { | ||||||
| arr[lo..hi] == old(arr[lo..hi]) | ||||||
| } | ||||||
|
|
||||||
| twostate lemma IdenticalSplit(arr: array<T>, lo: int, mid: int, hi: int) | ||||||
| requires 0 <= lo <= mid <= hi <= arr.Length | ||||||
| requires Identical(arr, lo, hi) | ||||||
| ensures Identical(arr, lo, mid) | ||||||
| ensures Identical(arr, mid, hi) | ||||||
| { | ||||||
| assert arr[lo..mid] == arr[lo..hi][..mid-lo]; | ||||||
| } | ||||||
|
|
||||||
| twostate lemma IdenticalShuffled(arr: array<T>, lo: int, hi: int) | ||||||
| requires 0 <= lo <= hi <= arr.Length | ||||||
| requires Identical(arr, lo, hi) | ||||||
| ensures Shuffled(arr, lo, hi) | ||||||
| {} | ||||||
|
|
||||||
| twostate predicate SameElements(arr: array<T>, lo: int, hi: int) | ||||||
|
||||||
| reads arr | ||||||
| requires 0 <= lo <= hi <= arr.Length | ||||||
| { | ||||||
| && Identical(arr, 0, lo) | ||||||
| && Shuffled(arr, lo, hi) | ||||||
| && Identical(arr, hi, arr.Length) | ||||||
| } | ||||||
|
|
||||||
| twostate lemma SameElementsExtend(arr: array<T>, lo: int, lo': int, hi': int, hi: int) | ||||||
| requires 0 <= lo <= lo' <= hi' <= hi <= arr.Length | ||||||
| requires SameElements(arr, lo', hi') | ||||||
| ensures SameElements(arr, lo, hi) | ||||||
| { | ||||||
| assert SameElements(arr, lo', hi'); | ||||||
| assert Identical(arr, 0, lo') && Shuffled(arr, lo', hi') && Identical(arr, hi', arr.Length); | ||||||
| IdenticalSplit(arr, 0, lo, lo'); IdenticalSplit(arr, hi', hi, arr.Length); | ||||||
| assert && Identical(arr, 0, lo) && Identical(arr, lo, lo') | ||||||
| && Shuffled(arr, lo', hi') | ||||||
| && Identical(arr, hi', hi) && Identical(arr, hi, arr.Length); | ||||||
| IdenticalShuffled(arr, lo, lo'); IdenticalShuffled(arr, hi', hi); | ||||||
| assert && Identical(arr, 0, lo) && Shuffled(arr, lo, lo') | ||||||
| && Shuffled(arr, lo', hi') | ||||||
| && Shuffled(arr, hi', hi) && Identical(arr, hi, arr.Length); | ||||||
| ShuffledConcat(arr, lo, lo', hi'); ShuffledConcat(arr, lo, hi', hi); | ||||||
| assert Identical(arr, 0, lo) && Shuffled(arr, lo, hi) && Identical(arr, hi, arr.Length); | ||||||
| assert SameElements(arr, lo, hi); | ||||||
| } | ||||||
|
|
||||||
| twostate predicate Shuffled(arr: array<T>, lo: int, hi: int) | ||||||
| requires 0 <= lo <= hi <= arr.Length | ||||||
| reads arr | ||||||
| { | ||||||
| multiset(arr[lo..hi]) == old(multiset(arr[lo..hi])) | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I remember adding this to almost every every function method in |
||||||
| } | ||||||
|
|
||||||
| predicate Sortable(arr: array<T>, lo: int, hi: int) | ||||||
| requires 0 <= lo <= hi <= arr.Length | ||||||
| reads arr | ||||||
| { | ||||||
| reveal C.Valid?(); // Leaks through | ||||||
| C.Valid?(Sets.OfSlice(arr, lo, hi)) | ||||||
| } | ||||||
|
|
||||||
| twostate lemma SetOfSliceShuffled(arr: array<T>, lo: int, hi: int) | ||||||
| requires 0 <= lo <= hi <= arr.Length | ||||||
| requires Shuffled(arr, lo, hi) | ||||||
| ensures Sets.OfSlice(arr, lo, hi) == old(Sets.OfSlice(arr, lo, hi)) | ||||||
| { | ||||||
| calc { | ||||||
| old(Sets.OfSlice(arr, lo, hi)); | ||||||
| set x <- old(arr[lo..hi]); | ||||||
| set x <- old(multiset(arr[lo..hi])); | ||||||
| set x <- multiset(arr[lo..hi]); | ||||||
| set x <- arr[lo..hi]; | ||||||
| Sets.OfSlice(arr, lo, hi); | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| twostate lemma SortableSameElements(arr: array<T>, lo: int, hi: int) | ||||||
| requires 0 <= lo <= hi <= arr.Length | ||||||
| requires old(Sortable(arr, lo, hi)) | ||||||
| requires SameElements(arr, lo, hi) | ||||||
| ensures Sortable(arr, lo, hi) | ||||||
| { | ||||||
| SetOfSliceShuffled(arr, lo, hi); | ||||||
| } | ||||||
|
|
||||||
| twostate lemma ShuffledConcat(arr: array<T>, lo: int, mid: int, hi: int) | ||||||
| requires 0 <= lo <= mid <= hi <= arr.Length | ||||||
| requires Shuffled(arr, lo, mid) | ||||||
| requires Shuffled(arr, mid, hi) | ||||||
| ensures Shuffled(arr, lo, hi) | ||||||
| { | ||||||
| calc { | ||||||
| old(multiset(arr[lo..hi])); | ||||||
| { assert old(arr[lo..hi] == arr[lo..mid] + arr[mid..hi]); } | ||||||
| old(multiset(arr[lo..mid] + arr[mid..hi])); | ||||||
| old(multiset(arr[lo..mid])) + old(multiset(arr[mid..hi])); | ||||||
| multiset(arr[lo..mid]) + multiset(arr[mid..hi]); | ||||||
| { assert arr[lo..hi] == arr[lo..mid] + arr[mid..hi]; } | ||||||
| multiset(arr[lo..hi]); | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| lemma Sortable_Slice(arr: array<T>, lo: int, hi: int, lo': int, hi': int) | ||||||
|
||||||
| lemma Sortable_Slice(arr: array<T>, lo: int, hi: int, lo': int, hi': int) | |
| lemma SortableSlice(arr: array<T>, lo: int, hi: int, lo': int, hi': int) |
Only instance of an underscore I see here and it really sticks out.
Also consider SortableSubslice instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SortableSlice or SortableSubslice both makes sense putting Subslice describes it better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should either implement this or drop the function and just inline this choice of pivot above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's implement it — it's not hard and it's an important part of making the algorithm safe.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As per offline conversation we decided not to block on this, but in that case we should add a comment on QuickSort that it doesn't yet have the intended O(n log n) worst case runtime yet (which is what @cpitclaudel was referring to as "safe").
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This definitely needs an example in
examplesto demonstrate using it. I assume you have to not only provide a concrete class that implementsSorter<T>and fills inCmp, but perhaps even has to invoke a lemma or two from the trait in order to prove that it works.Is it possible to also provide a
PredicateSorterclass that is constructed around a(T, T) -> Cmpfunction value? Perhaps it could even be a partial function if you also provide a ghost set of all values you intend to sort with it.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to clarify, I'd strongly prefer to have an example, but the
PredicateSorteridea can wait for a future PR.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh yikes, I went to write an example just to play around with it and immediately got the
A class may only extend a trait in the same module, unless the parent trait is annotated with {:termination false}error. That means this is currently useless unless we add{:termination false}.Unfortunately we should treat that as a hard blocker given there's already been resistance to putting
{:termination false}on any standard library code: #22 And especially given the discovery of dafny-lang/dafny#2500 I'm not inclined to push back on that.We can think about whether there's a good alternative for this code that doesn't use traits, but it might be better to stick with this if it's good UX and at least wait for the solution to dafny-lang/dafny#2500.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's not right: the proper API to use is
PredicateSorter, which is a regular class, defined right belowSorter. That doesn't require:termination false.Original code here, with examples: https://github.com/dafny-lang/compiler-bootstrap/blob/4f616822209828e48cf63d3da66ee1c010f689d4/src/Utils/Lib.Sort.dfy#L396-L420
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @cpitclaudel, I see the issue now is this PR doesn't include the
PredicateSorterclass. We should just add that as well.