symcalc 0.6.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/lib/symcalc.rb +278 -382
  3. metadata +16 -10
  4. data/lib/LICENSE.txt +0 -202
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dcfa9e29e383d1a24d3545c1944efac692a2e973861548dafeeaa61b34641147
4
- data.tar.gz: 5bca902b27d64a837d3908b1bd835f519f1f8f707e4dc56fd99d5b971e9821c9
3
+ metadata.gz: d1cb3c53ef5f25cad69cde67e01cffa1114da6954c06cd75b5a8377ffc382777
4
+ data.tar.gz: 617a794848e3a658f83f5a8aaf0873e9727acd46ceb485b12020c8813efabcfb
5
5
  SHA512:
6
- metadata.gz: 49729eafc708ea1e59c50ad7f3c5d3a9dfd06ce21d673379e2050cc091b4ade9631d554e0d113db992f64303f5bd1f2c5ea0123a84aa36536a744d8d577d6e64
7
- data.tar.gz: 7c84949657233ded9848633861fbd43963379b6fc468ef862cdb48830d2677547a1b7c8931d8e60d21356afef64cb5486229dcf29ff298af836a91b034321fcd
6
+ metadata.gz: '0108441596b3221a311b2ff0fd7f423adfb4440c0400f0bbbc138d454ea2db163fbb71b453566e89b3dfa70704dbb4ed7bae648b641d7966d5e04986db8ba50b'
7
+ data.tar.gz: 59e7edab69404cb37e93f82a6cfa0de2a05b8a4ae2105bbf9d526792359717539b0059640d5b8d141870b3964b6a31675a490a5071df2b4838fafb32fcc85782
data/lib/symcalc.rb CHANGED
@@ -6,10 +6,6 @@
6
6
  #
7
7
  # http://www.apache.org/licenses/LICENSE-2.0
8
8
  #
9
- # or
10
- #
11
- # read LICENSE.txt in the SymCalc lib folder
12
- #
13
9
  # Unless required by applicable law or agreed to in writing, software
14
10
  # distributed under the License is distributed on an "AS IS" BASIS,
15
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -17,38 +13,70 @@
17
13
  # limitations under the License.
18
14
 
19
15
 
16
+ # SymCalc auto simplify option
17
+ if !$SYMCALC_AUTO_SIMPLIFY
18
+ $SYMCALC_AUTO_SIMPLIFY = true
19
+ end
20
+
21
+ # The SymCalc module
22
+ module SymCalc
23
+
20
24
  class Equation
21
25
 
22
26
  def to_s
23
27
  self.display()
24
28
  end
25
29
 
26
- def inspect
27
- self.display()
30
+ # def inspect
31
+ # self.display()
32
+ # end
33
+
34
+ def display
35
+ ""
28
36
  end
29
37
 
30
38
  def coerce(other)
31
- [to_equation(other), self]
39
+ [SymCalc.to_equation(other), self]
32
40
  end
33
41
 
34
42
  def *(eq)
35
- return Multiplication.new(self, to_equation(eq))
43
+ eq = Multiplication.new([self, SymCalc.to_equation(eq)])
44
+ eq = eq.simplify if $SYMCALC_AUTO_SIMPLIFY
45
+ eq
36
46
  end
37
47
 
38
48
  def /(eq)
39
- return Division.new(self, to_equation(eq))
49
+ eq = Division.new(self, SymCalc.to_equation(eq))
50
+ eq = eq.simplify if $SYMCALC_AUTO_SIMPLIFY
51
+ eq
40
52
  end
41
53
 
42
54
  def +(eq)
43
- return Sum.new(self, to_equation(eq))
55
+ eq = Sum.new([self, SymCalc.to_equation(eq)])
56
+ eq = eq.simplify if $SYMCALC_AUTO_SIMPLIFY
57
+ eq
58
+ end
59
+
60
+ def -@()
61
+ eq = Negate.new(self)
62
+ eq = eq.simplify if $SYMCALC_AUTO_SIMPLIFY
63
+ eq
44
64
  end
45
65
 
46
66
  def -(eq)
47
- return Subtraction.new(self, to_equation(eq))
67
+ eq = Sum.new([self, Negate.new(SymCalc.to_equation(eq))])
68
+ eq = eq.simplify if $SYMCALC_AUTO_SIMPLIFY
69
+ eq
48
70
  end
49
71
 
50
72
  def **(eq)
51
- return Power.new(self, to_equation(eq))
73
+ eq = Power.new(self, SymCalc.to_equation(eq))
74
+ eq = eq.simplify if $SYMCALC_AUTO_SIMPLIFY
75
+ eq
76
+ end
77
+
78
+ def __derivative__(variable: nil)
79
+ raise "No derivative function implemented for this object"
52
80
  end
53
81
 
54
82
  # Calculate the derivative of the given function
@@ -66,17 +94,19 @@ class Equation
66
94
  #
67
95
  def derivative(order: 1, variable: nil)
68
96
  if variable == nil && self.all_variables.size < 2
69
- fx = self
97
+ fx = self.simplify
70
98
  order.times do
71
- fx = fx.simplify.__derivative__.simplify
99
+ fx = fx.__derivative__
100
+ fx = fx.simplify if $SYMCALC_AUTO_SIMPLIFY
72
101
  end
73
102
  return fx
74
103
  elsif variable == nil && self.all_variables.size > 1
75
104
  raise "Expected a variable as input for a #{self.all_variables.size}-dimensional function"
76
105
  else
77
- fx = self
106
+ fx = self.simplify
78
107
  order.times do
79
- fx = fx.simplify.__derivative__(variable: variable).simplify
108
+ fx = fx.__derivative__(variable: variable)
109
+ fx = fx.simplify if $SYMCALC_AUTO_SIMPLIFY
80
110
  end
81
111
  return fx
82
112
  end
@@ -90,98 +120,12 @@ class Equation
90
120
  # Accepts no arguments
91
121
  #
92
122
  def simplify
93
-
94
- simplified = self.__simplify__
95
-
96
- if [Multiplication, Division].include? simplified.class
97
- m_els = simplified.__get_m_elements__(Hash.new)
98
-
99
- if m_els.keys.size == 0
100
- return EquationValue.new(0)
101
- elsif m_els.keys.size == 1
102
- part = m_els.keys[0]
103
- power = to_equation(m_els.values[0])
104
- if part == "exp"
105
- part = (power == to_equation(1)) ? BasicVars::E : Exp.new(power)
106
- else
107
- part = part ** power if power != to_equation(1)
108
- end
109
- return part
110
- else
111
- els_index = 0
112
- eq = nil
113
-
114
- coeff = to_equation(1)
115
-
116
- m_els.size.times do |els_index|
117
- base = m_els.keys[els_index]
118
- power = m_els[base]
119
-
120
- base = base.simplify if base.is_a? Equation
121
- power = power.simplify if power.is_a? Equation
122
-
123
- base = to_equation base if base != "exp"
124
- power = to_equation power
125
-
126
- if power == to_equation(0)
127
- next
128
- end
129
-
130
- if base.is_a?(EquationValue) && power.is_a?(EquationValue)
131
- case power
132
- when EquationValue.new(1)
133
- coeff *= base
134
- else
135
- coeff *= base ** power
136
- end
137
- next
138
- end
139
-
140
- if base == "exp"
141
- case power
142
- when to_equation(1)
143
- part = BasicVars::E
144
- else
145
- part = Exp.new(power)
146
- end
147
- else
148
- case power
149
- when to_equation(1)
150
- part = base
151
- else
152
- part = base ** power
153
- end
154
- end
155
-
156
- if eq == nil
157
- eq = part
158
- else
159
- eq *= part
160
- end
161
-
162
- end
163
-
164
- coeff = coeff.__simplify__
165
-
166
- if coeff == to_equation(1)
167
- eq = eq
168
- elsif coeff == to_equation(0)
169
- eq = to_equation(0)
170
- elsif eq == nil
171
- eq = coeff
172
- else
173
- eq = coeff * eq
174
- end
175
-
176
- return eq
177
- end
178
-
179
- else
180
- return simplified
181
- end
182
-
123
+ self.__simplify__
183
124
  end
184
125
 
126
+ def __eval__(var_hash)
127
+ raise "__eval__ method not implemented for this class"
128
+ end
185
129
 
186
130
  # Evaluates the function at given variable values
187
131
  # Accepts the hash of variables and their values to evalualte the function
@@ -191,9 +135,10 @@ class Equation
191
135
  # fx = x ** 2
192
136
  # puts fx.eval(x: 3)
193
137
  #
194
- def eval(var_hash)
138
+ def eval(var_hash = nil)
139
+ var_hash = Hash.new if !var_hash
195
140
  if var_hash.values.size == 0
196
- result = self.__eval__(Hash.new)
141
+ result = self.__eval__(var_hash)
197
142
  elsif !var_hash.values[0].is_a?(Array)
198
143
  result = self.__eval__ var_hash
199
144
  elsif var_hash.values[0].is_a? Array
@@ -209,7 +154,7 @@ class Equation
209
154
 
210
155
 
211
156
  def __sub__ original, replacement
212
- return to_equation(replacement) if self == to_equation(original)
157
+ return SymCalc.to_equation(replacement) if self == SymCalc.to_equation(original)
213
158
  return self
214
159
  end
215
160
 
@@ -222,8 +167,11 @@ class Equation
222
167
  # puts f.sub(a, 3 * x) # => (3 * x) ** 2
223
168
  #
224
169
  def sub original, replacement
225
- self.__sub__(original, replacement).simplify
170
+ eq = self.__sub__(original, replacement)
171
+ eq = eq.simplify if $SYMCALC_AUTO_SIMPLIFY
172
+ eq
226
173
  end
174
+
227
175
  end
228
176
 
229
177
  # Converts the given argument into the equation type, if not already an equation
@@ -264,31 +212,49 @@ class EquationValue < Equation
264
212
  @value == value
265
213
  end
266
214
 
267
- def hash
268
- 1111000 + @value
215
+ def all_variables
216
+ []
217
+ end
218
+
219
+ end
220
+
221
+ # Implements the Constant class. Behaves like EquationValue when evaluating and like Variable when printing
222
+ class Constant < Equation
223
+
224
+ attr_accessor :value, :name
225
+
226
+ def initialize(name, value)
227
+ @value = value
228
+ @name = name
269
229
  end
270
230
 
271
- def eql? obj
272
- return false if !obj.is_a? EquationValue
273
- return obj.value == @value
231
+ def display
232
+ return @name
274
233
  end
275
234
 
276
- def __get_m_elements__ var_hash
277
- if var_hash.keys.include? self
278
- var_hash[self] = var_hash[self] + 1
235
+ def __eval__ var_hash
236
+ if @value.is_a? Integer
237
+ return @value.to_f
279
238
  else
280
- var_hash[self] = 1
239
+ return @value
281
240
  end
282
- return var_hash
283
241
  end
284
242
 
243
+ def __derivative__ variable: nil
244
+ return EquationValue.new 0
245
+ end
246
+
247
+ def ==(value)
248
+ @value == value
249
+ end
285
250
 
286
251
  def all_variables
287
252
  []
288
253
  end
289
-
254
+
290
255
  end
291
256
 
257
+
292
258
  # Implements the Variable class
293
259
  class Variable < Equation
294
260
 
@@ -301,9 +267,8 @@ class Variable < Equation
301
267
  # fx = x ** 2
302
268
  # fx.eval # => 25
303
269
  #
304
- def initialize name, fixed_value = nil
270
+ def initialize name
305
271
  @name = name
306
- @fixed_value = fixed_value
307
272
  end
308
273
 
309
274
  def display
@@ -311,9 +276,7 @@ class Variable < Equation
311
276
  end
312
277
 
313
278
  def __eval__ var_hash
314
- if @fixed_value
315
- return @fixed_value
316
- elsif var_hash.keys.include?(@name.to_sym) or var_hash.keys.include?(@name.to_s)
279
+ if var_hash.keys.include?(@name.to_sym) or var_hash.keys.include?(@name.to_s)
317
280
  return (var_hash[@name.to_sym] or var_hash[@name.to_s])
318
281
  else
319
282
  raise "No value provided for #{@name.to_s} in eval"
@@ -325,17 +288,12 @@ class Variable < Equation
325
288
  if variable == nil || variable == self
326
289
  return EquationValue.new 1
327
290
  else
328
- return to_equation(0)
291
+ return SymCalc.to_equation(0)
329
292
  end
330
293
  end
331
294
 
332
- def __get_m_elements__ var_hash
333
- if var_hash.keys.include? self
334
- var_hash[self] += 1
335
- else
336
- var_hash[self] = 1
337
- end
338
- return var_hash
295
+ def __simplify__
296
+ self
339
297
  end
340
298
 
341
299
  def all_variables
@@ -343,174 +301,183 @@ class Variable < Equation
343
301
  end
344
302
  end
345
303
 
346
- # Basic variables that are already implemented in SymCalc and have fixed values
347
- module BasicVars
348
-
349
- E = Variable.new "e", Math::E
350
-
351
- end
352
304
 
353
305
  # Implements sum operations in SymCalc
354
306
  class Sum < Equation
355
- def initialize lside, rside
356
- @lside = lside
357
- @rside = rside
307
+ def initialize elements
308
+ @elements = []
309
+ elements.each do |eq|
310
+ if eq.is_a? Sum
311
+ @elements += eq.instance_variable_get(:@elements)
312
+ else
313
+ @elements << eq
314
+ end
315
+ end
358
316
  end
359
317
 
360
318
  def display
361
- return "(#{@lside.display}) + (#{@rside.display})"
319
+ return @elements.map{|eq| "(#{eq.display})"}.join(" + ")
362
320
  end
363
321
 
364
322
  def __eval__ var_hash
365
- return @lside.eval(var_hash) + @rside.eval(var_hash)
323
+ return @elements.map{|eq| eq.eval(var_hash)}.sum
366
324
  end
367
325
 
368
326
  def __derivative__ variable: nil
369
- return @lside.derivative(variable: variable) + @rside.derivative(variable: variable)
327
+ return Sum.new(@elements.map{|eq| eq.__derivative__(variable: variable)})
370
328
  end
371
329
 
372
330
  def __simplify__
373
- @lside = @lside.__simplify__
374
- @rside = @rside.__simplify__
375
- if (@lside.is_a? EquationValue) && (@rside.is_a? EquationValue)
376
- return EquationValue.new((@lside + @rside).eval({}))
377
- elsif @lside == EquationValue.new(0)
378
- return @rside
379
- elsif @rside == EquationValue.new(0)
380
- return @lside
381
- else
382
- return self
331
+ simplified = @elements.map{|eq| eq.__simplify__}
332
+
333
+ simplified.filter! do |el|
334
+ if el.is_a? EquationValue
335
+ next if el.value == 0
336
+ end
337
+ true
383
338
  end
384
- end
385
-
386
- def __get_m_elements__ el_hash
387
- if el_hash.keys.include? self
388
- el_hash[self] += 1
389
- else
390
- el_hash[self] = 1
339
+
340
+ if simplified.size == 1
341
+ return simplified[0]
391
342
  end
392
- return el_hash
343
+
344
+ return Sum.new(simplified)
393
345
  end
394
346
 
395
347
  def all_variables
396
- return (@lside.all_variables + @rside.all_variables).uniq
348
+ return @elements.map{|eq| eq.all_variables }.flatten.uniq
397
349
  end
398
350
 
399
351
  def __sub__ original, replacement
400
- return to_equation(replacement) if self == to_equation(original)
401
- return @lside.__sub__(original, replacement) + @rside.__sub__(original, replacement)
352
+ return SymCalc.to_equation(replacement) if self == SymCalc.to_equation(original)
353
+
354
+ return Sum.new(@elements.map{|eq| eq.__sub__(original, replacement)})
402
355
  end
403
356
  end
404
357
 
405
358
  # Implements the subtraction operation in SymCalc
406
- class Subtraction < Equation
407
- def initialize lside, rside
408
- @lside = lside
409
- @rside = rside
359
+ class Negate < Equation
360
+ def initialize eq
361
+ @eq = eq
410
362
  end
411
363
 
412
364
  def display
413
- return "(#{@lside.display}) - (#{@rside.display})"
365
+ return "-(#{@eq.display})"
414
366
  end
415
367
 
416
368
  def __eval__ var_hash
417
- return @lside.eval(var_hash) - @rside.eval(var_hash)
369
+ return -@eq.eval(var_hash)
418
370
  end
419
371
 
420
372
  def __derivative__ variable: nil
421
- return @lside.derivative(variable: variable) - @rside.derivative(variable: variable)
422
- end
423
-
424
- def __get_m_elements__ el_hash
425
- if el_hash.keys.include? self
426
- el_hash[self] += 1
427
- else
428
- el_hash[self] = 1
429
- end
430
- return el_hash
373
+ return -@eq.__derivative__(variable: variable)
431
374
  end
432
375
 
433
376
  def __simplify__
434
- @lside = @lside.__simplify__
435
- @rside = @rside.__simplify__
436
-
437
- if (@lside.is_a? EquationValue) && (@rside.is_a? EquationValue)
438
- return EquationValue.new((@lside - @rside).eval(Hash.new))
439
- elsif @rside == EquationValue.new(0)
440
- return @lside
441
- elsif @lside == EquationValue.new(0)
442
- return -1 * @rside
443
- elsif @lside == @rside
444
- return EquationValue.new(0)
445
- else
446
- return self
447
- end
377
+ return Negate.new(@eq.__simplify__)
448
378
  end
449
379
 
450
380
  def all_variables
451
- return (@lside.all_variables + @rside.all_variables).uniq
381
+ return @eq.all_variables
452
382
  end
453
383
 
454
384
  def __sub__ original, replacement
455
- return to_equation(replacement) if self == to_equation(original)
456
- return @lside.__sub__(original, replacement) - @rside.__sub__(original, replacement)
385
+ return SymCalc.to_equation(replacement) if self == SymCalc.to_equation(original)
386
+ return Negate.new(@eq.__sub__(original, replacement))
457
387
  end
458
388
  end
459
389
 
460
390
  # Implements the multiplication operation in SymCalc
461
391
  class Multiplication < Equation
462
- def initialize lside, rside
463
- @lside = lside
464
- @rside = rside
392
+ def initialize elements
393
+ @elements = []
394
+ elements.each do |el|
395
+ if el.is_a? Multiplication
396
+ @elements += el.instance_variable_get(:@elements)
397
+ else
398
+ @elements << el
399
+ end
400
+ end
465
401
  end
466
402
 
467
403
  def display
468
- return "(#{@lside.display}) * (#{@rside.display})"
404
+ return @elements.map{|el| "(#{el.display})"}.join(" * ")
469
405
  end
470
406
 
471
407
  def __eval__ var_hash
472
- return @lside.eval(var_hash) * @rside.eval(var_hash)
408
+ result = 1
409
+ @elements.each do |el|
410
+ result *= el.__eval__(var_hash)
411
+ end
412
+ result
473
413
  end
474
414
 
475
415
  def __derivative__ variable: nil
476
- return @lside.derivative(variable: variable) * @rside + @lside * @rside.derivative(variable: variable)
416
+
417
+ sum_of_mults_arr = []
418
+
419
+ @elements.size.times do |element_index|
420
+ mult_arr = []
421
+
422
+
423
+ mult_arr << @elements[element_index].__derivative__(variable: variable)
424
+
425
+ @elements.size.times do |mult_el_index|
426
+ if element_index == mult_el_index
427
+ next
428
+ end
429
+ mult_arr << @elements[mult_el_index]
430
+ end
431
+
432
+ sum_of_mults_arr << Multiplication.new(mult_arr)
433
+ end
434
+
435
+ return Sum.new(sum_of_mults_arr)
477
436
  end
478
437
 
479
438
  def __simplify__
480
- @lside = @lside.__simplify__
481
- @rside = @rside.__simplify__
482
439
 
483
- if (@lside == EquationValue.new(0)) || (@rside == EquationValue.new(0))
484
- return EquationValue.new 0
485
- elsif @lside == EquationValue.new(1)
486
- return @rside
487
- elsif @rside == EquationValue.new(1)
488
- return @lside
489
- elsif @rside.is_a? EquationValue and @lside.is_a? EquationValue
490
- calculated = @rside.value * @lside.value
491
- if calculated.to_s.size <= 6
492
- return to_equation(calculated)
440
+ numbers = []
441
+ simplified = []
442
+
443
+ @elements.each do |eq|
444
+ s_eq = eq.__simplify__()
445
+
446
+ if s_eq.is_a? EquationValue
447
+ if s_eq.value == 0
448
+ return EquationValue.new(0)
449
+ elsif s_eq.value == 1
450
+ next
451
+ else
452
+ numbers << s_eq.value
453
+ end
493
454
  else
494
- return self
495
- end
496
- else
497
- return self
455
+ simplified << s_eq
456
+ end
498
457
  end
499
- end
500
-
501
- def __get_m_elements__ el_hash
502
- el_hash = @lside.__get_m_elements__(el_hash)
503
- el_hash = @rside.__get_m_elements__(el_hash)
504
- return el_hash
458
+
459
+
460
+ coeff = 1
461
+ numbers.each {|n| coeff *= n}
462
+
463
+ if coeff != 1
464
+ simplified.insert 0, EquationValue.new(coeff)
465
+ end
466
+
467
+ if simplified.size == 1
468
+ return simplified[0]
469
+ end
470
+
471
+ return Multiplication.new(simplified)
505
472
  end
506
473
 
507
474
  def all_variables
508
- return (@lside.all_variables + @rside.all_variables).uniq
475
+ return @elements.map{|el| el.all_variables}.flatten.uniq
509
476
  end
510
477
 
511
478
  def __sub__ original, replacement
512
- return to_equation(replacement) if self == to_equation(original)
513
- return @lside.__sub__(original, replacement) * @rside.__sub__(original, replacement)
479
+ return SymCalc.to_equation(replacement) if self == SymCalc.to_equation(original)
480
+ return Multiplication.new(@elements.map{|el| el.__sub__(original, replacement)})
514
481
  end
515
482
  end
516
483
 
@@ -533,8 +500,8 @@ class Division < Equation
533
500
  end
534
501
 
535
502
  def __derivative__ variable: nil
536
- return (@lside * @rside ** (-1)).derivative(variable: variable)
537
- # return (@lside.derivative * @rside - @lside * @rside.derivative) / (@rside ** 2)
503
+ # return (@lside * @rside ** (-1)).derivative(variable: variable)
504
+ return (@lside.derivative(variable: variable) * @rside - @lside * @rside.derivative(variable: variable)) / (@rside ** 2)
538
505
  end
539
506
 
540
507
  def __simplify__
@@ -555,26 +522,13 @@ class Division < Equation
555
522
  end
556
523
  end
557
524
 
558
- def __get_m_elements__ el_hash
559
- mult = @lside.__get_m_elements__(el_hash)
560
- div = @rside.__get_m_elements__({})
561
- div.each do |k, v|
562
- if mult.keys.include? k
563
- mult[k] -= v
564
- else
565
- mult[k] = -v
566
- end
567
- end
568
- return mult
569
- end
570
-
571
525
 
572
526
  def all_variables
573
527
  return (@lside.all_variables + @rside.all_variables).uniq
574
528
  end
575
529
 
576
530
  def __sub__ original, replacement
577
- return to_equation(replacement) if self == to_equation(original)
531
+ return SymCalc.to_equation(replacement) if self == SymCalc.to_equation(original)
578
532
  return @lside.__sub__(original, replacement) / @rside.__sub__(original, replacement)
579
533
  end
580
534
  end
@@ -586,7 +540,7 @@ class Exp < Equation
586
540
  attr_accessor :power
587
541
 
588
542
  def initialize power
589
- @power = to_equation(power)
543
+ @power = SymCalc.to_equation(power)
590
544
  end
591
545
 
592
546
  def display
@@ -619,21 +573,12 @@ class Exp < Equation
619
573
  end
620
574
  end
621
575
 
622
- def __get_m_elements__ var_hash
623
- if var_hash.keys.include? "exp"
624
- var_hash["exp"] += @power
625
- else
626
- var_hash["exp"] = @power
627
- end
628
- return var_hash
629
- end
630
-
631
576
  def all_variables
632
577
  return @power.all_variables
633
578
  end
634
579
 
635
580
  def __sub__ original, replacement
636
- return to_equation(replacement) if self == to_equation(original)
581
+ return SymCalc.to_equation(replacement) if self == SymCalc.to_equation(original)
637
582
  return Exp.new(@power.__sub__(original, replacement))
638
583
  end
639
584
  end
@@ -671,41 +616,29 @@ class Power < Equation
671
616
  return false
672
617
  end
673
618
  end
674
-
675
- def __get_m_elements__ var_hash
676
- elms = @base.__get_m_elements__({})
677
- elms.each do |k, v|
678
- if var_hash.keys.include? k
679
- var_hash[k] += v * @power
680
- else
681
- var_hash[k] = @power
682
- end
683
- end
684
- return var_hash
685
- end
686
-
619
+
687
620
  def __simplify__
688
621
 
689
- base = @base.simplify
690
- power = @power.simplify
622
+ s_base = @base.__simplify__
623
+ s_power = @power.__simplify__
691
624
 
692
- if power == EquationValue.new(0)
625
+ if s_power == EquationValue.new(0)
693
626
  return EquationValue.new(1)
694
- elsif power == EquationValue.new(1)
695
- return base
696
- elsif base.is_a?(EquationValue) && power.is_a?(EquationValue)
697
- computed = base.value ** power.value
627
+ elsif s_power == EquationValue.new(1)
628
+ return s_base
629
+ elsif s_base.is_a?(EquationValue) && s_power.is_a?(EquationValue)
630
+ computed = s_base.value ** s_power.value
698
631
  if computed.to_s.size <= 6
699
- return to_equation(computed)
632
+ return SymCalc.to_equation(computed)
700
633
  else
701
- return base ** power
634
+ return s_base ** power
702
635
  end
703
- elsif base.is_a?(Power)
704
- new_base = base.base
705
- new_power = base.power * power
706
- return (new_base ** new_power).simplify
636
+ elsif s_base.is_a?(Power)
637
+ new_base = s_base.base
638
+ new_power = s_base.power * s_power
639
+ return (new_base ** new_power).__simplify__
707
640
  else
708
- return base ** power
641
+ return Power.new(s_base, s_power)
709
642
  end
710
643
 
711
644
  end
@@ -715,7 +648,7 @@ class Power < Equation
715
648
  end
716
649
 
717
650
  def __sub__ original, replacement
718
- return to_equation(replacement) if self == to_equation(original)
651
+ return SymCalc.to_equation(replacement) if self == SymCalc.to_equation(original)
719
652
  return @base.__sub__(original, replacement) ** @power.__sub__(original, replacement)
720
653
  end
721
654
  end
@@ -742,21 +675,12 @@ class Sin < Equation
742
675
  return Sin.new(@eq.__simplify__)
743
676
  end
744
677
 
745
- def __get_m_elements__ var_hash
746
- if var_hash.keys.include? self
747
- var_hash[self] += 1
748
- else
749
- var_hash[self] = 1
750
- end
751
- return var_hash
752
- end
753
-
754
678
  def all_variables
755
679
  return @eq.all_variables
756
680
  end
757
681
 
758
682
  def __sub__ original, replacement
759
- return to_equation(replacement) if self == to_equation(original)
683
+ return SymCalc.to_equation(replacement) if self == SymCalc.to_equation(original)
760
684
  return Sin.new(@eq.__sub__(original, replacement))
761
685
  end
762
686
  end
@@ -783,21 +707,12 @@ class Cos < Equation
783
707
  return Cos.new(@eq.__simplify__)
784
708
  end
785
709
 
786
- def __get_m_elements__ var_hash
787
- if var_hash.keys.include? self
788
- var_hash[self] += 1
789
- else
790
- var_hash[self] = 1
791
- end
792
- return var_hash
793
- end
794
-
795
710
  def all_variables
796
711
  return @eq.all_variables
797
712
  end
798
713
 
799
714
  def __sub__ original, replacement
800
- return to_equation(replacement) if self == to_equation(original)
715
+ return SymCalc.to_equation(replacement) if self == SymCalc.to_equation(original)
801
716
  return Cos.new(@eq.__sub__(original, replacement))
802
717
  end
803
718
  end
@@ -827,22 +742,13 @@ class Ln < Equation
827
742
  def __simplify__
828
743
  return Ln.new(@eq.__simplify__)
829
744
  end
830
-
831
- def __get_m_elements__ var_hash
832
- if var_hash.keys.include? self
833
- var_hash[self] += 1
834
- else
835
- var_hash[self] = 1
836
- end
837
- return var_hash
838
- end
839
-
745
+
840
746
  def all_variables
841
747
  return @eq.all_variables
842
748
  end
843
749
 
844
750
  def __sub__ original, replacement
845
- return to_equation(replacement) if self == to_equation(original)
751
+ return SymCalc.to_equation(replacement) if self == SymCalc.to_equation(original)
846
752
  return Ln.new(@eq.__sub__(original, replacement))
847
753
  end
848
754
  end
@@ -861,8 +767,8 @@ class Log < Equation
861
767
  # fx = Log.new 10, x ** 2
862
768
  #
863
769
  def initialize base, eq
864
- @base = to_equation(base)
865
- @eq = to_equation(eq)
770
+ @base = SymCalc.to_equation(base)
771
+ @eq = SymCalc.to_equation(eq)
866
772
  end
867
773
 
868
774
  def display
@@ -881,21 +787,12 @@ class Log < Equation
881
787
  return Log.new(@base.__simplify__, @eq.__simplify__)
882
788
  end
883
789
 
884
- def __get_m_elements__ var_hash
885
- if var_hash.keys.include? self
886
- var_hash[self] += 1
887
- else
888
- var_hash[self] = 1
889
- end
890
- return var_hash
891
- end
892
-
893
790
  def all_variables
894
791
  return (@base.all_variables + @eq.all_variables).uniq
895
792
  end
896
793
 
897
794
  def __sub__ original, replacement
898
- return to_equation(replacement) if self == to_equation(original)
795
+ return SymCalc.to_equation(replacement) if self == SymCalc.to_equation(original)
899
796
  return Log.new(@base.__sub__(original, replacement), @eq.__sub__(original, replacement))
900
797
  end
901
798
  end
@@ -906,7 +803,7 @@ class Abs < Equation
906
803
  attr_accessor :eq
907
804
 
908
805
  def initialize eq
909
- @eq = to_equation(eq)
806
+ @eq = SymCalc.to_equation(eq)
910
807
  end
911
808
 
912
809
  def display
@@ -921,19 +818,6 @@ class Abs < Equation
921
818
  return @eq / Abs.new(@eq) * @eq.derivative(variable: variable)
922
819
  end
923
820
 
924
- def __simplify__
925
- return Abs.new(@eq.__simplify__)
926
- end
927
-
928
- def __get_m_elements__ var_hash
929
- if var_hash.keys.include? self
930
- var_hash[self] += 1
931
- else
932
- var_hash[self] = 1
933
- end
934
- return var_hash
935
- end
936
-
937
821
  def __simplify__
938
822
  if @eq.is_a?(Power) && @eq.power.is_a?(EquationValue) && @eq.power.value.is_a?(Numeric) && (@eq.power.value % 2 == 0)
939
823
  return @eq
@@ -946,50 +830,62 @@ class Abs < Equation
946
830
  end
947
831
 
948
832
  def __sub__ original, replacement
949
- return to_equation(replacement) if self == to_equation(original)
833
+ return SymCalc.to_equation(replacement) if self == SymCalc.to_equation(original)
950
834
  return Abs.new(@eq.__sub__(original, replacement))
951
835
  end
952
836
  end
953
837
 
954
838
 
955
- # The SymCalc module implemenets the standard function class creations shorter
956
- module SymCalc
957
-
958
- # sin(equation) is the same as Sin.new(equation), just shorter
959
- def sin(eq)
960
- return Sin.new(to_equation(eq))
961
- end
962
-
963
- # cos(equation) is the same as Cos.new(equation), just shorter
964
- def cos(eq)
965
- return Cos.new(to_equation(eq))
966
- end
967
-
968
- # ln(equation) is the same as Ln.new(equation), just shorter
969
- def ln(eq)
970
- return Ln.new(to_equation(eq))
971
- end
972
-
973
- # log(base, equation) is the same as Log.new(base, equation), just shorter
974
- def log(base, eq)
975
- return Log.new(to_equation(base), to_equation(eq))
976
- end
977
-
978
- # exp(equation) is the same as Exp.new(equation), just shorter
979
- def exp(power)
980
- return Exp.new(to_equation(power))
981
- end
982
-
983
- # var(name) is the same as Variable.new(name), just shorter
984
- def var(name, fixed_value=nil)
985
- Variable.new name, fixed_value
986
- end
987
-
988
- # abs(equation) is the same as Abs.new(equation), just shorter
989
- def abs eq
990
- return Abs.new(to_equation(eq))
991
- end
839
+ # Basic variables that are already implemented in SymCalc and have fixed values
840
+ module Constants
992
841
 
993
- module_function :sin, :cos, :ln, :log, :exp, :var, :abs
842
+ Pi = Constant.new "pi", Math::PI
843
+ E = Constant.new "e", Math::E
844
+
845
+ end
846
+
847
+
848
+
849
+
850
+ # sin(equation) is the same as Sin.new(equation), just shorter
851
+ def sin(eq)
852
+ return Sin.new(SymCalc.to_equation(eq))
853
+ end
854
+
855
+ # cos(equation) is the same as Cos.new(equation), just shorter
856
+ def cos(eq)
857
+ return Cos.new(SymCalc.to_equation(eq))
858
+ end
859
+
860
+ # ln(equation) is the same as Ln.new(equation), just shorter
861
+ def ln(eq)
862
+ return Ln.new(SymCalc.to_equation(eq))
863
+ end
864
+
865
+ # log(base, equation) is the same as Log.new(base, equation), just shorter
866
+ def log(base, eq)
867
+ return Log.new(SymCalc.to_equation(base), SymCalc.to_equation(eq))
868
+ end
869
+
870
+ # exp(equation) is the same as Exp.new(equation), just shorter
871
+ def exp(power)
872
+ return Exp.new(SymCalc.to_equation(power))
873
+ end
874
+
875
+ # var(name) is the same as Variable.new(name), just shorter
876
+ def var(name)
877
+ Variable.new name
878
+ end
879
+
880
+ def const(name, value)
881
+ Constant.new name, value
882
+ end
883
+
884
+ # abs(equation) is the same as Abs.new(equation), just shorter
885
+ def abs eq
886
+ return Abs.new(SymCalc.to_equation(eq))
887
+ end
888
+
889
+ module_function :sin, :cos, :ln, :log, :exp, :var, :abs, :const, :to_equation
994
890
 
995
891
  end
metadata CHANGED
@@ -1,28 +1,34 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: symcalc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kyryl Shyshko
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-01-15 00:00:00.000000000 Z
11
+ date: 2024-11-15 00:00:00.000000000 Z
12
12
  dependencies: []
13
- description: SymCalc empowers developers with the ability of creating symbolic functions,
14
- evaluating them and finding their derivatives in code.
13
+ description: 'SymCalc adds symbolic mathematics and calculus to your code. Create,
14
+ evaluate and differentiate mathematical functions with a single method call.
15
+
16
+ '
15
17
  email: kyryloshy@gmail.com
16
18
  executables: []
17
19
  extensions: []
18
20
  extra_rdoc_files: []
19
21
  files:
20
- - lib/LICENSE.txt
21
22
  - lib/symcalc.rb
22
- homepage: https://rubygems.org/gems/symcalc
23
+ homepage: https://symcalc.site/ruby
23
24
  licenses:
24
25
  - Apache-2.0
25
- metadata: {}
26
+ metadata:
27
+ changelog_uri: https://symcalc.site/ruby/changelog
28
+ documentation_uri: https://symcalc.site/ruby/docs
29
+ homepage_uri: https://symcalc.site/ruby
30
+ source_code_uri: https://github.com/symcalc/symcalc-ruby
31
+ bug_tracker_uri: https://github.com/symcalc/symcalc-ruby/issues
26
32
  post_install_message:
27
33
  rdoc_options: []
28
34
  require_paths:
@@ -31,15 +37,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
31
37
  requirements:
32
38
  - - ">="
33
39
  - !ruby/object:Gem::Version
34
- version: '0'
40
+ version: 3.0.0
35
41
  required_rubygems_version: !ruby/object:Gem::Requirement
36
42
  requirements:
37
43
  - - ">="
38
44
  - !ruby/object:Gem::Version
39
45
  version: '0'
40
46
  requirements: []
41
- rubygems_version: 3.3.11
47
+ rubygems_version: 3.5.10
42
48
  signing_key:
43
49
  specification_version: 4
44
- summary: Mathematical symbolic functions, calculus and differentiation in code
50
+ summary: Symbolic mathematics and calculus in Ruby
45
51
  test_files: []
data/lib/LICENSE.txt DELETED
@@ -1,202 +0,0 @@
1
-
2
- Apache License
3
- Version 2.0, January 2004
4
- http://www.apache.org/licenses/
5
-
6
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7
-
8
- 1. Definitions.
9
-
10
- "License" shall mean the terms and conditions for use, reproduction,
11
- and distribution as defined by Sections 1 through 9 of this document.
12
-
13
- "Licensor" shall mean the copyright owner or entity authorized by
14
- the copyright owner that is granting the License.
15
-
16
- "Legal Entity" shall mean the union of the acting entity and all
17
- other entities that control, are controlled by, or are under common
18
- control with that entity. For the purposes of this definition,
19
- "control" means (i) the power, direct or indirect, to cause the
20
- direction or management of such entity, whether by contract or
21
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
- outstanding shares, or (iii) beneficial ownership of such entity.
23
-
24
- "You" (or "Your") shall mean an individual or Legal Entity
25
- exercising permissions granted by this License.
26
-
27
- "Source" form shall mean the preferred form for making modifications,
28
- including but not limited to software source code, documentation
29
- source, and configuration files.
30
-
31
- "Object" form shall mean any form resulting from mechanical
32
- transformation or translation of a Source form, including but
33
- not limited to compiled object code, generated documentation,
34
- and conversions to other media types.
35
-
36
- "Work" shall mean the work of authorship, whether in Source or
37
- Object form, made available under the License, as indicated by a
38
- copyright notice that is included in or attached to the work
39
- (an example is provided in the Appendix below).
40
-
41
- "Derivative Works" shall mean any work, whether in Source or Object
42
- form, that is based on (or derived from) the Work and for which the
43
- editorial revisions, annotations, elaborations, or other modifications
44
- represent, as a whole, an original work of authorship. For the purposes
45
- of this License, Derivative Works shall not include works that remain
46
- separable from, or merely link (or bind by name) to the interfaces of,
47
- the Work and Derivative Works thereof.
48
-
49
- "Contribution" shall mean any work of authorship, including
50
- the original version of the Work and any modifications or additions
51
- to that Work or Derivative Works thereof, that is intentionally
52
- submitted to Licensor for inclusion in the Work by the copyright owner
53
- or by an individual or Legal Entity authorized to submit on behalf of
54
- the copyright owner. For the purposes of this definition, "submitted"
55
- means any form of electronic, verbal, or written communication sent
56
- to the Licensor or its representatives, including but not limited to
57
- communication on electronic mailing lists, source code control systems,
58
- and issue tracking systems that are managed by, or on behalf of, the
59
- Licensor for the purpose of discussing and improving the Work, but
60
- excluding communication that is conspicuously marked or otherwise
61
- designated in writing by the copyright owner as "Not a Contribution."
62
-
63
- "Contributor" shall mean Licensor and any individual or Legal Entity
64
- on behalf of whom a Contribution has been received by Licensor and
65
- subsequently incorporated within the Work.
66
-
67
- 2. Grant of Copyright License. Subject to the terms and conditions of
68
- this License, each Contributor hereby grants to You a perpetual,
69
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
- copyright license to reproduce, prepare Derivative Works of,
71
- publicly display, publicly perform, sublicense, and distribute the
72
- Work and such Derivative Works in Source or Object form.
73
-
74
- 3. Grant of Patent License. Subject to the terms and conditions of
75
- this License, each Contributor hereby grants to You a perpetual,
76
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
- (except as stated in this section) patent license to make, have made,
78
- use, offer to sell, sell, import, and otherwise transfer the Work,
79
- where such license applies only to those patent claims licensable
80
- by such Contributor that are necessarily infringed by their
81
- Contribution(s) alone or by combination of their Contribution(s)
82
- with the Work to which such Contribution(s) was submitted. If You
83
- institute patent litigation against any entity (including a
84
- cross-claim or counterclaim in a lawsuit) alleging that the Work
85
- or a Contribution incorporated within the Work constitutes direct
86
- or contributory patent infringement, then any patent licenses
87
- granted to You under this License for that Work shall terminate
88
- as of the date such litigation is filed.
89
-
90
- 4. Redistribution. You may reproduce and distribute copies of the
91
- Work or Derivative Works thereof in any medium, with or without
92
- modifications, and in Source or Object form, provided that You
93
- meet the following conditions:
94
-
95
- (a) You must give any other recipients of the Work or
96
- Derivative Works a copy of this License; and
97
-
98
- (b) You must cause any modified files to carry prominent notices
99
- stating that You changed the files; and
100
-
101
- (c) You must retain, in the Source form of any Derivative Works
102
- that You distribute, all copyright, patent, trademark, and
103
- attribution notices from the Source form of the Work,
104
- excluding those notices that do not pertain to any part of
105
- the Derivative Works; and
106
-
107
- (d) If the Work includes a "NOTICE" text file as part of its
108
- distribution, then any Derivative Works that You distribute must
109
- include a readable copy of the attribution notices contained
110
- within such NOTICE file, excluding those notices that do not
111
- pertain to any part of the Derivative Works, in at least one
112
- of the following places: within a NOTICE text file distributed
113
- as part of the Derivative Works; within the Source form or
114
- documentation, if provided along with the Derivative Works; or,
115
- within a display generated by the Derivative Works, if and
116
- wherever such third-party notices normally appear. The contents
117
- of the NOTICE file are for informational purposes only and
118
- do not modify the License. You may add Your own attribution
119
- notices within Derivative Works that You distribute, alongside
120
- or as an addendum to the NOTICE text from the Work, provided
121
- that such additional attribution notices cannot be construed
122
- as modifying the License.
123
-
124
- You may add Your own copyright statement to Your modifications and
125
- may provide additional or different license terms and conditions
126
- for use, reproduction, or distribution of Your modifications, or
127
- for any such Derivative Works as a whole, provided Your use,
128
- reproduction, and distribution of the Work otherwise complies with
129
- the conditions stated in this License.
130
-
131
- 5. Submission of Contributions. Unless You explicitly state otherwise,
132
- any Contribution intentionally submitted for inclusion in the Work
133
- by You to the Licensor shall be under the terms and conditions of
134
- this License, without any additional terms or conditions.
135
- Notwithstanding the above, nothing herein shall supersede or modify
136
- the terms of any separate license agreement you may have executed
137
- with Licensor regarding such Contributions.
138
-
139
- 6. Trademarks. This License does not grant permission to use the trade
140
- names, trademarks, service marks, or product names of the Licensor,
141
- except as required for reasonable and customary use in describing the
142
- origin of the Work and reproducing the content of the NOTICE file.
143
-
144
- 7. Disclaimer of Warranty. Unless required by applicable law or
145
- agreed to in writing, Licensor provides the Work (and each
146
- Contributor provides its Contributions) on an "AS IS" BASIS,
147
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
- implied, including, without limitation, any warranties or conditions
149
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
- PARTICULAR PURPOSE. You are solely responsible for determining the
151
- appropriateness of using or redistributing the Work and assume any
152
- risks associated with Your exercise of permissions under this License.
153
-
154
- 8. Limitation of Liability. In no event and under no legal theory,
155
- whether in tort (including negligence), contract, or otherwise,
156
- unless required by applicable law (such as deliberate and grossly
157
- negligent acts) or agreed to in writing, shall any Contributor be
158
- liable to You for damages, including any direct, indirect, special,
159
- incidental, or consequential damages of any character arising as a
160
- result of this License or out of the use or inability to use the
161
- Work (including but not limited to damages for loss of goodwill,
162
- work stoppage, computer failure or malfunction, or any and all
163
- other commercial damages or losses), even if such Contributor
164
- has been advised of the possibility of such damages.
165
-
166
- 9. Accepting Warranty or Additional Liability. While redistributing
167
- the Work or Derivative Works thereof, You may choose to offer,
168
- and charge a fee for, acceptance of support, warranty, indemnity,
169
- or other liability obligations and/or rights consistent with this
170
- License. However, in accepting such obligations, You may act only
171
- on Your own behalf and on Your sole responsibility, not on behalf
172
- of any other Contributor, and only if You agree to indemnify,
173
- defend, and hold each Contributor harmless for any liability
174
- incurred by, or claims asserted against, such Contributor by reason
175
- of your accepting any such warranty or additional liability.
176
-
177
- END OF TERMS AND CONDITIONS
178
-
179
- APPENDIX: How to apply the Apache License to your work.
180
-
181
- To apply the Apache License to your work, attach the following
182
- boilerplate notice, with the fields enclosed by brackets "[]"
183
- replaced with your own identifying information. (Don't include
184
- the brackets!) The text should be enclosed in the appropriate
185
- comment syntax for the file format. We also recommend that a
186
- file or class name and description of purpose be included on the
187
- same "printed page" as the copyright notice for easier
188
- identification within third-party archives.
189
-
190
- Copyright [yyyy] [name of copyright owner]
191
-
192
- Licensed under the Apache License, Version 2.0 (the "License");
193
- you may not use this file except in compliance with the License.
194
- You may obtain a copy of the License at
195
-
196
- http://www.apache.org/licenses/LICENSE-2.0
197
-
198
- Unless required by applicable law or agreed to in writing, software
199
- distributed under the License is distributed on an "AS IS" BASIS,
200
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201
- See the License for the specific language governing permissions and
202
- limitations under the License.