Naoki Matsumoto

97 papers A* 2A 1B 3C 1Journal 72Unranked 18
YearRankTypeTitle / Venue / Authors
2026 J jnl
RAIRO Theor. Informatics Appl.
Naoki Matsumoto
2026 J jnl
Discret. Math.
Naoki Matsumoto
2025 J jnl
CoRR
Kengo Enami, Naoki Matsumoto, Takamasa Yashima
2025 J jnl
CoRR
Naoki Matsumoto, Takamasa Yashima
2025 J jnl
IEICE Trans. Commun.
Naoki Matsumoto, Daisuke Kotani, Yasuo Okabe
2025 J jnl
IEEE Trans. Big Data
Tsuyoshi Yamashita, Naoki Matsumoto, Kunitake Kaneko
2025 J jnl
Knowl. Inf. Syst.
Shuhei Nakano, Tsuyoshi Yamashita, Andrew Shin, Naoki Matsumoto, Kunitake Kaneko
2025 B conf
COMPSAC
Naoki Matsumoto, Tomoya Kawanishi, Kenji Ohira, Masahiro Kozuka
2024 J jnl
IEEE Access
Osamu Fukuda, Wen Liang Yeoh, Kyohei Yoshida, Naoki Matsumoto, Nobuhiko Yamaguchi, Hiroshi Okumura
2024 J jnl
Discuss. Math. Graph Theory
Naoki Matsumoto, Kenta Noguchi, Takamasa Yashima
2024 J jnl
Discret. Appl. Math.
Akihiro Higashitani, Kazuki Kurimoto, Naoki Matsumoto
2024 J jnl
Discuss. Math. Graph Theory
Masayoshi Doki, Yoshimi Egawa, Naoki Matsumoto
2024 conf
ICOIN
Rintaro Nishiyama, Andrew Shin, Naoki Matsumoto, Kunitake Kaneko
2024 B conf
RV
Masaki Waga, Kotaro Matsuoka, Takashi Suwa, Naoki Matsumoto, Ryotaro Banno, Song Bian, Kohei Suenaga
2024 J jnl
CoRR
Masaki Waga, Kotaro Matsuoka, Takashi Suwa, Naoki Matsumoto, Ryotaro Banno, Song Bian, Kohei Suenaga
2024 conf
AINTEC
Naoki Matsumoto, Akihiro Suda
2024 J jnl
CoRR
Naoki Matsumoto, Akihiro Suda
2023 J jnl
Int. J. Comput. Assist. Radiol. Surg.
Jiayi Zhou, Norihiro Koizumi, Yu Nishiyama, Kiminao Kogiso, Tomohiro Ishikawa, Kento Kobayashi, Yusuke Watanabe, Takumi Fujibayashi, Miyu Yamada, Momoko Matsuyama, Hiroyuki Tsukihara, Ryosuke Tsumura, Kiyoshi Yoshinaka, Naoki Matsumoto, Masahiro Ogawa, Hideyo Miyazaki, Kazushi Numata, Hidetoshi Nagaoka, Toshiyuki Iwai, Hideyuki Iijima
2023 J jnl
RAIRO Oper. Res.
Naoki Matsumoto
2023 J jnl
J. Inf. Process.
Naoki Matsumoto, Daisuke Kotani, Yasuo Okabe
2023 J jnl
IEEE Access
Kohei Tsuchida, Naoki Matsumoto, Andrew Shin, Kunitake Kaneko
2023 B conf
IC2E
Naoki Matsumoto, Daisuke Kotani, Yasuo Okabe
2023 J jnl
Discret. Math.
Hikoe Enomoto, Jun Fujisawa, Naoki Matsumoto
2023 conf
SICE
Wen Liang Yeoh, Kyohei Yoshida, Naoki Matsumoto, Nobuhiko Yamaguchi, Hiroshi Okumura, Osamu Fukuda
2023 J jnl
Australas. J Comb.
Akihiro Higashitani, Naoki Matsumoto
2023 conf
ICOIN
Kohei Tsuchida, Naoki Matsumoto, Kunitake Kaneko
2023 J jnl
Discret. Appl. Math.
Michitaka Furuya, Naoki Matsumoto, Yumiko Ohno, Kenta Ozeki
2023 J jnl
Int. J. Comput. Assist. Radiol. Surg.
Takumi Fujibayashi, Norihiro Koizumi, Yu Nishiyama, Yusuke Watanabe, Jiayi Zhou, Momoko Matsuyama, Miyu Yamada, Ryosuke Tsumura, Kiyoshi Yoshinaka, Naoki Matsumoto, Hiroyuki Tsukihara, Kazushi Numata
2023 J jnl
Discret. Math.
Akihiro Higashitani, Naoki Matsumoto
2022 J jnl
Int. J. Comput. Assist. Radiol. Surg.
Momoko Matsuyama, Norihiro Koizumi, Akihide Otsuka, Kento Kobayashi, Shiho Yagasaki, Yusuke Watanabe, Jiayi Zhou, Yu Nishiyama, Naoki Matsumoto, Hiroyuki Tsukihara, Kazushi Numata
2022 conf
GCCE
Miyu Yamada, Ryosuke Tsumura, Norihiro Koizumi, Kiyoshi Yoshinaka, Yu Nishiyama, Naoki Matsumoto
2022 J jnl
Graphs Comb.
Yoshihiro Asayama, Naoki Matsumoto
2022 conf
PerCom Workshops
Naoki Matsumoto, Daisuke Kotani, Yasuo Okabe
2022 J jnl
CoRR
Naoki Matsumoto, Yuuki Takai
2022 conf
GCCE
Jiayi Zhou, Norihiro Koizumi, Yu Nishiyama, Ryosuke Tsumura, Hiroyuki Tsukihara, Naoki Matsumoto
2022 J jnl
Discret. Appl. Math.
Naoki Matsumoto, Ryusei Moriyama, Katsuhiro Ota
2022 J jnl
CoRR
Akihiro Higashitani, Naoki Matsumoto
2022 conf
CAV (1)
Ryotaro Banno, Kotaro Matsuoka, Naoki Matsumoto, Song Bian, Masaki Waga, Kohei Suenaga
2022 J jnl
CoRR
Ryotaro Banno, Kotaro Matsuoka, Naoki Matsumoto, Song Bian, Masaki Waga, Kohei Suenaga
2022 J jnl
Discret. Math. Algorithms Appl.
Naoki Matsumoto
2021 J jnl
Discret. Appl. Math.
Naoki Matsumoto, Yumiko Ohno
2021 J jnl
Int. J. Comput. Assist. Radiol. Surg.
Ryosuke Saito, Norihiro Koizumi, Yu Nishiyama, Tsubasa Imaizumi, Kenta Kusahara, Shiho Yagasaki, Naoki Matsumoto, Ryota Masuzaki, Toshimi Takahashi, Masahiro Ogawa
2021 J jnl
Discret. Appl. Math.
Naoki Matsumoto, Tomoki Nakamigawa
2021 J jnl
Discuss. Math. Graph Theory
Naoki Matsumoto, Yusuke Suzuki
2021 J jnl
Graphs Comb.
Fumiya Hidaka, Naoki Matsumoto, Atsuhiro Nakamoto
2021 J jnl
Australas. J Comb.
Naoki Matsumoto, Masaki Yamamoto, Masahito Yamazaki
2021 J jnl
CoRR
Akihiro Higashitani, Naoki Matsumoto
2021 A* conf
USENIX Security Symposium
Kotaro Matsuoka, Ryotaro Banno, Naoki Matsumoto, Takashi Sato, Song Bian
2020 J jnl
IGTR
Naoki Matsumoto
2020 J jnl
Graphs Comb.
Naoki Matsumoto, Tomoki Nakamigawa, Tadashi Sakuma
2020 J jnl
Discret. Appl. Math.
Naoki Matsumoto, Atsuhiro Nakamoto, Seiya Negami
2020 J jnl
AKCE Int. J. Graphs Comb.
Naoki Matsumoto
2020 J jnl
Int. J. Comput. Assist. Radiol. Surg.
Shiho Yagasaki, Norihiro Koizumi, Yu Nishiyama, Ryosuke Kondo, Tsubasa Imaizumi, Naoki Matsumoto, Masahiro Ogawa, Kazushi Numata
2020 J jnl
Discret. Math.
Naoki Matsumoto, Yumiko Ohno
2020 J jnl
CoRR
Akihiro Higashitani, Kazuki Kurimoto, Naoki Matsumoto
2020 J jnl
CoRR
Naoki Matsumoto, Atsuki Nagao
2020 J jnl
Discret. Math.
Naoki Matsumoto, Tomoki Nakamigawa
2020 J jnl
Graphs Comb.
Yoshihiro Asayama, Ryo Matsukawa, Naoki Matsumoto, Atsuhiro Nakamoto
2020 J jnl
CoRR
Shuhei Saito, Wei Wu, Naoki Matsumoto
2020 J jnl
CoRR
Kotaro Matsuoka, Ryotaro Banno, Naoki Matsumoto, Takashi Sato, Song Bian
2019 J jnl
Inf. Process. Lett.
Michitaka Furuya, Naoki Matsumoto
2019 conf
CBS
Tsubasa Imaizumi, Ryosuke Kondo, Kenta Kusahara, Yu Nishiyama, Hiroyuki Tsukihara, Naoki Matsumoto, Kazushi Numata, Norihiro Koizumi
2019 conf
CBS
Kento Kobayashi, Yudai Sasaki, Fumio Eura, Ryosuke Kondo, Kyohei Tomita, Takahiro Kobayashi, Yusuke Watanabe, Akihide Otsuka, Hiroyuki Tsukihara, Naoki Matsumoto, Kazushi Numata, Hidetoshi Nagaoka, Toshiyuki Iwai, Hideyuki Iijima, Yu Nishiyama, Norihiro Koizumi
2019 J jnl
Inf. Process. Lett.
Naoki Matsumoto
2019 J jnl
Australas. J Comb.
Naoki Matsumoto
2019 J jnl
Discret. Appl. Math.
Michitaka Furuya, Naoki Matsumoto
2018 conf
UR
Tsubasa Imaizumi, Norihiro Koizumi, Ryosuke Kondo, Yu Nishiyama, Naoki Matsumoto
2018 J jnl
Graphs Comb.
Yoshihiro Asayama, Naoki Matsumoto, Atsuhiro Nakamoto, Shota Ogano
2018 J jnl
Discret. Math.
Naoki Matsumoto, Atsuhiro Nakamoto, Tsubasa Yamaguchi
2018 J jnl
Electron. Notes Discret. Math.
Atsuhiro Nakamoto, Gen Kawatani, Naoki Matsumoto, Jorge Urrutia
2018 J jnl
J. Graph Theory
Michal Kotrbcík, Naoki Matsumoto, Bojan Mohar, Atsuhiro Nakamoto, Kenta Noguchi, Kenta Ozeki, Andrej Vodopivec
2018 J jnl
Graphs Comb.
Mojtaba Abdolmaleki, Joan P. Hutchinson, S. Gh. Ilchi, Ebadollah S. Mahmoodian, Naoki Matsumoto, Mohammad Amin Shabani
2018 conf
UR
Ryosuke Kondo, Norihiro Koizumi, Yu Nishiyama, Naoki Matsumoto, Kazushi Numata
2018 J jnl
Discret. Math.
Yoshimi Egawa, Hikoe Enomoto, Naoki Matsumoto
2017 J jnl
Contributions Discret. Math.
Naoki Matsumoto
2017 J jnl
Ars Comb.
Jinko Kanno, Naoki Matsumoto, Jianning Su, Ko Yamamoto
2017 conf
ISIE
Hirotaka Nakayama, Ryo Tanaka, Yoshihisa Ishida, Naoki Matsumoto
2017 J jnl
Graphs Comb.
Fiachra Knox, Naoki Matsumoto, Sebastián González Hermosillo de la Maza, Bojan Mohar, Cláudia Linhares Sales
2017 J jnl
Electron. J. Comb.
Michitaka Furuya, Naoki Matsumoto
2016 J jnl
Discret. Appl. Math.
Michiko Kasai, Naoki Matsumoto, Atsuhiro Nakamoto
2016 J jnl
Discret. Appl. Math.
Naoki Matsumoto, Atsuhiro Nakamoto, Shin-ichi Yonekura
2015 J jnl
J. Graph Theory
Michitaka Furuya, Naoki Matsumoto
2015 J jnl
Discret. Math.
Naoki Matsumoto, Atsuhiro Nakamoto
2015 J jnl
Graphs Comb.
Sheng Bau, Naoki Matsumoto, Atsuhiro Nakamoto, Lijuan Zheng
2015 J jnl
Graphs Comb.
Yuki Kawasaki, Naoki Matsumoto, Atsuhiro Nakamoto
2015 J jnl
Graphs Comb.
Naoki Matsumoto
2014 J jnl
Ars Comb.
Naoki Matsumoto, Kenta Noguchi
2013 J jnl
Electron. J. Comb.
Naoki Matsumoto
2012 C conf
ICCE
Arisa Tanaka, Naoki Matsumoto, Takahiko Mendori
2012 conf
TJJCCGG
Naoki Matsumoto, Atsuhiro Nakamoto
2011 J jnl
Electron. Notes Discret. Math.
Naoki Matsumoto, Atsuhiro Nakamoto
2005 conf
ISCAS (4)
Kuniyasu Shimizu, Tetsuro Endo, Naoki Matsumoto
2004 conf
ICPR (1)
Naoki Matsumoto, Seiichi Uchida, Hiroaki Sakoe
2002 conf
SIGGRAPH Abstracts and Applications
Laroussi Bouguila, Makoto Sato, Shoichi Hasegawa, Naoki Hashimoto, Naoki Matsumoto, Atsushi Toyama, Jelel Ezzine, Dalel Maghrebi
2001 conf
ISCAS (1)
Naoki Matsumoto
1995 A* conf
ICRA
Manabu Otsuka, Naoki Matsumoto, Takaharu Idogaki, Kazuhiro Kosuge, Tomotaka Itoh
1993 A conf
IROS
Naoki Matsumoto, A. Toyoda, S. Ito
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