pimath 0.0.17 → 0.0.18

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.
@@ -33,10 +33,6 @@ export class Monom {
33
33
  // ------------------------------------------
34
34
  // Getter and setter
35
35
  // ------------------------------------------
36
- get isMonom() {
37
- return true;
38
- }
39
-
40
36
  /**
41
37
  * Get the coefficient as fraction
42
38
  */
@@ -311,10 +307,10 @@ export class Monom {
311
307
 
312
308
  let stack: Monom[] = [], m, pow, letter, q1, q2
313
309
 
314
- if(rpn.length===0){
310
+ if (rpn.length === 0) {
315
311
  this.zero()
316
312
  return this
317
- }else if(rpn.length===1){
313
+ } else if (rpn.length === 1) {
318
314
  const element = rpn[0]
319
315
 
320
316
  this.one()
@@ -324,9 +320,8 @@ export class Monom {
324
320
  this.setLetter(element.token, 1)
325
321
  }
326
322
  return this
327
- }else{
323
+ } else {
328
324
  // Reset the monom
329
- this.one()
330
325
  for (const element of rpn) {
331
326
  if (element.tokenType === 'coefficient') {
332
327
  let M = new Monom().one()
@@ -343,16 +338,15 @@ export class Monom {
343
338
  q2 = (stack.pop()) || new Monom().zero()
344
339
  q1 = (stack.pop()) || new Monom().zero()
345
340
 
346
- if(q1.isZero() && q2.isZero()){
347
- this.opposed()
348
- break
349
- }
350
341
  stack.push(q1.subtract(q2))
342
+
351
343
  break;
352
344
  case '*':
353
345
  // Get the last element in the stack
354
- m = (stack.pop()) || new Monom().one()
355
- this.multiply(m)
346
+ q2 = (stack.pop()) || new Monom().one()
347
+ q1 = (stack.pop()) || new Monom().one()
348
+
349
+ stack.push(q1.multiply(q2))
356
350
  break
357
351
  case '^':
358
352
  // get the two last elements in the stack
@@ -361,16 +355,20 @@ export class Monom {
361
355
 
362
356
  letter = m.variables[0]
363
357
 
364
- if(letter!==undefined) {
358
+ if (letter !== undefined) {
365
359
  m.setLetter(letter, pow)
366
360
  }
367
- this.multiply(m.clone())
361
+
362
+ stack.push(m)
363
+ // this.multiply(m.clone())
368
364
  break
369
365
  }
370
366
  }
371
367
  }
372
368
  }
373
369
 
370
+ this.one()
371
+ this.multiply(stack[0])
374
372
  return this
375
373
  }
376
374
  /**
@@ -388,6 +386,14 @@ export class Monom {
388
386
  return F;
389
387
  };
390
388
 
389
+ makeSame = (M: Monom):Monom => {
390
+ // Copy the literal parts.
391
+ for (let k in M._literal) {
392
+ this.setLetter(k, M._literal[k].clone());
393
+ }
394
+ return this
395
+ }
396
+
391
397
  /**
392
398
  * Create a zero value monom
393
399
  */
@@ -439,6 +445,9 @@ export class Monom {
439
445
  add = (...M: Monom[]): Monom => {
440
446
  for (let m of M) {
441
447
  if (this.isSameAs(m)) {
448
+ if(this.isZero()){
449
+ this.makeSame(m)
450
+ }
442
451
  this._coefficient.add(m.coefficient);
443
452
  } else {
444
453
  console.log('Add: Is not similar: ', m.display);
@@ -454,7 +463,10 @@ export class Monom {
454
463
  subtract = (...M: Monom[]): Monom => {
455
464
  for (let m of M) {
456
465
  if (this.isSameAs(m)) {
457
- this._coefficient.add(m.coefficient.clone().opposed());
466
+ if(this.isZero()){
467
+ this.makeSame(m)
468
+ }
469
+ this._coefficient.add(m.clone().coefficient.opposed());
458
470
  } else {
459
471
  console.log('Subtract: Is not similar: ', m.display);
460
472
  }
@@ -571,14 +583,17 @@ export class Monom {
571
583
  M2: string[] = M.variables,
572
584
  K: string[] = M1.concat(M2.filter((item) => M1.indexOf(item) < 0));
573
585
 
574
- for (let key of K) {
575
- // The setLetter is not available in one of the monom
576
- if (this._literal[key] === undefined || M.literal[key] === undefined) {
577
- return false;
578
- }
579
- // The setLetter does not have the isSame power in each monoms.
580
- if (!this._literal[key].isEqual(M.literal[key])) {
581
- return false;
586
+ // To compare, both must be different than zero.
587
+ if (!this.isZero() && !M.isZero()) {
588
+ for (let key of K) {
589
+ // The setLetter is not available in one of the monom
590
+ if (this._literal[key] === undefined || M.literal[key] === undefined) {
591
+ return false;
592
+ }
593
+ // The setLetter does not have the isSame power in each monoms.
594
+ if (!this._literal[key].isEqual(M.literal[key])) {
595
+ return false;
596
+ }
582
597
  }
583
598
  }
584
599
 
@@ -34,11 +34,6 @@ export class Polynom {
34
34
  return this;
35
35
  }
36
36
 
37
- get isPolynom() {
38
- return true;
39
- };
40
-
41
-
42
37
  // ------------------------------------------
43
38
  // Getter and setter
44
39
  // ------------------------------------------
@@ -365,9 +360,9 @@ export class Polynom {
365
360
  add = (...values: any[]): Polynom => {
366
361
 
367
362
  for (let value of values) {
368
- if (value.isPolynom) {
363
+ if (value instanceof Polynom) {
369
364
  this._monoms = this._monoms.concat(value.monoms);
370
- } else if (value.isMonom) {
365
+ } else if (value instanceof Monom) {
371
366
  this._monoms.push(value.clone());
372
367
  } else if (Number.isSafeInteger(value)) {
373
368
  this._monoms.push(new Monom(value.toString()));
@@ -382,9 +377,9 @@ export class Polynom {
382
377
  subtract = (...values: any[]): Polynom => {
383
378
 
384
379
  for (let value of values) {
385
- if (value.isPolynom) {
380
+ if (value instanceof Polynom) {
386
381
  this._monoms = this._monoms.concat(value.clone().opposed().monoms);
387
- } else if (value.isMonom) {
382
+ } else if (value instanceof Monom) {
388
383
  this._monoms.push(value.clone().opposed());
389
384
  } else if (Number.isSafeInteger(value)) {
390
385
  this._monoms.push(new Monom(value.toString()).opposed());
@@ -1100,7 +1095,7 @@ export class Polynom {
1100
1095
  // Reduce the polynom.
1101
1096
  const M = this.clone().reduce();
1102
1097
  for (const m of M._monoms) {
1103
- if (m.degree(letter) === degree) {
1098
+ if (m.degree(letter).isEqual(degree)) {
1104
1099
  return m.clone();
1105
1100
  }
1106
1101
  }
@@ -1,47 +1,134 @@
1
1
  import {Point} from "./point";
2
- import {Fraction} from "../coefficients/fraction";
3
- import {Equation} from "../algebra/equation";
4
- import {Polynom} from "../algebra/polynom";
2
+ import {Fraction} from "../coefficients";
3
+ import {Equation, Monom, Polynom} from "../algebra";
4
+ import {Line} from "./line";
5
+ import {Vector} from "./vector";
5
6
 
6
7
 
7
8
  export class Circle {
8
9
  private _center: Point;
9
10
  private _radius: Fraction;
11
+ private _squareRadius: Fraction;
12
+ private _cartesian: Equation;
10
13
  private _exists: boolean;
14
+
11
15
  constructor(...values: any) {
12
16
  this._exists = false
13
17
 
14
- if(values!==undefined){this.parse(...values)}
18
+ if (values !== undefined) {
19
+ this.parse(...values)
20
+ }
21
+ }
22
+
23
+
24
+ get center(): Point {
25
+ return this._center;
15
26
  }
16
27
 
17
28
  private parse(...values: any) {
18
- if(values.length===2){
29
+ if (values.length === 1 && typeof values[0] === 'string') {
30
+ this.checkCircle(new Equation(values[0]))
31
+ } else if (values.length >= 2) {
19
32
  this._center = new Point(values[0])
20
- this._radius = new Fraction(values[1])
33
+
34
+ if(values[1] instanceof Point){
35
+ // Go through this point
36
+ this._squareRadius = new Vector(this._center, values[1]).normSquare
37
+ }else {
38
+ if (values[2] === true) {
39
+ this._squareRadius = new Fraction(values[1])
40
+ } else {
41
+ this._radius = new Fraction(values[1])
42
+ this._squareRadius = this._radius.clone().pow(2)
43
+ }
44
+ }
45
+ this._cartesian = (new Equation(
46
+ new Polynom(`(x-(${this._center.x.display}))^2+(y-(${this._center.y.display}))^2`),
47
+ new Polynom(`${this._squareRadius.display}`)
48
+ )).moveLeft()
21
49
  }
22
50
  }
23
51
 
52
+ get radius(): { tex: string, display: string } {
53
+ if (this._squareRadius.isSquare()) {
54
+ return {
55
+ tex: this._squareRadius.clone().sqrt().tex,
56
+ display: this._squareRadius.clone().sqrt().display,
57
+ }
58
+ } else {
59
+ return {
60
+ tex: `\\sqrt{${this._squareRadius.tex}}`,
61
+ display: `sqrt(${this._squareRadius.display})`
62
+ }
63
+ }
64
+ return this._squareRadius
65
+ }
66
+
24
67
  get tex(): string {
25
68
  let cx, cy
26
- if(this._center.x.isZero()){
69
+ if (this._center.x.isZero()) {
27
70
  cx = 'x^2'
28
- }else{
29
- cx = `\\left(x-${this._center.x.tex}\\right)^2`
71
+ } else {
72
+ cx = `\\left(x${this._center.x.isNegative()?'+':'-'}${this._center.x.clone().abs().tex}\\right)^2`
30
73
  }
31
- if(this._center.y.isZero()){
74
+ if (this._center.y.isZero()) {
32
75
  cy = 'y^2'
33
- }else{
34
- cy = `\\left(y-${this._center.y.tex}\\right)^2`
76
+ } else {
77
+ cy = `\\left(y${this._center.y.isNegative()?'+':'-'}${this._center.y.clone().abs().tex}\\right)^2`
78
+ }
79
+ return `${cx}+${cy}=${this._squareRadius.tex}`
80
+ }
81
+
82
+ get developed(): string {
83
+ return this._cartesian.tex
84
+ }
85
+
86
+ checkCircle = (P: Equation): boolean => {
87
+ if (P.degree('x').value === 2 && P.degree('y').value === 2) {
88
+ // Both must be of degree 2.
89
+ let x2 = P.left.monomByDegree(2, 'x'),
90
+ y2 = P.left.monomByDegree(2, 'y'),
91
+ x1: Monom, y1: Monom, c: Monom
92
+
93
+ // Both square monoms must have the same coefficient.
94
+ if (x2.coefficient.isEqual(y2.coefficient)) {
95
+ P.divide(x2.coefficient)
96
+
97
+ x1 = P.left.monomByDegree(1, 'x')
98
+ y1 = P.left.monomByDegree(1, 'y')
99
+
100
+ c = P.left.monomByDegree(0)
101
+
102
+ this._center = new Point(
103
+ x1.coefficient.clone().divide(2).opposed(),
104
+ y1.coefficient.clone().divide(2).opposed()
105
+ )
106
+
107
+ this._squareRadius = c.coefficient.clone().opposed()
108
+ .add(this._center.x.clone().pow(2))
109
+ .add(this._center.y.clone().pow(2))
110
+
111
+ }
35
112
  }
36
- return `${cx}+${cy}=${this._radius.pow(2).tex}`
113
+
114
+ return false
37
115
  }
38
116
 
39
- get developed():string {
40
- let equ = new Equation(
41
- new Polynom(`(x-(${this._center.x.display}))^2+(y-(${this._center.y.display}))^2`),
42
- new Polynom(`${this._radius.pow(2).display}`)
43
- )
117
+ /**
118
+ * Get the relative position between circle and line. It corresponds to the number of intersection.
119
+ * @param {Line} L
120
+ * @returns {number}
121
+ */
122
+ relativePosition = (L: Line): number => {
123
+ let distance = L.distanceTo(this.center),
124
+ radius = Math.sqrt(this._squareRadius.value)
44
125
 
45
- return equ.moveLeft().tex;
126
+ if (distance.value-radius>0.0000000001) {
127
+ return 0 // external
128
+ } else if (Math.abs(distance.value-radius)<0.0000000001) {
129
+ return 1 // tangent
130
+ } else {
131
+ return 2 // external
132
+ }
46
133
  }
47
134
  }
@@ -2,12 +2,17 @@
2
2
  * This class works for 2d line in a plane.
3
3
  */
4
4
 
5
- import {Fraction} from "../coefficients/fraction";
5
+ import {Fraction} from "../coefficients";
6
6
  import {Vector} from "./vector";
7
7
  import {Point} from "./point";
8
- import {Polynom} from "../algebra/polynom";
8
+ import {Equation, Polynom} from "../algebra";
9
9
  import {Numeric} from "../numeric";
10
- import {Equation} from "../algebra/equation";
10
+
11
+ enum LinePropriety {
12
+ None,
13
+ Parallel,
14
+ Perpendicular
15
+ }
11
16
 
12
17
  export class Line {
13
18
  // A line is defined as the canonical form
@@ -20,19 +25,30 @@ export class Line {
20
25
  private _n: Vector;
21
26
  private _exists: boolean
22
27
 
28
+ private _referencePropriety: LinePropriety
29
+ private _referenceLine: Line
30
+
31
+ static PERPENDICULAR = LinePropriety.Perpendicular
32
+ static PARALLEL = LinePropriety.Parallel
33
+
23
34
  constructor(...values: any) {
24
35
 
25
36
  this._exists = false;
26
37
 
27
- if (values !== undefined) {
38
+ if (values.length > 0) {
28
39
  this.parse(...values);
29
40
  }
30
41
 
31
42
  return this;
32
43
  }
33
44
 
34
- get isLine():boolean {return true;}
35
- get exists(): boolean {return this._exists; }
45
+ get isLine(): boolean {
46
+ return true;
47
+ }
48
+
49
+ get exists(): boolean {
50
+ return this._exists;
51
+ }
36
52
 
37
53
  // ------------------------------------------
38
54
  // Getter and setter
@@ -40,6 +56,7 @@ export class Line {
40
56
  get equation(): Equation {
41
57
  return new Equation(new Polynom().parse('xy', this._a, this._b, this._c), new Polynom('0')).simplify();
42
58
  }
59
+
43
60
  get tex(): { canonical: string, mxh: string, parametric: string } {
44
61
  // canonical => ax + by + c = 0
45
62
  // mxh => y = -a/b x - c/b
@@ -47,7 +64,7 @@ export class Line {
47
64
 
48
65
  let canonical = this.equation;
49
66
  // Make sur the first item is positive.
50
- if(this._a.isNegative()){
67
+ if (this._a.isNegative()) {
51
68
  canonical.multiply(-1);
52
69
  }
53
70
 
@@ -102,6 +119,10 @@ export class Line {
102
119
  return new Vector(this._a, this._b);
103
120
  }
104
121
 
122
+ get director(): Vector {
123
+ return this._d.clone()
124
+ }
125
+
105
126
  set d(value: Vector) {
106
127
  this._d = value;
107
128
  }
@@ -117,61 +138,96 @@ export class Line {
117
138
  // ------------------------------------------
118
139
  // Creation / parsing functions
119
140
  // ------------------------------------------
120
- parse = (...values: any): Line => {
141
+ /**
142
+ * Parse data to a line
143
+ * @param {any} values
144
+ * @returns {Line}
145
+ */
146
+ parse = (...values: unknown[]): Line => {
121
147
  this._exists = false;
122
148
 
123
- if (values.length === 3) {
124
- return this.parseByCoefficient(values[0], values[1], values[2]);
125
- } else if (values.length === 2) {
126
- if (values[0].isPoint && values[1].isVector) {
149
+ // Nothing is given...
150
+ if (values.length === 0) {
151
+ return this
152
+ }
153
+
154
+ // One value only: already a line (clone it), an Equation, a string (as Equation)
155
+ if (values.length === 1) {
156
+ if (values[0] instanceof Line) {
157
+ // Already a Line
158
+ return values[0].clone()
159
+ } else if (values[0] instanceof Equation) {
160
+ // It's an Equation
161
+ return this.parseEquation(values[0])
162
+ } else if (typeof values[0] === "string") {
163
+ // It's a string - create an Equation from it.
164
+ try {
165
+ let E = new Equation(values[0])
166
+ return this.parse(E)
167
+ } catch (e) {
168
+ return this
169
+ }
170
+ }
171
+ }
172
+
173
+ if (values.length === 2) {
174
+ if (values[0] instanceof Point && values[1] instanceof Vector) {
127
175
  return this.parseByPointAndVector(values[0], values[1]);
128
- } else if (values[0].isPoint && values[1].isPoint) {
176
+ } else if (values[0] instanceof Point && values[1] instanceof Point) {
129
177
  return this.parseByPointAndVector(values[0], new Vector(values[0], values[1]));
178
+ } else if (values[0] instanceof Vector && values[1] instanceof Point) {
179
+ return this.parseByPointAndNormal(values[1], values[0])
130
180
  }
131
- } else if (values.length === 1){
132
- // It's a already a line - clone it !
133
- if(values[0].isLine){
134
- return values[0].clone();
135
- }
181
+ }
136
182
 
137
- // Maybe it's a string or an equation
138
- let equ = new Equation(values[0]);
139
- if(equ.isEquation){
140
- // Check if it's a valid equation.
141
- equ.reorder(true)
183
+ if (values.length === 3) {
184
+ if (
185
+ (values[0] instanceof Fraction || typeof values[0] === 'number')
186
+ &&
187
+ (values[1] instanceof Fraction || typeof values[1] === 'number')
188
+ &&
189
+ (values[2] instanceof Fraction || typeof values[2] === 'number')
190
+ ) {
191
+ return this.parseByCoefficient(values[0], values[1], values[2]);
192
+ }
193
+ }
142
194
 
143
- // It must contain either x, y or both.
144
- let letters = new Set(equ.letters());
195
+ // TODO: Add the ability to create line from a normal vector
196
+ console.log('Someting wrong happend while creating the line')
197
+ return this;
198
+ }
145
199
 
146
- // No 'x', no 'y' in the equations
147
- if(!(letters.has('x') || letters.has('y'))){return;}
200
+ parseEquation = (equ: Equation): Line => {
201
+ // Reorder the eequation
202
+ equ.reorder(true)
148
203
 
149
- // Another letter in the equation.
150
- for(let elem of ['x', 'y']){
151
- if(letters.has(elem)){
152
- letters.delete(elem)}
153
- }
204
+ // It must contain either x, y or both.
205
+ let letters = new Set(equ.letters());
154
206
 
155
- if(letters.size>0){
156
- console.log('Extra variable in the equation.')
157
- return this;
158
- }
207
+ // No 'x', no 'y' in the equations
208
+ if (!(letters.has('x') || letters.has('y'))) {
209
+ return this
210
+ }
159
211
 
160
- // Everything should be ok now...
161
- return this.parseByCoefficient(equ.left.monomByLetter('x').coefficient, equ.left.monomByLetter('y').coefficient, equ.left.monomByDegree(0).coefficient)
212
+ // Another letter in the equation ?
213
+ for (let elem of ['x', 'y']) {
214
+ if (letters.has(elem)) {
215
+ letters.delete(elem)
162
216
  }
163
217
  }
164
- // TODO: Add the ability to create line from a normal vector
165
- console.log('Someting wrong happend while creating the line')
166
- return this;
167
- }
168
218
 
169
- parseByCoefficient = (a: Fraction, b: Fraction, c: Fraction): Line => {
219
+ if (letters.size > 0) {
220
+ return this
221
+ }
222
+
223
+ // Everything should be ok now...
224
+ return this.parseByCoefficient(equ.left.monomByLetter('x').coefficient, equ.left.monomByLetter('y').coefficient, equ.left.monomByDegree(0).coefficient)
225
+ }
226
+ parseByCoefficient = (a: Fraction | number, b: Fraction | number, c: Fraction | number): Line => {
170
227
  this._a = new Fraction(a);
171
228
  this._b = new Fraction(b);
172
229
  this._c = new Fraction(c);
173
230
 
174
- // TODO: initialize direction and reference point
175
231
  this._d = new Vector(this._b.clone(), this._a.clone().opposed());
176
232
  this._OA = new Point(new Fraction().zero(), this._c.clone());
177
233
  this._n = this._d.clone().normal();
@@ -205,6 +261,31 @@ export class Line {
205
261
  return this;
206
262
  }
207
263
 
264
+ parseByPointAndNormal = (P: Point, n: Vector): Line => {
265
+ return this.parseByCoefficient(
266
+ n.x,
267
+ n.y,
268
+ P.x.clone().multiply(n.x)
269
+ .add(P.y.clone().multiply(n.y)).opposed()
270
+ )
271
+ }
272
+
273
+ parseByPointAndLine = (P: Point, L: Line, orientation?: LinePropriety): Line => {
274
+
275
+ if (orientation === undefined) {
276
+ orientation = LinePropriety.Parallel
277
+ }
278
+
279
+ if (orientation === LinePropriety.Parallel) {
280
+ return this.parseByPointAndNormal(P, L.normal)
281
+ } else if (orientation === LinePropriety.Perpendicular) {
282
+ return this.parseByPointAndNormal(P, L.director)
283
+ }
284
+
285
+ this._exists = false
286
+ return this
287
+ }
288
+
208
289
  clone = (): Line => {
209
290
  this._a = this._a.clone();
210
291
  this._b = this._b.clone();
@@ -214,6 +295,7 @@ export class Line {
214
295
  this._OA = this._OA.clone();
215
296
  this._n = this._n.clone();
216
297
 
298
+ this._exists = this.exists
217
299
  return this;
218
300
  }
219
301
  // ------------------------------------------
@@ -320,7 +402,7 @@ export class Line {
320
402
  )
321
403
 
322
404
  // There is an intersection point
323
- if(iPt.hasIntersection) {
405
+ if (iPt.hasIntersection) {
324
406
  return iPt.point.x.value >= Math.min(A.x.value, B.x.value)
325
407
  && iPt.point.x.value <= Math.max(A.x.value, B.x.value)
326
408
  && iPt.point.y.value >= Math.min(A.y.value, B.y.value)
@@ -328,36 +410,41 @@ export class Line {
328
410
  }
329
411
  return false;
330
412
  }
413
+
331
414
  // ------------------------------------------
332
415
  // Special functions
333
416
  // ------------------------------------------
334
- canonicalAsFloatCoefficient(decimals: number): string{
335
- if(decimals===undefined){
417
+ canonicalAsFloatCoefficient(decimals: number): string {
418
+ if (decimals === undefined) {
336
419
  decimals = 2;
337
420
  }
338
421
 
339
422
  let ca = this._a.value,
340
423
  cb = this._b.value,
341
- cc= this._c.value,
424
+ cc = this._c.value,
342
425
  canonical = '';
343
426
 
344
- if(!this._a.isZero()){
345
- if(this._a.isOne()){
427
+ if (!this._a.isZero()) {
428
+ if (this._a.isOne()) {
346
429
  canonical = 'x'
347
- }else if(this._a.clone().opposed().isOne()){
430
+ } else if (this._a.clone().opposed().isOne()) {
348
431
  canonical = '-x'
349
- }else{
350
- canonical = this._a.value.toFixed(decimals)+'x'
432
+ } else {
433
+ canonical = this._a.value.toFixed(decimals) + 'x'
351
434
  }
352
435
  }
353
436
 
354
- if(!this._b.isZero()){
355
- if(this._b.isPositive()){canonical+='+'}
437
+ if (!this._b.isZero()) {
438
+ if (this._b.isPositive()) {
439
+ canonical += '+'
440
+ }
356
441
  canonical += this._b.value.toFixed(decimals) + 'y'
357
442
  }
358
443
 
359
- if(!this._c.isZero()){
360
- if(this._c.isPositive()){canonical+='+'}
444
+ if (!this._c.isZero()) {
445
+ if (this._c.isPositive()) {
446
+ canonical += '+'
447
+ }
361
448
  canonical += this._c.value.toFixed(decimals)
362
449
  }
363
450
 
@@ -53,6 +53,15 @@ export class Point {
53
53
  return `\\left(${pts.join(';')}\\right)`
54
54
  }
55
55
 
56
+ get display(): string {
57
+ let pts = [];
58
+
59
+ pts.push(this._x.tex);
60
+ pts.push(this._y.tex);
61
+
62
+ return `(${pts.join(';')})`
63
+ }
64
+
56
65
  // ------------------------------------------
57
66
  // Creation / parsing functions
58
67
  // ------------------------------------------