text:
#import "DiffMatchPatch.h"
#import "DiffMatchPatchInternals.h"

// Object Classes
#import "DMDiff.h"
#import "DMPatch.h"

// Text parsing and conversion
#import "DiffMatchPatchCFUtilities.h"
#import "NSString+UriCompatibility.h"
#import "NSString+EscapeHTMLCharacters.h"

#pragma mark -
#pragma mark Helpers which define the default properties

DiffProperties diff_defaultDiffProperties()
{
	DiffProperties diffProperties;
	diffProperties.checkLines = FALSE;		// Perform a slower, more accurate diff
	diffProperties.deadline = 0.0;			// No timeout
	return diffProperties;
}

MatchProperties match_defaultMatchProperties()
{
	MatchProperties properties;
	properties.matchThreshold = 0.5f;
	properties.matchDistance = 1000;
	properties.matchMaximumBits = 32;
	return properties;
}

PatchProperties patch_defaultPatchProperties()
{
	PatchProperties properties;
	properties.diffProperties = diff_defaultDiffProperties();
	properties.matchProperties = match_defaultMatchProperties();

	properties.diffEditingCost = 4;
	properties.patchDeleteThreshold = 0.5f;
	properties.patchMargin = 4;

	return properties;
}

#pragma mark -
#pragma mark Diff Functions

// Described in DiffMatchPatch.h
NSArray *diff_diffsBetweenTexts(NSString *text1, NSString *text2)
{
	DiffProperties properties = diff_defaultDiffProperties();
	return diff_diffsBetweenTextsWithProperties(text1, text2, properties);
}

// Described in DiffMatchPatch.h
NSArray *diff_diffsBetweenTextsWithOptions(NSString *text1, NSString *text2, BOOL highQuality, NSTimeInterval timeLimit)
{
	timeLimit = MAX(0.0, timeLimit);
	if(timeLimit > 0.0) {
		timeLimit = [NSDate timeIntervalSinceReferenceDate] + timeLimit;
	}

	DiffProperties properties = diff_defaultDiffProperties();
	properties.checkLines = !highQuality;
	properties.deadline = timeLimit;

	return diff_diffsBetweenTextsWithProperties(text1, text2, properties);
}

/**
 * Find the differences between two texts.  Simplifies the problem by
 * stripping any common prefix or suffix off the texts before diffing.
 *
 * @param text1			Old NSString to be diffed.
 * @param text2			New NSString to be diffed.
 * @param properties	See the DiffProperties struct for settings
 * @return NSMutableArray of DMDiff objects.
 */

NSMutableArray *diff_diffsBetweenTextsWithProperties(NSString *text1, NSString *text2, DiffProperties properties)
{
	// Check for null inputs.
	if(text1 == nil || text2 == nil) {
		NSLog(@"Null inputs. (diff_diffsBetweenTextsWithProperties)");
		return nil;
	}

	// Test if the deadline is zero or has already passed
	if(fabs(properties.deadline) < 0.00000001) {
		properties.deadline = [[NSDate distantFuture] timeIntervalSinceReferenceDate];
	} else {
		NSTimeInterval currentTime = [NSDate timeIntervalSinceReferenceDate];

		if(properties.deadline < currentTime) {
			// The deadline has already passed so use a fairly tight deadline
			properties.deadline = currentTime + 0.3f;	// 300 milliseconds
		}
	}

	// Check for equality (speedup).
	NSMutableArray *diffs;

	if([text1 isEqualToString:text2]) {
		diffs = [NSMutableArray array];

		if(text1.length != 0) {
			[diffs addObject:[DMDiff diffWithOperation:DIFF_EQUAL andText:text1]];
		}

		return diffs;
	}

	// Trim off common prefix (speedup).
	NSUInteger commonlength = (NSUInteger)diff_commonPrefix((__bridge CFStringRef)text1, (__bridge CFStringRef)text2);
	NSString *commonprefix = [text1 substringToIndex:commonlength];
	text1 = [text1 substringFromIndex:commonlength];
	text2 = [text2 substringFromIndex:commonlength];

	// Trim off common suffix (speedup).
	commonlength = (NSUInteger)diff_commonSuffix((__bridge CFStringRef)text1, (__bridge CFStringRef)text2);
	NSString *commonsuffix = [text1 substringFromIndex:text1.length - commonlength];
	text1 = [text1 substringToIndex:(text1.length - commonlength)];
	text2 = [text2 substringToIndex:(text2.length - commonlength)];

	// Compute the diff on the middle block.
	diffs = diff_computeDiffsBetweenTexts(text1, text2, properties);

	// Restore the prefix and suffix.
	if(commonprefix.length != 0) {
		[diffs insertObject:[DMDiff diffWithOperation:DIFF_EQUAL andText:commonprefix] atIndex:0];
	}

	if(commonsuffix.length != 0) {
		[diffs addObject:[DMDiff diffWithOperation:DIFF_EQUAL andText:commonsuffix]];
	}

	diff_cleanupMerge(&diffs);
	return diffs;
}

/**
 * Compute the differences between two texts. Assumes that the texts do not
 * have any common prefix or suffix.
 *
 * @param text1 Old NSString to be diffed.
 * @param text2 New NSString to be diffed.
 * @param checklines Speedup flag.  If NO, then don't run a
 *     line-level diff first to identify the changed areas.
 *     If YES, then run a faster slightly less optimal diff.
 * @param deadline Time the diff should be complete by.
 * @return NSMutableArray of Diff objects.
 */

NSMutableArray *diff_computeDiffsBetweenTexts(NSString *text1, NSString *text2, DiffProperties properties) {
	NSMutableArray *diffs = [[NSMutableArray alloc] init];

	if(text1.length == 0) {
		// Just add some text (speedup).
		[diffs addObject:[DMDiff diffWithOperation:DIFF_INSERT andText:text2]];
		return diffs;
	}

	if(text2.length == 0) {
		// Just delete some text (speedup).
		[diffs addObject:[DMDiff diffWithOperation:DIFF_DELETE andText:text1]];
		return diffs;
	}

	NSString *longtext = text1.length > text2.length ? text1 : text2;
	NSString *shorttext = text1.length > text2.length ? text2 : text1;
	NSUInteger i = [longtext rangeOfString:shorttext].location;

	if(i != NSNotFound) {
		// Shorter text is inside the longer text (speedup).
		DMDiffOperation op = (text1.length > text2.length) ? DIFF_DELETE : DIFF_INSERT;
		[diffs addObject:[DMDiff diffWithOperation:op andText:[longtext substringToIndex:i]]];
		[diffs addObject:[DMDiff diffWithOperation:DIFF_EQUAL andText:shorttext]];
		[diffs addObject:[DMDiff diffWithOperation:op andText:[longtext substringFromIndex:(i + shorttext.length)]]];
		return diffs;
	}

	if(shorttext.length == 1) {
		// Single character string.
		// After the previous speedup, the character can't be an equality.
		[diffs addObject:[DMDiff diffWithOperation:DIFF_DELETE andText:text1]];
		[diffs addObject:[DMDiff diffWithOperation:DIFF_INSERT andText:text2]];
		return diffs;
	}

	// Check to see if the problem can be split in two.
	NSArray *hm = nil;

	// Only risk returning a non-optimal diff if we have limited time.
	if(properties.deadline != [[NSDate distantFuture] timeIntervalSinceReferenceDate]) {
		hm = (__bridge_transfer NSArray *)diff_halfMatchCreate((__bridge CFStringRef)text1, (__bridge CFStringRef)text2);
	}

	if(hm != nil) {
		@autoreleasepool {
			// A half-match was found, sort out the return data.
			NSString *text1_a = hm[0];
			NSString *text1_b = hm[1];
			NSString *text2_a = hm[2];
			NSString *text2_b = hm[3];
			NSString *mid_common = hm[4];

			// Send both pairs off for separate processing.
			NSMutableArray *diffs_a = diff_diffsBetweenTextsWithProperties(text1_a, text2_a, properties);
			NSMutableArray *diffs_b = diff_diffsBetweenTextsWithProperties(text1_b, text2_b, properties);

			// Merge the results.
			[diffs_a addObject:[DMDiff diffWithOperation:DIFF_EQUAL andText:mid_common]];
			[diffs_a addObjectsFromArray:diffs_b];

			diffs = diffs_a;
		}

		return diffs;
	}

	if(properties.checkLines && text1.length > 100 && text2.length > 100) {
		return diff_computeDiffsUsingLineMode(text1, text2, properties);
	}

	return diff_bisectOfStrings(text1, text2, properties);
}

/**
 * Do a quick line-level diff on both strings, then rediff the parts for
 * greater accuracy.
 * This speedup can produce non-minimal diffs.
 * @param text1 Old NSString to be diffed.
 * @param text2 New NSString to be diffed.
 * @param deadline Time when the diff should be complete by.
 * @return NSMutableArray of Diff objects.
 */

NSMutableArray *diff_computeDiffsUsingLineMode(NSString *text1, NSString *text2, DiffProperties properties)
{
	DiffProperties nextDiffProperties = properties;
	nextDiffProperties.checkLines = FALSE;

	// Scan the text on a line-by-line basis first.
	NSArray *b = diff_linesToCharsForStrings(text1, text2);
	text1 = (NSString *)b[0];
	text2 = (NSString *)b[1];
	NSMutableArray *linearray = (NSMutableArray *)b[2];

	NSMutableArray *diffs = diff_diffsBetweenTextsWithProperties(text1, text2, nextDiffProperties);

	// Convert the diff back to original text.
	diff_charsToLines(&diffs, linearray);

	// Eliminate freak matches (e.g. blank lines)
	diff_cleanupSemantic(&diffs);

	// Rediff any Replacement blocks, this time character-by-character.
	// Add a dummy entry at the end.
	[diffs addObject:[DMDiff diffWithOperation:DIFF_EQUAL andText:@""]];
	NSUInteger indexOfCurrentDiff = 0;
	NSUInteger count_delete = 0;
	NSUInteger count_insert = 0;
	NSString *text_delete = @"";
	NSString *text_insert = @"";

	while(indexOfCurrentDiff < diffs.count) {
		switch(((DMDiff *)diffs[indexOfCurrentDiff]).operation) {
			case DIFF_INSERT:
				count_insert++;
				text_insert = [text_insert stringByAppendingString:((DMDiff *)diffs[indexOfCurrentDiff]).text];
				break;

			case DIFF_DELETE:
				count_delete++;
				text_delete = [text_delete stringByAppendingString:((DMDiff *)diffs[indexOfCurrentDiff]).text];
				break;

			case DIFF_EQUAL:
				// Upon reaching an equality, check for prior redundancies.
				if(count_delete >= 1 && count_insert >= 1) {
					// Delete the offending records and add the merged ones.
					NSMutableArray *a = diff_diffsBetweenTextsWithProperties(text_delete, text_insert, nextDiffProperties);
					[diffs removeObjectsInRange:NSMakeRange(indexOfCurrentDiff - count_delete - count_insert, count_delete + count_insert)];
					indexOfCurrentDiff = indexOfCurrentDiff - count_delete - count_insert;
					NSUInteger insertionIndex = indexOfCurrentDiff;

					for(DMDiff *thisDiff in a) {
						[diffs insertObject:thisDiff atIndex:insertionIndex];
						insertionIndex++;
					}

					indexOfCurrentDiff = indexOfCurrentDiff + a.count;
				}

				count_insert = 0;
				count_delete = 0;
				text_delete = @"";
				text_insert = @"";
				break;
		}

		indexOfCurrentDiff++;
	}

	[diffs removeLastObject];                   // Remove the dummy entry at the end.

	return diffs;
}

/**
 * Find the 'middle snake' of a diff, split the problem in two and return the recursively constructed diff.
 * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations.
 * @param text1 Old string to be diffed.
 * @param text2 New string to be diffed.
 * @param deadline Time at which to bail if not yet complete.
 * @return NSMutableArray of Diff objects.
 */

NSMutableArray *diff_bisectOfStrings(NSString *text1, NSString *text2, DiffProperties properties)
{
	BOOL validDeadline = properties.deadline != [[NSDate distantFuture] timeIntervalSinceReferenceDate];

	NSMutableArray *diffs = nil;
	BOOL haveFoundDiffs = FALSE;

	CFStringRef _text1 = (__bridge CFStringRef)text1;
	CFStringRef _text2 = (__bridge CFStringRef)text2;

	// Cache the text lengths to prevent multiple calls.
	CFIndex text1_length = CFStringGetLength(_text1);
	CFIndex text2_length = CFStringGetLength(_text2);
	CFIndex max_d = (text1_length + text2_length + 1) / 2;
	CFIndex v_offset = max_d;
	CFIndex v_length = 2 * max_d;
	CFIndex *v1 = malloc(v_length * sizeof(CFIndex));
	CFIndex *v2 = malloc(v_length * sizeof(CFIndex));

	for(CFIndex x = 0; x < v_length; x++) {
		v1[x] = -1;
		v2[x] = -1;
	}

	v1[v_offset + 1] = 0;
	v2[v_offset + 1] = 0;
	CFIndex delta = text1_length - text2_length;

	// Prepare access to chars arrays for text1 (massive speedup).
	const UniChar *text1_chars;
	UniChar *text1_buffer = NULL;
	diff_CFStringPrepareUniCharBuffer(_text1, &text1_chars, &text1_buffer, CFRangeMake(0, text1_length));

	// Prepare access to chars arrays for text2 (massive speedup).
	const UniChar *text2_chars;
	UniChar *text2_buffer = NULL;
	diff_CFStringPrepareUniCharBuffer(_text2, &text2_chars, &text2_buffer, CFRangeMake(0, text2_length));

	// If the total number of characters is odd, then the front path will collide with the reverse path.
	BOOL front = (delta % 2 != 0);

	// Offsets for start and end of k loop. Prevents mapping of space beyond the grid.
	CFIndex k1start = 0;
	CFIndex k1end = 0;
	CFIndex k2start = 0;
	CFIndex k2end = 0;

	for(CFIndex d = 0; d < max_d; d++) {
		// Bail out if deadline is reached.
		if(validDeadline && ([NSDate timeIntervalSinceReferenceDate] > properties.deadline)) {
			break;
		}

		// Walk the front path one step.
		for(CFIndex k1 = -d + k1start; k1 <= d - k1end; k1 += 2) {
			CFIndex k1_offset = v_offset + k1;
			CFIndex x1;

			if(k1 == -d || (k1 != d && v1[k1_offset - 1] < v1[k1_offset + 1])) {
				x1 = v1[k1_offset + 1];
			} else {
				x1 = v1[k1_offset - 1] + 1;
			}

			CFIndex y1 = x1 - k1;

			while(x1 < text1_length && y1 < text2_length && text1_chars[x1] == text2_chars[y1]) {
				x1++;
				y1++;
			}

			v1[k1_offset] = x1;

			if(x1 > text1_length) {
				// Ran off the right of the graph.
				k1end += 2;
			} else if(y1 > text2_length) {
				// Ran off the bottom of the graph.
				k1start += 2;
			} else if(front) {
				CFIndex k2_offset = v_offset + delta - k1;

				if(k2_offset >= 0 && k2_offset < v_length && v2[k2_offset] != -1) {
					// Mirror x2 onto top-left coordinate system.
					CFIndex x2 = text1_length - v2[k2_offset];

					if(x1 >= x2) {
						// Overlap detected.
						diffs = diff_bisectSplitOfStrings(text1, text2, x1, y1, properties);
						haveFoundDiffs = TRUE;
						break;
					}
				}
			}
		}

		if(haveFoundDiffs) {
			break;
		}

		// Walk the reverse path one step.
		for(CFIndex k2 = -d + k2start; k2 <= d - k2end; k2 += 2) {
			CFIndex k2_offset = v_offset + k2;
			CFIndex x2;

			if(k2 == -d || (k2 != d && v2[k2_offset - 1] < v2[k2_offset + 1])) {
				x2 = v2[k2_offset + 1];
			} else {
				x2 = v2[k2_offset - 1] + 1;
			}

			CFIndex y2 = x2 - k2;

			while((x2 < text1_length && y2 < text2_length) && (text1_chars[text1_length - x2 - 1] == text2_chars[text2_length - y2 - 1])) {
				x2++;
				y2++;
			}

			v2[k2_offset] = x2;

			if(x2 > text1_length) {
				// Ran off the left of the graph.
				k2end += 2;
			} else if(y2 > text2_length) {
				// Ran off the top of the graph.
				k2start += 2;
			} else if(!front) {
				CFIndex k1_offset = v_offset + delta - k2;

				if(k1_offset >= 0 && k1_offset < v_length && v1[k1_offset] != -1) {
					CFIndex x1 = v1[k1_offset];
					CFIndex y1 = v_offset + x1 - k1_offset;
					// Mirror x2 onto top-left coordinate system.
					x2 = text1_length - x2;

					if(x1 >= x2) {
						// Overlap detected.
						diffs = diff_bisectSplitOfStrings(text1, text2, x1, y1, properties);
						haveFoundDiffs = TRUE;
						break;
					}
				}
			}
		}

		if(haveFoundDiffs) {
			break;
		}
	}

	// Free buffers
	if(text1_buffer != NULL) {
		free(text1_buffer);
	};

	if(text2_buffer != NULL) {
		free(text2_buffer);
	};

	free(v1);
	free(v2);

	// Diff took too long and hit the deadline or
	// number of diffs equals number of characters, no commonality at all.
	if(!diffs) {
		diffs = [[NSMutableArray alloc] initWithCapacity:2];
		[diffs addObject:[DMDiff diffWithOperation:DIFF_DELETE andText:text1]];
		[diffs addObject:[DMDiff diffWithOperation:DIFF_INSERT andText:text2]];
	}

	return diffs;
}

/**
 * Given the location of the 'middle snake', split the diff in two parts and recurse.
 * @param text1 Old string to be diffed.
 * @param text2 New string to be diffed.
 * @param x Index of split point in text1.
 * @param y Index of split point in text2.
 * @param deadline Time at which to bail if not yet complete.
 * @return NSMutableArray of Diff objects.
 */

 NSMutableArray *diff_bisectSplitOfStrings(NSString *text1, NSString *text2, NSUInteger x, NSUInteger y, DiffProperties properties)
{
	NSString *text1a = [text1 substringToIndex:x];
	NSString *text2a = [text2 substringToIndex:y];
	NSString *text1b = [text1 substringFromIndex:x];
	NSString *text2b = [text2 substringFromIndex:y];

	// Compute both diffs serially.
	NSMutableArray *diffs = diff_diffsBetweenTextsWithProperties(text1a, text2a, properties);
	NSMutableArray *diffsb = diff_diffsBetweenTextsWithProperties(text1b, text2b, properties);

	[diffs addObjectsFromArray:diffsb];
	return diffs;
}

/**
 * Split two texts into a list of strings.  Reduce the texts to a string of
 * hashes where each Unicode character represents one line.
 * @param text1 First NSString.
 * @param text2 Second NSString.
 * @return Three element NSArray, containing the encoded text1, the
 *     encoded text2 and the NSMutableArray of unique strings. The zeroth element
 *     of the NSArray of unique strings is intentionally blank.
 */

NSArray *diff_linesToCharsForStrings(NSString *text1, NSString *text2)
{
	NSMutableArray *lineArray = [NSMutableArray array];		// NSString objects
	CFMutableDictionaryRef lineHash = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, NULL);
	// keys: NSString, values:raw CFIndex
	// e.g. [lineArray objectAtIndex:4] == "Hello\n"
	// e.g. [lineHash objectForKey:"Hello\n"] == 4

	// "\x00" is a valid character, but various debuggers don't like it.
	// So we'll insert a junk entry to avoid generating a nil character.
	[lineArray addObject:@""];

	NSString *chars1 = (NSString *)CFBridgingRelease(diff_linesToCharsMungeCFStringCreate((__bridge CFStringRef)text1, (__bridge CFMutableArrayRef)lineArray, lineHash));
	NSString *chars2 = (NSString *)CFBridgingRelease(diff_linesToCharsMungeCFStringCreate((__bridge CFStringRef)text2, (__bridge CFMutableArrayRef)lineArray, lineHash));
	NSArray *result = @[chars1, chars2, lineArray];
	CFRelease(lineHash);

	return result;
}

/**
 * Split two texts into a list of strings.  Reduce the texts to a string of
 * hashes where each Unicode character represents one token (or boundary between tokens).
 * A token can be a type of text fragment: a word, sentence, paragraph or line.
 * The type is determined by the mode object.
 * @param text1 First NSString.
 * @param text2 Second NSString.
 * @param mode value determining the tokenization mode.
 * @return Three element NSArray, containing the encoded text1, the
 *     encoded text2 and the NSMutableArray of unique strings. The zeroth element
 *     of the NSArray of unique strings is intentionally blank.
 */

NSArray *diff_tokensToCharsForStrings(NSString *text1, NSString *text2, DiffTokenMode mode)
{
	CFOptionFlags tokenizerOptions = 0;

	switch(mode) {
		case DiffWordTokens:
			tokenizerOptions = kCFStringTokenizerUnitWordBoundary;
			break;

		case DiffSentenceTokens:
			tokenizerOptions = kCFStringTokenizerUnitSentence;
			break;

		case DiffLineBreakDelimiteredTokens:
			tokenizerOptions = kCFStringTokenizerUnitLineBreak;
			break;

		case DiffParagraphTokens:
		default:
			tokenizerOptions = kCFStringTokenizerUnitParagraph;
			break;
	}

	NSMutableArray *tokenArray = [NSMutableArray array];	// NSString objects
	CFMutableDictionaryRef tokenHash = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, NULL);
	// keys: NSString, values:raw CFIndex
	// e.g. [tokenArray objectAtIndex:4] == "Hello"
	// e.g. [tokenHash objectForKey:"Hello"] == 4

	// "\x00" is a valid character, but various debuggers don't like it.
	// So we'll insert a junk entry to avoid generating a nil character.
	[tokenArray addObject:@""];

	NSString *tokens1 = (__bridge_transfer NSString *)diff_tokensToCharsMungeCFStringCreate((__bridge CFStringRef)text1, (__bridge CFMutableArrayRef)tokenArray, tokenHash, tokenizerOptions);
	NSString *tokens2 = (__bridge_transfer NSString *)diff_tokensToCharsMungeCFStringCreate((__bridge CFStringRef)text2, (__bridge CFMutableArrayRef)tokenArray, tokenHash, tokenizerOptions);
	NSArray *result = @[tokens1, tokens2, tokenArray];

	CFRelease(tokenHash);

	return result;
}

/**
 * Rehydrate the text in a diff from an NSString of line hashes to real lines
 * of text.
 * @param NSArray of Diff objects.
 * @param NSArray of unique strings.
 */

void diff_charsToLines(NSArray **diffs, NSArray *lineArray)
{
	if(diffs == NULL) {
		return;
	}

	for(DMDiff *diff in *diffs) {
		diff.text = (__bridge_transfer NSString *)diff_charsToTokenCFStringCreate((__bridge CFStringRef)diff.text, (__bridge CFArrayRef)lineArray);
	}
}

/**
 * Rehydrate the text in a diff from an NSString of token hashes to real text tokens.
 * @param NSArray of Diff objects.
 * @param NSArray of unique strings.
 */

void diff_charsToTokens(NSArray **diffs, NSArray *tokenArray)
{
	if(diffs == NULL) {
		return;
	}

	for(DMDiff *diff in *diffs) {
		diff.text = (__bridge_transfer NSString *)diff_charsToTokenCFStringCreate((__bridge CFStringRef)diff.text, (__bridge CFArrayRef)tokenArray);
	}
}

/**
 * Reorder and merge like edit sections.  Merge equalities.
 * Any edit section can move as long as it doesn't cross an equality.
 * @param diffs NSMutableArray of Diff objects.
 */

void diff_cleanupMerge(NSMutableArray **inputDiffs)
{
	if(inputDiffs == NULL || [*inputDiffs count] == 0) {
		return;
	}

	NSMutableArray <DMDiff *> *diffs = *inputDiffs;
	[diffs addObject:[DMDiff diffWithOperation:DIFF_EQUAL andText:@""]];	// Add a dummy entry at the end.

	NSUInteger indexOfCurrentDiff = 0;
	NSUInteger count_delete = 0;
	NSUInteger count_insert = 0;
	NSString *text_delete = @"";
	NSString *text_insert = @"";
	NSUInteger commonlength;

	while(indexOfCurrentDiff < diffs.count) {
		DMDiff *thisDiff = diffs[indexOfCurrentDiff];

		switch(thisDiff.operation) {
			case DIFF_INSERT:
				count_insert++;
				text_insert = [text_insert stringByAppendingString:thisDiff.text];
				indexOfCurrentDiff++;
				break;

			case DIFF_DELETE:
				count_delete++;
				text_delete = [text_delete stringByAppendingString:thisDiff.text];
				indexOfCurrentDiff++;
				break;

			case DIFF_EQUAL:
				// Upon reaching an equality, check for prior redundancies.
				if(count_delete + count_insert > 1) {
					if(count_delete != 0 && count_insert != 0) {
						// Factor out any common prefixes.
						commonlength = (NSUInteger)diff_commonPrefix((__bridge CFStringRef)text_insert, (__bridge CFStringRef)text_delete);

						if(commonlength != 0) {
							if((indexOfCurrentDiff - count_delete - count_insert) > 0 && ((DMDiff *)[diffs objectAtIndex:(indexOfCurrentDiff - count_delete - count_insert - 1)]).operation == DIFF_EQUAL) {
								((DMDiff *)[diffs objectAtIndex:(indexOfCurrentDiff - count_delete - count_insert - 1)]).text = [((DMDiff *)[diffs objectAtIndex:(indexOfCurrentDiff - count_delete - count_insert - 1)]).text stringByAppendingString:[text_insert substringToIndex:commonlength]];
							} else {
								[diffs insertObject:[DMDiff diffWithOperation:DIFF_EQUAL andText:[text_insert substringToIndex:commonlength]] atIndex:0];
								indexOfCurrentDiff++;
							}
							text_insert = [text_insert substringFromIndex:commonlength];
							text_delete = [text_delete substringFromIndex:commonlength];
						}

						// Factor out any common suffixes.
						commonlength = (NSUInteger)diff_commonSuffix((__bridge CFStringRef)text_insert, (__bridge CFStringRef)text_delete);

						if(commonlength != 0) {
							thisDiff.text = [[text_insert substringFromIndex:(text_insert.length - commonlength)] stringByAppendingString:thisDiff.text];
							text_insert = [text_insert substringWithRange:NSMakeRange(0, text_insert.length - commonlength)];
							text_delete = [text_delete substringWithRange:NSMakeRange(0, text_delete.length - commonlength)];
						}
					}

					// Delete the offending records and add the merged ones.
					if(count_delete == 0) {
						diff_spliceTwoArrays(&diffs, indexOfCurrentDiff - count_insert, count_delete + count_insert, [NSMutableArray arrayWithObject:[DMDiff diffWithOperation:DIFF_INSERT andText:text_insert]]);
					} else if(count_insert == 0) {
						diff_spliceTwoArrays(&diffs, indexOfCurrentDiff - count_delete, count_delete + count_insert, [NSMutableArray arrayWithObject:[DMDiff diffWithOperation:DIFF_DELETE andText:text_delete]]);
					} else {
						diff_spliceTwoArrays(&diffs, indexOfCurrentDiff - count_delete - count_insert, count_delete + count_insert, [NSMutableArray arrayWithObjects:[DMDiff diffWithOperation:DIFF_DELETE andText:text_delete], [DMDiff diffWithOperation:DIFF_INSERT andText:text_insert], nil]);
					}

					indexOfCurrentDiff = indexOfCurrentDiff - count_delete - count_insert +
					(count_delete != 0 ? 1 : 0) + (count_insert != 0 ? 1 : 0) + 1;
				} else if(indexOfCurrentDiff != 0 && [diffs[indexOfCurrentDiff - 1] operation] == DIFF_EQUAL) {
					// Merge this equality with the previous one.
					DMDiff *prevDiff = diffs[indexOfCurrentDiff - 1];
					prevDiff.text = [prevDiff.text stringByAppendingString:thisDiff.text];
					[diffs removeObjectAtIndex:indexOfCurrentDiff];
				} else {
					indexOfCurrentDiff++;
				}

				count_insert = 0;
				count_delete = 0;
				text_delete = @"";
				text_insert = @"";
				break;
		}
	}

	if(((DMDiff *)diffs.lastObject).text.length == 0) {
		[diffs removeLastObject];  // Remove the dummy entry at the end.
	}

	// Second pass: look for single edits surrounded on both sides by
	// equalities which can be shifted sideways to eliminate an equality.
	// e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC
	BOOL changes = NO;
	indexOfCurrentDiff = 1;

	// Intentionally ignore the first and last element (as they don't need checking).
	while(indexOfCurrentDiff < (diffs.count - 1)) {
		DMDiff *prevDiff = diffs[indexOfCurrentDiff - 1];
		DMDiff *thisDiff = diffs[indexOfCurrentDiff];
		DMDiff *nextDiff = diffs[indexOfCurrentDiff + 1];

		if(prevDiff.operation == DIFF_EQUAL && nextDiff.operation == DIFF_EQUAL) {
			// This is a single edit surrounded by equalities.
			if([thisDiff.text hasSuffix:prevDiff.text]) {
				// Shift the edit over the previous equality.
				thisDiff.text = [prevDiff.text stringByAppendingString:[thisDiff.text substringToIndex:(thisDiff.text.length - prevDiff.text.length)]];
				nextDiff.text = [prevDiff.text stringByAppendingString:nextDiff.text];
				diff_spliceTwoArrays(inputDiffs, indexOfCurrentDiff - 1, 1, nil);
				changes = YES;
			} else if([thisDiff.text hasPrefix:nextDiff.text]) {
				// Shift the edit over the next equality.
				prevDiff.text = [prevDiff.text stringByAppendingString:nextDiff.text];
				thisDiff.text = [[thisDiff.text substringFromIndex:nextDiff.text.length] stringByAppendingString:nextDiff.text];
				diff_spliceTwoArrays(inputDiffs, indexOfCurrentDiff + 1, 1, nil);
				changes = YES;
			}
		}

		indexOfCurrentDiff++;
	}

	// If shifts were made, the diff needs reordering and another shift sweep.
	if(changes) {
		diff_cleanupMerge(inputDiffs);
	}
}

/**
 * Look for single edits surrounded on both sides by equalities
 * which can be shifted sideways to align the edit to a word boundary.
 * e.g: The c<ins>at c</ins>ame. -> The <ins>cat </ins>came.
 * @param diffs NSMutableArray of Diff objects.
 */

void diff_cleanupSemanticLossless(NSMutableArray **mutableDiffs)
{
	if(mutableDiffs == NULL || [*mutableDiffs count] == 0) {
		return;
	}

	NSMutableArray *diffs = *mutableDiffs;
	NSUInteger indexOfCurrentDiff = 1;

	// Intentionally ignore the first and last element (as they don't need checking).
	while(indexOfCurrentDiff < (diffs.count - 1)) {
		DMDiff *prevDiff = diffs[indexOfCurrentDiff - 1];
		DMDiff *thisDiff = diffs[indexOfCurrentDiff];
		DMDiff *nextDiff = diffs[indexOfCurrentDiff + 1];

		if(prevDiff.operation == DIFF_EQUAL && nextDiff.operation == DIFF_EQUAL) {
			// This is a single edit surrounded by equalities.
			NSString *equality1 = prevDiff.text;
			NSString *edit = thisDiff.text;
			NSString *equality2 = nextDiff.text;

			// First, shift the edit as far left as possible.
			NSUInteger commonOffset = (NSUInteger)diff_commonSuffix((__bridge CFStringRef)equality1, (__bridge CFStringRef)edit);

			if(commonOffset > 0) {
				NSString *commonString = [edit substringFromIndex:(edit.length - commonOffset)];
				equality1 = [equality1 substringToIndex:(equality1.length - commonOffset)];
				edit = [commonString stringByAppendingString:[edit substringToIndex:(edit.length - commonOffset)]];
				equality2 = [commonString stringByAppendingString:equality2];
			}

			// Second, step right character by character,
			// looking for the best fit.
			NSString *bestEquality1 = equality1;
			NSString *bestEdit = edit;
			NSString *bestEquality2 = equality2;
			CFIndex bestScore = diff_cleanupSemanticScore((__bridge CFStringRef)equality1, (__bridge CFStringRef)edit) + diff_cleanupSemanticScore((__bridge CFStringRef)edit, (__bridge CFStringRef)equality2);

			while((edit.length != 0 && equality2.length != 0) && ([edit characterAtIndex:0] == [equality2 characterAtIndex:0])) {
				equality1 = [equality1 stringByAppendingString:[edit substringToIndex:1]];
				edit = [[edit substringFromIndex:1] stringByAppendingString:[equality2 substringToIndex:1]];
				equality2 = [equality2 substringFromIndex:1];
				CFIndex score = diff_cleanupSemanticScore((__bridge CFStringRef)equality1, (__bridge CFStringRef)edit) + diff_cleanupSemanticScore((__bridge CFStringRef)edit, (__bridge CFStringRef)equality2);

				// The >= encourages trailing rather than leading whitespace on edits.
				if(score >= bestScore) {
					bestScore = score;
					bestEquality1 = equality1;
					bestEdit = edit;
					bestEquality2 = equality2;
				}
			}

			if(prevDiff.text != bestEquality1) {
				// We have an improvement, save it back to the diff.
				if(bestEquality1.length != 0) {
					prevDiff.text = bestEquality1;
				} else {
					[diffs removeObjectAtIndex:indexOfCurrentDiff - 1];
					indexOfCurrentDiff--;
				}

				thisDiff.text = bestEdit;

				if(bestEquality2.length != 0) {
					nextDiff.text = bestEquality2;
				} else {
					[diffs removeObjectAtIndex:indexOfCurrentDiff + 1];
					indexOfCurrentDiff--;
				}
			}
		}

		indexOfCurrentDiff++;
	}
}

/**
 * Reduce the number of edits by eliminating operationally trivial
 * equalities.
 * @param diffs NSMutableArray of Diff objects.
 */

void patch_cleanupDiffsForEfficiency(NSMutableArray **diffs, PatchProperties properties)
{
	if(diffs == NULL || [*diffs count] == 0) {
		return;
	}

	BOOL changes = NO;
	// Stack of indices where equalities are found.
	CFMutableArrayRef equalities = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);

	// Always equal to equalities.lastObject.text
	NSString *lastequality = nil;
	CFIndex indexOfCurrentDiff = 0;	// Index of current position.
	BOOL pre_ins = NO;		// Is there an insertion operation before the last equality.
	BOOL pre_del = NO;		// Is there a deletion operation before the last equality.
	BOOL post_ins = NO;		// Is there an insertion operation after the last equality.
	BOOL post_del = NO;		// Is there a deletion operation after the last equality.

	NSUInteger indexToChange = 0;
	DMDiff *diffToChange = nil;

	while(indexOfCurrentDiff < [*diffs count]) {
		DMDiff *thisDiff = [*diffs objectAtIndex:indexOfCurrentDiff];

		if(thisDiff.operation == DIFF_EQUAL) {
			// Equality found.
			if(thisDiff.text.length < properties.diffEditingCost && (post_ins || post_del)) {
				// Candidate found.
				CFArrayAppendValue(equalities, (void *)indexOfCurrentDiff);
				pre_ins = post_ins;
				pre_del = post_del;
				lastequality = thisDiff.text;
			} else {
				// Not a candidate, and can never become one.
				CFArrayRemoveAllValues(equalities);
				lastequality = nil;
			}

			post_ins = post_del = NO;
		} else {
			// An insertion or deletion.
			if(thisDiff.operation == DIFF_DELETE) {
				post_del = YES;
			} else {
				post_ins = YES;
			}

			/*
			 * Five types to be split:
			 * <ins>A</ins><del>B</del>XY<ins>C</ins><del>D</del>
			 * <ins>A</ins>X<ins>C</ins><del>D</del>
			 * <ins>A</ins><del>B</del>X<ins>C</ins>
			 * <ins>A</del>X<ins>C</ins><del>D</del>
			 * <ins>A</ins><del>B</del>X<del>C</del>
			 */
			if((lastequality != nil) && ((pre_ins && pre_del && post_ins && post_del)
				|| ((lastequality.length < properties.diffEditingCost / 2)
					&& ((pre_ins ? 1 : 0) + (pre_del ? 1 : 0) + (post_ins ? 1 : 0) + (post_del ? 1 : 0)) == 3))) {
				// Duplicate record.
				CFIndex indexOfLastEquality = diff_CFArrayLastValueAsCFIndex(equalities);
				[*diffs insertObject:[DMDiff diffWithOperation:DIFF_DELETE andText:lastequality] atIndex:indexOfLastEquality];

				// Change second copy to insert.
				indexToChange = indexOfLastEquality + 1;
				diffToChange = [*diffs objectAtIndex:indexToChange];

				// The following assumes, that the diff we are changing is currently not used in a collection where its hash determines its position (e.g. a dictionary)
				diffToChange.operation = DIFF_INSERT;

				diff_CFArrayRemoveLastValue(equalities);	// Throw away the equality we just deleted.
				lastequality = nil;

				if(pre_ins && pre_del) {
				   // No changes made which could affect previous entry, keep going.
				   post_ins = post_del = YES;
				   CFArrayRemoveAllValues(equalities);
				} else {
				   if(CFArrayGetCount(equalities) > 0) {
					   diff_CFArrayRemoveLastValue(equalities);
				   }

				   indexOfCurrentDiff = CFArrayGetCount(equalities) > 0 ? indexOfLastEquality : -1;
				   post_ins = post_del = NO;
				}

				changes = YES;
			}
		}

		indexOfCurrentDiff++;
	}

	if(changes) {
		diff_cleanupMerge(diffs);
	}

	CFRelease(equalities);
}

/**
 * Convert a Diff list into a pretty HTML report.
 * @param diffs NSMutableArray of Diff objects.
 * @return HTML representation.
 */

NSString *diff_prettyHTMLFromDiffs(NSArray *diffs)
{
	NSMutableString *html = [NSMutableString string];

	for(DMDiff *diff in diffs) {
		NSString *diffText = [diff.text stringByEscapingHTML];

		switch(diff.operation) {
			case DIFF_INSERT:
				[html appendFormat:@"<ins>%@</ins>", diffText];
				break;

			case DIFF_DELETE:
				[html appendFormat:@"<del>%@</del>", diffText];
				break;

			case DIFF_EQUAL:
				[html appendFormat:@"<span>%@</span>", diffText];
				break;
		}
	}

	return html;
}

/**
 * Compute and return the source text (all equalities and deletions).
 * @param diffs NSMutableArray of Diff objects.
 * @return Source text.
 */

NSString *diff_text1(NSArray *diffs)
{
	NSMutableString *text = [NSMutableString string];

	for(DMDiff *diff in diffs) {
		if(diff.operation != DIFF_INSERT) {
			[text appendString:diff.text];
		}
	}

	return text;
}

/**
 * Compute and return the destination text (all equalities and insertions).
 * @param diffs NSMutableArray of Diff objects.
 * @return Destination text.
 */

NSString *diff_text2(NSArray *diffs)
{
	NSMutableString *text = [NSMutableString string];

	for(DMDiff *diff in diffs) {
		if(diff.operation != DIFF_DELETE) {
			[text appendString:diff.text];
		}
	}

	return text;
}

/**
 * Crush the diff into an encoded NSString which describes the operations
 * required to transform text1 into text2.
 * E.g. =3\t-2\t+ing  -> Keep 3 chars, delete 2 chars, insert 'ing'.
 * Operations are tab-separated.  Inserted text is escaped using %xx
 * notation.
 * @param diffs array of DMDiff objects.
 * @return Delta text.
 */

NSString *diff_deltaFromDiffs(NSArray *diffs)
{
	NSMutableString *delta = [NSMutableString string];

	for(DMDiff *diff in diffs) {
		switch(diff.operation) {
			case DIFF_INSERT:
				[delta appendFormat:@"+%@\t", [[diff.text encodedURIString] stringByReplacingOccurrencesOfString:@"%20" withString:@" "]];
				break;

			case DIFF_DELETE:
				[delta appendFormat:@"-%" PRId32 "\t", (int32_t)diff.text.length];
				break;

			case DIFF_EQUAL:
				[delta appendFormat:@"=%" PRId32 "\t", (int32_t)diff.text.length];
				break;
		}
	}

	if(delta.length != 0) {
		// Strip off trailing tab character.
		return [delta substringToIndex:(delta.length - 1)];
	}

	return delta;
}

/**
 * Given the original text1, and an encoded NSString which describes the
 * operations required to transform text1 into text2, compute the full diff.
 * @param text1 Source NSString for the diff.
 * @param delta Delta text.
 * @param error NSError if invalid input.
 * @return NSMutableArray of DMDiff objects or nil if invalid.
 */

NSArray *diff_diffsFromOriginalTextAndDelta(NSString *text1, NSString *delta, NSError **error)
{
	NSMutableArray *diffs = [NSMutableArray array];
	NSUInteger indexOfCurrentDiff = 0;											// Cursor in text1
	NSArray *tokens = [delta componentsSeparatedByString:@"\t"];
	NSInteger n = 0;

	for(NSString *token in tokens) {
		if(token.length == 0) {
			// Blank tokens are ok (from a trailing \t).
			continue;
		}

		// Each token begins with a one character parameter which specifies the
		// operation of this token (delete, insert, equality).
		NSString *param = [token substringFromIndex:1];

		switch([token characterAtIndex:0]) {
			case '+':
				{
					param = [param decodedURIString];

					if(param == nil) {
						if(error != NULL) {
							NSString *errorDescription = [NSString stringWithFormat:NSLocalizedString(@"Invalid character in diff_fromDelta: %@", @"Error"), param];
							*error = [NSError errorWithDomain:@"DiffMatchPatchErrorDomain" code:99 userInfo:@{NSLocalizedDescriptionKey: errorDescription}];
						}

						return nil;
					}

					[diffs addObject:[DMDiff diffWithOperation:DIFF_INSERT andText:param]];
					break;
				}

			case '-':
				// Fall through.
			case '=':
				{
					n = [param integerValue];

					if(n == 0) {
						if(error != NULL) {
							NSString *errorDescription = [NSString stringWithFormat:NSLocalizedString(@"Invalid number in diff_fromDelta: %@", @"Error"), param];
							*error = [NSError errorWithDomain:@"DiffMatchPatchErrorDomain" code:100 userInfo:@{NSLocalizedDescriptionKey: errorDescription}];
						}

						return nil;
					} else if(n < 0) {
						if(error != NULL) {
							NSString *errorDescription = [NSString stringWithFormat:NSLocalizedString(@"Negative number in diff_fromDelta: %@", @"Error"), param];
							*error = [NSError errorWithDomain:@"DiffMatchPatchErrorDomain" code:101 userInfo:@{NSLocalizedDescriptionKey: errorDescription}];
						}

						return nil;
					}

					NSString *text = nil;
					NSRange text1Range = NSMakeRange(0, text1.length);

					if(NSLocationInRange(indexOfCurrentDiff, text1Range) && NSLocationInRange(indexOfCurrentDiff + (NSUInteger)n - 1, text1Range)) {
						text = [text1 substringWithRange:NSMakeRange(indexOfCurrentDiff, (NSUInteger)n)];
						indexOfCurrentDiff += (NSUInteger)n;
					} else {
						if(error != NULL) {
							NSString *errorDescription = [NSString stringWithFormat:NSLocalizedString(@"Delta length (%lu) larger than source text length (%lu).", @"Error"), (unsigned long)indexOfCurrentDiff, (unsigned long)text1.length];
							*error = [NSError errorWithDomain:@"DiffMatchPatchErrorDomain" code:102 userInfo:@{NSLocalizedDescriptionKey: errorDescription}];
						}

						return nil;
					}

					if([token characterAtIndex:0] == '=') {
						[diffs addObject:[DMDiff diffWithOperation:DIFF_EQUAL andText:text]];
					} else {
						[diffs addObject:[DMDiff diffWithOperation:DIFF_DELETE andText:text]];
					}

					break;
				}

			default:
				// Anything else is an error.
				if(error != NULL) {
					NSString *errorDescription = [NSString stringWithFormat:NSLocalizedString(@"Invalid diff operation in diff_fromDelta: %C", @"Error"), [token characterAtIndex:0]];
					*error = [NSError errorWithDomain:@"DiffMatchPatchErrorDomain" code:102 userInfo:@{NSLocalizedDescriptionKey: errorDescription}];
				}

				return nil;
		}
	}

	if(indexOfCurrentDiff != text1.length) {
		if(error != NULL) {
			NSString *errorDescription = [NSString stringWithFormat:NSLocalizedString(@"Delta length (%lu) smaller than source text length (%lu).", @"Error"), (unsigned long)indexOfCurrentDiff, (unsigned long)text1.length];
			*error = [NSError errorWithDomain:@"DiffMatchPatchErrorDomain" code:103 userInfo:@{NSLocalizedDescriptionKey: errorDescription}];
		}

		return nil;
	}

	return diffs;
}

/**
 * loc is a location in text1, compute and return the equivalent location in text2.
 *		e.g. "The cat" vs "The big cat", 1->1, 5->8
 *
 * @param diffs NSMutableArray of DMDiff objects.
 * @param loc Location within text1.
 * @return Location within text2.
 */

NSUInteger diff_translateLocationFromText1ToText2(NSArray *diffs, NSUInteger location)
{
	NSUInteger chars1 = 0;
	NSUInteger chars2 = 0;
	NSUInteger last_chars1 = 0;
	NSUInteger last_chars2 = 0;
	DMDiff *lastDiff = nil;

	for(DMDiff *diff in diffs) {
		if(diff.operation != DIFF_INSERT) {
			// Equality or deletion.
			chars1 += diff.text.length;
		}

		if(diff.operation != DIFF_DELETE) {
			// Equality or insertion.
			chars2 += diff.text.length;
		}

		if(chars1 > location) {
			// Overshot the location.
			lastDiff = diff;
			break;
		}

		last_chars1 = chars1;
		last_chars2 = chars2;
	}

	if(lastDiff != nil && lastDiff.operation == DIFF_DELETE) {
		// The location was deleted.
		return last_chars2;
	}

	// Add the remaining character length.
	return last_chars2 + (location - last_chars1);
}

/**
 * Compute the Levenshtein distance; the number of inserted, deleted or
 * substituted characters.
 * @param diffs NSArray of Diff objects.
 * @return Number of changes.
 */

NSUInteger diff_levenshtein(NSArray *diffs)
{
	NSUInteger levenshtein = 0;
	NSUInteger insertions = 0;
	NSUInteger deletions = 0;

	for(DMDiff *diff in diffs) {
		switch(diff.operation) {
			case DIFF_INSERT:
				insertions += diff.text.length;
				break;

			case DIFF_DELETE:
				deletions += diff.text.length;
				break;

			case DIFF_EQUAL:
				// A deletion and an insertion is one substitution.
				levenshtein += MAX(insertions, deletions);
				insertions = 0;
				deletions = 0;
				break;
		}
	}

	levenshtein += MAX(insertions, deletions);
	return levenshtein;
}

/**
 * Reduce the number of edits by eliminating semantically trivial
 * equalities.
 * @param diffs NSMutableArray of Diff objects.
 */

void diff_cleanupSemantic(NSMutableArray **diffs)
{
	if(diffs == NULL || [*diffs count] == 0) {
		return;
	}

	// Stack of indices where equalities are found.
	CFMutableArrayRef equalities = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);

	NSString *lastEquality = nil;		// Always equal to [diffs objectAtIndex:equalitiesLastValue].text
	NSUInteger indexOfCurrentDiff = 0;	// Index of current position.

	// Number of characters that changed prior to the equality.
	NSUInteger length_insertions1 = 0;
	NSUInteger length_deletions1 = 0;

	// Number of characters that changed after the equality.
	NSUInteger length_insertions2 = 0;
	NSUInteger length_deletions2 = 0;

	NSUInteger indexToChange;
	DMDiff *diffToChange = nil;
	BOOL changes = NO;

	while(indexOfCurrentDiff < [*diffs count]) {
		DMDiff *thisDiff = [*diffs objectAtIndex:indexOfCurrentDiff];

		if(thisDiff.operation == DIFF_EQUAL) {                                                                                // Equality found.
			CFArrayAppendValue(equalities, (void *)indexOfCurrentDiff);
			length_insertions1 = length_insertions2;
			length_deletions1 = length_deletions2;
			length_insertions2 = 0;
			length_deletions2 = 0;
			lastEquality = thisDiff.text;
		} else {                                                                                // an insertion or deletion
			if(thisDiff.operation == DIFF_INSERT) {
				length_insertions2 += thisDiff.text.length;
			} else {
				length_deletions2 += thisDiff.text.length;
			}

			// Eliminate an equality that is smaller or equal to the edits on both sides of it.
			if(lastEquality != nil
			   && (lastEquality.length <= MAX(length_insertions1, length_deletions1))
			   && (lastEquality.length <= MAX(length_insertions2, length_deletions2))) {
				// Duplicate record.
				CFIndex indexOfLastEquality = diff_CFArrayLastValueAsCFIndex(equalities);
				[*diffs insertObject:[DMDiff diffWithOperation:DIFF_DELETE andText:lastEquality] atIndex:indexOfLastEquality];

				// Change second copy to insert.
				indexToChange = indexOfLastEquality + 1;
				diffToChange = [*diffs objectAtIndex:indexToChange];

				// The following assumes, that the diff we are changing is currently not used in a collection where its hash determines its position (e.g. a dictionary)
				diffToChange.operation = DIFF_INSERT;

				// Throw away the equality we just deleted.
				diff_CFArrayRemoveLastValue(equalities);

				if(CFArrayGetCount(equalities) > 0) {
					diff_CFArrayRemoveLastValue(equalities);
				}

				// Setting an unsigned value to -1 may seem weird to some,
				// but we will pass thru a ++ below:
				// => overflow => 0
				indexOfCurrentDiff = CFArrayGetCount(equalities) > 0 ? diff_CFArrayLastValueAsCFIndex(equalities) : -1;

				// Reset the counters.
				length_insertions1 = 0;
				length_deletions1 = 0;
				length_insertions2 = 0;
				length_deletions2 = 0;
				lastEquality = nil;
				changes = YES;
			}
		}

		indexOfCurrentDiff++;
	}

	// Normalize the diff.
	if(changes) {
		diff_cleanupMerge(diffs);
	}

	diff_cleanupSemanticLossless(diffs);

	// Jan: someDiff.text will NOT retain and autorelease the NSString object.
	// This is why "prevDiff.text = " below can cause it's previous value to be deallocated
	// instead of just released as one would expect without taking the above into account.
	// Thus we need to retain its previous value before "prevDiff.text = " and release afterwards.
	// Alternatively, we could remove the nonatomic from the "text" @property definition.
	// This would cause much more of a perfomance hit then warranted, though.

	// Find any overlaps between deletions and insertions.
	// e.g: <del>abcxxx</del><ins>xxxdef</ins>
	// -> <del>abc</del>xxx<ins>def</ins>
	// e.g: <del>xxxabc</del><ins>defxxx</ins>
	// -> <ins>def</ins>xxx<del>abc</del>
	// Only extract an overlap if it is as big as the edit ahead or behind it.
	indexOfCurrentDiff = 1;

	while(indexOfCurrentDiff < [*diffs count]) {
		DMDiff *prevDiff = [*diffs objectAtIndex:indexOfCurrentDiff - 1];
		DMDiff *thisDiff = [*diffs objectAtIndex:indexOfCurrentDiff];

		if(prevDiff.operation == DIFF_DELETE && thisDiff.operation == DIFF_INSERT) {
			NSString *deletion = prevDiff.text;
			NSString *insertion = thisDiff.text;
			NSUInteger overlap_length1 = (NSUInteger)diff_commonOverlap((__bridge CFStringRef)deletion, (__bridge CFStringRef)insertion);
			NSUInteger overlap_length2 = (NSUInteger)diff_commonOverlap((__bridge CFStringRef)insertion, (__bridge CFStringRef)deletion);

			if(overlap_length1 >= overlap_length2) {
				if((overlap_length1 >= deletion.length / 2.0f) || (overlap_length1 >= insertion.length / 2.0f)) {
					// Overlap found. Insert an equality and trim the surrounding edits.
					[*diffs insertObject:[DMDiff diffWithOperation:DIFF_EQUAL andText:[insertion substringToIndex:overlap_length1]] atIndex:indexOfCurrentDiff];
					prevDiff.text = [deletion substringToIndex:(deletion.length - overlap_length1)];

					DMDiff *nextDiff = [*diffs objectAtIndex:indexOfCurrentDiff + 1];
					nextDiff.text = [insertion substringFromIndex:overlap_length1];
					indexOfCurrentDiff++;
				}
			} else {
				if(overlap_length2 >= deletion.length / 2.0f ||
				   overlap_length2 >= insertion.length / 2.0f) {
					// Reverse overlap found.
					// Insert an equality and swap and trim the surrounding edits.
					[*diffs insertObject:[DMDiff diffWithOperation:DIFF_EQUAL andText:[deletion substringToIndex:overlap_length2]] atIndex:indexOfCurrentDiff];
					prevDiff.operation = DIFF_INSERT;
					prevDiff.text = [insertion substringToIndex:(insertion.length - overlap_length2)];
			DMDiff *nextDiff = [*diffs objectAtIndex:indexOfCurrentDiff + 1];
					nextDiff.operation = DIFF_DELETE;
					nextDiff.text = [deletion substringFromIndex:overlap_length2];
					indexOfCurrentDiff++;
				}
			}

			indexOfCurrentDiff++;
		}

		indexOfCurrentDiff++;
	}

	CFRelease(equalities);
}

#pragma mark -
#pragma mark Match Functions

NSUInteger match_locationOfMatchInText(NSString *text, NSString *pattern, NSUInteger approximateLocation)
{
	MatchProperties properties = match_defaultMatchProperties();
	return match_locationOfMatchInTextWithProperties(text, pattern, approximateLocation, properties);
}

NSUInteger match_locationOfMatchInTextWithOptions(NSString *text, NSString *pattern, NSUInteger approximateLocation, double matchThreshold, NSUInteger matchDistance)
{
	MatchProperties properties = match_defaultMatchProperties();
	properties.matchThreshold = matchThreshold;
	properties.matchDistance = matchDistance;

	return match_locationOfMatchInTextWithProperties(text, pattern, approximateLocation, properties);
}

/**
 * Locate the best instance of 'pattern' in 'text' near 'nearestLocation'.
 * Returns NSNotFound if no match found.
 * @param text				The text to search.
 * @param pattern			The pattern to search for.
 * @param nearestLocation	The location to search around.
 * @param properties		See the MatchProperties struct in DiffMatchPatchInternals.h for more info
 * @return Index of the best match or NSNotFound.
 */

NSUInteger match_locationOfMatchInTextWithProperties(NSString *text, NSString *pattern, NSUInteger approximateLocation, MatchProperties properties)
{
	// Check for null inputs.
	if(text == nil || pattern == nil) {
		NSLog(@"Null inputs. (match_main)");
		return NSNotFound;
	}

	if(text.length == 0) {
		NSLog(@"Empty text. (match_main)");
		return NSNotFound;
	}

	NSUInteger new_loc;
	new_loc = MIN(approximateLocation, text.length);
	new_loc = MAX((NSUInteger)0, new_loc);

	if([text isEqualToString:pattern]) {
		// Shortcut (potentially not guaranteed by the algorithm)
		return 0;
	} else if(text.length == 0) {
		// Nothing to match.
		return NSNotFound;
	} else if(new_loc + pattern.length <= text.length
			  && [[text substringWithRange:NSMakeRange(new_loc, pattern.length)] isEqualToString:pattern]) {
		// Perfect match at the perfect spot!   (Includes case of empty pattern)
		return new_loc;
	} else {
		// Do a fuzzy compare.
		return match_bitapOfTextAndPattern(text, pattern, new_loc, properties);
	}
}

/**
 * Locate the best instance of 'pattern' in 'text' near 'loc' using the
 * Bitap algorithm.   Returns NSNotFound if no match found.
 * @param text The text to search.
 * @param pattern The pattern to search for.
 * @param loc The location to search around.
 * @return Best match index or NSNotFound.
 */

NSUInteger match_bitapOfTextAndPattern(NSString *text, NSString *pattern, NSUInteger approximateLocation, MatchProperties properties)
{
	NSCAssert((properties.matchMaximumBits == 0 || pattern.length <= properties.matchMaximumBits), @"Pattern too long for this application.");

	// Initialise the alphabet.
	NSMutableDictionary *alphabet = match_alphabetFromPattern(pattern);

	// Highest score beyond which we give up.
	double score_threshold = properties.matchThreshold;

	// Is there a nearby exact match? (speedup)
	NSUInteger best_loc = [text rangeOfString:pattern options:NSLiteralSearch range:NSMakeRange(approximateLocation, text.length - approximateLocation)].location;

	if(best_loc != NSNotFound) {
		score_threshold = MIN(match_bitapScoreForErrorCount(0, best_loc, approximateLocation, pattern, properties), score_threshold);

		// What about in the other direction? (speedup)
		NSUInteger searchRangeLoc = MIN(approximateLocation + pattern.length, text.length);
		NSRange searchRange = NSMakeRange(0, searchRangeLoc);
		best_loc = [text rangeOfString:pattern options:(NSLiteralSearch | NSBackwardsSearch) range:searchRange].location;

		if(best_loc != NSNotFound) {
			score_threshold = MIN(match_bitapScoreForErrorCount(0, best_loc, approximateLocation, pattern, properties), score_threshold);
		}
	}

	// Initialise the bit arrays.
	NSUInteger matchmask = 1 << (pattern.length - 1);
	best_loc = NSNotFound;

	NSUInteger bin_min, bin_mid;
	NSUInteger bin_max = pattern.length + text.length;
	NSUInteger *rd = NULL;
	NSUInteger *last_rd = NULL;

	for(NSUInteger d = 0; d < pattern.length; d++) {
		// Scan for the best match; each iteration allows for one more error.
		// Run a binary search to determine how far from 'loc' we can stray at
		// this error level.
		bin_min = 0;
		bin_mid = bin_max;

		while(bin_min < bin_mid) {
			double score = match_bitapScoreForErrorCount(d, (approximateLocation + bin_mid), approximateLocation, pattern, properties);

			if(score <= score_threshold) {
				bin_min = bin_mid;
			} else {
				bin_max = bin_mid;
			}

			bin_mid = (bin_max - bin_min) / 2 + bin_min;
		}

		// Use the result from this iteration as the maximum for the next.
		bin_max = bin_mid;
		NSUInteger start = MAX_OF_CONST_AND_DIFF(1, approximateLocation, bin_mid);
		NSUInteger finish = MIN(approximateLocation + bin_mid, text.length) + pattern.length;

		rd = (NSUInteger *)calloc((finish + 2), sizeof(NSUInteger));
		rd[finish + 1] = (1 << d) - 1;

		for(NSUInteger j = finish; j >= start; j--) {
			NSUInteger charMatch;

			if(text.length <= j - 1 || ![alphabet objectForKey:[text substringWithRange:NSMakeRange((j - 1), 1)]]) {
				// Out of range.
				charMatch = 0;
			} else {
				charMatch = [(NSNumber *)[alphabet objectForKey:[text substringWithRange:NSMakeRange((j - 1), 1)]] unsignedIntegerValue];
			}

			if(d == 0) {
				// First pass: exact match.
				rd[j] = (((rd[j + 1] << 1) | 1) & charMatch);
			} else {
				// Subsequent passes: fuzzy match.
				rd[j] = (((rd[j + 1] << 1) | 1) & charMatch)
				| (((last_rd[j + 1] | last_rd[j]) << 1) | 1) | last_rd[j + 1];
			}

			if((rd[j] & matchmask) != 0) {
				double score = match_bitapScoreForErrorCount(d, (j - 1), approximateLocation, pattern, properties);

				// This match will almost certainly be better than any existing match. But check anyway.
				if(score <= score_threshold) {
					// Told you so.
					score_threshold = score;
					best_loc = j - 1;

					if(best_loc > approximateLocation) {
						// When passing loc, don't exceed our current distance from loc.
						start = MAX_OF_CONST_AND_DIFF(1, 2 * approximateLocation, best_loc);
					} else {
						// Already passed loc, downhill from here.
						break;
					}
				}
			}
		}

		if(last_rd != NULL) {
			free(last_rd);
		}

		last_rd = rd;

		if(match_bitapScoreForErrorCount((d + 1), approximateLocation, approximateLocation, pattern, properties) > score_threshold) {
			// No hope for a (better) match at greater error levels.
			break;
		}
	}

	if(last_rd != NULL && last_rd != rd) {
		free(last_rd);
	}

	if(rd != NULL) {
		free(rd);
	}

	return best_loc;
}

/**
 * Compute and return the score for a match with e errors and x location.
 * @param e Number of errors in match.
 * @param x Location of match.
 * @param loc Expected location of match.
 * @param pattern Pattern being sought.
 * @return Overall score for match (0.0 = good, 1.0 = bad).
 */

double match_bitapScoreForErrorCount(NSUInteger e, NSUInteger x, NSUInteger approximateLocation, NSString *pattern, MatchProperties properties)
{
	double score;
	double accuracy = (double)e / pattern.length;
	NSUInteger proximity = (NSUInteger)ABS((long long)approximateLocation - (long long)x);

	if(properties.matchDistance == 0) {
		return proximity == 0 ? accuracy : 1.0;		// Dodge divide by zero error.
	}

	score = accuracy + (proximity / (double)properties.matchDistance);
	return score;
}

/**
 * Initialise the alphabet for the Bitap algorithm.
 * @param pattern The text to encode.
 * @return Hash of character locations
 *     (NSMutableDictionary: keys:NSString/unichar, values:NSNumber/NSUInteger).
 */

NSMutableDictionary *match_alphabetFromPattern(NSString *pattern)
{
	NSMutableDictionary *alphabet = [NSMutableDictionary dictionary];
	CFStringRef str = (__bridge CFStringRef)pattern;
	CFStringInlineBuffer inlineBuffer;
	CFIndex length;
	CFIndex cnt;

	length = CFStringGetLength(str);
	CFStringInitInlineBuffer(str, &inlineBuffer, CFRangeMake(0, length));

	UniChar unicodeCharacter;
	NSString *character = nil;

	for(cnt = 0; cnt < length; cnt++) {
		unicodeCharacter = CFStringGetCharacterFromInlineBuffer(&inlineBuffer, cnt);

		// Create a string from the unichar
		character = (__bridge_transfer NSString *)CFStringCreateWithCharacters(kCFAllocatorDefault, &unicodeCharacter, 1);

		if(![alphabet objectForKey:character]) {
			[alphabet setObject:@(0) forKey:character];
		}
	}

	NSUInteger i = 0;

	for(cnt = 0; cnt < length; cnt++) {
		unicodeCharacter = CFStringGetCharacterFromInlineBuffer(&inlineBuffer, cnt);
		character = (__bridge_transfer NSString *)CFStringCreateWithCharacters(kCFAllocatorDefault, &unicodeCharacter, 1);	// Create a string from a unichar
		NSUInteger value = [(NSNumber *)[alphabet objectForKey:character] unsignedIntegerValue] | (1 << (pattern.length - i - 1));
		[alphabet setObject:@(value) forKey:character];
		i++;
	}

	return alphabet;
}

#pragma mark -
#pragma mark Patch Functions

NSArray *patch_patchesFromTexts(NSString *text1, NSString *text2)
{
	PatchProperties properties = patch_defaultPatchProperties();
	return patch_patchesFromTextsWithProperties(text1, text2, properties);
}

/**
 * Compute a list of patches to turn text1 into text2.
 * A set of diffs will be computed.
 * @param text1 Old text.
 * @param text2 New text.
 * @return NSMutableArray of Patch objects.
 */

NSArray *patch_patchesFromTextsWithProperties(NSString *text1, NSString *text2, PatchProperties properties)
{
	// Check for null inputs.
	if(text1 == nil || text2 == nil) {
		NSLog(@"Null inputs. (patch_make)");
		return nil;
	}

	// No diffs provided, compute our own.
	NSMutableArray *diffs = diff_diffsBetweenTextsWithProperties(text1, text2, properties.diffProperties);

	if(diffs.count > 2) {
		diff_cleanupSemantic(&diffs);
		patch_cleanupDiffsForEfficiency(&diffs, properties);
	}

	return patch_patchesFromTextAndDiffs(text1, diffs, properties);
}

/**
 * Compute a list of patches to turn text1 into text2.
 * text1 will be derived from the provided diffs.
 * @param diffs NSMutableArray of Diff objects for text1 to text2.
 * @return NSMutableArray of Patch objects.
 */
 NSArray *patch_patchesFromDiffs(NSArray *diffs, PatchProperties properties)
{
	// Check for nil inputs not needed since nil can't be passed in C#.
	// No origin NSString *provided, comAdde our own.
	return  patch_patchesFromTextAndDiffs(diff_text1(diffs), diffs, properties);
}

/**
 * Compute a list of patches to turn text1 into text2.
 * text2 is not provided, diffs are the delta between text1 and text2.
 * @param text1 Old text.
 * @param diffs NSMutableArray of Diff objects for text1 to text2.
 * @return NSMutableArray of Patch objects.
 */
NSArray *patch_patchesFromTextAndDiffs(NSString *text1, NSArray *diffs, PatchProperties properties)
{
	// Check for null inputs.
	if(text1 == nil) {
		NSLog(@"Null inputs. (patch_make)");
		return nil;
	}

	NSMutableArray *patches = [NSMutableArray array];

	if(diffs.count == 0) {
		return patches;		// Get rid of the nil case.
	}

	DMPatch *patch = [DMPatch new];
	NSUInteger char_count1 = 0;		// Number of characters into the text1 NSString.
	NSUInteger char_count2 = 0;		// Number of characters into the text2 NSString.

	// Start with text1 (prepatch_text) and apply the diffs until we arrive at text2 (postpatch_text).
	// We recreate the patches one by one to determine context info.

	NSString *prepatch_text = text1;
	NSMutableString *postpatch_text = [text1 mutableCopy];

	for(DMDiff *diff in diffs) {
		if(patch.diffs.count == 0 && diff.operation != DIFF_EQUAL) {
			// A new patch starts here.
			patch.start1 = char_count1;
			patch.start2 = char_count2;
		}

		switch(diff.operation) {
			case DIFF_INSERT:
				[patch.diffs addObject:diff];
				patch.length2 += diff.text.length;
				[postpatch_text insertString:diff.text atIndex:char_count2];
				break;

			case DIFF_DELETE:
				patch.length1 += diff.text.length;
				[patch.diffs addObject:diff];
				[postpatch_text deleteCharactersInRange:NSMakeRange(char_count2, diff.text.length)];
				break;

			case DIFF_EQUAL:
				if(diff.text.length <= 2 * properties.patchMargin
				   && [patch.diffs count] != 0 && diff != diffs.lastObject) {
					// Small equality inside a patch.
					[patch.diffs addObject:diff];
					patch.length1 += diff.text.length;
					patch.length2 += diff.text.length;
				}

				if(diff.text.length >= 2 * properties.patchMargin) {
					// Time for a new patch.
					if(patch.diffs.count != 0) {
						[patch addContext:prepatch_text withMargin:properties.patchMargin maximumBits:properties.matchProperties.matchMaximumBits];
						[patches addObject:patch];
						patch = [DMPatch new];

						// 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.
						prepatch_text = [postpatch_text copy];
						char_count1 = char_count2;
					}
				}

				break;
		}

		// Update the current character count.
		if(diff.operation != DIFF_INSERT) {
			char_count1 += diff.text.length;
		}

		if(diff.operation != DIFF_DELETE) {
			char_count2 += diff.text.length;
		}
	}

	// Pick up the leftover patch if not empty.
	if(patch.diffs.count != 0) {
		[patch addContext:prepatch_text withMargin:properties.patchMargin maximumBits:properties.matchProperties.matchMaximumBits];
		[patches addObject:patch];
	}

	return patches;
}

/**
 * Merge a set of patches onto the text.  Return a patched text, as well
 * as an index set of for each value for which patches were applied.
 *
 * @param patches					An NSArray of DMPatch objects
 * @param text						The old text
 * @param indexesOfAppliedPatches	An NSIndexSet of the patches, passed by reference (optional)
 * @return The patched text
 */

NSString *patch_applyPatchesToText(NSArray *sourcePatches, NSString *text, NSIndexSet **indexesOfAppliedPatches)
{
	PatchProperties properties = patch_defaultPatchProperties();
	return patch_applyPatchesToTextWithProperties(sourcePatches, text, indexesOfAppliedPatches, properties);
}

/**
 * Merge a set of patches onto the text.  Return a patched text, as well
 * as an index set of for each value for which patches were applied.
 *
 * @param patches					An NSArray of DMPatch objects
 * @param text						The old text
 * @param indexesOfAppliedPatches	An NSIndexSet of the patches, passed by reference (optional)
 * @param properties				PatchProperties defining the properties used to patch the text
 * @return The patched text
 */

NSString *patch_applyPatchesToTextWithProperties(NSArray *sourcePatches, NSString *text, NSIndexSet **indexesOfAppliedPatches, PatchProperties properties)
{
	if(sourcePatches.count == 0) {
		if(indexesOfAppliedPatches != NULL)
			*indexesOfAppliedPatches = [[NSIndexSet alloc] init];

		return text;
	}

	// Deep copy the patches so that no changes are made to originals.
	NSMutableArray *patches = [[NSMutableArray alloc] initWithArray:sourcePatches copyItems:TRUE];
	NSMutableString *mutableText = [text mutableCopy];

	NSString *nullPadding = patch_addPaddingToPatches(&patches, properties);
	[mutableText insertString:nullPadding atIndex:0];
	[mutableText appendString:nullPadding];
	patch_splitMax(&patches, properties);

	// 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.
	NSUInteger delta = 0;
	NSUInteger maxBits = properties.matchProperties.matchMaximumBits;

	NSMutableIndexSet *appliedPatches = [[NSMutableIndexSet alloc] init];
	NSUInteger patchIndex = 0;

	for(DMPatch *currentPatch in patches) {
		NSUInteger expected_loc = currentPatch.start2 + delta;
		NSString *text1 = diff_text1(currentPatch.diffs);
		NSUInteger start_loc;
		NSUInteger endLocation = NSNotFound;
		if(text1.length > maxBits) {
			// patch_splitMax will only provide an oversized pattern in the case of a monster delete.
			start_loc = match_locationOfMatchInText(mutableText, [text1 substringWithRange:NSMakeRange(0, maxBits)], expected_loc);
			if(start_loc != NSNotFound) {
				endLocation = match_locationOfMatchInText(mutableText, [text1 substringFromIndex:text1.length - maxBits], (expected_loc + text1.length - maxBits));

				if(endLocation == NSNotFound || start_loc >= endLocation) {
					// Can't find valid trailing context. Drop this patch.
					start_loc = NSNotFound;
				}
			}
		} else {
			start_loc = match_locationOfMatchInText(mutableText, text1, expected_loc);
		}

		if(start_loc == NSNotFound) {
			// No match found.  :(
			[appliedPatches removeIndex:patchIndex];
			// Subtract the delta for this failed patch from subsequent patches.
			delta -= currentPatch.length2 - currentPatch.length1;
		} else {
			// Found a match.   :)
			[appliedPatches addIndex:patchIndex];

			delta = start_loc - expected_loc;

			NSString *text2 = nil;

			if(endLocation == NSNotFound) {
				text2 = [mutableText substringWithRange:NSMakeRange(start_loc, MIN(text1.length, mutableText.length))];
			} else {
				text2 = [mutableText substringWithRange:NSMakeRange(start_loc, MIN(endLocation + maxBits, mutableText.length))];
			}

			if(text1 == text2) {
				// Perfect match, just shove the Replacement text in.
				[mutableText replaceCharactersInRange:NSMakeRange(start_loc, text1.length) withString:diff_text2(currentPatch.diffs)];
			} else {
				// Imperfect match.   Run a diff to get a framework of equivalent indices.
				NSMutableArray *diffs = diff_diffsBetweenTextsWithProperties(text1, text2, properties.diffProperties);

				if(text1.length > maxBits && (diff_levenshtein(diffs) / (float)text1.length) > properties.patchDeleteThreshold) {
					// The end points match, but the content is unacceptably bad.
					[appliedPatches removeIndex:patchIndex];
				} else {
					diff_cleanupSemanticLossless(&diffs);

					NSUInteger index1 = 0;
					for(DMDiff *currentDiff in currentPatch.diffs) {
						if(currentDiff.operation != DIFF_EQUAL) {
							NSUInteger index2 = diff_translateLocationFromText1ToText2(diffs, index1);

							if(currentDiff.operation == DIFF_INSERT) {
								// Insertion
								[mutableText insertString:currentDiff.text atIndex:(start_loc + index2)];
							} else if(currentDiff.operation == DIFF_DELETE) {
								// Deletion
								NSUInteger deletionEndPosition = diff_translateLocationFromText1ToText2(diffs, (index1 + currentDiff.text.length));
								[mutableText deleteCharactersInRange:NSMakeRange(start_loc + index2, (deletionEndPosition - index2))];
							}
						}

						if(currentDiff.operation != DIFF_DELETE) {
							index1 += currentDiff.text.length;
						}
					}
				}
			}
		}

		patchIndex++;
	}

	// Strip the padding.
	text = [mutableText substringWithRange:NSMakeRange(nullPadding.length, mutableText.length - 2 * nullPadding.length)];

	if(indexesOfAppliedPatches != NULL) {
		*indexesOfAppliedPatches = appliedPatches;
	}

	return text;
}

/**
 * Add some padding on text start and end so that edges can match something.
 * Intended to be called only from within patch_apply.
 * @param patches NSMutableArray of Patch objects.
 * @return The padding NSString added to each side.
 */
NSString *patch_addPaddingToPatches(NSMutableArray **patches, PatchProperties properties)
{
	if(patches == NULL || [*patches count] == 0) {
		return nil;
	}

	uint16_t paddingLength = properties.patchMargin;
	NSMutableString *nullPadding = [NSMutableString string];

	for(UniChar x = 1; x <= paddingLength; x++) {
		CFStringAppendCharacters((CFMutableStringRef)nullPadding, &x, 1);
	}

	// Bump all the patches forward.
	for(DMPatch *patch in *patches) {
		patch.start1 += paddingLength;
		patch.start2 += paddingLength;
	}

	// Add some padding on start of first diff.
	DMPatch *patch = [*patches objectAtIndex:0];
	NSMutableArray *diffs = patch.diffs;

	if(diffs.count == 0 || ((DMDiff *)diffs[0]).operation != DIFF_EQUAL) {
		// Add nullPadding equality.
		[diffs insertObject:[DMDiff diffWithOperation:DIFF_EQUAL andText:nullPadding] atIndex:0];
		patch.start1 -= paddingLength;                                                                                // Should be 0.
		patch.start2 -= paddingLength;                                                                                // Should be 0.
		patch.length1 += paddingLength;
		patch.length2 += paddingLength;
	} else if(paddingLength > ((DMDiff *)diffs[0]).text.length) {
		// Grow first equality.
		DMDiff *firstDiff = diffs[0];
		NSUInteger extraLength = paddingLength - firstDiff.text.length;
		firstDiff.text = [[nullPadding substringFromIndex:(firstDiff.text.length)]
						  stringByAppendingString:firstDiff.text];
		patch.start1 -= extraLength;
		patch.start2 -= extraLength;
		patch.length1 += extraLength;
		patch.length2 += extraLength;
	}

	// Add some padding on end of last diff.
	patch = [*patches lastObject];
	diffs = patch.diffs;

	if(diffs.count == 0 || ((DMDiff *)diffs.lastObject).operation != DIFF_EQUAL) {
		// Add nullPadding equality.
		[diffs addObject:[DMDiff diffWithOperation:DIFF_EQUAL andText:nullPadding]];
		patch.length1 += paddingLength;
		patch.length2 += paddingLength;
	} else if(paddingLength > ((DMDiff *)diffs.lastObject).text.length) {
		// Grow last equality.
		DMDiff *lastDiff = diffs.lastObject;
		NSUInteger extraLength = paddingLength - lastDiff.text.length;
		lastDiff.text = [lastDiff.text stringByAppendingString:[nullPadding substringToIndex:extraLength]];
		patch.length1 += extraLength;
		patch.length2 += extraLength;
	}

	return nullPadding;
}

/**
 * Look through the patches and break up any which are longer than the
 * maximum limit of the match algorithm.
 * Intended to be called only from within patch_apply.
 * @param patches NSMutableArray of Patch objects.
 */
void patch_splitMax(NSMutableArray **patches, PatchProperties properties)
{
	if(patches == NULL) {
		return;
	}

	NSUInteger patch_size = properties.matchProperties.matchMaximumBits;
	NSUInteger numberOfPatches = [*patches count];

	for(NSUInteger x = 0; x < numberOfPatches; x++) {
		if([(DMPatch *)[*patches objectAtIndex:x] length1] <= patch_size) {
			continue;
		}

		DMPatch *bigpatch = [*patches objectAtIndex:x];

		// Remove the big old patch.
		diff_spliceTwoArrays(patches, x--, 1, nil);
		NSUInteger start1 = bigpatch.start1;
		NSUInteger start2 = bigpatch.start2;
		NSString *precontext = @"";

		while(bigpatch.diffs.count != 0) {
			// Create one of several smaller patches.
			DMPatch *patch = [DMPatch new];
			BOOL empty = YES;
			patch.start1 = start1 - precontext.length;
			patch.start2 = start2 - precontext.length;

			if(precontext.length != 0) {
				patch.length1 = patch.length2 = precontext.length;
				[patch.diffs
				 addObject:[DMDiff diffWithOperation:DIFF_EQUAL andText:precontext]];
			}

			while(bigpatch.diffs.count != 0 && patch.length1 < patch_size - properties.patchMargin) {
				DMDiffOperation diff_type = ((DMDiff *)(bigpatch.diffs)[0]).operation;
				NSString *diff_text = ((DMDiff *)(bigpatch.diffs)[0]).text;

				if(diff_type == DIFF_INSERT) {
					// Insertions are harmless.
					patch.length2 += diff_text.length;
					start2 += diff_text.length;
					[patch.diffs addObject:(bigpatch.diffs)[0]];
					[bigpatch.diffs removeObjectAtIndex:0];
					empty = NO;
				} else if(diff_type == DIFF_DELETE && patch.diffs.count == 1
					  && ((DMDiff *)(patch.diffs)[0]).operation == DIFF_EQUAL
						  && diff_text.length > 2 * patch_size) {
					// This is a large deletion.  Let it pass in one chunk.
					patch.length1 += diff_text.length;
					start1 += diff_text.length;
					empty = NO;
					[patch.diffs addObject:[DMDiff diffWithOperation:diff_type andText:diff_text]];
					[bigpatch.diffs removeObjectAtIndex:0];
				} else {
					// Deletion or equality.  Only take as much as we can stomach.
					diff_text = [diff_text substringWithRange:NSMakeRange(0, MIN(diff_text.length, (patch_size - patch.length1 - properties.patchMargin)))];
					patch.length1 += diff_text.length;
					start1 += diff_text.length;

					if(diff_type == DIFF_EQUAL) {
						patch.length2 += diff_text.length;
						start2 += diff_text.length;
					} else {
						empty = NO;
					}

					[patch.diffs addObject:[DMDiff diffWithOperation:diff_type andText:diff_text]];

					if(diff_text == ((DMDiff *)(bigpatch.diffs)[0]).text) {
						[bigpatch.diffs removeObjectAtIndex:0];
					} else {
						DMDiff *firstDiff = (bigpatch.diffs)[0];
						firstDiff.text = [firstDiff.text substringFromIndex:diff_text.length];
					}
				}
			}
			// Compute the head context for the next patch.
			precontext = diff_text2(patch.diffs);
			precontext = [precontext substringFromIndex:MAX_OF_CONST_AND_DIFF(0, precontext.length, properties.patchMargin)];

			// Append the end context for this patch.
			NSString *postcontext = nil;
			NSString *text1 = diff_text1(bigpatch.diffs);

			if(text1.length > properties.patchMargin) {
				postcontext = [text1 substringToIndex:properties.patchMargin];
			} else {
				postcontext = text1;
			}

			if(postcontext.length != 0) {
				patch.length1 += postcontext.length;
				patch.length2 += postcontext.length;

				if(patch.diffs.count != 0 && ((DMDiff *)(patch.diffs)[(patch.diffs.count - 1)]).operation == DIFF_EQUAL) {
					DMDiff *lastDiff = [patch.diffs lastObject];
					lastDiff.text = [lastDiff.text stringByAppendingString:postcontext];
				} else {
					[patch.diffs addObject:[DMDiff diffWithOperation:DIFF_EQUAL andText:postcontext]];
				}
			}

			if(!empty) {
				diff_spliceTwoArrays(patches, ++x, 0, [NSMutableArray arrayWithObject:patch]);
			}
		}
	}
}

/**
 * Take a list of patches and return a textual representation.
 * @param patches NSMutableArray of Patch objects.
 * @return Text representation of patches.
 */

NSString *patch_patchesToText(NSArray *patches)
{
	NSMutableString *text = [NSMutableString string];

	for(DMPatch *patch in patches) {
		[text appendString:[patch patchText]];
	}

	return text;
}

/**
 * Parse a textual representation of patches and return a NSMutableArray of DMPatch objects.
 * @param textline Text representation of patches.
 * @param error NSError if invalid input.
 * @return NSMutableArray of Patch objects.
 */

NSArray *patch_parsePatchesFromText(NSString *textline, NSError **error)
{
	NSMutableArray *patches = [NSMutableArray array];

	if(textline.length == 0) {
		return patches;
	}

	NSArray *text = [textline componentsSeparatedByString:@"\n"];
	NSUInteger textPointer = 0;
	DMPatch *patch = nil;

	// NSString *patchHeader = @"^@@ -(\\d+),?(\\d*) \\+(\\d+),?(\\d*) @@$";
	NSString *patchHeaderStart = @"@@ -";
	NSString *patchHeaderMid = @"+";
	NSString *patchHeaderEnd = @"@@";
	NSString *optionalValueDelimiter = @",";
	BOOL scanSuccess, hasOptional;
	NSInteger scannedValue, optionalValue;
	NSDictionary *errorDetail = nil;
	NSString *textAtTextPointer = nil;

	unichar sign;
	NSString *line = nil;

	while(textPointer < text.count) {
		NSString *thisLine = text[textPointer];
		NSScanner *theScanner = [NSScanner scannerWithString:thisLine];
		patch = [DMPatch new];

		scanSuccess = ([theScanner scanString:patchHeaderStart intoString:NULL] && [theScanner scanInteger:&scannedValue]);

		if(scanSuccess) {
			patch.start1 = scannedValue;
			hasOptional = [theScanner scanString:optionalValueDelimiter intoString:NULL];

			if(hasOptional) {
				// First set has an optional value.
				scanSuccess = [theScanner scanInteger:&optionalValue];

				if(scanSuccess) {
					if(optionalValue == 0) {
						patch.length1 = 0;
					} else {
						patch.start1--;
						patch.length1 = optionalValue;
					}
				}
			} else {
				patch.start1--;
				patch.length1 = 1;
			}

			if(scanSuccess) {
				scanSuccess = ([theScanner scanString:patchHeaderMid intoString:NULL] && [theScanner scanInteger:&scannedValue]);

				if(scanSuccess) {
					patch.start2 = scannedValue;
					hasOptional = [theScanner scanString:optionalValueDelimiter intoString:NULL];

					if(hasOptional) {
						// Second set has an optional value.
						scanSuccess = [theScanner scanInteger:&optionalValue];

						if(scanSuccess) {
							if(optionalValue == 0) {
								patch.length2 = 0;
							} else {
								patch.start2--;
								patch.length2 = optionalValue;
							}
						}
					} else {
						patch.start2--;
						patch.length2 = 1;
					}

					if(scanSuccess) {
						scanSuccess = ([theScanner scanString:patchHeaderEnd intoString:NULL] && [theScanner isAtEnd] == YES);
					}
				}
			}
		}

		if(!scanSuccess) {
			if(error != NULL) {
				errorDetail = @{NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedString(@"Invalid patch string: %@", @"Error"), text[textPointer]]};
				*error = [NSError errorWithDomain:@"DiffMatchPatchErrorDomain" code:104 userInfo:errorDetail];
			}

			return nil;
		}

		[patches addObject:patch];

		textPointer++;

		while(textPointer < text.count) {
			textAtTextPointer = text[textPointer];

			if(textAtTextPointer.length > 0) {
				sign = [textAtTextPointer characterAtIndex:0];
			} else {
				// Blank line?  Whatever.
				textPointer++;
				continue;
			}

			line = [[textAtTextPointer substringFromIndex:1] decodedURIString];

			if(sign == '-') {
				// Deletion.
				[patch.diffs addObject:[DMDiff diffWithOperation:DIFF_DELETE andText:line]];
			} else if(sign == '+') {
				// Insertion.
				[patch.diffs addObject:[DMDiff diffWithOperation:DIFF_INSERT andText:line]];
			} else if(sign == ' ') {
				// Minor equality.
				[patch.diffs addObject:[DMDiff diffWithOperation:DIFF_EQUAL andText:line]];
			} else if(sign == '@') {
				// Start of next patch.
				break;
			} else {
				// WTF?
				if(error != NULL) {
					errorDetail = @{NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedString(@"Invalid patch mode '%C' in: %@", @"Error"), sign, line]};
					*error = [NSError errorWithDomain:@"DiffMatchPatchErrorDomain" code:104 userInfo:errorDetail];
				}

				return nil;
			}

			textPointer++;
		}
	}
	return patches;
}

#pragma mark -

void diff_spliceTwoArrays(NSMutableArray **input, NSUInteger start, NSUInteger count, NSArray *objects)
{
	// A JavaScript-style diff_splice function
	if(input == NULL) {
		return;
	}

	NSRange deletionRange = NSMakeRange(start, count);

	if(objects == nil) {
		[*input removeObjectsInRange:deletionRange];
	} else {
		[*input replaceObjectsInRange:deletionRange withObjectsFromArray:objects];
	}
}


text:
function [results]=FS_sampling_holdout(Train,Test,data_sample,clvalue,knnk);

xdata=data_sample(:,1:end-1);
group=data_sample(:,end);
None=0;
TT=[];
TrainingSample=xdata(Train,:);
TrainingLabel=group(Train,1);
TestingSample=xdata(Test,:);
TestingLabel=group(Test,1);
size(TrainingSample)
switch clvalue
    case 1
        options.MaxIter = 100000;
        svmStruct=svmtrain(TrainingSample, TrainingLabel, ...
            'showplot',false,'rbf_sigma',0.5,'Options', options);
        OutLabel=svmclassify(svmStruct,TestingSample);
    case 2
        tree=ClassificationTree.fit(TrainingSample, TrainingLabel);
        OutLabel=predict(tree,TestingSample);

    case 3
        knncl=ClassificationKNN.fit(TrainingSample, TrainingLabel,'NumNeighbors',knnk);
        OutLabel=predict(knncl,TestingSample);
    case 4
        discriminantcl=ClassificationDiscriminant.fit(TrainingSample, TrainingLabel,'DiscrimType', 'diagQuadratic');
        OutLabel=predict(discriminantcl,TestingSample);
        OutLabel=char(OutLabel);
    case 5
        RF=TreeBagger(50,TrainingSample, TrainingLabel);
        [Predict_label,Scores] = predict(RF,TestingSample);
        Predict_label=char(Predict_label);
        OutLabel=double(Predict_label);
        Q=OutLabel;
        minvalue=min([Q]);
        maxvalue=max([Q]);
        M=size(Q,1);
        for i=1:M
            if Q(i)>=maxvalue
                Q(i)=1;
            else
                Q(i)=0;
            end
        end
        predicted=sum(Q)
        Q(1)==1
        Q(1)==0
        OutLabel=Q;
        %

end
[Acc Pre Recall FM AUC]= resultanalysis(OutLabel,TestingLabel);
results=[Acc Pre Recall FM AUC]';

redpajama_set_name: RedPajamaGithub


text:
'use strict';

/**
 * Properties of a user's secret store.
 *
 */
class UserSecretStore {
  /**
   * Create a UserSecretStore.
   * @property {string} [keyVaultUri] The URI of the user's Key vault.
   * @property {string} [keyVaultId] The ID of the user's Key vault.
   */
  constructor() {
  }

  /**
   * Defines the metadata of UserSecretStore
   *
   * @returns {object} metadata of UserSecretStore
   *
   */
  mapper() {
    return {
      required: false,
      serializedName: 'UserSecretStore',
      type: {
        name: 'Composite',
        className: 'UserSecretStore',
        modelProperties: {
          keyVaultUri: {
            required: false,
            serializedName: 'keyVaultUri',
            type: {
              name: 'String'
            }
          },
          keyVaultId: {
            required: false,
            serializedName: 'keyVaultId',
            type: {
              name: 'String'
            }
          }
        }
      }
    };
  }
}

module.exports = UserSecretStore;


text:
set -e

RIAK=$1
CMD=$2

echo $1
echo $2

# ping
echo "ping:"
curl http://$RIAK/ping
echo ""

# init
# curl -i "http://$RIAK/buckets/__presto_schema/keys/__schema"
curl -XPUT "http://$RIAK/buckets/__presto_schema/keys/__schema" \
  -H "content-type: application/json" \
  -d '{"tables":["foobartable", "spam"]}'

curl "http://$RIAK/buckets/__presto_schema/keys/__schema"

curl -XPUT "http://$RIAK/buckets/__presto_schema/keys/default.foobartable" \
  -H "content-type: application/json" \
  -d '{"name":"foobartable", "columns":[{"name":"col1","type":"STRING","index":false},{"name":"col2","type":"LONG","index":false},{"name":"__pkey","type":"STRING","index":false}]}'

curl -XPUT "http://$RIAK/buckets/__presto_schema/keys/default.spam" \
  -H "content-type: application/json" \
  -d '{"name":"spam", "columns":[{"name":"col1","type":"STRING","index":false},{"name":"col2","type":"LONG", "index":true},{"name":"__pkey","type":"STRING","index":false}]}'

curl "http://$RIAK/buckets/__presto_schema/keys/default.foobartable"
curl "http://$RIAK/buckets/__presto_schema/keys/default.spam"

echo "try 'select * from spam s cross join foobartable f where s.col1=f.col1;'"


text:
package twigkit.html.helper;

import twigkit.html.*;
import twigkit.html.attr.Attribute;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author mr.olafsson
 */
public abstract class HtmlHttpServlet extends HttpServlet implements HtmlCapability {

    private HtmlCapability htmlCapability;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        htmlCapability = new HtmlCapabilityImpl(resp.getWriter(), HtmlHttpServlet.class);
        doGetHtml(req, resp);
    }

    protected abstract void doGetHtml(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException;

    /**
     * Delegate methods
     */

    @Override
    public ContainerTag html(Attribute... attr) throws IOException {
        return htmlCapability.html(attr);
    }

    @Override
    public ContainerTag body(Attribute... attr) throws IOException {
        return htmlCapability.body(attr);
    }

    @Override
    public ContainerTag head(Attribute... attr) throws IOException {
        return htmlCapability.head(attr);
    }

    @Override
    public Content meta(Attribute... attr) throws IOException {
        return htmlCapability.meta(attr);
    }

    @Override
    public ConditionalWrapper when(boolean test) {
        return htmlCapability.when(test);
    }

    @Override
    public Content exec(Code code) throws IOException {
        return htmlCapability.exec(code);
    }

    @Override
    public ContainerTag div(Attribute... attr) throws IOException {
        return htmlCapability.div(attr);
    }

    @Override
    public ContainerTag span(Attribute... attr) throws IOException {
        return htmlCapability.span(attr);
    }

    @Override
    public ContainerTag em(Attribute... attr) throws IOException {
        return htmlCapability.em(attr);
    }

    @Override
    public ContainerTag h1(Attribute... attr) throws IOException {
        return htmlCapability.h1(attr);
    }

    @Override
    public ContainerTag h2(Attribute... attr) throws IOException {
        return htmlCapability.h2(attr);
    }

    @Override
    public ContainerTag h3(Attribute... attr) throws IOException {
        return htmlCapability.h3(attr);
    }

    @Override
    public ContainerTag h4(Attribute... attr) throws IOException {
        return htmlCapability.h4(attr);
    }

    @Override
    public ContainerTag h5(Attribute... attr) throws IOException {
        return htmlCapability.h5(attr);
    }

    @Override
    public ContainerTag h6(Attribute... attr) throws IOException {
        return htmlCapability.h6(attr);
    }

    @Override
    public ContainerTag p(Attribute... attr) throws IOException {
        return htmlCapability.p(attr);
    }

    @Override
    public ContainerTag ul(Attribute... attr) throws IOException {
        return htmlCapability.ul(attr);
    }

    @Override
    public ContainerTag ol(Attribute... attr) throws IOException {
        return htmlCapability.ol(attr);
    }

    @Override
    public ContainerTag li(Attribute... attr) throws IOException {
        return htmlCapability.li(attr);
    }

    @Override
    public ContainerTag dl(Attribute... attr) throws IOException {
        return htmlCapability.dl(attr);
    }

    @Override
    public ContainerTag dt(Attribute... attr) throws IOException {
        return htmlCapability.dt(attr);
    }

    @Override
    public ContainerTag dd(Attribute... attr) throws IOException {
        return htmlCapability.dd(attr);
    }

    @Override
    public ContainerTag a(Attribute... attr) throws IOException {
        return htmlCapability.a(attr);
    }

    @Override
    public SelfClosingTag img(Attribute... attr) throws IOException {
        return htmlCapability.img(attr);
    }

    @Override
    public ContainerTag form(Attribute... attr) throws IOException {
        return htmlCapability.form(attr);
    }

    @Override
    public ContainerTag input(Attribute... attr) throws IOException {
        return htmlCapability.input(attr);
    }

    @Override
    public SelfClosingTag textarea(String value, Attribute... attr) throws IOException {
        return htmlCapability.textarea(value, attr);
    }

    @Override
    public SelfClosingTag checkbox(Attribute... attr) throws IOException {
        return htmlCapability.checkbox(attr);
    }

    @Override
    public ContainerTag fieldset(Attribute... attr) throws IOException {
        return htmlCapability.fieldset(attr);
    }

    @Override
    public ContainerTag legend(Attribute... attr) throws IOException {
        return htmlCapability.legend(attr);
    }

    @Override
    public ContainerTag script(Attribute... attr) throws IOException {
        return htmlCapability.script(attr);
    }

    @Override
    public ContainerTag custom(String name, Attribute... attr) throws IOException {
        return htmlCapability.custom(name, attr);
    }

    @Override
    public Text text(Object text) throws IOException {
        return htmlCapability.text(text);
    }

    @Override
    public Attribute when(boolean test, Attribute attr) {
        return htmlCapability.when(test, attr);
    }

    @Override
    public Attribute attr(String name, String... values) {
        return htmlCapability.attr(name, values);
    }

    @Override
    public Attribute.Class cls(String... values) {
        return htmlCapability.cls(values);
    }

    @Override
    public Attribute.Id id(String id) {
        return htmlCapability.id(id);
    }

    @Override
    public Attribute.Data data(String name, String value) {
        return htmlCapability.data(name, value);
    }

    @Override
    public Attribute.Href href(String value) {
        return htmlCapability.href(value);
    }

    @Override
    public Attribute.Src src(String value) {
        return htmlCapability.src(value);
    }

    @Override
    public Attribute.Target target(String value) {
        return htmlCapability.target(value);
    }

    @Override
    public Attribute.Title title(String value) {
        return htmlCapability.title(value);
    }

    @Override
    public Attribute.Style style(String value) {
        return htmlCapability.style(value);
    }

    @Override
    public Attribute.Height height(int value) {
        return htmlCapability.height(value);
    }

    @Override
    public Attribute.Width width(int value) {
        return htmlCapability.width(value);
    }

    @Override
    public Content iterate(Loop iterate) {
        return htmlCapability.iterate(iterate);
    }

    @Override
    public Content component(Component component) throws IOException {
        return htmlCapability.component(component);
    }
}


text:
FROM php:8.0-cli

RUN pecl install swoole > /dev/null && \
    docker-php-ext-enable swoole
RUN docker-php-ext-install pdo_mysql pcntl opcache > /dev/null

RUN echo "opcache.enable_cli=1" >> /usr/local/etc/php/conf.d/docker-php-ext-opcache.ini
#RUN echo "opcache.jit=1205" >> /usr/local/etc/php/conf.d/docker-php-ext-opcache.ini
#RUN echo "opcache.jit_buffer_size=128M" >> /usr/local/etc/php/conf.d/docker-php-ext-opcache.ini

ADD ./ /lumen
WORKDIR /lumen

RUN mkdir -p /lumen/bootstrap/cache /lumen/storage/logs /lumen/storage/framework/sessions /lumen/storage/framework/views /lumen/storage/framework/cache
RUN chmod -R 777 /lumen

RUN apt-get update > /dev/null && \
    apt-get install -yqq git unzip > /dev/null
RUN php -r "copy('https://install.phpcomposer.com/installer', 'composer-setup.php');" && php composer-setup.php && php -r "unlink('composer-setup.php');"
RUN mv composer.phar /usr/local/bin/composer

COPY deploy/laravel-s/composer.json ./

RUN echo "LARAVELS_LISTEN_IP=0.0.0.0" >> .env
RUN echo "LARAVELS_LISTEN_PORT=8080" >> .env

RUN composer install -a --no-dev --quiet
RUN php artisan laravels publish

EXPOSE 8080

CMD php bin/laravels start


text:
var assert      = require('assert'),
    should      = require('should'),
    fs          = require('fs'),
    dotenv      = require('../lib/main');

var result;

describe('dotenv', function() {
  before(function() {
    process.env["MACHINE"] = "my_computer";
    result = dotenv;
  });

  it('version should be set', function() {
    result.version.should.eql("0.5.1");
  });

  describe('.load()', function() {
    before(function() {
      result.load();
    });

    it('sets the basic environment variables', function() {
      process.env.BASIC.should.eql("basic");
    });

    it('expands environment variables', function() {
      process.env.BASIC_EXPAND.should.eql("basic");
    });

    it('expands undefined variables to an empty string', function() {
      process.env.UNDEFINED_EXPAND.should.eql("");
    });

    it('does not expand escaped variables', function() {
      process.env.ESCAPED_EXPAND.should.equal("$ESCAPED");
    });

    describe('machine environment variables are already set', function() {
      before(function() {
        process.env.MACHINE="my_computer";
        process.env.BASIC="should_not_be_chosen_because_exists_in_local_env";
      });

      it('prioritizes local file set value', function() {
        process.env.BASIC_EXPAND.should.eql("basic");
      });

      it('defers to the machine set value', function() {
        process.env.MACHINE_EXPAND.should.eql("my_computer");
      });
    });

    it('sets empty enviroment variable', function () {
      process.env.EMPTY.should.eql("");
    });

    it('sets double quotes environment variables', function() {
      process.env.DOUBLE_QUOTES.should.eql("double_quotes");
    });

    it('sets single quotes environment variables', function() {
      process.env.SINGLE_QUOTES.should.eql("single_quotes");
    });

    it('expands newlines but only if double quoted', function() {
      process.env.EXPAND_NEWLINES.should.eql("expand\nnewlines");
      process.env.DONT_EXPAND_NEWLINES_1.should.eql("dontexpand\\nnewlines");
      process.env.DONT_EXPAND_NEWLINES_2.should.eql("dontexpand\\nnewlines");
    });

    it('reads from .env.staging', function() {
      process.env.FROM_STAGING_ENV.should.eql("from_staging_env");
    });

    it('overrides any values in .env with .env.environment', function() {
      process.env.ENVIRONMENT_OVERRIDE.should.eql("staging");
    });

    it('reads from a skipped line in .env.development', function() {
      process.env.AFTER_LINE.should.eql("after_line");
    });

    it('ignores commented lines', function() {
      should.not.exist(process.env.COMMENTS);
    });

    it('respects equals signs in values', function() {
      process.env.EQUAL_SIGNS.should.eql("equals==");
    });

    it('should handle zero width unicode characters', function() {
      process.env.ZERO_WIDTH_CHARACTER.should.eql("user:pass@troup.mongohq.com:1004/dude");
    });

    it ('retains inner quotes', function() {
      process.env.RETAIN_INNER_QUOTES.should.eql('{"foo": "bar"}');
      process.env.RETAIN_INNER_QUOTES_AS_STRING.should.eql('{"foo": "bar"}');
    });

    it('should return true if .env file exists', function() {
      result.load().should.eql(true);
    });

    it('should return true if .env.ENVIRONMENT file does not exists', function() {
      var tmp = process.env.NODE_ENV;
      process.env.NODE_ENV = 'DNE';
      result.load().should.eql(true);
      process.env.NODE_ENV = tmp; // reset for future tests
    });

    it('should return false if .env file does not exist', function() {
      fs.renameSync('.env', '.tmpenv');
      result.load().should.eql(false);
      fs.renameSync('.tmpenv', '.env');
    });

  });

  describe('.load() after an ENV was already set on the machine', function() {
    before(function() {
      process.env.ENVIRONMENT_OVERRIDE = "set_on_machine";
      result.load();
    });

    it('sets using the value set on the machine', function() {
      process.env.ENVIRONMENT_OVERRIDE.should.eql("set_on_machine");
      delete process.env.ENVIRONMENT_OVERRIDE; //clean up for other tests
    });
  });

  describe('.load() if NODE_ENV is set in .env', function() {
    before(function() {
      result.load();
    });

    it('ENVIRONMENT_OVERRIDE should equal the value set in the .env.staging', function() {
      process.env.ENVIRONMENT_OVERRIDE.should.eql('staging');
      delete process.env.NODE_ENV; //cleanup for other tests
      delete process.env.ENVIRONMENT_OVERRIDE;
    });
  });

  describe('.load() if NODE_ENV is set in .env but NODE_ENV is already set on machine', function() {
    before(function() {
      process.env.NODE_ENV = "development"
      result.load();
    });

    it('ENVIRONMENT_OVERRIDE should equal the value set in the .env.development because that is the environment being set by the machine. machine wins here.', function() {
      process.env.ENVIRONMENT_OVERRIDE.should.eql('development');
      delete process.env.NODE_ENV; //clean up for other tests
      delete process.env.ENVIRONMENT_OVERRIDE;
    });
  });

  describe('.parse()', function(){
    it('should return an object', function(){
      dotenv.parse('').should.be.an.Object;
    });
    var buffer;
    before(function(done){
      fs.readFile('.env', function(err,res){
        buffer = res;
        done();
      })
    });

    it('should parse a buffer from a file into an object', function(){
      var payload = dotenv.parse( buffer );
      payload.should.be.an.Object;
      payload.should.have.property('BASIC', 'basic');
    })
  })
});