first commit
This commit is contained in:
commit
ce858cdc8b
|
@ -0,0 +1,19 @@
|
|||
Copyright (c) 2020 eli2and40
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,26 @@
|
|||
# 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,6 @@
|
|||
[build-system]
|
||||
requires = [
|
||||
"setuptools>=42",
|
||||
"wheel"
|
||||
]
|
||||
build-backend = "setuptools.build_meta"
|
|
@ -0,0 +1,34 @@
|
|||
from setuptools import setup, find_packages
|
||||
|
||||
with open('README.md', 'r') as fob:
|
||||
long_description = fob.read()
|
||||
|
||||
setup(
|
||||
name='sl4ng',
|
||||
version='0.0.1',
|
||||
author='Kenneth Elisandro',
|
||||
author_email='eli2and40@tilde.club',
|
||||
url='https://tildegit.org/eli2and40/sl4ng',
|
||||
# packages=find_packages(),
|
||||
packages=['sl4ng'],
|
||||
classifiers=[
|
||||
"Programming Language :: Python :: 3",
|
||||
"License :: OSI Approved :: MIT License",
|
||||
"Operating System :: OS Independent",
|
||||
],
|
||||
long_description=long_description,
|
||||
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',
|
||||
|
||||
)
|
|
@ -0,0 +1,157 @@
|
|||
from typing import Iterable, Any, Iterator, Sequence
|
||||
import os
|
||||
|
||||
from .debug import *
|
||||
from .files import *
|
||||
from .functional import *
|
||||
from .iteration import *
|
||||
from .maths import *
|
||||
from .persistance import *
|
||||
from .stats import *
|
||||
from .strings import *
|
||||
from .system import *
|
||||
from .types import *
|
||||
from .web import *
|
||||
|
||||
|
||||
|
||||
|
||||
HERE,THIS = os.path.split(__file__)
|
||||
|
||||
|
||||
|
||||
|
||||
# def sgm(func:type(lambda:1),iterable:Iterable,arity:int=2) -> Any:
|
||||
# """Returns the sum of a iterable through a function, or the range(n) through a function for some integer n
|
||||
# Dependencies: itertools.chain
|
||||
# In: function, iterable
|
||||
# Out: value"""
|
||||
# from itertools import chain,combinations
|
||||
# if hasattr(iterable,'__iter__') or hasattr(iterable,'__next__'):
|
||||
# if isinstance(iterable,str):
|
||||
# if iterable.isnumeric():
|
||||
# return sum(func(int(i)) for i in iterable)
|
||||
# elif all(hasattr(i,'__len__') for i in iterable):
|
||||
# if all(len(x)==len(y) for x,y in combinations(iterable,2)):
|
||||
# return sum(
|
||||
# func(
|
||||
# *(
|
||||
# v[i] for v in iterable
|
||||
# )
|
||||
# ) for i in range(len(iterable[0]))
|
||||
# )
|
||||
# else:
|
||||
# return sum(func(pair) for pair in combinations(iterable,arity))
|
||||
# elif isinstance(iterable,int):
|
||||
# return sum(func(i) for i in range(iterable))
|
||||
# elif isinstance(iterable,float):
|
||||
# return sum(sgm(func,str(iterable).split('.')[0]),sgm(func,str(iterable).split('.')[1]))
|
||||
|
||||
|
||||
|
||||
# def primes2(n):
|
||||
# """Generates a list of primes with n terms
|
||||
# Dependencies: factors (from meta)
|
||||
# In: int
|
||||
# Out: list"""
|
||||
# result = []
|
||||
# index = 2
|
||||
# while len(result) <= n:
|
||||
# if len(factors(index)) == 2:
|
||||
# result.append(index)
|
||||
# index += 1
|
||||
# return result
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# t = range(10_000_000_000_000)
|
||||
# t = range(1_000_000_000)
|
||||
# arg = (i for i in t)
|
||||
# arg = (1 for i in t)
|
||||
# arg = t
|
||||
# print(eq(arg))
|
||||
# show((list(range(10000)) for i in range(10)),1,False,2,' ',False,True)
|
||||
|
||||
# print(entropy(int(i) for i in y))
|
||||
|
||||
# v = 15
|
||||
# l = []
|
||||
# for j in range(1,10):
|
||||
# print(sgm(lambda *args:pipe(args),[[1,2,3] for i in range(j)]))
|
||||
# l.append(sgm(lambda *args:pipe(args),[range(1,v) for i in range(j)]))
|
||||
|
||||
# print(l)
|
||||
# print(gcd2(l))
|
||||
# [print(i,max(j for j in factors(i) if i>j)) for i in l]
|
||||
# print(guid())
|
||||
# help(guid)
|
||||
# ffplay(r"C:\Users\Kenneth\Downloads\byextension\wav")
|
||||
# ffplay(r'F:\Programs\Project Files\FL\2019 old\bisto\bonnuci\blackburn_2\submixes\blackburn_2_Master.wav',hide=False)
|
||||
# ffplay(r"C:\Users\Kenneth\Downloads\byextension\wav\alixperez-crooklyn.wav*C:\Users\Kenneth\Downloads\byextension\wav\alixperez-melanie.wav*C:\Users\Kenneth\Downloads\byextension\mkv\Knucks - Home-eKb0XPEnwOk.mkv*C:\Users\Kenneth\Downloads\byextension\mkv\UK Garage - M Dubs - 'Bump n Grind'-fFjWiAR1c-I.mkv*C:\Users\Kenneth\Downloads\byextension\mkv\Ghost - The Club-4_uggscguzs.mkv*C:\Users\Kenneth\Downloads\music\music\Alix Perez\Morning Sun _ Playing Tricks\02 Playing Tricks.mp3*C:\Users\Kenneth\Downloads\music\music\Alix Perez\Morning Sun _ Playing Tricks\01 Morning Sun.mp3*C:\Users\Kenneth\Downloads\music\music\Alix Perez\Dub Rock _ Love Bug\01 Dubrock.mp3*C:\Users\Kenneth\Downloads\music\music\Alix Perez\Dub Rock _ Love Bug\02 Lovebug (feat. Specific).mp3*C:\Users\Kenneth\Downloads\music\music\Unitz\The Drop _ Mornin Blues\01 The Drop.mp3*C:\Users\Kenneth\Music\Collection\Archy Marshall\A New Place 2 Drown\04 Ammi Ammi.mp3*C:\Users\Kenneth\Music\Collection\Archy Marshall\A New Place 2 Drown\09 Empty Vessels.mp3*C:\Users\Kenneth\Downloads\music\music\Belangeo\Unknown Album\belangeo_Tribal.ogg*C:\Users\Kenneth\Music\Collection\Alix Perez\1984\11 Suffer In Silence (feat. Zero T).mp3*C:\Users\Kenneth\Music\Collection\Alix Perez\1984\03 Fade Away.mp3*C:\Users\Kenneth\Music\Collection\Alix Perez\1984\08 I_m Free.mp3*C:\Users\Kenneth\Music\Collection\Alix Perez\1984\15 Hemlines (feat. Sabre).mp3*C:\Users\Kenneth\Downloads\music\music\Alix Perez\Magnolias _ Backlash\02 Backlash.mp3*C:\Users\Kenneth\Downloads\music\music\Las\Outlaw EP\03 Drumspeak.m4a*C:\Users\Kenneth\Downloads\music\music\Las\Backyard _ Tic\02 Tic.mp3*C:\Users\Kenneth\Downloads\music\music\Las\Backyard _ Tic\01 Backyard.mp3*C:\Users\Kenneth\Downloads\music\music\Las\Las x Mikael - Single\01 Dem Break.m4a*C:\Users\Kenneth\Music\Collection\LAS\Your Eyes EP\03 Aspect.mp3*C:\Users\Kenneth\Music\Collection\LAS\Your Eyes EP\04 Lesson.mp3*F:\Programs\Project Files\FL\2019 old\bisto\bonnuci\blackburn_2\submixes\blackburn_2_Master.wav",fullscreen=False,randomize=False,hide=False)
|
||||
# ffplay('*'.join(i for i in gather(r'c:\users\kenneth\downloads\music',ext='mp3',names=False)))
|
||||
# ffplay('*'.join(i for i in gather(r'c:\users\kenneth\music\collection\alix perez',names=False)),fullscreen=False,hide=False,randomize=False)
|
||||
# ffplay(r'C:\Users\Kenneth\Downloads\byextension\wav*c:\users\kenneth\music\collection\alix perez',randomize=False)
|
||||
# ffplay(
|
||||
# '*'.join((
|
||||
# r"C:\Users\Kenneth\Downloads\byextension\mp3\Ryan Patrick Maguire - $†@®.mp3",
|
||||
# r"C:\Users\Kenneth\Downloads\byextension\mp3\Ryan Patrick Maguire - free_language.mp3",
|
||||
# r"C:\Users\Kenneth\Downloads\byextension\mp3\Ryan Patrick Maguire - moDernisT.mp3"
|
||||
# )))
|
||||
# ffplay(r'C:\Users\Kenneth\Downloads\byextension\mp3\SAR01.mp3*C:\Users\Kenneth\Downloads\byextension\mp3\SAR262.mp3')
|
||||
# compare(succ,pred)
|
||||
# tst = {
|
||||
# 'a':1,
|
||||
# 'b':{},
|
||||
# 'c':{},
|
||||
# 'd':2,
|
||||
# 'e':[],
|
||||
# 'f':[],
|
||||
# 'g':1,
|
||||
# 'h':1,
|
||||
# }
|
||||
# print(deduplicate(tst))
|
||||
# for i in range(10): print(agora(),agora(True))
|
||||
# f = r'e:\projects\monties\2020\fileManagement\knownExtensions.pkl'
|
||||
# [print(i,f.index(i),i==j) for i,j in zip(set(f),shuffle(set(f))) if i==j]# else print(i,j)]# in set(f) for j in shuffle(set(f))]
|
||||
# from math import log
|
||||
# help(log)
|
||||
# print((val:=factorial(len(f))),log(val,10),pyramid(log(val,10)),sep='\n',end='\n\n')
|
||||
# n = 1
|
||||
# while (k:=pyramid(n))>0:
|
||||
# k /= n
|
||||
# n += 1
|
||||
# print(n-1,k)
|
||||
|
||||
# print(n,pyramid(n))
|
||||
# help(straw(__file__,False)[0])
|
||||
# print(straw(__file__,text=False,lines=False))
|
||||
# for i in straw(__file__,False):
|
||||
# print(i.decode('utf-8'))
|
||||
# priter(f,1)
|
||||
# priter(shuffle(f))
|
||||
# print(empty(f))
|
||||
# print(os.curdir)
|
||||
# print(shutil.get_unpack_formats())
|
||||
# print(unpack(r"C:\Users\Kenneth\Downloads\music\music.rar"))
|
||||
# extractRar(r"C:\Users\Kenneth\Downloads\music\music.rar")
|
||||
# print(nameSpacer(__file__+'loll'))
|
||||
# unpack(r"C:\Users\Kenneth\Downloads\music\music.rar")
|
||||
pass
|
|
@ -0,0 +1,231 @@
|
|||
from typing import Any, Iterable
|
||||
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__"'
|
||||
|
||||
|
||||
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]
|
||||
|
||||
|
||||
def beeper(inpt:int) -> SineWave:
|
||||
"""Produces a series of beeps of increasing frequency
|
||||
Dependencies: Beep(a function from the winsound module)
|
||||
In: (number of beeps desired)
|
||||
Out: Series of beeps of increasing frequency"""
|
||||
for i in range(inpt):
|
||||
frequency = 65*2**(i)
|
||||
duration = 500
|
||||
winsound.Beep(frequency, duration)
|
||||
|
||||
|
||||
def syspath():
|
||||
"""Displays the path
|
||||
Dependencies: sys, sys.path
|
||||
In: (N/A)
|
||||
Out: string with formatted list"""
|
||||
return f"current:\n{sys.path}"
|
||||
|
||||
|
||||
def padd(inpt):
|
||||
"""Adds a string to the path
|
||||
Dependencies: sys, sys.path
|
||||
In: (desired address)
|
||||
Out: N/A"""
|
||||
sys.path.append(inpt)
|
||||
return("done :)\n{0}".format(sys.path))
|
||||
|
||||
|
||||
def test(func:type(lambda:1), unsortedlist:tuple) -> float:
|
||||
"""Test measures the amount of time needed to run a function on a given Input
|
||||
dependencies: time.clock() (perf_counter due to deprecation )
|
||||
In: function, input
|
||||
Out: Difference in time between the call and return of the function
|
||||
lifted from davejm's sorting module on github"""
|
||||
#Set copy equal to a new copy (different mem. location) of unsortedList to preserve current list
|
||||
#using the [:] split command which returns a copy of the whole list but as a new object
|
||||
from time import perf_counter as clock
|
||||
copy = unsortedlist[:]
|
||||
start = clock() #Set start time. TODO - May convert to using timit module.
|
||||
func(copy) #Run bubble or shuttle sort with copy of unsorted list as argument
|
||||
duration = clock() - start #Work out how long the execution of algorithm took and set to duration
|
||||
return duration
|
||||
|
||||
|
||||
def modir(module:type(os), copy:bool=True) -> str:
|
||||
"""
|
||||
Returns the full path of the directory inwhich a given module is located
|
||||
Dependencies: os, pyperclip
|
||||
In: module object, boolean (t-copy,f-not copy)
|
||||
Out: String or Boolean
|
||||
|
||||
Example:
|
||||
import matplotlib.pyplot as plt
|
||||
print(modir(plt),modir(plt,False),sep='\n')
|
||||
|
||||
Prints
|
||||
None
|
||||
E:\Languages\Python37-64\Lib\site-packages\matplotlib
|
||||
"""
|
||||
pth = os.sep.join(module.__file__.split(os.sep)[:-1])
|
||||
return pth if not copy else pyperclip.copy(pth)
|
||||
|
||||
|
||||
def printer(iterable:Iterable, indent:int=0) -> None:
|
||||
"""
|
||||
Prints each element of an iterable on a new line, features an optional indent argument
|
||||
"""
|
||||
assert isinstance(indent, int), "Indentation level must be a positive integer"
|
||||
for i in iterable:
|
||||
print(indent*"\t"+f"{i}")
|
||||
|
||||
|
||||
def tryimport(name:str, pack:str=None, substitute:bool=False, subpack:str=None, silent:bool=False) -> Any:
|
||||
"""
|
||||
Import a package, or something from within it, failing its installation import an optional substitute.
|
||||
Example:
|
||||
trasher = tryimport('send2trash',pack='send2trash',substitute='remove',subpack='os')
|
||||
would try to 'import send2trash from send2trash' and be ready to 'import remove from os' if send2trash is not installed, and assign the result to the 'trasher' variable
|
||||
Dependencies: None
|
||||
Arguments: name,pack=None,substitute=False,subpack=None; all strings
|
||||
Outputs: module/object
|
||||
"""
|
||||
try:
|
||||
message = f'from {pack} import {name}' if pack else f'import {name}'
|
||||
exec(message)
|
||||
return eval(name)
|
||||
except ModuleNotFoundError:
|
||||
if substitute:
|
||||
if subpack:
|
||||
return tryimport(substitute, subpack)
|
||||
else:
|
||||
return tryimport(substitute)
|
||||
elif subpack:
|
||||
return tryimport(subpack)
|
||||
else:
|
||||
if not silent:
|
||||
raise ModuleNotFoundError
|
||||
|
||||
|
||||
def aspectRatio(x:int, y:int, w:int=None, h:int=None) -> float:
|
||||
"""
|
||||
Compute aspect ratio and scale to desired size
|
||||
aspectRatio(a, b) ~> a/b
|
||||
aspectRatio(a, b, w=c) ~> (c,round(c*(b/a)))
|
||||
aspectRatio(a, b, h=c) ~> (round(c*(a/b)), c)
|
||||
aspectRatio(a, b, w=c, h=d) ~> a/b==c/d
|
||||
Dependencies: None
|
||||
In: x, y, w, h: numbers
|
||||
Outs: int or tuple
|
||||
"""
|
||||
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:
|
||||
"""
|
||||
Print each element of an array.
|
||||
>>> 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):
|
||||
print(
|
||||
(
|
||||
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()
|
||||
|
||||
|
||||
def pop(arg:Any=None, file:bool=True, silent:bool=False) -> str:
|
||||
"""
|
||||
Open the folder containing a given module, or object's module, or the content at the path of the given string
|
||||
Opens current working directory if no object is given
|
||||
Open the module's file if it is given
|
||||
If a path is given, it will be opened.
|
||||
Return the path which is opened
|
||||
|
||||
This will raise an attribute error whenever there is no file to which the object/module is imputed
|
||||
"""
|
||||
module = type(os)
|
||||
if arg:
|
||||
if isinstance(arg, str):
|
||||
path = arg
|
||||
elif isinstance(arg, module):
|
||||
path = arg.__file__
|
||||
else:
|
||||
mstr = arg.__module__
|
||||
if (top:=mstr.split('.')[0]) in globals().keys():
|
||||
m = eval(mstr)
|
||||
else:
|
||||
t = exec(f'import {top}')
|
||||
m = eval(mstr)
|
||||
path = m.__file__
|
||||
if not file:
|
||||
path = os.path.dirname(path)
|
||||
else:
|
||||
path = os.getcwd()
|
||||
if not silent:
|
||||
os.startfile(path)
|
||||
return path
|
||||
|
||||
|
||||
def hasDocs(module:type(os), copy:bool=True) -> bool:
|
||||
"""
|
||||
Returns directory of a given module's documentation... if it can be found by lesser mortals such as I
|
||||
Dependencies: os, pyperclip
|
||||
In: module object, boolean (t-copy,f-not copy)
|
||||
Out: String or Boolean
|
||||
"""
|
||||
base = modir(module, False)
|
||||
directory = os.path.join(base, 'docs') if os.path.exists(os.path.join(base, 'docs')) else os.path.join(base, 'doc')
|
||||
d = False if not os.path.exists(directory) else directory
|
||||
return d if not copy else pyperclip.copy(d)
|
||||
|
||||
|
||||
def getsource(obj:Any, *args, copy:bool=False, **kwargs) -> str:
|
||||
"""
|
||||
Wrapper on inspect.getsource which uses .show to present the text more readably and pyperclip.copy to copy said text to the clipboard.
|
||||
"""
|
||||
text = inspect.getsource(obj).splitlines()
|
||||
if copy:
|
||||
pyperclip.copy('\n'.join(text))
|
||||
return
|
||||
show(text, *args, **kwargs)
|
||||
|
||||
|
||||
|
||||
if __name__=='__main__':
|
||||
pass
|
|
@ -0,0 +1,13 @@
|
|||
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.
|
@ -0,0 +1,57 @@
|
|||
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)
|
|
@ -0,0 +1,151 @@
|
|||
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))]
|
|
@ -0,0 +1,185 @@
|
|||
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
|
|
@ -0,0 +1,94 @@
|
|||
from typing import Iterable, Any, Callable
|
||||
import time
|
||||
|
||||
from .types import function, generator, regurge
|
||||
|
||||
|
||||
|
||||
def repeat(func:function, inpt:Any, times:int=2, **kwargs) -> type:
|
||||
"""
|
||||
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:function, value:bool=False, name:bool=False) -> function:
|
||||
"""
|
||||
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 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, regurge(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, regurge(argument))[iterates])
|
||||
for func in functions:
|
||||
result = func((result, regurge(result))[iterates])
|
||||
return result
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,553 @@
|
|||
# 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 math import log
|
||||
import random
|
||||
|
||||
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:
|
||||
"""
|
||||
xrange(start, stop, step)
|
||||
An implementation of the old xrange generator
|
||||
examples:
|
||||
>>> [*xrange(2)]
|
||||
[0, 1]
|
||||
>>> [*xrange(2, 1)]
|
||||
[1]
|
||||
>>> [*xrange(2, 1, 0.5)]
|
||||
[1, 1.5]
|
||||
>>> [*xrange(2, 1, 0.5, reverse=True)] #
|
||||
[1.5, 1.0]
|
||||
>>> [*xrange(10, 1, 1, True)]
|
||||
[9, 8, 7, 6, 5, 4, 3, 2, 1]
|
||||
|
||||
todo:
|
||||
add the ability to use single-argument negative stops?
|
||||
"""
|
||||
if reverse:
|
||||
while start<stop:
|
||||
yield stop - step
|
||||
stop -= step
|
||||
else:
|
||||
while start<stop:
|
||||
yield start
|
||||
start += step
|
||||
|
||||
|
||||
def tight(iterable:Iterable[Any]) -> generator:
|
||||
"""
|
||||
Produce a new iterator of unique elements from a given array
|
||||
will consume a generator
|
||||
"""
|
||||
consumable = list(regurge(iterable))
|
||||
# consumable = list(consumable)
|
||||
yielded = []
|
||||
for i in iterable:
|
||||
if not i in yielded:
|
||||
yield i
|
||||
yielded.append(i)
|
||||
|
||||
|
||||
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
|
||||
>>> for i in walks(itertools.count(),2):print(''.join(i))
|
||||
(0, 1)
|
||||
(1, 2)
|
||||
(2, 3)
|
||||
# etc.
|
||||
Inspired by the hyperoperation 16**2[5]2
|
||||
Extended to generators with cues from more_itertools' "stagger"
|
||||
Extended to infinite generators by pedantry
|
||||
"""
|
||||
consumable = regurge(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:
|
||||
"""
|
||||
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
|
||||
|
||||
|
||||
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)
|
||||
>>> tuple(nopes(t))
|
||||
(0,2,3)
|
||||
>>> tuple(nopes(t,True))
|
||||
(1,4)
|
||||
"""
|
||||
consumable = regurge(iterable)
|
||||
for i, j in enumerate(consumable):
|
||||
if (not j, j)[yeps]:
|
||||
yield i
|
||||
|
||||
|
||||
def pull(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')
|
||||
"""
|
||||
consumable = regurge(iterable)
|
||||
consumable = list(consumable)
|
||||
return tuple(consumable[i%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
|
||||
"""
|
||||
if isinstance(unhashable, dict):
|
||||
trimmed = {}
|
||||
for key, val in unhashable.items():
|
||||
if val not in trimmed.values():
|
||||
trimmed[key] = val
|
||||
return trimmed
|
||||
else:
|
||||
raise TypeError(f'Protocol for your {tipo(unhashable)} is pending')
|
||||
|
||||
|
||||
def band(iterable:Iterable) -> float:
|
||||
"""
|
||||
Returns the extrema of the given iterable
|
||||
"""
|
||||
consumable = regurge(iterable)
|
||||
consumable = list(consumable)
|
||||
return min(consumable), max(consumable)
|
||||
|
||||
|
||||
def bandGap(iterable:Iterable) -> float:
|
||||
"""
|
||||
Returns the breadth of a given iterable of elements overwhich subtraction, max, and min, are well defined
|
||||
"""
|
||||
consumable = regurge(iterable)
|
||||
consumable = list(consumable)
|
||||
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)
|
||||
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:
|
||||
"""
|
||||
Returns the sum of a iterable
|
||||
"""
|
||||
consumable = regurge(iterable)
|
||||
consumable = list(consumable)
|
||||
for i in consumable:
|
||||
v0 += i
|
||||
return v0
|
||||
|
||||
|
||||
def pipe(iterable:Iterable[complex]) -> complex:
|
||||
"""
|
||||
Returns the multiplicative product of the elements of a collection
|
||||
"""
|
||||
consumable = regurge(iterable)
|
||||
consumable = list(consumable)
|
||||
if len(consumable) < 1:
|
||||
return "this list is empty"
|
||||
else:
|
||||
v0 = 1
|
||||
for i in consumable:
|
||||
v0 *= i
|
||||
return v0
|
||||
|
||||
|
||||
def powerset(iterable:Iterable) -> 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))
|
||||
|
||||
|
||||
def sample(iterable:Iterable, size:int) -> tuple:
|
||||
"""
|
||||
Obtains a random sample of any length from any iterable and returns it as a tuple
|
||||
"""
|
||||
consumable = regurge(iterable)
|
||||
consumable = list(consumable)
|
||||
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:
|
||||
"""
|
||||
Given an iterable, this function returns a new tuple of its elements in a new order
|
||||
"""
|
||||
consumable = regurge(iterable)
|
||||
consumable = list(consumable)
|
||||
cache = []
|
||||
pot = []
|
||||
while len(cache) < len(consumable):
|
||||
v0 = random.randrange(len(consumable))
|
||||
if v0 not in cache:
|
||||
cache.append(v0)
|
||||
pot.append(consumable[v0])
|
||||
return tuple(pot)
|
||||
randomizer = shuffler = shuffle
|
||||
|
||||
|
||||
def unzip(iterable:Iterable[Sequence[Any]]) -> List[list]:
|
||||
"""
|
||||
Obtain the inverse of a zip of a collection of arrays
|
||||
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)))
|
||||
[0, 0, 0]
|
||||
[1, 1, 1]
|
||||
[2, 2, 2]
|
||||
|
||||
|
||||
>>> m3ta.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]
|
||||
[3, 3, 3, 3, 3, 3]
|
||||
[4, 4, 4, 4, 4]
|
||||
[5, 5, 5, 5]
|
||||
[6, 6, 6]
|
||||
[7, 7]
|
||||
[8]
|
||||
|
||||
|
||||
"""
|
||||
consumable = regurge(iterable)
|
||||
str_escape = lambda string: string.replace("'", "\'")
|
||||
length = 0
|
||||
racks = []
|
||||
for i in consumable:
|
||||
for j, k in enumerate(i):
|
||||
if j > length-1:
|
||||
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})"
|
||||
eval(app_elem)
|
||||
return racks
|
||||
|
||||
def diffs(iterable:Iterable[complex], flip:bool=False) -> generator:
|
||||
"""
|
||||
Yield the difference between each element of an iterable and its predecessor
|
||||
example:
|
||||
>>> [*diffs(range(3))]
|
||||
[1, 1]
|
||||
>>> [*diffs(range(3), True)]
|
||||
[-1, -1]
|
||||
"""
|
||||
consumable = iter(regurge(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:
|
||||
"""
|
||||
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.
|
||||
example:
|
||||
>>> [*discontinuities(range(3))]
|
||||
[]
|
||||
>>> [*discontinuities(range(3), 2)]
|
||||
[0, 1]
|
||||
"""
|
||||
iterable = iter(iterable)
|
||||
last = next(iterable)
|
||||
for i, e in enumerate(iterable):
|
||||
if e - last != delta:
|
||||
yield i
|
||||
last = e
|
||||
|
||||
def stationaries(iterable:Iterable[complex]) -> generator:
|
||||
"""
|
||||
Generate the indices of the stationary points of an iterable
|
||||
example:
|
||||
>>> [*stationaries(range(3))]
|
||||
[]
|
||||
>>> [*stationaries((3, 4, 4, 3, 2, 1, 1), 2)]
|
||||
[1, 5]
|
||||
"""
|
||||
iterable = iter(iterable)
|
||||
last = next(iterable)
|
||||
for i, e in enumerate(iterable):
|
||||
if e == last:
|
||||
yield i
|
||||
last = e
|
||||
|
||||
def discontinuous(iterable:Iterable[complex], differential:complex=1) -> generator:
|
||||
"""
|
||||
Check if an additive semigroupoid has any jumps which are inequivalent to a given differential
|
||||
example:
|
||||
>>> [*discontinuous(range(3))]
|
||||
False
|
||||
>>> [*discontinuous(range(3), 2)]
|
||||
True
|
||||
"""
|
||||
if differential:
|
||||
last = None
|
||||
for i, e in enumerate(iterable):
|
||||
if i > 0:
|
||||
if not eq(last + differential, e):
|
||||
return False
|
||||
last = e
|
||||
return True
|
||||
return eq(*iterable)
|
||||
|
||||
|
||||
|
||||
def sums(iterable:Iterable[complex], flip:bool=False) -> generator:
|
||||
"""
|
||||
Yield the sum of each element of an iterable and its predecessor
|
||||
example:
|
||||
>>> [*sums('abc')]
|
||||
['ba', 'cb']
|
||||
>>> [*sums('abc', True)]
|
||||
['ab', 'bc']
|
||||
"""
|
||||
consumable = iter(regurge(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:
|
||||
"""
|
||||
Yield the quotient of each element of an iterable by its predecessor
|
||||
example:
|
||||
>>> [*quots(range(1, 4))]
|
||||
[2.0, 1.5]
|
||||
>>> [*quots(range(1, 4), True)]
|
||||
[0.5, 0.6666666666666666]
|
||||
"""
|
||||
consumable = iter(regurge(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:
|
||||
"""
|
||||
Yield the product of each element of an iterable by its predecessor
|
||||
example:
|
||||
>>> [*prods(range(1, 4))]
|
||||
[2, 6]
|
||||
>>> [*prods(range(1, 4), True)]
|
||||
[2, 6]
|
||||
"""
|
||||
consumable = iter(regurge(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:
|
||||
"""
|
||||
Yield the log of each element of an iterable in the base of its predecessor
|
||||
example:
|
||||
>>> [*logs(range(2, 5))]
|
||||
[1.5849625007211563, 1.2618595071429148]
|
||||
>>> [*logs(range(2, 5), True)]
|
||||
[0.6309297535714574, 0.7924812503605781]
|
||||
"""
|
||||
consumable = iter(regurge(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:
|
||||
"""
|
||||
Yield the log of each element of an iterable in the base of its predecessor
|
||||
example:
|
||||
>>> [*cumsum(range(4))]
|
||||
[0, 1, 3, 6]
|
||||
>>> [*cumsum(range(4), False)]
|
||||
[1, 3, 6]
|
||||
"""
|
||||
consumable = iter(regurge(iterable))
|
||||
last = 0
|
||||
if first:
|
||||
yield last
|
||||
for i in consumable:
|
||||
last += i
|
||||
yield last
|
||||
|
||||
def cumdif(iterable:Iterable[complex], first:bool=True) -> generator:
|
||||
"""
|
||||
Yield the log of each element of an iterable in the base of its predecessor
|
||||
example:
|
||||
>>> [*cumdif(range(4))]
|
||||
[0, -1, -3, -6]
|
||||
>>> [*cumdif(range(4), False)]
|
||||
[-1, -3, -6]
|
||||
"""
|
||||
consumable = iter(regurge(iterable))
|
||||
last = next(consumable)
|
||||
if first:
|
||||
yield last
|
||||
for i in consumable:
|
||||
last -= i
|
||||
yield last
|
||||
|
||||
def cumprod(iterable:Iterable[complex], first:bool=True) -> generator:
|
||||
"""
|
||||
Yield the cumulative product of the elemements of an iterable
|
||||
example:
|
||||
>>> [*cumprod(range(1, 4))]
|
||||
[1, 2, 6]
|
||||
>>> [*cumprod(range(1, 4), False)]
|
||||
[2, 6]
|
||||
"""
|
||||
consumable = iter(regurge(iterable))
|
||||
last = next(consumable)
|
||||
if first:
|
||||
yield last
|
||||
for i in consumable:
|
||||
last *= i
|
||||
yield last
|
||||
|
||||
def cumquot(iterable:Iterable[complex], first:bool=True) -> generator:
|
||||
"""
|
||||
Yield the cumulative quotient of the elemements of an iterable
|
||||
example:
|
||||
>>> [*cumquot(range(1, 4))]
|
||||
[1, 0.5, 0.16666666666666666]
|
||||
>>> [*cumquot(range(1, 4), False)]
|
||||
[0.5, 0.16666666666666666]
|
||||
"""
|
||||
consumable = iter(regurge(iterable))
|
||||
last = next(consumable)
|
||||
if first:
|
||||
yield last
|
||||
for i in consumable:
|
||||
last /= i
|
||||
yield last
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
# def chooser(iterable:Iterable, *)
|
||||
|
||||
|
||||
def dupes(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:
|
||||
if set to false, all duplicate copies of the element shall be yielded
|
||||
:key:
|
||||
the function/type to use as the key for the sorting function (sorted(array, key=key))
|
||||
eg
|
||||
>>> [*dupes([1,2,2,2,1,3])]
|
||||
[1, 2]
|
||||
>>> [*dupes([1,2,2,2,1,3], False)]
|
||||
[1, 2, 2]
|
||||
"""
|
||||
array = iter(sorted(array, key=key))
|
||||
last_seen = next(array)
|
||||
last_yield = None
|
||||
for i in array:
|
||||
if i==last_seen:
|
||||
if once:
|
||||
if i!=last_yield:
|
||||
yield i
|
||||
else:
|
||||
yield i
|
||||
last_yield = i
|
||||
last_seen = i
|
||||
|
||||
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
|
||||
:fill:
|
||||
the default value of any
|
||||
eg:
|
||||
>>> [*slices('abc', 2, None)]
|
||||
[('a', 'b'), ('c', None)]
|
||||
>>> [*filter(all, slices('abc', 2, None))]
|
||||
[('a', 'b')]
|
||||
"""
|
||||
itr = iter(iterable)
|
||||
while (main:=[*islice(itr, 0, length)]):
|
||||
main += [fill for i in range(length-len(main))]
|
||||
yield tuple(main)
|
||||
|
||||
if __name__ == "__main__":
|
||||
# pass
|
||||
for i in range(1,10):
|
||||
print([*range(i)])
|
||||
print(median(range(i)))
|
|
@ -0,0 +1,371 @@
|
|||
# A house for small stones
|
||||
|
||||
from functools import lru_cache, reduce
|
||||
from itertools import chain, combinations, count
|
||||
from math import pi, ceil
|
||||
from typing import Iterable
|
||||
|
||||
from .types import generator
|
||||
|
||||
def pyramid(length:int, shift:int=0) -> float:
|
||||
"""
|
||||
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):
|
||||
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))))
|
||||
n = int(n) - 1
|
||||
bank = []
|
||||
track = 2
|
||||
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 succ(n:int) -> int:
|
||||
"""Returns the successor of the input number
|
||||
Dependencies: N/A
|
||||
In: (number)
|
||||
Out: 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: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"""
|
||||
while a != b:
|
||||
if a > b:
|
||||
a -= b
|
||||
else:
|
||||
b -= a
|
||||
return a
|
||||
|
||||
|
||||
def gcd(*args:[int, tuple]) -> int:
|
||||
"""
|
||||
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):
|
||||
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]
|
||||
|
||||
|
||||
def eratosthenes(n:int, imaginarypart:bool=False) -> 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)
|
||||
rack = range(2, n)
|
||||
marked = set()
|
||||
primes = set()
|
||||
for i in rack:
|
||||
if i not in marked:
|
||||
multiples = {j for j in rack if j%i==0 and j>i}
|
||||
marked.update(multiples)
|
||||
yield i
|
||||
|
||||
|
||||
def factors0(n:int) -> 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 {}
|
||||
for p in primes:
|
||||
if n%p==0:
|
||||
e = 1
|
||||
while n%p**e==0:
|
||||
facts.add(p**e)
|
||||
e += 1
|
||||
if facts == {n, 1}:
|
||||
yield from facts
|
||||
else:
|
||||
for numbers in chain.from_iterable(combinations(facts, r) for r in range(1, len(facts))):
|
||||
if n%pipe(numbers)==0:
|
||||
facts.add(pipe(numbers))
|
||||
yield from facts
|
||||
|
||||
|
||||
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])
|
||||
if all(isinstance(i, int) or i==int(i) for i in args):
|
||||
# facs = []
|
||||
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
|
||||
|
||||
|
||||
@lru_cache(maxsize = 500)
|
||||
def factorial(n:int) -> int:
|
||||
"""
|
||||
Return n! for any integer
|
||||
Dependencies: lru_cache(from functools)
|
||||
In: (int)
|
||||
Out: int
|
||||
"""
|
||||
if n>=0:
|
||||
k = 1
|
||||
while n:
|
||||
k *= n
|
||||
n -= 1
|
||||
return k
|
||||
else:
|
||||
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 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"""
|
||||
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 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 isPerfect(n:int) -> bool:
|
||||
"""Returns a Boolean evaluation of an integer's Arithmetic Perfection
|
||||
Dependencies: sigma, factors (both from meta)
|
||||
In: Integer
|
||||
Out: Boolean"""
|
||||
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"""
|
||||
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"""
|
||||
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 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"""
|
||||
# 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 = []
|
||||
while len(str(n)) > 1:
|
||||
digitList = [int(i) for i in "".join(str(n).split('.'))]
|
||||
n = pipe(digitList)
|
||||
cache.append(n)
|
||||
return len(cache)
|
||||
|
||||
|
||||
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"""
|
||||
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 = []
|
||||
while len(str(n)) > 1:
|
||||
digitList = [int(i) for i in "".join(str(n).split('.'))]
|
||||
n = sigma(digitList)
|
||||
cache.append(n)
|
||||
return len(cache)
|
||||
|
||||
|
||||
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"""
|
||||
interval = [i for i in range(b) if i > a]
|
||||
for num in interval:
|
||||
a += num
|
||||
return a+b
|
||||
|
||||
|
||||
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)
|
||||
0.6666666666666666
|
||||
"""
|
||||
v = 1/v if isinstance(v, int) else v
|
||||
n = ceil(v)
|
||||
p = str(v).replace('.', '')
|
||||
for i, j in enumerate(p):
|
||||
n -= int(j)*10**-i
|
||||
return n
|
||||
|
||||
|
||||
def root(value:complex, power:complex=2) -> complex:
|
||||
"""
|
||||
Get the root of a number
|
||||
|
||||
rt(complex(1,1))
|
||||
(1.0986841134678098+0.45508986056222733j)
|
||||
rt(complex(1,1),3)
|
||||
(1.0842150814913512+0.2905145555072514j)
|
||||
"""
|
||||
return value**(1/power)
|
||||
|
||||
|
||||
def odds(n:int=-1) -> generator:
|
||||
"""
|
||||
Yield the first n odd numbers, use a negative value for all of them
|
||||
"""
|
||||
ctr = count()
|
||||
while n:
|
||||
m = next(ctr)
|
||||
if m % 2:
|
||||
yield m
|
||||
n -= 1
|
||||
|
||||
def evens(n:int=-1) -> generator:
|
||||
"""
|
||||
Yield the first n even numbers, use a negative value for all of them
|
||||
"""
|
||||
ctr = count()
|
||||
while n:
|
||||
m = next(ctr)
|
||||
if not m % 2:
|
||||
yield m
|
||||
n -= 1
|
||||
|
||||
def congrues(n:int, modulus:int=6, cls:int=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:
|
||||
"""
|
||||
Generate a sequence of multiples of a root and a base. By default it will yield the doubles sequence of the root.
|
||||
"""
|
||||
counter = count(start=start, step=step)
|
||||
while terms:
|
||||
yield root * base**next(counter)
|
||||
terms -= 1
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pass
|
|
@ -0,0 +1,71 @@
|
|||
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)
|
|
@ -0,0 +1,220 @@
|
|||
from typing import Any, Iterable
|
||||
from math import log
|
||||
from functools import lru_cache, reduce
|
||||
from itertools import tee
|
||||
|
||||
from .strings import alphabet
|
||||
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)
|
||||
weights = {j: freq(j, iterable)/len(consumable) for j in set(consumable)}
|
||||
return -sigma([val*log(val, 2) for val in weights.values()])
|
||||
|
||||
|
||||
def entropy(iterable:Iterable[Any], base:int=2, mode:str='kbdUS', space:str=None) -> float:
|
||||
"""
|
||||
Computes a modal entropy for a given iterable. Ints and floats will be converted to strings. Comma format ints will raise errors.
|
||||
Space determines the character you wish to interpret as space if inpt is a string
|
||||
"""
|
||||
abc = alphabet
|
||||
letters = ''.join(i for i in abc if i.isalpha() or i==' ')
|
||||
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)
|
||||
|
||||
iterates = hasattr(consumable,'__iter__')
|
||||
|
||||
if mode=="kbdUS":
|
||||
consumable = str(consumable) if not iterates else tuple(str(i) for i in consumable)
|
||||
chars = abc if space==None else abc
|
||||
weights = {i:prob(i, chars) for i in set(consumable)}
|
||||
return -sigma(val*log(val, base) for val in weights.values())
|
||||
|
||||
elif mode=='abc':
|
||||
consumable = str(consumable) if not iterates else tuple(str(i) for i in consumable)
|
||||
chars = letters if space==None else abc
|
||||
weights = {i:prob(i, chars) for i in set(consumable)}
|
||||
return -sigma(val*log(val, base) for val in weights.values())
|
||||
|
||||
elif mode=='num':
|
||||
consumable = str(consumable) if not iterates else tuple(str(i) for i in consumable)
|
||||
chars = digits if space==None else abc
|
||||
weights = {i:prob(i, chars) for i in set(consumable)}
|
||||
return -sigma(val*log(val, base) for val in weights.values())
|
||||
|
||||
elif mode=='shan':
|
||||
consumable = str(consumable) if not iterates else consumable
|
||||
chars = tuple(i for i in consumable)
|
||||
weights = {i:prob(i, chars) for i in set(consumable)}
|
||||
return -sigma(val*log(val, base) for val in weights.values())
|
||||
|
||||
|
||||
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)
|
||||
return freq(item, consumable) / len(consumable)
|
||||
|
||||
|
||||
def freq(element:Any, iterable:Iterable[Any], overlap:bool=False) -> int:
|
||||
"""
|
||||
Returns the number of appearences of some term in some collection
|
||||
:: greedy ::
|
||||
handy for scanning sequences of digits in an integer:
|
||||
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)
|
||||
if type(element)==type(consumable)==str:
|
||||
if not overlap:
|
||||
return len(consumable.split(element))-1
|
||||
else:
|
||||
return freq(tuple(i for i in element), walks(consumable, len(element)))
|
||||
return sum(1 for i in consumable if i==element)
|
||||
|
||||
|
||||
def expectation(iterable:Iterable[complex]) -> complex:
|
||||
"""
|
||||
Returns the expectation value of a collection
|
||||
"""
|
||||
consumable = regurge(iterable)
|
||||
consumable = list(consumable)
|
||||
exVal = 0
|
||||
for i in consumable:
|
||||
exVal += (i*(freq(i, consumable))/len(consumable))
|
||||
return exVal
|
||||
|
||||
|
||||
def mean(iterable:Iterable[complex]) -> complex:
|
||||
"""
|
||||
Returns the mean value of a collection
|
||||
"""
|
||||
consumable = regurge(iterable)
|
||||
consumable = list(consumable)
|
||||
meanVal = sum(consumable)
|
||||
meanVal /= len(consumable)
|
||||
return meanVal
|
||||
|
||||
|
||||
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)
|
||||
if len(consumable)%2:
|
||||
index = round((len(consumable)-1)/2)
|
||||
middle = consumable[index]
|
||||
else:
|
||||
index = round((len(consumable))/2)-1
|
||||
middle = sum(consumable[index:index+2])/2
|
||||
return middle
|
||||
|
||||
|
||||
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)
|
||||
return sum(max(consumable), min(consumable))/2
|
||||
|
||||
|
||||
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)
|
||||
return abs(median(consumable) - midpoint(consumable))
|
||||
|
||||
|
||||
def geomean(iterable:Iterable[complex]) -> complex:
|
||||
"""
|
||||
Returns the geometric mean of a collection
|
||||
"""
|
||||
consumable = regurge(iterable)
|
||||
consumable = list(consumable)
|
||||
geoMean = (sigma(iterable))**(1/len(iterable))
|
||||
return geoMean
|
||||
|
||||
|
||||
def harmean(iterable:Iterable[complex]) -> complex:
|
||||
"""
|
||||
Returns the harmonic mean of a collection
|
||||
"""
|
||||
consumable = regurge(iterable)
|
||||
consumable = list(consumable)
|
||||
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)
|
||||
|
||||
|
||||
def popdev(iterable:Iterable[complex]) -> complex:
|
||||
"""
|
||||
Returns the population standard deviation of a collection
|
||||
"""
|
||||
consumable = regurge(iterable)
|
||||
consumable = list(consumable)
|
||||
avg = mean(consumable)
|
||||
v1 = [(i-avg)**2 for i in consumable]
|
||||
v2 = ((reduce(lambda x, y: x+y, v1))/len(v1))**(1/2)
|
||||
return v2
|
||||
|
||||
|
||||
def samdev(iterable:Iterable[complex]) -> complex:
|
||||
"""
|
||||
Returns the sample standard deviation of a collection
|
||||
"""
|
||||
consumable = regurge(iterable)
|
||||
consumable = list(consumable)
|
||||
avg = mean(consumable)
|
||||
v1 = [(i-avg)**2 for i in consumable]
|
||||
v2 = ((sigma(v1))/(len(v1)-1))**(1/2)
|
||||
return v2
|
||||
|
||||
|
||||
def popvar(iterable:Iterable[complex]) -> complex:
|
||||
"""
|
||||
Returns the population variance for a collection
|
||||
"""
|
||||
consumable = regurge(iterable)
|
||||
consumable = list(consumable)
|
||||
avg = mean(consumable)
|
||||
deviations = [(i-avg)**2 for i in consumable]
|
||||
pv = sum(deviations)/len(deviations)
|
||||
return pv
|
||||
|
||||
|
||||
def samvar(iterable:Iterable[complex]) -> complex:
|
||||
"""
|
||||
Returns the sample variance for a collection
|
||||
"""
|
||||
consumable = regurge(iterable)
|
||||
consumable = list(consumable)
|
||||
avg = mean(consumable)
|
||||
v2 = [(i-v1)**2 for i in consumable]
|
||||
v3 = sigma(v2)/len(v2)
|
||||
return v3
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pass
|
|
@ -0,0 +1,189 @@
|
|||
from typing import Iterable, Any
|
||||
from itertools import chain
|
||||
import string, random
|
||||
|
||||
import pyperclip
|
||||
|
||||
from .types import generator, regurge
|
||||
|
||||
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
|
||||
Examples:
|
||||
>>> m3ta.show(
|
||||
map(
|
||||
join,
|
||||
(range(i) for i in range(1,5))
|
||||
)
|
||||
)
|
||||
0
|
||||
01
|
||||
012
|
||||
0123
|
||||
|
||||
# Also works as a closure using keyword arguments
|
||||
>>> m3ta.show(
|
||||
map(
|
||||
join(sep=', ',head='[',tail=']'),
|
||||
(range(i) for i in range(1,5))
|
||||
)
|
||||
)
|
||||
[0]
|
||||
[0, 1]
|
||||
[0, 1, 2]
|
||||
[0, 1, 2, 3]
|
||||
"""
|
||||
if iterable:
|
||||
return head + sep.join(map(str, iterable)) + tail
|
||||
else:
|
||||
def wrapper(iterable:Iterable[Any]):
|
||||
iterable = map(str, iterable)
|
||||
return head + sep.join(iterable) + tail
|
||||
return wrapper
|
||||
|
||||
def ascii(omissions:str='', include:bool=False) -> str:
|
||||
"""
|
||||
Return the ascii character base excluding the given omissions:
|
||||
"p" -> ' ' + punctuation
|
||||
"u" -> uppercase
|
||||
"l" -> lowercase
|
||||
"d" -> digits
|
||||
Feel free to omit combinations:
|
||||
ascii('lup')
|
||||
0123456789
|
||||
"""
|
||||
d = {
|
||||
"p":" "+string.punctuation,
|
||||
"u":string.ascii_uppercase,
|
||||
"l":string.ascii_lowercase,
|
||||
"d":string.digits,
|
||||
}
|
||||
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)
|
||||
|
||||
|
||||
asciis = kbd = abc = ascii
|
||||
alphabet = kbd()
|
||||
|
||||
|
||||
def emptyString(line:str) -> bool:
|
||||
"""
|
||||
Determine if a string is empty ('', ' ','\n','\t') or not
|
||||
"""
|
||||
return any(line==i for i in '* *\n*\t'.split('*'))
|
||||
|
||||
|
||||
def rewrite(string:str, charmap:dict={'A':'T', 'T':'A', 'C':'G', 'G':'C', 'a':'t', 't':'a', 'c':'g', 'g':'c'}, sep:str='') -> str:
|
||||
"""
|
||||
Given a sequence derived from 'ATCG', this function returns the complimentary base pairs of the given dna sequence
|
||||
Dependencies: None
|
||||
Arguments: permutation from 'ATCG'
|
||||
Out: compliment of input
|
||||
"""
|
||||
return sep.join([charmap[string[i]] for i in range(len(string))])
|
||||
|
||||
|
||||
def monoalphabetic(message:str, shift:int, alphabet:str=kbd(), space:str=None) -> str:
|
||||
"""
|
||||
A simple implementation of the monoalphabetic cipher.
|
||||
By convention, use a:
|
||||
Positive integer shift if you want to encode
|
||||
Negative integer if you want to decode
|
||||
"""
|
||||
words = message.split(space)
|
||||
for index, word in enumerate(words):
|
||||
new = ''
|
||||
for letter in word:
|
||||
if decode:
|
||||
substitute = alphabet[(alphabet.index(letter) - shift) % len(alphabet)]
|
||||
else:
|
||||
substitute = alphabet[(alphabet.index(letter) + shift) % len(alphabet)]
|
||||
new += substitute
|
||||
words[index] = new
|
||||
if space == None:
|
||||
space = ' '
|
||||
return space.join(words)
|
||||
|
||||
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 memespace(string:str, spaces:int=1, keep_spaces:bool=False, copy:bool=False):
|
||||
"""
|
||||
Aestheicize a string
|
||||
eg:
|
||||
>>> memespace('so edgy')
|
||||
s o e d g y
|
||||
>>> memespace('so edgy', 2)
|
||||
s o e d g y
|
||||
>>> memespace('so edgy', 2, True)
|
||||
s o e d g y
|
||||
"""
|
||||
string = string if keep_spaces else join(string.split())
|
||||
out = ''
|
||||
for i, j in enumerate(string, 1):
|
||||
out += j
|
||||
out += ' '*spaces if i<len(string) else ''
|
||||
pyperclip.copy(out) if copy else None
|
||||
return out
|
||||
|
||||
def memecase(string, copy:bool=False):
|
||||
"""
|
||||
Randomize the case of text in strings
|
||||
>>> memecase('something about narwhals, bacon, and midnight')
|
||||
sOmeTHINg aBOuT nArwHaLS, BAcoN, aND mIDNighT
|
||||
"""
|
||||
out = ''
|
||||
for i in string:
|
||||
out += random.choice([i.upper(), i.lower()])
|
||||
pyperclip.copy(out) if copy else None
|
||||
return out
|
||||
|
||||
|
||||
def sinusize(string:str, copy:bool=False):
|
||||
"""
|
||||
Map the characters of a string to critical and solvent points of a sinusoid
|
||||
eg
|
||||
>>> sinusize('hello_world')
|
||||
e _ l
|
||||
h l o w r d
|
||||
l o
|
||||
"""
|
||||
mat = [[] for i in range(3)]
|
||||
for i, j in enumerate(string):
|
||||
if not i%2:
|
||||
mat[1].append(j)
|
||||
mat[0].append(' ')
|
||||
mat[2].append(' ')
|
||||
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(' ')
|
||||
out = join([join(line) for line in mat], '\n')
|
||||
pyperclip.copy(out) if copy else None
|
||||
return out
|
|
@ -0,0 +1,45 @@
|
|||
import time, random, string
|
||||
|
||||
import pyperclip, psutil
|
||||
|
||||
|
||||
|
||||
def agora(string:bool=False):
|
||||
"""
|
||||
Here's the local time, mr wolf!
|
||||
Dependencies: time
|
||||
In: string=False
|
||||
Out: tuple/string(HH:MM:SS)
|
||||
"""
|
||||
now = tuple(time.localtime())[3:-3]
|
||||
return now if not string else ':'.join(str(i) for i in now)
|
||||
|
||||
|
||||
def kill(process:str, casefold=True):
|
||||
"""
|
||||
Kill all processes of the argued process name
|
||||
Dependencies: psutil
|
||||
In: "process_name.exe"[str],casefold=True[bool]
|
||||
Out: None
|
||||
Inspired by Giampaolo Rodolà from StackOverflow
|
||||
http://stackoverflow.com/questions/2940858/ddg#4230226
|
||||
"""
|
||||
import psutil
|
||||
if casefold:
|
||||
[p.kill() for p in psutil.process_iter() if p.name().casefold()==process.casefold()+('.exe', '')[process.endswith('.exe')]]
|
||||
else:
|
||||
[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:
|
||||
"""
|
||||
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)
|
||||
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)
|
|
@ -0,0 +1,217 @@
|
|||
__all__ = 'generator module function SineWave defaultdict'.split()
|
||||
|
||||
from collections import defaultdict
|
||||
from typing import Iterable, Any, Iterator, Sequence
|
||||
from copy import deepcopy
|
||||
from itertools import tee, _tee
|
||||
import os
|
||||
|
||||
|
||||
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:
|
||||
"""
|
||||
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 __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):
|
||||
""""""
|
||||
def __repr__(self):
|
||||
pairs = (': '.join(map(str, pair)) for pair in self.items())
|
||||
rack = ',\n '.join(pairs)
|
||||
return "{ " + rack + "\n}"
|
||||
def __str__(self):
|
||||
return self.__repr__()
|
||||
|
||||
|
||||
|
||||
class SineWave:
|
||||
pass
|
|
@ -0,0 +1,54 @@
|
|||
from email.message import EmailMessage
|
||||
import os, smtplib, subprocess
|
||||
|
||||
import pyperclip
|
||||
|
||||
|
||||
|
||||
def getIP(copy:bool=False) -> str:
|
||||
"""
|
||||
Returns the IP address of the device on which the function is called
|
||||
Dependencies: subprocess.check_result, pyperclip.copy
|
||||
Arguments: copy=False
|
||||
Output: ipAddress [str]
|
||||
"""
|
||||
call = str(subprocess.check_output('ipconfig')).split('\\n')
|
||||
line = [l for l in call if 'ipv4' in l.lower()][0]
|
||||
address = line.strip().strip('. ').strip('\\r').split(': ')[1]
|
||||
if copy: pyperclip.copy(address)
|
||||
return address
|
||||
|
||||
|
||||
def quickMail(msg:str, dst:str=os.environ.get('personalEmail'), subj:str=None, head:str=None, extra:str=None, att:str=None) -> None:
|
||||
"""
|
||||
Sends a short email to the given dst using gmail's smtp configuration with default sender/receiver info taken from user environment variables
|
||||
Dependencies: os.environ.get [func], smtplib [mod], email.message.EmailMessage [obj]
|
||||
Arguments: message, **destination
|
||||
Output: None
|
||||
"""
|
||||
message = EmailMessage()
|
||||
message['Subject'] = f'{subj if subj!=None else head if head!=None else ""}'
|
||||
message['From'] = os.environ.get('promoEmail')
|
||||
message['To'] = dst
|
||||
message.set_content(msg)
|
||||
if any([subj!=None,head!=None,extra!=None]):
|
||||
message.add_alternative(f"""\
|
||||
<!DOCTYPE html>
|
||||
<html style='font-family:trebuchet ms;'>
|
||||
<body>
|
||||
<h1 style="color:SlateGray;">{head if head!=None else ''}</h1>
|
||||
<p>{msg}</p>
|
||||
<P>{extra if extra!=None else ''}
|
||||
</body>
|
||||
</html>
|
||||
""", subtype='html')
|
||||
if att != None:
|
||||
for file in att.split('*'):
|
||||
with open(file, "rb") as f:
|
||||
fileData = f.read()
|
||||
fileName = f.name.split(os.sep)[-1]
|
||||
fileType = (os.path.splitext(f.name)[1]).replace(".","")
|
||||
message.add_attachment(fileData, maintype="image", subtype=fileType, filename=fileName)
|
||||
with smtplib.SMTP_SSL('smtp.gmail.com', 465) as smtp:
|
||||
smtp.login(os.environ.get('promoEmail'), os.environ.get('promoEmailPass'))
|
||||
smtp.send_message(message)
|
Loading…
Reference in New Issue