Imandra [1] has an excellent literate programming walkthrough that hits on the same issues and plugs the holes one by one using formal methods, ultimately proving merge sort correct.
As I understand it this was a multilingual exercise. In some languages "mixed type values" isn't a thing so you can't test it and your users can't get it wrong.
Likewise for non-arrays.
It's notable that one of the participants had decided empty arrays shouldn't be a thing, so far as I know programming languages all admit the idea of the empty array although perhaps some of them don't distinguish between empty arrays and various other empty data structures.
> it apparently takes a sorting routine to test sorting routines
You don't have to sort to check sortedness.
boolean isSorted(int[] original, int[] sorted) {
if (original.length != sorted.length)
return false;
for (int i = 0; i < original.length; i++) {
if (i > 0 && sorted[i] < sorted[i - 1])
return false;
if (countInstances(original[i], original) != countIntasnces(original[i], sorted))
return false;
}
return true;
}
int countInstances(int value, int[] array) {
int count = 0;
for (int v : array) {
if (v == value)
count++;
}
return count;
}
I know that bubble sort, selection sort, and insertion sort can be implemented in about half the code as this. But this code is read-only and is more obviously correct than trying to prove an algorithm that mutates the array.
It occurs to me that the "you're helping" might read as snark and it wasn't really intended that way, or at least, not as snark at you, but rather all of us.
It's silly to reject help from e.g. peer review, static analysis, smarter / safer languages. If you look at some tool and say "What kind of idiot would make the mistakes this catches?" the answer is almost certainly "idiots like you".
Does it? I'm seeing two O(n) processes: one to confirm order and the other to confirm permutation of set.
In almost-Lua pseudocode:
function isOrdered(array, predicate)
-- zero and one length arrays are trivially ordered
if len(array) < 2 then return true end
local ordered = true
for i = 0, len(array) - 1 do
ordered = ordered and predicate(array[i], array + 1)
end
return ordered
end
function popCount(array)
local pops = {}
for item in iterate(array) do
pops = pops[item] and pops[item] + 1 or 1
end
return pops
end
Then you compare the two popcounts, if they're the same and the result is in-order then something made them that way.
Many problems have tricky, performant solutions and simple, slow solutions. Comparing to a simple reference implementation is a very good way to test.
For some problems it is much easier to check that the result is correct than to find the result (that's what P and NP are about for example). Sorting is one of these problems. You can check that one list is a permutation of another list in linear time. Comparing against a library implementation of sorting is just simpler in many languages.
Why not just do two tests: check if every element is present exactly once or some variation of it depending on what is considered unique; do the `for i in (0..array.size-2) { assert array[i] <= array[i+1] }` as was suggested. That way you don't need to test against a known good sorting algorithm.
[1] https://docs.imandra.ai/imandra-docs/notebooks/verifying-mer...