Ion Petre

139 papers A* 1A 1B 10C 6Journal 85Unranked 25
YearRankTypeTitle / Venue / Authors
2025 B conf
KES
Bogdan Oancea, Marian Necula, Eduard-Costin Milea, Alexandru Amarioarei, Ion Petre, Mihaela-Marinela Paun
2025 J jnl
Nat. Comput.
Artur Meski, Maciej Koutny, Lukasz Mikulski, Ion Petre, Wojciech Penczek, Marcin Piatkowski
2025 conf
Languages of Cooperation and Communication
Paolo Bottoni, Victor Mitrana, Ion Petre
2025 J jnl
Nat. Comput.
Linda Brodo, Roberta Gori, Paolo Milazzo, Ion Petre
2025 J jnl
Nat. Comput.
Natasha Jonoska, Ion Petre, Grzegorz Rozenberg
2025 B conf
KES
Eduard C. Milea, Andreia Alecu, Alice Stoica, Marian Necula, Ion Petre, Simona Litescu, Mihaela Paun
2025 J jnl
IEEE Access
Linda Brodo, Roberto Bruni, Moreno Falaschi, Ion Petre
2025 J jnl
Frontiers Digit. Health
Corina Vernic, Tudor Paul Tamas, Ion Petre, Sorin Ursoniu
2025 J jnl
Bull. EATCS
Juhani Karhumäki, Jarkko Kari, Lila Kari, Hermann Maurer, Ion Petre, Grzegorz Rozenberg
2024 J jnl
Nat. Comput.
Daniela Genova, Ion Petre
2024 J jnl
Theor. Comput. Sci.
Ion Petre, Andrei Paun
2024 J jnl
Fundam. Informaticae
Patric Gustafsson, Ion Petre
2023 J jnl
CoRR
Patric Gustafsson, Ion Petre
2022 ed.
CMSB
Ion Petre, Andrei Paun
2022 J jnl
Briefings Bioinform.
Nicoleta Siminea, Victor-Bogdan Popescu, José Ángel Sánchez Martín, Daniela Florea, Georgiana Gavril, Ana Maria Gheorghe, Corina Itcus, Krishna Kanhaiya, Octavian Pacioglu, Laura Ioana Popa, Romica Trandafir, Maria Iris Tusa, Manuela Sidoroff, Mihaela Paun, Eugen Czeizler, Andrei Paun, Ion Petre
2021 J jnl
Theor. Comput. Sci.
Lila Kari, Ion Petre, Grzegorz Rozenberg, Arto Salomaa
2021 J jnl
Theor. Comput. Sci.
Paola Bonizzoni, Lila Kari, Ion Petre, Grzegorz Rozenberg
2021 J jnl
Bioinform.
Victor-Bogdan Popescu, José Ángel Sánchez Martín, Daniela Schacherer, Sadra Safadoust, Negin Majidi, Andrei Andronescu, Alexandru Nedea, Diana Ion, Eduard Mititelu, Eugen Czeizler, Ion Petre
2021 J jnl
CoRR
Elio Nushi, Victor-Bogdan Popescu, José Ángel Sánchez Martín, Sergiu Ivanov, Eugen Czeizler, Ion Petre
2021 J jnl
Theor. Comput. Sci.
Lukasz Mikulski, Ion Petre
2021 J jnl
CoRR
Usman Sanwal, Thai Son Hoang, Luigia Petre, Ion Petre
2020 J jnl
Fundam. Informaticae
Luigia Petre, Usman Sanwal, Gohar Shah, Charmi Panchal, Dwitiya Tyagi, Ion Petre
2020 J jnl
CoRR
Sergiu Ivanov, Ion Petre
2020 J jnl
J. Membr. Comput.
Sergiu Ivanov, Ion Petre
2020 J jnl
CoRR
Victor-Bogdan Popescu, Krishna Kanhaiya, Iulian Nastac, Eugen Czeizler, Ion Petre
2020 J jnl
Fundam. Informaticae
José Ángel Sánchez Martín, Ion Petre
2020 J jnl
J. Membr. Comput.
Lukasz Mikulski, Ion Petre
2019 J jnl
Comput. Biol. Medicine
Bogdan Iancu, Usman Sanwal, Cristian Gratie, Ion Petre
2018 J jnl
BMC Bioinform.
Krishna Kanhaiya, Vladimir Rogojin, Keivan Kazemi, Eugen Czeizler, Ion Petre
2018 J jnl
IEEE ACM Trans. Comput. Biol. Bioinform.
Eugen Czeizler, Wu Kai Chiu, Cristian Gratie, Krishna Kanhaiya, Ion Petre
2018 conf
Enjoying Natural Computing
Sergiu Ivanov, Vladimir Rogojin, Sepinoud Azimi, Ion Petre
2017 J jnl
Fundam. Informaticae
Mikhail Barash, Ion Petre
2017 J jnl
Theor. Comput. Sci.
Gheorghe Paun, Ion Petre, Grzegorz Rozenberg, Arto Salomaa
2017 J jnl
Int. J. Found. Comput. Sci.
Sepinoud Azimi, Charmi Panchal, Andrzej Mizera, Ion Petre
2017 conf
The Role of Theory in Computer Science
Andrzej Ehrenfeucht, Ion Petre, Grzegorz Rozenberg
2017 J jnl
Comput. Biol. Medicine
Usman Sanwal, Luigia Petre, Ion Petre
2017 C ed.
CiE
Jarkko Kari, Florin Manea, Ion Petre
2016 J jnl
Theor. Comput. Sci.
Cristian Gratie, Ion Petre
2016 J jnl
Theor. Comput. Sci.
Sepinoud Azimi, Cristian Gratie, Sergiu Ivanov, Luca Manzoni, Ion Petre, Antonio E. Porreca
2016 conf
AlCoB
Charmi Panchal, Sepinoud Azimi, Ion Petre
2016 J jnl
Comput. Sci. J. Moldova
Vladimir Rogojin, Ion Petre
2016 ch.
From Action Systems to Distributed Systems
Diana-Elena Gratie, Bogdan Iancu, Sepinoud Azimi, Ion Petre
2016 conf
CMSB
Eugen Czeizler, Cristian Gratie, Wu Kai Chiu, Krishna Kanhaiya, Ion Petre
2015 conf
Int. Conf. on Membrane Computing
Sepinoud Azimi, Eugen Czeizler, Cristian Gratie, Diana-Elena Gratie, Bogdan Iancu, Nebiat Ibssa, Ion Petre, Vladimir Rogojin, Tolou Shadbahr, Fatemeh Shokri
2015 J jnl
Theor. Comput. Sci.
Sepinoud Azimi, Cristian Gratie, Sergiu Ivanov, Ion Petre
2015 J jnl
Theor. Comput. Sci.
Emanuela Merelli, Ion Petre
2015 conf
BioPPN@Petri Nets
Diana-Elena Gratie, Ion Petre
2015 J jnl
Fundam. Informaticae
Vladimir Rogojin, Ion Petre
2014 C conf
CiE
Cristian Gratie, Ion Petre
2014 conf
AlCoB
Bogdan Iancu, Diana-Elena Gratie, Sepinoud Azimi, Ion Petre
2014 conf
CS2Bio
Emanuela Merelli, Ion Petre
2014 ed.
CS2Bio
Emanuela Merelli, Ion Petre
2014 J jnl
Fundam. Informaticae
Sepinoud Azimi, Bogdan Iancu, Ion Petre
2014 conf
Discrete Mathematics and Computer Science
Sepinoud Azimi, Ion Petre
2013 conf
SFM
Diana-Elena Gratie, Bogdan Iancu, Ion Petre
2013 ed.
CompMod
Ion Petre
2012 J jnl
Fundam. Informaticae
Elena Czeizler, Andrzej Mizera, Ion Petre
2012 ch.
Handbook of Natural Computing
Robert Brijder, Mark Daley, Tero Harju, Natasa Jonoska, Ion Petre, Grzegorz Rozenberg
2012 J jnl
Theor. Comput. Sci.
Ion Petre, Sergey Verlan
2012 J jnl
Nat. Comput.
Jarkko Kari, Ion Petre
2012 J jnl
Theor. Comput. Sci.
Giorgio Ausiello, Hendrik Jan Hoogeboom, Juhani Karhumäki, Ion Petre, Arto Salomaa
2012 J jnl
IEEE ACM Trans. Comput. Biol. Bioinform.
Eugen Czeizler, Andrzej Mizera, Elena Czeizler, Ralph-Johan Back, John E. Eriksson, Ion Petre
2012 J jnl
Int. J. Unconv. Comput.
Bogdan Iancu, Elena Czeizler, Eugen Czeizler, Ion Petre
2012 J jnl
Trans. Comp. Sys. Biology
Andrzej Mizera, Eugen Czeizler, Ion Petre
2012 J jnl
Theor. Comput. Sci.
Sepinoud Azimi, Tero Harju, Miika Langille, Ion Petre
2012 J jnl
IEEE ACM Trans. Comput. Biol. Bioinform.
Eugen Czeizler, Vladimir Rogojin, Ion Petre
2012 ed.
Trans. Computational Systems Biology
Corrado Priami, Ion Petre, Erik P. de Vink
2011 J jnl
Nat. Comput.
Ion Petre, Andrzej Mizera, Claire L. Hyder, Annika Meinander, Andrey Mikhailov, Richard I. Morimoto, Lea Sistonen, John E. Eriksson, Ralph-Johan Back
2011 J jnl
Nat. Comput.
Paolo Bottoni, Anna Labella, Florin Manea, Victor Mitrana, Ion Petre, José M. Sempere
2011 J jnl
Fundam. Informaticae
Sepinoud Azimi, Tero Harju, Miika Langille, Ion Petre, Vladimir Rogojin
2011 J jnl
ERCIM News
Giancarlo Mauri, Ion Petre
2011 ed.
CompMod
Ion Petre, Erik P. de Vink
2011 conf
SASB
Elena Czeizler, Eugen Czeizler, Bogdan Iancu, Ion Petre
2011 conf
CMSB
Eugen Czeizler, Vladimir Rogojin, Ion Petre
2011 ed.
Trans. Computational Systems Biology
Corrado Priami, Ralph-Johan Back, Ion Petre, Erik P. de Vink
2011 C ed.
UC
Cristian S. Calude, Jarkko Kari, Ion Petre, Grzegorz Rozenberg
2010 J jnl
Biosyst.
Sergey Verlan, Artiom Alhazov, Ion Petre
2010 J jnl
Theor. Comput. Sci.
Victor Mitrana, Ion Petre, Vladimir Rogojin
2010 J jnl
Theor. Comput. Sci.
Artiom Alhazov, Chang Li, Ion Petre
2010 J jnl
Theor. Comput. Sci.
Robert Brijder, Miika Langille, Ion Petre
2010 J jnl
Scholarpedia
Ion Petre, Grzegorz Rozenberg
2010 J jnl
CoRR
Ion Petre, Sergey Verlan
2010 J jnl
Comput. Sci. J. Moldova
Miika Langille, Ion Petre, Vladimir Rogojin
2009 conf
Algorithmic Bioprocesses
Ion Petre, Andrzej Mizera, Claire L. Hyder, Andrey Mikhailov, John E. Eriksson, Lea Sistonen, Ralph-Johan Back
2009 C conf
CiE
Ion Petre, Andrzej Mizera, Ralph-Johan Back
2009 conf
CMSB
Elena Czeizler, Eugen Czeizler, Ralph-Johan Back, Ion Petre
2009 ed.
COMPMOD
Ralph-Johan Back, Ion Petre, Erik P. de Vink
2009 J jnl
Theor. Comput. Sci.
Artiom Alhazov, Ion Petre, Vladimir Rogojin
2009 ed.
Trans. Computational Systems Biology
Corrado Priami, Ralph-Johan Back, Ion Petre
2008 J jnl
Fundam. Informaticae
Tseren-Onolt Ishdorj, Remco Loos, Ion Petre
2008 J jnl
Inf. Comput.
Ion Petre, Vladimir Rogojin
2008 J jnl
Int. J. Found. Comput. Sci.
Tseren-Onolt Ishdorj, Ion Petre
2008 J jnl
Discret. Appl. Math.
Tero Harju, Chang Li, Ion Petre
2008 conf
AFL
Ion Petre
2008 J jnl
Soft Comput.
Tero Harju, Chang Li, Ion Petre
2008 J jnl
Theor. Comput. Sci.
Adrian Atanasiu, Radu-Florian Atanasiu, Ion Petre
2008 J jnl
Discret. Appl. Math.
Tero Harju, Ion Petre, Vladimir Rogojin, Grzegorz Rozenberg
2008 J jnl
Theor. Comput. Sci.
Miika Langille, Ion Petre
2008 J jnl
Nat. Comput.
Artiom Alhazov, Ion Petre, Vladimir Rogojin
2008 B conf
ICGT
Ion Petre, Grzegorz Rozenberg
2008 conf
BIONETICS
Miika Langille, Ion Petre, Vladimir Rogojin
2007 B conf
FCT
Robert Brijder, Miika Langille, Ion Petre
2007 J jnl
Int. J. Found. Comput. Sci.
Tseren-Onolt Ishdorj, Ion Petre, Vladimir Rogojin
2007 C conf
UC
Tseren-Onolt Ishdorj, Ion Petre
2007 J jnl
Theor. Comput. Sci.
Erzsébet Csuhaj-Varjú, Ion Petre, György Vaszil
2007 B conf
DNA
Artiom Alhazov, Ion Petre, Vladimir Rogojin
2006 conf
KDECB
Tero Harju, Chang Li, Ion Petre, Grzegorz Rozenberg
2006 J jnl
it Inf. Technol.
Ion Petre
2006 conf
Nanotechnology: Science and Computation
Tero Harju, Ion Petre, Grzegorz Rozenberg
2006 J jnl
Nat. Comput.
Tero Harju, Chang Li, Ion Petre, Grzegorz Rozenberg
2006 J jnl
Inf. Process. Lett.
Lucian Ilie, Solomon Marcus, Ion Petre
2006 J jnl
Fundam. Informaticae
Miika Langille, Ion Petre
2005 J jnl
Theory Comput. Syst.
Juhani Karhumäki, Michel Latteux, Ion Petre
2005 J jnl
Theor. Comput. Sci.
Juhani Karhumäki, Michel Latteux, Ion Petre
2005 B conf
DNA
Tero Harju, Ion Petre, Vladimir Rogojin, Grzegorz Rozenberg
2004 conf
Aspects of Molecular Computing
Tero Harju, Ion Petre, Grzegorz Rozenberg
2004 J jnl
Bull. EATCS
Tero Harju, Ion Petre, Grzegorz Rozenberg
2004 B conf
DNA
Tero Harju, Chang Li, Ion Petre, Grzegorz Rozenberg
2004 B conf
ICGT
Tero Harju, Ion Petre, Grzegorz Rozenberg
2004 conf
Theory Is Forever
Tero Harju, Ion Petre, Grzegorz Rozenberg
2003 J jnl
Theor. Comput. Sci.
Andrzej Ehrenfeucht, Tero Harju, Ion Petre, David M. Prescott, Grzegorz Rozenberg
2003 J jnl
Bull. EATCS
Tero Harju, Ion Petre, Grzegorz Rozenberg
2003 conf
Grammars and Automata for String Processing
Ion Petre
2003 A conf
STACS
Juhani Karhumäki, Michel Latteux, Ion Petre
2002 J jnl
Theory Comput. Syst.
Andrzej Ehrenfeucht, Tero Harju, Ion Petre, Grzegorz Rozenberg
2002 J jnl
Theor. Comput. Sci.
Juhani Karhumäki, Ion Petre
2002 J jnl
Math. Struct. Comput. Sci.
Andrzej Ehrenfeucht, Ion Petre, David M. Prescott, Grzegorz Rozenberg
2002 conf
Formal and Natural Computing
Juhani Karhumäki, Ion Petre
2002 B conf
ICGT
Tero Harju, Ion Petre, Grzegorz Rozenberg
2001 conf
Words, Semigroups, and Transductions
Andrzej Ehrenfeucht, Ion Petre, David M. Prescott, Grzegorz Rozenberg
2001 J jnl
Bull. EATCS
Juhani Karhumäki, Ion Petre
2001 B conf
DNA
Andrzej Ehrenfeucht, Tero Harju, Ion Petre, Grzegorz Rozenberg
2001 conf
Where Mathematics, Computer Science, Linguistics and Biology Meet
Andrzej Ehrenfeucht, Ion Petre, David M. Prescott, Grzegorz Rozenberg
2000 A* conf
ICALP
Juhani Karhumäki, Ion Petre
2000 ch.
Finite Versus Infinite
Lucian Ilie, Ion Petre, Grzegorz Rozenberg
1999 J jnl
Bull. EATCS
Ion Petre
1999 J jnl
J. Univers. Comput. Sci.
Ion Petre, Luigia Petre
1999 C conf
Developments in Language Theory
Ion Petre
1999 J jnl
J. Autom. Lang. Comb.
Ion Petre
tests/unit/test_cfg_features.py
← Index tests/unit/test_cfg_features.py python
"""
Unit tests for cfg_features.py — all new CFG feature computations.

These tests use plain Python data structures (index-based adjacency lists)
and require no Binary Ninja dependency.
"""
import pytest

from redb.extractors.decompiler.bninja.analysis.cfg_features import (
    bfs_order,
    bfs_max_depth,
    count_back_edges,
    compute_topology_hash,
    compute_md_index_topdown,
    compute_md_index_bottomup,
    compute_prime_product,
    build_block_features,
    compute_cfg_feature_tlsh,
    compute_wl_minhash,
    pack_adjacency,
    LLIL_OP_CATEGORIES,
    CAT_ARITHMETIC,
    CAT_LOGIC,
    CAT_CALL,
    CAT_MEMORY,
    NUM_WL_MINHASH_PERMS,
)


# ===================================================================
# Helper: common graph topologies
# ===================================================================

def _linear_chain(n):
    """0 -> 1 -> 2 -> ... -> (n-1)"""
    return [[i + 1] if i < n - 1 else [] for i in range(n)]


def _diamond():
    """
    0 -> 1, 0 -> 2, 1 -> 3, 2 -> 3
    (classic if/else diamond)
    """
    return [[1, 2], [3], [3], []]


def _predecessors_from_successors(successors, n):
    preds = [[] for _ in range(n)]
    for src, targets in enumerate(successors):
        for tgt in targets:
            preds[tgt].append(src)
    return preds


# ===================================================================
# TestBfsOrder
# ===================================================================

class TestBfsOrder:
    def test_empty_graph(self):
        assert bfs_order([], 0) == []

    def test_single_node(self):
        assert bfs_order([[]], 1) == [0]

    def test_linear_chain(self):
        succs = _linear_chain(4)
        assert bfs_order(succs, 4) == [0, 1, 2, 3]

    def test_diamond(self):
        succs = _diamond()
        order = bfs_order(succs, 4)
        assert order[0] == 0
        assert order[-1] == 3
        assert set(order) == {0, 1, 2, 3}

    def test_unreachable_nodes(self):
        # 0 -> 1, node 2 is unreachable
        succs = [[1], [], []]
        order = bfs_order(succs, 3)
        assert order[:2] == [0, 1]
        assert 2 in order  # unreachable appended

    def test_all_nodes_visited(self):
        succs = _diamond()
        order = bfs_order(succs, 4)
        assert len(order) == 4


# ===================================================================
# TestBfsMaxDepth
# ===================================================================

class TestBfsMaxDepth:
    def test_empty_graph(self):
        assert bfs_max_depth([], 0) == 0

    def test_single_block(self):
        assert bfs_max_depth([[]], 1) == 0

    def test_linear_chain(self):
        succs = _linear_chain(5)
        assert bfs_max_depth(succs, 5) == 4

    def test_diamond(self):
        succs = _diamond()
        assert bfs_max_depth(succs, 4) == 2

    def test_wide_graph(self):
        # 0 -> 1, 0 -> 2, 0 -> 3 (all at depth 1)
        succs = [[1, 2, 3], [], [], []]
        assert bfs_max_depth(succs, 4) == 1


# ===================================================================
# TestCountBackEdges
# ===================================================================

class TestCountBackEdges:
    def test_empty_graph(self):
        assert count_back_edges([], 0) == 0

    def test_no_loops(self):
        succs = _linear_chain(3)
        assert count_back_edges(succs, 3) == 0

    def test_single_loop(self):
        # 0 -> 1 -> 2 -> 0 (one back edge: 2->0)
        succs = [[1], [2], [0]]
        assert count_back_edges(succs, 3) == 1

    def test_nested_loops(self):
        # 0 -> 1 -> 2 -> 1 (inner), 2 -> 3 -> 0 (outer)
        succs = [[1], [2], [1, 3], [0]]
        assert count_back_edges(succs, 4) == 2

    def test_self_loop(self):
        # 0 -> 0 (self-loop)
        succs = [[0]]
        assert count_back_edges(succs, 1) == 1

    def test_diamond_no_loops(self):
        succs = _diamond()
        assert count_back_edges(succs, 4) == 0

    def test_single_node_no_loop(self):
        succs = [[]]
        assert count_back_edges(succs, 1) == 0


# ===================================================================
# TestTopologyHash
# ===================================================================

class TestTopologyHash:
    def test_same_graph_same_hash(self):
        succs = _diamond()
        bfs = bfs_order(succs, 4)
        h1 = compute_topology_hash(succs, bfs, 4)
        h2 = compute_topology_hash(succs, bfs, 4)
        assert h1 == h2

    def test_different_graphs_different_hash(self):
        succs1 = _linear_chain(3)
        bfs1 = bfs_order(succs1, 3)
        h1 = compute_topology_hash(succs1, bfs1, 3)

        succs2 = _diamond()
        bfs2 = bfs_order(succs2, 4)
        h2 = compute_topology_hash(succs2, bfs2, 4)

        assert h1 != h2

    def test_returns_16_bytes(self):
        succs = _diamond()
        bfs = bfs_order(succs, 4)
        h = compute_topology_hash(succs, bfs, 4)
        assert isinstance(h, bytes)
        assert len(h) == 16

    def test_isomorphic_graphs_same_hash(self):
        # Graph A: 0->1, 0->2, 1->3, 2->3 (diamond with successors [1,2])
        succs_a = [[1, 2], [3], [3], []]
        # Graph B: same structure but successors listed as [2,1]
        # BFS from 0 will visit them in different order, but after remapping
        # the canonical form should be identical for isomorphic graphs
        succs_b = [[2, 1], [3], [3], []]

        bfs_a = bfs_order(succs_a, 4)
        bfs_b = bfs_order(succs_b, 4)

        h_a = compute_topology_hash(succs_a, bfs_a, 4)
        h_b = compute_topology_hash(succs_b, bfs_b, 4)
        assert h_a == h_b

    def test_empty_graph(self):
        h = compute_topology_hash([], [], 0)
        assert h == b'\x00' * 16

    def test_single_node(self):
        succs = [[]]
        bfs = bfs_order(succs, 1)
        h = compute_topology_hash(succs, bfs, 1)
        assert isinstance(h, bytes)
        assert len(h) == 16


# ===================================================================
# TestMdIndex
# ===================================================================

class TestMdIndex:
    def test_single_block_topdown(self):
        succs = [[]]
        preds = [[]]
        bfs = [0]
        result = compute_md_index_topdown(succs, preds, bfs)
        assert isinstance(result, int)
        assert result > 0

    def test_single_block_bottomup(self):
        succs = [[]]
        preds = [[]]
        result = compute_md_index_bottomup(succs, preds, 1)
        assert isinstance(result, int)
        assert result > 0

    def test_linear_chain_topdown_vs_bottomup(self):
        succs = _linear_chain(4)
        preds = _predecessors_from_successors(succs, 4)
        bfs = bfs_order(succs, 4)
        td = compute_md_index_topdown(succs, preds, bfs)
        bu = compute_md_index_bottomup(succs, preds, 4)
        # Top-down and bottom-up should be different for a linear chain
        # (entry has in_deg=0, exit has out_deg=0, so the sequences differ)
        assert td != bu

    def test_deterministic(self):
        succs = _diamond()
        preds = _predecessors_from_successors(succs, 4)
        bfs = bfs_order(succs, 4)
        td1 = compute_md_index_topdown(succs, preds, bfs)
        td2 = compute_md_index_topdown(succs, preds, bfs)
        assert td1 == td2

    def test_different_graphs_different_index(self):
        succs1 = _linear_chain(3)
        preds1 = _predecessors_from_successors(succs1, 3)
        bfs1 = bfs_order(succs1, 3)
        td1 = compute_md_index_topdown(succs1, preds1, bfs1)

        succs2 = _diamond()
        preds2 = _predecessors_from_successors(succs2, 4)
        bfs2 = bfs_order(succs2, 4)
        td2 = compute_md_index_topdown(succs2, preds2, bfs2)

        assert td1 != td2

    def test_topdown_empty(self):
        assert compute_md_index_topdown([], [], []) == 0

    def test_bottomup_empty(self):
        assert compute_md_index_bottomup([], [], 0) == 0


# ===================================================================
# TestPrimeProduct
# ===================================================================

class TestPrimeProduct:
    def test_empty(self):
        assert compute_prime_product([]) == 0

    def test_known_sequence(self):
        # Use actual LLIL enum values from conftest_binja_stubs:
        # LLIL_NOP=0 -> prime 1, LLIL_LOAD=4 -> prime 5
        from redb.extractors.decompiler.bninja.analysis.cfg_features import LLIL_OP_PRIMES
        nop_val = 0   # LLIL_NOP
        load_val = 4  # LLIL_LOAD
        expected = LLIL_OP_PRIMES.get(nop_val, 1) * LLIL_OP_PRIMES.get(load_val, 1)
        result = compute_prime_product([nop_val, load_val])
        assert result == expected

    def test_order_independence(self):
        # LLIL_LOAD=4, LLIL_STORE=5, LLIL_ADD=13
        ops_a = [4, 5, 13]
        ops_b = [13, 4, 5]
        assert compute_prime_product(ops_a) == compute_prime_product(ops_b)

    def test_unknown_ops_map_to_1(self):
        # Unknown ops get prime 1, so they don't change the product
        result_known = compute_prime_product([4])  # LLIL_LOAD -> 5
        result_with_unknown = compute_prime_product([4, 9999])  # LOAD * unknown(1)
        assert result_known == result_with_unknown

    def test_mod_2_64(self):
        # Product should be mod 2^64
        result = compute_prime_product([4] * 1000)  # LLIL_LOAD
        assert 0 <= result < 2**64

    def test_single_op(self):
        # LLIL_STORE=5 -> prime 7
        assert compute_prime_product([5]) == 7


# ===================================================================
# TestBuildBlockFeatures
# ===================================================================

class TestBuildBlockFeatures:
    def test_empty_llil(self):
        succs = [[1], []]
        features = build_block_features([[], []], succs, 2)
        assert len(features) == 2
        # All zeros except successor_count
        assert features[0] == [0, 0, 0, 0, 0, 0, 0, 1]  # 1 successor
        assert features[1] == [0, 0, 0, 0, 0, 0, 0, 0]  # 0 successors

    def test_correct_categorization(self):
        # Set up categories for testing
        import redb.extractors.decompiler.bninja.analysis.cfg_features as cf
        old_cats = cf.LLIL_OP_CATEGORIES.copy()
        cf.LLIL_OP_CATEGORIES.update({
            100: CAT_ARITHMETIC,
            101: CAT_ARITHMETIC,
            200: CAT_LOGIC,
            300: CAT_CALL,
            400: CAT_MEMORY,
        })
        try:
            block_ops = [[100, 101, 200, 300, 400]]
            succs = [[]]
            features = build_block_features(block_ops, succs, 1)
            assert features[0][0] == 5   # instr_count
            assert features[0][1] == 2   # arithmetic
            assert features[0][2] == 1   # logic
            assert features[0][4] == 1   # call
            assert features[0][6] == 1   # memory
        finally:
            cf.LLIL_OP_CATEGORIES.clear()
            cf.LLIL_OP_CATEGORIES.update(old_cats)

    def test_cap_at_65535(self):
        # More than 65535 ops in one block
        huge_ops = [0] * 70000  # NOP x 70000
        succs = [[]]
        features = build_block_features([huge_ops], succs, 1)
        assert features[0][0] == 65535  # capped

    def test_missing_block_ops(self):
        # block_llil_ops shorter than n
        succs = [[1], [2], []]
        features = build_block_features([[1, 2]], succs, 3)
        assert len(features) == 3
        # Block 1 and 2 get empty ops since block_llil_ops only has 1 entry
        assert features[1] == [0, 0, 0, 0, 0, 0, 0, 1]
        assert features[2] == [0, 0, 0, 0, 0, 0, 0, 0]


# ===================================================================
# TestCfgFeatureTlsh
# ===================================================================

class TestCfgFeatureTlsh:
    def test_too_few_blocks_returns_none(self):
        # 5 blocks = 5 * 9 bytes = 45 < 50
        bb_features = [[10, 1, 0, 2, 0, 1, 1, 2]] * 5
        bfs = list(range(5))
        result = compute_cfg_feature_tlsh(bb_features, bfs)
        assert result is None

    def test_uniform_data_returns_none(self):
        # 7 identical blocks — TLSH returns TNULL for low-entropy input
        bb_features = [[10, 1, 0, 2, 0, 1, 1, 2]] * 7
        bfs = list(range(7))
        result = compute_cfg_feature_tlsh(bb_features, bfs)
        assert result is None

    def test_varied_data_returns_string(self):
        # 20 blocks with varied features — enough entropy for TLSH
        bb_features = [
            [i * 7 + 3, (i * 13) % 50, (i * 17) % 30, (i * 23) % 40,
             (i * 11) % 20, (i * 7) % 25, (i * 19) % 35, (i * 3) % 10]
            for i in range(20)
        ]
        bfs = list(range(20))
        result = compute_cfg_feature_tlsh(bb_features, bfs)
        assert isinstance(result, str)
        assert len(result) > 0
        assert result.startswith("T1")


# ===================================================================
# TestWlMinhash
# ===================================================================

class TestWlMinhash:
    def test_empty_function(self):
        result = compute_wl_minhash([], [], [], 0)
        assert result == [255] * NUM_WL_MINHASH_PERMS

    def test_returns_128_elements(self):
        succs = _diamond()
        preds = _predecessors_from_successors(succs, 4)
        bb_feats = [[5, 1, 0, 2, 0, 1, 1, 2]] * 4
        result = compute_wl_minhash(succs, preds, bb_feats, 4)
        assert len(result) == 128

    def test_all_uint8(self):
        succs = _linear_chain(3)
        preds = _predecessors_from_successors(succs, 3)
        bb_feats = [[3, 1, 0, 1, 0, 0, 1, 1]] * 3
        result = compute_wl_minhash(succs, preds, bb_feats, 3)
        assert all(0 <= v <= 255 for v in result)

    def test_identical_graphs_same_signature(self):
        succs = _diamond()
        preds = _predecessors_from_successors(succs, 4)
        bb_feats = [[5, 1, 0, 2, 0, 1, 1, 2]] * 4
        sig1 = compute_wl_minhash(succs, preds, bb_feats, 4)
        sig2 = compute_wl_minhash(succs, preds, bb_feats, 4)
        assert sig1 == sig2

    def test_different_graphs_different_signatures(self):
        # Graph 1: linear chain
        succs1 = _linear_chain(4)
        preds1 = _predecessors_from_successors(succs1, 4)
        bb_feats1 = [[5, 1, 0, 2, 0, 1, 1, i] for i in range(4)]
        sig1 = compute_wl_minhash(succs1, preds1, bb_feats1, 4)

        # Graph 2: diamond
        succs2 = _diamond()
        preds2 = _predecessors_from_successors(succs2, 4)
        bb_feats2 = [[10, 3, 2, 1, 0, 0, 0, i] for i in range(4)]
        sig2 = compute_wl_minhash(succs2, preds2, bb_feats2, 4)

        assert sig1 != sig2

    def test_single_node(self):
        succs = [[]]
        preds = [[]]
        bb_feats = [[1, 0, 0, 0, 0, 0, 0, 0]]
        result = compute_wl_minhash(succs, preds, bb_feats, 1)
        assert len(result) == 128


# ===================================================================
# TestPackAdjacency
# ===================================================================

class TestPackAdjacency:
    def test_empty(self):
        assert pack_adjacency([]) == []

    def test_single_edge(self):
        succs = [[1], []]
        edges = pack_adjacency(succs)
        assert len(edges) == 1
        assert edges[0] == (0 << 16) | 1

    def test_correct_packing(self):
        succs = _diamond()
        edges = pack_adjacency(succs)
        assert len(edges) == 4
        # 0->1, 0->2, 1->3, 2->3
        expected = {
            (0 << 16) | 1,
            (0 << 16) | 2,
            (1 << 16) | 3,
            (2 << 16) | 3,
        }
        assert set(edges) == expected

    def test_roundtrip(self):
        """Unpack edges and verify source/target pairs."""
        succs = [[1, 2], [3], [3], []]
        edges = pack_adjacency(succs)
        unpacked = [(e >> 16, e & 0xFFFF) for e in edges]
        expected = [(0, 1), (0, 2), (1, 3), (2, 3)]
        assert sorted(unpacked) == sorted(expected)

    def test_large_index_filtered(self):
        # Create a successor list where index >= 65536
        succs = [[] for _ in range(65537)]
        succs[0] = [65536]  # target is exactly 65536 — should be filtered
        edges = pack_adjacency(succs)
        assert len(edges) == 0

    def test_max_valid_index(self):
        # Index 65535 is the maximum valid
        succs = [[] for _ in range(65536)]
        succs[0] = [65535]
        edges = pack_adjacency(succs)
        assert len(edges) == 1
        assert edges[0] == (0 << 16) | 65535