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,386 @@
1
+ import { Line } from "./line"
2
+ import { Vector } from "./vector"
3
+ import { Numeric } from "../numeric"
4
+ import { Fraction } from "../coefficients"
5
+ import { Equation } from "../algebra"
6
+ import { Polynom } from "../algebra"
7
+ import { Monom } from "../algebra"
8
+ import {type IPiMathObject, LinePropriety} from "../pimath.interface"
9
+ import { Point } from "./point"
10
+
11
+ export class Circle
12
+ implements
13
+ IPiMathObject<Circle> {
14
+ #center: Point | undefined = undefined
15
+ #squareRadius: Fraction | undefined = undefined
16
+ #cartesian: Equation | undefined = undefined
17
+
18
+ constructor()
19
+ constructor(equation: string | Equation)
20
+ constructor(circle: Circle)
21
+ constructor(center: Point, radius: Fraction | number, square?: boolean)
22
+ constructor(center: Point, pointThrough: Point)
23
+ constructor(A: Point, B: Point, C: Point)
24
+ constructor(...values: unknown[]) {
25
+ if (values.length > 0) {
26
+ this.parse(...values)
27
+ }
28
+ }
29
+
30
+ get center(): Point {
31
+ return this.#center ?? new Point()
32
+ }
33
+
34
+ get squareRadius(): Fraction {
35
+ return this.#squareRadius ?? new Fraction(0)
36
+ }
37
+
38
+ get cartesian(): Equation {
39
+ if (this.#cartesian === undefined) { throw new Error('Cartesian equation not defined') }
40
+
41
+ return this.#cartesian
42
+ }
43
+
44
+ get radius(): { tex: string, display: string, value: number } {
45
+ if (this.#squareRadius === undefined) { return { tex: '', display: '', value: 0 } }
46
+ if (this.#squareRadius.isSquare()) {
47
+ return {
48
+ tex: this.#squareRadius.clone().sqrt().tex,
49
+ display: this.#squareRadius.clone().sqrt().display,
50
+ value: this.#squareRadius.clone().sqrt().value
51
+ }
52
+ } else {
53
+ return {
54
+ tex: `\\sqrt{${this.#squareRadius.tex}}`,
55
+ display: `sqrt(${this.#squareRadius.display})`,
56
+ value: this.#squareRadius.clone().sqrt().value
57
+ }
58
+ }
59
+ }
60
+
61
+ get tex(): string {
62
+
63
+ let cx, cy
64
+ if (this.center.x.isZero()) {
65
+ cx = 'x^2'
66
+ } else {
67
+ cx = `\\left(x${this.center.x.isNegative() ? '+' : '-'}${this.center.x.clone().abs().tex}\\right)^2`
68
+ }
69
+ if (this.center.y.isZero()) {
70
+ cy = 'y^2'
71
+ } else {
72
+ cy = `\\left(y${this.center.y.isNegative() ? '+' : '-'}${this.center.y.clone().abs().tex}\\right)^2`
73
+ }
74
+ return `${cx}+${cy}=${this.squareRadius.tex}`
75
+ }
76
+
77
+ get developed(): string {
78
+ return this.cartesian.tex
79
+ }
80
+
81
+ get display(): string {
82
+ let cx, cy
83
+ if (this.center.x.isZero()) {
84
+ cx = 'x^2'
85
+ } else {
86
+ cx = `(x${this.center.x.isNegative() ? '+' : '-'}${this.center.x.clone().abs().tex})^2`
87
+ }
88
+ if (this.center.y.isZero()) {
89
+ cy = 'y^2'
90
+ } else {
91
+ cy = `(y${this.center.y.isNegative() ? '+' : '-'}${this.center.y.clone().abs().tex})^2`
92
+ }
93
+ return `${cx}+${cy}=${this.squareRadius.display}`
94
+ }
95
+
96
+ /**
97
+ * Get the relative position between circle and line. It corresponds to the number of intersection.
98
+ * @param {Line} L
99
+ * @returns {number}
100
+ */
101
+ relativePosition = (L: Line): number => {
102
+ if (this.#center === undefined || this.#squareRadius === undefined) { throw new Error('Circle not defined') }
103
+
104
+ const distance = L.distanceTo(this.#center), radius = Math.sqrt(this.#squareRadius.value)
105
+
106
+ if (distance.value - radius > 0.0000000001) {
107
+ return 0 // external
108
+ } else if (Math.abs(distance.value - radius) < 0.0000000001) {
109
+ return 1 // tangent
110
+ } else {
111
+ return 2 // secant
112
+ }
113
+ }
114
+
115
+ lineIntersection = (L: Line): Point[] => {
116
+ const intersectionPoints: Point[] = []
117
+ // let solX: Fraction
118
+
119
+ if (this.#cartesian === undefined) { return [] }
120
+ const equX = this.#cartesian.clone(), lineX = L.getEquation().clone().isolate('x'),
121
+ lineY = L.getEquation().clone().isolate('y')
122
+
123
+ if (lineX instanceof Equation && lineY instanceof Equation) {
124
+ equX.replaceBy('y', lineY.right).simplify()
125
+ equX.solve()
126
+
127
+ // TODO: rework the solutions of an equation.
128
+ // for (const x of equX.solutions) {
129
+ // if (x.exact === false && isNaN(x.value)) {
130
+ // continue
131
+ // }
132
+
133
+ // solX = new Fraction(x.exact === false ? x.value : x.exact)
134
+ // intersectionPoints.push(new Point(solX.clone(), lineY.right.evaluate(solX)))
135
+ // }
136
+ }
137
+
138
+ return intersectionPoints
139
+ }
140
+
141
+ tangents = (P: Point | Fraction): Line[] => {
142
+ if (P instanceof Fraction) {
143
+ return this.#tangentsWithSlope(P)
144
+ } else if (this.isPointOnCircle(P)) {
145
+ return this.#tangentsThroughOnePointOnTheCircle(P)
146
+ } else if (this.#center !== undefined && this.#center.distanceTo(P).value > this.radius.value) {
147
+ //TODO: Must check it's outside the circle
148
+ return this.#tangentsThroughOnePointOutsideTheCircle(P)
149
+ } else {
150
+ console.log('No tangents as the point is inside !')
151
+ }
152
+ return []
153
+ }
154
+
155
+ isPointOnCircle = (P: Point): boolean => {
156
+ return this.#cartesian?.test({ x: P.x, y: P.y }) ?? false
157
+ }
158
+
159
+ getPointsOnCircle = (numberIsInteger?: boolean): Point[] => {
160
+ if (numberIsInteger === undefined) {
161
+ numberIsInteger = false
162
+ }
163
+
164
+ // It means searching for pythagorician triples that make a perfect square.
165
+ // (x-4)^2 + (y+3)^2 = 15
166
+
167
+ const triplets = Numeric.pythagoreanTripletsWithTarget(this.squareRadius.value, true)
168
+
169
+ const points: Point[] = []
170
+
171
+ triplets.forEach(triplet => {
172
+ // Allow positive / negative values
173
+ // x-a = t => x = a + t
174
+ // x-a = -t => x = a - t
175
+
176
+ for (const k of [[1, 1], [-1, 1], [-1, -1], [1, -1]]) {
177
+ points.push(new Point(
178
+ this.center.x.clone().add(k[0] * triplet[0]),
179
+ this.center.y.clone().add(k[1] * triplet[1])
180
+ )
181
+ )
182
+ // Check if the point is not already in points.
183
+ // TODO: isInListOfPoints not implemented
184
+ // if (!pt.isInListOfPoints(points)) {
185
+ // points.push(pt)
186
+ // }
187
+ }
188
+ })
189
+ return points
190
+ }
191
+
192
+ clone(): Circle {
193
+ return new Circle(
194
+ this.center.clone(),
195
+ this.squareRadius.clone(),
196
+ true
197
+ )
198
+ // this.#center = this.center.clone()
199
+ // this.#squareRadius = this.squareRadius.clone()
200
+ // this._calculateCartesian()
201
+ // return this
202
+ }
203
+
204
+ setRadius(radius: Fraction | number, square?: boolean): this {
205
+ if (square) {
206
+ this.#squareRadius = new Fraction(radius)
207
+ } else {
208
+ this.#squareRadius = new Fraction(radius).pow(2)
209
+ }
210
+ this.#calculateCartesian()
211
+ return this
212
+ }
213
+
214
+ #tangentsThroughOnePointOnTheCircle = (P: Point): Line[] => {
215
+ const CT = new Vector(this.center, P)
216
+ return [new Line(P, CT, LinePropriety.Perpendicular)]
217
+ }
218
+
219
+ #tangentsThroughOnePointOutsideTheCircle = (P: Point): Line[] => {
220
+ // y = mx + h
221
+ // px, py => h = -m px + py => mx - y -m.px + py = 0 =>
222
+ // Centre: cx, cy, radius: r
223
+ // (m.cx - cy -m.px + py)^2 = r^2 * (m^2 + 1)
224
+ // (m(cx-py) - (cy - py))^2 = r^2 * (m^2 + 1)
225
+
226
+ const cx_px = this.center.x.clone().subtract(P.x), cy_py = this.center.y.clone().subtract(P.y),
227
+ polyLeft = new Polynom('x'), polyRight = new Polynom('x^2+1')
228
+
229
+ polyLeft.multiply(cx_px).subtract(cy_py).pow(2)
230
+ polyRight.multiply(this.squareRadius)
231
+
232
+ const equ = new Equation(polyLeft, polyRight)
233
+ const solutions = equ.solve()
234
+
235
+ return solutions.map(sol => {
236
+ // h = -m px + py
237
+ let h: Fraction
238
+ const equ = new Equation('y', 'x')
239
+
240
+ if (sol.exact instanceof Fraction) {
241
+ h = P.x.clone().opposite().multiply(sol.exact).add(P.y)
242
+ equ.right.multiply(sol.exact).add(h)
243
+ } else {
244
+ h = P.x.clone().opposite().multiply(sol.value).add(P.y)
245
+ equ.right.multiply(sol.value).add(h)
246
+ }
247
+
248
+ return new Line(equ)
249
+ })
250
+
251
+ }
252
+
253
+ #tangentsWithSlope = (slope: Fraction): Line[] => {
254
+ // d(C;t)=r => ac1+bc2 + x = +- sqrt(a^2 + b^2)*r
255
+ // x = -ac1-bc2 +- sqrt(a^2 + b^2)*r
256
+ // y = a/bx + h => ax-by + H = 0
257
+
258
+ const a = slope.numerator, b = -slope.denominator, c1 = this.center.x.clone(), c2 = this.center.y.clone()
259
+
260
+ const sq = this.squareRadius.clone().multiply(slope.numerator ** 2 + slope.denominator ** 2),
261
+ x1 = c1.clone().multiply(a).opposite().subtract(c2.clone().multiply(b)).add(sq.clone().sqrt()),
262
+ x2 = c1.clone().multiply(a).opposite().subtract(c2.clone().multiply(b)).subtract(sq.clone().sqrt())
263
+
264
+ return [new Line(a, b, x1), new Line(a, b, x2)]
265
+ }
266
+
267
+ #reset(): this {
268
+ this.#center = undefined
269
+ this.#squareRadius = undefined
270
+ this.#cartesian = undefined
271
+
272
+ return this
273
+ }
274
+
275
+ parse(...values: unknown[]): this {
276
+ // Data can be given in these formats:
277
+ // one value, a string -> make it an Equation
278
+ // one value, an Equation
279
+ // one value, a circle -> clone it
280
+ // two values: two points (center and pointThrough)
281
+ // two values: point and Fraction (center and radius)
282
+ // three values: Vector2D, Fraction, Boolean (center, square radius, true)
283
+
284
+ this.#reset()
285
+
286
+ if (typeof values[0] === 'string') {
287
+ this.#parseEquation(new Equation(values[0]))
288
+ } else if (values[0] instanceof Equation) {
289
+ this.#parseEquation(values[0])
290
+ } else if (values[0] instanceof Circle) {
291
+ this.#parseCopyCircle(values[0])
292
+ } else if (values[0] instanceof Point && values.length > 1) {
293
+ if (values[1] instanceof Point) {
294
+ if (values[2] instanceof Point) {
295
+ // TODO: Add the method to parse through three points
296
+ // this._parseThroughtThreePoints(values[0], values[1], values[2])
297
+ } else {
298
+ this.#parseCenterAndPointThrough(values[0], values[1])
299
+ }
300
+ } else if (values[1] instanceof Fraction || typeof values[1] === 'number') {
301
+ this.#parseCenterAndRadius(values[0], values[1], (typeof values[2] === "boolean") ? values[2] : false)
302
+ }
303
+ }
304
+
305
+ // Calculate once the different values.
306
+ this.#calculateCartesian()
307
+
308
+
309
+ return this
310
+ }
311
+
312
+ #calculateCartesian() {
313
+ this.#cartesian = (
314
+ new Equation(
315
+ new Polynom(`(x-(${this.center.x.display}))^2+(y-(${this.center.y.display}))^2`),
316
+ new Polynom(this.squareRadius.display))
317
+ ).moveLeft()
318
+ }
319
+
320
+ #parseCopyCircle(circle: Circle): this {
321
+ this.#center = circle.center.clone()
322
+ this.#squareRadius = circle.squareRadius.clone()
323
+ this.#calculateCartesian()
324
+ return this
325
+ }
326
+
327
+ #parseCenterAndRadius(center: Point, radius: Fraction | number, square?: boolean): this {
328
+ this.#center = center.clone()
329
+ if (square) {
330
+ this.#squareRadius = (new Fraction(radius))
331
+ } else {
332
+ this.#squareRadius = new Fraction(radius).pow(2)
333
+ }
334
+
335
+ return this
336
+ }
337
+
338
+ #parseCenterAndPointThrough(center: Point, pointThrough: Point): this {
339
+ this.#center = center.clone()
340
+ this.#squareRadius = new Vector(this.#center, pointThrough).normSquare
341
+ return this
342
+ }
343
+
344
+ #parseEquation(equ: Equation): this {
345
+
346
+ // Move everything to the left.
347
+ equ.moveLeft()
348
+
349
+ if (equ.degree('x').value === 2 && equ.degree('y').value === 2) {
350
+ // Both must be of degree 2.
351
+ const x2 = equ.left.monomByDegree(2, 'x'), y2 = equ.left.monomByDegree(2, 'y')
352
+ let x1: Monom, y1: Monom, c: Monom
353
+
354
+ // Both square monoms must have the same coefficient.
355
+ if (x2.coefficient.isEqual(y2.coefficient)) {
356
+ equ.divide(x2.coefficient)
357
+
358
+ x1 = equ.left.monomByDegree(1, 'x')
359
+ y1 = equ.left.monomByDegree(1, 'y')
360
+
361
+ c = equ.left.monomByDegree(0)
362
+
363
+ this.#center = new Point(x1.coefficient.clone().divide(2).opposite(), y1.coefficient.clone().divide(2).opposite())
364
+
365
+ this.#squareRadius = c.coefficient.clone().opposite()
366
+ .add(this.#center.x.clone().pow(2))
367
+ .add(this.#center.y.clone().pow(2))
368
+
369
+ } else {
370
+ // The circle is not a valid circle
371
+ this.#center = undefined
372
+ this.#squareRadius = undefined
373
+ }
374
+ }
375
+ return this
376
+ }
377
+
378
+ // private _parseThroughtThreePoints(A: Point, B: Point, C: Point): this {
379
+ // const T = new Triangle(A, B, C), mAB = T.remarquables.mediators.AB.clone(),
380
+ // mAC = T.remarquables.mediators.AC.clone()
381
+ // this.parse(mAB.intersection(mAC).point, A)
382
+
383
+ // return this
384
+ // }
385
+
386
+ }
@@ -0,0 +1,70 @@
1
+ import { Fraction } from "../coefficients"
2
+ import type { Vector } from "./vector"
3
+
4
+ type V = Vector
5
+ export function areVectorsEquals(v1: V, v2: V): boolean {
6
+ return v1.dimension === v2.dimension &&
7
+ v1.array.every(
8
+ (value, index) => {
9
+ return v2.array[index].isEqual(value)
10
+ })
11
+ }
12
+
13
+ export function areVectorsColinears(v1: V, v2: V): boolean {
14
+ if (v1.dimension !== v2.dimension) { return false }
15
+
16
+ // Constant of proportionality
17
+ const k = v2.array[0].value / v1.array[0].value
18
+ return v1.array.every(
19
+ (value, index) => {
20
+ return v2.array[index].value === value.value * k
21
+ })
22
+ }
23
+
24
+
25
+ export function dotProduct(v1: V, v2: V): Fraction {
26
+ if (v1.dimension !== v2.dimension) { return new Fraction().invalid() }
27
+
28
+ // Calculate the dot product
29
+ // Why does the reduce not add the last element?
30
+
31
+ return v1.array.reduce(
32
+ (acc, value, index) => {
33
+ return acc.add(value.clone().multiply(v2.array[index]))
34
+ }, new Fraction(0))
35
+ }
36
+
37
+ export function determinantFromVectors(...values: V[]): Fraction {
38
+ // TODO: Make it work for vectors of dimension n
39
+ // Check if the vectors are in the same dimension
40
+ if (values.some((value) => value.dimension !== values[0].dimension)) {
41
+ throw new Error('All vectors must have the same dimension')
42
+ }
43
+
44
+ // Check if the vectors are in dimension 2 or 3 and that the number of values is correct
45
+ if (values[0].dimension !== values.length ) {
46
+ throw new Error(`The determinant of dimension ${values[0].dimension} must have the same number of vectors (${values.length} given)`)
47
+ }
48
+
49
+ // Calculate the determinant 2x2
50
+ if (values[0].dimension === 2) {
51
+ return values[0].array[0].clone().multiply(values[1].array[1])
52
+ .subtract(values[0].array[1].clone().multiply(values[1].array[0]))
53
+ }
54
+
55
+ // Calculate the determinant 3x3
56
+ return values[0].array[0].clone()
57
+ .multiply(
58
+ values[1].array[1].clone().multiply(values[2].array[2])
59
+ .subtract(values[1].array[2].clone().multiply(values[2].array[1]))
60
+ )
61
+ .subtract(values[0].array[1].clone()
62
+ .multiply(
63
+ values[1].array[0].clone().multiply(values[2].array[2])
64
+ .subtract(values[1].array[2].clone().multiply(values[2].array[0]))
65
+ )
66
+ )
67
+ .add(values[0].array[2].clone()
68
+ .multiply(values[1].array[0].clone().multiply(values[2].array[1])
69
+ .subtract(values[1].array[1].clone().multiply(values[2].array[0]))))
70
+ }
@@ -0,0 +1,10 @@
1
+ // Import / export every files from the geometry folder
2
+ export * from './geomMath'
3
+ export * from './circle'
4
+ export * from './line'
5
+ export * from './line3'
6
+ export * from './plane3'
7
+ export * from './point'
8
+ export * from './triangle'
9
+ export * from './vector'
10
+ export * from './sphere3'