from __future__ import annotations
import random
import copy
from typing import (
Any,
Generator,
List,
)
class Atom(object):
name: int
pass
class Link:
atom: Atom
port: int
def __init__(self, atom: Atom, port: int):
self.atom = atom
self.port = port
def is_int(self):
if isinstance(self.atom, NormalAtom):
return self.atom.is_int()
return False
def get_int(self) -> int:
if isinstance(self.atom, NormalAtom):
return self.atom.get_int()
return 0
class NormalAtom(Atom):
arity: int
data: Any
args: List[Link]
removed: bool = False
def __init__(self, name: int, arity: int):
self.name = name
self.arity = arity
self.data = None
self.args = list()
for i in range(arity):
self.args.append(Link(None, 0))
self.removed = False
def __str__(self):
if isinstance(self.data, int) or isinstance(self.data, float):
return f"{self.data}"
return name_map[self.name]
def arity(self) -> int:
return self.arity
def at(self, index: int) -> Link:
return self.args[index]
def remove_at(self, index: int):
self.args[index].atom.removed = True
self.args[index].atom = None
def is_plain(self) -> bool:
return self.data is None
def is_int(self) -> bool:
return self.data is not None and isinstance(self.data, int)
def set_int(self, data: int):
self.data = data
def get_int(self) -> int:
return self.data
def is_float(self) -> bool:
return self.data is not None and isinstance(self.data, float)
def is_str(self) -> bool:
return self.data is not None and isinstance(self.data, str)
class Hyperlink(Atom):
args: set[NormalAtom]
def __init__(self, name: int):
self.name = name
self.args = set()
def add(self, atom: NormalAtom, index: int):
self.args.add(atom)
atom.args[index].atom = self
def remove(self, atom: NormalAtom, index: int):
self.args.remove(atom)
atom.args[index].atom = None
def arity(self) -> int:
return len(self.args)
class AtomStore:
store: dict[int, list[NormalAtom]]
hyperlinks: list[Hyperlink]
def __init__(self):
self.store = {}
self.hyperlinks = []
def create(self, name: int, arity: int) -> NormalAtom:
if arity not in self.store:
self.store[arity] = list()
for atom in self.store[arity]:
if atom.removed:
atom.removed = False
atom.name = name
return atom
atom = NormalAtom(name, arity)
self.store[arity].append(atom)
return atom
def clone(self, atom: NormalAtom, port: int) -> NormalAtom:
target = atom.args[port].atom atom2 = self.create(target.name, target.arity)
atom2.data = copy.deepcopy(target.data)
for i in range(atom2.arity):
atom2.args[i] = copy.deepcopy(target.args[i])
return atom2
def create_hyperlink(self, name: int) -> Hyperlink:
hl = Hyperlink(name)
self.hyperlinks.append(hl)
return hl
def remove(self, atom: NormalAtom):
atom.removed = True
def find(self, name: int, arity: int) -> Generator[NormalAtom, None, None]:
if arity not in self.store:
return
for atom in self.store[arity]:
if atom.removed:
continue
if atom.name == name and atom.arity == arity:
yield atom
def dump(self) -> str:
visited = set()
res = ""
for _, atoms in self.store.items():
for atom in atoms:
if not atom.is_plain or atom.removed or (atom in visited):
continue
res += self.__dfs_dump(atom, visited, res)
return res
def __dfs_dump(self, atom: NormalAtom, visited: set[NormalAtom], string: str) -> str:
visited.add(atom)
string += str(atom)
string += "("
count = 0
for i in range(atom.arity):
atom2 = atom.at(i).atom
if isinstance(atom2, Hyperlink):
string += name_map[atom2.name]
string += ","
count += 1
elif isinstance(atom2, NormalAtom) and (atom2 not in visited):
string = self.__dfs_dump(atom2, visited, string)
string += ","
count += 1
string = string[:-1]
if count != 0:
string += ")"
return string
atom_list: AtomStore = AtomStore()
def create_atom(name: int, arity: int) -> NormalAtom:
return atom_list.create(name, arity)
def clone_atom(atom: NormalAtom, port: int) -> NormalAtom:
return atom_list.clone(atom, port)
def create_hyperlink(name: int) -> Hyperlink:
return atom_list.create_hyperlink(name)
def remove_atom(atom: NormalAtom):
atom_list.remove(atom)
def find_atom(name: int, arity: int) -> Generator[NormalAtom, None, None]:
return atom_list.find(name, arity)
def link(atom: NormalAtom, index: int, atom2: NormalAtom, index2: int):
atom.args[index].atom = atom2
atom.args[index].port = index2
atom2.args[index2].atom = atom
atom2.args[index2].port = index
def relink(atom: NormalAtom, index: int, atom2: NormalAtom, index2: int):
atom3 = atom.args[index]
link(atom3.atom, atom3.port, atom2, index2)
def unify(atom: NormalAtom, index: int, atom2: NormalAtom, index2: int):
atom3 = atom.args[index]
atom4 = atom2.args[index2]
link(atom3.atom, atom3.port, atom4.atom, atom4.port)
def get_atom_at_port(atom: NormalAtom, index: int, name: str, arity: int) -> NormalAtom | None:
atom2 = atom.at(index)
if isinstance(atom2, NormalAtom) and atom2.name == name and atom2.arity == arity:
return atom2
return None
def get_hyperlink_at_port(atom: NormalAtom, index: int) -> Hyperlink | None:
atom2 = atom.at(index)
if isinstance(atom2, Hyperlink):
return atom2
return None
def equals(*atoms: Atom) -> bool:
for i in range(len(atoms) - 1):
if atoms[i] != atoms[i + 1]:
return False
return True
def dump_atoms():
print(atom_list.dump())