feat: dump stuff from school again ig
|
@ -0,0 +1,57 @@
|
|||
from __future__ import annotations
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
def parseValue(value: str):
|
||||
value = value.strip()
|
||||
|
||||
if value.lower() == "true":
|
||||
return True
|
||||
elif value.lower() == "false":
|
||||
return False
|
||||
elif len(value.split(".")) == 2:
|
||||
return float(value)
|
||||
elif value.isnumeric():
|
||||
return int(value)
|
||||
else:
|
||||
return value
|
||||
|
||||
class Data:
|
||||
def __init__(self, fileName: str):
|
||||
with open(fileName, "r") as file:
|
||||
out = [[parseValue(value) for value in line.strip().split(",")] for line in file.readlines()]
|
||||
self.__data = { header: lst for (header, lst) in zip(out[0], zip(*out[1::])) }
|
||||
|
||||
def getHeaderData(self, header: str):
|
||||
"""Get the data under a header, CaSe insensitive"""
|
||||
for realHeader in self.__data.keys():
|
||||
if header.lower() == realHeader.lower():
|
||||
return self.__data[realHeader]
|
||||
|
||||
return None
|
||||
|
||||
def bubbleSort(lst: list, asc: bool = True) -> list:
|
||||
n = len(lst)
|
||||
|
||||
for i in range(n):
|
||||
for j in range(0, n - i - 1):
|
||||
if lst[j] > lst[j + 1]:
|
||||
lst[j], lst[j + 1] = lst[j + 1], lst[j]
|
||||
|
||||
return lst if asc else lst[::-1]
|
||||
|
||||
|
||||
def getHeights(data: Data, male: bool) -> tuple:
|
||||
heights = data.getHeaderData("height")
|
||||
males = data.getHeaderData("male")
|
||||
|
||||
return [height for height, isMale in zip(heights, males) if male == isMale]
|
||||
|
||||
def meanHeight(data: Data, male: bool):
|
||||
heights = getHeights(data, male)
|
||||
return sum(heights) / len(heights)
|
||||
|
||||
sports = Data("sports_data.csv")
|
||||
femaleHeights = bubbleSort(getHeights(sports, False))
|
||||
|
||||
plt.bar([i for i in range(len(femaleHeights))], femaleHeights)
|
||||
plt.savefig('yes.png')
|
BIN
school/a-level/Y12 2022-2024/Algorithms/Data Analysis/yes.png
Normal file
After Width: | Height: | Size: 9.1 KiB |
|
@ -0,0 +1,29 @@
|
|||
from math import log, floor
|
||||
|
||||
class Memoize:
|
||||
"""Helper class to memoize a decorated function."""
|
||||
def __init__(self, function):
|
||||
self.function = function
|
||||
self.memo = {}
|
||||
|
||||
def __call__(self, *args):
|
||||
if not args in self.memo:
|
||||
self.memo[args] = self.function(*args)
|
||||
return self.memo[args]
|
||||
|
||||
@Memoize
|
||||
def pdi(power: int, base: int, number: int) -> int:
|
||||
"""Computes the perfect digital invariant."""
|
||||
return sum(pow((number % pow(base, i + 1) - number % pow(base, i)) / pow(base, i), power) for i in range(0, floor(log(number, base)) + 1))
|
||||
|
||||
def isHappy(number: int, base: int = 10) -> bool:
|
||||
"""Check if a number is happy in a given base. Defaults to base 10."""
|
||||
seen = set()
|
||||
|
||||
while number > 1 and number not in seen:
|
||||
seen.add(number)
|
||||
number = pdi(2, base, number)
|
||||
|
||||
return number == 1
|
||||
|
||||
print(isHappy(19))
|
10
school/a-level/Y12 2022-2024/Algorithms/Happy Numbers.py
Normal file
|
@ -0,0 +1,10 @@
|
|||
def happyCheck(number: int):
|
||||
seen = set()
|
||||
|
||||
while number > 1 and number not in seen:
|
||||
seen.add(number)
|
||||
number = sum(int(digit) ** 2 for digit in list(str(number)))
|
||||
|
||||
return number == 1
|
||||
|
||||
print(happyCheck(19))
|
20
school/a-level/Y12 2022-2024/Algorithms/Merge Sort.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
def mergeSort(lst: list) -> list:
|
||||
if len(lst) > 1:
|
||||
mid = len(lst) // 2
|
||||
left = lst[:mid]
|
||||
right = lst[mid:]
|
||||
|
||||
mergeSort(left)
|
||||
mergeSort(right)
|
||||
|
||||
i = j = k = 0
|
||||
|
||||
while i < len(left) and j < len(right):
|
||||
if left[i] <= right[j]:
|
||||
lst[k] = left[i]
|
||||
i += 1
|
||||
else:
|
||||
lst[k] = right[j]
|
||||
|
||||
|
||||
print(mergeSort([3,5,2,1]))
|
57
school/a-level/Y12 2022-2024/Algorithms/bitwise/apply.py
Normal file
|
@ -0,0 +1,57 @@
|
|||
from typing import Callable
|
||||
from operator import and_, or_, xor
|
||||
|
||||
def applyOperator(binary: list, mask: list, operator: Callable[[int, int], list]) -> list:
|
||||
return [operator(a, b) for a, b in zip(binary, mask)]
|
||||
|
||||
def getBinary(prompt: str, length: int = None) -> list:
|
||||
while True:
|
||||
try:
|
||||
bits = list(input(f"{prompt} "))
|
||||
|
||||
for bit in bits:
|
||||
if bit not in ["0", "1"]:
|
||||
raise ValueError("Invalid binary number. Please try again.")
|
||||
|
||||
if len(bits) == 0:
|
||||
raise ValueError("Please make sure you enter a value.")
|
||||
elif length and len(bits) != length:
|
||||
raise ValueError(f"Please make sure the value is {length} bits long.")
|
||||
|
||||
return [int(x) for x in bits]
|
||||
except ValueError as error:
|
||||
print(error)
|
||||
|
||||
def makeChoice(prompt: str, choiceCount: int):
|
||||
while True:
|
||||
try:
|
||||
choice = int(input(f"{prompt} "))
|
||||
|
||||
if choice < 1 or choice > choiceCount:
|
||||
raise ValueError("Please make a choice that is within bounds.")
|
||||
|
||||
return choice
|
||||
except ValueError as error:
|
||||
print(error)
|
||||
|
||||
operators = {
|
||||
'AND': ('&', lambda binary, mask: applyOperator(binary, mask, and_)),
|
||||
'OR': ('|', lambda binary, mask: applyOperator(binary, mask, or_)),
|
||||
'XOR': ('^', lambda binary, mask: applyOperator(binary, mask, xor))
|
||||
}
|
||||
|
||||
# take in the binary values
|
||||
binary = getBinary("Please enter your binary number:")
|
||||
mask = getBinary("Please enter the mask:", len(binary))
|
||||
|
||||
# choose an operation
|
||||
choices = [(i + 1, operator) for i, operator in enumerate(operators.keys())]
|
||||
|
||||
for i, operator in choices:
|
||||
print(f'{i}. {operator}')
|
||||
|
||||
symbol, operation = operators.get(choices[makeChoice("Please choose a logical operation:", len(choices)) - 1][1])
|
||||
output = "".join(str(x) for x in operation(binary, mask))
|
||||
binary, mask = "".join(str(x) for x in binary), "".join(str(x) for x in mask)
|
||||
|
||||
print(f"{binary} {symbol} {mask} = {output}")
|
141
school/a-level/Y12 2022-2024/Algorithms/bitwise/applyCurses.py
Normal file
|
@ -0,0 +1,141 @@
|
|||
# windows: pip install windows-curses
|
||||
import curses
|
||||
from operator import and_, or_, xor
|
||||
|
||||
def initCurses() -> tuple:
|
||||
"""Initialise curses and any necessary colour pairs"""
|
||||
screen = curses.initscr()
|
||||
screen.keypad(True)
|
||||
|
||||
curses.cbreak()
|
||||
curses.noecho()
|
||||
curses.start_color()
|
||||
|
||||
# default
|
||||
curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLACK)
|
||||
default = curses.color_pair(1)
|
||||
|
||||
# highlighted
|
||||
curses.init_pair(2, curses.COLOR_BLACK, curses.COLOR_WHITE)
|
||||
highlighted = curses.color_pair(2)
|
||||
|
||||
# error
|
||||
curses.init_pair(3, curses.COLOR_RED, curses.COLOR_BLACK)
|
||||
error = curses.color_pair(3)
|
||||
|
||||
return (screen, (default, highlighted, error))
|
||||
|
||||
def destroyCurses(screen):
|
||||
"""Close out of curses and reverse any destructive changes to the terminal"""
|
||||
curses.nocbreak()
|
||||
screen.keypad(False)
|
||||
curses.echo()
|
||||
curses.endwin()
|
||||
|
||||
def selectMenu(screen, options: list, header: str = "Pick an option:"):
|
||||
selectedIndex = 0
|
||||
optionCount = len(options)
|
||||
|
||||
while True:
|
||||
screen.clear()
|
||||
screen.addstr(f"{header}\n\n", curses.A_BOLD)
|
||||
|
||||
for i in range(optionCount):
|
||||
screen.addstr(f"{i + 1}. ")
|
||||
screen.addstr(f"{options[i]}\n", highlighted if i == selectedIndex else default)
|
||||
|
||||
c = screen.getch()
|
||||
|
||||
if c == curses.KEY_UP or c == curses.KEY_LEFT:
|
||||
selectedIndex -= 1 - optionCount
|
||||
selectedIndex %= optionCount
|
||||
|
||||
elif c == curses.KEY_DOWN or c == curses.KEY_RIGHT:
|
||||
selectedIndex += 1
|
||||
selectedIndex %= optionCount
|
||||
|
||||
elif c == curses.KEY_ENTER or chr(c) in '\n\r':
|
||||
return selectedIndex + 1
|
||||
|
||||
def getBinary(screen, prompt: str, length: int = None) -> list:
|
||||
errorMessage = None
|
||||
|
||||
while True:
|
||||
screen.clear()
|
||||
screen.addstr(0, 0, f"{prompt}", curses.A_BOLD)
|
||||
|
||||
# display an error message if necessary
|
||||
if errorMessage:
|
||||
screen.addstr(3, 0, errorMessage, error | curses.A_BOLD)
|
||||
errorMessage = None
|
||||
|
||||
# fetch the inputted data
|
||||
curses.echo()
|
||||
bits = list(screen.getstr(1, 0, 50).decode("utf-8"))
|
||||
curses.noecho()
|
||||
|
||||
# validate that a binary value was entered
|
||||
for bit in bits:
|
||||
if bit not in ["0", "1"]:
|
||||
errorMessage = "Invalid binary number. Please try again."
|
||||
|
||||
if len(bits) == 0:
|
||||
errorMessage = "Please make sure you enter a value."
|
||||
|
||||
elif length and len(bits) != length:
|
||||
errorMessage = f"Please make sure the value is {length} bits long."
|
||||
|
||||
if errorMessage:
|
||||
continue
|
||||
|
||||
return [int(x) for x in bits]
|
||||
|
||||
def applyOperator(screen, binary: list, mask: list) -> list:
|
||||
# todo: fix
|
||||
# operatorList = list(OPERATORS.keys())
|
||||
# symbol, operator = OPERATORS[operatorList[selectMenu(screen, operatorList) - 1]]
|
||||
# output = "".join(str(operator(a, b)) for a, b in zip(binary, mask))
|
||||
binary = "".join(str(x) for x in binary)
|
||||
mask = "".join(str(x) for x in mask)
|
||||
print(binary, mask, screen)
|
||||
|
||||
while True:
|
||||
screen.clear()
|
||||
screen.addstr(0, 0, f"{binary} {mask}", curses.A_BOLD)
|
||||
screen.addstr(3, 0, "Press the backspace key to get back to the main menu.")
|
||||
screen.addstr(4, 0, "")
|
||||
|
||||
# get pressed key
|
||||
c = screen.getch()
|
||||
|
||||
if c == 8 or c == 127 or c == curses.KEY_BACKSPACE:
|
||||
break
|
||||
|
||||
OPERATORS = {
|
||||
'AND': ('&', lambda binary, mask: applyOperator(binary, mask, and_)),
|
||||
'OR': ('|', lambda binary, mask: applyOperator(binary, mask, or_)),
|
||||
'XOR': ('^', lambda binary, mask: applyOperator(binary, mask, xor))
|
||||
}
|
||||
|
||||
if __name__ == "__main__":
|
||||
# init curses
|
||||
screen, (default, highlighted, error) = initCurses()
|
||||
binary, mask = [], []
|
||||
|
||||
while True:
|
||||
binaryStr = "[NONE]" if len(binary) == 0 else "".join(str(x) for x in binary)
|
||||
maskStr = "[NONE]" if len(mask) == 0 else "".join(str(x) for x in mask)
|
||||
|
||||
choice = selectMenu(screen, ["Apply a gate", "Change the binary number", "Change the mask", "Exit"], f"Current binary number: {binaryStr}\nCurrent mask: {maskStr}\n\nWhat would you like to do?")
|
||||
|
||||
if choice == 1:
|
||||
applyOperator(screen, binary, mask)
|
||||
elif choice == 2:
|
||||
binary = getBinary(screen, "Please enter a new binary value:", None if len(mask) == 0 else len)
|
||||
elif choice == 3:
|
||||
mask = getBinary(screen, "Please enter a new mask:", None if len(binary) == 0 else len(binary))
|
||||
elif choice == 4:
|
||||
break
|
||||
|
||||
# destroy curses
|
||||
destroyCurses(screen)
|
|
@ -0,0 +1,6 @@
|
|||
def xorCipher(text: str, key: int) -> str:
|
||||
"""Encrypts/decrypts with the XOR cipher"""
|
||||
if key > 2**8 - 1:
|
||||
raise ValueError("Key can not be greater than 255.")
|
||||
|
||||
return "".join(chr(ord(c) ^ key) for c in text)
|
9
school/a-level/Y12 2022-2024/Algorithms/caesar.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
# def encode(text: str, key: int) -> str:
|
||||
# return ''.join(chr(ord(c) + (key % 26)) for c in text)
|
||||
|
||||
# def decode(ciphertext: str, key: int) -> str:
|
||||
# return ''.join(chr(ord(c) - (key % 26)) for c in ciphertext)
|
||||
|
||||
c,o,e,d=chr,ord,lambda t,k:''.join(c(o(x)+k%26)for x in t),lambda t,k:''.join(c(o(x)-k%26)for x in t)
|
||||
|
||||
print(e("cats", 6))
|
|
@ -0,0 +1,15 @@
|
|||
def recursive_factorial(n):
|
||||
if n == 1:
|
||||
return 1
|
||||
|
||||
return n * recursive_factorial(n - 1)
|
||||
|
||||
def iterative_factorial(n):
|
||||
out = 1
|
||||
|
||||
for i in range(2, n + 1):
|
||||
out *= i
|
||||
|
||||
return out
|
||||
|
||||
assert iterative_factorial(5) == recursive_factorial(5)
|
|
@ -0,0 +1,43 @@
|
|||
from timeit import timeit
|
||||
from random import randint
|
||||
from functools import wraps
|
||||
|
||||
TRIAL_COUNT = 100
|
||||
n = randint(2, 20)
|
||||
|
||||
# non-memoized
|
||||
def fib(n):
|
||||
if n in {0, 1}:
|
||||
return n
|
||||
|
||||
return fib(n - 1) + fib(n - 2)
|
||||
|
||||
# memoized
|
||||
def memoize(func):
|
||||
cache = {}
|
||||
|
||||
@wraps(func)
|
||||
def wrapper(*args):
|
||||
if args not in cache:
|
||||
cache[args] = func(*args)
|
||||
return cache[args]
|
||||
|
||||
return wrapper
|
||||
|
||||
memo_fib = memoize(fib)
|
||||
|
||||
# output
|
||||
a = fib(n)
|
||||
b = memo_fib(n)
|
||||
|
||||
assert a == b
|
||||
|
||||
time_a = timeit(lambda: fib(n), number=TRIAL_COUNT)
|
||||
time_b = timeit(lambda: memo_fib(n), number=TRIAL_COUNT)
|
||||
time_diff = time_a - time_b
|
||||
|
||||
print(f"""fib({n}) = {a}
|
||||
non-memo: {time_a} seconds
|
||||
memo: {time_b} seconds
|
||||
|
||||
difference of {time_diff} seconds ({round(time_diff / time_a * 100, 3)}% of non-memo)""")
|
After Width: | Height: | Size: 9.5 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 25 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 17 KiB |
|
@ -0,0 +1,17 @@
|
|||
from typing import Dict
|
||||
import re
|
||||
|
||||
def letterFrequency(text: str) -> Dict[str, int]:
|
||||
output = {}
|
||||
text = re.sub('[^a-zA-Z]+', '', text)
|
||||
|
||||
for letter in text:
|
||||
if letter in output:
|
||||
output[letter] += 1
|
||||
else:
|
||||
output[letter] = 1
|
||||
|
||||
return output
|
||||
|
||||
frequency = letterFrequency('I wish I wish with all my heart to fly with dragons in a land apart')
|
||||
print(frequency)
|
|
@ -0,0 +1,92 @@
|
|||
from time import sleep
|
||||
|
||||
# :eyes:
|
||||
def clearTerminal():
|
||||
print('\n' * 25)
|
||||
|
||||
def findPrimes(upperBound: int):
|
||||
"""
|
||||
Implementation of the Sieve of Eratosthenes.
|
||||
https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes
|
||||
"""
|
||||
mask = [True for _ in range(upperBound)]
|
||||
primes = []
|
||||
|
||||
mask[0] = False
|
||||
mask[1] = False
|
||||
|
||||
for i in range(2, upperBound):
|
||||
if mask[i]:
|
||||
primes.append(i)
|
||||
|
||||
j = 2 * i
|
||||
|
||||
while j < upperBound:
|
||||
mask[j] = False
|
||||
j += i
|
||||
|
||||
return primes
|
||||
|
||||
POSSIBLE_PRIMES = findPrimes(1_000_000) # Would be better to hardcode this list as it is constant, however I want good marks and to show complex algorithms I guess!!!
|
||||
inputtedPrimes = []
|
||||
|
||||
def getPrime(primeList):
|
||||
while True:
|
||||
try:
|
||||
value = int(input("Please enter a prime number to add to the list: "))
|
||||
|
||||
# Ensure that the value fits the requirements set out by the brief
|
||||
if value > 999_999 or value < 0:
|
||||
raise ValueError('Please ensure you enter an integer at most 6 digits long!')
|
||||
elif value not in POSSIBLE_PRIMES:
|
||||
raise ValueError('That is not a prime number! Please try again.')
|
||||
elif value in primeList:
|
||||
raise ValueError('You have already inputted that prime number! Please enter a different one.')
|
||||
|
||||
# Mutate the prime list and return it back to the caller
|
||||
primeList.append(value)
|
||||
return primeList
|
||||
except ValueError as e:
|
||||
# Check to see if the error was raised by Python or manually, and print messages accordingly
|
||||
if e.args[0].startswith("invalid literal"):
|
||||
print('Please enter a valid integer!')
|
||||
else:
|
||||
print(e)
|
||||
|
||||
if __name__ == "__main__":
|
||||
while True:
|
||||
print("""Welcome to the prime checker!
|
||||
What would you like to do?
|
||||
|
||||
1) Add a number to the list
|
||||
2) View the list of inputted primes
|
||||
3) Exit
|
||||
""")
|
||||
|
||||
while True:
|
||||
try:
|
||||
choice = int(input("Please enter your choice: "))
|
||||
|
||||
if choice not in [1, 2, 3]:
|
||||
raise ValueError
|
||||
|
||||
break
|
||||
except ValueError:
|
||||
print("Please enter a valid option from the list (1-3)")
|
||||
|
||||
clearTerminal()
|
||||
|
||||
if choice == 1:
|
||||
inputtedPrimes = getPrime(inputtedPrimes)
|
||||
elif choice == 2:
|
||||
# Only print the list if there is values inside to print
|
||||
if len(inputtedPrimes) > 0:
|
||||
print(', '.join(map(lambda x: str(x), inputtedPrimes)))
|
||||
else:
|
||||
print('There is currently nothing in the list!')
|
||||
|
||||
sleep(2.5)
|
||||
elif choice == 3:
|
||||
exit()
|
||||
|
||||
clearTerminal()
|
|
@ -0,0 +1,18 @@
|
|||
import unittest
|
||||
import sys
|
||||
|
||||
from primeList import findPrimes
|
||||
|
||||
# My code can not be unit tested much with how I have structured it but here's proof I know how to do it I guess :D
|
||||
|
||||
class SieveTest(unittest.TestCase):
|
||||
def test_sieve(self):
|
||||
"""
|
||||
Test that the sieve correctly generates a list of primes up until 100.
|
||||
"""
|
||||
primes = findPrimes(100)
|
||||
self.assertEqual(primes, [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97])
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.argv.append('-v')
|
||||
unittest.main()
|
|
@ -0,0 +1,16 @@
|
|||
def intInput(name):
|
||||
while True:
|
||||
try:
|
||||
number = int(input(f'Please enter an integer for {name}: '))
|
||||
return number
|
||||
except ValueError:
|
||||
print('Please enter a valid integer.')
|
||||
|
||||
A = intInput('A')
|
||||
B = intInput('B')
|
||||
C = intInput('C')
|
||||
|
||||
if (A < B) or (B < C):
|
||||
A = B
|
||||
|
||||
print(A)
|
|
@ -0,0 +1,190 @@
|
|||
from __future__ import annotations
|
||||
from random import uniform
|
||||
from typing import List, Iterable, Callable, Union
|
||||
from functools import reduce
|
||||
from operator import iconcat
|
||||
|
||||
class OperationError(Exception):
|
||||
pass
|
||||
|
||||
class MatrixRow:
|
||||
def __init__(self, length: int):
|
||||
self.__values: List[float] = [0 for _ in range(length)]
|
||||
self.__length = length
|
||||
|
||||
def __getitem__(self, column: int):
|
||||
"""Index the row."""
|
||||
if column <= 0 or column > self.__length:
|
||||
raise IndexError("Out of bounds")
|
||||
|
||||
return self.__values[column - 1]
|
||||
|
||||
def __setitem__(self, column: int, newValue: float):
|
||||
"""Update a value in the row at a given column."""
|
||||
if column <= 0 or column > self.__length:
|
||||
raise IndexError("Out of bounds")
|
||||
|
||||
self.__values[column - 1] = newValue
|
||||
|
||||
def __iter__(self) -> Iterable:
|
||||
processedValues = []
|
||||
|
||||
for value in self.__values:
|
||||
if type(value) == float and value.is_integer():
|
||||
processedValues.append(int(value))
|
||||
else:
|
||||
processedValues.append(value)
|
||||
|
||||
return iter(processedValues)
|
||||
|
||||
|
||||
class SquareMatrix:
|
||||
def __init__(self, n: int):
|
||||
self.__rows: List[MatrixRow] = [MatrixRow(n) for _ in range(n)]
|
||||
self.__n = n
|
||||
self.__rowFormatting = "{:^10}| " * n
|
||||
self.__divider = "-" * (n * 12)
|
||||
|
||||
@staticmethod
|
||||
def From(rows: List[List[float]]) -> SquareMatrix:
|
||||
n = len(rows)
|
||||
|
||||
if len(set([len(row) for row in rows])) != 1 or len(rows[0]) != n:
|
||||
raise ValueError("Rows do not form a valid square matrix.")
|
||||
|
||||
matrix = SquareMatrix(n)
|
||||
|
||||
for i in range(n):
|
||||
for j in range(n):
|
||||
matrix[i + 1][j + 1] = rows[i][j]
|
||||
|
||||
return matrix
|
||||
|
||||
def size(self) -> int:
|
||||
"""Returns the size of the square matrix."""
|
||||
return self.__n
|
||||
|
||||
def populate(self, minimum: float, maximum: float) -> SquareMatrix:
|
||||
"""Populates the matrix with random values."""
|
||||
if minimum > maximum:
|
||||
raise ValueError('Minimum value must be less than maximum value!')
|
||||
|
||||
for i in range(1, self.__n + 1):
|
||||
for j in range(1, self.__n + 1):
|
||||
self[i][j] = uniform(minimum, maximum)
|
||||
|
||||
return self
|
||||
|
||||
def sumCorners(self) -> float:
|
||||
"""Computes the sum of the corners of the matrix."""
|
||||
return self[1][1] + self[1][self.__n] + self[self.__n][1] + self[self.__n][self.__n]
|
||||
|
||||
def values(self) -> float:
|
||||
"""Return a list of all of the values in a matrix."""
|
||||
return reduce(iconcat, self.__rows, [])
|
||||
|
||||
def averageValue(self) -> float:
|
||||
"""Computes the avaerage value in the matrix."""
|
||||
return sum(self.values()) / (self.__n ** 2)
|
||||
|
||||
def diagonalSum(self, left: bool = True) -> float:
|
||||
"""Computes a diagonal sum of the matrix."""
|
||||
sum = 0
|
||||
|
||||
for i, row in enumerate(self.__rows):
|
||||
if left:
|
||||
sum += row[i + 1]
|
||||
else:
|
||||
sum += row[self.__n - i]
|
||||
|
||||
return sum
|
||||
|
||||
def diagonalDifference(self) -> float:
|
||||
"""Computes the diagonal difference of the matrix."""
|
||||
left = self.diagonalSum()
|
||||
right = self.diagonalSum(False)
|
||||
|
||||
return abs(right - left)
|
||||
|
||||
def transpose(self) -> SquareMatrix:
|
||||
"""Transpose the matrix."""
|
||||
transposed = list(zip(*self.__rows))
|
||||
|
||||
for i in range(self.__n):
|
||||
for j in range(self.__n):
|
||||
self[i + 1][j + 1] = transposed[i][j]
|
||||
|
||||
return self
|
||||
|
||||
def __str__(self) -> str:
|
||||
"""Represents the matrix in a string."""
|
||||
out = self.__divider
|
||||
|
||||
for row in self.__rows:
|
||||
out += f"\n|{self.__rowFormatting.format(*row)}\n{self.__divider}"
|
||||
|
||||
return out
|
||||
|
||||
def __getitem__(self, row: int):
|
||||
"""Allows indexing of the matrix"""
|
||||
if row - 1 > self.__n:
|
||||
raise IndexError("Out of bounds")
|
||||
|
||||
return self.__rows[row - 1]
|
||||
|
||||
def __applyToMatrices(self, matrix: SquareMatrix, method: Callable[[float, float], float]) -> SquareMatrix:
|
||||
"""Returns a new matrix containing the results of a method applied to the corresponding values of this matrix, and another one."""
|
||||
if matrix.size() != self.__n:
|
||||
raise OperationError("Matrix sizes are incompatible")
|
||||
|
||||
newMatrix = SquareMatrix(self.__n)
|
||||
|
||||
for i in range(1, self.__n + 1):
|
||||
for j in range(1, self.__n + 1):
|
||||
newMatrix[i][j] = method(self[i][j], matrix[i][j])
|
||||
|
||||
return newMatrix
|
||||
|
||||
def __applyToSelf(self, method: Callable[[float], float]) -> SquareMatrix:
|
||||
"""Returns a new matrix containing the results of a method applies to the value of this matrix."""
|
||||
newMatrix = SquareMatrix(self.__n)
|
||||
|
||||
for i in range(1, self.__n + 1):
|
||||
for j in range(1, self.__n + 1):
|
||||
newMatrix[i][j] = method(self[i][j])
|
||||
|
||||
return newMatrix
|
||||
|
||||
def __add__(self, value: Union[SquareMatrix, float]) -> SquareMatrix:
|
||||
"""Add two matrices."""
|
||||
if isinstance(value, SquareMatrix):
|
||||
return self.__applyToMatrices(value, lambda x, y: x + y)
|
||||
else:
|
||||
return self.__applyToSelf(lambda x: x + value)
|
||||
|
||||
def __sub__(self, value: Union[SquareMatrix, float]) -> SquareMatrix:
|
||||
"""Subtract two matrices."""
|
||||
if isinstance(value, SquareMatrix):
|
||||
return self.__applyToMatrices(value, lambda x, y: x - y)
|
||||
else:
|
||||
return self.__applyToSelf(lambda x: x - value)
|
||||
|
||||
def __mul__(self, scalar: float) -> SquareMatrix:
|
||||
"""Multiply the matrix by a scalar."""
|
||||
return self.__applyToSelf(lambda x: x * scalar)
|
||||
|
||||
def __floordiv__(self, scalar: float) -> SquareMatrix:
|
||||
"""Floor divide the matrix by a scalar."""
|
||||
return self.__applyToSelf(lambda x: x // scalar)
|
||||
|
||||
def __mod__(self, scalar: float) -> SquareMatrix:
|
||||
"""Modulo the matrix by a scalar."""
|
||||
return self.__applyToSelf(lambda x: x % scalar)
|
||||
|
||||
def __pow__(self, scalar: float) -> SquareMatrix:
|
||||
"""Power the matrix by a scalar."""
|
||||
return self.__applyToSelf(lambda x: x ** scalar)
|
||||
|
||||
a = SquareMatrix.From([[12.4,3,15],[4,7,8],[5,6,2]])
|
||||
|
||||
print(a)
|
|
@ -0,0 +1,79 @@
|
|||
from typing import List, Callable
|
||||
from random import randint
|
||||
from functools import reduce
|
||||
from operator import iconcat
|
||||
|
||||
def getInteger(prompt: str, err: str, validation: Callable[[int], bool] = None) -> int:
|
||||
"""Fetch an integer from the user and validate it if necessary."""
|
||||
while True:
|
||||
try:
|
||||
value = int(input(prompt))
|
||||
|
||||
if validation and validation(value):
|
||||
raise ValueError
|
||||
|
||||
return value
|
||||
except ValueError:
|
||||
print(err)
|
||||
|
||||
def generateMatrix(n: int, min: int, max: int) -> List[List[int]]:
|
||||
"""Generates an nxn square matrix and populates it with random integer values between two bounds."""
|
||||
if min > max:
|
||||
raise ValueError('Minimum value must be less than maximum value!')
|
||||
|
||||
return [[randint(min, max) for _ in range(n)] for _ in range(n)]
|
||||
|
||||
def printMatrix(matrix: List[List[int]]):
|
||||
n = len(matrix)
|
||||
rowFormatting = "{:^7}| " * n
|
||||
divider = '-' * (n * 9 - 1)
|
||||
|
||||
print(divider)
|
||||
|
||||
for row in matrix:
|
||||
print(rowFormatting.format(*row))
|
||||
print(divider)
|
||||
|
||||
def diagonalSum(matrix: List[List[int]], left: bool = True) -> int:
|
||||
"""Computes the diagonal sum of an nxn square matrix."""
|
||||
sum = 0
|
||||
n = len(matrix)
|
||||
|
||||
for i, row in enumerate(matrix):
|
||||
if left:
|
||||
sum += row[i]
|
||||
else:
|
||||
sum += row[n - i - 1]
|
||||
|
||||
return sum
|
||||
|
||||
def diagonalDifference(matrix: List[List[int]]) -> int:
|
||||
"""Computes the diagonal difference of an nxn square matrix."""
|
||||
left = diagonalSum(matrix)
|
||||
right = diagonalSum(matrix, False)
|
||||
|
||||
return abs(right - left)
|
||||
|
||||
def sumOfCorners(matrix: List[List[int]]) -> int:
|
||||
"""Computes the sum of the corners of an nxn square matrix."""
|
||||
n = len(matrix)
|
||||
|
||||
return matrix[0][0] + matrix[0][n - 1] + matrix[n - 1][0] + matrix[n - 1][n - 1]
|
||||
|
||||
def averageValue(matrix: List[List[int]]) -> int:
|
||||
"""Computes the average value in an nxn square matrix."""
|
||||
values = reduce(iconcat, matrix, [])
|
||||
|
||||
return sum(values) / (len(matrix) ** 2)
|
||||
|
||||
n = getInteger('Please enter the dimension of the square matrix: ', 'Please enter a natural number greater than 1.', lambda x: x <= 1)
|
||||
matrix = generateMatrix(n, 1, 100)
|
||||
# matrix = [[12,3,15],[4,7,8],[5,6,2]]
|
||||
difference = diagonalDifference(matrix)
|
||||
corners = sumOfCorners(matrix)
|
||||
average = averageValue(matrix)
|
||||
|
||||
printMatrix(matrix)
|
||||
print(f"""Diagonal difference: {difference}
|
||||
Sum of the corners: {corners}
|
||||
Average value: {average}""")
|
|
@ -0,0 +1,34 @@
|
|||
from random import randint as r
|
||||
from functools import reduce
|
||||
from operator import iconcat
|
||||
a,p=range,print
|
||||
|
||||
def q(p,e,v):
|
||||
while True:
|
||||
try:
|
||||
value = int(input(p))
|
||||
if v and v(value):raise ValueError
|
||||
return value
|
||||
except ValueError:p(e)
|
||||
|
||||
def g(n,a,b):
|
||||
if a > b:raise ValueError('Minimum value must be less than maximum value!');return[[r(a,b)for _ in a(n)]for _ in a(n)]
|
||||
|
||||
def o(m):
|
||||
n,r,d=len(m),"{:^7}| "*n,'-'*(n*9-1);p(d);[p(f'{r.format(*x)}\n{d}')for x in m]
|
||||
|
||||
def s(m,l=True):
|
||||
s,n=0,len(m);[(s:=s+x[i])if l else(s:=s+x[n-i-1])for i,x in enumerate(m)];return s
|
||||
|
||||
def d(m):
|
||||
l,x=s(m),s(m, False)
|
||||
|
||||
return abs(x-l)
|
||||
|
||||
def c(m):
|
||||
n=len(m)-1;return m[0][0]+m[0][n]+m[n][0]+m[n][n]
|
||||
|
||||
a,n=lambda m:sum(reduce(iconcat,m,[]))/(len(m)**2),q('Please enter the dimension of the square matrix: ','Please enter a natural number greater than 1.',lambda x:x<=1);m=g(n,1,100);z,x,k=d(m),c(m),a(m);o(m)
|
||||
p(f"""Diagonal difference: {z}
|
||||
Sum of the corners: {x}
|
||||
Average value: {k}""")
|
215
school/a-level/Y12 2022-2024/Video Playlist.py
Normal file
|
@ -0,0 +1,215 @@
|
|||
# note: On Windows, pip install windows-curses
|
||||
from __future__ import annotations
|
||||
from urllib.request import urlopen
|
||||
from json import loads as loadJson
|
||||
from webbrowser import open_new_tab as browserOpen
|
||||
from typing import List
|
||||
from re import search as searchRegex
|
||||
from pickle import load as pickleLoad, dump as pickleDump
|
||||
from os.path import join as pathJoin, exists as pathExists
|
||||
from sys import path as sysPath
|
||||
import curses
|
||||
|
||||
class InvalidID(Exception): pass
|
||||
|
||||
class Playlist:
|
||||
def __init__(self, nickname: str):
|
||||
self.__nickname = nickname
|
||||
self.__videos: List[Video] = []
|
||||
self.__file = Playlist.getPath(nickname)
|
||||
|
||||
if pathExists(self.__file):
|
||||
with open(self.__file, "rb") as f:
|
||||
self.__videos = pickleLoad(f)
|
||||
|
||||
@staticmethod
|
||||
def getPath(nickname: str) -> str:
|
||||
return pathJoin(sysPath[0], f"{nickname}.playlist")
|
||||
|
||||
def getName(self) -> str:
|
||||
return self.__nickname
|
||||
|
||||
def addVideo(self, video: Video):
|
||||
self.__videos.append(video)
|
||||
return self.__save()
|
||||
|
||||
def __save(self):
|
||||
with open(self.__file, "wb") as f:
|
||||
pickleDump(self.__videos, f)
|
||||
|
||||
return self
|
||||
|
||||
def __getitem__(self, index: int):
|
||||
if index > len(self.__videos) - 1:
|
||||
raise IndexError("Out of bounds")
|
||||
|
||||
return self.__videos[index]
|
||||
|
||||
def __delitem__(self, index: int):
|
||||
if index > len(self.__videos) - 1:
|
||||
raise IndexError("Out of bounds")
|
||||
|
||||
del self.__videos[index]
|
||||
self.__save()
|
||||
|
||||
def __len__(self):
|
||||
return len(self.__videos)
|
||||
|
||||
class Video:
|
||||
def __init__(self, id: str):
|
||||
self.__id = id
|
||||
self.__url = f"https://youtu.be/{self.__id}"
|
||||
|
||||
try:
|
||||
with urlopen(f"https://www.youtube.com/oembed?url=https://youtu.be/{self.__id}&format=json") as response:
|
||||
data = response.read()
|
||||
|
||||
data = loadJson(data)
|
||||
self.__title = data["title"]
|
||||
self.__author = data["author_name"]
|
||||
except:
|
||||
raise InvalidID(f"{id} is not a valid ID!")
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.__author} - {self.__title}"
|
||||
|
||||
def open(self):
|
||||
browserOpen(self.__url)
|
||||
|
||||
# Initialise curses
|
||||
screen = curses.initscr()
|
||||
screen.keypad(True)
|
||||
curses.cbreak()
|
||||
curses.noecho()
|
||||
|
||||
# Initialise colours
|
||||
curses.start_color()
|
||||
curses.init_pair(1, curses.COLOR_BLACK, curses.COLOR_WHITE)
|
||||
curses.init_pair(2, curses.COLOR_WHITE, curses.COLOR_BLACK)
|
||||
curses.init_pair(3, curses.COLOR_RED, curses.COLOR_BLACK)
|
||||
|
||||
black, white, red = curses.color_pair(1), curses.color_pair(2), curses.color_pair(3)
|
||||
|
||||
def selectMenu(screen, options: List[str]):
|
||||
selectedIndex = 0
|
||||
optionCount = len(options)
|
||||
|
||||
while True:
|
||||
screen.clear()
|
||||
screen.addstr("Pick an option:\n\n", curses.A_BOLD)
|
||||
|
||||
for i in range(optionCount):
|
||||
screen.addstr(f"{i + 1}. ")
|
||||
screen.addstr(f"{options[i]}\n", black if i == selectedIndex else white)
|
||||
|
||||
c = screen.getch()
|
||||
|
||||
if c == curses.KEY_UP or c == curses.KEY_LEFT:
|
||||
selectedIndex -= 1 - optionCount
|
||||
selectedIndex %= optionCount
|
||||
|
||||
elif c == curses.KEY_DOWN or c == curses.KEY_RIGHT:
|
||||
selectedIndex += 1
|
||||
selectedIndex %= optionCount
|
||||
|
||||
elif c == curses.KEY_ENTER or chr(c) in '\n\r':
|
||||
return selectedIndex + 1
|
||||
|
||||
def addVideo(screen, playlist: Playlist):
|
||||
hasErrored = False
|
||||
|
||||
while True:
|
||||
screen.clear()
|
||||
screen.addstr(0, 0, "Please enter a YouTube URL/ID:", curses.A_BOLD)
|
||||
|
||||
# Display the error message if necessary
|
||||
if hasErrored:
|
||||
screen.addstr(3, 0, "Please make sure you have entered a valid URL/ID.", red | curses.A_BOLD)
|
||||
|
||||
# Fetch the inputted data
|
||||
curses.echo()
|
||||
videoId = screen.getstr(1, 0, 50).decode("utf-8")
|
||||
curses.noecho()
|
||||
|
||||
# Attempt to extract a video ID
|
||||
parsed = searchRegex(r"(?:youtu\.be\/|youtube\.com(?:\/embed\/|\/v\/|\/watch\?v=|\/user\/\S+|\/ytscreeningroom\?v=))([\w\-]{10,12})\b", videoId)
|
||||
|
||||
if parsed:
|
||||
videoId = parsed.group(1)
|
||||
|
||||
# Attempt to add the video to the playlist
|
||||
try:
|
||||
video = Video(str(videoId))
|
||||
playlist.addVideo(video)
|
||||
|
||||
break
|
||||
except InvalidID:
|
||||
hasErrored = True
|
||||
|
||||
def viewPlaylist(screen, playlist: Playlist):
|
||||
selectedIndex = 0
|
||||
selectedPlay = True
|
||||
playlistLength = len(playlist)
|
||||
|
||||
while True:
|
||||
screen.clear()
|
||||
screen.addstr(f"{playlist.getName()} playlist\n", curses.A_BOLD)
|
||||
screen.addstr("Press the backspace key to exit this menu.\n\n")
|
||||
|
||||
if playlistLength == 0:
|
||||
screen.addstr("There is nothing here!\n")
|
||||
c = screen.getch()
|
||||
else:
|
||||
for i in range(playlistLength):
|
||||
screen.addstr(f"{i + 1}. {playlist[i]} ")
|
||||
screen.addstr("[PLAY]", black if selectedPlay and selectedIndex == i else white)
|
||||
screen.addstr(" ")
|
||||
screen.addstr("[DELETE]\n", black if not selectedPlay and selectedIndex == i else white)
|
||||
|
||||
c = screen.getch()
|
||||
|
||||
if c == curses.KEY_UP:
|
||||
selectedIndex -= 1 - playlistLength
|
||||
selectedIndex %= playlistLength
|
||||
|
||||
elif c == curses.KEY_DOWN:
|
||||
selectedIndex += 1
|
||||
selectedIndex %= playlistLength
|
||||
|
||||
elif c == curses.KEY_LEFT:
|
||||
selectedPlay = True
|
||||
|
||||
elif c == curses.KEY_RIGHT:
|
||||
selectedPlay = False
|
||||
|
||||
elif c == curses.KEY_ENTER or chr(c) in '\n\r':
|
||||
if selectedPlay:
|
||||
playlist[selectedIndex].open()
|
||||
else:
|
||||
del playlist[selectedIndex]
|
||||
playlistLength -= 1
|
||||
selectedIndex = 0 if selectedIndex == 0 else selectedIndex - 1
|
||||
|
||||
if c == 8 or c == 127 or c == curses.KEY_BACKSPACE:
|
||||
break
|
||||
|
||||
# todo: add custom playlist names
|
||||
|
||||
if __name__ == "__main__":
|
||||
playlist = Playlist("main")
|
||||
|
||||
while True:
|
||||
choice = selectMenu(screen, ["Add a video to the playlist", "View the playlist", "Exit"])
|
||||
|
||||
if choice == 1:
|
||||
addVideo(screen, playlist)
|
||||
elif choice == 2:
|
||||
viewPlaylist(screen, playlist)
|
||||
elif choice == 3:
|
||||
break
|
||||
|
||||
# Exit curses
|
||||
curses.nocbreak()
|
||||
screen.keypad(False)
|
||||
curses.echo()
|
||||
curses.endwin()
|
25
school/a-level/Y12 2022-2024/curry.py
Normal file
|
@ -0,0 +1,25 @@
|
|||
import functools
|
||||
import inspect
|
||||
from copy import copy
|
||||
from typing import Callable
|
||||
|
||||
def curry(function: Callable) -> Callable:
|
||||
# call functools.partial recursively in order to curry a function
|
||||
def inner(*args, **kwargs):
|
||||
partial = functools.partial(function, *args, **kwargs)
|
||||
signature = inspect.signature(partial.func)
|
||||
|
||||
try:
|
||||
signature.bind(*partial.args, **partial.keywords)
|
||||
except TypeError:
|
||||
return curry(copy(partial)) # there must be more arguments to curry
|
||||
else:
|
||||
return partial()
|
||||
|
||||
return inner
|
||||
|
||||
def add(a, b):
|
||||
return a + b
|
||||
|
||||
x = curry(add)(2)
|
||||
print(x)
|
50
school/a-level/Y12 2022-2024/curses wrapper maybe.py
Normal file
|
@ -0,0 +1,50 @@
|
|||
import curses
|
||||
from typing import List, Tuple, Callable, Any
|
||||
|
||||
class Application:
|
||||
def __init__(self):
|
||||
# Initialise curses
|
||||
self.__curses = curses.initscr()
|
||||
self.__curses.keypad(True)
|
||||
|
||||
curses.start_color() # todo: check for colour support
|
||||
curses.cbreak()
|
||||
curses.noecho()
|
||||
|
||||
# Default colours
|
||||
curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLACK) # default
|
||||
curses.init_pair(2, curses.COLOR_BLACK, curses.COLOR_WHITE) # selected
|
||||
|
||||
def selectMenu(self, options: List[Tuple[str, Callable[[], Any]]]):
|
||||
selectedIndex = 0
|
||||
optionCount = len(options)
|
||||
screen = self.__curses
|
||||
|
||||
while True:
|
||||
screen.clear()
|
||||
screen.addstr("Pick an option:\n\n", curses.A_BOLD)
|
||||
|
||||
for i in range(optionCount):
|
||||
colour = curses.color_pair(2) if i == selectedIndex else curses.color_pair(1)
|
||||
screen.addstr(f"{i + 1}. ")
|
||||
screen.addstr(f"{options[i][0]}\n", colour)
|
||||
|
||||
c = screen.getch()
|
||||
|
||||
if c in [curses.KEY_UP, curses.KEY_LEFT]:
|
||||
selectedIndex = (selectedIndex + optionCount - 1) % optionCount
|
||||
elif c in [curses.KEY_DOWN, curses.KEY_RIGHT]:
|
||||
selectedIndex = (selectedIndex + 1) % optionCount
|
||||
elif c == curses.KEY_ENTER or chr(c) in '\n\r':
|
||||
break
|
||||
|
||||
options[selectedIndex][1]()
|
||||
|
||||
def quit(self):
|
||||
curses.nocbreak()
|
||||
self.__curses.keypad(False)
|
||||
curses.echo()
|
||||
curses.endwin()
|
||||
|
||||
app = Application()
|
||||
app.selectMenu([("yes", lambda: (app.quit(),print("i dont think so"))), ("no", lambda: (app.quit(), print("no need to be a cynic")))])
|
3
school/a-level/Y12 2022-2024/golfed full adder.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
exec(bytes('ⱩⱲⱺⱬⱧ㵡湩ⱴ慲杮ⱥ楢Ɱ敬Ɱ慬扭慤砠洬戬⠺㩹氽獩⡴慭⡰慬扭慤琠椺琨Ⱙ∨∰晩砨㴾⤰汥敳ㄢ⤢稫砨洦嬩㨲⥝Ⱙ祛椮獮牥⡴ⰰ⤰潦 湩爠ㄨ〰椩⡬⥹戼ⱝ⥹せ孝㨺ㄭⱝ慬扭慤砠礬⠺㩢洽硡氨稨砨嬩㨲⥝氬稨礨嬩㨲⥝⬩ⰱ㩭椽∨∱截㈬Ⱙ㩰朽砨洬戬Ⱙ㩱朽礨洬戬Ⱙ㩷〽漬㴺⡛幷愨扞Ⱙ㩷⠽♡⥢⡼♷⠨慾戦簩愨縦⥢⤩嬩崰潦⡲ⱡ⥢湩稠灩瀨焬崩漬愮灰湥⡤⥷晩砨〾愩摮礨〾攩獬⡥Ⱙ㩯漽㩛ⴺ崱猬㴺‰晩漨せ㵝〽攩獬ⵥ㈨⨪氨漨⤱Ⱙ㩯漽ㅛ㨺孝㨺ㄭⱝ獛㴺⭳㈨⨪晩漨楛㵝ㄽ攩獬⤰潦⡲⥩湩爠氨漨⤩孝ㄭ⥝ㅛ崰','u16')[2:])
|
||||
|
||||
print(a(5,-6))
|
BIN
school/a-level/Y12 2022-2024/main.playlist
Normal file
10
school/a-level/Y12 2022-2024/q13 sol.py
Normal file
|
@ -0,0 +1,10 @@
|
|||
from random import choice
|
||||
|
||||
def randomDigit(dateOfBirth):
|
||||
return choice("".join(dateOfBirth.split("/")))
|
||||
|
||||
def getUsername(firstName, lastName, dateOfBirth):
|
||||
day, month, year = dateOfBirth.split("/")
|
||||
return f"{firstName[0:3]}{randomDigit(dateOfBirth)}{randomDigit(dateOfBirth)}{lastName[len(lastName)-3:len(lastName)]}{day}{month}{year[len(year)-2:len(year)]}"
|
||||
|
||||
print(getUsername("John", "Smith", "19/03/1989"))
|
|
@ -0,0 +1,215 @@
|
|||
from __future__ import annotations
|
||||
from dataclasses import dataclass
|
||||
from typing import Generic, TypeVar, Tuple, List
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
@dataclass
|
||||
class Node(Generic[T]):
|
||||
data: T
|
||||
left: Node[T] = None
|
||||
right: Node[T] = None
|
||||
|
||||
class BST(Generic[T]):
|
||||
"""Full implementation of a Binary Search Tree"""
|
||||
|
||||
def __init__(self):
|
||||
self.__root: Node[T] = None
|
||||
|
||||
def findMinimum(self) -> Node[T]:
|
||||
"""Find the minimum node in the tree."""
|
||||
current = self.__root
|
||||
|
||||
# Keep traversing left when possible - smaller numbers will always be on the left!
|
||||
while current.left:
|
||||
current = current.left
|
||||
|
||||
return current
|
||||
|
||||
def findMaximum(self) -> Node[T]:
|
||||
"""Find the maximum node in the tree."""
|
||||
current = self.__root
|
||||
|
||||
# Keep traversing right when possible - larger numbers will always be on the right!
|
||||
while current.right:
|
||||
current = current.right
|
||||
|
||||
return current
|
||||
|
||||
def insert(self, data: T) -> BST[T]:
|
||||
"""Inserts a new piece of data into the tree."""
|
||||
node = Node(data)
|
||||
|
||||
if self.__root == None:
|
||||
# Ensure there is a root node in the first place!
|
||||
self.__root = node
|
||||
return self
|
||||
else:
|
||||
current = self.__root
|
||||
parent = None
|
||||
|
||||
while True:
|
||||
parent = current
|
||||
|
||||
# If the currently selected node has a greater value than the newly created node, move left to a smaller valued node
|
||||
if node.data < current.data:
|
||||
current = current.left
|
||||
|
||||
# If there is no node to the left, insert the newly made node!
|
||||
if current == None:
|
||||
parent.left = node
|
||||
return self
|
||||
# Do the same thing but to the right if the selected node has a lesser value than the newly created node.
|
||||
else:
|
||||
current = current.right
|
||||
|
||||
if current == None:
|
||||
parent.right = node
|
||||
return self
|
||||
|
||||
def getNodeWithParent(self, data: T) -> Tuple[Node[T], Node[T]]:
|
||||
"""Find a node containing the data point, and return it alongside its parent. Returns (parent, child)"""
|
||||
parent = None
|
||||
current = self.__root
|
||||
|
||||
# If there is no root node, there is also no parent node
|
||||
if current == None:
|
||||
return (None, None)
|
||||
|
||||
while True:
|
||||
# If the node containing the data has been found, return the (parent, child) pair!
|
||||
if current.data == data:
|
||||
return (parent, current)
|
||||
# If the selected node has a greater value than the one being searched for, traverse left!
|
||||
elif current.data > data:
|
||||
parent = current
|
||||
current = current.left
|
||||
# Otherwise, traverse right!
|
||||
else:
|
||||
parent = current
|
||||
current = current.right
|
||||
|
||||
def remove(self, data: T) -> BST[T]:
|
||||
parent, node = self.getNodeWithParent(data)
|
||||
|
||||
# If there is no node with that data, just do nothing
|
||||
if parent == None and node == None:
|
||||
return self
|
||||
|
||||
# Determine the amount of children that the node has
|
||||
childrenCount = 0
|
||||
|
||||
if node.left and node.right:
|
||||
childrenCount = 2
|
||||
elif (node.left == None) and (node.rightChild == None):
|
||||
childrenCount = 0
|
||||
else:
|
||||
childrenCount = 1
|
||||
|
||||
# Remove the node from the tree
|
||||
if childrenCount == 0:
|
||||
if parent:
|
||||
if parent.right == node:
|
||||
parent.right = None
|
||||
else:
|
||||
parent.left = None
|
||||
else:
|
||||
self.__root = None
|
||||
elif childrenCount == 1:
|
||||
nextNode = None
|
||||
|
||||
if node.left:
|
||||
nextNode = node.left
|
||||
else:
|
||||
nextNode = node.right
|
||||
|
||||
if parent:
|
||||
if parent.left == node:
|
||||
parent.left = nextNode
|
||||
else:
|
||||
parent.right = nextNode
|
||||
else:
|
||||
self.__root = nextNode
|
||||
else:
|
||||
leftmostParent = node
|
||||
leftmostNode = node.right
|
||||
|
||||
while leftmostNode.left:
|
||||
leftmostParent = leftmostNode
|
||||
leftmostNode = leftmostNode.left
|
||||
|
||||
node.data = leftmostNode.data
|
||||
|
||||
if leftmostParent.left == leftmostNode:
|
||||
leftmostParent.left = leftmostNode.rightChild
|
||||
else:
|
||||
leftmostNode.rightChild = leftmostNode.rightChild
|
||||
|
||||
def search(self, data: T) -> T:
|
||||
"""Search through the binary tree. Returns the data value if found, otherwise returns None."""
|
||||
current = self.__root
|
||||
|
||||
while True:
|
||||
# If the final node has been reached, return None
|
||||
if current == None:
|
||||
return None
|
||||
# If the node has been found, return the data!
|
||||
elif current.data == data:
|
||||
return data
|
||||
# If the currently selected node has a higher value than the data being searched for, traverse left
|
||||
elif current.data > data:
|
||||
current = current.left
|
||||
# Otherwise, traverse right
|
||||
else:
|
||||
current = current.right
|
||||
|
||||
def rootNode(self) -> Node[T]:
|
||||
return self.__root
|
||||
|
||||
@staticmethod
|
||||
def inOrder(root: Node[T] | BST[T]) -> List[T]:
|
||||
if isinstance(root, BST):
|
||||
root = root.rootNode()
|
||||
|
||||
out = []
|
||||
|
||||
if root:
|
||||
out = BST.inOrder(root.left)
|
||||
out.append(root.data)
|
||||
out = out + BST.inOrder(root.right)
|
||||
|
||||
return out
|
||||
|
||||
@staticmethod
|
||||
def preOrder(root: Node[T] | BST[T]) -> List[T]:
|
||||
if isinstance(root, BST):
|
||||
root = root.rootNode()
|
||||
|
||||
out = []
|
||||
|
||||
if root:
|
||||
out.append(root.data)
|
||||
out = out + BST.preOrder(root.left)
|
||||
out = out + BST.preOrder(root.right)
|
||||
|
||||
return out
|
||||
|
||||
@staticmethod
|
||||
def postOrder(root: Node[T] | BST[T]) -> List[T]:
|
||||
if isinstance(root, BST):
|
||||
root = root.rootNode()
|
||||
|
||||
out =[]
|
||||
|
||||
if root:
|
||||
out = BST.postOrder(root.left)
|
||||
out = out + BST.postOrder(root.right)
|
||||
out.append(root.data)
|
||||
|
||||
return out
|
||||
|
||||
tree = BST[int]()
|
||||
|
||||
tree.insert(12).insert(14).insert(8).insert(2).insert(8).insert(7).insert(23).insert(19).insert(6).insert(22)
|
||||
|
||||
print(tree.postOrder(tree.rootNode()))
|
|
@ -0,0 +1,171 @@
|
|||
from __future__ import annotations
|
||||
from dataclasses import dataclass
|
||||
from typing import Generic, TypeVar, Tuple
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
@dataclass
|
||||
class Node(Generic[T]):
|
||||
data: T
|
||||
leftChild: Node[T] = None
|
||||
rightChild: Node[T] = None
|
||||
|
||||
class BinarySearchTree(Generic[T]):
|
||||
"""Full implementation of a Binary Search Tree"""
|
||||
|
||||
def __init__(self):
|
||||
self.__rootNode: Node[T] = None
|
||||
|
||||
def findMinimum(self) -> Node[T]:
|
||||
"""Find the minimum node in the tree."""
|
||||
current = self.__rootNode
|
||||
|
||||
# Keep traversing left when possible - smaller numbers will always be on the left!
|
||||
while current.leftChild:
|
||||
current = current.leftChild
|
||||
|
||||
return current
|
||||
|
||||
def findMaximum(self) -> Node[T]:
|
||||
"""Find the maximum node in the tree."""
|
||||
current = self.__rootNode
|
||||
|
||||
# Keep traversing right when possible - larger numbers will always be on the right!
|
||||
while current.rightChild:
|
||||
current = current.rightChild
|
||||
|
||||
return current
|
||||
|
||||
def insert(self, data: T) -> BinarySearchTree[T]:
|
||||
"""Inserts a new piece of data into the tree."""
|
||||
node = Node(data)
|
||||
|
||||
if self.__rootNode == None:
|
||||
# Ensure there is a root node in the first place!
|
||||
self.__rootNode = node
|
||||
return self
|
||||
else:
|
||||
current = self.__rootNode
|
||||
parent = None
|
||||
|
||||
while True:
|
||||
parent = current
|
||||
|
||||
# If the currently selected node has a greater value than the newly created node, move left to a smaller valued node
|
||||
if node.data < current.data:
|
||||
current = current.leftChild
|
||||
|
||||
# If there is no node to the left, insert the newly made node!
|
||||
if current == None:
|
||||
parent.leftChild = node
|
||||
return self
|
||||
# Do the same thing but to the right if the selected node has a lesser value than the newly created node.
|
||||
else:
|
||||
current = current.rightChild
|
||||
|
||||
if current == None:
|
||||
parent.rightChild = node
|
||||
return self
|
||||
|
||||
def getNodeWithParent(self, data: T) -> Tuple[Node[T], Node[T]]:
|
||||
"""Find a node containing the data point, and return it alongside its parent. Returns (parent, child)"""
|
||||
parent = None
|
||||
current = self.__rootNode
|
||||
|
||||
# If there is no root node, there is also no parent node
|
||||
if current == None:
|
||||
return (None, None)
|
||||
|
||||
while True:
|
||||
# If the node containing the data has been found, return the (parent, child) pair!
|
||||
if current.data == data:
|
||||
return (parent, current)
|
||||
# If the selected node has a greater value than the one being searched for, traverse left!
|
||||
elif current.data > data:
|
||||
parent = current
|
||||
current = current.leftChild
|
||||
# Otherwise, traverse right!
|
||||
else:
|
||||
parent = current
|
||||
current = current.rightChild
|
||||
|
||||
def remove(self, data: T) -> BinarySearchTree[T]:
|
||||
parent, node = self.getNodeWithParent(data)
|
||||
|
||||
# If there is no node with that data, just do nothing
|
||||
if parent == None and node == None:
|
||||
return self
|
||||
|
||||
# Determine the amount of children that the node has
|
||||
childrenCount = 0
|
||||
|
||||
if node.leftChild and node.rightChild:
|
||||
childrenCount = 2
|
||||
elif (node.leftChild == None) and (node.rightChild == None):
|
||||
childrenCount = 0
|
||||
else:
|
||||
childrenCount = 1
|
||||
|
||||
# Remove the node from the tree
|
||||
if childrenCount == 0:
|
||||
if parent:
|
||||
if parent.rightChild == node:
|
||||
parent.rightChild = None
|
||||
else:
|
||||
parent.leftChild = None
|
||||
else:
|
||||
self.__rootNode = None
|
||||
elif childrenCount == 1:
|
||||
nextNode = None
|
||||
|
||||
if node.leftChild:
|
||||
nextNode = node.leftChild
|
||||
else:
|
||||
nextNode = node.rightChild
|
||||
|
||||
if parent:
|
||||
if parent.leftChild == node:
|
||||
parent.leftChild = nextNode
|
||||
else:
|
||||
parent.rightChild = nextNode
|
||||
else:
|
||||
self.__rootNode = nextNode
|
||||
else:
|
||||
leftmostParent = node
|
||||
leftmostNode = node.rightChild
|
||||
|
||||
while leftmostNode.leftChild:
|
||||
leftmostParent = leftmostNode
|
||||
leftmostNode = leftmostNode.leftChild
|
||||
|
||||
node.data = leftmostNode.data
|
||||
|
||||
if leftmostParent.leftChild == leftmostNode:
|
||||
leftmostParent.leftChild = leftmostNode.rightChild
|
||||
else:
|
||||
leftmostNode.rightChild = leftmostNode.rightChild
|
||||
|
||||
def search(self, data: T) -> T:
|
||||
"""Search through the binary tree. Returns the data value if found, otherwise returns None."""
|
||||
current = self.__rootNode
|
||||
|
||||
while True:
|
||||
# If the final node has been reached, return None
|
||||
if current == None:
|
||||
return None
|
||||
# If the node has been found, return the data!
|
||||
elif current.data == data:
|
||||
return data
|
||||
# If the currently selected node has a higher value than the data being searched for, traverse left
|
||||
elif current.data > data:
|
||||
current = current.leftChild
|
||||
# Otherwise, traverse right
|
||||
else:
|
||||
current = current.rightChild
|
||||
|
||||
tree = BinarySearchTree[int]()
|
||||
|
||||
for i in range(10):
|
||||
tree.insert(i)
|
||||
found = tree.search(i)
|
||||
print(found)
|
|
@ -0,0 +1,39 @@
|
|||
from __future__ import annotations
|
||||
from dataclasses import dataclass
|
||||
from queue import Queue
|
||||
|
||||
@dataclass
|
||||
class Node:
|
||||
character: str = None
|
||||
frequency: int = None
|
||||
left: Node = None
|
||||
right: Node = None
|
||||
|
||||
def buildTree(message: str):
|
||||
frequencies = { k: v for k, v in sorted([(i, message.count(i)) for i in set(message)], key = lambda x: x[1], reverse = True )}
|
||||
frequencies = list(frequencies.items())
|
||||
|
||||
subtrees = Queue()
|
||||
|
||||
for i in range(len(frequencies)):
|
||||
subtree = Node(None, sum(map(lambda x: x[1], frequencies[i:])))
|
||||
subtree.right = Node(frequencies[i][0], frequencies[i][1])
|
||||
subtrees.put(subtree)
|
||||
|
||||
tree = subtrees.get()
|
||||
current = tree
|
||||
|
||||
for _ in range(subtrees.qsize()):
|
||||
subtree = subtrees.get()
|
||||
current.left = subtree
|
||||
current = current.left
|
||||
|
||||
return tree
|
||||
|
||||
def encode(message: str):
|
||||
tree = buildTree(message)
|
||||
print(tree)
|
||||
frequencies = { i: message.count(i) for i in set(message) }
|
||||
frequencies = { k: v for k, v in sorted(frequencies.items(), key = lambda x: x[1] )}
|
||||
|
||||
print(encode("Hello World"))
|
|
@ -192,7 +192,7 @@ class LinkedList(Generic[T]):
|
|||
|
||||
return out + "]"
|
||||
|
||||
names = LinkedList[str](["Harold", "Janet"])
|
||||
names = LinkedList[str](["John Doe", "Jane Doe"])
|
||||
names.extend(["Alfred", "Sophie"]).insert(1, "John Snow")
|
||||
names.remove("Alfred")
|
||||
names += "Woody"
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
# node -> (character, frequency, left, right)
|
||||
import queue,operator;b=lambda m:(f:={i:m.count(i)for i in set(m)},f:={k:v for(k,v)in sorted(f.items(),key=lambda x:x[1],reverse=True)},f:=list(f.items()),s:=queue.Queue(),[s.put([None,sum(map(lambda x:x[1],f[i:])),None,[f[i][0],f[i][1],None,None]])for i in range(len(f))],t:=s.get(),c:=t,[(operator.setitem(c,2,s.get()),c:=c[2])for _ in range(s.qsize())],t)[8]
|
||||
print(b("Hello World"))
|
|
@ -27,12 +27,13 @@ class CashRegister:
|
|||
|
||||
for coinType in self.__coins:
|
||||
# Set an initial value for the change
|
||||
if changeValue - coinType > 0:
|
||||
if changeValue - coinType >= 0:
|
||||
change[coinType] = 0
|
||||
|
||||
# Keep attempting to provide change from the tray until we overpay
|
||||
while changeValue - coinType > 0:
|
||||
while changeValue - coinType >= 0:
|
||||
changeValue -= coinType
|
||||
changeValue = round(changeValue, 2)
|
||||
self.__coins[coinType] -= 1
|
||||
change[coinType] += 1
|
||||
|
||||
|
|
30
school/a-level/Y13 2021-2023/LMC/LMC Higher or Lower.txt
Normal file
|
@ -0,0 +1,30 @@
|
|||
INP
|
||||
STA toGuess
|
||||
|
||||
loop INP
|
||||
STA guess
|
||||
|
||||
LDA toGuess
|
||||
SUB guess
|
||||
|
||||
BRZ equal
|
||||
BRP small
|
||||
BRA big
|
||||
|
||||
small LDA higher
|
||||
OUT
|
||||
BRA loop
|
||||
|
||||
big LDA lower
|
||||
OUT
|
||||
BRA loop
|
||||
|
||||
equal LDA win
|
||||
OUT
|
||||
HLT
|
||||
|
||||
toGuess DAT
|
||||
guess DAT
|
||||
higher DAT 1
|
||||
lower DAT 0
|
||||
win DAT 9
|
22
school/a-level/Y13 2021-2023/LMC/LMC Triangular numbers.txt
Normal file
|
@ -0,0 +1,22 @@
|
|||
INP
|
||||
STA quant
|
||||
|
||||
loop LDA number
|
||||
ADD counter
|
||||
OUT
|
||||
STA number
|
||||
|
||||
LDA counter
|
||||
ADD one
|
||||
STA counter
|
||||
|
||||
LDA quant
|
||||
SUB counter
|
||||
|
||||
BRP loop
|
||||
HLT
|
||||
|
||||
quant DAT
|
||||
counter DAT 1
|
||||
number DAT 0
|
||||
one DAT 1
|
|
@ -1,4 +1,4 @@
|
|||
# Operating Systems
|
||||
# Operating Systems
|
||||
|
||||
## The Kernel
|
||||
|
||||
|
@ -34,8 +34,8 @@ The process manager is a part of the kernel. Jobs can consist of many processes
|
|||
|
||||
### Process States
|
||||
|
||||
- HOLD
|
||||
- READY
|
||||
- RUNNING
|
||||
- WAIT
|
||||
- FINISHED/FAIL
|
||||
- HOLD
|
||||
- READY
|
||||
- RUNNING
|
||||
- WAIT
|
||||
- FINISHED/FAIL
|
||||
|
|