@@ -102,13 +102,11 @@ object Rul2Model {
102102 case Some ((rule, RhdAndLhd )) =>
103103 val key = new EquivRule (rule)
104104 if (! rulesShared.contains(key)) {
105- if (! rulesRhd.contains(key) && ! rulesLhd.contains(key))
106- rulesShared.addOne(key, rule)
107- else
108- require(
109- rulesRhd.contains(key) == rulesLhd.contains(key),
110- s " There's an unexpected rule conflict that differs between RHD and LHD: $rule"
111- )
105+ (rulesRhd.get(key).filterNot(rulesHaveSameOutput(rule, _)), rulesLhd.get(key).filterNot(rulesHaveSameOutput(rule, _))) match {
106+ case (None , None ) => rulesShared.addOne(key, rule)
107+ case (Some (rule2), Some (rule3)) => // ignore, as both RHD/LHD already define a different rule, so this rule never has an effect
108+ case _ => require(false , s " There's an unexpected rule conflict that differs between RHD and LHD: $rule" )
109+ }
112110 }
113111 case None => // ignore
114112 }
@@ -124,6 +122,17 @@ object Rul2Model {
124122 lookupRule.unapply(key).flatMap(rule => applyRule(rule, t0, t1))
125123 }
126124
125+ /** Check if two rules with equivalent LHS actually lead to the same output on
126+ * the RHS (and thus are not at conflict with each other).
127+ */
128+ def rulesHaveSameOutput (x : Rule [IdTile ], y : Rule [IdTile ]): Boolean = (
129+ x(0 ) == y(0 ) && x(1 ) == y(1 ) && x(2 ) == y(2 ) && x(3 ) == y(3 ) ||
130+ x(0 ) == y(0 ) * R2F1 && x(1 ) == y(1 ) * R2F1 && x(2 ) == y(2 ) * R2F1 && x(3 ) == y(3 ) * R2F1 ||
131+ x(0 ) == y(1 ) * R2F0 && x(1 ) == y(0 ) * R2F0 && x(2 ) == y(3 ) * R2F0 && x(3 ) == y(2 ) * R2F0 ||
132+ x(0 ) == y(1 ) * R0F1 && x(1 ) == y(0 ) * R0F1 && x(2 ) == y(3 ) * R0F1 && x(3 ) == y(2 ) * R0F1 ||
133+ (x(2 ).id == 0 || x(3 ).id == 0 ) && (y(2 ).id == 0 || y(3 ).id == 0 )
134+ )
135+
127136}
128137
129138/** Holds all RUL2 code in memory. Instantiate this with `Rul2Model.load`. */
0 commit comments