// File: codeandkey[P]neocortex/src/search.cpp
// Function: neocortex::search::Search::alphabeta(neocortex::Position&, int, int, int, int, neocortex::PV*, std::atomic<bool>&)
int search::Search::alphabeta(Position& root, int depth, int alpha, int beta, int ply_dist, PV* pv_line, std::atomic<bool>& abort_watch) {
	PV local_pv;
	Move tt_move, tt_exact_move;
	int value;
	int depth_reduction = 1;
	int depth_extension = 0;

	if (allocated_time_expired() || abort_watch) {
		return score::INCOMPLETE;
	}

	/* Check for threefold repetition or 50-move rule */
	if (root.num_repetitions() >= 3 || root.halfmove_clock() >= 50) {
		return eval::CONTEMPT;
	}

	if (!depth) {
		return quiescence(root, search::QDEPTH, alpha, beta, ply_dist, pv_line);
	}

	int alpha_orig = alpha;

	/* Look up cached results in TT */

	tt::entry* entry = tt::lookup(root.get_tt_key());

	tt::lock(root.get_tt_key());
	if (entry->key == root.get_tt_key() && entry->depth >= 1) {
		if (entry->depth >= depth) {
			switch (entry->type) {
			case tt::entry::EXACT:
				/* Get exact pv move from TT */
				tt_exact_move = entry->pv_move;
			case tt::entry::LOWERBOUND:
				if (entry->value > alpha) alpha = entry->value;
				break;
			case tt::entry::UPPERBOUND:
				if (entry->value < beta) beta = entry->value;
				break;
			}
		}
		else {
			/* TT entry isn't deep enough to use, but we keep the PV move for ordering */
			if (entry->type == tt::entry::EXACT) {
				tt_move = entry->pv_move;
			}
		}
	}
	tt::unlock(root.get_tt_key());

	/* If there is an exact PV, just search under the PV move */
	if (tt_exact_move.is_valid()) {
		// Re-search under pv node to regenerate complete pv, should be fast as all should hit TT
		root.make_move(tt_exact_move);

		value = -alphabeta(root, depth + depth_extension - depth_reduction, -beta, -alpha, ply_dist + 1, &local_pv, abort_watch);

		pv_line->moves[0] = tt_exact_move;
		pv_line->len = local_pv.len + 1;

		memcpy(pv_line->moves + 1, local_pv.moves, local_pv.len * sizeof local_pv.moves[0]);
		root.unmake_move(tt_exact_move);

		return value;
	}

	/* Perform null move reduction if available */
	if (root.null_move_allowed() && depth >= search::NULL_MOVE_REDUCTION) {
		root.make_move(Move::null);
		int nm_score = -alphabeta(root, depth - search::NULL_MOVE_REDUCTION, -beta, -alpha, ply_dist + 1, &local_pv, abort_watch);
		root.unmake_move(Move::null);

		if (nm_score >= beta) {
			return beta; // cutoff
		}
	}

	/* Check / capture extension */
	if (depth >= 5) { // don't do excessive extensions near horizon
		if (root.check() || root.capture()) {
			depth_extension++;
		}
	}

	Move pl_moves[MAX_PL_MOVES];
	int num_pl_moves = root.pseudolegal_moves(pl_moves);
	int num_moves = 0; /* num of legal moves */

	root.order_moves(pl_moves, num_pl_moves, tt_move);

	for (int i = 0; i < num_pl_moves; ++i) {
		if (root.make_move(pl_moves[i])) {

			num_moves++;

			int new_depth = depth + depth_extension - depth_reduction;

			if (new_depth < 0) {
				new_depth = 0;
			}

			value = -alphabeta(root, new_depth, -beta, -alpha, ply_dist + 1, &local_pv, abort_watch);

			/*if (depth == 1) {
				neocortex_debug("Made move %s, resulting score %d\n", pl_moves[i].to_uci().c_str(), value);
			}*/

			root.unmake_move(pl_moves[i]);

			if (value == score::INCOMPLETE) {
				return value;
			}

			if (value >= beta) {
				// Add history bonus for cutoff in normal AB
				root.add_history_bonus(pl_moves[i], depth);
				return beta;
			}

			if (value > alpha) {
				alpha = value;

				pv_line->moves[0] = pl_moves[i];
				pv_line->len = local_pv.len + 1;

				memcpy(pv_line->moves + 1, local_pv.moves, local_pv.len * sizeof local_pv.moves[0]);
			}
		}
		else {
			root.unmake_move(pl_moves[i]);
		}
	}

	if (!num_moves) {
		if (root.check()) {
			pv_line->len = 0;
			return score::CHECKMATED + ply_dist;
		}
		else {
			pv_line->len = 0;
			return eval::CONTEMPT;
		}
	}

	/* Write result to TT */
	tt::lock(root.get_tt_key());
	entry->key = root.get_tt_key();
	entry->value = alpha;
	entry->depth = depth;

	if (alpha <= alpha_orig) {
		entry->type = tt::entry::UPPERBOUND;
	}
	else if (alpha >= beta) {
		entry->type = tt::entry::LOWERBOUND;
	}
	else {
		assert(pv_line->moves[0].is_valid());

		entry->pv_move = pv_line->moves[0];
		entry->type = tt::entry::EXACT;
	}
	tt::unlock(root.get_tt_key());

	return alpha;
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0xc50, %rsp            # imm = 0xC50
movq 0x18(%rbp), %rax
movq 0x10(%rbp), %rax
movq %rdi, -0x10(%rbp)
movq %rsi, -0x18(%rbp)
movl %edx, -0x1c(%rbp)
movl %ecx, -0x20(%rbp)
movl %r8d, -0x24(%rbp)
movl %r9d, -0x28(%rbp)
movq -0x10(%rbp), %rax
movq %rax, -0xc18(%rbp)
leaq -0x62c(%rbp), %rdi
callq 0xae50
leaq -0x638(%rbp), %rdi
callq 0xa870
leaq -0x644(%rbp), %rdi
callq 0xa870
movq -0xc18(%rbp), %rdi
movl $0x1, -0x64c(%rbp)
movl $0x0, -0x650(%rbp)
callq 0x17b50
testb $0x1, %al
jne 0x1706b
movq 0x18(%rbp), %rdi
callq 0x194f0
testb $0x1, %al
jne 0x1706b
jmp 0x17077
movl $0x80000000, -0x4(%rbp) # imm = 0x80000000
jmp 0x1793a
movq -0x18(%rbp), %rdi
callq 0x13fd0
cmpl $0x3, %eax
jge 0x17093
movq -0x18(%rbp), %rdi
callq 0x14080
cmpl $0x32, %eax
jl 0x1709f
movl $0x0, -0x4(%rbp)
jmp 0x1793a
cmpl $0x0, -0x1c(%rbp)
jne 0x170d5
movq -0xc18(%rbp), %rdi
movq -0x18(%rbp), %rsi
movl -0x20(%rbp), %ecx
movl -0x24(%rbp), %r8d
movl -0x28(%rbp), %r9d
movq 0x10(%rbp), %rax
movl $0x4, %edx
movq %rax, (%rsp)
callq 0x17c10
movl %eax, -0x4(%rbp)
jmp 0x1793a
movl -0x20(%rbp), %eax
movl %eax, -0x654(%rbp)
movq -0x18(%rbp), %rdi
callq 0x13fa0
movq %rax, %rdi
callq 0x1c220
movq %rax, -0x660(%rbp)
movq -0x18(%rbp), %rdi
callq 0x13fa0
movq %rax, %rdi
callq 0x1c250
movq -0x660(%rbp), %rax
movq (%rax), %rax
movq %rax, -0xc20(%rbp)
movq -0x18(%rbp), %rdi
callq 0x13fa0
movq %rax, %rcx
movq -0xc20(%rbp), %rax
cmpq %rcx, %rax
jne 0x1720d
movq -0x660(%rbp), %rax
cmpl $0x1, 0x1c(%rax)
jl 0x1720d
movq -0x660(%rbp), %rax
movl 0x1c(%rax), %eax
cmpl -0x1c(%rbp), %eax
jl 0x171e1
movq -0x660(%rbp), %rax
movl 0x14(%rax), %eax
movl %eax, -0xc24(%rbp)
testl %eax, %eax
je 0x171a3
jmp 0x1716e
movl -0xc24(%rbp), %eax
subl $0x1, %eax
je 0x171c1
jmp 0x1717b
movl -0xc24(%rbp), %eax
subl $0x2, %eax
jne 0x171df
jmp 0x17188
movq -0x660(%rbp), %rax
movq 0x8(%rax), %rcx
movq %rcx, -0x644(%rbp)
movl 0x10(%rax), %eax
movl %eax, -0x63c(%rbp)
movq -0x660(%rbp), %rax
movl 0x18(%rax), %eax
cmpl -0x20(%rbp), %eax
jle 0x171bf
movq -0x660(%rbp), %rax
movl 0x18(%rax), %eax
movl %eax, -0x20(%rbp)
jmp 0x171df
movq -0x660(%rbp), %rax
movl 0x18(%rax), %eax
cmpl -0x24(%rbp), %eax
jge 0x171dd
movq -0x660(%rbp), %rax
movl 0x18(%rax), %eax
movl %eax, -0x24(%rbp)
jmp 0x171df
jmp 0x1720b
movq -0x660(%rbp), %rax
cmpl $0x2, 0x14(%rax)
jne 0x17209
movq -0x660(%rbp), %rax
movq 0x8(%rax), %rcx
movq %rcx, -0x638(%rbp)
movl 0x10(%rax), %eax
movl %eax, -0x630(%rbp)
jmp 0x1720b
jmp 0x1720d
movq -0x18(%rbp), %rdi
callq 0x13fa0
movq %rax, %rdi
callq 0x1c2a0
leaq -0x644(%rbp), %rdi
callq 0xacf0
testb $0x1, %al
jne 0x17233
jmp 0x17376
movq -0x18(%rbp), %rdi
movq -0x644(%rbp), %rax
movq %rax, -0x66c(%rbp)
movl -0x63c(%rbp), %eax
movl %eax, -0x664(%rbp)
movq -0x66c(%rbp), %rax
movq %rax, -0x67c(%rbp)
movl -0x664(%rbp), %eax
movl %eax, -0x674(%rbp)
movq -0x67c(%rbp), %rsi
movl -0x674(%rbp), %edx
callq 0xd190
movq -0xc18(%rbp), %rdi
movq -0x18(%rbp), %rsi
movl -0x1c(%rbp), %edx
addl -0x650(%rbp), %edx
subl -0x64c(%rbp), %edx
xorl %ecx, %ecx
subl -0x24(%rbp), %ecx
xorl %r8d, %r8d
subl -0x20(%rbp), %r8d
movl -0x28(%rbp), %r9d
addl $0x1, %r9d
movq 0x18(%rbp), %rax
leaq -0x62c(%rbp), %r10
movq %r10, (%rsp)
movq %rax, 0x8(%rsp)
callq 0x16fe0
movl %eax, %ecx
xorl %eax, %eax
subl %ecx, %eax
movl %eax, -0x648(%rbp)
movq 0x10(%rbp), %rax
movq -0x644(%rbp), %rcx
movq %rcx, 0x4(%rax)
movl -0x63c(%rbp), %ecx
movl %ecx, 0xc(%rax)
movl -0x62c(%rbp), %ecx
addl $0x1, %ecx
movq 0x10(%rbp), %rax
movl %ecx, (%rax)
movq 0x10(%rbp), %rdi
addq $0x4, %rdi
addq $0xc, %rdi
leaq -0x62c(%rbp), %rsi
addq $0x4, %rsi
movslq -0x62c(%rbp), %rax
imulq $0xc, %rax, %rdx
callq 0x5360
movq -0x18(%rbp), %rdi
movq -0x644(%rbp), %rax
movq %rax, -0x688(%rbp)
movl -0x63c(%rbp), %eax
movl %eax, -0x680(%rbp)
movq -0x688(%rbp), %rax
movq %rax, -0x698(%rbp)
movl -0x680(%rbp), %eax
movl %eax, -0x690(%rbp)
movq -0x698(%rbp), %rsi
movl -0x690(%rbp), %edx
callq 0xdd20
movl -0x648(%rbp), %eax
movl %eax, -0x4(%rbp)
jmp 0x1793a
movq -0x18(%rbp), %rdi
callq 0x197d0
testb $0x1, %al
jne 0x17388
jmp 0x17488
cmpl $0x3, -0x1c(%rbp)
jl 0x17488
movq -0x18(%rbp), %rdi
leaq 0x258eb(%rip), %rax     # 0x3cc88
movq (%rax), %rcx
movq %rcx, -0x6a4(%rbp)
movl 0x8(%rax), %eax
movl %eax, -0x69c(%rbp)
movq -0x6a4(%rbp), %rax
movq %rax, -0x6b4(%rbp)
movl -0x69c(%rbp), %eax
movl %eax, -0x6ac(%rbp)
movq -0x6b4(%rbp), %rsi
movl -0x6ac(%rbp), %edx
callq 0xd190
movq -0xc18(%rbp), %rdi
movq -0x18(%rbp), %rsi
movl -0x1c(%rbp), %edx
subl $0x3, %edx
xorl %ecx, %ecx
subl -0x24(%rbp), %ecx
xorl %r8d, %r8d
subl -0x20(%rbp), %r8d
movl -0x28(%rbp), %r9d
addl $0x1, %r9d
movq 0x18(%rbp), %rax
leaq -0x62c(%rbp), %r10
movq %r10, (%rsp)
movq %rax, 0x8(%rsp)
callq 0x16fe0
movl %eax, %ecx
xorl %eax, %eax
subl %ecx, %eax
movl %eax, -0x6b8(%rbp)
movq -0x18(%rbp), %rdi
leaq 0x25857(%rip), %rax     # 0x3cc88
movq (%rax), %rcx
movq %rcx, -0x6c4(%rbp)
movl 0x8(%rax), %eax
movl %eax, -0x6bc(%rbp)
movq -0x6c4(%rbp), %rax
movq %rax, -0x6d4(%rbp)
movl -0x6bc(%rbp), %eax
movl %eax, -0x6cc(%rbp)
movq -0x6d4(%rbp), %rsi
movl -0x6cc(%rbp), %edx
callq 0xdd20
movl -0x6b8(%rbp), %eax
cmpl -0x24(%rbp), %eax
jl 0x17486
movl -0x24(%rbp), %eax
movl %eax, -0x4(%rbp)
jmp 0x1793a
jmp 0x17488
cmpl $0x5, -0x1c(%rbp)
jl 0x174bb
movq -0x18(%rbp), %rdi
callq 0xbc10
testb $0x1, %al
jne 0x174aa
movq -0x18(%rbp), %rdi
callq 0xbbe0
testb $0x1, %al
jne 0x174aa
jmp 0x174b9
movl -0x650(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x650(%rbp)
jmp 0x174bb
leaq -0xb90(%rbp), %rax
movq %rax, %rcx
addq $0x4b0, %rcx            # imm = 0x4B0
movq %rcx, -0xc38(%rbp)
movq %rax, -0xc30(%rbp)
movq -0xc30(%rbp), %rdi
movq %rdi, -0xc40(%rbp)
callq 0xa870
movq -0xc40(%rbp), %rax
movq -0xc38(%rbp), %rcx
addq $0xc, %rax
cmpq %rcx, %rax
movq %rax, -0xc30(%rbp)
jne 0x174da
movq -0x18(%rbp), %rdi
leaq -0xb90(%rbp), %rsi
callq 0xe050
movl %eax, -0xb94(%rbp)
movl $0x0, -0xb98(%rbp)
movq -0x18(%rbp), %rdi
leaq -0xb90(%rbp), %rsi
movl -0xb94(%rbp), %edx
movq -0x638(%rbp), %rax
movq %rax, -0xba4(%rbp)
movl -0x630(%rbp), %eax
movl %eax, -0xb9c(%rbp)
movq -0xba4(%rbp), %rax
movq %rax, -0xbb4(%rbp)
movl -0xb9c(%rbp), %eax
movl %eax, -0xbac(%rbp)
movq -0xbb4(%rbp), %rcx
movl -0xbac(%rbp), %r8d
callq 0x11480
movl $0x0, -0xbb8(%rbp)
movl -0xbb8(%rbp), %eax
cmpl -0xb94(%rbp), %eax
jge 0x1780a
movq -0x18(%rbp), %rdi
movslq -0xbb8(%rbp), %rcx
leaq -0xb90(%rbp), %rax
imulq $0xc, %rcx, %rcx
addq %rcx, %rax
movq (%rax), %rcx
movq %rcx, -0xbc4(%rbp)
movl 0x8(%rax), %eax
movl %eax, -0xbbc(%rbp)
movq -0xbc4(%rbp), %rax
movq %rax, -0xbd4(%rbp)
movl -0xbbc(%rbp), %eax
movl %eax, -0xbcc(%rbp)
movq -0xbd4(%rbp), %rsi
movl -0xbcc(%rbp), %edx
callq 0xd190
testb $0x1, %al
jne 0x17600
jmp 0x1779c
movl -0xb98(%rbp), %eax
addl $0x1, %eax
movl %eax, -0xb98(%rbp)
movl -0x1c(%rbp), %eax
addl -0x650(%rbp), %eax
subl -0x64c(%rbp), %eax
movl %eax, -0xbd8(%rbp)
cmpl $0x0, -0xbd8(%rbp)
jge 0x17637
movl $0x0, -0xbd8(%rbp)
movq -0xc18(%rbp), %rdi
movq -0x18(%rbp), %rsi
movl -0xbd8(%rbp), %edx
xorl %ecx, %ecx
subl -0x24(%rbp), %ecx
xorl %r8d, %r8d
subl -0x20(%rbp), %r8d
movl -0x28(%rbp), %r9d
addl $0x1, %r9d
movq 0x18(%rbp), %rax
leaq -0x62c(%rbp), %r10
movq %r10, (%rsp)
movq %rax, 0x8(%rsp)
callq 0x16fe0
movl %eax, %ecx
xorl %eax, %eax
subl %ecx, %eax
movl %eax, -0x648(%rbp)
movq -0x18(%rbp), %rdi
movslq -0xbb8(%rbp), %rcx
leaq -0xb90(%rbp), %rax
imulq $0xc, %rcx, %rcx
addq %rcx, %rax
movq (%rax), %rcx
movq %rcx, -0xbe4(%rbp)
movl 0x8(%rax), %eax
movl %eax, -0xbdc(%rbp)
movq -0xbe4(%rbp), %rax
movq %rax, -0xbf4(%rbp)
movl -0xbdc(%rbp), %eax
movl %eax, -0xbec(%rbp)
movq -0xbf4(%rbp), %rsi
movl -0xbec(%rbp), %edx
callq 0xdd20
cmpl $0x80000000, -0x648(%rbp) # imm = 0x80000000
jne 0x176f3
movl -0x648(%rbp), %eax
movl %eax, -0x4(%rbp)
jmp 0x1793a
movl -0x648(%rbp), %eax
cmpl -0x24(%rbp), %eax
jl 0x1772a
movq -0x18(%rbp), %rdi
movslq -0xbb8(%rbp), %rax
leaq -0xb90(%rbp), %rsi
imulq $0xc, %rax, %rax
addq %rax, %rsi
movl -0x1c(%rbp), %edx
callq 0x19890
movl -0x24(%rbp), %eax
movl %eax, -0x4(%rbp)
jmp 0x1793a
movl -0x648(%rbp), %eax
cmpl -0x20(%rbp), %eax
jle 0x1779a
movl -0x648(%rbp), %eax
movl %eax, -0x20(%rbp)
movslq -0xbb8(%rbp), %rax
leaq -0xb90(%rbp), %rcx
imulq $0xc, %rax, %rax
addq %rax, %rcx
movq 0x10(%rbp), %rax
movq (%rcx), %rdx
movq %rdx, 0x4(%rax)
movl 0x8(%rcx), %ecx
movl %ecx, 0xc(%rax)
movl -0x62c(%rbp), %ecx
addl $0x1, %ecx
movq 0x10(%rbp), %rax
movl %ecx, (%rax)
movq 0x10(%rbp), %rdi
addq $0x4, %rdi
addq $0xc, %rdi
leaq -0x62c(%rbp), %rsi
addq $0x4, %rsi
movslq -0x62c(%rbp), %rax
imulq $0xc, %rax, %rdx
callq 0x5360
jmp 0x177f4
movq -0x18(%rbp), %rdi
movslq -0xbb8(%rbp), %rcx
leaq -0xb90(%rbp), %rax
imulq $0xc, %rcx, %rcx
addq %rcx, %rax
movq (%rax), %rcx
movq %rcx, -0xc00(%rbp)
movl 0x8(%rax), %eax
movl %eax, -0xbf8(%rbp)
movq -0xc00(%rbp), %rax
movq %rax, -0xc10(%rbp)
movl -0xbf8(%rbp), %eax
movl %eax, -0xc08(%rbp)
movq -0xc10(%rbp), %rsi
movl -0xc08(%rbp), %edx
callq 0xdd20
jmp 0x177f6
movl -0xbb8(%rbp), %eax
addl $0x1, %eax
movl %eax, -0xbb8(%rbp)
jmp 0x1758d
cmpl $0x0, -0xb98(%rbp)
jne 0x17852
movq -0x18(%rbp), %rdi
callq 0xbc10
testb $0x1, %al
jne 0x17822
jmp 0x1783c
movq 0x10(%rbp), %rax
movl $0x0, (%rax)
movl -0x28(%rbp), %eax
addl $0xffc2f700, %eax       # imm = 0xFFC2F700
movl %eax, -0x4(%rbp)
jmp 0x1793a
movq 0x10(%rbp), %rax
movl $0x0, (%rax)
movl $0x0, -0x4(%rbp)
jmp 0x1793a
movq -0x18(%rbp), %rdi
callq 0x13fa0
movq %rax, %rdi
callq 0x1c250
movq -0x18(%rbp), %rdi
callq 0x13fa0
movq %rax, %rcx
movq -0x660(%rbp), %rax
movq %rcx, (%rax)
movl -0x20(%rbp), %ecx
movq -0x660(%rbp), %rax
movl %ecx, 0x18(%rax)
movl -0x1c(%rbp), %ecx
movq -0x660(%rbp), %rax
movl %ecx, 0x1c(%rax)
movl -0x20(%rbp), %eax
cmpl -0x654(%rbp), %eax
jg 0x178ae
movq -0x660(%rbp), %rax
movl $0x1, 0x14(%rax)
jmp 0x17923
movl -0x20(%rbp), %eax
cmpl -0x24(%rbp), %eax
jl 0x178c6
movq -0x660(%rbp), %rax
movl $0x0, 0x14(%rax)
jmp 0x17921
movq 0x10(%rbp), %rdi
addq $0x4, %rdi
callq 0xacf0
testb $0x1, %al
jne 0x178d9
jmp 0x178db
jmp 0x178fa
leaq 0x105be(%rip), %rdi     # 0x27ea0
leaq 0x105d4(%rip), %rsi     # 0x27ebd
movl $0x191, %edx            # imm = 0x191
leaq 0x105d7(%rip), %rcx     # 0x27ecc
callq 0x5290
movq 0x10(%rbp), %rcx
movq -0x660(%rbp), %rax
movq 0x4(%rcx), %rdx
movq %rdx, 0x8(%rax)
movl 0xc(%rcx), %ecx
movl %ecx, 0x10(%rax)
movq -0x660(%rbp), %rax
movl $0x2, 0x14(%rax)
jmp 0x17923
movq -0x18(%rbp), %rdi
callq 0x13fa0
movq %rax, %rdi
callq 0x1c2a0
movl -0x20(%rbp), %eax
movl %eax, -0x4(%rbp)
movl -0x4(%rbp), %eax
addq $0xc50, %rsp            # imm = 0xC50
popq %rbp
retq
nopw %cs:(%rax,%rax)