symbolic 0.0.3 → 0.0.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/README.rdoc +4 -2
- data/Rakefile +1 -1
- data/lib/extensions/kernel.rb +11 -0
- data/lib/symbolic.rb +8 -208
- data/lib/symbolic/expression.rb +50 -0
- data/lib/symbolic/method.rb +19 -0
- data/lib/symbolic/operatable.rb +19 -0
- data/lib/symbolic/optimizations.rb +57 -0
- data/lib/symbolic/unary_minus.rb +33 -0
- data/lib/symbolic/variable.rb +24 -0
- metadata +8 -1
data/README.rdoc
CHANGED
@@ -1,12 +1,14 @@
|
|
1
1
|
Symbolic math for ruby.
|
2
2
|
|
3
|
+
Installation: gem install symbolic
|
4
|
+
|
3
5
|
Symbolic math can be really helpful if you want to simplify some giant equation or if you don't want to get a performance hit re-evaluating big math expressions every time when few variables change.
|
4
6
|
|
5
7
|
Currently I've implemented only naive optimizations to simplify math expressions, but it's pretty simple to add your own - see Symbolic::Optimizations module for examples.
|
6
8
|
|
7
|
-
Available as gem from gemcutter: gem install symbolic
|
8
|
-
|
9
9
|
Simple example:
|
10
|
+
require 'rubygems'
|
11
|
+
require 'symbolic'
|
10
12
|
|
11
13
|
Symbolic.enabled = true
|
12
14
|
|
data/Rakefile
CHANGED
@@ -5,7 +5,7 @@ begin
|
|
5
5
|
require 'jeweler'
|
6
6
|
Jeweler::Tasks.new do |gem|
|
7
7
|
gem.name = "symbolic"
|
8
|
-
gem.version = '0.0.
|
8
|
+
gem.version = '0.0.4'
|
9
9
|
gem.summary = 'Symbolic math for ruby'
|
10
10
|
gem.description = %Q{Symbolic math can be really helpful if you want to simplify some giant equation or if you don't want to get a performance hit re-evaluating big math expressions every time when few variables change.}
|
11
11
|
gem.email = "ravwar@gmail.com"
|
data/lib/symbolic.rb
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
require 'symbolic/operatable'
|
2
|
+
require 'symbolic/optimizations'
|
3
|
+
require 'symbolic/variable'
|
4
|
+
require 'symbolic/expression'
|
5
|
+
require 'symbolic/method'
|
6
|
+
require 'symbolic/unary_minus'
|
7
|
+
require 'extensions/kernel'
|
8
|
+
|
1
9
|
module Symbolic
|
2
10
|
@enabled = false
|
3
11
|
class << self
|
@@ -100,212 +108,4 @@ module Symbolic
|
|
100
108
|
end
|
101
109
|
end # disable
|
102
110
|
end # class << self
|
103
|
-
|
104
|
-
class Operatable
|
105
|
-
def -@
|
106
|
-
UnaryMinus.create self
|
107
|
-
end
|
108
|
-
|
109
|
-
def *(value)
|
110
|
-
Optimizations.multiply self, value
|
111
|
-
end
|
112
|
-
|
113
|
-
def +(value)
|
114
|
-
Optimizations.plus self, value
|
115
|
-
end
|
116
|
-
|
117
|
-
def -(value)
|
118
|
-
Optimizations.minus self, value
|
119
|
-
end
|
120
|
-
end # Operations
|
121
|
-
|
122
|
-
module Optimizations
|
123
|
-
def self.plus(symbolic_var, var, reverse=false)
|
124
|
-
if var == 0
|
125
|
-
symbolic_var
|
126
|
-
elsif var.is_a? UnaryMinus
|
127
|
-
symbolic_var - var.variable
|
128
|
-
elsif reverse && symbolic_var.is_a?(UnaryMinus)
|
129
|
-
var - symbolic_var.variable
|
130
|
-
else
|
131
|
-
if reverse
|
132
|
-
Expression.new var, symbolic_var, '+'
|
133
|
-
else
|
134
|
-
Expression.new symbolic_var, var, '+'
|
135
|
-
end
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
def self.minus(symbolic_var, var, reverse=false)
|
140
|
-
if var == 0
|
141
|
-
symbolic_var
|
142
|
-
elsif var.is_a? UnaryMinus
|
143
|
-
symbolic_var + var.variable
|
144
|
-
elsif reverse && symbolic_var.is_a?(UnaryMinus)
|
145
|
-
var + symbolic_var.variable
|
146
|
-
else
|
147
|
-
if reverse
|
148
|
-
Expression.new var, symbolic_var, '-'
|
149
|
-
else
|
150
|
-
Expression.new symbolic_var, var, '-'
|
151
|
-
end
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
|
-
def self.multiply(symbolic_var, var, reverse=false)
|
156
|
-
if var == 0
|
157
|
-
var
|
158
|
-
elsif var == 1
|
159
|
-
symbolic_var
|
160
|
-
elsif var == -1
|
161
|
-
-symbolic_var
|
162
|
-
elsif var.is_a?(Numeric) && var < 0
|
163
|
-
-(-var*symbolic_var)
|
164
|
-
elsif var.is_a? UnaryMinus
|
165
|
-
UnaryMinus.create symbolic_var*var.variable
|
166
|
-
elsif symbolic_var.is_a? UnaryMinus
|
167
|
-
UnaryMinus.create symbolic_var.variable*var
|
168
|
-
else
|
169
|
-
if reverse
|
170
|
-
Expression.new var, symbolic_var, '*'
|
171
|
-
else
|
172
|
-
Expression.new symbolic_var, var, '*'
|
173
|
-
end
|
174
|
-
end
|
175
|
-
end
|
176
|
-
end
|
177
|
-
|
178
|
-
class Variable < Operatable
|
179
|
-
attr_accessor :value, :name
|
180
|
-
|
181
|
-
@@index = 0
|
182
|
-
|
183
|
-
def initialize(options)
|
184
|
-
unless @name = options[:name]
|
185
|
-
@@index += 1
|
186
|
-
@name = "var#{@@index}"
|
187
|
-
end
|
188
|
-
|
189
|
-
@value = options[:value]
|
190
|
-
end
|
191
|
-
|
192
|
-
def to_s
|
193
|
-
@name
|
194
|
-
end
|
195
|
-
|
196
|
-
def undefined_variables
|
197
|
-
@value ? [] : [self]
|
198
|
-
end
|
199
|
-
end
|
200
|
-
|
201
|
-
class Expression < Operatable
|
202
|
-
def initialize(var1, var2, operation)
|
203
|
-
var1, var2 = var2, var1 if operation == '*' && var2.is_a?(Numeric)
|
204
|
-
@var1, @var2, @operation = var1, var2, operation
|
205
|
-
end
|
206
|
-
|
207
|
-
def to_s
|
208
|
-
var1 = "#{@var1}"
|
209
|
-
var2 = "#{@var2}"
|
210
|
-
if @operation == '*'
|
211
|
-
var1 = "(#{@var1})" if @var1.is_a?(Expression) && (@var1.plus? || @var1.minus?)
|
212
|
-
var2 = "(#{@var2})" if @var2.is_a?(Expression) && (@var2.plus? || @var1.minus?)
|
213
|
-
end
|
214
|
-
"#{var1}#{@operation}#{var2}"
|
215
|
-
end
|
216
|
-
|
217
|
-
def plus?
|
218
|
-
@operation == '+'
|
219
|
-
end
|
220
|
-
|
221
|
-
def minus?
|
222
|
-
@operation == '-'
|
223
|
-
end
|
224
|
-
|
225
|
-
def multiply?
|
226
|
-
@operation == '*'
|
227
|
-
end
|
228
|
-
|
229
|
-
def value
|
230
|
-
if undefined_variables.empty?
|
231
|
-
value_of(@var1).send @operation, value_of(@var2)
|
232
|
-
end
|
233
|
-
end
|
234
|
-
|
235
|
-
def undefined_variables
|
236
|
-
(undefined_variables_of(@var1) + undefined_variables_of(@var2)).uniq
|
237
|
-
end
|
238
|
-
|
239
|
-
private
|
240
|
-
|
241
|
-
def undefined_variables_of(variable)
|
242
|
-
variable.is_a?(Operatable) ? variable.undefined_variables : []
|
243
|
-
end
|
244
|
-
|
245
|
-
def value_of(variable)
|
246
|
-
variable.is_a?(Operatable) ? variable.value : variable
|
247
|
-
end
|
248
|
-
end
|
249
|
-
|
250
|
-
class Method < Operatable
|
251
|
-
def initialize(variable, operation)
|
252
|
-
@variable, @operation = variable, operation
|
253
|
-
end
|
254
|
-
|
255
|
-
def to_s
|
256
|
-
"#{@operation}(#{@variable})"
|
257
|
-
end
|
258
|
-
|
259
|
-
def value
|
260
|
-
Math.send @operation, @variable.value if undefined_variables.empty?
|
261
|
-
end
|
262
|
-
|
263
|
-
def undefined_variables
|
264
|
-
@variable.undefined_variables
|
265
|
-
end
|
266
|
-
end
|
267
|
-
|
268
|
-
class UnaryMinus < Operatable
|
269
|
-
attr_reader :variable
|
270
|
-
|
271
|
-
def self.create(expression)
|
272
|
-
if expression.is_a? UnaryMinus
|
273
|
-
expression.variable
|
274
|
-
else
|
275
|
-
new expression
|
276
|
-
end
|
277
|
-
end
|
278
|
-
|
279
|
-
def initialize(variable)
|
280
|
-
@variable = variable
|
281
|
-
end
|
282
|
-
|
283
|
-
def to_s
|
284
|
-
if @variable.is_a? Variable
|
285
|
-
"-#{@variable}"
|
286
|
-
else
|
287
|
-
"(-#{@variable})"
|
288
|
-
end
|
289
|
-
end
|
290
|
-
|
291
|
-
def value
|
292
|
-
-@variable.value if undefined_variables.empty?
|
293
|
-
end
|
294
|
-
|
295
|
-
def undefined_variables
|
296
|
-
@variable.undefined_variables
|
297
|
-
end
|
298
|
-
end
|
299
|
-
end
|
300
|
-
|
301
|
-
module Kernel
|
302
|
-
def var(options={})
|
303
|
-
Symbolic::Variable.new options
|
304
|
-
end
|
305
|
-
|
306
|
-
def symbolic
|
307
|
-
Symbolic.enabled = true
|
308
|
-
yield
|
309
|
-
Symbolic.enabled = false
|
310
|
-
end
|
311
111
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Symbolic
|
2
|
+
class Expression < Operatable
|
3
|
+
def initialize(var1, var2, operation)
|
4
|
+
var1, var2 = var2, var1 if operation == '*' && var2.is_a?(Numeric)
|
5
|
+
@var1, @var2, @operation = var1, var2, operation
|
6
|
+
end
|
7
|
+
|
8
|
+
def to_s
|
9
|
+
var1 = "#{@var1}"
|
10
|
+
var2 = "#{@var2}"
|
11
|
+
if @operation == '*'
|
12
|
+
var1 = "(#{@var1})" if @var1.is_a?(Expression) && (@var1.plus? || @var1.minus?)
|
13
|
+
var2 = "(#{@var2})" if @var2.is_a?(Expression) && (@var2.plus? || @var1.minus?)
|
14
|
+
end
|
15
|
+
"#{var1}#{@operation}#{var2}"
|
16
|
+
end
|
17
|
+
|
18
|
+
def plus?
|
19
|
+
@operation == '+'
|
20
|
+
end
|
21
|
+
|
22
|
+
def minus?
|
23
|
+
@operation == '-'
|
24
|
+
end
|
25
|
+
|
26
|
+
def multiply?
|
27
|
+
@operation == '*'
|
28
|
+
end
|
29
|
+
|
30
|
+
def value
|
31
|
+
if undefined_variables.empty?
|
32
|
+
value_of(@var1).send @operation, value_of(@var2)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def undefined_variables
|
37
|
+
(undefined_variables_of(@var1) + undefined_variables_of(@var2)).uniq
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def undefined_variables_of(variable)
|
43
|
+
variable.is_a?(Operatable) ? variable.undefined_variables : []
|
44
|
+
end
|
45
|
+
|
46
|
+
def value_of(variable)
|
47
|
+
variable.is_a?(Operatable) ? variable.value : variable
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Symbolic
|
2
|
+
class Method < Operatable
|
3
|
+
def initialize(variable, operation)
|
4
|
+
@variable, @operation = variable, operation
|
5
|
+
end
|
6
|
+
|
7
|
+
def to_s
|
8
|
+
"#{@operation}(#{@variable})"
|
9
|
+
end
|
10
|
+
|
11
|
+
def value
|
12
|
+
Math.send @operation, @variable.value if undefined_variables.empty?
|
13
|
+
end
|
14
|
+
|
15
|
+
def undefined_variables
|
16
|
+
@variable.undefined_variables
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Symbolic
|
2
|
+
class Operatable
|
3
|
+
def -@
|
4
|
+
UnaryMinus.create self
|
5
|
+
end
|
6
|
+
|
7
|
+
def *(value)
|
8
|
+
Optimizations.multiply self, value
|
9
|
+
end
|
10
|
+
|
11
|
+
def +(value)
|
12
|
+
Optimizations.plus self, value
|
13
|
+
end
|
14
|
+
|
15
|
+
def -(value)
|
16
|
+
Optimizations.minus self, value
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Symbolic
|
2
|
+
module Optimizations
|
3
|
+
def self.plus(symbolic_var, var, reverse=false)
|
4
|
+
if var == 0
|
5
|
+
symbolic_var
|
6
|
+
elsif var.is_a? UnaryMinus
|
7
|
+
symbolic_var - var.variable
|
8
|
+
elsif reverse && symbolic_var.is_a?(UnaryMinus)
|
9
|
+
var - symbolic_var.variable
|
10
|
+
else
|
11
|
+
if reverse
|
12
|
+
Expression.new var, symbolic_var, '+'
|
13
|
+
else
|
14
|
+
Expression.new symbolic_var, var, '+'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.minus(symbolic_var, var, reverse=false)
|
20
|
+
if var == 0
|
21
|
+
symbolic_var
|
22
|
+
elsif var.is_a? UnaryMinus
|
23
|
+
symbolic_var + var.variable
|
24
|
+
elsif reverse && symbolic_var.is_a?(UnaryMinus)
|
25
|
+
var + symbolic_var.variable
|
26
|
+
else
|
27
|
+
if reverse
|
28
|
+
Expression.new var, symbolic_var, '-'
|
29
|
+
else
|
30
|
+
Expression.new symbolic_var, var, '-'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.multiply(symbolic_var, var, reverse=false)
|
36
|
+
if var == 0
|
37
|
+
var
|
38
|
+
elsif var == 1
|
39
|
+
symbolic_var
|
40
|
+
elsif var == -1
|
41
|
+
-symbolic_var
|
42
|
+
elsif var.is_a?(Numeric) && var < 0
|
43
|
+
-(-var*symbolic_var)
|
44
|
+
elsif var.is_a? UnaryMinus
|
45
|
+
UnaryMinus.create symbolic_var*var.variable
|
46
|
+
elsif symbolic_var.is_a? UnaryMinus
|
47
|
+
UnaryMinus.create symbolic_var.variable*var
|
48
|
+
else
|
49
|
+
if reverse
|
50
|
+
Expression.new var, symbolic_var, '*'
|
51
|
+
else
|
52
|
+
Expression.new symbolic_var, var, '*'
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Symbolic
|
2
|
+
class UnaryMinus < Operatable
|
3
|
+
attr_reader :variable
|
4
|
+
|
5
|
+
def self.create(expression)
|
6
|
+
if expression.is_a? UnaryMinus
|
7
|
+
expression.variable
|
8
|
+
else
|
9
|
+
new expression
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(variable)
|
14
|
+
@variable = variable
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_s
|
18
|
+
if @variable.is_a? Variable
|
19
|
+
"-#{@variable}"
|
20
|
+
else
|
21
|
+
"(-#{@variable})"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def value
|
26
|
+
-@variable.value if undefined_variables.empty?
|
27
|
+
end
|
28
|
+
|
29
|
+
def undefined_variables
|
30
|
+
@variable.undefined_variables
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Symbolic
|
2
|
+
class Variable < Operatable
|
3
|
+
attr_accessor :value, :name
|
4
|
+
|
5
|
+
@@index = 0
|
6
|
+
|
7
|
+
def initialize(options)
|
8
|
+
unless @name = options[:name]
|
9
|
+
@@index += 1
|
10
|
+
@name = "var#{@@index}"
|
11
|
+
end
|
12
|
+
|
13
|
+
@value = options[:value]
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_s
|
17
|
+
@name
|
18
|
+
end
|
19
|
+
|
20
|
+
def undefined_variables
|
21
|
+
@value ? [] : [self]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: symbolic
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- brainopia
|
@@ -34,7 +34,14 @@ files:
|
|
34
34
|
- .gitignore
|
35
35
|
- README.rdoc
|
36
36
|
- Rakefile
|
37
|
+
- lib/extensions/kernel.rb
|
37
38
|
- lib/symbolic.rb
|
39
|
+
- lib/symbolic/expression.rb
|
40
|
+
- lib/symbolic/method.rb
|
41
|
+
- lib/symbolic/operatable.rb
|
42
|
+
- lib/symbolic/optimizations.rb
|
43
|
+
- lib/symbolic/unary_minus.rb
|
44
|
+
- lib/symbolic/variable.rb
|
38
45
|
- spec/spec.opts
|
39
46
|
- spec/spec_helper.rb
|
40
47
|
- spec/symbolic_spec.rb
|