symbolic 0.3.3 → 0.3.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 +5 -5
- data/Rakefile +3 -3
- data/lib/symbolic.rb +12 -15
- data/lib/symbolic/coerced.rb +11 -5
- data/lib/symbolic/expression.rb +73 -0
- data/lib/symbolic/factors.rb +119 -0
- data/lib/symbolic/summands.rb +65 -0
- data/lib/symbolic/variable.rb +8 -5
- data/spec/symbolic_spec.rb +16 -16
- metadata +7 -6
- data/lib/symbolic/factor.rb +0 -162
- data/lib/symbolic/summand.rb +0 -86
data/README.rdoc
CHANGED
@@ -1,18 +1,18 @@
|
|
1
1
|
Symbolic math for ruby.
|
2
2
|
|
3
|
-
== Installation
|
3
|
+
== Installation
|
4
4
|
|
5
5
|
gem install symbolic
|
6
6
|
|
7
|
-
== Introduction
|
8
|
-
|
9
|
-
Symbolic doesn't have any external dependencies. It uses only pure ruby (less than 400 LOC (lines of code)).
|
7
|
+
== Introduction
|
10
8
|
|
11
9
|
This gem can help you
|
12
10
|
- if you want to get a simplified form of a big equation
|
13
11
|
- if you want to speed up similar calculations
|
14
12
|
- if you need an abstraction layer for math
|
15
13
|
|
14
|
+
Symbolic doesn't have any external dependencies. It uses only pure ruby (less than 400 lines of code).
|
15
|
+
|
16
16
|
== Tutorial
|
17
17
|
|
18
18
|
First, you need to create a symbolic variable.
|
@@ -73,7 +73,7 @@ You can get an information about number of different operations used in a symbol
|
|
73
73
|
f.operations # => {"+"=>1, "-"=>2, "*"=>3, "/"=>0, "**"=>1, "-@"=>0}
|
74
74
|
|
75
75
|
|
76
|
-
== TODO
|
76
|
+
== TODO
|
77
77
|
- a lot of refactoring (code is pretty messy at this stage)
|
78
78
|
- plotting capabilities
|
79
79
|
- derivatives
|
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.3.
|
8
|
+
gem.version = '0.3.4'
|
9
9
|
gem.summary = 'Symbolic math for ruby'
|
10
10
|
gem.description = <<TEXT
|
11
11
|
Symbolic math for ruby.
|
@@ -14,13 +14,13 @@ Symbolic math for ruby.
|
|
14
14
|
This gem can help you
|
15
15
|
- if you want to get a simplified form of a big equation
|
16
16
|
- if you want to speed up similar calculations
|
17
|
-
- if you need an abstraction layer for math
|
17
|
+
- if you need an abstraction layer for math.
|
18
18
|
|
19
19
|
|
20
20
|
Symbolic doesn't have any external dependencies. It uses only pure ruby (less than 400 LOC (lines of code)).
|
21
21
|
TEXT
|
22
22
|
gem.email = "ravwar@gmail.com"
|
23
|
-
gem.homepage = "http://github.com/
|
23
|
+
gem.homepage = "http://brainopia.github.com/symbolic"
|
24
24
|
gem.authors = ["brainopia"]
|
25
25
|
gem.add_development_dependency "rspec", ">= 1.2.9"
|
26
26
|
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
data/lib/symbolic.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
require 'symbolic/coerced'
|
2
2
|
require 'symbolic/variable'
|
3
|
-
require 'symbolic/
|
4
|
-
require 'symbolic/
|
3
|
+
require 'symbolic/expression'
|
4
|
+
require 'symbolic/summands'
|
5
|
+
require 'symbolic/factors'
|
5
6
|
require 'symbolic/function'
|
6
7
|
require 'symbolic/math'
|
7
8
|
require 'symbolic/statistics'
|
@@ -17,42 +18,38 @@ module Symbolic
|
|
17
18
|
end
|
18
19
|
|
19
20
|
def -@
|
20
|
-
|
21
|
+
Factors.add self, -1
|
21
22
|
end
|
22
23
|
|
23
24
|
def +(var)
|
24
|
-
|
25
|
+
Summands.add self, var
|
25
26
|
end
|
26
27
|
|
27
28
|
def -(var)
|
28
|
-
|
29
|
+
Summands.subtract self, var
|
29
30
|
end
|
30
31
|
|
31
32
|
def *(var)
|
32
|
-
|
33
|
+
Factors.add self, var
|
33
34
|
end
|
34
35
|
|
35
36
|
def /(var)
|
36
|
-
|
37
|
+
Factors.subtract self, var
|
37
38
|
end
|
38
39
|
|
39
40
|
def **(var)
|
40
|
-
|
41
|
+
Factors.power self, var
|
41
42
|
end
|
42
43
|
|
43
44
|
def coerce(numeric)
|
44
45
|
[Coerced.new(self), numeric]
|
45
46
|
end
|
46
47
|
|
47
|
-
|
48
|
-
|
49
|
-
def factors
|
50
|
-
[1, { self => 1 }]
|
48
|
+
def inspect
|
49
|
+
"Symbolic: #{to_s}"
|
51
50
|
end
|
52
51
|
|
53
|
-
|
54
|
-
[0, { self => 1 }]
|
55
|
-
end
|
52
|
+
private
|
56
53
|
|
57
54
|
def variables_of(var)
|
58
55
|
var.variables rescue []
|
data/lib/symbolic/coerced.rb
CHANGED
@@ -1,27 +1,33 @@
|
|
1
1
|
module Symbolic
|
2
|
+
=begin
|
3
|
+
This class restores a correct order of arguments after using a coerce method.
|
4
|
+
For example, expression "1 - symbolic" calls to Symbolic#coerce
|
5
|
+
which gives us a power to reverse a receiver and parameter of method
|
6
|
+
so we set a receiver as Coerced.new(symbolic) to reverse arguments back after coercing.
|
7
|
+
=end
|
2
8
|
class Coerced
|
3
9
|
def initialize(symbolic)
|
4
10
|
@symbolic = symbolic
|
5
11
|
end
|
6
12
|
|
7
13
|
def +(numeric)
|
8
|
-
|
14
|
+
Summands.add numeric, @symbolic
|
9
15
|
end
|
10
16
|
|
11
17
|
def -(numeric)
|
12
|
-
|
18
|
+
Summands.subtract numeric, @symbolic
|
13
19
|
end
|
14
20
|
|
15
21
|
def *(numeric)
|
16
|
-
|
22
|
+
Factors.add numeric, @symbolic
|
17
23
|
end
|
18
24
|
|
19
25
|
def /(numeric)
|
20
|
-
|
26
|
+
Factors.subtract numeric, @symbolic
|
21
27
|
end
|
22
28
|
|
23
29
|
def **(numeric)
|
24
|
-
|
30
|
+
Factors.power numeric, @symbolic
|
25
31
|
end
|
26
32
|
end
|
27
33
|
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module Symbolic
|
2
|
+
class Expression
|
3
|
+
include Symbolic
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def add(var1, var2)
|
7
|
+
simplify_expression! expression = unite(convert(var1), convert(var2))
|
8
|
+
simplify(*expression) || new(*expression)
|
9
|
+
end
|
10
|
+
|
11
|
+
def subtract(var1, var2)
|
12
|
+
add var1, convert(var2).reverse
|
13
|
+
end
|
14
|
+
|
15
|
+
def unite(expr1, expr2)
|
16
|
+
numeric = unite_numeric expr1.numeric, expr2.numeric
|
17
|
+
symbolic = unite_symbolic expr1.symbolic, expr2.symbolic
|
18
|
+
[numeric, symbolic]
|
19
|
+
end
|
20
|
+
|
21
|
+
def unite_symbolic(symbolic1, symbolic2)
|
22
|
+
symbolic1.merge(symbolic2) {|base, coef1, coef2| coef1 + coef2 }
|
23
|
+
end
|
24
|
+
|
25
|
+
def unite_numeric(numeric1, numeric2)
|
26
|
+
numeric1.send operation, numeric2
|
27
|
+
end
|
28
|
+
|
29
|
+
def convert(var)
|
30
|
+
case var
|
31
|
+
when Summands then summands var
|
32
|
+
when Factors then factors var
|
33
|
+
when Numeric then numeric var
|
34
|
+
else one var; end
|
35
|
+
end
|
36
|
+
|
37
|
+
def numeric(numeric)
|
38
|
+
new numeric, {}
|
39
|
+
end
|
40
|
+
|
41
|
+
def one(symbolic)
|
42
|
+
new identity_element, symbolic => 1
|
43
|
+
end
|
44
|
+
|
45
|
+
def simple?(var)
|
46
|
+
case var
|
47
|
+
when Numeric, Variable
|
48
|
+
true
|
49
|
+
when Function, Summands
|
50
|
+
false
|
51
|
+
when Factors
|
52
|
+
var.symbolic.all? {|k,v| simple? k }
|
53
|
+
else
|
54
|
+
false
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
attr_reader :numeric, :symbolic
|
60
|
+
|
61
|
+
def initialize(numeric, symbolic)
|
62
|
+
@numeric, @symbolic = numeric, symbolic
|
63
|
+
end
|
64
|
+
|
65
|
+
def variables
|
66
|
+
@symbolic.map {|k,v| [variables_of(k), variables_of(v)] }.flatten.uniq
|
67
|
+
end
|
68
|
+
|
69
|
+
def ==(object)
|
70
|
+
(object.numeric == @numeric) and (object.symbolic == @symbolic) rescue false
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
module Symbolic
|
2
|
+
class Factors < Expression
|
3
|
+
class << self
|
4
|
+
def operation
|
5
|
+
'*'
|
6
|
+
end
|
7
|
+
|
8
|
+
def identity_element
|
9
|
+
1
|
10
|
+
end
|
11
|
+
|
12
|
+
def summands(summands)
|
13
|
+
one summands
|
14
|
+
end
|
15
|
+
|
16
|
+
def factors(factors)
|
17
|
+
factors
|
18
|
+
end
|
19
|
+
|
20
|
+
def power(base, exponent)
|
21
|
+
simplify_expression! factors = unite_exponents(base, exponent)
|
22
|
+
simplify(*factors) || new(*factors)
|
23
|
+
end
|
24
|
+
|
25
|
+
def add(var1, var2)
|
26
|
+
if distributable? var1, var2
|
27
|
+
distribute(var1, var2)
|
28
|
+
elsif distributable? var2, var1
|
29
|
+
distribute(var2, var1)
|
30
|
+
else
|
31
|
+
super
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def subtract(var1, var2)
|
36
|
+
simplify_expression! factors = unite(convert(var1), convert(var2).reverse)
|
37
|
+
simplify(*factors) || new(*factors)
|
38
|
+
end
|
39
|
+
|
40
|
+
def distributable?(var1, var2)
|
41
|
+
simple?(var1) && var2.is_a?(Summands)
|
42
|
+
end
|
43
|
+
|
44
|
+
def distribute(var1, var2)
|
45
|
+
var2.symbolic.map {|k,v| k*v }.inject(var2.numeric*var1) do |sum, it|
|
46
|
+
sum + it*var1
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def simplify_expression!(factors)
|
51
|
+
factors[1].delete_if {|base, exp| (base == identity_element) || (exp == 0) }
|
52
|
+
factors[0] = 0 if factors[1].any? {|base, exp| base == 0 }
|
53
|
+
end
|
54
|
+
|
55
|
+
def simplify(numeric, symbolic)
|
56
|
+
if numeric == 0 || symbolic.empty?
|
57
|
+
(numeric.round == numeric) ? numeric.to_i : numeric.to_f
|
58
|
+
elsif numeric == identity_element && symbolic.size == 1 && symbolic.values.first == 1
|
59
|
+
symbolic.keys.first
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def unite_exponents(base, exponent)
|
64
|
+
if base.is_a? Factors
|
65
|
+
return base.numeric**exponent, Hash[*base.symbolic.map {|base,exp| [base,exp*exponent] }.flatten]
|
66
|
+
else
|
67
|
+
[identity_element, { base => exponent }]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def reverse
|
73
|
+
self.class.new numeric**-1, Hash[*symbolic.map {|k,v| [k,-v]}.flatten]
|
74
|
+
end
|
75
|
+
|
76
|
+
def value
|
77
|
+
@symbolic.inject(numeric) {|value, (base, exp)| value * base.value ** exp.value }
|
78
|
+
end
|
79
|
+
|
80
|
+
def to_s
|
81
|
+
simplify_output
|
82
|
+
end
|
83
|
+
|
84
|
+
def coefficient_to_string(numeric)
|
85
|
+
"#{'-' if numeric < 0}#{"#{rational_to_string numeric.abs}*" if numeric.abs != 1}"
|
86
|
+
end
|
87
|
+
|
88
|
+
def exponent_to_string(base, exponent)
|
89
|
+
"#{brackets base}#{"**#{brackets exponent}" if exponent != 1}"
|
90
|
+
end
|
91
|
+
|
92
|
+
def brackets(var)
|
93
|
+
[Numeric, Symbolic::Variable].any? {|klass| var.is_a? klass } ? var : "(#{var})"
|
94
|
+
end
|
95
|
+
|
96
|
+
def simplify_output
|
97
|
+
groups = @symbolic.group_by {|b,e| e.is_a?(Numeric) && e < 0 }
|
98
|
+
reversed_factors = groups[true] ? [1, Hash[*groups[true].flatten] ] : nil
|
99
|
+
factors = groups[false] ? [@numeric, Hash[*groups[false].flatten] ] : nil
|
100
|
+
output = '' << (factors ? output(factors) : rational_to_string(@numeric))
|
101
|
+
output << "/#{reversed_output reversed_factors}" if reversed_factors
|
102
|
+
output
|
103
|
+
end
|
104
|
+
|
105
|
+
def output(factors)
|
106
|
+
coefficient_to_string(factors[0]) <<
|
107
|
+
factors[1].map {|base,exp| exponent_to_string base,exp }.join('*')
|
108
|
+
end
|
109
|
+
|
110
|
+
def reversed_output(factors)
|
111
|
+
result = output [factors[0], Hash[*factors[1].map {|b,e| [b,-e] }.flatten]]
|
112
|
+
(factors[1].length > 1) ? "(#{result})" : result
|
113
|
+
end
|
114
|
+
|
115
|
+
def rational_to_string(numeric)
|
116
|
+
((numeric.round == numeric) ? numeric.to_i : numeric.to_f).to_s
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# TODO: 2*symbolic is a 2 power of symbolic Summand
|
2
|
+
module Symbolic
|
3
|
+
class Summands < Expression
|
4
|
+
class << self
|
5
|
+
def operation
|
6
|
+
'+'
|
7
|
+
end
|
8
|
+
|
9
|
+
def identity_element
|
10
|
+
0
|
11
|
+
end
|
12
|
+
|
13
|
+
def summands(summands)
|
14
|
+
summands
|
15
|
+
end
|
16
|
+
|
17
|
+
def factors(factors)
|
18
|
+
if factors.symbolic.length == 1 && factors.symbolic.values.first == 1
|
19
|
+
new identity_element, factors.symbolic.keys.first => factors.numeric
|
20
|
+
else
|
21
|
+
new identity_element, Factors.new(1, factors.symbolic) => factors.numeric
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def simplify_expression!(summands)
|
26
|
+
summands[1].delete_if {|base, coef| coef == 0 }
|
27
|
+
end
|
28
|
+
|
29
|
+
def simplify(numeric, symbolic)
|
30
|
+
if symbolic.empty?
|
31
|
+
numeric
|
32
|
+
elsif numeric == identity_element && symbolic.size == 1
|
33
|
+
symbolic.values.first * symbolic.keys.first
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def value
|
39
|
+
symbolic.inject(numeric) {|value, (base, coef)| value + base.value * coef.value }
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_s
|
43
|
+
output = symbolic.map {|base, coef| coef_to_string(coef) + base.to_s }
|
44
|
+
output << remainder_to_string(numeric) if numeric != 0
|
45
|
+
output[0].sub!(/^\+/, '')
|
46
|
+
output.join
|
47
|
+
end
|
48
|
+
|
49
|
+
def reverse
|
50
|
+
self.class.new -numeric, Hash[*symbolic.map {|k,v| [k,-v]}.flatten]
|
51
|
+
end
|
52
|
+
|
53
|
+
def coef_to_string(coef)
|
54
|
+
"#{(coef > 0) ? '+' : '-' }#{ "#{rational_to_string(coef.abs)}*" if coef.abs != 1}"
|
55
|
+
end
|
56
|
+
|
57
|
+
def remainder_to_string(numeric)
|
58
|
+
"#{'+' if numeric > 0}#{numeric}"
|
59
|
+
end
|
60
|
+
|
61
|
+
def rational_to_string(numeric)
|
62
|
+
((numeric.round == numeric) ? numeric.to_i : numeric.to_f).to_s
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
data/lib/symbolic/variable.rb
CHANGED
@@ -1,4 +1,11 @@
|
|
1
1
|
module Symbolic
|
2
|
+
=begin
|
3
|
+
This class is used to create symbolic variables.
|
4
|
+
Symbolic variables presented by name and value.
|
5
|
+
Name is neccessary for printing meaningful symbolic expressions.
|
6
|
+
Value is neccesary for calculation of symbolic expressions.
|
7
|
+
If value isn't set for variable, but there is an associated proc, then value is taken from evaluating the proc.
|
8
|
+
=end
|
2
9
|
class Variable
|
3
10
|
include Symbolic
|
4
11
|
attr_accessor :name, :proc
|
@@ -11,7 +18,7 @@ module Symbolic
|
|
11
18
|
end
|
12
19
|
|
13
20
|
def value
|
14
|
-
@value || @proc && @proc.call
|
21
|
+
@value || @proc && @proc.call.value
|
15
22
|
end
|
16
23
|
|
17
24
|
def to_s
|
@@ -21,9 +28,5 @@ module Symbolic
|
|
21
28
|
def variables
|
22
29
|
[self]
|
23
30
|
end
|
24
|
-
|
25
|
-
def detailed_operations
|
26
|
-
Hash.new 0
|
27
|
-
end
|
28
31
|
end
|
29
32
|
end
|
data/spec/symbolic_spec.rb
CHANGED
@@ -18,15 +18,23 @@ describe "Symbolic" do
|
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
21
|
+
def self.should_evaluate_to(conditions)
|
22
|
+
conditions.each do |symbolic_expression, result|
|
23
|
+
it symbolic_expression do
|
24
|
+
expression(symbolic_expression).value.should == result
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.should_print(conditions)
|
30
|
+
conditions.each do |symbolic_expression, result|
|
31
|
+
it symbolic_expression do
|
32
|
+
expression(symbolic_expression).to_s.should == result
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
29
36
|
|
37
|
+
describe "evaluation (x=1, y=2):" do
|
30
38
|
should_evaluate_to \
|
31
39
|
'x' => 1,
|
32
40
|
'y' => 2,
|
@@ -111,14 +119,6 @@ describe "Symbolic" do
|
|
111
119
|
end
|
112
120
|
|
113
121
|
describe "to_s:" do
|
114
|
-
def self.should_print(conditions)
|
115
|
-
conditions.each do |symbolic_expression, result|
|
116
|
-
it symbolic_expression do
|
117
|
-
expression(symbolic_expression).to_s.should == result
|
118
|
-
end
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
122
|
should_print \
|
123
123
|
'x' => 'x',
|
124
124
|
'-x' => '-x',
|
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.3.
|
4
|
+
version: 0.3.4
|
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-22 00:00:00 +03:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -29,7 +29,7 @@ description: |
|
|
29
29
|
This gem can help you
|
30
30
|
- if you want to get a simplified form of a big equation
|
31
31
|
- if you want to speed up similar calculations
|
32
|
-
- if you need an abstraction layer for math
|
32
|
+
- if you need an abstraction layer for math.
|
33
33
|
|
34
34
|
|
35
35
|
Symbolic doesn't have any external dependencies. It uses only pure ruby (less than 400 LOC (lines of code)).
|
@@ -47,21 +47,22 @@ files:
|
|
47
47
|
- Rakefile
|
48
48
|
- lib/symbolic.rb
|
49
49
|
- lib/symbolic/coerced.rb
|
50
|
+
- lib/symbolic/expression.rb
|
50
51
|
- lib/symbolic/extensions/kernel.rb
|
51
52
|
- lib/symbolic/extensions/matrix.rb
|
52
53
|
- lib/symbolic/extensions/numeric.rb
|
53
54
|
- lib/symbolic/extensions/rational.rb
|
54
|
-
- lib/symbolic/
|
55
|
+
- lib/symbolic/factors.rb
|
55
56
|
- lib/symbolic/function.rb
|
56
57
|
- lib/symbolic/math.rb
|
57
58
|
- lib/symbolic/statistics.rb
|
58
|
-
- lib/symbolic/
|
59
|
+
- lib/symbolic/summands.rb
|
59
60
|
- lib/symbolic/variable.rb
|
60
61
|
- spec/spec.opts
|
61
62
|
- spec/spec_helper.rb
|
62
63
|
- spec/symbolic_spec.rb
|
63
64
|
has_rdoc: true
|
64
|
-
homepage: http://github.com/
|
65
|
+
homepage: http://brainopia.github.com/symbolic
|
65
66
|
licenses: []
|
66
67
|
|
67
68
|
post_install_message:
|
data/lib/symbolic/factor.rb
DELETED
@@ -1,162 +0,0 @@
|
|
1
|
-
module Symbolic
|
2
|
-
class Factor
|
3
|
-
include Symbolic
|
4
|
-
|
5
|
-
class << self
|
6
|
-
def exponent(base, exponent)
|
7
|
-
simplify_exponents! factors = unite_exponents(base, exponent)
|
8
|
-
simplify(*factors) || new(factors)
|
9
|
-
end
|
10
|
-
|
11
|
-
def multiply(var1, var2)
|
12
|
-
return distribute(var1, var2) if distributable? var1, var2
|
13
|
-
return distribute(var2, var1) if distributable? var2, var1
|
14
|
-
|
15
|
-
simplify_exponents! factors = unite(factors(var1), factors(var2))
|
16
|
-
simplify(*factors) || new(factors)
|
17
|
-
end
|
18
|
-
|
19
|
-
def divide(var1, var2)
|
20
|
-
simplify_exponents! factors = unite(factors(var1), reverse(var2))
|
21
|
-
simplify(*factors) || new(factors)
|
22
|
-
end
|
23
|
-
|
24
|
-
def distributable?(var1, var2)
|
25
|
-
simple?(var1) && var2.is_a?(Summand)
|
26
|
-
end
|
27
|
-
|
28
|
-
def distribute(var1, var2)
|
29
|
-
numeric, symbolic = var2.send(:summands)
|
30
|
-
symbolic.map {|k,v| k*v }.inject(numeric*var1) do |sum, it|
|
31
|
-
sum + it*var1
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
def simplify_exponents!(factors)
|
36
|
-
factors[1].delete_if {|base, exp| (base == 1) || (exp == 0) }
|
37
|
-
factors[0] = 0 if factors[1].any? {|base, exp| base == 0 }
|
38
|
-
end
|
39
|
-
|
40
|
-
def simplify(numeric, symbolic)
|
41
|
-
if numeric == 0 || symbolic.empty?
|
42
|
-
(numeric.round == numeric) ? numeric.to_i : numeric.to_f
|
43
|
-
elsif numeric == 1 && symbolic.size == 1 && symbolic.values.first == 1
|
44
|
-
symbolic.keys.first
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
def reverse(var)
|
49
|
-
factors(var).dup.tap do |it|
|
50
|
-
it[0] = it[0]**-1
|
51
|
-
it[1] = Hash[*it[1].map {|b,e| [b,-e] }.flatten]
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
def factors(var)
|
56
|
-
var.is_a?(Symbolic) ? var.send(:factors) : [var, {}]
|
57
|
-
end
|
58
|
-
|
59
|
-
def unite(factors1, factors2)
|
60
|
-
numeric1, symbolic1 = factors1
|
61
|
-
numeric2, symbolic2 = factors2
|
62
|
-
|
63
|
-
numeric = numeric1 * numeric2
|
64
|
-
symbolic = symbolic1.merge(symbolic2) {|base, exp1, exp2| exp1 + exp2 }
|
65
|
-
[numeric, symbolic]
|
66
|
-
end
|
67
|
-
|
68
|
-
def unite_exponents(base, exponent)
|
69
|
-
if base.is_a? Factor
|
70
|
-
numeric, symbolic = factors(base)
|
71
|
-
return numeric**exponent, Hash[*symbolic.map {|base,exp| [base,exp*exponent] }.flatten]
|
72
|
-
else
|
73
|
-
[1, { base => exponent }]
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
def simple?(var)
|
78
|
-
case var
|
79
|
-
when Numeric
|
80
|
-
true
|
81
|
-
when Variable
|
82
|
-
true
|
83
|
-
when Function
|
84
|
-
false
|
85
|
-
when Summand
|
86
|
-
false
|
87
|
-
when Factor
|
88
|
-
var.send(:factors)[1].all? {|k,v| simple? k }
|
89
|
-
else
|
90
|
-
false
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
def initialize(factors)
|
96
|
-
@factors = factors
|
97
|
-
end
|
98
|
-
|
99
|
-
def value
|
100
|
-
@factors[1].inject(@factors[0]) {|value, (base, exp)| value * base.value ** exp.value }
|
101
|
-
end
|
102
|
-
|
103
|
-
def variables
|
104
|
-
@factors[1].map {|k,v| [variables_of(k), variables_of(v)] }.flatten.uniq
|
105
|
-
end
|
106
|
-
|
107
|
-
def to_s
|
108
|
-
simplify_output
|
109
|
-
end
|
110
|
-
|
111
|
-
def ==(object)
|
112
|
-
object.send(:factors) == @factors rescue false
|
113
|
-
end
|
114
|
-
|
115
|
-
private
|
116
|
-
|
117
|
-
attr_reader :factors
|
118
|
-
|
119
|
-
def summands
|
120
|
-
if @factors[0] == 1
|
121
|
-
super
|
122
|
-
else
|
123
|
-
[0, {(self/@factors[0]) => @factors[0]}]
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
def coefficient_to_string(numeric)
|
128
|
-
"#{'-' if numeric < 0}#{"#{rational_to_string numeric.abs}*" if numeric.abs != 1}"
|
129
|
-
end
|
130
|
-
|
131
|
-
def exponent_to_string(base, exponent)
|
132
|
-
"#{brackets base}#{"**#{brackets exponent}" if exponent != 1}"
|
133
|
-
end
|
134
|
-
|
135
|
-
def brackets(var)
|
136
|
-
[Numeric, Symbolic::Variable].any? {|klass| var.is_a? klass } ? var : "(#{var})"
|
137
|
-
end
|
138
|
-
|
139
|
-
def simplify_output
|
140
|
-
groups = @factors[1].group_by {|b,e| e.is_a?(Numeric) && e < 0 }
|
141
|
-
reversed_factors = groups[true] ? [1, Hash[*groups[true].flatten] ] : nil
|
142
|
-
factors = groups[false] ? [@factors[0], Hash[*groups[false].flatten] ] : nil
|
143
|
-
output = '' << (factors ? output(factors) : rational_to_string(@factors[0]))
|
144
|
-
output << "/#{reversed_output reversed_factors}" if reversed_factors
|
145
|
-
output
|
146
|
-
end
|
147
|
-
|
148
|
-
def output(factors)
|
149
|
-
coefficient_to_string(factors[0]) <<
|
150
|
-
factors[1].map {|base,exp| exponent_to_string base,exp }.join('*')
|
151
|
-
end
|
152
|
-
|
153
|
-
def reversed_output(factors)
|
154
|
-
result = output [factors[0], Hash[*factors[1].map {|b,e| [b,-e] }.flatten]]
|
155
|
-
(factors[1].length > 1) ? "(#{result})" : result
|
156
|
-
end
|
157
|
-
|
158
|
-
def rational_to_string(numeric)
|
159
|
-
((numeric.round == numeric) ? numeric.to_i : numeric.to_f).to_s
|
160
|
-
end
|
161
|
-
end
|
162
|
-
end
|
data/lib/symbolic/summand.rb
DELETED
@@ -1,86 +0,0 @@
|
|
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
|
-
[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] if output.first[0] == '+'
|
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
|