diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..81b6afa0 --- /dev/null +++ b/.clang-format @@ -0,0 +1,308 @@ +--- +Language: Cpp +AccessModifierOffset: -1 +AlignAfterOpenBracket: Align +AlignArrayOfStructures: None +AlignConsecutiveAssignments: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: true +AlignConsecutiveBitFields: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveDeclarations: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveMacros: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveShortCaseStatements: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCaseArrows: false + AlignCaseColons: false +AlignConsecutiveTableGenBreakingDAGArgColons: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveTableGenCondOperatorColons: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveTableGenDefinitionColons: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: false +AlignEscapedNewlines: Left +AlignOperands: Align +AlignTrailingComments: + Kind: Always + OverEmptyLines: 0 +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowBreakBeforeNoexceptSpecifier: Never +AllowShortBlocksOnASingleLine: Never +AllowShortCaseExpressionOnASingleLine: true +AllowShortCaseLabelsOnASingleLine: true +AllowShortCompoundRequirementOnASingleLine: true +AllowShortEnumsOnASingleLine: true +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: WithoutElse +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakBeforeMultilineStrings: true +AttributeMacros: + - __capability +BinPackArguments: true +BinPackParameters: true +BitFieldColonSpacing: Both +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterExternBlock: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakAdjacentStringLiterals: true +BreakAfterAttributes: Leave +BreakAfterJavaFieldAnnotations: false +BreakAfterReturnType: None +BreakArrays: true +BreakBeforeBinaryOperators: None +BreakBeforeConceptDeclarations: Always +BreakBeforeBraces: Attach +BreakBeforeInlineASMColon: OnlyMultiline +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +BreakFunctionDefinitionParameters: false +BreakInheritanceList: BeforeColon +BreakStringLiterals: true +BreakTemplateDeclarations: Yes +ColumnLimit: 0 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: true +DisableFormat: false +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IfMacros: + - KJ_IF_MAYBE +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^' + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: '^<.*\.h>' + Priority: 1 + SortPriority: 0 + CaseSensitive: false + - Regex: '^<.*' + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: '.*' + Priority: 3 + SortPriority: 0 + CaseSensitive: false +IncludeIsMainRegex: '([-_](test|unittest))?$' +IncludeIsMainSourceRegex: '' +IndentAccessModifiers: false +IndentCaseBlocks: false +IndentCaseLabels: true +IndentExternBlock: AfterExternBlock +IndentGotoLabels: true +IndentPPDirectives: None +IndentRequiresClause: true +IndentWidth: 2 +IndentWrappedFunctionNames: false +InsertBraces: true +InsertNewlineAtEOF: false +InsertTrailingCommas: None +IntegerLiteralSeparator: + Binary: 0 + BinaryMinDigits: 0 + Decimal: 0 + DecimalMinDigits: 0 + Hex: 0 + HexMinDigits: 0 +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLines: + AtEndOfFile: false + AtStartOfBlock: false + AtStartOfFile: true +LambdaBodyIndentation: Signature +LineEnding: DeriveLF +MacroBlockBegin: '' +MacroBlockEnd: '' +MainIncludeChar: Quote +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Never +ObjCBlockIndentWidth: 2 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PackConstructorInitializers: NextLine +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakOpenParenthesis: 0 +PenaltyBreakScopeResolution: 500 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyIndentedWhitespace: 0 +PenaltyReturnTypeOnItsOwnLine: 200 +PointerAlignment: Left +PPIndentWidth: -1 +QualifierAlignment: Leave +RawStringFormats: + - Language: Cpp + Delimiters: + - cc + - CC + - cpp + - Cpp + - CPP + - 'c++' + - 'C++' + CanonicalDelimiter: '' + BasedOnStyle: google + - Language: TextProto + Delimiters: + - pb + - PB + - proto + - PROTO + EnclosingFunctions: + - EqualsProto + - EquivToProto + - PARSE_PARTIAL_TEXT_PROTO + - PARSE_TEST_PROTO + - PARSE_TEXT_PROTO + - ParseTextOrDie + - ParseTextProtoOrDie + - ParseTestProto + - ParsePartialTestProto + CanonicalDelimiter: pb + BasedOnStyle: google +ReferenceAlignment: Pointer +ReflowComments: false +RemoveBracesLLVM: false +RemoveParentheses: Leave +RemoveSemicolon: false +RequiresClausePosition: OwnLine +RequiresExpressionIndentation: OuterScope +SeparateDefinitionBlocks: Leave +ShortNamespaceLines: 1 +SkipMacroDefinitionBody: false +SortIncludes: CaseSensitive +SortJavaStaticImport: Before +SortUsingDeclarations: LexicographicNumeric +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceAroundPointerQualifiers: Default +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeJsonColon: false +SpaceBeforeParens: ControlStatements +SpaceBeforeParensOptions: + AfterControlStatements: true + AfterForeachMacros: true + AfterFunctionDefinitionName: false + AfterFunctionDeclarationName: false + AfterIfMacros: true + AfterOverloadedOperator: false + AfterPlacementOperator: true + AfterRequiresInClause: false + AfterRequiresInExpression: false + BeforeNonEmptyParentheses: false +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: Never +SpacesInContainerLiterals: true +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 +SpacesInParens: Never +SpacesInParensOptions: + ExceptDoubleParentheses: false + InCStyleCasts: false + InConditionalStatements: false + InEmptyParentheses: false + Other: false +SpacesInSquareBrackets: false +Standard: Auto +StatementAttributeLikeMacros: + - Q_EMIT +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TableGenBreakInsideDAGArg: DontBreak +TabWidth: 2 +UseTab: Never +VerilogBreakBetweenInstancePorts: true +WhitespaceSensitiveMacros: + - BOOST_PP_STRINGIZE + - CF_SWIFT_NAME + - NS_SWIFT_NAME + - PP_STRINGIZE + - STRINGIZE +... + diff --git a/.codecov.yml b/.codecov.yml new file mode 100644 index 00000000..fd894e6f --- /dev/null +++ b/.codecov.yml @@ -0,0 +1,5 @@ +ignore: + - "**/libpopcnt.h" + - "test/**" + - "benchmark/**" + - "example/**" \ No newline at end of file diff --git a/.github/actions/benchmark_epilogue/action.yml b/.github/actions/benchmark_epilogue/action.yml new file mode 100644 index 00000000..1b9b0d2e --- /dev/null +++ b/.github/actions/benchmark_epilogue/action.yml @@ -0,0 +1,79 @@ +name: "Benchmark Epilogue" +description: "Processes coverage information with lcov and uploads it to coveralls/codecov" +inputs: + workspace: + required: True + description: "Source code / workspace" + compiler: + required: True + description: 'Compiler used to build benchmark' + os: + required: True + description: 'OS used to build benchmark' + stdlib: + required: True + description: 'C++ standard library used to build benchmark' + build-output-dir: + required: true + description: 'Build output directory' + base_ref: + required: true + description: 'Base ref for PR/comparison' + cpp_compiler: + required: true + description: 'Cpp compiler to use' + c_compiler: + required: true + description: 'C compiler to use' + build_type: + required: true + description: 'Build type (release/debug)' + +runs: + using: "composite" + steps: + - name: Checkout target branch (BASE) + uses: actions/checkout@v4 + with: + ref: ${{ inputs.base_ref }} + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Install benchmark compare.py requirements + shell: bash + run: | + pip install --upgrade setuptools + pip install -r ${{ inputs.build-output-dir}}/_deps/benchmark-src/tools/requirements.txt + + - name: Benchmark Workflow + shell: bash + run: | + mv ${{ inputs.build-output-dir }}/benchmark/benchmark_result.json ${{ inputs.build-output-dir }}/benchmark/benchmark_result_new.json + cmake -B ${{ inputs.build-output-dir}} -S ${{ inputs.workspace }} \ + --preset benchmark_${{ inputs.os }}_${{ inputs.compiler }}_${{ inputs.stdlib }} \ + -DCMAKE_CXX_COMPILER=${{ inputs.cpp_compiler }} \ + -DCMAKE_C_COMPILER=${{ inputs.c_compiler }} \ + -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} + cmake --build ${{ inputs.build-output-dir}} --target benchmark --config ${{ inputs.build_type }} --parallel + ctest --test-dir ${{ inputs.build-output-dir}} --build-config ${{ inputs.build_type }} --output-on-failure --parallel + mv ${{ inputs.build-output-dir }}/benchmark/benchmark_result.json ${{ inputs.build-output-dir }}/benchmark/benchmark_result_ref.json + + - name: Compare Benchmarks + shell: bash + run: | + python3 ${{ inputs.build-output-dir}}/_deps/benchmark-src/tools/compare.py benchmarks \ + ${{ inputs.build-output-dir }}/benchmark/benchmark_result_ref.json \ + ${{ inputs.build-output-dir }}/benchmark/benchmark_result_new.json \ + | tee ${{ inputs.build-output-dir }}/benchmark/comparison.txt + + - name: Archive benchmark results + uses: actions/upload-artifact@v4 + with: + name: benchmark_${{ inputs.os }}_${{ inputs.compiler }}_${{ inputs.stdlib }}_json + path: | + ${{ inputs.build-output-dir }}/benchmark/benchmark_result_ref.json + ${{ inputs.build-output-dir }}/benchmark/benchmark_result_new.json + ${{ inputs.build-output-dir }}/benchmark/comparison.txt diff --git a/.github/actions/coverage_clang/action.yml b/.github/actions/coverage_clang/action.yml new file mode 100644 index 00000000..f3a93909 --- /dev/null +++ b/.github/actions/coverage_clang/action.yml @@ -0,0 +1,61 @@ +name: "Coverage Clang" +description: "enerates the baseline coverage" +inputs: + build-output-dir: + required: true + description: 'Build output directory' + GITHUB_TOKEN: + required: true + +runs: + using: "composite" + steps: + - name: Test + shell: bash + run: + LLVM_PROFILE_FILE=coverage.profdataraw ${{ inputs.build-output-dir }}/test/bitlib-tests + + - name: Generate Coverage Report + shell: bash + run: | + # Merge raw coverage into .profdata + llvm-profdata merge -sparse coverage.profdataraw -o coverage.profdata + + # Show summary coverage report + llvm-cov report ${{ inputs.build-output-dir }}/test/bitlib-tests \ + -instr-profile=coverage.profdata \ + -ignore-filename-regex='/usr/.*|.*_deps/.*|.*/libpopcnt\.h|.*test/inc/.*|.*test/src/.*' + + # Generate HTML report + llvm-cov show ${{ inputs.build-output-dir }}/test/bitlib-tests \ + -instr-profile=coverage.profdata \ + -format=html \ + -output-dir=out/coverage_clang \ + -ignore-filename-regex='/usr/.*|.*_deps/.*|.*/libpopcnt\.h|.*test/inc/.*|.*test/src/.*' + + llvm-cov export ${{ inputs.build-output-dir }}/test/bitlib-tests \ + -instr-profile=coverage.profdata \ + -ignore-filename-regex='/usr/.*|.*_deps/.*|.*/libpopcnt\.h|.*test/inc/.*|.*test/src/.*' \ + -format=lcov > coverage_clang.info + + - name: Upload coverage artifact + uses: actions/upload-artifact@v4 + with: + name: coverage_clang_report + path: | + out/coverage_clang + coverage.profdata + coverage_clang.info + + - name: Coveralls + uses: coverallsapp/github-action@master + with: + github-token: ${{ inputs.GITHUB_TOKEN }} + path-to-lcov: coverage_clang.info + + - name: Upload to Codecov + uses: codecov/codecov-action@v5 + with: + files: coverage_clang.info + flags: unittests + name: codecov-coverage-report diff --git a/.github/actions/coverage_gcc/action.yml b/.github/actions/coverage_gcc/action.yml new file mode 100644 index 00000000..4d3ef65c --- /dev/null +++ b/.github/actions/coverage_gcc/action.yml @@ -0,0 +1,81 @@ +name: "Coverage GCC" +description: "Installs lcov, generates the baseline coverage, runs tests, processes coverage and uploads reports" +inputs: + build-output-dir: + required: true + description: 'Build output directory' + GITHUB_TOKEN: + required: true + +runs: + using: "composite" + steps: + - name: "Install lcov" + shell: bash + run: | + sudo apt-get update + sudo apt-get install -y lcov + + - name: "Initialize 0% coverage baseline" + shell: bash + run: | + lcov --capture --initial \ + --base-directory "$GITHUB_WORKSPACE" \ + --directory "${{ inputs.build-output-dir }}" \ + --output-file coverage.baseline \ + --ignore-errors mismatch + + - name: Test + shell: bash + run: ${{ inputs.build-output-dir }}/test/bitlib-tests + + - name: Generate Coverage Report + shell: bash + run: | + # Capture actual coverage data after tests + lcov --capture \ + --directory "${{ inputs.build-output-dir }}" \ + --output-file coverage_gcc.info \ + --ignore-errors mismatch + + # Combine with baseline + lcov --add-tracefile coverage.baseline \ + --add-tracefile coverage_gcc.info\ + --output-file coverage_gcc.info + + # Clean the coverage report from system and external deps + lcov --remove coverage_gcc.info \ + '/usr/*' \ + '*/_deps/*' \ + '*/include/bitlib/bit-algorithms/libpopcnt.h' \ + '*/test/inc/*' \ + '*/test/src/*' \ + --output-file coverage_gcc.info + + # Show a summary in the logs + lcov --list coverage_gcc.info + + # Generate an HTML report + genhtml coverage_gcc.info --output-directory out/coverage_gcc + + - name: Upload coverage artifact + uses: actions/upload-artifact@v4 + with: + name: coverage_gcc_report + path: | + out/coverage_gcc + coverage.baseline + coverage_gcc.info + + - name: Coveralls + uses: coverallsapp/github-action@master + with: + github-token: ${{ inputs.GITHUB_TOKEN }} + path-to-lcov: coverage_gcc.info + + - name: Upload to Codecov + uses: codecov/codecov-action@v5 + with: + files: coverage_gcc.info + flags: unittests + name: codecov-coverage-report diff --git a/.github/actions/xwin_sdk_crt/action.yml b/.github/actions/xwin_sdk_crt/action.yml new file mode 100644 index 00000000..f4db6286 --- /dev/null +++ b/.github/actions/xwin_sdk_crt/action.yml @@ -0,0 +1,106 @@ +# .github/actions/xwin_sdk_crt/action.yml +name: "xwin_sdk_crt" +description: "Build xwin, then download & cache Windows SDK & CRT via xwin on Linux runners" + +inputs: + # xwin inputs + xwin-repo: + description: "GitHub repo for xwin (owner/repo)" + required: false + default: "https://github.com/Jake-Shadle/xwin" + xwin-hash: + description: "Git sha to checkout for xwin" + required: false + default: "11d9c9f69a374985faf833fbb77f006d4eb49cd6" + cargo-home: + description: "CARGO_HOME path (where xwin will be installed)" + required: false + default: "${{ github.workspace }}/.cargo" + + # Windows SDK/CRT inputs + sdk-version: + description: "Windows SDK version (e.g. 10.0.22621.0)" + required: true + crt-version: + description: "Windows CRT version (e.g. 14.30.30705.0)" + required: true + cache-path: + description: "Where to install & cache the SDK/CRT" + required: false + default: ".xwin_sdk_crt" + +runs: + using: "composite" + steps: + # 1) Restore xwin cache (binary + cargo registry/git) + - name: Restore xwin cache + id: xwin-cache + uses: actions/cache@v4 + with: + path: | + ${{ inputs.cargo-home }}/bin/xwin + ${{ inputs.cargo-home }}/registry + ${{ inputs.cargo-home }}/git + key: xwin-${{ inputs.xwin-hash }} + + # 2) Build & install xwin if cache miss + - name: Build & install xwin + if: steps.xwin-cache.outputs.cache-hit != 'true' + shell: bash + env: + CARGO_HOME: ${{ inputs.cargo-home }} + run: | + cargo install xwin --git ${{ inputs.xwin-repo }} --rev ${{ inputs.xwin-hash }} --locked --root $CARGO_HOME + + # 3) Save xwin cache on miss + - name: Save xwin cache + if: steps.xwin-cache.outputs.cache-hit != 'true' + uses: actions/cache@v4 + with: + path: | + ${{ inputs.cargo-home }}/bin/xwin + ${{ inputs.cargo-home }}/registry + ${{ inputs.cargo-home }}/git + key: xwin-${{ inputs.xwin-hash }} + + # 4) Restore Windows SDK/CRT cache + - name: Restore SDK/CRT cache + id: sdkcache + uses: actions/cache@v4 + with: + path: ${{ inputs.cache-path }} + key: xwin_sdk-${{ inputs.sdk-version }}_crt-${{ inputs.crt-version }} + restore-keys: | + xwin_sdk-${{ inputs.sdk-version }}_crt-${{ inputs.crt-version }} + + # 5) Download SDK & CRT via xwin if cache miss + - name: Download SDK & CRT via xwin + id: xwin + if: steps.sdkcache.outputs.cache-hit != 'true' + shell: bash + run: | + set -euo pipefail + + mkdir -p ${{ inputs.cache-path }} + + "${{ inputs.cargo-home }}/bin/xwin" --accept-license \ + --cache-dir ./.xwin-cache \ + --log-level info \ + --sdk-version ${{ inputs.sdk-version }} \ + --crt-version ${{ inputs.crt-version }} \ + splat \ + --include-debug-libs \ + --output ${{ inputs.cache-path }} + + rm -rf ./.xwin-cache + + echo "result=success" >> "$GITHUB_OUTPUT" + + + # 6) Save SDK/CRT cache on miss + - name: Save SDK/CRT cache + if: steps.sdkcache.outputs.cache-hit != 'true' && steps.xwin.outputs.result == 'success' + uses: actions/cache@v4 + with: + path: ${{ inputs.cache-path }} + key: xwin_sdk-${{ inputs.sdk-version }}_crt-${{ inputs.crt-version }} diff --git a/.github/problem_matchers/clang_problem_matcher.json b/.github/problem_matchers/clang_problem_matcher.json new file mode 100644 index 00000000..b3af1a14 --- /dev/null +++ b/.github/problem_matchers/clang_problem_matcher.json @@ -0,0 +1,17 @@ +{ + "problemMatcher": [ + { + "owner": "clang", + "pattern": [ + { + "regexp": "^(.*?):(\\d+):(\\d*):?\\s+(?:fatal\\s+)?(warning|error):\\s+(.*)$", + "file": 1, + "line": 2, + "column": 3, + "severity": 4, + "message": 5 + } + ] + } + ] +} \ No newline at end of file diff --git a/.github/problem_matchers/gcc_problem_matcher.json b/.github/problem_matchers/gcc_problem_matcher.json new file mode 100644 index 00000000..75374fac --- /dev/null +++ b/.github/problem_matchers/gcc_problem_matcher.json @@ -0,0 +1,17 @@ +{ + "problemMatcher": [ + { + "owner": "gcc", + "pattern": [ + { + "regexp": "^(.*?):(\\d+):(\\d*):?\\s+(?:fatal\\s+)?(warning|error):\\s+(.*)$", + "file": 1, + "line": 2, + "column": 3, + "severity": 4, + "message": 5 + } + ] + } + ] +} \ No newline at end of file diff --git a/.github/problem_matchers/msvc_problem_matcher.json b/.github/problem_matchers/msvc_problem_matcher.json new file mode 100644 index 00000000..5d85a18b --- /dev/null +++ b/.github/problem_matchers/msvc_problem_matcher.json @@ -0,0 +1,18 @@ +{ + "problemMatcher": [ + { + "owner": "msvc", + "pattern": [ + { + "regexp": "^(?:\\s+\\d+\\>)?([^\\s].*)\\((\\d+),?(\\d+)?(?:,\\d+,\\d+)?\\)\\s*:\\s+(error|warning|info)\\s+(\\w{1,2}\\d+)\\s*:\\s*(.*)$", + "file": 1, + "line": 2, + "column": 3, + "severity": 4, + "code": 5, + "message": 6 + } + ] + } + ] +} \ No newline at end of file diff --git a/.github/toolchains/clangcl_linux_to_windows.cmake b/.github/toolchains/clangcl_linux_to_windows.cmake new file mode 100644 index 00000000..f01d0e0a --- /dev/null +++ b/.github/toolchains/clangcl_linux_to_windows.cmake @@ -0,0 +1,63 @@ +set(CMAKE_SYSTEM_NAME Windows) +set(CMAKE_SYSTEM_PROCESSOR amd64) + +set(CMAKE_C_COMPILER clang-cl) +set(CMAKE_C_FLAGS "/winsdkdir:$ENV{WINSDKDIR} /vctoolsdir:$ENV{VCTOOLSDIR} --target=x86_64-pc-windows-msvc -fuse-ld=lld-link /imsvc $ENV{VCTOOLSDIR}/include /imsvc $ENV{WINSDKDIR}/include/ucrt /imsvc $ENV{WINSDKDIR}/include/um /imsvc $ENV{WINSDKDIR}/include/shared -Wno-unused-command-line-argument ${CMAKE_C_FLAGS}") +set(CMAKE_CXX_COMPILER clang-cl) +set(CMAKE_CXX_FLAGS "/winsdkdir:$ENV{WINSDKDIR} /vctoolsdir:$ENV{VCTOOLSDIR} --target=x86_64-pc-windows-msvc -fuse-ld=lld-link /imsvc $ENV{VCTOOLSDIR}/include /imsvc $ENV{WINSDKDIR}/include/ucrt /imsvc $ENV{WINSDKDIR}/include/um /imsvc $ENV{WINSDKDIR}/include/shared -Wno-unused-command-line-argument ${CMAKE_CXX_FLAGS}") + +set(CMAKE_AR llvm-lib) +set(CMAKE_LINKER lld-link) +set(CMAKE_MT llvm-mt) +set(CMAKE_RC_COMPILER llvm-rc) + +set(CMAKE_EXE_LINKER_FLAGS_INIT "/winsdkdir:$ENV{WINSDKDIR}") +set(CMAKE_EXE_LINKER_FLAGS_INIT "${CMAKE_EXE_LINKER_FLAGS_INIT} /vctoolsdir:$ENV{VCTOOLSDIR}") +set(CMAKE_EXE_LINKER_FLAGS_INIT "${CMAKE_EXE_LINKER_FLAGS_INIT} /libpath:$ENV{WINSDKDIR}/lib/um/x86_64") +set(CMAKE_EXE_LINKER_FLAGS_INIT "${CMAKE_EXE_LINKER_FLAGS_INIT} /libpath:$ENV{WINSDKDIR}/lib/ucrt/x86_64") +set(CMAKE_EXE_LINKER_FLAGS_INIT "${CMAKE_EXE_LINKER_FLAGS_INIT} /libpath:$ENV{VCTOOLSDIR}/lib/x86_64") + +set(CMAKE_SHARED_LINKER_FLAGS_INIT "/winsdkdir:$ENV{WINSDKDIR}") +set(CMAKE_SHARED_LINKER_FLAGS_INIT "${CMAKE_SHARED_LINKER_FLAGS_INIT} /vctoolsdir:$ENV{VCTOOLSDIR}") +set(CMAKE_SHARED_LINKER_FLAGS_INIT "${CMAKE_SHARED_LINKER_FLAGS_INIT} /libpath:$ENV{WINSDKDIR}/lib/um/x86_64") +set(CMAKE_SHARED_LINKER_FLAGS_INIT "${CMAKE_SHARED_LINKER_FLAGS_INIT} /libpath:$ENV{WINSDKDIR}/lib/ucrt/x86_64") +set(CMAKE_SHARED_LINKER_FLAGS_INIT "${CMAKE_SHARED_LINKER_FLAGS_INIT} /libpath:$ENV{VCTOOLSDIR}/lib/x86_64") + +set(CMAKE_MODULE_LINKER_FLAGS_INIT "/winsdkdir:$ENV{WINSDKDIR}") +set(CMAKE_MODULE_LINKER_FLAGS_INIT "${CMAKE_MODULE_LINKER_FLAGS_INIT} /vctoolsdir:$ENV{VCTOOLSDIR}") +set(CMAKE_MODULE_LINKER_FLAGS_INIT "${CMAKE_MODULE_LINKER_FLAGS_INIT} /libpath:$ENV{WINSDKDIR}/lib/um/x86_64") +set(CMAKE_MODULE_LINKER_FLAGS_INIT "${CMAKE_MODULE_LINKER_FLAGS_INIT} /libpath:$ENV{WINSDKDIR}/lib/ucrt/x86_64") +set(CMAKE_MODULE_LINKER_FLAGS_INIT "${CMAKE_MODULE_LINKER_FLAGS_INIT} /libpath:$ENV{VCTOOLSDIR}/lib/x86_64") + +set(CMAKE_STATIC_LINKER_FLAGS_INIT "${CMAKE_STATIC_LINKER_FLAGS_INIT} /libpath:$ENV{WINSDKDIR}/lib/um/x86_64") +set(CMAKE_STATIC_LINKER_FLAGS_INIT "${CMAKE_STATIC_LINKER_FLAGS_INIT} /libpath:$ENV{WINSDKDIR}/lib/ucrt/x86_64") +set(CMAKE_STATIC_LINKER_FLAGS_INIT "${CMAKE_STATIC_LINKER_FLAGS_INIT} /libpath:$ENV{VCTOOLSDIR}/lib/x86_64") + +# Check if the CLANGRTLIB environment variable is set and not empty +if(DEFINED ENV{CLANGRTLIB} AND NOT "$ENV{CLANGRTLIB}" STREQUAL "") + set(CLANGRTLIB_DIR "$ENV{CLANGRTLIB}") + + # Check if it is a valid directory + if(IS_DIRECTORY "${CLANGRTLIB_DIR}") + set(CLANGRTLIB_FILE "${CLANGRTLIB_DIR}/clang_rt.builtins-x86_64.lib") + + # Check if the target lib file exists + if(EXISTS "${CLANGRTLIB_FILE}") + message(STATUS "Found clang_rt.builtins-x86_64.lib in ${CLANGRTLIB_DIR}") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /libpath:${CLANGRTLIB_DIR} ${CLANGRTLIB_FILE}") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /libpath:${CLANGRTLIB_DIR} ${CLANGRTLIB_FILE}") + set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} /libpath:${CLANGRTLIB_DIR} ${CLANGRTLIB_FILE}") + set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} /libpath:${CLANGRTLIB_DIR} ${CLANGRTLIB_FILE}") + endif() + endif() +endif() + +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS_INIT} ${CMAKE_EXE_LINKER_FLAGS}" CACHE STRING "Flags used by the linker for all executables." FORCE) +set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS_INIT} ${CMAKE_SHARED_LINKER_FLAGS}" CACHE STRING "Flags used by the linker for all shared libraries." FORCE) +set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS_INIT} ${CMAKE_MODULE_LINKER_FLAGS}" CACHE STRING "Flags used by the linker for all modules." FORCE) +set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS_INIT} ${CMAKE_STATIC_LINKER_FLAGS}" CACHE STRING "Flags used by the linker for all static libraries." FORCE) + +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) \ No newline at end of file diff --git a/.github/workflows/clang_format.yaml b/.github/workflows/clang_format.yaml new file mode 100644 index 00000000..24a2e0b4 --- /dev/null +++ b/.github/workflows/clang_format.yaml @@ -0,0 +1,64 @@ +name: Clang Format Check + +on: + push: + branches: [ "master" ] + paths-ignore: + - '**/*.md' + - '*.md' + pull_request: + branches: [ "master" ] + paths-ignore: + - '**/*.md' + - '*.md' + +jobs: + clang-format: + name: Clang Format Check + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Install clang-format + shell: bash + run: | + sudo apt-get update + sudo apt-get install -y clang-format-19 + + - name: Fetch base branch + if: github.event_name == 'pull_request' + run: | + echo "Fetching base branch ${{ github.base_ref }}" + git fetch origin ${{ github.base_ref }}:${{ github.base_ref }} + + - name: Get Git Diff + id: diff + run: | + if [[ "${{ github.event_name }}" == "pull_request" ]]; then + echo "Getting diff for pull request" + git diff origin/${{ github.base_ref }}...HEAD -- '*.c' '*.cpp' '*.hpp' '*.cc' '*.cxx' '*.h' '*.hh' '*.hxx' > diff.patch + else + echo "Getting diff for commit" + git diff ${{ github.event.before }} ${{ github.event.after }} -- '*.c' '*.cpp' '*.hpp' '*.cc' '*.cxx' '*.h' '*.hh' '*.hxx' > diff.patch + fi + + - name: Check formatting + shell: bash + run: | + cat diff.patch | clang-format-diff-19 -p 1 > diff_format.patch + + if [ -s diff_format.patch ]; then + echo "❌ Code formatting issues found. See diff_format.patch." + exit 1 + fi + + - name: Upload clang-formatted diff + if: failure() + uses: actions/upload-artifact@v4 + with: + name: clang-format-diff + path: diff_format.patch diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/cmake-multi-platform.yml new file mode 100644 index 00000000..94241aba --- /dev/null +++ b/.github/workflows/cmake-multi-platform.yml @@ -0,0 +1,123 @@ +# .github/workflows/main.yaml +name: CMake on multiple platforms + +on: + push: + branches: [ "master" ] + paths-ignore: + - '**/*.md' + - '*.md' + pull_request: + branches: [ "master" ] + paths-ignore: + - '**/*.md' + - '*.md' + +jobs: + run-matrix: + uses: ./.github/workflows/cmake_build_and_test.yml + strategy: + # Set fail-fast to false to ensure that feedback is delivered for all matrix combinations. Consider changing this to true when your workflow is stable. + fail-fast: false + + # Set up a matrix to run the following 3 configurations: + # 1. + # 2. + # 3. + # + # To add more build types (Release, Debug, RelWithDebInfo, etc.) customize the build_type list. + matrix: + include: + - build_distro: windows-latest + build_os: windows + compiler: msvc + preset: tests + build_type: Release + + - build_distro: windows-latest + build_os: windows + compiler: msvc + preset: tests + + - build_distro: ubuntu-latest + build_os: linux + compiler: gcc + preset: benchmark + + - build_distro: ubuntu-latest + build_os: linux + compiler: clang + stdlib: libcxx + preset: benchmark + + - build_distro: ubuntu-latest + build_os: linux + compiler: gcc + preset: tests + + - build_distro: ubuntu-latest + build_os: linux + compiler: gcc + preset: warnings + + - build_distro: ubuntu-latest + build_os: linux + compiler: clang + preset: warnings + + - build_distro: ubuntu-latest + build_os: linux + compiler: clang + preset: tests + + - build_distro: ubuntu-latest + build_os: linux + compiler: gcc + preset: coverage + + - build_distro: ubuntu-latest + build_os: linux + compiler: clang + preset: coverage + + - build_distro: ubuntu-latest + build_os: linux + test_distro: windows-latest + test_os: windows + compiler: clangcl + preset: tests + + - build_distro: ubuntu-latest + build_os: linux + test_distro: windows-latest + test_os: windows + compiler: clangcl + preset: tests + build_type: Release + + with: + build_distro: ${{ matrix.build_distro }} + build_os: ${{ matrix.build_os }} + test_distro: ${{ matrix.test_distro || matrix.build_distro }} + test_os: ${{ matrix.test_os || matrix.build_os }} + compiler: ${{ matrix.compiler }} + stdlib: ${{ matrix.stdlib || matrix.compiler == 'msvc' && 'msvcstl' || matrix.compiler == 'clangcl' && 'msvcstl' || matrix.compiler == 'clang' && 'libcxx' || 'libstdcxx' }} + preset: ${{ matrix.preset }} + build_type: ${{ matrix.build_type || matrix.preset == 'benchmark' && 'Release' || 'Debug' }} + head_ref: ${{ github.event.pull_request.head.ref }} + base_ref: ${{ github.event.pull_request.base.ref }} + secrets: inherit + status_check: + runs-on: ubuntu-latest + needs: run-matrix + if: ${{ always() }} + steps: + - name: Check for failed jobs + shell: bash + run: | + if [ -n "${{ needs.run-matrix.result }}" != "success" ]; then + echo "Some jobs failed. Please check the logs." + exit 1 + else + echo "All jobs completed successfully." + fi \ No newline at end of file diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml deleted file mode 100644 index 4bdbf576..00000000 --- a/.github/workflows/cmake.yml +++ /dev/null @@ -1,65 +0,0 @@ -name: Build - -on: - push: - branches: [ master ] - paths-ignore: - - '**/*.md' - pull_request: - branches: [ master ] - paths-ignore: - - '**/*.md' - -env: - # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) - BUILD_TYPE: RelWithDebInfo - -jobs: - linux: - # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. - # You can convert this to a matrix build if you need cross-platform coverage. - # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-20.04, ubuntu-22.04] - compiler: - - { compiler: GNU, CC: gcc-11, CXX: g++-11 } - cpp: [17, 20] - - steps: - - name: Cancel Workflow Action - uses: styfle/cancel-workflow-action@0.9.1 - - name: Checkout - uses: actions/checkout@v2 - with: - submodules: 'true' - - name: Install requirements - run: | - sudo apt install build-essential manpages-dev software-properties-common - sudo add-apt-repository ppa:ubuntu-toolchain-r/test - sudo apt update && sudo apt install ${{ matrix.compiler.CC }} ${{ matrix.compiler.CXX }} - sudo apt-get install libgtest-dev - cmake --version - - - - name: Configure CMake - env: - CC: ${{ matrix.compiler.CC }} - CXX: ${{ matrix.compiler.CXX }} - STD: ${{ matrix.cpp }} - # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. - # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type - run: cmake -Bbuild -DBITLIB_TEST=1 -DBITLIB_GTEST_REPEAT=10 -DBITLIB_EXAMPLE=1 -DCMAKE_CXX_STANDARD=${STD} - - - - name: Build - # Build your program with the given configuration - run: cmake --build build -- -j - - - name: Test - run: ctest --test-dir build -j $(nproc --all) - #run: ./bin/bitlib-tests --gtest_repeat=10 --gtest_break_on_failure --gtest_brief=1 - - - name: Run Example - run: ./build/example/example1 diff --git a/.github/workflows/cmake_build_and_test.yml b/.github/workflows/cmake_build_and_test.yml new file mode 100644 index 00000000..bd88cb01 --- /dev/null +++ b/.github/workflows/cmake_build_and_test.yml @@ -0,0 +1,322 @@ +# .github/workflows/reusable.yaml +name: Reusable Workflow + +on: + workflow_call: + inputs: + build_distro: + required: true + type: string + build_os: + required: true + type: string + test_distro: + required: true + type: string + test_os: + required: true + type: string + compiler: + required: true + type: string + stdlib: + required: true + type: string + preset: + required: true + type: string + build_type: + required: true + type: string + head_ref: + required: true + type: string + base_ref: + required: true + type: string + +jobs: + build: + runs-on: ${{ inputs.build_distro }} + steps: + - uses: actions/checkout@v4 + + - name: Download & Cache xwin + SDK/CRT + if: inputs.build_os == 'linux' && inputs.test_os == 'windows' && inputs.compiler == 'clangcl' + uses: ./.github/actions/xwin_sdk_crt + with: + sdk-version: '10.0.26100' + crt-version: '14.44.17.14' + + - name: "SDK/CRT Env Vars" + if: inputs.build_os == 'linux' && inputs.test_os == 'windows' && inputs.compiler == 'clangcl' + shell: bash + run: | + echo "WINSDKDIR=${{ github.workspace }}/.xwin_sdk_crt/sdk" >> "$GITHUB_ENV" + echo "VCTOOLSDIR=${{ github.workspace }}/.xwin_sdk_crt/crt" >> "$GITHUB_ENV" + + - name: "Install Clang 19" + id: install_clang + if: inputs.build_os == 'linux' && (inputs.compiler == 'clang' || inputs.compiler == 'clangcl') + shell: bash + run: | + sudo apt-get update + sudo apt-get install -y wget gnupg lsb-release + wget https://apt.llvm.org/llvm.sh + chmod +x llvm.sh + sudo ./llvm.sh 19 all + # Install libc++ and libc++abi for Clang 19 + sudo apt-get install -y libc++-19-dev libc++abi-19-dev + sudo ln -sf /usr/bin/llvm-cov-19 /usr/local/bin/llvm-cov + sudo ln -sf /usr/bin/llvm-profdata-19 /usr/local/bin/llvm-profdata + sudo ln -sf /usr/bin/clang-19 /usr/local/bin/clang + sudo ln -sf /usr/bin/clang++-19 /usr/local/bin/clang++ + sudo ln -sf /usr/bin/clang-format-19 /usr/local/bin/clang-format + sudo ln -sf /usr/bin/clang-cl-19 /usr/local/bin/clang-cl + sudo ln -sf /usr/bin/llvm-lib-19 /usr/local/bin/llvm-lib + sudo ln -sf /usr/bin/lld-link-19 /usr/local/bin/lld-link + sudo ln -sf /usr/bin/llvm-mt-19 /usr/local/bin/llvm-mt + sudo ln -sf /usr/bin/llvm-rc-19 /usr/local/bin/llvm-rc + LLVM_VERSION=$(clang-cl --version | head -n1 | grep -oP '\d+\.\d+\.\d+') + echo "LLVM_VERSION=${LLVM_VERSION}" >> "$GITHUB_OUTPUT" + + - name: Restore clang-cl rt libs from cache + id: cache_clang_cl_rt_libs + if: inputs.build_os != 'windows' && inputs.compiler == 'clangcl' + uses: actions/cache@v4 + with: + path: '${{ github.workspace }}/.windows-rt-libs' + key: clangcl_windows_rt_libs_${{ steps.install_clang.outputs.LLVM_VERSION }} + + - name: "Get clang-cl rt libs" + if: inputs.build_os != 'windows' && inputs.compiler == 'clangcl' && steps.cache_clang_cl_rt_libs.outputs.cache-hit != 'true' + env: + LLVM_VERSION: ${{ steps.install_clang.outputs.LLVM_VERSION }} + OUTPUT_DIR: '${{ github.workspace }}/.windows-rt-libs' + shell: bash + run: | + # Download and extract the clang-cl runtime libraries + LLVM_TAG="llvmorg-${LLVM_VERSION}" + TAR_NAME="clang+llvm-${LLVM_VERSION}-x86_64-pc-windows-msvc.tar.xz" + LLVM_URL="https://github.com/llvm/llvm-project/releases/download/${LLVM_TAG}/${TAR_NAME}" + + # === Download === + echo "[*] Downloading LLVM Windows tarball: $LLVM_URL" + curl -L -o "${TAR_NAME}" "${LLVM_URL}" + + # === Prepare output dir === + rm -rf "${OUTPUT_DIR}" + mkdir -p "${OUTPUT_DIR}" + TAR_DIR=$(pwd) + pushd "${OUTPUT_DIR}" + + # === Extract only clang_rt.*.lib files === + echo "[*] Extracting clang_rt.*.lib files to '${OUTPUT_DIR}'..." + tar -xf "${TAR_DIR}/${TAR_NAME}" \ + --strip-components=6 \ + --wildcards '*/lib/clang/*/lib/windows/clang_rt.*.lib' + + popd + + ls -al "${OUTPUT_DIR}" + ls -al . + + # === Cleanup === + echo "[*] Cleaning up..." + rm -f "${TAR_NAME}" + + echo "[✓] Done. Runtime libraries copied to: ${OUTPUT_DIR}" + + - name: Set reusable strings + id: strings + shell: bash + run: | + if [[ "${{ inputs.compiler }}" == "gcc" ]]; then + echo "c_compiler=gcc" >> "$GITHUB_OUTPUT" + echo "cpp_compiler=g++" >> "$GITHUB_OUTPUT" + elif [[ "${{ inputs.compiler }}" == "clang" ]]; then + echo "c_compiler=clang" >> "$GITHUB_OUTPUT" + echo "cpp_compiler=clang++" >> "$GITHUB_OUTPUT" + elif [[ "${{ inputs.compiler }}" == "clangcl" ]]; then + echo "c_compiler=clang-cl" >> "$GITHUB_OUTPUT" + echo "cpp_compiler=clang-cl" >> "$GITHUB_OUTPUT" + elif [[ "${{ inputs.compiler }}" == "msvc" ]]; then + echo "c_compiler=cl.exe" >> "$GITHUB_OUTPUT" + echo "cpp_compiler=cl.exe" >> "$GITHUB_OUTPUT" + fi + if [[ "${{ inputs.preset}}" == "benchmark" ]]; then + echo "cmake_target=bitlib-bench" >> "$GITHUB_OUTPUT" + else + # tests, warnings, coverage + echo "cmake_target=bitlib-tests" >> "$GITHUB_OUTPUT" + fi + if [[ "${{ inputs.build_os }}" != "${{ inputs.test_os }}" ]]; then + echo "toolchain_file=.github/toolchains/${{ inputs.compiler }}_${{ inputs.build_os }}_to_${{ inputs.test_os }}.cmake" >> "$GITHUB_OUTPUT" + else + echo "toolchain_file=" >> "$GITHUB_OUTPUT" + fi + echo "build-output-dir=$(readlink -f '${{ github.workspace }}/../build')" >> "$GITHUB_OUTPUT" + + - name: Configure CMake + shell: bash + env: + CMAKE_TOOLCHAIN_FILE: ${{ steps.strings.outputs.toolchain_file }} + CLANGRTLIB: '${{ github.workspace }}/.windows-rt-libs' + run: | + # Configure + cmake \ + -B '${{ steps.strings.outputs.build-output-dir }}' \ + -S '${{ github.workspace }}' \ + --preset=${{ inputs.preset }}_${{ inputs.test_os }}_${{ inputs.compiler }}_${{ inputs.stdlib }} \ + ${{ inputs.build_type && format('-DCMAKE_BUILD_TYPE={0}', inputs.build_type) }} + + # Delete any coverage files created from CMake probing + find '${{ github.workspace }}' \( -name '*.gcno' -o -name '*.gcda' \) -delete + find '${{ steps.strings.outputs.build-output-dir }}' \( -name '*.gcno' -o -name '*.gcda' \) -delete + + - name: Register Compiler Problem Matcher + shell: bash + run: | + if [[ "${{ inputs.compiler }}" == "clangcl" ]]; then + echo "::add-matcher::.github/problem_matchers/msvc_problem_matcher.json" + else + echo "::add-matcher::.github/problem_matchers/${{ inputs.compiler}}_problem_matcher.json" + fi + + - name: Build + shell: bash + run: | + cmake --build '${{ steps.strings.outputs.build-output-dir }}' \ + --target ${{ steps.strings.outputs.cmake_target }} \ + --config ${{ inputs.build_type }} \ + --parallel + + - name: Unregister Compiler Problem Matcher + shell: bash + run: | + if [[ "${{ inputs.compiler }}" == "clangcl" ]]; then + echo "::remove-matcher owner=msvc::" + else + echo "::remove-matcher owner=${{ inputs.compiler}}::" + fi + + - name: Upload build dir + uses: actions/upload-artifact@v4 + with: + name: ${{ inputs.preset }}_${{ inputs.test_os }}_${{ inputs.compiler }}_${{ inputs.stdlib }}_${{ inputs.build_type }}_${{ github.head_ref || github.ref_name }} + path: '${{ steps.strings.outputs.build-output-dir }}' + + test: + runs-on: ${{ inputs.test_distro }} + needs: build + steps: + - uses: actions/checkout@v4 + + - name: "Install LLVM 19 toolchain" + if: inputs.test_os == 'linux' && inputs.compiler == 'clang' + shell: bash + run: | + sudo apt-get update + sudo apt-get install -y wget gnupg lsb-release + wget https://apt.llvm.org/llvm.sh + chmod +x llvm.sh + sudo ./llvm.sh 19 + sudo ln -sf /usr/bin/llvm-cov-19 /usr/local/bin/llvm-cov + sudo ln -sf /usr/bin/llvm-profdata-19 /usr/local/bin/llvm-profdata + + - name: "Install Clang 19 Libcxx" + if: inputs.test_os == 'linux' && inputs.stdlib == 'libcxx' + shell: bash + run: | + # Install libc++ and libc++abi for Clang 19 + sudo apt-get install -y libc++-19-dev libc++abi-19-dev + + - name: Download build dir + uses: actions/download-artifact@v4 + with: + name: ${{ inputs.preset }}_${{ inputs.test_os }}_${{ inputs.compiler }}_${{ inputs.stdlib }}_${{ inputs.build_type }}_${{ github.head_ref || github.ref_name }} + path: '${{ github.workspace }}/../build' + + - name: Set reusable strings + id: strings + shell: bash + run: | + if [[ "${{ inputs.preset }}" == "benchmark" ]]; then + chmod +x '${{ github.workspace }}/../build/benchmark/bitlib-bench'* + echo 'binary-sub-dir=benchmark' >> "$GITHUB_OUTPUT" + echo "cmake_target=bitlib-bench" >> "$GITHUB_OUTPUT" + else + # tests, warnings, coverage + chmod +x '${{ github.workspace }}/../build/test/bitlib-tests'* + echo 'binary-sub-dir=test' >> "$GITHUB_OUTPUT" + echo "cmake_target=bitlib-tests" >> "$GITHUB_OUTPUT" + fi + echo "build-output-dir=$(readlink -f '${{ github.workspace }}/../build')" >> "$GITHUB_OUTPUT" + + - name: Run coverage for Clang + if: inputs.preset == 'coverage' && inputs.compiler == 'clang' + uses: ./.github/actions/coverage_clang + with: + build-output-dir: ${{ steps.strings.outputs.build-output-dir }} + GITHUB_TOKEN: ${{ secrets.github_token }} + + - name: Run coverage for GCC + if: inputs.preset == 'coverage' && inputs.compiler == 'gcc' + uses: ./.github/actions/coverage_gcc + with: + build-output-dir: ${{ steps.strings.outputs.build-output-dir }} + GITHUB_TOKEN: ${{ secrets.github_token }} + + - name: Test + if: inputs.preset != 'coverage' + shell: bash + run: | + ctest --test-dir ${{ steps.strings.outputs.build-output-dir }} \ + --build-config ${{ inputs.build_type }} \ + --output-on-failure \ + --parallel + + - name: Download base_ref build + id: base_ref_build + if: inputs.preset == 'benchmark' && github.event_name == 'pull_request' + uses: dawidd6/action-download-artifact@v6 + with: + branch: ${{ github.event.pull_request.base.ref }} + name: ${{ inputs.preset }}_${{ inputs.test_os }}_${{ inputs.compiler }}_${{ inputs.stdlib }}_${{ inputs.build_type }}_${{ github.event.pull_request.base.ref }} + workflow_conclusion: success # only successful runs + path: ${{ steps.strings.outputs.build-output-dir }}/base_ref_build + if_no_artifact_found: 'warn' + + - name: Run base_ref benchmark + if: inputs.preset == 'benchmark' && github.event_name == 'pull_request' && steps.base_ref_build.outcome == 'success' + shell: bash + run: | + ctest --test-dir ${{ steps.strings.outputs.build-output-dir }}/base_ref_build \ + --build-config ${{ inputs.build_type }} \ + --output-on-failure \ + --parallel + + - name: Setup Python + if: inputs.preset == 'benchmark' && github.event_name == 'pull_request' && steps.base_ref_build.outcome == 'success' + uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Compare Benchmarks + if: inputs.preset == 'benchmark' && github.event_name == 'pull_request' && steps.base_ref_build.outcome == 'success' + shell: bash + run: | + pip install --upgrade setuptools + python -m pip install -r '${{ steps.strings.outputs.build-output-dir }}/_deps/benchmark-src/tools/requirements.txt' + python3 '${{ steps.strings.outputs.build-output-dir }}/_deps/benchmark-src/tools/compare.py' benchmarks \ + '${{ steps.strings.outputs.build-output-dir }}/base_ref_build/benchmark/benchmark_result.json' \ + '${{ steps.strings.outputs.build-output-dir }}/benchmark/benchmark_result.json' \ + | tee '${{ steps.strings.outputs.build-output-dir }}/benchmark/comparison.txt' + + - name: Archive benchmark results + if: inputs.preset == 'benchmark' + uses: actions/upload-artifact@v4 + with: + name: benchmark_${{ inputs.test_os }}_${{ inputs.compiler }}_${{ inputs.stdlib }}_${{ inputs.build_type }}_${{ github.head_ref || github.ref_name }}_json + path: | + ${{ steps.strings.outputs.build-output-dir }}/benchmark/benchmark_result.json diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml deleted file mode 100644 index 85591956..00000000 --- a/.github/workflows/coverage.yml +++ /dev/null @@ -1,96 +0,0 @@ -name: Coverage - -on: - push: - branches: [ master ] - paths-ignore: - - '**/*.md' - pull_request: - branches: [ master ] - paths-ignore: - - '**/*.md' - - -env: - # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) - BUILD_TYPE: RelWithDebInfo - -jobs: - linux: - # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. - # You can convert this to a matrix build if you need cross-platform coverage. - # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-20.04] - compiler: - - { compiler: GNU, CC: gcc-11, CXX: g++-11 } - - steps: - - name: Cancel Workflow Action - uses: styfle/cancel-workflow-action@0.9.1 - - name: Checkout - uses: actions/checkout@v2 - with: - submodules: 'true' - - uses: conda-incubator/setup-miniconda@v2 - with: - miniconda-version: "latest" - activate-environment: test - channels: conda-forge,defaults - channel-priority: true - python-version: 3.8 - - name: Install requirements - shell: bash -l {0} - run: | - sudo apt install build-essential manpages-dev software-properties-common - sudo add-apt-repository ppa:ubuntu-toolchain-r/test - sudo apt update && sudo apt install lcov ${{ matrix.compiler.CC }} ${{ matrix.compiler.CXX }} - sudo apt install libgtest-dev - conda activate test - pip install --user lcov - conda install cppcheck cpplint - cmake --version - - - name: Configure CMake - shell: bash -l {0} - env: - CC: ${{ matrix.compiler.CC }} - CXX: ${{ matrix.compiler.CXX }} - # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. - # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type - run: | - which cpplint - cmake -Bbuild -DBITLIB_TEST=1 -DBITLIB_COVERAGE=1 -DCMAKE_CXX_CPPCHECK="cppcheck;--std=c++17;--file-filter=*BitLib*" -DCMAKE_CXX_CPPLINT="cpplint;--linelength=140;" - - - - name: Build - shell: bash -l {0} - # Build your program with the given configuration - run: | - cmake --build build -- -j - #run: make bitlib-tests - - - name: Test - shell: bash -l {0} - run: ctest --test-dir build -j $(nproc --all) - #run: ./bin/bitlib-tests --gtest_repeat=10 --gtest_break_on_failure --gtest_brief=1 - - - name: Run lcov - shell: bash -l {0} - run: | - lcov --directory build/test/CMakeFiles/bitlib-tests.dir/src --gcov-tool /usr/bin/gcov-11 --output-file lcov.out --include "*bitlib*" -c - lcov -r lcov.out "*/usr/*" -r lcov.out "*_deps/*" -r lcov.out "*unit-tests*" -r lcov.out "*ext/*" -r lcov.out "*libpopcnt*" -o lcov_filtered.out - - #- name: Code Coverage Report - #uses: romeovs/lcov-reporter-action@v0.2.11 - #with: - #github-token: ${{ secrets.GITHUB_TOKEN }} - #lcov-file: lcov_filtered.out - - - name: Coveralls - uses: coverallsapp/github-action@master - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - path-to-lcov: lcov_filtered.out diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..0d464ac5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +**/.claude/settings.local.json +.vs \ No newline at end of file diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 29bb15de..00000000 --- a/.gitmodules +++ /dev/null @@ -1,9 +0,0 @@ -[submodule "benchmark/ext/BitArray"] - path = benchmark/ext/BitArray - url = https://github.com/noporpoise/BitArray.git -[submodule "benchmark/ext/itsy_bitsy"] - path = benchmark/ext/itsy_bitsy - url = https://github.com/ThePhD/itsy_bitsy.git -[submodule "benchmark/ext/dynamic_bitset"] - path = benchmark/ext/dynamic_bitset - url = https://github.com/pinam45/dynamic_bitset.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 4668a909..4cbea98a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,71 +1,116 @@ -# specify the C++ standard -cmake_minimum_required(VERSION 3.14) +cmake_minimum_required(VERSION 3.20) -# set the project name -project(Bit-Vector VERSION 0.3.0) - -# set output directory of builds -#set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) - -# set CXX standard -# Things seem to be faster in cxx 20, and there is also std::shift_* -# Should fall back on 17 if 20 is not supported -set(CMAKE_CXX_STANDARD 17 CACHE STRING "C++ standard to be used") -set(CMAKE_CXX_STANDARD_REQUIRED ON) +if (POLICY CMP0168) + cmake_policy(SET CMP0168 NEW) +endif() +if (POLICY CMP0167) + cmake_policy(SET CMP0167 NEW) +endif() -# set up linters/checkers -#set(CMAKE_CXX_CPPCHECK cppcheck;--std=c++17;--file-filter=*bitlib*) -#set(CMAKE_CXX_CPPLINT cpplint;--linelength=100;--filter=-whitespace;) -#set(CMAKE_CXX_CLANG_TIDY - #clang-tidy; - #-header-filter=include/;) +# set the project name +project(BitLib VERSION 1.0.0) +option(BITLIB_HWY "Build with google highway SIMD extensions" OFF) +option(BITLIB_BENCHMARK "Build bitlib benchmarks" OFF) +option(BITLIB_EXAMPLE "Build bitlib examples" OFF) +option(BITLIB_TEST "Build bitlib tests" OFF) +option(BITLIB_TEST_WERROR "Build bitlib tests with -Werror" OFF) +option(BITLIB_MDSPAN "Accessor Support for mdspan" ON) add_library(bitlib INTERFACE) add_library(bitlib::bitlib ALIAS bitlib) target_include_directories(bitlib INTERFACE - $ - $ + $ + $ ) +target_sources(bitlib INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bitlib.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit_concepts.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-algorithms/bit_algorithm_details.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-algorithms/bit_algorithm.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-algorithms/copy_backward.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-algorithms/copy.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-algorithms/count.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-algorithms/equal.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-algorithms/fill.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-algorithms/find.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-algorithms/libpopcnt.h + ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-algorithms/move.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-algorithms/reverse.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-algorithms/rotate.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-algorithms/shift.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-algorithms/swap_ranges.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-algorithms/transform.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-algorithms/type_traits.hpp -# specify global compiler flags -include_directories("include/" "utils/" ) + ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-containers/bit_array_dynamic_extent.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-containers/bit_array.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-containers/bit_bitsof.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-containers/bit-containers.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-containers/bit_mdspan_accessor.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-containers/bit_span.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-containers/bit_vector.hpp -# Add fmt library (useful for printing words in binary and other debugging stuff) -#include(FetchContent) -#FetchContent_Declare( - #fmt - #GIT_REPOSITORY https://github.com/fmtlib/fmt.git - #GIT_TAG e57ca2e3685b160617d3d95fcd9e789c4e06ca88 #v10.1.0 -#) -#FetchContent_MakeAvailable(fmt) + ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-iterator/bit_details.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-iterator/bit.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-iterator/bit_iterator.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-iterator/bit_reference.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-iterator/bit_value.hpp +) +target_compile_features(bitlib INTERFACE cxx_std_23) +include(CheckCXXSourceCompiles) -option(BITLIB_HWY "Build with google highway SIMD extensions" OFF) -option(BITLIB_BENCHMARK "Build bitlib benchmarks" OFF) -option(BITLIB_EXAMPLE "Build bitlib examples" OFF) -option(BITLIB_TEST "Build bitlib tests" OFF) -option(BITLIB_PROFILE "Buid simple example for profiling" OFF) -option(BITLIB_COVERAGE "Compute test coverage" OFF) +if (BITLIB_MDSPAN) + if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(USING_LIBSTDCXX 1) + endif() + if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + set(CODE " + #include + #ifdef __GLIBCXX__ + int main() { return 0; } + #else + #error Not using libstdc++ + #endif + ") -if (BITLIB_HWY) - add_definitions(-DBITLIB_HWY) -endif() + check_cxx_source_compiles("${CODE}" USING_LIBSTDCXX) + endif() + if (USING_LIBSTDCXX) + if (NOT TARGET std::mdspan) + find_package(mdspan CONFIG) + if (NOT mdspan_DIR OR "${mdspan_DIR}" STREQUAL "mdspan-NOTFOUND") + include(FetchContent) -if(BITLIB_BENCHMARK) - set(BENCHMARK_ENABLE_GTEST_TESTS OFF) - add_subdirectory(benchmark) + FetchContent_Declare( + mdspan + GIT_REPOSITORY https://github.com/kokkos/mdspan.git + GIT_TAG 414a5dccf5c775b2eecc13d408b256e94f23d1d2 # stable 2025/04/25 + ) + FetchContent_MakeAvailable(mdspan) + endif() + endif() + target_compile_definitions(bitlib INTERFACE MDSPAN_IMPL_STANDARD_NAMESPACE=std MDSPAN_IMPL_PROPOSED_NAMESPACE=experimental) + target_link_libraries(bitlib INTERFACE std::mdspan) + endif() endif() -if(BITLIB_EXAMPLE) - add_subdirectory(example) + +if (BITLIB_HWY) + target_compile_definitions(bitlib INTERFACE -DBITLIB_HWY) endif() -if(BITLIB_TEST) - enable_testing() - add_subdirectory(test) + +if (BITLIB_BENCHMARK) + enable_testing() + add_subdirectory(benchmark) endif() -if(BITLIB_PROFILE) - add_subdirectory(profile) +if (BITLIB_EXAMPLE) + add_subdirectory(example) endif() +if (BITLIB_TEST) + enable_testing() + add_subdirectory(test) +endif() diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 00000000..ca824f0a --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,869 @@ +{ + "version": 6, + "configurePresets": [ + { + "name": "base", + "hidden": true, + "binaryDir": "${sourceDir}/../build/${presetName}", + "cacheVariables": { + "CMAKE_EXPORT_COMPILE_COMMANDS": "YES" + } + }, + { + "name": "windows_base", + "hidden": true, + "inherits": "base", + "condition": { + "type": "anyOf", + "conditions": [ + { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + }, + { + "type": "notEquals", + "lhs": "$env{CMAKE_TOOLCHAIN_FILE}", + "rhs": "" + } + ] + } + }, + { + "name": "linux_base", + "hidden": true, + "inherits": "base", + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Linux" + } + }, + { + "name": "msvc_base", + "hidden": true, + "environment": { + "CC_FLAGS": "/bigobj /EHsc /GL", + "CC_FLAGS_DEBUG": "/Od /Zi /MDd /D_DEBUG /D_ITERATOR_DEBUG_LEVEL=2" + }, + "cacheVariables": { + "CMAKE_C_COMPILER": "cl", + "CMAKE_CXX_COMPILER": "cl", + "CMAKE_C_FLAGS": "$env{MSVC_FLAGS} $env{CC_FLAGS} $env{TARGET_FLAGS}", + "CMAKE_CXX_FLAGS": "$env{MSVC_FLAGS} $env{CC_FLAGS} $env{STDLIB_FLAGS} $env{TARGET_FLAGS}", + "CMAKE_EXE_LINKER_FLAGS": "$env{STDLIB_FLAGS}", + "CMAKE_C_FLAGS_DEBUG": "$env{MSVC_FLAGS_DEBUG} $env{CC_FLAGS_DEBUG} $env{TARGET_FLAGS_DEBUG}", + "CMAKE_CXX_FLAGS_DEBUG": "$env{MSVC_FLAGS_DEBUG} $env{CC_FLAGS_DEBUG} $env{TARGET_FLAGS_DEBUG}", + "CMAKE_C_FLAGS_RELEASE": "$env{MSVC_FLAGS_RELEASE} $env{CC_FLAGS_RELEASE} $env{TARGET_FLAGS_RELEASE}", + "CMAKE_CXX_FLAGS_RELEASE": "$env{MSVC_FLAGS_RELEASE} $env{CC_FLAGS_RELEASE} $env{TARGET_FLAGS_RELEASE}" + } + }, + { + "name": "tests_base", + "hidden": true, + "cacheVariables": { + "BITLIB_TEST": "ON" + } + }, + { + "name": "benchmark_base", + "hidden": true, + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "BITLIB_BENCHMARK": "ON" + } + }, + { + "name": "warnings_base", + "hidden": true, + "environment": { + "TARGET_FLAGS": "-Wall -Wextra -Wpedantic -Wshadow -Werror" + } + }, + { + "name": "gnu_base", + "hidden": true, + "environment": { + "GNU_FLAGS_DEBUG": "-DPOPCNT_NO_UNALIGNED -DBITLIB_DETECT_UNDEFINED_SHIFT -g3 -fno-omit-frame-pointer -fstack-protector-all -fsanitize=address -fsanitize=undefined -finstrument-functions -fno-inline", + "GNU_FLAGS_RELEASE": "-O3 -DNDEBUG -march=native" + }, + "cacheVariables": { + "CMAKE_C_FLAGS": "$env{GNU_FLAGS} $env{CC_FLAGS} $env{TARGET_FLAGS}", + "CMAKE_CXX_FLAGS": "$env{GNU_FLAGS} $env{CC_FLAGS} $env{STDLIB_FLAGS} $env{TARGET_FLAGS}", + "CMAKE_EXE_LINKER_FLAGS": "$env{STDLIB_FLAGS}", + "CMAKE_C_FLAGS_DEBUG": "$env{GNU_FLAGS_DEBUG} $env{CC_FLAGS_DEBUG} $env{TARGET_FLAGS_DEBUG}", + "CMAKE_CXX_FLAGS_DEBUG": "$env{GNU_FLAGS_DEBUG} $env{CC_FLAGS_DEBUG} $env{TARGET_FLAGS_DEBUG}", + "CMAKE_C_FLAGS_RELEASE": "$env{GNU_FLAGS_RELEASE} $env{CC_FLAGS_RELEASE} $env{TARGET_FLAGS_RELEASE}", + "CMAKE_CXX_FLAGS_RELEASE": "$env{GNU_FLAGS_RELEASE} $env{CC_FLAGS_RELEASE} $env{TARGET_FLAGS_RELEASE}" + } + }, + { + "name": "gcc_base", + "hidden": true, + "inherits": "gnu_base", + "cacheVariables": { + "CMAKE_C_COMPILER": "gcc", + "CMAKE_CXX_COMPILER": "g++" + }, + "environment": { + "CC_FLAGS_DEBUG": "-fno-inline-small-functions -fno-default-inline", + "STDLIB_FLAGS": "" + } + }, + { + "name": "libcxx_base", + "hidden": true, + "environment": { + "STDLIB_FLAGS": "-stdlib=libc++" + } + }, + { + "name": "libstdcxx_base", + "hidden": true, + "environment": { + "STDLIB_FLAGS": "-stdlib=libstdc++" + } + }, + { + "name": "clang_base", + "hidden": true, + "inherits": "gnu_base", + "cacheVariables": { + "CMAKE_C_COMPILER": "clang", + "CMAKE_CXX_COMPILER": "clang++" + } + }, + { + "name": "coverage_gcc_base", + "hidden": true, + "environment": { + "TARGET_FLAGS": "--coverage", + "TARGET_FLAGS_RELEASE": "-O2 -g" + } + }, + { + "name": "coverage_clang_base", + "hidden": true, + "environment": { + "TARGET_FLAGS": "-fprofile-instr-generate -fcoverage-mapping", + "TARGET_FLAGS_RELEASE": "-O2 -g" + } + }, + { + "name": "profile_gnu_base", + "hidden": true, + "environment": { + "TARGET_FLAGS_RELEASE": "-pg -fno-omit-frame-pointer -O2 -DNDEBUG -march=native -pg -ggdb" + } + }, + { + "name": "tests_windows_msvc_msvcstl", + "inherits": [ + "tests_base", + "windows_base", + "msvc_base" + ] + }, + { + "name": "benchmark_windows_msvc_msvcstl", + "inherits": [ + "benchmark_base", + "windows_base", + "msvc_base" + ] + }, + { + "name": "tests_windows_clangcl_msvcstl", + "inherits": [ + "tests_base", + "windows_base", + "msvc_base" + ], + "cacheVariables": { + "CMAKE_CXX_COMPILER": "clang-cl", + "CMAKE_C_COMPILER": "clang-cl" + } + }, + { + "name": "benchmark_windows_clangcl_msvcstl", + "inherits": [ + "benchmark_base", + "windows_base", + "msvc_base" + ], + "cacheVariables": { + "CMAKE_CXX_COMPILER": "clang-cl", + "CMAKE_C_COMPILER": "clang-cl" + } + }, + { + "name": "tests_linux_gcc_libstdcxx", + "inherits": [ + "tests_base", + "linux_base", + "gcc_base" + ] + }, + { + "name": "tests_linux_clang_libcxx", + "inherits": [ + "tests_base", + "linux_base", + "clang_base", + "libcxx_base" + ] + }, + { + "name": "tests_linux_clang_libstdcxx", + "inherits": [ + "tests_base", + "linux_base", + "clang_base", + "libstdcxx_base" + ] + }, + { + "name": "coverage_linux_gcc_libstdcxx", + "inherits": [ + "tests_base", + "linux_base", + "gcc_base", + "coverage_gcc_base" + ] + }, + { + "name": "coverage_linux_clang_libcxx", + "inherits": [ + "tests_base", + "linux_base", + "clang_base", + "coverage_clang_base", + "libcxx_base" + ] + }, + { + "name": "coverage_linux_clang_libstdcxx", + "inherits": [ + "tests_base", + "linux_base", + "clang_base", + "coverage_clang_base", + "libstdcxx_base" + ] + }, + { + "name": "benchmark_linux_gcc_libstdcxx", + "inherits": [ + "benchmark_base", + "linux_base", + "gcc_base" + ] + }, + { + "name": "benchmark_linux_clang_libcxx", + "inherits": [ + "benchmark_base", + "linux_base", + "clang_base", + "libcxx_base" + ] + }, + { + "name": "benchmark_linux_clang_libstdcxx", + "inherits": [ + "benchmark_base", + "linux_base", + "clang_base", + "libstdcxx_base" + ] + }, + { + "name": "warnings_linux_gcc_libstdcxx", + "inherits": [ + "tests_linux_gcc_libstdcxx", + "warnings_base" + ] + }, + { + "name": "warnings_linux_clang_libcxx", + "inherits": [ + "tests_linux_clang_libcxx", + "warnings_base" + ] + } + ], + "buildPresets": [ + { + "name": "tests_windows_msvc_msvcstl", + "configurePreset": "tests_windows_msvc_msvcstl", + "targets": "bitlib-tests", + "configuration": "Debug", + "jobs": 1, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + } + }, + { + "name": "benchmark_windows_msvc_msvcstl", + "configurePreset": "benchmark_windows_msvc_msvcstl", + "targets": "bitlib-bench", + "configuration": "Release", + "jobs": 1, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + } + }, + { + "name": "tests_windows_clangcl_msvcstl", + "configurePreset": "tests_windows_clangcl_msvcstl", + "targets": "bitlib-tests", + "configuration": "Debug", + "jobs": 0, + "condition": { + "type": "anyOf", + "conditions": [ + { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + }, + { + "type": "notEquals", + "lhs": "$env{CMAKE_TOOLCHAIN_FILE}", + "rhs": "" + } + ] + } + }, + { + "name": "benchmark_windows_clangcl_msvcstl", + "configurePreset": "benchmark_windows_clangcl_msvcstl", + "targets": "bitlib-bench", + "configuration": "Release", + "jobs": 0, + "condition": { + "type": "anyOf", + "conditions": [ + { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + }, + { + "type": "notEquals", + "lhs": "$env{CMAKE_TOOLCHAIN_FILE}", + "rhs": "" + } + ] + } + }, + { + "name": "tests_linux_gcc_libstdcxx", + "configurePreset": "tests_linux_gcc_libstdcxx", + "targets": "bitlib-tests", + "jobs": 0, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Linux" + } + }, + { + "name": "warnings_linux_gcc_libstdcxx", + "configurePreset": "warnings_linux_gcc_libstdcxx", + "targets": "bitlib-tests", + "jobs": 0, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Linux" + } + }, + { + "name": "warnings_linux_clang_libcxx", + "configurePreset": "warnings_linux_clang_libcxx", + "targets": "bitlib-tests", + "jobs": 0, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Linux" + } + }, + { + "name": "coverage_linux_gcc_libstdcxx", + "configurePreset": "coverage_linux_gcc_libstdcxx", + "targets": "bitlib-tests", + "jobs": 0, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Linux" + } + }, + { + "name": "benchmark_linux_gcc_libstdcxx", + "configurePreset": "benchmark_linux_gcc_libstdcxx", + "targets": "bitlib-bench", + "jobs": 0, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Linux" + } + }, + { + "name": "tests_linux_clang_libcxx", + "configurePreset": "tests_linux_clang_libcxx", + "targets": "bitlib-tests", + "jobs": 0, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Linux" + } + }, + { + "name": "tests_linux_clang_libstdcxx", + "configurePreset": "tests_linux_clang_libstdcxx", + "targets": "bitlib-tests", + "jobs": 0, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Linux" + } + }, + { + "name": "coverage_linux_clang_libcxx", + "configurePreset": "coverage_linux_clang_libcxx", + "targets": "bitlib-tests", + "jobs": 0, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Linux" + } + }, + { + "name": "coverage_linux_clang_libstdcxx", + "configurePreset": "coverage_linux_clang_libstdcxx", + "targets": "bitlib-tests", + "jobs": 0, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Linux" + } + }, + { + "name": "benchmark_linux_clang_libcxx", + "configurePreset": "benchmark_linux_clang_libcxx", + "targets": "bitlib-bench", + "jobs": 0, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Linux" + } + } + ], + "testPresets": [ + { + "name": "tests_windows_msvc_msvcstl", + "configurePreset": "tests_windows_msvc_msvcstl", + "configuration": "Debug", + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + } + }, + { + "name": "benchmark_windows_msvc_msvcstl", + "configurePreset": "benchmark_windows_msvc_msvcstl", + "configuration": "Release", + "execution": { + "jobs": 1 + }, + "output": { + "verbosity": "verbose" + }, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + } + }, + { + "name": "tests_windows_clangcl_msvcstl", + "configurePreset": "tests_windows_clangcl_msvcstl", + "configuration": "Debug", + "condition": { + "type": "anyOf", + "conditions": [ + { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + }, + { + "type": "notEquals", + "lhs": "$env{CMAKE_TOOLCHAIN_FILE}", + "rhs": "" + } + ] + } + }, + { + "name": "benchmark_windows_clangcl_msvcstl", + "configurePreset": "benchmark_windows_clangcl_msvcstl", + "configuration": "Release", + "execution": { + "jobs": 1 + }, + "output": { + "verbosity": "verbose" + }, + "condition": { + "type": "anyOf", + "conditions": [ + { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + }, + { + "type": "notEquals", + "lhs": "$env{CMAKE_TOOLCHAIN_FILE}", + "rhs": "" + } + ] + } + }, + { + "name": "tests_linux_gcc_libstdcxx", + "configurePreset": "tests_linux_gcc_libstdcxx", + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Linux" + } + }, + { + "name": "coverage_linux_gcc_libstdcxx", + "configurePreset": "coverage_linux_gcc_libstdcxx", + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Linux" + } + }, + { + "name": "benchmark_linux_gcc_libstdcxx", + "configurePreset": "benchmark_linux_gcc_libstdcxx", + "execution": { + "jobs": 1 + }, + "output": { + "verbosity": "verbose" + }, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Linux" + } + }, + { + "name": "tests_linux_clang_libcxx", + "configurePreset": "tests_linux_clang_libcxx", + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Linux" + } + }, + { + "name": "tests_linux_clang_libstdcxx", + "configurePreset": "tests_linux_clang_libstdcxx", + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Linux" + } + }, + { + "name": "coverage_linux_clang_libcxx", + "configurePreset": "coverage_linux_clang_libcxx", + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Linux" + } + }, + { + "name": "coverage_linux_clang_libstdcxx", + "configurePreset": "coverage_linux_clang_libstdcxx", + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Linux" + } + }, + { + "name": "benchmark_linux_clang_libcxx", + "configurePreset": "benchmark_linux_clang_libcxx", + "execution": { + "jobs": 1 + }, + "output": { + "verbosity": "verbose" + }, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Linux" + } + }, + { + "name": "warnings_linux_gcc_libstdcxx", + "configurePreset": "warnings_linux_gcc_libstdcxx", + "execution": { + "jobs": 1 + }, + "output": { + "verbosity": "verbose" + }, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Linux" + } + }, + { + "name": "warnings_linux_clang_libcxx", + "configurePreset": "warnings_linux_clang_libcxx", + "execution": { + "jobs": 1 + }, + "output": { + "verbosity": "verbose" + }, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Linux" + } + } + ], + "workflowPresets": [ + { + "name": "tests_windows_msvc_msvcstl", + "steps": [ + { + "type": "configure", + "name": "tests_windows_msvc_msvcstl" + }, + { + "type": "build", + "name": "tests_windows_msvc_msvcstl" + }, + { + "type": "test", + "name": "tests_windows_msvc_msvcstl" + } + ] + }, + { + "name": "benchmark_windows_msvc_msvcstl", + "steps": [ + { + "type": "configure", + "name": "benchmark_windows_msvc_msvcstl" + }, + { + "type": "build", + "name": "benchmark_windows_msvc_msvcstl" + }, + { + "type": "test", + "name": "benchmark_windows_msvc_msvcstl" + } + ] + }, + { + "name": "tests_windows_clangcl_msvcstl", + "steps": [ + { + "type": "configure", + "name": "tests_windows_clangcl_msvcstl" + }, + { + "type": "build", + "name": "tests_windows_clangcl_msvcstl" + }, + { + "type": "test", + "name": "tests_windows_clangcl_msvcstl" + } + ] + }, + { + "name": "benchmark_windows_clangcl_msvcstl", + "steps": [ + { + "type": "configure", + "name": "benchmark_windows_clangcl_msvcstl" + }, + { + "type": "build", + "name": "benchmark_windows_clangcl_msvcstl" + }, + { + "type": "test", + "name": "benchmark_windows_clangcl_msvcstl" + } + ] + }, + { + "name": "tests_linux_gcc_libstdcxx", + "steps": [ + { + "type": "configure", + "name": "tests_linux_gcc_libstdcxx" + }, + { + "type": "build", + "name": "tests_linux_gcc_libstdcxx" + }, + { + "type": "test", + "name": "tests_linux_gcc_libstdcxx" + } + ] + }, + { + "name": "coverage_linux_gcc_libstdcxx", + "steps": [ + { + "type": "configure", + "name": "coverage_linux_gcc_libstdcxx" + }, + { + "type": "build", + "name": "coverage_linux_gcc_libstdcxx" + }, + { + "type": "test", + "name": "coverage_linux_gcc_libstdcxx" + } + ] + }, + { + "name": "benchmark_linux_gcc_libstdcxx", + "steps": [ + { + "type": "configure", + "name": "benchmark_linux_gcc_libstdcxx" + }, + { + "type": "build", + "name": "benchmark_linux_gcc_libstdcxx" + }, + { + "type": "test", + "name": "benchmark_linux_gcc_libstdcxx" + } + ] + }, + { + "name": "tests_linux_clang_libcxx", + "steps": [ + { + "type": "configure", + "name": "tests_linux_clang_libcxx" + }, + { + "type": "build", + "name": "tests_linux_clang_libcxx" + }, + { + "type": "test", + "name": "tests_linux_clang_libcxx" + } + ] + }, + { + "name": "coverage_linux_clang_libcxx", + "steps": [ + { + "type": "configure", + "name": "coverage_linux_clang_libcxx" + }, + { + "type": "build", + "name": "coverage_linux_clang_libcxx" + }, + { + "type": "test", + "name": "coverage_linux_clang_libcxx" + } + ] + }, + { + "name": "benchmark_linux_clang_libcxx", + "steps": [ + { + "type": "configure", + "name": "benchmark_linux_clang_libcxx" + }, + { + "type": "build", + "name": "benchmark_linux_clang_libcxx" + }, + { + "type": "test", + "name": "benchmark_linux_clang_libcxx" + } + ] + }, + { + "name": "warnings_linux_gcc_libstdcxx", + "steps": [ + { + "type": "configure", + "name": "warnings_linux_gcc_libstdcxx" + }, + { + "type": "build", + "name": "warnings_linux_gcc_libstdcxx" + }, + { + "type": "test", + "name": "warnings_linux_gcc_libstdcxx" + } + ] + }, + { + "name": "warnings_linux_clang_libcxx", + "steps": [ + { + "type": "configure", + "name": "warnings_linux_clang_libcxx" + }, + { + "type": "build", + "name": "warnings_linux_clang_libcxx" + }, + { + "type": "test", + "name": "warnings_linux_clang_libcxx" + } + ] + } + ] +} diff --git a/LICENSE b/LICENSE index e66d784a..8952a658 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,7 @@ -BSD 3-Clause License +BitLib BSD 3-Clause License Copyright (c) 2022, Bryce Kille +Copyright (c) 2025, Peter McLean All rights reserved. Redistribution and use in source and binary forms, with or without @@ -27,3 +28,34 @@ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +--------------------------THIRD-PARTY LICENSES------------------------------- + +libpopcnt.h - C/C++ library for counting the number of 1 bits (bit +population count) in an array as quickly as possible using +specialized CPU instructions i.e. POPCNT, AVX2, AVX512, NEON. + +Copyright (c) 2016 - 2020, Kim Walisch +Copyright (c) 2016 - 2018, Wojciech Muła +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md index 7087e59e..5a69101a 100644 --- a/README.md +++ b/README.md @@ -1,49 +1,118 @@ # BitLib -![Actions](https://github.com/bkille/BitLib/actions/workflows/cmake.yml/badge.svg?branch=master)[![Coverage Status](https://coveralls.io/repos/github/bkille/BitLib/badge.svg?branch=master)](https://coveralls.io/github/bkille/BitLib?branch=master) +![Actions](https://github.com/PeterCDMcLean/BitLib/actions/workflows/cmake-multi-platform.yml/badge.svg?branch=master)[![Coverage Status](https://coveralls.io/repos/github/PeterCDMcLean/BitLib/badge.svg?branch=master)](https://coveralls.io/github/PeterCDMcLean/BitLib?branch=master) -**This repository acts as an efficient replacement of `std::vector`. It provides implementations of many of the functions in [``](https://en.cppreference.com/w/cpp/algorithm) optimized for containers of bits, in addition to providing a `bit_vector` class which has roughly the same interface as `std::vector`**. +# Overview -This project is built on "[bit iterators](https://github.com/vreverdy/bit)" developed by Vincent Reverdy and many of the implementations in `include/bit-algorithms` come from some of my previous work with Vincent [here](https://github.com/vreverdy/bit-algorithms). +This project provides convenient and efficient stl-like containers and algorithms for bit access. -# Example -The code below is from `example/src/example1.cpp`. While the type of word that the bitvector is built off of is templated and you can use any unsigned type, it is likely that you'll want to use `uint64_t` or another 64 bit unsigned type, as that will leverage the most bit-parallelism. -```cpp -#include -#include "bitlib/bitlib.hpp" - -int main() { - bit::bit_vector bv1 ("011111010010"); - std::cout << "Original bitvec: " << bv1.debug_string() << std::endl; - // Original bitvec: 01111101 0010 +# Example - // Same behavior as std::reverse - bit::reverse(bv1.begin(), bv1.end()); - std::cout << "Reversed bitvec: " << bv1.debug_string() << std::endl; - // Reversed bitvec: 01001011 1110 +```c++ +#include "bitlib/bitlib.hpp" - // Same behavior as std::rotate - bit::rotate(bv1.begin(), bv1.begin() + 3, bv1.end()); - std::cout << "Rotated bitvec: " << bv1.debug_string() << std::endl; - // Rotated bitvec: 01011111 0010 +auto fp_half_num = 0x10'3DAE_b; +auto exponent = fp_half_num(10, 15); +auto mantissa = fp_half_num(0, 10); +auto twos_complement_exponent = static_cast(exponent) - ((1 << 5)-1); + +struct fp_dynamic : bit::bit_array<> { + const size_t mantissa_bits; + fp_dynamic() = delete; + fp_dynamic( + size_t exponent_bits, + size_t mantissa_bits) : + bit::bit_array<>(1+exponent_bits+mantissa_bits), + mantissa_bits(mantissa_bits) {} + bit::bit_reference<> sign() { + return (*this).back(); + } + auto exponent() { + return (*this)(mantissa_bits, this->size()-1); + } + auto mantissa() { + return (*this)(0, mantissa_bits); + } +}; + +int main(int argn, char* argv[]) { + fp_dynamic fp(6u, 7u); + fp.sign() = bit::bit1; + fp.exponent() = 6'5_b; + fp.mantissa() = 0x7'3F_b; + std::cout << "fp: " << fp << "\n"; +} - // Same behavior as the corresponding std::vector::push_back and std::vector::insert - bv1.push_back(bit::bit0); - bv1.insert(bv1.end(), 10, bit::bit1); - std::cout << "Extended bitvec: " << bv1.debug_string() << std::endl; - // Extended bitvec: 01011111 00100111 1111111 +``` - return 0; -} +# Table of Contents + +- [Overview](#overview) +- [Example](#example) +- [Table of Contents](#table-of-contents) +- [Requirements](#requirements) +- [CMake](#cmake) +- [Sized Literal](#sized-literal) +- [Bit Endian](#endian) +- [Slice Operator](#slice-operator) +- [Policy](#policy) +- [Owning Containers](#owning-containers) + - [bit_array](#bit_array) + - [bit_vector](#bit_vector) +- [Non-owning Views](#non-owning-views) + - [bit_array_ref](#bit_array_ref) + - [bit_span](#bit_span) + - [mdspan with bit_default_accessor](#mdspan-with-bit-accessors) +- [Iterators and References](#iterators-and-references) + - [bit_iterator](#bit_iterator) + - [bit_reference](#bit_reference) + - [bit_word_pointer_adapter](#bit_word_pointer_adapter) + - [bit_word_reference_adapter](#bit_word_reference_adapter) +- [Algorithms](#algorithms) +- [Testing](#testing) +- [Future Work](#future-work) +- [Contributors](#contributors) +- [License](#license) + +# Requirements + +- _gcc_ or _clang_ +- `C++23` +- cmake verison 3.20. + * cmake version 3.31 if compiling bitlib's benchmark suite + +# CMake +### options +bitlib provides the following cmake options and their defaults +```cmake +option(BITLIB_HWY "Build with google highway SIMD extensions" OFF) +option(BITLIB_BENCHMARK "Build bitlib benchmarks" OFF) +option(BITLIB_EXAMPLE "Build bitlib examples" OFF) +option(BITLIB_TEST "Build bitlib tests" OFF) +option(BITLIB_TEST_WERROR "Build bitlib tests with -Werror" OFF) +option(BITLIB_MDSPAN "Accessor Support for mdspan" ON) ``` -# Installation -BitLib is a header-only libarary. Currently, the BitLib library requires at least `-std=c++17`. +Options can be set through command line switches: +```bash +cmake -G .. -DBITLIB_MDSPAN=OFF +``` +or within cmake files before the bitlib targets have been added +```cmake +set(BITLIB_MDSPAN OFF CACHE BOOL "Disable mdspan" FORCE) +``` +The bitlib targets are added through one of two ways: -## CMake -You can automatically fetch the library using Cmake's `FetchContent`. +### add_subdirectory +```cmake +add_subdirectory(..../bitlib) +add_executable(example) +target_sources(example PRIVATE example.cpp) +target_link_libraries(example PRIVATE bitlib::bitlib) +``` +### FetchContent ```cmake include(FetchContent) FetchContent_Declare( @@ -53,41 +122,251 @@ FetchContent_Declare( ) FetchContent_MakeAvailable(bitlib) -add_executable(example example.cpp) -target_link_libraries(example bitlib::bitlib) +add_executable(example) +target_sources(example PRIVATE example.cpp) +target_link_libraries(example PRIVATE bitlib::bitlib) ``` -## Manual include -Alternatively, you can copy the `include/bitlib` directory to somewhere in your include path. +# Sized Literal -## SIMD support, testing and benchmarking +This provides a sized literal for compile-time bit_array. -SIMD support (enabled via Google's [highway](https://github.com/google/highway) library) can be enabled by defining `BITLIB_HWY`. For example, with `cmake`, you can run `cmake -DBITLIB_HWY=1`. Other options can be found in the `CMakeLists.txt` file: +There are four components of the literal: +``` + [base_prefix][size']data_b + | | | | + |---> | <--| |--- '_b' suffix +ex: 0xC'7B_b; +``` + - base prefix + - size + * before first apostrophe `'` + - data + - `_b` suffix -```cmake -option(BITLIB_HWY "Build with google highway SIMD extensions" OFF) -option(BITLIB_BENCHMARK "Build bitlib benchmarks" OFF) -option(BITLIB_EXAMPLE "Build bitlib examples" OFF) -option(BITLIB_TEST "Build bitlib tests" OFF) -option(BITLIB_PROFILE "Buid simple example for profiling" OFF) -option(BITLIB_COVERAGE "Compute test coverage" OFF) +### Base +Similar to standard C++ literals, the base is optionally prefixed: + +- default base 10 +- `0b` binary base +- `0x` hexadecimal base +- `0` octal base + +### Size +This user-defined literal repurposes the apostrophe `'` numeric separator in the C++ standard. +The digits of the numeric leading up to the first apostrophe are the number of bits in the literal. +All digits that follow the first apostrophe are digits in the actual numeric. +Any apostophes after the first are considered standard numeric separators. + +> [!IMPORTANT] +> The size is expressed in the same base as the rest of the literal + +### Data + +Numeric literal in the base described + +> [!CAUTION] +> This literal does not support negative literals + +> [!CAUTION] +> This literal does not support literals above 64 bits + +### Suffix + +The literal must be followed by the user defined literal suffix: _b + +### Examples + +```c++ +#include "bitlib/bit.hpp" + +auto dec_lit = 31'123456_b; // 31 bit base 10 literal +auto hex_lit = 0x1F'1E240_b; // 31 bit hex literal +auto bin_lit = 0b11111'11110001001000000_b; //31 bit binary literal +auto oct_lit = 037'361100_b; // 31 bit octal literal ``` -# Usage -The goal of BitLib is to be as similar to the C++ STL as possible. The interface of most functions and classes are the same as they are in the STL. Instead of the values being `bool`, we have `bit::bit_value`, which can take on either `bit::bit0` or `bit::bit1`. +# Bit Endian -## Containers - Right now, the only container I have implemented is the bitvector. `bit::bit_vector` is essentially a wrapper around `std::vector`. The interfaces are nearly identical. In addition to the normal `vector` constructors, you can also provide a string to construct your bitvector: -```cpp -using WordType = uint64_t; -bit::bit_vector bvec1 ("011111010010"); +'Bit Endian' is the format of a sequence of bits: + - 'Big Bit Endian' (BBE) the left-most bit is the most-significant-bit and right-most bit is the least-significant-bit + - 'Little Bit Endian' (LBE) the left-most bit is the least-significant-bit and right-most is the most-significant-bit + +Typical bit integrals are represented as 'Bit Bit Endian' +```c++ + int bits = 0b10101000; + // MSB --| |-- LSB ``` -While the type of word that the bitvector is built off of is templated and you can use any unsigned type, it is likely that you'll want to use `uint64_t` or another 64 bit unsigned type, as that will leverage the most bit-parallelism. +Several parts of the API that take or output sequences of bits may be sensitive to bit endian +or use the unintuitive little bit endian. + - initializer list of `bit_value` is in little bit endian. + ex: `{bit1, bit0, bit1, bit1}` == `0b1101`; + - `to_string` can be templated with a programmable endian. By default it is big bit endian + - `shift_left` and `shift_right` mimic std::shift and work in _little bit endian_. +```c++ + auto bits = 0b1000'10101000_b; + bit::shift_left(bits.begin(), bits.end(), 3); // a left shift in _little bit endian_ + bits == 0b1000'00010101_b; // is actually a right shift in big bit endian +``` + +# Slice Operator + +All containers and views provide a slice operator which take a half-open range and +return a [mutable view](#bit_array_ref) of the given range. +Example: -## Algorithms -The algorithms again work in the same manner as the STL. The functions provided here have the same interface as those in the STL, however under the hood, they take advantage of bit-parallelism. It should be noted that if there is an STL algorithm that is not supported yet by BitLib, you can still use the STL implementation. For example: -```cpp +```c++ +auto lit = 0x1F'1E240_b; +auto ref = lit(4, 8); // reference the bits from [4,8) i.e. 4,5,6,7. +assert(ref == 0x4'4_b); +assert(ref == 0x4); +lit(4, 8) = 0xA; +assert(lit == 0x1F'1E2A0_b); +assert(ref(1,4) == 0x5); // array_ref can be sliced further +``` + +# Policy + +Template class controlling behavior such as expansion (aka sign-extension) and truncation +The containers can be specialized with a custom policy type to +throwing an exception on loss of data or clamping instead of truncation + +The default policy truncates when necessary and sign extends for conversion to/from signed integrals + +# Owning Containers + + - `bit::bit_array` is an alias for `bit::array` + - `bit::array` is a fixed-size and non-rebindable container with similar API to `std::array` + - `bit::bit_vector` is a resizable container with similar API to `std::vector` + +## bit_array + +Provides compile-time or construction time container for an array of bits. + +### Compile-time: +Storage is on the stack. The number of bytes is the nearest power of two integral size. +```c++ +bit::bit_array<11> vec_11(0x123); // Will use a uint16_t to hold the data +bit::bit_array<65> vec_65(); // Will use a uint64_t to hold the data +``` +The storage word type can be specified: +```c++ +bit::bit_array<65, uint8_t> vec_65_bytes(); // 9 bytes on stack +``` + +### Construction-time +A non-resizable construction-time storage is used when the N (aka Extent) +is equal to std::dynamic_extent (similar to std::span). +Since the N template parameter is by default std::dynamic_extent this is the +default template specialization `bit_array<>`. + +```c++ +bit::bit_array<> vec_11(11, 0x123); // 8 bytes on stack for data, 8 bytes for size +bit::bit_array<> vec_64(65); // same stack size as above + 16 bytes in heap +``` +The storage word size is by default uintptr_t. +The container will perform small buffer optimization +when the number of bits is equal or less than `bitsof()` typically 64. + +## bit_vector + +bit_vector provides similar API to `std::vector`. It owns runtime reallocatable storage. +The first template parameter is the integral datatype of the underlying storage. + +```c++ +bit::bit_vector vec(199, bit::bit1); +vec.push_back(bit0); +vec.insert(98, bit0); +vec(0, 7) = 7'7_b; // supports slice operator +``` + +> [!IMPORTANT] +> bit_vector does not support construction from integral or implicit cast to integral +> bit_vector is also lacking bit operators such as ~, <<, >>, &, | ... + +# Non-Owning Views +## bit_array_ref +## bit_span +## mdspan with bit accessors + +> [!TIP] +> When building bitlib unit tests/benchmark, +> cmake mdspan support can be enabled/disabled with cmake variable `ENABLE_MDSPAN` + +The `std::mdspan` container (C++23) can be used with a custom accessors that +use proxy pointers and references. This makes it suitable for accessing multi-dimensional +bit dense data. + +There are three flavours of accessors: + - `bit_default_accessor` which povides individual `bit_value` access which behaves like a typical stl mdspan + - `bit_word_accessor` which provides compile-time bit array value type access + - `bit_word_accessor` which provides construction-time bit array value type access +> [!INFO] +> The dynamic_extent `bit_word_accessor` requires a non-default constructor to the accessor. +> The mdspan must use the mdspan constructor which takes the +> container pointer, extent instance and accessor instance. + +bit_value: +```c++ +bit::bit_array<7*8*9> bits(); +std::mdspan< + bit::bit_value, + std::extents, + std::layout_right, + bit::bit_default_accessor +> myspan ( + &bits[0] +); +myspan[6, 7, 8] = bit::bit1; // set last bit to one +assert(bits[7*8*9-1] == bit::bit1); +``` + +Compile-time: +```c++ +bit_array<7*8*9> bits(); +std::mdspan< + bit::bit_word_accessor<7>::element_type, + std::extents, + std::layout_right, + bit::bit_word_accessor<7> +> myspan ( + &bits[0] +); +myspan[7, 8] = 0x7'7F_b; // set last 7 bit word to all ones. +assert(bits(7*8*9-7, 7*8*9) == 0x7F); +``` + +Construction-time: +```c++ +bit::bit_array<> bits(7*8*9); +std::mdspan< + bit::bit_word_accessor<>::element_type, + std::dextents, + std::layout_right, + bit::bit_word_accessor<> +> myspan ( + &bits[0], + std::dextents{8, 9}, + bit::bit_word_accessor<>(7) +); +myspan[7, 8] = 0x7'7F_b; // set last 7 bit word to all ones. +assert(bits(7*8*9-7, 7*8*9) == 0x7F); +``` + +# Iterators and References +## bit_iterator +## bit_reference +## bit_word_pointer_adapter +## bit_word_reference_adapter + +# Algorithms +The algorithms again work in the same manner as the STL. +The functions provided here have the same interface as those in the STL. +However, they take advantage of bit-parallelism. +It should be noted that if there is an STL algorithm that is not supported yet by BitLib, +the STL implementation should work. For example: +```c++ using WordType = uint64_t; bit::bit_vector bvec1 ("011111010010"); bit::bit_vector bvec2 = bvec1; @@ -95,8 +374,10 @@ bit::equal(bvec1.begin(), bvec1.end(), bvec2.begin(), bvec1.end()); std::equal(bvec1.begin(), bvec1.end(), bvec2.begin(), bvec1.end()); // Also works, but much slower as it works bit-by-bit ``` -For algorithms which take a function (i.e. `bit::transform`), the function should have `WordType` as the input types as well as the return type. For example, to compute the intersection of two bitvectors: -```cpp +For algorithms which take a function (i.e. `bit::transform`), +the function should have `WordType` as the input types as well as the return type. +For example, to compute the intersection of two bitvectors: +```c++ using WordType = uint64_t; auto binary_op = std::bit_and(); @@ -106,152 +387,66 @@ auto bitret = bit::transform( bitvec1.end(), bitvec2.begin(), bitvec3.begin() - binary_op); + binary_op); ``` + #### accumulate +Order sensitive lambda reduction operation. + #### copy_backward + #### copy -## Iterators -The bit-iterators are the foundation of the library. In most cases, users will only need to work w/ the `bit::bit_vector::begin()` and `bit::bit_vector::end()` methods to obtain iterators. However, constructing a bit iterator from any address is also straightforward: -```cpp -using WordType = uint64_t; -std::array wordArr = {1,2,3,4}; -bit::bit_iterator(&(wordArr[0])); // Constructs a bit iterator starting from the first bit from the first word of the vector -bit::bit_iterator(&(wordArr[0]), 1); // Constructs a bit iterator from the second bit (position 1) of the first word of the vector -``` + #### count +Count bit1 or bit0 -In order to grab the underlying word that a bit pointed to by a bit_iterator comes from, you can use the `bit_iterator.base()` function. + #### equal +Compare two bit sequences -It is worth noting that the "position" of a bit always increases from LSB to MSB. For those looking to create their own algorithms from bit_iterators, this can be a common "gotcha". For example, shifting a word to the right by `k` will eliminate the first `k` bits of the container. This is only important to those implementing their own algorithms. `bit::shift_*` works as described in the documentation i.e. `shift_right` shifts the container towards `end()` and `shift_left` shifts the container towards `begin()`. + #### fill +Fill with bit1 or bit0 -``` - MSB|<-----|LSB -Position: 76543210 -Value: 01010001 --> Sequence: 10001010 + #### find +Get the position of the first bit1 or bit0 + #### move +Alias of copy -// bit::shift_right by 2 - MSB|<-----|LSB -Position: 76543210 -Value: 01000100 --> Sequence: 00100010 -``` + #### reverse +Reverse the bit sequence in-place -# Documentation -Given that the majority of the library is focused on having the same interface as the C++ STL iterators, containers, and algorithms, users should use the official [STL documentation website](https://en.cppreference.com/). We do plan on adding our own documentation in the future, however. + #### rotate +Rotate the bit sequence in-place + #### shift -# Performance Benchmarks -I used Google's [benchmark](https://github.com/google/benchmark) library for computing benchmarks. Each benchmark is formatted as `{bit, BitArray, std}::function` (size) [(alignment-tags)]. + #### swap_ranges - * `bit` is for this library, `BitArray` is for the popular C-based [BitArray library](https://github.com/noporpoise/BitArray), [dynamic_bitset](https://github.com/pinam45/dynamic_bitset) is a header-only library similar to Boost's dynamic_bitset, and`std` is the standard library operating on the infamous `vector`. -* (size) denotes the size of the container in bits. `small = 1 << 8`, `medium= 1 << 16`, `large = 1 << 24`, `huge = 1 << 31` -* (alignment-tags) refers to the memory alignment of the bit-iterators. `U` means the iterator does not fall on a word boundary, `R` means the iterator is placed at random, and `A` means the iterator is aligned with a word boundary. + #### to_from_string +Convert to string or from string -For example, `bit::rotate (large) (ARA)` refers to our library's implementation of the `rotate` algorithm operating on a container of 65536 bits, where `first` and `last` are aligned but `n_first` is selected at random. + #### transform +Word-by-word lambda operation on single or double operand -``` ---------------------------------------------------------------------------------------- -Benchmark Time CPU Iterations ---------------------------------------------------------------------------------------- -bit::set (large) 1.90 ns 1.90 ns 367974893 -dynamic_bitset::set (large) 2.37 ns 2.37 ns 296837879 -bitarray::set (large) 2.19 ns 2.19 ns 319133940 -std::set (large) 2.39 ns 2.39 ns 293135332 -bit::shift_left (small) 26.8 ns 26.8 ns 25929070 -bit::shift_left (small) (UU) 22.4 ns 22.4 ns 31233265 -dynamic_bitset::shift_left (small) 13.1 ns 13.1 ns 53627207 -bitarray::shift_left (small) 38.2 ns 38.2 ns 18339126 -std::shift_left (small) 345 ns 345 ns 2029283 -bit::shift_left (large) 371224 ns 371211 ns 1886 -bit::shift_left (large) (UU) 371536 ns 371530 ns 1880 -dynamic_bitset::shift_left (large) 638896 ns 638880 ns 1097 -bitarray::shift_left (large) 3156273 ns 3156003 ns 222 -std::shift_left (large) 105227752 ns 105223527 ns 7 -bit::shift_right (small) 26.9 ns 26.9 ns 25976563 -bit::shift_right (small) (UU) 39.3 ns 39.3 ns 17962533 -dynamic_bitset::shift_right (small) 12.2 ns 12.2 ns 57419526 -bitarray::shift_right (small) 38.1 ns 38.1 ns 18325350 -std::shift_right (small) 504 ns 504 ns 1386280 -bit::shift_right (large) 413297 ns 413269 ns 1693 -bit::shift_right (large) (UU) 413692 ns 413655 ns 1682 -dynamic_bitset::shift_right (large) 557287 ns 557305 ns 1257 -bitarray::shift_right (large) 3156463 ns 3156516 ns 222 -std::shift_right (large) 210100788 ns 210083631 ns 3 -bit::reverse (small) (UU) 43.4 ns 43.4 ns 16112098 -bitarray::reverse (small) (UU) 95.1 ns 95.1 ns 7387177 -std::reverse (small) 419 ns 419 ns 1677069 -bit::reverse (large) 1245260 ns 1245160 ns 563 -bit::reverse (large) (UU) 1800771 ns 1800680 ns 389 -bitarray::reverse (large) 16899481 ns 16898587 ns 41 -bitarray::reverse (large) (UU) 22719408 ns 22720393 ns 31 -std::reverse (large) 293563397 ns 293542850 ns 2 -bit::transform(UnaryOp) (small) 8.75 ns 8.75 ns 80079214 -bit::transform(UnaryOp) (small) (UU) 16.6 ns 16.6 ns 42254961 -dynamic_bitset::transform(UnaryOp) (small) 4.00 ns 4.00 ns 169219246 -bitarray::transform(UnaryOp) (small) 8.39 ns 8.39 ns 83877004 -std::transform(UnaryOp) (small) 763 ns 763 ns 917975 -bit::transform(UnaryOp) (large) 373982 ns 373950 ns 1853 -bit::transform(UnaryOp) (large) (UU) 2059234 ns 2059268 ns 339 -dynamic_bitset::transform(UnaryOp) (large) 379368 ns 379368 ns 1805 -bitarray::transform(UnaryOp) (large) 739552 ns 739544 ns 881 -std::transform(UnaryOp) (large) 197977698 ns 197969224 ns 4 -bit::transform(BinaryOp) (small) 4.38 ns 4.38 ns 160002060 -bit::transform(BinaryOp) (small) (UU) 42.1 ns 42.1 ns 16549758 -dynamic_bitset::transform(BinaryOp) (small) 4.36 ns 4.36 ns 160692979 -bitarray::transform(BinaryOp) (small) 10.7 ns 10.7 ns 66178974 -std::transform(BinaryOp) (small) 855 ns 855 ns 832115 -bit::transform(BinaryOp) (large) 763642 ns 763574 ns 912 -bit::transform(BinaryOp) (large) (UU) 10966202 ns 10966406 ns 64 -dynamic_bitset::transform(BinaryOp) (large) 758617 ns 758574 ns 906 -bitarray::transform(BinaryOp) (large) 518286 ns 518267 ns 1177 -std::transform(BinaryOp) (large) 802270688 ns 802303941 ns 1 -bit::rotate (small) 131 ns 131 ns 16525922 -std::rotate (small) 1782 ns 1782 ns 417293 -bit::rotate (large) 7333284 ns 7333170 ns 96 -std::rotate (large) 514697313 ns 514718779 ns 1 -bit::count (small) 8.14 ns 8.14 ns 86522765 -dynamic_bitset::count (small) 6.29 ns 6.29 ns 108878018 -bitarray::count (small) 5.47 ns 5.47 ns 133692569 -std::count (small) 234 ns 234 ns 2997782 -bit::count (large) 365194 ns 365159 ns 1919 -dynamic_bitset::count (large) 365279 ns 365269 ns 1919 -bitarray::count (large) 917302 ns 917185 ns 764 -std::count (large) 58934071 ns 58931785 ns 12 -bit::swap_ranges (small) 9.58 ns 9.57 ns 73128377 -bit::swap_ranges (small) (UU) 19.7 ns 19.7 ns 35498474 -std::swap_ranges (small) 756 ns 756 ns 912041 -bit::swap_ranges (large) 852205 ns 852241 ns 821 -bit::swap_ranges (large) (UU) 5691899 ns 5692145 ns 123 -std::swap_ranges (large) 522198664 ns 522161939 ns 1 -bit::copy (small) (UU) 25.0 ns 25.0 ns 28200772 -std::copy (small) 707 ns 707 ns 990757 -bit::copy (large) (UU) 5952278 ns 5951729 ns 116 -std::copy (large) 189551338 ns 189554366 ns 4 -bit::equal (small) (UU) 13.1 ns 13.1 ns 53616228 -std::equal (small) 886 ns 886 ns 790035 -bit::equal (large) (UU) 1960399 ns 1960375 ns 357 -std::equal (large) 234389098 ns 234398907 ns 3 -bit::move (small) (UU) 23.5 ns 23.5 ns 29764745 -std::move (small) 706 ns 706 ns 992054 -bit::move (large) (UU) 5135837 ns 5135619 ns 136 -std::move (large) 188961979 ns 188953500 ns 4 -bit::copy_backward (small) (UU) 39.0 ns 39.0 ns 17977387 -std::copy_backward (small) 527 ns 527 ns 1313265 -bit::copy_backward (large) (UU) 9163333 ns 9163038 ns 76 -std::copy_backward (large) 444362971 ns 444350668 ns 2 -bit::fill (small) (UU) 6.48 ns 6.48 ns 108934237 -dynamic_bitset::fill (small) 4.79 ns 4.79 ns 146205764 -bitarray::fill (small) 14.5 ns 14.5 ns 48030428 -std::fill (small) 9.15 ns 9.15 ns 76612702 -bit::fill (large) (UU) 440400 ns 440396 ns 1590 -dynamic_bitset::fill (large) 429375 ns 429359 ns 1631 -bitarray::fill (large) 369732 ns 369736 ns 1964 -std::fill (large) 356517 ns 356488 ns 1894 -bit::find (small) (UU) 3.10 ns 3.10 ns 228714994 -dynamic_bitset::find (small) 3.05 ns 3.05 ns 229830138 -bitarray::find (small) 7.38 ns 7.38 ns 99039746 -std::find (small) 110 ns 110 ns 6311725 -bit::find (large) (UU) 182002 ns 182006 ns 3850 -dynamic_bitset::find (large) 259896 ns 259908 ns 2696 -bitarray::find (large) 252434 ns 252445 ns 2774 -std::find (large) 28570723 ns 28567762 ns 25 -``` - +# Future Work + +Some features in-mind for this library: + +- bit_integer + * A compile-time/construction-time integer with support for all numeric operators + +- bit_vector + * Alias to a `bit::vector<...>` class which will support any vector-like container (ex: folly:small_vector) + +- bit_float + * Arbitrary precision floating point type + + +# Contributors +- Vincent Reverdy +- Bryce Kille +- Peter McLean + +# License + +This open source library is license under [BSD 3-Clause License](./LICENSE). + +> [!IMPORTANT] +> This library uses libpopcnt which has its own [BSD 2-Clause License](include/bitlib/bit-algorithms/libpopcnt.h) diff --git a/benchmark/CMakeLists.txt b/benchmark/CMakeLists.txt index 6870bea1..3bcd87e9 100644 --- a/benchmark/CMakeLists.txt +++ b/benchmark/CMakeLists.txt @@ -1,26 +1,103 @@ +cmake_minimum_required(VERSION 3.31) + set(BENCHMARK_ENABLE_GTEST_TESTS OFF) -find_package(benchmark REQUIRED) +include(FetchContent) + +set(BENCHMARK_ENABLE_GTEST_TESTS OFF CACHE BOOL "" FORCE) +set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "" FORCE) +set(BENCHMARK_ENABLE_WERROR OFF CACHE BOOL "" FORCE) + +find_package(benchmark CONFIG) +if (NOT benchmark_DIR OR "${benchmark_DIR}" STREQUAL "benchmark-NOTFOUND") + FetchContent_Declare( + benchmark + GIT_REPOSITORY https://github.com/google/benchmark.git + GIT_TAG afa23b7699c17f1e26c88cbf95257b20d78d6247 #v1.9.2 + ) + FetchContent_MakeAvailable(benchmark) +endif() + +find_package(sul-dynamic_bitset) +if (NOT sul-dynamic_bitset_DIR OR "${sul-dynamic_bitset_DIR}" STREQUAL "sul-dynamic_bitset-NOTFOUND") + FetchContent_Declare( + sul-dynamic_bitset + GIT_REPOSITORY https://github.com/pinam45/dynamic_bitset.git + GIT_TAG ef647f07a663a4cbbd3bf8c70848876e63b64ce1 #v1.3.2 + EXCLUDE_FROM_ALL + FIND_PACKAGE_ARGS + ) + FetchContent_MakeAvailable(sul-dynamic_bitset) +endif() + +if (NOT TARGET itsy::bitsy) + + set(ITSY_BITSY_SINGLE OFF CACHE BOOL "" FORCE) + set(ITSY_BITSY_TESTS OFF CACHE BOOL "" FORCE) -# set output directory of builds -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + find_package(itsy.bitsy) + if (NOT itsy.bitsy_DIR OR "${itsy.bitsy_DIR}" STREQUAL "itsy.bitsy-NOTFOUND") + FetchContent_Declare( + itsy.bitsy + GIT_REPOSITORY https://github.com/ThePhD/itsy_bitsy.git + GIT_TAG d5b6bf9509bb2dff6235452d427f0b1c349d5f8b # main @ Aug 1, 2022 + EXCLUDE_FROM_ALL + FIND_PACKAGE_ARGS + ) + FetchContent_MakeAvailable(itsy.bitsy) + endif() +endif() -# set build type -set(CMAKE_BUILD_TYPE Release) +FetchContent_Declare( + BitArray + GIT_REPOSITORY https://github.com/noporpoise/BitArray.git + GIT_TAG 560ed785a0687b35301a8c23b8bb9fc06b69d827 # v2.0 + EXCLUDE_FROM_ALL +) +FetchContent_MakeAvailable(BitArray) +add_library(BitArray SHARED EXCLUDE_FROM_ALL) +target_sources(BitArray PRIVATE + ${CMAKE_BINARY_DIR}/_deps/bitarray-src/bit_array.c + PUBLIC + ${CMAKE_BINARY_DIR}/_deps/bitarray-src/bit_array.h + ${CMAKE_BINARY_DIR}/_deps/bitarray-src/bit_macros.h +) +target_include_directories(BitArray PUBLIC + ${CMAKE_BINARY_DIR}/_deps/bitarray-src +) # Add targets -file(GLOB BENCH_SOURCES "src/*.cc") -add_executable(bitlib-bench ${BENCH_SOURCES}) +add_executable(bitlib-bench EXCLUDE_FROM_ALL) -add_subdirectory(ext/dynamic_bitset) +target_sources(bitlib-bench PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/src/benchmark_main.cc + ${CMAKE_CURRENT_SOURCE_DIR}/src/copy_backward_bench.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/copy_bench.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/count_bench.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/equal_bench.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/fill_bench.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/find_bench.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/move_bench.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/reverse_bench.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/rotate_bench.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/rw_bench.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/shift_bench.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/swap_ranges-bench.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/transform_bench.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/inc/benchmark_utils.hpp +) +target_include_directories(bitlib-bench PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/src + ${CMAKE_CURRENT_SOURCE_DIR}/inc +) -# specify benchmark-specific libraries -include_directories( - ${googlebench_SOURCE_DIR}/benchmark/include - src/utils - ext/BitArray - ext/itsy_bitsy/include) -target_link_libraries(bitlib-bench PRIVATE benchmark::benchmark -pthread ${CMAKE_CURRENT_LIST_DIR}/ext/BitArray/libbitarr.a sul::dynamic_bitset) +target_link_libraries(bitlib-bench PRIVATE + bitlib + benchmark::benchmark + sul::dynamic_bitset + BitArray) -target_compile_options(bitlib-bench PUBLIC -O3 -DNDEBUG -march=native -Wpedantic) -install(TARGETS bitlib-bench DESTINATION .) +add_test( + NAME BenchmarkTest + COMMAND $ --benchmark_format=json --benchmark_out=benchmark_result.json --benchmark_min_warmup_time=0.001 --benchmark_min_time=0.01s +) diff --git a/benchmark/inc/benchmark_utils.hpp b/benchmark/inc/benchmark_utils.hpp new file mode 100644 index 00000000..4ba4d833 --- /dev/null +++ b/benchmark/inc/benchmark_utils.hpp @@ -0,0 +1,70 @@ +#ifndef _BENCHMARK_UTILS_HPP_ +#define _BENCHMARK_UTILS_HPP_ + +#include +#include +#include +#include +#include +#include + +// Produces container of random numbers from min to max +template +Container make_random_container( + std::size_t size, + T min = std::numeric_limits::min(), + T max = std::numeric_limits::max(), + const T& seed = T() +) +{ + Container c(size); + std::random_device device; + std::mt19937 engine(seed == T() ? device() : seed); + std::uniform_int_distribution distribution(min, max); + auto it = std::begin(c); + for (std::size_t i = 0; i < size; ++i) { + *it = distribution(engine); + ++it; + } + return c; +} + +// clang-format off +template + requires std::integral +struct uniform_dist_type { + static constexpr bool is_signed = std::is_signed_v; + static constexpr std::size_t size = sizeof(T); + + using type = std::conditional_t, + std::conditional_t, + std::conditional_t, + void // fallback (shouldn't happen for standard integral types) + >>>; +}; +// clang-format on + +template +using uniform_dist_type_t = typename uniform_dist_type::type; + +template +std::vector get_random_vec( + unsigned long long int size, + WordType min = std::numeric_limits::min(), + WordType max = std::numeric_limits::max()) { + std::random_device device; + std::mt19937 mersenne_engine(device()); + std::uniform_int_distribution> dist{min, max}; + + auto gen = [&dist, &mersenne_engine]() { + return static_cast(dist(mersenne_engine)); + }; + std::vector vec(size); + generate(begin(vec), end(vec), gen); + return vec; +} + +#endif diff --git a/benchmark/src/copy_backward_bench.hpp b/benchmark/src/copy_backward_bench.hpp index 229a08b5..da2408af 100644 --- a/benchmark/src/copy_backward_bench.hpp +++ b/benchmark/src/copy_backward_bench.hpp @@ -1,5 +1,6 @@ #include -#include "test_utils.hpp" + +#include "benchmark_utils.hpp" #include "bitlib/bitlib.hpp" auto BM_BitCopyBackward = [](benchmark::State& state, auto input) { @@ -30,8 +31,8 @@ auto BM_BoolCopyBackward = [](benchmark::State& state, auto input) { using container_type = typename std::tuple_element<0, decltype(input)>::type; unsigned int total_bits = std::get<2>(input); auto container_size = total_bits; - container_type boolvec1 = make_random_container (container_size); - container_type boolvec2 = make_random_container (container_size); + container_type boolvec1 = make_random_container(container_size); + container_type boolvec2 = make_random_container(container_size); auto first1 = boolvec1.begin(); auto first2 = boolvec2.begin(); diff --git a/benchmark/src/copy_bench.hpp b/benchmark/src/copy_bench.hpp index 7da3da86..8fe9246d 100644 --- a/benchmark/src/copy_bench.hpp +++ b/benchmark/src/copy_bench.hpp @@ -1,5 +1,6 @@ #include -#include "test_utils.hpp" + +#include "benchmark_utils.hpp" #include "bitlib/bitlib.hpp" auto BM_BitCopy = [](benchmark::State& state, auto input) { @@ -30,8 +31,8 @@ auto BM_BoolCopy = [](benchmark::State& state, auto input) { using container_type = typename std::tuple_element<0, decltype(input)>::type; unsigned int total_bits = std::get<2>(input); auto container_size = total_bits; - container_type boolvec1 = make_random_container (container_size); - container_type boolvec2 = make_random_container (container_size); + container_type boolvec1 = make_random_container(container_size); + container_type boolvec2 = make_random_container(container_size); auto first1 = boolvec1.begin(); auto first2 = boolvec2.begin(); diff --git a/benchmark/src/count_bench.hpp b/benchmark/src/count_bench.hpp index a169d005..476ef7f3 100644 --- a/benchmark/src/count_bench.hpp +++ b/benchmark/src/count_bench.hpp @@ -1,8 +1,9 @@ #include #include -#include "test_utils.hpp" -#include "bitlib/bit-algorithms/count.hpp" + +#include "benchmark_utils.hpp" #include "bit_array.h" +#include "bitlib/bit-algorithms/count.hpp" #include "sul/dynamic_bitset.hpp" auto BM_BitCount = [](benchmark::State& state, auto input) { @@ -11,7 +12,7 @@ auto BM_BitCount = [](benchmark::State& state, auto input) { unsigned int total_bits = std::get<2>(input); auto digits = bit::binary_digits::value; auto container_size = ceil(float(total_bits) / digits); - container_type bitcont = make_random_container(container_size); + container_type bitcont = make_random_container(container_size); auto first = bit::bit_iterator(std::begin(bitcont)); auto last = bit::bit_iterator(std::end(bitcont)); for (auto _ : state) { @@ -48,7 +49,7 @@ auto BM_BoolCount = [](benchmark::State& state, auto input) { using container_type = std::vector; using num_type = typename container_type::value_type; unsigned int container_size = std::get<2>(input); - container_type cont = make_random_container(container_size); + container_type cont = make_random_container(container_size); auto first = cont.begin(); auto last = cont.end(); for (auto _ : state) { diff --git a/benchmark/src/equal_bench.hpp b/benchmark/src/equal_bench.hpp index b6ce6f36..3e42376c 100644 --- a/benchmark/src/equal_bench.hpp +++ b/benchmark/src/equal_bench.hpp @@ -1,5 +1,6 @@ #include -#include "test_utils.hpp" + +#include "benchmark_utils.hpp" #include "bitlib/bitlib.hpp" auto BM_BitEqual = [](benchmark::State& state, auto input) { @@ -32,8 +33,8 @@ auto BM_BoolEqual = [](benchmark::State& state, auto input) { using container_type = typename std::tuple_element<0, decltype(input)>::type; unsigned int total_bits = std::get<2>(input); auto container_size = total_bits; - container_type boolvec1 = make_random_container (container_size); - container_type boolvec2 = make_random_container (container_size); + container_type boolvec1 = make_random_container(container_size); + container_type boolvec2 = make_random_container(container_size); auto first1 = boolvec1.begin(); auto first2 = boolvec2.begin(); diff --git a/benchmark/src/move_bench.hpp b/benchmark/src/move_bench.hpp index de76d6a3..237d9c2d 100644 --- a/benchmark/src/move_bench.hpp +++ b/benchmark/src/move_bench.hpp @@ -1,5 +1,6 @@ #include -#include "test_utils.hpp" + +#include "benchmark_utils.hpp" #include "bitlib/bitlib.hpp" auto BM_BitMove = [](benchmark::State& state, auto input) { @@ -29,8 +30,8 @@ auto BM_BoolMove = [](benchmark::State& state, auto input) { using container_type = typename std::tuple_element<0, decltype(input)>::type; unsigned int total_bits = std::get<2>(input); auto container_size = total_bits; - container_type boolvec1 = make_random_container (container_size); - container_type boolvec2 = make_random_container (container_size); + container_type boolvec1 = make_random_container(container_size); + container_type boolvec2 = make_random_container(container_size); auto first1 = boolvec1.begin(); auto first2 = boolvec2.begin(); diff --git a/benchmark/src/rw_bench.hpp b/benchmark/src/rw_bench.hpp index 2aac15d1..b9f976d9 100644 --- a/benchmark/src/rw_bench.hpp +++ b/benchmark/src/rw_bench.hpp @@ -1,7 +1,8 @@ #include -#include "test_utils.hpp" -#include "sul/dynamic_bitset.hpp" + +#include "benchmark_utils.hpp" #include "bitlib/bitlib.hpp" +#include "sul/dynamic_bitset.hpp" auto BM_BitSet = [](benchmark::State& state, auto input) { using container_type = typename std::tuple_element<0, decltype(input)>::type; @@ -13,8 +14,9 @@ auto BM_BitSet = [](benchmark::State& state, auto input) { auto first1 = bit::bit_iterator(std::begin(bitvec1)); for (auto _ : state) { - benchmark::DoNotOptimize(first1[total_bits/2] = bit::bit1); - benchmark::ClobberMemory(); + first1[total_bits / 2] = bit::bit1; + benchmark::DoNotOptimize(first1); + benchmark::ClobberMemory(); } }; @@ -38,7 +40,7 @@ auto BM_DynamicBitsetSet = [](benchmark::State& state, auto input) { using WordType = typename std::tuple_element<1, decltype(input)>::type; unsigned int total_bits = std::get<2>(input); sul::dynamic_bitset x(total_bits); - container_type boolvec1 = make_random_container (total_bits); + container_type boolvec1 = make_random_container(total_bits); for (auto i = 0; i < total_bits; ++i) { x[i] = boolvec1[i]; } @@ -53,7 +55,7 @@ auto BM_BoolSet = [](benchmark::State& state, auto input) { using container_type = typename std::tuple_element<0, decltype(input)>::type; unsigned int total_bits = std::get<2>(input); auto container_size = total_bits; - container_type boolvec1 = make_random_container (container_size); + container_type boolvec1 = make_random_container(container_size); for (auto _ : state) { @@ -98,7 +100,7 @@ auto BM_DynamicBitsetGet = [](benchmark::State& state, auto input) { using WordType = typename std::tuple_element<1, decltype(input)>::type; unsigned int total_bits = std::get<2>(input); sul::dynamic_bitset x(total_bits); - container_type boolvec1 = make_random_container (total_bits); + container_type boolvec1 = make_random_container(total_bits); for (auto i = 0; i < total_bits; ++i) { x[i] = boolvec1[i]; } @@ -113,7 +115,7 @@ auto BM_BoolGet = [](benchmark::State& state, auto input) { using container_type = typename std::tuple_element<0, decltype(input)>::type; unsigned int total_bits = std::get<2>(input); auto container_size = total_bits; - container_type boolvec1 = make_random_container (container_size); + container_type boolvec1 = make_random_container(container_size); bool x; for (auto _ : state) { diff --git a/benchmark/src/shift_bench.hpp b/benchmark/src/shift_bench.hpp index 8f38383e..3410b983 100644 --- a/benchmark/src/shift_bench.hpp +++ b/benchmark/src/shift_bench.hpp @@ -13,13 +13,14 @@ auto BM_BitShiftLeft = [](benchmark::State& state, auto input) { unsigned int total_bits = std::get<2>(input); auto digits = bit::binary_digits::value; auto container_size = ceil(float(total_bits) / digits); - container_type bitcont = make_random_container(container_size); + container_type bitcont = make_random_container(container_size); auto first = bit::bit_iterator(std::begin(bitcont)); auto last = bit::bit_iterator(std::end(bitcont)); auto n = total_bits / 2 - 1; for (auto _ : state) { - benchmark::DoNotOptimize(bit::shift_left(first, last, n)); - benchmark::ClobberMemory(); + auto result = bit::shift_left(first, last, n); + benchmark::DoNotOptimize(result); + benchmark::ClobberMemory(); } }; @@ -31,13 +32,14 @@ auto BM_BitShiftLeft_UU = [](benchmark::State& state, auto input) { unsigned int total_bits = std::get<2>(input); auto digits = bit::binary_digits::value; auto container_size = ceil(float(total_bits) / digits); - container_type bitcont = make_random_container(container_size); + container_type bitcont = make_random_container(container_size); bit::bit_iterator first = bit::bit_iterator(bitcont.begin()) + 1; bit::bit_iterator last = bit::bit_iterator(bitcont.end()) - 1; auto n = total_bits / 2 + 3; for (auto _ : state) { - benchmark::DoNotOptimize(bit::shift_left(first, last, n)); - benchmark::ClobberMemory(); + auto result = bit::shift_left(first, last, n); + benchmark::DoNotOptimize(result); + benchmark::ClobberMemory(); } }; @@ -72,13 +74,14 @@ auto BM_BoolShiftLeft = [](benchmark::State& state, auto input) { using container_type = std::vector; using num_type = typename container_type::value_type; unsigned int container_size = std::get<2>(input); - container_type cont = make_random_container(container_size); + container_type cont = make_random_container(container_size); auto first = cont.begin(); auto last = cont.end(); auto n = std::distance(first, last) / 2 - 1; for (auto _ : state) { - benchmark::DoNotOptimize(bit::word_shift_left(first, last, n)); - benchmark::ClobberMemory(); + auto result = bit::word_shift_left(first, last, n); + benchmark::DoNotOptimize(result); + benchmark::ClobberMemory(); } }; @@ -88,13 +91,14 @@ auto BM_BitShiftRight = [](benchmark::State& state, auto input) { unsigned int total_bits = std::get<2>(input); auto digits = bit::binary_digits::value; auto container_size = ceil(float(total_bits) / digits); - container_type bitcont = make_random_container(container_size); + container_type bitcont = make_random_container(container_size); auto first = bit::bit_iterator(std::begin(bitcont)); auto last = bit::bit_iterator(std::end(bitcont)); auto n = total_bits / 2 - 1; for (auto _ : state) { - benchmark::DoNotOptimize(bit::shift_right(first, last, n)); - benchmark::ClobberMemory(); + auto result = bit::shift_right(first, last, n); + benchmark::DoNotOptimize(result); + benchmark::ClobberMemory(); } }; @@ -104,13 +108,14 @@ auto BM_BitShiftRight_UU = [](benchmark::State& state, auto input) { unsigned int total_bits = std::get<2>(input); auto digits = bit::binary_digits::value; auto container_size = ceil(float(total_bits) / digits); - container_type bitcont = make_random_container(container_size); + container_type bitcont = make_random_container(container_size); auto first = bit::bit_iterator(std::begin(bitcont)) + 1; auto last = bit::bit_iterator(std::end(bitcont)) - 1; auto n = total_bits / 2 + 3; for (auto _ : state) { - benchmark::DoNotOptimize(bit::shift_right(first, last, n)); - benchmark::ClobberMemory(); + auto result = bit::shift_right(first, last, n); + benchmark::DoNotOptimize(result); + benchmark::ClobberMemory(); } }; @@ -144,7 +149,7 @@ auto BM_BoolShiftRight = [](benchmark::State& state, auto input) { using container_type = std::vector; using num_type = typename container_type::value_type; unsigned int container_size = std::get<2>(input); - container_type cont = make_random_container(container_size); + container_type cont = make_random_container(container_size); auto first = cont.begin(); auto last = cont.end(); auto n = std::distance(first, last) / 2 - 1; diff --git a/benchmark/src/swap_ranges-bench.hpp b/benchmark/src/swap_ranges-bench.hpp index 92cc5845..9e5be48d 100644 --- a/benchmark/src/swap_ranges-bench.hpp +++ b/benchmark/src/swap_ranges-bench.hpp @@ -1,5 +1,6 @@ #include -#include "test_utils.hpp" + +#include "benchmark_utils.hpp" #include "bitlib/bitlib.hpp" auto BM_BitSwapRangesAA = [](benchmark::State& state, auto input) { @@ -51,8 +52,8 @@ auto BM_BoolSwapRanges = [](benchmark::State& state, auto input) { using container_type = typename std::tuple_element<0, decltype(input)>::type; unsigned int total_bits = std::get<2>(input); auto container_size = total_bits; - container_type boolvec1 = make_random_container (container_size); - container_type boolvec2 = make_random_container (container_size); + container_type boolvec1 = make_random_container(container_size); + container_type boolvec2 = make_random_container(container_size); auto first1 = boolvec1.begin(); auto first2 = boolvec2.begin(); diff --git a/benchmark/src/transform_bench.hpp b/benchmark/src/transform_bench.hpp index 673d4953..7576934c 100644 --- a/benchmark/src/transform_bench.hpp +++ b/benchmark/src/transform_bench.hpp @@ -1,10 +1,12 @@ -#include -#include #include -#include "test_utils.hpp" + +#include +#include + +#include "benchmark_utils.hpp" +#include "bit_array.h" #include "bitlib/bitlib.hpp" #include "sul/dynamic_bitset.hpp" -#include "bit_array.h" auto BM_BitTransformUnaryAA = [](benchmark::State& state, auto input) { using container_type = typename std::tuple_element<0, decltype(input)>::type; @@ -84,8 +86,8 @@ auto BM_BoolTransformUnary = [](benchmark::State& state, auto input) { using container_type = typename std::tuple_element<0, decltype(input)>::type; unsigned int total_bits = std::get<2>(input); auto container_size = total_bits; - container_type boolvec1 = make_random_container (container_size); - container_type boolvec2 = make_random_container (container_size); + container_type boolvec1 = make_random_container(container_size); + container_type boolvec2 = make_random_container(container_size); auto first1 = boolvec1.begin(); auto first2 = boolvec2.begin(); @@ -191,9 +193,9 @@ auto BM_BoolTransformBinary = [](benchmark::State& state, auto input) { using container_type = typename std::tuple_element<0, decltype(input)>::type; unsigned int total_bits = std::get<2>(input); auto container_size = total_bits; - container_type boolvec1 = make_random_container (container_size); - container_type boolvec2 = make_random_container (container_size); - container_type boolvec3 = make_random_container (container_size); + container_type boolvec1 = make_random_container(container_size); + container_type boolvec2 = make_random_container(container_size); + container_type boolvec3 = make_random_container(container_size); auto first1 = boolvec1.begin(); auto first2 = boolvec2.begin(); diff --git a/doc/benchmarks.md b/doc/benchmarks.md new file mode 100644 index 00000000..e799d361 --- /dev/null +++ b/doc/benchmarks.md @@ -0,0 +1,118 @@ + +# Performance Benchmarks +I used Google's [benchmark](https://github.com/google/benchmark) library for computing benchmarks. Each benchmark is formatted as `{bit, BitArray, std}::function` (size) [(alignment-tags)]. + + * `bit` is for this library, `BitArray` is for the popular C-based [BitArray library](https://github.com/noporpoise/BitArray), [dynamic_bitset](https://github.com/pinam45/dynamic_bitset) is a header-only library similar to Boost's dynamic_bitset, and`std` is the standard library operating on the infamous `vector`. +* (size) denotes the size of the container in bits. `small = 1 << 8`, `medium= 1 << 16`, `large = 1 << 24`, `huge = 1 << 31` +* (alignment-tags) refers to the memory alignment of the bit-iterators. `U` means the iterator does not fall on a word boundary, `R` means the iterator is placed at random, and `A` means the iterator is aligned with a word boundary. + +For example, `bit::rotate (large) (ARA)` refers to our library's implementation of the `rotate` algorithm operating on a container of 65536 bits, where `first` and `last` are aligned but `n_first` is selected at random. + +``` +--------------------------------------------------------------------------------------- +Benchmark Time CPU Iterations +--------------------------------------------------------------------------------------- +bit::set (large) 1.90 ns 1.90 ns 367974893 +dynamic_bitset::set (large) 2.37 ns 2.37 ns 296837879 +bitarray::set (large) 2.19 ns 2.19 ns 319133940 +std::set (large) 2.39 ns 2.39 ns 293135332 +bit::shift_left (small) 26.8 ns 26.8 ns 25929070 +bit::shift_left (small) (UU) 22.4 ns 22.4 ns 31233265 +dynamic_bitset::shift_left (small) 13.1 ns 13.1 ns 53627207 +bitarray::shift_left (small) 38.2 ns 38.2 ns 18339126 +std::shift_left (small) 345 ns 345 ns 2029283 +bit::shift_left (large) 371224 ns 371211 ns 1886 +bit::shift_left (large) (UU) 371536 ns 371530 ns 1880 +dynamic_bitset::shift_left (large) 638896 ns 638880 ns 1097 +bitarray::shift_left (large) 3156273 ns 3156003 ns 222 +std::shift_left (large) 105227752 ns 105223527 ns 7 +bit::shift_right (small) 26.9 ns 26.9 ns 25976563 +bit::shift_right (small) (UU) 39.3 ns 39.3 ns 17962533 +dynamic_bitset::shift_right (small) 12.2 ns 12.2 ns 57419526 +bitarray::shift_right (small) 38.1 ns 38.1 ns 18325350 +std::shift_right (small) 504 ns 504 ns 1386280 +bit::shift_right (large) 413297 ns 413269 ns 1693 +bit::shift_right (large) (UU) 413692 ns 413655 ns 1682 +dynamic_bitset::shift_right (large) 557287 ns 557305 ns 1257 +bitarray::shift_right (large) 3156463 ns 3156516 ns 222 +std::shift_right (large) 210100788 ns 210083631 ns 3 +bit::reverse (small) (UU) 43.4 ns 43.4 ns 16112098 +bitarray::reverse (small) (UU) 95.1 ns 95.1 ns 7387177 +std::reverse (small) 419 ns 419 ns 1677069 +bit::reverse (large) 1245260 ns 1245160 ns 563 +bit::reverse (large) (UU) 1800771 ns 1800680 ns 389 +bitarray::reverse (large) 16899481 ns 16898587 ns 41 +bitarray::reverse (large) (UU) 22719408 ns 22720393 ns 31 +std::reverse (large) 293563397 ns 293542850 ns 2 +bit::transform(UnaryOp) (small) 8.75 ns 8.75 ns 80079214 +bit::transform(UnaryOp) (small) (UU) 16.6 ns 16.6 ns 42254961 +dynamic_bitset::transform(UnaryOp) (small) 4.00 ns 4.00 ns 169219246 +bitarray::transform(UnaryOp) (small) 8.39 ns 8.39 ns 83877004 +std::transform(UnaryOp) (small) 763 ns 763 ns 917975 +bit::transform(UnaryOp) (large) 373982 ns 373950 ns 1853 +bit::transform(UnaryOp) (large) (UU) 2059234 ns 2059268 ns 339 +dynamic_bitset::transform(UnaryOp) (large) 379368 ns 379368 ns 1805 +bitarray::transform(UnaryOp) (large) 739552 ns 739544 ns 881 +std::transform(UnaryOp) (large) 197977698 ns 197969224 ns 4 +bit::transform(BinaryOp) (small) 4.38 ns 4.38 ns 160002060 +bit::transform(BinaryOp) (small) (UU) 42.1 ns 42.1 ns 16549758 +dynamic_bitset::transform(BinaryOp) (small) 4.36 ns 4.36 ns 160692979 +bitarray::transform(BinaryOp) (small) 10.7 ns 10.7 ns 66178974 +std::transform(BinaryOp) (small) 855 ns 855 ns 832115 +bit::transform(BinaryOp) (large) 763642 ns 763574 ns 912 +bit::transform(BinaryOp) (large) (UU) 10966202 ns 10966406 ns 64 +dynamic_bitset::transform(BinaryOp) (large) 758617 ns 758574 ns 906 +bitarray::transform(BinaryOp) (large) 518286 ns 518267 ns 1177 +std::transform(BinaryOp) (large) 802270688 ns 802303941 ns 1 +bit::rotate (small) 131 ns 131 ns 16525922 +std::rotate (small) 1782 ns 1782 ns 417293 +bit::rotate (large) 7333284 ns 7333170 ns 96 +std::rotate (large) 514697313 ns 514718779 ns 1 +bit::count (small) 8.14 ns 8.14 ns 86522765 +dynamic_bitset::count (small) 6.29 ns 6.29 ns 108878018 +bitarray::count (small) 5.47 ns 5.47 ns 133692569 +std::count (small) 234 ns 234 ns 2997782 +bit::count (large) 365194 ns 365159 ns 1919 +dynamic_bitset::count (large) 365279 ns 365269 ns 1919 +bitarray::count (large) 917302 ns 917185 ns 764 +std::count (large) 58934071 ns 58931785 ns 12 +bit::swap_ranges (small) 9.58 ns 9.57 ns 73128377 +bit::swap_ranges (small) (UU) 19.7 ns 19.7 ns 35498474 +std::swap_ranges (small) 756 ns 756 ns 912041 +bit::swap_ranges (large) 852205 ns 852241 ns 821 +bit::swap_ranges (large) (UU) 5691899 ns 5692145 ns 123 +std::swap_ranges (large) 522198664 ns 522161939 ns 1 +bit::copy (small) (UU) 25.0 ns 25.0 ns 28200772 +std::copy (small) 707 ns 707 ns 990757 +bit::copy (large) (UU) 5952278 ns 5951729 ns 116 +std::copy (large) 189551338 ns 189554366 ns 4 +bit::equal (small) (UU) 13.1 ns 13.1 ns 53616228 +std::equal (small) 886 ns 886 ns 790035 +bit::equal (large) (UU) 1960399 ns 1960375 ns 357 +std::equal (large) 234389098 ns 234398907 ns 3 +bit::move (small) (UU) 23.5 ns 23.5 ns 29764745 +std::move (small) 706 ns 706 ns 992054 +bit::move (large) (UU) 5135837 ns 5135619 ns 136 +std::move (large) 188961979 ns 188953500 ns 4 +bit::copy_backward (small) (UU) 39.0 ns 39.0 ns 17977387 +std::copy_backward (small) 527 ns 527 ns 1313265 +bit::copy_backward (large) (UU) 9163333 ns 9163038 ns 76 +std::copy_backward (large) 444362971 ns 444350668 ns 2 +bit::fill (small) (UU) 6.48 ns 6.48 ns 108934237 +dynamic_bitset::fill (small) 4.79 ns 4.79 ns 146205764 +bitarray::fill (small) 14.5 ns 14.5 ns 48030428 +std::fill (small) 9.15 ns 9.15 ns 76612702 +bit::fill (large) (UU) 440400 ns 440396 ns 1590 +dynamic_bitset::fill (large) 429375 ns 429359 ns 1631 +bitarray::fill (large) 369732 ns 369736 ns 1964 +std::fill (large) 356517 ns 356488 ns 1894 +bit::find (small) (UU) 3.10 ns 3.10 ns 228714994 +dynamic_bitset::find (small) 3.05 ns 3.05 ns 229830138 +bitarray::find (small) 7.38 ns 7.38 ns 99039746 +std::find (small) 110 ns 110 ns 6311725 +bit::find (large) (UU) 182002 ns 182006 ns 3850 +dynamic_bitset::find (large) 259896 ns 259908 ns 2696 +bitarray::find (large) 252434 ns 252445 ns 2774 +std::find (large) 28570723 ns 28567762 ns 25 +``` + diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 10292aca..570cee0b 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -1,8 +1,19 @@ -file( GLOB EXAMPLE_SOURCES src/*.cpp ) -foreach( examplesourcefile ${EXAMPLE_SOURCES} ) - # Cut off the file extension and directory path - get_filename_component( examplename ${examplesourcefile} NAME_WE ) - add_executable( ${examplename} ${examplesourcefile} ) - # Make sure YourLib is linked to each app - #target_link_libraries( ${examplename} YourLib ) -endforeach( examplesourcefile ${EXAMPLE_SOURCES} ) +project(BitLib_Examples) +cmake_minimum_required(VERSION 3.20) + +if(NOT TARGET bitlib::bitlib) + add_subdirectory(../ bitlib_dir) +endif() + +add_executable(BitLib_Example1 EXCLUDE_FROM_ALL) + +target_sources(BitLib_Example1 PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/src/example1.cpp" +) +target_link_libraries(BitLib_Example1 PRIVATE bitlib::bitlib) + +add_executable(BitLib_Example3 EXCLUDE_FROM_ALL) +target_sources(BitLib_Example3 PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/src/example3.cpp" +) +target_link_libraries(BitLib_Example3 PRIVATE bitlib::bitlib) \ No newline at end of file diff --git a/profile/src/main.cpp b/example/src/example2.cpp similarity index 100% rename from profile/src/main.cpp rename to example/src/example2.cpp diff --git a/example/src/example3.cpp b/example/src/example3.cpp new file mode 100644 index 00000000..a2cf2ee3 --- /dev/null +++ b/example/src/example3.cpp @@ -0,0 +1,30 @@ +#include "bitlib/bitlib.hpp" + +struct fp_dynamic : bit::bit_array<> { + const size_t mantissa_bits; + fp_dynamic() = delete; + fp_dynamic( + size_t exponent_bits, + size_t mantissa_bits) : + bit::bit_array<>(1+exponent_bits+mantissa_bits), + mantissa_bits(mantissa_bits) {} + bit::bit_reference<> sign() { + return (*this).back(); + } + auto exponent() { + return (*this)(mantissa_bits, this->size()-1); + } + auto mantissa() { + return (*this)(0, mantissa_bits); + } +}; + +#include + +int main(int argn, char* argv[]) { + fp_dynamic fp(6u, 7u); + fp.sign() = bit::bit1; + fp.exponent() = 6'5_b; + fp.mantissa() = 0x7'3F_b; + std::cout << "fp: " << fp << "\n"; +} diff --git a/include/bitlib/bit-algorithms/accumulate.hpp b/include/bitlib/bit-algorithms/accumulate.hpp new file mode 100644 index 00000000..e70c76d0 --- /dev/null +++ b/include/bitlib/bit-algorithms/accumulate.hpp @@ -0,0 +1,275 @@ +// ================================= array_REF =================================== // +// Project: The Experimental Bit Algorithms Library +// \file accumulate.hpp +// Description: Implementation of accumulate +// Creator: Vincent Reverdy +// Contributor: Peter McLean [2025] +// License: BSD 3-Clause License +// ========================================================================== // + +#ifndef _BIT_ACCUMULATE_HPP_INCLUDED +#define _BIT_ACCUMULATE_HPP_INCLUDED + +#include "bitlib/bit-iterator/bit_iterator.hpp" +#include "bitlib/bit-iterator/bit_details.hpp" + +namespace bit { + +namespace policy { +struct AccumulateInitialSubword { + static constexpr bool initial_sub_word = true; +}; + +struct AccumulateNoInitialSubword { + static constexpr bool initial_sub_word = false; +}; +} // namespace policy + +template +constexpr auto accumulate( + bit_iterator first, + bit_iterator last, + T acc, + BinaryOperation binary_op, + BinaryOperationSubword binary_op_subword) { + using word_type = typename bit_iterator::word_type; + using size_type = typename bit_iterator::size_type; + constexpr size_type digits = bitsof(); + + size_type total_bits_to_op = distance(first, last); + if constexpr (initial_sub_word) { + size_type sub_digits; + if constexpr (forward) { + sub_digits = std::min(digits - first.position(), total_bits_to_op); + if (sub_digits != 0) { + acc = binary_op_subword(std::move(acc), get_masked_word(first, sub_digits), sub_digits); + advance(first, digits - first.position()); + } + } else { + sub_digits = std::min(last.position(), total_bits_to_op); + if (sub_digits != 0) { + reverse(last, sub_digits); + acc = binary_op_subword(std::move(acc), get_masked_word(last, sub_digits), sub_digits); + } + } + total_bits_to_op -= sub_digits; + } + + const size_type whole_words_to_op = total_bits_to_op / digits; + const size_type remaining_bits_to_op = total_bits_to_op % digits; + + for (size_t i = 0; i < whole_words_to_op; i ++) { + if constexpr (forward) { + acc = binary_op(std::move(acc), get_word(first)); + advance(first, digits); + } else { + reverse(last, digits); + acc = binary_op(std::move(acc), get_word(last)); + } + } + if (remaining_bits_to_op > 0) { + if constexpr (forward) { + acc = binary_op_subword(std::move(acc), get_masked_word(first, remaining_bits_to_op), remaining_bits_to_op); + } else { + reverse(last, remaining_bits_to_op); + acc = binary_op_subword(std::move(acc), get_masked_word(last, remaining_bits_to_op), remaining_bits_to_op); + } + } + + return acc; +} + +template +constexpr auto accumulate_while( + bit_iterator first, + bit_iterator last, + T acc, + BinaryOperation binary_op, + BinaryOperationSubword binary_op_subword) { + using word_type = typename bit_iterator::word_type; + using size_type = typename bit_iterator::size_type; + constexpr size_type digits = bitsof(); + + size_type total_bits_to_op = distance(first, last); + size_type whole_words_to_op = total_bits_to_op / digits; + + bool keep_going = true;; + if constexpr (initial_sub_word) { + if (whole_words_to_op > 1) { + size_type sub_digits; + if constexpr (forward) { + sub_digits = std::min(digits - first.position(), total_bits_to_op); + if (sub_digits != 0) { + std::tie(keep_going, acc) = binary_op_subword(std::move(acc), get_masked_word(first, sub_digits), sub_digits); + advance(first, digits - first.position()); + } + } else { + sub_digits = std::min(last.position(), total_bits_to_op); + if (sub_digits != 0) { + reverse(last, sub_digits); + std::tie(keep_going, acc) = binary_op_subword(std::move(acc), get_masked_word(first, sub_digits), sub_digits); + } + } + total_bits_to_op -= sub_digits; + if (!keep_going) { + return acc; // Stop early if the operation indicates to stop + } + } + } + + whole_words_to_op = total_bits_to_op / digits; + const size_type remaining_bits_to_op = total_bits_to_op % digits; + + for (size_type i = 0; i < whole_words_to_op; i ++) { + if constexpr (forward) { + std::tie(keep_going, acc) = binary_op(std::move(acc), get_word(first)); + advance(first, digits); + } else { + reverse(last, digits); + std::tie(keep_going, acc) = binary_op(std::move(acc), get_word(last)); + } + if (!keep_going) { + return acc; // Stop early if the operation indicates to stop + } + } + if (remaining_bits_to_op > 0) { + if constexpr (forward) { + std::tie(std::ignore, acc) = binary_op_subword(std::move(acc), get_masked_word(first, remaining_bits_to_op), remaining_bits_to_op); + } else { + reverse(last, remaining_bits_to_op); + std::tie(std::ignore, acc) = binary_op_subword(std::move(acc), get_masked_word(last, remaining_bits_to_op), remaining_bits_to_op); + } + } + + return acc; +} + +// Requires BinaryOperation to have a third but defaulted argument +template +constexpr auto accumulate( + const bit_iterator& first, + const bit_iterator& last, + const T& acc, + BinaryOperation binary_op) { + return accumulate(first, last, acc, binary_op, binary_op); +} + +template +constexpr auto accumulate( + const bit_iterator& first, + const bit_iterator& last, + const T& acc, + BinaryOperation binary_op, + BinaryOperationSubword binary_op_subword) { + return accumulate(first, last, acc, binary_op, binary_op_subword); +} + +// Requires BinaryOperation to have a third but defaulted argument +template +constexpr auto accumulate_while( + const bit_iterator& first, + const bit_iterator& last, + const T& acc, + BinaryOperation binary_op) { + return accumulate_while(first, last, acc, binary_op, binary_op); +} + +template +constexpr auto accumulate_while( + const bit_iterator& first, + const bit_iterator& last, + const T& acc, + BinaryOperation binary_op, + BinaryOperationSubword binary_op_subword) { + return accumulate_while(first, last, acc, binary_op, binary_op_subword); +} + +// Requires BinaryOperation to have a third but defaulted argument +template +constexpr auto accumulate_backward_while( + const bit_iterator& first, + const bit_iterator& last, + const T& acc, + BinaryOperation binary_op) { + return accumulate_while(first, last, acc, binary_op, binary_op); +} + +template +constexpr auto accumulate_backward_while( + const bit_iterator& first, + const bit_iterator& last, + const T& acc, + BinaryOperation binary_op, + BinaryOperationSubword binary_op_subword) { + return accumulate_while(first, last, acc, binary_op, binary_op_subword); +} + +// Requires BinaryOperation to have a third but defaulted argument +template +constexpr auto accumulate( + Policy, + const bit_iterator& first, + const bit_iterator& last, + const T& acc, + BinaryOperation binary_op) { + return accumulate(first, last, acc, binary_op, binary_op); +} + +template +constexpr auto accumulate( + Policy, + const bit_iterator& first, + const bit_iterator& last, + const T& acc, + BinaryOperation binary_op, + BinaryOperationSubword binary_op_subword) { + return accumulate(first, last, acc, binary_op, binary_op_subword); +} + +// Requires BinaryOperation to have a third but defaulted argument +template +constexpr auto accumulate_while( + Policy, + const bit_iterator& first, + const bit_iterator& last, + const T& acc, + BinaryOperation binary_op) { + return accumulate_while(first, last, acc, binary_op, binary_op); +} + +template +constexpr auto accumulate_while( + Policy, + const bit_iterator& first, + const bit_iterator& last, + const T& acc, + BinaryOperation binary_op, + BinaryOperationSubword binary_op_subword) { + return accumulate_while(first, last, acc, binary_op, binary_op_subword); +} + +// Requires BinaryOperation to have a third but defaulted argument +template +constexpr auto accumulate_backward_while( + Policy, + const bit_iterator& first, + const bit_iterator& last, + const T& acc, + BinaryOperation binary_op) { + return accumulate_while(first, last, acc, binary_op, binary_op); +} + +template +constexpr auto accumulate_backward_while( + Policy, + const bit_iterator& first, + const bit_iterator& last, + const T& acc, + BinaryOperation binary_op, + BinaryOperationSubword binary_op_subword) { + return accumulate_while(first, last, acc, binary_op, binary_op_subword); +} + +} // namespace bit + +#endif // _BIT_ACCUMULATE_HPP_INCLUDED diff --git a/include/bitlib/bit-algorithms/addition.hpp b/include/bitlib/bit-algorithms/addition.hpp new file mode 100644 index 00000000..3a4f2766 --- /dev/null +++ b/include/bitlib/bit-algorithms/addition.hpp @@ -0,0 +1,60 @@ +// ================================== MOVE ================================== // +// Project: The Experimental Bit Algorithms Library +// Name: addition.hpp +// Description: bit range multiplication +// Contributor(s): +// License: BSD 3-Clause License +// ========================================================================== // +#ifndef _ADDITION_HPP_INCLUDED +#define _ADDITION_HPP_INCLUDED +// ========================================================================== // + +// ============================== PREAMBLE ================================== // +// C++ standard library +#include +#include +// Project sources +#include "bitlib/bit-algorithms/transform.hpp" +#include "bitlib/bit-containers/bit_bitsof.hpp" +#include "bitlib/bit-iterator/bit_details.hpp" +#include "bitlib/bit-iterator/bit_iterator.hpp" + +namespace bit { + +template + requires( + (is_static_castable_v::word_type>) && + (bitsof() <= bitsof::word_type>())) +constexpr unsigned char addition( + const bit_iterator& first, + const bit_iterator& last, + const bit_iterator& d_first, + const U& integral_operand) { + using word_type = typename bit_iterator::word_type; + unsigned char carry = 0; + transform(first, last, d_first, + [&carry, integral_operand](auto word, auto bits = bitsof()) -> word_type { + word_type result_word; + carry = add_carry(carry, static_cast(integral_operand), word, &result_word); + if (bits < bitsof()) { + carry = carry | (0 != lsr(result_word, bits)); + } + return result_word; + }); + return carry; +} + +template + requires( + (is_static_castable_v::word_type>) && + (bitsof() <= bitsof::word_type>())) +constexpr unsigned char addition( + const bit_iterator& first, + const bit_iterator& last, + const U& integral_operand) { + return addition(first, last, first, integral_operand); +} + +} // namespace bit + +#endif // _ADDITION_HPP_INCLUDED diff --git a/include/bitlib/bit-algorithms/bit_algorithm.hpp b/include/bitlib/bit-algorithms/bit_algorithm.hpp index 51cc6615..390f1154 100644 --- a/include/bitlib/bit-algorithms/bit_algorithm.hpp +++ b/include/bitlib/bit-algorithms/bit_algorithm.hpp @@ -14,31 +14,30 @@ #define _Bit_Algorithm_VERSION_MINOR @Bit_Algorithm_VERSION_MINOR@ // ========================================================================== // - - // ================================ PREAMBLE ================================ // -#include "debug_utils.hpp" //TODO does this belong somewhere else? #include "bit_algorithm_details.hpp" // overloads +#include "addition.hpp" #include "bit_algorithm_details.hpp" -#include "copy_backward.hpp" #include "copy.hpp" +#include "copy_backward.hpp" #include "count.hpp" -#include "debug_utils.hpp" +#include "division.hpp" #include "equal.hpp" #include "fill.hpp" #include "find.hpp" #include "move.hpp" +#include "multiplication.hpp" #include "reverse.hpp" #include "rotate.hpp" #include "shift.hpp" #include "swap_ranges.hpp" +#include "to_from_string.hpp" #include "transform.hpp" +#include "transform_accumulate.hpp" #include "type_traits.hpp" // ========================================================================== // - - // ========================================================================== // #endif // _BIT_ALGORITHM_HPP_INCLUDED // ========================================================================== // diff --git a/include/bitlib/bit-algorithms/bit_algorithm_details.hpp b/include/bitlib/bit-algorithms/bit_algorithm_details.hpp index 984c499f..3895ecd8 100644 --- a/include/bitlib/bit-algorithms/bit_algorithm_details.hpp +++ b/include/bitlib/bit-algorithms/bit_algorithm_details.hpp @@ -4,6 +4,7 @@ // Description: A set of utilities to assist in writing algorithms // Contributor(s): Vincent Reverdy [2019] // Bryce Kille [2019] +// Peter McLean [2025] // License: BSD 3-Clause License // ========================================================================== // #ifndef _BIT_ALGORITHM_DETAILS_HPP_INCLUDED @@ -21,44 +22,42 @@ namespace bit { // ========================================================================== // - - // -------------------------- Iterator Algorithms --------------------------- // // Returns the number of increments needed to get to last from first. // May be negative if last comes before first (Only when input is RAI) template typename bit_iterator::difference_type - distance(bit_iterator first, - bit_iterator last -) -{ - _assert_range_viability(first, last); - using word_type = typename bit_iterator::word_type; - using size_type = typename bit_iterator::size_type; - constexpr size_type digits = binary_digits::value; - return std::distance(first.base(), last.base())*digits - + (last.position() - first.position()); +distance(bit_iterator first, + bit_iterator last) { + _assert_range_viability(first, last); + using word_type = typename bit_iterator::word_type; + using difference_type = typename bit_iterator::difference_type; + constexpr difference_type digits = binary_digits::value; + return std::distance(first.base(), last.base()) * digits + + static_cast(last.position()) - + static_cast(first.position()); } // Increments the iterator n times. (If n is negative, the iterator is decremented n times) -template -void advance(bit_iterator& first, Distance n) -{ - first += n; +template +void advance(bit_iterator& first, typename bit_iterator::difference_type n) { + first += n; +} + +template +void reverse(InputIt& it, const T& n) { + ::std::advance(it, -static_cast>(n)); } -template +template bit_iterator next( - bit_iterator bit_it, - typename bit_iterator::difference_type n = 1 -) { - return bit_it + n; + bit_iterator bit_it, + typename bit_iterator::difference_type n = 1) { + return bit_it + n; } // -------------------------------------------------------------------------- // - - // --------------------------- Utility Functions ---------------------------- // // Returns distance(first, last) <= n @@ -103,18 +102,18 @@ constexpr bool is_within( // Get next len bits beginning at start and store them in a word of type T template -T get_word(bit_iterator first, size_t len=binary_digits::value) -{ - using native_word_type = typename bit_iterator::word_type; - constexpr T digits = binary_digits::value; - assert(digits >= len); - T offset = digits - first.position(); - T ret_word = *first.base() >> first.position(); - - // We've already assigned enough bits - if (len <= offset) { - return ret_word; - } +T get_word(const bit_iterator& first, size_t len = binary_digits::value) { + using native_word_type = typename bit_iterator::word_type; + constexpr size_t digits = binary_digits::value; + assert(digits >= len); + using non_const_T = std::remove_cvref_t; + size_t offset = digits - first.position(); + non_const_T ret_word = lsr(*first.base(), first.position()); + + // We've already assigned enough bits + if (len <= offset) { + return ret_word; + } InputIt it = std::next(first.base()); len -= offset; @@ -141,155 +140,87 @@ T get_word(bit_iterator first, size_t len=binary_digits::value) return ret_word; } -// Get next len bits beginning at start and store them in a word of type T -// If we reach `last` before we get len bits, break and return the current word -// bits_read will store the number of bits that we read. -//template -//T get_word(bit_iterator first, bit_iterator last, - //T& bits_read, T len=binary_digits::value - //) -//{ - //using native_word_type = typename bit_iterator::word_type; - //constexpr T native_digits = binary_digits::value; - //constexpr T ret_digits = binary_digits::value; - //assert(ret_digits >= len); - //bits_read = native_digits - first.position(); - //T ret_word = *first.base() >> first.position(); - - //// TODO vincent mentioned that we should aim for only 1 return function - //// per function. However I'm not sure how that can be accomplished here - //// without suffering a minor performance loss - - //// We have reached the last iterator - //if (first.base() == last.base()) { - //bits_read -= (native_digits - last.position()); - //return ret_word; - //} - //// We've already assigned enough bits - //if (len <= bits_read) { - //return ret_word; - //} - - //InputIt it = std::next(first.base()); - //len -= bits_read; - //// Fill up ret_word starting at bit [bits_read] using it - //// TODO define a mask and use the _bitblend that takes in the extra mask - //while (len > native_digits && it != last.base()) { - //ret_word = _bitblend( - //ret_word, - //static_cast(static_cast(*it) << bits_read), - //bits_read, - //native_digits - //); - //++it; - //bits_read += native_digits; - //len -= native_digits; - //} - - //// Assign remaining len bits of last word - //if (it == last.base()) { - //bits_read -= (native_digits - last.position()); - //ret_word = _bitblend( - //ret_word, - //static_cast(static_cast(*it) << bits_read), - //bits_read, - //last.position() - //); - //} else { - //ret_word = _bitblend( - //ret_word, - //static_cast(static_cast(*it) << bits_read), - //bits_read, - //len - //); - //} - //return ret_word; -//} - +template +T get_masked_word(const bit_iterator& first, size_t len = binary_digits::value) { + return get_word(first, len) & _mask(len); +} // Writes len bits from src beginning at dstIt template void write_word(src_type src, bit_iterator dst_bit_it, - src_type len=binary_digits::value - ) -{ - using dst_type = typename bit_iterator::word_type; - constexpr dst_type dst_digits = binary_digits::value; - constexpr dst_type src_digits = binary_digits::value; - - if constexpr (dst_digits >= src_digits) { - if (dst_bit_it.position() == 0 && len == dst_digits) { - *dst_bit_it.base() = src; - } - else { - *dst_bit_it.base() = _bitblend( - *dst_bit_it.base(), - src << dst_bit_it.position(), - dst_bit_it.position(), - std::min( - dst_digits - dst_bit_it.position(), - len - ) - ); - if (len > dst_digits - dst_bit_it.position()) { - OutputIt overflow_dst = std::next(dst_bit_it.base()); - *overflow_dst = _bitblend( - *overflow_dst, - src >> (dst_digits - dst_bit_it.position()), - 0, - len - (dst_digits - dst_bit_it.position()) - ); - } - } + size_t len = binary_digits::value) { + using dst_type = typename bit_iterator::word_type; + using size_type = typename bit_iterator::size_type; + constexpr size_type dst_digits = binary_digits::value; + constexpr size_type src_digits = binary_digits::value; + + if constexpr (dst_digits >= src_digits) { + if (dst_bit_it.position() == 0 && len == dst_digits) { + *dst_bit_it.base() = src; } else { - OutputIt it = dst_bit_it.base(); - if (dst_bit_it.position() != 0) { - *it = _bitblend( - *it, - static_cast(src), - static_cast(-1) << dst_bit_it.position() - ); - len -= dst_digits - dst_bit_it.position(); - // TODO would it be faster to jsut shift src every time it is - // passed as an argument and keep track of how much we need to - // shift? - src >>= dst_digits - dst_bit_it.position(); - ++it; - } - while (len >= dst_digits) { - *it = static_cast(src); - src >>= dst_digits; - len -= dst_digits; - ++it; - } - if (len > 0 ) { - *it = _bitblend( - *it, - static_cast(src), - (1 << len) - 1 - ); - } + *dst_bit_it.base() = _bitblend( + *dst_bit_it.base(), + static_cast(src << dst_bit_it.position()), + dst_bit_it.position(), + std::min( + dst_digits - dst_bit_it.position(), + len)); + if (len > dst_digits - dst_bit_it.position()) { + OutputIt overflow_dst = std::next(dst_bit_it.base()); + *overflow_dst = _bitblend( + *overflow_dst, + lsr(src, (dst_digits - dst_bit_it.position())), + 0, + len - (dst_digits - dst_bit_it.position())); + } + } + } else { + OutputIt it = dst_bit_it.base(); + if (dst_bit_it.position() != 0) { + *it = _bitblend( + *it, + static_cast(src), + static_cast(-1) << dst_bit_it.position()); + len -= dst_digits - dst_bit_it.position(); + // TODO would it be faster to jsut shift src every time it is + // passed as an argument and keep track of how much we need to + // shift? + src = lsr(src, dst_digits - dst_bit_it.position()); + ++it; } - return; + while (len >= dst_digits) { + *it = static_cast(src); + src = lsr(src, dst_digits); + len -= dst_digits; + ++it; + } + if (len > 0) { + *it = _bitblend( + *it, + static_cast(src), + _mask(len)); + } + } + return; } - // Shifts the range [first, last) to the left by n, filling the empty // bits with 0 template RandomAccessIt word_shift_left(RandomAccessIt first, - RandomAccessIt last, - typename RandomAccessIt::difference_type n -) -{ - if (n <= 0) return last; - if (n >= distance(first, last)) return first; - RandomAccessIt mid = first + n; - auto ret = std::move(mid, last, first); - return ret; + RandomAccessIt last, + typename RandomAccessIt::difference_type n) { + if (n <= 0) { + return last; + } + if (n >= distance(first, last)) { + return first; + } + RandomAccessIt mid = first + n; + auto ret = std::move(mid, last, first); + return ret; } - // Shifts the range [first, right) to the left by n, filling the empty // bits with 0 // NOT OPTIMIZED. Will be replaced with std::shift eventually. @@ -366,62 +297,60 @@ WordType _shift_towards_msb(WordType word, std::size_t n) { * is undefined */ template +[[deprecated("Unused")]] typename bit_iterator::word_type _padded_read(bit_iterator first, - bit_iterator last, const bit::bit_value bv) { - - using word_type = typename bit_iterator::word_type; - - constexpr std::size_t num_digits = binary_digits::value; - const std::size_t first_position = first.position(); - const std::size_t last_position = last.position(); - const word_type read = *(first.base()); - constexpr word_type all_ones = _all_ones(); - - word_type mask; - - if (_is_aligned_lsb(first)) { - if (_in_same_word(first, last)) { - // Case 1 - if (bv == bit0) { - mask = _shift_towards_lsb(all_ones, num_digits - last_position); - return read & mask; - } else { - mask = _shift_towards_msb(all_ones, last_position); - return read | mask; - } - } else { - // Case 0 - return read; - } + bit_iterator last, const bit::bit_value bv) { + using word_type = typename bit_iterator::word_type; + + constexpr std::size_t num_digits = binary_digits::value; + const std::size_t first_position = first.position(); + const std::size_t last_position = last.position(); + const word_type read = *(first.base()); + constexpr word_type all_ones = _all_ones(); + + word_type mask; + + if (_is_aligned_lsb(first)) { + if (_in_same_word(first, last)) { + // Case 1 + if (bv == bit0) { + mask = _shift_towards_lsb(all_ones, num_digits - last_position); + return read & mask; + } else { + mask = _shift_towards_msb(all_ones, last_position); + return read | mask; + } } else { - if (!_in_same_word(first, last)) { - // Case 2 - if (bv == bit0) { - mask = _shift_towards_msb(all_ones, first_position); - return read & mask; - } else { - mask = _shift_towards_lsb(all_ones, num_digits - first_position); - return read | mask; - } - } else { - // Case 3 - if (bv == bit0) { - mask = _shift_towards_msb(all_ones, first_position); - mask &= _shift_towards_lsb(all_ones, num_digits - last_position); - return read & mask; - } else { - mask = _shift_towards_lsb(all_ones, num_digits - first_position); - mask |= _shift_towards_msb(all_ones, last_position); - return read | mask; - } - } + // Case 0 + return read; } + } else { + if (!_in_same_word(first, last)) { + // Case 2 + if (bv == bit0) { + mask = _shift_towards_msb(all_ones, first_position); + return read & mask; + } else { + mask = _shift_towards_lsb(all_ones, num_digits - first_position); + return read | mask; + } + } else { + // Case 3 + if (bv == bit0) { + mask = _shift_towards_msb(all_ones, first_position); + mask &= _shift_towards_lsb(all_ones, num_digits - last_position); + return read & mask; + } else { + mask = _shift_towards_lsb(all_ones, num_digits - first_position); + mask |= _shift_towards_msb(all_ones, last_position); + return read | mask; + } + } + } } // -------------------------------------------------------------------------- // - - // ========================================================================== // -} // namespace bit -#endif // _BIT_ALGORITHM_DETAILS_HPP_INCLUDED +} // namespace bit +#endif // _BIT_ALGORITHM_DETAILS_HPP_INCLUDED // ========================================================================== // diff --git a/include/bitlib/bit-algorithms/copy.hpp b/include/bitlib/bit-algorithms/copy.hpp index 2d507516..2cfe8230 100644 --- a/include/bitlib/bit-algorithms/copy.hpp +++ b/include/bitlib/bit-algorithms/copy.hpp @@ -16,35 +16,62 @@ #include #include // Project sources -#include "bitlib/bitlib.hpp" +#include "bitlib/bit-iterator/bit.hpp" // Third-party libraries // Miscellaneous namespace bit { // ========================================================================== // +struct copy_impl; +template +constexpr bit_iterator copy( + const bit_iterator& first, + const bit_iterator& last, + const It2& d_first) { + return with_bit_iterator_adapter(first, last, d_first); +} -// ---------------------------- Copy Algorithms ----------------------------- // +template +constexpr bit_iterator copy( + const It1& first, + const It1& last, + const bit_iterator& d_first) { + return with_bit_iterator_adapter(first, last, d_first); +} -// Status: Does not work for Input/Output iterators due to distance call -template -constexpr bit_iterator copy(bit_iterator first, - bit_iterator last, - bit_iterator d_first -) -{ +template +constexpr bit_iterator copy( + const bit_iterator& first, + const bit_iterator& last, + const bit_iterator& d_first) { + return with_bit_iterator_adapter(first, last, d_first); +} + +// ---------------------------- Copy Algorithms ----------------------------- // +struct copy_impl { + // Status: Does not work for Input/Output iterators due to distance call + template + constexpr bit_iterator operator()( + bit_iterator first, + bit_iterator last, + bit_iterator d_first) { // Types and constants using dst_word_type = typename bit_iterator::word_type; using src_word_type = typename bit_iterator::word_type; + + // This checks for differing word types and uses an unoptimized copy in that event + static_assert(std::is_same::value, "Both types must be the same"); + using word_type = dst_word_type; using size_type = typename bit_iterator::size_type; constexpr size_type digits = binary_digits::value; // Assertions _assert_range_viability(first, last); - static_assert(::std::is_same::value, "Underlying word types must be equal"); - if (first == last) return d_first; - + if (first == last) { + return d_first; + } // Initialization const bool is_d_first_aligned = d_first.position() == 0; @@ -52,25 +79,21 @@ constexpr bit_iterator copy(bit_iterator first size_type remaining_bits_to_copy = total_bits_to_copy; auto it = d_first.base(); - // d_first is not aligned. Copy partial word to align it if (!is_d_first_aligned) { - size_type partial_bits_to_copy = ::std::min( - remaining_bits_to_copy, - digits - d_first.position() - ); - *it = _bitblend( - *it, - static_cast( - get_word(first, partial_bits_to_copy) - << static_cast(d_first.position()) - ), - static_cast(d_first.position()), - static_cast(partial_bits_to_copy) - ); - remaining_bits_to_copy -= partial_bits_to_copy; - advance(first, partial_bits_to_copy); - it++; + size_type partial_bits_to_copy = ::std::min( + remaining_bits_to_copy, + digits - d_first.position()); + *it = _bitblend( + *it, + static_cast( + get_word(first, partial_bits_to_copy) + << static_cast(d_first.position())), + d_first.position(), + partial_bits_to_copy); + remaining_bits_to_copy -= partial_bits_to_copy; + advance(first, partial_bits_to_copy); + it++; } if (remaining_bits_to_copy > 0) { @@ -92,21 +115,20 @@ constexpr bit_iterator copy(bit_iterator first } } if (remaining_bits_to_copy > 0) { - *it = _bitblend( - *it, - get_word(first, remaining_bits_to_copy), - static_cast( - (static_cast(1) << remaining_bits_to_copy) - 1) - ); + *it = _bitblend( + *it, + get_word(first, remaining_bits_to_copy), + _mask(remaining_bits_to_copy)); } } return d_first + total_bits_to_copy; -} + } +}; // -------------------------------------------------------------------------- // // ========================================================================== // -} // namespace bit +} // namespace bit #endif // _COPY_HPP_INCLUDED -// ========================================================================== // +// ========================================================================== // \ No newline at end of file diff --git a/include/bitlib/bit-algorithms/copy_backward.hpp b/include/bitlib/bit-algorithms/copy_backward.hpp index 55bd6f69..cd463b67 100644 --- a/include/bitlib/bit-algorithms/copy_backward.hpp +++ b/include/bitlib/bit-algorithms/copy_backward.hpp @@ -16,7 +16,7 @@ #include #include // Project sources -#include "bitlib/bitlib.hpp" +#include "bitlib/bit-iterator/bit.hpp" // Third-party libraries // Miscellaneous namespace bit { @@ -56,54 +56,49 @@ constexpr bit_iterator copy_backward(bit_iterator( - *it, - static_cast( - get_word(last - partial_bits_to_copy, partial_bits_to_copy) - ) << (d_last.position() - partial_bits_to_copy), - d_last.position() - partial_bits_to_copy, - static_cast(partial_bits_to_copy) - ); + *it, + static_cast( + get_word(last - partial_bits_to_copy, partial_bits_to_copy) + << (d_last.position() - partial_bits_to_copy)), + d_last.position() - partial_bits_to_copy, + partial_bits_to_copy); remaining_bits_to_copy -= partial_bits_to_copy; - advance(last, -partial_bits_to_copy); + reverse(last, partial_bits_to_copy); } if (remaining_bits_to_copy > 0) { - const bool is_last_aligned = last.position() == 0; - //size_type words_to_copy = ::std::ceil(remaining_bits_to_copy / static_cast(digits)); - // d_last will be aligned at this point - if (is_last_aligned && remaining_bits_to_copy > digits) { - auto N = ::std::distance(first.base(), last.base()) - 1; - it = ::std::copy_backward(first.base() + 1, last.base(), it); - last -= digits * N; - remaining_bits_to_copy -= digits * N; - it--; - } else { - // TODO benchmark if its faster to ::std::copy the entire range then shift - while (remaining_bits_to_copy >= digits) { - *(--it) = get_word(last - digits, digits); - remaining_bits_to_copy -= digits; - advance(last, -digits); - } - it--; - } - if (remaining_bits_to_copy > 0) { - *it = _bitblend( - *it, - get_word(last - remaining_bits_to_copy, remaining_bits_to_copy) - << (digits - remaining_bits_to_copy), - digits - remaining_bits_to_copy, - remaining_bits_to_copy - ); - remaining_bits_to_copy = 0; + const bool is_last_aligned = last.position() == 0; + //size_type words_to_copy = ::std::ceil(remaining_bits_to_copy / static_cast(digits)); + // d_last will be aligned at this point + if (is_last_aligned && remaining_bits_to_copy >= digits) { + auto N = ::std::distance(first.base(), last.base()) - 1; + it = ::std::copy_backward(first.base() + 1, last.base(), it); + last -= digits * N; + remaining_bits_to_copy -= digits * N; + } else { + // TODO benchmark if its faster to ::std::copy the entire range then shift + while (remaining_bits_to_copy >= digits) { + *(--it) = get_word(last - digits, digits); + remaining_bits_to_copy -= digits; + reverse(last, digits); } + } + if (remaining_bits_to_copy > 0) { + it--; + *it = _bitblend( + *it, + static_cast(get_word(last - remaining_bits_to_copy, remaining_bits_to_copy) + << (digits - remaining_bits_to_copy)), + digits - remaining_bits_to_copy, + remaining_bits_to_copy); + remaining_bits_to_copy = 0; + } } return d_last - total_bits_to_copy; } - - // ========================================================================== // -} // namespace bit +} // namespace bit -#endif // _COPY_BACKWARD_HPP_INCLUDED +#endif // _COPY_BACKWARD_HPP_INCLUDED // ========================================================================== // diff --git a/include/bitlib/bit-algorithms/count.hpp b/include/bitlib/bit-algorithms/count.hpp index 1a804b17..31db939f 100644 --- a/include/bitlib/bit-algorithms/count.hpp +++ b/include/bitlib/bit-algorithms/count.hpp @@ -12,8 +12,9 @@ #include #include // Project sources +#include "bitlib/bit-algorithms/accumulate.hpp" +#include "bitlib/bit-algorithms/libpopcnt.h" #include "bitlib/bit-iterator/bit.hpp" -#include "bitlib/bit-algorithms//libpopcnt.h" // Third-party libraries #ifdef BITLIB_HWY #include "hwy/highway.h" @@ -44,6 +45,7 @@ count( // Types and constants using word_type = typename bit_iterator::word_type; using difference_type = typename bit_iterator::difference_type; + using iterator_type = typename bit_iterator::iterator_type; constexpr difference_type digits = binary_digits::value; // Initialization @@ -51,13 +53,13 @@ count( // Computation when bits belong to several underlying words if (first.base() != last.base()) { - RandomAccessIt it = first.base(); + iterator_type it = first.base(); - if (first.position() != 0) { - word_type first_value = *first.base() >> first.position(); - result = _popcnt(first_value); - ++it; - } + if (first.position() != 0) { + word_type first_value = lsr(*first.base(), first.position()); + result = std::popcount(static_cast>(first_value)); + ++it; + } // The SIMD implementation here is actually slower than the standard //#ifdef BITLIB_HWY //// ReduceSum not implemented for unsigned char @@ -65,46 +67,50 @@ count( //{ //// Align to boundary //for (; it != last.base() && !is_aligned(&(*it), 64); ++it) { - //result += _popcnt(*it); - //} - - //// SIMD - //hn::ScalableTag d; - //for (; std::distance(it, last.base()) >= hn::Lanes(d); it += hn::Lanes(d)) - //{ - //const auto popcntV = hn::PopulationCount(hn::Load(d, &*it)); - //result += hn::ReduceSum(d, popcntV); - //} - - //// Remaining - //for (; it != last.base(); ++it) { - //result += _popcnt(*it); - //} - //} else -//#endif - { - // std:: version - //result += std::transform_reduce( - //it, - //last.base(), - //0, - //std::plus{}, - //[](word_type word) {return _popcnt(word); } - //); - - // libpopcnt - result += popcnt(&*it, (digits / 8) * std::distance(it, last.base())); - } - if (last.position() != 0) { - word_type last_value = *last.base() << (digits - last.position()); - result += _popcnt(last_value); + //result += std::popcount(*it); + //} + + //// SIMD + //hn::ScalableTag d; + //for (; std::distance(it, last.base()) >= hn::Lanes(d); it += hn::Lanes(d)) + //{ + //const auto popcntV = hn::PopulationCount(hn::Load(d, &*it)); + //result += hn::ReduceSum(d, popcntV); + //} + + //// Remaining + //for (; it != last.base(); ++it) { + //result += std::popcount(*it); + //} + //} else + //#endif + { + // std:: version + //result += std::transform_reduce( + //it, + //last.base(), + //0, + //std::plus{}, + //[](word_type word) {return std::popcount(word); } + //); + + // libpopcnt + auto words = (digits / 8) * std::distance(it, last.base()); + if (0 != words) { + result += popcnt(&*it, words); } - // Computation when bits belong to the same underlying word + } + if (last.position() != 0) { + word_type last_value = static_cast(*last.base() << (digits - last.position())); + result += std::popcount(static_cast>(last_value)); + } + // Computation when bits belong to the same underlying word } else { - result = _popcnt( - _bextr(*first.base(), first.position(), last.position() - - first.position()) - ); + result = std::popcount(static_cast>( + _bextr( + *first.base(), + first.position(), + last.position() - first.position()))); } // Negates when the number of zero bits is requested @@ -116,6 +122,24 @@ count( return result; } +template +constexpr int count_msb( + bit_iterator first, + bit_iterator last, + bit_value value) { + if (value) { + return bit::accumulate_backward_while( + first, last, 0, + [](int acc, auto word) { return std::make_pair((word == 0), acc + std::countl_one(word)); }, + [](int acc, auto word, auto bits) { return std::make_pair((word == 0), acc + std::countl_one(word) - (bit::bitsof(word) - bits)); }); + } else { + return bit::accumulate_backward_while( + first, last, 0, + [](int acc, auto word) { return std::make_pair((word == 0), acc + std::countl_zero(word)); }, + [](int acc, auto word, auto bits) { return std::make_pair((word == 0), acc + std::countl_zero(word) - (bit::bitsof(word) - bits)); }); + } +} + } // namespace bit #ifdef BITLIB_HWY HWY_AFTER_NAMESPACE(); diff --git a/include/bitlib/bit-algorithms/debug_utils.hpp b/include/bitlib/bit-algorithms/debug_utils.hpp deleted file mode 100644 index 74e590d1..00000000 --- a/include/bitlib/bit-algorithms/debug_utils.hpp +++ /dev/null @@ -1,40 +0,0 @@ -// =========================== DEBUG_UTILS ============================== // -// Project: The Experimental Bit Algorithms Library -// Name: debug_utils.hpp -// Description: Utilities useful for debugging -// Contributor: Bryce Kille [2019] -// License: BSD 3-Clause License -// ========================================================================== // -#ifndef _DEBUG_HPP_INCLUDED -#define _DEBUG_HPP_INCLUDED -// ========================================================================== // - - - -// ================================ PREAMBLE ================================ // -// C++ standard library -#include -// Project sources -// Third-party libraries -// Miscellaneous -namespace bit { -// ========================================================================== // - - - -// --------------------------- Utility Functions ---------------------------- // -//template -//std::string word_to_vec(T1 word) { - //std::bitset::value> word_c(word); - //std::string out = word_c.to_string(); - //std::reverse(out.begin(), out.end()); - //return out; -//} -// -------------------------------------------------------------------------- // - - - -// ========================================================================== // -} // namespace bit -#endif // _COPY_HPP_INCLUDED -// ========================================================================== // diff --git a/include/bitlib/bit-algorithms/division.hpp b/include/bitlib/bit-algorithms/division.hpp new file mode 100644 index 00000000..d7cfa28b --- /dev/null +++ b/include/bitlib/bit-algorithms/division.hpp @@ -0,0 +1,62 @@ +// ================================== MOVE ================================== // +// Project: The Experimental Bit Algorithms Library +// Name: division.hpp +// Description: bit range division +// Contributor(s): +// License: BSD 3-Clause License +// ========================================================================== // +#ifndef _DIVISION_HPP_INCLUDED +#define _DIVISION_HPP_INCLUDED +// ========================================================================== // + +// ============================== PREAMBLE ================================== // +// C++ standard library +#include +#include +// Project sources +#include "bitlib/bit-algorithms/transform.hpp" +#include "bitlib/bit-containers/bit_bitsof.hpp" +#include "bitlib/bit-iterator/bit_iterator.hpp" + +namespace bit { + +// 'Schoolbook' division by scalar +template + requires( + (std::is_same_v>, + std::remove_cvref_t>>) && + (is_static_castable_v::word_type>) && + (bitsof() <= bitsof::word_type>())) +constexpr typename bit_iterator::word_type division( + const bit_iterator& first, + const bit_iterator& last, + const bit_iterator& d_first, + const U& integral_operand) { + using word_type = typename bit_iterator::word_type; + auto long_div = [integral_operand]( + const word_type& remainder, + const word_type& word, + size_t bits = bitsof()) { + static_cast(bits); // Suppress unused variable warning + word_type next_remainder; + word_type result_word = _divx(remainder, word, static_cast(integral_operand), &next_remainder); + return std::make_pair(result_word, next_remainder); + }; + word_type remainder = 0; + return transform_accumulate_backward(first, last, d_first, d_first + distance(first, last), remainder, long_div, long_div); +} + +template + requires( + (is_static_castable_v::word_type>) && + (bitsof() <= bitsof::word_type>())) +constexpr typename bit_iterator::word_type division( + const bit_iterator& first, + const bit_iterator& last, + const U& integral_operand) { + return division(first, last, first, integral_operand); +} + +} // namespace bit + +#endif // _DIVISION_HPP_INCLUDED diff --git a/include/bitlib/bit-algorithms/equal.hpp b/include/bitlib/bit-algorithms/equal.hpp index e3fb1449..0b3c0e9f 100644 --- a/include/bitlib/bit-algorithms/equal.hpp +++ b/include/bitlib/bit-algorithms/equal.hpp @@ -2,48 +2,56 @@ // Project: The Experimental Bit Algorithms Library // Name: equal.hpp // Contributor: Bryce Kille [2019] +// Peter McLean [2025] // License: BSD 3-Clause License // ========================================================================== // #ifndef _EQUAL_HPP_INCLUDED #define _EQUAL_HPP_INCLUDED // ========================================================================== // - - // ================================ PREAMBLE ================================ // // C++ standard library #include #include // Project sources -#include "bitlib/bitlib.hpp" +#include "bitlib/bit-iterator/bit.hpp" // Third-party libraries // Miscellaneous namespace bit { // ========================================================================== // +struct equal_impl; - +// Status: Does not work for Input/Output iterators due to distance call +template +constexpr bool equal( + const bit_iterator& first, + const bit_iterator& last, + const bit_iterator& d_first) { + return with_bit_iterator_adapter(first, last, d_first); +} // ---------------------------- Equal Algorithms ----------------------------- // // Status: Does not work for Input/Output iterators due to distance call -template -constexpr bool equal( - bit_iterator first, - bit_iterator last, - bit_iterator d_first -) -{ +struct equal_impl { + template + constexpr bool operator()( + bit_iterator first, + bit_iterator last, + bit_iterator d_first) { // Types and constants using dst_word_type = typename bit_iterator::word_type; using src_word_type = typename bit_iterator::word_type; + static_assert(::std::is_same::value, "Underlying word types must be equal"); using word_type = dst_word_type; using size_type = typename bit_iterator::size_type; constexpr size_type digits = binary_digits::value; // Assertions _assert_range_viability(first, last); - static_assert(::std::is_same::value, "Underlying word types must be equal"); - if (first == last) return true; + if (first == last) { + return true; + } // Initialization const bool is_d_first_aligned = d_first.position() == 0; @@ -53,50 +61,55 @@ constexpr bool equal( // d_first is not aligned. if (!is_d_first_aligned) { - const size_type partial_bits_to_check = ::std::min( - remaining_bits_to_check, - digits - d_first.position()); - const word_type mask = static_cast( - (static_cast(1) << partial_bits_to_check) - 1 - ) << d_first.position(); - const word_type comp = static_cast( - get_word(first, partial_bits_to_check) - << d_first.position()); - if ((mask & *it) != (mask & comp)) { return false; } - remaining_bits_to_check -= partial_bits_to_check; - advance(first, partial_bits_to_check); - it++; + const size_type partial_bits_to_check = ::std::min( + remaining_bits_to_check, + digits - d_first.position()); + const word_type mask = _mask(partial_bits_to_check, d_first.position()); + const word_type comp = static_cast( + get_word(first, partial_bits_to_check) + << d_first.position()); + if ((mask & *it) != (mask & comp)) { + return false; + } + remaining_bits_to_check -= partial_bits_to_check; + advance(first, partial_bits_to_check); + it++; } if (remaining_bits_to_check > 0) { - const bool is_first_aligned = first.position() == 0; - // d_first will be aligned at this point - if (is_first_aligned && remaining_bits_to_check >= digits) { - auto N = ::std::distance(first.base(), last.base()); - bool found_mismatch = !::std::equal(first.base(), last.base(), it); - if (found_mismatch) {return false;} - it += N; - first += digits * N; - remaining_bits_to_check -= digits * N; - } else { - // TODO benchmark if its faster to ::std::check the entire range then shift - while (remaining_bits_to_check >= digits) { - if (*it != get_word(first, digits)) {return false;} - remaining_bits_to_check -= digits; - it++; - advance(first, digits); - } + const bool is_first_aligned = first.position() == 0; + // d_first will be aligned at this point + if (is_first_aligned && remaining_bits_to_check >= digits) { + auto N = ::std::distance(first.base(), last.base()); + bool found_mismatch = !::std::equal(first.base(), last.base(), it); + if (found_mismatch) { + return false; + } + it += N; + first += digits * N; + remaining_bits_to_check -= digits * N; + } else { + // TODO benchmark if its faster to ::std::check the entire range then shift + while (remaining_bits_to_check >= digits) { + if (*it != get_word(first, digits)) { + return false; + } + remaining_bits_to_check -= digits; + it++; + advance(first, digits); } - if (remaining_bits_to_check > 0) { - const word_type mask = static_cast( - (static_cast(1) << remaining_bits_to_check) - 1 - ); - const word_type comp = get_word(first, remaining_bits_to_check); - if ((mask & *it) != (mask & comp)) { return false; } + } + if (remaining_bits_to_check > 0) { + const word_type mask = _mask(remaining_bits_to_check); + const word_type comp = get_word(first, remaining_bits_to_check); + if ((mask & *it) != (mask & comp)) { + return false; } + } } return true; -} + } +}; // -------------------------------------------------------------------------- // diff --git a/include/bitlib/bit-algorithms/find.hpp b/include/bitlib/bit-algorithms/find.hpp index b35583c7..95b2c2be 100644 --- a/include/bitlib/bit-algorithms/find.hpp +++ b/include/bitlib/bit-algorithms/find.hpp @@ -9,6 +9,7 @@ // ============================== PREAMBLE ================================== // // C++ standard library +#include #include // Project sources #include "bitlib/bit-iterator/bit.hpp" @@ -37,6 +38,7 @@ constexpr bit_iterator find( ) { using word_type = typename bit_iterator::word_type; + using uword_type = std::make_unsigned_t; using size_type = typename bit_iterator::size_type; const std::size_t digits = binary_digits::value; @@ -46,17 +48,17 @@ constexpr bit_iterator find( if (!is_first_aligned) { - word_type shifted_first = *first.base() >> first.position(); - size_type num_trailing_complementary_bits = (bv == bit0) - ? _tzcnt(static_cast(~shifted_first)) - : _tzcnt(static_cast(shifted_first)); - if (std::next(first.base(), is_last_aligned) == last.base()) { - return first + std::min(num_trailing_complementary_bits, (size_type) distance(first, last)); - } else if (num_trailing_complementary_bits + first.position() < digits) { - return first + num_trailing_complementary_bits; - } else { - first += digits - first.position(); - } + word_type shifted_first = lsr(*first.base(), first.position()); + size_type num_trailing_complementary_bits = (bv == bit0) + ? std::countr_zero(static_cast(~shifted_first)) + : std::countr_zero(static_cast(shifted_first)); + if (std::next(first.base(), is_last_aligned) == last.base()) { + return first + std::min(num_trailing_complementary_bits, static_cast(distance(first, last))); + } else if (num_trailing_complementary_bits + first.position() < digits) { + return first + num_trailing_complementary_bits; + } else { + first += digits - first.position(); + } } // Initialization @@ -72,8 +74,8 @@ constexpr bit_iterator find( } size_type num_trailing_complementary_bits = (bv == bit0) - ? _tzcnt(static_cast(~*it)) - : _tzcnt(static_cast(*it)); + ? std::countr_zero(static_cast(~*it)) + : std::countr_zero(static_cast(*it)); return bit_iterator(it, (size_type) num_trailing_complementary_bits); } @@ -92,8 +94,8 @@ constexpr bit_iterator find( { it += hn::FindKnownFirstTrue(d, found); size_type num_trailing_complementary_bits = (bv == bit0) - ? _tzcnt(static_cast(~*it)) - : _tzcnt(static_cast(*it)); + ? std::countr_zero(static_cast(~*it)) + : std::countr_zero(static_cast(*it)); return bit_iterator(it, (size_type) num_trailing_complementary_bits); } } @@ -106,18 +108,18 @@ constexpr bit_iterator find( } if (it != last.base()) { - size_type num_trailing_complementary_bits = (bv == bit0) - ? _tzcnt(static_cast(~*it)) - : _tzcnt(static_cast(*it)); - return bit_iterator(it, (size_type) num_trailing_complementary_bits); + size_type num_trailing_complementary_bits = (bv == bit0) + ? std::countr_zero(static_cast(~*it)) + : std::countr_zero(static_cast(*it)); + return bit_iterator(it, static_cast(num_trailing_complementary_bits)); } // Deal with any unaligned boundaries if (!is_last_aligned) { - size_type num_trailing_complementary_bits = (bv == bit0) - ? _tzcnt(static_cast(~*it)) - : _tzcnt(static_cast(*it)); - return bit_iterator(it, (size_type) std::min(num_trailing_complementary_bits, last.position())); + size_type num_trailing_complementary_bits = (bv == bit0) + ? std::countr_zero(static_cast(~*it)) + : std::countr_zero(static_cast(*it)); + return bit_iterator(it, static_cast(std::min(num_trailing_complementary_bits, last.position()))); } return last; } diff --git a/include/bitlib/bit-algorithms/libpopcnt.h b/include/bitlib/bit-algorithms/libpopcnt.h index ffcd976b..db317b62 100644 --- a/include/bitlib/bit-algorithms/libpopcnt.h +++ b/include/bitlib/bit-algorithms/libpopcnt.h @@ -204,15 +204,14 @@ static inline uint64_t popcnt64(uint64_t x) static inline uint64_t popcnt64(uint64_t x) { - return _mm_popcnt_u32((uint32_t) x) + + return _mm_popcnt_u32((uint32_t)x) + _mm_popcnt_u32((uint32_t)(x >> 32)); } /* non x86 CPUs */ #elif defined(HAVE_BUILTIN_POPCOUNT) -static inline uint64_t popcnt64(uint64_t x) -{ +static inline uint64_t popcnt64(uint64_t x) { return __builtin_popcountll(x); } @@ -220,8 +219,7 @@ static inline uint64_t popcnt64(uint64_t x) * use pure integer algorithm */ #else -static inline uint64_t popcnt64(uint64_t x) -{ +static inline uint64_t popcnt64(uint64_t x) { return popcount64(x); } @@ -230,15 +228,15 @@ static inline uint64_t popcnt64(uint64_t x) #if defined(HAVE_CPUID) #if defined(_MSC_VER) - #include - #include +#include +#include #endif /* %ecx bit flags */ #define bit_POPCNT (1 << 23) /* %ebx bit flags */ -#define bit_AVX2 (1 << 5) +#define bit_AVX2 (1 << 5) #define bit_AVX512 (1 << 30) /* xgetbv bit flags */ @@ -246,31 +244,31 @@ static inline uint64_t popcnt64(uint64_t x) #define XSTATE_YMM (1 << 2) #define XSTATE_ZMM (7 << 5) -static inline void run_cpuid(int eax, int ecx, int* abcd) -{ +static inline void run_cpuid(int eax, int ecx, int* abcd) { #if defined(_MSC_VER) __cpuidex(abcd, eax, ecx); #else int ebx = 0; int edx = 0; - #if defined(__i386__) && \ - defined(__PIC__) - /* in case of PIC under 32-bit EBX cannot be clobbered */ - __asm__ ("movl %%ebx, %%edi;" - "cpuid;" - "xchgl %%ebx, %%edi;" - : "=D" (ebx), - "+a" (eax), - "+c" (ecx), - "=d" (edx)); - #else - __asm__ ("cpuid;" - : "+b" (ebx), - "+a" (eax), - "+c" (ecx), - "=d" (edx)); - #endif +#if defined(__i386__) && \ + defined(__PIC__) + /* in case of PIC under 32-bit EBX cannot be clobbered */ + __asm__( + "movl %%ebx, %%edi;" + "cpuid;" + "xchgl %%ebx, %%edi;" + : "=D"(ebx), + "+a"(eax), + "+c"(ecx), + "=d"(edx)); +#else + __asm__("cpuid;" + : "+b"(ebx), + "+a"(eax), + "+c"(ecx), + "=d"(edx)); +#endif abcd[0] = eax; abcd[1] = ebx; @@ -282,14 +280,13 @@ static inline void run_cpuid(int eax, int ecx, int* abcd) #if defined(HAVE_AVX2) || \ defined(HAVE_AVX512) -static inline int get_xcr0() -{ +static inline int get_xcr0() { int xcr0; #if defined(_MSC_VER) - xcr0 = (int) _xgetbv(0); + xcr0 = (int)_xgetbv(0); #else - __asm__ ("xgetbv" : "=a" (xcr0) : "c" (0) : "%edx" ); + __asm__("xgetbv" : "=a"(xcr0) : "c"(0) : "%edx"); #endif return xcr0; @@ -297,15 +294,15 @@ static inline int get_xcr0() #endif -static inline int get_cpuid() -{ +static inline int get_cpuid() { int flags = 0; int abcd[4]; run_cpuid(1, 0, abcd); - if ((abcd[2] & bit_POPCNT) == bit_POPCNT) + if ((abcd[2] & bit_POPCNT) == bit_POPCNT) { flags |= bit_POPCNT; + } #if defined(HAVE_AVX2) || \ defined(HAVE_AVX512) @@ -313,25 +310,26 @@ static inline int get_cpuid() int osxsave_mask = (1 << 27); /* ensure OS supports extended processor state management */ - if ((abcd[2] & osxsave_mask) != osxsave_mask) + if ((abcd[2] & osxsave_mask) != osxsave_mask) { return 0; + } int ymm_mask = XSTATE_SSE | XSTATE_YMM; int zmm_mask = XSTATE_SSE | XSTATE_YMM | XSTATE_ZMM; int xcr0 = get_xcr0(); - if ((xcr0 & ymm_mask) == ymm_mask) - { + if ((xcr0 & ymm_mask) == ymm_mask) { run_cpuid(7, 0, abcd); - if ((abcd[1] & bit_AVX2) == bit_AVX2) + if ((abcd[1] & bit_AVX2) == bit_AVX2) { flags |= bit_AVX2; + } - if ((xcr0 & zmm_mask) == zmm_mask) - { - if ((abcd[1] & bit_AVX512) == bit_AVX512) + if ((xcr0 & zmm_mask) == zmm_mask) { + if ((abcd[1] & bit_AVX512) == bit_AVX512) { flags |= bit_AVX512; + } } } @@ -347,33 +345,31 @@ static inline int get_cpuid() #include #if !defined(_MSC_VER) - __attribute__ ((target ("avx2"))) +__attribute__((target("avx2"))) #endif -static inline void CSA256(__m256i* h, __m256i* l, __m256i a, __m256i b, __m256i c) -{ +static inline void +CSA256(__m256i* h, __m256i* l, __m256i a, __m256i b, __m256i c) { __m256i u = _mm256_xor_si256(a, b); *h = _mm256_or_si256(_mm256_and_si256(a, b), _mm256_and_si256(u, c)); *l = _mm256_xor_si256(u, c); } #if !defined(_MSC_VER) - __attribute__ ((target ("avx2"))) +__attribute__((target("avx2"))) #endif -static inline __m256i popcnt256(__m256i v) -{ +static inline __m256i +popcnt256(__m256i v) { __m256i lookup1 = _mm256_setr_epi8( 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, - 5, 6, 6, 7, 6, 7, 7, 8 - ); + 5, 6, 6, 7, 6, 7, 7, 8); __m256i lookup2 = _mm256_setr_epi8( 4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0, 4, 3, 3, 2, 3, 2, 2, 1, - 3, 2, 2, 1, 2, 1, 1, 0 - ); + 3, 2, 2, 1, 2, 1, 1, 0); __m256i low_mask = _mm256_set1_epi8(0x0f); __m256i lo = _mm256_and_si256(v, low_mask); @@ -392,10 +388,10 @@ static inline __m256i popcnt256(__m256i v) * @see https://arxiv.org/abs/1611.07612 */ #if !defined(_MSC_VER) - __attribute__ ((target ("avx2"))) +__attribute__((target("avx2"))) #endif -static inline uint64_t popcnt_avx2(const __m256i* ptr, uint64_t size) -{ +static inline uint64_t +popcnt_avx2(const __m256i* ptr, uint64_t size) { __m256i cnt = _mm256_setzero_si256(); __m256i ones = _mm256_setzero_si256(); __m256i twos = _mm256_setzero_si256(); @@ -408,8 +404,7 @@ static inline uint64_t popcnt_avx2(const __m256i* ptr, uint64_t size) uint64_t limit = size - size % 16; uint64_t* cnt64; - for(; i < limit; i += 16) - { + for (; i < limit; i += 16) { CSA256(&twosA, &ones, ones, _mm256_loadu_si256(ptr + i + 0), _mm256_loadu_si256(ptr + i + 1)); CSA256(&twosB, &ones, ones, _mm256_loadu_si256(ptr + i + 2), _mm256_loadu_si256(ptr + i + 3)); CSA256(&foursA, &twos, twos, twosA, twosB); @@ -435,10 +430,11 @@ static inline uint64_t popcnt_avx2(const __m256i* ptr, uint64_t size) cnt = _mm256_add_epi64(cnt, _mm256_slli_epi64(popcnt256(twos), 1)); cnt = _mm256_add_epi64(cnt, popcnt256(ones)); - for(; i < size; i++) + for (; i < size; i++) { cnt = _mm256_add_epi64(cnt, popcnt256(_mm256_loadu_si256(ptr + i))); + } - cnt64 = (uint64_t*) &cnt; + cnt64 = (uint64_t*)&cnt; return cnt64[0] + cnt64[1] + @@ -453,10 +449,10 @@ static inline uint64_t popcnt_avx2(const __m256i* ptr, uint64_t size) #include #if !defined(_MSC_VER) - __attribute__ ((target ("avx512bw"))) +__attribute__((target("avx512bw"))) #endif -static inline __m512i popcnt512(__m512i v) -{ +static inline __m512i +popcnt512(__m512i v) { __m512i m1 = _mm512_set1_epi8(0x55); __m512i m2 = _mm512_set1_epi8(0x33); __m512i m4 = _mm512_set1_epi8(0x0F); @@ -472,10 +468,10 @@ static inline __m512i popcnt512(__m512i v) } #if !defined(_MSC_VER) - __attribute__ ((target ("avx512bw"))) +__attribute__((target("avx512bw"))) #endif -static inline void CSA512(__m512i* h, __m512i* l, __m512i a, __m512i b, __m512i c) -{ +static inline void +CSA512(__m512i* h, __m512i* l, __m512i a, __m512i b, __m512i c) { *l = _mm512_ternarylogic_epi32(c, b, a, 0x96); *h = _mm512_ternarylogic_epi32(c, b, a, 0xe8); } @@ -488,10 +484,10 @@ static inline void CSA512(__m512i* h, __m512i* l, __m512i a, __m512i b, __m512i * @see https://arxiv.org/abs/1611.07612 */ #if !defined(_MSC_VER) - __attribute__ ((target ("avx512bw"))) +__attribute__((target("avx512bw"))) #endif -static inline uint64_t popcnt_avx512(const __m512i* ptr, const uint64_t size) -{ +static inline uint64_t +popcnt_avx512(const __m512i* ptr, const uint64_t size) { __m512i cnt = _mm512_setzero_si512(); __m512i ones = _mm512_setzero_si512(); __m512i twos = _mm512_setzero_si512(); @@ -504,8 +500,7 @@ static inline uint64_t popcnt_avx512(const __m512i* ptr, const uint64_t size) uint64_t limit = size - size % 16; uint64_t* cnt64; - for(; i < limit; i += 16) - { + for (; i < limit; i += 16) { CSA512(&twosA, &ones, ones, _mm512_loadu_si512(ptr + i + 0), _mm512_loadu_si512(ptr + i + 1)); CSA512(&twosB, &ones, ones, _mm512_loadu_si512(ptr + i + 2), _mm512_loadu_si512(ptr + i + 3)); CSA512(&foursA, &twos, twos, twosA, twosB); @@ -531,10 +526,11 @@ static inline uint64_t popcnt_avx512(const __m512i* ptr, const uint64_t size) cnt = _mm512_add_epi64(cnt, _mm512_slli_epi64(popcnt512(twos), 1)); cnt = _mm512_add_epi64(cnt, popcnt512(ones)); - for(; i < size; i++) + for (; i < size; i++) { cnt = _mm512_add_epi64(cnt, popcnt512(_mm512_loadu_si512(ptr + i))); + } - cnt64 = (uint64_t*) &cnt; + cnt64 = (uint64_t*)&cnt; return cnt64[0] + cnt64[1] + @@ -556,11 +552,10 @@ static inline uint64_t popcnt_avx512(const __m512i* ptr, const uint64_t size) * @data: An array * @size: Size of data in bytes */ -static inline uint64_t popcnt(const void* data, uint64_t size) -{ +static inline uint64_t popcnt(const void* data, uint64_t size) { uint64_t i = 0; uint64_t cnt = 0; - const uint8_t* ptr = (const uint8_t*) data; + const uint8_t* ptr = (const uint8_t*)data; /* * CPUID runtime checks are only enabled if this is needed. @@ -568,74 +563,83 @@ static inline uint64_t popcnt(const void* data, uint64_t size) * code using -march=native on a CPU with AVX512. */ #if defined(HAVE_CPUID) - #if defined(__cplusplus) - /* C++11 thread-safe singleton */ - static const int cpuid = get_cpuid(); - #else - static int cpuid_ = -1; - int cpuid = cpuid_; - if (cpuid == -1) - { - cpuid = get_cpuid(); +#if defined(__cplusplus) + /* C++11 thread-safe singleton */ + static const int cpuid = get_cpuid(); +#else + static int cpuid_ = -1; + int cpuid = cpuid_; + if (cpuid == -1) { + cpuid = get_cpuid(); - #if defined(_MSC_VER) - _InterlockedCompareExchange(&cpuid_, cpuid, -1); - #else - __sync_val_compare_and_swap(&cpuid_, -1, cpuid); - #endif - } - #endif +#if defined(_MSC_VER) + _InterlockedCompareExchange(&cpuid_, cpuid, -1); +#else + __sync_val_compare_and_swap(&cpuid_, -1, cpuid); +#endif + } +#endif #endif #if defined(HAVE_AVX512) - #if defined(__AVX512__) || defined(__AVX512BW__) - /* AVX512 requires arrays >= 1024 bytes */ - if (i + 1024 <= size) - #else - if ((cpuid & bit_AVX512) && - i + 1024 <= size) - #endif - { - const __m512i* ptr512 = (const __m512i*)(ptr + i); - cnt += popcnt_avx512(ptr512, (size - i) / 64); - i = size - size % 64; - } +#if defined(__AVX512__) || defined(__AVX512BW__) + /* AVX512 requires arrays >= 1024 bytes */ + if (i + 1024 <= size) +#else + if ((cpuid & bit_AVX512) && + i + 1024 <= size) +#endif + { + const __m512i* ptr512 = (const __m512i*)(ptr + i); + cnt += popcnt_avx512(ptr512, (size - i) / 64); + i = size - size % 64; + } #endif #if defined(HAVE_AVX2) - #if defined(__AVX2__) - /* AVX2 requires arrays >= 512 bytes */ - if (i + 512 <= size) - #else - if ((cpuid & bit_AVX2) && - i + 512 <= size) - #endif - { - const __m256i* ptr256 = (const __m256i*)(ptr + i); - cnt += popcnt_avx2(ptr256, (size - i) / 32); - i = size - size % 32; - } +#if defined(__AVX2__) + /* AVX2 requires arrays >= 512 bytes */ + if (i + 512 <= size) +#else + if ((cpuid & bit_AVX2) && + i + 512 <= size) +#endif + { + const __m256i* ptr256 = (const __m256i*)(ptr + i); + cnt += popcnt_avx2(ptr256, (size - i) / 32); + i = size - size % 32; + } #endif #if defined(HAVE_POPCNT) - /* +/* * The user has compiled without -mpopcnt. * Unfortunately the MSVC compiler does not have * a POPCNT macro so we cannot get rid of the * runtime check for MSVC. */ - #if !defined(__POPCNT__) - if (cpuid & bit_POPCNT) - #endif - { +#if !defined(__POPCNT__) + if (cpuid & bit_POPCNT) +#endif + { +#ifdef POPCNT_NO_UNALIGNED + // Address sanitizer will complain about unaligned accesses + // When we test with ASAN we enable aligned-only accesses + for (; ((uintptr_t)(ptr + i) % 8) && (i < size); i++) { + cnt += popcnt64(ptr[i]); + } +#endif + /* We use unaligned memory accesses here to improve performance */ - for (; i < size - size % 8; i += 8) + for (; (i + 8) <= size; i += 8) { cnt += popcnt64(*(const uint64_t*)(ptr + i)); - for (; i < size; i++) + } + for (; i < size; i++) { cnt += popcnt64(ptr[i]); + } - return cnt; - } + return cnt; + } #endif #if !defined(HAVE_POPCNT) || \ @@ -644,8 +648,18 @@ static inline uint64_t popcnt(const void* data, uint64_t size) * Pure integer popcount algorithm. * We use unaligned memory accesses here to improve performance. */ - for (; i < size - size % 8; i += 8) + +#ifdef POPCNT_NO_UNALIGNED + // Address sanitizer will complain about unaligned accesses + // When we test with ASAN we enable aligned-only accesses + for (; ((uintptr_t)(ptr + i) % 8) && (i < size); i++) { + cnt += popcount64(ptr[i]); + } +#endif + + for (; (i + 8) <= size; i += 8) { cnt += popcount64(*(const uint64_t*)(ptr + i)); + } if (i < size) { @@ -701,7 +715,7 @@ static inline uint64_t popcnt(const void* data, uint64_t size) * and 31 * 8 bits = 248 which is OK. */ uint64_t limit = (i + 31 < iters) ? i + 31 : iters; - + /* Each iteration processes 64 bytes */ for (; i < limit; i++) { diff --git a/include/bitlib/bit-algorithms/move.hpp b/include/bitlib/bit-algorithms/move.hpp index 401cbfdb..4b78ed20 100644 --- a/include/bitlib/bit-algorithms/move.hpp +++ b/include/bitlib/bit-algorithms/move.hpp @@ -14,92 +14,34 @@ #include #include // Project sources -#include "bitlib/bitlib.hpp" -// Third-party libraries -// Miscellaneous +#include "bitlib/bit-algorithms/copy.hpp" namespace bit { // ========================================================================== // -// Status: Does not work for Input/Output iterators due to distance call -template -constexpr bit_iterator move(bit_iterator first, - bit_iterator last, - bit_iterator d_first -) -{ - // Types and constants - using dst_word_type = typename bit_iterator::word_type; - using src_word_type = typename bit_iterator::word_type; - using word_type = dst_word_type; - using size_type = typename bit_iterator::size_type; - constexpr size_type digits = binary_digits::value; - - // Assertions - _assert_range_viability(first, last); - static_assert(std::is_same::value, "Underlying word types must be equal"); - if (first == last) return d_first; - - - // Initialization - const bool is_d_first_aligned = d_first.position() == 0; - size_type total_bits_to_move = distance(first, last); - size_type remaining_bits_to_move = total_bits_to_move; - auto it = d_first.base(); - - - // d_first is not aligned. Copy partial word to align it - if (!is_d_first_aligned) { - size_type partial_bits_to_move = std::min( - remaining_bits_to_move, - digits - d_first.position() - ); - *it = _bitblend( - *it, - static_cast( - get_word(first, partial_bits_to_move) - << static_cast(d_first.position()) - ), - static_cast(d_first.position()), - static_cast(partial_bits_to_move) - ); - remaining_bits_to_move -= partial_bits_to_move; - advance(first, partial_bits_to_move); - it++; - } - - if (remaining_bits_to_move > 0) { - const bool is_first_aligned = first.position() == 0; - //size_type words_to_move = std::ceil(remaining_bits_to_move / static_cast(digits)); - // d_first will be aligned at this point - if (is_first_aligned && remaining_bits_to_move > digits) { - auto N = std::distance(first.base(), last.base()); - it = std::move(first.base(), last.base(), it); - first += digits * N; - remaining_bits_to_move -= digits * N; - } else { - // TODO benchmark if its faster to std::move the entire range then shift - while (remaining_bits_to_move >= digits) { - *it = get_word(first, digits); - remaining_bits_to_move -= digits; - it++; - advance(first, digits); - } - } - if (remaining_bits_to_move > 0) { - *it = _bitblend( - *it, - get_word(first, remaining_bits_to_move), - static_cast( - (static_cast(1) << remaining_bits_to_move) - 1) - ); - } - } - return d_first + total_bits_to_move; +template +constexpr bit_iterator move( + const bit_iterator& first, + const bit_iterator& last, + const It2& d_first) { + return copy(first, last, d_first); } -// -------------------------------------------------------------------------- // +template +constexpr bit_iterator move( + const It1& first, + const It1& last, + const bit_iterator& d_first) { + return copy(first, last, d_first); +} +template +constexpr bit_iterator move( + const bit_iterator& first, + const bit_iterator& last, + const bit_iterator& d_first) { + return copy(first, last, d_first); +} // ========================================================================== // } // namespace bit diff --git a/include/bitlib/bit-algorithms/multiplication.hpp b/include/bitlib/bit-algorithms/multiplication.hpp new file mode 100644 index 00000000..20b246a1 --- /dev/null +++ b/include/bitlib/bit-algorithms/multiplication.hpp @@ -0,0 +1,58 @@ +// ================================== MOVE ================================== // +// Project: The Experimental Bit Algorithms Library +// Name: multiplication.hpp +// Description: bit range multiplication +// Contributor(s): +// License: BSD 3-Clause License +// ========================================================================== // +#ifndef _MULTIPLICATION_HPP_INCLUDED +#define _MULTIPLICATION_HPP_INCLUDED +// ========================================================================== // + +// ============================== PREAMBLE ================================== // +// C++ standard library +#include +#include +// Project sources +#include "bitlib/bit-algorithms/transform.hpp" +#include "bitlib/bit-containers/bit_bitsof.hpp" +#include "bitlib/bit-iterator/bit_iterator.hpp" + +namespace bit { + +template + requires( + (is_static_castable_v::word_type>) && + (bitsof() <= bitsof::word_type>())) +constexpr typename bit_iterator::word_type multiplication( + const bit_iterator& first, + const bit_iterator& last, + const bit_iterator& d_first, + const U& integral_operand) { + using word_type = typename bit_iterator::word_type; + word_type carry = 0; + transform(first, last, d_first, + [&carry, integral_operand](auto word, auto bits = bitsof()) -> word_type { + word_type result_word = (carry + _mulx(static_cast(integral_operand), word, &carry)); + if (bits < bitsof()) { + carry = static_cast((carry << (bitsof() - bits)) | lsr(result_word, bits)); + } + return result_word; + }); + return carry; +} + +template + requires( + (is_static_castable_v::word_type>) && + (bitsof() <= bitsof::word_type>())) +constexpr typename bit_iterator::word_type multiplication( + const bit_iterator& first, + const bit_iterator& last, + const U& integral_operand) { + return multiplication(first, last, first, integral_operand); +} + +} // namespace bit + +#endif // _MULTIPLICATION_HPP_INCLUDED diff --git a/include/bitlib/bit-algorithms/reverse.hpp b/include/bitlib/bit-algorithms/reverse.hpp index 37dacb7c..582b24c0 100644 --- a/include/bitlib/bit-algorithms/reverse.hpp +++ b/include/bitlib/bit-algorithms/reverse.hpp @@ -101,12 +101,11 @@ constexpr void reverse( } // Reverse when bit iterators belong to the same underlying word } else { - *it = _bitblend( - *it, - _bitswap(*it >> first.position()) >> gap, - first.position(), - last.position() - first.position() - ); + *it = _bitblend( + *it, + lsr(_bitswap(lsr(*it, first.position())), gap), + first.position(), + last.position() - first.position()); } } diff --git a/include/bitlib/bit-algorithms/rotate.hpp b/include/bitlib/bit-algorithms/rotate.hpp index f9509ba8..e4e81c3e 100644 --- a/include/bitlib/bit-algorithms/rotate.hpp +++ b/include/bitlib/bit-algorithms/rotate.hpp @@ -208,29 +208,25 @@ bit_iterator rotate( // Within the same word if (std::next(first.base(), is_last_aligned) == last.base()) { if (is_first_aligned && is_last_aligned) { - *first.base() = - (*first.base() >> n_first.position()) - | - static_cast( - *first.base() << (digits - n_first.position()) - ); - return std::next(first, digits - n_first.position()); + *first.base() = + (lsr(*first.base(), n_first.position())) | + static_cast( + *first.base() << (digits - n_first.position())); + return std::next(first, digits - n_first.position()); } else { size_type last_pos = is_last_aligned ? digits : last.position(); size_type k = n_first.position() - first.position(); size_type p = last_pos - n_first.position(); size_type d = last_pos - first.position(); - word_type mask = ((1ULL << d) - 1) << first.position(); + word_type mask = _mask(d, first.position()); word_type rotated = *first.base() & mask; - rotated = static_cast(rotated >> k) - | static_cast(rotated << p); + rotated = static_cast(lsr(rotated, k)) | static_cast(rotated << p); *first.base() = _bitblend( *first.base(), rotated, first.position(), - d - ); + d); return std::next(first, p); } } @@ -240,7 +236,7 @@ bit_iterator rotate( size_type k = distance(first, n_first); word_type temp = get_word(first, k); bit_iterator new_last = shift_left(first, last, k); - write_word(temp, new_last, static_cast(k)); + write_word(temp, new_last, static_cast(k)); return new_last; } else if (is_within(n_first, last)) { size_type p = distance(n_first, last); diff --git a/include/bitlib/bit-algorithms/shift.hpp b/include/bitlib/bit-algorithms/shift.hpp index e598e0ca..883bf2a4 100644 --- a/include/bitlib/bit-algorithms/shift.hpp +++ b/include/bitlib/bit-algorithms/shift.hpp @@ -9,7 +9,6 @@ #define _SHIFT_HPP_INCLUDED // ========================================================================== // - // ================================ PREAMBLE ================================ // // C++ standard library #include @@ -22,7 +21,7 @@ HWY_BEFORE_NAMESPACE(); #endif // Miscellaneous #define is_aligned(POINTER, BYTE_COUNT) \ - (((uintptr_t)(const void *)(POINTER)) % (BYTE_COUNT) == 0) + (((uintptr_t)(const void *)(POINTER)) % (BYTE_COUNT) == 0) #if __cplusplus >= 202002L #define STD_SHIFT_RIGHT(FIRST, LAST, N) std::shift_right(FIRST, LAST, N) @@ -39,120 +38,106 @@ namespace hn = hwy::HWY_NAMESPACE; #endif // ========================================================================== // - - // --------------------------- Shift Algorithms ----------------------------- // template bit_iterator shift_left( - bit_iterator first, - bit_iterator last, - typename bit_iterator::difference_type n -) { - // Assertions - _assert_range_viability(first, last); - - // Types and constants - using word_type = typename bit_iterator::word_type; - using size_type = typename bit_iterator::size_type; - using difference_type = typename bit_iterator::difference_type; - constexpr size_type digits = binary_digits::value; - - // Initialization - auto d = bit::distance(first, last); - const bool is_first_aligned = first.position() == 0; - const bool is_last_aligned = last.position() == 0; - auto middle = first + n; - - // Out of range cases - if (n <= 0) return last; - if (n >= d) - { - //bit::fill(first, last, bit::bit0); - return first; - } + bit_iterator first, + bit_iterator last, + typename bit_iterator::difference_type n) { + // Assertions + _assert_range_viability(first, last); + + // Types and constants + using word_type = typename bit_iterator::word_type; + using size_type = typename bit_iterator::size_type; + using difference_type = typename bit_iterator::difference_type; + constexpr size_type digits = binary_digits::value; + + // Initialization + auto d = bit::distance(first, last); + const bool is_first_aligned = first.position() == 0; + const bool is_last_aligned = last.position() == 0; + auto middle = first + n; + + // Out of range cases + if (n <= 0) { + return last; + } + if (n >= d) { + //bit::fill(first, last, bit::bit0); + return first; + } - // Single word case - // Triggered if all relevant bits are in first.base() + // Single word case + // Triggered if all relevant bits are in first.base() + // clang-format off if (std::next(first.base(), is_last_aligned) == last.base()) { *first.base() = _bitblend( *first.base(), - (( + static_cast(lsr( *first.base() & ( - static_cast(-1) >> ( + lsr(static_cast(-1), ( digits - (is_last_aligned ? digits : last.position()) - ) + )) ) - )) >> n, + , n)), first.position(), (is_last_aligned ? digits : last.position()) - first.position() ); return first + d - n; } + // clang-format on // Triggered if all remaining bits can fit in a word - if (d - n <= digits) - { - word_type new_word = get_word(middle, d - n); - write_word(new_word, first, d - n); - return first + d - n; + if (d - n <= static_cast(digits)) { + word_type new_word = get_word(middle, d - n); + write_word(new_word, first, d - n); + return first + d - n; } - // Multiple word case - word_type first_value = *first.base(); - word_type last_value = !is_last_aligned ? *last.base() : 0; // Align first - if (!is_first_aligned) - { - if (first.position() >= middle.position()) - { - *first.base() = _bitblend( - *first.base(), - (*middle.base()) << (first.position() - middle.position()), - first.position(), - digits - first.position() - ); - } - else - { - const int n1 = digits - middle.position(); - const int n2 = digits - first.position() - n1; - *first.base() = _bitblend( - *first.base(), - (*middle.base()) >> (middle.position() - first.position()), - first.position(), - n1 - ); - *first.base() = _bitblend( - *first.base(), - (*std::next(middle.base())) << (digits - n2), - first.position() + n1, - n2 - ); - } - const int shifted = std::min(d - n, (digits - first.position())); - first += shifted; - middle += shifted; + if (!is_first_aligned) { + if (first.position() >= middle.position()) { + *first.base() = _bitblend( + *first.base(), + static_cast((*middle.base()) << (first.position() - middle.position())), + first.position(), + digits - first.position()); + } else { + const size_t n1 = digits - middle.position(); + const size_t n2 = digits - first.position() - n1; + *first.base() = _bitblend( + *first.base(), + lsr(*middle.base(), (middle.position() - first.position())), + first.position(), + n1); + *first.base() = _bitblend( + *first.base(), + static_cast((*std::next(middle.base()) << (digits - n2))), + first.position() + n1, + n2); + } + const int shifted = std::min(d - n, (digits - first.position())); + first += shifted; + middle += shifted; } - if (middle.base() == last.base()) - { - const int bits_left = last.position() - middle.position(); - if (bits_left > 0) - { - *first.base() = _bitblend( - *first.base(), - *middle.base() >> middle.position(), - 0, - bits_left - ); - first += bits_left; - } - // https://en.cppreference.com/w/cpp/algorithm/shift - // "Elements that are in the original range but not the new range - // are left in a valid but unspecified state." - // - //bit::fill(first, last, bit::bit0); - return first; + if (middle.base() == last.base()) { + const int bits_left = last.position() - middle.position(); + if (bits_left > 0) { + *first.base() = _bitblend( + *first.base(), + lsr(*middle.base(), middle.position()), + 0, + bits_left); + first += bits_left; + } + // https://en.cppreference.com/w/cpp/algorithm/shift + // "Elements that are in the original range but not the new range + // are left in a valid but unspecified state." + // + //bit::fill(first, last, bit::bit0); + return first; } // More initialization @@ -161,76 +146,70 @@ bit_iterator shift_left( const size_type offset = middle.position(); // At this point, first is aligned - if (offset == 0) - { - first = bit::bit_iterator( - STD_SHIFT_LEFT(first.base(), - last.base(), - word_shifts), - 0 - ); - if (!is_last_aligned) - { - write_word(*last.base(), first, last.position()); - first += last.position(); - } - // https://en.cppreference.com/w/cpp/algorithm/shift - // "Elements that are in the original range but not the new range - // are left in a valid but unspecified state." - // - //bit::fill(first, last, bit::bit0); - return first; + if (offset == 0) { + first = bit::bit_iterator( + STD_SHIFT_LEFT(first.base(), + last.base(), + word_shifts), + 0); + if (!is_last_aligned) { + write_word(*last.base(), first, last.position()); + first += last.position(); + } + // https://en.cppreference.com/w/cpp/algorithm/shift + // "Elements that are in the original range but not the new range + // are left in a valid but unspecified state." + // + //bit::fill(first, last, bit::bit0); + return first; } // Shift bit sequence to the lsb #ifdef BITLIB_HWY // Align to 64 bit boundary while (std::next(middle.base()) < last.base() && !is_aligned(&*first.base(), 64)) { - *first.base() = _shrd(*middle.base(), *std::next(middle.base()), offset); - first += digits; - middle += digits; + *first.base() = _shrd(*middle.base(), *std::next(middle.base()), offset); + first += digits; + middle += digits; } const hn::ScalableTag d_tag; - while (std::distance(middle.base(), last.base()) >= hn::Lanes(d_tag) + 10 + !is_last_aligned) - { - const auto v = hn::ShiftRightSame(hn::LoadU(d_tag, &*middle.base()), offset); - const auto v_plus1 = hn::ShiftLeftSame(hn::LoadU(d_tag, &*(middle.base()+1)), digits - offset); - hn::Store(v | v_plus1, d_tag, &*first.base()); - first += hn::Lanes(d_tag)*digits; - middle += hn::Lanes(d_tag)*digits; + while (std::distance(middle.base(), last.base()) >= hn::Lanes(d_tag) + 10 + !is_last_aligned) { + const auto v = hn::ShiftRightSame(hn::LoadU(d_tag, &*middle.base()), offset); + const auto v_plus1 = hn::ShiftLeftSame(hn::LoadU(d_tag, &*(middle.base() + 1)), digits - offset); + hn::Store(v | v_plus1, d_tag, &*first.base()); + first += hn::Lanes(d_tag) * digits; + middle += hn::Lanes(d_tag) * digits; } #endif auto first_base = first.base(); auto middle_base = middle.base(); while (std::next(middle_base) < last.base()) { - *first_base = _shrd(*middle_base, *std::next(middle_base), offset); - first_base++; - middle_base++;; + *first_base = _shrd(*middle_base, *std::next(middle_base), offset); + first_base++; + middle_base++; + ; } first = bit_iterator(first_base, 0); middle = bit_iterator(middle_base, middle.position()); // If middle is now penultimate word - if (std::next(middle.base()) == last.base()) - { - *first.base() = _bitblend( - *first.base(), - *middle.base() >> offset, - 0, - digits - offset - ); - first += digits - offset; - middle += digits - offset; + if (std::next(middle.base()) == last.base()) { + *first.base() = _bitblend( + *first.base(), + lsr(*middle.base(), offset), + 0, + digits - offset); + first += digits - offset; + middle += digits - offset; } - if (!is_last_aligned) - { - const difference_type bits_left = last.position() - middle.position(); - const word_type new_word = get_word(middle, bits_left); - write_word(new_word, first, bits_left); - first += bits_left; + if (!is_last_aligned) { + const difference_type bits_left = last.position() - middle.position(); + const word_type new_word = get_word(middle, bits_left); + write_word(new_word, first, bits_left); + first += bits_left; } //bit::fill(first, last, bit::bit0); @@ -239,150 +218,144 @@ bit_iterator shift_left( template bit_iterator shift_right( - bit_iterator first, - bit_iterator last, - typename bit_iterator::difference_type n -) { - // Types and constants - using word_type = typename bit_iterator::word_type; - using size_type = typename bit_iterator::size_type; - - // Initialization - const bool is_first_aligned = first.position() == 0; - const bool is_last_aligned = last.position() == 0; - constexpr auto digits = binary_digits::value; - auto d = bit::distance(first, last); - bit_iterator middle = last - n; - - // Out of range cases - if (n <= 0) return first; - else if (n >= d) return last; - - // Single word case - if (std::next(first.base(), is_last_aligned) == last.base()) { - *first.base() = _bitblend( - *first.base(), - ( - *first.base() & ( - static_cast(-1) << first.position() - ) - ) << n, - first.position(), - (is_last_aligned ? digits : last.position()) - first.position() - ); - return first + n; + bit_iterator first, + bit_iterator last, + typename bit_iterator::difference_type n) { + // Types and constants + using word_type = typename bit_iterator::word_type; + using size_type = typename bit_iterator::size_type; + using difference_type = typename bit_iterator::difference_type; + + // Initialization + const bool is_last_aligned = last.position() == 0; + constexpr auto digits = binary_digits::value; + auto d = bit::distance(first, last); + + // Out of range cases + if (n <= 0) { + return first; + } else if (n >= d) { + return last; + } + + bit_iterator middle = last - n; + + // Single word case + if (std::next(first.base(), is_last_aligned) == last.base()) { + *first.base() = _bitblend( + *first.base(), + static_cast( + (*first.base() & (static_cast(-1) << first.position())) + << n), + first.position(), + ((is_last_aligned ? digits : last.position()) - first.position())); + return first + n; + } + + // Align last + if (last.position() != 0) { + const size_type bits_to_align = std::min( + last.position(), + bit::distance(first, middle)); + const word_type word_to_write = get_word( + middle - bits_to_align, + bits_to_align); + write_word( + word_to_write, + last - bits_to_align, + bits_to_align); + middle -= bits_to_align; + last -= bits_to_align; + + // Nothing left to do + if (middle == first) { + return first + n; } + } + + // More initialization + const size_type word_shifts = n / digits; + const size_type offset = middle.position(); + + // Shift bit sequence to the msb + if (offset == 0) { + /*auto new_first = */ + static_cast(bit::bit_iterator( + STD_SHIFT_RIGHT( + first.base(), + last.base(), + word_shifts), + first.position())); + // https://en.cppreference.com/w/cpp/algorithm/shift + // "Elements that are in the original range but not the new range + // are left in a valid but unspecified state." + // + //bit::fill(first, new_first, bit::bit0); + return first + n; + } - // Align last - if (last.position() != 0) - { - const size_type bits_to_align = std::min( - last.position(), - bit::distance(first, middle)); - const word_type word_to_write = get_word( - middle - bits_to_align, - bits_to_align); - write_word( - word_to_write, - last - bits_to_align, - bits_to_align); - middle -= bits_to_align; - last -= bits_to_align; - - // Nothing left to do - if (middle == first) - return first + n; + if (bit::distance(first, middle) >= static_cast(digits)) { +#ifdef BITLIB_HWY + // Align to 64 bit boundary + const hn::ScalableTag d; + while (std::prev(middle.base()) > first.base() && !is_aligned(&*(last.base() - hn::Lanes(d)), 64)) { + *std::prev(last.base()) = _shrd(*std::prev(middle.base()), *middle.base(), offset); + last -= digits; + middle -= digits; } - // More initialization - const size_type word_shifts = n / digits; - const size_type offset = middle.position(); - - // Shift bit sequence to the msb - if (offset == 0) { - auto new_first = bit::bit_iterator( - STD_SHIFT_RIGHT( - first.base(), - last.base(), - word_shifts), - first.position() - ); - // https://en.cppreference.com/w/cpp/algorithm/shift - // "Elements that are in the original range but not the new range - // are left in a valid but unspecified state." - // - //bit::fill(first, new_first, bit::bit0); - return first + n; + while (std::distance(first.base(), middle.base()) > hn::Lanes(d) + 1) { + const auto v = hn::ShiftRightSame( + hn::LoadU(d, &*(middle.base() - hn::Lanes(d))), + offset); + const auto v_plus1 = hn::ShiftLeftSame( + hn::LoadU(d, &*(middle.base() - hn::Lanes(d) + 1)), + digits - offset); + hn::Store(v | v_plus1, d, &*(last.base() - hn::Lanes(d))); + + last -= digits * hn::Lanes(d); + middle -= digits * hn::Lanes(d); } - - if (bit::distance(first, middle) >= digits) - { -#ifdef BITLIB_HWY - // Align to 64 bit boundary - const hn::ScalableTag d; - while (std::prev(middle.base()) > first.base() && !is_aligned(&*(last.base() - hn::Lanes(d)), 64)) { - *std::prev(last.base()) = _shrd(*std::prev(middle.base()), *middle.base(), offset); - last -= digits; - middle -= digits; - } - - while (std::distance(first.base(), middle.base()) > hn::Lanes(d) + 1) - { - const auto v = hn::ShiftRightSame( - hn::LoadU(d, &*(middle.base() - hn::Lanes(d))), - offset); - const auto v_plus1 = hn::ShiftLeftSame( - hn::LoadU(d, &*(middle.base() - hn::Lanes(d) + 1)), - digits - offset); - hn::Store(v | v_plus1, d, &*(last.base() - hn::Lanes(d))); - - last -= digits * hn::Lanes(d); - middle -= digits * hn::Lanes(d); - } #endif - auto last_base_prev = std::prev(last.base()); - auto middle_base_prev = std::prev(middle.base()); - - while (middle_base_prev > first.base()) { - *last_base_prev = _shrd(*middle_base_prev, *std::next(middle_base_prev), offset); - last_base_prev--; - middle_base_prev--; - } + auto last_base_prev = std::prev(last.base()); + auto middle_base_prev = std::prev(middle.base()); - if (first.position() <= middle.position()) - { - *last_base_prev = _shrd(*middle_base_prev, *std::next(middle_base_prev), offset); - last_base_prev--; - middle_base_prev--; - } - - last = bit_iterator(std::next(last_base_prev), last.position()); - middle = bit_iterator(std::next(middle_base_prev), middle.position()); + while (middle_base_prev > first.base()) { + *last_base_prev = _shrd(*middle_base_prev, *std::next(middle_base_prev), offset); + last_base_prev--; + middle_base_prev--; } - if (first.position() != middle.position()) - { - const size_type bits_to_align = bit::distance(first, middle); - const word_type word_to_write = get_word( - first, - bits_to_align); - write_word( - word_to_write, - last - bits_to_align, - bits_to_align); + if (first.position() <= middle.position()) { + *last_base_prev = _shrd(*middle_base_prev, *std::next(middle_base_prev), offset); + last = bit_iterator(last_base_prev, last.position()); + middle = bit_iterator(middle_base_prev, middle.position()); + } else { + last = bit_iterator(std::next(last_base_prev), last.position()); + middle = bit_iterator(std::next(middle_base_prev), middle.position()); } - - return first + n; + } + + if (first.position() != middle.position()) { + const size_type bits_to_align = bit::distance(first, middle); + const word_type word_to_write = get_word( + first, + bits_to_align); + write_word( + word_to_write, + last - bits_to_align, + bits_to_align); + } + + return first + n; } // -------------------------------------------------------------------------- // - - // ========================================================================== // -} // namespace bit +} // namespace bit #ifdef BITLIB_HWY HWY_AFTER_NAMESPACE(); #endif -#endif // _SHIFT_HPP_INCLUDED +#endif // _SHIFT_HPP_INCLUDED // ========================================================================== // diff --git a/include/bitlib/bit-algorithms/swap_ranges.hpp b/include/bitlib/bit-algorithms/swap_ranges.hpp index 67cd5a06..4841b639 100644 --- a/include/bitlib/bit-algorithms/swap_ranges.hpp +++ b/include/bitlib/bit-algorithms/swap_ranges.hpp @@ -34,6 +34,8 @@ constexpr bit_iterator swap_ranges( using word_type2 = typename bit_iterator::word_type; using size_type1 = typename bit_iterator::size_type; using size_type2 = typename bit_iterator::size_type; + using iterator_type1 = typename bit_iterator::iterator_type; + using iterator_type2 = typename bit_iterator::iterator_type; constexpr size_type1 digits1 = binary_digits::value; constexpr size_type2 digits2 = binary_digits::value; @@ -41,8 +43,8 @@ constexpr bit_iterator swap_ranges( //const bool is_first1_aligned = first1.position() == 0; const bool is_last1_aligned = last1.position() == 0; //const bool is_first2_aligned = first2.position() == 0; - RandomAccessIt1 it1 = first1.base(); - RandomAccessIt2 it2 = first2.base(); + iterator_type1 it1 = first1.base(); + iterator_type2 it2 = first2.base(); if (first1 == last1) return first2; diff --git a/include/bitlib/bit-algorithms/to_from_string.hpp b/include/bitlib/bit-algorithms/to_from_string.hpp new file mode 100644 index 00000000..c57ee3dc --- /dev/null +++ b/include/bitlib/bit-algorithms/to_from_string.hpp @@ -0,0 +1,475 @@ +// ============================= to_from_string ============================= // +// Project: The Experimental Bit Algorithms Library +// \file to_from_string.hpp +// Description: Implementation of to_string and from_string +// Creator: Vincent Reverdy +// Contributor: Peter McLean [2025] +// License: BSD 3-Clause License +// ========================================================================== // + +#ifndef _BIT_TO_STRING_HPP_INCLUDED +#define _BIT_TO_STRING_HPP_INCLUDED + +#include +#include +#include +#include +#include + +#include "bitlib/bit-algorithms/accumulate.hpp" +#include "bitlib/bit-algorithms/count.hpp" +#include "bitlib/bit-algorithms/division.hpp" +#include "bitlib/bit-algorithms/multiplication.hpp" +#include "bitlib/bit-containers/bit_policy.hpp" +#include "bitlib/bit_concepts.hpp" + +namespace bit { + +namespace string { + +template +constexpr auto make_digit_map() { + static_assert((Base >= 2) && ((Base & (Base - 1)) == 0), "Base must be power of 2 >= 2"); + static_assert(Base <= 64, "Base too large for simple char mapping"); + + ::std::array map{}; + for (unsigned char i = 0; i < static_cast(Base); ++i) { + map[i] = static_cast((i < 10) ? ('0' + i) : ('A' + (i - 10))); + } + return map; +} + +constexpr std::span make_digit_map(std::size_t Base) { + switch (Base) { + case 2: { + static constexpr auto map = make_digit_map<2>(); + return std::span(static_cast(map.data()), map.size()); + } + case 4: { + static constexpr auto map = make_digit_map<4>(); + return std::span(static_cast(map.data()), map.size()); + } + case 8: { + static constexpr auto map = make_digit_map<8>(); + return std::span(static_cast(map.data()), map.size()); + } + case 16: { + static constexpr auto map = make_digit_map<16>(); + return std::span(static_cast(map.data()), map.size()); + } + case 32: { + static constexpr auto map = make_digit_map<32>(); + return std::span(static_cast(map.data()), map.size()); + } + case 64: { + static constexpr auto map = make_digit_map<64>(); + return std::span(static_cast(map.data()), map.size()); + } + default: + return {}; // or throw, or abort + } +} + +template +constexpr auto make_from_digit_map() { + static_assert((Base >= 2) && ((Base & (Base - 1)) == 0), "Base must be power of 2 >= 2"); + static_assert(Base <= 64, "Base too large for simple char mapping"); + + ::std::array map{}; + map.fill(~0); + for (unsigned char i = '0'; i <= 'z'; ++i) { + if (i >= '0' && i <= '9') { + map[i] = static_cast(i - '0'); + } + if (i >= 'a' && i <= 'z') { + map[i] = static_cast((i - 'a') + 10); + } + if (i >= 'A' && i <= 'Z') { + map[i] = static_cast((i - 'A') + 10); + } + } + return map; +} + +constexpr auto make_from_digit_map(std::size_t Base) { + switch (Base) { + case 2: { + static constexpr auto map2 = make_from_digit_map<2>(); + return map2; + } + case 4: { + static constexpr auto map4 = make_from_digit_map<4>(); + return map4; + } + case 8: { + static constexpr auto map8 = make_from_digit_map<8>(); + return map8; + } + case 16: { + static constexpr auto map16 = make_from_digit_map<16>(); + return map16; + } + case 32: { + static constexpr auto map32 = make_from_digit_map<32>(); + return map32; + } + case 64: { + static constexpr auto map64 = make_from_digit_map<64>(); + return map64; + } + default: + throw std::runtime_error("Base not implemented"); + } +} + +struct metadata_t { + size_t base; + bool is_signed; + std::endian endian; + bool str_sign_extend_zeros; + char fill; +}; + +constexpr metadata_t typical(size_t base = 10, bool str_sign_extend_zeros = false) { + return { + .base = base, + .is_signed = false, + .endian = std::endian::big, + .str_sign_extend_zeros = str_sign_extend_zeros, + .fill = '\0'}; +} + +} // namespace string + +template +constexpr CharIt to_string( + const bit_iterator& bit_first, + const bit_iterator& bit_last, + const CharIt str_first, + const CharIt str_last, + string::metadata_t meta = string::typical()) { + if (std::has_single_bit(meta.base)) { + const auto base_bits = std::bit_width(meta.base - 1); + const auto base_digits = string::make_digit_map(meta.base); + + CharIt start = accumulate_while( + policy::AccumulateNoInitialSubword{}, + bit_first, bit_last, str_last, + [meta, base_bits, base_digits, str_first](CharIt cursor, auto word, const size_t bits = bitsof()) { + for (size_t i = 0; i < ((bits + base_bits - 1) / base_bits); i++) { + if (cursor == str_first) { + return std::make_pair(false, cursor); + } + *(--cursor) = base_digits[word & (meta.base - 1)]; + word >>= static_cast(base_bits); + } + return std::make_pair(cursor != str_first, cursor); + }); + if (start != str_first) { + return std::copy(start, str_last, str_first); + } else { + return str_last; + } + } else { + using word_type = typename bit_iterator::word_type; + size_t store_bits = distance(bit_first, bit_last); + std::vector vec((store_bits + bitsof() - 1) / bitsof()); + vec.back() = 0; // Ensure last word is zeroed + bit_iterator bit_it(vec.data()); + + const unsigned char base = static_cast(meta.base); + auto remainder = ::bit::division(bit_first, bit_last, bit_it, base); + CharIt cursor = str_last; + *(--cursor) = static_cast(remainder + '0'); + + while ((cursor != str_first) && (store_bits -= ::bit::count_msb(bit_it, bit_it + store_bits, bit0))) { + remainder = ::bit::division(bit_it, bit_it + store_bits, bit_it, base); + *(--cursor) = static_cast(remainder + '0'); + } + if (cursor != str_first) { + return std::copy(cursor, str_last, str_first); + } + return str_last; + } +} + +template +constexpr size_t estimate_length( + const bit_iterator& first, + const bit_iterator& last, + const size_t base, + const bool str_sign_extend_zeros) { + if (std::has_single_bit(base)) { + const auto base_bits = std::bit_width(base - 1); + + size_t skip_leading_bits = str_sign_extend_zeros ? 0 : count_msb(first, last, bit0); + + size_t str_len = (distance(first, last) - skip_leading_bits); + str_len = (str_len + base_bits - 1) / base_bits; // Round up to nearest base digit + return static_cast(std::max(static_cast(1), str_len)); + } else { + const uint32_t LOG2BASE = static_cast(std::ceil(static_cast(1 << 16) / std::logbf(static_cast(base)))); + size_t skip_leading_bits = str_sign_extend_zeros ? 0 : count_msb(first, last, bit0); + const auto bits = distance(first, last) - skip_leading_bits; + const auto fixed_point = (bits * LOG2BASE); + const size_t max_len = (fixed_point >> 16) + ((fixed_point & ((1 << 16) - 1)) != 0); + return static_cast(std::max(static_cast(1), max_len)); + } +} + +template +constexpr std::string to_string( + const bit_iterator& first, + const bit_iterator& last, + string::metadata_t meta = string::typical()) { + std::string buffer(estimate_length(first, last, meta.base, meta.str_sign_extend_zeros), meta.fill); + if (meta.fill) { + std::fill(to_string(first, last, buffer.begin(), buffer.end(), meta), buffer.end(), meta.fill); + } else { + buffer.resize(to_string(first, last, buffer.begin(), buffer.end(), meta) - buffer.begin()); + } + return buffer; +} + +template +constexpr std::string to_string( + const bit_iterator& first, + const bit_iterator& last) { + static_assert(meta.endian == std::endian::big, "Only bit big endian support (MSB on the left)"); + return to_string(first, last, meta); +} + +constexpr std::string to_string(const bit_sized_range auto& bits, string::metadata_t meta = string::typical()) { + return to_string(bits.begin(), bits.end(), meta); +} + +template +constexpr std::string to_string(const bit_sized_range auto& bits) { + return to_string(bits, meta); +} + +template +constexpr CharIt to_string( + const bit_iterator& first, + const bit_iterator& last, + const CharIt str_first, + const CharIt str_last) { + static_assert(meta.endian == std::endian::big, "Only bit big endian support (MSB on the left)"); + return to_string(first, last, str_first, str_last, meta); +} + +template +constexpr CharIt to_string( + const bit_sized_range auto& bits, + const CharIt str_first, + const CharIt str_last, + string::metadata_t meta = string::typical()) { + return to_string(str_first, str_last, bits.begin(), bits.end(), meta); +} + +template +constexpr CharIt to_string( + const bit_sized_range auto& bits, + const CharIt str_first, + const CharIt str_last) { + return to_string(bits, str_first, str_last, meta); +} + +template ::value_type>> +constexpr void from_string( + const CharIt str_first, const CharIt str_last, + const bit_iterator& bit_first, const bit_iterator& bit_last, + string::metadata_t meta = string::typical()) { + // TODO: This should be a policy + if (str_first == str_last) { + return; // Nothing to do + } + if (std::has_single_bit(meta.base)) { + const auto base_bits = std::bit_width(meta.base - 1); + const auto base_from_digits = string::make_from_digit_map(meta.base); + using word_type = uint64_t; + std::vector vec; + size_t store_bits = distance(bit_first, bit_last); + + bit_iterator bit_it = bit_first; + auto cursor = std::distance(str_first, str_last) - 1; + while ((cursor >= 0) && store_bits) { + word_type work = 0; + size_t bits = 0; + for (; (bits < bitsof()) && (cursor >= 0); cursor--) { + char c = str_first[cursor]; + // TODO: This should be a policy + if (static_cast(c) >= base_from_digits.size()) { + continue; + } + auto digit = base_from_digits[c]; + // TODO: This should be a policy + if (~0 == digit) { + continue; + } + work |= (static_cast(digit) << bits); + bits += base_bits; + } + if (store_bits < bits) { + Policy::truncation::template from_integral( + work, bit_it, bit_last); + return; + } else if ((store_bits > bits) && (cursor < 0)) { + const bit_iterator p_integral(&work); + bit_it = ::bit::copy(p_integral, p_integral + bits, bit_it); + // TODO: policy + ::bit::fill(bit_it, bit_last, meta.is_signed ? bit_it[-1] : bit0); // Clear the rest + return; + } else if (store_bits >= bits) { + const bit_iterator p_integral(&work); + bit_it = ::bit::copy(p_integral, p_integral + bits, bit_it); + store_bits -= bits; + } + } + } else { + if (meta.base != 10) { + throw std::runtime_error("Base not implemented"); + } + using word_type = typename bit_iterator::word_type; + std::vector vec; + + // TODO: template with uninitialized_t + ::bit::fill(bit_first, bit_last, bit0); // Clear the bits first + + CharIt cursor = str_first; + while (cursor != str_last) { + unsigned char c = static_cast(*cursor - '0'); + if (c <= 9) { + auto overflow_mult = ::bit::multiplication(bit_first, bit_last, word_type{10}); + auto overflow_add = ::bit::addition(bit_first, bit_last, c); + if (overflow_mult || overflow_add) { + //Policy::truncation::template overflow(bit_first, bit_last); + return; + } + } + cursor++; + } + //Policy::extension::template extend(bit_first, bit_last); + } +} + +template ::value_type>> +constexpr void from_string( + const CharIt str_first, const CharIt str_last, + const bit_iterator& bit_first, const bit_iterator& bit_last) { + static_assert(meta.endian == std::endian::big, "Only bit big endian support (MSB on the left)"); + from_string(str_first, str_last, bit_first, bit_last, meta); +} + +template +constexpr std::vector from_string( + const CharIt first, const CharIt last, string::metadata_t meta = string::typical()) { + if (std::has_single_bit(meta.base)) { + const auto base_bits = std::bit_width(meta.base - 1); + const auto base_from_digits = string::make_from_digit_map(meta.base); + + std::vector vec; + + last--; + while (last >= first) { + uintptr_t work = 0; + size_t bits = 0; + for (; (bits < bitsof()) && (last >= first); last--) { + char c = *last; + // TODO: This should be a policy + if (static_cast(c) >= base_from_digits.size()) { + continue; + } + auto digit = base_from_digits[c]; + // TODO: This should be a policy + if (~0 == digit) { + continue; + } + work |= (digit << bits); + bits += base_bits; + } + if (bits) { + vec.push_back(work); + } + } + return vec; + } else { + //from_string base 10 not implemented yet; + return {}; + } +} + +template +constexpr std::vector from_string( + const CharIt first, const CharIt last) { + static_assert(meta.endian == std::endian::big, "Only bit big endian support (MSB on the left)"); + return from_string(first, last, meta); +} + +template ::value_type>> +constexpr void from_string( + const std::string& str, + const bit_iterator& bit_first, const bit_iterator& bit_last) { + static_assert(meta.endian == std::endian::big, "Only bit big endian support (MSB on the left)"); + from_string(str.c_str(), str.c_str() + str.length(), bit_first, bit_last); +} + +template ::word_type>> +constexpr void from_string( + const std::string& str, + RangeT&& bits) { + using range_iterator_t = std::ranges::iterator_t; + using RandomAccessIt = typename range_iterator_t::iterator_type; + from_string(str.begin(), str.end(), bits.begin(), bits.end()); +} + +template ::value_type>> +constexpr void from_string( + const std::string& str, + const bit_iterator& bit_first, const bit_iterator& bit_last, + string::metadata_t meta = string::typical()) { + from_string( + str.begin(), str.end(), + bit_first, bit_last, + meta); +} + +template ::word_type>> +constexpr void from_string( + const std::string& str, + RangeT&& bits, + string::metadata_t meta = string::typical()) { + using range_iterator_t = std::ranges::iterator_t; + using RandomAccessIt = typename range_iterator_t::iterator_type; + from_string( + str.begin(), str.end(), + bits.begin(), bits.end(), + meta); +} + +template ::word_type>> +constexpr void from_string( + const CharIt first, const CharIt last, + RangeT&& bits, + string::metadata_t meta = string::typical()) { + using range_iterator_t = std::ranges::iterator_t; + using RandomAccessIt = typename range_iterator_t::iterator_type; + from_string(first, last, bits.begin(), bits.end(), meta); +} + +template ::word_type>> +constexpr void from_string( + const CharIt first, const CharIt last, + RangeT&& bits) { + using range_iterator_t = std::ranges::iterator_t; + using RandomAccessIt = typename range_iterator_t::iterator_type; + return from_string(first, last, bits.begin(), bits.end(), meta); +} + +} // namespace bit + +#endif // _BIT_TO_STRING_HPP_INCLUDED \ No newline at end of file diff --git a/include/bitlib/bit-algorithms/transform.hpp b/include/bitlib/bit-algorithms/transform.hpp index 88577882..12a857c9 100644 --- a/include/bitlib/bit-algorithms/transform.hpp +++ b/include/bitlib/bit-algorithms/transform.hpp @@ -31,54 +31,65 @@ namespace bit { //return d_first; //} -template -constexpr bit_iterator transform( - bit_iterator first, - bit_iterator last, - bit_iterator d_first, - UnaryOperation unary_op) { - using word_type = typename bit_iterator::word_type; - using size_type = typename bit_iterator::size_type; - constexpr size_type digits = binary_digits::value; +template + requires( + (std::is_same_v>, + std::remove_cvref_t>>)) +constexpr bit_iterator transform( + bit_iterator first, + bit_iterator last, + bit_iterator d_first, + UnaryOperation unary_op) { + using word_type = typename bit_iterator::word_type; + using size_type = typename bit_iterator::size_type; + constexpr size_type digits = binary_digits::value; - // Assertions - _assert_range_viability(first, last); - if (first == last) return d_first; + // Assertions + _assert_range_viability(first, last); + if (first == last) { + return d_first; + } + // Initialization + const bool is_d_first_aligned = d_first.position() == 0; + size_type total_bits_to_op = distance(first, last); + size_type remaining_bits_to_op = total_bits_to_op; + auto it = d_first.base(); - // Initialization - const bool is_d_first_aligned = d_first.position() == 0; - size_type total_bits_to_op = distance(first, last); - size_type remaining_bits_to_op = total_bits_to_op; - auto it = d_first.base(); - - // d_first is not aligned. Copy partial word to align it - if (!is_d_first_aligned) { - size_type partial_bits_to_op = ::std::min( - remaining_bits_to_op, - digits - d_first.position() - ); - *it = _bitblend( - *it, - unary_op( - static_cast( - get_word(first, partial_bits_to_op) - << static_cast(d_first.position()) - ) - ), - static_cast(d_first.position()), - static_cast(partial_bits_to_op)); - remaining_bits_to_op -= partial_bits_to_op; - advance(first, partial_bits_to_op); - it++; + // d_first is not aligned. Copy partial word to align it + if (!is_d_first_aligned) { + size_type partial_bits_to_op = ::std::min( + remaining_bits_to_op, + digits - d_first.position()); + word_type result; + if constexpr (std::is_invocable_v) { + result = unary_op( + static_cast( + get_masked_word(first, partial_bits_to_op) + << static_cast(d_first.position())), + partial_bits_to_op); + } else { + result = unary_op( + static_cast( + get_masked_word(first, partial_bits_to_op) + << static_cast(d_first.position()))); } - auto firstIt = first.base(); - if (remaining_bits_to_op > 0) { - const bool is_first_aligned = first.position() == 0; - //size_type words_to_op = ::std::ceil(remaining_bits_to_op / static_cast(digits)); - // d_first will be aligned at this point - if (is_first_aligned && remaining_bits_to_op > digits) { - auto N = ::std::distance(firstIt, last.base()); + *it = _bitblend( + *it, + result, + static_cast(d_first.position()), + static_cast(partial_bits_to_op)); + remaining_bits_to_op -= partial_bits_to_op; + advance(first, partial_bits_to_op); + it++; + } + auto firstIt = first.base(); + if (remaining_bits_to_op > 0) { + const bool is_first_aligned = first.position() == 0; + //size_type words_to_op = ::std::ceil(remaining_bits_to_op / static_cast(digits)); + // d_first will be aligned at this point + if (is_first_aligned && remaining_bits_to_op > digits) { + auto N = ::std::distance(firstIt, last.base()); #ifdef BITLIB_HWY if constexpr (std::is_same_v>) { @@ -103,112 +114,122 @@ constexpr bit_iterator transform( } #endif size_t std_dist = ::std::distance(firstIt, last.base()); - it = std::transform(firstIt, last.base(), it, unary_op); + for (auto in_it = firstIt; in_it != last.base(); std::advance(in_it, 1), std::advance(it, 1)) { + if constexpr (std::is_invocable_v) { + *it = unary_op(*in_it, digits); + } else { + *it = unary_op(*in_it); + } + } + //it = std::transform(firstIt, last.base(), it, unary_op); firstIt += std_dist; - first = bit_iterator(firstIt); + first = bit_iterator(firstIt); remaining_bits_to_op -= digits * N; + } else { + while (remaining_bits_to_op >= digits) { + if constexpr (std::is_invocable_v) { + *it = unary_op(get_word(first, digits), digits); } else { - while (remaining_bits_to_op >= digits) { - *it = unary_op(get_word(first, digits)); - remaining_bits_to_op -= digits; - it++; - advance(first, digits); - } + *it = unary_op(get_word(first, digits)); } + + remaining_bits_to_op -= digits; + it++; + advance(first, digits); + } + } if (remaining_bits_to_op > 0) { - *it = _bitblend( - *it, - unary_op(get_word(first, remaining_bits_to_op)), - static_cast( - (static_cast(1) << remaining_bits_to_op) - 1) - ); + word_type result; + if constexpr (std::is_invocable_v) { + result = unary_op(get_masked_word(first, remaining_bits_to_op), remaining_bits_to_op); + } else { + result = unary_op(get_masked_word(first, remaining_bits_to_op)); + } + *it = _bitblend( + *it, + result, + _mask(remaining_bits_to_op)); } - } + } return d_first + total_bits_to_op; } -template -constexpr bit_iterator transform( - bit_iterator first1, - bit_iterator last1, - bit_iterator first2, - bit_iterator d_first, - BinaryOperation binary_op) { - using word_type = typename bit_iterator::word_type; - using size_type = typename bit_iterator::size_type; - constexpr size_type digits = binary_digits::value; +template +constexpr bit_iterator transform( + bit_iterator first1, + bit_iterator last1, + bit_iterator first2, + bit_iterator d_first, + BinaryOperation binary_op) { + using word_type = typename bit_iterator::word_type; + using size_type = typename bit_iterator::size_type; + constexpr size_type digits = binary_digits::value; - // Assertions - _assert_range_viability(first1, last1); - if (first1 == last1) return d_first; + // Assertions + _assert_range_viability(first1, last1); + if (first1 == last1) { + return d_first; + } + // Initialization + const bool is_d_first_aligned = d_first.position() == 0; + size_type total_bits_to_op = distance(first1, last1); + size_type remaining_bits_to_op = total_bits_to_op; + auto it = d_first.base(); - // Initialization - const bool is_d_first_aligned = d_first.position() == 0; - size_type total_bits_to_op = distance(first1, last1); - size_type remaining_bits_to_op = total_bits_to_op; - auto it = d_first.base(); - - // d_first is not aligned. Copy partial word to align it - if (!is_d_first_aligned) { - size_type partial_bits_to_op = ::std::min( - remaining_bits_to_op, - digits - d_first.position() - ); - *it = _bitblend( - *it, - binary_op( - static_cast( - get_word(first1, partial_bits_to_op) - << static_cast(d_first.position()) - ), - static_cast( - get_word(first2, partial_bits_to_op) - << static_cast(d_first.position()) - ) - ), - static_cast(d_first.position()), - static_cast(partial_bits_to_op)); - remaining_bits_to_op -= partial_bits_to_op; - advance(first1, partial_bits_to_op); - advance(first2, partial_bits_to_op); + // d_first is not aligned. Copy partial word to align it + if (!is_d_first_aligned) { + size_type partial_bits_to_op = ::std::min( + remaining_bits_to_op, + digits - d_first.position()); + *it = _bitblend( + *it, + binary_op( + static_cast( + get_masked_word(first1, partial_bits_to_op) + << static_cast(d_first.position())), + static_cast( + get_masked_word(first2, partial_bits_to_op) + << static_cast(d_first.position()))), + d_first.position(), + partial_bits_to_op); + remaining_bits_to_op -= partial_bits_to_op; + advance(first1, partial_bits_to_op); + advance(first2, partial_bits_to_op); + it++; + } + if (remaining_bits_to_op > 0) { + const bool is_first1_aligned = first1.position() == 0; + const bool is_first2_aligned = first2.position() == 0; + //size_type words_to_op = ::std::ceil(remaining_bits_to_op / static_cast(digits)); + // d_first will be aligned at this point + if (is_first1_aligned && is_first2_aligned && remaining_bits_to_op > digits) { + auto N = ::std::distance(first1.base(), last1.base()); + it = std::transform(first1.base(), last1.base(), first2.base(), it, binary_op); + first1 += digits * N; + first2 += digits * N; + remaining_bits_to_op -= digits * N; + } else { + while (remaining_bits_to_op >= digits) { + *it = binary_op( + get_word(first1, digits), + get_word(first2, digits)); + remaining_bits_to_op -= digits; it++; + advance(first1, digits); + advance(first2, digits); + } } if (remaining_bits_to_op > 0) { - const bool is_first1_aligned = first1.position() == 0; - const bool is_first2_aligned = first2.position() == 0; - //size_type words_to_op = ::std::ceil(remaining_bits_to_op / static_cast(digits)); - // d_first will be aligned at this point - if (is_first1_aligned && is_first2_aligned && remaining_bits_to_op > digits) { - auto N = ::std::distance(first1.base(), last1.base()); - it = std::transform(first1.base(), last1.base(), first2.base(), it, binary_op); - first1 += digits * N; - first2 += digits * N; - remaining_bits_to_op -= digits * N; - } else { - while (remaining_bits_to_op >= digits) { - *it = binary_op( - get_word(first1, digits), - get_word(first2, digits)); - remaining_bits_to_op -= digits; - it++; - advance(first1, digits); - advance(first2, digits); - } - } - if (remaining_bits_to_op > 0) { - *it = _bitblend( - *it, - binary_op( - get_word(first1, remaining_bits_to_op), - get_word(first2, remaining_bits_to_op) - ), - static_cast( - (static_cast(1) << remaining_bits_to_op) - 1) - ); - } + *it = _bitblend( + *it, + binary_op( + get_masked_word(first1, remaining_bits_to_op), + get_masked_word(first2, remaining_bits_to_op)), + _mask(remaining_bits_to_op)); } - return d_first + total_bits_to_op; + } + return d_first + total_bits_to_op; } //template diff --git a/include/bitlib/bit-algorithms/transform_accumulate.hpp b/include/bitlib/bit-algorithms/transform_accumulate.hpp new file mode 100644 index 00000000..445fb362 --- /dev/null +++ b/include/bitlib/bit-algorithms/transform_accumulate.hpp @@ -0,0 +1,154 @@ +// ================================= array_REF =================================== // +// Project: The Experimental Bit Algorithms Library +// \file accumulate.hpp +// Description: Implementation of accumulate +// Creator: Vincent Reverdy +// Contributor: Peter McLean [2025] +// License: BSD 3-Clause License +// ========================================================================== // + +#ifndef _BIT_TRANSFORM_ACCUMULATE_HPP_INCLUDED +#define _BIT_TRANSFORM_ACCUMULATE_HPP_INCLUDED + +#include "bitlib/bit-iterator/bit_iterator.hpp" +#include "bitlib/bit-iterator/bit_details.hpp" + +namespace bit { + +template < + bool forward, + bool initial_sub_word, + typename RandomAccessItIn, + typename RandomAccessItOut, + typename T, + typename BinaryOperation, + typename BinaryOperationSubword> + requires( + (std::is_same_v>, + std::remove_cvref_t>>)) +constexpr auto transform_accumulate( + bit_iterator first, + bit_iterator last, + bit_iterator d_first, + bit_iterator d_last, + T acc, + BinaryOperation binary_op, + BinaryOperationSubword binary_op_subword) { + using word_type = typename bit_iterator::word_type; + using size_type = typename bit_iterator::size_type; + constexpr size_type digits = bitsof(); + + size_type total_bits_to_op = distance(first, last); + + RandomAccessItOut d_it; + if constexpr (forward) { + d_it = d_first.base(); + } else { + d_it = d_last.base(); + } + + if constexpr (forward) { + if (d_first.position() != 0) { + size_type partial_bits_to_op = ::std::min( + total_bits_to_op, + digits - d_first.position()); + if (partial_bits_to_op != 0) { + word_type word; + std::tie(word, acc) = binary_op_subword(std::move(acc), get_masked_word(first, partial_bits_to_op), partial_bits_to_op); + *d_it = _bitblend( + *d_it, + word, + d_first.position(), + partial_bits_to_op); + total_bits_to_op -= partial_bits_to_op; + advance(first, partial_bits_to_op); + advance(d_it, 1); + } + } + } else { + if (d_last.position() != 0) { + size_type partial_bits_to_op = ::std::min( + total_bits_to_op, + d_last.position()); + if (partial_bits_to_op != 0) { + reverse(last, partial_bits_to_op); + word_type word; + std::tie(word, acc) = binary_op_subword(std::move(acc), get_masked_word(last, partial_bits_to_op), partial_bits_to_op); + *d_it = _bitblend( + *d_it, + word, + d_first.position(), + partial_bits_to_op); + total_bits_to_op -= partial_bits_to_op; + } + } + } + + + const size_type whole_words_to_op = total_bits_to_op / digits; + const size_type remaining_bits_to_op = total_bits_to_op % digits; + + for (size_t i = 0; i < whole_words_to_op; i ++) { + if constexpr (forward) { + word_type word; + std::tie(word, acc) = binary_op(std::move(acc), get_word(first)); + *d_it = word; + advance(first, digits); + std::advance(d_it, 1); + } else { + reverse(last, digits); + reverse(d_it, 1); + word_type word; + std::tie(word, acc) = binary_op(std::move(acc), get_word(last)); + *d_it = word; + } + } + if (remaining_bits_to_op > 0) { + if constexpr (forward) { + word_type word; + std::tie(word, acc) = binary_op_subword(std::move(acc), get_masked_word(first, remaining_bits_to_op), remaining_bits_to_op); + *d_it = _bitblend( + *d_it, + word, + 0, + remaining_bits_to_op); + + } else { + reverse(last, remaining_bits_to_op); + reverse(d_it, 1); + word_type word; + std::tie(word, acc) = binary_op_subword(std::move(acc), get_masked_word(last, remaining_bits_to_op), remaining_bits_to_op); + *d_it = _bitblend( + *d_it, + word, + 0, + remaining_bits_to_op); + } + } + + return acc; +} + +template < + typename RandomAccessItIn, + typename RandomAccessItOut, + typename T, + typename BinaryOperation, + typename BinaryOperationSubword> + requires( + (std::is_same_v>, + std::remove_cvref_t>>)) +constexpr auto transform_accumulate_backward( + const bit_iterator& first, + const bit_iterator& last, + const bit_iterator& d_first, + const bit_iterator& d_last, + const T& acc, + BinaryOperation binary_op, + BinaryOperationSubword binary_op_subword) { + return transform_accumulate(first, last, d_first, d_last, acc, binary_op, binary_op_subword); +} + +} // namespace bit + +#endif // _BIT_TRANSFORM_ACCUMULATE_HPP_INCLUDED diff --git a/include/bitlib/bit-algorithms/type_traits.hpp b/include/bitlib/bit-algorithms/type_traits.hpp index 0cd1fb20..56b76d29 100644 --- a/include/bitlib/bit-algorithms/type_traits.hpp +++ b/include/bitlib/bit-algorithms/type_traits.hpp @@ -55,9 +55,8 @@ struct is_bit // Is bit structure specialization: bit reference template -struct is_bit> -: std::true_type -{ +struct is_bit> + : std::true_type { }; // Is bit value template definition diff --git a/include/bitlib/bit-containers/bit-containers.hpp b/include/bitlib/bit-containers/bit-containers.hpp index bbbd540a..e4d0e6f8 100644 --- a/include/bitlib/bit-containers/bit-containers.hpp +++ b/include/bitlib/bit-containers/bit-containers.hpp @@ -7,6 +7,12 @@ #ifndef _BIT_CONTAINERS_HPP_INCLUDED #define _BIT_CONTAINERS_HPP_INCLUDED // ============================== PREAMBLE ================================== // +#include "bit_array.hpp" +#include "bit_array_dynamic_extent.hpp" +#include "bit_array_ref.hpp" +#include "bit_literal.hpp" +#include "bit_mdspan_accessor.hpp" +#include "bit_span.hpp" #include "bit_vector.hpp" // ========================================================================== // #endif diff --git a/include/bitlib/bit-containers/bit_array.hpp b/include/bitlib/bit-containers/bit_array.hpp new file mode 100644 index 00000000..aeded261 --- /dev/null +++ b/include/bitlib/bit-containers/bit_array.hpp @@ -0,0 +1,254 @@ +// ================================= BIT_ARRAY =================================== // +// Project: The Experimental Bit Algorithms Library +// \file bit_array.hpp +// Description: Implementation of bit_array +// Creator: Vincent Reverdy +// Contributor: Peter McLean [2025] +// License: BSD 3-Clause License +// ========================================================================== // +#ifndef _BIT_ARRAY_HPP_INCLUDED +#define _BIT_ARRAY_HPP_INCLUDED +// ========================================================================== // + +// ================================ PREAMBLE ================================ // +// C++ standard library +#include +#include +#include +#include +#include +#include +#include + +// Project sources +#include "bitlib/bit-algorithms/bit_algorithm.hpp" +#include "bitlib/bit-containers/bit_array_base.hpp" +#include "bitlib/bit-containers/bit_bitsof.hpp" +#include "bitlib/bit-containers/bit_policy.hpp" +#include "bitlib/bit-iterator/bit.hpp" +#include "bitlib/bit_concepts.hpp" + +namespace bit { + +template , + std::conditional_t< + (N == std::dynamic_extent), + std::uintptr_t, + ceil_integral>, + T>, + typename Policy = policy::typical>> +class array; + +template >, + typename Policy = policy::typical>> +using bit_array = array; + +namespace detail { + +template +constexpr size_t Words() { return (N * bitsof() + bitsof() - 1) / bitsof(); } + +template +struct array_iterator_types { + using iterator = typename std::conditional, + bit_iterator()>::iterator>, + typename std::array()>::iterator>::type; + + using const_iterator = typename std::conditional, + bit_iterator()>::const_iterator>, + typename std::array()>::const_iterator>::type; +}; +} // namespace detail + +template +class array : public array_base, T, N, W, false, Policy, detail::array_iterator_types> { + public: + using base = array_base, T, N, W, false, Policy, detail::array_iterator_types>; + using base::end; + using base::size; + using typename base::const_iterator; + using typename base::const_pointer; + using typename base::const_reference; + using typename base::difference_type; + using typename base::iterator; + using typename base::pointer; + using typename base::reference; + using typename base::size_type; + using typename base::value_type; + using typename base::word_type; + + static constexpr std::size_t bits = N * bitsof(); + + protected: + static constexpr std::size_t Words(std::size_t size_) { + return (size_ * bitsof() + bitsof() - 1) / bitsof(); + } + + private: + std::array storage; + + public: + /* + * Constructors, copies and moves... + */ + constexpr array() noexcept : storage{} {} + + constexpr array(value_type bit_val) { + this->fill(bit_val); + } + + constexpr array(detail::uninitialized_t, const size_t& size) { + assert(size == N); + } + + template + constexpr array(const U& integral) { + this->from_integral(integral); + } + + constexpr array(const array& other) noexcept + : base(), storage(other.storage) {} + + constexpr array(const array&& other) noexcept + : base(), storage(other.storage) {} + + constexpr array(const bit_sized_range auto& other) : base() { + if (other.size() != this->size()) [[unlikely]] { + throw std::invalid_argument("other bit_range contains an invalid number of bits for array."); + } + ::bit::copy(other.begin(), other.end(), this->begin()); + }; + + constexpr array(const std::initializer_list init) + requires(!std::is_same_v) + : base() { + if (init.size() != bitsof(*this)) [[unlikely]] { + throw std::invalid_argument("initialize_list contains an invalid number of bits for array."); + } + std::copy(init.begin(), init.end(), this->begin()); + } + + constexpr array(const std::initializer_list init) : base() { + if (init.size() != bitsof(*this)) [[unlikely]] { + throw std::invalid_argument("initialize_list contains an invalid number of bits for array."); + } + std::copy(init.begin(), init.end(), this->begin()); + } + + constexpr array(const std::initializer_list init) : base(), storage{} { + // Make sure we handle the case where init.size() != Words + auto it = init.begin(); + for (size_type i = 0; i < std::min(Words(N), init.size()); ++i, ++it) { + storage[i] = *it; + } + } + + constexpr array(const std::string_view s) + requires(std::is_same_v) + : base() { + if (bitsof(*this) != static_cast(std::count(s.begin(), s.end(), '0') + std::count(s.begin(), s.end(), '1'))) [[unlikely]] { + throw std::invalid_argument("String contains an invalid number of bits for array."); + }; + size_type i = 0; + for (char c : s) { + if (c == '0') { + begin()[i++] = bit0; + } else if (c == '1') { + begin()[i++] = bit1; + } + } + } + + ~array() = default; + /* + * Assignment + */ + constexpr array& operator=(const array& other) = default; + + constexpr array& operator=(const bit_sized_range auto& other) { + if (other.size() != this->size()) [[unlikely]] { + throw std::invalid_argument("other bit_sized_range contains an invalid number of bits for array."); + } + ::bit::copy(other.begin(), other.end(), this->begin()); + return *this; + }; + + constexpr array& operator=(array&& other) noexcept { + std::copy(other.storage.begin(), other.storage.end(), storage.begin()); + return *this; + } + + constexpr word_type* data() noexcept { + return size() ? storage.data() : nullptr; + } + + constexpr const word_type* data() const noexcept { + return size() ? storage.data() : nullptr; + } + + /* + * Iterators + */ + constexpr iterator begin() noexcept { + return iterator(storage.begin()); + } + + constexpr const_iterator begin() const noexcept { + return const_iterator(storage.begin()); + } + + /* + * Capacity + */ + constexpr size_type size() const noexcept { + return N; + } + + /* + * Operations + */ + constexpr void swap(array& other) noexcept { + std::swap(this->storage, other.storage); + } + + constexpr operator bit_value() const noexcept + requires(std::is_same_v && N == 1) + { + return this->begin()[0]; + } +}; + +static_assert(bit_range>, "array does not satisfy bit_range concept!"); +static_assert(bit_sized_range>, "array does not satisfy bit_sized_range concept!"); +#ifdef CONTIGUOUS_RANGE +static_assert(bit_contiguous_range>, "array does not satisfy bit_contiguous_range concept!"); +static_assert(bit_contiguous_sized_range>, "array does not satisfy bit_contiguous_sized_range concept!"); +#endif + +#if 0 +// Class Template Argument Deduction +// CTAD guide for the constructor taking a word_type&, +// deducing Extent as bitsof(). +template +array(word_type&) -> array()>; +template +array(word_type*) -> array()>; + +// CTAD guide for the constructor taking a word_type* and a size, +// which should always be used when Extent is std::dynamic_extent. +template +array(word_type&, std::size_t) -> array; +template +array(word_type*, std::size_t) -> array; +#endif + +} // namespace bit + +#endif // _array_HPP_INCLUDED \ No newline at end of file diff --git a/include/bitlib/bit-containers/bit_array_base.hpp b/include/bitlib/bit-containers/bit_array_base.hpp new file mode 100644 index 00000000..842ef00a --- /dev/null +++ b/include/bitlib/bit-containers/bit_array_base.hpp @@ -0,0 +1,346 @@ +// ================================= BIT_ARRAY_BASE =================================== // +// Project: The Experimental Bit Algorithms Library +// \file bit_array_base.hpp +// Description: Base implementation for bit_array variants +// Creator: Vincent Reverdy +// Contributor: Peter McLean [2025] +// License: BSD 3-Clause License +// ========================================================================== // +#ifndef _BIT_ARRAY_BASE_HPP_INCLUDED +#define _BIT_ARRAY_BASE_HPP_INCLUDED +// ========================================================================== // + +// ================================ PREAMBLE ================================ // +// C++ standard library +#include +#include +#include +#include +#include +#include +#include + +// Project sources +#include "bitlib/bit-algorithms/bit_algorithm.hpp" +#include "bitlib/bit-containers/bit_bitsof.hpp" +#include "bitlib/bit-containers/bit_policy.hpp" +#include "bitlib/bit-iterator/bit.hpp" + +namespace bit { + +template +class array_ref; + +template +class array; + +/** + * @brief Base class template for array implementations + * + * This is a CRTP (Curiously Recurring Template Pattern) base class that provides + * common functionality for array variants. + * + * @tparam Derived The derived class (CRTP pattern) + * @tparam T The value type (typically bit_value) + * @tparam W The word type used for storage + * @tparam N The size of the bit array, or std::dynamic_extent for dynamic size + * @tparam Policy The policy for integral conversion (default is typical) + * @tparam Iterators A struct that provides iterator and const_iterator types + */ +template +class array_base : public detail::container_size_storage { + protected: + constexpr Derived& derived() noexcept { + return static_cast(*this); + } + + constexpr const Derived& derived() const noexcept { + return static_cast(*this); + } + + public: + using detail::container_size_storage::size; + using word_type = W; + using value_type = T; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using reference = typename std::conditional, bit_reference, T&>::type; + using const_reference = typename std::conditional, const bit_reference, const T&>::type; + using pointer = typename std::conditional, bit_pointer, T&>::type; + using const_pointer = const pointer; + using iterator = Iterators::iterator; + using const_iterator = Iterators::const_iterator; + + constexpr array_base() noexcept : detail::container_size_storage() {} + constexpr array_base(const size_type& extent) noexcept + requires(N == std::dynamic_extent) + : detail::container_size_storage(extent) {} + + // Element access + constexpr reference operator[](size_type pos) { + return derived().begin()[pos]; + } + + constexpr const_reference operator[](size_type pos) const { + return derived().begin()[pos]; + } + + constexpr reference at(size_type pos) { + if (pos < size()) { + return derived().begin()[pos]; + } else { + throw std::out_of_range("Position is out of range"); + } + } + + constexpr const_reference at(size_type pos) const { + if (pos < size()) { + return derived().begin()[pos]; + } else { + throw std::out_of_range("Position is out of range"); + } + } + + constexpr reference front() { + return derived().begin()[0]; + } + + constexpr const_reference front() const { + return derived().begin()[0]; + } + + constexpr reference back() { + return derived().begin()[size() - 1]; + } + + constexpr const_reference back() const { + return derived().begin()[size() - 1]; + } + + constexpr iterator end() noexcept { + return derived().begin() + size(); + } + + constexpr const_iterator end() const noexcept { + return const_iterator(derived().begin()) + size(); + } + + constexpr const_iterator cbegin() const noexcept { + return const_iterator(derived().begin()); + } + + constexpr const_iterator cend() const noexcept { + return const_iterator(derived().end()); + } + + // Capacity + constexpr bool empty() const noexcept { + return 0 == size(); + } + + constexpr size_type max_size() const noexcept { + return size(); + } + + // String representation + constexpr std::string debug_string() const { + return debug_string(derived().begin(), derived().end()); + } + + constexpr std::string debug_string(const_iterator first, const_iterator last) const { + std::string ret = ""; + auto position = 0; + for (auto it = first; it != last; ++it) { + if (position % bitsof() == 0 && position != 0) { + ret += " "; + } else if (position % 8 == 0 && position != 0) { + ret += '.'; + } + ret += *it == bit1 ? '1' : '0'; + ++position; + } + return ret; + } + + /** + * @brief Slice operations - returns a array_ref + */ + constexpr auto operator()(size_type offset, size_type right) const noexcept { + return array_ref(&this->at(offset), right - offset); + } + + /** + * @brief Slice operations - returns a array_ref + */ + constexpr auto operator()(size_type offset, size_type right) noexcept { + return array_ref(&this->at(offset), right - offset); + } + + // Common operations + constexpr void fill(value_type bit_val) noexcept { + std::fill(derived().begin(), derived().end(), bit_val); + } + + /** + * @brief Explicit conversion to integral types + */ + template + explicit constexpr operator U() const noexcept { + assert(size() <= bitsof()); + U integral; + + if constexpr (N == std::dynamic_extent) { + if (size() > bitsof()) { + Policy::truncation::template to_integral(derived(), integral); + } else { + ::bit::copy(derived().begin(), end(), &integral); + } + if (size() < bitsof()) { + Policy::extension::template to_integral(detail::uninitialized, derived(), integral); + } + } else { + if constexpr (N > bitsof()) { + Policy::truncation::template to_integral(derived(), integral); + } else { + ::bit::copy(derived().begin(), end(), &integral); + } + if constexpr (N < bitsof()) { + Policy::extension::template to_integral(detail::uninitialized, derived(), integral); + } + } + + return integral; + } + + using compatible_bitarray = array; + + constexpr compatible_bitarray operator~() { + compatible_bitarray result(detail::uninitialized, size()); + transform(derived().begin(), derived().end(), result.begin(), [](const word_type& bits) -> word_type { return ~bits; }); + return result; + } + + constexpr compatible_bitarray operator|(const bit_sized_range auto& other) const { + assert(other.size() == size()); + compatible_bitarray result(detail::uninitialized, size()); + transform(derived().begin(), derived().end(), other.begin(), result.begin(), + [](const word_type& a, const word_type& b) -> word_type { return a | b; }); + return result; + } + constexpr Derived& operator|=(const bit_sized_range auto& other) { + assert(other.size() == size()); + transform(derived().cbegin(), derived().cend(), other.begin(), derived().begin(), + [](const word_type& a, const word_type& b) -> word_type { return a | b; }); + return derived(); + } + constexpr compatible_bitarray operator&(const bit_sized_range auto& other) const { + assert(other.size() == size()); + compatible_bitarray result(detail::uninitialized, size()); + transform(derived().begin(), derived().end(), other.begin(), result.begin(), + [](const word_type& a, const word_type& b) -> word_type { return a & b; }); + return result; + } + constexpr Derived& operator&=(const bit_sized_range auto& other) { + assert(other.size() == size()); + transform(derived().cbegin(), derived().cend(), other.begin(), derived().begin(), + [](const word_type& a, const word_type& b) -> word_type { return a & b; }); + return derived(); + } + constexpr compatible_bitarray operator^(const bit_sized_range auto& other) const { + assert(other.size() == size()); + compatible_bitarray result(detail::uninitialized, size()); + transform(derived().begin(), derived().end(), other.begin(), result.begin(), + [](const word_type& a, const word_type& b) -> word_type { return a ^ b; }); + return result; + } + constexpr Derived& operator^=(const bit_sized_range auto& other) { + assert(other.size() == size()); + transform(derived().cbegin(), derived().cend(), other.begin(), derived().begin(), + [](const word_type& a, const word_type& b) -> word_type { return a ^ b; }); + return derived(); + } + + friend std::ostream& operator<<(std::ostream& os, const array_base& cv) { + // Save stream formatting settings + std::ios_base::fmtflags flags = os.flags(); + char fill = os.fill(); + std::streamsize width = os.width(); + + std::string content; + switch (flags & std::ios_base::basefield) { + case std::ios_base::hex: + content = ::bit::to_string(cv.derived(), ::bit::string::typical(16)); + break; + case std::ios_base::oct: + content = ::bit::to_string(cv.derived(), ::bit::string::typical(8)); + break; + case std::ios_base::dec: + content = ::bit::to_string(cv.derived(), ::bit::string::typical(10)); + break; + default: + content = ::bit::to_string(cv.derived(), ::bit::string::typical(2)); + break; + } + + // Clear width to avoid affecting next output + os.width(0); + + // Apply padding manually if needed + if (width > static_cast(content.size())) { + std::streamsize pad = width - content.size(); + bool left = (flags & std::ios_base::adjustfield) == std::ios_base::left; + + if (!left) { + os << std::string(pad, fill); + } + + os << content; + + if (left) { + os << std::string(pad, fill); + } + } else { + os << content; + } + + return os; + } + + protected: + template + constexpr void from_integral(const U& integral) { + if constexpr (N == std::dynamic_extent) { + if ((size() * bitsof()) < bitsof()) { + Policy::truncation::template from_integral(integral, derived()); + } else { + ::bit::copy(&integral, &integral + 1, derived().begin()); + } + if (bitsof() < (size() * bitsof())) { + Policy::extension::template from_integral(detail::uninitialized, integral, derived()); + } + } else { + if constexpr ((N * bitsof()) < bitsof()) { + Policy::truncation::template from_integral(integral, derived()); + } else { + ::bit::copy(&integral, &integral + 1, derived().begin()); + } + if constexpr (bitsof() < (N * bitsof())) { + Policy::extension::template from_integral(detail::uninitialized, integral, derived()); + } + } + } +}; + +constexpr bool operator==(const bit_sized_range auto& lhs, const bit_sized_range auto& rhs) { + if (lhs.size() != rhs.size()) { + return false; + } + return ::bit::equal(lhs.begin(), lhs.end(), rhs.begin()); +} + +} // namespace bit + +#endif // _BIT_ARRAY_BASE_HPP_INCLUDED diff --git a/include/bitlib/bit-containers/bit_array_dynamic_extent.hpp b/include/bitlib/bit-containers/bit_array_dynamic_extent.hpp new file mode 100644 index 00000000..67b428e9 --- /dev/null +++ b/include/bitlib/bit-containers/bit_array_dynamic_extent.hpp @@ -0,0 +1,336 @@ +// ================================= BIT_ARRAY =================================== // +// Project: The Experimental Bit Algorithms Library +// \file bit_array_dynamic_extent.hpp +// Description: Implementation of bit_array +// Creator: Vincent Reverdy +// Contributor: Peter McLean [2025] +// License: BSD 3-Clause License +// ========================================================================== // +#ifndef _BIT_ARRAY_DYNAMIC_EXTENT_HPP_INCLUDED +#define _BIT_ARRAY_DYNAMIC_EXTENT_HPP_INCLUDED + +#include +#include +#include +#include +#include +#include // std::dynamic_extent +#include + +#include "bitlib/bit-algorithms/bit_algorithm.hpp" +#include "bitlib/bit-containers/bit_array.hpp" +#include "bitlib/bit-containers/bit_array_base.hpp" +#include "bitlib/bit-containers/bit_bitsof.hpp" +#include "bitlib/bit-containers/bit_policy.hpp" +#include "bitlib/bit-iterator/bit.hpp" +#include "bitlib/bit_concepts.hpp" + +namespace bit { +namespace detail { + +template +struct array_dextent_iterator_types { + using iterator = typename std::conditional, + bit_iterator, + word_type*>::type; + using const_iterator = typename std::conditional, + bit_iterator, + const word_type*>::type; +}; +} // namespace detail +template +class array + : public array_base, T, std::dynamic_extent, W, false, Policy, detail::array_dextent_iterator_types> { + public: + using base = array_base, T, std::dynamic_extent, W, false, Policy, detail::array_dextent_iterator_types>; + using base::end; + using base::size; + using typename base::const_iterator; + using typename base::const_pointer; + using typename base::const_reference; + using typename base::difference_type; + using typename base::iterator; + using typename base::pointer; + using typename base::reference; + using typename base::size_type; + using typename base::value_type; + using typename base::word_type; + using Allocator = typename Policy::allocator; + + private: + using word_type_ptr = word_type*; +#if defined(__clang__) || defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsizeof-pointer-div" +#endif + static const size_type FixedWords = sizeof(word_type_ptr) / sizeof(word_type); +#if defined(__clang__) || defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + static const size_type FixedBits = FixedWords * bitsof(); + + struct Storage { + union { + word_type_ptr pointer; + word_type fixed[FixedWords]; + }; + Allocator m_allocator; + + Storage(size_type words, const Allocator& allocator, detail::uninitialized_t) : m_allocator(allocator) { + if (words > FixedWords) { + new (&pointer) word_type_ptr(m_allocator.allocate(words)); + } + } + + Storage(size_type words, const Allocator& allocator = Allocator()) : m_allocator(allocator) { + if (words > FixedWords) { + new (&pointer) word_type_ptr(m_allocator.allocate(words)); + for (size_type i = 0; i < words; ++i) { + new (pointer + i) word_type(); + } + } else { + for (size_type i = 0; i < words; ++i) { + new (&fixed[i]) word_type(); + } + } + } + + Storage(size_type words, Storage&& other) { + if (words > FixedWords) { + new (&pointer) word_type_ptr(std::move(other.pointer)); + other.pointer = nullptr; // Prevent double deletion + } else { + for (size_type i = 0; i < words; ++i) { + new (&fixed[i]) word_type(std::move(other.fixed[i])); + } + } + } + + Storage(size_type words, const Storage& other, const Allocator& allocator = Allocator()) + : m_allocator(allocator) { + if (words > FixedWords) { + new (&pointer) word_type_ptr(m_allocator.allocate(words)); + for (size_t i = 0; i < words; ++i) { + new (pointer + i) word_type(other.pointer[i]); + } + std::copy_n(other.pointer, words, pointer); + } else { + for (size_type i = 0; i < words; ++i) { + new (&fixed[i]) word_type(other.fixed[i]); + } + } + } + template + Storage(size_type words, const U& val, const Allocator& allocator = Allocator()) + : m_allocator(allocator) { + if (words > FixedWords) { + new (&pointer) word_type_ptr(m_allocator.allocate(words)); + for (size_t i = 0; i < words; ++i) { + new (pointer + i) word_type(val); + } + } else { + for (size_type i = 0; i < words; ++i) { + new (&fixed[i]) word_type(val); + } + } + } + Storage() = delete; + ~Storage() {}; + } storage; + + static constexpr size_type Words(size_type N) { + return (N * bitsof() + bitsof() - 1) / bitsof(); + }; + + public: + ~array() { + if (size() > FixedBits) { + storage.m_allocator.deallocate(storage.pointer, Words(size())); + } else if constexpr (!std::is_fundamental_v) { + for (size_type i = 0; i < Words(size()); ++i) { + // W is 'word_type', but MSVC is unhappy with using an alias + storage.fixed[i].~W(); + } + } + } + + /* + * Constructors, copies and moves... + */ + array() = delete; + + constexpr array(const size_type extent, const Allocator& allocator = Allocator()) + : base(extent), storage(Words(extent), allocator) { + } + + constexpr array(detail::uninitialized_t, const size_type extent, const Allocator& allocator = Allocator()) + : base(extent), storage(Words(extent), allocator, detail::uninitialized) { + } + + template + constexpr array(const size_type extent, const U& integral, const Allocator& allocator = Allocator()) + : base(extent), storage(Words(extent), allocator, detail::uninitialized) { + this->from_integral(integral); + } + + constexpr array(const size_type extent, const word_type val, const Allocator& allocator = Allocator()) + : base(extent), storage(Words(extent), val, allocator) { + } + + constexpr array(const size_type extent, const value_type bit_val, const Allocator& allocator = Allocator()) + requires(!std::is_same::value) + : base(extent), storage(Words(extent), allocator, detail::uninitialized) { + this->fill(bit_val); + } + + constexpr array(const array& other) + : base(other.size()), storage(Words(size()), other.storage) { + } + + constexpr array(const array& other, const Allocator& allocator) + : base(other.size()), storage(Words(size()), other.storage, allocator) { + } + + constexpr array(const bit_sized_range auto& other, const Allocator& allocator = Allocator()) + : base(other.size()), storage(Words(size()), allocator, detail::uninitialized) { + ::bit::copy(other.begin(), other.end(), this->begin()); + } + + constexpr array(array&& other) + : base(other.size()), storage(Words(size()), std::move(other.storage)) { + } + + constexpr array(array&& other, const Allocator& allocator) + : base(other.size()), storage(Words(size()), std::move(other.storage), allocator) { + } + + constexpr array(const std::initializer_list init, const Allocator& allocator = Allocator()) + requires(!std::is_same_v) + : base(init.size()), storage(Words(size()), allocator, detail::uninitialized) { + std::copy(init.begin(), init.end(), this->begin()); + } + +#if 0 + No known conversion from bool to bit_value + bit_value has explicit constructor from bool to bit_value so this doesnt work + constexpr array::array(const std::initializer_list init) + : storage(std::make_unique(Words(init.size()))), + base(init.size()) { + std::copy(init.begin(), init.end(), this->begin()); + } +#endif + + template + constexpr array(const std::initializer_list init, const Allocator& allocator = Allocator()) + : base(bitsof() * init.size()), storage(Words(size()), allocator, detail::uninitialized) { + std::copy(init.begin(), init.end(), data()); + } + + constexpr array(const std::string_view s, const Allocator& allocator = Allocator()) + requires(std::is_same_v) + : base(std::count(s.begin(), s.end(), '0') + std::count(s.begin(), s.end(), '1')), storage(Words(size()), allocator, detail::uninitialized) { + size_type i = 0; + for (char c : s) { + if (c == '0') { + begin()[i++] = bit0; + } else if (c == '1') { + begin()[i++] = bit1; + } + } + } + + /* + * Assignment + */ + constexpr array& operator=(const array& other) { + if (nullptr == data() || size() != other.size()) { + throw std::invalid_argument("Cannot reassign array extent"); + } + if (this == &other) [[unlikely]] { + return *this; + } + std::copy(other.begin(), other.end(), this->begin()); + return *this; + } + + constexpr array& operator=(const bit_sized_range auto& other) { + if (other.size() != this->size()) [[unlikely]] { + throw std::invalid_argument("other bit_range contains an invalid number of bits for array."); + } + ::bit::copy(other.begin(), other.end(), this->begin()); + return *this; + }; + + constexpr array& operator=(array&& other) { + if (nullptr == data() || size() != other.size()) { + throw std::invalid_argument("Cannot reassign array extent"); + } + array temp(std::move(other)); + swap(temp); + return *this; + } + + /* + * Element Access + */ + constexpr word_type* data() noexcept { + if (size() > FixedBits) { + return storage.pointer; + } else { + return storage.fixed; + } + } + + constexpr const word_type* data() const noexcept { + if (size() > FixedBits) { + return storage.pointer; + } else { + return storage.fixed; + } + } + + /* + * Iterators + */ + constexpr iterator begin() noexcept { + if (size() > FixedBits) { + return iterator(storage.pointer); + } else { + return iterator(storage.fixed); + } + } + + constexpr const_iterator begin() const noexcept { + if (size() > FixedBits) { + return const_iterator(storage.pointer); + } else { + return const_iterator(storage.fixed); + } + } + + /* + * Operations + */ + constexpr void swap(array& other) noexcept { + assert(size() == other.size()); + if (size() > FixedBits) { + std::swap(this->storage.pointer, other.storage.pointer); + } else { + for (size_type i = 0; i < Words(size()); ++i) { + std::swap(this->storage.fixed[i], other.storage.fixed[i]); + } + } + } +}; + +static_assert(bit_range>, "array<> does not satisfy bit_contiguous_range concept!"); +static_assert(bit_sized_range>, "array<> does not satisfy bit_contiguous_sized_range concept!"); +#ifdef CONTIGUOUS_RANGE +static_assert(bit_contiguous_range>, "array<> does not satisfy bit_contiguous_range concept!"); +static_assert(bit_contiguous_sized_range>, "array<> does not satisfy bit_contiguous_sized_range concept!"); +#endif + +// ========================================================================== // +} // namespace bit + +#endif // _BIT_ARRAY_DYNAMIC_EXTENT_HPP_INCLUDED diff --git a/include/bitlib/bit-containers/bit_array_ref.hpp b/include/bitlib/bit-containers/bit_array_ref.hpp new file mode 100644 index 00000000..796858a4 --- /dev/null +++ b/include/bitlib/bit-containers/bit_array_ref.hpp @@ -0,0 +1,222 @@ +// ================================= BIT_ARRAY_REF =================================== // +// Project: The Experimental Bit Algorithms Library +// \file bit_array_ref.hpp +// Description: Implementation of bit_array_ref +// Creator: Vincent Reverdy +// Contributor: Peter McLean [2025] +// License: BSD 3-Clause License +// ========================================================================== // +#ifndef _BIT_ARRAY_REF_HPP_INCLUDED +#define _BIT_ARRAY_REF_HPP_INCLUDED + +#include +#include +#include +#include +#include // std::dynamic_extent +#include + +#include "bitlib/bit-algorithms/bit_algorithm.hpp" +#include "bitlib/bit-containers/bit_array_base.hpp" +#include "bitlib/bit-containers/bit_bitsof.hpp" +#include "bitlib/bit-containers/bit_policy.hpp" +#include "bitlib/bit-iterator/bit.hpp" +#include "bitlib/bit_concepts.hpp" + +namespace bit { + +template >> +class array_ref; + +template >> +using bit_array_ref = array_ref; + +namespace detail { +template +struct array_ref_iterator_types { + using iterator = bit_iterator; + using const_iterator = bit_iterator; +}; +} // namespace detail +/** + * @brief A non-owning reference to a bit array + * + * Similar to array_dynamic_extent but does not allocate or deallocate memory. + * The pointer and size are const class members and cannot be re-bound. + * Assignment operations always copy content and can't rebind the pointer/size. + * + * @tparam T The value type (typically bit_value) + * @tparam W The word type used for storage + */ +template +class array_ref + : public array_base, T, N, W, false, Policy, detail::array_ref_iterator_types> { + public: + using base = array_base, T, N, W, false, Policy, detail::array_ref_iterator_types>; + using base::end; + using base::size; + using typename base::const_iterator; + using typename base::const_pointer; + using typename base::const_reference; + using typename base::difference_type; + using typename base::iterator; + using typename base::pointer; + using typename base::reference; + using typename base::size_type; + using typename base::value_type; + using typename base::word_type; + + private: + const bit_pointer m_storage; + + public: + // Constructors + array_ref() = delete; + + /** + * @brief Constructs a non-owning reference to a bit array + * + * @param storage Pointer to the storage + * @param extent Number of bits + */ + constexpr array_ref(word_type* storage, size_type extent) + requires(N == std::dynamic_extent) + : base(extent), m_storage(storage) { + } + constexpr array_ref(word_type* storage) + requires(N != std::dynamic_extent) + : base(), m_storage(storage) { + } + + /** + * @brief Constructs a non-owning reference to a bit array using a bit_pointer + * + * @param storage bit_pointer to the storage + * @param extent Number of bits + */ + constexpr array_ref(bit_pointer storage, size_type extent) + requires(N == std::dynamic_extent) + : base(extent), m_storage(storage) { + } + constexpr array_ref(bit_pointer storage) + requires(N != std::dynamic_extent) + : base(), m_storage(storage) { + } + + /** + * @brief Constructs a non-owning reference to a bit array from a bit_sized_range + * + * @param other bit_sized_range + */ + constexpr array_ref(bit_range auto& other, size_type extent) + requires(N == std::dynamic_extent) + : base(extent), m_storage(&(*other.begin())) { + assert(extent <= static_cast(other.end() - other.begin())); + } + constexpr array_ref(bit_range auto& other) + requires(N != std::dynamic_extent) + : base(), m_storage(&(*other.begin())) { + assert(N <= static_cast(other.end() - other.begin())); + } + + /** + * @brief Constructs a non-owning reference to a bit array from a bit_sized_range + * + * @param other bit_sized_range + */ + constexpr array_ref(const bit_range auto& other, size_type extent) + requires((N == std::dynamic_extent) && std::is_const_v) + : base(extent), m_storage(&(*other.begin())) { + assert(extent <= (other.end() - other.begin())); + } + constexpr array_ref(const bit_range auto& other) + requires((N != std::dynamic_extent) && std::is_const_v) + : base(), m_storage(&(*other.begin())) { + assert(N <= (other.end() - other.begin())); + } + + /** + * @brief Copy constructor + */ + constexpr array_ref(const array_ref& other) = default; + + /** + * @brief Move constructor + */ + constexpr array_ref(array_ref&& other) = default; + + /** + * @brief Range Assignment operator - copies content but doesn't rebind + */ + constexpr array_ref& operator=(const bit_sized_range auto& other) { + if (size() != other.size()) { + throw std::invalid_argument("Cannot assign from array_ref of different size"); + } + ::bit::copy(other.begin(), other.end(), this->begin()); + return *this; + } + + /** + * @brief Copy Assignment operator - copies content but doesn't rebind + */ + constexpr array_ref& operator=(const array_ref& other) { + if (this != &other) { + if (size() != other.size()) { + throw std::invalid_argument("Cannot assign from array_ref of different size"); + } + ::bit::copy(other.begin(), other.end(), this->begin()); + } + return *this; + } + + /** + * @brief Move assignment operator - copies content but doesn't rebind + */ + constexpr array_ref& operator=(array_ref&& other) { + if (this != &other) { + if (size() != other.size()) { + throw std::invalid_argument("Cannot assign from array_ref of different size"); + } + ::bit::copy(other.begin(), other.end(), this->begin()); + } + return *this; + } + + /** + * @brief No destructor needed as we don't own the memory + */ + ~array_ref() = default; + + /* + * Iterators + */ + constexpr iterator begin() noexcept { + return iterator(m_storage); + } + + constexpr const_iterator begin() const noexcept { + return const_iterator(m_storage); + } + + /* + * Operations + */ + constexpr void swap(array_ref& other) { + if (size() != other.size()) { + throw std::invalid_argument("Cannot swap array_ref of different sizes"); + } + swap_ranges(begin(), end(), other.begin()); + } +}; + +static_assert(bit_range>, "array_ref<> does not satisfy bit_range concept!"); +static_assert(bit_sized_range>, "array_ref<> does not satisfy bit_sized_range concept!"); +#ifdef CONTIGUOUS_RANGE +static_assert(bit_contiguous_range>, "array_ref<> does not satisfy bit_contiguous_range concept!"); +static_assert(bit_contiguous_sized_range>, "array_ref<> does not satisfy bit_contiguous_sized_range concept!"); +#endif + +// ========================================================================== // +} // namespace bit + +#endif // _BIT_ARRAY_REF_HPP_INCLUDED \ No newline at end of file diff --git a/include/bitlib/bit-containers/bit_bitsof.hpp b/include/bitlib/bit-containers/bit_bitsof.hpp new file mode 100644 index 00000000..ca369407 --- /dev/null +++ b/include/bitlib/bit-containers/bit_bitsof.hpp @@ -0,0 +1,52 @@ +#ifndef _BIT_BITSOF_HPP_INCLUDED +#define _BIT_BITSOF_HPP_INCLUDED + +#include +#include + +/* +A bitsof() template function that resembles sizeof() as much as possible. +Possible calling styles: + - `bitsof(_var_)` + - `bitsof<_type>()` + - `bitsof(_type())` <- Default constructor required + - `bitsof(_type{})` <- Default constructor required +When calling within template code with a type, the callee must use the `bitsof()` or `bitsof(type())` form +*/ + +namespace bit { + +// Concept to check for a public static constexpr size_t bits member. +template +concept HasBits = requires { + { T::bits } -> std::convertible_to; +}; + +// General case: for types that do not have a bits member. +template + requires(!HasBits && !std::is_integral_v) +constexpr std::size_t bitsof() noexcept { + return 8u * sizeof(T); +} + +template + requires(!HasBits && std::is_integral_v) +constexpr std::size_t bitsof() noexcept { + return std::integral_constant>::digits>::value; +} + +// Overload for types with a bits member. +template requires HasBits +constexpr std::size_t bitsof() noexcept { + return T::bits; +} + +// Overload for objects of any non-bits type. +template +constexpr std::size_t bitsof(const T&) noexcept{ + return bitsof(); +} + +} // namespace bit + +#endif diff --git a/include/bitlib/bit-containers/bit_literal.hpp b/include/bitlib/bit-containers/bit_literal.hpp new file mode 100644 index 00000000..53b20258 --- /dev/null +++ b/include/bitlib/bit-containers/bit_literal.hpp @@ -0,0 +1,148 @@ +// ================================= array =================================== // +// Project: The Experimental Bit Algorithms Library +// \file bit_literal.hpp +// Description: Implementation of array user defined literal +// Creator: Vincent Reverdy +// Contributor: Peter McLean [2025] +// License: BSD 3-Clause License +// ========================================================================== // +#ifndef _BIT_LITERAL_HPP_INCLUDED +#define _BIT_LITERAL_HPP_INCLUDED + +// ================================ PREAMBLE ================================ // +// C++ standard library +#include + +// Project sources +#include "bitlib/bit-containers/bit_array.hpp" +#include "bitlib/bit-containers/bit_policy.hpp" +#include "bitlib/bit-iterator/bit_details.hpp" + +namespace bit { + +class bit_value; + +template +class array; + +template +using bit_array = array; + +// ========================================================================== // +template +constexpr void _parameter_pack_base_bits_prefix_len(size_t& base, size_t& bits, size_t& prefix_len, uint64_t& num) { + if ('\'' == Bit) { + if (0 != bits) { + // This happens when there are more than one apostrophe in the sequence. + // We ignore this apostrophe, it should be part of the 'data', not the width. + return; + } + bits = num; + num = 0; + ++prefix_len; + if (0 == base) { + base = 10; + } + } else if (0 == base) { + if (Bit == 'b' || Bit == 'B') { + base = 2; + num = 0; + } else if (Bit == 'x' || Bit == 'X') { + base = 16; + num = 0; + } else { + num = (num * 10) + (Bit - '0'); + } + ++prefix_len; + } else { + uint8_t decimal; + switch (base) { + case 2: + decimal = static_cast(Bit - '0'); + break; + case 10: + decimal = static_cast(Bit - '0'); + break; + case 16: + decimal = static_cast(Bit - '0'); + if (Bit >= 'a' && Bit <= 'f') { + decimal = static_cast(Bit - 'a') + 10u; + } + if (Bit >= 'A' && Bit <= 'F') { + decimal = static_cast(Bit - 'A') + 10u; + } + break; + } + num = num * base + decimal; + } +} + +template +constexpr void _parameter_pack_base_bits_prefix_len(size_t& base, size_t& bits, size_t& prefix_len, uint64_t& num) + requires(sizeof...(Bits) > 0) +{ + _parameter_pack_base_bits_prefix_len(base, bits, prefix_len, num); + _parameter_pack_base_bits_prefix_len(base, bits, prefix_len, num); +} + +template +constexpr std::pair _parameter_pack_decode_prefixed_num() { + size_t base = 0; + size_t bits = 0; + size_t prefix_len = 0; + uint64_t num = 0; + _parameter_pack_base_bits_prefix_len(base, bits, prefix_len, num); + size_t digits = (sizeof...(Bits) - prefix_len); + if (0 == base) { + base = 10; + digits = prefix_len; + prefix_len = 0; + } + if (0 == bits) { + if (base == 2) { + bits = digits; + } else if (base == 10) { + bits = std::bit_width(num); + } else { + bits = 4 * digits; + } + } + return {bits, num}; +} + +} // namespace bit + +#ifdef BITLIB_LITERAL_NAMESPACE +namespace BITLIB_LITERAL_NAMESPACE { +#endif + +#ifndef BITLIB_LITERAL_SUFFIX +#define _BITLIB_LITERAL_SUFFIX b +#else +#define _BITLIB_LITERAL_SUFFIX BITLIB_LITERAL_SUFFIX +#endif + +#define _BITLIB_CONCAT(x, y) x##y +#define _BITLIB_LITERAL_OPERATOR(suffix) _BITLIB_CONCAT(operator""_, suffix) + +template +constexpr auto _BITLIB_LITERAL_OPERATOR(_BITLIB_LITERAL_SUFFIX)() { + constexpr auto pair = bit::_parameter_pack_decode_prefixed_num(); + constexpr auto bits = pair.first; + constexpr auto num = pair.second; + static_assert( + (((1ull << bits) - 1ull) >= num), + "bit literal size prefix has too few bits to represent the given value"); + using word_type = bit::ceil_integral; + return bit::bit_array>(static_cast(num)); +} + +#undef _BITLIB_LITERAL_SUFFIX +#undef _BITLIB_LITERAL_OPERATOR +#undef _BITLIB_CONCAT + +#ifdef BITLIB_LITERAL_NAMESPACE +} +#endif + +#endif // _BIT_LITERAL_HPP_INCLUDED diff --git a/include/bitlib/bit-containers/bit_mdspan_accessor.hpp b/include/bitlib/bit-containers/bit_mdspan_accessor.hpp new file mode 100644 index 00000000..80076231 --- /dev/null +++ b/include/bitlib/bit-containers/bit_mdspan_accessor.hpp @@ -0,0 +1,60 @@ +// =============================== BIT VALUE ================================ // +// Project: The C++ Bit Library +// Name: bit_value.hpp +// Description: A class representing an independent, non-referenced bit +// Creator: Vincent Reverdy +// Contributor(s): Peter McLean [2025] +// License: BSD 3-Clause License +// ========================================================================== // +#ifndef _BIT_MDSPAN_ACCESSOR_HPP_INCLUDED +#define _BIT_MDSPAN_ACCESSOR_HPP_INCLUDED + +#include "bitlib/bit-iterator/bit_details.hpp" +#include "bitlib/bit-iterator/bit_reference.hpp" +#include "bitlib/bit-iterator/bit_value.hpp" + +namespace bit { +template +struct bit_default_accessor { + using element_type = bit_value; + using data_handle_type = bit_pointer; + using reference = bit_reference; + using offset_policy = bit_default_accessor; + constexpr reference access(data_handle_type p, std::size_t i) const noexcept { + return p[i]; + } + constexpr data_handle_type offset(data_handle_type p, std::size_t i) const noexcept { + return p + i; + } +}; + +template +struct bit_word_accessor : private detail::container_size_storage { + using element_type = bit_array; + using data_handle_type = bit_pointer; + using reference = bit_array_ref; + using offset_policy = bit_word_accessor; + constexpr reference access(data_handle_type p, std::size_t i) const noexcept + requires(N == std::dynamic_extent) + { + return reference(p + i * this->size(), this->size()); + } + constexpr reference access(data_handle_type p, std::size_t i) const noexcept + requires(N != std::dynamic_extent) + { + return reference(p + i * this->size()); + } + constexpr data_handle_type offset(data_handle_type p, std::size_t i) const noexcept { + return p + i * this->size(); + } + constexpr bit_word_accessor() + requires(N != std::dynamic_extent) + : detail::container_size_storage() {} + constexpr bit_word_accessor(const size_t& size) + requires(N == std::dynamic_extent) + : detail::container_size_storage(size) {} +}; + +} // namespace bit + +#endif diff --git a/include/bitlib/bit-containers/bit_policy.hpp b/include/bitlib/bit-containers/bit_policy.hpp new file mode 100644 index 00000000..0a4d31f7 --- /dev/null +++ b/include/bitlib/bit-containers/bit_policy.hpp @@ -0,0 +1,220 @@ +// ================================= BIT_ARRAY_REF =================================== // +// Project: The Experimental Bit Algorithms Library +// \file bit_array_ref.hpp +// Description: Implementation of bit_array_ref +// Creator: Vincent Reverdy +// Contributor: Peter McLean [2025] +// License: BSD 3-Clause License +// ========================================================================== // +#ifndef _BIT_POLICY_HPP_INCLUDED +#define _BIT_POLICY_HPP_INCLUDED + +#include "bitlib/bit_concepts.hpp" +#include "bitlib/bit-algorithms/bit_algorithm.hpp" +#include "bitlib/bit-iterator/bit.hpp" +#include "bitlib/bit-iterator/bit_details.hpp" + +namespace bit::policy { + +struct truncate; +struct sign_extend; +template +struct typical { + using allocator = std::allocator>; + using extension = sign_extend; + using truncation = truncate; +}; + +struct truncate { + template + constexpr static void to_integral(const bit_iterator& first, U& integral) noexcept { + bit_pointer integral_begin(&integral); + ::bit::copy(first, first + bitsof(), integral_begin); + } + + template + constexpr static void to_integral(const bit_range auto& value, U& integral) noexcept { + to_integral(value.begin(), integral); + } + + template + constexpr static void from_integral( + const U& integral, + const bit_iterator& first, + const bit_iterator& last) noexcept { + const bit_pointer integral_begin(&integral); + if constexpr (N == std::dynamic_extent) { + ::bit::copy(integral_begin, integral_begin + distance(first, last), first); + } else { + ::bit::copy(integral_begin, integral_begin + N, first); + } + } + + template + constexpr static void from_integral(const U& integral, bit_sized_range auto& value) noexcept { + const bit_pointer integral_begin(&integral); + if constexpr (N == std::dynamic_extent) { + ::bit::copy(integral_begin, integral_begin + value.size(), value.begin()); + } else { + ::bit::copy(integral_begin, integral_begin + N, value.begin()); + } + } +}; + +struct sign_extend { + template + constexpr static void to_integral( + detail::initialized_t, + const bit_iterator& first, + const bit_iterator& last, + U& integral) noexcept { + bit_pointer integral_begin(&integral); + if constexpr (N == std::dynamic_extent) { + if constexpr (std::is_signed_v) { + if (last[-1]) { + ::bit::fill(integral_begin + distance(first, last), integral_begin + bitsof(), bit1); + } + } + } else { + if constexpr (std::is_signed_v) { + if (first[N - 1]) { + ::bit::fill(integral_begin + N, integral_begin + bitsof(), bit1); + } + } + } + } + + template + constexpr static void to_integral( + detail::initialized_t, const bit_sized_range auto& value, U& integral) noexcept { + bit_pointer integral_begin(&integral); + if constexpr (N == std::dynamic_extent) { + if constexpr (std::is_signed_v) { + if (value.end()[-1]) { + ::bit::fill(integral_begin + value.size(), integral_begin + bitsof(), bit1); + } + } + } else { + if constexpr (std::is_signed_v) { + if (value.begin()[N - 1]) { + ::bit::fill(integral_begin + N, integral_begin + bitsof(), bit1); + } + } + } + } + + template + constexpr static void to_integral( + detail::uninitialized_t, + const bit_iterator& first, + const bit_iterator& last, U& integral) noexcept { + bit_pointer integral_begin(&integral); + if constexpr (N == std::dynamic_extent) { + if constexpr (std::is_signed_v) { + ::bit::fill(integral_begin + distance(first, last), integral_begin + bitsof(), last[-1]); + } + } else { + if constexpr (std::is_signed_v) { + ::bit::fill(integral_begin + N, integral_begin + bitsof(), first[N - 1]); + } + } + } + + template + constexpr static void to_integral( + detail::uninitialized_t, + const bit_sized_range auto& value, + U& integral) noexcept { + bit_pointer integral_begin(&integral); + if constexpr (N == std::dynamic_extent) { + if constexpr (std::is_signed_v) { + ::bit::fill(integral_begin + value.size(), integral_begin + bitsof(), value.end()[-1]); + } else { + ::bit::fill(integral_begin + value.size(), integral_begin + bitsof(), bit0); + } + } else { + if constexpr (std::is_signed_v) { + ::bit::fill(integral_begin + value.size(), integral_begin + bitsof(), value.begin()[N - 1]); + } else { + ::bit::fill(integral_begin + value.size(), integral_begin + bitsof(), bit0); + } + } + } + + template + constexpr static void to_integral(const bit_sized_range auto& value, U& integral) noexcept { + return to_integral(detail::uninitialized, value, integral); + } + + template + constexpr static void to_integral( + const bit_iterator& first, + const bit_iterator& last, + U& integral) noexcept { + return to_integral(detail::uninitialized, first, last, integral); + } + + template + constexpr static void from_integral( + detail::initialized_t, + const U& integral, + const bit_iterator& first, + const bit_iterator& last) noexcept { + if constexpr (N == std::dynamic_extent) { + if constexpr (std::is_signed_v) { + if (integral < 0) { + ::bit::fill(first + bitsof(), last, bit1); + } + } + } else { + if constexpr (std::is_signed_v) { + if (integral < 0) { + ::bit::fill(first + bitsof(), last, bit1); + } + } + } + } + + template + constexpr static void from_integral( + detail::uninitialized_t, + const U& integral, + const bit_iterator& first, + const bit_iterator& last) noexcept { + if constexpr (N == std::dynamic_extent) { + ::bit::fill(first + bitsof(), last, (integral < 0) ? bit1 : bit0); + } else { + ::bit::fill(first + N, last, (integral < 0) ? bit1 : bit0); + } + } + + template + constexpr static void from_integral( + const U& integral, + const bit_iterator& first, + const bit_iterator& last) noexcept { + from_integral(detail::uninitialized, integral, first, last); + } + + template + constexpr static void from_integral( + detail::uninitialized_t, + const U& integral, + bit_sized_range auto& value) noexcept { + from_integral(detail::uninitialized, integral, value.begin(), value.end()); + } + + template + constexpr static void from_integral(detail::initialized_t, const U& integral, bit_sized_range auto& value) noexcept { + from_integral(detail::initialized, integral, value.begin(), value.end()); + } + + template + constexpr static void from_integral(const U& integral, bit_sized_range auto& value) noexcept { + from_integral(detail::uninitialized, integral, value.begin(), value.end()); + } +}; + +} // namespace bit::policy + +#endif // _BIT_POLICY_HPP_INCLUDED diff --git a/include/bitlib/bit-containers/bit_span.hpp b/include/bitlib/bit-containers/bit_span.hpp new file mode 100644 index 00000000..cd4b61eb --- /dev/null +++ b/include/bitlib/bit-containers/bit_span.hpp @@ -0,0 +1,342 @@ +#ifndef _BIT_SPAN_HPP_INCLUDED +#define _BIT_SPAN_HPP_INCLUDED +#include +#include + +#include +#include +#include +#include +#include +#include +// Project sources +#include "bitlib/bit-algorithms/bit_algorithm.hpp" +#include "bitlib/bit-containers/bit_array_ref.hpp" +#include "bitlib/bit-containers/bit_bitsof.hpp" +#include "bitlib/bit-containers/bit_policy.hpp" +#include "bitlib/bit-iterator/bit.hpp" + +namespace bit { + +// Helper storage: for a fixed extent no runtime size is stored. +template +struct bit_span_storage { + constexpr std::size_t size() const noexcept { return Extent; } + bit_span_storage() = default; +}; + +// Specialization for dynamic_extent: store the size at runtime. +template +struct bit_span_storage { + std::size_t size_; + constexpr std::size_t size() const noexcept { return size_; } + bit_span_storage() : size_(0) {} + bit_span_storage(std::size_t size) : size_(size) {} +}; + +template >> +class bit_span : private bit_span_storage { + public: + using word_type = WordType; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using reference = bit_reference; + using const_reference = const bit_reference; + using pointer = bit_pointer; + using const_pointer = const pointer; + using iterator = bit_iterator; + using const_iterator = bit_iterator; + + private: + // The start of the span, represented as a bit_pointer. + pointer data_; + + template + static constexpr size_t subspan_extent() { + if constexpr (NewCount != std::dynamic_extent) + return NewCount; + else if constexpr (Extent != std::dynamic_extent) + return Extent - NewOffset; + else + return std::dynamic_extent; + } + + public: + // --- Constructors --- + + // Default constructor: + constexpr bit_span() noexcept; + + constexpr bit_span(const bit_span& other) noexcept; + + // Construct from a bit_pointer and a bit count. + constexpr bit_span(pointer data, size_type bit_count) noexcept + requires(Extent == std::dynamic_extent); + constexpr bit_span(pointer data) noexcept; + + // Construct from a WordType pointer and a bit count. + constexpr bit_span(WordType* word_ptr, size_type bit_count) noexcept + requires(Extent == std::dynamic_extent); + constexpr bit_span(WordType* word_ptr) noexcept; + constexpr bit_span(WordType& word_ref, size_type bit_count) noexcept + requires(Extent == std::dynamic_extent); + constexpr bit_span(WordType& word_ref) noexcept; + + constexpr bit_span(WordType& s) + requires(std::is_scalar_v); + constexpr bit_span(WordType* s) + requires(std::is_scalar_v); + + // --- Observers --- + + // Returns the number of bits in the span. + constexpr size_type size() const noexcept; + // Checks if the span is empty. + constexpr bool empty() const noexcept; + + // --- Element Access --- + + // Non-const element access. + constexpr reference operator[](size_type pos); + // Const element access returns a const_reference. + constexpr const_reference operator[](size_type pos) const; + // Bounds-checked access + constexpr reference at(size_type idx); + // Bounds-checked access + constexpr const_reference at(size_type idx) const; + + // Non-const front/back access. + constexpr reference front(); + constexpr reference back(); + + // Const front/back access returns a const_reference. + constexpr const_reference front() const; + constexpr const_reference back() const; + + // --- Iterators --- + + // Returns an iterator to the first bit. + constexpr iterator begin() const noexcept; + + // Returns an iterator just past the last bit. + constexpr iterator end() const noexcept; + + // --- Subspan Functionality --- + + // Creates a subspan starting at offset with count bits. + template + constexpr auto subspan() const noexcept -> bit_span(), Policy>; + constexpr bit_span subspan(size_type offset, size_type count = std::dynamic_extent) const noexcept + requires(Extent == std::dynamic_extent); + + constexpr bit_array_ref operator()(size_type begin, size_type end) const noexcept; + + template + constexpr bit_span first() const noexcept + requires(NewExtent != std::dynamic_extent); + constexpr bit_span first(size_type offset) const noexcept + requires(Extent == std::dynamic_extent); + + template + constexpr bit_span last() const noexcept + requires(RevOffset != std::dynamic_extent); + constexpr bit_span last(size_type offset) const noexcept + requires(Extent == std::dynamic_extent); +}; + +static_assert(bit_range>, "bit_span does not satisfy bit_range concept!"); +static_assert(bit_sized_range>, "bit_span does not satisfy bit_sized_range concept!"); +static_assert(bit_range>, "bit_span does not satisfy bit_range concept!"); +static_assert(bit_sized_range>, "bit_span does not satisfy bit_sized_range concept!"); + +// Class Template Argument Deduction + +// CTAD guide for the constructor taking a WordType&, +// deducing Extent as bitsof(). +template +bit_span(WordType&) -> bit_span()>; +template +bit_span(WordType*) -> bit_span()>; + +// CTAD guide for the constructor taking a WordType* and a size, +// which should always be used when Extent is std::dynamic_extent. +template +bit_span(WordType&, std::size_t) -> bit_span; +template +bit_span(WordType*, std::size_t) -> bit_span; + +// --- Constructors --- + +// Default constructor: +// For dynamic extent, we start with a null pointer and size zero. +// For fixed extent, the pointer is null but the size is always Extent. +template +constexpr bit_span::bit_span() noexcept : bit_span_storage(), data_(nullptr) {} + +template +constexpr bit_span::bit_span(const bit_span& other) noexcept : bit_span_storage(other), data_(other.data_) { +} + +// Construct from a bit_pointer and a bit count. +template +constexpr bit_span::bit_span(pointer data, size_type bit_count) noexcept + requires(Extent == std::dynamic_extent) + : bit_span_storage(bit_count), data_(data) {} + +// Construct from a WordType pointer and a word count. +// This converts the word count to a bit count using bitsof. +template +constexpr bit_span::bit_span(WordType* word_ptr, size_type bit_count) noexcept + requires(Extent == std::dynamic_extent) + : bit_span_storage(bit_count), data_(pointer(word_ptr, 0)) {} + +template +constexpr bit_span::bit_span(WordType& word_ref, size_type bit_count) noexcept + requires(Extent == std::dynamic_extent) + : bit_span(&word_ref, bit_count) {} + +// Construct from a bit_pointer and a bit count. +template +constexpr bit_span::bit_span(pointer data) noexcept : bit_span_storage(), data_(data) {} + +template +constexpr bit_span::bit_span(WordType* word_ptr) noexcept : bit_span_storage(), data_(pointer(word_ptr, 0)) {} + +template +constexpr bit_span::bit_span(WordType* word_ptr) + requires(std::is_scalar_v) + : bit_span_storage(), data_(word_ptr) { +} + +template +constexpr bit_span::bit_span(WordType& word_ref) + requires(std::is_scalar_v) + : bit_span(&word_ref) { +} + +// --- Observers --- + +// Returns the number of bits in the span. +template +constexpr bit_span::size_type bit_span::size() const noexcept { + if constexpr (Extent == std::dynamic_extent) { + return this->size_; + } else { + return Extent; + } +} + +// Checks if the span is empty. +template +constexpr bool bit_span::empty() const noexcept { return size() == 0; } + +// --- Element Access --- + +// Non-const element access. +template +constexpr bit_span::reference bit_span::operator[](size_type pos) { return begin()[pos]; } + +// Const element access returns a const_reference. +template +constexpr bit_span::const_reference bit_span::operator[](size_type pos) const { return begin()[pos]; } + +template +constexpr bit_span::reference bit_span::at(size_type pos) { + if (pos >= size()) { + throw std::out_of_range("span::at - index out of range"); + } + return data_[pos]; +} + +// Const element access returns a const_reference. +template +constexpr bit_span::const_reference bit_span::at(size_type pos) const { + if (pos >= size()) { + throw std::out_of_range("span::at - index out of range"); + } + return data_[pos]; +} + +// Non-const front/back access. +template +constexpr bit_span::reference bit_span::front() { return begin()[0]; } +template +constexpr bit_span::reference bit_span::back() { return begin()[size() - 1]; } + +// Const front/back access returns a const_reference. +template +constexpr bit_span::const_reference bit_span::front() const { return begin()[0]; } +template +constexpr bit_span::const_reference bit_span::back() const { return begin()[size() - 1]; } + +// --- Iterators --- + +// Returns an iterator to the first bit. +template +constexpr bit_span::iterator bit_span::begin() const noexcept { return iterator(data_); } + +// Returns an iterator just past the last bit. +template +constexpr bit_span::iterator bit_span::end() const noexcept { return begin() + size(); } + +// --- Subspan Functionality --- + +template +template +constexpr auto bit_span::subspan() const noexcept + -> bit_span(), Policy> { + using NewSpanType = bit_span(), Policy>; + + if constexpr (NewExtent == std::dynamic_extent) { + return NewSpanType(begin() + NewOffset, size() - NewOffset); + } else { + return NewSpanType(begin() + NewOffset); + } +} + +// Creates a subspan starting at offset with count bits. +template +constexpr bit_span bit_span::subspan(size_type offset, size_type count) const noexcept + requires(Extent == std::dynamic_extent) +{ + size_type new_count = (count == std::dynamic_extent) ? size() - offset : count; + return bit_span(begin() + offset, new_count); +} + +template +constexpr bit_array_ref bit_span::operator()(size_type begin, size_type end) const noexcept { + return bit_array_ref(&(this->begin()[begin]), end - begin); +} + +template +template +constexpr bit_span bit_span::first() const noexcept + requires(NewExtent != std::dynamic_extent) +{ + return bit_span(data_); +} + +template +constexpr bit_span bit_span::first(size_type offset) const noexcept + requires(Extent == std::dynamic_extent) +{ + return bit_span(begin() + offset); +} + +template +template +constexpr bit_span bit_span::last() const noexcept + requires(RevOffset != std::dynamic_extent) +{ + return bit_span(begin() + size() - RevOffset); +} + +template +constexpr bit_span bit_span::last(size_type offset) const noexcept + requires(Extent == std::dynamic_extent) +{ + return bit_span(begin() + size() - offset); +} + +} // namespace bit +#endif diff --git a/include/bitlib/bit-containers/bit_vector.hpp b/include/bitlib/bit-containers/bit_vector.hpp index f971c494..bc8450bf 100644 --- a/include/bitlib/bit-containers/bit_vector.hpp +++ b/include/bitlib/bit-containers/bit_vector.hpp @@ -2,7 +2,7 @@ // Project: The Experimental Bit Algorithms Library // \file bit_vector.hpp // Description: Implementation of bit_vector -// Contributor: Bryce Kille +// Contributor: Bryce Kille // License: BSD 3-Clause License // ========================================================================== // #ifndef _BIT_VECTOR_HPP_INCLUDED @@ -13,111 +13,104 @@ // ================================ PREAMBLE ================================ // // C++ standard library -#include +#include #include -#include #include -#include -#include +#include +#include #include +#include // Project sources -#include "bitlib/bit-iterator/bit.hpp" #include "bitlib/bit-algorithms/bit_algorithm.hpp" +#include "bitlib/bit_concepts.hpp" +#include "bitlib/bit-containers/bit_array_ref.hpp" +#include "bitlib/bit-iterator/bit.hpp" + // Third-party libraries // Miscellaneous namespace bit { // ========================================================================== // - - /* ****************************** BIT VECTOR ****************************** */ //! A bit-vector with a similar interface to std::vector -template> +template > class bit_vector { - private: - static constexpr size_t digits = binary_digits::value; - std::vector word_vector; - size_t length_ = 0; - - // TODO are privates always inlined? - // @brief Get the number of words needed to represet num_bits bits - constexpr uint64_t word_count(unsigned int num_bits) { - return ((num_bits + digits - 1) / digits); - } - - // Iterator pair constructor specializations - // Passing in iterator over bool - template - typename std::enable_if< - std::is_same< - typename std::iterator_traits::value_type, - bool - >::value - >::type - constexpr range_constructor( - RandomAccessIt first, - RandomAccessIt last, - const Allocator& alloc); - - // Passing in iterator over WordType constructs via whole words - template - typename std::enable_if< - std::is_same< - typename std::iterator_traits::value_type, - WordType - >::value - >::type - constexpr range_constructor( - RandomAccessIt first, - RandomAccessIt last, - const Allocator& alloc); - - public: - /* + private: + static constexpr size_t digits = binary_digits::value; + std::vector word_vector; + size_t length_ = 0; + + // TODO are privates always inlined? + // @brief Get the number of words needed to represet num_bits bits + constexpr uint64_t word_count(size_t num_bits) { + return ((num_bits + digits - 1) / digits); + } + + // Iterator pair constructor specializations + // Passing in iterator over bool + template + typename std::enable_if< + std::is_same< + typename std::iterator_traits::value_type, + bool>::value>::type constexpr range_constructor(RandomAccessIt first, + RandomAccessIt last, + const Allocator& alloc); + + // Passing in iterator over WordType constructs via whole words + template + typename std::enable_if< + std::is_same< + typename std::iterator_traits::value_type, + WordType>::value>::type constexpr range_constructor(RandomAccessIt first, + RandomAccessIt last, + const Allocator& alloc); + + public: + /* * Types and typedefs */ - using value_type = bit_value; - using base_type = WordType; - using allocator_type = Allocator; - using size_type = std::size_t; - using difference_type = std::ptrdiff_t; - using reference = bit_reference; - using const_reference = const reference; - using pointer = bit_pointer; - using iterator = bit_iterator::iterator>; - using const_iterator = bit_iterator::const_iterator>; - - - /* + using value_type = bit_value; + using base_type = WordType; + using allocator_type = Allocator; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using reference = bit_reference; // typename std::vector::reference>; + using const_reference = const reference; + using pointer = bit_pointer; + using iterator = bit_iterator::iterator>; + using const_iterator = bit_iterator::const_iterator>; + + /* * Constructors, copies and moves... */ - constexpr bit_vector() noexcept(noexcept(Allocator())); - constexpr explicit bit_vector(const Allocator& alloc) noexcept; - constexpr bit_vector( - size_type count, - value_type bit_val, - const Allocator& alloc=Allocator()); - constexpr explicit bit_vector( - size_type count, - const Allocator& alloc=Allocator()); - template - constexpr bit_vector( - bit_iterator first, - bit_iterator last, - const Allocator& alloc=Allocator()); - template - constexpr bit_vector( - RandomAccessIt first, - RandomAccessIt last, - const Allocator& alloc=Allocator()); - constexpr bit_vector(const bit_vector& other) = default; - constexpr bit_vector(const bit_vector& other, const Allocator& alloc); - constexpr bit_vector(const bit_vector&& other) noexcept; - constexpr bit_vector(const bit_vector&& other, const Allocator& alloc); - constexpr bit_vector(std::initializer_list init, const Allocator& alloc=Allocator()); - constexpr bit_vector(std::initializer_list init, const Allocator& alloc=Allocator()); - constexpr bit_vector(std::initializer_list init, const Allocator& alloc=Allocator()); - constexpr bit_vector(std::string_view s); + constexpr bit_vector() noexcept(noexcept(Allocator())); + constexpr explicit bit_vector(const Allocator& alloc) noexcept; + constexpr bit_vector( + size_type count, + value_type bit_val, + const Allocator& alloc = Allocator()); + constexpr explicit bit_vector( + size_type count, + const Allocator& alloc = Allocator()); + template + constexpr bit_vector(Iterator first, Iterator last, const Allocator& alloc = Allocator()); + + template + constexpr bit_vector(std::from_range_t, _Range&& rg, const Allocator& alloc = Allocator()); + + template + constexpr bit_vector( + RandomAccessIt first, + RandomAccessIt last, + const Allocator& alloc = Allocator()); + constexpr bit_vector(const bit_vector& other) = default; + constexpr bit_vector(const bit_vector& other, const Allocator& alloc); + constexpr bit_vector(const bit_vector&& other) noexcept; + constexpr bit_vector(const bit_vector&& other, const Allocator& alloc); + constexpr bit_vector(std::initializer_list init, const Allocator& alloc = Allocator()); + constexpr bit_vector(std::initializer_list init, const Allocator& alloc = Allocator()); + constexpr bit_vector(std::initializer_list init, const Allocator& alloc = Allocator()); + constexpr bit_vector(std::string_view s); #if __cplusplus == 201703L ~bit_vector(); @@ -175,19 +168,28 @@ class bit_vector { constexpr void clear() noexcept; constexpr iterator insert(const_iterator pos, const value_type& value); constexpr iterator insert(const_iterator pos, size_type count, const value_type& value); - constexpr iterator insert(const_iterator pos, iterator first, iterator last); + template + constexpr iterator insert(const_iterator pos, OtherIt first, OtherIt last); constexpr iterator erase(iterator pos); constexpr iterator erase(iterator first, iterator last); constexpr void push_back(const value_type& value); constexpr void pop_back(); constexpr void resize(size_type count); constexpr void resize(size_type count, const value_type& value); + template + constexpr iterator insert_range(const_iterator pos, R&& range); + template + constexpr void append_range(R&& range); + /* + * Slice + */ + constexpr bit_array_ref operator()(size_type begin, size_type end) const noexcept; /* * Helper functions */ - constexpr std::string debug_string(const_iterator first, const_iterator last); + constexpr std::string debug_string(const_iterator first, const_iterator end); constexpr std::string debug_string(); // TODO Make constexpr @@ -195,6 +197,13 @@ class bit_vector { //return os << bv.debug_string(bv.cbegin(), bv.cend());; //} }; +static_assert(bit_range>, "bit_vector does not satisfy bit_range concept!"); +static_assert(bit_sized_range>, "bit_vector does not satisfy bit_sized_range concept!"); +#ifdef CONTIGUOUS_RANGE +static_assert(bit_contiguous_range>, "bit_vector does not satisfy bit_contiguous_range concept!"); +static_assert(bit_contiguous_sized_range>, "bit_vector does not satisfy bit_contiguous_sized_range concept!"); +#endif + /* ************************************************************************** */ @@ -217,16 +226,19 @@ constexpr bit_vector::bit_vector(size_type count, const All : word_vector(word_count(count), alloc), length_(count) {} //TODO needs to work for input iterators -template -template -constexpr bit_vector::bit_vector( - bit_iterator first, - bit_iterator last, - const Allocator& alloc) +template + +template +constexpr bit_vector::bit_vector(Iterator first, Iterator last, const Allocator& alloc) : word_vector(distance(first, last), alloc), length_(distance(first, last)) { - copy(first, last, this->begin()); + copy(first, last, this->begin()); } +template +template +constexpr bit_vector::bit_vector(std::from_range_t, _Range&& rg, const Allocator& alloc) + : bit_vector(rg.begin(), rg.end(), alloc) { +} template template @@ -519,60 +531,62 @@ bit_vector::insert( } // TODO should use std::insert to maintain the constant amortized time. -template +template constexpr typename bit_vector::iterator bit_vector::insert( - const_iterator pos, - size_type count, - const bit_vector::value_type& value) { - const auto d = distance(cbegin(), pos); - if (count == 0) { - return begin() + d; - } - const float bits_available = word_vector.size() * digits; - const bool need_to_add = length_ + count > bits_available; - if (need_to_add) { - const auto words_to_add = word_count(length_ + count - bits_available); - word_vector.resize(word_vector.size() + words_to_add); - } - length_ += count; - shift_right(begin() + d, begin() + length_, count); - fill(begin() + d, begin() + d + count, value); + const_iterator pos, + size_type count, + const bit_vector::value_type& value) { + const auto d = distance(cbegin(), pos); + if (count == 0) { return begin() + d; -} - -template + } + const size_t bits_available = word_vector.size() * digits; + const bool need_to_add = length_ + count > bits_available; + if (need_to_add) { + const auto words_to_add = word_count(length_ + count - bits_available); + word_vector.resize(word_vector.size() + words_to_add); + } + length_ += count; + shift_right(begin() + d, begin() + length_, count); + fill(begin() + d, begin() + d + count, value); + return begin() + d; +} + +template +template constexpr typename bit_vector::iterator bit_vector::insert( - const_iterator pos, - iterator first, - iterator last) { - const auto d = distance(cbegin(), pos); - const size_t count = distance(first, last); - if (count == 0) { - return begin() + d; - } - const float bits_available = word_vector.size()*digits; - const auto need_to_add = length_ + count > bits_available; - if (need_to_add) { - const auto words_to_add = word_count(length_ + count - bits_available); - word_vector.resize(word_vector.size() + words_to_add); - } - length_ += count; - shift_right(begin() + d, begin() + length_, count); - copy(first, last, begin() + d); + const_iterator pos, + OtherIt first, + OtherIt last) { + const auto d = distance(cbegin(), pos); + const size_t count = distance(first, last); + if (count == 0) { return begin() + d; + } + const size_t bits_available = word_vector.size() * digits; + const auto need_to_add = length_ + count > bits_available; + if (need_to_add) { + const auto words_to_add = word_count(length_ + count - bits_available); + word_vector.resize(word_vector.size() + words_to_add); + } + length_ += count; + shift_right(begin() + d, begin() + length_, count); + copy(first, last, begin() + d); + return begin() + d; } template constexpr typename bit_vector::iterator bit_vector::erase(iterator pos) { - shift_left(pos, begin() + length_, 1); - length_ -= 1; - if (length_ % digits == 0) { - word_vector.pop_back(); - } - return pos; + difference_type d = distance(begin(), pos); + shift_left(pos, begin() + length_, 1); + length_ -= 1; + if (length_ % digits == 0) { + word_vector.pop_back(); + } + return begin() + d; } template @@ -627,45 +641,46 @@ constexpr void bit_vector::resize(size_type count, const va } // -------------------------------------------------------------------------- // +template +template +constexpr bit_vector::iterator bit_vector::insert_range(const_iterator pos, R&& range) { + return this->insert(pos, range.begin(), range.end()); +} +template +template +constexpr void bit_vector::append_range(R&& range) { + this->insert(this->end(), range.begin(), range.end()); +} + +/* + * Slice +*/ +template +constexpr bit_array_ref bit_vector::operator()(size_type begin, size_type end) const noexcept { + return bit_array_ref(&this->at(begin), end - begin); +} // ------------------------ BIT VECTOR: DEBUGGING -------------------------- // -template -constexpr std::string bit_vector::debug_string(const_iterator first, const_iterator last) { - std::string ret = ""; - iterator mem = first; - auto position = 0; - for (iterator it = first; it != last; ++it) { - if (position % digits == 0 && position != 0) { - ret += " "; - } else if (position % 8 == 0 && position != 0) { - ret += '.'; - } - ret += *it == bit1 ? '1' : '0'; - mem = it; - ++position; +template +constexpr std::string bit_vector::debug_string(const_iterator first, const_iterator end) { + std::string ret = ""; + auto position = 0; + for (const_iterator it = first; it != end; ++it) { + if (position % digits == 0 && position != 0) { + ret += " "; + } else if (position % 8 == 0 && position != 0) { + ret += '.'; } - return ret; + ret += *it == bit1 ? '1' : '0'; + ++position; + } + return ret; } template constexpr std::string bit_vector::debug_string() { - auto first = begin(); - auto last = end(); - std::string ret = ""; - iterator mem = first; - auto position = 0; - for (iterator it = first; it != last; ++it) { - if (position % digits == 0 && position != 0) { - ret += " "; - } else if (position % 8 == 0 && position != 0) { - ret += '.'; - } - ret += *it == bit1 ? '1' : '0'; - mem = it; - ++position; - } - return ret; + return debug_string(begin(), end()); } // -------------------------------------------------------------------------- // diff --git a/include/bitlib/bit-iterator/bit.hpp b/include/bitlib/bit-iterator/bit.hpp index cab3f30a..679019fb 100644 --- a/include/bitlib/bit-iterator/bit.hpp +++ b/include/bitlib/bit-iterator/bit.hpp @@ -20,15 +20,17 @@ // C++ standard library // Project sources #include "bit_details.hpp" -#include "bit_value.hpp" -#include "bit_reference.hpp" -#include "bit_pointer.hpp" #include "bit_iterator.hpp" +#include "bit_reference.hpp" +#include "bit_value.hpp" +#include "bit_word_pointer_adapter.hpp" +#include "bit_word_reference_adapter.hpp" + // Third-party libraries // Miscellaneous // ========================================================================== // - +static_assert(std::is_same_v>::word_type, uint8_t>); // ========================================================================== // #endif // _BIT_HPP_INCLUDED diff --git a/include/bitlib/bit-iterator/bit_details.hpp b/include/bitlib/bit-iterator/bit_details.hpp index 595cd810..664ed475 100644 --- a/include/bitlib/bit-iterator/bit_details.hpp +++ b/include/bitlib/bit-iterator/bit_details.hpp @@ -15,50 +15,73 @@ // ================================ PREAMBLE ================================ // // C++ standard library -#include -#include +// clang-format off +#if defined(_MSC_VER) + #if __has_include() + #include + #else + #define NO_X86_INTRINSICS + #endif +#else + #if __has_include() + #include + #else + #if __has_include() + #include + #else + #define NO_X86_INTRINSICS + #endif + #endif +#endif // defined(_MSC_VER) +// clang-format on + +#include +#include #include +#include +#include #include -#include #include -#include +#include +#include #include +#include #include +#include + +#include "bitlib/bit-containers/bit_bitsof.hpp" +#include "bitlib/bit_concepts.hpp" + // Project sources // Third-party libraries // Miscellaneous namespace bit { class bit_value; -template class bit_reference; -template class bit_pointer; -template class bit_iterator; -// ========================================================================== // - - - -/* ***************************** BINARY DIGITS ****************************** */ -// Binary digits structure definition -template -struct binary_digits -: std::conditional< - std::is_const::value || std::is_volatile::value, - binary_digits::type>, - std::integral_constant::digits> ->::type -{ - // Assertions - static_assert(std::is_integral::value, ""); - static_assert(std::is_unsigned::value, ""); - static_assert(!std::is_same::value, ""); - static_assert(!std::is_same::value, ""); -}; - -// Binary digits value -template -constexpr std::size_t binary_digits_v = binary_digits::value; -/* ************************************************************************** */ +template +class bit_reference; +template +class bit_iterator; +template +using bit_pointer = bit_iterator; +template +class bit_word_pointer_adapter; +// ========================================================================== // +template +using ceil_integral = std::conditional_t< + (N <= bitsof()), + std::uint8_t, + std::conditional_t< + (N <= bitsof()), + std::uint16_t, + std::conditional_t< + (N <= bitsof()), + std::uint32_t, + std::conditional_t< + (N <= bitsof()), + std::uint64_t, + std::uint64_t>>>>; /* *************** IMPLEMENTATION DETAILS: CV ITERATOR TRAITS *************** */ // Cv iterator traits structure definition @@ -79,8 +102,9 @@ struct _cv_iterator_traits using _raw_pointer_t = typename std::remove_cv<_no_pointer_t>::type; using _raw_reference_t = typename std::remove_cv<_no_reference_t>::type; using _cv_value_t = _no_reference_t; - static_assert(std::is_same<_raw_pointer_t, _raw_value_t>::value, ""); - static_assert(std::is_same<_raw_reference_t, _raw_value_t>::value, ""); + + // static_assert(std::is_same<_raw_pointer_t, _raw_value_t>::value, ""); + // static_assert(std::is_same<_raw_reference_t, _raw_value_t>::value, ""); // Types public: @@ -92,171 +116,86 @@ struct _cv_iterator_traits }; /* ************************************************************************** */ - - -/* *********** IMPLEMENTATION DETAILS: NARROWEST AND WIDEST TYPES *********** */ -// Narrowest type structure declaration -template -struct _narrowest_type; - -// Narrowest type structure specialization: selects the only passed type -template -struct _narrowest_type -: std::common_type -{ - static_assert(binary_digits::value, ""); -}; - -// Narrowest type structure specialization: selects the type with less bits -template -struct _narrowest_type -: _narrowest_type< - typename std::conditional< - (binary_digits::value < binary_digits::value), - T, - typename std::conditional< - (binary_digits::value > binary_digits::value), - U, - typename std::common_type::type - >::type - >::type -> -{ -}; - -// Narrowest type structure specialization: recursively selects the right type -template -struct _narrowest_type -: _narrowest_type::type> -{ -}; - -// Narrowest type alias -template -using _narrowest_type_t = typename _narrowest_type::type; - -// Widest type structure declaration -template -struct _widest_type; - -// Widest type structure specialization: selects the only passed type -template -struct _widest_type -: std::common_type -{ - static_assert(binary_digits::value, ""); -}; - -// Widest type structure specialization: selects the type with more bits -template -struct _widest_type -: _widest_type< - typename std::conditional< - (binary_digits::value > binary_digits::value), - T, - typename std::conditional< - (binary_digits::value < binary_digits::value), - U, - typename std::common_type::type - >::type - >::type -> -{ -}; - -// Widest type structure specialization: recursively selects the right type -template -struct _widest_type -: _widest_type::type> -{ +/* +exact_floor_integral is used to determine the exact integral type that a proxy reference +can be implicitly converted to. +If the proxy reference supports multiple types, it will pick the smallest, preferring unsigned. +*/ +template +struct exact_floor_integral { + private: + using U = std::remove_cvref_t; + + template + static constexpr bool is_exactly_convertible() { + if constexpr (!std::is_convertible_v) { + return false; + } else { + // Try brace-initialization to detect narrowing + return requires(From f) { + To{f}; // will fail if narrowing + }; + } + } + + public: + using type = + std::conditional_t< + is_exactly_convertible(), uint8_t, + std::conditional_t< + is_exactly_convertible(), int8_t, + std::conditional_t< + is_exactly_convertible(), uint16_t, + std::conditional_t< + is_exactly_convertible(), int16_t, + std::conditional_t< + is_exactly_convertible(), uint32_t, + std::conditional_t< + is_exactly_convertible(), int32_t, + std::conditional_t< + is_exactly_convertible(), uint64_t, + std::conditional_t< + is_exactly_convertible(), int64_t, + void>>>>>>>>; }; -// Widest type alias -template -using _widest_type_t = typename _widest_type::type; -/* ************************************************************************** */ +// Helper alias +template +using exact_floor_integral_t = typename exact_floor_integral::type; +template +struct is_static_castable : std::false_type {}; +template +struct is_static_castable(std::declval()))>> + : std::true_type {}; -/* ************ IMPLEMENTATION DETAILS: NARROWER AND WIDER TYPES ************ */ -// Narrower type structure definition -template -struct _narrower_type -{ - using tuple = std::tuple< - unsigned long long int, - unsigned long int, - unsigned int, - unsigned short int, - unsigned char - >; - using lhs_bits = binary_digits; - using rhs_bits = binary_digits::type>; - using type = typename std::conditional< - (lhs_bits::value > rhs_bits::value), - typename std::tuple_element::type, - typename std::conditional< - (I + 1 < std::tuple_size::value), - typename _narrower_type< - T, - (I + 1 < std::tuple_size::value ? I + 1 : -1) - >::type, - typename std::tuple_element::type - >::type - >::type; -}; +template +constexpr bool is_static_castable_v = is_static_castable::value; -// Narrower type structure specialization: not found -template -struct _narrower_type -{ - using type = T; +/* ***************************** BINARY DIGITS ****************************** */ +// Binary digits structure definition +// Implementation template: only instantiates static_asserts for non-byte types. +template ::value> +struct binary_digits_impl : std::integral_constant>>::digits> { + static_assert(std::is_integral>::value, "Type must be integral"); + //static_assert(std::is_unsigned::value, "Type must be unsigned"); + static_assert(!std::is_same, bool>::value, "Type must not be bool"); + static_assert(!std::is_same, char>::value, "Type must not be char"); }; -// Narrower type alias -template -using _narrower_type_t = typename _narrower_type::type; +// Specialization for std::byte. +template <> +struct binary_digits_impl : std::integral_constant::digits> {}; -// Wider type structure definition -template -struct _wider_type -{ - using tuple = std::tuple< - unsigned char, - unsigned short int, - unsigned int, - unsigned long int, - unsigned long long int - >; - using lhs_bits = binary_digits; - using rhs_bits = binary_digits::type>; - using type = typename std::conditional< - (lhs_bits::value < rhs_bits::value), - typename std::tuple_element::type, - typename std::conditional< - (I + 1 < std::tuple_size::value), - typename _narrower_type< - T, - (I + 1 < std::tuple_size::value ? I + 1 : -1) - >::type, - typename std::tuple_element::type - >::type - >::type; -}; +// Public interface that removes cv-qualifiers. +template +struct binary_digits : binary_digits_impl> {}; -// Wider type structure specialization: not found -template -struct _wider_type -{ - using type = T; -}; - -// Wider type alias +// Binary digits value template -using _wider_type_t = typename _wider_type::type; -/* ************************************************************************** */ - - +constexpr std::size_t binary_digits_v = binary_digits::value; +/*************************************************************************** */ /* ******************* IMPLEMENTATION DETAILS: UTILITIES ******************** */ // Assertions @@ -264,50 +203,11 @@ template constexpr bool _assert_range_viability(Iterator first, Iterator last); /* ************************************************************************** */ - - -/* ****************** IMPLEMENTATION DETAILS: INSTRUCTIONS ****************** */ -// Population count -template -constexpr T _popcnt(T src) noexcept; -template -constexpr T _popcnt(T src, X...) noexcept; - -// Leading zeros count -template -constexpr T _lzcnt(T src) noexcept; -template -constexpr T _lzcnt(T src, X...) noexcept; - -// Trailing zeros count -template -constexpr T _tzcnt(T src) noexcept; -template -constexpr T _tzcnt(T src, X...) noexcept; - // Bit field extraction template -constexpr T _bextr(T src, T start, T len) noexcept; -template -constexpr T _bextr(T src, T start, T len, X...) noexcept; - -// Parallel bits deposit -template -constexpr T _pdep(T src, T msk) noexcept; -template -constexpr T _pdep(T src, T msk, X...) noexcept; - -// Parallel bits extract -template -constexpr T _pext(T src, T msk) noexcept; +constexpr T _bextr(T src, size_t start, size_t len) noexcept; template -constexpr T _pext(T src, T msk, X...) noexcept; - -// Byte swap -template -constexpr T _byteswap(T src) noexcept; -template -constexpr T _byteswap(T src, X...) noexcept; +constexpr T _bextr(T src, size_t start, size_t len, X...) noexcept; // Bit swap template @@ -317,12 +217,6 @@ constexpr T _bitswap(T src) noexcept; template constexpr T _bitswap() noexcept; -// Bit blend -template -constexpr T _bitblend(T src0, T src1, T msk) noexcept; -template -constexpr T _bitblend(T src0, T src1, T start, T len) noexcept; - // Bit exchange template constexpr void _bitexch(T& src0, T& src1, T msk) noexcept; @@ -331,475 +225,272 @@ constexpr void _bitexch(T& src0, T& src1, S start, S len) noexcept; template constexpr void _bitexch(T& src0, T& src1, S start0, S start1, S len) noexcept; -// Bit compare -template -constexpr T _bitcmp(T src0, T src1, T start0, T start1, T len) noexcept; - // Double precision shift left template -constexpr T _shld(T dst, T src, T cnt) noexcept; +constexpr T _shld(const T& dst, const T& src, const size_t& cnt) noexcept; // Double precision shift right template -constexpr T _shrd(T dst, T src, T cnt) noexcept; - -// Add carry -template -using _supports_adc = decltype(__builtin_ia32_addcarryx_u64(T()...)); -template > -constexpr C _addcarry(C carry, T src0, T src1, T* dst) noexcept; -template -constexpr C _addcarry(C carry, T src0, T src1, T* dst, X...) noexcept; - -// Sub borrow -template -using _supports_sbb = decltype(__builtin_ia32_sbb_u64(T()...)); -template -using _supports_sbb_alt = decltype(__builtin_ia32_subborrow_u64(T()...)); -template > -constexpr B _subborrow(B borrow, T src0, T src1, T* dst) noexcept; -template > -constexpr B _subborrow(const B& borrow, T src0, T src1, T* dst) noexcept; -template -constexpr B _subborrow(B borrow, T src0, T src1, T* dst, X...) noexcept; +constexpr T _shrd(const T& dst, const T& src, const size_t& cnt) noexcept; // Multiword multiply -template +template constexpr T _mulx(T src0, T src1, T* hi) noexcept; -template +template constexpr T _mulx(T src0, T src1, T* hi, X...) noexcept; +template +constexpr T _divx(const T& numerator_hi, const T& numerator_lo, const T& denominator, T* remainder) noexcept; +template +constexpr T _divx(const T& numerator_hi, const T& numerator_lo, const T& denominator, T* remainder, X...) noexcept; /* ************************************************************************** */ +/* +Logical shift right +*/ +template +constexpr T lsr(const T val, const size_type shift) { +#ifdef BITLIB_DETECT_UNDEFINED_SHIFT + assert(static_cast(shift) < bitsof()); +#endif + return static_cast(static_cast>(val) >> shift); +} + +/* +Logic shift right when `val` operand is a proxy reference +*/ +template +constexpr exact_floor_integral_t lsr(const T val, const size_type shift) { + static_assert(!std::is_same_v, void>, + "Type T must be convertible to an integral type"); +#ifdef BITLIB_DETECT_UNDEFINED_SHIFT + assert(static_cast(shift) < bitsof>()); +#endif + return static_cast>(static_cast>>(val) >> shift); +} + +/* +Logical shift left +*/ +template +constexpr T lsl(const T val, const size_type shift) { +#ifdef BITLIB_DETECT_UNDEFINED_SHIFT + assert(static_cast(shift) < bitsof()); +#endif + return static_cast(static_cast>(val) << shift); +} + +/* +Logic shift left when `val` operand is a proxy reference +*/ +template +constexpr exact_floor_integral_t lsl(const T val, const size_type shift) { + static_assert(!std::is_same_v, void>, + "Type T must be convertible to an integral type"); +#ifdef BITLIB_DETECT_UNDEFINED_SHIFT + assert(static_cast(shift) < bitsof>()); +#endif + return static_cast>(static_cast>>(val) << shift); +} + +enum class _mask_len { + unknown, + in_range +}; +enum class _mask_start { + unknown, + in_range +}; +template +constexpr T _mask(const size_type len) { + using unsigned_t = std::make_unsigned_t; + constexpr unsigned_t one = unsigned_t(1); + if constexpr (len_in_range != _mask_len::unknown) { +#ifdef BITLIB_DETECT_UNDEFINED_SHIFT + assert(static_cast(len) < bitsof()); +#endif + return static_cast((one << static_cast(len)) - one); + } else { + // The digits_mask is solely here to prevent Undefined Sanitizer + // complaining about shift of len >= digits + // Note: on -O1 the (len & digits_mask) is optimized to simply (len) + constexpr unsigned_t digits_mask = static_cast(bitsof()) - one; + return static_cast((one << (static_cast(len) & digits_mask)) * static_cast(len < bitsof()) - one); + } +} +template +constexpr T _mask(const size_type len, const size_type start) { + if constexpr (start_in_range != _mask_start::unknown) { + return static_cast(_mask(len) << start); + } else { + return static_cast((_mask(len) << start) * (start < bitsof())); + } +} // ------------- IMPLEMENTATION DETAILS: UTILITIES: ASSERTIONS -------------- // // If the range allows multipass iteration, checks if last - first >= 0 template -constexpr bool _assert_range_viability(Iterator first, Iterator last) -{ - using traits_t = std::iterator_traits; - using category_t = typename traits_t::iterator_category; - using multi_t = std::forward_iterator_tag; - constexpr bool is_multipass = std::is_base_of::value; - const bool is_viable = !is_multipass || std::distance(first, last) >= 0; - assert(is_viable); - return is_viable; -} -// -------------------------------------------------------------------------- // - - - -// --------- IMPLEMENTATION DETAILS: INSTRUCTIONS: POPULATION COUNT --------- // -// Counts the number of bits set to 1 with compiler intrinsics -template -constexpr T _popcnt(T src) noexcept -{ - static_assert(binary_digits::value, ""); - constexpr T digits = binary_digits::value; - if (digits <= std::numeric_limits::digits) { - src = __builtin_popcount(src); - } else if (digits <= std::numeric_limits::digits) { - src = __builtin_popcountl(src); - } else if (digits <= std::numeric_limits::digits) { - src = __builtin_popcountll(src); - } else { - src = _popcnt(src, std::ignore); - } - return src; -} - -// Counts the number of bits set to 1 without compiler intrinsics -template -constexpr T _popcnt(T src, X...) noexcept -{ - static_assert(binary_digits::value, ""); - T dst = T(); - for (dst = T(); src; src >>= 1) { - dst += src & 1; - } - return dst; +constexpr bool _assert_range_viability(Iterator first, Iterator last) { + using traits_t = std::iterator_traits; + using category_t = typename traits_t::iterator_category; + using multi_t = std::forward_iterator_tag; + constexpr bool is_multipass = std::is_base_of::value; + const bool is_viable = !is_multipass || std::distance(first, last) >= 0; + assert(is_viable); + return is_viable; } // -------------------------------------------------------------------------- // - - -// ------- IMPLEMENTATION DETAILS: INSTRUCTIONS: LEADING ZEROS COUNT -------- // -// Counts the number of leading zeros with compiler intrinsics -template -constexpr T _lzcnt(T src) noexcept -{ - static_assert(binary_digits::value, ""); - constexpr T digits = binary_digits::value; - T dst = T(); - if (digits < std::numeric_limits::digits) { - dst = src ? __builtin_clz(src) - - (std::numeric_limits::digits - - digits) - : digits; - } else if (digits == std::numeric_limits::digits) { - dst = src ? __builtin_clz(src) : digits; - } else if (digits < std::numeric_limits::digits) { - dst = src ? __builtin_clzl(src) - - (std::numeric_limits::digits - - digits) - : digits; - } else if (digits == std::numeric_limits::digits) { - dst = src ? __builtin_clzl(src) : digits; - } else if (digits < std::numeric_limits::digits) { - dst = src ? __builtin_clzll(src) - - (std::numeric_limits::digits - - digits) - : digits; - } else if (digits == std::numeric_limits::digits) { - dst = src ? __builtin_clzll(src) : digits; - } else { - dst = _lzcnt(src, std::ignore); - } - return dst; -} - -// Counts the number of leading zeros without compiler intrinsics -template -constexpr T _lzcnt(T src, X...) noexcept -{ - static_assert(binary_digits::value, ""); - constexpr T digits = binary_digits::value; - T dst = src != T(); - while (src >>= 1) { - ++dst; - } - return digits - dst; -} -// -------------------------------------------------------------------------- // - - - -// ------- IMPLEMENTATION DETAILS: INSTRUCTIONS: TRAILING ZEROS COUNT ------- // -// Counts the number of trailing zeros with compiler intrinsics -template -constexpr T _tzcnt(T src) noexcept -{ - static_assert(binary_digits::value, ""); - constexpr T digits = binary_digits::value; - T dst = T(); - if (digits <= std::numeric_limits::digits) { - dst = src ? __builtin_ctz(src) : digits; - } else if (digits <= std::numeric_limits::digits) { - dst = src ? __builtin_ctzl(src) : digits; - } else if (digits <= std::numeric_limits::digits) { - dst = src ? __builtin_ctzll(src) : digits; - } else { - dst = _tzcnt(src, std::ignore); - } - return dst; -} - -// Counts the number of trailing zeros without compiler intrinsics -template -constexpr T _tzcnt(T src, X...) noexcept -{ - static_assert(binary_digits::value, ""); - constexpr T digits = binary_digits::value; - T dst = digits; - if (src) { - src = (src ^ (src - 1)) >> 1; - for (dst = T(); src; dst++) { - src >>= 1; - } - } - return dst; -} -// -------------------------------------------------------------------------- // - - - // ------- IMPLEMENTATION DETAILS: INSTRUCTIONS: BIT FIELD EXTRACTION ------- // // Extacts to lsbs a field of contiguous bits with compiler intrinsics template -constexpr T _bextr(T src, T start, T len) noexcept -{ - static_assert(binary_digits::value, ""); - constexpr T digits = binary_digits::value; - T dst = T(); - if (digits <= std::numeric_limits::digits) { - dst = __builtin_ia32_bextr_u32(src, start, len); - } else if (digits <= std::numeric_limits::digits) { - dst = __builtin_ia32_bextr_u64(src, start, len); - } else { - dst = _bextr(src, start, len, std::ignore); - } - return dst; +constexpr T _bextr(T src, size_t start, size_t len) noexcept { + static_assert(binary_digits::value, ""); + constexpr size_t digits = binary_digits::value; + T dst = T(); + if (digits <= std::numeric_limits::digits) { + dst = __builtin_ia32_bextr_u32(src, start, len); + } else if (digits <= std::numeric_limits::digits) { + dst = __builtin_ia32_bextr_u64(src, start, len); + } else { + dst = _bextr(src, start, len, std::ignore); + } + return dst; } // Extacts to lsbs a field of contiguous bits without compiler intrinsics template -constexpr T _bextr(T src, T start, T len, X...) noexcept -{ - static_assert(binary_digits::value, ""); - constexpr T digits = binary_digits::value; - constexpr T one = 1; - const T msk = (one << len) * (len < digits) - one; - return (src >> start) & msk * (start < digits); -} -// -------------------------------------------------------------------------- // - - - -// ------- IMPLEMENTATION DETAILS: INSTRUCTIONS: PARALLEL BIT DEPOSIT ------- // -// Deposits bits according to a mask with compiler instrinsics -template -constexpr T _pdep(T src, T msk) noexcept -{ - static_assert(binary_digits::value, ""); - constexpr T digits = binary_digits::value; - T dst = T(); - if (digits <= std::numeric_limits::digits) { - dst = _pdep_u32(src, msk); - } else if (digits <= std::numeric_limits::digits) { - dst = _pdep_u64(src, msk); - } else { - dst = _pdep(src, msk, std::ignore); - } - return dst; -} - -// Deposits bits according to a mask without compiler instrinsics -template -constexpr T _pdep(T src, T msk, X...) noexcept -{ - static_assert(binary_digits::value, ""); - constexpr T digits = binary_digits::value; - T dst = T(); - T cnt = T(); - while (msk) { - dst >>= 1; - if (msk & 1) { - dst |= src << (digits - 1); - src >>= 1; - } - msk >>= 1; - ++cnt; - } - dst >>= (digits - cnt) * (cnt > 0); - return dst; -} -// -------------------------------------------------------------------------- // - - - -// ------- IMPLEMENTATION DETAILS: INSTRUCTIONS: PARALLEL BIT EXTRACT ------- // -// Extracts bits according to a mask with compiler instrinsics -template -constexpr T _pext(T src, T msk) noexcept -{ - static_assert(binary_digits::value, ""); - constexpr T digits = binary_digits::value; - T dst = T(); - if (digits <= std::numeric_limits::digits) { - dst = _pext_u32(src, msk); - } else if (digits <= std::numeric_limits::digits) { - dst = _pext_u64(src, msk); - } else { - dst = _pext(src, msk, std::ignore); - } - return dst; -} - -// Extracts bits according to a mask without compiler instrinsics -template -constexpr T _pext(T src, T msk, X...) noexcept -{ - static_assert(binary_digits::value, ""); - constexpr T digits = binary_digits::value; - T dst = T(); - T cnt = T(); - while (msk) { - if (msk & 1) { - dst >>= 1; - dst |= src << (digits - 1); - ++cnt; - } - src >>= 1; - msk >>= 1; - } - dst >>= (digits - cnt) * (cnt > 0); - return dst; -} -// -------------------------------------------------------------------------- // - - - -// ------------ IMPLEMENTATION DETAILS: INSTRUCTIONS: BYTE SWAP ------------- // -// Reverses the order of the underlying bytes with compiler intrinsics -template -constexpr T _byteswap(T src) noexcept -{ - static_assert(binary_digits::value, ""); - using byte_t = unsigned char; - constexpr T digits = sizeof(T) * std::numeric_limits::digits; - std::uint64_t tmp64 = 0; - std::uint64_t* ptr64 = nullptr; - if (std::is_same::value) { - ptr64 = reinterpret_cast(&src); - tmp64 = __builtin_bswap64(*ptr64); - *ptr64 = __builtin_bswap64(*(ptr64 + 1)); - *(ptr64 + 1) = tmp64; - } else if (digits == std::numeric_limits::digits) { - src = __builtin_bswap16(src); - } else if (digits == std::numeric_limits::digits) { - src = __builtin_bswap32(src); - } else if (digits == std::numeric_limits::digits) { - src = __builtin_bswap64(src); - } else if (digits > std::numeric_limits::digits) { - src = _byteswap(src, std::ignore); - } - return src; -} - -// Reverses the order of the underlying bytes without compiler intrinsics -template -constexpr T _byteswap(T src, X...) noexcept -{ - static_assert(binary_digits::value, ""); - using byte_t = unsigned char; - constexpr T half = sizeof(T) / 2; - constexpr T end = sizeof(T) - 1; - unsigned char* bytes = reinterpret_cast(&src); - unsigned char byte = 0; - for (T i = T(); i < half; ++i) { - byte = bytes[i]; - bytes[i] = bytes[end - i]; - bytes[end - i] = byte; - } - return src; +constexpr T _bextr(T src, size_t start, size_t len, X...) noexcept { + static_assert(binary_digits::value, ""); + const T msk = _mask(len); + return (lsr(src, start))&msk; } // -------------------------------------------------------------------------- // - - // ------------- IMPLEMENTATION DETAILS: INSTRUCTIONS: BIT SWAP ------------- // // Reverses the order of the bits with or without of compiler intrinsics template -constexpr T _bitswap(T src) noexcept -{ - static_assert(binary_digits::value, ""); - using byte_t = unsigned char; - constexpr auto ignore = nullptr; - constexpr T digits = binary_digits::value; - constexpr unsigned long long int first = 0x80200802ULL; - constexpr unsigned long long int second = 0x0884422110ULL; - constexpr unsigned long long int third = 0x0101010101ULL; - constexpr unsigned long long int fourth = 32; - constexpr bool is_size1 = sizeof(T) == 1; - constexpr bool is_byte = digits == std::numeric_limits::digits; - constexpr bool is_octet = std::numeric_limits::digits == 8; - constexpr bool is_pow2 = _popcnt(digits, ignore) == 1; - T dst = src; - T i = digits - 1; - if (is_size1 && is_byte && is_octet) { - dst = ((src * first) & second) * third >> fourth; - } else if (is_pow2) { - dst = _bitswap(src); - } else { - for (src >>= 1; src; src >>= 1) { - dst <<= 1; - dst |= src & 1; - i--; - } - dst <<= i; +constexpr T _bitswap(T src) noexcept { + static_assert(binary_digits::value, ""); + using byte_t = unsigned char; + constexpr T digits = binary_digits::value; + constexpr unsigned long long int first = 0x80200802ULL; + constexpr unsigned long long int second = 0x0884422110ULL; + constexpr unsigned long long int third = 0x0101010101ULL; + constexpr unsigned long long int fourth = 32; + constexpr bool is_size1 = sizeof(T) == 1; + constexpr bool is_byte = digits == std::numeric_limits::digits; + constexpr bool is_octet = std::numeric_limits::digits == 8; + constexpr bool is_pow2 = std::has_single_bit(static_cast>(digits)); + T dst = src; + T i = digits - 1; + if (is_size1 && is_byte && is_octet) { + dst = static_cast(lsr(((static_cast>(src) * first) & second) * third, fourth)); + } else if (is_pow2) { + dst = _bitswap(src); + } else { + for (src = lsr(src, 1); src; src = lsr(src, 1)) { + dst <<= 1; + dst |= src & 1; + i--; } - return dst; + dst <<= i; + } + return dst; } // Reverses the order of the bits: recursive metafunction template -constexpr T _bitswap(T src) noexcept -{ - static_assert(binary_digits::value, ""); - constexpr T cnt = N >> 1; - constexpr T msk = _bitswap(); - src = ((src >> cnt) & msk) | ((src << cnt) & ~msk); - return cnt > 1 ? _bitswap(src) : src; +constexpr T _bitswap(T src) noexcept { + static_assert(binary_digits::value, ""); + constexpr T cnt = N >> 1; + constexpr T msk = _bitswap(); + src = static_cast(((lsr(src, cnt))&msk) | ((src << cnt) & ~msk)); + return cnt > 1 ? _bitswap(src) : src; } // Reverses the order of the bits: mask for the recursive metafunction template -constexpr T _bitswap() noexcept -{ - static_assert(binary_digits::value, ""); - constexpr T digits = binary_digits::value; - T cnt = digits; - T msk = ~T(); - while (cnt != N) { - cnt >>= 1; - msk ^= (msk << cnt); - } - return msk; +constexpr T _bitswap() noexcept { + static_assert(binary_digits::value, ""); + constexpr T digits = binary_digits::value; + T cnt = digits; + T msk = ~T(); + while (cnt != N) { + cnt = lsr(cnt, 1); + msk ^= static_cast(msk << cnt); + } + return msk; } // -------------------------------------------------------------------------- // - - // ------------ IMPLEMENTATION DETAILS: INSTRUCTIONS: BIT BLEND ------------- // // Replaces bits of src0 by the ones of src1 where the mask is true -template -constexpr T _bitblend(T src0, T src1, T msk) noexcept + +template +constexpr exact_floor_integral_t _bitblend( + const T src0_, + const U src1_, + const exact_floor_integral_t msk) noexcept + requires(std::is_same_v, exact_floor_integral_t>) { - static_assert(binary_digits::value, ""); - return src0 ^ ((src0 ^ src1) & msk); + static_assert(binary_digits>::value, ""); + const exact_floor_integral_t src0 = static_cast>(src0_); + const exact_floor_integral_t src1 = static_cast>(src1_); + return src0 ^ ((src0 ^ src1) & msk); } // Replaces len bits of src0 by the ones of src1 starting at start -template -constexpr T _bitblend(T src0, T src1, T start, T len) noexcept -{ - static_assert(binary_digits::value, ""); - constexpr T digits = binary_digits::value; - constexpr T one = 1; - const T msk = ((one << len) * (len < digits) - one) << start; - return src0 ^ ((src0 ^ src1) & msk * (start < digits)); +template +constexpr exact_floor_integral_t _bitblend( + const T src0_, + const U src1_, + const size_t start, + const size_t len) noexcept + requires(std::is_same_v, exact_floor_integral_t>) +{ + using resolved_t = exact_floor_integral_t; + using promoted_t = std::conditional_t() < bitsof(), int, resolved_t>; + static_assert(binary_digits>::value, ""); + const promoted_t src0 = static_cast(src0_); + const promoted_t src1 = static_cast(src1_); + const resolved_t msk = _mask(len, start); + return static_cast(src0 ^ ((src0 ^ src1) & msk)); } // -------------------------------------------------------------------------- // - - // ---------- IMPLEMENTATION DETAILS: INSTRUCTIONS: BIT EXCHANGE ------------ // // Exchanges/swaps bits of src0 by the ones of src1 where the mask is true template -constexpr void _bitexch(T& src0, T& src1, T msk) noexcept -{ - src0 = src0 ^ static_cast(src1 & msk); - src1 = src1 ^ static_cast(src0 & msk); - src0 = src0 ^ static_cast(src1 & msk); - return; +constexpr void _bitexch(T& src0, T& src1, T msk) noexcept { + src0 = src0 ^ static_cast(src1 & msk); + src1 = src1 ^ static_cast(src0 & msk); + src0 = src0 ^ static_cast(src1 & msk); + return; } // Replaces len bits of src0 by the ones of src1 starting at start template -constexpr void _bitexch(T& src0, T& src1, S start, S len) noexcept -{ - static_assert(binary_digits::value, ""); - constexpr auto digits = binary_digits::value; - constexpr T one = 1; - const T msk = (len < digits) - ? ((one << len) - one) << start : -1; - src0 = src0 ^ static_cast(src1 & msk); - src1 = src1 ^ static_cast(src0 & msk); - src0 = src0 ^ static_cast(src1 & msk); - return; +constexpr void _bitexch(T& src0, T& src1, S start, S len) noexcept { + static_assert(binary_digits::value, ""); + const T msk = _mask(len, start); + src0 = src0 ^ static_cast(src1 & msk); + src1 = src1 ^ static_cast(src0 & msk); + src0 = src0 ^ static_cast(src1 & msk); } // Replaces len bits of src0 by the ones of src1 starting at start0 // in src0 and start1 in src1. // len <= digits-max(start0, start1) +// clang-format off template constexpr void _bitexch(T& src0, T& src1, S start0, S start1, S len) noexcept { static_assert(binary_digits::value, ""); - constexpr auto digits = binary_digits::value; - constexpr T one = 1; - const T msk = (len < digits) ? - ((one << len) - one) : -1; + const T msk = _mask(len); if (start0 >= start1) { src0 = src0 ^ ( static_cast(src1 << (start0 - start1)) @@ -807,7 +498,7 @@ constexpr void _bitexch(T& src0, T& src1, S start0, S start1, S len) noexcept static_cast(msk << start0) ); src1 = src1 ^ ( - static_cast(src0 >> (start0 - start1)) + static_cast(lsr(src0, (start0 - start1))) & static_cast(msk << start1) ); @@ -818,7 +509,7 @@ constexpr void _bitexch(T& src0, T& src1, S start0, S start1, S len) noexcept ); } else { src0 = src0 ^ ( - static_cast(src1 >> (start1 - start0)) + static_cast(lsr(src1, (start1 - start0))) & static_cast(msk << start0) ); @@ -828,210 +519,288 @@ constexpr void _bitexch(T& src0, T& src1, S start0, S start1, S len) noexcept static_cast(msk << start1) ); src0 = src0 ^ ( - static_cast(src1 >> (start1 - start0)) + static_cast(lsr(src1, (start1 - start0))) & static_cast(msk << start0) ); } return; } +// clang-format on // -------------------------------------------------------------------------- // - - -// ----------- IMPLEMENTATION DETAILS: INSTRUCTIONS: BIT COMPARE ------------ // -// Compares a subsequence of bits within src0 and src1 and returns 0 if equal -template -constexpr T _bitcmp(T src0, T src1, T start0, T start1, T len) noexcept -{ - static_assert(binary_digits::value, ""); - return _bextr(src0, start0, len) == _bextr(src1, start1, len); -} -// -------------------------------------------------------------------------- // - - - // --- IMPLEMENTATION DETAILS: INSTRUCTIONS: DOUBLE PRECISION SHIFT LEFT ---- // // Left shifts dst by cnt bits, filling the lsbs of dst by the msbs of src template -constexpr T _shld(T dst, T src, T cnt) noexcept -{ - static_assert(binary_digits::value, ""); - constexpr T digits = binary_digits::value; - if (cnt < digits) { - dst = (dst << cnt) | (src >> (digits - cnt)); - } else { - dst = (src << (cnt - digits)) * (cnt < digits + digits); - } - return dst; +constexpr T _shld(const T& dst, const T& src, const size_t& cnt) noexcept { + static_assert(binary_digits::value, ""); + constexpr size_t digits = binary_digits::value; + if (cnt < digits) { + return static_cast(lsl(dst, cnt) | (lsr(src, (digits - cnt)))); + } else { + return static_cast(lsl(src, cnt - digits) * (cnt < (digits + digits))); + } } // -------------------------------------------------------------------------- // - - // --- IMPLEMENTATION DETAILS: INSTRUCTIONS: DOUBLE PRECISION SHIFT RIGHT --- // // Right shifts dst by cnt bits, filling the msbs of dst by the lsbs of src template -constexpr T _shrd(T dst, T src, T cnt) noexcept -{ - static_assert(binary_digits::value, ""); - constexpr T digits = binary_digits::value; - if (cnt < digits) { - dst = (dst >> cnt) | (src << (digits - cnt)); - } else { - dst = (src >> (cnt - digits)) * (cnt < digits + digits); - } - return dst; +constexpr T _shrd(const T& dst, const T& src, const size_t& cnt) noexcept { + static_assert(binary_digits::value, ""); + constexpr size_t digits = binary_digits::value; + if (cnt < digits) { + return static_cast((lsr(dst, cnt)) | lsl(src, (digits - cnt))); + } else { + return static_cast((lsr(src, (cnt - digits))) * (cnt < (digits + digits))); + } } // -------------------------------------------------------------------------- // - - -// ------------ IMPLEMENTATION DETAILS: INSTRUCTIONS: ADD CARRY ------------- // -// Adds src0 and src1 and returns the new carry bit with intrinsics -template -constexpr C _addcarry(C carry, T src0, T src1, T* dst) noexcept -{ - static_assert(binary_digits::value, ""); - using wider_t = typename _wider_type::type; - constexpr T digits = binary_digits::value; - wider_t tmp = 0; - unsigned int udst = 0; - unsigned long long int ulldst = 0; - if (digits == std::numeric_limits::digits) { - carry = __builtin_ia32_addcarryx_u32(carry, src0, src1, &udst); - *dst = udst; - } else if (digits == std::numeric_limits::digits) { - carry = __builtin_ia32_addcarryx_u64(carry, src0, src1, &ulldst); - *dst = ulldst; - } else if (digits < binary_digits::value) { - tmp = static_cast(src0) + static_cast(src1); - tmp += static_cast(static_cast(carry)); - *dst = tmp; - carry = static_cast(tmp >> digits); - } else { - carry = _addcarry(carry, src0, src1, dst, std::ignore); +#if defined(NO_X86_INTRINSICS) +template +static inline unsigned char add_carry_sub_borrow(unsigned char c_in, U a, U b, U* out) noexcept { + static_assert(std::is_unsigned_v, "Only unsigned types are supported"); + + if constexpr (Add) { + U sum1 = a + b; + U sum2 = sum1 + static_cast(c_in); + *out = sum2; + + // Carry occurs if either sum1 overflows a+b or sum2 overflows sum1+carry + return static_cast((sum1 < a) || (sum2 < sum1)); + } else { + U diff1 = a - b; + U diff2 = diff1 - static_cast(c_in); + *out = diff2; + + // Borrow occurs if a < b or a - b < carry_in + return static_cast((a < b) || (diff1 < c_in)); + } +} +#else +#if defined(__ADX__) +template +unsigned char ADDCARRYSUBBORROW32(unsigned char c, uint32_t a, uint32_t b, uint32_t* out) { + return (Add ? _addcarryx_u32(c, a, b, out) : _subborrow_u32(c, a, b, out)); +} +template +unsigned char ADDCARRYSUBBORROW64(unsigned char c, uint64_t a, uint64_t b, uint64_t* out) { + static_assert(sizeof(uint64_t) == sizeof(unsigned long long int)); + return (Add ? _addcarryx_u64(c, a, b, reinterpret_cast(out)) : _subborrow_u64(c, a, b, reinterpret_cast(out))); +} +#else +template +unsigned char ADDCARRYSUBBORROW32(unsigned char c, uint32_t a, uint32_t b, uint32_t* out) { + return (Add ? _addcarry_u32(c, a, b, out) : _subborrow_u32(c, a, b, out)); +} +template +unsigned char ADDCARRYSUBBORROW64(unsigned char c, uint64_t a, uint64_t b, uint64_t* out) { + static_assert(sizeof(uint64_t) == sizeof(unsigned long long int)); + return (Add ? _addcarry_u64(c, a, b, reinterpret_cast(out)) : _subborrow_u64(c, a, b, reinterpret_cast(out))); +} +#endif + +template +static inline unsigned char add_carry_sub_borrow(unsigned char c_in, U a, U b, U* out) noexcept { + if constexpr (32 > bitsof()) { + // a [aaaaaaaa111111111111111111111111111] + // b + [bbbbbbbb000000000000000000000000000] + // carry + [0000000c] + const uint8_t shift = (32 - bitsof()); + uint32_t carry_propagation = Add ? ((1 << shift) - 1) : 0; + uint32_t tmp_out; + unsigned char carry = ADDCARRYSUBBORROW32( + c_in, + (static_cast(a) << shift) | carry_propagation, + (static_cast(b) << shift), + &tmp_out); + *out = static_cast(tmp_out >> shift); + return carry; + } else if constexpr (32 == bitsof()) { + return ADDCARRYSUBBORROW32(c_in, static_cast(a), static_cast(b), reinterpret_cast(out)); + } else if constexpr (64 == bitsof()) { + return ADDCARRYSUBBORROW64(c_in, static_cast(a), static_cast(b), reinterpret_cast(out)); + } else if constexpr (0 == (bitsof() % 64)) { + using t64 = std::conditional, int64_t, uint64_t>; + unsigned char carry; + for (int i = 0; i < (bitsof() / 64); i++) { + carry = ADDCARRYSUBBORROW64(c_in, static_cast(a >> (i * 64)), static_cast(b >> (i * 64)), reinterpret_cast(out) + i); } return carry; + } else { + assert(((void)"add carry intrinsics support only support powers of 2 bits", false)); + } } +#endif // NO_X86_INTRINSICS -// Adds src0 and src1 and returns the new carry bit without intrinsics -template -constexpr C _addcarry(C carry, T src0, T src1, T* dst, X...) noexcept -{ - static_assert(binary_digits::value, ""); - *dst = src0 + src1 + static_cast(static_cast(carry)); - return carry ? *dst <= src0 || *dst <= src1 : *dst < src0 || *dst < src1; +template +static inline unsigned char add_carry(unsigned char c_in, U a, U b, U* out) noexcept { + return add_carry_sub_borrow(c_in, a, b, out); } -// -------------------------------------------------------------------------- // +template +static inline unsigned char sub_borrow(unsigned char c_in, U a, U b, U* out) noexcept { + return add_carry_sub_borrow(c_in, a, b, out); +} +// -------------------------------------------------------------------------- // -// ------------ IMPLEMENTATION DETAILS: INSTRUCTIONS: SUB BORROW ------------ // -// Subtracts src1 to src0 and returns the new borrow bit with intrinsics -template -constexpr B _subborrow(B borrow, T src0, T src1, T* dst) noexcept -{ - static_assert(binary_digits::value, ""); - using wider_t = typename _wider_type::type; - constexpr T digits = binary_digits::value; - wider_t tmp = 0; - unsigned int udst = 0; - unsigned long long int ulldst = 0; - if (digits == std::numeric_limits::digits) { - borrow = __builtin_ia32_sbb_u32(borrow, src0, src1, &udst); - *dst = udst; - } else if (digits == std::numeric_limits::digits) { - borrow = __builtin_ia32_sbb_u64(borrow, src0, src1, &ulldst); - *dst = ulldst; - } else if (digits < binary_digits::value) { - tmp = static_cast(src1); - tmp += static_cast(static_cast(borrow)); - borrow = tmp > static_cast(src0); - *dst = static_cast(src0) - tmp; - } else { - borrow = _subborrow(borrow, src0, src1, dst, std::ignore); +template +constexpr T _divx(const T& numerator_hi, const T& numerator_lo, const T& denominator, T* remainder) noexcept { + constexpr auto digits = bitsof(); + if constexpr (digits > bitsof()) { + T128 tmp128 = (static_cast(numerator_hi) << digits) | static_cast(numerator_lo); + T quotient = static_cast(tmp128 / static_cast(denominator)); + if (remainder) { + *remainder = static_cast(tmp128 % static_cast(denominator)); + } + return quotient; + } else { + return _divx(numerator_hi, numerator_lo, denominator, remainder, std::ignore); + } +} + +// This code is from a 'public domain' post of ridiculousfish: +// https://ridiculousfish.com/blog/posts/labor-of-division-episode-v.html +template +constexpr T _divx(const T& numerator_hi, const T& numerator_lo, const T& denominator, T* remainder, X...) noexcept { + constexpr auto digits = bitsof(); + using wider_t = ceil_integral; + if constexpr ((digits + digits) <= binary_digits::value) { + wider_t tmp = (static_cast(numerator_hi) << digits) | static_cast(numerator_lo); + T quotient = static_cast(tmp / static_cast(denominator)); + if (remainder) { + *remainder = static_cast(tmp % static_cast(denominator)); + } + return quotient; + } else if constexpr (digits > bitsof()) { + T numhi = numerator_hi; + T numlo = numerator_lo; + T den = denominator; + // We work in base 2**32. + // A uint32 holds a single digit. A uint64 holds two digits. + // Our numerator is conceptually [num3, num2, num1, num0]. + // Our denominator is [den1, den0]. + const uint64_t b = (1ull << 32); + + // The high and low digits of our computed quotient. + uint32_t q1; + uint32_t q0; + + // The normalization shift factor. + int shift; + + // The high and low digits of our denominator (after normalizing). + // Also the low 2 digits of our numerator (after normalizing). + uint32_t den1; + uint32_t den0; + uint32_t num1; + uint32_t num0; + + // A partial remainder. + uint64_t rem; + + // The estimated quotient, and its corresponding remainder (unrelated to true remainder). + uint64_t qhat; + uint64_t rhat; + + // Variables used to correct the estimated quotient. + uint64_t c1; + uint64_t c2; + + // Check for overflow and divide by 0. + if (numhi >= den) { + if (remainder != nullptr) { + *remainder = ~0ull; + } + return ~0ull; } - return borrow; -} -// Subtracts src1 to src0 and returns the new borrow bit with other intrinsics -template -constexpr B _subborrow(const B& borrow, T src0, T src1, T* dst) noexcept -{ - static_assert(binary_digits::value, ""); - using wider_t = typename _wider_type::type; - constexpr T digits = binary_digits::value; - wider_t tmp = 0; - unsigned int udst = 0; - unsigned long long int ulldst = 0; - B flag = borrow; - if (digits == std::numeric_limits::digits) { - flag = __builtin_ia32_subborrow_u32(borrow, src0, src1, &udst); - *dst = udst; - } else if (digits == std::numeric_limits::digits) { - flag = __builtin_ia32_subborrow_u64(borrow, src0, src1, &ulldst); - *dst = ulldst; - } else if (digits < binary_digits::value) { - tmp = static_cast(src1); - tmp += static_cast(static_cast(borrow)); - flag = tmp > static_cast(src0); - *dst = static_cast(src0) - tmp; - } else { - flag = _subborrow(borrow, src0, src1, dst, std::ignore); + // Determine the normalization factor. We multiply den by this, so that its leading digit is at + // least half b. In binary this means just shifting left by the number of leading zeros, so that + // there's a 1 in the MSB. + // We also shift numer by the same amount. This cannot overflow because numhi < den. + // The expression (-shift & 63) is the same as (64 - shift), except it avoids the UB of shifting + // by 64. The funny bitwise 'and' ensures that numlo does not get shifted into numhi if shift is 0. + // clang 11 has an x86 codegen bug here: see LLVM bug 50118. The sequence below avoids it. + shift = std::countl_zero(den); + den <<= shift; + numhi <<= shift; + numhi |= (numlo >> (-shift & 63)) & (-static_cast(shift) >> 63); + numlo <<= shift; + + // Extract the low digits of the numerator and both digits of the denominator. + num1 = static_cast(numlo >> 32); + num0 = static_cast(numlo & 0xFFFFFFFFu); + den1 = static_cast(den >> 32); + den0 = static_cast(den & 0xFFFFFFFFu); + + // We wish to compute q1 = [n3 n2 n1] / [d1 d0]. + // Estimate q1 as [n3 n2] / [d1], and then correct it. + // Note while qhat may be 2 digits, q1 is always 1 digit. + qhat = numhi / den1; + rhat = numhi % den1; + c1 = qhat * den0; + c2 = rhat * b + num1; + if (c1 > c2) { + qhat -= (c1 - c2 > den) ? 2 : 1; } - return flag; -} + q1 = static_cast(qhat); + + // Compute the true (partial) remainder. + rem = numhi * b + num1 - q1 * den; + + // We wish to compute q0 = [rem1 rem0 n0] / [d1 d0]. + // Estimate q0 as [rem1 rem0] / [d1] and correct it. + qhat = rem / den1; + rhat = rem % den1; + c1 = qhat * den0; + c2 = rhat * b + num0; + if (c1 > c2) { + qhat -= (c1 - c2 > den) ? 2 : 1; + } + q0 = static_cast(qhat); -// Subtracts src1 to src0 and returns the new borrow bit without intrinsics -template -constexpr B _subborrow(B borrow, T src0, T src1, T* dst, X...) noexcept -{ - static_assert(binary_digits::value, ""); - *dst = src0 - (src1 + static_cast(static_cast(borrow))); - return borrow ? src1 >= src0 : src1 > src0; + // Return remainder if requested. + if (remainder != nullptr) { + *remainder = (rem * b + num0 - q0 * den) >> shift; + } + return (static_cast(q1) << 32) | q0; + } else { + assert(false); + } } -// -------------------------------------------------------------------------- // - - // -------- IMPLEMENTATION DETAILS: INSTRUCTIONS: MULTIWORD MULTIPLY -------- // // Multiplies src0 and src1 and gets the full result with compiler intrinsics -template -constexpr T _mulx(T src0, T src1, T* hi) noexcept -{ - static_assert(binary_digits::value, ""); - using wider_t = typename _wider_type::type; - constexpr T digits = binary_digits::value; - wider_t tmp = 0; - T128 tmp128 = 0; - T lo = 0; - if (digits == std::numeric_limits::digits) { - tmp128 = static_cast(src0) * static_cast(src1); - *hi = tmp128 >> digits; - lo = tmp128; - } else if (digits + digits == binary_digits::value) { - tmp = static_cast(src0) * static_cast(src1); - *hi = tmp >> digits; - lo = tmp; - } else { - lo = _mulx(src0, src1, hi, std::ignore); - } - return lo; -} - -// Multiplies src0 and src1 and gets the full result without compiler intrinsics -template -constexpr T _mulx(T src0, T src1, T* hi, X...) noexcept -{ - static_assert(binary_digits::value, ""); - constexpr T digits = binary_digits::value; +template +constexpr T _mulx(T src0, T src1, T* hi) noexcept { + constexpr auto digits = bitsof(); + if constexpr (digits > bitsof()) { + T128 tmp128 = static_cast(src0) * static_cast(src1); + *hi = static_cast(tmp128 >> digits); + return static_cast(tmp128); + } else { + return _mulx(src0, src1, hi, std::ignore); + } +} + +template +constexpr T _mulx(T src0, T src1, T* hi, X...) noexcept { + constexpr T digits = bitsof(); + using wider_t = ceil_integral() + bitsof()>; + if constexpr ((digits + digits) <= bitsof()) { + wider_t tmp = static_cast(src0) * static_cast(src1); + *hi = static_cast(tmp >> digits); + return static_cast(tmp); + } else { + // Multiplies src0 and src1 and gets the full result without compiler intrinsics constexpr T offset = digits / 2; - constexpr T ones = ~static_cast(0); - const T lsbs0 = src0 & static_cast(ones >> (digits - offset)); - const T msbs0 = src0 >> offset; - const T lsbs1 = src1 & static_cast(ones >> (digits - offset)); - const T msbs1 = src1 >> offset; + const T lsbs0 = src0 & _mask(digits - offset); + const T msbs0 = lsr(src0, offset); + const T lsbs1 = src1 & _mask(digits - offset); + const T msbs1 = lsr(src1, offset); const T llsbs = lsbs0 * lsbs1; const T mlsbs = msbs0 * lsbs1; const T lmsbs = lsbs0 * msbs1; @@ -1039,15 +808,130 @@ constexpr T _mulx(T src0, T src1, T* hi, X...) noexcept const T lo = llsbs + static_cast(mi << offset); const T lcarry = lo < llsbs || lo < static_cast(mi << offset); const T mcarry = static_cast(mi < mlsbs || mi < lmsbs) << offset; - *hi = static_cast(mi >> offset) + msbs0 * msbs1 + mcarry + lcarry; + *hi = static_cast(lsr(mi, offset)) + msbs0 * msbs1 + mcarry + lcarry; return lo; + } } // -------------------------------------------------------------------------- // +template +constexpr auto with_bit_iterator_adapter( + const bit_iterator& first, + const bit_iterator& last, + const bit_iterator& d_first) { + using dst_word_type = typename bit_iterator::word_type; + using src_word_type = typename bit_iterator::word_type; + if constexpr (!std::is_same_v && bitsof() != bitsof()) { + if constexpr (bitsof() > bitsof()) { + bit_iterator> adapted_first( + bit_word_pointer_adapter(first.base(), first.position() / bitsof()), first.position() % bitsof()); + bit_iterator> adapted_last( + bit_word_pointer_adapter(last.base(), last.position() / bitsof()), last.position() % bitsof()); + return AlgoFunc{}(adapted_first, adapted_last, d_first); + } else { + bit_iterator> adapted_d_first( + bit_word_pointer_adapter(d_first.base(), d_first.position() / bitsof()), d_first.position() % bitsof()); + return AlgoFunc{}(first, last, adapted_d_first); + } + } else { + return AlgoFunc{}(first, last, d_first); + } +} + +template +constexpr auto with_bit_iterator_adapter( + const SrcIt& first, + const SrcIt& last, + const bit_iterator& d_first) { + return with_bit_iterator_adapter(bit_iterator(first), bit_iterator(last), d_first); +} + +template +constexpr auto with_bit_iterator_adapter( + const bit_iterator& first, + const bit_iterator& last, + const DstIt& d_first) { + return with_bit_iterator_adapter(first, last, bit_iterator(d_first)); +} + +template +constexpr auto with_bit_iterator_adapter( + const bit_iterator& first, + const bit_iterator& last) { + using dst_word_type = typename bit_iterator::word_type; + using src_word_type = typename bit_iterator::word_type; + if constexpr (!std::is_same_v && bitsof() != bitsof()) { + if constexpr (bitsof() > bitsof()) { + bit_iterator> adapted_first( + bit_word_pointer_adapter(first.base(), first.position() / bitsof()), first.position() % bitsof()); + return AlgoFunc{}(adapted_first, last); + } else { + bit_iterator> adapted_last( + bit_word_pointer_adapter(last.base(), last.position() / bitsof()), last.position() % bitsof()); + return AlgoFunc{}(first, adapted_last); + } + } else { + return AlgoFunc{}(first, last); + } +} + +template +constexpr auto with_bit_iterator_adapter( + const SrcIt& first, + const bit_iterator& last) { + return with_bit_iterator_adapter(bit_iterator(first), last); +} + +template +constexpr auto with_bit_iterator_adapter( + const bit_iterator& first, + const DstIt& last) { + return with_bit_iterator_adapter(first, bit_iterator(last)); +} +namespace detail { + +struct uninitialized_t { + explicit uninitialized_t() = default; +}; +inline constexpr uninitialized_t uninitialized{}; + +struct initialized_t { + explicit initialized_t() = default; +}; +inline constexpr initialized_t initialized{}; + +template +struct container_size_storage { + constexpr size_type size() const noexcept { + return Extent; + } + + constexpr container_size_storage() noexcept {} +}; + +template +struct container_size_storage { + using maybe_const_size_type = std::conditional_t>; + + maybe_const_size_type size_; + constexpr size_type size() const noexcept { + return size_; + } + constexpr void resize(const size_type& new_size) + requires(resizeable) + { + size_ = new_size; + } + + constexpr container_size_storage() noexcept : size_() {} + constexpr container_size_storage(const size_type& size) noexcept + : size_(size) {} +}; + +} // namespace detail // ========================================================================== // -} // namespace bit +} // namespace bit #endif // _BIT_DETAILS_HPP_INCLUDED -// ========================================================================== // - + // ========================================================================== // \ No newline at end of file diff --git a/include/bitlib/bit-iterator/bit_iterator.hpp b/include/bitlib/bit-iterator/bit_iterator.hpp index 235d028b..e9f340ec 100644 --- a/include/bitlib/bit-iterator/bit_iterator.hpp +++ b/include/bitlib/bit-iterator/bit_iterator.hpp @@ -14,124 +14,113 @@ // ================================ PREAMBLE ================================ // // C++ standard library +#include +#include +#include +#include +#include + +#include "bitlib/bit-containers/bit_bitsof.hpp" +#include "bitlib/bit_concepts.hpp" // Project sources #include "bit_details.hpp" +#include "bit_reference.hpp" // Third-party libraries // Miscellaneous namespace bit { -// ========================================================================== // +/* ****************************** BIT ITERATOR ****************************** */ +template +class bit_word_pointer_adapter; -/* ****************************** BIT ITERATOR ****************************** */ // Bit iterator class definition template class bit_iterator { - // Assertions - private: - using _traits_t = _cv_iterator_traits; - static_assert(binary_digits::value, ""); - - // Types - public: - using iterator_type = Iterator; - using word_type = typename _traits_t::value_type; - using iterator_category = typename _traits_t::iterator_category; - using value_type = bit_value; - using difference_type = std::ptrdiff_t; - using pointer = bit_pointer; - using reference = bit_reference; - using size_type = std::size_t; - - // Lifecycle - public: - constexpr bit_iterator(); - template - constexpr bit_iterator(const bit_iterator& other); - explicit constexpr bit_iterator(iterator_type i); - constexpr bit_iterator(iterator_type i, size_type pos); - - // Assignment - public: - template - constexpr bit_iterator& operator=(const bit_iterator& other); - - // Access - public: - constexpr reference operator*() const noexcept; - constexpr pointer operator->() const noexcept; - constexpr reference operator[](difference_type n) const; - - // Increment and decrement operators - public: - constexpr bit_iterator& operator++(); - constexpr bit_iterator& operator--(); - constexpr bit_iterator operator++(int); - constexpr bit_iterator operator--(int); - constexpr bit_iterator operator+(difference_type n) const; - constexpr bit_iterator operator-(difference_type n) const; - constexpr bit_iterator& operator+=(difference_type n); - constexpr bit_iterator& operator-=(difference_type n); - - // Underlying details - public: - constexpr iterator_type base() const; - constexpr size_type position() const noexcept; - constexpr typename std::remove_cv::type mask() const noexcept; - - // Implementation details: data members - private: - iterator_type _current; - size_type _position; - - // Non-member arithmetic operators - template - friend constexpr bit_iterator operator+( - typename bit_iterator::difference_type n, - const bit_iterator& i - ); - template - friend constexpr typename std::common_type< - typename bit_iterator::difference_type, - typename bit_iterator::difference_type - >::type operator-( - const bit_iterator& lhs, - const bit_iterator& rhs - ); - - // Comparison operators - public: - template - friend constexpr bool operator==( - const bit_iterator& lhs, - const bit_iterator& rhs - ); - template - friend constexpr bool operator!=( - const bit_iterator& lhs, - const bit_iterator& rhs - ); - template - friend constexpr bool operator<( - const bit_iterator& lhs, - const bit_iterator& rhs - ); - template - friend constexpr bool operator<=( - const bit_iterator& lhs, - const bit_iterator& rhs - ); - template - friend constexpr bool operator>( - const bit_iterator& lhs, - const bit_iterator& rhs - ); - template - friend constexpr bool operator>=( - const bit_iterator& lhs, - const bit_iterator& rhs - ); + template + friend class bit_iterator; + + // Assertions + private: + using _traits_t = _cv_iterator_traits; + static_assert(binary_digits>::value, ""); + + // Types + public: + using iterator_type = Iterator; + using word_type = std::iter_value_t; + using iterator_category = typename _traits_t::iterator_category; + using value_type = bit_value; + using difference_type = std::ptrdiff_t; + using pointer = bit_pointer; + using reference = bit_reference; // typename _traits_t::reference; + using size_type = std::size_t; + using mask_type = std::make_unsigned_t>; + + // Lifecycle + public: + constexpr bit_iterator(); + constexpr bit_iterator(const bit_iterator& other); + template + constexpr bit_iterator(const bit_iterator& other) + requires std::constructible_from; + explicit constexpr bit_iterator(iterator_type i); + constexpr bit_iterator(iterator_type i, size_type pos); + explicit constexpr bit_iterator(const pointer& ptr) + requires std::constructible_from; + + template + constexpr bit_iterator(const bit_iterator>& other); + + // Assignment + public: + constexpr bit_iterator& operator=(const bit_iterator& other); + template + constexpr bit_iterator& operator=(const bit_iterator& other); + + // Access + public: + constexpr reference operator*() const noexcept; + constexpr pointer operator->() const noexcept; + constexpr reference operator[](difference_type n) const; + + // Increment and decrement operators + public: + constexpr bit_iterator& operator++(); + constexpr bit_iterator& operator--(); + constexpr bit_iterator operator++(int); + constexpr bit_iterator operator--(int); + constexpr bit_iterator operator+(difference_type n) const; + constexpr bit_iterator operator-(difference_type n) const; + constexpr bit_iterator& operator+=(difference_type n); + constexpr bit_iterator& operator-=(difference_type n); + constexpr difference_type operator-(const bit_iterator&) const; + + // Underlying details + public: + constexpr iterator_type base() const; + constexpr iterator_type address() const; + constexpr size_type position() const noexcept; + + auto operator<=>(const bit_iterator&) const = default; + // Implementation details: data members + private: + iterator_type _current; + size_type _position; + + // Non-member arithmetic operators + template + friend constexpr bit_iterator operator+( + typename bit_iterator::difference_type n, + const bit_iterator& i); + template + friend constexpr typename std::common_type< + typename bit_iterator::difference_type, + typename bit_iterator::difference_type>::type + operator-( + const bit_iterator& lhs, + const bit_iterator& rhs); }; /* ************************************************************************** */ @@ -140,59 +129,75 @@ class bit_iterator // ------------------------ BIT ITERATOR: LIFECYCLE ------------------------- // // Implicitly default constructs a bit iterator template -constexpr bit_iterator::bit_iterator( -) -: _current() -, _position() -{ +constexpr bit_iterator::bit_iterator() + : _current(), _position() { } // Implicitly constructs a bit iterator from another bit iterator +template +constexpr bit_iterator::bit_iterator(const bit_iterator& other) + : _current(other.base()), _position(other.position()) { + assert(_position < bitsof()); +} + template template -constexpr bit_iterator::bit_iterator( - const bit_iterator& other -) -: _current(other.base()) -, _position(other.position()) -{ +constexpr bit_iterator::bit_iterator(const bit_iterator& other) + requires std::constructible_from + : _current(other.base()), _position(other.position()) { + assert(_position < bitsof()); } // Explicitly constructs an aligned bit iterator from an iterator template -constexpr bit_iterator::bit_iterator( - const iterator_type i -) -: _current(i) -, _position(0) -{ +constexpr bit_iterator::bit_iterator(const iterator_type i) + : _current(i), _position(0) { } // Explicitly constructs an unaligned bit iterator from an iterator template -constexpr bit_iterator::bit_iterator( - const iterator_type i, - size_type pos -) -: _current(i) -, _position((assert(pos < binary_digits::value), pos)) -{ +constexpr bit_iterator::bit_iterator(const iterator_type i, size_type pos) + : _current(i), _position((assert(pos < binary_digits::value), pos)) { +} + +// Explicitly constructs an unaligned bit iterator from a pointer +template +constexpr bit_iterator::bit_iterator(const pointer& ptr) + requires std::constructible_from + : _current(ptr._current), _position(ptr.position()) { + assert(_position < bitsof()); +} + +template +template +constexpr bit_iterator::bit_iterator(const bit_iterator>& other) + : _current(other.base().base()), _position(other.base().index() * bitsof::value_type>() + other.position()) { + assert(_position < bitsof()); } // -------------------------------------------------------------------------- // // ------------------------ BIT ITERATOR: ASSIGNMENT ------------------------ // +// Assigns a bit iterator to the bit iterator +template +constexpr bit_iterator& bit_iterator::operator=( + const bit_iterator& other) { + _current = other._current; + _position = other._position; + assert(_position < bitsof()); + return *this; +} + // Assigns a bit iterator to the bit iterator template template constexpr bit_iterator& bit_iterator::operator=( - const bit_iterator& other -) -{ - _current = other._current; - _position = other._position; - return *this; + const bit_iterator& other) { + _current = other._current; + _position = other._position; + assert(_position < bitsof()); + return *this; } // -------------------------------------------------------------------------- // @@ -202,282 +207,165 @@ constexpr bit_iterator& bit_iterator::operator=( // Gets a bit reference from the bit iterator template constexpr typename bit_iterator::reference -bit_iterator::operator*( -) const noexcept -{ - return reference(*_current, _position); +bit_iterator::operator*() const noexcept { + return reference(*_current, _position); } // Gets a pointer to a bit template -constexpr typename bit_iterator::pointer -bit_iterator::operator->( -) const noexcept -{ - return pointer(&*_current, _position); +constexpr typename bit_iterator::pointer bit_iterator::operator->() const noexcept { + return pointer(&*_current, _position); } // Gets a bit reference, decrementing or incrementing the iterator template -constexpr typename bit_iterator::reference -bit_iterator::operator[]( - difference_type n -) const -{ - constexpr difference_type digits = binary_digits::value; - const difference_type sum = _position + n; - difference_type diff = sum / digits; - if (sum < 0 && diff * digits != sum) { - --diff; - } - return reference(*std::next(_current, diff), sum - diff * digits); +constexpr typename bit_iterator::reference bit_iterator::operator[](difference_type n) const { + constexpr difference_type digits = binary_digits::value; + const difference_type sum = static_cast(_position) + n; + const difference_type diff = (sum - (n < 0) * (digits - 1)) / digits; + return reference(*std::next(_current, diff), static_cast(sum) % digits); } // -------------------------------------------------------------------------- // - - // ------------- BIT ITERATOR: INCREMENT AND DECREMENT OPERATORS ------------ // // Increments the bit iterator and returns it template -constexpr bit_iterator& bit_iterator::operator++( -) -{ - constexpr size_type digits = binary_digits::value; - if (_position + 1 < digits) { - ++_position; - } else { - ++_current; - _position = 0; - } - return *this; +constexpr bit_iterator& bit_iterator::operator++() { + constexpr auto digits = binary_digits::value; + _position = (_position + 1) % digits; + if (0 == _position) { + ++_current; + } + return *this; } // Decrements the bit iterator and returns it template -constexpr bit_iterator& bit_iterator::operator--( -) -{ - constexpr size_type digits = binary_digits::value; - if (_position) { - --_position; - } else { - --_current; - _position = digits - 1; - } - return *this; +constexpr bit_iterator& bit_iterator::operator--() { + constexpr size_type digits = binary_digits::value; + if (0 == _position) { + --_current; + } + _position = (_position - 1) % digits; + return *this; } // Increments the bit iterator and returns the old one template -constexpr bit_iterator bit_iterator::operator++( - int -) -{ - bit_iterator old = *this; - ++(*this); - return old; +constexpr bit_iterator bit_iterator::operator++(int) { + bit_iterator old = *this; + ++(*this); + return old; } // Decrements the bit iterator and returns the old one template -constexpr bit_iterator bit_iterator::operator--( - int -) -{ - bit_iterator old = *this; - --(*this); - return old; +constexpr bit_iterator bit_iterator::operator--(int) { + bit_iterator old = *this; + --(*this); + return old; } // Looks forward several bits and gets an iterator at this position template -constexpr bit_iterator bit_iterator::operator+( - difference_type n -) const -{ - constexpr difference_type digits = binary_digits::value; - const difference_type sum = _position + n; - difference_type diff = sum / digits; - if (sum < 0 && diff * digits != sum) { - --diff; - } - return bit_iterator(std::next(_current, diff), sum - diff * digits); +constexpr bit_iterator bit_iterator::operator+(difference_type n) const { + constexpr difference_type digits = binary_digits::value; + const difference_type sum = static_cast(_position) + n; + const difference_type diff = (sum - (n < 0) * (digits - 1)) / digits; + const size_type new_pos = static_cast(sum) % static_cast(digits); + return bit_iterator(std::next(_current, diff), new_pos); } // Looks backward several bits and gets an iterator at this position template -constexpr bit_iterator bit_iterator::operator-( - difference_type n -) const -{ - constexpr difference_type digits = binary_digits::value; - const difference_type sum = _position - n; - difference_type diff = sum / digits; - if (sum < 0 && diff * digits != sum) { - --diff; - } - return bit_iterator(std::next(_current, diff), sum - diff * digits); +constexpr bit_iterator bit_iterator::operator-(difference_type n) const { + constexpr difference_type digits = binary_digits::value; + const difference_type sum = static_cast(_position) - n; + const difference_type diff = (sum - (n > 0) * (digits - 1)) / digits; + const size_type new_pos = static_cast(sum) % static_cast(digits); + return bit_iterator(std::next(_current, diff), new_pos); } // Increments the iterator by several bits and returns it template -constexpr bit_iterator& bit_iterator::operator+=( - difference_type n -) -{ - *this = *this + n; - return *this; +constexpr bit_iterator& bit_iterator::operator+=(difference_type n) { + constexpr difference_type digits = binary_digits::value; + const difference_type sum = static_cast(_position) + n; + const difference_type diff = (sum - (n < 0) * (digits - 1)) / digits; + const size_type new_pos = static_cast(sum) % static_cast(digits); + _current = std::next(_current, diff); + _position = new_pos; + return *this; } // Decrements the iterator by several bits and returns it template -constexpr bit_iterator& bit_iterator::operator-=( - difference_type n -) -{ - *this = *this - n; - return *this; +constexpr bit_iterator& bit_iterator::operator-=(difference_type n) { + constexpr difference_type digits = binary_digits::value; + const difference_type sum = static_cast(_position) - n; + const difference_type diff = (sum - (n > 0) * (digits - 1)) / digits; + const size_type new_pos = static_cast(sum) % static_cast(digits); + _current = std::next(_current, diff); + _position = new_pos; + return *this; } // -------------------------------------------------------------------------- // - +template +constexpr bit_iterator::difference_type +bit_iterator::operator-(const bit_iterator& other) const { + constexpr difference_type digits = binary_digits::value; + return (_current - other._current) * digits + + (static_cast(_position) - static_cast(other._position)); +} // -------------------- BIT ITERATOR: UNDERLYING DETAILS -------------------- // // Returns a copy of the underlying iterator template -constexpr typename bit_iterator::iterator_type -bit_iterator::base( -) const -{ - return _current; +constexpr typename bit_iterator::iterator_type bit_iterator::base() const { + return _current; } -// Returns the position of the bit within the underlying word template -constexpr typename bit_iterator::size_type -bit_iterator::position( -) const noexcept -{ - return _position; +constexpr typename bit_iterator::iterator_type bit_iterator::address() const { + return _current; } -// Returns a mask corresponding to the bit associated with the iterator +// Returns the position of the bit within the underlying word template -constexpr typename std::remove_cv< - typename bit_iterator::word_type ->::type bit_iterator::mask( -) const noexcept -{ - return static_cast(1) << _position; +constexpr typename bit_iterator::size_type bit_iterator::position() const noexcept { + return _position; } -// -------------------------------------------------------------------------- // - - // -------------- BIT ITERATOR: NON-MEMBER ARITHMETIC OPERATORS ------------- // // Advances the bit iterator several times template -constexpr bit_iterator operator+( - typename bit_iterator::difference_type n, - const bit_iterator& i -) -{ - return i + n; +constexpr bit_iterator operator+(typename bit_iterator::difference_type n, const bit_iterator& i) { + return i + n; } // Computes the distance in bits separating two bit iterators template constexpr typename std::common_type< typename bit_iterator::difference_type, - typename bit_iterator::difference_type ->::type operator-( - const bit_iterator& lhs, - const bit_iterator& rhs -) -{ - using lhs_utype = typename bit_iterator::word_type; - using rhs_utype = typename bit_iterator::word_type; - using lhs_type = typename bit_iterator::difference_type; - using rhs_type = typename bit_iterator::difference_type; - using difference_type = typename std::common_type::type; - constexpr difference_type lhs_digits = binary_digits::value; - constexpr difference_type rhs_digits = binary_digits::value; - constexpr difference_type digits = rhs_digits; - static_assert(lhs_digits == rhs_digits, ""); - const difference_type main = lhs._current - rhs._current; - return main * digits + (lhs._position - rhs._position); + typename bit_iterator::difference_type>::type +operator-(const bit_iterator& lhs, const bit_iterator& rhs) { + using lhs_utype = typename bit_iterator::word_type; + using rhs_utype = typename bit_iterator::word_type; + using lhs_type = typename bit_iterator::difference_type; + using rhs_type = typename bit_iterator::difference_type; + using difference_type = typename std::common_type::type; + constexpr difference_type lhs_digits = binary_digits::value; + constexpr difference_type rhs_digits = binary_digits::value; + constexpr difference_type digits = rhs_digits; + static_assert(lhs_digits == rhs_digits, ""); + const difference_type main = lhs._current - rhs._current; + return main * digits + (static_cast(lhs._position) - static_cast(rhs._position)); } -// -------------------------------------------------------------------------- // - - - -// ------------------- BIT ITERATOR: COMPARISON OPERATORS ------------------- // -// Checks if the left hand side is equal to the right hand side -template -constexpr bool operator==( - const bit_iterator& lhs, - const bit_iterator& rhs -) -{ - return lhs._current == rhs._current && lhs._position == rhs._position; -} - -// Checks if the left hand side is non equal to the right hand side -template -constexpr bool operator!=( - const bit_iterator& lhs, - const bit_iterator& rhs -) -{ - return lhs._current != rhs._current || lhs._position != rhs._position; -} - -// Checks if the left hand side is less than the right hand side -template -constexpr bool operator<( - const bit_iterator& lhs, - const bit_iterator& rhs -) -{ - return lhs._current < rhs._current - || (lhs._current == rhs._current && lhs._position < rhs._position); -} - -// Checks if the left hand side is less than or equal to the right hand side -template -constexpr bool operator<=( - const bit_iterator& lhs, - const bit_iterator& rhs -) -{ - return lhs._current < rhs._current - || (lhs._current == rhs._current && lhs._position <= rhs._position); -} - -// Checks if the left hand side is greater than the right hand side -template -constexpr bool operator>( - const bit_iterator& lhs, - const bit_iterator& rhs -) -{ - return lhs._current > rhs._current - || (lhs._current == rhs._current && lhs._position > rhs._position); -} - -// Checks if the left hand side is greater than or equal to the right hand side -template -constexpr bool operator>=( - const bit_iterator& lhs, - const bit_iterator& rhs -) -{ - return lhs._current > rhs._current - || (lhs._current == rhs._current && lhs._position >= rhs._position); -} -// -------------------------------------------------------------------------- // - +static_assert(bit_iterator_c>, "bit_iterator does not satisfy bit_iterator_c concept!"); +static_assert(bit_pointer_c, bit_reference>, "bit_pointer does not satisfy bit_pointer_c concept!"); +static_assert(bit_iterator_c>, "bit_pointer does not satisfy bit_iterator_c concept!"); // ========================================================================== // } // namespace bit diff --git a/include/bitlib/bit-iterator/bit_pointer.hpp b/include/bitlib/bit-iterator/bit_pointer.hpp deleted file mode 100644 index 4a564726..00000000 --- a/include/bitlib/bit-iterator/bit_pointer.hpp +++ /dev/null @@ -1,496 +0,0 @@ -// ============================== BIT POINTER =============================== // -// Project: The C++ Bit Library -// Name: bit_pointer.hpp -// Description: A class representing a pointer to a bit -// Creator: Vincent Reverdy -// Contributor(s): Vincent Reverdy [2015-2017] -// License: BSD 3-Clause License -// ========================================================================== // -#ifndef _BIT_POINTER_HPP_INCLUDED -#define _BIT_POINTER_HPP_INCLUDED -// ========================================================================== // - - - -// ================================ PREAMBLE ================================ // -// C++ standard library -// Project sources -#include "bit_details.hpp" -// Third-party libraries -// Miscellaneous -namespace bit { -// ========================================================================== // - - - -/* ****************************** BIT POINTER ******************************* */ -// Bit pointer class definition -template -class bit_pointer -{ - // Assertions - static_assert(binary_digits::value, ""); - - // Friendship - template friend class bit_pointer; - - // Types - public: - using word_type = WordType; - using size_type = std::size_t; - using difference_type = std::ptrdiff_t; - - // Lifecycle - public: - constexpr bit_pointer() noexcept; - template - constexpr bit_pointer(const bit_pointer& other) noexcept; - constexpr bit_pointer(std::nullptr_t) noexcept; - explicit constexpr bit_pointer(word_type* ptr) noexcept; - constexpr bit_pointer(word_type* ptr, size_type pos); - - // Assignment - public: - constexpr bit_pointer& operator=(std::nullptr_t) noexcept; - constexpr bit_pointer& operator=(const bit_pointer& other) noexcept; - template - constexpr bit_pointer& operator=(const bit_pointer& other) noexcept; - - // Conversion - public: - explicit constexpr operator bool() const noexcept; - - // Access - public: - constexpr bit_reference operator*() const noexcept; - constexpr bit_reference* operator->() const noexcept; - constexpr bit_reference operator[](difference_type n) const; - - // Increment and decrement operators - public: - constexpr bit_pointer& operator++(); - constexpr bit_pointer& operator--(); - constexpr bit_pointer operator++(int); - constexpr bit_pointer operator--(int); - constexpr bit_pointer operator+(difference_type n) const; - constexpr bit_pointer operator-(difference_type n) const; - constexpr bit_pointer& operator+=(difference_type n); - constexpr bit_pointer& operator-=(difference_type n); - - // Implementation details: data members - private: - bit_reference _ref; - - // Non-member arithmetic operators - template - friend constexpr bit_pointer operator+( - typename bit_pointer::difference_type n, - bit_pointer x - ); - template - friend constexpr typename std::common_type< - typename bit_pointer::difference_type, - typename bit_pointer::difference_type - >::type operator-( - bit_pointer lhs, - bit_pointer rhs - ); - - // Comparison operators - template - friend constexpr bool operator==( - bit_pointer lhs, - bit_pointer rhs - ) noexcept; - template - friend constexpr bool operator!=( - bit_pointer lhs, - bit_pointer rhs - ) noexcept; - template - friend constexpr bool operator<( - bit_pointer lhs, - bit_pointer rhs - ) noexcept; - template - friend constexpr bool operator<=( - bit_pointer lhs, - bit_pointer rhs - ) noexcept; - template - friend constexpr bool operator>( - bit_pointer lhs, - bit_pointer rhs - ) noexcept; - template - friend constexpr bool operator>=( - bit_pointer lhs, - bit_pointer rhs - ) noexcept; -}; -/* ************************************************************************** */ - - - -// ------------------------- BIT POINTER: LIFECYCLE ------------------------- // -// Implicitly default constructs a null bit pointer -template -constexpr bit_pointer::bit_pointer( -) noexcept -: _ref(nullptr) -{ -} - -// Implicitly constructs a bit pointer from another bit pointer -template -template -constexpr bit_pointer::bit_pointer( - const bit_pointer& other -) noexcept -: _ref(other._ref) -{ -} - -// Explicitly constructs a bit pointer from a null pointer -template -constexpr bit_pointer::bit_pointer( - std::nullptr_t -) noexcept -: _ref(nullptr) -{ -} - -// Explicitly constructs an aligned bit pointer from a pointer -template -constexpr bit_pointer::bit_pointer( - word_type* ptr -) noexcept -: _ref(ptr) -{ -} - -// Explicitly constructs an unaligned bit pointer from a pointer -template -constexpr bit_pointer::bit_pointer( - word_type* ptr, - size_type pos -) -: _ref(ptr, pos) -{ -} -// -------------------------------------------------------------------------- // - - - -// ------------------------ BIT POINTER: ASSIGNMENT ------------------------- // -// Assigns a null pointer to the bit pointer -template -constexpr bit_pointer& bit_pointer::operator=( - std::nullptr_t -) noexcept -{ - _ref._ptr = nullptr; - _ref._mask = 0; - return *this; -} - -// Copies a bit pointer to the bit pointer -template -constexpr bit_pointer& bit_pointer::operator=( - const bit_pointer& other -) noexcept -{ - _ref._ptr = other._ref._ptr; - _ref._mask = other._ref._mask; - return *this; -} - -// Assigns a bit pointer to the bit pointer -template -template -constexpr bit_pointer& bit_pointer::operator=( - const bit_pointer& other -) noexcept -{ - _ref._ptr = other._ref._ptr; - _ref._mask = other._ref._mask; - return *this; -} -// -------------------------------------------------------------------------- // - - - -// ------------------------ BIT POINTER: CONVERSION ------------------------- // -// Returns true if the bit pointer is null, false otherwise -template -constexpr bit_pointer::operator bool( -) const noexcept -{ - return _ref._ptr; -} -// -------------------------------------------------------------------------- // - - - -// -------------------------- BIT POINTER: ACCESS --------------------------- // -// Gets a bit reference from the bit pointer -template -constexpr bit_reference bit_pointer::operator*( -) const noexcept -{ - return _ref; -} - -// Gets a pointer to a bit reference -template -constexpr bit_reference* bit_pointer::operator->( -) const noexcept -{ - return std::addressof(const_cast&>(_ref)); -} - -// Gets a bit reference, decrementing or incrementing the pointer -template -constexpr bit_reference bit_pointer::operator[]( - difference_type n -) const -{ - constexpr difference_type digits = binary_digits::value; - const difference_type sum = _ref.position() + n; - difference_type diff = sum / digits; - if (sum < 0 && diff * digits != sum) { - --diff; - } - return bit_reference(_ref._ptr + diff, sum - diff * digits); -} -// -------------------------------------------------------------------------- // - - - -// ------------- BIT POINTER: INCREMENT AND DECREMENT OPERATORS ------------- // -// Increments the bit pointer and returns it -template -constexpr bit_pointer& bit_pointer::operator++( -) -{ - using type = typename std::remove_cv::type; - constexpr size_type digits = binary_digits::value; - constexpr type one = 1; - constexpr type mask = one; - const size_type pos = _ref.position(); - if (pos + 1 < digits) { - _ref._mask <<= 1; - } else { - ++_ref._ptr; - _ref._mask = mask; - } - return *this; -} - -// Decrements the bit pointer and returns it -template -constexpr bit_pointer& bit_pointer::operator--( -) -{ - using type = typename std::remove_cv::type; - constexpr size_type digits = binary_digits::value; - constexpr type one = 1; - constexpr type mask = static_cast(one << (digits - 1)); - const size_type pos = _ref.position(); - if (pos) { - _ref._mask >>= 1; - } else { - --_ref._ptr; - _ref._mask = mask; - } - return *this; -} - -// Increments the bit pointer and returns the old one -template -constexpr bit_pointer bit_pointer::operator++( - int -) -{ - bit_pointer old = *this; - ++(*this); - return old; -} - -// Decrements the bit pointer and returns the old one -template -constexpr bit_pointer bit_pointer::operator--( - int -) -{ - bit_pointer old = *this; - --(*this); - return old; -} - -// Looks forward several bits and gets a pointer at this position -template -constexpr bit_pointer bit_pointer::operator+( - difference_type n -) const -{ - constexpr difference_type digits = binary_digits::value; - const difference_type sum = _ref.position() + n; - difference_type diff = sum / digits; - if (sum < 0 && diff * digits != sum) { - --diff; - } - return bit_pointer(_ref._ptr + diff, sum - diff * digits); -} - -// Looks backward several bits and gets a pointer at this position -template -constexpr bit_pointer bit_pointer::operator-( - difference_type n -) const -{ - constexpr difference_type digits = binary_digits::value; - const difference_type sum = _ref.position() - n; - difference_type diff = sum / digits; - if (sum < 0 && diff * digits != sum) { - --diff; - } - return bit_pointer(_ref._ptr + diff, sum - diff * digits); -} - -// Increments the pointer by several bits and returns it -template -constexpr bit_pointer& bit_pointer::operator+=( - difference_type n -) -{ - *this = *this + n; - return *this; -} - -// Decrements the pointer by several bits and returns it -template -constexpr bit_pointer& bit_pointer::operator-=( - difference_type n -) -{ - *this = *this - n; - return *this; -} -// -------------------------------------------------------------------------- // - - - -// -------------- BIT POINTER: NON-MEMBER ARITHMETIC OPERATORS -------------- // -// Advances the bit pointer several times -template -constexpr bit_pointer operator+( - typename bit_pointer::difference_type n, - bit_pointer x -) -{ - return x + n; -} - -// Computes the distance in bits separating two bit pointers -template -constexpr typename std::common_type< - typename bit_pointer::difference_type, - typename bit_pointer::difference_type ->::type operator-( - bit_pointer lhs, - bit_pointer rhs -) -{ - using lhs_type = typename bit_pointer::difference_type; - using rhs_type = typename bit_pointer::difference_type; - using difference_type = typename std::common_type::type; - constexpr difference_type lhs_digits = binary_digits::value; - constexpr difference_type rhs_digits = binary_digits::value; - constexpr difference_type digits = rhs_digits; - static_assert(lhs_digits == rhs_digits, ""); - const difference_type main = lhs._ref.address() - rhs._ref.address(); - return main * digits + (lhs._ref.position() - rhs._ref.position()); -} -// -------------------------------------------------------------------------- // - - - -// ------------------- BIT POINTER: COMPARISON OPERATORS -------------------- // -// Checks if the left hand side is equal to the right hand side -template -constexpr bool operator==( - bit_pointer lhs, - bit_pointer rhs -) noexcept -{ - return lhs._ref.address() == rhs._ref.address() - && lhs._ref.position() == rhs._ref.position(); -} - -// Checks if the left hand side is non equal to the right hand side -template -constexpr bool operator!=( - bit_pointer lhs, - bit_pointer rhs -) noexcept -{ - return lhs._ref.address() != rhs._ref.address() - || lhs._ref.position() != rhs._ref.position(); -} - -// Checks if the left hand side is less than the right hand side -template -constexpr bool operator<( - bit_pointer lhs, - bit_pointer rhs -) noexcept -{ - return lhs._ref.address() < rhs._ref.address() - || (lhs._ref.address() == rhs._ref.address() - && lhs._ref.position() < rhs._ref.position()); -} - -// Checks if the left hand side is less than or equal to the right hand side -template -constexpr bool operator<=( - bit_pointer lhs, - bit_pointer rhs -) noexcept -{ - return lhs._ref.address() < rhs._ref.address() - || (lhs._ref.address() == rhs._ref.address() - && lhs._ref.position() <= rhs._ref.position()); -} - -// Checks if the left hand side is greater than the right hand side -template -constexpr bool operator>( - bit_pointer lhs, - bit_pointer rhs -) noexcept -{ - return lhs._ref.address() > rhs._ref.address() - || (lhs._ref.address() == rhs._ref.address() - && lhs._ref.position() > rhs._ref.position()); -} - -// Checks if the left hand side is greater than or equal to the right hand side -template -constexpr bool operator>=( - bit_pointer lhs, - bit_pointer rhs -) noexcept -{ - return lhs._ref.address() > rhs._ref.address() - || (lhs._ref.address() == rhs._ref.address() - && lhs._ref.position() >= rhs._ref.position()); -} -// -------------------------------------------------------------------------- // - - - -// ========================================================================== // -} // namespace bit -#endif // _BIT_POINTER_HPP_INCLUDED -// ========================================================================== // diff --git a/include/bitlib/bit-iterator/bit_reference.hpp b/include/bitlib/bit-iterator/bit_reference.hpp index 93441581..17af860c 100644 --- a/include/bitlib/bit-iterator/bit_reference.hpp +++ b/include/bitlib/bit-iterator/bit_reference.hpp @@ -15,7 +15,11 @@ // ================================ PREAMBLE ================================ // // C++ standard library // Project sources +#include +#include + #include "bit_details.hpp" +#include "bit_value.hpp" // Third-party libraries // Miscellaneous namespace bit { @@ -25,156 +29,115 @@ namespace bit { /* ***************************** BIT REFERENCE ****************************** */ // Bit reference class definition -template -class bit_reference -{ - // Assertions - static_assert(binary_digits::value, ""); - - // Friendship - template friend class bit_reference; - friend class bit_pointer; - - // Types - public: - using word_type = WordType; - using size_type = std::size_t; - - // Lifecycle - public: - template - constexpr bit_reference(const bit_reference& other) noexcept; - constexpr bit_reference(const bit_reference& other) noexcept; - explicit constexpr bit_reference(word_type& ref) noexcept; - constexpr bit_reference(word_type& ref, size_type pos); - - // Assignment - public: - constexpr bit_reference& operator=(const bit_reference& other) noexcept; - template - constexpr bit_reference& operator=(const bit_reference& other) noexcept; - constexpr bit_reference& operator=(bit_value val) noexcept; - constexpr bit_reference& assign(word_type val) noexcept; - constexpr bit_reference& assign(word_type val, size_type pos); - - // Bitwise assignment operators - public: - constexpr bit_reference& operator&=(bit_value other) noexcept; - constexpr bit_reference& operator|=(bit_value other) noexcept; - constexpr bit_reference& operator^=(bit_value other) noexcept; - - // Conversion - public: - explicit constexpr operator bool() const noexcept; - - // Access - public: - constexpr bit_pointer operator&() const noexcept; - - // Swap members - public: - template - void swap(bit_reference other); - void swap(bit_value& other); - - // Bit manipulation - public: - constexpr bit_reference& set(bool b) noexcept; - constexpr bit_reference& set() noexcept; - constexpr bit_reference& reset() noexcept; - constexpr bit_reference& flip() noexcept; - - // Underlying details - public: - constexpr word_type* address() const noexcept; - constexpr size_type position() const noexcept; - constexpr typename std::remove_cv::type mask() const noexcept; - - // Implementation details: function members - private: - bit_reference() noexcept = default; - explicit constexpr bit_reference(std::nullptr_t) noexcept; - explicit constexpr bit_reference(word_type* ptr) noexcept; - constexpr bit_reference(word_type* ptr, size_type pos); - - // Implementation details: data members - private: - word_type* _ptr; - typename std::remove_cv::type _mask; +template +class bit_reference { + public: + using WordType = exact_floor_integral_t>; + using const_word_type = std::add_const_t; + + private: + // Assertions + static_assert(binary_digits::value, ""); + + // Friendship + template + friend class bit_reference; + + // Types + public: + using word_type = WordType; + using size_type = std::size_t; + using mask_type = std::make_unsigned_t; + + // Lifecycle + public: + constexpr bit_reference(const bit_reference& other) noexcept; + explicit constexpr bit_reference(const WordRef& ref) noexcept; + constexpr bit_reference(const WordRef& ref, size_type pos); + + // Assignment + public: + constexpr bit_reference& operator=(const bit_reference& other) const noexcept; + template + constexpr bit_reference& operator=(const bit_reference& other) const noexcept; + constexpr bit_reference& operator=(const bit_value val) const noexcept; + constexpr bit_reference& assign(word_type val) const noexcept; + constexpr bit_reference& assign(word_type val, size_type pos) const; + + // Bitwise assignment operators + public: + constexpr bit_reference& operator&=(bit_value other) const noexcept; + constexpr bit_reference& operator|=(bit_value other) const noexcept; + constexpr bit_reference& operator^=(bit_value other) const noexcept; + + // Conversion + public: + explicit constexpr operator bool() const noexcept; + + // Access + public: + constexpr bit_pointer operator&() const noexcept; + constexpr bit_pointer operator&() noexcept; + + // Swap members + public: + template + void swap(bit_reference other) const; + void swap(bit_value& other) const; + + // Bit manipulation + public: + constexpr bit_reference& set(bool b) const noexcept; + constexpr bit_reference& set() const noexcept; + constexpr bit_reference& reset() const noexcept; + constexpr bit_reference& flip() const noexcept; + + // Implementation details: function members + private: + bit_reference() noexcept = default; + explicit constexpr bit_reference(word_type* ptr) noexcept; + constexpr bit_reference(word_type* ptr, size_type pos); + + // Implementation details: data members + private: + WordRef _ref; + const mask_type _mask; }; +static_assert(bit_reference_c>, "bit_reference does not satisfy bit_reference_c concept!"); // Swap template -void swap( - bit_reference lhs, - bit_reference rhs -) noexcept; +void swap(bit_reference lhs, bit_reference rhs) noexcept; template -void swap( - bit_reference lhs, - bit_value& rhs -) noexcept; +void swap(bit_reference lhs, bit_value& rhs) noexcept; template -void swap( - bit_value& lhs, - bit_reference rhs -) noexcept; +void swap(bit_value& lhs, bit_reference rhs) noexcept; // Stream functions template -std::basic_istream& operator>>( - std::basic_istream& is, - bit_reference& x -); +std::basic_istream& operator>>(std::basic_istream& is, bit_reference& x); template -std::basic_ostream& operator<<( - std::basic_ostream& os, - bit_reference x -); +std::basic_ostream& operator<<(std::basic_ostream& os, bit_reference x); /* ************************************************************************** */ - - // ------------------------ BIT REFERENCE: LIFECYCLE ------------------------ // -// Implicitly constructs a bit reference from another bit reference -template -template -constexpr bit_reference::bit_reference( - const bit_reference& other -) noexcept -: _ptr(other._ptr) -, _mask(other._mask) -{ -} -template -constexpr bit_reference::bit_reference( - const bit_reference& other -) noexcept -: _ptr(other._ptr) -, _mask(other._mask) -{ +template +constexpr bit_reference::bit_reference(const bit_reference& other) noexcept + : _ref(other._ref), _mask(other._mask) { } // Explicitly constructs an aligned bit reference -template -constexpr bit_reference::bit_reference( - word_type& ref -) noexcept -: _ptr(&ref) -, _mask(1) -{ +template +constexpr bit_reference::bit_reference(const WordRef& ref) noexcept + : _ref(ref), _mask(1) { } // Explicitly constructs an unaligned bit reference -template -constexpr bit_reference::bit_reference( - word_type& ref, - size_type pos -) -: _ptr((assert(pos < binary_digits::value), &ref)) -, _mask(static_cast(1) << pos) -{ +template +constexpr bit_reference::bit_reference(const WordRef& ref, size_type pos) + : _ref(ref), _mask(static_cast(1) << pos) { + assert(pos < binary_digits::value); } // -------------------------------------------------------------------------- // @@ -182,56 +145,40 @@ constexpr bit_reference::bit_reference( // ----------------------- BIT REFERENCE: ASSIGNMENT ------------------------ // // Copies a bit reference to the bit reference -template -constexpr bit_reference& bit_reference::operator=( - const bit_reference& other -) noexcept -{ - other ? set() : reset(); - return *this; +template +constexpr bit_reference& bit_reference::operator=(const bit_reference& other) const noexcept { + other ? set() : reset(); + return const_cast&>(*this); } // Assigns a bit reference to the bit reference -template +template template -constexpr bit_reference& bit_reference::operator=( - const bit_reference& other -) noexcept -{ - other ? set() : reset(); - return *this; +constexpr bit_reference& bit_reference::operator=(const bit_reference& other) const noexcept { + other ? set() : reset(); + return const_cast&>(*this); } // Assigns a bit value to the bit reference -template -constexpr bit_reference& bit_reference::operator=( - bit_value val -) noexcept -{ - val ? set() : reset(); - return *this; +template +constexpr bit_reference& bit_reference::operator=(const bit_value val) const noexcept { + val ? set() : reset(); + return const_cast&>(*this); } // Assigns the aligned bit of a value to the bit reference -template -constexpr bit_reference& bit_reference::assign( - word_type val -) noexcept -{ - val & 1 ? set() : reset(); - return *this; +template +constexpr bit_reference& bit_reference::assign(word_type val) const noexcept { + val & 1 ? set() : reset(); + return const_cast&>(*this); } // Assigns an unaligned bit of a value to the bit reference -template -constexpr bit_reference& bit_reference::assign( - word_type val, - size_type pos -) -{ - assert(pos < binary_digits::value); - val >> pos & 1 ? set() : reset(); - return *this; +template +constexpr bit_reference& bit_reference::assign(word_type val, size_type pos) const { + assert(pos < binary_digits::value); + ((val >> pos) & 1) ? set() : reset(); + return const_cast&>(*this); } // -------------------------------------------------------------------------- // @@ -239,33 +186,24 @@ constexpr bit_reference& bit_reference::assign( // -------------- BIT REFERENCE: BITWISE ASSIGNMENT OPERATORS --------------- // // Assigns the value of the referenced bit through a bitwise and operation -template -constexpr bit_reference& bit_reference::operator&=( - bit_value other -) noexcept -{ - *_ptr &= ~(_mask * static_cast(!other._value)); - return *this; +template +constexpr bit_reference& bit_reference::operator&=(bit_value other) const noexcept { + _ref &= ~(_mask * static_cast(!other._value)); + return const_cast&>(*this); } // Assigns the value of the referenced bit through a bitwise or operation -template -constexpr bit_reference& bit_reference::operator|=( - bit_value other -) noexcept -{ - *_ptr |= _mask * static_cast(other._value); - return *this; +template +constexpr bit_reference& bit_reference::operator|=(bit_value other) const noexcept { + _ref |= _mask * static_cast(other._value); + return const_cast&>(*this); } // Assigns the value of the referenced bit through a bitwise xor operation -template -constexpr bit_reference& bit_reference::operator^=( - bit_value other -) noexcept -{ - *_ptr ^= _mask * static_cast(other._value); - return *this; +template +constexpr bit_reference& bit_reference::operator^=(bit_value other) const noexcept { + _ref ^= _mask * static_cast(other._value); + return const_cast&>(*this); } // -------------------------------------------------------------------------- // @@ -273,11 +211,9 @@ constexpr bit_reference& bit_reference::operator^=( // ----------------------- BIT REFERENCE: CONVERSION ------------------------ // // Explicitly converts the bit reference to a boolean value -template -constexpr bit_reference::operator bool( -) const noexcept -{ - return *_ptr & _mask; +template +constexpr bit_reference::operator bool() const noexcept { + return _ref & _mask; } // -------------------------------------------------------------------------- // @@ -285,11 +221,14 @@ constexpr bit_reference::operator bool( // ------------------------- BIT REFERENCE: ACCESS -------------------------- // // Gets a bit pointer from the bit reference -template -constexpr bit_pointer bit_reference::operator&( -) const noexcept -{ - return bit_pointer(_ptr, position()); +template +constexpr bit_pointer::const_word_type> bit_reference::operator&() const noexcept { + return bit_pointer(&_ref, std::countr_zero(_mask)); +} + +template +constexpr bit_pointer::WordType> bit_reference::operator&() noexcept { + return bit_pointer(&_ref, std::countr_zero(_mask)); } // -------------------------------------------------------------------------- // @@ -297,28 +236,22 @@ constexpr bit_pointer bit_reference::operator&( // ---------------------- BIT REFERENCE: SWAP MEMBERS ----------------------- // // Swaps the value of the referenced bit with another bit reference -template +template template -void bit_reference::swap( - bit_reference other -) -{ - if (other != *this) { - flip(); - other.flip(); - } +void bit_reference::swap(bit_reference other) const { + if (other != *this) { + flip(); + other.flip(); + } } // Swaps the value of the referenced bit with a bit value -template -void bit_reference::swap( - bit_value& other -) -{ - if (other != *this) { - flip(); - other.flip(); - } +template +void bit_reference::swap(bit_value& other) const { + if (other != *this) { + flip(); + other.flip(); + } } // -------------------------------------------------------------------------- // @@ -326,115 +259,61 @@ void bit_reference::swap( // -------------------- BIT REFERENCE: BIT MANIPULATION --------------------- // // Sets the value of the referenced bit to the provided boolean value -template -constexpr bit_reference& bit_reference::set( - bool b -) noexcept -{ - b ? set() : reset(); - return *this; +template +constexpr bit_reference& bit_reference::set( + bool b) const noexcept { + b ? set() : reset(); + return const_cast&>(*this); } // Sets the value of the referenced bit to 1 -template -constexpr bit_reference& bit_reference::set( -) noexcept -{ - *_ptr |= _mask; - return *this; +template +constexpr bit_reference& bit_reference::set() const noexcept { + _ref |= _mask; + return const_cast&>(*this); } // Resets the value of the referenced bit to 0 -template -constexpr bit_reference& bit_reference::reset( -) noexcept -{ - *_ptr &= ~_mask; - return *this; +template +constexpr bit_reference& bit_reference::reset() const noexcept { + _ref &= static_cast(~_mask); + return const_cast&>(*this); } // Flips the value of the referenced bit -template -constexpr bit_reference& bit_reference::flip( -) noexcept -{ - *_ptr ^= _mask; - return *this; -} -// -------------------------------------------------------------------------- // - - - -// ------------------- BIT REFERENCE: UNDERLYING DETAILS -------------------- // -// Returns a pointer to the underlying word -template -constexpr typename bit_reference::word_type* -bit_reference::address( -) const noexcept -{ - return _ptr; -} - -// Returns the position of the referenced bit within the underlying word -template -constexpr typename bit_reference::size_type -bit_reference::position( -) const noexcept -{ - return _tzcnt(_mask); -} - -// Returns a mask corresponding to the referenced bit -template -constexpr typename std::remove_cv< - typename bit_reference::word_type ->::type bit_reference::mask( -) const noexcept -{ - return _mask; +template +constexpr bit_reference& bit_reference::flip() const noexcept { + _ref ^= _mask; + return const_cast&>(*this); } // -------------------------------------------------------------------------- // - - // -------------------------- BIT REFERENCE: SWAP --------------------------- // // Swaps two bit references template -void swap( - bit_reference lhs, - bit_reference rhs -) noexcept -{ - if (lhs != rhs) { - lhs.flip(); - rhs.flip(); - } +void swap(bit_reference lhs, bit_reference rhs) noexcept { + if (lhs != rhs) { + lhs.flip(); + rhs.flip(); + } } // Swaps a bit reference and a bit value template -void swap( - bit_reference lhs, - bit_value& rhs -) noexcept -{ - if (lhs != rhs) { - lhs.flip(); - rhs.flip(); - } +void swap(bit_reference lhs, bit_value& rhs) noexcept { + if (lhs != rhs) { + lhs.flip(); + rhs.flip(); + } } // Swaps a bit value and a bit reference template -void swap( - bit_value& lhs, - bit_reference rhs -) noexcept -{ - if (lhs != rhs) { - lhs.flip(); - rhs.flip(); - } +void swap(bit_value& lhs, bit_reference rhs) noexcept { + if (lhs != rhs) { + lhs.flip(); + rhs.flip(); + } } // -------------------------------------------------------------------------- // @@ -443,101 +322,73 @@ void swap( // -------------------- BIT REFERENCE: STREAM FUNCTIONS --------------------- // // Extracts a bit reference from an input stream template -std::basic_istream& operator>>( - std::basic_istream& is, - bit_reference& x -) -{ - using stream_type = std::basic_istream; - using traits_type = typename stream_type::traits_type; - using ios_base = typename stream_type::ios_base; - constexpr char zero = '0'; - constexpr char one = '1'; - constexpr typename stream_type::int_type eof = traits_type::eof(); - typename ios_base::iostate state = ios_base::goodbit; - typename stream_type::char_type char_value = 0; - typename stream_type::int_type int_value = 0; - typename stream_type::sentry sentry(is); - bool ok = false; - bit_value tmp = x; - if (sentry) { - try { - int_value = is.rdbuf()->sbumpc(); - if (traits_type::eq_int_type(int_value, eof)) { - state |= ios_base::eofbit; - } else { - char_value = traits_type::to_char_type(int_value); - if (traits_type::eq(char_value, is.widen(zero))) { - tmp.reset(); - ok = true; - } else if (traits_type::eq(char_value, is.widen(one))) { - tmp.set(); - ok = true; - } else { - int_value = is.rdbuf()->sputbackc(char_value); - if (traits_type::eq_int_type(int_value, eof)) { - state |= ios_base::failbit; - } - } - } - } catch(...) { - is.setstate(ios_base::badbit); +std::basic_istream& operator>>(std::basic_istream& is, bit_reference& x) { + using stream_type = std::basic_istream; + using traits_type = typename stream_type::traits_type; + using ios_base = typename stream_type::ios_base; + constexpr char zero = '0'; + constexpr char one = '1'; + constexpr typename stream_type::int_type eof = traits_type::eof(); + typename ios_base::iostate state = ios_base::goodbit; + typename stream_type::char_type char_value = 0; + typename stream_type::int_type int_value = 0; + typename stream_type::sentry sentry(is); + bool ok = false; + bit_value tmp = x; + if (sentry) { + try { + int_value = is.rdbuf()->sbumpc(); + if (traits_type::eq_int_type(int_value, eof)) { + state |= ios_base::eofbit; + } else { + char_value = traits_type::to_char_type(int_value); + if (traits_type::eq(char_value, is.widen(zero))) { + tmp.reset(); + ok = true; + } else if (traits_type::eq(char_value, is.widen(one))) { + tmp.set(); + ok = true; + } else { + int_value = is.rdbuf()->sputbackc(char_value); + if (traits_type::eq_int_type(int_value, eof)) { + state |= ios_base::failbit; + } } + } + } catch (...) { + is.setstate(ios_base::badbit); } - if (ok) { - x = tmp; - } else { - state |= ios_base::failbit; - } - state ? is.setstate(state) : void(); - return is; + } + if (ok) { + x = tmp; + } else { + state |= ios_base::failbit; + } + state ? is.setstate(state) : void(); + return is; } // Inserts a bit reference in an output stream template -std::basic_ostream& operator<<( - std::basic_ostream& os, - bit_reference x -) -{ - constexpr char zero = '0'; - constexpr char one = '1'; - return os << os.widen(x ? one : zero); +std::basic_ostream& operator<<(std::basic_ostream& os, bit_reference x) { + constexpr char zero = '0'; + constexpr char one = '1'; + return os << os.widen(x ? one : zero); } // -------------------------------------------------------------------------- // - - // -------- BIT REFERENCE: IMPLEMENTATION DETAILS: FUNCTION MEMBERS --------- // -// Privately explicitly constructs a bit reference from a nullptr -template -constexpr bit_reference::bit_reference( - std::nullptr_t -) noexcept -: _ptr(nullptr) -, _mask() -{ -} - // Privately explicitly constructs an aligned bit reference from a pointer -template -constexpr bit_reference::bit_reference( - word_type* ptr -) noexcept -: _ptr(ptr) -, _mask(1) -{ +template +constexpr bit_reference::bit_reference(word_type* ptr) noexcept + : _ref(*ptr), _mask(1) { } // Privately explicitly constructs an unaligned bit reference from a pointer -template -constexpr bit_reference::bit_reference( - word_type* ptr, - size_type pos -) -: _ptr((assert(pos < binary_digits::value), ptr)) -, _mask(static_cast(1) << pos) -{ +template +constexpr bit_reference::bit_reference(word_type* ptr, size_type pos) + : _ref(*ptr), _mask(static_cast(1) << pos) { + assert(pos < binary_digits::value); } // -------------------------------------------------------------------------- // diff --git a/include/bitlib/bit-iterator/bit_value.hpp b/include/bitlib/bit-iterator/bit_value.hpp index 0fd6ee42..02f7c9ba 100644 --- a/include/bitlib/bit-iterator/bit_value.hpp +++ b/include/bitlib/bit-iterator/bit_value.hpp @@ -32,6 +32,7 @@ class bit_value // Types public: + static constexpr std::size_t bits = 1; using size_type = std::size_t; // Lifecycle @@ -47,14 +48,14 @@ class bit_value // Assignment public: - template - constexpr bit_value& operator=(bit_reference ref) noexcept; - template - constexpr bit_value& assign(WordType val) noexcept; - template - constexpr bit_value& assign(WordType val, size_type pos); - - // Bitwise assignment operators + template + constexpr bit_value& operator=(bit_reference ref) noexcept; + template + constexpr bit_value& assign(WordType val) noexcept; + template + constexpr bit_value& assign(WordType val, size_type pos); + + // Bitwise assignment operators public: constexpr bit_value& operator&=(bit_value other) noexcept; constexpr bit_value& operator|=(bit_value other) noexcept; diff --git a/include/bitlib/bit-iterator/bit_word_pointer_adapter.hpp b/include/bitlib/bit-iterator/bit_word_pointer_adapter.hpp new file mode 100644 index 00000000..b82045dd --- /dev/null +++ b/include/bitlib/bit-iterator/bit_word_pointer_adapter.hpp @@ -0,0 +1,200 @@ +#ifndef _BIT_WORD_POINTER_ADAPTER_HPP_ +#define _BIT_WORD_POINTER_ADAPTER_HPP_ + +#include "bit_details.hpp" + +namespace bit { + +template +class bit_word_reference_adapter; + +template +class bit_word_pointer_adapter { + private: + // Assertions + using _traits_t = _cv_iterator_traits; + static_assert(binary_digits::value, ""); + + public: + using target_word = std::remove_const_t; + using target_word_ref = _traits_t::reference; + using source_word = typename _cv_iterator_traits::value_type; + using source_word_ref = std::add_lvalue_reference_t; + + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using reference = bit_word_reference_adapter; + using pointer = bit_word_pointer_adapter; + + using iterator_type = target_word_ptr; + using iterator_category = typename _cv_iterator_traits::iterator_category; + using value_type = target_word; + + private: + static_assert(std::is_integral_v, "source_word_ptr must be a pointer to a integral type"); + static_assert(std::is_integral_v, "target_word must be an integral type"); + + static constexpr bool is_small_to_big = sizeof(target_word) > sizeof(source_word); + static constexpr bool is_big_to_small = sizeof(target_word) < sizeof(source_word); + static constexpr size_t ratio = is_small_to_big ? sizeof(target_word) / sizeof(source_word) + : sizeof(source_word) / sizeof(target_word); + + source_word_ptr _source; + size_type _index; + + public: + explicit constexpr bit_word_pointer_adapter(source_word_ptr source) + requires(is_small_to_big) + : _source(source) { + } + explicit constexpr bit_word_pointer_adapter(source_word_ptr source, const size_type index = 0) + requires(is_big_to_small) + : _source(source), _index(index) { + } + + constexpr bit_word_pointer_adapter(const bit_word_pointer_adapter&) = default; + constexpr bit_word_pointer_adapter& operator=(const bit_word_pointer_adapter&) = default; + constexpr bit_word_pointer_adapter(bit_word_pointer_adapter&&) = default; + constexpr bit_word_pointer_adapter& operator=(bit_word_pointer_adapter&&) = default; + constexpr ~bit_word_pointer_adapter() = default; + + constexpr reference operator*() const noexcept { + if constexpr (is_small_to_big) { + return reference(*_source); + } else { + return reference(*_source, _index); + } + } + + constexpr bit_word_pointer_adapter operator->() const noexcept { + return bit_word_pointer_adapter(&*_source, _index); + } + + constexpr reference operator[](difference_type n) const noexcept { + if constexpr (is_small_to_big) { + return reference(*std::next(_source, n * ratio)); + } else { + const difference_type sum = _index + n; + const difference_type src_diff = (sum - (n < 0) * static_cast(ratio - 1)) / static_cast(ratio); + const size_type new_index = sum % ratio; + return reference(*std::next(_source, src_diff), new_index); + } + } + + constexpr bit_word_pointer_adapter& operator++() noexcept { + if constexpr (is_small_to_big) { + _source = std::next(_source, ratio); + } else { + ++_index; + if (_index >= ratio) { + _source = std::next(_source); + _index -= ratio; + } + } + return *this; + } + constexpr bit_word_pointer_adapter operator++(int) noexcept { + bit_word_pointer_adapter old = *this; + ++(*this); + return old; + } + constexpr bit_word_pointer_adapter& operator--() noexcept { + if constexpr (is_small_to_big) { + _source = std::next(_source, -ratio); + } else { + if ((_index--) == 0) { + _source = std::prev(_source); + _index += ratio; + } + } + return *this; + } + constexpr bit_word_pointer_adapter operator--(int) noexcept { + bit_word_pointer_adapter old = *this; + --(*this); + return old; + } + constexpr bit_word_pointer_adapter operator+(difference_type n) const noexcept { + if constexpr (is_small_to_big) { + return bit_word_pointer_adapter(std::next(_source, n * ratio)); + } else { + const difference_type sum = _index + n; + const difference_type src_diff = (sum - (n < 0) * static_cast(ratio - 1)) / static_cast(ratio); + const size_type new_index = static_cast(sum) % ratio; + return bit_word_pointer_adapter(std::next(_source, src_diff), new_index); + } + } + constexpr bit_word_pointer_adapter operator-(difference_type n) const noexcept { + if constexpr (is_small_to_big) { + return bit_word_pointer_adapter(std::next(_source, -n * ratio)); + } else { + const difference_type sum = _index - n; + const difference_type src_diff = (sum - (n > 0) * static_cast(ratio - 1)) / static_cast(ratio); + const size_type new_index = static_cast(sum) % ratio; + return bit_word_pointer_adapter(std::next(_source, src_diff), new_index); + } + } + constexpr bit_word_pointer_adapter& operator+=(difference_type n) noexcept { + if constexpr (is_small_to_big) { + _source = std::next(_source, n * ratio); + } else { + const difference_type sum = _index + n; + const difference_type src_diff = (sum - (n < 0) * static_cast(ratio - 1)) / static_cast(ratio); + const size_type new_index = static_cast(sum) % ratio; + _source = std::next(_source, src_diff); + _index = new_index; + } + return *this; + } + constexpr bit_word_pointer_adapter& operator-=(difference_type n) noexcept { + if constexpr (is_small_to_big) { + _source = std::next(_source, -n * ratio); + } else { + const difference_type sum = _index - n; + const difference_type src_diff = (sum - (n > 0) * static_cast(ratio - 1)) / static_cast(ratio); + const size_type new_index = static_cast(sum) % ratio; + _source = std::next(_source, src_diff); + _index = new_index; + } + return *this; + } + constexpr bool operator==(const bit_word_pointer_adapter&) const = default; + constexpr auto operator<=>(const bit_word_pointer_adapter&) const = default; + + constexpr size_type index() const noexcept { + return _index; + } + constexpr source_word_ptr base() const noexcept { + return _source; + } + template + friend constexpr auto operator-( + const bit_word_pointer_adapter& lhs, + const bit_word_pointer_adapter& rhs); +}; + +template +constexpr auto operator-( + const bit_word_pointer_adapter& lhs, + const bit_word_pointer_adapter& rhs) { + using lhs_type = typename bit_word_pointer_adapter::difference_type; + using rhs_type = typename bit_word_pointer_adapter::difference_type; + static_assert( + bit_word_pointer_adapter::ratio == + bit_word_pointer_adapter::ratio, + "Cannot subtract iterators with different ratios"); + if constexpr (bit_word_pointer_adapter::is_big_to_small) { + auto main = (lhs._source - rhs._source); + return main * static_cast>(bit_word_pointer_adapter::ratio) + (static_cast(lhs._index) - static_cast(rhs._index)); + } else { + // "small→large" mode: each base‐step is 1 small word, but difference is in big words: + auto small_diff = (lhs._source - rhs._source); + return small_diff / static_cast>(bit_word_pointer_adapter::ratio); + } +} + +static_assert(std::is_same_v>, uint8_t>); + +} // namespace bit + +#endif // _BIT_WORD_POINTER_ADAPTER_HPP_ diff --git a/include/bitlib/bit-iterator/bit_word_reference_adapter.hpp b/include/bitlib/bit-iterator/bit_word_reference_adapter.hpp new file mode 100644 index 00000000..88b53b17 --- /dev/null +++ b/include/bitlib/bit-iterator/bit_word_reference_adapter.hpp @@ -0,0 +1,115 @@ +#ifndef _BIT_WORD_REFERENCE_ADAPTER_HPP_ +#define _BIT_WORD_REFERENCE_ADAPTER_HPP_ + +namespace bit { + +template +class bit_word_pointer_adapter; + +template +class bit_word_reference_adapter { + private: + // Assertions + static_assert(std::is_reference_v, "source_word_ref must be a reference type"); + + using source_word = std::remove_reference_t; + using source_word_ptr = std::add_pointer_t; + using target_word = std::remove_cvref_t; + using target_word_ptr = std::add_pointer_t; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using pointer = bit_word_pointer_adapter; + + static_assert(std::is_integral_v, "source_word_ref must reference to a integral type"); + static_assert(std::is_integral_v, "target_word must be an integral type"); + + static constexpr bool is_small_to_big = sizeof(target_word) > sizeof(source_word); + static constexpr bool is_big_to_small = sizeof(target_word) < sizeof(source_word); + static constexpr size_t ratio = is_small_to_big ? sizeof(target_word) / sizeof(source_word) + : sizeof(source_word) / sizeof(target_word); + + source_word_ref _source; + size_type _index; + + public: + // Constructor from source + explicit constexpr bit_word_reference_adapter(source_word_ref source) + requires(is_small_to_big) + : _source(source), _index(0) { + } + constexpr bit_word_reference_adapter(source_word_ref source, const size_type index) + requires(is_big_to_small) + : _source(source), _index(index) { + } + explicit constexpr bit_word_reference_adapter(const bit_word_reference_adapter& other) + : _source(other._source), _index(other._index) { + } + constexpr bit_word_reference_adapter& operator=(const bit_word_reference_adapter& other) { + if constexpr (is_small_to_big) { + *reinterpret_cast(&_source) = *reinterpret_cast(&other._source); + } else { + *(reinterpret_cast(&_source) + _index) = *(reinterpret_cast(&other._source) + other._index); + } + return *this; + } + + constexpr ~bit_word_reference_adapter() = default; + + constexpr pointer operator&() const noexcept { + if constexpr (is_small_to_big) { + return pointer(&_source); + } else { + return pointer(&_source, _index); + } + } + + // Assignment from value type. + // NOT safe for big-endian systems + constexpr bit_word_reference_adapter& operator=(const target_word& value) noexcept { + if constexpr (is_small_to_big) { + *reinterpret_cast(&_source) = value; + } else { + *(reinterpret_cast(&_source) + _index) = value; + } + return *this; + } + + // Implicit conversion to value type + // NOT safe for big-endian systems + constexpr operator target_word() const noexcept { + if constexpr (is_small_to_big) { + return *reinterpret_cast(&_source); + } else { + return *(reinterpret_cast(&_source) + _index); + } + } + +#if 0 + // Compound assignment (+=, -=, etc.) + bit_word_reference_adapter& operator+=(const target_word&); + bit_word_reference_adapter& operator-=(const target_word&); + // ... others like *=, /=, etc. + + // Increment/decrement + bit_word_reference_adapter& operator++(); // Prefix ++ + target_word operator++(int); // Postfix ++ + bit_word_reference_adapter& operator--(); // Prefix -- + target_word operator--(int); // Postfix -- +#endif + + // Swap support + + void swap(bit_word_reference_adapter& other) noexcept { + std::swap(_source, other._source); + std::swap(_index, other._index); + } + + friend void swap(bit_word_reference_adapter a, bit_word_reference_adapter b) { + std::swap(a._source, b._source); + std::swap(a._index, b._index); + } +}; + +} // namespace bit + +#endif // _BIT_WORD_REFERENCE_ADAPTER_HPP_ diff --git a/include/bitlib/bit-iterator/linear_overload.hpp b/include/bitlib/bit-iterator/linear_overload.hpp deleted file mode 100644 index f912708e..00000000 --- a/include/bitlib/bit-iterator/linear_overload.hpp +++ /dev/null @@ -1,242 +0,0 @@ -// ============================ LINEAR OVERLOAD ============================= // -// Project: The C++ Bit Library -// Name: linear_overload.hpp -// Description: Utilities to invoke the first valid call of an overload set -// Creator: Vincent Reverdy -// Contributor(s): Vincent Reverdy [2015-2017] -// License: BSD 3-Clause License -// ========================================================================== // -#ifndef _LINEAR_OVERLOAD_HPP_INCLUDED -#define _LINEAR_OVERLOAD_HPP_INCLUDED -// ========================================================================== // - - - -// ================================ PREAMBLE ================================ // -// C++ standard library -#include -#include -#include -// Project sources -// Third-party libraries -// Miscellaneous -namespace bit { -// ========================================================================== // - - - -/* **************************** LINEAR OVERLOAD ***************************** */ -// Linear overload class definition -template -class linear_overload -{ - // Types - public: - using tuple = std::tuple; - - // Lifecycle - public: - template - explicit constexpr linear_overload(G&&... g); - - // Access - public: - template - decltype(auto) get() noexcept; - template - constexpr decltype(auto) get() const noexcept; - template - decltype(auto) get() noexcept; - template - constexpr decltype(auto) get() const noexcept; - - // Capacity - public: - static constexpr bool empty() noexcept; - static constexpr std::size_t size() noexcept; - static constexpr std::size_t max_size() noexcept; - - // Call - public: - template < - std::size_t N = 0, - class... Args, - class = typename std::enable_if= size()>::type - > - void operator()(Args&&...); - template < - std::size_t N = 0, - class = typename std::enable_if::type, - class = decltype(std::get(std::declval())()) - > - decltype(auto) operator()(); - template < - std::size_t N = 0, - class Arg, - class... Args, - class = typename std::enable_if::type, - class = decltype(std::get(std::declval())( - std::declval(), - std::declval()... - )) - > - decltype(auto) operator()(Arg&& arg, Args&&... args); - template < - std::size_t N = 0, - class... Args, - class = typename std::enable_if::type - > - decltype(auto) operator()(Args&&... args); - - // Implementation details: data members - private: - tuple _f; - - // Maker - public: - template - friend constexpr linear_overload overload_linearly(G&&... g); -}; -/* ************************************************************************** */ - - - -// ----------------------- LINEAR OVERLOAD: LIFECYCLE ----------------------- // -// Explicitly constructs a linear overload from a list of functions -template -template -constexpr linear_overload::linear_overload( - G&&... g -) -: _f{std::forward(g)...} -{ -} -// -------------------------------------------------------------------------- // - - - -// ------------------------ LINEAR OVERLOAD: ACCESS ------------------------- // -// Gets the i-th function of the linear overload -template -template -decltype(auto) linear_overload::get( -) noexcept -{ - return std::get(_f); -} - -// Gets the i-th function of the immutable linear overload -template -template -constexpr decltype(auto) linear_overload::get( -) const noexcept -{ - return std::get(_f); -} - -// Gets the function of the given type from the linear overload -template -template -decltype(auto) linear_overload::get() noexcept -{ - return std::get(_f); -} - -// Gets the function of the given type from the immutable linear overload -template -template -constexpr decltype(auto) linear_overload::get( -) const noexcept -{ - return std::get(_f); -} -// -------------------------------------------------------------------------- // - - - -// ----------------------- LINEAR OVERLOAD: CAPACITY ------------------------ // -// Checks whether the linear overload is empty -template -constexpr bool linear_overload::empty( -) noexcept -{ - return std::tuple_size::value == 0; -} - -// Returns the number of functions in the linear overload -template -constexpr std::size_t linear_overload::size( -) noexcept -{ - return std::tuple_size::value; -} - -// Returns the maximum possible number of functions in the linear overload -template -constexpr std::size_t linear_overload::max_size( -) noexcept -{ - return std::tuple_size::value; -} -// -------------------------------------------------------------------------- // - - - -// ------------------------- LINEAR OVERLOAD: CALL -------------------------- // -// Calls the linear overload with the provided arguments: no valid overload -template -template -void linear_overload::operator()( - Args&&... -) -{ -} - -// Calls the linear overload with the provided arguments: no argument -template -template -decltype(auto) linear_overload::operator()( -) -{ - return std::get(_f)(); -} - -// Calls the linear overload with the provided arguments: valid call -template -template -decltype(auto) linear_overload::operator()( - Arg&& arg, - Args&&... args -) -{ - return std::get(_f)(std::forward(arg), std::forward(args)...); -} - -// Calls the linear overload with the provided arguments: invalid call -template -template -decltype(auto) linear_overload::operator()( - Args&&... args -) -{ - return operator()(std::forward(args)...); -} -// -------------------------------------------------------------------------- // - - - -// ------------------------- LINEAR OVERLOAD: MAKER ------------------------- // -// Builds a linear overload from a list of functions -template -constexpr linear_overload overload_linearly(G&&... g) -{ - return linear_overload(std::forward(g)...); -} -// -------------------------------------------------------------------------- // - - - -// ========================================================================== // -} // namespace bit -#endif // _LINEAR_OVERLOAD_HPP_INCLUDED -// ========================================================================== // diff --git a/include/bitlib/bit_concepts.hpp b/include/bitlib/bit_concepts.hpp new file mode 100644 index 00000000..027a8a41 --- /dev/null +++ b/include/bitlib/bit_concepts.hpp @@ -0,0 +1,103 @@ +#ifndef _BIT_CONCEPTS_HPP_INCLUDED +#define _BIT_CONCEPTS_HPP_INCLUDED + +#include +#include +#include +#include + +#if (defined(__GLIBCXX__) && (_GLIBCXX_RELEASE < 14)) +namespace std { +struct from_range_t { + explicit from_range_t() = default; +}; +inline constexpr from_range_t from_range{}; +} // namespace std +#endif + +namespace bit { + +class bit_value; + +template +concept reference_like = + std::convertible_to && // Can be converted to the value type + std::assignable_from; // Supports assignment from value type + +static_assert(reference_like, "pointer_like does not match native types"); + +template +concept pointer_like = reference_like && requires(pointer_type proxy, reference_type ref) { + // Must be dereferenceable + { *proxy } -> std::same_as; + + // Must be assignable from reference_type* + { proxy = &ref } -> std::same_as; + + // Should support comparison with other proxies + { proxy == proxy } -> std::convertible_to; + { proxy != proxy } -> std::convertible_to; + + // Should support swapping + { std::swap(proxy, proxy) }; +}; + +static_assert(pointer_like, "pointer_like does not match native types"); + +template +concept bit_reference_c = reference_like; + +template +concept bit_pointer_c = pointer_like; + +template +concept bit_iterator_c = + std::random_access_iterator && + std::same_as::value_type, bit_value> && + bit_reference_c::reference> && + bit_pointer_c::pointer, typename std::iterator_traits::reference> && + requires(It it) { + // Must have a nested size_type, iterator_type. + typename It::size_type; + typename It::iterator_type; + typename It::word_type; + typename It::difference_type; + requires std::integral; + requires std::random_access_iterator; + + // It must provide base(), position(), and mask() with proper return types. + { it.base() } -> std::same_as; + { it.position() } -> std::same_as; + }; + +template +concept bit_contiguous_iterator_c = bit_iterator_c && + std::contiguous_iterator && + std::has_unique_object_representations_v::value_type>; + + +template +concept bit_range = + std::ranges::range && + std::convertible_to, bit_value>; + +template +concept bit_sized_range = + bit_range && + std::ranges::sized_range; + +#ifdef CONTIGUOUS_RANGE +template +concept bit_contiguous_range = bit_range && + std::contiguous_iterator::iterator_type> && + std::has_unique_object_representations_v::iterator_type>::value_type>; + +template +concept bit_contiguous_sized_range = + bit_contiguous_range && + std::ranges::sized_range; +#endif + +} // namespace bit + +#endif diff --git a/profile/CMakeLists.txt b/profile/CMakeLists.txt deleted file mode 100644 index 2efc67f3..00000000 --- a/profile/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -# set output directory of builds -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) - -# set build type -set(CMAKE_BUILD_TYPE RelWithDebInfo) - -# Add targets -file(GLOB PROFILE_SOURCES "src/*.cpp") -add_executable(bitlib-profile ${PROFILE_SOURCES}) - -# specify benchmark-specific libraries -include_directories(src/utils) - -target_compile_options(bitlib-profile PUBLIC -O2 -ggdb -Wpedantic) -install(TARGETS bitlib-profile DESTINATION .) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e84ae521..aad7387e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -3,37 +3,82 @@ # Testing # ########### -find_package(GTest REQUIRED) +include(FetchContent) include(GoogleTest) -file(GLOB TEST_SOURCES "src/*.cpp") -add_executable(bitlib-tests ${TEST_SOURCES}) +if (NOT TARGET GTest::gtest) + find_package(GTest) + if (NOT GTest_DIR OR "${GTest_DIR}" STREQUAL "GTest_DIR-NOTFOUND") + FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG 6910c9d9165801d8827d628cb72eb7ea9dd538c5 # v1.16.x + ) + # For Windows: Prevent overriding the parent project's compiler/linker settings + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + FetchContent_MakeAvailable(googletest) + endif() +endif() + +add_executable(bitlib-tests) + +set(TEST_SOURCES + "${CMAKE_CURRENT_SOURCE_DIR}/src/fixtures.hpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/test-addition.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/test-array.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/test-bitwise.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/test-copy_backward.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/test-copy.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/test-count.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/test-count_leading.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/test-division.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/test-equal.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/test-fill.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/test-find.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/test-iterator_adapter.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/test-literal.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/test-move.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/test-multiplication.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/test-reverse.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/test-rotate.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/test-shift.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/test-span.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/test-swap_ranges.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/test-to_from_string.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/test-transform.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/test-usecase.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/test-array_ref.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/vector_test.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/inc/test_utils.hpp" +) -target_compile_options(bitlib-tests PUBLIC -O0 -g -fno-omit-frame-pointer -Wpedantic -fno-inline) +target_sources(bitlib-tests PRIVATE ${TEST_SOURCES}) -if (BITLIB_COVERAGE) - target_compile_options(bitlib-tests PUBLIC --coverage -fprofile-arcs -ftest-coverage -pg) +if (BITLIB_MDSPAN) + target_sources(bitlib-tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/test-mdspan.cpp) endif() -# ASAN throws no matter what when using Google HWY, not sure why but need to fix -if (NOT BITLIB_HWY) - target_compile_options(bitlib-tests PUBLIC -fsanitize=address) - target_link_options(bitlib-tests PUBLIC -fsanitize=address) +target_include_directories(bitlib-tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/inc) + +if (BITLIB_TEST_WERROR) + if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|AppleClang") + target_compile_options(bitlib-tests PRIVATE -Werror) + elseif (MSVC) + target_compile_options(bitlib-tests PRIVATE /WX) + endif() endif() # specify test-specific libraries -include_directories(${googletest_SOURCE_DIR}/googletest/include/gtest src/utils) -target_link_libraries(bitlib-tests PUBLIC GTest::gtest GTest::gtest_main -pthread -lgcov --coverage) +target_link_libraries(bitlib-tests PRIVATE bitlib GTest::gtest GTest::gtest_main) if (NOT BITLIB_GTEST_REPEAT) - set(BITLIB_GTEST_REPEAT 1) + set(BITLIB_GTEST_REPEAT 1) endif() -enable_testing() -gtest_discover_tests( +if (CMAKE_CROSSCOMPILING) + gtest_add_tests("./$" ${TEST_SOURCES}) +else() + gtest_discover_tests( bitlib-tests EXTRA_ARGS --gtest_repeat=${BITLIB_GTEST_REPEAT}) - -#add_test(bitlib-check - #COMMAND env CTEST_OUTPUT_ON_FAILURE=1 GTEST_COLOR=1 ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/bitlib-tests - #DEPENDS bitlib-tests) +endif() diff --git a/utils/test_utils.hpp b/test/inc/test_utils.hpp similarity index 55% rename from utils/test_utils.hpp rename to test/inc/test_utils.hpp index 3155fd82..46f1effe 100644 --- a/utils/test_utils.hpp +++ b/test/inc/test_utils.hpp @@ -3,7 +3,7 @@ // Name: test_utils.hpp // Description: General utilities for testing // Creator: Bryce Kille -// Contributor(s): Bryce Kille [2019], +// Contributor(s): Bryce Kille [2019], // Collin Gress [2019] // License: BSD 3-Clause License // ========================================================================== // @@ -14,12 +14,14 @@ // ================================ PREAMBLE ================================ // // C++ standard library +#include + +#include #include +#include #include #include #include -#include -#include // Project sources #include "bitlib/bitlib.hpp" // Third-party libraries @@ -39,10 +41,10 @@ constexpr auto comparator = [](auto b1, auto b2){ template struct rebind_container; // Helper struct for rebind_container -template class Container, - class NewType, +template class Container, + class NewType, class... Parameters > struct rebind_container, NewType> @@ -56,7 +58,7 @@ template auto bitcont_to_boolcont(const Container bitcont){ auto bfirst = bit::bit_iterator(std::begin(bitcont)); auto blast = bit::bit_iterator(std::end(bitcont)); - typename rebind_container::type c(std::distance(bfirst, + typename rebind_container::type c(std::distance(bfirst, blast) ); auto benchmark_it = std::begin(c); @@ -67,57 +69,82 @@ auto bitcont_to_boolcont(const Container bitcont){ return c; } -// Produces container of random numbers from min to max -template -Container make_random_container( - std::size_t size, - T min = std::numeric_limits::min(), - T max = std::numeric_limits::max(), - const T& seed = T() -) -{ - Container c(size); - std::random_device device; - std::mt19937 engine(seed == T() ? device() : seed); - std::uniform_int_distribution distribution(min, max); - auto it = std::begin(c); - for (std::size_t i = 0; i < size; ++i) { - *it = distribution(engine); - ++it; - } - return c; -} +inline std::mt19937 GetSeededRNGFromTestName() { + auto test_info = ::testing::UnitTest::GetInstance()->current_test_info(); + if (test_info != nullptr) { + std::string full_test_name = std::string(test_info->test_suite_name()) + "." + test_info->name(); + // Hash the name to get a seed + std::size_t hash_value = std::hash{}(full_test_name); -inline unsigned long long generate_random_number(size_t min, size_t max) { - // First create an instance of an engine. + // Use the hash as a seed + return std::mt19937(static_cast(hash_value)); + } else { std::random_device rnd_device; // Specify the engine and distribution. - std::mt19937 mersenne_engine {rnd_device()}; // Generates random integers - std::uniform_int_distribution dist {min, max}; - - return dist(mersenne_engine); + return std::mt19937(rnd_device()); + } } +inline unsigned long long generate_random_number(size_t min, size_t max) { + std::mt19937 mersenne_engine = GetSeededRNGFromTestName(); + std::uniform_int_distribution dist{min, max}; + + return dist(mersenne_engine); +} + +// clang-format off +template + requires std::integral +struct uniform_dist_type { + static constexpr bool is_signed = std::is_signed_v; + static constexpr std::size_t size = sizeof(T); + + using type = std::conditional_t, + std::conditional_t, + std::conditional_t, + void // fallback (shouldn't happen for standard integral types) + >>>; +}; +// clang-format on + +template +using uniform_dist_type_t = typename uniform_dist_type::type; + +template +std::array get_random_arr( + WordType min = std::numeric_limits::min(), + WordType max = std::numeric_limits::max()) { + // Specify the engine and distribution. + std::mt19937 mersenne_engine = GetSeededRNGFromTestName(); + std::uniform_int_distribution> dist{min, max}; + + auto gen = [&dist, &mersenne_engine]() { + return static_cast(dist(mersenne_engine)); + }; + std::array arr{}; + generate(begin(arr), end(arr), gen); + return arr; +} template std::vector get_random_vec( unsigned long long int size, - WordType min = std::numeric_limits::min(), + WordType min = std::numeric_limits::min(), WordType max = std::numeric_limits::max() ) { - // First create an instance of an engine. - std::random_device rnd_device; - // Specify the engine and distribution. - std::mt19937 mersenne_engine {rnd_device()}; // Generates random integers - std::uniform_int_distribution dist {min, max}; - - auto gen = [&dist, &mersenne_engine](){ - return dist(mersenne_engine); - }; - std::vector vec(size); - generate(begin(vec), end(vec), gen); - return vec; + std::mt19937 mersenne_engine = GetSeededRNGFromTestName(); + std::uniform_int_distribution> dist{min, max}; + + auto gen = [&dist, &mersenne_engine]() { + return static_cast(dist(mersenne_engine)); + }; + std::vector vec(size); + generate(begin(vec), end(vec), gen); + return vec; } template @@ -136,7 +163,7 @@ template std::vector boolvec_from_bitvec(bit::bit_vector bv) { std::vector ret_vec{}; for (bit::bit_value value : bv) { - ret_vec.push_back(value == bit::bit1 ? true : false); + ret_vec.push_back(value == bit::bit1 ? true : false); } return ret_vec; } diff --git a/test/src/fixtures.hpp b/test/src/fixtures.hpp index b8cd5942..b2d6c36b 100644 --- a/test/src/fixtures.hpp +++ b/test/src/fixtures.hpp @@ -1,14 +1,13 @@ // =============================== FIXTURES ================================= // // Project: The Experimental Bit Algorithms Library -// Description: Fixtures for testing -// Contributor(s): Bryce Kille +// Description: Fixtures for testing +// Contributor(s): Bryce Kille // License: BSD 3-Clause License // ========================================================================== // #ifndef _FIXTURES_HPP_INCLUDED #define _FIXTURES_HPP_INCLUDED // ========================================================================== // - // ============================== PREAMBLE ================================== // // C++ standard library #include @@ -20,115 +19,112 @@ #include #include // Project sources -#include "bitlib/bit-iterator/bit_iterator.hpp" #include "bitlib/bit-containers/bit-containers.hpp" +#include "bitlib/bit-containers/bit_bitsof.hpp" +#include "bitlib/bit-iterator/bit_iterator.hpp" #include "test_utils.hpp" // Third-party libraries #include "gtest/gtest.h" // Miscellaneous // ========================================================================== // - //TODO tests need a lot of cleanup. We should only copy what we need from random_vec //and also refactor the vec generation to reduce duplication -using BaseTypes = ::testing::Types; - +using BaseTypes = ::testing::Types; -template +template class VectorTest : public testing::Test { - protected: + protected: + using base_type = WordType; + using vec_type = bit::bit_vector; + vec_type empty_vec; + std::vector empty_vec_bool; + vec_type v2_ = vec_type(18); + vec_type v3_ = vec_type("010111111"); - using base_type = WordType; - using vec_type = bit::bit_vector; - vec_type empty_vec; - std::vector empty_vec_bool; - vec_type v2_ = vec_type(18); - vec_type v3_ = vec_type("010111111"); - - std::vector random_bitvecs; - std::vector> random_boolvecs; - std::vector random_vec; - const size_t word_size = 4; + std::vector random_bitvecs; + std::vector> random_boolvecs; + std::vector random_vec; + void SetUp() override { const size_t digits = bit::binary_digits::value; - const size_t bit_size = word_size*digits; - - void SetUp() override { - empty_vec = vec_type(); - random_vec = get_random_vec(word_size); - for (size_t cont_size = 1; cont_size < bit_size; ++cont_size) { - auto bitvec = vec_type(bit_size); - std::memcpy(&(*bitvec.begin().base()), &(random_vec[0]), word_size); - bitvec.resize(cont_size); + const size_t word_size = 4; + const size_t bit_size = word_size * digits; + empty_vec = vec_type(); + random_vec = get_random_vec(word_size); + for (size_t cont_size = 1; cont_size < bit_size; ++cont_size) { + auto bitvec = vec_type(bit_size); + std::memcpy(&(*bitvec.begin().base()), &(random_vec[0]), word_size); + bitvec.resize(cont_size); - auto boolvec = boolvec_from_bitvec(bitvec); - random_bitvecs.push_back(bitvec); - random_boolvecs.push_back(boolvec); - } - size_t big_size = 64*64*10; - for (int i = -4; i < 4; ++i) { - size_t cont_size = big_size + i; - auto bitvec = vec_type(bit_size); - std::memcpy(&(*bitvec.begin().base()), &(random_vec[0]), word_size); - bitvec.resize(cont_size); + auto boolvec = boolvec_from_bitvec(bitvec); + random_bitvecs.push_back(bitvec); + random_boolvecs.push_back(boolvec); + } + size_t big_size = 64 * 64 * 10; + for (int i = -4; i < 4; ++i) { + size_t cont_size = big_size + i; + auto bitvec = vec_type(bit_size); + std::memcpy(&(*bitvec.begin().base()), &(random_vec[0]), word_size); + bitvec.resize(cont_size); - auto boolvec = boolvec_from_bitvec(bitvec); - random_bitvecs.push_back(bitvec); - random_boolvecs.push_back(boolvec); - } + auto boolvec = boolvec_from_bitvec(bitvec); + random_bitvecs.push_back(bitvec); + random_boolvecs.push_back(boolvec); } + } }; TYPED_TEST_SUITE(VectorTest, BaseTypes); template class SingleRangeTest : public testing::Test { - protected: + protected: + using base_type = WordType; - using base_type = WordType; + std::vector> random_bitvecs; + std::vector> random_boolvecs; + std::vector random_vec; - std::vector> random_bitvecs; - std::vector> random_boolvecs; - std::vector random_vec; + protected: + void SetUp() override { + const size_t digits = bit::binary_digits::value; size_t word_size = 4; - size_t digits = bit::binary_digits::value; - size_t bit_size = word_size*digits; + size_t bit_size = word_size * digits; + random_vec = get_random_vec(word_size); + for (size_t cont_size = 1; cont_size < bit_size; ++cont_size) { + auto bitvec = bit::bit_vector(bit_size); + std::copy(random_vec.begin(), random_vec.end(), bitvec.begin().base()); + bitvec.resize(cont_size); - void SetUp() override { - random_vec = get_random_vec(word_size); - for (size_t cont_size = 1; cont_size < bit_size; ++cont_size) { - auto bitvec = bit::bit_vector(bit_size); - std::copy(random_vec.begin(), random_vec.end(), bitvec.begin().base()); - bitvec.resize(cont_size); + auto boolvec = boolvec_from_bitvec(bitvec); + random_bitvecs.push_back(bitvec); + random_boolvecs.push_back(boolvec); + } + word_size = 2 * 64 * 64; + bit_size = word_size * digits; + random_vec = get_random_vec(word_size); + for (size_t cont_size = bit_size - digits - 4; cont_size < bit_size - digits + 4; ++cont_size) { + auto bitvec = bit::bit_vector(bit_size); + std::copy(random_vec.begin(), random_vec.end(), bitvec.begin().base()); + bitvec.resize(cont_size); - auto boolvec = boolvec_from_bitvec(bitvec); - random_bitvecs.push_back(bitvec); - random_boolvecs.push_back(boolvec); - } - word_size = 2*64*64; - random_vec = get_random_vec(word_size); - size_t bit_size = (word_size)*digits; - for (size_t cont_size = bit_size - digits - 4; cont_size < bit_size - digits + 4; ++cont_size) { - auto bitvec = bit::bit_vector(bit_size); - std::copy(random_vec.begin(), random_vec.end(), bitvec.begin().base()); - bitvec.resize(cont_size); + auto boolvec = boolvec_from_bitvec(bitvec); + random_bitvecs.push_back(bitvec); + random_boolvecs.push_back(boolvec); + } - auto boolvec = boolvec_from_bitvec(bitvec); - random_bitvecs.push_back(bitvec); - random_boolvecs.push_back(boolvec); - } + auto zeros = bit::bit_vector(bit_size); + std::fill(zeros.begin(), zeros.end(), bit::bit0); + *(zeros.end() - 1024 - digits - 4) = bit::bit1; + random_bitvecs.push_back(zeros); + random_boolvecs.push_back(boolvec_from_bitvec(zeros)); - auto zeros = bit::bit_vector(bit_size); - std::fill(zeros.begin(), zeros.end(), bit::bit0); - *(zeros.end() - 1024 - digits - 4) = bit::bit1; - random_bitvecs.push_back(zeros); - random_boolvecs.push_back(boolvec_from_bitvec(zeros)); - - auto ones = bit::bit_vector(bit_size); - std::fill(ones.begin(), ones.end(), bit::bit1); - *(ones.end() - 1024 - digits - 4) = bit::bit0; - random_bitvecs.push_back(ones); - random_boolvecs.push_back(boolvec_from_bitvec(ones)); - } + auto ones = bit::bit_vector(bit_size); + std::fill(ones.begin(), ones.end(), bit::bit1); + *(ones.end() - 1024 - digits - 4) = bit::bit0; + random_bitvecs.push_back(ones); + random_boolvecs.push_back(boolvec_from_bitvec(ones)); + } }; TYPED_TEST_SUITE(SingleRangeTest, BaseTypes); @@ -144,24 +140,25 @@ class DoubleRangeTest : public testing::Test { std::vector> random_boolvecs2; std::vector random_vec; std::vector random_vec_big; - size_t digits = bit::binary_digits::value; - size_t word_size = 4; - size_t bit_size = word_size*bit::binary_digits::value; - size_t big_size = 64*64*2; void SetUp() override { - // TODO this is ugly, need to refactor - random_vec = get_random_vec(word_size); - random_vec_big = get_random_vec(big_size); - for (size_t cont_size = 1; cont_size < bit_size; ++cont_size) { - auto bitvec = bit::bit_vector(bit_size); - std::copy(random_vec.begin(), random_vec.end(), bitvec.begin().base()); - bitvec.resize(cont_size); + const size_t word_size = 4; + const size_t big_size = 64 * 64 * 2; + const size_t digits = bit::binary_digits::value; + const size_t bit_size = word_size * digits; - auto boolvec = boolvec_from_bitvec(bitvec); - random_bitvecs1.push_back(bitvec); - random_boolvecs1.push_back(boolvec); - } + // TODO this is ugly, need to refactor + random_vec = get_random_vec(word_size); + random_vec_big = get_random_vec(big_size); + for (size_t cont_size = 1; cont_size < bit_size; ++cont_size) { + auto bitvec = bit::bit_vector(bit_size); + std::copy(random_vec.begin(), random_vec.end(), bitvec.begin().base()); + bitvec.resize(cont_size); + + auto boolvec = boolvec_from_bitvec(bitvec); + random_bitvecs1.push_back(bitvec); + random_boolvecs1.push_back(boolvec); + } for (int i = -4; i < 4; ++i) { size_t cont_size = (big_size-1)*digits + i; auto bitvec = bit::bit_vector(big_size*digits); @@ -196,6 +193,102 @@ class DoubleRangeTest : public testing::Test { } }; TYPED_TEST_SUITE(DoubleRangeTest, BaseTypes); +template +class MixedDoubleRangeTest : public testing::Test { + protected: + using FromWordType = typename FromToPair::first_type; + using ToWordType = typename FromToPair::second_type; + + static constexpr size_t word_size = 4; + static constexpr size_t big_size = 64 * 64 * 2; + + // test data + std::vector> random_bitvecs1; + std::vector> random_bitvecs2; + std::vector> random_boolvecs1, random_boolvecs2; + + void SetUp() override { + // templated lambda to fill any bit-vector / bool-vector pair + auto fill = [&]( + std::vector>& bitvecs, + std::vector>& boolvecs, + std::vector const& small_words, + std::vector const& big_words) { + const size_t digits = bit::bitsof(); + const size_t num_small_words = ((word_size * std::max(sizeof(FromWordType), sizeof(ToWordType))) / sizeof(W)); + const size_t num_big_words = ((big_size * std::max(sizeof(FromWordType), sizeof(ToWordType))) / sizeof(W)); + const size_t small_bit_size = num_small_words * digits; + const size_t big_bit_size = num_big_words * digits; + assert(small_words.size() == num_small_words); + assert(big_words.size() == num_big_words); + // small-size runs + for (size_t cont_size = 1; cont_size < small_bit_size; ++cont_size) { + bit::bit_vector bv(small_bit_size); + std::copy(small_words.begin(), small_words.end(), bv.begin().base()); + bv.resize(cont_size); + bitvecs.push_back(bv); + boolvecs.push_back(boolvec_from_bitvec(bv)); + } + // "big" runs around (big_size-1)*digits +/-4 + const auto max_digits = std::max(bit::bitsof(), bit::bitsof()); + for (int i = -4; i < 4; ++i) { + size_t cont_size = big_bit_size - max_digits + i; + bit::bit_vector bv(big_bit_size); + std::copy(big_words.begin(), big_words.end(), + bv.begin().base()); + bv.resize(cont_size); + bitvecs.push_back(bv); + boolvecs.push_back(boolvec_from_bitvec(bv)); + } + }; + + // — fill for FromWordType — + const auto small_words1 = (word_size * std::max(sizeof(FromWordType), sizeof(ToWordType))) / sizeof(FromWordType); + const auto big_words1 = (big_size * std::max(sizeof(FromWordType), sizeof(ToWordType))) / sizeof(FromWordType); + auto small1 = get_random_vec(small_words1); + auto big1 = get_random_vec(big_words1); + fill.template operator()( + random_bitvecs1, + random_boolvecs1, + small1, big1); + + // — fill for ToWordType — + const auto small_words2 = (word_size * std::max(sizeof(FromWordType), sizeof(ToWordType))) / sizeof(ToWordType); + const auto big_words2 = (big_size * std::max(sizeof(FromWordType), sizeof(ToWordType))) / sizeof(ToWordType); + auto small2 = get_random_vec(small_words2); + auto big2 = get_random_vec(big_words2); + fill.template operator()( + random_bitvecs2, + random_boolvecs2, + small2, big2); + + // now random_bitvecs1.size() == random_bitvecs2.size() + } +}; + +// ----------------------------------------------------------------------------- +// Instantiate for the pairs you care about +// ----------------------------------------------------------------------------- +using TypePairs = ::testing::Types< + std::pair, + std::pair, + std::pair, + std::pair, + std::pair, + std::pair, + std::pair, + std::pair, + std::pair, + std::pair, + std::pair, + std::pair, + std::pair, + std::pair, + std::pair, + std::pair>; + +TYPED_TEST_SUITE(MixedDoubleRangeTest, TypePairs); + // ========================================================================== // #endif // _FIXTURES_HPP_INCLUDED // ========================================================================== // diff --git a/test/src/test-addition.cpp b/test/src/test-addition.cpp new file mode 100644 index 00000000..8a90ca57 --- /dev/null +++ b/test/src/test-addition.cpp @@ -0,0 +1,16 @@ +#include + +#include "bitlib/bitlib.hpp" +#include "gtest/gtest.h" + +TEST(BitAddition, Basic) { + auto bits = 0xF'0F_b; + EXPECT_EQ(0, bit::addition(bits.begin(), bits.end(), static_cast(0x1u))); + EXPECT_EQ(0xF'10_b, bits); +} + +TEST(BitAddition, LowBits) { + auto bits = 0x7'F_b; + EXPECT_EQ(0, bit::addition(bits.begin(), bits.end(), 1'1_b)); + EXPECT_EQ(0x7'10_b, bits); +} diff --git a/test/src/test-array.cpp b/test/src/test-array.cpp new file mode 100644 index 00000000..fc532b28 --- /dev/null +++ b/test/src/test-array.cpp @@ -0,0 +1,494 @@ +#include +#include +#include + +#include "bitlib/bit-containers/bit_array.hpp" +#include "bitlib/bit-containers/bit_array_dynamic_extent.hpp" +#include "fixtures.hpp" + +// Third-party libraries +#include "gtest/gtest.h" + +/* + * Constructor tests + */ +// Tests the default c'tor. +TEST(ArrayTest, DefaultConstructor) { + bit::bit_array<11> barr(bit::bit0); + EXPECT_EQ(2, sizeof(barr)); + EXPECT_EQ(11u, barr.size()); + EXPECT_EQ(bit::bit0, barr[0]); + EXPECT_EQ(bit::bit0, barr[1]); + EXPECT_EQ(bit::bit0, barr[2]); + EXPECT_EQ(bit::bit0, barr[3]); + EXPECT_EQ(bit::bit0, barr[4]); + EXPECT_EQ(bit::bit0, barr[5]); + EXPECT_EQ(bit::bit0, barr[6]); + EXPECT_EQ(bit::bit0, barr[7]); + EXPECT_EQ(bit::bit0, barr[8]); + EXPECT_EQ(bit::bit0, barr[9]); + EXPECT_EQ(bit::bit0, barr[10]); +} + +TEST(ArrayTest, BitsOf) { + bit::bit_array<11> barr(bit::bit0); + EXPECT_EQ(11u, bitsof(barr)); + EXPECT_EQ(11u, bitsof(bit::bit_array<11>())); +} + +TEST(ArrayTest, BasicIteration) { + // <-- LSB, apparently 🙄 + bit::bit_array<11> barr("0110_0101_110"); + int i = 0; + for (const auto& bbit : barr) { + switch (10 - i++) { + case 0: EXPECT_EQ(bit::bit0, bbit); break; + case 1: EXPECT_EQ(bit::bit1, bbit); break; + case 2: EXPECT_EQ(bit::bit1, bbit); break; + case 3: EXPECT_EQ(bit::bit1, bbit); break; + case 4: EXPECT_EQ(bit::bit0, bbit); break; + case 5: EXPECT_EQ(bit::bit1, bbit); break; + case 6: EXPECT_EQ(bit::bit0, bbit); break; + case 7: EXPECT_EQ(bit::bit0, bbit); break; + case 8: EXPECT_EQ(bit::bit1, bbit); break; + case 9: EXPECT_EQ(bit::bit1, bbit); break; + case 10: EXPECT_EQ(bit::bit0, bbit); break; + } + } +} + +TEST(ArrayTest, ZeroSize) { + bit::bit_array<0> barr{}; + std::array foo{}; + EXPECT_EQ(sizeof(foo), sizeof(barr)); + EXPECT_EQ(0, bitsof(barr)); +} + +// Test that the default constructor initializes all bits to false. +TEST(ArrayTest, DefaultInitialization) { + bit::bit_array<8> ba; + for (size_t i = 0; i < ba.size(); ++i) { + EXPECT_FALSE(ba[i]) << "Bit " << i << " should be false by default"; + } +} + +// Test the fill() method. +TEST(ArrayTest, FillMethod) { + bit::bit_array<10> ba; + ba.fill(bit::bit_value(true)); + for (size_t i = 0; i < ba.size(); ++i) { + EXPECT_TRUE(ba[i]) << "Bit " << i << " should be true after fill(true)"; + } + ba.fill(bit::bit_value(false)); + for (size_t i = 0; i < ba.size(); ++i) { + EXPECT_FALSE(ba[i]) << "Bit " << i << " should be false after fill(false)"; + } +} + +// Test element access via operator[] and at(), including out-of-range checking. +TEST(ArrayTest, ElementAccess) { + bit::bit_array<5> ba; + ba.fill(bit::bit_value(false)); + ba[2] = bit::bit_value(true); + EXPECT_TRUE(ba.at(2)); + EXPECT_THROW(ba.at(5), std::out_of_range); +} + +// Test front() and back() member functions. +TEST(ArrayTest, FrontBackAccess) { + bit::bit_array<4> ba; + ba.fill(bit::bit_value(false)); + ba.front() = bit::bit_value(true); + ba.back() = bit::bit_value(true); + EXPECT_TRUE(ba.front()); + EXPECT_TRUE(ba.back()); +} + +// Test iterator functionality (both non-const and range-based). +TEST(ArrayTest, IteratorFunctionality) { + bit::bit_array<4> ba; + ba.fill(bit::bit_value(false)); + int index = 0; + for (auto it = ba.begin(); it != ba.end(); ++it) { + // Change the second element using the iterator. + if (index == 1) { + *it = bit::bit_value(true); + } + ++index; + } + EXPECT_TRUE(ba[1]); +} + +// Test const_iterator functionality. +TEST(ArrayTest, ConstIteratorFunctionality) { + bit::bit_array<4> ba; + ba.fill(bit::bit_value(true)); + const bit::bit_array<4>& const_ba = ba; + for (auto it = const_ba.begin(); it != const_ba.end(); ++it) { + EXPECT_TRUE(*it); + } +} + +// Test the swap() member function. +TEST(ArrayTest, SwapFunctionality) { + bit::bit_array<4> ba1, ba2; + ba1.fill(bit::bit_value(false)); + ba2.fill(bit::bit_value(true)); + ba1.swap(ba2); + for (size_t i = 0; i < ba1.size(); ++i) { + EXPECT_TRUE(ba1[i]) << "After swap, ba1[" << i << "] should be true"; + EXPECT_FALSE(ba2[i]) << "After swap, ba2[" << i << "] should be false"; + } +} + +// Test comparison operators (== and !=). +TEST(ArrayTest, ComparisonOperators) { + bit::bit_array<5> ba1 = {bit::bit1, bit::bit1, bit::bit0, bit::bit0, bit::bit1}; + bit::bit_array<5> ba2 = {bit::bit1, bit::bit1, bit::bit0, bit::bit0, bit::bit1}; + EXPECT_EQ(ba1, ba2); + ba2[2] = bit::bit_value(true); // Change one element + EXPECT_NE(ba1, ba2); + bit::bit_array ba3{bit::bit1, bit::bit1, bit::bit1, bit::bit0, bit::bit0, bit::bit1}; + EXPECT_NE(ba1, ba3); +} + +// Test the data() method to access the underlying storage. +TEST(ArrayTest, DataAccess) { + bit::bit_array<8> ba; + ba.fill(bit::bit_value(false)); + // Assume data() returns a pointer to a boolean array. + uint8_t* data_ptr = ba.data(); + EXPECT_FALSE(data_ptr[0]); + ba[0] = bit::bit_value(true); + EXPECT_TRUE(data_ptr[0]); +} + +// Test size() and empty() functions. +TEST(ArrayTest, SizeAndEmpty) { + bit::bit_array<0> ba_empty; + EXPECT_EQ(ba_empty.size(), 0); + EXPECT_TRUE(ba_empty.empty()); + + bit::bit_array<5> ba; + EXPECT_EQ(ba.size(), 5); + EXPECT_FALSE(ba.empty()); +} + +// Test initializer list construction. +TEST(ArrayTest, InitializerListConstruction) { + bit::bit_array<3> ba = {bit::bit1, bit::bit0, bit::bit1}; + EXPECT_TRUE(ba[0]); + EXPECT_FALSE(ba[1]); + EXPECT_TRUE(ba[2]); +} + +// Test copy constructor and copy assignment operator. +TEST(ArrayTest, CopyAndAssignment) { + bit::bit_array<5> ba1 = {bit::bit1, bit::bit0, bit::bit1, bit::bit0, bit::bit1}; + bit::bit_array<5> ba_copy(ba1); + EXPECT_EQ(ba1, ba_copy); + + bit::bit_array<5> ba_assigned; + ba_assigned = ba1; + EXPECT_EQ(ba1, ba_assigned); +} + +// Test move semantics (move constructor and move assignment), if implemented. +TEST(ArrayTest, MoveSemantics) { + bit::bit_array<5> ba1 = {bit::bit1, bit::bit0, bit::bit1, bit::bit0, bit::bit1}; + bit::bit_array<5> ba_moved(std::move(ba1)); + // We test the moved-to container's values. The moved-from object is valid but unspecified. + EXPECT_TRUE(ba_moved[0]); + EXPECT_FALSE(ba_moved[1]); + EXPECT_TRUE(ba_moved[2]); + EXPECT_FALSE(ba_moved[3]); + EXPECT_TRUE(ba_moved[4]); + + bit::bit_array<5> ba2 = {bit::bit0, bit::bit0, bit::bit0, bit::bit0, bit::bit0}; + ba2 = std::move(ba_moved); + EXPECT_TRUE(ba2[0]); + EXPECT_FALSE(ba2[1]); + EXPECT_TRUE(ba2[2]); + EXPECT_FALSE(ba2[3]); + EXPECT_TRUE(ba2[4]); +} + +TEST(ArrayTest, Throws) { + bit::bit_array<5> ba1{bit::bit1, bit::bit0, bit::bit1, bit::bit0, bit::bit1}; + EXPECT_THROW(ba1.at(5), std::out_of_range); + bit::bit_array ba2{bit::bit1, bit::bit0, bit::bit1, bit::bit0, bit::bit1}; + bit::bit_array<5> ba3(ba2); + EXPECT_EQ(ba1, ba3); + EXPECT_EQ(ba1, ba2); + bit::bit_array ba4{bit::bit1, bit::bit1, bit::bit0, bit::bit1, bit::bit0, bit::bit1}; + + using barr5 = bit::bit_array<5>; // command in template messes up gtest macro + EXPECT_THROW(barr5{ba4}, std::invalid_argument) << "Copy constructor must take the same size"; + EXPECT_THROW(barr5{bit::bit0}, std::invalid_argument) << "Initializer list must be the correct size"; + + using namespace std::literals; + EXPECT_THROW(barr5{"010101"sv}, std::invalid_argument) << "String view constructor must be the correct size"; +} + +TEST(BitArrayDynamicTest, Throws) { + bit::bit_array<> ba1(5); + ba1 = {bit::bit1, bit::bit0, bit::bit1, bit::bit0, bit::bit1}; + EXPECT_THROW(ba1.at(5), std::out_of_range); + bit::bit_array<> ba2(6); + ba2 = {bit::bit0, bit::bit1, bit::bit0, bit::bit1, bit::bit0, bit::bit1}; + EXPECT_THROW(ba1 = ba2, std::invalid_argument); + EXPECT_THROW(ba1 = std::move(ba2), std::invalid_argument); + EXPECT_NO_THROW(ba1 = ba2(0, 5)); +} + +// +// Test Suite for bit::bit_array<> +// + +// As array<> is dynamic, bitsof will not return the number stored bits +// the class contains a size_t and a pointer +TEST(BitArrayDynamicTest, Bitsof) { + bit::bit_array<> arr(23, bit::bit1); + EXPECT_EQ(bitsof(arr), 8 * sizeof(arr)); + EXPECT_EQ(arr.size(), 23); +} + +TEST(BitArrayDynamicTest, SizeConstructorCreatesArrayOfGivenSize) { + const std::size_t size = 10; + bit::bit_array<> arr(size); + EXPECT_EQ(arr.size(), size); + EXPECT_FALSE(arr.empty()); +} + +TEST(BitArrayDynamicTest, SizeAndValueConstructorInitializesCorrectly) { + const std::size_t size = 16; + // Initialize all bits to true. + bit::bit_array<> arr(size, bit::bit_value(true)); + for (std::size_t i = 0; i < size; ++i) { + EXPECT_EQ(arr[i], bit::bit_value(true)); + } +} + +TEST(BitArrayDynamicTest, CopyConstructorCopiesContent) { + const std::size_t size = 8; + bit::bit_array<> original(size, bit::bit_value(true)); + bit::bit_array<> copy(original); + EXPECT_TRUE(copy == original); +} + +// Test copy constructor and copy assignment operator. +TEST(BitArrayDynamicTest, CopyAndAssignment) { + bit::bit_array ba1{bit::bit1, bit::bit0, bit::bit1, bit::bit0, bit::bit1}; + bit::bit_array ba_copy(ba1); + EXPECT_EQ(ba1, ba_copy); + + bit::bit_array ba2{bit::bit1, bit::bit1, bit::bit0, bit::bit1, bit::bit0, bit::bit1}; + EXPECT_THROW(ba1 = ba2, std::invalid_argument) << "Copy assignment from invalid size should throw"; + EXPECT_THROW(ba1 = ba2(0, 6), std::invalid_argument) << "Assign from bit_sized_range (array_ref) of unequal size should throw"; +} + +TEST(BitArrayDynamicTest, MoveConstructorMovesContent) { + const std::size_t size = 8; + bit::bit_array<> original(size, bit::bit_value(true)); + bit::bit_array<> moved(std::move(original)); + // Check that moved now contains the expected content. + EXPECT_EQ(moved.size(), size); + for (std::size_t i = 0; i < size; ++i) { + EXPECT_EQ(moved[i], bit::bit_value(true)); + } + // The state of original is valid but unspecified. +} + +TEST(BitArrayDynamicTest, InitializerListBitValueConstructorWorks) { + std::initializer_list init = {bit::bit0, bit::bit1, bit::bit0}; + bit::bit_array<> arr(init); + EXPECT_EQ(arr.size(), init.size()); + auto it = arr.begin(); + for (bit::bit_value expected : init) { + EXPECT_EQ(*it, expected); + ++it; + } +} + +TEST(BitArrayDynamicTest, InitializerListWordTypeConstructorWorks) { + // For this test, we assume that the initializer list for WordType initializes the underlying storage. + // Here we use two bytes as an example. + std::initializer_list init = {0b10101010, 0b01010101}; + bit::bit_array arr(init); + // Assuming each std::uint8_t provides 8 bits, we expect the size to be the number of initializer elements * 8. + EXPECT_EQ(arr.size(), init.size() * 8u); + // Check that the underlying storage matches the initializer values. + const std::uint8_t* data = reinterpret_cast(arr.data()); + auto wordIt = init.begin(); + for (std::size_t i = 0; i < init.size(); ++i) { + EXPECT_EQ(data[i], *(wordIt++)); + } +} + +TEST(BitArrayDynamicTest, StringViewConstructorWorks) { + // Assuming the string_view constructor interprets a string of '0' and '1' characters. + std::string_view s = "1011001"; + bit::bit_array<> arr(s); + EXPECT_EQ(arr.size(), s.size()); + for (std::size_t i = 0; i < s.size(); ++i) { + // Interpret '1' as true and '0' as false. + bool expected = (s[i] == '1'); + EXPECT_EQ(arr[i], bit::bit_value(expected)); + } +} + +TEST(BitArrayDynamicTest, ElementAccessAtAndBracketConsistency) { + const std::size_t size = 5; + bit::bit_array<> arr(size, bit::bit_value(false)); + // Set a value using operator[] and check with at(). + arr[2] = bit::bit_value(true); + EXPECT_EQ(arr.at(2), bit::bit_value(true)); + EXPECT_EQ(arr[2], bit::bit_value(true)); + + // Test front() and back(). + arr[0] = bit::bit_value(true); + arr[size - 1] = bit::bit_value(true); + EXPECT_EQ(arr.front(), bit::bit_value(true)); + EXPECT_EQ(arr.back(), bit::bit_value(true)); +} + +TEST(BitArrayDynamicTest, IteratorTraversal) { + const std::size_t size = 10; + bit::bit_array<> arr(size, bit::bit_value(false)); + // Set alternate bits to true. + for (std::size_t i = 0; i < size; ++i) { + arr[i] = bit::bit_value(i % 2 == 0); + } + std::size_t index = 0; + for (auto it = arr.begin(); it != arr.end(); ++it, ++index) { + bool expected = (index % 2 == 0); + EXPECT_EQ(*it, bit::bit_value(expected)); + } +} + +TEST(BitArrayDynamicTest, CapacityFunctions) { + bit::bit_array<> arr{bit::bit0}; + EXPECT_FALSE(arr.empty()); + EXPECT_EQ(arr.size(), 1u); + // max_size() is expected to be >= size. + EXPECT_GE(arr.max_size(), arr.size()); +} + +TEST(BitArrayDynamicTest, FillOperationSetsAllBits) { + const std::size_t size = 20; + bit::bit_array<> arr(size, bit::bit_value(false)); + arr.fill(bit::bit_value(true)); + for (std::size_t i = 0; i < size; ++i) { + EXPECT_EQ(arr[i], bit::bit_value(true)); + } +} + +TEST(BitArrayDynamicTest, SwapOperationSwapsContents) { + const std::size_t size1 = 8; + const std::size_t size2 = 8; + bit::bit_array<> arr1(size1, bit::bit_value(true)); + bit::bit_array<> arr2(size2, bit::bit_value(false)); + + arr1.swap(arr2); + + EXPECT_EQ(arr1.size(), size2); + EXPECT_EQ(arr2.size(), size1); + + for (std::size_t i = 0; i < arr1.size(); ++i) { + EXPECT_EQ(arr1[i], bit::bit_value(false)); + } + for (std::size_t i = 0; i < arr2.size(); ++i) { + EXPECT_EQ(arr2[i], bit::bit_value(true)); + } +} + +TEST(BitArrayDynamicTest, AssignmentOperatorCopiesContent) { + const std::size_t size = 10; + bit::bit_array<> arr1(size, bit::bit_value(true)); + bit::bit_array<> arr2(size); + arr2 = arr1; + EXPECT_TRUE(arr2 == arr1); +} + +TEST(BitArrayDynamicTest, MoveAssignmentOperatorMovesContent) { + const std::size_t size = 10; + bit::bit_array<> arr1(size, bit::bit_value(true)); + bit::bit_array<> arr2(size); + arr2 = std::move(arr1); + EXPECT_EQ(arr2.size(), size); + for (std::size_t i = 0; i < size; ++i) { + EXPECT_EQ(arr2[i], bit::bit_value(true)); + } + // The state of arr1 is valid but unspecified. +} + +struct StructOfBitArrays { + bit::bit_array<> arr1; + bit::bit_array<> arr2; + bit::bit_array<> arr3; + StructOfBitArrays() = delete; + StructOfBitArrays(size_t s1, size_t s2, size_t s3) + : arr1(s1), arr2(s2), arr3(s3) { + } +}; + +TEST(BitArrayDynamicTest, StructOfBitArray) { + size_t s1 = rand(); + size_t s2 = rand(); + size_t s3 = rand(); + StructOfBitArrays s(s1, s2, s3); + EXPECT_EQ(s.arr1.size(), s1); + EXPECT_EQ(s.arr2.size(), s2); + EXPECT_EQ(s.arr3.size(), s3); +} + +TEST(BitArrayDynamicTest, StringConstructor) { + bit::bit_array<> arr("01001101"); + + EXPECT_EQ(arr.size(), 8); + + EXPECT_EQ(arr[7], bit::bit1); + EXPECT_EQ(arr[6], bit::bit0); + EXPECT_EQ(arr[5], bit::bit1); + EXPECT_EQ(arr[4], bit::bit1); + EXPECT_EQ(arr[3], bit::bit0); + EXPECT_EQ(arr[2], bit::bit0); + EXPECT_EQ(arr[1], bit::bit1); + EXPECT_EQ(arr[0], bit::bit0); +} + +// Test comparison operators (== and !=). +TEST(BitArrayDynamicTest, ComparisonOperators) { + bit::bit_array ba1{bit::bit1, bit::bit1, bit::bit0, bit::bit0, bit::bit1}; + bit::bit_array ba2{bit::bit1, bit::bit1, bit::bit0, bit::bit0, bit::bit1}; + EXPECT_EQ(ba1, ba2); + ba2[2] = bit::bit_value(true); // Change one element + EXPECT_NE(ba1, ba2); + bit::bit_array ba3{bit::bit0, bit::bit1, bit::bit1, bit::bit0, bit::bit0, bit::bit1}; + EXPECT_NE(ba1, ba3); +} + +TEST(BitArrayTest, Slice) { + auto arr = 0x20'DEADBEEF_b; + auto span2 = arr(4, 8); + EXPECT_EQ(span2.size(), 4); + EXPECT_EQ(span2[0], (0xE & (1 << 0)) ? bit::bit1 : bit::bit0); + EXPECT_EQ(span2[1], (0xE & (1 << 1)) ? bit::bit1 : bit::bit0); + EXPECT_EQ(span2[2], (0xE & (1 << 2)) ? bit::bit1 : bit::bit0); + EXPECT_EQ(span2[3], (0xE & (1 << 3)) ? bit::bit1 : bit::bit0); + span2 = 0x4'A_b; + EXPECT_EQ(arr, 0x20'DEADBEAF_b); +} + +TEST(BitArrayTest, SliceModify) { + auto arr = 0x24'DEADBEEF_b; + auto span2 = arr(4, 8); + EXPECT_EQ(span2.size(), 4); + EXPECT_EQ(span2[0], (0xE & (1 << 0)) ? bit::bit1 : bit::bit0); + EXPECT_EQ(span2[1], (0xE & (1 << 1)) ? bit::bit1 : bit::bit0); + EXPECT_EQ(span2[2], (0xE & (1 << 2)) ? bit::bit1 : bit::bit0); + EXPECT_EQ(span2[3], (0xE & (1 << 3)) ? bit::bit1 : bit::bit0); + span2[3] = bit::bit0; + EXPECT_EQ(span2[3], bit::bit0); + EXPECT_EQ(arr, 0x24'DEADBE6F_b); +} diff --git a/test/src/test-array_ref.cpp b/test/src/test-array_ref.cpp new file mode 100644 index 00000000..fb100303 --- /dev/null +++ b/test/src/test-array_ref.cpp @@ -0,0 +1,223 @@ +#include + +#include "bitlib/bitlib.hpp" +#include "gtest/gtest.h" + +TEST(BitArrayRef, ConstructorAndAssignment) { + // Prepare original bit array + bit::bit_array<> original(64, bit::bit0); + original[0] = bit::bit1; + original[10] = bit::bit1; + original[63] = bit::bit1; + + // Create a bit_array_ref to original's storage + bit::bit_array_ref<> ref(original.data(), original.size()); + + // Verify the reference matches the original content + EXPECT_EQ(ref.size(), original.size()); + EXPECT_EQ(ref[0], original[0]); + EXPECT_EQ(ref[10], original[10]); + EXPECT_EQ(ref[63], original[63]); + + // Modify through the reference + ref[20] = bit::bit1; + ref[30] = bit::bit1; + + // Verify the original was modified + EXPECT_EQ(original[20], bit::bit1); + EXPECT_EQ(original[30], bit::bit1); + + // Create another array + bit::bit_array<> other(64, bit::bit1); + + // Create another reference + bit::bit_array_ref<> other_ref(other.data(), other.size()); + + // Copy content (not pointer) + ref = other_ref; + + // Verify content was copied + for (size_t i = 0; i < ref.size(); ++i) { + EXPECT_EQ(ref[i], bit::bit1); + EXPECT_EQ(original[i], bit::bit1); + } +} + +TEST(BitArrayRef, SliceOperations) { + // Prepare original bit array + bit::bit_array<> original(64, bit::bit0); + original[10] = bit::bit1; + original[20] = bit::bit1; + original[30] = bit::bit1; + + // Create a slice (bit_array_ref) from offset 5 to 35 + auto slice = original(5, 35); + + // Verify the slice's content + EXPECT_EQ(slice.size(), 30); + EXPECT_EQ(slice[5], bit::bit1); // original[10] + EXPECT_EQ(slice[15], bit::bit1); // original[20] + EXPECT_EQ(slice[25], bit::bit1); // original[30] + + // Modify through the slice + slice[10] = bit::bit1; // original[15] + + // Verify the original was modified + EXPECT_EQ(original[15], bit::bit1); + + // Create another slice from our slice + auto subslice = slice(10, 20); + + // Verify the subslice's content + EXPECT_EQ(subslice.size(), 10); + EXPECT_EQ(subslice[0], bit::bit1); // slice[10], original[15] + EXPECT_EQ(subslice[5], bit::bit1); // slice[15], original[20] + + // Modify through the subslice + subslice[2] = bit::bit1; // slice[12], original[17] + + // Verify the original and slice were modified + EXPECT_EQ(slice[12], bit::bit1); + EXPECT_EQ(original[17], bit::bit1); +} + +TEST(BitArrayRef, Operations) { + // Prepare original bit array + bit::bit_array<> original(64, bit::bit0); + + // Create a bit_array_ref to original's storage + bit::bit_array_ref<> ref(original.data(), original.size()); + + // Test fill + ref.fill(bit::bit1); + + // Verify the original was filled with bit1 + for (size_t i = 0; i < original.size(); ++i) { + EXPECT_EQ(original[i], bit::bit1); + } + + // Create two arrays + bit::bit_array<> array1(32, bit::bit1); + bit::bit_array<> array2(32, bit::bit0); + + // Create refs + bit::bit_array_ref<> ref1(array1.data(), array1.size()); + bit::bit_array_ref<> ref2(array2.data(), array2.size()); + + // Test swap + ref1.swap(ref2); + + // Verify contents were swapped + for (size_t i = 0; i < array1.size(); ++i) { + EXPECT_EQ(array1[i], bit::bit0); + EXPECT_EQ(array2[i], bit::bit1); + } + + // Test equality + EXPECT_NE(ref1, ref2); + + ref1.fill(bit::bit1); + ref2.fill(bit::bit1); + + EXPECT_EQ(ref1, ref2); +} + +TEST(BitArrayRef, IntegralConversion) { + // Test small values + bit::bit_array<> value16(16); + value16[0] = bit::bit1; // 1 + value16[1] = bit::bit1; // 2 + value16[2] = bit::bit1; // 4 + + bit::bit_array_ref<> ref16(value16.data(), value16.size()); + + // Conversion + uint16_t int_val = static_cast(ref16); + EXPECT_EQ(int_val, 7u); // 2^0 + 2^1 + 2^2 = 1 + 2 + 4 = 7 + + // Test larger values + bit::bit_array<> value32(32); + value32[0] = bit::bit1; // 1 + value32[16] = bit::bit1; // 65536 + value32[31] = bit::bit1; // 2^31 + + bit::bit_array_ref<> ref32(value32.data(), value32.size()); + + // Conversion + uint32_t int_val32 = static_cast(ref32); + EXPECT_EQ(int_val32, 1u + 65536u + (1u << 31)); +} + +TEST(BitArrayRef, BitPointerConstructor) { + // Prepare original bit array + bit::bit_array<> original(64, bit::bit0); + original[0] = bit::bit1; + original[10] = bit::bit1; + original[63] = bit::bit1; + + // Create a bit_pointer to original's storage + bit::bit_pointer bit_ptr(original.data()); + + // Create a bit_array_ref using the bit_pointer + bit::bit_array_ref<> ref(bit_ptr, original.size()); + + // Verify the reference matches the original content + EXPECT_EQ(ref.size(), original.size()); + EXPECT_EQ(ref[0], original[0]); + EXPECT_EQ(ref[10], original[10]); + EXPECT_EQ(ref[63], original[63]); + + // Modify through the reference + ref[20] = bit::bit1; + ref[30] = bit::bit1; + + // Verify the original was modified + EXPECT_EQ(original[20], bit::bit1); + EXPECT_EQ(original[30], bit::bit1); +} + +TEST(BitArrayRef, MoveAssignment) { + bit::bit_array<> original(64, bit::bit0); + original[3] = bit::bit1; + original[13] = bit::bit1; + original[33] = bit::bit1; + original[43] = bit::bit1; + bit::bit_array_ref<> ref1(original, 32); + bit::bit_array_ref<> ref2(&original[32], 32); + ref1 = ref2; + EXPECT_EQ(ref1, original(32, 64)); +} + +TEST(BitArrayRef, OffsetBitPointerConstructor) { + // Prepare original bit array + bit::bit_array<> original(64, bit::bit0); + original[3] = bit::bit1; + original[13] = bit::bit1; + + // Create an offset bit_pointer (starting from the 3rd bit) + bit::bit_pointer bit_ptr(original.data(), 3); + + // Create a bit_array_ref using the offset bit_pointer + bit::bit_array_ref<> ref(bit_ptr, 32); + + // Verify the reference matches the expected content + EXPECT_EQ(ref.size(), 32); + EXPECT_EQ(ref[0], bit::bit1); // This should be original[3] + EXPECT_EQ(ref[10], bit::bit1); // This should be original[13] + + // Modify through the reference + ref[5] = bit::bit1; + + // Verify the original was modified at the correct offset + EXPECT_EQ(original[8], bit::bit1); // 3 + 5 = 8 +} + +TEST(BitArrayRef, Throws) { + bit::bit_array<> original(64, bit::bit0); + bit::bit_array_ref<> ref(original, 32); + bit::bit_array<> right_side(33, bit::bit0); + EXPECT_THROW(ref = right_side, std::invalid_argument); + bit::bit_array_ref<> ref2(&original[31], 33); + EXPECT_THROW(ref = ref2, std::invalid_argument); + EXPECT_THROW(ref.swap(ref2), std::invalid_argument); +} diff --git a/test/src/test-bitwise.cpp b/test/src/test-bitwise.cpp new file mode 100644 index 00000000..98a66b09 --- /dev/null +++ b/test/src/test-bitwise.cpp @@ -0,0 +1,46 @@ +#include +#include +#include + +#include "bitlib/bit-containers/bit_array.hpp" +#include "bitlib/bit-containers/bit_array_dynamic_extent.hpp" +#include "fixtures.hpp" + +// Third-party libraries +#include "gtest/gtest.h" + +TEST(Bitwise, UnaryNot) { + auto num = 13'8191_b; + EXPECT_EQ(~num, 13'0_b); + EXPECT_EQ(~(num(0, 7)), 7'0_b); +} + +TEST(Bitwise, Or) { + auto num = 0x20'DEAD0000_b; + auto num2 = 0x20'0000BEEF_b; + auto ord = num | num2; + EXPECT_EQ(ord, 0x20'DEADBEEF_b); + EXPECT_EQ(ord.size(), 0x20); + num |= num2; + EXPECT_EQ(ord, num); +} + +TEST(Bitwise, And) { + auto num = 0x20'DEAD0000_b; + auto num2 = 0x20'0000BEEF_b; + auto anded = num & num2; + EXPECT_EQ(anded, 0x20'00000000_b); + EXPECT_EQ(anded.size(), 0x20); + num &= num2; + EXPECT_EQ(anded, num); +} + +TEST(Bitwise, Xor) { + auto num = 0x20'DEAD0000_b; + auto num2 = 0x20'0000BEEF_b; + auto xord = num ^ num2; + EXPECT_EQ(xord, 0x20'DEADBEEF_b); + EXPECT_EQ(xord.size(), 0x20); + num ^= num2; + EXPECT_EQ(xord, num); +} diff --git a/test/src/test-copy.cpp b/test/src/test-copy.cpp index 225a0b4b..12ab40fe 100644 --- a/test/src/test-copy.cpp +++ b/test/src/test-copy.cpp @@ -1,11 +1,10 @@ // ============================= COPY TESTS =============================== // // Project: The Experimental Bit Algorithms Library -// Description: Tests for copy algorithms +// Description: Tests for copy algorithms // Contributor(s): Bryce Kille // License: BSD 3-Clause License // ========================================================================== // - // ============================== PREAMBLE ================================== // // C++ standard library #include @@ -28,44 +27,44 @@ TYPED_TEST(DoubleRangeTest, Copy) { std::vector& boolvec1 = this->random_boolvecs1[idx]; std::vector& boolvec2 = this->random_boolvecs2[idx]; long long start1 = generate_random_number( - 0, - std::min(bitvec1.size() - 1, digits + 1)); + 0, + std::min(bitvec1.size() - 1, digits + 1)); long long start2 = generate_random_number( - 0, - std::min(bitvec2.size() - 1, digits + 1)); + 0, + std::min(bitvec2.size() - 1, digits + 1)); const auto min_range = (start2 > start1) ? start2 - start1 : 0; const auto max_range = std::max( - min_range, - std::min(digits, bitvec1.size() - start1)); + min_range, + std::min(digits, bitvec1.size() - start1)); long long end1 = generate_random_number(min_range, max_range); auto bitret = bit::copy( - bitvec1.begin() + start1, - bitvec1.end() - end1, - bitvec2.begin() + start2); + bitvec1.begin() + start1, + bitvec1.end() - end1, + bitvec2.begin() + start2); auto boolret = std::copy( - boolvec1.begin() + start1, - boolvec1.end() - end1, - boolvec2.begin() + start2); + boolvec1.begin() + start1, + boolvec1.end() - end1, + boolvec2.begin() + start2); EXPECT_EQ( - bit::distance(bitvec2.begin(), bitret), - std::distance(boolvec2.begin(), boolret)); + bit::distance(bitvec2.begin(), bitret), + std::distance(boolvec2.begin(), boolret)); EXPECT_TRUE(std::equal( bitvec2.begin(), bitvec2.end(), boolvec2.begin(), boolvec2.end(), comparator) ); start2 = generate_random_number(0, start1); bitret = bit::copy( - bitvec1.begin() + start1, - bitvec1.end() - end1, - bitvec1.begin() + start2); + bitvec1.begin() + start1, + bitvec1.end() - end1, + bitvec1.begin() + start2); boolret = std::copy( - boolvec1.begin() + start1, - boolvec1.end() - end1, - boolvec1.begin() + start2); + boolvec1.begin() + start1, + boolvec1.end() - end1, + boolvec1.begin() + start2); EXPECT_EQ( - bit::distance(bitvec1.begin(), bitret), - std::distance(boolvec1.begin(), boolret)); + bit::distance(bitvec1.begin(), bitret), + std::distance(boolvec1.begin(), boolret)); EXPECT_TRUE(std::equal( bitvec1.begin(), bitvec1.end(), boolvec1.begin(), boolvec1.end(), comparator) @@ -73,4 +72,61 @@ TYPED_TEST(DoubleRangeTest, Copy) { } } +TYPED_TEST(MixedDoubleRangeTest, Copy) { + for (size_t idx = 0; idx < this->random_bitvecs1.size(); ++idx) { + bit::bit_vector& bitvec1 = this->random_bitvecs1[idx]; + bit::bit_vector& bitvec2 = this->random_bitvecs2[idx]; + constexpr auto min_digits = std::min(bit::bitsof(), bit::bitsof()); + std::vector& boolvec1 = this->random_boolvecs1[idx]; + std::vector& boolvec2 = this->random_boolvecs2[idx]; + long long start1 = generate_random_number( + 0, + std::min(bitvec1.size() - 1, min_digits + 1)); + long long start2 = generate_random_number( + 0, + std::min(bitvec2.size() - 1, min_digits + 1)); + const auto min_range = (start2 > start1) ? start2 - start1 : 0; + const auto max_range = std::max( + min_range, + std::min(min_digits, bitvec1.size() - start1)); + long long end1 = generate_random_number(min_range, max_range); + + auto bitret2 = bit::copy( + bitvec1.begin() + start1, + bitvec1.end() - end1, + bitvec2.begin() + start2); + auto boolret = std::copy( + boolvec1.begin() + start1, + boolvec1.end() - end1, + boolvec2.begin() + start2); + EXPECT_EQ( + bit::distance(bitvec2.begin(), bitret2), + std::distance(boolvec2.begin(), boolret)); + EXPECT_TRUE(std::equal( + bitvec2.begin(), bitvec2.end(), + boolvec2.begin(), boolvec2.end(), comparator)); + start2 = generate_random_number(0, start1); + auto bitret1 = bit::copy( + bitvec1.begin() + start1, + bitvec1.end() - end1, + bitvec1.begin() + start2); + boolret = std::copy( + boolvec1.begin() + start1, + boolvec1.end() - end1, + boolvec1.begin() + start2); + EXPECT_EQ( + bit::distance(bitvec1.begin(), bitret1), + std::distance(boolvec1.begin(), boolret)); + EXPECT_TRUE(std::equal( + bitvec1.begin(), bitvec1.end(), + boolvec1.begin(), boolvec1.end(), comparator)); + } +} +TEST(Copy, ImplicitConvert) { + std::array words = {0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x1A, 0x1B, 0x1C, 0x1D}; + bit::bit_array<> + bits(5 * 8 + 4, bit::bit0); + bit::copy(words.begin(), words.begin() + 5, bits.begin() + 4); + EXPECT_EQ(bits, 0x2C'0E0D0C0B0A0_b); +} diff --git a/test/src/test-count.cpp b/test/src/test-count.cpp index bb0ad072..8dca0004 100644 --- a/test/src/test-count.cpp +++ b/test/src/test-count.cpp @@ -1,11 +1,10 @@ // ============================== COUNT TESTS =============================== // // Project: The Experimental Bit Algorithms Library -// Description: Tests for count algorithms +// Description: Tests for count algorithms // Contributor(s): Bryce Kille // License: BSD 3-Clause License // ========================================================================== // - // ============================== PREAMBLE ================================== // // C++ standard library #include @@ -20,20 +19,20 @@ // ========================================================================== // TYPED_TEST(SingleRangeTest, Count) { - for (size_t idx = 0; idx < this->bit_size - 1; ++idx) { - bit::bit_vector bitvec = this->random_bitvecs[idx]; - std::vector boolvec = this->random_boolvecs[idx]; - size_t start_count = 16; - while (start_count--) { - unsigned long long start = generate_random_number(0, std::min(bitvec.size() - 1, 16)); - auto bitret = bit::count(bitvec.begin() + start, bitvec.end(), bit::bit1); - auto boolret = std::count(boolvec.begin() + start, boolvec.end(), true); - EXPECT_EQ(bitret, boolret); - bitret = bit::count(bitvec.begin() + start, bitvec.end(), bit::bit0); - boolret = std::count(boolvec.begin() + start, boolvec.end(), false); - EXPECT_EQ(bitret, boolret); - } + for (size_t idx = 0; idx < this->random_bitvecs.size(); ++idx) { + bit::bit_vector bitvec = this->random_bitvecs[idx]; + std::vector boolvec = this->random_boolvecs[idx]; + size_t start_count = 16; + while (start_count--) { + unsigned long long start = generate_random_number(0, std::min(bitvec.size() - 1, 16)); + auto bitret = bit::count(bitvec.begin() + start, bitvec.end(), bit::bit1); + auto boolret = std::count(boolvec.begin() + start, boolvec.end(), true); + EXPECT_EQ(bitret, boolret); + bitret = bit::count(bitvec.begin() + start, bitvec.end(), bit::bit0); + boolret = std::count(boolvec.begin() + start, boolvec.end(), false); + EXPECT_EQ(bitret, boolret); } + } } diff --git a/test/src/test-count_leading.cpp b/test/src/test-count_leading.cpp new file mode 100644 index 00000000..254b4dec --- /dev/null +++ b/test/src/test-count_leading.cpp @@ -0,0 +1,18 @@ +#include +#include +#include + +#include "bitlib/bit-containers/bit_array.hpp" +#include "bitlib/bit-containers/bit_array_dynamic_extent.hpp" +#include "bitlib/bit-algorithms/count.hpp" +#include "fixtures.hpp" + +// Third-party libraries +#include "gtest/gtest.h" + +TEST(CountLeading, CountLeadingZeroes) { + auto num = bit::bit_array<128>(bit::bit0); + auto clz = bit::count_msb(num.begin(), num.end(), bit::bit0); + std::cout << "Count leading zeroes: " << clz << std::endl; + EXPECT_EQ(clz, 128); +} diff --git a/test/src/test-division.cpp b/test/src/test-division.cpp new file mode 100644 index 00000000..c3ca4587 --- /dev/null +++ b/test/src/test-division.cpp @@ -0,0 +1,22 @@ +#include + +#include "bitlib/bitlib.hpp" +#include "gtest/gtest.h" + +TEST(BitDivision, Basic) { + auto bits = 0xF'0F_b; + EXPECT_EQ((15 % 2), bit::division(bits.begin(), bits.end(), static_cast(0x2u))); + EXPECT_EQ(0xF'07_b, bits); +} + +TEST(BitDivision, AllOnes) { + auto bits = 0xF'00FF_b; + EXPECT_EQ((255 % 255), bit::division(bits.begin(), bits.end(), 8'255_b)); + EXPECT_EQ(0xF'0001_b, bits); +} + +TEST(BitDivision, LowBits) { + auto bits = 0xF'F_b; + EXPECT_EQ((15 % 2), bit::division(bits.begin(), bits.end(), 2'2_b)); + EXPECT_EQ(0xF'07_b, bits); +} diff --git a/test/src/test-equal.cpp b/test/src/test-equal.cpp index f8b09383..01355f3a 100644 --- a/test/src/test-equal.cpp +++ b/test/src/test-equal.cpp @@ -1,11 +1,10 @@ // ============================= EQUAL TESTS =============================== // // Project: The Experimental Bit Algorithms Library -// Description: Tests for equal algorithms +// Description: Tests for equal algorithms // Contributor(s): Bryce Kille // License: BSD 3-Clause License // ========================================================================== // - // ============================== PREAMBLE ================================== // // C++ standard library #include @@ -28,46 +27,46 @@ TYPED_TEST(DoubleRangeTest, Equal) { std::vector& boolvec1 = this->random_boolvecs1[idx]; std::vector& boolvec2 = this->random_boolvecs2[idx]; long long start1 = generate_random_number( - 0, - std::min(bitvec1.size() - 1, digits + 1)); + 0, + std::min(bitvec1.size() - 1, digits + 1)); long long start2 = generate_random_number( - 0, - std::min(bitvec2.size() - 1, digits + 1)); + 0, + std::min(bitvec2.size() - 1, digits + 1)); const auto min_range = (start2 > start1) ? start2 - start1 : 0; const auto max_range = std::max( - min_range, - std::min(digits, bitvec1.size() - start1)); + min_range, + std::min(digits, bitvec1.size() - start1)); long long end1 = generate_random_number(min_range, max_range); auto bitret = bit::equal( - bitvec1.begin() + start1, - bitvec1.end() - end1, - bitvec2.begin() + start2); + bitvec1.begin() + start1, + bitvec1.end() - end1, + bitvec2.begin() + start2); auto boolret = std::equal( - boolvec1.begin() + start1, - boolvec1.end() - end1, - boolvec2.begin() + start2); + boolvec1.begin() + start1, + boolvec1.end() - end1, + boolvec2.begin() + start2); EXPECT_EQ(boolret, bitret); EXPECT_TRUE(std::equal( bitvec2.begin(), bitvec2.end(), boolvec2.begin(), boolvec2.end(), comparator) ); bit::copy( - bitvec1.begin() + start1, - bitvec1.end() - end1, - bitvec2.begin() + start2); + bitvec1.begin() + start1, + bitvec1.end() - end1, + bitvec2.begin() + start2); std::copy( - boolvec1.begin() + start1, - boolvec1.end() - end1, - boolvec2.begin() + start2); + boolvec1.begin() + start1, + boolvec1.end() - end1, + boolvec2.begin() + start2); bitret = bit::equal( - bitvec1.begin() + start1, - bitvec1.end() - end1, - bitvec2.begin() + start2); + bitvec1.begin() + start1, + bitvec1.end() - end1, + bitvec2.begin() + start2); boolret = std::equal( - boolvec1.begin() + start1, - boolvec1.end() - end1, - boolvec2.begin() + start2); + boolvec1.begin() + start1, + boolvec1.end() - end1, + boolvec2.begin() + start2); EXPECT_EQ(boolret, bitret); EXPECT_TRUE(std::equal( bitvec1.begin(), bitvec1.end(), @@ -76,4 +75,62 @@ TYPED_TEST(DoubleRangeTest, Equal) { } } +TYPED_TEST(MixedDoubleRangeTest, Equal) { + for (size_t idx = 0; idx < this->random_bitvecs1.size(); ++idx) { + bit::bit_vector& bitvec1 = this->random_bitvecs1[idx]; + bit::bit_vector& bitvec2 = this->random_bitvecs2[idx]; + constexpr auto min_digits = std::min(bit::bitsof(), bit::bitsof()); + std::vector& boolvec1 = this->random_boolvecs1[idx]; + std::vector& boolvec2 = this->random_boolvecs2[idx]; + long long start1 = generate_random_number( + 0, + std::min(bitvec1.size() - 1, min_digits + 1)); + long long start2 = generate_random_number( + 0, + std::min(bitvec2.size() - 1, min_digits + 1)); + const auto min_range = (start2 > start1) ? start2 - start1 : 0; + const auto max_range = std::max( + min_range, + std::min(min_digits, bitvec1.size() - start1)); + long long end1 = generate_random_number(min_range, max_range); + auto bitret = bit::equal( + bitvec1.begin() + start1, + bitvec1.end() - end1, + bitvec2.begin() + start2); + auto boolret = std::equal( + boolvec1.begin() + start1, + boolvec1.end() - end1, + boolvec2.begin() + start2); + EXPECT_EQ(boolret, bitret); + EXPECT_TRUE(std::equal( + bitvec1.begin(), bitvec1.end(), + boolvec1.begin(), boolvec1.end(), comparator)); + EXPECT_TRUE(std::equal( + bitvec2.begin(), bitvec2.end(), + boolvec2.begin(), boolvec2.end(), comparator)); + bit::copy( + bitvec1.begin() + start1, + bitvec1.end() - end1, + bitvec2.begin() + start2); + std::copy( + boolvec1.begin() + start1, + boolvec1.end() - end1, + boolvec2.begin() + start2); + EXPECT_TRUE(std::equal( + bitvec2.begin(), bitvec2.end(), + boolvec2.begin(), boolvec2.end(), comparator)); + bitret = bit::equal( + bitvec1.begin() + start1, + bitvec1.end() - end1, + bitvec2.begin() + start2); + boolret = std::equal( + boolvec1.begin() + start1, + boolvec1.end() - end1, + boolvec2.begin() + start2); + EXPECT_EQ(boolret, bitret); + EXPECT_TRUE(std::equal( + bitvec1.begin(), bitvec1.end(), + boolvec1.begin(), boolvec1.end(), comparator)); + } +} diff --git a/test/src/test-iterator_adapter.cpp b/test/src/test-iterator_adapter.cpp new file mode 100644 index 00000000..8f35b79d --- /dev/null +++ b/test/src/test-iterator_adapter.cpp @@ -0,0 +1,321 @@ +// ============================= TRANSFORM TESTS =============================== // +// Project: The Experimental Bit Algorithms Library +// Description: Tests for transform algorithms +// Contributor(s): Bryce Kille +// License: BSD 3-Clause License +// ========================================================================== // + + +// ============================== PREAMBLE ================================== // +// C++ standard library +#include +#include +#include +// Project sources +#include "bitlib/bit-algorithms/bit_algorithm.hpp" +#include "bitlib/bit-containers/bit-containers.hpp" +#include "fixtures.hpp" +// Third-party libraries +#include "gtest/gtest.h" +// Miscellaneous +// ========================================================================== // +#include "bitlib/bit-iterator/bit_word_pointer_adapter.hpp" + +// +// GoogleTest suite for bit::bit_word_pointer_adapter. +// Covers both "big‐to‐small" and "small‐to‐large" modes. +// + +// Assumes a little‐endian platform (so that reinterpret_cast<…> of bytes +// within larger words behaves predictably). If you compile on a big‐endian +// machine, these tests will fail. +// +// Verify that we are on a little‐endian machine: +static_assert(std::endian::native == std::endian::little, + "These tests assume little‐endian byte order"); + +TEST(IteratorAdapter, Basic) { + std::vector base{0x1234, 0x2345, 0x3456, 0x4567}; + bit::bit_word_pointer_adapter::iterator> adapter(base.begin()); + EXPECT_EQ(*adapter, 0x34); + EXPECT_EQ(*(++adapter), 0x12); + EXPECT_EQ(*(adapter++), 0x12); + EXPECT_EQ(*adapter, 0x45); + adapter += 2; + EXPECT_EQ(*adapter, 0x56); +} + +// ----------------------------------------------------------------------------- +// TEST SUITE: Big-to-Small Mode (BaseIterator.value_type > Iterator.value_type) +// ----------------------------------------------------------------------------- +TEST(BitIteratorAdapter_BigToSmall, DereferenceAndIncrement) { + // Base‐words are 32-bit => 4 × 8-bit subwords + constexpr size_t N = 2; + uint32_t base_arr[N] = {0x04030201u, 0x08070605u}; + // Bytes in memory (little‐endian): + // base_arr[0]: {0x01,0x02,0x03,0x04} + // base_arr[1]: {0x05,0x06,0x07,0x08} + + // Iterator type: iterate in 8-bit steps over a 32-bit array + using Iter8 = uint8_t*; + using Base32 = uint32_t*; + using Adapter8_32 = bit::bit_word_pointer_adapter; + + Adapter8_32 it(base_arr, /*pos=*/0); + + // Check that *it sees the first byte = 0x01 + EXPECT_EQ(*it, 0x01u); + + // Advance 1: pos=1, still in base_arr[0] + ++it; + EXPECT_EQ(it.index(), 1u); + EXPECT_EQ(*it, 0x02u); + + // Advance to pos=3 + it += 2; + EXPECT_EQ(it.index(), 3u); + EXPECT_EQ(*it, 0x04u); + + // Next increment should roll over to base_arr[1], pos=0 + ++it; + EXPECT_EQ(it.base(), base_arr + 1); + EXPECT_EQ(it.index(), 0u); + EXPECT_EQ(*it, 0x05u); + + // Further increments + ++it; // pos=1 + EXPECT_EQ(it.index(), 1u); + EXPECT_EQ(*it, 0x06u); + + ++it; // pos=2 + EXPECT_EQ(it.index(), 2u); + EXPECT_EQ(*it, 0x07u); + + ++it; // pos=3 + EXPECT_EQ(it.index(), 3u); + EXPECT_EQ(*it, 0x08u); +} + +TEST(BitIteratorAdapter_BigToSmall, OperatorStarAndArrow) { + // Use a single base word = 0xAABBCCDD + uint32_t base_val = 0xAABBCCDDu; + // Memory bytes: { DD, CC, BB, AA } little-endian + using Iter8 = uint8_t*; + using Base32 = uint32_t*; + using Adapter8_32 = bit::bit_word_pointer_adapter; + + Adapter8_32 it(&base_val, /*pos=*/0); + + EXPECT_EQ(*it, 0xDDu); + // operator->() should return a pointer to the byte + auto p = *it; + EXPECT_EQ(p, 0xDDu); + + // Move to pos=2 + it += 2; + EXPECT_EQ(*it, 0xBBu); + EXPECT_EQ(it.index(), 2u); + + // operator->() at pos=2 + p = *it; + EXPECT_EQ(p, 0xBBu); +} + +TEST(BitIteratorAdapter_BigToSmall, OperatorPlusMinusAndDistance) { + // Fill two 32-bit words + uint32_t base_arr[2] = {0x11223344u, 0x55667788u}; + // Bytes: {44,33,22,11, 88,77,66,55} + + using Iter8 = uint8_t*; + using Base32 = uint32_t*; + using Adapter8_32 = bit::bit_word_pointer_adapter; + + Adapter8_32 it(base_arr, /*pos=*/0); // points at first byte (0x44) + + // it + 3 => should point to 4th byte of base_arr[0] = 0x11 + auto it3 = it + 3; + EXPECT_EQ(it3.base(), base_arr); + EXPECT_EQ(it3.index(), 3u); + EXPECT_EQ(*it3, 0x11u); + + // it + 4 => moves into base_arr[1], pos=0 => should be 0x88 + auto it4 = it + 4; + EXPECT_EQ(it4.base(), base_arr + 1); + EXPECT_EQ(it4.index(), 0u); + EXPECT_EQ(*it4, 0x88u); + + // Now distance between it and it+7 = 7 + auto it7 = it + 7; + EXPECT_EQ(it7.base(), base_arr + 1); + EXPECT_EQ(it7.index(), 3u); + EXPECT_EQ(*it7, 0x55u); + + EXPECT_EQ(it7 - it, 7); + EXPECT_EQ(it - it, 0); + + // Test operator- (iterator difference) with reversed operands + EXPECT_EQ((it - it7), -7); + + // Advance it4 by -2 => (it4 - 2) = it + 2 => base_arr[0], pos=2 => 0x22 + auto it2 = it4 - 2; + EXPECT_EQ(it2.base(), base_arr); + EXPECT_EQ(it2.index(), 2u); + EXPECT_EQ(*it2, 0x22u); +} + +TEST(BitIteratorAdapter_BigToSmall, OperatorSubscript) { + // 1 uint32_t: 0xDEADBEEFu => bytes { EF, BE, AD, DE } + uint32_t base_val = 0xDEADBEEFu; + using Iter8 = uint8_t*; + using Base32 = uint32_t*; + using Adapter8_32 = bit::bit_word_pointer_adapter; + + Adapter8_32 it(&base_val, /*pos=*/0); + // it[0] == 0xEF, it[1] == 0xBE, it[2] == 0xAD, it[3] == 0xDE + EXPECT_EQ(it[0], static_cast(0xEFu)); + EXPECT_EQ(it[1], static_cast(0xBEu)); + EXPECT_EQ(it[2], static_cast(0xADu)); + EXPECT_EQ(it[3], static_cast(0xDEu)); + + // Negative index: it[ -1 ] => moves to previous base word; + // but since there's no previous word, behavior is UB. We skip testing negative here. + + // For safety, create two‐element array: + uint32_t arr2[2] = {0x01020304u, 0x05060708u}; + Adapter8_32 it2(arr2, 0); + EXPECT_EQ(it2[0], 0x04u); + EXPECT_EQ(it2[1], 0x03u); + EXPECT_EQ(it2[2], 0x02u); + EXPECT_EQ(it2[3], 0x01u); + EXPECT_EQ(it2[4], 0x08u); + EXPECT_EQ(it2[5], 0x07u); + EXPECT_EQ(it2[6], 0x06u); + EXPECT_EQ(it2[7], 0x05u); +} + +// ----------------------------------------------------------------------------- +// TEST SUITE: Small-to-Large Mode (BaseIterator.value_type < Iterator.value_type) +// ----------------------------------------------------------------------------- +TEST(BitIteratorAdapter_SmallToLarge, DereferenceAndIncrement) { + // Base: array of uint8_t; Iterator’s value_type = uint16_t => ratio = 2 + uint8_t base_arr[] = { + 0x11, 0x22, // first 16-bit chunk => 0x2211 + 0x33, 0x44, // second 16-bit chunk => 0x4433 + 0x55, 0x66 // third 16-bit chunk => 0x6655 + }; + using Iter16 = uint16_t*; + using Base8 = uint8_t*; + using Adapter16_8 = bit::bit_word_pointer_adapter; + + Adapter16_8 it(base_arr); + + // *it: combine base[0] + (base[1] << 8) => 0x2211 + uint16_t first = *it; + EXPECT_EQ(first, static_cast(0x2211u)); + + // ++it => skip 2 bytes: now base points to base_arr+2 + ++it; + uint16_t second = *it; + EXPECT_EQ(second, static_cast(0x4433u)); + + // ++it again + ++it; + uint16_t third = *it; + EXPECT_EQ(third, static_cast(0x6655u)); +} + +TEST(BitIteratorAdapter_SmallToLarge, OperatorPlusMinusDistance) { + // Base: eight uint8_t => four uint16_t logical elements + uint8_t base_arr[8] = { + 0x10, 0x01, // 0x0110 + 0x20, 0x02, // 0x0220 + 0x30, 0x03, // 0x0330 + 0x40, 0x04 // 0x0440 + }; + using Iter16 = uint16_t*; + using Base8 = uint8_t*; + using Adapter16_8 = bit::bit_word_pointer_adapter; + + Adapter16_8 it(base_arr); + + // it + 0 => *it == 0x0110 + EXPECT_EQ(*it, static_cast(0x0110u)); + + // it + 1 => combine bytes at indices [2,3] => 0x0220 + auto it1 = it + 1; + EXPECT_EQ(*it1, static_cast(0x0220u)); + + // it + 2 => 0x0330 + auto it2 = it + 2; + EXPECT_EQ(*it2, static_cast(0x0330u)); + + // it + 3 => 0x0440 + auto it3 = it + 3; + EXPECT_EQ(*it3, static_cast(0x0440u)); + + // Distance: it3 - it == 3 + EXPECT_EQ(it3 - it, 3); + EXPECT_EQ(it - it, 0); + + // it2 - it1 == 1 + EXPECT_EQ(it2 - it1, 1); + + // Subtraction reversed: + EXPECT_EQ(it - it3, -3); +} + +TEST(BitIteratorAdapter_SmallToLarge, OperatorStarAndArrow) { + uint8_t base_arr[4] = {0xAA, 0xBB, 0xCC, 0xDD}; + using Iter16 = uint16_t*; + using Base8 = uint8_t*; + using Adapter16_8 = bit::bit_word_pointer_adapter; + + Adapter16_8 it(base_arr); + // *it => (0xBB << 8) | 0xAA = 0xBBAA + EXPECT_EQ(*it, static_cast(0xBBAAu)); +} + +TEST(BitIteratorAdapter_SmallToLarge, OperatorPlusEqualsAndMinusEquals) { + // Base: six uint8_t => three 16-bit words + uint8_t base_arr[6] = { + 0x01, 0xA0, // 0xA001 + 0x02, 0xB0, // 0xB002 + 0x03, 0xC0 // 0xC003 + }; + using Iter16 = uint16_t*; + using Base8 = uint8_t*; + using Adapter16_8 = bit::bit_word_pointer_adapter; + + Adapter16_8 it(base_arr); + + // it += 1 => now at second 16-bit chunk: 0xB002 + it += 1; + EXPECT_EQ(*it, static_cast(0xB002u)); + + // it += 1 => now at third: 0xC003 + it += 1; + EXPECT_EQ(*it, static_cast(0xC003u)); + + // it -= 2 => go back to first chunk: 0xA001 + it -= 2; + EXPECT_EQ(*it, static_cast(0xA001u)); +} + +TEST(BitIteratorAdapter_SmallToLarge, OperatorMinusBetweenDifferentIterators) { + // Base: eight uint8_t => four 16-bit words + uint8_t base_arr[8] = { + 0x01, 0x10, // 0x1001 + 0x02, 0x20, // 0x2002 + 0x03, 0x30, // 0x3003 + 0x04, 0x40 // 0x4004 + }; + using Iter16 = uint16_t*; + using Base8 = uint8_t*; + using Adapter16_8 = bit::bit_word_pointer_adapter; + + Adapter16_8 it0(base_arr); + Adapter16_8 it2(base_arr + 4); // same as (it0 + 2) + + EXPECT_EQ(it2 - it0, 2); + EXPECT_EQ(it0 - it2, -2); +} diff --git a/test/src/test-literal.cpp b/test/src/test-literal.cpp new file mode 100644 index 00000000..d91e2cf3 --- /dev/null +++ b/test/src/test-literal.cpp @@ -0,0 +1,106 @@ +// =============================== FIXTURES ================================= // +// Project: The Experimental Bit Algorithms Library +// Description: Fixtures for testing +// Contributor(s): Bryce Kille +// License: BSD 3-Clause License +// ========================================================================== // + + +// ============================== PREAMBLE ================================== // +// C++ standard library +#include +#include +// Project sources +#include "bitlib/bit-containers/bit-containers.hpp" +#include "fixtures.hpp" +// Third-party libraries +#include "gtest/gtest.h" +// Miscellaneous +// ========================================================================== // + +TEST(BitLiteral, dec_base) { + auto one = 1'1_b; + EXPECT_EQ(one.size(), 1); + EXPECT_EQ(one[0], bit::bit1); + auto three = 4'1'1_b; + EXPECT_EQ(three.size(), 4); + EXPECT_EQ(three[0], bit::bit1); + EXPECT_EQ(three[1], bit::bit1); + auto ten = 4'10_b; + EXPECT_EQ(ten.size(), 4); + EXPECT_EQ(ten[0], bit::bit0); + EXPECT_EQ(ten[1], bit::bit1); + EXPECT_EQ(ten[2], bit::bit0); + EXPECT_EQ(ten[3], bit::bit1); + auto ten_2 = 10_b; + EXPECT_EQ(ten_2.size(), 4); + EXPECT_EQ(ten_2[0], bit::bit0); + EXPECT_EQ(ten_2[1], bit::bit1); + EXPECT_EQ(ten_2[2], bit::bit0); + EXPECT_EQ(ten_2[3], bit::bit1); +} + +TEST(BitLiteral, hex_base) { + auto fifteen = 0xF'1234_b; + EXPECT_EQ(fifteen.size(), 15); + EXPECT_EQ(fifteen[0], bit::bit0); + EXPECT_EQ(fifteen[1], bit::bit0); + EXPECT_EQ(fifteen[2], bit::bit1); + auto unsized = 0x1234_b; + EXPECT_EQ(unsized.size(), 16); + EXPECT_EQ(unsized[0], bit::bit0); + EXPECT_EQ(unsized[1], bit::bit0); + EXPECT_EQ(unsized[2], bit::bit1); +} + +TEST(BitLiteral, bin_base) { + auto fifteen = 0b1111'001000110100_b; + EXPECT_EQ(fifteen.size(), 15); + EXPECT_EQ(fifteen[0], bit::bit0); + EXPECT_EQ(fifteen[1], bit::bit0); + EXPECT_EQ(fifteen[2], bit::bit1); + auto unsized = 0b01101_b; + EXPECT_EQ(unsized.size(), 5); + EXPECT_EQ(unsized[0], bit::bit1); + EXPECT_EQ(unsized[1], bit::bit0); + EXPECT_EQ(unsized[2], bit::bit1); +} + +TEST(BitLiteral, UserDefinedLiteral) { + auto arr = 0b01001101_b; + EXPECT_EQ(arr.size(), 8); + EXPECT_EQ(arr[7], bit::bit0); + EXPECT_EQ(arr[0], bit::bit1); + auto arr2 = 0b1000'01001101_b; + EXPECT_EQ(arr2.size(), 8); +} + +TEST(BitLiteral, UserDefinedHexLiteral) { + auto arr = 0x010A110A_b; + EXPECT_EQ(arr.size(), 32); + EXPECT_EQ(arr[0], bit::bit0); + EXPECT_EQ(arr[1], bit::bit1); + EXPECT_EQ(arr[2], bit::bit0); + EXPECT_EQ(arr[3], bit::bit1); + EXPECT_EQ(arr[7], bit::bit0); + auto arr2 = 0x19'010A110A_b; + EXPECT_EQ(arr2.size(), 25); +} + +TEST(BitLiteral, UserDefinedDecLiteral) { + auto arr = 16'12345_b; + EXPECT_EQ(arr.size(), 16); + auto arr2 = 0x3039_b; + EXPECT_EQ(arr, arr2); + auto arr3 = 16'123'45_b; + EXPECT_EQ(arr, arr3); +} + +TEST(BitLiteral, SizeOneLiteral) { + auto arr = 1_b; + EXPECT_EQ(arr.size(), 1); + + bit::bit_value b = arr; + EXPECT_EQ(b, bit::bit1); + EXPECT_EQ(arr, bit::bit1); +} diff --git a/test/src/test-mdspan.cpp b/test/src/test-mdspan.cpp new file mode 100644 index 00000000..a3d66615 --- /dev/null +++ b/test/src/test-mdspan.cpp @@ -0,0 +1,112 @@ +#include "bitlib/bitlib.hpp" +// Third-party libraries +#include +#include + +#include "gtest/gtest.h" +#if defined(MDSPAN_IMPL_STANDARD_NAMESPACE) +#include "mdspan/mdspan.hpp" +#else +#include +#endif + +TEST(MdSpanTest, BitDefaultLayout) { + uint32_t rot = 0xDEADBEEF; + bit::bit_array<> dynarr(5 * 6 * 7); + for (int i = 0; i < 5; i++) { + for (int j = 0; j < 6; j++) { + for (int k = 0; k < 7; k++) { + dynarr[i * 6 * 7 + j * 7 + k] = (rot & 1) ? bit::bit1 : bit::bit0; + rot = (rot >> 1) | ((rot & 1) << 31); + } + } + } + rot = 0xDEADBEEF; + std::mdspan< + bit::bit_value, + std::dextents, + std::layout_right, + bit::bit_default_accessor > + myspan(&dynarr[0], 5, 6, 7); + + for (size_t i = 0; i < myspan.extent(0); i++) { + for (size_t j = 0; j < myspan.extent(1); j++) { + for (size_t k = 0; k < myspan.extent(2); k++) { + bit::bit_value expected = ((rot & 1) ? bit::bit1 : bit::bit0); + EXPECT_EQ(dynarr[i * 6 * 7 + j * 7 + k], expected); + bit::bit_value actual = myspan[i, j, k]; + EXPECT_EQ(actual, expected); + rot = (rot >> 1) | ((rot & 1) << 31); + } + } + } +} + +TEST(MdSpanTest, BitFixedWordLayout) { + uint32_t rot = 0xDEADBEEF; + bit::bit_array<> dynarr(5 * 6 * 7); + for (int i = 0; i < 5; i++) { + for (int j = 0; j < 6; j++) { + for (int k = 0; k < 7; k++) { + dynarr[i * 6 * 7 + j * 7 + k] = (rot & 1) ? bit::bit1 : bit::bit0; + rot = (rot >> 1) | ((rot & 1) << 31); + } + } + } + rot = 0xDEADBEEF; + using accessor_t = bit::bit_word_accessor<5, uintptr_t>; + using mdspan_t = + std::mdspan< + accessor_t::element_type, + std::dextents, + std::layout_right, + accessor_t>; + + mdspan_t myspan(&dynarr[0], 6, 7); + for (size_t i = 0; i < myspan.extent(0); i++) { + for (size_t j = 0; j < myspan.extent(1); j++) { + auto ref = myspan[i, j]; + for (size_t k = 0; k < 5; k++) { + bit::bit_value expected = ((rot & 1) ? bit::bit1 : bit::bit0); + EXPECT_EQ(ref[k], expected); + rot = (rot >> 1) | ((rot & 1) << 31); + } + } + } +} + +TEST(MdSpanTest, BitDynamicWordLayout) { + uint32_t rot = 0xDEADBEEF; + bit::bit_array<> dynarr(5 * 6 * 7); + for (int i = 0; i < 5; i++) { + for (int j = 0; j < 6; j++) { + for (int k = 0; k < 7; k++) { + dynarr[i * 6 * 7 + j * 7 + k] = (rot & 1) ? bit::bit1 : bit::bit0; + rot = (rot >> 1) | ((rot & 1) << 31); + } + } + } + rot = 0xDEADBEEF; + using accessor_t = bit::bit_word_accessor; + using extents_t = std::dextents; + using mdspan_t = + std::mdspan< + accessor_t::element_type, + std::dextents, + std::layout_right, + accessor_t>; + + accessor_t myaccessor(5); + extents_t dyn_ext{6, 7}; + mdspan_t myspan(&dynarr[0], dyn_ext, myaccessor); + for (size_t i = 0; i < myspan.extent(0); i++) { + for (size_t j = 0; j < myspan.extent(1); j++) { + auto ref = myspan[i, j]; + for (size_t k = 0; k < 5; k++) { + bit::bit_value expected = ((rot & 1) ? bit::bit1 : bit::bit0); + EXPECT_EQ(ref[k], expected); + rot = (rot >> 1) | ((rot & 1) << 31); + } + } + } +} diff --git a/test/src/test-move.cpp b/test/src/test-move.cpp index bede7a79..f1f9d807 100644 --- a/test/src/test-move.cpp +++ b/test/src/test-move.cpp @@ -1,11 +1,10 @@ // ============================= MOVE TESTS =============================== // // Project: The Experimental Bit Algorithms Library -// Description: Tests for move algorithms +// Description: Tests for move algorithms // Contributor(s): Bryce Kille // License: BSD 3-Clause License // ========================================================================== // - // ============================== PREAMBLE ================================== // // C++ standard library #include @@ -20,57 +19,55 @@ // ========================================================================== // TYPED_TEST(DoubleRangeTest, Move) { - for (size_t idx = 0; idx < this->random_bitvecs1.size(); ++idx) { - using WordType = typename TestFixture::base_type; - bit::bit_vector& bitvec1 = this->random_bitvecs1[idx]; - bit::bit_vector& bitvec2 = this->random_bitvecs2[idx]; - constexpr auto digits = bit::binary_digits::value; - std::vector& boolvec1 = this->random_boolvecs1[idx]; - std::vector& boolvec2 = this->random_boolvecs2[idx]; - long long start1 = generate_random_number( - 0, - std::min(bitvec1.size() - 1, digits + 1)); - long long start2 = generate_random_number( - 0, - std::min(bitvec2.size() - 1, digits + 1)); - const auto min_range = (start2 > start1) ? start2 - start1 : 0; - const auto max_range = std::max( - min_range, - std::min(digits, bitvec1.size() - start1)); - long long end1 = generate_random_number(min_range, max_range); + using WordType = typename TestFixture::base_type; + constexpr auto digits = bit::binary_digits::value; + for (size_t idx = 0; idx < this->random_bitvecs1.size(); ++idx) { + bit::bit_vector& bitvec1 = this->random_bitvecs1[idx]; + bit::bit_vector& bitvec2 = this->random_bitvecs2[idx]; + std::vector& boolvec1 = this->random_boolvecs1[idx]; + std::vector& boolvec2 = this->random_boolvecs2[idx]; + long long start1 = generate_random_number( + 0, + std::min(bitvec1.size() - 1, digits + 1)); + long long start2 = generate_random_number( + 0, + std::min(bitvec2.size() - 1, digits + 1)); + const auto min_range = (start2 > start1) ? start2 - start1 : 0; + const auto max_range = std::max( + min_range, + std::min(digits, bitvec1.size() - start1)); + long long end1 = generate_random_number(min_range, max_range); - auto bitret = bit::move( - bitvec1.begin() + start1, - bitvec1.end() - end1, - bitvec2.begin() + start2); - auto boolret = std::move( - boolvec1.begin() + start1, - boolvec1.end() - end1, - boolvec2.begin() + start2); - EXPECT_EQ( - bit::distance(bitvec2.begin(), bitret), - std::distance(boolvec2.begin(), boolret)); - EXPECT_TRUE(std::equal( - bitvec2.begin(), bitvec2.end(), - boolvec2.begin(), boolvec2.end(), comparator) - ); - start2 = generate_random_number(0, start1); - bitret = bit::move( - bitvec1.begin() + start1, - bitvec1.end() - end1, - bitvec1.begin() + start2); - boolret = std::move( - boolvec1.begin() + start1, - boolvec1.end() - end1, - boolvec1.begin() + start2); - EXPECT_EQ( - bit::distance(bitvec1.begin(), bitret), - std::distance(boolvec1.begin(), boolret)); - EXPECT_TRUE(std::equal( - bitvec1.begin(), bitvec1.end(), - boolvec1.begin(), boolvec1.end(), comparator) - ); - } + auto bitret = bit::move( + bitvec1.begin() + start1, + bitvec1.end() - end1, + bitvec2.begin() + start2); + auto boolret = std::move( + boolvec1.begin() + start1, + boolvec1.end() - end1, + boolvec2.begin() + start2); + EXPECT_EQ( + bit::distance(bitvec2.begin(), bitret), + std::distance(boolvec2.begin(), boolret)); + EXPECT_TRUE(std::equal( + bitvec2.begin(), bitvec2.end(), + boolvec2.begin(), boolvec2.end(), comparator)); + start2 = generate_random_number(0, start1); + bitret = bit::move( + bitvec1.begin() + start1, + bitvec1.end() - end1, + bitvec1.begin() + start2); + boolret = std::move( + boolvec1.begin() + start1, + boolvec1.end() - end1, + boolvec1.begin() + start2); + EXPECT_EQ( + bit::distance(bitvec1.begin(), bitret), + std::distance(boolvec1.begin(), boolret)); + EXPECT_TRUE(std::equal( + bitvec1.begin(), bitvec1.end(), + boolvec1.begin(), boolvec1.end(), comparator)); + } } diff --git a/test/src/test-multiplication.cpp b/test/src/test-multiplication.cpp new file mode 100644 index 00000000..8c952ca9 --- /dev/null +++ b/test/src/test-multiplication.cpp @@ -0,0 +1,22 @@ +#include + +#include "bitlib/bitlib.hpp" +#include "gtest/gtest.h" + +TEST(BitMultiplication, Basic) { + auto bits = 0xF'0F_b; + EXPECT_EQ(0, bit::multiplication(bits.begin(), bits.end(), static_cast(0x2u))); + EXPECT_EQ(0xF'1E_b, bits); +} + +TEST(BitMultiplication, AllOnes) { + auto bits = 0xF'00FF_b; + EXPECT_EQ(1, bit::multiplication(bits.begin(), bits.end(), 8'255_b)); + EXPECT_EQ(0xF'7E01_b, bits); +} + +TEST(BitMultiplication, LowBits) { + auto bits = 0xF'F_b; + EXPECT_EQ(0, bit::multiplication(bits.begin(), bits.end(), 2'2_b)); + EXPECT_EQ(0xF'1E_b, bits); +} diff --git a/test/src/test-span.cpp b/test/src/test-span.cpp new file mode 100644 index 00000000..262377a2 --- /dev/null +++ b/test/src/test-span.cpp @@ -0,0 +1,240 @@ +#include +#include + +#include "bitlib/bit-containers/bit_span.hpp" + +#include "fixtures.hpp" + +// Third-party libraries +#include "gtest/gtest.h" + +namespace { + // For these tests we use a simple word type. + using WordType = uint8_t; + // We assume that bit::bitsof() returns the number of bits in a WordType. +} + +// ---------- Dynamic Extent Tests ---------- + +TEST(BitSpanTest, DefaultConstructorDynamic) { + bit::bit_span span; + EXPECT_EQ(span.size(), 0u); + EXPECT_TRUE(span.empty()); +} + +TEST(BitSpanTest, ConstructorFromBitPointerDynamic) { + WordType word = 0; + bit::bit_pointer ptr(&word, 0); + bit::bit_span span(ptr, bit::bitsof()); + EXPECT_EQ(span.size(), bit::bitsof()); + EXPECT_FALSE(span.empty()); +} + +TEST(BitSpanTest, ConstructorDeductionWordType) { + uint32_t word = 0xDEADBEEF; + bit::bit_span span(word); + EXPECT_EQ(span.size(), 32); + //0xF + EXPECT_EQ(span[0], bit::bit1); + EXPECT_EQ(span[1], bit::bit1); + EXPECT_EQ(span[2], bit::bit1); + EXPECT_EQ(span[3], bit::bit1); + //0xE + EXPECT_EQ(span[4], bit::bit0); + EXPECT_EQ(span[5], bit::bit1); + EXPECT_EQ(span[6], bit::bit1); + EXPECT_EQ(span[7], bit::bit1); + //0xE + EXPECT_EQ(span[8], bit::bit0); + EXPECT_EQ(span[9], bit::bit1); + EXPECT_EQ(span[10], bit::bit1); + EXPECT_EQ(span[11], bit::bit1); + //0xB + EXPECT_EQ(span[12], bit::bit1); + EXPECT_EQ(span[13], bit::bit1); + EXPECT_EQ(span[14], bit::bit0); + EXPECT_EQ(span[15], bit::bit1); +} + +TEST(BitSpanTest, ConstructorFromWordPointerDynamic) { + WordType word = 0; + // One word gives bit::bitsof() bits. + bit::bit_span span(&word, 1); + EXPECT_EQ(span.size(), 1); +} + +TEST(BitSpanTest, ElementAccessDynamic) { + WordType word = 0; + bit::bit_span span(&word, 1); + // Initially, all bits are bit::bit0. + EXPECT_EQ(span.front(), bit::bit0); + EXPECT_EQ(span.back(), bit::bit0); + + // Modify first bit. + span[0] = bit::bit1; + EXPECT_EQ(span.front(), bit::bit1); + EXPECT_EQ(span[0], bit::bit1); + + // Modify last bit. + span[bit::bitsof() - 1] = bit::bit1; + EXPECT_EQ(span.back(), bit::bit1); +} + +TEST(BitSpanTest, IteratorsDynamic) { + WordType word = 0; + bit::bit_span span(&word, 1); + + // Set a pattern: even indices = bit::bit1, odd indices = bit::bit0. + for (std::size_t i = 0; i < span.size(); ++i) { + span[i] = (i % 2 == 0) ? bit::bit1 : bit::bit0; + } + + std::size_t idx = 0; + for (auto it = span.begin(); it != span.end(); ++it, ++idx) { + if (idx % 2 == 0) + EXPECT_EQ(*it, bit::bit1); + else + EXPECT_EQ(*it, bit::bit0); + } + EXPECT_EQ(idx, span.size()); +} + +TEST(BitSpanTest, SubspanDynamic) { + WordType word = 0; + bit::bit_span span(&word, 1); + + // Set a known pattern: 1,0,1,0,... + for (std::size_t i = 0; i < span.size(); ++i) { + span[i] = (i % 2 == 0) ? bit::bit1 : bit::bit0; + } + // Create a subspan starting at index 2 with 4 bits. + auto sub = span.subspan(2, 4); + EXPECT_EQ(sub.size(), 4u); + for (std::size_t i = 0; i < sub.size(); ++i) { + EXPECT_EQ(sub[i], span[i + 2]); + } +} + +TEST(BitSpanTest, SubspanDefaultCountDynamic) { + WordType word = 0; + bit::bit_span span(&word, bit::bitsof(word)); + + // Fill span with a simple pattern. + for (std::size_t i = 0; i < span.size(); ++i) { + span[i] = (i % 2 == 0) ? bit::bit1 : bit::bit0; + } + auto sub = span.subspan(3); + EXPECT_EQ(sub.size(), span.size() - 3); + for (std::size_t i = 0; i < sub.size(); ++i) { + EXPECT_EQ(sub[i], span[i + 3]); + } +} + +TEST(BitSpanTest, ConstSpanDynamic) { + WordType word = 0; + bit::bit_span span(&word, 1); + span[0] = bit::bit1; + const bit::bit_span& cspan = span; + EXPECT_EQ(cspan[0], bit::bit1); + EXPECT_EQ(cspan.front(), bit::bit1); + EXPECT_EQ(cspan.back(), bit::bit1); +} + +TEST(BitSpanTest, CopyConstructionDynamic) { + WordType word = 0; + bit::bit_span span1(&word, 1); + span1[0] = bit::bit1; + bit::bit_span span2 = span1; + EXPECT_EQ(span2.size(), span1.size()); + EXPECT_EQ(span2[0], bit::bit1); +} + +// ---------- Fixed Extent Tests ---------- + +TEST(BitSpanTest, DefaultConstructorFixed) { + // For a fixed extent, the size is known at compile time. + bit::bit_span()> span; + EXPECT_EQ(span.size(), bit::bitsof()); + // Do not dereference the span if data is null. +} + +TEST(BitSpanTest, ConstructorFromWordPointerFixed) { + WordType word = 0; + // For a fixed extent, one word is required. + bit::bit_span()> span(&word); + EXPECT_EQ(span.size(), bit::bitsof()); +} + +TEST(BitSpanTest, ElementAccessFixed) { + WordType word = 0; + bit::bit_span()> span(&word); + // Initially, all bits should be bit::bit0. + EXPECT_EQ(span.front(), bit::bit0); + EXPECT_EQ(span.back(), bit::bit0); + + // Modify a bit and check. + span[3] = bit::bit1; + EXPECT_EQ(span[3], bit::bit1); +} + +TEST(BitSpanTest, IteratorsFixed) { + WordType word = 0; + bit::bit_span()> span(&word); + + // Set a pattern: set bit at index 1 and 5. + span[1] = bit::bit1; + span[5] = bit::bit1; + + std::size_t idx = 0; + for (auto it = span.begin(); it != span.end(); ++it, ++idx) { + if (idx == 1 || idx == 5) + EXPECT_EQ(*it, bit::bit1); + else + EXPECT_EQ(*it, bit::bit0); + } + EXPECT_EQ(idx, bit::bitsof()); +} + +TEST(BitSpanTest, CopyConstructionFixed) { + WordType word = 0; + bit::bit_span()> span1(&word); + span1[2] = bit::bit1; + bit::bit_span()> span2 = span1; + EXPECT_EQ(span2.size(), bit::bitsof()); + EXPECT_EQ(span2[2], bit::bit1); +} + +TEST(BitSpanTest, Slice) { + uint32_t word = 0xDEADBEEF; + bit::bit_span span1(word); + auto span2 = span1(4, 8); + EXPECT_EQ(span2.size(), 4); + EXPECT_EQ(span2[0], (0xE & (1 << 0)) ? bit::bit1 : bit::bit0); + EXPECT_EQ(span2[1], (0xE & (1 << 1)) ? bit::bit1 : bit::bit0); + EXPECT_EQ(span2[2], (0xE & (1 << 2)) ? bit::bit1 : bit::bit0); + EXPECT_EQ(span2[3], (0xE & (1 << 3)) ? bit::bit1 : bit::bit0); +} + +// ---------- Const Reference Alias Tests ---------- + +TEST(BitSpanTest, ConstReferenceAlias) { + WordType word = 0; + bit::bit_span span(&word, 1); + span[0] = bit::bit1; + + // Create a const view. + const bit::bit_span& cspan = span; + + // Use a static_assert to verify that the type of cspan[0] + // is the same as the defined const_reference. + static_assert(std::is_same_v::const_reference>, + "cspan[0] is not of type const_reference"); + + // At runtime, verify the value is as expected. + EXPECT_EQ(cspan[0], bit::bit1); + + // The following assignment should fail to compile if uncommented, + // which confirms that the const_reference is read-only. + // cspan[0] = bit::bit0; +} diff --git a/test/src/test-to_from_string.cpp b/test/src/test-to_from_string.cpp new file mode 100644 index 00000000..b76ff380 --- /dev/null +++ b/test/src/test-to_from_string.cpp @@ -0,0 +1,111 @@ +#include +#include +#include +#include + +#include "bitlib/bit-algorithms/count.hpp" +#include "bitlib/bit-algorithms/to_from_string.hpp" +#include "bitlib/bit-containers/bit_array.hpp" +#include "bitlib/bit-containers/bit_array_dynamic_extent.hpp" +#include "fixtures.hpp" + +// Third-party libraries +#include "gtest/gtest.h" + +TEST(ToString, Blah) { + auto num = 0x20'DEADBEEF_b; + EXPECT_EQ(static_cast(num), 0xDEADBEEF); + auto str = bit::to_string(num); + EXPECT_EQ(str, "DEADBEEF"); + num = 0x20'0EADBEEF_b; + str = bit::to_string(num); + EXPECT_EQ(str, "EADBEEF"); + str = bit::to_string(num); + EXPECT_EQ(str, "0EADBEEF"); + auto num2 = 0x23'10EADBEEF_b; + EXPECT_EQ(num2.size(), 35); + str = bit::to_string(num2); + EXPECT_EQ(str, "10EADBEEF"); +} + +TEST(ToString, base10) { + auto num = 10'123_b; + auto str = bit::to_string(num); + EXPECT_EQ(str, "123"); +} + +TEST(ToString, Streaming) { + std::stringstream sstr; + auto num = 10'123_b; + sstr << num; + EXPECT_EQ(sstr.str(), "123"); +} + +TEST(FromString, Blah) { + bit::bit_array<16> arr_16; + bit::from_string("DEADBEEF", arr_16); + EXPECT_EQ(arr_16, 0x10'BEEF_b); + bit::bit_array<18> arr_18; + bit::from_string("BEEF", arr_18); + EXPECT_EQ(arr_18, 0x12'0BEEF_b); + bit::from_string("123", arr_16); + EXPECT_EQ(arr_16, 16'123_b); +} + +TEST(FromString, IntoBookendRange) { + for (const auto word : get_random_vec(64)) { + for (int i = 1; i < 8; i++) { + auto bits = bit::bit_array<32>(word); + auto bits2 = bits; + bit::from_string( + "123ABC", bits(i, i + 4 * 6)); + bits2(i, i + 4 * 6) = 0x18'123ABC_b; + EXPECT_EQ(bits, bits2); + } + for (int i = 1; i < 8; i++) { + auto bits = bit::bit_array<32>(word); + auto bits2 = bits; + bit::from_string( + "F0000F", bits(i, i + 4 * 6)); + bits2(i, i + 4 * 6) = 0x18'F0000F_b; + EXPECT_EQ(bits, bits2); + } + } +} + +TEST(FromString, IntoBookendRangeOverflow) { + for (int i = 1; i < 8; i++) { + auto bits = 0x20'DEADBEEF_b; + auto bits2 = bits; + bit::from_string( + "F123ABC", bits(i, i + 4 * 6)); + bits2(i, i + 4 * 6) = 0x18'123ABC_b; + EXPECT_EQ(bits, bits2); + } + for (int i = 1; i < 8; i++) { + auto bits = 0x20'00000000_b; + auto bits2 = bits; + bit::from_string( + "F123ABC", bits(i, i + 4 * 6)); + bits2(i, i + 4 * 6) = 0x18'123ABC_b; + EXPECT_EQ(bits, bits2); + } +} + +TEST(FromString, IntoBookendRangeLarge) { + for (int i = 0; i < 128; i++) { + for (auto b : {bit::bit0, bit::bit1}) { + auto bits = bit::bit_array<>(200 + i, b); + auto bits2 = bits; + auto setbits = bit::bit_array<>(200 + i, b ? bit::bit0 : bit::bit1); + auto str = bit::to_string(setbits); + for (int j = 64 / 4; j < 128 / 4; j++) { + for (int k = 0; k < 64; k++) { + bit::from_string(str.substr(str.length() - 1 - j, j), bits(k, k + j * 4)); + bits2(k, k + j * 4) = setbits(0, j * 4); + EXPECT_EQ(bits, bits2); + } + } + } + } +} \ No newline at end of file diff --git a/test/src/test-usecase.cpp b/test/src/test-usecase.cpp new file mode 100644 index 00000000..f5181982 --- /dev/null +++ b/test/src/test-usecase.cpp @@ -0,0 +1,91 @@ +#include "bitlib/bitlib.hpp" +// Third-party libraries +#include +#include + +#include "gtest/gtest.h" + +TEST(UseCaseTest, VectorAppend) { + bit::bit_vector vec; + uint16_t test = 0xBEEF; + vec.append_range(bit::bit_span(test)); + EXPECT_EQ(vec.size(), 16); + EXPECT_EQ(vec[0], bit::bit1); + EXPECT_EQ(vec[4], bit::bit0); + EXPECT_EQ(vec[8], bit::bit0); + EXPECT_EQ(vec[12], bit::bit1); + vec.push_back(bit::bit0); + EXPECT_EQ(vec.size(), 17); + EXPECT_EQ(vec[16], bit::bit0); + + uint32_t test32 = 0xABBADABA; + vec.append_range(bit::bit_span(test32)); + EXPECT_EQ(vec[17 + 0], bit::bit0); + EXPECT_EQ(vec[17 + 4], bit::bit1); + EXPECT_EQ(vec[17 + 8], bit::bit0); + EXPECT_EQ(vec[17 + 12], bit::bit1); + EXPECT_EQ(vec[17 + 16], bit::bit0); + EXPECT_EQ(vec[17 + 20], bit::bit1); + EXPECT_EQ(vec[17 + 24], bit::bit1); + EXPECT_EQ(vec[17 + 28], bit::bit0); +} + +TEST(UseCaseTest, RangeCopy) { + uint32_t test32 = 0xABBADABA; + auto bspan = bit::bit_span(test32); + static_assert(std::indirectly_writable>); + static_assert(std::ranges::input_range>); + static_assert(std::weakly_incrementable::iterator>); + static_assert(std::indirectly_readable>>); + + using BitVecIterator = bit::bit_vector::iterator; + using BitSpanIterator = std::ranges::iterator_t>; + using OtherRef = std::iter_reference_t; // the type used on the RHS + + // 1. Check that *it = t is valid. + static_assert( + requires(BitVecIterator out, OtherRef in) { + *out = in; + }, + "Requirement failed: *it = t must be a valid assignment."); + + // 2. Check that *it++ = t is valid. + static_assert( + requires(BitVecIterator out, OtherRef in) { + *out++ = in; + }, + "Requirement failed: *it++ = t must be a valid assignment."); + + // 3. Check that const_cast(*it) = t is valid. + + static_assert( + requires(BitVecIterator&& out, OtherRef&& in) { + const_cast&&>(*out) = std::forward(in); + }, + "Requirement failed: const_cast(*it) = t must be a valid assignment."); + + static_assert(std::indirectly_writable::iterator, std::iter_reference_t>>>); + /* + static_assert(std::indirectly_copyable>, bit::bit_vector::iterator>); + */ + + bit::bit_vector v(32); + std::ranges::copy(bspan, v.begin()); + EXPECT_EQ(v.size(), 32); + EXPECT_EQ(v[0], (0xABBADABA & (1 << 0)) ? bit::bit1 : bit::bit0); + EXPECT_EQ(v[1], (0xABBADABA & (1 << 1)) ? bit::bit1 : bit::bit0); + EXPECT_EQ(v[2], (0xABBADABA & (2 << 1)) ? bit::bit1 : bit::bit0); +} + +/* +c++23 +TEST(UseCaseTest, StartLifeAs) { + uint16_t test = 0xBEEF; + auto ba = std::start_lifetime_as>(&test); + EXPECT_EQ(ba.size(), 16); + EXPECT_EQ(ba[0], bit::bit1); + EXPECT_EQ(ba[4], bit::bit0); + EXPECT_EQ(ba[8], bit::bit0); + EXPECT_EQ(ba[12], bit::bit1); +} +*/ diff --git a/test/src/vector_test.cpp b/test/src/vector_test.cpp index 666b031d..6bf3b27a 100644 --- a/test/src/vector_test.cpp +++ b/test/src/vector_test.cpp @@ -1,6 +1,6 @@ // =============================== FIXTURES ================================= // // Project: The Experimental Bit Algorithms Library -// Description: Fixtures for testing +// Description: Fixtures for testing // Contributor(s): Bryce Kille // License: BSD 3-Clause License // ========================================================================== // @@ -28,110 +28,114 @@ TYPED_TEST(VectorTest, DefaultConstructor) { // Tests the size c'tor. TYPED_TEST(VectorTest, SizeInitializerConstructor) { - EXPECT_EQ(18, this->v2_.size()); - for (auto bv: this->v2_) { - EXPECT_FALSE(bv); - } - using WordType = typename TestFixture::base_type; - using vec_type = typename TestFixture::vec_type; - for (unsigned int veclen = 0; veclen < 4*this->digits; veclen++) { - this->empty_vec = vec_type(veclen); - for (unsigned int i = 0; i < veclen; ++i) { - EXPECT_FALSE(this->empty_vec[i]); - } - this->empty_vec = bit::bit_vector(veclen); - unsigned int i = 0; - // TODO misplaced test for range-based for loop - for (auto bv: this->empty_vec) { - EXPECT_FALSE(bv); - i++; - } - EXPECT_EQ(i, veclen); - } + EXPECT_EQ(18, this->v2_.size()); + for (auto bv : this->v2_) { + EXPECT_FALSE(bv); + } + using WordType = typename TestFixture::base_type; + constexpr auto digits = bit::binary_digits::value; + using VecType = typename TestFixture::vec_type; + for (unsigned int veclen = 0; veclen < 4 * digits; veclen++) { + this->empty_vec = VecType(veclen); + for (unsigned int i = 0; i < veclen; ++i) { + EXPECT_FALSE(this->empty_vec[i]); + } + this->empty_vec = bit::bit_vector(veclen); + unsigned int i = 0; + // TODO misplaced test for range-based for loop + for (auto bv : this->empty_vec) { + EXPECT_FALSE(bv); + i++; + } + EXPECT_EQ(i, veclen); + } } TYPED_TEST(VectorTest, CountValueConstructor) { - using vec_type = typename TestFixture::vec_type; - for (unsigned int veclen = 0; veclen < 4*this->digits; veclen++) { - this->empty_vec = vec_type(veclen, bit::bit1); - unsigned int i = 0; - // TODO misplaced test for range-based for loop - for (auto bv: this->empty_vec) { - EXPECT_TRUE(bv); - i++; - } - EXPECT_EQ(i, veclen); - this->empty_vec = vec_type(veclen, bit::bit0); - i = 0; - // TODO misplaced test for range-based for loop - for (auto bv: this->empty_vec) { - EXPECT_FALSE(bv); - i++; - } - EXPECT_EQ(i, veclen); - } + using VecType = typename TestFixture::vec_type; + using WordType = typename TestFixture::base_type; + constexpr auto digits = bit::binary_digits::value; + for (unsigned int veclen = 0; veclen < 4 * digits; veclen++) { + this->empty_vec = VecType(veclen, bit::bit1); + unsigned int i = 0; + // TODO misplaced test for range-based for loop + for (auto bv : this->empty_vec) { + EXPECT_TRUE(bv); + i++; + } + EXPECT_EQ(i, veclen); + this->empty_vec = VecType(veclen, bit::bit0); + i = 0; + // TODO misplaced test for range-based for loop + for (auto bv : this->empty_vec) { + EXPECT_FALSE(bv); + i++; + } + EXPECT_EQ(i, veclen); + } } // Test when first, last are WordType iterators TYPED_TEST(VectorTest, WordIterPairConstructor) { using WordType = typename TestFixture::base_type; - using vec_type = typename TestFixture::vec_type; + using VecType = typename TestFixture::vec_type; using iterator_type = typename std::vector::iterator; - for (unsigned int veclen = 0; veclen < 4*this->digits; veclen++) { - std::vector word_vec = get_random_vec(veclen); - vec_type test(word_vec.begin(), word_vec.end()); - EXPECT_TRUE(std::equal( - test.begin(), - test.end(), - bit::bit_iterator(word_vec.begin()), - bit::bit_iterator(word_vec.end()))); + constexpr auto digits = bit::binary_digits::value; + for (unsigned int veclen = 0; veclen < 4 * digits; veclen++) { + std::vector word_vec = get_random_vec(veclen); + VecType test(word_vec.begin(), word_vec.end()); + EXPECT_TRUE(std::equal( + test.begin(), + test.end(), + bit::bit_iterator(word_vec.begin()), + bit::bit_iterator(word_vec.end()))); } } // Test when first, last are bool iterators TYPED_TEST(VectorTest, BoolIterPairConstructor) { - using vec_type = typename TestFixture::vec_type; - for (unsigned int vec_idx = 0; vec_idx < this->random_bitvecs.size(); ++vec_idx) { - auto& boolvec = this->random_boolvecs[vec_idx]; - vec_type test = vec_type(boolvec.begin(), boolvec.end()); - EXPECT_TRUE(std::equal( - test.begin(), - test.end(), - boolvec.begin(), - boolvec.end(), - comparator)); - } + using VecType = typename TestFixture::vec_type; + for (unsigned int vec_idx = 0; vec_idx < this->random_bitvecs.size(); ++vec_idx) { + auto& boolvec = this->random_boolvecs[vec_idx]; + VecType test = VecType(boolvec.begin(), boolvec.end()); + EXPECT_TRUE(std::equal( + test.begin(), + test.end(), + boolvec.begin(), + boolvec.end(), + comparator)); + } } // Test copy ctor TYPED_TEST(VectorTest, CopyConstructor) { - using vec_type = typename TestFixture::vec_type; - for (unsigned int vec_idx = 0; vec_idx < this->random_bitvecs.size(); ++vec_idx) { - auto& bitvec = this->random_bitvecs[vec_idx]; - auto& boolvec = this->random_boolvecs[vec_idx]; - vec_type test = vec_type(bitvec); - EXPECT_TRUE(std::equal( - test.begin(), - test.end(), - boolvec.begin(), - boolvec.end(), - comparator)); - } + using VecType = typename TestFixture::vec_type; + for (unsigned int vec_idx = 0; vec_idx < this->random_bitvecs.size(); ++vec_idx) { + auto& bitvec = this->random_bitvecs[vec_idx]; + auto& boolvec = this->random_boolvecs[vec_idx]; + VecType test = VecType(bitvec); + EXPECT_TRUE(std::equal( + test.begin(), + test.end(), + boolvec.begin(), + boolvec.end(), + comparator)); + } } TYPED_TEST(VectorTest, MoveConstructor) { - using vec_type = typename TestFixture::vec_type; - for (unsigned int vec_idx = 0; vec_idx < this->random_bitvecs.size(); ++vec_idx) { - auto& bitvec = this->random_bitvecs[vec_idx]; - auto& boolvec = this->random_boolvecs[vec_idx]; - vec_type test = vec_type(std::move(bitvec)); - EXPECT_TRUE(std::equal( - test.begin(), - test.end(), - boolvec.begin(), - boolvec.end(), - comparator)); - } + using VecType = typename TestFixture::vec_type; + for (unsigned int vec_idx = 0; vec_idx < this->random_bitvecs.size(); ++vec_idx) { + auto& bitvec = this->random_bitvecs[vec_idx]; + auto& boolvec = this->random_boolvecs[vec_idx]; + VecType test = VecType(std::move(bitvec)); + EXPECT_TRUE(std::equal( + test.begin(), + test.end(), + boolvec.begin(), + boolvec.end(), + comparator)); + } } // Tests the string c'tor. @@ -140,20 +144,21 @@ TYPED_TEST(VectorTest, StringConstructor) { EXPECT_EQ(false, static_cast(this->v3_[0])); EXPECT_EQ(true, static_cast(this->v3_[8])); using WordType = typename TestFixture::base_type; - for (unsigned int strlen = 0; strlen < 4*this->digits; strlen++) { - std::string rand_bs(strlen, 0); - this->empty_vec_bool.clear(); - for (auto& pos: rand_bs) { - pos = generate_random_number('0', '1'); - this->empty_vec_bool.push_back(pos == '1'); - } - this->empty_vec = bit::bit_vector(rand_bs); - EXPECT_TRUE(std::equal( - this->empty_vec.begin(), - this->empty_vec.end(), - this->empty_vec_bool.begin(), - this->empty_vec_bool.end(), - comparator)); + constexpr auto digits = bit::binary_digits::value; + for (unsigned int strlen = 0; strlen < 4 * digits; strlen++) { + std::string rand_bs(strlen, 0); + this->empty_vec_bool.clear(); + for (auto& pos : rand_bs) { + pos = static_cast(generate_random_number('0', '1')); + this->empty_vec_bool.push_back(pos == '1'); + } + this->empty_vec = bit::bit_vector(rand_bs); + EXPECT_TRUE(std::equal( + this->empty_vec.begin(), + this->empty_vec.end(), + this->empty_vec_bool.begin(), + this->empty_vec_bool.end(), + comparator)); } } @@ -161,7 +166,7 @@ TYPED_TEST(VectorTest, StringConstructor) { TYPED_TEST(VectorTest, CopyAssignment) { for (auto _ = 0; _ < 128; ++_) { this->empty_vec = this->v2_; - EXPECT_NE(this->empty_vec.begin().base(), this->v2_.begin().base()); + EXPECT_NE(std::to_address(this->empty_vec.begin().base()), std::to_address(this->v2_.begin().base())); EXPECT_TRUE(std::equal(this->empty_vec.begin(), this->empty_vec.end(), this->v2_.begin())); EXPECT_EQ(this->empty_vec.size(), this->v2_.size()); this->v2_.insert(this->v2_.end(), bit::bit1); @@ -169,59 +174,58 @@ TYPED_TEST(VectorTest, CopyAssignment) { this->v2_.insert(this->v2_.end(), bit::bit1); } for (unsigned int vec_idx = 0; vec_idx < this->random_bitvecs.size(); ++vec_idx) { - auto& bitvec = this->random_bitvecs[vec_idx]; - auto& boolvec = this->random_boolvecs[vec_idx]; - this->empty_vec = bitvec; - EXPECT_NE(this->empty_vec.begin().base(), bitvec.begin().base()); - EXPECT_TRUE(std::equal( - this->empty_vec.begin(), - this->empty_vec.end(), - boolvec.begin(), - boolvec.end(), - comparator)); - if (bitvec.size() > 0) { - bitvec[0] = ~bitvec[0]; - EXPECT_NE(bitvec[0], this->empty_vec[0]); - } + auto& bitvec = this->random_bitvecs[vec_idx]; + auto& boolvec = this->random_boolvecs[vec_idx]; + this->empty_vec = bitvec; + EXPECT_NE(std::to_address(this->empty_vec.begin().base()), std::to_address(bitvec.begin().base())); + EXPECT_TRUE(std::equal( + this->empty_vec.begin(), + this->empty_vec.end(), + boolvec.begin(), + boolvec.end(), + comparator)); + if (bitvec.size() > 0) { + bitvec[0] = ~bitvec[0]; + EXPECT_NE(bitvec[0], this->empty_vec[0]); + } } } // Test the move c'tor TYPED_TEST(VectorTest, MoveAssignment) { - bit::bit_vector v2_copy = this->v2_; - auto v2_data = this->v2_.begin().base(); - this->empty_vec = std::move(this->v2_); - EXPECT_EQ(this->empty_vec.begin().base(), v2_data); - EXPECT_TRUE(std::equal(this->empty_vec.begin(), this->empty_vec.end(), v2_copy.begin())); - EXPECT_EQ(this->empty_vec.size(), v2_copy.size()); - for (unsigned int vec_idx = 0; vec_idx < this->random_bitvecs.size(); ++vec_idx) { - auto& bitvec = this->random_bitvecs[vec_idx]; - auto& boolvec = this->random_boolvecs[vec_idx]; - auto bitvec_base = bitvec.begin().base(); - this->empty_vec = std::move(bitvec); - EXPECT_EQ(this->empty_vec.begin().base(), bitvec_base); - EXPECT_TRUE(std::equal( - this->empty_vec.begin(), - this->empty_vec.end(), - boolvec.begin(), - boolvec.end(), - comparator)); - } + bit::bit_vector v2_copy = this->v2_; + auto v2_data = this->v2_.begin().base(); + this->empty_vec = std::move(this->v2_); + EXPECT_EQ(std::to_address(this->empty_vec.begin().base()), std::to_address(v2_data)); + EXPECT_TRUE(std::equal(this->empty_vec.begin(), this->empty_vec.end(), v2_copy.begin())); + EXPECT_EQ(this->empty_vec.size(), v2_copy.size()); + for (unsigned int vec_idx = 0; vec_idx < this->random_bitvecs.size(); ++vec_idx) { + auto& bitvec = this->random_bitvecs[vec_idx]; + auto& boolvec = this->random_boolvecs[vec_idx]; + auto bitvec_base = bitvec.begin().base(); + this->empty_vec = std::move(bitvec); + EXPECT_EQ(std::to_address(this->empty_vec.begin().base()), std::to_address(bitvec_base)); + EXPECT_TRUE(std::equal( + this->empty_vec.begin(), + this->empty_vec.end(), + boolvec.begin(), + boolvec.end(), + comparator)); + } } - // Test the initializer list c'tor TYPED_TEST(VectorTest, InitializerListConstructor) { - bit::bit_vector v2_copy = this->v2_; - using vec_type = typename TestFixture::vec_type; - std::vector boolvec {true, false, true, true, true, false, false, true, false, true, true, false}; - vec_type test {true, false, true, true, true, false, false, true, false, true, true, false}; - EXPECT_TRUE(std::equal( - test.begin(), - test.end(), - boolvec.begin(), - boolvec.end(), - comparator)); + bit::bit_vector v2_copy = this->v2_; + using VecType = typename TestFixture::vec_type; + std::vector boolvec{true, false, true, true, true, false, false, true, false, true, true, false}; + VecType test{true, false, true, true, true, false, false, true, false, true, true, false}; + EXPECT_TRUE(std::equal( + test.begin(), + test.end(), + boolvec.begin(), + boolvec.end(), + comparator)); } /* @@ -230,104 +234,106 @@ TYPED_TEST(VectorTest, InitializerListConstructor) { // Test read TYPED_TEST(VectorTest, BracketRead) { - EXPECT_EQ(this->v3_[0], bit::bit0); - for (unsigned int vec_idx = 0; vec_idx < this->random_bitvecs.size(); ++vec_idx) { - auto& bitvec = this->random_bitvecs[vec_idx]; - auto& boolvec = this->random_boolvecs[vec_idx]; - for (unsigned int i = 0; i < boolvec.size(); i++) { - EXPECT_TRUE(comparator(bitvec[i], boolvec[i])); - } + EXPECT_EQ(this->v3_[0], bit::bit0); + for (unsigned int vec_idx = 0; vec_idx < this->random_bitvecs.size(); ++vec_idx) { + auto& bitvec = this->random_bitvecs[vec_idx]; + auto& boolvec = this->random_boolvecs[vec_idx]; + for (unsigned int i = 0; i < boolvec.size(); i++) { + EXPECT_TRUE(comparator(bitvec[i], boolvec[i])); } + } } // Test write TYPED_TEST(VectorTest, BracketWrite) { - this->v3_[0] = bit::bit1; - EXPECT_EQ(this->v3_[0], bit::bit1); - for (unsigned int vec_idx = 0; vec_idx < this->random_bitvecs.size(); ++vec_idx) { - auto& bitvec = this->random_bitvecs[vec_idx]; - auto& boolvec = this->random_boolvecs[vec_idx]; - for (unsigned int i = 0; i < boolvec.size(); i++) { - bitvec[i] = ~bitvec[i]; - boolvec[i] = !boolvec[i]; - } - EXPECT_TRUE(std::equal( - bitvec.begin(), - bitvec.end(), - boolvec.begin(), - boolvec.end(), - comparator)); -} + this->v3_[0] = bit::bit1; + EXPECT_EQ(this->v3_[0], bit::bit1); + for (unsigned int vec_idx = 0; vec_idx < this->random_bitvecs.size(); ++vec_idx) { + auto& bitvec = this->random_bitvecs[vec_idx]; + auto& boolvec = this->random_boolvecs[vec_idx]; + for (unsigned int i = 0; i < boolvec.size(); i++) { + bitvec[i] = ~bitvec[i]; + boolvec[i] = !boolvec[i]; + } + EXPECT_TRUE(std::equal( + bitvec.begin(), + bitvec.end(), + boolvec.begin(), + boolvec.end(), + comparator)); + } } // Test at TYPED_TEST(VectorTest, AtRead) { - EXPECT_EQ(this->v3_.at(0), bit::bit0); - EXPECT_EQ(this->v3_.at(8), bit::bit1); - for (unsigned int vec_idx = 0; vec_idx < this->random_bitvecs.size(); ++vec_idx) { - auto& bitvec = this->random_bitvecs[vec_idx]; - auto& boolvec = this->random_boolvecs[vec_idx]; - for (unsigned int i = 0; i < boolvec.size(); i++) { - EXPECT_TRUE(comparator(bitvec.at(i), boolvec.at(i))); - } - for (unsigned int i = boolvec.size(); i < boolvec.size() + 4*this->digits; i++) { - EXPECT_THROW(bitvec.at(i), std::out_of_range); - } + using WordType = typename TestFixture::base_type; + constexpr auto digits = bit::binary_digits::value; + EXPECT_EQ(this->v3_.at(0), bit::bit0); + EXPECT_EQ(this->v3_.at(8), bit::bit1); + for (size_t vec_idx = 0; vec_idx < this->random_bitvecs.size(); ++vec_idx) { + auto& bitvec = this->random_bitvecs[vec_idx]; + auto& boolvec = this->random_boolvecs[vec_idx]; + for (size_t i = 0; i < boolvec.size(); i++) { + EXPECT_TRUE(comparator(bitvec.at(i), boolvec.at(i))); + } + for (size_t i = boolvec.size(); i < boolvec.size() + 4 * digits; i++) { + EXPECT_THROW(bitvec.at(i), std::out_of_range); } + } } - /* * Iterators */ //TODO - /* * Capacity */ // Test empty TYPED_TEST(VectorTest, Empty) { - EXPECT_TRUE(this->empty_vec.empty()); - EXPECT_FALSE(this->v2_.empty()); - this->v2_.clear(); - EXPECT_TRUE(this->v2_.empty()); + EXPECT_TRUE(this->empty_vec.empty()); + EXPECT_FALSE(this->v2_.empty()); + this->v2_.clear(); + EXPECT_TRUE(this->v2_.empty()); } // Test size TYPED_TEST(VectorTest, Size) { - EXPECT_EQ(this->empty_vec.size(), 0); - EXPECT_EQ(this->v2_.size(), 18); - EXPECT_EQ(this->v3_.size(), 9); + EXPECT_EQ(this->empty_vec.size(), 0); + EXPECT_EQ(this->v2_.size(), 18); + EXPECT_EQ(this->v3_.size(), 9); } // Test reserve and capacity TYPED_TEST(VectorTest, ReserveAndCapacity) { - this->empty_vec.reserve(1); - EXPECT_GE(this->empty_vec.capacity(), 1); - this->empty_vec.reserve(9); - EXPECT_GE(this->empty_vec.capacity(), 9); - this->empty_vec.reserve(64); - EXPECT_GE(this->empty_vec.capacity(), 64); + this->empty_vec.reserve(1); + EXPECT_GE(this->empty_vec.capacity(), 1); + this->empty_vec.reserve(9); + EXPECT_GE(this->empty_vec.capacity(), 9); + this->empty_vec.reserve(64); + EXPECT_GE(this->empty_vec.capacity(), 64); } // Test shrink_to_fit TYPED_TEST(VectorTest, ShrinkToFit) { - this->empty_vec.shrink_to_fit(); - EXPECT_EQ(this->empty_vec.capacity(), 0); - this->empty_vec.reserve(12345); - this->empty_vec.shrink_to_fit(); - EXPECT_EQ(this->empty_vec.capacity(), 0); - for (unsigned int vec_idx = 0; vec_idx < this->random_bitvecs.size(); ++vec_idx) { - auto& bitvec = this->random_bitvecs[vec_idx]; - for (unsigned int _ = 0; _ < this->digits; _++) { - bitvec.pop_back(); - } - auto old_cap = bitvec.capacity(); - bitvec.shrink_to_fit(); - EXPECT_LT(bitvec.capacity(), old_cap); - } + using WordType = typename TestFixture::base_type; + constexpr auto digits = bit::binary_digits::value; + this->empty_vec.shrink_to_fit(); + EXPECT_EQ(this->empty_vec.capacity(), 0); + this->empty_vec.reserve(12345); + this->empty_vec.shrink_to_fit(); + EXPECT_EQ(this->empty_vec.capacity(), 0); + for (unsigned int vec_idx = 0; vec_idx < this->random_bitvecs.size(); ++vec_idx) { + auto& bitvec = this->random_bitvecs[vec_idx]; + for (unsigned int _ = 0; _ < digits; _++) { + bitvec.pop_back(); + } + auto old_cap = bitvec.capacity(); + bitvec.shrink_to_fit(); + EXPECT_LT(bitvec.capacity(), old_cap); + } } /* @@ -336,287 +342,304 @@ TYPED_TEST(VectorTest, ShrinkToFit) { // Test clear TYPED_TEST(VectorTest, Clear) { - this->empty_vec.clear(); - EXPECT_EQ(this->empty_vec.size(), 0); - this->empty_vec.reserve(1); - EXPECT_EQ(this->empty_vec.size(), 0); - this->empty_vec.clear(); - EXPECT_EQ(this->empty_vec.size(), 0); - this->v2_.clear(); - EXPECT_EQ(this->v2_.size(), 0); - this->v3_.clear(); - EXPECT_EQ(this->v3_.size(), 0); - for (unsigned int vec_idx = 0; vec_idx < this->random_bitvecs.size(); ++vec_idx) { - auto& bitvec = this->random_bitvecs[vec_idx]; - bitvec.clear(); - EXPECT_EQ(bitvec.size(), 0); - EXPECT_THROW(bitvec.at(0), std::out_of_range); - } + this->empty_vec.clear(); + EXPECT_EQ(this->empty_vec.size(), 0); + this->empty_vec.reserve(1); + EXPECT_EQ(this->empty_vec.size(), 0); + this->empty_vec.clear(); + EXPECT_EQ(this->empty_vec.size(), 0); + this->v2_.clear(); + EXPECT_EQ(this->v2_.size(), 0); + this->v3_.clear(); + EXPECT_EQ(this->v3_.size(), 0); + for (unsigned int vec_idx = 0; vec_idx < this->random_bitvecs.size(); ++vec_idx) { + auto& bitvec = this->random_bitvecs[vec_idx]; + bitvec.clear(); + EXPECT_EQ(bitvec.size(), 0); + EXPECT_THROW(bitvec.at(0), std::out_of_range); + } } // Test insert TYPED_TEST(VectorTest, InsertAtEnd1) { - // First signature - for (auto _ = 64*this->digits; _--;) { - bool to_insert_bool = generate_random_number(0, 1) > 0 ? true : false; - bit::bit_value to_insert_bit = to_insert_bool ? bit::bit1 : bit::bit0; - auto bitret = this->empty_vec.insert( - this->empty_vec.end(), - to_insert_bit); - auto boolret = this->empty_vec_bool.insert( - this->empty_vec_bool.end(), - to_insert_bool); - EXPECT_TRUE(std::equal( - this->empty_vec.begin(), - this->empty_vec.end(), - this->empty_vec_bool.begin(), - this->empty_vec_bool.end(), - comparator)); - EXPECT_EQ( - std::distance(this->empty_vec.begin(), bitret), - std::distance(this->empty_vec_bool.begin(), boolret)); - } + using WordType = typename TestFixture::base_type; + constexpr auto digits = bit::binary_digits::value; + // First signature + for (auto _ = 64 * digits; _--;) { + bool to_insert_bool = generate_random_number(0, 1) > 0 ? true : false; + bit::bit_value to_insert_bit = to_insert_bool ? bit::bit1 : bit::bit0; + auto bitret = this->empty_vec.insert( + this->empty_vec.end(), + to_insert_bit); + auto boolret = this->empty_vec_bool.insert( + this->empty_vec_bool.end(), + to_insert_bool); + EXPECT_TRUE(std::equal( + this->empty_vec.begin(), + this->empty_vec.end(), + this->empty_vec_bool.begin(), + this->empty_vec_bool.end(), + comparator)); + EXPECT_EQ( + std::distance(this->empty_vec.begin(), bitret), + std::distance(this->empty_vec_bool.begin(), boolret)); + } } TYPED_TEST(VectorTest, InsertAtBegin1) { - // First signature - for (auto _ = 64*this->digits; _--;) { - bool to_insert_bool = generate_random_number(0, 1) > 0 ? true : false; - bit::bit_value to_insert_bit = to_insert_bool ? bit::bit1 : bit::bit0; - auto bitret = this->empty_vec.insert( - this->empty_vec.begin(), - to_insert_bit); - auto boolret = this->empty_vec_bool.insert( - this->empty_vec_bool.begin(), - to_insert_bool); - EXPECT_TRUE(std::equal( - this->empty_vec.begin(), - this->empty_vec.end(), - this->empty_vec_bool.begin(), - this->empty_vec_bool.end(), - comparator)); - EXPECT_EQ( - std::distance(this->empty_vec.begin(), bitret), - std::distance(this->empty_vec_bool.begin(), boolret)); - } + using WordType = typename TestFixture::base_type; + constexpr auto digits = bit::binary_digits::value; + // First signature + for (auto _ = 64 * digits; _--;) { + bool to_insert_bool = generate_random_number(0, 1) > 0 ? true : false; + bit::bit_value to_insert_bit = to_insert_bool ? bit::bit1 : bit::bit0; + auto bitret = this->empty_vec.insert( + this->empty_vec.begin(), + to_insert_bit); + auto boolret = this->empty_vec_bool.insert( + this->empty_vec_bool.begin(), + to_insert_bool); + EXPECT_TRUE(std::equal( + this->empty_vec.begin(), + this->empty_vec.end(), + this->empty_vec_bool.begin(), + this->empty_vec_bool.end(), + comparator)); + EXPECT_EQ( + std::distance(this->empty_vec.begin(), bitret), + std::distance(this->empty_vec_bool.begin(), boolret)); + } } TYPED_TEST(VectorTest, InsertAtRand1) { - // First signature - for (auto _ = 64*this->digits; _--;) { - auto insert_location = generate_random_number(0, this->empty_vec.size()); - bool to_insert_bool = generate_random_number(0, 1) > 0 ? true : false; - bit::bit_value to_insert_bit = to_insert_bool ? bit::bit1 : bit::bit0; - auto bitret = this->empty_vec.insert( - this->empty_vec.begin() + insert_location, - to_insert_bit); - auto boolret = this->empty_vec_bool.insert( - this->empty_vec_bool.begin() + insert_location, - to_insert_bool); - EXPECT_TRUE(std::equal( - this->empty_vec.begin(), - this->empty_vec.end(), - this->empty_vec_bool.begin(), - this->empty_vec_bool.end(), - comparator)); - EXPECT_EQ( - std::distance(this->empty_vec.begin(), bitret), - std::distance(this->empty_vec_bool.begin(), boolret)); - } + using WordType = typename TestFixture::base_type; + constexpr auto digits = bit::binary_digits::value; + // First signature + for (auto _ = 64 * digits; _--;) { + auto insert_location = generate_random_number(0, this->empty_vec.size()); + bool to_insert_bool = generate_random_number(0, 1) > 0 ? true : false; + bit::bit_value to_insert_bit = to_insert_bool ? bit::bit1 : bit::bit0; + auto bitret = this->empty_vec.insert( + this->empty_vec.begin() + insert_location, + to_insert_bit); + auto boolret = this->empty_vec_bool.insert( + this->empty_vec_bool.begin() + insert_location, + to_insert_bool); + EXPECT_TRUE(std::equal( + this->empty_vec.begin(), + this->empty_vec.end(), + this->empty_vec_bool.begin(), + this->empty_vec_bool.end(), + comparator)); + EXPECT_EQ( + std::distance(this->empty_vec.begin(), bitret), + std::distance(this->empty_vec_bool.begin(), boolret)); + } } TYPED_TEST(VectorTest, InsertAtBegin2) { - // Second signature - for (auto _ = 16; _--;) { - auto to_insert = generate_random_number(0, 4*this->digits); - auto bitret = this->empty_vec.insert( - this->empty_vec.begin(), - to_insert, - bit::bit1); - auto boolret = this->empty_vec_bool.insert( - this->empty_vec_bool.begin(), - to_insert, - true); - EXPECT_TRUE(std::equal( - this->empty_vec.begin(), - this->empty_vec.end(), - this->empty_vec_bool.begin(), - this->empty_vec_bool.end(), - comparator)); - EXPECT_EQ( - std::distance(this->empty_vec.begin(), bitret), - std::distance(this->empty_vec_bool.begin(), boolret)); - } + using WordType = typename TestFixture::base_type; + constexpr auto digits = bit::binary_digits::value; + // Second signature + for (auto _ = 16; _--;) { + auto to_insert = generate_random_number(0, 4 * digits); + auto bitret = this->empty_vec.insert( + this->empty_vec.begin(), + to_insert, + bit::bit1); + auto boolret = this->empty_vec_bool.insert( + this->empty_vec_bool.begin(), + to_insert, + true); + EXPECT_TRUE(std::equal( + this->empty_vec.begin(), + this->empty_vec.end(), + this->empty_vec_bool.begin(), + this->empty_vec_bool.end(), + comparator)); + EXPECT_EQ( + std::distance(this->empty_vec.begin(), bitret), + std::distance(this->empty_vec_bool.begin(), boolret)); + } } TYPED_TEST(VectorTest, InsertAtEnd2) { - // Second signature - for (auto _ = 16; _--;) { - auto to_insert = generate_random_number(0, 444*this->digits); - auto bitret = this->empty_vec.insert( - this->empty_vec.end(), - to_insert, - bit::bit1); - auto boolret = this->empty_vec_bool.insert( - this->empty_vec_bool.end(), - to_insert, - true); - EXPECT_TRUE(std::equal( - this->empty_vec.begin(), - this->empty_vec.end(), - this->empty_vec_bool.begin(), - this->empty_vec_bool.end(), - comparator)); - EXPECT_EQ( - std::distance(this->empty_vec.begin(), bitret), - std::distance(this->empty_vec_bool.begin(), boolret)); - } + using WordType = typename TestFixture::base_type; + constexpr auto digits = bit::binary_digits::value; + // Second signature + for (auto _ = 16; _--;) { + auto to_insert = generate_random_number(0, 444 * digits); + auto bitret = this->empty_vec.insert( + this->empty_vec.end(), + to_insert, + bit::bit1); + auto boolret = this->empty_vec_bool.insert( + this->empty_vec_bool.end(), + to_insert, + true); + EXPECT_TRUE(std::equal( + this->empty_vec.begin(), + this->empty_vec.end(), + this->empty_vec_bool.begin(), + this->empty_vec_bool.end(), + comparator)); + EXPECT_EQ( + std::distance(this->empty_vec.begin(), bitret), + std::distance(this->empty_vec_bool.begin(), boolret)); + } } TYPED_TEST(VectorTest, InsertAtRand2) { - // Second signature - for (auto _ = 16; _--;) { - auto to_insert = generate_random_number(0, 4*this->digits); - auto insert_location = generate_random_number(0, this->empty_vec.size()); - auto bitret = this->empty_vec.insert( - this->empty_vec.begin() + insert_location, - to_insert, - bit::bit1); - auto boolret = this->empty_vec_bool.insert( - this->empty_vec_bool.begin() + insert_location, - to_insert, - true); - EXPECT_TRUE(std::equal( - this->empty_vec.begin(), - this->empty_vec.end(), - this->empty_vec_bool.begin(), - this->empty_vec_bool.end(), - comparator)); - EXPECT_EQ( - std::distance(this->empty_vec.begin(), bitret), - std::distance(this->empty_vec_bool.begin(), boolret)); - } + using WordType = typename TestFixture::base_type; + constexpr auto digits = bit::binary_digits::value; + // Second signature + for (auto _ = 16; _--;) { + auto to_insert = generate_random_number(0, 4 * digits); + auto insert_location = generate_random_number(0, this->empty_vec.size()); + auto bitret = this->empty_vec.insert( + this->empty_vec.begin() + insert_location, + to_insert, + bit::bit1); + auto boolret = this->empty_vec_bool.insert( + this->empty_vec_bool.begin() + insert_location, + to_insert, + true); + EXPECT_TRUE(std::equal( + this->empty_vec.begin(), + this->empty_vec.end(), + this->empty_vec_bool.begin(), + this->empty_vec_bool.end(), + comparator)); + EXPECT_EQ( + std::distance(this->empty_vec.begin(), bitret), + std::distance(this->empty_vec_bool.begin(), boolret)); + } } TYPED_TEST(VectorTest, InsertAtBegin3) { - // Third signature - for (auto _ = 16; _--;) { - auto to_insert = generate_random_number(0, this->random_bitvecs.size() - 1); - auto insert_bitvec = this->random_bitvecs[to_insert]; - auto insert_boolvec = this->random_boolvecs[to_insert]; - auto bitret = this->empty_vec.insert( - this->empty_vec.begin(), - insert_bitvec.begin(), - insert_bitvec.end()); - auto boolret = this->empty_vec_bool.insert( - this->empty_vec_bool.begin(), - insert_boolvec.begin(), - insert_boolvec.end()); - EXPECT_TRUE(std::equal( - this->empty_vec.begin(), - this->empty_vec.end(), - this->empty_vec_bool.begin(), - this->empty_vec_bool.end(), - comparator)); - EXPECT_EQ( - std::distance(this->empty_vec.begin(), bitret), - std::distance(this->empty_vec_bool.begin(), boolret)); - } + // Third signature + for (auto _ = 16; _--;) { + auto to_insert = generate_random_number(0, this->random_bitvecs.size() - 1); + auto insert_bitvec = this->random_bitvecs[to_insert]; + auto insert_boolvec = this->random_boolvecs[to_insert]; + auto bitret = this->empty_vec.insert( + this->empty_vec.begin(), + insert_bitvec.begin(), + insert_bitvec.end()); + auto boolret = this->empty_vec_bool.insert( + this->empty_vec_bool.begin(), + insert_boolvec.begin(), + insert_boolvec.end()); + EXPECT_TRUE(std::equal( + this->empty_vec.begin(), + this->empty_vec.end(), + this->empty_vec_bool.begin(), + this->empty_vec_bool.end(), + comparator)); + EXPECT_EQ( + std::distance(this->empty_vec.begin(), bitret), + std::distance(this->empty_vec_bool.begin(), boolret)); + } } TYPED_TEST(VectorTest, InsertAtEnd3) { - // Third signature - for (auto _ = 16; _--;) { - auto to_insert = generate_random_number(0, this->random_bitvecs.size() - 1); - auto insert_bitvec = this->random_bitvecs[to_insert]; - auto insert_boolvec = this->random_boolvecs[to_insert]; - auto bitret = this->empty_vec.insert( - this->empty_vec.end(), - insert_bitvec.begin(), - insert_bitvec.end()); - auto boolret = this->empty_vec_bool.insert( - this->empty_vec_bool.end(), - insert_boolvec.begin(), - insert_boolvec.end()); - EXPECT_TRUE(std::equal( - this->empty_vec.begin(), - this->empty_vec.end(), - this->empty_vec_bool.begin(), - this->empty_vec_bool.end(), - comparator)); - EXPECT_EQ( - std::distance(this->empty_vec.begin(), bitret), - std::distance(this->empty_vec_bool.begin(), boolret)); - } + // Third signature + for (auto _ = 16; _--;) { + auto to_insert = generate_random_number(0, this->random_bitvecs.size() - 1); + auto insert_bitvec = this->random_bitvecs[to_insert]; + auto insert_boolvec = this->random_boolvecs[to_insert]; + auto bitret = this->empty_vec.insert( + this->empty_vec.end(), + insert_bitvec.begin(), + insert_bitvec.end()); + auto boolret = this->empty_vec_bool.insert( + this->empty_vec_bool.end(), + insert_boolvec.begin(), + insert_boolvec.end()); + EXPECT_TRUE(std::equal( + this->empty_vec.begin(), + this->empty_vec.end(), + this->empty_vec_bool.begin(), + this->empty_vec_bool.end(), + comparator)); + EXPECT_EQ( + std::distance(this->empty_vec.begin(), bitret), + std::distance(this->empty_vec_bool.begin(), boolret)); + } } TYPED_TEST(VectorTest, InsertAtRand3) { - // Third signature - for (auto _ = 16; _--;) { - auto to_insert = generate_random_number(0, this->random_bitvecs.size() - 1); - auto insert_bitvec = this->random_bitvecs[to_insert]; - auto insert_boolvec = this->random_boolvecs[to_insert]; - auto insert_location = generate_random_number(0, this->empty_vec.size()); - auto bitret = this->empty_vec.insert( - this->empty_vec.begin() + insert_location, - insert_bitvec.begin(), - insert_bitvec.end()); - auto boolret = this->empty_vec_bool.insert( - this->empty_vec_bool.begin() + insert_location, - insert_boolvec.begin(), - insert_boolvec.end()); - EXPECT_TRUE(std::equal( - this->empty_vec.begin(), - this->empty_vec.end(), - this->empty_vec_bool.begin(), - this->empty_vec_bool.end(), - comparator)); - EXPECT_EQ( - std::distance(this->empty_vec.begin(), bitret), - std::distance(this->empty_vec_bool.begin(), boolret)); - } + // Third signature + for (auto _ = 16; _--;) { + auto to_insert = generate_random_number(0, this->random_bitvecs.size() - 1); + auto insert_bitvec = this->random_bitvecs[to_insert]; + auto insert_boolvec = this->random_boolvecs[to_insert]; + auto insert_location = generate_random_number(0, this->empty_vec.size()); + auto bitret = this->empty_vec.insert( + this->empty_vec.begin() + insert_location, + insert_bitvec.begin(), + insert_bitvec.end()); + auto boolret = this->empty_vec_bool.insert( + this->empty_vec_bool.begin() + insert_location, + insert_boolvec.begin(), + insert_boolvec.end()); + EXPECT_TRUE(std::equal( + this->empty_vec.begin(), + this->empty_vec.end(), + this->empty_vec_bool.begin(), + this->empty_vec_bool.end(), + comparator)); + EXPECT_EQ( + std::distance(this->empty_vec.begin(), bitret), + std::distance(this->empty_vec_bool.begin(), boolret)); + } } // Test erase TYPED_TEST(VectorTest, EraseAtBegin1) { - // First signature - for (auto _ = 16; _--;) { - auto vec_idx = generate_random_number(0, this->random_bitvecs.size() - 1); - auto& bitvec = this->random_bitvecs[vec_idx]; - auto& boolvec = this->random_boolvecs[vec_idx]; - if (boolvec.size() == 0) { continue; } - auto bitret = bitvec.erase(bitvec.begin()); - auto boolret = boolvec.erase(boolvec.begin()); - EXPECT_TRUE(std::equal( - bitvec.begin(), - bitvec.end(), - boolvec.begin(), - boolvec.end(), - comparator)); - EXPECT_EQ( - std::distance(bitvec.begin(), bitret), - std::distance(boolvec.begin(), boolret)); - } + // First signature + for (auto _ = 16; _--;) { + auto vec_idx = generate_random_number(0, this->random_bitvecs.size() - 1); + auto& bitvec = this->random_bitvecs[vec_idx]; + auto& boolvec = this->random_boolvecs[vec_idx]; + if (boolvec.size() == 0) { + continue; + } + auto bitret = bitvec.erase(bitvec.begin()); + auto boolret = boolvec.erase(boolvec.begin()); + EXPECT_TRUE(std::equal( + bitvec.begin(), + bitvec.end(), + boolvec.begin(), + boolvec.end(), + comparator)); + EXPECT_EQ(bitvec.size(), boolvec.size()); + EXPECT_EQ( + std::distance(bitvec.begin(), bitret), + std::distance(boolvec.begin(), boolret)); + } } TYPED_TEST(VectorTest, EraseAtEnd1) { - // First signature - for (auto _ = 16; _--;) { - auto vec_idx = generate_random_number(0, this->random_bitvecs.size() - 1); - auto& bitvec = this->random_bitvecs[vec_idx]; - auto& boolvec = this->random_boolvecs[vec_idx]; - if (boolvec.size() == 0) { continue; } - auto bitret = bitvec.erase(bitvec.end() - 1); - auto boolret = boolvec.erase(boolvec.end() - 1); - EXPECT_TRUE(std::equal( - bitvec.begin(), - bitvec.end(), - boolvec.begin(), - boolvec.end(), - comparator)); - EXPECT_EQ( - std::distance(bitvec.begin(), bitret), - std::distance(boolvec.begin(), boolret)); - } + // First signature + for (auto _ = 16; _--;) { + auto vec_idx = generate_random_number(0, this->random_bitvecs.size() - 1); + auto& bitvec = this->random_bitvecs[vec_idx]; + auto& boolvec = this->random_boolvecs[vec_idx]; + if (boolvec.size() == 0) { + continue; + } + auto bitret = bitvec.erase(bitvec.end() - 1); + auto boolret = boolvec.erase(boolvec.end() - 1); + EXPECT_TRUE(std::equal( + bitvec.begin(), + bitvec.end(), + boolvec.begin(), + boolvec.end(), + comparator)); + EXPECT_EQ( + std::distance(bitvec.begin(), bitret), + std::distance(boolvec.begin(), boolret)); + } } TYPED_TEST(VectorTest, EraseAtRand1) { @@ -630,13 +653,13 @@ TYPED_TEST(VectorTest, EraseAtRand1) { auto bitret = bitvec.erase(bitvec.begin() + erase_location); auto boolret = boolvec.erase(boolvec.begin() + erase_location); EXPECT_TRUE(std::equal( - bitvec.begin(), - bitvec.end(), - boolvec.begin(), - boolvec.end(), + bitvec.begin(), + bitvec.end(), + boolvec.begin(), + boolvec.end(), comparator)); EXPECT_EQ( - std::distance(bitvec.begin(), bitret), + std::distance(bitvec.begin(), bitret), std::distance(boolvec.begin(), boolret)); } } @@ -652,13 +675,13 @@ TYPED_TEST(VectorTest, EraseAtBegin2) { auto bitret = bitvec.erase(bitvec.begin(), bitvec.begin() + erase_size); auto boolret = boolvec.erase(boolvec.begin(), boolvec.begin() + erase_size); EXPECT_TRUE(std::equal( - bitvec.begin(), - bitvec.end(), - boolvec.begin(), - boolvec.end(), + bitvec.begin(), + bitvec.end(), + boolvec.begin(), + boolvec.end(), comparator)); EXPECT_EQ( - std::distance(bitvec.begin(), bitret), + std::distance(bitvec.begin(), bitret), std::distance(boolvec.begin(), boolret)); } } @@ -674,13 +697,13 @@ TYPED_TEST(VectorTest, EraseAtEnd2) { auto bitret = bitvec.erase(bitvec.begin() + erase_start, bitvec.end()); auto boolret = boolvec.erase(boolvec.begin() + erase_start, boolvec.end()); EXPECT_TRUE(std::equal( - bitvec.begin(), - bitvec.end(), - boolvec.begin(), - boolvec.end(), + bitvec.begin(), + bitvec.end(), + boolvec.begin(), + boolvec.end(), comparator)); EXPECT_EQ( - std::distance(bitvec.begin(), bitret), + std::distance(bitvec.begin(), bitret), std::distance(boolvec.begin(), boolret)); } } @@ -697,58 +720,73 @@ TYPED_TEST(VectorTest, EraseAtRand2) { auto bitret = bitvec.erase(bitvec.begin() + erase_start, bitvec.begin() + erase_end); auto boolret = boolvec.erase(boolvec.begin() + erase_start, boolvec.begin() + erase_end); EXPECT_TRUE(std::equal( - bitvec.begin(), - bitvec.end(), - boolvec.begin(), - boolvec.end(), + bitvec.begin(), + bitvec.end(), + boolvec.begin(), + boolvec.end(), comparator)); EXPECT_EQ( - std::distance(bitvec.begin(), bitret), + std::distance(bitvec.begin(), bitret), std::distance(boolvec.begin(), boolret)); } } // Test push_back TYPED_TEST(VectorTest, PushBack) { - // First signature - for (unsigned int vec_idx = 0; vec_idx < this->random_bitvecs.size(); ++vec_idx) { - for (auto _ = 4*this->digits; _ > 0; _--) { - auto& bitvec = this->random_bitvecs[vec_idx]; - auto& boolvec = this->random_boolvecs[vec_idx]; - bool to_insert_bool = generate_random_number(0, 1) > 0 ? true : false; - bit::bit_value to_insert_bit = to_insert_bool ? bit::bit1 : bit::bit0; - bitvec.push_back(to_insert_bit); - boolvec.push_back(to_insert_bool); - EXPECT_TRUE(std::equal( - bitvec.begin(), - bitvec.end(), - boolvec.begin(), - boolvec.end(), - comparator)); - } - } + using WordType = typename TestFixture::base_type; + constexpr auto digits = bit::binary_digits::value; + // First signature + for (unsigned int vec_idx = 0; vec_idx < this->random_bitvecs.size(); ++vec_idx) { + for (auto _ = 4 * digits; _ > 0; _--) { + auto& bitvec = this->random_bitvecs[vec_idx]; + auto& boolvec = this->random_boolvecs[vec_idx]; + bool to_insert_bool = generate_random_number(0, 1) > 0 ? true : false; + bit::bit_value to_insert_bit = to_insert_bool ? bit::bit1 : bit::bit0; + bitvec.push_back(to_insert_bit); + boolvec.push_back(to_insert_bool); + EXPECT_TRUE(std::equal( + bitvec.begin(), + bitvec.end(), + boolvec.begin(), + boolvec.end(), + comparator)); + } + } } // Test pop_back TYPED_TEST(VectorTest, PopBack) { - // First signature - for (unsigned int vec_idx = 0; vec_idx < this->random_bitvecs.size(); ++vec_idx) { - auto& bitvec = this->random_bitvecs[vec_idx]; - auto& boolvec = this->random_boolvecs[vec_idx]; - for (auto _ = std::min(boolvec.size(), 4*this->digits); _>0; _--) { - bitvec.pop_back(); - boolvec.pop_back(); - EXPECT_TRUE(std::equal( - bitvec.begin(), - bitvec.end(), - boolvec.begin(), - boolvec.end(), - comparator)); - } - } + using WordType = typename TestFixture::base_type; + constexpr auto digits = bit::binary_digits::value; + // First signature + for (unsigned int vec_idx = 0; vec_idx < this->random_bitvecs.size(); ++vec_idx) { + auto& bitvec = this->random_bitvecs[vec_idx]; + auto& boolvec = this->random_boolvecs[vec_idx]; + for (auto _ = std::min(boolvec.size(), 4 * digits); _ > 0; _--) { + bitvec.pop_back(); + boolvec.pop_back(); + EXPECT_TRUE(std::equal( + bitvec.begin(), + bitvec.end(), + boolvec.begin(), + boolvec.end(), + comparator)); + } + } +} + +TEST(BitVectorTest, Slice) { + uint32_t word = 0xDEADBEEF; + bit::bit_span span1(word); + auto vec = bit::bit_vector(std::from_range, span1); + auto span2 = span1(4, 8); + EXPECT_EQ(span2.size(), 4); + EXPECT_EQ(span2[0], (0xE & (1 << 0)) ? bit::bit1 : bit::bit0); + EXPECT_EQ(span2[1], (0xE & (1 << 1)) ? bit::bit1 : bit::bit0); + EXPECT_EQ(span2[2], (0xE & (1 << 2)) ? bit::bit1 : bit::bit0); + EXPECT_EQ(span2[3], (0xE & (1 << 3)) ? bit::bit1 : bit::bit0); } - //TYPED_TEST(VectorTest, Print) { //std::cout << this->v3_ << std::endl; //}