--[[
NOT A B R(A) := not R(B)
	Applies a boolean NOT to the value in R(B) and places the result in R(A).
	R(A) and R(B) are always registers.

EQ A B C if ((RK(B) == RK(C)) ~= A) then PC++
LT A B C if ((RK(B) <  RK(C)) ~= A) then PC++
LE A B C if ((RK(B) <= RK(C)) ~= A) then PC++
	Compares RK(B) and RK(C), which may be registers or constants. If the
	boolean result is not A, then skip the next instruction. Conversely, if the
	boolean result equals A, continue with the next instruction.
	EQ is for equality. LT is for "less than" comparison. LE is for "less than or
	equal to" comparison. The boolean A field allows the full set of relational
	comparison operations to be synthesized from these three instructions.
	The Lua code generator produces either 0 or 1 for the boolean A.
]]

local trace = function(...) end -- print

local function isNonBooleanConstant( operand, constants )
	return operand > 255 and type( constants[ operand - 255 ] ) ~= "boolean"
end

local function isBadNotEqualsPair( previous, current, constants )
	if current.opcode == "EQ" and previous.opcode == "NOT" then
		local notResult = previous.A
		if current.B == notResult then
			return isNonBooleanConstant( current.C, constants )
		elseif current.C == notResult then
			return isNonBooleanConstant( current.B, constants )
		end
	end
end

local function findNotEqualsBugs( crackInstructions, f, results )
	results = results or { }

	if f.code and f.lines then
		local previous
		local constants = f.constants.scalars
		for i, current in ipairs( crackInstructions( f.code ) ) do
			local line = f.lines[ i ]
			trace( current.opcode, unpack( current ) )
			if previous and isBadNotEqualsPair( previous, current, constants ) then
				table.insert( results, { kind = "notequals", line = line } )
			end
			previous = current
		end
	end

	for _, sf in ipairs( f.constants.functions ) do
		findNotEqualsBugs( crackInstructions, sf, results )
	end
	
	return results
end

return findNotEqualsBugs
