To prove a sort algorithm is unstable only requires finding one failure. Proving a sort algorithm is stable would be more involved. One way to check for failure is to use an array of integers and split the integers into two parts, the upper 8 bits as a pseudo random value, the lower 24 bits equal to the index of the integer (0 to count-1). Then run the sort, using only the upper 8 bits for the compare, for example in C:
if((b[j]&0xff000000) < (b[i]&0xff000000)) ...
After the sort is completed, check that the array is in order using all 32 bits.
Using this method, I was able to confirm that this variation of merge sort is unstable.
Apparently the reason this is called "fast" merge sort, is that there is no check for the end of a run when doing the merge. The left run is copied into aux[] in forward order from lo to mid, while the right run is copied into aux[] in reverse order from hi to mid+1. The merge then starts at both ends (lo and hi) and works towards the middle (mid and mid+1), the left run using i forwards from lo to mid, the right run backwards using j from hi to mid+1. Since there is no check for reaching the end of a run, i may be incremented above mid (potential stability issue), or j may be decremented below mid+1 (not a stability issue). Stability is broken in the case where i is incremented above mid, and aux[mid+1] == aux[mid+2], the two highest elements from the original right run. In this case the elements are copied in reverse order.
Although the book called it fast merge sort, it would be faster to avoid copying the data in aux, and instead changing the direction of merge based on the level of recursion. For top down, this can be done with a one type copy and swapping array references in the recursive calls, such as this wiki example:
https://en.wikipedia.org/wiki/Merge_sort#Top-down_implementation
The initial copy can be avoided using a pair of mutually recursive functions, one that ends up with the result in a[], the other that ends up with the result in b[].
Slightly faster is a bottom up merge sort, since it skips all of the recursive splitting and storing of indexes on the stack. In this case, the direction of merge is based on the merge pass. To keep the number of passes even, a check can be made in advance for an odd pass count, and pairs of elements swapped in place before starting the first bottom up merge sort pass.