using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text.RegularExpressions;
using FuzzySharp.Edits;
using FuzzySharp.Extensions;
using FuzzySharp.Extractor;
using FuzzySharp.PreProcess;
using FuzzySharp.SimilarityRatio;
using FuzzySharp.SimilarityRatio.Scorer;
using FuzzySharp.SimilarityRatio.Scorer.Composite;
using FuzzySharp.SimilarityRatio.Scorer.Generic;
using FuzzySharp.SimilarityRatio.Scorer.StrategySensitive;
using FuzzySharp.SimilarityRatio.Scorer.StrategySensitive.Generic;
using FuzzySharp.SimilarityRatio.Strategy;
using FuzzySharp.SimilarityRatio.Strategy.Generic;
using FuzzySharp.Utils;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = "")]
[assembly: AssemblyCompany("Jacob Bayer")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Fuzzy string matcher based on FuzzyWuzzy algorithm from SeatGeek")]
[assembly: AssemblyFileVersion("1.0.4.0")]
[assembly: AssemblyInformationalVersion("2.0.2+53b71acd66e53a4ff9f4229348de48295f99c0a5")]
[assembly: AssemblyProduct("FuzzySharp")]
[assembly: AssemblyTitle("FuzzySharp")]
[assembly: AssemblyVersion("1.0.4.0")]
namespace FuzzySharp
{
public static class Fuzz
{
public static int Ratio(string input1, string input2)
{
return ScorerCache.Get<DefaultRatioScorer>().Score(input1, input2);
}
public static int Ratio(string input1, string input2, PreprocessMode preprocessMode)
{
return ScorerCache.Get<DefaultRatioScorer>().Score(input1, input2, preprocessMode);
}
public static int PartialRatio(string input1, string input2)
{
return ScorerCache.Get<PartialRatioScorer>().Score(input1, input2);
}
public static int PartialRatio(string input1, string input2, PreprocessMode preprocessMode)
{
return ScorerCache.Get<PartialRatioScorer>().Score(input1, input2, preprocessMode);
}
public static int TokenSortRatio(string input1, string input2)
{
return ScorerCache.Get<TokenSortScorer>().Score(input1, input2);
}
public static int TokenSortRatio(string input1, string input2, PreprocessMode preprocessMode)
{
return ScorerCache.Get<TokenSortScorer>().Score(input1, input2, preprocessMode);
}
public static int PartialTokenSortRatio(string input1, string input2)
{
return ScorerCache.Get<PartialTokenSortScorer>().Score(input1, input2);
}
public static int PartialTokenSortRatio(string input1, string input2, PreprocessMode preprocessMode)
{
return ScorerCache.Get<PartialTokenSortScorer>().Score(input1, input2, preprocessMode);
}
public static int TokenSetRatio(string input1, string input2)
{
return ScorerCache.Get<TokenSetScorer>().Score(input1, input2);
}
public static int TokenSetRatio(string input1, string input2, PreprocessMode preprocessMode)
{
return ScorerCache.Get<TokenSetScorer>().Score(input1, input2, preprocessMode);
}
public static int PartialTokenSetRatio(string input1, string input2)
{
return ScorerCache.Get<PartialTokenSetScorer>().Score(input1, input2);
}
public static int PartialTokenSetRatio(string input1, string input2, PreprocessMode preprocessMode)
{
return ScorerCache.Get<PartialTokenSetScorer>().Score(input1, input2, preprocessMode);
}
public static int TokenDifferenceRatio(string input1, string input2)
{
return ScorerCache.Get<TokenDifferenceScorer>().Score(input1, input2);
}
public static int TokenDifferenceRatio(string input1, string input2, PreprocessMode preprocessMode)
{
return ScorerCache.Get<TokenDifferenceScorer>().Score(input1, input2, preprocessMode);
}
public static int PartialTokenDifferenceRatio(string input1, string input2)
{
return ScorerCache.Get<PartialTokenDifferenceScorer>().Score(input1, input2);
}
public static int PartialTokenDifferenceRatio(string input1, string input2, PreprocessMode preprocessMode)
{
return ScorerCache.Get<PartialTokenDifferenceScorer>().Score(input1, input2, preprocessMode);
}
public static int TokenInitialismRatio(string input1, string input2)
{
return ScorerCache.Get<TokenInitialismScorer>().Score(input1, input2);
}
public static int TokenInitialismRatio(string input1, string input2, PreprocessMode preprocessMode)
{
return ScorerCache.Get<TokenInitialismScorer>().Score(input1, input2, preprocessMode);
}
public static int PartialTokenInitialismRatio(string input1, string input2)
{
return ScorerCache.Get<PartialTokenInitialismScorer>().Score(input1, input2);
}
public static int PartialTokenInitialismRatio(string input1, string input2, PreprocessMode preprocessMode)
{
return ScorerCache.Get<PartialTokenInitialismScorer>().Score(input1, input2);
}
public static int TokenAbbreviationRatio(string input1, string input2)
{
return ScorerCache.Get<TokenAbbreviationScorer>().Score(input1, input2);
}
public static int TokenAbbreviationRatio(string input1, string input2, PreprocessMode preprocessMode)
{
return ScorerCache.Get<TokenAbbreviationScorer>().Score(input1, input2, preprocessMode);
}
public static int PartialTokenAbbreviationRatio(string input1, string input2)
{
return ScorerCache.Get<PartialTokenAbbreviationScorer>().Score(input1, input2);
}
public static int PartialTokenAbbreviationRatio(string input1, string input2, PreprocessMode preprocessMode)
{
return ScorerCache.Get<PartialTokenAbbreviationScorer>().Score(input1, input2, preprocessMode);
}
public static int WeightedRatio(string input1, string input2)
{
return ScorerCache.Get<WeightedRatioScorer>().Score(input1, input2);
}
public static int WeightedRatio(string input1, string input2, PreprocessMode preprocessMode)
{
return ScorerCache.Get<WeightedRatioScorer>().Score(input1, input2, preprocessMode);
}
}
public static class Levenshtein
{
private static EditOp[] GetEditOps<T>(T[] arr1, T[] arr2) where T : IEquatable<T>
{
return GetEditOps(arr1.Length, arr1, arr2.Length, arr2);
}
private static EditOp[] GetEditOps(string s1, string s2)
{
return GetEditOps(s1.Length, s1.ToCharArray(), s2.Length, s2.ToCharArray());
}
private static EditOp[] GetEditOps<T>(int len1, T[] c1, int len2, T[] c2) where T : IEquatable<T>
{
int num = 0;
int num2 = 0;
int num3 = 0;
while (len1 > 0 && len2 > 0 && c1[num].Equals(c2[num2]))
{
len1--;
len2--;
num++;
num2++;
num3++;
}
int o = num3;
while (len1 > 0 && len2 > 0 && c1[num + len1 - 1].Equals(c2[num2 + len2 - 1]))
{
len1--;
len2--;
}
len1++;
len2++;
int[] array = new int[len2 * len1];
for (int i = 0; i < len2; i++)
{
array[i] = i;
}
for (int i = 1; i < len1; i++)
{
array[len2 * i] = i;
}
for (int i = 1; i < len1; i++)
{
int num4 = (i - 1) * len2;
int num5 = i * len2;
int num6 = num5 + len2 - 1;
T val = c1[num + i - 1];
int num7 = num2;
int num8 = i;
num5++;
while (num5 <= num6)
{
int num9 = array[num4++] + ((!val.Equals(c2[num7++])) ? 1 : 0);
num8++;
if (num8 > num9)
{
num8 = num9;
}
num9 = array[num4] + 1;
if (num8 > num9)
{
num8 = num9;
}
array[num5++] = num8;
}
}
return EditOpsFromCostMatrix(len1, c1, num, num3, len2, c2, num2, o, array);
}
private static EditOp[] EditOpsFromCostMatrix<T>(int len1, T[] c1, int p1, int o1, int len2, T[] c2, int p2, int o2, int[] matrix) where T : IEquatable<T>
{
int num = 0;
int num2 = matrix[len1 * len2 - 1];
EditOp[] array = new EditOp[num2];
int num3 = len1 - 1;
int num4 = len2 - 1;
int num5 = len1 * len2 - 1;
while (num3 > 0 || num4 > 0)
{
if (num3 != 0 && num4 != 0 && matrix[num5] == matrix[num5 - len2 - 1] && c1[p1 + num3 - 1].Equals(c2[p2 + num4 - 1]))
{
num3--;
num4--;
num5 -= len2 + 1;
num = 0;
continue;
}
if (num < 0 && num4 != 0 && matrix[num5] == matrix[num5 - 1] + 1)
{
EditOp editOp = new EditOp();
num2--;
array[num2] = editOp;
editOp.EditType = EditType.INSERT;
editOp.SourcePos = num3 + o1;
editOp.DestPos = --num4 + o2;
num5--;
continue;
}
if (num > 0 && num3 != 0 && matrix[num5] == matrix[num5 - len2] + 1)
{
EditOp editOp2 = new EditOp();
num2--;
array[num2] = editOp2;
editOp2.EditType = EditType.DELETE;
editOp2.SourcePos = --num3 + o1;
editOp2.DestPos = num4 + o2;
num5 -= len2;
continue;
}
if (num3 != 0 && num4 != 0 && matrix[num5] == matrix[num5 - len2 - 1] + 1)
{
num2--;
EditOp editOp3 = (array[num2] = new EditOp());
editOp3.EditType = EditType.REPLACE;
editOp3.SourcePos = --num3 + o1;
editOp3.DestPos = --num4 + o2;
num5 -= len2 + 1;
num = 0;
continue;
}
if (num == 0 && num4 != 0 && matrix[num5] == matrix[num5 - 1] + 1)
{
num2--;
EditOp editOp4 = (array[num2] = new EditOp());
editOp4.EditType = EditType.INSERT;
editOp4.SourcePos = num3 + o1;
editOp4.DestPos = --num4 + o2;
num5--;
num = -1;
continue;
}
if (num == 0 && num3 != 0 && matrix[num5] == matrix[num5 - len2] + 1)
{
num2--;
EditOp editOp5 = (array[num2] = new EditOp());
editOp5.EditType = EditType.DELETE;
editOp5.SourcePos = --num3 + o1;
editOp5.DestPos = num4 + o2;
num5 -= len2;
num = 1;
continue;
}
throw new InvalidOperationException("Cant calculate edit op");
}
return array;
}
public static MatchingBlock[] GetMatchingBlocks<T>(T[] s1, T[] s2) where T : IEquatable<T>
{
return GetMatchingBlocks(s1.Length, s2.Length, GetEditOps(s1, s2));
}
public static MatchingBlock[] GetMatchingBlocks(string s1, string s2)
{
return GetMatchingBlocks(s1.Length, s2.Length, GetEditOps(s1, s2));
}
public static MatchingBlock[] GetMatchingBlocks(int len1, int len2, OpCode[] ops)
{
int num = ops.Length;
int num2 = 0;
int num3 = 0;
int num4 = num;
while (num4-- != 0)
{
if (ops[num2].EditType == EditType.KEEP)
{
num3++;
while (num4 != 0 && ops[num2].EditType == EditType.KEEP)
{
num4--;
num2++;
}
if (num4 == 0)
{
break;
}
}
num2++;
}
MatchingBlock[] array = new MatchingBlock[num3 + 1];
int num5 = 0;
num2 = 0;
array[num5] = new MatchingBlock();
num4 = num;
while (num4 != 0)
{
if (ops[num2].EditType == EditType.KEEP)
{
array[num5].SourcePos = ops[num2].SourceBegin;
array[num5].DestPos = ops[num2].DestBegin;
while (num4 != 0 && ops[num2].EditType == EditType.KEEP)
{
num4--;
num2++;
}
if (num4 == 0)
{
array[num5].Length = len1 - array[num5].SourcePos;
num5++;
break;
}
array[num5].Length = ops[num2].SourceBegin - array[num5].SourcePos;
num5++;
array[num5] = new MatchingBlock();
}
num4--;
num2++;
}
MatchingBlock matchingBlock = new MatchingBlock
{
SourcePos = len1,
DestPos = len2,
Length = 0
};
array[num5] = matchingBlock;
return array;
}
private static MatchingBlock[] GetMatchingBlocks(int len1, int len2, EditOp[] ops)
{
int num = ops.Length;
int num2 = 0;
int i = 0;
int num3;
int num4 = (num3 = 0);
int num5 = num;
while (num5 != 0)
{
for (; ops[i].EditType == EditType.KEEP; i++)
{
if (--num5 == 0)
{
break;
}
}
if (num5 == 0)
{
break;
}
if (num4 < ops[i].SourcePos || num3 < ops[i].DestPos)
{
num2++;
num4 = ops[i].SourcePos;
num3 = ops[i].DestPos;
}
EditType editType = ops[i].EditType;
switch (editType)
{
case EditType.REPLACE:
do
{
num4++;
num3++;
num5--;
i++;
}
while (num5 != 0 && ops[i].EditType == editType && num4 == ops[i].SourcePos && num3 == ops[i].DestPos);
continue;
case EditType.DELETE:
do
{
num4++;
num5--;
i++;
}
while (num5 != 0 && ops[i].EditType == editType && num4 == ops[i].SourcePos && num3 == ops[i].DestPos);
continue;
case EditType.INSERT:
break;
default:
continue;
}
do
{
num3++;
num5--;
i++;
}
while (num5 != 0 && ops[i].EditType == editType && num4 == ops[i].SourcePos && num3 == ops[i].DestPos);
}
if (num4 < len1 || num3 < len2)
{
num2++;
}
MatchingBlock[] array = new MatchingBlock[num2 + 1];
i = 0;
num4 = (num3 = 0);
int num6 = 0;
num5 = num;
while (num5 != 0)
{
for (; ops[i].EditType == EditType.KEEP; i++)
{
if (--num5 == 0)
{
break;
}
}
if (num5 == 0)
{
break;
}
if (num4 < ops[i].SourcePos || num3 < ops[i].DestPos)
{
MatchingBlock matchingBlock = new MatchingBlock();
matchingBlock.SourcePos = num4;
matchingBlock.DestPos = num3;
matchingBlock.Length = ops[i].SourcePos - num4;
num4 = ops[i].SourcePos;
num3 = ops[i].DestPos;
array[num6++] = matchingBlock;
}
EditType editType = ops[i].EditType;
switch (editType)
{
case EditType.REPLACE:
do
{
num4++;
num3++;
num5--;
i++;
}
while (num5 != 0 && ops[i].EditType == editType && num4 == ops[i].SourcePos && num3 == ops[i].DestPos);
continue;
case EditType.DELETE:
do
{
num4++;
num5--;
i++;
}
while (num5 != 0 && ops[i].EditType == editType && num4 == ops[i].SourcePos && num3 == ops[i].DestPos);
continue;
case EditType.INSERT:
break;
default:
continue;
}
do
{
num3++;
num5--;
i++;
}
while (num5 != 0 && ops[i].EditType == editType && num4 == ops[i].SourcePos && num3 == ops[i].DestPos);
}
if (num4 < len1 || num3 < len2)
{
MatchingBlock matchingBlock2 = new MatchingBlock();
matchingBlock2.SourcePos = num4;
matchingBlock2.DestPos = num3;
matchingBlock2.Length = len1 - num4;
array[num6++] = matchingBlock2;
}
MatchingBlock matchingBlock3 = new MatchingBlock();
matchingBlock3.SourcePos = len1;
matchingBlock3.DestPos = len2;
matchingBlock3.Length = 0;
array[num6] = matchingBlock3;
return array;
}
private static OpCode[] EditOpsToOpCodes(EditOp[] ops, int len1, int len2)
{
int num = ops.Length;
int i = 0;
int num2 = 0;
int num3;
int num4 = (num3 = 0);
int num5 = num;
EditType editType;
while (num5 != 0)
{
for (; ops[i].EditType == EditType.KEEP; i++)
{
if (--num5 == 0)
{
break;
}
}
if (num5 == 0)
{
break;
}
if (num4 < ops[i].SourcePos || num3 < ops[i].DestPos)
{
num2++;
num4 = ops[i].SourcePos;
num3 = ops[i].DestPos;
}
num2++;
editType = ops[i].EditType;
switch (editType)
{
case EditType.REPLACE:
do
{
num4++;
num3++;
num5--;
i++;
}
while (num5 != 0 && ops[i].EditType == editType && num4 == ops[i].SourcePos && num3 == ops[i].DestPos);
continue;
case EditType.DELETE:
do
{
num4++;
num5--;
i++;
}
while (num5 != 0 && ops[i].EditType == editType && num4 == ops[i].SourcePos && num3 == ops[i].DestPos);
continue;
case EditType.INSERT:
break;
default:
continue;
}
do
{
num3++;
num5--;
i++;
}
while (num5 != 0 && ops[i].EditType == editType && num4 == ops[i].SourcePos && num3 == ops[i].DestPos);
}
if (num4 < len1 || num3 < len2)
{
num2++;
}
OpCode[] array = new OpCode[num2];
i = 0;
num4 = (num3 = 0);
int j = 0;
for (num5 = num; num5 != 0; array[j].EditType = editType, array[j].SourceEnd = num4, array[j].DestEnd = num3, j++)
{
for (; ops[i].EditType == EditType.KEEP; i++)
{
if (--num5 == 0)
{
break;
}
}
if (num5 == 0)
{
break;
}
OpCode opCode = (array[j] = new OpCode());
opCode.SourceBegin = num4;
opCode.DestBegin = num3;
if (num4 < ops[i].SourcePos || num3 < ops[i].DestPos)
{
opCode.EditType = EditType.KEEP;
int num6 = (opCode.SourceEnd = ops[i].SourcePos);
num4 = num6;
num6 = (opCode.DestEnd = ops[i].DestPos);
num3 = num6;
j++;
OpCode opCode2 = (array[j] = new OpCode());
opCode2.SourceBegin = num4;
opCode2.DestBegin = num3;
}
editType = ops[i].EditType;
switch (editType)
{
case EditType.REPLACE:
do
{
num4++;
num3++;
num5--;
i++;
}
while (num5 != 0 && ops[i].EditType == editType && num4 == ops[i].SourcePos && num3 == ops[i].DestPos);
continue;
case EditType.DELETE:
do
{
num4++;
num5--;
i++;
}
while (num5 != 0 && ops[i].EditType == editType && num4 == ops[i].SourcePos && num3 == ops[i].DestPos);
continue;
case EditType.INSERT:
break;
default:
continue;
}
do
{
num3++;
num5--;
i++;
}
while (num5 != 0 && ops[i].EditType == editType && num4 == ops[i].SourcePos && num3 == ops[i].DestPos);
}
if (num4 < len1 || num3 < len2)
{
if (array[j] == null)
{
array[j] = new OpCode();
}
array[j].EditType = EditType.KEEP;
array[j].SourceBegin = num4;
array[j].DestBegin = num3;
array[j].SourceEnd = len1;
array[j].DestEnd = len2;
j++;
}
return array;
}
public static int EditDistance(string s1, string s2, int xcost = 0)
{
return EditDistance(s1.ToCharArray(), s2.ToCharArray(), xcost);
}
public static int EditDistance<T>(T[] c1, T[] c2, int xcost = 0) where T : IEquatable<T>
{
int num = 0;
int num2 = 0;
int num3 = c1.Length;
int num4 = c2.Length;
while (num3 > 0 && num4 > 0 && c1[num].Equals(c2[num2]))
{
num3--;
num4--;
num++;
num2++;
}
while (num3 > 0 && num4 > 0 && c1[num + num3 - 1].Equals(c2[num2 + num4 - 1]))
{
num3--;
num4--;
}
if (num3 == 0)
{
return num4;
}
if (num4 == 0)
{
return num3;
}
if (num3 > num4)
{
int num5 = num3;
int num6 = num;
num3 = num4;
num4 = num5;
num = num2;
num2 = num6;
T[] array = c2;
c2 = c1;
c1 = array;
}
if (num3 == 1)
{
if (xcost != 0)
{
return num4 + 1 - 2 * Memchr(c2, num2, c1[num], num4);
}
return num4 - Memchr(c2, num2, c1[num], num4);
}
num3++;
num4++;
int num7 = num3 >> 1;
int[] array2 = new int[num4];
int num8 = num4 - 1;
for (int i = 0; i < num4 - ((xcost == 0) ? num7 : 0); i++)
{
array2[i] = i;
}
if (xcost != 0)
{
for (int i = 1; i < num3; i++)
{
int num9 = 1;
T val = c1[num + i - 1];
int num10 = num2;
int num11 = i;
int num12 = i;
while (num9 <= num8)
{
num12 = ((!val.Equals(c2[num10++])) ? (num12 + 1) : (--num11));
num11 = array2[num9];
num11++;
if (num12 > num11)
{
num12 = num11;
}
array2[num9++] = num12;
}
}
}
else
{
array2[0] = num3 - num7 - 1;
for (int i = 1; i < num3; i++)
{
T val2 = c1[num + i - 1];
int num14;
int num15;
int num18;
int num17;
if (i >= num3 - num7)
{
int num13 = i - (num3 - num7);
num14 = num2 + num13;
num15 = num13;
int num16 = array2[num15++] + ((!val2.Equals(c2[num14++])) ? 1 : 0);
num17 = array2[num15];
num17++;
num18 = num17;
if (num17 > num16)
{
num17 = num16;
}
array2[num15++] = num17;
}
else
{
num15 = 1;
num14 = num2;
num18 = (num17 = i);
}
if (i <= num7 + 1)
{
num8 = num4 + i - num7 - 2;
}
while (num15 <= num8)
{
int num19 = --num18 + ((!val2.Equals(c2[num14++])) ? 1 : 0);
num17++;
if (num17 > num19)
{
num17 = num19;
}
num18 = array2[num15];
num18++;
if (num17 > num18)
{
num17 = num18;
}
array2[num15++] = num17;
}
if (i <= num7)
{
int num20 = --num18 + ((!val2.Equals(c2[num14])) ? 1 : 0);
num17++;
if (num17 > num20)
{
num17 = num20;
}
array2[num15] = num17;
}
}
}
return array2[num8];
}
private static int Memchr<T>(T[] haystack, int offset, T needle, int num) where T : IEquatable<T>
{
if (num != 0)
{
int num2 = 0;
do
{
if (haystack[offset + num2].Equals(needle))
{
return 1;
}
num2++;
}
while (--num != 0);
}
return 0;
}
public static double GetRatio<T>(T[] input1, T[] input2) where T : IEquatable<T>
{
int num = input1.Length;
int num2 = input2.Length;
int num3 = num + num2;
int num4 = EditDistance(input1, input2, 1);
if (num4 != 0)
{
return (double)(num3 - num4) / (double)num3;
}
return 1.0;
}
public static double GetRatio<T>(IEnumerable<T> input1, IEnumerable<T> input2) where T : IEquatable<T>
{
T[] array = input1.ToArray();
T[] array2 = input2.ToArray();
int num = array.Length;
int num2 = array2.Length;
int num3 = num + num2;
int num4 = EditDistance(array, array2, 1);
if (num4 != 0)
{
return (double)(num3 - num4) / (double)num3;
}
return 1.0;
}
public static double GetRatio(string s1, string s2)
{
return GetRatio(s1.ToCharArray(), s2.ToCharArray());
}
}
public static class Process
{
private static readonly IRatioScorer s_defaultScorer = ScorerCache.Get<WeightedRatioScorer>();
private static readonly Func<string, string> s_defaultStringProcessor = StringPreprocessorFactory.GetPreprocessor(PreprocessMode.Full);
public static IEnumerable<ExtractedResult<string>> ExtractAll(string query, IEnumerable<string> choices, Func<string, string> processor = null, IRatioScorer scorer = null, int cutoff = 0)
{
if (processor == null)
{
processor = s_defaultStringProcessor;
}
if (scorer == null)
{
scorer = s_defaultScorer;
}
return ResultExtractor.ExtractWithoutOrder(query, choices, processor, scorer, cutoff);
}
public static IEnumerable<ExtractedResult<T>> ExtractAll<T>(T query, IEnumerable<T> choices, Func<T, string> processor, IRatioScorer scorer = null, int cutoff = 0)
{
if (scorer == null)
{
scorer = s_defaultScorer;
}
return ResultExtractor.ExtractWithoutOrder(query, choices, processor, scorer, cutoff);
}
public static IEnumerable<ExtractedResult<string>> ExtractTop(string query, IEnumerable<string> choices, Func<string, string> processor = null, IRatioScorer scorer = null, int limit = 5, int cutoff = 0)
{
if (processor == null)
{
processor = s_defaultStringProcessor;
}
if (scorer == null)
{
scorer = s_defaultScorer;
}
return ResultExtractor.ExtractTop(query, choices, processor, scorer, limit, cutoff);
}
public static IEnumerable<ExtractedResult<T>> ExtractTop<T>(T query, IEnumerable<T> choices, Func<T, string> processor, IRatioScorer scorer = null, int limit = 5, int cutoff = 0)
{
if (scorer == null)
{
scorer = s_defaultScorer;
}
return ResultExtractor.ExtractTop(query, choices, processor, scorer, limit, cutoff);
}
public static IEnumerable<ExtractedResult<string>> ExtractSorted(string query, IEnumerable<string> choices, Func<string, string> processor = null, IRatioScorer scorer = null, int cutoff = 0)
{
if (processor == null)
{
processor = s_defaultStringProcessor;
}
if (scorer == null)
{
scorer = s_defaultScorer;
}
return ResultExtractor.ExtractSorted(query, choices, processor, scorer, cutoff);
}
public static IEnumerable<ExtractedResult<T>> ExtractSorted<T>(T query, IEnumerable<T> choices, Func<T, string> processor, IRatioScorer scorer = null, int cutoff = 0)
{
if (scorer == null)
{
scorer = s_defaultScorer;
}
return ResultExtractor.ExtractSorted(query, choices, processor, scorer, cutoff);
}
public static ExtractedResult<string> ExtractOne(string query, IEnumerable<string> choices, Func<string, string> processor = null, IRatioScorer scorer = null, int cutoff = 0)
{
if (processor == null)
{
processor = s_defaultStringProcessor;
}
if (scorer == null)
{
scorer = s_defaultScorer;
}
return ResultExtractor.ExtractOne(query, choices, processor, scorer, cutoff);
}
public static ExtractedResult<T> ExtractOne<T>(T query, IEnumerable<T> choices, Func<T, string> processor, IRatioScorer scorer = null, int cutoff = 0)
{
if (scorer == null)
{
scorer = s_defaultScorer;
}
return ResultExtractor.ExtractOne(query, choices, processor, scorer, cutoff);
}
public static ExtractedResult<string> ExtractOne(string query, params string[] choices)
{
return ResultExtractor.ExtractOne(query, choices, s_defaultStringProcessor, s_defaultScorer);
}
}
}
namespace FuzzySharp.Utils
{
public abstract class Heap<T> : IEnumerable<T>, IEnumerable
{
private const int InitialCapacity = 0;
private const int GrowFactor = 2;
private const int MinGrow = 1;
private int _capacity;
private T[] _heap = new T[0];
private int _tail;
public int Count => _tail;
public int Capacity => _capacity;
protected Comparer<T> Comparer { get; }
protected abstract bool Dominates(T x, T y);
protected Heap()
: this(Comparer<T>.Default)
{
}
protected Heap(Comparer<T> comparer)
: this(Enumerable.Empty<T>(), comparer)
{
}
protected Heap(IEnumerable<T> collection)
: this(collection, Comparer<T>.Default)
{
}
protected Heap(IEnumerable<T> collection, Comparer<T> comparer)
{
if (collection == null)
{
throw new ArgumentNullException("collection");
}
Comparer = comparer ?? throw new ArgumentNullException("comparer");
foreach (T item in collection)
{
if (Count == Capacity)
{
Grow();
}
_heap[_tail++] = item;
}
for (int num = Parent(_tail - 1); num >= 0; num--)
{
BubbleDown(num);
}
}
public void Add(T item)
{
if (Count == Capacity)
{
Grow();
}
_heap[_tail++] = item;
BubbleUp(_tail - 1);
}
private void BubbleUp(int i)
{
while (i != 0 && !Dominates(_heap[Parent(i)], _heap[i]))
{
Swap(i, Parent(i));
i = Parent(i);
}
}
public T GetMin()
{
if (Count == 0)
{
throw new InvalidOperationException("Heap is empty");
}
return _heap[0];
}
public T ExtractDominating()
{
if (Count == 0)
{
throw new InvalidOperationException("Heap is empty");
}
T result = _heap[0];
_tail--;
Swap(_tail, 0);
BubbleDown(0);
return result;
}
private void BubbleDown(int i)
{
while (true)
{
int num = Dominating(i);
if (num == i)
{
break;
}
Swap(i, num);
i = num;
}
}
private int Dominating(int i)
{
int dominatingNode = i;
dominatingNode = GetDominating(YoungChild(i), dominatingNode);
return GetDominating(OldChild(i), dominatingNode);
}
private int GetDominating(int newNode, int dominatingNode)
{
if (newNode < _tail && !Dominates(_heap[dominatingNode], _heap[newNode]))
{
return newNode;
}
return dominatingNode;
}
private void Swap(int i, int j)
{
T val = _heap[i];
_heap[i] = _heap[j];
_heap[j] = val;
}
private static int Parent(int i)
{
return (i + 1) / 2 - 1;
}
private static int YoungChild(int i)
{
return (i + 1) * 2 - 1;
}
private static int OldChild(int i)
{
return YoungChild(i) + 1;
}
private void Grow()
{
int num = _capacity * 2 + 1;
T[] array = new T[num];
Array.Copy(_heap, array, _capacity);
_heap = array;
_capacity = num;
}
public IEnumerator<T> GetEnumerator()
{
return _heap.Take(Count).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
public class MaxHeap<T> : Heap<T>
{
public MaxHeap()
: this(Comparer<T>.Default)
{
}
public MaxHeap(Comparer<T> comparer)
: base(comparer)
{
}
public MaxHeap(IEnumerable<T> collection, Comparer<T> comparer)
: base(collection, comparer)
{
}
public MaxHeap(IEnumerable<T> collection)
: base(collection)
{
}
protected override bool Dominates(T x, T y)
{
return base.Comparer.Compare(x, y) >= 0;
}
}
public class MinHeap<T> : Heap<T>
{
public MinHeap()
: this(Comparer<T>.Default)
{
}
public MinHeap(Comparer<T> comparer)
: base(comparer)
{
}
public MinHeap(IEnumerable<T> collection)
: base(collection)
{
}
public MinHeap(IEnumerable<T> collection, Comparer<T> comparer)
: base(collection, comparer)
{
}
protected override bool Dominates(T x, T y)
{
return base.Comparer.Compare(x, y) <= 0;
}
}
public class Permutor<T> where T : IComparable<T>
{
private readonly List<T> _set;
public Permutor(IEnumerable<T> set)
{
_set = set.ToList();
}
public List<T> PermutationAt(long i)
{
List<T> list = new List<T>(_set.OrderBy((T e) => e).ToList());
for (long num = 0L; num < i - 1; num++)
{
NextPermutation(list);
}
return list;
}
public List<T> NextPermutation()
{
NextPermutation(_set);
return _set;
}
public bool NextPermutation(List<T> set)
{
int num = set.Count - 1;
while (num > 0 && set[num - 1].CompareTo(set[num]) >= 0)
{
num--;
}
if (num <= 0)
{
return false;
}
int num2 = set.Count - 1;
while (set[num2].CompareTo(set[num - 1]) <= 0)
{
num2--;
}
T value = set[num - 1];
set[num - 1] = set[num2];
set[num2] = value;
num2 = set.Count - 1;
while (num < num2)
{
value = set[num];
set[num] = set[num2];
set[num2] = value;
num++;
num2--;
}
return true;
}
}
public static class Permutation
{
public static List<List<T>> AllPermutations<T>(this IEnumerable<T> seed)
{
List<T> list = new List<T>(seed);
return Permute(list, 0, list.Count - 1).ToList();
}
public static List<List<T>> PermutationsOfSize<T>(this IEnumerable<T> seed, int size)
{
if (seed.Count() < size)
{
return new List<List<T>>();
}
return seed.PermutationsOfSize(new List<T>(), size).ToList();
}
private static IEnumerable<List<T>> PermutationsOfSize<T>(this IEnumerable<T> seed, List<T> set, int size)
{
if (size == 0)
{
foreach (List<T> item in set.AllPermutations())
{
yield return item;
}
yield break;
}
List<T> seedAsList = seed.ToList();
for (int i = 0; i < seedAsList.Count; i++)
{
List<T> set2 = new List<T>(set) { seedAsList[i] };
foreach (List<T> item2 in seedAsList.Skip(i + 1).PermutationsOfSize(set2, size - 1))
{
yield return item2;
}
}
}
private static IEnumerable<List<T>> Permute<T>(List<T> set, int start, int end)
{
if (start == end)
{
yield return new List<T>(set);
yield break;
}
for (int i = start; i <= end; i++)
{
Swap(set, start, i);
foreach (List<T> item in Permute(set, start + 1, end))
{
yield return item;
}
Swap(set, start, i);
}
}
private static void Swap<T>(List<T> set, int a, int b)
{
T value = set[a];
set[a] = set[b];
set[b] = value;
}
public static IEnumerable<List<T>> Cycles<T>(IEnumerable<T> seed)
{
LinkedList<T> set = new LinkedList<T>(seed);
for (int i = 0; i < set.Count; i++)
{
yield return new List<T>(set);
T value = set.First();
set.RemoveFirst();
set.AddLast(value);
}
}
public static bool IsPermutationOf<T>(this IEnumerable<T> set, IEnumerable<T> other)
{
return new HashSet<T>(set).SetEquals(other);
}
}
}
namespace FuzzySharp.SimilarityRatio
{
public static class ScorerCache
{
private static readonly ConcurrentDictionary<Type, IRatioScorer> s_scorerCache = new ConcurrentDictionary<Type, IRatioScorer>();
public static IRatioScorer Get<T>() where T : IRatioScorer, new()
{
return s_scorerCache.GetOrAdd(typeof(T), new T());
}
}
}
namespace FuzzySharp.SimilarityRatio.Strategy
{
internal class DefaultRatioStrategy
{
public static int Calculate(string input1, string input2)
{
if (input1.Length == 0 || input2.Length == 0)
{
return 0;
}
return (int)Math.Round(100.0 * Levenshtein.GetRatio(input1, input2));
}
}
internal class PartialRatioStrategy
{
public static int Calculate(string input1, string input2)
{
if (input1.Length == 0 || input2.Length == 0)
{
return 0;
}
string text;
string text2;
if (input1.Length < input2.Length)
{
text = input1;
text2 = input2;
}
else
{
text = input2;
text2 = input1;
}
MatchingBlock[] matchingBlocks = Levenshtein.GetMatchingBlocks(text, text2);
List<double> list = new List<double>();
MatchingBlock[] array = matchingBlocks;
foreach (MatchingBlock matchingBlock in array)
{
int num = matchingBlock.DestPos - matchingBlock.SourcePos;
int num2 = ((num > 0) ? num : 0);
int num3 = num2 + text.Length;
if (num3 > text2.Length)
{
num3 = text2.Length;
}
string s = text2.Substring(num2, num3 - num2);
double ratio = Levenshtein.GetRatio(text, s);
if (ratio > 0.995)
{
return 100;
}
list.Add(ratio);
}
return (int)Math.Round(100.0 * list.Max());
}
}
}
namespace FuzzySharp.SimilarityRatio.Strategy.Generic
{
internal class DefaultRatioStrategy<T> where T : IEquatable<T>
{
public static int Calculate(T[] input1, T[] input2)
{
if (input1.Length == 0 || input2.Length == 0)
{
return 0;
}
return (int)Math.Round(100.0 * Levenshtein.GetRatio(input1, input2));
}
}
internal class PartialRatioStrategy<T> where T : IEquatable<T>
{
public static int Calculate(T[] input1, T[] input2)
{
if (input1.Length == 0 || input2.Length == 0)
{
return 0;
}
T[] array;
T[] array2;
if (input1.Length < input2.Length)
{
array = input1;
array2 = input2;
}
else
{
array = input2;
array2 = input1;
}
MatchingBlock[] matchingBlocks = Levenshtein.GetMatchingBlocks(array, array2);
List<double> list = new List<double>();
MatchingBlock[] array3 = matchingBlocks;
foreach (MatchingBlock matchingBlock in array3)
{
int num = matchingBlock.DestPos - matchingBlock.SourcePos;
int num2 = ((num > 0) ? num : 0);
int num3 = num2 + array.Length;
if (num3 > array2.Length)
{
num3 = array2.Length;
}
IEnumerable<T> input3 = array2.Skip(num2).Take(num3 - num2);
double ratio = Levenshtein.GetRatio(array, input3);
if (ratio > 0.995)
{
return 100;
}
list.Add(ratio);
}
return (int)Math.Round(100.0 * list.Max());
}
}
}
namespace FuzzySharp.SimilarityRatio.Scorer
{
public interface IRatioScorer
{
int Score(string input1, string input2);
int Score(string input1, string input2, PreprocessMode preprocessMode);
}
public abstract class ScorerBase : IRatioScorer
{
public abstract int Score(string input1, string input2);
public int Score(string input1, string input2, PreprocessMode preprocessMode)
{
Func<string, string> preprocessor = StringPreprocessorFactory.GetPreprocessor(preprocessMode);
input1 = preprocessor(input1);
input2 = preprocessor(input2);
return Score(input1, input2);
}
}
}
namespace FuzzySharp.SimilarityRatio.Scorer.StrategySensitive
{
public class DefaultRatioScorer : SimpleRatioScorerBase
{
protected override Func<string, string, int> Scorer => DefaultRatioStrategy.Calculate;
}
public class PartialRatioScorer : SimpleRatioScorerBase
{
protected override Func<string, string, int> Scorer => PartialRatioStrategy.Calculate;
}
public abstract class SimpleRatioScorerBase : StrategySensitiveScorerBase
{
public override int Score(string input1, string input2)
{
return Scorer(input1, input2);
}
}
public abstract class StrategySensitiveScorerBase : ScorerBase
{
protected abstract Func<string, string, int> Scorer { get; }
}
public class PartialTokenAbbreviationScorer : TokenAbbreviationScorerBase
{
protected override Func<string, string, int> Scorer => PartialRatioStrategy.Calculate;
}
public class TokenAbbreviationScorer : TokenAbbreviationScorerBase
{
protected override Func<string, string, int> Scorer => DefaultRatioStrategy.Calculate;
}
public abstract class TokenAbbreviationScorerBase : StrategySensitiveScorerBase
{
public override int Score(string input1, string input2)
{
string text;
string text2;
if (input1.Length < input2.Length)
{
text = input1;
text2 = input2;
}
else
{
text = input2;
text2 = input1;
}
if ((double)text2.Length / (double)text.Length < 1.5)
{
return 0;
}
string[] array = (from Match m in Regex.Matches(text2, "[a-zA-Z]+")
select m.Value).ToArray();
string[] array2 = (from Match m in Regex.Matches(text, "[a-zA-Z]+")
select m.Value).ToArray();
if (array2.Length > 4)
{
return 0;
}
string[] seed;
string[] array3;
if (array.Length > array2.Length)
{
seed = array;
array3 = array2;
}
else
{
seed = array2;
array3 = array;
}
List<List<string>> list = seed.PermutationsOfSize(array3.Length);
List<int> list2 = new List<int>();
foreach (List<string> item in list)
{
double num = 0.0;
for (int i = 0; i < array3.Length; i++)
{
string text3 = item[i];
string text4 = array3[i];
if (StringContainsInOrder(text3, text4))
{
int num2 = Scorer(text3, text4);
num += (double)num2;
}
}
list2.Add((int)(num / (double)array3.Length));
}
if (list2.Count != 0)
{
return list2.Max();
}
return 0;
}
private bool StringContainsInOrder(string s1, string s2)
{
if (s1.Length < s2.Length)
{
return false;
}
int num = 0;
for (int i = 0; i < s1.Length; i++)
{
if (s2[num] == s1[i])
{
num++;
}
if (num == s2.Length)
{
return true;
}
if (i + s2.Length - num == s1.Length)
{
return false;
}
}
return false;
}
}
public class PartialTokenDifferenceScorer : TokenDifferenceScorerBase
{
protected override Func<string[], string[], int> Scorer => PartialRatioStrategy<string>.Calculate;
}
public class TokenDifferenceScorer : TokenDifferenceScorerBase
{
protected override Func<string[], string[], int> Scorer => DefaultRatioStrategy<string>.Calculate;
}
public abstract class TokenDifferenceScorerBase : StrategySensitiveScorerBase<string>, IRatioScorer
{
public override int Score(string[] input1, string[] input2)
{
return Scorer(input1, input2);
}
public int Score(string input1, string input2)
{
string[] input3 = (from s in Regex.Split(input1, "\\s+")
where s.Any()
orderby s
select s).ToArray();
string[] input4 = (from s in Regex.Split(input2, "\\s+")
where s.Any()
orderby s
select s).ToArray();
return Score(input3, input4);
}
public int Score(string input1, string input2, PreprocessMode preprocessMode)
{
Func<string, string> preprocessor = StringPreprocessorFactory.GetPreprocessor(preprocessMode);
input1 = preprocessor(input1);
input2 = preprocessor(input2);
return Score(input1, input2);
}
}
public class PartialTokenInitialismScorer : TokenInitialismScorerBase
{
protected override Func<string, string, int> Scorer => PartialRatioStrategy.Calculate;
}
public class TokenInitialismScorer : TokenInitialismScorerBase
{
protected override Func<string, string, int> Scorer => DefaultRatioStrategy.Calculate;
}
public abstract class TokenInitialismScorerBase : StrategySensitiveScorerBase
{
public override int Score(string input1, string input2)
{
string text;
string text2;
if (input1.Length < input2.Length)
{
text = input1;
text2 = input2;
}
else
{
text = input2;
text2 = input1;
}
if ((double)text2.Length / (double)text.Length < 3.0)
{
return 0;
}
IEnumerable<char> values = from s in Regex.Split(text2, "\\s+")
where s.Any()
select s[0];
return Scorer(string.Join("", values), text);
}
}
public class PartialTokenSetScorer : TokenSetScorerBase
{
protected override Func<string, string, int> Scorer => PartialRatioStrategy.Calculate;
}
public class TokenSetScorer : TokenSetScorerBase
{
protected override Func<string, string, int> Scorer => DefaultRatioStrategy.Calculate;
}
public abstract class TokenSetScorerBase : StrategySensitiveScorerBase
{
public override int Score(string input1, string input2)
{
HashSet<string> hashSet = new HashSet<string>(from s in Regex.Split(input1, "\\s+")
where s.Any()
select s);
HashSet<string> hashSet2 = new HashSet<string>(from s in Regex.Split(input2, "\\s+")
where s.Any()
select s);
string text = string.Join(" ", from s in hashSet.Intersect(hashSet2)
orderby s
select s).Trim();
string text2 = (text + " " + string.Join(" ", from s in hashSet.Except(hashSet2)
orderby s
select s)).Trim();
string arg = (text + " " + string.Join(" ", from s in hashSet2.Except(hashSet)
orderby s
select s)).Trim();
return new int[3]
{
Scorer(text, text2),
Scorer(text, arg),
Scorer(text2, arg)
}.Max();
}
}
public class PartialTokenSortScorer : TokenSortScorerBase
{
protected override Func<string, string, int> Scorer => PartialRatioStrategy.Calculate;
}
public abstract class TokenSortScorerBase : StrategySensitiveScorerBase
{
public override int Score(string input1, string input2)
{
string arg = string.Join(" ", from s in Regex.Split(input1, "\\s+")
where s.Any()
orderby s
select s).Trim();
string arg2 = string.Join(" ", from s in Regex.Split(input2, "\\s+")
where s.Any()
orderby s
select s).Trim();
return Scorer(arg, arg2);
}
}
public class TokenSortScorer : TokenSortScorerBase
{
protected override Func<string, string, int> Scorer => DefaultRatioStrategy.Calculate;
}
}
namespace FuzzySharp.SimilarityRatio.Scorer.StrategySensitive.Generic
{
public abstract class StrategySensitiveScorerBase<T> : ScorerBase<T> where T : IEquatable<T>
{
protected abstract Func<T[], T[], int> Scorer { get; }
}
}
namespace FuzzySharp.SimilarityRatio.Scorer.Generic
{
public interface IRatioScorer<in T> where T : IEquatable<T>
{
int Score(T[] input1, T[] input2);
}
public abstract class ScorerBase<T> : IRatioScorer<T> where T : IEquatable<T>
{
public abstract int Score(T[] input1, T[] input2);
}
}
namespace FuzzySharp.SimilarityRatio.Scorer.Composite
{
public class WeightedRatioScorer : ScorerBase
{
private static double UNBASE_SCALE = 0.95;
private static double PARTIAL_SCALE = 0.9;
private static bool TRY_PARTIALS = true;
public override int Score(string input1, string input2)
{
int length = input1.Length;
int length2 = input2.Length;
if (length == 0 || length2 == 0)
{
return 0;
}
bool flag = TRY_PARTIALS;
double uNBASE_SCALE = UNBASE_SCALE;
double num = PARTIAL_SCALE;
int num2 = Fuzz.Ratio(input1, input2);
double num3 = (double)Math.Max(length, length2) / (double)Math.Min(length, length2);
if (num3 < 1.5)
{
flag = false;
}
if (num3 > 8.0)
{
num = 0.6;
}
if (flag)
{
double num4 = (double)Fuzz.PartialRatio(input1, input2) * num;
double num5 = (double)Fuzz.TokenSortRatio(input1, input2) * uNBASE_SCALE * num;
double num6 = (double)Fuzz.TokenSetRatio(input1, input2) * uNBASE_SCALE * num;
return (int)Math.Round(new double[4] { num2, num4, num5, num6 }.Max());
}
double num7 = (double)Fuzz.TokenSortRatio(input1, input2) * uNBASE_SCALE;
double num8 = (double)Fuzz.TokenSetRatio(input1, input2) * uNBASE_SCALE;
return (int)Math.Round(new double[3] { num2, num7, num8 }.Max());
}
}
}
namespace FuzzySharp.PreProcess
{
public enum PreprocessMode
{
Full,
None
}
internal class StringPreprocessorFactory
{
private static string pattern = "[^ a-zA-Z0-9]";
private static string Default(string input)
{
input = Regex.Replace(input, pattern, " ");
input = input.ToLower();
return input.Trim();
}
public static Func<string, string> GetPreprocessor(PreprocessMode mode)
{
return mode switch
{
PreprocessMode.Full => Default,
PreprocessMode.None => (string s) => s,
_ => throw new InvalidOperationException($"Invalid string preprocessor mode: {mode}"),
};
}
}
}
namespace FuzzySharp.Extractor
{
public class ExtractedResult<T> : IComparable<ExtractedResult<T>>
{
public readonly T Value;
public readonly int Score;
public readonly int Index;
public ExtractedResult(T value, int score)
{
Value = value;
Score = score;
}
public ExtractedResult(T value, int score, int index)
{
Value = value;
Score = score;
Index = index;
}
public int CompareTo(ExtractedResult<T> other)
{
return Comparer<int>.Default.Compare(Score, other.Score);
}
public override string ToString()
{
if (typeof(T) == typeof(string))
{
return $"(string: {Value}, score: {Score}, index: {Index})";
}
return $"(value: {Value.ToString()}, score: {Score}, index: {Index})";
}
}
public static class ResultExtractor
{
public static IEnumerable<ExtractedResult<T>> ExtractWithoutOrder<T>(T query, IEnumerable<T> choices, Func<T, string> processor, IRatioScorer scorer, int cutoff = 0)
{
int index = 0;
string processedQuery = processor(query);
foreach (T choice in choices)
{
int num = scorer.Score(processedQuery, processor(choice));
if (num >= cutoff)
{
yield return new ExtractedResult<T>(choice, num, index);
}
index++;
}
}
public static ExtractedResult<T> ExtractOne<T>(T query, IEnumerable<T> choices, Func<T, string> processor, IRatioScorer calculator, int cutoff = 0)
{
return ExtractWithoutOrder(query, choices, processor, calculator, cutoff).Max();
}
public static IEnumerable<ExtractedResult<T>> ExtractSorted<T>(T query, IEnumerable<T> choices, Func<T, string> processor, IRatioScorer calculator, int cutoff = 0)
{
return from r in ExtractWithoutOrder(query, choices, processor, calculator, cutoff)
orderby r.Score descending
select r;
}
public static IEnumerable<ExtractedResult<T>> ExtractTop<T>(T query, IEnumerable<T> choices, Func<T, string> processor, IRatioScorer calculator, int limit, int cutoff = 0)
{
return ExtractWithoutOrder(query, choices, processor, calculator, cutoff).MaxN(limit).Reverse();
}
}
}
namespace FuzzySharp.Extensions
{
public static class EnumerableExtensions
{
public static IEnumerable<T> MaxN<T>(this IEnumerable<T> source, int n) where T : IComparable<T>
{
MinHeap<T> queue = new MinHeap<T>(Comparer<T>.Create((T x, T y) => x.CompareTo(y)));
foreach (T item in source)
{
if (queue.Count < n)
{
queue.Add(item);
}
else if (item.CompareTo(queue.GetMin()) > 0)
{
queue.ExtractDominating();
queue.Add(item);
}
}
for (int i = 0; i < n; i++)
{
if (queue.Count <= 0)
{
break;
}
yield return queue.ExtractDominating();
}
}
public static IEnumerable<T> MaxNBy<T, TVal>(this IEnumerable<T> source, int n, Func<T, TVal> selector) where TVal : IComparable<TVal>
{
MinHeap<T> queue = new MinHeap<T>(Comparer<T>.Create((T x, T y) => selector(x).CompareTo(selector(y))));
foreach (T item in source)
{
if (queue.Count < n)
{
queue.Add(item);
}
else if (selector(item).CompareTo(selector(queue.GetMin())) > 0)
{
queue.ExtractDominating();
queue.Add(item);
}
}
for (int i = 0; i < n; i++)
{
if (queue.Count <= 0)
{
break;
}
yield return queue.ExtractDominating();
}
}
}
}
namespace FuzzySharp.Edits
{
public enum EditType
{
DELETE,
EQUAL,
INSERT,
REPLACE,
KEEP
}
public class EditOp
{
public EditType EditType { get; set; }
public int SourcePos { get; set; }
public int DestPos { get; set; }
public override string ToString()
{
return $"{EditType}({SourcePos}, {DestPos})";
}
}
public class MatchingBlock
{
public int SourcePos { get; set; }
public int DestPos { get; set; }
public int Length { get; set; }
public override string ToString()
{
return $"({SourcePos},{DestPos},{Length})";
}
}
public class OpCode
{
public EditType EditType { get; set; }
public int SourceBegin { get; set; }
public int SourceEnd { get; set; }
public int DestBegin { get; set; }
public int DestEnd { get; set; }
public override string ToString()
{
return $"{EditType}({SourceBegin},{SourceEnd},{DestBegin},{DestEnd})";
}
}
}