summaryrefslogtreecommitdiff
path: root/fpoptimizer/treerules.dat
diff options
context:
space:
mode:
Diffstat (limited to 'fpoptimizer/treerules.dat')
-rw-r--r--fpoptimizer/treerules.dat1005
1 files changed, 1005 insertions, 0 deletions
diff --git a/fpoptimizer/treerules.dat b/fpoptimizer/treerules.dat
new file mode 100644
index 0000000..cf4693f
--- /dev/null
+++ b/fpoptimizer/treerules.dat
@@ -0,0 +1,1005 @@
+# This datafile documents all the possible optimizations that the optimizer can/should do.
+# It is parsed by the parser described in fpoptimizer_grammar_gen.y, which
+# is compiled into C++ code in fpoptimizer_grammar_gen.cc. The parser produces
+# a C++ file, fpoptimizer_grammar.cc , which lists the grammar rules in tabular
+# format. The grammar rules are utilized by fpoptimizer_optimize.cc , which
+# matches the function trees into the rules and performing those replacements
+# which can be performed.
+#
+# Copyright 2010 Joel Yliluoma, written specifically
+# for Warp's Function Parser (fparser).
+#
+
+# asinh: log(x + sqrt(x*x + 1))
+# acosh: log(x + sqrt(x*x - 1))
+# atanh: log( (1+x) / (1-x)) / 2
+# asin: atan2(x, sqrt(1-x*x)) Complex: -i*log(i*x + sqrt(1 - x*x))
+# acos: atan2(sqrt(1-x*x), x) Complex: -i*log(x + i*sqrt(1 - x*x))
+# atan: Complex: -0.5i * log( (1+i*x) / (1-i*x) )
+# atan2: atan(y/x)
+# = 2*atan(y/(sqrt(x^2+y^2) + x))
+# sin: Complex: (exp(i*x) - exp(-i*x)) / (2i)
+# cos: Complex: (exp(i*x) + exp(-i*x)) / (2)
+# tan: sin/cos Complex: (i-i*exp(2i*x)) / (exp(2i*x)+1)
+# sinh: (exp(x)-exp(-x)) / 2
+# = exp(-x) * (exp(2*x)-1) / 2
+# cosh: (exp(x)+exp(-x)) / 2
+# = exp(-x) * (exp(2*x)+1) / 2
+# tanh: sinh/cosh
+# = (exp(2*x)-1) / (exp(2*x)+1)
+# log10: log/CONSTANT_L10I
+# log2: log/CONSTANT_L2I
+# sqrt: pow(x, 0.5)
+# exp: pow(CONSTANT_E, x)
+# int: floor(x + 0.5)
+
+# Substitution rule syntax:
+#
+# %token NUMERIC_CONSTANT # literals such as 0, 1, 1.5 or CONSTANT_DR, CONSTANT_L10I
+# %token NAMEDHOLDER_TOKEN # placeholders such as x, y, a, b
+# %token RESTHOLDER_TOKEN # placeholders such as <1>, <2>, <7>
+# %token IMMEDHOLDER_TOKEN # placeholders % and &
+# %token BUILTIN_FUNC_NAME # such as COS, CEIL, POW, +, *, MIN, MAX
+# %token OPCODE_TOKEN # opcodes such as cCos, cMul
+# %token UNARY_TRANSFORMATION # /, -, ! # inverts/negates/inverts the param
+# %token PARAM_CONSTRAINT # parameter constraint specifier:
+# @E = Even integer only
+# @O = Odd integer only
+# @I = Integer only
+# @F = (Float) non-integer only
+# @L = Logical operation (something that only yields 0 or 1)
+# @P = Positive only (also zero)
+# @N = Negative only
+# @Q = Only those where positive/negative not known
+# @1 = value evaluating to +1 or -1 only
+# @M = (Multiple) value NOT evaluating to +1 or -1
+# @C = Const (x@C is similar to % and &)
+# @V = Explicitly non-const
+# %token CONST_CONSTRAINT # constraint applicable to a numeric literal:
+# @R = Radians: Constant is tested after normalizing to -pi/2..pi/2 range
+# # they can be applied to IMMEDHOLDER_TOKENs and NAMEDHOLDER_TOKENs
+# %token RULE_CONSTRAINT @L = Logical context only
+# i.e. when the result of the tree will only
+# only be evaluated using fabs(value) >= 0.5.
+# @I = This rule applies for integer types only
+# @F = This rule applies for non-integer types only
+# @C = This rule applies to complex types only
+# @R = This rule applies to real (non-complex) types only
+# %token NEWLINE # newline
+#
+# %%
+# grammar:
+# grammar substitution
+# | grammar rule_constraints substitution
+# | grammar NEWLINE
+# | /* empty */
+# ;
+#
+# substitution:
+# function '->' param NEWLINE
+# /* Entire function is changed into the particular param */
+#
+# | function '->' function NEWLINE
+# /* Entire function changes, the param_notinv_list is rewritten */
+# /* NOTE: "p x -> o y" is a shortcut for "p x -> (o y)" */
+#
+# | function ':' paramlist NEWLINE
+# /* The params provided are replaced with the new param_maybeinv_list */
+# ;
+#
+# function:
+# OPCODE_TOKEN '[' paramlist ']'
+# /* Match a function with opcode=opcode,
+# * and the exact parameter list as specified
+# */
+# OPCODE_TOKEN '{' paramlist '}'
+# /* Match a function with opcode=opcode,
+# * and the exact parameter list in any order
+# */
+# | OPCODE_TOKEN paramlist
+# /* Match a function with opcode=opcode and the given way of matching params */
+# /* There may be more parameters, don't care about them */
+# ;
+#
+# paramlist: /* left-recursive list of 0-n params with no delimiter */
+# | paramlist param /* param */
+# | paramlist RESTHOLDER_TOKEN /* a placeholder for all remaining params */
+# | /* empty */
+# ;
+#
+# param:
+# NUMERIC_CONSTANT const_constraints /* particular immed */
+# | IMMEDHOLDER_TOKEN param_constraints /* a placeholder for some immed */
+# | BUILTIN_FUNC_NAME '(' paramlist ')' /* literal logarithm/sin/etc. of the provided immed-type params -- also sum/product/minimum/maximum */
+# | NAMEDHOLDER_TOKEN param_constraints /* any expression, indicated by "x", "a" etc. */
+# | (' function ')' param_constraints /* a subtree */
+# | UNARY_TRANSFORMATION param /* the negated/inverted literal value of the param */
+# ;
+#
+# param_constraints: /* List of possible constraints to the given param, eg. odd,int,etc */
+# param_constraints PARAM_CONSTRAINT
+# | /* empty */
+# ;
+#
+# rule_constraints: /* List of possible constraints to the given rule */
+# rule_constraints RULE_CONSTRAINT
+# | /* empty */
+# ;
+#
+# const_constraints: /* List of possible constraints to the given numeric value */
+# const_constraints CONST_CONSTRAINT
+# | /* empty */
+# ;
+
+[LOGICAL]
+
+# Change cLessOrEq into cLess for less rules to check
+# This way, the only comparison opcodes appearing in cIf
+# are cEqual,cNEqual,cLess.
+# cNEqual could be changed to cEqual, but we see no need.
+cIf [(cLessOrEq[x y]) z a] : (cLess[y x]) a z
+cIf [(cGreaterOrEq[x y]) z a] : (cGreater[y x]) a z
+#cIf [(cNEqual[x y]) z a] : (cEqual[y x]) a z
+
+cIf [(cLess[x y]) x y] -> cMin x y # TEST 20/cmp*_minmax
+cIf [(cLess[x y]) y x] -> cMax x y # TEST 20/cmp*_minmax
+#cIf [(cLessOrEq[x y]) x y] -> cMin x y # TEST 20/cmp*_minmax
+#cIf [(cLessOrEq[x y]) y x] -> cMax x y # TEST 20/cmp*_minmax
+cIf [(cGreater[x y]) y x] -> cMin x y # TEST 20/cmp*_minmax
+cIf [(cGreater[x y]) x y] -> cMax x y # TEST 20/cmp*_minmax
+#cIf [(cGreaterOrEq[x y]) y x] -> cMin x y # TEST 20/cmp*_minmax
+#cIf [(cGreaterOrEq[x y]) x y] -> cMax x y # TEST 20/cmp*_minmax
+
+# abs(x) = (x<0 ? -x : x)
+# abs(x) = x * (x<0 ? -1 : 1)
+# x * (x<0 ? -5 : 5) = abs(x)*5
+# x * ((x<0 ? -5 : 5) + y) = abs(x)*5 + x*y
+
+#x<0 ? -x : x POS
+#x>0 ? x : -x POS
+#x<0 ? x : -x NEG
+#x>0 ? -x : x NEG
+
+@R cMul (cIf [(cLess[x 0]) % -%]) x : (cAbs[x]) -% # TEST 20/ifabs
+@R cMul (cIf [(cGreater[x 0]) % -%]) x : (cAbs[x]) % # TEST 20/ifabs
+
+@R cMul (cAdd (cIf [(cLess[x 0]) % -%]) <1>) x : (cAdd (cMul (cAbs[x]) -%) (cMul x (cAdd <1>))) # TEST 20/ifabs
+@R cMul (cAdd (cIf [(cGreater[x 0]) % -%]) <1>) x : (cAdd (cMul (cAbs[x]) %) (cMul x (cAdd <1>))) # TEST 20/ifabs
+
+cMul % (cIf [x & z@C]) : (cIf [x *(% &) *(% z@C)]) # TEST 20/if_join_mul2, ifabs
+cAdd % (cIf [x & z@C]) : (cIf [x +(% &) +(% z@C)]) # TEST 20/if_join_add2
+
+@R cIf [(cGreater[x 0]) (cFloor[x]) (cCeil[x])] -> cTrunc x # TEST 20/trunc_from_if
+#@R cIf [(cGreaterOrEq[x 0]) (cFloor[x]) (cCeil[x])] -> cTrunc x # TEST 20/trunc_from_if
+@R cIf [(cLess[x 0]) (cCeil[x]) (cFloor[x])] -> cTrunc x # TEST 20/trunc_from_if
+#@R cIf [(cLessOrEq[x 0]) (cCeil[x]) (cFloor[x])] -> cTrunc x # TEST 20/trunc_from_if
+
+cAdd (cIf[x y z]) (cIf[x a b]) : (cIf [x (cAdd y a) (cAdd z b)]) # TEST 20/if_join_add
+cMul (cIf[x y z]) (cIf[x a b]) : (cIf [x (cMul y a) (cMul z b)]) # TEST 20/if_join_mul
+cAnd (cIf[x y z]) (cIf[x a b]) : (cIf [x (cAnd y a) (cAnd z b)]) # TEST 20/if_join_and
+cOr (cIf[x y z]) (cIf[x a b]) : (cIf [x (cOr y a) (cOr z b)]) # TEST 20/if_join_or
+@R cMin (cIf[x y z]) (cIf[x a b]) : (cIf [x (cMin y a) (cMin z b)]) # TEST 20/if_join_min
+@R cMax (cIf[x y z]) (cIf[x a b]) : (cIf [x (cMax y a) (cMax z b)]) # TEST 20/if_join_max
+
+@R cAnd (cNot[x]) (cNot[y]) : (cNot [(cOr x y)]) # TEST 20/nor2, nor2plus, nor3
+@R cOr (cNot[x]) (cNot[y]) : (cNot [(cAnd x y)]) # TEST 20/nand2, nand2plus, nand3
+
+@R cAnd (cNot[z]) (cIf[x (cNot[y]) %@L]) : (cNot [(cOr z (cIf[x y (cNot[%])]))])
+@R cOr (cNot[z]) (cIf[x (cNot[y]) %@L]) : (cNot [(cAnd z (cIf[x y (cNot[%])]))])
+
+@R cAnd (cNot[z]) (cIf[x %@L (cNot[y])]) : (cNot [(cOr z (cIf[x (cNot[%]) y]))])
+@R cOr (cNot[z]) (cIf[x %@L (cNot[y])]) : (cNot [(cAnd z (cIf[x (cNot[%]) y]))])
+
+# From logic, follows that...
+# (a==b) & (b==c) & (a==c) -- one of these is redundant
+cAnd (cEqual[x y]) (cEqual[y z]) (cEqual[x z]) : (cEqual[x y]) (cEqual[y z])
+# Note: ^ Replacement function refers to y twice
+
+# !x = abs(x) < 0.5
+# Thus, !(x*2) = abs(x) < 0.5/2
+# Note: Due to range-based optimizations, % can never be 0 here. These are safe.
+@R @F cGreater [% (cAbs[x])] -> cNot[(cMul x 0.5 /%)] # TEST 20/absnzlt
+@R @F cLessOrEq [% (cAbs[x])] -> cNotNot[(cMul x 0.5 /%)] # TEST 20/absnzge
+
+# abs(x) > 0 --> abs(x) != 0 --> x != 0
+@R cEqual [0 (cAbs[x])] : x 0 # TEST 20/abseq0
+@R cNEqual [0 (cAbs[x])] : x 0 # TEST 20/absneq0
+
+@I cEqual [0 x] -> cNot [x] # TEST 20/eq0
+@I cNEqual [0 x] -> cNotNot [x] # TEST 20/neq0
+@I cEqual [1 x@L] -> x # TEST 20/eq1
+@I cNEqual [1 x@L] -> cNot [x] # TEST 20/neq1
+@I cNot [(cAdd % <1>)] -> cEqual -% (cAdd <1>) # TEST 20/xaddnot
+@I cNotNot [(cAdd % <1>)] -> cNEqual -% (cAdd <1>) # TEST 20/xaddnotnot
+@R @I cLess [0 (cAbs[x])] -> cNotNot [x] # TEST 20/gt0_abs
+@R @I cLessOrEq [1 (cAbs[x])] -> cNotNot [x] # TEST 20/ge1_abs
+@R @I cGreater [1 (cAbs[x])] -> cNot [x] # TEST 20/gt1_abs
+@R @I cGreaterOrEq [0 (cAbs[x])] -> cNot [x] # TEST 20/ge0_abs
+
+cIf [x 1 0] -> cNotNot [x] # TEST 20/if10 (factor 1)
+cIf [x 0 1] -> cNot [x] # TEST 20/if10 (factor 10)
+cAbsIf [x 1 0] -> cAbsNotNot [x] # TEST 20/if10 (factor 100)
+cAbsIf [x 0 1] -> cAbsNot [x] # TEST 20/if10 (factor 1000)
+
+# In logical contexts:
+@R @L cMul %@N : -% # TEST 20/l_mulneg
+@R @L cMul (cAbs[x]) : x # TEST 20/l_mulabs
+@R @L cNotNot [x] -> x # TEST 20/l_notnot
+@R @L cAbs [x] -> x # TEST 20/l_abs
+
+#@R @F cAnd (cLess[% x]) (cAbsNot[x]) : (cNot (cMul /+(0.5 -%) (cAdd x *(+(% 0.5) -0.5))))
+#@R @F cAnd (cLess[% x]) (cGreater[& x]) : (cNot (cMul /+(& -%) (cAdd x *(+(% &) -0.5))))
+#@R @F cAbsAnd (cLess[% x]) (cGreater[& x]) : (cNot (cMul /+(& -%) (cAdd x *(+(% &) -0.5))))
+#@R @F cAbsAnd (cLess[% x]) (cAbsNot[x]) : (cNot (cMul /+(0.5 -%) (cAdd x *(+(% 0.5) -0.5))))
+#@R @F cOr (cGreaterOrEq[% x]) (cLessOrEq[& x]) : (cNotNot (cMul /+(& -%) (cAdd x *(+(% &) -0.5))))
+#@R @F cOr (cGreaterOrEq[% x]) (cAbsNotNot[x]) : (cNotNot (cMul /+(0.5 -%) (cAdd x *(+(% 0.5) -0.5))))
+#@R @F cAbsOr (cGreaterOrEq[% x]) (cLessOrEq[& x]) : (cNotNot (cMul /+(& -%) (cAdd x *(+(% &) -0.5))))
+#@R @F cAbsOr (cGreaterOrEq[% x]) (cAbsNotNot[x]) : (cNotNot (cMul /+(0.5 -%) (cAdd x *(+(% 0.5) -0.5))))
+# ^ these rules only work if & > %, and currently there's no way to verify it
+
+#@R cOr (cAbsNot[x]) (cAbsNot[(cMul{x -1})]) : (cNot[x])
+#@R cAbsOr (cAbsNot[x]) (cAbsNot[(cMul{x -1})]) : (cNot[x])
+#@R cOr (cAbsNotNot[x]) (cAbsNotNot[(cMul{x -1})]) : (cNotNot[x])
+#@R cAbsOr (cAbsNotNot[x]) (cAbsNotNot[(cMul{x -1})]) : (cNotNot[x])
+
+@R @F cAbsNotNot (cMul %@P <1>) -> (cGreaterOrEq[(cMul <1>) *(0.5 /%)])
+@R @F cAbsNotNot (cMul %@N <1>) -> (cLessOrEq[(cMul <1>) *(0.5 /%)])
+
+# min(x, max(x, ...)) = x
+# max(x, min(x, ...)) = x
+cMin x (cMax x <1>) : x # TEST 20/mixedminmax
+cMax x (cMin x <1>) : x # TEST 20/mixedminmax
+
+[SIMPLIFY_EQUATION]
+
+@R cLess [(cAdd % <1>) &] : (cAdd <1>) SUB(& %) # TEST 20/cmp*_add_imm
+@R cLessOrEq [(cAdd % <1>) &] : (cAdd <1>) SUB(& %) # TEST 20/cmp*_add_imm
+@R cGreater [(cAdd % <1>) &] : (cAdd <1>) SUB(& %) # TEST 20/cmp*_add_imm
+@R cGreaterOrEq [(cAdd % <1>) &] : (cAdd <1>) SUB(& %) # TEST 20/cmp*_add_imm
+@R cEqual [(cAdd % <1>) &] : (cAdd <1>) SUB(& %) # TEST 20/cmp*_add_imm
+@R cNEqual [(cAdd % <1>) &] : (cAdd <1>) SUB(& %) # TEST 20/cmp*_add_imm
+
+@R cLess [(cAdd % <1>) (cAdd & <2>)] : (cAdd <1>) (cAdd <2> & -%) # TEST 20/cmp*_add_imm
+@R cLessOrEq [(cAdd % <1>) (cAdd & <2>)] : (cAdd <1>) (cAdd <2> & -%) # TEST 20/cmp*_add_imm
+@R cGreater [(cAdd % <1>) (cAdd & <2>)] : (cAdd <1>) (cAdd <2> & -%) # TEST 20/cmp*_add_imm
+@R cGreaterOrEq [(cAdd % <1>) (cAdd & <2>)] : (cAdd <1>) (cAdd <2> & -%) # TEST 20/cmp*_add_imm
+@R cEqual [(cAdd % <1>) (cAdd & <2>)] : (cAdd <1>) (cAdd <2> & -%) # TEST 20/cmp*_add_imm
+@R cNEqual [(cAdd % <1>) (cAdd & <2>)] : (cAdd <1>) (cAdd <2> & -%) # TEST 20/cmp*_add_imm
+
+@R cLess [(cAdd x <1>) (cAdd x <2>)] : (cAdd <1>) (cAdd <2>) # TEST 20/cmp*_add_reduce
+@R cLessOrEq [(cAdd x <1>) (cAdd x <2>)] : (cAdd <1>) (cAdd <2>) # TEST 20/cmp*_add_reduce
+@R cGreater [(cAdd x <1>) (cAdd x <2>)] : (cAdd <1>) (cAdd <2>) # TEST 20/cmp*_add_reduce
+@R cGreaterOrEq [(cAdd x <1>) (cAdd x <2>)] : (cAdd <1>) (cAdd <2>) # TEST 20/cmp*_add_reduce
+@R cEqual [(cAdd x <1>) (cAdd x <2>)] : (cAdd <1>) (cAdd <2>) # TEST 20/cmp*_add_reduce
+@R cNEqual [(cAdd x <1>) (cAdd x <2>)] : (cAdd <1>) (cAdd <2>) # TEST 20/cmp*_add_reduce
+
+@R @F cLess [(cMul %@P <1>) &] : (cMul <1>) DIV(& %) # TEST 20/cmp*_mul_imm_pos
+@R @F cLessOrEq [(cMul %@P <1>) &] : (cMul <1>) DIV(& %) # TEST 20/cmp*_mul_imm_pos
+@R @F cGreater [(cMul %@P <1>) &] : (cMul <1>) DIV(& %) # TEST 20/cmp*_mul_imm_pos
+@R @F cGreaterOrEq [(cMul %@P <1>) &] : (cMul <1>) DIV(& %) # TEST 20/cmp*_mul_imm_pos
+@R @F cLess [(cMul %@N <1>) &] : DIV(& %) (cMul <1>) # TEST 20/cmp*_mul_imm_neg
+@R @F cLessOrEq [(cMul %@N <1>) &] : DIV(& %) (cMul <1>) # TEST 20/cmp*_mul_imm_neg
+@R @F cGreater [(cMul %@N <1>) &] : DIV(& %) (cMul <1>) # TEST 20/cmp*_mul_imm_neg
+@R @F cGreaterOrEq [(cMul %@N <1>) &] : DIV(& %) (cMul <1>) # TEST 20/cmp*_mul_imm_neg
+@R @F cEqual [(cMul % <1>) &] : (cMul <1>) DIV(& %) # TEST 20/cmp*_mul_imm_pos
+@R @F cNEqual [(cMul % <1>) &] : (cMul <1>) DIV(& %) # TEST 20/cmp*_mul_imm_pos
+
+@R @F cLess [(cMul %@P <1>) (cMul & <2>)] : (cMul <1>) (cMul <2> DIV(& %)) # TEST 20/cmp*_mul_imm_pos
+@R @F cLessOrEq [(cMul %@P <1>) (cMul & <2>)] : (cMul <1>) (cMul <2> DIV(& %)) # TEST 20/cmp*_mul_imm_pos
+@R @F cGreater [(cMul %@P <1>) (cMul & <2>)] : (cMul <1>) (cMul <2> DIV(& %)) # TEST 20/cmp*_mul_imm_pos
+@R @F cGreaterOrEq [(cMul %@P <1>) (cMul & <2>)] : (cMul <1>) (cMul <2> DIV(& %)) # TEST 20/cmp*_mul_imm_pos
+@R @F cLess [(cMul %@N <1>) (cMul & <2>)] : (cMul <2> DIV(& %)) (cMul <1>) # TEST 20/cmp*_mul_imm_neg
+@R @F cLessOrEq [(cMul %@N <1>) (cMul & <2>)] : (cMul <2> DIV(& %)) (cMul <1>) # TEST 20/cmp*_mul_imm_neg
+@R @F cGreater [(cMul %@N <1>) (cMul & <2>)] : (cMul <2> DIV(& %)) (cMul <1>) # TEST 20/cmp*_mul_imm_neg
+@R @F cGreaterOrEq [(cMul %@N <1>) (cMul & <2>)] : (cMul <2> DIV(& %)) (cMul <1>) # TEST 20/cmp*_mul_imm_neg
+@R @F cEqual [(cMul % <1>) (cMul & <2>)] : (cMul <1>) (cMul <2> DIV(& %)) # TEST 20/cmp*_mul_imm_pos
+@R @F cNEqual [(cMul % <1>) (cMul & <2>)] : (cMul <1>) (cMul <2> DIV(& %)) # TEST 20/cmp*_mul_imm_pos
+
+#cLess [(cMul x <1>) (cMul x <2>)] : (cMul <1>) (cMul <2>)
+#cLessOrEq [(cMul x <1>) (cMul x <2>)] : (cMul <1>) (cMul <2>)
+#cGreater [(cMul x <1>) (cMul x <2>)] : (cMul <1>) (cMul <2>)
+#cGreaterOrEq [(cMul x <1>) (cMul x <2>)] : (cMul <1>) (cMul <2>)
+#cEqual [(cMul x <1>) (cMul x <2>)] : (cMul <1>) (cMul <2>)
+#cNEqual [(cMul x <1>) (cMul x <2>)] : (cMul <1>) (cMul <2>)
+# ^ Note: This fails when x=0
+
+# Instead of x, generate (x^%)^(1/%) to further
+# delegate the possible responsibility of adding an abs() call.
+@R @F cLess [(cPow [x %@P]) &] : (cPow [(cPow [x %]) /%]) POW(& /%) # TEST 20/cmp*_pow_imm_*
+@R @F cLessOrEq [(cPow [x %@P]) &] : (cPow [(cPow [x %]) /%]) POW(& /%) # TEST 20/cmp*_pow_imm_*
+@R @F cGreater [(cPow [x %@P]) &] : (cPow [(cPow [x %]) /%]) POW(& /%) # TEST 20/cmp*_pow_imm_*
+@R @F cGreaterOrEq [(cPow [x %@P]) &] : (cPow [(cPow [x %]) /%]) POW(& /%) # TEST 20/cmp*_pow_imm_*
+@R @F cEqual [(cPow [x %@P]) &] : (cPow [(cPow [x %]) /%]) POW(& /%) # TEST 20/cmp*_pow_imm_*
+@R @F cNEqual [(cPow [x %@P]) &] : (cPow [(cPow [x %]) /%]) POW(& /%) # TEST 20/cmp*_pow_imm_*
+
+#@R @F cLess [(cPow [x %@P]) (cPow [y &@P])] : (cPow [(cPow [x %]) /MIN(% &)]) (cPow [(cPow [y &]) /MIN(% &)]) # TEST 20/cmp*_powpow_imm_pospos
+#@R @F cLessOrEq [(cPow [x %@P]) (cPow [y &@P])] : (cPow [(cPow [x %]) /MIN(% &)]) (cPow [(cPow [y &]) /MIN(% &)]) # TEST 20/cmp*_powpow_imm_pospos
+#@R @F cGreater [(cPow [x %@P]) (cPow [y &@P])] : (cPow [(cPow [x %]) /MIN(% &)]) (cPow [(cPow [y &]) /MIN(% &)]) # TEST 20/cmp*_powpow_imm_pospos
+#@R @F cGreaterOrEq [(cPow [x %@P]) (cPow [y &@P])] : (cPow [(cPow [x %]) /MIN(% &)]) (cPow [(cPow [y &]) /MIN(% &)]) # TEST 20/cmp*_powpow_imm_pospos
+#@R @F cEqual [(cPow [x %@P]) (cPow [y &@P])] : (cPow [(cPow [x %]) /MIN(% &)]) (cPow [(cPow [y &]) /MIN(% &)]) # TEST 20/cmp*_powpow_imm_pospos
+#@R @F cNEqual [(cPow [x %@P]) (cPow [y &@P])] : (cPow [(cPow [x %]) /MIN(% &)]) (cPow [(cPow [y &]) /MIN(% &)]) # TEST 20/cmp*_powpow_imm_pospos
+# ^ Note: This fails in "pow(x,2) op pow(y,3)" when x=-5, y=-6"
+# TODO: Figure out how to workaround
+
+@R @F cLess [(cPow [% x]) (cPow [% y])] : x y # TEST 20/cmp*_powpow_imm_base
+@R @F cLessOrEq [(cPow [% x]) (cPow [% y])] : x y # TEST 20/cmp*_powpow_imm_base
+@R @F cGreater [(cPow [% x]) (cPow [% y])] : x y # TEST 20/cmp*_powpow_imm_base
+@R @F cGreaterOrEq [(cPow [% x]) (cPow [% y])] : x y # TEST 20/cmp*_powpow_imm_base
+@R @F cEqual [(cPow [% x]) (cPow [% y])] : x y # TEST 20/cmp*_powpow_imm_base
+@R @F cNEqual [(cPow [% x]) (cPow [% y])] : x y # TEST 20/cmp*_powpow_imm_base
+# ^Note: This fails on exp(x)=exp(y) because of epsilon
+
+@R @F cLess [&@P (cPow [%@P x])] : DIV(LOG(&) LOG(%)) x # TEST 20/cmp*_pow_imm_pospos_base
+@R @F cLessOrEq [&@P (cPow [%@P x])] : DIV(LOG(&) LOG(%)) x # TEST 20/cmp*_pow_imm_pospos_base
+@R @F cGreater [&@P (cPow [%@P x])] : DIV(LOG(&) LOG(%)) x # TEST 20/cmp*_pow_imm_pospos_base
+@R @F cGreaterOrEq [&@P (cPow [%@P x])] : DIV(LOG(&) LOG(%)) x # TEST 20/cmp*_pow_imm_pospos_base
+@R @F cEqual [&@P (cPow [%@P x])] : DIV(LOG(&) LOG(%)) x # TEST 20/cmp*_pow_imm_pospos_base
+@R @F cNEqual [&@P (cPow [%@P x])] : DIV(LOG(&) LOG(%)) x # TEST 20/cmp*_pow_imm_pospos_base
+
+[REMOVE_REDUNDANT]
+
+@R cMul (cAbs[x]) (cAbs[y]) : (cAbs[(cMul x y)]) # TEST 20/mergemultiabs
+
+[EXTRACT1]
+
+# ceil(-x) = -floor(x); floor(-x) = -ceil(x)
+@R @F cFloor[(cMul -1 <1>)] -> cMul -1 (cCeil[(cMul <1>)]) # TEST 20/negfloor
+@R @F cCeil[(cMul -1 <1>)] -> cMul -1 (cFloor[(cMul <1>)]) # TEST 20/negceil
+
+[LOGARITHM]
+
+#### Logarithm optimizations
+# log(x^y) = y*log(x)
+#@F cLog [(cPow [x y])] -> cMul y (cLog[(cPow [(cPow [x y]) (cPow [y -1])])])
+# ^makes an infinite loop
+#@F cLog [(cPow [x@P y])] -> cMul y (cLog[x])
+#@F cLog [(cPow [x y@E])] -> cMul y (cLog [(cAbs [x])])
+# ^ Done in ConstantFolding()
+
+# CONSTANT_E^log(x) = x
+# CONSTANT_E^(log(x)*y) = x^y
+# Generalized as: p^ log(x) = x^ log(p)
+# p^(log(x)*y) = x^(log(p)*y)
+#
+# Warning: This loses the information that x > 0,
+# that could be utilized in further optimizations.
+#
+@F cPow [% (cLog[x]) ] : x LOG(%) # TEST 20/powimmlog
+@F cPow [% (cMul (cLog[x]) <1>)] : x (cMul LOG(%) <1>) # TEST 20/powimmlog
+
+# Because log(exp(6)*x) = log(x)+6, we can also do this:
+# y^(log(x)+z)
+# = y^(log(x*exp(z)))
+# = (x*exp(z))^log(y)
+# = x^log(y) * y^z
+#cPow [y (cAdd {(cLog[x]) &})] -> cMul (cPow [y &]) (cPow [x (cLog [y])])
+#
+# Probably beneficial to do it only when y is const,
+# though. Otherwise we only trade + for *, which is bad.
+# Also z should be const, otherwise we get two pows instead of one.
+@F cPow [% (cAdd {(cLog[x]) &})] -> cMul POW(% &) (cPow [x LOG(%)]) # TEST 20/powimmaddimmlog
+
+# x^(y*z) = (x*y)^z - done by ConstantFolding
+
+# z^(log(x)/log(z)*y) = x^y
+# Note: This rule fails when z=0, because log(0)=-inf and 0^x = 1
+@F cPow [z (cMul (cPow [(cLog[z]) -1]) (cLog[x]) <1>)] : x (cMul <1>)
+@F cPow [% (cMul /LOG(%) (cLog[x]) <1>)] : x (cMul <1>)
+
+# log(x) + log(y) = log(x*y)
+@F cAdd (cLog[x]) (cLog[y]) : (cLog (cMul [x y])) # TEST 20/addlog
+
+# When x is const, the reverse is more beneficial
+# i.e. log(2*x) = log(x) + log(2)
+@F cLog [(cMul %@P <1>)] -> cAdd (cLog [(cMul <1>)]) LOG(%) # TEST 20/mulimmlog
+
+# log(x * z^y) = (log(x) / log(z) + y) * log(z)
+# = log(x) + log(z)*y
+# Only worthwhile when z is an immed, otherwise we trade cPow for cLog
+# Note that when z = CONSTANT_E, this reduces rather nicely into log(x) + y
+@F cLog [(cMul (cPow [% y]) <1>)] -> cAdd (cMul [LOG(%) y]) (cLog [(cMul <1>)])
+
+#cLog [(cMul (cPow [% y]) <1>)] -> cMul LOG(%) (cAdd [y (cMul (cLog [(cMul <1>)]) /LOG(%))])
+# When y=1, the reverse is more useful:
+@F cMul {% (cAdd {1 (cMul {(cLog [x]) /%})})} -> cAdd (cLog [x]) %
+
+
+[POW_TRICKS]
+
+###### Note: Before adding new rules (especially those which handle constant values),
+###### verify that it is not already done in ConstantFolding().
+
+# (x*5) ^ 2 = x^2 * (5^2)
+# (x*5)^-1 = 0.2/x , shrug
+cPow [(cMul %@P <1>) &] -> cMul POW(% &) (cPow [(cMul <1>) &]) # TEST 20/powmulimm_*
+# ^Limited to positive values so that (-4*x)^3.3 won't be changed into nan*x^3.3
+
+cPow [(cMul %@N <1>) &@E] -> cMul POW(% &) (cPow [(cMul <1>) &]) # TEST 20/powmulimm_*
+# ^This changes (-5*x)^2 into x^2 * (-5)^2 = x^2 * 25, but only when & is an even integer.
+
+# z^(x+y/log(z)) = z^x * exp(y)
+# Note: This rule fails when z=0, because log(0)=-inf and 0^z = 1
+@F cPow [z (cAdd <1> (cMul <2> (cPow [(cLog [z]) -1])))] -> cMul (cPow [z (cAdd <1>)]) (cPow [CONSTANT_E (cMul <2>)])
+#@F cPow [% (cAdd <1> (cMul <2> /LOG(%)))] -> cMul (cPow [% (cAdd <1>)]) (cPow [CONSTANT_E (cMul <2>)])
+@F cPow [z (cAdd <1> (cPow [(cLog [z]) -1]))] -> cMul CONSTANT_E (cPow [z (cAdd <1>)])
+cPow [% (cAdd <1> &@M)] -> cMul POW(% &) (cPow [% (cAdd <1>)])
+
+# x*y / (x*z) = y/z
+cMul (cPow [(cMul x <2>) -1]) x : (cPow [(cMul <2>) -1])
+
+# (x^y)^z -> x^(y*z)
+# safe when y is odd or float, or z is an integer, or x is not negative
+cPow [ (cPow[x y@O]) z ] : x (cMul [y z])
+cPow [ (cPow[x y@F]) z ] : x (cMul [y z])
+cPow [ (cPow[x y]) z@I ] : x (cMul [y z])
+cPow [ (cPow[x@P y]) z ] : x (cMul [y z])
+
+# (x^y)^z where (y*z) makes an even integer could also be changed safely
+#cPow [(cPow [x y@I]) z@E] : x (cMul [y z])
+
+# If pow() makes a signless value into a positive value, guard that fact with abs()
+cPow [ (cPow[x@Q y])@P z ] : (cAbs [x]) (cMul [y z])
+
+# abs(x)^e -> x^e when e=even integer
+# This removes the abs() generated by the above rule when needless
+cPow [(cAbs[x]) y@E] : x y
+cPow [(cMul (cAbs[x]) <1>) y@E] : (cMul x <1>) y
+#cPow [(cMul %@N <1>) y@E] : (cMul -% <1>) y
+# ^ already done by constantfolding
+
+# x^y * (n + x^z) = n*x^y + x^(y+z)
+cMul (cPow [x y]) (cAdd {%@1 (cPow [x z])}) : (cAdd (cMul (cPow[x y]) %) (cPow[x (cAdd y z)]))
+# x^y * (n + a^z) = n*x^y + x^(y+z*log(a)/log(x))
+cMul (cPow [& y]) (cAdd { 1 (cPow [x@P z])}) : (cAdd (cPow[& y]) (cPow[& (cAdd y (cMul z (cLog[x]) /LOG(&)))]))
+cMul (cPow [& y]) (cAdd {-1 (cPow [x@P z])}) : (cAdd (cMul (cPow[& y]) -1) (cPow[& (cAdd y (cMul z (cLog[x]) /LOG(&)))]))
+# exp(-x)*(exp(2*n)+1)*4
+# becomes
+# exp(1) ^ (-x) * (exp(2) ^ n * 4 + 4)
+# so we detect that here as well.
+cMul (cPow [& y]) (cAdd {(cMul {% (cPow [x@P z])}) %}) : % (cAdd (cPow[& y]) (cPow[& (cAdd y (cMul z (cLog[x]) /LOG(&)))]))
+cMul (cPow [& y]) (cAdd {(cMul {% (cPow [x@P z])}) -%}) : % (cAdd (cMul (cPow[& y]) -1) (cPow[& (cAdd y (cMul z (cLog[x]) /LOG(&)))]))
+
+# exp(2*y) - 2*exp(y) = (exp(x)-1)^2 - 1
+# exp(2*y) - 6*exp(y) = (exp(x)-3)^2 - 9
+# exp(2*y) + 6*exp(y) = (exp(x)+3)^2 - 9
+# note: x^(2*y) = (x^2)^y (especially: exp(2*y) = exp(2)^y)
+cAdd (cMul {% (cPow[x y ])}) (cPow[x (cMul { 2 y})]) : (cPow[(cAdd (cPow[x y ]) *(% 0.5)) 2]) -POW(*(% 0.5) 2)
+cAdd (cMul {% (cPow[x (cMul {& y})])}) (cPow[x (cMul {*(& 2) y})]) : (cPow[(cAdd (cPow[x (cMul y &)]) *(% 0.5)) 2]) -POW(*(% 0.5) 2)
+cAdd (cMul {% (cPow[& y ])}) (cPow[POW(& 2) y ]) : (cPow[(cAdd (cPow[& y ]) *(% 0.5)) 2]) -POW(*(% 0.5) 2)
+
+[BINOMIAL]
+
+# Opcodes we will NOT find in the intermediate stage:
+# Done by bytecode parser:
+# Meta opcodes: cDup, cNop, cFetch, cPopNMov, cJump
+# Meta opcodes: cVar, cImmed
+# Implemented through cMul: cDiv, cRDiv, cInv, cSqr
+# Implemented through cAdd: cSub, cRSub, cNeg
+# Implemented through constant-multiplying: cDeg, cRad
+# Implemented through cSin, cCos: cCot, cCsc, cSec, cTan
+# Implemented through cPow: cSqrt, cExp
+# Implemented through cLog: cLog2, cLog10
+# Done by entry rules:
+# Extracted: cAsinh, cAcosh, cAtanh
+# Extracted: cSinh, cCosh, cTanh
+
+#### CONTINUED: Flattening the topology of add/mul/min/max/and/or groups
+
+# a^2 + a*b*X/Z + b^2 = (a+b)^2 + (X/Z-2)*(a*b)
+#cAdd (cPow[x 2]) (cPow[y 2]) (cMul x y <1>) : (cPow [(cAdd [x y]) 2]) (cMul [x y (cAdd [(cMul <1>) -2])])
+# For optimizing x^2+2*x*y+y^2:
+# With this rule, eval=0.287154 us, optimized = 0.0758879 us
+# Without this rule, eval=0.314538 us, optimized = 0.0831386 us
+# For optimizing x^2+3*x*y+y^2:
+# With this rule, eval=0.295956 us, optimized = 0.0781288 us
+# Without this rule, eval=0.300723 us, optimized = 0.075689 us
+# The benchmark results seem too varying, so it is hard to tell
+# whether this rule had some advantage. It _looks_ like it did
+# though, so better keep it, I suppose. -Bisqwit
+#
+# How about this?
+# (a+b+c)^2 = c^2 + 2*b*c + 2*a*c + b^2 + 2*a*b + a^2
+# Seems that it becomes:
+# a^2 + b^2 + c^2 + 2*((a+b)*c + a*b)
+# Is it worth adding rule for making that into (a+b+c)^2?
+# Too specific, I suppose.
+
+# These are the same as above, but work also if pow() is expanded
+# Note: It would work even with y and z instead of % and &, but we
+# limit into numeric literals for simplicity.
+cAdd (cMul (cPow[x %@P@I]) <1>) (cMul (cPow[x &@I]) <2>) : (cMul (cPow[x MIN(% &)]) (cAdd (cMul <1> (cPow[x (cAdd % -MIN(% &))])) (cMul <2> (cPow[x (cAdd & -MIN(% &))]))))
+
+# Note:
+# x^4*a + b*x^9 -> (x^5 * b + a)*x^4: Eval time goes 0.046 -> 0.056
+# x^5*a + b*x^11 -> (x^6 * b + a)*x^5: Eval time goes 0.060 -> 0.049
+# srsly, what?
+
+# x^2 + N*y - y^2 = x^2 - (y + N* 0.5)^2 + (N/2)^2
+# x^2 + N*y + y^2 = x^2 + (y + N*-0.5)^2 - (N/2)^2
+@F cAdd (cMul {-1 (cPow[x 2])}) (cMul{% x}) : (cMul {-1 (cPow[(cAdd x *(% -0.5)) 2])}) POW(*(% 0.5) 2)
+@F cAdd (cPow[x 2]) (cMul{% x}) : (cPow[(cAdd x *(% 0.5)) 2]) -POW(*(% 0.5) 2)
+
+#cAdd (cMul (cPow[x %@P@I]) <1>) (cMul x <2>) : (cMul (cPow[x MIN(% 1)]) (cAdd (cMul <1> (cPow[x (cAdd % -MIN(% 1))])) (cMul <2> (cPow[x (cAdd 1 -MIN(% 1))]))))
+
+
+# N*x^2 + M*x^2 = (x*sqrt(N) + y*sqrt(M))^2 - (2*sqrt(N*M))*x*y
+# N*x^2 + M*x*y = (x*sqrt(N) + y*0.5*M/sqrt(N))^2 - 0.25*M^2/N*y^2
+cAdd (cMul {%@P (cPow[x 2])}) (cMul {& x y}) : (cPow[(cAdd (cMul x SQRT(%)) (cMul y *(0.5 *(& /SQRT(%)))) ) 2]) (cMul (cPow[y 2]) *(*(-0.25 /%) POW(& 2)))
+
+# N*(x^2 + y^2) + M*x*y = N*(x+y)^2 + (2*N-M)*x*y
+cAdd (cMul {%@P (cAdd {(cPow[x 2]) (cPow[y 2])})}) (cMul {&@P x y}) : (cMul % (cPow[(cAdd x y ) 2])) (cMul +(& *(-2 %)) x y)
+cAdd (cMul {%@P (cAdd {(cPow[x 2]) (cPow[y 2])})}) (cMul {&@N x y}) : (cMul % (cPow[(cAdd x (cMul -1 y)) 2])) (cMul +(& *( 2 %)) x y)
+
+# The replacement expanded below:
+# (cMul (cPow[x MIN(% 1)])
+# (cAdd (cMul <1> (cPow[x (cAdd % -MIN(% 1))]))
+# (cMul <2> (cPow[x (cAdd 1 -MIN(% 1))]))))
+#
+# Example: x^2*y + x*z -> x^1 * (y*x^1 + z*x^0)
+# Example: x^6*y + x*z -> x^1 * (y*x^5 + z*x^0)
+# Example: x^-6*y + x*z -> x^-6 * (y*x^0 + z*x^7) -- not good, so restricted with @P
+#
+# Example: x*z + 2*x^0.7 -> x^0.7 * (x^0.3 * z+2) -- not good, so also restricted with @I
+#
+
+# Note: These two rules should be done in constantfolding, but it's complicated.
+# 5*x - 5*y = 5*(x-y)
+cAdd (cMul %@P <1>) (cMul -% <2>) : (cMul % (cAdd (cMul <1>) (cMul -1 <2>))) # TEST 20/addnegmulpos
+# 5 - 5*x = -5*(x-1)
+#cAdd %@M (cMul -% <2>) : (cMul -% (cAdd (cMul <2>) -1))
+cAdd %@M (cMul -% <2>) : (cMul % (cAdd 1 (cMul <2> -1))) # TEST 20/addnegmulneg
+
+# (5.1*x + 4.1*y + z+w)*2
+# -> (5.1*2*x + 2*(4.1*y + z+w))
+# -> (5.1*2*x + (4.1*2*y + 2*(z+w)))
+cMul (cAdd (cMul %@M <1>) <2>) & : (cAdd (cMul % & <1>) (cMul & (cAdd <2>))) # TEST 20/addmulconstmul
+
+# (2+x+y)*4 = 2*4 + 4*(x+y)
+cMul (cAdd %@M <1>) & : (cAdd *(% &) (cMul & (cAdd <1>))) # TEST 20/addconstmul
+
+
+[TRIGONOMETRIC]
+
+# sin(-x) = -sin(x)
+@F cSin [(cMul -1 <1>)] -> cMul -1 (cSin [(cMul <1>)]) # TEST 20/negsin
+# However, -sin(5*x) better expressed as sin(-5*x)
+@F cMul -1 (cSin [(cMul %@N <1>)]) : (cSin [(cMul -% <1>)])
+# cos(-x) = cos(x)
+@F cCos [(cMul -1 <1>)] : (cMul <1>) # TEST 20/negcos
+@R @F cCos [(cAbs [x])] : x # TEST 20/abscos
+
+# cos(pi/2 - x) = sin(x)
+@F cCos [(cAdd {CONSTANT_PIHALF@R (cMul %@N <1>)})] -> cSin[(cMul -% <1>)] # TEST 20/trig_modulo
+# sin(pi/2 - x) = cos(x)
+@F cSin [(cAdd {CONSTANT_PIHALF@R (cMul %@N <1>)})] -> cCos[(cMul -% <1>)] # TEST 20/trig_modulo
+# cos(x - pi/2) = cos(pi/2 - x) = sin(x)
+@F cCos [(cAdd -CONSTANT_PIHALF@R <1>)] -> cSin[(cAdd <1>)] # TEST 20/trig_modulo
+# sin(x - pi/2) = -sin(pi/2 - x) = -cos(x)
+@F cSin [(cAdd -CONSTANT_PIHALF@R <1>)] -> cMul -1 (cCos[(cAdd <1>)]) # TEST 20/trig_modulo
+# sin(x + pi/2) = cos(x)
+@F cSin [(cAdd CONSTANT_PIHALF@R <1>)] -> cCos[(cAdd <1>)] # TEST 20/trig_modulo
+# cos(x + pi/2) = sin(-x)
+@F cCos [(cAdd CONSTANT_PIHALF@R <1>)] -> cSin[(cMul -1 (cAdd <1>))] # TEST 20/trig_modulo
+# sin(x + pi) = -sin(x)
+@F cSin [(cAdd CONSTANT_PI@R <1>)] -> cMul -1 (cSin[(cAdd <1>)]) # TEST 20/trig_modulo
+# cos(x + pi) = -cos(x)
+@F cCos [(cAdd CONSTANT_PI@R <1>)] -> cMul -1 (cCos[(cAdd <1>)]) # TEST 20/trig_modulo
+#
+@F cCos [(cAdd 0@R <1>)] -> cCos[(cAdd <1>)]
+@F cSin [(cAdd 0@R <1>)] -> cSin[(cAdd <1>)]
+
+
+# sin(x)^2 + cos(x)^2 = 1
+@F cAdd (cPow[ (cSin[x]) 2]) (cPow [(cCos[x]) 2]) : 1 # TEST 20/addsin2cos2
+# y-sin(x)^2 = cos(x)^2+(y-1)
+# y-cos(x)^2 = sin(x)^2+(y-1)
+@F cAdd 1 (cMul { -1 (cPow[ (cSin[x]) 2]) }) : (cPow [(cCos[x]) 2]) # TEST 20/sub1sin2
+@F cAdd 1 (cMul { -1 (cPow[ (cCos[x]) 2]) }) : (cPow [(cSin[x]) 2]) # TEST 20/sub1cos2
+
+# sin(x)*cos(y) + cos(x)*sin(y) = sin(x+y)
+# sin(x)*cos(y) - cos(x)*sin(y) = sin(x-y)
+# cos(x)*cos(y) + sin(x)*sin(y) = cos(x+y)
+# cos(x)*cos(y) - sin(x)*sin(y) = cos(x-y)
+
+@F cAdd (cMul {(cSin[x]) (cCos[y])}) (cMul {(cCos[x]) (cSin[y]) }) : (cSin [(cAdd[x y] )])
+@F cAdd (cMul {(cSin[x]) (cCos[y])}) (cMul {(cCos[x]) (cSin[y]) -1}) : (cSin [(cAdd[x (cMul [-1 y])])])
+@F cAdd (cMul {(cCos[x]) (cCos[y])}) (cMul {(cSin[x]) (cSin[y]) }) : (cCos [(cAdd[x y] )])
+@F cAdd (cMul {(cCos[x]) (cCos[y])}) (cMul {(cSin[x]) (cSin[y]) -1}) : (cCos [(cAdd[x (cMul [-1 y])])])
+
+#@F cAdd (cMul {(cSin[x]) (cCos[y]) -1}) (cMul {(cCos[x]) (cSin[y]) -1}) : (cMul [-1 (cSin [(cAdd[x y] )]) ])
+#@F cAdd (cMul {(cCos[x]) (cCos[y]) -1}) (cMul {(cSin[x]) (cSin[y]) -1}) : (cMul [-1 (cCos [(cAdd[x y] )]) ])
+# ^This one is redundant, subexpression grouping already catches it
+@F cAdd (cMul {(cCos[x]) (cCos[y]) -1}) (cMul {(cSin[x]) (cSin[y]) }) : (cMul [-1 (cCos [(cAdd[x (cMul [-1 y])])]) ])
+#@F cAdd (cMul {(cSin[x]) (cCos[y]) -1}) (cMul {(cCos[x]) (cSin[y]) }) : (cMul [-1 (cSin [(cAdd[x (cMul [-1 y])])]) ])
+# ^This one is redudant: It just reaffirms that sin(x) = -sin(-x).
+
+# sin(asin(x)) = x
+@F cSin [(cAsin [x])] -> x # TEST 20/asinsin
+
+# cos(acos(x)) = x
+@F cCos [(cAcos [x])] -> x # TEST 20/acoscos
+
+# Note: asin(sin(x)) must not be converted, because
+# even though asin(sin(1.1)) = 1.1, asin(sin(1500)) != 1500.
+
+# atan(x/y) = atan2(x,y) -- do this only when we don't know whether y is zero.
+# If we know that y is nonzero, ConstantFolding
+# will revert this optimization.
+@R @F cAtan [(cMul {x (cPow [y@Q %@N])})] -> cAtan2 [x (cPow [y -%])]
+
+@R @F cAtan2 [(cMul x@P <1>) (cMul x@P <2>)] : (cMul <1>) (cMul <2>)
+@R @F cAtan2 [(cMul x@N@V <1>) (cMul x@N@V <2>)] : (cMul -1 <1>) (cMul -1 <2>)
+
+# asin(x): atan2(x, (1-x*x)^0.5)
+# asin(x): atan(x * (1-x*x)^-0.5) - automatically converted to the above.
+@R @F cAtan2 [x (cPow [(cAdd {(cMul {(cPow [x 2]) -1}) 1}) 0.5])] -> cAsin[x]
+
+# acos(x): atan2((1-x*x)^0.5, x)
+# acos(x): atan((1-x*x)^0.5 * x^-1) - automatically converted to the above.
+@R @F cAtan2 [(cPow [(cAdd {(cMul {(cPow [x 2]) -1}) 1}) 0.5]) x] -> cAcos[x]
+
+
+[REGENERATE_TAN]
+
+# sin(x)/cos(x) = tan(x)
+@F cMul (cSin[x]) (cPow [(cCos[x]) -1]) : (cTan[x])
+@F cMul (cPow [(cSin[x]) -1]) (cCos[x]) : (cPow [(cTan[x]) -1])
+# tan(x)*cos(x) = sin(x)
+@F cMul (cTan[x]) (cCos[x]) : (cSin[x])
+# sin(x)/tan(x) = cos(x)
+@F cMul (cPow [(cTan[x]) -1]) (cSin[x]) : (cCos[x])
+@F cMul (cTan[x]) (cPow [(cSin[x]) -1]) : (cPow [(cCos[x]) -1])
+
+# cos(x)^(-2) * sin(x) = tan(x)/cos(x)
+# sin(x)^2 / cos(x) = tan(x)*sin(x)
+
+# sin(-5*x) / cos(5*x) = tan(-5*x)
+@F cMul (cSin [(cMul % <1>)]) (cPow [(cCos [(cMul -% <1>)]) -1]) : (cTan [(cMul % <1>)])
+
+# tan(-x) = -tan(x)
+@F cTan [(cMul -1 <1>)] -> cMul [-1 (cTan [(cMul <1>)])]
+
+# However, -tan(5*x) better expressed as tan(-5*x)
+@F cMul -1 (cTan [(cMul %@N <1>)]) : (cTan [(cMul -% <1>)])
+
+# asin(tan(x)) = x / (1-x^2)^0.5
+#@F cAsin [(cTan [x])] -> cMul x (cPow [(cAdd (cMul (cPow [x 2]) -1) 1) -0.5])
+#
+# ^Disabled: Incorrectly produces error when x = 1
+
+# acos(tan(x)) = (1-x^2)^0.5 / x
+#@F cAcos [(cTan [x])] -> cMul (cPow [x -1]) (cPow [(cAdd (cMul (cPow [x 2]) -1) 1) 0.5])
+#
+# ^Disabled: Incorrectly produces error when x = 0
+# Incorrectly produces negative numbers when acos does no such thing
+
+# cot(pi/2 - x) = 1/tan(pi/2 - x) = tan(x)
+# tan(pi/2 - x) = 1/tan(x)
+# reverse is probably better
+# but cot() isn't exactly bad, so keep it
+#cPow [(cTan[x]) -1] -> cTan [(cAdd [CONSTANT_PIHALF@R (cMul [-1 x])])]
+
+@F cMul (cTan [(cAdd {CONSTANT_PIHALF@R (cMul {-1 x})})]) (cTan [x]) : 1
+@F cMul (cTan [(cAdd {CONSTANT_PIHALF@R (cMul -1 <1>)})]) (cTan [(cMul <1>)]) : 1
+
+# tan(atan(x)) = x
+@F cTan [(cAtan [x])] -> x
+
+@F cTan [(cAtan2 [x y])] -> cMul x (cPow [y -1])
+
+
+[REGENERATE_TANH]
+
+# sinh(x)/cosh(x) = tanh(x)
+@F cMul (cSinh[x]) (cPow [(cCosh[x]) -1]) : (cTanh[x])
+@F cMul (cPow [(cSinh[x]) -1]) (cCosh[x]) : (cPow [(cTanh[x]) -1])
+# tanh(x)*cosh(x) = sinh(x)
+@F cMul (cTanh[x]) (cCosh[x]) : (cSinh[x])
+# sinh(x)/tanh(x) = cosh(x)
+@F cMul (cPow [(cTanh[x]) -1]) (cSinh[x]) : (cCosh[x])
+@F cMul (cTanh[x]) (cPow [(cSinh[x]) -1]) : (cPow [(cCosh[x]) -1])
+
+# sinh(-5*x) / cosh(5*x) = tanh(-5*x)
+@F cMul (cSinh [(cMul {% x})]) (cPow [(cCosh [(cMul {-% x})]) -1]) : (cTanh [(cMul % x)])
+@F cMul (cSin [(cMul {% x})]) (cPow [(cCos [(cMul {-% x})]) -1]) : (cTan [(cMul % x)])
+#^ Note: Should use (cMul % <1>) instead of (cMul {% x}),
+# but cannot, due to repeated restholders
+
+# tanh(-x) = -tanh(x)
+@F cTanh [(cMul -1 <1>)] -> cMul [-1 (cTanh [(cMul <1>)])]
+
+# However, -tanh(5*x) better expressed as tanh(-5*x)
+@F cMul -1 (cTanh [(cMul %@N <1>)]) : (cTanh [(cMul -% <1>)])
+
+
+# tanh(x) = (exp(2*x)-1) / (exp(2*x)+1)
+# 1/tanh(x) = (exp(2*x)+1) / (exp(2*x)-1)
+# exp(2*x) = exp(2)^x
+# y^x = exp(log(y)*x)
+# tanh(x*log(y)/2) = (y^x-1) / (y^x+1)
+@F cMul (cAdd {-1 (cPow [% x])}) (cPow [(cAdd { 1 (cPow [% x])}) -1]) : (cTanh [(cMul x LOG(%) 0.5)])
+@F cMul (cAdd { 1 (cPow [% x])}) (cPow [(cAdd {-1 (cPow [% x])}) -1]) : (cPow [(cTanh [(cMul x LOG(%) 0.5)]) -1])
+
+
+[SINH_COSH_EXP_TRANSFORMATIONS]
+
+# sinh(-x) = -sinh(x)
+@F cSinh [(cMul -1 <1>)] -> cMul [-1 (cSinh [(cMul <1>)])] # TEST 20/negsinh
+# However, -sinh(5*x) better expressed as sinh(-5*x)
+@F cMul -1 (cSinh [(cMul %@N <1>)]) : (cSinh [(cMul -% <1>)])
+# cosh(-x) = cosh(x)
+@F cCosh [(cMul -1 <1>)] : (cMul <1>) # TEST 20/negcosh
+@R @F cCosh [(cAbs [x])] : x # TEST 20/abscosh
+
+# x - 1/x = sinh(log(x))*2. However, this alone is a pessimal conversion.
+#@F cAdd x (cMul {-1 (cPow [ x -1])}) : (cMul (cSinh [(cLog [x])]) 2)
+#@F cAdd <1> (cMul {-1 (cPow [(cAdd <1>) -1])}) : (cMul (cSinh [(cLog [(cAdd <1>)])]) 2)
+# So we add the requirement of cPow to it.
+# cLog(cPow()) reduces into optimal opcodes.
+#
+# sinh(x)*2 = (exp(x)-exp(-x)) -- note: exp(-x) = 1/exp(x)
+# cosh(x)*2 = (exp(x)+exp(-x))
+@F cAdd (cPow [& x]) (cMul { -1 (cPow [/& x]) }) : (cMul (cSinh [(cLog [(cPow [& x])])]) 2)
+@F cAdd (cPow [& x]) (cPow [/& x]) : (cMul (cCosh [(cLog [(cPow [& x])])]) 2)
+#@F cAdd (cPow [y x]) (cMul { -1 (cPow [y (cMul {x -1})]) }) : (cMul (cSinh [(cLog [(cPow [y x])])]) 2)
+#@F cAdd (cPow [y x]) (cPow [y (cMul {x -1})]) : (cMul (cCosh [(cLog [(cPow [y x])])]) 2)
+#@F cAdd (cPow [y %]) (cMul { -1 (cPow [y -%]) }) : (cMul (cSinh [(cLog [(cPow [y %])])]) 2)
+#@F cAdd (cPow [y %]) (cPow [y -%]) : (cMul (cCosh [(cLog [(cPow [y %])])]) 2)
+
+# Because sinh(-x) = -sinh(x),
+# sinh(x)*-2 = (exp(-x)-exp(x))
+@F cAdd (cMul {-1 (cPow [& x])}) (cPow [/& x]) : (cMul (cSinh [(cMul x LOG(&))]) -2)
+@F cAdd (cMul {% (cPow [& x])}) (cMul { -% (cPow [/& x])}) : (cMul (cSinh [(cMul x LOG(&))]) 2 %)
+@F cAdd (cMul {% (cPow [& x])}) (cMul { % (cPow [/& x])}) : (cMul (cCosh [(cMul x LOG(&))]) 2 %)
+
+
+# exp(x) = cosh(x)+sinh(x)
+@F cAdd (cCosh [x]) (cSinh [x]) : (cPow [CONSTANT_E x])
+# -cosh(x) = sinh(x)-exp(x)
+# cosh(x) = exp(x)-sinh(x)
+# -sinh(x) = cosh(x)-exp(x)
+# sinh(x) = exp(x)-cosh(x)
+@F cAdd (cSinh [x]) (cMul {(cPow [CONSTANT_E x]) -1}) : (cMul -1 (cCosh [x]))
+@F cAdd (cMul {(cSinh [x]) -1}) (cPow [CONSTANT_E x]) : (cCosh [x])
+@F cAdd (cCosh [x]) (cMul {(cPow [CONSTANT_E x]) -1}) : (cMul -1 (cSinh [x]))
+@F cAdd (cMul {(cCosh [x]) -1}) (cPow [CONSTANT_E x]) : (cSinh [x])
+# exp(-x) = cosh(x)-sinh(x)
+# -exp(-x) = sinh(x)-cosh(x)
+@F cAdd (cCosh [x]) (cMul {(cSinh [x]) -1}) : (cPow [CONSTANT_EI x])
+@F cAdd (cMul {(cCosh [x]) -1}) (cSinh [x]) : (cMul {(cPow [CONSTANT_EI x]) -1})
+# sinh(x) = cosh(x)-exp(-x)
+# -sinh(x) = exp(-x)-cosh(x)
+# cosh(x) = exp(-x)+sinh(x)
+@F cAdd (cCosh [x]) (cMul {(cPow [CONSTANT_EI x]) -1}) : (cSinh [x])
+@F cAdd (cMul {(cCosh [x]) -1}) (cPow [CONSTANT_EI x]) : (cMul -1 (cSinh [x]))
+@F cAdd (cSinh [x]) (cPow [CONSTANT_EI x]) : (cCosh [x])
+
+
+# sinh(x) = ((((E^2) ^ x) + -1) * ((E^-1) ^ x) * 0.5)
+# sinh(3*x) = ((((E^6) ^ x) + -1) * ((E^-3) ^ x) * 0.5)
+
+# sinh(3*x)*2 = (((E^6) ^ x) + -1) * ((E^-3) ^ x)
+# sinh(3*x)*2 / (E^-3) ^x = (((E^6) ^ x) + -1)
+# sinh(3*x)*2 * (E^ 3) ^x = (((E^6) ^ x) + -1)
+
+#@F cAdd {-1 (cPow [% x])} : (cMul (cSinh [(cMul x LOG(%) 0.5)]) 2 (cPow [% (cMul x 0.5)]))
+#@F cAdd { 1 (cPow [% x])} : (cMul (cCosh [(cMul x LOG(%) 0.5)]) 2 (cPow [% (cMul x 0.5)]))
+
+#@F cMul (cAdd {-1 (cPow [% x])}) (cPow [POW(% -0.5) x]) : (cSinh [(cMul x LOG(%) 0.5)]) 2
+#@F cMul (cPow [% x]) (cAdd {-1 (cPow [POW(% -2) x])}) : (cSinh [(cMul x LOG(%) 0.5)]) 2
+
+# tanh(x) / cosh(x)
+# = sinh(x) / cosh(x)^2
+@F cMul (cSinh[x]) (cPow[(cCosh[x]) %]) : (cTanh[x]) (cPow[(cCosh[x]) +(% 1)])
+
+# sinh(log(x)) = 0.5*(x - 1/x) (valid only for x>0)
+#@F cSinh [(cLog [x])] -> cMul 0.5 (cAdd x (cMul -1 (cPow [x -1])))
+
+
+[ASINH_ACOSH_ATANH_TRANSFORMATIONS]
+
+# sinh(acosh(x)) = sqrt(x^2 - 1) (not a typo)
+# cosh(asinh(x)) = sqrt(x^2 + 1) (not a typo)
+# Not sure whether these are faster. They are more opcodes, but
+# simpler. The rationale is in allowing for further optimizations.
+@F cSinh [(cAcosh [x])] -> cPow [(cAdd [(cPow [x 2]) -1]) 0.5] # TEST 20/acoshsinh
+@F cCosh [(cAsinh [x])] -> cPow [(cAdd [(cPow [x 2]) 1]) 0.5] # TEST 20/asinhcosh
+
+# asinh: log(x + sqrt(x*x + 1))
+# exp(asinh): x + sqrt(x*x + 1)#
+# Disabled: On x86_64, "asinh(x)" is slower than "log(sqrt(x^2+1)+x)"
+@F cAdd (cPow [(cAdd {1 (cPow [ x 2])}) 0.5]) x : (cPow [CONSTANT_E (cAsinh [x])])
+@F cAdd (cPow [(cAdd {1 (cPow [(cAdd <1>) 2])}) 0.5]) <1> -> (cPow [CONSTANT_E (cAsinh [(cAdd <1>)])])
+@F cAdd (cPow [(cAdd {1 (cPow [ x 2])}) -0.5]) x : (cPow [CONSTANT_EI (cAsinh [x])])
+@F cAdd (cPow [(cAdd {1 (cPow [(cAdd <1>) 2])}) -0.5]) <1> -> (cPow [CONSTANT_EI (cAsinh [(cAdd <1>)])])
+
+
+# acosh: log(x + sqrt(x*x - 1))
+# exp(acosh): x + sqrt(x*x - 1)
+# Disabled: On x86_64, "acosh(x)" is slower than "log(sqrt(x^2-1)+x)"
+@F cLog [(cAdd {(cPow [(cAdd {-1 (cPow [x 2])}) 0.5]) x})] -> cAcosh [x]
+#cAdd {(cPow [(cAdd {(cPow [x 2]) -1}) 0.5]) x} -> cPow [CONSTANT_E (cAcosh [x])]
+
+# atanh(x): log( (1+x) / (1-x)) / 2
+# 2*atanh(x): log( (1+x) / (1-x))
+@F cLog [(cMul {(cAdd {1 x}) (cPow [(cAdd {1 (cMul {-1 x})}) -1])})] -> cMul [(cAtanh [x]) 2]
+
+# atanh(y*x): log( (1+y*x) / (1+(-1*y*x))) / 2
+# 2*atanh(y*x): log( (1+y*x) / (1+(-1*y*x)))
+
+# atanh(5*x): log( (1+5*x) / (1+(-5*x))) / 2
+# 2*atanh(5*x): log( (1+5*x) / (1+(-5*x)))
+
+# atanh(y+x): log( (1+y+x) / (1+(-1*(y+x)))) / 2
+# 2*atanh(y+x): log( (1+y+x) / (1+(-1*(y+x))))
+
+#@F cLog [(cMul {(cAdd {1 (cMul {% x})}) (cPow [(cAdd {1 (cMul {-% x})}) -1])})] -> cMul [(cAtanh [(cMul % x)]) 2]
+
+# atanh(x) = log2( ((x*-2)+1) / ((x*2)-1) ) * log(2)/2
+# atanh(x)*2/log(2) = log2( ((x*-2)+1) / ((x*2)-1) )
+# y^(atanh(x)*2/log(y)) = ((x*-y)+1) / ((x*y)-1)
+
+#@F cMul (cAdd {1 (cMul {x %})}) (cPow [(cAdd {-1 (cMul {x -%})}) -1]) : (cPow [% (cMul (cAtanh[x]) 2 /LOG(%))])
+#@F cMul (cPow [(cAdd {(cMul {x %}) &}) -1]) (cAdd {(cMul {x -%}) 2 -&}) : (cPow [-% (cMul (cAtanh[(cAdd (cMul -% x) & -1)]) 2 /LOG(-%))])
+
+
+
+[REGENERATE_HIGHLEVEL_OPCODES]
+
+# x * CONSTANT_RD = cDeg(x)
+@F cMul CONSTANT_RD <1> -> cDeg [(cMul <1>)]
+
+# x * CONSTANT_DR = cRad(x)
+@F cMul CONSTANT_DR <1> -> cRad [(cMul <1>)]
+
+@F cFloor [(cAdd 0.5 <1>)] -> cInt [(cAdd <1>)]
+
+# log(x) / CONSTANT_L10 = log10(x)
+@F cMul (cLog [x]) CONSTANT_L10I : (cLog10 [x])
+@F cMul (cPow [(cLog [x]) -1]) CONSTANT_L10 : (cPow [(cLog10 [x]) -1])
+
+# log(x) / CONSTANT_L2 = log2(x)
+@F cMul (cLog [x]) CONSTANT_L2I : (cLog2 [x])
+@F cMul (cPow [(cLog [x]) -1]) CONSTANT_L2 : (cPow [(cLog2 [x]) -1])
+
+#@F cPow [ (cSin[x]) %@N ] : (cCsc[x]) -%
+#@F cPow [ (cCos[x]) %@N ] : (cSec[x]) -%
+#@F cPow [ (cTan[x]) %@N ] : (cCot[x]) -%
+# ^ DONE BY MAKEBYTECODE
+
+@F cPow [ (cAdd [( cPow [x %@E] ) ( cPow [y &@E] )] ) 0.5 ] -> (cHypot [(cPow [ x *(% 0.5)]) (cPow [ y *(& 0.5)])])
+@F cPow [ (cAdd {( cPow [x %@E] ) (cMul {z@C (cPow [y &@E])})} ) 0.5 ] -> (cHypot [(cPow [ x *(% 0.5)]) (cPow [(cMul y POW(z@C /&)) *(& 0.5)])])
+@F cPow [ (cAdd {(cMul {a@C (cPow [x %@E])}) (cMul {z@C (cPow [y &@E])})} ) 0.5 ] -> (cHypot [(cPow [(cMul x POW(a@C /%)) *(% 0.5)]) (cPow [(cMul y POW(z@C /&)) *(& 0.5)])])
+
+@F cMul (cExp[x]) (cExp[y]) : (cExp[(cAdd x y)]) # TEST 20/expexp_a 20/expexp_b 20/expexp_c
+@F cMul (cExp2[x]) (cExp2[y]) : (cExp2[(cAdd x y)]) # TEST 20/expexp_a 20/expexp_b 20/expexp_c
+
+[ABS_LOGICAL]
+
+@R cNot [x@P] -> cAbsNot [x] # TEST 20/posnot
+@R cNotNot [x@P] -> cAbsNotNot [x] # TEST 20/posnotnot
+@R cAnd x@P y@P : (cAbsAnd x y)
+@R cOr x@P y@P : (cAbsOr x y)
+@R cIf [x@P y z] -> cAbsIf x y z
+
+#@R @L cAbsNotNot [x] -> x
+#@R @L cIf [x (cAbsNotNot[y]) z] : x y z
+#@R @L cIf [x y (cAbsNotNot[z])] : x y z
+# ^ These are mistakes; (x>=0.5 & y) is not same as (x & y)
+
+@R cAbsIf [(cLessOrEq[x y]) z a] : (cLess[y x]) a z
+@R cAbsIf [(cNotNot[x]) y z] -> cIf x y z
+
+@R @F cLess [x 0.5] -> cAbsNot[x] # TEST 20/lthalf
+@R @F cGreaterOrEq [x 0.5] -> cAbsNotNot[x] # TEST 20/gehalf
+
+[NON_SHORTCUT_LOGICAL_EVALUATION]
+
+@R cOr x@L y@L : (cNotNot (cAdd x y))
+@R cOr x@L (cAdd <1>)@P : (cNotNot (cAdd x <1>))
+@R cAnd x@L <1> -> (cNotNot (cMul x (cAnd <1>)))
+# ^Conflicts with "cMul x@L y@L"
+
+[SHORTCUT_LOGICAL_EVALUATION]
+
+@R cMul x@L y@L : (cAnd x y) # TEST 20/muland2, muland2plus, muland3, mulnor2, mulnor2plus, mulnor3, mulandlt
+# ^Conflicts with "cAnd x@L <1>"
+
+# @L cAdd x@L@M <1> -> cOr x (cAdd <1>)
+# cMul x@L <1> -> cAnd x (cMul <1>)
+
+#cAdd x@L@M <1> -> cAbsIf [x (cAdd <1> 1) (cAdd <1>)]
+@R cMul x@L <1> -> cAbsIf [x (cMul <1>) 0]
+
+cAnd x <1> -> cIf [x (cNotNot[(cAnd <1>)]) 0]
+cOr x <1> -> cIf [x 1 (cNotNot[(cOr <1>)]) ]
+cAbsAnd x <1> -> cAbsIf [x (cNotNot[(cAbsAnd <1>)]) 0]
+cAbsOr x <1> -> cAbsIf [x 1 (cNotNot[(cAbsOr <1>)]) ]
+
+[IGNORE_IF_SIDEEFFECTS]
+
+# These rules are the polar opposite of what
+# is done in SHORTCUT_LOGICAL_EVALUATION.
+# Do not include them in the same optimization set.
+
+cIf [x y@L 0] -> cAnd x y
+cIf [x 0 y@L] -> cAnd (cNot[x]) y
+cIf [x y 0] -> cMul (cNotNot[x]) y
+cIf [x 0 y] -> cMul (cNot[x]) y
+
+cIf [x 1 y@L] -> cOr x y
+cIf [x y@L 1] -> cOr (cNot[x]) y
+cIf [x y 0] -> cMul (cNotNot[x]) y
+cIf [x 0 y] -> cMul (cNot[x]) y
+
+# These cannot be done because y may have side
+# effects or just be computation-heavy.
+
+[BASE2_EXPAND_COMPONENTS]
+
+#@F cLog [x] -> cMul (cLog2[x]) CONSTANT_L2
+#@F cLog10 [x] -> cMul (cLog2[x]) CONSTANT_L10B
+#@F cExp [x] -> cExp2 [(cMul CONSTANT_L2I x)]
+#@F cMul (cLog2by [x y]) <1> -> cLog2by [x (cMul y <1>)]
+@F cAsin [x] -> cAtan2 x (cSqrt (cSub 1 (cSqr x)))
+@F cAcos [x] -> cAtan2 (cSqrt (cSub 1 (cSqr x))) x
+@F cSinh [x] -> cMul 0.5 (cSub (cExp[x]) (cInv (cExp[x])))
+@F cCosh [x] -> cMul 0.5 (cSub (cExp[x]) (cInv (cExp[x])))
+@F cTanh [x] -> cDiv (cAdd (cExp [(cMul x 2)]) -1) (cAdd (cExp [(cMul x 2)]) 1)
+@F cAtanh [x] -> cMul 0.5 (cLog (cDiv (cAdd 1 x) (cSub 1 x)))
+@F cAsinh [x] -> cLog (cAdd x (cSqrt (cAdd 1 (cSqr x))))
+@F cAcosh [x] -> cLog (cAdd x (cSqrt (cAdd -1 (cSqr x))))
+
+#@F cLog2 [x] -> cLog2by x 1
+#@F cMul (cPow[(cLog2[x]) %]) & : (cPow[(cLog2by[x POW(& /%)]) %])
+@F cMul (cPow[(cLog2by[x 1]) %]) & : (cPow[(cLog2by[x POW(& /%)]) %])
+
+##### Now construct the rounds of optimization:
+
+$optimize_round1:
+LOGICAL
+REMOVE_REDUNDANT
+LOGARITHM
+POW_TRICKS
+BINOMIAL
+TRIGONOMETRIC
+EXTRACT1
+
+$optimize_round2:
+LOGICAL
+REMOVE_REDUNDANT
+POW_TRICKS
+SINH_COSH_EXP_TRANSFORMATIONS
+ASINH_ACOSH_ATANH_TRANSFORMATIONS
+
+$optimize_round3:
+SIMPLIFY_EQUATION
+REGENERATE_TAN
+REGENERATE_TANH
+
+$optimize_round4:
+REGENERATE_HIGHLEVEL_OPCODES
+
+$optimize_recreate:
+REGENERATE_HIGHLEVEL_OPCODES
+BINOMIAL
+
+$optimize_ignore_if_sideeffects
+IGNORE_IF_SIDEEFFECTS
+LOGICAL
+
+$optimize_shortcut_logical_evaluation
+#SHORTCUT_LOGICAL_EVALUATION
+LOGICAL
+
+$optimize_nonshortcut_logical_evaluation
+NON_SHORTCUT_LOGICAL_EVALUATION
+LOGICAL
+
+$optimize_abslogical
+ABS_LOGICAL
+
+#$optimize_base2_expand
+#BASE2_EXPAND_COMPONENTS
+#BINOMIAL
+#POW_TRICKS