Vassilis Kekatos

109 papers B 1C 5Misc 11Journal 68Unranked 23
YearRankTypeTitle / Venue / Authors
2025 J jnl
CoRR
Zain ul Abdeen, Vassilis Kekatos, Ming Jin
2025 J jnl
CoRR
Ashutossh Gupta, Vassilis Kekatos, Dionysios Aliprantis, Steve Pekarek
2025 J jnl
CoRR
Ashutossh Gupta, Vassilis Kekatos, Ruoyu Yang, Dionysios Aliprantis, Steve Pekarek
2025 C conf
ACC
Ashutossh Gupta, Manish K. Singh, Vassilis Kekatos
2025 J jnl
CoRR
Sean Reiter, Mark Embree, Serkan Gugercin, Vassilis Kekatos
2025 conf
SmartGridComm
Thinh Viet Le, Md. Obaidur Rahman, Vassilis Kekatos
2025 J jnl
CoRR
Thinh Viet Le, Md. Obaidur Rahman, Vassilis Kekatos
2025 J jnl
CoRR
Vassilis Kekatos, Ridley Annin, Manish K. Singh, Junjie Qin
2025 J jnl
CoRR
Thinh Viet Le, Mark M. Wilde, Vassilis Kekatos
2024 J jnl
IEEE Trans. Smart Grid
Deepjyoti Deka, Vassilis Kekatos, Guido Cavraro
2024 J jnl
IEEE Trans. Smart Grid
Ilgiz Murzakhanov, Sarthak Gupta, Spyros Chatzivasileiadis, Vassilis Kekatos
2024 J jnl
IEEE Trans. Smart Grid
Sarthak Gupta, Vassilis Kekatos, Spyros Chatzivasileiadis
2023 J jnl
CoRR
Jinlei Wei, Sarthak Gupta, Dionysios C. Aliprantis, Vassilis Kekatos
2023 Misc conf
ICASSP
Sarthak Gupta, Vassilis Kekatos
2023 J jnl
CoRR
Shaohui Liu, Hao Zhu, Vassilis Kekatos
2023 J jnl
IEEE Control. Syst. Lett.
Sarthak Gupta, Ali Mehrizi-Sani, Spyros Chatzivasileiadis, Vassilis Kekatos
2023 conf
HICSS
Shaohui Liu, Hao Zhu, Vassilis Kekatos
2023 J jnl
IEEE Trans. Smart Grid
Mana Jalali, Manish Kumar Singh, Vassilis Kekatos, Georgios B. Giannakis, Chen-Ching Liu
2023 J jnl
CoRR
Thinh Viet Le, Vassilis Kekatos
2022 J jnl
IEEE Trans. Smart Grid
Sarthak Gupta, Vassilis Kekatos, Ming Jin
2022 J jnl
IEEE Trans. Smart Grid
Sina Taheri, Vassilis Kekatos, Sriharsha Veeramachaneni, Baosen Zhang
2022 J jnl
CoRR
Sina Taheri, Vassilis Kekatos, Harsha Veeramachaneni, Baosen Zhang
2022 J jnl
CoRR
Shaohui Liu, Hao Zhu, Vassilis Kekatos
2022 J jnl
IEEE Control. Syst. Lett.
Siddharth Bhela, Harsha Nagarajan, Deepjyoti Deka, Vassilis Kekatos
2022 J jnl
CoRR
Deepjyoti Deka, Vassilis Kekatos, Guido Cavraro
2022 C conf
ACC
Zain ul Abdeen, He Yin, Vassilis Kekatos, Ming Jin
2022 J jnl
CoRR
Zain ul Abdeen, He Yin, Vassilis Kekatos, Ming Jin
2022 J jnl
CoRR
Ilgiz Murzakhanov, Sarthak Gupta, Spyros Chatzivasileiadis, Vassilis Kekatos
2022 J jnl
CoRR
Manish Kumar Singh, Vassilis Kekatos
2021 J jnl
CoRR
Shaohui Liu, Hao Zhu, Vassilis Kekatos
2021 J jnl
CoRR
Sarthak Gupta, Vassilis Kekatos, Ming Jin
2021 J jnl
IEEE Trans. Smart Grid
Sina Taheri, Mana Jalali, Vassilis Kekatos, Lang Tong
2021 conf
CDC
Mana Jalali, Vassilis Kekatos, Siddharth Bhela, Hao Zhu
2021 J jnl
CoRR
Mana Jalali, Vassilis Kekatos, Siddharth Bhela, Hao Zhu, Virgilio Centeno
2021 Misc conf
CISS
Manish Kumar Singh, Sarthak Gupta, Vassilis Kekatos
2021 J jnl
IEEE Trans. Control. Netw. Syst.
Manish Kumar Singh, Vassilis Kekatos
2021 C conf
ACC
Manish Kumar Singh, Guido Cavraro, Andrey Bernstein, Vassilis Kekatos
2021 J jnl
CoRR
Manish Kumar Singh, Guido Cavraro, Andrey Bernstein, Vassilis Kekatos
2020 conf
SmartGridComm
Sarthak Gupta, Vassilis Kekatos, Ming Jin
2020 J jnl
IEEE Trans. Smart Grid
Mana Jalali, Vassilis Kekatos, Nikolaos Gatsis, Deepjyoti Deka
2020 J jnl
IEEE Trans. Smart Grid
Hamed Mohsenian Rad, Mario Paolone, Vassilis Kekatos, Omid Ardakanian, Yan Xu, Di Shi, Reza Arghandeh
2020 conf
SmartGridComm
Manish Kumar Singh, Sarthak Gupta, Vassilis Kekatos, Guido Cavraro, Andrey Bernstein
2020 J jnl
IEEE Trans. Control. Netw. Syst.
Manish Kumar Singh, Vassilis Kekatos
2020 J jnl
IEEE Trans. Control. Netw. Syst.
Manish Kumar Singh, Vassilis Kekatos
2020 J jnl
IEEE Trans. Smart Grid
Sina Taheri, Vassilis Kekatos
2020 J jnl
IEEE Control. Syst. Lett.
Guido Cavraro, Andrey Bernstein, Vassilis Kekatos, Yingchen Zhang
2020 J jnl
CoRR
Sina Taheri, Vassilis Kekatos, Harsha Veeramachaneni
2019 C conf
ACC
Siddharth Bhela, Deepjyoti Deka, Harsha Nagarajan, Vassilis Kekatos
2019 J jnl
CoRR
Manish Kumar Singh, Vassilis Kekatos, Sina Taheri, Kevin P. Schneider, Chen-Ching Liu
2019 J jnl
IEEE Trans. Control. Netw. Syst.
Guido Cavraro, Vassilis Kekatos
2019 C conf
ACC
Manish Kumar Singh, Vassilis Kekatos
2019 J jnl
IEEE Trans. Smart Grid
Sarthak Gupta, Vassilis Kekatos, Walid Saad
2019 J jnl
IEEE Trans. Smart Grid
Guido Cavraro, Vassilis Kekatos, Sriharsha Veeramachaneni
2018 J jnl
IEEE Trans. Smart Grid
Siddharth Bhela, Vassilis Kekatos, Sriharsha Veeramachaneni
2018 J jnl
IEEE Control. Syst. Lett.
Guido Cavraro, Vassilis Kekatos
2018 conf
GlobalSIP
Aditie Garg, Mana Jalali, Vassilis Kekatos, Nikolaos Gatsis
2018 J jnl
CoRR
Aditie Garg, Mana Jalali, Vassilis Kekatos, Nikolaos Gatsis
2018 J jnl
IEEE Trans. Smart Grid
Luis M. Lopez-Ramos, Vassilis Kekatos, Antonio G. Marques, Georgios B. Giannakis
2017 Misc conf
ICASSP
Siddharth Bhela, Vassilis Kekatos, Liang Zhang, Sriharsha Veeramachaneni
2017 J jnl
CoRR
Vassilis Kekatos, Gang Wang, Hao Zhu, Georgios B. Giannakis
2017 conf
GlobalSIP
Siddharth Bhela, Vassilis Kekatos, Sriharsha Veeramachaneni
2017 conf
CAMSAP
Siddharth Bhela, Vassilis Kekatos, Sriharsha Veeramachaneni
2016 conf
CDC
Liang Zhang, Vassilis Kekatos, Georgios B. Giannakis
2016 J jnl
CoRR
Siddharth Bhela, Vassilis Kekatos, Sriharsha Veeramachaneni
2016 J jnl
IEEE Trans. Signal Process.
Dimitris Berberidis, Vassilis Kekatos, Georgios B. Giannakis
2016 J jnl
IEEE Trans. Smart Grid
Vassilis Kekatos, Georgios B. Giannakis, Ross Baldick
2016 conf
GlobalSIP
Sarthak Gupta, Vassilis Kekatos
2016 Misc conf
ICASSP
Gang Wang, Vassilis Kekatos, Georgios B. Giannakis
2016 J jnl
CoRR
Luis M. Lopez-Ramos, Vassilis Kekatos, Antonio G. Marqués, Georgios B. Giannakis
2015 Misc conf
ICASSP
Dimitris K. Berberidis, Vassilis Kekatos, Gang Wang, Georgios B. Giannakis
2015 J jnl
CoRR
Gang Wang, Vassilis Kekatos, Antonio J. Conejo, Georgios B. Giannakis
2015 conf
SmartGridComm
Vassilis Kekatos, Liang Zhang, Georgios B. Giannakis, Ross Baldick
2015 conf
GlobalSIP
Luis M. Lopez-Ramos, Vassilis Kekatos, Antonio G. Marqués, Georgios B. Giannakis
2015 J jnl
CoRR
Vassilis Kekatos, Liang Zhang, Georgios B. Giannakis, Ross Baldick
2014 J jnl
IEEE J. Sel. Top. Signal Process.
Vassilis Kekatos, Yu Zhang, Georgios B. Giannakis
2014 Misc conf
ACSSC
Vassilis Kekatos
2014 Misc conf
ICASSP
Vassilis Kekatos, Yu Zhang, Georgios B. Giannakis
2014 J jnl
CoRR
Vassilis Kekatos, Georgios B. Giannakis, Ross Baldick
2014 Misc conf
ACSSC
Dimitris Berberidis, Gang Wang, Georgios B. Giannakis, Vassilis Kekatos
2014 conf
GlobalSIP
Gang Wang, Dimitris Berberidis, Vassilis Kekatos, Georgios B. Giannakis
2014 J jnl
CoRR
Vassilis Kekatos, Gang Wang, Antonio J. Conejo, Georgios B. Giannakis
2013 conf
CAMSAP
Vassilis Kekatos, Evangelos Vlachos, Dimitris Ampeliotis, Georgios B. Giannakis, Kostas Berberidis
2013 conf
ISGT
Vassilis Kekatos, Sriharsha Veeramachaneni, Marc Light, Georgios B. Giannakis
2013 J jnl
CoRR
Vassilis Kekatos, Yu Zhang, Georgios B. Giannakis
2013 conf
ISWCS
Konstantinos Slavakis, Yannis Kopsinis, Sergios Theodoridis, Georgios B. Giannakis, Vassilis Kekatos
2013 J jnl
CoRR
Vassilis Kekatos, Georgios B. Giannakis, Ross Baldick
2013 Misc conf
ACSSC
Vassilis Kekatos, Yu Zhang, Georgios B. Giannakis
2013 J jnl
IEEE Signal Process. Mag.
Georgios B. Giannakis, Vassilis Kekatos, Nikolaos Gatsis, Seung-Jun Kim, Hao Zhu, Bruce F. Wollenberg
2013 conf
DSP
Yu Zhang, Nikolaos Gatsis, Vassilis Kekatos, Georgios B. Giannakis
2012 B conf
GLOBECOM
Vassilis Kekatos, Georgios B. Giannakis
2012 J jnl
IEEE Trans. Signal Process.
Pedro A. Forero, Vassilis Kekatos, Georgios B. Giannakis
2011 conf
CAMSAP
Vassilis Kekatos, Georgios B. Giannakis
2011 conf
EUSIPCO
Vassilis Kekatos, Georgios B. Giannakis
2011 J jnl
IEEE Trans. Signal Process.
Vassilis Kekatos, Georgios B. Giannakis
2011 Misc conf
ICASSP
Pedro A. Forero, Vassilis Kekatos, Georgios B. Giannakis
2011 J jnl
CoRR
Pedro A. Forero, Vassilis Kekatos, Georgios B. Giannakis
2011 J jnl
CoRR
Vassilis Kekatos, Georgios B. Giannakis
2011 J jnl
IEEE Trans. Signal Process.
Vassilis Kekatos, Georgios B. Giannakis
2011 Misc conf
ICASSP
Georgios B. Giannakis, Gonzalo Mateos, Shahrokh Farahmand, Vassilis Kekatos, Hao Zhu
2010 J jnl
CoRR
Vassilis Kekatos, Georgios B. Giannakis
2009 J jnl
IEEE Trans. Signal Process.
Aris S. Lalos, Vassilis Kekatos, Kostas Berberidis
2007 conf
ICASSP (3)
Vassilis Kekatos, Kostas Berberidis, Athanasios A. Rontogiannis
2007 conf
VTC Fall
Vassilis Kekatos, Aris S. Lalos, Kostas Berberidis
2007 J jnl
EURASIP J. Adv. Signal Process.
Vassilis Kekatos, Athanasios A. Rontogiannis, Kostas Berberidis
2007
Vassilis Kekatos
2006 J jnl
EURASIP J. Wirel. Commun. Netw.
Vassilis Kekatos, Athanasios A. Rontogiannis, Kostas Berberidis
2006 conf
ICC
Athanasios A. Rontogiannis, Vassilis Kekatos, Kostas Berberidis
2006 J jnl
IEEE Signal Process. Lett.
Athanasios A. Rontogiannis, Vassilis Kekatos, Kostas Berberidis
2006 conf
SPPRA
Anastasis Kounoudes, Anixi Antonakoudi, Vassilis Kekatos, Philippos Peleties
redb/extractors/decompiler/bninja/analysis/cfg_features.py
← Index redb/extractors/decompiler/bninja/analysis/cfg_features.py python
import struct
from collections import deque
from typing import Optional

import blake3
import mmh3


# ---------------------------------------------------------------------------
# Task 1.1: Core Graph Utilities
# ---------------------------------------------------------------------------

def bfs_order(successors: list[list[int]], n: int) -> list[int]:
    """
    BFS traversal from node 0 (entry block), returns node indices in visit order.
    Unreachable nodes appended at the end.
    """
    if n == 0:
        return []

    visited = set()
    order = []
    queue = deque([0])
    visited.add(0)

    while queue:
        idx = queue.popleft()
        order.append(idx)
        for target in successors[idx]:
            if target not in visited:
                visited.add(target)
                queue.append(target)

    # Append unreachable blocks (dead code)
    for i in range(n):
        if i not in visited:
            order.append(i)

    return order


def bfs_max_depth(successors: list[list[int]], n: int) -> int:
    """
    Maximum BFS depth from entry block (node 0).
    Replaces the per-block depth column with a single scalar.
    """
    if n == 0:
        return 0

    depth = {0: 0}
    max_d = 0
    queue = deque([0])

    while queue:
        node = queue.popleft()
        for s in successors[node]:
            if s not in depth:
                depth[s] = depth[node] + 1
                if depth[s] > max_d:
                    max_d = depth[s]
                queue.append(s)

    return max_d


# ---------------------------------------------------------------------------
# Task 1.2: Back-Edge Detection (Iterative DFS)
# ---------------------------------------------------------------------------

def count_back_edges(successors: list[list[int]], n: int) -> int:
    """
    Count natural loops via iterative DFS back-edge detection.
    A back edge is an edge to a GRAY (in-stack) node.

    Iterative to avoid stack overflow on functions with 1000+ blocks
    (common in obfuscated malware, VM dispatchers, unrolled loops).
    """
    if n == 0:
        return 0

    WHITE, GRAY, BLACK = 0, 1, 2
    color = [WHITE] * n
    back_edges = 0

    stack = [(0, iter(successors[0]))]
    color[0] = GRAY

    while stack:
        u, children = stack[-1]
        try:
            v = next(children)
            if color[v] == GRAY:
                back_edges += 1
            elif color[v] == WHITE:
                color[v] = GRAY
                stack.append((v, iter(successors[v])))
        except StopIteration:
            color[u] = BLACK
            stack.pop()

    return back_edges


# ---------------------------------------------------------------------------
# Task 1.3: Topology Hash
# ---------------------------------------------------------------------------

def compute_topology_hash(
    successors: list[list[int]],
    bfs: list[int],
    n: int,
) -> bytes:
    """
    BLAKE3 hash of BFS-ordered canonical adjacency.
    Pure graph shape — ignores all block content.
    Two functions with identical control flow structure produce identical hashes.

    Returns 16 bytes (128-bit).
    """
    if n == 0:
        return b'\x00' * 16

    # Remap: original index -> BFS position
    remap = {original: position for position, original in enumerate(bfs)}

    canonical = bytearray()
    for position in range(n):
        original_idx = bfs[position]
        remapped_succs = sorted(
            remap[s] for s in successors[original_idx] if s in remap
        )
        # Pack: node_index (2 bytes) + num_successors (1 byte) + successor indices (2 bytes each)
        canonical.extend(struct.pack('<HB', position, len(remapped_succs)))
        for s in remapped_succs:
            canonical.extend(struct.pack('<H', s))

    return blake3.blake3(bytes(canonical)).digest(length=16)


# ---------------------------------------------------------------------------
# Task 1.4: MD-Index (Top-Down and Bottom-Up)
# ---------------------------------------------------------------------------

def compute_md_index_topdown(
    successors: list[list[int]],
    predecessors: list[list[int]],
    bfs: list[int],
) -> int:
    """
    BinDiff-style top-down MD-index.
    Hash of (in_degree, out_degree) sequence in BFS order from entry.
    Returns UInt64.
    """
    if not bfs:
        return 0

    degree_bytes = bytearray()
    for idx in bfs:
        in_deg = min(len(predecessors[idx]), 255)
        out_deg = min(len(successors[idx]), 255)
        degree_bytes.extend(struct.pack('<BB', in_deg, out_deg))

    h = blake3.blake3(bytes(degree_bytes)).digest(length=8)
    return struct.unpack('<Q', h)[0]


def compute_md_index_bottomup(
    successors: list[list[int]],
    predecessors: list[list[int]],
    n: int,
) -> int:
    """
    Bottom-up MD-index: BFS from exit blocks (no successors),
    traversing edges in reverse.
    Returns UInt64.
    """
    if n == 0:
        return 0

    exits = [i for i in range(n) if len(successors[i]) == 0]
    if not exits:
        exits = [n - 1]  # Fallback: use last block

    visited = set(exits)
    order = []
    queue = deque(exits)

    while queue:
        idx = queue.popleft()
        order.append(idx)
        for pred in predecessors[idx]:
            if pred not in visited:
                visited.add(pred)
                queue.append(pred)

    # Append unreachable blocks
    for i in range(n):
        if i not in visited:
            order.append(i)

    degree_bytes = bytearray()
    for idx in order:
        in_deg = min(len(predecessors[idx]), 255)
        out_deg = min(len(successors[idx]), 255)
        degree_bytes.extend(struct.pack('<BB', in_deg, out_deg))

    h = blake3.blake3(bytes(degree_bytes)).digest(length=8)
    return struct.unpack('<Q', h)[0]


# ---------------------------------------------------------------------------
# Task 1.5: Prime Product
# ---------------------------------------------------------------------------

# Small primes assigned to LLIL opcode categories.
# Keys are the integer values of binaryninja.LowLevelILOperation enum members.
# We use integer keys so this module doesn't import binaryninja.
#
# Mapping rationale: same operation class -> same prime.
# Using LLIL (not native asm) makes this architecture-independent.
#
# Populated at import time by cfg.py using the real LowLevelILOperation enum values.
# Unknown ops map to prime 1 (identity element) in compute_prime_product().
LLIL_OP_PRIMES: dict[int, int] = {}


def compute_prime_product(llil_operations: list[int]) -> int:
    """
    Product of small primes assigned to each LLIL opcode.
    Position-independent: block reordering doesn't change the result.
    Mod 2^64 for fixed-size storage.

    Args:
        llil_operations: flat list of LLIL operation enum integer values
                         for all instructions in the function.
    Returns:
        UInt64 prime product, or 0 if no instructions.
    """
    if not llil_operations:
        return 0

    product = 1
    for op in llil_operations:
        prime = LLIL_OP_PRIMES.get(op, 1)
        product = (product * prime) % (2**64)

    return product


# ---------------------------------------------------------------------------
# Task 1.6: ACFG Block Features
# ---------------------------------------------------------------------------

# Instruction category indices for ACFG feature vectors
CAT_ARITHMETIC = 0
CAT_LOGIC = 1
CAT_TRANSFER = 2
CAT_CALL = 3
CAT_COMPARISON = 4
CAT_MEMORY = 5
CAT_OTHER = 6

# Maps LLIL operation integer values to category indices.
# Populated at import time by cfg.py using the real LowLevelILOperation enum.
LLIL_OP_CATEGORIES: dict[int, int] = {}


def build_block_features(
    block_llil_ops: list[list[int]],
    successors: list[list[int]],
    n: int,
) -> list[list[int]]:
    """
    Extract Gemini-style ACFG features per block.

    Args:
        block_llil_ops: per-block list of LLIL operation integer values.
                        block_llil_ops[i] is the list of ops for block i.
                        Empty list if LLIL unavailable for that block.
        successors: index-based adjacency list.
        n: number of blocks.

    Returns:
        List of [instr_count, arithmetic, logic, transfer, call, comparison,
                 memory, successor_count] per block. All values capped at 65535.
    """
    features = []
    for i in range(n):
        cats = [0, 0, 0, 0, 0, 0, 0]
        ops = block_llil_ops[i] if i < len(block_llil_ops) else []
        for op in ops:
            cat = LLIL_OP_CATEGORIES.get(op, CAT_OTHER)
            cats[cat] += 1

        instr_count = len(ops)
        features.append([
            min(instr_count, 65535),
            min(cats[CAT_ARITHMETIC], 65535),
            min(cats[CAT_LOGIC], 65535),
            min(cats[CAT_TRANSFER], 65535),
            min(cats[CAT_CALL], 65535),
            min(cats[CAT_COMPARISON], 65535),
            min(cats[CAT_MEMORY], 65535),
            min(len(successors[i]), 65535),
        ])

    return features


# ---------------------------------------------------------------------------
# Task 1.7: CFG Feature TLSH
# ---------------------------------------------------------------------------

def compute_cfg_feature_tlsh(
    bb_features: list[list[int]],
    bfs: list[int],
) -> Optional[str]:
    """
    TLSH hash of BFS-ordered per-block feature vectors.
    Captures both structure (BFS ordering) and instruction distribution.

    Returns TLSH hex string or None if too few bytes for TLSH (< 50).
    """
    import tlsh as _tlsh

    feature_bytes = bytearray()
    for idx in bfs:
        feats = bb_features[idx]
        feature_bytes.extend(struct.pack(
            '<HBBBBBBB',
            min(feats[0], 65535),
            min(feats[1], 255),
            min(feats[2], 255),
            min(feats[3], 255),
            min(feats[4], 255),
            min(feats[5], 255),
            min(feats[6], 255),
            min(feats[7], 255),
        ))

    if len(feature_bytes) < 50:
        return None

    try:
        h = _tlsh.hash(bytes(feature_bytes))
        return h if h and h != 'TNULL' else None
    except Exception:
        return None


# ---------------------------------------------------------------------------
# Task 1.8: WL-MinHash
# ---------------------------------------------------------------------------

# Pre-computed seeds for MinHash permutations.
NUM_WL_MINHASH_PERMS = 128
_WL_MINHASH_SEEDS = list(range(NUM_WL_MINHASH_PERMS))  # Seeds 0..127


def compute_wl_minhash(
    successors: list[list[int]],
    predecessors: list[list[int]],
    bb_features: list[list[int]],
    n: int,
    iterations: int = 3,
) -> list[int]:
    """
    Weisfeiler-Leman MinHash for fuzzy topology similarity.

    Initial labels: mmh3 hash of per-block ACFG feature tuple (content-aware).
    WL refinement: incorporate sorted neighbor labels at each iteration.
    MinHash: 128-permutation signature over shingle set.

    Returns list of 128 uint8 values, or [255]*128 sentinel for empty functions.
    """
    if n == 0:
        return [255] * NUM_WL_MINHASH_PERMS

    # Initial labels: hash of instruction category tuple per block
    labels = []
    for i in range(n):
        feats = bb_features[i] if i < len(bb_features) else [0] * 8
        # mmh3 with seed=0 for initial labels
        label = mmh3.hash(str(tuple(feats)), 0) & 0xFFFFFFFF
        labels.append(label)

    # Collect shingles: (iteration, label) pairs as strings for mmh3
    shingles: set[str] = set()

    # Iteration 0: individual block labels
    for label in labels:
        shingles.add(f"0:{label}")

    # WL iterations: refine labels by neighborhood aggregation
    for iteration in range(1, iterations + 1):
        new_labels = []
        for i in range(n):
            succ_labels = tuple(sorted(labels[s] for s in successors[i]))
            pred_labels = tuple(sorted(labels[p] for p in predecessors[i]))
            composite = f"{labels[i]}|{succ_labels}|{pred_labels}"
            new_label = mmh3.hash(composite, 0) & 0xFFFFFFFF
            new_labels.append(new_label)
            shingles.add(f"{iteration}:{new_label}")
        labels = new_labels

    if not shingles:
        return [255] * NUM_WL_MINHASH_PERMS

    # Compute MinHash signature using mmh3 with different seeds
    shingle_list = list(shingles)
    signature = []
    for seed in _WL_MINHASH_SEEDS:
        min_val = 0xFFFFFFFF
        for s in shingle_list:
            h = mmh3.hash(s, seed) & 0xFFFFFFFF
            if h < min_val:
                min_val = h
        # Compress to uint8 for storage
        signature.append(min_val & 0xFF)

    return signature


# ---------------------------------------------------------------------------
# Task 1.9: Packed Adjacency
# ---------------------------------------------------------------------------

def pack_adjacency(successors: list[list[int]]) -> list[int]:
    """
    Pack CFG edges as Array(UInt32).
    Each UInt32 = (source_index << 16) | target_index.
    Supports up to 65,535 blocks per function.
    """
    edges = []
    for src, targets in enumerate(successors):
        for tgt in targets:
            if src < 65536 and tgt < 65536:
                edges.append((src << 16) | tgt)
    return edges