Bring in a (bugged) proquint
This commit is contained in:
parent
4a46ea97a1
commit
97e3917a7f
4 changed files with 191 additions and 0 deletions
3
projects/proquint/README.md
Normal file
3
projects/proquint/README.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# Proquint
|
||||||
|
|
||||||
|
An alternative implementation to https://github.com/dsw/proquint/tree/master/python, which is kinda garbo.
|
105
projects/proquint/proquint.py
Normal file
105
projects/proquint/proquint.py
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
"""Proquint - pronounceable codings of integers.
|
||||||
|
|
||||||
|
Implemented from http://arxiv.org/html/0901.4016
|
||||||
|
"""
|
||||||
|
|
||||||
|
from functools import cache
|
||||||
|
|
||||||
|
|
||||||
|
class Proquint(object):
|
||||||
|
# Class parameters
|
||||||
|
################################################################################################
|
||||||
|
CONSONANTS = "bdfghjklmnprstvz"
|
||||||
|
VOWELS = "aiou"
|
||||||
|
BYTEORDER = "big"
|
||||||
|
|
||||||
|
# Implementation helpers
|
||||||
|
################################################################################################
|
||||||
|
@classmethod
|
||||||
|
@cache
|
||||||
|
def _consonant_to_uint(cls, c: str) -> int:
|
||||||
|
if idx := cls.CONSONANTS.index(c) == -1:
|
||||||
|
raise KeyError
|
||||||
|
return idx
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
@cache
|
||||||
|
def _vowel_to_uint(cls, c: str) -> int:
|
||||||
|
if idx := cls.VOWELS.index(c) == -1:
|
||||||
|
raise KeyError
|
||||||
|
return idx
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _encode(cls, buffer: bytes) -> str:
|
||||||
|
for n, m in zip(buffer[0::2], buffer[1::2]):
|
||||||
|
n = n << 16 | m
|
||||||
|
c1 = n & 0x0F
|
||||||
|
v1 = (n >> 4) & 0x03
|
||||||
|
c2 = (n >> 6) & 0x0F
|
||||||
|
v2 = (n >> 10) & 0x03
|
||||||
|
c3 = (n >> 12) & 0x0F
|
||||||
|
|
||||||
|
yield f"{cls.CONSONANTS[c1]}{cls.VOWELS[v1]}{cls.CONSONANTS[c2]}{cls.VOWELS[v2]}{cls.CONSONANTS[c3]}"
|
||||||
|
|
||||||
|
# Core methods
|
||||||
|
################################################################################################
|
||||||
|
@classmethod
|
||||||
|
def encode_bytes(cls, buffer: bytes) -> str:
|
||||||
|
"""Encode a sequence of bytes into a proquint string.
|
||||||
|
|
||||||
|
>>>
|
||||||
|
"""
|
||||||
|
|
||||||
|
return "-".join(cls._encode(buffer))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def decode(cls, buffer: str) -> int:
|
||||||
|
"""Convert proquint string identifier into corresponding 32-bit integer value.
|
||||||
|
|
||||||
|
>>> hex(Proquint.decode('lusab-babad'))
|
||||||
|
'0x7F000001'
|
||||||
|
"""
|
||||||
|
|
||||||
|
res = 0
|
||||||
|
|
||||||
|
for i, c in enumerate([c for c in buffer if c != '-']):
|
||||||
|
if mag := cls._consonant_to_uint(c) is not None:
|
||||||
|
res <<= 4
|
||||||
|
res += mag
|
||||||
|
else:
|
||||||
|
mag = cls._vowel_to_uint(c)
|
||||||
|
if mag is not None:
|
||||||
|
res <<= 2
|
||||||
|
res += mag
|
||||||
|
elif i != 5:
|
||||||
|
raise ValueError('Bad proquint format')
|
||||||
|
return res
|
||||||
|
|
||||||
|
# Handy aliases
|
||||||
|
################################################################################################
|
||||||
|
@classmethod
|
||||||
|
def encode(cls, val: int, width: int, byteorder=BYTEORDER):
|
||||||
|
"""Encode an integer into a proquint string."""
|
||||||
|
|
||||||
|
if width % 8 != 0 or width < 8:
|
||||||
|
raise ValueError(f"Width must be a positive power of 2 greater than 8")
|
||||||
|
|
||||||
|
return cls.encode_bytes(val.to_bytes(width // 8, byteorder))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def encode_i16(cls, val: int):
|
||||||
|
"""Encode a 16bi int to a proquint string."""
|
||||||
|
|
||||||
|
return cls.encode(val, 16)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def encode_i32(cls, val: int):
|
||||||
|
"""Encode a 32bi int to a proquint string."""
|
||||||
|
|
||||||
|
return cls.encode(val, 32)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def encode_i64(cls, val: int):
|
||||||
|
"""Encode a 64bi int into a proquint string."""
|
||||||
|
|
||||||
|
return cls.encode(val, 64)
|
13
projects/proquint/setup.cfg
Normal file
13
projects/proquint/setup.cfg
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
[metadata]
|
||||||
|
# This includes the license file(s) in the wheel.
|
||||||
|
# https://wheel.readthedocs.io/en/stable/user_guide.html#including-license-files-in-the-generated-wheel-file
|
||||||
|
license_files = LICENSE.txt
|
||||||
|
|
||||||
|
[bdist_wheel]
|
||||||
|
# This flag says to generate wheels that support both Python 2 and Python
|
||||||
|
# 3. If your code will not run unchanged on both Python 2 and 3, you will
|
||||||
|
# need to generate separate wheels for each Python version that you
|
||||||
|
# support. Removing this line (or setting universal to 0) will prevent
|
||||||
|
# bdist_wheel from trying to make a universal wheel. For more see:
|
||||||
|
# https://packaging.python.org/guides/distributing-packages-using-setuptools/#wheels
|
||||||
|
universal=1
|
70
projects/proquint/setup.py
Normal file
70
projects/proquint/setup.py
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
"""A setuptools based setup module.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# io.open is needed for projects that support Python 2.7
|
||||||
|
# It ensures open() defaults to text mode with universal newlines,
|
||||||
|
# and accepts an argument to specify the text encoding
|
||||||
|
# Python 3 only projects can skip this import
|
||||||
|
from io import open
|
||||||
|
from os import path
|
||||||
|
|
||||||
|
# Always prefer setuptools over distutils
|
||||||
|
from setuptools import find_packages, setup
|
||||||
|
|
||||||
|
|
||||||
|
here = path.abspath(path.dirname(__file__))
|
||||||
|
|
||||||
|
# Get the long description from the README file
|
||||||
|
with open(path.join(here, "README.md"), encoding="utf-8") as f:
|
||||||
|
long_description = f.read()
|
||||||
|
|
||||||
|
# Arguments marked as "Required" below must be included for upload to PyPI.
|
||||||
|
# Fields marked as "Optional" may be commented out.
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name="proquint", # Required
|
||||||
|
version="0.1.0", # Required
|
||||||
|
description="Enunciable numerics",
|
||||||
|
long_description=long_description, # Optional
|
||||||
|
long_description_content_type="text/markdown", # Optional (see note above)
|
||||||
|
url="https://github.com/arrdem/source",
|
||||||
|
author="Reid 'arrdem' McKenzie",
|
||||||
|
author_email="me@arrdem.com",
|
||||||
|
classifiers=[
|
||||||
|
# Optional
|
||||||
|
# https://pypi.org/pypi?%3Aaction=list_classifiers
|
||||||
|
"Development Status :: 3 - Alpha",
|
||||||
|
"Intended Audience :: Developers",
|
||||||
|
"License :: OSI Approved :: BSD License",
|
||||||
|
"Programming Language :: Python :: 3.5",
|
||||||
|
],
|
||||||
|
# This field adds keywords for your project which will appear on the
|
||||||
|
# project page. What does your project relate to?
|
||||||
|
#
|
||||||
|
# Note that this is a string of words separated by whitespace, not a list.
|
||||||
|
keywords="sample setuptools development", # Optional
|
||||||
|
# You can just specify package directories manually here if your project is
|
||||||
|
# simple. Or you can use find_packages().
|
||||||
|
#
|
||||||
|
# Alternatively, if you just want to distribute a single Python file, use
|
||||||
|
# the `py_modules` argument instead as follows, which will expect a file
|
||||||
|
# called `my_module.py` to exist:
|
||||||
|
#
|
||||||
|
# py_modules=["my_module"],
|
||||||
|
#
|
||||||
|
packages=find_packages(exclude=["docs", "tests"]),
|
||||||
|
python_requires=">=3.5",
|
||||||
|
# List additional groups of dependencies here (e.g. development
|
||||||
|
# dependencies). Users will be able to install these using the "extras"
|
||||||
|
# syntax, for example:
|
||||||
|
#
|
||||||
|
# $ pip install sampleproject[dev]
|
||||||
|
#
|
||||||
|
# Similar to `install_requires` above, these must be valid existing
|
||||||
|
# projects.
|
||||||
|
extras_require={ # Optional
|
||||||
|
"dev": ["check-manifest"],
|
||||||
|
"test": ["pytest", "hypothesis"],
|
||||||
|
},
|
||||||
|
)
|
Loading…
Reference in a new issue