PK!|8ç$$pupy/__init__.py# -*- coding: utf-8 -*- # ~ Jesse K. Rubin ~ Pretty Useful Python from __future__ import division from __future__ import print_function from __future__ import with_statement from pupy.savings_n_loads import ljson from pupy.savings_n_loads import load_jasm from pupy.savings_n_loads import loads from pupy.savings_n_loads import lpak from pupy.savings_n_loads import save_jasm from pupy.savings_n_loads import savings from pupy.savings_n_loads import sjson from pupy.savings_n_loads import spak try: range except NameError: range = xrange PK!£œ¥¥pupy/__main__.py# -*- coding: utf-8 -*- # ~ Jesse K. Rubin ~ Pretty Useful Python """ Entrypoint module, in case you use `python -mpupy`. Why does this file exist, and why __main__? For more info, read: - https://www.python.org/dev/peps/pep-0338/ - https://docs.python.org/2/using/cmdline.html#cmdoption-m - https://docs.python.org/3/using/cmdline.html#cmdoption-m """ from pupy.cli import main if __name__ == "__main__": main() PK!öàÄMPPpupy/_version.py# -*- coding: utf-8 -*- # ~ Jesse K. Rubin ~ Pretty Useful Python from pathlib import Path __version__ = ( [ l for l in open(str(Path(__file__).resolve().parents[1] / "pyproject.toml")) .read() .split("\n") if "version" in l ][0] .replace("version = ", "") .strip('"') ) PK!T|‹ä$ä$pupy/amazon_prime.py# -*- coding: utf-8 -*- # ~ Jesse K. Rubin ~ Pretty Useful Python from __future__ import division from __future__ import print_function import collections from bisect import bisect from bisect import bisect_right from itertools import count from math import sqrt from sys import version_info from pupy.decorations import cash_it from pupy.maths import divisors_gen try: range except NameError: range = xrange def prime_gen(plim=0, kprimes=None): """Infinite (within reason) prime number generator My big modification is the pdiv_dictionary() function that recreats the dictionary of divisors so that you can continue to generate prime numbers from a (sorted) list of prime numbers. Based on: eratosthenes by David Eppstein, UC Irvine, 28 Feb 2002 http://code.activestate.com/recipes/117119/ and the thread at the url :param plim: prime_limit; default=0 makes for an infinite generator :type plim: int :param kprimes: known_primes as an iterable (Default value = None) :type kprimes: iter .. doctest:: >>> list(prime_gen(50)) [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47] >>> list(prime_gen(10)) [2, 3, 5, 7] """ if kprimes is None: kprimes = [2, 3, 5, 7, 11] def _dictionary(): """Recreates the prime divisors dictionary used by the generator""" div_dict = {} for pdiv in kprimes: multiple = kprimes[-1] // pdiv * pdiv if multiple % 2 == 0: multiple += pdiv else: multiple += 2 * pdiv while multiple in div_dict: multiple += pdiv * 2 div_dict[multiple] = pdiv return div_dict # [1] # See if the upper bound is greater than the known primes if 0 < plim <= kprimes[-1]: for p in kprimes: if p <= plim: yield p return # return bc we are done # [2] # Recreate the prime divisibility dictionary using kprimes; # Set start and yield first 4 primes divz = _dictionary() start = kprimes[-1] + 2 # max prime + 2 (make sure it is odd) if start == 13: yield 2 yield 3 yield 5 yield 7 yield 11 # use count or range depending on if generator is infinite it = count(start, 2) if plim == 0 else range(start, plim, 2) for num in it: prime_div = divz.pop(num, None) if prime_div: multiple = (2 * prime_div) + num while multiple in divz: multiple += 2 * prime_div divz[multiple] = prime_div else: divz[num * num] = num yield num def prime_factorization_gen(n): """generates all numbers in the prime factorization of n :param n: number to be factored :type n: int .. doctest:: >>> list(prime_factorization_gen(12)) [2, 2, 3] >>> list(prime_factorization_gen(16)) [2, 2, 2, 2] """ for factor in prime_factors_gen(n): if n <= 1: break while n % factor == 0: n //= factor yield factor def prime_factors_gen(n): """prime factors generator :param n: number to be factorized :type n: int .. doctest:: python >>> list(prime_factors_gen(12)) [2, 3] >>> list(prime_factors_gen(16)) [2] """ return (p for p in divisors_gen(n) if is_prime(p)) @cash_it def is_prime(number): """Checks if a number is prime :param number: number to check if is prime :type number: int :returns: -> True if number is prime :rtype: bool .. doctest:: python >>> is_prime(1) False >>> is_prime(2) True >>> is_prime(3) True >>> is_prime(4) False >>> is_prime(5) True >>> is_prime(6) False >>> is_prime(7) True >>> is_prime(100) False >>> is_prime(89) True """ if number == 2 or number == 3: return True if number < 2 or number % 2 == 0: return False if number < 9: return True if number % 3 == 0: return False for step in range(5, int(sqrt(number)) + 1, 6): if step >= number: break if number % step == 0: return False if number % (step + 2) == 0: return False return True # class OctopusPrime(list): class OctopusPrime(collections.MutableSequence): """OctopusPrime, the 8-leg autobot, here to help you find PRIMES .. ───────────▄▄▄▄▄▄▄▄▄─────────── ────────▄█████████████▄──────── █████──█████████████████──█████ â–████▌─▀███▄───────▄███▀─â–████▌ ─█████▄──▀███▄───▄███▀──▄█████─ ─â–██▀███▄──▀███▄███▀──▄███▀██▌─ ──███▄▀███▄──▀███▀──▄███▀▄███── ──â–█▄▀█▄▀███─▄─▀─▄─███▀▄█▀▄█▌── ───███▄▀█▄██─██▄██─██▄█▀▄███─── ────▀███▄▀██─█████─██▀▄███▀──── ───█▄─▀█████─█████─█████▀─▄█─── ───███────────███────────███─── ───███▄────▄█─███─█▄────▄███─── ───█████─▄███─███─███▄─█████─── ───█████─████─███─████─█████─── ───█████─████─███─████─█████─── ───█████─████─███─████─█████─── ───█████─████▄▄▄▄▄████─█████─── ────▀███─█████████████─███▀──── ──────▀█─███─▄▄▄▄▄─███─█▀────── ─────────▀█▌â–█████▌â–█▀───────── ────────────███████──────────── """ def __init__(self, plim=100): # list.__init__(self, list(prime_gen(plim=plim))) # super(OctopusPrime, self).__init__() p = [ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, ] if plim == 100: self._list = p[:] elif plim < 100: self._list = list(filter(lambda n: n <= plim, p)) else: self._list = list(prime_gen(plim, p)) self.max_loaded = self._list[-1] def _transform(self, n=None): """TRANSFORM / grow the list :param n: (Default value = None) """ n = n if n is not None else self._list[-1] * 10 self._list.extend(list(prime_gen(plim=n, kprimes=self._list))) def primes_below(self, upper_bound): """Lists primes, p, such that p < upper_bound :param upper_bound: exclusive upper bound :type upper_bound: int :returns: -> primes less than upper_bound :rtype: list """ return self.primes_between(1, upper_bound) def primes_between(self, lower_bound, upper_bound): """Lists primes, p, such that, lower_bound < p < upper_bound :param lower_bound: exclusive lower bound :type lower_bound: int :param upper_bound: exclusive upper bound :type upper_bound: int :returns: -> primes between lower_bound and upper_bound :rtype: list """ if upper_bound > self[-1]: self._transform(upper_bound) return self[bisect_right(self, lower_bound) : bisect(self, upper_bound)] def __len__(self): return len(self._list) def __getitem__(self, i): return self._list[i] def __delitem__(self, i): del self._list[i] def __setitem__(self, key, value): self._list[key] = value def insert(self, index, object): """ :param index: :param object: """ self._list.insert(index, object) def __str__(self): return str(self._list) def __repr__(self): return str(self._list) if __name__ == "__main__": from doctest import testmod testmod() PK!J‚JJpupy/amazon_prime.pyi# -*- coding: utf-8 -*- # ~ Jesse K. Rubin ~ Pretty Useful Python from collections import MutableSequence from typing import Any from typing import List def prime_gen(plim: int = ..., kprimes: List[int] = ...): ... def prime_factorization_gen(n: int) -> None: ... def prime_factors_gen(n: int): ... def is_prime(number: int): ... class OctopusPrime(MutableSequence): max_loaded: int = ... def __init__(self, plim: int = ...) -> None: self._list = None ... def __len__(self) -> int: ... def __delitem__(self, key) -> None: ... def __getitem__(self, key) -> Any: ... def __setitem__(self, key): ... def _transform(self, n: int = ...) -> None: ... def primes_below(self, upper_bound: int): ... def primes_between(self, lower_bound: int, upper_bound: int): ... def insert(self, key): ... PK!«.Ëww pupy/cli.py# -*- coding: utf-8 -*- # ~ Jesse K. Rubin ~ Pretty Useful Python """ ======== Pupy CLI ======== """ from argparse import ZERO_OR_MORE from argparse import ArgumentParser from codecs import decode from os import listdir from os import rename from pupy._version import __version__ def unescaped_str(arg_str): return decode(str(arg_str), "unicode_escape") parser = ArgumentParser(description="Command description.") parser.add_argument( "-r", "--replace", metavar="PAT", nargs=2, help="Rename all things in." ) parser.add_argument("-V", "--version", action="store_true", help="Print pupy version.") parser.add_argument( "--rm-pattern", metavar="PAT", type=unescaped_str, nargs=ZERO_OR_MORE, help="REPLACE pattern with a space.", ) def main(args=None): """ :param args: """ args = parser.parse_args(args=args) if args.version: print("Pupy version: {}".format(__version__)) if args.replace: frum, two = args.replace print("Replacing:", frum) print("With:", two) for f in listdir("."): print("For f/d:", f) rename(f, f.replace(frum, two)) if args.rm_pattern: for pattern in args.rm_pattern: print("Removing pattern:", pattern) for f in listdir("."): print("For f/d:", f) rename(f, f.replace(pattern, "")) PK!¡zÊÈÈ pupy/cli.pyi# -*- coding: utf-8 -*- # ~ Jesse K. Rubin ~ Pretty Useful Python from typing import Any, Optional def unescaped_str(arg_str: Any): ... parser: Any def main(args: Optional[Any] = ...) -> None: ... PK!†ùE4pupy/decorations.py# -*- coding: utf-8 -*- # ~ Jesse K. Rubin ~ Pretty Useful Python from __future__ import division from __future__ import print_function from cProfile import Profile from functools import wraps from inspect import getfile from os import chdir from os import getcwd from os import makedirs from os import path, mkdir from time import time from typing import Callable from typing import Tuple from pupy.utils import fmt_seconds import logging from logging.config import dictConfig # "%(color)s[%(levelname)1.1s %(asctime)s %(module)s:%(lineno)d]%(end_color)s %(message)s" logging_config = dict( version=1, formatters={ 'f': { 'format': "[%(levelname)1.1s %(asctime)s %(filename)s:%(lineno)d] %(message)s" } }, handlers={ 'h': { 'class' : 'logging.StreamHandler', 'formatter': 'f', 'level' : logging.DEBUG } }, root={ 'handlers': ['h'], 'level' : logging.DEBUG, }, ) dictConfig(logging_config) logger = logging.getLogger() def in_n_out(funk: Callable): """Chdir in to the dir the test_function is in and change dirs out when done :type funk: Callable :param funk: docin.api logger functions logger.(debug/info/warn/error) :return: wrapped function """ @wraps(funk) def chin_n_chout(*args, **kwargs): """ :param args: :param kwargs: :return: """ # thing = os.path.split(os.path.realpath(__file__)) cd = getcwd() dirpath = args[0] if path.isdir(dirpath): chdir(dirpath) funk_results = funk(*args, **kwargs) chdir(cd) return funk_results return chin_n_chout def flog(funk=None, loglevel='debug', funk_call=True, tictoc=False): D = { 'debug': logger.debug, 'info' : logger.info, 'warn' : logger.warning, 'error': logger.error } def _decorate_flog_wrapper(_funk): def _fmt_args(*args): return ", ".join(str(arg) for arg in args) def _fmt_kwargs(**kwargs): return ", ".join("{}={}".format(str(k), str(v)) for k, v in kwargs.items()) def _fmt_call(*args, **kwargs): params_str = ", ".join( s for s in (_fmt_args(*args), _fmt_kwargs(**kwargs)) if s ) return "{}({})".format(_funk.__name__, params_str) @wraps(_funk) def _flog_wrapper(*args, **kwargs): ti = time() _ret = _funk(*args, **kwargs) tf = time() msg_parts = [_fmt_call(*args, **kwargs) if funk_call else None, fmt_seconds(ti, tf) if tictoc else None] msg_str = ' | '.join(part for part in msg_parts if part) if any(el for el in msg_parts): D[loglevel]("[FLOG] | {}".format(msg_str)) return _ret return _flog_wrapper return _decorate_flog_wrapper(funk) if funk else _decorate_flog_wrapper def dirdec(funk): @wraps(funk) def _wrapper(*args, **kwargs): result = funk(*args, **kwargs) try: mkdir(result) except (FileExistsError, OSError) as e: pass return result return _wrapper def mkdirs(funk): @wraps(funk) def _wrapper(*args, **kwargs): dirpath = path.split(args[0])[0] try: makedirs(dirpath, exist_ok=True) except OSError: pass return funk(*args, **kwargs) return _wrapper def cash_it(funk): """args-2-return value cache. This function is particularly useful for when you want that lru-cache, but you/one is working with python two. :param funk: function to be cached :type funk: function :returns: wrapped function :rtype: function """ cash_money = {} @wraps(funk) def cash_wrap(*argz): """ """ if argz not in cash_money: rv = funk(*argz) cash_money[argz] = rv return cash_money[argz] return cash_wrap def cprof(funk): """"cProfiling decorator src: https://zapier.com/engineering/profiling-python-boss/ :param funk: """ @wraps(funk) def profiled_funk(*args, **kwargs): """ """ profile = Profile() try: profile.enable() ret_val = funk(*args, **kwargs) profile.disable() finally: print("__CPROFILE__") profile.print_stats() return ret_val return profiled_funk class tictoc(object): """Timing decorator object :param runs: # of runs to time over (defaults to 1) """ def __init__(self, runs=1): self.runs = runs def __str__(self, t_total, funk, args_string): str_list = [ "__TICTOC__", " file: {}".format(getfile(funk)), " funk: {}".format(funk.__name__), " args: {}".format(args_string), " time: {}".format(tictoc.ftime(t_total)), " runs: {}".format(self.runs), ] return "\n".join(str_list) def __call__(self, time_funk, printing=True): @wraps(time_funk) def time_wrapper(*args, **kwargs): """ """ self.args = str(args) ts = time() for i in range(self.runs): result = time_funk(*args, **kwargs) te = time() t_total = (te - ts) / self.runs if printing: print(self.__str__(t_total, time_funk, str(args))) return result return time_wrapper @staticmethod def ftime(t1, t2=None): """Formats time string Formats t1 if t2 is None as a string; Calculates the time and formats the time t2-t1 if t2 is not None. :param t1: time 1 :type t1: double :param t2: time 2 (Default value = None) :type t2: None or double :returns: -> formated time string :rtype: str """ if t2 is not None: return tictoc.ftime((t2 - t1)) elif t1 == 0.0: return "~0.0~" elif t1 >= 1: return "%.3f s" % t1 elif 1 > t1 >= 0.001: return "%.3f ms" % ((10 ** 3) * t1) elif 0.001 > t1 >= 0.000001: return "%.3f μs" % ((10 ** 6) * t1) elif 0.000001 > t1 >= 0.000000001: return "%.3f ns" % ((10 ** 9) * t1) else: return tictoc.ftime((t2 - t1)) PK!rª6ffpupy/decorations.pyi# -*- coding: utf-8 -*- # ~ Jesse K. Rubin ~ Pretty Useful Python from typing import Any from typing import Callable from typing import Dict from typing import Optional def cash_it(funk: Callable): ... def mkdirs(funk: Callable): ... def dirdec(funk: Callable): ... def flog(funk: Callable): ... def in_n_out(funk: Callable): ... class Jasm: file_path: str = ... def __init__(self, path: str) -> None: ... def __call__(self, funk: Callable): ... @staticmethod def read(fpath: str): ... @staticmethod def write(fpath: str, obj: Dict) -> None: ... def cprof(funk: Callable): ... class tictoc(object): runs: Any = ... def __init__(self, runs: int = ...) -> None: ... args: Any = ... def __call__(self, time_funk: Callable, printing: bool = ...): ... @staticmethod def ftime(t1: float, t2: Optional[float] = ...): ... PK!Léëääpupy/foreign.py# -*- coding: utf-8 -*- # ~ Jesse K. Rubin ~ Pretty Useful Python from __future__ import division from __future__ import print_function from collections import Counter from collections import deque from functools import reduce from operator import mul from os import getcwd from os import path from os import sep from os import walk def files_gen(dirpath=getcwd(), abs=True): """Yields paths beneath dirpath param; dirpath defaults to os.getcwd() :param dirpath: Directory path to walking down/through. :param abs: Yield the absolute path :return: Generator object that yields filepaths (absolute or relative) """ return ( fpath if abs else fpath.replace(dirpath, "").strip(sep) for fpath in ( path.join(pwd, file) for pwd, dirs, files in walk(dirpath) for file in files ) ) def dirs_gen(dirpath=getcwd(), abs=True): """Yields paths beneath dirpath param; dirpath defaults to os.getcwd() :param dirpath: Directory path to walking down/through. :param abs: Yield the absolute path :return: Generator object that yields dir-paths (absolute or relative) """ return ( fpath if abs else fpath.replace(dirpath, "").strip(sep) for fpath in (pwd for pwd, dirs, files in walk(dirpath)) ) def exhaust(it): """Exhaust an interable / use it up; useful for evaluating a map object. :param it: iterable object to run through :return: None .. docstring::python >>> a = [1, 2, 3, 4, 5, 6] >>> a_map = map(lambda x: x*2, a) >>> a_exhausted = exhaust(a_map) >>> a_exhausted == None # will be none after being exhausted True """ deque(it, maxlen=0) def chunks(list, chunk_size): """Yields chunks of something slicable with length <= chunk_size :param list: list to be broken up :type list: list or tuple :param chunk_size: size of the chunks :type chunk_size: int .. docstring::python >>> list(chunks([1, 2, 3, 4, 5, 6], 3)) [[1, 2, 3], [4, 5, 6]] >>> list(chunks([1, 2, 3, 4, 5, 6], 2)) [[1, 2], [3, 4], [5, 6]] >>> list(chunks('abcdefghijklmnopqrstuvwxyz', 2)) ['ab', 'cd', 'ef', 'gh', 'ij', 'kl', 'mn', 'op', 'qr', 'st', 'uv', 'wx', 'yz'] >>> list(chunks('abcdefghijklmnopqrstuvwxyz', 13)) ['abcdefghijklm', 'nopqrstuvwxyz'] """ return (list[i: i + chunk_size] for i in range(0, len(list), chunk_size)) def is_permutation(a, b): """Checks if two integers or lists are permutations lists are permutations :param a: possible perumtation of b :type a: int or list :param b: possible perumtation of a :type b: int or list :returns: True if a and b are permutations of one another; False otherwise :rtype: bool It works for list!!! .. doctest:: python >>> a = [1, 2, 3, 4] >>> b = [4, 3, 2, 1] >>> is_permutation(a, b) True >>> a = [1, 3, 2, 4] >>> b = [4, 3, 2, 1] >>> is_permutation(a, b) True >>> a = [1, 3, 2, 4] >>> b = [4, 3, 2, 1, 1] >>> is_permutation(a, b) # False cuz of the extra 'one' False It works for integers!?!?!? .. doctest:: python >>> a = 1234 >>> b = 4321 >>> is_permutation(a, b) True >>> a = 12344 >>> b = 43212 >>> is_permutation(a, b) False It also works for strings!!! .. doctest:: python >>> a = 'abcd' >>> b = 'dbca' >>> is_permutation(a, b) # False cuz of the extra 'one' True >>> a = 'pood' >>> b = 'doop' >>> is_permutation(a, b) # False cuz of the extra 'one' True >>> a = 'snorkel' >>> b = 'doop' >>> is_permutation(a, b) # False cuz of the extra 'one' False """ if isinstance(a, int): a = digits_list(a) if isinstance(b, int): b = digits_list(b) return len(a) == len(b) and Counter(a) == Counter(b) def rotate(rlist, rn=1, left_rotate=True): """Rotate a list (rlist) by rn indices to the left or right :param rlist: list/toople or slicable to be rotated :type rlist: list or tuple :param rn: steps bywhich to rotate (Default value = 1) :type rn: int :param left_rotate: True (default) left rotates; False right rotates. :type left_rotate: bool :returns: rotated list :rtype: list .. doctest:: python >>> rotate([1, 2, 3, 4], left_rotate=True) [2, 3, 4, 1] >>> rotate([1, 2, 3, 4], left_rotate=False) [4, 1, 2, 3] >>> rotate([1, 2, 3, 4], rn=4, left_rotate=False) [1, 2, 3, 4] """ def _left_rotate(l, n=1): """ :param l: :param n: (Default value = 1) """ return l[n:] + l[:n] def _right_rotate(l, n=1): """ :param l: :param n: (Default value = 1) """ return l[-n:] + l[:-n] return _left_rotate(rlist, rn) if left_rotate else _right_rotate(rlist, rn) def rotations_gen(rlist): """Yields all rotations of a list :param rlist: .. doctest::python >>> for rot in rotations_gen((1, 2, 3, 4)): ... print(rot) ... (1, 2, 3, 4) (4, 1, 2, 3) (3, 4, 1, 2) (2, 3, 4, 1) """ return ((rlist[-i:] + rlist[:-i]) for i in range(len(rlist))) def digits_list(number): """Returns a list of the digits in num :param number: number w/ digits to be listsed :type number: int :returns: -> digits in a list :rtype: list .. doctest::python >>> digits_list(1111) [1, 1, 1, 1] >>> digits_list(982) [9, 8, 2] >>> digits_list(101) [1, 0, 1] >>> digits_list(123) [1, 2, 3] """ digits = deque() for _ in range(len(str(number))): number, r = divmod(number, 10) digits.appendleft(r) return list(digits) def int_from_digits(digits): """Converts an iterable of digits digits to a number The iteratble can be ints or strings/chars :rtype: int .. doctest::python >>> int_from_digits([3, 2, 1]) 321 >>> int_from_digits([1, 1, 1, 1, 2, 3]) 111123 >>> int_from_digits([1, 2, 3]) 123 """ return sum(digits[len(digits) - i - 1] * 10 ** i for i in range(0, len(digits), 1)) def iter_product(l): """Product of all the elements in a list or tuple :param l: list with integer elements :returns: product of all the elements in a list :rtype: int .. doctest::python >>> iter_product([1, 2, 3, 4]) 24 >>> iter_product(tuple([1, 2, 3, 4])) 24 >>> iter_product([-1, -2, -3, 4]) -24 """ return reduce(mul, l) PK!`“¯ÝÝpupy/foreign.pyi# -*- coding: utf-8 -*- # ~ Jesse K. Rubin ~ Pretty Useful Python from typing import Any from typing import Generator from typing import Iterable from typing import List from typing import Union def chunks(list: List[Any], chunk_size: int) -> Generator: ... def is_permutation(a: Union[Iterable[Any], int], b: Union[Iterable[Any], int]): ... def rotate(rlist: Any, rn: int = ..., left_rotate: bool = ...): ... def rotations_gen(rlist: Union[List[Any], tuple]) -> None: ... def digits_list(number: int): ... def int_from_digits(dlist: Union[List[int], tuple, Iterable[int]]): ... def iter_product(l: Union[List[int], tuple, Iterable[int]]): ... def files_gen(dirpath: str, abs: bool): ... def dirs_gen(dirpath: str, abs: bool): ... PK!Š} äV=V= pupy/maths.py# -*- coding: utf-8 -*- # ~ Jesse K. Rubin ~ Pretty Useful Python from __future__ import division from __future__ import print_function from collections import Counter from math import acos from math import factorial from math import pi from math import sqrt from operator import add from operator import floordiv from operator import methodcaller from operator import sub from operator import truediv from pupy.decorations import cash_it from pupy.foreign import iter_product def partitions_gen(numero, min_p=1, max_p=None): """Partitions generator Adapted from: code.activestate.com/recipes/218332-generator-for-integer-partitions/min_p :param numero: number for which to yield partiton tuples :type numero: int :param min_p: smallest part size (Default value = 1) :type min_p: int :param max_p: largest part size (Default value = None) :type max_p: int .. docstring::python >>> list(partitions_gen(4)) [(4,), (1, 3), (1, 1, 2), (1, 1, 1, 1), (2, 2)] >>> list(partitions_gen(4, min_p=1, max_p=2)) [(1, 1, 2), (1, 1, 1, 1), (2, 2)] """ if max_p is None or max_p >= numero: yield (numero,) for i in range(min_p, numero // 2 + 1): for p in partitions_gen(numero - i, i, max_p): yield (i,) + p @cash_it def rfactorial(n): """Recursive factorial function :param n: .. docstring::python >>> from math import factorial >>> rfactorial(1) == factorial(1) True >>> rfactorial(2) == factorial(2) True >>> rfactorial(3) == factorial(3) True >>> rfactorial(4) == factorial(4) True >>> rfactorial(5) == factorial(5) True >>> rfactorial(6) == factorial(6) True >>> rfactorial(7) == factorial(7) True >>> rfactorial(8) == factorial(8) True >>> rfactorial(9) == factorial(9) True """ if n == 1: return 1 else: return rfactorial(n - 1) * n def radians_2_degrees(rads): """Converts radians to degrees :param rads: .. doctest:: python >>> from math import pi >>> radians_2_degrees(2*pi) == 360.0 True >>> radians_2_degrees(2*pi) 360.0 """ return 180 * rads / pi def degrees_2_radians(degs): """Converts degrees to radians :param degs: .. doctest:: python >>> from math import pi >>> degrees_2_radians(360.0) == 2*pi True """ return degs * pi / 180 def power_mod(number, exponent, mod): """ :param number: :param exponent: :param mod: """ if exponent > 0: if exponent % 2 == 0: return power_mod(number, floordiv(exponent, 2), mod) return power_mod(number, floordiv(exponent, 2), mod) * number else: return 1 def divisors_gen(n): """Divisors generator :param n: number w/ divisors to be generated :type n: int .. doctest:: python >>> list(divisors_gen(1)) [1] >>> list(divisors_gen(4)) [1, 2, 4] >>> list(divisors_gen(16)) [1, 2, 4, 8, 16] """ large_divisors = [] for i in range(1, int(sqrt(n) + 1)): if n % i == 0: yield i if i * i != n: large_divisors.append(n // i) for divisor in reversed(large_divisors): yield divisor def gcd_it(a, b): """iterative gcd :param a: :param b: >>> from pupy.maths import gcd_it >>> from pupy.maths import gcd_r >>> gcd_it(1, 4) == gcd_r(1, 4) True >>> gcd_it(2, 6) == gcd_r(2, 6) True >>> gcd_it(3, 14) == gcd_r(3, 14) True >>> gcd_it(4, 300) == gcd_r(4, 300) True """ while a: a, b = b % a, a return b @cash_it def gcd_r(a, b): """recursive greatest common divisor :param a: :param b: .. doctest:: python >>> from pupy.maths import gcd_it >>> from pupy.maths import gcd_r >>> gcd_it(1, 4) == gcd_r(1, 4) True >>> gcd_it(2, 6) == gcd_r(2, 6) True >>> gcd_it(3, 14) == gcd_r(3, 14) True >>> gcd_it(4, 300) == gcd_r(4, 300) True """ if b > a: return gcd_r(b, a) r = a % b if r == 0: return b return gcd_r(r, b) def reverse(n): """Reverses a number :param n: number to be reversed :type n: int :returns: reversed of a number :rtype: int .. doctest:: python >>> reverse(1) 1 >>> reverse(12345) 54321 >>> reverse(54321) 12345 >>> reverse(54321000) 12345 """ reversed = 0 while n > 0: reversed *= 10 reversed += n % 10 n //= 10 return reversed @cash_it def fib_r(n): """Recursively the nth fibonacci number :param n: nth fibonacci sequence number :type n: int :returns: the nth fibonacci number :rtype: int .. docstring::python >>> fib_r(1) 1 >>> fib_r(2) 2 >>> fib_r(6) 13 """ return n if n < 3 else fib_r(n - 1) + fib_r(n - 2) def expo(d, n): """greatest exponent for a divisor of n :param d: divisor :type d: int :param n: number be divided :type n: int :returns: number of times a divisor divides n :rtype: int """ if n < d: # flip d, n = n, d c = n divs = 0 while c % d == 0: c //= d divs += 1 return divs def pytriple_gen(max_c): """primative pythagorean triples generator special thanks to 3Blue1Brown's video on pythagorean triples https://www.youtube.com/watch?v=QJYmyhnaaek&t=300s :param max_c: max value of c to yeild triples up to :type max_c: int """ for real_pts in range(2, int(sqrt(max_c)) + 1, 1): for imag_pts in range(real_pts % 2 + 1, real_pts, 2): comp = complex(real_pts, imag_pts) sqrd = comp * comp real = int(sqrd.real) imag = int(sqrd.imag) if abs(real - imag) % 2 == 1 and gcd_it(imag, real) == 1: sea = int((comp * comp.conjugate()).real) if sea > max_c: break else: yield (imag, real, sea) if real > imag else (real, imag, sea) def repermutations(toop): """ :param toop: """ c = Counter(n for n in toop) a = list(factorial(nc) for nc in c.values()) ans = factorial(len(toop)) // iter_product(a) return ans def disjoint(a, b): """ :param a: :param b: .. doctest:: python >>> a = [1, 2, 3, 4] >>> b = [2, 3, 4, 5] >>> disjoint(a, b) False >>> a = [1, 2, 3, 4] >>> b = [5, 6, 7, 8] >>> disjoint(a, b) True >>> a = ['joe', 'frank', 3, 4] >>> b = [5, 6, 7, 'frank'] >>> disjoint(a, b) False """ return not any(ae in b for ae in a) def set_cmp(a, b): """Compare the elements of two iterables (a and b) :param a: first iterable to compare elements of :param b: second iterable to compare elements of :return: tuple of sets of the form (common elements, in a only, in b only) .. doctest:: >>> a = [n for n in range(6)] >>> a [0, 1, 2, 3, 4, 5] >>> b = [n for n in range(3, 9)] >>> b [3, 4, 5, 6, 7, 8] >>> set_cmp(a, b) ({3, 4, 5}, {0, 1, 2}, {8, 6, 7}) """ if not isinstance(a, set) or not isinstance(b, set): return set_cmp(set(a), set(b)) return a & b, a - b, b - a def n_choose_r(n, r): """ :param n: :param r: """ return factorial(n) // factorial(r) // factorial(n - r) def pytriple_gen_2(): """ """ diagonal_size = 3 cur_x = 1 cur_y = 2 while True: if cur_y <= cur_x: diagonal_size += 1 cur_x = 1 cur_y = diagonal_size - 1 imag_part = cur_y real_part = cur_x to_yield = get_pythag_triple(real_part, imag_part) cur_x += 1 cur_y -= 1 if gcd_it(to_yield[0], to_yield[1]) > 1: continue yield to_yield def get_pythag_triple(real, imag): """ :param real: :param imag: """ comp = complex(real, imag) sea = int((comp * comp.conjugate()).real) sqrd = comp * comp real = abs(int(sqrd.real)) imag = abs(int(sqrd.imag)) return min(imag, real), max(imag, real), sea class Trigon(object): """Trigon object composed of three points connected by lines.""" def __init__(self, pt1, pt2, pt3): self.pt1 = Vuple(pt1) self.pt2 = Vuple(pt2) self.pt3 = Vuple(pt3) @classmethod def from_points(cls, pts): """ :param pts: return: """ if len(pts) == 3: return Trigon(*pts) if len(pts) == 6: it = iter(pts) return Trigon(*zip(it, it)) def __str__(self): return "<< {}, {}, {} >>".format(self.pt1, self.pt2, self.pt3) def __contains__(self, point): if type(point) is not Vuple: point = Vuple(point) return self.area() == sum( map(methodcaller("area"), self.inner_triangles(point)) ) def inner_triangles(self, point): """Triangle funk that returns the three triangles w/ a point The point (p) is connected to each point of a triangle. with points, a, b, and c. The three triangles are t1=(a, b, p), t2=(a, c, p), and t3 = (b, c, p). :param point: point to connect to Triangle Vertices :type point: tuple or Vuple :returns: t1, t2, t3-> Three triangles """ t1 = Trigon(point, self.pt2, self.pt3) t2 = Trigon(self.pt1, point, self.pt3) t3 = Trigon(self.pt1, self.pt2, point) return t1, t2, t3 def is_perimeter_point(self, point): """ :param point: """ if type(point) is not Vuple: point = Vuple(point) return any( tri_area == 0 for tri_area in map(methodcaller("area"), self.inner_triangles(point)) ) def points(self): """ """ return self.pt1, self.pt2, self.pt3 def contains_origin(self): """True if the origin (0,0) lies within the Triangle""" return (0, 0) in self def area(self): """ """ return abs(truediv(Vuple.cross(self.pt1 - self.pt2, self.pt3 - self.pt2), 2)) @staticmethod def area_from_points(pt1, pt2, pt3): """ :param pt1: :param pt2: :param pt3: """ return abs(truediv(Vuple.cross(pt1 - pt2, pt3 - pt2), 2)) class Vuple(tuple): """VUPLE == Vector+Tuple""" def __new__(cls, *args): """ :param args: :return: """ return super(Vuple, cls).__new__(cls, tuple(*args)) def __gt__(self, other): return Vuple.mag_sqrd(self) > Vuple.mag_sqrd(other) def __eq__(self, other): return all(a == b for a, b in zip(self, other)) def __add__(self, k): """ .. docstring::python >>> va = Vuple((8, 13)) >>> vb = Vuple((26, 7)) >>> va + vb (34, 20) """ if type(k) is int or type(k) is float: return Vuple((k + el for el in self)) elif type(k) is Vuple and len(self) == len(k): return Vuple(map(add, self, k)) raise ValueError("huh idk") def __iadd__(self, k): return self.__add__(k) def __sub__(self, k): return Vuple(map(sub, self, k)) def __isub__(self, k): return self.__sub__(k) def __mul__(self, k): """Multiply by a scalar for each element or cross product if also iterable of same length :param k: scalar ot other iterable to do the cross producting with :return: A Vuple or a sum as a cost product .. docstring::python >>> a = Vuple((1, 2, 3)) >>> a * a 14 >>> a = Vuple((1, 1, 1)) >>> a * 4 (4, 4, 4) """ if type(k) is int or type(k) is float: return self._mul_scalar(k) elif type(k) is Vuple: if len(k) != len(self): raise ValueError("Sizes do not match!") return Vuple.dot(self, k) def __imul__(self, k): return self.__mul__(k) def _mul_scalar(self, k): """ :param k: """ return Vuple((k * el for el in self)) def __truediv__(self, k): if type(k) is int or type(k) is float: return self._truediv_scalar(k) def _truediv_scalar(self, k): """ :param k: """ return Vuple((el / k for el in self)) def __itruediv__(self, k): return self.__truediv__(k) def __floordiv__(self, k): if type(k) is int or type(k) is float: return self._floordiv_scalar_int(k) def __ifloordiv__(self, k): return self.__floordiv__(k) def _floordiv_scalar_int(self, k): """ :param k: """ return Vuple((el // k for el in self)) def normalize(self): """Normalizes the Vuple ST self.magnitude == 1 :return: Unit Vuple """ return Vuple.unit_vuple(self) @staticmethod def unit_vuple(voop): """ :param voop: """ return Vuple(voop) / Vuple.mag(voop) def get_mag_sqrd(self): """ """ return Vuple.mag_sqrd(self) @staticmethod def mag_sqrd(voop): """ :param voop: """ return sum(el * el for el in voop) def get_mag(self): """ """ return Vuple.mag(self) @staticmethod def mag(voop): """ :param voop: .. docstring::python >>> v = Vuple((3, 4, 5)) """ return sqrt(Vuple.mag_sqrd(voop)) @staticmethod def dot(a, b): """ :param a: :param b: """ return sum(va * vb for va, vb in zip(a, b)) @staticmethod def cross(v1, v2): """Cross product of two 2d vectors :param v1: first vector :param v2: second vector :returns: cross product of v1 and v2 """ if len(v1) == 2 and len(v2) == 2: return (v1[0] * v2[1]) - (v1[1] * v2[0]) else: raise ValueError("cross product gt 2d not implemented") @staticmethod def angle(v1, v2, radians=False): """ :param v1: :param v2: :param radians: (Default value = False) """ # return acos(Vuple.dproduct(v1, v2)/(Vuple.mag(v1)*Vuple.mag(v2))) q = 1 if radians else 180 / pi return q * acos(Vuple.dot(Vuple.unit_vuple(v1), Vuple.unit_vuple(v2))) def is_disjoint(self, them): """ :param them: """ return disjoint(self, them) def product(self): """Multiplies all elements in the Vuple :return: .. doctest:: pythonm >>> v = Vuple((1, 2, 3, 4)) >>> v.product() 24 >>> v = Vuple((100, 1, -1, 2)) >>> v.product() -200 >>> v = Vuple((100, -1, -1, 2)) >>> v.product() 200 """ return iter_product(self) if __name__ == "__main__": import doctest doctest.testmod() PK!çàÌ" " pupy/maths.pyi# -*- coding: utf-8 -*- # ~ Jesse K. Rubin ~ Pretty Useful Python from typing import Any from typing import Generator from typing import Iterable from typing import List from typing import Sequence from typing import Tuple from typing import Union Flint = Union[int, float] def partitions_gen(numero: int, min_p: int = ..., max_p: int = ...) -> Generator: ... def rfactorial(n: int): ... def radians_2_degrees(rads: Flint): ... def degrees_2_radians(degs: Flint): ... def power_mod(number: Flint, exponent: Flint, mod: Flint): ... def divisors_gen(n: int) -> Generator: ... def gcd_it(a: int, b: int): ... def gcd_r(a: int, b: int): ... def reverse(n: int): ... def fib_r(n: int): ... def expo(d: Any, n: Any): ... def pytriple_gen(max_c: int) -> Generator: ... def repermutations(toop: tuple) -> int: ... def disjoint(a: Iterable, b: Iterable) -> bool: ... def n_choose_r(n: int, r: int) -> int: ... def pytriple_gen_2() -> None: ... def get_pythag_triple(real: Any, imag: Any): ... def set_cmp(a: Iterable, b: Iterable) -> Tuple[set, set, set]: ... class Trigon: pt1: Any = ... pt2: Any = ... pt3: Any = ... def __init__(self, pt1: Any, pt2: Any, pt3: Any) -> None: ... @classmethod def from_points(cls, pts: List[Any]): ... def __str__(self): ... def __contains__(self, point: Any): ... def inner_triangles(self, point: Any): ... def is_perimeter_point(self, point: Any): ... def points(self): ... def contains_origin(self): ... def area(self): ... @staticmethod def area_from_points(pt1: Any, pt2: Any, pt3: Any): ... class Vuple(tuple): Voops = Union[tuple, int, float, Sequence] def __new__(cls, *args: Any): ... def __gt__(self, other: Any): ... def __eq__(self, other: Any): ... def __add__(self, k: Voops): ... def __sub__(self, k: Voops): ... def __mul__(self, k: Union[tuple, int, float]): ... def __imul__(self, k: Union[tuple, int, float]): ... def _mul_scalar(self, k: Flint): ... def __truediv__(self, k: Voops): ... def _truediv_scalar(self, k: Flint): ... def __itruediv__(self, k: Voops): ... def __floordiv__(self, k: Voops): ... def __ifloordiv__(self, k: Voops): ... def _floordiv_scalar_int(self, k: Flint): ... def normalize(self): ... def get_mag_sqrd(self): ... def get_mag(self): ... def is_disjoint(self, them: Any): ... def product(self): ... @staticmethod def unit_vuple(voop: Union[Vuple, tuple]): ... @staticmethod def mag_sqrd(voop: Union[Vuple, tuple, List[Union[int, float]]]): ... @staticmethod def mag(voop: Any): ... @staticmethod def dot(a: Any, b: Any): ... @staticmethod def cross(v1: Any, v2: Any): ... @staticmethod def angle( v1: Union[Vuple, tuple], v2: Union[Vuple, tuple], radians: bool = ... ): ... PK!„ÂÎí¦¦pupy/savings_n_loads.py# -*- coding: utf-8 -*- # ~ Jesse K. Rubin ~ Pretty Useful Python from __future__ import print_function from __future__ import with_statement from codecs import getwriter from io import open from itertools import count from os import path from os import utime from pupy.decorations import mkdirs try: from ujson import dump from ujson import load except Exception as e: from json import dump from json import load def safepath(path_str): """Checks if a file/dir path is save/unused; returns an unused path. :param path_str: """ if path.exists(path_str): f_bn, f_ext = path.splitext(path_str) for n in count(1): safe_save_path = f_bn + "_({}).".format(str(n)) + f_ext if not path.exists(safe_save_path): return safe_save_path return path_str @mkdirs def savings(filepath, string, clobber=True): """Writes a string to filepath :param filepath: Filepath save location :param string: File as a string to be saved :param clobber: Save over a file if the filepath exists :return: None? what do you want? confirmation? .. note:: Decorated w/ @mkdirs Function is decorated with @mkdirs decorator; @mkdirs creates parent directories for the given filepath if they do not already exist. """ if not clobber and path.exists(filepath): filepath = safepath(filepath) try: with open(safepath(filepath) if clobber else filepath, "wb") as file: file.write(string.encode("utf-8")) except Exception as e: raise e def loads(filepath): """Read and return the file-contents as a string given a filepath :param filepath: Path to a file to read :return: Content of the file read as a string """ try: with open(filepath, "r", encoding="utf-8") as f: return f.read() except UnicodeDecodeError as e: with open(filepath, "r", encoding="latin2") as f: return f.read() @mkdirs def sjson(filepath, data, min=False): """Save json-serial-ize-able data to a specific filepath. :param filepath: destination filepath :param data: json cereal-izable dictionary/list/thing :param min: Bool flag -- minify the json file :return: None """ if type(data) == dict and any(type(val) == bytes for val in data.values()): data = {k: str(v, encoding="utf-8") for k, v in data.items()} with open(filepath, "wb") as jsonfile: if min: dump(data, getwriter("utf-8")(jsonfile), ensure_ascii=False) else: dump( data, getwriter("utf-8")(jsonfile), indent=4, sort_keys=True, ensure_ascii=False, ) def save_jasm(filepath, data, min=False): """Alias for sjson (which stands for 'save-json')""" return sjson(filepath, data, min) def sjasm(filepath, data, min=False): """Alias for sjson (which stands for 'save-json')""" return sjson(filepath, data, min) def spak(filepath, data, min=False): """Alias for sjson (which stands for 'save-json')""" return sjson(filepath, data, min) @mkdirs def ljson(filepath): """Load a json file given a filepath and return the file-data :param filepath: path to the jasm file you want to load :return: Loaded file contents """ with open(filepath) as infile: return load(infile) def load_jasm(filepath): """Alias for ljson (which stands for 'load-json')""" return ljson(filepath) def ljasm(filepath): """Alias for ljson (which stands for 'load-json')""" return ljson(filepath) def lpak(filepath): """Alias for ljson (which stands for 'load-json')""" return ljson(filepath) @mkdirs def touch(filepath): """Touches a file just like touch on the command line :param filepath: filepath to 'touch' in a unix-y sense :return: None """ with open(filepath, "a"): utime(filepath, None) PK!W”Õƒ¿¿pupy/savings_n_loads.pyi# -*- coding: utf-8 -*- # ~ Jesse K. Rubin ~ Pretty Useful Python from typing import Any def timestamp(): ... def safepath(filepath: Any): ... def ensure_save(filepath: Any, n: int = ...): ... def savings(filepath: Any, string: Any, clobber: bool = ...) -> None: ... def loads(filepath: Any): ... def sjson(filepath: Any, data: Any, min: bool = ...) -> None: ... def save_jasm(filepath: Any, data: Any, min: bool = ...): ... def sjasm(filepath: Any, data: Any, min: bool = ...): ... def spak(filepath: Any, data: Any, min: bool = ...): ... def ljson(filepath: Any): ... def load_jasm(filepath: Any): ... def ljasm(filepath: Any): ... def lpak(filepath: Any): ... def touch(filepath: Any) -> None: ... PK!½l÷*íípupy/sodoku.py# -*- coding: utf-8 -*- # ~ Jesse K. Rubin ~ Pretty Useful Python from __future__ import division from __future__ import print_function from itertools import chain from pupy.decorations import cash_it class SodokuError(ValueError): """simple Sodoku error""" def __init__(self, message, row=None, col=None): self.message = message self.row, self.col = row, col super(SodokuError, self).__init__(message, row, col) class Sodoku(object): """Sodoku [ 0, 1, 2, 3, 4, 5, 6, 7, 8] [ 9, 10, 11, 12, 13, 14, 15, 16, 17] [18, 19, 20, 21, 22, 23, 24, 25, 26] [27, 28, 29, 30, 31, 32, 33, 34, 35] [36, 37, 38, 39, 40, 41, 42, 43, 44] [45, 46, 47, 48, 49, 50, 51, 52, 53] [54, 55, 56, 57, 58, 59, 60, 61, 62] [63, 64, 65, 66, 67, 68, 69, 70, 71] [72, 73, 74, 75, 76, 77, 78, 79, 80] """ def __init__(self, board): self.is_solved = False self.board = board.replace(".", "0") def solve(self): """ """ if 17 > sum(1 for n in self.board if n != "0"): raise SodokuError("not enough info") full_set = "123456789" d = { i: ("".join(c for c in full_set) if self.board[i] == "0" else self.board[i]) for i in range(81) } d = Sodoku.update_dictionary(d) tf, d = Sodoku.reduce_dictionary(d) if not tf: raise SodokuError("check_unsolvable") a = [d[ind] for ind in range(81)] self.board = "".join(a) self.is_solved = True def euler_096_three_digit_number(self): """ Returns: """ if not self.is_solved: self.solve() return int(self.board[0:3]) @staticmethod def first_unknown(d): """ Args: d: Returns: """ for i in range(81): if len(d[i]) > 1: return i @staticmethod def unsolvable(rcbd): """ Args: rcbd: Returns: """ return any(len(v) == 0 for v in rcbd.values()) @staticmethod def check_unsolvable(d): """ Args: d: Returns: """ nd = {k: v for k, v in d.items()} for rcb in range(9): box = { str(n): [ ind for ind in Sodoku.ibox(*divmod(rcb, 3)) if str(n) in d[ind] ] for n in range(1, 10) } row = { str(n): [ind for ind in Sodoku.irow(rcb) if str(n) in d[ind]] for n in range(1, 10) } col = { str(n): [ ind for ind in Sodoku.icolumn(rcb) if str(n) in d[ind] or str(n) == d[ind] ] for n in range(1, 10) } if ( Sodoku.unsolvable(box) or Sodoku.unsolvable(row) or Sodoku.unsolvable(col) ): raise SodokuError("UNSOLVABLE") return nd @staticmethod def update_dictionary(d): """ Args: d: Returns: """ nd = {k: v for k, v in d.items()} for i in range(81): if len(nd[i]) == 1: for nay in Sodoku.neighbors(i): if len(nd[nay]) != 1 and nd[i] in nd[nay]: nd[nay] = nd[nay].replace(nd[i], "") return nd @staticmethod def reduce_dictionary(d): """ Args: d: Returns: """ if all(len(v) == 1 for v in d.values()): return True, d try: d = Sodoku.check_unsolvable(d) except SodokuError: return False, d d = Sodoku.update_dictionary(d) if any(len(v) == 0 for k, v in d.items()): return False, d fz = Sodoku.first_unknown(d) if fz is None: if Sodoku.hasdup(d): return False, d return Sodoku.reduce_dictionary(d) for poss in d[fz]: nd = {k: v for k, v in d.items()} nd[fz] = str(poss) if not Sodoku.hasdup(nd): valid, ret = Sodoku.reduce_dictionary(nd) if valid: return valid, ret return False, d def __str__(self): header = " S O D O K U " top_border = "â•”â•â•â•â•â•â•â•╦â•â•â•â•â•â•â•╦â•â•â•â•â•â•â•â•—" mid_border = "â• â•â•â•â•â•â•â•╬â•â•â•â•â•â•â•╬â•â•â•â•â•â•â•â•£" bot_border = "╚â•â•â•â•â•â•â•â•©â•â•â•â•â•â•â•â•©â•â•â•â•â•â•â•â•" top_boxes = "\n".join( "â•‘ {} {} {} â•‘ {} {} {} â•‘ {} {} {} â•‘".format(*self.board[l * 9 : l * 9 + 9]) for l in range(0, 3) ) mid_boxes = "\n".join( "â•‘ {} {} {} â•‘ {} {} {} â•‘ {} {} {} â•‘".format(*self.board[l * 9 : l * 9 + 9]) for l in range(3, 6) ) bot_boxes = "\n".join( "â•‘ {} {} {} â•‘ {} {} {} â•‘ {} {} {} â•‘".format(*self.board[l * 9 : l * 9 + 9]) for l in range(6, 9) ) strings = [ header, top_border, top_boxes, mid_border, mid_boxes, mid_border, bot_boxes, bot_border, ] return "\n".join(strings) @staticmethod def hasdup(d): """ Args: d: Returns: """ for i in range(81): if len(d[i]) == 1: for n in Sodoku.neighbors(i): if d[n] == d[i]: return True return False def get_oneline_str(self): """ Returns: """ return self.board @staticmethod def neighbors(index, size=9): """ Args: index: size: Returns: """ return { ni for ni in chain( Sodoku.irow(index // size), Sodoku.icolumn(index % size), Sodoku.box_box(index), ) } - {index} @staticmethod def irow(n, bsize=9): """ Args: n: bsize: Returns: """ return {i for i in range(n * bsize, n * bsize + bsize)} @staticmethod def icolumn(n, bsize=9): """ Args: n: bsize: Returns: """ return {i for i in range(n, bsize ** 2, bsize)} @staticmethod def ibox(box_r, box_c, bsize=9): """ Args: box_r: box_c: bsize: Returns: """ return { i * bsize + j for i in range((box_r * 3), (box_r * 3) + 3) for j in range((box_c * 3), (box_c * 3) + 3) } @staticmethod @cash_it def box_box(index): """ Args: index: bsize: Returns: """ for box_r in range(3): for box_c in range(3): box = Sodoku.ibox(box_r, box_c) if index in box: return box PK!;>¸¥œœpupy/sodoku.pyi# -*- coding: utf-8 -*- # ~ Jesse K. Rubin ~ Pretty Useful Python from typing import Any from typing import Optional class SodokuError(ValueError): message: str = ... def __init__( self, message: str, row: Optional[int] = ..., col: Optional[int] = ... ) -> None: ... class Sodoku: is_solved: bool = ... board: Any = ... def __init__(self, board: Any) -> None: ... def solve(self) -> None: ... def euler_096_three_digit_number(self): ... @staticmethod def first_unknown(d: Any): ... @staticmethod def unsolvable(rcbd: Any): ... @staticmethod def check_unsolvable(d: Any): ... @staticmethod def update_dictionary(d: Any): ... @staticmethod def reduce_dictionary(d: Any): ... @staticmethod def hasdup(d: Any): ... def get_oneline_str(self): ... @staticmethod def neighbors(index: Any, size: int = ...): ... @staticmethod def irow(n: Any, bsize: int = ...): ... @staticmethod def icolumn(n: Any, bsize: int = ...): ... @staticmethod def ibox(box_r: Any, box_c: Any, bsize: int = ...): ... @staticmethod def box_box(index: Any, bsize: int = ...): ... PK!®ÿ³¨Â pupy/ui.py# -*- coding: utf-8 -*- # ~ Jesse K. Rubin ~ Pretty Useful Python from sys import stdout def yesno(question, default="yes"): """Ask a yes/no question via raw_input() and return their answer. "question" is a string that is presented to the user. "default" is the presumed answer if the user just hits . It must be "yes" (the default), "no" or None (meaning an answer is required of the user). The "answer" return value is True for "yes" or False for "no". """ valid = { "yes": True, "y" : True, "ye" : True, "no" : False, "n" : False } if default is None: prompt = " [y/n] " elif default == "yes": prompt = " [Y/n] " elif default == "no": prompt = " [y/N] " else: raise ValueError("invalid default answer: '%s'" % default) while True: out_string = question + prompt n_dashes = ( len(question) if "\n" not in question else len(question.split("\n")[0]) ) out_string = "{}\n{}".format("_" * n_dashes, out_string) stdout.write(out_string) choice = input().lower() if default is not None and choice == "": return valid[default] elif choice in valid: return valid[choice] else: stdout.write( "Please respond with 'yes' or 'no' " "(or 'y' or 'n').\n" ) PK!à1Ü/  pupy/utils.py# -*- coding: utf-8 -*- # ~ Jesse K. Rubin ~ Pretty Useful Python from os import path, stat from datetime import datetime def fmt_bytes(num): """ this function will convert bytes to MB.... GB... etc .. doctest:: python >>> fmt_bytes(100) '100.0 bytes' >>> fmt_bytes(1000) '1000.0 bytes' >>> fmt_bytes(10000) '9.8 KB' >>> fmt_bytes(100000) '97.7 KB' >>> fmt_bytes(1000000) '976.6 KB' >>> fmt_bytes(10000000) '9.5 MB' >>> fmt_bytes(100000000) '95.4 MB' >>> fmt_bytes(1000000000) '953.7 MB' >>> fmt_bytes(10000000000) '9.3 GB' >>> fmt_bytes(100000000000) '93.1 GB' >>> fmt_bytes(1000000000000) '931.3 GB' >>> fmt_bytes(10000000000000) '9.1 TB' >>> fmt_bytes(100000000000000) '90.9 TB' """ for x in ["bytes", "KB", "MB", "GB", "TB"]: if num < 1024.0: return "%3.1f %s" % (num, x) num /= 1024.0 def fmt_file_size(filepath): """ this function will return the file size """ if path.isfile(filepath): file_info = stat(filepath) return fmt_bytes(file_info.st_size) def fmt_seconds(t1, t2=None): """Formats time string Formats t1 if t2 is None as a string; Calculates the time and formats the time t2-t1 if t2 is not None. :param t1: time 1/initial in seconds :type t1: double :param t2: time 2 (Default value = None) :type t2: None or double :returns: formated string of the t1 - t2 or t1 :rtype: str """ if t2 is not None: return fmt_seconds((t2 - t1)) elif t1 == 0.0: return "0 sec" elif t1 >= 1: return "%.3f sec" % t1 elif 1 > t1 >= 0.001: return "%.3f ms" % ((10 ** 3) * t1) elif 0.001 > t1 >= 0.000001: return "%.3f μs" % ((10 ** 6) * t1) elif 0.000001 > t1 >= 0.000000001: return "%.3f ns" % ((10 ** 9) * t1) else: return fmt_seconds((t2 - t1)) def path2name(path_str): """Get the parent-directory for a file or directory path as a string :param somepath: path as a string which ya would like the parent of :return: The parent directory as a string .. doctest:: python >>> from os import getcwd >>> path2name(getcwd()) in getcwd() True """ return path.split(path.abspath(path_str))[-1] def parent_dirpath(fdpath): """ :param fdpath: file/dir-path as as string :return: .. doctest:: python >>> from os import path >>> parent_dirpath(path.abspath(__file__)) in path.abspath(__file__) True """ return path.split(fdpath)[0] def timestamp(ts=None): """Time stamp string w/ format yyyymmdd-HHMMSS :return: timestamp string .. doctest:: python >>> from datetime import datetime >>> timestamp(1551111111.111111) '20190225-161151' >>> datetime.now().strftime("%Y%m%d-%H%M%S") == timestamp() True """ if ts is None: return datetime.now().strftime("%Y%m%d-%H%M%S") elif isinstance(ts, float): return datetime.fromtimestamp(ts).strftime("%Y%m%d-%H%M%S") elif isinstance(ts, datetime): return ts.strftime("%Y%m%d-%H%M%S") PK!çDpUpupy/utils.pyi# -*- coding: utf-8 -*- # ~ Jesse K. Rubin ~ Pretty Useful Python def parent_dirpath(fdpath: str): ... def fmt_seconds(t1, t2=None): ... def fmt_bytes(num: int): ... def fmt_file_size(filepath: str): ... def timestamp(ts): ... def path2name(path_str: str) -> str: ... PK!G.YÌÌ pupy/werd.py# -*- coding: utf-8 -*- # ~ Jesse K. Rubin ~ Pretty Useful Python """ String Methods """ def binary_string(number): """Number to binary string :param number: some number (an integer) to turn into a binary string :return: Some string which is the binary string .. doctest:: python >>> binary_string(200) '11001000' >>> binary_string(10) '1010' """ return bin(number)[2:] def string_score(strang): """Sum of letter values where a==1 and z == 26 :param strang: string to be scored :type strang: str :returns: -> score of the string :rtype: int .. doctest:: python >>> string_score('me') 18 >>> string_score('poooood') 95 >>> string_score('gregory') 95 """ return sum((ord(character) - 96 for character in strang.lower())) def is_palindrome(string): """True a string is a palindrome; False if string is not a palindrome. :param string: .. doctest::python >>> is_palindrome("racecar") True >>> is_palindrome("greg") False """ return all( character == string[-index - 1] for index, character in enumerate(string) ) PK!åWQÄÄ pupy/werd.pyi# -*- coding: utf-8 -*- # ~ Jesse K. Rubin ~ Pretty Useful Python def binary_string(number: int) -> str: ... def string_score(string: str) -> int: ... def is_palindrome(string: str) -> bool: ... PK!ðWÎ/$$pupy-2.4.1.dist-info/LICENSEBSD 2-Clause License Copyright (c) 2018, jesse k rubin All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. PK!H|n-æWYpupy-2.4.1.dist-info/WHEEL ÊA € н§ð#Z;/í"¢ÖŸ bFF]xûzëwÜÀK;´<ÂÑç͆¦*mTíÖ»0¸Ì*Ri.´4ÅûœÑVm0[ºþÙ—ûH, JÜÍPK!HSM8îEpupy-2.4.1.dist-info/METADATA•KO1…÷ý×-q™A'i‘¸‘ë—Z>ìÃdþ½aYvwzÎùrOWø^¼¡óÒh%“†+d`£íÈ?’’mTŠ».…Cè z<Æl>Œ&Faa¹HðkóԬ߲”{Ô>«Å ™ÇTs >Ñ{ürq'õÙ+PqÙ^$÷¢·èÞ(òŒßQ:ôÅæïƒÙ´¤õ5\M+zCG'1D9ˆj:"-÷^%¦ûçQÀ¬· ˜[ëÌúwš9ÄÈÆá¸RR Xr-búeß?-êU™Û§u&Qåöém6q—MÔäPK!H¿Ú%ôˆQpupy-2.4.1.dist-info/RECORDuÕÇ’›Jཟ…š´¸ r!Ø¨È ¢‘žþNÕŒU²Ë~¯þó×éÓݳ[ÞnESŒ·ÛG·"Cà$õXZmxîi™m²”ËUêýüš…âe¬¬êÜ8¡ÂEHÈü辜:(šß—¥Þsƒa© ’ö‰s &ÙÄ„¹Ñ=‘é2‡2OÂ©ç˜ 8öíLI?móæTDã«C•Uš:ÂÝSåMq§7DÀì©Sp"m"„ ¨/'¨ƒ­mn]_ÔÉ›U÷výlÐG~±7L9nÕ 1Ë?GzäWkÏëø1CXá_±â5¡ö4Ñx³÷`Y®‘Mz\V8kûäÉ3vÖÙpŒëR (Â@ü ‹ªâ-Ðç´þ’ú®FžO2jìZIš#ïòŸØº–|Ñ£~"ˆDOF°ì»ñÊQÖLx<ýæ¦Â\s¶GŽ)²÷(®ñ¸»€5Ѥ’2áA±‡öÀ¢%Fòd|€—•~N°åŠaàŸÎ+O‹±GõÌè.bך«ëͲ–ËŠç<6«—ÚsÖ{FŸ²†ÐñåÔÁ˜¿7D¥Üªðf=Ùý&O¼U[S…/›ÅGó`_£V&Ž|36…¡\Œ¤Qüwæ†a {‡&çÒOµtgŠCÈAJ̀ㄙJ¨kQˆWgÈïv†`*šl¸5·ª â÷\¼×‡B<œÝ%”¸¼ªydˆ ElK kÏyf3ì>‡ƒ(JýË{LåM#°æ˜ÓzŠâ(¿dÜã È,«ÔÏîÅ€2ðtöW„F¿ÛÚ¸-Ÿï×dÝÆ;´&iyDzÁ…3„©Y^lÙn£}(ƒ˜Ïš‹b A‰Ð%ÿ`^iL]Å|Zõç©*ìÖ¢‡p´nvX†*7ÝÊ“ ÐX††1ßu=ßßæKkeM}£’6Í82ÀÃÛÕÙÊ·SÓ̉_|¾5HïÑs,ª÷žï–È;=ݶ¹ LAÝ ð²VÇmç”9¨Ú_sÍÀ=üâbAѤíOC$Ó‘^U§®{¾7’Þìðd{<ÝŠ÷&¨Díî©’ÙÆêJÙ(gGvûϳ†ýõTI2~‘Ò˜Ó†¿“œyý­??}—?ßkÖÀ 7(f2‰RõžÚ\†ý;¸—\Nä\îõ´˜‘(°ìVª°Ø[8δóŽ1Q³`ŽvÍGèvÎÉ`G’`Eùñ?PK!|8ç$$¤pupy/__init__.pyPK!£œ¥¥¤Rpupy/__main__.pyPK!öàÄMPP¤%pupy/_version.pyPK!T|‹ä$ä$¤£pupy/amazon_prime.pyPK!J‚JJ¤¹*pupy/amazon_prime.pyiPK!«.Ëww ¤6.pupy/cli.pyPK!¡zÊÈÈ ¤Ö3pupy/cli.pyiPK!†ùE4¤È4pupy/decorations.pyPK!rª6ff¤ùNpupy/decorations.pyiPK!Léëä䤑Rpupy/foreign.pyPK!`“¯Ýݤ¢mpupy/foreign.pyiPK!Š} äV=V= ¤­ppupy/maths.pyPK!çàÌ" " ¤.®pupy/maths.pyiPK!„ÂÎí¦¦¤|¹pupy/savings_n_loads.pyPK!W”Õƒ¿¿¤WÉpupy/savings_n_loads.pyiPK!½l÷*íí¤LÌpupy/sodoku.pyPK!;>¸¥œœ¤eépupy/sodoku.pyiPK!®ÿ³¨Â ¤.îpupy/ui.pyPK!à1Ü/  ¤ôpupy/utils.pyPK!çDpU¤Hpupy/utils.pyiPK!G.YÌÌ ¤†pupy/werd.pyPK!åWQÄÄ ¤|pupy/werd.pyiPK!ðWÎ/$$¤kpupy-2.4.1.dist-info/LICENSEPK!H|n-æWY€É pupy-2.4.1.dist-info/WHEELPK!HSM8îE€Xpupy-2.4.1.dist-info/METADATAPK!H¿Ú%ôˆQ€pupy-2.4.1.dist-info/RECORDPKuB