// File: /workspace/github_cpp_makefile_star2_O2/VanAndelInstitute[P]intent/src/edlib.cpp
// Function: edlibAlign
EdlibAlignResult edlibAlign(const char* const queryOriginal, const int queryLength,
                                       const char* const targetOriginal, const int targetLength,
                                       const EdlibAlignConfig config) {
    EdlibAlignResult result;
    result.status = EDLIB_STATUS_OK;
    result.editDistance = -1;
    result.endLocations = result.startLocations = NULL;
    result.numLocations = 0;
    result.alignment = NULL;
    result.alignmentLength = 0;
    result.alphabetLength = 0;

    /*------------ TRANSFORM SEQUENCES AND RECOGNIZE ALPHABET -----------*/
    unsigned char* query, * target;
    string alphabet = transformSequences(queryOriginal, queryLength, targetOriginal, targetLength,
                                         &query, &target);
    result.alphabetLength = static_cast<int>(alphabet.size());
    /*-------------------------------------------------------*/

    // Handle special situation when at least one of the sequences has length 0.
    if (queryLength == 0 || targetLength == 0) {
        if (config.mode == EDLIB_MODE_NW) {
            result.editDistance = std::max(queryLength, targetLength);
            result.endLocations = static_cast<int *>(malloc(sizeof(int) * 1));
            result.endLocations[0] = targetLength - 1;
            result.numLocations = 1;
        } else if (config.mode == EDLIB_MODE_SHW || config.mode == EDLIB_MODE_HW) {
            result.editDistance = queryLength;
            result.endLocations = static_cast<int *>(malloc(sizeof(int) * 1));
            result.endLocations[0] = -1;
            result.numLocations = 1;
        } else {
            result.status = EDLIB_STATUS_ERROR;
        }

        free(query);
        free(target);
        return result;
    }

    /*--------------------- INITIALIZATION ------------------*/
    int maxNumBlocks = ceilDiv(queryLength, WORD_SIZE); // bmax in Myers
    int W = maxNumBlocks * WORD_SIZE - queryLength; // number of redundant cells in last level blocks
    EqualityDefinition equalityDefinition(alphabet, config.additionalEqualities, config.additionalEqualitiesLength);
    Word* Peq = buildPeq(static_cast<int>(alphabet.size()), query, queryLength, equalityDefinition);
    /*-------------------------------------------------------*/

    /*------------------ MAIN CALCULATION -------------------*/
    // TODO: Store alignment data only after k is determined? That could make things faster.
    int positionNW; // Used only when mode is NW.
    AlignmentData* alignData = NULL;
    bool dynamicK = false;
    int k = config.k;
    if (k < 0) { // If valid k is not given, auto-adjust k until solution is found.
        dynamicK = true;
        k = WORD_SIZE; // Gives better results than smaller k.
    }

    do {
        if (config.mode == EDLIB_MODE_HW || config.mode == EDLIB_MODE_SHW) {
            myersCalcEditDistanceSemiGlobal(Peq, W, maxNumBlocks,
                                            queryLength, target, targetLength,
                                            k, config.mode, &(result.editDistance),
                                            &(result.endLocations), &(result.numLocations));
        } else {  // mode == EDLIB_MODE_NW
            myersCalcEditDistanceNW(Peq, W, maxNumBlocks,
                                    queryLength, target, targetLength,
                                    k, &(result.editDistance), &positionNW,
                                    false, &alignData, -1);
        }
        k *= 2;
    } while(dynamicK && result.editDistance == -1);

    if (result.editDistance >= 0) {  // If there is solution.
        // If NW mode, set end location explicitly.
        if (config.mode == EDLIB_MODE_NW) {
            result.endLocations = static_cast<int *>(malloc(sizeof(int) * 1));
            result.endLocations[0] = targetLength - 1;
            result.numLocations = 1;
        }

        // Find starting locations.
        if (config.task == EDLIB_TASK_LOC || config.task == EDLIB_TASK_PATH) {
            result.startLocations = static_cast<int *>(malloc(result.numLocations * sizeof(int)));
            if (config.mode == EDLIB_MODE_HW) {  // If HW, I need to calculate start locations.
                const unsigned char* rTarget = createReverseCopy(target, targetLength);
                const unsigned char* rQuery  = createReverseCopy(query, queryLength);
                // Peq for reversed query.
                Word* rPeq = buildPeq(static_cast<int>(alphabet.size()), rQuery, queryLength, equalityDefinition);
                for (int i = 0; i < result.numLocations; i++) {
                    int endLocation = result.endLocations[i];
                    if (endLocation == -1) {
                        // NOTE: Sometimes one of optimal solutions is that query starts before target, like this:
                        //                       AAGG <- target
                        //                   CCTT     <- query
                        //   It will never be only optimal solution and it does not happen often, however it is
                        //   possible and in that case end location will be -1. What should we do with that?
                        //   Should we just skip reporting such end location, although it is a solution?
                        //   If we do report it, what is the start location? -4? -1? Nothing?
                        // TODO: Figure this out. This has to do in general with how we think about start
                        //   and end locations.
                        //   Also, we have alignment later relying on this locations to limit the space of it's
                        //   search -> how can it do it right if these locations are negative or incorrect?
                        result.startLocations[i] = 0;  // I put 0 for now, but it does not make much sense.
                    } else {
                        int bestScoreSHW, numPositionsSHW;
                        int* positionsSHW;
                        myersCalcEditDistanceSemiGlobal(
                                rPeq, W, maxNumBlocks,
                                queryLength, rTarget + targetLength - endLocation - 1, endLocation + 1,
                                result.editDistance, EDLIB_MODE_SHW,
                                &bestScoreSHW, &positionsSHW, &numPositionsSHW);
                        // Taking last location as start ensures that alignment will not start with insertions
                        // if it can start with mismatches instead.
                        result.startLocations[i] = endLocation - positionsSHW[numPositionsSHW - 1];
                        free(positionsSHW);
                    }
                }
                delete[] rTarget;
                delete[] rQuery;
                delete[] rPeq;
            } else {  // If mode is SHW or NW
                for (int i = 0; i < result.numLocations; i++) {
                    result.startLocations[i] = 0;
                }
            }
        }

        // Find alignment -> all comes down to finding alignment for NW.
        // Currently we return alignment only for first pair of locations.
        if (config.task == EDLIB_TASK_PATH) {
            int alnStartLocation = result.startLocations[0];
            int alnEndLocation = result.endLocations[0];
            const unsigned char* alnTarget = target + alnStartLocation;
            const int alnTargetLength = alnEndLocation - alnStartLocation + 1;
            const unsigned char* rAlnTarget = createReverseCopy(alnTarget, alnTargetLength);
            const unsigned char* rQuery  = createReverseCopy(query, queryLength);
            obtainAlignment(query, rQuery, queryLength,
                            alnTarget, rAlnTarget, alnTargetLength,
                            equalityDefinition, static_cast<int>(alphabet.size()), result.editDistance,
                            &(result.alignment), &(result.alignmentLength));
            delete[] rAlnTarget;
            delete[] rQuery;
        }
    }
    /*-------------------------------------------------------*/

    //--- Free memory ---//
    delete[] Peq;
    free(query);
    free(target);
    if (alignData) delete alignData;
    //-------------------//

    return result;
}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x101c8, %rsp          # imm = 0x101C8
movl %r8d, %r12d
movq %rcx, %rbx
movl %edx, %r15d
movq %rsi, %rbp
movq %rdi, 0xa8(%rsp)
movabsq $-0x100000000, %rax     # imm = 0xFFFFFFFF00000000
movq %rax, (%rdi)
leaq 0x4(%rdi), %rax
movq %rax, 0x20(%rsp)
leaq 0x8(%rdi), %rax
movq %rax, 0x38(%rsp)
leaq 0x18(%rdi), %rax
movq %rax, 0x18(%rsp)
leaq 0x20(%rdi), %rax
movq %rax, 0x90(%rsp)
leaq 0x28(%rdi), %rax
movq %rax, 0x88(%rsp)
andl $0x0, 0x18(%rdi)
xorps %xmm0, %xmm0
movups %xmm0, 0x8(%rdi)
movq %rdi, 0x48(%rsp)
movups %xmm0, 0x20(%rdi)
movslq %edx, %rdi
callq 0x6550
movq %rax, %r14
movq %r12, 0x10(%rsp)
movslq %r12d, %rdi
movq %rdi, 0xa0(%rsp)
callq 0x6550
movq %rax, 0x98(%rsp)
movq %rax, 0x40(%rsp)
leaq 0x5e60(%rip), %rsi      # 0x28435
leaq 0x68(%rsp), %rdi
leaq 0x1c0(%rsp), %rdx
callq 0x1463a
xorl %eax, %eax
cmpq $0x100, %rax            # imm = 0x100
je 0x225fe
movb $0x0, 0xc0(%rsp,%rax)
incq %rax
jmp 0x225e9
xorl %r13d, %r13d
testl %r15d, %r15d
movl %r15d, %eax
movl $0x0, %r15d
movl %eax, 0x28(%rsp)
cmovgl %eax, %r15d
leaq 0x68(%rsp), %r12
movq %r14, 0x60(%rsp)
cmpq %r13, %r15
je 0x2266f
movzbl (%rbp,%r13), %eax
cmpb $0x1, 0xc0(%rsp,%rax)
jne 0x2263e
movb 0x1c0(%rsp,%rax), %r14b
jmp 0x2265e
movb $0x1, 0xc0(%rsp,%rax)
movb 0x70(%rsp), %r14b
movb %r14b, 0x1c0(%rsp,%rax)
movsbl %al, %esi
movq %r12, %rdi
callq 0x6370
movq 0x60(%rsp), %rax
movb %r14b, (%rax,%r13)
movq %rax, %r14
incq %r13
jmp 0x2261f
xorl %r13d, %r13d
movq 0x10(%rsp), %rax
testl %eax, %eax
movl $0x0, %r15d
cmovgl %eax, %r15d
leaq 0x68(%rsp), %r12
cmpq %r13, %r15
je 0x226d4
movzbl (%rbx,%r13), %eax
cmpb $0x1, 0xc0(%rsp,%rax)
jne 0x226a6
movb 0x1c0(%rsp,%rax), %bpl
jmp 0x226c6
movb $0x1, 0xc0(%rsp,%rax)
movb 0x70(%rsp), %bpl
movb %bpl, 0x1c0(%rsp,%rax)
movsbl %al, %esi
movq %r12, %rdi
callq 0x6370
movq 0x40(%rsp), %rax
movb %bpl, (%rax,%r13)
incq %r13
jmp 0x22688
movl 0x70(%rsp), %eax
movq 0x48(%rsp), %rdx
movl %eax, 0x2c(%rdx)
movl 0x28(%rsp), %r15d
testl %r15d, %r15d
setne %al
cmpl $0x0, 0x10(%rsp)
setne %cl
testb %cl, %al
jne 0x22728
leaq 0x10200(%rsp), %rax
movl 0x4(%rax), %eax
testl %eax, %eax
movq 0x20(%rsp), %rcx
je 0x22745
cmpl $0x2, %eax
ja 0x2276d
movl %r15d, (%rcx)
pushq $0x4
popq %rdi
callq 0x6550
movq 0x38(%rsp), %rcx
movq %rax, (%rcx)
orl $-0x1, (%rax)
jmp 0x22768
pushq $0x40
popq %r13
testb $0x3f, %r15b
leaq 0x10200(%rsp), %rbp
movl %r15d, %eax
je 0x2278a
cltd
idivl %r13d
incl %eax
jmp 0x2278d
movq 0x10(%rsp), %rbx
cmpl %ebx, %r15d
cmovlel %ebx, %r15d
movl %r15d, (%rcx)
pushq $0x4
popq %rdi
callq 0x6550
movq 0x38(%rsp), %rcx
movq %rax, (%rcx)
decl %ebx
movl %ebx, (%rax)
movq 0x18(%rsp), %rdx
movl $0x1, (%rdx)
movq %r14, %rdi
callq 0x64b0
movq 0x40(%rsp), %rdi
callq 0x64b0
jmp 0x22b22
sarl $0x6, %eax
movl %eax, 0xc(%rsp)
movq 0x10(%rbp), %rdx
movl 0x18(%rbp), %ecx
leaq 0x1c0(%rsp), %rbx
leaq 0x68(%rsp), %r12
movq %rbx, %rdi
movq %r12, %rsi
callq 0x24ad2
movl 0x8(%r12), %edi
movq %r14, %rsi
movl %r15d, %edx
movq %rbx, %rcx
callq 0x22b6d
movq %rax, 0x50(%rsp)
movl 0xc(%rsp), %eax
shll $0x6, %eax
leaq 0xc0(%rsp), %rcx
andq $0x0, (%rcx)
subl %r15d, %eax
movl %eax, 0x2c(%rsp)
movl (%rbp), %ebx
movl 0x4(%rbp), %eax
testl %ebx, %ebx
cmovnsl %ebx, %r13d
movq %rax, 0x30(%rsp)
leal -0x1(%rax), %ebp
cmpl $0x1, %ebp
ja 0x22839
subq $0x8, %rsp
movq 0x58(%rsp), %rdi
movl 0x34(%rsp), %esi
movl 0x14(%rsp), %edx
movl %r15d, %ecx
movq 0x48(%rsp), %r8
movq 0x18(%rsp), %r9
pushq 0x20(%rsp)
pushq 0x48(%rsp)
movq 0x38(%rsp), %r12
pushq %r12
pushq 0x50(%rsp)
pushq %r13
callq 0x22c77
addq $0x30, %rsp
jmp 0x2287b
movq 0x50(%rsp), %rdi
movl 0x2c(%rsp), %esi
movl 0xc(%rsp), %edx
movl %r15d, %ecx
movq 0x40(%rsp), %r8
movq 0x10(%rsp), %r9
pushq $-0x1
leaq 0xc8(%rsp), %rax
pushq %rax
pushq $0x0
leaq 0xd4(%rsp), %rax
pushq %rax
movq 0x40(%rsp), %r12
pushq %r12
pushq %r13
callq 0x23217
addq $0x30, %rsp
movl (%r12), %r12d
testl %ebx, %ebx
jns 0x22890
addl %r13d, %r13d
cmpl $-0x1, %r12d
je 0x227f6
testl %r12d, %r12d
js 0x22ae6
cmpl $0x0, 0x30(%rsp)
jne 0x228c4
pushq $0x4
popq %rdi
callq 0x6550
movq 0x38(%rsp), %rcx
movq %rax, (%rcx)
movq 0x10(%rsp), %rcx
decl %ecx
movl %ecx, (%rax)
movq 0x18(%rsp), %rax
movl $0x1, (%rax)
leaq 0x10200(%rsp), %rax
movl 0x8(%rax), %ecx
leal -0x1(%rcx), %eax
cmpl $0x1, %eax
ja 0x22ae6
movq %rcx, 0x18(%rsp)
movq 0x48(%rsp), %rbx
movslq 0x18(%rbx), %rbp
movq %rbp, %rdi
shlq $0x2, %rdi
callq 0x6550
movq %rax, %r13
movq %rax, 0x10(%rbx)
cmpl $0x2, 0x30(%rsp)
jne 0x22a09
movq %rbp, 0x20(%rsp)
movq 0x40(%rsp), %rdi
movq 0x10(%rsp), %rsi
callq 0x239b0
movq %rax, 0x30(%rsp)
movq %r14, %rdi
movl %r15d, %esi
callq 0x239b0
movl 0x70(%rsp), %edi
leaq 0x1c0(%rsp), %rcx
movq %rax, 0x48(%rsp)
movq %rax, %rsi
movl %r15d, %edx
callq 0x22b6d
movq %rax, 0x10(%rsp)
movq 0x38(%rsp), %rax
movq (%rax), %rbx
xorl %ebp, %ebp
movq 0x20(%rsp), %rax
testl %eax, %eax
movl $0x0, %r14d
cmovgl %eax, %r14d
movq 0xa0(%rsp), %rax
movq 0x30(%rsp), %rcx
addq %rcx, %rax
decq %rax
movq %rax, 0x20(%rsp)
cmpq %rbp, %r14
je 0x22a2a
movslq (%rbx,%rbp,4), %r15
cmpq $-0x1, %r15
je 0x229f6
movq 0x20(%rsp), %r8
subq %r15, %r8
leal 0x1(%r15), %r9d
subq $0x8, %rsp
movq 0x18(%rsp), %rdi
movl 0x34(%rsp), %esi
movl 0x14(%rsp), %edx
movl 0x30(%rsp), %ecx
leaq 0x64(%rsp), %rax
pushq %rax
leaq 0xc0(%rsp), %rax
pushq %rax
leaq 0xd0(%rsp), %rax
pushq %rax
pushq $0x1
pushq %r12
callq 0x22c77
addq $0x30, %rsp
movq 0xb0(%rsp), %rdi
movslq 0x5c(%rsp), %rax
subl -0x4(%rdi,%rax,4), %r15d
movl %r15d, (%r13,%rbp,4)
callq 0x64b0
jmp 0x229fc
andl $0x0, (%r13,%rbp,4)
incq %rbp
movl 0x28(%rsp), %r15d
jmp 0x2297f
xorl %eax, %eax
testl %ebp, %ebp
movl $0x0, %ecx
cmovgl %ebp, %ecx
movq 0x18(%rsp), %rdx
cmpq %rax, %rcx
je 0x22a52
andl $0x0, (%r13,%rax,4)
incq %rax
jmp 0x22a1a
movq 0x30(%rsp), %rdi
callq 0x6500
movq 0x48(%rsp), %rdi
callq 0x6500
movq 0x10(%rsp), %rdi
callq 0x6500
movq 0x60(%rsp), %r14
movq 0x18(%rsp), %rdx
cmpl $0x2, %edx
jne 0x22ae6
movslq (%r13), %rax
movq 0x38(%rsp), %rcx
movq (%rcx), %rcx
movl (%rcx), %ebp
movq 0x98(%rsp), %rdi
addq %rax, %rdi
subl %eax, %ebp
incl %ebp
movq %rdi, %r13
movl %ebp, %esi
callq 0x239b0
movq %rax, %rbx
movq %r14, %rdi
movl %r15d, %esi
callq 0x239b0
movl %r15d, %edx
movq %r14, %r15
movq %rax, %r14
movl 0x70(%rsp), %eax
subq $0x8, %rsp
movq %r15, %rdi
movq %r14, %rsi
movq %r13, %rcx
movq %rbx, %r8
movl %ebp, %r9d
pushq 0x90(%rsp)
pushq 0xa0(%rsp)
pushq %r12
pushq %rax
leaq 0x1e8(%rsp), %rax
pushq %rax
callq 0x239f0
addq $0x30, %rsp
movq %rbx, %rdi
callq 0x6500
movq %r14, %rdi
callq 0x6500
movq %r15, %r14
movq 0x50(%rsp), %rdi
callq 0x6500
movq %r14, %rdi
callq 0x64b0
movq 0x40(%rsp), %rdi
callq 0x64b0
movq 0xc0(%rsp), %rbx
testq %rbx, %rbx
je 0x22b22
movq %rbx, %rdi
callq 0x24b8c
pushq $0x28
popq %rsi
movq %rbx, %rdi
callq 0x63e0
leaq 0x68(%rsp), %rdi
callq 0x6768
movq 0xa8(%rsp), %rax
addq $0x101c8, %rsp          # imm = 0x101C8
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
jmp 0x22b58
jmp 0x22b58
jmp 0x22b58
jmp 0x22b58
jmp 0x22b58
jmp 0x22b58
jmp 0x22b58
jmp 0x22b58
jmp 0x22b58
movq %rax, %rbx
leaq 0x68(%rsp), %rdi
callq 0x6768
movq %rbx, %rdi
callq 0x66a0