pimath 0.1.39 → 0.2.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 (113) hide show
  1. package/dist/pimath.js +3127 -2865
  2. package/dist/pimath.js.map +1 -1
  3. package/package.json +16 -12
  4. package/src/algebra/equation.ts +558 -0
  5. package/src/algebra/equationSolver.ts +488 -0
  6. package/src/algebra/factor.ts +338 -0
  7. package/src/algebra/index.ts +11 -0
  8. package/src/algebra/linearSystem.ts +439 -0
  9. package/src/algebra/logicalset.ts +255 -0
  10. package/src/algebra/matrix.ts +474 -0
  11. package/src/algebra/monom.ts +977 -0
  12. package/src/algebra/operations.ts +23 -0
  13. package/src/algebra/polyFactor.ts +668 -0
  14. package/src/algebra/polynom.ts +1247 -0
  15. package/src/analyze/index.ts +4 -0
  16. package/src/analyze/solution.ts +178 -0
  17. package/src/analyze/tableOfSigns.ts +30 -0
  18. package/src/coefficients/fraction.ts +718 -0
  19. package/src/coefficients/index.ts +4 -0
  20. package/src/coefficients/root.ts +346 -0
  21. package/src/geometry/TupleN.ts +128 -0
  22. package/src/geometry/circle.ts +456 -0
  23. package/src/geometry/geomMath.ts +71 -0
  24. package/src/geometry/index.ts +11 -0
  25. package/src/geometry/line.ts +653 -0
  26. package/src/geometry/line3.ts +211 -0
  27. package/src/geometry/plane3.ts +179 -0
  28. package/src/geometry/point.ts +104 -0
  29. package/src/geometry/sphere3.ts +214 -0
  30. package/src/geometry/triangle.ts +482 -0
  31. package/src/geometry/vector.ts +225 -0
  32. package/src/helpers.ts +35 -0
  33. package/src/index.ts +61 -0
  34. package/src/numeric.ts +196 -0
  35. package/src/pimath.interface.ts +162 -0
  36. package/src/randomization/algebra/rndEquation.ts +41 -0
  37. package/src/randomization/algebra/rndMonom.ts +39 -0
  38. package/src/randomization/algebra/rndPolynom.ts +100 -0
  39. package/src/randomization/coefficient/rndFraction.ts +38 -0
  40. package/src/randomization/geometry/rndCircle.ts +27 -0
  41. package/src/randomization/geometry/rndLine.ts +35 -0
  42. package/src/randomization/geometry/rndLine3.ts +27 -0
  43. package/src/randomization/geometry/rndVector.ts +63 -0
  44. package/src/randomization/random.ts +89 -0
  45. package/src/randomization/rndHelpers.ts +102 -0
  46. package/src/randomization/rndTypes.ts +67 -0
  47. package/types/algebra/equation.d.ts +18 -17
  48. package/types/algebra/equation.d.ts.map +1 -1
  49. package/types/algebra/equationSolver.d.ts +7 -3
  50. package/types/algebra/equationSolver.d.ts.map +1 -1
  51. package/types/algebra/factor.d.ts +1 -1
  52. package/types/algebra/factor.d.ts.map +1 -1
  53. package/types/algebra/linearSystem.d.ts +23 -6
  54. package/types/algebra/linearSystem.d.ts.map +1 -1
  55. package/types/algebra/logicalset.d.ts +1 -1
  56. package/types/algebra/logicalset.d.ts.map +1 -1
  57. package/types/algebra/monom.d.ts +1 -6
  58. package/types/algebra/monom.d.ts.map +1 -1
  59. package/types/algebra/operations.d.ts.map +1 -1
  60. package/types/algebra/polyFactor.d.ts +9 -4
  61. package/types/algebra/polyFactor.d.ts.map +1 -1
  62. package/types/algebra/polynom.d.ts +10 -7
  63. package/types/algebra/polynom.d.ts.map +1 -1
  64. package/types/analyze/index.d.ts +2 -0
  65. package/types/analyze/index.d.ts.map +1 -0
  66. package/types/analyze/solution.d.ts +27 -0
  67. package/types/analyze/solution.d.ts.map +1 -0
  68. package/types/analyze/tableOfSigns.d.ts +9 -0
  69. package/types/analyze/tableOfSigns.d.ts.map +1 -0
  70. package/types/coefficients/fraction.d.ts +14 -12
  71. package/types/coefficients/fraction.d.ts.map +1 -1
  72. package/types/coefficients/index.d.ts +1 -1
  73. package/types/coefficients/index.d.ts.map +1 -1
  74. package/types/coefficients/root.d.ts +41 -0
  75. package/types/coefficients/root.d.ts.map +1 -0
  76. package/types/geometry/TupleAbstract.d.ts +22 -0
  77. package/types/geometry/TupleAbstract.d.ts.map +1 -0
  78. package/types/geometry/TupleN.d.ts +24 -0
  79. package/types/geometry/TupleN.d.ts.map +1 -0
  80. package/types/geometry/circle.d.ts +26 -17
  81. package/types/geometry/circle.d.ts.map +1 -1
  82. package/types/geometry/geomMath.d.ts +2 -1
  83. package/types/geometry/geomMath.d.ts.map +1 -1
  84. package/types/geometry/index.d.ts.map +1 -1
  85. package/types/geometry/line.d.ts +21 -30
  86. package/types/geometry/line.d.ts.map +1 -1
  87. package/types/geometry/line3.d.ts +19 -19
  88. package/types/geometry/line3.d.ts.map +1 -1
  89. package/types/geometry/matrix.d.ts +11 -11
  90. package/types/geometry/plane3.d.ts +9 -9
  91. package/types/geometry/plane3.d.ts.map +1 -1
  92. package/types/geometry/point.d.ts +12 -7
  93. package/types/geometry/point.d.ts.map +1 -1
  94. package/types/geometry/triangle.d.ts +68 -23
  95. package/types/geometry/triangle.d.ts.map +1 -1
  96. package/types/geometry/vector.d.ts +24 -44
  97. package/types/geometry/vector.d.ts.map +1 -1
  98. package/types/helpers.d.ts +1 -0
  99. package/types/helpers.d.ts.map +1 -1
  100. package/types/index.d.ts +6 -4
  101. package/types/index.d.ts.map +1 -1
  102. package/types/numeric.d.ts +2 -0
  103. package/types/numeric.d.ts.map +1 -1
  104. package/types/pimath.interface.d.ts +38 -44
  105. package/types/pimath.interface.d.ts.map +1 -1
  106. package/types/randomization/algebra/rndPolynom.d.ts.map +1 -1
  107. package/types/randomization/coefficient/rndFraction.d.ts +1 -1
  108. package/types/randomization/coefficient/rndFraction.d.ts.map +1 -1
  109. package/types/randomization/geometry/rndLine.d.ts.map +1 -1
  110. package/types/randomization/random.d.ts +3 -2
  111. package/types/randomization/random.d.ts.map +1 -1
  112. package/types/randomization/rndTypes.d.ts +15 -10
  113. package/types/randomization/rndTypes.d.ts.map +1 -1
@@ -0,0 +1,482 @@
1
+ import {Fraction} from "../coefficients"
2
+ import {Line} from "./line"
3
+ import {Vector} from "./vector"
4
+ import {Point} from "./point"
5
+ import type {InputValue, remarquableLines} from "../pimath.interface"
6
+ import {Numeric} from "../numeric"
7
+
8
+ type TRIANGLE_SIDES = 'AB' | 'AC' | 'BC'
9
+
10
+ // TODO: add a check if it's a triangle or not.
11
+ export class Triangle {
12
+ // This defines the triangle
13
+ #A: Point = new Point()
14
+ #B: Point = new Point()
15
+ #C: Point = new Point()
16
+ #isValid = false
17
+ // This is calculated
18
+ #lines: Record<TRIANGLE_SIDES, Line> | null = null
19
+ #radians = true
20
+ #remarquables: remarquableLines = {
21
+ mediators: null, medians: null, heights: null, externalBisectors: null, bisectors: null
22
+ }
23
+
24
+ constructor(...values: unknown[]) {
25
+ if (values.length > 0) {
26
+ this.parse(...values)
27
+ }
28
+
29
+ return this
30
+ }
31
+
32
+ parse = (...values: unknown[]): this => {
33
+
34
+ if (values.length === 1) {
35
+ if (values[0] instanceof Triangle) {
36
+ return this.copy(values[0])
37
+ }
38
+ }
39
+
40
+ if (values.length === 3) {
41
+ // Possibilities:
42
+ // - Three points (or part of points, only dict for example, or array
43
+ // - Three lines
44
+ // - Three lines as text.
45
+ if (values.every((x: unknown) => typeof x === 'string')) {
46
+ // Three lines as text.
47
+ const [a, b, c] = values
48
+ return this.fromLines(a, b, c)
49
+ }
50
+
51
+ if (values.every((x: unknown) => x instanceof Line)) {
52
+ // We have three lines
53
+ return this.fromLines(values[0], values[1], values[2])
54
+ }
55
+
56
+ if (values.every((x: unknown) => (x instanceof Point))) {
57
+ return this.fromPoints(values[0], values[1], values[2])
58
+ }
59
+ }
60
+
61
+ if (values.length === 6) {
62
+ const v: Fraction[] = values.map((x: unknown) => new Fraction(x as string))
63
+
64
+ if (v.some(x => x.isNaN())) {
65
+ throw new Error('One of the values is not a valid number')
66
+ }
67
+
68
+ return this.fromCoordinates(v[0], v[1], v[2], v[3], v[4], v[5])
69
+ }
70
+
71
+ return this
72
+ }
73
+
74
+ /**
75
+ * Clone the Triangle class
76
+ */
77
+ clone = (): Triangle => {
78
+ return new Triangle(
79
+ this.#A.clone(),
80
+ this.#B.clone(),
81
+ this.#C.clone()
82
+ )
83
+ }
84
+
85
+ /**
86
+ * Copy the values from another triangle
87
+ * @param value
88
+ */
89
+ copy(value: Triangle): this {
90
+ this.#A = value.A.clone()
91
+ this.#B = value.B.clone()
92
+ this.#C = value.C.clone()
93
+
94
+ return this
95
+ }
96
+
97
+ get A(): Point {
98
+ return this.#A
99
+ }
100
+
101
+ set A(value: Point) {
102
+ this.#A = value
103
+ this.#A.onChange = () => this.reset()
104
+ this.reset()
105
+ }
106
+
107
+ get AB(): Vector {
108
+ return this.#getSegment('A', 'B')
109
+ }
110
+
111
+ get AC(): Vector {
112
+ return this.#getSegment('A', 'C')
113
+ }
114
+
115
+ get B(): Point {
116
+ return this.#B
117
+ }
118
+
119
+ set B(value: Point) {
120
+ this.#B = value
121
+ this.#B.onChange = () => this.reset()
122
+ this.reset()
123
+ }
124
+
125
+ get BA(): Vector {
126
+ return this.#getSegment('B', 'A')
127
+ }
128
+
129
+ get BC(): Vector {
130
+ return this.#getSegment('B', 'C')
131
+ }
132
+
133
+ get C(): Point {
134
+ return this.#C
135
+ }
136
+
137
+ set C(value: Point) {
138
+ this.#C = value
139
+ this.#C.onChange = () => this.reset()
140
+ this.reset()
141
+ }
142
+
143
+ get CA(): Vector {
144
+ return this.#getSegment('C', 'A')
145
+ }
146
+
147
+ get CB(): Vector {
148
+ return this.#getSegment('C', 'B')
149
+ }
150
+
151
+ get asDegree(): this {
152
+ this.#radians = false
153
+ return this
154
+ }
155
+
156
+ get asRadians(): this {
157
+ this.#radians = true
158
+ return this
159
+ }
160
+
161
+ fromCoordinates(
162
+ x1: InputValue<Fraction>, y1: InputValue<Fraction>,
163
+ x2: InputValue<Fraction>, y2: InputValue<Fraction>,
164
+ x3: InputValue<Fraction>, y3: InputValue<Fraction>): this {
165
+
166
+ return this.fromPoints(
167
+ new Point(x1, y1),
168
+ new Point(x2, y2),
169
+ new Point(x3, y3),
170
+ )
171
+ }
172
+
173
+ fromLines(line1: Line | string, line2: Line | string, line3: Line | string): this {
174
+ const AB: Line = new Line(line1).clone()
175
+ const BC: Line = new Line(line2).clone()
176
+ const AC: Line = new Line(line3).clone()
177
+
178
+ // Get the intersection points -> build the triangle using these intersection points.
179
+ let intersect = AB.intersection(BC)
180
+ if (intersect.hasIntersection) {
181
+ this.#B = intersect.point
182
+ } else {
183
+ this.#isValid = false
184
+ return this
185
+ }
186
+
187
+ intersect = BC.intersection(AC)
188
+ if (intersect.hasIntersection) {
189
+ this.#C = intersect.point
190
+ } else {
191
+ this.#isValid = false
192
+ return this
193
+ }
194
+
195
+ intersect = AC.intersection(AB)
196
+ if (intersect.hasIntersection) {
197
+ this.#A = intersect.point
198
+ } else {
199
+ this.#isValid = false
200
+ return this
201
+ }
202
+
203
+ // reset the remarquables lines.
204
+ this.reset()
205
+
206
+ // Force the use of the given lines.
207
+ this.#lines = {AB, AC, BC}
208
+
209
+ return this
210
+
211
+ }
212
+
213
+ fromPoints(A: Point, B: Point, C: Point): this {
214
+ // We have three points.
215
+ this.#A = A.clone()
216
+ this.#B = B.clone()
217
+ this.#C = C.clone()
218
+
219
+ this.reset()
220
+
221
+ return this
222
+ }
223
+
224
+ getAngle(name: 'A' | 'B' | 'C'): number {
225
+ const a = this.BC.norm
226
+ const b = this.AC.norm
227
+ const c = this.AB.norm
228
+
229
+ if (name === 'A') {
230
+ return this.#cosThm(a, b, c)
231
+ }
232
+
233
+ if (name === 'C') {
234
+ return this.#cosThm(c, b, a)
235
+ }
236
+
237
+ return this.#cosThm(b, a, c)
238
+ }
239
+
240
+
241
+ getBisectors(internal = true): { 'A': Line, 'B': Line, 'C': Line, 'intersection': Point | null } {
242
+
243
+ if (!this.#remarquables.bisectors) {
244
+ const A = this.#calculateBisectors('A', internal)
245
+ const B = this.#calculateBisectors('B', internal)
246
+ const C = this.#calculateBisectors('C', internal)
247
+
248
+ const intersection = A.intersection(B).point
249
+ this.#remarquables.bisectors = {A, B, C, intersection}
250
+ }
251
+
252
+ return this.#remarquables.bisectors
253
+ }
254
+
255
+ getHeights(): { 'A': Line, 'B': Line, 'C': Line, 'intersection': Point | null } {
256
+
257
+ if (!this.#remarquables.heights) {
258
+ const A = new Line().fromPointAndNormal(this.#A, new Vector(this.#B, this.#C))
259
+ const B = new Line().fromPointAndNormal(this.#B, new Vector(this.#A, this.#C))
260
+ const C = new Line().fromPointAndNormal(this.#C, new Vector(this.#A, this.#B))
261
+
262
+ const intersection = A.intersection(B).point
263
+ this.#remarquables.heights = {A, B, C, intersection}
264
+ }
265
+
266
+ return this.#remarquables.heights
267
+ }
268
+
269
+ getMedians(): { 'A': Line, 'B': Line, 'C': Line, 'intersection': Point | null } {
270
+ const middles = this.getMiddles()
271
+
272
+ if (!this.#remarquables.medians) {
273
+ const A = new Line().fromPoints(this.#A, middles.BC)
274
+ const B = new Line().fromPoints(this.#B, middles.AC)
275
+ const C = new Line().fromPoints(this.#C, middles.AB)
276
+
277
+ const intersection = A.intersection(B).point
278
+ this.#remarquables.medians = {A, B, C, intersection}
279
+ }
280
+
281
+ return this.#remarquables.medians
282
+ }
283
+
284
+ getMediators(): { 'a': Line, 'b': Line, 'c': Line, 'intersection': Point | null } {
285
+ const middles = this.getMiddles()
286
+
287
+ if (!this.#remarquables.mediators) {
288
+ const a = new Line().fromPointAndNormal(middles.BC, new Vector(this.#B, this.#C))
289
+ const b = new Line().fromPointAndNormal(middles.AC, new Vector(this.#A, this.#C))
290
+ const c = new Line().fromPointAndNormal(middles.AB, new Vector(this.#A, this.#B))
291
+
292
+ const intersection = a.intersection(b).point
293
+ this.#remarquables.mediators = {a, b, c, intersection}
294
+ }
295
+
296
+ return this.#remarquables.mediators
297
+ }
298
+
299
+ getMiddles() {
300
+ return {
301
+ 'AB': new Point().middleOf(this.#A, this.#B),
302
+ 'AC': new Point().middleOf(this.#A, this.#C),
303
+ 'BC': new Point().middleOf(this.#B, this.#C)
304
+ }
305
+ }
306
+
307
+ getPoints(): Point[] {
308
+ return [this.A, this.B, this.C]
309
+ }
310
+
311
+ getSortedPoints(): Point[] {
312
+ return this.getPoints().sort((a, b) => {
313
+ return a.x.value === b.x.value
314
+ ? a.y.value - b.y.value
315
+ : a.x.value - b.x.value
316
+ })
317
+ }
318
+
319
+ isEqual(T: Triangle): boolean {
320
+ if (!this.#isValid || !T.isValid) return false
321
+
322
+ // TODO: compare points in a particular order.
323
+ const tri1 = this.getSortedPoints()
324
+ const tri2 = T.getSortedPoints()
325
+
326
+ return tri1.every((_, index) => tri1[index].isEqual(tri2[index]))
327
+
328
+ }
329
+
330
+ isEquilateral(): boolean {
331
+ if (!this.#isValid) return false
332
+
333
+ const dAB = this.AB.normSquare.value
334
+ const dBC = this.BC.normSquare.value
335
+ const dAC = this.AC.normSquare.value
336
+
337
+ return (dAB === dBC) && (dAB === dAC)
338
+ }
339
+
340
+ isIsocele(): boolean {
341
+ if (!this.#isValid) return false
342
+
343
+ const dAB = this.AB.normSquare.value
344
+ const dBC = this.BC.normSquare.value
345
+ const dAC = this.AC.normSquare.value
346
+
347
+ return dAB === dBC ||
348
+ dAB === dAC ||
349
+ dBC === dAC
350
+ }
351
+
352
+ isRectangle(): boolean {
353
+ if (!this.#isValid) return false
354
+
355
+ return this.AB.isNormalTo(this.BC) ||
356
+ this.AB.isNormalTo(this.AC) ||
357
+ this.BC.isNormalTo(this.AC)
358
+ }
359
+
360
+ get isValid(): boolean {
361
+ return this.#isValid
362
+ }
363
+
364
+ set isValid(value: boolean) {
365
+ this.#isValid = value
366
+ }
367
+
368
+ get lines(): Record<TRIANGLE_SIDES, Line> {
369
+ if (this.#lines === null) {
370
+ this.#lines = {
371
+ 'AB': new Line(this.#A, this.#B),
372
+ 'BC': new Line(this.#B, this.#C),
373
+ 'AC': new Line(this.#A, this.#C)
374
+ }
375
+ }
376
+
377
+ return this.#lines
378
+ }
379
+
380
+ medianA(): Line {
381
+ return this.getMedians().A
382
+ }
383
+
384
+ medianB(): Line {
385
+ return this.getMedians().B
386
+ }
387
+
388
+ medianC(): Line {
389
+ return this.getMedians().C
390
+ }
391
+
392
+ get remarquables(): remarquableLines | null {
393
+ return this.#remarquables
394
+ }
395
+
396
+ public reset(): this {
397
+ // Check if the triangle is valid
398
+ // the three points must NOT be aligned.
399
+ this.#isValid = !this.AB.isColinearTo(this.AC)
400
+
401
+ this.#lines = null
402
+ this.#remarquables = {
403
+ mediators: null,
404
+ medians: null,
405
+ heights: null,
406
+ externalBisectors: null,
407
+ bisectors: null
408
+ }
409
+
410
+ return this
411
+ }
412
+
413
+ #calculateBisectors(pt: string, internal = true): Line {
414
+ const tlines = this.lines
415
+ let d1: Vector = new Vector()
416
+ let d2: Vector = new Vector()
417
+ let P: Point = new Point()
418
+
419
+ if (pt === 'A') {
420
+ P = this.A.clone()
421
+ d1 = tlines.AB.clone().d
422
+ d2 = tlines.AC.clone().d
423
+ } else if (pt === 'B') {
424
+ P = this.B.clone()
425
+ d1 = tlines.AB.clone().d.opposite()
426
+ d2 = tlines.BC.clone().d
427
+ } else if (pt === 'C') {
428
+ P = this.C.clone()
429
+ d1 = tlines.BC.clone().d.opposite()
430
+ d2 = tlines.AC.clone().d.opposite()
431
+ }
432
+
433
+ if (d1 === undefined || d2 === undefined) {
434
+ throw new Error(`The point ${pt} does not exist`)
435
+ }
436
+
437
+ const director = internal
438
+ ? d1.unit().add(d2.unit())
439
+ : d1.unit().subtract(d2.unit())
440
+
441
+ return new Line().fromPointAndDirection(P, director)
442
+ }
443
+
444
+ #cosThm(opposite: number, adjacent1: number, adjacent2: number): number {
445
+ const ratio = ((adjacent1 ** 2 + adjacent2 ** 2) - opposite ** 2) / (2 * adjacent1 * adjacent2)
446
+
447
+ return this.#radians
448
+ ? Math.acos(ratio)
449
+ : Numeric.numberCorrection(Math.acos(ratio) * 180 / Math.PI)
450
+ }
451
+
452
+ /**
453
+ * Get the Point class for the given name
454
+ * @param ptName
455
+ */
456
+ #getPointByName = (ptName: string): Point => {
457
+ switch (ptName.toUpperCase()) {
458
+ case 'A':
459
+ return this.#A
460
+ case 'B':
461
+ return this.#B
462
+ case 'C':
463
+ return this.#C
464
+ }
465
+
466
+ // Something went wrong ! Return the first point
467
+ return this.#A
468
+ }
469
+
470
+ /**
471
+ * Get the vector for the segment given by name.
472
+ * @param ptName1
473
+ * @param ptName2
474
+ */
475
+ #getSegment = (ptName1: string, ptName2: string): Vector => {
476
+ return new Vector(
477
+ this.#getPointByName(ptName1),
478
+ this.#getPointByName(ptName2)
479
+ )
480
+ }
481
+
482
+ }
@@ -0,0 +1,225 @@
1
+ /**
2
+ * Vector2D module contains everything necessary to handle 2d vectors.
3
+ * @module Vector
4
+ */
5
+ import type {InputValue, IPiMathObject} from "../pimath.interface"
6
+ import {Fraction} from "../coefficients"
7
+ import {Numeric} from "../numeric"
8
+ import {areVectorsColinears, areVectorsEquals, dotProduct} from "./geomMath"
9
+ import {TupleN} from "./TupleN"
10
+ import {type Point} from "./point"
11
+
12
+ export class Vector extends TupleN implements IPiMathObject<Vector> {
13
+ constructor(...values: (Vector | Point)[] | InputValue<Fraction>[]) {
14
+ super()
15
+
16
+ if (values.length > 0) {
17
+ this.parse(...values)
18
+ }
19
+
20
+ return this
21
+ };
22
+
23
+ // ------------------------------------------
24
+ // Getter and setter
25
+
26
+ public parse(...values: (Vector | Point)[] | InputValue<Fraction>[]): this {
27
+ if (values.length === 0) {
28
+ throw new Error(`Invalid value`)
29
+ }
30
+
31
+ if (values.length === 1) {
32
+ if (values[0] instanceof TupleN) {
33
+ this.array = values[0].copy()
34
+ return this
35
+ }
36
+
37
+ if (typeof values[0] === 'string') {
38
+ return this.fromString(values[0])
39
+ }
40
+
41
+ throw new Error(`Invalid value`)
42
+ }
43
+
44
+ // Two values are given
45
+ if (values.length === 2) {
46
+ const [A, B] = values
47
+
48
+ // The two values are vectors
49
+ if (A instanceof TupleN && B instanceof TupleN) {
50
+ if (A.dimension !== B.dimension) {
51
+ throw new Error('Vectors must have the same dimension')
52
+ }
53
+
54
+ this.array = B.array.map((x, index) => x.clone().subtract(A.array[index]))
55
+ return this
56
+ }
57
+ }
58
+
59
+ // Two ore more values as number, string, fraction...
60
+ this.array = values.map(x => new Fraction(x as InputValue<Fraction>))
61
+ return this
62
+ }
63
+
64
+ public clone(): Vector {
65
+ return new Vector(...this.copy())
66
+ }
67
+
68
+ get tex(): string {
69
+ return `\\begin{pmatrix} ${this.array.map(x => x.tex).join(' \\\\ ')} \\end{pmatrix}`
70
+ }
71
+
72
+ get display(): string {
73
+ return `((${this.array.map(x => x.display).join(',')}))`
74
+ }
75
+
76
+ static asDisplay(...values: string[]): string {
77
+ return `((${values.join(',')}))`
78
+ }
79
+
80
+ static asTex(...values: string[]): string {
81
+ return `\\begin{pmatrix} ${values.join(' \\\\ ')} \\end{pmatrix}`
82
+ }
83
+
84
+ add = (V: Vector): this => {
85
+ this.array.forEach((x, index) => x.add(V.array[index]))
86
+ return this
87
+ }
88
+
89
+ angle = (V: Vector, sharp?: boolean, radian?: boolean): number => {
90
+
91
+ let scalar = this.dot(V).value
92
+ if (sharp) {
93
+ scalar = Math.abs(scalar)
94
+ }
95
+
96
+ const toDegree = radian ? 1 : 180 / Math.PI
97
+
98
+ return toDegree * Math.acos(scalar / (this.norm * V.norm))
99
+ }
100
+
101
+ cross(value: Vector): Vector {
102
+ if (this.dimension !== 3 || value.dimension !== 3) {
103
+ throw new Error('Cross product can only be determined in 3D')
104
+ }
105
+
106
+ return new Vector(
107
+ this.y.clone().multiply(value.z).subtract(this.z.clone().multiply(value.y)),
108
+ this.z.clone().multiply(value.x).subtract(this.x.clone().multiply(value.z)),
109
+ this.x.clone().multiply(value.y).subtract(this.y.clone().multiply(value.x))
110
+ )
111
+ }
112
+
113
+ // ------------------------------------------
114
+ // Creation / parsing functions
115
+
116
+ divideByScalar = (k: InputValue<Fraction>): this => {
117
+ return this.multiplyByScalar(new Fraction(k).inverse())
118
+ }
119
+
120
+ dot = (V: Vector |Point): Fraction => {
121
+ return dotProduct(this, V)
122
+ }
123
+
124
+ override fromString(value: string): this {
125
+ if (value.startsWith('((') && value.endsWith('))')) {
126
+ return super.fromString(value.slice(1, -1))
127
+ }
128
+
129
+ return super.fromString(value)
130
+ }
131
+
132
+ isColinearTo = (v: Vector): boolean => {
133
+ return areVectorsColinears(this, v)
134
+ }
135
+
136
+ isEqual = (v: Vector): boolean => {
137
+ return areVectorsEquals(this, v)
138
+ }
139
+
140
+ isNormalTo = (v: Vector): boolean => {
141
+ return this.dot(v).isZero()
142
+ }
143
+
144
+ // ------------------------------------------
145
+ get isNull(): boolean {
146
+ return this.array.every(x => x.isZero())
147
+ }
148
+
149
+ isOne(): boolean {
150
+ return this.array.every((x, index) => index === 0 ? x.isOne() : x.isZero())
151
+ }
152
+
153
+ isZero(): boolean {
154
+ return this.array.every(x => x.isZero())
155
+ }
156
+
157
+ multiplyByScalar = (k: InputValue<Fraction>): this => {
158
+ const scalar = new Fraction(k)
159
+ this.array.forEach(x => x.multiply(scalar))
160
+ return this
161
+ }
162
+
163
+ get norm(): number {
164
+ return Math.sqrt(this.normSquare.value)
165
+ }
166
+
167
+ get normSquare(): Fraction {
168
+ // Get the norm square of the vector
169
+ return this.array.reduce((acc, x) => acc.add(x.clone().pow(2)), new Fraction(0))
170
+ }
171
+
172
+ normal = (): this => {
173
+ if (this.dimension >= 3) {
174
+ throw new Error('Normal vector can only be determined in 2D')
175
+ }
176
+
177
+ const x = this.x.clone().opposite()
178
+ const y = this.y.clone()
179
+
180
+ this.array[0] = y
181
+ this.array[1] = x
182
+ return this
183
+ }
184
+
185
+ one = (): this => {
186
+ this.zero()
187
+ this.x.one()
188
+ return this
189
+ }
190
+
191
+ opposite = (): this => {
192
+ this.array.forEach(x => x.opposite())
193
+ return this
194
+ }
195
+
196
+ simplify = (): this => {
197
+ const lcm = Numeric.lcm(...this.array.map(x => x.denominator))
198
+ const gcd = Numeric.gcd(...this.array.map(x => x.numerator))
199
+
200
+ this.multiplyByScalar(new Fraction(lcm, gcd))
201
+
202
+ if(this.x.isNegative()) this.opposite()
203
+
204
+ return this
205
+ }
206
+
207
+ subtract = (V: Vector): this => {
208
+ return this.add(V.clone().opposite())
209
+ }
210
+
211
+ translate(...values: Fraction[]): this {
212
+ this.array.forEach((x, index) => x.add(values[index]))
213
+ return this
214
+ }
215
+
216
+ unit = (): this => {
217
+ const norm = this.norm
218
+ if (norm === 0) {
219
+ return this
220
+ }
221
+
222
+ return this.divideByScalar(norm)
223
+ }
224
+
225
+ }