Quantcast

Split the generic "operator" node into "arithmetic" and "compare" nodes.

Johnny C. Lam [10-15-13 - 01:24]
Split the generic "operator" node into "arithmetic" and "compare" nodes.

"Arithmetic" and "compare" nodes return different things when computed, so
split the computation into separate functions.

Modify the arithmetic computations so that the resulting values have an
origin of atTime, since all computations will ultimately be carried out at
t = atTime.

git-svn-id: svn://svn.curseforge.net/wow/ovale/mainline/trunk@1044 d5049fe3-3747-40f7-a4b5-f36d6801af5f
Filename
OvaleBestAction.lua
OvaleCompile.lua
diff --git a/OvaleBestAction.lua b/OvaleBestAction.lua
index f250fea..5bfdec3 100644
--- a/OvaleBestAction.lua
+++ b/OvaleBestAction.lua
@@ -143,6 +143,179 @@ local function ComputeAnd(element, atTime)
 	return startB, endB, prioriteB, elementB
 end

+local function ComputeArithmetic(element, atTime)
+	local self = OvaleBestAction
+	local startA, endA, _, elementA = self:Compute(element.a, atTime)
+	local startB, endB, _, elementB = self:Compute(element.b, atTime)
+
+	-- Take intersection of (startA, endA) and (startB, endB)
+	if isBefore(startA, startB) then
+		startA = startB
+	end
+	if isAfter(endA, endB) then
+		endA = endB
+	end
+	if not isBefore(startA, endA) then
+		return nil
+	end
+
+	--[[
+		A(t) = a + (t - b)*c
+		B(t) = x + (t - y)*z
+
+		Silently "typecast" non-values to a constant value of 0.
+	--]]
+	local a = elementA and elementA.value or 0
+	local b = elementA and elementA.origin or 0
+	local c = elementA and elementA.rate or 0
+	local x = elementB and elementB.value or 0
+	local y = elementB and elementB.origin or 0
+	local z = elementB and elementB.rate or 0
+
+	Ovale:Logf("%f+(t-%f)*%f %s %f+(t-%f)*%f [%d]", a, b, c, element.operator, x, y, z, element.nodeId)
+
+	-- result(t) = l + (t - m) * n
+	local l, m, n
+
+	--[[
+		A(t) = a + (t - b)*c = a + (t - t0 + t0 - b)*c = [a + (t0 - b)*c] + (t - t0)*c = A(t0) + (t - t0)*c
+		B(t) = x + (t - y)*z = x + (t - t0 + t0 - y)*z = [x + (t0 - y)*z] + (t - t0)*z = B(t0) + (t - t0)*z
+	--]]
+	local A = a + (atTime - b)*c
+	local B = x + (atTime - y)*z
+
+	if element.operator == "+" then
+		--[[
+			A(t) = A(t0) + (t - t0)*c
+			B(t) = B(t0) + (t - t0)*z
+
+			A(t) + B(t) = [A(t0) + B(t0)] + (t - t0)*(c + z)
+		--]]
+		l = A + B
+		m = atTime
+		n = c + z
+	elseif element.operator == "-" then
+		--[[
+			A(t) = A(t0) + (t - t0)*c
+			B(t) = B(t0) + (t - t0)*z
+
+			A(t) - B(t) = [A(t0) - B(t0)] + (t - t0)*(c - z)
+		--]]
+		l = A - B
+		m = atTime
+		n = c - z
+	elseif element.operator == "*" then
+		--[[
+			A(t) = A(t0) + (t - t0)*c
+			B(t) = B(t0) + (t - t0)*z
+		--]]
+		if c == 0 then
+			l = A * B
+			m = atTime
+			n = A * z
+		elseif z == 0 then
+			l = A * B
+			m = atTime
+			n = c * B
+		else
+			Ovale:Error("at least one value must be constant when multiplying")
+			return nil
+		end
+	elseif element.operator == "/" then
+		--[[
+			      A(t) = A(t0) + (t - t0)*c
+			A(t)/B(t0) = [A(t0)/B(t0)] + (t - t0)*[c / B(t0)]
+
+			Divide by B(t0) instead of B(t) to allow constructs like {target.Health() / target.TimeToDie()}.
+		--]]
+		l = A / B
+		m = atTime
+		n = c / B
+	elseif element.operator == "%" then
+		if c == 0 and z == 0 then
+			l = A % B
+			m = atTime
+			n = 0
+		else
+			Ovale:Error("Parameters of % must be constants")
+			return nil
+		end
+	end
+	Ovale:Logf("result = %f+(t-%f)*%f [%d]", l, m, n, element.nodeId)
+	return startA, endA, OVALE_DEFAULT_PRIORITY, PutValue(element, l, m, n)
+end
+
+local function ComputeCompare(element, atTime)
+	local self = OvaleBestAction
+	local startA, endA, _, elementA = self:Compute(element.a, atTime)
+	local startB, endB, _, elementB = self:Compute(element.b, atTime)
+
+	-- Take intersection of (startA, endA) and (startB, endB)
+	if isBefore(startA, startB) then
+		startA = startB
+	end
+	if isAfter(endA, endB) then
+		endA = endB
+	end
+	if not isBefore(startA, endA) then
+		return nil
+	end
+
+	--[[
+		A(t) = a + (t - b)*c
+		B(t) = x + (t - y)*z
+
+		Silently "typecast" non-values to a constant value of 0.
+	--]]
+	local a = elementA and elementA.value or 0
+	local b = elementA and elementA.origin or 0
+	local c = elementA and elementA.rate or 0
+	local x = elementB and elementB.value or 0
+	local y = elementB and elementB.origin or 0
+	local z = elementB and elementB.rate or 0
+
+	Ovale:Logf("%f+(t-%f)*%f %s %f+(t-%f)*%f [%d]", a, b, c, element.operator, x, y, z, element.nodeId)
+
+	--[[
+		         A(t) = B(t)
+		a + (t - b)*c = x + (t - y)*z
+		a + t*c - b*c = x + t*z - y*z
+		    t*c - t*z = (x - y*z) - (a - b*c)
+		    t*(c - z) = B(0) - A(0)
+	--]]
+	local A = a - b*c
+	local B = x - y*z
+	if c == z then
+		if (element.operator == "==" and A == B)
+				or (element.operator == "<" and A < B)
+				or (element.operator == "<=" and A <= B)
+				or (element.operator == ">" and A > B)
+				or (element.operator == ">=" and A >= B) then
+			return startA, endA
+		end
+	else
+		local t = (B - A)/(c - z)
+		if (c > z and element.operator == "<")
+				or (c > z and element.operator == "<=")
+				or (c < z and element.operator == ">")
+				or (c < z and element.operator == ">=") then
+			-- (startA, endA) intersect (-inf, t)
+			endA = minTime(endA, t)
+		end
+		if (c < z and element.operator == "<")
+				or (c < z and element.operator == "<=")
+				or (c > z and element.operator == ">")
+				or (c > z and element.operator == ">=") then
+			-- (startA, endA) intersect (t, inf)
+			startA = maxTime(startA, t)
+		end
+		if isBefore(startA, endA) then
+			return startA, endA
+		end
+	end
+	return nil
+end
+
 local function ComputeCustomFunction(element, atTime)
 	Ovale:Logf("custom function %s", element.name)
 	local self = OvaleBestAction
@@ -396,191 +569,6 @@ local function ComputeOr(element, atTime)
 	return startB, endB
 end

-local function ComputeOperator(element, atTime)
-	local self = OvaleBestAction
-	local startA, endA, prioA, elementA = self:Compute(element.a, atTime)
-	local startB, endB, prioB, elementB = self:Compute(element.b, atTime)
-	if not elementA or not elementB then
-		Ovale:Logf("operator %s: elementA or elementB is nil", element.operator)
-		return nil
-	end
-
-	-- A(t) = a + (t - b) * c
-	-- B(t) = x + (t - y) * z
-	local a, b, c
-	local x, y, z
-
-	if elementA then
-		a = elementA.value
-		b = elementA.origin
-		c = elementA.rate
-	else
-		-- A boolean used in a number context has the value 1
-		a = 1
-		b = 0
-		c = 0
-	end
-	if elementB then
-		x = elementB.value
-		y = elementB.origin
-		z = elementB.rate
-	else
-		x = 1
-		y = 0
-		z = 0
-	end
-
-	if startA == endA then
-		startA, endA = 0, nil
-		a, b, c = 0, 0, 0
-	end
-	if startB == endB then
-		startB, endB = 0, nil
-		x, y, z = 0, 0, 0
-	end
-
-	if isBefore(startA, startB) then
-		startA = startB
-	end
-	if isAfter(endA, endB) then
-		endA = endB
-	end
-
-	if not a or not x or not b or not y then
-		Ovale:Logf("operator %s: a or x is nil", element.operator)
-		return nil
-	end
-
-	Ovale:Logf("%f+(t-%f)*%f %s %f+(t-%f)*%f", a, b, c, element.operator, x, y, z)
-
-	-- result(t) = l + (t - m) * n
-	local l, m, n
-
-	if element.operator == "*" then
-		if c == 0 then
-			l = a * x
-			m = y
-			n = a * z
-		elseif z == 0 then
-			l = x * a
-			m = b
-			n = x * c
-		else
-			Ovale:Error("at least one value must be constant when multiplying")
-			return nil
-		end
-	elseif element.operator == "+" then
-		if c + z == 0 then
-			l = (a + x) - (b - y) * c
-			m = 0
-			n = 0
-		else
-			l = a + x
-			m = (b * c + y * z) / (c + z)
-			n = c + z
-		end
-	elseif element.operator == "-" then
-		if c - z == 0 then
-			l = (a - x) - (b - y) * c
-			m = 0
-			n = 0
-		else
-			l = a - x
-			m = (b * c - y * z) / (c - z)
-			n = c - z
-		end
-	elseif element.operator == "/" then
-		if z ~= 0 then
-			-- To allow constructs like {target.Health() / target.DeadIn()}
-			x = x + (atTime - y) * z
-		end
-		l = a / x
-		m = b
-		n = c / x
-	elseif element.operator == "%" then
-		if c == 0 and z == 0 then
-			l = c % z
-			m = 0
-			n = 0
-		else
-			Ovale:Error("Parameters of % must be constants")
-			return nil
-		end
-	else
-		-- Comparisons
-		-- a + (t-b)*c = x + (t-y)*z
-		-- (t-b)*c - (t-y)*z = x-a
-		-- t*c - b*c - t*z + y*z = x-a
-		-- t*(c-z) = x-a + b*c - y*z
-		-- t = (x-a + b*c - y*z)/(c-z)
-		local A, B, t
-		if c == z then
-			A = a - b * c
-			B = x - y * z
-		else
-			t = (x - a + b * c - y * z) / (c - z)
-		end
-		if element.operator == "<" then
-			if c == z then
-				if A < B then
-					return startA, endA
-				else
-					return nil
-				end
-			elseif c > z then
-				return startA, minTime(endA, t)
-			else
-				return maxTime(startA, t), endA
-			end
-		elseif element.operator == "<=" then
-			if c == z then
-				if A <= B then
-					return startA, endA
-				else
-					return nil
-				end
-			elseif c > z then
-				return startA, minTime(endA, t)
-			else
-				return maxTime(startA, t), endA
-			end
-		elseif element.operator == ">" then
-			if c == z then
-				if A > B then
-					return startA, endA
-				else
-					return nil
-				end
-			elseif c < z then
-				return startA, minTime(endA, t)
-			else
-				return maxTime(startA, t), endA
-			end
-		elseif element.operator == ">=" then
-			if c == z then
-				if A >= B then
-					return startA, endA
-				else
-					return nil
-				end
-			elseif c < z then return
-				startA, minTime(endA, t)
-			else
-				return maxTime(startA, t), endA
-			end
-		elseif element.operator == "==" then
-			if c == z and A == B then
-				return startA, endA
-			else
-				return nil
-			end
-		end
-	end
-
-	Ovale:Logf("result = %f+(t-%f)*%f", l, m, n)
-	return startA, endA, OVALE_DEFAULT_PRIORITY, PutValue(element, l, m, n)
-end
-
 local function ComputeUnless(element, atTime)
 	Ovale:Logf("%s [%d]", element.type, element.nodeId)
 	local self = OvaleBestAction
@@ -633,13 +621,14 @@ end
 local OVALE_COMPUTE_VISITOR =
 {
 	["and"] = ComputeAnd,
+	["arithmetic"] = ComputeArithmetic,
+	["compare"] = ComputeCompare,
 	["customfunction"] = ComputeCustomFunction,
 	["function"] = ComputeFunction,
 	["group"] = ComputeGroup,
 	["if"] = ComputeAnd,
 	["lua"] = ComputeLua,
 	["not"] = ComputeNot,
-	["operator"] = ComputeOperator,
 	["or"] = ComputeOr,
 	["unless"] = ComputeUnless,
 	["value"] = ComputeValue,
diff --git a/OvaleCompile.lua b/OvaleCompile.lua
index a67aeef..ba48388 100644
--- a/OvaleCompile.lua
+++ b/OvaleCompile.lua
@@ -432,13 +432,29 @@ local function ParseOr(a,b)
 	return AddNode(node)
 end

-local function ParseOp(a, op, b)
-	local node = self_pool:Get()
-	node.type = "operator"
-	node.operator = op
-	node.a = self_node[tonumber(a)]
-	node.b = self_node[tonumber(b)]
-	return AddNode(node)
+local ParseOp
+do
+	local operator = {
+		["+"] = "arithmetic",
+		["-"] = "arithmetic",
+		["*"] = "arithmetic",
+		["/"] = "arithmetic",
+		["%"] = "arithmetic",
+		["<"] = "compare",
+		["<="] = "compare",
+		["=="] = "compare",
+		[">="] = "compare",
+		[">"] = "compare",
+	}
+
+	function ParseOp(a, op, b)
+		local node = self_pool:Get()
+		node.type = operator[op]
+		node.operator = op
+		node.a = self_node[tonumber(a)]
+		node.b = self_node[tonumber(b)]
+		return AddNode(node)
+	end
 end

 local function ParseGroup(text)