// File: /workspace/github_cpp_makefile_star2_O2/VanAndelInstitute[P]intent/src/edlib.cpp
// Function: myersCalcEditDistanceNW(unsigned long const*, int, int, int, unsigned char const*, int, int, int*, int*, bool, AlignmentData**, int)
static int myersCalcEditDistanceNW(const Word* const Peq, const int W, const int maxNumBlocks,
                                   const int queryLength,
                                   const unsigned char* const target, const int targetLength,
                                   int k, int* const bestScore_,
                                   int* const position_, const bool findAlignment,
                                   AlignmentData** const alignData, const int targetStopPosition) {
    if (targetStopPosition > -1 && findAlignment) {
        // They can not be both set at the same time!
        return EDLIB_STATUS_ERROR;
    }

    // Each STRONG_REDUCE_NUM column is reduced in more expensive way.
    const int STRONG_REDUCE_NUM = 2048; // TODO: Choose this number dinamically (based on query and target lengths?), so it does not affect speed of computation

    if (k < abs(targetLength - queryLength)) {
        *bestScore_ = *position_ = -1;
        return EDLIB_STATUS_OK;
    }

    k = min(k, max(queryLength, targetLength));  // Upper bound for k

    // 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;
    // This is optimal now, by my formula.
    int lastBlock = min(maxNumBlocks, ceilDiv(min(k, (k + queryLength - targetLength) / 2) + 1, WORD_SIZE)) - 1;
    Block* bl; // Current block

    Block* blocks = new Block[maxNumBlocks];

    // 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++;
    }

    // If we want to find alignment, we have to store needed data.
    if (findAlignment)
        *alignData = new AlignmentData(maxNumBlocks, targetLength);
    else if (targetStopPosition > -1)
        *alignData = new AlignmentData(maxNumBlocks, 1);
    else
        *alignData = NULL;

    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 = 1;
        bl = blocks + firstBlock;
        for (int b = firstBlock; b <= lastBlock; b++) {
            hout = calculateBlock(bl->P, bl->M, Peq_c[b], hout, bl->P, bl->M);
            bl->score += hout;
            bl++;
        }
        bl--;
        //------------------------------------------------------------------//
        // bl now points to last block

        // Update k. I do it only on end of column because it would slow calculation too much otherwise.
        // NOTICE: I add W when in last block because it is actually result from W cells to the left and W cells up.
        k = min(k, bl->score
                + max(targetLength - c - 1, queryLength - ((1 + lastBlock) * WORD_SIZE - 1) - 1)
                + (lastBlock == maxNumBlocks - 1 ? W : 0));

        //---------- Adjust number of blocks according to Ukkonen ----------//
        //--- Adjust last block ---//
        // If block is not beneath band, calculate next block. Only next because others are certainly beneath band.
        if (lastBlock + 1 < maxNumBlocks
            && !(//score[lastBlock] >= k + WORD_SIZE ||  // NOTICE: this condition could be satisfied if above block also!
                 ((lastBlock + 1) * WORD_SIZE - 1
                  > k - bl->score + 2 * WORD_SIZE - 2 - targetLength + c + queryLength))) {
            lastBlock++; bl++;
            bl->P = static_cast<Word>(-1); // All 1s
            bl->M = static_cast<Word>(0);
            int newHout = calculateBlock(bl->P, bl->M, Peq_c[lastBlock], hout, bl->P, bl->M);
            bl->score = (bl - 1)->score - hout + WORD_SIZE + newHout;
            hout = newHout;
        }

        // While block is out of band, move one block up.
        // NOTE: Condition used here is more loose than the one from the article, since I simplified the max() part of it.
        // I could consider adding that max part, for optimal performance.
        while (lastBlock >= firstBlock
               && (bl->score >= k + WORD_SIZE
                   || ((lastBlock + 1) * WORD_SIZE - 1 >
                       // TODO: Does not work if do not put +1! Why???
                       k - bl->score + 2 * WORD_SIZE - 2 - targetLength + c + queryLength + 1))) {
            lastBlock--; bl--;
        }
        //-------------------------//

        //--- Adjust first block ---//
        // While outside of band, advance block
        while (firstBlock <= lastBlock
               && (blocks[firstBlock].score >= k + WORD_SIZE
                   || ((firstBlock + 1) * WORD_SIZE - 1 <
                       blocks[firstBlock].score - k - targetLength + queryLength + c))) {
            firstBlock++;
        }
        //--------------------------/


        // TODO: consider if this part is useful, it does not seem to help much
        if (c % STRONG_REDUCE_NUM == 0) { // Every some columns do more expensive but more efficient reduction
            while (lastBlock >= firstBlock) {
                // If all cells outside of band, remove block
                vector<int> scores = getBlockCellValues(*bl);
                int numCells = lastBlock == maxNumBlocks - 1 ? WORD_SIZE - W : WORD_SIZE;
                int r = lastBlock * WORD_SIZE + numCells - 1;
                bool reduce = true;
                for (int i = WORD_SIZE - numCells; i < WORD_SIZE; i++) {
                    // TODO: Does not work if do not put +1! Why???
                    if (scores[i] <= k && r <= k - scores[i] - targetLength + c + queryLength + 1) {
                        reduce = false;
                        break;
                    }
                    r--;
                }
                if (!reduce) break;
                lastBlock--; bl--;
            }

            while (firstBlock <= lastBlock) {
                // If all cells outside of band, remove block
                vector<int> scores = getBlockCellValues(blocks[firstBlock]);
                int numCells = firstBlock == maxNumBlocks - 1 ? WORD_SIZE - W : WORD_SIZE;
                int r = firstBlock * WORD_SIZE + numCells - 1;
                bool reduce = true;
                for (int i = WORD_SIZE - numCells; i < WORD_SIZE; i++) {
                    if (scores[i] <= k && r >= scores[i] - k - targetLength + c + queryLength) {
                        reduce = false;
                        break;
                    }
                    r--;
                }
                if (!reduce) break;
                firstBlock++;
            }
        }


        // If band stops to exist finish
        if (lastBlock < firstBlock) {
            *bestScore_ = *position_ = -1;
            delete[] blocks;
            return EDLIB_STATUS_OK;
        }
        //------------------------------------------------------------------//


        //---- Save column so it can be used for reconstruction ----//
        if (findAlignment && c < targetLength) {
            bl = blocks + firstBlock;
            for (int b = firstBlock; b <= lastBlock; b++) {
                (*alignData)->Ps[maxNumBlocks * c + b] = bl->P;
                (*alignData)->Ms[maxNumBlocks * c + b] = bl->M;
                (*alignData)->scores[maxNumBlocks * c + b] = bl->score;
                (*alignData)->firstBlocks[c] = firstBlock;
                (*alignData)->lastBlocks[c] = lastBlock;
                bl++;
            }
        }
        //----------------------------------------------------------//
        //---- If this is stop column, save it and finish ----//
        if (c == targetStopPosition) {
            for (int b = firstBlock; b <= lastBlock; b++) {
                (*alignData)->Ps[b] = (blocks + b)->P;
                (*alignData)->Ms[b] = (blocks + b)->M;
                (*alignData)->scores[b] = (blocks + b)->score;
                (*alignData)->firstBlocks[0] = firstBlock;
                (*alignData)->lastBlocks[0] = lastBlock;
            }
            *bestScore_ = -1;
            *position_ = targetStopPosition;
            delete[] blocks;
            return EDLIB_STATUS_OK;
        }
        //----------------------------------------------------//

        targetChar++;
    }

    if (lastBlock == maxNumBlocks - 1) { // If last block of last column was calculated
        // Obtain best score from block -> it is complicated because query is padded with W cells
        int bestScore = getBlockCellValues(blocks[lastBlock])[W];
        if (bestScore <= k) {
            *bestScore_ = bestScore;
            *position_ = targetLength - 1;
            delete[] blocks;
            return EDLIB_STATUS_OK;
        }
    }

    *bestScore_ = *position_ = -1;
    delete[] blocks;
    return EDLIB_STATUS_OK;
}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0xe8, %rsp
movq %rdx, 0x40(%rsp)
movl %esi, 0x3c(%rsp)
movq %rdi, 0xd8(%rsp)
cmpl $0x0, 0x148(%rsp)
setns %al
testb %al, 0x138(%rsp)
je 0x23255
pushq $0x1
popq %rax
jmp 0x23986
movl %r9d, %ebx
movl %ecx, %r14d
movq 0x130(%rsp), %rsi
movq 0x128(%rsp), %r12
movl 0x120(%rsp), %eax
movl %r9d, %ecx
subl %r14d, %ecx
movl %ecx, %edx
negl %edx
cmovsl %ecx, %edx
cmpl %eax, %edx
jle 0x23290
orl $-0x1, (%rsi)
orl $-0x1, (%r12)
jmp 0x23984
movq %r8, %r13
movl %r14d, %ecx
subl %ebx, %ecx
movl %ebx, %esi
cmovgl %r14d, %esi
cmpl %esi, %eax
cmovll %eax, %esi
movq %rcx, 0x60(%rsp)
leal (%rcx,%rsi), %eax
pushq $0x2
popq %rcx
cltd
idivl %ecx
movl %eax, %r15d
cmpl %eax, %esi
cmovll %esi, %r15d
incl %r15d
pushq $0x40
popq %rbp
testb $0x3f, %r15b
movq %rsi, 0x30(%rsp)
je 0x232d9
movl %r15d, %eax
cltd
idivl %ebp
movl %eax, %r15d
incl %r15d
jmp 0x232dd
sarl $0x6, %r15d
movq 0x40(%rsp), %rax
cmpl %r15d, %eax
cmovll %eax, %r15d
leal -0x1(%r15), %ecx
movq %rcx, 0x80(%rsp)
cltq
pushq $0x18
popq %rcx
movq %rax, 0xb8(%rsp)
mulq %rcx
pushq $-0x1
popq %rdi
cmovnoq %rax, %rdi
callq 0x6030
movq %rax, 0x20(%rsp)
xorl %eax, %eax
testl %r15d, %r15d
cmovlel %eax, %r15d
imulq $0x18, %r15, %rcx
movq 0x20(%rsp), %rdx
cmpl %eax, %ecx
je 0x23344
movl %ebp, 0x10(%rdx,%rax)
orq $-0x1, (%rdx,%rax)
andq $0x0, 0x8(%rdx,%rax)
addq $0x18, %rax
addl $0x40, %ebp
jmp 0x23328
cmpb $0x0, 0x138(%rsp)
je 0x2336a
pushq $0x28
popq %rdi
callq 0x63d0
movq %rax, %r15
movq %rax, %rdi
movq 0x40(%rsp), %rsi
movl %ebx, %edx
callq 0x24e4e
jmp 0x23394
cmpl $0x0, 0x148(%rsp)
js 0x23391
pushq $0x28
popq %rdi
callq 0x63d0
movq %rax, %r15
pushq $0x1
popq %rdx
movq %rax, %rdi
movq 0x40(%rsp), %rsi
callq 0x24e4e
jmp 0x23394
xorl %r15d, %r15d
movq 0x30(%rsp), %r10
movq 0x140(%rsp), %rax
movq %r15, (%rax)
movq 0x40(%rsp), %rax
leal -0x1(%rax), %ebp
movq %r14, 0x90(%rsp)
leal 0x7e(%r14), %eax
movq %rax, 0xb0(%rsp)
subl %ebx, %eax
movq %rax, 0xc8(%rsp)
pushq $0x40
popq %rax
subl 0x3c(%rsp), %eax
movl %eax, 0x54(%rsp)
movq 0x60(%rsp), %rax
incl %eax
movq %rax, 0x60(%rsp)
xorl %r15d, %r15d
testl %ebx, %ebx
movl $0x0, %ecx
movq %rbx, 0x78(%rsp)
cmovgl %ebx, %ecx
movq %rcx, 0xd0(%rsp)
movq 0x20(%rsp), %rax
addq $0x10, %rax
movq %rax, 0xc0(%rsp)
xorl %eax, %eax
xorl %r14d, %r14d
movq 0x80(%rsp), %rbx
movl %ebp, 0x2c(%rsp)
movq %rax, 0x48(%rsp)
cmpq 0xd0(%rsp), %rax
je 0x2386e
movq %r15, 0xe0(%rsp)
movq %r13, 0x88(%rsp)
movzbl (%r13), %eax
imull 0x40(%rsp), %eax
cltq
movq 0xd8(%rsp), %rcx
leaq (%rcx,%rax,8), %rax
movq %rax, 0x58(%rsp)
movslq %r14d, %rax
movq %rbx, 0x80(%rsp)
movslq %ebx, %r13
imulq $0x18, %rax, %rbp
movq %rbp, 0x68(%rsp)
movq %rax, 0x70(%rsp)
movq %rax, %rbx
pushq $0x1
popq %r12
movq 0x20(%rsp), %rax
leaq (%rax,%rbp), %r15
cmpq %r13, %rbx
jg 0x234cf
leaq (%rax,%rbp), %r9
addq $0x8, %r9
movq (%rax,%rbp), %rdi
movq 0x8(%rax,%rbp), %rsi
movq 0x58(%rsp), %rax
movq (%rax,%rbx,8), %rdx
movl %r12d, %ecx
movq %r15, %r8
movq %r10, %r15
callq 0x2499e
movq %r15, %r10
movl %eax, %r12d
movq 0x20(%rsp), %rax
addl %r12d, 0x10(%rax,%rbp)
incq %rbx
addq $0x18, %rbp
jmp 0x23485
movq 0x48(%rsp), %r9
movl %r9d, %ecx
notl %ecx
movq 0x78(%rsp), %rdi
addl %edi, %ecx
movq 0x80(%rsp), %rbx
leal 0x1(%rbx), %r11d
movq %rax, %r8
movl %r11d, %eax
shll $0x6, %eax
movq 0x90(%rsp), %rdx
subl %eax, %edx
cmpl %edx, %ecx
cmovgl %ecx, %edx
cmpl 0x2c(%rsp), %ebx
movl $0x0, %esi
cmovel 0x3c(%rsp), %esi
movl -0x8(%r8,%rbp), %ecx
addl %ecx, %esi
addl %edx, %esi
cmpl %esi, %r10d
cmovgel %esi, %r10d
leaq (%r8,%rbp), %r13
addq $-0x18, %r13
cmpl 0x40(%rsp), %r11d
movq %r10, 0x30(%rsp)
jge 0x235a5
decl %eax
movq 0xb0(%rsp), %rdx
addl %r9d, %edx
addl %edi, %ecx
subl %ecx, %edx
addl %r10d, %edx
cmpl %edx, %eax
jg 0x235a5
movq 0x20(%rsp), %rbx
orq $-0x1, (%rbx,%rbp)
andq $0x0, 0x8(%rbx,%rbp)
leaq (%rbx,%rbp), %r9
addq $0x8, %r9
movslq %r11d, %rax
movq 0x58(%rsp), %rcx
movq (%rcx,%rax,8), %rdx
pushq $-0x1
popq %rdi
xorl %esi, %esi
movl %r12d, %ecx
movq %r15, %r8
movl %r11d, 0x58(%rsp)
callq 0x2499e
movq 0x30(%rsp), %r10
movl -0x8(%rbx,%rbp), %ecx
subl %r12d, %eax
addl %ecx, %eax
addl $0x40, %eax
movl %eax, 0x10(%rbx,%rbp)
movq %r15, %r13
movl 0x58(%rsp), %eax
movl %eax, %ebx
movl 0x2c(%rsp), %ebp
movq 0x70(%rsp), %r8
movq 0x68(%rsp), %r9
leal 0x40(%r10), %eax
movq 0xc8(%rsp), %rcx
movq 0x48(%rsp), %rdx
addl %edx, %ecx
addl %r10d, %ecx
movslq %ebx, %rbx
movl %ebx, %edx
shll $0x6, %edx
orl $0x3e, %edx
cmpl %r14d, %ebx
jl 0x235f5
movl 0x10(%r13), %esi
cmpl %eax, %esi
jge 0x235e9
movl %ecx, %edi
subl %esi, %edi
cmpl %edi, %edx
jle 0x235f5
addq $-0x18, %r13
decq %rbx
addl $-0x40, %edx
jmp 0x235d4
movq 0x90(%rsp), %rcx
movq 0x48(%rsp), %r12
leal (%r12,%rcx), %r15d
movq 0x78(%rsp), %rcx
addl %r10d, %ecx
subl %ecx, %r15d
shll $0x6, %r14d
orl $0x3f, %r14d
addq 0xc0(%rsp), %r9
cmpq %rbx, %r8
jg 0x23642
movl (%r9), %ecx
cmpl %eax, %ecx
jge 0x23635
addl %r15d, %ecx
cmpl %ecx, %r14d
jge 0x23642
incq %r8
addl $0x40, %r14d
addq $0x18, %r9
jmp 0x23621
testw $0x7ff, %r12w           # imm = 0x7FF
je 0x23657
movq 0x88(%rsp), %r13
jmp 0x237c2
movl %r12d, %ebp
addl 0x60(%rsp), %ebp
addl %r10d, %ebp
cmpl %r8d, %ebx
jl 0x23707
movq %r8, %r12
movq 0x10(%r13), %rax
movq %rax, 0x10(%rsp)
movups (%r13), %xmm0
movups %xmm0, (%rsp)
leaq 0x98(%rsp), %rdi
callq 0x24a63
movq 0x30(%rsp), %r9
cmpl 0x2c(%rsp), %ebx
pushq $0x40
popq %rcx
movl %ecx, %edx
cmovel 0x54(%rsp), %edx
movl %ebx, %esi
shll $0x6, %esi
subl %edx, %ecx
movq 0x98(%rsp), %rax
movslq %ecx, %rcx
addl %esi, %edx
decl %edx
movq %rcx, %r14
cmpq $0x3f, %rcx
jg 0x236dd
movl %edx, %esi
movl (%rax,%r14,4), %edi
leaq 0x1(%r14), %rcx
leal -0x1(%rsi), %edx
cmpl %r9d, %edi
jg 0x236b5
movl %ebp, %r8d
subl %edi, %r8d
cmpl %r8d, %esi
jg 0x236b5
jmp 0x236e3
decl %ebx
addq $-0x18, %r13
leaq 0x98(%rsp), %rdi
callq 0x22326
cmpq $0x3f, %r14
movq 0x30(%rsp), %r10
movq %r12, %r8
movq 0x48(%rsp), %r12
jg 0x23661
movq 0x88(%rsp), %r13
movl 0x2c(%rsp), %ebp
cmpl %ebx, %r8d
jg 0x237c2
movq %rbx, %r14
movslq %r8d, %rax
imulq $0x18, %rax, %rax
movq 0x20(%rsp), %rdx
movq 0x10(%rdx,%rax), %rcx
movq %rcx, 0x10(%rsp)
movups (%rdx,%rax), %xmm0
movups %xmm0, (%rsp)
leaq 0x98(%rsp), %rdi
movq %r8, %rbx
callq 0x24a63
movq 0x30(%rsp), %r8
cmpl %ebp, %ebx
pushq $0x40
popq %rdx
movl %edx, %eax
cmovel 0x54(%rsp), %eax
movq %rbx, %r12
movl %ebx, %ecx
shll $0x6, %ecx
addl %eax, %ecx
subl %eax, %edx
movq 0x98(%rsp), %rax
movslq %edx, %rsi
movq %rsi, %rbx
cmpq $0x3f, %rsi
jg 0x23798
movl %ecx, %edx
leal -0x1(%rdx), %ecx
movl (%rax,%rbx,4), %edi
leaq 0x1(%rbx), %rsi
cmpl %r8d, %edi
jg 0x23775
addl %r15d, %edi
cmpl %edi, %edx
jle 0x23775
jmp 0x2379b
incl %r12d
leaq 0x98(%rsp), %rdi
callq 0x22326
cmpq $0x3f, %rbx
movq 0x30(%rsp), %r10
movq %r14, %rbx
movq %r12, %r8
movq 0x48(%rsp), %r12
jg 0x23713
cmpl %r8d, %ebx
movq 0x140(%rsp), %r11
movq 0xe0(%rsp), %r15
jl 0x238d4
movq %r8, %r14
movslq %ebx, %rax
cmpb $0x0, 0x138(%rsp)
je 0x23840
movslq %r14d, %rcx
imulq $0x18, %rcx, %rdx
addq 0x20(%rsp), %rdx
leaq (%r15,%rcx), %rsi
cmpq %rax, %rcx
jg 0x23840
movq (%rdx), %rdi
movq (%r11), %r8
movq (%r8), %r9
movq %rdi, (%r9,%rsi,8)
movq 0x8(%rdx), %rdi
movq 0x8(%r8), %r9
movq %rdi, (%r9,%rsi,8)
movl 0x10(%rdx), %edi
movq 0x10(%r8), %r9
movl %edi, (%r9,%rsi,4)
movq 0x18(%r8), %rdi
movl %r14d, (%rdi,%r12,4)
movq 0x20(%r8), %rdi
movl %ebx, (%rdi,%r12,4)
addq $0x18, %rdx
incq %rcx
incq %rsi
jmp 0x237fb
movl 0x148(%rsp), %ecx
cmpq %rcx, %r12
je 0x238ef
incq %r13
incq %r12
addq 0xb8(%rsp), %r15
movq %r12, %rax
movq 0x128(%rsp), %r12
jmp 0x2341d
cmpl %ebp, %ebx
jne 0x238bf
movslq %ebp, %rax
imulq $0x18, %rax, %rax
movq 0x20(%rsp), %rdx
movq 0x10(%rdx,%rax), %rcx
movq %rcx, 0x10(%rsp)
movups (%rdx,%rax), %xmm0
movups %xmm0, (%rsp)
leaq 0x98(%rsp), %r15
movq %r15, %rdi
movq %r10, %r14
callq 0x24a63
movslq 0x3c(%rsp), %rax
movq (%r15), %rcx
movl (%rcx,%rax,4), %ebx
movq %r15, %rdi
callq 0x22326
cmpl %r14d, %ebx
jle 0x23965
movq 0x130(%rsp), %rax
orl $-0x1, (%rax)
orl $-0x1, (%r12)
jmp 0x2397a
movq 0x130(%rsp), %rax
orl $-0x1, (%rax)
movq 0x128(%rsp), %rax
orl $-0x1, (%rax)
jmp 0x2397a
movq %r14, %rcx
movslq %ecx, %rcx
imulq $0x18, %rcx, %rdx
movq 0x20(%rsp), %rsi
addq %rsi, %rdx
addq $0x10, %rdx
cmpq %rax, %rcx
jg 0x23947
movq -0x10(%rdx), %rsi
movq (%r11), %rdi
movq (%rdi), %r8
movq %rsi, (%r8,%rcx,8)
movq -0x8(%rdx), %rsi
movq 0x8(%rdi), %r8
movq %rsi, (%r8,%rcx,8)
movl (%rdx), %esi
movq 0x10(%rdi), %r8
movl %esi, (%r8,%rcx,4)
movq %r14, %r8
movq 0x18(%rdi), %rsi
movl %r8d, (%rsi)
movq 0x20(%rdi), %rsi
movl %ebx, (%rsi)
incq %rcx
addq $0x18, %rdx
jmp 0x23905
movq 0x128(%rsp), %rax
orl $-0x1, (%rax)
movl 0x148(%rsp), %eax
movq 0x130(%rsp), %rcx
movl %eax, (%rcx)
jmp 0x2397a
movl %ebx, (%r12)
movq 0x78(%rsp), %rcx
decl %ecx
movq 0x130(%rsp), %rax
movl %ecx, (%rax)
movq 0x20(%rsp), %rdi
callq 0x6500
xorl %eax, %eax
addq $0xe8, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
jmp 0x2399a
movq %rax, %rbx
pushq $0x28
popq %rsi
movq %r15, %rdi
callq 0x63e0
movq %rbx, %rdi
callq 0x66a0