symbolic 0.3.3 → 0.3.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|