more_math 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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