Add bin and edit workflow
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 9s

This commit is contained in:
2026-09-16 13:11:16 -06:00
parent c8ac4fcae5
commit 4cee170d66
17576 changed files with 895740 additions and 2 deletions
@@ -0,0 +1,8 @@
module Math
# Absolute value of +x+.
def self.abs(x)
x.abs
end
end
@@ -0,0 +1 @@
require 'facets/math/acsc'
@@ -0,0 +1,8 @@
module Math
# Arcus cotangens of +x+
def self.acot(x)
(PI * 0.5) - atan(x)
end
end
@@ -0,0 +1,8 @@
module Math
# Area cotangens hyperbolicus of +x+
def self.acoth(x)
0.5 * log((x + 1.0) / (x - 1.0))
end
end
@@ -0,0 +1,13 @@
module Math
# Arcus cosecans of `x`.
def self.acsc(x)
asin(1.0 / x)
end
# Arcus cosecans of `x`.
def self.acosec(x)
asin(1.0 / x)
end
end
@@ -0,0 +1,8 @@
module Math
# Area cosecans hyperbolicus of +x+
def acsch(x)
::Math.log(1.0 / x + Math.sqrt(1.0 + 1.0 / (x * x)))
end
end
@@ -0,0 +1,17 @@
require 'facets/math/rmd'
require 'facets/math/mean'
module Math
# The average absolute difference of two independent values drawn
# from the sample. Equal to the RMD * mean.
def self.amd(array)
rmd(array) * mean(array)
end
class << self
alias_method :absolute_mean_difference, :amd
#alias_method :md, :mean_difference
end
end
@@ -0,0 +1,15 @@
module Math
#
EPSILON = 0.000000001
# Approximately equal.
#
# TODO: Use core extension Numeric#approx? instead (?)
def self.approx_equal(a, b, epsilon=EPSILON)
c = a - b
c *= -1.0 if c < 0
c < epsilon
end
end
@@ -0,0 +1,8 @@
module Math
# Arcus secans of +x+
def self.asec(x)
acos(1.0 / x)
end
end
@@ -0,0 +1,8 @@
module Math
# Area secans hyperbolicus of +x+
def asech(x)
log((1.0 + sqrt(1.0 - x * x)) / x)
end
end
@@ -0,0 +1,16 @@
require 'facets/math/theil_index'
module Math
# Closely related to the Theil index and easily expressible
# in terms of it.
#
# AI = 1-e^{theil_index}
#
# http://en.wikipedia.org/wiki/Atkinson_index
def self.atkinson_index(array)
t = theil_index(array)
(t < 0) ? -1 : 1-Math::E**(-t)
end
end
@@ -0,0 +1,14 @@
require 'facets/math/tgamma'
module Math
# Beta function of `x` and `y`.
#
# beta(x, y) = tgamma(x) * tgamma(y) / tgamma(x + y)
#
def self.beta(x, y)
#exp(lgamma(x).first + lgamma(y).first - lgamma(x+y).first)
tgamma(x) * tgamma(y) / tgamma(x + y)
end
end
@@ -0,0 +1,10 @@
module Math
# Returns the Cumulative Density Function of this
# sample (normalised to a fraction of 1.0).
def self.cdf(array, normalised=1.0)
s = sum(array).to_f
array.sort.inject([0.0]) { |c,d| c << c[-1] + normalised*d.to_f/s }
end
end
@@ -0,0 +1,8 @@
module Math
# Smallest integer not smaller than +x+.
def self.ceil(x)
x.ceil
end
end
@@ -0,0 +1 @@
require 'facets/math/csc'
@@ -0,0 +1 @@
require 'facets/math/csch'
@@ -0,0 +1,8 @@
module Math
# Cotangens of +x+
def self.cot(x)
tan((PI * 0.5) - x)
end
end
@@ -0,0 +1,8 @@
module Math
# Cotangens hyperbolicus of +x+
def self.coth(x)
1.0 / tanh(x)
end
end
@@ -0,0 +1,13 @@
module Math
# Cosecans of `x`.
def self.csc(x)
1.0 / sin(x)
end
# Cosecans of `x`.
def self.cosec(x)
1.0 / sin(x)
end
end
@@ -0,0 +1,13 @@
module Math
# Cosecans hyperbolicus of `x`.
def self.csch(x)
1.0 / sinh(x)
end
# Cosecans hyperbolicus of `x`.
def self.cosech(x)
1.0 / sinh(x)
end
end
@@ -0,0 +1,9 @@
module Math
# Kronecker symbol of +i+ and +j+.
# Returns 1 if +i+ and +j+ are equal, 0 otherwise.
def self.delta(i, j)
return Integer(i) == Integer(j) ? 1 : 0
end
end
@@ -0,0 +1,19 @@
module Math
# Calculates the Euclidean Distance between points +p+ and +q+.
#
# `p`, `q` is assumed to described coordinates in N-dimensions, e. g.:
#
# Math.distance([1, 1], [2, 2]) # 2D coordinates
# Math.distance([1, 1, 1], [2, 2, 2]) # 3D coordinates
#
# If N is 1, then `::distance` may also be invoked like so:
#
# Math.distance(1, 1)
#
def self.distance(p, q)
p, q = [p].flatten, [q].flatten
sqrt(p.zip(q).inject(0){ |sum, coord| sum + (coord.first - coord.last)**2 })
end
end
@@ -0,0 +1,5 @@
module Math
# Euler's constant.
EC = 0.577_215_664_901_532_861
end
@@ -0,0 +1,21 @@
module Math
# Levi-Civita symbol of +i+, +j+, and +k+ - 1 if (+i+, +j+, +k+)
# is (1, 2, 3), (2, 3, 1), or (3, 1, 2), -1 if it is (1, 3, 2),
# (2, 1, 3), or (3, 2, 1), 0 as long as +i+, +j+, and +k+ are
# all elements of {1, 2, 3}, otherwise returns <code>nil</code>.
def self.epsilon(i, j, k)
i = Integer(i)
return nil if i < 1 or i > 3
j = Integer(j)
return nil if j < 1 or j > 3
k = Integer(k)
return nil if k < 1 or k > 3
case i * 16 + j * 4 + k
when 27, 45, 54 then return 1
when 30, 39, 57 then return -1
end
0
end
end
@@ -0,0 +1,8 @@
module Math
# 10 to the power +x+
def self.exp10(x)
10.0 ** x
end
end
@@ -0,0 +1,8 @@
module Math
# 2 to the power +x+
def self.exp2(x)
2.0 ** x
end
end
@@ -0,0 +1,37 @@
module Math
# First 16 factorials.
FACTORIALS = [
1,
1,
2,
6,
24,
120,
720,
5_040,
40_320,
362_880,
3_628_800,
39_916_800,
479_001_600,
6_227_020_800,
87_178_291_200,
1_307_674_368_000
]
# 1 * 2 * ... * +n+, <code>nil</code> for negative numbers
def self.factorial(n)
n = Integer(n)
if n < 0
nil
elsif FACTORIALS.length > n
FACTORIALS[n]
else
h = FACTORIALS.last
(FACTORIALS.length .. n).each { |i| FACTORIALS.push h *= i }
h
end
end
end
@@ -0,0 +1,8 @@
module Math
# Largest integer not larger than +x+.
def self.floor(x)
x.floor
end
end
@@ -0,0 +1,23 @@
module Math
# Greatest common divisor of +m+ and +n+, +nil+ for non-positive
# numbers - gcd is computed by means of the Euclidian algorithm.
def self.gcd(m, n)
m = Integer(m)
n = Integer(n)
if m <= 0 || n <= 0
return nil
end
loop {
if m < n
m, n = n, m
end
if (l = m % n) == 0
break
end
m = l
}
n
end
end
@@ -0,0 +1,33 @@
require 'facets/math/approx_equal'
module Math
# Calculates the Gini Coefficient (a measure of inequality of a distribution
# based on the area between the Lorenz curve and the uniform curve).
#
# http://en.wikipedia.org/wiki/Gini_coefficient
#
# This is a slightly cleaner way of calculating the Gini Coefficient then
# the previous implementationj.
#
# GC = \frac{\sum_{i=1}^N (2i-N-1)x_i}{N^2-\bar{x}}
#
def self.gini_coefficient(array)
return -1 if size <= 0 or any? { |x| x < 0 }
return 0 if size < 2 or all? { |x| approx_equal(x,0) }
s = 0
sort.each_with_index { |li,i| s += (2*i+1-size)*li }
s.to_f/(size**2*mean).to_f
end
## OLD WAY
## GC = \frac{1}{N} \left ( N+1-2\frac{\sum_{i=1}^N (N+1-i)y_i}{\sum_{i=1}^N y_i} \right )
## def self.gini_coefficient2(array)
## return -1 if size <= 0 or any? { |x| x < 0 }
## return 0 if size < 2 or all? { |x| Math::float_equal(x,0) }
## s = 0
## sort.each_with_index { |yi,i| s += (size - i)*yi }
## (size+1-2*(s.to_f/sum.to_f)).to_f/size.to_f
## end
end
@@ -0,0 +1,19 @@
module Math
# The Kullback-Leibler divergence from this array to that of +q+.
#
# NB: You will possibly want to sort both P and Q before calling this
# depending on what you're actually trying to measure.
#
# http://en.wikipedia.org/wiki/Kullback-Leibler_divergence
#
def self.kldivergence(array, q)
fail "Buggy."
fail "Cannot compare differently sized arrays." unless size = q.size
kld = 0
each_with_index { |pi,i| kld += pi*Math::log(pi.to_f/q[i].to_f) }
kld
end
end
@@ -0,0 +1,15 @@
module Math
# Least common multiple of +m+ and +n+, computed by multiplying
# +m+ and +n+ and dividing the product by the gcd of +m+ and +n+,
# +nil+ for non-positive numbers.
def self.lcm(m, n)
m = Integer(m)
n = Integer(n)
if m <= 0 || n <= 0
return nil
end
m / gcd(m, n) * n
end
end
@@ -0,0 +1,23 @@
require 'facets/math/lngamma'
## This is the old definition by Josef Schugt.
##
## Around v2.0, Ruby finally added it's own `lgamma`
## function. That's good, but unforutnately it returns
## an array that includes the `sign(gamma(x))` too.
#
# def Math.lgamma(x)
# h = x + 5.5
# h -= (x + 0.5) * log(h)
#
# sum = 1.000_000_000_190_015
# sum += 76.180_091_729_471_46 / (x + 1.0)
# sum -= 86.505_320_329_416_77 / (x + 2.0)
# sum += 24.014_098_240_830_91 / (x + 3.0)
# sum -= 1.231_739_572_450_155 / (x + 4.0)
# sum += 0.120_865_097_386_617_9e-2 / (x + 5.0)
# sum -= 0.539_523_938_495_3e-5 / (x + 6.0)
#
# -h + log(2.506_628_274_631_000_5 * sum / x)
# end
@@ -0,0 +1,12 @@
module Math
# Returns real solution(s) of <code>+a+x + +b+ = +c+</code> or +nil+
# if no or an infinite number of solutions exist. If
# <code>c</code> is missing it is assumed to be 0.
#
# @author Josef Schugt
def self.linsolve(a, b, c = 0.0)
a == 0 ? nil : (c - b) / a
end
end
@@ -0,0 +1,18 @@
module Math
# Logarithmus naturalis of gamma function of `x`.
#
# Notice the use of `ln` prefix to differentiate from
# Ruby's built-in `#lgamma` function which returns an Array.
#
def self.lngamma(x)
lgamma(x).first
end
# Old name used by Extmath library.
def self.ln_gamma(x)
lgamma(x).first
end
end
@@ -0,0 +1,14 @@
module Math
INVERSE_LN_2 = 1.0 / ::Math.log(2.0)
unless defined?(log2)
# Logarithmus dualis of +x+.
def self.log2(x)
Math.log(x) * INVERSE_LN_2
end
end
end
@@ -0,0 +1 @@
require 'facets/math/min'
@@ -0,0 +1,16 @@
require 'facets/math/sum'
module Math
# Mean average.
def self.mean(array, &blk)
s = array.size
return 0.0 if s == 0
sum(array, &blk) / s
end
class << self
alias_method :mean_average, :mean
end
end
@@ -0,0 +1,30 @@
require 'facets/math/percentile'
module Math
# Returns the numerical median for the an array of values;
# or nil if array is empty.
#
def self.median(array)
percentile(array, 50)
end
# better definition ?
=begin
#
def self.median(array)
return 0 if array.size == 0
tmp = array.sort
mid = tmp.size / 2
if (tmp.size % 2) == 0
(tmp[mid-1] + tmp[mid]).to_f / 2
else
tmp[mid]
end
end
=end
end
@@ -0,0 +1,35 @@
module Math
#
def self.min(array, &block)
if block_given?
if min = array.find{ |i| i }
min = yield(min)
array.each do |i|
j = yield(i)
min = j if min > j
end
min
end
else
array.min
end
end
#
def self.max(array, block)
if block_given?
if max = find{|i| i}
max = yield(max)
each{|i|
j = yield(i)
max = j if max < j
}
max
end
else
array.max
end
end
end
@@ -0,0 +1,40 @@
module Math
# Returns the percentile value for percentile _pcnt_; nil if array is empty.
#
# +pcnt+ should be expressed as an integer, e.g. `percentile(90)` returns
# the 90th percentile of the array.
#
# Algorithm from NIST[http://www.itl.nist.gov/div898/handbook/prc/section2/prc262.htm]
#
# NOTE: This is not a common core extension and is not
# loaded automatically when using <code>require 'facets'</code>.
#
# CREDIT: Ben Koski
#
# @non-core
# require 'facets/array/precentile'
#
def self.percentile(array, pcnt)
sorted_array = array.sort
return nil if array.length == 0
rank = (pcnt.to_f / 100) * (array.length + 1)
whole = rank.truncate
# if has fractional part
if whole != rank
s0 = sorted_array[whole - 1]
s1 = sorted_array[whole]
f = (rank - rank.truncate).abs
return (f * (s1 - s0)) + s0
else
return sorted_array[whole - 1]
end
end
end
@@ -0,0 +1,13 @@
module Math
# `x` to the power `y`.
def self.pow(x, y)
x ** y
end
# `x` to the power `y`.
def self.pwr(x, y)
x ** y
end
end
@@ -0,0 +1 @@
require 'facets/math/std'
@@ -0,0 +1 @@
require 'facets/math/variance'
@@ -0,0 +1,16 @@
require 'facets/math/approx_equal'
module Math
# Calculates the relative mean difference of this sample.
# Makes use of the fact that the Gini Coefficient is half the RMD.
def self.rmd(array)
return 0.0 if approx_equal(mean(array), 0.0)
gini_coefficient(array) * 2
end
class << self
alias_method :relative_mean_difference, :rmd
end
end
@@ -0,0 +1,8 @@
module Math
# The `y` root of `x`.
def self.root(x, y)
x ** (1.0 / y)
end
end
@@ -0,0 +1,9 @@
module Math
# Round number to an integer.
#
def self.round(x)
x.round
end
end
@@ -0,0 +1,8 @@
module Math
# Secans of +x+.
def self.sec(x)
1.0 / cos(x)
end
end
@@ -0,0 +1,8 @@
module Math
# Secans hyperbolicus of +x+
def self.sech(x)
1.0 / cosh(x)
end
end
@@ -0,0 +1,20 @@
module Math
# Sign of `x`. This function returns `-1.0` if `x` is negative,
# `+1.0` if `x` is positive `x`, and `0.0` if `x = 0`.
def self.sign(x, zero=0.0)
(x > 0.0) ? 1.0 : ((x < 0.0) ? -1.0 : zero)
end
# Same as `Math.sign`.
def self.sgn(x, zero=0.0)
(x > 0.0) ? 1.0 : ((x < 0.0) ? -1.0 : zero)
end
# The *Heaviside step function*, also called the the *unit step function*.
# This functions works like `Math.sign` but by default returns `1.0` for zero.
def self.unit_step(x, zero=1.0)
(x > 0.0) ? 1.0 : ((x < 0.0) ? -1.0 : zero)
end
end
@@ -0,0 +1,8 @@
module Math
# Sinc function of +x+.
def self.sinc(x)
(x == 0.0) ? 1.0 : sin(x) / x
end
end
@@ -0,0 +1,8 @@
module Math
# Square of number.
def self.sqr(x)
x * x
end
end
@@ -0,0 +1,55 @@
require 'facets/math/linsolve'
module Math
# Returns array of real solution of <code>ax**2 + bx + c = d</code>
# or <code>nil</code> if no or an infinite number of solutions exist.
# If +d+ is missing it is assumed to be 0.
#
# In order to solve <code>ax**2 + bx + c = d</code> +sqsolve+ identifies several cases:
# * <code>a == 0:</code>
# The equation to be solved is the linear equation <code>bx + c = d</code>. #sqsolve> delegates the computation to
# #linsolve>. If it results in +nil+, +nil+ is returned (not <code>[nil]</code>!). Otherwise a one-element array
# containing result of #linsolve is returned.
# * <code>a != 0:</code>
# The equation to be solved actually is a second order one.
# * <code>c == d</code>
# The equation to be solved is <code>ax**2 + bx = 0</code>. One solution of this equation obviously is
# <code>x = 0</code>, the second one solves <code>ax + b = 0</code>. The solution of the latter is
# delegated to +linsolve+. An array containing both results in ascending order is returned.
# * <code>c != d</code>
# The equation cannot be separated into <code>x</code> times some factor.
# * <code>b == 0</code>
# The equation to be solved is <code>ax**2 + c = d</code>. This can be written as the linear equation
# <code>ay + c = d</code> with <code>y = x ** 2</code>. The solution of the linear equation is delegated
# to +linsolve+. If the returned value for +y+ is +nil+, that becomes the overall return value.
# Otherwise an array containing the negative and positive squareroot of +y+ is returned
# * <code>b != 0 </code>
# The equation cannot be reduced to simpler cases. We now first have to compute what is called the
# discriminant <code>x = b**2 + 4a(d - c)</code> (that's what we need to compute the square root of).
# If the descriminant is negative no real solution exists and <code>nil</code> is returned. The ternary
# operator checking whether <code>b</code> is negative does ensure better numerical stability --only one
# of the two solutions is computed using the widely know formula for solving second order equations.
# The second one is computed from the fact that the product of both solutions is <code>(c - d) / a</code>.
# Take a look at a book on numerical mathematics if you don't understand why this should be done.
#
# @author Josef Schugt
def self.sqsolve(a, b, c, d = 0.0)
if a == 0.0
x = linsolve(b, c, d)
return x.nil? ? nil: [ linsolve(b, c, d) ]
else
return [0.0, linsolve(a, b)].sort if c == d
if b == 0.0
x = linsolve(a, c, d)
x < 0.0 ? nil : [-Math.sqrt(x), Math.sqrt(x)]
else
x = b * b + 4.0 * a * (d - c)
return nil if x < 0.0
x = b < 0 ? b - Math.sqrt(x) : b + Math.sqrt(x)
[-0.5 * x / a, 2.0 * (d - c) / x].sort
end
end
end
end
@@ -0,0 +1,27 @@
require 'facets/math/variance'
module Math
# Standard deviation of a sample.
#
def self.std(array, &block)
sqrt(variance(array, &block))
end
class << self
alias_method :standard_deviation, :std
end
# Standard deviation of a population.
#
def self.pstd(array, &block)
Math::sqrt(pvariance(array, &block))
end
# Calculates the standard error of a sample.
def self.stderr(array)
return 0.0 if array.size < 2
std(array) / sqrt(array.size)
end
end
@@ -0,0 +1 @@
require 'facets/math/std'
@@ -0,0 +1,16 @@
module Math
# Returns sum. When a block is given, summation is taken over the
# each result of block evaluation.
#
def self.sum(array) #:yield:
sum = 0.0
if block_given?
array.each{|i| sum += yield(i)}
else
array.each{|i| sum += i}
end
sum
end
end
@@ -0,0 +1,14 @@
require 'facets/math/sum'
require 'facets/math/mean'
module Math
# The sum of the squared deviations from the mean.
#
def self.summed_sqdevs(array)
return 0 if array.size < 2
m = mean(array)
sum(array.map{ |x| (x - m) ** 2 })
end
end
@@ -0,0 +1,5 @@
module Math
# See http://tauday.com/tau-manifesto
TAU = 2 * PI
end
@@ -0,0 +1,10 @@
require 'facets/math/lgamma'
module Math
# Exp of LGamma.
def self.tgamma(x)
exp(lngamma(x)) #exp(log(gamma(x).abs)
end
end
@@ -0,0 +1,24 @@
require 'facets/math/sum'
require 'facets/math/mean'
require 'facets/math/approx_equal'
module Math
# Calculates the Theil index (a statistic used to measure
# economic inequality).
#
# TI = \sum_{i=1}^N \frac{x_i}{\sum_{j=1}^N x_j} ln \frac{x_i}{\bar{x}}
#
# http://en.wikipedia.org/wiki/Theil_index
#
def self.theil_index(array)
return -1 if array.size <= 0 or any? { |x| x < 0 }
return 0 if array.size < 2 or all? { |x| approx_equal(x, 0) }
m = mean(array)
s = sum(array).to_f
inject(0) do |theil, xi|
theil + ((xi > 0) ? (log(xi.to_f/m) * xi.to_f/s) : 0.0)
end
end
end
@@ -0,0 +1,31 @@
require 'facets/math/summed_sqdevs'
module Math
#
def self.variance(array, &block)
sum2 = if block_given?
sum(array){ |i| j = block[i]; j*j }
else
sum(array){ |i| i**2 }
end
sum2/array.size - mean(array, &block)**2
end
# Variance of the sample.
# Variance of 0 or 1 elements is 0.0.
#
# TODO: Same as #variance? Then choose one.
def self.variance2(array)
return 0.0 if array.size < 2
summed_sqdevs(array) / (array.size - 1)
end
# Variance of a population.
# Variance of 0 or 1 elements is 0.0.
def self.pvariance(array)
return 0.0 if array.size < 2
summed_sqdevs(array) / array.size
end
end