
=== 1. matching and captures ===
ADD(x, x) against a+a                                      match     {'x': 'param#0'}
ADD(x, x) against a+b                                      no match  
ADD(x, cvar c) against a+5                                 match     {'x': 'param#0', 'c': 'const'}
ADD(x, cvar c) against a+b                                 no match  
ADD(x, cvar c) against a+5 with dtype f32                  no match  
ADD(x, cvar c) against f+5.0 with dtype f32                match     {'x': 'param#2', 'c': 'const'}
MUL(ADD(x, c1), c2) against (a+3)*4                        match     {'x': 'param#0', 'c1': 'const', 'c2': 'const'}
ADD(x, x) against (a+b)+(a+b)                              match     {'x': 'add'}
ADD(x, x) against (a+b)+(b+a)  (no canonical order yet)    no match  
CONST(arg=7) against CONST 7                               match     {'k': 'const'}
CONST(arg=7) against CONST 8                               no match  

=== 2. the by-op index ===
rules in the default matcher: 30  (fold 1, inline 1, symbolic 28); fast math adds 5
  a node with op ADD       is tried against  7 rules
  a node with op MUL       is tried against  8 rules
  a node with op NEG       is tried against  4 rules
  a node with op CMPLT     is tried against  3 rules
  a node with op FLOORDIV  is tried against  5 rules
  a node with op FLOORMOD  is tried against  6 rules
  a node with op WHERE     is tried against  6 rules
  a node with op CAST      is tried against  3 rules
  a node with op CONST     is tried against  0 rules
  a node with op LOAD      is tried against  0 rules
  a node with op CALL      is tried against  1 rules
rule names in order: ['const fold', 'inline call', 'canonical order', 'range is a point', 'max by range', 'x*1', 'x*-1', 'x+(-0.0) (float)'] ...

=== 3. the first matching rule wins ===
a*1 ->  PARAM   (rule 'x*1', the first that matches)
a*0 for an int: rule that fires: ['range is a point'] -> 0 (anything times the constant 0 has the range [0, 0], a single point)
