// File: /workspace/github_c_makefile_star2_O2/sc932[P]ALE/src/ssw.c
// Function: sw_sse2_word
static alignment_end* sw_sse2_word (const int8_t* ref,
							 int8_t ref_dir,	// 0: forward ref; 1: reverse ref
							 int32_t refLen,
							 int32_t readLen,
							 const uint8_t weight_gapO, /* will be used as - */
							 const uint8_t weight_gapE, /* will be used as - */
							 const __m128i* vProfile,
							 uint16_t terminate,
							 int32_t maskLen) {

#define max8(m, vm) (vm) = _mm_max_epi16((vm), _mm_srli_si128((vm), 8)); \
					(vm) = _mm_max_epi16((vm), _mm_srli_si128((vm), 4)); \
					(vm) = _mm_max_epi16((vm), _mm_srli_si128((vm), 2)); \
					(m) = _mm_extract_epi16((vm), 0)

	uint16_t max = 0;		                     /* the max alignment score */
	int32_t end_read = readLen - 1;
	int32_t end_ref = 0; /* 1_based best alignment ending point; Initialized as isn't aligned - 0. */
	int32_t segLen = (readLen + 7) / 8; /* number of segment */

	/* array to record the largest score of each reference position */
	uint16_t* maxColumn = (uint16_t*) calloc(refLen, 2);

	/* array to record the alignment read ending position of the largest score of each reference position */
	int32_t* end_read_column = (int32_t*) calloc(refLen, sizeof(int32_t));

	/* Define 16 byte 0 vector. */
	__m128i vZero = _mm_set1_epi32(0);

	__m128i* pvHStore = (__m128i*) calloc(segLen, sizeof(__m128i));
	__m128i* pvHLoad = (__m128i*) calloc(segLen, sizeof(__m128i));
	__m128i* pvE = (__m128i*) calloc(segLen, sizeof(__m128i));
	__m128i* pvHmax = (__m128i*) calloc(segLen, sizeof(__m128i));

	int32_t i, j, k;
	/* 16 byte insertion begin vector */
	__m128i vGapO = _mm_set1_epi16(weight_gapO);

	/* 16 byte insertion extension vector */
	__m128i vGapE = _mm_set1_epi16(weight_gapE);

	__m128i vMaxScore = vZero; /* Trace the highest score of the whole SW matrix. */
	__m128i vMaxMark = vZero; /* Trace the highest score till the previous column. */
	__m128i vTemp;
	int32_t edge, begin = 0, end = refLen, step = 1;

	/* outer loop to process the reference sequence */
	if (ref_dir == 1) {
		begin = refLen - 1;
		end = -1;
		step = -1;
	}
	for (i = begin; LIKELY(i != end); i += step) {
		int32_t cmp;
		__m128i e, vF = vZero; /* Initialize F value to 0.
							   Any errors to vH values will be corrected in the Lazy_F loop.
							 */
		__m128i vH = pvHStore[segLen - 1];
		vH = _mm_slli_si128 (vH, 2); /* Shift the 128-bit value in vH left by 2 byte. */

		/* Swap the 2 H buffers. */
		__m128i* pv = pvHLoad;

		__m128i vMaxColumn = vZero; /* vMaxColumn is used to record the max values of column i. */

		const __m128i* vP = vProfile + ref[i] * segLen; /* Right part of the vProfile */
		pvHLoad = pvHStore;
		pvHStore = pv;

		/* inner loop to process the query sequence */
		for (j = 0; LIKELY(j < segLen); j ++) {
			vH = _mm_adds_epi16(vH, _mm_load_si128(vP + j));

			/* Get max from vH, vE and vF. */
			e = _mm_load_si128(pvE + j);
			vH = _mm_max_epi16(vH, e);
			vH = _mm_max_epi16(vH, vF);
			vMaxColumn = _mm_max_epi16(vMaxColumn, vH);

			/* Save vH values. */
			_mm_store_si128(pvHStore + j, vH);

			/* Update vE value. */
			vH = _mm_subs_epu16(vH, vGapO); /* saturation arithmetic, result >= 0 */
			e = _mm_subs_epu16(e, vGapE);
			e = _mm_max_epi16(e, vH);
			_mm_store_si128(pvE + j, e);

			/* Update vF value. */
			vF = _mm_subs_epu16(vF, vGapE);
			vF = _mm_max_epi16(vF, vH);

			/* Load the next vH. */
			vH = _mm_load_si128(pvHLoad + j);
		}

		/* Lazy_F loop: has been revised to disallow adjecent insertion and then deletion, so don't update E(i, j), learn from SWPS3 */
		for (k = 0; LIKELY(k < 8); ++k) {
			vF = _mm_slli_si128 (vF, 2);
			for (j = 0; LIKELY(j < segLen); ++j) {
				vH = _mm_load_si128(pvHStore + j);
				vH = _mm_max_epi16(vH, vF);
				vMaxColumn = _mm_max_epi16(vMaxColumn, vH); //newly added line
				_mm_store_si128(pvHStore + j, vH);
				vH = _mm_subs_epu16(vH, vGapO);
				vF = _mm_subs_epu16(vF, vGapE);
				if (UNLIKELY(! _mm_movemask_epi8(_mm_cmpgt_epi16(vF, vH)))) goto end;
			}
		}

end:
		vMaxScore = _mm_max_epi16(vMaxScore, vMaxColumn);
		vTemp = _mm_cmpeq_epi16(vMaxMark, vMaxScore);
		cmp = _mm_movemask_epi8(vTemp);
		if (cmp != 0xffff) {
			uint16_t temp;
			vMaxMark = vMaxScore;
			max8(temp, vMaxScore);
			vMaxScore = vMaxMark;

			if (LIKELY(temp > max)) {
				max = temp;
				end_ref = i;
				for (j = 0; LIKELY(j < segLen); ++j) pvHmax[j] = pvHStore[j];
			}
		}

		/* Record the max score of current column. */
		max8(maxColumn[i], vMaxColumn);
		if (maxColumn[i] == terminate) break;
	}

	/* Trace the alignment ending position on read. */
	uint16_t *t = (uint16_t*)pvHmax;
	int32_t column_len = segLen * 8;
	for (i = 0; LIKELY(i < column_len); ++i, ++t) {
		int32_t temp;
		if (*t == max) {
			temp = i / 8 + i % 8 * segLen;
			if (temp < end_read) end_read = temp;
		}
	}

	free(pvHmax);
	free(pvE);
	free(pvHLoad);
	free(pvHStore);

	/* Find the most possible 2nd best alignment. */
	alignment_end* bests = (alignment_end*) calloc(2, sizeof(alignment_end));
	bests[0].score = max;
	bests[0].ref = end_ref;
	bests[0].read = end_read;

	bests[1].score = 0;
	bests[1].ref = 0;
	bests[1].read = 0;

	edge = (end_ref - maskLen) > 0 ? (end_ref - maskLen) : 0;
	for (i = 0; i < edge; i ++) {
		if (maxColumn[i] > bests[1].score) {
			bests[1].score = maxColumn[i];
			bests[1].ref = i;
		}
	}
	edge = (end_ref + maskLen) > refLen ? refLen : (end_ref + maskLen);
	for (i = edge; i < refLen; i ++) {
		if (maxColumn[i] > bests[1].score) {
			bests[1].score = maxColumn[i];
			bests[1].ref = i;
		}
	}

	free(maxColumn);
	free(end_read_column);
	return bests;
}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x38, %rsp
movl %r9d, 0x18(%rsp)
movl %r8d, 0x14(%rsp)
movl %edx, %r8d
movl %esi, 0x8(%rsp)
movq %rdi, 0x30(%rsp)
leal -0x1(%rcx), %ebx
leal 0x7(%rcx), %eax
pushq $0x8
popq %rcx
cltd
idivl %ecx
movl %eax, %r14d
movl %r8d, 0x1c(%rsp)
movslq %r8d, %rdi
movq %rdi, 0x28(%rsp)
pushq $0x2
popq %rsi
callq 0x3310
movq %rax, 0x20(%rsp)
movslq %r14d, %rbp
pushq $0x10
popq %r12
movq %rbp, %rdi
movq %r12, %rsi
callq 0x3310
movq %rax, %r15
movq %rbp, %rdi
movq %r12, %rsi
callq 0x3310
movq %rax, %r13
movq %rbp, %rdi
movq %r12, %rsi
callq 0x3310
movq %rax, %r12
movq %rbp, %rdi
pushq $0x10
popq %rsi
callq 0x3310
movq %r13, %r10
movd 0x14(%rsp), %xmm0
pshuflw $0x0, %xmm0, %xmm0      # xmm0 = xmm0[0,0,0,0,4,5,6,7]
pshufd $0x0, %xmm0, %xmm0      # xmm0 = xmm0[0,0,0,0]
movd 0x18(%rsp), %xmm1
pshuflw $0x0, %xmm1, %xmm1      # xmm1 = xmm1[0,0,0,0,4,5,6,7]
pshufd $0x0, %xmm1, %xmm1      # xmm1 = xmm1[0,0,0,0]
movq 0x28(%rsp), %r9
leal -0x1(%r9), %ecx
leal -0x1(%rbp), %edx
xorl %r8d, %r8d
testl %ebp, %ebp
cmovlel %r8d, %ebp
xorl %esi, %esi
cmpb $0x1, 0x8(%rsp)
setne %sil
movslq %ecx, %rcx
movslq %edx, %rdx
movl $0x0, %edi
movq %rdi, 0x8(%rsp)
cmovneq %r8, %rcx
leaq -0x1(%rsi,%rsi), %rsi
pushq $-0x1
popq %rdi
cmovneq %r9, %rdi
shlq $0x4, %rbp
pxor %xmm3, %xmm3
shlq $0x4, %rdx
pxor %xmm2, %xmm2
xorl %r13d, %r13d
movq %r15, %r8
movq %r10, %r15
cmpq %rdi, %rcx
je 0xc572
movdqa (%r8,%rdx), %xmm6
pslldq $0x2, %xmm6             # xmm6 = zero,zero,xmm6[0,1,2,3,4,5,6,7,8,9,10,11,12,13]
movq 0x30(%rsp), %r9
movsbl (%r9,%rcx), %r9d
imull %r14d, %r9d
movslq %r9d, %r9
shlq $0x4, %r9
addq 0x70(%rsp), %r9
pxor %xmm5, %xmm5
xorl %r10d, %r10d
pxor %xmm4, %xmm4
cmpq %r10, %rbp
je 0xc35f
paddsw (%r9,%r10), %xmm6
movdqa (%r12,%r10), %xmm7
movdqa %xmm7, %xmm8
pmaxsw %xmm5, %xmm8
pmaxsw %xmm6, %xmm8
pmaxsw %xmm8, %xmm4
movdqa %xmm8, (%r15,%r10)
psubusw %xmm0, %xmm8
psubusw %xmm1, %xmm7
pmaxsw %xmm8, %xmm7
movdqa %xmm7, (%r12,%r10)
psubusw %xmm1, %xmm5
pmaxsw %xmm8, %xmm5
movdqa (%r8,%r10), %xmm6
addq $0x10, %r10
jmp 0xc30b
xorl %r9d, %r9d
cmpl $0x8, %r9d
je 0xc3ae
pslldq $0x2, %xmm5             # xmm5 = zero,zero,xmm5[0,1,2,3,4,5,6,7,8,9,10,11,12,13]
xorl %r10d, %r10d
cmpq %r10, %rbp
je 0xc3a9
movdqa (%r15,%r10), %xmm6
pmaxsw %xmm5, %xmm6
pmaxsw %xmm6, %xmm4
movdqa %xmm6, (%r15,%r10)
psubusw %xmm0, %xmm6
psubusw %xmm1, %xmm5
movdqa %xmm5, %xmm7
pcmpgtw %xmm6, %xmm7
pmovmskb %xmm7, %r11d
testl %r11d, %r11d
je 0xc3ae
addq $0x10, %r10
jmp 0xc370
incl %r9d
jmp 0xc362
pmaxsw %xmm4, %xmm2
movdqa %xmm3, %xmm5
pcmpeqw %xmm2, %xmm5
pmovmskb %xmm5, %r9d
cmpl $0xffff, %r9d           # imm = 0xFFFF
je 0xc427
movdqa %xmm2, %xmm3
psrldq $0x8, %xmm3             # xmm3 = xmm3[8,9,10,11,12,13,14,15],zero,zero,zero,zero,zero,zero,zero,zero
pmaxsw %xmm2, %xmm3
movdqa %xmm3, %xmm5
psrldq $0x4, %xmm5             # xmm5 = xmm5[4,5,6,7,8,9,10,11,12,13,14,15],zero,zero,zero,zero
pmaxsw %xmm3, %xmm5
movdqa %xmm5, %xmm3
psrldq $0x2, %xmm3             # xmm3 = xmm3[2,3,4,5,6,7,8,9,10,11,12,13,14,15],zero,zero
pmaxsw %xmm5, %xmm3
movd %xmm3, %r9d
movdqa %xmm2, %xmm3
cmpw %r13w, %r9w
jbe 0xc427
xorl %r10d, %r10d
cmpq %r10, %rbp
je 0xc418
movdqa (%r15,%r10), %xmm3
movdqa %xmm3, (%rax,%r10)
addq $0x10, %r10
jmp 0xc401
movdqa %xmm2, %xmm3
movl %ecx, %r10d
movq %r10, 0x8(%rsp)
movl %r9d, %r13d
movdqa %xmm4, %xmm5
psrldq $0x8, %xmm5             # xmm5 = xmm5[8,9,10,11,12,13,14,15],zero,zero,zero,zero,zero,zero,zero,zero
pmaxsw %xmm4, %xmm5
movdqa %xmm5, %xmm4
psrldq $0x4, %xmm4             # xmm4 = xmm4[4,5,6,7,8,9,10,11,12,13,14,15],zero,zero,zero,zero
pmaxsw %xmm5, %xmm4
movdqa %xmm4, %xmm5
psrldq $0x2, %xmm5             # xmm5 = xmm5[2,3,4,5,6,7,8,9,10,11,12,13,14,15],zero,zero
pmaxsw %xmm4, %xmm5
movd %xmm5, %r9d
movq 0x20(%rsp), %r10
movw %r9w, (%r10,%rcx,2)
addq %rsi, %rcx
movq %r8, %r10
movq %r8, %r11
cmpw 0x78(%rsp), %r9w
jne 0xc2cc
leal (,%r14,8), %ecx
xorl %edx, %edx
testl %ecx, %ecx
cmovlel %edx, %ecx
cmpl %edx, %ecx
je 0xc4a6
cmpw %r13w, (%rax,%rdx,2)
jne 0xc4a1
movl %edx, %esi
shrl $0x3, %esi
movl %edx, %edi
andl $0x7, %edi
imull %r14d, %edi
addl %esi, %edi
cmpl %ebx, %edi
cmovll %edi, %ebx
incq %rdx
jmp 0xc481
movq %rax, %rdi
movq %r11, %r14
callq 0x3070
movq %r12, %rdi
callq 0x3070
movq %r14, %rdi
callq 0x3070
movq %r15, %rdi
callq 0x3070
pushq $0x2
popq %rdi
pushq $0xc
popq %rsi
callq 0x3310
movq %rax, %r14
movw %r13w, (%rax)
movq 0x8(%rsp), %r9
movl %r9d, 0x4(%rax)
movl %ebx, 0x8(%rax)
andw $0x0, 0xc(%rax)
andq $0x0, 0x10(%rax)
movl %r9d, %ecx
movl 0x80(%rsp), %r8d
subl %r8d, %ecx
xorl %eax, %eax
testl %ecx, %ecx
cmovlel %eax, %ecx
xorl %edx, %edx
movq 0x20(%rsp), %rdi
cmpq %rdx, %rcx
je 0xc52b
movzwl (%rdi,%rdx,2), %esi
cmpw %ax, %si
jbe 0xc526
movw %si, 0xc(%r14)
movl %edx, 0x10(%r14)
movl %esi, %eax
incq %rdx
jmp 0xc50d
addl %r8d, %r9d
movl 0x1c(%rsp), %edx
cmpl %edx, %r9d
cmovgel %edx, %r9d
movslq %r9d, %rcx
movl %edx, %edx
cmpl %ecx, %edx
je 0xc55b
movzwl (%rdi,%rcx,2), %esi
cmpw %ax, %si
jbe 0xc556
movw %si, 0xc(%r14)
movl %ecx, 0x10(%r14)
movl %esi, %eax
incq %rcx
jmp 0xc53e
callq 0x3070
movq %r14, %rax
addq $0x38, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
movq %r15, %r11
movq %r8, %r15
jmp 0xc472