Move patch functionality into patch*.go
diff --git a/diffmatchpatch/diffmatchpatch.go b/diffmatchpatch/diffmatchpatch.go
index f0b0d3d..f1ec0c2 100644
--- a/diffmatchpatch/diffmatchpatch.go
+++ b/diffmatchpatch/diffmatchpatch.go
@@ -11,12 +11,7 @@
 package diffmatchpatch
 
 import (
-	"bytes"
-	"errors"
-	"math"
-	"net/url"
 	"regexp"
-	"strconv"
 	"strings"
 	"time"
 	"unicode/utf8"
@@ -151,58 +146,6 @@
 	Text string
 }
 
-// Patch represents one patch operation.
-type Patch struct {
-	diffs   []Diff
-	start1  int
-	start2  int
-	length1 int
-	length2 int
-}
-
-// String emulates GNU diff's format.
-// Header: @@ -382,8 +481,9 @@
-// Indicies are printed as 1-based, not 0-based.
-func (p *Patch) String() string {
-	var coords1, coords2 string
-
-	if p.length1 == 0 {
-		coords1 = strconv.Itoa(p.start1) + ",0"
-	} else if p.length1 == 1 {
-		coords1 = strconv.Itoa(p.start1 + 1)
-	} else {
-		coords1 = strconv.Itoa(p.start1+1) + "," + strconv.Itoa(p.length1)
-	}
-
-	if p.length2 == 0 {
-		coords2 = strconv.Itoa(p.start2) + ",0"
-	} else if p.length2 == 1 {
-		coords2 = strconv.Itoa(p.start2 + 1)
-	} else {
-		coords2 = strconv.Itoa(p.start2+1) + "," + strconv.Itoa(p.length2)
-	}
-
-	var text bytes.Buffer
-	_, _ = text.WriteString("@@ -" + coords1 + " +" + coords2 + " @@\n")
-
-	// Escape the body of the patch with %xx notation.
-	for _, aDiff := range p.diffs {
-		switch aDiff.Type {
-		case DiffInsert:
-			_, _ = text.WriteString("+")
-		case DiffDelete:
-			_, _ = text.WriteString("-")
-		case DiffEqual:
-			_, _ = text.WriteString(" ")
-		}
-
-		_, _ = text.WriteString(strings.Replace(url.QueryEscape(aDiff.Text), "+", " ", -1))
-		_, _ = text.WriteString("\n")
-	}
-
-	return unescaper.Replace(text.String())
-}
-
 // DiffMatchPatch holds the configuration for diff-match-patch operations.
 type DiffMatchPatch struct {
 	// Number of seconds to map a diff before giving up (0 for infinity).
@@ -239,504 +182,3 @@
 		MatchMaxBits:         32,
 	}
 }
-
-// PatchAddContext increases the context until it is unique,
-// but doesn't let the pattern expand beyond MatchMaxBits.
-func (dmp *DiffMatchPatch) PatchAddContext(patch Patch, text string) Patch {
-	if len(text) == 0 {
-		return patch
-	}
-
-	pattern := text[patch.start2 : patch.start2+patch.length1]
-	padding := 0
-
-	// Look for the first and last matches of pattern in text.  If two
-	// different matches are found, increase the pattern length.
-	for strings.Index(text, pattern) != strings.LastIndex(text, pattern) &&
-		len(pattern) < dmp.MatchMaxBits-2*dmp.PatchMargin {
-		padding += dmp.PatchMargin
-		maxStart := max(0, patch.start2-padding)
-		minEnd := min(len(text), patch.start2+patch.length1+padding)
-		pattern = text[maxStart:minEnd]
-	}
-	// Add one chunk for good luck.
-	padding += dmp.PatchMargin
-
-	// Add the prefix.
-	prefix := text[max(0, patch.start2-padding):patch.start2]
-	if len(prefix) != 0 {
-		patch.diffs = append([]Diff{Diff{DiffEqual, prefix}}, patch.diffs...)
-	}
-	// Add the suffix.
-	suffix := text[patch.start2+patch.length1 : min(len(text), patch.start2+patch.length1+padding)]
-	if len(suffix) != 0 {
-		patch.diffs = append(patch.diffs, Diff{DiffEqual, suffix})
-	}
-
-	// Roll back the start points.
-	patch.start1 -= len(prefix)
-	patch.start2 -= len(prefix)
-	// Extend the lengths.
-	patch.length1 += len(prefix) + len(suffix)
-	patch.length2 += len(prefix) + len(suffix)
-
-	return patch
-}
-
-// PatchMake computes a list of patches.
-func (dmp *DiffMatchPatch) PatchMake(opt ...interface{}) []Patch {
-	if len(opt) == 1 {
-		diffs, _ := opt[0].([]Diff)
-		text1 := dmp.DiffText1(diffs)
-		return dmp.PatchMake(text1, diffs)
-	} else if len(opt) == 2 {
-		text1 := opt[0].(string)
-		switch t := opt[1].(type) {
-		case string:
-			diffs := dmp.DiffMain(text1, t, true)
-			if len(diffs) > 2 {
-				diffs = dmp.DiffCleanupSemantic(diffs)
-				diffs = dmp.DiffCleanupEfficiency(diffs)
-			}
-			return dmp.PatchMake(text1, diffs)
-		case []Diff:
-			return dmp.patchMake2(text1, t)
-		}
-	} else if len(opt) == 3 {
-		return dmp.PatchMake(opt[0], opt[2])
-	}
-	return []Patch{}
-}
-
-// patchMake2 computes a list of patches to turn text1 into text2.
-// text2 is not provided, diffs are the delta between text1 and text2.
-func (dmp *DiffMatchPatch) patchMake2(text1 string, diffs []Diff) []Patch {
-	// Check for null inputs not needed since null can't be passed in C#.
-	patches := []Patch{}
-	if len(diffs) == 0 {
-		return patches // Get rid of the null case.
-	}
-
-	patch := Patch{}
-	charCount1 := 0 // Number of characters into the text1 string.
-	charCount2 := 0 // Number of characters into the text2 string.
-	// Start with text1 (prepatchText) and apply the diffs until we arrive at
-	// text2 (postpatchText). We recreate the patches one by one to determine
-	// context info.
-	prepatchText := text1
-	postpatchText := text1
-
-	for i, aDiff := range diffs {
-		if len(patch.diffs) == 0 && aDiff.Type != DiffEqual {
-			// A new patch starts here.
-			patch.start1 = charCount1
-			patch.start2 = charCount2
-		}
-
-		switch aDiff.Type {
-		case DiffInsert:
-			patch.diffs = append(patch.diffs, aDiff)
-			patch.length2 += len(aDiff.Text)
-			postpatchText = postpatchText[:charCount2] +
-				aDiff.Text + postpatchText[charCount2:]
-		case DiffDelete:
-			patch.length1 += len(aDiff.Text)
-			patch.diffs = append(patch.diffs, aDiff)
-			postpatchText = postpatchText[:charCount2] + postpatchText[charCount2+len(aDiff.Text):]
-		case DiffEqual:
-			if len(aDiff.Text) <= 2*dmp.PatchMargin &&
-				len(patch.diffs) != 0 && i != len(diffs)-1 {
-				// Small equality inside a patch.
-				patch.diffs = append(patch.diffs, aDiff)
-				patch.length1 += len(aDiff.Text)
-				patch.length2 += len(aDiff.Text)
-			}
-			if len(aDiff.Text) >= 2*dmp.PatchMargin {
-				// Time for a new patch.
-				if len(patch.diffs) != 0 {
-					patch = dmp.PatchAddContext(patch, prepatchText)
-					patches = append(patches, patch)
-					patch = Patch{}
-					// Unlike Unidiff, our patch lists have a rolling context.
-					// http://code.google.com/p/google-diff-match-patch/wiki/Unidiff
-					// Update prepatch text & pos to reflect the application of the
-					// just completed patch.
-					prepatchText = postpatchText
-					charCount1 = charCount2
-				}
-			}
-		}
-
-		// Update the current character count.
-		if aDiff.Type != DiffInsert {
-			charCount1 += len(aDiff.Text)
-		}
-		if aDiff.Type != DiffDelete {
-			charCount2 += len(aDiff.Text)
-		}
-	}
-
-	// Pick up the leftover patch if not empty.
-	if len(patch.diffs) != 0 {
-		patch = dmp.PatchAddContext(patch, prepatchText)
-		patches = append(patches, patch)
-	}
-
-	return patches
-}
-
-// PatchDeepCopy returns an array that is identical to a
-// given an array of patches.
-func (dmp *DiffMatchPatch) PatchDeepCopy(patches []Patch) []Patch {
-	patchesCopy := []Patch{}
-	for _, aPatch := range patches {
-		patchCopy := Patch{}
-		for _, aDiff := range aPatch.diffs {
-			patchCopy.diffs = append(patchCopy.diffs, Diff{
-				aDiff.Type,
-				aDiff.Text,
-			})
-		}
-		patchCopy.start1 = aPatch.start1
-		patchCopy.start2 = aPatch.start2
-		patchCopy.length1 = aPatch.length1
-		patchCopy.length2 = aPatch.length2
-		patchesCopy = append(patchesCopy, patchCopy)
-	}
-	return patchesCopy
-}
-
-// PatchApply merges a set of patches onto the text.  Returns a patched text, as well
-// as an array of true/false values indicating which patches were applied.
-func (dmp *DiffMatchPatch) PatchApply(patches []Patch, text string) (string, []bool) {
-	if len(patches) == 0 {
-		return text, []bool{}
-	}
-
-	// Deep copy the patches so that no changes are made to originals.
-	patches = dmp.PatchDeepCopy(patches)
-
-	nullPadding := dmp.PatchAddPadding(patches)
-	text = nullPadding + text + nullPadding
-	patches = dmp.PatchSplitMax(patches)
-
-	x := 0
-	// delta keeps track of the offset between the expected and actual
-	// location of the previous patch.  If there are patches expected at
-	// positions 10 and 20, but the first patch was found at 12, delta is 2
-	// and the second patch has an effective expected position of 22.
-	delta := 0
-	results := make([]bool, len(patches))
-	for _, aPatch := range patches {
-		expectedLoc := aPatch.start2 + delta
-		text1 := dmp.DiffText1(aPatch.diffs)
-		var startLoc int
-		endLoc := -1
-		if len(text1) > dmp.MatchMaxBits {
-			// PatchSplitMax will only provide an oversized pattern
-			// in the case of a monster delete.
-			startLoc = dmp.MatchMain(text, text1[:dmp.MatchMaxBits], expectedLoc)
-			if startLoc != -1 {
-				endLoc = dmp.MatchMain(text,
-					text1[len(text1)-dmp.MatchMaxBits:], expectedLoc+len(text1)-dmp.MatchMaxBits)
-				if endLoc == -1 || startLoc >= endLoc {
-					// Can't find valid trailing context.  Drop this patch.
-					startLoc = -1
-				}
-			}
-		} else {
-			startLoc = dmp.MatchMain(text, text1, expectedLoc)
-		}
-		if startLoc == -1 {
-			// No match found.  :(
-			results[x] = false
-			// Subtract the delta for this failed patch from subsequent patches.
-			delta -= aPatch.length2 - aPatch.length1
-		} else {
-			// Found a match.  :)
-			results[x] = true
-			delta = startLoc - expectedLoc
-			var text2 string
-			if endLoc == -1 {
-				text2 = text[startLoc:int(math.Min(float64(startLoc+len(text1)), float64(len(text))))]
-			} else {
-				text2 = text[startLoc:int(math.Min(float64(endLoc+dmp.MatchMaxBits), float64(len(text))))]
-			}
-			if text1 == text2 {
-				// Perfect match, just shove the Replacement text in.
-				text = text[:startLoc] + dmp.DiffText2(aPatch.diffs) + text[startLoc+len(text1):]
-			} else {
-				// Imperfect match.  Run a diff to get a framework of equivalent
-				// indices.
-				diffs := dmp.DiffMain(text1, text2, false)
-				if len(text1) > dmp.MatchMaxBits && float64(dmp.DiffLevenshtein(diffs))/float64(len(text1)) > dmp.PatchDeleteThreshold {
-					// The end points match, but the content is unacceptably bad.
-					results[x] = false
-				} else {
-					diffs = dmp.DiffCleanupSemanticLossless(diffs)
-					index1 := 0
-					for _, aDiff := range aPatch.diffs {
-						if aDiff.Type != DiffEqual {
-							index2 := dmp.DiffXIndex(diffs, index1)
-							if aDiff.Type == DiffInsert {
-								// Insertion
-								text = text[:startLoc+index2] + aDiff.Text + text[startLoc+index2:]
-							} else if aDiff.Type == DiffDelete {
-								// Deletion
-								startIndex := startLoc + index2
-								text = text[:startIndex] +
-									text[startIndex+dmp.DiffXIndex(diffs, index1+len(aDiff.Text))-index2:]
-							}
-						}
-						if aDiff.Type != DiffDelete {
-							index1 += len(aDiff.Text)
-						}
-					}
-				}
-			}
-		}
-		x++
-	}
-	// Strip the padding off.
-	text = text[len(nullPadding) : len(nullPadding)+(len(text)-2*len(nullPadding))]
-	return text, results
-}
-
-// PatchAddPadding adds some padding on text start and end so that edges can match something.
-// Intended to be called only from within patchApply.
-func (dmp *DiffMatchPatch) PatchAddPadding(patches []Patch) string {
-	paddingLength := dmp.PatchMargin
-	nullPadding := ""
-	for x := 1; x <= paddingLength; x++ {
-		nullPadding += string(x)
-	}
-
-	// Bump all the patches forward.
-	for i := range patches {
-		patches[i].start1 += paddingLength
-		patches[i].start2 += paddingLength
-	}
-
-	// Add some padding on start of first diff.
-	if len(patches[0].diffs) == 0 || patches[0].diffs[0].Type != DiffEqual {
-		// Add nullPadding equality.
-		patches[0].diffs = append([]Diff{Diff{DiffEqual, nullPadding}}, patches[0].diffs...)
-		patches[0].start1 -= paddingLength // Should be 0.
-		patches[0].start2 -= paddingLength // Should be 0.
-		patches[0].length1 += paddingLength
-		patches[0].length2 += paddingLength
-	} else if paddingLength > len(patches[0].diffs[0].Text) {
-		// Grow first equality.
-		extraLength := paddingLength - len(patches[0].diffs[0].Text)
-		patches[0].diffs[0].Text = nullPadding[len(patches[0].diffs[0].Text):] + patches[0].diffs[0].Text
-		patches[0].start1 -= extraLength
-		patches[0].start2 -= extraLength
-		patches[0].length1 += extraLength
-		patches[0].length2 += extraLength
-	}
-
-	// Add some padding on end of last diff.
-	last := len(patches) - 1
-	if len(patches[last].diffs) == 0 || patches[last].diffs[len(patches[last].diffs)-1].Type != DiffEqual {
-		// Add nullPadding equality.
-		patches[last].diffs = append(patches[last].diffs, Diff{DiffEqual, nullPadding})
-		patches[last].length1 += paddingLength
-		patches[last].length2 += paddingLength
-	} else if paddingLength > len(patches[last].diffs[len(patches[last].diffs)-1].Text) {
-		// Grow last equality.
-		lastDiff := patches[last].diffs[len(patches[last].diffs)-1]
-		extraLength := paddingLength - len(lastDiff.Text)
-		patches[last].diffs[len(patches[last].diffs)-1].Text += nullPadding[:extraLength]
-		patches[last].length1 += extraLength
-		patches[last].length2 += extraLength
-	}
-
-	return nullPadding
-}
-
-// PatchSplitMax looks through the patches and breaks up any which are longer than the
-// maximum limit of the match algorithm.
-// Intended to be called only from within patchApply.
-func (dmp *DiffMatchPatch) PatchSplitMax(patches []Patch) []Patch {
-	patchSize := dmp.MatchMaxBits
-	for x := 0; x < len(patches); x++ {
-		if patches[x].length1 <= patchSize {
-			continue
-		}
-		bigpatch := patches[x]
-		// Remove the big old patch.
-		patches = append(patches[:x], patches[x+1:]...)
-		x--
-
-		start1 := bigpatch.start1
-		start2 := bigpatch.start2
-		precontext := ""
-		for len(bigpatch.diffs) != 0 {
-			// Create one of several smaller patches.
-			patch := Patch{}
-			empty := true
-			patch.start1 = start1 - len(precontext)
-			patch.start2 = start2 - len(precontext)
-			if len(precontext) != 0 {
-				patch.length1 = len(precontext)
-				patch.length2 = len(precontext)
-				patch.diffs = append(patch.diffs, Diff{DiffEqual, precontext})
-			}
-			for len(bigpatch.diffs) != 0 && patch.length1 < patchSize-dmp.PatchMargin {
-				diffType := bigpatch.diffs[0].Type
-				diffText := bigpatch.diffs[0].Text
-				if diffType == DiffInsert {
-					// Insertions are harmless.
-					patch.length2 += len(diffText)
-					start2 += len(diffText)
-					patch.diffs = append(patch.diffs, bigpatch.diffs[0])
-					bigpatch.diffs = bigpatch.diffs[1:]
-					empty = false
-				} else if diffType == DiffDelete && len(patch.diffs) == 1 && patch.diffs[0].Type == DiffEqual && len(diffText) > 2*patchSize {
-					// This is a large deletion.  Let it pass in one chunk.
-					patch.length1 += len(diffText)
-					start1 += len(diffText)
-					empty = false
-					patch.diffs = append(patch.diffs, Diff{diffType, diffText})
-					bigpatch.diffs = bigpatch.diffs[1:]
-				} else {
-					// Deletion or equality.  Only take as much as we can stomach.
-					diffText = diffText[:min(len(diffText), patchSize-patch.length1-dmp.PatchMargin)]
-
-					patch.length1 += len(diffText)
-					start1 += len(diffText)
-					if diffType == DiffEqual {
-						patch.length2 += len(diffText)
-						start2 += len(diffText)
-					} else {
-						empty = false
-					}
-					patch.diffs = append(patch.diffs, Diff{diffType, diffText})
-					if diffText == bigpatch.diffs[0].Text {
-						bigpatch.diffs = bigpatch.diffs[1:]
-					} else {
-						bigpatch.diffs[0].Text =
-							bigpatch.diffs[0].Text[len(diffText):]
-					}
-				}
-			}
-			// Compute the head context for the next patch.
-			precontext = dmp.DiffText2(patch.diffs)
-			precontext = precontext[max(0, len(precontext)-dmp.PatchMargin):]
-
-			postcontext := ""
-			// Append the end context for this patch.
-			if len(dmp.DiffText1(bigpatch.diffs)) > dmp.PatchMargin {
-				postcontext = dmp.DiffText1(bigpatch.diffs)[:dmp.PatchMargin]
-			} else {
-				postcontext = dmp.DiffText1(bigpatch.diffs)
-			}
-
-			if len(postcontext) != 0 {
-				patch.length1 += len(postcontext)
-				patch.length2 += len(postcontext)
-				if len(patch.diffs) != 0 && patch.diffs[len(patch.diffs)-1].Type == DiffEqual {
-					patch.diffs[len(patch.diffs)-1].Text += postcontext
-				} else {
-					patch.diffs = append(patch.diffs, Diff{DiffEqual, postcontext})
-				}
-			}
-			if !empty {
-				x++
-				patches = append(patches[:x], append([]Patch{patch}, patches[x:]...)...)
-			}
-		}
-	}
-	return patches
-}
-
-// PatchToText takes a list of patches and returns a textual representation.
-func (dmp *DiffMatchPatch) PatchToText(patches []Patch) string {
-	var text bytes.Buffer
-	for _, aPatch := range patches {
-		_, _ = text.WriteString(aPatch.String())
-	}
-	return text.String()
-}
-
-// PatchFromText parses a textual representation of patches and returns a List of Patch
-// objects.
-func (dmp *DiffMatchPatch) PatchFromText(textline string) ([]Patch, error) {
-	patches := []Patch{}
-	if len(textline) == 0 {
-		return patches, nil
-	}
-	text := strings.Split(textline, "\n")
-	textPointer := 0
-	patchHeader := regexp.MustCompile("^@@ -(\\d+),?(\\d*) \\+(\\d+),?(\\d*) @@$")
-
-	var patch Patch
-	var sign uint8
-	var line string
-	for textPointer < len(text) {
-
-		if !patchHeader.MatchString(text[textPointer]) {
-			return patches, errors.New("Invalid patch string: " + text[textPointer])
-		}
-
-		patch = Patch{}
-		m := patchHeader.FindStringSubmatch(text[textPointer])
-
-		patch.start1, _ = strconv.Atoi(m[1])
-		if len(m[2]) == 0 {
-			patch.start1--
-			patch.length1 = 1
-		} else if m[2] == "0" {
-			patch.length1 = 0
-		} else {
-			patch.start1--
-			patch.length1, _ = strconv.Atoi(m[2])
-		}
-
-		patch.start2, _ = strconv.Atoi(m[3])
-
-		if len(m[4]) == 0 {
-			patch.start2--
-			patch.length2 = 1
-		} else if m[4] == "0" {
-			patch.length2 = 0
-		} else {
-			patch.start2--
-			patch.length2, _ = strconv.Atoi(m[4])
-		}
-		textPointer++
-
-		for textPointer < len(text) {
-			if len(text[textPointer]) > 0 {
-				sign = text[textPointer][0]
-			} else {
-				textPointer++
-				continue
-			}
-
-			line = text[textPointer][1:]
-			line = strings.Replace(line, "+", "%2b", -1)
-			line, _ = url.QueryUnescape(line)
-			if sign == '-' {
-				// Deletion.
-				patch.diffs = append(patch.diffs, Diff{DiffDelete, line})
-			} else if sign == '+' {
-				// Insertion.
-				patch.diffs = append(patch.diffs, Diff{DiffInsert, line})
-			} else if sign == ' ' {
-				// Minor equality.
-				patch.diffs = append(patch.diffs, Diff{DiffEqual, line})
-			} else if sign == '@' {
-				// Start of next patch.
-				break
-			} else {
-				// WTF?
-				return patches, errors.New("Invalid patch mode '" + string(sign) + "' in: " + string(line))
-			}
-			textPointer++
-		}
-
-		patches = append(patches, patch)
-	}
-	return patches, nil
-}
diff --git a/diffmatchpatch/diffmatchpatch_test.go b/diffmatchpatch/diffmatchpatch_test.go
index 8fad70a..9f62f7b 100644
--- a/diffmatchpatch/diffmatchpatch_test.go
+++ b/diffmatchpatch/diffmatchpatch_test.go
@@ -13,7 +13,6 @@
 	"fmt"
 	"io/ioutil"
 	"runtime"
-	"strings"
 	"testing"
 
 	"github.com/stretchr/testify/assert"
@@ -108,323 +107,6 @@
 	}
 }
 
-func TestPatchString(t *testing.T) {
-	type TestCase struct {
-		Patch Patch
-
-		Expected string
-	}
-
-	for i, tc := range []TestCase{
-		{
-			Patch: Patch{
-				start1:  20,
-				start2:  21,
-				length1: 18,
-				length2: 17,
-
-				diffs: []Diff{
-					{DiffEqual, "jump"},
-					{DiffDelete, "s"},
-					{DiffInsert, "ed"},
-					{DiffEqual, " over "},
-					{DiffDelete, "the"},
-					{DiffInsert, "a"},
-					{DiffEqual, "\nlaz"},
-				},
-			},
-
-			Expected: "@@ -21,18 +22,17 @@\n jump\n-s\n+ed\n  over \n-the\n+a\n %0Alaz\n",
-		},
-	} {
-		actual := tc.Patch.String()
-		assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc))
-	}
-}
-
-func TestPatchFromText(t *testing.T) {
-	type TestCase struct {
-		Patch string
-
-		ErrorMessagePrefix string
-	}
-
-	dmp := New()
-
-	for i, tc := range []TestCase{
-		{"", ""},
-		{"@@ -21,18 +22,17 @@\n jump\n-s\n+ed\n  over \n-the\n+a\n %0Alaz\n", ""},
-		{"@@ -1 +1 @@\n-a\n+b\n", ""},
-		{"@@ -1,3 +0,0 @@\n-abc\n", ""},
-		{"@@ -0,0 +1,3 @@\n+abc\n", ""},
-		{"Bad\nPatch\n", "Invalid patch string"},
-	} {
-		patches, err := dmp.PatchFromText(tc.Patch)
-		if tc.ErrorMessagePrefix == "" {
-			assert.Nil(t, err)
-
-			if tc.Patch == "" {
-				assert.Equal(t, []Patch{}, patches, fmt.Sprintf("Test case #%d, %#v", i, tc))
-			} else {
-				assert.Equal(t, tc.Patch, patches[0].String(), fmt.Sprintf("Test case #%d, %#v", i, tc))
-			}
-		} else {
-			e := err.Error()
-			if strings.HasPrefix(e, tc.ErrorMessagePrefix) {
-				e = tc.ErrorMessagePrefix
-			}
-			assert.Equal(t, tc.ErrorMessagePrefix, e)
-		}
-	}
-
-	diffs := []Diff{
-		{DiffDelete, "`1234567890-=[]\\;',./"},
-		{DiffInsert, "~!@#$%^&*()_+{}|:\"<>?"},
-	}
-
-	patches, err := dmp.PatchFromText("@@ -1,21 +1,21 @@\n-%601234567890-=%5B%5D%5C;',./\n+~!@#$%25%5E&*()_+%7B%7D%7C:%22%3C%3E?\n")
-	assert.Len(t, patches, 1)
-	assert.Equal(t, diffs,
-		patches[0].diffs,
-	)
-	assert.Nil(t, err)
-}
-
-func TestPatchToText(t *testing.T) {
-	type TestCase struct {
-		Patch string
-	}
-
-	dmp := New()
-
-	for i, tc := range []TestCase{
-		{"@@ -21,18 +22,17 @@\n jump\n-s\n+ed\n  over \n-the\n+a\n  laz\n"},
-		{"@@ -1,9 +1,9 @@\n-f\n+F\n oo+fooba\n@@ -7,9 +7,9 @@\n obar\n-,\n+.\n  tes\n"},
-	} {
-		patches, err := dmp.PatchFromText(tc.Patch)
-		assert.Nil(t, err)
-
-		actual := dmp.PatchToText(patches)
-		assert.Equal(t, tc.Patch, actual, fmt.Sprintf("Test case #%d, %#v", i, tc))
-	}
-}
-
-func TestPatchAddContext(t *testing.T) {
-	type TestCase struct {
-		Name string
-
-		Patch string
-		Text  string
-
-		Expected string
-	}
-
-	dmp := New()
-	dmp.PatchMargin = 4
-
-	for i, tc := range []TestCase{
-		{"Simple case", "@@ -21,4 +21,10 @@\n-jump\n+somersault\n", "The quick brown fox jumps over the lazy dog.", "@@ -17,12 +17,18 @@\n fox \n-jump\n+somersault\n s ov\n"},
-		{"Not enough trailing context", "@@ -21,4 +21,10 @@\n-jump\n+somersault\n", "The quick brown fox jumps.", "@@ -17,10 +17,16 @@\n fox \n-jump\n+somersault\n s.\n"},
-		{"Not enough leading context", "@@ -3 +3,2 @@\n-e\n+at\n", "The quick brown fox jumps.", "@@ -1,7 +1,8 @@\n Th\n-e\n+at\n  qui\n"},
-		{"Ambiguity", "@@ -3 +3,2 @@\n-e\n+at\n", "The quick brown fox jumps.  The quick brown fox crashes.", "@@ -1,27 +1,28 @@\n Th\n-e\n+at\n  quick brown fox jumps. \n"},
-	} {
-		patches, err := dmp.PatchFromText(tc.Patch)
-		assert.Nil(t, err)
-
-		actual := dmp.PatchAddContext(patches[0], tc.Text)
-		assert.Equal(t, tc.Expected, actual.String(), fmt.Sprintf("Test case #%d, %s", i, tc.Name))
-	}
-}
-
-func TestPatchMakeAndPatchToText(t *testing.T) {
-	type TestCase struct {
-		Name string
-
-		Input1 interface{}
-		Input2 interface{}
-		Input3 interface{}
-
-		Expected string
-	}
-
-	dmp := New()
-
-	text1 := "The quick brown fox jumps over the lazy dog."
-	text2 := "That quick brown fox jumped over a lazy dog."
-
-	for i, tc := range []TestCase{
-		{"Null case", "", "", nil, ""},
-		{"Text2+Text1 inputs", text2, text1, nil, "@@ -1,8 +1,7 @@\n Th\n-at\n+e\n  qui\n@@ -21,17 +21,18 @@\n jump\n-ed\n+s\n  over \n-a\n+the\n  laz\n"},
-		{"Text1+Text2 inputs", text1, text2, nil, "@@ -1,11 +1,12 @@\n Th\n-e\n+at\n  quick b\n@@ -22,18 +22,17 @@\n jump\n-s\n+ed\n  over \n-the\n+a\n  laz\n"},
-		{"Diff input", dmp.DiffMain(text1, text2, false), nil, nil, "@@ -1,11 +1,12 @@\n Th\n-e\n+at\n  quick b\n@@ -22,18 +22,17 @@\n jump\n-s\n+ed\n  over \n-the\n+a\n  laz\n"},
-		{"Text1+Diff inputs", text1, dmp.DiffMain(text1, text2, false), nil, "@@ -1,11 +1,12 @@\n Th\n-e\n+at\n  quick b\n@@ -22,18 +22,17 @@\n jump\n-s\n+ed\n  over \n-the\n+a\n  laz\n"},
-		{"Text1+Text2+Diff inputs (deprecated)", text1, text2, dmp.DiffMain(text1, text2, false), "@@ -1,11 +1,12 @@\n Th\n-e\n+at\n  quick b\n@@ -22,18 +22,17 @@\n jump\n-s\n+ed\n  over \n-the\n+a\n  laz\n"},
-		{"Character encoding", "`1234567890-=[]\\;',./", "~!@#$%^&*()_+{}|:\"<>?", nil, "@@ -1,21 +1,21 @@\n-%601234567890-=%5B%5D%5C;',./\n+~!@#$%25%5E&*()_+%7B%7D%7C:%22%3C%3E?\n"},
-		{"Long string with repeats", strings.Repeat("abcdef", 100), strings.Repeat("abcdef", 100) + "123", nil, "@@ -573,28 +573,31 @@\n cdefabcdefabcdefabcdefabcdef\n+123\n"},
-		{"Corner case of #31 fixed by #32", "2016-09-01T03:07:14.807830741Z", "2016-09-01T03:07:15.154800781Z", nil, "@@ -15,16 +15,16 @@\n 07:1\n+5.15\n 4\n-.\n 80\n+0\n 78\n-3074\n 1Z\n"},
-	} {
-		var patches []Patch
-		if tc.Input3 != nil {
-			patches = dmp.PatchMake(tc.Input1, tc.Input2, tc.Input3)
-		} else if tc.Input2 != nil {
-			patches = dmp.PatchMake(tc.Input1, tc.Input2)
-		} else if ps, ok := tc.Input1.([]Patch); ok {
-			patches = ps
-		} else {
-			patches = dmp.PatchMake(tc.Input1)
-		}
-
-		actual := dmp.PatchToText(patches)
-		assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name))
-	}
-
-	// Corner case of #28 wrong patch with timeout of 0
-	dmp.DiffTimeout = 0
-
-	text1 = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus ut risus et enim consectetur convallis a non ipsum. Sed nec nibh cursus, interdum libero vel."
-	text2 = "Lorem a ipsum dolor sit amet, consectetur adipiscing elit. Vivamus ut risus et enim consectetur convallis a non ipsum. Sed nec nibh cursus, interdum liberovel."
-
-	diffs := dmp.DiffMain(text1, text2, true)
-	// Additional check that the diff texts are equal to the originals even if we are using DiffMain with checklines=true #29
-	assert.Equal(t, text1, dmp.DiffText1(diffs))
-	assert.Equal(t, text2, dmp.DiffText2(diffs))
-
-	patches := dmp.PatchMake(text1, diffs)
-
-	actual := dmp.PatchToText(patches)
-	assert.Equal(t, "@@ -1,14 +1,16 @@\n Lorem \n+a \n ipsum do\n@@ -148,13 +148,12 @@\n m libero\n- \n vel.\n", actual)
-}
-
-func TestPatchSplitMax(t *testing.T) {
-	type TestCase struct {
-		Text1 string
-		Text2 string
-
-		Expected string
-	}
-
-	dmp := New()
-
-	for i, tc := range []TestCase{
-		{"abcdefghijklmnopqrstuvwxyz01234567890", "XabXcdXefXghXijXklXmnXopXqrXstXuvXwxXyzX01X23X45X67X89X0", "@@ -1,32 +1,46 @@\n+X\n ab\n+X\n cd\n+X\n ef\n+X\n gh\n+X\n ij\n+X\n kl\n+X\n mn\n+X\n op\n+X\n qr\n+X\n st\n+X\n uv\n+X\n wx\n+X\n yz\n+X\n 012345\n@@ -25,13 +39,18 @@\n zX01\n+X\n 23\n+X\n 45\n+X\n 67\n+X\n 89\n+X\n 0\n"},
-		{"abcdef1234567890123456789012345678901234567890123456789012345678901234567890uvwxyz", "abcdefuvwxyz", "@@ -3,78 +3,8 @@\n cdef\n-1234567890123456789012345678901234567890123456789012345678901234567890\n uvwx\n"},
-		{"1234567890123456789012345678901234567890123456789012345678901234567890", "abc", "@@ -1,32 +1,4 @@\n-1234567890123456789012345678\n 9012\n@@ -29,32 +1,4 @@\n-9012345678901234567890123456\n 7890\n@@ -57,14 +1,3 @@\n-78901234567890\n+abc\n"},
-		{"abcdefghij , h : 0 , t : 1 abcdefghij , h : 0 , t : 1 abcdefghij , h : 0 , t : 1", "abcdefghij , h : 1 , t : 1 abcdefghij , h : 1 , t : 1 abcdefghij , h : 0 , t : 1", "@@ -2,32 +2,32 @@\n bcdefghij , h : \n-0\n+1\n  , t : 1 abcdef\n@@ -29,32 +29,32 @@\n bcdefghij , h : \n-0\n+1\n  , t : 1 abcdef\n"},
-	} {
-		patches := dmp.PatchMake(tc.Text1, tc.Text2)
-		patches = dmp.PatchSplitMax(patches)
-
-		actual := dmp.PatchToText(patches)
-		assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc))
-	}
-}
-
-func TestPatchAddPadding(t *testing.T) {
-	type TestCase struct {
-		Name string
-
-		Text1 string
-		Text2 string
-
-		Expected            string
-		ExpectedWithPadding string
-	}
-
-	dmp := New()
-
-	for i, tc := range []TestCase{
-		{"Both edges full", "", "test", "@@ -0,0 +1,4 @@\n+test\n", "@@ -1,8 +1,12 @@\n %01%02%03%04\n+test\n %01%02%03%04\n"},
-		{"Both edges partial", "XY", "XtestY", "@@ -1,2 +1,6 @@\n X\n+test\n Y\n", "@@ -2,8 +2,12 @@\n %02%03%04X\n+test\n Y%01%02%03\n"},
-		{"Both edges none", "XXXXYYYY", "XXXXtestYYYY", "@@ -1,8 +1,12 @@\n XXXX\n+test\n YYYY\n", "@@ -5,8 +5,12 @@\n XXXX\n+test\n YYYY\n"},
-	} {
-		patches := dmp.PatchMake(tc.Text1, tc.Text2)
-
-		actual := dmp.PatchToText(patches)
-		assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name))
-
-		dmp.PatchAddPadding(patches)
-
-		actualWithPadding := dmp.PatchToText(patches)
-		assert.Equal(t, tc.ExpectedWithPadding, actualWithPadding, fmt.Sprintf("Test case #%d, %s", i, tc.Name))
-	}
-}
-
-func TestPatchApply(t *testing.T) {
-	type TestCase struct {
-		Name string
-
-		Text1    string
-		Text2    string
-		TextBase string
-
-		Expected        string
-		ExpectedApplies []bool
-	}
-
-	dmp := New()
-	dmp.MatchDistance = 1000
-	dmp.MatchThreshold = 0.5
-	dmp.PatchDeleteThreshold = 0.5
-
-	for i, tc := range []TestCase{
-		{"Null case", "", "", "Hello world.", "Hello world.", []bool{}},
-		{"Exact match", "The quick brown fox jumps over the lazy dog.", "That quick brown fox jumped over a lazy dog.", "The quick brown fox jumps over the lazy dog.", "That quick brown fox jumped over a lazy dog.", []bool{true, true}},
-		{"Partial match", "The quick brown fox jumps over the lazy dog.", "That quick brown fox jumped over a lazy dog.", "The quick red rabbit jumps over the tired tiger.", "That quick red rabbit jumped over a tired tiger.", []bool{true, true}},
-		{"Failed match", "The quick brown fox jumps over the lazy dog.", "That quick brown fox jumped over a lazy dog.", "I am the very model of a modern major general.", "I am the very model of a modern major general.", []bool{false, false}},
-		{"Big delete, small Diff", "x1234567890123456789012345678901234567890123456789012345678901234567890y", "xabcy", "x123456789012345678901234567890-----++++++++++-----123456789012345678901234567890y", "xabcy", []bool{true, true}},
-		{"Big delete, big Diff 1", "x1234567890123456789012345678901234567890123456789012345678901234567890y", "xabcy", "x12345678901234567890---------------++++++++++---------------12345678901234567890y", "xabc12345678901234567890---------------++++++++++---------------12345678901234567890y", []bool{false, true}},
-	} {
-		patches := dmp.PatchMake(tc.Text1, tc.Text2)
-
-		actual, actualApplies := dmp.PatchApply(patches, tc.TextBase)
-		assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name))
-		assert.Equal(t, tc.ExpectedApplies, actualApplies, fmt.Sprintf("Test case #%d, %s", i, tc.Name))
-	}
-
-	dmp.PatchDeleteThreshold = 0.6
-
-	for i, tc := range []TestCase{
-		{"Big delete, big Diff 2", "x1234567890123456789012345678901234567890123456789012345678901234567890y", "xabcy", "x12345678901234567890---------------++++++++++---------------12345678901234567890y", "xabcy", []bool{true, true}},
-	} {
-		patches := dmp.PatchMake(tc.Text1, tc.Text2)
-
-		actual, actualApplies := dmp.PatchApply(patches, tc.TextBase)
-		assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name))
-		assert.Equal(t, tc.ExpectedApplies, actualApplies, fmt.Sprintf("Test case #%d, %s", i, tc.Name))
-	}
-
-	dmp.MatchDistance = 0
-	dmp.MatchThreshold = 0.0
-	dmp.PatchDeleteThreshold = 0.5
-
-	for i, tc := range []TestCase{
-		{"Compensate for failed patch", "abcdefghijklmnopqrstuvwxyz--------------------1234567890", "abcXXXXXXXXXXdefghijklmnopqrstuvwxyz--------------------1234567YYYYYYYYYY890", "ABCDEFGHIJKLMNOPQRSTUVWXYZ--------------------1234567890", "ABCDEFGHIJKLMNOPQRSTUVWXYZ--------------------1234567YYYYYYYYYY890", []bool{false, true}},
-	} {
-		patches := dmp.PatchMake(tc.Text1, tc.Text2)
-
-		actual, actualApplies := dmp.PatchApply(patches, tc.TextBase)
-		assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name))
-		assert.Equal(t, tc.ExpectedApplies, actualApplies, fmt.Sprintf("Test case #%d, %s", i, tc.Name))
-	}
-
-	dmp.MatchThreshold = 0.5
-	dmp.MatchDistance = 1000
-
-	for i, tc := range []TestCase{
-		{"No side effects", "", "test", "", "test", []bool{true}},
-		{"No side effects with major delete", "The quick brown fox jumps over the lazy dog.", "Woof", "The quick brown fox jumps over the lazy dog.", "Woof", []bool{true, true}},
-		{"Edge exact match", "", "test", "", "test", []bool{true}},
-		{"Near edge exact match", "XY", "XtestY", "XY", "XtestY", []bool{true}},
-		{"Edge partial match", "y", "y123", "x", "x123", []bool{true}},
-	} {
-		patches := dmp.PatchMake(tc.Text1, tc.Text2)
-
-		actual, actualApplies := dmp.PatchApply(patches, tc.TextBase)
-		assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name))
-		assert.Equal(t, tc.ExpectedApplies, actualApplies, fmt.Sprintf("Test case #%d, %s", i, tc.Name))
-	}
-}
-
 func TestIndexOf(t *testing.T) {
 	type TestCase struct {
 		String   string
diff --git a/diffmatchpatch/patch.go b/diffmatchpatch/patch.go
new file mode 100644
index 0000000..be16c4e
--- /dev/null
+++ b/diffmatchpatch/patch.go
@@ -0,0 +1,572 @@
+// Copyright (c) 2012-2016 The go-diff authors. All rights reserved.
+// https://github.com/sergi/go-diff
+// See the included LICENSE file for license details.
+//
+// go-diff is a Go implementation of Google's Diff, Match, and Patch library
+// Original library is Copyright (c) 2006 Google Inc.
+// http://code.google.com/p/google-diff-match-patch/
+
+package diffmatchpatch
+
+import (
+	"bytes"
+	"errors"
+	"math"
+	"net/url"
+	"regexp"
+	"strconv"
+	"strings"
+)
+
+// Patch represents one patch operation.
+type Patch struct {
+	diffs   []Diff
+	start1  int
+	start2  int
+	length1 int
+	length2 int
+}
+
+// String emulates GNU diff's format.
+// Header: @@ -382,8 +481,9 @@
+// Indicies are printed as 1-based, not 0-based.
+func (p *Patch) String() string {
+	var coords1, coords2 string
+
+	if p.length1 == 0 {
+		coords1 = strconv.Itoa(p.start1) + ",0"
+	} else if p.length1 == 1 {
+		coords1 = strconv.Itoa(p.start1 + 1)
+	} else {
+		coords1 = strconv.Itoa(p.start1+1) + "," + strconv.Itoa(p.length1)
+	}
+
+	if p.length2 == 0 {
+		coords2 = strconv.Itoa(p.start2) + ",0"
+	} else if p.length2 == 1 {
+		coords2 = strconv.Itoa(p.start2 + 1)
+	} else {
+		coords2 = strconv.Itoa(p.start2+1) + "," + strconv.Itoa(p.length2)
+	}
+
+	var text bytes.Buffer
+	_, _ = text.WriteString("@@ -" + coords1 + " +" + coords2 + " @@\n")
+
+	// Escape the body of the patch with %xx notation.
+	for _, aDiff := range p.diffs {
+		switch aDiff.Type {
+		case DiffInsert:
+			_, _ = text.WriteString("+")
+		case DiffDelete:
+			_, _ = text.WriteString("-")
+		case DiffEqual:
+			_, _ = text.WriteString(" ")
+		}
+
+		_, _ = text.WriteString(strings.Replace(url.QueryEscape(aDiff.Text), "+", " ", -1))
+		_, _ = text.WriteString("\n")
+	}
+
+	return unescaper.Replace(text.String())
+}
+
+// PatchAddContext increases the context until it is unique,
+// but doesn't let the pattern expand beyond MatchMaxBits.
+func (dmp *DiffMatchPatch) PatchAddContext(patch Patch, text string) Patch {
+	if len(text) == 0 {
+		return patch
+	}
+
+	pattern := text[patch.start2 : patch.start2+patch.length1]
+	padding := 0
+
+	// Look for the first and last matches of pattern in text.  If two
+	// different matches are found, increase the pattern length.
+	for strings.Index(text, pattern) != strings.LastIndex(text, pattern) &&
+		len(pattern) < dmp.MatchMaxBits-2*dmp.PatchMargin {
+		padding += dmp.PatchMargin
+		maxStart := max(0, patch.start2-padding)
+		minEnd := min(len(text), patch.start2+patch.length1+padding)
+		pattern = text[maxStart:minEnd]
+	}
+	// Add one chunk for good luck.
+	padding += dmp.PatchMargin
+
+	// Add the prefix.
+	prefix := text[max(0, patch.start2-padding):patch.start2]
+	if len(prefix) != 0 {
+		patch.diffs = append([]Diff{Diff{DiffEqual, prefix}}, patch.diffs...)
+	}
+	// Add the suffix.
+	suffix := text[patch.start2+patch.length1 : min(len(text), patch.start2+patch.length1+padding)]
+	if len(suffix) != 0 {
+		patch.diffs = append(patch.diffs, Diff{DiffEqual, suffix})
+	}
+
+	// Roll back the start points.
+	patch.start1 -= len(prefix)
+	patch.start2 -= len(prefix)
+	// Extend the lengths.
+	patch.length1 += len(prefix) + len(suffix)
+	patch.length2 += len(prefix) + len(suffix)
+
+	return patch
+}
+
+// PatchMake computes a list of patches.
+func (dmp *DiffMatchPatch) PatchMake(opt ...interface{}) []Patch {
+	if len(opt) == 1 {
+		diffs, _ := opt[0].([]Diff)
+		text1 := dmp.DiffText1(diffs)
+		return dmp.PatchMake(text1, diffs)
+	} else if len(opt) == 2 {
+		text1 := opt[0].(string)
+		switch t := opt[1].(type) {
+		case string:
+			diffs := dmp.DiffMain(text1, t, true)
+			if len(diffs) > 2 {
+				diffs = dmp.DiffCleanupSemantic(diffs)
+				diffs = dmp.DiffCleanupEfficiency(diffs)
+			}
+			return dmp.PatchMake(text1, diffs)
+		case []Diff:
+			return dmp.patchMake2(text1, t)
+		}
+	} else if len(opt) == 3 {
+		return dmp.PatchMake(opt[0], opt[2])
+	}
+	return []Patch{}
+}
+
+// patchMake2 computes a list of patches to turn text1 into text2.
+// text2 is not provided, diffs are the delta between text1 and text2.
+func (dmp *DiffMatchPatch) patchMake2(text1 string, diffs []Diff) []Patch {
+	// Check for null inputs not needed since null can't be passed in C#.
+	patches := []Patch{}
+	if len(diffs) == 0 {
+		return patches // Get rid of the null case.
+	}
+
+	patch := Patch{}
+	charCount1 := 0 // Number of characters into the text1 string.
+	charCount2 := 0 // Number of characters into the text2 string.
+	// Start with text1 (prepatchText) and apply the diffs until we arrive at
+	// text2 (postpatchText). We recreate the patches one by one to determine
+	// context info.
+	prepatchText := text1
+	postpatchText := text1
+
+	for i, aDiff := range diffs {
+		if len(patch.diffs) == 0 && aDiff.Type != DiffEqual {
+			// A new patch starts here.
+			patch.start1 = charCount1
+			patch.start2 = charCount2
+		}
+
+		switch aDiff.Type {
+		case DiffInsert:
+			patch.diffs = append(patch.diffs, aDiff)
+			patch.length2 += len(aDiff.Text)
+			postpatchText = postpatchText[:charCount2] +
+				aDiff.Text + postpatchText[charCount2:]
+		case DiffDelete:
+			patch.length1 += len(aDiff.Text)
+			patch.diffs = append(patch.diffs, aDiff)
+			postpatchText = postpatchText[:charCount2] + postpatchText[charCount2+len(aDiff.Text):]
+		case DiffEqual:
+			if len(aDiff.Text) <= 2*dmp.PatchMargin &&
+				len(patch.diffs) != 0 && i != len(diffs)-1 {
+				// Small equality inside a patch.
+				patch.diffs = append(patch.diffs, aDiff)
+				patch.length1 += len(aDiff.Text)
+				patch.length2 += len(aDiff.Text)
+			}
+			if len(aDiff.Text) >= 2*dmp.PatchMargin {
+				// Time for a new patch.
+				if len(patch.diffs) != 0 {
+					patch = dmp.PatchAddContext(patch, prepatchText)
+					patches = append(patches, patch)
+					patch = Patch{}
+					// Unlike Unidiff, our patch lists have a rolling context.
+					// http://code.google.com/p/google-diff-match-patch/wiki/Unidiff
+					// Update prepatch text & pos to reflect the application of the
+					// just completed patch.
+					prepatchText = postpatchText
+					charCount1 = charCount2
+				}
+			}
+		}
+
+		// Update the current character count.
+		if aDiff.Type != DiffInsert {
+			charCount1 += len(aDiff.Text)
+		}
+		if aDiff.Type != DiffDelete {
+			charCount2 += len(aDiff.Text)
+		}
+	}
+
+	// Pick up the leftover patch if not empty.
+	if len(patch.diffs) != 0 {
+		patch = dmp.PatchAddContext(patch, prepatchText)
+		patches = append(patches, patch)
+	}
+
+	return patches
+}
+
+// PatchDeepCopy returns an array that is identical to a
+// given an array of patches.
+func (dmp *DiffMatchPatch) PatchDeepCopy(patches []Patch) []Patch {
+	patchesCopy := []Patch{}
+	for _, aPatch := range patches {
+		patchCopy := Patch{}
+		for _, aDiff := range aPatch.diffs {
+			patchCopy.diffs = append(patchCopy.diffs, Diff{
+				aDiff.Type,
+				aDiff.Text,
+			})
+		}
+		patchCopy.start1 = aPatch.start1
+		patchCopy.start2 = aPatch.start2
+		patchCopy.length1 = aPatch.length1
+		patchCopy.length2 = aPatch.length2
+		patchesCopy = append(patchesCopy, patchCopy)
+	}
+	return patchesCopy
+}
+
+// PatchApply merges a set of patches onto the text.  Returns a patched text, as well
+// as an array of true/false values indicating which patches were applied.
+func (dmp *DiffMatchPatch) PatchApply(patches []Patch, text string) (string, []bool) {
+	if len(patches) == 0 {
+		return text, []bool{}
+	}
+
+	// Deep copy the patches so that no changes are made to originals.
+	patches = dmp.PatchDeepCopy(patches)
+
+	nullPadding := dmp.PatchAddPadding(patches)
+	text = nullPadding + text + nullPadding
+	patches = dmp.PatchSplitMax(patches)
+
+	x := 0
+	// delta keeps track of the offset between the expected and actual
+	// location of the previous patch.  If there are patches expected at
+	// positions 10 and 20, but the first patch was found at 12, delta is 2
+	// and the second patch has an effective expected position of 22.
+	delta := 0
+	results := make([]bool, len(patches))
+	for _, aPatch := range patches {
+		expectedLoc := aPatch.start2 + delta
+		text1 := dmp.DiffText1(aPatch.diffs)
+		var startLoc int
+		endLoc := -1
+		if len(text1) > dmp.MatchMaxBits {
+			// PatchSplitMax will only provide an oversized pattern
+			// in the case of a monster delete.
+			startLoc = dmp.MatchMain(text, text1[:dmp.MatchMaxBits], expectedLoc)
+			if startLoc != -1 {
+				endLoc = dmp.MatchMain(text,
+					text1[len(text1)-dmp.MatchMaxBits:], expectedLoc+len(text1)-dmp.MatchMaxBits)
+				if endLoc == -1 || startLoc >= endLoc {
+					// Can't find valid trailing context.  Drop this patch.
+					startLoc = -1
+				}
+			}
+		} else {
+			startLoc = dmp.MatchMain(text, text1, expectedLoc)
+		}
+		if startLoc == -1 {
+			// No match found.  :(
+			results[x] = false
+			// Subtract the delta for this failed patch from subsequent patches.
+			delta -= aPatch.length2 - aPatch.length1
+		} else {
+			// Found a match.  :)
+			results[x] = true
+			delta = startLoc - expectedLoc
+			var text2 string
+			if endLoc == -1 {
+				text2 = text[startLoc:int(math.Min(float64(startLoc+len(text1)), float64(len(text))))]
+			} else {
+				text2 = text[startLoc:int(math.Min(float64(endLoc+dmp.MatchMaxBits), float64(len(text))))]
+			}
+			if text1 == text2 {
+				// Perfect match, just shove the Replacement text in.
+				text = text[:startLoc] + dmp.DiffText2(aPatch.diffs) + text[startLoc+len(text1):]
+			} else {
+				// Imperfect match.  Run a diff to get a framework of equivalent
+				// indices.
+				diffs := dmp.DiffMain(text1, text2, false)
+				if len(text1) > dmp.MatchMaxBits && float64(dmp.DiffLevenshtein(diffs))/float64(len(text1)) > dmp.PatchDeleteThreshold {
+					// The end points match, but the content is unacceptably bad.
+					results[x] = false
+				} else {
+					diffs = dmp.DiffCleanupSemanticLossless(diffs)
+					index1 := 0
+					for _, aDiff := range aPatch.diffs {
+						if aDiff.Type != DiffEqual {
+							index2 := dmp.DiffXIndex(diffs, index1)
+							if aDiff.Type == DiffInsert {
+								// Insertion
+								text = text[:startLoc+index2] + aDiff.Text + text[startLoc+index2:]
+							} else if aDiff.Type == DiffDelete {
+								// Deletion
+								startIndex := startLoc + index2
+								text = text[:startIndex] +
+									text[startIndex+dmp.DiffXIndex(diffs, index1+len(aDiff.Text))-index2:]
+							}
+						}
+						if aDiff.Type != DiffDelete {
+							index1 += len(aDiff.Text)
+						}
+					}
+				}
+			}
+		}
+		x++
+	}
+	// Strip the padding off.
+	text = text[len(nullPadding) : len(nullPadding)+(len(text)-2*len(nullPadding))]
+	return text, results
+}
+
+// PatchAddPadding adds some padding on text start and end so that edges can match something.
+// Intended to be called only from within patchApply.
+func (dmp *DiffMatchPatch) PatchAddPadding(patches []Patch) string {
+	paddingLength := dmp.PatchMargin
+	nullPadding := ""
+	for x := 1; x <= paddingLength; x++ {
+		nullPadding += string(x)
+	}
+
+	// Bump all the patches forward.
+	for i := range patches {
+		patches[i].start1 += paddingLength
+		patches[i].start2 += paddingLength
+	}
+
+	// Add some padding on start of first diff.
+	if len(patches[0].diffs) == 0 || patches[0].diffs[0].Type != DiffEqual {
+		// Add nullPadding equality.
+		patches[0].diffs = append([]Diff{Diff{DiffEqual, nullPadding}}, patches[0].diffs...)
+		patches[0].start1 -= paddingLength // Should be 0.
+		patches[0].start2 -= paddingLength // Should be 0.
+		patches[0].length1 += paddingLength
+		patches[0].length2 += paddingLength
+	} else if paddingLength > len(patches[0].diffs[0].Text) {
+		// Grow first equality.
+		extraLength := paddingLength - len(patches[0].diffs[0].Text)
+		patches[0].diffs[0].Text = nullPadding[len(patches[0].diffs[0].Text):] + patches[0].diffs[0].Text
+		patches[0].start1 -= extraLength
+		patches[0].start2 -= extraLength
+		patches[0].length1 += extraLength
+		patches[0].length2 += extraLength
+	}
+
+	// Add some padding on end of last diff.
+	last := len(patches) - 1
+	if len(patches[last].diffs) == 0 || patches[last].diffs[len(patches[last].diffs)-1].Type != DiffEqual {
+		// Add nullPadding equality.
+		patches[last].diffs = append(patches[last].diffs, Diff{DiffEqual, nullPadding})
+		patches[last].length1 += paddingLength
+		patches[last].length2 += paddingLength
+	} else if paddingLength > len(patches[last].diffs[len(patches[last].diffs)-1].Text) {
+		// Grow last equality.
+		lastDiff := patches[last].diffs[len(patches[last].diffs)-1]
+		extraLength := paddingLength - len(lastDiff.Text)
+		patches[last].diffs[len(patches[last].diffs)-1].Text += nullPadding[:extraLength]
+		patches[last].length1 += extraLength
+		patches[last].length2 += extraLength
+	}
+
+	return nullPadding
+}
+
+// PatchSplitMax looks through the patches and breaks up any which are longer than the
+// maximum limit of the match algorithm.
+// Intended to be called only from within patchApply.
+func (dmp *DiffMatchPatch) PatchSplitMax(patches []Patch) []Patch {
+	patchSize := dmp.MatchMaxBits
+	for x := 0; x < len(patches); x++ {
+		if patches[x].length1 <= patchSize {
+			continue
+		}
+		bigpatch := patches[x]
+		// Remove the big old patch.
+		patches = append(patches[:x], patches[x+1:]...)
+		x--
+
+		start1 := bigpatch.start1
+		start2 := bigpatch.start2
+		precontext := ""
+		for len(bigpatch.diffs) != 0 {
+			// Create one of several smaller patches.
+			patch := Patch{}
+			empty := true
+			patch.start1 = start1 - len(precontext)
+			patch.start2 = start2 - len(precontext)
+			if len(precontext) != 0 {
+				patch.length1 = len(precontext)
+				patch.length2 = len(precontext)
+				patch.diffs = append(patch.diffs, Diff{DiffEqual, precontext})
+			}
+			for len(bigpatch.diffs) != 0 && patch.length1 < patchSize-dmp.PatchMargin {
+				diffType := bigpatch.diffs[0].Type
+				diffText := bigpatch.diffs[0].Text
+				if diffType == DiffInsert {
+					// Insertions are harmless.
+					patch.length2 += len(diffText)
+					start2 += len(diffText)
+					patch.diffs = append(patch.diffs, bigpatch.diffs[0])
+					bigpatch.diffs = bigpatch.diffs[1:]
+					empty = false
+				} else if diffType == DiffDelete && len(patch.diffs) == 1 && patch.diffs[0].Type == DiffEqual && len(diffText) > 2*patchSize {
+					// This is a large deletion.  Let it pass in one chunk.
+					patch.length1 += len(diffText)
+					start1 += len(diffText)
+					empty = false
+					patch.diffs = append(patch.diffs, Diff{diffType, diffText})
+					bigpatch.diffs = bigpatch.diffs[1:]
+				} else {
+					// Deletion or equality.  Only take as much as we can stomach.
+					diffText = diffText[:min(len(diffText), patchSize-patch.length1-dmp.PatchMargin)]
+
+					patch.length1 += len(diffText)
+					start1 += len(diffText)
+					if diffType == DiffEqual {
+						patch.length2 += len(diffText)
+						start2 += len(diffText)
+					} else {
+						empty = false
+					}
+					patch.diffs = append(patch.diffs, Diff{diffType, diffText})
+					if diffText == bigpatch.diffs[0].Text {
+						bigpatch.diffs = bigpatch.diffs[1:]
+					} else {
+						bigpatch.diffs[0].Text =
+							bigpatch.diffs[0].Text[len(diffText):]
+					}
+				}
+			}
+			// Compute the head context for the next patch.
+			precontext = dmp.DiffText2(patch.diffs)
+			precontext = precontext[max(0, len(precontext)-dmp.PatchMargin):]
+
+			postcontext := ""
+			// Append the end context for this patch.
+			if len(dmp.DiffText1(bigpatch.diffs)) > dmp.PatchMargin {
+				postcontext = dmp.DiffText1(bigpatch.diffs)[:dmp.PatchMargin]
+			} else {
+				postcontext = dmp.DiffText1(bigpatch.diffs)
+			}
+
+			if len(postcontext) != 0 {
+				patch.length1 += len(postcontext)
+				patch.length2 += len(postcontext)
+				if len(patch.diffs) != 0 && patch.diffs[len(patch.diffs)-1].Type == DiffEqual {
+					patch.diffs[len(patch.diffs)-1].Text += postcontext
+				} else {
+					patch.diffs = append(patch.diffs, Diff{DiffEqual, postcontext})
+				}
+			}
+			if !empty {
+				x++
+				patches = append(patches[:x], append([]Patch{patch}, patches[x:]...)...)
+			}
+		}
+	}
+	return patches
+}
+
+// PatchToText takes a list of patches and returns a textual representation.
+func (dmp *DiffMatchPatch) PatchToText(patches []Patch) string {
+	var text bytes.Buffer
+	for _, aPatch := range patches {
+		_, _ = text.WriteString(aPatch.String())
+	}
+	return text.String()
+}
+
+// PatchFromText parses a textual representation of patches and returns a List of Patch
+// objects.
+func (dmp *DiffMatchPatch) PatchFromText(textline string) ([]Patch, error) {
+	patches := []Patch{}
+	if len(textline) == 0 {
+		return patches, nil
+	}
+	text := strings.Split(textline, "\n")
+	textPointer := 0
+	patchHeader := regexp.MustCompile("^@@ -(\\d+),?(\\d*) \\+(\\d+),?(\\d*) @@$")
+
+	var patch Patch
+	var sign uint8
+	var line string
+	for textPointer < len(text) {
+
+		if !patchHeader.MatchString(text[textPointer]) {
+			return patches, errors.New("Invalid patch string: " + text[textPointer])
+		}
+
+		patch = Patch{}
+		m := patchHeader.FindStringSubmatch(text[textPointer])
+
+		patch.start1, _ = strconv.Atoi(m[1])
+		if len(m[2]) == 0 {
+			patch.start1--
+			patch.length1 = 1
+		} else if m[2] == "0" {
+			patch.length1 = 0
+		} else {
+			patch.start1--
+			patch.length1, _ = strconv.Atoi(m[2])
+		}
+
+		patch.start2, _ = strconv.Atoi(m[3])
+
+		if len(m[4]) == 0 {
+			patch.start2--
+			patch.length2 = 1
+		} else if m[4] == "0" {
+			patch.length2 = 0
+		} else {
+			patch.start2--
+			patch.length2, _ = strconv.Atoi(m[4])
+		}
+		textPointer++
+
+		for textPointer < len(text) {
+			if len(text[textPointer]) > 0 {
+				sign = text[textPointer][0]
+			} else {
+				textPointer++
+				continue
+			}
+
+			line = text[textPointer][1:]
+			line = strings.Replace(line, "+", "%2b", -1)
+			line, _ = url.QueryUnescape(line)
+			if sign == '-' {
+				// Deletion.
+				patch.diffs = append(patch.diffs, Diff{DiffDelete, line})
+			} else if sign == '+' {
+				// Insertion.
+				patch.diffs = append(patch.diffs, Diff{DiffInsert, line})
+			} else if sign == ' ' {
+				// Minor equality.
+				patch.diffs = append(patch.diffs, Diff{DiffEqual, line})
+			} else if sign == '@' {
+				// Start of next patch.
+				break
+			} else {
+				// WTF?
+				return patches, errors.New("Invalid patch mode '" + string(sign) + "' in: " + string(line))
+			}
+			textPointer++
+		}
+
+		patches = append(patches, patch)
+	}
+	return patches, nil
+}
diff --git a/diffmatchpatch/patch_test.go b/diffmatchpatch/patch_test.go
new file mode 100644
index 0000000..12d1441
--- /dev/null
+++ b/diffmatchpatch/patch_test.go
@@ -0,0 +1,334 @@
+// Copyright (c) 2012-2016 The go-diff authors. All rights reserved.
+// https://github.com/sergi/go-diff
+// See the included LICENSE file for license details.
+//
+// go-diff is a Go implementation of Google's Diff, Match, and Patch library
+// Original library is Copyright (c) 2006 Google Inc.
+// http://code.google.com/p/google-diff-match-patch/
+
+package diffmatchpatch
+
+import (
+	"fmt"
+	"strings"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestPatchString(t *testing.T) {
+	type TestCase struct {
+		Patch Patch
+
+		Expected string
+	}
+
+	for i, tc := range []TestCase{
+		{
+			Patch: Patch{
+				start1:  20,
+				start2:  21,
+				length1: 18,
+				length2: 17,
+
+				diffs: []Diff{
+					{DiffEqual, "jump"},
+					{DiffDelete, "s"},
+					{DiffInsert, "ed"},
+					{DiffEqual, " over "},
+					{DiffDelete, "the"},
+					{DiffInsert, "a"},
+					{DiffEqual, "\nlaz"},
+				},
+			},
+
+			Expected: "@@ -21,18 +22,17 @@\n jump\n-s\n+ed\n  over \n-the\n+a\n %0Alaz\n",
+		},
+	} {
+		actual := tc.Patch.String()
+		assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc))
+	}
+}
+
+func TestPatchFromText(t *testing.T) {
+	type TestCase struct {
+		Patch string
+
+		ErrorMessagePrefix string
+	}
+
+	dmp := New()
+
+	for i, tc := range []TestCase{
+		{"", ""},
+		{"@@ -21,18 +22,17 @@\n jump\n-s\n+ed\n  over \n-the\n+a\n %0Alaz\n", ""},
+		{"@@ -1 +1 @@\n-a\n+b\n", ""},
+		{"@@ -1,3 +0,0 @@\n-abc\n", ""},
+		{"@@ -0,0 +1,3 @@\n+abc\n", ""},
+		{"Bad\nPatch\n", "Invalid patch string"},
+	} {
+		patches, err := dmp.PatchFromText(tc.Patch)
+		if tc.ErrorMessagePrefix == "" {
+			assert.Nil(t, err)
+
+			if tc.Patch == "" {
+				assert.Equal(t, []Patch{}, patches, fmt.Sprintf("Test case #%d, %#v", i, tc))
+			} else {
+				assert.Equal(t, tc.Patch, patches[0].String(), fmt.Sprintf("Test case #%d, %#v", i, tc))
+			}
+		} else {
+			e := err.Error()
+			if strings.HasPrefix(e, tc.ErrorMessagePrefix) {
+				e = tc.ErrorMessagePrefix
+			}
+			assert.Equal(t, tc.ErrorMessagePrefix, e)
+		}
+	}
+
+	diffs := []Diff{
+		{DiffDelete, "`1234567890-=[]\\;',./"},
+		{DiffInsert, "~!@#$%^&*()_+{}|:\"<>?"},
+	}
+
+	patches, err := dmp.PatchFromText("@@ -1,21 +1,21 @@\n-%601234567890-=%5B%5D%5C;',./\n+~!@#$%25%5E&*()_+%7B%7D%7C:%22%3C%3E?\n")
+	assert.Len(t, patches, 1)
+	assert.Equal(t, diffs,
+		patches[0].diffs,
+	)
+	assert.Nil(t, err)
+}
+
+func TestPatchToText(t *testing.T) {
+	type TestCase struct {
+		Patch string
+	}
+
+	dmp := New()
+
+	for i, tc := range []TestCase{
+		{"@@ -21,18 +22,17 @@\n jump\n-s\n+ed\n  over \n-the\n+a\n  laz\n"},
+		{"@@ -1,9 +1,9 @@\n-f\n+F\n oo+fooba\n@@ -7,9 +7,9 @@\n obar\n-,\n+.\n  tes\n"},
+	} {
+		patches, err := dmp.PatchFromText(tc.Patch)
+		assert.Nil(t, err)
+
+		actual := dmp.PatchToText(patches)
+		assert.Equal(t, tc.Patch, actual, fmt.Sprintf("Test case #%d, %#v", i, tc))
+	}
+}
+
+func TestPatchAddContext(t *testing.T) {
+	type TestCase struct {
+		Name string
+
+		Patch string
+		Text  string
+
+		Expected string
+	}
+
+	dmp := New()
+	dmp.PatchMargin = 4
+
+	for i, tc := range []TestCase{
+		{"Simple case", "@@ -21,4 +21,10 @@\n-jump\n+somersault\n", "The quick brown fox jumps over the lazy dog.", "@@ -17,12 +17,18 @@\n fox \n-jump\n+somersault\n s ov\n"},
+		{"Not enough trailing context", "@@ -21,4 +21,10 @@\n-jump\n+somersault\n", "The quick brown fox jumps.", "@@ -17,10 +17,16 @@\n fox \n-jump\n+somersault\n s.\n"},
+		{"Not enough leading context", "@@ -3 +3,2 @@\n-e\n+at\n", "The quick brown fox jumps.", "@@ -1,7 +1,8 @@\n Th\n-e\n+at\n  qui\n"},
+		{"Ambiguity", "@@ -3 +3,2 @@\n-e\n+at\n", "The quick brown fox jumps.  The quick brown fox crashes.", "@@ -1,27 +1,28 @@\n Th\n-e\n+at\n  quick brown fox jumps. \n"},
+	} {
+		patches, err := dmp.PatchFromText(tc.Patch)
+		assert.Nil(t, err)
+
+		actual := dmp.PatchAddContext(patches[0], tc.Text)
+		assert.Equal(t, tc.Expected, actual.String(), fmt.Sprintf("Test case #%d, %s", i, tc.Name))
+	}
+}
+
+func TestPatchMakeAndPatchToText(t *testing.T) {
+	type TestCase struct {
+		Name string
+
+		Input1 interface{}
+		Input2 interface{}
+		Input3 interface{}
+
+		Expected string
+	}
+
+	dmp := New()
+
+	text1 := "The quick brown fox jumps over the lazy dog."
+	text2 := "That quick brown fox jumped over a lazy dog."
+
+	for i, tc := range []TestCase{
+		{"Null case", "", "", nil, ""},
+		{"Text2+Text1 inputs", text2, text1, nil, "@@ -1,8 +1,7 @@\n Th\n-at\n+e\n  qui\n@@ -21,17 +21,18 @@\n jump\n-ed\n+s\n  over \n-a\n+the\n  laz\n"},
+		{"Text1+Text2 inputs", text1, text2, nil, "@@ -1,11 +1,12 @@\n Th\n-e\n+at\n  quick b\n@@ -22,18 +22,17 @@\n jump\n-s\n+ed\n  over \n-the\n+a\n  laz\n"},
+		{"Diff input", dmp.DiffMain(text1, text2, false), nil, nil, "@@ -1,11 +1,12 @@\n Th\n-e\n+at\n  quick b\n@@ -22,18 +22,17 @@\n jump\n-s\n+ed\n  over \n-the\n+a\n  laz\n"},
+		{"Text1+Diff inputs", text1, dmp.DiffMain(text1, text2, false), nil, "@@ -1,11 +1,12 @@\n Th\n-e\n+at\n  quick b\n@@ -22,18 +22,17 @@\n jump\n-s\n+ed\n  over \n-the\n+a\n  laz\n"},
+		{"Text1+Text2+Diff inputs (deprecated)", text1, text2, dmp.DiffMain(text1, text2, false), "@@ -1,11 +1,12 @@\n Th\n-e\n+at\n  quick b\n@@ -22,18 +22,17 @@\n jump\n-s\n+ed\n  over \n-the\n+a\n  laz\n"},
+		{"Character encoding", "`1234567890-=[]\\;',./", "~!@#$%^&*()_+{}|:\"<>?", nil, "@@ -1,21 +1,21 @@\n-%601234567890-=%5B%5D%5C;',./\n+~!@#$%25%5E&*()_+%7B%7D%7C:%22%3C%3E?\n"},
+		{"Long string with repeats", strings.Repeat("abcdef", 100), strings.Repeat("abcdef", 100) + "123", nil, "@@ -573,28 +573,31 @@\n cdefabcdefabcdefabcdefabcdef\n+123\n"},
+		{"Corner case of #31 fixed by #32", "2016-09-01T03:07:14.807830741Z", "2016-09-01T03:07:15.154800781Z", nil, "@@ -15,16 +15,16 @@\n 07:1\n+5.15\n 4\n-.\n 80\n+0\n 78\n-3074\n 1Z\n"},
+	} {
+		var patches []Patch
+		if tc.Input3 != nil {
+			patches = dmp.PatchMake(tc.Input1, tc.Input2, tc.Input3)
+		} else if tc.Input2 != nil {
+			patches = dmp.PatchMake(tc.Input1, tc.Input2)
+		} else if ps, ok := tc.Input1.([]Patch); ok {
+			patches = ps
+		} else {
+			patches = dmp.PatchMake(tc.Input1)
+		}
+
+		actual := dmp.PatchToText(patches)
+		assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name))
+	}
+
+	// Corner case of #28 wrong patch with timeout of 0
+	dmp.DiffTimeout = 0
+
+	text1 = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus ut risus et enim consectetur convallis a non ipsum. Sed nec nibh cursus, interdum libero vel."
+	text2 = "Lorem a ipsum dolor sit amet, consectetur adipiscing elit. Vivamus ut risus et enim consectetur convallis a non ipsum. Sed nec nibh cursus, interdum liberovel."
+
+	diffs := dmp.DiffMain(text1, text2, true)
+	// Additional check that the diff texts are equal to the originals even if we are using DiffMain with checklines=true #29
+	assert.Equal(t, text1, dmp.DiffText1(diffs))
+	assert.Equal(t, text2, dmp.DiffText2(diffs))
+
+	patches := dmp.PatchMake(text1, diffs)
+
+	actual := dmp.PatchToText(patches)
+	assert.Equal(t, "@@ -1,14 +1,16 @@\n Lorem \n+a \n ipsum do\n@@ -148,13 +148,12 @@\n m libero\n- \n vel.\n", actual)
+}
+
+func TestPatchSplitMax(t *testing.T) {
+	type TestCase struct {
+		Text1 string
+		Text2 string
+
+		Expected string
+	}
+
+	dmp := New()
+
+	for i, tc := range []TestCase{
+		{"abcdefghijklmnopqrstuvwxyz01234567890", "XabXcdXefXghXijXklXmnXopXqrXstXuvXwxXyzX01X23X45X67X89X0", "@@ -1,32 +1,46 @@\n+X\n ab\n+X\n cd\n+X\n ef\n+X\n gh\n+X\n ij\n+X\n kl\n+X\n mn\n+X\n op\n+X\n qr\n+X\n st\n+X\n uv\n+X\n wx\n+X\n yz\n+X\n 012345\n@@ -25,13 +39,18 @@\n zX01\n+X\n 23\n+X\n 45\n+X\n 67\n+X\n 89\n+X\n 0\n"},
+		{"abcdef1234567890123456789012345678901234567890123456789012345678901234567890uvwxyz", "abcdefuvwxyz", "@@ -3,78 +3,8 @@\n cdef\n-1234567890123456789012345678901234567890123456789012345678901234567890\n uvwx\n"},
+		{"1234567890123456789012345678901234567890123456789012345678901234567890", "abc", "@@ -1,32 +1,4 @@\n-1234567890123456789012345678\n 9012\n@@ -29,32 +1,4 @@\n-9012345678901234567890123456\n 7890\n@@ -57,14 +1,3 @@\n-78901234567890\n+abc\n"},
+		{"abcdefghij , h : 0 , t : 1 abcdefghij , h : 0 , t : 1 abcdefghij , h : 0 , t : 1", "abcdefghij , h : 1 , t : 1 abcdefghij , h : 1 , t : 1 abcdefghij , h : 0 , t : 1", "@@ -2,32 +2,32 @@\n bcdefghij , h : \n-0\n+1\n  , t : 1 abcdef\n@@ -29,32 +29,32 @@\n bcdefghij , h : \n-0\n+1\n  , t : 1 abcdef\n"},
+	} {
+		patches := dmp.PatchMake(tc.Text1, tc.Text2)
+		patches = dmp.PatchSplitMax(patches)
+
+		actual := dmp.PatchToText(patches)
+		assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc))
+	}
+}
+
+func TestPatchAddPadding(t *testing.T) {
+	type TestCase struct {
+		Name string
+
+		Text1 string
+		Text2 string
+
+		Expected            string
+		ExpectedWithPadding string
+	}
+
+	dmp := New()
+
+	for i, tc := range []TestCase{
+		{"Both edges full", "", "test", "@@ -0,0 +1,4 @@\n+test\n", "@@ -1,8 +1,12 @@\n %01%02%03%04\n+test\n %01%02%03%04\n"},
+		{"Both edges partial", "XY", "XtestY", "@@ -1,2 +1,6 @@\n X\n+test\n Y\n", "@@ -2,8 +2,12 @@\n %02%03%04X\n+test\n Y%01%02%03\n"},
+		{"Both edges none", "XXXXYYYY", "XXXXtestYYYY", "@@ -1,8 +1,12 @@\n XXXX\n+test\n YYYY\n", "@@ -5,8 +5,12 @@\n XXXX\n+test\n YYYY\n"},
+	} {
+		patches := dmp.PatchMake(tc.Text1, tc.Text2)
+
+		actual := dmp.PatchToText(patches)
+		assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name))
+
+		dmp.PatchAddPadding(patches)
+
+		actualWithPadding := dmp.PatchToText(patches)
+		assert.Equal(t, tc.ExpectedWithPadding, actualWithPadding, fmt.Sprintf("Test case #%d, %s", i, tc.Name))
+	}
+}
+
+func TestPatchApply(t *testing.T) {
+	type TestCase struct {
+		Name string
+
+		Text1    string
+		Text2    string
+		TextBase string
+
+		Expected        string
+		ExpectedApplies []bool
+	}
+
+	dmp := New()
+	dmp.MatchDistance = 1000
+	dmp.MatchThreshold = 0.5
+	dmp.PatchDeleteThreshold = 0.5
+
+	for i, tc := range []TestCase{
+		{"Null case", "", "", "Hello world.", "Hello world.", []bool{}},
+		{"Exact match", "The quick brown fox jumps over the lazy dog.", "That quick brown fox jumped over a lazy dog.", "The quick brown fox jumps over the lazy dog.", "That quick brown fox jumped over a lazy dog.", []bool{true, true}},
+		{"Partial match", "The quick brown fox jumps over the lazy dog.", "That quick brown fox jumped over a lazy dog.", "The quick red rabbit jumps over the tired tiger.", "That quick red rabbit jumped over a tired tiger.", []bool{true, true}},
+		{"Failed match", "The quick brown fox jumps over the lazy dog.", "That quick brown fox jumped over a lazy dog.", "I am the very model of a modern major general.", "I am the very model of a modern major general.", []bool{false, false}},
+		{"Big delete, small Diff", "x1234567890123456789012345678901234567890123456789012345678901234567890y", "xabcy", "x123456789012345678901234567890-----++++++++++-----123456789012345678901234567890y", "xabcy", []bool{true, true}},
+		{"Big delete, big Diff 1", "x1234567890123456789012345678901234567890123456789012345678901234567890y", "xabcy", "x12345678901234567890---------------++++++++++---------------12345678901234567890y", "xabc12345678901234567890---------------++++++++++---------------12345678901234567890y", []bool{false, true}},
+	} {
+		patches := dmp.PatchMake(tc.Text1, tc.Text2)
+
+		actual, actualApplies := dmp.PatchApply(patches, tc.TextBase)
+		assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name))
+		assert.Equal(t, tc.ExpectedApplies, actualApplies, fmt.Sprintf("Test case #%d, %s", i, tc.Name))
+	}
+
+	dmp.PatchDeleteThreshold = 0.6
+
+	for i, tc := range []TestCase{
+		{"Big delete, big Diff 2", "x1234567890123456789012345678901234567890123456789012345678901234567890y", "xabcy", "x12345678901234567890---------------++++++++++---------------12345678901234567890y", "xabcy", []bool{true, true}},
+	} {
+		patches := dmp.PatchMake(tc.Text1, tc.Text2)
+
+		actual, actualApplies := dmp.PatchApply(patches, tc.TextBase)
+		assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name))
+		assert.Equal(t, tc.ExpectedApplies, actualApplies, fmt.Sprintf("Test case #%d, %s", i, tc.Name))
+	}
+
+	dmp.MatchDistance = 0
+	dmp.MatchThreshold = 0.0
+	dmp.PatchDeleteThreshold = 0.5
+
+	for i, tc := range []TestCase{
+		{"Compensate for failed patch", "abcdefghijklmnopqrstuvwxyz--------------------1234567890", "abcXXXXXXXXXXdefghijklmnopqrstuvwxyz--------------------1234567YYYYYYYYYY890", "ABCDEFGHIJKLMNOPQRSTUVWXYZ--------------------1234567890", "ABCDEFGHIJKLMNOPQRSTUVWXYZ--------------------1234567YYYYYYYYYY890", []bool{false, true}},
+	} {
+		patches := dmp.PatchMake(tc.Text1, tc.Text2)
+
+		actual, actualApplies := dmp.PatchApply(patches, tc.TextBase)
+		assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name))
+		assert.Equal(t, tc.ExpectedApplies, actualApplies, fmt.Sprintf("Test case #%d, %s", i, tc.Name))
+	}
+
+	dmp.MatchThreshold = 0.5
+	dmp.MatchDistance = 1000
+
+	for i, tc := range []TestCase{
+		{"No side effects", "", "test", "", "test", []bool{true}},
+		{"No side effects with major delete", "The quick brown fox jumps over the lazy dog.", "Woof", "The quick brown fox jumps over the lazy dog.", "Woof", []bool{true, true}},
+		{"Edge exact match", "", "test", "", "test", []bool{true}},
+		{"Near edge exact match", "XY", "XtestY", "XY", "XtestY", []bool{true}},
+		{"Edge partial match", "y", "y123", "x", "x123", []bool{true}},
+	} {
+		patches := dmp.PatchMake(tc.Text1, tc.Text2)
+
+		actual, actualApplies := dmp.PatchApply(patches, tc.TextBase)
+		assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name))
+		assert.Equal(t, tc.ExpectedApplies, actualApplies, fmt.Sprintf("Test case #%d, %s", i, tc.Name))
+	}
+}