moved file, path, and, persistence related tools to a forthcoming filey push. planning to remove types. added magnitudes. minor tweaks and additions throughout
This commit is contained in:
parent
af907967d8
commit
539c3a702b
|
@ -1,129 +1,139 @@
|
|||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
pip-wheel-metadata/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
sl4ng
|
||||
=====
|
||||
|
||||
This package serves as a workflow booster for emailing, iterating, and a
|
||||
veritable smorgasboard of cases scattered in between.
|
||||
|
||||
You want persistant data but Pickle and Dill ask one too many damned
|
||||
questions? sl4ng.persistance.save sl4ng.persistance.load
|
||||
|
||||
You be writing generators so nice that you want to use them twice,
|
||||
thrice, or indefinitely? We gotchu! sl4ng.types.regenerator
|
||||
|
||||
YouΓÇÖre on the brink of the creative breakthrough of the ages but youΓÇÖll
|
||||
end it all in a fit of rage if you accidentally overwite your projects
|
||||
ever again? We gotchu! sl4ng.files.paths.namespacer
|
||||
|
||||
You want to look at an objectΓÇÖs source code but inspect.getsource makes
|
||||
a mess of rendering things in your REPL, or perhaps you want to jump
|
||||
straight into its package-folder or source-file? sl4ng.debug.getsource
|
||||
sl4ng.debug.pop
|
||||
|
||||
You want to want to see if your iterable is the codomain of a constant
|
||||
function? We gotchu sl4ng.functional.eq
|
||||
|
||||
You really like your dictionary, but itΓÇÖs got some duplicate values?
|
||||
We.. Got.. Chu! sl4ng.iteration.deduplicate
|
||||
|
||||
YouΓÇÖve read this far and think ΓÇ£Damn, son, this package looks diggity
|
||||
fresh, but some of those functions are deeply nestedΓÇ¥? We gotchu
|
||||
Everything is imported to init
|
|
@ -0,0 +1,2 @@
|
|||
psutil
|
||||
pyperclip
|
25
setup.py
25
setup.py
|
@ -3,13 +3,15 @@ from setuptools import setup, find_packages
|
|||
with open('README.md', 'r') as fob:
|
||||
long_description = fob.read()
|
||||
|
||||
with open('requirements.txt', 'r') as fob:
|
||||
requirements = fob.readlines()
|
||||
|
||||
setup(
|
||||
name='sl4ng',
|
||||
version='0.0.1',
|
||||
author='Kenneth Elisandro',
|
||||
author_email='eli2and40@tilde.club',
|
||||
url='https://tildegit.org/eli2and40/sl4ng',
|
||||
# packages=find_packages(),
|
||||
version='0.0.2',
|
||||
author='Kenneth Sabalo',
|
||||
author_email='kennethsantanasabalo@tilde.club',
|
||||
url='https://github.com/kendfss/sl4ng',
|
||||
packages=['sl4ng'],
|
||||
classifiers=[
|
||||
"Programming Language :: Python :: 3",
|
||||
|
@ -20,15 +22,6 @@ setup(
|
|||
long_description_content_type='text/markdown',
|
||||
keywords='utilities productivity',
|
||||
license='MIT',
|
||||
requires=[
|
||||
'pyperclip',
|
||||
'dill',
|
||||
'psutil',
|
||||
'send2trash',
|
||||
'tqdm',
|
||||
'filetype',
|
||||
],
|
||||
# py_modules=['sl4ng'],
|
||||
python_requires='>=3.8',
|
||||
|
||||
requires=requirements,
|
||||
python_requires='>3.9',
|
||||
)
|
|
@ -2,21 +2,22 @@ from typing import Iterable, Any, Iterator, Sequence
|
|||
import os
|
||||
|
||||
from .debug import *
|
||||
from .files import *
|
||||
from .functional import *
|
||||
# from .files import *
|
||||
# from .functional import *
|
||||
from .iteration import *
|
||||
from .maths import *
|
||||
from .persistance import *
|
||||
# from .persistence import *
|
||||
from .stats import *
|
||||
from .strings import *
|
||||
from .system import *
|
||||
from .types import *
|
||||
# from .types import *
|
||||
from .web import *
|
||||
from .magnitudes import *
|
||||
|
||||
|
||||
|
||||
|
||||
HERE,THIS = os.path.split(__file__)
|
||||
HERE, THIS = os.path.split(__file__)
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,30 +1,36 @@
|
|||
from typing import Any, Iterable
|
||||
from typing import Any, Iterable, Generator, Callable
|
||||
import sys, winsound, os, time, inspect
|
||||
|
||||
import pyperclip
|
||||
|
||||
from .types import SineWave, generator, regurge, function
|
||||
|
||||
|
||||
separator = " ".join(70*'-')
|
||||
spaceprint = lambda x: print(x, "\n{}".format(separator))
|
||||
|
||||
main = '__name__ == "__main__"'
|
||||
|
||||
mainame = main = '__name__ == "__main__"'
|
||||
|
||||
def tipo(inpt:Any=type(lambda:0), keep_module:bool=False) -> str:
|
||||
def nome(inpt):
|
||||
"""
|
||||
Towards a tool that can get the name of a variable
|
||||
"""
|
||||
for k, v in locals().items():
|
||||
if v == inpt:
|
||||
return k
|
||||
|
||||
def tipo(inpt, keep_module:bool=True) -> str:
|
||||
"""
|
||||
Return the name of an object's type
|
||||
Dependencies: None
|
||||
In: object
|
||||
Out: str
|
||||
params:
|
||||
keep_module
|
||||
If set to False, the name of the class will be returned without any explicit reference to package nesting
|
||||
"""
|
||||
if isinstance(inpt, type):
|
||||
inpt = str(inpt)
|
||||
if keep_module:
|
||||
return str(type(inpt)).split("'")[1]
|
||||
return str(type(inpt)).split("'")[1].split('.')[-1]
|
||||
|
||||
|
||||
def beeper(inpt:int) -> SineWave:
|
||||
def beeper(inpt:int):
|
||||
"""Produces a series of beeps of increasing frequency
|
||||
Dependencies: Beep(a function from the winsound module)
|
||||
In: (number of beeps desired)
|
||||
|
@ -90,6 +96,7 @@ def modir(module:type(os), copy:bool=True) -> str:
|
|||
def printer(iterable:Iterable, indent:int=0) -> None:
|
||||
"""
|
||||
Prints each element of an iterable on a new line, features an optional indent argument
|
||||
ultimately replaced by "show"
|
||||
"""
|
||||
assert isinstance(indent, int), "Indentation level must be a positive integer"
|
||||
for i in iterable:
|
||||
|
@ -137,33 +144,26 @@ def aspectRatio(x:int, y:int, w:int=None, h:int=None) -> float:
|
|||
return w/h==x/y if w and h else (w, (w*(y/x))) if w else ((h*(x/y)), h) if h else x/y
|
||||
|
||||
|
||||
def show(iterable:Iterable[Any], indentation:int=0, enum:bool=False, first:int=1, indentor:str='\t', tail=True, head=False, file=sys.stdout, sep:str='') -> None:
|
||||
def show(iterable:Iterable[Any], indentation:int=0, enum:bool=False, first:int=1, indentor:str='\t', tail=True, head=True, file=sys.stdout, sep:str='') -> Generator:
|
||||
"""
|
||||
Print each element of an array.
|
||||
Print each element of an iterable.
|
||||
>>> show(zip('abc','123'))
|
||||
('a', '1')
|
||||
('b', '2')
|
||||
('c', '3')
|
||||
|
||||
|
||||
>>>
|
||||
"""
|
||||
# consumable = regurge(iterable)
|
||||
consumable = iter(regurge(iterable))
|
||||
if (wasstr:=isinstance(file, str)):
|
||||
file = open(file)
|
||||
print('\n', file=file) if head else None
|
||||
for i, j in enumerate(consumable, first):
|
||||
for i, j in enumerate(iterable, first):
|
||||
print(
|
||||
(
|
||||
f"{indentation*indentor}{j}",
|
||||
f"{indentation*indentor}{i}\t{j}"
|
||||
f"{indentation * indentor}{j}",
|
||||
f"{indentation * indentor}{i}\t{j}"
|
||||
)[enum],
|
||||
sep=sep,
|
||||
file=file
|
||||
)
|
||||
# if sep:
|
||||
# print(sep)
|
||||
print('\n', file=file) if tail else None
|
||||
if wasstr:
|
||||
file.close()
|
||||
|
@ -221,8 +221,9 @@ def getsource(obj:Any, *args, copy:bool=False, **kwargs) -> str:
|
|||
"""
|
||||
text = inspect.getsource(obj).splitlines()
|
||||
if copy:
|
||||
pyperclip.copy('\n'.join(text))
|
||||
return
|
||||
text = '\n'.join(text)
|
||||
pyperclip.copy(text)
|
||||
return text
|
||||
show(text, *args, **kwargs)
|
||||
|
||||
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
from .paths import *
|
||||
from .operations import *
|
||||
from .tasks import *
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pass
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,57 +0,0 @@
|
|||
import os, subprocess
|
||||
|
||||
from send2trash import send2trash
|
||||
from tqdm import tqdm
|
||||
|
||||
from .paths import nameUpdater
|
||||
|
||||
|
||||
def convert(file:str, format:str='.wav', bitRate:int=450, delete:bool=False, options:str='') -> str:
|
||||
"""
|
||||
Convert an audio file
|
||||
"""
|
||||
trash = discard
|
||||
os.chdir(os.path.split(file)[0])
|
||||
|
||||
_title = lambda file: file.split(os.sep)[-1].split('.')[0]
|
||||
_new = lambda file, format: nameUpdater(_title(file)+format)
|
||||
_name = lambda file: file.split(os.sep)[-1]
|
||||
format = '.' + format if '.' != format[0] else format
|
||||
|
||||
name = _title(file)
|
||||
new = _new(file, format)
|
||||
cmd = f'ffmpeg -y -i "{file}" -ab {bitRate*1000} "{new}"' if bitRate != 0 else f'ffmpeg {options} -y -i "{file}" "{new}"'
|
||||
announcement = f"Converting:\n\t{file} --> {new}\n\t{cmd=}"
|
||||
print(announcement)
|
||||
subprocess.run(cmd)
|
||||
print('Conversion is complete')
|
||||
if delete:
|
||||
trash(file)
|
||||
print(f'Deletion is complete\n\t{new}\n\n\n')
|
||||
return new
|
||||
|
||||
|
||||
def move(file:str, destination:str) -> None:
|
||||
"""
|
||||
Move a file to a given directory
|
||||
"""
|
||||
nmxt = os.path.split(file)[-1]
|
||||
ext = os.path.splitext(nmxt)[-1].replace('.', '')
|
||||
with open(file, 'rb') as src:
|
||||
src = tuple(i for i in src)
|
||||
with open(destination, 'wb') as dst:
|
||||
print(f'moving {nmxt.split(".")[0]} to the "{ext}" directory')
|
||||
for datum in tqdm(src):
|
||||
dst.write(datum)
|
||||
|
||||
|
||||
def discard(path:str, recycle:bool=True) -> None:
|
||||
"""
|
||||
Remove an address from the file-system
|
||||
"""
|
||||
fb = (os.remove, send2trash)
|
||||
first, backup = fb if not recycle else fb[::-1]
|
||||
try:
|
||||
first(path)
|
||||
except PermissionError:
|
||||
backup(path)
|
|
@ -1,151 +0,0 @@
|
|||
from typing import Iterable
|
||||
|
||||
import os, sys, time
|
||||
|
||||
|
||||
defaultScriptDirectory = r'e:\projects\monties'
|
||||
commons = {
|
||||
'music': os.path.join(os.path.expanduser('~'), 'music', 'collection'),
|
||||
'images': os.path.join(os.path.expanduser('~'), 'pictures'),
|
||||
'pictures': os.path.join(os.path.expanduser('~'), 'pictures'),
|
||||
'pics': os.path.join(os.path.expanduser('~'), 'pictures'),
|
||||
'videos': os.path.join(os.path.expanduser('~'), 'videos'),
|
||||
'ytdls': {
|
||||
'music': {
|
||||
'singles': os.path.join(os.path.expanduser('~'), 'videos', 'ytdls', 'music', 'singles'),
|
||||
'mixes': os.path.join(os.path.expanduser('~'), 'videos', 'ytdls', 'music', 'mixes'),
|
||||
'albums': os.path.join(os.path.expanduser('~'), 'videos', 'ytdls', 'music', 'album'),
|
||||
},
|
||||
'graff': os.path.join(os.path.expanduser('~'), 'videos', 'ytdls', 'graff'),
|
||||
'bullshit': os.path.join(os.path.expanduser('~'), 'videos', 'ytdls', 'bullshitters'),
|
||||
'bull': os.path.join(os.path.expanduser('~'), 'videos', 'ytdls', 'bullshitters'),
|
||||
'code': os.path.join(os.path.expanduser('~'), 'videos', 'ytdls', 'cscode'),
|
||||
'cs': os.path.join(os.path.expanduser('~'), 'videos', 'ytdls', 'cscode'),
|
||||
'maths': os.path.join(os.path.expanduser('~'), 'videos', 'ytdls', 'maths'),
|
||||
'math': os.path.join(os.path.expanduser('~'), 'videos', 'ytdls', 'maths'),
|
||||
'movies': os.path.join(os.path.expanduser('~'), 'videos', 'ytdls', 'movies'),
|
||||
'other': os.path.join(os.path.expanduser('~'), 'videos', 'ytdls', 'other'),
|
||||
'physics': os.path.join(os.path.expanduser('~'), 'videos', 'ytdls', 'physics'),
|
||||
'phys': os.path.join(os.path.expanduser('~'), 'videos', 'ytdls', 'physics'),
|
||||
'politics': os.path.join(os.path.expanduser('~'), 'videos', 'ytdls', 'politics'),
|
||||
'pol': os.path.join(os.path.expanduser('~'), 'videos', 'ytdls', 'politics'),
|
||||
},
|
||||
'documents': os.path.join(os.path.expanduser('~'), 'documents'),
|
||||
'docs': os.path.join(os.path.expanduser('~'), 'documents'),
|
||||
'downloads': os.path.join(os.path.expanduser('~'), 'downloads'),
|
||||
'desktop': os.path.join(os.path.expanduser('~'), 'desktop'),
|
||||
'books': os.path.join(os.path.expanduser('~'), 'documents', 'bookes'),
|
||||
'monties': os.path.join(defaultScriptDirectory, str(time.localtime()[0])),
|
||||
'scripts': defaultScriptDirectory,
|
||||
'demos': os.path.join(defaultScriptDirectory, 'demos'),
|
||||
'site': os.path.join(sys.exec_prefix, 'lib', 'site-packages'),
|
||||
'home': os.path.expanduser('~'),
|
||||
'user': os.path.expanduser('~'),
|
||||
'root': os.path.expanduser('~'),
|
||||
'~': os.path.expanduser('~'),
|
||||
}
|
||||
os.makedirs(commons['monties'], exist_ok=True)
|
||||
|
||||
|
||||
def delevel(path:str, steps:int=1) -> str:
|
||||
"""
|
||||
This will climb the given path tree by the given number of steps.
|
||||
No matter how large the number of steps, it will stop as soon as it reaches the root.
|
||||
Probably needs revision for paths on systems which hide the root drive.
|
||||
example
|
||||
>>> for i in range(4):print(delevel(r'c:/users/admin',i))
|
||||
c:/users/admin
|
||||
c:/users
|
||||
c:/
|
||||
c:/
|
||||
dependencies: os.sep
|
||||
"""
|
||||
while steps and (len(path.split(os.sep))-1):
|
||||
path = os.sep.join((path.split(os.sep)[:-1]))
|
||||
steps -= 1
|
||||
return path if not path.endswith(':') else path+os.sep
|
||||
|
||||
|
||||
def namespacer(path:str, sep:str='_', start:int=2) -> str:
|
||||
"""
|
||||
Returns a unique version of a given string by appending an integer
|
||||
|
||||
example:
|
||||
tree:
|
||||
/folder
|
||||
/file.ext
|
||||
/file_2.ext
|
||||
|
||||
>>> nameSpacer('file', sep='-', start=2)
|
||||
file-2.ext
|
||||
>>> nameSpacer('file', sep='_', start=2)
|
||||
file_3.ext
|
||||
>>> nameSpacer('file', sep='_', start=0)
|
||||
file_0.ext
|
||||
"""
|
||||
id = start
|
||||
oldPath = path[:]
|
||||
while os.path.exists(path): ##for general use
|
||||
newPath = list(os.path.splitext(path))
|
||||
if sep in newPath[0]:
|
||||
if newPath[0].split(sep)[-1].isnumeric():
|
||||
# print('case1a')
|
||||
id = newPath[0].split(sep)[-1]
|
||||
newPath[0] = newPath[0].replace(f'{sep}{id}', f'{sep}{str(int(id)+1)}')
|
||||
path = ''.join(newPath)
|
||||
else:
|
||||
# print('case1b')
|
||||
newPath[0] += f'{sep}{id}'
|
||||
path = ''.join(newPath)
|
||||
id += 1
|
||||
else:
|
||||
# print('case2')
|
||||
newPath[0] += f'{sep}{id}'
|
||||
path = ''.join(newPath)
|
||||
id += 1
|
||||
return path
|
||||
nameUpdater = nameSpacer = name_spacer = namespacer
|
||||
|
||||
|
||||
def mcd(args:Iterable[str], go_back:bool=False, recursive:bool=True, overwrite:bool=False) -> str:
|
||||
"""
|
||||
recursively create and enter directories.
|
||||
|
||||
go_back:
|
||||
if set to True, the process will return to the starting directory
|
||||
recursive:
|
||||
if set to false, all directories will be created in the starting directory
|
||||
|
||||
eg
|
||||
each of the following calls create the following tree:
|
||||
dir-1
|
||||
dir0: starting directory
|
||||
dir1
|
||||
dir2
|
||||
dir3
|
||||
dir4
|
||||
dir5
|
||||
|
||||
>>> mcd('dir1 dir2 .. dir3 .. .. dir4 .. .. dir5'.split())
|
||||
>>> mcd('dir1/dir2 ../dir3 ../../dir4 ../../dir5'.split())
|
||||
"""
|
||||
home = os.getcwd()
|
||||
for arg in args:
|
||||
arg = nameSpacer(arg) if arg!='..' and not overwrite else arg
|
||||
os.makedirs(arg, exist_ok=True)
|
||||
os.chdir(arg if recursive else home)
|
||||
last_stop = home if go_back else os.getcwd()
|
||||
os.chdir(last_stop)
|
||||
return last_stop
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
here, this = os.path.split(__file__)
|
||||
this_2 = nameSpacer(this)
|
||||
if not os.path.exists(this_2):
|
||||
with open(this_2, 'x') as f:
|
||||
print(nameUpdater(this, '-', 2))
|
||||
print(nameUpdater(this, '_', 2))
|
||||
print(nameUpdater(this, '_', 0))
|
||||
os.remove(this_2)
|
||||
# [*map(os.remove, (this, this_2))]
|
|
@ -1,185 +0,0 @@
|
|||
from typing import Sequence, Iterable
|
||||
import re, os, subprocess
|
||||
|
||||
import filetype as ft
|
||||
|
||||
from ..types import generator
|
||||
from ..iteration import shuffle
|
||||
|
||||
|
||||
def search(keyword:str, directory:str, extension:str='', walk=True, **kwargs) -> generator:
|
||||
"""
|
||||
Find files matching the given keyword within a directory
|
||||
"""
|
||||
if re.search('\s', keyword):
|
||||
keyword = map(lambda x: x.strip(), keyword.split())
|
||||
if not isinstance(keyword, str):
|
||||
keyword = '|'.join(map(re.escape, keyword))
|
||||
else:
|
||||
keyword = re.escape(keyword)
|
||||
return filter(
|
||||
lambda file:re.search(keyword, file.split(os.sep)[-1], re.I),
|
||||
gather(directory, ext=extension, names=False, walk=walk, **kwargs)
|
||||
)
|
||||
|
||||
|
||||
def sortbysize(files:Iterable[str]=None) -> list:
|
||||
"""
|
||||
Sort a collection of file paths by the size of the corresponding files (largest to smallest)
|
||||
"""
|
||||
files = [os.getcwd(), list(files)][bool(files)]
|
||||
size = lambda file: os.stat(file).st_size
|
||||
out = []
|
||||
while len(files)>0:
|
||||
sizes = set(size(file) for file in files)
|
||||
for file in files:
|
||||
if size(file) == max(sizes):
|
||||
out.append(file)
|
||||
files.remove(file)
|
||||
return out
|
||||
|
||||
|
||||
def gather(folder:str=None, names:bool=False, walk:bool=True, ext:str=None) -> generator:
|
||||
"""
|
||||
Generate an iterable of the files rooted in a given folder
|
||||
"""
|
||||
folder = [os.getcwd(), folder][bool(folder)]
|
||||
if walk:
|
||||
if ext:
|
||||
ext = ext.replace('.', '')
|
||||
pred = [i for i in ',`* ' if i in ext]
|
||||
pattern = '|'.join(f'\.{i}$' for i in ext.split(pred[0] if pred else None))
|
||||
pat = re.compile(pattern, re.I)
|
||||
for root, folders, files in os.walk(folder):
|
||||
for file in files:
|
||||
if os.path.isfile(p:=os.path.join(root, file)) and pat.search(file) and file!='NTUSER.DAT':
|
||||
yield file if names else p
|
||||
else:
|
||||
for root, folders, titles in os.walk(folder):
|
||||
for t in titles:
|
||||
if os.path.exists(p:=os.path.join(root, t)):
|
||||
yield t if names else p
|
||||
else:
|
||||
if ext:
|
||||
ext = ext.replace('.', '')
|
||||
pred = [i for i in ',`* ' if i in ext]
|
||||
pattern = '|'.join(f'\.{i}$' for i in ext.split(pred[0] if pred else None))
|
||||
pat = re.compile(pattern, re.I)
|
||||
for file in os.listdir(folder):
|
||||
if os.path.isfile(p:=os.path.join(folder, file)) and pat.search(file) and file!='NTUSER.DAT':
|
||||
yield file if names else p
|
||||
else:
|
||||
for file in os.listdir(folder):
|
||||
if os.path.isfile(p:=os.path.join(folder, file)):
|
||||
yield file if names else p
|
||||
|
||||
|
||||
def straw(path:str, text:bool=True, lines:bool=False) -> [str, list]:
|
||||
"""
|
||||
Extract the text, or bytes if the keyword is set to false, from a file
|
||||
::text::
|
||||
text or bytes?
|
||||
::lines::
|
||||
split text by line or return raw?
|
||||
"""
|
||||
if os.path.isfile(path):
|
||||
mode = "rb r".split()[text]
|
||||
with open(path, mode) as f:
|
||||
return f.readlines() if lines else f.read()
|
||||
|
||||
|
||||
def unarchive(path:str, destination:str=None, app:str='rar') -> None:
|
||||
"""
|
||||
Extract an archive to a chosen destination, or one generated based on the name of the archive
|
||||
App refers to the comandlet you wish to invoke via subprocess.run
|
||||
"""
|
||||
options = {
|
||||
'tar':'-x -f',
|
||||
'rar':'e -or -r',
|
||||
'winrar':'',
|
||||
}
|
||||
if destination != None:
|
||||
os.makedirs(destination, exist_ok=True)
|
||||
os.chdir(destination)
|
||||
subprocess.run(f'{app} {options[app]} "{path}" ')
|
||||
else:
|
||||
destination = os.path.splitext(path)[0]
|
||||
src = os.path.join('.', os.path.split(path)[1])
|
||||
print(destination, path, src)
|
||||
os.makedirs(destination, exist_ok=True)
|
||||
os.chdir(destination)
|
||||
subprocess.run(f'{app} {options[app]} "{src}" ')
|
||||
|
||||
extractRar = unarchive
|
||||
|
||||
|
||||
def empty(path:str, make:bool=False) -> bool:
|
||||
"""
|
||||
Check if a given file or folder is empty or not with the option to create it if it doesn't exit
|
||||
"""
|
||||
if os.path.exists(path):
|
||||
if os.path.isfile(path):
|
||||
with open(path, 'rb') as f:
|
||||
return not bool(len(tuple(i for i in f)))
|
||||
elif os.path.isdir(path):
|
||||
return not bool(len(os.listdir(file)))
|
||||
elif make:
|
||||
if os.path.splitext(path)[-1]:
|
||||
x = open(path, 'x')
|
||||
x.close()
|
||||
else:
|
||||
os.makedirs(path, exist_ok=True)
|
||||
return True
|
||||
|
||||
|
||||
def ffplay(files:Sequence[str], hide:bool=True, fullscreen:bool=True, loop:bool=True, quiet:bool=True, randomize:bool=True, silent:bool=False) -> None:
|
||||
"""
|
||||
Play a collection of files using ffmpeg's "ffplay" cli
|
||||
|
||||
If entering files as a string, separate each path by an asterisk (*), othewise feel free to use any iterator
|
||||
-loop {f"-loop {loop}" if loop else ""}
|
||||
"""
|
||||
|
||||
# fullscreen = False if hide else fullscreen
|
||||
namext = lambda file: os.path.split(file)[1]
|
||||
nome = lambda file: os.path.splitext(namext(file))[0]
|
||||
ext = lambda file: os.path.splitext(file)[1]
|
||||
isvid = lambda file: ft.match(file) in ft.video_matchers
|
||||
vidtitle = lambda vid: '-'.join(i.strip() for i in vid.split('-')[:-1])
|
||||
albumtrack = lambda file: bool(re.search(f'\d+\s.+{ext(file)}', file, re.I))
|
||||
attitle = lambda file: ' '.join(i.strip() for i in nome(file).split(' ')[1:])
|
||||
aov = lambda file: albumtrack(file) or isvid(file)
|
||||
title = lambda file: ''.join(i for i in os.path.splitext(namext(file)[1])[0] if i not in '0123456789').strip()
|
||||
windowtitle = lambda file: [namext(file), [attitle(file), vidtitle(file)][isvid(file)]][aov(file)]
|
||||
play = lambda file: subprocess.run(f'ffplay {("", "-nodisp")[hide]} -window_title "{windowtitle(file)}" -autoexit {"-fs" if fullscreen else ""} {"-v error" if quiet else ""} "{file}"')
|
||||
files = files.split('*') if isinstance(files,str) else files
|
||||
if loop:
|
||||
while (1 if loop==True else loop+1):
|
||||
files = shuffle(files) if randomize else files
|
||||
for i,f in enumerate(files, 1):
|
||||
if os.path.isdir(f):
|
||||
fls = [os.path.join(f, i) for i in gather(f, names=False)]
|
||||
for j,file in enumerate(fls, 1):
|
||||
name = os.path.split(file)[1]
|
||||
print(f'{j} of {len(fls)}:\t{name}') if not silent else None
|
||||
ffplay(file, hide, fullscreen, False, quiet, randomize, True)
|
||||
else:
|
||||
folder,name = os.path.split(f)
|
||||
print(f'{i} of {len(files)}:\t{name}') if not silent else None
|
||||
play(f)
|
||||
loop -= 1
|
||||
else:
|
||||
files = shuffle(files) if randomize else files
|
||||
for i, f in enumerate(files, 1):
|
||||
if os.path.isdir(f):
|
||||
fls = [os.path.join(f, i) for i in gather(f, names=False)]
|
||||
for j, file in enumerate(fls, 1):
|
||||
name = os.path.split(file)[1]
|
||||
print(f'{j} of {len(fls)}:\t{name}') if not silent else None
|
||||
ffplay(file, hide, fullscreen, False, quiet, randomize, True)
|
||||
else:
|
||||
print(f'{i} of {len(files)}:\t{title(f)}') if not silent else None
|
||||
play(f)
|
||||
|
||||
|
||||
size = lambda file: os.stat(file).st_size
|
|
@ -1,19 +1,211 @@
|
|||
# Statisticals checked against: http://www.alcula.com/calculators/statistics/
|
||||
|
||||
from itertools import tee, islice, chain, combinations
|
||||
from typing import Iterable, Any, Sequence, List, Callable
|
||||
from copy import deepcopy
|
||||
from itertools import tee, _tee, islice, chain, combinations
|
||||
from numbers import Number, Real
|
||||
from typing import Iterable, Any, Sequence, List, Callable, Generator, Hashable
|
||||
from math import log
|
||||
import random
|
||||
import random, time
|
||||
|
||||
from .types import generator, regurge, regenerator
|
||||
from .maths import binomial
|
||||
from .functional import eq
|
||||
|
||||
def xrange(stop:complex, start:complex=0, step:complex=1, reverse:bool=False) -> generator:
|
||||
|
||||
class __regen:
|
||||
"""
|
||||
A namespace for utilities used in creating regenerators
|
||||
"""
|
||||
@staticmethod
|
||||
def choose(iterable:Iterable[Any], *indices:Sequence[int]) -> Generator:
|
||||
"""
|
||||
Yield specific elements from an iterable by index:
|
||||
>>> [*choose(range(1, 10), 0, 3)]
|
||||
[1, 4]
|
||||
>>> [*choose(range(1, 10), (0, 3))]
|
||||
[1, 4]
|
||||
|
||||
"""
|
||||
indices = [*flatten(indices)]
|
||||
yielded = []
|
||||
for i, e in enumerate(iterable):
|
||||
if i in indices:
|
||||
yield e
|
||||
yielded.append(e)
|
||||
if len(yielded) == len(indices):
|
||||
break
|
||||
@staticmethod
|
||||
def tipo(inpt:Any=type(lambda:0), keep_module:bool=False) -> str:
|
||||
"""
|
||||
Return the name of an object's type
|
||||
Dependencies: None
|
||||
In: object
|
||||
Out: str
|
||||
"""
|
||||
if keep_module:
|
||||
return str(type(inpt)).split("'")[1]
|
||||
return str(type(inpt)).split("'")[1].split('.')[-1]
|
||||
@staticmethod
|
||||
def flatten(iterable:Iterable) -> Generator:
|
||||
"""
|
||||
Flatten a 2d iterable
|
||||
Example:
|
||||
>>> list(flatten([[1, 2], [3, 4]]))
|
||||
[1, 2, 3, 4]
|
||||
based on:
|
||||
https://pythonprinciples.com/challenges/Flatten-a-list/
|
||||
"""
|
||||
for i in iterable:
|
||||
if hasattr(i, '__iter__') or hasattr(i, '__next__'):
|
||||
yield from i
|
||||
else:
|
||||
yield i
|
||||
|
||||
class regenerator:
|
||||
"""
|
||||
A self-replenishing (or non-consumable) iterator. All methods whose return value is iterable return regenerators
|
||||
:args & kwargs:
|
||||
Any arguments needed to initialize the Generator-type/function. Will not be used unless hasattr(iterable, '__call__') and iterable is not a regenerator.
|
||||
eg:
|
||||
>>> x = regurge(i for i in range(2))
|
||||
>>> [*x]
|
||||
[0, 1]
|
||||
>>> bytes(x)
|
||||
b'\x00\x01'
|
||||
>>> [*x]
|
||||
[0, 1]
|
||||
"""
|
||||
def __init__(self, iterable, *args, **kwargs):
|
||||
if hasattr(iterable, '__call__') and not isinstance(iterable, type(self)):
|
||||
self.active, self._inert = tee(iterable(*args, **kwargs))
|
||||
else:
|
||||
self.active, self._inert = tee(iterable)
|
||||
def __next__(self):
|
||||
return next(self.active)
|
||||
def __iter__(self):
|
||||
self.active, self._inert = tee(self._inert)
|
||||
return self.active
|
||||
def __getitem__(self, index:int):
|
||||
if isinstance(index, int):
|
||||
index = index if index >= 0 else len(self) + index
|
||||
for i, e in enumerate(self):
|
||||
if i == index:
|
||||
return e
|
||||
else:
|
||||
raise
|
||||
raise IndexError(f'{__regen.tipo(self)} contains fewer than {index} elements')
|
||||
def __call__(self, *indices:Iterable[int]):
|
||||
"""
|
||||
Access particular indices of the underlying iterator
|
||||
eg
|
||||
>>> x = regen(range(3))
|
||||
>>> [*x(1)]
|
||||
[1]
|
||||
>>> [*x(1,2)]
|
||||
[1, 2]
|
||||
>>> [*x(1,2,3)]
|
||||
[1, 2]
|
||||
"""
|
||||
return type(self)(__regen.choose(self.active, *__regen.flatten(indices)))
|
||||
def __bool__(self):
|
||||
"""
|
||||
Returns True iff. the underlying iterator is non-empty. False otherwise.
|
||||
"""
|
||||
tmp, self._inert = tee(self._inert)
|
||||
try:
|
||||
next(tmp)
|
||||
return True
|
||||
except StopIteration:
|
||||
return False
|
||||
def __matmul__(self, other:Iterable):
|
||||
if hasattr(other, '__iter__'):
|
||||
return type(self)(product(self, other))
|
||||
raise TypeError(f'Matrix-multiplication is not defined between {__regen.tipo(self, True)} and "{__regen.tipo(other, True)}"-type. It must have an "__iter__" or "__index__" method.')
|
||||
def __len__(self):
|
||||
return sum(1 for i in self)
|
||||
def __add__(self, other:Iterable):
|
||||
"""
|
||||
Create a new regenerator whose first elements come from self and remaining element(s) is/come-from other.
|
||||
If other is not iterable it shall be added as the last element.
|
||||
If you want to add an iterable as a single element, use self.append
|
||||
eg
|
||||
>>> x = regenerator(range(2))
|
||||
>>> y = x + 10
|
||||
>>> [*y]
|
||||
[0, 1, 10]
|
||||
>>> y += x
|
||||
>>> [*y]
|
||||
[0, 1, 10, 0, 1]
|
||||
"""
|
||||
other = other if hasattr(other, '__iter__') else [other]
|
||||
return type(self)([*self, *other])
|
||||
def __radd__(self, other:Iterable):
|
||||
"""
|
||||
Swap the order of __add__
|
||||
"""
|
||||
other = other if hasattr(other, '__iter__') else [other]
|
||||
return type(self)(chain(other, self))
|
||||
def __mul__(self, value:int):
|
||||
"""
|
||||
Replicate the behaviour of multiplying lists by integers
|
||||
"""
|
||||
if hasattr(value, '__int__'):
|
||||
return type(self)(chain.from_iterable(self for i in range(int(value))))
|
||||
raise TypeError(f'Multiplication is not defined for "{__regen.tipo(other, True)}". It must have an "__int__"')
|
||||
def __rmul__(self, value:int):
|
||||
"""Commutative multiplication"""
|
||||
return self.__mul__(value)
|
||||
def __pow__(self, value:int):
|
||||
"""
|
||||
value-dimensional Cartesian product of self with itself
|
||||
"""
|
||||
if hasattr(value, '__int__'):
|
||||
return type(self)(product(self, repeat=int(value)))
|
||||
raise TypeError(f'Exponentiation is not defined for {type(other)}. It must have an "__int__" method.')
|
||||
|
||||
def count(self, value:Any):
|
||||
"""
|
||||
how many copies of value?
|
||||
"""
|
||||
return sum(1 for i in self if i==value)
|
||||
def scale(self, value:Any):
|
||||
"""
|
||||
Multiply every element of self by value. Done in place.
|
||||
"""
|
||||
self._inert = (i*value for i in self)
|
||||
return self
|
||||
def boost(self, value:Any):
|
||||
"""
|
||||
Add value to every element of self. Done in place.
|
||||
"""
|
||||
self._inert = (i+value for i in self)
|
||||
return self
|
||||
def indices(self, value:Any, shift:int=0):
|
||||
"""
|
||||
Similar to a list's index method, except that it returns every index whose element is a match
|
||||
Works by calling enumerate and selecting at equivalent elements.
|
||||
:shift:
|
||||
kwarg for the "enumerate" call.
|
||||
"""
|
||||
return type(self)(i for i, e in enumerate(self, shift) if e == value)
|
||||
def append(self, value:Any):
|
||||
"""
|
||||
add "value" as the last element of the array
|
||||
"""
|
||||
other = [value]
|
||||
self._inert = chain(self._inert, other)
|
||||
return self
|
||||
def inject(self, value:Any):
|
||||
"""
|
||||
If "value" is an iterable: its elements will be added to the end of "self"
|
||||
Otherwise: it is the same as append
|
||||
"""
|
||||
other = value if hasattr(value, '__iter__') else [value]
|
||||
self._inert = chain(self._inert, *other)
|
||||
return self
|
||||
regen = regenerator
|
||||
|
||||
|
||||
def xrange(stop:Number, start:Number=0, step:Number=1, reverse:bool=False) -> Generator:
|
||||
"""
|
||||
xrange(start, stop, step)
|
||||
An implementation of the old xrange generator
|
||||
An implementation of the old xrange Generator
|
||||
examples:
|
||||
>>> [*xrange(2)]
|
||||
[0, 1]
|
||||
|
@ -39,21 +231,20 @@ def xrange(stop:complex, start:complex=0, step:complex=1, reverse:bool=False) ->
|
|||
start += step
|
||||
|
||||
|
||||
def tight(iterable:Iterable[Any]) -> generator:
|
||||
def tight(iterable:Iterable[Any], yielded:list=None) -> Generator:
|
||||
"""
|
||||
Produce a new iterator of unique elements from a given array
|
||||
will consume a generator
|
||||
will consume a Generator
|
||||
"""
|
||||
consumable = list(regurge(iterable))
|
||||
# consumable = list(consumable)
|
||||
yielded = []
|
||||
yielded = yielded if not isinstance(yielded, type(None)) else []
|
||||
for i in iterable:
|
||||
if not i in yielded:
|
||||
yield i
|
||||
yielded.append(i)
|
||||
unique = tight
|
||||
|
||||
|
||||
def walks(iterable:Iterable[Any], length:int=2) -> generator:
|
||||
def walks(iterable:Iterable[Any], length:int=2) -> Generator:
|
||||
"""
|
||||
Break an iterable into len(iterable)-length steps of the given length, with each step's starting point one after its predecessor
|
||||
example
|
||||
|
@ -66,22 +257,21 @@ def walks(iterable:Iterable[Any], length:int=2) -> generator:
|
|||
Extended to generators with cues from more_itertools' "stagger"
|
||||
Extended to infinite generators by pedantry
|
||||
"""
|
||||
consumable = regurge(iterable)
|
||||
consumable = regenerator(iterable)
|
||||
t = tee(consumable, length)
|
||||
yield from zip(*(it if n==0 else islice(it, n, None) for n, it in enumerate(t)))
|
||||
|
||||
|
||||
def flatten(iterable:Iterable) -> generator:
|
||||
def flatten(iterable:Iterable) -> Generator:
|
||||
"""
|
||||
Flatten a 2d iterable
|
||||
Transform an N-dimensional array into a N-1-dimensional array.
|
||||
Example:
|
||||
>>> list(flatten([[1, 2], [3, 4]]))
|
||||
[1, 2, 3, 4]
|
||||
based on:
|
||||
https://pythonprinciples.com/challenges/Flatten-a-list/
|
||||
"""
|
||||
consumable = regurge(iterable)
|
||||
for i in consumable:
|
||||
for i in iterable:
|
||||
if hasattr(i, '__iter__') or hasattr(i, '__next__'):
|
||||
for j in i:
|
||||
yield j
|
||||
|
@ -89,48 +279,76 @@ def flatten(iterable:Iterable) -> generator:
|
|||
yield i
|
||||
|
||||
|
||||
def nopes(iterable:Iterable[Any], yeps:bool=False) -> generator:
|
||||
def flat(iterable:Iterable, dict_keys:bool=False, strings:bool=False) -> Generator:
|
||||
"""
|
||||
Create a completely flat version of an iterable.
|
||||
params:
|
||||
strings
|
||||
yield each character from each string if set to True
|
||||
dict_keys
|
||||
yield the keys instead of the values if set to True
|
||||
"""
|
||||
# if isinstance(iterable, fo;):
|
||||
# yield iterable
|
||||
if isinstance(iterable, str):
|
||||
yield from iterable if strings else [iterable]
|
||||
elif isinstance(iterable, dict):
|
||||
itr = iterable.keys() if dict_keys else iterable.values()
|
||||
for val in itr:
|
||||
yield from flat(val, dict_keys, strings)
|
||||
elif isinstance(iterable, (list, tuple, map, filter)):
|
||||
for val in iterable:
|
||||
if hasattr(val, '__iter__'):
|
||||
yield from flat(val, dict_keys, strings)
|
||||
else:
|
||||
yield val
|
||||
elif hasattr(iterable, '__iter__'):
|
||||
yield from iterable
|
||||
else:
|
||||
yield iterable
|
||||
|
||||
|
||||
|
||||
def nopes(iterable:Iterable[Any], yeps:bool=False) -> Generator:
|
||||
"""
|
||||
if yeps==False
|
||||
Return the indices of all false-like boolean values of an iterable
|
||||
Return indices of all true-like boolean values of an iterable
|
||||
|
||||
example
|
||||
t = (0,1,0,0,1)
|
||||
>>> t = (0, 1, 0, 0, 1)
|
||||
>>> tuple(nopes(t))
|
||||
(0,2,3)
|
||||
>>> tuple(nopes(t,True))
|
||||
(1,4)
|
||||
(0, 2, 3)
|
||||
>>> tuple(nopes(t, True))
|
||||
(1, 4)
|
||||
"""
|
||||
consumable = regurge(iterable)
|
||||
for i, j in enumerate(consumable):
|
||||
for i, j in enumerate(iterable):
|
||||
if (not j, j)[yeps]:
|
||||
yield i
|
||||
|
||||
|
||||
def pull(iterable:Iterable, stop:int, start:int=0, step:int=1) -> tuple:
|
||||
def roll(iterable:Iterable, stop:int, start:int=0, step:int=1) -> tuple:
|
||||
"""
|
||||
Return 'stop' elements from an effective cycle of the iterable, using only those whose modulus is greater than 'start'
|
||||
|
||||
Example:
|
||||
pull('boris',5)
|
||||
('b', 'o', 'r', 'i', 's')
|
||||
pull('boris',6)
|
||||
('b', 'o', 'r', 'i', 's', 'b')
|
||||
pull('boris',6,1)
|
||||
('o', 'r', 'i', 's', 'b', 'o')
|
||||
pull('boris',6,1,2)
|
||||
('r', 's', 'o')
|
||||
>>> roll('boris', 5)
|
||||
('b', 'o', 'r', 'i', 's')
|
||||
>>> roll('boris', 6)
|
||||
('b', 'o', 'r', 'i', 's', 'b')
|
||||
>>> roll('boris', 6, 1)
|
||||
('o', 'r', 'i', 's', 'b', 'o')
|
||||
>>> roll('boris', 6, 1, 2)
|
||||
('r', 's', 'o')
|
||||
"""
|
||||
consumable = regurge(iterable)
|
||||
consumable = list(consumable)
|
||||
return tuple(consumable[i%len(consumable)] for i in range(0, stop+start, step) if i>=start)
|
||||
consumable = regenerator(iterable)
|
||||
return tuple(consumable[(i + 0) % len(consumable)] for i in range(0, stop + start, step) if i >= start)
|
||||
|
||||
|
||||
def deduplicate(unhashable:Iterable) -> dict:
|
||||
"""
|
||||
Because dictionaries seem to be less hashable than lists, which are also formally unhashable
|
||||
This will consume a generator
|
||||
This will consume a Generator
|
||||
"""
|
||||
if isinstance(unhashable, dict):
|
||||
trimmed = {}
|
||||
|
@ -142,63 +360,55 @@ def deduplicate(unhashable:Iterable) -> dict:
|
|||
raise TypeError(f'Protocol for your {tipo(unhashable)} is pending')
|
||||
|
||||
|
||||
def band(iterable:Iterable) -> float:
|
||||
def band(iterable:Iterable[Real]) -> Real:
|
||||
"""
|
||||
Returns the extrema of the given iterable
|
||||
"""
|
||||
consumable = regurge(iterable)
|
||||
consumable = list(consumable)
|
||||
consumable = regenerator(iterable)
|
||||
return min(consumable), max(consumable)
|
||||
|
||||
|
||||
def bandGap(iterable:Iterable) -> float:
|
||||
def bandgap(iterable:Iterable[Real]) -> Real:
|
||||
"""
|
||||
Returns the breadth of a given iterable of elements overwhich subtraction, max, and min, are well defined
|
||||
"""
|
||||
consumable = regurge(iterable)
|
||||
consumable = list(consumable)
|
||||
consumable = regenerator(iterable)
|
||||
return max(consumable) - min(consumable)
|
||||
|
||||
|
||||
def options(iterable:Iterable) -> int:
|
||||
"""
|
||||
Returns the number of ways to choose elements from the given iterable
|
||||
This will consume a generator
|
||||
"""
|
||||
consumable = regurge(iterable)
|
||||
consumable = list(consumable)
|
||||
length = len(consumable)
|
||||
return sum(binomial(length, i) for i in range(length))
|
||||
|
||||
|
||||
def lispart(lis:Iterable, depth:int) -> list:
|
||||
"""
|
||||
Returns a collection of n*m elements as a list of m lists with n elements each
|
||||
devised by David C. Ullrich from stack exchange
|
||||
"""
|
||||
consumable = regurge(iterable)
|
||||
consumable = list(consumable)
|
||||
consumable = regenerator(consumable)
|
||||
assert(len(consumable)%depth == 0), f'The iterable cannot be factored into "{depth}" arrays'
|
||||
return [consumable[j*depth:(j+1)*depth] for j in range(int(len(consumable)/depth))]
|
||||
|
||||
|
||||
def sigma(iterable:Iterable[complex], v0:Any=0) -> complex:
|
||||
def cast(x:int, y:int, iterable:Iterable=None) -> list:
|
||||
"""
|
||||
Return a YxX matrix (y lists of length x) whose elements are from slices of the iterable
|
||||
"""
|
||||
base = regenerator(iterable) if iterable else tuple(range(x * y))
|
||||
assert x*y==len(list(tee(base)[0])), f"Iterable does not cast to (x, y) = {(x, y)}"
|
||||
# return [base[slice(i*x, x+i*x)] for i in range(y)]
|
||||
return [base[i * x: x + i * x] for i in range(y)]
|
||||
|
||||
def sigma(iterable:Iterable[Any], v0:Any=0) -> Any:
|
||||
"""
|
||||
Returns the sum of a iterable
|
||||
"""
|
||||
consumable = regurge(iterable)
|
||||
consumable = list(consumable)
|
||||
for i in consumable:
|
||||
for i in iterable:
|
||||
v0 += i
|
||||
return v0
|
||||
|
||||
|
||||
def pipe(iterable:Iterable[complex]) -> complex:
|
||||
def pipe(iterable:Iterable[Number]) -> Number:
|
||||
"""
|
||||
Returns the multiplicative product of the elements of a collection
|
||||
"""
|
||||
consumable = regurge(iterable)
|
||||
consumable = list(consumable)
|
||||
consumable = regenerator(iterable)
|
||||
if len(consumable) < 1:
|
||||
return "this list is empty"
|
||||
else:
|
||||
|
@ -208,30 +418,29 @@ def pipe(iterable:Iterable[complex]) -> complex:
|
|||
return v0
|
||||
|
||||
|
||||
def powerset(iterable:Iterable) -> regenerator:
|
||||
def powerset(iterable:Iterable[Any]) -> regenerator:
|
||||
"""
|
||||
Returns the powerset of an iterable as a list
|
||||
"""
|
||||
consumable = regenerator(iterable)
|
||||
return chain.from_iterable(combinations(consumable, r) for r in range(sum(1 for i in consumable)+1))
|
||||
return regenerator(chain.from_iterable(combinations(consumable, r) for r in range(sum(1 for i in consumable)+1)))
|
||||
|
||||
|
||||
def sample(iterable:Iterable, size:int) -> tuple:
|
||||
def sample(iterable:Iterable[Any], size:int) -> tuple:
|
||||
"""
|
||||
Obtains a random sample of any length from any iterable and returns it as a tuple
|
||||
Unlike random.sample, this may yield the same element several times.
|
||||
"""
|
||||
consumable = regurge(iterable)
|
||||
consumable = list(consumable)
|
||||
consumable = regenerator(iterable)
|
||||
choiceIndices = tuple(random.randint(0, len(consumable)-1) for i in range(size))
|
||||
return tuple(consumable[i] for i in choiceIndices)
|
||||
|
||||
|
||||
def shuffle(iterable:Iterable) -> tuple:
|
||||
def shuffle(iterable:Iterable[Any]) -> tuple:
|
||||
"""
|
||||
Given an iterable, this function returns a new tuple of its elements in a new order
|
||||
"""
|
||||
consumable = regurge(iterable)
|
||||
consumable = list(consumable)
|
||||
consumable = regenerator(iterable)
|
||||
cache = []
|
||||
pot = []
|
||||
while len(cache) < len(consumable):
|
||||
|
@ -249,13 +458,13 @@ def unzip(iterable:Iterable[Sequence[Any]]) -> List[list]:
|
|||
This is about the same as a clockwise rotation of a matrix by 90 degrees
|
||||
This will omit empty arrays
|
||||
examples
|
||||
>>> m3ta.show(unzip(range(3) for i in range(3)))
|
||||
>>> sl4ng.show(unzip(range(3) for i in range(3)))
|
||||
[0, 0, 0]
|
||||
[1, 1, 1]
|
||||
[2, 2, 2]
|
||||
|
||||
|
||||
>>> m3ta.show(unzip(range(j) for j in range(10)))
|
||||
>>> sl4ng.show(unzip(range(j) for j in range(10)))
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||
[1, 1, 1, 1, 1, 1, 1, 1]
|
||||
[2, 2, 2, 2, 2, 2, 2]
|
||||
|
@ -268,8 +477,8 @@ def unzip(iterable:Iterable[Sequence[Any]]) -> List[list]:
|
|||
|
||||
|
||||
"""
|
||||
consumable = regurge(iterable)
|
||||
str_escape = lambda string: string.replace("'", "\'")
|
||||
consumable = regenerator(iterable)
|
||||
str_escape = lambda string: string.replace("'", "\'").replace("\\", "\\\\")
|
||||
length = 0
|
||||
racks = []
|
||||
for i in consumable:
|
||||
|
@ -278,11 +487,11 @@ def unzip(iterable:Iterable[Sequence[Any]]) -> List[list]:
|
|||
exec(f"x{j} = []")
|
||||
racks.append(eval(f'x{j}'))
|
||||
length += 1
|
||||
app_elem = f"x{j}.append('{str_escape(k)}')" if isinstance(k, str) else f"x{j}.append({k})"
|
||||
app_elem = f"""x{j}.append('{(k, str_escape(k))[isinstance(k, str)]}')"""
|
||||
eval(app_elem)
|
||||
return racks
|
||||
|
||||
def diffs(iterable:Iterable[complex], flip:bool=False) -> generator:
|
||||
def diffs(iterable:Iterable[Number], flip:bool=False) -> Generator:
|
||||
"""
|
||||
Yield the difference between each element of an iterable and its predecessor
|
||||
example:
|
||||
|
@ -291,17 +500,17 @@ def diffs(iterable:Iterable[complex], flip:bool=False) -> generator:
|
|||
>>> [*diffs(range(3), True)]
|
||||
[-1, -1]
|
||||
"""
|
||||
consumable = iter(regurge(iterable))
|
||||
consumable = iter(iterable)
|
||||
last = next(consumable)
|
||||
for i in consumable:
|
||||
yield i - last if not flip else last - i
|
||||
last = i
|
||||
|
||||
def discontinuities(iterable:Iterable[complex], delta:complex=1) -> generator:
|
||||
def discontinuities(iterable:Iterable[Number], delta:Number=1) -> Generator:
|
||||
"""
|
||||
Obtain the ordinal positions of any elements who do not differ from their successors by delta
|
||||
Iterable must, at least, be a semigroupoid with subtraction
|
||||
Not generator safe.
|
||||
Not Generator safe.
|
||||
example:
|
||||
>>> [*discontinuities(range(3))]
|
||||
[]
|
||||
|
@ -315,7 +524,7 @@ def discontinuities(iterable:Iterable[complex], delta:complex=1) -> generator:
|
|||
yield i
|
||||
last = e
|
||||
|
||||
def stationaries(iterable:Iterable[complex]) -> generator:
|
||||
def stationaries(iterable:Iterable[Number]) -> Generator:
|
||||
"""
|
||||
Generate the indices of the stationary points of an iterable
|
||||
example:
|
||||
|
@ -331,7 +540,7 @@ def stationaries(iterable:Iterable[complex]) -> generator:
|
|||
yield i
|
||||
last = e
|
||||
|
||||
def discontinuous(iterable:Iterable[complex], differential:complex=1) -> generator:
|
||||
def discontinuous(iterable:Iterable[Number], differential:Number=1) -> Generator:
|
||||
"""
|
||||
Check if an additive semigroupoid has any jumps which are inequivalent to a given differential
|
||||
example:
|
||||
|
@ -352,7 +561,7 @@ def discontinuous(iterable:Iterable[complex], differential:complex=1) -> generat
|
|||
|
||||
|
||||
|
||||
def sums(iterable:Iterable[complex], flip:bool=False) -> generator:
|
||||
def sums(iterable:Iterable[Number], flip:bool=False) -> Generator:
|
||||
"""
|
||||
Yield the sum of each element of an iterable and its predecessor
|
||||
example:
|
||||
|
@ -361,13 +570,13 @@ def sums(iterable:Iterable[complex], flip:bool=False) -> generator:
|
|||
>>> [*sums('abc', True)]
|
||||
['ab', 'bc']
|
||||
"""
|
||||
consumable = iter(regurge(iterable))
|
||||
consumable = iter(iterable)
|
||||
last = next(consumable)
|
||||
for i in consumable:
|
||||
yield i + last if not flip else last + i
|
||||
last = i
|
||||
|
||||
def quots(iterable:Iterable[complex], flip:bool=False) -> generator:
|
||||
def quots(iterable:Iterable[Number], flip:bool=False) -> Generator:
|
||||
"""
|
||||
Yield the quotient of each element of an iterable by its predecessor
|
||||
example:
|
||||
|
@ -376,13 +585,13 @@ def quots(iterable:Iterable[complex], flip:bool=False) -> generator:
|
|||
>>> [*quots(range(1, 4), True)]
|
||||
[0.5, 0.6666666666666666]
|
||||
"""
|
||||
consumable = iter(regurge(iterable))
|
||||
consumable = iter(iterable)
|
||||
last = next(consumable)
|
||||
for i in consumable:
|
||||
yield i / last if not flip else last / i
|
||||
last = i
|
||||
|
||||
def prods(iterable:Iterable[complex], flip:bool=False) -> generator:
|
||||
def prods(iterable:Iterable[Number], flip:bool=False) -> Generator:
|
||||
"""
|
||||
Yield the product of each element of an iterable by its predecessor
|
||||
example:
|
||||
|
@ -391,13 +600,13 @@ def prods(iterable:Iterable[complex], flip:bool=False) -> generator:
|
|||
>>> [*prods(range(1, 4), True)]
|
||||
[2, 6]
|
||||
"""
|
||||
consumable = iter(regurge(iterable))
|
||||
consumable = iter(iterable)
|
||||
last = next(consumable)
|
||||
for i in consumable:
|
||||
yield i * last if not flip else last * i
|
||||
last = i
|
||||
|
||||
def logs(iterable:Iterable[complex], flip:bool=False) -> generator:
|
||||
def logs(iterable:Iterable[Number], flip:bool=False) -> Generator:
|
||||
"""
|
||||
Yield the log of each element of an iterable in the base of its predecessor
|
||||
example:
|
||||
|
@ -406,13 +615,13 @@ def logs(iterable:Iterable[complex], flip:bool=False) -> generator:
|
|||
>>> [*logs(range(2, 5), True)]
|
||||
[0.6309297535714574, 0.7924812503605781]
|
||||
"""
|
||||
consumable = iter(regurge(iterable))
|
||||
consumable = iter(iterable)
|
||||
last = next(consumable)
|
||||
for i in consumable:
|
||||
yield log(i, last) if not flip else log(last, i)
|
||||
last = i
|
||||
|
||||
def cumsum(iterable:Iterable[complex], first:bool=True) -> generator:
|
||||
def cumsum(iterable:Iterable[Number], first:bool=True) -> Generator:
|
||||
"""
|
||||
Yield the log of each element of an iterable in the base of its predecessor
|
||||
example:
|
||||
|
@ -421,7 +630,7 @@ def cumsum(iterable:Iterable[complex], first:bool=True) -> generator:
|
|||
>>> [*cumsum(range(4), False)]
|
||||
[1, 3, 6]
|
||||
"""
|
||||
consumable = iter(regurge(iterable))
|
||||
consumable = iter(iterable)
|
||||
last = 0
|
||||
if first:
|
||||
yield last
|
||||
|
@ -429,7 +638,7 @@ def cumsum(iterable:Iterable[complex], first:bool=True) -> generator:
|
|||
last += i
|
||||
yield last
|
||||
|
||||
def cumdif(iterable:Iterable[complex], first:bool=True) -> generator:
|
||||
def cumdif(iterable:Iterable[Number], first:bool=True) -> Generator:
|
||||
"""
|
||||
Yield the log of each element of an iterable in the base of its predecessor
|
||||
example:
|
||||
|
@ -438,7 +647,7 @@ def cumdif(iterable:Iterable[complex], first:bool=True) -> generator:
|
|||
>>> [*cumdif(range(4), False)]
|
||||
[-1, -3, -6]
|
||||
"""
|
||||
consumable = iter(regurge(iterable))
|
||||
consumable = iter(iterable)
|
||||
last = next(consumable)
|
||||
if first:
|
||||
yield last
|
||||
|
@ -446,7 +655,7 @@ def cumdif(iterable:Iterable[complex], first:bool=True) -> generator:
|
|||
last -= i
|
||||
yield last
|
||||
|
||||
def cumprod(iterable:Iterable[complex], first:bool=True) -> generator:
|
||||
def cumprod(iterable:Iterable[Number], first:bool=True) -> Generator:
|
||||
"""
|
||||
Yield the cumulative product of the elemements of an iterable
|
||||
example:
|
||||
|
@ -455,7 +664,7 @@ def cumprod(iterable:Iterable[complex], first:bool=True) -> generator:
|
|||
>>> [*cumprod(range(1, 4), False)]
|
||||
[2, 6]
|
||||
"""
|
||||
consumable = iter(regurge(iterable))
|
||||
consumable = iter(iterable)
|
||||
last = next(consumable)
|
||||
if first:
|
||||
yield last
|
||||
|
@ -463,7 +672,7 @@ def cumprod(iterable:Iterable[complex], first:bool=True) -> generator:
|
|||
last *= i
|
||||
yield last
|
||||
|
||||
def cumquot(iterable:Iterable[complex], first:bool=True) -> generator:
|
||||
def cumquot(iterable:Iterable[Number], first:bool=True) -> Generator:
|
||||
"""
|
||||
Yield the cumulative quotient of the elemements of an iterable
|
||||
example:
|
||||
|
@ -472,7 +681,7 @@ def cumquot(iterable:Iterable[complex], first:bool=True) -> generator:
|
|||
>>> [*cumquot(range(1, 4), False)]
|
||||
[0.5, 0.16666666666666666]
|
||||
"""
|
||||
consumable = iter(regurge(iterable))
|
||||
consumable = iter(iterable)
|
||||
last = next(consumable)
|
||||
if first:
|
||||
yield last
|
||||
|
@ -481,7 +690,7 @@ def cumquot(iterable:Iterable[complex], first:bool=True) -> generator:
|
|||
yield last
|
||||
|
||||
|
||||
def choose(iterable:Iterable[Any], *indices:Sequence[int]) -> generator:
|
||||
def choose(iterable:Iterable[Any], *indices:Iterable[int]) -> Generator:
|
||||
"""
|
||||
Yield specific elements from an iterable by index:
|
||||
>>> [*choose(range(1, 10), 0, 3)]
|
||||
|
@ -490,7 +699,7 @@ def choose(iterable:Iterable[Any], *indices:Sequence[int]) -> generator:
|
|||
[1, 4]
|
||||
|
||||
"""
|
||||
indices = [*flatten(indices)]
|
||||
indices = regenerator(flat(indices))
|
||||
yielded = []
|
||||
for i, e in enumerate(iterable):
|
||||
if i in indices:
|
||||
|
@ -499,11 +708,16 @@ def choose(iterable:Iterable[Any], *indices:Sequence[int]) -> generator:
|
|||
if len(yielded) == len(indices):
|
||||
break
|
||||
|
||||
def skip(iterable:Iterable[Any], *indices:Iterable[int]) -> Generator:
|
||||
"""
|
||||
Skip specific elements from an iterable by index
|
||||
"""
|
||||
indices = tuple(flat(indices))
|
||||
for i, e in enumerate(iterable):
|
||||
if not i in indices:
|
||||
yield e
|
||||
|
||||
# def chooser(iterable:Iterable, *)
|
||||
|
||||
|
||||
def dupes(array:Iterable, once:bool=True, key:Callable=lambda x:x) -> generator:
|
||||
def dupers(array:Iterable, once:bool=True, key:Callable=lambda x:x) -> Generator:
|
||||
"""
|
||||
Yield the elements of a finite iterable whose frequency is greater than one
|
||||
:once:
|
||||
|
@ -529,7 +743,7 @@ def dupes(array:Iterable, once:bool=True, key:Callable=lambda x:x) -> generator:
|
|||
last_yield = i
|
||||
last_seen = i
|
||||
|
||||
def slices(iterable:Iterable, length:int, fill:Any=None) -> generator:
|
||||
def slices(iterable:Iterable, length:int, fill:Any=None) -> Generator:
|
||||
"""
|
||||
Yield the adjacent slices of a given length for the given iterable. Trailing values will be padded by copies of 'fill'
|
||||
use filter(all, slices(iterable, length)) to discard remainders
|
||||
|
@ -546,8 +760,227 @@ def slices(iterable:Iterable, length:int, fill:Any=None) -> generator:
|
|||
main += [fill for i in range(length-len(main))]
|
||||
yield tuple(main)
|
||||
|
||||
|
||||
def repeat(func:Callable, inpt:Any, times:int=2, **kwargs):
|
||||
"""
|
||||
Get the output of recursive calls of a function some number of times
|
||||
Dependencies: None
|
||||
In: function,input,times[=1]
|
||||
Out: Function call
|
||||
|
||||
repeat(lambda x: x+1,0)
|
||||
2
|
||||
repeat(lambda x: x+1,0,1)
|
||||
1
|
||||
"""
|
||||
for i in range(times):
|
||||
inpt = func(inpt, **kwargs)
|
||||
return inpt
|
||||
recursor = repeat
|
||||
|
||||
|
||||
def eq(*args:Iterable[Any]) -> bool:
|
||||
"""
|
||||
Check if arguments are equal
|
||||
Will always return True if the only argument given is not an iterable
|
||||
"""
|
||||
if len(args) == 1:
|
||||
if hasattr(arg:=args[0], '__iter__'):
|
||||
return eq(*arg)
|
||||
return True
|
||||
|
||||
args = iter(args)
|
||||
arg0 = next(args)
|
||||
|
||||
for i in args:
|
||||
if i != arg0:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def clock(func:Callable, value:bool=False, name:bool=False, verbose:bool=False) -> Callable:
|
||||
"""
|
||||
A decorator for benchmarking functions
|
||||
|
||||
:name:
|
||||
option to print the name of the function with the time taken to call it
|
||||
:value:
|
||||
return the value of the function instead of the period
|
||||
|
||||
taken from
|
||||
Mari Wahl, mari.wahl9@gmail.com
|
||||
University of New York at Stony Brook
|
||||
"""
|
||||
def wrapper(*args:Any, **kwargs:Any) -> Any:
|
||||
t = time.perf_counter()
|
||||
res = func(*args, **kwargs)
|
||||
delta = time.perf_counter()-t
|
||||
if verbose:
|
||||
if name:
|
||||
print(f"{func.__name__}\n\t{delta}")
|
||||
else:
|
||||
print(f"d={delta}")
|
||||
return res if value else delta
|
||||
return wrapper
|
||||
benchmark = clock
|
||||
|
||||
|
||||
def imap(argument:Any, *functions:Callable) -> Generator:
|
||||
"""
|
||||
Converse of a map. Yield calls of the functions with the object as an argument
|
||||
Generator safe
|
||||
"""
|
||||
iterates = hasattr(object, '__iter__')
|
||||
for func in functions:
|
||||
yield func((argument, regenerator(argument))[iterates])
|
||||
|
||||
|
||||
def rmap(argument:Any, *functions:Callable) -> Any:
|
||||
"""
|
||||
Recursively call functions on an argument. Return the result of the last call
|
||||
Generator safe
|
||||
"""
|
||||
functions = iter(functions)
|
||||
iterates = hasattr(object, '__iter__')
|
||||
result = next(functions)((argument, regenerator(argument))[iterates])
|
||||
for func in functions:
|
||||
result = func((result, regenerator(result))[iterates])
|
||||
return result
|
||||
|
||||
|
||||
def dictate(dictionary:dict, *omissions:Hashable, include:bool=False):
|
||||
"""
|
||||
Create a selective clone of a dictionary
|
||||
params:
|
||||
omissions
|
||||
the hashable values corresponding to keys you would like to remove
|
||||
include
|
||||
If true, only keys specified in "omissions" will persist in the final dictionary
|
||||
|
||||
>>> dictate(dict(zip('abc', range(3))), 'a')
|
||||
{'b': 1, 'c': 2}
|
||||
>>> dictate(dict(zip('abc', range(3))), 'a', include=True)
|
||||
{'a': 0}
|
||||
>>> dictate(dict(zip('abc', range(3))), 'b c'.split(), include=True)
|
||||
{'b': 1, 'c': 2}
|
||||
|
||||
"""
|
||||
if include:
|
||||
return {key: dictionary.get(key) for key in dictionary if key in flat(omissions)}
|
||||
return {key: dictionary.get(key) for key in dictionary if not key in flat(omissions)}
|
||||
|
||||
|
||||
def itersplit(iterable:Iterable[Any], *indices:int, cumulative:bool=False):
|
||||
"""
|
||||
Split an iterable at the given indices or their cumulative sums
|
||||
|
||||
examples:
|
||||
>>> sl4ng.show(itersplit('gravitation', 3, 6, 8))
|
||||
('g', 'r', 'a')
|
||||
('v', 'i', 't')
|
||||
('a', 't')
|
||||
('i', 'o', 'n')
|
||||
>>> sl4ng.show(itersplit('gravitation', 3, 3, 2, cumulative=True))
|
||||
('g', 'r', 'a')
|
||||
('v', 'i', 't')
|
||||
('a', 't')
|
||||
('i', 'o', 'n')
|
||||
"""
|
||||
indices = flat(indices)
|
||||
indices = regenerator(cumsum(indices, first=False)) if cumulative else sorted(indices)
|
||||
assert all(isinstance(i, int) for i in indices), "Some indices aren't integers"
|
||||
|
||||
boxes = tuple([] for i in indices)
|
||||
index = 0
|
||||
iterable = iter(iterable)
|
||||
for i, e in enumerate(iterable):
|
||||
if i >= indices[index]:
|
||||
yield tuple(boxes[index])
|
||||
index += 1
|
||||
|
||||
try:
|
||||
boxes[index].append(e)
|
||||
except IndexError:
|
||||
break
|
||||
|
||||
|
||||
if (last := tuple([e, *iterable])):
|
||||
yield tuple(last)
|
||||
|
||||
|
||||
def interval(start:float, stop:float, step:float, reverse=False) -> Generator:
|
||||
"""
|
||||
Compute an [x0, x1, dx)-interval and its cardinality
|
||||
Params
|
||||
start:
|
||||
least element of the interval
|
||||
stop:
|
||||
cutoff element
|
||||
step:
|
||||
difference between consecutive elements
|
||||
reverse:
|
||||
traverse the interval in reverse order
|
||||
|
||||
"""
|
||||
# ctr = 0
|
||||
# itr = []
|
||||
while start + step < stop:
|
||||
# itr.append(start)
|
||||
yield start
|
||||
start += step
|
||||
# ctr += 1
|
||||
# return ctr, itr
|
||||
|
||||
class Interval:
|
||||
def __init__(self, start:float, stop:float, step:float):
|
||||
"""
|
||||
Compute a yielding [)-interval and access useful properties such as its cardinality.
|
||||
This is essentially equivalent to "range", for non-integer aritmetics
|
||||
Params
|
||||
start:
|
||||
least element of the interval
|
||||
stop:
|
||||
cutoff element
|
||||
step:
|
||||
difference between consecutive elements
|
||||
reverse:
|
||||
traverse the interval in reverse order
|
||||
|
||||
"""
|
||||
self.start = start
|
||||
self.stop = stop
|
||||
self.step = step
|
||||
def __iter__(self):
|
||||
start = self.start
|
||||
stop = self.stop
|
||||
step = self.step
|
||||
while start + step < stop:
|
||||
yield start
|
||||
start += step
|
||||
def __len__(self):
|
||||
return sum(1 for i in self)
|
||||
def __reversed__(self):
|
||||
start = self.start
|
||||
stop = self.stop
|
||||
step = self.step
|
||||
while stop - step > start:
|
||||
yield start
|
||||
stop -= step
|
||||
def __getitem__(self, key:int):
|
||||
for index, item in enumerate(self):
|
||||
if index==key:
|
||||
return item
|
||||
raise IndexError
|
||||
|
||||
if __name__ == "__main__":
|
||||
from statistics import median
|
||||
# pass
|
||||
for i in range(1,10):
|
||||
print([*range(i)])
|
||||
print(median(range(i)))
|
||||
# for i in range(1,10):
|
||||
# print([*range(i)])
|
||||
# print(median(range(i)))
|
||||
n = 6
|
||||
for i in range(n):
|
||||
print(roll(range(3), i))
|
||||
for i in range(n):
|
||||
print(roll(range(3), 3, i))
|
||||
|
|
@ -0,0 +1,215 @@
|
|||
"""
|
||||
Making numbers readable and dimensional one order of magnitude at a time
|
||||
"""
|
||||
__all__ = 'nice_size nice_duration MemorySize Duration'.split()
|
||||
|
||||
from .debug import show
|
||||
from .iteration import sample, nopes
|
||||
|
||||
|
||||
orders = {
|
||||
0: 'mono',
|
||||
1: 'deca',
|
||||
2: 'hecto',
|
||||
3: 'kilo',
|
||||
6: 'mega',
|
||||
9: 'giga',
|
||||
12: 'tera',
|
||||
15: 'peta',
|
||||
18: 'exa',
|
||||
21: 'zetta',
|
||||
24: 'yotta',
|
||||
}
|
||||
|
||||
sredro = {v:k for k, v in orders.items()}
|
||||
|
||||
def lasso(number:complex, unit:str='') -> str:
|
||||
"""
|
||||
Make a large number more readable by inserting commas before every third power of 10 and adding units
|
||||
"""
|
||||
return f'{number:,} {unit}'.strip()
|
||||
|
||||
def set_case(unit:str, lower:bool=False) -> str:
|
||||
"""
|
||||
Set the case of a number's unit string
|
||||
"""
|
||||
return [unit.upper().strip(), unit.lower().strip()][lower]
|
||||
|
||||
def set_length(mag:str, unit:str, long:bool=False, sep:str='-') -> str:
|
||||
"""
|
||||
Set the length of a number's unit string
|
||||
"""
|
||||
return ('', sep)[long].join(((mag[0], unit[0]), (mag, unit))[long])
|
||||
|
||||
def magnitude(value:complex, omissions:list='mono deca hecto'.split()):
|
||||
"""
|
||||
Determine the best order of magnitude for a given number
|
||||
"""
|
||||
mags = tuple(sorted(i for i in orders.keys() if not orders[i] in omissions))
|
||||
booly = lambda i: len(str(int(value))) < len(str(10**mags[i+1]))
|
||||
fits = tuple(nopes((booly(i) for i in range(len(mags)-1)),True))
|
||||
fit = mags[min(fits) if fits else len(mags)-1]
|
||||
return orders[fit]
|
||||
|
||||
def nice_size(self:complex, unit:str='bytes', long:bool=False, lower:bool=False, precision:int=2, sep:str='-', omissions:list='mono deca hecto'.split()):
|
||||
"""
|
||||
This should behave well on int subclasses
|
||||
"""
|
||||
mag = magnitude(self, omissions)
|
||||
precision = sredro[mag] if self<5 else precision
|
||||
unit = set_case(set_length(mag, unit, long, sep), lower)
|
||||
val = round(self*10**-(sredro[mag]), precision)
|
||||
return lasso(val, unit)
|
||||
|
||||
class MemorySize(int):
|
||||
"""
|
||||
Why should you have to sacrifice utility for readability?
|
||||
"""
|
||||
def __repr__(self):
|
||||
return nice_size(self)
|
||||
|
||||
|
||||
|
||||
second = 1
|
||||
minute = 60 * second
|
||||
hour = 60 * minute
|
||||
day = 24 * hour
|
||||
week = 7 * day
|
||||
feb = 4 * week
|
||||
year = 365.25 * day
|
||||
# year = 13.044642857142858 * feb
|
||||
|
||||
|
||||
def time_dict(duration):
|
||||
periods = {
|
||||
# 'years': int(duration // year),
|
||||
'febs': int(duration // feb),
|
||||
# 'febs': int(duration % year // feb),
|
||||
'weeks': int(duration % feb // week),
|
||||
# 'weeks': int(duration % feb // week),
|
||||
'days': int(duration % week // day),
|
||||
# 'days': int(duration % week // day),
|
||||
'hours': int(duration % day // hour),
|
||||
# 'hours': int(duration % day // hour),
|
||||
'minutes': int(duration % hour // minute),
|
||||
# 'minutes': int(duration % hour // minute),
|
||||
|
||||
'seconds': round(duration % minute),
|
||||
}
|
||||
return periods
|
||||
|
||||
|
||||
def length(period):
|
||||
return eval(period[:-1])
|
||||
|
||||
|
||||
def timer_duration(periods, peak, base, watch):
|
||||
string = ''
|
||||
if watch:
|
||||
peak = 'hours'
|
||||
base = 'minutes'
|
||||
elif length(base) > second:
|
||||
base = 'seconds'
|
||||
for k,v in periods.items():
|
||||
if length(peak) >= length(k) >= length(base):
|
||||
if k == peak:
|
||||
string += f'{v:02d}'
|
||||
else:
|
||||
string += f':{v:02d}'
|
||||
return string
|
||||
|
||||
|
||||
def verbose_duration(periods, peak, base, short=False):
|
||||
string = ''
|
||||
for k, v in periods.items():
|
||||
if v:
|
||||
if length(peak) >= length(k) >= length(base):
|
||||
if k == peak:
|
||||
string += f'{v} {k[:1 if short else None]}'
|
||||
else:
|
||||
string += f', {v} {k[:1 if short else None]}'
|
||||
return string
|
||||
|
||||
|
||||
def nice_duration(duration, verbose=False, short=False, watch=False):
|
||||
"""
|
||||
Given a number of seconds, return a string which indicates the amount of time in:
|
||||
Months (28 days), weeks, days, hours, minutes, seconds
|
||||
|
||||
parameters:
|
||||
duration
|
||||
number of seconds
|
||||
verbose
|
||||
include units in result
|
||||
short
|
||||
abbreviate units
|
||||
watch
|
||||
only "hours:minutes"
|
||||
|
||||
examples:
|
||||
>>> humanize_duration(62)
|
||||
'01:02'
|
||||
|
||||
>>> t = 12532510
|
||||
|
||||
>>> humanize_duration(t)
|
||||
20:05:01:15:10
|
||||
|
||||
>>> humanize_duration(t, verbose=True)
|
||||
20 weeks, 5 days, 1 hours, 15 months, 10 seconds
|
||||
|
||||
>>> humanize_duration(t,verbose=True, short=True)
|
||||
20 w, 5 d, 1 h, 15 m, 10 s
|
||||
|
||||
>>> humanize_duration(t, watch=True) # equates to "what time will it be after this many seconds have passed"
|
||||
01:15
|
||||
"""
|
||||
periods = time_dict(duration)
|
||||
significant = [i for i in periods.keys() if periods.get(i)]
|
||||
base = min(significant, key=length)
|
||||
peak = max(significant, key=length)
|
||||
if not verbose:
|
||||
return timer_duration(periods, peak, base, watch)
|
||||
return verbose_duration(periods, peak, base, short)
|
||||
|
||||
class Duration(float):
|
||||
"""
|
||||
Why should you have to sacrifice utility for readability?
|
||||
"""
|
||||
def __repr__(self):
|
||||
return nice_duration(self)
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
band = lambda level, base=10, shift=0, root=1: tuple(root+i+shift for i in ((level+1)*base, level*base))[::-1]
|
||||
format = lambda selection: int(''.join(str(i) for i in selection))
|
||||
|
||||
digits = range(*band(0))
|
||||
|
||||
l = 0
|
||||
s = 0
|
||||
b = 30
|
||||
r = 1
|
||||
x,y = band(l, b, s, r)
|
||||
# print(x,y)
|
||||
val = sample(digits,y)
|
||||
sizes = [format(val[:i][::-1]) for i in range(x, y)]
|
||||
# show(sizes)
|
||||
# sizes = [44259260028,315436]
|
||||
|
||||
for size in sizes[::-1]:
|
||||
string = f"""{(len(lasso(max(sizes)))-len(lasso(size)))*' '+lasso(size)}
|
||||
{size = }
|
||||
{len(str(size)) = }
|
||||
{nice_size(size) = }
|
||||
{magnitude(size) = }
|
||||
|
||||
|
||||
""".splitlines()
|
||||
print(*((x.strip(), x)[i<1] for i, x in enumerate(string)), sep='\n\t', end='\n\n')
|
||||
print(lasso(sizes[-1]))
|
||||
print(band(l,s,b,r))
|
||||
print(x,y)
|
||||
print(f'{format(digits):,}')
|
||||
# print(all(rep(size)==rep2(size) for size in sizes))
|
316
sl4ng/maths.py
316
sl4ng/maths.py
|
@ -3,78 +3,72 @@
|
|||
from functools import lru_cache, reduce
|
||||
from itertools import chain, combinations, count
|
||||
from math import pi, ceil
|
||||
from typing import Iterable
|
||||
from typing import Iterable, Generator
|
||||
from numbers import Number, Real, Complex, Integral
|
||||
|
||||
from .types import generator
|
||||
# from .types import regenerator
|
||||
from .iteration import flat, regenerator
|
||||
|
||||
def pyramid(length:int, shift:int=0) -> float:
|
||||
def sign(number:Real) -> str:
|
||||
return '-+'[number >= 0]
|
||||
|
||||
def pyramid(length:Integral, shift:Integral=0) -> Real:
|
||||
"""
|
||||
Returns the product of dividing 1 by each intger in the range(2+shift,2+int(length)+shift) (aka - the number of poops in your pocket)
|
||||
if the length is not an integer it will be converted to one
|
||||
Dependencies: N/A
|
||||
In: length, shift=0 (integers)
|
||||
Out: N/A
|
||||
"""
|
||||
val = 1
|
||||
for i in range(2+shift, 2+int(length)+shift):
|
||||
for i in range(2 + shift, 2 + int(length) + shift):
|
||||
val /= i
|
||||
return val
|
||||
|
||||
|
||||
def primes1(n:int) -> list:
|
||||
"""Generates a list of the first n primes. A cast will be used if the input is not an integer
|
||||
Dependencies: N/A
|
||||
In: (number)
|
||||
Out: list"""
|
||||
# pwimes = list(x for x in range(n) if x > 1 and all(x%y for y in range(2, min(x, 11))))
|
||||
def first_primes(n:Integral) -> list:
|
||||
"""
|
||||
Generates a list of the first n primes. A cast will be used if the input is not an integer
|
||||
"""
|
||||
n = int(n) - 1
|
||||
bank = []
|
||||
track = 2
|
||||
while len(bank)<n+1:
|
||||
if all(track%y for y in range(2, min(track,11))):
|
||||
while len(bank)< n + 1:
|
||||
if all(track % y for y in range(2, min(track, 11))):
|
||||
bank.append(track)
|
||||
track += 1
|
||||
return sorted(set(bank))
|
||||
|
||||
|
||||
def primes2(n:int) -> list:
|
||||
"""Generates a list of primes with value lower than the input integer
|
||||
Dependencies: N/A
|
||||
In: (integer)
|
||||
Out: list"""
|
||||
pwimes = list({x for x in range(n) if x > 1 and all(x%y for y in range(2, min(x, 11)))})
|
||||
return list(pwimes)
|
||||
def primeslt(n:Integral) -> Generator:
|
||||
"""
|
||||
Generates a list of primes with value lower than the input integer
|
||||
"""
|
||||
return (x for x in range(n) if x > 1 and all(x % y for y in range(2, min(x, 11))))
|
||||
primes_lower_than = primeslt
|
||||
|
||||
def succ(n:Integral) -> Integral:
|
||||
"""
|
||||
Returns the successor of the input number
|
||||
"""
|
||||
return n + 1
|
||||
|
||||
|
||||
def succ(n:int) -> int:
|
||||
"""Returns the successor of the input number
|
||||
Dependencies: N/A
|
||||
In: (number)
|
||||
Out: number"""
|
||||
return n+1
|
||||
def pred(n:Integral) -> Integral:
|
||||
"""
|
||||
Returns the predecessor of the input number
|
||||
"""
|
||||
return n - 1
|
||||
|
||||
|
||||
def pred(n:int) -> int:
|
||||
"""Returns the predecessor of the input number
|
||||
Dependencies: N/A
|
||||
In: (number)
|
||||
Out: number"""
|
||||
return n-1
|
||||
def rt(x:Integral, n:Complex=2) -> Complex:
|
||||
"""
|
||||
Returns the n-th root of the input number, x
|
||||
"""
|
||||
return x ** (1 / n)
|
||||
|
||||
|
||||
def rt(x:int, n:complex) -> complex:
|
||||
"""Returns the nth root of the input number, x
|
||||
Dependencies: N/A
|
||||
In: (x, n)
|
||||
Out: float(number)"""
|
||||
return x**(1/n)
|
||||
|
||||
|
||||
def gcd0(a:int, b:int) -> int:
|
||||
"""The famous euclidean algorithm for computing the greatest common divisor of a pair of numbers a and b
|
||||
Dependencies: N/A
|
||||
In: (a: first number, b: second number)
|
||||
Out: int"""
|
||||
def gcd0(a:Integral, b:Integral) -> Integral:
|
||||
"""
|
||||
The famous euclidean algorithm for computing the greatest common divisor of a pair of numbers a and b
|
||||
"""
|
||||
while a != b:
|
||||
if a > b:
|
||||
a -= b
|
||||
|
@ -83,30 +77,24 @@ def gcd0(a:int, b:int) -> int:
|
|||
return a
|
||||
|
||||
|
||||
def gcd(*args:[int, tuple]) -> int:
|
||||
def gcd(*args:[int, tuple]) -> Integral:
|
||||
"""
|
||||
Compute the gcd for more than two integers at a time. Returns input if only one argument is given and it is greater than zero
|
||||
Dependencies: itertools.combinations
|
||||
In: subscriptable
|
||||
Out: int
|
||||
"""
|
||||
if any(i<=0 for i in args):
|
||||
if any(i <= 0 for i in args):
|
||||
return None
|
||||
if len(args)>1:
|
||||
gcds = {d for pair in combinations(args, 2) if all(i%(d:=gcd0(*pair))==0 for i in args)}
|
||||
return max(gcds)
|
||||
elif sum(args)>0:
|
||||
return args[0]
|
||||
if len(args) > 1:
|
||||
gcds = {d for pair in combinations(args, 2) if all(i % (d := gcd0(*pair)) == 0 for i in args)}
|
||||
return max(gcds) if gcds else 1
|
||||
elif sum(args) > 0:
|
||||
return max(args)
|
||||
|
||||
|
||||
def eratosthenes(n:int, imaginarypart:bool=False) -> generator:
|
||||
def eratosthenes(n:Integral, imaginarypart:bool=False) -> Generator:
|
||||
"""
|
||||
Implements eratothenes' sieve as a generator.
|
||||
Implements eratothenes' sieve as a Generator.
|
||||
If the input is not an int it will be rounded to one.
|
||||
Imaginary-part-based rounding optionable
|
||||
Dependencies: None
|
||||
In: int
|
||||
Out: generator
|
||||
"""
|
||||
iscomp = isinstance(n, complex) or issubclass(type(n), complex)
|
||||
n = round(n.imag) if imaginarypart and iscomp else round(n.real) if iscomp else round(n)
|
||||
|
@ -115,18 +103,15 @@ def eratosthenes(n:int, imaginarypart:bool=False) -> generator:
|
|||
primes = set()
|
||||
for i in rack:
|
||||
if i not in marked:
|
||||
multiples = {j for j in rack if j%i==0 and j>i}
|
||||
multiples = {j for j in rack if j % i == 0 and j > i}
|
||||
marked.update(multiples)
|
||||
yield i
|
||||
|
||||
|
||||
def factors0(n:int) -> generator:
|
||||
'''
|
||||
def _factors(n:Integral) -> Generator:
|
||||
"""
|
||||
Compute the factors of an integer
|
||||
Dependencies: itertools.(chain, combinations)
|
||||
In: int, or sequence of ints
|
||||
Out: generator
|
||||
'''
|
||||
"""
|
||||
pipe = lambda array: reduce(lambda x, y: x*y, array, 1)
|
||||
primes = tuple(eratosthenes(n))
|
||||
facts = {n, 1} if n!= 0 else {}
|
||||
|
@ -145,34 +130,26 @@ def factors0(n:int) -> generator:
|
|||
yield from facts
|
||||
|
||||
|
||||
def factors(*args:[int, tuple]) -> generator:
|
||||
'''
|
||||
def factors(*args:[int, tuple]) -> Generator:
|
||||
"""
|
||||
Compute the common factors of any finite number of integers
|
||||
Dependencies: m3ta.factors0
|
||||
In: int, or sequence of ints
|
||||
Out: generator
|
||||
'''
|
||||
if len(args) == 1 and hasattr(args[0], '__iter__'):
|
||||
args = tuple(args[0])
|
||||
"""
|
||||
args = regenerator(flat(args))
|
||||
if all(isinstance(i, int) or i==int(i) for i in args):
|
||||
# facs = []
|
||||
yielded = set()
|
||||
for i in args:
|
||||
for j in factors0(i):
|
||||
# facs.append(j)
|
||||
if all(not arg%j for arg in args):
|
||||
yield j
|
||||
# for i in facs:
|
||||
# if freq(i, facs)==len(args):
|
||||
# yield i
|
||||
if not i in yielded:
|
||||
for j in _factors(i):
|
||||
if not j in yielded:
|
||||
yielded.add(j)
|
||||
if all(not arg%j for arg in args):
|
||||
yield j
|
||||
|
||||
|
||||
@lru_cache(maxsize = 500)
|
||||
def factorial(n:int) -> int:
|
||||
def factorial(n:Integral) -> Integral:
|
||||
"""
|
||||
Return n! for any integer
|
||||
Dependencies: lru_cache(from functools)
|
||||
In: (int)
|
||||
Out: int
|
||||
"""
|
||||
if n>=0:
|
||||
k = 1
|
||||
|
@ -184,124 +161,133 @@ def factorial(n:int) -> int:
|
|||
return -factorial(abs(n))
|
||||
|
||||
|
||||
def binomial(n:int, k:int) -> int:
|
||||
'''Returns the n choose k for any k in range(n)
|
||||
Dependencies: factorial
|
||||
In: (integer)
|
||||
Out: float'''
|
||||
return round(factorial(n)/(factorial(n-k)*factorial(k)))
|
||||
def binomial(n:Integral, k:Integral) -> Integral:
|
||||
"""
|
||||
Returns the n choose k for any k in range(n)
|
||||
"""
|
||||
return int(factorial(n)/(factorial(n-k)*factorial(k)))
|
||||
|
||||
|
||||
def isHarmoDiv(n:int) -> bool:
|
||||
"""Computes a boolean whose value corresponds to the statement 'the number n is a Harmonic Divisor Number'
|
||||
Dependencies: harmean (from meta)
|
||||
In: Number
|
||||
Out: Boolean"""
|
||||
def options(iterable:Iterable) -> Integral:
|
||||
"""
|
||||
Returns the number of ways to choose elements from the given iterable
|
||||
This will consume a Generator
|
||||
"""
|
||||
consumable = regenerator(iterable)
|
||||
length = sum(1 for i in consumable)
|
||||
return sum(binomial(length, i) for i in range(length))
|
||||
|
||||
|
||||
def isHarmoDiv(n:Integral) -> bool:
|
||||
"""
|
||||
Computes a boolean whose value corresponds to the statement 'the number n is a Harmonic Divisor Number'
|
||||
"""
|
||||
facts = factors(n)
|
||||
return int(harmean(facts)) == harmean(facts)
|
||||
|
||||
|
||||
def isFactor(divisor:int, predicate:int) -> bool:
|
||||
"""Determines if a Number is a multiple of a Divisor
|
||||
Dependencies: N/A
|
||||
In: (Divisor, Number)
|
||||
Out: Boolean"""
|
||||
return predicate % divisor == 0
|
||||
def isfactor(divisor:Integral, divisee:Integral) -> bool:
|
||||
"""
|
||||
Determines if a Number is a multiple of a Divisor
|
||||
"""
|
||||
return divisee % divisor == 0
|
||||
|
||||
|
||||
def isprime(n:int) -> bool:
|
||||
"""Determines if a Number is a multiple of a Divisor
|
||||
Dependencies: N/A
|
||||
In: (Divisor, Number)
|
||||
Out: Boolean"""
|
||||
return n in eratosthenes(n+1)
|
||||
def isprime(n:Integral) -> bool:
|
||||
"""
|
||||
Confirm that an integer has no factors other than 1 and itself
|
||||
"""
|
||||
if n < 2:
|
||||
return False
|
||||
tried = []
|
||||
for i in range(2, int(n/2)+1):
|
||||
if any(not i%j for j in tried):
|
||||
continue
|
||||
else:
|
||||
tried.append(i) if i > 1 else None
|
||||
if not n%i:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def isPerfect(n:int) -> bool:
|
||||
"""Returns a Boolean evaluation of an integer's Arithmetic Perfection
|
||||
Dependencies: sigma, factors (both from meta)
|
||||
In: Integer
|
||||
Out: Boolean"""
|
||||
def isperfect(n:Integral) -> bool:
|
||||
"""
|
||||
Check if an integer is equal to the sum of its factors
|
||||
"""
|
||||
return n == sum(factors(n))
|
||||
|
||||
|
||||
def isAbundant(n:int) -> bool:
|
||||
"""Returns a Boolean evaluation of an integer's Arithmetic Abundance
|
||||
Dependencies: sigma, factors (both from meta)
|
||||
In: Integer
|
||||
Out: Boolean"""
|
||||
def isabundant(n:Integral) -> bool:
|
||||
"""
|
||||
Check if an integer is greater than the sum of its factors
|
||||
"""
|
||||
return n > sigma(factors(n))
|
||||
|
||||
|
||||
def isDeficient(n:int) -> bool:
|
||||
"""Returns a Boolean evaluation of an integer's Arithmetic Deficience
|
||||
Dependencies: sigma, factors (both from meta)
|
||||
In: Integer
|
||||
Out: Boolean"""
|
||||
def isdeficient(n:Integral) -> bool:
|
||||
"""
|
||||
Check if an integer is smaller than the sum of its factors
|
||||
"""
|
||||
return n < sigma(factors(n))
|
||||
|
||||
|
||||
def isFilial(n:int) -> bool:
|
||||
fctrs = factors(n)
|
||||
lace = [int(i) for i in str(n)]
|
||||
return sigma(lace) in fctrs
|
||||
def isfilial(n:Integral) -> bool:
|
||||
"""
|
||||
Check if an integer is divisible by the sum of its digits
|
||||
"""
|
||||
return not n % sum(eval(i) for i in str(n))
|
||||
|
||||
|
||||
def mulPer(n:int) -> int:
|
||||
"""Computes the Multiplicative Persistence of an int or float in base-10 positional notation
|
||||
Dependencies: pipe (from meta)
|
||||
In: Integer
|
||||
Out: Integer"""
|
||||
def mulper(n:Integral) -> Integral:
|
||||
"""
|
||||
Computes the Multiplicative Persistence of an int or float in base-10 positional notation
|
||||
If the number is a float the decimal will be removed
|
||||
"""
|
||||
# Exclusions
|
||||
if len(str(n)) == 1:
|
||||
return 0
|
||||
elif (str(0) in str(n)) or ((len(str(n)) == 2) and (str(1) in str(n))):
|
||||
return 1
|
||||
else:
|
||||
cache = []
|
||||
ctr = 0
|
||||
while len(str(n)) > 1:
|
||||
digitList = [int(i) for i in "".join(str(n).split('.'))]
|
||||
n = pipe(digitList)
|
||||
cache.append(n)
|
||||
return len(cache)
|
||||
# digitList = [int(i) for i in "".join(str(n).split('.'))]
|
||||
digitList = map(int, str(i).replace('.', ''))
|
||||
n = reduce(lambda x, y: x*y, digitList, 1)
|
||||
ctr += 1
|
||||
return ctr
|
||||
|
||||
|
||||
def addPer(n:int) -> int:
|
||||
"""Computes the Additive Persistence of an int or float in base-10 positional notation
|
||||
Dependencies: sigma (from meta)
|
||||
In: Integer
|
||||
Out: Integer"""
|
||||
def addper(n:Integral) -> Integral:
|
||||
"""
|
||||
Computes the Additive Persistence of an int or float in base-10 positional notation
|
||||
"""
|
||||
if len(str(n)) == 1:
|
||||
return 0
|
||||
elif len(str(n)) < 1:
|
||||
return ValueError("Your number of choice has less than one digit")
|
||||
else:
|
||||
cache = []
|
||||
ctr = 0
|
||||
while len(str(n)) > 1:
|
||||
digitList = [int(i) for i in "".join(str(n).split('.'))]
|
||||
n = sigma(digitList)
|
||||
cache.append(n)
|
||||
return len(cache)
|
||||
n = sum(digitList)
|
||||
ctr += 1
|
||||
return ctr
|
||||
|
||||
|
||||
def triangular(a:int, b:int) -> int:
|
||||
"""Returns the triangular number of an interval [a,b]
|
||||
dependencies: none
|
||||
In: integers a and b
|
||||
Out: integer"""
|
||||
def triangular(a:Integral, b:Integral) -> Integral:
|
||||
"""
|
||||
Returns the triangular number of an interval [a,b]
|
||||
"""
|
||||
interval = [i for i in range(b) if i > a]
|
||||
for num in interval:
|
||||
a += num
|
||||
return a+b
|
||||
|
||||
|
||||
def rationability(v:complex) -> complex:
|
||||
def rationability(v:Complex) -> Complex:
|
||||
"""
|
||||
Get the subtractive series of the digits of a float, or that of an integer's reciprocal, measured from its math.ceil value
|
||||
Dependencies: math.ceil
|
||||
In: int/float
|
||||
Out: float
|
||||
|
||||
rationability(math.pi)
|
||||
0.8584073464102069
|
||||
rationability(3)
|
||||
|
@ -315,7 +301,7 @@ def rationability(v:complex) -> complex:
|
|||
return n
|
||||
|
||||
|
||||
def root(value:complex, power:complex=2) -> complex:
|
||||
def root(value:Complex, power:Complex=2) -> Complex:
|
||||
"""
|
||||
Get the root of a number
|
||||
|
||||
|
@ -327,7 +313,7 @@ def root(value:complex, power:complex=2) -> complex:
|
|||
return value**(1/power)
|
||||
|
||||
|
||||
def odds(n:int=-1) -> generator:
|
||||
def odds(n:Integral=-1) -> Generator:
|
||||
"""
|
||||
Yield the first n odd numbers, use a negative value for all of them
|
||||
"""
|
||||
|
@ -338,7 +324,7 @@ def odds(n:int=-1) -> generator:
|
|||
yield m
|
||||
n -= 1
|
||||
|
||||
def evens(n:int=-1) -> generator:
|
||||
def evens(n:Integral=-1) -> Generator:
|
||||
"""
|
||||
Yield the first n even numbers, use a negative value for all of them
|
||||
"""
|
||||
|
@ -349,14 +335,14 @@ def evens(n:int=-1) -> generator:
|
|||
yield m
|
||||
n -= 1
|
||||
|
||||
def congrues(n:int, modulus:int=6, cls:int=1) -> bool:
|
||||
def congrues(n:Integral, modulus:Integral=6, cls:Integral=1) -> bool:
|
||||
"""
|
||||
Check if n is equal to +-cls modulo modulus
|
||||
"""
|
||||
return n % modulus in {cls, modulus-cls}
|
||||
|
||||
|
||||
def mulseq(root:int, base:int=2, terms:int=-1, start:int=0, step:int=1) -> generator:
|
||||
def mulseq(root:Integral, base:Integral=2, terms:Integral=-1, start:Integral=0, step:Integral=1) -> Generator:
|
||||
"""
|
||||
Generate a sequence of multiples of a root and a base. By default it will yield the doubles sequence of the root.
|
||||
"""
|
||||
|
|
|
@ -1,71 +0,0 @@
|
|||
from typing import Any
|
||||
import os, pickle, re
|
||||
|
||||
import dill
|
||||
|
||||
from .iteration import deduplicate
|
||||
from .debug import tryimport
|
||||
from .files import nameSpacer
|
||||
|
||||
def dillsave(obj:Any, filename:str, overwrite:bool=True) -> Any:
|
||||
"""
|
||||
Pickles the given object to a file with the given path/name.
|
||||
If the object cannot be serialized with pickle, we shall use dill instead
|
||||
Returns the object
|
||||
"""
|
||||
if overwrite:
|
||||
with open(filename, 'wb') as fob:
|
||||
dill.dump(obj, fob, protocol=dill.HIGHEST_PROTOCOL)
|
||||
else:
|
||||
with open(nameSpacer(filename), 'wb') as fob:
|
||||
dill.dump(obj, fob, protocol=dill.HIGHEST_PROTOCOL)
|
||||
|
||||
def save(obj:Any, filename:str, overwrite:bool=True) -> Any:
|
||||
"""
|
||||
Pickles the given object to a file with the given path/name.
|
||||
If the object cannot be serialized with pickle, we shall use dill instead
|
||||
Returns the object
|
||||
"""
|
||||
try:
|
||||
if overwrite:
|
||||
with open(filename, 'wb') as fob:
|
||||
pickle.dump(obj, fob, protocol=pickle.HIGHEST_PROTOCOL)
|
||||
else:
|
||||
with open(nameSpacer(filename), 'wb') as fob:
|
||||
pickle.dump(obj, fob, protocol=pickle.HIGHEST_PROTOCOL)
|
||||
except pickle.PicklingError:
|
||||
dillsave(obj, filename, overwrite)
|
||||
except TypeError:
|
||||
dillsave(obj, filename, overwrite)
|
||||
return obj
|
||||
|
||||
def load(filename:str) -> Any:
|
||||
"""
|
||||
Return unpickled data from a chosen file
|
||||
If the file doesn't exist it will be created
|
||||
"""
|
||||
|
||||
if os.path.exists(filename):
|
||||
with open(filename, 'rb') as fob:
|
||||
var = pickle.load(fob)
|
||||
return var
|
||||
else:
|
||||
x = open(filename)
|
||||
x.close()
|
||||
return
|
||||
|
||||
def jar(file, duplicates:bool=False, flags:int=re.I):
|
||||
"""
|
||||
Consolidate your pickles and eat them, discard the clones by default though
|
||||
"""
|
||||
trash = tryimport('send2trash', 'send2trash', 'remove', 'os')
|
||||
name, ext = os.path.splitext(file)
|
||||
pattern = f'^{name}|{name}_\d+\.{ext}$'
|
||||
p = re.compile(pattern)
|
||||
folder = None if not os.sep in file else os.path.split(file)[0]
|
||||
os.chdir(folder) if folder else None
|
||||
matches = deduplicate({f: load(f) for f in os.listdir(folder) if p.search(f, flags)})
|
||||
results = list(matches.values())[:]
|
||||
for i in matches: print(i)
|
||||
[trash(file) for file in matches]
|
||||
save(results, file)
|
|
@ -4,14 +4,14 @@ from functools import lru_cache, reduce
|
|||
from itertools import tee
|
||||
|
||||
from .strings import alphabet
|
||||
from .types import regurge
|
||||
from .iteration import regenerator
|
||||
# from .types import regurge
|
||||
|
||||
def shannonEntropy(iterable:Iterable[Any]) -> float:
|
||||
"""
|
||||
Returns the Information, or Shannon, Entropy of an iterable
|
||||
"""
|
||||
consumable = regurge(iterable)
|
||||
consumable = list(consumable)
|
||||
consumable = regenerator(iterable)
|
||||
weights = {j: freq(j, iterable)/len(consumable) for j in set(consumable)}
|
||||
return -sigma([val*log(val, 2) for val in weights.values()])
|
||||
|
||||
|
@ -26,7 +26,7 @@ def entropy(iterable:Iterable[Any], base:int=2, mode:str='kbdUS', space:str=None
|
|||
digits = ''.join(i for i in abc if i.isnumeric() or i=='.' or i=='-')
|
||||
prob = lambda x,y: freq(x, y)/len(y)
|
||||
|
||||
consumable = regurge(iterable)
|
||||
consumable = regenerator(iterable)
|
||||
|
||||
iterates = hasattr(consumable,'__iter__')
|
||||
|
||||
|
@ -60,8 +60,7 @@ def probability(item:Any, iterable:Iterable) -> float:
|
|||
Returns the quotient of the frequency with which an item
|
||||
occurs in an iterable by the length of said iterable
|
||||
"""
|
||||
consumable = regurge(iterable)
|
||||
consumable = list(consumable)
|
||||
consumable = regenerator(iterable)
|
||||
return freq(item, consumable) / len(consumable)
|
||||
|
||||
|
||||
|
@ -73,8 +72,7 @@ def freq(element:Any, iterable:Iterable[Any], overlap:bool=False) -> int:
|
|||
say you're looking for 00 and there's a "000"
|
||||
you may only get one of what could be two matches
|
||||
"""
|
||||
consumable = regurge(iterable)
|
||||
consumable = list(consumable)
|
||||
consumable = regenerator(iterable)
|
||||
if type(element)==type(consumable)==str:
|
||||
if not overlap:
|
||||
return len(consumable.split(element))-1
|
||||
|
@ -87,8 +85,7 @@ def expectation(iterable:Iterable[complex]) -> complex:
|
|||
"""
|
||||
Returns the expectation value of a collection
|
||||
"""
|
||||
consumable = regurge(iterable)
|
||||
consumable = list(consumable)
|
||||
consumable = regenerator(iterable)
|
||||
exVal = 0
|
||||
for i in consumable:
|
||||
exVal += (i*(freq(i, consumable))/len(consumable))
|
||||
|
@ -99,8 +96,7 @@ def mean(iterable:Iterable[complex]) -> complex:
|
|||
"""
|
||||
Returns the mean value of a collection
|
||||
"""
|
||||
consumable = regurge(iterable)
|
||||
consumable = list(consumable)
|
||||
consumable = regenerator(iterable)
|
||||
meanVal = sum(consumable)
|
||||
meanVal /= len(consumable)
|
||||
return meanVal
|
||||
|
@ -111,8 +107,8 @@ def median(iterable:Iterable[complex]) -> complex:
|
|||
Returns the median value of a collection
|
||||
Will avoid consuming a generator/map/filter
|
||||
"""
|
||||
consumable = regurge(iterable)
|
||||
consumable = sorted(consumable)
|
||||
# consumable = regurge(iterable)
|
||||
consumable = regenerator(sorted(iterable))
|
||||
if len(consumable)%2:
|
||||
index = round((len(consumable)-1)/2)
|
||||
middle = consumable[index]
|
||||
|
@ -127,8 +123,7 @@ def midpoint(iterable:Iterable[complex]) -> complex:
|
|||
Returns the midpoint of a collection
|
||||
Will avoid consuming a generator/map/filter
|
||||
"""
|
||||
consumable = regurge(iterable)
|
||||
consumable = sorted(consumable)
|
||||
consumable = regenerator(iterable)
|
||||
return sum(max(consumable), min(consumable))/2
|
||||
|
||||
|
||||
|
@ -137,8 +132,7 @@ def central_deviation(iterable:Iterable[complex]) -> complex:
|
|||
Returns the difference between an iterable's midpoint and its median
|
||||
Will avoid consuming a generator/map/filter
|
||||
"""
|
||||
consumable = regurge(iterable)
|
||||
consumable = (consumable)
|
||||
consumable = regenerator(iterable)
|
||||
return abs(median(consumable) - midpoint(consumable))
|
||||
|
||||
|
||||
|
@ -146,8 +140,7 @@ def geomean(iterable:Iterable[complex]) -> complex:
|
|||
"""
|
||||
Returns the geometric mean of a collection
|
||||
"""
|
||||
consumable = regurge(iterable)
|
||||
consumable = list(consumable)
|
||||
consumable = regenerator(iterable)
|
||||
geoMean = (sigma(iterable))**(1/len(iterable))
|
||||
return geoMean
|
||||
|
||||
|
@ -156,8 +149,7 @@ def harmean(iterable:Iterable[complex]) -> complex:
|
|||
"""
|
||||
Returns the harmonic mean of a collection
|
||||
"""
|
||||
consumable = regurge(iterable)
|
||||
consumable = list(consumable)
|
||||
consumable = regenerator(iterable)
|
||||
assert 0 not in [i for i in consumable], "Input contains a zero, try a different one."
|
||||
reciprocals = [1/i for i in consumable]
|
||||
return len(consumable)/sigma(reciprocals)
|
||||
|
@ -167,8 +159,7 @@ def popdev(iterable:Iterable[complex]) -> complex:
|
|||
"""
|
||||
Returns the population standard deviation of a collection
|
||||
"""
|
||||
consumable = regurge(iterable)
|
||||
consumable = list(consumable)
|
||||
consumable = regenerator(iterable)
|
||||
avg = mean(consumable)
|
||||
v1 = [(i-avg)**2 for i in consumable]
|
||||
v2 = ((reduce(lambda x, y: x+y, v1))/len(v1))**(1/2)
|
||||
|
@ -179,8 +170,7 @@ def samdev(iterable:Iterable[complex]) -> complex:
|
|||
"""
|
||||
Returns the sample standard deviation of a collection
|
||||
"""
|
||||
consumable = regurge(iterable)
|
||||
consumable = list(consumable)
|
||||
consumable = regenerator(iterable)
|
||||
avg = mean(consumable)
|
||||
v1 = [(i-avg)**2 for i in consumable]
|
||||
v2 = ((sigma(v1))/(len(v1)-1))**(1/2)
|
||||
|
@ -191,8 +181,7 @@ def popvar(iterable:Iterable[complex]) -> complex:
|
|||
"""
|
||||
Returns the population variance for a collection
|
||||
"""
|
||||
consumable = regurge(iterable)
|
||||
consumable = list(consumable)
|
||||
consumable = regenerator(iterable)
|
||||
avg = mean(consumable)
|
||||
deviations = [(i-avg)**2 for i in consumable]
|
||||
pv = sum(deviations)/len(deviations)
|
||||
|
@ -203,8 +192,7 @@ def samvar(iterable:Iterable[complex]) -> complex:
|
|||
"""
|
||||
Returns the sample variance for a collection
|
||||
"""
|
||||
consumable = regurge(iterable)
|
||||
consumable = list(consumable)
|
||||
consumable = regenerator(iterable)
|
||||
avg = mean(consumable)
|
||||
v2 = [(i-v1)**2 for i in consumable]
|
||||
v3 = sigma(v2)/len(v2)
|
||||
|
|
|
@ -1,15 +1,18 @@
|
|||
from typing import Iterable, Any
|
||||
from typing import Iterable, Any, Generator, Callable, Iterator
|
||||
from itertools import chain
|
||||
import string, random
|
||||
|
||||
import pyperclip
|
||||
|
||||
from .types import generator, regurge
|
||||
from .iteration import regenerator
|
||||
# from .types import function
|
||||
from .debug import tipo
|
||||
|
||||
|
||||
def join(iterable:Iterable[Any]=None, sep:str='', head:str='', tail:str='') -> str:
|
||||
"""
|
||||
Cast elements of an array to string and concatenate them.
|
||||
This will consume a generator
|
||||
This will consume a Generator
|
||||
Examples:
|
||||
>>> m3ta.show(
|
||||
map(
|
||||
|
@ -34,7 +37,7 @@ def join(iterable:Iterable[Any]=None, sep:str='', head:str='', tail:str='') -> s
|
|||
[0, 1, 2]
|
||||
[0, 1, 2, 3]
|
||||
"""
|
||||
if iterable:
|
||||
if not isinstance(iterable, type(None)):
|
||||
return head + sep.join(map(str, iterable)) + tail
|
||||
else:
|
||||
def wrapper(iterable:Iterable[Any]):
|
||||
|
@ -42,7 +45,7 @@ def join(iterable:Iterable[Any]=None, sep:str='', head:str='', tail:str='') -> s
|
|||
return head + sep.join(iterable) + tail
|
||||
return wrapper
|
||||
|
||||
def ascii(omissions:str='', include:bool=False) -> str:
|
||||
def ascii(omissions:str='w', include:bool=False) -> str:
|
||||
"""
|
||||
Return the ascii character base excluding the given omissions:
|
||||
"p" -> ' ' + punctuation
|
||||
|
@ -58,8 +61,9 @@ def ascii(omissions:str='', include:bool=False) -> str:
|
|||
"u":string.ascii_uppercase,
|
||||
"l":string.ascii_lowercase,
|
||||
"d":string.digits,
|
||||
"w":string.whitespace,
|
||||
}
|
||||
return "".join(d[k] for k in d if k in omissions) if include else "".join(d[k] for k in d if not k in omissions)
|
||||
return "".join(d[key] for key in d if key in omissions) if include else "".join(d[key] for key in d if not key in omissions)
|
||||
|
||||
|
||||
asciis = kbd = abc = ascii
|
||||
|
@ -106,28 +110,34 @@ def monoalphabetic(message:str, shift:int, alphabet:str=kbd(), space:str=None) -
|
|||
|
||||
caesar = monoalphabetic
|
||||
|
||||
def multisplit(splitters:Iterable[str], target:str=None) -> generator:
|
||||
"""
|
||||
Split a string by a the elements of an iterable
|
||||
>>> splitter = multisplit('word1 word2 word3'.split())
|
||||
>>> map(splitter, texts)
|
||||
- OR -
|
||||
>>> [*multisplit('breakfast', 'green eggs and ham')]
|
||||
['g', 'n ', 'gg', ' ', 'nd h', 'm']
|
||||
- OR -
|
||||
>>> [*multisplit('breakfast'.split(), 'green eggs and ham')]
|
||||
['green eggs and ham']
|
||||
|
||||
"""
|
||||
def wrapper(target):
|
||||
nonlocal splitters
|
||||
splitters = iter(splitters)
|
||||
result = target.split(next(splitters))
|
||||
for splitter in splitters:
|
||||
result = [*chain.from_iterable(i.split(splitter) for i in result)]
|
||||
yield from filter(None, result)
|
||||
return wrapper(target) if target else wrapper
|
||||
|
||||
def splitall(splitters:Iterable[str], target:str) -> regenerator:
|
||||
"""
|
||||
>>> [*splitall('-_.', 'author-file_name.ext')] == 'author file name ext'.split()
|
||||
True
|
||||
"""
|
||||
splitters = iter(splitters)
|
||||
result = target.split(next(splitters))
|
||||
for splitter in splitters:
|
||||
result = [*chain.from_iterable(i.split(splitter) for i in result)]
|
||||
yield from filter(None, result)
|
||||
class splitter:
|
||||
"""
|
||||
Callable which splits a string by a the elements of an iterable. Ignore any empty strings.
|
||||
>>> [*splitter('-_.')('author-file_name.ext')] == 'author file name ext'.split()
|
||||
True
|
||||
"""
|
||||
def __init__(self, splitters:Iterable[str]):
|
||||
self.splitters = regenerator(splitters)
|
||||
def __call__(self, argument) -> regenerator:
|
||||
return splitall(self.splitters, argument)
|
||||
def __repr__(self):
|
||||
return f"{tipo(self)}{tuple(self.splitters)}"
|
||||
def multisplit(splitters:Iterable[str], target:str=None) -> (Callable, regenerator):
|
||||
"""
|
||||
Wrapper on sl4ng.strings.splitall and sl4ng.strings.splitter
|
||||
"""
|
||||
return splitall(splitters, target) if not isinstance(target, type(None)) else splitter(splitters)
|
||||
|
||||
def memespace(string:str, spaces:int=1, keep_spaces:bool=False, copy:bool=False):
|
||||
"""
|
||||
|
@ -172,18 +182,36 @@ def sinusize(string:str, copy:bool=False):
|
|||
"""
|
||||
mat = [[] for i in range(3)]
|
||||
for i, j in enumerate(string):
|
||||
if not i%2:
|
||||
mat[1].append(j)
|
||||
if not i % 2:
|
||||
mat[0].append(' ')
|
||||
mat[1].append(j)
|
||||
mat[2].append(' ')
|
||||
elif not (i%4) - 1:
|
||||
elif not (i % 4) - 1:
|
||||
mat[0].append(j)
|
||||
mat[1].append(' ')
|
||||
mat[2].append(' ')
|
||||
else:
|
||||
mat[2].append(j)
|
||||
mat[0].append(' ')
|
||||
mat[1].append(' ')
|
||||
mat[2].append(j)
|
||||
out = join([join(line) for line in mat], '\n')
|
||||
pyperclip.copy(out) if copy else None
|
||||
return out
|
||||
|
||||
def clean_url(url, root=True, protocol='http'):
|
||||
"""
|
||||
Remove trailing/leading slashes from a url
|
||||
params
|
||||
root
|
||||
if the url starts at the domain level
|
||||
protocol
|
||||
ignored if root=False
|
||||
what protocol should be added to the start of a url
|
||||
"""
|
||||
out = '/'.join(filter(None, url.split('/')))
|
||||
if root:
|
||||
if ':/' in out:
|
||||
out = out.replace(':/', '://')
|
||||
else:
|
||||
out = '://'.join((protocol, out))
|
||||
return out
|
|
@ -1,12 +1,12 @@
|
|||
import time, random, string
|
||||
|
||||
import pyperclip, psutil
|
||||
from .iteration import shuffle
|
||||
|
||||
|
||||
|
||||
def agora(string:bool=False):
|
||||
def agora(string:bool=False) -> str | tuple:
|
||||
"""
|
||||
Here's the local time, mr wolf!
|
||||
Here's the local time, Mrs. Wolf!
|
||||
Dependencies: time
|
||||
In: string=False
|
||||
Out: tuple/string(HH:MM:SS)
|
||||
|
@ -15,9 +15,9 @@ def agora(string:bool=False):
|
|||
return now if not string else ':'.join(str(i) for i in now)
|
||||
|
||||
|
||||
def kill(process:str, casefold=True):
|
||||
def kill(process:str, casefold:bool=True):
|
||||
"""
|
||||
Kill all processes of the argued process name
|
||||
Kill all instances of the argued process name
|
||||
Dependencies: psutil
|
||||
In: "process_name.exe"[str],casefold=True[bool]
|
||||
Out: None
|
||||
|
@ -31,15 +31,17 @@ def kill(process:str, casefold=True):
|
|||
[p.kill() for p in psutil.process_iter() if p.name()==process+('.exe', '')[process.endswith('.exe')]]
|
||||
|
||||
|
||||
def guid(format:tuple=[8, 4, 4, 4, 12], sep:str='-', copy:bool=True) -> str:
|
||||
def guid(format:tuple=[8, 4, 4, 4, 12], sep:str='-', copy:bool=True, braces:bool=True) -> str:
|
||||
"""
|
||||
Generate a GUID for different resources
|
||||
Dependencies: random,string,pyperclip.copy,m3ta.shuffle
|
||||
In: format{length of the intervals of the guid you want}
|
||||
Out: str
|
||||
"""
|
||||
chars = ''.join(i for i in shuffle(string.ascii_uppercase + string.digits+string.ascii_lowercase) if i!=sep)
|
||||
# chars = ''.join(i for i in shuffle(string.ascii_uppercase + string.digits+string.ascii_lowercase) if i!=sep)
|
||||
chars = ''.join(i for i in shuffle(string.hexdigits) if i != sep)
|
||||
ranstr = [random.choice(chars) for i in range(sum(format))]
|
||||
[ranstr.insert(i + sum(format[:i+1]), sep) for i, j in enumerate(format[:-1])]
|
||||
pyperclip.copy(''.join(ranstr)) if copy else None
|
||||
return ''.join(ranstr)
|
||||
result = "{%s}" % (s := ''.join(ranstr)) if braces else s
|
||||
pyperclip.copy(result) if copy else None
|
||||
return result
|
216
sl4ng/types.py
216
sl4ng/types.py
|
@ -1,217 +1,33 @@
|
|||
__all__ = 'generator module function SineWave defaultdict'.split()
|
||||
__all__ = 'generator module function DDict defaultdict'.split()
|
||||
|
||||
from collections import defaultdict
|
||||
from typing import Iterable, Any, Iterator, Sequence
|
||||
from copy import deepcopy
|
||||
from itertools import tee, _tee
|
||||
from typing import Tuple
|
||||
from warnings import warn
|
||||
import os
|
||||
|
||||
from .iteration import unique, flat
|
||||
# from .persistance import save, load
|
||||
from .debug import tipo
|
||||
|
||||
generator = type(i for i in range(0))
|
||||
module = type(os)
|
||||
function = type(lambda x: x)
|
||||
|
||||
|
||||
class _regen:
|
||||
@staticmethod
|
||||
def choose(iterable:Iterable[Any], *indices:Sequence[int]) -> generator:
|
||||
"""
|
||||
Yield specific elements from an iterable by index:
|
||||
>>> [*choose(range(1, 10), 0, 3)]
|
||||
[1, 4]
|
||||
>>> [*choose(range(1, 10), (0, 3))]
|
||||
[1, 4]
|
||||
|
||||
"""
|
||||
indices = [*flatten(indices)]
|
||||
yielded = []
|
||||
for i, e in enumerate(iterable):
|
||||
if i in indices:
|
||||
yield e
|
||||
yielded.append(e)
|
||||
if len(yielded) == len(indices):
|
||||
break
|
||||
@staticmethod
|
||||
def tipo(inpt:Any=type(lambda:0), keep_module:bool=False) -> str:
|
||||
"""
|
||||
Return the name of an object's type
|
||||
Dependencies: None
|
||||
In: object
|
||||
Out: str
|
||||
"""
|
||||
if keep_module:
|
||||
return str(type(inpt)).split("'")[1]
|
||||
return str(type(inpt)).split("'")[1].split('.')[-1]
|
||||
@staticmethod
|
||||
def flatten(iterable:Iterable) -> generator:
|
||||
"""
|
||||
Flatten a 2d iterable
|
||||
Example:
|
||||
>>> list(flatten([[1, 2], [3, 4]]))
|
||||
[1, 2, 3, 4]
|
||||
based on:
|
||||
https://pythonprinciples.com/challenges/Flatten-a-list/
|
||||
"""
|
||||
consumable = regurge(iterable)
|
||||
for i in consumable:
|
||||
if hasattr(i, '__iter__') or hasattr(i, '__next__'):
|
||||
for j in i:
|
||||
yield j
|
||||
else:
|
||||
yield i
|
||||
|
||||
|
||||
class regenerator:
|
||||
class DDict(defaultdict):
|
||||
"""
|
||||
A self-replenishing (or non-consumable) iterator. All methods whose return value is iterable return regenerators
|
||||
:args & kwargs:
|
||||
Any arguments needed to initialize the generator-type/function. Will not be used unless hasattr(iterable, '__call__') and iterable is not a regenerator.
|
||||
eg:
|
||||
>>> x = regurge(i for i in range(2))
|
||||
>>> [*x]
|
||||
[0, 1]
|
||||
>>> bytes(x)
|
||||
b'\x00\x01'
|
||||
>>> [*x]
|
||||
[0, 1]
|
||||
the standard collections.defaultdict with a less obscure __repr__ method
|
||||
"""
|
||||
def __init__(self, iterable, *args, **kwargs):
|
||||
if hasattr(iterable, '__call__') and not isinstance(iterable, type(self)):
|
||||
self.active, self._inert = tee(iterable(*args, **kwargs))
|
||||
else:
|
||||
self.active, self._inert = tee(iterable)
|
||||
def __next__(self):
|
||||
return next(self.active)
|
||||
def __iter__(self):
|
||||
self.active, self._inert = tee(self._inert)
|
||||
return self.active
|
||||
def __call__(self, *indices:Iterable[int]):
|
||||
"""
|
||||
Access particular indices of the underlying iterator
|
||||
eg
|
||||
>>> x = regen(range(3))
|
||||
>>> [*x(1)]
|
||||
[1]
|
||||
>>> [*x(1,2)]
|
||||
[1, 2]
|
||||
>>> [*x(1,2,3)]
|
||||
[1, 2]
|
||||
"""
|
||||
return type(self)(_regen.choose(self.active, *_regen.flatten(indices)))
|
||||
def __bool__(self):
|
||||
"""
|
||||
Returns True iff. the underlying iterator is non-empty. False otherwise.
|
||||
"""
|
||||
tmp, self._inert = tee(self._inert)
|
||||
try:
|
||||
next(tmp)
|
||||
return True
|
||||
except StopIteration:
|
||||
return False
|
||||
def __matmul__(self, other:Iterable):
|
||||
if hasattr(other, '__iter__'):
|
||||
return type(self)(product(self, other))
|
||||
raise TypeError(f'Matrix-multiplication is not defined for "{_regen.tipo(other, True)}". It must have an "__iter__" or "__index__" method.')
|
||||
def __len__(self):
|
||||
return sum(1 for i in self)
|
||||
def __add__(self, other:Any):
|
||||
"""
|
||||
Create a new regenerator whose first elements come from self and remaining element(s) is/come-from other.
|
||||
If other is not iterable it shall be added as the last element.
|
||||
If you want to add an iterable as a single element, use self.append
|
||||
eg
|
||||
>>> x = regenerator(range(2))
|
||||
>>> y = x + 10
|
||||
>>> [*y]
|
||||
[0, 1, 10]
|
||||
>>> y += x
|
||||
>>> [*y]
|
||||
[0, 1, 10, 0, 1]
|
||||
"""
|
||||
other = other if hasattr(other, '__iter__') else [other]
|
||||
return type(self)([*self, *other])
|
||||
def __radd__(self, other):
|
||||
"""
|
||||
Swap the order of __add__
|
||||
"""
|
||||
other = other if hasattr(other, '__iter__') else [other]
|
||||
return type(self)(chain(other, self))
|
||||
def __mul__(self, value):
|
||||
"""
|
||||
Replicate the behaviour of multiplying lists by integers
|
||||
"""
|
||||
if hasattr(value, '__int__'):
|
||||
return type(self)(chain.from_iterable(self for i in range(int(value))))
|
||||
raise TypeError(f'Multiplication is not defined for "{_regen.tipo(other, True)}". It must have an "__int__"')
|
||||
def __rmul__(self, value):
|
||||
"""Commutative multiplication"""
|
||||
return self.__mul__(value)
|
||||
def __pow__(self, value):
|
||||
"""
|
||||
value-dimensional Cartesian product of self with itself
|
||||
"""
|
||||
if hasattr(value, '__int__'):
|
||||
return type(self)(product(self, repeat=int(value)))
|
||||
raise TypeError(f'Exponentiation is not defined for {type(other)}. It must have an "__int__" method.')
|
||||
def scale(self, value):
|
||||
"""
|
||||
Multiply every element of self by value. Done in place.
|
||||
"""
|
||||
self._inert = (i*value for i in self)
|
||||
return self
|
||||
def boost(self, value):
|
||||
"""
|
||||
Add value to every element of self. Done in place.
|
||||
"""
|
||||
self._inert = (i+value for i in self)
|
||||
return self
|
||||
def indices(self, value, shift:int=0):
|
||||
"""
|
||||
Similar to a list's index method, except that it returns every index whose element is a match
|
||||
Works by calling enumerate and selecting at equivalent elements.
|
||||
:shift:
|
||||
kwarg for the "enumerate" call.
|
||||
"""
|
||||
return type(self)(i for i, e in enumerate(self, shift) if e == value)
|
||||
def append(self, value):
|
||||
"""
|
||||
add "value" as the last element of the array
|
||||
"""
|
||||
other = [value]
|
||||
self._inert = chain(self._inert, other)
|
||||
return self
|
||||
def inject(self, value):
|
||||
"""
|
||||
If "value" is an iterable: its elements will be added to the end of "self"
|
||||
Otherwise: it is the same as append
|
||||
"""
|
||||
other = value if hasattr(value, '__iter__') else [value]
|
||||
self._inert = chain(self._inert, *other)
|
||||
return self
|
||||
regen = regenerator
|
||||
|
||||
def regurge(iterable:Iterable[Any]) -> Iterable:
|
||||
"""
|
||||
Secure your generators by passing them through this function in order to produce a copy.
|
||||
Your generator will be replaced with a tee object.
|
||||
If your iterable is not a generator the function shall return a copy.deepcopy of it
|
||||
"""
|
||||
if issubclass(type(iterable), (map, filter, generator, _tee)):
|
||||
iterable, consumable = tee(iterable)
|
||||
else:
|
||||
consumable = iterable
|
||||
return consumable
|
||||
|
||||
class defaultdict(defaultdict):
|
||||
""""""
|
||||
@property
|
||||
def __rack(self):
|
||||
pairs = (': '.join(map(repr, pair)) for pair in self.items())
|
||||
r = ',\n '.join(pairs)
|
||||
return '{\n ' + r + '\n}'
|
||||
def __repr__(self):
|
||||
pairs = (': '.join(map(str, pair)) for pair in self.items())
|
||||
rack = ',\n '.join(pairs)
|
||||
return "{ " + rack + "\n}"
|
||||
return tipo(self) + self.__rack
|
||||
def __str__(self):
|
||||
return self.__repr__()
|
||||
return ' '.join(self.__rack.split())
|
||||
defaultdict = DDict
|
||||
|
||||
|
||||
|
||||
class SineWave:
|
||||
pass
|
Loading…
Reference in New Issue