public static DeltaResults applyDelta(JsonNode base, JsonNode delta){
		DeltaResults dr = new DeltaResults();
		// if object: create or recurse
		// otherwise: swap out
		if(base.isObject() && delta.isObject()){
			// we know they are objects, so casting is safe
			ObjectNode baseObj = ((ObjectNode) base);
			ObjectNode deltaObj = ((ObjectNode) delta);
			// initialize results to new JsonObjects
			dr.result = JsonNodeFactory.instance.objectNode();
			dr.inverse = JsonNodeFactory.instance.objectNode();
			// loop over all child nodes in delta
			Iterator<Entry<String, JsonNode>> itr = deltaObj.getFields();
			while(itr.hasNext()){
				Entry<String, JsonNode> pair = itr.next();
				String key = pair.getKey();
				JsonNode dChild = pair.getValue();
				if(baseObj.has(pair.getKey())){
					JsonNode bChild = base.get(key);
					// so far delta matches base, so we recurse one level deeper
					if(dChild.isNull()){
						// one caveat: delta's 'null' value means remove it (i.e. just don't set it at all)
						// store previous value in inverse
						((ObjectNode)dr.inverse).put(key, bChild);
					} else if(bChild.equals(dChild)){
						// subtrees are equal. there is nothing to update.
						// so, we put the existing value back into the result and leave inverse blank
						((ObjectNode)dr.result).put(key, bChild);
					} else{
						// still more to process in the subtree. recursing.
						DeltaResults child_dr = applyDelta(bChild, dChild);
						((ObjectNode)dr.result).put(key, child_dr.result);
						((ObjectNode)dr.inverse).put(key, child_dr.inverse);
					}
				} else{
					// creating a new field
					((ObjectNode)dr.result).put(key, dChild);
					((ObjectNode)dr.inverse).put(key, JsonNodeFactory.instance.nullNode());
				}
			}
			// looping over child nodes of base (to set elements that weren't altered by delta)
			itr = baseObj.getFields();
			while(itr.hasNext()){
				Entry<String, JsonNode> pair = itr.next();
				String key = pair.getKey();
				JsonNode bChild = pair.getValue();
				if(!deltaObj.has(key)) ((ObjectNode)dr.result).put(key, bChild);
			}
		} else{
			// not an object, so we just set the new value
			// (and store the previous value in 'inverse' so that it can be undone)
			dr.inverse = base;
			dr.result = delta;
		}
		return dr;
	}