Index: src/uk/me/parabola/mkgmap/osmstyle/RuleDetails.java
===================================================================
--- src/uk/me/parabola/mkgmap/osmstyle/RuleDetails.java	(revision 4183)
+++ src/uk/me/parabola/mkgmap/osmstyle/RuleDetails.java	(working copy)
@@ -42,4 +42,9 @@
 	public Set<String> getChangingTags() {
 		return changingTags;
 	}
+	
+	@Override
+	public String toString() {
+		return "keystring=\"" + keystring + "\" " + rule.toString();
+	}
 }
Index: src/uk/me/parabola/mkgmap/osmstyle/RuleIndex.java
===================================================================
--- src/uk/me/parabola/mkgmap/osmstyle/RuleIndex.java	(revision 4183)
+++ src/uk/me/parabola/mkgmap/osmstyle/RuleIndex.java	(working copy)
@@ -15,12 +15,13 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.BitSet;
-import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Optional;
 import java.util.Set;
 
@@ -27,7 +28,6 @@
 import uk.me.parabola.mkgmap.osmstyle.eval.Op;
 import uk.me.parabola.mkgmap.reader.osm.Rule;
 import uk.me.parabola.mkgmap.reader.osm.TagDict;
-import uk.me.parabola.util.MultiHashMap;
 
 /**
  * An index to reduce the number of rules that have to be executed.
@@ -72,21 +72,21 @@
 	private boolean inited;
 
 	private class TagHelper{
-		// This is an index of all rules that start with EXISTS (A=*) or (A=B)
-		final BitSet checked;
+		// This is an index of all rules that start with EXISTS (A=*)
+		final BitSet exists;
 		// This is an index of all rules that start with EQUALS (A=B) 
 		Map<String, BitSet> tagVals;
 		
-		public TagHelper(BitSet checked){
-			this.checked = checked;
+		public TagHelper(BitSet exits){
+			this.exists = exits;
 		}
 
 		public void addTag(String val, BitSet value) {
 			if (tagVals == null)
 				tagVals = new HashMap<>();
-			if (checked != null){	
+			if (exists != null){	
 				BitSet merged = new BitSet();
-				merged.or(checked);
+				merged.or(exists);
 				merged.or(value);
 				tagVals.put(val, merged);
 			} else
@@ -100,8 +100,8 @@
 					return (BitSet) set.clone();
 				}
 			} 
-			if (checked != null)
-				return (BitSet) checked.clone();
+			if (exists != null)
+				return (BitSet) exists.clone();
 			return new BitSet();
 		}
 	}
@@ -163,6 +163,8 @@
 	public void prepare() {
 		if (inited)
 			return;
+		// This is an index of all rules that start with EXISTS (A=*)
+		Map<String, BitSet> existKeys = new HashMap<String, BitSet>();
 		// This is an index of all rules that start with EQUALS (A=B)
 		Map<String, BitSet> tagVals = new HashMap<String, BitSet>();
 		
@@ -169,17 +171,6 @@
 		// This is an index of all rules by the tag name (A).
 		Map<String, BitSet> tagnames = new HashMap<String, BitSet>();
 
-		// Maps a rule number to the tags that might be changed by that rule
-		Map<Integer, List<String>> changeTags = new HashMap<Integer, List<String>>();
-		
-		// Collection of possibly changed or added tags were the new value is
-		// NOT known (e.g. set x=$y)
-		Set<String> modOrAddedTagKeys = new HashSet<>();
-
-		// Collection of possibly changed or added tags were the new value is
-		// known (e.g. set x=1)
-		MultiHashMap<String, String> modOrAddedTags = new MultiHashMap<>();
-
 		// remove unnecessary rules
 		filterRules();
 
@@ -187,99 +178,78 @@
 			int ruleNumber = i;
 			RuleDetails rd = ruleDetails.get(i);
 			String keystring = rd.getKeystring();
-			Set<String> changeableTags = rd.getChangingTags();
 
 			if (keystring.endsWith("=*")) {
 				String key = keystring.substring(0, keystring.length() - 2);
+				addNumberToMap(existKeys, key, ruleNumber);
 				addNumberToMap(tagnames, key, ruleNumber);
 			} else {
 				addNumberToMap(tagVals, keystring, ruleNumber);
-				// check if any of the previous rules may have changed the tag
 				int ind = keystring.indexOf('=');
-				assert ind >= 0 : "rule index: error in keystring " + keystring; 
-				String key = keystring.substring(0, ind);
-				boolean needed = (modOrAddedTagKeys.contains(key));
-				if (!needed) {
-					List<String> values = modOrAddedTags.get(key);
-					if (values.contains(keystring.substring(ind + 1)))
-						needed = true;
-				}
-				if (needed) {
+				if (ind >= 0) {
+					String key = keystring.substring(0, ind);
 					addNumberToMap(tagnames, key, ruleNumber);
+				} else {
+					assert false: "rule index: no = in keystring " + keystring;	
 				}
 			}
-			addChangables(changeTags, changeableTags, ruleNumber);
-			for (String ent : changeableTags) {
-				int ind = ent.indexOf('=');
+		}
+		
+		// find the additional rules which might be triggered as a result of actions changing tags.
+		Map<Integer, BitSet> additionalRules = new LinkedHashMap<>();
+		for (int i = 0; i < ruleDetails.size(); i++) {
+			int ruleNumber = i;
+			RuleDetails rd = ruleDetails.get(i);
+			final Set<String> changeableTags = rd.getChangingTags();
+			BitSet addedRules = new BitSet();
+			for (String s : changeableTags) {
+				int ind = s.indexOf('=');
 				if (ind >= 0) {
-					modOrAddedTags.add(ent.substring(0, ind), ent.substring(ind + 1));
+					BitSet set = tagVals.get(s);
+					if (set != null) {
+						addedRules.or(set);
+					}
+
+					// Exists rules can also be triggered, so add them too.
+					String key = s.substring(0, ind);
+					BitSet set1 = existKeys.get(key);
+					if (set1 != null) {
+						addedRules.or(set1);
+					}
 				} else {
-					modOrAddedTagKeys.add(ent);
+					BitSet set = tagnames.get(s);
+					if (set != null)
+						addedRules.or(set);
 				}
 			}
+			// Only rules after the current one can be affected 
+			addedRules.clear(0, ruleNumber);
+			if (!addedRules.isEmpty()) {
+				additionalRules.put(ruleNumber, addedRules);
+			}
 		}
 		
-		for (Map.Entry<Integer, List<String>> ent : changeTags.entrySet()) {
-			int ruleNumber = ent.getKey();
-			List<String> changeTagList = ent.getValue();
-
-			// When we add new rules, we may, in turn get more changeable tags
-			// which will force us to run again to find more rules that could
-			// be executed.  So save rules that we find here.
-			Set<String> newChanged = new HashSet<String>(changeTagList);
-			// we have to find all rules that might be now matched
-			do {
-				for (String s : new ArrayList<String>(newChanged)) {
-					BitSet set;
-
-					// If we know the value that could be set, then we can restrict to
-					// rules that would match that value.  Otherwise we look for any
-					// rule using the tag, no matter what the value.
-					int ind = s.indexOf('=');
-					if (ind >= 0) {
-						String key = s.substring(0, ind);
-						set = tagnames.get(key);
-					} else {
-						set = tagnames.get(s);
+		// now add all the additional rules to the existing sets
+		for (Entry<Integer, BitSet> e : additionalRules.entrySet()) {
+			int ruleNumber = e.getKey();
+			BitSet addSet = e.getValue();
+			// Find every rule number set that contains the rule number that we
+			// are examining and add all the newly found rules to each such set.
+			for (Map<String, BitSet> m : Arrays.asList(existKeys, tagVals, tagnames)) {
+				for (Entry<String, BitSet> e2 : m.entrySet()) {
+					BitSet bi = e2.getValue();
+					if (bi.get(ruleNumber)) {
+						// contains the rule that we are looking at so we must
+						// also add the rules in the set we found.
+						bi.or(addSet);
 					}
-
-					if (set != null && !set.isEmpty()) {
-						// create copy that can be safely modified
-						BitSet tmp  = new BitSet();
-						tmp.or(set);
-						set = tmp;
-						
-						for (int i = set.nextSetBit(0); i >= 0; i = set.nextSetBit(i + 1)) {
-							// Only rules after this one can be affected
-							if (i > ruleNumber) {
-								newChanged.addAll(ruleDetails.get(i).getChangingTags());
-							} else {
-								set.clear(i);
-							}
-						}
-
-						// Find every rule number set that contains the rule number that we
-						// are examining and add all the newly found rules to each such set.
-						for (Map<String, BitSet> m : Arrays.asList(tagVals, tagnames)) {
-							Collection<BitSet> bitSets = m.values();
-							for (BitSet bi : bitSets) {
-								if (bi.get(ruleNumber)) {
-									// contains the rule that we are looking at so we must
-									// also add the rules in the set we found.
-									bi.or(set);
-								}
-							}
-						}
-					}
 				}
-
-				newChanged.removeAll(changeTagList);
-				changeTagList.addAll(newChanged);
-			} while (!newChanged.isEmpty());
+			}
+			
 		}
-
+		
 		// compress the index: create one hash map with one entry for each key
-		for (Map.Entry<String, BitSet> entry  : tagnames.entrySet()){
+		for (Map.Entry<String, BitSet> entry  : existKeys.entrySet()){
 			Short skey = TagDict.getInstance().xlate(entry.getKey());
 			tagKeyMap.put(skey, new TagHelper(entry.getValue()));
 		}
@@ -374,25 +344,6 @@
 		set.set(ruleNumber);
 	}
 
-	/**
-	 * For each rule number, we maintain a list of tags that might be
-	 * changed by that rule.
-	 * @param changeTags 
-	 * @param changeableTags The tags that might be changed if the rule is
-	 * matched.
-	 * @param ruleNumber The rule number.
-	 */
-	private static void addChangables(Map<Integer, List<String>> changeTags, Set<String> changeableTags, int ruleNumber) {
-		if(changeableTags.isEmpty())
-			return;
-		List<String> tags = changeTags.get(ruleNumber);
-		if (tags == null) {
-			tags = new ArrayList<String>();
-			changeTags.put(ruleNumber, tags);
-		}
-		tags.addAll(changeableTags);
-	}
-
 	public List<RuleDetails> getRuleDetails() {
 		return ruleDetails;
 	}
Index: src/uk/me/parabola/mkgmap/osmstyle/RuleSet.java
===================================================================
--- src/uk/me/parabola/mkgmap/osmstyle/RuleSet.java	(revision 4183)
+++ src/uk/me/parabola/mkgmap/osmstyle/RuleSet.java	(working copy)
@@ -307,5 +307,20 @@
 		return false;
 	}
 
+	public BitSet getRules(Element el) {
+		if (!compiled || cacheId == Integer.MAX_VALUE)
+			compile();
+		// new element, invalidate all caches
+		cacheId++;
+
+		// Get all the rules that could match from the index.  
+		BitSet candidates = new BitSet();
+		for (Entry<Short, String> tagEntry : el.getFastTagEntryIterator()) {
+			BitSet rules = index.getRulesForTag(tagEntry.getKey(), tagEntry.getValue());
+			if (rules != null && !rules.isEmpty() )
+				candidates.or(rules);
+		}
+		return candidates;
+	}
 	
 } 
Index: test/uk/me/parabola/mkgmap/osmstyle/RuleSetTest.java
===================================================================
--- test/uk/me/parabola/mkgmap/osmstyle/RuleSetTest.java	(revision 4183)
+++ test/uk/me/parabola/mkgmap/osmstyle/RuleSetTest.java	(working copy)
@@ -14,6 +14,7 @@
 package uk.me.parabola.mkgmap.osmstyle;
 
 import java.util.ArrayList;
+import java.util.BitSet;
 import java.util.List;
 
 import uk.me.parabola.mkgmap.reader.osm.Element;
@@ -367,6 +368,33 @@
 		assertEquals("first element", 0x1f, list.get(0).getType());
 	}
 	
+	@Test
+	public void testIndexWithOddOrder() {
+		RuleSet rs = makeRuleSet("a=* {set b=1}" +
+				"b=1 {set c=1}" +
+				"d=2 {set c=2}" + 
+				"c=* {set a=2}" +
+				"c=1 {set d=2}" +
+				"c=2 {set d=1}" +
+				"d=1 [0x10401 resolution 24]" + 
+				"d=2 [0x10402 resolution 24]" 
+		);
+		
+		Way el = new Way(1);
+		el.addTag("a", "1");
+		
+		BitSet candidates = rs.getRules(el);
+		BitSet expected = new BitSet();
+		expected.flip(0,8);
+		expected.clear(2);
+		expected.clear(5);
+		expected.clear(6);
+		assertEquals("candidates", expected, candidates);
+		List<GType> list = resolveList(rs, el);
+		assertEquals("first element", 0x10402, list.get(0).getType());
+		
+	}
+	
 	private List<GType> resolveList(RuleSet rs, Way el) {
 		final List<GType> list = new ArrayList<GType>();
 		rs.resolveType(el, new TypeResult() {
