''' <summary>
''' A port of the LoDMath static member class written in AS3 under the MIT license agreement.
''' </summary>
''' <remarks>
''' As per the license agrrement of the lodGameBox license agreement
'''
''' Copyright (c) 2009 Dylan Engelman
'''
'''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.
'''
''' http://code.google.com/p/lodgamebox/source/browse/trunk/com/lordofduct/util/LoDMath.as
''' </remarks>
Public NotInheritable Class LoDMath
#Region "Public ReadOnly Properties"
Public Const PI_hh As Double = 3.1415926535897931
Public Const PI As Double = 3.1415926535897931 ' Number pi
Public Const PI_2 As Double = 1.5707963267948966 ' PI / 2 OR 90 deg
Public Const PI_4 As Double = 0.78539816339744828 ' PI / 4 OR 45 deg
Public Const PI_8 As Double = 0.39269908169872414 ' PI / 8 OR 22.5 deg
Public Const PI_16 As Double = 0.19634954084936207 ' PI / 16 OR 11.25 deg
Public Const TWO_PI As Double = 6.2831853071795862 ' 2 * PI OR 180 deg
Public Const THREE_PI_2 As Double = 4.71238898038469 ' 3 * PI_2 OR 270 deg
Public Const E As Double = 2.71828182845905 ' Number e
Public Const LN10 As Double = 2.3025850929940459 ' ln(10)
Public Const LN2 As Double = 0.69314718055994529 ' ln(2)
Public Const LOG10E As Double = 0.43429448190325182 ' logB10(e)
Public Const LOG2E As Double = 1.4426950408889634 ' logB2(e)
Public Const SQRT1_2 As Double = 0.70710678118654757 ' sqrt( 1 / 2 )
Public Const SQRT2 As Double = 1.4142135623730951 ' sqrt( 2 )
Public Const DEG_TO_RAD As Double = 0.017453292519943295 ' PI / 180
Public Const RAD_TO_DEG As Double = 57.295779513082323 ' 180.0 / PI
Public Const B_16 As Integer = 65536 ' 2^16
Public Const B_31 As Long = 2147483648 ' 2^31
Public Const B_32 As Long = 4294967296 ' 2^32
Public Const B_48 As Long = 281474976710656 ' 2^48
Public Const B_53 As Long = 9007199254740992 ' 2^53 !!NOTE!! largest accurate double floating point whole value
''shared readonly because vb for some reason takes the written value of 2^63 as an overflow... though it ISN'T, so we convert from string at set
Public Shared ReadOnly B_63 As ULong = ULng("9223372036854775808") ' 2^63
Public Const B_64_m1 As ULong = ULong.MaxValue '18446744073709551615 or 2^64 - 1 or ULong.MaxValue...
Public Const ONE_THIRD As Double = 0.33333333333333331 ' 1.0/3.0
Public Const TWO_THIRDS As Double = 0.66666666666666663 ' 2.0/3.0
Public Const ONE_SIXTH As Double = 0.16666666666666666 ' 1.0/6.0
Public Const COS_PI_3 As Double = 0.8660254037844386 ' COS( PI / 3 )
Public Const SIN_2PI_3 As Double = 0.03654595 ' SIN( 2*PI/3 )
Public Const CIRCLE_ALPHA As Double = 0.55228474983079345 ' 4*(Math.sqrt(2)-1)/3.0
Public Const ONN As Boolean = True
Public Const OFF As Boolean = False
Public Const SHORT_EPSILON As Double = 0.1 ' round integer epsilon
Public Const PERC_EPSILON As Double = 0.001 ' percentage epsilon
Public Const EPSILON As Double = 0.0001 ' single float average epsilon
Public Const LONG_EPSILON As Double = 0.00000001 ' arbitrary 8 digit epsilon
''shared readOnly because you can't set a const from a method call
Public Shared ReadOnly MACHINE_EPSILON As Double = MathUtil.ComputeMachineEpsilon()
Public Shared Function ComputeMachineEpsilon() As Double
Dim fourThirds As Double = 4.0 / 3.0
Dim third As Double = fourThirds - 1.0
Dim one As Double = third + third + third
Return Math.Abs(1.0 - one)
End Function
#End Region
#Region "Public Shared Methods"
#Region "series"
Public Shared Function Summation(ByVal ParamArray arr As Short()) As Short
Dim result As Short = 0
Dim value As Short
For Each value In arr
result += value
Next
Return result
End Function
Public Shared Function Summation(ByVal ParamArray arr As Integer()) As Integer
Dim result As Integer = 0
Dim value As Integer
For Each value In arr
result += value
Next
Return result
End Function
Public Shared Function Summation(ByVal ParamArray arr As Long()) As Long
Dim result As Long = 0
Dim value As Long
For Each value In arr
result += value
Next
Return result
End Function
Public Shared Function Summation(ByVal ParamArray arr As Double()) As Double
Dim result As Double = 0
Dim value As Double
For Each value In arr
result += value
Next
Return result
End Function
Public Shared Function ProductSeries(ByVal ParamArray arr As Short()) As Double
If arr Is Nothing OrElse arr.Length = 0 Then Return Double.NaN
Dim result As Double = 1
Dim value As Short
For Each value In arr
result *= value
Next
Return result
End Function
Public Shared Function ProductSeries(ByVal ParamArray arr As Integer()) As Double
If arr Is Nothing OrElse arr.Length = 0 Then Return Double.NaN
Dim result As Double = 1
Dim value As Integer
For Each value In arr
result *= value
Next
Return result
End Function
Public Shared Function ProductSeries(ByVal ParamArray arr As Long()) As Double
If arr Is Nothing OrElse arr.Length = 0 Then Return Double.NaN
Dim result As Double = 1
Dim value As Long
For Each value In arr
result *= value
Next
Return result
End Function
Public Shared Function ProductSeries(ByVal ParamArray arr As Double()) As Double
If arr Is Nothing OrElse arr.Length = 0 Then Return Double.NaN
Dim result As Double = 1
Dim value As Double
For Each value In arr
result *= value
Next
Return result
End Function
#End Region
#Region "Value interpolating and warping"
''' <summary>
''' The average of an array of values
''' </summary>
''' <param name="values">An array of values</param>
''' <returns>the average</returns>
''' <remarks></remarks>
Public Shared Function Average(ByVal ParamArray values() As Short) As Double
Dim avg As Double = 0
For Each value As Double In values
avg += value
Next
Return avg / values.Length
End Function
Public Shared Function Average(ByVal ParamArray values() As Integer) As Double
Dim avg As Double = 0
For Each value As Double In values
avg += value
Next
Return avg / values.Length
End Function
Public Shared Function Average(ByVal ParamArray values() As Long) As Double
Dim avg As Double = 0
For Each value As Double In values
avg += value
Next
Return avg / values.Length
End Function
Public Shared Function Average(ByVal ParamArray values() As Double) As Double
Dim avg As Double = 0
For Each value As Double In values
avg += value
Next
Return avg / values.Length
End Function
''' <summary>
''' a one dimensional linear interpolation of a value.
''' </summary>
''' <param name="a">from value</param>
''' <param name="b">to value</param>
''' <param name="weight">lerp value</param>
''' <returns>the value lerped from a to b</returns>
''' <remarks></remarks>
Public Shared Function Interpolate(ByVal a As Double, ByVal b As Double, ByVal weight As Double) As Double
Return (b - a) * weight + a
End Function
''' <summary>
''' The percentage a value is from min to max
'''
''' eg:
''' 8 of 10 out of 0->10 would be 0.8f
'''
''' Good for calculating the lerp weight
''' </summary>
''' <param name="value">The value to text</param>
''' <param name="max">The max value</param>
''' <param name="min">The min value</param>
''' <returns>The percentage value is from min</returns>
''' <remarks></remarks>
Public Shared Function PercentageMinMax(ByVal value As Double, ByVal max As Double, Optional ByVal min As Double = 0) As Double
value -= min
max -= min
If max = 0 Then
Return 0
Else
Return value / max
End If
End Function
''' <summary>
''' The percentage a value is from max to min
'''
''' eg:
''' 8 of 10 out of 0->10 would be 0.2f
'''
''' Good for calculating a discount
''' </summary>
''' <param name="value">The value to text</param>
''' <param name="max">The max value</param>
''' <param name="min">The min value</param>
''' <returns>The percentage value is from max</returns>
''' <remarks></remarks>
Public Shared Function PercentageOffMinMax(ByVal value As Double, ByVal max As Double, Optional ByVal min As Double = 0) As Double
value -= max
min -= max
If min = 0 Then
Return 0
Else
Return value / min
End If
End Function
''' <summary>
''' Return the minimum value of several values
''' </summary>
''' <param name="args"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function Min(ByVal value As Double, ByVal ParamArray args() As Double) As Double
For i As Integer = 0 To args.Length - 1
If args(i) < value Then value = args(i)
Next
Return value
End Function
Public Shared Function Min(ByVal value As Short, ByVal ParamArray args() As Short) As Double
For i As Integer = 0 To args.Length - 1
If args(i) < value Then value = args(i)
Next
Return value
End Function
Public Shared Function Min(ByVal value As Integer, ByVal ParamArray args() As Integer) As Double
For i As Integer = 0 To args.Length - 1
If args(i) < value Then value = args(i)
Next
Return value
End Function
Public Shared Function Min(ByVal value As Long, ByVal ParamArray args() As Long) As Double
For i As Integer = 0 To args.Length - 1
If args(i) < value Then value = args(i)
Next
Return value
End Function
''' <summary>
''' Return the maximum of several values
''' </summary>
''' <param name="args"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function Max(ByVal value As Double, ByVal ParamArray args() As Double) As Double
For i As Integer = 0 To args.Length - 1
If args(i) > value Then value = args(i)
Next
Return value
End Function
Public Shared Function Max(ByVal value As Short, ByVal ParamArray args() As Short) As Double
For i As Integer = 0 To args.Length - 1
If args(i) > value Then value = args(i)
Next
Return value
End Function
Public Shared Function Max(ByVal value As Integer, ByVal ParamArray args() As Integer) As Double
For i As Integer = 0 To args.Length - 1
If args(i) > value Then value = args(i)
Next
Return value
End Function
Public Shared Function Max(ByVal value As Long, ByVal ParamArray args() As Long) As Double
For i As Integer = 0 To args.Length - 1
If args(i) > value Then value = args(i)
Next
Return value
End Function
''' <summary>
''' Wraps a value around some significant range.
'''
''' Similar to modulo, but works in a unary direction over any range (including negative values).
'''
''' ex:
''' Wrap(8,6,2) == 4
''' Wrap(4,2,0) == 0
''' Wrap(4,2,-2) == -2
''' </summary>
''' <param name="value">value to wrap</param>
''' <param name="max">max in range</param>
''' <param name="min">min in range</param>
''' <returns>A value wrapped around min to max</returns>
''' <remarks></remarks>
Public Shared Function Wrap(ByVal value As Short, ByVal max As Short, Optional ByVal min As Short = 0) As Short
value -= min
max -= min
If max = 0 Then Return min
value = value Mod max
value += min
While value < min
value += max
End While
Return value
End Function
Public Shared Function Wrap(ByVal value As Integer, ByVal max As Integer, Optional ByVal min As Integer = 0) As Integer
value -= min
max -= min
If max = 0 Then Return min
value = value Mod max
value += min
While value < min
value += max
End While
Return value
End Function
Public Shared Function Wrap(ByVal value As Long, ByVal max As Long, Optional ByVal min As Long = 0) As Long
value -= min
max -= min
If max = 0 Then Return min
value = value Mod max
value += min
While value < min
value += max
End While
Return value
End Function
Public Shared Function Wrap(ByVal value As Double, ByVal max As Double, Optional ByVal min As Double = 0) As Double
value -= min
max -= min
If max = 0 Then Return min
value = value Mod max
value += min
While value < min
value += max
End While
Return value
End Function
''' <summary>
''' Arithmetic version of Wrap... unsure of which is more efficient.
'''
''' Here for demo purposes
''' </summary>
''' <param name="value">value to wrap</param>
''' <param name="max">max in range</param>
''' <param name="min">min in range</param>
''' <returns>A value wrapped around min to max</returns>
''' <remarks></remarks>
Public Shared Function ArithWrap(ByVal value As Double, ByVal max As Double, Optional ByVal min As Double = 0) As Double
max -= min
If Not max Then Return min
Return value - max * Math.Floor((value - min) / max)
End Function
''' <summary>
''' Clamp a value into a range.
'''
''' If input is LT min, min returned
''' If input is GT max, max returned
''' else input returned
''' </summary>
''' <param name="input">value to clamp</param>
''' <param name="max">max in range</param>
''' <param name="min">min in range</param>
''' <returns>calmped value</returns>
''' <remarks></remarks>
Public Shared Function Clamp(ByVal input As Short, ByVal max As Short, Optional ByVal min As Short = 0) As Short
Return Math.Max(min, Math.Min(max, input))
End Function
Public Shared Function Clamp(ByVal input As Integer, ByVal max As Integer, Optional ByVal min As Integer = 0) As Integer
Return Math.Max(min, Math.Min(max, input))
End Function
Public Shared Function Clamp(ByVal input As Long, ByVal max As Long, Optional ByVal min As Long = 0) As Long
Return Math.Max(min, Math.Min(max, input))
End Function
Public Shared Function Clamp(ByVal input As Double, ByVal max As Double, Optional ByVal min As Double = 0) As Double
Return Math.Max(min, Math.Min(max, input))
End Function
''' <summary>
''' roundTo some place comparative to a 'base', default is 10 for decimal place
'''
''' 'place' is represented by the power applied to 'base' to get that place
''' </summary>
''' <param name="value">the value to round</param>
''' <param name="place">the place to round to</param>
''' <param name="base">the base to round in... default is 10 for decimal</param>
''' <returns>The value rounded</returns>
''' <remarks>e.g.
'''
''' 2000/7 ~= 285.714285714285714285714 ~= (bin)100011101.1011011011011011
'''
''' roundTo(2000/7,3) == 0
''' roundTo(2000/7,2) == 300
''' roundTo(2000/7,1) == 290
''' roundTo(2000/7,0) == 286
''' roundTo(2000/7,-1) == 285.7
''' roundTo(2000/7,-2) == 285.71
''' roundTo(2000/7,-3) == 285.714
''' roundTo(2000/7,-4) == 285.7143
''' roundTo(2000/7,-5) == 285.71429
'''
''' roundTo(2000/7,3,2) == 288 -- 100100000
''' roundTo(2000/7,2,2) == 284 -- 100011100
''' roundTo(2000/7,1,2) == 286 -- 100011110
''' roundTo(2000/7,0,2) == 286 -- 100011110
''' roundTo(2000/7,-1,2) == 285.5 -- 100011101.1
''' roundTo(2000/7,-2,2) == 285.75 -- 100011101.11
''' roundTo(2000/7,-3,2) == 285.75 -- 100011101.11
''' roundTo(2000/7,-4,2) == 285.6875 -- 100011101.1011
''' roundTo(2000/7,-5,2) == 285.71875 -- 100011101.10111
'''
''' note what occurs when we round to the 3rd space (8ths place), 100100000, this is to be assumed
''' because we are rounding 100011.1011011011011011 which rounds up.</remarks>
Public Shared Function roundTo(ByVal value As Double, Optional ByVal place As Integer = 0, Optional ByVal base As UInteger = 10) As Double
Dim p As Double = Math.Pow(base, -place)
Return Math.Round(value * p) / p
End Function
Public Shared Function floorTo(ByVal value As Double, Optional ByVal place As Integer = 0, Optional ByVal base As UInteger = 10) As Double
Dim p As Double = Math.Pow(base, -place)
Return Math.Floor(value * p) / p
End Function
Public Shared Function ceilTo(ByVal value As Double, Optional ByVal place As Integer = 0, Optional ByVal base As UInteger = 10) As Double
Dim p As Double = Math.Pow(base, -place)
Return Math.Ceiling(value * p) / p
End Function
#End Region
#Region "Simple fuzzy arithmetic"
''' <summary>
''' Test if Double is kind of equal to some other value by some epsilon.
'''
''' Due to float error, two values may be considered similar... but the computer considers them different.
''' By using some epsilon (degree of error) once can test if the two values are similar.
''' </summary>
''' <param name="a"></param>
''' <param name="b"></param>
''' <param name="epsilon"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function FuzzyEqual(ByVal a As Double, ByVal b As Double, ByVal epsilon As Double) As Boolean
Return Math.Abs(a - b) < epsilon
End Function
Public Shared Function FuzzyEqual(ByVal a As Double, ByVal b As Double) As Boolean
Return FuzzyEqual(a, b, EPSILON)
End Function
''' <summary>
''' Test if Double is greater than some other value by some degree of error in epsilon.
'''
''' Due to float error, two values may be considered similar... but the computer considers them different.
''' By using some epsilon (degree of error) once can test if the two values are similar.
''' </summary>
''' <param name="a"></param>
''' <param name="b"></param>
''' <param name="epsilon"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function FuzzyLessThan(ByVal a As Double, ByVal b As Double, ByVal epsilon As Double) As Boolean
Return a < b + epsilon
End Function
Public Shared Function FuzzyLessThan(ByVal a As Double, ByVal b As Double) As Boolean
Return FuzzyLessThan(a, b, EPSILON)
End Function
''' <summary>
''' Test if Double is less than some other value by some degree of error in epsilon.
'''
''' Due to float error, two values may be considered similar... but the computer considers them different.
''' By using some epsilon (degree of error) once can test if the two values are similar.
''' </summary>
''' <param name="a"></param>
''' <param name="b"></param>
''' <param name="epsilon"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function FuzzyGreaterThan(ByVal a As Double, ByVal b As Double, ByVal epsilon As Double) As Boolean
Return a > b - epsilon
End Function
Public Shared Function FuzzyGreaterThan(ByVal a As Double, ByVal b As Double) As Boolean
Return FuzzyGreaterThan(a, b, EPSILON)
End Function
''' <summary>
''' Test if a value is near some target value, if with in some range of 'epsilon', the target is returned.
'''
''' eg:
''' Slam(1.52,2,0.1) == 1.52
''' Slam(1.62,2,0.1) == 1.62
''' Slam(1.72,2,0.1) == 1.72
''' Slam(1.82,2,0.1) == 1.82
''' Slam(1.92,2,0.1) == 2
''' </summary>
''' <param name="value"></param>
''' <param name="target"></param>
''' <param name="epsilon"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function Slam(ByVal value As Double, ByVal target As Double, ByVal epsilon As Double) As Double
If Math.Abs(value - target) < epsilon Then
Return target
Else
Return value
End If
End Function
Public Shared Function Slam(ByVal value As Double, ByVal target As Double) As Double
Return Slam(value, target, EPSILON)
End Function
#End Region
#Region "Angular Math"
''' <summary>
''' convert radians to degrees
''' </summary>
''' <param name="angle"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function RadiansToDegrees(ByVal angle As Double) As Double
Return angle * RAD_TO_DEG
End Function
''' <summary>
''' convert degrees to radians
''' </summary>
''' <param name="angle"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function DegreesToRadians(ByVal angle As Double) As Double
Return angle * DEG_TO_RAD
End Function
''' <summary>
''' Find the angle of a segment from (x1, y1) -> (x2, y2 )
''' </summary>
''' <param name="x1"></param>
''' <param name="y1"></param>
''' <param name="x2"></param>
''' <param name="y2"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function AngleBetween(ByVal x1 As Double, ByVal y1 As Double, ByVal x2 As Double, ByVal y2 As Double) As Double
Return Math.Atan2(y2 - y1, x2 - x1)
End Function
''' <summary>
''' set an angle with in the bounds of -PI to PI
''' </summary>
''' <param name="angle"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function NormalizeAngle(ByVal angle As Double, Optional ByVal useRadians As Boolean = True) As Double
Dim rd As Double = IIf(useRadians, PI, 180)
Return Wrap(angle, PI, -PI)
End Function
''' <summary>
''' closest angle between two angles from a1 to a2
''' absolute value the return for exact angle
''' </summary>
''' <param name="a1"></param>
''' <param name="a2"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function NearestAngleBetween(ByVal a1 As Double, ByVal a2 As Double, Optional ByVal useRadians As Boolean = True) As Double
Dim rd_2 As Double = IIf(useRadians, PI_2, 90)
Dim two_rd As Double = IIf(useRadians, TWO_PI, 360)
a1 = NormalizeAngle(a1)
a2 = NormalizeAngle(a2)
If a1 < -rd_2 And a2 > rd_2 Then a1 += two_rd
If a2 < -rd_2 And a1 > rd_2 Then a2 += two_rd
Return a2 - a1
End Function
''' <summary>
''' normalizes independent and then sets dep to the nearest value respective to independent
'''
'''
''' for instance if dep=-170 degrees and ind=170 degrees then 190 degrees will be returned as an alternative to -170 degrees
''' note: angle is passed in radians, this written example is in degrees for ease of reading
''' </summary>
''' <param name="dep"></param>
''' <param name="ind"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function NormalizeAngleToAnother(ByVal dep As Double, ByVal ind As Double, Optional ByVal useRadians As Boolean = True) As Double
Return ind + NearestAngleBetween(ind, dep, useRadians)
End Function
''' <summary>
''' interpolate across the shortest arc between two angles
''' </summary>
''' <param name="a1"></param>
''' <param name="a2"></param>
''' <param name="weight"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function InterpolateAngle(ByVal a1 As Double, ByVal a2 As Double, ByVal weight As Double, Optional ByVal useRadians As Boolean = True) As Double
a1 = NormalizeAngle(a1, useRadians)
a2 = NormalizeAngleToAnother(a2, a1, useRadians)
Return Interpolate(a1, a2, weight)
End Function
#End Region
#Region "Advanced Math"
''' <summary>
''' Compute the logarithm of any value of any base
''' </summary>
''' <param name="value"></param>
''' <param name="base"></param>
''' <returns></returns>
''' <remarks>
''' a logarithm is the exponent that some constant (base) would have to be raised to
''' to be equal to value.
'''
''' i.e.
''' 4 ^ x = 16
''' can be rewritten as to solve for x
''' logB4(16) = x
''' which with this function would be
''' LoDMath.logBaseOf(16,4)
'''
''' which would return 2, because 4^2 = 16
''' </remarks>
Public Shared Function LogBaseOf(ByVal value As Double, ByVal base As Double) As Double
Return Math.Log(value) / Math.Log(base)
End Function
''' <summary>
''' Check if a value is prime.
''' </summary>
''' <param name="value"></param>
''' <returns></returns>
''' <remarks>
''' In this method to increase speed we first check if the value is ltOReq 1, because values ltOReq 1 are not prime by definition.
''' Then we check if the value is even but not equal to 2. If so the value is most certainly not prime.
''' Lastly we loop through all odd divisors. No point in checking 1 or even divisors, because if it were divisible by an even
''' number it would be divisible by 2. If any divisor existed when i > value / i then its compliment would have already
''' been located. And lastly the loop will never reach i == val because i will never be > sqrt(val).
'''
''' proof of validity for algorithm:
'''
''' all trivial values are thrown out immediately by checking if even or less then 2
'''
''' all remaining possibilities MUST be odd, an odd is resolved as the multiplication of 2 odd values only. (even * anyValue == even)
'''
''' in resolution a * b = val, a = val / b. As every compliment a for b, b and a can be swapped resulting in b being ltOReq a. If a compliment for b
''' exists then that compliment would have already occured (as it is odd) in the swapped addition at the even split.
'''
''' Example...
'''
''' 16
''' 1 * 16
''' 2 * 8
''' 4 * 4
''' 8 * 2
''' 16 * 1
'''
''' checks for 1, 2, and 4 would have already checked the validity of 8 and 16.
'''
''' Thusly we would only have to loop as long as i ltOReq val / i. Once we've reached the middle compliment, all subsequent factors have been resolved.
'''
''' This shrinks the number of loops for odd values from [ floor(val / 2) - 1 ] down to [ ceil(sqrt(val) / 2) - 1 ]
'''
''' example, if we checked EVERY odd number for the validity of the prime 7927, we'd loop 3962 times
'''
''' but by this algorithm we loop only 43 times. Significant improvement!
''' </remarks>
Public Shared Function IsPrime(ByVal value As Long) As Boolean
' check if value is in prime number range
If value < 2 Then Return False
' check if even, but not equal to 2
If Not (value Mod 2) And value <> 2 Then Return False
' if 2 or odd, check if any non-trivial divisors exist
Dim sqrrt As Long = Math.Floor(Math.Sqrt(value))
For i As Long = 3 To sqrrt Step 2
If Not (value Mod i) Then Return False
Next
Return True
End Function
''' <summary>
''' Relative Primality between two integers
'''
''' By definition two integers are considered relatively prime if their
''' 'greatest common divisor' is 1. So thusly we simply just check if
''' the GCD of m and n is 1.
''' </summary>
''' <param name="m"></param>
''' <param name="n"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function IsRelativelyPrime(ByVal m As Short, ByVal n As Short) As Boolean
Return GCD(m, n) = 1
End Function
Public Shared Function IsRelativelyPrime(ByVal m As Integer, ByVal n As Integer) As Boolean
Return GCD(m, n) = 1
End Function
Public Shared Function IsRelativelyPrime(ByVal m As Long, ByVal n As Long) As Boolean
Return GCD(m, n) = 1
End Function
Public Shared Function FactorsOf(ByVal value As Integer) As Integer()
value = Math.Abs(value)
Dim arr As New ArrayList()
Dim sqrrt As Integer = Math.Floor(Math.Sqrt(value))
Dim c As Integer
For i As Integer = 1 To sqrrt
If Not (value Mod i) Then
arr.Add(i)
c = value / i
If c <> i Then arr.Add(c)
End If
Next
arr.Sort()
Return arr.ToArray(GetType(Integer))
End Function
Public Shared Function CommonFactorsOf(ByVal m As Integer, ByVal n As Integer) As Integer()
Dim arr As New ArrayList()
Dim i As Integer
m = Math.Abs(m)
n = Math.Abs(n)
If m > n Then
i = m
m = n
n = i
End If
For i = 1 To m
If Not (m Mod i) And Not (n Mod i) Then
arr.Add(i)
End If
Next
Return arr.ToArray(GetType(Integer))
End Function
''' <summary>
''' Greatest Common Denominator using Euclid's algorithm
''' </summary>
''' <param name="m"></param>
''' <param name="n"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function GCD(ByVal m As Integer, ByVal n As Integer) As Integer
Dim r As Integer
' make sure positive, GCD is always positive
m = Math.Abs(m)
n = Math.Abs(n)
' m must be >= n
If m < n Then
r = m
m = n
n = r
End If
' now start loop, loop is infinite... we will cancel out sooner or later
While True
r = m Mod n
If Not r Then Return n
m = n
n = r
End While
' fail safe
Return 1
End Function
Public Shared Function GCD(ByVal m As Long, ByVal n As Long) As Long
Dim r As Long
' make sure positive, GCD is always positive
m = Math.Abs(m)
n = Math.Abs(n)
' m must be >= n
If m < n Then
r = m
m = n
n = r
End If
' now start loop, loop is infinite... we will cancel out sooner or later
While True
r = m Mod n
If Not r Then Return n
m = n
n = r
End While
' fail safe
Return 1
End Function
Public Shared Function LCM(ByVal m As Integer, ByVal n As Integer) As Integer
Return (m * n) / GCD(m, n)
End Function
''' <summary>
''' Factorial - N!
'''
''' Simple product series
''' </summary>
''' <param name="value"></param>
''' <returns></returns>
''' <remarks>
''' By definition 0! == 1
'''
''' Factorial assumes the idea that the value is an integer >= 0... thusly UInteger is used
''' </remarks>
Public Shared Function Factorial(ByVal value As UInteger) As Long
If value <= 0 Then Return 1
Dim res As Long = value
While --value
res *= value
End While
Return res
End Function
''' <summary>
''' Falling facotiral
'''
''' defined: (N)! / (N - x)!
'''
''' written subscript: (N)x OR (base)exp
''' </summary>
''' <param name="base"></param>
''' <param name="exp"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function FallingFactorial(ByVal base As UInteger, ByVal exp As UInteger) As Long
Return Factorial(base) / Factorial(base - exp)
End Function
''' <summary>
''' rising factorial
'''
''' defined: (N + x - 1)! / (N - 1)!
'''
''' written superscript N^(x) OR base^(exp)
''' </summary>
''' <param name="base"></param>
''' <param name="exp"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function RisingFactorial(ByVal base As UInteger, ByVal exp As UInteger) As Long
Return Factorial(base + exp - 1) / Factorial(base - 1)
End Function
''' <summary>
''' binomial coefficient
'''
''' defined: N! / (k!(N-k)!)
''' reduced: N! / (N-k)! == (N)k (fallingfactorial)
''' reduced: (N)k / k!
''' </summary>
''' <param name="n"></param>
''' <param name="k"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function BinCoef(ByVal n As UInteger, ByVal k As UInteger) As Long
Return FallingFactorial(n, k) / Factorial(k)
End Function
''' <summary>
''' rising binomial coefficient
'''
''' as one can notice in the analysis of binCoef(...) that
''' binCoef is the (N)k divided by k!. Similarly rising binCoef
''' is merely N^(k) / k!
''' </summary>
''' <param name="n"></param>
''' <param name="k"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function RisingBinCoef(ByVal n As UInteger, ByVal k As UInteger) As Long
Return RisingFactorial(n, k) / Factorial(k)
End Function
#End Region
#End Region
End Class