afmaths 2.0.2__tar.gz → 2.0.4__tar.gz
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.
- {afmaths-2.0.2 → afmaths-2.0.4}/PKG-INFO +4 -1
- afmaths-2.0.4/lib/afmaths/__init__.py +0 -0
- afmaths-2.0.4/lib/afmaths/cs.py +190 -0
- afmaths-2.0.4/lib/afmaths/formula.py +66 -0
- afmaths-2.0.4/lib/afmaths/geometry.py +210 -0
- afmaths-2.0.4/lib/afmaths/graph.py +38 -0
- afmaths-2.0.4/lib/afmaths/list.py +63 -0
- afmaths-2.0.4/lib/afmaths/operation.py +115 -0
- afmaths-2.0.4/lib/afmaths/physics.py +270 -0
- afmaths-2.0.4/lib/afmaths/space_engineering.py +590 -0
- afmaths-2.0.4/lib/afmaths/statistics.py +12 -0
- {afmaths-2.0.2 → afmaths-2.0.4/lib}/afmaths.egg-info/PKG-INFO +4 -1
- afmaths-2.0.4/lib/afmaths.egg-info/SOURCES.txt +17 -0
- afmaths-2.0.4/lib/afmaths.egg-info/requires.txt +1 -0
- afmaths-2.0.4/lib/afmaths.egg-info/top_level.txt +1 -0
- {afmaths-2.0.2 → afmaths-2.0.4}/setup.py +8 -3
- afmaths-2.0.2/afmaths.egg-info/SOURCES.txt +0 -6
- afmaths-2.0.2/afmaths.egg-info/top_level.txt +0 -1
- {afmaths-2.0.2 → afmaths-2.0.4}/LICENSE +0 -0
- {afmaths-2.0.2 → afmaths-2.0.4/lib}/afmaths.egg-info/dependency_links.txt +0 -0
- {afmaths-2.0.2 → afmaths-2.0.4}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: afmaths
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.4
|
|
4
4
|
Summary: A simple package of math functions.
|
|
5
5
|
Home-page: https://github.com/Arturius771/afmaths
|
|
6
6
|
Author: Artur Foden
|
|
@@ -8,12 +8,14 @@ Classifier: Programming Language :: Python :: 3
|
|
|
8
8
|
Requires-Python: >=3.12
|
|
9
9
|
Description-Content-Type: text/markdown
|
|
10
10
|
License-File: LICENSE
|
|
11
|
+
Requires-Dist: astronomy_types>=2.1.0
|
|
11
12
|
Dynamic: author
|
|
12
13
|
Dynamic: classifier
|
|
13
14
|
Dynamic: description
|
|
14
15
|
Dynamic: description-content-type
|
|
15
16
|
Dynamic: home-page
|
|
16
17
|
Dynamic: license-file
|
|
18
|
+
Dynamic: requires-dist
|
|
17
19
|
Dynamic: requires-python
|
|
18
20
|
Dynamic: summary
|
|
19
21
|
|
|
@@ -52,6 +54,7 @@ Ensure that setuptools and wheel are installed in your environment:
|
|
|
52
54
|
|
|
53
55
|
```bash
|
|
54
56
|
pip install -r requirements.txt
|
|
57
|
+
pip install -e .
|
|
55
58
|
```
|
|
56
59
|
|
|
57
60
|
## 3. Update version number
|
|
File without changes
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import math
|
|
2
|
+
from geometry import pythagoras
|
|
3
|
+
from operation import (
|
|
4
|
+
HALF,
|
|
5
|
+
add,
|
|
6
|
+
divide,
|
|
7
|
+
exponentiate,
|
|
8
|
+
factorial,
|
|
9
|
+
multiply,
|
|
10
|
+
subtract,
|
|
11
|
+
)
|
|
12
|
+
from list import sum
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def file_compression_ratio(uncompressed_size: float):
|
|
16
|
+
return lambda compressed_size: divide(compressed_size)(uncompressed_size)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def compressed_file_size(uncompressed_size):
|
|
20
|
+
return lambda compression_ratio: divide(compression_ratio)(uncompressed_size)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def diagonal_pixel_length(length_in_pixels):
|
|
24
|
+
return lambda width_in_pixels: math.floor(
|
|
25
|
+
pythagoras(length_in_pixels)(width_in_pixels)
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def travelling_salesman_problem_total_routes(number_of_cities) -> float:
|
|
30
|
+
##(n - 1)!/2
|
|
31
|
+
subtract1 = subtract(1)
|
|
32
|
+
total_routes = HALF(factorial(subtract1(number_of_cities)))
|
|
33
|
+
|
|
34
|
+
return total_routes
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def check_drive_clusters(
|
|
38
|
+
sectors_per_cluster, sector_size_bytes, physical_file_size_bytes
|
|
39
|
+
) -> tuple[int, float]:
|
|
40
|
+
## TM255
|
|
41
|
+
if (
|
|
42
|
+
physical_file_size_bytes % (multiply(sectors_per_cluster)(sector_size_bytes))
|
|
43
|
+
== 0
|
|
44
|
+
):
|
|
45
|
+
number_of_clusters = physical_file_size_bytes // multiply(sectors_per_cluster)(
|
|
46
|
+
sector_size_bytes
|
|
47
|
+
)
|
|
48
|
+
else:
|
|
49
|
+
number_of_clusters = (
|
|
50
|
+
physical_file_size_bytes // (sectors_per_cluster * sector_size_bytes)
|
|
51
|
+
) + 1
|
|
52
|
+
|
|
53
|
+
multiply_number_of_clusters = multiply(number_of_clusters)
|
|
54
|
+
multiply_number_of_clusters_by_sectors_per_cluster = multiply(
|
|
55
|
+
multiply_number_of_clusters(sectors_per_cluster)
|
|
56
|
+
)
|
|
57
|
+
slack_space_bytes = subtract(physical_file_size_bytes)(
|
|
58
|
+
multiply_number_of_clusters_by_sectors_per_cluster(sector_size_bytes)
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
return (number_of_clusters, slack_space_bytes)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def ml_f1_score(precision: float, recall: float) -> float:
|
|
65
|
+
"""F1 score: related to the harmonic mean of precision and recall. Calculated as F1 = 2/[(1/Precision) + (1/Recall)] = 2/[(TP + FP)/TP + (TP + FN)/TP] = 2/[(2TP + FP + FN)/TP] = 2TP/[2TP + FP + FN] . A high F1 score implies the system has low numbers of false positives and false negatives. - TM358"""
|
|
66
|
+
f1 = divide(add(divide(precision)(1))(divide(recall)(1)))(2)
|
|
67
|
+
|
|
68
|
+
return f1
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def ml_precesion(true_positives):
|
|
72
|
+
return lambda false_positives: divide(add(true_positives)(false_positives))(
|
|
73
|
+
true_positives
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def ml_recall(true_positives):
|
|
78
|
+
"""Fraction of total positives out of both true and false positives - also known as the true positive rate."""
|
|
79
|
+
return lambda false_negatives: divide(add(true_positives)(false_negatives))(
|
|
80
|
+
true_positives
|
|
81
|
+
) # TM358
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def ml_false_positive_rate(false_positives):
|
|
85
|
+
return lambda true_negatives: divide(add(false_positives)(true_negatives))(
|
|
86
|
+
false_positives
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def ml_weighted_inputs(inputs: list[float], weights: list[float]) -> list[float] | None:
|
|
91
|
+
"Multiply the inputs by the weights - TM358 Block 1"
|
|
92
|
+
weighted_inputs = []
|
|
93
|
+
loop_count = 0
|
|
94
|
+
if len(inputs) != len(weights):
|
|
95
|
+
return None
|
|
96
|
+
for x in inputs:
|
|
97
|
+
weighted_inputs.append(multiply(x)(weights[inputs.index(x, loop_count)]))
|
|
98
|
+
loop_count += 1
|
|
99
|
+
|
|
100
|
+
return weighted_inputs
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def ml_perceptron(inputs: list[float], weights: list[float], bias: float = 0) -> float:
|
|
104
|
+
x = ml_weighted_inputs(inputs, weights)
|
|
105
|
+
if x == None:
|
|
106
|
+
return 0 # TODO: this is dangerous
|
|
107
|
+
return ml_activation_function(add(sum(x))(bias))
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def ml_activation_function(input: float, threshold: float = 0):
|
|
111
|
+
if input > threshold:
|
|
112
|
+
return 1
|
|
113
|
+
else:
|
|
114
|
+
return 0
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def byte_to_ascii_text(input: int) -> str:
|
|
118
|
+
# https://www.rapidtables.com/convert/number/binary-to-ascii.html
|
|
119
|
+
value = byte_to_decimal(input)
|
|
120
|
+
|
|
121
|
+
return chr(value)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def byte_to_decimal(input: int) -> int:
|
|
125
|
+
"""Takes an 8 bit value and returns its value in decimal form."""
|
|
126
|
+
value = 0
|
|
127
|
+
tracker = len(str(input)) - 1
|
|
128
|
+
|
|
129
|
+
for bit in str(input):
|
|
130
|
+
if int(bit) != 0:
|
|
131
|
+
exponentiateByTracker = exponentiate(tracker)
|
|
132
|
+
value += exponentiateByTracker(2)
|
|
133
|
+
tracker -= 1
|
|
134
|
+
return value
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def byte_to_hex(input: int) -> str:
|
|
138
|
+
str_input = str(input)
|
|
139
|
+
length = len(str_input)
|
|
140
|
+
|
|
141
|
+
if length == 8:
|
|
142
|
+
return f"{bit_to_hex(str_input[:4])}{bit_to_hex(str_input[4:])}"
|
|
143
|
+
if length == 7:
|
|
144
|
+
return f"{bit_to_hex(str_input[:3])}{bit_to_hex(str_input[3:])}"
|
|
145
|
+
if length == 6:
|
|
146
|
+
return f"{bit_to_hex(str_input[:2])}{bit_to_hex(str_input[2:])}"
|
|
147
|
+
if length == 5:
|
|
148
|
+
return f"{bit_to_hex(str_input[:1])}{bit_to_hex(str_input[1:])}"
|
|
149
|
+
if length <= 4:
|
|
150
|
+
return f"{bit_to_hex(str_input)}"
|
|
151
|
+
|
|
152
|
+
return str_input
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def bit_to_hex(bit_string: str) -> str:
|
|
156
|
+
bit_string = bit_string
|
|
157
|
+
if bit_string == "0":
|
|
158
|
+
return "0"
|
|
159
|
+
if bit_string == "1":
|
|
160
|
+
return "1"
|
|
161
|
+
if bit_string == "10":
|
|
162
|
+
return "2"
|
|
163
|
+
if bit_string == "11":
|
|
164
|
+
return "3"
|
|
165
|
+
if bit_string == "100":
|
|
166
|
+
return "4"
|
|
167
|
+
if bit_string == "101":
|
|
168
|
+
return "5"
|
|
169
|
+
if bit_string == "110":
|
|
170
|
+
return "6"
|
|
171
|
+
if bit_string == "111":
|
|
172
|
+
return "7"
|
|
173
|
+
if bit_string == "1000":
|
|
174
|
+
return "8"
|
|
175
|
+
if bit_string == "1001":
|
|
176
|
+
return "9"
|
|
177
|
+
if bit_string == "1010":
|
|
178
|
+
return "A"
|
|
179
|
+
if bit_string == "1011":
|
|
180
|
+
return "B"
|
|
181
|
+
if bit_string == "1100":
|
|
182
|
+
return "C"
|
|
183
|
+
if bit_string == "1101":
|
|
184
|
+
return "D"
|
|
185
|
+
if bit_string == "1110":
|
|
186
|
+
return "E"
|
|
187
|
+
if bit_string == "1111":
|
|
188
|
+
return "F"
|
|
189
|
+
|
|
190
|
+
return bit_string
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
from functools import reduce
|
|
2
|
+
import math
|
|
3
|
+
from .operation import (
|
|
4
|
+
SQUARE,
|
|
5
|
+
add,
|
|
6
|
+
divide,
|
|
7
|
+
exponentiate,
|
|
8
|
+
multiply,
|
|
9
|
+
factorial,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def sigmoid(input: float, bias: float = 0) -> float:
|
|
14
|
+
"""Calculates the sigmoid of a value"""
|
|
15
|
+
# TM358 Section Block 1 section 5
|
|
16
|
+
add_bias = add(bias)
|
|
17
|
+
return divide(add(1)(exponentiate(add_bias(-input))(math.e)))(1)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def inverse_square_law(source_strength: float, distance_metres: float) -> float:
|
|
21
|
+
"""Calculates the inverse square law"""
|
|
22
|
+
##TM255 block 1
|
|
23
|
+
four_times_pi = multiply(4)(math.pi)
|
|
24
|
+
denominator = multiply(SQUARE(distance_metres))(four_times_pi)
|
|
25
|
+
return divide(denominator)(source_strength)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def taylor_series(value):
|
|
29
|
+
"""Calculates the Taylor series of a value"""
|
|
30
|
+
|
|
31
|
+
# Written for sin and cos functions so may not be generic for pure Taylor Series functions.
|
|
32
|
+
def steps(
|
|
33
|
+
initial_value_in_series, increment=1, initial_denmoninator=1, steps=3, sign=-1
|
|
34
|
+
):
|
|
35
|
+
taylor = [initial_value_in_series]
|
|
36
|
+
for _ in range(steps):
|
|
37
|
+
exponent = exponentiate(initial_denmoninator)
|
|
38
|
+
division = divide(factorial(initial_denmoninator))
|
|
39
|
+
taylor.append(division(exponent(value)))
|
|
40
|
+
initial_denmoninator += increment
|
|
41
|
+
|
|
42
|
+
for index in range(len(taylor)):
|
|
43
|
+
sign = -1 if sign == +1 else +1
|
|
44
|
+
taylor[index] = taylor[index] * sign
|
|
45
|
+
|
|
46
|
+
return reduce(lambda a, b: a + b, taylor)
|
|
47
|
+
|
|
48
|
+
return steps
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def herons_method(value: float):
|
|
52
|
+
"""
|
|
53
|
+
O(log precision) method for calculating square roots. The square roots of two values (eg. 25, 2982186) will be calculated in the same number of cycles to the same precision
|
|
54
|
+
"""
|
|
55
|
+
# www.youtube.com/watch?v=l2TCgS_eLwA
|
|
56
|
+
initial_estimate = value / 2
|
|
57
|
+
current_step = initial_estimate
|
|
58
|
+
epsilon = value * exponentiate(-9)(10)
|
|
59
|
+
|
|
60
|
+
while True:
|
|
61
|
+
next_step = 1 / 2 * (current_step + (value / current_step))
|
|
62
|
+
if next_step < epsilon:
|
|
63
|
+
break
|
|
64
|
+
current_step = next_step
|
|
65
|
+
|
|
66
|
+
return current_step
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
from astronomy_types import (
|
|
2
|
+
Coordinate2D,
|
|
3
|
+
Distance,
|
|
4
|
+
Eccentricity,
|
|
5
|
+
Radians,
|
|
6
|
+
Ratio,
|
|
7
|
+
Scalar,
|
|
8
|
+
SemiMajorAxis,
|
|
9
|
+
TrueAnomaly,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
from .operation import (
|
|
13
|
+
HALF,
|
|
14
|
+
SQUARE,
|
|
15
|
+
add,
|
|
16
|
+
divide,
|
|
17
|
+
multiply,
|
|
18
|
+
subtract,
|
|
19
|
+
)
|
|
20
|
+
import math
|
|
21
|
+
from .formula import taylor_series
|
|
22
|
+
|
|
23
|
+
pythagoras = lambda a: lambda b: add(SQUARE(a))(SQUARE(b))
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def euclid(m: int, n: int) -> int:
|
|
27
|
+
"""Given two positive integers, m and n, find their greatest common divisor which is the largest positive integer that divides both evenly."""
|
|
28
|
+
|
|
29
|
+
remainder = m % n
|
|
30
|
+
if remainder == 0:
|
|
31
|
+
return n
|
|
32
|
+
else:
|
|
33
|
+
m = n
|
|
34
|
+
n = remainder
|
|
35
|
+
euclid(m, n)
|
|
36
|
+
return n
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def sieve_of_eratosthenes(n: int) -> list[int]:
|
|
40
|
+
"""Finds prime numbers up to n"""
|
|
41
|
+
# https://www.youtube.com/watch?v=fwxjMKBMR7s
|
|
42
|
+
numbers = [True] * n
|
|
43
|
+
primes = []
|
|
44
|
+
|
|
45
|
+
for index in range(2, n):
|
|
46
|
+
if numbers[index]:
|
|
47
|
+
primes.append(index)
|
|
48
|
+
for multiple in range(index * index, n, index):
|
|
49
|
+
numbers[multiple] = False
|
|
50
|
+
|
|
51
|
+
return primes
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def tangent(angle_degrees: float) -> float:
|
|
55
|
+
"""Returns a value in radians"""
|
|
56
|
+
return divide(cosine(angle_degrees))(sine(angle_degrees))
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def arctangent(radians):
|
|
60
|
+
return math.degrees(math.atan(radians))
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def sine(angle_degrees: float) -> float:
|
|
64
|
+
"""Returns a value in radians"""
|
|
65
|
+
return taylor_series(math.radians(angle_degrees))(math.radians(angle_degrees), 2, 3)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def cosine(angle_degrees: float) -> float:
|
|
69
|
+
"""Returns a value in radians"""
|
|
70
|
+
return taylor_series(math.radians(angle_degrees))(1, 2, 2)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
# TODO: Check naming, this may be valid for more than just right triangles
|
|
74
|
+
def area_of_right_triangle(base_length: float, height_length: float) -> float:
|
|
75
|
+
"""Area = base * height / 2"""
|
|
76
|
+
return HALF(multiply(base_length)(height_length))
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def area_of_quarter_circle(side_length: float, radius: float) -> float:
|
|
80
|
+
"""Area = s^2 - ((1/4)*pi*r^2)"""
|
|
81
|
+
return subtract((1 / 4) * math.pi * SQUARE(radius))(SQUARE(side_length))
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def semi_major_axis_from_axes(a: float, b: float) -> SemiMajorAxis:
|
|
85
|
+
"""Returns the semi major axis of an ellipse given the lengths of the two axes"""
|
|
86
|
+
return HALF(add(a)(b))
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def circle_bounding_box_from_coordinates(
|
|
90
|
+
coordinates: Coordinate2D, radius: float
|
|
91
|
+
) -> tuple[Coordinate2D, Coordinate2D]:
|
|
92
|
+
"""
|
|
93
|
+
Calculate the bounding box of a circle given its center coordinates and radius.
|
|
94
|
+
|
|
95
|
+
Parameters:
|
|
96
|
+
x (float): The x-coordinate of the circle's center.
|
|
97
|
+
y (float): The y-coordinate of the circle's center.
|
|
98
|
+
radius (float): The radius of the circle.
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
tuple: A tuple containing the coordinates of the bounding box in the format (x0, y0, x1, y1).
|
|
102
|
+
"""
|
|
103
|
+
return draw_circle_with_eccentricity(
|
|
104
|
+
coordinates, radius, Eccentricity(Ratio(Scalar(0)))
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def draw_circle_with_eccentricity(
|
|
109
|
+
coordinates: Coordinate2D, radius: float, eccentricity: Eccentricity
|
|
110
|
+
) -> tuple[Coordinate2D, Coordinate2D]:
|
|
111
|
+
"""
|
|
112
|
+
Calculate the bounding box of an ellipse given its center coordinates, radius, and eccentricity.
|
|
113
|
+
|
|
114
|
+
Parameters:
|
|
115
|
+
x (float): The x-coordinate of the ellipse's center.
|
|
116
|
+
y (float): The y-coordinate of the ellipse's center.
|
|
117
|
+
radius (float): The semi-major axis of the ellipse.
|
|
118
|
+
eccentricity (float): The eccentricity of the ellipse (0 <= eccentricity < 1).
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
tuple: A tuple containing the coordinates of the bounding box in the format (x0, y0, x1, y1).
|
|
122
|
+
"""
|
|
123
|
+
if eccentricity < 0 or eccentricity >= 1:
|
|
124
|
+
raise ValueError("Eccentricity must be in the range [0, 1).")
|
|
125
|
+
|
|
126
|
+
semi_minor_axis = radius * (1 - eccentricity**2) ** 0.5
|
|
127
|
+
x0 = coordinates.x - radius
|
|
128
|
+
y0 = coordinates.y - semi_minor_axis
|
|
129
|
+
x1 = coordinates.x + radius
|
|
130
|
+
y1 = coordinates.y + semi_minor_axis
|
|
131
|
+
return (Coordinate2D(x0, y0), Coordinate2D(x1, y1))
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def calculate_distance(
|
|
135
|
+
coordinates1: Coordinate2D, coordinates2: Coordinate2D
|
|
136
|
+
) -> Distance:
|
|
137
|
+
"""
|
|
138
|
+
Calculate the distance between two points in 2D space.
|
|
139
|
+
|
|
140
|
+
Parameters:
|
|
141
|
+
x1 (float): The x-coordinate of the first point.
|
|
142
|
+
y1 (float): The y-coordinate of the first point.
|
|
143
|
+
x2 (float): The x-coordinate of the second point.
|
|
144
|
+
y2 (float): The y-coordinate of the second point.
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
float: The distance between the two points.
|
|
148
|
+
"""
|
|
149
|
+
return (
|
|
150
|
+
(coordinates2.x - coordinates1.x) ** 2 + (coordinates2.y - coordinates1.y) ** 2
|
|
151
|
+
) ** 0.5
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def calculate_foci(
|
|
155
|
+
semi_major_axis: SemiMajorAxis, eccentricity: Eccentricity
|
|
156
|
+
) -> tuple[Coordinate2D, Coordinate2D]:
|
|
157
|
+
"""
|
|
158
|
+
Calculate the coordinates of the foci of an ellipse given its semi-major axis and eccentricity.
|
|
159
|
+
|
|
160
|
+
Parameters:
|
|
161
|
+
a (float): The semi-major axis of the ellipse.
|
|
162
|
+
e (float): The eccentricity of the ellipse (0 <= eccentricity < 1).
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
tuple: A tuple containing the coordinates of the two foci in the format ((x1, y1), (x2, y2)).
|
|
166
|
+
"""
|
|
167
|
+
if eccentricity < 0 or eccentricity >= 1:
|
|
168
|
+
raise ValueError("Eccentricity must be in the range [0, 1).")
|
|
169
|
+
|
|
170
|
+
c = semi_major_axis * eccentricity
|
|
171
|
+
# [X1, Y1] = (-c, 0) and [X2, Y2] = (c, 0) for an ellipse centered at the origin
|
|
172
|
+
return (Coordinate2D(-c, 0), Coordinate2D(c, 0))
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def calculate_semi_major_axis(
|
|
176
|
+
radius: Distance, eccentricity: Eccentricity
|
|
177
|
+
) -> SemiMajorAxis:
|
|
178
|
+
"""
|
|
179
|
+
Calculate the semi-major axis of an ellipse given the distance from the center to a point on the ellipse and the eccentricity.
|
|
180
|
+
|
|
181
|
+
Parameters:
|
|
182
|
+
r (float): The distance from the center of the ellipse to a point on the ellipse.
|
|
183
|
+
e (float): The eccentricity of the ellipse (0 <= eccentricity < 1).
|
|
184
|
+
|
|
185
|
+
Returns:
|
|
186
|
+
float: The semi-major axis of the ellipse.
|
|
187
|
+
"""
|
|
188
|
+
if eccentricity < 0 or eccentricity >= 1:
|
|
189
|
+
raise ValueError("Eccentricity must be in the range [0, 1).")
|
|
190
|
+
|
|
191
|
+
return SemiMajorAxis(Distance(Scalar(radius / (1 - eccentricity))))
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def calculate_semi_minor_axis(
|
|
195
|
+
semi_major_axis: SemiMajorAxis, eccentricity: Eccentricity
|
|
196
|
+
) -> float:
|
|
197
|
+
"""
|
|
198
|
+
Calculate the semi-minor axis of an ellipse given its semi-major axis and eccentricity.
|
|
199
|
+
|
|
200
|
+
Parameters:
|
|
201
|
+
a (float): The semi-major axis of the ellipse.
|
|
202
|
+
e (float): The eccentricity of the ellipse (0 <= eccentricity < 1).
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
float: The semi-minor axis of the ellipse.
|
|
206
|
+
"""
|
|
207
|
+
if eccentricity < 0 or eccentricity >= 1:
|
|
208
|
+
raise ValueError("Eccentricity must be in the range [0, 1).")
|
|
209
|
+
|
|
210
|
+
return semi_major_axis * (1 - eccentricity**2) ** 0.5
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
from operation import divide, multiply, subtract, termial
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@dataclass(frozen=True)
|
|
7
|
+
class GraphCoordinates:
|
|
8
|
+
x: float
|
|
9
|
+
y: float
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def slope_gradiant(point1: GraphCoordinates, point2: GraphCoordinates) -> float:
|
|
13
|
+
"""Calculates the slope between two points"""
|
|
14
|
+
##https://www.bbc.co.uk/bitesize/topics/zvhs34j/articles/z4ctng8
|
|
15
|
+
return divide(subtract(point1.x)(point2.x))(subtract(point1.y)(point2.y))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def equation_of_line(
|
|
19
|
+
point1: GraphCoordinates, point2: GraphCoordinates
|
|
20
|
+
) -> tuple[float, float]:
|
|
21
|
+
"""Returns the slope and y-intercept of the line passing through two points"""
|
|
22
|
+
##m = gradient
|
|
23
|
+
m = slope_gradiant(point1, point2)
|
|
24
|
+
##b = y intercept when x = 0
|
|
25
|
+
rhs = multiply(m)(point1.x)
|
|
26
|
+
b = subtract(rhs)(point1.y)
|
|
27
|
+
##y = mx + b
|
|
28
|
+
return (m, b)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def interpolations_for_bezier_curve(number_of_control_points: int) -> int:
|
|
32
|
+
"""Calculates the number of interpolations needed for a bezier curve with a given number of control points
|
|
33
|
+
interpolations = n(n+1)/2
|
|
34
|
+
"""
|
|
35
|
+
# if points < 2:
|
|
36
|
+
# return 0
|
|
37
|
+
# return points - 1 + interpolations_for_bezier_curve(points - 1)
|
|
38
|
+
return termial(number_of_control_points)
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import math
|
|
2
|
+
import statistics
|
|
3
|
+
from operation import divide, subtract
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def sort(number_list: list[float]) -> list[float]:
|
|
7
|
+
return sorted(number_list)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def length(number_list: list[float]) -> int:
|
|
11
|
+
return len(number_list)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def sum(number_list: list[float]) -> float:
|
|
15
|
+
return math.sum(number_list)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def minimum(number_list: list[float]) -> float:
|
|
19
|
+
return min(number_list)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def maximum(number_list: list[float]) -> float:
|
|
23
|
+
return max(number_list)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def range(number_list: list[float]) -> float:
|
|
27
|
+
return subtract(minimum(number_list))(maximum(number_list))
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def mean(number_list: list[float]) -> float:
|
|
31
|
+
return divide(length(number_list))(sum(number_list))
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def median(number_list: list[float]) -> float:
|
|
35
|
+
return statistics.median(number_list)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def quartiles(number_list: list[float]) -> tuple[float, float, float]:
|
|
39
|
+
"""Calculates the quartiles of a list of numbers"""
|
|
40
|
+
number_list = sorted(number_list)
|
|
41
|
+
q1_index = int(math.ceil(length(number_list) * 0.25))
|
|
42
|
+
q1_result = number_list[q1_index - 1]
|
|
43
|
+
number_list[0 : length(number_list) // 2]
|
|
44
|
+
q3_result = median(number_list)
|
|
45
|
+
iqr_result = subtract(q1_result)(q3_result)
|
|
46
|
+
|
|
47
|
+
return q1_result, q3_result, iqr_result
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def dataplotter(number_list: list[float]) -> None:
|
|
51
|
+
##MU123
|
|
52
|
+
"""
|
|
53
|
+
Runs some useful functions on a provided list
|
|
54
|
+
"""
|
|
55
|
+
sort(number_list)
|
|
56
|
+
length(number_list)
|
|
57
|
+
sum(number_list)
|
|
58
|
+
minimum(number_list)
|
|
59
|
+
maximum(number_list)
|
|
60
|
+
range(number_list)
|
|
61
|
+
mean(number_list)
|
|
62
|
+
median(number_list)
|
|
63
|
+
quartiles(number_list)
|