├── .gitignore ├── LICENSE ├── PyPyNum.png ├── README.md └── pypynum ├── PyPyNum.png ├── README.md ├── __init__.py ├── arrays.py ├── chars.py ├── ciphers.py ├── consts.py ├── crandom.py ├── dataproc.py ├── dists.py ├── equations.py ├── fft.py ├── files.py ├── geoms.py ├── graphs.py ├── groups.py ├── hypcmpnms.py ├── images.py ├── interp.py ├── kernels.py ├── logics.py ├── maths.py ├── matrices.py ├── multiprec.py ├── networks.py ├── numbers.py ├── plotting.py ├── polys.py ├── pprinters.py ├── random.py ├── regs.py ├── seqs.py ├── special.py ├── stattest.py ├── symbols.py ├── tensors.py ├── test.py ├── this.py ├── tools.py ├── trees.py ├── types.py ├── ufuncs.py ├── utils.py ├── vectors.py └── zh_cn.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 98 | __pypackages__/ 99 | 100 | # Celery stuff 101 | celerybeat-schedule 102 | celerybeat.pid 103 | 104 | # SageMath parsed files 105 | *.sage.py 106 | 107 | # Environments 108 | .env 109 | .venv 110 | env/ 111 | venv/ 112 | ENV/ 113 | env.bak/ 114 | venv.bak/ 115 | 116 | # Spyder project settings 117 | .spyderproject 118 | .spyproject 119 | 120 | # Rope project settings 121 | .ropeproject 122 | 123 | # mkdocs documentation 124 | /site 125 | 126 | # mypy 127 | .mypy_cache/ 128 | .dmypy.json 129 | dmypy.json 130 | 131 | # Pyre type checker 132 | .pyre/ 133 | 134 | # pytype static type analyzer 135 | .pytype/ 136 | 137 | # Cython debug symbols 138 | cython_debug/ 139 | -------------------------------------------------------------------------------- /PyPyNum.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonSJL/PyPyNum/af2ae76b28e2e5b6c9a850d480de149cd5d7261e/PyPyNum.png -------------------------------------------------------------------------------- /pypynum/PyPyNum.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonSJL/PyPyNum/af2ae76b28e2e5b6c9a850d480de149cd5d7261e/pypynum/PyPyNum.png -------------------------------------------------------------------------------- /pypynum/__init__.py: -------------------------------------------------------------------------------- 1 | r""" 2 | ________ ___ ___ ________ ___ ___ ________ ___ ___ _____ ______ 3 | |\ __ \ |\ \ / /||\ __ \ |\ \ / /||\ ___ \ |\ \|\ \ |\ _ \ _ \ 4 | \ \ \|\ \\ \ \/ / /\ \ \|\ \\ \ \/ / /\ \ \\ \ \\ \ \\\ \\ \ \\\__\ \ \ 5 | \ \ ____\\ \ / / \ \ ____\\ \ / / \ \ \\ \ \\ \ \\\ \\ \ \\|__| \ \ 6 | \ \ \___| \/ / / \ \ \___| \/ / / \ \ \\ \ \\ \ \\\ \\ \ \ \ \ \ 7 | \ \__\ __/ / / \ \__\ __/ / / \ \__\\ \__\\ \_______\\ \__\ \ \__\ 8 | \|__| |\___/ / \|__| |\___/ / \|__| \|__| \|_______| \|__| \|__| 9 | \|___|/ \|___|/ 10 | 11 | PyPyNum 12 | ========== 13 | 14 | PyPyNum is a multifunctional Python math lib. It includes modules for math, data analysis, array ops, crypto, 15 | physics, randomness, data prep, stats, solving eqns, image processing, interp, matrix calc, and high-precision math. 16 | Designed for scientific computing, data science, and machine learning, PyPyNum provides efficient and versatile tools. 17 | 18 | Copyright 19 | ========== 20 | 21 | - Author: Shen Jiayi 22 | - Email: 2261748025@qq.com 23 | - Copyright: Copyright (c) 2023, Shen Jiayi. All rights reserved. 24 | """ 25 | 26 | __author__ = "Shen Jiayi" 27 | __email__ = "2261748025@qq.com" 28 | __copyright__ = "Copyright (c) 2023, Shen Jiayi. All rights reserved." 29 | 30 | from . import consts 31 | from . import zh_cn 32 | from .arrays import * 33 | from .chars import int2superscript, superscript2int, int2subscript, subscript2int 34 | from .ciphers import * 35 | from .crandom import * 36 | from .dataproc import * 37 | from .dists import * 38 | from .equations import * 39 | from .fft import * 40 | from .files import * 41 | from .geoms import * 42 | from .graphs import * 43 | from .groups import * 44 | from .hypcmpnms import * 45 | from .images import * 46 | from .interp import * 47 | from .kernels import * 48 | from .logics import * 49 | from .maths import * 50 | from .matrices import * 51 | from .multiprec import * 52 | from .networks import * 53 | from .numbers import * 54 | from .plotting import * 55 | from .polys import * 56 | from .pprinters import * 57 | from .random import * 58 | from .regs import * 59 | from .seqs import * 60 | from .special import * 61 | from .stattest import * 62 | from .symbols import * 63 | from .tensors import * 64 | from .tools import * 65 | from .trees import * 66 | from .types import config 67 | from .ufuncs import * 68 | from .utils import * 69 | from .vectors import * 70 | 71 | __version__ = "1.17.2" 72 | print("PyPyNum", "Version -> " + __version__, "PyPI -> https://pypi.org/project/PyPyNum/", 73 | "Gitee -> https://www.gitee.com/PythonSJL/PyPyNum", "GitHub -> https://github.com/PythonSJL/PyPyNum", sep=" | ") 74 | for key, value in tuple(globals().items()): 75 | if key.endswith("Error") or str(value).startswith("typing."): 76 | del globals()[key] 77 | del key, value 78 | -------------------------------------------------------------------------------- /pypynum/chars.py: -------------------------------------------------------------------------------- 1 | """ 2 | Special mathematical characters 3 | """ 4 | 5 | div = "÷" 6 | mul = "×" 7 | overline = "̄" 8 | sgn = "±" 9 | strikethrough = "̶" 10 | subscript = "₀₁₂₃₄₅₆₇₈₉₊₋₌₍₎ₐₑₕᵢⱼₖₗₘₙₒₚᵣₛₜᵤᵥₓ" 11 | superscript = "⁰¹²³⁴⁵⁶⁷⁸⁹⁺⁻⁼⁽⁾ᴬᴮᴰᴱᴳᴴᴵᴶᴷᴸᴹᴺᴼᴾᴿᵀᵁⱽᵂᵃᵇᶜᵈᵉᶠᵍʰⁱʲᵏˡᵐⁿᵒᵖʳˢᵗᵘᵛʷˣʸᶻᵋᵝᵟᵠᶿ" 12 | underline = "_" 13 | arrow = ( 14 | ("↖", "↑", "↗"), 15 | ("←", "⇌", "→"), 16 | ("↙", "↓", "↘"), 17 | ("↔", "⇋", "↕"), 18 | ("⇐", "⇔", "⇒") 19 | ) 20 | tab = ( 21 | ("┌", "┬", "┐"), 22 | ("├", "┼", "┤"), 23 | ("└", "┴", "┘"), 24 | ("─", "╭", "╮"), 25 | ("│", "╰", "╯") 26 | ) 27 | pi = "Ππ𝜫𝝅𝝥𝝿𝞟𝞹Пп∏ϖ∐ℼㄇ兀" 28 | notsign = "¬" 29 | degree = "°" 30 | permille = "‰" 31 | permyriad = "‱" 32 | prime = "′" 33 | dprime = "″" 34 | arc = "⌒" 35 | ln = "㏑" 36 | log = "㏒" 37 | others = "".join(map(chr, range(0x2200, 0x2300))) 38 | 39 | 40 | def int2superscript(standard_str: str) -> str: 41 | superscript_map = {"0": "⁰", "1": "¹", "2": "²", "3": "³", "4": "⁴", 42 | "5": "⁵", "6": "⁶", "7": "⁷", "8": "⁸", "9": "⁹"} 43 | return "".join([superscript_map.get(digit, digit) for digit in standard_str]) 44 | 45 | 46 | def superscript2int(superscript_str: str) -> str: 47 | standard_map = {"⁰": "0", "¹": "1", "²": "2", "³": "3", "⁴": "4", 48 | "⁵": "5", "⁶": "6", "⁷": "7", "⁸": "8", "⁹": "9"} 49 | return "".join([standard_map.get(char, char) for char in superscript_str]) 50 | 51 | 52 | def int2subscript(standard_str: str) -> str: 53 | subscript_map = {"0": "₀", "1": "₁", "2": "₂", "3": "₃", "4": "₄", 54 | "5": "₅", "6": "₆", "7": "₇", "8": "₈", "9": "₉"} 55 | return "".join([subscript_map.get(digit, digit) for digit in standard_str]) 56 | 57 | 58 | def subscript2int(subscript_str: str) -> str: 59 | standard_map = {"₀": "0", "₁": "1", "₂": "2", "₃": "3", "₄": "4", 60 | "₅": "5", "₆": "6", "₇": "7", "₈": "8", "₉": "9"} 61 | return "".join([standard_map.get(char, char) for char in subscript_str]) 62 | -------------------------------------------------------------------------------- /pypynum/ciphers.py: -------------------------------------------------------------------------------- 1 | from .types import ContentError 2 | 3 | ContentError = ContentError("The input string is invalid") 4 | __ORD_A = 65 5 | __ORD_a = 97 6 | __MORSE_CODE = { 7 | "A": ".-", "B": "-...", "C": "-.-.", "D": "-..", "E": ".", "F": "..-.", "G": "--.", "H": "....", "I": "..", 8 | "J": ".---", "K": "-.-", "L": ".-..", "M": "--", "N": "-.", "O": "---", "P": ".--.", "Q": "--.-", "R": ".-.", 9 | "S": "...", "T": "-", "U": "..-", "V": "...-", "W": ".--", "X": "-..-", "Y": "-.--", "Z": "--..", "0": "-----", 10 | "1": ".----", "2": "..---", "3": "...--", "4": "....-", "5": ".....", "6": "-....", "7": "--...", "8": "---..", 11 | "9": "----.", ".": ".-.-.-", ",": "--..--", "?": "..--..", "!": "-.-.--", "'": '.----.', "/": "-..-.", "(": "-.--.", 12 | ")": "-.--.-", "&": ".-...", ":": "---...", ";": "-.-.-.", "=": "-...-", "+": ".-.-.", "-": "-....-", "_": "..--.-", 13 | "\"": ".-..-.", "$": "...-..-", "@": ".--.-.", " ": "/" 14 | } 15 | __MORSE_CODE_REVERSE = {v: k for k, v in __MORSE_CODE.items()} 16 | 17 | 18 | def base_64(text: str, decrypt: bool = False) -> str: 19 | base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" 20 | base64_index = {c: i for i, c in enumerate(base64_chars)} 21 | base64_padding = "=" 22 | 23 | def base64_encode(data): 24 | encoded = "" 25 | remainder = len(data) % 3 26 | padding_len = 3 - remainder if remainder else 0 27 | for i in range(0, len(data) - remainder, 3): 28 | chunk = data[i:i + 3] 29 | int_value = (chunk[0] << 16) + (chunk[1] << 8) + chunk[2] 30 | for j in range(4): 31 | bits = (int_value >> (6 * (3 - j))) & 0x3F 32 | encoded += base64_chars[bits] 33 | if padding_len: 34 | chunk = data[-remainder:] 35 | binary = "".join([format(i, "08b") for i in chunk]) + "0" * (6 - len(chunk) * 8 % 6) 36 | encoded += "".join([base64_chars[int(binary[j:j + 6], 2)] for j in range( 37 | 0, len(binary), 6)]) + base64_padding * (padding_len % 3) 38 | return encoded 39 | 40 | def base64_decode(encoded): 41 | encoded = encoded.replace("\n", "").replace(" ", "") 42 | decoded = b"" 43 | if not all([c in base64_chars or c == base64_padding for c in encoded]): 44 | raise ContentError 45 | position = encoded.find(base64_padding) 46 | padding_len = len(encoded) - position if position != -1 else 0 47 | encoded = encoded.rstrip(base64_padding) 48 | if padding_len > 2 or base64_padding in encoded: 49 | raise ContentError 50 | idx = 0 51 | while idx < len(encoded): 52 | chunk = encoded[idx:idx + 4] 53 | int_value = 0 54 | for j in range(4): 55 | if j < len(chunk): 56 | int_value = (int_value << 6) + base64_index[chunk[j]] 57 | decoded += (int_value >> 16 & 0xFF).to_bytes(1, "big") 58 | decoded += (int_value >> 8 & 0xFF).to_bytes(1, "big") 59 | decoded += (int_value & 0xFF).to_bytes(1, "big") 60 | idx += 4 61 | if padding_len: 62 | try: 63 | decoded = decoded[:-3] + int(format(int.from_bytes( 64 | decoded[-3:], "big") >> 2 * padding_len, "b"), 2).to_bytes(3 - padding_len, "big") 65 | except OverflowError: 66 | raise ContentError 67 | return decoded 68 | 69 | if decrypt: 70 | decoded_bytes = base64_decode(text).decode("UTF-8") 71 | return decoded_bytes 72 | else: 73 | encoded_bytes = base64_encode(text.encode("UTF-8")) 74 | return encoded_bytes 75 | 76 | 77 | def atbash(text: str) -> str: 78 | return "".join([chr(219 - ord(c)) if "a" <= c <= "z" 79 | else chr(155 - ord(c)) if "A" <= c <= "Z" else c for c in text]) 80 | 81 | 82 | def rot13(text: str) -> str: 83 | return caesar(text, 13) 84 | 85 | 86 | def caesar(text: str, shift: int, decrypt: bool = False) -> str: 87 | result = "" 88 | if decrypt: 89 | shift = -shift 90 | for char in text: 91 | if char.isalpha(): 92 | ascii_offset = __ORD_a if char.islower() else __ORD_A 93 | new_char = chr((ord(char) - ascii_offset + shift) % 26 + ascii_offset) 94 | result += new_char 95 | else: 96 | result += char 97 | return result 98 | 99 | 100 | def vigenere(text: str, key: str, decrypt: bool = False) -> str: 101 | result = "" 102 | key_index = 0 103 | for char in text: 104 | if char.isalpha(): 105 | key_char = key[key_index] 106 | key_index = (key_index + 1) % len(key) 107 | shift = ord(key_char.lower()) - __ORD_a 108 | if decrypt: 109 | shift = 26 - shift 110 | if char.isupper(): 111 | result += chr((ord(char) - __ORD_A + shift) % 26 + __ORD_A) 112 | else: 113 | result += chr((ord(char) - __ORD_a + shift) % 26 + __ORD_a) 114 | else: 115 | result += char 116 | return result 117 | 118 | 119 | def substitution(text: str, sub_map: dict, decrypt: bool = False) -> str: 120 | result = "" 121 | if decrypt: 122 | reverse_map = {v: k for k, v in sub_map.items()} 123 | sub_map = reverse_map 124 | for char in text: 125 | if char in sub_map: 126 | result += sub_map[char] 127 | else: 128 | result += char 129 | return result 130 | 131 | 132 | def morse(text: str, decrypt: bool = False) -> str: 133 | code_dict = __MORSE_CODE_REVERSE if decrypt else __MORSE_CODE 134 | result = [] 135 | if decrypt: 136 | text = text.split() 137 | else: 138 | text = text.upper() 139 | for item in text: 140 | if item in code_dict: 141 | result.append(code_dict[item]) 142 | else: 143 | result.append(item) 144 | char = "" if decrypt else " " 145 | return char.join(result) 146 | 147 | 148 | def playfair(text: str, key: str, decrypt: bool = False) -> str: 149 | def shift(coordinate, direction): 150 | row, col = coordinate 151 | if direction == "R": 152 | col = (col + 1) % 5 153 | elif direction == "L": 154 | col = (col - 1) % 5 155 | elif direction == "D": 156 | row = (row + 1) % 5 157 | elif direction == "U": 158 | row = (row - 1) % 5 159 | return row, col 160 | 161 | key = key.upper().replace("J", "I") 162 | dedup = [] 163 | for k in key: 164 | if k.isalpha() and k not in dedup: 165 | dedup.append(k) 166 | key = "".join(dedup) 167 | if len(text) & 1 != 0: 168 | text += key[0] 169 | char_to_coord = {} 170 | coords = [(i, j) for i in range(5) for j in range(5)] 171 | letters = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", 172 | "W", "X", "Y", "Z"] 173 | for char, coord in zip(key, coords): 174 | char_to_coord[char] = coord 175 | letters.remove(char) 176 | for char, coord in zip(letters, coords[len(key):]): 177 | char_to_coord[char] = coord 178 | coord_to_char = {v: k for k, v in char_to_coord.items()} 179 | result = "" 180 | for i in range(0, len(text), 2): 181 | pair = text[i:i + 2] 182 | is_lowercase1 = pair[0].islower() 183 | is_lowercase2 = pair[1].islower() 184 | c1, c2 = pair.upper().replace("J", "I") 185 | if c1 != c2 and pair.isalpha(): 186 | c1_coord, c2_coord = char_to_coord[c1], char_to_coord[c2] 187 | if c1_coord[0] == c2_coord[0]: 188 | c1_coord = shift(c1_coord, "L") if decrypt else shift(c1_coord, "R") 189 | c2_coord = shift(c2_coord, "L") if decrypt else shift(c2_coord, "R") 190 | elif c1_coord[1] == c2_coord[1]: 191 | c1_coord = shift(c1_coord, "U") if decrypt else shift(c1_coord, "D") 192 | c2_coord = shift(c2_coord, "U") if decrypt else shift(c2_coord, "D") 193 | else: 194 | c1_coord, c2_coord = (c1_coord[0], c2_coord[1]), (c2_coord[0], c1_coord[1]) 195 | encrypted_c1, encrypted_c2 = coord_to_char[c1_coord], coord_to_char[c2_coord] 196 | if is_lowercase1: 197 | encrypted_c1 = encrypted_c1.lower() 198 | if is_lowercase2: 199 | encrypted_c2 = encrypted_c2.lower() 200 | result += encrypted_c1 + encrypted_c2 201 | else: 202 | result += pair 203 | return result 204 | 205 | 206 | def hill256(text: bytes, key: list, decrypt: bool = False) -> bytes: 207 | from .arrays import fill 208 | from .matrices import mat 209 | 210 | def decrypt_key(k): 211 | try: 212 | det = round(k.det()) 213 | except ValueError: 214 | raise ValueError("The key must be a square matrix") 215 | res = 0 216 | while res < mod: 217 | if det * res % mod == 1: 218 | break 219 | else: 220 | res += 1 221 | if res == mod: 222 | raise ValueError("The key square matrix does not have a multiplicative inverse element") 223 | return round(k.inv() * det % mod) * res % mod 224 | 225 | if not isinstance(text, bytes): 226 | raise TypeError("The input content needs to be encoded as a byte type") 227 | mod = 0x100 228 | length = len(text) 229 | key = mat(key) 230 | if decrypt: 231 | key = decrypt_key(key) 232 | cols = key.cols 233 | result = key @ mat(fill([cols, length // cols + bool(length % cols)], tuple(text), False)) % mod 234 | return b"".join([result[i][j].to_bytes(1, "big") for i in range(result.rows) for j in range(result.cols)]) 235 | 236 | 237 | def ksa(key: bytes) -> list: 238 | if not isinstance(key, bytes): 239 | raise TypeError("The key needs to be of byte type") 240 | length = len(key) 241 | if not length: 242 | raise ValueError("The key cannot be empty") 243 | s = list(range(0x100)) 244 | j = 0 245 | for i in range(0x100): 246 | j = (j + s[i] + key[i % length]) % 0x100 247 | s[i], s[j] = s[j], s[i] 248 | return s 249 | 250 | 251 | def prga(s: list): 252 | i = 0 253 | j = 0 254 | while True: 255 | i = (i + 1) % 0x100 256 | j = (j + s[i]) % 0x100 257 | s[i], s[j] = s[j], s[i] 258 | k = s[(s[i] + s[j]) % 0x100] 259 | yield k 260 | 261 | 262 | def rc4(text: bytes, key: bytes) -> bytes: 263 | if not isinstance(text, bytes): 264 | raise TypeError("The input content needs to be encoded as a byte type") 265 | stream = prga(ksa(key)) 266 | result = bytearray() 267 | for byte in text: 268 | k = next(stream) 269 | result.append(byte ^ k) 270 | return bytes(result) 271 | -------------------------------------------------------------------------------- /pypynum/consts.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module defines a collection of constants representing various physical, mathematical, and unit conversion 3 | factors. These constants are commonly used in scientific and engineering calculations. 4 | 5 | - **EB**: Exabyte, equal to 2^60 bytes. 6 | - **G**: Gravitational constant (m^3 kg^-1 s^-2). 7 | - **GB**: Gigabyte, equal to 2^30 bytes. 8 | - **KB**: Kilobyte, equal to 2^10 bytes. 9 | - **MB**: Megabyte, equal to 2^20 bytes. 10 | - **PB**: Petabyte, equal to 2^50 bytes. 11 | - **TB**: Terabyte, equal to 2^40 bytes. 12 | - **YB**: Yottabyte, equal to 2^80 bytes. 13 | - **ZB**: Zettabyte, equal to 2^70 bytes. 14 | - **a_0**: Bohr radius (meters). 15 | - **acre**: Area of one acre (square meters). 16 | - **alpha**: Fine-structure constant. 17 | - **alpha_f**: Feigenbaum constant. 18 | - **amu**: Atomic mass unit (kilograms). 19 | - **apery**: Apery's constant. 20 | - **arcmin**: Minute of arc (degrees). 21 | - **arcsec**: Second of arc (degrees). 22 | - **atm**: Standard atmosphere (pascals). 23 | - **atto**: Prefix denoting 10^-18. 24 | - **au**: Astronomical unit (meters). 25 | - **btu**: British thermal unit (joules). 26 | - **c**: Speed of light in a vacuum (meters per second). 27 | - **cal**: Calorie (joules). 28 | - **catalan**: Catalan's constant. 29 | - **centi**: Prefix denoting 10^-2. 30 | - **ci**: Curie (becquerels). 31 | - **conway**: Conway's constant. 32 | - **ct**: Carat (kilograms). 33 | - **cup**: Volume of one US cup (cubic meters). 34 | - **day**: Number of seconds in one day. 35 | - **debye**: Unit of electric dipole moment (coulomb-meters). 36 | - **deci**: Prefix denoting 10^-1. 37 | - **deka**: Prefix denoting 10^1. 38 | - **delta_f**: Feigenbaum constant delta. 39 | - **e**: Euler's number. 40 | - **epsilon_0**: Vacuum permittivity (farads per meter). 41 | - **exa**: Prefix denoting 10^18. 42 | - **faraday**: Faraday constant (coulombs). 43 | - **fathom**: Length of one fathom (meters). 44 | - **fc**: Foot-candle (lumens per square meter). 45 | - **femto**: Prefix denoting 10^-15. 46 | - **fl**: Foot-lambert (candelas per square meter). 47 | - **fl_oz**: Fluid ounce (cubic meters). 48 | - **foias**: Foias constant. 49 | - **foot**: Length of one foot (meters). 50 | - **g**: Standard acceleration due to gravity (m/s^2). 51 | - **gal_can**: Canadian gallon (cubic meters). 52 | - **gal_uk**: UK gallon (cubic meters). 53 | - **gal_us**: US gallon (cubic meters). 54 | - **gamma**: Euler-Mascheroni constant. 55 | - **gauss**: Unit of magnetic field strength (tesla). 56 | - **giga**: Prefix denoting 10^9. 57 | - **glaisher**: Glaisher-Kinkelin constant. 58 | - **h**: Planck constant (joule-seconds). 59 | - **hbar**: Reduced Planck constant (joule-seconds). 60 | - **hecto**: Prefix denoting 10^2. 61 | - **hour**: Number of seconds in one hour. 62 | - **hp**: Horsepower (watts). 63 | - **inch**: Length of one inch (meters). 64 | - **inf**: Infinity (float representation). 65 | - **inh2o**: Inches of water (pascals). 66 | - **inhg**: Inches of mercury (pascals). 67 | - **k_b**: Boltzmann constant (joules per kelvin). 68 | - **k_e**: Coulomb's constant (newton-meter^2 per coulomb^2). 69 | - **kilo**: Prefix denoting 10^3. 70 | - **kip**: Kip (force) (newtons). 71 | - **kmh**: Kilometers per hour (meters per second). 72 | - **knot**: Nautical mile per hour (meters per second). 73 | - **lbf**: Pound-force (newtons). 74 | - **lbm**: Pound-mass (kilograms). 75 | - **ly**: Light-year (meters). 76 | - **m_e**: Electron mass (kilograms). 77 | - **m_mu**: Muon mass (kilograms). 78 | - **m_n**: Neutron mass (kilograms). 79 | - **m_p**: Proton mass (kilograms). 80 | - **m_sun**: Solar mass (kilograms). 81 | - **mega**: Prefix denoting 10^6. 82 | - **micro**: Prefix denoting 10^-6. 83 | - **mil**: Thousandth of an inch (meters). 84 | - **mile**: Length of one mile (meters). 85 | - **milli**: Prefix denoting 10^-3. 86 | - **minute**: Number of seconds in one minute. 87 | - **mmhg**: Millimeters of mercury (pascals). 88 | - **mph**: Miles per hour (meters per second). 89 | - **mu_0**: Vacuum permeability (henry per meter). 90 | - **mu_b**: Bohr magneton (joules per tesla). 91 | - **mu_e**: Electron magnetic moment (joules per tesla). 92 | - **mu_n**: Neutron magnetic moment (joules per tesla). 93 | - **mu_p**: Proton magnetic moment (joules per tesla). 94 | - **n_a**: Avogadro constant (per mole). 95 | - **nan**: Not a number (float representation). 96 | - **nano**: Prefix denoting 10^-9. 97 | - **nmi**: Nautical mile (meters). 98 | - **oz_t**: Troy ounce (kilograms). 99 | - **ozm**: Ounce-mass (kilograms). 100 | - **parsec**: Parsec (meters). 101 | - **pdl**: Poundal (force) (newtons). 102 | - **peta**: Prefix denoting 10^15. 103 | - **phi**: Golden ratio. 104 | - **pi**: Pi, the ratio of a circle's circumference to its diameter. 105 | - **pico**: Prefix denoting 10^-12. 106 | - **pint**: Volume of one pint (cubic meters). 107 | - **point**: Point (typography) (meters). 108 | - **psi**: Pounds per square inch (pascals). 109 | - **q_e**: Elementary charge (coulombs). 110 | - **quart**: Volume of one quart (cubic meters). 111 | - **r_g**: Universal gas constant (joules per mole kelvin). 112 | - **radian**: Radians in one degree. 113 | - **roentgen**: Roentgen (exposure to ionizing radiation). 114 | - **ry**: Rydberg constant (energy). 115 | - **sigma**: Stefan-Boltzmann constant (watts per square meter per kelvin^4). 116 | - **sigma_t**: Thomson cross-section (square meters). 117 | - **tbsp**: Tablespoon (cubic meters). 118 | - **tera**: Prefix denoting 10^12. 119 | - **texpoint**: Tex point (typography) (meters). 120 | - **therm**: Therm (energy). 121 | - **torr**: Torr (pressure). 122 | - **tsp**: Teaspoon (cubic meters). 123 | - **uk_ton**: UK ton (kilograms). 124 | - **us_ton**: US ton (kilograms). 125 | - **v_m**: Molar volume (cubic meters per mole). 126 | - **week**: Number of seconds in one week. 127 | - **yard**: Length of one yard (meters). 128 | - **year**: Number of seconds in one year. 129 | - **yocto**: Prefix denoting 10^-24. 130 | - **yotta**: Prefix denoting 10^24. 131 | - **zepto**: Prefix denoting 10^-21. 132 | - **zetta**: Prefix denoting 10^21. 133 | """ 134 | 135 | EB = 1152921504606846976 136 | G = 6.6743e-11 137 | GB = 1073741824 138 | KB = 1024 139 | MB = 1048576 140 | PB = 1125899906842624 141 | TB = 1099511627776 142 | YB = 1208925819614629174706176 143 | ZB = 1180591620717411303424 144 | a_0 = 5.291772083e-11 145 | acre = 4046.85642241 146 | alpha = 0.0072973525693 147 | alpha_f = 2.5029078750958926 148 | amu = 1.6605402e-27 149 | apery = 1.2020569031595942 150 | arcmin = 0.016666666666666666 151 | arcsec = 0.0002777777777777778 152 | atm = 101325 153 | atto = 1e-18 154 | au = 149597870700 155 | btu = 1055.05585262 156 | c = 299792458 157 | cal = 4.1868 158 | catalan = 0.915965594177219 159 | centi = 0.01 160 | ci = 37000000000 161 | conway = 1.3035772690342964 162 | ct = 0.0002 163 | cup = 0.000236588236501 164 | day = 86400 165 | debye = 3.33564095198e-30 166 | deci = 0.1 167 | deka = 10 168 | delta_f = 4.66920160910299 169 | e = 2.718281828459045 170 | epsilon_0 = 8.854187817620389e-12 171 | exa = 1000000000000000000 172 | faraday = 96485.3429775 173 | fathom = 1.8288 174 | fc = 10.76 175 | femto = 1e-15 176 | fl = 10.7639104 177 | fl_oz = 2.95735295626e-05 178 | foias = 1.1874523510652712 179 | foot = 0.3048 180 | g = 9.80665 181 | gal_can = 0.00454609 182 | gal_uk = 0.004546092 183 | gal_us = 0.00378541178402 184 | gamma = 0.5772156649015329 185 | gauss = 0.8346268416740732 186 | giga = 1000000000 187 | glaisher = 1.2824271291006226 188 | h = 6.62607015e-34 189 | hbar = 1.0545718176461565e-34 190 | hecto = 100 191 | hour = 3600 192 | hp = 745.6998715822701 193 | inch = 0.0254 194 | inf = float("inf") 195 | inh2o = 249.0889 196 | inhg = 3386.38815789 197 | k_b = 1.380649e-23 198 | k_e = 8.9875517923e+9 199 | kilo = 1000 200 | kip = 4448.22161526 201 | kmh = 0.2777777777777778 202 | knot = 0.5144444444444445 203 | lbf = 4.44822161526 204 | lbm = 0.45359237 205 | ly = 9460730472580800 206 | m_e = 9.1093837015e-31 207 | m_mu = 1.88353109e-28 208 | m_n = 1.67492749804e-27 209 | m_p = 1.67262192369e-27 210 | m_sun = 1.98892e+30 211 | mega = 1000000 212 | micro = 1e-06 213 | mil = 2.54e-05 214 | mile = 1609.344 215 | milli = 0.001 216 | minute = 60 217 | mmhg = 133322.368421 218 | mph = 0.44704 219 | mu_0 = 1.2566370614359173e-06 220 | mu_b = 9.27400899e-24 221 | mu_e = 9.28476362e-24 222 | mu_n = 5.05078317e-27 223 | mu_p = 1.410606633e-26 224 | n_a = 6.02214076e+23 225 | nan = float("nan") 226 | nano = 1e-09 227 | nmi = 1852 228 | oz_t = 0.031103475 229 | ozm = 0.028349523125 230 | parsec = 3.085677581491367e+16 231 | pdl = 0.138255 232 | peta = 1000000000000000 233 | phi = 1.618033988749895 234 | pi = 3.141592653589793 235 | pico = 1e-12 236 | pint = 0.000473176473002 237 | point = 0.00035277777777777776 238 | psi = 6894.75729317 239 | q_e = 1.602176634e-19 240 | quart = 0.000946352946004 241 | r_g = 8.314462618 242 | radian = 57.29577951308232 243 | roentgen = 0.000258 244 | ry = 2.17987196968e-18 245 | sigma = 5.67040047374e-08 246 | sigma_t = 6.65245893699e-29 247 | tbsp = 1.47867647813e-05 248 | tera = 1000000000000 249 | texpoint = 0.000351459803515 250 | therm = 105506000 251 | torr = 133.322368421 252 | tsp = 4.92892159375e-06 253 | uk_ton = 1016.0469088 254 | us_ton = 907.18474 255 | v_m = 0.022710981 256 | week = 604800 257 | yard = 0.9144 258 | year = 31557600 259 | yocto = 1e-24 260 | yotta = 1000000000000000000000000 261 | zepto = 1e-21 262 | zetta = 1000000000000000000000 263 | -------------------------------------------------------------------------------- /pypynum/crandom.py: -------------------------------------------------------------------------------- 1 | from math import cos as __cos, sin as __sin 2 | from random import randint as __randint, random as __random, uniform as __uniform 3 | from .types import real as __real 4 | 5 | 6 | def randint_polar(left: int, right: int, mod: __real = None, angle: __real = None) -> complex: 7 | """ 8 | Generate a complex number in polar form with integer modulus and angle 9 | randomly selected from a specified range. 10 | :param left: The lower bound (inclusive) of the random number range. 11 | :param right: The upper bound (inclusive) of the random number range. 12 | :param mod: The modulus of the complex number. If None, it is randomly 13 | selected from the range [left, right]. 14 | :param angle: The angle of the complex number. If None, it is randomly 15 | selected from the range [0, 2π]. 16 | :return: A complex number in polar form. 17 | """ 18 | mod = __randint(left, right) if mod is None else mod 19 | angle = __uniform(0, 6.283185307179586) if angle is None else angle 20 | return mod * (__cos(angle) + __sin(angle) * 1j) 21 | 22 | 23 | def randint_rect(left: int, right: int, real: __real = None, imag: __real = None) -> complex: 24 | """ 25 | Generate a complex number in rectangular form with integer real and 26 | imaginary parts randomly selected from a specified range. 27 | :param left: The lower bound (inclusive) of the random number range. 28 | :param right: The upper bound (inclusive) of the random number range. 29 | :param real: The real part of the complex number. If None, it is randomly 30 | selected from the range [left, right]. 31 | :param imag: The imaginary part of the complex number. If None, it is 32 | randomly selected from the range [left, right]. 33 | :return: A complex number in rectangular form. 34 | """ 35 | real = __randint(left, right) if real is None else real 36 | imag = __randint(left, right) if imag is None else imag 37 | return complex(real, imag) 38 | 39 | 40 | def random_polar(mod: __real = None, angle: __real = None) -> complex: 41 | """ 42 | Generate a complex number in polar form with floating-point modulus and 43 | angle randomly selected from the range [0, 2π]. 44 | :param mod: The modulus of the complex number. If None, it is randomly 45 | selected from the range [0, 1]. 46 | :param angle: The angle of the complex number. If None, it is uniformly 47 | selected from the range [0, 2π]. 48 | :return: A complex number in polar form. 49 | """ 50 | mod = __random() if mod is None else mod 51 | angle = __uniform(0, 6.283185307179586) if angle is None else angle 52 | return mod * (__cos(angle) + __sin(angle) * 1j) 53 | 54 | 55 | def random_rect(real: __real = None, imag: __real = None) -> complex: 56 | """ 57 | Generate a complex number in rectangular form with floating-point real and 58 | imaginary parts randomly selected from the range [0, 1]. 59 | :param real: The real part of the complex number. If None, it is randomly 60 | selected from the range [0, 1]. 61 | :param imag: The imaginary part of the complex number. If None, it is 62 | randomly selected from the range [0, 1]. 63 | :return: A complex number in rectangular form. 64 | """ 65 | real = __random() if real is None else real 66 | imag = __random() if imag is None else imag 67 | return complex(real, imag) 68 | 69 | 70 | def uniform_polar(left: __real, right: __real, mod: __real = None, angle: __real = None) -> complex: 71 | """ 72 | Generate a complex number in polar form with uniformly distributed 73 | floating-point modulus and angle from a specified range. 74 | :param left: The lower bound (inclusive) of the random number range. 75 | :param right: The upper bound (inclusive) of the random number range. 76 | :param mod: The modulus of the complex number. If None, it is uniformly 77 | selected from the range [left, right]. 78 | :param angle: The angle of the complex number. If None, it is uniformly 79 | selected from the range [0, 2π]. 80 | :return: A complex number in polar form. 81 | """ 82 | mod = __uniform(left, right) if mod is None else mod 83 | angle = __uniform(0, 6.283185307179586) if angle is None else angle 84 | return mod * (__cos(angle) + __sin(angle) * 1j) 85 | 86 | 87 | def uniform_rect(left: __real, right: __real, real: __real = None, imag: __real = None) -> complex: 88 | """ 89 | Generate a complex number in rectangular form with uniformly distributed 90 | floating-point real and imaginary parts from a specified range. 91 | :param left: The lower bound (inclusive) of the random number range. 92 | :param right: The upper bound (inclusive) of the random number range. 93 | :param real: The real part of the complex number. If None, it is uniformly 94 | selected from the range [left, right]. 95 | :param imag: The imaginary part of the complex number. If None, it is 96 | uniformly selected from the range [left, right]. 97 | :return: A complex number in rectangular form. 98 | """ 99 | real = __uniform(left, right) if real is None else real 100 | imag = __uniform(left, right) if imag is None else imag 101 | return complex(real, imag) 102 | -------------------------------------------------------------------------------- /pypynum/dataproc.py: -------------------------------------------------------------------------------- 1 | from .types import Any, Callable, Union, real 2 | 3 | 4 | class Series: 5 | def __init__(self, data: Any = None, index: Any = None) -> None: 6 | if data is None: 7 | self.data = [] 8 | self.index = [] 9 | else: 10 | self.data = data 11 | self.index = index 12 | 13 | def __repr__(self) -> str: 14 | def string(obj): 15 | return obj.__str__().__repr__()[1:-1] 16 | 17 | escaped_indices = tuple(map(string, self.index)) 18 | escaped_data = tuple(map(string, self.data)) 19 | max_index_length = max(map(len, escaped_indices)) 20 | max_data_length = max(map(len, escaped_data)) 21 | format_str = "{{:<{}}} {{:>{}}}".format(max_index_length, max_data_length) 22 | return "\n".join([format_str.format(index, data) for index, data in zip(escaped_indices, escaped_data)]) 23 | 24 | def describe(self, percentiles: Any = (0.25, 0.5, 0.75), interpolation: str = "linear", ddof: int = 1) -> "Series": 25 | from .maths import quantile 26 | data = self.__dropna(None) 27 | if all([isinstance(x, (int, float)) for x in self.data]): 28 | count = len(data) 29 | mean = sum(data) / count 30 | std = (sum([(x - mean) ** 2 for x in data]) / (count - ddof)) ** 0.5 31 | min_val = min(data) 32 | max_val = max(data) 33 | percentiles = sorted(set(percentiles)) 34 | 35 | def func(q): 36 | return quantile(data, q, interpolation, True) 37 | 38 | data = [count, mean, std, min_val] + list(map(func, percentiles)) + [max_val] 39 | index = ["count", "mean", "std", "min"] + [str(p * 100) + "%" for p in percentiles] + ["max"] 40 | return Series(data, index) 41 | else: 42 | count = len(data) 43 | unique = len(set(data)) 44 | top = max(data, key=data.count) 45 | freq = data.count(top) 46 | data = [count, unique, top, freq] 47 | index = ["count", "unique", "top", "freq"] 48 | return Series(data, index) 49 | 50 | def count(self, skipna: bool = True) -> int: 51 | return len(self.__dropna(None) if skipna else self.data) 52 | 53 | def mean(self, skipna: bool = True) -> real: 54 | from .maths import mean 55 | data = self.__dropna(None) if skipna else self.data 56 | return mean(data) 57 | 58 | def var(self, dof: int = 1, skipna: bool = True) -> real: 59 | from .maths import var 60 | data = self.__dropna(None) if skipna else self.data 61 | return var(data, dof) 62 | 63 | def std(self, dof: int = 1, skipna: bool = True) -> real: 64 | from .maths import std 65 | data = self.__dropna(None) if skipna else self.data 66 | return std(data, dof) 67 | 68 | def min(self, skipna: bool = True) -> real: 69 | data = self.__dropna(None) if skipna else self.data 70 | return min(data) 71 | 72 | def max(self, skipna: bool = True) -> real: 73 | data = self.__dropna(None) if skipna else self.data 74 | return max(data) 75 | 76 | def quantile(self, q: real, interpolation: str = "linear", skipna: bool = True) -> real: 77 | from .maths import quantile 78 | data = self.__dropna(None) if skipna else self.data 79 | return quantile(data, q, interpolation) 80 | 81 | def __dropna(self, ignore_index: Union[bool, None] = False) -> Union["Series", tuple]: 82 | filtered_pairs = [(x, idx) for x, idx in zip(self.data, self.index) if self.__notna(x)] 83 | new_data, new_index = zip(*filtered_pairs) if filtered_pairs else ((), ()) 84 | if ignore_index is None: 85 | return new_data 86 | if ignore_index: 87 | new_index = range(len(new_data)) 88 | return Series(new_data, new_index) 89 | 90 | def dropna(self, ignore_index: bool = False) -> "Series": 91 | return self.__dropna(bool(ignore_index)) 92 | 93 | def __getattribute__(self, item: Any) -> Any: 94 | if item == "data" or item == "index": 95 | return list(super().__getattribute__(item)) 96 | else: 97 | if item in self.index: 98 | data = [x for x, idx in zip(self.data, self.index) if idx == item] 99 | length = len(data) 100 | if length == 1: 101 | return data[0] 102 | else: 103 | return Series(data, [item for _ in range(length)]) 104 | else: 105 | return super().__getattribute__(item) 106 | 107 | def __setattr__(self, key: Any, value: Any) -> None: 108 | if key == "data": 109 | if isinstance(value, (list, tuple)): 110 | if hasattr(self, "index") and len(value) != len(self.index): 111 | raise ValueError("Length of 'data' does not match length of 'index'.") 112 | super().__setattr__(key, list(value)) 113 | else: 114 | raise TypeError("'data' must be a list or tuple.") 115 | elif key == "index": 116 | if value is None: 117 | super().__setattr__(key, list(range(len(self.data)))) 118 | elif isinstance(value, (list, tuple)): 119 | if len(self.data) != len(value): 120 | raise ValueError("Length of 'index' does not match length of 'data'.") 121 | super().__setattr__(key, list(value)) 122 | else: 123 | raise TypeError("'index' must be a list or tuple.") 124 | else: 125 | if key in self.index: 126 | data = [x if idx != key else value for x, idx in zip(self.data, self.index)] 127 | super().__setattr__("data", data) 128 | else: 129 | super().__setattr__(key, value) 130 | 131 | def apply(self, func: Callable) -> "Series": 132 | return Series(map(func, self.data), self.index) 133 | 134 | def head(self, n: int = 5) -> "Series": 135 | return Series(self.data[:n], self.index[:n]) 136 | 137 | def tail(self, n: int = 5) -> "Series": 138 | return Series(self.data[-n:], self.index[-n:]) 139 | 140 | def abs(self) -> "Series": 141 | return Series(map(abs, self.data), self.index) 142 | 143 | def sum(self, skipna: bool = True) -> real: 144 | from math import fsum 145 | data = self.__dropna(None) if skipna else self.data 146 | return fsum(data) 147 | 148 | def idxmax(self, skipna: bool = True) -> Any: 149 | data = self.__dropna(None) if skipna else self.data 150 | return self.index[data.index(max(data))] 151 | 152 | def idxmin(self, skipna: bool = True) -> Any: 153 | data = self.__dropna(None) if skipna else self.data 154 | return self.index[data.index(min(data))] 155 | 156 | def isna(self) -> "Series": 157 | return Series(map(self.__isna, self.data), self.index) 158 | 159 | def notna(self) -> "Series": 160 | return Series(map(self.__notna, self.data), self.index) 161 | 162 | def fillna(self, value: Any) -> "Series": 163 | filled_data = [value if self.__isna(x) else x for x in self.data] 164 | return Series(filled_data, self.index) 165 | 166 | def replace(self, to_replace: Any, value: Any) -> "Series": 167 | return Series([value if x == to_replace else x for x in self.data], self.index) 168 | 169 | def copy(self) -> "Series": 170 | return Series(self.data, self.index) 171 | 172 | def __sort(self, items, ascending=True, na_position="last"): 173 | def sort_key(item): 174 | value = item[1] 175 | isna = self.__isna(value) 176 | return isna if bool(ascending) is (na_position == "last") else not isna, item[0] if isna else value 177 | 178 | if na_position not in ["first", "last"]: 179 | raise ValueError("'na_position' must be either 'first' or 'last'") 180 | sorted_items = sorted(enumerate(items), key=sort_key, reverse=not ascending) 181 | return tuple(zip(*sorted_items)) 182 | 183 | def sort_index(self, ascending: bool = True, na_position: str = "last", ignore_index: bool = False) -> "Series": 184 | sorted_positions, sorted_indices = self.__sort(self.index, ascending, na_position) 185 | sorted_values = map(self.data.__getitem__, sorted_positions) 186 | new_index = range(len(self.index)) if ignore_index else sorted_indices 187 | return Series(sorted_values, new_index) 188 | 189 | def sort_values(self, ascending: bool = True, na_position: str = "last", ignore_index: bool = False) -> "Series": 190 | sorted_positions, sorted_values = self.__sort(self.data, ascending, na_position) 191 | new_index = range(len(self.index)) if ignore_index else map(self.index.__getitem__, sorted_positions) 192 | return Series(sorted_values, new_index) 193 | 194 | @staticmethod 195 | def __isna(value: Any) -> bool: 196 | return value is None or isinstance(value, float) and value != value 197 | 198 | @staticmethod 199 | def __notna(value: Any) -> bool: 200 | return not Series.__isna(value) 201 | 202 | def cov(self, other: "Series", min_periods: int = None, dof: int = 1) -> real: 203 | from .maths import cov 204 | data1 = self.__dropna(None) 205 | data2 = other.__dropna(None) 206 | if len(data1) < min_periods or len(data2) < min_periods: 207 | return float("nan") 208 | return cov(data1, data2, dof) 209 | 210 | def corr(self, other: "Series", min_periods: int = None) -> real: 211 | from .maths import corr_coeff 212 | data1 = self.__dropna(None) 213 | data2 = other.__dropna(None) 214 | if len(data1) < min_periods or len(data2) < min_periods: 215 | return float("nan") 216 | return corr_coeff(data1, data2) 217 | -------------------------------------------------------------------------------- /pypynum/equations.py: -------------------------------------------------------------------------------- 1 | def lin_eq(left: list, right: list) -> list: 2 | from .matrices import mat 3 | try: 4 | return (mat(left).inv() @ mat([right]).t()).t()[0] 5 | except ValueError: 6 | return [float("inf")] * len(right) 7 | 8 | 9 | def poly_eq(coefficients: list) -> list: 10 | from .matrices import eigen, mat 11 | p = [_ / coefficients[0] for _ in coefficients[1:]] 12 | return sorted(eigen(mat([[-p[i] if j == 0 else 1 if i + 1 == j else 0 for j in range(len(p))] 13 | for i in range(len(p))]))[0].diag(), key=lambda c: (c.real, c.imag)) 14 | -------------------------------------------------------------------------------- /pypynum/fft.py: -------------------------------------------------------------------------------- 1 | class FT1D: 2 | def __init__(self, *data): 3 | if not all([isinstance(i, (int, float, complex)) for i in data]): 4 | raise ValueError("The input data must all be numerical values") 5 | n = len(data) 6 | if n & (n - 1): 7 | n = 2 ** n.bit_length() 8 | data = list(data) + [0] * (n - len(data)) 9 | self.data = data 10 | 11 | def __repr__(self): 12 | return self.__class__.__name__ + str(self.data) 13 | 14 | def fft(self): 15 | from cmath import exp 16 | 17 | def inner(data): 18 | n = len(data) 19 | if n <= 1: 20 | return data 21 | even = inner(data[0::2]) 22 | odd = inner(data[1::2]) 23 | t = [exp(-6.283185307179586j * k / n) * odd[k] for k in range(n // 2)] 24 | return [even[k] + t[k] for k in range(n // 2)] + [even[k] - t[k] for k in range(n // 2)] 25 | 26 | self.data = inner(self.data) 27 | 28 | def ifft(self): 29 | from cmath import exp 30 | 31 | def inner(data): 32 | n = len(data) 33 | if n <= 1: 34 | return data 35 | even = inner(data[0::2]) 36 | odd = inner(data[1::2]) 37 | t = [exp(6.283185307179586j * k / n) * odd[k] for k in range(n // 2)] 38 | return [even[k] + t[k] for k in range(n // 2)] + [even[k] - t[k] for k in range(n // 2)] 39 | 40 | self.data = [k / len(self.data) for k in inner(self.data)] 41 | -------------------------------------------------------------------------------- /pypynum/files.py: -------------------------------------------------------------------------------- 1 | def read(file: str) -> list: 2 | """ 3 | Read and parse files with the suffix ".ppn" 4 | :param file: string 5 | :return: 6 | """ 7 | exec("\n".join([ 8 | "from .arrays import Array", 9 | "from .matrices import Matrix", 10 | "from .tensors import Tensor", 11 | "from .vectors import Vector", 12 | "from .geoms import Point, Line, Triangle, Quadrilateral, Polygon, Circle", 13 | "from .groups import Group", 14 | "from .hypcmpnms import Quaternion, Euler, Octonion", 15 | "from .polys import Polynomial" 16 | ])) 17 | suffix = ".ppn" 18 | if not file.endswith(suffix): 19 | raise FileExistsError("The file extension can only be '{}'".format(suffix)) 20 | result = [] 21 | with open(file, "r") as r: 22 | data = [item.split("\\", 1) for item in r.read().split("\n") if item] 23 | for item in data: 24 | try: 25 | result.append(eval("{}(*{})".format(item[0], item[1]))) 26 | except (IndexError, NameError, SyntaxError): 27 | raise FileExistsError("Data '{}' does not support processing".format("\\".join(item))) 28 | return result 29 | 30 | 31 | def write(file: str, *cls: object): 32 | """ 33 | Save data to a file with the suffix ".ppn" 34 | :param file: string 35 | :param cls: instance 36 | :return: 37 | """ 38 | from .arrays import Array 39 | from .geoms import Point, Line, Triangle, Quadrilateral, Polygon, Circle 40 | from .groups import Group 41 | from .hypcmpnms import Quaternion, Euler, Octonion 42 | from .polys import Polynomial 43 | suffix = ".ppn" 44 | if not file.endswith(suffix): 45 | raise FileExistsError("The file extension can only be '{}'".format(suffix)) 46 | with open(file, "w") as w: 47 | for item in cls: 48 | _type = str(type(item)) 49 | if "." not in _type: 50 | raise TypeError("Type '{}' does not support saving".format(type(item))) 51 | prefix = _type[_type.rfind(".") + 1:-2] 52 | if isinstance(item, Array): 53 | w.write("{}\\{}\n".format(prefix, str([item.data]).replace(" ", ""))) 54 | elif isinstance(item, Point): 55 | w.write("{}\\{}\n".format(prefix, str([item.p]).replace(" ", ""))) 56 | elif isinstance(item, Line): 57 | w.write("{}\\{}\n".format(prefix, str([item.a, item.b]).replace(" ", ""))) 58 | elif isinstance(item, Triangle): 59 | w.write("{}\\{}\n".format(prefix, str([item.a, item.b, item.c]).replace(" ", ""))) 60 | elif isinstance(item, Quadrilateral): 61 | w.write("{}\\{}\n".format(prefix, str([item.a, item.b, item.c, item.d]).replace(" ", ""))) 62 | elif isinstance(item, Polygon): 63 | w.write("{}\\{}\n".format(prefix, str(item.points).replace(" ", ""))) 64 | elif isinstance(item, Circle): 65 | w.write("{}\\{}\n".format(prefix, str([item.center, item.radius]).replace(" ", ""))) 66 | elif isinstance(item, Group): 67 | w.write("{}\\{}\n".format(prefix, str([item.elements()]).replace(" ", ""))) 68 | elif isinstance(item, (Quaternion, Euler, Octonion)): 69 | w.write("{}\\{}\n".format(prefix, str(item.data()).replace(" ", ""))) 70 | elif isinstance(item, Polynomial): 71 | w.write("{}\\{}\n".format(prefix, str([item.terms]).replace(" ", ""))) 72 | else: 73 | raise TypeError("Type '{}' does not support saving".format(type(item))) 74 | -------------------------------------------------------------------------------- /pypynum/geoms.py: -------------------------------------------------------------------------------- 1 | from .types import arr, real 2 | 3 | 4 | class Point: 5 | def __init__(self, p: arr): 6 | if isinstance(p, (list, tuple)): 7 | if len(p) == 2: 8 | self.p = p 9 | else: 10 | raise ValueError("The coordinate value length can only be two") 11 | else: 12 | raise TypeError("The type of coordinate value can only be an array") 13 | 14 | def __repr__(self): 15 | return "Point({})".format(self.p) 16 | 17 | 18 | class Line: 19 | def __init__(self, a: arr, b: arr): 20 | if isinstance(a, (list, tuple)) and isinstance(b, (list, tuple)): 21 | if len(a) == len(b) == 2: 22 | self.a, self.b = a, b 23 | else: 24 | raise ValueError("The coordinate value length can only be two") 25 | else: 26 | raise TypeError("The type of coordinate value can only be an array") 27 | 28 | def __repr__(self) -> str: 29 | return "Line({}, {})".format(self.a, self.b) 30 | 31 | def length(self) -> float: 32 | return ((self.a[0] - self.b[0]) ** 2 + (self.a[1] - self.b[1]) ** 2) ** 0.5 33 | 34 | def expr(self) -> list: 35 | if self.b[0] - self.a[0] == 0: 36 | k = None 37 | else: 38 | k = (self.b[1] - self.a[1]) / (self.b[0] - self.a[0]) 39 | if k is None: 40 | b = None 41 | else: 42 | b = self.a[1] - k * self.a[0] 43 | return [k, b] 44 | 45 | def vertical(self, p: arr) -> tuple: 46 | if isinstance(p, (list, tuple)): 47 | if len(p) == 2: 48 | k, b = self.expr() 49 | if k == 0: 50 | x = p[0] 51 | y = b 52 | return x, y 53 | if k is None: 54 | x = self.a[0] 55 | y = p[1] 56 | return x, y 57 | kp = -1 / k 58 | bp = p[1] - kp * p[0] 59 | x = (bp - b) / (k - kp) 60 | y = kp * x + bp 61 | return x, y 62 | raise ValueError("The coordinate value length can only be two") 63 | raise TypeError("The type of coordinate value can only be an array") 64 | 65 | 66 | class Triangle: 67 | def __init__(self, a: arr, b: arr, c: arr): 68 | if isinstance(a, (list, tuple)) and isinstance(b, (list, tuple)) and isinstance(c, (list, tuple)): 69 | if len(a) == len(b) == len(c) == 2: 70 | self.a, self.b, self.c = a, b, c 71 | else: 72 | raise ValueError("The coordinate value length can only be two") 73 | else: 74 | raise TypeError("The type of coordinate value can only be an array") 75 | 76 | def __repr__(self) -> str: 77 | return "Triangle({}, {}, {})".format(self.a, self.b, self.c) 78 | 79 | def length(self) -> tuple: 80 | return distance(self.a, self.b), distance(self.b, self.c), distance(self.c, self.a) 81 | 82 | def perimeter(self) -> float: 83 | return sum(self.length()) 84 | 85 | def area(self) -> float: 86 | return abs((self.b[0] - self.a[0]) * (self.c[1] - self.a[1]) 87 | - (self.b[1] - self.a[1]) * (self.c[0] - self.a[0])) / 2 88 | 89 | def incenter(self) -> tuple: 90 | a, b, c = self.length() 91 | x = (a * self.a[0] + b * self.b[0] + c * self.c[0]) / (a + b + c) 92 | y = (a * self.a[1] + b * self.b[1] + c * self.c[1]) / (a + b + c) 93 | return x, y 94 | 95 | def circumcenter(self) -> tuple: 96 | x = (self.a[0] * self.b[0] + self.b[0] * self.c[0] + self.c[0] * self.a[0]) / ( 97 | self.a[0] + self.b[0] + self.c[0]) 98 | y = (self.a[1] * self.b[1] + self.b[1] * self.c[1] + self.c[1] * self.a[1]) / ( 99 | self.a[1] + self.b[1] + self.c[1]) 100 | return x, y 101 | 102 | def centroid(self) -> tuple: 103 | x = (self.a[0] + self.b[0] + self.c[0]) / 3 104 | y = (self.a[1] + self.b[1] + self.c[1]) / 3 105 | return x, y 106 | 107 | def orthocenter(self) -> tuple: 108 | if self.area() == 0: 109 | raise ValueError("A triangle with zero area has no orthogonal center") 110 | a, b, c = self.a, self.b, self.c 111 | divisor = a[0] * b[1] - a[0] * c[1] - a[1] * b[0] + a[1] * c[0] + b[0] * c[1] - b[1] * c[0] 112 | x = (-a[0] * a[1] * b[0] + a[0] * a[1] * c[0] + a[0] * b[0] * b[1] - a[0] * c[0] * c[1] - a[1] ** 2 * b[1] + a[ 113 | 1] ** 2 * c[1] + a[1] * b[1] ** 2 - a[1] * c[1] ** 2 - b[0] * b[1] * c[0] + b[0] * c[0] * c[1] - b[1] ** 2 * 114 | c[1] + b[1] * c[1] ** 2) / divisor 115 | y = (a[0] ** 2 * b[0] - a[0] ** 2 * c[0] + a[0] * a[1] * b[1] - a[0] * a[1] * c[1] - a[0] * b[0] ** 2 + a[0] * 116 | c[0] ** 2 - a[1] * b[0] * b[1] + a[1] * c[0] * c[1] + b[0] ** 2 * c[0] + b[0] * b[1] * c[1] - b[0] * c[ 117 | 0] ** 2 - b[1] * c[0] * c[1]) / divisor 118 | return x, y 119 | 120 | def is_isosceles(self, error: real = 0) -> bool: 121 | _length = self.length() 122 | return True if abs(_length[0] - _length[1]) <= error or abs(_length[1] - _length[2]) <= error or abs( 123 | _length[2] - _length[0]) <= error else False 124 | 125 | def is_equilateral(self, error: real = 0) -> bool: 126 | _length = self.length() 127 | return True if abs(_length[0] - _length[1]) <= error and abs(_length[1] - _length[2]) <= error and abs( 128 | _length[2] - _length[0]) <= error else False 129 | 130 | def is_right(self, error: real = 0) -> bool: 131 | _length = sorted(self.length()) 132 | return True if abs( 133 | _length[0] * _length[0] + _length[1] * _length[1] - _length[2] * _length[2]) <= error else False 134 | 135 | 136 | class Quadrilateral: 137 | def __init__(self, a: arr, b: arr, c: arr, d: arr): 138 | if isinstance(a, (list, tuple)) and isinstance(b, (list, tuple)) and isinstance( 139 | c, (list, tuple)) and isinstance(d, (list, tuple)): 140 | if len(a) == len(b) == len(c) == len(d) == 2: 141 | self.a, self.b, self.c, self.d = a, b, c, d 142 | else: 143 | raise ValueError("The coordinate value length can only be two") 144 | else: 145 | raise TypeError("The type of coordinate value can only be an array") 146 | 147 | def __repr__(self) -> str: 148 | return "Quadrilateral({}, {}, {}, {})".format(self.a, self.b, self.c, self.d) 149 | 150 | def length(self) -> tuple: 151 | return distance(self.a, self.b), distance(self.b, self.c), distance(self.c, self.d), distance(self.d, self.a) 152 | 153 | def perimeter(self) -> float: 154 | return sum(self.length()) 155 | 156 | def area(self) -> float: 157 | return abs((self.b[0] - self.a[0]) * (self.c[1] - self.a[1]) - (self.b[1] - self.a[1]) * ( 158 | self.c[0] - self.a[0]) - (self.d[0] - self.a[0]) * (self.c[1] - self.a[1]) + ( 159 | self.d[1] - self.a[1]) * (self.c[0] - self.a[0])) / 2 160 | 161 | def centroid(self) -> tuple: 162 | _ = Triangle(self.a, self.b, self.c) 163 | x1, y1 = _.centroid() 164 | s1 = _.area() 165 | _ = Triangle(self.a, self.d, self.c) 166 | x2, y2 = _.centroid() 167 | s2 = _.area() 168 | x = (x1 * s1 + x2 * s2) / (s1 + s2) 169 | y = (y1 * s1 + y2 * s2) / (s1 + s2) 170 | return x, y 171 | 172 | def __slopes(self): 173 | return [Line(self.a, self.b).expr()[0], Line(self.c, self.d).expr()[0], Line(self.b, self.c).expr()[0], 174 | Line(self.d, self.a).expr()[0]] 175 | 176 | def is_trapezoidal(self, error: real = 0) -> bool: 177 | slopes = self.__slopes() 178 | slopes = [_ if _ is not None else 0 for _ in slopes] 179 | return True if abs(slopes[0] - slopes[1]) <= error or abs(slopes[2] - slopes[3]) <= error else False 180 | 181 | def is_parallelogram(self, error: real = 0) -> bool: 182 | slopes = self.__slopes() 183 | slopes = [_ if _ is not None else 0 for _ in slopes] 184 | return True if abs(slopes[0] - slopes[1]) <= error and abs(slopes[2] - slopes[3]) <= error else False 185 | 186 | def is_diamond(self, error: real = 0) -> bool: 187 | _length = self.length() 188 | return True if abs(_length[0] - _length[1]) <= error and abs(_length[1] - _length[2]) <= error and abs( 189 | _length[2] - _length[3]) <= error and abs(_length[3] - _length[0]) <= error else False 190 | 191 | def is_rectangular(self, error: real = 0) -> bool: 192 | slopes = [Line(self.a, self.b).expr()[0], Line(self.b, self.c).expr()[0]] 193 | if None in slopes: 194 | slopes.remove(None) 195 | if slopes: 196 | if abs(slopes[0]) <= error: 197 | return True 198 | return False 199 | return False 200 | return True if abs(1 / max(slopes) + min(slopes)) <= error and self.is_parallelogram(error) else False 201 | 202 | def is_square(self, error: real = 0) -> bool: 203 | return True if self.is_diamond(error) and self.is_rectangular(error) else False 204 | 205 | 206 | class Polygon: 207 | def __init__(self, *p: arr): 208 | if len(p) < 3: 209 | raise ValueError("The number of vertices in a polygon cannot be less than three") 210 | for item in p: 211 | if isinstance(item, (list, tuple)): 212 | if len(item) != 2: 213 | raise ValueError("The coordinate value length can only be two") 214 | else: 215 | raise TypeError("The type of coordinate value can only be an array") 216 | self.points = p 217 | 218 | def __repr__(self) -> str: 219 | return "Polygon(" + ", ".join(str(_) for _ in self.points) + ")" 220 | 221 | def length(self) -> tuple: 222 | return tuple(distance(*item) for item in zip(self.points, self.points[1:] + (self.points[0],))) 223 | 224 | def perimeter(self) -> float: 225 | return sum(self.length()) 226 | 227 | def area(self) -> float: 228 | _area = 0 229 | q = self.points[-1] 230 | for p in self.points: 231 | _area += p[0] * q[1] - p[1] * q[0] 232 | q = p 233 | return abs(_area) / 2 234 | 235 | def centroid(self) -> tuple: 236 | raise NotImplementedError 237 | 238 | 239 | class Circle: 240 | def __init__(self, center: arr, radius: real): 241 | if isinstance(center, (list, tuple)): 242 | if len(center) == 2: 243 | self.center = center 244 | else: 245 | raise ValueError("The coordinate value length can only be two") 246 | else: 247 | raise TypeError("The type of coordinate value can only be an array") 248 | if isinstance(radius, (int, float)): 249 | self.radius = radius 250 | else: 251 | raise TypeError("The type of length value can only be real numbers") 252 | 253 | def __repr__(self) -> str: 254 | return "Circle({}, {})".format(self.center, self.radius) 255 | 256 | def perimeter(self) -> float: 257 | return 6.283185307179586 * self.radius 258 | 259 | def area(self) -> float: 260 | return 3.141592653589793 * self.radius * self.radius 261 | 262 | def expr(self) -> list: 263 | return [self.center[0], self.center[1], self.radius] 264 | 265 | def chord(self, radian: real) -> float: 266 | from math import sin 267 | return 2 * self.radius * sin(radian / 2) 268 | 269 | 270 | def distance(g1, g2, error: real = 0) -> float: 271 | geom = Point, Line, Triangle, Quadrilateral, Polygon, Circle 272 | if isinstance(g1, (list, tuple)) and isinstance(g2, (list, tuple)): 273 | return ((g1[0] - g2[0]) ** 2 + (g1[1] - g2[1]) ** 2) ** 0.5 274 | if isinstance(g1, (list, tuple)): 275 | g1 = Point(g1) 276 | if isinstance(g2, (list, tuple)): 277 | g2 = Point(g2) 278 | if isinstance(g1, geom) and isinstance(g2, geom): 279 | if geom.index(type(g2)) < geom.index(type(g1)): 280 | return distance(g2, g1) 281 | elif isinstance(g1, Point) and isinstance(g2, Point): 282 | p1, p2 = g1.p, g2.p 283 | return ((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2) ** 0.5 284 | elif isinstance(g1, Point) and isinstance(g2, Line): 285 | return Line(g1.p, g2.vertical(g1.p)).length() 286 | elif isinstance(g1, Line) and isinstance(g2, Line): 287 | e1, e2 = g1.expr(), g2.expr() 288 | if e1 == e2 == [None, None]: 289 | return abs(g1.a[0] - g2.a[0]) 290 | elif abs(e1[0] - e2[0]) <= error: 291 | return abs(e1[1] - e2[1]) / (((e1[0] + e2[0]) / 2) ** 2 + 1) ** 0.5 292 | return 0 293 | elif isinstance(g1, Point) and isinstance(g2, Triangle): 294 | to_vertexes = [distance(g1.p, g2.a), distance(g1.p, g2.b), distance(g1.p, g2.c)] 295 | points = [Line(g2.a, g2.b).vertical(g1.p), Line(g2.b, g2.c).vertical(g1.p), Line(g2.c, g2.a).vertical(g1.p)] 296 | to_sides = [distance(g1.p, _) for _ in points] 297 | minimum = min(min(to_vertexes), min(to_sides)) 298 | if min(to_sides) < min(to_vertexes): 299 | def determine(): 300 | index = to_sides.index(min(to_sides)) 301 | if (index == 0 and min(g2.a[0], g2.b[0]) < points[index][0] < max(g2.a[0], g2.b[0]) 302 | and min(g2.a[1], g2.b[1]) < points[index][1] < max(g2.a[1], g2.b[1])) or ( 303 | index == 1 and min(g2.b[0], g2.c[0]) < points[index][0] < max(g2.b[0], g2.c[0]) 304 | and min(g2.b[1], g2.c[1]) < points[index][1] < max(g2.b[1], g2.c[1])) or ( 305 | index == 2 and min(g2.c[0], g2.a[0]) < points[index][0] < max(g2.c[0], g2.a[0]) 306 | and min(g2.c[1], g2.a[1]) < points[index][1] < max(g2.c[1], g2.a[1])): 307 | return True 308 | return False 309 | 310 | if determine(): 311 | return minimum 312 | else: 313 | to_sides[to_sides.index(min(to_sides))] = float("inf") 314 | minimum = min(min(to_vertexes), min(to_sides)) 315 | if determine(): 316 | return minimum 317 | to_sides[to_sides.index(min(to_sides))] = float("inf") 318 | minimum = min(min(to_vertexes), min(to_sides)) 319 | return minimum 320 | elif isinstance(g1, Line) and isinstance(g2, Triangle): 321 | pass 322 | elif isinstance(g1, Triangle) and isinstance(g2, Triangle): 323 | ... 324 | raise NotImplementedError 325 | raise TypeError("The input parameters can only be geometric shapes") 326 | -------------------------------------------------------------------------------- /pypynum/graphs.py: -------------------------------------------------------------------------------- 1 | class BaseGraph: 2 | def __init__(self): 3 | self.graph = {} 4 | 5 | def __repr__(self): 6 | return "{}({})".format(self.__class__.__name__, self.to_list()) 7 | 8 | def to_list(self): 9 | if isinstance(self, BaseWeGraph): 10 | return sorted(map(lambda item: (item[0], sorted(item[1].items())), self.graph.items())) 11 | else: 12 | return sorted(self.graph.items()) 13 | 14 | def add_vertex(self, vertex): 15 | if vertex not in self.graph: 16 | self.graph[vertex] = [] 17 | 18 | def remove_vertex(self, vertex): 19 | if vertex in self.graph: 20 | neighbors = self.graph[vertex] 21 | del self.graph[vertex] 22 | for neighbor in neighbors: 23 | self.graph[neighbor].remove(vertex) 24 | 25 | def has_vertex(self, vertex): 26 | return vertex in self.graph 27 | 28 | def has_edge(self, vertex1, vertex2): 29 | return vertex1 in self.graph and vertex2 in self.graph[vertex1] 30 | 31 | def get_edges(self, vertex): 32 | return self.graph.get(vertex, {}) 33 | 34 | def all_vertices(self): 35 | return sorted(set(list(self.graph) + sum([list(self.graph[vertex]) for vertex in self.graph], []))) 36 | 37 | def all_edges(self): 38 | edges = [] 39 | for vertex, neighbours in self.graph.items(): 40 | for neighbour in neighbours: 41 | edges.append((vertex, neighbour)) 42 | return sorted(edges) 43 | 44 | def to_adjacency_matrix(self): 45 | vertices = self.all_vertices() 46 | num_vertices = len(vertices) 47 | matrix = [[0] * num_vertices for _ in range(num_vertices)] 48 | if isinstance(self, BaseWeGraph): 49 | for u in self.graph: 50 | for v in self.graph[u].items(): 51 | try: 52 | v, w = v 53 | matrix_index_u = vertices.index(u) 54 | matrix_index_v = vertices.index(v) 55 | matrix[matrix_index_u][matrix_index_v] = w 56 | except ValueError: 57 | continue 58 | else: 59 | for u in self.graph: 60 | for v in self.graph[u]: 61 | try: 62 | matrix_index_u = vertices.index(u) 63 | matrix_index_v = vertices.index(v) 64 | matrix[matrix_index_u][matrix_index_v] = 1 65 | except ValueError: 66 | continue 67 | return [[self.__class__.__name__] + vertices] + [[vertex] + row for vertex, row in zip(vertices, matrix)] 68 | 69 | def dfs(self, start_vertex, visited=None): 70 | if visited is None: 71 | visited = set() 72 | visited.add(start_vertex) 73 | search_list = [start_vertex] 74 | if start_vertex not in self.graph: 75 | return [start_vertex] 76 | for neighbour in self.graph[start_vertex]: 77 | if neighbour not in visited: 78 | search_list.extend(self.dfs(neighbour, visited)) 79 | return search_list 80 | 81 | def bfs(self, start_vertex): 82 | visited = {start_vertex} 83 | queue = [start_vertex] 84 | search_list = [start_vertex] 85 | while queue: 86 | vertex = queue.pop(0) 87 | if vertex in self.graph: 88 | for neighbour in self.graph[vertex]: 89 | if neighbour not in visited: 90 | visited.add(neighbour) 91 | queue.append(neighbour) 92 | search_list.append(neighbour) 93 | return search_list 94 | 95 | def is_connected(self): 96 | visited = set() 97 | queue = [] 98 | for node in self.graph: 99 | if node not in visited: 100 | visited.add(node) 101 | queue.append(node) 102 | break 103 | while queue: 104 | node = queue.pop(0) 105 | for neighbor in self.graph[node]: 106 | if neighbor not in visited: 107 | visited.add(neighbor) 108 | queue.append(neighbor) 109 | return len(visited) == len(self.graph) 110 | 111 | def is_complete(self): 112 | num_nodes = len(self.graph) 113 | return all([len(self.graph[node]) == num_nodes - 1 for node in self.graph]) 114 | 115 | 116 | class DiGraph(BaseGraph): 117 | def add_edge(self, vertex1, vertex2): 118 | if vertex1 in self.graph: 119 | self.graph[vertex1].append(vertex2) 120 | else: 121 | self.graph[vertex1] = [vertex2] 122 | 123 | def remove_edge(self, vertex1, vertex2): 124 | if vertex1 in self.graph and vertex2 in self.graph[vertex1]: 125 | self.graph[vertex1].remove(vertex2) 126 | if not self.graph[vertex1]: 127 | del self.graph[vertex1] 128 | 129 | def __str__(self): 130 | edges = [] 131 | for vertex, neighbours in self.graph.items(): 132 | for neighbour in neighbours: 133 | edges.append("{} -> {}".format(vertex, neighbour)) 134 | return "\n".join(sorted(edges)) 135 | 136 | 137 | class UnGraph(BaseGraph): 138 | def add_edge(self, vertex1, vertex2): 139 | if vertex1 in self.graph: 140 | self.graph[vertex1].append(vertex2) 141 | else: 142 | self.graph[vertex1] = [vertex2] 143 | if vertex2 in self.graph: 144 | self.graph[vertex2].append(vertex1) 145 | else: 146 | self.graph[vertex2] = [vertex1] 147 | 148 | def remove_edge(self, vertex1, vertex2): 149 | if vertex1 in self.graph and vertex2 in self.graph: 150 | if vertex2 in self.graph[vertex1]: 151 | self.graph[vertex1].remove(vertex2) 152 | if vertex1 in self.graph[vertex2]: 153 | self.graph[vertex2].remove(vertex1) 154 | if not self.graph[vertex1]: 155 | del self.graph[vertex1] 156 | if not self.graph[vertex2]: 157 | del self.graph[vertex2] 158 | 159 | def __str__(self): 160 | edges = [] 161 | for vertex, neighbours in self.graph.items(): 162 | for neighbour in neighbours: 163 | edges.append("{} -- {}".format(vertex, neighbour)) 164 | return "\n".join(sorted(edges)) 165 | 166 | 167 | class BaseWeGraph(BaseGraph): 168 | def add_edge(self, vertex1, vertex2, weight=0): 169 | raise NotImplementedError 170 | 171 | def remove_edge(self, vertex1, vertex2): 172 | raise NotImplementedError 173 | 174 | def add_vertex(self, vertex): 175 | if vertex not in self.graph: 176 | self.graph[vertex] = {} 177 | 178 | def remove_vertex(self, vertex): 179 | if vertex in self.graph: 180 | neighbors = self.graph[vertex] 181 | del self.graph[vertex] 182 | for neighbor in neighbors: 183 | self.remove_edge(neighbor, vertex) 184 | 185 | def get_edge_weight(self, vertex1, vertex2): 186 | if self.has_edge(vertex1, vertex2): 187 | return self.graph[vertex1][vertex2] 188 | 189 | def get_in_degree_weight_sum(self, vertex): 190 | return sum([self.get_edge_weight(neighbor, vertex) 191 | for neighbor in self.graph if vertex in self.graph[neighbor]]) 192 | 193 | def get_out_degree_weight_sum(self, vertex): 194 | return sum([self.get_edge_weight(vertex, neighbor) for neighbor in self.graph.get(vertex, {})]) 195 | 196 | def dijkstra(self, start): 197 | distances = {vertex: float("inf") for vertex in self.graph} 198 | distances[start] = 0 199 | predecessors = {vertex: None for vertex in self.graph} 200 | queue = [(0, start)] 201 | visited = set() 202 | while queue: 203 | current_distance, current_vertex = min(queue, key=lambda x: x[0]) 204 | if current_vertex in visited: 205 | queue.remove((current_distance, current_vertex)) 206 | continue 207 | visited.add(current_vertex) 208 | queue.remove((current_distance, current_vertex)) 209 | for neighbor, weight in self.graph[current_vertex].items(): 210 | distance = current_distance + weight 211 | if distance < distances[neighbor]: 212 | distances[neighbor] = distance 213 | predecessors[neighbor] = current_vertex 214 | queue.append((distance, neighbor)) 215 | return distances, predecessors 216 | 217 | def reconstruct_path(self, start, end): 218 | distances, predecessors = self.dijkstra(start) 219 | path = [] 220 | current = end 221 | while current is not None: 222 | path.append(current) 223 | current = predecessors.get(current) 224 | return path[::-1] if path else None 225 | 226 | 227 | class WeDiGraph(BaseWeGraph): 228 | def add_edge(self, vertex1, vertex2, weight=0): 229 | if vertex1 not in self.graph: 230 | self.graph[vertex1] = {} 231 | self.graph[vertex1][vertex2] = weight 232 | 233 | def remove_edge(self, vertex1, vertex2): 234 | if vertex1 in self.graph and vertex2 in self.graph[vertex1]: 235 | del self.graph[vertex1][vertex2] 236 | if not self.graph[vertex1]: 237 | del self.graph[vertex1] 238 | 239 | def __str__(self): 240 | edges = [] 241 | for vertex, neighbours in self.graph.items(): 242 | for neighbour, weight in neighbours.items(): 243 | edges.append("{} -[{}]> {}".format(vertex, weight, neighbour)) 244 | return "\n".join(sorted(edges)) 245 | 246 | 247 | class WeUnGraph(BaseWeGraph): 248 | def add_edge(self, vertex1, vertex2, weight=0): 249 | if vertex1 not in self.graph: 250 | self.graph[vertex1] = {} 251 | if vertex2 not in self.graph: 252 | self.graph[vertex2] = {} 253 | self.graph[vertex1][vertex2] = weight 254 | self.graph[vertex2][vertex1] = weight 255 | 256 | def remove_edge(self, vertex1, vertex2): 257 | if vertex1 in self.graph and vertex2 in self.graph[vertex1]: 258 | del self.graph[vertex1][vertex2] 259 | del self.graph[vertex2][vertex1] 260 | if not self.graph[vertex1]: 261 | del self.graph[vertex1] 262 | if vertex1 != vertex2 and not self.graph[vertex2]: 263 | del self.graph[vertex2] 264 | 265 | def __str__(self): 266 | edges = [] 267 | for vertex, neighbours in self.graph.items(): 268 | for neighbour, weight in neighbours.items(): 269 | edges.append("{} -[{}]- {}".format(vertex, weight, neighbour)) 270 | return "\n".join(sorted(edges)) 271 | -------------------------------------------------------------------------------- /pypynum/groups.py: -------------------------------------------------------------------------------- 1 | from .ufuncs import * 2 | 3 | 4 | class Group: 5 | def __init__(self, data, operation=multiply): 6 | self.__data = set(data) 7 | self.__operation = None 8 | self.setop(operation) 9 | if any([any([not hasattr(elem, attr) for attr in ["__add__", "__sub__", "__mul__", "__truediv__"]]) 10 | for elem in self.__data]): 11 | raise TypeError("All elements in the group must be computable") 12 | 13 | def __repr__(self): 14 | return "G{" + ", ".join(sorted([repr(elem) for elem in self.__data])) + "}" 15 | 16 | def elements(self): 17 | return set(self.__data) 18 | 19 | def getop(self): 20 | return self.__operation 21 | 22 | def setop(self, operation): 23 | if not hasattr(operation, "__call__"): 24 | raise TypeError("The operation function must be callable") 25 | if operation.__code__.co_argcount != 2: 26 | raise TypeError("The operation function must have two arguments") 27 | self.__operation = operation 28 | 29 | def is_closed(self, modulus=None): 30 | for a in self.__data: 31 | for b in self.__data: 32 | result = self.__operation(a, b) 33 | if modulus is not None: 34 | result %= modulus 35 | if result not in self.__data: 36 | return False 37 | return True 38 | 39 | def is_associative(self, modulus=None): 40 | for a in self.__data: 41 | for b in self.__data: 42 | for c in self.__data: 43 | if modulus is None: 44 | if self.__operation(self.__operation(a, b), c) != self.__operation(a, self.__operation(b, c)): 45 | return False 46 | else: 47 | if self.__operation(self.__operation(a, b) % modulus, c) % modulus != self.__operation( 48 | a, self.__operation(b, c) % modulus) % modulus: 49 | return False 50 | return True 51 | 52 | def has_identity(self): 53 | identity = None 54 | for e in self.__data: 55 | if all([self.__operation(e, a) == a and self.__operation(a, e) == a for a in self.__data]): 56 | if identity is not None: 57 | return False 58 | identity = e 59 | return identity is not None 60 | 61 | def has_inverses(self): 62 | identity = self.identity() 63 | if identity is None: 64 | return False 65 | for a in self.__data: 66 | for b in self.__data: 67 | if self.__operation(a, b) == identity and self.__operation(b, a) == identity: 68 | break 69 | else: 70 | return False 71 | return True 72 | 73 | def identity(self): 74 | for e in self.__data: 75 | if all([self.__operation(e, a) == a and self.__operation(a, e) == a for a in self.__data]): 76 | return e 77 | return None 78 | 79 | def is_semigroup(self, modulus=None): 80 | return True if self.is_closed(modulus) and self.is_associative(modulus) else False 81 | 82 | def is_monoid(self, modulus=None): 83 | return True if self.is_closed(modulus) and self.is_associative(modulus) and self.has_identity() else False 84 | 85 | def is_group(self, modulus=None): 86 | return True if all([self.is_closed(modulus), self.is_associative(modulus), 87 | self.has_identity(), self.has_inverses()]) else False 88 | 89 | def order(self): 90 | return len(self.__data) 91 | 92 | def is_supergroup(self, other, modulus=None): 93 | if not isinstance(other, Group): 94 | return False 95 | if not other.is_group(modulus): 96 | return False 97 | if self.getop() != other.getop(): 98 | return False 99 | return self.elements().issuperset(other.elements()) 100 | 101 | def is_subgroup(self, other, modulus=None): 102 | if not isinstance(other, Group): 103 | return False 104 | if not other.is_group(modulus): 105 | return False 106 | if self.getop() != other.getop(): 107 | return False 108 | return self.elements().issubset(other.elements()) 109 | 110 | def __eq__(self, other): 111 | return self.__data == other.__data 112 | 113 | def __ne__(self, other): 114 | return self.__data != other.__data 115 | 116 | 117 | def group(data): 118 | return Group(data) 119 | -------------------------------------------------------------------------------- /pypynum/hypcmpnms.py: -------------------------------------------------------------------------------- 1 | from .matrices import Matrix 2 | from .types import ShapeError, Union, real 3 | 4 | 5 | class Quaternion: 6 | def __init__(self, w: real, x: real, y: real, z: real): 7 | self.__quaternion = w, x, y, z 8 | self.__w, self.__x, self.__y, self.__z = self.__quaternion 9 | 10 | def __repr__(self): 11 | return "({}+{}i+{}j+{}k)".format(*self.__quaternion).replace("+-", "-") 12 | 13 | def __add__(self, other: "Quaternion"): 14 | return Quaternion(*tuple(map(lambda i, j: i + j, self.__quaternion, other.__quaternion))) 15 | 16 | def __sub__(self, other: "Quaternion"): 17 | return Quaternion(*tuple(map(lambda i, j: i - j, self.__quaternion, other.__quaternion))) 18 | 19 | def __mul__(self, other: "int | float | Quaternion"): 20 | if isinstance(other, Quaternion): 21 | w = self.__w * other.__w - self.__x * other.__x - self.__y * other.__y - self.__z * other.__z 22 | x = self.__w * other.__x + self.__x * other.__w + self.__y * other.__z - self.__z * other.__y 23 | y = self.__w * other.__y + self.__y * other.__w + self.__z * other.__x - self.__x * other.__z 24 | z = self.__w * other.__z + self.__z * other.__w + self.__x * other.__y - self.__y * other.__x 25 | return Quaternion(w, x, y, z) 26 | elif isinstance(other, (int, float)): 27 | return Quaternion(*[other * i for i in self.__quaternion]) 28 | else: 29 | return NotImplemented 30 | 31 | def __rmul__(self, other: real): 32 | if isinstance(other, (int, float)): 33 | return self.__mul__(other) 34 | else: 35 | return NotImplemented 36 | 37 | def __truediv__(self, other: "int | float | Quaternion"): 38 | if isinstance(other, Quaternion): 39 | return self * other.inverse() 40 | elif isinstance(other, (int, float)): 41 | return Quaternion(*[i / other for i in self.__quaternion]) 42 | else: 43 | return NotImplemented 44 | 45 | def __round__(self, n: int = None): 46 | if n is None: 47 | return Quaternion(*[round(i) for i in self.__quaternion]) 48 | return Quaternion(*[round(i, n) for i in self.__quaternion]) 49 | 50 | def __abs__(self): 51 | return self.norm() 52 | 53 | def __eq__(self, other: "Quaternion"): 54 | return self.__quaternion == other.__quaternion 55 | 56 | def __iadd__(self, other: "Quaternion"): 57 | return self + other 58 | 59 | def __isub__(self, other: "Quaternion"): 60 | return self - other 61 | 62 | def __imul__(self, other: "Quaternion"): 63 | return self * other 64 | 65 | def __itruediv__(self, other: "Quaternion"): 66 | return self / other 67 | 68 | def __setitem__(self, key: int, value: real): 69 | q = list(self.__quaternion) 70 | q[key] = value 71 | self.__quaternion = tuple(q) 72 | self.__w, self.__x, self.__y, self.__z = self.__quaternion 73 | 74 | def __pos__(self): 75 | return Quaternion(*self.__quaternion) 76 | 77 | def __neg__(self): 78 | return Quaternion(*[-i for i in self.__quaternion]) 79 | 80 | def data(self): 81 | return list(self.__quaternion) 82 | 83 | def norm(self): 84 | return sum([i ** 2 for i in self.__quaternion]) ** 0.5 85 | 86 | def conjugate(self): 87 | return Quaternion(self.__w, -self.__x, -self.__y, -self.__z) 88 | 89 | def normalize(self): 90 | norm = self.norm() 91 | return Quaternion(*[i / norm for i in self.__quaternion]) 92 | 93 | def inverse(self): 94 | norm2 = sum([i ** 2 for i in self.__quaternion]) 95 | if norm2 == 0: 96 | raise ZeroDivisionError("Cannot invert zero quaternion") 97 | return Quaternion(*[i / norm2 for i in self.conjugate().__quaternion]) 98 | 99 | 100 | def quat(w: real = 0, x: real = 0, y: real = 0, z: real = 0) -> Quaternion: 101 | return Quaternion(w, x, y, z) 102 | 103 | 104 | class Euler: 105 | def __init__(self, y: real, p: real, r: real): 106 | self.__angles = y, p, r 107 | self.__y, self.__p, self.__r = self.__angles 108 | 109 | def __repr__(self): 110 | return "ypr({},{},{})".format(*self.__angles) 111 | 112 | def __round__(self, n: int = None): 113 | if n is None: 114 | return Quaternion(*[round(i) for i in self.__angles]) 115 | return Quaternion(*[round(i, n) for i in self.__angles]) 116 | 117 | def __setitem__(self, key: int, value: real): 118 | e = list(self.__angles) 119 | e[key] = value 120 | self.__angles = tuple(e) 121 | self.__y, self.__p, self.__r = self.__angles 122 | 123 | def data(self): 124 | return list(self.__angles) 125 | 126 | 127 | def euler(yaw: real = 0, pitch: real = 0, roll: real = 0) -> Euler: 128 | return Euler(yaw, pitch, roll) 129 | 130 | 131 | def convert(data: Union[Quaternion, Matrix, Euler], to: str) -> Union[Quaternion, Matrix, Euler]: 132 | if to not in ["Q", "M", "E"]: 133 | raise ValueError("The parameter 'to' can only be 'Q', 'M', or 'E'") 134 | elif isinstance(data, Euler): 135 | if to == "Q": 136 | from math import cos, sin 137 | q = [x / 2.0 for x in data.data()] 138 | w = sin(q[0]) * sin(q[1]) * sin(q[2]) + cos(q[0]) * cos(q[1]) * cos(q[2]) 139 | x = -cos(q[0]) * sin(q[1]) * sin(q[2]) + sin(q[0]) * cos(q[1]) * cos(q[2]) 140 | y = sin(q[0]) * cos(q[1]) * sin(q[2]) + cos(q[0]) * sin(q[1]) * cos(q[2]) 141 | z = -sin(q[0]) * sin(q[1]) * cos(q[2]) + cos(q[0]) * cos(q[1]) * sin(q[2]) 142 | return Quaternion(w, x, y, z) 143 | elif isinstance(data, Matrix): 144 | if data.shape != [3, 3]: 145 | raise ShapeError("The rotation matrix must be a third-order square matrix") 146 | elif to == "Q": 147 | [[m11, m12, m13], [m21, m22, m23], [m31, m32, m33]] = data 148 | squares = [m11 + m22 + m33, m11 - m22 - m33, m22 - m11 - m33, m33 - m11 - m22] 149 | max_square_sum = max(squares) 150 | max_index = squares.index(max_square_sum) 151 | sqrt_val = (max_square_sum + 1.0) ** 0.5 * 0.5 152 | mult = 0.25 / sqrt_val 153 | if max_index == 0: 154 | w = max_index 155 | x = (m23 - m32) * mult 156 | y = (m31 - m13) * mult 157 | z = (m12 - m21) * mult 158 | elif max_index == 1: 159 | x = max_index 160 | w = (m23 - m32) * mult 161 | y = (m12 + m21) * mult 162 | z = (m31 + m13) * mult 163 | elif max_index == 2: 164 | y = max_index 165 | w = (m31 - m13) * mult 166 | x = (m12 + m21) * mult 167 | z = (m23 + m32) * mult 168 | else: 169 | z = max_index 170 | w = (m12 - m21) * mult 171 | x = (m31 + m13) * mult 172 | y = (m23 + m32) * mult 173 | return Quaternion(w, x, y, z) 174 | elif isinstance(data, Quaternion): 175 | if to == "M": 176 | w, x, y, z = data.normalize().data() 177 | return Matrix([[1 - 2 * (y ** 2 + z ** 2), 2 * (x * y - z * w), 2 * (x * z + y * w)], 178 | [2 * (x * y + z * w), 1 - 2 * (x ** 2 + z ** 2), 2 * (y * z - x * w)], 179 | [2 * (x * z - y * w), 2 * (y * z + x * w), 1 - 2 * (x ** 2 + y ** 2)]], False) 180 | elif to == "E": 181 | from math import asin, atan2 182 | w, x, y, z = data.normalize().data() 183 | t0 = 2.0 * (w * x + y * z) 184 | t1 = 1.0 - 2.0 * (x * x + y * y) 185 | yaw = atan2(t0, t1) 186 | t2 = 2.0 * (w * y - z * x) 187 | pitch = asin(t2) 188 | t3 = 2.0 * (w * z + x * y) 189 | t4 = 1.0 - 2.0 * (y * y + z * z) 190 | roll = atan2(t3, t4) 191 | return Euler(yaw, pitch, roll) 192 | else: 193 | raise TypeError("Data can only be Euler or Matrix or Quaternion") 194 | 195 | 196 | class Octonion: 197 | def __init__(self, s: real, t: real, u: real, v: real, w: real, x: real, y: real, z: real): 198 | self.__octonion = s, t, u, v, w, x, y, z 199 | self.__s, self.__t, self.__u, self.__v, self.__w, self.__x, self.__y, self.__z = self.__octonion 200 | 201 | def __repr__(self): 202 | return "({}+{}i+{}j+{}k+{}l+{}m+{}n+{}o)".format(*self.__octonion).replace("+-", "-") 203 | 204 | def __add__(self, other: "Octonion"): 205 | ss, st, su, sv, sw, sx, sy, sz = self.__octonion 206 | os, ot, ou, ov, ow, ox, oy, oz = other.__octonion 207 | return Octonion(ss + os, st + ot, su + ou, sv + ov, sw + ow, sx + ox, sy + oy, sz + oz) 208 | 209 | def __sub__(self, other: "Octonion"): 210 | ss, st, su, sv, sw, sx, sy, sz = self.__octonion 211 | os, ot, ou, ov, ow, ox, oy, oz = other.__octonion 212 | return Octonion(ss - os, st - ot, su - ou, sv - ov, sw - ow, sx - ox, sy - oy, sz - oz) 213 | 214 | def __mul__(self, other: "int | float | Octonion"): 215 | if isinstance(other, Octonion): 216 | ss, st, su, sv, sw, sx, sy, sz = self.__octonion 217 | os, ot, ou, ov, ow, ox, oy, oz = other.__octonion 218 | s = ss * os - st * ot - su * ou - sv * ov - sw * ow - sx * ox - sy * oy - sz * oz 219 | t = ss * ot + st * os + su * ov - sv * ou + sw * ox - sx * ow - sy * oz + sz * oy 220 | u = ss * ou - st * ov + su * os + sv * ot + sw * oy + sx * oz - sy * ow - sz * ox 221 | v = ss * ov + st * ou - su * ot + sv * os + sw * oz - sx * oy + sy * ox - sz * ow 222 | w = ss * ow - st * ox - su * oy - sv * oz + sw * os + sx * ot + sy * ou + sz * ov 223 | x = ss * ox + st * ow - su * oz + sv * oy - sw * ot + sx * os - sy * ov + sz * ou 224 | y = ss * oy + st * oz + su * ow - sv * ox - sw * ou + sx * ov + sy * os - sz * ot 225 | z = ss * oz - st * oy + su * ox + sv * ow - sw * ov - sx * ou + sy * ot + sz * os 226 | return Octonion(s, t, u, v, w, x, y, z) 227 | elif isinstance(other, (int, float)): 228 | ss, st, su, sv, sw, sx, sy, sz = self.__octonion 229 | return Octonion(ss * other, st * other, su * other, sv * other, 230 | sw * other, sx * other, sy * other, sz * other) 231 | else: 232 | return NotImplemented 233 | 234 | def __rmul__(self, other: real): 235 | if isinstance(other, (int, float)): 236 | return self.__mul__(other) 237 | else: 238 | return NotImplemented 239 | 240 | def __truediv__(self, other: "int | float | Octonion"): 241 | if isinstance(other, Octonion): 242 | return self * other.inverse() 243 | elif isinstance(other, (int, float)): 244 | ss, st, su, sv, sw, sx, sy, sz = self.__octonion 245 | return Octonion(ss / other, st / other, su / other, sv / other, 246 | sw / other, sx / other, sy / other, sz / other) 247 | else: 248 | return NotImplemented 249 | 250 | def __round__(self, n: int = None): 251 | if n is None: 252 | return Octonion(*[round(i) for i in self.__octonion]) 253 | return Octonion(*[round(i, n) for i in self.__octonion]) 254 | 255 | def __abs__(self): 256 | return self.norm() 257 | 258 | def __eq__(self, other: "Octonion"): 259 | return self.__octonion == other.__octonion 260 | 261 | def __iadd__(self, other: "Octonion"): 262 | return self + other 263 | 264 | def __isub__(self, other: "Octonion"): 265 | return self - other 266 | 267 | def __imul__(self, other: "Octonion"): 268 | return self * other 269 | 270 | def __itruediv__(self, other: "Octonion"): 271 | return self / other 272 | 273 | def __setitem__(self, key: int, value: real): 274 | self.__octonion = list(self.__octonion) 275 | self.__octonion[key] = value 276 | self.__s, self.__t, self.__u, self.__v, self.__w, self.__x, self.__y, self.__z = self.__octonion 277 | 278 | def __pos__(self): 279 | return Octonion(*self.__octonion) 280 | 281 | def __neg__(self): 282 | return Octonion(*[-i for i in self.__octonion]) 283 | 284 | def data(self): 285 | return list(self.__octonion) 286 | 287 | def norm(self): 288 | return sum([i ** 2 for i in self.__octonion]) ** 0.5 289 | 290 | def conjugate(self): 291 | return Octonion(self.__s, -self.__t, -self.__u, -self.__v, -self.__w, -self.__x, -self.__y, -self.__z) 292 | 293 | def normalize(self): 294 | norm = self.norm() 295 | return Octonion(*[i / norm for i in self.__octonion]) 296 | 297 | def inverse(self): 298 | norm2 = sum([i ** 2 for i in self.__octonion]) 299 | if norm2 == 0: 300 | raise ZeroDivisionError("Cannot invert zero octonion") 301 | return Octonion(*[i / norm2 for i in self.conjugate().__octonion]) 302 | 303 | 304 | def octo(s: real = 0, t: real = 0, u: real = 0, v: real = 0, 305 | w: real = 0, x: real = 0, y: real = 0, z: real = 0) -> Octonion: 306 | return Octonion(s, t, u, v, w, x, y, z) 307 | -------------------------------------------------------------------------------- /pypynum/interp.py: -------------------------------------------------------------------------------- 1 | from .tools import linspace 2 | from .types import arr 3 | 4 | 5 | def interp1d(data: arr, length: int) -> list: 6 | """ 7 | Introduction 8 | ========== 9 | One-dimensional data interpolation using piecewise polynomial interpolation. 10 | 11 | Example 12 | ========== 13 | >>> interp1d((2, 4, 4, 2), 6) 14 | [2, 3.320000000000001, 4.160000000000005, 4.160000000000012, 3.3200000000000074, 2] 15 | >>> 16 | :param data: List of data points to be interpolated. Must be at least two points. 17 | :param length: The number of points in the resampled data set. 18 | :return: A list of interpolated values at the new points. 19 | """ 20 | from .regs import lin_reg, par_reg 21 | expr = [lambda x: sum([k * x ** (1 - n) for n, k in enumerate(lin_reg([0, 1], [data[0], data[1]]))])] 22 | for i in range(len(data) - 2): 23 | temp = par_reg(list(range(i, i + 3)), data[i:i + 3]) 24 | expr.append(lambda x, coefficients=temp: sum([k * x ** (2 - n) for n, k in enumerate(coefficients)])) 25 | expr.append(lambda x: sum([k * x ** (1 - n) for n, k in 26 | enumerate(lin_reg([len(data) - 2, len(data) - 1], [data[-2], data[-1]]))])) 27 | result = linspace(0, len(data) - 1, length) 28 | for item in range(length): 29 | if int(result[item]) != result[item]: 30 | result[item] = (expr[int(result[item])](result[item]) + expr[int(result[item] + 1)](result[item])) / 2 31 | else: 32 | result[item] = data[int(result[item])] 33 | return result 34 | 35 | 36 | def bicubic(x): 37 | """ 38 | Calculate the bicubic interpolation basis function value. 39 | :param x: The x value for which the basis function is evaluated. 40 | :return: The value of the bicubic basis function at x. 41 | """ 42 | absx = abs(x) 43 | if absx <= 1: 44 | return 1.5 * absx ** 3 - 2.5 * absx ** 2 + 1 45 | elif absx < 2: 46 | return -0.5 * absx ** 3 + 2.5 * absx ** 2 - 4 * absx + 2 47 | else: 48 | return 0 49 | 50 | 51 | def contribute(src, x, y, channels=None): 52 | """ 53 | Calculate the contribution of the source array at a specific point using bicubic interpolation. 54 | :param src: The source 2D array from which to interpolate. 55 | :param x: The x-coordinate of the point to interpolate. 56 | :param y: The y-coordinate of the point to interpolate. 57 | :param channels: The number of channels if src is a multichannel array. 58 | :return: The interpolated value at the point (x, y). 59 | """ 60 | src_height = len(src) 61 | src_width = len(src[0]) 62 | value = 0.0 if channels is None else [0.0] * channels 63 | x_int = int(x) 64 | y_int = int(y) 65 | dx = x - x_int 66 | dy = y - y_int 67 | for j in range(-1, 3): 68 | ny = y_int + j 69 | if 0 <= ny < src_height: 70 | wy = bicubic(j - dy) 71 | for i in range(-1, 3): 72 | nx = x_int + i 73 | if 0 <= nx < src_width: 74 | wx = bicubic(i - dx) 75 | if channels is None: 76 | value += wx * wy * src[ny][nx] 77 | else: 78 | for c in range(channels): 79 | value[c] += wx * wy * src[ny][nx][c] 80 | return value 81 | 82 | 83 | def interp2d(src, new_height, new_width, channels=None, round_res=False, min_val=None, max_val=None): 84 | """ 85 | Introduction 86 | ========== 87 | Two-dimensional data interpolation using bicubic interpolation. 88 | 89 | Example 90 | ========== 91 | >>> interp2d([[1, 2], [3, 4]], 3, 3) 92 | [[1.0, 1.6875, 2.0], [2.25, 3.1640625, 3.375], [3.0, 3.9375, 4.0]] 93 | >>> 94 | :param src: The source 2D array to be interpolated. 95 | :param new_height: The desired height of the interpolated array. 96 | :param new_width: The desired width of the interpolated array. 97 | :param channels: The number of channels if src is a multichannel array. 98 | :param round_res: Whether to round the result to the nearest integer. 99 | :param min_val: The minimum value to clip the interpolated results. 100 | :param max_val: The maximum value to clip the interpolated results. 101 | :return: A 2D array of the interpolated values with the new dimensions. 102 | """ 103 | src_height = len(src) 104 | src_width = len(src[0]) 105 | dst = [[None] * new_width for _ in range(new_height)] 106 | for dst_y in range(new_height): 107 | for dst_x in range(new_width): 108 | src_x = (src_width - 1) * dst_x / (new_width - 1) 109 | src_y = (src_height - 1) * dst_y / (new_height - 1) 110 | value = contribute(src, src_x, src_y, channels) 111 | if min_val is not None: 112 | value = max(min_val, value) if channels is None else tuple(max(min_val, v) for v in value) 113 | if max_val is not None: 114 | value = min(max_val, value) if channels is None else tuple(min(max_val, v) for v in value) 115 | if round_res: 116 | value = round(value) if channels is None else tuple(map(round, value)) 117 | dst[dst_y][dst_x] = value 118 | return dst 119 | -------------------------------------------------------------------------------- /pypynum/logics.py: -------------------------------------------------------------------------------- 1 | from .types import LogicError 2 | 3 | FullError = LogicError("The input of this logic component is full") 4 | InputError = LogicError("The output port number of the connection must be an integer") 5 | 6 | 7 | class Basic: 8 | def __init__(self, label): 9 | if not isinstance(label, str): 10 | raise TypeError("The type of label can only be a string") 11 | self.type = str(type(self)) 12 | self.__label = label 13 | self.data = None 14 | self.order0 = 0 15 | self.order1 = 0 16 | self.order2 = 0 17 | self.order3 = 0 18 | 19 | def __repr__(self): 20 | return self.__class__.__name__ + "({})".format(self.__label) 21 | 22 | def out(self): 23 | raise NotImplementedError 24 | 25 | def set_order0(self, data): 26 | if not isinstance(data, int): 27 | raise InputError 28 | self.order0 = data 29 | 30 | def set_order1(self, data): 31 | if isinstance(self, Unary): 32 | raise NotImplementedError 33 | if not isinstance(data, int): 34 | raise InputError 35 | self.order1 = data 36 | 37 | def set_order2(self, data): 38 | if isinstance(self, Unary) or isinstance(self, Binary): 39 | raise NotImplementedError 40 | if not isinstance(data, int): 41 | raise InputError 42 | self.order2 = data 43 | 44 | def set_order3(self, data): 45 | if isinstance(self, Unary) or isinstance(self, Binary) or isinstance(self, Ternary): 46 | raise NotImplementedError 47 | if not isinstance(data, int): 48 | raise InputError 49 | self.order3 = data 50 | 51 | 52 | class Unary(Basic): 53 | def __init__(self, label, pin0=None): 54 | super().__init__(label) 55 | if pin0 is not None and not isinstance(pin0, int): 56 | raise InputError 57 | self.pin0 = pin0 58 | 59 | def set_pin0(self, data): 60 | if not isinstance(data, int) and not isinstance(data, Basic): 61 | raise InputError 62 | self.pin0 = data 63 | 64 | def out(self): 65 | self.data = self.pin0.out()[ 66 | self.order0] if isinstance(self.pin0, Basic) else 0 if self.pin0 is None else self.pin0 67 | 68 | 69 | class Binary(Basic): 70 | def __init__(self, label, pin0=None, pin1=None): 71 | super().__init__(label) 72 | if any(map(lambda item: item is not None and not isinstance(item, int), [pin0, pin1])): 73 | raise InputError 74 | self.pin0 = pin0 75 | self.pin1 = pin1 76 | 77 | def set_pin0(self, data): 78 | if not isinstance(data, int) and not isinstance(data, Basic): 79 | raise InputError 80 | self.pin0 = data 81 | 82 | def set_pin1(self, data): 83 | if not isinstance(data, int) and not isinstance(data, Basic): 84 | raise InputError 85 | self.pin1 = data 86 | 87 | def out(self): 88 | self.data = [ 89 | self.pin0.out()[self.order0] if isinstance(self.pin0, Basic) else 0 if self.pin0 is None else self.pin0, 90 | self.pin1.out()[self.order1] if isinstance(self.pin1, Basic) else 0 if self.pin1 is None else self.pin1 91 | ] 92 | 93 | 94 | class Ternary(Basic): 95 | def __init__(self, label, pin0=None, pin1=None, pin2=None): 96 | super().__init__(label) 97 | if any(map(lambda item: item is not None and not isinstance(item, int), [pin0, pin1, pin2])): 98 | raise InputError 99 | self.pin0 = pin0 100 | self.pin1 = pin1 101 | self.pin2 = pin2 102 | 103 | def set_pin0(self, data): 104 | if not isinstance(data, int) and not isinstance(data, Basic): 105 | raise InputError 106 | self.pin0 = data 107 | 108 | def set_pin1(self, data): 109 | if not isinstance(data, int) and not isinstance(data, Basic): 110 | raise InputError 111 | self.pin1 = data 112 | 113 | def set_pin2(self, data): 114 | if not isinstance(data, int) and not isinstance(data, Basic): 115 | raise InputError 116 | self.pin2 = data 117 | 118 | def out(self): 119 | self.data = [ 120 | self.pin0.out()[self.order0] if isinstance(self.pin0, Basic) else 0 if self.pin0 is None else self.pin0, 121 | self.pin1.out()[self.order1] if isinstance(self.pin1, Basic) else 0 if self.pin1 is None else self.pin1, 122 | self.pin2.out()[self.order2] if isinstance(self.pin2, Basic) else 0 if self.pin2 is None else self.pin2 123 | ] 124 | 125 | 126 | class Quaternary(Basic): 127 | def __init__(self, label, pin0=None, pin1=None, pin2=None, pin3=None): 128 | super().__init__(label) 129 | if any(map(lambda item: item is not None and not isinstance(item, int), [pin0, pin1, pin2, pin3])): 130 | raise InputError 131 | self.pin0 = pin0 132 | self.pin1 = pin1 133 | self.pin2 = pin2 134 | self.pin3 = pin3 135 | 136 | def set_pin0(self, data): 137 | if not isinstance(data, int) and not isinstance(data, Basic): 138 | raise InputError 139 | self.pin0 = data 140 | 141 | def set_pin1(self, data): 142 | if not isinstance(data, int) and not isinstance(data, Basic): 143 | raise InputError 144 | self.pin1 = data 145 | 146 | def set_pin2(self, data): 147 | if not isinstance(data, int) and not isinstance(data, Basic): 148 | raise InputError 149 | self.pin2 = data 150 | 151 | def set_pin3(self, data): 152 | if not isinstance(data, int) and not isinstance(data, Basic): 153 | raise InputError 154 | self.pin3 = data 155 | 156 | def out(self): 157 | self.data = [ 158 | self.pin0.out()[self.order0] if isinstance(self.pin0, Basic) else 0 if self.pin0 is None else self.pin0, 159 | self.pin1.out()[self.order1] if isinstance(self.pin1, Basic) else 0 if self.pin1 is None else self.pin1, 160 | self.pin2.out()[self.order2] if isinstance(self.pin2, Basic) else 0 if self.pin2 is None else self.pin2, 161 | self.pin3.out()[self.order3] if isinstance(self.pin3, Basic) else 0 if self.pin3 is None else self.pin3 162 | ] 163 | 164 | 165 | def connector(previous, latter): 166 | if not isinstance(previous, Basic) or not isinstance(latter, Basic): 167 | raise TypeError("The connected logical components must inherit from the Basic class") 168 | previous = previous 169 | latter = latter 170 | if isinstance(latter, Unary): 171 | if latter.pin0 is None: 172 | latter.set_pin0(previous) 173 | else: 174 | raise FullError 175 | elif isinstance(latter, Binary): 176 | if latter.pin0 is None: 177 | latter.set_pin0(previous) 178 | elif latter.pin1 is None: 179 | latter.set_pin1(previous) 180 | else: 181 | raise FullError 182 | elif isinstance(latter, Ternary): 183 | if latter.pin0 is None: 184 | latter.set_pin0(previous) 185 | elif latter.pin1 is None: 186 | latter.set_pin1(previous) 187 | elif latter.pin2 is None: 188 | latter.set_pin2(previous) 189 | else: 190 | raise FullError 191 | else: 192 | raise NotImplementedError 193 | 194 | 195 | class NOT(Unary): 196 | def out(self): 197 | super().out() 198 | return [1 - self.data] 199 | 200 | 201 | class AND(Binary): 202 | def out(self): 203 | super().out() 204 | return [1 if all(self.data) else 0] 205 | 206 | 207 | class OR(Binary): 208 | def out(self): 209 | super().out() 210 | return [1 if any(self.data) else 0] 211 | 212 | 213 | class NAND(Binary): 214 | def out(self): 215 | super().out() 216 | return [0 if all(self.data) else 1] 217 | 218 | 219 | class NOR(Binary): 220 | def out(self): 221 | super().out() 222 | return [0 if any(self.data) else 1] 223 | 224 | 225 | class XNOR(Binary): 226 | def out(self): 227 | super().out() 228 | return [1 if self.data[0] == self.data[1] else 0] 229 | 230 | 231 | class XOR(Binary): 232 | def out(self): 233 | super().out() 234 | return [0 if self.data[0] == self.data[1] else 1] 235 | 236 | 237 | class HalfAdder(Binary): 238 | def out(self): 239 | super().out() 240 | return [0 if self.data[0] == self.data[1] else 1, 1 if all(self.data) else 0] 241 | 242 | 243 | class FullAdder(Ternary): 244 | def out(self): 245 | super().out() 246 | return [0 if sum(self.data) % 2 == 0 else 1, 1 if sum(self.data) > 1 else 0] 247 | 248 | 249 | class HalfSuber(Binary): 250 | def out(self): 251 | super().out() 252 | return [0 if self.data[0] == self.data[1] else 1, 1 if not self.data[0] and self.data[1] else 0] 253 | 254 | 255 | class FullSuber(Ternary): 256 | def out(self): 257 | super().out() 258 | return [0 if sum(self.data) % 2 == 0 else 1, 1 if self.data[0] - self.data[1] - self.data[2] < 0 else 0] 259 | 260 | 261 | class TwoBMuler(Quaternary): 262 | def out(self): 263 | super().out() 264 | if self.data[0] == 0 and self.data[1] == 0 or self.data[2] == 0 and self.data[3] == 0: 265 | return [0, 0, 0, 0] 266 | elif self.data[0] == 1 and self.data[1] == 0: 267 | return [self.data[2], self.data[3], 0, 0] 268 | elif self.data[2] == 1 and self.data[3] == 0: 269 | return [self.data[0], self.data[1], 0, 0] 270 | elif self.data[0] == 0 and self.data[1] == 1: 271 | return [0, self.data[2], self.data[3], 0] 272 | elif self.data[2] == 0 and self.data[3] == 1: 273 | return [0, self.data[0], self.data[1], 0] 274 | else: 275 | return [1, 0, 0, 1] 276 | 277 | 278 | class TwoBDiver(Quaternary): 279 | def out(self): 280 | super().out() 281 | if self.data[2] == 0 and self.data[3] == 0: 282 | return [1, 1, 1, 1] 283 | elif self.data[0] == 0 and self.data[1] == 0: 284 | return [0, 0, 0, 0] 285 | elif self.data[2] == 1 and self.data[3] == 0: 286 | return [self.data[0], self.data[1], 0, 0] 287 | elif self.data[2] == 0 and self.data[3] == 1: 288 | return [self.data[1], 0, self.data[0], 0] 289 | elif self.data == [1, 1, 1, 1]: 290 | return [1, 0, 0, 0] 291 | else: 292 | return [0, 0, self.data[0], self.data[1]] 293 | 294 | 295 | class JKFF(Binary): 296 | def __init__(self, label, pin0=None, pin1=None, state=0): 297 | super().__init__(label, pin0, pin1) 298 | self.__state = state 299 | 300 | def out(self): 301 | super().out() 302 | j, k = self.data 303 | if j == 0 and k == 0: 304 | self.__state = self.__state 305 | elif j == 0 and k == 1: 306 | self.__state = 0 307 | elif j == 1 and k == 0: 308 | self.__state = 1 309 | elif j == 1 and k == 1: 310 | self.__state = 1 - self.__state 311 | return [self.__state] 312 | 313 | 314 | class DFF(Unary): 315 | def __init__(self, label, pin0=None, state=0): 316 | super().__init__(label, pin0) 317 | self.__state = state 318 | 319 | def out(self): 320 | super().out() 321 | d = self.data 322 | if d == 0: 323 | self.__state = 0 324 | elif d == 1: 325 | self.__state = 1 326 | return [self.__state] 327 | 328 | 329 | class TFF(Unary): 330 | def __init__(self, label, pin0=None, state=0): 331 | super().__init__(label, pin0) 332 | self.__state = state 333 | 334 | def out(self): 335 | super().out() 336 | t = self.data 337 | if t == 0: 338 | self.__state = self.__state 339 | elif t == 1: 340 | self.__state = 1 - self.__state 341 | return [self.__state] 342 | 343 | 344 | class COMP(Binary): 345 | def out(self): 346 | super().out() 347 | return [1, 0, 0] if self.data[0] and not self.data[1] else [ 348 | 0, 0, 1] if not self.data[0] and self.data[1] else [0, 1, 0] 349 | -------------------------------------------------------------------------------- /pypynum/networks.py: -------------------------------------------------------------------------------- 1 | from .maths import sigmoid 2 | from .random import gauss 3 | 4 | 5 | class NeuralNetwork: 6 | def __init__(self, _input, _hidden, _output): 7 | self.input = _input + 1 8 | self.hidden = _hidden 9 | self.output = _output 10 | self.ai = [1.0] * self.input 11 | self.ah = [1.0] * self.hidden 12 | self.ao = [1.0] * self.output 13 | self.wi = [[gauss(0, 1) for _ in range(self.hidden)] for _ in range(self.input)] 14 | self.wo = [[gauss(0, 1) for _ in range(self.output)] for _ in range(self.hidden)] 15 | self.ci = [[0.0 for _ in range(self.hidden)] for _ in range(self.input)] 16 | self.co = [[0.0 for _ in range(self.output)] for _ in range(self.hidden)] 17 | 18 | def feedforward(self, inputs): 19 | if len(inputs) != self.input - 1: 20 | raise ValueError("Input quantity error") 21 | for i in range(self.input - 1): 22 | self.ai[i] = inputs[i] 23 | for j in range(self.hidden): 24 | _sum = 0.0 25 | for i in range(self.input): 26 | _sum += self.ai[i] * self.wi[i][j] 27 | self.ah[j] = sigmoid(_sum) 28 | for k in range(self.output): 29 | _sum = 0.0 30 | for j in range(self.hidden): 31 | _sum += self.ah[j] * self.wo[j][k] 32 | self.ao[k] = sigmoid(_sum) 33 | return self.ao[:] 34 | 35 | def backpropagate(self, targets, n): 36 | def dsigmoid(y): 37 | return y * (1.0 - y) 38 | 39 | if len(targets) != self.output: 40 | raise ValueError("Target quantity error") 41 | output_deltas = [0.0] * self.output 42 | for k in range(self.output): 43 | error = -(targets[k] - self.ao[k]) 44 | output_deltas[k] = dsigmoid(self.ao[k]) * error 45 | hidden_deltas = [0.0] * self.hidden 46 | for j in range(self.hidden): 47 | error = 0.0 48 | for k in range(self.output): 49 | error += output_deltas[k] * self.wo[j][k] 50 | hidden_deltas[j] = dsigmoid(self.ah[j]) * error 51 | for j in range(self.hidden): 52 | for k in range(self.output): 53 | change = output_deltas[k] * self.ah[j] 54 | self.wo[j][k] -= n * change + self.co[j][k] 55 | self.co[j][k] = change 56 | for i in range(self.input): 57 | for j in range(self.hidden): 58 | change = hidden_deltas[j] * self.ai[i] 59 | self.wi[i][j] -= n * change + self.ci[i][j] 60 | self.ci[i][j] = change 61 | error = 0.0 62 | for k in range(len(targets)): 63 | error += (targets[k] - self.ao[k]) ** 2 64 | return 0.5 * error 65 | 66 | def train(self, x, y, iterations=3000, n=0.0002): 67 | for i in range(iterations): 68 | error = 0.0 69 | for p in zip(x, y): 70 | inputs = p[0] 71 | targets = p[1] 72 | self.feedforward(inputs) 73 | error = self.backpropagate(targets, n) 74 | if i < 100 or i % 100 == 99: 75 | print("epoch {}: error {:.9f}".format(i + 1, error)) 76 | 77 | def predict(self, x): 78 | predictions = [] 79 | for p in x: 80 | predictions.append(self.feedforward(p)) 81 | return predictions 82 | 83 | 84 | def neuraln(_input, _hidden, _output): 85 | return NeuralNetwork(_input, _hidden, _output) 86 | -------------------------------------------------------------------------------- /pypynum/numbers.py: -------------------------------------------------------------------------------- 1 | ContentError = ValueError("The content of the string is invalid") 2 | __ROMAN_VALUES = (1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1) 3 | __ROMAN_SYMBOLS = ("M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I") 4 | 5 | 6 | def int2words(integer: int) -> str: 7 | """ 8 | introduction 9 | ========== 10 | Convert integers into natural language. 11 | 12 | example 13 | ========== 14 | >>> int2words(4294967296) 15 | 'four billion two hundred and ninety-four million nine hundred and sixty-seven thousand two hundred and ninety-six' 16 | >>> 17 | :param integer: integer 18 | :return: 19 | """ 20 | if not isinstance(integer, int): 21 | raise TypeError("The input must be an integer") 22 | if integer == 0: 23 | return "zero" 24 | if integer < 0: 25 | return "negative " + int2words(abs(integer)) 26 | ones = ["", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", 27 | "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"] 28 | tens = ["", "", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"] 29 | 30 | def two_digit(num): 31 | if num < 20: 32 | return ones[num] if num > 0 else "" 33 | ten, one = divmod(num, 10) 34 | return tens[ten] + ("-" if one else "") + ones[one] 35 | 36 | def three_digit(num): 37 | if num == 0: 38 | return "" 39 | hundred, rem = divmod(num, 100) 40 | return ones[hundred] + " hundred" + (" and " if rem else "") + two_digit(rem) 41 | 42 | big_units = ["quadragintillion", "novemtrigintillion", "octrigintillion", "septrigintillion", "sextrigintillion", 43 | "quintrigintillion", "quattuortrigintillion", "trestrigintillion", "duotrigintillion", 44 | "untrigintillion", "trigintillion", "novemvigintillion", "octovigintillion", "septenvigintillion", 45 | "sexvigintillion", "quinvigintillion", "quattuorvigintillion", "trevigintillion", "duovigintillion", 46 | "unvigintillion", "vigintillion", "novemdecillion", "octodecillion", "septendecillion", "sexdecillion", 47 | "quindecillion", "quattuordecillion", "tredecillion", "duodecillion", "undecillion", "decillion", 48 | "nonillion", "octillion", "septillion", "sextillion", "quintillion", "quadrillion", "trillion", 49 | "billion", "million", "thousand"] 50 | unit_value = 10 ** (3 * len(big_units)) 51 | result = [] 52 | for name in big_units: 53 | if integer >= unit_value: 54 | value, integer = divmod(integer, unit_value) 55 | result.append(int2words(value) + " " + name) 56 | unit_value //= 1000 57 | if integer > 0: 58 | words = two_digit(integer) if integer < 100 else three_digit(integer) 59 | result.append(words) 60 | return " ".join(result) 61 | 62 | 63 | def str2int(string: str) -> int: 64 | """ 65 | introduction 66 | ========== 67 | Converts strings to integers and supports parsing ordinary floating-point numbers 68 | and scientific notation floating-point numbers. 69 | 70 | example 71 | ========== 72 | >>> str2int("0.123456789e+5") 73 | 12345 74 | >>> 75 | :param string: string 76 | :return: 77 | """ 78 | for item in string: 79 | if item not in "0123456789e.+-": 80 | raise ContentError 81 | length = 0 82 | string = string.lower() 83 | if "e" in string: 84 | if string.count("e") != 1: 85 | raise ContentError 86 | parts = string.split("e") 87 | integer_part = parts[0] 88 | exponent_part = parts[1] 89 | if not exponent_part: 90 | raise ContentError 91 | if "." in integer_part: 92 | if integer_part.count(".") != 1: 93 | raise ContentError 94 | integer_part, length = integer_part.replace(".", ""), len(integer_part.split(".")[1]) 95 | integer_value = int(integer_part) 96 | exponent_value = int(exponent_part) 97 | if exponent_value > 0: 98 | integer_value *= 10 ** exponent_value 99 | elif exponent_value < 0: 100 | div = divmod(integer_value, 10 ** (-exponent_value)) 101 | integer_value = div[0] if not div[1] or div[0] >= 0 else div[0] + 1 102 | if integer_value and length: 103 | div = divmod(integer_value, 10 ** length) 104 | return div[0] if not div[1] or div[0] >= 0 else div[0] + 1 105 | else: 106 | return integer_value 107 | else: 108 | if "." in string: 109 | if string.count(".") != 1: 110 | raise ContentError 111 | integer_part = string.split(".")[0] 112 | return int(integer_part) if integer_part else 0 113 | else: 114 | return int(string) 115 | 116 | 117 | def int2roman(integer: int, overline: bool = True) -> str: 118 | """ 119 | introduction 120 | ========== 121 | Convert natural numbers to Roman numerals and support with or without an overline. 122 | 123 | example 124 | ========== 125 | >>> int2roman(12345) 126 | 'X̄ĪĪCCCXLV' 127 | >>> 128 | :param integer: integer 129 | :param overline: bool 130 | :return: 131 | """ 132 | if not isinstance(integer, int): 133 | if isinstance(integer, float): 134 | integer = round(integer) 135 | else: 136 | raise TypeError("The number to be converted can only be a natural number") 137 | if integer < 0: 138 | raise ValueError("The number to be converted can only be a natural number") 139 | 140 | def int2roman_helper(number): 141 | roman = "" 142 | i = 0 143 | while number > 0: 144 | tmp = number // __ROMAN_VALUES[i] 145 | roman += __ROMAN_SYMBOLS[i] * tmp 146 | number -= __ROMAN_VALUES[i] * tmp 147 | i += 1 148 | return roman 149 | 150 | def roman_overline(num): 151 | return "̄".join(int2roman_helper(num)) + "̄" 152 | 153 | if overline and integer >= 4000: 154 | thousands = integer // 1000 155 | remainder = integer % 1000 156 | else: 157 | thousands = 0 158 | remainder = integer 159 | roman_num = "" 160 | if thousands > 0: 161 | roman_num += roman_overline(thousands) 162 | roman_num += int2roman_helper(remainder) 163 | return roman_num 164 | 165 | 166 | def roman2int(roman_num: str) -> int: 167 | """ 168 | introduction 169 | ========== 170 | Convert Roman numerals to natural numbers and support the presence or absence of an overline. 171 | 172 | example 173 | ========== 174 | >>> roman2int("X̄ĪĪCCCXLV") 175 | 12345 176 | >>> 177 | :param roman_num: string 178 | :return: 179 | """ 180 | 181 | def roman2int_helper(number): 182 | part = 0 183 | last = 10000 184 | i = 0 185 | while i < len(number): 186 | if number[i:i + 2] in __ROMAN_SYMBOLS: 187 | value = __ROMAN_VALUES[__ROMAN_SYMBOLS.index(number[i:i + 2])] 188 | if value > last: 189 | raise ContentError 190 | else: 191 | last = value 192 | part += value 193 | i += 2 194 | elif number[i] in __ROMAN_SYMBOLS: 195 | value = __ROMAN_VALUES[__ROMAN_SYMBOLS.index(number[i])] 196 | if value > last: 197 | raise ContentError 198 | else: 199 | last = value 200 | part += value 201 | i += 1 202 | else: 203 | raise ContentError 204 | return part 205 | 206 | if "̄" in roman_num: 207 | index = roman_num.rfind("̄") 208 | high = roman_num[:index + 1] 209 | over = high[1::2] 210 | if over.count("̄") != len(over): 211 | raise ContentError 212 | return roman2int_helper(high.replace("̄", "")) * 1000 + roman2int_helper(roman_num[index + 1:]) 213 | else: 214 | return roman2int_helper(roman_num) 215 | 216 | 217 | def float2fraction(number: float, mixed: bool = False, error: float = 1e-15) -> tuple: 218 | """ 219 | introduction 220 | ========== 221 | Convert floating-point numbers to fractions and support mixed numbers and false fractions. 222 | 223 | example 224 | ========== 225 | >>> float2fraction(3.141592653589793, False, 1e-6) 226 | (355, 113) 227 | >>> 228 | :param number: float 229 | :param mixed: bool 230 | :param error: float 231 | :return: 232 | """ 233 | if number < 0: 234 | number = -number 235 | flag = True 236 | else: 237 | flag = False 238 | whole = int(number) 239 | number -= whole 240 | numerator = 0 241 | denominator = 1 242 | while abs(number - float(numerator) / float(denominator)) > error: 243 | if number > float(numerator) / float(denominator): 244 | numerator += 1 245 | else: 246 | denominator += 1 247 | if flag: 248 | whole = -whole 249 | numerator = -numerator 250 | return (whole, numerator, denominator) if mixed else (whole * denominator + numerator, denominator) 251 | 252 | 253 | def split_float(s: str) -> tuple: 254 | """ 255 | Introduction 256 | ========== 257 | Decompose a floating-point number string into its sign, integer part, fractional part, and exponent part. 258 | 259 | Example 260 | ========== 261 | >>> split_float("-123.456e+7") 262 | ('-', '123', '456', '+7') 263 | >>> 264 | :param s: The string representation of the floating-point number to be decomposed. 265 | :return: A tuple containing the sign, integer part, fractional part, and exponent part of the input string. 266 | :raises ValueError: If the input string cannot be converted to a float, represents a NaN (Not a Number), 267 | or represents an infinity. 268 | """ 269 | sign, integer, fraction, exponent = "", "", "", "" 270 | s = str(s).strip() 271 | try: 272 | floating = float(s) 273 | except ValueError: 274 | raise ValueError("The input string cannot be converted to a float") 275 | if floating != floating: 276 | raise ValueError("The input string represents a NaN (Not a Number)") 277 | if s in ("inf", "-inf"): 278 | raise ValueError("The input string represents an infinity") 279 | if s[0] in ["+", "-"]: 280 | sign, s = s[0], s[1:] 281 | if "e" in s or "E" in s: 282 | exp_index = s.index("e") if "e" in s else s.index("E") 283 | exponent, s = s[exp_index + 1:], s[:exp_index] 284 | if "." in s: 285 | dot_index = s.index(".") 286 | integer, fraction = s[:dot_index], s[dot_index + 1:].rstrip("0") 287 | else: 288 | integer = s 289 | return sign, integer, fraction, exponent 290 | 291 | 292 | def parse_float(s: str) -> tuple: 293 | """ 294 | Introduction 295 | ========== 296 | Parse a floating-point number string and return the sign bit, exponent, and mantissa. 297 | 298 | Example 299 | ========== 300 | >>> parse_float("-123.456e+7") 301 | ('1', '4', '123456') 302 | >>> 303 | :param s: The string representation of the floating-point number to be parsed. 304 | :return: A tuple containing the sign bit (0 for positive, 1 for negative), the exponent, and the mantissa. 305 | :raises ValueError: If the input string is not a valid floating-point number string. 306 | """ 307 | sign, integer, fraction, exponent = split_float(s) 308 | sign = "1" if sign == "-" else "0" 309 | new_exponent = int(exponent) if exponent else 0 310 | if fraction: 311 | new_exponent -= len(fraction) 312 | mantissa = integer + fraction or "0" 313 | new_mantissa = mantissa.lstrip("0") 314 | if not new_mantissa: 315 | new_mantissa = "0" 316 | new_exponent -= len(mantissa) - len(new_mantissa) 317 | return sign, str(new_exponent), new_mantissa 318 | 319 | 320 | if __name__ == "__main__": 321 | import doctest 322 | 323 | doctest.testmod(verbose=True) 324 | -------------------------------------------------------------------------------- /pypynum/plotting.py: -------------------------------------------------------------------------------- 1 | from .tools import linspace 2 | from .types import Union, arr, real 3 | 4 | thing = Union[list, str] 5 | 6 | 7 | def color(text: str, rgb: arr) -> str: 8 | """ 9 | To render colors to text. 10 | :param text: string 11 | :param rgb: list | tuple 12 | :return: 13 | """ 14 | if not isinstance(rgb, (list, tuple)) or len(rgb) != 3: 15 | raise ValueError("RGB must be a triplet") 16 | red, green, blue = rgb 17 | if not (0 <= red <= 255 and 0 <= green <= 255 and 0 <= blue <= 255): 18 | raise ValueError("The valid range for RGB values is from 0 to 255") 19 | number = 16 + round(red / 51.2) * 36 + round(green / 51.2) * 6 + round(blue / 51.2) 20 | return "\x1b[38;5;{}m{}\x1b[m".format(number, text) 21 | 22 | 23 | def change(data: thing) -> thing: 24 | """ 25 | Transform the background between list and string forms. 26 | :param data: list | string 27 | :return: 28 | """ 29 | if isinstance(data, list): 30 | return "\n".join(["".join(_) for _ in data]) 31 | elif isinstance(data, str): 32 | return [list(_) for _ in data.split("\n")] 33 | raise TypeError("The input parameter type can only be a list or string") 34 | 35 | 36 | def background(right: real = 5, left: real = -5, top: real = 5, bottom: real = -5, 37 | complexity: real = 5, ratio: real = 3, string: bool = False) -> thing: 38 | """ 39 | Generate an empty coordinate system. 40 | :param right: integer | float 41 | :param left: integer | float 42 | :param top: integer | float 43 | :param bottom: integer | float 44 | :param complexity: integer | float 45 | :param ratio: integer | float 46 | :param string: bool. 47 | :return: 48 | """ 49 | if abs(ratio) != ratio: 50 | raise ValueError("The ratio cannot be less than zero") 51 | if right - left < 1 / complexity or top - bottom < 1 / complexity: 52 | raise ValueError("The defined width or height cannot be less than the reciprocal of complexity") 53 | x = linspace(left, right, round((right - left) * complexity + 1)) 54 | plane = [[" "] * 10 + ["|"] + [" "] * len(x) if _ != round((top - bottom) * complexity / 2 / ratio) 55 | else [" "] * 10 + ["|"] + list(" ".join("_" * (len(x) // 2 + 1))) for _ in range( 56 | round((top - bottom) * complexity / ratio))] + [[" "] * 10 + ["|"] + ["_"] * len(x)] 57 | plane[0][:10] = "{:.2e}".format(top).rjust(10) 58 | plane[-1][:10] = "{:.2e}".format(bottom).rjust(10) 59 | plane[round((top - bottom) * complexity / 2 / ratio)][:10] = "{:.2e}".format((top + bottom) / 2).rjust(10) 60 | plane.append([" "] * 11 + list("{:.2e}".format(left).ljust(10)) + list( 61 | "{:.2e}".format((right + left) / 2).center(len(x) - 20)) + list("{:.2e}".format(right).rjust(10))) 62 | return plane if not string else change(plane) 63 | 64 | 65 | def unary(function, right: real = 5, left: real = -5, top: real = 5, bottom: real = -5, complexity: real = 5, 66 | ratio: real = 3, string: bool = True, basic: list = None, character: str = ".", data: bool = False, 67 | coloration=False) -> thing: 68 | """ 69 | Draw a graph of a unary function. 70 | :param function: function 71 | :param right: integer | float 72 | :param left: integer | float 73 | :param top: integer | float 74 | :param bottom: integer | float 75 | :param complexity: integer | float 76 | :param ratio: integer | float 77 | :param string: bool. 78 | :param basic: list 79 | :param character: string 80 | :param data: bool. 81 | :param coloration: bool. 82 | :return: 83 | """ 84 | if not isinstance(character, str) or (len(character) != 1 and not coloration): 85 | raise ValueError("The parameter character must be one character") 86 | x = linspace(left, right, round((right - left) * complexity + 1)) 87 | y = list(map(function, x)) 88 | plane = [_[:] for _ in basic] if basic else background(right, left, top, bottom, complexity, ratio) 89 | for i, d in enumerate(y): 90 | d = round((top - d) * complexity / ratio) 91 | if 0 <= d <= len(y) - 1: 92 | plane[d][i + 11] = character 93 | if string: 94 | plane = change(plane) 95 | return [plane, list(zip(x, y))] if data else plane 96 | 97 | 98 | def binary(function, right: real = 5, left: real = -5, top: real = 5, bottom: real = -5, complexity: real = 5, 99 | ratio: real = 3, error=0, compare="==", string: bool = True, basic: list = None, 100 | character: str = ".", data: bool = False, coloration=False) -> thing: 101 | """ 102 | Draw a graph of binary equations. 103 | :param function: function 104 | :param right: integer | float 105 | :param left: integer | float 106 | :param top: integer | float 107 | :param bottom: integer | float 108 | :param complexity: integer | float 109 | :param ratio: integer | float 110 | :param error: integer | float 111 | :param compare: string 112 | :param string: bool. 113 | :param basic: list 114 | :param character: string 115 | :param data: bool. 116 | :param coloration: bool. 117 | :return: 118 | """ 119 | if not isinstance(character, str) or (len(character) != 1 and not coloration): 120 | raise ValueError("The parameter character must be one character") 121 | x = linspace(left, right, round((right - left) * complexity + 1)) 122 | y = linspace(top, bottom, round((top - bottom) * complexity / ratio + 1)) 123 | plane = [_[:] for _ in basic] if basic else background(right, left, top, bottom, complexity, ratio) 124 | coordinates = [] 125 | for p1, c1 in enumerate(y): 126 | for p0, c0 in enumerate(x): 127 | if compare == "==": 128 | flag = abs(function(c0, c1)) <= error 129 | elif compare == "<=": 130 | flag = function(c0, c1) <= error 131 | elif compare == ">=": 132 | flag = function(c0, c1) >= error 133 | else: 134 | raise ValueError("The parameter used for comparison can only be '==' or '<=' or '>='") 135 | if flag: 136 | if data: 137 | coordinates.append((c0, c1)) 138 | plane[p1][p0 + 11] = character 139 | if string: 140 | plane = change(plane) 141 | return [plane, coordinates] if data else plane 142 | 143 | 144 | def c_unary(function, projection: str = "ri", right: real = 5, left: real = -5, top: real = 5, bottom: real = -5, 145 | complexity: real = 5, ratio: real = 3, string: bool = True, basic: list = None, character: str = ".", 146 | data: bool = False, coloration=False) -> thing: 147 | """ 148 | Draw a graph of a complex function of one variable. 149 | :param function: function 150 | :param projection: string 151 | :param right: integer | float 152 | :param left: integer | float 153 | :param top: integer | float 154 | :param bottom: integer | float 155 | :param complexity: integer | float 156 | :param ratio: integer | float 157 | :param string: bool. 158 | :param basic: list 159 | :param character: string 160 | :param data: bool. 161 | :param coloration: bool. 162 | :return: 163 | """ 164 | if not isinstance(character, str) or (len(character) != 1 and not coloration): 165 | raise ValueError("The parameter character must be one character") 166 | x = linspace(left, right, round((right - left) * complexity + 1)) 167 | y = linspace(top, bottom, round((top - bottom) * complexity / ratio + 1)) 168 | plane = [_[:] for _ in basic] if basic else background(right, left, top, bottom, complexity, ratio) 169 | coordinates = [((c0, c1), function(complex(c0, c1))) for p1, c1 in enumerate(y) for p0, c0 in enumerate(x)] 170 | for x, y in coordinates: 171 | if projection == "ri": 172 | _c0, _c1 = y.real, y.imag 173 | else: 174 | raise ValueError("Other modes are currently not supported") 175 | c0, c1 = round((_c0 - left) * complexity), round((top - _c1) * complexity / ratio) 176 | if 0 <= c0 <= len(plane[0]) - 12 and 0 <= c1 <= len(plane) - 2: 177 | plane[c1][c0 + 11] = character 178 | if string: 179 | plane = change(plane) 180 | return [plane, coordinates] if data else plane 181 | -------------------------------------------------------------------------------- /pypynum/polys.py: -------------------------------------------------------------------------------- 1 | from .types import config 2 | 3 | 4 | class Polynomial: 5 | def __init__(self, terms=None): 6 | if terms is None: 7 | self.terms = [] 8 | else: 9 | try: 10 | self.terms = sorted([tuple(item) if len(item) == 2 else None for item in terms if item[1] != 0]) 11 | except TypeError: 12 | raise TypeError("The input polynomial must be a sequence consisting of degree-coefficient pairs") 13 | 14 | def add_term(self, degree, coefficient): 15 | if coefficient == 0: 16 | return 17 | 18 | def binary_search(terms, deg): 19 | low, high = 0, len(terms) - 1 20 | while low <= high: 21 | mid = (low + high) // 2 22 | if terms[mid][0] == deg: 23 | return mid 24 | elif terms[mid][0] < deg: 25 | low = mid + 1 26 | else: 27 | high = mid - 1 28 | return low 29 | 30 | insert_index = binary_search(self.terms, degree) 31 | if insert_index < len(self.terms) and self.terms[insert_index][0] == degree: 32 | try: 33 | self.terms[insert_index] = (degree, self.terms[insert_index][1] + coefficient) 34 | except OverflowError: 35 | self.terms[insert_index] = (degree, int(self.terms[insert_index][1]) + int(coefficient)) 36 | else: 37 | self.terms.insert(insert_index, (degree, coefficient)) 38 | 39 | def is_zero(self): 40 | return not self.terms 41 | 42 | def gcd(self, other): 43 | temp = self 44 | while not other.is_zero(): 45 | temp, other = other, temp % other 46 | return temp 47 | 48 | def lcm(self, other): 49 | gcd_poly = self.gcd(other) 50 | if gcd_poly.is_zero(): 51 | raise ValueError("Cannot compute LCM of polynomials with zero GCD") 52 | return self * other // gcd_poly 53 | 54 | def roots(self, real_roots=True, tolerance=1e-15): 55 | from .equations import poly_eq 56 | all_roots = poly_eq(self.coeffs(True)) 57 | if real_roots: 58 | return [(1 if root.real > 0 else -1) * abs(root) for root in all_roots if abs(root.imag) < tolerance] 59 | return all_roots 60 | 61 | def evaluate(self, x): 62 | return sum([coefficient * x ** degree for degree, coefficient in self.terms]) 63 | 64 | def degs(self, reverse=False): 65 | terms = reversed(self.terms) if reverse else self.terms 66 | return [deg for deg, _ in terms] 67 | 68 | def coeffs(self, reverse=False): 69 | max_deg = self.degree() 70 | coeffs = [0] * (max_deg + 1) 71 | for deg, coeff in self.terms: 72 | coeffs[deg] = coeff 73 | if reverse: 74 | coeffs.reverse() 75 | return coeffs 76 | 77 | def sqrt(self): 78 | deg, coeff = self.terms[-1] 79 | new = deg // 2, coeff ** 0.5 80 | root = Polynomial([new]) 81 | temp = Polynomial([new]) 82 | rem = self - root * root 83 | for _ in range(deg): 84 | temp.add_term(*temp.terms[0]) 85 | try: 86 | root.add_term(*(rem // temp).terms[-1]) 87 | except IndexError: 88 | break 89 | temp.add_term(*root.terms[0]) 90 | rem = rem - temp * Polynomial([temp.terms[0]]) 91 | return root, rem 92 | 93 | def remove0terms(self): 94 | self.terms = [(deg, coeff) for deg, coeff in self.terms if coeff != 0] 95 | 96 | def degree(self): 97 | if self.terms: 98 | return self.terms[-1][0] 99 | return -1 100 | 101 | def lead_coeff(self): 102 | if self.terms: 103 | return self.terms[-1][1] 104 | return 0 105 | 106 | def deriv(self): 107 | derivative_terms = [(deg - 1, deg * coeff) for deg, coeff in self.terms if deg > 0] 108 | return Polynomial(derivative_terms) 109 | 110 | def integ(self, constant=0): 111 | integrated_terms = [(deg + 1, coeff / (deg + 1)) for deg, coeff in self.terms] 112 | if constant != 0: 113 | integrated_terms.append((0, constant)) 114 | return Polynomial(integrated_terms) 115 | 116 | def latex(self): 117 | return self.__repr__(True) 118 | 119 | def __repr__(self, use_latex=False): 120 | from .chars import int2superscript 121 | use_latex = config.use_latex or use_latex 122 | use_unicode = config.use_unicode 123 | if not self.terms: 124 | return "0" 125 | result = "" 126 | for i, (degree, coefficient) in enumerate(self.terms): 127 | if i != 0: 128 | if coefficient < 0: 129 | result += " - " 130 | else: 131 | result += " + " 132 | elif coefficient < 0: 133 | result += "-" 134 | if degree == 0: 135 | result += str(abs(coefficient)) 136 | elif abs(coefficient) != 1: 137 | result += str(abs(coefficient)) 138 | if degree > 0: 139 | if degree > 1: 140 | if use_latex: 141 | result += "x^{" + str(degree) + "}" 142 | elif use_unicode: 143 | result += "x" + int2superscript(str(degree)) 144 | else: 145 | result += "x^" + str(degree) 146 | else: 147 | result += "x" 148 | return result 149 | 150 | def __pos__(self): 151 | return Polynomial([(degree, coefficient) for degree, coefficient in self.terms]) 152 | 153 | def __neg__(self): 154 | return Polynomial([(degree, -coefficient) for degree, coefficient in self.terms]) 155 | 156 | def __add__(self, other): 157 | if isinstance(other, (int, float)): 158 | return Polynomial([(deg, coeff + other if deg == 0 else coeff) for deg, coeff in self.terms]) 159 | result = Polynomial() 160 | i, j = 0, 0 161 | while i < len(self.terms) or j < len(other.terms): 162 | if i == len(self.terms): 163 | result.add_term(other.terms[j][0], other.terms[j][1]) 164 | j += 1 165 | elif j == len(other.terms): 166 | result.add_term(self.terms[i][0], self.terms[i][1]) 167 | i += 1 168 | else: 169 | degree_self, coefficient_self = self.terms[i] 170 | degree_other, coefficient_other = other.terms[j] 171 | if degree_self < degree_other: 172 | result.add_term(degree_self, coefficient_self) 173 | i += 1 174 | elif degree_self > degree_other: 175 | result.add_term(degree_other, coefficient_other) 176 | j += 1 177 | else: 178 | try: 179 | result.add_term(degree_self, coefficient_self + coefficient_other) 180 | except OverflowError: 181 | result.add_term(degree_self, int(coefficient_self) + int(coefficient_other)) 182 | i += 1 183 | j += 1 184 | return result 185 | 186 | def __sub__(self, other): 187 | if isinstance(other, (int, float)): 188 | return Polynomial([(deg, coeff - other if deg == 0 else coeff) for deg, coeff in self.terms]) 189 | negative_other = -other 190 | return self + negative_other 191 | 192 | def __mul__(self, other): 193 | if isinstance(other, (int, float)): 194 | return Polynomial([(deg, coeff * other) for deg, coeff in self.terms]) 195 | result_terms = {} 196 | for degree_self, coeff_self in self.terms: 197 | for degree_other, coeff_other in other.terms: 198 | degree_product = degree_self + degree_other 199 | coeff_product = coeff_self * coeff_other 200 | if degree_product in result_terms: 201 | result_terms[degree_product] = result_terms[degree_product] + coeff_product 202 | else: 203 | result_terms[degree_product] = coeff_product 204 | return Polynomial(sorted(result_terms.items())) 205 | 206 | def __truediv__(self, other): 207 | if isinstance(other, (int, float)): 208 | if other == 0: 209 | raise ValueError("Cannot divide by zero") 210 | return Polynomial([(deg, coeff / other) for deg, coeff in self.terms]) 211 | if not other.terms or other.lead_coeff() == 0: 212 | raise ValueError("Cannot divide by zero polynomial") 213 | quotient = Polynomial() 214 | remainder = Polynomial(self.terms) 215 | other_degree = other.degree() 216 | other_lead_coeff = other.lead_coeff() 217 | while remainder.degree() >= other_degree: 218 | quotient_degree = remainder.degree() - other_degree 219 | try: 220 | quotient_coefficient = (remainder.lead_coeff() / other_lead_coeff) 221 | except OverflowError: 222 | quotient_coefficient = (remainder.lead_coeff() // other_lead_coeff) 223 | quotient_term = Polynomial([(quotient_degree, quotient_coefficient)]) 224 | quotient.add_term(quotient_degree, quotient_coefficient) 225 | remainder = remainder - quotient_term * other 226 | return quotient, remainder 227 | 228 | def __floordiv__(self, other): 229 | if isinstance(other, (int, float)): 230 | return Polynomial([(deg, coeff // other) for deg, coeff in self.terms]) 231 | return (self / other)[0] 232 | 233 | def __mod__(self, other): 234 | if isinstance(other, (int, float)): 235 | return Polynomial([(deg, coeff % other) for deg, coeff in self.terms]) 236 | return (self / other)[1] 237 | 238 | def __divmod__(self, other): 239 | if isinstance(other, (int, float)): 240 | if other == 0: 241 | raise ValueError("Cannot divide or modulo by zero") 242 | int_part_terms = [] 243 | mod_part_terms = [] 244 | for deg, coeff in self.terms: 245 | int_coeff, mod_coeff = divmod(coeff, other) 246 | if int_coeff != 0 or deg == 0: 247 | int_part_terms.append((deg, int_coeff)) 248 | if mod_coeff != 0: 249 | mod_part_terms.append((deg, mod_coeff)) 250 | int_part = Polynomial(int_part_terms) 251 | mod_part = Polynomial(mod_part_terms) 252 | return int_part, mod_part 253 | else: 254 | return self / other 255 | 256 | def __int__(self): 257 | self.terms = [(degree, int(coefficient)) for degree, coefficient in self.terms] 258 | return 0 259 | 260 | def __float__(self): 261 | self.terms = [(degree, float(coefficient)) for degree, coefficient in self.terms] 262 | return 0.0 263 | 264 | def __round__(self, n=None): 265 | return Polynomial([(degree, round(coefficient, n)) for degree, coefficient in self.terms]) 266 | 267 | def __pow__(self, power, modulo=None): 268 | if power == 0: 269 | return Polynomial([(0, 1)]) 270 | result = Polynomial([(0, 1)]) 271 | for _ in range(power): 272 | result = result * self 273 | if modulo: 274 | result = result % modulo 275 | return result 276 | 277 | def stationaries(self, tolerance=1e-15): 278 | derivative = self.deriv() 279 | return derivative.roots(tolerance=tolerance) 280 | 281 | def saddles(self, tolerance=1e-15): 282 | first_derivative = self.deriv() 283 | second_derivative = first_derivative.deriv() 284 | stationary_points = self.stationaries(tolerance=tolerance) 285 | saddle_points = [] 286 | for point in stationary_points: 287 | if abs(second_derivative.evaluate(point)) > tolerance and first_derivative.evaluate( 288 | point) * second_derivative.evaluate(point) < 0: 289 | saddle_points.append(point) 290 | return saddle_points 291 | 292 | def minima(self, tolerance=1e-15): 293 | second_derivative = self.deriv().deriv() 294 | stationary_points = self.stationaries(tolerance=tolerance) 295 | minimums = [] 296 | for point in stationary_points: 297 | if second_derivative.evaluate(point) > 0: 298 | minimums.append(point) 299 | return minimums 300 | 301 | def maxima(self, tolerance=1e-15): 302 | second_derivative = self.deriv().deriv() 303 | stationary_points = self.stationaries(tolerance=tolerance) 304 | maximums = [] 305 | for point in stationary_points: 306 | if second_derivative.evaluate(point) < 0: 307 | maximums.append(point) 308 | return maximums 309 | 310 | def inflections(self, tolerance=1e-15): 311 | second_derivative = self.deriv().deriv() 312 | third_derivative = second_derivative.deriv() 313 | potential_inflections = second_derivative.roots(tolerance=tolerance) 314 | inflection_points = [x for x in potential_inflections if abs(third_derivative.evaluate(x)) > tolerance] 315 | return inflection_points 316 | 317 | 318 | def poly(terms=None): 319 | return Polynomial(terms) 320 | 321 | 322 | def from_coeffs(coeffs): 323 | return Polynomial([(degree, coefficient) for degree, coefficient in enumerate(coeffs) if coefficient != 0]) 324 | 325 | 326 | def from_coords(coords): 327 | from .regs import poly_reg 328 | return from_coeffs(reversed(poly_reg(*zip(*coords)))) 329 | 330 | 331 | def legpoly(n, single=True): 332 | x = Polynomial([(1, 1)]) 333 | p = [Polynomial([(0, 1)]), x] 334 | for i in range(1, n): 335 | m = Polynomial([(0, (2 * i + 1) / (i + 1))]) * x * p[i] - Polynomial([(0, i / (i + 1))]) * p[i - 1] 336 | p.append(m) 337 | return p[n] if single else p 338 | 339 | 340 | def leggauss(n): 341 | polynomial = legpoly(n) 342 | roots = polynomial.roots(False) 343 | weights = [] 344 | for node in roots: 345 | factor1 = 1 - node ** 2 346 | derivative_poly = polynomial.deriv() 347 | derivative_value = derivative_poly.evaluate(node) 348 | weight = 2 / (factor1 * derivative_value ** 2) 349 | weights.append(weight) 350 | return roots, weights 351 | 352 | 353 | def chebpoly(n, single=True): 354 | result = [Polynomial([(0, 1)]), Polynomial([(1, 1)])] 355 | multiple = Polynomial([(1, 2)]) 356 | for _ in range(n - 1): 357 | result.append(multiple * result[-1] - result[-2]) 358 | return result[n] if single else result 359 | 360 | 361 | def chebgauss(n): 362 | from math import cos 363 | pi_over_n = 3.141592653589793 / n 364 | pi_over_2n = pi_over_n / 2 365 | roots = [cos((2 * i - 1) * pi_over_2n) for i in range(1, n + 1)] 366 | weights = [pi_over_n for _ in range(n)] 367 | return roots, weights 368 | 369 | 370 | def lagpoly(n, single=True): 371 | result = [Polynomial([(0, 1)]), Polynomial([(0, 1), (1, -1)])] 372 | for i in range(2, n + 1): 373 | result.append((Polynomial([(0, 2 * i - 1), (1, -1)]) * result[-1] - Polynomial([(0, i - 1)]) * result[-2]) / i) 374 | return result[n] if single else result 375 | 376 | 377 | def laggauss(n): 378 | polynomial, next_polynomial = lagpoly(n + 1, False)[-2:] 379 | roots = polynomial.roots() 380 | weights = [] 381 | const = (n + 1) ** 2 382 | for node in roots: 383 | next_polynomial_value = next_polynomial.evaluate(node) 384 | weight = node / const * next_polynomial_value ** 2 385 | weights.append(weight) 386 | return roots, weights 387 | -------------------------------------------------------------------------------- /pypynum/pprinters.py: -------------------------------------------------------------------------------- 1 | def pprint_matrix(matrix, style="numpy", output=True): 2 | if str(type(matrix)) != "": 3 | raise TypeError("The input must be of type 'pypynum.matrices.Matrix'") 4 | style = style.strip().lower() 5 | supported_styles = ["numpy", "mpmath", "sympy", "borderless", "numbered"] 6 | if style not in supported_styles: 7 | raise ValueError("Unsupported style '{}'. Supported styles are: {}".format(style, supported_styles)) 8 | max_length = max([len(str(item)) for row in matrix for item in row]) 9 | if style == "numbered": 10 | max_length = max(max_length, len(str(matrix.cols))) 11 | format_str = "{:" + str(max_length) + "}" 12 | row_strings = [] 13 | separator = " " if style in ["mpmath", "sympy"] else " " 14 | for row in matrix: 15 | formatted_row = separator.join([format_str.format(item) for item in row]) 16 | row_strings.append(formatted_row) 17 | result = "" 18 | if style == "numpy": 19 | result = str(matrix) 20 | elif style == "mpmath": 21 | result = "\n".join(["[" + row_str + "]" for row_str in row_strings]) 22 | elif style == "sympy": 23 | border_length = max_length + 2 24 | column_count = matrix.cols 25 | middle_border = "⎢" + " " * (border_length * column_count - 2) + "⎥" 26 | end = len(row_strings) - 1 27 | if end < 1: 28 | result = "[" + row_strings[0] + "]" 29 | else: 30 | result = "⎡" + row_strings[0] + "⎤" 31 | for i, row_str in enumerate(row_strings[1:end]): 32 | result += "".join(["\n", middle_border, "\n", "⎢", row_str, "⎥"]) 33 | result += "".join(["\n", middle_border, "\n", "⎣", row_strings[-1], "⎦"]) 34 | elif style == "borderless": 35 | result = "\n".join(row_strings) 36 | elif style == "numbered": 37 | row_number_width, column_number_width = matrix.rows, matrix.cols 38 | row_number_format = "{:>" + str(len(str(row_number_width))) + "}" 39 | column_number_format = "{:^" + str(max_length) + "}" 40 | column_header = " " * len(str(row_number_width)) + " | " + " ".join( 41 | [column_number_format.format(str(j + 1)) for j in range(column_number_width)]) 42 | for i, row in enumerate(row_strings): 43 | row_string = row_number_format.format((i + 1)) + " | " + row 44 | row_strings[i] = row_string 45 | result = "".join([column_header, "\n", "-" * len(row_strings[0]), "\n", "\n".join(row_strings)]) 46 | if output: 47 | print(result) 48 | else: 49 | return result 50 | -------------------------------------------------------------------------------- /pypynum/random.py: -------------------------------------------------------------------------------- 1 | from random import choice as __choice, gauss as __gauss, randint as __randint, random as __random, uniform as __uniform 2 | from .types import RandomError, Union, arr, ite, real 3 | 4 | 5 | def __validate_shape(shape): 6 | if not (isinstance(shape, (list, tuple)) and all([isinstance(item, int) and item > 0 for item in shape])): 7 | raise RandomError("The shape must be all positive integers") 8 | 9 | 10 | def __create_nested_list(dimensions, func): 11 | return func() if len(dimensions) == 0 else [__create_nested_list(dimensions[1:], func) 12 | for _ in range(dimensions[0])] 13 | 14 | 15 | def choice(seq: ite, shape: arr = None): 16 | if not isinstance(seq, (list, tuple, str)): 17 | raise TypeError("The parameter seq must be iterable") 18 | if shape is not None: 19 | __validate_shape(shape) 20 | return __create_nested_list(shape, lambda: __choice(seq)) 21 | return __choice(seq) 22 | 23 | 24 | def gauss(mu: real = 0, sigma: real = 1, shape: arr = None) -> Union[float, list]: 25 | if not (isinstance(mu, (int, float)) and isinstance(sigma, (int, float))): 26 | raise RandomError("The parameters mu and sigma must both be real numbers") 27 | if shape is not None: 28 | __validate_shape(shape) 29 | return __create_nested_list(shape, lambda: __gauss(mu, sigma)) 30 | return __gauss(mu, sigma) 31 | 32 | 33 | def randint(a: int, b: int, shape: arr = None) -> Union[int, list]: 34 | if not (isinstance(a, int) and isinstance(b, int)): 35 | raise RandomError("The range must be all integers") 36 | if shape is not None: 37 | __validate_shape(shape) 38 | return __create_nested_list(shape, lambda: __randint(a, b)) 39 | return __randint(a, b) 40 | 41 | 42 | def rand(shape: arr = None) -> Union[float, list]: 43 | if shape is not None: 44 | __validate_shape(shape) 45 | return __create_nested_list(shape, __random) 46 | return __random() 47 | 48 | 49 | def uniform(a: real, b: real, shape: arr = None) -> Union[float, list]: 50 | if not (isinstance(a, (int, float)) and isinstance(b, (int, float))): 51 | raise RandomError("The range must be all real numbers") 52 | if shape is not None: 53 | __validate_shape(shape) 54 | return __create_nested_list(shape, lambda: __uniform(a, b)) 55 | return __uniform(a, b) 56 | -------------------------------------------------------------------------------- /pypynum/regs.py: -------------------------------------------------------------------------------- 1 | from .types import arr 2 | 3 | 4 | def lin_reg(x: arr, y: arr) -> list: 5 | from .maths import mean 6 | if len(x) != len(y): 7 | raise ValueError("The array length of the independent and dependent variables must be equal") 8 | x_mean = mean(x) 9 | y_mean = mean(y) 10 | numerator = sum([(x - x_mean) * (y - y_mean) for x, y in zip(x, y)]) 11 | denominator = sum([(x - x_mean) ** 2 for x in x]) 12 | alpha = numerator / denominator 13 | beta = y_mean - alpha * x_mean 14 | return [alpha, beta] 15 | 16 | 17 | def par_reg(x: arr, y: arr) -> list: 18 | from .maths import mean, var 19 | if len(x) != len(y): 20 | raise ValueError("The array length of the independent and dependent variables must be equal") 21 | 22 | def solve_equations(a1, b1, c1, a2, b2, c2): 23 | denominator = a1 * b2 - a2 * b1 24 | if denominator == 0: 25 | return None 26 | return [(c1 * b2 - c2 * b1) / denominator, (a1 * c2 - a2 * c1) / denominator] 27 | 28 | x2 = [_ ** 2 for _ in x] 29 | xy = [a + b for a, b in zip(x, y)] 30 | x2x = [a ** 2 + a for a in x] 31 | x2y = [a ** 2 + b for a, b in zip(x, y)] 32 | vx2 = var(x2) 33 | vxy = var(xy) 34 | vx2x = var(x2x) 35 | vx2y = var(x2y) 36 | v1y = (vxy - var(x) - var(y)) / 2 37 | v2y = (vx2y - vx2 - var(y)) / 2 38 | v12 = (vx2x - vx2 - var(x)) / 2 39 | o = [var(x), v12, v1y, v12, vx2, v2y] 40 | b, a = solve_equations(o[0], o[1], o[2], o[3], o[4], o[5]) 41 | c = mean(y) - b * mean(x) - a * mean([_ ** 2 for _ in x]) 42 | return [a, b, c] 43 | 44 | 45 | def poly_reg(x: arr, y: arr, n: int = None) -> list: 46 | from .matrices import mat 47 | if len(x) != len(y): 48 | raise ValueError("The array length of the independent and dependent variables must be equal") 49 | if n is None: 50 | n = len(x) - 1 51 | if not isinstance(n, int) or n <= 0: 52 | raise ValueError("The degree of a polynomial must be a natural number") 53 | m = len(x) 54 | _x = [[_ ** (n - i) for _ in x] for i in range(n)] 55 | _x.append([1] * m) 56 | _x = mat(_x) 57 | return ((_x @ _x.t()).inv() @ _x @ mat([list(y)]).t()).flatten() 58 | -------------------------------------------------------------------------------- /pypynum/seqs.py: -------------------------------------------------------------------------------- 1 | from .kernels import matmul2x2kernel, matmul3x3kernel, matmul4x4kernel 2 | from .tools import fast_pow 3 | from .types import Union, arr, real 4 | 5 | 6 | def pascal(n: int) -> list: 7 | triangle = [[1]] 8 | for i in range(1, n + 1): 9 | new_row = [1] 10 | for j in range(1, i): 11 | new_row.append(triangle[i - 1][j - 1] + triangle[i - 1][j]) 12 | new_row.append(1) 13 | triangle.append(new_row) 14 | return triangle 15 | 16 | 17 | def bell(n: int) -> list: 18 | triangle = [[1]] 19 | for i in range(1, n + 1): 20 | new_row = [triangle[i - 1][-1]] 21 | for j in range(1, i + 1): 22 | new_row.append(new_row[j - 1] + triangle[i - 1][j - 1]) 23 | triangle.append(new_row) 24 | return triangle 25 | 26 | 27 | def stirling1(n: int) -> list: 28 | triangle = [[1]] 29 | for i in range(1, n + 1): 30 | new_row = [0] 31 | for j in range(1, i): 32 | new_row.append((i - 1) * triangle[i - 1][j] + triangle[i - 1][j - 1]) 33 | new_row.append(triangle[i - 1][i - 1]) 34 | triangle.append(new_row) 35 | return triangle 36 | 37 | 38 | def stirling2(n: int) -> list: 39 | triangle = [[1]] 40 | for i in range(1, n + 1): 41 | new_row = [0] 42 | for j in range(1, i): 43 | new_row.append(j * triangle[i - 1][j] + triangle[i - 1][j - 1]) 44 | new_row.append(triangle[i - 1][i - 1]) 45 | triangle.append(new_row) 46 | return triangle 47 | 48 | 49 | def farey(n: int) -> list: 50 | a2, b2 = None, None 51 | if n == 0: 52 | return [] 53 | elif n == 1: 54 | return [(0, 1), (1, 1)] 55 | else: 56 | f = farey(n - 1) 57 | m = len(f) - 1 58 | result = [] 59 | for i in range(m): 60 | a1, b1 = f[i] 61 | a2, b2 = f[i + 1] 62 | result.append((a1, b1)) 63 | temp = (a1 + a2, b1 + b2) 64 | if temp[0] <= temp[1] <= n: 65 | result.append(temp) 66 | result.append((a2, b2)) 67 | return result 68 | 69 | 70 | def fibonacci(n: int, single: bool = True) -> Union[int, list]: 71 | if isinstance(n, float): 72 | n = int(n) 73 | if not single: 74 | if n == 0: 75 | return [0] 76 | res = [0, 1] 77 | for i in range(2, n + 1): 78 | res.append(res[i - 1] + res[i - 2]) 79 | return res 80 | if n <= 1: 81 | return max(n, 0) 82 | a = [[1, 1], [1, 0]] 83 | res = fast_pow(a, n, [[1, 0], [0, 1]], matmul2x2kernel) 84 | return res[0][1] 85 | 86 | 87 | def pell(n: int, single: bool = True) -> Union[int, list]: 88 | if isinstance(n, float): 89 | n = int(n) 90 | if not single: 91 | if n == 0: 92 | return [0] 93 | res = [0, 1] 94 | for i in range(2, n + 1): 95 | res.append(2 * res[i - 1] + res[i - 2]) 96 | return res 97 | if n <= 1: 98 | return max(n, 0) 99 | a = [[2, 1], [1, 0]] 100 | res = fast_pow(a, n, [[1, 0], [0, 1]], matmul2x2kernel) 101 | return res[0][1] 102 | 103 | 104 | def lucas(n: int, single: bool = True) -> Union[int, list]: 105 | if isinstance(n, float): 106 | n = int(n) 107 | if not single: 108 | if n == 0: 109 | return [2] 110 | res = [2, 1] 111 | for i in range(2, n + 1): 112 | res.append(res[i - 1] + res[i - 2]) 113 | return res 114 | if n == 0: 115 | return 2 116 | if n == 1: 117 | return 1 118 | a = [[1, 1], [1, 0]] 119 | res = fast_pow(a, n, [[1, 0], [0, 1]], matmul2x2kernel) 120 | return res[0][0] + res[1][1] 121 | 122 | 123 | def tribonacci(n: int, single: bool = True) -> Union[int, list]: 124 | if isinstance(n, float): 125 | n = int(n) 126 | res = [0, 1, 1] 127 | if not single: 128 | if n < 3: 129 | return res[:n] 130 | for i in range(3, n + 1): 131 | res.append(res[i - 1] + res[i - 2] + res[i - 3]) 132 | return res[:n] if n < 3 else res 133 | if n < 3: 134 | return res[n] 135 | a = [[1, 1, 1], [1, 0, 0], [0, 1, 0]] 136 | res = fast_pow(a, n - 1, [[1, 0, 0], [0, 1, 0], [0, 0, 1]], matmul3x3kernel) 137 | return res[0][0] 138 | 139 | 140 | def tetranacci(n: int, single: bool = True) -> Union[int, list]: 141 | if isinstance(n, float): 142 | n = int(n) 143 | res = [0, 1, 1, 2] 144 | if not single: 145 | if n < 4: 146 | return res[:n] 147 | for i in range(4, n + 1): 148 | res.append(res[i - 1] + res[i - 2] + res[i - 3] + res[i - 4]) 149 | return res 150 | if n < 4: 151 | return res[n] 152 | a = [[1, 1, 1, 1], [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0]] 153 | res = fast_pow(a, n - 1, [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]], matmul4x4kernel) 154 | return res[0][0] 155 | 156 | 157 | def catalan(n: int, single: bool = True) -> Union[int, list]: 158 | try: 159 | from math import comb 160 | except ImportError: 161 | from .maths import combination as comb 162 | if isinstance(n, float): 163 | n = int(n) 164 | if not single: 165 | if n == 0: 166 | return [1] 167 | res = [1] 168 | for i in range(1, n + 1): 169 | res.append(res[i - 1] * (4 * i - 2) // (i + 1)) 170 | return res 171 | return comb(2 * n, n) // (n + 1) 172 | 173 | 174 | def bernoulli(n: int, single: bool = True) -> arr: 175 | from fractions import Fraction 176 | try: 177 | from math import comb 178 | except ImportError: 179 | from .maths import combination as comb 180 | if isinstance(n, float): 181 | n = int(n) 182 | result = [1, Fraction(-0.5)] 183 | if not single and n == 0: 184 | return [(1, 1)] 185 | for m in range(2, n + 1): 186 | if m & 1: 187 | b = 0 188 | else: 189 | s = sum([comb(m + 1, i) * result[i] for i in range(m)]) 190 | b = -s / (m + 1) 191 | result.append(b) 192 | return result[n].as_integer_ratio() if single else [n.as_integer_ratio() for n in result] 193 | 194 | 195 | def recaman(n: int, single: bool = True) -> Union[int, list]: 196 | if isinstance(n, float): 197 | n = int(n) 198 | seq = [] 199 | for i in range(n + 1): 200 | if i == 0: 201 | x = 0 202 | else: 203 | x = seq[i - 1] - i 204 | if x >= 0 and x not in seq: 205 | seq += [x] 206 | else: 207 | seq += [seq[i - 1] + i] 208 | return seq[n] if single else seq 209 | 210 | 211 | def sylvester(n: int, single: bool = True) -> Union[int, list]: 212 | if isinstance(n, float): 213 | n = int(n) 214 | result = [2] 215 | a = 2 216 | for _ in range(n): 217 | a = a * a - a + 1 218 | result.append(a) 219 | return result[n] if single else result 220 | 221 | 222 | def padovan(n: int, single: bool = True) -> Union[int, list]: 223 | if isinstance(n, float): 224 | n = int(n) 225 | result = [1, 0, 0] 226 | if not single and n < len(result): 227 | return result[:n + 1] 228 | for i in range(3, n + 1): 229 | result.append(result[i - 2] + result[i - 3]) 230 | return result[n] if single else result 231 | 232 | 233 | def perrin(n: int, single: bool = True) -> Union[int, list]: 234 | if isinstance(n, float): 235 | n = int(n) 236 | result = [3, 0, 2] 237 | if not single and n < len(result): 238 | return result[:n + 1] 239 | for i in range(3, n + 1): 240 | result.append(result[i - 2] + result[i - 3]) 241 | return result[n] if single else result 242 | 243 | 244 | def pelllucas(n: int, single: bool = True) -> Union[int, list]: 245 | if isinstance(n, float): 246 | n = int(n) 247 | if not single: 248 | if n == 0: 249 | return [2] 250 | res = [2, 2] 251 | for i in range(2, n + 1): 252 | res.append(2 * res[i - 1] + res[i - 2]) 253 | return res 254 | if n <= 1: 255 | return 2 256 | a = [[2, 1], [1, 0]] 257 | res = fast_pow(a, n, [[1, 0], [0, 1]], matmul2x2kernel) 258 | return res[0][0] + res[1][1] 259 | 260 | 261 | def arithmetic_sequence(*, a1: real = None, an: real = None, d: real = None, n: real = None, s: real = None) -> dict: 262 | if [a1, an, d, n, s].count(None) != 2: 263 | raise ValueError("Must provide exactly three valid parameters") 264 | 265 | def solve_quadratic_equation(a, b, c): 266 | delta = b ** 2 - 4 * a * c 267 | if delta > 0: 268 | x1 = (-b + delta ** 0.5) / (2 * a) 269 | x2 = (-b - delta ** 0.5) / (2 * a) 270 | return [x1, x2] 271 | elif delta == 0: 272 | x = -b / (2 * a) 273 | return [x] 274 | else: 275 | return [] 276 | 277 | if any([not isinstance(val, (int, float, type(None))) for val in [a1, an, d, n, s]]): 278 | raise ValueError("The input parameters must all be real numbers") 279 | if a1 is not None and an is not None and d is not None: 280 | n = (an - a1) / d + 1 281 | s = n / 2 * (2 * a1 + (n - 1) * d) 282 | elif a1 is not None and an is not None and n is not None: 283 | d = (an - a1) / (n - 1) 284 | s = n / 2 * (a1 + an) 285 | elif a1 is not None and an is not None and s is not None: 286 | n = s * 2 / (a1 + an) 287 | d = (an - a1) / (n - 1) 288 | elif a1 is not None and d is not None and n is not None: 289 | an = a1 + (n - 1) * d 290 | s = n / 2 * (2 * a1 + (n - 1) * d) 291 | elif a1 is not None and d is not None and s is not None: 292 | n = [item for item in solve_quadratic_equation(d, 2 * a1 - d, -2 * s) if item > 0] 293 | if len(n) == 1: 294 | n = n[0] 295 | an = a1 + (n - 1) * d 296 | elif a1 is not None and n is not None and s is not None: 297 | an = 2 * s / n - a1 298 | d = (an - a1) / (n - 1) 299 | elif an is not None and d is not None and n is not None: 300 | a1 = an - (n - 1) * d 301 | s = n / 2 * (a1 + an) 302 | elif an is not None and d is not None and s is not None: 303 | n = [item for item in solve_quadratic_equation(-d, 2 * an + d, -2 * s) if item > 0] 304 | if len(n) == 1: 305 | n = n[0] 306 | a1 = an - (n - 1) * d 307 | elif an is not None and n is not None and s is not None: 308 | d = (an * n - s) / (n * (n - 1) / 2) 309 | a1 = an - (n - 1) * d 310 | elif d is not None and n is not None and s is not None: 311 | a1 = (s / n) - (d * (n - 1)) / 2 312 | an = a1 + (n - 1) * d 313 | if isinstance(n, list): 314 | n = sorted(n) 315 | return {"a1": a1, "an": an, "d": d, "n": n, "s": s} 316 | 317 | 318 | def geometric_sequence(*, a1: real = None, an: real = None, r: real = None, n: real = None, s: real = None) -> dict: 319 | from math import log 320 | if [a1, an, r, n, s].count(None) != 2: 321 | raise ValueError("Must provide exactly three valid parameters") 322 | if any([not isinstance(val, (int, float, type(None))) for val in [a1, an, r, n, s]]): 323 | raise ValueError("The input parameters must all be real numbers") 324 | if a1 is not None and an is not None and r is not None: 325 | n = log(an / a1, r) + 1 326 | s = a1 * (r ** n - 1) / (r - 1) if r != 1 else a1 * n 327 | elif a1 is not None and an is not None and n is not None: 328 | r = (an / a1) ** (1 / (n - 1)) 329 | s = a1 * (r ** n - 1) / (r - 1) if r != 1 else a1 * n 330 | elif a1 is not None and an is not None and s is not None: 331 | r = (s - a1) / (s - an) 332 | n = log(an / a1, r) + 1 333 | elif a1 is not None and r is not None and n is not None: 334 | an = a1 * r ** (n - 1) 335 | s = a1 * (r ** n - 1) / (r - 1) if r != 1 else a1 * n 336 | elif a1 is not None and r is not None and s is not None: 337 | an = (s * r - s + a1) / r 338 | n = log(an / a1, r) + 1 339 | elif a1 is not None and n is not None and s is not None: 340 | an = NotImplemented 341 | r = NotImplemented 342 | elif an is not None and r is not None and n is not None: 343 | a1 = an / r ** (n - 1) 344 | s = a1 * (r ** n - 1) / (r - 1) if r != 1 else a1 * n 345 | elif an is not None and r is not None and s is not None: 346 | a1 = s * (1 - r) + an * r 347 | n = log(an / a1, r) + 1 348 | elif an is not None and n is not None and s is not None: 349 | a1 = NotImplemented 350 | r = NotImplemented 351 | elif r is not None and n is not None and s is not None: 352 | a1 = s * (r - 1) / (r ** n - 1) 353 | an = a1 * r ** (n - 1) 354 | if isinstance(n, list): 355 | n = sorted(n) 356 | return {"a1": a1, "an": an, "r": r, "n": n, "s": s} 357 | -------------------------------------------------------------------------------- /pypynum/stattest.py: -------------------------------------------------------------------------------- 1 | def mediantest(*samples, ties="below", lambda_=1, corr=True): 2 | from math import isnan 3 | from .matrices import mat 4 | from .maths import median 5 | if len(samples) < 2: 6 | raise ValueError("median_test requires two or more samples.") 7 | data = [list(sample) for sample in samples] 8 | cdata = [item for sublist in data for item in sublist] 9 | grand_median = median([x for x in cdata if x is not None and not (isinstance(x, float) and isnan(x))]) 10 | table = [[0] * len(data) for _ in range(2)] 11 | for k, sample in enumerate(data): 12 | above = sum([x > grand_median for x in sample if x is not None 13 | and not (isinstance(x, float) and isnan(x))]) 14 | below = sum([x < grand_median for x in sample if x is not None 15 | and not (isinstance(x, float) and isnan(x))]) 16 | equal = len(sample) - (above + below) 17 | table[0][k] += above 18 | table[1][k] += below 19 | if ties == "below": 20 | table[1][k] += equal 21 | elif ties == "above": 22 | table[0][k] += equal 23 | elif ties != "ignore": 24 | raise ValueError("Invalid ties option: {}. Valid options are: ['below', 'above', 'ignore']".format(ties)) 25 | stat, p, _, _ = chi2_cont(table, lambda_=lambda_, corr=corr) 26 | return stat, p, grand_median, mat(table) 27 | 28 | 29 | def skewtest(data: list, two_tailed: bool = True) -> tuple: 30 | from math import log, sqrt 31 | from .dists import normal_cdf 32 | from .maths import skew 33 | n = len(data) 34 | y = skew(data) * sqrt(((n + 1) * (n + 3)) / (6 * (n - 2))) 35 | beta = 3 * (n ** 2 + 27 * n - 70) * (n + 1) * (n + 3) / ((n - 2) * (n + 5) * (n + 7) * (n + 9)) 36 | w = sqrt(2 * (beta - 1)) - 1 37 | delta = 1 / sqrt(0.5 * log(w)) 38 | alpha = sqrt(2 / (w - 1)) 39 | z = delta * log(y / alpha + sqrt((y / alpha) ** 2 + 1)) 40 | p_value = normal_cdf(z) if z > 0 else 1 - normal_cdf(z) 41 | if two_tailed: 42 | p_value = 2 * min(p_value, 1 - p_value) 43 | return z, p_value 44 | 45 | 46 | def kurttest(data: list, two_tailed: bool = True) -> tuple: 47 | from math import sqrt 48 | from .dists import normal_cdf 49 | from .maths import kurt, sign 50 | n = len(data) 51 | b = kurt(data, fisher=False) 52 | e = 3 * (n - 1) / (n + 1) 53 | vb = 24 * n * (n - 2) * (n - 3) / ((n + 1) * (n + 1) * (n + 3) * (n + 5)) 54 | x = (b - e) / sqrt(vb) 55 | sb = 6 * (n * n - 5 * n + 2) / ((n + 7) * (n + 9)) * sqrt((6 * (n + 3) * (n + 5)) / (n * (n - 2) * (n - 3))) 56 | a = 6 + 8 / sb * (2 / sb + sqrt(1 + 4 / (sb ** 2))) 57 | d = 1 + x * sqrt(2 / (a - 4)) 58 | t = sign(d) * (d == 0 and float("nan") or ((1 - 2 / a) / abs(d)) ** (1 / 3)) 59 | z = (1 - 2 / (9 * a) - t) / sqrt(2 / (9 * a)) 60 | p_value = normal_cdf(z) if z > 0 else 1 - normal_cdf(z) 61 | if two_tailed: 62 | p_value = 2 * min(p_value, 1 - p_value) 63 | return z, p_value 64 | 65 | 66 | def normaltest(data: list) -> tuple: 67 | from .dists import chi2_cdf 68 | s = skewtest(data)[0] 69 | k = kurttest(data)[0] 70 | n = s * s + k * k 71 | return n, 1 - chi2_cdf(2, n) 72 | 73 | 74 | def chisquare(observed: list, expected: list = None) -> tuple: 75 | from .dists import chi2_pdf 76 | from .maths import integ 77 | if expected is None: 78 | expected = [1 / len(observed)] * len(observed) 79 | chi2_stat = sum([(o - e) ** 2 / e for o, e in zip(observed, expected)]) 80 | dof = len(observed) - 1 81 | try: 82 | p_value = integ(chi2_pdf, chi2_stat, 1000, df=dof) 83 | except OverflowError: 84 | p_value = float("nan") 85 | return chi2_stat, p_value 86 | 87 | 88 | def chi2_cont(contingency: list, lambda_: float = 1.0, calc_p: bool = True, corr: bool = True) -> tuple: 89 | from .matrices import mat 90 | from .dists import chi2_cdf 91 | from .maths import xlogy 92 | contingency = mat(contingency) 93 | dof = (contingency.rows - 1) * (contingency.cols - 1) 94 | row_sums = [sum(row) for row in contingency] 95 | col_sums = [sum([contingency[i][j] for i in range(contingency.rows)]) for j in range(contingency.cols)] 96 | grand_total = sum(row_sums) 97 | expected = [[row_sums[i] * col_sums[j] / grand_total for j in range(contingency.cols)] 98 | for i in range(contingency.rows)] 99 | if dof == 1 and corr: 100 | corrected = [[0, 0], [0, 0]] 101 | for i in range(2): 102 | for j in range(2): 103 | diff = expected[i][j] - contingency[i][j] 104 | direction = 1 if diff > 0 else -1 if diff < 0 else 0 105 | magnitude = min(0.5, abs(diff)) 106 | corrected[i][j] = contingency[i][j] + direction * magnitude 107 | contingency = mat(corrected) 108 | chi2 = 0 109 | for i in range(contingency.rows): 110 | for j in range(contingency.cols): 111 | observed_freq = contingency[i][j] 112 | expected_freq = expected[i][j] 113 | if expected_freq > 0: 114 | if lambda_ == 1: 115 | terms = (observed_freq - expected_freq) ** 2 / expected_freq 116 | elif lambda_ == 0: 117 | terms = 2.0 * xlogy(observed_freq, observed_freq / expected_freq) 118 | elif lambda_ == -1: 119 | terms = 2.0 * xlogy(expected_freq, expected_freq / observed_freq) 120 | else: 121 | terms = observed_freq * ((observed_freq / expected_freq) ** lambda_ - 1) 122 | terms /= 0.5 * lambda_ * (lambda_ + 1) 123 | chi2 += terms 124 | try: 125 | p = 1 - chi2_cdf(chi2, df=dof) if calc_p else None 126 | except OverflowError: 127 | p = float("nan") 128 | return chi2, p, dof, mat(expected) 129 | -------------------------------------------------------------------------------- /pypynum/symbols.py: -------------------------------------------------------------------------------- 1 | __OPERATORS = {"**", "*", "//", "/", "%", "+", "-"} 2 | __BASIC = "%()*+-./0123456789" 3 | __ENGLISH = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" 4 | __GREEK = "ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩαβγδεζηθικλμνξοπρστυφχψω" 5 | __VALID = set(__BASIC + __ENGLISH + __GREEK) 6 | 7 | 8 | def parse_expr(expr: str) -> list: 9 | expr = expr.replace(" ", "") 10 | if any([item not in __VALID for item in expr]): 11 | raise ValueError("Characters other than Arabic numerals, English letters, Greek letters, operators, " 12 | "decimal points, and parentheses cannot appear in expressions") 13 | depth = 0 14 | pointer = 0 15 | result = [] 16 | for index in range(len(expr)): 17 | if expr[index] == "(": 18 | if depth == 0: 19 | result.append(index) 20 | depth += 1 21 | elif expr[index] == ")": 22 | if depth == 0: 23 | raise ValueError("The parentheses in the expression are not paired") 24 | depth -= 1 25 | if depth == 0: 26 | sub = parse_expr(expr[result[-1] + 1:index]) 27 | if not sub: 28 | raise ValueError("The inside of the parentheses in an expression cannot be empty") 29 | result[-1] = sub 30 | elif depth == 0 and expr[index] in __OPERATORS: 31 | number = expr[pointer:index] 32 | if number and "(" not in number: 33 | if number.count(".") > 1: 34 | raise ValueError("Syntax error in expression") 35 | nan = True 36 | for item in number: 37 | if nan and item.isdigit(): 38 | nan = False 39 | elif not nan and not (item == "." or item.isdigit()): 40 | raise NameError("The name of the algebra is invalid") 41 | result.append(number) 42 | if index != 0: 43 | pointer = index + 1 44 | else: 45 | pointer = index 46 | if expr[index] in "*/" and expr[index] == expr[index - 1]: 47 | if result[-1] in __OPERATORS: 48 | raise ValueError("Syntax error in expression") 49 | result.append(expr[index] * 2) 50 | elif index != 0 and expr[index] != expr[index + 1]: 51 | if result[-1] in __OPERATORS: 52 | raise ValueError("Syntax error in expression") 53 | result.append(expr[index]) 54 | number = expr[pointer:] 55 | if number and "(" not in number: 56 | result.append(number) 57 | if depth != 0: 58 | raise ValueError("The parentheses in the expression are not paired") 59 | return result if len(result) != 1 else result[0] 60 | 61 | 62 | # TODO 表达式展开 63 | # TODO 表达式化简 64 | # TODO 符号微分 65 | # TODO 符号积分 66 | ... 67 | -------------------------------------------------------------------------------- /pypynum/tensors.py: -------------------------------------------------------------------------------- 1 | from .arrays import Array, fill 2 | from .types import ShapeError 3 | 4 | MatchError = ShapeError("The shapes of two tensors do not match") 5 | 6 | 7 | class Tensor(Array): 8 | """ 9 | It is a tensor class that supports basic operations. 10 | :param data: An array in the form of a list 11 | :param check: Check the rationality of the input array 12 | """ 13 | 14 | def __init__(self, data, check=True): 15 | from warnings import warn 16 | warn("The 'Tensor' class is deprecated and will be removed in a future version. " 17 | "Please use the 'Array' class instead for similar functionality.", FutureWarning) 18 | super().__init__(data, check) 19 | 20 | def __add__(self, other): 21 | if isinstance(other, Tensor): 22 | if self.shape != other.shape: 23 | raise MatchError 24 | return Tensor(fill(self.shape, [t1 + t2 for t1, t2 in zip(self.flatten(), other.flatten())], rtype=list), 25 | False) 26 | elif isinstance(other, (int, float, complex)): 27 | return Tensor(tensor_and_number(self, "+", other), False) 28 | else: 29 | raise ValueError("The other must be a tensor or a number") 30 | 31 | def __sub__(self, other): 32 | if isinstance(other, Tensor): 33 | if self.shape != other.shape: 34 | raise MatchError 35 | return Tensor(fill(self.shape, [t1 - t2 for t1, t2 in zip(self.flatten(), other.flatten())], rtype=list), 36 | False) 37 | elif isinstance(other, (int, float, complex)): 38 | return Tensor(tensor_and_number(self, "-", other), False) 39 | else: 40 | raise ValueError("The other must be a tensor or a number") 41 | 42 | def __mul__(self, other): 43 | if isinstance(other, Tensor): 44 | if self.shape != other.shape: 45 | raise MatchError 46 | return Tensor(fill(self.shape, [t1 * t2 for t1, t2 in zip(self.flatten(), other.flatten())], rtype=list), 47 | False) 48 | elif isinstance(other, (int, float, complex)): 49 | return Tensor(tensor_and_number(self, "*", other), False) 50 | else: 51 | raise ValueError("The other must be a tensor or a number") 52 | 53 | def __matmul__(self, other): 54 | if isinstance(other, Tensor): 55 | if self.shape[:-2] != other.shape[:-2]: 56 | raise MatchError 57 | 58 | def matmul(s, o): 59 | if not isinstance(s[0][0], list) or not isinstance(o[0][0], list): 60 | return [[sum([s[i][k] * o[k][j] for k in range(len(s[0]))]) 61 | for j in range(len(o[0]))] for i in range(len(s))] 62 | if len(s) != len(o): 63 | raise ValueError("Tensor dimensions do not match") 64 | return [matmul(t1, t2) for t1, t2 in zip(s, o)] 65 | 66 | return Tensor(matmul(self.data, other.data), False) 67 | else: 68 | raise ValueError("The other must be a tensor") 69 | 70 | 71 | def tensor_and_number(tensor, operator, number): 72 | if isinstance(tensor, Tensor) or isinstance(tensor, list): 73 | _result = [] 74 | for item in tensor: 75 | _result.append(tensor_and_number(item, operator, number)) 76 | return _result 77 | else: 78 | if operator in ["+", "-", "*"]: 79 | return eval("{} {} {}".format(tensor, operator, number)) 80 | 81 | 82 | def ten(data: list) -> Tensor: 83 | return Tensor(data) 84 | 85 | 86 | del Array 87 | -------------------------------------------------------------------------------- /pypynum/test.py: -------------------------------------------------------------------------------- 1 | """ 2 | A Code Test File 3 | """ 4 | 5 | print("""\x1b[92m 6 | from pypynum import (arrays, geoms, hypcmpnms, logics, matrices, multiprec, special, vectors, 7 | ciphers, consts, equations, maths, plotting, random, regs, tools) 8 | \x1b[m""") 9 | print("...") 10 | 11 | from . import (arrays, geoms, hypcmpnms, logics, matrices, multiprec, special, vectors, 12 | ciphers, consts, equations, maths, plotting, random, regs, tools) 13 | 14 | # arrays 15 | print("""\x1b[92m 16 | print(arrays.array()) 17 | print(arrays.array([1, 2, 3, 4, 5, 6, 7, 8])) 18 | print(arrays.array([[1, 2, 3, 4], [5, 6, 7, 8]])) 19 | print(arrays.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])) 20 | \x1b[m""") 21 | print("\"\"\"") 22 | print(arrays.array()) 23 | print(arrays.array([1, 2, 3, 4, 5, 6, 7, 8])) 24 | print(arrays.array([[1, 2, 3, 4], [5, 6, 7, 8]])) 25 | print(arrays.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])) 26 | print("\"\"\"") 27 | 28 | # geoms 29 | print("""\x1b[92m 30 | triangle = geoms.Triangle((0, 0), (2, 2), (3, 0)) 31 | print(triangle.perimeter()) 32 | print(triangle.area()) 33 | print(triangle.centroid()) 34 | \x1b[m""") 35 | print("\"\"\"") 36 | triangle = geoms.Triangle((0, 0), (2, 2), (3, 0)) 37 | print(triangle.perimeter()) 38 | print(triangle.area()) 39 | print(triangle.centroid()) 40 | print("\"\"\"") 41 | 42 | # hypcmpnms 43 | print("""\x1b[92m 44 | q0 = hypcmpnms.quat(1, 2, 3, 4) 45 | q1 = hypcmpnms.quat(5, 6, 7, 8) 46 | print(q0) 47 | print(q1) 48 | print(q0 + q1) 49 | print(q0 * q1) 50 | print(q0.inverse()) 51 | print(q1.conjugate()) 52 | \x1b[m""") 53 | print("\"\"\"") 54 | q0 = hypcmpnms.quat(1, 2, 3, 4) 55 | q1 = hypcmpnms.quat(5, 6, 7, 8) 56 | print(q0) 57 | print(q1) 58 | print(q0 + q1) 59 | print(q0 * q1) 60 | print(q0.inverse()) 61 | print(q1.conjugate()) 62 | print("\"\"\"") 63 | 64 | # logics 65 | print("""\x1b[92m 66 | a, b, c = 1, 1, 1 67 | adder0, adder1 = logics.HalfAdder("alpha", a, b), logics.HalfAdder("beta", c, None) 68 | xor0 = logics.XOR("alpha") 69 | ff0, ff1 = logics.DFF("alpha"), logics.DFF("beta") 70 | xor0.set_order0(1) 71 | xor0.set_order1(1) 72 | logics.connector(adder0, adder1) 73 | logics.connector(adder0, xor0) 74 | logics.connector(adder1, xor0) 75 | logics.connector(adder1, ff0) 76 | logics.connector(xor0, ff1) 77 | print("sum: {}, carry: {}".format(ff0.out(), ff1.out())) 78 | \x1b[m""") 79 | print("\"\"\"") 80 | a, b, c = 1, 1, 1 81 | adder0, adder1 = logics.HalfAdder("alpha", a, b), logics.HalfAdder("beta", c, None) 82 | xor0 = logics.XOR("alpha") 83 | ff0, ff1 = logics.DFF("alpha"), logics.DFF("beta") 84 | xor0.set_order0(1) 85 | xor0.set_order1(1) 86 | logics.connector(adder0, adder1) 87 | logics.connector(adder0, xor0) 88 | logics.connector(adder1, xor0) 89 | logics.connector(adder1, ff0) 90 | logics.connector(xor0, ff1) 91 | print("sum: {}, carry: {}".format(ff0.out(), ff1.out())) 92 | print("\"\"\"") 93 | 94 | # matrices 95 | print("""\x1b[92m 96 | m0 = matrices.mat([[1, 2], [3, 4]]) 97 | m1 = matrices.mat([[5, 6], [7, 8]]) 98 | print(m0) 99 | print(m1) 100 | print(m0 + m1) 101 | print(m0 @ m1) 102 | print(m0.inv()) 103 | print(m1.rank()) 104 | \x1b[m""") 105 | print("\"\"\"") 106 | m0 = matrices.mat([[1, 2], [3, 4]]) 107 | m1 = matrices.mat([[5, 6], [7, 8]]) 108 | print(m0) 109 | print(m1) 110 | print(m0 + m1) 111 | print(m0 @ m1) 112 | print(m0.inv()) 113 | print(m1.rank()) 114 | print("\"\"\"") 115 | 116 | # multiprec 117 | print("""\x1b[92m 118 | mp_complex1 = multiprec.MPComplex("1.4142135623730950488016887242096980785696718753769", 119 | "2.7182818284590452353602874713527", sigfigs=30) 120 | mp_complex2 = multiprec.MPComplex("1.7320508075688772935274463415059", 121 | "3.141592653589793238462643383279502884197169399375105820974944", sigfigs=40) 122 | modulus = mp_complex1.modulus(sigfigs=25) 123 | print("Modulus of the complex1:", modulus) 124 | sqrt_complex = mp_complex2.sqrt() 125 | print("Square root of the complex2:", sqrt_complex) 126 | power_result = mp_complex1 ** mp_complex2 127 | print("Power of complex1 raised to complex2:", power_result) 128 | euler_gamma = multiprec.mp_euler_gamma(sigfigs=45) 129 | print("Value of Euler's gamma constant:", euler_gamma) 130 | log_2 = multiprec.mp_log(2, 10, sigfigs=50) 131 | print("Logarithm of 2 (base 10):", log_2) 132 | exp_e_squared = multiprec.mp_exp(multiprec.mp_e() ** 2, sigfigs=20) 133 | print("Value of exp(e^2):", exp_e_squared) 134 | \x1b[m""") 135 | print("\"\"\"") 136 | mp_complex1 = multiprec.MPComplex("1.4142135623730950488016887242096980785696718753769", 137 | "2.7182818284590452353602874713527", sigfigs=30) 138 | mp_complex2 = multiprec.MPComplex("1.7320508075688772935274463415059", 139 | "3.141592653589793238462643383279502884197169399375105820974944", sigfigs=40) 140 | modulus = mp_complex1.modulus(sigfigs=25) 141 | print("Modulus of the complex1:", modulus) 142 | sqrt_complex = mp_complex2.sqrt() 143 | print("Square root of the complex2:", sqrt_complex) 144 | power_result = mp_complex1 ** mp_complex2 145 | print("Power of complex1 raised to complex2:", power_result) 146 | euler_gamma = multiprec.mp_euler_gamma(sigfigs=45) 147 | print("Value of Euler's gamma constant:", euler_gamma) 148 | log_2 = multiprec.mp_log(2, 10, sigfigs=50) 149 | print("Logarithm of 2 (base 10):", log_2) 150 | exp_e_squared = multiprec.mp_exp(multiprec.mp_e() ** 2, sigfigs=20) 151 | print("Value of exp(e^2):", exp_e_squared) 152 | print("\"\"\"") 153 | 154 | # special 155 | print("""\x1b[92m 156 | print("Bessel Function of the first kind, order 0 at x=1:", special.besselj0(1)) 157 | print("Modified Bessel function of the first kind, order 1 at x=1:", special.besseli1(1)) 158 | print("Hypergeometric function 0F1 at z=0.5 with b0=1:", special.hyp0f1(1, 0.5)) 159 | print("Hypergeometric function 1F1 at z=1 with a0=1, b0=1:", special.hyp1f1(1, 1, 1)) 160 | print("q-Pochhammer Symbol with a=2+1j, q=0.5+0.1j, n=2+1j:", special.qpochhammer(2 + 1j, 0.5 + 0.1j, 2 + 1j)) 161 | print("q-Gamma Function at n=2 with q=0.5+0.1j:", special.qgamma(2, 0.5 + 0.1j)) 162 | \x1b[m""") 163 | print("\"\"\"") 164 | print("Bessel Function of the first kind, order 0 at x=1:", special.besselj0(1)) 165 | print("Modified Bessel function of the first kind, order 1 at x=1:", special.besseli1(1)) 166 | print("Hypergeometric function 0F1 at z=0.5 with b0=1:", special.hyp0f1(1, 0.5)) 167 | print("Hypergeometric function 1F1 at z=1 with a0=1, b0=1:", special.hyp1f1(1, 1, 1)) 168 | print("q-Pochhammer Symbol with a=2+1j, q=0.5+0.1j, n=2+1j:", special.qpochhammer(2 + 1j, 0.5 + 0.1j, 2 + 1j)) 169 | print("q-Gamma Function at n=2 with q=0.5+0.1j:", special.qgamma(2, 0.5 + 0.1j)) 170 | print("\"\"\"") 171 | 172 | # ciphers 173 | print("""\x1b[92m 174 | string = "PyPyNum" 175 | encrypted = ciphers.caesar(string, 10) 176 | print(string) 177 | print(encrypted) 178 | print(ciphers.caesar(encrypted, 10, decrypt=True)) 179 | encrypted = ciphers.vigenere(string, "ciphers") 180 | print(string) 181 | print(encrypted) 182 | print(ciphers.vigenere(encrypted, "ciphers", decrypt=True)) 183 | encrypted = ciphers.morse(string) 184 | print(string) 185 | print(encrypted) 186 | print(ciphers.morse(encrypted, decrypt=True)) 187 | \x1b[m""") 188 | print("\"\"\"") 189 | string = "PyPyNum" 190 | encrypted = ciphers.caesar(string, 10) 191 | print(string) 192 | print(encrypted) 193 | print(ciphers.caesar(encrypted, 10, decrypt=True)) 194 | encrypted = ciphers.vigenere(string, "ciphers") 195 | print(string) 196 | print(encrypted) 197 | print(ciphers.vigenere(encrypted, "ciphers", decrypt=True)) 198 | encrypted = ciphers.morse(string) 199 | print(string) 200 | print(encrypted) 201 | print(ciphers.morse(encrypted, decrypt=True)) 202 | print("\"\"\"") 203 | 204 | # vectors 205 | print("""\x1b[92m 206 | v0 = vectors.vec([1, 2, 3, 4]) 207 | v1 = vectors.vec([5, 6, 7, 8]) 208 | print(v0) 209 | print(v1) 210 | print(v0 + v1) 211 | print(v0 @ v1) 212 | print(v0.normalize()) 213 | print(v1.angles()) 214 | \x1b[m""") 215 | print("\"\"\"") 216 | v0 = vectors.vec([1, 2, 3, 4]) 217 | v1 = vectors.vec([5, 6, 7, 8]) 218 | print(v0) 219 | print(v1) 220 | print(v0 * v1) 221 | print(v0 @ v1) 222 | print(v0.normalize()) 223 | print(v1.angles()) 224 | print("\"\"\"") 225 | 226 | # consts 227 | print("""\x1b[92m 228 | print(consts.TB) 229 | print(consts.e) 230 | print(consts.h) 231 | print(consts.phi) 232 | print(consts.pi) 233 | print(consts.tera) 234 | \x1b[m""") 235 | print("\"\"\"") 236 | print(consts.TB) 237 | print(consts.e) 238 | print(consts.h) 239 | print(consts.phi) 240 | print(consts.pi) 241 | print(consts.tera) 242 | print("\"\"\"") 243 | 244 | # equations 245 | print("""\x1b[92m 246 | p = [1, -2, -3, 4] 247 | m = [ 248 | [ 249 | [1, 2, 3], 250 | [6, 10, 12], 251 | [7, 16, 9] 252 | ], 253 | [-1, -2, -3] 254 | ] 255 | print(equations.poly_eq(p)) 256 | print(equations.lin_eq(*m)) 257 | \x1b[m""") 258 | print("\"\"\"") 259 | p = [1, -2, -3, 4] 260 | m = [ 261 | [ 262 | [1, 2, 3], 263 | [6, 10, 12], 264 | [7, 16, 9] 265 | ], 266 | [-1, -2, -3] 267 | ] 268 | print(equations.poly_eq(p)) 269 | print(equations.lin_eq(*m)) 270 | print("\"\"\"") 271 | 272 | # maths 273 | print("""\x1b[92m 274 | print(maths.cot(consts.pi / 3)) 275 | print(maths.gamma(1.5)) 276 | print(maths.pi(1, 10, lambda x: x ** 2)) 277 | print(maths.product([2, 3, 5, 7, 11, 13, 17, 19, 23, 29])) 278 | print(maths.sigma(1, 10, lambda x: x ** 2)) 279 | print(maths.var([2, 3, 5, 7, 11, 13, 17, 19, 23, 29])) 280 | \x1b[m""") 281 | print("\"\"\"") 282 | print(maths.cot(consts.pi / 3)) 283 | print(maths.gamma(1.5)) 284 | print(maths.pi(1, 10, lambda x: x ** 2)) 285 | print(maths.product([2, 3, 5, 7, 11, 13, 17, 19, 23, 29])) 286 | print(maths.sigma(1, 10, lambda x: x ** 2)) 287 | print(maths.var([2, 3, 5, 7, 11, 13, 17, 19, 23, 29])) 288 | print("\"\"\"") 289 | 290 | # plotting 291 | print("""\x1b[92m 292 | plt = plotting.unary(lambda x: x ** 2, top=10, bottom=0, character="+") 293 | print(plt) 294 | print(plotting.binary(lambda x, y: x ** 2 + y ** 2 - 10, right=10, left=0, compare="<=", basic=plotting.change(plt))) 295 | print(plotting.c_unary(lambda x: x ** x, right=2, left=-2, top=2, bottom=-2, complexity=20, character="-")) 296 | \x1b[m""") 297 | print("\"\"\"") 298 | plt = plotting.unary(lambda x: x ** 2, top=10, bottom=0, character="+") 299 | print(plt) 300 | print(plotting.binary(lambda x, y: x ** 2 + y ** 2 - 10, right=10, left=0, compare="<=", basic=plotting.change(plt))) 301 | print(plotting.c_unary(lambda x: x ** x, right=2, left=-2, top=2, bottom=-2, complexity=20, character="-")) 302 | print("\"\"\"") 303 | 304 | # random 305 | print("""\x1b[92m 306 | print(random.gauss(0, 1, [2, 3, 4])) 307 | print(random.rand([2, 3, 4])) 308 | print(random.randint(0, 9, [2, 3, 4])) 309 | print(random.uniform(0, 9, [2, 3, 4])) 310 | \x1b[m""") 311 | print("\"\"\"") 312 | print(random.gauss(0, 1, [2, 3, 4])) 313 | print(random.rand([2, 3, 4])) 314 | print(random.randint(0, 9, [2, 3, 4])) 315 | print(random.uniform(0, 9, [2, 3, 4])) 316 | print("\"\"\"") 317 | 318 | # regs 319 | print("""\x1b[92m 320 | print(regs.lin_reg(list(range(5)), [2, 4, 6, 7, 8])) 321 | print(regs.par_reg(list(range(5)), [2, 4, 6, 7, 8])) 322 | print(regs.poly_reg(list(range(5)), [2, 4, 6, 7, 8], 4)) 323 | \x1b[m""") 324 | print("\"\"\"") 325 | print(regs.lin_reg(list(range(5)), [2, 4, 6, 7, 8])) 326 | print(regs.par_reg(list(range(5)), [2, 4, 6, 7, 8])) 327 | print(regs.poly_reg(list(range(5)), [2, 4, 6, 7, 8], 4)) 328 | print("\"\"\"") 329 | 330 | # tools 331 | print("""\x1b[92m 332 | print(tools.classify([1, 2.3, 4 + 5j, "string", list, True, 3.14, False, tuple, tools])) 333 | print(tools.dedup(["Python", 6, "NumPy", int, "PyPyNum", 9, "pypynum", "NumPy", 6, True])) 334 | print(tools.frange(0, 3, 0.4)) 335 | print(tools.linspace(0, 2.8, 8)) 336 | \x1b[m""") 337 | print("\"\"\"") 338 | print(tools.classify([1, 2.3, 4 + 5j, "string", list, True, 3.14, False, tuple, tools])) 339 | print(tools.dedup(["Python", 6, "NumPy", int, "PyPyNum", 9, "pypynum", "NumPy", 6, True])) 340 | print(tools.frange(0, 3, 0.4)) 341 | print(tools.linspace(0, 2.8, 8)) 342 | print("\"\"\"") 343 | 344 | for _ in tuple(globals()): 345 | if _[0] != "_": 346 | del globals()[_] 347 | del _ 348 | 349 | print("""\x1b[91m 350 | # Tip: 351 | # The test has been successfully passed and ended. 352 | # These tests are only part of the functionality of this package. 353 | # More features need to be explored and tried by yourself! 354 | \x1b[m""") 355 | -------------------------------------------------------------------------------- /pypynum/this.py: -------------------------------------------------------------------------------- 1 | """ 2 | The Zen of PyPyNum 3 | """ 4 | 5 | Zen = ("\x9a¤¢\xa0¢°¼¥°âõòýõäàõð°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°\x9a\x9a" 6 | "±äù°õóñâòýõ°ã·äõÜ\x9a¾ãõùâÿõøä°üñóùäñýõøäñý°öÿ°þÿùäñêùüñäþõýäâñàýÿó°õøä°ÿä°þùûñ°ãù°þ÷ùãõô°þù°éäùâñüåôÿÝ\x9a" 7 | "¾ýõâÿõøä°ôõøãùüòñäãõ½üüõç°ñ°öÿ°øäåâä°õøä°øäùç°ãõäñþÿãõâ°äù°¼âñõüó°ãù°þÿùäåüÿã°ñ°öÙ\x9a" 8 | "¾öÿÿâà°üñóùäñýõøäñý°ñ°öÿ°âÿ÷ùâ°õøä°øäùç°äù°âÿäóñöõâ°¼õæùãåüõ°ãù°þÿùäåüÿã°ñ°öÙ\x9a" 9 | "¾öÿÿâà°äåÿ½äø÷åÿøä½üüõç°ñ°ãñ°¼ýõüòÿâà°ñ°öÿ°øäàõô°õøä°âõæÿóþå°þñó°õóþõùäñà°¼äõÉ\x9a" 10 | "¾õæùãùóõô°ôþñ°äöùçã°«öÿÿâà°äóõâùô°õøä°ÿä°þùûñ°ãù°÷þùæüÿã°þù°ããõþäàýÿâÀ\x9a" 11 | "¾äþõýõüõ°éäùäþõôù°õøä°ãñ°õåáùþå°ãñ°¼þÿùäåüÿã°äþñ÷õüõ°¼õü÷þùã°ñ°õò°ôüåÿøã°õâõøÄ\x9a" 12 | "¾ýõâÿõøä°ôõþùöõô½üüõç°ñ°öÿ°éäùâñüó°õøä°ûõõã°õç°¼éäùå÷ùòýñ°öÿ°õóñö°õøä°þÙ\x9a" 13 | "¾óù÷õäñâäã°ôþñ°üñþÿùäþõäþù°ãù°¼ôÿùâõà°ôõóñüà½üüõç°ñ°õûùü°¼õóþõüùÃ\x9a" 14 | "¾ôõüñõóþÿó°äÿþ°¼ôõãÿàèõ°õò°äãåý°¼öÿÿâà°ñ°þù°þÿùäóùôñâäþÿó°ñ°õûùü°¼ãâÿââÕ\x9a" 15 | "¾éäùâåà°üñóùäõâÿõøä°ãõôõãâõàåã°þõäöÿ°¼ýõâÿõøä°õüòñóùüààñ°þñ°õûùü°¼éäùüñóùäóñâÀ\x9a" 16 | "¾õüåâ°üñâõþõ÷°õøä°õäñóãåöòÿ°äÿþ°äãåý°¼öÿÿâà°ñ°þù°ãþÿùäàõóèõ°õûùü°¼ãõãñó°üñùóõàÃ\x9a" 17 | "¾õüòñãþõàãùôþù°ãù°¼ýõâÿõøä°þõæÿâà½üüõç°ñ°õûùü°¼éäùüùòñôñõÂ\x9a" 18 | "¾éóþñôþåôõâ°äåÿøäùç°õóþõããõ°ãþùñäõâ°¼üñùýÿþéüÿà°üñýùþùý°ñ°õûùü°¼õôÿó°õãâñàÃ\x9a" 19 | "¾ãþÿùäóþåö°öÿ°õâåäñþ°âñõþùü°õøä°âÿââùý°õôÿó°âåÿ°þù°ãõùøóâñâõùø°äñüÖ\x9a" 20 | "¾þÿùäñóùäãùøàÿã°öÿ°õüóñþþùà°õøä°ãù°ýâÿö°þù°éäùóùüàýùÃ\x9a" 21 | "¾ãýõüòÿâà°äãõûâñô°õøä°ãõäñþùýåüüù°óù÷ÿü°þù°éäùâñüÓ\x9a" 22 | "¾ãýøäùâÿ÷üñ°éûüåò°õøä°ãôþõóãþñâä°ãóùäñýõøäñý°þù°õóþñ÷õüÕ\x9a¾þÿùäñýùèÿâààñ°ãø÷ùõçäåÿ°þÿùãùóõâÀ\x9a\x9a" 23 | "¾ãôñõâøä°þÿøäéÀ°õâåà°øäùç°ãýøäùâÿ÷üñ°âåÿ°õæñõç°õç°¼éâñåäóþñã°üñóùäñýõøäñý°ãùøä°þÙ\x9a\x9a" 24 | "ùéñùÚ°þõøÃ°éò°¼ýåÞéÀéÀ°öÿ°þõʰõøÄ°°°°°°°°°°°°°°°°\x9a") 25 | print("".join([chr(ord(_) ^ 144) for _ in Zen[::-1]])) 26 | for _ in tuple(globals()): 27 | if _[0] != "_": 28 | del globals()[_] 29 | del _ 30 | -------------------------------------------------------------------------------- /pypynum/types.py: -------------------------------------------------------------------------------- 1 | """ 2 | Special types collection 3 | """ 4 | 5 | from decimal import Decimal 6 | from typing import * 7 | 8 | arr = Union[list, tuple] 9 | ite = Union[list, tuple, str] 10 | num = Union[int, float, complex] 11 | prec = Union[int, float, str, Decimal] 12 | real = Union[int, float] 13 | 14 | 15 | class ArgsError(ValueError): 16 | pass 17 | 18 | 19 | class ContentError(ValueError): 20 | pass 21 | 22 | 23 | class LogicError(ValueError): 24 | pass 25 | 26 | 27 | class RandomError(ValueError): 28 | pass 29 | 30 | 31 | class ShapeError(ValueError): 32 | pass 33 | 34 | 35 | class Config: 36 | """ 37 | Introduction 38 | ========== 39 | The Config class is designed to manage and configure application settings. It provides a mechanism to ensure 40 | consistency in configurations and prevents accidental changes by limiting attribute modification. 41 | 42 | Roadmap 43 | ========== 44 | The Config class is currently stable. Future updates may include new configuration options 45 | to support additional features. 46 | 47 | Usage 48 | ========== 49 | 50 | Modifying Configuration: 51 | ---------- 52 | You can modify the configuration by setting attributes directly, but only with predefined boolean values. 53 | 54 | - config.use_latex = True # Enable LaTeX formatting 55 | 56 | Reading Configuration: 57 | ---------- 58 | You can read the current configuration by accessing the attributes. 59 | 60 | - print(config.use_unicode) # Output: False 61 | 62 | Ensuring Exclusive Configuration: 63 | ---------- 64 | The Config class is designed to be mutually exclusive, meaning only one configuration item can be True at a time. 65 | 66 | - config.use_std = True # This will automatically set other configuration items to False 67 | 68 | Getting Configuration Information: 69 | ---------- 70 | Use the `__repr__` method to get a string representation of the current configuration. 71 | 72 | - print(config) # Output: Config(use_latex=True, use_std=False, use_unicode=False) 73 | """ 74 | use_latex = False 75 | use_unicode = False 76 | use_std = True 77 | attributes = ("attributes",) 78 | 79 | def __init__(self): 80 | Config.attributes = tuple(attr for attr in dir(Config) if attr[0] != "_" and attr != "attributes") 81 | 82 | def __setattr__(self, name, value): 83 | if name in self.attributes: 84 | value = bool(value) 85 | if value is True: 86 | for attr in self.attributes: 87 | if attr != name and getattr(self, attr, False) is True: 88 | super().__setattr__(attr, False) 89 | super().__setattr__(name, value) 90 | else: 91 | raise AttributeError("Attribute '{}' is read-only and cannot be added".format(name)) 92 | 93 | def __delattr__(self, name): 94 | raise AttributeError("Attribute '{}' cannot be deleted".format(name)) 95 | 96 | def __init_subclass__(cls, **kwargs): 97 | raise NotImplementedError("Config cannot be subclassed") 98 | 99 | def __copy__(self): 100 | raise TypeError("Config cannot be copied") 101 | 102 | def __deepcopy__(self, memo): 103 | raise TypeError("Config cannot be copied") 104 | 105 | def __getattribute__(self, name): 106 | if name == "__class__": 107 | raise AttributeError("Access to '__class__' is forbidden") 108 | return super().__getattribute__(name) 109 | 110 | def __repr__(self): 111 | return "Config({})".format(", ".join("{}={}".format(attr, getattr(self, attr)) for attr in self.attributes)) 112 | 113 | 114 | config = Config() 115 | del Config 116 | -------------------------------------------------------------------------------- /pypynum/ufuncs.py: -------------------------------------------------------------------------------- 1 | from .arrays import Array as __Array 2 | 3 | 4 | def apply(a, func, rtype=None): 5 | def inner(data): 6 | return [inner(item) for item in data] if isinstance(data, list) else func(data) 7 | 8 | if isinstance(a, __Array): 9 | if callable(func): 10 | if rtype is None: 11 | rtype = type(a) 12 | return rtype(inner(a.data)) 13 | else: 14 | raise TypeError("The 'func' argument must be callable, got {} instead".format(type(func))) 15 | else: 16 | raise TypeError("The 'a' argument must be an instance of Array, got {} instead".format(type(a))) 17 | 18 | 19 | def base_ufunc(*arrays, func, args=(), rtype=None): 20 | _type = str(type(func)) 21 | if not all([isinstance(item, __Array) for item in arrays]) or not ( 22 | _type.startswith(" b) 122 | 123 | 124 | def ge(x, y): 125 | return ufunc_helper(x, y, lambda a, b: a >= b) 126 | 127 | 128 | def eq(x, y): 129 | return ufunc_helper(x, y, lambda a, b: a == b) 130 | 131 | 132 | def le(x, y): 133 | return ufunc_helper(x, y, lambda a, b: a <= b) 134 | 135 | 136 | def lt(x, y): 137 | return ufunc_helper(x, y, lambda a, b: a < b) 138 | 139 | 140 | def ne(x, y): 141 | return ufunc_helper(x, y, lambda a, b: a != b) 142 | -------------------------------------------------------------------------------- /pypynum/vectors.py: -------------------------------------------------------------------------------- 1 | from .arrays import Array 2 | from .types import ShapeError 3 | 4 | MatchError = ShapeError("The shapes of two vectors do not match") 5 | 6 | 7 | class Vector(Array): 8 | """ 9 | It is a vector class that supports basic operations, 10 | as well as functions such as norm and all angles. 11 | :param data: An array in the form of a list 12 | :param check: Check the rationality of the input array 13 | """ 14 | 15 | def __init__(self, data, check=True): 16 | super().__init__(data, check) 17 | if len(self.shape) != 1: 18 | raise ShapeError("Vectors can only be one-dimensional in shape") 19 | self.len = len(data) 20 | 21 | def __matmul__(self, other): 22 | if self.len != other.len: 23 | raise MatchError 24 | return sum([x * y for x, y in zip(self.data, other.data)]) 25 | 26 | def __abs__(self): 27 | return self.norm() 28 | 29 | def __pos__(self): 30 | return Vector([x for x in self.data]) 31 | 32 | def __neg__(self): 33 | return Vector([-x for x in self.data]) 34 | 35 | def norm(self, p=2): 36 | if p == 0: 37 | return len([x for x in self.data if x != 0]) 38 | elif p == 1: 39 | return sum(map(abs, self.data)) 40 | elif p == 2: 41 | return sum([x ** 2 for x in self.data]) ** 0.5 42 | elif p == float("inf"): 43 | return max(map(abs, self.data)) 44 | else: 45 | return sum([x ** p for x in self.data]) ** (1 / p) 46 | 47 | def normalize(self): 48 | norm = self.norm() 49 | return Vector([x / norm for x in self.data]) 50 | 51 | def cosine(self, other): 52 | if self.len != other.len: 53 | raise MatchError 54 | dot_product = self @ other 55 | norm_self = self.norm() 56 | norm_other = other.norm() 57 | return dot_product / (norm_self * norm_other) 58 | 59 | def angles(self, axes=None): 60 | from math import acos 61 | identity = [] 62 | for i in range(self.len): 63 | unit_vector = [0] * self.len 64 | unit_vector[i] = 1 65 | identity.append(Vector(unit_vector)) 66 | if axes is None or axes == [] or axes == (): 67 | result = [acos(self @ axis / (self.norm() * axis.norm())) for axis in identity] 68 | elif isinstance(axes, int) and axes >= 0: 69 | axis = identity[axes] 70 | result = acos(self @ axis / (self.norm() * axis.norm())) 71 | elif isinstance(axes, (list, tuple)): 72 | result = [acos(self @ identity[a] / (self.norm() * identity[a].norm())) for a in axes] 73 | else: 74 | raise TypeError("The axes parameter can only be None, non-negative integer, list, or tuple") 75 | return result 76 | 77 | def chebyshev(self, other): 78 | if self.len != other.len: 79 | raise MatchError 80 | return max([abs(x - y) for x, y in zip(self.data, other.data)]) 81 | 82 | def manhattan(self, other): 83 | if self.len != other.len: 84 | raise MatchError 85 | return sum([abs(x - y) for x, y in zip(self.data, other.data)]) 86 | 87 | def euclidean(self, other): 88 | if self.len != other.len: 89 | raise MatchError 90 | return sum([(x - y) ** 2 for x, y in zip(self.data, other.data)]) ** 0.5 91 | 92 | def minkowski(self, other, p): 93 | if self.len != other.len: 94 | raise MatchError 95 | return sum([abs(x - y) ** p for x, y in zip(self.data, other.data)]) ** (1 / p) 96 | 97 | 98 | def vec(data): 99 | return Vector(data) 100 | 101 | 102 | del Array 103 | -------------------------------------------------------------------------------- /pypynum/zh_cn.py: -------------------------------------------------------------------------------- 1 | from pypynum.arrays import * 2 | from pypynum.chars import int2superscript, superscript2int, int2subscript, subscript2int 3 | from pypynum.ciphers import * 4 | from pypynum.equations import * 5 | from pypynum.files import * 6 | from pypynum.fft import * 7 | from pypynum.maths import * 8 | from pypynum.multiprec import * 9 | from pypynum.numbers import * 10 | from pypynum.special import * 11 | from pypynum.types import Any, Callable, Dict, Iterator, List, Tuple 12 | 13 | 14 | def 数组(数据: list = None, 检查: bool = True) -> Array: 15 | return Array(数据, 检查) 16 | 17 | 18 | def 转换为列表(数据) -> list: 19 | return aslist(数据) 20 | 21 | 22 | def 转换为数组(数据) -> Array: 23 | return asarray(数据) 24 | 25 | 26 | def 全部填充(形状, 填充值, 返回类型=Array): 27 | return full(形状, 填充值, 返回类型) 28 | 29 | 30 | def 类似形状填充(数组A, 填充值, 返回类型=Array): 31 | return full_like(数组A, 填充值, 返回类型) 32 | 33 | 34 | def 全零(形状, 返回类型=Array): 35 | return full(形状, 0, 返回类型) 36 | 37 | 38 | def 类似形状全零(数组A, 返回类型=Array): 39 | return full_like(数组A, 0, 返回类型) 40 | 41 | 42 | def 全一(形状, 返回类型=Array): 43 | return full(形状, 1, 返回类型) 44 | 45 | 46 | def 类似形状全一(数组A, 返回类型=Array): 47 | return full_like(数组A, 1, 返回类型) 48 | 49 | 50 | def 填充序列(形状, 序列=None, 重复=True, 填充=0, 返回类型=Array): 51 | return fill(形状, 序列, 重复, 填充, 返回类型) 52 | 53 | 54 | def 整数转上标(标准字符串: str) -> str: 55 | return int2superscript(标准字符串) 56 | 57 | 58 | def 上标转整数(上标字符串: str) -> str: 59 | return superscript2int(上标字符串) 60 | 61 | 62 | def 整数转下标(标准字符串: str) -> str: 63 | return int2subscript(标准字符串) 64 | 65 | 66 | def 下标转整数(下标字符串: str) -> str: 67 | return subscript2int(下标字符串) 68 | 69 | 70 | def base64密码(文本: str, 解密: bool = False) -> str: 71 | return base_64(文本, 解密) 72 | 73 | 74 | def 阿特巴什密码(文本: str) -> str: 75 | return atbash(文本) 76 | 77 | 78 | def ROT13密码(文本: str) -> str: 79 | return rot13(文本) 80 | 81 | 82 | def 凯撒密码(文本: str, 移位: int, 解密: bool = False) -> str: 83 | return caesar(文本, 移位, 解密) 84 | 85 | 86 | def 维吉尼亚密码(文本: str, 密钥: str, 解密: bool = False) -> str: 87 | return vigenere(文本, 密钥, 解密) 88 | 89 | 90 | def 代替密码(文本: str, 替换映射: dict, 解密: bool = False) -> str: 91 | return substitution(文本, 替换映射, 解密) 92 | 93 | 94 | def 莫尔斯密码(文本: str, 解密: bool = False) -> str: 95 | return morse(文本, 解密) 96 | 97 | 98 | def 普莱费尔密码(文本: str, 密钥: str, 解密: bool = False) -> str: 99 | return playfair(文本, 密钥, 解密) 100 | 101 | 102 | def 希尔256密码(文本: bytes, 密钥: list, 解密: bool = False) -> bytes: 103 | return hill256(文本, 密钥, 解密) 104 | 105 | 106 | def RC4初始化密钥调度算法(密钥: bytes) -> list: 107 | return ksa(密钥) 108 | 109 | 110 | def RC4伪随机生成算法(密钥序列: list): 111 | return prga(密钥序列) 112 | 113 | 114 | def RC4密码(文本: bytes, 密钥: bytes) -> bytes: 115 | return rc4(文本, 密钥) 116 | 117 | 118 | def 线性方程组(左边: list, 右边: list) -> list: 119 | return lin_eq(左边, 右边) 120 | 121 | 122 | def 多项式方程(系数: list) -> list: 123 | return poly_eq(系数) 124 | 125 | 126 | def 读取(文件: str) -> list: 127 | return read(文件) 128 | 129 | 130 | def 写入(文件: str, *对象: object): 131 | return write(文件, *对象) 132 | 133 | 134 | def 一维傅里叶变换(*数据) -> FT1D: 135 | return FT1D(*数据) 136 | 137 | 138 | def Fraction转为Decimal(分数对象: Fraction, 有效位数: int) -> Decimal: 139 | return frac2dec(分数对象, 有效位数) 140 | 141 | 142 | def 多精度自然常数(有效位数: int, 方法: str = "series") -> Decimal: 143 | return mp_e(有效位数, 方法) 144 | 145 | 146 | def 多精度圆周率(有效位数: int, 方法: str = "chudnovsky") -> Decimal: 147 | return mp_pi(有效位数, 方法) 148 | 149 | 150 | def 多精度黄金分割率(有效位数: int, 方法: str = "algebraic") -> Decimal: 151 | return mp_phi(有效位数, 方法) 152 | 153 | 154 | def 多精度正弦(x: real, 有效位数: int) -> Decimal: 155 | return mp_sin(x, 有效位数) 156 | 157 | 158 | def 多精度余弦(x: real, 有效位数: int) -> Decimal: 159 | return mp_cos(x, 有效位数) 160 | 161 | 162 | def 多精度自然对数(真数: real, 有效位数: int, 使用内置方法: bool = True) -> Decimal: 163 | return mp_ln(真数, 有效位数, 使用内置方法) 164 | 165 | 166 | def 多精度对数(真数: real, 底数: real, 有效位数: int, 使用内置方法: bool = True) -> Decimal: 167 | return mp_log(真数, 底数, 有效位数, 使用内置方法) 168 | 169 | 170 | def 多精度反正切(x: real, 有效位数: int) -> Decimal: 171 | return mp_atan(x, 有效位数) 172 | 173 | 174 | def 多精度方位角(y: real, x: real, 有效位数: int) -> Decimal: 175 | return mp_atan2(y, x, 有效位数) 176 | 177 | 178 | def 多精度自然指数(指数: real, 有效位数: int, 使用内置方法: bool = True) -> Decimal: 179 | return mp_exp(指数, 有效位数, 使用内置方法) 180 | 181 | 182 | def 多精度双曲正弦(x: real, 有效位数: int) -> Decimal: 183 | return mp_sinh(x, 有效位数) 184 | 185 | 186 | def 多精度双曲余弦(x: real, 有效位数: int) -> Decimal: 187 | return mp_cosh(x, 有效位数) 188 | 189 | 190 | def 多精度反正弦(x: real, 有效位数: int) -> Decimal: 191 | return mp_asin(x, 有效位数) 192 | 193 | 194 | def 多精度反余弦(x: real, 有效位数: int) -> Decimal: 195 | return mp_acos(x, 有效位数) 196 | 197 | 198 | def 多精度菲涅耳正弦积分(x: real, 有效位数: int) -> Decimal: 199 | return mp_fresnel_s(x, 有效位数) 200 | 201 | 202 | def 多精度菲涅耳余弦积分(x: real, 有效位数: int) -> Decimal: 203 | return mp_fresnel_c(x, 有效位数) 204 | 205 | 206 | def 多精度欧拉伽马(有效位数: int) -> Decimal: 207 | return mp_euler_gamma(有效位数) 208 | 209 | 210 | def 多精度复数(实部: prec, 虚部: prec, 有效位数: int = 28) -> MPComplex: 211 | return MPComplex(实部, 虚部, 有效位数) 212 | 213 | 214 | def 转为多精度复数(实部: Union[int, float, str, Decimal, complex, MPComplex], 虚部: prec, 215 | 有效位数: int = 28) -> MPComplex: 216 | return asmpc(实部, 虚部, 有效位数) 217 | 218 | 219 | def 下伽玛(s: num, x: num) -> num: 220 | return lowergamma(s, x) 221 | 222 | 223 | def 上伽玛(s: num, x: num) -> num: 224 | return uppergamma(s, x) 225 | 226 | 227 | def 贝塞尔函数J0(x: num) -> num: 228 | return besselj0(x) 229 | 230 | 231 | def 贝塞尔函数J1(x: num) -> num: 232 | return besselj1(x) 233 | 234 | 235 | def 贝塞尔函数Jv(v: real, x: num) -> num: 236 | return besseljv(v, x) 237 | 238 | 239 | def 贝塞尔函数I0(x: num) -> num: 240 | return besseli0(x) 241 | 242 | 243 | def 贝塞尔函数I1(x: num) -> num: 244 | return besseli1(x) 245 | 246 | 247 | def 贝塞尔函数Iv(v: real, x: num) -> num: 248 | return besseliv(v, x) 249 | 250 | 251 | def 乘积和(*多个数组: List[Any]) -> float: 252 | return sumprod(*多个数组) 253 | 254 | 255 | def x对数y乘积(x: float, y: float) -> float: 256 | return xlogy(x, y) 257 | 258 | 259 | def 序列滚动(序列: Iterator[Any], 偏移: int) -> Iterator[Any]: 260 | return roll(序列, 偏移) 261 | 262 | 263 | def y次方根(被开方数: num, 开方数: num) -> num: 264 | return root(被开方数, 开方数) 265 | 266 | 267 | def 自然指数(指数: real) -> real: 268 | return exp(指数) 269 | 270 | 271 | def 自然对数(真数: real) -> real: 272 | return ln(真数) 273 | 274 | 275 | def 最大公约数(*args: int) -> int: 276 | return gcd(*args) 277 | 278 | 279 | def 最小公倍数(*args: int) -> int: 280 | return lcm(*args) 281 | 282 | 283 | def 正弦(x: real) -> real: 284 | return sin(x) 285 | 286 | 287 | def 余弦(x: real) -> real: 288 | return cos(x) 289 | 290 | 291 | def 正切(x: real) -> real: 292 | return tan(x) 293 | 294 | 295 | def 余割(x: real) -> real: 296 | return csc(x) 297 | 298 | 299 | def 正割(x: real) -> real: 300 | return sec(x) 301 | 302 | 303 | def 余切(x: real) -> real: 304 | return cot(x) 305 | 306 | 307 | def 反正弦(x: real) -> real: 308 | return asin(x) 309 | 310 | 311 | def 反余弦(x: real) -> real: 312 | return acos(x) 313 | 314 | 315 | def 反正切(x: real) -> real: 316 | return atan(x) 317 | 318 | 319 | def 反余割(x: real) -> real: 320 | return acsc(x) 321 | 322 | 323 | def 反正割(x: real) -> real: 324 | return asec(x) 325 | 326 | 327 | def 反余切(x: real) -> real: 328 | return acot(x) 329 | 330 | 331 | def 双曲正弦(x: real) -> real: 332 | return sinh(x) 333 | 334 | 335 | def 双曲余弦(x: real) -> real: 336 | return cosh(x) 337 | 338 | 339 | def 双曲正切(x: real) -> real: 340 | return tanh(x) 341 | 342 | 343 | def 双曲余割(x: real) -> real: 344 | return csch(x) 345 | 346 | 347 | def 双曲正割(x: real) -> real: 348 | return sech(x) 349 | 350 | 351 | def 双曲余切(x: real) -> real: 352 | return coth(x) 353 | 354 | 355 | def 反双曲正弦(x: real) -> real: 356 | return asinh(x) 357 | 358 | 359 | def 反双曲余弦(x: real) -> real: 360 | return acosh(x) 361 | 362 | 363 | def 反双曲正切(x: real) -> real: 364 | return atanh(x) 365 | 366 | 367 | def 反双曲余割(x: real) -> real: 368 | return acsch(x) 369 | 370 | 371 | def 反双曲正割(x: real) -> real: 372 | return asech(x) 373 | 374 | 375 | def 反双曲余切(x: real) -> real: 376 | return acoth(x) 377 | 378 | 379 | def 极差(数据: List[float]) -> float: 380 | return ptp(数据) 381 | 382 | 383 | def 中位数(数据: List[float]) -> float: 384 | return median(数据) 385 | 386 | 387 | def 频率统计(数据: List[Any]) -> Dict[Any, int]: 388 | return freq(数据) 389 | 390 | 391 | def 众数(数据: List[Any]): 392 | return mode(数据) 393 | 394 | 395 | def 平均数(数据: List[float]) -> float: 396 | return mean(数据) 397 | 398 | 399 | def 几何平均数(数据: List[float]) -> float: 400 | return geom_mean(数据) 401 | 402 | 403 | def 平方平均数(数据: List[float]) -> float: 404 | return square_mean(数据) 405 | 406 | 407 | def 调和平均数(数据: List[float]) -> float: 408 | return harm_mean(数据) 409 | 410 | 411 | def 原点矩(数据: List[float], 阶数: int) -> float: 412 | return raw_moment(数据, 阶数) 413 | 414 | 415 | def 中心矩(数据: List[float], 阶数: int) -> float: 416 | return central_moment(数据, 阶数) 417 | 418 | 419 | def 方差(数据: List[float], 自由度: int = 0) -> float: 420 | return var(数据, 自由度) 421 | 422 | 423 | def 偏度(数据: List[float]) -> float: 424 | return skew(数据) 425 | 426 | 427 | def 峰度(数据: List[float], 费希尔: bool = True) -> float: 428 | return kurt(数据, 费希尔) 429 | 430 | 431 | def 标准差(数据: List[float], 自由度: int = 0) -> float: 432 | return std(数据, 自由度) 433 | 434 | 435 | def 协方差(x: List[float], y: List[float], 自由度: int = 0) -> float: 436 | return cov(x, y, 自由度) 437 | 438 | 439 | def 相关系数(x: List[float], y: List[float]) -> float: 440 | return corr_coeff(x, y) 441 | 442 | 443 | def 判定系数(x: List[float], y: List[float]) -> float: 444 | return coeff_det(x, y) 445 | 446 | 447 | def 分位数(数据: list, 分位值: float, 插值方法: str = "linear", 已排序: bool = False) -> float: 448 | return quantile(数据, 分位值, 插值方法, 已排序) 449 | 450 | 451 | def 积累乘积(数据: List[float]) -> float: 452 | return product(数据) 453 | 454 | 455 | def 连续加和(下界: int, 上界: int, 函数: Callable) -> float: 456 | return sigma(下界, 上界, 函数) 457 | 458 | 459 | def 连续乘积(下界: int, 上界: int, 函数: Callable) -> float: 460 | return pi(下界, 上界, 函数) 461 | 462 | 463 | def 导数(函数, 参数: float, 步长: float = 1e-6, *额外参数, **额外关键字参数) -> float: 464 | return deriv(函数, 参数, 步长, *额外参数, **额外关键字参数) 465 | 466 | 467 | def 积分(函数, 积分开始: float, 积分结束: float, 积分点数: int = 1000000, *额外参数, **额外关键字参数) -> float: 468 | return integ(函数, 积分开始, 积分结束, 积分点数, *额外参数, **额外关键字参数) 469 | 470 | 471 | def 贝塔函数(p: float, q: float) -> float: 472 | return beta(p, q) 473 | 474 | 475 | def 伽玛函数(alpha: float) -> float: 476 | return gamma(alpha) 477 | 478 | 479 | def 阶乘函数(n: int) -> int: 480 | return factorial(n) 481 | 482 | 483 | def 排列数(总数: int, 选取数: int) -> int: 484 | return arrangement(总数, 选取数) 485 | 486 | 487 | def 组合数(总数: int, 选取数: int) -> int: 488 | return combination(总数, 选取数) 489 | 490 | 491 | def 黎曼函数(alpha: float) -> float: 492 | return zeta(alpha) 493 | 494 | 495 | def 误差函数(x: real) -> real: 496 | return erf(x) 497 | 498 | 499 | def S型函数(x: real) -> real: 500 | return sigmoid(x) 501 | 502 | 503 | def 符号函数(x: num) -> num: 504 | return sign(x) 505 | 506 | 507 | def 负一整数次幂(指数: int) -> int: 508 | return parity(指数) 509 | 510 | 511 | def 累加和(序列: List[float]) -> List[float]: 512 | return cumsum(序列) 513 | 514 | 515 | def 累乘积(序列: List[float]) -> List[float]: 516 | return cumprod(序列) 517 | 518 | 519 | def 多次方根取整(被开方数: int, 开方数: int) -> int: 520 | return iroot(被开方数, 开方数) 521 | 522 | 523 | def 欧拉函数(n: int) -> int: 524 | return totient(n) 525 | 526 | 527 | def 模运算阶(a: int, n: int, b: int) -> int: 528 | return mod_order(a, n, b) 529 | 530 | 531 | def 原根(a: int, 单个: bool = False) -> Union[int, List[int]]: 532 | return primitive_root(a, 单个) 533 | 534 | 535 | def 归一化(数据: List[float], 目标: float = 1) -> List[float]: 536 | return normalize(数据, 目标) 537 | 538 | 539 | def 加权平均(数据: List[float], 权重: List[float]) -> float: 540 | return average(数据, 权重) 541 | 542 | 543 | def 扩展欧几里得算法(a: int, b: int) -> Tuple[int, int, int]: 544 | return exgcd(a, b) 545 | 546 | 547 | def 中国剩余定理(n: List[int], a: List[int]) -> int: 548 | return crt(n, a) 549 | 550 | 551 | def 平方根取整(被开方数: int) -> int: 552 | return isqrt(被开方数) 553 | 554 | 555 | def 可能是平方数(n: int) -> bool: 556 | return is_possibly_square(n) 557 | 558 | 559 | def 判断平方数(n: int) -> bool: 560 | return is_square(n) 561 | 562 | 563 | def 整数转单词(整数: int) -> str: 564 | return int2words(整数) 565 | 566 | 567 | def 字符串转整数(字符串: str) -> int: 568 | return str2int(字符串) 569 | 570 | 571 | def 整数转罗马数(整数: int, 上划线: bool = True) -> str: 572 | return int2roman(整数, 上划线) 573 | 574 | 575 | def 罗马数转整数(罗马数: str) -> int: 576 | return roman2int(罗马数) 577 | 578 | 579 | def 浮点数转分数(数值: float, 是否带分数: bool = False, 误差: float = 1e-15) -> tuple: 580 | return float2fraction(数值, 是否带分数, 误差) 581 | 582 | 583 | def 拆分浮点数字符串(字符串: str) -> tuple: 584 | return split_float(字符串) 585 | 586 | 587 | def 解析浮点数字符串(字符串: str) -> tuple: 588 | return parse_float(字符串) 589 | --------------------------------------------------------------------------------