diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..2787041 --- /dev/null +++ b/.clang-format @@ -0,0 +1,155 @@ +--- +# Refer to the following link for the explanation of each params: +# http://releases.llvm.org/8.0.0/tools/clang/docs/ClangFormatStyleOptions.html +Language: Cpp +# BasedOnStyle: Google +AccessModifierOffset: -1 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: true +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Left +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: true +AllowShortCaseLabelsOnASingleLine: true +AllowShortFunctionsOnASingleLine: false +AllowShortIfStatementsOnASingleLine: true +AllowShortLoopsOnASingleLine: true +# This is deprecated +AlwaysBreakAfterDefinitionReturnType: All +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: false +BinPackParameters: false +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + # disabling the below splits, else, they'll just add to the vertical length of source files! + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: false +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 120 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +# Kept the below 2 to be the same as `IndentWidth` to keep everything uniform +ConstructorInitializerIndentWidth: 2 +ContinuationIndentWidth: 2 +Cpp11BracedListStyle: true +DerivePointerAlignment: true +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^' + Priority: 2 + - Regex: '^<.*\.h>' + Priority: 1 + - Regex: '^<.*' + Priority: 2 + - Regex: '.*' + Priority: 3 +IncludeIsMainRegex: '([-_](test|unittest))?$' +IndentCaseLabels: true +IndentPPDirectives: None +IndentWidth: 4 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Never +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 200 +PointerAlignment: Left +RawStringFormats: + - Language: Cpp + Delimiters: + - cc + - CC + - cpp + - Cpp + - CPP + - 'c++' + - 'C++' + CanonicalDelimiter: '' + - Language: TextProto + Delimiters: + - pb + - PB + - proto + - PROTO + EnclosingFunctions: + - EqualsProto + - EquivToProto + - PARSE_PARTIAL_TEXT_PROTO + - PARSE_TEST_PROTO + - PARSE_TEXT_PROTO + - ParseTextOrDie + - ParseTextProtoOrDie + CanonicalDelimiter: '' + BasedOnStyle: google +# Enabling comment reflow causes doxygen comments to be messed up in their formats! +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: c++20 +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +# Be consistent with indent-width, even for people who use tab for indentation! +TabWidth: 3 +UseTab: Never diff --git a/.gitignore b/.gitignore index 49f5923..e0aaec6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,131 @@ -#clion build folders. -cmake-build-*/* -#clion settings. -.idea/* \ No newline at end of file +#vscode +.vscode/ + +# unix +*.o +*.so +!asm/*.o + +# windows - most output files go to %userprofile%\jbld\jout +# some end up in the project folder and are marked here +# sometimes entire project subfolders are created that should not be in repo +*.sdf +*.opensdf +*.suo +*.ilk +*.pdb +*.exp + +# windows - need to run with different versions of vs +# following files are in repo for vs2013 +# but are not pushed to avoid getting unwanted user specific changes +# clone will have to copy from .template files to work +*.sln* +*.vcxproj* +!*.template +.vs/ + +# mac +.DS_Store + +#android +android/libs/ +android/obj/ +android/jni/jsrc +android/jni/clapack +android/jni/pcre +android/jni/pcre2 +android/jni/tsdll +android/jni/hostdefs +android/jni/netdefs +clapack/ +pcre/ + +release +/test/temp.ijs + +# build folders +out/ +build/ +bin/ +make2/ + +# clion's cmake creates cmake-build-release, cmake-build-debug, etc... +cmake-build-*/ +# clion's settings folder. keep or not up to you. can be useful for spellcheck etc. +.idea + +# QtCreator config files +# A listing of all the files included in the project +*.files + +# Include directories +*.includes + +# Project configuration settings like predefined Macros +*.config + +# Qt Creator settings +*.creator + +# User project settings +*.creator.user* + +# Qt Creator backups +*.autosave + +# Flags for Clang Code Model +*.cxxflags +*.cflags + +# Meson Build System files +# subproject directories +/subprojects/* +!/subprojects/*.wrap + +# Meson Directories +meson-logs +meson-private + +# Meson Files +meson_benchmark_setup.dat +meson_test_setup.dat +sanitycheckcpp.cc # C++ specific +sanitycheckcpp.exe # C++ specific + +# Ninja +build.ninja +.ninja_deps +.ninja_logs + +# Misc +compile_commands.json +# Prerequisites +*.d + +# Precompiled Headers +*.gch +*.pch + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# doxygen +docs/doxygen +docs/latex +docs/html +docs/make.bat +docs/xml +docs/_build +docs/Makefile + +#python in clion +venv/* diff --git a/CMakeLists.txt b/CMakeLists.txt index 4c06a14..a25ffc8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,10 +1,11 @@ cmake_minimum_required(VERSION 3.18) project(An-Algorithm-Library C CXX) -set(CMAKE_CXX_STANDARD 17) ## Supported values are 98, 11, 14, 17, and 20. -set(CMAKE_CXX_STANDARD_REQUIRED ON) ## the value of the CXX_STANDARD target property is treated as a requirement -set(CMAKE_CXX_EXTENSIONS OFF) ## on g++ this ensures: -std=c++11 and not -std=gnu++11 -set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) ## automatically create a module definition (.def) file with all global - ## symbols found in the input .obj files for a SHARED library on Windows. - -add_subdirectory(src) \ No newline at end of file +add_subdirectory(src) +# see https://cliutils.gitlab.io/modern-cmake/chapters/testing.html +if (CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) + include(CTest) +endif () +if ((CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) AND BUILD_TESTING) +add_subdirectory(tests) +endif () \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..2edc38a --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,61 @@ +### Getting Started & Building: + +1. Prerequisites: + * CMake version 3.18.0+ -> Ubuntu systems seem to have a problem getting the latest version, please build from + sources or use another package such as snap. + * [Ubuntu CMake update instructions](https://apt.kitware.com/) + * Ninja +2. Checkout the repository: + ```sh + git clone https://github.com/codereport/An-Algorithm-Library.git + ``` + +#### Linux or Windows w/ Clang + +0. Switch between GCC or Clang, on linux: + * GCC + ```sh + export CC=gcc + export CXX=g++ + ``` + * Clang + ```sh + export CC=clang + export CXX=clang++ + ``` +1. Run cmake: + ```sh + cd An-Algorithm-Library + mkdir build + cmake -G "Ninja Multi-Config" -B build + ``` +2. Run tests: + ```sh + ninja -C build test + ``` + * For more verbose output: + + ```sh + # On linux you can combine these lines with && + ninja -C build + cd build + ctest -C debug --verbose + cd .. + ``` + +#### Windows MSVC + +1. Run cmake: + ```sh + cd An-Algorithm-Library + mkdir build + cmake -G "Visual Studio 16 2019" -B build + ``` +2. Run tests: + * For more verbose output: + ```sh + MSBuild build\ALL_BUILD.vcxproj + cd build + ctest -C debug --verbose + cd .. + ``` \ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..5b3e116 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,38 @@ +version: 1.0.{build} +environment: + matrix: + - job_name: Windows MSVC build + appveyor_build_worker_image: Visual Studio 2019 + + - job_name: Linux GCC build + appveyor_build_worker_image: Ubuntu2004 + + - job_name: Linux Clang build + appveyor_build_worker_image: Ubuntu1604 +for: + - matrix: + only: + - job_name: Linux Clang build + init: + - sh: "#update cmake!\nsudo apt-get update\nsudo apt-get install apt-transport-https ca-certificates gnupg software-properties-common wget\nwget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null | gpg --dearmor - | sudo tee /etc/apt/trusted.gpg.d/kitware.gpg >/dev/null\nsudo apt-add-repository 'deb https://apt.kitware.com/ubuntu/ xenial main'\nsudo apt-get -y update\nsudo apt-get -y install kitware-archive-keyring\nsudo rm /etc/apt/trusted.gpg.d/kitware.gpg\nsudo apt-get -y install cmake\nsudo apt-get -y install ninja-build\n#update clang++!\nwget -O - https://apt.llvm.org/llvm-snapshot.gpg.key|sudo apt-key add -\n# Fingerprint: 6084 F3CF 814B 57C1 CF12 EFD5 15CF 4D18 AF4F 7421\n# i386 not available\n#sudo add-apt-repository 'deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial main'\n#sudo add-apt-repository 'deb-src http://apt.llvm.org/xenial/ llvm-toolchain-xenial main'\n# 11\n#sudo add-apt-repository 'deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-11 main'\n#sudo add-apt-repository 'deb-src http://apt.llvm.org/xenial/ llvm-toolchain-xenial-11 main'\n# 12\nsudo add-apt-repository 'deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-12 main'\n#sudo add-apt-repository 'deb-src http://apt.llvm.org/xenial/ llvm-toolchain-xenial-12 main'\nsudo apt-get -y update\nsudo apt-get -y -f install clang-12 clang-tools-12 clang-12-doc libclang-common-12-dev libclang-12-dev libclang1-12 clang-format-12 clangd-12\nsudo apt-get -y autoremove\nsudo apt-get -y autoclean\nsudo apt-get -y -f install libc++abi1-12 libc++abi-12-dev libc++1-12 libc++-12-dev\n#set clang++ to specific version:\nsudo update-alternatives --remove-all clang\nsudo update-alternatives --remove-all clang++\nsudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-12 90\nsudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-12 90\nexport CC=clang\nexport CXX=clang++" + build_script: + - sh: "mkdir build\ncmake -G \"Ninja Multi-Config\" -B build\nninja -C build" + test_script: + - sh: "cd build && ctest -C debug --verbose && cd .." + + - matrix: + only: + - job_name: Linux GCC build + init: + - sh: "sudo update-alternatives --remove-all gcc \nsudo update-alternatives --remove-all g++\nsudo apt-get update\nsudo apt-get install apt-transport-https ca-certificates gnupg software-properties-common wget\nwget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null | gpg --dearmor - | sudo tee /etc/apt/trusted.gpg.d/kitware.gpg >/dev/null\nsudo add-apt-repository ppa:ubuntu-toolchain-r/test\nsudo apt-add-repository 'deb https://apt.kitware.com/ubuntu/ focal main'\nsudo apt-get -y update\nsudo apt-get -y install gcc-10 g++-10 ninja-build cmake\n#sudo apt-get -y upgrade\nsudo update-alternatives --remove-all gcc\nsudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-10 90\nsudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-10 90\nsudo update-alternatives --install /usr/bin/cc cc /usr/bin/gcc 30\nsudo update-alternatives --set cc /usr/bin/gcc\nsudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/g++ 30\nsudo update-alternatives --set c++ /usr/bin/g++\nsudo apt -y autoremove\nexport CC=gcc\nexport CXX=g++" + build_script: + - sh: "mkdir build\ncmake -G \"Ninja Multi-Config\" -B build\nninja -C build" + test_script: + - sh: "cd build && ctest -C debug --verbose && cd .." + - matrix: + only: + - job_name: Windows MSVC build + build_script: + - cmd: "mkdir build\ncmake -G \"Visual Studio 16 2019\" -B build\nMSBuild build\\ALL_BUILD.vcxproj" + test_script: + - cmd: "cd build\nctest -C debug --verbose\ncd .." \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..297ffd8 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,5 @@ +include(cmake/compiler_warnings.cmake) +include(cmake/clang_compat.cmake) +include(cmake/ut.cmake) +include(cmake/add_test_common.cmake) +add_test_common(algorithm_test) \ No newline at end of file diff --git a/tests/cmake/add_test_common.cmake b/tests/cmake/add_test_common.cmake new file mode 100644 index 0000000..abdb832 --- /dev/null +++ b/tests/cmake/add_test_common.cmake @@ -0,0 +1,15 @@ +function(add_test_common file_name) + add_executable(${PROJECT_NAME}_${file_name} + src/${file_name}.cpp) + target_link_libraries(${PROJECT_NAME}_${file_name} + PRIVATE Boost::ut + PRIVATE ${PROJECT_NAME}_aal + PRIVATE ${PROJECT_NAME}_project_warnings + PRIVATE ${PROJECT_NAME}_clang_compat + ) + target_include_directories(${PROJECT_NAME}_${file_name} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src) + set_target_properties(${PROJECT_NAME}_${file_name} PROPERTIES OUTPUT_NAME "${file_name}") + add_test( + NAME ${PROJECT_NAME}_${file_name} COMMAND ${PROJECT_NAME}_${file_name} + ) +endfunction() \ No newline at end of file diff --git a/tests/cmake/clang_compat.cmake b/tests/cmake/clang_compat.cmake new file mode 100644 index 0000000..8454ef1 --- /dev/null +++ b/tests/cmake/clang_compat.cmake @@ -0,0 +1,12 @@ +function(set_clang_compat project_name) + if (MSVC) + elseif (CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") + target_compile_options(${project_name} INTERFACE -stdlib=libc++) + target_link_options(${project_name} INTERFACE -stdlib=libc++ -lc++abi) + elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + else () + endif () +endfunction() + +add_library(${PROJECT_NAME}_clang_compat INTERFACE) +set_clang_compat(${PROJECT_NAME}_clang_compat) diff --git a/tests/cmake/compiler_warnings.cmake b/tests/cmake/compiler_warnings.cmake new file mode 100644 index 0000000..52cf4e9 --- /dev/null +++ b/tests/cmake/compiler_warnings.cmake @@ -0,0 +1,87 @@ +# from here: +# https://github.com/lefticus/cppbestpractices/blob/master/02-Use_the_Tools_Available.md +# https://github.com/lefticus/cpp_starter_project/blob/master/cmake/CompilerWarnings.cmake +# lefticus/cpp_starter_project is licensed under the The Unlicense +# A license with no conditions whatsoever which dedicates works to the public domain. Unlicensed works, modifications, +# and larger works may be distributed under different terms and without source code. + +function(set_project_warnings project_name) + option(WARNINGS_AS_ERRORS "Treat compiler warnings as errors" TRUE) + + set(MSVC_WARNINGS + /W4 # Baseline reasonable warnings + /w14242 # 'identifier': conversion from 'type1' to 'type1', possible loss of data + /w14254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data + /w14263 # 'function': member function does not override any base class virtual member function + /w14265 # 'classname': class has virtual functions, but destructor is not virtual instances of this class may not + # be destructed correctly + /w14287 # 'operator': unsigned/negative constant mismatch + /we4289 # nonstandard extension used: 'variable': loop control variable declared in the for-loop is used outside + # the for-loop scope + /w14296 # 'operator': expression is always 'boolean_value' + /w14311 # 'variable': pointer truncation from 'type1' to 'type2' + /w14545 # expression before comma evaluates to a function which is missing an argument list + /w14546 # function call before comma missing argument list + /w14547 # 'operator': operator before comma has no effect; expected operator with side-effect + /w14549 # 'operator': operator before comma has no effect; did you intend 'operator'? + /w14555 # expression has no effect; expected expression with side- effect + /w14619 # pragma warning: there is no warning number 'number' + /w14640 # Enable warning on thread un-safe static member initialization + /w14826 # Conversion from 'type1' to 'type_2' is sign-extended. This may cause unexpected runtime behavior. + /w14905 # wide string literal cast to 'LPSTR' + /w14906 # string literal cast to 'LPWSTR' + /w14928 # illegal copy-initialization; more than one user-defined conversion has been implicitly applied + /permissive- # standards conformance mode for MSVC compiler. + #/wd4505 # suppress unreferenced local function has been removed. + ) + + set(CLANG_WARNINGS + -Wall + -Wextra # reasonable and standard + -Wshadow # warn the user if a variable declaration shadows one from a parent context + -Wno-error=shadow # ut has a lot of shadows. This prevents errors from that. + -Wnon-virtual-dtor # warn the user if a class with virtual functions has a non-virtual destructor. This helps + # catch hard to track down memory errors + -Wold-style-cast # warn for c-style casts + -Wcast-align # warn for potential performance problem casts + -Wunused # warn on anything being unused + -Woverloaded-virtual # warn if you overload (not override) a virtual function + -Wpedantic # warn if non-standard C++ is used + -Wconversion # warn on type conversions that may lose data + -Wsign-conversion # warn on sign conversions + -Wnull-dereference # warn if a null dereference is detected + -Wdouble-promotion # warn if float is implicit promoted to double + -Wformat=2 # warn on security issues around functions that format output (ie printf) + ) + + if (WARNINGS_AS_ERRORS) + message(STATUS "Warnings as errors: enabled") + set(CLANG_WARNINGS ${CLANG_WARNINGS} -Werror) + set(MSVC_WARNINGS ${MSVC_WARNINGS} /WX) + endif () + + set(GCC_WARNINGS + ${CLANG_WARNINGS} + -Wmisleading-indentation # warn if indentation implies blocks where blocks do not exist + -Wduplicated-cond # warn if if / else chain has duplicated conditions + -Wduplicated-branches # warn if if / else branches have duplicated code + -Wlogical-op # warn about logical operations being used where bitwise were probably wanted + -Wuseless-cast # warn if you perform a cast to the same type + ) + + if (MSVC) + set(PROJECT_WARNINGS ${MSVC_WARNINGS}) + elseif (CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") + set(PROJECT_WARNINGS ${CLANG_WARNINGS}) + elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(PROJECT_WARNINGS ${GCC_WARNINGS}) + else () + message(AUTHOR_WARNING "No compiler warnings set for '${CMAKE_CXX_COMPILER_ID}' compiler.") + endif () + + target_compile_options(${project_name} INTERFACE ${PROJECT_WARNINGS}) + +endfunction() + +add_library(${PROJECT_NAME}_project_warnings INTERFACE) +set_project_warnings(${PROJECT_NAME}_project_warnings ON) \ No newline at end of file diff --git a/tests/cmake/ut.cmake b/tests/cmake/ut.cmake new file mode 100644 index 0000000..519fa62 --- /dev/null +++ b/tests/cmake/ut.cmake @@ -0,0 +1,13 @@ +include(FetchContent) +FetchContent_Declare( + ut + GIT_REPOSITORY https://github.com/boost-ext/ut.git + GIT_TAG origin/master + GIT_REMOTE_UPDATE_STRATEGY CHECKOUT # this discards local changes before pulling the new data. +) +#FetchContent_MakeAvailable(ut) +FetchContent_GetProperties(ut) +if(NOT ut_POPULATED) + FetchContent_Populate(ut) + add_subdirectory(${ut_SOURCE_DIR} ${ut_BINARY_DIR} EXCLUDE_FROM_ALL) +endif() \ No newline at end of file diff --git a/tests/src/algorithm_test.cpp b/tests/src/algorithm_test.cpp new file mode 100644 index 0000000..42c937b --- /dev/null +++ b/tests/src/algorithm_test.cpp @@ -0,0 +1,226 @@ +#include // single header +// import boost.ut; // single module (C++20) +#include +#include +#include "aal/algorithm.hpp" +/** + * multiply all the parameter pack values together + * @note if sizeof...(a) == 1 it returns a. + */ +static constexpr auto multiply = [](const auto &...a) { return (a * ...); }; + +/** + * add all the parameter pack values together + * @note if sizeof...(a) == 1 it returns a. + */ +static constexpr auto plus = [](const auto &...a) { return (a + ...); }; + +/** + * are all the parameter pack values equal? + * @param first value + * @param rest of values. + */ +static constexpr auto equal_to = [](const auto &first, const auto &...rest) -> bool { + static_assert(sizeof...(rest) != 0); + return ((first == rest) || ...); +}; + +/** + * are any of the parameter pack values not equal? + * @param first value + * @param rest of values. + */ +static constexpr auto not_equal_to = [](const auto &first, const auto &...rest) -> bool { + static_assert(sizeof...(rest) != 0); + return ((first != rest) || ...); +}; + +/** + * Transform values to signed types and pass to op + * @param op operation that is performed that returns a value. + * @param a values + */ +static constexpr auto make_signed = [](const auto &op, const auto &...a) { + static_assert((std::is_integral_v> && ...)); + return op(static_cast>>(a)...); +}; + +/** + * Transform values to unsigned types and pass to op + * @param op operation that is performed that returns a value. + * @param a values + */ +static constexpr auto make_unsigned = [](const auto &op, const auto &...a) { + static_assert((std::is_integral_v> && ...)); + return op((static_cast>>(a))...); +}; + +/** + * Eager transform and output to rvalue range + * @tparam I first range iterator type + * @tparam Is other range iterators type + * @tparam O output range r-value type + * @tparam Op operation type + * @param op operation + * @param o output range r-value + * @param f first range iterator begin + * @param l first range iterator end + * @param fs other range iterator begins + * @return returns output range post transform. + */ + +template +requires requires(O &&o) { + std::begin(o); +} +[[nodiscard]] consteval auto +transform(const Op op, O &&o, I f, const I l, Is... fs) { + aal::var::transform(op, std::begin(o), f, l, fs...); + return o; +} + +/** + * Eager constant time construct a std::array of numbers using iota. + * @tparam start number + * @tparam count of values + * @return std::array from start to start+count + */ +template +requires(count < std::numeric_limits::max() - start) [[nodiscard]] consteval auto iota() { + // std::iota is not constexpr in clang. + std::array iota_range{}; + auto i = start; + aal::var::transform([&i]([[maybe_unused]] const auto &input) { return i++; }, + std::begin(iota_range), + std::begin(iota_range), + std::end(iota_range)); + return iota_range; +} + +int +main() { + using namespace boost::ut::literals; + using namespace boost::ut::operators::terse; + using namespace boost::ut; + [[maybe_unused]] suite tests = [] { + static constexpr auto input = iota<0, 10U>(); + static constexpr auto b = std::begin(input); + static constexpr auto e = std::end(input); + + "plus"_test = [] { + expect(1_i == plus(1)); + expect(0_i == plus(1, -1)); + expect(3_i == plus(1, 1, 1)); + expect(957_i == plus(100, 158, 699)); + }; + + "multiply"_test = [] { + expect(1_i == multiply(1)); + expect(-1_i == multiply(1, -1)); + expect(1_i == multiply(1, 1, 1)); + expect(11044200_i == multiply(100, 158, 699)); + }; + + "equal_to"_test = [] { + expect(constant); + expect(equal_to(0.0, 0)); + expect(make_signed(equal_to, 0U, 0)); + expect(make_unsigned(equal_to, 0U, 0)); + expect(make_signed(equal_to, 3, 3U, 3U, 3)); + expect(make_signed(equal_to, 3, 3U, 3)); + expect(make_unsigned(equal_to, 3, 3U, 3, char(3))); + }; + + "not_equal_to"_test = [] { + expect(constant); + expect(make_signed(not_equal_to, -3, 3U, 6U, '5', 3)); + expect(!make_signed(not_equal_to, 3, 3U, 3U, 3, char(3))); + expect(!make_signed(not_equal_to, 3, 3U, 3)); + expect(!make_unsigned(not_equal_to, 3, 3U, 3)); + }; + + "find"_test = [] { + static constexpr auto result = aal::var::find([&](const auto &...a) { return equal_to(3, a...); }, b, e); + expect(constant(result)>); + expect(constant<3 == *std::get<0>(result)>); + }; + + "any_of"_test = [] { + static constexpr auto result = aal::var::any_of([&](const auto &...a) { return equal_to(3, a...); }, b, e); + expect(constant); + }; + + "transform == 3"_test = [] { + std::array output{}; + const auto result = + aal::var::transform([&](const auto &...a) { return equal_to(3, a...); }, std::begin(output), b, e); + expect(output[3]); + expect(!*result); + }; + + "transform == 3 constexpr"_test = [] { + constexpr auto result = + transform([&](const auto &...a) { return equal_to(3, a...); }, std::array(), b, e); + expect(constant); + }; + + "transform plus 3"_test = [&] { + std::array result{}; + aal::var::transform([&](const auto &...a) { return plus(3, a...); }, std::begin(result), b, e); + expect(result[3] == 6_i); + }; + + "transform == 3 constexpr"_test = [&] { + constexpr auto result = + transform([&](const auto &...a) { return plus(3, a...); }, std::array(), b, e); + expect(constant); + expect(constant); + expect(constant); + }; + + "transform plus 2 times"_test = [] { + std::array result{}; + aal::var::transform(plus, std::begin(result), b, e, b); + expect(result[3] == 6_i); + expect(result[2] == 4_i); + expect(result[1] == 2_i); + }; + + "transform plus 2 times constexpr"_test = [] { + constexpr auto result = transform(plus, std::array(), b, e, b); + expect(constant); + expect(constant); + expect(constant); + }; + + "transform plus 3 times"_test = [] { + std::array result{}; + aal::var::transform(plus, std::begin(result), b, e, b, b); + expect(result[3] == 9_i); + expect(result[2] == 6_i); + expect(result[1] == 3_i); + }; + + "transform plus 3 times constexpr"_test = [] { + constexpr auto result = transform(plus, std::array(), b, e, b, b); + expect(constant); + expect(constant); + expect(constant); + }; + + "transform multiply 3 times"_test = [] { + std::array result{}; + aal::var::transform(multiply, std::begin(result), b, e, b, b); + expect(result[3] == 27_i); + expect(result[2] == 8_i); + expect(result[1] == 1_i); + }; + + "transform multiply 3 times constexpr"_test = [] { + constexpr auto result = transform(multiply, std::array(), b, e, b, b); + expect(constant); + expect(constant); + expect(constant); + }; + }; +} \ No newline at end of file