Skip to content

Commit 9719f0e

Browse files
authored
Merge pull request #40 from JHay0112/random-testing
Random Tests
2 parents b32ccc3 + 52b06d8 commit 9719f0e

File tree

4 files changed

+178
-42
lines changed

4 files changed

+178
-42
lines changed

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
# jmath
2+
3+
[![wakatime](https://wakatime.com/badge/github/JHay0112/jmath.svg)](https://wakatime.com/badge/github/JHay0112/jmath)
4+
25
jmath is a hobby project developing a set of mathematical tools in the Python Language.
36

47
***
@@ -41,4 +44,4 @@ Open a terminal in the root director and run `pytest tests/`, this requires pyte
4144

4245
## Github Workflows
4346

44-
Github workflows help automate testing, updating the requirements, and publishing this repo.
47+
Github workflows help automate testing, updating the requirements, and publishing this repo.

jmath/linearalgebra.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class Vector:
2020
Parameters
2121
----------
2222
23-
\*components
23+
components
2424
Scalar vector components
2525
2626
Examples
@@ -102,21 +102,19 @@ def __add__(self, vector: "Vector") -> "Vector":
102102
"""Add vectors together"""
103103

104104
# Add the foreign components to local components and return
105-
return(Vector(list(map(operator.add, self.components, vector.components))))
105+
return Vector([i + j for i, j in zip(self.components, vector.components)])
106106

107107
@__same_size
108108
def __sub__(self, vector: "Vector") -> "Vector":
109109
"""Subtract vectors from each other"""
110+
110111
# Subtract the foreign components from local components and return
111-
return(Vector(list(map(operator.sub, self.components, vector.components))))
112+
return Vector([i - j for i, j in zip(self.components, vector.components)])
112113

113114
@__same_size
114115
def __matmul__(self, vector: "Vector") -> float:
115116
"""The dot product of two vectors"""
116-
dot = 0
117-
for i in range(len(self.components)):
118-
dot += self.components[i] * vector.components[i]
119-
return dot
117+
return sum([i * j for i, j in zip(self.components, vector.components)])
120118

121119
def __mul__(self, scalar: float) -> "Vector":
122120
"""Scalar multiplication"""
@@ -291,7 +289,7 @@ class Point(Vector):
291289
Parameters
292290
----------
293291
294-
\*components
292+
components
295293
Coordinates in n-space
296294
"""
297295
def __init__(self, *components: List[float]):

tests/test_linearalgebra.py

Lines changed: 103 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -9,39 +9,97 @@
99

1010
# - Imports
1111

12-
from ..jmath.linearalgebra import *
12+
from ..jmath.linearalgebra import Vector, Point, Line
13+
from .tools import random_integer, random_integers, repeat
14+
from typing import Tuple, List
15+
from math import sqrt
1316

1417
# - Functions
1518

16-
def test_vector_arithmetic():
17-
"""Tests vector addition and subtraction"""
18-
# Add/Subtract
19-
added = Vector(1, 2) + Vector(3, 4)
20-
subtracted = Vector(1, 2) - Vector(3, 4)
21-
# Test Add
22-
excepted = Vector(4, 6)
23-
assert added == excepted
24-
# Test Subtract
25-
excepted = Vector(-2, -2)
26-
assert subtracted == excepted
19+
def vector_component_pair(len: int = random_integer()) -> Tuple[Vector, List[int]]:
20+
"""
21+
Generates a vector and component pair randomly.
22+
23+
Parameters
24+
----------
2725
26+
len
27+
The length vector/components to generate
28+
"""
29+
components = random_integers(len)
30+
return (Vector(components), components)
31+
32+
@repeat
33+
def test_vector_equality():
34+
"""Tests that vectors are equal as expected."""
35+
v, c = vector_component_pair()
36+
37+
assert v == v
38+
assert v == Vector(c)
39+
assert v.components == c
40+
assert v != v.negative()
41+
42+
@repeat
43+
def test_vector_addition():
44+
"""Tests vector addition."""
45+
len = random_integer()
46+
v1, c1 = vector_component_pair(len)
47+
v2, c2 = vector_component_pair(len)
48+
49+
expected = Vector([i + j for i, j in zip(c1, c2)])
50+
51+
assert (v1 + v2) == expected
52+
53+
@repeat
54+
def test_vector_subtraction():
55+
"""Tests vector subtraction."""
56+
len = random_integer()
57+
v1, c1 = vector_component_pair(len)
58+
v2, c2 = vector_component_pair(len)
59+
60+
expected = Vector([i - j for i, j in zip(c1, c2)])
61+
62+
assert (v1 - v2) == expected
63+
64+
@repeat
2865
def test_vector_scaling():
2966
"""Tests vector multiplication and division"""
30-
# Divide/multiply
31-
multiplied = Vector(3, 4) * 4
32-
divided = Vector(1, 2) / 2
67+
# Produce initial conditions
68+
length = random_integer(min = 3, max = 10)
69+
scalor = random_integer(min = 1, max = 10)
70+
v, c = vector_component_pair(length)
71+
72+
# Mult/div vectors
73+
mult = v * scalor
74+
div = v / scalor
75+
76+
# Compute expected
77+
mult_expected = Vector([scalor * i for i in c])
78+
div_expected = Vector([round(i / scalor, 5) for i in c])
79+
80+
# Round division vector to factor out floating point error
81+
div.components = [round(i, 5) for i in div.components]
82+
3383
# Test multiply
34-
expected = Vector(12, 16)
35-
assert multiplied == expected
84+
assert mult_expected == mult
3685
# Test divide
37-
expected = Vector(1/2, 1)
38-
assert divided == expected
86+
assert div_expected == div
3987

88+
@repeat
4089
def test_dot_product():
4190
"""Tests the dot product"""
42-
dot = Vector(1, 2) @ Vector(3, 4)
43-
expected = 1*3 + 2*4
44-
assert dot == expected
91+
# Generate vectors and components
92+
len = random_integer()
93+
v1, c1 = vector_component_pair(len)
94+
v2, c2 = vector_component_pair(len)
95+
96+
# Compute dot product
97+
dot = v1 @ v2
98+
99+
# Predict dot product
100+
predicted_dot = sum([i * j for i, j in zip(c1, c2)])
101+
102+
assert dot == predicted_dot
45103

46104
def test_projection():
47105
"""Tests projecting vectors"""
@@ -54,19 +112,24 @@ def test_projection():
54112
line = Line(None, vec2)
55113
assert vec1.projection(line) == expected
56114

115+
@repeat
57116
def test_magnitude():
58117
"""Tests vector magnitude"""
59-
vector = Vector(3, 4, 5, -2)
60-
assert round(vector.magnitude(), 2) == 7.35
118+
119+
v, c = vector_component_pair()
120+
121+
# Square components, sum, and sqrt
122+
predicted_magnitude = sqrt(sum([i ** 2 for i in c]))
123+
124+
assert round(predicted_magnitude, 5) == round(v.magnitude(), 5)
61125

126+
@repeat
62127
def test_vector_size():
63128
"""Tests that a vector will return the correct size"""
64-
# Vector with six entries
65-
vector = Vector(1, 2, 3, 4, 5, 6)
66-
assert len(vector) == 6
67-
# Vector with one entry
68-
vector = Vector(0)
69-
assert len(vector) == 1
129+
130+
v, c = vector_component_pair()
131+
132+
assert len(v) == len(c)
70133

71134
def test_point_in_line():
72135
"""Tests whether a point is in a line"""
@@ -92,11 +155,18 @@ def test_angle_between():
92155

93156
assert vec1.angle_between(vec2) == 0
94157

158+
@repeat
95159
def test_negative():
96160
"""Test that a negative vector does indeed give one with all the components reversed"""
97-
vec = Vector(1, 2).negative()
98-
expected_vec = Vector(-1, -2)
99-
assert vec == expected_vec
161+
162+
# Generate vector component pair
163+
v, c = vector_component_pair()
164+
# Make negative vector
165+
v = v.negative()
166+
# Make components negative
167+
c = Vector([-i for i in c])
168+
169+
assert v == c
100170

101171
def test_unit_vector():
102172
"""Tests that a unit vector is produced correctly"""

tests/tools.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
'''
2+
Tools for generating tests.
3+
'''
4+
5+
# - Imports
6+
7+
from random import randint
8+
from typing import List
9+
10+
# - Functions
11+
12+
def repeat(func):
13+
"""
14+
Wrapper that repeats a function 10 times.
15+
"""
16+
17+
def inner():
18+
for i in range(10):
19+
return func()
20+
21+
return inner
22+
23+
def random_integer(min: int = 0, max: int = 100) -> int:
24+
"""
25+
Generates a random integer. Wrapper of random.randint.
26+
27+
Parameters
28+
----------
29+
30+
min
31+
The minimum value int to produce.
32+
max
33+
The maximum value int to produce.
34+
"""
35+
36+
return randint(min, max)
37+
38+
def random_integers(length: int):
39+
"""
40+
Returns a random value list of set size.
41+
42+
Parameters
43+
----------
44+
45+
length
46+
The length of the list to generate
47+
"""
48+
return [random_integer() for i in range(length)]
49+
50+
def random_list(min: int = 3, max: int = 30) -> List[int]:
51+
"""
52+
Generates a random length list of random integers.
53+
54+
Parameters
55+
----------
56+
57+
min
58+
The minimum length of the list.
59+
max
60+
The maximum length of the list.
61+
"""
62+
63+
length = random_integer(min, max)
64+
65+
return random_integers(length)

0 commit comments

Comments
 (0)