From 61cb43ed81840ec298fed7cc36e0bdc554723db1 Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Thu, 20 Aug 2009 15:24:49 +0800 Subject: [PATCH] globalization: double dense vector parsing Signed-off-by: Christoph Ruegg --- src/MathNet.Numerics.4.5.resharper | 2 +- .../LinearAlgebra/Double/DenseVector.cs | 163 +++++++++++++++++- src/Numerics/LinearAlgebra/Double/Vector.cs | 16 +- src/Numerics/Properties/Resources.Designer.cs | 2 +- src/Numerics/Properties/Resources.resx | 2 +- ...ingTest.cs => ComplexTest.TextHandling.cs} | 6 +- .../Double/DenseVectorTest.TextHandling.cs | 131 ++++++++++++++ src/UnitTests/UnitTests.csproj | 3 +- 8 files changed, 317 insertions(+), 8 deletions(-) rename src/UnitTests/ComplexTests/{ComplexTextHandlingTest.cs => ComplexTest.TextHandling.cs} (98%) create mode 100644 src/UnitTests/LinearAlgebraTests/Double/DenseVectorTest.TextHandling.cs diff --git a/src/MathNet.Numerics.4.5.resharper b/src/MathNet.Numerics.4.5.resharper index acbdd02a..376414cf 100644 --- a/src/MathNet.Numerics.4.5.resharper +++ b/src/MathNet.Numerics.4.5.resharper @@ -33,7 +33,7 @@ Bluestein false - 80 + 120 diff --git a/src/Numerics/LinearAlgebra/Double/DenseVector.cs b/src/Numerics/LinearAlgebra/Double/DenseVector.cs index d29b6bfc..a0ec57b7 100644 --- a/src/Numerics/LinearAlgebra/Double/DenseVector.cs +++ b/src/Numerics/LinearAlgebra/Double/DenseVector.cs @@ -29,9 +29,11 @@ namespace MathNet.Numerics.LinearAlgebra.Double { using System; - + using System.Collections.Generic; + using System.Globalization; using Algorithms; using Algorithms.LinearAlgebra; + using NumberTheory; using Properties; using Threading; @@ -627,5 +629,164 @@ namespace MathNet.Numerics.LinearAlgebra.Double } #endregion + + #region Parse Functions + + /// + /// Creates a double dense vector based on a string. The string can be in the following formats (without the + /// quotes): 'n', 'n,n,..', '(n,n,..)', '[n,n,...]', where n is a double. + /// + /// + /// A double dense vector containing the values specified by the given string. + /// + /// + /// The string to parse. + /// + public static DenseVector Parse(string value) + { + return Parse(value, null); + } + + /// + /// Creates a double dense vector based on a string. The string can be in the following formats (without the + /// quotes): 'n', 'n,n,..', '(n,n,..)', '[n,n,...]', where n is a double. + /// + /// + /// A double dense vector containing the values specified by the given string. + /// + /// + /// the string to parse. + /// + /// + /// An that supplies culture-specific formatting information. + /// + public static DenseVector Parse(string value, IFormatProvider formatProvider) + { + if (value == null) + { + throw new ArgumentNullException(value); + } + + value = value.Trim(); + if (value.Length == 0) + { + throw new FormatException(); + } + + // strip out parens + if (value.StartsWith("(", StringComparison.Ordinal)) + { + if (!value.EndsWith(")", StringComparison.Ordinal)) + { + throw new FormatException(); + } + + value = value.Substring(1, value.Length - 2).Trim(); + } + + if (value.StartsWith("[", StringComparison.Ordinal)) + { + if (!value.EndsWith("]", StringComparison.Ordinal)) + { + throw new FormatException(); + } + + value = value.Substring(1, value.Length - 2).Trim(); + } + + // keywords + var textInfo = formatProvider.GetTextInfo(); + var keywords = new[] { textInfo.ListSeparator }; + + // lexing + var tokens = new LinkedList(); + GlobalizationHelper.Tokenize(tokens.AddFirst(value), keywords, 0); + var token = tokens.First; + + if (token == null || tokens.Count.IsEven()) + { + throw new FormatException(); + } + + // parsing + var data = new double[(tokens.Count + 1) >> 1]; + for (int i = 0; i < data.Length; i++) + { + if (token == null || token.Value == textInfo.ListSeparator) + { + throw new FormatException(); + } + + data[i] = Double.Parse(token.Value, NumberStyles.Any, formatProvider); + + token = token.Next; + if(token != null) + { + token = token.Next; + } + } + + return new DenseVector(data); + } + + /// + /// Converts the string representation of a real dense vector to double-precision dense vector equivalent. + /// A return value indicates whether the conversion succeeded or failed. + /// + /// + /// A string containing a real vector to convert. + /// + /// + /// The parsed value. + /// + /// + /// If the conversion succeeds, the result will contain a complex number equivalent to value. + /// Otherwise the result will be null. + /// + public static bool TryParse(string value, out DenseVector result) + { + return TryParse(value, null, out result); + } + + /// + /// Converts the string representation of a real dense vector to double-precision dense vector equivalent. + /// A return value indicates whether the conversion succeeded or failed. + /// + /// + /// A string containing a real vector to convert. + /// + /// + /// An that supplies culture-specific formatting information about value. + /// + /// + /// The parsed value. + /// + /// + /// If the conversion succeeds, the result will contain a complex number equivalent to value. + /// Otherwise the result will be null. + /// + public static bool TryParse(string value, IFormatProvider formatProvider, out DenseVector result) + { + bool ret; + try + { + result = Parse(value, formatProvider); + ret = true; + } + catch (ArgumentNullException) + { + result = null; + ret = false; + } + catch (FormatException) + { + result = null; + ret = false; + } + + return ret; + } + + #endregion } } \ No newline at end of file diff --git a/src/Numerics/LinearAlgebra/Double/Vector.cs b/src/Numerics/LinearAlgebra/Double/Vector.cs index 24e60d19..522f4d8e 100644 --- a/src/Numerics/LinearAlgebra/Double/Vector.cs +++ b/src/Numerics/LinearAlgebra/Double/Vector.cs @@ -932,6 +932,20 @@ namespace MathNet.Numerics.LinearAlgebra.Double #region IFormattable + /// + /// Returns a that represents this instance. + /// + /// + /// An that supplies culture-specific formatting information. + /// + /// + /// A that represents this instance. + /// + public string ToString(IFormatProvider formatProvider) + { + return ToString(null, formatProvider); + } + /// /// Returns a that represents this instance. /// @@ -939,7 +953,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double /// The format to use. /// /// - /// The format provider to use. + /// An that supplies culture-specific formatting information. /// /// /// A that represents this instance. diff --git a/src/Numerics/Properties/Resources.Designer.cs b/src/Numerics/Properties/Resources.Designer.cs index 81e461da..5228a1ce 100644 --- a/src/Numerics/Properties/Resources.Designer.cs +++ b/src/Numerics/Properties/Resources.Designer.cs @@ -223,7 +223,7 @@ namespace MathNet.Numerics.Properties { } /// - /// Looks up a localized string similar to Value must not be positive.. + /// Looks up a localized string similar to Value must be positive.. /// internal static string ArgumentMustBePositive { get { diff --git a/src/Numerics/Properties/Resources.resx b/src/Numerics/Properties/Resources.resx index 9130340e..001aa37d 100644 --- a/src/Numerics/Properties/Resources.resx +++ b/src/Numerics/Properties/Resources.resx @@ -259,7 +259,7 @@ Value must be greater than or equal to one. - Value must not be positive. + Value must be positive. A user defined provider has not been specified. diff --git a/src/UnitTests/ComplexTests/ComplexTextHandlingTest.cs b/src/UnitTests/ComplexTests/ComplexTest.TextHandling.cs similarity index 98% rename from src/UnitTests/ComplexTests/ComplexTextHandlingTest.cs rename to src/UnitTests/ComplexTests/ComplexTest.TextHandling.cs index 4c5050ef..de8106db 100644 --- a/src/UnitTests/ComplexTests/ComplexTextHandlingTest.cs +++ b/src/UnitTests/ComplexTests/ComplexTest.TextHandling.cs @@ -1,4 +1,4 @@ -// +// // Math.NET Numerics, part of the Math.NET Project // http://mathnet.opensourcedotnet.info // @@ -270,7 +270,9 @@ namespace MathNet.Numerics.UnitTests.ComplexTests [Row("1,")] [Row(",1")] [Row(null)] - public void TryParseReturnsFalseWhenGiveBadValueWithInvariant(string str) + [Row("()")] + [Row("( )")] + public void TryParseReturnsFalseWhenGivenBadValueWithInvariant(string str) { Complex z; var ret = Complex.TryParse(str, CultureInfo.InvariantCulture, out z); diff --git a/src/UnitTests/LinearAlgebraTests/Double/DenseVectorTest.TextHandling.cs b/src/UnitTests/LinearAlgebraTests/Double/DenseVectorTest.TextHandling.cs new file mode 100644 index 00000000..347bd845 --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Double/DenseVectorTest.TextHandling.cs @@ -0,0 +1,131 @@ +// +// Math.NET Numerics, part of the Math.NET Project +// http://mathnet.opensourcedotnet.info +// +// Copyright (c) 2009 Math.NET +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double +{ + using System; + using System.Globalization; + using LinearAlgebra.Double; + using MbUnit.Framework; + + [TestFixture] + public class DenseVectorTextHandlingTest + { + [Test] + [Row("2", "2")] + [Row("(3)", "3")] + [Row("[1,2,3]", "1,2,3")] + [Row(" [ 1 , 2 , 3 ] ", "1,2,3")] + [Row(" [ -1 , 2 , +3 ] ", "-1,2,3")] + [Row(" [1.2,3.4 , 5.6] ", "1.2,3.4,5.6")] + public void CanParseDoubleDenseVectorsWithInvariant(string stringToParse, string expectedToString) + { + var formatProvider = CultureInfo.InvariantCulture; + DenseVector vector = DenseVector.Parse(stringToParse, formatProvider); + + Assert.AreEqual(expectedToString, vector.ToString(formatProvider)); + } + + [Test] + [Row(" 1.2,3.4 , 5.6 ", "1.2,3.4,5.6", "en-US")] + [Row(" 1.2;3.4 ; 5.6 ", "1.2;3.4;5.6", "de-CH")] + [Row(" 1,2;3,4 ; 5,6 ", "1,2;3,4;5,6", "de-DE")] + public void CanParseDoubleDenseVectorsWithCulture(string stringToParse, string expectedToString, string culture) + { + var formatProvider = CultureInfo.GetCultureInfo(culture); + DenseVector vector = DenseVector.Parse(stringToParse, formatProvider); + + Assert.AreEqual(expectedToString, vector.ToString(formatProvider)); + } + + [Test] + [Row("15")] + [Row("1{0}2{1}3{0}4{1}5{0}6")] + public void CanParseDoubleDenseVectors(string vectorAsString) + { + var mappedString = String.Format( + vectorAsString, + CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator, + CultureInfo.CurrentCulture.TextInfo.ListSeparator); + + DenseVector vector = DenseVector.Parse(mappedString); + + Assert.AreEqual(mappedString, vector.ToString()); + } + + [Test] + [MultipleAsserts] + public void ParseThrowsFormatExceptionIfMissingClosingParen() + { + Assert.Throws(() => DenseVector.Parse("(1")); + Assert.Throws(() => DenseVector.Parse("[1")); + } + + [Test] + public void CanTryParseDoubleDenseVector() + { + var data = new[] { 1.2, 3.4, 5.6e-78 }; + var text = String.Format( + "{1}{0}{2}{0}{3}", + CultureInfo.CurrentCulture.TextInfo.ListSeparator, + data[0], + data[1], + data[2]); + + DenseVector vector; + var ret = DenseVector.TryParse(text, out vector); + Assert.IsTrue(ret); + AssertHelpers.AlmostEqualList(data, vector.Data, 1e-15); + + ret = DenseVector.TryParse(text, CultureInfo.CurrentCulture, out vector); + Assert.IsTrue(ret); + AssertHelpers.AlmostEqualList(data, vector.Data, 1e-15); + } + + [Test] + [Row(null)] + [Row("")] + [Row(",")] + [Row("1,")] + [Row(",1")] + [Row("1,2,")] + [Row(",1,2,")] + [Row("1,,2,,3")] + [Row("1e+")] + [Row("1e")] + [Row("()")] + [Row("[ ]")] + public void TryParseReturnsFalseWhenGivenBadValueWithInvariant(string str) + { + DenseVector vector; + var ret = DenseVector.TryParse(str, CultureInfo.InvariantCulture, out vector); + Assert.IsFalse(ret); + Assert.IsNull(vector); + } + } +} diff --git a/src/UnitTests/UnitTests.csproj b/src/UnitTests/UnitTests.csproj index 3e24b05d..42972add 100644 --- a/src/UnitTests/UnitTests.csproj +++ b/src/UnitTests/UnitTests.csproj @@ -62,7 +62,7 @@ - + @@ -82,6 +82,7 @@ +