more_math 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES ADDED
@@ -0,0 +1,2 @@
1
+ 2010-11-01 (0.0.0)
2
+ * Initial Version
data/LICENSE ADDED
@@ -0,0 +1,18 @@
1
+ Copyright (c) 2010 Florian Frank
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7
+ of the Software, and to permit persons to whom the Software is furnished to do
8
+ so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE X
16
+ CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
17
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,20 @@
1
+ = MoreMath - More mathematics in Ruby
2
+
3
+ == Description
4
+
5
+ Ruby library that contains various mathematical functions and algorithms.
6
+
7
+ == Download
8
+
9
+ The homepage of this library is located at
10
+
11
+ * http://flori.github.com/more_math
12
+
13
+ == Author
14
+
15
+ Florian Frank mailto:flori@ping.de
16
+
17
+ == License
18
+
19
+ This software is licensed under the X11 (or MIT) license:
20
+ http://www.xfree86.org/3.3.6/COPYRIGHT2.html#3
data/Rakefile ADDED
@@ -0,0 +1,84 @@
1
+ begin
2
+ require 'rake/gempackagetask'
3
+ rescue LoadError
4
+ end
5
+ require 'rake/clean'
6
+ require 'rbconfig'
7
+ include Config
8
+
9
+ PKG_NAME = 'more_math'
10
+ PKG_VERSION = File.read('VERSION').chomp
11
+ PKG_FILES = FileList['**/*'].exclude(/^(doc|CVS|pkg|coverage)/)
12
+ CLEAN.include 'coverage', 'doc'
13
+ CLOBBER.include FileList['data/*']
14
+
15
+ desc "Run unit tests"
16
+ task :test do
17
+ sh %{RUBYOPT="-Ilib $RUBYOPT" testrb tests/*.rb}
18
+ end
19
+
20
+ desc "Testing library with coverage"
21
+ task :coverage do
22
+ sh 'rcov -x tests -Ilib tests/*.rb'
23
+ end
24
+
25
+ desc "Installing library"
26
+ task :install do
27
+ ruby 'install.rb'
28
+ end
29
+
30
+ desc "Creating documentation"
31
+ task :doc do
32
+ ruby 'make_doc.rb'
33
+ end
34
+
35
+ if defined? Gem
36
+ spec = Gem::Specification.new do |s|
37
+ s.name = PKG_NAME
38
+ s.version = PKG_VERSION
39
+ s.summary = "Library that provides more mathematics."
40
+ s.description = "Library that provides more mathematical functions/algorithms than standard Ruby."
41
+ s.add_dependency('dslkit', '~> 0.2')
42
+
43
+ s.files = PKG_FILES
44
+
45
+ s.require_path = 'lib'
46
+
47
+ s.has_rdoc = true
48
+ s.rdoc_options <<
49
+ '--title' << 'MoreMath -- More Math in Ruby' << '--main' << 'README'
50
+ s.extra_rdoc_files << 'README'
51
+ s.test_files = Dir['tests/*.rb']
52
+
53
+ s.author = "Florian Frank"
54
+ s.email = "flori@ping.de"
55
+ s.homepage = "http://flori.github.com/#{PKG_NAME}"
56
+ s.rubyforge_project = PKG_NAME
57
+ end
58
+
59
+ Rake::GemPackageTask.new(spec) do |pkg|
60
+ pkg.need_tar = true
61
+ pkg.package_files += PKG_FILES
62
+ end
63
+ end
64
+
65
+ desc m = "Writing version information for #{PKG_VERSION}"
66
+ task :version do
67
+ puts m
68
+ File.open(File.join('lib', PKG_NAME, 'version.rb'), 'w') do |v|
69
+ v.puts <<EOT
70
+ module MoreMath
71
+ # MoreMath version
72
+ VERSION = '#{PKG_VERSION}'
73
+ VERSION_ARRAY = VERSION.split(/\\./).map { |x| x.to_i } # :nodoc:
74
+ VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc:
75
+ VERSION_MINOR = VERSION_ARRAY[1] # :nodoc:
76
+ VERSION_BUILD = VERSION_ARRAY[2] # :nodoc:
77
+ end
78
+ EOT
79
+ end
80
+ end
81
+
82
+ task :default => [ :version, :test ]
83
+
84
+ task :release => [ :clobber, :version, :package ]
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.0
data/install.rb ADDED
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rbconfig'
4
+ require 'fileutils'
5
+ include FileUtils::Verbose
6
+
7
+ include Config
8
+
9
+ file = 'lib/more_math.rb'
10
+ libdir = CONFIG["sitelibdir"]
11
+ install(file, libdir, :mode => 0755)
12
+ mkdir_p subdir = File.join(libdir, 'more_math')
13
+ for f in Dir['lib/more_math/*.rb']
14
+ install(f, subdir)
15
+ end
16
+ mkdir_p subdir = File.join(libdir, 'more_math', 'constants')
17
+ for f in Dir['lib/more_math/constants/*.rb']
18
+ install(f, subdir)
19
+ end
@@ -0,0 +1,49 @@
1
+ require 'more_math'
2
+
3
+ module MoreMath
4
+ module CantorPairingFunction
5
+
6
+ module_function
7
+
8
+ def cantor_pairing(*xs)
9
+ if xs.size == 1 and xs.first.respond_to?(:to_ary)
10
+ xs = xs.first.to_ary
11
+ end
12
+ case xs.size
13
+ when 0, 1
14
+ raise ArgumentError, "at least two arguments are required"
15
+ when 2
16
+ x, y, = *xs
17
+ (x + y) * (x + y + 1) / 2 + y
18
+ else
19
+ cantor_pairing(cantor_pairing(*xs[0..1]), *xs[2..-1])
20
+ end
21
+ end
22
+
23
+ def self.cantor_pairing_inv_f(z)
24
+ z * (z + 1) / 2
25
+ end
26
+
27
+ def self.cantor_pairing_inv_q(z)
28
+ v = 0
29
+ while cantor_pairing_inv_f(v) <= z
30
+ v += 1
31
+ end
32
+ v - 1
33
+ end
34
+
35
+ def cantor_pairing_inv(c, n = 2)
36
+ raise ArgumentError, "n is required to be >= 2" unless n >= 2
37
+ result = []
38
+ begin
39
+ q = CantorPairingFunction.cantor_pairing_inv_q(c)
40
+ y = c - CantorPairingFunction.cantor_pairing_inv_f(q)
41
+ x = q - y
42
+ result.unshift y
43
+ c = x
44
+ n -= 1
45
+ end until n <= 1
46
+ result.unshift x
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,29 @@
1
+ module MoreMath
2
+ module Constants
3
+ module FunctionsConstants
4
+ LANCZOS_COEFFICIENTS = [
5
+ 0.99999999999999709182,
6
+ 57.156235665862923517,
7
+ -59.597960355475491248,
8
+ 14.136097974741747174,
9
+ -0.49191381609762019978,
10
+ 0.33994649984811888699e-4,
11
+ 0.46523628927048575665e-4,
12
+ -0.98374475304879564677e-4,
13
+ 0.15808870322491248884e-3,
14
+ -0.21026444172410488319e-3,
15
+ 0.21743961811521264320e-3,
16
+ -0.16431810653676389022e-3,
17
+ 0.84418223983852743293e-4,
18
+ -0.26190838401581408670e-4,
19
+ 0.36899182659531622704e-5,
20
+ ]
21
+
22
+ HALF_LOG_2_PI = 0.5 * Math.log(2 * Math::PI)
23
+
24
+ ERF_A = -8 * (Math::PI - 3) / (3 * Math::PI * (Math::PI - 4))
25
+
26
+ ROOT2 = Math.sqrt(2)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,140 @@
1
+ require 'more_math'
2
+
3
+ module MoreMath
4
+ # This class implements a continued fraction of the form:
5
+ #
6
+ # b_1
7
+ # a_0 + -------------------------
8
+ # b_2
9
+ # a_1 + --------------------
10
+ # b_3
11
+ # a_2 + ---------------
12
+ # b_4
13
+ # a_3 + ----------
14
+ # b_5
15
+ # a_4 + -----
16
+ # ...
17
+ #
18
+ class ContinuedFraction
19
+ # Creates a continued fraction instance. With the defaults for_a { 1 } and
20
+ # for_b { 1 } it approximates the golden ration phi if evaluated.
21
+ def initialize
22
+ @a = proc { 1.0 }
23
+ @b = proc { 1.0 }
24
+ end
25
+
26
+ # Creates a ContinuedFraction instances and passes its arguments to a call
27
+ # to for_a.
28
+ def self.for_a(arg = nil, &block)
29
+ new.for_a(arg, &block)
30
+ end
31
+
32
+ # Creates a ContinuedFraction instances and passes its arguments to a call
33
+ # to for_b.
34
+ def self.for_b(arg = nil, &block)
35
+ new.for_b(arg, &block)
36
+ end
37
+
38
+ # This method either takes a block or an argument +arg+. The argument +arg+
39
+ # has to respond to an integer index n >= 0 and return the value a_n. The
40
+ # block has to return the value for a_n when +n+ is passed as the first
41
+ # argument to the block. If a_n is dependent on an +x+ value (see the call
42
+ # method) the +x+ will be the second argument of the block.
43
+ def for_a(arg = nil, &block)
44
+ if arg and !block
45
+ @a = arg
46
+ elsif block and !arg
47
+ @a = block
48
+ else
49
+ raise ArgumentError, "exactly one argument or one block required"
50
+ end
51
+ self
52
+ end
53
+
54
+ # This method either takes a block or an argument +arg+. The argument +arg+
55
+ # has to respond to an integer index n >= 1 and return the value b_n. The
56
+ # block has to return the value for b_n when +n+ is passed as the first
57
+ # argument to the block. If b_n is dependent on an +x+ value (see the call
58
+ # method) the +x+ will be the second argument of the block.
59
+ def for_b(arg = nil, &block)
60
+ if arg and !block
61
+ @b = arg
62
+ elsif block and !arg
63
+ @b = block
64
+ else
65
+ raise ArgumentError, "exactly one argument or one block required"
66
+ end
67
+ self
68
+ end
69
+
70
+ # Returns the value for a_n or a_n(x).
71
+ def a(n, x = nil)
72
+ result = if x
73
+ @a[n, x]
74
+ else
75
+ @a[n]
76
+ end and result.to_f
77
+ end
78
+
79
+ # Returns the value for b_n or b_n(x).
80
+ def b(n, x = nil)
81
+ result = if x
82
+ @b[n, x]
83
+ else
84
+ @b[n]
85
+ end and result.to_f
86
+ end
87
+
88
+ # Evaluates the continued fraction for the value +x+ (if any) with the
89
+ # accuracy +epsilon+ and +max_iterations+ as the maximum number of
90
+ # iterations using the Wallis-method with scaling.
91
+ def call(x = nil, epsilon = 1E-16, max_iterations = 1 << 31)
92
+ c_0, c_1 = 1.0, a(0, x)
93
+ c_1 == nil and return 0 / 0.0
94
+ d_0, d_1 = 0.0, 1.0
95
+ result = c_1 / d_1
96
+ n = 0
97
+ error = 1 / 0.0
98
+ $DEBUG and warn "n=%u, a=%f, b=nil, c=%f, d=%f result=%f, error=nil" %
99
+ [ n, c_1, c_1, d_1, result ]
100
+ while n < max_iterations and error > epsilon
101
+ n += 1
102
+ a_n, b_n = a(n, x), b(n, x)
103
+ a_n and b_n or break
104
+ c_2 = a_n * c_1 + b_n * c_0
105
+ d_2 = a_n * d_1 + b_n * d_0
106
+ if c_2.infinite? or d_2.infinite?
107
+ if a_n != 0
108
+ c_2 = c_1 + (b_n / a_n * c_0)
109
+ d_2 = d_1 + (b_n / a_n * d_0)
110
+ elsif b_n != 0
111
+ c_2 = (a_n / b_n * c_1) + c_0
112
+ d_2 = (a_n / b_n * d_1) + d_0
113
+ else
114
+ raise Errno::ERANGE
115
+ end
116
+ end
117
+ r = c_2 / d_2
118
+ error = (r / result - 1).abs
119
+
120
+ result = r
121
+
122
+ $DEBUG and warn "n=%u, a=%f, b=%f, c=%f, d=%f, result=%f, error=%.16f" %
123
+ [ n, a_n, b_n, c_1, d_1, result, error ]
124
+
125
+ c_0, c_1 = c_1, c_2
126
+ d_0, d_1 = d_1, d_2
127
+ end
128
+ n >= max_iterations and raise Errno::ERANGE
129
+ result
130
+ end
131
+
132
+ alias [] call
133
+
134
+ # Returns this continued fraction as a Proc object which takes the same
135
+ # arguments like its call method does.
136
+ def to_proc
137
+ proc { |*a| call(*a) }
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,134 @@
1
+ require 'more_math'
2
+ require 'more_math/functions'
3
+ require 'more_math/constants/functions_constants'
4
+
5
+ module MoreMath
6
+ # This class is used to compute the Normal Distribution.
7
+ class NormalDistribution
8
+ include Functions
9
+ include Constants::FunctionsConstants
10
+
11
+ # Creates a NormalDistribution instance for the values +mu+ and +sigma+.
12
+ def initialize(mu = 0.0, sigma = 1.0)
13
+ @mu, @sigma = mu.to_f, sigma.to_f
14
+ end
15
+
16
+ attr_reader :mu
17
+
18
+ attr_reader :sigma
19
+
20
+ # Returns the cumulative probability (p-value) of the NormalDistribution
21
+ # for the value +x+.
22
+ def probability(x)
23
+ 0.5 * (1 + erf((x - @mu) / (@sigma * ROOT2)))
24
+ end
25
+
26
+ # Returns the inverse cumulative probability value of the
27
+ # NormalDistribution for the probability +p+.
28
+ def inverse_probability(p)
29
+ case
30
+ when p <= 0
31
+ -1 / 0.0
32
+ when p >= 1
33
+ 1 / 0.0
34
+ when p == 0.5 # This is a bit sloppy, maybe improve this later.
35
+ @mu
36
+ else
37
+ begin
38
+ NewtonBisection.new { |x| probability(x) - p }.solve(nil, 1_000_000)
39
+ rescue
40
+ 0 / 0.0
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ STD_NORMAL_DISTRIBUTION = NormalDistribution.new
47
+
48
+ # This class is used to compute the Chi-Square Distribution.
49
+ class ChiSquareDistribution
50
+ include Functions
51
+
52
+ # Creates a ChiSquareDistribution for +df+ degrees of freedom.
53
+ def initialize(df)
54
+ @df = df
55
+ @df_half = @df / 2.0
56
+ end
57
+
58
+ attr_reader :df
59
+
60
+ # Returns the cumulative probability (p-value) of the ChiSquareDistribution
61
+ # for the value +x+.
62
+ def probability(x)
63
+ if x < 0
64
+ 0.0
65
+ else
66
+ gammaP_regularized(x / 2, @df_half)
67
+ end
68
+ end
69
+
70
+ # Returns the inverse cumulative probability value of the
71
+ # NormalDistribution for the probability +p+.
72
+ def inverse_probability(p)
73
+ case
74
+ when p <= 0, p >= 1
75
+ 0.0
76
+ else
77
+ begin
78
+ bisect = NewtonBisection.new { |x| probability(x) - p }
79
+ range = bisect.bracket 0.5..10
80
+ bisect.solve(range, 1_000_000)
81
+ rescue
82
+ 0 / 0.0
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ # This class is used to compute the T-Distribution.
89
+ class TDistribution
90
+ include Functions
91
+
92
+ # Returns a TDistribution instance for the degrees of freedom +df+.
93
+ def initialize(df)
94
+ @df = df
95
+ end
96
+
97
+ # Degrees of freedom.
98
+ attr_reader :df
99
+
100
+ # Returns the cumulative probability (p-value) of the TDistribution for the
101
+ # t-value +x+.
102
+ def probability(x)
103
+ if x == 0
104
+ 0.5
105
+ else
106
+ t = beta_regularized(@df / (@df + x ** 2.0), 0.5 * @df, 0.5)
107
+ if x < 0.0
108
+ 0.5 * t
109
+ else
110
+ 1 - 0.5 * t
111
+ end
112
+ end
113
+ end
114
+
115
+ # Returns the inverse cumulative probability (t-value) of the TDistribution
116
+ # for the probability +p+.
117
+ def inverse_probability(p)
118
+ case
119
+ when p <= 0
120
+ -1 / 0.0
121
+ when p >= 1
122
+ 1 / 0.0
123
+ else
124
+ begin
125
+ bisect = NewtonBisection.new { |x| probability(x) - p }
126
+ range = bisect.bracket(-10..10)
127
+ bisect.solve(range, 1_000_000)
128
+ rescue
129
+ 0 / 0.0
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,6 @@
1
+ module MoreMath
2
+ module Exceptions
3
+ class MoreMathException < StandardError; end
4
+ class DivergentException < MoreMathException ; end
5
+ end
6
+ end
@@ -0,0 +1,151 @@
1
+ require 'more_math'
2
+
3
+ module MoreMath
4
+ module Functions
5
+ module_function
6
+
7
+ include Math
8
+ extend Math
9
+
10
+ # Returns the natural logarithm of Euler gamma function value for +x+ using
11
+ # the Lanczos approximation.
12
+ if Math.respond_to?(:lgamma)
13
+ def log_gamma(x)
14
+ lgamma(x).first
15
+ end
16
+ else
17
+ def log_gamma(x)
18
+ if x.nan? || x <= 0
19
+ 0 / 0.0
20
+ else
21
+ sum = 0.0
22
+ lc = Constants::FunctionsConstants::LANCZOS_COEFFICIENTS
23
+ half_log_2_pi = Constants::FunctionsConstants::HALF_LOG_2_PI
24
+ (lc.size - 1).downto(1) do |i|
25
+ sum += lc[i] / (x + i)
26
+ end
27
+ sum += lc[0]
28
+ tmp = x + 607.0 / 128 + 0.5
29
+ (x + 0.5) * log(tmp) - tmp + half_log_2_pi + log(sum / x)
30
+ end
31
+ rescue Errno::ERANGE, Errno::EDOM
32
+ 0 / 0.0
33
+ end
34
+ end
35
+
36
+ # Returns the natural logarithm of the beta function value for +(a, b)+.
37
+ def log_beta(a, b)
38
+ log_gamma(a) + log_gamma(b) - log_gamma(a + b)
39
+ rescue Errno::ERANGE, Errno::EDOM
40
+ 0 / 0.0
41
+ end
42
+
43
+ # Return an approximation value of Euler's regularized beta function for
44
+ # +x+, +a+, and +b+ with an error <= +epsilon+, but only iterate
45
+ # +max_iterations+-times.
46
+ def beta_regularized(x, a, b, epsilon = 1E-16, max_iterations = 1 << 16)
47
+ x, a, b = x.to_f, a.to_f, b.to_f
48
+ case
49
+ when a.nan? || b.nan? || x.nan? || a <= 0 || b <= 0 || x < 0 || x > 1
50
+ 0 / 0.0
51
+ when x > (a + 1) / (a + b + 2)
52
+ 1 - beta_regularized(1 - x, b, a, epsilon, max_iterations)
53
+ else
54
+ fraction = ContinuedFraction.for_b do |n, x|
55
+ if n % 2 == 0
56
+ m = n / 2.0
57
+ (m * (b - m) * x) / ((a + (2 * m) - 1) * (a + (2 * m)))
58
+ else
59
+ m = (n - 1) / 2.0
60
+ -((a + m) * (a + b + m) * x) / ((a + 2 * m) * (a + 2 * m + 1))
61
+ end
62
+ end
63
+ exp(a * log(x) + b * log(1.0 - x) - log(a) - log_beta(a, b)) /
64
+ fraction[x, epsilon, max_iterations]
65
+ end
66
+ rescue Errno::ERANGE, Errno::EDOM
67
+ 0 / 0.0
68
+ end
69
+
70
+ # Return an approximation of the regularized gammaP function for +x+ and
71
+ # +a+ with an error of <= +epsilon+, but only iterate
72
+ # +max_iterations+-times.
73
+ def gammaP_regularized(x, a, epsilon = 1E-16, max_iterations = 1 << 16)
74
+ x, a = x.to_f, a.to_f
75
+ case
76
+ when a.nan? || x.nan? || a <= 0 || x < 0
77
+ 0 / 0.0
78
+ when x == 0
79
+ 0.0
80
+ when 1 <= a && a < x
81
+ 1 - gammaQ_regularized(x, a, epsilon, max_iterations)
82
+ else
83
+ n = 0
84
+ an = 1 / a
85
+ sum = an
86
+ while an.abs > epsilon && n < max_iterations
87
+ n += 1
88
+ an *= x / (a + n)
89
+ sum += an
90
+ end
91
+ if n >= max_iterations
92
+ raise Errno::ERANGE
93
+ else
94
+ exp(-x + a * log(x) - log_gamma(a)) * sum
95
+ end
96
+ end
97
+ rescue Errno::ERANGE, Errno::EDOM
98
+ 0 / 0.0
99
+ end
100
+
101
+ # Return an approximation of the regularized gammaQ function for +x+ and
102
+ # +a+ with an error of <= +epsilon+, but only iterate
103
+ # +max_iterations+-times.
104
+ def gammaQ_regularized(x, a, epsilon = 1E-16, max_iterations = 1 << 16)
105
+ x, a = x.to_f, a.to_f
106
+ case
107
+ when a.nan? || x.nan? || a <= 0 || x < 0
108
+ 0 / 0.0
109
+ when x == 0
110
+ 1.0
111
+ when a > x || a < 1
112
+ 1 - gammaP_regularized(x, a, epsilon, max_iterations)
113
+ else
114
+ fraction = ContinuedFraction.for_a do |n, x|
115
+ (2 * n + 1) - a + x
116
+ end.for_b do |n, x|
117
+ n * (a - n)
118
+ end
119
+ exp(-x + a * log(x) - log_gamma(a)) *
120
+ fraction[x, epsilon, max_iterations] ** -1
121
+ end
122
+ rescue Errno::ERANGE, Errno::EDOM
123
+ 0 / 0.0
124
+ end
125
+
126
+ unless Math.respond_to?(:erf)
127
+ # Returns an approximate value for the error function's value for +x+.
128
+ def erf(x)
129
+ erf_a = MoreMath::Constants::FunctionsConstants::ERF_A
130
+ r = sqrt(1 - exp(-x ** 2 * (4 / Math::PI + erf_a * x ** 2) / (1 + erf_a * x ** 2)))
131
+ x < 0 ? -r : r
132
+ end
133
+ else
134
+ def erf(x)
135
+ Math.erf(x)
136
+ end
137
+ end
138
+
139
+ # Returns Cantor's tuple function for the tuple +*xs+ (the size must be at
140
+ # least 2).
141
+ def cantor_pairing(*xs)
142
+ CantorPairingFunction.cantor_pairing(*xs)
143
+ end
144
+
145
+ # Returns the inverse of Cantor's tuple function for the value +c+. +n+ is
146
+ # the length of the tuple (defaults to 2, a pair).
147
+ def cantor_pairing_inv(c, n = 2)
148
+ CantorPairingFunction.cantor_pairing_inv(c, n)
149
+ end
150
+ end
151
+ end