symbolic 0.0.6 → 0.1.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.
- data/README.rdoc +5 -3
- data/Rakefile +1 -1
- data/lib/symbolic/expression.rb +15 -7
- data/lib/symbolic/optimizations.rb +6 -2
- data/lib/symbolic/unary_minus.rb +4 -0
- data/lib/symbolic/variable.rb +1 -1
- data/spec/symbolic_spec.rb +73 -27
- metadata +2 -2
data/README.rdoc
CHANGED
@@ -1,14 +1,16 @@
|
|
1
1
|
Symbolic math for ruby.
|
2
2
|
|
3
|
-
Installation:
|
3
|
+
== Installation:
|
4
|
+
|
4
5
|
gem install symbolic
|
5
6
|
|
6
|
-
Introduction:
|
7
|
+
== Introduction:
|
8
|
+
|
7
9
|
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.
|
8
10
|
|
9
11
|
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.
|
10
12
|
|
11
|
-
Examples:
|
13
|
+
== Examples:
|
12
14
|
x = var :name => 'x'
|
13
15
|
y = var :name => 'y'
|
14
16
|
|
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.1.0'
|
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/expression.rb
CHANGED
@@ -1,18 +1,14 @@
|
|
1
1
|
module Symbolic
|
2
2
|
class Expression < Operatable
|
3
|
+
attr_reader :var1, :var2, :operation
|
4
|
+
|
3
5
|
def initialize(var1, var2, operation)
|
4
6
|
var1, var2 = var2, var1 if operation == '*' && var2.is_a?(Numeric)
|
5
7
|
@var1, @var2, @operation = var1, var2, operation
|
6
8
|
end
|
7
9
|
|
8
10
|
def to_s
|
9
|
-
|
10
|
-
var2 = "#{@var2}"
|
11
|
-
if ['*', '/'].include? @operation
|
12
|
-
var1 = "(#{@var1})" if @var1.is_a?(Expression) && (@var1.plus? || @var1.minus?) || @var1.is_a?(UnaryMinus)
|
13
|
-
var2 = "(#{@var2})" if @var2.is_a?(Expression) && (@var2.plus? || @var2.minus?) || @var2.is_a?(UnaryMinus)
|
14
|
-
end
|
15
|
-
"#{var1}#{@operation}#{var2}"
|
11
|
+
"#{brackets @var1}#{@operation}#{brackets @var2}"
|
16
12
|
end
|
17
13
|
|
18
14
|
def plus?
|
@@ -37,8 +33,20 @@ module Symbolic
|
|
37
33
|
(undefined_variables_of(@var1) + undefined_variables_of(@var2)).uniq
|
38
34
|
end
|
39
35
|
|
36
|
+
def ==(object)
|
37
|
+
object.var1 == @var1 && object.var2 == @var2 && object.operation == @operation
|
38
|
+
end
|
39
|
+
|
40
40
|
private
|
41
41
|
|
42
|
+
def brackets(var)
|
43
|
+
brackets_conditional(var) ? "(#{var})" : var.to_s
|
44
|
+
end
|
45
|
+
|
46
|
+
def brackets_conditional(var)
|
47
|
+
%w(* /).include?(@operation) && (var.is_a?(UnaryMinus) || var.is_a?(Expression) && (var.plus? || var.minus?))
|
48
|
+
end
|
49
|
+
|
42
50
|
def undefined_variables_of(variable)
|
43
51
|
variable.is_a?(Operatable) ? variable.undefined_variables : []
|
44
52
|
end
|
@@ -3,9 +3,11 @@ module Symbolic
|
|
3
3
|
def self.addition(symbolic_var, var, reverse=false)
|
4
4
|
if var == 0
|
5
5
|
symbolic_var
|
6
|
+
elsif var.is_a?(Numeric) && var < 0
|
7
|
+
symbolic_var - (-var)
|
6
8
|
elsif var.is_a? UnaryMinus
|
7
9
|
symbolic_var - var.variable
|
8
|
-
elsif
|
10
|
+
elsif symbolic_var.is_a? UnaryMinus
|
9
11
|
var - symbolic_var.variable
|
10
12
|
else
|
11
13
|
if reverse
|
@@ -18,9 +20,11 @@ module Symbolic
|
|
18
20
|
|
19
21
|
def self.subtraction(symbolic_var, var, reverse=false)
|
20
22
|
if var == 0
|
21
|
-
symbolic_var
|
23
|
+
symbolic_var * (reverse ? -1 : 1)
|
22
24
|
elsif var.is_a? UnaryMinus
|
23
25
|
symbolic_var + var.variable
|
26
|
+
elsif !reverse && var.is_a?(Numeric) && var < 0
|
27
|
+
symbolic_var + (-var)
|
24
28
|
elsif reverse && symbolic_var.is_a?(UnaryMinus)
|
25
29
|
var + symbolic_var.variable
|
26
30
|
else
|
data/lib/symbolic/unary_minus.rb
CHANGED
data/lib/symbolic/variable.rb
CHANGED
data/spec/symbolic_spec.rb
CHANGED
@@ -1,43 +1,89 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) +'/spec_helper')
|
2
2
|
|
3
3
|
describe "Symbolic" do
|
4
|
-
|
5
|
-
var :
|
4
|
+
before(:all) do
|
5
|
+
@x = var :name => :x, :value => 1
|
6
|
+
@y = var :name => :y, :value => 2
|
6
7
|
end
|
7
8
|
|
8
|
-
def
|
9
|
-
|
9
|
+
def expression(string)
|
10
|
+
eval string.gsub(/[a-z]+/, '@\0')
|
10
11
|
end
|
11
12
|
|
12
13
|
describe "evaluation (x=1, y=2):" do
|
13
|
-
def self.
|
14
|
+
def self.should_evaluate_to(conditions)
|
14
15
|
conditions.each do |symbolic_expression, result|
|
15
16
|
it symbolic_expression do
|
16
|
-
symbolic_expression.value.should == result
|
17
|
+
expression(symbolic_expression).value.should == result
|
17
18
|
end
|
18
19
|
end
|
19
20
|
end
|
20
21
|
|
21
|
-
|
22
|
-
x => 1,
|
23
|
-
y => 2,
|
24
|
-
+x => 1,
|
25
|
-
-x => -1,
|
26
|
-
x + 4 => 5,
|
27
|
-
3 + x => 4,
|
28
|
-
x + y => 3,
|
29
|
-
x - 1 => 0,
|
30
|
-
1 - x => 0,
|
31
|
-
x - y => -1,
|
32
|
-
-x + 3 => 2,
|
33
|
-
-y - x => -3,
|
34
|
-
x*3 => 3,
|
35
|
-
4*y => 8,
|
36
|
-
(+x)*(-y) => -2,
|
37
|
-
x/2 => 0,
|
38
|
-
y/2 => 1,
|
39
|
-
x/2.0 => 0.5,
|
40
|
-
-2/x => -2,
|
41
|
-
4/(-y) => -2
|
22
|
+
should_evaluate_to \
|
23
|
+
'x' => 1,
|
24
|
+
'y' => 2,
|
25
|
+
'+x' => 1,
|
26
|
+
'-x' => -1,
|
27
|
+
'x + 4' => 5,
|
28
|
+
'3 + x' => 4,
|
29
|
+
'x + y' => 3,
|
30
|
+
'x - 1' => 0,
|
31
|
+
'1 - x' => 0,
|
32
|
+
'x - y' => -1,
|
33
|
+
'-x + 3' => 2,
|
34
|
+
'-y - x' => -3,
|
35
|
+
'x*3' => 3,
|
36
|
+
'4*y' => 8,
|
37
|
+
'(+x)*(-y)' => -2,
|
38
|
+
'x/2' => 0,
|
39
|
+
'y/2' => 1,
|
40
|
+
'x/2.0' => 0.5,
|
41
|
+
'-2/x' => -2,
|
42
|
+
'4/(-y)' => -2
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "optimization:" do
|
46
|
+
def self.should_equal(conditions)
|
47
|
+
conditions.each do |non_optimized, optimized|
|
48
|
+
it non_optimized do
|
49
|
+
expression(non_optimized).should == expression(optimized)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
should_equal \
|
55
|
+
'-(-x)' => 'x',
|
56
|
+
|
57
|
+
'0 + x' => 'x',
|
58
|
+
'x + 0' => 'x',
|
59
|
+
'x + (-2)' => 'x - 2',
|
60
|
+
'-2 + x' => 'x - 2',
|
61
|
+
'-x + 2' => '2 - x',
|
62
|
+
'x + (-y)' => 'x - y',
|
63
|
+
'-y + x' => 'x - y',
|
64
|
+
|
65
|
+
'0 - x' => '-x',
|
66
|
+
'x - 0' => 'x',
|
67
|
+
'x - (-2)' => 'x + 2',
|
68
|
+
'-2 - (-x)' => 'x - 2',
|
69
|
+
'x - (-y)' => 'x + y',
|
70
|
+
|
71
|
+
'0 * x' => '0',
|
72
|
+
'x * 0' => '0',
|
73
|
+
'1 * x' => 'x',
|
74
|
+
'x * 1' => 'x',
|
75
|
+
'-1 * x' => '-x',
|
76
|
+
'x * (-1)' => '-x',
|
77
|
+
'x * (-3)' => '-(x*3)',
|
78
|
+
'-3 * x' => '-(x*3)',
|
79
|
+
'-3 * (-x)' => 'x*3',
|
80
|
+
'x*(-y)' => '-(x*y)',
|
81
|
+
'-x*y' => '-(x*y)',
|
82
|
+
'(-x)*(-y)' => 'x*y',
|
83
|
+
|
84
|
+
'x / 1' => 'x'
|
85
|
+
end
|
86
|
+
|
87
|
+
describe "formulas" do
|
42
88
|
end
|
43
89
|
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.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- brainopia
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-11-
|
12
|
+
date: 2009-11-25 00:00:00 +03:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|