Package hosh
hosh solves the problem of determining the identity of multi-valued objects or sequences of events (and provide extra modules for group theory)
Expand source code
# Copyright (c) 2021. Davi Pereira dos Santos
# This file is part of the hosh project.
# Please respect the license - more about this in the section (*) below.
#
# hosh is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# hosh is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with hosh. If not, see <http://www.gnu.org/licenses/>.
#
# (*) Removing authorship by any means, e.g. by distribution of derived
# works or verbatim, obfuscated, compiled or rewritten versions of any
# part of this work is illegal and is unethical regarding the effort and
# time spent here.
"""hosh solves the problem of determining the identity of multi-valued objects or sequences of events
(and provide extra modules for group theory)"""
from .config import setup
from .groups import *
from .hosh_ import Hosh as H
from .misc.helper import Helper
from .misc.identity import ø, Identity
__pdoc__ = {
"hosh": False,
}
Hosh = H
"""All identifiers are instances of this class"""
ħ = Helper(UT40_4)
"""UTF-8 shortcut to create 40-digit Hosh objects (AltGr+H in most keyboards)
Other options are also available: ħ16, ħ32, ħ40, ħ64"""
identity = ø()
"""Shortcut to the 40-digit identity Hosh object"""
ø = identity
"""UTF-8 shortcut to the 40-digit identity Hosh object (AltGr+O in most keyboards)
Other options are also available: ø16, ø32, ø40, ø64"""
ħ16, ħ32, ħ40, ħ64 = [Helper(version) for version in groups.values()]
ø16, ø32, ø40, ø64 = [Identity(version) for version in groups.values()]
def rho_elem(index, asbin=False):
"""
>>> rho_elem(1).id
'L9.Vd4B6O6z0WdCgyk4mx1v118i91N.DkR7nS5Ua'
>>> rho_elem(2).id
'Od4uNccS.19C9PuEHnzB-VK0GKFajq5VJUJi1rCf'
>>> rho_elem(2, asbin=True)
b'<RESERVED ELEMENT: rho_2>'
"""
r = f"<RESERVED ELEMENT: rho_{index}>".encode()
return r if asbin else Hosh(r)
def removal_elem(field, asbin=False):
"""
>>> removal_elem("_myfield").id
'zZgRpxed-6aboKQ39.v5.HfBjr3j8WJlDwYVvUpi'
>>> removal_elem("myfield").id
'eE-IoUO1EiZkxKkRyrXJcgXHc0pTD2As7IODrxTc'
>>> removal_elem("myfield", asbin=True)
b'<RESERVED ELEMENT: DELETE VALUE AT FIELD myfield>'
"""
r = f"<RESERVED ELEMENT: DELETE VALUE AT FIELD {field}>".encode()
return r if asbin else Hosh(r)
Sub-modules
hosh.config
hosh.groups
hosh.hosh_
hosh.misc
-
Support modules are hidden here to avoid polluting the API
hosh.theme
Global variables
var identity
-
Shortcut to the 40-digit identity Hosh object
var ø
-
UTF-8 shortcut to the 40-digit identity Hosh object (AltGr+O in most keyboards)
Other options are also available: ø16, ø32, ø40, ø64
var ħ
-
UTF-8 shortcut to create 40-digit Hosh objects (AltGr+H in most keyboards)
Other options are also available: ħ16, ħ32, ħ40, ħ64
Functions
def removal_elem(field, asbin=False)
-
>>> removal_elem("_myfield").id 'zZgRpxed-6aboKQ39.v5.HfBjr3j8WJlDwYVvUpi' >>> removal_elem("myfield").id 'eE-IoUO1EiZkxKkRyrXJcgXHc0pTD2As7IODrxTc' >>> removal_elem("myfield", asbin=True) b'<RESERVED ELEMENT: DELETE VALUE AT FIELD myfield>'
Expand source code
def removal_elem(field, asbin=False): """ >>> removal_elem("_myfield").id 'zZgRpxed-6aboKQ39.v5.HfBjr3j8WJlDwYVvUpi' >>> removal_elem("myfield").id 'eE-IoUO1EiZkxKkRyrXJcgXHc0pTD2As7IODrxTc' >>> removal_elem("myfield", asbin=True) b'<RESERVED ELEMENT: DELETE VALUE AT FIELD myfield>' """ r = f"<RESERVED ELEMENT: DELETE VALUE AT FIELD {field}>".encode() return r if asbin else Hosh(r)
def rho_elem(index, asbin=False)
-
>>> rho_elem(1).id 'L9.Vd4B6O6z0WdCgyk4mx1v118i91N.DkR7nS5Ua' >>> rho_elem(2).id 'Od4uNccS.19C9PuEHnzB-VK0GKFajq5VJUJi1rCf' >>> rho_elem(2, asbin=True) b'<RESERVED ELEMENT: rho_2>'
Expand source code
def rho_elem(index, asbin=False): """ >>> rho_elem(1).id 'L9.Vd4B6O6z0WdCgyk4mx1v118i91N.DkR7nS5Ua' >>> rho_elem(2).id 'Od4uNccS.19C9PuEHnzB-VK0GKFajq5VJUJi1rCf' >>> rho_elem(2, asbin=True) b'<RESERVED ELEMENT: rho_2>' """ r = f"<RESERVED ELEMENT: rho_{index}>".encode() return r if asbin else Hosh(r)
Classes
class Hosh (content, etype='default:ordered', version=Group(p=1099511627689, p4=1461501636868331575725436266114840805196834679841, p6=1766847063939562670646036165286872353986524172769430561878277294118845361, digits=40, bytes=30, firstp='0_100000000_____________________________', lastp='f_8afffffff_____________________________', firstp4='00_1000000000000000000000000000000000000', lastp4='.._87c2a630003eec7dffff561b0000004aeffff', firstp6='1000000000000000000000000000000000000000', lastp6='g-8KOjCQREq2Vz8VTc30gLMd..vvX6000ov.....'))
-
Operable hash.
Generate a Hosh object from a binary content or a list of 6 ints.
Usage:
>>> from hosh import Hosh >>> a = Hosh(b"lots of data") >>> b = Hosh(b"lots of data 2") >>> a.id '.-0byLo.CdKjKN6RFTYqBIy30OST3oLyjYPf.6p8' >>> b.id 'SXBse5Ie-yUCa7h7gZiHXGkkKdispqxlc4FnCYit' >>> (a * b).id 'ALiaB9XPu.MoIwwoTPYrxqkGfVpktOgUv0tDB3IB' >>> (b * a).id 'gr9psTs5dYGrCCdgWMAPWM4dDDzktOgUv0tDB3IB' >>> a * b * ~b == a True >>> c = Hosh(b"lots of data 3") >>> (a * b) * c == a * (b * c) True >>> e = Hosh(b"lots of data 4") >>> f = Hosh(b"lots of data 5") >>> e * f != f * e True >>> a * b != b * a True >>> x = Hosh(b"lots of data 6", "hybrid") >>> y = Hosh(b"lots of data 7", "hybrid") >>> z = Hosh(b"lots of data 8", "unordered") >>> x * y == y * x True >>> x * a != a * x True >>> x * z == z * x True >>> a * z == z * a True >>> from hosh import ø >>> print(ø) # Handy syntax using ø for identity. 0000000000000000000000000000000000000000 >>> print(ø * "7ysdf98ys34hg543hdf98ysdf98ysdfysdf98ysd") # str, bytes or int are converted as id, blob or element rank. 7ysdf98ys34hg543hdf98ysdf98ysdfysdf98ysd >>> print(ø * "7ysdf98ysdf98ysdf98ysdfysdf98ysdasddsa32" * "6gdsf76dfqwe123de8gaf87gaf87gaf87agdfa78") 94UrdYKjCGQWdd5P.W4xvFJgc9hZpIHlhytqHkaa >>> h = ø.u * b"sdff" >>> print(h) f_9e1a267c8_____________________________ >>> x.id, (+x).id # Making an ordered x. ('ZN_60eec3e6c7b68087329e16b581401a6bb2b1f', '6BDj3b7Mmj7n-6B8XYaP3akO7400s9FlG4AtcHTp') >>> +x * y != y * +x True >>> ++x == x True >>> x ** y == +(+x * +y) # a ** b is a shortcut for +(+a * +b) True >>> x ** y != y ** x True >>> (x ** b"1") * (y ** b"2") != (x ** b"2") * (y ** b"1") True >>> (x ** b"1") * (y ** b"2") == (y ** b"2") * (x ** b"1") True >>> (x ** y) // y == x True >>> f + e - x == ø - x + e + f # Alternative (always unordered, i.e., form an Abelian group) operation True >>> import pickle >>> d = pickle.dumps(x, protocol=5) >>> d b'\x80\x05\x95j\x00\x00\x00\x00\x00\x00\x00\x8c\x08builtins\x94\x8c\x07getattr\x94\x93\x94\x8c\nhosh.hosh_\x94\x8c\x04Hosh\x94\x93\x94\x8c\x06fromid\x94\x86\x94R\x94\x8c(ZN_60eec3e6c7b68087329e16b581401a6bb2b1f\x94\x85\x94R\x94.' >>> pickle.loads(d) == x True
Parameters
content
- Binary content to be hashed, or a list of six integers
etype
- ordered, hybrid, unordered According to the subset of the desired element: Z, H\Z or G\H
version
- Group namedtuple: changes the number of digits and robustness against collisions UT32_4 is enough for most usages. It accepts more than 4 billion repetitions of the same operation in a row. UT64_4 provides unspeakable limits for operations, please see scientific paper for details. UT40_4 is recommended and default, since it is the most compatible with other systems (git, SHA-1, etc)
Expand source code
class Hosh: r""" Operable hash. Generate a Hosh object from a binary content or a list of 6 ints. Usage: >>> from hosh import Hosh >>> a = Hosh(b"lots of data") >>> b = Hosh(b"lots of data 2") >>> a.id '.-0byLo.CdKjKN6RFTYqBIy30OST3oLyjYPf.6p8' >>> b.id 'SXBse5Ie-yUCa7h7gZiHXGkkKdispqxlc4FnCYit' >>> (a * b).id 'ALiaB9XPu.MoIwwoTPYrxqkGfVpktOgUv0tDB3IB' >>> (b * a).id 'gr9psTs5dYGrCCdgWMAPWM4dDDzktOgUv0tDB3IB' >>> a * b * ~b == a True >>> c = Hosh(b"lots of data 3") >>> (a * b) * c == a * (b * c) True >>> e = Hosh(b"lots of data 4") >>> f = Hosh(b"lots of data 5") >>> e * f != f * e True >>> a * b != b * a True >>> x = Hosh(b"lots of data 6", "hybrid") >>> y = Hosh(b"lots of data 7", "hybrid") >>> z = Hosh(b"lots of data 8", "unordered") >>> x * y == y * x True >>> x * a != a * x True >>> x * z == z * x True >>> a * z == z * a True >>> from hosh import ø >>> print(ø) # Handy syntax using ø for identity. 0000000000000000000000000000000000000000 >>> print(ø * "7ysdf98ys34hg543hdf98ysdf98ysdfysdf98ysd") # str, bytes or int are converted as id, blob or element rank. 7ysdf98ys34hg543hdf98ysdf98ysdfysdf98ysd >>> print(ø * "7ysdf98ysdf98ysdf98ysdfysdf98ysdasddsa32" * "6gdsf76dfqwe123de8gaf87gaf87gaf87agdfa78") 94UrdYKjCGQWdd5P.W4xvFJgc9hZpIHlhytqHkaa >>> h = ø.u * b"sdff" >>> print(h) f_9e1a267c8_____________________________ >>> x.id, (+x).id # Making an ordered x. ('ZN_60eec3e6c7b68087329e16b581401a6bb2b1f', '6BDj3b7Mmj7n-6B8XYaP3akO7400s9FlG4AtcHTp') >>> +x * y != y * +x True >>> ++x == x True >>> x ** y == +(+x * +y) # a ** b is a shortcut for +(+a * +b) True >>> x ** y != y ** x True >>> (x ** b"1") * (y ** b"2") != (x ** b"2") * (y ** b"1") True >>> (x ** b"1") * (y ** b"2") == (y ** b"2") * (x ** b"1") True >>> (x ** y) // y == x True >>> f + e - x == ø - x + e + f # Alternative (always unordered, i.e., form an Abelian group) operation True >>> import pickle >>> d = pickle.dumps(x, protocol=5) >>> d b'\x80\x05\x95j\x00\x00\x00\x00\x00\x00\x00\x8c\x08builtins\x94\x8c\x07getattr\x94\x93\x94\x8c\nhosh.hosh_\x94\x8c\x04Hosh\x94\x93\x94\x8c\x06fromid\x94\x86\x94R\x94\x8c(ZN_60eec3e6c7b68087329e16b581401a6bb2b1f\x94\x85\x94R\x94.' >>> pickle.loads(d) == x True Parameters ---------- content Binary content to be hashed, or a list of six integers etype ordered, hybrid, unordered According to the subset of the desired element: Z, H\\Z or G\\H version Group namedtuple: changes the number of digits and robustness against collisions UT32_4 is enough for most usages. It accepts more than 4 billion repetitions of the same operation in a row. UT64_4 provides unspeakable limits for operations, please see scientific paper for details. UT40_4 is recommended and default, since it is the most compatible with other systems (git, SHA-1, etc) """ _n, _id, _ansi_light, _ansi_dark = None, None, None, None _sansi_light, _sansi_dark, _sid, _etype, _rgb_light, _rgb_dark = None, None, None, None, None, None _etype_inducer, _bits, _ø = None, None, None _rev = None components_cache_size = 100 def __init__(self, content, etype="default:ordered", version=UT40_4): self.version = version self.p, self.p4, self.p6, self.digits, self.bytes, _, _, _, _, _, _ = version self._composition_memo = {} if isinstance(content, list): if etype != "default:ordered": raise DanglingEtype(f"Cannot set etype={etype} when providing cells ({content}).") if max(content) >= self.p: raise CellValueTooHigh(f"A cell value exceeds the limit for the group: {max(content)} >= {self.p}") self.cells = content elif isinstance(content, bytes): if etype == "default:ordered": etype = "ordered" self.cells, self._id = cells_id_fromblob(content, etype, self.bytes, self.p) else: raise WrongContent( f"No valid content provided: {content}\n" f"It should be a bytes object to be hashed or a list of ints." ) @property def ø(self): """Identity element compatible with this Hosh object Usage: >>> from hosh import Hosh >>> (b := Hosh(b"23987rg23")).id 'J5.uRTue8X4r1xu.JFkPbURVVGvTRPSFLncXdyzj' >>> b.etype 'ordered' >>> b.ø.etype 'unordered' >>> b.etype == b.ø.etype_inducer True >>> b.ø.id '0000000000000000000000000000000000000000' >>> (b.ø * b"qwer").etype 'ordered' """ if self._ø is None: from hosh import Identity self._ø = Identity(version=self.version, etype_inducer=self.etype) return self._ø @property def rev(self): """ Reversed element (warning: this is not the inverse element) Element with the internal cells reversed. This operation is its own inverse. This is useful wherever a unary operation is needed. For instance, a function can be represented as a value by its original identifier, and can be represented as (an applied) function by its reversed element identifier. Not all hoshes are digest-reversible, i.e., at the digit level, due to the intrinsic mismatch between base 64 (i.e., a power of two) representation and the group (prime) order. Therefore, we must resort to reversing the cells. As an exception, unordered elements do have (most) digits reversed as it has only one internal cell. Probabilistically irrelevant corner cases: The presence of empty cells (i.e., with zero value) might cause migration from one etype to another. The presence of duplicate cells (i.e., with the same value) might make the hosh reverse to itself. See examples below. Usage: >>> from hosh import Hosh, groups >>> h = Hosh.fromid("J5.uRTue8X4r1xu.JFkPbURVVGvTRPSFLncXdyzj").rev >>> h.id 'lUz6uu1ZBCJf342R7-qKOOqWJgaf3TDHx2M.CWGT' >>> h.rev.rev == h True >>> h = Hosh.fromid("ab_cabcdefabcdefabcdefabcdefabcdefabcdef") >>> h.rev.id 'Hh_c7201818f76878562c52010943fe4f2a7f3b2' >>> h = Hosh.fromid("2_dbe78441d_____________________________") >>> h.rev.id '2_dbd14487e_____________________________' >>> # Limits of subgroup Z. >>> Hosh.fromid(groups[40].firstp).id, Hosh.fromid(groups[40].lastp).id ('0_100000000_____________________________', 'f_8afffffff_____________________________') >>> Hosh.fromid(groups[40].firstp).rev.id, Hosh.fromid(groups[40].lastp).rev.id ('0_100000000_____________________________', 'f_8afffffff_____________________________') >>> # Limits of subgroup H. >>> Hosh.fromid(groups[40].firstp4).id, Hosh.fromid(groups[40].lastp4).id ('00_1000000000000000000000000000000000000', '.._87c2a630003eec7dffff561b0000004aeffff') >>> Hosh.fromid(groups[40].firstp4).rev.id, Hosh.fromid(groups[40].lastp4).rev.id ('00_9ed100000015ffffffff00000000000000000', '.._87c2a630003eec7dffff561b0000004aeffff') >>> Hosh.fromid(groups[40].firstp4).cells, Hosh.fromid(groups[40].lastp4).cells ([0, 0, 0, 0, 1, 0], [0, 0, 1099511627688, 1099511627688, 1099511627688, 1099511627688]) >>> Hosh.fromid(groups[40].firstp4).rev.cells, Hosh.fromid(groups[40].lastp4).rev.cells ([0, 0, 0, 1, 0, 0], [0, 0, 1099511627688, 1099511627688, 1099511627688, 1099511627688]) >>> # Limits of group G. >>> Hosh.fromid(groups[40].firstp6).id, Hosh.fromid(groups[40].lastp6).id ('1000000000000000000000000000000000000000', 'g-8KOjCQREq2Vz8VTc30gLMd..vvX6000ov.....') >>> Hosh.fromid(groups[40].firstp6).rev.id, Hosh.fromid(groups[40].lastp6).rev.id ('00_1000000000000000000000000000000000000', 'g-8KOjCQREq2Vz8VTc30gLMd..vvX6000ov.....') >>> Hosh.fromid(groups[40].firstp6).cells, Hosh.fromid(groups[40].lastp6).cells ([0, 1, 0, 0, 0, 0], [1099511627688, 1099511627688, 1099511627688, 1099511627688, 1099511627688, 1099511627688]) >>> Hosh.fromid(groups[40].firstp6).rev.cells, Hosh.fromid(groups[40].lastp6).rev.cells ([0, 0, 0, 0, 1, 0], [1099511627688, 1099511627688, 1099511627688, 1099511627688, 1099511627688, 1099511627688]) >>> # Near limits of group G. >>> Hosh.fromn(groups[40].p4+1).id, Hosh.fromn(groups[40].p6 - 2).id ('2000000000000000000000000000000000000000', 'f-8KOjCQREq2Vz8VTc30gLMd..vvX6000ov.....') >>> Hosh.fromn(groups[40].p4+1).rev.id, Hosh.fromn(groups[40].p6 - 2).rev.id ('ihdwjXvMdIj40gZQq-..5Ai0000j-....3000000', '7XoPrombpt9-UXQnse30Cgud..fcZ6000kv.....') >>> Hosh.fromn(groups[40].p4+1).cells, Hosh.fromn(groups[40].p6 - 2).cells ([0, 1, 0, 0, 0, 1], [1099511627688, 1099511627688, 1099511627688, 1099511627688, 1099511627688, 1099511627687]) >>> Hosh.fromn(groups[40].p4+1).rev.cells, Hosh.fromn(groups[40].p6 - 2).rev.cells ([1, 0, 0, 0, 1, 0], [1099511627687, 1099511627688, 1099511627688, 1099511627688, 1099511627688, 1099511627688]) """ if self._rev is None: id = self.id if self.etype == "ordered": self._rev = Hosh(list(reversed(self.cells))) elif self.etype == "hybrid": self._rev = Hosh(self.cells[:2] + list(reversed(self.cells[2:]))) elif self.etype == "unordered": self._rev = Hosh.fromid(id[:4] + "".join(reversed(id[4:11])) + "_____________________________") else: # pragma: no cover raise Exception(f"Unexpected condition. element type: {self.etype}") return self._rev @property def etype(self): """ Type of this element Usage: >>> from hosh import Hosh >>> Hosh.fromn(5).etype 'unordered' Returns ------- 'ordered', 'hybrid' or 'unordered' """ if self._etype is None: if sum(self.cells[:5]) == 0: self._etype = "unordered" elif sum(self.cells[:2]) == 0: self._etype = "hybrid" else: self._etype = "ordered" return self._etype @property def etype_inducer(self): """ Type this element uses to coerce an element of undefined type. Usage: >>> from hosh import ø, Hosh >>> ø.etype_inducer 'ordered' >>> ø.h.etype_inducer 'hybrid' >>> ø.u.etype_inducer 'unordered' >>> Hosh(b"12124").etype_inducer 'ordered' >>> Hosh(b"12124", etype="hybrid").etype_inducer 'hybrid' Returns ------- 'ordered', 'hybrid', 'unordered' """ if self._etype_inducer is None: self._etype_inducer = self.etype return self._etype_inducer @property def id(self): """ Textual representation of this element Returns ------- Textual representation """ if self._id is None: self._id = id_fromcells(self.cells, self.digits, self.p) return self._id @classmethod def fromid(cls, id): """ Create an element from a textual id. Usage: >>> a = Hosh.fromid("abcdefabcdefabcdefabcdefabcdefab") >>> a.n 1094566309952642687224764830259410933250743749332933330234 >>> a.cells [748932665, 516513868, 468764361, 3316970622, 2727293743, 316029245] >>> a.etype 'ordered' >>> bid = a.id[:2] + "_" + a.id[3:] >>> bid 'ab_defabcdefabcdefabcdefabcdefab' >>> b = Hosh.fromid(bid) >>> b.id 'ab_defabcdefabcdefabcdefabcdefab' >>> b.n 59377482839139050825606534576063885287 >>> b.cells [0, 0, 749449200, 1774140626, 3139018916, 292801225] >>> b.etype 'hybrid' >>> Hosh.fromid("0000000000000000000000000000000000000000000000000000000000000000") == 0 True Parameters ---------- id Parameters ---------- id Returns ------- A new Hosh object """ if len(id) not in groups: raise WrongIdentifier(f"Wrong identifier length: {len(id)} id:[{id}]") return Hosh(cells_fromid(id, p=groups[len(id)].p), version=groups[len(id)]) @classmethod def fromn(cls, n: int, version=UT40_4): """ Create a Hosh object representing the given int. Default 'p' is according to version UT64.4. Usage: >>> h = Hosh.fromn(7647544756746324134134) >>> h.id '00_e49c1c505dcd0039e91000000000000000000' Parameters ---------- n version Returns ------- A new Hosh object """ p, order = version.p, version.p6 if n > order: raise ElementTooHigh(f"Element outside allowed range: {n} >= {order}") return Hosh(int2cells(n, p), version=version) @property def n(self): """ Lexicographic rank of this eloement (according to the format adopted in internal integer cells. Returns ------- Number """ if self._n is None: self._n = cells2int(self.cells, self.p) return self._n @property def sid(self): """ Shorter id (base-777 using up to 2 bytes utf8 per char) Usage: >>> from hosh import ø >>> (ø * b'65e987978g').sid 'ȟɟìӧДɫŖāöơɟբƢŊþXÊϱՎҲģţՀɄЌ' Returns ------- Short utf-8 textual representation """ if self._sid is None: self._sid = b777enc(self.n, self.digits * 5 // 8) return self._sid @property def ansi(self): r""" Colored textual (ANSI) representation of this element >>> from hosh import Hosh >>> Hosh.fromid("Iaz3L67a2BQv0GifoWOjWale6LYFTGmJJ1ZPfdoP").ansi '\x1b[38;5;229m\x1b[1m\x1b[48;5;0mI\x1b[0m\x1b[38;5;228m\x1b[1m\x1b[48;5;0ma\x1b[0m\x1b[38;5;222m\x1b[1m\x1b[48;5;0mz\x1b[0m\x1b[38;5;223m\x1b[1m\x1b[48;5;0m3\x1b[0m\x1b[38;5;228m\x1b[1m\x1b[48;5;0mL\x1b[0m\x1b[38;5;222m\x1b[1m\x1b[48;5;0m6\x1b[0m\x1b[38;5;222m\x1b[1m\x1b[48;5;0m7\x1b[0m\x1b[38;5;223m\x1b[1m\x1b[48;5;0ma\x1b[0m\x1b[38;5;228m\x1b[1m\x1b[48;5;0m2\x1b[0m\x1b[38;5;221m\x1b[1m\x1b[48;5;0mB\x1b[0m\x1b[38;5;216m\x1b[1m\x1b[48;5;0mQ\x1b[0m\x1b[38;5;186m\x1b[1m\x1b[48;5;0mv\x1b[0m\x1b[38;5;222m\x1b[1m\x1b[48;5;0m0\x1b[0m\x1b[38;5;221m\x1b[1m\x1b[48;5;0mG\x1b[0m\x1b[38;5;181m\x1b[1m\x1b[48;5;0mi\x1b[0m\x1b[38;5;194m\x1b[1m\x1b[48;5;0mf\x1b[0m\x1b[38;5;229m\x1b[1m\x1b[48;5;0mo\x1b[0m\x1b[38;5;228m\x1b[1m\x1b[48;5;0mW\x1b[0m\x1b[38;5;222m\x1b[1m\x1b[48;5;0mO\x1b[0m\x1b[38;5;223m\x1b[1m\x1b[48;5;0mj\x1b[0m\x1b[38;5;228m\x1b[1m\x1b[48;5;0mW\x1b[0m\x1b[38;5;222m\x1b[1m\x1b[48;5;0ma\x1b[0m\x1b[38;5;222m\x1b[1m\x1b[48;5;0ml\x1b[0m\x1b[38;5;223m\x1b[1m\x1b[48;5;0me\x1b[0m\x1b[38;5;228m\x1b[1m\x1b[48;5;0m6\x1b[0m\x1b[38;5;221m\x1b[1m\x1b[48;5;0mL\x1b[0m\x1b[38;5;216m\x1b[1m\x1b[48;5;0mY\x1b[0m\x1b[38;5;186m\x1b[1m\x1b[48;5;0mF\x1b[0m\x1b[38;5;222m\x1b[1m\x1b[48;5;0mT\x1b[0m\x1b[38;5;221m\x1b[1m\x1b[48;5;0mG\x1b[0m\x1b[38;5;181m\x1b[1m\x1b[48;5;0mm\x1b[0m\x1b[38;5;194m\x1b[1m\x1b[48;5;0mJ\x1b[0m\x1b[38;5;229m\x1b[1m\x1b[48;5;0mJ\x1b[0m\x1b[38;5;228m\x1b[1m\x1b[48;5;0m1\x1b[0m\x1b[38;5;222m\x1b[1m\x1b[48;5;0mZ\x1b[0m\x1b[38;5;223m\x1b[1m\x1b[48;5;0mP\x1b[0m\x1b[38;5;228m\x1b[1m\x1b[48;5;0mf\x1b[0m\x1b[38;5;222m\x1b[1m\x1b[48;5;0md\x1b[0m\x1b[38;5;222m\x1b[1m\x1b[48;5;0mo\x1b[0m\x1b[38;5;223m\x1b[1m\x1b[48;5;0mP\x1b[0m' Returns ------- Textual representation """ if self._ansi_light is None: self._ansi_light, self._ansi_dark = id2ansi(self.id) return self._ansi_dark if GLOBAL["dark_theme"] else self._ansi_light @property def idc(self): # pragma: no cover print("'hosh.idc' is deprecated, use 'hosh.ansi' instead") return self.ansi @property def rgb(self): """ Colored textual (RGB) representation of this element >>> from hosh import Hosh >>> Hosh.fromid("Iaz3L67a2BQv0GifoWOjWale6LYFTGmJJ1ZPfdoP").rgb [[6, 28, 104], [255, 255, 184], [255, 255, 141], [255, 220, 155], [255, 233, 172], [255, 250, 139], [255, 218, 144], [254, 223, 150], [255, 229, 187], [255, 255, 127], [255, 206, 98], [242, 176, 123], [212, 201, 138], [237, 216, 120], [252, 198, 115], [234, 193, 174], [229, 253, 204], [255, 255, 184], [255, 255, 141], [255, 220, 155], [255, 233, 172], [255, 250, 139], [255, 218, 144], [254, 223, 150], [255, 229, 187], [255, 255, 127], [255, 206, 98], [242, 176, 123], [212, 201, 138], [237, 216, 120], [252, 198, 115], [234, 193, 174], [229, 253, 204], [255, 255, 184], [255, 255, 141], [255, 220, 155], [255, 233, 172], [255, 250, 139], [255, 218, 144], [254, 223, 150], [255, 229, 187]] """ if self._rgb_light is None: self._rgb_light, self._rgb_dark = id2rgb(self.id) return self._rgb_dark if GLOBAL["dark_theme"] else self._rgb_light @property def html(self): """ HTML page containing a colored textual representation of this element Returns ------- Textual representation """ return ansi2html(self.ansi) @property def shtml(self): """Short colored html digest""" return ansi2html(self.sansi) @property def sidc(self): # pragma: no cover print("'hosh.sidc' is deprecated, please use 'hosh.sansi' instead") return self.sansi @property def sansi(self): """ Shorter colored id (base-777 using up to 2 bytes utf8 per char) Usage: >>> from hosh import ø >>> print((ø * b'65e987978g').sansi) \x1b[38;5;156m\x1b[1m\x1b[48;5;0mȟ\x1b[0m\x1b[38;5;155m\x1b[1m\x1b[48;5;0mɟ\x1b[0m\x1b[38;5;185m\x1b[1m\x1b[48;5;0mì\x1b[0m\x1b[38;5;113m\x1b[1m\x1b[48;5;0mӧ\x1b[0m\x1b[38;5;119m\x1b[1m\x1b[48;5;0mД\x1b[0m\x1b[38;5;185m\x1b[1m\x1b[48;5;0mɫ\x1b[0m\x1b[38;5;113m\x1b[1m\x1b[48;5;0mŖ\x1b[0m\x1b[38;5;119m\x1b[1m\x1b[48;5;0mā\x1b[0m\x1b[38;5;149m\x1b[1m\x1b[48;5;0mö\x1b[0m\x1b[38;5;113m\x1b[1m\x1b[48;5;0mơ\x1b[0m\x1b[38;5;83m\x1b[1m\x1b[48;5;0mɟ\x1b[0m\x1b[38;5;155m\x1b[1m\x1b[48;5;0mբ\x1b[0m\x1b[38;5;149m\x1b[1m\x1b[48;5;0mƢ\x1b[0m\x1b[38;5;119m\x1b[1m\x1b[48;5;0mŊ\x1b[0m\x1b[38;5;185m\x1b[1m\x1b[48;5;0mþ\x1b[0m\x1b[38;5;119m\x1b[1m\x1b[48;5;0mX\x1b[0m\x1b[38;5;156m\x1b[1m\x1b[48;5;0mÊ\x1b[0m\x1b[38;5;155m\x1b[1m\x1b[48;5;0mϱ\x1b[0m\x1b[38;5;185m\x1b[1m\x1b[48;5;0mՎ\x1b[0m\x1b[38;5;113m\x1b[1m\x1b[48;5;0mҲ\x1b[0m\x1b[38;5;119m\x1b[1m\x1b[48;5;0mģ\x1b[0m\x1b[38;5;185m\x1b[1m\x1b[48;5;0mţ\x1b[0m\x1b[38;5;113m\x1b[1m\x1b[48;5;0mՀ\x1b[0m\x1b[38;5;119m\x1b[1m\x1b[48;5;0mɄ\x1b[0m\x1b[38;5;149m\x1b[1m\x1b[48;5;0mЌ\x1b[0m Returns ------- Short utf-8 colored textual representation """ if self._sansi_light is None: self._sansi_light, self._sansi_dark = id2ansi(self.sid) return self._sansi_dark if GLOBAL["dark_theme"] else self._sansi_light def __repr__(self): if GLOBAL["format"] == BW: return self.sid if GLOBAL["short"] else self.id elif GLOBAL["format"] == ANSI: return self.sansi if GLOBAL["short"] else self.ansi elif GLOBAL["format"] == HTML: return self.shtml if GLOBAL["short"] else self.html elif callable(GLOBAL["format"]): # pragma: no cover return GLOBAL["format"](self) else: # pragma: no cover raise Exception(f"Unknown format: {GLOBAL['format']}") def __xor__(self, other: int): if other == 1: return self return Hosh(cellspow(self.cells, other, self.p), version=self.version) def __mul__(self, other: Union["Hosh", str, bytes, int]): if (other := self.convert(other)) is NotImplemented: # pragma: no cover return NotImplemented return Hosh(cellsmul(self.cells, other.cells, self.p), version=self.version) def __rmul__(self, other: Union["Hosh", str, bytes, int]): """ >>> from hosh import ø >>> (ø * b"13dfv34y4" )* b"434vbfrdg" == b"13dfv34y4" * (ø * b"434vbfrdg") True Parameters ---------- other Returns ------- """ if (other := self.convert(other)) is NotImplemented: # pragma: no cover return NotImplemented return Hosh(cellsmul(other.cells, self.cells, self.p), version=self.version) def __rpow__(self, other): """ >>> from hosh import ø >>> (ø * b"13dfv34y4") ** b"434vbfrdg" == b"13dfv34y4" ** (ø * b"434vbfrdg") True Parameters ---------- other Returns ------- """ if (other := self.convert(other)) is NotImplemented: # pragma: no cover return NotImplemented return +(+other * +self) def __pow__(self, power, modulo=None): if (power := self.convert(power)) is NotImplemented: # pragma: no cover return NotImplemented return +(+self * +power) def __rfloordiv__(self, other): """ >>> from hosh import ø >>> (ø * b"13dfv34y4") // b"434vbfrdg" == b"13dfv34y4" // (ø * b"434vbfrdg") True Parameters ---------- other Returns ------- """ if (other := self.convert(other)) is NotImplemented: # pragma: no cover return NotImplemented return +(+other / +self) def __floordiv__(self, other): """Lift""" if (other := self.convert(other)) is NotImplemented: # pragma: no cover return NotImplemented return +(+self / +other) def __neg__(self): return Hosh(cellsinv(self.cells, self.p, additive=True), version=self.version) def __pos__(self): """Change disposition of element-matrix cells in a way that even hybrid ids will not commute. ps. Semantics of +hosh are completely unrelated from -hosh as -hosh creates the inverse additive element. Switch positions of cells a2 and a5. This operation is its own inverse. Cells are represented as a list in the format: [a5, a4, a3, a2, a1, a0] Cells are represented as a matrix in the format: 1 a4 a1 a0 0 1 a2 a3 0 0 1 a5 0 0 0 1 """ cells = self.cells.copy() cells[3] = cells[0] cells[0] = self.cells[3] return Hosh(cells, version=self.version) def __invert__(self): return Hosh(cellsinv(self.cells, self.p), version=self.version) def __rtruediv__(self, other): """ >>> from hosh import ø >>> (ø * b"13dfv34y4") / b"434vbfrdg" == b"13dfv34y4" / (ø * b"434vbfrdg") True Parameters ---------- other Returns ------- """ if (other := self.convert(other)) is NotImplemented: # pragma: no cover return NotImplemented return Hosh(cellsmul(other.cells, cellsinv(self.cells, self.p), self.p), version=self.version) def __truediv__(self, other): if (other := self.convert(other)) is NotImplemented: # pragma: no cover return NotImplemented return Hosh(cellsmul(self.cells, cellsinv(other.cells, self.p), self.p), version=self.version) def __add__(self, other): """Matrix addition modulo p, keeping unidiagonal""" if (other := self.convert(other)) is NotImplemented: # pragma: no cover return NotImplemented cells = list(map(lambda x, y: (x + y) % self.p, self.cells, other.cells)) return Hosh(cells, version=self.version) def __sub__(self, other): #TODO: check if a - b here is different from a + (-b) ? """Matrix subtraction modulo p, keeping unidiagonal""" if (other := self.convert(other)) is NotImplemented: # pragma: no cover return NotImplemented cells = list(map(lambda x, y: (x - y) % self.p, self.cells, other.cells)) return Hosh(cells, version=self.version) # REMINDER: the chosen implementation differs from the alternative bellow! # return Hosh.fromn((self.n + self.convert(other).n) % self.order, self.version) def __str__(self): return self.sid if GLOBAL["short"] else self.id def __eq__(self, other): if (other := self.convert(other)) is NotImplemented: # pragma: no cover return NotImplemented return self.n == other.n def __ne__(self, other): if (other := self.convert(other)) is NotImplemented: # pragma: no cover return NotImplemented return self.n != other.n def show(self, colored=True): """ Usage: >>> Hosh(b"asdf86fasd").show(colored=False) voh8t1KrYmzCqpyrUO9.5QbGdouoZsnExarMSa34 """ return print(self.ansi if colored else self.id) def short(self, colored=True): """ Usage: >>> Hosh(b"asdf86fasd").short(colored=False) lϊӑơӫǯÃϺŮϳȐŁЬĽҪƉǏԛȪƜfÞӠȕՇ """ return print(self.sansi if colored else self.sid) def __hash__(self): return self.n % maxsize def convert(self, other): """ Usage: >>> from hosh import ø >>> ø.convert([0,0,0,0,0,0]).id '0000000000000000000000000000000000000000' >>> from hosh import Hosh >>> ø.convert(0).id '0000000000000000000000000000000000000000' Parameters ---------- other Returns ------- """ if isinstance(other, str): other = Hosh.fromid(other) elif isinstance(other, bytes): other = Hosh(other, etype=self.etype_inducer, version=self.version) elif isinstance(other, int): other = Hosh.fromn(other, version=self.version) elif isinstance(other, list): other = Hosh(other, version=self.version) elif not isinstance(other, Hosh): return NotImplemented if self.version != other.version: raise WrongVersion(f"Incompatible operands: {self.version} != {other.version}") return other def root(self, k): """ >>> a = Hosh(b"a") >>> for i in range(1, 5): ... r = a.root(i) ... r^i == a True True True True """ if k == 1: return self return Hosh(cellsroot(self.cells, k, self.p), version=self.version) def power_component(self, i, n): """Elements corresponding to `n` components of "multiplicative decomposition" such that `x = x1 * x2 * x3 * ... * xn = x * x² * x³ * ... * x^n` Not very useful as the resulting elements commute among themselves. This happens because they are all powers of x, making up just a sequence of `x`s . Parameters ========== i Desired component index n Desired total number of components Returns ======= Hosh (component) >>> a = Hosh(b"a") >>> a.power_component(0, 1) == a True >>> a.power_component(0, 2) * a.power_component(1, 2) == a True >>> a.power_component(0, 3) * a.power_component(1, 3) * a.power_component(2, 3) == a True >>> a.power_component(2, 3) * a.power_component(1, 3) * a.power_component(0, 3) == a True """ if i >= n: # pragma: no cover raise Exception(f"Hosh component should be defined by 'index' ({i}) < '#components' ({n})") if n == 1: return self exp = n * (n + 1) // 2 r = self.root(exp) return r ^ (i + 1) def bad_additive_components(self, n): """ Return the `n` additive components for `x` such that `x = x1 + x2 + ... + xn` `xn` fills the gap left by the other components remainder. We do not recommend this "decomposition" as it always generates different ids for the same subvalue. For instance, if a list `[value1, value2, value3]` induces ids `a`, `b`, and `any`, another list `[value1, value2, ...]` necessarily induces `c` and `d` as first two ids such that `a != c` and `b != d`. Parameters ========== n Desired total number of components Returns ======= Generator of hoshes >>> from functools import reduce >>> import operator >>> reduce(operator.add, Hosh(b"x").bad_additive_components(5)) == Hosh(b"x") True """ den = n * (n + 1) // 2 p = self.p def fac(x): parc, rem = divmod(x + p, den) lst = [i * parc for i in range(1, n + 1)] lst[-1] += rem return [l % p for l in lst] return (Hosh(list(x), version=self.version) for x in zip(*(fac(c) for c in self.cells))) def bad_additive_component(self, i, n): """ Return the `i`-th additive component for `x` such that `x = x1 + x2 + ... + xn` See `bad_additive_components` for more details. >>> from functools import reduce >>> import operator >>> list(Hosh(b"x").bad_additive_components(2))[0] == Hosh(b"x").bad_additive_component(0, 2) True >>> list(Hosh(b"x").bad_additive_components(2))[1] == Hosh(b"x").bad_additive_component(1, 2) True >>> list(Hosh(b"x").bad_additive_components(3))[0] == Hosh(b"x").bad_additive_component(0, 3) True >>> list(Hosh(b"x").bad_additive_components(3))[1] == Hosh(b"x").bad_additive_component(1, 3) True >>> list(Hosh(b"x").bad_additive_components(3))[2] == Hosh(b"x").bad_additive_component(2, 3) True >>> list(Hosh(b"x").bad_additive_components(5))[0] == Hosh(b"x").bad_additive_component(0, 5) True >>> list(Hosh(b"x").bad_additive_components(5))[1] == Hosh(b"x").bad_additive_component(1, 5) True >>> list(Hosh(b"x").bad_additive_components(5))[2] == Hosh(b"x").bad_additive_component(2, 5) True >>> list(Hosh(b"x").bad_additive_components(5))[4] == Hosh(b"x").bad_additive_component(4, 5) True >>> list(Hosh(b"x").bad_additive_components(7))[0] == Hosh(b"x").bad_additive_component(0, 7) True >>> list(Hosh(b"x").bad_additive_components(7))[1] == Hosh(b"x").bad_additive_component(1, 7) True >>> list(Hosh(b"x").bad_additive_components(7))[2] == Hosh(b"x").bad_additive_component(2, 7) True >>> list(Hosh(b"x").bad_additive_components(7))[4] == Hosh(b"x").bad_additive_component(4, 7) True """ den = n * (n + 1) // 2 p = self.p i += 1 toggle = 1 if i == n else 0 def fac(x): parc, rem = divmod(x + p, den) return (i * parc + toggle * rem) % p return Hosh(list(fac(c) for c in self.cells), version=self.version) def components(self, start, stop, n, additive=False): r""" Pseudo"decomposition" based on the hosh of the current id concatenated as bytes to a given component index Perform a multiplicative decomposition by default. Syntax: Hosh(b"blob").components(i, m, n) # Takes a slice of elements. Hosh(b"blob")[i:m, n] # Takes a slice of elements. Hosh(b"blob")[i, n] # Takes element `i` out of `n` components. Hosh(b"blob")[:n, n] # All `n` elements. Warning: Hosh(b"blob")[-1] # Reverse element. Not to be confused with inverse element. The components are arbitrarily internally defined group elements based on current id as hashed bytes: Hosh(id+"-1"), Hosh(id+"-2"), ..., Hosh(id+"-n") The last element (xn) is the exception as it makes the product x1 * x2 * ... * xn match x: x1 = id+"-1" * x ... xn-1 = id+"-n-1" * x xn = (id+"-1" * x * ... * id+"-n-1")-¹ Parameters ========== start Start of a slice stop Stop of a slice n Desired total number of components additive Set up an additive decomposition Returns ======= List if hoshes >>> from hosh import Hosh >>> a = Hosh(b"a") >>> a[-1].rev == a True >>> a[0, 1] == a True >>> a[0:, 1][0] == a True >>> a[:1, 1][0] == a True >>> a[0:1, 1][0] == a True >>> from operator import mul >>> reduce(mul, a[:, 3]) == a True >>> [x.id for x in a[:, 3]] ['Bd6Axil5pFSp15HUBz8eCujvu3gBsEk6XMpRsMNo', '32MloLPcivDbbPMCJn1RBY31aNZ6z-Dqnt4vQhot', 'la3xnZmlhn3lFBAnvWw-UWAvK.2hk-QqUNFYAs3e'] >>> a[:, 3][0] * a[:, 3][1] * a[:, 3][2] == a True >>> a[0, 3] * a[1, 3] * a[2, 3] == a True >>> a.id 'cIXBKPediDiOKabeZ6SthD04rnzaquNXaAEhSud4' >>> from operator import add, mul >>> from functools import reduce >>> print("\n".join(x.id for x in a.components(0, 7, 7))) Bd6Axil5pFSp15HUBz8eCujvu3gBsEk6XMpRsMNo 32MloLPcivDbbPMCJn1RBY31aNZ6z-Dqnt4vQhot Y2JlyuF8.KJc0DvvcIivLA5uLYloF7HN9ovO14Sq 6.F-NB-G4vBXs7evbBImex9x3foNi85Ca7wDb1c3 tZmUsjVcZAGTajUOzsSNrr7a7BQVNSiA6xaiPEYf iCLMdAlduXvUtK1.awng0D0YP49kV8Cit7OLXyab BA6UvITsIN822llT9eErc1R0rmf.ARbc0adwEbWk >>> reduce(mul, a.components(0, 2, 7)) * reduce(mul, a.components(2, 3, 7)) * reduce(mul, a.components(3, 7, 7)) == a True >>> reduce(mul, a.components(0, 7, 7)) == a True >>> print("\n".join(x.id for x in a.components(0, 7, 7, additive=True))) Bd6Axil5pFSp15HUBz8eCujvu3gBsEk6XMpRsMNo 32MloLPcivDbbPMCJn1RBY31aNZ6z-Dqnt4vQhot Y2JlyuF8.KJc0DvvcIivLA5uLYloF7HN9ovO14Sq 6.F-NB-G4vBXs7evbBImex9x3foNi85Ca7wDb1c3 tZmUsjVcZAGTajUOzsSNrr7a7BQVNSiA6xaiPEYf iCLMdAlduXvUtK1.awng0D0YP49kV8Cit7OLXyab 7jJmsfrPpa2CeeYCAiByF3HW2J9.ARbc0adwEbWk >>> reduce(add, a.components(0, 2, 7, additive=True)) + reduce(add, a.components(2, 3, 7, additive=True)) + reduce(add, a.components(3, 7, 7, additive=True)) == a True >>> reduce(add, a.components(0, 7, 7, additive=True)) == a True """ if stop > n: # pragma: no cover raise Exception(f"Wrong value: stop=`{stop}` >= n=`{n}`") acc = self.ø operator = add if additive else mul for i in range(0, stop): if stop == n and i == stop - 1: break if (t := (i, n, additive)) not in self._composition_memo: self._composition_memo[t] = Hosh(f"{self.id}-{i}".encode(), version=self.version) if len(self._composition_memo) > self.components_cache_size: # pragma: no cover first = next(iter(self._composition_memo)) del self._composition_memo[first] h = self._composition_memo[t] acc = operator(acc, h) if i >= start: yield h if stop == n: inv = Hosh(cellsinv(acc.cells, self.p, additive), version=self.version) last = operator(inv, self) yield last def __getitem__(self, item: Union[int, tuple]): """ Reverse element: Hosh(b"blob")[-1] """ if item == -1: return self.rev if not isinstance(item, tuple) or len(item) != 2: # pragma: no cover raise Exception("Wrong syntax, tuple or `-1` expected: hosh[-1] or hosh[i, n] or hosh[l:m, n]") slc, n = item if n <= 0: # pragma: no cover raise Exception(f"Wrong value: n={n} <= 0") if isinstance(idx := slc, int): if idx < n - 1: return Hosh(f"{self.id}-{idx}".encode()) if n == 1: return self if idx == n - 1: return list(self.components(0, n, n))[idx] raise Exception(f"Wrong value: i={slc} > n={n}") # pragma: no cover if not isinstance(slc, slice) or slc.step is not None: # pragma: no cover raise Exception(f"Wrong syntax. Simple slice or index expected as first tuple item, not `{slc}`: hosh[i, n] or hosh[l:m, n]") if (start := slc.start) is None: start = 0 if (stop := slc.stop) is None: stop = n if start > n or start < 0: # pragma: no cover raise Exception(f"Wrong values, expected: 0 <= i={start} <= n={n}") if n == 1: return [self] return list(self.components(start, stop, n)) @property def bits(self): """ >>> from hosh import Hosh, groups >>> Hosh(b"asd").bits '000110111101001001111010010101011100011010000011101010100000000001101101111101100111110100001011001001111001110110101101100110000000011111000110100100110011110010101110100011111101100010010111100111010101010011001110000001000100001101001111' >>> bits = Hosh.fromn(groups[40].p6 - 1).bits # Max number. >>> bits '111111111111111111111111111111011111011000000000000000000000000110111011011111101111111111111111001101110000101001110100000000000011001100111000100101011110111011111001000010011000001000000100101101100110010011110110001000101011101110110000' >>> int(bits, 2) == groups[40].p6 - 1 True """ if self._bits is None: self._bits = '{:b}'.format(self.n).rjust(self.digits * 6, "0") return self._bits def __reduce__(self): return self.fromid, (self.id,)
Subclasses
Class variables
var components_cache_size
Static methods
def fromid(id)
-
Create an element from a textual id.
Usage:
>>> a = Hosh.fromid("abcdefabcdefabcdefabcdefabcdefab") >>> a.n 1094566309952642687224764830259410933250743749332933330234 >>> a.cells [748932665, 516513868, 468764361, 3316970622, 2727293743, 316029245] >>> a.etype 'ordered' >>> bid = a.id[:2] + "_" + a.id[3:] >>> bid 'ab_defabcdefabcdefabcdefabcdefab' >>> b = Hosh.fromid(bid) >>> b.id 'ab_defabcdefabcdefabcdefabcdefab' >>> b.n 59377482839139050825606534576063885287 >>> b.cells [0, 0, 749449200, 1774140626, 3139018916, 292801225] >>> b.etype 'hybrid' >>> Hosh.fromid("0000000000000000000000000000000000000000000000000000000000000000") == 0 True
Parameters
id
Parameters
id
Returns
A new Hosh object
Expand source code
@classmethod def fromid(cls, id): """ Create an element from a textual id. Usage: >>> a = Hosh.fromid("abcdefabcdefabcdefabcdefabcdefab") >>> a.n 1094566309952642687224764830259410933250743749332933330234 >>> a.cells [748932665, 516513868, 468764361, 3316970622, 2727293743, 316029245] >>> a.etype 'ordered' >>> bid = a.id[:2] + "_" + a.id[3:] >>> bid 'ab_defabcdefabcdefabcdefabcdefab' >>> b = Hosh.fromid(bid) >>> b.id 'ab_defabcdefabcdefabcdefabcdefab' >>> b.n 59377482839139050825606534576063885287 >>> b.cells [0, 0, 749449200, 1774140626, 3139018916, 292801225] >>> b.etype 'hybrid' >>> Hosh.fromid("0000000000000000000000000000000000000000000000000000000000000000") == 0 True Parameters ---------- id Parameters ---------- id Returns ------- A new Hosh object """ if len(id) not in groups: raise WrongIdentifier(f"Wrong identifier length: {len(id)} id:[{id}]") return Hosh(cells_fromid(id, p=groups[len(id)].p), version=groups[len(id)])
def fromn(n: int, version=Group(p=1099511627689, p4=1461501636868331575725436266114840805196834679841, p6=1766847063939562670646036165286872353986524172769430561878277294118845361, digits=40, bytes=30, firstp='0_100000000_____________________________', lastp='f_8afffffff_____________________________', firstp4='00_1000000000000000000000000000000000000', lastp4='.._87c2a630003eec7dffff561b0000004aeffff', firstp6='1000000000000000000000000000000000000000', lastp6='g-8KOjCQREq2Vz8VTc30gLMd..vvX6000ov.....'))
-
Create a Hosh object representing the given int.
Default 'p' is according to version UT64.4.
Usage:
>>> h = Hosh.fromn(7647544756746324134134) >>> h.id '00_e49c1c505dcd0039e91000000000000000000'
Parameters
n
version
Returns
A new Hosh object
Expand source code
@classmethod def fromn(cls, n: int, version=UT40_4): """ Create a Hosh object representing the given int. Default 'p' is according to version UT64.4. Usage: >>> h = Hosh.fromn(7647544756746324134134) >>> h.id '00_e49c1c505dcd0039e91000000000000000000' Parameters ---------- n version Returns ------- A new Hosh object """ p, order = version.p, version.p6 if n > order: raise ElementTooHigh(f"Element outside allowed range: {n} >= {order}") return Hosh(int2cells(n, p), version=version)
Instance variables
var ansi
-
Colored textual (ANSI) representation of this element
>>> from hosh import Hosh >>> Hosh.fromid("Iaz3L67a2BQv0GifoWOjWale6LYFTGmJJ1ZPfdoP").ansi '\x1b[38;5;229m\x1b[1m\x1b[48;5;0mI\x1b[0m\x1b[38;5;228m\x1b[1m\x1b[48;5;0ma\x1b[0m\x1b[38;5;222m\x1b[1m\x1b[48;5;0mz\x1b[0m\x1b[38;5;223m\x1b[1m\x1b[48;5;0m3\x1b[0m\x1b[38;5;228m\x1b[1m\x1b[48;5;0mL\x1b[0m\x1b[38;5;222m\x1b[1m\x1b[48;5;0m6\x1b[0m\x1b[38;5;222m\x1b[1m\x1b[48;5;0m7\x1b[0m\x1b[38;5;223m\x1b[1m\x1b[48;5;0ma\x1b[0m\x1b[38;5;228m\x1b[1m\x1b[48;5;0m2\x1b[0m\x1b[38;5;221m\x1b[1m\x1b[48;5;0mB\x1b[0m\x1b[38;5;216m\x1b[1m\x1b[48;5;0mQ\x1b[0m\x1b[38;5;186m\x1b[1m\x1b[48;5;0mv\x1b[0m\x1b[38;5;222m\x1b[1m\x1b[48;5;0m0\x1b[0m\x1b[38;5;221m\x1b[1m\x1b[48;5;0mG\x1b[0m\x1b[38;5;181m\x1b[1m\x1b[48;5;0mi\x1b[0m\x1b[38;5;194m\x1b[1m\x1b[48;5;0mf\x1b[0m\x1b[38;5;229m\x1b[1m\x1b[48;5;0mo\x1b[0m\x1b[38;5;228m\x1b[1m\x1b[48;5;0mW\x1b[0m\x1b[38;5;222m\x1b[1m\x1b[48;5;0mO\x1b[0m\x1b[38;5;223m\x1b[1m\x1b[48;5;0mj\x1b[0m\x1b[38;5;228m\x1b[1m\x1b[48;5;0mW\x1b[0m\x1b[38;5;222m\x1b[1m\x1b[48;5;0ma\x1b[0m\x1b[38;5;222m\x1b[1m\x1b[48;5;0ml\x1b[0m\x1b[38;5;223m\x1b[1m\x1b[48;5;0me\x1b[0m\x1b[38;5;228m\x1b[1m\x1b[48;5;0m6\x1b[0m\x1b[38;5;221m\x1b[1m\x1b[48;5;0mL\x1b[0m\x1b[38;5;216m\x1b[1m\x1b[48;5;0mY\x1b[0m\x1b[38;5;186m\x1b[1m\x1b[48;5;0mF\x1b[0m\x1b[38;5;222m\x1b[1m\x1b[48;5;0mT\x1b[0m\x1b[38;5;221m\x1b[1m\x1b[48;5;0mG\x1b[0m\x1b[38;5;181m\x1b[1m\x1b[48;5;0mm\x1b[0m\x1b[38;5;194m\x1b[1m\x1b[48;5;0mJ\x1b[0m\x1b[38;5;229m\x1b[1m\x1b[48;5;0mJ\x1b[0m\x1b[38;5;228m\x1b[1m\x1b[48;5;0m1\x1b[0m\x1b[38;5;222m\x1b[1m\x1b[48;5;0mZ\x1b[0m\x1b[38;5;223m\x1b[1m\x1b[48;5;0mP\x1b[0m\x1b[38;5;228m\x1b[1m\x1b[48;5;0mf\x1b[0m\x1b[38;5;222m\x1b[1m\x1b[48;5;0md\x1b[0m\x1b[38;5;222m\x1b[1m\x1b[48;5;0mo\x1b[0m\x1b[38;5;223m\x1b[1m\x1b[48;5;0mP\x1b[0m'
Returns
Textual representation
Expand source code
@property def ansi(self): r""" Colored textual (ANSI) representation of this element >>> from hosh import Hosh >>> Hosh.fromid("Iaz3L67a2BQv0GifoWOjWale6LYFTGmJJ1ZPfdoP").ansi '\x1b[38;5;229m\x1b[1m\x1b[48;5;0mI\x1b[0m\x1b[38;5;228m\x1b[1m\x1b[48;5;0ma\x1b[0m\x1b[38;5;222m\x1b[1m\x1b[48;5;0mz\x1b[0m\x1b[38;5;223m\x1b[1m\x1b[48;5;0m3\x1b[0m\x1b[38;5;228m\x1b[1m\x1b[48;5;0mL\x1b[0m\x1b[38;5;222m\x1b[1m\x1b[48;5;0m6\x1b[0m\x1b[38;5;222m\x1b[1m\x1b[48;5;0m7\x1b[0m\x1b[38;5;223m\x1b[1m\x1b[48;5;0ma\x1b[0m\x1b[38;5;228m\x1b[1m\x1b[48;5;0m2\x1b[0m\x1b[38;5;221m\x1b[1m\x1b[48;5;0mB\x1b[0m\x1b[38;5;216m\x1b[1m\x1b[48;5;0mQ\x1b[0m\x1b[38;5;186m\x1b[1m\x1b[48;5;0mv\x1b[0m\x1b[38;5;222m\x1b[1m\x1b[48;5;0m0\x1b[0m\x1b[38;5;221m\x1b[1m\x1b[48;5;0mG\x1b[0m\x1b[38;5;181m\x1b[1m\x1b[48;5;0mi\x1b[0m\x1b[38;5;194m\x1b[1m\x1b[48;5;0mf\x1b[0m\x1b[38;5;229m\x1b[1m\x1b[48;5;0mo\x1b[0m\x1b[38;5;228m\x1b[1m\x1b[48;5;0mW\x1b[0m\x1b[38;5;222m\x1b[1m\x1b[48;5;0mO\x1b[0m\x1b[38;5;223m\x1b[1m\x1b[48;5;0mj\x1b[0m\x1b[38;5;228m\x1b[1m\x1b[48;5;0mW\x1b[0m\x1b[38;5;222m\x1b[1m\x1b[48;5;0ma\x1b[0m\x1b[38;5;222m\x1b[1m\x1b[48;5;0ml\x1b[0m\x1b[38;5;223m\x1b[1m\x1b[48;5;0me\x1b[0m\x1b[38;5;228m\x1b[1m\x1b[48;5;0m6\x1b[0m\x1b[38;5;221m\x1b[1m\x1b[48;5;0mL\x1b[0m\x1b[38;5;216m\x1b[1m\x1b[48;5;0mY\x1b[0m\x1b[38;5;186m\x1b[1m\x1b[48;5;0mF\x1b[0m\x1b[38;5;222m\x1b[1m\x1b[48;5;0mT\x1b[0m\x1b[38;5;221m\x1b[1m\x1b[48;5;0mG\x1b[0m\x1b[38;5;181m\x1b[1m\x1b[48;5;0mm\x1b[0m\x1b[38;5;194m\x1b[1m\x1b[48;5;0mJ\x1b[0m\x1b[38;5;229m\x1b[1m\x1b[48;5;0mJ\x1b[0m\x1b[38;5;228m\x1b[1m\x1b[48;5;0m1\x1b[0m\x1b[38;5;222m\x1b[1m\x1b[48;5;0mZ\x1b[0m\x1b[38;5;223m\x1b[1m\x1b[48;5;0mP\x1b[0m\x1b[38;5;228m\x1b[1m\x1b[48;5;0mf\x1b[0m\x1b[38;5;222m\x1b[1m\x1b[48;5;0md\x1b[0m\x1b[38;5;222m\x1b[1m\x1b[48;5;0mo\x1b[0m\x1b[38;5;223m\x1b[1m\x1b[48;5;0mP\x1b[0m' Returns ------- Textual representation """ if self._ansi_light is None: self._ansi_light, self._ansi_dark = id2ansi(self.id) return self._ansi_dark if GLOBAL["dark_theme"] else self._ansi_light
var bits
-
>>> from hosh import Hosh, groups >>> Hosh(b"asd").bits '000110111101001001111010010101011100011010000011101010100000000001101101111101100111110100001011001001111001110110101101100110000000011111000110100100110011110010101110100011111101100010010111100111010101010011001110000001000100001101001111' >>> bits = Hosh.fromn(groups[40].p6 - 1).bits # Max number. >>> bits '111111111111111111111111111111011111011000000000000000000000000110111011011111101111111111111111001101110000101001110100000000000011001100111000100101011110111011111001000010011000001000000100101101100110010011110110001000101011101110110000' >>> int(bits, 2) == groups[40].p6 - 1 True
Expand source code
@property def bits(self): """ >>> from hosh import Hosh, groups >>> Hosh(b"asd").bits '000110111101001001111010010101011100011010000011101010100000000001101101111101100111110100001011001001111001110110101101100110000000011111000110100100110011110010101110100011111101100010010111100111010101010011001110000001000100001101001111' >>> bits = Hosh.fromn(groups[40].p6 - 1).bits # Max number. >>> bits '111111111111111111111111111111011111011000000000000000000000000110111011011111101111111111111111001101110000101001110100000000000011001100111000100101011110111011111001000010011000001000000100101101100110010011110110001000101011101110110000' >>> int(bits, 2) == groups[40].p6 - 1 True """ if self._bits is None: self._bits = '{:b}'.format(self.n).rjust(self.digits * 6, "0") return self._bits
var etype
-
Type of this element
Usage:
>>> from hosh import Hosh >>> Hosh.fromn(5).etype 'unordered'
Returns
'ordered', 'hybrid' or 'unordered'
Expand source code
@property def etype(self): """ Type of this element Usage: >>> from hosh import Hosh >>> Hosh.fromn(5).etype 'unordered' Returns ------- 'ordered', 'hybrid' or 'unordered' """ if self._etype is None: if sum(self.cells[:5]) == 0: self._etype = "unordered" elif sum(self.cells[:2]) == 0: self._etype = "hybrid" else: self._etype = "ordered" return self._etype
var etype_inducer
-
Type this element uses to coerce an element of undefined type.
Usage:
>>> from hosh import ø, Hosh >>> ø.etype_inducer 'ordered' >>> ø.h.etype_inducer 'hybrid' >>> ø.u.etype_inducer 'unordered' >>> Hosh(b"12124").etype_inducer 'ordered' >>> Hosh(b"12124", etype="hybrid").etype_inducer 'hybrid'
Returns
'ordered', 'hybrid', 'unordered'
Expand source code
@property def etype_inducer(self): """ Type this element uses to coerce an element of undefined type. Usage: >>> from hosh import ø, Hosh >>> ø.etype_inducer 'ordered' >>> ø.h.etype_inducer 'hybrid' >>> ø.u.etype_inducer 'unordered' >>> Hosh(b"12124").etype_inducer 'ordered' >>> Hosh(b"12124", etype="hybrid").etype_inducer 'hybrid' Returns ------- 'ordered', 'hybrid', 'unordered' """ if self._etype_inducer is None: self._etype_inducer = self.etype return self._etype_inducer
var html
-
HTML page containing a colored textual representation of this element
Returns
Textual representation
Expand source code
@property def html(self): """ HTML page containing a colored textual representation of this element Returns ------- Textual representation """ return ansi2html(self.ansi)
var id
-
Textual representation of this element
Returns
Textual representation
Expand source code
@property def id(self): """ Textual representation of this element Returns ------- Textual representation """ if self._id is None: self._id = id_fromcells(self.cells, self.digits, self.p) return self._id
var idc
-
Expand source code
@property def idc(self): # pragma: no cover print("'hosh.idc' is deprecated, use 'hosh.ansi' instead") return self.ansi
var n
-
Lexicographic rank of this eloement (according to the format adopted in internal integer cells.
Returns
Number
Expand source code
@property def n(self): """ Lexicographic rank of this eloement (according to the format adopted in internal integer cells. Returns ------- Number """ if self._n is None: self._n = cells2int(self.cells, self.p) return self._n
var rev
-
Reversed element (warning: this is not the inverse element)
Element with the internal cells reversed. This operation is its own inverse.
This is useful wherever a unary operation is needed. For instance, a function can be represented as a value by its original identifier, and can be represented as (an applied) function by its reversed element identifier.
Not all hoshes are digest-reversible, i.e., at the digit level, due to the intrinsic mismatch between base 64 (i.e., a power of two) representation and the group (prime) order. Therefore, we must resort to reversing the cells. As an exception, unordered elements do have (most) digits reversed as it has only one internal cell.
Probabilistically irrelevant corner cases: The presence of empty cells (i.e., with zero value) might cause migration from one etype to another. The presence of duplicate cells (i.e., with the same value) might make the hosh reverse to itself. See examples below.
Usage:
>>> from hosh import Hosh, groups >>> h = Hosh.fromid("J5.uRTue8X4r1xu.JFkPbURVVGvTRPSFLncXdyzj").rev >>> h.id 'lUz6uu1ZBCJf342R7-qKOOqWJgaf3TDHx2M.CWGT' >>> h.rev.rev == h True >>> h = Hosh.fromid("ab_cabcdefabcdefabcdefabcdefabcdefabcdef") >>> h.rev.id 'Hh_c7201818f76878562c52010943fe4f2a7f3b2' >>> h = Hosh.fromid("2_dbe78441d_____________________________") >>> h.rev.id '2_dbd14487e_____________________________'
>>> # Limits of subgroup Z. >>> Hosh.fromid(groups[40].firstp).id, Hosh.fromid(groups[40].lastp).id ('0_100000000_____________________________', 'f_8afffffff_____________________________') >>> Hosh.fromid(groups[40].firstp).rev.id, Hosh.fromid(groups[40].lastp).rev.id ('0_100000000_____________________________', 'f_8afffffff_____________________________')
>>> # Limits of subgroup H. >>> Hosh.fromid(groups[40].firstp4).id, Hosh.fromid(groups[40].lastp4).id ('00_1000000000000000000000000000000000000', '.._87c2a630003eec7dffff561b0000004aeffff') >>> Hosh.fromid(groups[40].firstp4).rev.id, Hosh.fromid(groups[40].lastp4).rev.id ('00_9ed100000015ffffffff00000000000000000', '.._87c2a630003eec7dffff561b0000004aeffff') >>> Hosh.fromid(groups[40].firstp4).cells, Hosh.fromid(groups[40].lastp4).cells ([0, 0, 0, 0, 1, 0], [0, 0, 1099511627688, 1099511627688, 1099511627688, 1099511627688]) >>> Hosh.fromid(groups[40].firstp4).rev.cells, Hosh.fromid(groups[40].lastp4).rev.cells ([0, 0, 0, 1, 0, 0], [0, 0, 1099511627688, 1099511627688, 1099511627688, 1099511627688])
>>> # Limits of group G. >>> Hosh.fromid(groups[40].firstp6).id, Hosh.fromid(groups[40].lastp6).id ('1000000000000000000000000000000000000000', 'g-8KOjCQREq2Vz8VTc30gLMd..vvX6000ov.....') >>> Hosh.fromid(groups[40].firstp6).rev.id, Hosh.fromid(groups[40].lastp6).rev.id ('00_1000000000000000000000000000000000000', 'g-8KOjCQREq2Vz8VTc30gLMd..vvX6000ov.....') >>> Hosh.fromid(groups[40].firstp6).cells, Hosh.fromid(groups[40].lastp6).cells ([0, 1, 0, 0, 0, 0], [1099511627688, 1099511627688, 1099511627688, 1099511627688, 1099511627688, 1099511627688]) >>> Hosh.fromid(groups[40].firstp6).rev.cells, Hosh.fromid(groups[40].lastp6).rev.cells ([0, 0, 0, 0, 1, 0], [1099511627688, 1099511627688, 1099511627688, 1099511627688, 1099511627688, 1099511627688])
>>> # Near limits of group G. >>> Hosh.fromn(groups[40].p4+1).id, Hosh.fromn(groups[40].p6 - 2).id ('2000000000000000000000000000000000000000', 'f-8KOjCQREq2Vz8VTc30gLMd..vvX6000ov.....') >>> Hosh.fromn(groups[40].p4+1).rev.id, Hosh.fromn(groups[40].p6 - 2).rev.id ('ihdwjXvMdIj40gZQq-..5Ai0000j-....3000000', '7XoPrombpt9-UXQnse30Cgud..fcZ6000kv.....') >>> Hosh.fromn(groups[40].p4+1).cells, Hosh.fromn(groups[40].p6 - 2).cells ([0, 1, 0, 0, 0, 1], [1099511627688, 1099511627688, 1099511627688, 1099511627688, 1099511627688, 1099511627687]) >>> Hosh.fromn(groups[40].p4+1).rev.cells, Hosh.fromn(groups[40].p6 - 2).rev.cells ([1, 0, 0, 0, 1, 0], [1099511627687, 1099511627688, 1099511627688, 1099511627688, 1099511627688, 1099511627688])
Expand source code
@property def rev(self): """ Reversed element (warning: this is not the inverse element) Element with the internal cells reversed. This operation is its own inverse. This is useful wherever a unary operation is needed. For instance, a function can be represented as a value by its original identifier, and can be represented as (an applied) function by its reversed element identifier. Not all hoshes are digest-reversible, i.e., at the digit level, due to the intrinsic mismatch between base 64 (i.e., a power of two) representation and the group (prime) order. Therefore, we must resort to reversing the cells. As an exception, unordered elements do have (most) digits reversed as it has only one internal cell. Probabilistically irrelevant corner cases: The presence of empty cells (i.e., with zero value) might cause migration from one etype to another. The presence of duplicate cells (i.e., with the same value) might make the hosh reverse to itself. See examples below. Usage: >>> from hosh import Hosh, groups >>> h = Hosh.fromid("J5.uRTue8X4r1xu.JFkPbURVVGvTRPSFLncXdyzj").rev >>> h.id 'lUz6uu1ZBCJf342R7-qKOOqWJgaf3TDHx2M.CWGT' >>> h.rev.rev == h True >>> h = Hosh.fromid("ab_cabcdefabcdefabcdefabcdefabcdefabcdef") >>> h.rev.id 'Hh_c7201818f76878562c52010943fe4f2a7f3b2' >>> h = Hosh.fromid("2_dbe78441d_____________________________") >>> h.rev.id '2_dbd14487e_____________________________' >>> # Limits of subgroup Z. >>> Hosh.fromid(groups[40].firstp).id, Hosh.fromid(groups[40].lastp).id ('0_100000000_____________________________', 'f_8afffffff_____________________________') >>> Hosh.fromid(groups[40].firstp).rev.id, Hosh.fromid(groups[40].lastp).rev.id ('0_100000000_____________________________', 'f_8afffffff_____________________________') >>> # Limits of subgroup H. >>> Hosh.fromid(groups[40].firstp4).id, Hosh.fromid(groups[40].lastp4).id ('00_1000000000000000000000000000000000000', '.._87c2a630003eec7dffff561b0000004aeffff') >>> Hosh.fromid(groups[40].firstp4).rev.id, Hosh.fromid(groups[40].lastp4).rev.id ('00_9ed100000015ffffffff00000000000000000', '.._87c2a630003eec7dffff561b0000004aeffff') >>> Hosh.fromid(groups[40].firstp4).cells, Hosh.fromid(groups[40].lastp4).cells ([0, 0, 0, 0, 1, 0], [0, 0, 1099511627688, 1099511627688, 1099511627688, 1099511627688]) >>> Hosh.fromid(groups[40].firstp4).rev.cells, Hosh.fromid(groups[40].lastp4).rev.cells ([0, 0, 0, 1, 0, 0], [0, 0, 1099511627688, 1099511627688, 1099511627688, 1099511627688]) >>> # Limits of group G. >>> Hosh.fromid(groups[40].firstp6).id, Hosh.fromid(groups[40].lastp6).id ('1000000000000000000000000000000000000000', 'g-8KOjCQREq2Vz8VTc30gLMd..vvX6000ov.....') >>> Hosh.fromid(groups[40].firstp6).rev.id, Hosh.fromid(groups[40].lastp6).rev.id ('00_1000000000000000000000000000000000000', 'g-8KOjCQREq2Vz8VTc30gLMd..vvX6000ov.....') >>> Hosh.fromid(groups[40].firstp6).cells, Hosh.fromid(groups[40].lastp6).cells ([0, 1, 0, 0, 0, 0], [1099511627688, 1099511627688, 1099511627688, 1099511627688, 1099511627688, 1099511627688]) >>> Hosh.fromid(groups[40].firstp6).rev.cells, Hosh.fromid(groups[40].lastp6).rev.cells ([0, 0, 0, 0, 1, 0], [1099511627688, 1099511627688, 1099511627688, 1099511627688, 1099511627688, 1099511627688]) >>> # Near limits of group G. >>> Hosh.fromn(groups[40].p4+1).id, Hosh.fromn(groups[40].p6 - 2).id ('2000000000000000000000000000000000000000', 'f-8KOjCQREq2Vz8VTc30gLMd..vvX6000ov.....') >>> Hosh.fromn(groups[40].p4+1).rev.id, Hosh.fromn(groups[40].p6 - 2).rev.id ('ihdwjXvMdIj40gZQq-..5Ai0000j-....3000000', '7XoPrombpt9-UXQnse30Cgud..fcZ6000kv.....') >>> Hosh.fromn(groups[40].p4+1).cells, Hosh.fromn(groups[40].p6 - 2).cells ([0, 1, 0, 0, 0, 1], [1099511627688, 1099511627688, 1099511627688, 1099511627688, 1099511627688, 1099511627687]) >>> Hosh.fromn(groups[40].p4+1).rev.cells, Hosh.fromn(groups[40].p6 - 2).rev.cells ([1, 0, 0, 0, 1, 0], [1099511627687, 1099511627688, 1099511627688, 1099511627688, 1099511627688, 1099511627688]) """ if self._rev is None: id = self.id if self.etype == "ordered": self._rev = Hosh(list(reversed(self.cells))) elif self.etype == "hybrid": self._rev = Hosh(self.cells[:2] + list(reversed(self.cells[2:]))) elif self.etype == "unordered": self._rev = Hosh.fromid(id[:4] + "".join(reversed(id[4:11])) + "_____________________________") else: # pragma: no cover raise Exception(f"Unexpected condition. element type: {self.etype}") return self._rev
var rgb
-
Colored textual (RGB) representation of this element
>>> from hosh import Hosh >>> Hosh.fromid("Iaz3L67a2BQv0GifoWOjWale6LYFTGmJJ1ZPfdoP").rgb [[6, 28, 104], [255, 255, 184], [255, 255, 141], [255, 220, 155], [255, 233, 172], [255, 250, 139], [255, 218, 144], [254, 223, 150], [255, 229, 187], [255, 255, 127], [255, 206, 98], [242, 176, 123], [212, 201, 138], [237, 216, 120], [252, 198, 115], [234, 193, 174], [229, 253, 204], [255, 255, 184], [255, 255, 141], [255, 220, 155], [255, 233, 172], [255, 250, 139], [255, 218, 144], [254, 223, 150], [255, 229, 187], [255, 255, 127], [255, 206, 98], [242, 176, 123], [212, 201, 138], [237, 216, 120], [252, 198, 115], [234, 193, 174], [229, 253, 204], [255, 255, 184], [255, 255, 141], [255, 220, 155], [255, 233, 172], [255, 250, 139], [255, 218, 144], [254, 223, 150], [255, 229, 187]]
Expand source code
@property def rgb(self): """ Colored textual (RGB) representation of this element >>> from hosh import Hosh >>> Hosh.fromid("Iaz3L67a2BQv0GifoWOjWale6LYFTGmJJ1ZPfdoP").rgb [[6, 28, 104], [255, 255, 184], [255, 255, 141], [255, 220, 155], [255, 233, 172], [255, 250, 139], [255, 218, 144], [254, 223, 150], [255, 229, 187], [255, 255, 127], [255, 206, 98], [242, 176, 123], [212, 201, 138], [237, 216, 120], [252, 198, 115], [234, 193, 174], [229, 253, 204], [255, 255, 184], [255, 255, 141], [255, 220, 155], [255, 233, 172], [255, 250, 139], [255, 218, 144], [254, 223, 150], [255, 229, 187], [255, 255, 127], [255, 206, 98], [242, 176, 123], [212, 201, 138], [237, 216, 120], [252, 198, 115], [234, 193, 174], [229, 253, 204], [255, 255, 184], [255, 255, 141], [255, 220, 155], [255, 233, 172], [255, 250, 139], [255, 218, 144], [254, 223, 150], [255, 229, 187]] """ if self._rgb_light is None: self._rgb_light, self._rgb_dark = id2rgb(self.id) return self._rgb_dark if GLOBAL["dark_theme"] else self._rgb_light
var sansi
-
Shorter colored id (base-777 using up to 2 bytes utf8 per char)
Usage:
>>> from hosh import ø >>> print((ø * b'65e987978g').sansi) [38;5;156m[1m[48;5;0mȟ[0m[38;5;155m[1m[48;5;0mɟ[0m[38;5;185m[1m[48;5;0mì[0m[38;5;113m[1m[48;5;0mӧ[0m[38;5;119m[1m[48;5;0mД[0m[38;5;185m[1m[48;5;0mɫ[0m[38;5;113m[1m[48;5;0mŖ[0m[38;5;119m[1m[48;5;0mā[0m[38;5;149m[1m[48;5;0mö[0m[38;5;113m[1m[48;5;0mơ[0m[38;5;83m[1m[48;5;0mɟ[0m[38;5;155m[1m[48;5;0mբ[0m[38;5;149m[1m[48;5;0mƢ[0m[38;5;119m[1m[48;5;0mŊ[0m[38;5;185m[1m[48;5;0mþ[0m[38;5;119m[1m[48;5;0mX[0m[38;5;156m[1m[48;5;0mÊ[0m[38;5;155m[1m[48;5;0mϱ[0m[38;5;185m[1m[48;5;0mՎ[0m[38;5;113m[1m[48;5;0mҲ[0m[38;5;119m[1m[48;5;0mģ[0m[38;5;185m[1m[48;5;0mţ[0m[38;5;113m[1m[48;5;0mՀ[0m[38;5;119m[1m[48;5;0mɄ[0m[38;5;149m[1m[48;5;0mЌ[0m
Returns
Short utf-8 colored textual representation
Expand source code
@property def sansi(self): """ Shorter colored id (base-777 using up to 2 bytes utf8 per char) Usage: >>> from hosh import ø >>> print((ø * b'65e987978g').sansi) \x1b[38;5;156m\x1b[1m\x1b[48;5;0mȟ\x1b[0m\x1b[38;5;155m\x1b[1m\x1b[48;5;0mɟ\x1b[0m\x1b[38;5;185m\x1b[1m\x1b[48;5;0mì\x1b[0m\x1b[38;5;113m\x1b[1m\x1b[48;5;0mӧ\x1b[0m\x1b[38;5;119m\x1b[1m\x1b[48;5;0mД\x1b[0m\x1b[38;5;185m\x1b[1m\x1b[48;5;0mɫ\x1b[0m\x1b[38;5;113m\x1b[1m\x1b[48;5;0mŖ\x1b[0m\x1b[38;5;119m\x1b[1m\x1b[48;5;0mā\x1b[0m\x1b[38;5;149m\x1b[1m\x1b[48;5;0mö\x1b[0m\x1b[38;5;113m\x1b[1m\x1b[48;5;0mơ\x1b[0m\x1b[38;5;83m\x1b[1m\x1b[48;5;0mɟ\x1b[0m\x1b[38;5;155m\x1b[1m\x1b[48;5;0mբ\x1b[0m\x1b[38;5;149m\x1b[1m\x1b[48;5;0mƢ\x1b[0m\x1b[38;5;119m\x1b[1m\x1b[48;5;0mŊ\x1b[0m\x1b[38;5;185m\x1b[1m\x1b[48;5;0mþ\x1b[0m\x1b[38;5;119m\x1b[1m\x1b[48;5;0mX\x1b[0m\x1b[38;5;156m\x1b[1m\x1b[48;5;0mÊ\x1b[0m\x1b[38;5;155m\x1b[1m\x1b[48;5;0mϱ\x1b[0m\x1b[38;5;185m\x1b[1m\x1b[48;5;0mՎ\x1b[0m\x1b[38;5;113m\x1b[1m\x1b[48;5;0mҲ\x1b[0m\x1b[38;5;119m\x1b[1m\x1b[48;5;0mģ\x1b[0m\x1b[38;5;185m\x1b[1m\x1b[48;5;0mţ\x1b[0m\x1b[38;5;113m\x1b[1m\x1b[48;5;0mՀ\x1b[0m\x1b[38;5;119m\x1b[1m\x1b[48;5;0mɄ\x1b[0m\x1b[38;5;149m\x1b[1m\x1b[48;5;0mЌ\x1b[0m Returns ------- Short utf-8 colored textual representation """ if self._sansi_light is None: self._sansi_light, self._sansi_dark = id2ansi(self.sid) return self._sansi_dark if GLOBAL["dark_theme"] else self._sansi_light
var shtml
-
Short colored html digest
Expand source code
@property def shtml(self): """Short colored html digest""" return ansi2html(self.sansi)
var sid
-
Shorter id (base-777 using up to 2 bytes utf8 per char)
Usage:
>>> from hosh import ø >>> (ø * b'65e987978g').sid 'ȟɟìӧДɫŖāöơɟբƢŊþXÊϱՎҲģţՀɄЌ'
Returns
Short utf-8 textual representation
Expand source code
@property def sid(self): """ Shorter id (base-777 using up to 2 bytes utf8 per char) Usage: >>> from hosh import ø >>> (ø * b'65e987978g').sid 'ȟɟìӧДɫŖāöơɟբƢŊþXÊϱՎҲģţՀɄЌ' Returns ------- Short utf-8 textual representation """ if self._sid is None: self._sid = b777enc(self.n, self.digits * 5 // 8) return self._sid
var sidc
-
Expand source code
@property def sidc(self): # pragma: no cover print("'hosh.sidc' is deprecated, please use 'hosh.sansi' instead") return self.sansi
var ø
-
Identity element compatible with this Hosh object
Usage:
>>> from hosh import Hosh >>> (b := Hosh(b"23987rg23")).id 'J5.uRTue8X4r1xu.JFkPbURVVGvTRPSFLncXdyzj' >>> b.etype 'ordered' >>> b.ø.etype 'unordered' >>> b.etype == b.ø.etype_inducer True >>> b.ø.id '0000000000000000000000000000000000000000' >>> (b.ø * b"qwer").etype 'ordered'
Expand source code
@property def ø(self): """Identity element compatible with this Hosh object Usage: >>> from hosh import Hosh >>> (b := Hosh(b"23987rg23")).id 'J5.uRTue8X4r1xu.JFkPbURVVGvTRPSFLncXdyzj' >>> b.etype 'ordered' >>> b.ø.etype 'unordered' >>> b.etype == b.ø.etype_inducer True >>> b.ø.id '0000000000000000000000000000000000000000' >>> (b.ø * b"qwer").etype 'ordered' """ if self._ø is None: from hosh import Identity self._ø = Identity(version=self.version, etype_inducer=self.etype) return self._ø
Methods
def bad_additive_component(self, i, n)
-
Return the
i
-th additive component forx
such thatx = x1 + x2 + ... + xn
See
bad_additive_components
for more details.>>> from functools import reduce >>> import operator >>> list(Hosh(b"x").bad_additive_components(2))[0] == Hosh(b"x").bad_additive_component(0, 2) True >>> list(Hosh(b"x").bad_additive_components(2))[1] == Hosh(b"x").bad_additive_component(1, 2) True >>> list(Hosh(b"x").bad_additive_components(3))[0] == Hosh(b"x").bad_additive_component(0, 3) True >>> list(Hosh(b"x").bad_additive_components(3))[1] == Hosh(b"x").bad_additive_component(1, 3) True >>> list(Hosh(b"x").bad_additive_components(3))[2] == Hosh(b"x").bad_additive_component(2, 3) True >>> list(Hosh(b"x").bad_additive_components(5))[0] == Hosh(b"x").bad_additive_component(0, 5) True >>> list(Hosh(b"x").bad_additive_components(5))[1] == Hosh(b"x").bad_additive_component(1, 5) True >>> list(Hosh(b"x").bad_additive_components(5))[2] == Hosh(b"x").bad_additive_component(2, 5) True >>> list(Hosh(b"x").bad_additive_components(5))[4] == Hosh(b"x").bad_additive_component(4, 5) True >>> list(Hosh(b"x").bad_additive_components(7))[0] == Hosh(b"x").bad_additive_component(0, 7) True >>> list(Hosh(b"x").bad_additive_components(7))[1] == Hosh(b"x").bad_additive_component(1, 7) True >>> list(Hosh(b"x").bad_additive_components(7))[2] == Hosh(b"x").bad_additive_component(2, 7) True >>> list(Hosh(b"x").bad_additive_components(7))[4] == Hosh(b"x").bad_additive_component(4, 7) True
Expand source code
def bad_additive_component(self, i, n): """ Return the `i`-th additive component for `x` such that `x = x1 + x2 + ... + xn` See `bad_additive_components` for more details. >>> from functools import reduce >>> import operator >>> list(Hosh(b"x").bad_additive_components(2))[0] == Hosh(b"x").bad_additive_component(0, 2) True >>> list(Hosh(b"x").bad_additive_components(2))[1] == Hosh(b"x").bad_additive_component(1, 2) True >>> list(Hosh(b"x").bad_additive_components(3))[0] == Hosh(b"x").bad_additive_component(0, 3) True >>> list(Hosh(b"x").bad_additive_components(3))[1] == Hosh(b"x").bad_additive_component(1, 3) True >>> list(Hosh(b"x").bad_additive_components(3))[2] == Hosh(b"x").bad_additive_component(2, 3) True >>> list(Hosh(b"x").bad_additive_components(5))[0] == Hosh(b"x").bad_additive_component(0, 5) True >>> list(Hosh(b"x").bad_additive_components(5))[1] == Hosh(b"x").bad_additive_component(1, 5) True >>> list(Hosh(b"x").bad_additive_components(5))[2] == Hosh(b"x").bad_additive_component(2, 5) True >>> list(Hosh(b"x").bad_additive_components(5))[4] == Hosh(b"x").bad_additive_component(4, 5) True >>> list(Hosh(b"x").bad_additive_components(7))[0] == Hosh(b"x").bad_additive_component(0, 7) True >>> list(Hosh(b"x").bad_additive_components(7))[1] == Hosh(b"x").bad_additive_component(1, 7) True >>> list(Hosh(b"x").bad_additive_components(7))[2] == Hosh(b"x").bad_additive_component(2, 7) True >>> list(Hosh(b"x").bad_additive_components(7))[4] == Hosh(b"x").bad_additive_component(4, 7) True """ den = n * (n + 1) // 2 p = self.p i += 1 toggle = 1 if i == n else 0 def fac(x): parc, rem = divmod(x + p, den) return (i * parc + toggle * rem) % p return Hosh(list(fac(c) for c in self.cells), version=self.version)
def bad_additive_components(self, n)
-
Return the
n
additive components forx
such thatx = x1 + x2 + ... + xn
xn
fills the gap left by the other components remainder. We do not recommend this "decomposition" as it always generates different ids for the same subvalue. For instance, if a list[value1, value2, value3]
induces idsa
,b
, andany
, another list[value1, value2, …]
necessarily inducesc
andd
as first two ids such thata != c
andb != d
.Parameters
n Desired total number of components
Returns
Generator of hoshes
>>> from functools import reduce >>> import operator >>> reduce(operator.add, Hosh(b"x").bad_additive_components(5)) == Hosh(b"x") True
Expand source code
def bad_additive_components(self, n): """ Return the `n` additive components for `x` such that `x = x1 + x2 + ... + xn` `xn` fills the gap left by the other components remainder. We do not recommend this "decomposition" as it always generates different ids for the same subvalue. For instance, if a list `[value1, value2, value3]` induces ids `a`, `b`, and `any`, another list `[value1, value2, ...]` necessarily induces `c` and `d` as first two ids such that `a != c` and `b != d`. Parameters ========== n Desired total number of components Returns ======= Generator of hoshes >>> from functools import reduce >>> import operator >>> reduce(operator.add, Hosh(b"x").bad_additive_components(5)) == Hosh(b"x") True """ den = n * (n + 1) // 2 p = self.p def fac(x): parc, rem = divmod(x + p, den) lst = [i * parc for i in range(1, n + 1)] lst[-1] += rem return [l % p for l in lst] return (Hosh(list(x), version=self.version) for x in zip(*(fac(c) for c in self.cells)))
def components(self, start, stop, n, additive=False)
-
Pseudo"decomposition" based on the hosh of the current id concatenated as bytes to a given component index
Perform a multiplicative decomposition by default.
Syntax
Hosh(b"blob").components(i, m, n) # Takes a slice of elements. Hosh(b"blob")[i:m, n] # Takes a slice of elements. Hosh(b"blob")[i, n] # Takes element
i
out ofn
components. Hosh(b"blob")[:n, n] # Alln
elements.Warning
Hosh(b"blob")[-1] # Reverse element. Not to be confused with inverse element.
The components are arbitrarily internally defined group elements based on current id as hashed bytes: Hosh(id+"-1"), Hosh(id+"-2"), …, Hosh(id+"-n")
The last element (xn) is the exception as it makes the product x1 * x2 * … * xn match x: x1 = id+"-1" * x … xn-1 = id+"-n-1" * x xn = (id+"-1" * x * … * id+"-n-1")-¹
Parameters
start Start of a slice stop Stop of a slice n Desired total number of components additive Set up an additive decomposition
Returns
List if hoshes
>>> from hosh import Hosh >>> a = Hosh(b"a") >>> a[-1].rev == a True >>> a[0, 1] == a True >>> a[0:, 1][0] == a True >>> a[:1, 1][0] == a True >>> a[0:1, 1][0] == a True >>> from operator import mul >>> reduce(mul, a[:, 3]) == a True >>> [x.id for x in a[:, 3]] ['Bd6Axil5pFSp15HUBz8eCujvu3gBsEk6XMpRsMNo', '32MloLPcivDbbPMCJn1RBY31aNZ6z-Dqnt4vQhot', 'la3xnZmlhn3lFBAnvWw-UWAvK.2hk-QqUNFYAs3e'] >>> a[:, 3][0] * a[:, 3][1] * a[:, 3][2] == a True >>> a[0, 3] * a[1, 3] * a[2, 3] == a True >>> a.id 'cIXBKPediDiOKabeZ6SthD04rnzaquNXaAEhSud4' >>> from operator import add, mul >>> from functools import reduce >>> print("\n".join(x.id for x in a.components(0, 7, 7))) Bd6Axil5pFSp15HUBz8eCujvu3gBsEk6XMpRsMNo 32MloLPcivDbbPMCJn1RBY31aNZ6z-Dqnt4vQhot Y2JlyuF8.KJc0DvvcIivLA5uLYloF7HN9ovO14Sq 6.F-NB-G4vBXs7evbBImex9x3foNi85Ca7wDb1c3 tZmUsjVcZAGTajUOzsSNrr7a7BQVNSiA6xaiPEYf iCLMdAlduXvUtK1.awng0D0YP49kV8Cit7OLXyab BA6UvITsIN822llT9eErc1R0rmf.ARbc0adwEbWk >>> reduce(mul, a.components(0, 2, 7)) * reduce(mul, a.components(2, 3, 7)) * reduce(mul, a.components(3, 7, 7)) == a True >>> reduce(mul, a.components(0, 7, 7)) == a True
>>> print("\n".join(x.id for x in a.components(0, 7, 7, additive=True))) Bd6Axil5pFSp15HUBz8eCujvu3gBsEk6XMpRsMNo 32MloLPcivDbbPMCJn1RBY31aNZ6z-Dqnt4vQhot Y2JlyuF8.KJc0DvvcIivLA5uLYloF7HN9ovO14Sq 6.F-NB-G4vBXs7evbBImex9x3foNi85Ca7wDb1c3 tZmUsjVcZAGTajUOzsSNrr7a7BQVNSiA6xaiPEYf iCLMdAlduXvUtK1.awng0D0YP49kV8Cit7OLXyab 7jJmsfrPpa2CeeYCAiByF3HW2J9.ARbc0adwEbWk >>> reduce(add, a.components(0, 2, 7, additive=True)) + reduce(add, a.components(2, 3, 7, additive=True)) + reduce(add, a.components(3, 7, 7, additive=True)) == a True >>> reduce(add, a.components(0, 7, 7, additive=True)) == a True
Expand source code
def components(self, start, stop, n, additive=False): r""" Pseudo"decomposition" based on the hosh of the current id concatenated as bytes to a given component index Perform a multiplicative decomposition by default. Syntax: Hosh(b"blob").components(i, m, n) # Takes a slice of elements. Hosh(b"blob")[i:m, n] # Takes a slice of elements. Hosh(b"blob")[i, n] # Takes element `i` out of `n` components. Hosh(b"blob")[:n, n] # All `n` elements. Warning: Hosh(b"blob")[-1] # Reverse element. Not to be confused with inverse element. The components are arbitrarily internally defined group elements based on current id as hashed bytes: Hosh(id+"-1"), Hosh(id+"-2"), ..., Hosh(id+"-n") The last element (xn) is the exception as it makes the product x1 * x2 * ... * xn match x: x1 = id+"-1" * x ... xn-1 = id+"-n-1" * x xn = (id+"-1" * x * ... * id+"-n-1")-¹ Parameters ========== start Start of a slice stop Stop of a slice n Desired total number of components additive Set up an additive decomposition Returns ======= List if hoshes >>> from hosh import Hosh >>> a = Hosh(b"a") >>> a[-1].rev == a True >>> a[0, 1] == a True >>> a[0:, 1][0] == a True >>> a[:1, 1][0] == a True >>> a[0:1, 1][0] == a True >>> from operator import mul >>> reduce(mul, a[:, 3]) == a True >>> [x.id for x in a[:, 3]] ['Bd6Axil5pFSp15HUBz8eCujvu3gBsEk6XMpRsMNo', '32MloLPcivDbbPMCJn1RBY31aNZ6z-Dqnt4vQhot', 'la3xnZmlhn3lFBAnvWw-UWAvK.2hk-QqUNFYAs3e'] >>> a[:, 3][0] * a[:, 3][1] * a[:, 3][2] == a True >>> a[0, 3] * a[1, 3] * a[2, 3] == a True >>> a.id 'cIXBKPediDiOKabeZ6SthD04rnzaquNXaAEhSud4' >>> from operator import add, mul >>> from functools import reduce >>> print("\n".join(x.id for x in a.components(0, 7, 7))) Bd6Axil5pFSp15HUBz8eCujvu3gBsEk6XMpRsMNo 32MloLPcivDbbPMCJn1RBY31aNZ6z-Dqnt4vQhot Y2JlyuF8.KJc0DvvcIivLA5uLYloF7HN9ovO14Sq 6.F-NB-G4vBXs7evbBImex9x3foNi85Ca7wDb1c3 tZmUsjVcZAGTajUOzsSNrr7a7BQVNSiA6xaiPEYf iCLMdAlduXvUtK1.awng0D0YP49kV8Cit7OLXyab BA6UvITsIN822llT9eErc1R0rmf.ARbc0adwEbWk >>> reduce(mul, a.components(0, 2, 7)) * reduce(mul, a.components(2, 3, 7)) * reduce(mul, a.components(3, 7, 7)) == a True >>> reduce(mul, a.components(0, 7, 7)) == a True >>> print("\n".join(x.id for x in a.components(0, 7, 7, additive=True))) Bd6Axil5pFSp15HUBz8eCujvu3gBsEk6XMpRsMNo 32MloLPcivDbbPMCJn1RBY31aNZ6z-Dqnt4vQhot Y2JlyuF8.KJc0DvvcIivLA5uLYloF7HN9ovO14Sq 6.F-NB-G4vBXs7evbBImex9x3foNi85Ca7wDb1c3 tZmUsjVcZAGTajUOzsSNrr7a7BQVNSiA6xaiPEYf iCLMdAlduXvUtK1.awng0D0YP49kV8Cit7OLXyab 7jJmsfrPpa2CeeYCAiByF3HW2J9.ARbc0adwEbWk >>> reduce(add, a.components(0, 2, 7, additive=True)) + reduce(add, a.components(2, 3, 7, additive=True)) + reduce(add, a.components(3, 7, 7, additive=True)) == a True >>> reduce(add, a.components(0, 7, 7, additive=True)) == a True """ if stop > n: # pragma: no cover raise Exception(f"Wrong value: stop=`{stop}` >= n=`{n}`") acc = self.ø operator = add if additive else mul for i in range(0, stop): if stop == n and i == stop - 1: break if (t := (i, n, additive)) not in self._composition_memo: self._composition_memo[t] = Hosh(f"{self.id}-{i}".encode(), version=self.version) if len(self._composition_memo) > self.components_cache_size: # pragma: no cover first = next(iter(self._composition_memo)) del self._composition_memo[first] h = self._composition_memo[t] acc = operator(acc, h) if i >= start: yield h if stop == n: inv = Hosh(cellsinv(acc.cells, self.p, additive), version=self.version) last = operator(inv, self) yield last
def convert(self, other)
-
Usage:
>>> from hosh import ø >>> ø.convert([0,0,0,0,0,0]).id '0000000000000000000000000000000000000000'
>>> from hosh import Hosh >>> ø.convert(0).id '0000000000000000000000000000000000000000'
Parameters
other
Returns
Expand source code
def convert(self, other): """ Usage: >>> from hosh import ø >>> ø.convert([0,0,0,0,0,0]).id '0000000000000000000000000000000000000000' >>> from hosh import Hosh >>> ø.convert(0).id '0000000000000000000000000000000000000000' Parameters ---------- other Returns ------- """ if isinstance(other, str): other = Hosh.fromid(other) elif isinstance(other, bytes): other = Hosh(other, etype=self.etype_inducer, version=self.version) elif isinstance(other, int): other = Hosh.fromn(other, version=self.version) elif isinstance(other, list): other = Hosh(other, version=self.version) elif not isinstance(other, Hosh): return NotImplemented if self.version != other.version: raise WrongVersion(f"Incompatible operands: {self.version} != {other.version}") return other
def power_component(self, i, n)
-
Elements corresponding to
n
components of "multiplicative decomposition" such thatx = x1 * x2 * x3 * ... * xn = x * x² * x³ * ... * x^n
Not very useful as the resulting elements commute among themselves. This happens because they are all powers of x, making up just a sequence of
x
s .Parameters
i Desired component index n Desired total number of components
Returns
Hosh (component)
>>> a = Hosh(b"a") >>> a.power_component(0, 1) == a True >>> a.power_component(0, 2) * a.power_component(1, 2) == a True >>> a.power_component(0, 3) * a.power_component(1, 3) * a.power_component(2, 3) == a True >>> a.power_component(2, 3) * a.power_component(1, 3) * a.power_component(0, 3) == a True
Expand source code
def power_component(self, i, n): """Elements corresponding to `n` components of "multiplicative decomposition" such that `x = x1 * x2 * x3 * ... * xn = x * x² * x³ * ... * x^n` Not very useful as the resulting elements commute among themselves. This happens because they are all powers of x, making up just a sequence of `x`s . Parameters ========== i Desired component index n Desired total number of components Returns ======= Hosh (component) >>> a = Hosh(b"a") >>> a.power_component(0, 1) == a True >>> a.power_component(0, 2) * a.power_component(1, 2) == a True >>> a.power_component(0, 3) * a.power_component(1, 3) * a.power_component(2, 3) == a True >>> a.power_component(2, 3) * a.power_component(1, 3) * a.power_component(0, 3) == a True """ if i >= n: # pragma: no cover raise Exception(f"Hosh component should be defined by 'index' ({i}) < '#components' ({n})") if n == 1: return self exp = n * (n + 1) // 2 r = self.root(exp) return r ^ (i + 1)
def root(self, k)
-
>>> a = Hosh(b"a") >>> for i in range(1, 5): ... r = a.root(i) ... r^i == a True True True True
Expand source code
def root(self, k): """ >>> a = Hosh(b"a") >>> for i in range(1, 5): ... r = a.root(i) ... r^i == a True True True True """ if k == 1: return self return Hosh(cellsroot(self.cells, k, self.p), version=self.version)
def short(self, colored=True)
-
Usage:
>>> Hosh(b"asdf86fasd").short(colored=False) lϊӑơӫǯÃϺŮϳȐŁЬĽҪƉǏԛȪƜfÞӠȕՇ
Expand source code
def short(self, colored=True): """ Usage: >>> Hosh(b"asdf86fasd").short(colored=False) lϊӑơӫǯÃϺŮϳȐŁЬĽҪƉǏԛȪƜfÞӠȕՇ """ return print(self.sansi if colored else self.sid)
def show(self, colored=True)
-
Usage:
>>> Hosh(b"asdf86fasd").show(colored=False) voh8t1KrYmzCqpyrUO9.5QbGdouoZsnExarMSa34
Expand source code
def show(self, colored=True): """ Usage: >>> Hosh(b"asdf86fasd").show(colored=False) voh8t1KrYmzCqpyrUO9.5QbGdouoZsnExarMSa34 """ return print(self.ansi if colored else self.id)