frac 0.9.3 → 0.9.4
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/Gemfile +10 -0
- data/README.rdoc +52 -33
- data/Rakefile +77 -0
- data/VERSION +2 -0
- data/ext/extconf.rb +0 -1
- data/ext/frac_ext.c +101 -90
- data/frac.gemspec +53 -0
- data/lib/frac.js +41 -0
- data/lib/frac.rb +62 -15
- data/test/frac_test.html +39 -0
- data/test/frac_test.rb +43 -12
- metadata +95 -15
data/Gemfile
ADDED
data/README.rdoc
CHANGED
@@ -1,33 +1,52 @@
|
|
1
|
-
= Find rational approximation to given real number
|
2
|
-
|
3
|
-
Convert Float to
|
4
|
-
|
5
|
-
Math.
|
6
|
-
Math.
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
Math.
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
(
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
1
|
+
= Find rational approximation to given real number
|
2
|
+
|
3
|
+
Convert Float or String to Fraction with given denominator maximum.
|
4
|
+
|
5
|
+
Math::Fraction.new(0.2) # => Fraction (1/5)
|
6
|
+
Math::Fraction.new("-3 1/8") # => Fraction (-3 1/8)
|
7
|
+
|
8
|
+
Math::Fraction.new(0.333) # => Fraction (1/3)
|
9
|
+
Math::Fraction.new("0.333") # => Fraction (1/3)
|
10
|
+
Math::Fraction.new(0.33, 100) # => Fraction (33/100)
|
11
|
+
Math::Fraction.new(1.to_f / 3) # => Fraction (1/3)
|
12
|
+
|
13
|
+
Math::Fraction.new(0.2).to_s # => String "1/5"
|
14
|
+
Math::Fraction.new(1.2).to_s # => String "1 1/5"
|
15
|
+
Math::Fraction.new(1.2).to_r # => Rational (6/5)
|
16
|
+
Math::Fraction.new(1.2).to_a # => Array [1, 1, 5]
|
17
|
+
Math::Fraction.new("0.333").to_a # => Array [0, 1, 3]
|
18
|
+
|
19
|
+
Math::Fraction.new(0.2, 100).to_r # => Rational (1/5)
|
20
|
+
Math::Fraction.new(0.33, 10).to_r # => Rational (1/3)
|
21
|
+
Math::Fraction.new(0.33, 100).to_r # => Rational (33/100)
|
22
|
+
|
23
|
+
== Difference from Ruby 1.9 built-in Float#to_r
|
24
|
+
|
25
|
+
# Built-in
|
26
|
+
1.1.to_r # => (2476979795053773/2251799813685248)
|
27
|
+
|
28
|
+
# Math::Fraction with big max denominator
|
29
|
+
Math::Fraction.new(1.1, 1_000_000_000_000_000_000).to_r # => (11/10)
|
30
|
+
|
31
|
+
== Installation
|
32
|
+
|
33
|
+
gem install frac
|
34
|
+
|
35
|
+
== Source
|
36
|
+
|
37
|
+
Idea and most implementation from http://www.ics.uci.edu/~eppstein/numth/frap.c
|
38
|
+
|
39
|
+
Based on the theory of continued fractions
|
40
|
+
|
41
|
+
if x = a1 + 1/(a2 + 1/(a3 + 1/(a4 + ...)))
|
42
|
+
|
43
|
+
then best approximation is found by truncating this series
|
44
|
+
(with some adjustments in the last term).
|
45
|
+
|
46
|
+
Note the fraction can be recovered as the first column of the matrix
|
47
|
+
|
48
|
+
( a1 1 ) ( a2 1 ) ( a3 1 ) ...
|
49
|
+
( 1 0 ) ( 1 0 ) ( 1 0 )
|
50
|
+
|
51
|
+
Instead of keeping the sequence of continued fraction terms,
|
52
|
+
we just keep the last partial product of these matrices.
|
data/Rakefile
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
|
4
|
+
begin
|
5
|
+
Bundler.setup(:default, :development)
|
6
|
+
rescue Bundler::BundlerError => e
|
7
|
+
$stderr.puts e.message
|
8
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
9
|
+
exit e.status_code
|
10
|
+
end
|
11
|
+
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
gem.name = %q{frac}
|
17
|
+
gem.homepage = %q{https://github.com/valodzka/frac}
|
18
|
+
gem.license = %q{}
|
19
|
+
gem.summary = %q{Find rational approximation to given real number}
|
20
|
+
gem.email = %q{pavel@valodzka.name}
|
21
|
+
gem.authors = [%q{Pavel Valodzka}]
|
22
|
+
gem.extensions = ["ext/extconf.rb"]
|
23
|
+
gem.require_paths = ["lib"]
|
24
|
+
gem.summary = <<-RDOC
|
25
|
+
Find rational approximation to given real number.
|
26
|
+
|
27
|
+
Based on the theory of continued fractions
|
28
|
+
|
29
|
+
if x = a1 + 1/(a2 + 1/(a3 + 1/(a4 + ...)))
|
30
|
+
|
31
|
+
then best approximation is found by truncating this series
|
32
|
+
(with some adjustments in the last term).
|
33
|
+
Note the fraction can be recovered as the first column of the matrix
|
34
|
+
|
35
|
+
( a1 1 ) ( a2 1 ) ( a3 1 ) ...
|
36
|
+
( 1 0 ) ( 1 0 ) ( 1 0 )
|
37
|
+
|
38
|
+
Instead of keeping the sequence of continued fraction terms,
|
39
|
+
we just keep the last partial product of these matrices.
|
40
|
+
RDOC
|
41
|
+
end
|
42
|
+
|
43
|
+
require 'rake/extensiontask'
|
44
|
+
Rake::ExtensionTask.new do |ext|
|
45
|
+
ext.ext_dir = 'ext'
|
46
|
+
ext.lib_dir = 'lib'
|
47
|
+
ext.name = 'frac_ext'
|
48
|
+
end
|
49
|
+
|
50
|
+
Jeweler::RubygemsDotOrgTasks.new
|
51
|
+
|
52
|
+
require 'rake/testtask'
|
53
|
+
Rake::TestTask.new(:test) do |test|
|
54
|
+
test.libs << 'lib' << 'test'
|
55
|
+
test.pattern = 'test/**/*.rb'
|
56
|
+
test.verbose = true
|
57
|
+
end
|
58
|
+
|
59
|
+
require 'rcov/rcovtask'
|
60
|
+
Rcov::RcovTask.new do |test|
|
61
|
+
test.libs << 'test'
|
62
|
+
test.pattern = 'test/**/*.rb'
|
63
|
+
test.verbose = true
|
64
|
+
end
|
65
|
+
|
66
|
+
task :default => [ :compile, :test ]
|
67
|
+
|
68
|
+
require 'rdoc/task'
|
69
|
+
Rake::RDocTask.new do |rdoc|
|
70
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
71
|
+
|
72
|
+
rdoc.rdoc_dir = 'rdoc'
|
73
|
+
rdoc.title = "frac #{version}"
|
74
|
+
rdoc.rdoc_files.include('README*')
|
75
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
76
|
+
end
|
77
|
+
|
data/VERSION
ADDED
data/ext/extconf.rb
CHANGED
data/ext/frac_ext.c
CHANGED
@@ -1,90 +1,101 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
**
|
4
|
-
**
|
5
|
-
**
|
6
|
-
**
|
7
|
-
**
|
8
|
-
**
|
9
|
-
**
|
10
|
-
**
|
11
|
-
**
|
12
|
-
**
|
13
|
-
**
|
14
|
-
**
|
15
|
-
**
|
16
|
-
**
|
17
|
-
**
|
18
|
-
** (
|
19
|
-
**
|
20
|
-
**
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
#include <
|
25
|
-
|
26
|
-
|
27
|
-
#
|
28
|
-
#
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
1
|
+
/*
|
2
|
+
** find rational approximation to given real number
|
3
|
+
** David Eppstein / UC Irvine / 8 Aug 1993
|
4
|
+
**
|
5
|
+
** With corrections from Arno Formella, May 2008
|
6
|
+
**
|
7
|
+
** usage: a.out r d
|
8
|
+
** r is real number to approx
|
9
|
+
** d is the maximum denominator allowed
|
10
|
+
**
|
11
|
+
** based on the theory of continued fractions
|
12
|
+
** if x = a1 + 1/(a2 + 1/(a3 + 1/(a4 + ...)))
|
13
|
+
** then best approximation is found by truncating this series
|
14
|
+
** (with some adjustments in the last term).
|
15
|
+
**
|
16
|
+
** Note the fraction can be recovered as the first column of the matrix
|
17
|
+
** ( a1 1 ) ( a2 1 ) ( a3 1 ) ...
|
18
|
+
** ( 1 0 ) ( 1 0 ) ( 1 0 )
|
19
|
+
** Instead of keeping the sequence of continued fraction terms,
|
20
|
+
** we just keep the last partial product of these matrices.
|
21
|
+
*/
|
22
|
+
|
23
|
+
#include <stdio.h>
|
24
|
+
#include <ruby.h>
|
25
|
+
|
26
|
+
#ifndef RFLOAT_VALUE
|
27
|
+
# define RFLOAT_VALUE(v) RFLOAT(rb_Float(v))->value
|
28
|
+
#endif
|
29
|
+
|
30
|
+
#ifdef HAVE_LONG_LONG
|
31
|
+
# define N_TYPE LONG_LONG
|
32
|
+
# define R2N NUM2LL
|
33
|
+
# define N2R LL2NUM
|
34
|
+
#else
|
35
|
+
# define N_TYPE long
|
36
|
+
# define R2N NUM2LONG
|
37
|
+
# define N2R LONG2NUM
|
38
|
+
#endif
|
39
|
+
|
40
|
+
static VALUE find_fracs(VALUE mod, VALUE rv, VALUE dv)
|
41
|
+
{
|
42
|
+
VALUE ret;
|
43
|
+
N_TYPE m[2][2], ai, maxden = R2N(rb_Integer(dv));
|
44
|
+
double startx, x = RFLOAT_VALUE(rb_Float(rv));
|
45
|
+
int sign = 1;
|
46
|
+
|
47
|
+
if (maxden <= 0)
|
48
|
+
rb_raise(rb_eArgError, "maximum denominator should be > 0");
|
49
|
+
|
50
|
+
if (x < 0) {
|
51
|
+
sign = -1;
|
52
|
+
x = -x;
|
53
|
+
}
|
54
|
+
|
55
|
+
startx = x;
|
56
|
+
|
57
|
+
/* initialize matrix */
|
58
|
+
m[0][0] = m[1][1] = 1;
|
59
|
+
m[0][1] = m[1][0] = 0;
|
60
|
+
|
61
|
+
/* loop finding terms until denom gets too big */
|
62
|
+
while (m[1][0] * ( ai = (N_TYPE)x ) + m[1][1] <= maxden) {
|
63
|
+
N_TYPE t;
|
64
|
+
t = m[0][0] * ai + m[0][1];
|
65
|
+
m[0][1] = m[0][0];
|
66
|
+
m[0][0] = t;
|
67
|
+
t = m[1][0] * ai + m[1][1];
|
68
|
+
m[1][1] = m[1][0];
|
69
|
+
m[1][0] = t;
|
70
|
+
if(x==(double)ai) break; // AF: division by zero
|
71
|
+
x = 1/(x - (double) ai);
|
72
|
+
if(x>(double)0x7FFFFFFF) break; // AF: representation failure
|
73
|
+
}
|
74
|
+
|
75
|
+
{
|
76
|
+
/* now remaining x is between 0 and 1/ai */
|
77
|
+
/* approx as either 0 or 1/m where m is max that will fit in maxden */
|
78
|
+
/* first try zero */
|
79
|
+
VALUE num1, den1, err1, num2, den2, err2;
|
80
|
+
|
81
|
+
num1 = N2R(sign*m[0][0]);
|
82
|
+
den1 = N2R(m[1][0]);
|
83
|
+
err1 = rb_float_new(startx - ((double) m[0][0] / (double) m[1][0]));
|
84
|
+
|
85
|
+
/* now try other possibility */
|
86
|
+
ai = (maxden - m[1][1]) / m[1][0];
|
87
|
+
m[0][0] = m[0][0] * ai + m[0][1];
|
88
|
+
m[1][0] = m[1][0] * ai + m[1][1];
|
89
|
+
|
90
|
+
num2 = N2R(sign*m[0][0]);
|
91
|
+
den2 = N2R(m[1][0]);
|
92
|
+
err2 = rb_float_new(startx - ((double) m[0][0] / (double) m[1][0]));
|
93
|
+
|
94
|
+
return rb_ary_new3(6, num1, den1, err1, num2, den2, err2);
|
95
|
+
}
|
96
|
+
}
|
97
|
+
|
98
|
+
void Init_frac_ext()
|
99
|
+
{
|
100
|
+
rb_define_module_function(rb_mMath, "find_fracs", find_fracs, 2);
|
101
|
+
}
|
data/frac.gemspec
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{frac}
|
8
|
+
s.version = "0.9.3"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Pavel Valodzka"]
|
12
|
+
s.date = %q{2011-06-19}
|
13
|
+
s.email = %q{pavel@valodzka.name}
|
14
|
+
s.extensions = ["ext/extconf.rb"]
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"README.rdoc"
|
17
|
+
]
|
18
|
+
s.files = [
|
19
|
+
"README.rdoc",
|
20
|
+
"VERSION",
|
21
|
+
"ext/extconf.rb",
|
22
|
+
"ext/frac_ext.c",
|
23
|
+
"frac.gemspec",
|
24
|
+
"lib/frac.js",
|
25
|
+
"lib/frac.rb",
|
26
|
+
"test/frac_test.html",
|
27
|
+
"test/frac_test.rb"
|
28
|
+
]
|
29
|
+
s.homepage = %q{https://github.com/valodzka/frac}
|
30
|
+
s.licenses = [""]
|
31
|
+
s.require_paths = ["lib"]
|
32
|
+
s.rubygems_version = %q{1.6.2}
|
33
|
+
s.summary = %q{Find rational approximation to given real number. Based on the theory of continued fractions if x = a1 + 1/(a2 + 1/(a3 + 1/(a4 + ...))) then best approximation is found by truncating this series (with some adjustments in the last term). Note the fraction can be recovered as the first column of the matrix ( a1 1 ) ( a2 1 ) ( a3 1 ) ... ( 1 0 ) ( 1 0 ) ( 1 0 ) Instead of keeping the sequence of continued fraction terms, we just keep the last partial product of these matrices.}
|
34
|
+
|
35
|
+
if s.respond_to? :specification_version then
|
36
|
+
s.specification_version = 3
|
37
|
+
|
38
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
39
|
+
s.add_development_dependency(%q<rake>, [">= 0"])
|
40
|
+
s.add_development_dependency(%q<jeweler>, [">= 0"])
|
41
|
+
s.add_development_dependency(%q<rcov>, [">= 0"])
|
42
|
+
else
|
43
|
+
s.add_dependency(%q<rake>, [">= 0"])
|
44
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
45
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
46
|
+
end
|
47
|
+
else
|
48
|
+
s.add_dependency(%q<rake>, [">= 0"])
|
49
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
50
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
data/lib/frac.js
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
|
2
|
+
(function(){
|
3
|
+
var find_fracs = function(x, maxden) {
|
4
|
+
var startx = x, ai;
|
5
|
+
var m = [[1.0, 0.0],[0.0, 1.0]];
|
6
|
+
|
7
|
+
if (maxden <= 0)
|
8
|
+
throw "Max denominator should be greater then 0";
|
9
|
+
|
10
|
+
while (m[1][0] * (ai = parseInt(x)) + m[1][1] <= maxden) {
|
11
|
+
var t = m[0][0] * ai + m[0][1];
|
12
|
+
m[0][1] = m[0][0];
|
13
|
+
m[0][0] = t;
|
14
|
+
t = m[1][0] * ai + m[1][1];
|
15
|
+
m[1][1] = m[1][0];
|
16
|
+
m[1][0] = t;
|
17
|
+
if (x == ai) break; /* AF: division by zero */
|
18
|
+
x = 1/(x - ai);
|
19
|
+
if (x > 0x7FFFFFFF) break /* AF: representation failure */
|
20
|
+
}
|
21
|
+
|
22
|
+
var num1 = m[0][0];
|
23
|
+
var den1 = m[1][0];
|
24
|
+
var err1 = startx - num1 / den1;
|
25
|
+
|
26
|
+
ai = (maxden - m[1][1]) / m[1][0];
|
27
|
+
m[0][0] = m[0][0] * ai + m[0][1];
|
28
|
+
m[1][0] = m[1][0] * ai + m[1][1];
|
29
|
+
|
30
|
+
var num2 = m[0][0];
|
31
|
+
var den2 = m[1][0];
|
32
|
+
var err2 = startx - num2 / den2;
|
33
|
+
|
34
|
+
return [err1, [num1, den1], err2, [num2, den2]];
|
35
|
+
};
|
36
|
+
|
37
|
+
Math["frac"] = function(x, maxden) {
|
38
|
+
var fracs = find_fracs(parseFloat(x), parseInt(maxden));
|
39
|
+
return Math.abs(fracs[0]) < Math.abs(fracs[2]) ? fracs[1] : fracs[3];
|
40
|
+
};
|
41
|
+
})();
|
data/lib/frac.rb
CHANGED
@@ -1,15 +1,62 @@
|
|
1
|
-
|
2
|
-
require
|
3
|
-
|
4
|
-
|
5
|
-
module Math
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
def frac(float, maxden)
|
11
|
-
arr = find_fracs(
|
12
|
-
arr[2].abs > arr[5].abs ? Rational(arr[3], arr[4]) : Rational(arr[0], arr[1])
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
1
|
+
require 'rational'
|
2
|
+
require File.join(File.dirname(__FILE__), %w{.. lib frac_ext})
|
3
|
+
|
4
|
+
# Rational approximation to given real number.
|
5
|
+
module Math
|
6
|
+
|
7
|
+
class << self
|
8
|
+
private :find_fracs
|
9
|
+
|
10
|
+
def frac(float, maxden)
|
11
|
+
arr = find_fracs(float, maxden)
|
12
|
+
arr[2].abs > arr[5].abs ? Rational(arr[3], arr[4]) : Rational(arr[0], arr[1])
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class Fraction
|
17
|
+
|
18
|
+
def initialize(float, maxden = 0x100)
|
19
|
+
if float.is_a?(String)
|
20
|
+
@r = 0
|
21
|
+
sign = 1
|
22
|
+
float.split(' ', 2).each do |part|
|
23
|
+
if (part.include?("/"))
|
24
|
+
@r += sign * Rational(*(part.split('/', 2).map(&method(:Integer))))
|
25
|
+
else
|
26
|
+
@r += Math.frac(part, maxden)
|
27
|
+
sign = @r >= 0 ? 1 : -1
|
28
|
+
end
|
29
|
+
end
|
30
|
+
else
|
31
|
+
@r = Math.frac(float, maxden)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_a
|
36
|
+
i = @r.to_i
|
37
|
+
sign = i >= 0 ? 1 : -1
|
38
|
+
[ i, (@r.numerator - i * @r.denominator) * sign, @r.denominator ]
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_r
|
42
|
+
@r
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_f
|
46
|
+
@r.to_f
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_s
|
50
|
+
n = to_a
|
51
|
+
if n[1] == 0
|
52
|
+
n[0].to_s
|
53
|
+
elsif n[0] == 0
|
54
|
+
"#{n[1]}/#{n[2]}"
|
55
|
+
else
|
56
|
+
"#{n[0]} #{n[1]}/#{n[2]}"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
data/test/frac_test.html
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
|
2
|
+
<script src="../lib/frac.js" type="text/javascript"></script>
|
3
|
+
|
4
|
+
<script>
|
5
|
+
function msg(str) {
|
6
|
+
document.write(str);
|
7
|
+
};
|
8
|
+
|
9
|
+
function err(str) {
|
10
|
+
msg("<p style='color:red;'>"+str+"</p>");
|
11
|
+
};
|
12
|
+
|
13
|
+
function ok(str) {
|
14
|
+
msg("<p style='color:green'>"+str+"</p>");
|
15
|
+
};
|
16
|
+
|
17
|
+
function assert(expected, result, msg) {
|
18
|
+
if (expected != result && expected.toString() != result.toString())
|
19
|
+
err("Error, expected " + expected + ", got " + result + ": " + msg);
|
20
|
+
else
|
21
|
+
ok("OK: " + msg);
|
22
|
+
};
|
23
|
+
|
24
|
+
var tests = [
|
25
|
+
[ 0.333,[ 1, 3], 0x100],
|
26
|
+
[ 0.77, [77, 100], 0x100],
|
27
|
+
[ 0.2, [ 1, 5], 0x100],
|
28
|
+
[ 2, [ 2, 1], 0x100],
|
29
|
+
[-0.5, [-1, 2], 0x100],
|
30
|
+
[ 0, [ 0, 1], 0x100],
|
31
|
+
["0.85",[17, 20], 0x100],
|
32
|
+
[ 0.5, [ 1, 2], '1000'],
|
33
|
+
[ 0.001,[ 0, 1], 2]];
|
34
|
+
|
35
|
+
for (var i = 0; i < tests.length; ++i) {
|
36
|
+
var sample = tests[i];
|
37
|
+
assert(sample[1], Math.frac(sample[0], sample[2]), sample[0] + " -> " + sample[1][0] + " / " + sample[1][1]);
|
38
|
+
}
|
39
|
+
</script>
|
data/test/frac_test.rb
CHANGED
@@ -3,17 +3,40 @@ require File.join(File.dirname(__FILE__), %w{.. lib frac})
|
|
3
3
|
|
4
4
|
class TC_Frac < Test::Unit::TestCase
|
5
5
|
def test_frac
|
6
|
-
[[ 0.333, 1,
|
7
|
-
[
|
8
|
-
[ 0.
|
9
|
-
[ 2,
|
10
|
-
[
|
11
|
-
[
|
12
|
-
[
|
13
|
-
[
|
14
|
-
[
|
15
|
-
|
16
|
-
|
6
|
+
[[ 0.333, [1, 3], 0x100, [0, 1, 3], "1/3" ],
|
7
|
+
[ 1.to_f/3, [1, 3], 0x100, [0, 1, 3], "1/3" ],
|
8
|
+
[ 0.77, [77, 100], 0x100, [0, 77, 100], "77/100" ],
|
9
|
+
[ 0.2, [1, 5], 0x100, [0, 1, 5], "1/5" ],
|
10
|
+
[ 2, [2, 1], 0x100, [2, 0, 1], "2" ],
|
11
|
+
[-0.5, [-1, 2], 0x100, [0, -1, 2], "-1/2" ],
|
12
|
+
[-1.5, [-3, 2], 0x100, [-1, 1, 2], "-1 1/2" ],
|
13
|
+
[-1.7, [-17, 10], 0x100, [-1, 7, 10], "-1 7/10" ],
|
14
|
+
[-2.7, [-27, 10], 10, [-2, 7, 10], "-2 7/10" ],
|
15
|
+
[ 0, [0, 1], 0x100, [0, 0, 1], "0" ],
|
16
|
+
[ 0.85, [17, 20], 0x100, [0, 17, 20], "17/20" ],
|
17
|
+
[ 0.5, [1, 2], '1000', [0, 1, 2], "1/2" ],
|
18
|
+
[ 0.001, [0, 1], 2, [0, 0, 1], "0" ],
|
19
|
+
[ 0.001, [1, 1000], 1000, [0, 1, 1000], "1/1000" ],
|
20
|
+
[ 3.56, [89, 25], 0x100, [3, 14, 25], "3 14/25" ],
|
21
|
+
[ "3.56", [89, 25], 0x100, [3, 14, 25], "3 14/25" ],
|
22
|
+
].each{|float, num_den, prec, n, s|
|
23
|
+
assert_equal Rational(num_den[0], num_den[1]), Math.frac(float, prec), "#{float.inspect} -> #{num_den[0]} / #{num_den[1]}"
|
24
|
+
assert_equal n, Math::Fraction.new(float, prec).to_a, "#{float.inspect} -> #{n}"
|
25
|
+
assert_equal s, Math::Fraction.new(float, prec).to_s, "#{float.inspect} -> #{s}"
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_frac_strings
|
30
|
+
[[ "0.333", [0, 1, 3], "1/3", 1.to_f / 3 ],
|
31
|
+
[ "3.56", [3, 14, 25], "3 14/25", 3.56 ],
|
32
|
+
[ "3 1/8", [3, 1, 8], "3 1/8", 3.125 ],
|
33
|
+
[ "-3 1/8", [-3, 1, 8], "-3 1/8", -3.125 ],
|
34
|
+
[ "-1/8", [0, -1, 8], "-1/8", -0.125 ],
|
35
|
+
].each{|float, n, s, f|
|
36
|
+
r = Math::Fraction.new(float)
|
37
|
+
assert_equal n, r.to_a, "#{float.inspect} -> #{n}"
|
38
|
+
assert_equal s, r.to_s, "#{float.inspect} -> #{s}"
|
39
|
+
assert_equal f, r.to_f, "#{float.inspect} -> #{f}"
|
17
40
|
}
|
18
41
|
end
|
19
42
|
|
@@ -23,11 +46,19 @@ class TC_Frac < Test::Unit::TestCase
|
|
23
46
|
end
|
24
47
|
end
|
25
48
|
|
26
|
-
def
|
49
|
+
def test_frac_validation
|
27
50
|
[[1.1, ''], [nil, 0x100], [1.1, 0], [1.5, -100]].each{|params|
|
28
51
|
assert_raise ArgumentError, TypeError do
|
29
52
|
Math.frac(*params)
|
30
53
|
end
|
31
54
|
}
|
32
55
|
end
|
56
|
+
|
57
|
+
def test_fractions_validation
|
58
|
+
['junk', 'junk 2/3', '10 junk/2', '11 3/junk', nil].each{|param|
|
59
|
+
assert_raise ArgumentError, TypeError, "For param #{param}" do
|
60
|
+
Math::Fraction.new(param)
|
61
|
+
end
|
62
|
+
}
|
63
|
+
end
|
33
64
|
end
|
metadata
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: frac
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
+
hash: 51
|
4
5
|
prerelease: false
|
5
6
|
segments:
|
6
7
|
- 0
|
7
8
|
- 9
|
8
|
-
-
|
9
|
-
version: 0.9.
|
9
|
+
- 4
|
10
|
+
version: 0.9.4
|
10
11
|
platform: ruby
|
11
12
|
authors:
|
12
13
|
- Pavel Valodzka
|
@@ -14,53 +15,132 @@ autorequire:
|
|
14
15
|
bindir: bin
|
15
16
|
cert_chain: []
|
16
17
|
|
17
|
-
date:
|
18
|
+
date: 2011-06-24 00:00:00 +03:00
|
18
19
|
default_executable:
|
19
|
-
dependencies:
|
20
|
-
|
21
|
-
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
prerelease: false
|
23
|
+
type: :development
|
24
|
+
name: rake
|
25
|
+
version_requirements: &id001 !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ">="
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
hash: 3
|
31
|
+
segments:
|
32
|
+
- 0
|
33
|
+
version: "0"
|
34
|
+
requirement: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
prerelease: false
|
37
|
+
type: :development
|
38
|
+
name: jeweler
|
39
|
+
version_requirements: &id002 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
hash: 3
|
45
|
+
segments:
|
46
|
+
- 0
|
47
|
+
version: "0"
|
48
|
+
requirement: *id002
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
prerelease: false
|
51
|
+
type: :development
|
52
|
+
name: rcov
|
53
|
+
version_requirements: &id003 !ruby/object:Gem::Requirement
|
54
|
+
none: false
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
hash: 3
|
59
|
+
segments:
|
60
|
+
- 0
|
61
|
+
version: "0"
|
62
|
+
requirement: *id003
|
63
|
+
- !ruby/object:Gem::Dependency
|
64
|
+
prerelease: false
|
65
|
+
type: :development
|
66
|
+
name: rdoc
|
67
|
+
version_requirements: &id004 !ruby/object:Gem::Requirement
|
68
|
+
none: false
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
hash: 3
|
73
|
+
segments:
|
74
|
+
- 0
|
75
|
+
version: "0"
|
76
|
+
requirement: *id004
|
77
|
+
- !ruby/object:Gem::Dependency
|
78
|
+
prerelease: false
|
79
|
+
type: :development
|
80
|
+
name: rake-compiler
|
81
|
+
version_requirements: &id005 !ruby/object:Gem::Requirement
|
82
|
+
none: false
|
83
|
+
requirements:
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
hash: 3
|
87
|
+
segments:
|
88
|
+
- 0
|
89
|
+
version: "0"
|
90
|
+
requirement: *id005
|
91
|
+
description:
|
22
92
|
email: pavel@valodzka.name
|
23
93
|
executables: []
|
24
94
|
|
25
95
|
extensions:
|
26
96
|
- ext/extconf.rb
|
27
|
-
extra_rdoc_files:
|
28
|
-
|
97
|
+
extra_rdoc_files:
|
98
|
+
- README.rdoc
|
29
99
|
files:
|
30
|
-
-
|
100
|
+
- Gemfile
|
101
|
+
- README.rdoc
|
102
|
+
- Rakefile
|
103
|
+
- VERSION
|
31
104
|
- ext/extconf.rb
|
105
|
+
- ext/frac_ext.c
|
106
|
+
- frac.gemspec
|
107
|
+
- lib/frac.js
|
32
108
|
- lib/frac.rb
|
109
|
+
- test/frac_test.html
|
33
110
|
- test/frac_test.rb
|
34
|
-
- README.rdoc
|
35
111
|
has_rdoc: true
|
36
|
-
homepage:
|
37
|
-
licenses:
|
38
|
-
|
112
|
+
homepage: https://github.com/valodzka/frac
|
113
|
+
licenses:
|
114
|
+
- ""
|
39
115
|
post_install_message:
|
40
116
|
rdoc_options: []
|
41
117
|
|
42
118
|
require_paths:
|
43
119
|
- lib
|
44
120
|
required_ruby_version: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
45
122
|
requirements:
|
46
123
|
- - ">="
|
47
124
|
- !ruby/object:Gem::Version
|
125
|
+
hash: 3
|
48
126
|
segments:
|
49
127
|
- 0
|
50
128
|
version: "0"
|
51
129
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
130
|
+
none: false
|
52
131
|
requirements:
|
53
132
|
- - ">="
|
54
133
|
- !ruby/object:Gem::Version
|
134
|
+
hash: 3
|
55
135
|
segments:
|
56
136
|
- 0
|
57
137
|
version: "0"
|
58
138
|
requirements: []
|
59
139
|
|
60
140
|
rubyforge_project:
|
61
|
-
rubygems_version: 1.3.
|
141
|
+
rubygems_version: 1.3.7
|
62
142
|
signing_key:
|
63
|
-
specification_version:
|
143
|
+
specification_version: 3
|
64
144
|
summary: Find rational approximation to given real number. Based on the theory of continued fractions if x = a1 + 1/(a2 + 1/(a3 + 1/(a4 + ...))) then best approximation is found by truncating this series (with some adjustments in the last term). Note the fraction can be recovered as the first column of the matrix ( a1 1 ) ( a2 1 ) ( a3 1 ) ... ( 1 0 ) ( 1 0 ) ( 1 0 ) Instead of keeping the sequence of continued fraction terms, we just keep the last partial product of these matrices.
|
65
145
|
test_files: []
|
66
146
|
|