/// <summary>
		/// Patch a JSON object
		/// </summary>
		/// <param name="left">Unpatched JSON object</param>
		/// <param name="patch">JSON Patch Document</param>
		/// <returns>Patched JSON object</returns>
		/// <exception cref="System.IO.InvalidDataException">Thrown if the patch document is invalid</exception>
		public JToken Patch(JToken left, JToken patch)
		{
			if (patch == null)
				return left;

			if (patch.Type == JTokenType.Object)
			{
				var patchObj = (JObject)patch;
				JProperty arrayDiffCanary = patchObj.Property("_t");

				if (left != null
					&& left.Type == JTokenType.Array
					&& arrayDiffCanary != null
					&& arrayDiffCanary.Value.Type == JTokenType.String
					&& arrayDiffCanary.ToObject<string>() == "a")
				{
					return ArrayPatch((JArray)left, patchObj);
				}

				return ObjectPatch(left as JObject, patchObj);
			}

			if (patch.Type == JTokenType.Array)
			{
				var patchArray = (JArray)patch;

				if (patchArray.Count == 1) // Add
				{
					return patchArray[0];
				}
				else if (patchArray.Count == 2) // Replace
				{
					return patchArray[1];
				}
				else if (patchArray.Count == 3) // Delete, Move or TextDiff
				{
					if (patchArray[2].Type != JTokenType.Integer)
						throw new InvalidDataException("Invalid patch object");

					int op = patchArray[2].Value<int>();

					if (op == 0)
					{
						return null;
					}
					else if (op == 2)
					{
						var dmp = new diff_match_patch();
						List<Patch> patches = dmp.patch_fromText(patchArray[0].ToObject<string>());

						if (patches.Count != 1)
							throw new InvalidDataException("Invalid textline");

						string right = dmp.diff_text2(patches[0].diffs);
						return right;
					}
					else
					{
						throw new InvalidDataException("Invalid patch object");
					}
				}
				else
				{
					throw new InvalidDataException("Invalid patch object");
				}
			}

			return null;
		}