diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index dec82b56d169945566e20f7c8993d78f204dcb1b..1d2bb62cab19276bdd5692058bd694d6ef0a884a 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -13,8 +13,6 @@ before_script:
   # - export CXXFLAGS='--coverage'
   # Install without dependencies to make sure that requirements.txt is up-to-date
   - pip install --no-deps -ve .
-  # Move file, but should be handled by installation
-  - mv _b_asic* b_asic
   - pip show b_asic
   - export QT_API=$QT_API
   # Install test dependencies
diff --git a/CMakeLists.txt b/CMakeLists.txt
deleted file mode 100644
index c1009f8e785cde77d86fd7d7d0fe62d4dc5455de..0000000000000000000000000000000000000000
--- a/CMakeLists.txt
+++ /dev/null
@@ -1,99 +0,0 @@
-cmake_minimum_required(VERSION 3.8)
-project("B-ASIC"
-	VERSION 1.0.0
-	DESCRIPTION "Better ASIC Toolbox for Python 3"
-	LANGUAGES C CXX)
-
-option(ASIC_USE_FETCHCONTENT "Automatically download dependencies" ON)
-option(ASIC_USE_CLANG_TIDY "Use clang-tidy for static analysis" OFF)
-option(ASIC_BUILDING_PYTHON_DISTRIBUTION "Don't copy compiled binaries to project directory" OFF)
-
-set(LIBRARY_NAME "b_asic") # Name of the python library directory.
-set(TARGET_NAME "_${LIBRARY_NAME}") # Name of this extension module.
-
-# Find dependencies.
-if(ASIC_USE_FETCHCONTENT)
-	add_subdirectory(dependencies)
-else()
-	find_package(fmt REQUIRED)
-	find_package(pybind11 CONFIG REQUIRED)
-endif()
-
-# Set output directory for compiled binaries.
-if(NOT CMAKE_LIBRARY_OUTPUT_DIRECTORY)
-	include(GNUInstallDirs)
-	set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_INSTALL_LIBDIR}")
-endif()
-set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
-set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
-set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
-set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
-set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
-set(CMAKE_PDB_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
-set(CMAKE_PDB_OUTPUT_DIRECTORY_DEBUG "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
-set(CMAKE_PDB_OUTPUT_DIRECTORY_RELEASE "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
-set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
-set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
-set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
-
-# Add files to be compiled into Python module.
-pybind11_add_module("${TARGET_NAME}"
-	# Main files.
-	"${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp"
-	"${CMAKE_CURRENT_SOURCE_DIR}/src/simulation.cpp"
-
-	# For DOD simulation.
-	"${CMAKE_CURRENT_SOURCE_DIR}/src/simulation/compile.cpp"
-	"${CMAKE_CURRENT_SOURCE_DIR}/src/simulation/run.cpp"
-	"${CMAKE_CURRENT_SOURCE_DIR}/src/simulation/simulation.cpp"
-
-	# For OOP simulation (see legacy folder).
-	#"${CMAKE_CURRENT_SOURCE_DIR}/src/simulation/custom_operation.cpp"
-	#"${CMAKE_CURRENT_SOURCE_DIR}/src/simulation/operation.cpp"
-	#"${CMAKE_CURRENT_SOURCE_DIR}/src/simulation/signal_flow_graph.cpp"
-	#"${CMAKE_CURRENT_SOURCE_DIR}/src/simulation/simulation.cpp"
-	#"${CMAKE_CURRENT_SOURCE_DIR}/src/simulation/special_operations.cpp"
-)
-
-# Include headers.
-target_include_directories("${TARGET_NAME}" PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src")
-
-# Use C++17.
-target_compile_features("${TARGET_NAME}" PRIVATE cxx_std_17)
-
-# Set compiler-specific options using generator expressions.
-target_compile_options("${TARGET_NAME}" PRIVATE
-	$<$<CXX_COMPILER_ID:GNU>:   -std=c++17  -Wall -Wextra   -Wpedantic      -Werror -Wno-psabi      $<$<CONFIG:Debug>:-Og -g>   $<$<CONFIG:Release>:-O3>    $<$<CONFIG:MinSizeRel>:-Os> $<$<CONFIG:RelWithDebInfo>:-O3 -g>>
-	$<$<CXX_COMPILER_ID:Clang>: -std=c++17  -Wall -Wextra   -Wpedantic      -Werror                 $<$<CONFIG:Debug>:-Og -g>   $<$<CONFIG:Release>:-O3>    $<$<CONFIG:MinSizeRel>:-Os> $<$<CONFIG:RelWithDebInfo>:-O3 -g>>
-	$<$<CXX_COMPILER_ID:MSVC>:  /std:c++17  /W3             /permissive-    /WX     /wd4996 /utf-8  $<$<CONFIG:Debug>:/Od>      $<$<CONFIG:Release>:/Ot>    $<$<CONFIG:MinSizeRel>:/Os> $<$<CONFIG:RelWithDebInfo>:/Ot /Od>>)
-
-# Add libraries. Note: pybind11 is already added in pybind11_add_module.
-if(ASIC_USE_FETCHCONTENT)
-	target_link_libraries("${TARGET_NAME}" PRIVATE
-		dependency_fmt)
-else()
-	target_link_libraries("${TARGET_NAME}" PRIVATE
-		$<TARGET_NAME_IF_EXISTS:fmt::fmt-header-only>
-		$<$<NOT:$<TARGET_EXISTS:fmt::fmt-header-only>>:fmt::fmt>)
-endif()
-
-# Set up clang-tidy.
-if(ASIC_USE_CLANG_TIDY)
-	find_program(CLANG_TIDY NAMES clang-tidy REQUIRED)
-	set_property(TARGET "${TARGET_NAME}" PROPERTY CXX_CLANG_TIDY ${CLANG_TIDY})
-	if(MSVC)
-		set_target_properties("${TARGET_NAME}" PROPERTIES
-			VS_GLOBAL_RunCodeAnalysis true
-			VS_GLOBAL_EnableMicrosoftCodeAnalysis false
-			VS_GLOBAL_EnableClangTidyCodeAnalysis true
-			VS_GLOBAL_ClangTidyToolExe "${CLANG_TIDY}")
-	endif()
-endif()
-
-# Copy binaries to project folder for debugging during development.
-if(NOT ASIC_BUILDING_PYTHON_DISTRIBUTION)
-	add_custom_target(copy_binaries ALL
-		COMMAND ${CMAKE_COMMAND} -E copy "$<TARGET_FILE:${TARGET_NAME}>" "${CMAKE_CURRENT_LIST_DIR}/${LIBRARY_NAME}"
-		COMMENT "Copying binaries to ${CMAKE_CURRENT_LIST_DIR}/${LIBRARY_NAME}"
-		DEPENDS "${TARGET_NAME}")
-endif()
diff --git a/b_asic/__init__.py b/b_asic/__init__.py
index 9db092b3f80fe2b3d1c4645d3b923175e9837406..f1fded40251d120eab5d5fb61bf128f27911bea3 100644
--- a/b_asic/__init__.py
+++ b/b_asic/__init__.py
@@ -1,12 +1,6 @@
 """B-ASIC - Better ASIC Toolbox.
 ASIC toolbox that simplifies circuit design and optimization.
 """
-# Extension module (C++).
-# NOTE: If this import gives an error,
-# make sure the C++ module has been compiled and installed properly.
-# See the included README.md for more information on how to build/install.
-from b_asic._b_asic import *
-
 # Python modules.
 from b_asic.core_operations import *
 from b_asic.graph_component import *
diff --git a/setup.py b/setup.py
index 637b4da4adc4e80eb9c2187680fcea6b77399d7f..db7a75bbc64e6e51a272e1affec7e88121161c57 100644
--- a/setup.py
+++ b/setup.py
@@ -1,72 +1,4 @@
-import os
-import shutil
-import subprocess
-import sys
-
 import setuptools
-from setuptools import Extension
-from setuptools.command.build_ext import build_ext
-
-
-class CMakeExtension(Extension):
-    def __init__(self, name, sourcedir=""):
-        super().__init__(name, sources=[])
-        self.sourcedir = os.path.abspath(sourcedir)
-
-
-class CMakeBuild(build_ext):
-    def build_extension(self, ext):
-        CMAKE_EXE = os.environ.get(
-            "CMAKE_EXE", shutil.which("cmake3") or shutil.which("cmake")
-        )
-        if not isinstance(ext, CMakeExtension):
-            return super().build_extension(ext)
-
-        if not CMAKE_EXE:
-            raise RuntimeError(
-                f"Cannot build extension {ext.name}: CMake executable not "
-                "found! Set the CMAKE_EXE environment variable or update your "
-                "path."
-            )
-
-        cmake_build_type = "Debug" if self.debug else "Release"
-        cmake_output_dir = os.path.abspath(
-            os.path.dirname(self.get_ext_fullpath(ext.name))
-        )
-        cmake_configure_argv = [
-            CMAKE_EXE,
-            ext.sourcedir,
-            "-DASIC_BUILDING_PYTHON_DISTRIBUTION=ON",
-            "-DCMAKE_BUILD_TYPE=" + cmake_build_type,
-            "-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=" + cmake_output_dir,
-            "-DPYTHON_EXECUTABLE=" + sys.executable,
-        ]
-        cmake_build_argv = [
-            CMAKE_EXE,
-            "--build",
-            ".",
-            "--config",
-            cmake_build_type,
-        ]
-
-        if not os.path.exists(self.build_temp):
-            os.makedirs(self.build_temp)
-
-        env = os.environ.copy()
-
-        print(f"=== Configuring {ext.name} ===")
-        print(f"Temp dir: {self.build_temp}")
-        print(f"Output dir: {cmake_output_dir}")
-        subprocess.check_call(cmake_configure_argv, cwd=self.build_temp, env=env)
-
-        print(f"=== Building {ext.name} ===")
-        print(f"Temp dir: {self.build_temp}")
-        print(f"Output dir: {cmake_output_dir}")
-        print(f"Build type: {cmake_build_type}")
-        subprocess.check_call(cmake_build_argv, cwd=self.build_temp, env=env)
-
-        print()
-
 
 setuptools.setup(
     author=(
@@ -81,6 +13,4 @@ setuptools.setup(
         " andbo467@student.liu.se, mikael.henriksson@liu.se, frans.skarman@liu.se,"
         " petter.kallstrom@liu.se, olle.hansson@liu.se"
     ),
-    ext_modules=[CMakeExtension("_b_asic")],
-    cmdclass={"build_ext": CMakeBuild},
 )
diff --git a/src/algorithm.hpp b/src/algorithm.hpp
deleted file mode 100644
index 4a609160d3bf9e9e0b065e105dd7ada3f759e8b9..0000000000000000000000000000000000000000
--- a/src/algorithm.hpp
+++ /dev/null
@@ -1,350 +0,0 @@
-#ifndef ASIC_ALGORITHM_HPP
-#define ASIC_ALGORITHM_HPP
-
-#include <cstddef>
-#include <iterator>
-#include <memory>
-#include <type_traits>
-#include <utility>
-
-namespace asic {
-namespace detail {
-
-template <typename Reference>
-class arrow_proxy final {
-public:
-	template <typename Ref>
-	constexpr explicit arrow_proxy(Ref&& r)
-		: m_r(std::forward<Ref>(r)) {}
-
-	Reference* operator->() {
-		return std::addressof(m_r);
-	}
-
-private:
-	Reference m_r;
-};
-
-template <typename T>
-class range_view final {
-public:
-	class iterator final {
-	public:
-		using difference_type = std::ptrdiff_t;
-		using value_type = T const;
-		using reference = value_type&;
-		using pointer = value_type*;
-		using iterator_category = std::random_access_iterator_tag;
-
-		constexpr iterator() noexcept = default;
-		constexpr explicit iterator(T value) noexcept
-			: m_value(value) {}
-
-		[[nodiscard]] constexpr bool operator==(iterator const& other) const noexcept {
-			return m_value == other.m_value;
-		}
-
-		[[nodiscard]] constexpr bool operator!=(iterator const& other) const noexcept {
-			return m_value != other.m_value;
-		}
-
-		[[nodiscard]] constexpr bool operator<(iterator const& other) const noexcept {
-			return m_value < other.m_value;
-		}
-
-		[[nodiscard]] constexpr bool operator>(iterator const& other) const noexcept {
-			return m_value > other.m_value;
-		}
-
-		[[nodiscard]] constexpr bool operator<=(iterator const& other) const noexcept {
-			return m_value <= other.m_value;
-		}
-
-		[[nodiscard]] constexpr bool operator>=(iterator const& other) const noexcept {
-			return m_value >= other.m_value;
-		}
-
-		[[nodiscard]] constexpr reference operator*() const noexcept {
-			return m_value;
-		}
-
-		[[nodiscard]] constexpr pointer operator->() const noexcept {
-			return std::addressof(**this);
-		}
-
-		constexpr iterator& operator++() noexcept {
-			++m_value;
-			return *this;
-		}
-
-		constexpr iterator operator++(int) noexcept {
-			return iterator{m_value++};
-		}
-
-		constexpr iterator& operator--() noexcept {
-			--m_value;
-			return *this;
-		}
-
-		constexpr iterator operator--(int) noexcept {
-			return iterator{m_value--};
-		}
-
-		constexpr iterator& operator+=(difference_type n) noexcept {
-			m_value += n;
-			return *this;
-		}
-
-		constexpr iterator& operator-=(difference_type n) noexcept {
-			m_value -= n;
-			return *this;
-		}
-
-		[[nodiscard]] constexpr T operator[](difference_type n) noexcept {
-			return m_value + static_cast<T>(n);
-		}
-
-		[[nodiscard]] constexpr friend iterator operator+(iterator const& lhs, difference_type rhs) noexcept {
-			return iterator{lhs.m_value + rhs};
-		}
-
-		[[nodiscard]] constexpr friend iterator operator+(difference_type lhs, iterator const& rhs) noexcept {
-			return iterator{lhs + rhs.m_value};
-		}
-
-		[[nodiscard]] constexpr friend iterator operator-(iterator const& lhs, difference_type rhs) noexcept {
-			return iterator{lhs.m_value - rhs};
-		}
-
-		[[nodiscard]] constexpr friend difference_type operator-(iterator const& lhs, iterator const& rhs) noexcept {
-			return static_cast<difference_type>(lhs.m_value - rhs.m_value);
-		}
-
-	private:
-		T m_value{};
-	};
-
-	using sentinel = iterator;
-
-	template <typename First, typename Last>
-	constexpr range_view(First&& first, Last&& last) noexcept
-		: m_begin(std::forward<First>(first))
-		, m_end(std::forward<Last>(last)) {}
-
-	[[nodiscard]] constexpr iterator begin() const noexcept {
-		return m_begin;
-	}
-
-	[[nodiscard]] constexpr sentinel end() const noexcept {
-		return m_end;
-	}
-
-private:
-	iterator m_begin;
-	sentinel m_end;
-};
-
-template <typename Range, typename Iterator, typename Sentinel>
-class enumerate_view final {
-public:
-	using sentinel = Sentinel;
-
-	class iterator final {
-	public:
-		using difference_type = typename std::iterator_traits<Iterator>::difference_type;
-		using value_type = typename std::iterator_traits<Iterator>::value_type;
-		using reference = std::pair<std::size_t const&, decltype(*std::declval<Iterator const>())>;
-		using pointer = arrow_proxy<reference>;
-		using iterator_category =
-			std::common_type_t<typename std::iterator_traits<Iterator>::iterator_category, std::bidirectional_iterator_tag>;
-
-		constexpr iterator() = default;
-
-		constexpr iterator(Iterator it, std::size_t index)
-			: m_it(std::move(it))
-			, m_index(index) {}
-
-		[[nodiscard]] constexpr bool operator==(iterator const& other) const {
-			return m_it == other.m_it;
-		}
-
-		[[nodiscard]] constexpr bool operator!=(iterator const& other) const {
-			return m_it != other.m_it;
-		}
-
-		[[nodiscard]] constexpr bool operator==(sentinel const& other) const {
-			return m_it == other;
-		}
-
-		[[nodiscard]] constexpr bool operator!=(sentinel const& other) const {
-			return m_it != other;
-		}
-
-		[[nodiscard]] constexpr reference operator*() const {
-			return reference{m_index, *m_it};
-		}
-
-		[[nodiscard]] constexpr pointer operator->() const {
-			return pointer{**this};
-		}
-
-		constexpr iterator& operator++() {
-			++m_it;
-			++m_index;
-			return *this;
-		}
-
-		constexpr iterator operator++(int) {
-			return iterator{m_it++, m_index++};
-		}
-
-		constexpr iterator& operator--() {
-			--m_it;
-			--m_index;
-			return *this;
-		}
-
-		constexpr iterator operator--(int) {
-			return iterator{m_it--, m_index--};
-		}
-
-	private:
-		Iterator m_it;
-		std::size_t m_index = 0;
-	};
-
-	template <typename R>
-	constexpr explicit enumerate_view(R&& range)
-		: m_range(std::forward<R>(range)) {}
-
-	[[nodiscard]] constexpr iterator begin() const {
-		return iterator{std::begin(m_range), 0};
-	}
-
-	[[nodiscard]] constexpr sentinel end() const {
-		return std::end(m_range);
-	}
-
-private:
-	Range m_range;
-};
-
-template <typename Range1, typename Range2, typename Iterator1, typename Iterator2, typename Sentinel1, typename Sentinel2>
-class zip_view final {
-public:
-	using sentinel = std::pair<Sentinel1, Sentinel2>;
-
-	class iterator final {
-	public:
-		using difference_type = std::common_type_t<typename std::iterator_traits<Iterator1>::difference_type,
-												   typename std::iterator_traits<Iterator2>::difference_type>;
-		using value_type =
-			std::pair<typename std::iterator_traits<Iterator1>::value_type, typename std::iterator_traits<Iterator2>::value_type>;
-		using reference = std::pair<decltype(*std::declval<Iterator1 const>()), decltype(*std::declval<Iterator2 const>())>;
-		using pointer = arrow_proxy<reference>;
-		using iterator_category =
-			std::common_type_t<typename std::iterator_traits<Iterator1>::iterator_category,
-							   typename std::iterator_traits<Iterator2>::iterator_category, std::bidirectional_iterator_tag>;
-
-		constexpr iterator() = default;
-
-		constexpr iterator(Iterator1 it1, Iterator2 it2)
-			: m_it1(std::move(it1))
-			, m_it2(std::move(it2)) {}
-
-		[[nodiscard]] constexpr bool operator==(iterator const& other) const {
-			return m_it1 == other.m_it1 && m_it2 == other.m_it2;
-		}
-
-		[[nodiscard]] constexpr bool operator!=(iterator const& other) const {
-			return !(*this == other);
-		}
-
-		[[nodiscard]] constexpr bool operator==(sentinel const& other) const {
-			return m_it1 == other.first || m_it2 == other.second;
-		}
-
-		[[nodiscard]] constexpr bool operator!=(sentinel const& other) const {
-			return !(*this == other);
-		}
-
-		[[nodiscard]] constexpr reference operator*() const {
-			return reference{*m_it1, *m_it2};
-		}
-
-		[[nodiscard]] constexpr pointer operator->() const {
-			return pointer{**this};
-		}
-
-		constexpr iterator& operator++() {
-			++m_it1;
-			++m_it2;
-			return *this;
-		}
-
-		constexpr iterator operator++(int) {
-			return iterator{m_it1++, m_it2++};
-		}
-
-		constexpr iterator& operator--() {
-			--m_it1;
-			--m_it2;
-			return *this;
-		}
-
-		constexpr iterator operator--(int) {
-			return iterator{m_it1--, m_it2--};
-		}
-
-	private:
-		Iterator1 m_it1;
-		Iterator2 m_it2;
-	};
-
-	template <typename R1, typename R2>
-	constexpr zip_view(R1&& range1, R2&& range2)
-		: m_range1(std::forward<R1>(range1))
-		, m_range2(std::forward<R2>(range2)) {}
-
-	[[nodiscard]] constexpr iterator begin() const {
-		return iterator{std::begin(m_range1), std::begin(m_range2)};
-	}
-
-	[[nodiscard]] constexpr sentinel end() const {
-		return sentinel{std::end(m_range1), std::end(m_range2)};
-	}
-
-private:
-	Range1 m_range1;
-	Range2 m_range2;
-};
-
-} // namespace detail
-
-template <typename First, typename Last, typename T = std::remove_cv_t<std::remove_reference_t<First>>>
-[[nodiscard]] constexpr auto range(First&& first, Last&& last) {
-	return detail::range_view<T>{std::forward<First>(first), std::forward<Last>(last)};
-}
-
-template <typename Last, typename T = std::remove_cv_t<std::remove_reference_t<Last>>>
-[[nodiscard]] constexpr auto range(Last&& last) {
-	return detail::range_view<T>{T{}, std::forward<Last>(last)};
-}
-
-template <typename Range, typename Iterator = decltype(std::begin(std::declval<Range>())),
-		  typename Sentinel = decltype(std::end(std::declval<Range>()))>
-[[nodiscard]] constexpr auto enumerate(Range&& range) {
-	return detail::enumerate_view<Range, Iterator, Sentinel>{std::forward<Range>(range)};
-}
-
-template <typename Range1, typename Range2, typename Iterator1 = decltype(std::begin(std::declval<Range1>())),
-		  typename Iterator2 = decltype(std::begin(std::declval<Range2>())),
-		  typename Sentinel1 = decltype(std::end(std::declval<Range1>())), typename Sentinel2 = decltype(std::end(std::declval<Range2>()))>
-[[nodiscard]] constexpr auto zip(Range1&& range1, Range2&& range2) {
-	return detail::zip_view<Range1, Range2, Iterator1, Iterator2, Sentinel1, Sentinel2>{std::forward<Range1>(range1),
-																						std::forward<Range2>(range2)};
-}
-
-} // namespace asic
-
-#endif // ASIC_ALGORITHM_HPP
diff --git a/src/debug.hpp b/src/debug.hpp
deleted file mode 100644
index e1253790b21aedfb5c9d0205139dd74cf388f8fc..0000000000000000000000000000000000000000
--- a/src/debug.hpp
+++ /dev/null
@@ -1,94 +0,0 @@
-#ifndef ASIC_DEBUG_HPP
-#define ASIC_DEBUG_HPP
-
-#ifndef NDEBUG
-
-#ifndef ASIC_ENABLE_DEBUG_LOGGING
-#define ASIC_ENABLE_DEBUG_LOGGING 1
-#endif
-
-#ifndef ASIC_ENABLE_ASSERTS
-#define ASIC_ENABLE_ASSERTS 1
-#endif
-
-#else
-
-#ifndef ASIC_ENABLE_DEBUG_LOGGING
-#define ASIC_ENABLE_DEBUG_LOGGING 0
-#endif
-
-#ifndef ASIC_ENABLE_ASSERTS
-#define ASIC_ENABLE_ASSERTS 0
-#endif
-
-#endif // NDEBUG
-
-#if ASIC_ENABLE_DEBUG_LOGGING
-#include <filesystem>
-#include <fmt/format.h>
-#include <fstream>
-#include <ostream>
-#include <string_view>
-#include <utility>
-#endif // ASIC_ENABLE_DEBUG_LOGGING
-
-#if ASIC_ENABLE_ASSERTS
-#include <cstdio>
-#include <cstdlib>
-#include <filesystem>
-#include <fmt/format.h>
-#include <string_view>
-#endif // ASIC_ENABLE_ASSERTS
-
-namespace asic {
-
-constexpr auto debug_log_filename = "_b_asic_debug_log.txt";
-
-namespace detail {
-
-#if ASIC_ENABLE_DEBUG_LOGGING
-inline void log_debug_msg_string(std::string_view file, int line, std::string_view string) {
-	static auto log_file = std::ofstream{debug_log_filename, std::ios::trunc};
-	log_file << fmt::format("{:<40}: {}", fmt::format("{}:{}", std::filesystem::path{file}.filename().generic_string(), line), string)
-			 << std::endl;
-}
-
-template <typename Format, typename... Args>
-inline void log_debug_msg(std::string_view file, int line, Format&& format, Args&&... args) {
-	log_debug_msg_string(file, line, fmt::format(std::forward<Format>(format), std::forward<Args>(args)...));
-}
-#endif // ASIC_ENABLE_DEBUG_LOGGING
-
-#if ASIC_ENABLE_ASSERTS
-inline void fail_assert(std::string_view file, int line, std::string_view condition_string) {
-#if ASIC_ENABLE_DEBUG_LOGGING
-	log_debug_msg(file, line, "Assertion failed: {}", condition_string);
-#endif // ASIC_ENABLE_DEBUG_LOGGING
-	fmt::print(stderr, "{}:{}: Assertion failed: {}\n", std::filesystem::path{file}.filename().generic_string(), line, condition_string);
-	std::abort();
-}
-
-template <typename BoolConvertible>
-inline void check_assert(std::string_view file, int line, std::string_view condition_string, BoolConvertible&& condition) {
-	if (!static_cast<bool>(condition)) {
-		fail_assert(file, line, condition_string);
-	}
-}
-#endif // ASIC_ENABLE_ASSERTS
-
-} // namespace detail
-} // namespace asic
-
-#if ASIC_ENABLE_DEBUG_LOGGING
-#define ASIC_DEBUG_MSG(...) (asic::detail::log_debug_msg(__FILE__, __LINE__, __VA_ARGS__))
-#else
-#define ASIC_DEBUG_MSG(...) ((void)0)
-#endif // ASIC_ENABLE_DEBUG_LOGGING
-
-#if ASIC_ENABLE_ASSERTS
-#define ASIC_ASSERT(condition) (asic::detail::check_assert(__FILE__, __LINE__, #condition, (condition)))
-#else
-#define ASIC_ASSERT(condition) ((void)0)
-#endif
-
-#endif // ASIC_DEBUG_HPP
diff --git a/src/main.cpp b/src/main.cpp
deleted file mode 100644
index 8ac36c73b6261d6a7a0daaa9760ed037e9d5cb5a..0000000000000000000000000000000000000000
--- a/src/main.cpp
+++ /dev/null
@@ -1,9 +0,0 @@
-#include "simulation.hpp"
-
-#define NOMINMAX
-#include <pybind11/pybind11.h>
-
-PYBIND11_MODULE(_b_asic, module) { // NOLINT
-	module.doc() = "Better ASIC Toolbox Extension Module.";
-	asic::define_simulation_class(module);
-}
diff --git a/src/number.hpp b/src/number.hpp
deleted file mode 100644
index 869a93f17965108d295022d0ddb2025ce7164ff8..0000000000000000000000000000000000000000
--- a/src/number.hpp
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef ASIC_NUMBER_HPP
-#define ASIC_NUMBER_HPP
-
-#define NOMINMAX
-#include <complex>
-#include <pybind11/complex.h>
-
-namespace asic {
-
-using number = std::complex<double>;
-
-} // namespace asic
-
-#endif // ASIC_NUMBER_HPP
diff --git a/src/simulation.cpp b/src/simulation.cpp
deleted file mode 100644
index 9381925b03c6140e75a48133995e9b06fbee030c..0000000000000000000000000000000000000000
--- a/src/simulation.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-#include "simulation.hpp"
-
-#include "simulation/simulation.hpp"
-
-namespace py = pybind11;
-
-namespace asic {
-
-void define_simulation_class(pybind11::module& module) {
-	// clang-format off
-	py::class_<simulation>(module, "FastSimulation")
-		.def(py::init<py::handle>(),
-			py::arg("sfg"),
-			"SFG Constructor.")
-
-		.def(py::init<py::handle, std::optional<std::vector<std::optional<input_provider_type>>>>(),
-			py::arg("sfg"), py::arg("input_providers"),
-			"SFG Constructor.")
-
-		.def("set_input", &simulation::set_input,
-			py::arg("index"), py::arg("input_provider"),
-			"Set the input function used to get values for the specific input at the given index to the internal SFG.")
-
-		.def("set_inputs", &simulation::set_inputs,
-			py::arg("input_providers"),
-			"Set the input functions used to get values for the inputs to the internal SFG.")
-
-		.def("step", &simulation::step,
-			py::arg("save_results") = true, py::arg("bits_override") = py::none{}, py::arg("quantize") = true,
-			"Run one iteration of the simulation and return the resulting output values.")
-
-		.def("run_until", &simulation::run_until,
-			py::arg("iteration"), py::arg("save_results") = true, py::arg("bits_override") = py::none{}, py::arg("quantize") = true,
-			"Run the simulation until its iteration is greater than or equal to the given iteration\n"
-			"and return the output values of the last iteration.")
-
-		.def("run_for", &simulation::run_for,
-			py::arg("iterations"), py::arg("save_results") = true, py::arg("bits_override") = py::none{}, py::arg("quantize") = true,
-			"Run a given number of iterations of the simulation and return the output values of the last iteration.")
-
-		.def("run", &simulation::run,
-			py::arg("save_results") = true, py::arg("bits_override") = py::none{}, py::arg("quantize") = true,
-			"Run the simulation until the end of its input arrays and return the output values of the last iteration.")
-
-		.def_property_readonly("iteration", &simulation::iteration,
-			"Get the current iteration number of the simulation.")
-
-		.def_property_readonly("results", &simulation::results,
-			"Get a mapping from result keys to numpy arrays containing all results, including intermediate values,\n"
-			"calculated for each iteration up until now that was run with save_results enabled.\n"
-			"The mapping is indexed using the key() method of Operation with the appropriate output index.\n"
-			"Example result after 3 iterations: {\"c1\": [3, 6, 7], \"c2\": [4, 5, 5], \"bfly1.0\": [7, 0, 0], \"bfly1.1\": [-1, 0, 2], \"0\": [7, -2, -1]}")
-
-		.def("clear_results", &simulation::clear_results,
-			"Clear all results that were saved until now.")
-
-		.def("clear_state", &simulation::clear_state,
-			"Clear all current state of the simulation, except for the results and iteration.");
-	// clang-format on
-}
-
-} // namespace asic
diff --git a/src/simulation.hpp b/src/simulation.hpp
deleted file mode 100644
index 5e7ab381e62b35a69aaf3f056e2808182c2e351d..0000000000000000000000000000000000000000
--- a/src/simulation.hpp
+++ /dev/null
@@ -1,13 +0,0 @@
-#ifndef ASIC_SIMULATION_HPP
-#define ASIC_SIMULATION_HPP
-
-#define NOMINMAX
-#include <pybind11/pybind11.h>
-
-namespace asic {
-
-void define_simulation_class(pybind11::module& module);
-
-} // namespace asic
-
-#endif // ASIC_SIMULATION_HPP
diff --git a/src/simulation/compile.cpp b/src/simulation/compile.cpp
deleted file mode 100644
index fd8c204e587f14f3001ede86db22b23eb9c96743..0000000000000000000000000000000000000000
--- a/src/simulation/compile.cpp
+++ /dev/null
@@ -1,314 +0,0 @@
-#include "compile.hpp"
-
-#include "../algorithm.hpp"
-#include "../debug.hpp"
-#include "../span.hpp"
-#include "format_code.hpp"
-
-#define NOMINMAX
-#include <Python.h>
-#include <fmt/format.h>
-#include <limits>
-#include <optional>
-#include <string_view>
-#include <tuple>
-#include <unordered_map>
-#include <utility>
-
-namespace py = pybind11;
-
-namespace asic {
-
-[[nodiscard]] static result_key key_base(py::handle op, std::string_view prefix) {
-	auto const graph_id = op.attr("graph_id").cast<std::string_view>();
-	return (prefix.empty()) ? result_key{graph_id} : fmt::format("{}.{}", prefix, graph_id);
-}
-
-[[nodiscard]] static result_key key_of_output(py::handle op, std::size_t output_index, std::string_view prefix) {
-	auto base = key_base(op, prefix);
-	if (base.empty()) {
-		return fmt::to_string(output_index);
-	}
-	if (op.attr("output_count").cast<std::size_t>() == 1) {
-		return base;
-	}
-	return fmt::format("{}.{}", base, output_index);
-}
-
-class compiler final {
-public:
-	simulation_code compile(py::handle sfg) {
-		ASIC_DEBUG_MSG("Compiling code...");
-		this->initialize_code(sfg.attr("input_count").cast<std::size_t>(), sfg.attr("output_count").cast<std::size_t>());
-		auto deferred_delays = delay_queue{};
-		this->add_outputs(sfg, deferred_delays);
-		this->add_deferred_delays(std::move(deferred_delays));
-		this->resolve_invalid_result_indices();
-		ASIC_DEBUG_MSG("Compiled code:\n{}\n", format_compiled_simulation_code(m_code));
-		return std::move(m_code);
-	}
-
-private:
-	struct sfg_info final {
-		py::handle sfg;
-		std::size_t prefix_length;
-
-		sfg_info(py::handle sfg, std::size_t prefix_length)
-			: sfg(sfg)
-			, prefix_length(prefix_length) {}
-
-		[[nodiscard]] std::size_t find_input_operation_index(py::handle op) const {
-			for (auto const& [i, in] : enumerate(sfg.attr("input_operations"))) {
-				if (in.is(op)) {
-					return i;
-				}
-			}
-			throw py::value_error{"Stray Input operation in simulation SFG"};
-		}
-	};
-
-	using sfg_info_stack = std::vector<sfg_info>;
-	using delay_queue = std::vector<std::tuple<std::size_t, py::handle, std::string, sfg_info_stack>>;
-	using added_output_cache = std::unordered_set<PyObject const*>;
-	using added_result_cache = std::unordered_map<PyObject const*, result_index_type>;
-	using added_custom_operation_cache = std::unordered_map<PyObject const*, std::size_t>;
-
-	static constexpr auto no_result_index = std::numeric_limits<result_index_type>::max();
-
-	void initialize_code(std::size_t input_count, std::size_t output_count) {
-		m_code.required_stack_size = 0;
-		m_code.input_count = input_count;
-		m_code.output_count = output_count;
-	}
-
-	void add_outputs(py::handle sfg, delay_queue& deferred_delays) {
-		for (auto const i : range(m_code.output_count)) {
-			this->add_operation_output(sfg, i, std::string_view{}, sfg_info_stack{}, deferred_delays);
-		}
-	}
-
-	void add_deferred_delays(delay_queue&& deferred_delays) {
-		while (!deferred_delays.empty()) {
-			auto new_deferred_delays = delay_queue{};
-			for (auto const& [delay_index, op, prefix, sfg_stack] : deferred_delays) {
-				this->add_source(op, 0, prefix, sfg_stack, deferred_delays);
-				this->add_instruction(instruction_type::update_delay, no_result_index, -1).index = delay_index;
-			}
-			deferred_delays = new_deferred_delays;
-		}
-	}
-
-	void resolve_invalid_result_indices() {
-		for (auto& instruction : m_code.instructions) {
-			if (instruction.result_index == no_result_index) {
-				instruction.result_index = static_cast<result_index_type>(m_code.result_keys.size());
-			}
-		}
-	}
-
-	[[nodiscard]] static sfg_info_stack push_sfg(sfg_info_stack const& sfg_stack, py::handle sfg, std::size_t prefix_length) {
-		auto const new_size = static_cast<std::size_t>(sfg_stack.size() + 1);
-		auto new_sfg_stack = sfg_info_stack{};
-		new_sfg_stack.reserve(new_size);
-		for (auto const& info : sfg_stack) {
-			new_sfg_stack.push_back(info);
-		}
-		new_sfg_stack.emplace_back(sfg, prefix_length);
-		return new_sfg_stack;
-	}
-
-	[[nodiscard]] static sfg_info_stack pop_sfg(sfg_info_stack const& sfg_stack) {
-		ASIC_ASSERT(!sfg_stack.empty());
-		auto const new_size = static_cast<std::size_t>(sfg_stack.size() - 1);
-		auto new_sfg_stack = sfg_info_stack{};
-		new_sfg_stack.reserve(new_size);
-		for (auto const& info : span{sfg_stack}.first(new_size)) {
-			new_sfg_stack.push_back(info);
-		}
-		return new_sfg_stack;
-	}
-
-	instruction& add_instruction(instruction_type type, result_index_type result_index, std::ptrdiff_t stack_diff) {
-		m_stack_depth += stack_diff;
-		if (m_stack_depth < 0) {
-			throw py::value_error{"Detected input/output count mismatch in simulation SFG"};
-		}
-		if (auto const stack_size = static_cast<std::size_t>(m_stack_depth); stack_size > m_code.required_stack_size) {
-			m_code.required_stack_size = stack_size;
-		}
-		auto& instruction = m_code.instructions.emplace_back();
-		instruction.type = type;
-		instruction.result_index = result_index;
-		return instruction;
-	}
-
-	[[nodiscard]] std::optional<result_index_type> begin_operation_output(py::handle op, std::size_t output_index,
-																		  std::string_view prefix) {
-		auto* const pointer = op.attr("outputs")[py::int_{output_index}].ptr();
-		if (m_incomplete_outputs.count(pointer) != 0) {
-			// Make sure the output doesn't depend on its own value, unless it's a delay operation.
-			if (op.attr("type_name")().cast<std::string_view>() != "t") {
-				throw py::value_error{"Direct feedback loop detected in simulation SFG"};
-			}
-		}
-		// Try to add a new result.
-		auto const [it, inserted] = m_added_results.try_emplace(pointer, static_cast<result_index_type>(m_code.result_keys.size()));
-		if (inserted) {
-			if (m_code.result_keys.size() >= static_cast<std::size_t>(std::numeric_limits<result_index_type>::max())) {
-				throw py::value_error{fmt::format("Simulation SFG requires too many outputs to be stored (limit: {})",
-												  std::numeric_limits<result_index_type>::max())};
-			}
-			m_code.result_keys.push_back(key_of_output(op, output_index, prefix));
-			m_incomplete_outputs.insert(pointer);
-			return it->second;
-		}
-		// If the result has already been added, we re-use the old result and
-		// return std::nullopt to indicate that we don't need to add all the required instructions again.
-		this->add_instruction(instruction_type::push_result, it->second, 1).index = static_cast<std::size_t>(it->second);
-		return std::nullopt;
-	}
-
-	void end_operation_output(py::handle op, std::size_t output_index) {
-		auto* const pointer = op.attr("outputs")[py::int_{output_index}].ptr();
-		[[maybe_unused]] auto const erased = m_incomplete_outputs.erase(pointer);
-		ASIC_ASSERT(erased == 1);
-	}
-
-	[[nodiscard]] std::size_t try_add_custom_operation(py::handle op) {
-		auto const [it, inserted] = m_added_custom_operations.try_emplace(op.ptr(), m_added_custom_operations.size());
-		if (inserted) {
-			auto& custom_operation = m_code.custom_operations.emplace_back();
-			custom_operation.evaluate_output = op.attr("evaluate_output");
-			custom_operation.input_count = op.attr("input_count").cast<std::size_t>();
-			custom_operation.output_count = op.attr("output_count").cast<std::size_t>();
-		}
-		return it->second;
-	}
-
-	[[nodiscard]] std::size_t add_delay_info(number initial_value, result_index_type result_index) {
-		auto const delay_index = m_code.delays.size();
-		auto& delay = m_code.delays.emplace_back();
-		delay.initial_value = initial_value;
-		delay.result_index = result_index;
-		return delay_index;
-	}
-
-	void add_source(py::handle op, std::size_t input_index, std::string_view prefix, sfg_info_stack const& sfg_stack,
-					delay_queue& deferred_delays) {
-		auto const signal = py::object{op.attr("inputs")[py::int_{input_index}].attr("signals")[py::int_{0}]};
-		auto const src = py::handle{signal.attr("source")};
-		auto const operation = py::handle{src.attr("operation")};
-		auto const index = src.attr("index").cast<std::size_t>();
-		this->add_operation_output(operation, index, prefix, sfg_stack, deferred_delays);
-		if (!signal.attr("bits").is_none()) {
-			auto const bits = signal.attr("bits").cast<std::size_t>();
-			if (bits > 64) {
-				throw py::value_error{"Cannot quantize to more than 64 bits"};
-			}
-			this->add_instruction(instruction_type::quantize, no_result_index, 0).bit_mask = static_cast<std::int64_t>(
-				(std::int64_t{1} << bits) - 1);
-		}
-	}
-
-	void add_unary_operation_output(py::handle op, result_index_type result_index, std::string_view prefix, sfg_info_stack const& sfg_stack,
-									delay_queue& deferred_delays, instruction_type type) {
-		this->add_source(op, 0, prefix, sfg_stack, deferred_delays);
-		this->add_instruction(type, result_index, 0);
-	}
-
-	void add_binary_operation_output(py::handle op, result_index_type result_index, std::string_view prefix,
-									 sfg_info_stack const& sfg_stack, delay_queue& deferred_delays, instruction_type type) {
-		this->add_source(op, 0, prefix, sfg_stack, deferred_delays);
-		this->add_source(op, 1, prefix, sfg_stack, deferred_delays);
-		this->add_instruction(type, result_index, -1);
-	}
-
-	void add_operation_output(py::handle op, std::size_t output_index, std::string_view prefix, sfg_info_stack const& sfg_stack,
-							  delay_queue& deferred_delays) {
-		auto const type_name = op.attr("type_name")().cast<std::string_view>();
-		if (type_name == "out") {
-			this->add_source(op, 0, prefix, sfg_stack, deferred_delays);
-		} else if (auto const result_index = this->begin_operation_output(op, output_index, prefix)) {
-			if (type_name == "c") {
-				this->add_instruction(instruction_type::push_constant, *result_index, 1).value = op.attr("value").cast<number>();
-			} else if (type_name == "add") {
-				this->add_binary_operation_output(op, *result_index, prefix, sfg_stack, deferred_delays, instruction_type::addition);
-			} else if (type_name == "sub") {
-				this->add_binary_operation_output(op, *result_index, prefix, sfg_stack, deferred_delays, instruction_type::subtraction);
-			} else if (type_name == "mul") {
-				this->add_binary_operation_output(op, *result_index, prefix, sfg_stack, deferred_delays, instruction_type::multiplication);
-			} else if (type_name == "div") {
-				this->add_binary_operation_output(op, *result_index, prefix, sfg_stack, deferred_delays, instruction_type::division);
-			} else if (type_name == "min") {
-				this->add_binary_operation_output(op, *result_index, prefix, sfg_stack, deferred_delays, instruction_type::min);
-			} else if (type_name == "max") {
-				this->add_binary_operation_output(op, *result_index, prefix, sfg_stack, deferred_delays, instruction_type::max);
-			} else if (type_name == "sqrt") {
-				this->add_unary_operation_output(op, *result_index, prefix, sfg_stack, deferred_delays, instruction_type::square_root);
-			} else if (type_name == "conj") {
-				this->add_unary_operation_output(
-					op, *result_index, prefix, sfg_stack, deferred_delays, instruction_type::complex_conjugate);
-			} else if (type_name == "abs") {
-				this->add_unary_operation_output(op, *result_index, prefix, sfg_stack, deferred_delays, instruction_type::absolute);
-			} else if (type_name == "cmul") {
-				this->add_source(op, 0, prefix, sfg_stack, deferred_delays);
-				this->add_instruction(instruction_type::constant_multiplication, *result_index, 0).value = op.attr("value").cast<number>();
-			} else if (type_name == "bfly") {
-				if (output_index == 0) {
-					this->add_source(op, 0, prefix, sfg_stack, deferred_delays);
-					this->add_source(op, 1, prefix, sfg_stack, deferred_delays);
-					this->add_instruction(instruction_type::addition, *result_index, -1);
-				} else {
-					this->add_source(op, 0, prefix, sfg_stack, deferred_delays);
-					this->add_source(op, 1, prefix, sfg_stack, deferred_delays);
-					this->add_instruction(instruction_type::subtraction, *result_index, -1);
-				}
-			} else if (type_name == "in") {
-				if (sfg_stack.empty()) {
-					throw py::value_error{"Encountered Input operation outside SFG in simulation"};
-				}
-				auto const& info = sfg_stack.back();
-				auto const input_index = info.find_input_operation_index(op);
-				if (sfg_stack.size() == 1) {
-					this->add_instruction(instruction_type::push_input, *result_index, 1).index = input_index;
-				} else {
-					this->add_source(info.sfg, input_index, prefix.substr(0, info.prefix_length), pop_sfg(sfg_stack), deferred_delays);
-					this->add_instruction(instruction_type::forward_value, *result_index, 0);
-				}
-			} else if (type_name == "t") {
-				auto const delay_index = this->add_delay_info(op.attr("initial_value").cast<number>(), *result_index);
-				deferred_delays.emplace_back(delay_index, op, std::string{prefix}, sfg_stack);
-				this->add_instruction(instruction_type::push_delay, *result_index, 1).index = delay_index;
-			} else if (type_name == "sfg") {
-				auto const output_op = py::handle{op.attr("output_operations")[py::int_{output_index}]};
-				this->add_source(output_op, 0, key_base(op, prefix), push_sfg(sfg_stack, op, prefix.size()), deferred_delays);
-				this->add_instruction(instruction_type::forward_value, *result_index, 0);
-			} else {
-				auto const custom_operation_index = this->try_add_custom_operation(op);
-				auto const& custom_operation = m_code.custom_operations[custom_operation_index];
-				for (auto const i : range(custom_operation.input_count)) {
-					this->add_source(op, i, prefix, sfg_stack, deferred_delays);
-				}
-				auto const custom_source_index = m_code.custom_sources.size();
-				auto& custom_source = m_code.custom_sources.emplace_back();
-				custom_source.custom_operation_index = custom_operation_index;
-				custom_source.output_index = output_index;
-				auto const stack_diff = std::ptrdiff_t{1} - static_cast<std::ptrdiff_t>(custom_operation.input_count);
-				this->add_instruction(instruction_type::custom, *result_index, stack_diff).index = custom_source_index;
-			}
-			this->end_operation_output(op, output_index);
-		}
-	}
-
-	simulation_code m_code{};
-	added_output_cache m_incomplete_outputs{};
-	added_result_cache m_added_results{};
-	added_custom_operation_cache m_added_custom_operations{};
-	std::ptrdiff_t m_stack_depth = 0;
-};
-
-simulation_code compile_simulation(pybind11::handle sfg) {
-	return compiler{}.compile(sfg);
-}
-
-} // namespace asic
diff --git a/src/simulation/compile.hpp b/src/simulation/compile.hpp
deleted file mode 100644
index ee7fa3f18c90592428250da25a7259963ddaf2a3..0000000000000000000000000000000000000000
--- a/src/simulation/compile.hpp
+++ /dev/null
@@ -1,62 +0,0 @@
-#ifndef ASIC_SIMULATION_COMPILE_HPP
-#define ASIC_SIMULATION_COMPILE_HPP
-
-#include "instruction.hpp"
-
-#define NOMINMAX
-#include <cstddef>
-#include <pybind11/pybind11.h>
-#include <string>
-#include <vector>
-
-namespace asic {
-
-using result_key = std::string;
-
-struct simulation_code final {
-	struct custom_operation final {
-		// Python function used to evaluate the custom operation.
-		pybind11::object evaluate_output{};
-		// Number of inputs that the custom operation takes.
-		std::size_t input_count = 0;
-		// Number of outputs that the custom operation gives.
-		std::size_t output_count = 0;
-	};
-
-	struct custom_source final {
-		// Index into custom_operations where the custom_operation corresponding to this custom_source is located.
-		std::size_t custom_operation_index = 0;
-		// Output index of the custom_operation that this source gets it value from.
-		std::size_t output_index = 0;
-	};
-
-	struct delay_info final {
-		// Initial value to set at the start of the simulation.
-		number initial_value{};
-		// The result index where the current value should be stored at the start of each iteration.
-		result_index_type result_index = 0;
-	};
-
-	// Instructions to execute for one full iteration of the simulation.
-	std::vector<instruction> instructions{};
-	// Custom operations used by the simulation.
-	std::vector<custom_operation> custom_operations{};
-	// Signal sources that use custom operations.
-	std::vector<custom_source> custom_sources{};
-	// Info about the delay operations used in the simulation.
-	std::vector<delay_info> delays{};
-	// Keys for each result produced by the simulation. The index of the key matches the index of the result in the simulation state.
-	std::vector<result_key> result_keys{};
-	// Number of values expected as input to the simulation.
-	std::size_t input_count = 0;
-	// Number of values given as output from the simulation. This will be the number of values left on the stack after a full iteration of the simulation has been run.
-	std::size_t output_count = 0;
-	// Maximum number of values that need to be able to fit on the stack in order to run a full iteration of the simulation.
-	std::size_t required_stack_size = 0;
-};
-
-[[nodiscard]] simulation_code compile_simulation(pybind11::handle sfg);
-
-} // namespace asic
-
-#endif // ASIC_SIMULATION_COMPILE_HPP
diff --git a/src/simulation/format_code.hpp b/src/simulation/format_code.hpp
deleted file mode 100644
index b338c325d807068a370b59e807288a762f80aa6b..0000000000000000000000000000000000000000
--- a/src/simulation/format_code.hpp
+++ /dev/null
@@ -1,129 +0,0 @@
-#ifndef ASIC_SIMULATION_FORMAT_CODE_HPP
-#define ASIC_SIMULATION_FORMAT_CODE_HPP
-
-#include "../algorithm.hpp"
-#include "../debug.hpp"
-#include "../number.hpp"
-#include "compile.hpp"
-#include "instruction.hpp"
-
-#include <fmt/format.h>
-#include <string>
-
-namespace asic {
-
-[[nodiscard]] inline std::string format_number(number const& value) {
-	if (value.imag() == 0) {
-		return fmt::to_string(value.real());
-	}
-	if (value.real() == 0) {
-		return fmt::format("{}j", value.imag());
-	}
-	if (value.imag() < 0) {
-		return fmt::format("{}-{}j", value.real(), -value.imag());
-	}
-	return fmt::format("{}+{}j", value.real(), value.imag());
-}
-
-[[nodiscard]] inline std::string format_compiled_simulation_code_result_keys(simulation_code const& code) {
-	auto result = std::string{};
-	for (auto const& [i, result_key] : enumerate(code.result_keys)) {
-		result += fmt::format("{:>2}: \"{}\"\n", i, result_key);
-	}
-	return result;
-}
-
-[[nodiscard]] inline std::string format_compiled_simulation_code_delays(simulation_code const& code) {
-	auto result = std::string{};
-	for (auto const& [i, delay] : enumerate(code.delays)) {
-		ASIC_ASSERT(delay.result_index < code.result_keys.size());
-		result += fmt::format("{:>2}: Initial value: {}, Result: {}: \"{}\"\n",
-							  i,
-							  format_number(delay.initial_value),
-							  delay.result_index,
-							  code.result_keys[delay.result_index]);
-	}
-	return result;
-}
-
-[[nodiscard]] inline std::string format_compiled_simulation_code_instruction(instruction const& instruction) {
-	// clang-format off
-	switch (instruction.type) {
-		case instruction_type::push_input:              return fmt::format("push_input inputs[{}]", instruction.index);
-		case instruction_type::push_result:             return fmt::format("push_result results[{}]", instruction.index);
-		case instruction_type::push_delay:              return fmt::format("push_delay delays[{}]", instruction.index);
-		case instruction_type::push_constant:           return fmt::format("push_constant {}", format_number(instruction.value));
-		case instruction_type::quantize:                return fmt::format("quantize {:#018x}", instruction.bit_mask);
-		case instruction_type::addition:                return "addition";
-		case instruction_type::subtraction:             return "subtraction";
-		case instruction_type::multiplication:          return "multiplication";
-		case instruction_type::division:                return "division";
-		case instruction_type::min:                     return "min";
-		case instruction_type::max:                     return "max";
-		case instruction_type::square_root:             return "square_root";
-		case instruction_type::complex_conjugate:       return "complex_conjugate";
-		case instruction_type::absolute:                return "absolute";
-		case instruction_type::constant_multiplication: return fmt::format("constant_multiplication {}", format_number(instruction.value));
-		case instruction_type::update_delay:            return fmt::format("update_delay delays[{}]", instruction.index);
-		case instruction_type::custom:                  return fmt::format("custom custom_sources[{}]", instruction.index);
-		case instruction_type::forward_value:           return "forward_value";
-	}
-	// clang-format on
-	return std::string{};
-}
-
-[[nodiscard]] inline std::string format_compiled_simulation_code_instructions(simulation_code const& code) {
-	auto result = std::string{};
-	for (auto const& [i, instruction] : enumerate(code.instructions)) {
-		auto instruction_string = format_compiled_simulation_code_instruction(instruction);
-		if (instruction.result_index < code.result_keys.size()) {
-			instruction_string = fmt::format(
-				"{:<26} -> {}: \"{}\"", instruction_string, instruction.result_index, code.result_keys[instruction.result_index]);
-		}
-		result += fmt::format("{:>2}: {}\n", i, instruction_string);
-	}
-	return result;
-}
-
-[[nodiscard]] inline std::string format_compiled_simulation_code(simulation_code const& code) {
-	return fmt::format(
-		"==============================================\n"
-		"> Code stats\n"
-		"==============================================\n"
-		"Input count: {}\n"
-		"Output count: {}\n"
-		"Instruction count: {}\n"
-		"Required stack size: {}\n"
-		"Delay count: {}\n"
-		"Result count: {}\n"
-		"Custom operation count: {}\n"
-		"Custom source count: {}\n"
-		"==============================================\n"
-		"> Delays\n"
-		"==============================================\n"
-		"{}"
-		"==============================================\n"
-		"> Result keys\n"
-		"==============================================\n"
-		"{}"
-		"==============================================\n"
-		"> Instructions\n"
-		"==============================================\n"
-		"{}"
-		"==============================================",
-		code.input_count,
-		code.output_count,
-		code.instructions.size(),
-		code.required_stack_size,
-		code.delays.size(),
-		code.result_keys.size(),
-		code.custom_operations.size(),
-		code.custom_sources.size(),
-		format_compiled_simulation_code_delays(code),
-		format_compiled_simulation_code_result_keys(code),
-		format_compiled_simulation_code_instructions(code));
-}
-
-} // namespace asic
-
-#endif // ASIC_SIMULATION_FORMAT_CODE_HPP
diff --git a/src/simulation/instruction.hpp b/src/simulation/instruction.hpp
deleted file mode 100644
index 9b03d9e0bf8419166483f60e4d3b9a97efb7e875..0000000000000000000000000000000000000000
--- a/src/simulation/instruction.hpp
+++ /dev/null
@@ -1,55 +0,0 @@
-#ifndef ASIC_SIMULATION_INSTRUCTION_HPP
-#define ASIC_SIMULATION_INSTRUCTION_HPP
-
-#include "../number.hpp"
-
-#include <cstddef>
-#include <cstdint>
-#include <optional>
-
-namespace asic {
-
-enum class instruction_type : std::uint8_t {
-	push_input,              // push(inputs[index])
-	push_result,             // push(results[index])
-	push_delay,              // push(delays[index])
-	push_constant,           // push(value)
-	quantize,                // push(trunc(pop(), bit_mask))
-	addition,                // rhs=pop(), lhs=pop(), push(lhs + rhs)
-	subtraction,             // rhs=pop(), lhs=pop(), push(lhs - rhs)
-	multiplication,          // rhs=pop(), lhs=pop(), push(lhs * rhs)
-	division,                // rhs=pop(), lhs=pop(), push(lhs / rhs)
-	min,                     // rhs=pop(), lhs=pop(), push(min(lhs, rhs))
-	max,                     // rhs=pop(), lhs=pop(), push(max(lhs, rhs))
-	square_root,             // push(sqrt(pop()))
-	complex_conjugate,       // push(conj(pop()))
-	absolute,                // push(abs(pop()))
-	constant_multiplication, // push(pop() * value)
-	update_delay,            // delays[index] = pop()
-	custom,                  // Custom operation. Uses custom_source[index].
-	forward_value            // Forward the current value on the stack (push(pop()), i.e. do nothing).
-};
-
-using result_index_type = std::uint16_t;
-
-struct instruction final {
-	constexpr instruction() noexcept // NOLINT(cppcoreguidelines-pro-type-member-init)
-		: index(0) {}
-
-	union {
-		// Index used by push_input, push_result, delay and custom.
-		std::size_t index;
-		// Bit mask used by quantize.
-		std::int64_t bit_mask;
-		// Constant value used by push_constant and constant_multiplication.
-		number value;
-	};
-	// Index into where the result of the instruction will be stored. If the result should be ignored, this index will be one past the last valid result index.
-	result_index_type result_index = 0;
-	// Specifies what kind of operation the instruction should execute.
-	instruction_type type = instruction_type::forward_value;
-};
-
-} // namespace asic
-
-#endif // ASIC_SIMULATION_INSTRUCTION_HPP
diff --git a/src/simulation/run.cpp b/src/simulation/run.cpp
deleted file mode 100644
index 1998b82ecbcc017961f13f0c1b98fd039b2e7467..0000000000000000000000000000000000000000
--- a/src/simulation/run.cpp
+++ /dev/null
@@ -1,188 +0,0 @@
-#include "run.hpp"
-
-#include "../algorithm.hpp"
-#include "../debug.hpp"
-#include "format_code.hpp"
-
-#define NOMINMAX
-#include <algorithm>
-#include <complex>
-#include <cstddef>
-#include <fmt/format.h>
-#include <iterator>
-#include <pybind11/pybind11.h>
-#include <pybind11/stl.h>
-#include <stdexcept>
-
-namespace py = pybind11;
-
-namespace asic {
-
-[[nodiscard]] static number quantize_value(number value, std::int64_t bit_mask) {
-	if (value.imag() != 0) {
-		throw py::type_error{"Complex value cannot be quantized"};
-	}
-	return number{static_cast<number::value_type>(static_cast<std::int64_t>(value.real()) & bit_mask)};
-}
-
-[[nodiscard]] static std::int64_t setup_truncation_parameters(bool& quantize, std::optional<std::uint8_t>& bits_override) {
-	if (quantize && bits_override) {
-		quantize = false; // Ignore quantize instructions, they will be quantized using bits_override instead.
-		if (*bits_override > 64) {
-			throw py::value_error{"Cannot quantize to more than 64 bits"};
-		}
-		return static_cast<std::int64_t>((std::int64_t{1} << *bits_override) - 1); // Return the bit mask override to use.
-	}
-	bits_override.reset(); // Don't use bits_override if quantize is false.
-	return std::int64_t{};
-}
-
-simulation_state run_simulation(simulation_code const& code, span<number const> inputs, span<number> delays,
-								std::optional<std::uint8_t> bits_override, bool quantize) {
-	ASIC_ASSERT(inputs.size() == code.input_count);
-	ASIC_ASSERT(delays.size() == code.delays.size());
-	ASIC_ASSERT(code.output_count <= code.required_stack_size);
-
-	auto state = simulation_state{};
-
-	// Setup results.
-	state.results.resize(code.result_keys.size() + 1); // Add one space to store ignored results.
-	// Initialize delay results to their current values.
-	for (auto const& [i, delay] : enumerate(code.delays)) {
-		state.results[delay.result_index] = delays[i];
-	}
-
-	// Setup stack.
-	state.stack.resize(code.required_stack_size);
-	auto* stack_pointer = state.stack.data();
-
-	// Utility functions to make the stack manipulation code below more readable.
-	// Should hopefully be inlined by the compiler.
-	auto const push = [&](number value) -> void {
-		ASIC_ASSERT(std::distance(state.stack.data(), stack_pointer) < static_cast<std::ptrdiff_t>(state.stack.size()));
-		*stack_pointer++ = value;
-	};
-	auto const pop = [&]() -> number {
-		ASIC_ASSERT(std::distance(state.stack.data(), stack_pointer) > std::ptrdiff_t{0});
-		return *--stack_pointer;
-	};
-	auto const peek = [&]() -> number {
-		ASIC_ASSERT(std::distance(state.stack.data(), stack_pointer) > std::ptrdiff_t{0});
-		ASIC_ASSERT(std::distance(state.stack.data(), stack_pointer) <= static_cast<std::ptrdiff_t>(state.stack.size()));
-		return *(stack_pointer - 1);
-	};
-
-	// Check if results should be quantized.
-	auto const bit_mask_override = setup_truncation_parameters(quantize, bits_override);
-
-	// Hot instruction evaluation loop.
-	for (auto const& instruction : code.instructions) {
-		ASIC_DEBUG_MSG("Evaluating {}.", format_compiled_simulation_code_instruction(instruction));
-		// Execute the instruction.
-		switch (instruction.type) {
-			case instruction_type::push_input:
-				push(inputs[instruction.index]);
-				break;
-			case instruction_type::push_result:
-				push(state.results[instruction.index]);
-				break;
-			case instruction_type::push_delay:
-				push(delays[instruction.index]);
-				break;
-			case instruction_type::push_constant:
-				push(instruction.value);
-				break;
-			case instruction_type::quantize:
-				if (quantize) {
-					push(quantize_value(pop(), instruction.bit_mask));
-				}
-				break;
-			case instruction_type::addition: {
-				auto const rhs = pop();
-				auto const lhs = pop();
-				push(lhs + rhs);
-				break;
-			}
-			case instruction_type::subtraction: {
-				auto const rhs = pop();
-				auto const lhs = pop();
-				push(lhs - rhs);
-				break;
-			}
-			case instruction_type::multiplication: {
-				auto const rhs = pop();
-				auto const lhs = pop();
-				push(lhs * rhs);
-				break;
-			}
-			case instruction_type::division: {
-				auto const rhs = pop();
-				auto const lhs = pop();
-				push(lhs / rhs);
-				break;
-			}
-			case instruction_type::min: {
-				auto const rhs = pop();
-				auto const lhs = pop();
-				if (lhs.imag() != 0 || rhs.imag() != 0) {
-					throw std::runtime_error{"Min does not support complex numbers."};
-				}
-				push(std::min(lhs.real(), rhs.real()));
-				break;
-			}
-			case instruction_type::max: {
-				auto const rhs = pop();
-				auto const lhs = pop();
-				if (lhs.imag() != 0 || rhs.imag() != 0) {
-					throw std::runtime_error{"Max does not support complex numbers."};
-				}
-				push(std::max(lhs.real(), rhs.real()));
-				break;
-			}
-			case instruction_type::square_root:
-				push(std::sqrt(pop()));
-				break;
-			case instruction_type::complex_conjugate:
-				push(std::conj(pop()));
-				break;
-			case instruction_type::absolute:
-				push(number{std::abs(pop())});
-				break;
-			case instruction_type::constant_multiplication:
-				push(pop() * instruction.value);
-				break;
-			case instruction_type::update_delay:
-				delays[instruction.index] = pop();
-				break;
-			case instruction_type::custom: {
-				using namespace pybind11::literals;
-				auto const& src = code.custom_sources[instruction.index];
-				auto const& op = code.custom_operations[src.custom_operation_index];
-				auto input_values = std::vector<number>{};
-				input_values.reserve(op.input_count);
-				for (auto i = std::size_t{0}; i < op.input_count; ++i) {
-					input_values.push_back(pop());
-				}
-				push(op.evaluate_output(src.output_index, std::move(input_values), "quantize"_a = quantize).cast<number>());
-				break;
-			}
-			case instruction_type::forward_value:
-				// Do nothing, since doing push(pop()) would be pointless.
-				break;
-		}
-		// If we've been given a global override for how many bits to use, always quantize the result.
-		if (bits_override) {
-			push(quantize_value(pop(), bit_mask_override));
-		}
-		// Store the result.
-		state.results[instruction.result_index] = peek();
-	}
-
-	// Remove the space that we used for ignored results.
-	state.results.pop_back();
-	// Erase the portion of the stack that does not contain the output values.
-	state.stack.erase(state.stack.begin() + static_cast<std::ptrdiff_t>(code.output_count), state.stack.end());
-	return state;
-}
-
-} // namespace asic
diff --git a/src/simulation/run.hpp b/src/simulation/run.hpp
deleted file mode 100644
index 7cc05e5fa68709cfaac662948e751afadd7d37e3..0000000000000000000000000000000000000000
--- a/src/simulation/run.hpp
+++ /dev/null
@@ -1,23 +0,0 @@
-#ifndef ASIC_SIMULATION_RUN_HPP
-#define ASIC_SIMULATION_RUN_HPP
-
-#include "../number.hpp"
-#include "../span.hpp"
-#include "compile.hpp"
-
-#include <cstdint>
-#include <vector>
-
-namespace asic {
-
-struct simulation_state final {
-	std::vector<number> stack{};
-	std::vector<number> results{};
-};
-
-simulation_state run_simulation(simulation_code const& code, span<number const> inputs, span<number> delays,
-								std::optional<std::uint8_t> bits_override, bool quantize);
-
-} // namespace asic
-
-#endif // ASIC_SIMULATION_RUN_HPP
diff --git a/src/simulation/simulation.cpp b/src/simulation/simulation.cpp
deleted file mode 100644
index 34ac320fbde53729a45769efcac806a4f449aac4..0000000000000000000000000000000000000000
--- a/src/simulation/simulation.cpp
+++ /dev/null
@@ -1,130 +0,0 @@
-#include "simulation.hpp"
-
-#include "../algorithm.hpp"
-#include "../debug.hpp"
-#include "compile.hpp"
-#include "run.hpp"
-
-#define NOMINMAX
-#include <fmt/format.h>
-#include <limits>
-#include <pybind11/numpy.h>
-#include <utility>
-
-namespace py = pybind11;
-
-namespace asic {
-
-simulation::simulation(pybind11::handle sfg, std::optional<std::vector<std::optional<input_provider_type>>> input_providers)
-	: m_code(compile_simulation(sfg))
-	, m_input_functions(sfg.attr("input_count").cast<std::size_t>(), [](iteration_type) -> number { return number{}; }) {
-	m_delays.reserve(m_code.delays.size());
-	for (auto const& delay : m_code.delays) {
-		m_delays.push_back(delay.initial_value);
-	}
-	if (input_providers) {
-		this->set_inputs(std::move(*input_providers));
-	}
-}
-
-void simulation::set_input(std::size_t index, input_provider_type input_provider) {
-	if (index >= m_input_functions.size()) {
-		throw py::index_error{fmt::format("Input index out of range (expected 0-{}, got {})", m_input_functions.size() - 1, index)};
-	}
-	if (auto* const callable = std::get_if<input_function_type>(&input_provider)) {
-		m_input_functions[index] = std::move(*callable);
-	} else if (auto* const numeric = std::get_if<number>(&input_provider)) {
-		m_input_functions[index] = [value = *numeric](iteration_type) -> number {
-			return value;
-		};
-	} else if (auto* const list = std::get_if<std::vector<number>>(&input_provider)) {
-		if (!m_input_length) {
-			m_input_length = static_cast<iteration_type>(list->size());
-		} else if (*m_input_length != static_cast<iteration_type>(list->size())) {
-			throw py::value_error{fmt::format("Inconsistent input length for simulation (was {}, got {})", *m_input_length, list->size())};
-		}
-		m_input_functions[index] = [values = std::move(*list)](iteration_type n) -> number {
-			return values.at(n);
-		};
-	}
-}
-
-void simulation::set_inputs(
-	std::vector<std::optional<input_provider_type>> input_providers) { // NOLINT(performance-unnecessary-value-param)
-	if (input_providers.size() != m_input_functions.size()) {
-		throw py::value_error{fmt::format(
-			"Wrong number of inputs supplied to simulation (expected {}, got {})", m_input_functions.size(), input_providers.size())};
-	}
-	for (auto&& [i, input_provider] : enumerate(input_providers)) {
-		if (input_provider) {
-			this->set_input(i, std::move(*input_provider));
-		}
-	}
-}
-
-std::vector<number> simulation::step(bool save_results, std::optional<std::uint8_t> bits_override, bool quantize) {
-	return this->run_for(1, save_results, bits_override, quantize);
-}
-
-std::vector<number> simulation::run_until(iteration_type iteration, bool save_results, std::optional<std::uint8_t> bits_override,
-										  bool quantize) {
-	auto result = std::vector<number>{};
-	while (m_iteration < iteration) {
-		ASIC_DEBUG_MSG("Running simulation iteration.");
-		auto inputs = std::vector<number>(m_code.input_count);
-		for (auto&& [input, function] : zip(inputs, m_input_functions)) {
-			input = function(m_iteration);
-		}
-		auto state = run_simulation(m_code, inputs, m_delays, bits_override, quantize);
-		result = std::move(state.stack);
-		if (save_results) {
-			m_results.push_back(std::move(state.results));
-		}
-		++m_iteration;
-	}
-	return result;
-}
-
-std::vector<number> simulation::run_for(iteration_type iterations, bool save_results, std::optional<std::uint8_t> bits_override,
-										bool quantize) {
-	if (iterations > std::numeric_limits<iteration_type>::max() - m_iteration) {
-		throw py::value_error("Simulation iteration type overflow!");
-	}
-	return this->run_until(m_iteration + iterations, save_results, bits_override, quantize);
-}
-
-std::vector<number> simulation::run(bool save_results, std::optional<std::uint8_t> bits_override, bool quantize) {
-	if (m_input_length) {
-		return this->run_until(*m_input_length, save_results, bits_override, quantize);
-	}
-	throw py::index_error{"Tried to run unlimited simulation"};
-}
-
-iteration_type simulation::iteration() const noexcept {
-	return m_iteration;
-}
-
-pybind11::dict simulation::results() const noexcept {
-	auto results = py::dict{};
-	if (!m_results.empty()) {
-		for (auto const& [i, key] : enumerate(m_code.result_keys)) {
-			auto values = std::vector<number>{};
-			values.reserve(m_results.size());
-			for (auto const& result : m_results) {
-				values.push_back(result[i]);
-			}
-			results[py::str{key}] = py::array{static_cast<py::ssize_t>(values.size()), values.data()};
-		}
-	}
-	return results;
-}
-
-void simulation::clear_results() noexcept {
-	m_results.clear();
-}
-
-void simulation::clear_state() noexcept {
-	m_delays.clear();
-}
-
-} // namespace asic
diff --git a/src/simulation/simulation.hpp b/src/simulation/simulation.hpp
deleted file mode 100644
index ec930e83a13400ebaf0566154d06a06c8f0d0901..0000000000000000000000000000000000000000
--- a/src/simulation/simulation.hpp
+++ /dev/null
@@ -1,55 +0,0 @@
-#ifndef ASIC_SIMULATION_DOD_HPP
-#define ASIC_SIMULATION_DOD_HPP
-
-#include "../number.hpp"
-#include "compile.hpp"
-
-#define NOMINMAX
-#include <cstddef>
-#include <cstdint>
-#include <functional>
-#include <optional>
-#include <pybind11/functional.h>
-#include <pybind11/pybind11.h>
-#include <pybind11/stl.h>
-#include <variant>
-#include <vector>
-
-namespace asic {
-
-using iteration_type = std::uint32_t;
-using input_function_type = std::function<number(iteration_type)>;
-using input_provider_type = std::variant<number, std::vector<number>, input_function_type>;
-
-class simulation final {
-public:
-	simulation(pybind11::handle sfg, std::optional<std::vector<std::optional<input_provider_type>>> input_providers = std::nullopt);
-
-	void set_input(std::size_t index, input_provider_type input_provider);
-	void set_inputs(std::vector<std::optional<input_provider_type>> input_providers);
-
-	[[nodiscard]] std::vector<number> step(bool save_results, std::optional<std::uint8_t> bits_override, bool quantize);
-	[[nodiscard]] std::vector<number> run_until(iteration_type iteration, bool save_results, std::optional<std::uint8_t> bits_override,
-												bool quantize);
-	[[nodiscard]] std::vector<number> run_for(iteration_type iterations, bool save_results, std::optional<std::uint8_t> bits_override,
-											  bool quantize);
-	[[nodiscard]] std::vector<number> run(bool save_results, std::optional<std::uint8_t> bits_override, bool quantize);
-
-	[[nodiscard]] iteration_type iteration() const noexcept;
-	[[nodiscard]] pybind11::dict results() const noexcept;
-
-	void clear_results() noexcept;
-	void clear_state() noexcept;
-
-private:
-	simulation_code m_code;
-	std::vector<input_function_type> m_input_functions;
-	std::vector<number> m_delays{};
-	std::optional<iteration_type> m_input_length{};
-	iteration_type m_iteration = 0;
-	std::vector<std::vector<number>> m_results{};
-};
-
-} // namespace asic
-
-#endif // ASIC_SIMULATION_DOD_HPP
diff --git a/src/span.hpp b/src/span.hpp
deleted file mode 100644
index 3ccc34eaa9bd485d84808ff812d52e74a98c53f4..0000000000000000000000000000000000000000
--- a/src/span.hpp
+++ /dev/null
@@ -1,362 +0,0 @@
-#ifndef ASIC_SPAN_HPP
-#define ASIC_SPAN_HPP
-
-#include <algorithm>
-#include <array>
-#include <cassert>
-#include <cstddef>
-#include <iterator>
-#include <limits>
-#include <type_traits>
-#include <utility>
-
-namespace asic {
-
-constexpr auto dynamic_size = static_cast<std::size_t>(-1);
-
-// C++17-compatible std::span substitute.
-template <typename T, std::size_t Size = dynamic_size>
-class span;
-
-namespace detail {
-
-template <typename T>
-struct is_span_impl : std::false_type {};
-
-template <typename T, std::size_t Size>
-struct is_span_impl<span<T, Size>> : std::true_type {};
-
-template <typename T>
-struct is_span : is_span_impl<std::remove_cv_t<T>> {};
-
-template <typename T>
-constexpr auto is_span_v = is_span<T>::value;
-
-template <typename T>
-struct is_std_array_impl : std::false_type {};
-
-template <typename T, std::size_t Size>
-struct is_std_array_impl<std::array<T, Size>> : std::true_type {};
-
-template <typename T>
-struct is_std_array : is_std_array_impl<std::remove_cv_t<T>> {};
-
-template <typename T>
-constexpr auto is_std_array_v = is_std_array<T>::value;
-
-template <std::size_t From, std::size_t To>
-struct is_size_convertible : std::bool_constant<From == To || From == dynamic_size || To == dynamic_size> {};
-
-template <std::size_t From, std::size_t To>
-constexpr auto is_size_convertible_v = is_size_convertible<From, To>::value;
-
-template <typename From, typename To>
-struct is_element_type_convertible : std::bool_constant<std::is_convertible_v<From (*)[], To (*)[]>> {};
-
-template <typename From, typename To>
-constexpr auto is_element_type_convertible_v = is_element_type_convertible<From, To>::value;
-
-template <typename T, std::size_t Size>
-struct span_base {
-	using element_type = T;
-	using pointer = element_type*;
-	using size_type = std::size_t;
-
-	constexpr span_base() noexcept = default;
-
-	constexpr span_base(pointer data, [[maybe_unused]] size_type size)
-		: m_data(data) {
-		assert(size == Size);
-	}
-
-	template <size_type N>
-	constexpr span_base(span_base<T, N> other)
-		: m_data(other.data()) {
-		static_assert(N == Size || N == dynamic_size);
-		assert(other.size() == Size);
-	}
-
-	[[nodiscard]] constexpr pointer data() const noexcept {
-		return m_data;
-	}
-
-	[[nodiscard]] constexpr size_type size() const noexcept {
-		return Size;
-	}
-
-private:
-	pointer m_data = nullptr;
-};
-
-template <typename T>
-struct span_base<T, dynamic_size> {
-	using element_type = T;
-	using pointer = element_type*;
-	using size_type = std::size_t;
-
-	constexpr span_base() noexcept = default;
-
-	constexpr span_base(pointer data, size_type size)
-		: m_data(data)
-		, m_size(size) {}
-
-	template <size_type N>
-	explicit constexpr span_base(span_base<T, N> other)
-		: m_data(other.data())
-		, m_size(other.size()) {}
-
-	[[nodiscard]] constexpr pointer data() const noexcept {
-		return m_data;
-	}
-
-	[[nodiscard]] constexpr size_type size() const noexcept {
-		return m_size;
-	}
-
-private:
-	pointer m_data = nullptr;
-	size_type m_size = 0;
-};
-
-template <typename T, std::size_t Size, std::size_t Offset, std::size_t N>
-struct subspan_type {
-	using type = span<T, (N != dynamic_size) ? N : (Size != dynamic_size) ? Size - Offset : Size>;
-};
-
-template <typename T, std::size_t Size, std::size_t Offset, std::size_t Count>
-using subspan_type_t = typename subspan_type<T, Size, Offset, Count>::type;
-
-} // namespace detail
-
-template <typename T, std::size_t Size>
-class span final : public detail::span_base<T, Size> { // NOLINT(cppcoreguidelines-special-member-functions)
-public:
-	using element_type = typename detail::span_base<T, Size>::element_type;
-	using pointer = typename detail::span_base<T, Size>::pointer;
-	using size_type = typename detail::span_base<T, Size>::size_type;
-	using value_type = std::remove_cv_t<element_type>;
-	using reference = element_type&;
-	using iterator = element_type*;
-	using const_iterator = const element_type*;
-	using reverse_iterator = std::reverse_iterator<iterator>;
-	using const_reverse_iterator = std::reverse_iterator<const_iterator>;
-
-	// Default constructor.
-	constexpr span() noexcept = default;
-
-	// Construct from pointer, size.
-	constexpr span(pointer data, size_type size)
-		: detail::span_base<T, Size>(data, size) {}
-
-	// Copy constructor.
-	template <typename U, std::size_t N, typename = std::enable_if_t<detail::is_size_convertible_v<N, Size>>,
-			  typename = std::enable_if_t<detail::is_element_type_convertible_v<U, T>>>
-	constexpr span(span<U, N> const& other)
-		: span(other.data(), other.size()) {}
-
-	// Copy assignment.
-	constexpr span& operator=(span const&) noexcept = default;
-
-	// Destructor.
-	~span() = default;
-
-	// Construct from begin, end.
-	constexpr span(pointer begin, pointer end)
-		: span(begin, end - begin) {}
-
-	// Construct from C array.
-	template <std::size_t N>
-	constexpr span(element_type (&arr)[N]) noexcept
-		: span(std::data(arr), N) {}
-
-	// Construct from std::array.
-	template <std::size_t N, typename = std::enable_if_t<N != 0>>
-	constexpr span(std::array<value_type, N>& arr) noexcept
-		: span(std::data(arr), N) {}
-
-	// Construct from empty std::array.
-	constexpr span(std::array<value_type, 0>&) noexcept
-		: span() {}
-
-	// Construct from const std::array.
-	template <std::size_t N, typename = std::enable_if_t<N != 0>>
-	constexpr span(std::array<value_type, N> const& arr) noexcept
-		: span(std::data(arr), N) {}
-
-	// Construct from empty const std::array.
-	constexpr span(std::array<value_type, 0> const&) noexcept
-		: span() {}
-
-	// Construct from other container.
-	template <
-		typename Container, typename = std::enable_if_t<!detail::is_span_v<Container>>,
-		typename = std::enable_if_t<!detail::is_std_array_v<Container>>, typename = decltype(std::data(std::declval<Container>())),
-		typename = decltype(std::size(std::declval<Container>())),
-		typename = std::enable_if_t<std::is_convertible_v<typename Container::pointer, pointer>>,
-		typename = std::enable_if_t<std::is_convertible_v<typename Container::pointer, decltype(std::data(std::declval<Container>()))>>>
-	constexpr span(Container& container)
-		: span(std::data(container), std::size(container)) {}
-
-	// Construct from other const container.
-	template <
-		typename Container, typename Element = element_type, typename = std::enable_if_t<std::is_const_v<Element>>,
-		typename = std::enable_if_t<!detail::is_span_v<Container>>, typename = std::enable_if_t<!detail::is_std_array_v<Container>>,
-		typename = decltype(std::data(std::declval<Container>())), typename = decltype(std::size(std::declval<Container>())),
-		typename = std::enable_if_t<std::is_convertible_v<typename Container::pointer, pointer>>,
-		typename = std::enable_if_t<std::is_convertible_v<typename Container::pointer, decltype(std::data(std::declval<Container>()))>>>
-	constexpr span(Container const& container)
-		: span(std::data(container), std::size(container)) {}
-
-	[[nodiscard]] constexpr iterator begin() const noexcept {
-		return this->data();
-	}
-
-	[[nodiscard]] constexpr const_iterator cbegin() const noexcept {
-		return this->data();
-	}
-
-	[[nodiscard]] constexpr iterator end() const noexcept {
-		return this->data() + this->size();
-	}
-
-	[[nodiscard]] constexpr const_iterator cend() const noexcept {
-		return this->data() + this->size();
-	}
-
-	[[nodiscard]] constexpr reverse_iterator rbegin() const noexcept {
-		return std::make_reverse_iterator(this->end());
-	}
-
-	[[nodiscard]] constexpr const_reverse_iterator crbegin() const noexcept {
-		return std::make_reverse_iterator(this->cend());
-	}
-
-	[[nodiscard]] constexpr reverse_iterator rend() const noexcept {
-		return std::make_reverse_iterator(this->begin());
-	}
-
-	[[nodiscard]] constexpr const_reverse_iterator crend() const noexcept {
-		return std::make_reverse_iterator(this->cbegin());
-	}
-
-	[[nodiscard]] constexpr reference operator[](size_type i) const noexcept {
-		assert(i < this->size());
-		return this->data()[i];
-	}
-
-	[[nodiscard]] constexpr reference operator()(size_type i) const noexcept {
-		assert(i < this->size());
-		return this->data()[i];
-	}
-
-	[[nodiscard]] constexpr size_type size_bytes() const noexcept {
-		return this->size() * sizeof(element_type);
-	}
-
-	[[nodiscard]] constexpr bool empty() const noexcept {
-		return this->size() == 0;
-	}
-
-	[[nodiscard]] constexpr reference front() const noexcept {
-		assert(!this->empty());
-		return this->data()[0];
-	}
-
-	[[nodiscard]] constexpr reference back() const noexcept {
-		assert(!this->empty());
-		return this->data()[this->size() - 1];
-	}
-
-	template <std::size_t N>
-	[[nodiscard]] constexpr span<T, N> first() const {
-		static_assert(N != dynamic_size && N <= Size);
-		return {this->data(), N};
-	}
-
-	template <std::size_t N>
-	[[nodiscard]] constexpr span<T, N> last() const {
-		static_assert(N != dynamic_size && N <= Size);
-		return {this->data() + (Size - N), N};
-	}
-
-	template <std::size_t Offset, std::size_t N = dynamic_size>
-	[[nodiscard]] constexpr auto subspan() const -> detail::subspan_type_t<T, Size, Offset, N> {
-		static_assert(Offset <= Size);
-		return {this->data() + Offset, (N == dynamic_size) ? this->size() - Offset : N};
-	}
-
-	[[nodiscard]] constexpr span<T, dynamic_size> first(size_type n) const {
-		assert(n <= this->size());
-		return {this->data(), n};
-	}
-
-	[[nodiscard]] constexpr span<T, dynamic_size> last(size_type n) const {
-		return this->subspan(this->size() - n);
-	}
-
-	[[nodiscard]] constexpr span<T, dynamic_size> subspan(size_type offset, size_type n = dynamic_size) const {
-		if constexpr (Size == dynamic_size) {
-			assert(offset <= this->size());
-			if (n == dynamic_size) {
-				return {this->data() + offset, this->size() - offset};
-			}
-			assert(n <= this->size());
-			assert(offset + n <= this->size());
-			return {this->data() + offset, n};
-		} else {
-			return span<T, dynamic_size>{*this}.subspan(offset, n);
-		}
-	}
-};
-
-template <typename T, std::size_t LhsSize, std::size_t RhsSize>
-[[nodiscard]] constexpr bool operator==(span<T, LhsSize> lhs, span<T, RhsSize> rhs) {
-	return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
-}
-
-template <typename T, std::size_t LhsSize, std::size_t RhsSize>
-[[nodiscard]] constexpr bool operator!=(span<T, LhsSize> lhs, span<T, RhsSize> rhs) {
-	return !(lhs == rhs);
-}
-
-template <typename T, std::size_t LhsSize, std::size_t RhsSize>
-[[nodiscard]] constexpr bool operator<(span<T, LhsSize> lhs, span<T, RhsSize> rhs) {
-	return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
-}
-
-template <typename T, std::size_t LhsSize, std::size_t RhsSize>
-[[nodiscard]] constexpr bool operator<=(span<T, LhsSize> lhs, span<T, RhsSize> rhs) {
-	return !(lhs > rhs);
-}
-
-template <typename T, std::size_t LhsSize, std::size_t RhsSize>
-[[nodiscard]] constexpr bool operator>(span<T, LhsSize> lhs, span<T, RhsSize> rhs) {
-	return rhs < lhs;
-}
-
-template <typename T, std::size_t LhsSize, std::size_t RhsSize>
-[[nodiscard]] constexpr bool operator>=(span<T, LhsSize> lhs, span<T, RhsSize> rhs) {
-	return !(lhs < rhs);
-}
-
-template <typename Container>
-span(Container&) -> span<typename Container::value_type>;
-
-template <typename Container>
-span(Container const&) -> span<typename Container::value_type const>;
-
-template <typename T, std::size_t N>
-span(T (&)[N]) -> span<T, N>;
-
-template <typename T, std::size_t N>
-span(std::array<T, N>&) -> span<T, N>;
-
-template <typename T, std::size_t N>
-span(std::array<T, N> const&) -> span<T const, N>;
-
-template <typename T, typename Dummy>
-span(T, Dummy &&) -> span<std::remove_reference_t<decltype(std::declval<T>()[0])>>;
-
-} // namespace asic
-
-#endif // ASIC_SPAN_HPP
diff --git a/test/test_fast_simulation.py b/test/test_fast_simulation.py
deleted file mode 100644
index d27738473bdcfabedce5e8f4bcabcd8d3590ef4a..0000000000000000000000000000000000000000
--- a/test/test_fast_simulation.py
+++ /dev/null
@@ -1,277 +0,0 @@
-import numpy as np
-import pytest
-
-from b_asic import (
-    SFG,
-    Addition,
-    Butterfly,
-    Constant,
-    FastSimulation,
-    Output,
-    Subtraction,
-)
-
-
-class TestRunFor:
-    def test_with_lambdas_as_input(self, sfg_two_inputs_two_outputs):
-        simulation = FastSimulation(
-            sfg_two_inputs_two_outputs, [lambda n: n + 3, lambda n: 1 + n * 2]
-        )
-
-        output = simulation.run_for(101, save_results=True)
-
-        assert output[0] == 304
-        assert output[1] == 505
-
-        assert simulation.results["0"][100] == 304
-        assert simulation.results["1"][100] == 505
-
-        assert simulation.results["in1"][0] == 3
-        assert simulation.results["in2"][0] == 1
-        assert simulation.results["add1"][0] == 4
-        assert simulation.results["add2"][0] == 5
-        assert simulation.results["0"][0] == 4
-        assert simulation.results["1"][0] == 5
-
-        assert simulation.results["in1"][1] == 4
-        assert simulation.results["in2"][1] == 3
-        assert simulation.results["add1"][1] == 7
-        assert simulation.results["add2"][1] == 10
-        assert simulation.results["0"][1] == 7
-        assert simulation.results["1"][1] == 10
-
-        assert simulation.results["in1"][2] == 5
-        assert simulation.results["in2"][2] == 5
-        assert simulation.results["add1"][2] == 10
-        assert simulation.results["add2"][2] == 15
-        assert simulation.results["0"][2] == 10
-        assert simulation.results["1"][2] == 15
-
-        assert simulation.results["in1"][3] == 6
-        assert simulation.results["in2"][3] == 7
-        assert simulation.results["add1"][3] == 13
-        assert simulation.results["add2"][3] == 20
-        assert simulation.results["0"][3] == 13
-        assert simulation.results["1"][3] == 20
-
-    def test_with_numpy_arrays_as_input(self, sfg_two_inputs_two_outputs):
-        input0 = np.array([5, 9, 25, -5, 7])
-        input1 = np.array([7, 3, 3, 54, 2])
-        simulation = FastSimulation(
-            sfg_two_inputs_two_outputs, [input0, input1]
-        )
-
-        output = simulation.run_for(5, save_results=True)
-
-        assert output[0] == 9
-        assert output[1] == 11
-
-        assert isinstance(simulation.results["in1"], np.ndarray)
-        assert isinstance(simulation.results["in2"], np.ndarray)
-        assert isinstance(simulation.results["add1"], np.ndarray)
-        assert isinstance(simulation.results["add2"], np.ndarray)
-        assert isinstance(simulation.results["0"], np.ndarray)
-        assert isinstance(simulation.results["1"], np.ndarray)
-
-        assert simulation.results["in1"][0] == 5
-        assert simulation.results["in2"][0] == 7
-        assert simulation.results["add1"][0] == 12
-        assert simulation.results["add2"][0] == 19
-        assert simulation.results["0"][0] == 12
-        assert simulation.results["1"][0] == 19
-
-        assert simulation.results["in1"][1] == 9
-        assert simulation.results["in2"][1] == 3
-        assert simulation.results["add1"][1] == 12
-        assert simulation.results["add2"][1] == 15
-        assert simulation.results["0"][1] == 12
-        assert simulation.results["1"][1] == 15
-
-        assert simulation.results["in1"][2] == 25
-        assert simulation.results["in2"][2] == 3
-        assert simulation.results["add1"][2] == 28
-        assert simulation.results["add2"][2] == 31
-        assert simulation.results["0"][2] == 28
-        assert simulation.results["1"][2] == 31
-
-        assert simulation.results["in1"][3] == -5
-        assert simulation.results["in2"][3] == 54
-        assert simulation.results["add1"][3] == 49
-        assert simulation.results["add2"][3] == 103
-        assert simulation.results["0"][3] == 49
-        assert simulation.results["1"][3] == 103
-
-        assert simulation.results["0"][4] == 9
-        assert simulation.results["1"][4] == 11
-
-    def test_with_numpy_array_overflow(self, sfg_two_inputs_two_outputs):
-        input0 = np.array([5, 9, 25, -5, 7])
-        input1 = np.array([7, 3, 3, 54, 2])
-        simulation = FastSimulation(
-            sfg_two_inputs_two_outputs, [input0, input1]
-        )
-        simulation.run_for(5)
-        with pytest.raises(IndexError):
-            simulation.step()
-
-    def test_run_whole_numpy_array(self, sfg_two_inputs_two_outputs):
-        input0 = np.array([5, 9, 25, -5, 7])
-        input1 = np.array([7, 3, 3, 54, 2])
-        simulation = FastSimulation(
-            sfg_two_inputs_two_outputs, [input0, input1]
-        )
-        simulation.run()
-        assert len(simulation.results["0"]) == 5
-        assert len(simulation.results["1"]) == 5
-        with pytest.raises(IndexError):
-            simulation.step()
-
-    def test_delay(self, sfg_delay):
-        simulation = FastSimulation(sfg_delay)
-        simulation.set_input(0, [5, -2, 25, -6, 7, 0])
-        simulation.run_for(6, save_results=True)
-
-        assert simulation.results["0"][0] == 0
-        assert simulation.results["0"][1] == 5
-        assert simulation.results["0"][2] == -2
-        assert simulation.results["0"][3] == 25
-        assert simulation.results["0"][4] == -6
-        assert simulation.results["0"][5] == 7
-
-    def test_find_result_key(self, precedence_sfg_delays):
-        sim = FastSimulation(
-            precedence_sfg_delays,
-            [[0, 4, 542, 42, 31.314, 534.123, -453415, 5431]],
-        )
-        sim.run()
-        assert (
-            sim.results[
-                precedence_sfg_delays.find_result_keys_by_name("ADD2")[0]
-            ][4]
-            == 31220
-        )
-        assert (
-            sim.results[
-                precedence_sfg_delays.find_result_keys_by_name("A1")[0]
-            ][2]
-            == 80
-        )
-
-
-class TestRun:
-    def test_save_results(self, sfg_two_inputs_two_outputs):
-        simulation = FastSimulation(sfg_two_inputs_two_outputs, [2, 3])
-        assert not simulation.results
-        simulation.run_for(10, save_results=False)
-        assert not simulation.results
-        simulation.run_for(10)
-        assert len(simulation.results["0"]) == 10
-        assert len(simulation.results["1"]) == 10
-        simulation.run_for(10, save_results=True)
-        assert len(simulation.results["0"]) == 20
-        assert len(simulation.results["1"]) == 20
-        simulation.run_for(10, save_results=False)
-        assert len(simulation.results["0"]) == 20
-        assert len(simulation.results["1"]) == 20
-        simulation.run_for(13, save_results=True)
-        assert len(simulation.results["0"]) == 33
-        assert len(simulation.results["1"]) == 33
-        simulation.step(save_results=False)
-        assert len(simulation.results["0"]) == 33
-        assert len(simulation.results["1"]) == 33
-        simulation.step()
-        assert len(simulation.results["0"]) == 34
-        assert len(simulation.results["1"]) == 34
-        simulation.clear_results()
-        assert not simulation.results
-
-    def test_nested(self, sfg_nested):
-        input0 = np.array([5, 9])
-        input1 = np.array([7, 3])
-        simulation = FastSimulation(sfg_nested, [input0, input1])
-
-        output0 = simulation.step()
-        output1 = simulation.step()
-
-        assert output0[0] == 11405
-        assert output1[0] == 4221
-
-    def test_accumulator(self, sfg_accumulator):
-        data_in = np.array([5, -2, 25, -6, 7, 0])
-        reset = np.array([0, 0, 0, 1, 0, 0])
-        simulation = FastSimulation(sfg_accumulator, [data_in, reset])
-        output0 = simulation.step()
-        output1 = simulation.step()
-        output2 = simulation.step()
-        output3 = simulation.step()
-        output4 = simulation.step()
-        output5 = simulation.step()
-        assert output0[0] == 0
-        assert output1[0] == 5
-        assert output2[0] == 3
-        assert output3[0] == 28
-        assert output4[0] == 0
-        assert output5[0] == 7
-
-    def test_simple_accumulator(self, sfg_simple_accumulator):
-        data_in = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
-        simulation = FastSimulation(sfg_simple_accumulator, [data_in])
-        simulation.run()
-        assert list(simulation.results["0"]) == [
-            0,
-            1,
-            3,
-            6,
-            10,
-            15,
-            21,
-            28,
-            36,
-            45,
-        ]
-
-    def test_simple_filter(self, sfg_simple_filter):
-        input0 = np.array([1, 2, 3, 4, 5])
-        simulation = FastSimulation(sfg_simple_filter, [input0])
-        simulation.run_for(len(input0), save_results=True)
-        assert all(
-            simulation.results["0"] == np.array([0, 1.0, 2.5, 4.25, 6.125])
-        )
-
-    def test_custom_operation(self, sfg_custom_operation):
-        simulation = FastSimulation(sfg_custom_operation, [lambda n: n + 1])
-        simulation.run_for(5)
-        assert all(simulation.results["0"] == np.array([2, 4, 6, 8, 10]))
-        assert all(simulation.results["1"] == np.array([2, 4, 8, 16, 32]))
-
-
-class TestLarge:
-    def test_1k_additions(self):
-        prev_op = Addition(Constant(1), Constant(1))
-        for _ in range(999):
-            prev_op = Addition(prev_op, Constant(2))
-        sfg = SFG(outputs=[Output(prev_op)])
-        simulation = FastSimulation(sfg, [])
-        assert simulation.step()[0] == 2000
-
-    def test_1k_subtractions(self):
-        prev_op = Subtraction(Constant(0), Constant(2))
-        for _ in range(999):
-            prev_op = Subtraction(prev_op, Constant(2))
-        sfg = SFG(outputs=[Output(prev_op)])
-        simulation = FastSimulation(sfg, [])
-        assert simulation.step()[0] == -2000
-
-    def test_1k_butterfly(self):
-        prev_op_add = Addition(Constant(1), Constant(1))
-        prev_op_sub = Subtraction(Constant(-1), Constant(1))
-        for _ in range(499):
-            prev_op_add = Addition(prev_op_add, Constant(2))
-        for _ in range(499):
-            prev_op_sub = Subtraction(prev_op_sub, Constant(2))
-        butterfly = Butterfly(prev_op_add, prev_op_sub)
-        sfg = SFG(
-            outputs=[Output(butterfly.output(0)), Output(butterfly.output(1))]
-        )
-        simulation = FastSimulation(sfg, [])
-        assert list(simulation.step()) == [0, 2000]
diff --git a/test/test_sfg.py b/test/test_sfg.py
index ce95b1060b7c1a614d4296b4a722616565b547ca..516f4377a6e61f969969ff0527a55a11814c373e 100644
--- a/test/test_sfg.py
+++ b/test/test_sfg.py
@@ -9,7 +9,7 @@ from typing import Counter, Dict, Type
 
 import pytest
 
-from b_asic import SFG, FastSimulation, Input, Output, Signal
+from b_asic import SFG, Input, Output, Signal
 from b_asic.core_operations import (
     Absolute,
     Addition,
@@ -345,112 +345,6 @@ class TestReplaceComponents:
             )
 
 
-class TestConstructSFG:
-    def test_1k_additions(self):
-        prev_op = Addition(Constant(1), Constant(1))
-        for _ in range(999):
-            prev_op = Addition(prev_op, Constant(2))
-        sfg = SFG(outputs=[Output(prev_op)])
-        sim = FastSimulation(sfg)
-        sim.step()
-        assert sim.results["0"][0].real == 2000
-
-    def test_1k_subtractions(self):
-        prev_op = Subtraction(Constant(0), Constant(2))
-        for _ in range(999):
-            prev_op = Subtraction(prev_op, Constant(2))
-        sfg = SFG(outputs=[Output(prev_op)])
-        sim = FastSimulation(sfg)
-        sim.step()
-        assert sim.results["0"][0].real == -2000
-
-    def test_1k_butterfly(self):
-        prev_op_add = Addition(Constant(1), Constant(1))
-        prev_op_sub = Subtraction(Constant(-1), Constant(1))
-        for _ in range(499):
-            prev_op_add = Addition(prev_op_add, Constant(2))
-        for _ in range(499):
-            prev_op_sub = Subtraction(prev_op_sub, Constant(2))
-        butterfly = Butterfly(prev_op_add, prev_op_sub)
-        sfg = SFG(outputs=[Output(butterfly.output(0)), Output(butterfly.output(1))])
-        sim = FastSimulation(sfg)
-        sim.step()
-        assert sim.results["0"][0].real == 0
-        assert sim.results["1"][0].real == 2000
-
-    def test_1k_multiplications(self):
-        prev_op = Multiplication(Constant(3), Constant(0.5))
-        for _ in range(999):
-            prev_op = Multiplication(prev_op, Constant(1.01))
-        sfg = SFG(outputs=[Output(prev_op)])
-        sim = FastSimulation(sfg)
-        sim.step()
-        assert sim.results["0"][0].real == 31127.458868040336
-
-    def test_1k_divisions(self):
-        prev_op = Division(Constant(3), Constant(0.5))
-        for _ in range(999):
-            prev_op = Division(prev_op, Constant(1.01))
-        sfg = SFG(outputs=[Output(prev_op)])
-        sim = FastSimulation(sfg)
-        sim.step()
-        assert sim.results["0"][0].real == 0.00028913378500165966
-
-    def test_1k_mins(self):
-        prev_op = Min(Constant(3.14159), Constant(43.14123843))
-        for _ in range(999):
-            prev_op = Min(prev_op, Constant(43.14123843))
-        sfg = SFG(outputs=[Output(prev_op)])
-        sim = FastSimulation(sfg)
-        sim.step()
-        assert sim.results["0"][0].real == 3.14159
-
-    def test_1k_maxs(self):
-        prev_op = Max(Constant(3.14159), Constant(43.14123843))
-        for _ in range(999):
-            prev_op = Max(prev_op, Constant(3.14159))
-        sfg = SFG(outputs=[Output(prev_op)])
-        sim = FastSimulation(sfg)
-        sim.step()
-        assert sim.results["0"][0].real == 43.14123843
-
-    def test_1k_square_roots(self):
-        prev_op = SquareRoot(Constant(1000000))
-        for _ in range(4):
-            prev_op = SquareRoot(prev_op)
-        sfg = SFG(outputs=[Output(prev_op)])
-        sim = FastSimulation(sfg)
-        sim.step()
-        assert sim.results["0"][0].real == 1.539926526059492
-
-    def test_1k_complex_conjugates(self):
-        prev_op = ComplexConjugate(Constant(10 + 5j))
-        for _ in range(999):
-            prev_op = ComplexConjugate(prev_op)
-        sfg = SFG(outputs=[Output(prev_op)])
-        sim = FastSimulation(sfg)
-        sim.step()
-        assert sim.results["0"] == [10 + 5j]
-
-    def test_1k_absolutes(self):
-        prev_op = Absolute(Constant(-3.14159))
-        for _ in range(999):
-            prev_op = Absolute(prev_op)
-        sfg = SFG(outputs=[Output(prev_op)])
-        sim = FastSimulation(sfg)
-        sim.step()
-        assert sim.results["0"][0].real == 3.14159
-
-    def test_1k_constant_multiplications(self):
-        prev_op = ConstantMultiplication(1.02, Constant(3.14159))
-        for _ in range(999):
-            prev_op = ConstantMultiplication(1.02, prev_op)
-        sfg = SFG(outputs=[Output(prev_op)])
-        sim = FastSimulation(sfg)
-        sim.step()
-        assert sim.results["0"][0].real == 1251184247.0026844
-
-
 class TestInsertComponent:
     def test_insert_component_in_sfg(self, large_operation_tree_names):
         sfg = SFG(outputs=[Output(large_operation_tree_names)])
diff --git a/test/test_simulation.py b/test/test_simulation.py
index d2d59512d556a4cdaf85f0ef50994617e98f80d6..cdcb86c0c82e1a6c6a2b5f0a603ef101ad086a5d 100644
--- a/test/test_simulation.py
+++ b/test/test_simulation.py
@@ -2,7 +2,6 @@ import numpy as np
 import pytest
 
 from b_asic import Simulation
-from b_asic._b_asic import FastSimulation as Simulation
 
 
 class TestRunFor: