symbolic 0.2.5 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +6 -6
- data/Rakefile +1 -1
- data/lib/symbolic/coerced.rb +5 -5
- data/lib/{extensions → symbolic/extensions}/kernel.rb +0 -0
- data/lib/{extensions → symbolic/extensions}/matrix.rb +0 -0
- data/lib/{extensions → symbolic/extensions}/numeric.rb +0 -4
- data/lib/symbolic/extensions/rational.rb +13 -0
- data/lib/symbolic/factor.rb +129 -0
- data/lib/symbolic/math.rb +10 -6
- data/lib/symbolic/statistics.rb +13 -0
- data/lib/symbolic/summand.rb +86 -0
- data/lib/symbolic.rb +29 -21
- data/spec/symbolic_spec.rb +11 -10
- metadata +9 -14
- data/lib/symbolic/operation/binary/addition.rb +0 -15
- data/lib/symbolic/operation/binary/division.rb +0 -25
- data/lib/symbolic/operation/binary/exponentiation.rb +0 -35
- data/lib/symbolic/operation/binary/multiplication.rb +0 -84
- data/lib/symbolic/operation/binary/subtraction.rb +0 -19
- data/lib/symbolic/operation/binary.rb +0 -54
- data/lib/symbolic/operation/unary/minus.rb +0 -39
- data/lib/symbolic/operation/unary.rb +0 -13
- data/lib/symbolic/operation.rb +0 -61
data/README.rdoc
CHANGED
@@ -12,16 +12,16 @@ Symbolic math can be really helpful if you want to simplify some giant equation
|
|
12
12
|
x = var :name => 'x'
|
13
13
|
y = var :name => 'y'
|
14
14
|
|
15
|
-
f = (
|
16
|
-
puts f # => (
|
15
|
+
f = (-2*(-x) + y*(-1.0) + 6*x/(3*x))*x - 2*y + 2*y - (2**x)**y
|
16
|
+
puts f # => (2*x-y+2)*x-2**(x*y)
|
17
17
|
|
18
|
-
p f.
|
19
|
-
p f.
|
20
|
-
p f.undefined_variables.map &:name # => ["x", "y"]
|
18
|
+
p f.operations # => {"+"=>1, "-"=>2, "*"=>3, "/"=>0, "**"=>1, "-@"=>0}
|
19
|
+
p f.variables.map &:name # => ["x", "y"]
|
21
20
|
x.value = 2
|
21
|
+
p f.undefined_variables.map &:name # => ["y"]
|
22
22
|
y.value = 4
|
23
23
|
|
24
|
-
puts f.value # =>
|
24
|
+
puts f.value # => -252.0
|
25
25
|
|
26
26
|
|
27
27
|
g = Symbolic::Math.cos(y)
|
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.
|
8
|
+
gem.version = '0.3.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/coerced.rb
CHANGED
@@ -5,23 +5,23 @@ module Symbolic
|
|
5
5
|
end
|
6
6
|
|
7
7
|
def +(numeric)
|
8
|
-
|
8
|
+
Summand.add numeric, @symbolic
|
9
9
|
end
|
10
10
|
|
11
11
|
def -(numeric)
|
12
|
-
|
12
|
+
Summand.subtract numeric, @symbolic
|
13
13
|
end
|
14
14
|
|
15
15
|
def *(numeric)
|
16
|
-
|
16
|
+
Factor.multiply numeric, @symbolic
|
17
17
|
end
|
18
18
|
|
19
19
|
def /(numeric)
|
20
|
-
|
20
|
+
Factor.divide numeric, @symbolic
|
21
21
|
end
|
22
22
|
|
23
23
|
def **(numeric)
|
24
|
-
|
24
|
+
Factor.exponent numeric, @symbolic
|
25
25
|
end
|
26
26
|
end
|
27
27
|
end
|
File without changes
|
File without changes
|
@@ -0,0 +1,129 @@
|
|
1
|
+
class Symbolic::Factor
|
2
|
+
include Symbolic
|
3
|
+
|
4
|
+
class << self
|
5
|
+
def exponent(base, exponent)
|
6
|
+
simplify_exponents! factors = unite_exponents(base, exponent)
|
7
|
+
simplify(*factors) || new(factors)
|
8
|
+
end
|
9
|
+
|
10
|
+
def multiply(var1, var2)
|
11
|
+
simplify_exponents! factors = unite(factors(var1), factors(var2))
|
12
|
+
simplify(*factors) || new(factors)
|
13
|
+
end
|
14
|
+
|
15
|
+
def divide(var1, var2)
|
16
|
+
simplify_exponents! factors = unite(factors(var1), reverse(var2))
|
17
|
+
simplify(*factors) || new(factors)
|
18
|
+
end
|
19
|
+
|
20
|
+
def simplify_exponents!(factors)
|
21
|
+
factors[1].delete_if {|base, exp| (base == 1) || (exp == 0) }
|
22
|
+
factors[0] = 0 if factors[1].any? {|base, exp| base == 0 }
|
23
|
+
end
|
24
|
+
|
25
|
+
def simplify(numeric, symbolic)
|
26
|
+
if numeric == 0 || symbolic.empty?
|
27
|
+
(numeric.round == numeric) ? numeric.to_i : numeric.to_f
|
28
|
+
elsif numeric == 1 && symbolic.size == 1 && symbolic.values.first == 1
|
29
|
+
symbolic.keys.first
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def reverse(var)
|
34
|
+
factors(var).dup.tap do |it|
|
35
|
+
it[0] = it[0]**-1
|
36
|
+
it[1] = Hash[*it[1].map {|b,e| [b,-e] }.flatten]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def factors(var)
|
41
|
+
var.is_a?(Symbolic) ? var.send(:factors) : [var, {}]
|
42
|
+
end
|
43
|
+
|
44
|
+
def unite(factors1, factors2)
|
45
|
+
numeric1, symbolic1 = factors1
|
46
|
+
numeric2, symbolic2 = factors2
|
47
|
+
|
48
|
+
numeric = numeric1 * numeric2
|
49
|
+
symbolic = symbolic1.merge(symbolic2) {|base, exp1, exp2| exp1 + exp2 }
|
50
|
+
return numeric, symbolic
|
51
|
+
end
|
52
|
+
|
53
|
+
def unite_exponents(base, exponent)
|
54
|
+
if base.is_a? Symbolic::Factor
|
55
|
+
numeric, symbolic = factors(base)
|
56
|
+
return numeric**exponent, Hash[*symbolic.map {|base,exp| [base,exp*exponent] }.flatten]
|
57
|
+
else
|
58
|
+
[1, { base => exponent }]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def initialize(factors)
|
64
|
+
@factors = factors
|
65
|
+
end
|
66
|
+
|
67
|
+
def value
|
68
|
+
@factors[1].inject(@factors[0]) {|value, (base, exp)| value * base.value ** exp.value }
|
69
|
+
end
|
70
|
+
|
71
|
+
def variables
|
72
|
+
@factors[1].map {|k,v| [variables_of(k), variables_of(v)] }.flatten.uniq
|
73
|
+
end
|
74
|
+
|
75
|
+
def to_s
|
76
|
+
simplify_output
|
77
|
+
end
|
78
|
+
|
79
|
+
def ==(object)
|
80
|
+
object.send(:factors) == @factors rescue false
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
attr_reader :factors
|
86
|
+
|
87
|
+
def summands
|
88
|
+
if @factors[0] == 1
|
89
|
+
super
|
90
|
+
else
|
91
|
+
[0, {(self/@factors[0]) => @factors[0]}]
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def coefficient_to_string(numeric)
|
96
|
+
"#{'-' if numeric < 0}#{"#{rational_to_string numeric.abs}*" if numeric.abs != 1}"
|
97
|
+
end
|
98
|
+
|
99
|
+
def exponent_to_string(base, exponent)
|
100
|
+
"#{brackets base}#{"**#{brackets exponent}" if exponent != 1}"
|
101
|
+
end
|
102
|
+
|
103
|
+
def brackets(var)
|
104
|
+
[Numeric, Symbolic::Variable].any? {|klass| var.is_a? klass } ? var : "(#{var})"
|
105
|
+
end
|
106
|
+
|
107
|
+
def simplify_output
|
108
|
+
groups = @factors[1].group_by {|b,e| e.is_a?(Numeric) && e < 0 }
|
109
|
+
reversed_factors = groups[true] ? [1, Hash[*groups[true].flatten] ] : nil
|
110
|
+
factors = groups[false] ? [@factors[0], Hash[*groups[false].flatten] ] : nil
|
111
|
+
output = '' << (factors ? output(factors) : rational_to_string(@factors[0]))
|
112
|
+
output << "/#{reversed_output reversed_factors}" if reversed_factors
|
113
|
+
output
|
114
|
+
end
|
115
|
+
|
116
|
+
def output(factors)
|
117
|
+
coefficient_to_string(factors[0]) <<
|
118
|
+
factors[1].map {|base,exp| exponent_to_string base,exp }.join('*')
|
119
|
+
end
|
120
|
+
|
121
|
+
def reversed_output(factors)
|
122
|
+
result = output [factors[0], Hash[*factors[1].map {|b,e| [b,-e] }.flatten]]
|
123
|
+
(factors[1].length > 1) ? "(#{result})" : result
|
124
|
+
end
|
125
|
+
|
126
|
+
def rational_to_string(numeric)
|
127
|
+
((numeric.round == numeric) ? numeric.to_i : numeric.to_f).to_s
|
128
|
+
end
|
129
|
+
end
|
data/lib/symbolic/math.rb
CHANGED
@@ -1,9 +1,13 @@
|
|
1
1
|
module Symbolic::Math
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
2
|
+
Math.methods(false).each do |method|
|
3
|
+
instance_eval <<-CODE, __FILE__, __LINE__
|
4
|
+
def #{method}(argument)
|
5
|
+
unless argument.is_a? Numeric
|
6
|
+
Symbolic::Function.new argument, :#{method}
|
7
|
+
else
|
8
|
+
::Math.#{method} argument
|
9
|
+
end
|
10
|
+
end
|
11
|
+
CODE
|
8
12
|
end
|
9
13
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Symbolic
|
2
|
+
def operations
|
3
|
+
formula = to_s
|
4
|
+
stats = {}
|
5
|
+
stats['+'] = formula.scan(/\+/).size
|
6
|
+
stats['-'] = formula.scan(/[^(]-/).size
|
7
|
+
stats['*'] = formula.scan(/[^*]\*[^*]/).size
|
8
|
+
stats['/'] = formula.scan(/\//).size
|
9
|
+
stats['**']= formula.scan(/\*\*/).size
|
10
|
+
stats['-@']= formula.scan(/\(-/).size + formula.scan(/^-/).size
|
11
|
+
stats
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
class Symbolic::Summand
|
2
|
+
include Symbolic
|
3
|
+
|
4
|
+
class << self
|
5
|
+
def add(var1, var2)
|
6
|
+
simplify_coefficients! summands = unite(summands(var1), summands(var2))
|
7
|
+
simplify(*summands) || new(summands)
|
8
|
+
end
|
9
|
+
|
10
|
+
def subtract(var1, var2)
|
11
|
+
simplify_coefficients! summands = unite(summands(var1), reverse(var2))
|
12
|
+
simplify(*summands) || new(summands)
|
13
|
+
end
|
14
|
+
|
15
|
+
def reverse(var)
|
16
|
+
summands(var).tap do |it|
|
17
|
+
it[0] = -it[0]
|
18
|
+
it[1] = Hash[*it[1].map {|b,e| [b,-e] }.flatten]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def summands(var)
|
23
|
+
var.is_a?(Symbolic) ? var.send(:summands) : [var, {}]
|
24
|
+
end
|
25
|
+
|
26
|
+
def unite(summands1, summands2)
|
27
|
+
numeric1, symbolic1 = summands1
|
28
|
+
numeric2, symbolic2 = summands2
|
29
|
+
|
30
|
+
numeric = numeric1 + numeric2
|
31
|
+
symbolic = symbolic1.merge(symbolic2) {|base, coef1, coef2| coef1 + coef2 }
|
32
|
+
return numeric, symbolic
|
33
|
+
end
|
34
|
+
|
35
|
+
def simplify_coefficients!(summands)
|
36
|
+
summands[1].delete_if {|base, coef| coef == 0 }
|
37
|
+
end
|
38
|
+
|
39
|
+
def simplify(numeric, symbolic)
|
40
|
+
if symbolic.empty?
|
41
|
+
numeric
|
42
|
+
elsif numeric == 0 && symbolic.size == 1
|
43
|
+
symbolic.values.first * symbolic.keys.first
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def initialize(summands)
|
49
|
+
@summands = summands
|
50
|
+
end
|
51
|
+
|
52
|
+
def value
|
53
|
+
@summands[1].inject(@summands[0]) {|value, (base, coef)| value + base.value * coef.value }
|
54
|
+
end
|
55
|
+
|
56
|
+
def variables
|
57
|
+
@summands[1].map {|k,v| [variables_of(k), variables_of(v)] }.flatten.uniq
|
58
|
+
end
|
59
|
+
|
60
|
+
def ==(object)
|
61
|
+
object.send(:summands) == @summands rescue false
|
62
|
+
end
|
63
|
+
|
64
|
+
def to_s
|
65
|
+
output = @summands[1].map {|base, coef| coef_to_string(coef) + base.to_s }
|
66
|
+
output << remainder_to_string(@summands[0]) if @summands[0] != 0
|
67
|
+
output[0] = output.first[1..-1]
|
68
|
+
output.join
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
attr_reader :summands
|
74
|
+
|
75
|
+
def coef_to_string(coef)
|
76
|
+
"#{(coef > 0) ? '+' : '-' }#{ "#{rational_to_string(coef.abs)}*" if coef.abs != 1}"
|
77
|
+
end
|
78
|
+
|
79
|
+
def remainder_to_string(numeric)
|
80
|
+
"#{'+' if numeric > 0}#{numeric}"
|
81
|
+
end
|
82
|
+
|
83
|
+
def rational_to_string(numeric)
|
84
|
+
((numeric.round == numeric) ? numeric.to_i : numeric.to_f).to_s
|
85
|
+
end
|
86
|
+
end
|
data/lib/symbolic.rb
CHANGED
@@ -1,27 +1,21 @@
|
|
1
1
|
Symbolic = Module.new
|
2
2
|
|
3
|
-
require 'symbolic/operation'
|
4
|
-
require 'symbolic/operation/unary'
|
5
|
-
require 'symbolic/operation/unary/minus'
|
6
|
-
require 'symbolic/operation/binary'
|
7
|
-
require 'symbolic/operation/binary/addition'
|
8
|
-
require 'symbolic/operation/binary/subtraction'
|
9
|
-
require 'symbolic/operation/binary/multiplication'
|
10
|
-
require 'symbolic/operation/binary/division'
|
11
|
-
require 'symbolic/operation/binary/exponentiation'
|
12
|
-
|
13
3
|
require 'symbolic/coerced'
|
14
4
|
require 'symbolic/variable'
|
5
|
+
require 'symbolic/summand'
|
6
|
+
require 'symbolic/factor'
|
15
7
|
require 'symbolic/function'
|
16
8
|
require 'symbolic/math'
|
9
|
+
require 'symbolic/statistics'
|
17
10
|
|
18
|
-
require 'extensions/kernel'
|
19
|
-
require 'extensions/numeric'
|
20
|
-
require 'extensions/matrix'
|
11
|
+
require 'symbolic/extensions/kernel'
|
12
|
+
require 'symbolic/extensions/numeric'
|
13
|
+
require 'symbolic/extensions/matrix' if Object.const_defined? 'Matrix'
|
14
|
+
require 'symbolic/extensions/rational' if RUBY_VERSION == '1.8.7'
|
21
15
|
|
22
16
|
module Symbolic
|
23
17
|
def -@
|
24
|
-
|
18
|
+
Factor.multiply self, -1
|
25
19
|
end
|
26
20
|
|
27
21
|
def +@
|
@@ -29,30 +23,44 @@ module Symbolic
|
|
29
23
|
end
|
30
24
|
|
31
25
|
def +(var)
|
32
|
-
|
26
|
+
Summand.add self, var
|
33
27
|
end
|
34
28
|
|
35
29
|
def -(var)
|
36
|
-
|
30
|
+
Summand.subtract self, var
|
37
31
|
end
|
38
32
|
|
39
33
|
def *(var)
|
40
|
-
|
34
|
+
Factor.multiply self, var
|
41
35
|
end
|
42
36
|
|
43
37
|
def /(var)
|
44
|
-
|
38
|
+
Factor.divide self, var
|
45
39
|
end
|
46
40
|
|
47
41
|
def **(var)
|
48
|
-
|
42
|
+
Factor.exponent self, var
|
49
43
|
end
|
50
44
|
|
51
45
|
def coerce(numeric)
|
52
46
|
return Coerced.new(self), numeric
|
53
47
|
end
|
54
48
|
|
55
|
-
def
|
56
|
-
|
49
|
+
def undefined_variables
|
50
|
+
variables.select {|it| it.value.nil? }
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def factors
|
56
|
+
[1, { self => 1 }]
|
57
|
+
end
|
58
|
+
|
59
|
+
def summands
|
60
|
+
[0, { self => 1 }]
|
61
|
+
end
|
62
|
+
|
63
|
+
def variables_of(var)
|
64
|
+
var.variables rescue []
|
57
65
|
end
|
58
66
|
end
|
data/spec/symbolic_spec.rb
CHANGED
@@ -43,9 +43,8 @@ describe "Symbolic" do
|
|
43
43
|
'x*3' => 3,
|
44
44
|
'4*y' => 8,
|
45
45
|
'(+x)*(-y)' => -2,
|
46
|
-
'x/2' => 0,
|
46
|
+
'x/2' => 0.5,
|
47
47
|
'y/2' => 1,
|
48
|
-
'x/2.0' => 0.5,
|
49
48
|
'-2/x' => -2,
|
50
49
|
'4/(-y)' => -2,
|
51
50
|
'x**2' => 1,
|
@@ -105,9 +104,10 @@ describe "Symbolic" do
|
|
105
104
|
'x.variables' => '[x]',
|
106
105
|
'(-(x+y)).variables' => '[x,y]',
|
107
106
|
'(x+y).undefined_variables' => '[]',
|
108
|
-
'(
|
109
|
-
'
|
110
|
-
'
|
107
|
+
'(x*y).undefined_variables' => '[]'#,
|
108
|
+
# '(-x**y-4*y+5-y/x).detailed_operations' =>
|
109
|
+
# '{"+" => 1, "-" => 2, "*" => 1, "/" => 1, "-@" => 1, "**" => 1}',
|
110
|
+
# '(-x**y-4*y+5-y/x).operations' => '7'
|
111
111
|
end
|
112
112
|
|
113
113
|
describe "to_s:" do
|
@@ -124,16 +124,17 @@ describe "Symbolic" do
|
|
124
124
|
'-x' => '-x',
|
125
125
|
'x+1' => 'x+1',
|
126
126
|
'x-4' => 'x-4',
|
127
|
-
'-(x+y)' => '-x-y',
|
128
|
-
'-(x-y)' => 'y-x',
|
127
|
+
# '-(x+y)' => '-x-y',
|
128
|
+
# '-(x-y)' => 'y-x',
|
129
129
|
'x*y' => 'x*y',
|
130
130
|
'(-x)*y' => '-x*y',
|
131
131
|
'(x+2)*(y+3)*4' => '4*(x+2)*(y+3)',
|
132
132
|
'4/x' => '4/x',
|
133
|
-
'
|
133
|
+
'2*x**(-1)*y**(-1)' => '2/(x*y)',
|
134
|
+
# '(-(2+x))/(-(-y))' => '(-2-x)/y',
|
134
135
|
'x**y' => 'x**y',
|
135
136
|
'x**(y-4)' => 'x**(y-4)',
|
136
|
-
'(x+1)**(y*2)' => '(x+1)**(2*y)'
|
137
|
-
'-(x**y -2)+5' => '2-x**y+5'
|
137
|
+
'(x+1)**(y*2)' => '(x+1)**(2*y)'#,
|
138
|
+
# '-(x**y -2)+5' => '2-x**y+5'
|
138
139
|
end
|
139
140
|
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.
|
4
|
+
version: 0.3.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-12-
|
12
|
+
date: 2009-12-13 00:00:00 +03:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -34,22 +34,17 @@ files:
|
|
34
34
|
- .gitignore
|
35
35
|
- README.rdoc
|
36
36
|
- Rakefile
|
37
|
-
- lib/extensions/kernel.rb
|
38
|
-
- lib/extensions/matrix.rb
|
39
|
-
- lib/extensions/numeric.rb
|
40
37
|
- lib/symbolic.rb
|
41
38
|
- lib/symbolic/coerced.rb
|
39
|
+
- lib/symbolic/extensions/kernel.rb
|
40
|
+
- lib/symbolic/extensions/matrix.rb
|
41
|
+
- lib/symbolic/extensions/numeric.rb
|
42
|
+
- lib/symbolic/extensions/rational.rb
|
43
|
+
- lib/symbolic/factor.rb
|
42
44
|
- lib/symbolic/function.rb
|
43
45
|
- lib/symbolic/math.rb
|
44
|
-
- lib/symbolic/
|
45
|
-
- lib/symbolic/
|
46
|
-
- lib/symbolic/operation/binary/addition.rb
|
47
|
-
- lib/symbolic/operation/binary/division.rb
|
48
|
-
- lib/symbolic/operation/binary/exponentiation.rb
|
49
|
-
- lib/symbolic/operation/binary/multiplication.rb
|
50
|
-
- lib/symbolic/operation/binary/subtraction.rb
|
51
|
-
- lib/symbolic/operation/unary.rb
|
52
|
-
- lib/symbolic/operation/unary/minus.rb
|
46
|
+
- lib/symbolic/statistics.rb
|
47
|
+
- lib/symbolic/summand.rb
|
53
48
|
- lib/symbolic/variable.rb
|
54
49
|
- spec/spec.opts
|
55
50
|
- spec/spec_helper.rb
|
@@ -1,25 +0,0 @@
|
|
1
|
-
class Symbolic::Operation::Binary::Division < Symbolic::Operation::Binary
|
2
|
-
def self.simplify_first_arg(var1, var2)
|
3
|
-
if var1 == 0
|
4
|
-
0
|
5
|
-
elsif negative?(var1)
|
6
|
-
-(var1.abs / var2)
|
7
|
-
end
|
8
|
-
end
|
9
|
-
|
10
|
-
def self.simplify_second_arg(var1, var2)
|
11
|
-
if var2 == 1
|
12
|
-
var1
|
13
|
-
elsif negative?(var2)
|
14
|
-
-(var1 / var2.abs)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
def sign
|
19
|
-
'/'
|
20
|
-
end
|
21
|
-
|
22
|
-
def brackets_for
|
23
|
-
[:addition, :subtraction]
|
24
|
-
end
|
25
|
-
end
|
@@ -1,35 +0,0 @@
|
|
1
|
-
class Symbolic::Operation::Binary::Exponentiation < Symbolic::Operation::Binary
|
2
|
-
def self.simplify_first_arg(var1, var2)
|
3
|
-
if var1 == 0
|
4
|
-
0
|
5
|
-
elsif var1 == 1
|
6
|
-
1
|
7
|
-
elsif negative?(var1) && var2.respond_to?(:even?)
|
8
|
-
without_sign = var1.abs ** var2
|
9
|
-
var2.even? ? without_sign : -without_sign
|
10
|
-
elsif operation(var1) == :exponentiation
|
11
|
-
var1.send(:base) ** (var1.send(:exponent) * var2)
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
def self.simplify_second_arg(var1, var2)
|
16
|
-
if var2 == 0
|
17
|
-
1
|
18
|
-
elsif var2 == 1
|
19
|
-
var1
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def brackets_for
|
24
|
-
[:unary_minus, :addition, :subtraction, :multiplication, :division]
|
25
|
-
end
|
26
|
-
|
27
|
-
def sign
|
28
|
-
'**'
|
29
|
-
end
|
30
|
-
|
31
|
-
private
|
32
|
-
|
33
|
-
alias base var1
|
34
|
-
alias exponent var2
|
35
|
-
end
|
@@ -1,84 +0,0 @@
|
|
1
|
-
class Symbolic::Operation
|
2
|
-
class Binary::Multiplication < Binary
|
3
|
-
|
4
|
-
class << self
|
5
|
-
def for(var1, var2)
|
6
|
-
# TODO: def -@(var); -1 * var; end
|
7
|
-
sign1, var1 = sign_and_modulus var1
|
8
|
-
sign2, var2 = sign_and_modulus var2
|
9
|
-
sign = (sign1 == sign2) ? :+@ : :-@
|
10
|
-
|
11
|
-
factors = unite factors(var1), factors(var2)
|
12
|
-
(simplify(factors) || new(factors)).send sign
|
13
|
-
end
|
14
|
-
|
15
|
-
def simplify(factors)
|
16
|
-
factors.first if factors.length == 1
|
17
|
-
end
|
18
|
-
|
19
|
-
def unite(factors1, factors2)
|
20
|
-
numeric = extract_numeric!(factors1) * extract_numeric!(factors2)
|
21
|
-
return [0] if numeric == 0
|
22
|
-
factors = unite_by_exponent factors1, factors2
|
23
|
-
factors.unshift(numeric) if numeric != 1 || factors.empty?
|
24
|
-
factors
|
25
|
-
end
|
26
|
-
|
27
|
-
def unite_by_exponent(factors1, factors2)
|
28
|
-
exponents(factors1).
|
29
|
-
merge(exponents factors2) {|base,exponent1,exponent2| exponent1 + exponent2 }.
|
30
|
-
delete_if {|base, exponent| exponent == 0 }.
|
31
|
-
map {|base, exponent| base**exponent }
|
32
|
-
end
|
33
|
-
|
34
|
-
def exponents(factors)
|
35
|
-
exponents = factors.map {|it| base_and_exponent it }
|
36
|
-
Hash[exponents]
|
37
|
-
end
|
38
|
-
|
39
|
-
def base_and_exponent(var)
|
40
|
-
var.is_a?(Binary::Exponentiation) ? [var.send(:base), var.send(:exponent)] : [var, 1]
|
41
|
-
end
|
42
|
-
|
43
|
-
def extract_numeric!(factors)
|
44
|
-
factors.first.is_a?(Numeric) ? factors.shift : 1
|
45
|
-
end
|
46
|
-
|
47
|
-
def sign_and_modulus(var)
|
48
|
-
negative?(var) ? [:-@, var.abs] : [:+@, var]
|
49
|
-
end
|
50
|
-
|
51
|
-
def factors(var)
|
52
|
-
var.respond_to?(:factors) ? var.send(:factors).dup : [var]
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def sign
|
57
|
-
'*'
|
58
|
-
end
|
59
|
-
|
60
|
-
def brackets_for
|
61
|
-
[:addition, :subtraction]
|
62
|
-
end
|
63
|
-
|
64
|
-
def initialize(factors)
|
65
|
-
@factors = factors
|
66
|
-
end
|
67
|
-
|
68
|
-
def variables
|
69
|
-
@factors.map(&:variables).flatten.uniq
|
70
|
-
end
|
71
|
-
|
72
|
-
def value
|
73
|
-
@factors.inject(1) {|product, it| product*it.value } if undefined_variables.empty?
|
74
|
-
end
|
75
|
-
|
76
|
-
def to_s
|
77
|
-
@factors.map {|it| brackets it }.join '*'
|
78
|
-
end
|
79
|
-
|
80
|
-
protected
|
81
|
-
|
82
|
-
attr_reader :factors
|
83
|
-
end
|
84
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
class Symbolic::Operation::Binary::Subtraction < Symbolic::Operation::Binary
|
2
|
-
def self.simplify_first_arg(var1, var2)
|
3
|
-
-var2 if var1 == 0
|
4
|
-
end
|
5
|
-
|
6
|
-
def self.simplify_second_arg(var1, var2)
|
7
|
-
if var2 == 0
|
8
|
-
var1
|
9
|
-
elsif negative?(var2)
|
10
|
-
var1 + var2.abs
|
11
|
-
elsif [:addition, :subtraction].include? operation(var2)
|
12
|
-
var1 + (-var2)
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
def sign
|
17
|
-
'-'
|
18
|
-
end
|
19
|
-
end
|
@@ -1,54 +0,0 @@
|
|
1
|
-
class Symbolic::Operation::Binary < Symbolic::Operation
|
2
|
-
def self.simplify(var1, var2)
|
3
|
-
simplify_first_arg(var1, var2) || simplify_second_arg(var1, var2)
|
4
|
-
end
|
5
|
-
|
6
|
-
def self.symmetric
|
7
|
-
def self.simplify(var1, var2)
|
8
|
-
simplify_first_arg(var1, var2) || simplify_first_arg(var2, var1)
|
9
|
-
end
|
10
|
-
|
11
|
-
def ==(object)
|
12
|
-
(object.class == self.class) &&
|
13
|
-
((object.var1 == @var1 && object.var2 == @var2) || (object.var1 == @var2 && object.var2 == @var1))
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
def initialize(var1, var2)
|
18
|
-
@var1, @var2 = var1, var2
|
19
|
-
end
|
20
|
-
|
21
|
-
def variables
|
22
|
-
@var1.variables | @var2.variables
|
23
|
-
end
|
24
|
-
|
25
|
-
def undefined_variables
|
26
|
-
variables.select {|it| it.value.nil? }
|
27
|
-
end
|
28
|
-
|
29
|
-
def value
|
30
|
-
@var1.value.send sign, @var2.value if undefined_variables.empty?
|
31
|
-
end
|
32
|
-
|
33
|
-
def detailed_operations
|
34
|
-
operations_of(@var1).tap {|it| it.merge!(operations_of @var2)[sign] += 1 }
|
35
|
-
end
|
36
|
-
|
37
|
-
def to_s
|
38
|
-
"#{brackets @var1}#{sign}#{brackets @var2}"
|
39
|
-
end
|
40
|
-
|
41
|
-
def ==(object)
|
42
|
-
(object.class == self.class) && (object.var1 == @var1 && object.var2 == @var2)
|
43
|
-
end
|
44
|
-
|
45
|
-
protected
|
46
|
-
|
47
|
-
attr_reader :var1, :var2
|
48
|
-
|
49
|
-
private
|
50
|
-
|
51
|
-
def operations_of(var)
|
52
|
-
var.is_a?(Symbolic) ? var.detailed_operations : Hash.new(0)
|
53
|
-
end
|
54
|
-
end
|
@@ -1,39 +0,0 @@
|
|
1
|
-
class Symbolic::Operation
|
2
|
-
class Unary::Minus < Unary
|
3
|
-
def self.simplify(expression)
|
4
|
-
case operation(expression)
|
5
|
-
when :unary_minus
|
6
|
-
expression.abs
|
7
|
-
when :addition
|
8
|
-
-expression.send(:var1)-expression.send(:var2)
|
9
|
-
when :subtraction
|
10
|
-
expression.send(:var2) - expression.send(:var1)
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
def abs
|
15
|
-
# TODO: implement Symbolic#abs
|
16
|
-
@expression
|
17
|
-
end
|
18
|
-
|
19
|
-
def brackets_for
|
20
|
-
[:addition, :subtraction]
|
21
|
-
end
|
22
|
-
|
23
|
-
def value
|
24
|
-
-@expression.value if undefined_variables.empty?
|
25
|
-
end
|
26
|
-
|
27
|
-
def ==(object)
|
28
|
-
object.is_a?(Unary::Minus) && object.abs == @expression
|
29
|
-
end
|
30
|
-
|
31
|
-
def detailed_operations
|
32
|
-
@expression.detailed_operations.tap {|it| it['-@'] += 1}
|
33
|
-
end
|
34
|
-
|
35
|
-
def to_s
|
36
|
-
"-#{brackets @expression}"
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
data/lib/symbolic/operation.rb
DELETED
@@ -1,61 +0,0 @@
|
|
1
|
-
class Symbolic::Operation
|
2
|
-
include Symbolic
|
3
|
-
|
4
|
-
def self.for(*args)
|
5
|
-
simplify(*args) || new(*args)
|
6
|
-
end
|
7
|
-
|
8
|
-
def self.negative?(var)
|
9
|
-
var.is_a?(Unary::Minus) || (var.is_a?(Numeric) && var < 0)
|
10
|
-
end
|
11
|
-
|
12
|
-
private
|
13
|
-
|
14
|
-
def self.operation(expression)
|
15
|
-
case expression
|
16
|
-
when Unary::Minus then :unary_minus
|
17
|
-
when Binary::Addition then :addition
|
18
|
-
when Binary::Subtraction then :subtraction
|
19
|
-
when Binary::Multiplication then :multiplication
|
20
|
-
when Binary::Division then :division
|
21
|
-
when Binary::Exponentiation then :exponentiation
|
22
|
-
when Symbolic::Variable then :variable
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
def operation(expression)
|
27
|
-
self.class.operation expression
|
28
|
-
end
|
29
|
-
|
30
|
-
def unary_minus?(expression)
|
31
|
-
operation(expression) == :unary_minus
|
32
|
-
end
|
33
|
-
|
34
|
-
def addition?(expression)
|
35
|
-
operation(expression) == :addition
|
36
|
-
end
|
37
|
-
|
38
|
-
def subtraction?(expression)
|
39
|
-
operation(expression) == :subtraction
|
40
|
-
end
|
41
|
-
|
42
|
-
def multiplication?(expression)
|
43
|
-
operation(expression) == :multiplication
|
44
|
-
end
|
45
|
-
|
46
|
-
def division?(expression)
|
47
|
-
operation(expression) == :division
|
48
|
-
end
|
49
|
-
|
50
|
-
def variable?(expression)
|
51
|
-
operation(expession) == :variable
|
52
|
-
end
|
53
|
-
|
54
|
-
def brackets(var)
|
55
|
-
brackets_for.include?(operation var) ? "(#{var})" : var.to_s
|
56
|
-
end
|
57
|
-
|
58
|
-
def brackets_for
|
59
|
-
[]
|
60
|
-
end
|
61
|
-
end
|