Compare commits

..

No commits in common. "3040f9970757ce8fae91ba1c791c1909c9f17df5" and "b03c5a3ba017e05b4bc3604769c97f7111a56665" have entirely different histories.

34 changed files with 409 additions and 1337 deletions

View File

@ -16,7 +16,7 @@ repos:
- id: auto-walrus
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.0.284
rev: v0.0.282
hooks:
- id: ruff
@ -33,7 +33,7 @@ repos:
- tomli
- repo: https://github.com/tox-dev/pyproject-fmt
rev: "0.13.1"
rev: "0.13.0"
hooks:
- id: pyproject-fmt
@ -51,7 +51,7 @@ repos:
- id: validate-pyproject
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.5.0
rev: v1.4.1
hooks:
- id: mypy
args:

View File

@ -62,7 +62,7 @@
## Boolean Algebra
* [And Gate](boolean_algebra/and_gate.py)
* [Nand Gate](boolean_algebra/nand_gate.py)
* [Nor Gate](boolean_algebra/nor_gate.py)
* [Norgate](boolean_algebra/norgate.py)
* [Not Gate](boolean_algebra/not_gate.py)
* [Or Gate](boolean_algebra/or_gate.py)
* [Quine Mc Cluskey](boolean_algebra/quine_mc_cluskey.py)
@ -74,7 +74,6 @@
* [Game Of Life](cellular_automata/game_of_life.py)
* [Nagel Schrekenberg](cellular_automata/nagel_schrekenberg.py)
* [One Dimensional](cellular_automata/one_dimensional.py)
* [Wa Tor](cellular_automata/wa_tor.py)
## Ciphers
* [A1Z26](ciphers/a1z26.py)
@ -336,11 +335,9 @@
* [Minimum Tickets Cost](dynamic_programming/minimum_tickets_cost.py)
* [Optimal Binary Search Tree](dynamic_programming/optimal_binary_search_tree.py)
* [Palindrome Partitioning](dynamic_programming/palindrome_partitioning.py)
* [Regex Match](dynamic_programming/regex_match.py)
* [Rod Cutting](dynamic_programming/rod_cutting.py)
* [Subset Generation](dynamic_programming/subset_generation.py)
* [Sum Of Subset](dynamic_programming/sum_of_subset.py)
* [Tribonacci](dynamic_programming/tribonacci.py)
* [Viterbi](dynamic_programming/viterbi.py)
* [Word Break](dynamic_programming/word_break.py)
@ -555,7 +552,6 @@
* [Chudnovsky Algorithm](maths/chudnovsky_algorithm.py)
* [Collatz Sequence](maths/collatz_sequence.py)
* [Combinations](maths/combinations.py)
* [Continued Fraction](maths/continued_fraction.py)
* [Decimal Isolate](maths/decimal_isolate.py)
* [Decimal To Fraction](maths/decimal_to_fraction.py)
* [Dodecahedron](maths/dodecahedron.py)
@ -574,7 +570,9 @@
* [Fermat Little Theorem](maths/fermat_little_theorem.py)
* [Fibonacci](maths/fibonacci.py)
* [Find Max](maths/find_max.py)
* [Find Max Recursion](maths/find_max_recursion.py)
* [Find Min](maths/find_min.py)
* [Find Min Recursion](maths/find_min_recursion.py)
* [Floor](maths/floor.py)
* [Gamma](maths/gamma.py)
* [Gamma Recursive](maths/gamma_recursive.py)
@ -1171,7 +1169,6 @@
* [Is Pangram](strings/is_pangram.py)
* [Is Spain National Id](strings/is_spain_national_id.py)
* [Is Srilankan Phone Number](strings/is_srilankan_phone_number.py)
* [Is Valid Email Address](strings/is_valid_email_address.py)
* [Jaro Winkler](strings/jaro_winkler.py)
* [Join](strings/join.py)
* [Knuth Morris Pratt](strings/knuth_morris_pratt.py)

View File

@ -74,7 +74,10 @@ def is_for_table(string1: str, string2: str, count: int) -> bool:
"""
list1 = list(string1)
list2 = list(string2)
count_n = sum(item1 != item2 for item1, item2 in zip(list1, list2))
count_n = 0
for i in range(len(list1)):
if list1[i] != list2[i]:
count_n += 1
return count_n == count
@ -89,34 +92,40 @@ def selection(chart: list[list[int]], prime_implicants: list[str]) -> list[str]:
temp = []
select = [0] * len(chart)
for i in range(len(chart[0])):
count = sum(row[i] == 1 for row in chart)
count = 0
rem = -1
for j in range(len(chart)):
if chart[j][i] == 1:
count += 1
rem = j
if count == 1:
rem = max(j for j, row in enumerate(chart) if row[i] == 1)
select[rem] = 1
for i, item in enumerate(select):
if item != 1:
continue
for i in range(len(select)):
if select[i] == 1:
for j in range(len(chart[0])):
if chart[i][j] != 1:
continue
for row in chart:
row[j] = 0
if chart[i][j] == 1:
for k in range(len(chart)):
chart[k][j] = 0
temp.append(prime_implicants[i])
while True:
counts = [chart[i].count(1) for i in range(len(chart))]
max_n = max(counts)
rem = counts.index(max_n)
max_n = 0
rem = -1
count_n = 0
for i in range(len(chart)):
count_n = chart[i].count(1)
if count_n > max_n:
max_n = count_n
rem = i
if max_n == 0:
return temp
temp.append(prime_implicants[rem])
for j in range(len(chart[0])):
if chart[rem][j] != 1:
continue
for i in range(len(chart)):
chart[i][j] = 0
for i in range(len(chart[0])):
if chart[rem][i] == 1:
for j in range(len(chart)):
chart[j][i] = 0
def prime_implicant_chart(

View File

@ -1,550 +0,0 @@
"""
Wa-Tor algorithm (1984)
@ https://en.wikipedia.org/wiki/Wa-Tor
@ https://beltoforion.de/en/wator/
@ https://beltoforion.de/en/wator/images/wator_medium.webm
This solution aims to completely remove any systematic approach
to the Wa-Tor planet, and utilise fully random methods.
The constants are a working set that allows the Wa-Tor planet
to result in one of the three possible results.
"""
from collections.abc import Callable
from random import randint, shuffle
from time import sleep
from typing import Literal
WIDTH = 50 # Width of the Wa-Tor planet
HEIGHT = 50 # Height of the Wa-Tor planet
PREY_INITIAL_COUNT = 30 # The initial number of prey entities
PREY_REPRODUCTION_TIME = 5 # The chronons before reproducing
PREDATOR_INITIAL_COUNT = 50 # The initial number of predator entities
# The initial energy value of predator entities
PREDATOR_INITIAL_ENERGY_VALUE = 15
# The energy value provided when consuming prey
PREDATOR_FOOD_VALUE = 5
PREDATOR_REPRODUCTION_TIME = 20 # The chronons before reproducing
MAX_ENTITIES = 500 # The max number of organisms on the board
# The number of entities to delete from the unbalanced side
DELETE_UNBALANCED_ENTITIES = 50
class Entity:
"""
Represents an entity (either prey or predator).
>>> e = Entity(True, coords=(0, 0))
>>> e.prey
True
>>> e.coords
(0, 0)
>>> e.alive
True
"""
def __init__(self, prey: bool, coords: tuple[int, int]) -> None:
self.prey = prey
# The (row, col) pos of the entity
self.coords = coords
self.remaining_reproduction_time = (
PREY_REPRODUCTION_TIME if prey else PREDATOR_REPRODUCTION_TIME
)
self.energy_value = None if prey is True else PREDATOR_INITIAL_ENERGY_VALUE
self.alive = True
def reset_reproduction_time(self) -> None:
"""
>>> e = Entity(True, coords=(0, 0))
>>> e.reset_reproduction_time()
>>> e.remaining_reproduction_time == PREY_REPRODUCTION_TIME
True
>>> e = Entity(False, coords=(0, 0))
>>> e.reset_reproduction_time()
>>> e.remaining_reproduction_time == PREDATOR_REPRODUCTION_TIME
True
"""
self.remaining_reproduction_time = (
PREY_REPRODUCTION_TIME if self.prey is True else PREDATOR_REPRODUCTION_TIME
)
def __repr__(self) -> str:
"""
>>> Entity(prey=True, coords=(1, 1))
Entity(prey=True, coords=(1, 1), remaining_reproduction_time=5)
>>> Entity(prey=False, coords=(2, 1)) # doctest: +NORMALIZE_WHITESPACE
Entity(prey=False, coords=(2, 1),
remaining_reproduction_time=20, energy_value=15)
"""
repr_ = (
f"Entity(prey={self.prey}, coords={self.coords}, "
f"remaining_reproduction_time={self.remaining_reproduction_time}"
)
if self.energy_value is not None:
repr_ += f", energy_value={self.energy_value}"
return f"{repr_})"
class WaTor:
"""
Represents the main Wa-Tor algorithm.
:attr time_passed: A function that is called every time
time passes (a chronon) in order to visually display
the new Wa-Tor planet. The time_passed function can block
using time.sleep to slow the algorithm progression.
>>> wt = WaTor(10, 15)
>>> wt.width
10
>>> wt.height
15
>>> len(wt.planet)
15
>>> len(wt.planet[0])
10
>>> len(wt.get_entities()) == PREDATOR_INITIAL_COUNT + PREY_INITIAL_COUNT
True
"""
time_passed: Callable[["WaTor", int], None] | None
def __init__(self, width: int, height: int) -> None:
self.width = width
self.height = height
self.time_passed = None
self.planet: list[list[Entity | None]] = [[None] * width for _ in range(height)]
# Populate planet with predators and prey randomly
for _ in range(PREY_INITIAL_COUNT):
self.add_entity(prey=True)
for _ in range(PREDATOR_INITIAL_COUNT):
self.add_entity(prey=False)
self.set_planet(self.planet)
def set_planet(self, planet: list[list[Entity | None]]) -> None:
"""
Ease of access for testing
>>> wt = WaTor(WIDTH, HEIGHT)
>>> planet = [
... [None, None, None],
... [None, Entity(True, coords=(1, 1)), None]
... ]
>>> wt.set_planet(planet)
>>> wt.planet == planet
True
>>> wt.width
3
>>> wt.height
2
"""
self.planet = planet
self.width = len(planet[0])
self.height = len(planet)
def add_entity(self, prey: bool) -> None:
"""
Adds an entity, making sure the entity does
not override another entity
>>> wt = WaTor(WIDTH, HEIGHT)
>>> wt.set_planet([[None, None], [None, None]])
>>> wt.add_entity(True)
>>> len(wt.get_entities())
1
>>> wt.add_entity(False)
>>> len(wt.get_entities())
2
"""
while True:
row, col = randint(0, self.height - 1), randint(0, self.width - 1)
if self.planet[row][col] is None:
self.planet[row][col] = Entity(prey=prey, coords=(row, col))
return
def get_entities(self) -> list[Entity]:
"""
Returns a list of all the entities within the planet.
>>> wt = WaTor(WIDTH, HEIGHT)
>>> len(wt.get_entities()) == PREDATOR_INITIAL_COUNT + PREY_INITIAL_COUNT
True
"""
return [entity for column in self.planet for entity in column if entity]
def balance_predators_and_prey(self) -> None:
"""
Balances predators and preys so that prey
can not dominate the predators, blocking up
space for them to reproduce.
>>> wt = WaTor(WIDTH, HEIGHT)
>>> for i in range(2000):
... row, col = i // HEIGHT, i % WIDTH
... wt.planet[row][col] = Entity(True, coords=(row, col))
>>> entities = len(wt.get_entities())
>>> wt.balance_predators_and_prey()
>>> len(wt.get_entities()) == entities
False
"""
entities = self.get_entities()
shuffle(entities)
if len(entities) >= MAX_ENTITIES - MAX_ENTITIES / 10:
prey = [entity for entity in entities if entity.prey]
predators = [entity for entity in entities if not entity.prey]
prey_count, predator_count = len(prey), len(predators)
entities_to_purge = (
prey[:DELETE_UNBALANCED_ENTITIES]
if prey_count > predator_count
else predators[:DELETE_UNBALANCED_ENTITIES]
)
for entity in entities_to_purge:
self.planet[entity.coords[0]][entity.coords[1]] = None
def get_surrounding_prey(self, entity: Entity) -> list[Entity]:
"""
Returns all the prey entities around (N, S, E, W) a predator entity.
Subtly different to the try_to_move_to_unoccupied square.
>>> wt = WaTor(WIDTH, HEIGHT)
>>> wt.set_planet([
... [None, Entity(True, (0, 1)), None],
... [None, Entity(False, (1, 1)), None],
... [None, Entity(True, (2, 1)), None]])
>>> wt.get_surrounding_prey(
... Entity(False, (1, 1))) # doctest: +NORMALIZE_WHITESPACE
[Entity(prey=True, coords=(0, 1), remaining_reproduction_time=5),
Entity(prey=True, coords=(2, 1), remaining_reproduction_time=5)]
>>> wt.set_planet([[Entity(False, (0, 0))]])
>>> wt.get_surrounding_prey(Entity(False, (0, 0)))
[]
>>> wt.set_planet([
... [Entity(True, (0, 0)), Entity(False, (1, 0)), Entity(False, (2, 0))],
... [None, Entity(False, (1, 1)), Entity(True, (2, 1))],
... [None, None, None]])
>>> wt.get_surrounding_prey(Entity(False, (1, 0)))
[Entity(prey=True, coords=(0, 0), remaining_reproduction_time=5)]
"""
row, col = entity.coords
adjacent: list[tuple[int, int]] = [
(row - 1, col), # North
(row + 1, col), # South
(row, col - 1), # West
(row, col + 1), # East
]
return [
ent
for r, c in adjacent
if 0 <= r < self.height
and 0 <= c < self.width
and (ent := self.planet[r][c]) is not None
and ent.prey
]
def move_and_reproduce(
self, entity: Entity, direction_orders: list[Literal["N", "E", "S", "W"]]
) -> None:
"""
Attempts to move to an unoccupied neighbouring square
in either of the four directions (North, South, East, West).
If the move was successful and the remaining_reproduction time is
equal to 0, then a new prey or predator can also be created
in the previous square.
:param direction_orders: Ordered list (like priority queue) depicting
order to attempt to move. Removes any systematic
approach of checking neighbouring squares.
>>> planet = [
... [None, None, None],
... [None, Entity(True, coords=(1, 1)), None],
... [None, None, None]
... ]
>>> wt = WaTor(WIDTH, HEIGHT)
>>> wt.set_planet(planet)
>>> wt.move_and_reproduce(Entity(True, coords=(1, 1)), direction_orders=["N"])
>>> wt.planet # doctest: +NORMALIZE_WHITESPACE
[[None, Entity(prey=True, coords=(0, 1), remaining_reproduction_time=4), None],
[None, None, None],
[None, None, None]]
>>> wt.planet[0][0] = Entity(True, coords=(0, 0))
>>> wt.move_and_reproduce(Entity(True, coords=(0, 1)),
... direction_orders=["N", "W", "E", "S"])
>>> wt.planet # doctest: +NORMALIZE_WHITESPACE
[[Entity(prey=True, coords=(0, 0), remaining_reproduction_time=5), None,
Entity(prey=True, coords=(0, 2), remaining_reproduction_time=4)],
[None, None, None],
[None, None, None]]
>>> wt.planet[0][1] = wt.planet[0][2]
>>> wt.planet[0][2] = None
>>> wt.move_and_reproduce(Entity(True, coords=(0, 1)),
... direction_orders=["N", "W", "S", "E"])
>>> wt.planet # doctest: +NORMALIZE_WHITESPACE
[[Entity(prey=True, coords=(0, 0), remaining_reproduction_time=5), None, None],
[None, Entity(prey=True, coords=(1, 1), remaining_reproduction_time=4), None],
[None, None, None]]
>>> wt = WaTor(WIDTH, HEIGHT)
>>> reproducable_entity = Entity(False, coords=(0, 1))
>>> reproducable_entity.remaining_reproduction_time = 0
>>> wt.planet = [[None, reproducable_entity]]
>>> wt.move_and_reproduce(reproducable_entity,
... direction_orders=["N", "W", "S", "E"])
>>> wt.planet # doctest: +NORMALIZE_WHITESPACE
[[Entity(prey=False, coords=(0, 0),
remaining_reproduction_time=20, energy_value=15),
Entity(prey=False, coords=(0, 1), remaining_reproduction_time=20,
energy_value=15)]]
"""
row, col = coords = entity.coords
adjacent_squares: dict[Literal["N", "E", "S", "W"], tuple[int, int]] = {
"N": (row - 1, col), # North
"S": (row + 1, col), # South
"W": (row, col - 1), # West
"E": (row, col + 1), # East
}
# Weight adjacent locations
adjacent: list[tuple[int, int]] = []
for order in direction_orders:
adjacent.append(adjacent_squares[order])
for r, c in adjacent:
if (
0 <= r < self.height
and 0 <= c < self.width
and self.planet[r][c] is None
):
# Move entity to empty adjacent square
self.planet[r][c] = entity
self.planet[row][col] = None
entity.coords = (r, c)
break
# (2.) See if it possible to reproduce in previous square
if coords != entity.coords and entity.remaining_reproduction_time <= 0:
# Check if the entities on the planet is less than the max limit
if len(self.get_entities()) < MAX_ENTITIES:
# Reproduce in previous square
self.planet[row][col] = Entity(prey=entity.prey, coords=coords)
entity.reset_reproduction_time()
else:
entity.remaining_reproduction_time -= 1
def perform_prey_actions(
self, entity: Entity, direction_orders: list[Literal["N", "E", "S", "W"]]
) -> None:
"""
Performs the actions for a prey entity
For prey the rules are:
1. At each chronon, a prey moves randomly to one of the adjacent unoccupied
squares. If there are no free squares, no movement takes place.
2. Once a prey has survived a certain number of chronons it may reproduce.
This is done as it moves to a neighbouring square,
leaving behind a new prey in its old position.
Its reproduction time is also reset to zero.
>>> wt = WaTor(WIDTH, HEIGHT)
>>> reproducable_entity = Entity(True, coords=(0, 1))
>>> reproducable_entity.remaining_reproduction_time = 0
>>> wt.planet = [[None, reproducable_entity]]
>>> wt.perform_prey_actions(reproducable_entity,
... direction_orders=["N", "W", "S", "E"])
>>> wt.planet # doctest: +NORMALIZE_WHITESPACE
[[Entity(prey=True, coords=(0, 0), remaining_reproduction_time=5),
Entity(prey=True, coords=(0, 1), remaining_reproduction_time=5)]]
"""
self.move_and_reproduce(entity, direction_orders)
def perform_predator_actions(
self,
entity: Entity,
occupied_by_prey_coords: tuple[int, int] | None,
direction_orders: list[Literal["N", "E", "S", "W"]],
) -> None:
"""
Performs the actions for a predator entity
:param occupied_by_prey_coords: Move to this location if there is prey there
For predators the rules are:
1. At each chronon, a predator moves randomly to an adjacent square occupied
by a prey. If there is none, the predator moves to a random adjacent
unoccupied square. If there are no free squares, no movement takes place.
2. At each chronon, each predator is deprived of a unit of energy.
3. Upon reaching zero energy, a predator dies.
4. If a predator moves to a square occupied by a prey,
it eats the prey and earns a certain amount of energy.
5. Once a predator has survived a certain number of chronons
it may reproduce in exactly the same way as the prey.
>>> wt = WaTor(WIDTH, HEIGHT)
>>> wt.set_planet([[Entity(True, coords=(0, 0)), Entity(False, coords=(0, 1))]])
>>> wt.perform_predator_actions(Entity(False, coords=(0, 1)), (0, 0), [])
>>> wt.planet # doctest: +NORMALIZE_WHITESPACE
[[Entity(prey=False, coords=(0, 0),
remaining_reproduction_time=20, energy_value=19), None]]
"""
assert entity.energy_value is not None # [type checking]
# (3.) If the entity has 0 energy, it will die
if entity.energy_value == 0:
self.planet[entity.coords[0]][entity.coords[1]] = None
return
# (1.) Move to entity if possible
if occupied_by_prey_coords is not None:
# Kill the prey
prey = self.planet[occupied_by_prey_coords[0]][occupied_by_prey_coords[1]]
assert prey is not None
prey.alive = False
# Move onto prey
self.planet[occupied_by_prey_coords[0]][occupied_by_prey_coords[1]] = entity
self.planet[entity.coords[0]][entity.coords[1]] = None
entity.coords = occupied_by_prey_coords
# (4.) Eats the prey and earns energy
entity.energy_value += PREDATOR_FOOD_VALUE
else:
# (5.) If it has survived the certain number of chronons it will also
# reproduce in this function
self.move_and_reproduce(entity, direction_orders)
# (2.) Each chronon, the predator is deprived of a unit of energy
entity.energy_value -= 1
def run(self, *, iteration_count: int) -> None:
"""
Emulate time passing by looping iteration_count times
>>> wt = WaTor(WIDTH, HEIGHT)
>>> wt.run(iteration_count=PREDATOR_INITIAL_ENERGY_VALUE - 1)
>>> len(list(filter(lambda entity: entity.prey is False,
... wt.get_entities()))) >= PREDATOR_INITIAL_COUNT
True
"""
for iter_num in range(iteration_count):
# Generate list of all entities in order to randomly
# pop an entity at a time to simulate true randomness
# This removes the systematic approach of iterating
# through each entity width by height
all_entities = self.get_entities()
for __ in range(len(all_entities)):
entity = all_entities.pop(randint(0, len(all_entities) - 1))
if entity.alive is False:
continue
directions: list[Literal["N", "E", "S", "W"]] = ["N", "E", "S", "W"]
shuffle(directions) # Randomly shuffle directions
if entity.prey:
self.perform_prey_actions(entity, directions)
else:
# Create list of surrounding prey
surrounding_prey = self.get_surrounding_prey(entity)
surrounding_prey_coords = None
if surrounding_prey:
# Again, randomly shuffle directions
shuffle(surrounding_prey)
surrounding_prey_coords = surrounding_prey[0].coords
self.perform_predator_actions(
entity, surrounding_prey_coords, directions
)
# Balance out the predators and prey
self.balance_predators_and_prey()
if self.time_passed is not None:
# Call time_passed function for Wa-Tor planet
# visualisation in a terminal or a graph.
self.time_passed(self, iter_num)
def visualise(wt: WaTor, iter_number: int, *, colour: bool = True) -> None:
"""
Visually displays the Wa-Tor planet using
an ascii code in terminal to clear and re-print
the Wa-Tor planet at intervals.
Uses ascii colour codes to colourfully display
the predators and prey.
(0x60f197) Prey = #
(0xfffff) Predator = x
>>> wt = WaTor(30, 30)
>>> wt.set_planet([
... [Entity(True, coords=(0, 0)), Entity(False, coords=(0, 1)), None],
... [Entity(False, coords=(1, 0)), None, Entity(False, coords=(1, 2))],
... [None, Entity(True, coords=(2, 1)), None]
... ])
>>> visualise(wt, 0, colour=False) # doctest: +NORMALIZE_WHITESPACE
# x .
x . x
. # .
<BLANKLINE>
Iteration: 0 | Prey count: 2 | Predator count: 3 |
"""
if colour:
__import__("os").system("")
print("\x1b[0;0H\x1b[2J\x1b[?25l")
reprint = "\x1b[0;0H" if colour else ""
ansi_colour_end = "\x1b[0m " if colour else " "
planet = wt.planet
output = ""
# Iterate over every entity in the planet
for row in planet:
for entity in row:
if entity is None:
output += " . "
else:
if colour is True:
output += (
"\x1b[38;2;96;241;151m"
if entity.prey
else "\x1b[38;2;255;255;15m"
)
output += f" {'#' if entity.prey else 'x'}{ansi_colour_end}"
output += "\n"
entities = wt.get_entities()
prey_count = sum(entity.prey for entity in entities)
print(
f"{output}\n Iteration: {iter_number} | Prey count: {prey_count} | "
f"Predator count: {len(entities) - prey_count} | {reprint}"
)
# Block the thread to be able to visualise seeing the algorithm
sleep(0.05)
if __name__ == "__main__":
import doctest
doctest.testmod()
wt = WaTor(WIDTH, HEIGHT)
wt.time_passed = visualise
wt.run(iteration_count=100_000)

View File

@ -2,7 +2,8 @@ import os
import random
import sys
from . import cryptomath_module, rabin_miller
from . import cryptomath_module as cryptoMath # noqa: N812
from . import rabin_miller as rabinMiller # noqa: N812
def main() -> None:
@ -12,26 +13,20 @@ def main() -> None:
def generate_key(key_size: int) -> tuple[tuple[int, int], tuple[int, int]]:
"""
>>> random.seed(0) # for repeatability
>>> public_key, private_key = generate_key(8)
>>> public_key
(26569, 239)
>>> private_key
(26569, 2855)
"""
p = rabin_miller.generate_large_prime(key_size)
q = rabin_miller.generate_large_prime(key_size)
print("Generating prime p...")
p = rabinMiller.generate_large_prime(key_size)
print("Generating prime q...")
q = rabinMiller.generate_large_prime(key_size)
n = p * q
# Generate e that is relatively prime to (p - 1) * (q - 1)
print("Generating e that is relatively prime to (p - 1) * (q - 1)...")
while True:
e = random.randrange(2 ** (key_size - 1), 2 ** (key_size))
if cryptomath_module.gcd(e, (p - 1) * (q - 1)) == 1:
if cryptoMath.gcd(e, (p - 1) * (q - 1)) == 1:
break
# Calculate d that is mod inverse of e
d = cryptomath_module.find_mod_inverse(e, (p - 1) * (q - 1))
print("Calculating d that is mod inverse of e...")
d = cryptoMath.find_mod_inverse(e, (p - 1) * (q - 1))
public_key = (n, e)
private_key = (n, d)

View File

@ -32,13 +32,13 @@ def main() -> None:
letter_code = random_chars(32)
file_name = paths[index].split(os.sep)[-1].rsplit(".", 1)[0]
file_root = f"{OUTPUT_DIR}/{file_name}_FLIP_{letter_code}"
cv2.imwrite(f"{file_root}.jpg", image, [cv2.IMWRITE_JPEG_QUALITY, 85])
cv2.imwrite(f"/{file_root}.jpg", image, [cv2.IMWRITE_JPEG_QUALITY, 85])
print(f"Success {index+1}/{len(new_images)} with {file_name}")
annos_list = []
for anno in new_annos[index]:
obj = f"{anno[0]} {anno[1]} {anno[2]} {anno[3]} {anno[4]}"
annos_list.append(obj)
with open(f"{file_root}.txt", "w") as outfile:
with open(f"/{file_root}.txt", "w") as outfile:
outfile.write("\n".join(line for line in annos_list))

View File

@ -1,4 +1,13 @@
def octal_to_binary(octal_number: str) -> str:
def octal_to_binary(octal_number):
"""
Convert an octal number to binary.
Args:
octal_number (str): The octal number as a string.
Returns:
str: The binary representation of the octal number.
"""
binary_number = ""
octal_digits = "01234567"

View File

@ -1,62 +1,5 @@
r"""
"""
A binary search Tree
Example
8
/ \
3 10
/ \ \
1 6 14
/ \ /
4 7 13
>>> t = BinarySearchTree()
>>> t.insert(8, 3, 6, 1, 10, 14, 13, 4, 7)
>>> print(" ".join(repr(i.value) for i in t.traversal_tree()))
8 3 1 6 4 7 10 14 13
>>> print(" ".join(repr(i.value) for i in t.traversal_tree(postorder)))
1 4 7 6 3 13 14 10 8
>>> t.remove(20)
Traceback (most recent call last):
...
ValueError: Value 20 not found
>>> BinarySearchTree().search(6)
Traceback (most recent call last):
...
IndexError: Warning: Tree is empty! please use another.
Other example:
>>> testlist = (8, 3, 6, 1, 10, 14, 13, 4, 7)
>>> t = BinarySearchTree()
>>> for i in testlist:
... t.insert(i)
Prints all the elements of the list in order traversal
>>> print(t)
{'8': ({'3': (1, {'6': (4, 7)})}, {'10': (None, {'14': (13, None)})})}
Test existence
>>> t.search(6) is not None
True
>>> t.search(-1) is not None
False
>>> t.search(6).is_right
True
>>> t.search(1).is_right
False
>>> t.get_max().value
14
>>> t.get_min().value
1
>>> t.empty()
False
>>> for i in testlist:
... t.remove(i)
>>> t.empty()
True
"""
from collections.abc import Iterable
@ -77,10 +20,6 @@ class Node:
return str(self.value)
return pformat({f"{self.value}": (self.left, self.right)}, indent=1)
@property
def is_right(self) -> bool:
return self.parent is not None and self is self.parent.right
class BinarySearchTree:
def __init__(self, root: Node | None = None):
@ -96,13 +35,18 @@ class BinarySearchTree:
if new_children is not None: # reset its kids
new_children.parent = node.parent
if node.parent is not None: # reset its parent
if node.is_right: # If it is the right child
if self.is_right(node): # If it is the right children
node.parent.right = new_children
else:
node.parent.left = new_children
else:
self.root = new_children
def is_right(self, node: Node) -> bool:
if node.parent and node.parent.right:
return node == node.parent.right
return False
def empty(self) -> bool:
return self.root is None
@ -175,12 +119,8 @@ class BinarySearchTree:
return node
def remove(self, value: int) -> None:
# Look for the node with that label
node = self.search(value)
if node is None:
msg = f"Value {value} not found"
raise ValueError(msg)
node = self.search(value) # Look for the node with that label
if node is not None:
if node.left is None and node.right is None: # If it has no children
self.__reassign_nodes(node, None)
elif node.left is None: # Has only right children
@ -188,12 +128,12 @@ class BinarySearchTree:
elif node.right is None: # Has only left children
self.__reassign_nodes(node, node.left)
else:
predecessor = self.get_max(
tmp_node = self.get_max(
node.left
) # Gets the max value of the left branch
self.remove(predecessor.value) # type: ignore
self.remove(tmp_node.value) # type: ignore
node.value = (
predecessor.value # type: ignore
tmp_node.value # type: ignore
) # Assigns the value to the node to delete and keep tree structure
def preorder_traverse(self, node: Node | None) -> Iterable:
@ -237,6 +177,55 @@ def postorder(curr_node: Node | None) -> list[Node]:
return node_list
def binary_search_tree() -> None:
r"""
Example
8
/ \
3 10
/ \ \
1 6 14
/ \ /
4 7 13
>>> t = BinarySearchTree()
>>> t.insert(8, 3, 6, 1, 10, 14, 13, 4, 7)
>>> print(" ".join(repr(i.value) for i in t.traversal_tree()))
8 3 1 6 4 7 10 14 13
>>> print(" ".join(repr(i.value) for i in t.traversal_tree(postorder)))
1 4 7 6 3 13 14 10 8
>>> BinarySearchTree().search(6)
Traceback (most recent call last):
...
IndexError: Warning: Tree is empty! please use another.
"""
testlist = (8, 3, 6, 1, 10, 14, 13, 4, 7)
t = BinarySearchTree()
for i in testlist:
t.insert(i)
# Prints all the elements of the list in order traversal
print(t)
if t.search(6) is not None:
print("The value 6 exists")
else:
print("The value 6 doesn't exist")
if t.search(-1) is not None:
print("The value -1 exists")
else:
print("The value -1 doesn't exist")
if not t.empty():
print("Max Value: ", t.get_max().value) # type: ignore
print("Min Value: ", t.get_min().value) # type: ignore
for i in testlist:
t.remove(i)
print(t)
if __name__ == "__main__":
import doctest

View File

@ -21,7 +21,6 @@ def rgb2gray(rgb: np.array) -> np.array:
def gray2binary(gray: np.array) -> np.array:
"""
Return binary image from gray image
>>> gray2binary(np.array([[127, 255, 0]]))
array([[False, True, False]])
>>> gray2binary(np.array([[0]]))

View File

@ -10,12 +10,12 @@ def get_rotation(
) -> np.ndarray:
"""
Get image rotation
:param img: np.ndarray
:param img: np.array
:param pt1: 3x2 list
:param pt2: 3x2 list
:param rows: columns image shape
:param cols: rows image shape
:return: np.ndarray
:return: np.array
"""
matrix = cv2.getAffineTransform(pt1, pt2)
return cv2.warpAffine(img, matrix, (rows, cols))

View File

@ -1,97 +0,0 @@
"""
Regex matching check if a text matches pattern or not.
Pattern:
'.' Matches any single character.
'*' Matches zero or more of the preceding element.
More info:
https://medium.com/trick-the-interviwer/regular-expression-matching-9972eb74c03
"""
def recursive_match(text: str, pattern: str) -> bool:
"""
Recursive matching algorithm.
Time complexity: O(2 ^ (|text| + |pattern|))
Space complexity: Recursion depth is O(|text| + |pattern|).
:param text: Text to match.
:param pattern: Pattern to match.
:return: True if text matches pattern, False otherwise.
>>> recursive_match('abc', 'a.c')
True
>>> recursive_match('abc', 'af*.c')
True
>>> recursive_match('abc', 'a.c*')
True
>>> recursive_match('abc', 'a.c*d')
False
>>> recursive_match('aa', '.*')
True
"""
if not pattern:
return not text
if not text:
return pattern[-1] == "*" and recursive_match(text, pattern[:-2])
if text[-1] == pattern[-1] or pattern[-1] == ".":
return recursive_match(text[:-1], pattern[:-1])
if pattern[-1] == "*":
return recursive_match(text[:-1], pattern) or recursive_match(
text, pattern[:-2]
)
return False
def dp_match(text: str, pattern: str) -> bool:
"""
Dynamic programming matching algorithm.
Time complexity: O(|text| * |pattern|)
Space complexity: O(|text| * |pattern|)
:param text: Text to match.
:param pattern: Pattern to match.
:return: True if text matches pattern, False otherwise.
>>> dp_match('abc', 'a.c')
True
>>> dp_match('abc', 'af*.c')
True
>>> dp_match('abc', 'a.c*')
True
>>> dp_match('abc', 'a.c*d')
False
>>> dp_match('aa', '.*')
True
"""
m = len(text)
n = len(pattern)
dp = [[False for _ in range(n + 1)] for _ in range(m + 1)]
dp[0][0] = True
for j in range(1, n + 1):
dp[0][j] = pattern[j - 1] == "*" and dp[0][j - 2]
for i in range(1, m + 1):
for j in range(1, n + 1):
if pattern[j - 1] in {".", text[i - 1]}:
dp[i][j] = dp[i - 1][j - 1]
elif pattern[j - 1] == "*":
dp[i][j] = dp[i][j - 2]
if pattern[j - 2] in {".", text[i - 1]}:
dp[i][j] |= dp[i - 1][j]
else:
dp[i][j] = False
return dp[m][n]
if __name__ == "__main__":
import doctest
doctest.testmod()

View File

@ -1,24 +0,0 @@
# Tribonacci sequence using Dynamic Programming
def tribonacci(num: int) -> list[int]:
"""
Given a number, return first n Tribonacci Numbers.
>>> tribonacci(5)
[0, 0, 1, 1, 2]
>>> tribonacci(8)
[0, 0, 1, 1, 2, 4, 7, 13]
"""
dp = [0] * num
dp[2] = 1
for i in range(3, num):
dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3]
return dp
if __name__ == "__main__":
import doctest
doctest.testmod()

View File

@ -6,32 +6,14 @@ from __future__ import annotations
Path = list[tuple[int, int]]
# 0's are free path whereas 1's are obstacles
TEST_GRIDS = [
[
grid = [
[0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0], # 0 are free path whereas 1's are obstacles
[0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 0],
[1, 0, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0],
],
[
[0, 0, 0, 1, 1, 0, 0],
[0, 0, 0, 0, 1, 0, 1],
[0, 0, 0, 1, 1, 0, 0],
[0, 1, 0, 0, 1, 0, 0],
[1, 0, 0, 1, 1, 0, 1],
[0, 0, 0, 0, 0, 0, 0],
],
[
[0, 0, 1, 0, 0],
[0, 1, 0, 0, 0],
[0, 0, 1, 0, 1],
[1, 0, 0, 1, 1],
[0, 0, 0, 0, 0],
],
]
delta = ([-1, 0], [0, -1], [1, 0], [0, 1]) # up, left, down, right
@ -83,14 +65,10 @@ class Node:
def __lt__(self, other) -> bool:
return self.f_cost < other.f_cost
def __eq__(self, other) -> bool:
return self.pos == other.pos
class GreedyBestFirst:
"""
>>> grid = TEST_GRIDS[2]
>>> gbf = GreedyBestFirst(grid, (0, 0), (len(grid) - 1, len(grid[0]) - 1))
>>> gbf = GreedyBestFirst((0, 0), (len(grid) - 1, len(grid[0]) - 1))
>>> [x.pos for x in gbf.get_successors(gbf.start)]
[(1, 0), (0, 1)]
>>> (gbf.start.pos_y + delta[3][0], gbf.start.pos_x + delta[3][1])
@ -100,14 +78,11 @@ class GreedyBestFirst:
>>> gbf.retrace_path(gbf.start)
[(0, 0)]
>>> gbf.search() # doctest: +NORMALIZE_WHITESPACE
[(0, 0), (1, 0), (2, 0), (2, 1), (3, 1), (4, 1), (4, 2), (4, 3),
(4, 4)]
[(0, 0), (1, 0), (2, 0), (3, 0), (3, 1), (4, 1), (5, 1), (6, 1),
(6, 2), (6, 3), (5, 3), (5, 4), (5, 5), (6, 5), (6, 6)]
"""
def __init__(
self, grid: list[list[int]], start: tuple[int, int], goal: tuple[int, int]
):
self.grid = grid
def __init__(self, start: tuple[int, int], goal: tuple[int, int]):
self.start = Node(start[1], start[0], goal[1], goal[0], 0, None)
self.target = Node(goal[1], goal[0], goal[1], goal[0], 99999, None)
@ -139,6 +114,14 @@ class GreedyBestFirst:
if child_node not in self.open_nodes:
self.open_nodes.append(child_node)
else:
# retrieve the best current path
better_node = self.open_nodes.pop(self.open_nodes.index(child_node))
if child_node.g_cost < better_node.g_cost:
self.open_nodes.append(child_node)
else:
self.open_nodes.append(better_node)
if not self.reached:
return [self.start.pos]
@ -148,22 +131,28 @@ class GreedyBestFirst:
"""
Returns a list of successors (both in the grid and free spaces)
"""
return [
successors = []
for action in delta:
pos_x = parent.pos_x + action[1]
pos_y = parent.pos_y + action[0]
if not (0 <= pos_x <= len(grid[0]) - 1 and 0 <= pos_y <= len(grid) - 1):
continue
if grid[pos_y][pos_x] != 0:
continue
successors.append(
Node(
pos_x,
pos_y,
self.target.pos_x,
self.target.pos_y,
self.target.pos_x,
parent.g_cost + 1,
parent,
)
for action in delta
if (
0 <= (pos_x := parent.pos_x + action[1]) < len(self.grid[0])
and 0 <= (pos_y := parent.pos_y + action[0]) < len(self.grid)
and self.grid[pos_y][pos_x] == 0
)
]
return successors
def retrace_path(self, node: Node | None) -> Path:
"""
@ -179,9 +168,6 @@ class GreedyBestFirst:
if __name__ == "__main__":
for idx, grid in enumerate(TEST_GRIDS):
print(f"==grid-{idx + 1}==")
init = (0, 0)
goal = (len(grid) - 1, len(grid[0]) - 1)
for elem in grid:
@ -189,7 +175,7 @@ if __name__ == "__main__":
print("------")
greedy_bf = GreedyBestFirst(grid, init, goal)
greedy_bf = GreedyBestFirst(init, goal)
path = greedy_bf.search()
if path:
for pos_x, pos_y in path:

View File

@ -43,43 +43,62 @@ def points_to_polynomial(coordinates: list[list[int]]) -> str:
x = len(coordinates)
count_of_line = 0
matrix: list[list[float]] = []
# put the x and x to the power values in a matrix
matrix: list[list[float]] = [
[
coordinates[count_of_line][0] ** (x - (count_in_line + 1))
for count_in_line in range(x)
]
for count_of_line in range(x)
]
while count_of_line < x:
count_in_line = 0
a = coordinates[count_of_line][0]
count_line: list[float] = []
while count_in_line < x:
count_line.append(a ** (x - (count_in_line + 1)))
count_in_line += 1
matrix.append(count_line)
count_of_line += 1
count_of_line = 0
# put the y values into a vector
vector: list[float] = [coordinates[count_of_line][1] for count_of_line in range(x)]
vector: list[float] = []
while count_of_line < x:
vector.append(coordinates[count_of_line][1])
count_of_line += 1
for count in range(x):
for number in range(x):
if count == number:
continue
fraction = matrix[number][count] / matrix[count][count]
count = 0
while count < x:
zahlen = 0
while zahlen < x:
if count == zahlen:
zahlen += 1
if zahlen == x:
break
bruch = matrix[zahlen][count] / matrix[count][count]
for counting_columns, item in enumerate(matrix[count]):
# manipulating all the values in the matrix
matrix[number][counting_columns] -= item * fraction
matrix[zahlen][counting_columns] -= item * bruch
# manipulating the values in the vector
vector[number] -= vector[count] * fraction
vector[zahlen] -= vector[count] * bruch
zahlen += 1
count += 1
count = 0
# make solutions
solution: list[str] = [
str(vector[count] / matrix[count][count]) for count in range(x)
]
solution: list[str] = []
while count < x:
solution.append(str(vector[count] / matrix[count][count]))
count += 1
count = 0
solved = "f(x)="
for count in range(x):
while count < x:
remove_e: list[str] = solution[count].split("E")
if len(remove_e) > 1:
solution[count] = f"{remove_e[0]}*10^{remove_e[1]}"
solved += f"x^{x - (count + 1)}*{solution[count]}"
if count + 1 != x:
solved += "+"
count += 1
return solved

View File

@ -1,4 +1,4 @@
total_users,total_events,days
total_user,total_events,days
18231,0.0,1
22621,1.0,2
15675,0.0,3

1 total_users total_user total_events days
2 18231 0.0 1
3 22621 1.0 2
4 15675 0.0 3

View File

@ -1,6 +1,6 @@
"""
this is code for forecasting
but I modified it and used it for safety checker of data
but i modified it and used it for safety checker of data
for ex: you have an online shop and for some reason some data are
missing (the amount of data that u expected are not supposed to be)
then we can use it
@ -11,8 +11,6 @@ missing (the amount of data that u expected are not supposed to be)
u can just adjust it for ur own purpose
"""
from warnings import simplefilter
import numpy as np
import pandas as pd
from sklearn.preprocessing import Normalizer
@ -47,10 +45,8 @@ def sarimax_predictor(train_user: list, train_match: list, test_match: list) ->
>>> sarimax_predictor([4,2,6,8], [3,1,2,4], [2])
6.6666671111109626
"""
# Suppress the User Warning raised by SARIMAX due to insufficient observations
simplefilter("ignore", UserWarning)
order = (1, 2, 1)
seasonal_order = (1, 1, 1, 7)
seasonal_order = (1, 1, 0, 7)
model = SARIMAX(
train_user, exog=train_match, order=order, seasonal_order=seasonal_order
)
@ -106,10 +102,6 @@ def data_safety_checker(list_vote: list, actual_result: float) -> bool:
"""
safe = 0
not_safe = 0
if not isinstance(actual_result, float):
raise TypeError("Actual result should be float. Value passed is a list")
for i in list_vote:
if i > actual_result:
safe = not_safe + 1
@ -122,11 +114,16 @@ def data_safety_checker(list_vote: list, actual_result: float) -> bool:
if __name__ == "__main__":
# data_input_df = pd.read_csv("ex_data.csv", header=None)
data_input = [[18231, 0.0, 1], [22621, 1.0, 2], [15675, 0.0, 3], [23583, 1.0, 4]]
data_input_df = pd.DataFrame(
data_input, columns=["total_user", "total_even", "days"]
)
"""
data column = total user in a day, how much online event held in one day,
what day is that(sunday-saturday)
"""
data_input_df = pd.read_csv("ex_data.csv")
# start normalization
normalize_df = Normalizer().fit_transform(data_input_df.values)
@ -141,23 +138,23 @@ if __name__ == "__main__":
x_test = x[len(x) - 1 :]
# for linear regression & sarimax
train_date = total_date[: len(total_date) - 1]
train_user = total_user[: len(total_user) - 1]
train_match = total_match[: len(total_match) - 1]
trn_date = total_date[: len(total_date) - 1]
trn_user = total_user[: len(total_user) - 1]
trn_match = total_match[: len(total_match) - 1]
test_date = total_date[len(total_date) - 1 :]
test_user = total_user[len(total_user) - 1 :]
test_match = total_match[len(total_match) - 1 :]
tst_date = total_date[len(total_date) - 1 :]
tst_user = total_user[len(total_user) - 1 :]
tst_match = total_match[len(total_match) - 1 :]
# voting system with forecasting
res_vote = [
linear_regression_prediction(
train_date, train_user, train_match, test_date, test_match
trn_date, trn_user, trn_match, tst_date, tst_match
),
sarimax_predictor(train_user, train_match, test_match),
support_vector_regressor(x_train, x_test, train_user),
sarimax_predictor(trn_user, trn_match, tst_match),
support_vector_regressor(x_train, x_test, trn_user),
]
# check the safety of today's data
not_str = "" if data_safety_checker(res_vote, test_user[0]) else "not "
print(f"Today's data is {not_str}safe.")
not_str = "" if data_safety_checker(res_vote, tst_user) else "not "
print("Today's data is {not_str}safe.")

View File

@ -19,9 +19,7 @@ def median(nums: list) -> int | float:
Returns:
Median.
"""
# The sorted function returns list[SupportsRichComparisonT@sorted]
# which does not support `+`
sorted_list: list[int] = sorted(nums)
sorted_list = sorted(nums)
length = len(sorted_list)
mid_index = length >> 1
return (

View File

@ -1,51 +0,0 @@
"""
Finding the continuous fraction for a rational number using python
https://en.wikipedia.org/wiki/Continued_fraction
"""
from fractions import Fraction
def continued_fraction(num: Fraction) -> list[int]:
"""
:param num:
Fraction of the number whose continued fractions to be found.
Use Fraction(str(number)) for more accurate results due to
float inaccuracies.
:return:
The continued fraction of rational number.
It is the all commas in the (n + 1)-tuple notation.
>>> continued_fraction(Fraction(2))
[2]
>>> continued_fraction(Fraction("3.245"))
[3, 4, 12, 4]
>>> continued_fraction(Fraction("2.25"))
[2, 4]
>>> continued_fraction(1/Fraction("2.25"))
[0, 2, 4]
>>> continued_fraction(Fraction("415/93"))
[4, 2, 6, 7]
"""
numerator, denominator = num.as_integer_ratio()
continued_fraction_list: list[int] = []
while True:
integer_part = int(numerator / denominator)
continued_fraction_list.append(integer_part)
numerator -= integer_part * denominator
if numerator == 0:
break
numerator, denominator = denominator, numerator
return continued_fraction_list
if __name__ == "__main__":
import doctest
doctest.testmod()
print("Continued Fraction of 0.84375 is: ", continued_fraction(Fraction("0.84375")))

View File

@ -5,7 +5,7 @@ import numpy as np
def euler_modified(
ode_func: Callable, y0: float, x0: float, step_size: float, x_end: float
) -> np.ndarray:
) -> np.array:
"""
Calculate solution at each step to an ODE using Euler's Modified Method
The Euler Method is straightforward to implement, but can't give accurate solutions.

View File

@ -1,23 +1,23 @@
from __future__ import annotations
def find_max_iterative(nums: list[int | float]) -> int | float:
def find_max(nums: list[int | float]) -> int | float:
"""
>>> for nums in ([3, 2, 1], [-3, -2, -1], [3, -3, 0], [3.0, 3.1, 2.9]):
... find_max_iterative(nums) == max(nums)
... find_max(nums) == max(nums)
True
True
True
True
>>> find_max_iterative([2, 4, 9, 7, 19, 94, 5])
>>> find_max([2, 4, 9, 7, 19, 94, 5])
94
>>> find_max_iterative([])
>>> find_max([])
Traceback (most recent call last):
...
ValueError: find_max_iterative() arg is an empty sequence
ValueError: find_max() arg is an empty sequence
"""
if len(nums) == 0:
raise ValueError("find_max_iterative() arg is an empty sequence")
raise ValueError("find_max() arg is an empty sequence")
max_num = nums[0]
for x in nums:
if x > max_num:
@ -25,59 +25,6 @@ def find_max_iterative(nums: list[int | float]) -> int | float:
return max_num
# Divide and Conquer algorithm
def find_max_recursive(nums: list[int | float], left: int, right: int) -> int | float:
"""
find max value in list
:param nums: contains elements
:param left: index of first element
:param right: index of last element
:return: max in nums
>>> for nums in ([3, 2, 1], [-3, -2, -1], [3, -3, 0], [3.0, 3.1, 2.9]):
... find_max_recursive(nums, 0, len(nums) - 1) == max(nums)
True
True
True
True
>>> nums = [1, 3, 5, 7, 9, 2, 4, 6, 8, 10]
>>> find_max_recursive(nums, 0, len(nums) - 1) == max(nums)
True
>>> find_max_recursive([], 0, 0)
Traceback (most recent call last):
...
ValueError: find_max_recursive() arg is an empty sequence
>>> find_max_recursive(nums, 0, len(nums)) == max(nums)
Traceback (most recent call last):
...
IndexError: list index out of range
>>> find_max_recursive(nums, -len(nums), -1) == max(nums)
True
>>> find_max_recursive(nums, -len(nums) - 1, -1) == max(nums)
Traceback (most recent call last):
...
IndexError: list index out of range
"""
if len(nums) == 0:
raise ValueError("find_max_recursive() arg is an empty sequence")
if (
left >= len(nums)
or left < -len(nums)
or right >= len(nums)
or right < -len(nums)
):
raise IndexError("list index out of range")
if left == right:
return nums[left]
mid = (left + right) >> 1 # the middle
left_max = find_max_recursive(nums, left, mid) # find max in range[left, mid]
right_max = find_max_recursive(
nums, mid + 1, right
) # find max in range[mid + 1, right]
return left_max if left_max >= right_max else right_max
if __name__ == "__main__":
import doctest

View File

@ -0,0 +1,58 @@
from __future__ import annotations
# Divide and Conquer algorithm
def find_max(nums: list[int | float], left: int, right: int) -> int | float:
"""
find max value in list
:param nums: contains elements
:param left: index of first element
:param right: index of last element
:return: max in nums
>>> for nums in ([3, 2, 1], [-3, -2, -1], [3, -3, 0], [3.0, 3.1, 2.9]):
... find_max(nums, 0, len(nums) - 1) == max(nums)
True
True
True
True
>>> nums = [1, 3, 5, 7, 9, 2, 4, 6, 8, 10]
>>> find_max(nums, 0, len(nums) - 1) == max(nums)
True
>>> find_max([], 0, 0)
Traceback (most recent call last):
...
ValueError: find_max() arg is an empty sequence
>>> find_max(nums, 0, len(nums)) == max(nums)
Traceback (most recent call last):
...
IndexError: list index out of range
>>> find_max(nums, -len(nums), -1) == max(nums)
True
>>> find_max(nums, -len(nums) - 1, -1) == max(nums)
Traceback (most recent call last):
...
IndexError: list index out of range
"""
if len(nums) == 0:
raise ValueError("find_max() arg is an empty sequence")
if (
left >= len(nums)
or left < -len(nums)
or right >= len(nums)
or right < -len(nums)
):
raise IndexError("list index out of range")
if left == right:
return nums[left]
mid = (left + right) >> 1 # the middle
left_max = find_max(nums, left, mid) # find max in range[left, mid]
right_max = find_max(nums, mid + 1, right) # find max in range[mid + 1, right]
return left_max if left_max >= right_max else right_max
if __name__ == "__main__":
import doctest
doctest.testmod(verbose=True)

View File

@ -1,86 +1,33 @@
from __future__ import annotations
def find_min_iterative(nums: list[int | float]) -> int | float:
def find_min(nums: list[int | float]) -> int | float:
"""
Find Minimum Number in a List
:param nums: contains elements
:return: min number in list
>>> for nums in ([3, 2, 1], [-3, -2, -1], [3, -3, 0], [3.0, 3.1, 2.9]):
... find_min_iterative(nums) == min(nums)
... find_min(nums) == min(nums)
True
True
True
True
>>> find_min_iterative([0, 1, 2, 3, 4, 5, -3, 24, -56])
>>> find_min([0, 1, 2, 3, 4, 5, -3, 24, -56])
-56
>>> find_min_iterative([])
>>> find_min([])
Traceback (most recent call last):
...
ValueError: find_min_iterative() arg is an empty sequence
ValueError: find_min() arg is an empty sequence
"""
if len(nums) == 0:
raise ValueError("find_min_iterative() arg is an empty sequence")
raise ValueError("find_min() arg is an empty sequence")
min_num = nums[0]
for num in nums:
min_num = min(min_num, num)
return min_num
# Divide and Conquer algorithm
def find_min_recursive(nums: list[int | float], left: int, right: int) -> int | float:
"""
find min value in list
:param nums: contains elements
:param left: index of first element
:param right: index of last element
:return: min in nums
>>> for nums in ([3, 2, 1], [-3, -2, -1], [3, -3, 0], [3.0, 3.1, 2.9]):
... find_min_recursive(nums, 0, len(nums) - 1) == min(nums)
True
True
True
True
>>> nums = [1, 3, 5, 7, 9, 2, 4, 6, 8, 10]
>>> find_min_recursive(nums, 0, len(nums) - 1) == min(nums)
True
>>> find_min_recursive([], 0, 0)
Traceback (most recent call last):
...
ValueError: find_min_recursive() arg is an empty sequence
>>> find_min_recursive(nums, 0, len(nums)) == min(nums)
Traceback (most recent call last):
...
IndexError: list index out of range
>>> find_min_recursive(nums, -len(nums), -1) == min(nums)
True
>>> find_min_recursive(nums, -len(nums) - 1, -1) == min(nums)
Traceback (most recent call last):
...
IndexError: list index out of range
"""
if len(nums) == 0:
raise ValueError("find_min_recursive() arg is an empty sequence")
if (
left >= len(nums)
or left < -len(nums)
or right >= len(nums)
or right < -len(nums)
):
raise IndexError("list index out of range")
if left == right:
return nums[left]
mid = (left + right) >> 1 # the middle
left_min = find_min_recursive(nums, left, mid) # find min in range[left, mid]
right_min = find_min_recursive(
nums, mid + 1, right
) # find min in range[mid + 1, right]
return left_min if left_min <= right_min else right_min
if __name__ == "__main__":
import doctest

View File

@ -0,0 +1,58 @@
from __future__ import annotations
# Divide and Conquer algorithm
def find_min(nums: list[int | float], left: int, right: int) -> int | float:
"""
find min value in list
:param nums: contains elements
:param left: index of first element
:param right: index of last element
:return: min in nums
>>> for nums in ([3, 2, 1], [-3, -2, -1], [3, -3, 0], [3.0, 3.1, 2.9]):
... find_min(nums, 0, len(nums) - 1) == min(nums)
True
True
True
True
>>> nums = [1, 3, 5, 7, 9, 2, 4, 6, 8, 10]
>>> find_min(nums, 0, len(nums) - 1) == min(nums)
True
>>> find_min([], 0, 0)
Traceback (most recent call last):
...
ValueError: find_min() arg is an empty sequence
>>> find_min(nums, 0, len(nums)) == min(nums)
Traceback (most recent call last):
...
IndexError: list index out of range
>>> find_min(nums, -len(nums), -1) == min(nums)
True
>>> find_min(nums, -len(nums) - 1, -1) == min(nums)
Traceback (most recent call last):
...
IndexError: list index out of range
"""
if len(nums) == 0:
raise ValueError("find_min() arg is an empty sequence")
if (
left >= len(nums)
or left < -len(nums)
or right >= len(nums)
or right < -len(nums)
):
raise IndexError("list index out of range")
if left == right:
return nums[left]
mid = (left + right) >> 1 # the middle
left_min = find_min(nums, left, mid) # find min in range[left, mid]
right_min = find_min(nums, mid + 1, right) # find min in range[mid + 1, right]
return left_min if left_min <= right_min else right_min
if __name__ == "__main__":
import doctest
doctest.testmod(verbose=True)

View File

@ -13,7 +13,7 @@ This script is inspired by a corresponding research paper.
import numpy as np
def sigmoid(vector: np.ndarray) -> np.ndarray:
def sigmoid(vector: np.array) -> np.array:
"""
Mathematical function sigmoid takes a vector x of K real numbers as input and
returns 1/ (1 + e^-x).
@ -25,15 +25,17 @@ def sigmoid(vector: np.ndarray) -> np.ndarray:
return 1 / (1 + np.exp(-vector))
def gaussian_error_linear_unit(vector: np.ndarray) -> np.ndarray:
def gaussian_error_linear_unit(vector: np.array) -> np.array:
"""
Implements the Gaussian Error Linear Unit (GELU) function
Parameters:
vector (np.ndarray): A numpy array of shape (1, n) consisting of real values
vector (np.array): A numpy array of shape (1,n)
consisting of real values
Returns:
gelu_vec (np.ndarray): The input numpy array, after applying gelu
gelu_vec (np.array): The input numpy array, after applying
gelu.
Examples:
>>> gaussian_error_linear_unit(np.array([-1.0, 1.0, 2.0]))

View File

@ -14,11 +14,7 @@ Jaccard similarity is widely used with MinHashing.
"""
def jaccard_similarity(
set_a: set[str] | list[str] | tuple[str],
set_b: set[str] | list[str] | tuple[str],
alternative_union=False,
):
def jaccard_similarity(set_a, set_b, alternative_union=False):
"""
Finds the jaccard similarity between two sets.
Essentially, its intersection over union.
@ -41,52 +37,41 @@ def jaccard_similarity(
>>> set_b = {'c', 'd', 'e', 'f', 'h', 'i'}
>>> jaccard_similarity(set_a, set_b)
0.375
>>> jaccard_similarity(set_a, set_a)
1.0
>>> jaccard_similarity(set_a, set_a, True)
0.5
>>> set_a = ['a', 'b', 'c', 'd', 'e']
>>> set_b = ('c', 'd', 'e', 'f', 'h', 'i')
>>> jaccard_similarity(set_a, set_b)
0.375
>>> set_a = ('c', 'd', 'e', 'f', 'h', 'i')
>>> set_b = ['a', 'b', 'c', 'd', 'e']
>>> jaccard_similarity(set_a, set_b)
0.375
>>> set_a = ('c', 'd', 'e', 'f', 'h', 'i')
>>> set_b = ['a', 'b', 'c', 'd']
>>> jaccard_similarity(set_a, set_b, True)
0.2
>>> set_a = {'a', 'b'}
>>> set_b = ['c', 'd']
>>> jaccard_similarity(set_a, set_b)
Traceback (most recent call last):
...
ValueError: Set a and b must either both be sets or be either a list or a tuple.
"""
if isinstance(set_a, set) and isinstance(set_b, set):
intersection_length = len(set_a.intersection(set_b))
intersection = len(set_a.intersection(set_b))
if alternative_union:
union_length = len(set_a) + len(set_b)
union = len(set_a) + len(set_b)
else:
union_length = len(set_a.union(set_b))
union = len(set_a.union(set_b))
return intersection_length / union_length
return intersection / union
elif isinstance(set_a, (list, tuple)) and isinstance(set_b, (list, tuple)):
if isinstance(set_a, (list, tuple)) and isinstance(set_b, (list, tuple)):
intersection = [element for element in set_a if element in set_b]
if alternative_union:
return len(intersection) / (len(set_a) + len(set_b))
union = len(set_a) + len(set_b)
return len(intersection) / union
else:
# Cast set_a to list because tuples cannot be mutated
union = list(set_a) + [element for element in set_b if element not in set_a]
union = set_a + [element for element in set_b if element not in set_a]
return len(intersection) / len(union)
raise ValueError(
"Set a and b must either both be sets or be either a list or a tuple."
)
return len(intersection) / len(union)
return None
if __name__ == "__main__":

View File

@ -1,20 +1,16 @@
"""
Author: P Shreyas Shetty
Implementation of Newton-Raphson method for solving equations of kind
f(x) = 0. It is an iterative method where solution is found by the expression
Author: P Shreyas Shetty
Implementation of Newton-Raphson method for solving equations of kind
f(x) = 0. It is an iterative method where solution is found by the expression
x[n+1] = x[n] + f(x[n])/f'(x[n])
If no solution exists, then either the solution will not be found when iteration
limit is reached or the gradient f'(x[n]) approaches zero. In both cases, exception
is raised. If iteration limit is reached, try increasing maxiter.
"""
If no solution exists, then either the solution will not be found when iteration
limit is reached or the gradient f'(x[n]) approaches zero. In both cases, exception
is raised. If iteration limit is reached, try increasing maxiter.
"""
import math as m
from collections.abc import Callable
DerivativeFunc = Callable[[float], float]
def calc_derivative(f: DerivativeFunc, a: float, h: float = 0.001) -> float:
def calc_derivative(f, a, h=0.001):
"""
Calculates derivative at point a for function f using finite difference
method
@ -22,14 +18,7 @@ def calc_derivative(f: DerivativeFunc, a: float, h: float = 0.001) -> float:
return (f(a + h) - f(a - h)) / (2 * h)
def newton_raphson(
f: DerivativeFunc,
x0: float = 0,
maxiter: int = 100,
step: float = 0.0001,
maxerror: float = 1e-6,
logsteps: bool = False,
) -> tuple[float, float, list[float]]:
def newton_raphson(f, x0=0, maxiter=100, step=0.0001, maxerror=1e-6, logsteps=False):
a = x0 # set the initial guess
steps = [a]
error = abs(f(a))
@ -47,7 +36,7 @@ def newton_raphson(
if logsteps:
# If logstep is true, then log intermediate steps
return a, error, steps
return a, error, []
return a, error
if __name__ == "__main__":

View File

@ -1,7 +1,7 @@
import numpy as np
def qr_householder(a: np.ndarray):
def qr_householder(a):
"""Return a QR-decomposition of the matrix A using Householder reflection.
The QR-decomposition decomposes the matrix A of shape (m, n) into an

View File

@ -11,7 +11,7 @@ https://en.wikipedia.org/wiki/Sigmoid_function
import numpy as np
def sigmoid(vector: np.ndarray) -> np.ndarray:
def sigmoid(vector: np.array) -> np.array:
"""
Implements the sigmoid function

View File

@ -12,12 +12,12 @@ https://en.wikipedia.org/wiki/Activation_function
import numpy as np
def tangent_hyperbolic(vector: np.ndarray) -> np.ndarray:
def tangent_hyperbolic(vector: np.array) -> np.array:
"""
Implements the tanh function
Parameters:
vector: np.ndarray
vector: np.array
Returns:
tanh (np.array): The input numpy array after applying tanh.

View File

@ -1,39 +0,0 @@
"""
Leaky Rectified Linear Unit (Leaky ReLU)
Use Case: Leaky ReLU addresses the problem of the vanishing gradient.
For more detailed information, you can refer to the following link:
https://en.wikipedia.org/wiki/Rectifier_(neural_networks)#Leaky_ReLU
"""
import numpy as np
def leaky_rectified_linear_unit(vector: np.ndarray, alpha: float) -> np.ndarray:
"""
Implements the LeakyReLU activation function.
Parameters:
vector (np.ndarray): The input array for LeakyReLU activation.
alpha (float): The slope for negative values.
Returns:
np.ndarray: The input array after applying the LeakyReLU activation.
Formula: f(x) = x if x > 0 else f(x) = alpha * x
Examples:
>>> leaky_rectified_linear_unit(vector=np.array([2.3,0.6,-2,-3.8]), alpha=0.3)
array([ 2.3 , 0.6 , -0.6 , -1.14])
>>> leaky_rectified_linear_unit(np.array([-9.2, -0.3, 0.45, -4.56]), alpha=0.067)
array([-0.6164 , -0.0201 , 0.45 , -0.30552])
"""
return np.where(vector > 0, vector, alpha * vector)
if __name__ == "__main__":
import doctest
doctest.testmod()

View File

@ -53,40 +53,6 @@ def volume_of_gas_system(moles: float, kelvin: float, pressure: float) -> float:
return moles * kelvin * UNIVERSAL_GAS_CONSTANT / pressure
def temperature_of_gas_system(moles: float, volume: float, pressure: float) -> float:
"""
>>> temperature_of_gas_system(2, 100, 5)
30.068090996146232
>>> temperature_of_gas_system(11, 5009, 1000)
54767.66101807144
>>> temperature_of_gas_system(3, -0.46, 23.5)
Traceback (most recent call last):
...
ValueError: Invalid inputs. Enter positive value.
"""
if moles < 0 or volume < 0 or pressure < 0:
raise ValueError("Invalid inputs. Enter positive value.")
return pressure * volume / (moles * UNIVERSAL_GAS_CONSTANT)
def moles_of_gas_system(kelvin: float, volume: float, pressure: float) -> float:
"""
>>> moles_of_gas_system(100, 5, 10)
0.06013618199229246
>>> moles_of_gas_system(110, 5009, 1000)
5476.766101807144
>>> moles_of_gas_system(3, -0.46, 23.5)
Traceback (most recent call last):
...
ValueError: Invalid inputs. Enter positive value.
"""
if kelvin < 0 or volume < 0 or pressure < 0:
raise ValueError("Invalid inputs. Enter positive value.")
return pressure * volume / (kelvin * UNIVERSAL_GAS_CONSTANT)
if __name__ == "__main__":
from doctest import testmod

View File

@ -226,7 +226,7 @@ def plot(
No doctest provided since this function does not have a return value.
"""
fig = plt.figure()
fig.canvas.manager.set_window_title(title)
fig.canvas.set_window_title(title)
ax = plt.axes(
xlim=(x_start, x_end), ylim=(y_start, y_end)
) # Set section to be plotted

View File

@ -1,117 +0,0 @@
"""
Implements an is valid email address algorithm
@ https://en.wikipedia.org/wiki/Email_address
"""
import string
email_tests: tuple[tuple[str, bool], ...] = (
("simple@example.com", True),
("very.common@example.com", True),
("disposable.style.email.with+symbol@example.com", True),
("other-email-with-hyphen@and.subdomains.example.com", True),
("fully-qualified-domain@example.com", True),
("user.name+tag+sorting@example.com", True),
("x@example.com", True),
("example-indeed@strange-example.com", True),
("test/test@test.com", True),
(
"123456789012345678901234567890123456789012345678901234567890123@example.com",
True,
),
("admin@mailserver1", True),
("example@s.example", True),
("Abc.example.com", False),
("A@b@c@example.com", False),
("abc@example..com", False),
("a(c)d,e:f;g<h>i[j\\k]l@example.com", False),
(
"12345678901234567890123456789012345678901234567890123456789012345@example.com",
False,
),
("i.like.underscores@but_its_not_allowed_in_this_part", False),
("", False),
)
# The maximum octets (one character as a standard unicode character is one byte)
# that the local part and the domain part can have
MAX_LOCAL_PART_OCTETS = 64
MAX_DOMAIN_OCTETS = 255
def is_valid_email_address(email: str) -> bool:
"""
Returns True if the passed email address is valid.
The local part of the email precedes the singular @ symbol and
is associated with a display-name. For example, "john.smith"
The domain is stricter than the local part and follows the @ symbol.
Global email checks:
1. There can only be one @ symbol in the email address. Technically if the
@ symbol is quoted in the local-part, then it is valid, however this
implementation ignores "" for now.
(See https://en.wikipedia.org/wiki/Email_address#:~:text=If%20quoted,)
2. The local-part and the domain are limited to a certain number of octets. With
unicode storing a single character in one byte, each octet is equivalent to
a character. Hence, we can just check the length of the string.
Checks for the local-part:
3. The local-part may contain: upper and lowercase latin letters, digits 0 to 9,
and printable characters (!#$%&'*+-/=?^_`{|}~)
4. The local-part may also contain a "." in any place that is not the first or
last character, and may not have more than one "." consecutively.
Checks for the domain:
5. The domain may contain: upper and lowercase latin letters and digits 0 to 9
6. Hyphen "-", provided that it is not the first or last character
7. The domain may also contain a "." in any place that is not the first or
last character, and may not have more than one "." consecutively.
>>> for email, valid in email_tests:
... assert is_valid_email_address(email) == valid
"""
# (1.) Make sure that there is only one @ symbol in the email address
if email.count("@") != 1:
return False
local_part, domain = email.split("@")
# (2.) Check octet length of the local part and domain
if len(local_part) > MAX_LOCAL_PART_OCTETS or len(domain) > MAX_DOMAIN_OCTETS:
return False
# (3.) Validate the characters in the local-part
if any(
char not in string.ascii_letters + string.digits + ".(!#$%&'*+-/=?^_`{|}~)"
for char in local_part
):
return False
# (4.) Validate the placement of "." characters in the local-part
if local_part.startswith(".") or local_part.endswith(".") or ".." in local_part:
return False
# (5.) Validate the characters in the domain
if any(char not in string.ascii_letters + string.digits + ".-" for char in domain):
return False
# (6.) Validate the placement of "-" characters
if domain.startswith("-") or domain.endswith("."):
return False
# (7.) Validate the placement of "." characters
if domain.startswith(".") or domain.endswith(".") or ".." in domain:
return False
return True
if __name__ == "__main__":
import doctest
doctest.testmod()
for email, valid in email_tests:
is_valid = is_valid_email_address(email)
assert is_valid == valid, f"{email} is {is_valid}"
print(f"Email address {email} is {'not ' if not is_valid else ''}valid")