// File: /workspace/github_cpp_makefile_star2_O2/VanAndelInstitute[P]intent/src/edlib.cpp
// Function: myersCalcEditDistanceSemiGlobal(unsigned long const*, int, int, int, unsigned char const*, int, int, EdlibAlignMode, int*, int**, int*)
static int myersCalcEditDistanceSemiGlobal(
        const Word* const Peq, const int W, const int maxNumBlocks,
        const int queryLength,
        const unsigned char* const target, const int targetLength,
        int k, const EdlibAlignMode mode,
        int* const bestScore_, int** const positions_, int* const numPositions_) {
    *positions_ = NULL;
    *numPositions_ = 0;

    // firstBlock is 0-based index of first block in Ukkonen band.
    // lastBlock is 0-based index of last block in Ukkonen band.
    int firstBlock = 0;
    int lastBlock = min(ceilDiv(k + 1, WORD_SIZE), maxNumBlocks) - 1; // y in Myers
    Block *bl; // Current block

    Block* blocks = new Block[maxNumBlocks];

    // For HW, solution will never be larger then queryLength.
    if (mode == EDLIB_MODE_HW) {
        k = min(queryLength, k);
    }

    // Each STRONG_REDUCE_NUM column is reduced in more expensive way.
    // This gives speed up of about 2 times for small k.
    const int STRONG_REDUCE_NUM = 2048;

    // Initialize P, M and score
    bl = blocks;
    for (int b = 0; b <= lastBlock; b++) {
        bl->score = (b + 1) * WORD_SIZE;
        bl->P = static_cast<Word>(-1); // All 1s
        bl->M = static_cast<Word>(0);
        bl++;
    }

    int bestScore = -1;
    vector<int> positions; // TODO: Maybe put this on heap?
    const int startHout = mode == EDLIB_MODE_HW ? 0 : 1; // If 0 then gap before query is not penalized;
    const unsigned char* targetChar = target;
    for (int c = 0; c < targetLength; c++) { // for each column
        const Word* Peq_c = Peq + (*targetChar) * maxNumBlocks;

        //----------------------- Calculate column -------------------------//
        int hout = startHout;
        bl = blocks + firstBlock;
        Peq_c += firstBlock;
        for (int b = firstBlock; b <= lastBlock; b++) {
            hout = calculateBlock(bl->P, bl->M, *Peq_c, hout, bl->P, bl->M);
            bl->score += hout;
            bl++; Peq_c++;
        }
        bl--; Peq_c--;
        //------------------------------------------------------------------//

        //---------- Adjust number of blocks according to Ukkonen ----------//
        if ((lastBlock < maxNumBlocks - 1) && (bl->score - hout <= k) // bl is pointing to last block
            && ((*(Peq_c + 1) & WORD_1) || hout < 0)) { // Peq_c is pointing to last block
            // If score of left block is not too big, calculate one more block
            lastBlock++; bl++; Peq_c++;
            bl->P = static_cast<Word>(-1); // All 1s
            bl->M = static_cast<Word>(0);
            bl->score = (bl - 1)->score - hout + WORD_SIZE + calculateBlock(bl->P, bl->M, *Peq_c, hout, bl->P, bl->M);
        } else {
            while (lastBlock >= firstBlock && bl->score >= k + WORD_SIZE) {
                lastBlock--; bl--; Peq_c--;
            }
        }

        // Every some columns, do some expensive but also more efficient block reducing.
        // This is important!
        //
        // Reduce the band by decreasing last block if possible.
        if (c % STRONG_REDUCE_NUM == 0) {
            while (lastBlock >= 0 && lastBlock >= firstBlock && allBlockCellsLarger(*bl, k)) {
                lastBlock--; bl--; Peq_c--;
            }
        }
        // For HW, even if all cells are > k, there still may be solution in next
        // column because starting conditions at upper boundary are 0.
        // That means that first block is always candidate for solution,
        // and we can never end calculation before last column.
        if (mode == EDLIB_MODE_HW && lastBlock == -1) {
            lastBlock++; bl++; Peq_c++;
        }

        // Reduce band by increasing first block if possible. Not applicable to HW.
        if (mode != EDLIB_MODE_HW) {
            while (firstBlock <= lastBlock && blocks[firstBlock].score >= k + WORD_SIZE) {
                firstBlock++;
            }
            if (c % STRONG_REDUCE_NUM == 0) { // Do strong reduction every some blocks
                while (firstBlock <= lastBlock && allBlockCellsLarger(blocks[firstBlock], k)) {
                    firstBlock++;
                }
            }
        }

        // If band stops to exist finish
        if (lastBlock < firstBlock) {
            *bestScore_ = bestScore;
            if (bestScore != -1) {
                *positions_ = static_cast<int *>(malloc(sizeof(int) * static_cast<int>(positions.size())));
                *numPositions_ = static_cast<int>(positions.size());
                copy(positions.begin(), positions.end(), *positions_);
            }
            delete[] blocks;
            return EDLIB_STATUS_OK;
        }
        //------------------------------------------------------------------//

        //------------------------- Update best score ----------------------//
        if (lastBlock == maxNumBlocks - 1) {
            int colScore = bl->score;
            if (colScore <= k) { // Scores > k dont have correct values (so we cannot use them), but are certainly > k.
                // NOTE: Score that I find in column c is actually score from column c-W
                if (bestScore == -1 || colScore <= bestScore) {
                    if (colScore != bestScore) {
                        positions.clear();
                        bestScore = colScore;
                        // Change k so we will look only for equal or better
                        // scores then the best found so far.
                        k = bestScore;
                    }
                    positions.push_back(c - W);
                }
            }
        }
        //------------------------------------------------------------------//

        targetChar++;
    }


    // Obtain results for last W columns from last column.
    if (lastBlock == maxNumBlocks - 1) {
        vector<int> blockScores = getBlockCellValues(*bl);
        for (int i = 0; i < W; i++) {
            int colScore = blockScores[i + 1];
            if (colScore <= k && (bestScore == -1 || colScore <= bestScore)) {
                if (colScore != bestScore) {
                    positions.clear();
                    k = bestScore = colScore;
                }
                positions.push_back(targetLength - W + i);
            }
        }
    }

    *bestScore_ = bestScore;
    if (bestScore != -1) {
        *positions_ = static_cast<int *>(malloc(sizeof(int) * static_cast<int>(positions.size())));
        *numPositions_ = static_cast<int>(positions.size());
        copy(positions.begin(), positions.end(), *positions_);
    }

    delete[] blocks;
    return EDLIB_STATUS_OK;
}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0xd8, %rsp
movl %r9d, 0x3c(%rsp)
movq %r8, 0x58(%rsp)
movl %ecx, %r14d
movq %rdx, 0x70(%rsp)
movq %rdi, 0xa8(%rsp)
movq 0x128(%rsp), %rax
andq $0x0, (%rax)
movq 0x130(%rsp), %rax
andl $0x0, (%rax)
movl 0x110(%rsp), %r12d
leal 0x1(%r12), %ebx
pushq $0x40
popq %r15
testb $0x3f, %bl
movq %rsi, 0x88(%rsp)
je 0x22ce3
movl %ebx, %eax
cltd
idivl %r15d
movl %eax, %ebx
incl %ebx
jmp 0x22ce6
sarl $0x6, %ebx
movq 0x70(%rsp), %rax
cmpl %eax, %ebx
cmovgel %eax, %ebx
leal -0x1(%rbx), %r13d
cltq
pushq $0x18
popq %rcx
mulq %rcx
pushq $-0x1
popq %rdi
cmovnoq %rax, %rdi
callq 0x6030
movq %rax, 0x60(%rsp)
cmpl %r12d, %r14d
cmovgel %r12d, %r14d
xorl %ebp, %ebp
testl %ebx, %ebx
cmovlel %ebp, %ebx
imulq $0x18, %rbx, %rax
movq 0x60(%rsp), %rcx
cmpl %ebp, %eax
je 0x22d42
movl %r15d, 0x10(%rcx,%rbp)
orq $-0x1, (%rcx,%rbp)
andq $0x0, 0x8(%rcx,%rbp)
addq $0x18, %rbp
addl $0x40, %r15d
jmp 0x22d24
xorl %eax, %eax
cmpl $0x2, 0x118(%rsp)
setne %al
movl %eax, 0x7c(%rsp)
cmovel %r14d, %r12d
xorps %xmm0, %xmm0
leaq 0x40(%rsp), %rax
movaps %xmm0, (%rax)
andq $0x0, 0x10(%rax)
movq 0x70(%rsp), %rax
decl %eax
movl %eax, 0x2c(%rsp)
movl 0x3c(%rsp), %eax
testl %eax, %eax
movl $0x0, %ecx
cmovgl %eax, %ecx
movl %ecx, 0x80(%rsp)
movq 0x60(%rsp), %rax
leaq 0x8(%rax), %rcx
movq %rcx, 0xa0(%rsp)
leaq -0x18(%rax), %rcx
movq %rcx, 0x98(%rsp)
leaq 0x10(%rax), %rcx
movq %rcx, 0x90(%rsp)
addq %rax, %rbp
pushq $-0x1
popq %rax
movl %eax, 0x24(%rsp)
xorl %r14d, %r14d
movq $0x0, 0x68(%rsp)
movq 0x58(%rsp), %rbx
cmpl 0x80(%rsp), %r14d
je 0x2308e
movl %r14d, 0x28(%rsp)
movq %r12, 0x30(%rsp)
movq %rbx, 0x58(%rsp)
movzbl (%rbx), %eax
imull 0x70(%rsp), %eax
cltq
movq 0xa8(%rsp), %rcx
leaq (%rcx,%rax,8), %r12
movslq 0x68(%rsp), %rbx
imulq $0x18, %rbx, %rcx
movq 0xa0(%rsp), %rax
leaq (%rax,%rcx), %rbp
movq 0x98(%rsp), %rax
movq %rcx, 0xb0(%rsp)
leaq (%rax,%rcx), %r15
movq %rbx, 0xb8(%rsp)
movl 0x7c(%rsp), %ecx
leaq -0x8(%rbp), %r14
cmpl %r13d, %ebx
jg 0x22e65
movq -0x8(%rbp), %rdi
movq (%rbp), %rsi
movq (%r12,%rbx,8), %rdx
movq %r14, %r8
movq %rbp, %r9
callq 0x2499e
movl %eax, %ecx
addl %eax, 0x8(%rbp)
incq %rbx
addq $0x18, %rbp
addq $0x18, %r15
jmp 0x22e33
cmpl 0x2c(%rsp), %r13d
jge 0x22e8e
movl -0x10(%rbp), %esi
subl %ecx, %esi
cmpl 0x30(%rsp), %esi
jg 0x22e8e
movq (%r12,%rbx,8), %rdx
testl %ecx, %ecx
js 0x22fbc
movl %edx, %eax
andl $0x1, %eax
jne 0x22fbc
movq 0x30(%rsp), %r12
leal 0x40(%r12), %eax
movq 0x68(%rsp), %rbx
cmpl %ebx, %r13d
jl 0x22eb1
cmpl %eax, 0x10(%r15)
jl 0x22eb1
decl %r13d
addq $-0x18, %r15
jmp 0x22e9d
movq %r15, %r14
testw $0x7ff, 0x28(%rsp)      # imm = 0x7FF
jne 0x22efa
testl %r13d, %r13d
js 0x22efa
cmpl %ebx, %r13d
jl 0x22efa
movq 0x10(%r14), %rax
movq %rax, 0x10(%rsp)
movups (%r14), %xmm0
movups %xmm0, (%rsp)
movl %r12d, %edi
callq 0x24a08
testb %al, %al
je 0x22efa
leal -0x1(%r13), %eax
addq $-0x18, %r14
testl %r13d, %r13d
movl %eax, %r13d
jg 0x22ec2
pushq $-0x1
popq %rax
movl %eax, %r13d
movl 0x118(%rsp), %edx
cmpl $0x2, %edx
sete %al
cmpl $-0x1, %r13d
sete %cl
leaq 0x18(%r14), %rbp
testb %cl, %al
cmoveq %r14, %rbp
movl $0x0, %eax
cmovnel %eax, %r13d
cmpl $0x2, %edx
je 0x22ffe
leal 0x40(%r12), %eax
movslq %r13d, %r15
leaq 0x1(%r15), %r14
movq 0xb8(%rsp), %rcx
cmpq %rcx, %r14
movq %rcx, %rbx
cmovgq %r14, %rbx
movq 0xb0(%rsp), %rdx
addq 0x90(%rsp), %rdx
cmpq %rcx, %rbx
je 0x22f6d
cmpl %eax, (%rdx)
jl 0x22f6a
incq %rcx
addq $0x18, %rdx
jmp 0x22f58
movq %rcx, %rbx
testw $0x7ff, 0x28(%rsp)      # imm = 0x7FF
jne 0x22ffe
movslq %ebx, %rbx
cmpq %rbx, %r14
cmovleq %rbx, %r14
imulq $0x18, %rbx, %r12
addq 0x60(%rsp), %r12
cmpq %r15, %rbx
jg 0x22ff6
movq 0x10(%r12), %rax
movq %rax, 0x10(%rsp)
movups (%r12), %xmm0
movups %xmm0, (%rsp)
movq 0x30(%rsp), %rdi
callq 0x24a08
testb %al, %al
je 0x22ff3
incq %rbx
addq $0x18, %r12
jmp 0x22f8d
orq $-0x1, -0x8(%rbp)
incl %r13d
andq $0x0, (%rbp)
pushq $-0x1
popq %rdi
movq %rsi, %rbx
xorl %esi, %esi
movq %r14, %r8
movq %rbp, %r9
callq 0x2499e
addl %ebx, %eax
addl $0x40, %eax
movl %eax, 0x8(%rbp)
movq 0x30(%rsp), %r12
movq 0x68(%rsp), %rbx
jmp 0x22eb4
movq %rbx, %r14
movl %r14d, %ebx
movq 0x30(%rsp), %r12
cmpl %ebx, %r13d
jl 0x2314a
movq %rbx, 0x68(%rsp)
cmpl 0x2c(%rsp), %r13d
jne 0x23079
movl 0x10(%rbp), %eax
cmpl %r12d, %eax
movq 0x58(%rsp), %rbx
movl 0x28(%rsp), %r14d
jg 0x23083
movl 0x24(%rsp), %esi
cmpl $-0x1, %esi
setne %cl
cmpl %esi, %eax
setg %dl
testb %dl, %cl
jne 0x23083
cmpl %esi, %eax
je 0x23054
movq 0x40(%rsp), %rcx
cmpq %rcx, 0x48(%rsp)
je 0x2304d
movq %rcx, 0x48(%rsp)
movl %eax, 0x24(%rsp)
movl %eax, %r12d
movl %r14d, %eax
subl 0x88(%rsp), %eax
movl %eax, 0xc0(%rsp)
leaq 0x40(%rsp), %rdi
leaq 0xc0(%rsp), %rsi
callq 0x24bf2
jmp 0x23083
movq 0x58(%rsp), %rbx
movl 0x28(%rsp), %r14d
incq %rbx
incl %r14d
jmp 0x22dcb
cmpl 0x2c(%rsp), %r13d
jne 0x23165
movq 0x10(%rbp), %rax
movq %rax, 0x10(%rsp)
movups (%rbp), %xmm0
movups %xmm0, (%rsp)
leaq 0xc0(%rsp), %rdi
callq 0x24a63
xorl %ebx, %ebx
movq 0x88(%rsp), %r13
testl %r13d, %r13d
movl $0x0, %r15d
cmovgl %r13d, %r15d
notl %r13d
addl 0x3c(%rsp), %r13d
leaq 0x40(%rsp), %r14
leaq 0x84(%rsp), %rbp
movq 0xc0(%rsp), %rcx
leal (%rbx,%r13), %eax
cmpq %rbx, %r15
je 0x23158
movl 0x24(%rsp), %edi
cmpl $-0x1, %edi
setne %sil
movl 0x4(%rcx,%rbx,4), %edx
incq %rbx
cmpl %edi, %edx
setg %dil
incl %eax
cmpl %r12d, %edx
jg 0x230ef
andb %dil, %sil
jne 0x230ef
cmpl 0x24(%rsp), %edx
je 0x23136
movq 0x40(%rsp), %rcx
cmpq %rcx, 0x48(%rsp)
je 0x2312f
movq %rcx, 0x48(%rsp)
movl %edx, 0x24(%rsp)
movl %edx, %r12d
movl %eax, 0x84(%rsp)
movq %r14, %rdi
movq %rbp, %rsi
callq 0x24bf2
jmp 0x230e3
movq 0x120(%rsp), %rax
movl 0x24(%rsp), %ecx
jmp 0x23171
leaq 0xc0(%rsp), %rdi
callq 0x22326
movl 0x24(%rsp), %ecx
movq 0x120(%rsp), %rax
movl %ecx, (%rax)
cmpl $-0x1, %ecx
je 0x231c4
movq 0x40(%rsp), %r14
movq 0x48(%rsp), %r15
movq %r15, %rdi
subq %r14, %rdi
movq %rdi, %rbx
shrq $0x2, %rbx
shlq $0x1e, %rdi
sarq $0x1e, %rdi
callq 0x6550
movq 0x128(%rsp), %rcx
movq %rax, (%rcx)
movq 0x130(%rsp), %rcx
movl %ebx, (%rcx)
subq %r14, %r15
je 0x231c4
movq %rax, %rdi
movq %r14, %rsi
movq %r15, %rdx
callq 0x6620
movq 0x60(%rsp), %rdi
callq 0x6500
leaq 0x40(%rsp), %rdi
callq 0x22326
addq $0xd8, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
movq %rax, %rbx
leaq 0xc0(%rsp), %rdi
callq 0x22326
jmp 0x23205
jmp 0x23202
jmp 0x23202
jmp 0x23202
movq %rax, %rbx
leaq 0x40(%rsp), %rdi
callq 0x22326
movq %rbx, %rdi
callq 0x66a0