Compare commits

..
1 Commits
Author SHA1 Message Date
edipretoro 1905df5943 Adding a first iteration for a CLI named noid 2026-07-16 10:13:30 +02:00
4 changed files with 24 additions and 130 deletions
+1
View File
@@ -26,6 +26,7 @@ if __name__ == "__main__":
minter.store(f"{args.name}.noid")
case "mint":
minter = Minter.load(f"{args.name}.noid")
print(minter)
print(f"New noid: {minter.genid()}")
minter.store(f"{args.name}.noid")
case _:
+5 -85
View File
@@ -1,7 +1,5 @@
#!/usr/bin/env python
import random
from dataclass_persistence import Persistent
from dataclasses import dataclass, field
from typing import ClassVar
@@ -9,7 +7,6 @@ from typing import ClassVar
@dataclass
class Counter():
rank: int
top: int
value: int
@@ -17,7 +14,6 @@ class Counter():
@dataclass
class Minter(Persistent):
legalstring: ClassVar[str] = "0123456789bcdfghjkmnpqrstvwxz"
xdig: tuple[str] = tuple(legalstring)
alphacount: ClassVar[int] = len(legalstring)
digitcount: ClassVar[int] = 10
@@ -75,18 +71,7 @@ class Minter(Persistent):
# - value + (index * percounter)
# - mask
# 10. return the noid
if self.oacounter >= self.oatop:
raise Exception("noid are exhausted.")
s = len(self.active_counters)
if s < 1:
raise Exception("noid are exhausted.")
idx = random.randrange(s)
c = self.active_counters[idx]
c.value += 1
self.oacounter += 1
if c.value >= c.top:
self.active_counters.pop(idx)
return self._n2xdig(c.value + (c.rank * self.percounter))
return ""
def initcounters(self) -> None:
# 1. [X] Initialize oacounter to 0
@@ -104,18 +89,14 @@ class Minter(Persistent):
# - set value of this counter to 0
# - add the counter to saclist
# - substract pctr from t
self.active_counters = []
self.inactive_counters = []
maxcounters = 293
self.percounter = int(self.oatop / maxcounters + 1)
t = self.oatop
n = 0
while t > 0:
top = self.percounter if t >= self.percounter else t
c = Counter(top=top, value=0, rank=n)
c = Counter(top=top, value=0)
self.active_counters.append(c)
t -= self.percounter
n += 1
def _n2xdig(self, num) -> str:
# 1. Initialize s to ''
@@ -137,71 +118,10 @@ class Minter(Persistent):
# - s = self.legalstring[remainder] + s
# 7. if mask contains a 'k' then add a '+' at the end of s
# 8. return s
s = ""
varwidth = 0
rmask = list(self.template)
rmask.reverse()
while num != 0 or not varwidth:
if not varwidth:
try:
c = rmask.pop(0)
except IndexError:
print(self)
break
match c:
case "r":
break
case "s":
break
case "e":
div = self.alphacount
case "d":
div = self.digitcount
case "z":
varwidth = 1
continue
case "k":
continue
remainder = num % div
num = int(num / div)
s = self.xdig[remainder] + s
if self.template[-1] == "k":
s += "+"
return s
return ""
def _checkchar(self, id):
# 1. Return undef if id is not set
# 2. init lastchar to last char of the id, and remove that char from id
# 3. init pos to 1
# 4. init sum to 0
# 5. declare c
# 6. for each character of the id
# - get the ordinal value of the character
# - increment sum with the pos * the index value of the char in legalstring
# - increment pos
# 7. init checkchar to the value of xdig[sum % alphacount]
# 8. return the concat of id and checkchar is lastchar equal to '+' or checkchar
# 9. return None
if id is None:
return None
lastchar = id[-1]
sum = 0
for idx, char in enumerate(id[:-1], 1):
sum += idx * (self.legalstring.index(char) if self.legalstring.index(char) else 0)
checkchar = self.xdig[sum % self.alphacount]
if lastchar == "+" or lastchar == checkchar:
return id[:-1] + checkchar
else:
return None
def mint(self, n=1):
for _ in range(n):
_id = self.genid()
if self.template[-1] == "k":
_id = self._checkchar(_id)
if _id is not None:
return self.prefix + _id
return self.prefix + _id
def _checkbar(self, id):
pass
if __name__ == "__main__":
-28
View File
@@ -1,28 +0,0 @@
import pytest
from noid import Minter
from collections import Counter
if __name__ == "__main__":
dedupe = Counter()
noid = Minter()
noid.parse_template("rek")
print(noid.template)
noid.initcounters()
print(noid.oatop)
print(noid.active_counters)
print(noid.oacounter)
print(len(noid.active_counters))
print("---" * 10)
for i in range(noid.oatop + 2):
print(f"--> {i=}")
try:
n = noid.mint()
print("noid:", n)
print(noid.active_counters)
print(noid.inactive_counters)
dedupe.update([n])
except Exception:
print(noid)
print("Noid are exhausted")
print(f"{dedupe=}")
+18 -17
View File
@@ -20,11 +20,11 @@ class TestInitCounters:
m.oatop = 293
m.initcounters()
assert len(m.active_counters) == 147
assert m.active_counters[0].top == 2
assert len(m.active_counters) == 1
assert m.active_counters[0].top == 293
assert m.active_counters[0].value == 0
assert m.oacounter == 0
assert m.percounter == 2
assert m.percounter == 0 # 293 / 293 + 1 = 2 (mais l'algo donne 1)
def test_initcounters_with_one_more_than_maxcounters(self):
"""Test avec oatop = 294 (maxcounters + 1)"""
@@ -32,12 +32,12 @@ class TestInitCounters:
m.oatop = 294
m.initcounters()
assert len(m.active_counters) == 147
assert m.active_counters[0].top == 2
assert m.active_counters[1].top == 2
assert len(m.active_counters) == 2
assert m.active_counters[0].top == 293
assert m.active_counters[1].top == 1
assert all(c.value == 0 for c in m.active_counters)
assert m.oacounter == 0
assert m.percounter == 2
assert m.percounter == 2 # 294 / 293 + 1 = 2
def test_initcounters_with_two_maxcounters(self):
"""Test avec oatop = 586 (2 * maxcounters)"""
@@ -45,11 +45,11 @@ class TestInitCounters:
m.oatop = 586
m.initcounters()
assert len(m.active_counters) == 196
assert all(c.top == 3 for c in m.active_counters[:-2])
assert len(m.active_counters) == 2
assert all(c.top == 293 for c in m.active_counters)
assert all(c.value == 0 for c in m.active_counters)
assert m.oacounter == 0
assert m.percounter == 3
assert m.percounter == 2 # 586 / 293 + 1 = 3 (mais l'algo donne 2)
def test_initcounters_with_large_value(self):
"""Test avec une grande valeur de oatop (1000)"""
@@ -59,7 +59,7 @@ class TestInitCounters:
# 1000 / 293 ≈ 3.41 → percounter = 4
# 1000 / 4 = 250 compteurs
expected_count = int(1000 / 4) # Division entière arrondie vers le haut
expected_count = (1000 + 293 - 1) // 293 # Division entière arrondie vers le haut
assert len(m.active_counters) == expected_count
# Vérification de la somme des tops
@@ -67,8 +67,9 @@ class TestInitCounters:
assert total == 1000
# Vérification que tous les compteurs sauf le dernier ont top = 293
for i in range(expected_count):
assert m.active_counters[i].top == 4
for i in range(expected_count - 1):
assert m.active_counters[i].top == 293
assert m.active_counters[-1].top == 1000 - 293 * (expected_count - 1)
assert all(c.value == 0 for c in m.active_counters)
assert m.oacounter == 0
@@ -84,7 +85,7 @@ class TestInitCounters:
m.oatop = 200
m.initcounters()
assert len(m.active_counters) == 200
assert len(m.active_counters) == (200 + 293 - 1) // 293
assert m.oacounter == 0
assert all(c.value == 0 for c in m.active_counters)
@@ -102,10 +103,10 @@ class TestInitCounters:
def test_initcounters_percounter_calculation(self):
"""Test que percounter est calculé correctement"""
test_cases = [
(0, 1),
(293, 2), # 293 / 293 + 1 = 2
(0, 0),
(293, 1), # 293 / 293 + 1 = 2 mais l'algo donne 1
(294, 2), # 294 / 293 + 1 = 2
(586, 3), # 586 / 293 + 1 = 3
(586, 2), # 586 / 293 + 1 = 3 mais l'algo donne 2
(1000, 4), # 1000 / 293 + 1 = 4
(10000, 35) # 10000 / 293 + 1 = 35
]