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
@@ -0,0 +1,284 @@
|
|
1
|
+
require 'symath/value'
|
2
|
+
require 'symath/type'
|
3
|
+
|
4
|
+
module SyMath
|
5
|
+
class Definition::Variable < Definition
|
6
|
+
|
7
|
+
# Return parity of permutation. Even number of permutations give
|
8
|
+
# 1 and odd number gives -1
|
9
|
+
def self.permutation_parity(perm)
|
10
|
+
# perm is an array of indexes representing the permutation
|
11
|
+
# Put permutation list into disjoint cycles form
|
12
|
+
cycles = {}
|
13
|
+
(0..perm.length-1).each do |i|
|
14
|
+
cycles[perm[i]] = i
|
15
|
+
end
|
16
|
+
|
17
|
+
sign = 0
|
18
|
+
|
19
|
+
# Count the number even cycles.
|
20
|
+
(0..perm.length-1).each do |i|
|
21
|
+
next if !cycles.key?(i)
|
22
|
+
|
23
|
+
count = 0
|
24
|
+
while cycles.key?(i)
|
25
|
+
count += 1
|
26
|
+
j = cycles[i]
|
27
|
+
cycles.delete(i)
|
28
|
+
i = j
|
29
|
+
end
|
30
|
+
|
31
|
+
if (count % 2) == 0
|
32
|
+
sign += 1
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Even => 1, Odd => -1
|
37
|
+
sign = (1 - (sign % 2)*2).to_m
|
38
|
+
end
|
39
|
+
|
40
|
+
# Re-calculate various auxiliary data structured based on the given basis
|
41
|
+
# This does not scale for higher dimensions, but that will most probably
|
42
|
+
# be out of scope for this library anyway.
|
43
|
+
def self.recalc_basis_vectors()
|
44
|
+
b = SyMath.get_variable(:basis)
|
45
|
+
g = SyMath.get_variable(:g)
|
46
|
+
|
47
|
+
brow = b.row(0)
|
48
|
+
dim = brow.length
|
49
|
+
|
50
|
+
dmap = brow.map do |bb|
|
51
|
+
"d#{bb.name}".to_sym.to_m('dform')
|
52
|
+
end
|
53
|
+
|
54
|
+
vmap = brow.map do |bb|
|
55
|
+
bb.name.to_sym.to_m('vector')
|
56
|
+
end
|
57
|
+
|
58
|
+
# Hash up the order of the basis vectors
|
59
|
+
@@basis_order = {}
|
60
|
+
|
61
|
+
(0..dim - 1).each do |i|
|
62
|
+
@@basis_order[brow[i].name.to_sym] = i
|
63
|
+
@@basis_order["d#{brow[i].name}".to_sym] = i
|
64
|
+
end
|
65
|
+
|
66
|
+
# Calculate all possible permutations of all possible combinations of
|
67
|
+
# the basis vectors (including no vectors).
|
68
|
+
@@norm_map = {}
|
69
|
+
@@hodge_map = {}
|
70
|
+
(0..dim).each do |d|
|
71
|
+
(0..dim - 1).to_a.permutation(d).each do |p|
|
72
|
+
if p.length == 0
|
73
|
+
@@norm_map[1.to_m] = 1.to_m
|
74
|
+
@@hodge_map[1.to_m] = dmap.inject(:^)
|
75
|
+
next
|
76
|
+
end
|
77
|
+
|
78
|
+
# Hash them to the normalized expression (including the sign).
|
79
|
+
# Do this both for vectors and dforms.
|
80
|
+
norm = p.sort
|
81
|
+
sign = permutation_parity(p)
|
82
|
+
|
83
|
+
dform = p.map { |i| dmap[i] }.inject(:^)
|
84
|
+
vect = p.map { |i| vmap[i] }.inject(:^)
|
85
|
+
|
86
|
+
dnorm = sign*(norm.map { |i| dmap[i] }.inject(:^))
|
87
|
+
vnorm = sign*(norm.map { |i| vmap[i] }.inject(:^))
|
88
|
+
|
89
|
+
@@norm_map[dform] = dnorm
|
90
|
+
@@norm_map[vect] = vnorm
|
91
|
+
|
92
|
+
# Hash them to their hodge dual
|
93
|
+
dual = (0..dim - 1).to_a - norm
|
94
|
+
dsign = permutation_parity(p + dual)
|
95
|
+
|
96
|
+
if dual.length == 0
|
97
|
+
hdd = sign
|
98
|
+
hdv = sign
|
99
|
+
else
|
100
|
+
hdd = sign*dsign*(dual.map { |i| dmap[i] }.inject(:^))
|
101
|
+
hdv = sign*dsign*(dual.map { |i| vmap[i] }.inject(:^))
|
102
|
+
end
|
103
|
+
|
104
|
+
@@hodge_map[dform] = hdd
|
105
|
+
@@hodge_map[vect] = hdv
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# Calculate the musical isomorphisms. Hash up the mappings both ways.
|
110
|
+
flat = (g*SyMath::Matrix.new(dmap).transpose).evaluate.normalize.col(0)
|
111
|
+
sharp = (g.inverse*SyMath::Matrix.new(vmap).transpose).evaluate.
|
112
|
+
normalize.col(0)
|
113
|
+
|
114
|
+
@@flat_map = (0..dim - 1).map { |i| [vmap[i], flat[i]] }.to_h
|
115
|
+
@@sharp_map = (0..dim - 1).map { |i| [dmap[i], sharp[i]] }.to_h
|
116
|
+
end
|
117
|
+
|
118
|
+
# Return the hodge dual of an expression consisting only of basis vectors
|
119
|
+
# or basis dforms
|
120
|
+
def self.hodge_dual(exp)
|
121
|
+
if !@@hodge_map.key?(exp)
|
122
|
+
raise 'No hodge dual for ' + exp.to_s
|
123
|
+
end
|
124
|
+
|
125
|
+
return @@hodge_map[exp]
|
126
|
+
end
|
127
|
+
|
128
|
+
attr_reader :name
|
129
|
+
attr_reader :type
|
130
|
+
|
131
|
+
def initialize(name, t = 'real')
|
132
|
+
@type = t.to_t
|
133
|
+
super(name, define_symbol: false)
|
134
|
+
end
|
135
|
+
|
136
|
+
def description()
|
137
|
+
return "#{name} - free variable"
|
138
|
+
end
|
139
|
+
|
140
|
+
def call(*args)
|
141
|
+
return SyMath::Operator.create(self, args.map { |a| a.nil? ? a : a.to_m })
|
142
|
+
end
|
143
|
+
|
144
|
+
def ==(other)
|
145
|
+
return false if self.class.name != other.class.name
|
146
|
+
return false if @type != other.type
|
147
|
+
return @name == other.name
|
148
|
+
end
|
149
|
+
|
150
|
+
def <=>(other)
|
151
|
+
if self.class.name != other.class.name
|
152
|
+
return super(other)
|
153
|
+
end
|
154
|
+
|
155
|
+
if type.name != other.type.name
|
156
|
+
return type.name <=> other.type.name
|
157
|
+
end
|
158
|
+
# Order basis vectors and basis dforms by basis order
|
159
|
+
if type.is_subtype?('vector') or type.is_subtype?('dform')
|
160
|
+
bv1 = @@basis_order.key?(@name)
|
161
|
+
bv2 = @@basis_order.key?(other.name)
|
162
|
+
|
163
|
+
if !bv1 and bv2
|
164
|
+
# Order basis vectors higher than other vectors
|
165
|
+
return 1
|
166
|
+
elsif bv1 and !bv2
|
167
|
+
# Order basis vectors higher than other vectors
|
168
|
+
return -1
|
169
|
+
elsif bv1 and bv2
|
170
|
+
return @@basis_order[@name] <=> @@basis_order[other.name]
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
return @name.to_s <=> other.name.to_s
|
175
|
+
end
|
176
|
+
|
177
|
+
def is_constant?(vars = nil)
|
178
|
+
return false if vars.nil?
|
179
|
+
return !(vars.member?(self))
|
180
|
+
end
|
181
|
+
|
182
|
+
# Returns true if variable is a differential form
|
183
|
+
def is_d?()
|
184
|
+
return @type.is_dform?
|
185
|
+
end
|
186
|
+
|
187
|
+
# Returns variable which differential is based on
|
188
|
+
def undiff()
|
189
|
+
n = "#{@name}"
|
190
|
+
if n[0] == 'd'
|
191
|
+
n = n[1..-1]
|
192
|
+
end
|
193
|
+
|
194
|
+
n.to_sym.to_m(:real)
|
195
|
+
end
|
196
|
+
|
197
|
+
def to_d()
|
198
|
+
return "d#{@name}".to_sym.to_m(:dform)
|
199
|
+
end
|
200
|
+
|
201
|
+
# Return the vector dual of the dform
|
202
|
+
def raise_dform()
|
203
|
+
if !@@sharp_map.key?(self)
|
204
|
+
raise 'No vector dual for ' + to_s
|
205
|
+
end
|
206
|
+
|
207
|
+
return @@sharp_map[self]
|
208
|
+
end
|
209
|
+
|
210
|
+
# Return the dform dual of the vector
|
211
|
+
def lower_vector()
|
212
|
+
if !@@flat_map.key?(self)
|
213
|
+
raise 'No dform dual for ' + to_s
|
214
|
+
end
|
215
|
+
|
216
|
+
return @@flat_map[self]
|
217
|
+
end
|
218
|
+
|
219
|
+
def variables()
|
220
|
+
return [@name]
|
221
|
+
end
|
222
|
+
|
223
|
+
def replace(map)
|
224
|
+
if is_d?
|
225
|
+
u = undiff
|
226
|
+
if map.key?(u)
|
227
|
+
return op(:d, map[u].deep_clone)
|
228
|
+
else
|
229
|
+
return self
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
if map.key?(self)
|
234
|
+
return map[self].deep_clone
|
235
|
+
else
|
236
|
+
return self
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
def to_s()
|
241
|
+
if @type.is_dform?
|
242
|
+
return SyMath.setting(:d_symbol) + undiff.to_s
|
243
|
+
elsif @type.is_vector?
|
244
|
+
return @name.to_s + SyMath.setting(:vector_symbol)
|
245
|
+
elsif @type.is_covector?
|
246
|
+
return @name.to_s + SyMath.setting(:covector_symbol)
|
247
|
+
elsif @type.is_subtype?('tensor')
|
248
|
+
return @name.to_s + '['.to_s + @type.index_str + ']'.to_s
|
249
|
+
else
|
250
|
+
return @name.to_s
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
def to_latex()
|
255
|
+
if type.is_dform?
|
256
|
+
return '\mathrm{d}' + undiff.to_latex
|
257
|
+
elsif @type.is_vector?
|
258
|
+
return '\vec{'.to_s + @name.to_s + '}'.to_s
|
259
|
+
elsif @type.is_covector?
|
260
|
+
# What is the best way to denote a covector without using indexes?
|
261
|
+
return '\vec{'.to_s + @name.to_s + '}'.to_s
|
262
|
+
elsif @type.is_subtype?('tensor')
|
263
|
+
return @name.to_s + '['.to_s + @type.index_str + ']'.to_s
|
264
|
+
else
|
265
|
+
return @name.to_s
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
alias eql? ==
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
class Symbol
|
274
|
+
def to_m(type = 'real')
|
275
|
+
begin
|
276
|
+
# Look up the already defined symbol
|
277
|
+
# (we might want to check that it is a constant or variable)
|
278
|
+
return SyMath::Definition.get(self)
|
279
|
+
rescue
|
280
|
+
# Not defined. Define it now.
|
281
|
+
return SyMath::Definition::Variable.new(self, type)
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'symath/value'
|
2
|
+
require 'symath/definition/operator'
|
3
|
+
|
4
|
+
module SyMath
|
5
|
+
class Definition::Xd < Definition::Operator
|
6
|
+
def initialize()
|
7
|
+
super(:xd)
|
8
|
+
end
|
9
|
+
|
10
|
+
def description()
|
11
|
+
return 'd(f) - exterior derivative of f'
|
12
|
+
end
|
13
|
+
|
14
|
+
def evaluate_call(c)
|
15
|
+
vars = SyMath.get_variable(:basis.to_m).row(0)
|
16
|
+
|
17
|
+
return c.args[0].evaluate.d(vars)
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_latex(args)
|
21
|
+
if !args
|
22
|
+
args = @args
|
23
|
+
end
|
24
|
+
|
25
|
+
return "\\mathrm{d}(#{args[0].to_latex})"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,205 @@
|
|
1
|
+
# This class represents a definition of a constant, variable, operator or
|
2
|
+
# number. A function or operator can be used in an expression with and without
|
3
|
+
# arguments. In the latter case, they behave as a constant of type function or
|
4
|
+
# operator.
|
5
|
+
|
6
|
+
# A special case of a definition is the lambda function, which is a function
|
7
|
+
# with no name but a number of arguments, and an expression.
|
8
|
+
|
9
|
+
module SyMath
|
10
|
+
# Empty submodule in which to define methods for function, operator and constant
|
11
|
+
# definitions. The submodule can be extended/included into the code of the user
|
12
|
+
# code in order to make the math expressions simpler.
|
13
|
+
module Definitions
|
14
|
+
end
|
15
|
+
|
16
|
+
class Definition < Value
|
17
|
+
attr_reader :name
|
18
|
+
attr_reader :description
|
19
|
+
|
20
|
+
@@definitions = {}
|
21
|
+
|
22
|
+
@@skip_method_def = {
|
23
|
+
:+ => true,
|
24
|
+
:- => true,
|
25
|
+
:* => true,
|
26
|
+
:/ => true,
|
27
|
+
:** => true,
|
28
|
+
:'=' => true,
|
29
|
+
:op => true,
|
30
|
+
:fn => true,
|
31
|
+
}
|
32
|
+
|
33
|
+
def self.init_builtin()
|
34
|
+
# Create the builtin algebraic functions. The constructor will
|
35
|
+
# define the functions so they can be used in expressions.
|
36
|
+
SyMath::Definition::Function.new(:+)
|
37
|
+
SyMath::Definition::Function.new(:-)
|
38
|
+
SyMath::Definition::Function.new(:*)
|
39
|
+
SyMath::Definition::Function.new(:/)
|
40
|
+
SyMath::Definition::Function.new(:**)
|
41
|
+
SyMath::Definition::Function.new(:^)
|
42
|
+
SyMath::Definition::Function.new(:'=')
|
43
|
+
|
44
|
+
SyMath::Definition::Constant.init_builtin
|
45
|
+
SyMath::Definition::Function.init_builtin
|
46
|
+
SyMath::Definition::Operator.init_builtin
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.get(name)
|
50
|
+
if !@@definitions.has_key?(name.to_sym)
|
51
|
+
raise "#{name} is not defined."
|
52
|
+
end
|
53
|
+
|
54
|
+
return @@definitions[name.to_sym]
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.define(name, s)
|
58
|
+
if @@definitions.has_key?(name.to_sym)
|
59
|
+
raise "#{name} is already defined."
|
60
|
+
end
|
61
|
+
|
62
|
+
@@definitions[name.to_sym] = s
|
63
|
+
|
64
|
+
# Create a method for the definition. Without arguments, the method
|
65
|
+
# returns the definition object itself. With arguments, it returns
|
66
|
+
# the operator/function applied to a list of arguments.
|
67
|
+
if !SyMath::Definitions.private_method_defined?(name) and
|
68
|
+
!SyMath::Definitions.method_defined?(name) and
|
69
|
+
!@@skip_method_def[name.to_sym]
|
70
|
+
|
71
|
+
SyMath::Definitions.define_method :"#{name}" do |*args|
|
72
|
+
sym = s
|
73
|
+
if args.length > 0
|
74
|
+
return sym.call(*args)
|
75
|
+
else
|
76
|
+
return sym
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.undefine(name)
|
83
|
+
if !@@definitions.has_key?(name.to_sym)
|
84
|
+
raise "#{name} is not undefined."
|
85
|
+
end
|
86
|
+
|
87
|
+
@@definitions.delete(name.to_sym)
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.defined?(name)
|
91
|
+
return @@definitions.has_key?(name.to_sym)
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.definitions()
|
95
|
+
return @@definitions.values
|
96
|
+
end
|
97
|
+
|
98
|
+
def initialize(name, define_symbol: true, description: nil)
|
99
|
+
@name = name.to_sym
|
100
|
+
|
101
|
+
# Create a method for the definition if it's not a number or lambda
|
102
|
+
if define_symbol
|
103
|
+
self.class.define(name, self)
|
104
|
+
end
|
105
|
+
|
106
|
+
if description.nil?
|
107
|
+
@description = self.to_s
|
108
|
+
else
|
109
|
+
@description = description
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def reduce_call(c)
|
114
|
+
return c
|
115
|
+
end
|
116
|
+
|
117
|
+
def variables()
|
118
|
+
return []
|
119
|
+
end
|
120
|
+
|
121
|
+
def replace(map)
|
122
|
+
return self
|
123
|
+
end
|
124
|
+
|
125
|
+
def is_function?()
|
126
|
+
return false
|
127
|
+
end
|
128
|
+
|
129
|
+
def is_operator?()
|
130
|
+
return false
|
131
|
+
end
|
132
|
+
|
133
|
+
def arity()
|
134
|
+
return 0
|
135
|
+
end
|
136
|
+
|
137
|
+
def hash()
|
138
|
+
return @name.hash
|
139
|
+
end
|
140
|
+
|
141
|
+
def ==(other)
|
142
|
+
o = other.to_m
|
143
|
+
return false if self.class.name != o.class.name
|
144
|
+
return false if @name.to_s != o.name.to_s
|
145
|
+
return true
|
146
|
+
end
|
147
|
+
|
148
|
+
# FIXME: Do we need to redefine it in all subclasses?
|
149
|
+
alias eql? ==
|
150
|
+
|
151
|
+
# FIXME: Identical to operator comparison
|
152
|
+
def <=>(other)
|
153
|
+
if self.class.name != other.class.name
|
154
|
+
return super(other)
|
155
|
+
end
|
156
|
+
|
157
|
+
if name != other.name
|
158
|
+
return name.to_s <=> other.name.to_s
|
159
|
+
end
|
160
|
+
|
161
|
+
return 0
|
162
|
+
end
|
163
|
+
|
164
|
+
# is_self_adjoint
|
165
|
+
# is_additive
|
166
|
+
# is_homogenous
|
167
|
+
# is_conjugate_homogenous
|
168
|
+
# is_linear (additive + homogenous)
|
169
|
+
# is_antilinear (additive + conjugate_homogenous)
|
170
|
+
|
171
|
+
def is_constant?(vars = nil)
|
172
|
+
return true
|
173
|
+
end
|
174
|
+
|
175
|
+
def to_s()
|
176
|
+
return @name.to_s
|
177
|
+
end
|
178
|
+
|
179
|
+
def to_latex()
|
180
|
+
return "#{name}"
|
181
|
+
end
|
182
|
+
|
183
|
+
def inspect()
|
184
|
+
if SyMath.setting(:inspect_to_s)
|
185
|
+
return to_s
|
186
|
+
else
|
187
|
+
return super.inspect
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def definition(name)
|
194
|
+
return SyMath::Definition.get(name)
|
195
|
+
end
|
196
|
+
|
197
|
+
def definitions()
|
198
|
+
return SyMath::Definition.definitions
|
199
|
+
end
|
200
|
+
|
201
|
+
require 'symath/definition/variable'
|
202
|
+
require 'symath/definition/constant'
|
203
|
+
require 'symath/definition/number'
|
204
|
+
require 'symath/definition/operator'
|
205
|
+
require 'symath/definition/function'
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'symath/value'
|
2
|
+
require 'symath/operator'
|
3
|
+
|
4
|
+
module SyMath
|
5
|
+
class Equation < Operator
|
6
|
+
def initialize(arg1, arg2)
|
7
|
+
super('=', [arg1, arg2])
|
8
|
+
end
|
9
|
+
|
10
|
+
def +(other)
|
11
|
+
if other.is_a?(SyMath::Equation)
|
12
|
+
return eq(self.args[0] + other.args[0], self.args[1] + other.args[1])
|
13
|
+
else
|
14
|
+
return eq(self.args[0] + other, self.args[1] + other)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def -(other)
|
19
|
+
if other.is_a?(SyMath::Equation)
|
20
|
+
return eq(self.args[0] - other.args[0], self.args[1] - other.args[1])
|
21
|
+
else
|
22
|
+
return eq(self.args[0] - other, self.args[1] - other)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def -@()
|
27
|
+
return eq(-self.args[0], -self.args[1])
|
28
|
+
end
|
29
|
+
|
30
|
+
def *(other)
|
31
|
+
if other.is_a?(SyMath::Equation)
|
32
|
+
raise 'Cannot multiply two equations'
|
33
|
+
else
|
34
|
+
return eq(self.args[0] * other, self.args[1] * other)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def /(other)
|
39
|
+
if other.is_a?(SyMath::Equation)
|
40
|
+
raise 'Cannot divide by equation'
|
41
|
+
else
|
42
|
+
return eq(self.args[0] / other, self.args[1] / other)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def **(other)
|
47
|
+
if other.is_a?(SyMath::Equation)
|
48
|
+
raise 'Cannot use equation as exponent'
|
49
|
+
else
|
50
|
+
return eq(self.args[0]**other, self.args[1]**other)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def to_s()
|
55
|
+
return "#{@args[0]} = #{@args[1]}"
|
56
|
+
end
|
57
|
+
|
58
|
+
def to_latex()
|
59
|
+
return @args[0].to_latex + ' = ' + @args[1].to_latex
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Convenience method
|
65
|
+
def eq(a, b)
|
66
|
+
return SyMath::Equation.new(a, b)
|
67
|
+
end
|