rical 1.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b1ce7f2b2a92c5a7b58a2328a98fcabfdc447efa
4
+ data.tar.gz: a26b4642e560f673200ed22f2e5d20de2dceb517
5
+ SHA512:
6
+ metadata.gz: 4957d9249119585429d3c9ea2e331a73613898d50b2691c2d7dcb7b47ed55863526c8b295424296ccc5c901dbeb06d5d612fb956968239df7276b2a7016affec
7
+ data.tar.gz: 10a861d8159996d6dd080f69d549c03a8ed42bc2e8206fa511986c64df9fd24a3d34cf347b6042c32e52fe7b854a134989321e82931d55a188b5e6dceb0380e2
@@ -0,0 +1,17 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ *.swp
16
+ *.swo
17
+ *~
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rical.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Luis Bacelar
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,97 @@
1
+ # **RICAL**
2
+ ### **R**oot and **I**nverse **CAL**culator
3
+
4
+ Using Newton-Raphson or Secant methods, and within an arbitrary error:
5
+
6
+ * Computes the values of a root of an arbitrary function _f(x)_
7
+ * Computes the value of inverse function _f<sup>-1</sup>(y) = x_ at point _y_
8
+
9
+ The Newton-Raphson requires:
10
+
11
+ * one "close enough" estimate - _x<sub>0</sub>_ - of the root or inverse value
12
+ * the derivative function _f'(x)_
13
+
14
+ The Secant method requires:
15
+
16
+ * two "close enough" estimates - _x<sub>0</sub>_, _x<sub>1</sub>_ - of the root or inverse value
17
+
18
+ If the estimates are not "close enough" (and what that means depends on _f(x)_), the methods may not converge to a solution.
19
+ This may also happen for a "low" number of iterations and/or a "low" error limit.
20
+ When no convergence is possible on the set conditions, a **`NoConvergenceError`** is raised.
21
+
22
+
23
+ ## Installation
24
+
25
+ Add this line to your application's Gemfile:
26
+
27
+ ```ruby
28
+ gem 'rical'
29
+ ```
30
+
31
+ And then execute:
32
+
33
+ $ bundle
34
+
35
+ Or install it yourself as:
36
+
37
+ $ gem install rical
38
+
39
+
40
+ ## Usage examples
41
+
42
+ 1. Set function(s) as lambdas
43
+
44
+ ```ruby
45
+ fx = -> (x) { x**2 - 1 }
46
+ dfx = -> (x) { 2*x }
47
+ ```
48
+
49
+ or set function(s) as Proc
50
+
51
+ ```ruby
52
+ fx = Proc.new do |x|
53
+ x**2 - 1
54
+ end
55
+ ```
56
+
57
+ or set function(s) as existing method(s)
58
+
59
+ ```ruby
60
+ def f(x)
61
+ x**2 - 1
62
+ end
63
+
64
+ fx = method(:f)
65
+ ```
66
+
67
+ 2. Call `root_for` of `inverse_for` passing function(s) and estimate(s):
68
+
69
+ **Newton-Raphson's Method**
70
+
71
+ ```ruby
72
+ Rical.root_for f: fx, df: dfx, x0: 1.0, method: :newtonr_raphson # :newton_raphson aliased to :n, :newton
73
+ Rical.inverse_for f: fx, df: dfx, x0: 1.0, y: 2.0, method: :newton
74
+ ```
75
+
76
+ **Secant's Method**
77
+
78
+ ```ruby
79
+ Rical.root_for f: fx, x0: 0.0, x1: 1.0, method: :secant # :secant aliased to :sec, :s
80
+ Rical.inverse_for f: fx, x0: 0.0, x1: 1.0, y: 2.0, method: :secant
81
+ ```
82
+
83
+ 3. Change number of iterations - _num_ - or maximum allowable error - _err_ - if needed:
84
+
85
+ ```ruby
86
+ Rical.root_for f: fx, ......, num: 1e3, err: 1e-12 # defaults num: 100, err: 1e-6
87
+ Rical.inverse_for f: fx, ......, num: 1e3, err: 1e-12 # defaults num: 100, err: 1e-6
88
+ ```
89
+
90
+
91
+ ## Contributing
92
+
93
+ 1. Fork it ( https://github.com/[my-github-username]/rical/fork )
94
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
95
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
96
+ 4. Push to the branch (`git push origin my-new-feature`)
97
+ 5. Create a new Pull Request
@@ -0,0 +1,3 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ Dir.glob('tasks/**/*.rake').each(&method(:import))
@@ -0,0 +1,49 @@
1
+ require 'rical/version'
2
+ require 'rical/errors'
3
+
4
+ module Rical
5
+ extend self
6
+
7
+ def root_for f:, df: nil, x0: 0.0, x1: 1.0, method:, num: 100, err: 1e-6
8
+ raise ArgumentError, 'f must respond_to call' unless f.respond_to? :call
9
+
10
+ case method
11
+ when :n, :newton, :newton_raphson
12
+ raise ArgumentError, 'df is required' unless df
13
+ raise ArgumentError, 'df must respond_to call' unless df.respond_to? :call
14
+ newton_raphson_root_for f, df, Float(x0), num, err
15
+ when :s, :sec, :secant
16
+ secant_root_for f, Float(x0), Float(x1), num, err
17
+ else
18
+ raise ArgumentError, 'unexpected method (allowed :newton, :secant)'
19
+ end
20
+ end
21
+
22
+ def inverse_for f:, y: 0.0, **args
23
+ f_1= -> (x) { f.call(x) - Float(y) }
24
+ root_for f: f_1, **args
25
+ end
26
+ alias_method :inv_for, :inverse_for
27
+
28
+ private
29
+ def newton_raphson_root_for f, df, x, n, err
30
+ n.times do
31
+ dfx = df.call x
32
+ dfx = [1e-3, err * 1e3].max if dfx == 0.0
33
+ x = x - ( f.call(x) / dfx )
34
+ return x if f.call(x).abs < err
35
+ end
36
+ raise NoConvergenceError
37
+ end
38
+
39
+ def secant_root_for f, x0, x1, n, err
40
+ n.times do
41
+ delta = f.call(x1) - f.call(x0)
42
+ delta = [1e-3, err * 1e3].max if delta == 0.0
43
+ x = x1 - (f.call(x1) * (x1 - x0)) / delta
44
+ return x if f.call(x).abs < err
45
+ x0, x1 = x1, x
46
+ end
47
+ raise NoConvergenceError
48
+ end
49
+ end
@@ -0,0 +1,4 @@
1
+ module Rical
2
+ class NoConvergenceError < StandardError
3
+ end
4
+ end
@@ -0,0 +1,3 @@
1
+ module Rical
2
+ VERSION = '1.0.0'
3
+ end
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'rical/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'rical'
8
+ spec.version = Rical::VERSION
9
+ spec.authors = ['Luis Bacelar']
10
+ spec.email = ['lmbacelar@gmail.com']
11
+ spec.summary = %q{Computes roots and x value for given y for arbitrary math f(x)=y}
12
+ spec.description = <<-DESCRIPTION
13
+ Computes approximation within a given error_limit to the values of a root of an arbitrary function f(x) or to the value of inverse function g(y) = x.
14
+ Uses Newton-Raphson or Secant methods.
15
+ Requires one (Newton-Raphson) or two (Secant) estimates of the target value.
16
+ Raises NoConvergenceError when it does not converge within the set error_limit, on set number of iterations.
17
+ DESCRIPTION
18
+ spec.homepage = ''
19
+ spec.license = 'MIT'
20
+
21
+ spec.files = `git ls-files -z`.split("\x0")
22
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
23
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
24
+ spec.require_paths = ['lib']
25
+
26
+ spec.add_development_dependency 'bundler', '~> 1.7'
27
+ spec.add_development_dependency 'rake', '~> 10.0'
28
+ spec.add_development_dependency 'rspec'
29
+ end
@@ -0,0 +1,78 @@
1
+ require 'spec_helper'
2
+
3
+ describe Rical do
4
+ f = -> (x) { x**2 - 2 }
5
+ df = -> (x) { 2*x }
6
+
7
+ context 'Newton-Raphson method' do
8
+ context 'root computation' do
9
+ it 'yields +√2 for f(x) = x²-2' do
10
+ expect(Rical.root_for f: f, df: df, x0: 10, method: :newton).to be_within(1e-6).of(2**0.5)
11
+ end
12
+
13
+ it 'yields -√2 for f(x) = x²-2' do
14
+ expect(Rical.root_for f: f, df: df, x0: -10, method: :newton).to be_within(1e-6).of(-2**0.5)
15
+ end
16
+
17
+ it 'rescues when df(x0) = 0' do
18
+ expect(Rical.root_for f: f, df: df, x0: 0.0, method: :newton).to be_within(1e-6).of(2**0.5)
19
+ end
20
+ end
21
+
22
+ context 'inverse computation' do
23
+ it 'yields +2.0 for y=2, f(x)=y=x²-2' do
24
+ expect(Rical.inverse_for f: f, df: df, y: 2, x0: 0, method: :newton).to be_within(1e-6).of(2.0)
25
+ end
26
+ end
27
+
28
+ end
29
+
30
+ context 'Secant method' do
31
+ context 'root computation' do
32
+ it 'yields +√2 for f(x) = x²-2' do
33
+ expect(Rical.root_for f: f, x0: 10, x1: 9, method: :secant).to be_within(1e-6).of(2**0.5)
34
+ end
35
+
36
+ it 'yields -√2 for f(x) = x²-2' do
37
+ expect(Rical.root_for f: f, x0: -10, x1: -9, method: :secant).to be_within(1e-6).of(-2**0.5)
38
+ end
39
+
40
+ it 'rescues when f(x1) - f(x0) = 0' do
41
+ # converges but to either one of the roots
42
+ expect(Rical.root_for(f: f, x0: 9, x1: -9, method: :secant).abs).to be_within(1e-6).of(2**0.5)
43
+ end
44
+ end
45
+
46
+ context 'inverse computation' do
47
+ it 'yields +2.0 for y=2, f(x)=y=x²-2' do
48
+ expect(Rical.inverse_for f: f, y: 2, x0: 0, x1: 1, method: :secant).to be_within(1e-6).of(2.0)
49
+ end
50
+ end
51
+ end
52
+
53
+ context 'error handling' do
54
+ it 'raises ArgumentError when f is not callable' do
55
+ expect{ Rical.root_for f: :dummy, df: df, method: :newton }.to raise_error ArgumentError
56
+ expect{ Rical.root_for f: :dummy, x0: 0, x1: 1, method: :secant }.to raise_error ArgumentError
57
+ end
58
+
59
+ it 'raises ArgumentError when no df given and method is Newton-Raphson' do
60
+ expect{ Rical.root_for f: f, method: :newton }.to raise_error ArgumentError
61
+ expect{ Rical.inverse_for f: f, method: :newton }.to raise_error ArgumentError
62
+ end
63
+
64
+ it 'raises ArgumentError when df is not callable and method is Newton-Raphson' do
65
+ expect{ Rical.root_for f: f, df: :dummy, method: :newton }.to raise_error ArgumentError
66
+ end
67
+
68
+ it 'raises NoConvergenceError when Newton-Raphson does not converge' do
69
+ expect{ Rical.root_for f: f, df: df, x0: 1e32, method: :newton, num: 10
70
+ }.to raise_error Rical::NoConvergenceError
71
+ end
72
+
73
+ it 'raises NoConvergenceError when Secant does not converge' do
74
+ expect{ Rical.root_for f: f, x0: 1e32, x1: 1.1e32, method: :secant, num: 10
75
+ }.to raise_error Rical::NoConvergenceError
76
+ end
77
+ end
78
+ end
@@ -0,0 +1 @@
1
+ require 'rical'
@@ -0,0 +1,3 @@
1
+ require 'rspec/core/rake_task'
2
+
3
+ RSpec::Core::RakeTask.new(:spec)
metadata ADDED
@@ -0,0 +1,105 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rical
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Luis Bacelar
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-01-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: |
56
+ Computes approximation within a given error_limit to the values of a root of an arbitrary function f(x) or to the value of inverse function g(y) = x.
57
+ Uses Newton-Raphson or Secant methods.
58
+ Requires one (Newton-Raphson) or two (Secant) estimates of the target value.
59
+ Raises NoConvergenceError when it does not converge within the set error_limit, on set number of iterations.
60
+ email:
61
+ - lmbacelar@gmail.com
62
+ executables: []
63
+ extensions: []
64
+ extra_rdoc_files: []
65
+ files:
66
+ - ".gitignore"
67
+ - ".rspec"
68
+ - Gemfile
69
+ - LICENSE.txt
70
+ - README.md
71
+ - Rakefile
72
+ - lib/rical.rb
73
+ - lib/rical/errors.rb
74
+ - lib/rical/version.rb
75
+ - rical.gemspec
76
+ - spec/rical_spec.rb
77
+ - spec/spec_helper.rb
78
+ - tasks/rspec.rake
79
+ homepage: ''
80
+ licenses:
81
+ - MIT
82
+ metadata: {}
83
+ post_install_message:
84
+ rdoc_options: []
85
+ require_paths:
86
+ - lib
87
+ required_ruby_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ requirements: []
98
+ rubyforge_project:
99
+ rubygems_version: 2.4.5
100
+ signing_key:
101
+ specification_version: 4
102
+ summary: Computes roots and x value for given y for arbitrary math f(x)=y
103
+ test_files:
104
+ - spec/rical_spec.rb
105
+ - spec/spec_helper.rb