Raja Chatila

138 papers A* 18A 16B 5C 7Journal 51Unranked 33
YearRankTypeTitle / Venue / Authors
2025 J jnl
ACM Comput. Surv.
Adrien Bennetot, Ivan Donadello, Ayoub El Qadi, Mauro Dragoni, Thomas Frossard, Benedikt Wagner, Anna Saranti, Silvia Tulli, Maria Trocan, Raja Chatila, Andreas Holzinger, Artur d'Avila Garcez, Natalia Díaz Rodríguez
2025 J jnl
IEEE Robotics Autom. Mag.
Dejanira Araiza-Illan, Kevin Baum, Helen Beebee, Raja Chatila, Sarah Moth-Lund Christensen, Simon Coghlan, Emily C. Collins, S. Kate Conroy, Alcino Cunha, Anna Dobrosovestnova, Hein Duijf, Vanessa Evers, Michael Fisher, Nico Hochgeschwender, Nadin Kökciyan, Séverin Lemaignan, Francisco Javier Rodríguez-Lera, Sara Ljungblad, Martin Magnusson, Masoumeh Mansouri, Michael Milford, AJung Moon, Thomas M. Powers, Pericle Salvini, Teresa Scantamburlo, Nick Schuster, Marija Slavkovik, Ufuk Topcu, Daniel F. Preciado Vanegas, Andrzej Wasowski, Yi Yang
2025 J jnl
CoRR
Geoffroy Morlat, Marceau Nahon, Augustin Chartouny, Raja Chatila, Ismael T. Freire, Mehdi Khamassi
2025 J jnl
CoRR
Nathaniël de Leeuw, Marceau Nahon, Mathis Reymond, Raja Chatila, Mehdi Khamassi
2024 J jnl
CoRR
Kathinka Evers, Michele Farisco, Raja Chatila, B. D. Earp, Ismael T. Freire, F. Hamker, E. Nemeth, Paul F. M. J. Verschure, Mehdi Khamassi
2024 J jnl
CoRR
Mehdi Khamassi, Marceau Nahon, Raja Chatila
2023 J jnl
CoRR
Katherine Evans, Nelson de Moura, Raja Chatila, Stéphane Chauvier
2023 J jnl
Inf. Process. Manag.
Natalia Díaz Rodríguez, Ruta Binkyte, Wafae Bakkali, Sannidhi Bookseller, Paola Tubaro, Andrius Bacevicius, Sami Zhioua, Raja Chatila
2023 J jnl
Ethics Inf. Technol.
Alistair Knott, Dino Pedreschi, Raja Chatila, Tapabrata Chakraborti, Susan Leavy, Ricardo Baeza-Yates, David M. Eyers, Andrew Trotman, Paul D. Teal, Przemyslaw Biecek, Stuart Russell, Yoshua Bengio
2023 J jnl
Int. J. Soc. Robotics
Rémi Dromnelle, Erwan Renaudo, Mohamed Chetouani, Petros Maragos, Raja Chatila, Benoît Girard, Mehdi Khamassi
2022 J jnl
Frontiers Neurorobotics
Erwan Renaudo, Philipp Zech, Raja Chatila, Mehdi Khamassi
2022 J jnl
Knowl. Based Syst.
Adrien Bennetot, Gianni Franchi, Javier Del Ser, Raja Chatila, Natalia Díaz Rodríguez
2022 J jnl
CoRR
Adrien Bennetot, Gianni Franchi, Javier Del Ser, Raja Chatila, Natalia Díaz Rodríguez
2021 J jnl
CoRR
Adrien Bennetot, Ivan Donadello, Ayoub El Qadi, Mauro Dragoni, Thomas Frossard, Benedikt Wagner, Anna Saranti, Silvia Tulli, Maria Trocan, Raja Chatila, Andreas Holzinger, Artur S. d'Avila Garcez, Natalia Díaz Rodríguez
2021 ch.
Reflections on Artificial Intelligence for Humanity
Raja Chatila, Virginia Dignum, Michael Fisher, Fosca Giannotti, Katharina Morik, Stuart Russell, Karen Yeung
2021 ch.
Reflections on Artificial Intelligence for Humanity
Vanessa Nurock, Raja Chatila, Marie-Hélène Parizeau
2020 B conf
RO-MAN
Rémi Dromnelle, Benoît Girard, Erwan Renaudo, Raja Chatila, Mehdi Khamassi
2020 J jnl
CoRR
Rémi Dromnelle, Benoît Girard, Erwan Renaudo, Raja Chatila, Mehdi Khamassi
2020 J jnl
Sci. Eng. Ethics
Katherine Evans, Nelson de Moura, Stephane Chauvier, Raja Chatila, Ebru Dogan
2020 C conf
IV
Nelson de Moura, Raja Chatila, Katherine Evans, Stéphane Chauvier, Ebru Dogan
2020 J jnl
Inf. Fusion
Alejandro Barredo Arrieta, Natalia Díaz Rodríguez, Javier Del Ser, Adrien Bennetot, Siham Tabik, Alberto Barbado, Salvador García, Sergio Gil-Lopez, Daniel Molina, Richard Benjamins, Raja Chatila, Francisco Herrera
2020 conf
Living Machines
Rémi Dromnelle, Erwan Renaudo, Guillaume Pourcel, Raja Chatila, Benoît Girard, Mehdi Khamassi
2020 J jnl
CoRR
Rémi Dromnelle, Erwan Renaudo, Guillaume Pourcel, Raja Chatila, Benoît Girard, Mehdi Khamassi
2019 J jnl
CoRR
Alejandro Barredo Arrieta, Natalia Díaz Rodríguez, Javier Del Ser, Adrien Bennetot, Siham Tabik, Alberto Barbado, Salvador García, Sergio Gil-Lopez, Daniel Molina, Richard Benjamins, Raja Chatila, Francisco Herrera
2019 J jnl
CoRR
Adrien Bennetot, Jean-Luc Laurent, Raja Chatila, Natalia Díaz Rodríguez
2019 conf
NeSy@IJCAI
Adrien Bennetot, Jean-Luc Laurent, Raja Chatila, Natalia Díaz Rodríguez
2019 J jnl
IEEE Robotics Autom. Mag.
Ludovic Righetti, Raj Madhavan, Raja Chatila
2018 J jnl
Minds Mach.
Luciano Floridi, Josh Cowls, Monica Beltrametti, Raja Chatila, Patrice Chazerand, Virginia Dignum, Christoph Luetge, Robert Madelin, Ugo Pagallo, Francesca Rossi, Burkhard Schafer, Peggy Valcke, Effy Vayena
2018 J jnl
Frontiers Neurorobotics
Mihai Andries, Ricardo Omar Chávez García, Raja Chatila, Alessandro Giusti, Luca Maria Gambardella
2018 C conf
AIES
Virginia Dignum, Matteo Baldoni, Cristina Baroglio, Maurizio Caon, Raja Chatila, Louise A. Dennis, Gonzalo Génova, Galit Haim, Malte S. Kließ, Maite López-Sánchez, Roberto Micalizio, Juan Pavón, Marija Slavkovik, Matthijs H. J. Smakman, Marlies van Steenbergen, Stefano Tedeschi, Leon van der Torre, Serena Villata, Tristan de Wildt
2018 conf
Robophilosophy/TRANSOR
Raja Chatila
2018 J jnl
IEEE Robotics Autom. Mag.
Ludovic Righetti, Q.-C. Pham, Raj Madhavan, Raja Chatila
2018 J jnl
IEEE Robotics Autom. Mag.
Q.-C. Pham, Raj Madhavan, Ludovic Righetti, W. Smart, Raja Chatila
2018 J jnl
Frontiers Robotics AI
Raja Chatila, Erwan Renaudo, Mihai Andries, Ricardo Omar Chávez García, Pierre Luce-Vayrac, Raphaël Gottstein, Rachid Alami, Aurélie Clodic, Sandra Devin, Benoît Girard, Mehdi Khamassi
2017 J jnl
IEEE Robotics Autom. Mag.
Alexei Grinbaum, Raja Chatila, Laurence Devillers, Jean-Gabriel Ganascia, Catherine Tessier, Max Dauchet
2017 J jnl
IEEE Robotics Autom. Mag.
Raja Chatila, Kay Firth-Butterfield, John C. Havens, Konstantinos Karachalios
2016 ch.
Springer Handbook of Robotics, 2nd Ed.
Michael Beetz, Raja Chatila, Joachim Hertzberg, Federico Pecora
2016 A conf
IROS
Ricardo Omar Chávez García, Pierre Luce-Vayrac, Raja Chatila
2016 conf
ISER
R. Omar Chavez-Garcia, Mihai Andries, Pierre Luce-Vayrac, Raja Chatila
2016 conf
EDIA@ECAI
Ebru Dogan, Raja Chatila, Stephane Chauvier, Katherine Evans, Petria Hadjixenophontos, Jerome Perrin
2016 C conf
IECON
Ren C. Luo, Wen-Chien Hung, Raja Chatila
2016 B conf
RO-MAN
Omar Adair Islas Ramírez, Giovanna Varni, Mihai Andries, Mohamed Chetouani, Raja Chatila
2016 B conf
RO-MAN
Omar A. Islas Ramírez, Harmish Khambhaita, Raja Chatila, Mohamed Chetouani, Rachid Alami
2015 J jnl
IEEE Robotics Autom. Mag.
Raja Chatila, Antonio Bicchi
2015 J jnl
IEEE Robotics Autom. Mag.
Raja Chatila
2015 conf
BICA
Erwan Renaudo, Benoît Girard, Raja Chatila, Mehdi Khamassi
2015 conf
FSR
Rudolph Triebel, Kai Oliver Arras, Rachid Alami, Lucas Beyer, Stefan Breuers, Raja Chatila, Mohamed Chetouani, Daniel Cremers, Vanessa Evers, Michelangelo Fiore, Hayley Hung, Omar A. Islas Ramírez, Michiel Joosse, Harmish Khambhaita, Tomasz Kucner, Bastian Leibe, Achim J. Lilienthal, Timm Linder, Manja Lohse, Martin Magnusson, Billy Okal, Luigi Palmieri, Umer Rafi, Marieke van Rooij, Lu Zhang
2015 J jnl
IEEE Robotics Autom. Mag.
Raja Chatila
2015 conf
ICDL-EPIROB
Erwan Renaudo, Benoît Girard, Raja Chatila, Mehdi Khamassi
2015 J jnl
IEEE Robotics Autom. Mag.
Raja Chatila
2015 J jnl
IEEE Robotics Autom. Mag.
Raja Chatila
2014 J jnl
IEEE Robotics Autom. Mag.
Raja Chatila
2014 J jnl
IEEE Robotics Autom. Mag.
Raja Chatila
2014 conf
Living Machines
Erwan Renaudo, Benoît Girard, Raja Chatila, Mehdi Khamassi
2014 conf
Robophilosophy
Aurélie Clodic, Rachid Alami, Raja Chatila
2014 J jnl
IEEE Robotics Autom. Mag.
Raja Chatila
2014 conf
AIM
Ren C. Luo, Meng-Chu Ko, Yi-Ting Chung, Raja Chatila
2014 J jnl
IEEE Robotics Autom. Mag.
Raja Chatila
2013 J jnl
Int. J. Syst. Sci.
Abdelkrim Souici, Michel Courdesses, Ouldali Ouldali, Raja Chatila
2013 J jnl
IEEE Robotics Autom. Mag.
Raja Chatila, Bernard Roth, Oussama Khatib
2012 J jnl
Int. J. Robotics Res.
Raja Chatila, Henrik I. Christensen, Oussama Khatib
2009 A conf
IROS
Julien Guitton, Jean-Loup Farges, Raja Chatila
2008 ch.
Springer Handbook of Robotics
Joachim Hertzberg, Raja Chatila
2008 ch.
Robotics and Cognitive Approaches to Spatial Mapping
Raja Chatila
2008 conf
ISER
Aurélie Clodic, Hung Cao, Samir Alili, Vincent Montreuil, Rachid Alami, Raja Chatila
2008 conf
ICINCO-RA (1)
Ayman Zureiki, Michel Devy, Raja Chatila
2008 A* conf
HRI
Raja Chatila
2007 ed.
DARS
Rachid Alami, Raja Chatila, Hajime Asama
2007 conf
ICIP (1)
Ayman Zureiki, Michel Devy, Raja Chatila
2006 conf
ISER
Abedallatif Baba, Raja Chatila
2006 A conf
IROS
Jason Held, Alexandre Lampe, Raja Chatila
2006 A* conf
ICRA
Alexandre Lampe, Raja Chatila
2006 B conf
RO-MAN
Aurélie Clodic, Sara Fleury, Rachid Alami, Raja Chatila, Gérard Bailly, Ludovic Brethes, Maxime Cottret, Patrick Danès, Xavier Dollat, Frédéric Elisei, Isabelle Ferrané, Matthieu Herrb, Guillaume Infantes, Christian Lemaire, Frédéric Lerasle, Jérôme Manhes, Patrick Marcoul, Paulo Menezes, Vincent Montreuil
2006 C conf
IAS
Abedallatif Baba, Raja Chatila
2006 conf
AAAI Spring Symposium: To Boldly Go Where No Human-Robot Team Has Gone Before
Rachid Alami, Aurélie Clodic, Vincent Montreuil, Emrah Akin Sisbot, Raja Chatila
2005 B conf
RO-MAN
Aurélie Clodic, Vincent Montreuil, Rachid Alami, Raja Chatila
2005 conf
Humanoids
Paulo Menezes, Frédéric Lerasle, Jorge Dias, Raja Chatila
2005 A* conf
IJCAI
Nicolas Do Huu, Williams Paquier, Raja Chatila
2005 J jnl
Int. J. Robotics Res.
Raja Chatila, Paolo Dario, Oussama Khatib
2005 J jnl
IEEE Robotics Autom. Mag.
Aníbal Ollero, Simon Lacroix, Luis Merino, Jeremi Gancet, Johan Wiklund, Volker Remuss, Iker Veiga Perez, Luis G. Gutiérrez, Domingos Xavier Viegas, Miguel Angel González Benitez, Anthony Mallet, Rachid Alami, Raja Chatila, Günter Hommel, Francisco Javier Colmenero Lechuga, Begoña C. Arrue, Joaquin Ferruz, José Ramiro Martinez de Dios, Fernando Caballero
2005 C ed.
ISRR
Paolo Dario, Raja Chatila
2005 conf
sOc-EUSAI
Rachid Alami, Aurélie Clodic, Vincent Montreuil, Emrah Akin Sisbot, Raja Chatila
2004 A conf
IROS
Delphine Dufourd, Raja Chatila, Dominique Luzeaux
2004 conf
IFIP Congress Topical Sessions
Raja Chatila, Georges Giralt
2004 conf
IFIP Congress Topical Sessions
Raja Chatila
2003 A* conf
ICRA
Williams Paquier, Raja Chatila
2003 C conf
ISRR
Raja Chatila
2002 A conf
IROS
Nicolas Vandapel, Raja Chatila
2002 J jnl
Int. J. Robotics Res.
Simon Lacroix, Anthony Mallet, David Bonnafous, Gérard Bauzil, Sara Fleury, Matthieu Herrb, Raja Chatila
2002 J jnl
Robotics Auton. Syst.
Nicolas Vandapel, Raja Chatila
2002 A conf
DSN
Raja Chatila, Jean-Claude Laprie
2001 conf
ETFA (2)
Félix Ingrand, Raja Chatila, Rachid Alami
2000 A* conf
ICRA
Rachid Alami, Raja Chatila, Sara Fleury, Matthieu Herrb, François Felix Ingrand, Maher Khatib, Benoit Morisset, Philippe Moutarlier, Thierry Siméon
2000 conf
ISER
Simon Lacroix, Anthony Mallet, David Bonnafous, Gérard Bauzil, Sara Fleury, Matthieu Herrb, Raja Chatila
1999 conf
ISER
Nicolas Vandapel, Stewart J. Moorehead, William Whittaker, Raja Chatila, Rafael Murrieta-Cid
1998 J jnl
Int. J. Robotics Res.
Rachid Alami, Raja Chatila, Sara Fleury, Malik Ghallab, Félix Ingrand
1998 A conf
IROS
Guillermo Andrade, Faïz Ben Amar, Philippe Bidaud, Raja Chatila
1998 A* conf
ICRA
H. Haddad, Maher Khatib, Simon Lacroix, Raja Chatila
1997 A* conf
ICRA
Maher Khatib, H. Jaouni, Raja Chatila, Jean-Paul Laumond
1997 A conf
IROS
Sara Fleury, Matthieu Herrb, Raja Chatila
1997 conf
ISER
Maher Khatib, H. Jaouni, Raja Chatila, Jean-Paul Laumond
1997 A* conf
ICRA
Maher Khatib, Bertrand Bouilly, Thierry Siméon, Raja Chatila
1996 conf
AMS
Raja Chatila
1996 A* conf
ICRA
Patrick Hébert, Stéphane Betgé-Brezetz, Raja Chatila
1996 A* conf
ICRA
François Felix Ingrand, Raja Chatila, Rachid Alami, Frédéric Robert
1996 A conf
IROS
Adelardo A. D. de Medeiros, Raja Chatila, Sara Fleury
1996 A* conf
ICRA
Stéphane Betgé-Brezetz, Patrick Hébert, Raja Chatila, Michel Devy
1995 J jnl
Robotics Auton. Syst.
Raja Chatila
1995 conf
ISER
Simon Lacroix, Raja Chatila
1995 A* conf
ICRA
Stéphane Betgé-Brezetz, Raja Chatila, Michel Devy
1995 J jnl
Robotics Auton. Syst.
Michel Devy, Raja Chatila, Philippe Fillatreau, Simon Lacroix, Fawzi Nashashibi
1995 J jnl
IEEE Trans. Robotics Autom.
Fabrice R. Noreils, Raja Chatila
1995 J jnl
Auton. Robots
Raja Chatila, Simon Lacroix, Thierry Siméon, Matthieu Herrb
1995 J jnl
IEEE Trans. Robotics Autom.
Sara Fleury, Philippe Souères, Jean-Paul Laumond, Raja Chatila
1995 conf
Reasoning with Uncertainty in Robotics
Patrick Hébert, Stéphane Betgé-Brezetz, Raja Chatila
1994 A* conf
ICRA
Simon Lacroix, Raja Chatila, Sara Fleury, Matthieu Herrb, Thierry Siméon
1994 A* conf
ICRA
Sara Fleury, Matthieu Herrb, Raja Chatila
1994 A conf
IROS
Jérôme Perret, Christophe Proust, Rachid Alami, Raja Chatila
1994 A* conf
ICRA
Stéphane Betgé-Brezetz, Raja Chatila, Michel Devy
1993 conf
ISER
Raja Chatila, Sara Fleury, Matthieu Herrb, Simon Lacroix, Christophe Proust
1993 ed.
ISER
Raja Chatila, Gerd Hirzinger
1993 conf
ICRA (1)
Sara Fleury, Philippe Souères, Jean-Paul Laumond, Raja Chatila
1993 A conf
IROS
Georges Giralt, Raja Chatila, Rachid Alami
1992 A* conf
ICRA
Raja Chatila, Rachid Alami, Bernard Degallaix, Hervé Laruelle
1992 A conf
IROS
Georges Giralt, Rachid Alami, Raja Chatila
1991 conf
ISER
Raja Chatila, Rachid Alami, Bernard Degallaix, Victor Pérébaskine, Paul Gaborit, Philippe Moutarlier
1991 conf
Geometric Reasoning for Perception and Action
Philippe Moutarlier, Raja Chatila
1991 A conf
IROS
Philippe Moutarlier, Raja Chatila
1990 ch.
Autonomous Robot Vehicles
Georges Giralt, Raja Chatila, Marc Vaisset
1990 A conf
IROS
Raja Chatila, Rogério Ferraz de Camargo
1989 A conf
IROS
Fabrice R. Noreils, Raja Chatila
1989 conf
ISER
Philippe Moutarlier, Raja Chatila
1989 C conf
IAS
Georges Giralt, Rachid Alami, Raja Chatila
1988 J jnl
Artif. Intell. Eng.
Ralph P. Sobek, Raja Chatila
1988 conf
Geometry and Robotics
Jean-Paul Laumond, Thierry Siméon, Raja Chatila, Georges Giralt
1985 A* conf
ICRA
Raja Chatila, Jean-Paul Laumond
1982 A conf
ECAI
Raja Chatila
1979 A* conf
IJCAI
Georges Giralt, Ralph P. Sobek, Raja Chatila
tests/unit/test_decompile_analysis.py
← Index tests/unit/test_decompile_analysis.py python
"""Unit tests (mocked Binary Ninja) for analysis modules:
- bninja/analysis/cfg.py — CFGAnalysis
- bninja/analysis/disassembly.py — DisassemblyAnalysis
- bninja/analysis/low_level.py — LowLevelAnalysis
"""
import sys
import pytest
from unittest.mock import MagicMock

from tests.unit.conftest_binja_stubs import (
    install_binja_stubs,
    BranchType,
    InstructionTextTokenType,
    MockBasicBlock,
    MockEdge,
    MockFunction,
    MockToken,
    MockDisassemblyLine,
    MockBinaryView,
    MockSymbol,
    SymbolType,
    LowLevelILOperation,
)

install_binja_stubs()

from redb.extractors.decompiler.bninja.analysis.cfg import CFGAnalysis
from redb.extractors.decompiler.bninja.analysis.disassembly import DisassemblyAnalysis
from redb.extractors.decompiler.bninja.arch.x86 import Arch_x86


# ============================================================================
# 9a. CFGAnalysis
# ============================================================================


class TestCFGCyclomaticComplexity:
    def test_cyclomatic_complexity_linear(self):
        """Single block, no edges: E - N + 2 = 0 - 1 + 2 = 1."""
        block = MockBasicBlock(start=0x1000, end=0x1010, outgoing_edges=[])
        func = MockFunction(start=0x1000, basic_blocks=[block])
        cfg = CFGAnalysis(func)
        result = cfg.extract_function_cfg()
        assert result["cyclomatic_complexity"] == 1

    def test_cyclomatic_complexity_branch(self):
        """Diamond: 4 blocks, 4 edges -> 4 - 4 + 2 = 2."""
        entry = MockBasicBlock(start=0x1000, end=0x1010)
        true_b = MockBasicBlock(start=0x1010, end=0x1020)
        false_b = MockBasicBlock(start=0x1020, end=0x1030)
        merge = MockBasicBlock(start=0x1030, end=0x1040)

        entry.outgoing_edges = [MockEdge(target=true_b), MockEdge(target=false_b)]
        true_b.outgoing_edges = [MockEdge(target=merge)]
        false_b.outgoing_edges = [MockEdge(target=merge)]
        merge.outgoing_edges = []

        func = MockFunction(start=0x1000, basic_blocks=[entry, true_b, false_b, merge])
        cfg = CFGAnalysis(func)
        result = cfg.extract_function_cfg()
        assert result["cyclomatic_complexity"] == 2

    def test_cyclomatic_complexity_loop(self):
        """Loop: 3 blocks, 3 edges -> 3 - 3 + 2 = 2."""
        header = MockBasicBlock(start=0x1000, end=0x1010)
        body = MockBasicBlock(start=0x1010, end=0x1020)
        exit_b = MockBasicBlock(start=0x1020, end=0x1030)

        header.outgoing_edges = [MockEdge(target=body), MockEdge(target=exit_b)]
        body.outgoing_edges = [MockEdge(target=header)]
        exit_b.outgoing_edges = []

        func = MockFunction(start=0x1000, basic_blocks=[header, body, exit_b])
        cfg = CFGAnalysis(func)
        result = cfg.extract_function_cfg()
        assert result["cyclomatic_complexity"] == 2


class TestCFGExtractFunctionCFG:
    def _make_simple_cfg(self):
        """Create a simple two-block CFG for testing structure."""
        entry = MockBasicBlock(start=0x1000, end=0x1010)
        exit_b = MockBasicBlock(start=0x1010, end=0x1020)

        entry.outgoing_edges = [MockEdge(source=entry, target=exit_b, edge_type=BranchType.UnconditionalBranch)]
        exit_b.incoming_edges = [MockEdge(source=entry, target=exit_b)]
        exit_b.outgoing_edges = []
        entry.incoming_edges = []

        func = MockFunction(start=0x1000, basic_blocks=[entry, exit_b])
        return func

    def test_extract_function_cfg_structure(self):
        func = self._make_simple_cfg()
        cfg = CFGAnalysis(func)
        result = cfg.extract_function_cfg()
        assert "function_address" not in result
        # New schema: no "blocks" or "measures" nesting
        assert "blocks" not in result
        assert "measures" not in result

    def test_function_cfg_new_keys(self):
        """Assert all expected keys are present in the new output dict."""
        func = self._make_simple_cfg()
        cfg = CFGAnalysis(func)
        result = cfg.extract_function_cfg()
        expected_keys = [
            "cfg_topology_hash",
            "block_count",
            "edge_count",
            "llil_total_operations",
            "call_count",
            "cyclomatic_complexity",
            "loop_count",
            "max_depth",
            "max_fan_out",
            "md_index_topdown",
            "md_index_bottomup",
            "prime_product_llil",
            "cfg_feature_tlsh",
            "wl_minhash",
            "bb_features",
            "cfg_adjacency",
        ]
        for key in expected_keys:
            assert key in result, f"Missing key: {key}"

    def test_returns_none_for_empty_blocks(self):
        func = MockFunction(start=0x1000, basic_blocks=[])
        cfg = CFGAnalysis(func)
        assert cfg.extract_function_cfg() is None


class TestCFGTopologyHash:
    def _make_two_block_cfg(self):
        entry = MockBasicBlock(start=0x1000, end=0x1010)
        exit_b = MockBasicBlock(start=0x1010, end=0x1020)
        entry.outgoing_edges = [MockEdge(target=exit_b)]
        exit_b.outgoing_edges = []
        return MockFunction(start=0x1000, basic_blocks=[entry, exit_b])

    def test_topology_hash_is_16_bytes(self):
        func = self._make_two_block_cfg()
        cfg = CFGAnalysis(func)
        result = cfg.extract_function_cfg()
        assert isinstance(result["cfg_topology_hash"], bytes)
        assert len(result["cfg_topology_hash"]) == 16

    def test_topology_hash_deterministic(self):
        func = self._make_two_block_cfg()
        r1 = CFGAnalysis(func).extract_function_cfg()
        r2 = CFGAnalysis(func).extract_function_cfg()
        assert r1["cfg_topology_hash"] == r2["cfg_topology_hash"]


class TestCFGLoopCount:
    def test_no_loops(self):
        entry = MockBasicBlock(start=0x1000, end=0x1010)
        exit_b = MockBasicBlock(start=0x1010, end=0x1020)
        entry.outgoing_edges = [MockEdge(target=exit_b)]
        exit_b.outgoing_edges = []
        func = MockFunction(start=0x1000, basic_blocks=[entry, exit_b])
        result = CFGAnalysis(func).extract_function_cfg()
        assert result["loop_count"] == 0

    def test_single_loop(self):
        header = MockBasicBlock(start=0x1000, end=0x1010)
        body = MockBasicBlock(start=0x1010, end=0x1020)
        exit_b = MockBasicBlock(start=0x1020, end=0x1030)
        header.outgoing_edges = [MockEdge(target=body), MockEdge(target=exit_b)]
        body.outgoing_edges = [MockEdge(target=header)]
        exit_b.outgoing_edges = []
        func = MockFunction(start=0x1000, basic_blocks=[header, body, exit_b])
        result = CFGAnalysis(func).extract_function_cfg()
        assert result["loop_count"] == 1


class TestCFGMaxDepth:
    def test_max_depth_linear(self):
        entry = MockBasicBlock(start=0x1000, end=0x1010)
        b1 = MockBasicBlock(start=0x1010, end=0x1020)
        b2 = MockBasicBlock(start=0x1020, end=0x1030)
        entry.outgoing_edges = [MockEdge(target=b1)]
        b1.outgoing_edges = [MockEdge(target=b2)]
        b2.outgoing_edges = []
        func = MockFunction(start=0x1000, basic_blocks=[entry, b1, b2])
        result = CFGAnalysis(func).extract_function_cfg()
        assert result["max_depth"] == 2

    def test_max_depth_single_block(self):
        block = MockBasicBlock(start=0x1000, end=0x1010, outgoing_edges=[])
        func = MockFunction(start=0x1000, basic_blocks=[block])
        result = CFGAnalysis(func).extract_function_cfg()
        assert result["max_depth"] == 0


class TestCFGCollectBlockLlilOps:
    """Test that _collect_block_llil_ops correctly maps LLIL data to native blocks."""

    def test_llil_fields_nonzero_with_mock_llil(self):
        """When LLIL is available, llil_total_operations and call_count should be non-zero."""
        # Two native blocks
        entry = MockBasicBlock(start=0x1000, end=0x1010)
        exit_b = MockBasicBlock(start=0x1010, end=0x1020)
        entry.outgoing_edges = [MockEdge(target=exit_b)]
        exit_b.outgoing_edges = []

        # LLIL instructions: SET_REG, CALL in first block; STORE, RET in second
        llil_instrs_1 = [
            MockLLILInstruction(LowLevelILOperation.LLIL_SET_REG),
            MockLLILInstruction(LowLevelILOperation.LLIL_CALL),
        ]
        llil_instrs_2 = [
            MockLLILInstruction(LowLevelILOperation.LLIL_STORE),
            MockLLILInstruction(LowLevelILOperation.LLIL_RET),
        ]

        # LLIL basic blocks map back to native blocks via source_block
        llil_bb1 = MockLLILBasicBlock(llil_instrs_1, source_block=entry)
        llil_bb2 = MockLLILBasicBlock(llil_instrs_2, source_block=exit_b)
        llil_func = MockLLILFunction([llil_bb1, llil_bb2])

        func = MockFunction(start=0x1000, basic_blocks=[entry, exit_b], llil=llil_func)
        result = CFGAnalysis(func, llil_function=llil_func).extract_function_cfg()

        assert result["llil_total_operations"] == 4
        assert result["call_count"] == 1
        assert result["prime_product_llil"] != 0

    def test_llil_none_gives_zero_fields(self):
        """Without LLIL, LLIL-dependent fields should be zero."""
        block = MockBasicBlock(start=0x1000, end=0x1010, outgoing_edges=[])
        func = MockFunction(start=0x1000, basic_blocks=[block])
        result = CFGAnalysis(func).extract_function_cfg()

        assert result["llil_total_operations"] == 0
        assert result["call_count"] == 0
        assert result["prime_product_llil"] == 0

    def test_bb_features_with_llil(self):
        """bb_features should reflect LLIL instruction categories when LLIL is available."""
        block = MockBasicBlock(start=0x1000, end=0x1010, outgoing_edges=[])

        llil_instrs = [
            MockLLILInstruction(LowLevelILOperation.LLIL_ADD),
            MockLLILInstruction(LowLevelILOperation.LLIL_LOAD),
            MockLLILInstruction(LowLevelILOperation.LLIL_CALL),
        ]
        llil_bb = MockLLILBasicBlock(llil_instrs, source_block=block)
        llil_func = MockLLILFunction([llil_bb])

        func = MockFunction(start=0x1000, basic_blocks=[block])
        result = CFGAnalysis(func, llil_function=llil_func).extract_function_cfg()

        feats = result["bb_features"]
        assert len(feats) == 1
        assert feats[0][0] == 3  # instruction count = 3
        # At least one non-zero category count (not all OTHER)
        category_counts = feats[0][1:7]
        assert sum(category_counts) > 0


# ============================================================================
# 9b. DisassemblyAnalysis
# ============================================================================


class TestDisassemblyAnalysisGetJson:
    def _make_analysis(self, instructions=None, basic_blocks=None):
        arch = Arch_x86()
        if instructions is None:
            instructions = [
                (
                    [
                        MockToken("push", InstructionTextTokenType.InstructionToken),
                        MockToken(" ", InstructionTextTokenType.TextToken),
                        MockToken("rbp", InstructionTextTokenType.RegisterToken),
                    ],
                    0x1000,
                ),
                (
                    [
                        MockToken("mov", InstructionTextTokenType.InstructionToken),
                        MockToken(" ", InstructionTextTokenType.TextToken),
                        MockToken("rsp", InstructionTextTokenType.RegisterToken),
                    ],
                    0x1003,
                ),
            ]
        if basic_blocks is None:
            basic_blocks = [MockBasicBlock(
                start=0x1000, end=0x1010,
                disassembly_text=[MockDisassemblyLine([MockToken("push rbp")])]
            )]

        func = MockFunction(
            name="test_func",
            start=0x1000,
            basic_blocks=basic_blocks,
            instructions=instructions,
            symbol=MockSymbol(symbol_type=SymbolType.FunctionSymbol, name="test_func"),
            stack_adjustment=MagicMock(value=-8),
            mlil=None,
        )
        bv = MockBinaryView()
        logger = MagicMock()
        return DisassemblyAnalysis(arch, func, bv, logger)

    def test_get_json_basic_structure(self):
        da = self._make_analysis()
        result, errors = da.get_json()
        expected_keys = [
            "disassembled_function_hash",
            "disassembled_function",
            "disassembled_function_no_addresses",
            "disassembled_function_name",
            "disassembled_function_address",
            "instructions_count",
            "function_type",
            "instructions_types",
            "control_flow_count",
            "memory_access_pattern",
            "register_usage",
            "data_references_count",
        ]
        for key in expected_keys:
            assert key in result, f"Missing key: {key}"

    def test_get_json_hash_deterministic(self):
        da = self._make_analysis()
        r1, _ = da.get_json()
        da2 = self._make_analysis()
        r2, _ = da2.get_json()
        assert r1["disassembled_function_hash"] == r2["disassembled_function_hash"]


class TestDisassemblyCollectInstructionTypes:
    def test_collect_instruction_types(self):
        arch = Arch_x86()
        instructions = [
            ([MockToken("MOV", InstructionTextTokenType.InstructionToken)], 0x1000),
            ([MockToken("ADD", InstructionTextTokenType.InstructionToken)], 0x1001),
            ([MockToken("MOV", InstructionTextTokenType.InstructionToken)], 0x1002),
        ]
        func = MockFunction(start=0x1000, instructions=instructions, symbol=MockSymbol())
        da = DisassemblyAnalysis(arch, func, MockBinaryView(), MagicMock())
        types = da.collect_instruction_types()
        assert "DATA_MOVEMENT" in types
        assert "ARITHMETIC" in types

    def test_collect_instruction_types_empty(self):
        arch = Arch_x86()
        func = MockFunction(start=0x1000, instructions=[], symbol=MockSymbol())
        da = DisassemblyAnalysis(arch, func, MockBinaryView(), MagicMock())
        types = da.collect_instruction_types()
        assert types == {}


class TestDisassemblyMemoryPatterns:
    def _make_memory_instruction(self, tokens):
        return ([t for t in tokens], 0x1000)

    def test_collect_memory_patterns_stack(self):
        arch = Arch_x86()
        tokens = [
            MockToken("[", InstructionTextTokenType.BeginMemoryOperandToken),
            MockToken("RSP", InstructionTextTokenType.RegisterToken),
            MockToken("+0x8", InstructionTextTokenType.TextToken),
            MockToken("]", InstructionTextTokenType.EndMemoryOperandToken),
        ]
        instructions = [self._make_memory_instruction(tokens)]
        func = MockFunction(start=0x1000, instructions=instructions, symbol=MockSymbol())
        da = DisassemblyAnalysis(arch, func, MockBinaryView(), MagicMock())
        patterns = da.collect_memory_patterns()
        assert "MEM_STACK" in patterns

    def test_collect_memory_patterns_direct(self):
        arch = Arch_x86()
        tokens = [
            MockToken("[", InstructionTextTokenType.BeginMemoryOperandToken),
            MockToken("0x402000", InstructionTextTokenType.TextToken),
            MockToken("]", InstructionTextTokenType.EndMemoryOperandToken),
        ]
        instructions = [self._make_memory_instruction(tokens)]
        func = MockFunction(start=0x1000, instructions=instructions, symbol=MockSymbol())
        da = DisassemblyAnalysis(arch, func, MockBinaryView(), MagicMock())
        patterns = da.collect_memory_patterns()
        assert "MEM_DIRECT" in patterns

    def test_collect_memory_patterns_scaled(self):
        arch = Arch_x86()
        tokens = [
            MockToken("[", InstructionTextTokenType.BeginMemoryOperandToken),
            MockToken("RAX+RCX*4", InstructionTextTokenType.TextToken),
            MockToken("]", InstructionTextTokenType.EndMemoryOperandToken),
        ]
        instructions = [self._make_memory_instruction(tokens)]
        func = MockFunction(start=0x1000, instructions=instructions, symbol=MockSymbol())
        da = DisassemblyAnalysis(arch, func, MockBinaryView(), MagicMock())
        patterns = da.collect_memory_patterns()
        assert "MEM_SCALED_INDEX" in patterns

    def test_collect_memory_patterns_base_offset(self):
        arch = Arch_x86()
        tokens = [
            MockToken("[", InstructionTextTokenType.BeginMemoryOperandToken),
            MockToken("RAX+0x10", InstructionTextTokenType.TextToken),
            MockToken("]", InstructionTextTokenType.EndMemoryOperandToken),
        ]
        instructions = [self._make_memory_instruction(tokens)]
        func = MockFunction(start=0x1000, instructions=instructions, symbol=MockSymbol())
        da = DisassemblyAnalysis(arch, func, MockBinaryView(), MagicMock())
        patterns = da.collect_memory_patterns()
        assert "MEM_BASE_OFFSET" in patterns


class TestDisassemblyRegisterUsage:
    def test_collect_register_usage_gpr(self):
        arch = Arch_x86()
        instructions = [
            ([MockToken("RAX", InstructionTextTokenType.RegisterToken)], 0x1000),
        ]
        func = MockFunction(start=0x1000, instructions=instructions, symbol=MockSymbol())
        da = DisassemblyAnalysis(arch, func, MockBinaryView(), MagicMock())
        regs = da.collect_register_usage()
        assert "GPR" in regs

    def test_collect_register_usage_simd(self):
        arch = Arch_x86()
        instructions = [
            ([MockToken("XMM0", InstructionTextTokenType.RegisterToken)], 0x1000),
        ]
        func = MockFunction(start=0x1000, instructions=instructions, symbol=MockSymbol())
        da = DisassemblyAnalysis(arch, func, MockBinaryView(), MagicMock())
        regs = da.collect_register_usage()
        assert "SIMD" in regs

    def test_collect_register_usage_fpu(self):
        arch = Arch_x86()
        instructions = [
            ([MockToken("ST0", InstructionTextTokenType.RegisterToken)], 0x1000),
        ]
        func = MockFunction(start=0x1000, instructions=instructions, symbol=MockSymbol())
        da = DisassemblyAnalysis(arch, func, MockBinaryView(), MagicMock())
        regs = da.collect_register_usage()
        assert "FPU" in regs


class TestDisassemblyMisc:
    def test_count_data_references(self):
        arch = Arch_x86()
        func = MockFunction(start=0x1000, instructions=[], symbol=MockSymbol(), mlil=None)
        da = DisassemblyAnalysis(arch, func, MockBinaryView(), MagicMock())
        assert da.count_data_references() == 0

    def test_compute_max_block_size(self):
        arch = Arch_x86()
        blocks = [
            MockBasicBlock(disassembly_text=[MockDisassemblyLine([]) for _ in range(3)]),
            MockBasicBlock(disassembly_text=[MockDisassemblyLine([]) for _ in range(5)]),
        ]
        func = MockFunction(start=0x1000, basic_blocks=blocks, instructions=[], symbol=MockSymbol())
        da = DisassemblyAnalysis(arch, func, MockBinaryView(), MagicMock())
        assert da.compute_max_block_size() == 5

    def test_compute_num_calls(self):
        arch = Arch_x86()
        instructions = [
            ([MockToken("CALL", InstructionTextTokenType.InstructionToken)], 0x1000),
            ([MockToken("MOV", InstructionTextTokenType.InstructionToken)], 0x1005),
            ([MockToken("CALL", InstructionTextTokenType.InstructionToken)], 0x1010),
        ]
        func = MockFunction(start=0x1000, instructions=instructions, symbol=MockSymbol())
        da = DisassemblyAnalysis(arch, func, MockBinaryView(), MagicMock())
        assert da.compute_num_calls() == 2

    def test_estimate_stack_size_value(self):
        arch = Arch_x86()
        stack = MagicMock()
        stack.value = -16
        func = MockFunction(start=0x1000, instructions=[], symbol=MockSymbol(), stack_adjustment=stack)
        da = DisassemblyAnalysis(arch, func, MockBinaryView(), MagicMock())
        assert da.estimate_stack_size() == -16

    def test_estimate_stack_size_int(self):
        arch = Arch_x86()
        func = MockFunction(start=0x1000, instructions=[], symbol=MockSymbol(), stack_adjustment=-8)
        da = DisassemblyAnalysis(arch, func, MockBinaryView(), MagicMock())
        assert da.estimate_stack_size() == -8

    def test_normalize_opcode(self):
        arch = Arch_x86()
        func = MockFunction(start=0x1000, instructions=[], symbol=MockSymbol())
        da = DisassemblyAnalysis(arch, func, MockBinaryView(), MagicMock())
        assert da.normalize_opcode("mov") == "MOV"
        assert da.normalize_opcode("PUSH") == "PUSH"


# ============================================================================
# 9c. LowLevelAnalysis (basic tests with mocked LLIL)
# ============================================================================


class MockLLILInstruction:
    """Mock LLIL instruction for low_level.py tests."""
    def __init__(self, operation, operands=None, address=0):
        self.operation = operation
        self.operands = operands or []
        self.address = address

    def __str__(self):
        return f"LLIL_{self.operation}"


class MockLLILBasicBlock:
    def __init__(self, instructions, source_block=None):
        self._instructions = instructions
        self.source_block = source_block

    def __iter__(self):
        return iter(self._instructions)


class MockLLILFunction:
    def __init__(self, basic_blocks):
        self.basic_blocks = basic_blocks
        self._instructions = []
        for bb in basic_blocks:
            self._instructions.extend(bb._instructions)

    @property
    def instructions(self):
        return iter(self._instructions)

    @property
    def source_function(self):
        mock = MagicMock()
        mock.start = 0x1000
        return mock


class TestLowLevelAnalysisCountControlFlow:
    def test_count_control_flow_instructions(self):
        from redb.extractors.decompiler.bninja.analysis.low_level import LowLevelAnalysis
        instrs = [
            MockLLILInstruction(LowLevelILOperation.LLIL_IF),
            MockLLILInstruction(LowLevelILOperation.LLIL_SET_REG),
            MockLLILInstruction(LowLevelILOperation.LLIL_CALL),
            MockLLILInstruction(LowLevelILOperation.LLIL_GOTO),
        ]
        bb = MockLLILBasicBlock(instrs)
        llil_func = MockLLILFunction([bb])

        func = MockFunction(start=0x1000, llil=llil_func, symbol=MockSymbol())
        func.low_level_il = None
        bv = MockBinaryView()
        bv.arch = MagicMock()
        bv.arch.stack_pointer = "sp"
        la = LowLevelAnalysis(func, bv, MagicMock())
        assert la.count_control_flow_instructions() == 3  # IF, CALL, GOTO


class TestLowLevelAnalysisNumCalls:
    def test_compute_num_calls_llil(self):
        from redb.extractors.decompiler.bninja.analysis.low_level import LowLevelAnalysis
        instrs = [
            MockLLILInstruction(LowLevelILOperation.LLIL_CALL),
            MockLLILInstruction(LowLevelILOperation.LLIL_TAILCALL),
            MockLLILInstruction(LowLevelILOperation.LLIL_SET_REG),
        ]
        bb = MockLLILBasicBlock(instrs)
        llil_func = MockLLILFunction([bb])

        func = MockFunction(start=0x1000, llil=llil_func, symbol=MockSymbol())
        func.low_level_il = None
        bv = MockBinaryView()
        la = LowLevelAnalysis(func, bv, MagicMock())
        assert la.compute_num_calls() == 2


class TestLowLevelAnalysisCollectNormalization:
    def test_collect_low_level(self):
        from redb.extractors.decompiler.bninja.analysis.low_level import LowLevelAnalysis
        instrs = [
            MockLLILInstruction(LowLevelILOperation.LLIL_SET_REG, address=0x1000),
            MockLLILInstruction(LowLevelILOperation.LLIL_STORE, address=0x1004),
        ]
        bb = MockLLILBasicBlock(instrs)
        llil_func = MockLLILFunction([bb])

        func = MockFunction(start=0x1000, llil=llil_func, symbol=MockSymbol())
        func.low_level_il = None
        bv = MockBinaryView()
        la = LowLevelAnalysis(func, bv, MagicMock())
        result, _ = la._collect_low_level_and_with_addr()
        assert len(result) == 2
        # Each item is a list of operation ints
        assert isinstance(result[0], list)

    def test_collect_low_level_with_addr_offset_clamping(self):
        from redb.extractors.decompiler.bninja.analysis.low_level import LowLevelAnalysis
        instrs = [
            MockLLILInstruction(LowLevelILOperation.LLIL_SET_REG, address=0x0FFF),  # Before function start
        ]
        bb = MockLLILBasicBlock(instrs)
        llil_func = MockLLILFunction([bb])

        func = MockFunction(start=0x1000, llil=llil_func, symbol=MockSymbol())
        func.low_level_il = None
        bv = MockBinaryView()
        la = LowLevelAnalysis(func, bv, MagicMock())
        _, result = la._collect_low_level_and_with_addr()
        assert len(result) == 1
        offset, _ = result[0]
        assert offset == 0  # Clamped to 0