symbolic 0.2.5 → 0.3.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 +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
|