symcalc 0.5

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 +7 -0
  2. data/lib/LICENSE.txt +95 -0
  3. data/lib/symcalc.rb +853 -0
  4. metadata +46 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: ab527844085471beb0057a27b9538997e055c41331a4d72791a3017ab3b0babb
4
+ data.tar.gz: 81d11403ccf1a3ebb81cc4af3ac952161f11d7213dbf9be1f0368db48f1380ba
5
+ SHA512:
6
+ metadata.gz: eea3871f2af7102b5c7718153d681efaf6ddc8761afdcacb1e65946f236f23e57e86994937c6937d9a773bc3bd6c701dcd7ecf36b0835a5446b39d7f0ec8dbab
7
+ data.tar.gz: a75450e788eebba783db6057055f8b2142df9e6a6663c2d97f6ce5b79267efde188d081b8f09d0c3b538758811aff6a4a638f66fd99e8f2d7487a94e62f1a799
data/lib/LICENSE.txt ADDED
@@ -0,0 +1,95 @@
1
+ Attribution-ShareAlike 4.0 International
2
+
3
+ By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution-ShareAlike 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions.
4
+
5
+ Section 1 – Definitions.
6
+
7
+ Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image.
8
+ Adapter's License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License.
9
+ BY-SA Compatible License means a license listed at creativecommons.org/compatiblelicenses , approved by Creative Commons as essentially the equivalent of this Public License.
10
+ Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights.
11
+ Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements.
12
+ Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material.
13
+ License Elements means the license attributes listed in the name of a Creative Commons Public License. The License Elements of this Public License are Attribution and ShareAlike.
14
+ Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License.
15
+ Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license.
16
+ Licensor means the individual(s) or entity(ies) granting rights under this Public License.
17
+ Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them.
18
+ Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world.
19
+ You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning.
20
+ Section 2 – Scope.
21
+
22
+ License grant .
23
+ Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to:
24
+ reproduce and Share the Licensed Material, in whole or in part; and
25
+ produce, reproduce, and Share Adapted Material.
26
+ Exceptions and Limitations . For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions.
27
+ Term . The term of this Public License is specified in Section 6(a) .
28
+ Media and formats; technical modifications allowed . The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material.
29
+ Downstream recipients .
30
+ Offer from the Licensor – Licensed Material . Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License.
31
+ Additional offer from the Licensor – Adapted Material . Every recipient of Adapted Material from You automatically receives an offer from the Licensor to exercise the Licensed Rights in the Adapted Material under the conditions of the Adapter’s License You apply.
32
+ No downstream restrictions . You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material.
33
+ No endorsement . Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i) .
34
+ Other rights .
35
+ Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise.
36
+ Patent and trademark rights are not licensed under this Public License.
37
+ To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties.
38
+ Section 3 – License Conditions.
39
+
40
+ Your exercise of the Licensed Rights is expressly made subject to the following conditions.
41
+
42
+ Attribution .
43
+ If You Share the Licensed Material (including in modified form), You must:
44
+
45
+ retain the following if it is supplied by the Licensor with the Licensed Material:
46
+ identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated);
47
+ a copyright notice;
48
+ a notice that refers to this Public License;
49
+ a notice that refers to the disclaimer of warranties;
50
+ a URI or hyperlink to the Licensed Material to the extent reasonably practicable;
51
+ indicate if You modified the Licensed Material and retain an indication of any previous modifications; and
52
+ indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License.
53
+ You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information.
54
+ If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable.
55
+ ShareAlike .
56
+ In addition to the conditions in Section 3(a) , if You Share Adapted Material You produce, the following conditions also apply.
57
+
58
+ The Adapter’s License You apply must be a Creative Commons license with the same License Elements, this version or later, or a BY-SA Compatible License.
59
+ You must include the text of, or the URI or hyperlink to, the Adapter's License You apply. You may satisfy this condition in any reasonable manner based on the medium, means, and context in which You Share Adapted Material.
60
+ You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, Adapted Material that restrict exercise of the rights granted under the Adapter's License You apply.
61
+ Section 4 – Sui Generis Database Rights.
62
+
63
+ Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material:
64
+
65
+ for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database;
66
+ if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material, including for purposes of Section 3(b) ; and
67
+ You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database.
68
+ For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights.
69
+
70
+ Section 5 – Disclaimer of Warranties and Limitation of Liability.
71
+
72
+ Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You.
73
+ To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You.
74
+ The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability.
75
+ Section 6 – Term and Termination.
76
+
77
+ This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically.
78
+ Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates:
79
+
80
+ automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or
81
+ upon express reinstatement by the Licensor.
82
+ For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License.
83
+
84
+ For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License.
85
+ Sections 1 , 5 , 6 , 7 , and 8 survive termination of this Public License.
86
+ Section 7 – Other Terms and Conditions.
87
+
88
+ The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed.
89
+ Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License.
90
+ Section 8 – Interpretation.
91
+
92
+ For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License.
93
+ To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions.
94
+ No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor.
95
+ Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority.
data/lib/symcalc.rb ADDED
@@ -0,0 +1,853 @@
1
+ #
2
+ #
3
+ # Copyright (c) 2023 Kyryl Shyshko
4
+ #
5
+ # This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License
6
+ # To view a copy of this license, visit https://creativecommons.org/licenses/by-sa/4.0/legalcode.en, or read LICENSE.txt
7
+ #
8
+ #
9
+
10
+
11
+ class Equation
12
+
13
+ def to_s
14
+ self.display()
15
+ end
16
+
17
+ def inspect
18
+ self.display()
19
+ end
20
+
21
+ def coerce(other)
22
+ [to_equation(other), self]
23
+ end
24
+
25
+ def *(eq)
26
+ return Multiplication.new(self, to_equation(eq))
27
+ end
28
+
29
+ def /(eq)
30
+ return Division.new(self, to_equation(eq))
31
+ end
32
+
33
+ def +(eq)
34
+ return Sum.new(self, to_equation(eq))
35
+ end
36
+
37
+ def -(eq)
38
+ return Subtraction.new(self, to_equation(eq))
39
+ end
40
+
41
+ def **(eq)
42
+ return Power.new(self, to_equation(eq))
43
+ end
44
+
45
+ # Calculate the derivative of the given function
46
+ # Accepts parameters order and variable.
47
+ # If the function has more than one dimensional, the variable needs to be provided
48
+ #
49
+ # Example:
50
+ # x = SymCalc.var("x")
51
+ # y = SymCalc.var("y")
52
+ # fx = x ** 2
53
+ # fx.derivative order: 2
54
+ #
55
+ # fxy = x ** 2 + 3 * y
56
+ # fxy.derivative variable: x
57
+ #
58
+ def derivative(order: 1, variable: nil)
59
+ if variable == nil && self.all_variables.size < 2
60
+ fx = self
61
+ order.times do
62
+ fx = fx.simplify.__derivative__.simplify
63
+ end
64
+ return fx
65
+ elsif variable == nil && self.all_variables.size > 1
66
+ raise "Expected a variable as input for a #{self.all_variables.size}-dimensional function"
67
+ else
68
+ fx = self
69
+ order.times do
70
+ fx = fx.simplify.__derivative__(variable: variable).simplify
71
+ end
72
+ return fx
73
+ end
74
+ end
75
+
76
+ def __simplify__
77
+ return self
78
+ end
79
+
80
+ # Simplifies the given function
81
+ # Accepts no arguments
82
+ #
83
+ def simplify
84
+
85
+ simplified = self.__simplify__
86
+
87
+ if [Multiplication, Division].include? simplified.class
88
+ m_els = simplified.__get_m_elements__(Hash.new)
89
+
90
+ if m_els.keys.size == 0
91
+ return EquationValue.new(0)
92
+ elsif m_els.keys.size == 1
93
+ part = m_els.keys[0]
94
+ power = to_equation(m_els.values[0])
95
+ if part == "exp"
96
+ part = (power == to_equation(1)) ? BasicVars::E : Exp.new(power)
97
+ else
98
+ part = part ** power if power != to_equation(1)
99
+ end
100
+ return part
101
+ else
102
+ els_index = 0
103
+ eq = nil
104
+
105
+ coeff = to_equation(1)
106
+
107
+ m_els.size.times do |els_index|
108
+ base = m_els.keys[els_index]
109
+ power = m_els[base]
110
+
111
+ base = base.simplify if base.is_a? Equation
112
+ power = power.simplify if power.is_a? Equation
113
+
114
+ base = to_equation base if base != "exp"
115
+ power = to_equation power
116
+
117
+ if power == to_equation(0)
118
+ next
119
+ end
120
+
121
+ if base.is_a?(EquationValue) && power.is_a?(EquationValue)
122
+ case power
123
+ when EquationValue.new(1)
124
+ coeff *= base
125
+ else
126
+ coeff *= base ** power
127
+ end
128
+ next
129
+ end
130
+
131
+ if base == "exp"
132
+ case power
133
+ when to_equation(1)
134
+ part = BasicVars::E
135
+ else
136
+ part = Exp.new(power)
137
+ end
138
+ else
139
+ case power
140
+ when to_equation(1)
141
+ part = base
142
+ else
143
+ part = base ** power
144
+ end
145
+ end
146
+
147
+ if eq == nil
148
+ eq = part
149
+ else
150
+ eq *= part
151
+ end
152
+
153
+ end
154
+
155
+ # coeff = to_equation(coeff.eval({}))
156
+ coeff = coeff.__simplify__
157
+
158
+
159
+ if coeff == to_equation(1)
160
+ eq = eq
161
+ elsif coeff == to_equation(0)
162
+ eq = to_equation(0)
163
+ elsif eq == nil
164
+ eq = coeff
165
+ else
166
+ eq = coeff * eq
167
+ end
168
+
169
+ return eq
170
+ end
171
+
172
+ else
173
+ return simplified
174
+ end
175
+
176
+ end
177
+
178
+
179
+ # Evaluates the function at given variable values
180
+ # Accepts the hash of variables and their values to evalualte the function
181
+ #
182
+ # Example:
183
+ # x = SymCalc.var("x")
184
+ # fx = x ** 2
185
+ # puts fx.eval(x: 3)
186
+ #
187
+ def eval(var_hash)
188
+ # begin
189
+ if var_hash.values.size == 0
190
+ return self.__eval__(Hash.new)
191
+ elsif !var_hash.values[0].is_a?(Array)
192
+ return self.__eval__ var_hash
193
+ elsif var_hash.values[0].is_a? Array
194
+ computed = []
195
+ var_hash.values[0].size.times do |i|
196
+ hash = var_hash.map {|k, v| [k, v[i]]}.to_h
197
+ computed << self.__eval__(hash)
198
+ end
199
+ return computed
200
+ end
201
+ # rescue
202
+ # puts "Could not compute the value. Skipping"
203
+ # return nil
204
+ # end
205
+ end
206
+
207
+
208
+
209
+
210
+ end
211
+
212
+ # Converts the given argument into the equation type, if not already an equation
213
+ def to_equation(eq)
214
+ if eq.is_a? Equation
215
+ eq
216
+ else
217
+ EquationValue.new(eq)
218
+ end
219
+ end
220
+
221
+ # Implements basic numeric values as the Equation class
222
+ class EquationValue < Equation
223
+
224
+ attr_accessor :value
225
+
226
+ def initialize(value)
227
+ @value = value
228
+ end
229
+
230
+ def display
231
+ return @value.to_s
232
+ end
233
+
234
+ def __eval__ var_hash
235
+ return @value.to_f
236
+ end
237
+
238
+ def __derivative__ variable: nil
239
+ return EquationValue.new 0
240
+ end
241
+
242
+ def ==(value)
243
+ if value.is_a? EquationValue
244
+ return @value == value.value
245
+ else
246
+ return false
247
+ end
248
+ end
249
+
250
+ def hash
251
+ 1111000 + @value
252
+ end
253
+
254
+ def eql? obj
255
+ return false if !obj.is_a? EquationValue
256
+ return obj.value == @value
257
+ end
258
+
259
+ def __get_m_elements__ var_hash
260
+ if var_hash.keys.include? self
261
+ var_hash[self] = var_hash[self] + 1
262
+ else
263
+ var_hash[self] = 1
264
+ end
265
+ return var_hash
266
+ end
267
+
268
+
269
+ def all_variables
270
+ []
271
+ end
272
+
273
+ end
274
+
275
+ # Implements the Variable class
276
+ class Variable < Equation
277
+
278
+ # Create a new symbolic variable with a custom display name, and an optional fixed value
279
+ # Accepts the display name and an optional fixed_value
280
+ # Fixed_value works in the cases of evaluating the function
281
+ #
282
+ # Example:
283
+ # x = Variable.new "x", 5
284
+ # fx = x ** 2
285
+ # fx.eval # => 25
286
+ #
287
+ def initialize name, fixed_value = nil
288
+ @name = name
289
+ @fixed_value = fixed_value
290
+ end
291
+
292
+ def display
293
+ return @name
294
+ end
295
+
296
+
297
+ def __eval__ var_hash
298
+ if @fixed_value
299
+ return @fixed_value
300
+ elsif var_hash.keys.include?(@name.to_sym) or var_hash.keys.include?(@name.to_s)
301
+ return (var_hash[@name.to_sym] or var_hash[@name.to_s])
302
+ else
303
+ raise "No value provided for #{@name.to_s} in eval"
304
+ # return nil
305
+ end
306
+ end
307
+
308
+ def __derivative__ variable: nil
309
+ if variable == nil || variable == self
310
+ return EquationValue.new 1
311
+ else
312
+ return to_equation(0)
313
+ end
314
+ end
315
+
316
+ def __get_m_elements__ var_hash
317
+ if var_hash.keys.include? self
318
+ var_hash[self] += 1
319
+ else
320
+ var_hash[self] = 1
321
+ end
322
+ return var_hash
323
+ end
324
+
325
+ def all_variables
326
+ return [self]
327
+ end
328
+ end
329
+
330
+ # Basic variables that are already implemented in SymCalc and have fixed values
331
+ module BasicVars
332
+
333
+ E = Variable.new "e", Math::E
334
+
335
+ end
336
+
337
+ # Implements sum operations in SymCalc
338
+ class Sum < Equation
339
+ def initialize lside, rside
340
+ @lside = lside
341
+ @rside = rside
342
+ end
343
+
344
+ def display
345
+ return "(#{@lside.display}) + (#{@rside.display})"
346
+ end
347
+
348
+ def __eval__ var_hash
349
+ return @lside.eval(var_hash) + @rside.eval(var_hash)
350
+ end
351
+
352
+ def __derivative__ variable: nil
353
+ return @lside.derivative(variable: variable) + @rside.derivative(variable: variable)
354
+ end
355
+
356
+ def __simplify__
357
+ @lside = @lside.__simplify__
358
+ @rside = @rside.__simplify__
359
+ if (@lside.is_a? EquationValue) && (@rside.is_a? EquationValue)
360
+ return EquationValue.new((@lside + @rside).eval({}))
361
+ elsif @lside == EquationValue.new(0)
362
+ return @rside
363
+ elsif @rside == EquationValue.new(0)
364
+ return @lside
365
+ else
366
+ return self
367
+ end
368
+ end
369
+
370
+ def __get_m_elements__ el_hash
371
+ if el_hash.keys.include? self
372
+ el_hash[self] += 1
373
+ else
374
+ el_hash[self] = 1
375
+ end
376
+ return el_hash
377
+ end
378
+
379
+ def all_variables
380
+ return (@lside.all_variables + @rside.all_variables).uniq
381
+ end
382
+
383
+ end
384
+
385
+ # Implements the subtraction operation in SymCalc
386
+ class Subtraction < Equation
387
+ def initialize lside, rside
388
+ @lside = lside
389
+ @rside = rside
390
+ end
391
+
392
+ def display
393
+ return "(#{@lside.display}) - (#{@rside.display})"
394
+ end
395
+
396
+ def __eval__ var_hash
397
+ return @lside.eval(var_hash) - @rside.eval(var_hash)
398
+ end
399
+
400
+ def __derivative__ variable: nil
401
+ return @lside.derivative(variable: variable) - @rside.derivative(variable: variable)
402
+ end
403
+
404
+ def __get_m_elements__ el_hash
405
+ if el_hash.keys.include? self
406
+ el_hash[self] += 1
407
+ else
408
+ el_hash[self] = 1
409
+ end
410
+ return el_hash
411
+ end
412
+
413
+ def __simplify__
414
+ @lside = @lside.__simplify__
415
+ @rside = @rside.__simplify__
416
+
417
+ if (@lside.is_a? EquationValue) && (@rside.is_a? EquationValue)
418
+ return EquationValue.new((@lside - @rside).eval(Hash.new))
419
+ elsif @rside == EquationValue.new(0)
420
+ return @lside
421
+ elsif @lside == EquationValue.new(0)
422
+ return -1 * @rside
423
+ elsif @lside == @rside
424
+ return EquationValue.new(0)
425
+ else
426
+ return self
427
+ end
428
+ end
429
+
430
+ def all_variables
431
+ return (@lside.all_variables + @rside.all_variables).uniq
432
+ end
433
+ end
434
+
435
+ # Implements the multiplication operation in SymCalc
436
+ class Multiplication < Equation
437
+ def initialize lside, rside
438
+ @lside = lside
439
+ @rside = rside
440
+ end
441
+
442
+ def display
443
+ return "(#{@lside.display}) * (#{@rside.display})"
444
+ end
445
+
446
+ def __eval__ var_hash
447
+ return @lside.eval(var_hash) * @rside.eval(var_hash)
448
+ end
449
+
450
+ def __derivative__ variable: nil
451
+ return @lside.derivative(variable: variable) * @rside + @lside * @rside.derivative(variable: variable)
452
+ end
453
+
454
+ def __simplify__
455
+ @lside = @lside.__simplify__
456
+ @rside = @rside.__simplify__
457
+
458
+ if (@lside == EquationValue.new(0)) || (@rside == EquationValue.new(0))
459
+ return EquationValue.new 0
460
+ elsif @lside == EquationValue.new(1)
461
+ return @rside
462
+ elsif @rside == EquationValue.new(1)
463
+ return @lside
464
+ elsif @rside.is_a? EquationValue and @lside.is_a? EquationValue
465
+ calculated = @rside.value * @lside.value
466
+ if calculated.to_s.size <= 6
467
+ return to_equation(calculated)
468
+ else
469
+ return self
470
+ end
471
+ else
472
+ return self
473
+ end
474
+ end
475
+
476
+ def __get_m_elements__ el_hash
477
+ el_hash = @lside.__get_m_elements__(el_hash)
478
+ el_hash = @rside.__get_m_elements__(el_hash)
479
+ return el_hash
480
+ end
481
+
482
+ def all_variables
483
+ return (@lside.all_variables + @rside.all_variables).uniq
484
+ end
485
+ end
486
+
487
+ # Implements the division operation in SymCalc
488
+ class Division < Equation
489
+
490
+ attr_accessor :lside, :rside
491
+
492
+ def initialize lside, rside
493
+ @lside = lside
494
+ @rside = rside
495
+ end
496
+
497
+ def display
498
+ return "(#{@lside.display}) / (#{@rside.display})"
499
+ end
500
+
501
+ def __eval__ var_hash
502
+ return @lside.eval(var_hash) / @rside.eval(var_hash)
503
+ end
504
+
505
+ def __derivative__ variable: nil
506
+ return (@lside * @rside ** (-1)).derivative(variable: variable)
507
+ # return (@lside.derivative * @rside - @lside * @rside.derivative) / (@rside ** 2)
508
+ end
509
+
510
+ def __simplify__
511
+ @lside = @lside.__simplify__
512
+ @rside = @rside.__simplify__
513
+ if @lside == EquationValue.new(0)
514
+ return EquationValue.new(0)
515
+ else
516
+ return self
517
+ end
518
+ end
519
+
520
+ def == eq
521
+ if eq.is_a? Division
522
+ return (eq.lside == @rside) && (eq.lside == @lside)
523
+ else
524
+ return false
525
+ end
526
+ end
527
+
528
+ def __get_m_elements__ el_hash
529
+ mult = @lside.__get_m_elements__(el_hash)
530
+ div = @rside.__get_m_elements__({})
531
+ div.each do |k, v|
532
+ if mult.keys.include? k
533
+ mult[k] -= v
534
+ else
535
+ mult[k] = -v
536
+ end
537
+ end
538
+ return mult
539
+ end
540
+
541
+
542
+ def all_variables
543
+ return (@lside.all_variables + @rside.all_variables).uniq
544
+ end
545
+ end
546
+
547
+
548
+ # Implements the exp operation in SymCalc
549
+ class Exp < Equation
550
+
551
+ attr_accessor :power
552
+
553
+ def initialize power
554
+ @power = to_equation(power)
555
+ end
556
+
557
+ def display
558
+ return "exp(#{@power.display})"
559
+ end
560
+
561
+ def __eval__ var_hash
562
+ return Math::E ** (@power.eval(var_hash))
563
+ end
564
+
565
+ def __derivative__ variable: nil
566
+ return Exp.new(@power) * @power.derivative(variable: variable)
567
+ end
568
+
569
+ def == eq
570
+ if eq.is_a? Exp
571
+ return eq.power == @power
572
+ else
573
+ return false
574
+ end
575
+ end
576
+
577
+ def __simplify__
578
+ if @power.is_a? Ln
579
+ return @power.eq
580
+ elsif @power.is_a?(Log) && @power.base == BasicVars::E
581
+ return @power.eq
582
+ else
583
+ return self
584
+ end
585
+ end
586
+
587
+ def __get_m_elements__ var_hash
588
+ if var_hash.keys.include? "exp"
589
+ var_hash["exp"] += @power
590
+ else
591
+ var_hash["exp"] = @power
592
+ end
593
+ return var_hash
594
+ end
595
+
596
+ def all_variables
597
+ return @power.all_variables
598
+ end
599
+ end
600
+
601
+
602
+ # Implements the power operation in SymCalc
603
+ class Power < Equation
604
+
605
+ attr_accessor :base, :power
606
+
607
+ def initialize base, power
608
+ @base = base
609
+ @power = power
610
+ end
611
+
612
+ def display
613
+ return "(#{@base.display})^(#{@power.display})"
614
+ end
615
+
616
+ def __eval__ var_hash
617
+ return @base.eval(var_hash) ** @power.eval(var_hash)
618
+ end
619
+
620
+ def __derivative__ variable: nil
621
+ # exp(@power * ln(@base)).derivative
622
+ return @base ** @power * (@power.derivative(variable: variable)*Ln.new(@base) + @base.derivative(variable: variable)*@power/@base)
623
+ end
624
+
625
+ def == eq
626
+ if eq.is_a? Power
627
+ return (eq.base == @base) && (eq.power == @power)
628
+ elsif (eq == EquationValue.new(1))
629
+ return @power == 0
630
+ else
631
+ return false
632
+ end
633
+ end
634
+
635
+ def __get_m_elements__ var_hash
636
+ elms = @base.__get_m_elements__({})
637
+ elms.each do |k, v|
638
+ if var_hash.keys.include? k
639
+ var_hash[k] += v * @power
640
+ else
641
+ var_hash[k] = @power
642
+ end
643
+ end
644
+ return var_hash
645
+ end
646
+
647
+ def __simplify__
648
+
649
+ base = @base.simplify
650
+ power = @power.simplify
651
+
652
+ if power == EquationValue.new(0)
653
+ return EquationValue.new(1)
654
+ elsif power == EquationValue.new(1)
655
+ return base
656
+ elsif base.is_a?(EquationValue) && power.is_a?(EquationValue)
657
+ computed = base.value ** power.value
658
+ if computed.to_s.size <= 6
659
+ return to_equation(computed)
660
+ else
661
+ return base ** power
662
+ end
663
+ else
664
+ return base ** power
665
+ end
666
+
667
+ end
668
+
669
+ def all_variables
670
+ return (@base.all_variables + @power.all_variables).uniq
671
+ end
672
+ end
673
+
674
+ # Implements the sin operation in SymCalc
675
+ class Sin < Equation
676
+ def initialize eq
677
+ @eq = eq
678
+ end
679
+
680
+ def display
681
+ return "sin(#{@eq.display})"
682
+ end
683
+
684
+ def __eval__ var_hash
685
+ return Math.sin(@eq.eval(var_hash))
686
+ end
687
+
688
+ def __derivative__ variable: nil
689
+ return Cos.new(@eq) * @eq.derivative(variable: variable)
690
+ end
691
+
692
+ def __get_m_elements__ var_hash
693
+ if var_hash.keys.include? self
694
+ var_hash[self] += 1
695
+ else
696
+ var_hash[self] = 1
697
+ end
698
+ return var_hash
699
+ end
700
+
701
+ def all_variables
702
+ return @eq.all_variables
703
+ end
704
+ end
705
+
706
+ # Implements the cos operation in SymCalc
707
+ class Cos < Equation
708
+ def initialize eq
709
+ @eq = eq
710
+ end
711
+
712
+ def display
713
+ return "cos(#{@eq.display})"
714
+ end
715
+
716
+ def __eval__ var_hash
717
+ return Math.cos(@eq.eval(var_hash))
718
+ end
719
+
720
+ def __derivative__ variable: nil
721
+ return -1 * Sin.new(@eq) * @eq.derivative(variable: variable)
722
+ end
723
+
724
+ def __get_m_elements__ var_hash
725
+ if var_hash.keys.include? self
726
+ var_hash[self] += 1
727
+ else
728
+ var_hash[self] = 1
729
+ end
730
+ return var_hash
731
+ end
732
+
733
+ def all_variables
734
+ return @eq.all_variables
735
+ end
736
+ end
737
+
738
+
739
+ # Implements the natural logarithm operation in SymCalc
740
+ class Ln < Equation
741
+
742
+ attr_accessor :eq
743
+
744
+ def initialize eq
745
+ @eq = eq
746
+ end
747
+
748
+ def display
749
+ return "ln(#{@eq.display})"
750
+ end
751
+
752
+ def __eval__ var_hash
753
+ return Math.log(@eq.eval(var_hash), Math::E)
754
+ end
755
+
756
+ def __derivative__ variable: nil
757
+ return 1 / @eq * @eq.derivative(variable: variable)
758
+ end
759
+
760
+ def __get_m_elements__ var_hash
761
+ if var_hash.keys.include? self
762
+ var_hash[self] += 1
763
+ else
764
+ var_hash[self] = 1
765
+ end
766
+ return var_hash
767
+ end
768
+
769
+ def all_variables
770
+ return @eq.all_variables
771
+ end
772
+ end
773
+
774
+ # Implements the logarithm operation in SymCalc
775
+ class Log < Equation
776
+
777
+ attr_accessor :eq, :base
778
+
779
+ # Implements the subtraction operation in SymCalc
780
+ # Accepts the base and equation arguments
781
+ #
782
+ # Example:
783
+ #
784
+ # x = SymCalc.var("x")
785
+ # fx = Log.new 10, x ** 2
786
+ #
787
+ def initialize base, eq
788
+ @base = to_equation(base)
789
+ @eq = to_equation(eq)
790
+ end
791
+
792
+ def display
793
+ return "log_(#{@base.display})(#{@eq.display})"
794
+ end
795
+
796
+ def __eval__ var_hash
797
+ return Math.log(@eq.eval(var_hash), @base.eval(var_hash))
798
+ end
799
+
800
+ def __derivative__ variable: nil
801
+ return 1 / (Ln.new(@base) * @eq) * @eq.derivative(variable: variable)
802
+ end
803
+
804
+ def __get_m_elements__ var_hash
805
+ if var_hash.keys.include? self
806
+ var_hash[self] += 1
807
+ else
808
+ var_hash[self] = 1
809
+ end
810
+ return var_hash
811
+ end
812
+
813
+ def all_variables
814
+ return (@base.all_variables + @eq.all_variables).uniq
815
+ end
816
+ end
817
+
818
+ # The SymCalc module implemenets the standard function class creations shorter
819
+ module SymCalc
820
+
821
+ # sin(equation) is the same as Sin.new(equation), just shorter
822
+ def sin(eq)
823
+ return Sin.new(to_equation(eq))
824
+ end
825
+
826
+ # cos(equation) is the same as Cos.new(equation), just shorter
827
+ def cos(eq)
828
+ return Cos.new(to_equation(eq))
829
+ end
830
+
831
+ # ln(equation) is the same as Ln.new(equation), just shorter
832
+ def ln(eq)
833
+ return Ln.new(to_equation(eq))
834
+ end
835
+
836
+ # log(base, equation) is the same as Log.new(base, equation), just shorter
837
+ def log(base, eq)
838
+ return Log.new(to_equation(base), to_equation(eq))
839
+ end
840
+
841
+ # exp(equation) is the same as Exp.new(equation), just shorter
842
+ def exp(power)
843
+ return Exp.new(to_equation(power))
844
+ end
845
+
846
+ # var(name) is the same as Variable.new(name), just shorter
847
+ def var(name, fixed_value=nil)
848
+ Variable.new name, fixed_value
849
+ end
850
+
851
+ module_function :sin, :cos, :ln, :log, :exp, :var
852
+
853
+ end
metadata ADDED
@@ -0,0 +1,46 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: symcalc
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.5'
5
+ platform: ruby
6
+ authors:
7
+ - Kyryl Shyshko
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-11-16 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: SymCalc empowers developers with the ability of creating symbolic functions,
14
+ deriving them and evaluating them, substituting variables for numbers.
15
+ email: kyryloshy@gmail.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - lib/LICENSE.txt
21
+ - lib/symcalc.rb
22
+ homepage: https://rubygems.org/gems/symcalc
23
+ licenses:
24
+ - CC BY-SA 4.0
25
+ metadata: {}
26
+ post_install_message:
27
+ rdoc_options: []
28
+ require_paths:
29
+ - lib
30
+ required_ruby_version: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ required_rubygems_version: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ requirements: []
41
+ rubygems_version: 3.3.11
42
+ signing_key:
43
+ specification_version: 4
44
+ summary: SymCalc is a Ruby gem for symbolic mathematics that implements basic calculus
45
+ in code.
46
+ test_files: []