Viktor Larsson

130 papers A* 41A 2B 1C 1Misc 2Journal 54Unranked 27
YearRankTypeTitle / Venue / Authors
2025 A* conf
CVPR
Jonathan Astermark, Anders Heyden, Viktor Larsson
2025 J jnl
CoRR
Jonathan Astermark, Anders Heyden, Viktor Larsson
2025 J jnl
CoRR
Johannes L. Schönberger, Viktor Larsson, Marc Pollefeys
2025 conf
ICCVW
Aidyn Ubingazhibov, Rémi Pautrat, Iago Suárez, Shaohui Liu, Marc Pollefeys, Viktor Larsson
2025 J jnl
CoRR
Aidyn Ubingazhibov, Rémi Pautrat, Iago Suárez, Shaohui Liu, Marc Pollefeys, Viktor Larsson
2025 conf
ICCVW
Gustav Hanning, Kalle Åström, Viktor Larsson
2025 J jnl
CoRR
Gustav Hanning, Kalle Åström, Viktor Larsson
2025 J jnl
IEEE Trans. Pattern Anal. Mach. Intell.
Paul-Edouard Sarlin, Philipp Lindenberger, Viktor Larsson, Marc Pollefeys
2025 A* conf
CVPR
Yifan Yu, Shaohui Liu, Rémi Pautrat, Marc Pollefeys, Viktor Larsson
2025 J jnl
CoRR
Yifan Yu, Shaohui Liu, Rémi Pautrat, Marc Pollefeys, Viktor Larsson
2025 J jnl
CoRR
Johan Edstedt, David Nordström, Yushan Zhang, Georg Bökman, Jonathan Astermark, Viktor Larsson, Anders Heyden, Fredrik Kahl, Mårten Wadenbäck, Michael Felsberg
2025 J jnl
IEEE Trans. Signal Process.
Martin Larsson, Viktor Larsson, Kalle Åström, Magnus Oskarsson
2025 conf
ICCVW
Olivier Moliner, Viktor Larsson, Kalle Åström
2025 J jnl
CoRR
Olivier Moliner, Viktor Larsson, Kalle Åström
2025 A* conf
CVPR
Yihan Wang, Linfei Pan, Marc Pollefeys, Viktor Larsson
2025 J jnl
CoRR
Gustav Hanning, Gabrielle Flood, Viktor Larsson
2025 conf
SCIA (1)
Gustav Hanning, Gabrielle Flood, Viktor Larsson
2024 conf
3DV
Jonathan Astermark, Yaqing Ding, Viktor Larsson, Anders Heyden
2024 conf
EUSIPCO
Malte Larsson, Viktor Larsson, Carl Olsson, Magnus Oskarsson
2024 conf
3DV
Zihan Zhu, Songyou Peng, Viktor Larsson, Zhaopeng Cui, Martin R. Oswald, Andreas Geiger, Marc Pollefeys
2024 A* conf
CVPR
Yaqing Ding, Jonathan Astermark, Magnus Oskarsson, Viktor Larsson
2024 A* conf
CVPR
Felix Rydell, Angélica Torres, Viktor Larsson
2024 J jnl
CoRR
Felix Rydell, Angélica Torres, Viktor Larsson
2024 conf
ECCV (36)
Shaohui Liu, Yidan Gao, Tianyi Zhang, Rémi Pautrat, Johannes L. Schönberger, Viktor Larsson, Marc Pollefeys
2024 J jnl
CoRR
Shaohui Liu, Yidan Gao, Tianyi Zhang, Rémi Pautrat, Johannes L. Schönberger, Viktor Larsson, Marc Pollefeys
2023 A* conf
CVPR
Shaohui Liu, Yifan Yu, Rémi Pautrat, Marc Pollefeys, Viktor Larsson
2023 J jnl
CoRR
Shaohui Liu, Yifan Yu, Rémi Pautrat, Marc Pollefeys, Viktor Larsson
2023 J jnl
IEEE Trans. Veh. Technol.
Ahad Hamednia, Victor Hanson, Jiaming Zhao, Nikolce Murgovski, Jimmy Forsman, Mitra Pourabdollah, Viktor Larsson, Jonas Fredriksson
2023 conf
ISMAR-Adjunct
Max Bergfelt, Viktor Larsson, Hideo Saito, Shohei Mori
2023 J jnl
CoRR
Luca Cavalli, Daniel Barath, Marc Pollefeys, Viktor Larsson
2023 A* conf
CVPR
Rémi Pautrat, Daniel Barath, Viktor Larsson, Martin R. Oswald, Marc Pollefeys
2023 conf
EUSIPCO
Malte Larsson, Viktor Larsson, Magnus Oskarsson
2023 A* conf
CVPR
Petr Hruby, Viktor Korotynskiy, Timothy Duff, Luke Oeding, Marc Pollefeys, Tomás Pajdla, Viktor Larsson
2023 A* conf
ICCV
Rémi Pautrat, Iago Suárez, Yifan Yu, Marc Pollefeys, Viktor Larsson
2023 J jnl
CoRR
Rémi Pautrat, Iago Suárez, Yifan Yu, Marc Pollefeys, Viktor Larsson
2023 A* conf
ICRA
Hao Dong, Xieyuanli Chen, Mihai Dusmanu, Viktor Larsson, Marc Pollefeys, Cyrill Stachniss
2023 J jnl
IEEE Trans. Signal Process.
Dalia El Badawy, Viktor Larsson, Marc Pollefeys, Ivan Dokmanic
2023 A* conf
ICCV
Yaqing Ding, Chiang-Heng Chien, Viktor Larsson, Karl Åström, Benjamin B. Kimia
2023 J jnl
CoRR
Zihan Zhu, Songyou Peng, Viktor Larsson, Zhaopeng Cui, Martin R. Oswald, Andreas Geiger, Marc Pollefeys
2023 J jnl
IEEE Trans. Veh. Technol.
Ahad Hamednia, Nikolce Murgovski, Jonas Fredriksson, Jimmy Forsman, Mitra Pourabdollah, Viktor Larsson
2023 A conf
WACV
Snehal Bhayani, Torsten Sattler, Viktor Larsson, Janne Heikkilä, Zuzana Kukelova
2023 A* conf
ICCV
Linfei Pan, Johannes L. Schönberger, Viktor Larsson, Marc Pollefeys
2023 A* conf
CVPR
Ganlin Zhang, Viktor Larsson, Daniel Barath
2023 J jnl
CoRR
Ganlin Zhang, Viktor Larsson, Daniel Barath
2023 A* conf
CVPR
Yaqing Ding, Jian Yang, Viktor Larsson, Carl Olsson, Kalle Åström
2023 A* conf
ICCV
Mikhail Terekhov, Viktor Larsson
2023 conf
SCIA (2)
Erik Tegler, Johanna Engman, David Gillsjö, Gabrielle Flood, Viktor Larsson, Magnus Oskarsson, Kalle Åström
2022 A* conf
CVPR
Linfei Pan, Marc Pollefeys, Viktor Larsson
2022 J jnl
CoRR
Ahad Hamednia, Jimmy Forsman, Nikolce Murgovski, Viktor Larsson, Jonas Fredriksson
2022 J jnl
CoRR
Rémi Pautrat, Daniel Barath, Viktor Larsson, Martin R. Oswald, Marc Pollefeys
2022 conf
ECCV (7)
Paul-Edouard Sarlin, Mihai Dusmanu, Johannes L. Schönberger, Pablo Speciale, Lukas Gruber, Viktor Larsson, Ondrej Miksik, Marc Pollefeys
2022 J jnl
CoRR
Paul-Edouard Sarlin, Mihai Dusmanu, Johannes L. Schönberger, Pablo Speciale, Lukas Gruber, Viktor Larsson, Ondrej Miksik, Marc Pollefeys
2022 J jnl
CoRR
Hao Dong, Xieyuanli Chen, Mihai Dusmanu, Viktor Larsson, Marc Pollefeys, Cyrill Stachniss
2022 A* conf
CVPR
Zihan Zhu, Songyou Peng, Viktor Larsson, Weiwei Xu, Hujun Bao, Zhaopeng Cui, Martin R. Oswald, Marc Pollefeys
2022 J jnl
J. Math. Imaging Vis.
Lucas Brynte, Viktor Larsson, José Pedro Iglesias, Carl Olsson, Fredrik Kahl
2022 J jnl
CoRR
Ahad Hamednia, Victor Hanson, Jiaming Zhao, Nikolce Murgovski, Jimmy Forsman, Mitra Pourabdollah, Viktor Larsson, Jonas Fredriksson
2022 J jnl
CoRR
Ahad Hamednia, Nikolce Murgovski, Jonas Fredriksson, Jimmy Forsman, Mitra Pourabdollah, Viktor Larsson
2022 J jnl
CoRR
Snehal Bhayani, Viktor Larsson, Torsten Sattler, Janne Heikkilä, Zuzana Kukelova
2022 A* conf
CVPR
Marcel Geppert, Viktor Larsson, Johannes L. Schönberger, Marc Pollefeys
2021 conf
ISCMI
Omar Alfakir, Viktor Larsson, Fernando Alonso-Fernandez
2021 J jnl
CoRR
Omar Alfakir, Viktor Larsson, Fernando Alonso-Fernandez
2021 A* conf
CVPR
Carl Olsson, Viktor Larsson, Fredrik Kahl
2021 A* conf
CVPR
Paul-Edouard Sarlin, Ajaykumar Unagar, Måns Larsson, Hugo Germain, Carl Toft, Viktor Larsson, Marc Pollefeys, Vincent Lepetit, Lars Hammarstrand, Fredrik Kahl, Torsten Sattler
2021 J jnl
CoRR
Paul-Edouard Sarlin, Ajaykumar Unagar, Måns Larsson, Hugo Germain, Carl Toft, Viktor Larsson, Marc Pollefeys, Vincent Lepetit, Lars Hammarstrand, Fredrik Kahl, Torsten Sattler
2021 conf
3DV
Daniel Barath, Yaqing Ding, Zuzana Kukelova, Viktor Larsson
2021 A* conf
ICCV
Peidong Liu, Xingxing Zuo, Viktor Larsson, Marc Pollefeys
2021 J jnl
CoRR
Peidong Liu, Xingxing Zuo, Viktor Larsson, Marc Pollefeys
2021 J jnl
IEEE Trans. Pattern Anal. Mach. Intell.
James Pritts, Zuzana Kukelova, Viktor Larsson, Yaroslava Lochman, Ondrej Chum
2021 J jnl
CoRR
Zihan Zhu, Songyou Peng, Viktor Larsson, Weiwei Xu, Hujun Bao, Zhaopeng Cui, Martin R. Oswald, Marc Pollefeys
2021 J jnl
CoRR
Lucas Brynte, Viktor Larsson, José Pedro Iglesias, Carl Olsson, Fredrik Kahl
2021 A* conf
ICCV
Viktor Larsson, Marc Pollefeys, Magnus Oskarsson
2021 A* conf
ICCV
Philipp Lindenberger, Paul-Edouard Sarlin, Viktor Larsson, Marc Pollefeys
2021 J jnl
CoRR
Philipp Lindenberger, Paul-Edouard Sarlin, Viktor Larsson, Marc Pollefeys
2021 A* conf
CVPR
Marcel Geppert, Viktor Larsson, Pablo Speciale, Johannes L. Schönberger, Marc Pollefeys
2021 A* conf
CVPR
Rémi Pautrat, Juan-Ting Lin, Viktor Larsson, Martin R. Oswald, Marc Pollefeys
2021 J jnl
CoRR
Rémi Pautrat, Juan-Ting Lin, Viktor Larsson, Martin R. Oswald, Marc Pollefeys
2020 J jnl
CoRR
Luca Cavalli, Viktor Larsson, Martin Ralf Oswald, Torsten Sattler, Marc Pollefeys
2020 conf
ECCV (5)
Viktor Larsson, Nicolas Zobernig, Kasim Taskin, Marc Pollefeys
2020 A* conf
CVPR
Peidong Liu, Zhaopeng Cui, Viktor Larsson, Marc Pollefeys
2020 A* conf
CVPR
Cenek Albl, Zuzana Kukelova, Viktor Larsson, Michal Polic, Tomás Pajdla, Konrad Schindler
2020 J jnl
CoRR
Cenek Albl, Zuzana Kukelova, Viktor Larsson, Tomás Pajdla, Konrad Schindler
2020 J jnl
CoRR
Bo Li, Viktor Larsson
2020 conf
ECCV (19)
Luca Cavalli, Viktor Larsson, Martin Ralf Oswald, Torsten Sattler, Marc Pollefeys
2020 conf
ECCV (16)
Yukai Lin, Viktor Larsson, Marcel Geppert, Zuzana Kukelova, Marc Pollefeys, Torsten Sattler
2020 J jnl
CoRR
Yukai Lin, Viktor Larsson, Marcel Geppert, Zuzana Kukelova, Marc Pollefeys, Torsten Sattler
2020 J jnl
Int. J. Comput. Vis.
James Pritts, Zuzana Kukelova, Viktor Larsson, Yaroslava Lochman, Ondrej Chum
2020 conf
ECCV (2)
Rémi Pautrat, Viktor Larsson, Martin R. Oswald, Marc Pollefeys
2020 J jnl
CoRR
Rémi Pautrat, Viktor Larsson, Martin R. Oswald, Marc Pollefeys
2020 conf
ECCV (1)
Marcel Geppert, Viktor Larsson, Pablo Speciale, Johannes L. Schönberger, Marc Pollefeys
2020 J jnl
IEEE Trans. Pattern Anal. Mach. Intell.
Cenek Albl, Zuzana Kukelova, Viktor Larsson, Tomás Pajdla
2020 A* conf
CVPR
Thomas Schöps, Viktor Larsson, Marc Pollefeys, Torsten Sattler
2019 J jnl
CoRR
James Pritts, Zuzana Kukelova, Viktor Larsson, Yaroslava Lochman, Ondrej Chum
2019 J jnl
CoRR
James Pritts, Zuzana Kukelova, Viktor Larsson, Yaroslava Lochman, Ondrej Chum
2019 Misc conf
ICASSP
Martin Larsson, Viktor Larsson, Kalle Åström, Magnus Oskarsson
2019 A* conf
ICCV
Zhaopeng Cui, Viktor Larsson, Marc Pollefeys
2019 A* conf
CVPR
Zuzana Kukelova, Viktor Larsson
2019 A* conf
ICCV
Viktor Larsson, Torsten Sattler, Zuzana Kukelova, Marc Pollefeys
2019 Misc conf
ICASSP
Kenneth Batstone, Gabrielle Flood, Thejasvi Beleyur, Viktor Larsson, Holger R. Goerlitz, Magnus Oskarsson, Kalle Åström
2019 J jnl
Pattern Recognit. Lett.
Jennifer Alvén, Fredrik Kahl, Matilda Landgren, Viktor Larsson, Johannes Ulén, Olof Enqvist
2019 J jnl
CoRR
Thomas Schöps, Viktor Larsson, Marc Pollefeys, Torsten Sattler
2018 A* conf
CVPR
Viktor Larsson, Magnus Oskarsson, Kalle Åström, Alge Wallis, Zuzana Kukelova, Tomás Pajdla
2018 J jnl
CoRR
Viktor Larsson, Magnus Oskarsson, Kalle Åström, Alge Wallis, Zuzana Kukelova, Tomás Pajdla
2018 A* conf
CVPR
Viktor Larsson, Zuzana Kukelova, Yinqiang Zheng
2018
Viktor Larsson
2018 A* conf
CVPR
James Pritts, Zuzana Kukelova, Viktor Larsson, Ondrej Chum
2018 conf
ACCV (5)
James Pritts, Zuzana Kukelova, Viktor Larsson, Ondrej Chum
2018 J jnl
CoRR
James Pritts, Zuzana Kukelova, Viktor Larsson, Ondrej Chum
2017 A* conf
CVPR
Viktor Larsson, Carl Olsson
2017 A* conf
CVPR
Viktor Larsson, Kalle Åström, Magnus Oskarsson
2017 A* conf
ICCV
Viktor Larsson, Zuzana Kukelova, Yinqiang Zheng
2017 ch.
Cloud-Based Benchmarking of Medical Image Analysis
Frida Fejne, Matilda Landgren, Jennifer Alvén, Johannes Ulén, Johan Fredriksson, Viktor Larsson, Olof Enqvist, Fredrik Kahl
2017 A* conf
ICCV
Carl Olsson, Marcus Carlsson, Fredrik Andersson, Viktor Larsson
2017 A* conf
ICCV
Viktor Larsson, Kalle Åström, Magnus Oskarsson
2017 J jnl
CoRR
James Pritts, Zuzana Kukelova, Viktor Larsson, Ondrej Chum
2016 J jnl
Int. J. Comput. Vis.
Viktor Larsson, Carl Olsson
2016 J jnl
Image Vis. Comput.
Johan Fredriksson, Viktor Larsson, Carl Olsson, Olof Enqvist, Fredrik Kahl
2016 A* conf
CVPR
Johan Fredriksson, Viktor Larsson, Carl Olsson, Fredrik Kahl
2016 A conf
BMVC
Viktor Larsson, Johan Fredriksson, Carl Toft, Fredrik Kahl
2016 B conf
ICPR
Jennifer Alvén, Fredrik Kahl, Matilda Landgren, Viktor Larsson, Johannes Ulén
2016 conf
ECCV (3)
Viktor Larsson, Kalle Åström
2015 conf
ICCV Workshops
Viktor Larsson, Carl Olsson, Fredrik Kahl
2015 J jnl
IEEE Trans. Veh. Technol.
Viktor Larsson, Lars Johannesson, Bo Egardt
2015 conf
VISCERAL Challenge@ISBI
Fredrik Kahl, Jennifer Alvén, Olof Enqvist, Frida Fejne, Johannes Ulén, Johan Fredriksson, Matilda Landgren, Viktor Larsson
2015 A* conf
CVPR
Johan Fredriksson, Viktor Larsson, Carl Olsson
2015 conf
AMCIS
Filip Gårdelöv, Viktor Larsson, Dick Stenmark
2014 J jnl
IEEE Trans. Intell. Transp. Syst.
Viktor Larsson, Lars Johannesson Mårdh, Bo Egardt, Sten Karlsson
2014 conf
EMMCVPR
Viktor Larsson, Carl Olsson
2014 conf
ECC
Viktor Larsson, Lars Johannesson, Bo Egardt
2014 conf
ECCV (3)
Viktor Larsson, Carl Olsson, Erik Bylow, Fredrik Kahl
2012 C conf
ACC
Viktor Larsson, Lars Johannesson, Bo Egardt, Anders Lasson
tests/unit/test_decompile_medium_level.py
← Index tests/unit/test_decompile_medium_level.py python
# tests/unit/test_decompile_medium_level.py
"""Unit tests (mocked BN) for bninja/analysis/medium_level.py
   and bninja/analysis/medium_level_normalization.py."""
# tests/unit/test_decompile_medium_level.py
import sys
from unittest.mock import MagicMock, patch

# Installa gli stubs BN
from tests.unit.conftest_binja_stubs import install_binja_stubs
install_binja_stubs()

# ── Definisci MockMLILInstruction PRIMA di importare il modulo ──
class MockMLILInstruction:
    def __init__(self, operation, address=0, operands=None):
        self.operation = operation
        self.address = address
        self.operands = operands or []

# ── Patcha il modulo BN in modo che isinstance() funzioni ──
sys.modules["binaryninja"].MediumLevelILInstruction = MockMLILInstruction
sys.modules["binaryninja"].SSAVariable = type("SSAVariable", (), {})
sys.modules["binaryninja"].Variable = type("Variable", (), {})
sys.modules["binaryninja"].ILIntrinsic = type("ILIntrinsic", (), {})

# Ora importa il modulo — vede già i tipi corretti
from redb.extractors.decompiler.bninja.analysis.medium_level_normalization import (
    MediumLevelNormalization,
)	

class MockMLILFunction:
    def __init__(self, instructions):
        self._instructions = instructions

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

    @property
    def basic_blocks(self):
        # one block containing all instructions, good enough for MinHasher
        block = MagicMock()
        block.__iter__ = lambda self_: iter([])  # not used by MediumLevelAnalysis
        return [block]


class MockFunction:
    def __init__(self, name="func", start=0x1000, mlil=None):
        self.name = name
        self.start = start
        self.mlil = mlil



class TestMediumLevelNormalization:
    def setup_method(self):
        from redb.extractors.decompiler.bninja.analysis.medium_level_normalization import (
            MediumLevelNormalization,
        )
        self.norm = MediumLevelNormalization()

    def test_normalize_skeleton_single_instruction(self):
        il = MockMLILInstruction(operation=42, operands=[])
        result = self.norm.normalize_instruction_all_levels(il)
        assert result == [42]

    def test_normalize_skeleton_nested(self):
        inner = MockMLILInstruction(operation=7, operands=[])
        outer = MockMLILInstruction(operation=1, operands=[inner])
        result = self.norm.normalize_instruction_all_levels(outer)
        assert result == [1, 7]

    def test_normalize_skeleton_with_list_operand(self):
        inner_a = MockMLILInstruction(operation=10, operands=[])
        inner_b = MockMLILInstruction(operation=11, operands=[])
        outer = MockMLILInstruction(operation=2, operands=[[inner_a, inner_b]])
        result = self.norm.normalize_instruction_all_levels(outer)
        assert result == [2, 10, 11]

    def test_normalize_skeleton_none(self):
        result = self.norm.normalize_instruction_all_levels(None)
        # collect on None should leave ops empty
        assert result == []

    def test_normalize_typed_appends_leaf_types(self):
        # operand is a plain int -> "CONST"
        il = MockMLILInstruction(operation=3, operands=[42])
        result = self.norm.normalize_instr_with_operands(il)
        assert result == [3, "CONST"]

    def test_normalize_typed_bool_before_int(self):
        # bool must be detected before int (since bool is an int subclass)
        il = MockMLILInstruction(operation=4, operands=[True])
        result = self.norm.normalize_instr_with_operands(il)
        assert result == [4, "BOOL"]

    def test_normalize_typed_float(self):
        il = MockMLILInstruction(operation=5, operands=[1.5])
        result = self.norm.normalize_instr_with_operands(il)
        assert result == [5, "FLOAT_CONST"]

    def test_normalize_typed_str(self):
        il = MockMLILInstruction(operation=6, operands=["hello"])
        result = self.norm.normalize_instr_with_operands(il)
        assert result == [6, "STR"]

    def test_normalize_typed_unknown_falls_back_to_typename(self):
        class Weird:
            pass
        il = MockMLILInstruction(operation=8, operands=[Weird()])
        result = self.norm.normalize_instr_with_operands(il)
        assert result == [8, "WEIRD"]

    def test_normalize_typed_nested_mlil(self):
        inner = MockMLILInstruction(operation=99, operands=[7])
        outer = MockMLILInstruction(operation=1, operands=[inner])
        result = self.norm.normalize_instr_with_operands(outer)
        assert result == [1, 99, "CONST"]

    def test_normalize_typed_list_mixed(self):
        inner = MockMLILInstruction(operation=50, operands=[])
        il = MockMLILInstruction(operation=2, operands=[[inner, 99]])
        result = self.norm.normalize_instr_with_operands(il)
        assert result == [2, 50, "CONST"]


class TestMediumLevelAnalysis:
    def _make_analysis(self, instructions=None, mlil=True, start=0x1000):
        from redb.extractors.decompiler.bninja.analysis.medium_level import (
            MediumLevelAnalysis,
        )
        mlil_func = MockMLILFunction(instructions or []) if mlil else None
        func = MockFunction(name="testfunc", start=start, mlil=mlil_func)
        bv = MagicMock()
        return MediumLevelAnalysis(func, bv, MagicMock())

    def test_collect_returns_empty_when_no_mlil(self):
        a = self._make_analysis(mlil=False)
        sk, sk_addr, ty, ty_addr = a._collect_mlil_skeleton_and_typed()
        assert sk == [] and sk_addr == [] and ty == [] and ty_addr == []

    def test_collect_skeleton_and_typed_basic(self):
        instrs = [
            MockMLILInstruction(operation=1, address=0x1000, operands=[]),
            MockMLILInstruction(operation=2, address=0x1004, operands=[42]),
        ]
        a = self._make_analysis(instructions=instrs, start=0x1000)
        sk, sk_addr, ty, ty_addr = a._collect_mlil_skeleton_and_typed()

        assert sk == [[1], [2]]
        assert ty == [[1], [2, "CONST"]]
        assert sk_addr == [(0, [1]), (4, [2])]
        assert ty_addr == [(0, [1]), (4, [2, "CONST"])]

    def test_collect_negative_offset_clamped_to_zero(self):
        instrs = [
            MockMLILInstruction(operation=1, address=0x900, operands=[]),
        ]
        a = self._make_analysis(instructions=instrs, start=0x1000)
        _, sk_addr, _, ty_addr = a._collect_mlil_skeleton_and_typed()
        assert sk_addr[0][0] == 0
        assert ty_addr[0][0] == 0

    def test_log_error_records_entry(self):
        a = self._make_analysis()
        a.log_error("boom", "fname", 0x1234, ValueError("x"), "loc")
        assert len(a.errors) == 1
        err = a.errors[0]
        assert err["function_name"] == "fname"
        assert err["function_address"] == "4660"  # hex 0x1234
        assert err["error_location"] == "loc"
        assert err["error_message"] == "boom"
        assert err["error_type"] == "ValueError"
        assert "timestamp" in err

    @patch(
        "redb.extractors.decompiler.bninja.analysis.medium_level.MinHasher"
    )
    def test_analyze_returns_expected_keys(self, mock_minhasher):
        mock_minhasher.return_value.calculateMinHash.return_value = [1, 2, 3]

        instrs = [
            MockMLILInstruction(operation=1, address=0x1000, operands=[]),
            MockMLILInstruction(operation=2, address=0x1004, operands=[42]),
            MockMLILInstruction(operation=3, address=0x1008, operands=[]),
        ]
        a = self._make_analysis(instructions=instrs, start=0x1000)
        result, errors = a.analyze()

        expected_keys = {
            "function_address",
            "body_mlil_skeleton_vector",
            "sha256_mlil_skeleton",
            "tlsh_mlil_skeleton",
            "minhash_mlil_skeleton",
            "body_mlil_typed_vector",
            "sha256_mlil_typed",
            "tlsh_mlil_typed",
            "minhash_mlil_typed",
        }
        assert set(result.keys()) == expected_keys
        assert result["function_address"] == 0x1000
        assert result["minhash_mlil_skeleton"] == [1, 2, 3]
        assert result["minhash_mlil_typed"] == [1, 2, 3]
        assert errors == []

    @patch(
        "redb.extractors.decompiler.bninja.analysis.medium_level.MinHasher"
    )
    def test_analyze_empty_mlil(self, mock_minhasher):
        mock_minhasher.return_value.calculateMinHash.return_value = []
        a = self._make_analysis(mlil=False)
        result, errors = a.analyze()
        assert result["body_mlil_skeleton_vector"] == []
        assert result["body_mlil_typed_vector"] == []
        assert errors == []

    @patch(
        "redb.extractors.decompiler.bninja.analysis.medium_level.MinHasher"
    )
    def test_analyze_sha256_differs_skeleton_vs_typed(self, mock_minhasher):
        mock_minhasher.return_value.calculateMinHash.return_value = []

        instrs = [
            MockMLILInstruction(operation=1, address=0x1000, operands=[42]),
            MockMLILInstruction(operation=2, address=0x1004, operands=["foo"]),
            MockMLILInstruction(operation=3, address=0x1008, operands=[True]),
        ]
        a = self._make_analysis(instructions=instrs)
        result, _ = a.analyze()
        # skeleton ignores operand leaves, typed includes them -> different hashes
        assert result["sha256_mlil_skeleton"] != result["sha256_mlil_typed"]


class TestMinHasherMLILKinds:
    def _make_func(self, instrs):
        # MinHasher iterates basic_blocks then over each block
        block = MagicMock()
        block.__iter__ = lambda self_: iter(instrs)
        f = MagicMock()
        f.basic_blocks = [block]
        return f

    def test_mlil_skeleton_uses_medium_normalizer(self):
        from redb.extractors.decompiler.bninja.similarity.minhasher import (
            MinHasher, TokenKind,
        )
        instrs = [
            MockMLILInstruction(operation=i, operands=[]) for i in range(5)
        ]
        func = self._make_func(instrs)
        hasher = MinHasher(seed=42, il_function=func, kind=TokenKind.MLIL)
        result = hasher.calculateMinHash()
        # 5 instructions -> 3 trigrams -> non-empty signature
        assert result != []

    def test_typed_mlil_differs_from_skeleton(self):
        from redb.extractors.decompiler.bninja.similarity.minhasher import (
            MinHasher, TokenKind,
        )
        instrs = [
            MockMLILInstruction(operation=1, operands=[42]),
            MockMLILInstruction(operation=2, operands=["s"]),
            MockMLILInstruction(operation=3, operands=[True]),
            MockMLILInstruction(operation=4, operands=[1.5]),
        ]
        func = self._make_func(instrs)
        skel = MinHasher(seed=42, il_function=func, kind=TokenKind.MLIL).calculateMinHash()
        typed = MinHasher(seed=42, il_function=func, kind=TokenKind.TYPED_MLIL).calculateMinHash()
        # Same seed, same instructions, but typed has extra leaf tokens
        # -> hashes should generally differ
        assert skel != typed

    def test_mlil_too_few_instructions(self):
        from redb.extractors.decompiler.bninja.similarity.minhasher import (
            MinHasher, TokenKind,
        )
        instrs = [MockMLILInstruction(operation=1, operands=[])] * 2
        func = self._make_func(instrs)
        hasher = MinHasher(seed=42, il_function=func, kind=TokenKind.MLIL)
        assert hasher.calculateMinHash() == []

    def test_unsupported_kind_raises(self):
        from redb.extractors.decompiler.bninja.similarity.minhasher import MinHasher
        func = self._make_func([])
        hasher = MinHasher(seed=42, il_function=func, kind="bogus")
        with pytest.raises(ValueError):
            hasher.calculateMinHash()