symbolic 0.3.3 → 0.3.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -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.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/brainopia/symbolic"
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
@@ -1,7 +1,8 @@
1
1
  require 'symbolic/coerced'
2
2
  require 'symbolic/variable'
3
- require 'symbolic/summand'
4
- require 'symbolic/factor'
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
- Factor.multiply self, -1
21
+ Factors.add self, -1
21
22
  end
22
23
 
23
24
  def +(var)
24
- Summand.add self, var
25
+ Summands.add self, var
25
26
  end
26
27
 
27
28
  def -(var)
28
- Summand.subtract self, var
29
+ Summands.subtract self, var
29
30
  end
30
31
 
31
32
  def *(var)
32
- Factor.multiply self, var
33
+ Factors.add self, var
33
34
  end
34
35
 
35
36
  def /(var)
36
- Factor.divide self, var
37
+ Factors.subtract self, var
37
38
  end
38
39
 
39
40
  def **(var)
40
- Factor.exponent self, var
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
- private
48
-
49
- def factors
50
- [1, { self => 1 }]
48
+ def inspect
49
+ "Symbolic: #{to_s}"
51
50
  end
52
51
 
53
- def summands
54
- [0, { self => 1 }]
55
- end
52
+ private
56
53
 
57
54
  def variables_of(var)
58
55
  var.variables rescue []
@@ -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
- Summand.add numeric, @symbolic
14
+ Summands.add numeric, @symbolic
9
15
  end
10
16
 
11
17
  def -(numeric)
12
- Summand.subtract numeric, @symbolic
18
+ Summands.subtract numeric, @symbolic
13
19
  end
14
20
 
15
21
  def *(numeric)
16
- Factor.multiply numeric, @symbolic
22
+ Factors.add numeric, @symbolic
17
23
  end
18
24
 
19
25
  def /(numeric)
20
- Factor.divide numeric, @symbolic
26
+ Factors.subtract numeric, @symbolic
21
27
  end
22
28
 
23
29
  def **(numeric)
24
- Factor.exponent numeric, @symbolic
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
@@ -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
@@ -18,15 +18,23 @@ describe "Symbolic" do
18
18
  end
19
19
  end
20
20
 
21
- describe "evaluation (x=1, y=2):" do
22
- def self.should_evaluate_to(conditions)
23
- conditions.each do |symbolic_expression, result|
24
- it symbolic_expression do
25
- expression(symbolic_expression).value.should == result
26
- end
27
- end
28
- end
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.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-16 00:00:00 +03:00
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/factor.rb
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/summand.rb
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/brainopia/symbolic
65
+ homepage: http://brainopia.github.com/symbolic
65
66
  licenses: []
66
67
 
67
68
  post_install_message:
@@ -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
@@ -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