// File: ksabry[P]bfbrute/src/ProgramIterator.h
// Function: ProgramIterator<400ul, 32ul, 12ul, 32ul>::StringDistanceRecursive(char const*, unsigned long, unsigned long, unsigned char*, unsigned long)
uint_fast32_t StringDistanceRecursive(const char* target, uint_fast32_t targetSize, uint_fast32_t shortCircuit, uint8_t* currentData, uint_fast32_t currentDataIdx)
	{
		if (targetSize == 0)
		{
			return 0;
		}

		uint_fast32_t bestScore = 100000;
		char c = target[0];

		// Compute best scores for reaching each specific data index
		uint_fast32_t dataDeltaScores[data_size];
		for (uint_fast32_t idx = 0; idx < data_size; idx++)
		{
			dataDeltaScores[idx] = abs(static_cast<int_fast32_t>(currentDataIdx - idx));
		}
		for (int_fast32_t startDelta = -8; startDelta <= 8; startDelta++)
		{
			int_fast32_t startIdx = static_cast<int_fast32_t>(currentDataIdx) + startDelta;
			if (startIdx < 0 || startIdx >= data_size)
			{
				continue;
			}
			for (int_fast32_t innerDelta = -8; innerDelta <= 8; innerDelta++)
			{
				if (innerDelta == 0)
				{
					continue;
				}
				int_fast32_t innerIdx = startIdx;
				while (currentData[innerIdx])
				{
					innerIdx += innerDelta;
					if (innerIdx < 0 || innerIdx >= data_size)
					{
						goto loop_fail;
					}
				}
				for (int_fast32_t endDelta = -8; endDelta <= 8; endDelta++)
				{
					int_fast32_t endIdx = innerIdx + endDelta;
					if (endIdx < 0 || endIdx >= data_size)
					{
						continue;
					}
					uint_fast32_t dataDeltaScore = 2 + abs(startDelta) + abs(innerDelta) + abs(endDelta);
					if (dataDeltaScore < dataDeltaScores[endIdx])
					{
						dataDeltaScores[endIdx] = dataDeltaScore;
					}
				}
			loop_fail:;
			}
		}

		// Search through reaching each specific data index and directly modifying the data to match the next character
		for (uint_fast32_t newDataIdx = 0; newDataIdx < data_size; newDataIdx++)
		{
			uint_fast32_t charScore = 1 + dataDeltaScores[newDataIdx] + abs(static_cast<int8_t>(c - currentData[newDataIdx]));

			if (charScore > shortCircuit)
			{
				continue;
			}

			char oldData = currentData[newDataIdx];
			currentData[newDataIdx] = c;
			uint_fast32_t restScore = StringDistanceRecursive(target + 1, targetSize - 1, shortCircuit - charScore, currentData, newDataIdx);
			currentData[newDataIdx] = oldData;

			if (charScore + restScore < bestScore)
			{
				bestScore = charScore + restScore;
			}

#ifdef CASE_INSENSITIVE
			if ('a' <= c && c <= 'z')
			{
				char upperC = toupper(c);
				uint_fast32_t upperCharScore = 1 + dataDeltaScores[newDataIdx] + abs(static_cast<int8_t>(upperC - currentData[newDataIdx]));

				if (upperCharScore > shortCircuit)
				{
					continue;
				}

				char oldData = currentData[newDataIdx];
				currentData[newDataIdx] = upperC;
				uint_fast32_t restScore = StringDistanceRecursive(target + 1, targetSize - 1, shortCircuit - upperCharScore, currentData, newDataIdx);
				currentData[newDataIdx] = oldData;

				if (upperCharScore + restScore < bestScore)
				{
					bestScore = upperCharScore + restScore;
				}
			}
#endif
		}

		// TODO: Search for any multi-output loops ><[><+-.><+-]
		return bestScore;
	}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0xd50, %rsp            # imm = 0xD50
movq %rdi, -0x10(%rbp)
movq %rsi, -0x18(%rbp)
movq %rdx, -0x20(%rbp)
movq %rcx, -0x28(%rbp)
movq %r8, -0x30(%rbp)
movq %r9, -0x38(%rbp)
movq -0x10(%rbp), %rax
movq %rax, -0xd38(%rbp)
cmpq $0x0, -0x20(%rbp)
jne 0x11192
movq $0x0, -0x8(%rbp)
jmp 0x114f4
movq $0x186a0, -0x40(%rbp)   # imm = 0x186A0
movq -0x18(%rbp), %rax
movb (%rax), %al
movb %al, -0x41(%rbp)
movq $0x0, -0xcd8(%rbp)
cmpq $0x190, -0xcd8(%rbp) # imm = 0x190
jae 0x111f1
movq -0x38(%rbp), %rdi
subq -0xcd8(%rbp), %rdi
callq 0x11510
movq %rax, %rcx
movq -0xcd8(%rbp), %rax
movq %rcx, -0xcd0(%rbp,%rax,8)
movq -0xcd8(%rbp), %rax
addq $0x1, %rax
movq %rax, -0xcd8(%rbp)
jmp 0x111ae
movq $-0x8, -0xce0(%rbp)
cmpq $0x8, -0xce0(%rbp)
jg 0x113ce
movq -0x38(%rbp), %rax
addq -0xce0(%rbp), %rax
movq %rax, -0xce8(%rbp)
cmpq $0x0, -0xce8(%rbp)
jl 0x11233
cmpq $0x190, -0xce8(%rbp) # imm = 0x190
jb 0x11238
jmp 0x113b7
movq $-0x8, -0xcf0(%rbp)
cmpq $0x8, -0xcf0(%rbp)
jg 0x113b5
cmpq $0x0, -0xcf0(%rbp)
jne 0x11260
jmp 0x1139e
movq -0xce8(%rbp), %rax
movq %rax, -0xcf8(%rbp)
movq -0x30(%rbp), %rax
movq -0xcf8(%rbp), %rcx
cmpb $0x0, (%rax,%rcx)
je 0x112b2
movq -0xcf0(%rbp), %rax
addq -0xcf8(%rbp), %rax
movq %rax, -0xcf8(%rbp)
cmpq $0x0, -0xcf8(%rbp)
jl 0x112ab
cmpq $0x190, -0xcf8(%rbp) # imm = 0x190
jb 0x112b0
jmp 0x1139c
jmp 0x1126e
movq $-0x8, -0xd00(%rbp)
cmpq $0x8, -0xd00(%rbp)
jg 0x1139a
movq -0xcf8(%rbp), %rax
addq -0xd00(%rbp), %rax
movq %rax, -0xd08(%rbp)
cmpq $0x0, -0xd08(%rbp)
jl 0x112f7
cmpq $0x190, -0xd08(%rbp) # imm = 0x190
jb 0x112fc
jmp 0x11383
movq -0xce0(%rbp), %rdi
callq 0x11510
addq $0x2, %rax
movq %rax, -0xd48(%rbp)
movq -0xcf0(%rbp), %rdi
callq 0x11510
movq %rax, %rcx
movq -0xd48(%rbp), %rax
addq %rcx, %rax
movq %rax, -0xd40(%rbp)
movq -0xd00(%rbp), %rdi
callq 0x11510
movq %rax, %rcx
movq -0xd40(%rbp), %rax
addq %rcx, %rax
movq %rax, -0xd10(%rbp)
movq -0xd10(%rbp), %rax
movq -0xd08(%rbp), %rcx
cmpq -0xcd0(%rbp,%rcx,8), %rax
jae 0x11381
movq -0xd10(%rbp), %rcx
movq -0xd08(%rbp), %rax
movq %rcx, -0xcd0(%rbp,%rax,8)
jmp 0x11383
movq -0xd00(%rbp), %rax
addq $0x1, %rax
movq %rax, -0xd00(%rbp)
jmp 0x112bd
jmp 0x1139c
jmp 0x1139e
movq -0xcf0(%rbp), %rax
addq $0x1, %rax
movq %rax, -0xcf0(%rbp)
jmp 0x11243
jmp 0x113b7
movq -0xce0(%rbp), %rax
addq $0x1, %rax
movq %rax, -0xce0(%rbp)
jmp 0x111fc
movq $0x0, -0xd18(%rbp)
cmpq $0x190, -0xd18(%rbp) # imm = 0x190
jae 0x114ec
movq -0xd18(%rbp), %rax
movq -0xcd0(%rbp,%rax,8), %rax
addq $0x1, %rax
movsbl -0x41(%rbp), %ecx
movq -0x30(%rbp), %rdx
movq -0xd18(%rbp), %rsi
movzbl (%rdx,%rsi), %edx
subl %edx, %ecx
movsbl %cl, %ecx
movl %ecx, %edx
negl %edx
cmovnsl %edx, %ecx
movslq %ecx, %rcx
addq %rcx, %rax
movq %rax, -0xd20(%rbp)
movq -0xd20(%rbp), %rax
cmpq -0x28(%rbp), %rax
jbe 0x1143b
jmp 0x114d5
movq -0xd38(%rbp), %rdi
movq -0x30(%rbp), %rax
movq -0xd18(%rbp), %rcx
movb (%rax,%rcx), %al
movb %al, -0xd21(%rbp)
movb -0x41(%rbp), %dl
movq -0x30(%rbp), %rax
movq -0xd18(%rbp), %rcx
movb %dl, (%rax,%rcx)
movq -0x18(%rbp), %rsi
addq $0x1, %rsi
movq -0x20(%rbp), %rdx
subq $0x1, %rdx
movq -0x28(%rbp), %rcx
subq -0xd20(%rbp), %rcx
movq -0x30(%rbp), %r8
movq -0xd18(%rbp), %r9
callq 0x11150
movq %rax, -0xd30(%rbp)
movb -0xd21(%rbp), %dl
movq -0x30(%rbp), %rax
movq -0xd18(%rbp), %rcx
movb %dl, (%rax,%rcx)
movq -0xd20(%rbp), %rax
addq -0xd30(%rbp), %rax
cmpq -0x40(%rbp), %rax
jae 0x114d3
movq -0xd20(%rbp), %rax
addq -0xd30(%rbp), %rax
movq %rax, -0x40(%rbp)
jmp 0x114d5
movq -0xd18(%rbp), %rax
addq $0x1, %rax
movq %rax, -0xd18(%rbp)
jmp 0x113d9
movq -0x40(%rbp), %rax
movq %rax, -0x8(%rbp)
movq -0x8(%rbp), %rax
addq $0xd50, %rsp            # imm = 0xD50
popq %rbp
retq
nopw %cs:(%rax,%rax)
nopl (%rax,%rax)