// File: VanAndelInstitute[P]intent/src/edlib.cpp
// Function: obtainAlignment(unsigned char const*, unsigned char const*, int, unsigned char const*, unsigned char const*, int, EqualityDefinition const&, int, int, unsigned char**, int*)
static int obtainAlignment(
        const unsigned char* const query, const unsigned char* const rQuery, const int queryLength,
        const unsigned char* const target, const unsigned char* const rTarget, const int targetLength,
        const EqualityDefinition& equalityDefinition, const int alphabetLength, const int bestScore,
        unsigned char** const alignment, int* const alignmentLength) {

    // Handle special case when one of sequences has length of 0.
    if (queryLength == 0 || targetLength == 0) {
        *alignmentLength = targetLength + queryLength;
        *alignment = static_cast<unsigned char*>(malloc((*alignmentLength) * sizeof(unsigned char)));
        for (int i = 0; i < *alignmentLength; i++) {
            (*alignment)[i] = queryLength == 0 ? EDLIB_EDOP_DELETE : EDLIB_EDOP_INSERT;
        }
        return EDLIB_STATUS_OK;
    }

    const int maxNumBlocks = ceilDiv(queryLength, WORD_SIZE);
    const int W = maxNumBlocks * WORD_SIZE - queryLength;
    int statusCode;

    // TODO: think about reducing number of memory allocations in alignment functions, probably
    // by sharing some memory that is allocated only once. That refers to: Peq, columns in Hirschberg,
    // and it could also be done for alignments - we could have one big array for alignment that would be
    // sparsely populated by each of steps in recursion, and at the end we would just consolidate those results.

    // If estimated memory consumption for traceback algorithm is smaller than 1MB use it,
    // otherwise use Hirschberg's algorithm. By running few tests I choose boundary of 1MB as optimal.
    long long alignmentDataSize = (2ll * sizeof(Word) + sizeof(int)) * maxNumBlocks * targetLength
        + 2ll * sizeof(int) * targetLength;
    if (alignmentDataSize < 1024 * 1024) {
        int score_, endLocation_;  // Used only to call function.
        AlignmentData* alignData = NULL;
        Word* Peq = buildPeq(alphabetLength, query, queryLength, equalityDefinition);
        myersCalcEditDistanceNW(Peq, W, maxNumBlocks,
                                queryLength,
                                target, targetLength,
                                bestScore,
                                &score_, &endLocation_, true, &alignData, -1);
        //assert(score_ == bestScore);
        //assert(endLocation_ == targetLength - 1);

        statusCode = obtainAlignmentTraceback(queryLength, targetLength,
                                              bestScore, alignData, alignment, alignmentLength);
        delete alignData;
        delete[] Peq;
    } else {
        statusCode = obtainAlignmentHirschberg(query, rQuery, queryLength,
                                               target, rTarget, targetLength,
                                               equalityDefinition, alphabetLength, bestScore,
                                               alignment, alignmentLength);
    }
    return statusCode;
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
pushq %r14
pushq %rbx
subq $0xb0, %rsp
movq 0x30(%rbp), %rax
movq 0x28(%rbp), %rax
movl 0x20(%rbp), %eax
movl 0x18(%rbp), %eax
movq 0x10(%rbp), %rax
movq %rdi, -0x20(%rbp)
movq %rsi, -0x28(%rbp)
movl %edx, -0x2c(%rbp)
movq %rcx, -0x38(%rbp)
movq %r8, -0x40(%rbp)
movl %r9d, -0x44(%rbp)
cmpl $0x0, -0x2c(%rbp)
je 0x66f43
cmpl $0x0, -0x44(%rbp)
jne 0x66fb5
movl -0x44(%rbp), %ecx
addl -0x2c(%rbp), %ecx
movq 0x30(%rbp), %rax
movl %ecx, (%rax)
movq 0x30(%rbp), %rax
movslq (%rax), %rdi
shlq $0x0, %rdi
callq 0x8770
movq %rax, %rcx
movq 0x28(%rbp), %rax
movq %rcx, (%rax)
movl $0x0, -0x48(%rbp)
movl -0x48(%rbp), %eax
movq 0x30(%rbp), %rcx
cmpl (%rcx), %eax
jge 0x66fa9
movl -0x2c(%rbp), %edx
movl $0x1, %eax
movl $0x2, %ecx
cmpl $0x0, %edx
cmovel %ecx, %eax
movb %al, %dl
movq 0x28(%rbp), %rax
movq (%rax), %rax
movslq -0x48(%rbp), %rcx
movb %dl, (%rax,%rcx)
movl -0x48(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x48(%rbp)
jmp 0x66f70
movl $0x0, -0x14(%rbp)
jmp 0x67118
movl -0x2c(%rbp), %edi
movl $0x40, %esi
callq 0x655f0
movl %eax, -0x4c(%rbp)
movl -0x4c(%rbp), %eax
shll $0x6, %eax
subl -0x2c(%rbp), %eax
movl %eax, -0x50(%rbp)
movslq -0x4c(%rbp), %rax
imulq $0x14, %rax, %rax
movslq -0x44(%rbp), %rcx
imulq %rcx, %rax
movslq -0x44(%rbp), %rcx
shlq $0x3, %rcx
addq %rcx, %rax
movq %rax, -0x60(%rbp)
cmpq $0x100000, -0x60(%rbp)  # imm = 0x100000
jge 0x670c9
movq $0x0, -0x70(%rbp)
movl 0x18(%rbp), %edi
movq -0x20(%rbp), %rsi
movl -0x2c(%rbp), %edx
movq 0x10(%rbp), %rcx
callq 0x65630
movq %rax, -0x78(%rbp)
movq -0x78(%rbp), %rdi
movl -0x50(%rbp), %esi
movl -0x4c(%rbp), %edx
movl -0x2c(%rbp), %ecx
movq -0x38(%rbp), %r8
movl -0x44(%rbp), %r9d
movl 0x20(%rbp), %ebx
leaq -0x64(%rbp), %r11
leaq -0x68(%rbp), %r10
leaq -0x70(%rbp), %rax
movl %ebx, (%rsp)
movq %r11, 0x8(%rsp)
movq %r10, 0x10(%rsp)
movl $0x1, 0x18(%rsp)
movq %rax, 0x20(%rsp)
movl $0xffffffff, 0x28(%rsp) # imm = 0xFFFFFFFF
callq 0x66170
movl -0x2c(%rbp), %edi
movl -0x44(%rbp), %esi
movl 0x20(%rbp), %edx
movq -0x70(%rbp), %rcx
movq 0x28(%rbp), %r8
movq 0x30(%rbp), %r9
callq 0x67800
movl %eax, -0x54(%rbp)
movq -0x70(%rbp), %rax
movq %rax, -0x80(%rbp)
cmpq $0x0, %rax
je 0x670aa
movq -0x80(%rbp), %rdi
callq 0x68e60
movq -0x80(%rbp), %rdi
movl $0x28, %esi
callq 0x8560
movq -0x78(%rbp), %rax
movq %rax, -0x88(%rbp)
cmpq $0x0, %rax
je 0x670c7
movq -0x88(%rbp), %rdi
callq 0x8710
jmp 0x67112
movq -0x20(%rbp), %rdi
movq -0x28(%rbp), %rsi
movl -0x2c(%rbp), %edx
movq -0x38(%rbp), %rcx
movq -0x40(%rbp), %r8
movl -0x44(%rbp), %r9d
movq 0x10(%rbp), %r14
movl 0x18(%rbp), %ebx
movl 0x20(%rbp), %r11d
movq 0x28(%rbp), %r10
movq 0x30(%rbp), %rax
movq %r14, (%rsp)
movl %ebx, 0x8(%rsp)
movl %r11d, 0x10(%rsp)
movq %r10, 0x18(%rsp)
movq %rax, 0x20(%rsp)
callq 0x68190
movl %eax, -0x54(%rbp)
movl -0x54(%rbp), %eax
movl %eax, -0x14(%rbp)
movl -0x14(%rbp), %eax
addq $0xb0, %rsp
popq %rbx
popq %r14
popq %rbp
retq
nopw (%rax,%rax)