Randy E. Ellis

99 papers A* 10A 11Journal 30Unranked 46
YearRankTypeTitle / Venue / Authors
2021 J jnl
Comput. methods Biomech. Biomed. Eng. Imaging Vis.
Katy Scott, Duncan Stuart, Jacob J. Peoples, Gianluigi Bisleri, Randy E. Ellis
2021 J jnl
Int. J. Comput. Assist. Radiol. Surg.
Rachel L. Theriault, Martin Kaufmann, Kevin Yi Mi Ren, Sonal Varma, Randy E. Ellis
2020 conf
ISBI
Jacob J. Peoples, Randy E. Ellis
2020 conf
ShapeMI@MICCAI
Jacob J. Peoples, Randy E. Ellis
2019 J jnl
Int. J. Comput. Assist. Radiol. Surg.
Jacob J. Peoples, Gianluigi Bisleri, Randy E. Ellis
2017 J jnl
Int. J. Comput. Assist. Radiol. Surg.
Andrew W. L. Dickinson, Michelle L. Zec, David R. Pichora, Brian J. Rasquinha, Randy E. Ellis
2017 J jnl
Int. J. Comput. Assist. Radiol. Surg.
Brian J. Rasquinha, Michael J. Rainbow, Michelle L. Zec, David R. Pichora, Randy E. Ellis
2017 J jnl
Int. J. Comput. Assist. Radiol. Surg.
Sima Zakani, John F. Rudan, Randy E. Ellis
2016 conf
MMVR
Brian J. Rasquinha, Kate S. M. Loe, Andrew W. L. Dickinson, John F. Rudan, Randy E. Ellis
2016 J jnl
Int. J. Comput. Assist. Radiol. Surg.
Anna Belkova, David R. Pichora, Randy E. Ellis
2016 J jnl
Int. J. Comput. Assist. Radiol. Surg.
Brandon Chan, Jason Auyeung, John F. Rudan, Randy E. Ellis, Manuela Kunz
2016 conf
MMVR
Andrew W. L. Dickinson, Brian J. Rasquinha, John F. Rudan, Randy E. Ellis
2015 conf
MICCAI (2)
Mohamed S. Hefny, Toshiyuki Okada, Masatoshi Hori, Yoshinobu Sato, Randy E. Ellis
2015 J jnl
Int. J. Comput. Assist. Radiol. Surg.
Mohamed S. Hefny, John F. Rudan, Randy E. Ellis
2015 conf
MICCAI (2)
Brian J. Rasquinha, Andrew W. L. Dickinson, Gabriel Venne, David R. Pichora, Randy E. Ellis
2015 J jnl
Int. J. Comput. Assist. Radiol. Surg.
Elodie Lugez, Hossein Sadjadi, David R. Pichora, Randy E. Ellis, Selim G. Akl, Gabor Fichtinger
2015 J jnl
Int. J. Comput. Assist. Radiol. Surg.
Manuela Kunz, S. Balaketheeswaran, Randy E. Ellis, John F. Rudan
2014 conf
MMVR
Mohamed S. Hefny, John F. Rudan, Randy E. Ellis
2014 J jnl
Int. J. Comput. Assist. Radiol. Surg.
Burton Ma, Manuela Kunz, Braden Gammon, Randy E. Ellis, David R. Pichora
2014 J jnl
Int. J. Comput. Assist. Radiol. Surg.
Erin Janine Smith, Gregory Allan, Braden Gammon, Richard W. Sellens, Randy E. Ellis, David R. Pichora
2014 conf
MMVR
Andrew W. L. Dickinson, Craig B. Casier, Richard W. Sellens, David R. Pichora, Randy E. Ellis
2013 conf
ISBI
Mohamed S. Hefny, Randy E. Ellis
2013 conf
MMVR
Mohamed S. Hefny, Andrew W. L. Dickinson, Andrew E. Giles, Gavin C. A. Wood, Randy E. Ellis
2012 J jnl
Int. J. Comput. Assist. Radiol. Surg.
Sima Zakani, Gabriel Venne, Erin Janine Smith, R. Bicknell, Randy E. Ellis
2012 J jnl
Int. J. Comput. Assist. Radiol. Surg.
Brian Rasquinha, Junaid Sayani, John F. Rudan, Gavin C. A. Wood, Randy E. Ellis
2012 J jnl
Int. J. Comput. Assist. Radiol. Surg.
Erin Janine Smith, Hisham Al-Sanawi, Braden Gammon, Paul St. John, David R. Pichora, Randy E. Ellis
2011 conf
MMVR
Amy M. VanBerlo, Aaron R. Campbell, Randy E. Ellis
2011 conf
MMVR
Joseph B. Anstey, Erin Janine Smith, Brian Rasquinha, John F. Rudan, Randy E. Ellis
2011 conf
MMVR
Manuela Kunz, John F. Rudan, Gavin C. A. Wood, Randy E. Ellis
2011 conf
Image-Guided Procedures
Amber L. Simpson, Burton Ma, Randy E. Ellis, A. James Stewart, Michael I. Miga
2011 conf
Image-Guided Procedures
Amy M. VanBerlo, Aaron R. Campbell, Randy E. Ellis
2010 conf
MICCAI (3)
Erin Janine Smith, Anton Oentoro, Hisham Al-Sanawi, Braden Gammon, Paul St. John, David R. Pichora, Randy E. Ellis
2010 J jnl
IEEE Trans. Medical Imaging
Burton Ma, Mehdi Hedjazi Moghari, Randy E. Ellis, Purang Abolmaesumi
2010 conf
Image-Guided Procedures
Anton Oentoro, Randy E. Ellis
2010 conf
Image-Guided Procedures
Elvis C. S. Chen, Sharyle A. Fowler, Lawrence C. Hookey, Randy E. Ellis
2010 conf
ISBI
Mohamed S. Hefny, Randy E. Ellis
2008 conf
Image-Guided Procedures
Thomas Kuiran Chen, Adrian D. Thurston, Mehdi Hedjazi Moghari, Randy E. Ellis, Purang Abolmaesumi
2008 conf
MICCAI (2)
Mohamed S. Hefny, Purang Abolmaesumi, Zahra Karimaghaloo, David G. Gobbi, Randy E. Ellis, Gabor Fichtinger
2008 J jnl
Medical Image Anal.
Maarten Beek, Purang Abolmaesumi, Suriya Luenam, Randy E. Ellis, Richard W. Sellens, David R. Pichora
2007 conf
MICCAI (2)
Burton Ma, Mehdi Hedjazi Moghari, Randy E. Ellis, Purang Abolmaesumi
2007 conf
MICCAI (2)
Burton Ma, Amber L. Simpson, Randy E. Ellis
2006 conf
MICCAI (1)
Elvis C. S. Chen, Randy E. Ellis
2006 conf
MICCAI (2)
Burton Ma, Randy E. Ellis
2006 conf
MICCAI (1)
Thomas Kuiran Chen, Purang Abolmaesumi, Adrian D. Thurston, Randy E. Ellis
2006 J jnl
IEEE Trans. Vis. Comput. Graph.
Marta Kersten, James Stewart, Nikolaus F. Troje, Randy E. Ellis
2006 conf
MICCAI (1)
Raúl San José Estépar, Nicholas Stylopoulos, Randy E. Ellis, Eigil Samset, Carl-Fredrik Westin, Christopher C. Thompson, Kirby G. Vosburgh
2006 conf
MICCAI (2)
Amber L. Simpson, Burton Ma, Elvis C. S. Chen, Randy E. Ellis, A. James Stewart
2006 conf
EMBC
Michael A. Greenspan, Liping Ingrid Wang, Randy E. Ellis
2006 J jnl
IEEE Trans. Medical Imaging
Liping Ingrid Wang, Michael A. Greenspan, Randy E. Ellis
2005 conf
MICCAI (2)
Thomas S. Y. Tang, Randy E. Ellis
2005 J jnl
IEEE Trans. Medical Imaging
Weiguang Yao, Purang Abolmaesumi, Michael A. Greenspan, Randy E. Ellis
2005 A conf
MICCAI
Amber L. Simpson, Burton Ma, Dan P. Borschneck, Randy E. Ellis
2005 A conf
MICCAI
Joel L. Lanovaz, Randy E. Ellis
2005 A conf
MICCAI
Erin Janine Smith, J. Tim Bryant, Randy E. Ellis
2005 A conf
MICCAI
Elvis C. S. Chen, Joel L. Lanovaz, Randy E. Ellis
2005 A conf
MICCAI
Burton Ma, Randy E. Ellis
2004 conf
MICCAI (2)
Hsin-Yun Yao, Vincent Hayward, Randy E. Ellis
2004 J jnl
Medical Image Anal.
Thomas S. Y. Tang, N. J. MacIntyre, H. S. Gill, R. A. Fellows, N. A. Hill, D. R. Wilson, Randy E. Ellis
2004 J jnl
Medical Image Anal.
Randy E. Ellis, Terry M. Peters
2004 conf
MICCAI (2)
Thomas S. Y. Tang, N. J. MacIntyre, H. S. Gill, R. A. Fellows, N. A. Hill, D. R. Wilson, Randy E. Ellis
2004 conf
MICCAI (1)
Burton Ma, Randy E. Ellis
2004 conf
MICCAI (1)
Burton Ma, Randy E. Ellis
2004 conf
MICCAI (2)
Maarten Beek, Carolyn F. Small, Steve Csongvay, Richard W. Sellens, Randy E. Ellis, David R. Pichora
2003 conf
MICCAI (1)
Burton Ma, Randy E. Ellis
2003 conf
MICCAI (1)
Thomas S. Y. Tang, N. J. MacIntyre, H. S. Gill, R. A. Fellows, N. A. Hill, D. R. Wilson, Randy E. Ellis
2003 conf
MICCAI (2)
D. J. Mayman, John F. Rudan, David R. Pichora, D. Watson, Randy E. Ellis
2003 ed.
MICCAI (1)
Randy E. Ellis, Terry M. Peters
2003 ed.
MICCAI (2)
Randy E. Ellis, Terry M. Peters
2003 J jnl
Medical Image Anal.
Burton Ma, Randy E. Ellis
2003 conf
MICCAI (2)
G. S. Athwal, S. Leclaire, Randy E. Ellis, David R. Pichora
2002 conf
MICCAI (1)
Burton Ma, John F. Rudan, Randy E. Ellis
2002 conf
MICCAI (1)
O. Iyun, Dan P. Borschneck, Randy E. Ellis
2001 J jnl
Medical Image Anal.
E. Chen, Randy E. Ellis, J. Tim Bryant, John F. Rudan
2001 A conf
MICCAI
Randy E. Ellis, D. Kerr, John F. Rudan, L. Davidson
2000 A conf
MICCAI
E. Chen, Randy E. Ellis, J. Tim Bryant
2000 A* conf
ICRA
P. L. McAllister, Randy E. Ellis
2000 A conf
MICCAI
Thomas S. Y. Tang, Randy E. Ellis, Gabor Fichtinger
2000 A conf
MICCAI
H. Croitoru, Randy E. Ellis, Carolyn F. Small, David R. Pichora
1999 A conf
MICCAI
Burton Ma, Randy E. Ellis, David J. Fleet
1998 A conf
MICCAI
C. Y. Tso, Randy E. Ellis, John F. Rudan, M. M. Harrison
1998 J jnl
IEEE Trans. Robotics Autom.
Nilanjan Sarkar, Xiaoping Yun, Randy E. Ellis
1997 conf
CVRMed
Randy E. Ellis, David J. Fleet, J. Tim Bryant, John F. Rudan, P. Fenton
1997 conf
ISER
Randy E. Ellis, P. Zion, C. Y. Tso
1997 conf
CIRA
Nilanjan Sarkar, Xiaoping Yun, Randy E. Ellis
1996 J jnl
Robotica
Randy E. Ellis, O. M. Ismaeil, M. G. Lipsett
1994 J jnl
Robotica
Randy E. Ellis, S. R. Ganeshan, Susan J. Lederman
1994 A* conf
ICRA
O. M. Ismaeil, Randy E. Ellis
1994 A* conf
ICRA
Randy E. Ellis, M. Qin
1994 J jnl
IEEE Trans. Syst. Man Cybern. Syst.
Randy E. Ellis, S. Laurie Ricker
1993 conf
ICRA (1)
S. Laurie Ricker, Randy E. Ellis
1993 conf
ISER
S. R. Ganeshan, Randy E. Ellis, Susan J. Lederman
1992 A* conf
ICRA
Randy E. Ellis, O. M. Ismaeil, I. H. Carmichael
1992 A* conf
ICRA
Randy E. Ellis, S. Laurie Ricker
1991 A* conf
ICRA
D. T. Pawluk, Randy E. Ellis
1991 J jnl
IEEE Trans. Robotics Autom.
Randy E. Ellis
1989 A* conf
ICRA
Randy E. Ellis
1987 A* conf
ICRA
Randy E. Ellis
1986 A* conf
ICRA
Randy E. Ellis
1986 A* conf
AAAI
Randy E. Ellis, Edward M. Riseman, Allen R. Hanson
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