diff --git a/.gitignore b/.gitignore index 21a4059..200a937 100644 --- a/.gitignore +++ b/.gitignore @@ -21,5 +21,7 @@ _deps bin ignored -maths/**/*.pdf -maths/**/*.synctex.gz +*.docx +*.pptx +*.doc +*.pdf diff --git a/school/a-level/Y12 2022-2024/GUI Programming/Cat or dog demo.py b/school/a-level/Y12 2022-2024/GUI Programming/Cat or dog demo.py new file mode 100644 index 0000000..b856c30 --- /dev/null +++ b/school/a-level/Y12 2022-2024/GUI Programming/Cat or dog demo.py @@ -0,0 +1,73 @@ +from tkinter import * + +def addCat(event): + global cat + cat = cat + 1 + updateDisplay() + +def addDog(event): + global dog + dog += 1 + updateDisplay() + +def resetCat(event, update = True): + global cat + cat = 0 + + if update: updateDisplay() + +def resetDog(event, update = True): + global dog + dog = 0 + + if update: updateDisplay() + +def reset(event): + resetCat(False) + resetDog(False) + + updateDisplay() + +def updateDisplay(): + global cat, dog + + cats = f'{cat} ' + ('cat' if cat == 1 else 'cats') + dogs = f'{dog} ' + ('dog' if dog == 1 else 'dogs') + isAre = 'is' if cat == 1 else 'are' + + outputLabel.configure(text = f'There {isAre} {cats}, and {dogs}.') + +cat = 0 +dog = 0 + +window = Tk("Cat or Dog") + +topFrame = Frame(window) +topFrame.pack() + +bottomFrame = Frame(window) +bottomFrame.pack(side = BOTTOM, pady = 10) + +welcome = Label(topFrame, fg="red", font=("Helvetica", 16), text = "Welcome to Cat or Dog Selector") +welcome.pack() + +outputLabel = Label(topFrame, text = "0") +outputLabel.pack() + +catButton = Button(bottomFrame, text='Cat') +catButton.bind("<1>", addCat) +catButton.bind("<3>", resetCat) +catButton.grid(row = 0, column = 0, padx = 10) + +dogButton = Button(bottomFrame, text='Dog') +dogButton.bind("<1>", addDog) +dogButton.bind("<3>", resetDog) +dogButton.grid(row = 0, column = 2, padx = 20) + +resetButton = Button(bottomFrame, text='Reset', bg='#000000', fg='#ffffff') +resetButton.bind("<1>", reset) +resetButton.grid(row = 0, column = 3) + +updateDisplay() + +window.mainloop() diff --git a/school/a-level/Y12 2022-2024/GUI Programming/GUI Dice.py b/school/a-level/Y12 2022-2024/GUI Programming/GUI Dice.py new file mode 100644 index 0000000..612c90c --- /dev/null +++ b/school/a-level/Y12 2022-2024/GUI Programming/GUI Dice.py @@ -0,0 +1,81 @@ +import tkinter as tk +from tkinter.font import Font, BOLD +from random import randint + +class Window(tk.Tk): + def __init__(self): + tk.Tk.__init__(self) + + self.sides = 6 + self.sum = 0 + self.geometry('500x500') + self.updateTitle() + + self.grid_rowconfigure(0, weight = 1) + self.grid_columnconfigure(0, weight = 1) + self.option_add('*font', Font(self, size = 12)) + self.tk_setPalette(background = 'white') + + container = tk.Frame() + container.grid(row = 0, column = 0, sticky = '') + + sideController = tk.Frame(master=container) + sideController.grid(row = 3, column = 0) + + self.outputLabel = tk.Label(container, font=Font(self, size = 25, weight = BOLD)) + self.sumLabel = tk.Label(container, font=Font(self, size = 20)) + self.sidesLabel = tk.Label(sideController) + + rollButton = tk.Button(container, text='Roll!') + decrementSides = tk.Button(sideController, text = '←', borderwidth = 0) + incrementSides = tk.Button(sideController, text = '→', borderwidth = 0) + + self.outputLabel.grid(row = 0, column = 0, pady=(0, 20)) + self.updateOutput() + + self.sumLabel.grid(row = 1, column = 0, pady=(0, 50)) + self.updateSum() + + rollButton.bind("<1>", self.roll) + rollButton.grid(row = 2, column = 0, pady=(0, 30)) + + decrementSides.bind("<1>", self.decrementSides) + decrementSides.grid(row = 0, column = 0) + + self.sidesLabel.grid(row = 0, column = 1) + self.updateSides() + + incrementSides.bind("<1>", self.incrementSides) + incrementSides.grid(row = 0, column = 2) + + def updateOutput(self, roll = 'N/A'): + self.outputLabel.configure(text = roll) + + def updateSum(self): + self.sumLabel.configure(text = f'Sum: {self.sum}') + + def roll(self, _): + number = randint(1, self.sides) + self.updateOutput(number) + + def updateTitle(self): + self.wm_title(f'{self.sides}-sided Dice') + + def updateSides(self): + self.sidesLabel.configure(text = self.sides) + self.updateTitle() + + def decrementSides(self, _): + self.sides -= 1 + + if self.sides < 2: + self.sides = 2 + + self.updateSides() + + def incrementSides(self, _): + self.sides += 1 + + self.updateSides() + +Window().mainloop() \ No newline at end of file diff --git a/school/a-level/Y12 2022-2024/Homework/11-01-23 - Code and test a program/primeList.py b/school/a-level/Y12 2022-2024/Homework/11-01-23 - Code and test a program/primeList/__init__.py similarity index 62% rename from school/a-level/Y12 2022-2024/Homework/11-01-23 - Code and test a program/primeList.py rename to school/a-level/Y12 2022-2024/Homework/11-01-23 - Code and test a program/primeList/__init__.py index f6ee1a4..85353c2 100644 --- a/school/a-level/Y12 2022-2024/Homework/11-01-23 - Code and test a program/primeList.py +++ b/school/a-level/Y12 2022-2024/Homework/11-01-23 - Code and test a program/primeList/__init__.py @@ -1,5 +1,6 @@ from time import sleep +# :eyes: def clearTerminal(): print('\n' * 25) @@ -26,25 +27,27 @@ def findPrimes(upperBound: int): return primes -possiblePrimes = findPrimes(1_000_000) +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 askForPrime(): - global possiblePrimes, 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 possiblePrimes: + elif value not in POSSIBLE_PRIMES: raise ValueError('That is not a prime number! Please try again.') - elif value in inputtedPrimes: + elif value in primeList: raise ValueError('You have already inputted that prime number! Please enter a different one.') - return inputtedPrimes.append(value) + # 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: @@ -69,14 +72,19 @@ What would you like to do? break except ValueError: - print("Please enter a valid integer that is at most six digits long.") + print("Please enter a valid option from the list (1-3)") clearTerminal() if choice == 1: - askForPrime() + inputtedPrimes = getPrime(inputtedPrimes) elif choice == 2: - print(', '.join(map(lambda x: str(x), inputtedPrimes))) + # 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() diff --git a/school/a-level/Y12 2022-2024/Homework/11-01-23 - Code and test a program/test.py b/school/a-level/Y12 2022-2024/Homework/11-01-23 - Code and test a program/test.py new file mode 100644 index 0000000..292103c --- /dev/null +++ b/school/a-level/Y12 2022-2024/Homework/11-01-23 - Code and test a program/test.py @@ -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() diff --git a/school/a-level/Y12 2022-2024/Homework/15-03-23 Code and test/Diagonal Difference OOP.py b/school/a-level/Y12 2022-2024/Homework/15-03-23 Code and test/Diagonal Difference OOP.py new file mode 100644 index 0000000..28611ca --- /dev/null +++ b/school/a-level/Y12 2022-2024/Homework/15-03-23 Code and test/Diagonal Difference OOP.py @@ -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) diff --git a/school/a-level/Y12 2022-2024/Homework/15-03-23 Code and test/Diagonal Difference.py b/school/a-level/Y12 2022-2024/Homework/15-03-23 Code and test/Diagonal Difference.py new file mode 100644 index 0000000..dce65f4 --- /dev/null +++ b/school/a-level/Y12 2022-2024/Homework/15-03-23 Code and test/Diagonal Difference.py @@ -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}""") diff --git a/school/a-level/Y12 2022-2024/Homework/30-01-23 - Logic Gates/Worksheet Program.py b/school/a-level/Y12 2022-2024/Homework/30-01-23 - Logic Gates/Worksheet Program.py new file mode 100644 index 0000000..c1fb938 --- /dev/null +++ b/school/a-level/Y12 2022-2024/Homework/30-01-23 - Logic Gates/Worksheet Program.py @@ -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) diff --git a/school/a-level/Y12 2022-2024/Logic/Custom Expressions.py b/school/a-level/Y12 2022-2024/Logic/Custom Expressions.py new file mode 100644 index 0000000..1f51a32 --- /dev/null +++ b/school/a-level/Y12 2022-2024/Logic/Custom Expressions.py @@ -0,0 +1,89 @@ +from enum import Enum +from typing import Tuple, List + +class Gate(Enum): + AND = '^' + OR = '∨' + XOR = '⊕' + NAND = '↑' + NOR = '↓' + XNOR = '⊙' + +class Symbol: + def __init__(self, character): + self.char = character + + def __str__(self): + return self.char + +class Expression: + def __init__(self, symbol): + self.tree: List[Tuple[Gate, Expression]] = [] + self.initialSymbol = symbol + + def AND(self, symbol): + self.tree.append((Gate.AND, symbol)) + return self + + def OR(self, symbol): + self.tree.append((Gate.OR, symbol)) + return self + + def XOR(self, symbol): + self.tree.append((Gate.XOR, symbol)) + return self + + def NAND(self, symbol): + self.tree.append((Gate.NAND, symbol)) + return self + + def NOR(self, symbol): + self.tree.append((Gate.NOR, symbol)) + return self + + def XNOR(self, symbol): + self.tree.append((Gate.XNOR, symbol)) + return self + + @staticmethod + def treeSymbols(tree): + symbols = set([]) + + for _, value in tree: + if isinstance(value, Expression): + if str(value.initialSymbol) not in symbols: + symbols.add(str(value.initialSymbol)) + symbols.update(Expression.treeSymbols(value.tree)) + elif str(value) not in symbols: + symbols.add(str(value)) + + return symbols + + + def compile(self): + # Count symbols + symbols = Expression.treeSymbols(self.tree) + + def run(*values): + for gate, value in self._tree: + + def __str__(self): + out = str(self.initialSymbol) + + for gate, value in self.tree: + if isinstance(value, Expression) and len(value.tree) > 0: + expression = f'({value})' + else: + expression = str(value) + + out += f' {gate.value} {expression}' + + return out + +A = Symbol('A') +B = Symbol('B') +C = Symbol('C') +D = Symbol('D') +# A.AND(B).execute() + +Expression(A).AND(Expression(B).AND(A)).OR(Expression(C).XOR(Expression(A).OR(D))).compile() \ No newline at end of file diff --git a/school/a-level/Y12 2022-2024/Logic/Framework.py b/school/a-level/Y12 2022-2024/Logic/Framework.py new file mode 100644 index 0000000..191f070 --- /dev/null +++ b/school/a-level/Y12 2022-2024/Logic/Framework.py @@ -0,0 +1,30 @@ +from __future__ import annotations +from enum import Enum + +class Gate(Enum): + AND = '^' + OR = '∨' + XOR = '⊕' + NAND = '↑' + NOR = '↓' + XNOR = '⊙' + +class Symbol(): + def __init__(self, symbol, initialValue): + self._symbol = symbol + self._value = initialValue + self._tree = [] + + def setValue(self, newValue): + self._value = newValue + return self + + def AND(self, symbol: Symbol): + self._tree.append((Gate.AND, symbol)) + return self + + def compute(): + + + def __str__(self): + return self._symbol \ No newline at end of file diff --git a/school/a-level/Y12 2022-2024/Logic/Full Adder.py b/school/a-level/Y12 2022-2024/Logic/Full Adder.py new file mode 100644 index 0000000..3325292 --- /dev/null +++ b/school/a-level/Y12 2022-2024/Logic/Full Adder.py @@ -0,0 +1,84 @@ +from typing import List + +def getInt(): + while True: + try: + value = int(input("Please enter a natural number: ")) + + return value + except ValueError: + print("Please enter a valid natural number.") + +def getBin(x: int) -> List[int]: + return list(map(lambda x: int(x), bin(abs(x))[2:])) + +def twosComplementToInt(bits: List[int]) -> int: + sum = 0 if bits[0] == 0 else -(2 ** (len(bits) - 1)) + del bits[0] + bits.reverse() + + for i in range(len(bits)): + if bits[i] == 1: + sum += 2 ** i + + return sum + +def add(x: int, y: int) -> int: + bits = max(len(bin(x)[2:]), len(bin(y)[2:])) + 1 + mask = int("1" * bits, 2) + xBin = list(map(lambda x: int(x), ("0" if x >= 0 else "1") + bin(x & mask)[2:])) + yBin = list(map(lambda x: int(x), ("0" if y >= 0 else "1") + bin(y & mask)[2:])) + + print(xBin, yBin) + + # Pad the shortner binary number + while len(xBin) < bits: + xBin.insert(0, 0) + while len(yBin) < bits: + yBin.insert(0, 0) + + # We add from right to left + xBin.reverse(); yBin.reverse() + + # Iteratively apply the full adder + cout = 0 + outBin = [] + + for a, b in zip(xBin, yBin): + sum = cout ^ (a ^ b) + cout = (a & b) | (cout & ((~a & b) | (a & ~b))) + outBin.append(sum) + + if x > 0 and y > 0: outBin.append(cout) + + # We read from left to right + outBin.reverse() + print(outBin) + + return twosComplementToInt(outBin) + +def subtract(x: int, y: int) -> int: + return add(x, -y) + +def multiply(x: int, y: int) -> int: + if x == 1: + return y + elif x == -1: + return -y + elif y == 1: + return x + elif y == -1: + return -x + + sgn = -1 if (x > 0 and y < 0) or (y > 0 and x < 0) else 1 + product = 0 + + for num in [abs(x) for _ in range(abs(y))]: + product = add(product, num) + + return multiply(sgn, product) + +x, y = getInt(), getInt() + +# Compare the results +print(x + y, add(x, y)) diff --git a/school/a-level/Y12 2022-2024/Logic/Karnaugh Maps/K-Map Solver.py b/school/a-level/Y12 2022-2024/Logic/Karnaugh Maps/K-Map Solver.py new file mode 100644 index 0000000..b4f2f30 --- /dev/null +++ b/school/a-level/Y12 2022-2024/Logic/Karnaugh Maps/K-Map Solver.py @@ -0,0 +1,51 @@ +from dataclasses import dataclass +from typing import Dict, List + +class VariableMismatchError(Exception): + pass + +@dataclass(frozen=True) +class Node: + """Represents a row of a truth table""" + variables: Dict[str, bool] + value: bool + +class KMap: + def __init__(self, variables: List[str]): + self.__nodes: List[Node] = [] + self.__variables = variables + + def addNode(self, node: Node): + variableCount = len(self.__variables) + nodeVariableCount = len(node.variables.keys()) + + if variableCount < nodeVariableCount: + raise VariableMismatchError(f"You tried assigning a node with {nodeVariableCount} variables to a K-Map that supports {variableCount} variables.") + + for variable, _ in node.variables: + if variable not in self.__variables: + raise VariableMismatchError(f"{variable} is not a variable in this K-Map!") + + self.__nodes.append(node) + return self + +map = [ + Node({ + "A": False, + "B": False + }, True), + Node({ + "A": True, + "B": False + }, True), + Node({ + "A": False, + "B": True + }, False), + Node({ + "A": True, + "B": True + }, True), +] + +print(map) \ No newline at end of file diff --git a/school/a-level/Y12 2022-2024/Logic/Logic Gates intro.pptx b/school/a-level/Y12 2022-2024/Logic/Logic Gates intro.pptx deleted file mode 100644 index b6a8877..0000000 Binary files a/school/a-level/Y12 2022-2024/Logic/Logic Gates intro.pptx and /dev/null differ diff --git a/school/a-level/Y12 2022-2024/Logic/Truth Table.py b/school/a-level/Y12 2022-2024/Logic/Truth Table.py index 1dd9bfc..a97674e 100644 --- a/school/a-level/Y12 2022-2024/Logic/Truth Table.py +++ b/school/a-level/Y12 2022-2024/Logic/Truth Table.py @@ -82,10 +82,13 @@ print('-' * (len(header) - 15 - (8 * inputQuantity))) # Find the cartesian product to generate pairs # https://en.wikipedia.org/wiki/Cartesian_product sets = [(0, 1)] * inputQuantity +print(sets) pairs = [[]] for set in sets: pairs = [x+[y] for x in pairs for y in set] +print(pairs) + for i, pair in enumerate(pairs): print(formatString.format(red(i + 1), *pair, bold(applyGate(*pair)))) \ No newline at end of file diff --git a/school/a-level/Y12 2022-2024/Storage/Pickle Shop.py b/school/a-level/Y12 2022-2024/Storage/Pickle Shop.py new file mode 100644 index 0000000..d60ee8e --- /dev/null +++ b/school/a-level/Y12 2022-2024/Storage/Pickle Shop.py @@ -0,0 +1,196 @@ +from __future__ import annotations +from pickle import load, dump +from dataclasses import dataclass +from sys import path as sysPath +from os.path import join as pathJoin, exists as pathExists +from typing import List, Callable, TypeVar +from time import sleep + +class ExistsError(Exception): + pass + +@dataclass +class Product: + name: str + price: float + formatString = " {:^30} | {:^10} " + currency = "£" + + @staticmethod + def format(product: Product, currency: str) -> str: + return Product.formatString.format(product.name, f"{currency}{product.price:.2f}") + +class Products: + def __init__(self, friendlyName: str, currency: str = "£"): + self.__products: List[Product] = [] + self.__file = self.getPath(friendlyName) + self.__currency = currency + + @staticmethod + def getPath(friendlyName: str) -> str: + return pathJoin(sysPath[0], f"{friendlyName}.products") + + @staticmethod + def From(friendlyName: str, currency: str = "£"): + path = Products.getPath(friendlyName) + + if not pathExists(path): + return Products(friendlyName, currency) + + with open(path, "rb") as f: + data = load(f) + products = Products(friendlyName, data["currency"]) + + for product in data["products"]: + products.add(product) + + return products + + def __save(self) -> Products: + """Save all of the products to a file.""" + with open(self.__file, "wb") as f: + dump({ + "products": self.__products, + "currency": self.__currency + }, f) + + return self + + def exists(self, name: str) -> Product: + """Checks if a product with that name already exists""" + for product in self.__products: + if product.name.lower() == name.lower(): + return product + + return None + + def setCurrency(self, currency: str) -> Products: + self.__currency = currency + return self.__save() + + def add(self, product: Product) -> Products: + """Add a product to the list.""" + if self.exists(product.name): + raise ExistsError(f"A product called {product.name} already exists!") + + self.__products.append(product) + return self.__save() + + def remove(self, name: str) -> Products: + """Remove a product from the list""" + for i, product in enumerate(self.__products): + if product.name.lower() == name.lower(): + del self.__products[i] + + return self.__save() + + def clear(self) -> Products: + """Clear the list of products""" + self.__products = [] + return self.__save() + + def __str__(self) -> str: + header = Product.formatString.format("Name", "Price") + divider = "-" * len(header) + out = f"{header}\n{divider}" + + for product in self.__products: + out += f"\n{Product.format(product, self.__currency)}" + + return out + + def __add__(self, product: Product) -> Products: + return self.add(product) + + def __getitem__(self, index: int) -> Product: + return self.__products[index] + + def __setitem__(self, index: int, product: Product): + self.__products[index] = product + self.__save() + + def __len__(self) -> int: + return len(self.__products) + +T = TypeVar("T") + +def getInput(caster: Callable[[str], T], prompt: str, error: str, validator: Callable[[T], bool] = None) -> T: + while True: + try: + value = caster(input(prompt)) + + if validator and validator(value): + raise ValueError + + return value + except ValueError: + print(error) + +def getProduct() -> Product: + name = input("Please enter a name for the product: ") + price = getInput(float, "Please enter a price for the product: ", "Please ensure that your input was a valid float!", lambda x: x <= 0) + + return Product(name, price) + +def clear(): + print('\n' * 50) + +if __name__ == "__main__": + # Load a product list + name = input("Please enter the name of a list of products: ") + products = Products.From(name.lower()) + + if len(products) == 0: + print(f"Created new products list \"{name}\"") + else: + print(f"Loaded pre-existing product list \"{name}\"") + + # Main menu loop + while True: + clear() + print("""1) View the list of products +2) Add a product +3) Remove a product +4) Update currency +5) Clear the list of products +6) Exit +""") + + choice = getInput(int, "What would you like to do? (1-5): ", "Please choose a valid option.", lambda x: x < 1 or x > 6) + print() + + if choice == 1: + print(products) + elif choice == 2: + while True: + try: + product = getProduct() + products.add(product) + break + except ExistsError: + print("Please make sure the product's name is unique!") + + print(f"Added product {product}") + elif choice == 3: + while True: + name = input("Please enter the name of a product to remove: ") + product = products.exists(name) + + if product: + break + + print("Make sure you enter the name of a valid product!") + + products.remove(name) + print(f"Removed product {product}") + elif choice == 4: + currency = input("Please enter a new currency symbol: ") + products.setCurrency(currency) + print(f"Currency symbol updated to {currency}") + elif choice == 5: + products.clear() + print("Cleared list of products.") + elif choice == 6: + exit() + + sleep(1) diff --git a/school/a-level/Y12 2022-2024/Storage/main.products b/school/a-level/Y12 2022-2024/Storage/main.products new file mode 100644 index 0000000..b370f00 Binary files /dev/null and b/school/a-level/Y12 2022-2024/Storage/main.products differ diff --git a/school/a-level/Y13 2021-2023/Advanced Data Structures/Hash Table.py b/school/a-level/Y13 2021-2023/Advanced Data Structures/Hash Table.py new file mode 100644 index 0000000..59b1bb3 --- /dev/null +++ b/school/a-level/Y13 2021-2023/Advanced Data Structures/Hash Table.py @@ -0,0 +1,92 @@ +from typing import Generic, TypeVar +from math import ceil + +K, V = TypeVar('K'), TypeVar('V') + +class HashItem(Generic[K, V]): + def __init__(self, key: K, value: V): + self.key = key + self.value = value + +class HashTable(Generic[K, V]): + def __init__(self): + self.__size = 256 + self.__count = 0 + self.__slots: list[HashItem[K, V]] = [None for _ in range(self.__size)] + + def __hash(self, key: K): + multiplier = 1 + hashValue = 0 + + for character in key: + hashValue += multiplier * ord(character) + multiplier += 1 + + return hashValue % self.__size + + def __filledSlots(self): + return list(filter(lambda x: x is not None, self.__slots)) + + def __load(self): + return len(list(self.__filledSlots())) / self.__size + + def __grow(self, newSize: int): + self.__slots += [None for _ in range(newSize - self.__size)] + self.__size = newSize + + def put(self, key: K, value: V): + item = HashItem(key, value) + hashValue = self.__hash(key) + + while self.__slots[hashValue] != None: + if self.__slots[hashValue].key == key: + break + + hashValue += 1 + hashValue %= self.__size + + if self.__slots[hashValue] == None: + self.__count += 1 + + # If the hash table is nearing its maximum load, increase the size of the hash table by 10% + if self.__load() >= 0.75: + self.__grow(ceil(self.__size * 1.1)) + + self.__slots[hashValue] = item + + def get(self, key: K): + hashValue = self.__hash(key) + + for slot in self.__filledSlots(): + if hashValue == self.__hash(slot.key): + return slot.value + + return None + + def __setitem__(self, key: K, value: V): + self.put(key, value) + + def __getitem__(self, key: K): + return self.get(key) + + def __str__(self): + template = " {: ^10} | {: ^10}" + out = template.format("Key", "Value") + out += "\n" + ("-" * (len(out) // 2)) + "|" + ("-" * (len(out) // 2)) + "\n" + + slots = self.__filledSlots() + + for i, slot in enumerate(slots): + out += template.format(slot.key, slot.value) + + if i + 1 != len(slots): + out += "\n" + + return out + + def __len__(self): + return len(self.__slots) + +ages = HashTable[str, int]() +ages["John Doe"] = 16 +print(ages) diff --git a/school/a-level/Y13 2021-2023/Advanced Data Structures/Linked List.py b/school/a-level/Y13 2021-2023/Advanced Data Structures/Linked List.py new file mode 100644 index 0000000..bf5a84f --- /dev/null +++ b/school/a-level/Y13 2021-2023/Advanced Data Structures/Linked List.py @@ -0,0 +1,201 @@ +from __future__ import annotations +from dataclasses import dataclass +from typing import Generic, TypeVar, Iterable, Union, List + +T = TypeVar("T") + +@dataclass +class Node(Generic[T]): + data: T + next: Node[T] = None + + def __repr__(self) -> str: + return str(self.data) + +class LinkedList(Generic[T]): + def __init__(self, initialValues: Iterable[T]): + self.head: Node[T] = None + self.tail: Node[T] = None + + self.extend(initialValues) + + def __iter__(self) -> Iterable[T]: + """Traverse the linked list.""" + node = self.head + + while node is not None: + yield node + node = node.next + + def __getitem__(self, index: int) -> Node[T]: + """Get an item by its index.""" + if index > len(self) - 1: + raise IndexError("Out of bounds") + + for i, node in enumerate(self): + if i == index: + return node + + def __setitem__(self, index: int, data: T): + """Set an item by its index.""" + if index > len(self) - 1: + raise IndexError("Out of bounds") + + for i in range(len(self)): + if i == index: + self[i].data = data + break + + def __delitem__(self, index: int): + """Delete an item using its index.""" + if index > len(self) - 1: + raise IndexError("Out of bounds") + + for i in range(len(self)): + if i == index: + for j in range(1, len(self) - i): + self[i + j - 1] = self[i + j].data + + self[len(self) - 2].next = None + break + + def __add__(self, data: Union[T, List[T], LinkedList[T]]) -> LinkedList[T]: + copy = self.copy() + + if isinstance(data, LinkedList) or type(data) == list: + return copy.extend(iter(data) if type(data) == list else iter(map(lambda x: x.data, data))) + else: + return copy.append(data) + + def __len__(self) -> int: + """Get the length of the linked list.""" + count = 0 + + for _ in self: + count += 1 + + return count + + def append(self, value: T) -> LinkedList[T]: + """Append data to the end of the linked list.""" + node = Node(value) + + if self.head is None: + self.head = node + return self + + for currentNode in self: + pass + + currentNode.next = node + + return self + + def clear(self) -> LinkedList[T]: + """Clear the linked list.""" + self.head = None + self.tail = None + return self + + def count(self, value: T) -> int: + """Count the number of occurrences of a value.""" + count = 0 + + for node in self: + if node.data == value: + count += 1 + + return count + + def extend(self, iterable: Iterable[T]) -> LinkedList[T]: + """Extend the list from an iterable.""" + for value in iterable: + self.append(value) + + return self + + def index(self, value: T) -> int: + """Finds the first index of a value. Returns -1 if the value is not in the list.""" + for i, node in enumerate(self): + if node.data == value: + return i + + return -1 + + def insert(self, index: int, value: T) -> LinkedList[T]: + """Inserts a value before an index.""" + if index > len(self) - 1: + raise IndexError("Out of bounds") + + for i in range(len(self)): + if i == index: + container = value + + for j in range(len(self)): + try: + temp = self[i + j].data + self[i + j].data = container + container = temp + except IndexError: + self.append(container) + + break + + return self + + def pop(self) -> Node[T]: + """Pop and return the final element of the list.""" + for node in self: + pass + + del self[len(self) - 1] + + return node + + def remove(self, value: T) -> LinkedList[T]: + for i, node in enumerate(self): + if node.data == value: + del self[i] + return self + + raise ValueError("Value not found") + + def copy(self) -> LinkedList[T]: + """Returns a copy of the list.""" + return LinkedList[T](iter(map(lambda x: x.data, self))) + + def sort(self) -> LinkedList[T]: + n = len(self) + swapped = False + + for i in range(n - 1): + for j in range(0, n - i - 1): + if self[j].data > self[j + 1].data: + swapped = True + self[j], self[j + 1] = self[j + 1].data, self[j].data + + if not swapped: + break + + return self + + def __repr__(self) -> str: + out = "[" + + for i, node in enumerate(self): + value = node.data + + out += f'"{value}"' if type(value) == str else value + + if i != len(self) - 1: + out += ", " + + return out + "]" + +names = LinkedList[str](["Harold", "Janet"]) +names.extend(["Alfred", "Sophie"]).insert(1, "John Snow") +names.remove("Alfred") +names += "Woody" +names.sort() + +print(names) diff --git a/school/a-level/Y13 2021-2023/LMC/LMC Higher or Lower.txt b/school/a-level/Y13 2021-2023/LMC/Higher or Lower.txt similarity index 100% rename from school/a-level/Y13 2021-2023/LMC/LMC Higher or Lower.txt rename to school/a-level/Y13 2021-2023/LMC/Higher or Lower.txt diff --git a/school/a-level/Y13 2021-2023/LMC/LMC Triangular numbers.txt b/school/a-level/Y13 2021-2023/LMC/Triangular Numbers.txt similarity index 100% rename from school/a-level/Y13 2021-2023/LMC/LMC Triangular numbers.txt rename to school/a-level/Y13 2021-2023/LMC/Triangular Numbers.txt diff --git a/school/a-level/Y13 2021-2023/Operating Systems/Operating Systems.doc b/school/a-level/Y13 2021-2023/Operating Systems/Operating Systems.doc deleted file mode 100644 index bb27462..0000000 Binary files a/school/a-level/Y13 2021-2023/Operating Systems/Operating Systems.doc and /dev/null differ diff --git a/school/a-level/Y13 2021-2023/Term 3 Revision Guide.pptx b/school/a-level/Y13 2021-2023/Term 3 Revision Guide.pptx deleted file mode 100644 index 40aebfc..0000000 Binary files a/school/a-level/Y13 2021-2023/Term 3 Revision Guide.pptx and /dev/null differ diff --git a/school/gcse/year 10/mark analyser/marks.csv b/school/gcse/year 10/mark analyser/marks.csv deleted file mode 100644 index 1953b4f..0000000 --- a/school/gcse/year 10/mark analyser/marks.csv +++ /dev/null @@ -1,2 +0,0 @@ -17,22,8,31,30,29,16,17,23,32 -23,6,25,44,19,21,8,18,29,41 \ No newline at end of file