symath 0.1.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.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +21 -0
- data/README.md +616 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/symath/definition/abs.rb +48 -0
- data/lib/symath/definition/arccos.rb +25 -0
- data/lib/symath/definition/arccot.rb +23 -0
- data/lib/symath/definition/arccsc.rb +24 -0
- data/lib/symath/definition/arcsec.rb +24 -0
- data/lib/symath/definition/arcsin.rb +25 -0
- data/lib/symath/definition/arctan.rb +23 -0
- data/lib/symath/definition/bounds.rb +39 -0
- data/lib/symath/definition/codiff.rb +31 -0
- data/lib/symath/definition/constant.rb +111 -0
- data/lib/symath/definition/cos.rb +17 -0
- data/lib/symath/definition/cot.rb +17 -0
- data/lib/symath/definition/csc.rb +17 -0
- data/lib/symath/definition/curl.rb +27 -0
- data/lib/symath/definition/d.rb +62 -0
- data/lib/symath/definition/div.rb +27 -0
- data/lib/symath/definition/exp.rb +112 -0
- data/lib/symath/definition/fact.rb +55 -0
- data/lib/symath/definition/flat.rb +31 -0
- data/lib/symath/definition/function.rb +197 -0
- data/lib/symath/definition/grad.rb +23 -0
- data/lib/symath/definition/hodge.rb +23 -0
- data/lib/symath/definition/int.rb +75 -0
- data/lib/symath/definition/laplacian.rb +23 -0
- data/lib/symath/definition/lmd.rb +97 -0
- data/lib/symath/definition/ln.rb +45 -0
- data/lib/symath/definition/number.rb +51 -0
- data/lib/symath/definition/operator.rb +228 -0
- data/lib/symath/definition/sec.rb +17 -0
- data/lib/symath/definition/sharp.rb +31 -0
- data/lib/symath/definition/sin.rb +17 -0
- data/lib/symath/definition/sqrt.rb +62 -0
- data/lib/symath/definition/tan.rb +17 -0
- data/lib/symath/definition/trig.rb +95 -0
- data/lib/symath/definition/variable.rb +284 -0
- data/lib/symath/definition/xd.rb +28 -0
- data/lib/symath/definition.rb +205 -0
- data/lib/symath/equation.rb +67 -0
- data/lib/symath/fraction.rb +177 -0
- data/lib/symath/matrix.rb +252 -0
- data/lib/symath/minus.rb +125 -0
- data/lib/symath/operation/differential.rb +167 -0
- data/lib/symath/operation/distributivelaw.rb +367 -0
- data/lib/symath/operation/exterior.rb +64 -0
- data/lib/symath/operation/integration.rb +329 -0
- data/lib/symath/operation/match.rb +166 -0
- data/lib/symath/operation/normalization.rb +458 -0
- data/lib/symath/operation.rb +36 -0
- data/lib/symath/operator.rb +163 -0
- data/lib/symath/parser.rb +473 -0
- data/lib/symath/parser.y +129 -0
- data/lib/symath/poly/dup.rb +835 -0
- data/lib/symath/poly/galois.rb +621 -0
- data/lib/symath/poly.rb +142 -0
- data/lib/symath/power.rb +224 -0
- data/lib/symath/product.rb +183 -0
- data/lib/symath/sum.rb +174 -0
- data/lib/symath/type.rb +282 -0
- data/lib/symath/value.rb +372 -0
- data/lib/symath/version.rb +3 -0
- data/lib/symath/wedge.rb +48 -0
- data/lib/symath.rb +157 -0
- data/symath.gemspec +39 -0
- metadata +160 -0
data/lib/symath/poly.rb
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
module SyMath
|
2
|
+
# Abstract base class for polynomial classes
|
3
|
+
class Poly
|
4
|
+
attr_reader :arr, :p, :var
|
5
|
+
|
6
|
+
# Transform polynomial back to expression form
|
7
|
+
def to_m()
|
8
|
+
if zero?
|
9
|
+
return 0.to_m
|
10
|
+
end
|
11
|
+
|
12
|
+
exp = 0.to_m
|
13
|
+
d = degree
|
14
|
+
|
15
|
+
(0..d).each do |i|
|
16
|
+
next if @arr[i] == 0
|
17
|
+
|
18
|
+
if d - i == 1
|
19
|
+
a = @var
|
20
|
+
elsif d - i == 0
|
21
|
+
a = 1.to_m
|
22
|
+
else
|
23
|
+
a = @var**(d - i)
|
24
|
+
end
|
25
|
+
|
26
|
+
exp += @arr[i].to_m*a
|
27
|
+
end
|
28
|
+
|
29
|
+
return exp
|
30
|
+
end
|
31
|
+
|
32
|
+
def strip!()
|
33
|
+
i = @arr.index { |e| e != 0 }
|
34
|
+
if i.nil?
|
35
|
+
@arr = []
|
36
|
+
else
|
37
|
+
@arr = @arr[i..-1]
|
38
|
+
end
|
39
|
+
|
40
|
+
return self
|
41
|
+
end
|
42
|
+
|
43
|
+
def zero?()
|
44
|
+
return @arr.empty?
|
45
|
+
end
|
46
|
+
|
47
|
+
# Return the degree of the highest power of the polynomial.
|
48
|
+
def degree()
|
49
|
+
# Hack: Degree of 0 should really be negative infinity
|
50
|
+
return -1 if zero?
|
51
|
+
return @arr.size - 1
|
52
|
+
end
|
53
|
+
|
54
|
+
def sort_factors(list)
|
55
|
+
return list.sort do |a, b|
|
56
|
+
[a.degree, a.arr] <=> [b.degree, b.arr]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def sort_factors_multiple(list)
|
61
|
+
return list.sort do |a, b|
|
62
|
+
[a[0].degree, a[1], a[0].arr] <=> [b[0].degree, b[1], b[0].arr]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def +(other)
|
67
|
+
if other.is_a?(self.class)
|
68
|
+
return add(other)
|
69
|
+
elsif other.is_a?(Integer)
|
70
|
+
return add_ground(other)
|
71
|
+
else
|
72
|
+
raise 'Cannot add ' + other.to_s
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def -(other)
|
77
|
+
if other.is_a?(self.class)
|
78
|
+
return sub(other)
|
79
|
+
elsif other.is_a?(Integer)
|
80
|
+
return sub_ground(other)
|
81
|
+
else
|
82
|
+
raise 'Cannot subtract ' + other.to_s
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def -@()
|
87
|
+
return neg
|
88
|
+
end
|
89
|
+
|
90
|
+
def *(other)
|
91
|
+
if other.is_a?(self.class)
|
92
|
+
return mul(other)
|
93
|
+
elsif other.is_a?(Integer)
|
94
|
+
return mul_ground(other)
|
95
|
+
else
|
96
|
+
raise 'Cannot multiply with ' + other.to_s
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def /(other)
|
101
|
+
if other.is_a?(self.class)
|
102
|
+
return quo(other)
|
103
|
+
elsif other.is_a?(Integer)
|
104
|
+
return quo_ground(other)
|
105
|
+
else
|
106
|
+
raise 'Cannot divide by ' + other.to_s
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def %(other)
|
111
|
+
if other.is_a?(self.class)
|
112
|
+
return rem(other)
|
113
|
+
elsif other.is_a?(Integer)
|
114
|
+
return trunc(other)
|
115
|
+
else
|
116
|
+
raise 'Cannot divide modulo ' + other.to_s
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def **(other)
|
121
|
+
if other.is_a?(Integer) and other == 2
|
122
|
+
return sqr
|
123
|
+
else
|
124
|
+
raise 'Cannot raise to the power of ' + other.to_s
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def [](i)
|
129
|
+
return arr[i]
|
130
|
+
end
|
131
|
+
|
132
|
+
def ==(other)
|
133
|
+
return (self.class == other.class and @arr == other.arr)
|
134
|
+
end
|
135
|
+
|
136
|
+
# Returns leading coefficient (i.e coefficient of the biggest power)
|
137
|
+
def lc()
|
138
|
+
return 0 if zero?
|
139
|
+
return @arr[0]
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
data/lib/symath/power.rb
ADDED
@@ -0,0 +1,224 @@
|
|
1
|
+
require 'symath/operator'
|
2
|
+
|
3
|
+
module SyMath
|
4
|
+
class Power < Operator
|
5
|
+
def self.compose_with_simplify(a, b)
|
6
|
+
a = a.to_m
|
7
|
+
b = b.to_m
|
8
|
+
|
9
|
+
if a.is_finite?() == false or b.is_finite?() == false
|
10
|
+
return self.simplify_inf(a, b)
|
11
|
+
end
|
12
|
+
|
13
|
+
# 0**0 = NaN
|
14
|
+
if a.is_zero? and b.is_zero?
|
15
|
+
return :nan.to_m
|
16
|
+
end
|
17
|
+
|
18
|
+
# n**1 = n
|
19
|
+
if b == 1
|
20
|
+
return a
|
21
|
+
end
|
22
|
+
|
23
|
+
if a.is_a?(SyMath::Power)
|
24
|
+
return a.base**(a.exponent*b)
|
25
|
+
end
|
26
|
+
|
27
|
+
return self.new(a, b)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.simplify_inf(a, b)
|
31
|
+
# Indefinite factors
|
32
|
+
if a.is_finite?.nil? or b.is_finite?.nil?
|
33
|
+
return self.new(a, b)
|
34
|
+
end
|
35
|
+
|
36
|
+
# NaN**(..) = NaN, (..)**NaN = NaN
|
37
|
+
if a.is_nan? or b.is_nan?
|
38
|
+
return :nan.to_m
|
39
|
+
end
|
40
|
+
|
41
|
+
# 1**oo = 1**-oo = oo**0 = -oo**0 = NaN
|
42
|
+
if a == 1 or b.is_zero?
|
43
|
+
return :nan.to_m
|
44
|
+
end
|
45
|
+
|
46
|
+
if SyMath.setting(:complex_arithmetic)
|
47
|
+
if b.is_finite? == false
|
48
|
+
return :nan.to_m
|
49
|
+
else
|
50
|
+
return :oo.to_m
|
51
|
+
end
|
52
|
+
else
|
53
|
+
if a.is_zero? and b.is_finite? == false
|
54
|
+
return :nan.to_m
|
55
|
+
end
|
56
|
+
|
57
|
+
# n**-oo = oo**-oo = -oo**-oo = 0
|
58
|
+
if b.is_finite? == false and b.is_negative?
|
59
|
+
return 0.to_m
|
60
|
+
end
|
61
|
+
|
62
|
+
if a.is_finite? == false and a.is_negative?
|
63
|
+
if b.is_finite? == true
|
64
|
+
# -oo*n = oo*(-1**n)
|
65
|
+
return :oo.to_m.mul(a.sign**b)
|
66
|
+
else
|
67
|
+
# -oo**oo = NaN
|
68
|
+
return :nan.to_m
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# -n**oo => NaN
|
73
|
+
if a.is_finite? and a.is_negative?
|
74
|
+
return :nan.to_m
|
75
|
+
end
|
76
|
+
|
77
|
+
# The only remaining possibilities:
|
78
|
+
# oo**n = n*oo = oo*oo = oo
|
79
|
+
return :oo.to_m
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def initialize(base, exponent)
|
84
|
+
super('**', [base, exponent])
|
85
|
+
end
|
86
|
+
|
87
|
+
def base()
|
88
|
+
return @args[0]
|
89
|
+
end
|
90
|
+
|
91
|
+
def exponent()
|
92
|
+
return @args[1]
|
93
|
+
end
|
94
|
+
|
95
|
+
# Expression is on the form a**-n, n is a positive number
|
96
|
+
def is_divisor_factor?()
|
97
|
+
return exponent.is_negative_number?
|
98
|
+
end
|
99
|
+
|
100
|
+
# Simple reduction rules, allows sign to change. Returns
|
101
|
+
# (reduced exp, sign, changed).
|
102
|
+
def reduce_modulo_sign
|
103
|
+
# a to the power of 1 reduces to a
|
104
|
+
if exponent == 1
|
105
|
+
return base, 1, true
|
106
|
+
end
|
107
|
+
|
108
|
+
# Powers of 1 reduces to 1
|
109
|
+
if base == 1 and exponent.is_finite?
|
110
|
+
return base, 1, true
|
111
|
+
end
|
112
|
+
|
113
|
+
# Power of 0 reduces to 0
|
114
|
+
if base == 0 and exponent.is_finite? and exponent != 0
|
115
|
+
return 0.to_m, 1, true
|
116
|
+
end
|
117
|
+
|
118
|
+
if base != 0 and exponent == 0
|
119
|
+
return 1.to_m, 1, true
|
120
|
+
end
|
121
|
+
|
122
|
+
if base == :e
|
123
|
+
fn = fn(:exp, exponent)
|
124
|
+
# FIXME: Merge functions reduce and reduce_modulo_sign
|
125
|
+
red = fn.reduce
|
126
|
+
if red != fn
|
127
|
+
return red, 1, true
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# Reduce negative number
|
132
|
+
if base.is_a?(SyMath::Minus)
|
133
|
+
if exponent.is_number?
|
134
|
+
exp = exponent
|
135
|
+
elsif exponent.is_negative_number?
|
136
|
+
exp = exponent.argument
|
137
|
+
else
|
138
|
+
exp = nil
|
139
|
+
end
|
140
|
+
|
141
|
+
if !exp.nil?
|
142
|
+
e, sign, changed = (base.argument**exp).reduce_modulo_sign
|
143
|
+
if exp.value.odd?
|
144
|
+
sign *= -1
|
145
|
+
end
|
146
|
+
return e, sign, true
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# Number power of number reduces to number
|
151
|
+
if base.is_number?
|
152
|
+
if exponent.is_number?
|
153
|
+
return (base.value ** exponent.value).to_m, 1, true
|
154
|
+
end
|
155
|
+
|
156
|
+
if exponent.is_negative_number? and exponent.argument.value > 1
|
157
|
+
return (base.value ** exponent.argument.value).to_m.power(-1), 1, true
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# p**q**r reduces to p**(q*r)
|
162
|
+
if base.is_a?(SyMath::Power)
|
163
|
+
return base.base.power(base.exponent.mul(exponent)), 1, true
|
164
|
+
end
|
165
|
+
|
166
|
+
# Reduce positive integer power of vectors and dforms to zero
|
167
|
+
if (base.type.is_dform? or base.type.is_vector?) and
|
168
|
+
exponent.is_number?
|
169
|
+
return 0.to_m, 1, true
|
170
|
+
end
|
171
|
+
|
172
|
+
# Remaining code reduces only quaternions
|
173
|
+
if !base.is_unit_quaternion?
|
174
|
+
return self, 1, false
|
175
|
+
end
|
176
|
+
|
177
|
+
# q**n for some unit quaternion
|
178
|
+
# Exponent is 1 or not a number
|
179
|
+
if !exponent.is_number? or exponent == 1
|
180
|
+
return self, 1, false
|
181
|
+
end
|
182
|
+
|
183
|
+
# e is on the form q**n for some integer n >= 2
|
184
|
+
x = exponent.value
|
185
|
+
|
186
|
+
if x.odd?
|
187
|
+
ret = base
|
188
|
+
x -= 1
|
189
|
+
else
|
190
|
+
ret = 1.to_m
|
191
|
+
end
|
192
|
+
|
193
|
+
if (x/2).odd?
|
194
|
+
return ret, -1, true
|
195
|
+
else
|
196
|
+
return ret, 1, true
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
def to_s()
|
201
|
+
if base.is_sum_exp? or base.is_prod_exp? or base.is_a?(SyMath::Power)
|
202
|
+
base_str = '(' + base.to_s + ')'
|
203
|
+
else
|
204
|
+
base_str = base.to_s
|
205
|
+
end
|
206
|
+
|
207
|
+
expo_str = (exponent.is_sum_exp? or exponent.is_prod_exp?) ?
|
208
|
+
'(' + exponent.to_s + ')' :
|
209
|
+
exponent.to_s
|
210
|
+
|
211
|
+
return base_str + '**' + expo_str
|
212
|
+
end
|
213
|
+
|
214
|
+
def to_latex()
|
215
|
+
if base.is_sum_exp? or base.is_prod_exp? or base.is_a?(SyMath::Power)
|
216
|
+
base_str = '\left(' + base.to_latex + '\right)'
|
217
|
+
else
|
218
|
+
base_str = base.to_latex
|
219
|
+
end
|
220
|
+
|
221
|
+
return base_str + '^' + '{' + exponent.to_latex + '}'
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
@@ -0,0 +1,183 @@
|
|
1
|
+
require 'symath/operator'
|
2
|
+
|
3
|
+
module SyMath
|
4
|
+
class Product < Operator
|
5
|
+
def self.compose_with_simplify(a, b)
|
6
|
+
a = a.to_m
|
7
|
+
b = b.to_m
|
8
|
+
|
9
|
+
# Multipling a value with an equation multiplies it with both sides,
|
10
|
+
# preserving the balance of the equation
|
11
|
+
if b.is_a?(SyMath::Equation)
|
12
|
+
return eq(a * b.args[0], a * b.args[1])
|
13
|
+
end
|
14
|
+
|
15
|
+
if a.is_finite?() == false or b.is_finite?() == false
|
16
|
+
return self.simplify_inf(a, b)
|
17
|
+
end
|
18
|
+
|
19
|
+
# First try some simple reductions
|
20
|
+
# a*1 => a
|
21
|
+
return a if b == 1
|
22
|
+
return b if a == 1
|
23
|
+
|
24
|
+
# -a*-b => a*b
|
25
|
+
if b.is_a?(SyMath::Minus) and a.is_a?(SyMath::Minus)
|
26
|
+
return a.argument*b.argument
|
27
|
+
end
|
28
|
+
|
29
|
+
# (-a)*b => -(a*b)
|
30
|
+
# a*(-b) => -(a*b)
|
31
|
+
return -(a*b.argument) if b.is_a?(SyMath::Minus)
|
32
|
+
return -(a.argument*b) if a.is_a?(SyMath::Minus)
|
33
|
+
|
34
|
+
if b.is_a?(SyMath::Matrix)
|
35
|
+
return self.new(a, b)
|
36
|
+
end
|
37
|
+
|
38
|
+
if a.base == b.base
|
39
|
+
return a.base ** (a.exponent + b.exponent)
|
40
|
+
end
|
41
|
+
|
42
|
+
# (1/a)*b => b/a
|
43
|
+
if a.is_a?(SyMath::Fraction) and a.dividend == 1.to_m
|
44
|
+
return b/a.divisor
|
45
|
+
end
|
46
|
+
|
47
|
+
# a*(1/b) => a*/b
|
48
|
+
if b.is_a?(SyMath::Fraction) and b.dividend == 1.to_m
|
49
|
+
return a/b.divisor
|
50
|
+
end
|
51
|
+
|
52
|
+
if a.type.is_subtype?(:tensor) and b.type.is_subtype?(:tensor)
|
53
|
+
# Expand expression if any of the parts are sum
|
54
|
+
if b.is_sum_exp?
|
55
|
+
return b.terms.map { |f| a.*(f) }.inject(:+)
|
56
|
+
end
|
57
|
+
|
58
|
+
if a.is_sum_exp?
|
59
|
+
return a.terms.map { |f| f.wedge(b) }.inject(:+)
|
60
|
+
end
|
61
|
+
|
62
|
+
return a.wedge(b)
|
63
|
+
end
|
64
|
+
|
65
|
+
return self.new(a, b)
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.simplify_inf(a, b)
|
69
|
+
# Indefinite factors
|
70
|
+
if a.is_finite?.nil? or b.is_finite?.nil?
|
71
|
+
return self.new(a, b)
|
72
|
+
end
|
73
|
+
|
74
|
+
# NaN multiplies to NaN
|
75
|
+
if a.is_nan? or b.is_nan?
|
76
|
+
return :nan.to_m
|
77
|
+
end
|
78
|
+
|
79
|
+
# oo*0 = 0*oo = NaN
|
80
|
+
if a.is_zero? or b.is_zero?
|
81
|
+
return :nan.to_m
|
82
|
+
end
|
83
|
+
|
84
|
+
if SyMath.setting(:complex_arithmetic)
|
85
|
+
return :oo.to_m
|
86
|
+
else
|
87
|
+
if (a.is_positive? and b.is_positive?) or
|
88
|
+
(a.is_negative? and b.is_negative?)
|
89
|
+
return :oo.to_m
|
90
|
+
end
|
91
|
+
|
92
|
+
if (a.is_negative? and b.is_positive?) or
|
93
|
+
(a.is_positive? and b.is_negative?)
|
94
|
+
return -:oo.to_m
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# :nocov:
|
99
|
+
raise 'Internal error'
|
100
|
+
# :nocov:
|
101
|
+
end
|
102
|
+
|
103
|
+
def initialize(arg1, arg2)
|
104
|
+
super('*', [arg1, arg2])
|
105
|
+
end
|
106
|
+
|
107
|
+
def factor1()
|
108
|
+
return @args[0]
|
109
|
+
end
|
110
|
+
|
111
|
+
def factor1=(f)
|
112
|
+
@args[0] = f
|
113
|
+
end
|
114
|
+
|
115
|
+
def factor2()
|
116
|
+
return @args[1]
|
117
|
+
end
|
118
|
+
|
119
|
+
def factor2=(f)
|
120
|
+
@args[1] = f
|
121
|
+
end
|
122
|
+
|
123
|
+
def is_commutative?()
|
124
|
+
return true
|
125
|
+
end
|
126
|
+
|
127
|
+
def is_associative?()
|
128
|
+
return true
|
129
|
+
end
|
130
|
+
|
131
|
+
def is_prod_exp?()
|
132
|
+
return true
|
133
|
+
end
|
134
|
+
|
135
|
+
def factors()
|
136
|
+
return Enumerator.new do |f|
|
137
|
+
factor1.factors.each { |f1| f << f1 }
|
138
|
+
factor2.factors.each { |f2| f << f2 }
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def evaluate()
|
143
|
+
if factor1.is_a?(SyMath::Matrix)
|
144
|
+
return factor1.matrix_mul(factor2)
|
145
|
+
elsif factor2.is_a?(SyMath::Matrix) and
|
146
|
+
factor1.type.is_scalar?
|
147
|
+
return factor2.matrix_mul(factor1)
|
148
|
+
end
|
149
|
+
|
150
|
+
return super
|
151
|
+
end
|
152
|
+
|
153
|
+
def type()
|
154
|
+
return factor1.type.product(factor2.type)
|
155
|
+
end
|
156
|
+
|
157
|
+
def to_s()
|
158
|
+
if SyMath.setting(:expl_parentheses)
|
159
|
+
return '('.to_s + factor1.to_s + '*' + factor2.to_s + ')'.to_s
|
160
|
+
else
|
161
|
+
return @args.map do |a|
|
162
|
+
if a.is_sum_exp?
|
163
|
+
'(' + a.to_s + ')'
|
164
|
+
else
|
165
|
+
a.to_s
|
166
|
+
end
|
167
|
+
end.join('*')
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def to_latex()
|
172
|
+
dot = SyMath.setting(:ltx_product_sign) ? ' \cdot ' : ' ';
|
173
|
+
|
174
|
+
return @args.map do |a|
|
175
|
+
if a.is_sum_exp?
|
176
|
+
'(' + a.to_latex + ')'
|
177
|
+
else
|
178
|
+
a.to_latex
|
179
|
+
end
|
180
|
+
end.join(dot)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
data/lib/symath/sum.rb
ADDED
@@ -0,0 +1,174 @@
|
|
1
|
+
require 'symath/operator'
|
2
|
+
|
3
|
+
module SyMath
|
4
|
+
class Sum < Operator
|
5
|
+
def self.compose_with_simplify(a, b)
|
6
|
+
a = a.to_m
|
7
|
+
b = b.to_m
|
8
|
+
|
9
|
+
# Adding a value to an equation adds it to both sides, preserving
|
10
|
+
# the balance of the equation
|
11
|
+
if b.is_a?(SyMath::Equation)
|
12
|
+
return eq(a + b.args[0], a + b.args[1])
|
13
|
+
end
|
14
|
+
|
15
|
+
if a.is_finite?() == false or b.is_finite?() == false
|
16
|
+
return self.simplify_inf(a, b)
|
17
|
+
end
|
18
|
+
|
19
|
+
return a if b == 0
|
20
|
+
return b if a == 0
|
21
|
+
|
22
|
+
sc = 1
|
23
|
+
sf = []
|
24
|
+
oc = 1
|
25
|
+
of = []
|
26
|
+
|
27
|
+
a.factors.each do |f|
|
28
|
+
if f == -1
|
29
|
+
sc *= -1
|
30
|
+
elsif f.is_number?
|
31
|
+
sc *= f.value
|
32
|
+
else
|
33
|
+
sf.push f
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
b.factors.each do |f|
|
38
|
+
if f == -1
|
39
|
+
oc *= -1
|
40
|
+
elsif f.is_number?
|
41
|
+
oc *= f.value
|
42
|
+
else
|
43
|
+
of.push f
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
sc += oc
|
48
|
+
|
49
|
+
if sf == of
|
50
|
+
if sc == 0
|
51
|
+
return 0.to_m
|
52
|
+
end
|
53
|
+
|
54
|
+
if sc != 1
|
55
|
+
sf.unshift sc.to_m
|
56
|
+
end
|
57
|
+
|
58
|
+
return sf.empty? ? 1.to_m : sf.inject(:*)
|
59
|
+
end
|
60
|
+
|
61
|
+
return self.new(a, b)
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.simplify_inf(a, b)
|
65
|
+
# Indefinite terms
|
66
|
+
if a.is_finite?.nil? or b.is_finite?.nil?
|
67
|
+
return a.add(b)
|
68
|
+
end
|
69
|
+
|
70
|
+
# NaN add to NaN
|
71
|
+
if a.is_nan? or b.is_nan?
|
72
|
+
return :nan.to_m
|
73
|
+
end
|
74
|
+
|
75
|
+
if SyMath.setting(:complex_arithmetic)
|
76
|
+
# +- oo +- oo = NaN
|
77
|
+
if (a.is_finite? == false and b.is_finite? == false)
|
78
|
+
return :nan.to_m
|
79
|
+
end
|
80
|
+
|
81
|
+
# oo + n = n + oo = NaN
|
82
|
+
if (a.is_finite? == false or b.is_finite? == false)
|
83
|
+
return :oo.to_m
|
84
|
+
end
|
85
|
+
else
|
86
|
+
# oo - oo = -oo + oo = NaN
|
87
|
+
if (a.is_finite? == false and b.is_finite? == false)
|
88
|
+
if (a.is_positive? and b.is_negative?) or
|
89
|
+
(a.is_negative? and b.is_positive?)
|
90
|
+
return :nan.to_m
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# oo + n = n + oo = oo + oo = oo
|
95
|
+
if a.is_finite? == false
|
96
|
+
return a
|
97
|
+
end
|
98
|
+
|
99
|
+
# n - oo = - oo + n = -oo - oo = -oo
|
100
|
+
if b.is_finite? == false
|
101
|
+
return b
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# :nocov:
|
106
|
+
raise 'Internal error'
|
107
|
+
# :nocov:
|
108
|
+
end
|
109
|
+
|
110
|
+
def initialize(arg1, arg2)
|
111
|
+
super('+', [arg1, arg2])
|
112
|
+
end
|
113
|
+
|
114
|
+
def term1()
|
115
|
+
return @args[0]
|
116
|
+
end
|
117
|
+
|
118
|
+
def term2()
|
119
|
+
return @args[1]
|
120
|
+
end
|
121
|
+
|
122
|
+
def is_commutative?()
|
123
|
+
return true
|
124
|
+
end
|
125
|
+
|
126
|
+
def is_associative?()
|
127
|
+
return true
|
128
|
+
end
|
129
|
+
|
130
|
+
def is_sum_exp?()
|
131
|
+
return true
|
132
|
+
end
|
133
|
+
|
134
|
+
# Return all terms in the sum
|
135
|
+
def terms()
|
136
|
+
return Enumerator.new do |s|
|
137
|
+
term1.terms.each { |s1| s << s1 }
|
138
|
+
term2.terms.each { |s2| s << s2 }
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def evaluate
|
143
|
+
if term1.type.is_matrix?
|
144
|
+
return term1.matrix_add(term2)
|
145
|
+
end
|
146
|
+
|
147
|
+
return super
|
148
|
+
end
|
149
|
+
|
150
|
+
def type()
|
151
|
+
return term1.type.sum(term2.type)
|
152
|
+
end
|
153
|
+
|
154
|
+
def to_s()
|
155
|
+
if SyMath.setting(:expl_parentheses)
|
156
|
+
return '('.to_s + term1.to_s + ' + ' + term2.to_s + ')'.to_s
|
157
|
+
else
|
158
|
+
if term2.is_a?(SyMath::Minus)
|
159
|
+
return term1.to_s + " " + term2.to_s
|
160
|
+
else
|
161
|
+
return term1.to_s + " + " + term2.to_s
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def to_latex()
|
167
|
+
if term2.is_a?(SyMath::Minus)
|
168
|
+
return term1.to_latex + ' ' + term2.to_latex
|
169
|
+
else
|
170
|
+
return term1.to_latex + ' + ' + term2.to_latex
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|