symath 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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