silicium 0.0.15 → 0.0.20

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +4 -0
  3. data/.gitignore +3 -1
  4. data/.travis.yml +12 -1
  5. data/Gemfile +4 -0
  6. data/Makefile +269 -0
  7. data/README.md +2 -0
  8. data/Rakefile +7 -0
  9. data/docs/Object.html +117 -0
  10. data/docs/README_md.html +142 -0
  11. data/docs/Silicium.html +101 -0
  12. data/docs/Silicium/Combinatorics.html +270 -0
  13. data/docs/Silicium/Dice.html +99 -0
  14. data/docs/Silicium/Dice/Polyhedron.html +315 -0
  15. data/docs/Silicium/Dice/PolyhedronSet.html +321 -0
  16. data/docs/Silicium/Error.html +106 -0
  17. data/docs/Silicium/Geometry.html +940 -0
  18. data/docs/Silicium/Geometry/Line2dCanon.html +243 -0
  19. data/docs/Silicium/Geometry/VariablesOrderException.html +106 -0
  20. data/docs/Silicium/Graphs.html +164 -0
  21. data/docs/Silicium/Graphs/GraphError.html +106 -0
  22. data/docs/Silicium/Graphs/OrientedGraph.html +775 -0
  23. data/docs/Silicium/Graphs/UnorientedGraph.html +284 -0
  24. data/docs/Silicium/IntegralDoesntExistError.html +106 -0
  25. data/docs/Silicium/NumericalIntegration.html +521 -0
  26. data/docs/Silicium/Optimization.html +639 -0
  27. data/docs/Silicium/Plotter.html +186 -0
  28. data/docs/Silicium/Plotter/Image.html +297 -0
  29. data/docs/created.rid +9 -0
  30. data/docs/css/fonts.css +167 -0
  31. data/docs/css/rdoc.css +619 -0
  32. data/docs/fonts/Lato-Light.ttf +0 -0
  33. data/docs/fonts/Lato-LightItalic.ttf +0 -0
  34. data/docs/fonts/Lato-Regular.ttf +0 -0
  35. data/docs/fonts/Lato-RegularItalic.ttf +0 -0
  36. data/docs/fonts/SourceCodePro-Bold.ttf +0 -0
  37. data/docs/fonts/SourceCodePro-Regular.ttf +0 -0
  38. data/docs/images/add.png +0 -0
  39. data/docs/images/arrow_up.png +0 -0
  40. data/docs/images/brick.png +0 -0
  41. data/docs/images/brick_link.png +0 -0
  42. data/docs/images/bug.png +0 -0
  43. data/docs/images/bullet_black.png +0 -0
  44. data/docs/images/bullet_toggle_minus.png +0 -0
  45. data/docs/images/bullet_toggle_plus.png +0 -0
  46. data/docs/images/date.png +0 -0
  47. data/docs/images/delete.png +0 -0
  48. data/docs/images/find.png +0 -0
  49. data/docs/images/loadingAnimation.gif +0 -0
  50. data/docs/images/macFFBgHack.png +0 -0
  51. data/docs/images/package.png +0 -0
  52. data/docs/images/page_green.png +0 -0
  53. data/docs/images/page_white_text.png +0 -0
  54. data/docs/images/page_white_width.png +0 -0
  55. data/docs/images/plugin.png +0 -0
  56. data/docs/images/ruby.png +0 -0
  57. data/docs/images/tag_blue.png +0 -0
  58. data/docs/images/tag_green.png +0 -0
  59. data/docs/images/transparent.png +0 -0
  60. data/docs/images/wrench.png +0 -0
  61. data/docs/images/wrench_orange.png +0 -0
  62. data/docs/images/zoom.png +0 -0
  63. data/docs/index.html +132 -0
  64. data/docs/js/darkfish.js +84 -0
  65. data/docs/js/navigation.js +105 -0
  66. data/docs/js/navigation.js.gz +0 -0
  67. data/docs/js/search.js +110 -0
  68. data/docs/js/search_index.js +1 -0
  69. data/docs/js/search_index.js.gz +0 -0
  70. data/docs/js/searcher.js +229 -0
  71. data/docs/js/searcher.js.gz +0 -0
  72. data/docs/table_of_contents.html +608 -0
  73. data/lib/geometry.rb +236 -0
  74. data/lib/graph.rb +164 -0
  75. data/lib/numerical_integration.rb +147 -0
  76. data/lib/optimization.rb +144 -0
  77. data/lib/plotter.rb +96 -0
  78. data/lib/silicium.rb +0 -1
  79. data/lib/silicium/version.rb +1 -1
  80. data/lib/theory_of_probability.rb +227 -0
  81. data/silicium.gemspec +4 -2
  82. metadata +90 -3
@@ -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
@@ -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
+