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.
Files changed (75) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +7 -0
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Gemfile +8 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +616 -0
  9. data/Rakefile +6 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +8 -0
  12. data/lib/symath/definition/abs.rb +48 -0
  13. data/lib/symath/definition/arccos.rb +25 -0
  14. data/lib/symath/definition/arccot.rb +23 -0
  15. data/lib/symath/definition/arccsc.rb +24 -0
  16. data/lib/symath/definition/arcsec.rb +24 -0
  17. data/lib/symath/definition/arcsin.rb +25 -0
  18. data/lib/symath/definition/arctan.rb +23 -0
  19. data/lib/symath/definition/bounds.rb +39 -0
  20. data/lib/symath/definition/codiff.rb +31 -0
  21. data/lib/symath/definition/constant.rb +111 -0
  22. data/lib/symath/definition/cos.rb +17 -0
  23. data/lib/symath/definition/cot.rb +17 -0
  24. data/lib/symath/definition/csc.rb +17 -0
  25. data/lib/symath/definition/curl.rb +27 -0
  26. data/lib/symath/definition/d.rb +62 -0
  27. data/lib/symath/definition/div.rb +27 -0
  28. data/lib/symath/definition/exp.rb +112 -0
  29. data/lib/symath/definition/fact.rb +55 -0
  30. data/lib/symath/definition/flat.rb +31 -0
  31. data/lib/symath/definition/function.rb +197 -0
  32. data/lib/symath/definition/grad.rb +23 -0
  33. data/lib/symath/definition/hodge.rb +23 -0
  34. data/lib/symath/definition/int.rb +75 -0
  35. data/lib/symath/definition/laplacian.rb +23 -0
  36. data/lib/symath/definition/lmd.rb +97 -0
  37. data/lib/symath/definition/ln.rb +45 -0
  38. data/lib/symath/definition/number.rb +51 -0
  39. data/lib/symath/definition/operator.rb +228 -0
  40. data/lib/symath/definition/sec.rb +17 -0
  41. data/lib/symath/definition/sharp.rb +31 -0
  42. data/lib/symath/definition/sin.rb +17 -0
  43. data/lib/symath/definition/sqrt.rb +62 -0
  44. data/lib/symath/definition/tan.rb +17 -0
  45. data/lib/symath/definition/trig.rb +95 -0
  46. data/lib/symath/definition/variable.rb +284 -0
  47. data/lib/symath/definition/xd.rb +28 -0
  48. data/lib/symath/definition.rb +205 -0
  49. data/lib/symath/equation.rb +67 -0
  50. data/lib/symath/fraction.rb +177 -0
  51. data/lib/symath/matrix.rb +252 -0
  52. data/lib/symath/minus.rb +125 -0
  53. data/lib/symath/operation/differential.rb +167 -0
  54. data/lib/symath/operation/distributivelaw.rb +367 -0
  55. data/lib/symath/operation/exterior.rb +64 -0
  56. data/lib/symath/operation/integration.rb +329 -0
  57. data/lib/symath/operation/match.rb +166 -0
  58. data/lib/symath/operation/normalization.rb +458 -0
  59. data/lib/symath/operation.rb +36 -0
  60. data/lib/symath/operator.rb +163 -0
  61. data/lib/symath/parser.rb +473 -0
  62. data/lib/symath/parser.y +129 -0
  63. data/lib/symath/poly/dup.rb +835 -0
  64. data/lib/symath/poly/galois.rb +621 -0
  65. data/lib/symath/poly.rb +142 -0
  66. data/lib/symath/power.rb +224 -0
  67. data/lib/symath/product.rb +183 -0
  68. data/lib/symath/sum.rb +174 -0
  69. data/lib/symath/type.rb +282 -0
  70. data/lib/symath/value.rb +372 -0
  71. data/lib/symath/version.rb +3 -0
  72. data/lib/symath/wedge.rb +48 -0
  73. data/lib/symath.rb +157 -0
  74. data/symath.gemspec +39 -0
  75. metadata +160 -0
data/README.md ADDED
@@ -0,0 +1,616 @@
1
+ # SyMath
2
+
3
+ Rudimentary symbolic math library for Ruby. This gem is mainly intended
4
+ as a coding excercise. The operations have not been optimized for speed.
5
+ The current state of the project is 'under construction'.
6
+
7
+ # Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```
12
+ gem 'symath'
13
+ ```
14
+
15
+ Then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install symath
22
+
23
+ ## Usage
24
+
25
+ Using the library:
26
+
27
+ <pre>
28
+ > require 'SyMath'
29
+ </pre>
30
+
31
+ ### Simple introduction
32
+
33
+ A convenient way to explore the SyMath library is using the interactive
34
+ Ruby interpreter, irb:
35
+
36
+ <pre>
37
+ > # Load the symath library
38
+ > require 'symath'
39
+ => false
40
+ > # Add the symbols module to your environment
41
+ > extend SyMath::Definitions
42
+ => main
43
+ </pre>
44
+
45
+ You can now say, for example:
46
+
47
+ <pre>
48
+ > # Simplify an expression
49
+ > sin(:x) + 2*sin(:x)
50
+ => 3*sin(:x)
51
+ > # Derivative of tan(2*y + 3)
52
+ > (d(tan(2*:y + 3))/d(:y)).evaluate
53
+ => 2*(tan(2*y + 3)**2 + 1)
54
+ </pre>
55
+
56
+ Ruby symbols, :x and :y in the above example, are converted into
57
+ symbolic math variables and Ruby numbers are converted into symbolic
58
+ math numbers. Functions, operators and constants (e, pi, i, etc.) are
59
+ available as methods through the SyMath::Definitions module. In some cases
60
+ it is necessary to tell Ruby that your number or symbol is to be
61
+ understood as a symbolic object, and not just a Ruby number or
62
+ symbol. Use the to_m method to explicitly convert them to symbolic
63
+ bjects:
64
+
65
+ <pre>
66
+ > # Ruby integer math
67
+ > 3**4
68
+ => 81
69
+ > # SyMath symbolic math
70
+ > 3.to_m**4
71
+ => 3**4
72
+ > (3.to_m**4).normalize
73
+ => 81
74
+ </pre>
75
+
76
+ An complete expression can also be converted from a string, using the
77
+ same to_m method:
78
+
79
+ <pre>
80
+ > 'ln(e) + sin(pi/2)'.to_m
81
+ => ln(e) + sin(pi/2)
82
+ > 'ln(e) + sin(pi/2)'.to_m.normalize
83
+ => 2
84
+ </pre>
85
+
86
+ ### The SyMath::Definitions module
87
+
88
+ The module SyMath::Definitions is available to be included or extended
89
+ to your class or code block. It gives a Ruby method for each operator,
90
+ function and constant that exists, so they can be referred to by their
91
+ name, as in the code examples above. If you don't want to use the
92
+ module, functions, operators and constants must be referred to by the
93
+ fn, op and definition methods:
94
+
95
+ <pre>
96
+ > # Using the SyMath::Definitions methods
97
+ > sin(:x)
98
+ => sin(x)
99
+ > int(:x)
100
+ => int(x)
101
+ > e
102
+ => e
103
+ > sin
104
+ => sin(...)
105
+ > # Using the generic creator functions
106
+ > fn(:sin, :x)
107
+ => sin(x)
108
+ > op(:int, :x)
109
+ => int(x)
110
+ > definition(:e)
111
+ => e
112
+ > definition(:sin)
113
+ => sin(...)
114
+ </pre>
115
+
116
+ The SyMath::Definitions module is updated dynamically after the user has
117
+ defined new functions, operators and constants.
118
+
119
+ ### String representaton of symbolic objects
120
+
121
+ Symbolic math objects, inheriting from SyMath::Value, all have a to_s
122
+ method which returns a string representation of the object. The string
123
+ representation is compatible with the String.to_m method which
124
+ converts a string representation into a symbolic object:
125
+
126
+ <pre>
127
+ > (ln(e) + sin(pi/2)).to_s
128
+ => "ln(e) + sin(pi/2)"
129
+ > 'ln(e) + sin(pi/2)'.to_m
130
+ => ln(e) + sin(pi/2)
131
+ </pre>
132
+
133
+ SyMath::Value overrides the Object.inspect method, returning the to_s
134
+ representation rather than the more verbose and less readable
135
+ Object.inspect output. This behaviour can be disabled with the setting
136
+ 'inspect_to_s':
137
+
138
+ <pre>
139
+ > SyMath.setting(:inspect_to_s, false)
140
+ => false
141
+ > ln(e) + sin(pi/2)
142
+ => "#&lt;SyMath::Sum:0x000055e8a1d93b38 @definition=..."
143
+ </pre>
144
+
145
+ ### Simplification and normalizing
146
+
147
+ Simple reduction rules are automatically applied when composing an
148
+ expression. These can be disabled with the setting
149
+ 'compose_with_simplify'. More thorough reductions are done by the use
150
+ of the normalize method.
151
+
152
+ <pre>
153
+ > e*e*e*e
154
+ => e**4
155
+ > SyMath.setting(:compose_with_simplify, false)
156
+ => false
157
+ > e*e*e*e
158
+ => e*e*e*e
159
+ > sin(pi/2).normalize
160
+ => 1
161
+ </pre>
162
+
163
+ ### Functions
164
+
165
+ The library comes with a number of built-in function, which the system
166
+ knows how to derivate and integrate over. The built-in functions also
167
+ have a number of reduction rules which are applied by the reduce
168
+ method and also as part of the 'normalize' method. A list of the
169
+ defined functions is returned by the functions method. The description
170
+ method gives a small description of the function:
171
+
172
+ <pre>
173
+ > SyMath::Definition::Function.functions
174
+ => [sqrt, sin, cos, tan, sec, csc, cot, arcsin, arccos, arctan,
175
+ arcsec, arccsc, arccot, ln, exp, abs, fact, sinh, cosh, tanh,
176
+ coth, sech, csch, arsinh, arcosh, artanh, arcoth, arsech,
177
+ arcsch]
178
+ > sin.description
179
+ => "sin(x) - trigonometric sine"
180
+ </pre>
181
+
182
+ ### Defining functions
183
+
184
+ User-defined functions can be added by the method define_fn:
185
+
186
+ <pre>
187
+ > define_fn('poly', [:x, :y], :x**3 + :y**2 + 1)
188
+ => poly
189
+ </pre>
190
+
191
+ The user-defined function will now be available as a method in the
192
+ SyMath::Definitions module and can be used in expressions, just as the
193
+ built in functions. Functions defined by an expression can be
194
+ evaluated by the evaluate method, which returns the expression with
195
+ each free variable replaced with the input arguments to the function:
196
+
197
+ <pre>
198
+ > poly(2, 3).evaluate
199
+ => 2**3 + 3**2 + 1
200
+ > poly(3).evaluate.normalize
201
+ => 18
202
+ </pre>
203
+
204
+ ### Lambda functions
205
+
206
+ A nameless user-defined function can be created using the lmd
207
+ method. The method returns a function object which does not have a
208
+ name, but otherwise works as a function. The lambda function has
209
+ important usages in operators. Since they eturn a function as the
210
+ result, it will typically be a lambda function. Also, the lambda
211
+ function can be used for wrapping an expression into a function before
212
+ doing an integral or derivative, in this way telling which variables
213
+ the operator should work on. The lambda function can be called using
214
+ the call method or the Ruby 'call' operator '()':
215
+
216
+ <pre>
217
+ > l = lmd(:x**3 + :y**2 + 1, :x, :y)
218
+ > l.(2, 3)
219
+ => (x**3 + y**2 + 1).(2,3)
220
+ > l.(2, 3).evaluate
221
+ => 2**3 + 3**2 + 1
222
+ > l.(2, 3).evaluate.normalize
223
+ => 18
224
+ </pre>
225
+
226
+ ### Operators
227
+
228
+ The library has some built-in operators, i.e. functions which take
229
+ functions as arguments and return functions. A list of the defined
230
+ operators is returned by the operators method. The description method
231
+ gives a small description of the operator:
232
+
233
+ <pre>
234
+ > SyMath::Definition::Operator.operators
235
+ => [d(...), xd(...), int(...), [f](b,), #(), b(), hodge(...), grad(f),
236
+ curl(f), div(f), laplacian(f), codiff(f), laplace(f), fourier(f),
237
+ invfourier(f), dpart(f,t)]
238
+ > codiff.description
239
+ => "codiff(f) - codifferential of function f"
240
+ </pre>
241
+
242
+ ### Defining operators
243
+
244
+ User-defined operators can be added by the method define_op:
245
+
246
+ <pre>
247
+ > define_op('d2', [:f, :x], d(d(:f)/d(:x))/d(:x))
248
+ => d2
249
+ > d2(:x**3 + 2, :x).evaluate
250
+ => 6*x
251
+ </pre>
252
+
253
+ The user-defined function will now be available as a method in the
254
+ SyMath::Definitions module and can be used in expressions.
255
+
256
+ ### Evaluating functions and operators
257
+
258
+ Evaluating a functions or operators which is defined by an expression
259
+ returns the expression with each free variable replaced with input
260
+ arguments. Functions which do not have an expression will typically
261
+ evaluate to itself (no reduction). Most operators which do not have an
262
+ expression has a built in evaluation, and returns a function or
263
+ expression according to the operator.
264
+
265
+ ### Derivative
266
+
267
+ The d-operator returns the differential of a function or expresson. If
268
+ a function is given, the differential is made over all the free
269
+ variables of the function. If an expression is given, the operator
270
+ differentiates over the first free variable found in the
271
+ expression. Wrapping the expression into a lambda function makes it
272
+ possible to say which variables to differentiate over. Note that the
273
+ differential is an operator, so it returns the result in a a lambda
274
+ function, and not just the expression.
275
+
276
+ <pre>
277
+ > d(sin(:x)).evaluate
278
+ => cos(x)*dx.(x)
279
+ > d(:x**2 + :y**3 + 1).evaluate.normalize
280
+ => (2*x*dx).(x)
281
+ > d(lmd(:x**2 + :y**3 + 1, :y)).evaluate.normalize
282
+ => (3*y**2*dy).(y)
283
+ > d(lmd(:x**2 + :y**3 + 1, :x, :y)).evaluate.normalize
284
+ => (3*y**2*dy + 2*x*dx).(x,y)
285
+ </pre>
286
+
287
+ As a special case, the notatonal form d(f)/d(x) is recognized as the
288
+ derivative of f with regards to x. This is calculated as d(lmd(f,
289
+ x))/d(x). This evaluates to the derivative expression:
290
+
291
+ <pre>
292
+ > (d(:y**2 + :x**3 + 1)/d(:x)).evaluate
293
+ => 3*x**2
294
+ </pre>
295
+
296
+ The partial derivative is available as well as 'syntactic sugar':
297
+
298
+ <pre>
299
+ > dpart(:x**2 + :y**3 + 1, :x).evaluate.normalize
300
+ => 2*x
301
+ > dpart(:x**2 + :y**3 + 1, :y).evaluate.normalize
302
+ => 3*y**2
303
+ </pre>
304
+
305
+ ### Integration
306
+
307
+ Integration is available as the int-operator. With one argument, the
308
+ operator evaluates to the antiderivative of the expression:
309
+
310
+ <pre>
311
+ > int(2**:x).evaluate
312
+ => 2**x/ln(2) + C
313
+ </pre>
314
+
315
+ The variable C is used by convention to represent the free constant
316
+ factor of the antiderivative.
317
+
318
+ With three arguments, the int-operator evaluates to the definite
319
+ integral from a to b:
320
+
321
+ <pre>
322
+ > int(2**:x, 3, 4).evaluate.normalize
323
+ => 8/ln(2)
324
+ </pre>
325
+
326
+ ### Complex numbers and quaternions
327
+
328
+ The imaginary unit, i, is available as a constant, and can be used for
329
+ composing expressions with complex numbers. Simple reduction rules are
330
+ built in which reduces i*i to -1, and so on.
331
+
332
+ The basic quaternions, i, j, k are also available as constants. The
333
+ quaternion i is identical to the complex imaginary unit. Some simple
334
+ reduction rules are available for the quaternions as well.
335
+
336
+ ### Exterior algebra
337
+
338
+ Caveat: Exterior algebra and differential forms are not well
339
+ understood by the author of this code. The following has not been
340
+ reviewed by any others who understand the subject better than me, and
341
+ it may very well contain a lot of errors and misunderstandings.
342
+
343
+ D-forms can be defined in several ways. The following are equal:
344
+
345
+ <pre>
346
+ > # Using the to_d method on a scalar variable
347
+ > :x.to_m.to_d
348
+ => dx
349
+ > # Differentiating a scalar variable
350
+ > d(:x)
351
+ => dx
352
+ > # Creating a variable, and specifying the dform type
353
+ > :dx.to_m('dform')
354
+ => dx
355
+ </pre>
356
+
357
+ D-forms can be wedged together, forming n-forms (note that the ^
358
+ operator has lower preceedence in Ruby than in math, so parantheses
359
+ must be used, e.g. when adding):
360
+
361
+ <pre>
362
+ > d(:x)^d(:y)^d(:z)
363
+ => dx^dy^dz
364
+ > (d(:x)^d(:x)^d(:z)).normalize
365
+ => 0
366
+ </pre>
367
+
368
+ A vector variable can be defined using the to_m method, and specifying
369
+ the vector type.
370
+
371
+ <pre>
372
+ > :v.to_m('vector')
373
+ => v'
374
+ </pre>
375
+
376
+ The exterior derivative and related operators all work in a local
377
+ coordinate system defined by set of basic vectors of names :x1, :x2,
378
+ x3. The names can be changed by setting the built-in variable 'basis':
379
+
380
+ <pre>
381
+ > SyMath.get_variable('basis')
382
+ => [x1, x2, x3]
383
+ > SyMath.assign_variable('basis', [:x, :y, :z])
384
+ => {dx=>x', dy=>y', dz=>z'}
385
+ > SyMath.get_variable('basis')
386
+ => [x, y, z]
387
+ </pre>
388
+
389
+ The rest of this section assumes that the following scalars, vectors
390
+ and d-forms are defined:
391
+
392
+ <pre>
393
+ > SyMath.assign_variable('basis', [:x1, :x2, :x3])
394
+ => {dx1=>x1', dx2=>x2', dx3=>x3'}
395
+ > x1 = :x1.to_m
396
+ > x2 = :x2.to_m
397
+ > x3 = :x3.to_m
398
+ > x1v = :x1.to_m('vector')
399
+ > x2v = :x2.to_m('vector')
400
+ > x3v = :x3.to_m('vector')
401
+ > dx1 = :dx1.to_m('dform')
402
+ > dx2 = :dx2.to_m('dform')
403
+ > dx3 = :dx3.to_m('dform')
404
+ </pre>
405
+
406
+ The exterior derivative is available as the xd-operator:
407
+
408
+ <pre>
409
+ > xd(:x1 - :x1*:x2 + :x3**2).evaluate
410
+ => dx1 - (dx1*x2 + x1*dx2) + 2*x3*dx3
411
+ </pre>
412
+
413
+ The musical isomorphisms are available as the flat and sharp operators:
414
+
415
+ <pre>
416
+ > flat(x1v^x2v).evaluate
417
+ => dx1^dx2
418
+ > sharp(dx1^dx2).evaluate
419
+ => x1'^x2'
420
+ </pre>
421
+
422
+ The flat and sharp operators use the metric tensor in their
423
+ calculations. This is available a the built-in 'g' variable:
424
+
425
+ <pre>
426
+ > SyMath.get_variable(:g)
427
+ => [1, 0, 0; 0, 1, 0; 0, 0, 1]
428
+ </pre>
429
+
430
+ It can be changed using the SyMath.assign_variable(:g, [value]) method.
431
+
432
+ The hodge star operator is available as well:
433
+
434
+ <pre>
435
+ > hodge(dx1^dx2).evaluate
436
+ => dx3
437
+ > hodge(3).evaluate
438
+ => 3*dx1^dx2^dx3
439
+ </pre>
440
+
441
+ Gradient, curl, divergence, laplacian and co-differential are defined
442
+ from the above operators in the usual way:
443
+
444
+ <pre>
445
+ > grad(x1 - x1*x2 + x3**2).evaluate
446
+ => 2*x3*x3' - x2*x1' - x1*x2' + x1'
447
+ > curl(-x2*x1v + x1*x2*x2v + x3*x3v).evaluate
448
+ => x2*x3' + x3'
449
+ > div(-x2*x1v + x1*x2*x2v + x3*x3v).evaluate
450
+ => x1 + 1
451
+ > laplacian(x1**2 + x2**2 + x3**2).evaluate
452
+ => 6
453
+ > codiff(x1**2*(dx1^dx3) + x2**2*(dx3^dx1) + x3**2*(dx1^dx2)).evaluate
454
+ => 2*x1*dx3
455
+ </pre>
456
+
457
+ ### Vectors and matrices
458
+
459
+ Vectors can be defined in the coordinate array form by converting an
460
+ array to a math object, using the to_m method. Matrices can be created
461
+ the same way from two dimensional arrays:
462
+
463
+ <pre>
464
+ > m = [[1, 2, 3], [4, 5, 6]].to_m
465
+ > v = [-3, 4, 1].to_m
466
+ > m*v.transpose
467
+ => [1, 2, 3; 4, 5, 6]*[- 3; 4; 1]
468
+ > (m*v.transpose).evaluate
469
+ => [8; 14]
470
+ </pre>
471
+
472
+ The vector and matrix cells can of course contain symbolic expressions
473
+ instead of just numbers.
474
+
475
+ ### Methods for manipulating expressions
476
+
477
+ The library contains a few more complex expression manipulation
478
+ methods which are available to all math expression objects inheriting
479
+ from the SyMath::Value class (the root class of the expression
480
+ components).
481
+
482
+ #### Normalization
483
+
484
+ The normalization method tries to put an expression on a normal form,
485
+ based on some heuristics.
486
+
487
+ * Expressions formed by natural numbers are calculated.
488
+ * Fractions of natural numbers are simplified as far as possible.
489
+ * Products of equal factors are collapsed to power expressions.
490
+ * Products of powers with equal base are collapsed.
491
+ * Sums of equal terms are simplified to integer products.
492
+ * Product factors are ordered if permitted by commutativity.
493
+ * Sum terms are ordered.
494
+
495
+ <pre>
496
+ > # FIXME: Find some better examples
497
+ > (:x*4*:x*3*:y*:y**10).normalize
498
+ => 12*x**2*y**11
499
+ </pre>
500
+
501
+ #### Variable replacement
502
+
503
+ The replace method replaces takes a map of 'variable => expression' as
504
+ argument. It looks up all instances of the variables in the original
505
+ expression, and replaces them with the expressions given by the map:
506
+
507
+ <pre>
508
+ > (:x**:y).replace({:x.to_m => :a + 2, :y.to_m => :b + 3})
509
+ => (a + 2)**(b + 3)
510
+ </pre>
511
+
512
+ #### Matching and pattern replacement
513
+
514
+ The match method can be seen as a 'reverse' operation to
515
+ replace-method covered in the last section. It compares an expression
516
+ to a template expression containing some free variables. It returns an
517
+ array of all possible maps for the free variables in the template so
518
+ that it matches the original expression:
519
+
520
+ <pre>
521
+ > (:x**2 + :y**2 + 3).match(:a + :b, [:a.to_m, :b.to_m])
522
+ => [{a=>x**2, b=>y**2 + 3},
523
+ {a=>y**2, b=>x**2 + 3},
524
+ {a=>3, b=>x**2 + y**2},
525
+ {a=>x**2 + y**2, b=>3},
526
+ {a=>x**2 + 3, b=>y**2},
527
+ {a=>y**2 + 3, b=>x**2}]
528
+ </pre>
529
+
530
+ #### Factorization and product expansion
531
+
532
+ The factorization method has been ripped from the python
533
+ Py-library. It factorizes a polynomial of one variable:
534
+
535
+ <pre>
536
+ > (6*:x**2 + 24*:x**3 - 27*:x**4 + 18*:x**5 + 72*:x**6 - 9*:x).factorize
537
+ => 3*x*(2*x - 1)*(4*x + 3)*(3*x**3 + 1)
538
+ </pre>
539
+
540
+ The expand method expands the polynomial:
541
+
542
+ <pre>
543
+ > (3*:x*(2*:x - 1)*(4*:x + 3)*(3*:x**3 + 1)).expand.normalize
544
+ => 72*x**6 + 18*x**5 - 27*x**4 + 24*x**3 + 6*x**2 - 9*x
545
+ </pre>
546
+
547
+ ### Settings
548
+
549
+ The library has some global settings which change the behaviour of the system:
550
+
551
+ <pre>
552
+ > # List all settings
553
+ > SyMath.settings
554
+ => {
555
+ # Symbol used when a d-form is created
556
+ :d_symbol => "d",
557
+ # Symbol used when a vector is stringified
558
+ :vector_symbol => "'",
559
+ # Co-vector symbol used in a tensor type signature
560
+ :covector_symbol => ".",
561
+ # Show all parentheses when stringifying an expression
562
+ :expl_parentheses => false,
563
+ # Put square roots on exponent form
564
+ :sq_exponent_form => false,
565
+ # Put fractions on exponent form
566
+ :fraction_exponent_form => false,
567
+ # In latex strings, insert a product sign between the factors
568
+ :ltx_product_sign => false,
569
+ # Use simplification rules when expressions are composed
570
+ :compose_with_simplify => true,
571
+ # Use oo as complex infinity
572
+ :complex_arithmetic => true,
573
+ # Return to_s representation by the inspect method
574
+ :inspect_to_s => true,
575
+ # Maximum value of factorial which is normalized to a number
576
+ :max_calculated_factorial => 100
577
+ }
578
+ > # Show one setting
579
+ > SyMath.setting(:vector_symbol)
580
+ => "'"
581
+ > # Change a setting
582
+ > SyMath.setting(:vector_symbol, '¤')
583
+ => "¤"
584
+ </pre>
585
+
586
+ ## Development
587
+
588
+ After checking out the repo, run `bin/setup` to install
589
+ dependencies. Then, run `rake spec` to run the tests. You can also run
590
+ `bin/console` for an interactive prompt that will allow you to
591
+ experiment.
592
+
593
+ To install this gem onto your local machine, run `bundle exec rake
594
+ install`. To release a new version, update the version number in
595
+ `version.rb`, and then run `bundle exec rake release`, which will
596
+ create a git tag for the version, push git commits and tags, and push
597
+ the `.gem` file to [rubygems.org](https://rubygems.org).
598
+
599
+ ## Contributing
600
+
601
+ Bug reports and pull requests are welcome on GitHub at
602
+ https://github.com/erikoest/symath. This project is intended to be a
603
+ safe, welcoming space for collaboration, and contributors are expected
604
+ to adhere to the [Contributor
605
+ Covenant](http://contributor-covenant.org) code of conduct.
606
+
607
+ ## License
608
+
609
+ The gem is available as open source under the terms of the [MIT
610
+ License](https://opensource.org/licenses/MIT).
611
+
612
+ ## Code of Conduct
613
+
614
+ Everyone interacting in the SyMath project’s codebases, issue trackers,
615
+ chat rooms and mailing lists is expected to follow the [code of
616
+ conduct](https://github.com/erikoest/symath/blob/master/CODE_OF_CONDUCT.md).
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "sy"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,48 @@
1
+ require 'symath/definition/function'
2
+
3
+ module SyMath
4
+ class Definition::Abs < Definition::Function
5
+ def initialize()
6
+ super(:abs)
7
+ end
8
+
9
+ def description()
10
+ return 'abs(x) - absolute value'
11
+ end
12
+
13
+ def reduce_call(c)
14
+ arg = c.args[0]
15
+ if arg.is_nan?
16
+ return :nan.to_m
17
+ # Corner case, -oo is positive with complex arithmetic, so we need a
18
+ # specific check for that.
19
+ elsif arg.is_negative? or arg == -:oo
20
+ return - arg
21
+ elsif arg.is_positive? or arg.is_zero?
22
+ return arg
23
+ else
24
+ return c
25
+ end
26
+ end
27
+
28
+ def to_s(args = nil)
29
+ if args
30
+ arg = args[0].to_s
31
+ else
32
+ arg = '...'
33
+ end
34
+
35
+ return "|#{arg}|"
36
+ end
37
+
38
+ def to_latex(args = nil)
39
+ if args
40
+ arg = args[0].to_latex
41
+ else
42
+ arg = '...'
43
+ end
44
+
45
+ return "\\lvert#{arg}\\rvert"
46
+ end
47
+ end
48
+ end