Olivier D. Faugeras

244 papers A* 46A 11B 2C 1Misc 6Journal 110Unranked 61
YearRankTypeTitle / Venue / Authors
2021 J jnl
CoRR
Olivier D. Faugeras, Anna Song, Romain Veltz
2019 J jnl
PLoS Comput. Biol.
Anna Song, Olivier D. Faugeras, Romain Veltz
2019 J jnl
J. Comput. Neurosci.
Mathieu Desroches, Olivier D. Faugeras, Martin Krupa, Massimo Mantegazza
2015 J jnl
Entropy
Olivier D. Faugeras, James MacLaurin
2015 J jnl
SIAM J. Math. Anal.
Romain Veltz, Olivier D. Faugeras
2014 J jnl
Entropy
Olivier D. Faugeras, James MacLaurin
2014 J jnl
Entropy
Olivier D. Faugeras, James MacLaurin
2014 J jnl
J. Comput. Neurosci.
James Rankin, Andrew Isaac Meso, Guillaume S. Masson, Olivier D. Faugeras, Pierre Kornprobst
2013 J jnl
SIAM J. Math. Anal.
Romain Veltz, Olivier D. Faugeras
2013 J jnl
J. Comput. Neurosci.
James Rankin, Émilien Tlapale, Romain Veltz, Olivier D. Faugeras, Pierre Kornprobst
2012 J jnl
Neural Comput.
Mathieu Galtier, Olivier D. Faugeras, Paul C. Bressloff
2012 conf
ECCV Workshops (1)
Olivier D. Faugeras
2012 J jnl
SIAM J. Appl. Dyn. Syst.
Jonathan Touboul, Geoffroy Hermann, Olivier D. Faugeras
2012 J jnl
Comput. Sci. Eng.
Javier Baladron Pezoa, Diego Fasoli, Olivier D. Faugeras
2011 J jnl
J. Comput. Neurosci.
Jonathan Touboul, Olivier D. Faugeras
2011 J jnl
IEEE Trans. Medical Imaging
Thomas Deneux, Olivier D. Faugeras, Sylvain Takerkart, Guillaume S. Masson, Ivo Vanzetta
2011 J jnl
J. Nonlinear Sci.
Pascal Chossat, Grégory Faye, Olivier D. Faugeras
2011 J jnl
Neural Comput.
Jonathan Touboul, Fabrice Wendling, Patrick Chauvel, Olivier D. Faugeras
2010 J jnl
Neural Comput.
Thomas Deneux, Olivier D. Faugeras
2010 J jnl
SIAM J. Appl. Dyn. Syst.
Romain Veltz, Olivier D. Faugeras
2009 J jnl
Frontiers Comput. Neurosci.
Olivier D. Faugeras, Jonathan Touboul, Bruno Cessac
2009 J jnl
SIAM J. Imaging Sci.
Christophe Lenglet, Emmanuel Prados, Jean-Philippe Pons, Rachid Deriche, Olivier D. Faugeras
2009 J jnl
PLoS Comput. Biol.
Pascal Chossat, Olivier D. Faugeras
2009 J jnl
Neural Comput.
Olivier D. Faugeras, Romain Veltz, François Grimbert
2008 J jnl
SIAM J. Appl. Math.
Olivier D. Faugeras, François Grimbert, Jean-Jacques E. Slotine
2008 J jnl
Neurocomputing
Iasonas Kokkinos, Rachid Deriche, Olivier D. Faugeras, Petros Maragos
2007 J jnl
Int. J. Comput. Vis.
Guillaume Charpiat, Pierre Maurel, Jean-Philippe Pons, Renaud Keriven, Olivier D. Faugeras
2007 J jnl
Int. J. Comput. Vis.
Jean-Philippe Pons, Renaud Keriven, Olivier D. Faugeras
2007 A* conf
CVPR
Guillaume Charpiat, Olivier D. Faugeras, Renaud Keriven
2006 J jnl
J. Math. Imaging Vis.
Emmanuel Prados, Fabio Camilli, Olivier D. Faugeras
2006 ch.
Statistics and Analysis of Shapes
Guillaume Charpiat, Olivier D. Faugeras, Renaud Keriven, Pierre Maurel
2006 J jnl
Neural Comput.
François Grimbert, Olivier D. Faugeras
2006 conf
ISBI
Ivo Vanzetta, Thomas Deneux, Guillaume S. Masson, Olivier D. Faugeras
2006 conf
CVPR (1)
Emmanuel Prados, Stefano Soatto, Christophe Lenglet, Jean-Philippe Pons, Nicolas Wotawa, Rachid Deriche, Olivier D. Faugeras
2006 conf
ICASSP (5)
Guillaume Charpiat, Olivier D. Faugeras, Renaud Keriven, Pierre Maurel
2006 conf
ISBI
Thomas Deneux, Olivier D. Faugeras
2006 J jnl
Int. J. Comput. Vis.
Nikos Paragios, Olivier D. Faugeras
2006 book
Nikos Paragios, Yunmei Chen, Olivier D. Faugeras
2006 J jnl
J. Comput. Phys.
Jean-Philippe Pons, Gerardo Hermosillo, Renaud Keriven, Olivier D. Faugeras
2006 ch.
Handbook of Mathematical Models in Computer Vision
Jean-Philippe Pons, Renaud Keriven, Olivier D. Faugeras
2006 conf
ICPR (4)
Pierre Maurel, Renaud Keriven, Olivier D. Faugeras
2006 ch.
Handbook of Mathematical Models in Computer Vision
Emmanuel Prados, Olivier D. Faugeras
2006 J jnl
J. Math. Imaging Vis.
Christophe Lenglet, Mikaël Rousson, Rachid Deriche, Olivier D. Faugeras
2006 J jnl
NeuroImage
Thomas Deneux, Olivier D. Faugeras
2005 J jnl
Int. J. Comput. Vis.
Emmanuel Prados, Olivier D. Faugeras
2005 conf
IPMI
Christophe Lenglet, Mikaël Rousson, Rachid Deriche, Olivier D. Faugeras, Stéphane Lehéricy, Kâmil Ugurbil
2005 J jnl
IEEE Trans. Medical Imaging
Jan Kybic, Maureen Clerc, Touffic Abboud, Olivier D. Faugeras, Renaud Keriven, Théodore Papadopoulo
2005 J jnl
Found. Comput. Math.
Guillaume Charpiat, Olivier D. Faugeras, Renaud Keriven
2005 A* conf
ICCV
Guillaume Charpiat, Renaud Keriven, Jean-Philippe Pons, Olivier D. Faugeras
2005 A* conf
ICCV
Guillaume Charpiat, Olivier D. Faugeras, Renaud Keriven
2005 conf
CVPR (2)
Jean-Philippe Pons, Renaud Keriven, Olivier D. Faugeras
2005 conf
CVPR (2)
Emmanuel Prados, Olivier D. Faugeras
2005 ed.
VLSM
Nikos Paragios, Olivier D. Faugeras, Tony Chan, Christoph Schnörr
2004 conf
ECCV (2)
Iasonas Kokkinos, Rachid Deriche, Petros Maragos, Olivier D. Faugeras
2004 conf
MICCAI (1)
Jean-Philippe Pons, Renaud Keriven, Olivier D. Faugeras
2004 J jnl
Medical Image Anal.
Bertrand Thirion, Olivier D. Faugeras
2004 J jnl
Int. J. Comput. Vis.
Olivier D. Faugeras
2004 conf
ECCV (4)
Christophe Lenglet, Rachid Deriche, Olivier D. Faugeras
2004 conf
ISBI
Olivier D. Faugeras, Bertrand Thirion
2004 J jnl
J. Math. Imaging Vis.
Christophe Chefd'Hotel, David Tschumperlé, Rachid Deriche, Olivier D. Faugeras
2004 J jnl
Int. J. Imaging Syst. Technol.
Ronald R. Peeters, Pierre Kornprobst, Mila Nikolova, Stefan Sunaert, Thierry Viéville, Grégoire Malandain, Rachid Deriche, Olivier D. Faugeras, Michael K. P. Ng, Paul Van Hecke
2004 conf
ECCV (4)
Emmanuel Prados, Olivier D. Faugeras
2004 J jnl
SIAM J. Appl. Math.
Gerardo Hermosillo, Olivier D. Faugeras
2003 A* conf
ICCV
Emmanuel Prados, Olivier D. Faugeras
2003 conf
EUROCAST
Bertrand Thirion, Olivier D. Faugeras
2003 J jnl
NeuroImage
Bertrand Thirion, Olivier D. Faugeras
2003 J jnl
Int. J. Comput. Vis.
Olivier D. Faugeras
2003 conf
MICCAI (2)
Bertrand Thirion, Olivier D. Faugeras
2003 A* conf
ICCV
Jean-Philippe Pons, Gerardo Hermosillo, Renaud Keriven, Olivier D. Faugeras
2003 J jnl
SIAM J. Appl. Math.
Gilles Aubert, Michel Barlaud, Olivier D. Faugeras, Stéphanie Jehan-Besson
2003 A* conf
ICCV
Stéphanie Jehan-Besson, Michel Barlaud, Gilles Aubert, Olivier D. Faugeras
2003 conf
ICIP (2)
Guillaume Charpiat, Olivier D. Faugeras, Renaud Keriven
2003 conf
IPMI
Geoffray Adde, Maureen Clerc, Olivier D. Faugeras, Renaud Keriven, Jan Kybic, Théodore Papadopoulo
2003 J jnl
Int. J. Comput. Vis.
José Gomes, Olivier D. Faugeras
2003 A* conf
ICCV
Jean-Philippe Pons, Renaud Keriven, Olivier D. Faugeras, Gerardo Hermosillo
2002 conf
ECCV (1)
Christophe Chefd'Hotel, David Tschumperlé, Rachid Deriche, Olivier D. Faugeras
2002 conf
ISBI
Bertrand Thirion, Olivier D. Faugeras
2002 J jnl
Int. J. Comput. Vis.
Olivier D. Faugeras
2002 conf
ISBI
Christophe Chefd'Hotel, Gerardo Hermosillo, Olivier D. Faugeras
2002 conf
ECCV (2)
Emmanuel Prados, Olivier D. Faugeras, Elisabeth Rouy
2002 J jnl
J. Vis. Commun. Image Represent.
Olivier D. Faugeras, Pietro Perona, Guillermo Sapiro
2002 conf
ISBI
Maureen Clerc, Olivier D. Faugeras, Renaud Keriven, Jan Kybic, Théodore Papadopoulo
2002 J jnl
Int. J. Comput. Vis.
Gerardo Hermosillo, Christophe Chefd'Hotel, Olivier D. Faugeras
2001 J jnl
Comput. Vis. Image Underst.
Quentin Delamarre, Olivier D. Faugeras
2001 J jnl
Medical Image Anal.
Liana M. Lorigo, Olivier D. Faugeras, W. Eric L. Grimson, Renaud Keriven, Ron Kikinis, Arya Nabavi, Carl-Fredrik Westin
2001 conf
CVPR (1)
Gerardo Hermosillo, Olivier D. Faugeras
2001 J jnl
Int. J. Comput. Vis.
Olivier D. Faugeras
2001 J jnl
Int. J. Comput. Vis.
Olivier D. Faugeras
2001 J jnl
Mach. Vis. Appl.
Frederic Devernay, Olivier D. Faugeras
2001 book
The geometry of multiple images - the laws that govern the formation of multiple images of a scene and some of their applications.
Olivier D. Faugeras, Quang-Tuan Luong, Théodore Papadopoulo
2001 conf
Scale-Space
José Gomes, Olivier D. Faugeras
2000 J jnl
J. Vis. Commun. Image Represent.
Olivier D. Faugeras, Pietro Perona, Guillermo Sapiro
2000 A* conf
CVPR
Liana M. Lorigo, W. Eric L. Grimson, Olivier D. Faugeras, Renaud Keriven, Ron Kikinis, Arya Nabavi, Carl-Fredrik Westin
2000 A conf
BMVC
W. Eric L. Grimson, Michael E. Leventon, Olivier D. Faugeras
2000 conf
IMA Conference on the Mathematics of Surfaces
Olivier D. Faugeras, José Gomes
2000 J jnl
Int. J. Comput. Vis.
Olivier D. Faugeras
2000 J jnl
Int. J. Comput. Vis.
Takeo Kanade, Olivier D. Faugeras
2000 conf
ECCV (1)
José Gomes, Olivier D. Faugeras
2000 J jnl
J. Vis. Commun. Image Represent.
José Gomes, Olivier D. Faugeras
2000 A conf
MICCAI
Carl-Fredrik Westin, Liana M. Lorigo, Olivier D. Faugeras, W. Eric L. Grimson, Steven Dawson, Alexander Norbash, Ron Kikinis
2000 J jnl
IEEE Trans. Pattern Anal. Mach. Intell.
Olivier D. Faugeras, Long Quan, Peter F. Sturm
2000 J jnl
J. Vis. Commun. Image Represent.
Olivier D. Faugeras, Mads Nielsen, Pietro Perona, Bart M. ter Haar Romeny, Guillermo Sapiro
2000 A* conf
CVPR
Michael E. Leventon, W. Eric L. Grimson, Olivier D. Faugeras
1999 A* conf
ICCV
Quentin Delamarre, Olivier D. Faugeras
1999 J jnl
IEEE Trans. Pattern Anal. Mach. Intell.
Gabriela Csurka, Olivier D. Faugeras
1999 conf
IPMI
Liana M. Lorigo, Olivier D. Faugeras, W. Eric L. Grimson, Renaud Keriven, Ron Kikinis, Carl-Fredrik Westin
1999 J jnl
Int. J. Comput. Vis.
Olivier D. Faugeras, Takeo Kanade
1999 conf
Scale-Space
José Gomes, Olivier D. Faugeras
1999 conf
Workshop on Vision Algorithms
P. Anandan, Olivier D. Faugeras, Richard I. Hartley, Jitendra Malik, Joseph L. Mundy
1999 conf
Scale-Space
Gerardo Hermosillo, Olivier D. Faugeras, José Gomes
1998 J jnl
Comput. Vis. Image Underst.
Olivier D. Faugeras, Luc Robert, Stéphane Laveau, Gabriela Csurka, Cyril Zeller, Cyrille Gauclin, Imad Zoghlami
1998 conf
ECCV (1)
Théodore Papadopoulo, Olivier D. Faugeras
1998 A* conf
ICCV
Olivier D. Faugeras, Théodore Papadopoulo
1998 conf
ECCV (1)
Olivier D. Faugeras, Renaud Keriven
1998 J jnl
Image Vis. Comput.
Gabriela Csurka, Olivier D. Faugeras
1998 B conf
FG
Quentin Delamarre, Olivier D. Faugeras
1998 conf
ACCV (1)
Rachid Deriche, Christophe Bouvin, Olivier D. Faugeras
1998 J jnl
Comput. Vis. Image Underst.
Quang-Tuan Luong, Olivier D. Faugeras
1998 A conf
MICCAI
Liana M. Lorigo, Olivier D. Faugeras, W. Eric L. Grimson, Renaud Keriven, Ron Kikinis
1998 conf
ECCV (1)
Olivier D. Faugeras, Long Quan, Peter F. Sturm
1998 J jnl
IEEE Trans. Image Process.
Olivier D. Faugeras, Renaud Keriven
1997 J jnl
Comput. Vis. Image Underst.
Charlie Rothwell, Olivier D. Faugeras, Gabriela Csurka
1997 conf
AFPAC
Gabriela Csurka, Olivier D. Faugeras
1997 J jnl
Comput. Vis. Image Underst.
Gabriela Csurka, Cyril Zeller, Zhengyou Zhang, Olivier D. Faugeras
1997 C conf
CAIP
Gabriela Csurka, Olivier D. Faugeras
1997 J jnl
Int. J. Comput. Vis.
Olivier D. Faugeras, Takeo Kanade
1997 conf
Scale-Space
Olivier D. Faugeras, Renaud Keriven
1997 J jnl
Int. J. Comput. Vis.
Quang-Tuan Luong, Olivier D. Faugeras
1997 J jnl
Int. J. Comput. Vis.
Nassir Navab, Olivier D. Faugeras
1997 A* conf
CVPR
Imad Zoghlami, Olivier D. Faugeras, Rachid Deriche
1996 conf
ECCV (2)
Théodore Papadopoulo, Olivier D. Faugeras
1996 A* conf
CVPR
Frederic Devernay, Olivier D. Faugeras
1996 J jnl
IEEE Trans. Robotics Autom.
Zhengyou Zhang, Quang-Tuan Luong, Olivier D. Faugeras
1996 J jnl
Int. J. Comput. Vis.
Thierry Viéville, Olivier D. Faugeras, Quang-Tuan Luong
1996 conf
ECCV (1)
Stéphane Laveau, Olivier D. Faugeras
1996 conf
ICIP (3)
Frederic F. Leymarie, Arnaud de La Fortelle, Jan J. Koenderink, Astrid M. L. Kappers, Marigo Stavridi, Bram van Ginneken, Stefan Müller, S. Krake, Olivier D. Faugeras, Luc Robert, Cyrille Gauclin, Stéphane Laveau, Cyril Zeller
1996 J jnl
Comput. Vis. Image Underst.
Thierry Viéville, Olivier D. Faugeras
1996 J jnl
Int. J. Comput. Vis.
Quang-Tuan Luong, Olivier D. Faugeras
1996 J jnl
Int. J. Comput. Vis.
Olivier D. Faugeras, Luc Robert
1995 A* conf
ICCV
Charlie Rothwell, Gabriela Csurka, Olivier D. Faugeras
1995 J jnl
Artif. Intell.
Zhengyou Zhang, Rachid Deriche, Olivier D. Faugeras, Quang-Tuan Luong
1995 J jnl
Comput. Vis. Image Underst.
W. Brent Seales, Olivier D. Faugeras
1995 A* conf
ICCV
Thierry Viéville, Olivier D. Faugeras
1995 A* conf
ICCV
Olivier D. Faugeras, Bernard Mourrain
1995 J jnl
Image Vis. Comput.
Luc Robert, Olivier D. Faugeras
1995 conf
ICIP (3)
Olivier D. Faugeras, Renaud Keriven
1994 conf
ICPR (1)
Stéphane Laveau, Olivier D. Faugeras
1994 conf
ECCV (1)
Quang-Tuan Luong, Olivier D. Faugeras
1994 conf
ICPR (1)
Quang-Tuan Luong, Olivier D. Faugeras
1994 conf
ICPR (1)
Cyril Zeller, Olivier D. Faugeras
1994 A* conf
CVPR
Frederic Devernay, Olivier D. Faugeras
1994 conf
ICPR (1)
Théodore Papadopoulo, Olivier D. Faugeras
1994 conf
ECCV (1)
Théodore Papadopoulo, Olivier D. Faugeras
1994 conf
ICPR (1)
Zhengyou Zhang, Quang-Tuan Luong, Olivier D. Faugeras
1994 conf
ICPR (1)
Quang-Tuan Luong, Olivier D. Faugeras
1994 conf
ECCV (1)
Rachid Deriche, Zhengyou Zhang, Quang-Tuan Luong, Olivier D. Faugeras
1994 J jnl
Int. J. Comput. Vis.
Thierry Viéville, Olivier D. Faugeras
1994 A conf
BMVC
Zhengyou Zhang, Quang-Tuan Luong, Olivier D. Faugeras
1994 conf
ECCV (1)
Olivier D. Faugeras, Luc Robert
1993 J jnl
Int. J. Comput. Vis.
Olivier D. Faugeras, Théodore Papadopoulo
1993 conf
Applications of Invariance in Computer Vision
Théodore Papadopoulo, Olivier D. Faugeras
1993 A conf
IROS
Thierry Viéville, François Romann, Bernard Hotz, Hervé Mathieu, Michel Buffa, Luc Robert, P. E. Dos Santos Facao, Olivier D. Faugeras, J. T. Audren
1993 conf
Applications of Invariance in Computer Vision
Olivier D. Faugeras
1993 J jnl
Int. J. Comput. Vis.
Olivier D. Faugeras
1993 A* conf
CVPR
Quang-Tuan Luong, Olivier D. Faugeras
1993 J jnl
Int. J. Comput. Vis.
Takeo Kanade, Robert C. Bolles, Olivier D. Faugeras
1993 A* conf
CVPR
Nassir Navab, Olivier D. Faugeras
1993 A* conf
ICCV
Luc Robert, Olivier D. Faugeras
1993 A* conf
ICCV
Nassir Navab, Olivier D. Faugeras, Thierry Viéville
1992 Misc conf
MVA
Michel Buffa, Olivier D. Faugeras, Zhengyou Zhang
1992 J jnl
Int. J. Comput. Vis.
Stephen J. Maybank, Olivier D. Faugeras
1992 A conf
ECAI
Quang-Tuan Luong, Olivier D. Faugeras
1992 A* conf
ICRA
L. Boissier, Bernard Hotz, Catherine Proy, Olivier D. Faugeras, Pascal Fua
1992 A* conf
ECCV
Olivier D. Faugeras, Quang-Tuan Luong, Stephen J. Maybank
1992 A conf
ECAI
Luc Robert, Rachid Deriche, Olivier D. Faugeras
1992 J jnl
IEEE Trans. Pattern Anal. Mach. Intell.
Zhengyou Zhang, Olivier D. Faugeras
1992 A* conf
ECCV
Zhengyou Zhang, Olivier D. Faugeras
1992 A* conf
CVPR
Olivier Monga, Serge Benayoun, Olivier D. Faugeras
1992 conf
VBC
Olivier Monga, Serge Benayoun, Olivier D. Faugeras
1992 A* conf
ECCV
Thierry Viéville, Olivier D. Faugeras
1992 conf
ICPR (1)
Quang-Tuan Luong, Olivier D. Faugeras
1992 J jnl
Int. J. Pattern Recognit. Artif. Intell.
Olivier D. Faugeras, Rachid Deriche, Hervé Mathieu, Nicholas Ayache, Gregory Randall
1992 J jnl
Int. J. Comput. Vis.
Zhengyou Zhang, Olivier D. Faugeras
1992 J jnl
IEEE Trans. Pattern Anal. Mach. Intell.
Régis Vaillant, Olivier D. Faugeras
1992 A* conf
ECCV
Olivier D. Faugeras
1992 J jnl
CVGIP Image Underst.
Olivier D. Faugeras, Joe L. Mundy, Narendra Ahuja, Charles R. Dyer, Alex Pentland, Ramesh C. Jain, Katsushi Ikeuchi, Kevin W. Bowyer
1991 A* conf
CVPR
Luc Robert, Olivier D. Faugeras
1991 J jnl
Image Vis. Comput.
Zhengyou Zhang, Olivier D. Faugeras
1990 conf
ICPR (1)
Rachid Deriche, Olivier D. Faugeras
1990 conf
ICPR (1)
Zhengyou Zhang, Olivier D. Faugeras
1990 A* ed.
ECCV
Olivier D. Faugeras
1990 J jnl
IEEE Trans. Pattern Anal. Mach. Intell.
Yuncai Liu, Thomas S. Huang, Olivier D. Faugeras
1990 A conf
BMVC
Zhengyou Zhang, Olivier D. Faugeras
1990 A* conf
ICCV
Thierry Viéville, Olivier D. Faugeras
1990 J jnl
Int. J. Imaging Syst. Technol.
Olivier D. Faugeras, Nassir Navab, Rachid Deriche
1990 ch.
Autonomous Robot Vehicles
Nicholas Ayache, Olivier D. Faugeras
1990 J jnl
Int. J. Comput. Vis.
Olivier D. Faugeras, Stephen J. Maybank
1990 A* conf
ECCV
Olivier D. Faugeras
1990 A* conf
ICCV
Nassir Navab, Rachid Deriche, Olivier D. Faugeras
1990 J jnl
Artif. Intell.
Olivier D. Faugeras, Elisabeth Le Bras-Mehlman, Jean-Daniel Boissonnat
1990 A* conf
ECCV
Rachid Deriche, Olivier D. Faugeras
1990 A conf
ECAI
Zhengyou Zhang, Olivier D. Faugeras
1990 A* conf
ICCV
Zhengyou Zhang, Olivier D. Faugeras
1990 J jnl
Image Vis. Comput.
Rachid Deriche, Olivier D. Faugeras
1989 J jnl
Comput. Vis. Graph. Image Process.
Amar Mitiche, Olivier D. Faugeras, Jake K. Aggarwal
1989 conf
DAGM-Symposium
Olivier D. Faugeras
1989 A conf
IROS
Olivier D. Faugeras
1989 J jnl
IEEE Trans. Robotics Autom.
Nicholas Ayache, Olivier D. Faugeras
1989 J jnl
IEEE Trans. Pattern Anal. Mach. Intell.
Thomas S. Huang, Olivier D. Faugeras
1988 Misc conf
MVA
Giorgio Toscani, Rachid Deriche, Olivier D. Faugeras
1988 A* conf
ICCV
Zhengyou Zhang, Olivier D. Faugeras, Nicholas Ayache
1988 J jnl
Int. J. Robotics Res.
Nicholas Ayache, Olivier D. Faugeras
1988 B conf
ICPR
Amar Mitiche, Olivier D. Faugeras, J. K. Aggarwal
1988 Misc conf
MVA
Olivier D. Faugeras, Rachid Deriche
1988 A* conf
CVPR
Yuncai Liu, Thomas S. Huang, Olivier D. Faugeras
1988 A* conf
ICCV
Elisabeth Le Bras-Mehlman, Michel Schmitt, Olivier D. Faugeras, Jean-Daniel Boissonnat
1988 J jnl
Int. J. Pattern Recognit. Artif. Intell.
Olivier D. Faugeras, Francis Lustman
1988 conf
Geometry and Robotics
Olivier D. Faugeras, Stephen J. Maybank
1988 A* conf
ICRA
Jean-Daniel Boissonnat, Olivier D. Faugeras, Elisabeth Le Bras-Mehlman
1988 A conf
IROS
Nicholas Ayache, Olivier D. Faugeras, Francis Lustman, Zhengyou Zhang
1987 J jnl
Comput. Vis. Graph. Image Process.
Jean Ponce, Olivier D. Faugeras
1987 A* conf
IJCAI
Olivier D. Faugeras
1987 A* conf
IJCAI
Nicholas Ayache, Olivier D. Faugeras
1987 A* conf
ICRA
Giorgio Toscani, Olivier D. Faugeras
1986 A* conf
ICRA
Olivier D. Faugeras, Nicholas Ayache, Bernard Faverjon
1986 J jnl
IEEE Trans. Pattern Anal. Mach. Intell.
Nicholas Ayache, Olivier D. Faugeras
1984 J jnl
Comput. Vis. Graph. Image Process.
Olivier D. Faugeras, Martial Hebert, Philippe Mussi, Jean-Daniel Boissonnat
1984 J jnl
IEEE Trans. Pattern Anal. Mach. Intell.
Bir Bhanu, Olivier D. Faugeras
1983 A* conf
IJCAI
Olivier D. Faugeras, Martial Hebert
1983 A* conf
IJCAI
Olivier D. Faugeras, Jean Ponce
1982 Misc conf
ICASSP
Noamen Keskes, A. Boulanouar, Olivier D. Faugeras
1982 J jnl
IEEE Trans. Pattern Anal. Mach. Intell.
Bir Bhanu, Olivier D. Faugeras
1982 Misc conf
ICASSP
Olivier D. Faugeras
1982 J jnl
IEEE Trans. Pattern Anal. Mach. Intell.
Bir Bhanu, Olivier D. Faugeras
1981 J jnl
IEEE Trans. Pattern Anal. Mach. Intell.
Olivier D. Faugeras, Marc Berthod
1981 J jnl
IEEE Trans. Pattern Anal. Mach. Intell.
Olivier D. Faugeras, Keith E. Price
1981 conf
DAGM-Symposium
Olivier D. Faugeras
1981 A* conf
IJCAI
Jean-Daniel Boissonnat, Olivier D. Faugeras
1980 A* conf
AAAI
Olivier D. Faugeras
1980 J jnl
IEEE Trans. Pattern Anal. Mach. Intell.
Olivier D. Faugeras, William K. Pratt
1980 J jnl
Pattern Recognit.
Olivier D. Faugeras, Marc Berthod
1980 conf
IFIP Congress
Marc Berthod, Olivier D. Faugeras
1979 Misc conf
ICASSP
Olivier D. Faugeras, Jean-François Abramatic
1978 J jnl
IEEE Trans. Syst. Man Cybern.
William K. Pratt, Olivier D. Faugeras, André Gagalowicz
tests/unit/test_decompile_modules.py
← Index tests/unit/test_decompile_modules.py python
"""
Unit tests for --decompile-modules flag.

Tests that:
1. CLI argument parsing and validation works correctly
2. decompile_modules is threaded through the entire call chain
3. BinaryNinjaDecompiler.analyze_binary() gates extractions per selected modules
4. DecompileBinja.prepare_export_data() filters DB tables per selected modules
"""
import os
import pytest
from unittest.mock import Mock, patch, MagicMock

pytestmark = [pytest.mark.unit]


# ============================================================================
# CLI Argument Parsing Tests
# ============================================================================

class TestDecompileModulesCliArgument:
    """Tests for --decompile-modules CLI argument parsing in start.py."""

    def _parse_args(self, args_list):
        """Helper to parse CLI args without running main()."""
        import argparse

        parser = argparse.ArgumentParser()
        input_group = parser.add_mutually_exclusive_group(required=True)
        input_group.add_argument("--path")
        input_group.add_argument("--s3", action="store_true")
        input_group.add_argument("--s3-solo", metavar="S3_KEY")
        input_group.add_argument("--nomad-job", action="store_true")
        input_group.add_argument("--date", metavar="YYYY-MM-DD")
        input_group.add_argument("--range", nargs=2)
        input_group.add_argument("--analyzed", action="store_true")

        parser.add_argument("--repo")
        parser.add_argument("--index_prefix", default="redb")
        parser.add_argument("-d", "--decompile", action="store_true")
        parser.add_argument("-m", "--modules", default="all")
        parser.add_argument("--decompile-modules", default="all")
        parser.add_argument("--force", action="store_true")
        parser.add_argument("--dry-run", action="store_true")

        return parser.parse_args(args_list)

    def test_decompile_modules_default_all(self):
        """--decompile-modules defaults to 'all'."""
        args = self._parse_args(["--path", "/tmp/test", "--repo", "test"])
        assert args.decompile_modules == "all"

    def test_decompile_modules_single(self):
        """--decompile-modules accepts a single module."""
        args = self._parse_args([
            "--path", "/tmp/test", "--repo", "test",
            "-d", "--decompile-modules", "strings"
        ])
        assert args.decompile_modules == "strings"

    def test_decompile_modules_comma_separated(self):
        """--decompile-modules accepts comma-separated modules."""
        args = self._parse_args([
            "--path", "/tmp/test", "--repo", "test",
            "-d", "--decompile-modules", "cfg,llil,strings"
        ])
        assert args.decompile_modules == "cfg,llil,strings"

    def test_decompile_modules_with_decompile_flag(self):
        """--decompile-modules works with -d flag."""
        args = self._parse_args([
            "--path", "/tmp/test", "--repo", "test",
            "-d", "--decompile-modules", "disassembly"
        ])
        assert args.decompile is True
        assert args.decompile_modules == "disassembly"


# ============================================================================
# Validation Tests
# ============================================================================

class TestDecompileModulesValidation:
    """Tests for --decompile-modules validation logic in start.py."""

    def test_valid_modules_set_parsing(self):
        """Valid comma-separated modules are parsed into a set."""
        VALID = {"all", "decompilation", "disassembly", "cfg", "llil", "strings"}
        raw = "cfg,llil,strings"
        modules_set = {m.strip() for m in raw.split(",")}
        invalid = modules_set - VALID
        assert invalid == set()
        assert modules_set == {"cfg", "llil", "strings"}

    def test_invalid_module_detected(self):
        """Invalid module names are detected."""
        VALID = {"all", "decompilation", "disassembly", "cfg", "llil", "strings"}
        raw = "cfg,invalid_module"
        modules_set = {m.strip() for m in raw.split(",")}
        invalid = modules_set - VALID
        assert invalid == {"invalid_module"}

    def test_all_keyword_as_set(self):
        """'all' is parsed into {'all'} set."""
        raw = "all"
        if raw == "all":
            modules_set = {"all"}
        assert modules_set == {"all"}

    def test_whitespace_handling(self):
        """Whitespace around module names is stripped."""
        raw = " cfg , llil , strings "
        modules_set = {m.strip() for m in raw.split(",")}
        assert modules_set == {"cfg", "llil", "strings"}


# ============================================================================
# Ingestor Parameter Storage Tests
# ============================================================================

class TestIngestorDecompileModules:
    """Tests that Ingestor stores decompile_modules correctly."""

    @patch('builtins.open', MagicMock())
    @patch('redb.ingestor.multiprocessing')
    @patch('redb.ingestor.datetime')
    def test_ingestor_stores_decompile_modules(self, mock_datetime, mock_mp):
        """Ingestor stores the decompile_modules set."""
        mock_mp.Manager.return_value.dict = Mock(side_effect=[{}, {}])
        mock_datetime.today.return_value.strftime.return_value = "20260222"

        from redb.ingestor import Ingestor
        ingestor = Ingestor(
            path="/tmp/test", repository="test", index_prefix="redb",
            decompile_modules={"cfg", "strings"}
        )
        assert ingestor.decompile_modules == {"cfg", "strings"}

    @patch('builtins.open', MagicMock())
    @patch('redb.ingestor.multiprocessing')
    @patch('redb.ingestor.datetime')
    def test_ingestor_defaults_to_all(self, mock_datetime, mock_mp):
        """Ingestor defaults decompile_modules to {'all'} when not provided."""
        mock_mp.Manager.return_value.dict = Mock(side_effect=[{}, {}])
        mock_datetime.today.return_value.strftime.return_value = "20260222"

        from redb.ingestor import Ingestor
        ingestor = Ingestor(
            path="/tmp/test", repository="test", index_prefix="redb"
        )
        assert ingestor.decompile_modules == {"all"}

    @patch('builtins.open', MagicMock())
    @patch('redb.ingestor.multiprocessing')
    @patch('redb.ingestor.datetime')
    def test_ingestor_none_defaults_to_all(self, mock_datetime, mock_mp):
        """Ingestor converts None decompile_modules to {'all'}."""
        mock_mp.Manager.return_value.dict = Mock(side_effect=[{}, {}])
        mock_datetime.today.return_value.strftime.return_value = "20260222"

        from redb.ingestor import Ingestor
        ingestor = Ingestor(
            path="/tmp/test", repository="test", index_prefix="redb",
            decompile_modules=None
        )
        assert ingestor.decompile_modules == {"all"}


# ============================================================================
# Function Chain Threading Tests
# ============================================================================

class TestDecompileModulesThreading:
    """Tests that decompile_modules is correctly threaded through the call chain."""

    @patch('redb.workers.process_binary_file')
    @patch('redb.workers.is_binary_file', return_value=True)
    def test_process_file_internal_passes_decompile_modules(self, mock_is_binary, mock_process):
        """_process_file_internal passes decompile_modules to process_binary_file."""
        from redb.ingestor import _process_file_internal

        mock_process.return_value = (Mock(), "pebin")
        logger = Mock()
        modules = {"cfg", "strings"}

        _process_file_internal(
            "/tmp/test.bin", False, "redb", logger,
            decompile_modules=modules
        )

        mock_process.assert_called_once()
        call_kwargs = mock_process.call_args[1]
        assert call_kwargs["decompile_modules"] == modules

    @patch('redb.workers.process_binary_file')
    @patch('redb.workers.is_binary_file', return_value=True)
    def test_process_file_internal_default_decompile_modules_none(self, mock_is_binary, mock_process):
        """_process_file_internal defaults decompile_modules to None."""
        from redb.ingestor import _process_file_internal

        mock_process.return_value = (Mock(), "pebin")
        logger = Mock()

        _process_file_internal("/tmp/test.bin", False, "redb", logger)

        call_kwargs = mock_process.call_args[1]
        assert call_kwargs["decompile_modules"] is None

    @patch('redb.workers._process_file_internal')
    def test_process_file_passes_decompile_modules(self, mock_internal):
        """process_file passes decompile_modules to _process_file_internal."""
        from redb.workers import process_file

        mock_internal.return_value = (Mock(), "pebin")
        modules = {"llil", "disassembly"}

        with patch('redb.workers.setup_direct_logger', return_value=Mock()):
            process_file(
                "/tmp/test.bin", False, "redb", "/tmp/log", 1, 1,
                decompile_modules=modules
            )

        mock_internal.assert_called_once()
        call_kwargs = mock_internal.call_args[1]
        assert call_kwargs["decompile_modules"] == modules

    @patch('redb.workers._process_file_internal')
    def test_process_s3_file_passes_decompile_modules(self, mock_internal):
        """process_s3_file passes decompile_modules to _process_file_internal."""
        from redb.workers import process_s3_file

        mock_internal.return_value = (Mock(), "pebin")
        modules = {"cfg"}

        with patch('redb.workers.setup_direct_logger', return_value=Mock()), \
             patch('redb.workers.download_s3_object', return_value="/tmp/downloaded.bin"):
            process_s3_file(
                "bucket", "key", "/tmp", False, "redb", "/tmp/log", 1, 1,
                decompile_modules=modules
            )

        mock_internal.assert_called_once()
        call_kwargs = mock_internal.call_args[1]
        assert call_kwargs["decompile_modules"] == modules

    @patch('redb.workers.process_binary_file')
    def test_process_zip_passes_decompile_modules(self, mock_process):
        """process_zip_file passes decompile_modules to process_binary_file."""
        from redb.workers import process_zip_file

        mock_process.return_value = (Mock(), "pebin")
        logger = Mock()
        modules = {"strings", "decompilation"}

        import tempfile
        import zipfile

        with tempfile.NamedTemporaryFile(suffix=".zip", delete=False) as tmp:
            tmp_path = tmp.name

        try:
            with zipfile.ZipFile(tmp_path, 'w') as zf:
                zf.writestr("test.bin", b"\x00" * 100)

            process_zip_file(
                tmp_path, False, "redb", logger, "all",
                decompile_modules=modules
            )

            mock_process.assert_called_once()
            call_kwargs = mock_process.call_args[1]
            assert call_kwargs["decompile_modules"] == modules
        finally:
            os.unlink(tmp_path)

    @patch('redb.workers.process_binary_file')
    def test_process_7zip_passes_decompile_modules(self, mock_process):
        """process_7zip_file passes decompile_modules to process_binary_file."""
        from redb.workers import process_7zip_file

        mock_process.return_value = (Mock(), "pebin")
        logger = Mock()
        modules = {"cfg", "llil"}

        with patch('redb.workers.py7zr') as mock_py7zr:
            mock_zf = Mock()
            mock_py7zr.SevenZipFile.return_value.__enter__ = Mock(return_value=mock_zf)
            mock_py7zr.SevenZipFile.return_value.__exit__ = Mock(return_value=False)

            def fake_extractall(path):
                dest = os.path.join(path, "test.bin")
                with open(dest, "wb") as f:
                    f.write(b"\x00" * 100)

            mock_zf.extractall = fake_extractall

            process_7zip_file(
                "/tmp/test.7z", False, "redb", logger, "all",
                decompile_modules=modules
            )

            if mock_process.called:
                call_kwargs = mock_process.call_args[1]
                assert call_kwargs["decompile_modules"] == modules

    def test_worker_unpacks_decompile_modules(self):
        """worker() correctly unpacks decompile_modules from args tuple."""
        from redb.workers import worker

        modules = {"strings", "cfg"}

        with patch('redb.workers.process_file') as mock_pf, \
             patch('redb.workers.signal'), \
             patch('redb.workers.gc'):
            mock_pf.return_value = (Mock(), "pebin")

            args = (
                "/tmp/test.bin",  # filepath
                True,             # decompile
                "redb",           # index_prefix
                "/tmp/log",       # log_file
                1,                # file_number
                10,               # total_files
                "all",            # selected_modules
                False,            # dry_run
                False,            # yara_scan
                False,            # with_yara
                False,            # force
                modules,          # decompile_modules
            )

            worker(args)

            mock_pf.assert_called_once()
            call_kwargs = mock_pf.call_args[1]
            assert call_kwargs["decompile_modules"] == modules


# ============================================================================
# BinaryNinjaDecompiler._module_selected() Tests
# ============================================================================

class TestModuleSelected:
    """Tests for BinaryNinjaDecompiler._module_selected() helper."""

    def _make_decompiler(self, decompile_modules):
        """Create a minimal BinaryNinjaDecompiler-like object for testing _module_selected."""

        class FakeDecompiler:
            def __init__(self, modules):
                self.decompile_modules = modules

            def _module_selected(self, module_name):
                return "all" in self.decompile_modules or module_name in self.decompile_modules

        return FakeDecompiler(decompile_modules)

    def test_all_selects_everything(self):
        d = self._make_decompiler({"all"})
        assert d._module_selected("strings") is True
        assert d._module_selected("decompilation") is True
        assert d._module_selected("disassembly") is True
        assert d._module_selected("cfg") is True
        assert d._module_selected("llil") is True

    def test_specific_module_selected(self):
        d = self._make_decompiler({"cfg", "strings"})
        assert d._module_selected("cfg") is True
        assert d._module_selected("strings") is True
        assert d._module_selected("decompilation") is False
        assert d._module_selected("disassembly") is False
        assert d._module_selected("llil") is False

    def test_empty_set_selects_nothing(self):
        d = self._make_decompiler(set())
        assert d._module_selected("strings") is False
        assert d._module_selected("decompilation") is False

    def test_single_module(self):
        d = self._make_decompiler({"llil"})
        assert d._module_selected("llil") is True
        assert d._module_selected("cfg") is False
        assert d._module_selected("strings") is False


# ============================================================================
# analyze_binary() Selective Execution Tests
# ============================================================================

class TestAnalyzeBinaryGating:
    """Tests that analyze_binary() gates extraction per decompile_modules.

    Uses a FakeDecompiler to test the gating logic without importing binaryninja.
    """

    def _make_analyze_binary_runner(self, decompile_modules):
        """Create a test harness that simulates the gating logic of analyze_binary()."""
        run_all = "all" in decompile_modules
        run_strings = run_all or "strings" in decompile_modules
        run_decompilation = run_all or "decompilation" in decompile_modules
        run_disassembly = run_all or "disassembly" in decompile_modules
        run_llil = run_all or "llil" in decompile_modules
        run_cfg = run_all or "cfg" in decompile_modules
        need_per_function = run_decompilation or run_disassembly or run_llil or run_cfg

        return {
            "run_strings": run_strings,
            "run_decompilation": run_decompilation,
            "run_disassembly": run_disassembly,
            "run_llil": run_llil,
            "run_cfg": run_cfg,
            "need_per_function": need_per_function,
        }

    def test_all_runs_everything(self):
        r = self._make_analyze_binary_runner({"all"})
        assert r["run_strings"] is True
        assert r["run_decompilation"] is True
        assert r["run_disassembly"] is True
        assert r["run_llil"] is True
        assert r["run_cfg"] is True
        assert r["need_per_function"] is True

    def test_strings_only_skips_per_function_loop(self):
        """Selecting only 'strings' should skip the per-function loop entirely."""
        r = self._make_analyze_binary_runner({"strings"})
        assert r["run_strings"] is True
        assert r["run_decompilation"] is False
        assert r["run_disassembly"] is False
        assert r["run_llil"] is False
        assert r["run_cfg"] is False
        assert r["need_per_function"] is False

    def test_cfg_only_runs_cfg_and_disassembly_in_loop(self):
        """Selecting only 'cfg' should run the per-function loop but skip decompilation/llil/strings."""
        r = self._make_analyze_binary_runner({"cfg"})
        assert r["run_strings"] is False
        assert r["run_decompilation"] is False
        assert r["run_disassembly"] is False  # Not selected (but backbone runs anyway in real code)
        assert r["run_llil"] is False
        assert r["run_cfg"] is True
        assert r["need_per_function"] is True

    def test_decompilation_only(self):
        r = self._make_analyze_binary_runner({"decompilation"})
        assert r["run_decompilation"] is True
        assert r["run_strings"] is False
        assert r["run_cfg"] is False
        assert r["run_llil"] is False
        assert r["need_per_function"] is True

    def test_multiple_modules(self):
        r = self._make_analyze_binary_runner({"cfg", "llil", "strings"})
        assert r["run_strings"] is True
        assert r["run_cfg"] is True
        assert r["run_llil"] is True
        assert r["run_decompilation"] is False
        assert r["run_disassembly"] is False
        assert r["need_per_function"] is True

    def test_disassembly_only(self):
        r = self._make_analyze_binary_runner({"disassembly"})
        assert r["run_disassembly"] is True
        assert r["run_decompilation"] is False
        assert r["run_strings"] is False
        assert r["run_cfg"] is False
        assert r["run_llil"] is False
        assert r["need_per_function"] is True


# ============================================================================
# prepare_export_data() Table Filtering Tests
# ============================================================================

class TestPrepareExportDataFiltering:
    """Tests that prepare_export_data() only includes tables for selected modules.

    Simulates the filtering logic from DecompileBinja.prepare_export_data()
    without importing binaryninja.
    """

    def _get_export_tables(self, decompile_modules):
        """Simulate the table selection logic from prepare_export_data()."""
        run_all = "all" in decompile_modules
        run_decompilation = run_all or "decompilation" in decompile_modules
        run_disassembly = run_all or "disassembly" in decompile_modules
        run_llil = run_all or "llil" in decompile_modules
        run_cfg = run_all or "cfg" in decompile_modules
        run_strings = run_all or "strings" in decompile_modules

        tables = set()
        if run_decompilation:
            tables.update(["decompiled_content", "decompiled_refs"])
        if run_disassembly:
            tables.update(["disassembled_content", "disassembled_refs", "function_similarity_metrics"])
        if run_llil:
            tables.update(["llil_content", "llil_refs"])
        if run_cfg:
            tables.update(["cfg_functions"])
        if run_strings:
            tables.update(["strings_raw"])
        # Errors always included if per-function loop ran
        if run_decompilation or run_disassembly or run_llil or run_cfg:
            tables.add("function_analysis_errors")

        return tables

    def test_all_includes_all_tables(self):
        tables = self._get_export_tables({"all"})
        assert "decompiled_content" in tables
        assert "decompiled_refs" in tables
        assert "disassembled_content" in tables
        assert "disassembled_refs" in tables
        assert "function_similarity_metrics" in tables
        assert "llil_content" in tables
        assert "llil_refs" in tables
        assert "cfg_functions" in tables
        assert "strings_raw" in tables
        assert "function_analysis_errors" in tables

    def test_strings_only_includes_only_strings(self):
        tables = self._get_export_tables({"strings"})
        assert tables == {"strings_raw"}

    def test_cfg_only_includes_cfg_and_errors(self):
        tables = self._get_export_tables({"cfg"})
        assert tables == {"cfg_functions", "function_analysis_errors"}

    def test_decompilation_only_tables(self):
        tables = self._get_export_tables({"decompilation"})
        assert tables == {"decompiled_content", "decompiled_refs", "function_analysis_errors"}

    def test_disassembly_only_tables(self):
        tables = self._get_export_tables({"disassembly"})
        assert tables == {
            "disassembled_content", "disassembled_refs",
            "function_similarity_metrics", "function_analysis_errors"
        }

    def test_llil_only_tables(self):
        tables = self._get_export_tables({"llil"})
        assert tables == {"llil_content", "llil_refs", "function_analysis_errors"}

    def test_combined_cfg_strings_tables(self):
        tables = self._get_export_tables({"cfg", "strings"})
        assert tables == {"cfg_functions", "strings_raw", "function_analysis_errors"}

    def test_no_errors_table_for_strings_only(self):
        """Error table should NOT be included when only strings is selected (no per-function loop)."""
        tables = self._get_export_tables({"strings"})
        assert "function_analysis_errors" not in tables


# ============================================================================
# DecompileBinja Parameter Acceptance Tests
# ============================================================================

class TestDecompileBinjaDecompileModules:
    """Tests that DecompileBinja accepts and stores decompile_modules.

    Since DecompileBinja imports binaryninja (which requires blake3 etc),
    we test the default/init logic in isolation.
    """

    def test_default_decompile_modules(self):
        """decompile_modules defaults to {'all'} when None."""
        modules = None
        result = modules or {"all"}
        assert result == {"all"}

    def test_explicit_decompile_modules(self):
        """Explicit decompile_modules is preserved."""
        modules = {"cfg", "llil"}
        result = modules or {"all"}
        assert result == {"cfg", "llil"}

    def test_empty_set_becomes_all(self):
        """Empty set is falsy and defaults to {'all'}."""
        modules = set()
        result = modules or {"all"}
        assert result == {"all"}


# ============================================================================
# Integration-Style: analyze_binary() Per-Function Gating (Logic Tests)
# ============================================================================

class TestAnalyzeBinaryPerFunctionGating:
    """Test that the per-function extraction gating produces correct results structures.

    These test the logic patterns from analyze_binary() using mock data,
    verifying that:
    - Unselected modules produce empty result lists
    - Selected modules produce populated result lists
    - Disassembly always runs when any per-function module is selected
    - Cross-linkage None handling works when counterparts are skipped
    """

    def _simulate_per_function_results(self, decompile_modules, num_functions=3):
        """Simulate the per-function extraction loop from analyze_binary().

        Returns results dict with the same structure as analyze_binary().
        """
        run_all = "all" in decompile_modules
        run_strings = run_all or "strings" in decompile_modules
        run_decompilation = run_all or "decompilation" in decompile_modules
        run_disassembly = run_all or "disassembly" in decompile_modules
        run_llil = run_all or "llil" in decompile_modules
        run_cfg = run_all or "cfg" in decompile_modules
        need_per_function = run_decompilation or run_disassembly or run_llil or run_cfg

        results = {
            "decompiled": [],
            "disassembled": [],
            "cfg": [],
            "llil": [],
            "errors": [],
            "strings": [],
        }

        if run_strings:
            results["strings"] = [{"string": f"str_{i}"} for i in range(5)]

        if not need_per_function:
            return results

        for i in range(num_functions):
            hlil_json = {"decompiled_function_hash": f"hlil_{i}"} if run_decompilation else None
            disass_json = {"disassembled_function_hash": f"disass_{i}", "disassembled_function_name": f"func_{i}", "disassembled_function_address": i * 100}  # always
            cfg_json = {"function_address": i * 100, "cyclomatic_complexity": 5} if run_cfg else None
            lowlevel_json = {"sha256_llil": f"llil_{i}", "tlsh_llil": "def", "minhash": [1, 2]} if run_llil else None

            # Simulate cross-linkage (same pattern as real code)
            if hlil_json and disass_json:
                hlil_json["disassembled_function_hash"] = disass_json["disassembled_function_hash"]
                disass_json["decompiled_function_hash"] = hlil_json["decompiled_function_hash"]
                results["decompiled"].append(hlil_json)
                if run_disassembly:
                    results["disassembled"].append(disass_json)
            elif hlil_json:
                hlil_json["disassembled_function_hash"] = None
                results["decompiled"].append(hlil_json)
            elif disass_json:
                disass_json["decompiled_function_hash"] = None
                if run_disassembly:
                    results["disassembled"].append(disass_json)

            if lowlevel_json and disass_json:
                lowlevel_json["disassembled_function_hash"] = disass_json["disassembled_function_hash"]
                disass_json["tlsh_llil"] = lowlevel_json.get("tlsh_llil")
                disass_json["minhash"] = lowlevel_json.get("minhash")
            elif lowlevel_json:
                lowlevel_json["disassembled_function_hash"] = None
            elif disass_json:
                disass_json["tlsh_llil"] = None
                disass_json["minhash"] = None

            if lowlevel_json:
                results["llil"].append(lowlevel_json)

            if cfg_json and disass_json:
                cfg_json["disassembled_function_hash"] = disass_json["disassembled_function_hash"]
                disass_json["cyclomatic_complexity"] = cfg_json.get("cyclomatic_complexity")
                results["cfg"].append(cfg_json)
            elif cfg_json:
                cfg_json["disassembled_function_hash"] = None
                results["cfg"].append(cfg_json)

            if disass_json and "cyclomatic_complexity" not in disass_json:
                disass_json["cyclomatic_complexity"] = None

        return results

    def test_all_populates_everything(self):
        r = self._simulate_per_function_results({"all"}, 3)
        assert len(r["decompiled"]) == 3
        assert len(r["disassembled"]) == 3
        assert len(r["cfg"]) == 3
        assert len(r["llil"]) == 3
        assert len(r["strings"]) == 5

    def test_strings_only_populates_strings(self):
        r = self._simulate_per_function_results({"strings"}, 3)
        assert len(r["strings"]) == 5
        assert len(r["decompiled"]) == 0
        assert len(r["disassembled"]) == 0
        assert len(r["cfg"]) == 0
        assert len(r["llil"]) == 0

    def test_cfg_only_populates_cfg_no_disassembly_export(self):
        """With only 'cfg' selected, cfg list is populated but disassembled is empty
        (disassembly runs internally but not exported)."""
        r = self._simulate_per_function_results({"cfg"}, 3)
        assert len(r["cfg"]) == 3
        assert len(r["disassembled"]) == 0  # Not selected for export
        assert len(r["decompiled"]) == 0
        assert len(r["llil"]) == 0
        assert len(r["strings"]) == 0

    def test_cfg_has_disassembled_function_hash_linkage(self):
        """CFG entries get disassembled_function_hash from backbone disassembly."""
        r = self._simulate_per_function_results({"cfg"}, 2)
        for cfg in r["cfg"]:
            assert cfg["disassembled_function_hash"] is not None
            assert cfg["disassembled_function_hash"].startswith("disass_")

    def test_decompilation_only_has_none_disassembly_linkage_when_no_disasm(self):
        """When only decompilation selected, disassembled still runs (backbone)
        so linkage is still available."""
        r = self._simulate_per_function_results({"decompilation"}, 2)
        assert len(r["decompiled"]) == 2
        # Disassembly runs as backbone, so linkage hash is present
        for d in r["decompiled"]:
            assert d["disassembled_function_hash"] is not None

    def test_disassembly_without_llil_has_none_fuzzy_hashes(self):
        """When LLIL is not selected, disassembly records have None fuzzy hashes."""
        r = self._simulate_per_function_results({"disassembly"}, 2)
        for d in r["disassembled"]:
            assert d["tlsh_llil"] is None
            assert d["minhash"] is None

    def test_disassembly_without_decompilation_has_none_decompiled_hash(self):
        """When decompilation is not selected, disassembly has None decompiled_function_hash."""
        r = self._simulate_per_function_results({"disassembly"}, 2)
        for d in r["disassembled"]:
            assert d["decompiled_function_hash"] is None

    def test_disassembly_without_cfg_has_none_cyclomatic(self):
        """When CFG is not selected, disassembly has None cyclomatic_complexity."""
        r = self._simulate_per_function_results({"disassembly"}, 2)
        for d in r["disassembled"]:
            assert d["cyclomatic_complexity"] is None

    def test_all_linkage_populated(self):
        """When all modules run, all cross-linkage fields are populated."""
        r = self._simulate_per_function_results({"all"}, 2)
        for d in r["disassembled"]:
            assert d["decompiled_function_hash"] is not None
            assert d["tlsh_llil"] is not None
            assert d["cyclomatic_complexity"] is not None
        for h in r["decompiled"]:
            assert h["disassembled_function_hash"] is not None
        for c in r["cfg"]:
            assert c["disassembled_function_hash"] is not None
        for ll in r["llil"]:
            assert ll["disassembled_function_hash"] is not None


# ============================================================================
# IOC Extraction Gating by decompile_modules
# ============================================================================

class TestIOCExtractionGating:
    """Tests that IOC extraction only runs when its data sources are present.

    IOCExtractorFromResults consumes in-memory analysis_results['strings']
    and analysis_results['decompiled']. It runs automatically (implicit
    dependency) when 'decompilation' or 'strings' modules produce data.
    It is NOT a standalone selectable module.
    """

    @staticmethod
    def _is_ioc_relevant(decompile_modules):
        """Replicate the gating logic from workers.py."""
        return (
            not decompile_modules
            or "all" in decompile_modules
            or "decompilation" in decompile_modules
            or "strings" in decompile_modules
        )

    def test_all_triggers_ioc(self):
        assert self._is_ioc_relevant({"all"}) is True

    def test_decompilation_triggers_ioc(self):
        assert self._is_ioc_relevant({"decompilation"}) is True

    def test_strings_triggers_ioc(self):
        assert self._is_ioc_relevant({"strings"}) is True

    def test_decompilation_and_strings_triggers_ioc(self):
        assert self._is_ioc_relevant({"decompilation", "strings"}) is True

    def test_cfg_only_skips_ioc(self):
        assert self._is_ioc_relevant({"cfg"}) is False

    def test_disassembly_only_skips_ioc(self):
        assert self._is_ioc_relevant({"disassembly"}) is False

    def test_llil_only_skips_ioc(self):
        assert self._is_ioc_relevant({"llil"}) is False

    def test_cfg_and_disassembly_skips_ioc(self):
        assert self._is_ioc_relevant({"cfg", "disassembly"}) is False

    def test_cfg_and_llil_skips_ioc(self):
        assert self._is_ioc_relevant({"cfg", "llil"}) is False

    def test_cfg_and_decompilation_triggers_ioc(self):
        """If decompilation is included alongside cfg, IOC should run."""
        assert self._is_ioc_relevant({"cfg", "decompilation"}) is True

    def test_disassembly_and_strings_triggers_ioc(self):
        """If strings is included alongside disassembly, IOC should run."""
        assert self._is_ioc_relevant({"disassembly", "strings"}) is True

    def test_none_modules_triggers_ioc(self):
        """When decompile_modules is None (default), IOC should run."""
        assert self._is_ioc_relevant(None) is True

    def test_empty_set_triggers_ioc(self):
        """When decompile_modules is empty set, IOC should run (falsy)."""
        assert self._is_ioc_relevant(set()) is True