public void Copy() { StringTransducer copy = StringTransducer.Copy(); StringInferenceTestUtilities.TestTransducerValue(copy, "important", "important", 1.0); StringInferenceTestUtilities.TestTransducerValue(copy, "important", "i", 0.0); StringInferenceTestUtilities.TestTransducerValue(copy, "important", "imp", 0.0); StringInferenceTestUtilities.TestTransducerValue(copy, "important", "t", 0.0); StringInferenceTestUtilities.TestTransducerValue(copy, "important", "mpo", 0.0); StringInferenceTestUtilities.TestTransducerValue(copy, string.Empty, string.Empty, 1.0); StringInferenceTestUtilities.TestTransducerValue(copy, "important", string.Empty, 0.0); StringInferenceTestUtilities.TestTransducerValue(copy, string.Empty, "important", 0.0); //// Test that projection on Copy() doesn't change the automaton StringAutomaton automaton = StringAutomaton.ConstantOn(2.0, "a", "ab", "ac"); automaton = automaton.Sum(StringAutomaton.ConstantOn(1.0, "a")); automaton = automaton.Sum(StringAutomaton.Constant(2.0)); automaton = automaton.Product(StringAutomaton.Constant(3.0)); StringAutomaton automatonClone = copy.ProjectSource(automaton); Assert.Equal(automaton, automatonClone); }
public void CopyElement() { StringTransducer copy = StringTransducer.CopyElement(DiscreteChar.OneOf('a', 'b')); StringInferenceTestUtilities.TestTransducerValue(copy, "a", "a", 1.0); StringInferenceTestUtilities.TestTransducerValue(copy, "b", "b", 1.0); StringInferenceTestUtilities.TestTransducerValue(copy, "a", "b", 0.0); StringInferenceTestUtilities.TestTransducerValue(copy, "b", "a", 0.0); StringInferenceTestUtilities.TestTransducerValue(copy, string.Empty, string.Empty, 0.0); StringInferenceTestUtilities.TestTransducerValue(copy, "bb", "bb", 0.0); StringInferenceTestUtilities.TestTransducerValue(copy, "bab", "bab", 0.0); StringInferenceTestUtilities.TestTransducerValue(copy, "bab", "ba", 0.0); //// Tests that projection on CopyElement(elements) shrinks the support StringAutomaton automaton = StringAutomaton.ConstantOn(2.0, "a", "ab", "ac"); automaton = automaton.Sum(StringAutomaton.ConstantOn(1.0, "a")); automaton = automaton.Sum(StringAutomaton.Constant(2.0)); automaton = automaton.Product(StringAutomaton.Constant(3.0)); for (int i = 0; i < 2; ++i) { StringInferenceTestUtilities.TestValue(automaton, 15, "a"); StringInferenceTestUtilities.TestValue(automaton, 6.0, "b"); StringInferenceTestUtilities.TestValue(automaton, i == 0 ? 6.0 : 0.0, string.Empty); StringInferenceTestUtilities.TestValue(automaton, i == 0 ? 12.0 : 0.0, "ac", "ab"); automaton = copy.ProjectSource(automaton); } }
public void PointMassDetection() { StringDistribution s1 = StringDistribution.OneOf("hello", "world", "people"); StringDistribution s2 = StringDistribution.OneOf("greetings", "people", "animals"); StringDistribution point1 = s1.Product(s2); Assert.True(point1.IsPointMass); Assert.Equal("people", point1.Point); StringDistribution point2 = StringDistribution.OneOf(new Dictionary <string, double> { { "a", 3.0 }, { "b", 0.0 } }); Assert.True(point2.IsPointMass); Assert.Equal("a", point2.Point); StringDistribution point3 = StringDistribution.CaseInvariant("123"); Assert.True(point3.IsPointMass); Assert.Equal("123", point3.Point); StringDistribution point4 = StringDistribution.Char('Z'); Assert.True(point4.IsPointMass); Assert.Equal("Z", point4.Point); StringDistribution point5 = StringDistribution.OneOf(1.0, StringDistribution.String("!"), 0.0, StringDistribution.Any()); Assert.True(point5.IsPointMass); Assert.Equal("!", point5.Point); StringDistribution point6 = StringDistribution.Repeat('@', minTimes: 3, maxTimes: 3); Assert.True(point6.IsPointMass); Assert.Equal("@@@", point6.Point); StringDistribution point7 = StringDistribution.String("hello").Append(StringDistribution.String(" world")); Assert.True(point7.IsPointMass); Assert.Equal("hello world", point7.Point); string point = string.Empty; StringAutomaton point8Automaton = StringAutomaton.Empty(); for (int i = 0; i < 22; ++i) { const string PointElement = "a"; point8Automaton.AppendInPlace(StringAutomaton.ConstantOn(1.0, PointElement, PointElement)); point += PointElement; } StringDistribution point8 = StringDistribution.FromWeightFunction(point8Automaton); Assert.True(point8.IsPointMass); Assert.Equal(point, point8.Point); }
/// <summary> /// Creates an automaton for validating the correctness of argument placeholders for a given argument. /// </summary> /// <param name="argToValidateIndex">The index of the argument to validate.</param> /// <param name="argNames">The names of all arguments.</param> /// <returns>The created automaton.</returns> private static StringAutomaton GetArgumentValidatingAutomaton(int argToValidateIndex, IReadOnlyList <string> argNames) { Debug.Assert( argNames != null && argNames.All(name => !string.IsNullOrEmpty(name)), "A valid array of argument names must be provided."); Debug.Assert( argToValidateIndex >= 0 && argToValidateIndex < argNames.Count, "The provided argument index must be a valid index in the argument name array."); string argListKey = ArgumentListToDictionaryKey(argToValidateIndex, argNames); StringAutomaton result; if (ArgsToValidatingAutomaton.TryGetValue(argListKey, out result)) { return(result); } // Accepts placeholder for the current argument StringAutomaton checkBracesForCurrentArg = StringAutomaton.ConstantOn(1.0, "{" + argNames[argToValidateIndex] + "}"); StringAutomaton checkBracesForOtherArgs = DisallowBracesAutomaton.Clone(); if (argNames.Count > 1) { // Skips placeholders for every argument except the current one StringAutomaton skipOtherArgs = StringAutomaton.ConstantOnElement(1.0, '{'); skipOtherArgs.AppendInPlace(StringAutomaton.ConstantOn(1.0, argNames.Where((arg, index) => index != argToValidateIndex))); skipOtherArgs.AppendInPlace(StringAutomaton.ConstantOnElement(1.0, '}')); // Accepts placeholders for arguments other than current, with arbitrary intermediate text checkBracesForOtherArgs.AppendInPlace(skipOtherArgs); checkBracesForOtherArgs = StringAutomaton.Repeat(checkBracesForOtherArgs, minTimes: 0); checkBracesForOtherArgs.AppendInPlace(DisallowBracesAutomaton); } // Checks the placeholder for the current argument, then skips placeholders for other arguments StringAutomaton validateArgumentThenOtherArguments = checkBracesForCurrentArg.Clone(); validateArgumentThenOtherArguments.AppendInPlace(checkBracesForOtherArgs); if (!RequirePlaceholderForEveryArgument) { // Make this block optional validateArgumentThenOtherArguments = StringAutomaton.Sum( validateArgumentThenOtherArguments, StringAutomaton.ConstantOn(1.0, string.Empty)); } // Accepts placeholders for arguments other then current, then for the current argument, then again other placeholders result = checkBracesForOtherArgs.Clone(); result.AppendInPlace(validateArgumentThenOtherArguments); result.TryDeterminize(); ArgsToValidatingAutomaton[argListKey] = result; return(result); }
/// <summary> /// Tests if a transducer projects each of given strings to a zero function. /// </summary> /// <param name="transducer">The transducer.</param> /// <param name="strings">The strings to test.</param> public static void TestIfTransducerRejects(StringTransducer transducer, params string[] strings) { foreach (string str in strings) { var res = transducer.ProjectSource(StringAutomaton.ConstantOn(1.0, str)); Assert.True(res.IsZero()); var res2 = transducer.ProjectSource(str); Assert.True(res2.IsZero()); } }
public void Repeat4() { StringAutomaton automaton = StringAutomaton.ConstantOn(2.0, "aa"); automaton = StringAutomaton.Repeat(automaton, minTimes: 0); StringInferenceTestUtilities.TestValue(automaton, 0.0, "a", "aaa"); StringInferenceTestUtilities.TestValue(automaton, 1.0, string.Empty); StringInferenceTestUtilities.TestValue(automaton, 2.0, "aa"); StringInferenceTestUtilities.TestValue(automaton, 4.0, "aaaa"); }
public void Repeat1() { StringAutomaton automaton = StringAutomaton.ConstantOn(2.0, "a"); StringTransducer repeat = StringTransducer.Repeat(StringTransducer.Consume(automaton), 1, 3); StringInferenceTestUtilities.TestTransducerValue(repeat, "a", string.Empty, 2.0); StringInferenceTestUtilities.TestTransducerValue(repeat, "aa", string.Empty, 4.0); StringInferenceTestUtilities.TestTransducerValue(repeat, "aaa", string.Empty, 8.0); StringInferenceTestUtilities.TestTransducerValue(repeat, "aaaa", string.Empty, 0.0); StringInferenceTestUtilities.TestTransducerValue(repeat, string.Empty, string.Empty, 0.0); StringInferenceTestUtilities.TestTransducerValue(repeat, string.Empty, "aaa", 0.0); }
public void Repeat2() { StringAutomaton automaton = StringAutomaton.ConstantOn(2.0, "a", string.Empty); StringTransducer repeat = StringTransducer.Repeat(StringTransducer.Copy(automaton), 1, 2); StringInferenceTestUtilities.TestTransducerValue(repeat, "a", "a", 10.0); StringInferenceTestUtilities.TestTransducerValue(repeat, "aa", "aa", 4.0); StringInferenceTestUtilities.TestTransducerValue(repeat, string.Empty, string.Empty, 6.0); StringInferenceTestUtilities.TestTransducerValue(repeat, "aaa", "aaa", 0.0); StringInferenceTestUtilities.TestTransducerValue(repeat, "aa", "a", 0.0); StringInferenceTestUtilities.TestTransducerValue(repeat, string.Empty, "a", 0.0); }
public void Repeat1() { StringAutomaton automaton = StringAutomaton.ConstantOn(1.0, "abc"); automaton.AppendInPlace(StringAutomaton.Constant(2.0, DiscreteChar.Upper())); StringInferenceTestUtilities.TestValue(automaton, 0.0, string.Empty, "ab", "abcab", "XYZ"); StringInferenceTestUtilities.TestValue(automaton, 2.0, "abc", "abcX", "abcXXYZ"); StringAutomaton loopyAutomaton = StringAutomaton.Repeat(automaton); StringInferenceTestUtilities.TestValue(loopyAutomaton, 0.0, string.Empty, "ab", "abcab", "XYZ"); StringInferenceTestUtilities.TestValue(loopyAutomaton, 2.0, "abc", "abcA"); StringInferenceTestUtilities.TestValue(loopyAutomaton, 4.0, "abcabc", "abcabcX", "abcABCabc", "abcXabcYZ"); StringInferenceTestUtilities.TestValue(loopyAutomaton, 8.0, "abcabcabc", "abcXabcYabcZZ"); }
public void Repeat3() { StringAutomaton automaton = StringAutomaton.ConstantOn(2.0, "a", "aa"); StringTransducer repeat = StringTransducer.Repeat(StringTransducer.Replace(automaton, automaton), minTimes: 0); StringInferenceTestUtilities.TestTransducerValue(repeat, string.Empty, string.Empty, 1.0); // TODO: it's not clear from the definition that this should hold StringInferenceTestUtilities.TestTransducerValue(repeat, "a", "a", 4.0); StringInferenceTestUtilities.TestTransducerValue(repeat, "a", "aa", 4.0); StringInferenceTestUtilities.TestTransducerValue(repeat, "aa", "aa", 20.0); StringInferenceTestUtilities.TestTransducerValue(repeat, "aaa", "aa", 32.0); StringInferenceTestUtilities.TestTransducerValue(repeat, "aa", "aaa", 32.0); StringInferenceTestUtilities.TestTransducerValue(repeat, "a", "aaa", 0.0); StringInferenceTestUtilities.TestTransducerValue(repeat, string.Empty, "a", 0.0); StringInferenceTestUtilities.TestTransducerValue(repeat, "b", "b", 0.0); }
/// <summary> /// Tests if a transducer has the desired value on a given pair of strings. /// </summary> /// <param name="transducer">The transducer.</param> /// <param name="string1">The first string.</param> /// <param name="string2">The second string.</param> /// <param name="trueValue">The desired value of the transducer on the strings.</param> public static void TestTransducerValue( StringTransducer transducer, string string1, string string2, double trueValue) { // Test ProjectSource(sequence) var result = transducer.ProjectSource(string1); TestValue(result, trueValue, string2); // Test ProjectSource(func) TestTransducerProjection(transducer, StringAutomaton.ConstantOn(1.0, string1), string2, trueValue); // Test GetValue() double value = transducer.GetValue(string1, string2); Assert.Equal(trueValue, value, LogValueEps); }
public void CopyAutomaton() { StringAutomaton automaton = StringAutomaton.ConstantOn(1.0, "prefix1", "prefix2"); automaton.AppendInPlace(StringAutomaton.Constant(1.0, DiscreteChar.Lower())); automaton.AppendInPlace(StringAutomaton.Constant(1.0, DiscreteChar.Upper())); automaton.AppendInPlace("!"); StringTransducer copy = StringTransducer.Copy(automaton); StringInferenceTestUtilities.TestTransducerValue(copy, "prefix1!", "prefix1!", 1.0); StringInferenceTestUtilities.TestTransducerValue(copy, "prefix2!", "prefix2!", 1.0); StringInferenceTestUtilities.TestTransducerValue(copy, "prefix1lower!", "prefix1lower!", 1.0); StringInferenceTestUtilities.TestTransducerValue(copy, "prefix2UPPER!", "prefix2UPPER!", 1.0); StringInferenceTestUtilities.TestTransducerValue(copy, "prefix1lowerUPPER!", "prefix1lowerUPPER!", 1.0); StringInferenceTestUtilities.TestIfTransducerRejects(copy, "prefix1lower", "prefix2UPPER", "!", "prefix1lowerUPPER"); StringInferenceTestUtilities.TestTransducerProjection(copy, automaton, "prefix1!", 1.0); StringInferenceTestUtilities.TestTransducerProjection(copy, automaton, "prefix2!", 1.0); StringInferenceTestUtilities.TestTransducerProjection(copy, automaton, "prefix1lower!", 1.0); StringInferenceTestUtilities.TestTransducerProjection(copy, automaton, "prefix2UPPER!", 1.0); StringInferenceTestUtilities.TestTransducerProjection(copy, automaton, "prefix1lowerUPPER!", 1.0); StringAutomaton subsetAutomaton = StringAutomaton.ConstantOn(2.0, "prefix1"); subsetAutomaton.AppendInPlace(StringAutomaton.ConstantOn(3.0, "lll", "mmm")); subsetAutomaton.AppendInPlace(StringAutomaton.ConstantOn(1.5, "!", "U!")); StringInferenceTestUtilities.TestTransducerProjection(copy, subsetAutomaton, "prefix1lll!", 9.0); StringInferenceTestUtilities.TestTransducerProjection(copy, subsetAutomaton, "prefix1mmmU!", 9.0); StringInferenceTestUtilities.TestTransducerProjection(copy, subsetAutomaton, "prefix1!", 0.0); StringInferenceTestUtilities.TestTransducerProjection(copy, subsetAutomaton, "prefix2lower!", 0.0); StringInferenceTestUtilities.TestTransducerProjection(copy, subsetAutomaton, "prefix2U!", 0.0); StringAutomaton supersetAutomaton = StringAutomaton.ConstantOn(1.5, "pr"); supersetAutomaton.AppendInPlace(StringAutomaton.Constant(2.0)); StringInferenceTestUtilities.TestTransducerProjection(copy, supersetAutomaton, "prefix1!", 3.0); StringInferenceTestUtilities.TestTransducerProjection(copy, supersetAutomaton, "prefix2!", 3.0); StringInferenceTestUtilities.TestTransducerProjection(copy, supersetAutomaton, "prefix1lower!", 3.0); StringInferenceTestUtilities.TestTransducerProjection(copy, supersetAutomaton, "prefix2UPPER!", 3.0); StringInferenceTestUtilities.TestTransducerProjection(copy, supersetAutomaton, "prefix1lowerUPPER!", 3.0); StringInferenceTestUtilities.TestTransducerProjection(copy, supersetAutomaton, "prefix11!", 0.0); StringInferenceTestUtilities.TestTransducerProjection(copy, supersetAutomaton, "prefix1lowerUPPERlower!", 0.0); StringInferenceTestUtilities.TestTransducerProjection(copy, supersetAutomaton, "prrrrr!", 0.0); }
public void Product3() { StringAutomaton weights1 = StringAutomaton.Sum( StringAutomaton.ConstantOn(1.0, "a"), StringAutomaton.ConstantOn(2.0, "b"), StringAutomaton.ConstantOn(4.0, "c")); StringAutomaton weights2 = StringAutomaton.Sum( StringAutomaton.ConstantOn(2.0, "a"), StringAutomaton.ConstantOn(5.0, "b"), StringAutomaton.ConstantOn(7.0, "c")); StringDistribution dist1 = StringDistribution.FromWeightFunction(weights1); StringDistribution dist2 = StringDistribution.FromWeightFunction(weights2); StringDistribution product = dist1.Product(dist2); StringInferenceTestUtilities.TestProbability(product, 2.0 / 40.0, "a"); StringInferenceTestUtilities.TestProbability(product, 10.0 / 40.0, "b"); StringInferenceTestUtilities.TestProbability(product, 28.0 / 40.0, "c"); }
public void CopySequence1() { StringTransducer copySequence = StringTransducer.Copy("important"); StringInferenceTestUtilities.TestTransducerValue(copySequence, "important", "important", 1.0); StringInferenceTestUtilities.TestTransducerValue(copySequence, "important", "imp", 0.0); StringInferenceTestUtilities.TestTransducerValue(copySequence, "important", "ortant", 0.0); StringInferenceTestUtilities.TestTransducerValue(copySequence, "important", string.Empty, 0.0); StringInferenceTestUtilities.TestTransducerProjection(copySequence, StringAutomaton.ConstantOn(3.0, "important"), "important", 3.0); StringInferenceTestUtilities.TestTransducerProjection(copySequence, StringAutomaton.ConstantOn(1.5, "important", "not important"), "important", 1.5); StringInferenceTestUtilities.TestTransducerProjection(copySequence, StringAutomaton.Constant(2.0), "important", 2.0); StringInferenceTestUtilities.TestTransducerProjection( copySequence, StringAutomaton.Constant(2.0).Append(StringAutomaton.ConstantOn(3.0, "nt")), "important", 6.0); StringInferenceTestUtilities.TestIfTransducerRejects(copySequence, string.Empty, "nothing is important", "importance", "imp", "ortant", "a"); }
public void Repeat3() { StringAutomaton automaton = StringAutomaton.ConstantOn(1.0, "a"); automaton.AppendInPlace(StringAutomaton.ConstantOn(1.0, "a")); automaton = StringAutomaton.Sum(automaton, StringAutomaton.ConstantOn(1.0, "a")); StringInferenceTestUtilities.TestValue(automaton, 0.0, string.Empty, "aaa", "ab", "X"); StringInferenceTestUtilities.TestValue(automaton, 1.0, "a", "aa"); // Yep, it turns out you can compute the Fibonacci sequence with an automaton StringAutomaton loopyAutomaton = StringAutomaton.Repeat(automaton); StringInferenceTestUtilities.TestValue(loopyAutomaton, 0.0, string.Empty, "ab"); StringInferenceTestUtilities.TestValue(loopyAutomaton, 1.0, "a"); StringInferenceTestUtilities.TestValue(loopyAutomaton, 2.0, "aa"); StringInferenceTestUtilities.TestValue(loopyAutomaton, 3.0, "aaa"); StringInferenceTestUtilities.TestValue(loopyAutomaton, 5.0, "aaaa"); StringInferenceTestUtilities.TestValue(loopyAutomaton, 8.0, "aaaaa"); }
/// <summary> /// An implementation of <see cref="StrAverageConditional(StringDistribution, IList{StringDistribution}, IReadOnlyList{string})"/> /// specialized for some cases for performance reasons. /// </summary> /// <param name="format">The message from <c>format</c>.</param> /// <param name="allowedArgs">The message from <c>args</c>, truncated to allowed values and converted to automata.</param> /// <param name="argNames">The names of the arguments.</param> /// <param name="withGroups">Whether the result should mark different arguments with groups.</param> /// <returns> /// Result distribution if there is an optimized implementation available for the provided parameters. /// <see langword="null"/> otherwise. /// </returns> /// <remarks> /// Supports the case of point mass <paramref name="format"/>. /// </remarks> private static StringDistribution TryOptimizedStrAverageConditionalImpl( StringDistribution format, IList <StringAutomaton> allowedArgs, IReadOnlyList <string> argNames, bool withGroups) { if (!format.IsPointMass) { // Fall back to the general case return(null); } // Check braces for correctness & replace placeholders with arguments simultaneously var result = StringAutomaton.Builder.ConstantOn(Weight.One, string.Empty); bool[] argumentSeen = new bool[allowedArgs.Count]; int openingBraceIndex = format.Point.IndexOf("{", StringComparison.Ordinal), closingBraceIndex = -1; while (openingBraceIndex != -1) { // Add the part of the format before the placeholder result.Append(StringAutomaton.ConstantOn(1.0, format.Point.Substring(closingBraceIndex + 1, openingBraceIndex - closingBraceIndex - 1))); // Find next opening and closing braces closingBraceIndex = format.Point.IndexOf("}", openingBraceIndex + 1, StringComparison.Ordinal); int nextOpeningBraceIndex = format.Point.IndexOf("{", openingBraceIndex + 1, StringComparison.Ordinal); // Opening brace must be followed by a closing brace if (closingBraceIndex == -1 || (nextOpeningBraceIndex != -1 && nextOpeningBraceIndex < closingBraceIndex)) { return(StringDistribution.Zero()); } string argumentName = format.Point.Substring(openingBraceIndex + 1, closingBraceIndex - openingBraceIndex - 1); int argumentIndex = argNames.IndexOf(argumentName); // Unknown or previously seen argument found if (argumentIndex == -1 || argumentSeen[argumentIndex]) { return(StringDistribution.Zero()); } // Replace the placeholder by the argument result.Append(allowedArgs[argumentIndex], withGroups ? argumentIndex + 1 : 0); // Mark the argument as 'seen' argumentSeen[argumentIndex] = true; openingBraceIndex = nextOpeningBraceIndex; } // There should be no closing braces after the last opening brace if (format.Point.IndexOf('}', closingBraceIndex + 1) != -1) { return(StringDistribution.Zero()); } if (RequirePlaceholderForEveryArgument && argumentSeen.Any(seen => !seen)) { // Some argument wasn't present although it was required return(StringDistribution.Zero()); } // Append the part of the format after the last placeholder result.Append(StringAutomaton.ConstantOn(1.0, format.Point.Substring(closingBraceIndex + 1, format.Point.Length - closingBraceIndex - 1))); return(StringDistribution.FromWorkspace(result.GetAutomaton())); }
/// <summary> /// An implementation of <see cref="FormatAverageConditional(StringDistribution, IList{StringDistribution}, IReadOnlyList{string})"/> /// specialized for some cases for performance reasons. /// </summary> /// <param name="str">The message from <c>str</c>.</param> /// <param name="allowedArgs">The message from <c>args</c>, truncated to allowed values and converted to automata.</param> /// <param name="argNames">The names of the arguments.</param> /// <param name="resultDist">The computed result.</param> /// <returns> /// <see langword="true"/> if there is an optimized implementation available for the provided parameters, /// and <paramref name="resultDist"/> has been computed using it. /// <see langword="false"/> otherwise. /// </returns> /// <remarks> /// Supports the case of point mass <paramref name="str"/> and <paramref name="allowedArgs"/>, /// where each of the arguments is present in <paramref name="str"/> at most once and the occurrences /// are non-overlapping. /// </remarks> private static bool TryOptimizedFormatAverageConditionalImpl( StringDistribution str, IList <StringAutomaton> allowedArgs, IReadOnlyList <string> argNames, out StringDistribution resultDist) { resultDist = null; string[] allowedArgPoints = Util.ArrayInit(allowedArgs.Count, i => allowedArgs[i].TryComputePoint()); if (!str.IsPointMass || !allowedArgPoints.All(argPoint => argPoint != null && SubstringOccurrencesCount(str.Point, argPoint) <= 1)) { // Fall back to the general case return(false); } // Obtain arguments present in 'str' (ordered by position) var argPositions = allowedArgPoints.Select((arg, argIndex) => Tuple.Create(argIndex, str.Point.IndexOf(arg, StringComparison.Ordinal))) .Where(t => t.Item2 != -1) .OrderBy(t => t.Item2) .ToList(); if (RequirePlaceholderForEveryArgument && argPositions.Count != allowedArgs.Count) { // Some argument is not in 'str' resultDist = StringDistribution.Zero(); return(true); } StringAutomaton result = StringAutomaton.ConstantOn(1.0, string.Empty); int curArgumentIndex = -1; int curArgumentPos = -1; int curArgumentLength = 1; for (int i = 0; i < argPositions.Count; ++i) { int prevArgumentIndex = curArgumentIndex; int prevArgumentPos = curArgumentPos; int prevArgumentLength = curArgumentLength; curArgumentIndex = argPositions[i].Item1; curArgumentPos = argPositions[i].Item2; curArgumentLength = allowedArgPoints[curArgumentIndex].Length; if (prevArgumentIndex != -1 && curArgumentPos < prevArgumentPos + prevArgumentLength) { // It's easier to fall back to the general case in case of overlapping arguments return(false); } // Append the contents of 'str' preceeding the current argument result.AppendInPlace(str.Point.Substring(prevArgumentPos + prevArgumentLength, curArgumentPos - prevArgumentPos - prevArgumentLength)); // The format may have included either the text ot the placeholder string argName = "{" + argNames[curArgumentIndex] + "}"; if (RequirePlaceholderForEveryArgument) { result.AppendInPlace(StringAutomaton.ConstantOn(1.0, argName)); } else { result.AppendInPlace(StringAutomaton.ConstantOn(1.0, argName, allowedArgPoints[curArgumentIndex])); } } // Append the rest of 'str' result.AppendInPlace(str.Point.Substring(curArgumentPos + curArgumentLength, str.Point.Length - curArgumentPos - curArgumentLength)); resultDist = StringDistribution.FromWorkspace(result); return(true); }