symbolic 0.0.1
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/.gitignore +21 -0
- data/README +1 -0
- data/Rakefile +35 -0
- data/lib/symbolic.rb +310 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +9 -0
- data/spec/symbolic_spec.rb +7 -0
- metadata +71 -0
data/.gitignore
ADDED
data/README
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Symbolic math for ruby.
|
data/Rakefile
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "symbolic"
|
8
|
+
gem.version = '0.0.1'
|
9
|
+
gem.summary = %Q{Symbolic math for ruby}
|
10
|
+
gem.email = "ravwar@gmail.com"
|
11
|
+
gem.homepage = "http://github.com/brainopia/symbolic"
|
12
|
+
gem.authors = ["brainopia"]
|
13
|
+
gem.add_development_dependency "rspec", ">= 1.2.9"
|
14
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
15
|
+
end
|
16
|
+
Jeweler::GemcutterTasks.new
|
17
|
+
rescue LoadError
|
18
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'spec/rake/spectask'
|
22
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
23
|
+
spec.libs << 'lib' << 'spec'
|
24
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
25
|
+
end
|
26
|
+
|
27
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
28
|
+
spec.libs << 'lib' << 'spec'
|
29
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
30
|
+
spec.rcov = true
|
31
|
+
end
|
32
|
+
|
33
|
+
task :spec => :check_dependencies
|
34
|
+
|
35
|
+
task :default => :spec
|
data/lib/symbolic.rb
ADDED
@@ -0,0 +1,310 @@
|
|
1
|
+
module Symbolic
|
2
|
+
@enabled = false
|
3
|
+
class << self
|
4
|
+
def enabled?
|
5
|
+
@enabled
|
6
|
+
end
|
7
|
+
|
8
|
+
def enabled=(enable_flag)
|
9
|
+
if enable_flag && !@enabled
|
10
|
+
enable
|
11
|
+
elsif !enable_flag && @enabled
|
12
|
+
disable
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def aliases
|
17
|
+
{ :* => :non_symbolic_multiplication,
|
18
|
+
:+ => :non_symbolic_addition,
|
19
|
+
:- => :non_symbolic_substraction }
|
20
|
+
end
|
21
|
+
|
22
|
+
def math_operations
|
23
|
+
[:cos, :sin]
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def numerical_context(&proc)
|
29
|
+
[Fixnum, Bignum, Float].each do |klass|
|
30
|
+
klass.class_eval &proc
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def enable
|
35
|
+
@enabled = true
|
36
|
+
numerical_context do
|
37
|
+
Symbolic.aliases.each do |standard_operation, non_symbolic_operation|
|
38
|
+
alias_method non_symbolic_operation, standard_operation
|
39
|
+
end
|
40
|
+
|
41
|
+
def *(value)
|
42
|
+
if value.is_a?(Operatable)
|
43
|
+
Optimizations.multiply value, self, :reverse
|
44
|
+
else
|
45
|
+
non_symbolic_multiplication(value)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def +(value)
|
50
|
+
if value.is_a? Operatable
|
51
|
+
Optimizations.plus value, self, :reverse
|
52
|
+
else
|
53
|
+
non_symbolic_addition value
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def -(value)
|
58
|
+
if value.is_a? Operatable
|
59
|
+
Optimizations.minus value, self, :reverse
|
60
|
+
else
|
61
|
+
non_symbolic_substraction value
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end # numerical_context
|
65
|
+
|
66
|
+
math_operations.each do |operation|
|
67
|
+
Math.module_eval <<-CODE
|
68
|
+
class << self
|
69
|
+
alias non_symbolic_#{operation} #{operation}
|
70
|
+
|
71
|
+
def #{operation}(value)
|
72
|
+
if value.is_a? Operatable
|
73
|
+
Symbolic::Method.new value, :#{operation}
|
74
|
+
else
|
75
|
+
non_symbolic_#{operation} value
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
CODE
|
80
|
+
end
|
81
|
+
end # enable
|
82
|
+
|
83
|
+
def disable
|
84
|
+
@enabled = false
|
85
|
+
numerical_context do
|
86
|
+
Symbolic.aliases.each do |standard_operation, non_symbolic_operation|
|
87
|
+
alias_method standard_operation, non_symbolic_operation
|
88
|
+
remove_method non_symbolic_operation
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
Math.module_eval do
|
93
|
+
class << self
|
94
|
+
Symbolic.math_operations.each do |operation|
|
95
|
+
non_symbolic_operation = "non_symbolic_#{operation}"
|
96
|
+
alias_method operation, non_symbolic_operation
|
97
|
+
remove_method non_symbolic_operation
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end # disable
|
102
|
+
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
|
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, @operation = var1, var2, operation
|
204
|
+
end
|
205
|
+
|
206
|
+
def to_s
|
207
|
+
var1 = "#{@var1}"
|
208
|
+
var2 = "#{@var2}"
|
209
|
+
if @operation == '*'
|
210
|
+
var1 = "(#{@var1})" if @var1.is_a?(Expression) && (@var1.plus? || @var1.minus?)
|
211
|
+
var2 = "(#{@var2})" if @var2.is_a?(Expression) && (@var2.plus? || @var1.minus?)
|
212
|
+
end
|
213
|
+
"#{var1}#{@operation}#{var2}"
|
214
|
+
end
|
215
|
+
|
216
|
+
def plus?
|
217
|
+
@operation == '+'
|
218
|
+
end
|
219
|
+
|
220
|
+
def minus?
|
221
|
+
@operation == '-'
|
222
|
+
end
|
223
|
+
|
224
|
+
def multiply?
|
225
|
+
@operation == '*'
|
226
|
+
end
|
227
|
+
|
228
|
+
def value
|
229
|
+
if undefined_variables.empty?
|
230
|
+
value_of(@var1).send @operation, value_of(@var2)
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
def undefined_variables
|
235
|
+
(undefined_variables_of(@var1) + undefined_variables_of(@var2)).uniq
|
236
|
+
end
|
237
|
+
|
238
|
+
private
|
239
|
+
|
240
|
+
def undefined_variables_of(variable)
|
241
|
+
variable.is_a?(Operatable) ? variable.undefined_variables : []
|
242
|
+
end
|
243
|
+
|
244
|
+
def value_of(variable)
|
245
|
+
variable.is_a?(Operatable) ? variable.value : variable
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
class Method < Operatable
|
250
|
+
def initialize(variable, operation)
|
251
|
+
@variable, @operation = variable, operation
|
252
|
+
end
|
253
|
+
|
254
|
+
def to_s
|
255
|
+
"#{@operation}(#{@variable})"
|
256
|
+
end
|
257
|
+
|
258
|
+
def value
|
259
|
+
Math.send @operation, @variable.value if undefined_variables.empty?
|
260
|
+
end
|
261
|
+
|
262
|
+
def undefined_variables
|
263
|
+
@variable.undefined_variables
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
class UnaryMinus < Operatable
|
268
|
+
attr_reader :variable
|
269
|
+
|
270
|
+
def self.create(expression)
|
271
|
+
if expression.is_a? UnaryMinus
|
272
|
+
expression.variable
|
273
|
+
else
|
274
|
+
new expression
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
def initialize(variable)
|
279
|
+
@variable = variable
|
280
|
+
end
|
281
|
+
|
282
|
+
def to_s
|
283
|
+
if @variable.is_a? Variable
|
284
|
+
"-#{@variable}"
|
285
|
+
else
|
286
|
+
"(-#{@variable})"
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
def value
|
291
|
+
-@variable.value if undefined_variables.empty?
|
292
|
+
end
|
293
|
+
|
294
|
+
def undefined_variables
|
295
|
+
@variable.undefined_variables
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
module Kernel
|
301
|
+
def var(options={})
|
302
|
+
Symbolic::Variable.new options
|
303
|
+
end
|
304
|
+
|
305
|
+
def symbolic
|
306
|
+
Symbolic.enabled = true
|
307
|
+
yield
|
308
|
+
Symbolic.enabled = false
|
309
|
+
end
|
310
|
+
end
|
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: symbolic
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- brainopia
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-11-17 00:00:00 +03:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rspec
|
17
|
+
type: :development
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.2.9
|
24
|
+
version:
|
25
|
+
description:
|
26
|
+
email: ravwar@gmail.com
|
27
|
+
executables: []
|
28
|
+
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files:
|
32
|
+
- README
|
33
|
+
files:
|
34
|
+
- .gitignore
|
35
|
+
- README
|
36
|
+
- Rakefile
|
37
|
+
- lib/symbolic.rb
|
38
|
+
- spec/spec.opts
|
39
|
+
- spec/spec_helper.rb
|
40
|
+
- spec/symbolic_spec.rb
|
41
|
+
has_rdoc: true
|
42
|
+
homepage: http://github.com/brainopia/symbolic
|
43
|
+
licenses: []
|
44
|
+
|
45
|
+
post_install_message:
|
46
|
+
rdoc_options:
|
47
|
+
- --charset=UTF-8
|
48
|
+
require_paths:
|
49
|
+
- lib
|
50
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: "0"
|
55
|
+
version:
|
56
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: "0"
|
61
|
+
version:
|
62
|
+
requirements: []
|
63
|
+
|
64
|
+
rubyforge_project:
|
65
|
+
rubygems_version: 1.3.5
|
66
|
+
signing_key:
|
67
|
+
specification_version: 3
|
68
|
+
summary: Symbolic math for ruby
|
69
|
+
test_files:
|
70
|
+
- spec/spec_helper.rb
|
71
|
+
- spec/symbolic_spec.rb
|