Browse Source

Random: clarify ranges, ensure them at the call site #427

netstandard
Christoph Ruegg 10 years ago
parent
commit
8e131b481b
  1. 12
      src/Numerics/Numerics.csproj
  2. 9
      src/Numerics/Properties/Resources.Designer.cs
  3. 57
      src/Numerics/Properties/Resources.resx
  4. 108
      src/Numerics/Random/RandomSource.cs

12
src/Numerics/Numerics.csproj

@ -167,6 +167,11 @@
<Compile Include="LinearRegression\WeightedRegression.cs" /> <Compile Include="LinearRegression\WeightedRegression.cs" />
<Compile Include="LinearRegression\SimpleRegression.cs" /> <Compile Include="LinearRegression\SimpleRegression.cs" />
<Compile Include="LinearRegression\Util.cs" /> <Compile Include="LinearRegression\Util.cs" />
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="Providers\LinearAlgebra\Acml\AcmlLinearAlgebraProvider.Complex.cs" /> <Compile Include="Providers\LinearAlgebra\Acml\AcmlLinearAlgebraProvider.Complex.cs" />
<Compile Include="Providers\LinearAlgebra\Acml\AcmlLinearAlgebraProvider.Complex32.cs" /> <Compile Include="Providers\LinearAlgebra\Acml\AcmlLinearAlgebraProvider.Complex32.cs" />
<Compile Include="Providers\LinearAlgebra\Acml\AcmlLinearAlgebraProvider.Double.cs" /> <Compile Include="Providers\LinearAlgebra\Acml\AcmlLinearAlgebraProvider.Double.cs" />
@ -428,11 +433,6 @@
<Compile Include="Interpolation\SplineBoundaryCondition.cs" /> <Compile Include="Interpolation\SplineBoundaryCondition.cs" />
<Compile Include="Precision.cs" /> <Compile Include="Precision.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="Random\RandomSource.cs" /> <Compile Include="Random\RandomSource.cs" />
<Compile Include="Random\Mcg31m1.cs" /> <Compile Include="Random\Mcg31m1.cs" />
<Compile Include="Random\Mcg59.cs" /> <Compile Include="Random\Mcg59.cs" />
@ -472,8 +472,8 @@
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="Properties\Resources.resx"> <EmbeddedResource Include="Properties\Resources.resx">
<Generator>PublicResXFileCodeGenerator</Generator> <Generator>PublicResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
<SubType>Designer</SubType> <SubType>Designer</SubType>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource> </EmbeddedResource>
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

9
src/Numerics/Properties/Resources.Designer.cs

@ -314,6 +314,15 @@ namespace MathNet.Numerics.Properties {
} }
} }
/// <summary>
/// Looks up a localized string similar to In the specified range, the exclusive maximum must be greater than the inclusive minimum..
/// </summary>
public static string ArgumentMaxExclusiveMustBeLargerThanMinInclusive {
get {
return ResourceManager.GetString("ArgumentMaxExclusiveMustBeLargerThanMinInclusive", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to In the specified range, the minimum is greater than maximum.. /// Looks up a localized string similar to In the specified range, the minimum is greater than maximum..
/// </summary> /// </summary>

57
src/Numerics/Properties/Resources.resx

@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<root> <root>
<!-- <!--
Microsoft ResX Schema Microsoft ResX Schema
Version 2.0 Version 2.0
The primary goals of this format is to allow a simple XML format The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes various data types are done through the TypeConverter classes
associated with the data types. associated with the data types.
Example: Example:
... ado.net/XML headers & schema ... ... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader> <resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader> <resheader name="version">2.0</resheader>
@ -26,36 +26,36 @@
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment> <comment>This is a comment</comment>
</data> </data>
There are any number of "resheader" rows that contain simple There are any number of "resheader" rows that contain simple
name/value pairs. name/value pairs.
Each data row contains a name, and value. The row also contains a Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture. text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the Classes that don't support this are serialized and stored with the
mimetype set. mimetype set.
The mimetype is used for serialized objects, and tells the The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly: extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below. read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64 mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding. : and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64 mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding. : and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64 mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter : using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding. : and then encoded with base64 encoding.
--> -->
@ -282,6 +282,9 @@
<data name="ArgumentMinValueGreaterThanMaxValue" xml:space="preserve"> <data name="ArgumentMinValueGreaterThanMaxValue" xml:space="preserve">
<value>In the specified range, the minimum is greater than maximum.</value> <value>In the specified range, the minimum is greater than maximum.</value>
</data> </data>
<data name="ArgumentMaxExclusiveMustBeLargerThanMinInclusive" xml:space="preserve">
<value>In the specified range, the exclusive maximum must be greater than the inclusive minimum.</value>
</data>
<data name="ArgumentUpperBoundMustBeLargerThanOrEqualToLowerBound" xml:space="preserve"> <data name="ArgumentUpperBoundMustBeLargerThanOrEqualToLowerBound" xml:space="preserve">
<value>The upper bound must be at least as large as the lower bound.</value> <value>The upper bound must be at least as large as the lower bound.</value>
</data> </data>

108
src/Numerics/Random/RandomSource.cs

@ -3,7 +3,7 @@
// http://numerics.mathdotnet.com // http://numerics.mathdotnet.com
// http://github.com/mathnet/mathnet-numerics // http://github.com/mathnet/mathnet-numerics
// //
// Copyright (c) 2009-2015 Math.NET // Copyright (c) 2009-2016 Math.NET
// //
// Permission is hereby granted, free of charge, to any person // Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation // obtaining a copy of this software and associated documentation
@ -133,28 +133,39 @@ namespace MathNet.Numerics.Random
return DoSampleInteger(); return DoSampleInteger();
} }
} }
else
return DoSampleInteger(); {
return DoSampleInteger();
}
} }
/// <summary> /// <summary>
/// Returns a random number less then a specified maximum. /// Returns a random number less then a specified maximum.
/// </summary> /// </summary>
/// <param name="maxExclusive">The exclusive upper bound of the random number returned.</param> /// <param name="maxExclusive">The exclusive upper bound of the random number returned. Range: maxExclusive ≥ 1.</param>
/// <returns>A 32-bit signed integer less than <paramref name="maxExclusive"/>.</returns> /// <returns>A 32-bit signed integer less than <paramref name="maxExclusive"/>.</returns>
/// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="maxExclusive"/> is negative. </exception> /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="maxExclusive"/> is zero or negative.</exception>
public sealed override int Next(int maxExclusive) public sealed override int Next(int maxExclusive)
{ {
// Invalid case: Zero and less are not valid use cases.
if (maxExclusive <= 0) if (maxExclusive <= 0)
{ {
throw new ArgumentException(Resources.ArgumentMustBePositive); throw new ArgumentException(Resources.ArgumentMustBePositive);
} }
// Fast case: Only zero is allowed to be returned. No sampling is needed.
if (maxExclusive == 1)
{
return 0;
}
// Simple case: standard range
if (maxExclusive == int.MaxValue) if (maxExclusive == int.MaxValue)
{ {
return Next(); return Next();
} }
// Sample with 1 < maxExclusive < int.MaxValue
if (_threadSafe) if (_threadSafe)
{ {
lock (_lock) lock (_lock)
@ -162,28 +173,39 @@ namespace MathNet.Numerics.Random
return DoSampleInteger(maxExclusive); return DoSampleInteger(maxExclusive);
} }
} }
else
return DoSampleInteger(maxExclusive); {
return DoSampleInteger(maxExclusive);
}
} }
/// <summary> /// <summary>
/// Returns a random number within a specified range. /// Returns a random number within a specified range.
/// </summary> /// </summary>
/// <param name="minInclusive">The inclusive lower bound of the random number returned.</param> /// <param name="minInclusive">The inclusive lower bound of the random number returned.</param>
/// <param name="maxExclusive">The exclusive upper bound of the random number returned. <paramref name="maxExclusive"/> must be greater than or equal to <paramref name="minInclusive"/>.</param> /// <param name="maxExclusive">The exclusive upper bound of the random number returned. Range: maxExclusive > minExclusive.</param>
/// <returns> /// <returns>
/// A 32-bit signed integer greater than or equal to <paramref name="minInclusive"/> and less than <paramref name="maxExclusive"/>; that is, the range of return values includes <paramref name="minInclusive"/> but not <paramref name="maxExclusive"/>. If <paramref name="minInclusive"/> equals <paramref name="maxExclusive"/>, <paramref name="minInclusive"/> is returned. /// A 32-bit signed integer greater than or equal to <paramref name="minInclusive"/> and less than <paramref name="maxExclusive"/>; that is, the range of return values includes <paramref name="minInclusive"/> but not <paramref name="maxExclusive"/>. If <paramref name="minInclusive"/> equals <paramref name="maxExclusive"/>, <paramref name="minInclusive"/> is returned.
/// </returns> /// </returns>
/// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="minInclusive"/> is greater than <paramref name="maxExclusive"/>. </exception> /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="minInclusive"/> is greater than <paramref name="maxExclusive"/>. </exception>
public sealed override int Next(int minInclusive, int maxExclusive) public sealed override int Next(int minInclusive, int maxExclusive)
{ {
if (minInclusive > maxExclusive) // Invalid case: empty range.
if (minInclusive >= maxExclusive)
{ {
throw new ArgumentException(Resources.ArgumentMinValueGreaterThanMaxValue); throw new ArgumentException(Resources.ArgumentMaxExclusiveMustBeLargerThanMinInclusive);
}
// Fast case: Only minInclusive is allowed to be returned. No sampling is needed.
if (maxExclusive == minInclusive + 1)
{
return minInclusive;
} }
// Simple case: simple range
if (minInclusive == 0) if (minInclusive == 0)
{ {
// Simple case: standard range
if (maxExclusive == int.MaxValue) if (maxExclusive == int.MaxValue)
{ {
return Next(); return Next();
@ -192,6 +214,7 @@ namespace MathNet.Numerics.Random
return Next(maxExclusive); return Next(maxExclusive);
} }
// Sample with minInclusive + 1 < maxExclusive
if (_threadSafe) if (_threadSafe)
{ {
lock (_lock) lock (_lock)
@ -199,8 +222,10 @@ namespace MathNet.Numerics.Random
return DoSampleInteger(minInclusive, maxExclusive); return DoSampleInteger(minInclusive, maxExclusive);
} }
} }
else
return DoSampleInteger(minInclusive, maxExclusive); {
return DoSampleInteger(minInclusive, maxExclusive);
}
} }
/// <summary> /// <summary>
@ -243,15 +268,30 @@ namespace MathNet.Numerics.Random
/// Fills an array with random numbers within a specified range. /// Fills an array with random numbers within a specified range.
/// </summary> /// </summary>
/// <param name="values">The array to fill with random values.</param> /// <param name="values">The array to fill with random values.</param>
/// <param name="maxExclusive">The exclusive upper bound of the random number returned.</param> /// <param name="maxExclusive">The exclusive upper bound of the random number returned. Range: maxExclusive ≥ 1.</param>
public void NextInt32s(int[] values, int maxExclusive) public void NextInt32s(int[] values, int maxExclusive)
{ {
// Invalid case: Zero and less are not valid use cases.
if (maxExclusive <= 0)
{
throw new ArgumentException(Resources.ArgumentMustBePositive);
}
// Fast case: Only zero is allowed to be returned. No sampling is needed.
if (maxExclusive == 1)
{
Array.Clear(values, 0, values.Length);
return;
}
// Simple case: standard range
if (maxExclusive == int.MaxValue) if (maxExclusive == int.MaxValue)
{ {
NextInt32s(values); NextInt32s(values);
return; return;
} }
// Sample with 1 < maxExclusive < int.MaxValue
if (_threadSafe) if (_threadSafe)
{ {
lock (_lock) lock (_lock)
@ -271,27 +311,46 @@ namespace MathNet.Numerics.Random
} }
} }
/// <summary>
/// Returns an array with random 32-bit signed integers within the specified range.
/// </summary>
/// <param name="count">The size of the array to fill.</param>
/// <param name="maxExclusive">The exclusive upper bound of the random number returned. Range: maxExclusive ≥ 1.</param>
public int[] NextInt32s(int count, int maxExclusive)
{
var values = new int[count];
NextInt32s(values, maxExclusive);
return values;
}
/// <summary> /// <summary>
/// Fills an array with random numbers within a specified range. /// Fills an array with random numbers within a specified range.
/// </summary> /// </summary>
/// <param name="values">The array to fill with random values.</param> /// <param name="values">The array to fill with random values.</param>
/// <param name="minInclusive">The inclusive lower bound of the random number returned.</param> /// <param name="minInclusive">The inclusive lower bound of the random number returned.</param>
/// <param name="maxExclusive">The exclusive upper bound of the random number returned. <paramref name="maxExclusive"/> must be greater than or equal to <paramref name="minInclusive"/>.</param> /// <param name="maxExclusive">The exclusive upper bound of the random number returned. Range: maxExclusive > minExclusive.</param>
public void NextInt32s(int[] values, int minInclusive, int maxExclusive) public void NextInt32s(int[] values, int minInclusive, int maxExclusive)
{ {
if (minInclusive > maxExclusive) // Invalid case: empty range.
if (minInclusive >= maxExclusive)
{ {
throw new ArgumentException(Resources.ArgumentMinValueGreaterThanMaxValue); throw new ArgumentException(Resources.ArgumentMaxExclusiveMustBeLargerThanMinInclusive);
} }
if (maxExclusive == int.MaxValue && minInclusive == 0) // Fast case: Only minInclusive is allowed to be returned. No sampling is needed.
if (maxExclusive == minInclusive + 1)
{ {
NextInt32s(values); for (var i = 0; i < values.Length; i++)
{
values[i] = minInclusive;
}
return; return;
} }
// Simple case: simple range
if (minInclusive == 0) if (minInclusive == 0)
{ {
// Simple case: standard range
if (maxExclusive == int.MaxValue) if (maxExclusive == int.MaxValue)
{ {
NextInt32s(values); NextInt32s(values);
@ -302,6 +361,7 @@ namespace MathNet.Numerics.Random
return; return;
} }
// Sample with minInclusive + 1 < maxExclusive
if (_threadSafe) if (_threadSafe)
{ {
lock (_lock) lock (_lock)
@ -326,7 +386,7 @@ namespace MathNet.Numerics.Random
/// </summary> /// </summary>
/// <param name="count">The size of the array to fill.</param> /// <param name="count">The size of the array to fill.</param>
/// <param name="minInclusive">The inclusive lower bound of the random number returned.</param> /// <param name="minInclusive">The inclusive lower bound of the random number returned.</param>
/// <param name="maxExclusive">The exclusive upper bound of the random number returned. <paramref name="maxExclusive"/> must be greater than or equal to <paramref name="minInclusive"/>.</param> /// <param name="maxExclusive">The exclusive upper bound of the random number returned. Range: maxExclusive > minExclusive.</param>
public int[] NextInt32s(int count, int minInclusive, int maxExclusive) public int[] NextInt32s(int count, int minInclusive, int maxExclusive)
{ {
var values = new int[count]; var values = new int[count];
@ -359,7 +419,7 @@ namespace MathNet.Numerics.Random
/// Returns an infinite sequence of random numbers within a specified range. /// Returns an infinite sequence of random numbers within a specified range.
/// </summary> /// </summary>
/// <param name="minInclusive">The inclusive lower bound of the random number returned.</param> /// <param name="minInclusive">The inclusive lower bound of the random number returned.</param>
/// <param name="maxExclusive">The exclusive upper bound of the random number returned. <paramref name="maxExclusive"/> must be greater than or equal to <paramref name="minInclusive"/>.</param> /// <param name="maxExclusive">The exclusive upper bound of the random number returned. Range: maxExclusive > minExclusive.</param>
public IEnumerable<int> NextInt32Sequence(int minInclusive, int maxExclusive) public IEnumerable<int> NextInt32Sequence(int minInclusive, int maxExclusive)
{ {
if (minInclusive > maxExclusive) if (minInclusive > maxExclusive)
@ -461,6 +521,7 @@ namespace MathNet.Numerics.Random
{ {
return 0; return 0;
} }
var bytes = new byte[4]; var bytes = new byte[4];
DoSampleBytes(bytes); DoSampleBytes(bytes);
@ -484,6 +545,7 @@ namespace MathNet.Numerics.Random
{ {
return 0; return 0;
} }
var bytes = new byte[8]; var bytes = new byte[8];
DoSampleBytes(bytes); DoSampleBytes(bytes);
@ -498,7 +560,7 @@ namespace MathNet.Numerics.Random
/// <summary> /// <summary>
/// Returns a random 32-bit signed integer within the specified range. /// Returns a random 32-bit signed integer within the specified range.
/// </summary> /// </summary>
/// <param name="maxExclusive">The exclusive upper bound of the random number returned.</param> /// <param name="maxExclusive">The exclusive upper bound of the random number returned. Range: maxExclusive ≥ 1.</param>
protected virtual int DoSampleInteger(int maxExclusive) protected virtual int DoSampleInteger(int maxExclusive)
{ {
// Fast case: Only a single number is allowed to be returned // Fast case: Only a single number is allowed to be returned
@ -507,7 +569,7 @@ namespace MathNet.Numerics.Random
{ {
return 0; return 0;
} }
// non-biased implementation // non-biased implementation
// (biased: return (int)(DoSample() * maxExclusive);) // (biased: return (int)(DoSample() * maxExclusive);)
@ -535,7 +597,7 @@ namespace MathNet.Numerics.Random
/// Returns a random 32-bit signed integer within the specified range. /// Returns a random 32-bit signed integer within the specified range.
/// </summary> /// </summary>
/// <param name="minInclusive">The inclusive lower bound of the random number returned.</param> /// <param name="minInclusive">The inclusive lower bound of the random number returned.</param>
/// <param name="maxExclusive">The exclusive upper bound of the random number returned. <paramref name="maxExclusive"/> must be greater than or equal to <paramref name="minInclusive"/>.</param> /// <param name="maxExclusive">The exclusive upper bound of the random number returned. Range: maxExclusive > minExclusive.</param>
protected virtual int DoSampleInteger(int minInclusive, int maxExclusive) protected virtual int DoSampleInteger(int minInclusive, int maxExclusive)
{ {
return DoSampleInteger(maxExclusive - minInclusive) + minInclusive; return DoSampleInteger(maxExclusive - minInclusive) + minInclusive;

Loading…
Cancel
Save