diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..a061362 --- /dev/null +++ b/.clang-format @@ -0,0 +1,16 @@ +BasedOnStyle: LLVM +IndentWidth: 4 + +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^".*' + Priority: 1 + - Regex: '^<(safe)/.*' + Priority: 2 + - Regex: '^<(boost|catch2|fmt|gmock|gtest|rapidcheck)/.*' + Priority: 3 + - Regex: '<.*' + Priority: 4 + +QualifierAlignment: Custom +QualifierOrder: ['constexpr', 'static', 'inline', 'type', 'const', 'volatile'] diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..7a4adf6 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,39 @@ +--- +Checks: > + boost-use-to-string, + bugprone-*, + -bugprone-easily-swappable-parameters, + clang-diagnostic-*, + clang-analyzer-*, + cppcoreguidelines-*, + -cppcoreguidelines-avoid-magic-numbers, + -cppcoreguidelines-avoid-non-const-global-variables, + -cppcoreguidelines-macro-usage, + -cppcoreguidelines-pro-bounds-pointer-arithmetic, + google-build-explicit-make-pair, + google-default-arguments, + google-explicit-constructor, + google-readability-casting, + google-runtime-int, + hicpp-signed-bitwise, + misc-misplaced-const, + misc-non-copyable-objects, + misc-redundant-expression, + misc-static-assert, + misc-throw-by-value-catch-by-reference, + misc-uniqueptr-reset-release, + modernize-*, + performance-*, + portability-*, + readability-*, + -readability-identifier-length, + -readability-identifier-naming, + -readability-magic-numbers, + -readability-named-parameter, + -readability-qualified-auto, + -readability-uppercase-literal-suffix +WarningsAsErrors: '*' +HeaderFilterRegex: '' +AnalyzeTemporaryDtors: false +FormatStyle: file +... diff --git a/.cmake-format.yaml b/.cmake-format.yaml new file mode 100644 index 0000000..4f94929 --- /dev/null +++ b/.cmake-format.yaml @@ -0,0 +1,3 @@ +format: + tab_size: 4 + line_width: 80 diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index c88e548..c4eaa03 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -1,13 +1,20 @@ name: Unit Tests on: + workflow_dispatch: + merge_group: push: branches: [ main ] pull_request: branches: [ main ] env: - BUILD_TYPE: Debug + DEBIAN_FRONTEND: noninteractive + CMAKE_GENERATOR: Ninja + +concurrency: + group: ${{ github.head_ref || github.run_id }} + cancel-in-progress: true jobs: build_and_test: @@ -15,69 +22,193 @@ jobs: strategy: fail-fast: false matrix: - compiler: - - name: clang-16 + compiler: [clang, gcc] + version: [12, 13, 14, 15, 16] + cxx_standard: [20] + stdlib: [libstdc++, libc++] + build_type: [Debug] + include: + - compiler: clang + cc: "clang" + cxx: "clang++" + - version: 16 + compiler: clang install: wget https://apt.llvm.org/llvm.sh && chmod +x llvm.sh && sudo ./llvm.sh 16 - cc: " /usr/lib/llvm-16/bin/clang" - cxx: "/usr/lib/llvm-16/bin/clang++" - - name: clang-15 - install: wget https://apt.llvm.org/llvm.sh && chmod +x llvm.sh && sudo ./llvm.sh 15 - cc: " /usr/lib/llvm-15/bin/clang" - cxx: "/usr/lib/llvm-15/bin/clang++" - - name: clang-14 - install: wget https://apt.llvm.org/llvm.sh && chmod +x llvm.sh && sudo ./llvm.sh 14 - cc: " /usr/lib/llvm-14/bin/clang" - cxx: "/usr/lib/llvm-14/bin/clang++" - - name: clang-13 - install: wget https://apt.llvm.org/llvm.sh && chmod +x llvm.sh && sudo ./llvm.sh 13 - cc: " /usr/lib/llvm-13/bin/clang" - cxx: "/usr/lib/llvm-13/bin/clang++" - - name: clang-12 - install: sudo apt update && sudo apt-get install -y clang-12 - cc: " /usr/lib/llvm-12/bin/clang" - cxx: "/usr/lib/llvm-12/bin/clang++" - - name: gcc-13 + toolchain_root: "/usr/lib/llvm-16" + - version: 16 + compiler: clang + stdlib: libc++ + install: wget https://apt.llvm.org/llvm.sh && chmod +x llvm.sh && sudo ./llvm.sh 16 && sudo apt install -y libc++-16-dev libc++abi-16-dev + - version: 15 + compiler: clang + install: sudo apt update && sudo apt install -y clang-15 + toolchain_root: "/usr/lib/llvm-15" + - version: 15 + compiler: clang + stdlib: libc++ + install: sudo apt update && sudo apt install -y clang-15 libc++-15-dev libc++abi-15-dev + - version: 14 + compiler: clang + install: sudo apt update && sudo apt install -y clang-14 + toolchain_root: "/usr/lib/llvm-14" + - version: 14 + compiler: clang + stdlib: libc++ + install: sudo apt update && sudo apt install -y clang-14 libc++-14-dev libc++abi-14-dev + - version: 13 + compiler: gcc install: sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test && sudo apt update && sudo apt-get install -y gcc-13 g++-13 - cc: "/usr/bin/gcc-13" - cxx: "/usr/bin/g++-13" - - name: gcc-12 - install: sudo apt update && sudo apt-get install -y gcc-12 g++-12 - cc: "/usr/bin/gcc-12" - cxx: "/usr/bin/g++-12" - - name: gcc-11 - install: sudo apt update && sudo apt-get install -y gcc-11 g++-11 - cc: "/usr/bin/gcc-11" - cxx: "/usr/bin/g++-11" - - name: gcc-10 - install: sudo apt update && sudo apt-get install -y gcc-10 g++-10 - cc: "/usr/bin/gcc-10" - cxx: "/usr/bin/g++-10" - - + toolchain_root: "/usr" + cc: "gcc-13" + cxx: "g++-13" + - version: 12 + compiler: gcc + install: sudo apt update && sudo apt install -y gcc-12 + toolchain_root: "/usr" + cc: "gcc-12" + cxx: "g++-12" + exclude: + - compiler: gcc + version: 16 + - compiler: gcc + version: 15 + - compiler: gcc + version: 14 + - compiler: clang + version: 13 + - compiler: clang + version: 12 + - compiler: gcc + stdlib: libc++ + steps: - - name: install support tools - run: sudo apt update && DEBIAN_FRONTEND=noninteractive TZ=America/Los_Angeles sudo apt-get install -y git wget software-properties-common gnupg cmake lsb-release libstdc++-10-dev ninja-build + - uses: actions/checkout@v3 + + - name: Install build tools + run: | + ${{ matrix.install }} + sudo apt install -y ninja-build + + - name: Configure CMake + env: + CC: ${{matrix.toolchain_root}}/bin/${{matrix.cc}} + CXX: ${{matrix.toolchain_root}}/bin/${{matrix.cxx}} + CXX_STANDARD: ${{matrix.cxx_standard}} + CXX_STDLIB: ${{matrix.stdlib}} + run: cmake -B ${{github.workspace}}/build -DCMAKE_TOOLCHAIN_FILE=${{github.workspace}}/toolchains/${{matrix.compiler}}.cmake -DCMAKE_BUILD_TYPE=${{matrix.build_type}} + + - name: Build Unit Tests + run: cmake --build ${{github.workspace}}/build --config ${{matrix.build_type}} -v -t build_unit_tests - - name: install compiler - run: ${{ matrix.compiler.install }} + - name: Test + working-directory: ${{github.workspace}}/build + run: ctest -j $(nproc) -C ${{matrix.build_type}} + quality_checks_pass: + runs-on: ubuntu-22.04 + steps: - uses: actions/checkout@v3 + - name: Install build tools + run: | + wget https://apt.llvm.org/llvm.sh && chmod +x llvm.sh && sudo ./llvm.sh 16 + sudo apt install -y ninja-build clang-tidy-16 clang-format-16 + + - name: Install cmake-format + run: | + pip3 install --upgrade pip + pip3 install pyyaml cmake-format + - name: Configure CMake env: - CC: ${{ matrix.compiler.cc }} - CXX: ${{ matrix.compiler.cxx }} - CXX_STANDARD: ${{ matrix.compiler.cxx_standard }} - run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} + CC: "/usr/lib/llvm-16/bin/clang" + CXX: "/usr/lib/llvm-16/bin/clang++" + CXX_STANDARD: 20 + run: cmake -B ${{github.workspace}}/build - - name: Build and Run Unit Tests - run: cmake --build ${{github.workspace}}/build --target unit_tests + - name: Run quality checks + run: cmake --build ${{github.workspace}}/build -t quality + sanitize: + runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + sanitizer: [undefined, address, thread] + steps: + - uses: actions/checkout@v3 - quality_checks_pass: - runs-on: ubuntu-20.04 - needs: build_and_test + - name: Install build tools + run: | + wget https://apt.llvm.org/llvm.sh && chmod +x llvm.sh && sudo ./llvm.sh 16 + sudo apt install -y ninja-build + + - name: Configure CMake + env: + CC: "/usr/lib/llvm-16/bin/clang" + CXX: "/usr/lib/llvm-16/bin/clang++" + CXX_STANDARD: 20 + SANITIZERS: ${{matrix.sanitizer}} + run: cmake -B ${{github.workspace}}/build + + - name: Build Unit Tests + run: cmake --build ${{github.workspace}}/build -t unit_tests + + valgrind: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + + - name: Install build tools + run: sudo apt update && sudo apt install -y gcc-12 ninja-build valgrind + + - name: Configure CMake + env: + CC: "/usr/bin/gcc-12" + CXX: "/usr/bin/g++-12" + CXX_STANDARD: 20 + run: cmake -B ${{github.workspace}}/build + + - name: Build Unit Tests + run: cmake --build ${{github.workspace}}/build -t build_unit_tests + + - name: Test + working-directory: ${{github.workspace}}/build + run: | + ctest -j $(nproc) -E EXPECT_FAIL -T memcheck + + LOGFILE=$(ls ./Testing/Temporary/LastDynamicAnalysis_*.log) + FAILSIZE=$(du -c ./Testing/Temporary/MemoryChecker.* | tail -1 | cut -f1) + echo "
" >> $GITHUB_STEP_SUMMARY + + echo "" >> $GITHUB_STEP_SUMMARY + if [ $FAILSIZE != "0" ]; then + echo "Failing tests:" | tee -a $GITHUB_STEP_SUMMARY + else + echo "No failing tests" >> $GITHUB_STEP_SUMMARY + fi + echo "" >> $GITHUB_STEP_SUMMARY + + for f in ./Testing/Temporary/MemoryChecker.* + do + if [ -s $f ]; then + FILENAME=$(cd $(dirname $f) && pwd)/$(basename $f) + TEST_COMMAND=$(grep $FILENAME $LOGFILE) + echo "" | tee -a $GITHUB_STEP_SUMMARY + echo "========================================" + echo $TEST_COMMAND | tee -a $GITHUB_STEP_SUMMARY + echo "--------------------" + cat $f + fi + done + + echo "
" >> $GITHUB_STEP_SUMMARY + test $FAILSIZE = "0" + + merge_ok: + runs-on: ubuntu-22.04 + needs: [build_and_test, quality_checks_pass, sanitize, valgrind] steps: - - name: Say Hello - run: echo "Hello!" + - name: Enable merge + run: echo "OK to merge!" diff --git a/.gitignore b/.gitignore index 962178d..c4902c9 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ /cmake-build-* .idea .vscode +.cache diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index e69de29..0000000 diff --git a/CMakeLists.txt b/CMakeLists.txt index c7b08f9..a275d33 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,20 +1,27 @@ -cmake_minimum_required(VERSION 3.16) -project(safe_arithmetic LANGUAGES CXX) +cmake_minimum_required(VERSION 3.25) -set(CMAKE_CXX_STANDARD 20) +project(safe_arithmetic LANGUAGES CXX) -include(cmake/CPM.cmake) +if(PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR) + message( + FATAL_ERROR + "In-source builds are a bad idea. Please make a build directory instead." + ) +endif() -include(cmake/libraries.cmake) include(cmake/dependencies.cmake) -include(cmake/test.cmake) -include(cmake/sanitizers.cmake) -include(CTest) -enable_testing() - -add_versioned_package("gh:boostorg/mp11#boost-1.80.0") +include(cmake/libraries.cmake) +include(cmake/quality.cmake) -add_subdirectory(include) -add_subdirectory(test) +add_versioned_package("gh:boostorg/mp11#boost-1.83.0") +add_library(safe_arithmetic INTERFACE) +target_compile_features(safe_arithmetic INTERFACE cxx_std_20) +target_include_directories(safe_arithmetic INTERFACE include) +target_link_libraries(safe_arithmetic INTERFACE boost_mp11) +if(PROJECT_IS_TOP_LEVEL) + set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + # clang_tidy_interface(safe_arithmetic) + add_subdirectory(test) +endif() diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..b892fc2 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,44 @@ +{ + "version": 3, + "cmakeMinimumRequired": { + "major": 3, + "minor": 21, + "patch": 0 + }, + "configurePresets": [ + { + "name": "base", + "hidden": true, + "generator": "Ninja", + "binaryDir": "${sourceDir}/build" + }, + { + "name": "default", + "inherits": "base", + "description": "Build using Clang with libstdc++", + "toolchainFile": "${sourceDir}/toolchains/clang.cmake" + }, + { + "name": "clang", + "inherits": "default" + }, + { + "name": "clang-libstdc++", + "inherits": "default" + }, + { + "name": "clang-libc++", + "inherits": "default", + "description": "Build using Clang with libc++", + "environment": { + "CXX_STDLIB": "libc++" + } + }, + { + "name": "gcc", + "inherits": "base", + "description": "Build using GCC", + "toolchainFile": "${sourceDir}/toolchains/gcc.cmake" + } + ] +} diff --git a/cmake/quality.cmake b/cmake/quality.cmake index edada42..13b005d 100644 --- a/cmake/quality.cmake +++ b/cmake/quality.cmake @@ -10,8 +10,10 @@ function(clang_tidy_interface TARGET) endfunction() if(PROJECT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) - if(DEFINED ENV{CLANG_TOOLS_PATH}) - list(APPEND CMAKE_PROGRAM_PATH $ENV{CLANG_TOOLS_PATH}) + get_filename_component(CT_ROOT ${CMAKE_CXX_COMPILER} DIRECTORY) + find_program(CLANG_FORMAT_PROGRAM "clang-format" HINTS ${CT_ROOT}) + if(CLANG_FORMAT_PROGRAM) + message(STATUS "clang-format found: ${CLANG_FORMAT_PROGRAM}") endif() include(${CMAKE_CURRENT_LIST_DIR}/sanitizers.cmake) @@ -32,8 +34,9 @@ if(PROJECT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) add_dependencies(quality check-clang-format check-cmake-format) if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - find_program(CLANG_TIDY_PROGRAM "clang-tidy") + find_program(CLANG_TIDY_PROGRAM "clang-tidy" HINTS ${CT_ROOT}) if(CLANG_TIDY_PROGRAM) + message(STATUS "clang-tidy found: ${CLANG_TIDY_PROGRAM}") add_custom_target(clang-tidy) include(${CMAKE_CURRENT_LIST_DIR}/clang-tidy.cmake) else() diff --git a/cmake/test.cmake b/cmake/test.cmake index fb23170..bf6fa8d 100644 --- a/cmake/test.cmake +++ b/cmake/test.cmake @@ -13,28 +13,30 @@ set(MEMORYCHECK_SUPPRESSIONS_FILE configure_file(${CMAKE_ROOT}/Modules/DartConfiguration.tcl.in ${PROJECT_BINARY_DIR}/DartConfiguration.tcl) -add_versioned_package("gh:emil-e/rapidcheck#a5724ea") +if(DEFINED ENV{CXX_STANDARD}) + set(CMAKE_CXX_STANDARD $ENV{CXX_STANDARD}) +else() + set(CMAKE_CXX_STANDARD 17) +endif() macro(get_catch2) - if(NOT DEFINED CPM_GOT_CATCH) - add_versioned_package("gh:catchorg/Catch2@3.3.2") + if(NOT TARGET Catch2::Catch2WithMain) + add_versioned_package("gh:catchorg/Catch2@3.4.0") list(APPEND CMAKE_MODULE_PATH ${Catch2_SOURCE_DIR}/extras) include(Catch) - set(CPM_GOT_CATCH 1) endif() endmacro() macro(get_gtest) - if(NOT DEFINED CPM_GOT_GTEST) - add_versioned_package("gh:google/googletest@1.13.0") + if(NOT TARGET gtest) + add_versioned_package("gh:google/googletest@1.14.0") include(GoogleTest) - set(CPM_GOT_GTEST 1) endif() endmacro() macro(get_gunit) get_gtest() - if(NOT DEFINED CPM_GOT_GUNIT) + if(NOT DEFINED gunit_SOURCE_DIR) add_versioned_package( NAME gunit @@ -44,27 +46,40 @@ macro(get_gunit) cpp-testing/GUnit DOWNLOAD_ONLY YES) - set(CPM_GOT_GUNIT 1) endif() endmacro() macro(add_boost_di) - if(NOT DEFINED CPM_GOT_BOOST_DI) + if(NOT TARGET Boost.DI) add_versioned_package("gh:boost-ext/di#9866a4a") - set(CPM_GOT_BOOST_DI 1) endif() endmacro() macro(add_gherkin) - if(NOT DEFINED CPM_GOT_GHERKIN) + if(NOT TARGET gherkin-cpp) add_subdirectory( ${gunit_SOURCE_DIR}/libs/gherkin-cpp ${gunit_BINARY_DIR}/libs/gherkin-cpp EXCLUDE_FROM_ALL SYSTEM) - set(CPM_GOT_GHERKIN 1) endif() endmacro() -function(add_unit_test name) +macro(add_rapidcheck) + if(NOT TARGET rapidcheck) + add_versioned_package(NAME rapidcheck GIT_TAG a5724ea GITHUB_REPOSITORY + emil-e/rapidcheck) + add_subdirectory( + ${rapidcheck_SOURCE_DIR}/extras/catch + ${rapidcheck_BINARY_DIR}/extras/catch EXCLUDE_FROM_ALL SYSTEM) + add_subdirectory( + ${rapidcheck_SOURCE_DIR}/extras/gtest + ${rapidcheck_BINARY_DIR}/extras/gtest EXCLUDE_FROM_ALL SYSTEM) + add_subdirectory( + ${rapidcheck_SOURCE_DIR}/extras/gmock + ${rapidcheck_BINARY_DIR}/extras/gmock EXCLUDE_FROM_ALL SYSTEM) + endif() +endmacro() + +function(add_unit_test_target name) set(options CATCH2 GTEST GUNIT) set(multiValueArgs FILES INCLUDE_DIRECTORIES LIBRARIES SYSTEM_LIBRARIES) cmake_parse_arguments(UNIT "${options}" "" "${multiValueArgs}" ${ARGN}) @@ -77,27 +92,38 @@ function(add_unit_test name) add_dependencies(build_unit_tests ${name}) if(UNIT_CATCH2) - get_catch2() catch_discover_tests(${name}) - target_link_libraries_system(${name} PRIVATE Catch2::Catch2WithMain) + target_link_libraries_system(${name} PRIVATE Catch2::Catch2WithMain + rapidcheck rapidcheck_catch) set(target_test_command $ "--order" "rand") elseif(UNIT_GTEST) - get_gtest() gtest_discover_tests(${name}) - target_link_libraries_system(${name} PRIVATE gmock gtest gmock_main rapidcheck) + target_link_libraries_system( + ${name} + PRIVATE + gmock + gtest + gmock_main + rapidcheck + rapidcheck_gtest + rapidcheck_gmock) set(target_test_command $ "--gtest_shuffle") - target_include_directories(${name} SYSTEM PRIVATE ${rapidcheck_SOURCE_DIR}/extras/gtest/include) elseif(UNIT_GUNIT) - get_gunit() - add_boost_di() - gtest_discover_tests(${name}) target_include_directories( ${name} SYSTEM PRIVATE ${gunit_SOURCE_DIR}/include ${gunit_SOURCE_DIR}/libs/json/single_include/nlohmann) - target_link_libraries_system(${name} PRIVATE gtest_main gmock_main - Boost.DI) + target_link_libraries_system( + ${name} + PRIVATE + gtest_main + gmock_main + Boost.DI + rapidcheck + rapidcheck_gtest + rapidcheck_gmock) set(target_test_command $ "--gtest_shuffle") + add_test(NAME ${name} COMMAND ${target_test_command}) else() set(target_test_command $) add_test(NAME ${name} COMMAND ${target_test_command}) @@ -114,7 +140,38 @@ function(add_unit_test name) add_dependencies(unit_tests "run_${name}") endfunction() -function(add_feature_test name) +function(detect_test_framework) + set(options CATCH2 GTEST GUNIT) + cmake_parse_arguments(TF "${options}" "" "" ${ARGN}) + if(TF_CATCH) + return(PROPAGATE TF_CATCH) + elseif(TF_GTEST) + return(PROPAGATE TF_GTEST) + elseif(TF_GUNIT) + return(PROPAGATE TF_GUNIT) + endif() +endfunction() + +macro(add_unit_test) + detect_test_framework(${ARGN}) + if(TF_CATCH) + get_catch2() + add_rapidcheck() + unset(TF_CATCH) + elseif(TF_GTEST) + get_gtest() + add_rapidcheck() + unset(TF_GTEST) + elseif(TF_GUNIT) + get_gunit() + add_boost_di() + add_rapidcheck() + unset(TF_GUNIT) + endif() + add_unit_test_target(${ARGN}) +endmacro() + +function(add_feature_test_target name) set(singleValueArgs FEATURE) set(multiValueArgs FILES INCLUDE_DIRECTORIES LIBRARIES SYSTEM_LIBRARIES) cmake_parse_arguments(FEAT "${options}" "${singleValueArgs}" @@ -127,15 +184,21 @@ function(add_feature_test name) target_link_libraries(${name} PRIVATE sanitizers) add_dependencies(build_unit_tests ${name}) - get_gunit() - add_gherkin() - add_boost_di() target_include_directories( ${name} SYSTEM PRIVATE ${gunit_SOURCE_DIR}/include - ${gunit_SOURCE_DIR}/libs/json/single_include/nlohmann) - target_link_libraries_system(${name} PRIVATE gtest_main gmock_main - gherkin-cpp Boost.DI) + ${gunit_SOURCE_DIR}/libs/json/single_include/nlohmann + ${rapidcheck_SOURCE_DIR}/extras/gmock/include) + target_link_libraries_system( + ${name} + PRIVATE + gtest_main + gmock_main + gherkin-cpp + Boost.DI + rapidcheck + rapidcheck_gtest + rapidcheck_gmock) set(target_test_command $ "--gtest_shuffle") add_custom_target(all_${name} ALL DEPENDS run_${name}) @@ -152,3 +215,11 @@ function(add_feature_test name) set_property(TEST ${name} PROPERTY ENVIRONMENT "SCENARIO=${FEATURE_FILE}") add_dependencies(unit_tests "run_${name}") endfunction() + +macro(add_feature_test) + get_gunit() + add_gherkin() + add_boost_di() + add_rapidcheck() + add_feature_test_target(${ARGN}) +endmacro() diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt index 6fd1253..f63cf86 100644 --- a/include/CMakeLists.txt +++ b/include/CMakeLists.txt @@ -1,3 +1,4 @@ add_library(safe_arithmetic INTERFACE) -target_include_directories(safe_arithmetic INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) +target_include_directories(safe_arithmetic + INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(safe_arithmetic INTERFACE boost_mp11) diff --git a/include/safe.hpp b/include/safe.hpp index eb0bba7..3722100 100644 --- a/include/safe.hpp +++ b/include/safe.hpp @@ -1,12 +1,12 @@ #pragma once +#include +#include #include -#include -#include #include -#include +#include #include -#include #include -#include -#include \ No newline at end of file +#include +#include +#include diff --git a/include/safe/algorithm.hpp b/include/safe/algorithm.hpp index 918ea56..df793ce 100644 --- a/include/safe/algorithm.hpp +++ b/include/safe/algorithm.hpp @@ -1,5 +1,4 @@ #pragma once - #include #include \ No newline at end of file diff --git a/include/safe/algorithm/accumulate.hpp b/include/safe/algorithm/accumulate.hpp index e2bf580..d09557e 100644 --- a/include/safe/algorithm/accumulate.hpp +++ b/include/safe/algorithm/accumulate.hpp @@ -1,21 +1,17 @@ #pragma once - -#include #include -#include - #include +#include +#include #include #include - namespace safe { - namespace detail { - template - inline consteval auto fold(auto e, auto op){ - if constexpr (count > 100) { +namespace detail { +template inline consteval auto fold(auto e, auto op) { + if constexpr (count > 100) { return safe::dsl::detail::simp( op(op(op(op(op(op(op(op(op(op( op(op(op(op(op(op(op(op(op(op( @@ -39,83 +35,76 @@ namespace safe { e), e), e), e), e), e), e), e), e), e) ); - } else if constexpr (count > 10) { - return safe::dsl::detail::simp( - op(op(op(op(op(op(op(op(op(op(fold(e, op), e), e), e), e), e), e), e), e), e), e) - ); - - } else if constexpr (count > 1) { + } else if constexpr (count > 10) { + return safe::dsl::detail::simp(op( + op(op(op(op(op(op(op(op(op(fold(e, op), e), e), + e), + e), + e), + e), + e), + e), + e), + e)); + + } else if constexpr (count > 1) { return safe::dsl::detail::simp(op(fold(e, op), e)); - } else { + } else { return e; - } - } - - inline constexpr auto plus_op = [](auto a, auto b){return a + b;}; } +} +constexpr inline auto plus_op = [](auto a, auto b) { return a + b; }; +} // namespace detail - template - [[nodiscard]] inline constexpr auto accumulate( - detail::iter_like auto first, - auto last, - auto init, - auto op - ) { - constexpr auto req = decltype((*first).requirement){}; - constexpr auto sum_req = detail::fold(req, op); +template +[[nodiscard]] constexpr inline auto accumulate(detail::iter_like auto first, + auto last, auto init, auto op) { + constexpr auto req = decltype((*first).requirement){}; + constexpr auto sum_req = detail::fold(req, op); - using ret_num_t = decltype((*first).unsafe_value()); + using ret_num_t = decltype((*first).unsafe_value()); - auto iter_count = size_t{}; - auto sum = init; - while ((first != last) && (iter_count < max_iter)) { - sum = op(sum, (*first).unsafe_value()); - first++; - iter_count++; - } - - return unsafe_cast>(sum); - } - - template - [[nodiscard]] inline constexpr auto accumulate( - detail::iter_like auto first, - auto last, - auto init - ) { - return accumulate(first, last, init, detail::plus_op); - } - - template - [[nodiscard]] inline constexpr auto accumulate( - detail::range_like auto & range, - auto init, - auto op - ) { - return accumulate(range.begin(), range.end(), init, op); + auto iter_count = size_t{}; + auto sum = init; + while ((first != last) && (iter_count < max_iter)) { + sum = op(sum, (*first).unsafe_value()); + first++; + iter_count++; } - template - [[nodiscard]] inline constexpr auto accumulate( - detail::range_like auto & range, - auto init - ) { - return accumulate(range.begin(), range.end(), init, detail::plus_op); - } - - template - [[nodiscard]] inline constexpr auto accumulate( - detail::range_like auto const & range, - auto init, - auto op - ) { - return accumulate(range.begin(), range.end(), init, op); - } - - template - [[nodiscard]] inline constexpr auto accumulate(auto const & range, auto init) { - return accumulate(range.begin(), range.end(), init, detail::plus_op); - } -} \ No newline at end of file + return unsafe_cast>(sum); +} + +template +[[nodiscard]] constexpr inline auto accumulate(detail::iter_like auto first, + auto last, auto init) { + return accumulate(first, last, init, detail::plus_op); +} + +template +[[nodiscard]] constexpr inline auto accumulate(detail::range_like auto &range, + auto init, auto op) { + return accumulate(range.begin(), range.end(), init, op); +} + +template +[[nodiscard]] constexpr inline auto accumulate(detail::range_like auto &range, + auto init) { + return accumulate(range.begin(), range.end(), init, + detail::plus_op); +} + +template +[[nodiscard]] constexpr inline auto +accumulate(detail::range_like auto const &range, auto init, auto op) { + return accumulate(range.begin(), range.end(), init, op); +} + +template +[[nodiscard]] constexpr inline auto accumulate(auto const &range, auto init) { + return accumulate(range.begin(), range.end(), init, + detail::plus_op); +} +} // namespace safe \ No newline at end of file diff --git a/include/safe/algorithm/irange.hpp b/include/safe/algorithm/irange.hpp index 68e4683..cd90b22 100644 --- a/include/safe/algorithm/irange.hpp +++ b/include/safe/algorithm/irange.hpp @@ -1,79 +1,65 @@ #pragma once - #include - namespace safe { - template - struct irange { - private: - BeginT begin_; - EndT end_; - - template - struct iterator { - private: - irange const * parent_; - T value_; - bool end_; +template struct irange { + private: + BeginT begin_; + EndT end_; - public: - using ret_t = decltype(clamp(0, parent_->begin_, parent_->end_ - s32_<1>)); + template struct iterator { + private: + irange const *parent_; + T value_; + bool end_; - constexpr iterator(irange const * parent, T value, bool end) - : parent_{parent} - , value_{value} - , end_{end} - {} + public: + using ret_t = + decltype(clamp(0, parent_->begin_, parent_->end_ - s32_<1>)); - constexpr ret_t operator*() const { - return unsafe_cast(value_); - } + constexpr iterator(irange const *parent, T value, bool end) + : parent_{parent}, value_{value}, end_{end} {} - constexpr auto operator++() { - auto const new_unsafe_value = value_++; + constexpr ret_t operator*() const { return unsafe_cast(value_); } - // FIXME: consolidate range checks between == and ++ - if (new_unsafe_value < parent_->end_.unsafe_value()) { - value_ = new_unsafe_value; + constexpr auto operator++() { + auto const new_unsafe_value = value_++; - } else { - end_ = true; - } + // FIXME: consolidate range checks between == and ++ + if (new_unsafe_value < parent_->end_.unsafe_value()) { + value_ = new_unsafe_value; - return *this; + } else { + end_ = true; } - constexpr bool operator==(iterator rhs) { - if (end_) { - return rhs.end_; - - } else { - return - parent_ == rhs.parent_ && - value_ == rhs.value_ && - !rhs.end_; - } - } - }; - - public: - constexpr irange( - BeginT begin, - EndT end - ) - : begin_{begin} - , end_{end} - {} - - constexpr auto begin() const { - return iterator{this, begin_.unsafe_value(), begin_ == end_}; + return *this; } - constexpr auto end() const { - // FIXME: need to find the right value for the end - return iterator{this, end_.unsafe_value() - 1, true}; + constexpr bool operator==(iterator rhs) { + if (end_) { + return rhs.end_; + + } else { + return parent_ == rhs.parent_ && value_ == rhs.value_ && + !rhs.end_; + } } }; -} \ No newline at end of file + + public: + constexpr irange(BeginT begin, EndT end) : begin_{begin}, end_{end} {} + + constexpr auto begin() const { + return iterator{ + this, begin_.unsafe_value(), begin_ == end_}; + } + + constexpr auto end() const { + // FIXME: need to find the right value for the end + return iterator{ + this, end_.unsafe_value() - 1, true}; + } +}; +} // namespace safe \ No newline at end of file diff --git a/include/safe/array.hpp b/include/safe/array.hpp index 736ce68..0e3ef78 100644 --- a/include/safe/array.hpp +++ b/include/safe/array.hpp @@ -1,206 +1,167 @@ #pragma once -#include -#include #include +#include +#include -#include #include +#include namespace safe { - template - struct array { - private: - using std_array = std::array; - std_array storage; - - public: - using value_type = typename std_array::value_type; - using size_type = typename std_array::size_type; - using difference_type = typename std_array::difference_type; - using reference = typename std_array::reference; - using const_reference = typename std_array::const_reference; - using pointer = typename std_array::pointer; - using const_pointer = typename std_array::const_pointer; - using iterator = typename std_array::iterator; - using const_iterator = typename std_array::const_iterator; - using reverse_iterator = typename std_array::reverse_iterator; - using const_reverse_iterator = typename std_array::const_reverse_iterator; - - // TODO: constructors - template - constexpr array(Us... values) - : storage({values...}) - {} - - [[nodiscard]] constexpr reference operator[]( - var> pos - ) { - return storage[pos.unsafe_value()]; - } - - [[nodiscard]] constexpr const_reference operator[]( - var> pos - ) const { - return storage[pos.unsafe_value()]; - } - - [[nodiscard]] constexpr reference at( - var> pos - ) { - return storage[pos.unsafe_value()]; - } - - [[nodiscard]] constexpr const_reference at( - var> pos - ) const { - return storage[pos.unsafe_value()]; - } - - [[nodiscard]] constexpr reference front() { - return storage.front(); - } - - [[nodiscard]] constexpr const_reference front() const { - return storage.front(); - } - - [[nodiscard]] constexpr reference back() { - return storage.back(); - } - - [[nodiscard]] constexpr const_reference back() const { - return storage.back(); - } - - // NOTE: intentionally omitting data() - - [[nodiscard]] constexpr iterator begin() { - return storage.begin(); - } - - [[nodiscard]] constexpr const_iterator begin() const { - return storage.begin(); - } - - [[nodiscard]] constexpr const_iterator cbegin() const { - return storage.cbegin(); - } - - [[nodiscard]] constexpr iterator end() { - return storage.end(); - } - - [[nodiscard]] constexpr const_iterator end() const { - return storage.end(); - } - - [[nodiscard]] constexpr const_iterator cend() const { - return storage.cend(); - } - - [[nodiscard]] constexpr reverse_iterator rbegin() { - return storage.rbegin(); - } - - [[nodiscard]] constexpr const_reverse_iterator rbegin() const { - return storage.rbegin(); - } - - [[nodiscard]] constexpr const_reverse_iterator crbegin() const { - return storage.crbegin(); - } - - [[nodiscard]] constexpr reverse_iterator rend() { - return storage.rend(); - } - - [[nodiscard]] constexpr const_reverse_iterator rend() const { - return storage.rend(); - } - - [[nodiscard]] constexpr const_reverse_iterator crend() const { - return storage.crend(); - } - - [[nodiscard]] constexpr bool empty() const { - return storage.empty(); - } - - [[nodiscard]] constexpr size_type size() const { - return storage.size(); - } - - [[nodiscard]] constexpr size_type max_size() const { - return storage.max_size(); - } - - constexpr void fill(T const & value) { - storage.fill(value); - } - - constexpr void swap(array & other) { - storage.swap(other.storage); - } - - [[nodiscard]] friend constexpr bool operator==( - array const & lhs, - array const & rhs - ) { - return lhs.storage == rhs.storage; - } - - [[nodiscard]] friend constexpr auto operator<=>( - array const & lhs, - array const & rhs - ) { - return lhs.storage <=> rhs.storage; - } - }; - - template - array(T, U...) -> array; -} +template struct array { + private: + using std_array = std::array; + std_array storage; + + public: + using value_type = typename std_array::value_type; + using size_type = typename std_array::size_type; + using difference_type = typename std_array::difference_type; + using reference = typename std_array::reference; + using const_reference = typename std_array::const_reference; + using pointer = typename std_array::pointer; + using const_pointer = typename std_array::const_pointer; + using iterator = typename std_array::iterator; + using const_iterator = typename std_array::const_iterator; + using reverse_iterator = typename std_array::reverse_iterator; + using const_reverse_iterator = typename std_array::const_reverse_iterator; + + // TODO: constructors + template + constexpr array(Us... values) : storage({values...}) {} + + [[nodiscard]] constexpr reference + operator[](var> pos) { + return storage[pos.unsafe_value()]; + } -namespace std { - template - [[nodiscard]] constexpr T & get(safe::array & a) noexcept { - return a[safe::constant]; + [[nodiscard]] constexpr const_reference + operator[](var> pos) const { + return storage[pos.unsafe_value()]; + } + + [[nodiscard]] constexpr reference + at(var> pos) { + return storage[pos.unsafe_value()]; + } + + [[nodiscard]] constexpr const_reference + at(var> pos) const { + return storage[pos.unsafe_value()]; } + [[nodiscard]] constexpr reference front() { return storage.front(); } - template - [[nodiscard]] constexpr T && get(safe::array && a) noexcept { - return a[safe::constant]; + [[nodiscard]] constexpr const_reference front() const { + return storage.front(); } + [[nodiscard]] constexpr reference back() { return storage.back(); } - template - [[nodiscard]] constexpr const T & get(safe::array const & a) noexcept { - return a[safe::constant]; + [[nodiscard]] constexpr const_reference back() const { + return storage.back(); } + // NOTE: intentionally omitting data() - template - [[nodiscard]] constexpr const T && get(safe::array const && a) noexcept { - return a[safe::constant]; + [[nodiscard]] constexpr iterator begin() { return storage.begin(); } + + [[nodiscard]] constexpr const_iterator begin() const { + return storage.begin(); + } + + [[nodiscard]] constexpr const_iterator cbegin() const { + return storage.cbegin(); + } + + [[nodiscard]] constexpr iterator end() { return storage.end(); } + + [[nodiscard]] constexpr const_iterator end() const { return storage.end(); } + + [[nodiscard]] constexpr const_iterator cend() const { + return storage.cend(); } - template - constexpr void swap( - safe::array & lhs, - safe::array & rhs - ) { - lhs.swap(rhs); + [[nodiscard]] constexpr reverse_iterator rbegin() { + return storage.rbegin(); } - template - struct tuple_size> - : std::integral_constant - {}; + [[nodiscard]] constexpr const_reverse_iterator rbegin() const { + return storage.rbegin(); + } + + [[nodiscard]] constexpr const_reverse_iterator crbegin() const { + return storage.crbegin(); + } + + [[nodiscard]] constexpr reverse_iterator rend() { return storage.rend(); } + + [[nodiscard]] constexpr const_reverse_iterator rend() const { + return storage.rend(); + } + + [[nodiscard]] constexpr const_reverse_iterator crend() const { + return storage.crend(); + } + + [[nodiscard]] constexpr bool empty() const { return storage.empty(); } + + [[nodiscard]] constexpr size_type size() const { return storage.size(); } + + [[nodiscard]] constexpr size_type max_size() const { + return storage.max_size(); + } + + constexpr void fill(T const &value) { storage.fill(value); } + + constexpr void swap(array &other) { storage.swap(other.storage); } + + [[nodiscard]] friend constexpr bool operator==(array const &lhs, + array const &rhs) { + return lhs.storage == rhs.storage; + } + + [[nodiscard]] friend constexpr auto operator<=>(array const &lhs, + array const &rhs) { + return lhs.storage <=> rhs.storage; + } +}; + +template array(T, U...) -> array; +} // namespace safe + +namespace std { +template +[[nodiscard]] constexpr T &get(safe::array &a) noexcept { + return a[safe::constant]; +} + +template +[[nodiscard]] constexpr T &&get(safe::array &&a) noexcept { + return a[safe::constant]; +} + +template +[[nodiscard]] constexpr T const &get(safe::array const &a) noexcept { + return a[safe::constant]; +} + +template +[[nodiscard]] constexpr T const &&get(safe::array const &&a) noexcept { + return a[safe::constant]; +} + +template +constexpr void swap(safe::array &lhs, safe::array &rhs) { + lhs.swap(rhs); +} + +template +struct tuple_size> : std::integral_constant { +}; - template - struct tuple_element> { - using type = T; - }; -} \ No newline at end of file +template +struct tuple_element> { + using type = T; +}; +} // namespace std \ No newline at end of file diff --git a/include/safe/big_integer.hpp b/include/safe/big_integer.hpp index 66605db..d4ae458 100644 --- a/include/safe/big_integer.hpp +++ b/include/safe/big_integer.hpp @@ -3,7 +3,7 @@ #include namespace safe { - using _big_integer::interface::big_integer; - using _big_integer::interface::to_big_integer; - using _big_integer::interface::common_integer_t; -} \ No newline at end of file +using _big_integer::interface::big_integer; +using _big_integer::interface::common_integer_t; +using _big_integer::interface::to_big_integer; +} // namespace safe \ No newline at end of file diff --git a/include/safe/big_integer/detail/algorithms.hpp b/include/safe/big_integer/detail/algorithms.hpp index 234ac53..ce273d8 100644 --- a/include/safe/big_integer/detail/algorithms.hpp +++ b/include/safe/big_integer/detail/algorithms.hpp @@ -1,69 +1,44 @@ #pragma once - #include -#include #include - +#include namespace safe::_big_integer::detail { - [[nodiscard]] constexpr auto reverse_zip_transform( - auto op - ) { - return [=]( - auto & result, - auto const & lhs, - auto const & rhs - ) -> void { - auto i = result.num_elems; - do { - i--; - result.set(i, op(lhs.get(i), rhs.get(i))); - } while (i > std::size_t{}); - }; - } - - [[nodiscard]] constexpr auto zip_transform( - auto op - ) { - return [=]( - auto & result, - auto const & lhs, - auto const & rhs - ) -> void { - for (auto i = std::size_t{}; i < result.num_elems; i++) { - result.set(i, op(lhs.get(i), rhs.get(i))); - } - }; - } - - [[nodiscard]] constexpr auto stateful_zip_transform( - auto initial_value, - auto op - ) { - return [=]( - auto & result, - auto const & lhs, - auto const & rhs - ) -> void { - auto state = initial_value; - for (auto i = std::size_t{}; i < result.num_elems; i++) { - result.set(i, op(state, lhs.get(i), rhs.get(i))); - } - }; - } - - [[nodiscard]] constexpr auto transform( - auto op - ) { - return [=]( - auto & result, - auto const & value - ) -> void { - for (auto i = std::size_t{}; i < result.num_elems; i++) { - result.set(i, op(value.get(i))); - } - }; - } -} \ No newline at end of file +[[nodiscard]] constexpr auto reverse_zip_transform(auto op) { + return [=](auto &result, auto const &lhs, auto const &rhs) -> void { + auto i = result.num_elems; + do { + i--; + result.set(i, op(lhs.get(i), rhs.get(i))); + } while (i > std::size_t{}); + }; +} + +[[nodiscard]] constexpr auto zip_transform(auto op) { + return [=](auto &result, auto const &lhs, auto const &rhs) -> void { + for (auto i = std::size_t{}; i < result.num_elems; i++) { + result.set(i, op(lhs.get(i), rhs.get(i))); + } + }; +} + +[[nodiscard]] constexpr auto stateful_zip_transform(auto initial_value, + auto op) { + return [=](auto &result, auto const &lhs, auto const &rhs) -> void { + auto state = initial_value; + for (auto i = std::size_t{}; i < result.num_elems; i++) { + result.set(i, op(state, lhs.get(i), rhs.get(i))); + } + }; +} + +[[nodiscard]] constexpr auto transform(auto op) { + return [=](auto &result, auto const &value) -> void { + for (auto i = std::size_t{}; i < result.num_elems; i++) { + result.set(i, op(value.get(i))); + } + }; +} +} // namespace safe::_big_integer::detail \ No newline at end of file diff --git a/include/safe/big_integer/detail/bitwise.hpp b/include/safe/big_integer/detail/bitwise.hpp index 76c6fab..3e9b613 100644 --- a/include/safe/big_integer/detail/bitwise.hpp +++ b/include/safe/big_integer/detail/bitwise.hpp @@ -1,15 +1,13 @@ #pragma once - -#include #include +#include #include - namespace safe::_big_integer::detail { - constexpr static auto bit_and = zip_transform(std::bit_and{}); - constexpr static auto bit_or = zip_transform(std::bit_or{}); - constexpr static auto bit_xor = zip_transform(std::bit_xor{}); - constexpr static auto bit_not = transform(std::bit_not{}); -} \ No newline at end of file +constexpr static auto bit_and = zip_transform(std::bit_and{}); +constexpr static auto bit_or = zip_transform(std::bit_or{}); +constexpr static auto bit_xor = zip_transform(std::bit_xor{}); +constexpr static auto bit_not = transform(std::bit_not{}); +} // namespace safe::_big_integer::detail \ No newline at end of file diff --git a/include/safe/big_integer/detail/compare.hpp b/include/safe/big_integer/detail/compare.hpp index 521a6d4..05d6167 100644 --- a/include/safe/big_integer/detail/compare.hpp +++ b/include/safe/big_integer/detail/compare.hpp @@ -1,55 +1,47 @@ #pragma once - -#include #include +#include #include #include - namespace safe::_big_integer::detail { - template< - std::size_t LhsNumBits, - std::size_t RhsNumBits> - [[nodiscard]] constexpr auto unsigned_compare( - storage const & lhs, - storage const & rhs - ) -> std::strong_ordering { - auto i = std::max(lhs.num_elems, rhs.num_elems); - do { - i--; - - if (lhs.get(i) < rhs.get(i)) { - return std::strong_ordering::less; - - } else if (lhs.get(i) > rhs.get(i)) { - return std::strong_ordering::greater; - } - } while (i > 0); - - return std::strong_ordering::equal; - } - - template< - std::size_t LhsNumBits, - std::size_t RhsNumBits> - [[nodiscard]] constexpr auto operator<=>( - storage const & lhs, - storage const & rhs - ) -> std::strong_ordering { - if (lhs.negative()) { - if (rhs.negative()) { - return unsigned_compare(lhs, rhs); - } else { - return std::strong_ordering::less; - } +template +[[nodiscard]] constexpr auto unsigned_compare(storage const &lhs, + storage const &rhs) + -> std::strong_ordering { + auto i = std::max(lhs.num_elems, rhs.num_elems); + do { + i--; + + if (lhs.get(i) < rhs.get(i)) { + return std::strong_ordering::less; + + } else if (lhs.get(i) > rhs.get(i)) { + return std::strong_ordering::greater; + } + } while (i > 0); + + return std::strong_ordering::equal; +} + +template +[[nodiscard]] constexpr auto operator<=>(storage const &lhs, + storage const &rhs) + -> std::strong_ordering { + if (lhs.negative()) { + if (rhs.negative()) { + return unsigned_compare(lhs, rhs); + } else { + return std::strong_ordering::less; + } + } else { + if (rhs.negative()) { + return std::strong_ordering::greater; } else { - if (rhs.negative()) { - return std::strong_ordering::greater; - } else { - return unsigned_compare(lhs, rhs); - } + return unsigned_compare(lhs, rhs); } } -} \ No newline at end of file +} +} // namespace safe::_big_integer::detail \ No newline at end of file diff --git a/include/safe/big_integer/detail/divides.hpp b/include/safe/big_integer/detail/divides.hpp index 28b592f..c8ae9e1 100644 --- a/include/safe/big_integer/detail/divides.hpp +++ b/include/safe/big_integer/detail/divides.hpp @@ -1,109 +1,96 @@ #pragma once - -#include #include -#include -#include #include +#include +#include +#include - -#include #include +#include namespace safe::_big_integer::detail { - template< - std::size_t LhsNumBits, - std::size_t RhsNumBits> - [[nodiscard]] constexpr auto largest_doubling( - storage a, - storage b - ) { - using ret_t = storage; - - ret_t ret = b; - - auto const not_big_enough = [&]() -> bool { - auto a_prime = a; - minus(a_prime, a_prime, ret); - return ret <= a_prime; - }; - - while (not_big_enough()) { - bit_shift_left(ret, ret, 1); - } +template +[[nodiscard]] constexpr auto largest_doubling(storage a, + storage b) { + using ret_t = storage; - return ret; - } - - constexpr static auto unsigned_divmod = []( - auto & quotient, - auto & remainder, - auto const & dividend, - auto const & divisor - ) -> void { - // NOTE: Algorithm is based on "Elements of Programming" - // section 5.7 Quotient + ret_t ret = b; - auto a = dividend; - auto b = divisor; + auto const not_big_enough = [&]() -> bool { + auto a_prime = a; + minus(a_prime, a_prime, ret); + return ret <= a_prime; + }; - if (a < b) { - quotient = make_storage(0); - remainder = a; + while (not_big_enough()) { + bit_shift_left(ret, ret, 1); + } - } else { - constexpr auto one = make_storage(1); - - auto c = largest_doubling(a, b); - minus(a, a, c); - quotient = one; - while (c != b) { - bit_shift_left(quotient, quotient, 1); - bit_shift_right(c, c, 1); - if (c <= a) { - minus(a, a, c); - plus(quotient, quotient, one); - } + return ret; +} + +constexpr static auto unsigned_divmod = [](auto "ient, auto &remainder, + auto const ÷nd, + auto const &divisor) -> void { + // NOTE: Algorithm is based on "Elements of Programming" + // section 5.7 Quotient + + auto a = dividend; + auto b = divisor; + + if (a < b) { + quotient = make_storage(0); + remainder = a; + + } else { + constexpr auto one = make_storage(1); + + auto c = largest_doubling(a, b); + minus(a, a, c); + quotient = one; + while (c != b) { + bit_shift_left(quotient, quotient, 1); + bit_shift_right(c, c, 1); + if (c <= a) { + minus(a, a, c); + plus(quotient, quotient, one); } - - remainder = a; } - }; + remainder = a; + } +}; - constexpr static auto divmod = []( - auto & quotient, - auto & remainder, - auto const & dividend, - auto const & divisor - ) -> void { - auto a = dividend; - auto b = divisor; - - if (a.negative()) { - if (b.negative()) { - negate(a, a); - negate(b, b); - unsigned_divmod(quotient, remainder, a, b); - - } else { - negate(a, a); - unsigned_divmod(quotient, remainder, a, b); - negate(quotient, quotient); - } +constexpr static auto divmod = [](auto "ient, auto &remainder, + auto const ÷nd, + auto const &divisor) -> void { + auto a = dividend; + auto b = divisor; - negate(remainder, remainder); + if (a.negative()) { + if (b.negative()) { + negate(a, a); + negate(b, b); + unsigned_divmod(quotient, remainder, a, b); } else { - if (b.negative()) { - negate(b, b); - unsigned_divmod(quotient, remainder, a, b); - negate(quotient, quotient); + negate(a, a); + unsigned_divmod(quotient, remainder, a, b); + negate(quotient, quotient); + } - } else { - unsigned_divmod(quotient, remainder, a, b); - } + negate(remainder, remainder); + + } else { + if (b.negative()) { + negate(b, b); + unsigned_divmod(quotient, remainder, a, b); + negate(quotient, quotient); + + } else { + unsigned_divmod(quotient, remainder, a, b); } - }; -} \ No newline at end of file + } +}; +} // namespace safe::_big_integer::detail \ No newline at end of file diff --git a/include/safe/big_integer/detail/multiplies.hpp b/include/safe/big_integer/detail/multiplies.hpp index e546cc0..fe69271 100644 --- a/include/safe/big_integer/detail/multiplies.hpp +++ b/include/safe/big_integer/detail/multiplies.hpp @@ -1,66 +1,62 @@ #pragma once - -#include #include -#include #include +#include +#include -#include #include +#include namespace safe::_big_integer::detail { - constexpr static auto unsigned_multiplies = []( - auto & result, - auto const & lhs, - auto const & rhs - ) -> void { - using result_t = std::remove_cvref_t; +constexpr static auto unsigned_multiplies = [](auto &result, auto const &lhs, + auto const &rhs) -> void { + using result_t = std::remove_cvref_t; - for (int i = 0; i < lhs.num_elems; i++) { - for (int j = 0; j < rhs.num_elems; j++) { - auto const raw_partial_product = - static_cast(lhs.get(i)) * - static_cast(rhs.get(j)); + for (int i = 0; i < lhs.num_elems; i++) { + for (int j = 0; j < rhs.num_elems; j++) { + auto const raw_partial_product = + static_cast(lhs.get(i)) * + static_cast(rhs.get(j)); - result_t partial_product{{ - static_cast(raw_partial_product & 0xffff'ffffu), - static_cast(raw_partial_product >> 32)}}; + result_t partial_product{ + {static_cast(raw_partial_product & 0xffff'ffffu), + static_cast(raw_partial_product >> 32)}}; - bit_shift_left(partial_product, partial_product, (i + j) * 32); + bit_shift_left(partial_product, partial_product, (i + j) * 32); - plus(result, result, partial_product); - } + plus(result, result, partial_product); } - }; - - constexpr static auto multiplies = []( - auto & result, - auto const & lhs, - auto const & rhs - ) -> void { - using lhs_t = std::remove_cvref_t; - using rhs_t = std::remove_cvref_t; - - if (lhs.negative()) { - lhs_t pos_lhs{}; negate(pos_lhs, lhs); - - if (rhs.negative()) { - rhs_t pos_rhs{}; negate(pos_rhs, rhs); - unsigned_multiplies(result, pos_lhs, pos_rhs); - } else { - unsigned_multiplies(result, pos_lhs, rhs); - negate(result, result); - } + } +}; + +constexpr static auto multiplies = [](auto &result, auto const &lhs, + auto const &rhs) -> void { + using lhs_t = std::remove_cvref_t; + using rhs_t = std::remove_cvref_t; + + if (lhs.negative()) { + lhs_t pos_lhs{}; + negate(pos_lhs, lhs); + + if (rhs.negative()) { + rhs_t pos_rhs{}; + negate(pos_rhs, rhs); + unsigned_multiplies(result, pos_lhs, pos_rhs); } else { - if (rhs.negative()) { - rhs_t pos_rhs{}; negate(pos_rhs, rhs); - unsigned_multiplies(result, lhs, pos_rhs); - negate(result, result); + unsigned_multiplies(result, pos_lhs, rhs); + negate(result, result); + } + } else { + if (rhs.negative()) { + rhs_t pos_rhs{}; + negate(pos_rhs, rhs); + unsigned_multiplies(result, lhs, pos_rhs); + negate(result, result); - } else { - unsigned_multiplies(result, lhs, rhs); - } + } else { + unsigned_multiplies(result, lhs, rhs); } - }; -} \ No newline at end of file + } +}; +} // namespace safe::_big_integer::detail \ No newline at end of file diff --git a/include/safe/big_integer/detail/operators.hpp b/include/safe/big_integer/detail/operators.hpp index 5d59fd7..9073d78 100644 --- a/include/safe/big_integer/detail/operators.hpp +++ b/include/safe/big_integer/detail/operators.hpp @@ -1,9 +1,8 @@ #pragma once - -#include #include +#include +#include +#include #include #include -#include -#include diff --git a/include/safe/big_integer/detail/plus.hpp b/include/safe/big_integer/detail/plus.hpp index ae28c6a..a2340c7 100644 --- a/include/safe/big_integer/detail/plus.hpp +++ b/include/safe/big_integer/detail/plus.hpp @@ -1,42 +1,32 @@ #pragma once - -#include #include #include +#include #include - namespace safe::_big_integer::detail { - constexpr static auto plus = - stateful_zip_transform( - elem_t{}, - []( - elem_t & carry, - double_elem_t const & lhs, - double_elem_t const & rhs - ){ - double_elem_t const result = - lhs + rhs + - static_cast(carry); - - carry = result >> 32; - return result & 0xffff'ffffu; - } - ); - - constexpr static auto negate = - [](auto & result, auto const & value) -> void { - std::remove_cvref_t not_value{}; - bit_not(not_value, value); - plus(result, not_value, make_storage(1)); - }; - - constexpr static auto minus = - [](auto & result, auto const & lhs, auto const & rhs) -> void { - std::remove_cvref_t negative_rhs{}; - negate(negative_rhs, rhs); - plus(result, lhs, negative_rhs); - }; -} \ No newline at end of file +constexpr static auto plus = + stateful_zip_transform(elem_t{}, [](elem_t &carry, double_elem_t const &lhs, + double_elem_t const &rhs) { + double_elem_t const result = + lhs + rhs + static_cast(carry); + + carry = result >> 32; + return result & 0xffff'ffffu; + }); + +constexpr static auto negate = [](auto &result, auto const &value) -> void { + std::remove_cvref_t not_value{}; + bit_not(not_value, value); + plus(result, not_value, make_storage(1)); +}; + +constexpr static auto minus = [](auto &result, auto const &lhs, + auto const &rhs) -> void { + std::remove_cvref_t negative_rhs{}; + negate(negative_rhs, rhs); + plus(result, lhs, negative_rhs); +}; +} // namespace safe::_big_integer::detail \ No newline at end of file diff --git a/include/safe/big_integer/detail/shift.hpp b/include/safe/big_integer/detail/shift.hpp index 95b9c4f..2bba128 100644 --- a/include/safe/big_integer/detail/shift.hpp +++ b/include/safe/big_integer/detail/shift.hpp @@ -1,100 +1,71 @@ #pragma once - -#include #include #include +#include -#include #include +#include // TODO: can we consolidate the implementations for left and right shift? namespace safe::_big_integer::detail { - template - struct shift_left { - T source; - int32_t shift_amt; - - constexpr shift_left( - T source_arg, - int32_t shift_amt_arg - ) - : source{source_arg} - , shift_amt{shift_amt_arg} - {} - - [[nodiscard]] constexpr auto get(int32_t i) const -> elem_t { - return source.get(i - shift_amt); +template struct shift_left { + T source; + int32_t shift_amt; + + constexpr shift_left(T source_arg, int32_t shift_amt_arg) + : source{source_arg}, shift_amt{shift_amt_arg} {} + + [[nodiscard]] constexpr auto get(int32_t i) const -> elem_t { + return source.get(i - shift_amt); + } +}; + +constexpr static auto bit_shift_left = + [](auto &result, auto const &lhs, + std::signed_integral auto const rhs) -> void { + auto const elem_shift_amt = rhs / 32; + auto const bit_shift_amt = rhs % 32; + + auto const lhs_shifted_upper = shift_left(lhs, elem_shift_amt); + auto const lhs_shifted_lower = shift_left(lhs, elem_shift_amt + 1); + + reverse_zip_transform([=](elem_t const upper, elem_t const lower) { + if (bit_shift_amt == 0) { + return upper; + } else { + return (upper << bit_shift_amt) | (lower >> (32 - bit_shift_amt)); } - }; - - constexpr static auto bit_shift_left = []( - auto & result, - auto const & lhs, - std::signed_integral auto const rhs - ) -> void { - auto const elem_shift_amt = rhs / 32; - auto const bit_shift_amt = rhs % 32; - - auto const lhs_shifted_upper = shift_left(lhs, elem_shift_amt); - auto const lhs_shifted_lower = shift_left(lhs, elem_shift_amt + 1); - - reverse_zip_transform([=]( - elem_t const upper, - elem_t const lower - ){ - if (bit_shift_amt == 0) { - return upper; - } else { - return - (upper << bit_shift_amt) | - (lower >> (32 - bit_shift_amt)); - } - - })(result, lhs_shifted_upper, lhs_shifted_lower); - }; - - template - struct shift_right { - T source; - int32_t shift_amt; - - constexpr shift_right( - T source_arg, - int32_t shift_amt_arg - ) - : source{source_arg} - , shift_amt{shift_amt_arg} - {} - - [[nodiscard]] constexpr auto get(int32_t i) const -> elem_t { - return source.get(i + shift_amt); + })(result, lhs_shifted_upper, lhs_shifted_lower); +}; + +template struct shift_right { + T source; + int32_t shift_amt; + + constexpr shift_right(T source_arg, int32_t shift_amt_arg) + : source{source_arg}, shift_amt{shift_amt_arg} {} + + [[nodiscard]] constexpr auto get(int32_t i) const -> elem_t { + return source.get(i + shift_amt); + } +}; + +constexpr static auto bit_shift_right = + [](auto &result, auto const &lhs, + std::signed_integral auto const rhs) -> void { + auto const elem_shift_amt = rhs / 32; + auto const bit_shift_amt = rhs % 32; + + auto const lhs_shifted_upper = shift_right(lhs, elem_shift_amt + 1); + auto const lhs_shifted_lower = shift_right(lhs, elem_shift_amt); + + zip_transform([=](elem_t const upper, elem_t const lower) { + if (bit_shift_amt == 0) { + return lower; + } else { + return (upper << (32 - bit_shift_amt)) | (lower >> bit_shift_amt); } - }; - - constexpr static auto bit_shift_right = []( - auto & result, - auto const & lhs, - std::signed_integral auto const rhs - ) -> void { - auto const elem_shift_amt = rhs / 32; - auto const bit_shift_amt = rhs % 32; - - auto const lhs_shifted_upper = shift_right(lhs, elem_shift_amt + 1); - auto const lhs_shifted_lower = shift_right(lhs, elem_shift_amt); - - zip_transform([=]( - elem_t const upper, - elem_t const lower - ){ - if (bit_shift_amt == 0) { - return lower; - } else { - return - (upper << (32 - bit_shift_amt)) | - (lower >> bit_shift_amt); - } - - })(result, lhs_shifted_upper, lhs_shifted_lower); - }; -} \ No newline at end of file + })(result, lhs_shifted_upper, lhs_shifted_lower); +}; +} // namespace safe::_big_integer::detail \ No newline at end of file diff --git a/include/safe/big_integer/detail/storage.hpp b/include/safe/big_integer/detail/storage.hpp index 134bb80..b95dae0 100644 --- a/include/safe/big_integer/detail/storage.hpp +++ b/include/safe/big_integer/detail/storage.hpp @@ -1,182 +1,170 @@ #pragma once +#include -#include #include #include +#include #include -#include - - namespace safe::_big_integer::detail { - using elem_t = std::uint32_t; - using double_elem_t = std::uint64_t; +using elem_t = std::uint32_t; +using double_elem_t = std::uint64_t; - template - struct storage { - public: - static constexpr auto num_elems = (NumBits + 31) / 32; - static constexpr auto num_bits = NumBits; +template struct storage { + public: + constexpr static auto num_elems = (NumBits + 31) / 32; + constexpr static auto num_bits = NumBits; - std::array elems{}; + std::array elems{}; - constexpr storage() = default; + constexpr storage() = default; - constexpr storage(std::array const & new_elems) { - elems = new_elems; - } - - template - constexpr storage(storage const & rhs) { - for (auto i = std::size_t{}; i < NumBits; i++) { - set(i, rhs.get(i)); - } - }; - - template - constexpr auto operator=(storage const & rhs) -> storage & { - for (auto i = std::size_t{}; i < NumBits; i++) { - set(i, rhs.get(i)); - } - return *this; - }; - - template - [[nodiscard]] constexpr auto operator==( - storage const & rhs - ) const -> bool { - for (auto i = std::size_t{}; i < std::max(num_elems, rhs.num_elems); i++) { - if (get(i) != rhs.get(i)) { - return false; - } - } - return true; - } + constexpr storage(std::array const &new_elems) { + elems = new_elems; + } - [[nodiscard]] constexpr bool negative() const { - return (elems.back() >> 31) & 1; + template + constexpr storage(storage const &rhs) { + for (auto i = std::size_t{}; i < NumBits; i++) { + set(i, rhs.get(i)); } + }; - [[nodiscard]] constexpr auto get(int32_t i) const -> elem_t { - if (i < 0) { - return 0u; - } else if (i < num_elems) { - return elems[i]; - } else { - if (negative()) { - return 0xffff'ffffu; - } else { - return 0u; - } - } + template + constexpr auto operator=(storage const &rhs) -> storage & { + for (auto i = std::size_t{}; i < NumBits; i++) { + set(i, rhs.get(i)); } + return *this; + }; - constexpr auto set(int32_t i, elem_t elem) -> void { - if (i >= 0 && i < num_elems) { - elems[i] = elem; + template + [[nodiscard]] constexpr auto + operator==(storage const &rhs) const -> bool { + for (auto i = std::size_t{}; i < std::max(num_elems, rhs.num_elems); + i++) { + if (get(i) != rhs.get(i)) { + return false; } } - }; - - template - requires (sizeof(T) <= 4 && std::signed_integral>) - [[nodiscard]] constexpr auto to_storage(T v) -> storage { - return {{static_cast(static_cast(v))}}; - } - - template - requires (sizeof(T) <= 4 && std::unsigned_integral>) - [[nodiscard]] constexpr auto to_storage(T v) -> storage<(sizeof(T) * 8) + 1> { - return {{static_cast(v)}}; + return true; } - template - requires (sizeof(T) == 8 && std::signed_integral>) - [[nodiscard]] constexpr auto to_storage(T v) -> storage<64> { - return {{ - static_cast(v), - static_cast(v >> 32) - }}; + [[nodiscard]] constexpr bool negative() const { + return (elems.back() >> 31) & 1; } - template - requires (sizeof(T) == 8 && std::unsigned_integral>) - [[nodiscard]] constexpr auto to_storage(T v) -> storage<65> { - return {{ - static_cast(v), - static_cast(v >> 32) - }}; - } - - template - [[nodiscard]] constexpr auto const & to_storage(interface::big_integer const & v) { - return v.unsafe_storage; - } - - template - [[nodiscard]] constexpr auto & to_storage(storage & v) { - return v; - } - - template - [[nodiscard]] constexpr auto const & to_storage(storage const & v) { - return v; - } - - template - [[nodiscard]] constexpr auto to_storage(std::integral_constant) { - return to_storage(value); - } - - [[nodiscard]] constexpr auto make_storage(auto v) { - return to_storage(v); - } - - [[nodiscard]] constexpr auto to_integral(std::integral auto value) { - return value; - } - - template - [[nodiscard]] constexpr auto to_integral(std::integral_constant) { - return value; - } - - template requires (NumBits > 32 && NumBits <= 64) - [[nodiscard]] constexpr auto to_integral(storage const & value) -> int64_t { - return - (static_cast(value.get(1)) << 32) | - (static_cast(value.get(0))); + [[nodiscard]] constexpr auto get(int32_t i) const -> elem_t { + if (i < 0) { + return 0u; + } else if (i < num_elems) { + return elems[i]; + } else { + if (negative()) { + return 0xffff'ffffu; + } else { + return 0u; + } + } } - template requires (NumBits <= 32) - [[nodiscard]] constexpr auto to_integral(storage const & value) -> int32_t { - return static_cast(value.get(0)); - } - - template - [[nodiscard]] constexpr auto to_integral(interface::big_integer const & value) { - return to_integral(value.unsafe_storage); + constexpr auto set(int32_t i, elem_t elem) -> void { + if (i >= 0 && i < num_elems) { + elems[i] = elem; + } } - - constexpr static auto max_width_plus_one = []( - std::size_t left_bits, - std::size_t right_bits - ) -> std::size_t { - return std::max(left_bits, right_bits) + 1; - }; - - constexpr static auto max_width = []( - std::size_t left_bits, - std::size_t right_bits - ) -> std::size_t { - return std::max(left_bits, right_bits); - }; - - constexpr static auto sum_width = []( - std::size_t left_bits, - std::size_t right_bits - ) -> std::size_t { - return left_bits + right_bits; - }; -} \ No newline at end of file +}; + +template + requires(sizeof(T) <= 4 && std::signed_integral>) +[[nodiscard]] constexpr auto to_storage(T v) -> storage { + return {{static_cast(static_cast(v))}}; +} + +template + requires(sizeof(T) <= 4 && std::unsigned_integral>) +[[nodiscard]] constexpr auto to_storage(T v) -> storage<(sizeof(T) * 8) + 1> { + return {{static_cast(v)}}; +} + +template + requires(sizeof(T) == 8 && std::signed_integral>) +[[nodiscard]] constexpr auto to_storage(T v) -> storage<64> { + return {{static_cast(v), static_cast(v >> 32)}}; +} + +template + requires(sizeof(T) == 8 && std::unsigned_integral>) +[[nodiscard]] constexpr auto to_storage(T v) -> storage<65> { + return {{static_cast(v), static_cast(v >> 32)}}; +} + +template +[[nodiscard]] constexpr auto const & +to_storage(interface::big_integer const &v) { + return v.unsafe_storage; +} + +template +[[nodiscard]] constexpr auto &to_storage(storage &v) { + return v; +} + +template +[[nodiscard]] constexpr auto const &to_storage(storage const &v) { + return v; +} + +template +[[nodiscard]] constexpr auto to_storage(std::integral_constant) { + return to_storage(value); +} + +[[nodiscard]] constexpr auto make_storage(auto v) { return to_storage(v); } + +[[nodiscard]] constexpr auto to_integral(std::integral auto value) { + return value; +} + +template +[[nodiscard]] constexpr auto to_integral(std::integral_constant) { + return value; +} + +template + requires(NumBits > 32 && NumBits <= 64) +[[nodiscard]] constexpr auto to_integral(storage const &value) + -> int64_t { + return (static_cast(value.get(1)) << 32) | + (static_cast(value.get(0))); +} + +template + requires(NumBits <= 32) +[[nodiscard]] constexpr auto to_integral(storage const &value) + -> int32_t { + return static_cast(value.get(0)); +} + +template +[[nodiscard]] constexpr auto +to_integral(interface::big_integer const &value) { + return to_integral(value.unsafe_storage); +} + +constexpr static auto max_width_plus_one = + [](std::size_t left_bits, std::size_t right_bits) -> std::size_t { + return std::max(left_bits, right_bits) + 1; +}; + +constexpr static auto max_width = [](std::size_t left_bits, + std::size_t right_bits) -> std::size_t { + return std::max(left_bits, right_bits); +}; + +constexpr static auto sum_width = [](std::size_t left_bits, + std::size_t right_bits) -> std::size_t { + return left_bits + right_bits; +}; +} // namespace safe::_big_integer::detail \ No newline at end of file diff --git a/include/safe/big_integer/interface/big_integer.hpp b/include/safe/big_integer/interface/big_integer.hpp index d22f7d4..5c3d052 100644 --- a/include/safe/big_integer/interface/big_integer.hpp +++ b/include/safe/big_integer/interface/big_integer.hpp @@ -1,446 +1,389 @@ #pragma once - -#include #include +#include #include +#include #include #include -#include - namespace safe::_big_integer::interface { - template - struct big_integer { - detail::storage unsafe_storage; - - constexpr big_integer(auto value) : unsafe_storage{detail::to_storage(value)} {} - constexpr big_integer() : unsafe_storage{} {} - - constexpr auto operator&=(auto const & rhs) -> big_integer & { - return do_assign_op(detail::bit_and, rhs); - } - - constexpr auto operator|=(auto const & rhs) -> big_integer & { - return do_assign_op(detail::bit_or, rhs); - } - - constexpr auto operator^=(auto const & rhs) -> big_integer & { - return do_assign_op(detail::bit_xor, rhs); - } - - constexpr auto operator+=(auto const & rhs) -> big_integer & { - return do_assign_op(detail::plus, rhs); - } - - constexpr auto operator-=(auto const & rhs) -> big_integer & { - return do_assign_op(detail::minus, rhs); - } - - constexpr auto operator/=(auto const & raw_rhs) -> big_integer & { - auto rhs = detail::to_storage(raw_rhs); - - decltype(unsafe_storage) q{}; - decltype(unsafe_storage) r{}; - - detail::divmod(q, r, unsafe_storage, rhs); - unsafe_storage = q; - - return *this; - } - - constexpr auto operator%=(auto const & raw_rhs) -> big_integer & { - auto rhs = detail::to_storage(raw_rhs); - - decltype(unsafe_storage) q{}; - decltype(unsafe_storage) r{}; - - detail::divmod(q, r, unsafe_storage, rhs); - unsafe_storage = r; - - return *this; - } - - constexpr auto operator<<=(auto const & raw_rhs) -> big_integer & { - auto rhs = detail::to_integral(raw_rhs); - detail::bit_shift_left(unsafe_storage, unsafe_storage, rhs); - return *this; - } - - constexpr auto operator>>=(auto const & raw_rhs) -> big_integer & { - auto rhs = detail::to_integral(raw_rhs); - detail::bit_shift_right(unsafe_storage, unsafe_storage, rhs); - return *this; - } - - constexpr auto operator-() const -> big_integer { - big_integer new_value{}; - detail::negate(new_value.unsafe_storage, unsafe_storage); - return new_value; - } - - constexpr auto operator~() const -> big_integer { - big_integer new_value{}; - detail::bit_not(new_value.unsafe_storage, unsafe_storage); - return new_value; - } - - private: - constexpr auto do_assign_op(auto op, auto const & rhs) -> big_integer & { - op(unsafe_storage, unsafe_storage, detail::to_storage(rhs)); - return *this; - } - }; - - template - struct common_integer; - - template - struct common_integer, big_integer> { - using type = big_integer; - }; - - template - struct common_integer> { - using type = big_integer(sizeof(T) * 8, RhsNumBits)>; - }; - - template - struct common_integer> { - using type = big_integer((sizeof(T) * 8) + 1, RhsNumBits)>; - }; - - template - struct common_integer, T> { - using type = big_integer(sizeof(T) * 8, LhsNumBits)>; - }; - - template - struct common_integer, T> { - using type = big_integer((sizeof(T) * 8) + 1, LhsNumBits)>; - }; - - template - using common_integer_t = typename common_integer, std::remove_cvref_t>::type; - - - - template - constexpr bool at_least_one_big_integer = false; - - template - constexpr bool at_least_one_big_integer, R> = true; - - template - constexpr bool at_least_one_big_integer> = true; - - template - constexpr bool at_least_one_big_integer, big_integer> = true; - - template - big_integer(detail::storage) -> big_integer; - - - [[nodiscard]] constexpr auto do_binary_op( - auto op, - auto width_calc, - auto const & raw_lhs, - auto const & raw_rhs - ) { - auto lhs = detail::to_storage(raw_lhs); - auto rhs = detail::to_storage(raw_rhs); - using lhs_t = std::remove_cvref_t; - using rhs_t = std::remove_cvref_t; +template struct big_integer { + detail::storage unsafe_storage; - constexpr auto result_width = width_calc(lhs_t::num_bits, rhs_t::num_bits); - detail::storage result{}; + constexpr big_integer(auto value) + : unsafe_storage{detail::to_storage(value)} {} + constexpr big_integer() : unsafe_storage{} {} - op(result, lhs, rhs); + constexpr auto operator&=(auto const &rhs) -> big_integer & { + return do_assign_op(detail::bit_and, rhs); + } - return big_integer(result); + constexpr auto operator|=(auto const &rhs) -> big_integer & { + return do_assign_op(detail::bit_or, rhs); } - template - [[nodiscard]] constexpr auto operator==( - big_integer const & lhs, - big_integer const & rhs - ) -> bool { - return lhs.unsafe_storage == rhs.unsafe_storage; + constexpr auto operator^=(auto const &rhs) -> big_integer & { + return do_assign_op(detail::bit_xor, rhs); } - template - [[nodiscard]] constexpr auto operator==( - big_integer const & lhs, - std::integral auto const & rhs - ) -> bool { - return lhs.unsafe_storage == detail::to_storage(rhs); + constexpr auto operator+=(auto const &rhs) -> big_integer & { + return do_assign_op(detail::plus, rhs); } - - - template - requires at_least_one_big_integer - [[nodiscard]] constexpr auto operator<=>( - L const & raw_lhs, - R const & raw_rhs - ) -> std::strong_ordering { - auto lhs = detail::to_storage(raw_lhs); + + constexpr auto operator-=(auto const &rhs) -> big_integer & { + return do_assign_op(detail::minus, rhs); + } + + constexpr auto operator/=(auto const &raw_rhs) -> big_integer & { auto rhs = detail::to_storage(raw_rhs); - return lhs <=> rhs; + + decltype(unsafe_storage) q{}; + decltype(unsafe_storage) r{}; + + detail::divmod(q, r, unsafe_storage, rhs); + unsafe_storage = q; + + return *this; } - template - requires at_least_one_big_integer - [[nodiscard]] constexpr auto operator+( - L const & raw_lhs, - R const & raw_rhs - ) { - return do_binary_op(detail::plus, detail::max_width_plus_one, raw_lhs, raw_rhs); + constexpr auto operator%=(auto const &raw_rhs) -> big_integer & { + auto rhs = detail::to_storage(raw_rhs); + + decltype(unsafe_storage) q{}; + decltype(unsafe_storage) r{}; + + detail::divmod(q, r, unsafe_storage, rhs); + unsafe_storage = r; + + return *this; } - template - requires at_least_one_big_integer - [[nodiscard]] constexpr auto operator-( - L const & raw_lhs, - R const & raw_rhs - ) { - return do_binary_op(detail::minus, detail::max_width_plus_one, raw_lhs, raw_rhs); + constexpr auto operator<<=(auto const &raw_rhs) -> big_integer & { + auto rhs = detail::to_integral(raw_rhs); + detail::bit_shift_left(unsafe_storage, unsafe_storage, rhs); + return *this; + } + + constexpr auto operator>>=(auto const &raw_rhs) -> big_integer & { + auto rhs = detail::to_integral(raw_rhs); + detail::bit_shift_right(unsafe_storage, unsafe_storage, rhs); + return *this; } - template + constexpr auto operator-() const -> big_integer { + big_integer new_value{}; + detail::negate(new_value.unsafe_storage, unsafe_storage); + return new_value; + } + + constexpr auto operator~() const -> big_integer { + big_integer new_value{}; + detail::bit_not(new_value.unsafe_storage, unsafe_storage); + return new_value; + } + + private: + constexpr auto do_assign_op(auto op, auto const &rhs) -> big_integer & { + op(unsafe_storage, unsafe_storage, detail::to_storage(rhs)); + return *this; + } +}; + +template struct common_integer; + +template +struct common_integer, big_integer> { + using type = big_integer; +}; + +template +struct common_integer> { + using type = big_integer(sizeof(T) * 8, RhsNumBits)>; +}; + +template +struct common_integer> { + using type = + big_integer((sizeof(T) * 8) + 1, RhsNumBits)>; +}; + +template +struct common_integer, T> { + using type = big_integer(sizeof(T) * 8, LhsNumBits)>; +}; + +template +struct common_integer, T> { + using type = + big_integer((sizeof(T) * 8) + 1, LhsNumBits)>; +}; + +template +using common_integer_t = typename common_integer, + std::remove_cvref_t>::type; + +template +constexpr bool at_least_one_big_integer = false; + +template +constexpr bool at_least_one_big_integer, R> = true; + +template +constexpr bool at_least_one_big_integer> = true; + +template +constexpr bool + at_least_one_big_integer, big_integer> = + true; + +template +big_integer(detail::storage) -> big_integer; + +[[nodiscard]] constexpr auto do_binary_op(auto op, auto width_calc, + auto const &raw_lhs, + auto const &raw_rhs) { + auto lhs = detail::to_storage(raw_lhs); + auto rhs = detail::to_storage(raw_rhs); + using lhs_t = std::remove_cvref_t; + using rhs_t = std::remove_cvref_t; + + constexpr auto result_width = width_calc(lhs_t::num_bits, rhs_t::num_bits); + detail::storage result{}; + + op(result, lhs, rhs); + + return big_integer(result); +} + +template +[[nodiscard]] constexpr auto operator==(big_integer const &lhs, + big_integer const &rhs) + -> bool { + return lhs.unsafe_storage == rhs.unsafe_storage; +} + +template +[[nodiscard]] constexpr auto operator==(big_integer const &lhs, + std::integral auto const &rhs) -> bool { + return lhs.unsafe_storage == detail::to_storage(rhs); +} + +template requires at_least_one_big_integer - [[nodiscard]] constexpr auto operator*( - L const & raw_lhs, - R const & raw_rhs - ) { - return do_binary_op(detail::multiplies, detail::sum_width, raw_lhs, raw_rhs); - } - - template +[[nodiscard]] constexpr auto operator<=>(L const &raw_lhs, R const &raw_rhs) + -> std::strong_ordering { + auto lhs = detail::to_storage(raw_lhs); + auto rhs = detail::to_storage(raw_rhs); + return lhs <=> rhs; +} + +template requires at_least_one_big_integer - [[nodiscard]] constexpr auto divmod( - L const & raw_lhs, - R const & raw_rhs - ) { - auto lhs = detail::to_storage(raw_lhs); - auto rhs = detail::to_storage(raw_rhs); - using lhs_t = std::remove_cvref_t; - using rhs_t = std::remove_cvref_t; +[[nodiscard]] constexpr auto operator+(L const &raw_lhs, R const &raw_rhs) { + return do_binary_op(detail::plus, detail::max_width_plus_one, raw_lhs, + raw_rhs); +} - lhs_t q{}; - lhs_t r{}; +template + requires at_least_one_big_integer +[[nodiscard]] constexpr auto operator-(L const &raw_lhs, R const &raw_rhs) { + return do_binary_op(detail::minus, detail::max_width_plus_one, raw_lhs, + raw_rhs); +} + +template + requires at_least_one_big_integer +[[nodiscard]] constexpr auto operator*(L const &raw_lhs, R const &raw_rhs) { + return do_binary_op(detail::multiplies, detail::sum_width, raw_lhs, + raw_rhs); +} - detail::divmod(q, r, lhs, rhs); +template + requires at_least_one_big_integer +[[nodiscard]] constexpr auto divmod(L const &raw_lhs, R const &raw_rhs) { + auto lhs = detail::to_storage(raw_lhs); + auto rhs = detail::to_storage(raw_rhs); + using lhs_t = std::remove_cvref_t; + using rhs_t = std::remove_cvref_t; - return std::make_pair(big_integer{q}, big_integer{r}); - } + lhs_t q{}; + lhs_t r{}; - template + detail::divmod(q, r, lhs, rhs); + + return std::make_pair(big_integer{q}, big_integer{r}); +} + +template requires at_least_one_big_integer - [[nodiscard]] constexpr auto operator/( - L const & lhs, - R const & rhs - ) { - auto [q, r] = divmod(lhs, rhs); - return q; - } +[[nodiscard]] constexpr auto operator/(L const &lhs, R const &rhs) { + auto [q, r] = divmod(lhs, rhs); + return q; +} - template +template requires at_least_one_big_integer - [[nodiscard]] constexpr auto operator%( - L const & lhs, - R const & rhs - ) { - auto [q, r] = divmod(lhs, rhs); - return r; - } +[[nodiscard]] constexpr auto operator%(L const &lhs, R const &rhs) { + auto [q, r] = divmod(lhs, rhs); + return r; +} - template +template requires at_least_one_big_integer - [[nodiscard]] constexpr auto operator|( - L const & raw_lhs, - R const & raw_rhs - ) { - return do_binary_op(detail::bit_or, detail::max_width, raw_lhs, raw_rhs); - } +[[nodiscard]] constexpr auto operator|(L const &raw_lhs, R const &raw_rhs) { + return do_binary_op(detail::bit_or, detail::max_width, raw_lhs, raw_rhs); +} - [[nodiscard]] constexpr auto operator&( - auto const & raw_lhs, - auto const & raw_rhs - ) { - return do_binary_op(detail::bit_and, detail::max_width, raw_lhs, raw_rhs); - } +[[nodiscard]] constexpr auto operator&(auto const &raw_lhs, + auto const &raw_rhs) { + return do_binary_op(detail::bit_and, detail::max_width, raw_lhs, raw_rhs); +} - template +template requires at_least_one_big_integer - [[nodiscard]] constexpr auto operator^( - L const & raw_lhs, - R const & raw_rhs - ) { - return do_binary_op(detail::bit_xor, detail::max_width, raw_lhs, raw_rhs); - } +[[nodiscard]] constexpr auto operator^(L const &raw_lhs, R const &raw_rhs) { + return do_binary_op(detail::bit_xor, detail::max_width, raw_lhs, raw_rhs); +} - [[nodiscard]] constexpr auto operator<<( - auto const & raw_lhs, - std::integral auto const & rhs - ) { - auto lhs = detail::to_storage(raw_lhs); - using lhs_t = std::remove_cvref_t; +[[nodiscard]] constexpr auto operator<<(auto const &raw_lhs, + std::integral auto const &rhs) { + auto lhs = detail::to_storage(raw_lhs); + using lhs_t = std::remove_cvref_t; - lhs_t result{}; + lhs_t result{}; - detail::bit_shift_left(result, lhs, rhs); + detail::bit_shift_left(result, lhs, rhs); - return big_integer{result}; - } + return big_integer{result}; +} - template - [[nodiscard]] constexpr auto operator<<( - auto const & raw_lhs, - std::integral_constant - ) { - auto lhs = detail::to_storage(raw_lhs); - using lhs_t = std::remove_cvref_t; +template +[[nodiscard]] constexpr auto operator<<(auto const &raw_lhs, + std::integral_constant) { + auto lhs = detail::to_storage(raw_lhs); + using lhs_t = std::remove_cvref_t; - using result_t = detail::storage; - result_t result{}; + using result_t = detail::storage; + result_t result{}; - detail::bit_shift_left(result, lhs, rhs); + detail::bit_shift_left(result, lhs, rhs); - return big_integer{result}; - } + return big_integer{result}; +} - template - [[nodiscard]] constexpr auto operator<<( - auto const & raw_lhs, - big_integer raw_rhs - ) { - auto lhs = detail::to_storage(raw_lhs); - using lhs_t = std::remove_cvref_t; +template +[[nodiscard]] constexpr auto operator<<(auto const &raw_lhs, + big_integer raw_rhs) { + auto lhs = detail::to_storage(raw_lhs); + using lhs_t = std::remove_cvref_t; - auto rhs = detail::to_integral(raw_rhs); + auto rhs = detail::to_integral(raw_rhs); - lhs_t result{}; - - detail::bit_shift_left(result, lhs, rhs); + lhs_t result{}; - return big_integer{result}; - } + detail::bit_shift_left(result, lhs, rhs); + return big_integer{result}; +} - [[nodiscard]] constexpr auto operator>>( - auto const & raw_lhs, - std::integral auto const & rhs - ) { - auto lhs = detail::to_storage(raw_lhs); - using lhs_t = std::remove_cvref_t; +[[nodiscard]] constexpr auto operator>>(auto const &raw_lhs, + std::integral auto const &rhs) { + auto lhs = detail::to_storage(raw_lhs); + using lhs_t = std::remove_cvref_t; - lhs_t result{}; + lhs_t result{}; - detail::bit_shift_right(result, lhs, rhs); + detail::bit_shift_right(result, lhs, rhs); - return big_integer{result}; - } + return big_integer{result}; +} - template - [[nodiscard]] constexpr auto operator>>( - auto const & raw_lhs, - std::integral_constant - ) { - auto lhs = detail::to_storage(raw_lhs); - using lhs_t = std::remove_cvref_t; +template +[[nodiscard]] constexpr auto operator>>(auto const &raw_lhs, + std::integral_constant) { + auto lhs = detail::to_storage(raw_lhs); + using lhs_t = std::remove_cvref_t; - using result_t = detail::storage; - result_t result{}; + using result_t = detail::storage; + result_t result{}; - detail::bit_shift_right(result, lhs, rhs); + detail::bit_shift_right(result, lhs, rhs); - return big_integer{result}; - } + return big_integer{result}; +} - template - [[nodiscard]] constexpr auto operator>>( - auto const & raw_lhs, - big_integer raw_rhs - ) { - auto lhs = detail::to_storage(raw_lhs); - using lhs_t = std::remove_cvref_t; +template +[[nodiscard]] constexpr auto operator>>(auto const &raw_lhs, + big_integer raw_rhs) { + auto lhs = detail::to_storage(raw_lhs); + using lhs_t = std::remove_cvref_t; - auto rhs = detail::to_integral(raw_rhs); + auto rhs = detail::to_integral(raw_rhs); - lhs_t result{}; - - detail::bit_shift_right(result, lhs, rhs); + lhs_t result{}; - return big_integer{result}; - } + detail::bit_shift_right(result, lhs, rhs); - [[nodiscard]] constexpr auto to_big_integer(auto const & value) { - return big_integer{detail::to_storage(value)}; - } + return big_integer{result}; +} + +[[nodiscard]] constexpr auto to_big_integer(auto const &value) { + return big_integer{detail::to_storage(value)}; } +} // namespace safe::_big_integer::interface -template -struct std::numeric_limits> { +template +struct std::numeric_limits< + safe::_big_integer::interface::big_integer> { using T = safe::_big_integer::interface::big_integer; - static constexpr bool is_specialized = true; - static constexpr bool is_signed = true; - static constexpr bool is_integer = true; - static constexpr bool is_exact = true; - static constexpr bool has_infinity = false; - static constexpr bool has_quiet_NaN = false; - static constexpr bool has_signaling_NaN = false; - static constexpr std::float_denorm_style has_denorm = std::denorm_absent; - static constexpr bool has_denorm_loss = false; - static constexpr std::float_round_style round_style = std::round_toward_zero; - static constexpr bool is_iec559 = false; - static constexpr bool is_bounded = true; - static constexpr bool is_modulo = false; - static constexpr std::size_t digits = NumBits; // FIXME: the standard says this should be 'int' - static constexpr int digits10 = std::numeric_limits::digits * std::log10(2); - static constexpr int max_digits10 = 0; - static constexpr int radix = 2; - static constexpr int min_exponent = 0; - static constexpr int min_exponent10 = 0; - static constexpr int max_exponent = 0; - static constexpr int max_exponent10 = 0; - static constexpr bool traps = true; // NOTE: it _might_ trap for divide by '0' - static constexpr bool tinyness_before = false; - - static constexpr auto min() noexcept -> T { + constexpr static bool is_specialized = true; + constexpr static bool is_signed = true; + constexpr static bool is_integer = true; + constexpr static bool is_exact = true; + constexpr static bool has_infinity = false; + constexpr static bool has_quiet_NaN = false; + constexpr static bool has_signaling_NaN = false; + constexpr static std::float_denorm_style has_denorm = std::denorm_absent; + constexpr static bool has_denorm_loss = false; + constexpr static std::float_round_style round_style = + std::round_toward_zero; + constexpr static bool is_iec559 = false; + constexpr static bool is_bounded = true; + constexpr static bool is_modulo = false; + constexpr static std::size_t digits = + NumBits; // FIXME: the standard says this should be 'int' + constexpr static int digits10 = + std::numeric_limits::digits * std::log10(2); + constexpr static int max_digits10 = 0; + constexpr static int radix = 2; + constexpr static int min_exponent = 0; + constexpr static int min_exponent10 = 0; + constexpr static int max_exponent = 0; + constexpr static int max_exponent10 = 0; + constexpr static bool traps = + true; // NOTE: it _might_ trap for divide by '0' + constexpr static bool tinyness_before = false; + + constexpr static auto min() noexcept -> T { return T{T{1} << ((T::num_elems * 32) - 1)}; } - static constexpr auto lowest() noexcept -> T { - return min(); - } + constexpr static auto lowest() noexcept -> T { return min(); } - static constexpr auto max() noexcept -> T { - return ~min(); - } + constexpr static auto max() noexcept -> T { return ~min(); } - static constexpr auto epsilon() noexcept -> T { - return T{0}; - } + constexpr static auto epsilon() noexcept -> T { return T{0}; } - static constexpr auto round_error() noexcept -> T { - return T{0}; - } + constexpr static auto round_error() noexcept -> T { return T{0}; } - static constexpr auto infinity() noexcept -> T { - return T{0}; - } + constexpr static auto infinity() noexcept -> T { return T{0}; } - static constexpr auto quiet_NaN() noexcept -> T { - return T{0}; - } + constexpr static auto quiet_NaN() noexcept -> T { return T{0}; } - static constexpr auto signaling_NaN() noexcept -> T { - return T{0}; - } + constexpr static auto signaling_NaN() noexcept -> T { return T{0}; } - static constexpr auto denorm_min() noexcept -> T { - return T{0}; - } + constexpr static auto denorm_min() noexcept -> T { return T{0}; } }; \ No newline at end of file diff --git a/include/safe/big_integer/interface/fwd.hpp b/include/safe/big_integer/interface/fwd.hpp index 1c6c52a..d756745 100644 --- a/include/safe/big_integer/interface/fwd.hpp +++ b/include/safe/big_integer/interface/fwd.hpp @@ -1,10 +1,7 @@ #pragma once - -#include - +#include namespace safe::_big_integer::interface { - template - struct big_integer; -} \ No newline at end of file +template struct big_integer; +} diff --git a/include/safe/constant.hpp b/include/safe/constant.hpp index 8bc3458..6752948 100644 --- a/include/safe/constant.hpp +++ b/include/safe/constant.hpp @@ -1,10 +1,11 @@ #pragma once -#include -#include #include +#include +#include namespace safe { - template - constexpr var> constant = detail::make_constant(); +template +constexpr var> constant = + detail::make_constant(); } \ No newline at end of file diff --git a/include/safe/detail/assume.hpp b/include/safe/detail/assume.hpp index d6ecb71..082741a 100644 --- a/include/safe/detail/assume.hpp +++ b/include/safe/detail/assume.hpp @@ -1,39 +1,44 @@ #pragma once - #if defined(SAFE_TESTING) - // if testing is turned on, turn assumptions into the appropriate framework's assertion - #if defined(RC_ASSERT) - // RapidCheck - #define SAFE_ASSUME(expr) RC_ASSERT(expr) +// if testing is turned on, turn assumptions into the appropriate framework's +// assertion +#if defined(RC_ASSERT) +// RapidCheck +#define SAFE_ASSUME(expr) RC_ASSERT(expr) - #elif defined(REQUIRE) - // Catch2 - #define SAFE_ASSUME(expr) REQUIRE(expr) +#elif defined(REQUIRE) +// Catch2 +#define SAFE_ASSUME(expr) REQUIRE(expr) - #elif defined(ASSERT_TRUE) - // GoogleTest - #define SAFE_ASSUME(expr) ASSERT_TRUE(expr) +#elif defined(ASSERT_TRUE) +// GoogleTest +#define SAFE_ASSUME(expr) ASSERT_TRUE(expr) - #else - #define SAFE_ASSUME(expr) - #endif #else - // adapted from section 4 in https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p1774r4.pdf - #if defined(__cpp_assume) - #define SAFE_ASSUME(expr) [[assume(expr)]] - #elif defined(__clang__) - // https://clang.llvm.org/docs/LanguageExtensions.html#builtin-assume - #define SAFE_ASSUME(expr) __builtin_assume(expr) - #elif defined(__GNUC__) && !defined(__ICC) - #if __GNUC__ >= 13 - // https://gcc.gnu.org/onlinedocs/gcc-13.1.0/gcc/Statement-Attributes.html#index-assume-statement-attribute - #define SAFE_ASSUME(expr) __attribute__((assume(expr))) - #else - // this is very questionable whether it does anything at all - #define SAFE_ASSUME(expr) if (expr) {} else { __builtin_unreachable(); } - #endif - #elif defined(_MSC_VER) || defined(__ICC) - #define SAFE_ASSUME(expr) __assume(expr) - #endif +#define SAFE_ASSUME(expr) +#endif +#else +// adapted from section 4 in +// https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p1774r4.pdf +#if defined(__cpp_assume) +#define SAFE_ASSUME(expr) [[assume(expr)]] +#elif defined(__clang__) +// https://clang.llvm.org/docs/LanguageExtensions.html#builtin-assume +#define SAFE_ASSUME(expr) __builtin_assume(expr) +#elif defined(__GNUC__) && !defined(__ICC) +#if __GNUC__ >= 13 +// https://gcc.gnu.org/onlinedocs/gcc-13.1.0/gcc/Statement-Attributes.html#index-assume-statement-attribute +#define SAFE_ASSUME(expr) __attribute__((assume(expr))) +#else +// this is very questionable whether it does anything at all +#define SAFE_ASSUME(expr) \ + if (expr) { \ + } else { \ + __builtin_unreachable(); \ + } +#endif +#elif defined(_MSC_VER) || defined(__ICC) +#define SAFE_ASSUME(expr) __assume(expr) +#endif #endif \ No newline at end of file diff --git a/include/safe/detail/concepts.hpp b/include/safe/detail/concepts.hpp index a980cfb..df32815 100644 --- a/include/safe/detail/concepts.hpp +++ b/include/safe/detail/concepts.hpp @@ -3,18 +3,16 @@ #include namespace safe::detail { - template - concept iter_like = - requires(I i) { - i++; - *i; - i != i; - }; +template +concept iter_like = requires(I i) { + i++; + *i; + i != i; +}; - template - concept range_like = - requires(R r) { - {r.begin()} -> iter_like; - r.end(); - }; -} \ No newline at end of file +template +concept range_like = requires(R r) { + { r.begin() } -> iter_like; + r.end(); +}; +} // namespace safe::detail \ No newline at end of file diff --git a/include/safe/detail/function.hpp b/include/safe/detail/function.hpp index dabaf72..2410dbd 100644 --- a/include/safe/detail/function.hpp +++ b/include/safe/detail/function.hpp @@ -5,102 +5,76 @@ #include namespace safe::detail { - using namespace boost::mp11; +using namespace boost::mp11; +template struct runtime { + [[nodiscard]] constexpr static bool check(InputT) { return true; } +}; - template< - typename ArgT, - typename InputT> - struct runtime { - [[nodiscard]] constexpr static bool check(InputT) { - return true; - } - }; - - template< - Var VarT, - typename InputT> - struct runtime { - [[nodiscard]] constexpr static bool check(InputT const & input) { - return VarT::requirement.check(input); - } - }; - - template< - Var VarT, - Var InputVarT> - struct runtime< - VarT, - InputVarT - > { - [[nodiscard]] constexpr static bool check(InputVarT const & input) { - if constexpr (VarT::requirement >= InputVarT::requirement) { - return true; - } else { - return VarT::requirement.check(input.unsafe_value()); - }; - } - }; - - template< - typename VarT, - typename ValueT> - [[nodiscard]] constexpr bool check(ValueT value) { - return detail::runtime::check(value); +template struct runtime { + [[nodiscard]] constexpr static bool check(InputT const &input) { + return VarT::requirement.check(input); } +}; - template - ReturnT ret_helper(ReturnT (T::*)(ArgTs...)); - - template - ReturnT ret_helper(ReturnT (T::*)(ArgTs...) const); - - template - mp_list args_helper(ReturnT (T::*)(ArgTs...)); - - template - mp_list args_helper(ReturnT (T::*)(ArgTs...) const); - - template - struct function_info { - using ret = decltype(ret_helper(&F::operator())); - using args = decltype(args_helper(&F::operator())); - }; - - template - struct function_info { - using ret = ReturnT; - using args = mp_list; - }; - - template - struct function_info { - using ret = ReturnT; - using args = mp_list; - }; - - template - using function_args_t = typename function_info::args; - - template - using function_ret_t = typename function_info::ret; - - template< - typename... ContractTs, - typename... ArgTs> - constexpr bool check( - mp_list, - ArgTs... args - ) { - return (check(args) && ...); +template struct runtime { + [[nodiscard]] constexpr static bool check(InputVarT const &input) { + if constexpr (VarT::requirement >= InputVarT::requirement) { + return true; + } else { + return VarT::requirement.check(input.unsafe_value()); + }; } +}; - template - [[nodiscard]] constexpr decltype(auto) unwrap_var(T && v) { - if constexpr (Var>) { - return v.unsafe_value(); - } else { - return std::forward(v); - } +template +[[nodiscard]] constexpr bool check(ValueT value) { + return detail::runtime::check(value); +} + +template +ReturnT ret_helper(ReturnT (T::*)(ArgTs...)); + +template +ReturnT ret_helper(ReturnT (T::*)(ArgTs...) const); + +template +mp_list args_helper(ReturnT (T::*)(ArgTs...)); + +template +mp_list args_helper(ReturnT (T::*)(ArgTs...) const); + +template struct function_info { + using ret = decltype(ret_helper(&F::operator())); + using args = decltype(args_helper(&F::operator())); +}; + +template +struct function_info { + using ret = ReturnT; + using args = mp_list; +}; + +template +struct function_info { + using ret = ReturnT; + using args = mp_list; +}; + +template using function_args_t = typename function_info::args; + +template using function_ret_t = typename function_info::ret; + +template +constexpr bool check(mp_list, ArgTs... args) { + return (check(args) && ...); +} + +template [[nodiscard]] constexpr decltype(auto) unwrap_var(T &&v) { + if constexpr (Var>) { + return v.unsafe_value(); + } else { + return std::forward(v); } -} \ No newline at end of file +} +} // namespace safe::detail \ No newline at end of file diff --git a/include/safe/detail/fwd.hpp b/include/safe/detail/fwd.hpp index b4c671b..4aa5da5 100644 --- a/include/safe/detail/fwd.hpp +++ b/include/safe/detail/fwd.hpp @@ -1,62 +1,48 @@ #pragma once - -#include - - #ifndef SAFE_INLINE #define SAFE_INLINE inline #endif namespace safe { - template - struct var; - - template - constexpr bool is_var_v = false; +template struct var; - template - constexpr bool is_var_v> = true; +template constexpr bool is_var_v = false; - template - concept Var = is_var_v; +template +constexpr bool is_var_v> = true; +template +concept Var = is_var_v; - [[nodiscard]] inline constexpr auto value(auto value); +[[nodiscard]] constexpr inline auto value(auto value); - namespace detail { - template - [[nodiscard]] inline constexpr auto make_constant(); - } - - template - struct unsafe_cast_ferry { - private: - T v; +namespace detail { +template +[[nodiscard]] constexpr inline auto make_constant(); +} - public: - SAFE_INLINE constexpr explicit(true) unsafe_cast_ferry( - T new_value - ) - : v{new_value} - {} +template struct unsafe_cast_ferry { + private: + T v; - [[nodiscard]] SAFE_INLINE constexpr T value() const { - return v; - } - }; + public: + SAFE_INLINE constexpr explicit(true) unsafe_cast_ferry(T new_value) + : v{new_value} {} -} + [[nodiscard]] SAFE_INLINE constexpr auto value() const -> T { return v; } +}; +} // namespace safe -template -requires(safe::Var) -[[nodiscard]] constexpr auto unsafe_cast(auto const & src) { +template + requires(safe::Var) +[[nodiscard]] constexpr auto unsafe_cast(auto const &src) { return T{safe::unsafe_cast_ferry{src}}; } -template -requires(!safe::Var) -[[nodiscard]] constexpr auto unsafe_cast(auto const & src) { +template + requires(!safe::Var) +[[nodiscard]] constexpr auto unsafe_cast(auto const &src) { return src; } diff --git a/include/safe/detail/integral_type.hpp b/include/safe/detail/integral_type.hpp index 9768775..56e8f2b 100644 --- a/include/safe/detail/integral_type.hpp +++ b/include/safe/detail/integral_type.hpp @@ -1,17 +1,13 @@ #pragma once -#include #include +#include -#include #include - +#include namespace safe::detail { - template - using integral_type = - var::lowest(), - std::numeric_limits::max()> - >; +template +using integral_type = var::lowest(), + std::numeric_limits::max()>>; } \ No newline at end of file diff --git a/include/safe/detail/make_constant.hpp b/include/safe/detail/make_constant.hpp index d3fd9e9..c06f86c 100644 --- a/include/safe/detail/make_constant.hpp +++ b/include/safe/detail/make_constant.hpp @@ -1,13 +1,11 @@ #pragma once - -#include #include - +#include namespace safe::detail { - template - [[nodiscard]] inline constexpr auto make_constant() { - return unsafe_cast>>(value); - } -} \ No newline at end of file +template +[[nodiscard]] constexpr inline auto make_constant() { + return unsafe_cast>>(value); +} +} // namespace safe::detail \ No newline at end of file diff --git a/include/safe/detail/var_assign_static_assert.hpp b/include/safe/detail/var_assign_static_assert.hpp index be20f76..7a4cdbe 100644 --- a/include/safe/detail/var_assign_static_assert.hpp +++ b/include/safe/detail/var_assign_static_assert.hpp @@ -1,25 +1,21 @@ #pragma namespace safe { - template - struct lhs_req { - constexpr static U value{}; - }; +template struct lhs_req { + constexpr static U value{}; +}; - template - struct rhs_req { - constexpr static U value{}; - }; +template struct rhs_req { + constexpr static U value{}; +}; - template - struct rhs_must_be_subset_of_lhs { - constexpr static bool value = LhsT::value >= RhsT::value; - }; +template struct rhs_must_be_subset_of_lhs { + constexpr static bool value = LhsT::value >= RhsT::value; +}; - inline constexpr void static_assert_assign_requirements(auto lhs, auto rhs) { - static_assert(rhs_must_be_subset_of_lhs< - lhs_req, - rhs_req - >::value); - } -} \ No newline at end of file +constexpr inline void static_assert_assign_requirements(auto lhs, auto rhs) { + static_assert( + rhs_must_be_subset_of_lhs, + rhs_req>::value); +} +} // namespace safe \ No newline at end of file diff --git a/include/safe/dsl.hpp b/include/safe/dsl.hpp index 0333fa9..37796e7 100644 --- a/include/safe/dsl.hpp +++ b/include/safe/dsl.hpp @@ -1,48 +1,40 @@ #pragma once - +#include #include -#include -#include -#include -#include - -#include -#include -#include +#include #include -#include #include -#include - -#include -#include -#include - +#include +#include +#include +#include +#include +#include #include #include -#include - -#include -#include - #include -#include #include - -#include - +#include +#include +#include +#include +#include +#include +#include +#include +#include namespace safe { - using safe::dsl::ival; - using safe::dsl::ival_t; +using safe::dsl::ival; +using safe::dsl::ival_t; - using safe::dsl::mask; - using safe::dsl::mask_t; +using safe::dsl::mask; +using safe::dsl::mask_t; - using safe::dsl::set; - using safe::dsl::set_t; +using safe::dsl::set; +using safe::dsl::set_t; - using safe::dsl::union_t; - using safe::dsl::intersection_t; -} \ No newline at end of file +using safe::dsl::intersection_t; +using safe::dsl::union_t; +} // namespace safe \ No newline at end of file diff --git a/include/safe/dsl/abs.hpp b/include/safe/dsl/abs.hpp index 1d06b71..f78e2da 100644 --- a/include/safe/dsl/abs.hpp +++ b/include/safe/dsl/abs.hpp @@ -1,44 +1,37 @@ #pragma once +#include #include #include #include -#include #include namespace safe::dsl { - namespace detail { - [[nodiscard]] constexpr auto abs(auto value) { - if (value < 0) { - return -value; - } else { - return decltype(-value){value}; - } - } +namespace detail { +[[nodiscard]] constexpr auto abs(auto value) { + if (value < 0) { + return -value; + } else { + return decltype(-value){value}; } +} +} // namespace detail - template - struct abs_t {}; - - template - struct abs_t : public detail::unary_op { - using val = detail::to_ival_t; - - constexpr static bool straddles_zero = - val::min < 0 && val::max > 0; - - using type = ival_t< - straddles_zero - ? 0 - : std::min(detail::abs(val::min), detail::abs(val::max)), - std::max(detail::abs(val::min), detail::abs(val::max))>; - }; - - template - [[nodiscard]] constexpr auto abs(T) - -> abs_t - { - return {}; - } -} \ No newline at end of file +template struct abs_t {}; + +template struct abs_t : public detail::unary_op { + using val = detail::to_ival_t; + + constexpr static bool straddles_zero = val::min < 0 && val::max > 0; + + using type = ival_t; +}; + +template [[nodiscard]] constexpr auto abs(T) -> abs_t { + return {}; +} +} // namespace safe::dsl \ No newline at end of file diff --git a/include/safe/dsl/add.hpp b/include/safe/dsl/add.hpp index 2e37d5b..3e7628a 100644 --- a/include/safe/dsl/add.hpp +++ b/include/safe/dsl/add.hpp @@ -1,43 +1,26 @@ #pragma once +#include #include #include #include -#include namespace safe::dsl { - template - struct add : public detail::binary_op {}; - - template< - Interval LhsT, - Interval RhsT> - struct add - : public detail::binary_op - { - using type = ival_t< - LhsT::min + RhsT::min, - LhsT::max + RhsT::max - >; - }; +template struct add : public detail::binary_op {}; - template< - Mask LhsT, - Mask RhsT> - struct add - : public detail::binary_op - { - constexpr static auto value = LhsT::value + RhsT::value; - using type = mask_t; - }; +template +struct add : public detail::binary_op { + using type = ival_t; +}; +template +struct add : public detail::binary_op { + constexpr static auto value = LhsT::value + RhsT::value; + using type = mask_t; +}; - template< - Operand LhsT, - Operand RhsT> - [[nodiscard]] constexpr auto operator+(LhsT, RhsT) - -> add - { - return {}; - } -} \ No newline at end of file +template +[[nodiscard]] constexpr auto operator+(LhsT, RhsT) -> add { + return {}; +} +} // namespace safe::dsl \ No newline at end of file diff --git a/include/safe/dsl/bit_width.hpp b/include/safe/dsl/bit_width.hpp index 1d5a1cc..a6d56e2 100644 --- a/include/safe/dsl/bit_width.hpp +++ b/include/safe/dsl/bit_width.hpp @@ -1,31 +1,25 @@ #pragma once +#include #include #include #include -#include #include #include namespace safe::dsl { - template - struct bit_width_t {}; +template struct bit_width_t {}; - template - struct bit_width_t : public detail::unary_op { - using val = detail::to_mask_t; +template struct bit_width_t : public detail::unary_op { + using val = detail::to_mask_t; - using type = ival_t< - std::bit_width(val::const_bits), - std::bit_width(val::var_bits | val::const_bits) - >; - }; + using type = ival_t; +}; - template - [[nodiscard]] constexpr auto bit_width(T) - -> bit_width_t - { - return {}; - } -} \ No newline at end of file +template +[[nodiscard]] constexpr auto bit_width(T) -> bit_width_t { + return {}; +} +} // namespace safe::dsl \ No newline at end of file diff --git a/include/safe/dsl/bitwise_and.hpp b/include/safe/dsl/bitwise_and.hpp index b09c937..6c51add 100644 --- a/include/safe/dsl/bitwise_and.hpp +++ b/include/safe/dsl/bitwise_and.hpp @@ -1,33 +1,24 @@ #pragma once +#include #include #include #include -#include - namespace safe::dsl { - template - struct bitwise_and : public detail::binary_op {}; +template +struct bitwise_and : public detail::binary_op {}; - template< - detail::Primitive LhsT, - detail::Primitive RhsT> - struct bitwise_and - : public detail::binary_op - { - using lhs = detail::to_mask_t; - using rhs = detail::to_mask_t; - constexpr static auto value = lhs::value & rhs::value; - using type = mask_t; - }; +template +struct bitwise_and : public detail::binary_op { + using lhs = detail::to_mask_t; + using rhs = detail::to_mask_t; + constexpr static auto value = lhs::value & rhs::value; + using type = mask_t; +}; - template< - Operand LhsT, - Operand RhsT> - [[nodiscard]] constexpr auto operator&(LhsT, RhsT) - -> bitwise_and - { - return {}; - } -} \ No newline at end of file +template +[[nodiscard]] constexpr auto operator&(LhsT, RhsT) -> bitwise_and { + return {}; +} +} // namespace safe::dsl \ No newline at end of file diff --git a/include/safe/dsl/bitwise_invert.hpp b/include/safe/dsl/bitwise_invert.hpp index 6b821d0..e1c7e2f 100644 --- a/include/safe/dsl/bitwise_invert.hpp +++ b/include/safe/dsl/bitwise_invert.hpp @@ -1,26 +1,22 @@ #pragma once +#include #include #include #include -#include - namespace safe::dsl { - template - struct bitwise_invert {}; +template struct bitwise_invert {}; - template - struct bitwise_invert : public detail::unary_op { - using mask_arg_t = detail::to_mask_t; - constexpr static auto value = ~mask_arg_t::value; - using type = mask_t; - }; +template +struct bitwise_invert : public detail::unary_op { + using mask_arg_t = detail::to_mask_t; + constexpr static auto value = ~mask_arg_t::value; + using type = mask_t; +}; - template - [[nodiscard]] constexpr auto operator~(T) - -> bitwise_invert - { - return {}; - } -} \ No newline at end of file +template +[[nodiscard]] constexpr auto operator~(T) -> bitwise_invert { + return {}; +} +} // namespace safe::dsl \ No newline at end of file diff --git a/include/safe/dsl/bitwise_or.hpp b/include/safe/dsl/bitwise_or.hpp index bdbc3ac..57b91fb 100644 --- a/include/safe/dsl/bitwise_or.hpp +++ b/include/safe/dsl/bitwise_or.hpp @@ -1,33 +1,24 @@ #pragma once +#include #include #include #include -#include namespace safe::dsl { - template - struct bitwise_or : public detail::binary_op {}; +template +struct bitwise_or : public detail::binary_op {}; +template +[[nodiscard]] constexpr auto operator|(LhsT, RhsT) -> bitwise_or { + return {}; +} - template< - Operand LhsT, - Operand RhsT> - [[nodiscard]] constexpr auto operator|(LhsT, RhsT) - -> bitwise_or - { - return {}; - } - - template< - detail::Primitive LhsT, - detail::Primitive RhsT> - struct bitwise_or - : public detail::binary_op - { - using lhs = detail::to_mask_t; - using rhs = detail::to_mask_t; - constexpr static auto value = lhs::value | rhs::value; - using type = mask_t; - }; -} \ No newline at end of file +template +struct bitwise_or : public detail::binary_op { + using lhs = detail::to_mask_t; + using rhs = detail::to_mask_t; + constexpr static auto value = lhs::value | rhs::value; + using type = mask_t; +}; +} // namespace safe::dsl \ No newline at end of file diff --git a/include/safe/dsl/bitwise_xor.hpp b/include/safe/dsl/bitwise_xor.hpp index d78dea3..ae0c46e 100644 --- a/include/safe/dsl/bitwise_xor.hpp +++ b/include/safe/dsl/bitwise_xor.hpp @@ -1,32 +1,24 @@ #pragma once +#include #include #include #include -#include namespace safe::dsl { - template - struct bitwise_xor : public detail::binary_op {}; +template +struct bitwise_xor : public detail::binary_op {}; - template< - detail::Primitive LhsT, - detail::Primitive RhsT> - struct bitwise_xor - : public detail::binary_op - { - using lhs = detail::to_mask_t; - using rhs = detail::to_mask_t; - constexpr static auto value = lhs::value ^ rhs::value; - using type = mask_t; - }; +template +struct bitwise_xor : public detail::binary_op { + using lhs = detail::to_mask_t; + using rhs = detail::to_mask_t; + constexpr static auto value = lhs::value ^ rhs::value; + using type = mask_t; +}; - template< - Operand LhsT, - Operand RhsT> - [[nodiscard]] constexpr auto operator^(LhsT, RhsT) - -> bitwise_xor - { - return {}; - } -} \ No newline at end of file +template +[[nodiscard]] constexpr auto operator^(LhsT, RhsT) -> bitwise_xor { + return {}; +} +} // namespace safe::dsl \ No newline at end of file diff --git a/include/safe/dsl/detail/eval_binary_op.hpp b/include/safe/dsl/detail/eval_binary_op.hpp index b41e092..f6b795a 100644 --- a/include/safe/dsl/detail/eval_binary_op.hpp +++ b/include/safe/dsl/detail/eval_binary_op.hpp @@ -1,59 +1,46 @@ #pragma once -#include -#include #include +#include +#include #include namespace safe::dsl::detail { - template - using enable_for_binary_op_t = typename std::enable_if_t>; - - template< - template typename op, - typename... LhsTs, - typename RhsT> - struct eval< - op, RhsT>, - enable_for_binary_op_t, RhsT>> - > { - using type = eval_t, eval_t>...>>; - }; - - template typename op, typename LhsT, typename... RhsTs> - struct eval< - op>, - enable_for_binary_op_t>> - > { - using type = eval_t, eval_t>...>>; - }; - - template typename op, typename... LhsTs, typename... RhsTs> - struct eval< - op, union_t>, - enable_for_binary_op_t, union_t>> - > { - using type = eval_t, union_t...>>>...>>; - }; - - template< - template typename op, - typename LhsT, - typename RhsT> - struct eval< - op, - std::enable_if_t< - std::is_base_of_v> && - !is_union_v && - !is_union_v && - ( - !is_primitive_v || - !is_primitive_v - ) - > - > { - using type = eval_t, eval_t>>; - }; -} \ No newline at end of file +template +using enable_for_binary_op_t = + typename std::enable_if_t>; + +template