silicium 0.0.15 → 0.0.20
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.
- checksums.yaml +4 -4
- data/.codeclimate.yml +4 -0
- data/.gitignore +3 -1
- data/.travis.yml +12 -1
- data/Gemfile +4 -0
- data/Makefile +269 -0
- data/README.md +2 -0
- data/Rakefile +7 -0
- data/docs/Object.html +117 -0
- data/docs/README_md.html +142 -0
- data/docs/Silicium.html +101 -0
- data/docs/Silicium/Combinatorics.html +270 -0
- data/docs/Silicium/Dice.html +99 -0
- data/docs/Silicium/Dice/Polyhedron.html +315 -0
- data/docs/Silicium/Dice/PolyhedronSet.html +321 -0
- data/docs/Silicium/Error.html +106 -0
- data/docs/Silicium/Geometry.html +940 -0
- data/docs/Silicium/Geometry/Line2dCanon.html +243 -0
- data/docs/Silicium/Geometry/VariablesOrderException.html +106 -0
- data/docs/Silicium/Graphs.html +164 -0
- data/docs/Silicium/Graphs/GraphError.html +106 -0
- data/docs/Silicium/Graphs/OrientedGraph.html +775 -0
- data/docs/Silicium/Graphs/UnorientedGraph.html +284 -0
- data/docs/Silicium/IntegralDoesntExistError.html +106 -0
- data/docs/Silicium/NumericalIntegration.html +521 -0
- data/docs/Silicium/Optimization.html +639 -0
- data/docs/Silicium/Plotter.html +186 -0
- data/docs/Silicium/Plotter/Image.html +297 -0
- data/docs/created.rid +9 -0
- data/docs/css/fonts.css +167 -0
- data/docs/css/rdoc.css +619 -0
- data/docs/fonts/Lato-Light.ttf +0 -0
- data/docs/fonts/Lato-LightItalic.ttf +0 -0
- data/docs/fonts/Lato-Regular.ttf +0 -0
- data/docs/fonts/Lato-RegularItalic.ttf +0 -0
- data/docs/fonts/SourceCodePro-Bold.ttf +0 -0
- data/docs/fonts/SourceCodePro-Regular.ttf +0 -0
- data/docs/images/add.png +0 -0
- data/docs/images/arrow_up.png +0 -0
- data/docs/images/brick.png +0 -0
- data/docs/images/brick_link.png +0 -0
- data/docs/images/bug.png +0 -0
- data/docs/images/bullet_black.png +0 -0
- data/docs/images/bullet_toggle_minus.png +0 -0
- data/docs/images/bullet_toggle_plus.png +0 -0
- data/docs/images/date.png +0 -0
- data/docs/images/delete.png +0 -0
- data/docs/images/find.png +0 -0
- data/docs/images/loadingAnimation.gif +0 -0
- data/docs/images/macFFBgHack.png +0 -0
- data/docs/images/package.png +0 -0
- data/docs/images/page_green.png +0 -0
- data/docs/images/page_white_text.png +0 -0
- data/docs/images/page_white_width.png +0 -0
- data/docs/images/plugin.png +0 -0
- data/docs/images/ruby.png +0 -0
- data/docs/images/tag_blue.png +0 -0
- data/docs/images/tag_green.png +0 -0
- data/docs/images/transparent.png +0 -0
- data/docs/images/wrench.png +0 -0
- data/docs/images/wrench_orange.png +0 -0
- data/docs/images/zoom.png +0 -0
- data/docs/index.html +132 -0
- data/docs/js/darkfish.js +84 -0
- data/docs/js/navigation.js +105 -0
- data/docs/js/navigation.js.gz +0 -0
- data/docs/js/search.js +110 -0
- data/docs/js/search_index.js +1 -0
- data/docs/js/search_index.js.gz +0 -0
- data/docs/js/searcher.js +229 -0
- data/docs/js/searcher.js.gz +0 -0
- data/docs/table_of_contents.html +608 -0
- data/lib/geometry.rb +236 -0
- data/lib/graph.rb +164 -0
- data/lib/numerical_integration.rb +147 -0
- data/lib/optimization.rb +144 -0
- data/lib/plotter.rb +96 -0
- data/lib/silicium.rb +0 -1
- data/lib/silicium/version.rb +1 -1
- data/lib/theory_of_probability.rb +227 -0
- data/silicium.gemspec +4 -2
- metadata +90 -3
data/lib/geometry.rb
ADDED
@@ -0,0 +1,236 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Silicium
|
4
|
+
|
5
|
+
module Geometry
|
6
|
+
|
7
|
+
##
|
8
|
+
# Represents a point as two coordinates
|
9
|
+
# in two-dimensional space
|
10
|
+
Point = Struct.new(:x, :y)
|
11
|
+
|
12
|
+
##
|
13
|
+
# Represents a point as three coordinates
|
14
|
+
# in three-dimensional space
|
15
|
+
Point3d = Struct.new(:x, :y, :z)
|
16
|
+
|
17
|
+
##
|
18
|
+
#Calculates the distance from given points in two-dimensional space
|
19
|
+
def distance_point_to_point2d(a, b)
|
20
|
+
Math.sqrt((b.x - a.x)**2 + (b.y - a.y)**2)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Class represents a line as equation y = k*x +b
|
24
|
+
# k - slope
|
25
|
+
# b - free_term
|
26
|
+
# in two-dimensional space
|
27
|
+
class Line2dCanon
|
28
|
+
attr_reader :slope
|
29
|
+
attr_reader :free_term
|
30
|
+
|
31
|
+
def initialize(p1, p2)
|
32
|
+
if (p1.x == p2.x) && (p1.y == p2.y)
|
33
|
+
raise ArgumentError, "You need 2 diffrent points"
|
34
|
+
end
|
35
|
+
if (p1.x == p2.x)
|
36
|
+
raise ArgumentError, "The straight line equation cannot be written in canonical form"
|
37
|
+
end
|
38
|
+
@slope = (p2.y - p1.y) / (p2.x - p1.x).to_f
|
39
|
+
@free_term = (p2.x * p1.y - p2.y * p1.x) / (p2.x - p1.x).to_f
|
40
|
+
end
|
41
|
+
|
42
|
+
##
|
43
|
+
# Checks the point lies on the line or not
|
44
|
+
def point_is_on_line?(p1)
|
45
|
+
p1.y == @slope * p1.x + @free_term
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
##
|
51
|
+
# Calculates the distance from given points in three-dimensional space
|
52
|
+
def distance_point_to_point3d(a, b)
|
53
|
+
Math.sqrt((b.x - a.x)**2 + (b.y - a.y)**2 + (b.z - a.z)**2)
|
54
|
+
end
|
55
|
+
|
56
|
+
##
|
57
|
+
# The distance from a point to a line on a plane
|
58
|
+
# The line is defined by two points
|
59
|
+
# https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line
|
60
|
+
def distance_point_line2d(p1, p2, a)
|
61
|
+
line_segment_length = distance_point_to_point2d(p1, p2)
|
62
|
+
((p2.y - p1.y) * a.x - (p2.x - p1.x) * a.y + p2.x * p1.y - p2.y * p1.x).abs / (line_segment_length * 1.0)
|
63
|
+
end
|
64
|
+
|
65
|
+
##
|
66
|
+
# The distance from a point to a line on a plane
|
67
|
+
# Line defined by an equation
|
68
|
+
# return 0 if the equation does not define a line.
|
69
|
+
def distance_point_line_equation2d(a, b, c, p)
|
70
|
+
if a == 0 and b == 0
|
71
|
+
return 0
|
72
|
+
end
|
73
|
+
(a * p.x + b * p.y + c).abs / Math.sqrt(a**2 + b**2)
|
74
|
+
end
|
75
|
+
|
76
|
+
def oriented_area(a, b, c)
|
77
|
+
a.x * (b.y - c.y) + b.x * (c.y - a.y) + c.x * (a.y - b.y)
|
78
|
+
end
|
79
|
+
|
80
|
+
##
|
81
|
+
# Determines if a clockwise crawl is performed
|
82
|
+
# for defined order of points
|
83
|
+
def clockwise(a, b, c)
|
84
|
+
oriented_area(a, b, c).negative?
|
85
|
+
end
|
86
|
+
|
87
|
+
##
|
88
|
+
# Determines if a counter-clockwise crawl is
|
89
|
+
# performed for defined order of points
|
90
|
+
def counter_clockwise(a, b, c)
|
91
|
+
oriented_area(a, b, c).positive?
|
92
|
+
end
|
93
|
+
|
94
|
+
def not_polygon?(points)
|
95
|
+
points.empty? || points.size == 1 || points.size == 2
|
96
|
+
end
|
97
|
+
|
98
|
+
def put_point_in_part(part, point, direction)
|
99
|
+
direction = method(direction)
|
100
|
+
while part.size >= 2 && !direction.call(part[part.size - 2], part[part.size - 1], point)
|
101
|
+
part.pop
|
102
|
+
end
|
103
|
+
part.push(point)
|
104
|
+
end
|
105
|
+
|
106
|
+
##
|
107
|
+
# Returns an array containing points that are included
|
108
|
+
# in the minimal convex hull for a given array of points
|
109
|
+
# https://e-maxx.ru/algo/convex_hull_graham
|
110
|
+
def minimal_convex_hull_2d(points)
|
111
|
+
return points if not_polygon?(points)
|
112
|
+
|
113
|
+
points.sort_by! { |p| [p.x, p.y] }
|
114
|
+
first = points[0]
|
115
|
+
last = points.last
|
116
|
+
up = [first]
|
117
|
+
down = [first]
|
118
|
+
|
119
|
+
(1...points.size).each do |i|
|
120
|
+
point = points[i]
|
121
|
+
is_last = i == points.size - 1
|
122
|
+
if is_last || clockwise(first, point, last)
|
123
|
+
put_point_in_part(up, point, :clockwise)
|
124
|
+
end
|
125
|
+
if is_last || counter_clockwise(first, point, last)
|
126
|
+
put_point_in_part(down, point, :counter_clockwise)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
hull = up
|
131
|
+
(1..(down.size - 2)).reverse_each do |j|
|
132
|
+
hull.push(down[j])
|
133
|
+
end
|
134
|
+
hull
|
135
|
+
end
|
136
|
+
|
137
|
+
def process_cf(line_equation, variable)
|
138
|
+
if line_equation.include?(variable)
|
139
|
+
before = line_equation.index('/') + 1
|
140
|
+
after = line_equation.index('=')
|
141
|
+
line_equation.slice(before..after).gsub('=', '').sub('*', '').gsub('(', '').gsub(')', '').to_f
|
142
|
+
else
|
143
|
+
0.0
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def cut_by_eq(line_equation)
|
148
|
+
line_equation.slice(line_equation.index('='), line_equation.length).sub('=', '')
|
149
|
+
end
|
150
|
+
|
151
|
+
def process_line_by_coordinates(line_equation, func)
|
152
|
+
copy_line = insert_eq(line_equation)
|
153
|
+
func = method(func)
|
154
|
+
res = []
|
155
|
+
res[0] = func.call(copy_line, 'x')
|
156
|
+
copy_line = cut_by_eq(copy_line)
|
157
|
+
res[1] = func.call(copy_line, 'y')
|
158
|
+
copy_line = cut_by_eq(copy_line)
|
159
|
+
res[2] = func.call(copy_line, 'z')
|
160
|
+
res
|
161
|
+
end
|
162
|
+
|
163
|
+
##
|
164
|
+
# Creates an array- directing vector in three-dimensional space .
|
165
|
+
# The equation is specified in the canonical form.
|
166
|
+
# Example, (x-0) / 26 = (y + 300) / * (- 15) = (z-200) / 51
|
167
|
+
#
|
168
|
+
# Important: mandatory order of variables: x, y, z
|
169
|
+
def directing_vector3d(line_equation)
|
170
|
+
process_line_by_coordinates(line_equation, :process_cf)
|
171
|
+
end
|
172
|
+
|
173
|
+
class VariablesOrderException < Exception
|
174
|
+
end
|
175
|
+
|
176
|
+
def needed_variables_order?(before, after)
|
177
|
+
before < after
|
178
|
+
end
|
179
|
+
|
180
|
+
def process_free_member(line_equation, variable)
|
181
|
+
if line_equation.include?(variable)
|
182
|
+
before = line_equation.index(variable) + 1
|
183
|
+
after = line_equation.index('/')
|
184
|
+
|
185
|
+
unless needed_variables_order?(before, after)
|
186
|
+
throw VariablesOrderException
|
187
|
+
end
|
188
|
+
|
189
|
+
line_equation.slice(before..after).gsub('/', '').to_f * (-1)
|
190
|
+
else
|
191
|
+
0.0
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
##
|
196
|
+
# Creates an array of coordinates of the point ([x, y, z] on the line
|
197
|
+
# given by the equation in the canonical form.
|
198
|
+
# Example, (x-0) / 26 = (y + 300) / * (- 15) = (z-200) / 51
|
199
|
+
#
|
200
|
+
# Important: mandatory order of variables: x, y, z
|
201
|
+
def height_point_3d(line_equation)
|
202
|
+
process_line_by_coordinates(line_equation, :process_free_member)
|
203
|
+
end
|
204
|
+
|
205
|
+
def vectors_product(v1, v2)
|
206
|
+
res = Array.new(3)
|
207
|
+
(0..2).each do |i|
|
208
|
+
res[i] = v1[(i + 1) % 3] * v2[(i + 2) % 3] - v1[(i + 2) % 3] * v2[(i + 1) % 3]
|
209
|
+
end
|
210
|
+
res
|
211
|
+
end
|
212
|
+
|
213
|
+
def vector_length(vector)
|
214
|
+
Math.sqrt(vector[0]**2 + vector[1]**2 + vector[2]**2)
|
215
|
+
end
|
216
|
+
|
217
|
+
##
|
218
|
+
# Calculates the distance from a point given by a Point3d structure
|
219
|
+
# to a straight line given by a canonical equation.
|
220
|
+
# Example, (x-0) / 26 = (y + 300) / * (- 15) = (z-200) / 51
|
221
|
+
#
|
222
|
+
# Important: mandatory order of variables: x, y, z
|
223
|
+
def point_to_line_distance_3d(point, line_eq)
|
224
|
+
dir_vector = directing_vector3d(line_eq)
|
225
|
+
line_point = height_point_3d(line_eq)
|
226
|
+
height_vector = [line_point[0] - point.x, line_point[1] - point.y, line_point[2] - point.z]
|
227
|
+
|
228
|
+
height_on_dir = vectors_product(height_vector, dir_vector)
|
229
|
+
vector_length(height_on_dir) / vector_length(dir_vector)
|
230
|
+
end
|
231
|
+
|
232
|
+
def insert_eq(line_equation)
|
233
|
+
line_equation.gsub(' ', '').insert(line_equation.length, '=')
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
data/lib/graph.rb
ADDED
@@ -0,0 +1,164 @@
|
|
1
|
+
#require 'set'
|
2
|
+
#require 'silicium'
|
3
|
+
|
4
|
+
module Silicium
|
5
|
+
module Graphs
|
6
|
+
Pair = Struct.new(:first, :second)
|
7
|
+
|
8
|
+
class GraphError < Error
|
9
|
+
|
10
|
+
end
|
11
|
+
|
12
|
+
class OrientedGraph
|
13
|
+
def initialize(initializer = [])
|
14
|
+
@vertices = {}
|
15
|
+
@edge_labels = {}
|
16
|
+
@vertex_labels = {}
|
17
|
+
initializer.each do |v|
|
18
|
+
add_vertex!(v[:v])
|
19
|
+
v[:i].each { |iv| add_edge_force!(v[:v], iv)}
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def add_vertex!(vertex_id)
|
24
|
+
if @vertices.has_key?(vertex_id)
|
25
|
+
return
|
26
|
+
end
|
27
|
+
@vertices[vertex_id] = [].to_set
|
28
|
+
end
|
29
|
+
|
30
|
+
def add_edge!(from, to)
|
31
|
+
if @vertices.has_key?(from) && @vertices.has_key?(to)
|
32
|
+
@vertices[from] << to
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# should only be used in constructor
|
37
|
+
def add_edge_force!(from, to)
|
38
|
+
add_vertex!(from)
|
39
|
+
add_vertex!(to)
|
40
|
+
add_edge!(from, to)
|
41
|
+
end
|
42
|
+
|
43
|
+
def adjacted_with(vertex)
|
44
|
+
unless @vertices.has_key?(vertex)
|
45
|
+
raise GraphError.new("Graph does not contain vertex #{vertex}")
|
46
|
+
end
|
47
|
+
|
48
|
+
@vertices[vertex].clone
|
49
|
+
end
|
50
|
+
|
51
|
+
def label_edge!(from, to, label)
|
52
|
+
unless @vertices.has_key?(from) && @vertices[from].include?(to)
|
53
|
+
raise GraphError.new("Graph does not contain edge (#{from}, #{to})")
|
54
|
+
end
|
55
|
+
|
56
|
+
@edge_labels[Pair.new(from, to)] = label
|
57
|
+
end
|
58
|
+
|
59
|
+
def label_vertex!(vertex, label)
|
60
|
+
unless @vertices.has_key?(vertex)
|
61
|
+
raise GraphError.new("Graph does not contain vertex #{vertex}")
|
62
|
+
end
|
63
|
+
|
64
|
+
@vertex_labels[vertex] = label
|
65
|
+
end
|
66
|
+
|
67
|
+
def get_edge_label(from, to)
|
68
|
+
if !@vertices.has_key?(from) || ! @vertices[from].include?(to)
|
69
|
+
raise GraphError.new("Graph does not contain edge (#{from}, #{to})")
|
70
|
+
end
|
71
|
+
|
72
|
+
@edge_labels[Pair.new(from, to)]
|
73
|
+
end
|
74
|
+
|
75
|
+
def get_vertex_label(vertex)
|
76
|
+
unless @vertices.has_key?(vertex)
|
77
|
+
raise GraphError.new("Graph does not contain vertex #{vertex}")
|
78
|
+
end
|
79
|
+
|
80
|
+
@vertex_labels[vertex]
|
81
|
+
end
|
82
|
+
|
83
|
+
def vertex_number
|
84
|
+
@vertices.count
|
85
|
+
end
|
86
|
+
|
87
|
+
def edge_number
|
88
|
+
res = 0
|
89
|
+
@vertices.values.each do |item|
|
90
|
+
res += item.count
|
91
|
+
end
|
92
|
+
res
|
93
|
+
end
|
94
|
+
|
95
|
+
def vertex_label_number
|
96
|
+
@vertex_labels.count
|
97
|
+
end
|
98
|
+
|
99
|
+
def edge_label_number
|
100
|
+
@edge_labels.count
|
101
|
+
end
|
102
|
+
|
103
|
+
def has_vertex?(vertex)
|
104
|
+
@vertices.has_key?(vertex)
|
105
|
+
end
|
106
|
+
|
107
|
+
def has_edge?(from, to)
|
108
|
+
@vertices.has_key?(from) && @vertices[from].include?(to)
|
109
|
+
end
|
110
|
+
|
111
|
+
def delete_vertex!(vertex)
|
112
|
+
if has_vertex?(vertex)
|
113
|
+
@vertices.keys.each do |key|
|
114
|
+
delete_edge!(key, vertex)
|
115
|
+
end
|
116
|
+
@vertices.delete(vertex)
|
117
|
+
@vertex_labels.delete(vertex)
|
118
|
+
|
119
|
+
@vertices.keys.each do |key|
|
120
|
+
@edge_labels.delete(Pair.new(vertex, key))
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def delete_edge!(from, to)
|
126
|
+
if has_edge?(from, to)
|
127
|
+
@vertices[from].delete(to)
|
128
|
+
@edge_labels.delete(Pair.new(from, to))
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
|
134
|
+
class UnorientedGraph < OrientedGraph
|
135
|
+
def add_edge!(from, to)
|
136
|
+
super(from, to)
|
137
|
+
super(to, from)
|
138
|
+
end
|
139
|
+
|
140
|
+
def label_edge!(from, to, label)
|
141
|
+
super(from, to, label)
|
142
|
+
super(to, from, label)
|
143
|
+
end
|
144
|
+
|
145
|
+
def delete_edge!(from, to)
|
146
|
+
super(from, to)
|
147
|
+
super(to, from)
|
148
|
+
end
|
149
|
+
|
150
|
+
def edge_number
|
151
|
+
res = 0
|
152
|
+
@vertices.each do |from, tos|
|
153
|
+
tos.each {|to| res += (to == from ? 2 : 1)}
|
154
|
+
end
|
155
|
+
res / 2
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def dijkstra_algorythm(graph, starting_vertex)
|
160
|
+
#
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|
@@ -0,0 +1,147 @@
|
|
1
|
+
module Silicium
|
2
|
+
class IntegralDoesntExistError < RuntimeError
|
3
|
+
|
4
|
+
end
|
5
|
+
##
|
6
|
+
# A class providing numerical integration methods
|
7
|
+
class NumericalIntegration
|
8
|
+
|
9
|
+
# Computes integral from +a+ to +b+ of +block+ with accuracy +eps+
|
10
|
+
def self.three_eights_integration(a, b, eps = 0.0001, &block)
|
11
|
+
wrapper_method([a, b], eps, :three_eights_integration_n, &block)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Computes integral from +a+ to +b+ of +block+ with +n+ segmentations
|
15
|
+
def self.three_eights_integration_n(a, b, n, &block)
|
16
|
+
dx = (b - a) / n.to_f
|
17
|
+
result = 0
|
18
|
+
x = a
|
19
|
+
n.times do
|
20
|
+
result +=
|
21
|
+
(block.call(x) + 3 * block.call((2 * x + x + dx) / 3.0) +
|
22
|
+
3 * block.call((x + 2 * (x + dx)) / 3.0) + block.call(x + dx)) / 8.0 * dx
|
23
|
+
x += dx
|
24
|
+
end
|
25
|
+
result
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
# Simpson integration with a segment
|
30
|
+
def self.simpson_integration_with_a_segment(a, b, n, &block)
|
31
|
+
dx = (b - a) / n.to_f
|
32
|
+
result = 0
|
33
|
+
i = 0
|
34
|
+
while i < n
|
35
|
+
result += (block.call(a + i * dx) + 4 * block.call(((a + i * dx) +
|
36
|
+
(a + (i + 1) * dx)) / 2.0) + block.call(a + (i + 1) * dx)) / 6.0 * dx
|
37
|
+
i += 1
|
38
|
+
end
|
39
|
+
result
|
40
|
+
end
|
41
|
+
|
42
|
+
# Simpson integration with specified accuracy
|
43
|
+
def self.simpson_integration(a, b, eps = 0.0001, &block)
|
44
|
+
wrapper_method([a, b], eps, :simpson_integration_with_a_segment, &block)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Left Rectangle Method and Right Rectangle Method
|
48
|
+
def self.left_rect_integration(left_p, right_p, eps = 0.0001, &block)
|
49
|
+
splits = 1
|
50
|
+
res1 = left_rect_integration_n(left_p, right_p, 1, &block)
|
51
|
+
res2 = left_rect_integration_n(left_p, right_p, 5, &block)
|
52
|
+
while (res1 - res2).abs > eps
|
53
|
+
res1 = left_rect_integration_n(left_p, right_p, splits, &block)
|
54
|
+
splits *= 5
|
55
|
+
res2 = left_rect_integration_n(left_p, right_p, splits, &block)
|
56
|
+
end
|
57
|
+
(res1 + res2) / 2.0
|
58
|
+
end
|
59
|
+
|
60
|
+
# Left Rectangle Auxiliary Method and Right Rectangle Auxiliary Method
|
61
|
+
def self.left_rect_integration_n(left_p, right_p, splits, &block)
|
62
|
+
dx = (right_p - left_p) / splits.to_f
|
63
|
+
result = 0
|
64
|
+
i = 0
|
65
|
+
while i < splits
|
66
|
+
result += block.call(left_p + i * dx)
|
67
|
+
i += 1
|
68
|
+
end
|
69
|
+
result * dx
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
# Middle Rectangles Method with a segment
|
74
|
+
def self.middle_rectangles_with_a_segment(a, b, n, &block)
|
75
|
+
dx = (b - a) / n.to_f
|
76
|
+
result = 0
|
77
|
+
i = 0
|
78
|
+
n.times do
|
79
|
+
result += block.call(a + dx * (i + 1 / 2)) * dx
|
80
|
+
i += 1
|
81
|
+
end
|
82
|
+
result
|
83
|
+
end
|
84
|
+
|
85
|
+
# Middle Rectangles Method with specified accuracy
|
86
|
+
def self.middle_rectangles(a, b, eps = 0.0001, &block)
|
87
|
+
wrapper_method([a, b], eps, :middle_rectangles_with_a_segment, &block)
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
# Trapezoid Method with a segment
|
92
|
+
def self.trapezoid_with_a_segment(a, b, n, &block)
|
93
|
+
dx = (b - a) / n.to_f
|
94
|
+
result = 0
|
95
|
+
i = 1
|
96
|
+
(n - 1).times do
|
97
|
+
result += block.call(a + dx * i)
|
98
|
+
i += 1
|
99
|
+
end
|
100
|
+
result += (block.call(a) + block.call(b)) / 2.0
|
101
|
+
result * dx
|
102
|
+
end
|
103
|
+
|
104
|
+
# Trapezoid Method with specified accuracy
|
105
|
+
def self.trapezoid(a, b, eps = 0.0001, &block)
|
106
|
+
wrapper_method([a, b], eps, :trapezoid_with_a_segment ,&block)
|
107
|
+
end
|
108
|
+
|
109
|
+
private
|
110
|
+
|
111
|
+
##
|
112
|
+
# Wrapper method for num_integratons methods
|
113
|
+
# @param [Array] a_b integration range
|
114
|
+
# @param [Numeric] eps
|
115
|
+
# @param [Proc] proc - integration Proc
|
116
|
+
# @param [Block] block - integrated function as Block
|
117
|
+
def self.wrapper_method(a_b, eps, method_name, &block)
|
118
|
+
n = 1
|
119
|
+
a, b = a_b
|
120
|
+
begin
|
121
|
+
begin
|
122
|
+
result = send(method_name, a, b, n, &block)
|
123
|
+
check_value(result)
|
124
|
+
n *= 5
|
125
|
+
result1 = send(method_name, a, b, n, &block)
|
126
|
+
check_value(result1)
|
127
|
+
end until (result - result1).abs < eps
|
128
|
+
rescue Math::DomainError
|
129
|
+
raise IntegralDoesntExistError, 'Domain error in math function'
|
130
|
+
rescue ZeroDivisionError
|
131
|
+
raise IntegralDoesntExistError, 'Divide by zero'
|
132
|
+
end
|
133
|
+
(result + result1) / 2.0
|
134
|
+
end
|
135
|
+
|
136
|
+
def self.check_value(value)
|
137
|
+
if value.nan?
|
138
|
+
raise IntegralDoesntExistError, 'We have not-a-number result :('
|
139
|
+
end
|
140
|
+
if value == Float::INFINITY
|
141
|
+
raise IntegralDoesntExistError, 'We have infinity :('
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
|