frac 0.9.3 → 0.9.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|