// File: Baobaobear[P]sort/./wikisort.hpp
// Function: void Wiki::MergeInPlace<baobao::TestClass*, std::less<baobao::TestClass>>(baobao::TestClass*, baobao::TestClass*, baobao::TestClass*, baobao::TestClass*, std::less<baobao::TestClass>)
void MergeInPlace(RandomAccessIterator first1, RandomAccessIterator last1,
                      RandomAccessIterator first2, RandomAccessIterator last2,
                      Comparison compare) {
        if (last1 - first1 == 0 || last2 - first2 == 0) return;

        /*
         this just repeatedly binary searches into B and rotates A into position.
         the paper suggests using the 'rotation-based Hwang and Lin algorithm' here,
         but I decided to stick with this because it had better situational performance

         (Hwang and Lin is designed for merging subarrays of very different sizes,
         but WikiSort almost always uses subarrays that are roughly the same size)

         normally this is incredibly suboptimal, but this function is only called
         when none of the A or B blocks in any subarray contained 2sqrtA unique values,
         which places a hard limit on the number of times this will ACTUALLY need
         to binary search and rotate.

         according to my analysis the worst case is sqrtA rotations performed on sqrtA items
         once the constant factors are removed, which ends up being O(n)

         again, this is NOT a general-purpose solution - it only works well in this case!
         kind of like how the O(n^2) insertion sort is used in some places
         */

        while (true) {
            // find the first place in B where the first item in A needs to be inserted
            RandomAccessIterator mid = std::lower_bound(first2, last2, *first1, compare);

            // rotate A into place
            std::size_t amount = mid - last1;
            std::rotate(first1, last1, mid);
            if (last2 == mid) break;

            // calculate the new A and B ranges
            first2 = mid;
            first1 += amount;
            last1 = first2;
            first1 = std::upper_bound(first1, last1, *first1, compare);
            if (std::distance(first1, last1) == 0) break;
        }
    }

// Assembly:
subq $0x48, %rsp
movq %rdi, 0x38(%rsp)
movq %rsi, 0x30(%rsp)
movq %rdx, 0x28(%rsp)
movq %rcx, 0x20(%rsp)
movq 0x30(%rsp), %rax
movq 0x38(%rsp), %rcx
subq %rcx, %rax
sarq $0x5, %rax
cmpq $0x0, %rax
je 0x1da16
movq 0x20(%rsp), %rax
movq 0x28(%rsp), %rcx
subq %rcx, %rax
sarq $0x5, %rax
cmpq $0x0, %rax
jne 0x1da1b
jmp 0x1daca
jmp 0x1da1d
movq 0x28(%rsp), %rdi
movq 0x20(%rsp), %rsi
movq 0x38(%rsp), %rdx
callq 0xf130
movq %rax, 0x18(%rsp)
movq 0x18(%rsp), %rax
movq 0x30(%rsp), %rcx
subq %rcx, %rax
sarq $0x5, %rax
movq %rax, 0x8(%rsp)
movq 0x38(%rsp), %rdi
movq 0x30(%rsp), %rsi
movq 0x18(%rsp), %rdx
callq 0xbc30
movq 0x20(%rsp), %rax
cmpq 0x18(%rsp), %rax
jne 0x1da6e
jmp 0x1daca
movq 0x18(%rsp), %rax
movq %rax, 0x28(%rsp)
movq 0x8(%rsp), %rax
shlq $0x5, %rax
addq 0x38(%rsp), %rax
movq %rax, 0x38(%rsp)
movq 0x28(%rsp), %rax
movq %rax, 0x30(%rsp)
movq 0x38(%rsp), %rdi
movq 0x30(%rsp), %rsi
movq 0x38(%rsp), %rdx
callq 0xf0e0
movq %rax, 0x38(%rsp)
movq 0x38(%rsp), %rdi
movq 0x30(%rsp), %rsi
callq 0xbb40
cmpq $0x0, %rax
jne 0x1dac5
jmp 0x1daca
jmp 0x1da1d
addq $0x48, %rsp
retq
nop