pimath 0.1.39 → 0.1.40

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 (65) hide show
  1. package/dist/pimath.js +188 -159
  2. package/dist/pimath.js.map +1 -1
  3. package/package.json +4 -2
  4. package/src/algebra/equation.ts +556 -0
  5. package/src/algebra/equationSolver.ts +539 -0
  6. package/src/algebra/factor.ts +339 -0
  7. package/src/algebra/index.ts +11 -0
  8. package/src/algebra/linearSystem.ts +388 -0
  9. package/src/algebra/logicalset.ts +256 -0
  10. package/src/algebra/matrix.ts +474 -0
  11. package/src/algebra/monom.ts +1015 -0
  12. package/src/algebra/operations.ts +24 -0
  13. package/src/algebra/polyFactor.ts +668 -0
  14. package/src/algebra/polynom.ts +1394 -0
  15. package/src/analyze/solution.ts +115 -0
  16. package/src/analyze/tableOfSigns.ts +30 -0
  17. package/src/coefficients/fraction.ts +678 -0
  18. package/src/coefficients/index.ts +4 -0
  19. package/src/coefficients/nthRoot.ts +149 -0
  20. package/src/coefficients/root.ts +299 -0
  21. package/src/geometry/circle.ts +386 -0
  22. package/src/geometry/geomMath.ts +70 -0
  23. package/src/geometry/index.ts +10 -0
  24. package/src/geometry/line.ts +677 -0
  25. package/src/geometry/line3.ts +206 -0
  26. package/src/geometry/plane3.ts +170 -0
  27. package/src/geometry/point.ts +66 -0
  28. package/src/geometry/sphere3.ts +214 -0
  29. package/src/geometry/triangle.ts +354 -0
  30. package/src/geometry/vector.ts +341 -0
  31. package/src/helpers.ts +35 -0
  32. package/src/index.ts +60 -0
  33. package/src/numeric.ts +199 -0
  34. package/src/pimath.interface.ts +160 -0
  35. package/src/randomization/algebra/rndEquation.ts +41 -0
  36. package/src/randomization/algebra/rndMonom.ts +39 -0
  37. package/src/randomization/algebra/rndPolynom.ts +86 -0
  38. package/src/randomization/coefficient/rndFraction.ts +38 -0
  39. package/src/randomization/geometry/rndCircle.ts +27 -0
  40. package/src/randomization/geometry/rndLine.ts +37 -0
  41. package/src/randomization/geometry/rndLine3.ts +27 -0
  42. package/src/randomization/geometry/rndVector.ts +63 -0
  43. package/src/randomization/random.ts +91 -0
  44. package/src/randomization/rndHelpers.ts +102 -0
  45. package/src/randomization/rndTypes.ts +63 -0
  46. package/types/algebra/equationSolver.d.ts +3 -0
  47. package/types/algebra/equationSolver.d.ts.map +1 -1
  48. package/types/algebra/polyFactor.d.ts +5 -0
  49. package/types/algebra/polyFactor.d.ts.map +1 -1
  50. package/types/analyze/solution.d.ts +21 -0
  51. package/types/analyze/solution.d.ts.map +1 -0
  52. package/types/analyze/tableOfSigns.d.ts +9 -0
  53. package/types/analyze/tableOfSigns.d.ts.map +1 -0
  54. package/types/coefficients/root.d.ts +38 -0
  55. package/types/coefficients/root.d.ts.map +1 -0
  56. package/types/geometry/point.d.ts +1 -1
  57. package/types/geometry/point.d.ts.map +1 -1
  58. package/types/helpers.d.ts +1 -0
  59. package/types/helpers.d.ts.map +1 -1
  60. package/types/index.d.ts +1 -0
  61. package/types/index.d.ts.map +1 -1
  62. package/types/numeric.d.ts +2 -0
  63. package/types/numeric.d.ts.map +1 -1
  64. package/types/pimath.interface.d.ts +26 -26
  65. package/types/pimath.interface.d.ts.map +1 -1
@@ -0,0 +1,354 @@
1
+ import { Fraction } from "../coefficients/fraction"
2
+ import { Line } from "./line"
3
+ import { Vector } from "./vector"
4
+ import { Point } from "./point"
5
+ import type {remarquableLines} from "../pimath.interface"
6
+
7
+ export class Triangle {
8
+ #A: Point = new Point()
9
+ #B: Point = new Point()
10
+ #C: Point = new Point()
11
+ #lines: { 'AB': Line, 'AC': Line, 'BC': Line } = {
12
+ 'AB': new Line(),
13
+ 'AC': new Line(),
14
+ 'BC': new Line()
15
+ }
16
+ #middles: { 'AB': Point, 'AC': Point, 'BC': Point } = {
17
+ 'AB': new Point(),
18
+ 'AC': new Point(),
19
+ 'BC': new Point()
20
+ }
21
+ #remarquables: remarquableLines | null = null
22
+
23
+ constructor(...values: unknown[]) {
24
+
25
+ if (values.length > 0) {
26
+ this.parse(...values)
27
+ }
28
+
29
+ return this
30
+ }
31
+
32
+ // ------------------------------------------
33
+ // Getter and setters
34
+ // ------------------------------------------
35
+
36
+ get A(): Point {
37
+ return this.#A
38
+ }
39
+
40
+ get B(): Point {
41
+ return this.#B
42
+ }
43
+
44
+ get C(): Point {
45
+ return this.#C
46
+ }
47
+
48
+ get AB(): Vector {
49
+ return this.#getSegment('A', 'B')
50
+ }
51
+
52
+ get BA(): Vector {
53
+ return this.#getSegment('B', 'A')
54
+ }
55
+
56
+ get BC(): Vector {
57
+ return this.#getSegment('B', 'C')
58
+ }
59
+
60
+ get CB(): Vector {
61
+ return this.#getSegment('C', 'B')
62
+ }
63
+
64
+ get AC(): Vector {
65
+ return this.#getSegment('A', 'C')
66
+ }
67
+
68
+ get CA(): Vector {
69
+ return this.#getSegment('C', 'A')
70
+ }
71
+
72
+ get isRectangle(): boolean {
73
+ if (this.AB.isNormalTo(this.BC)) {
74
+ return true
75
+ }
76
+ if (this.AB.isNormalTo(this.AC)) {
77
+ return true
78
+ }
79
+ if (this.BC.isNormalTo(this.AC)) {
80
+ return true
81
+ }
82
+
83
+ return false
84
+ }
85
+
86
+ get isEquilateral(): boolean {
87
+ return this.AB.normSquare.isEqual(this.BC.normSquare) &&
88
+ this.AB.normSquare.isEqual(this.AC.normSquare)
89
+ }
90
+
91
+ get isIsocele(): boolean {
92
+ return this.AB.normSquare.isEqual(this.BC.normSquare) ||
93
+ this.AB.normSquare.isEqual(this.AC.normSquare) ||
94
+ this.BC.normSquare.isEqual(this.AC.normSquare)
95
+ }
96
+
97
+ get lines(): { 'AB': Line, 'BC': Line, 'AC': Line } {
98
+ return this.#lines
99
+ }
100
+
101
+ get remarquables(): remarquableLines | null {
102
+ return this.#remarquables
103
+ }
104
+
105
+ // ------------------------------------------
106
+ // Creation / parsing functions
107
+ // ------------------------------------------
108
+
109
+ /**
110
+ * Parse values to a triangle. Supported formats:
111
+ * Vector2D, Vector2D, Vector2D
112
+ * x1, y1, x2, y2, x3, y3
113
+ * @param values
114
+ */
115
+ parse = (...values: unknown[]): Triangle => {
116
+ if (values.length === 6) {
117
+ // Check if all values are number or fractions.
118
+ const v: Fraction[] = values.map((x: unknown) => new Fraction(x as string))
119
+
120
+ if (v.some(x => x.isNaN())) {
121
+ throw new Error('One of the values is not a valid number')
122
+ }
123
+
124
+ return this.parse(
125
+ new Vector(v[0], v[1]),
126
+ new Vector(v[2], v[3]),
127
+ new Vector(v[4], v[5]),
128
+ )
129
+ } else if (values.length === 3) {
130
+ // Possibilities:
131
+ // - Three points (or part of points, only dict for example, or array
132
+ // - Three lines
133
+ // - Three lines as text.
134
+ if (values.every((x: unknown) => typeof x === 'string')) {
135
+ // Three lines as text.
136
+ return this.parse(
137
+ ...values.map((x) => {
138
+ return new Line(x)
139
+ })
140
+ )
141
+ } else if (values.every((x: unknown) => x instanceof Line)) {
142
+ // We have three lines
143
+ const AB: Line = (values[0]).clone()
144
+ const BC: Line = (values[1]).clone()
145
+ const AC: Line = (values[2]).clone()
146
+ this.#lines = { AB, BC, AC }
147
+
148
+ // Get the intersection points -> build the triangle using these intersection points.
149
+ let intersect = AB.intersection(BC)
150
+ if (intersect.hasIntersection) {
151
+ this.#B = intersect.point.clone()
152
+ } else {
153
+ throw new Error('Lines do not intersect !')
154
+ }
155
+
156
+ intersect = BC.intersection(AC)
157
+ if (intersect.hasIntersection) {
158
+ this.#C = intersect.point.clone()
159
+ } else {
160
+ throw new Error('Lines do not intersect !')
161
+ }
162
+
163
+ intersect = AC.intersection(AB)
164
+ if (intersect.hasIntersection) {
165
+ this.#A = intersect.point.clone()
166
+ } else {
167
+ throw new Error('Lines do not intersect !')
168
+ }
169
+
170
+ } else if (values.every((x: unknown) => (x instanceof Point))) {
171
+ // We have three points.
172
+ this.#A = (values[0]).clone()
173
+ this.#B = (values[1]).clone()
174
+ this.#C = (values[2]).clone()
175
+ this.#lines = {
176
+ 'AB': new Line(this.#A, this.#B),
177
+ 'BC': new Line(this.#B, this.#C),
178
+ 'AC': new Line(this.#A, this.#C)
179
+ }
180
+ }
181
+ } else if (values.length === 1) {
182
+ if (values[0] instanceof Triangle) {
183
+ return values[0].clone()
184
+ }
185
+ }
186
+
187
+ this.#updateTriangle()
188
+ return this
189
+ }
190
+
191
+ /**
192
+ * Clone the Triangle class
193
+ */
194
+ clone = (): Triangle => {
195
+ return new Triangle(
196
+ this.#A.clone(),
197
+ this.#B.clone(),
198
+ this.#C.clone()
199
+ )
200
+ }
201
+
202
+
203
+ // ------------------------------------------
204
+ // Triangle operations and properties
205
+ // ------------------------------------------
206
+
207
+ /**
208
+ * Generate the Line object for the three segments of the triangle
209
+ */
210
+ #updateTriangle = () => {
211
+ this.#A.asPoint = true
212
+ this.#B.asPoint = true
213
+ this.#C.asPoint = true
214
+
215
+ this.#middles = {
216
+ 'AB': new Point().middleOf(this.#A, this.#B),
217
+ 'AC': new Point().middleOf(this.#A, this.#C),
218
+ 'BC': new Point().middleOf(this.#B, this.#C)
219
+ }
220
+
221
+ this.#remarquables = this.#calculateRemarquableLines()
222
+ }
223
+
224
+
225
+ /**
226
+ * Get the Vector2D class for the given name
227
+ * @param ptName
228
+ */
229
+ #getPointByName = (ptName: string): Point => {
230
+ switch (ptName.toUpperCase()) {
231
+ case 'A':
232
+ return this.#A
233
+ case 'B':
234
+ return this.#B
235
+ case 'C':
236
+ return this.#C
237
+ }
238
+
239
+ // Something went wrong ! Return the first point
240
+ return this.#A
241
+ }
242
+ /**
243
+ * Get the vector for the segment given by name.
244
+ * @param ptName1
245
+ * @param ptName2
246
+ */
247
+ #getSegment = (ptName1: string, ptName2: string): Vector => {
248
+ return new Vector(
249
+ this.#getPointByName(ptName1),
250
+ this.#getPointByName(ptName2)
251
+ )
252
+ }
253
+
254
+ #calculateRemarquableLines = (): remarquableLines => {
255
+
256
+ const medians = {
257
+ 'A': new Line().fromPoints(this.#A, this.#middles.BC),
258
+ 'B': new Line().fromPoints(this.#B, this.#middles.AC),
259
+ 'C': new Line().fromPoints(this.#C, this.#middles.AB),
260
+ 'intersection': null
261
+ }
262
+
263
+ const mediators = {
264
+ 'AB': new Line().fromPointAndNormal(this.#middles.AB, new Vector(this.#A, this.#B).normal()),
265
+ 'AC': new Line().fromPointAndNormal(this.#middles.AC, new Vector(this.#A, this.#C).normal()),
266
+ 'BC': new Line().fromPointAndNormal(this.#middles.BC, new Vector(this.#B, this.#C).normal()),
267
+ 'intersection': null
268
+ }
269
+
270
+ const heights = {
271
+ 'A': new Line().fromPointAndNormal(this.#A, new Vector(this.#B, this.#C).normal()),
272
+ 'B': new Line().fromPointAndNormal(this.#B, new Vector(this.#A, this.#C).normal()),
273
+ 'C': new Line().fromPointAndNormal(this.#C, new Vector(this.#A, this.#B).normal()),
274
+ 'intersection': null
275
+ }
276
+
277
+ const bA = this.#calculateBisectors('A'),
278
+ bB = this.#calculateBisectors('B'),
279
+ bC = this.#calculateBisectors('C')
280
+
281
+ const bisectors = {
282
+ 'A': bA.internal,
283
+ 'B': bB.internal,
284
+ 'C': bB.internal,
285
+ 'intersection': null
286
+ }
287
+
288
+ const externalBisectors = {
289
+ 'A': bA.external,
290
+ 'B': bB.external,
291
+ 'C': bC.external,
292
+ 'intersection': null
293
+ }
294
+
295
+ const remarquables: remarquableLines = {
296
+ medians,
297
+ mediators,
298
+ heights,
299
+ bisectors,
300
+ externalBisectors
301
+ }
302
+
303
+ // As it's a triangle, we assume the lines are intersecting and aren't parallel or superposed.
304
+ remarquables.medians.intersection = remarquables.medians.A.intersection(remarquables.medians.B).point
305
+ remarquables.mediators.intersection = remarquables.mediators.AB.intersection(remarquables.mediators.BC).point
306
+ remarquables.heights.intersection = remarquables.heights.A.intersection(remarquables.heights.B).point
307
+ remarquables.bisectors.intersection = remarquables.bisectors.A.intersection(remarquables.bisectors.B).point
308
+
309
+ // Everything was calculated for the remarquable lines.
310
+ return remarquables
311
+ }
312
+
313
+ #calculateBisectors = (pt: string): { internal: Line, external: Line } => {
314
+ const tlines = this.lines
315
+ let d1, d2
316
+
317
+ if (pt === 'A') {
318
+ d1 = tlines.AB
319
+ d2 = tlines.AC
320
+ } else if (pt === 'B') {
321
+ d1 = tlines.AB
322
+ d2 = tlines.BC
323
+ } else if (pt === 'C') {
324
+ d1 = tlines.BC
325
+ d2 = tlines.AC
326
+ }
327
+
328
+ if (d1 === undefined || d2 === undefined) {
329
+ throw new Error(`The point ${pt} does not exist`)
330
+ }
331
+
332
+ const d1n = d1.n.simplify().norm
333
+ const d2n = d2.n.simplify().norm
334
+ const d1Equ = d1.getEquation().multiply(d2n)
335
+ const d2Equ = d2.getEquation().multiply(d1n)
336
+
337
+ const b1: Line = new Line(d1Equ.clone().subtract(d2Equ).simplify())
338
+ const b2: Line = new Line(d2Equ.clone().subtract(d1Equ).simplify())
339
+
340
+ // Must determine which bisectors is in the triangle
341
+ if (pt === 'A') {
342
+ return b1.hitSegment(this.B, this.C) ? { internal: b1, external: b2 } : { internal: b2, external: b1 }
343
+ }
344
+ if (pt === 'B') {
345
+ return b1.hitSegment(this.A, this.C) ? { internal: b1, external: b2 } : { internal: b2, external: b1 }
346
+ }
347
+ if (pt === 'C') {
348
+ return b1.hitSegment(this.B, this.A) ? { internal: b1, external: b2 } : { internal: b2, external: b1 }
349
+ }
350
+
351
+ // Default returns the first bisector
352
+ return { internal: b1, external: b2 }
353
+ }
354
+ }
@@ -0,0 +1,341 @@
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/fraction"
7
+ import { Numeric } from "../numeric"
8
+ import { areVectorsColinears, areVectorsEquals, dotProduct } from "./geomMath"
9
+
10
+ export class Vector implements
11
+ IPiMathObject<Vector> {
12
+ #array: Fraction[] = []
13
+ #asPoint = false
14
+
15
+ constructor(...values: Vector[] | InputValue<Fraction>[]) {
16
+ if (values.length > 0) {
17
+ this.parse(...values)
18
+ }
19
+ };
20
+
21
+ // ------------------------------------------
22
+ // Getter and setter
23
+ // ------------------------------------------
24
+ get array(): Fraction[] {
25
+ return this.#array
26
+ }
27
+
28
+ set array(value: Fraction[]) {
29
+ this.#array = value
30
+ }
31
+
32
+ get x(): Fraction {
33
+ return this.#array[0]
34
+ }
35
+
36
+ set x(value: Fraction | number | string) {
37
+ this.#array[0] = new Fraction(value)
38
+ }
39
+
40
+ get y(): Fraction {
41
+ return this.#array[1]
42
+ }
43
+
44
+ set y(value: Fraction | number | string) {
45
+ this.#array[1] = new Fraction(value)
46
+ }
47
+
48
+ get z(): Fraction {
49
+ if (this.dimension < 3) { throw new Error('Vector is not 3D') }
50
+ return this.#array[2]
51
+ }
52
+
53
+ set z(value: Fraction | number | string) {
54
+ if (this.dimension < 3) { throw new Error('Vector is not 3D') }
55
+ this.#array[2] = new Fraction(value)
56
+ }
57
+
58
+ get asPoint(): boolean {
59
+ return this.#asPoint
60
+ }
61
+
62
+ set asPoint(value: boolean) {
63
+ this.#asPoint = value
64
+ }
65
+
66
+
67
+ get normSquare(): Fraction {
68
+ // Get the norm square of the vector
69
+ return this.array.reduce((acc, x) => acc.add(x.clone().pow(2)), new Fraction(0))
70
+ }
71
+
72
+ get norm(): number {
73
+ return Math.sqrt(this.normSquare.value)
74
+ }
75
+
76
+ get tex(): string {
77
+ if (this.#asPoint) {
78
+ return `\\left(${this.array.map(x => x.tex).join(';')}\\right)`
79
+ }
80
+
81
+ return `\\begin{pmatrix} ${this.array.map(x => x.tex).join(' \\\\ ')} \\end{pmatrix}`
82
+ }
83
+
84
+ get display(): string {
85
+ if (this.#asPoint) {
86
+ return `(${this.array.map(x => x.display).join(';')})`
87
+ }
88
+
89
+ return `((${this.array.map(x => x.display).join(',')}))`
90
+ }
91
+
92
+ setDimension(value = 2): this{
93
+ if (value < 2) {
94
+ throw new Error('Dimension must be at least 2')
95
+ }
96
+
97
+ if (value < this.dimension) {
98
+ this.#array = this.#array.slice(0, value)
99
+ } else if(value > this.dimension) {
100
+ for(let i = this.dimension; i < value; i++) {
101
+ this.#array.push(new Fraction(0))
102
+ }
103
+ }
104
+
105
+ return this
106
+ }
107
+ get dimension(): number {
108
+ return this.array.length
109
+ }
110
+
111
+ // ------------------------------------------
112
+ // Creation / parsing functions
113
+ // ------------------------------------------
114
+ get isNull(): boolean {
115
+ return this.array.every(x => x.isZero())
116
+ }
117
+
118
+ static asTex(...values: string[]): string {
119
+ return `\\begin{pmatrix} ${values.join(' \\\\ ')} \\end{pmatrix}`
120
+ }
121
+ static asDisplay(...values: string[]): string {
122
+ return `((${values.join(',')}))`
123
+ }
124
+
125
+ public defineAsPoint(value?: boolean): this {
126
+ this.#asPoint = value !== false
127
+ return this
128
+ }
129
+ public parse(...values: Vector[] | InputValue<Fraction>[]): this {
130
+ if (values.length === 0) {
131
+ throw new Error(`Invalid value`)
132
+ }
133
+
134
+ if (values.length === 1) {
135
+ if (values[0] instanceof Vector) {
136
+ return values[0].clone() as this
137
+ } else if (typeof values[0] === 'string') {
138
+ return this.fromString(values[0])
139
+ } else {
140
+ throw new Error(`Invalid value`)
141
+ }
142
+ }
143
+
144
+ // Two values are given
145
+ if (values.length === 2) {
146
+ const [A, B] = values
147
+
148
+ // The two values are vectors
149
+ if (A instanceof Vector && B instanceof Vector) {
150
+ if (A.dimension !== B.dimension) { throw new Error('Vectors must have the same dimension') }
151
+
152
+ this.#array = B.array.map((x, index) => x.clone().subtract(A.array[index]))
153
+ return this
154
+ }
155
+ }
156
+
157
+ // Two ore more values as number, string, fraction...
158
+ this.#array = values.map(x => new Fraction(x as InputValue<Fraction>))
159
+
160
+ return this
161
+ }
162
+
163
+ public clone(): Vector {
164
+ const V = new Vector()
165
+ V.array = this.copy()
166
+ V.asPoint = this.asPoint
167
+ return V
168
+ }
169
+
170
+ public copy(): Fraction[] {
171
+ return this.#array.map(x => x.clone())
172
+ }
173
+
174
+ zero = (): this => {
175
+ this.#array.forEach(x => x.zero())
176
+ return this
177
+ }
178
+
179
+ one = (): this => {
180
+ this.zero()
181
+ this.x.one()
182
+ return this
183
+ }
184
+
185
+ opposite = (): this => {
186
+ this.#array.forEach(x => x.opposite())
187
+ return this
188
+ }
189
+
190
+ add = (V: Vector): this => {
191
+ this.#array.forEach((x, index) => x.add(V.array[index]))
192
+ return this
193
+ }
194
+
195
+ subtract = (V: Vector): this => {
196
+ return this.add(V.clone().opposite())
197
+ }
198
+
199
+ unit = (): this => {
200
+ const norm = this.norm
201
+ if (norm === 0) {
202
+ return this
203
+ }
204
+
205
+ return this.divideByScalar(norm)
206
+ }
207
+
208
+ middleOf(V1: Vector, V2: Vector): this {
209
+ if (V1.dimension !== V2.dimension) { throw new Error('Vectors must be the same dimension') }
210
+
211
+ this.array = []
212
+ V1.array.forEach((x, index) => {
213
+ this.array.push(x.clone().add(V2.array[index]).divide(2))
214
+ })
215
+
216
+ return this
217
+ }
218
+
219
+ translate(...values: Fraction[]): this {
220
+ this.array.forEach((x, index) => x.add(values[index]))
221
+ return this
222
+ }
223
+
224
+
225
+ dot = (V: Vector): Fraction => {
226
+ return dotProduct(this, V)
227
+ }
228
+
229
+ cross(value: Vector): Vector {
230
+ if (this.dimension !== 3 || value.dimension !== 3) {
231
+ throw new Error('Cross product can only be determined in 3D')
232
+ }
233
+
234
+ return new Vector(
235
+ this.y.clone().multiply(value.z).subtract(this.z.clone().multiply(value.y)),
236
+ this.z.clone().multiply(value.x).subtract(this.x.clone().multiply(value.z)),
237
+ this.x.clone().multiply(value.y).subtract(this.y.clone().multiply(value.x))
238
+ )
239
+ }
240
+
241
+ normal = (): this => {
242
+ if (this.dimension >= 3) { throw new Error('Normal vector can only be determined in 2D') }
243
+
244
+ const x = this.x.clone().opposite(),
245
+ y = this.y.clone()
246
+ this.#array[0] = y
247
+ this.#array[1] = x
248
+ return this
249
+ }
250
+
251
+ isZero(): boolean {
252
+ return this.array.every(x => x.isZero())
253
+ }
254
+ isOne(): boolean {
255
+ return this.array.every((x, index) => index === 0 ? x.isOne() : x.isZero())
256
+ }
257
+
258
+ isEqual = (v: Vector): boolean => {
259
+ return areVectorsEquals(this, v)
260
+ }
261
+
262
+ isColinearTo = (v: Vector): boolean => {
263
+ return areVectorsColinears(this, v)
264
+ }
265
+
266
+ isNormalTo = (v: Vector): boolean => {
267
+ return this.dot(v).isZero()
268
+ }
269
+
270
+ multiplyByScalar = (k: InputValue<Fraction>): this => {
271
+ const scalar = new Fraction(k)
272
+ this.array.forEach(x => x.multiply(scalar))
273
+ return this
274
+ }
275
+
276
+ divideByScalar = (k: InputValue<Fraction>): this => {
277
+ return this.multiplyByScalar(new Fraction(k).inverse())
278
+ }
279
+
280
+ simplify = (): this => {
281
+ // Multiply by the lcm of denominators.
282
+ return this
283
+ .multiplyByScalar(
284
+ Numeric.lcm(...this.array.map(x => x.denominator))
285
+ )
286
+ .divideByScalar(
287
+ Numeric.gcd(...this.array.map(x => x.numerator))
288
+ ).
289
+ multiplyByScalar(
290
+ this.x.isNegative() ? -1 : 1
291
+ )
292
+ }
293
+
294
+ angle = (V: Vector, sharp?: boolean, radian?: boolean): number => {
295
+
296
+ let scalar = this.dot(V).value
297
+ if (sharp) {
298
+ scalar = Math.abs(scalar)
299
+ }
300
+
301
+ const toDegree = radian ? 1 : 180 / Math.PI
302
+
303
+ return toDegree * Math.acos(scalar / (this.norm * V.norm))
304
+ }
305
+
306
+
307
+ fromString = (value: string): this => {
308
+ // Remove the first letter if it's a parenthesis.
309
+ if (value.startsWith('(')) {
310
+ value = value.substring(1)
311
+ }
312
+
313
+ // Remove the last letter if it's a parenthesis.
314
+ if (value.endsWith(')')) {
315
+ value = value.substring(0, value.length - 1)
316
+ }
317
+
318
+ // Split comma, semi colon or single space.
319
+ const components = value.split(/[,;\s]/g)
320
+ .filter((v) => v.trim() !== '')
321
+
322
+ // there must be at least two components.
323
+ if (components.length < 2) {
324
+ return this
325
+ }
326
+
327
+ // Validate the fraction values.
328
+ this.#array = components.map(x => new Fraction(x))
329
+ return this
330
+ }
331
+
332
+ distanceTo(item: Vector): { value: number, fraction: Fraction, tex: string } {
333
+ const V = new Vector(this, item)
334
+
335
+ return {
336
+ value: V.norm,
337
+ fraction: V.normSquare,
338
+ tex: V.tex
339
+ }
340
+ }
341
+ }