diff --git a/.appveyor.yml b/.appveyor.yml index fcfd4c87f7..787b2512d7 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -5,7 +5,7 @@ skip_tags: true skip_branch_with_pr: true image: - - Visual Studio 2019 + - Visual Studio 2022 clone_folder: c:\projects\cdt-plusplus @@ -19,9 +19,10 @@ clone_script: && git submodule update --init --recursive environment: - APPVEYOR_SAVE_CACHE_ON_ERROR : true + APPVEYOR_SAVE_CACHE_ON_ERROR: false + APPVEYOR_CACHE_SKIP_RESTORE: true VCPKG_DEFAULT_TRIPLET: x64-windows -# VCPKG_ROOT: c:\tools\vcpkg + VCPKG_ROOT: c:\tools\vcpkg cache: - c:\Users\appveyor\AppData\Local\vcpkg\archives\ @@ -30,29 +31,18 @@ cache: install: # Setup vcpkg - cd c:\tools\vcpkg -# - git clean -d -fx . - - git pull origin master --no-rebase -# - git reset --hard 56c0313 + - git pull - .\bootstrap-vcpkg.bat - set PATH=%PATH%;%VCPKG_ROOT% - vcpkg integrate install - cmake --version - # Install required libraries in classic mode -# - set VCPKG_INSTALL_ROOT=%APPVEYOR_BUILD_FOLDER%\vcpkg_installed -# - vcpkg install --x-install-root=%VCPKG_INSTALL_ROOT% catch2 docopt date fmt ms-gsl eigen3 pcg tbb tl-expected - # CGAL requires GMP which requires yasm-tool:x86-windows, even if you are using x64-windows. -# - vcpkg install --x-install-root=%VCPKG_INSTALL_ROOT% --recurse yasm-tool:x86-windows -# - vcpkg install --x-install-root=%VCPKG_INSTALL_ROOT% cgal - # Upgrade required libraries -# - vcpkg upgrade --no-dry-run --x-install-root=%VCPKG_INSTALL_ROOT% -# - vcpkg list --x-install-root=%VCPKG_INSTALL_ROOT% # Setup Clang-cl - set PATH=%PATH%;"C:\Program Files\LLVM\bin" - clang-cl -v - set CC=clang-cl -mrtm - set CXX=clang-cl -mrtm - # Visual Studio 2019 - - call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat" + # Visual Studio 2022 + - call "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat" - set CMAKE_CXX_COMPILER="C:\Program Files\LLVM\bin\clang-cl" platform: @@ -63,17 +53,17 @@ configuration: Release build: verbosity: detailed -before_build: - - cd %APPVEYOR_BUILD_FOLDER% - - vcpkg install --feature-flags=manifests - build_script: - - cmake -G Ninja -D CMAKE_BUILD_TYPE=Release -D ENABLE_CACHE=OFF -D CMAKE_TOOLCHAIN_FILE=C:/Tools/vcpkg/scripts/buildsystems/vcpkg.cmake -S . -B build + - cd %APPVEYOR_BUILD_FOLDER% + - cmake --preset=appveyor - cmake --build build -#on_success: +#test_script: # - cd build -# - ctest -VV +# - ctest --output-on-failure -j2 --output-junit test-results.xml +# +#after_test: +# - find "$APPVEYOR_BUILD_FOLDER" -type f -name 'test-results.xml' -print0 | xargs -0 -I '{}' curl -F 'file=@{}' "https://ci.appveyor.com/api/testresults/junit/$APPVEYOR_JOB_ID" notifications: - provider: Email diff --git a/.clang-format b/.clang-format index d50223b9a2..7bfe832775 100644 --- a/.clang-format +++ b/.clang-format @@ -104,9 +104,10 @@ IndentCaseBlocks: false IndentGotoLabels: true IndentPPDirectives: None IndentExternBlock: AfterExternBlock -IndentRequires: false +IndentRequiresClause: true IndentWidth: 2 IndentWrappedFunctionNames: false +InsertBraces: true InsertTrailingCommas: None JavaScriptQuotes: Leave JavaScriptWrapImports: true @@ -132,6 +133,15 @@ PenaltyReturnTypeOnItsOwnLine: 200 PenaltyIndentedWhitespace: 0 PointerAlignment: Left PPIndentWidth: -1 +QualifierAlignment: Custom +QualifierOrder: + - static + - inline + - type + - const + - constexpr + - volatile + - restrict RawStringFormats: - Language: Cpp Delimiters: diff --git a/.clang-tidy b/.clang-tidy index 759ca0fd9e..03c371d976 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,592 +1,374 @@ +#file: noinspection SpellCheckingInspection --- -Checks: 'clang-diagnostic-*,clang-analyzer-*,clang-diagnostic-*,clang-analyzer-*,*,-fuchsia-*,-google-*,-zircon-*,-abseil-*,-llvm-*,-cppcoreguidelines-avoid-c-arrays,-hicpp-avoid-c-arrays,-modernize-avoid-c-arrays, -hicpp-vararg, -cppcoreguidelines-pro-type-vararg, -llvmlibc-*, -readability-function-cognitive-complexity, -readability-function-size, -hicpp-function-size, -altera-*' +Checks: 'clang-diagnostic-*,clang-analyzer-*,clang-diagnostic-*,clang-analyzer-*,clang-diagnostic-*,clang-analyzer-*,*,-fuchsia-*,-google-*,-zircon-*,-abseil-*,-llvm-*,-cppcoreguidelines-avoid-c-arrays,-hicpp-avoid-c-arrays,-modernize-avoid-c-arrays, -hicpp-vararg, -cppcoreguidelines-pro-type-vararg, -llvmlibc-*, -readability-function-cognitive-complexity, -readability-function-size, -hicpp-function-size, -altera-*, -misc-include-cleaner' WarningsAsErrors: '0' -HeaderFilterRegex: '' +HeaderFileExtensions: + - '' + - h + - hh + - hpp + - hxx +ImplementationFileExtensions: + - c + - cc + - cpp + - cxx +HeaderFilterRegex: '^.(include|src|tests).*' AnalyzeTemporaryDtors: false FormatStyle: none +User: adam CheckOptions: - - key: cert-msc32-c.DisallowedSeedTypes - value: 'time_t,std::time_t' - - key: readability-suspicious-call-argument.PrefixSimilarAbove - value: '30' - - key: cppcoreguidelines-no-malloc.Reallocations - value: '::realloc' - - key: cppcoreguidelines-owning-memory.LegacyResourceConsumers - value: '::free;::realloc;::freopen;::fclose' - - key: bugprone-reserved-identifier.Invert - value: 'false' - - key: modernize-use-auto.MinTypeNameLength - value: '5' - - key: bugprone-unused-return-value.CheckedFunctions - value: '::std::async;::std::launder;::std::remove;::std::remove_if;::std::unique;::std::unique_ptr::release;::std::basic_string::empty;::std::vector::empty;::std::back_inserter;::std::distance;::std::find;::std::find_if;::std::inserter;::std::lower_bound;::std::make_pair;::std::map::count;::std::map::find;::std::map::lower_bound;::std::multimap::equal_range;::std::multimap::upper_bound;::std::set::count;::std::set::find;::std::setfill;::std::setprecision;::std::setw;::std::upper_bound;::std::vector::at;::bsearch;::ferror;::feof;::isalnum;::isalpha;::isblank;::iscntrl;::isdigit;::isgraph;::islower;::isprint;::ispunct;::isspace;::isupper;::iswalnum;::iswprint;::iswspace;::isxdigit;::memchr;::memcmp;::strcmp;::strcoll;::strncmp;::strpbrk;::strrchr;::strspn;::strstr;::wcscmp;::access;::bind;::connect;::difftime;::dlsym;::fnmatch;::getaddrinfo;::getopt;::htonl;::htons;::iconv_open;::inet_addr;::isascii;::isatty;::mmap;::newlocale;::openat;::pathconf;::pthread_equal;::pthread_getspecific;::pthread_mutex_trylock;::readdir;::readlink;::recvmsg;::regexec;::scandir;::semget;::setjmp;::shm_open;::shmget;::sigismember;::strcasecmp;::strsignal;::ttyname' - - key: bugprone-narrowing-conversions.PedanticMode - value: 'false' - - key: cert-dcl51-cpp.AggressiveDependentMemberLookup - value: 'false' - - key: hicpp-use-auto.MinTypeNameLength - value: '5' - - key: readability-inconsistent-declaration-parameter-name.Strict - value: 'false' - - key: hicpp-use-override.IgnoreDestructors - value: 'false' - - key: cppcoreguidelines-macro-usage.CheckCapsOnly - value: 'false' - - key: readability-suspicious-call-argument.DiceDissimilarBelow - value: '60' - - key: cert-dcl37-c.AllowedIdentifiers - value: '' - - key: hicpp-use-emplace.SmartPointers - value: '::std::shared_ptr;::std::unique_ptr;::std::auto_ptr;::std::weak_ptr' - - key: hicpp-member-init.IgnoreArrays - value: 'false' - - key: openmp-exception-escape.IgnoredExceptions - value: '' - - key: readability-suspicious-call-argument.Equality - value: 'true' - - key: hicpp-use-override.AllowOverrideAndFinal - value: 'false' - - key: misc-uniqueptr-reset-release.IncludeStyle - value: llvm - - key: android-comparison-in-temp-failure-retry.RetryMacros - value: TEMP_FAILURE_RETRY - - key: hicpp-signed-bitwise.IgnorePositiveIntegerLiterals - value: 'false' - - key: bugprone-easily-swappable-parameters.QualifiersMix - value: 'false' - - key: cert-err09-cpp.WarnOnLargeObjects - value: 'false' - - key: bugprone-suspicious-string-compare.WarnOnImplicitComparison - value: 'true' - - key: bugprone-argument-comment.CommentNullPtrs - value: '0' - - key: hicpp-use-equals-delete.IgnoreMacros - value: 'true' - - key: cppcoreguidelines-narrowing-conversions.WarnOnFloatingPointNarrowingConversion - value: 'true' - - key: cppcoreguidelines-init-variables.IncludeStyle - value: llvm - - key: modernize-use-nodiscard.ReplacementString - value: '[[nodiscard]]' - - key: modernize-loop-convert.MakeReverseRangeHeader - value: '' - - key: readability-suspicious-call-argument.SuffixSimilarAbove - value: '30' - - key: misc-definitions-in-headers.HeaderFileExtensions - value: ';h;hh;hpp;hxx' - - key: hicpp-uppercase-literal-suffix.NewSuffixes - value: '' - - key: cppcoreguidelines-narrowing-conversions.WarnOnIntegerNarrowingConversion - value: 'true' - - key: bugprone-easily-swappable-parameters.IgnoredParameterNames - value: '"";iterator;Iterator;begin;Begin;end;End;first;First;last;Last;lhs;LHS;rhs;RHS' - - key: modernize-loop-convert.UseCxx20ReverseRanges - value: 'true' - - key: cppcoreguidelines-prefer-member-initializer.UseAssignment - value: 'false' - - key: hicpp-function-size.VariableThreshold - value: '4294967295' - - key: cert-oop57-cpp.MemSetNames - value: '' - - key: hicpp-no-malloc.Deallocations - value: '::free' - - key: performance-type-promotion-in-math-fn.IncludeStyle - value: llvm - - key: bugprone-suspicious-include.ImplementationFileExtensions - value: 'c;cc;cpp;cxx' - - key: modernize-loop-convert.MakeReverseRangeFunction - value: '' - - key: bugprone-suspicious-missing-comma.SizeThreshold - value: '5' - - key: readability-inconsistent-declaration-parameter-name.IgnoreMacros - value: 'true' - - key: readability-identifier-naming.IgnoreFailedSplit - value: 'false' - - key: hicpp-multiway-paths-covered.WarnOnMissingElse - value: 'false' - - key: readability-qualified-auto.AddConstToQualified - value: 'true' - - key: bugprone-sizeof-expression.WarnOnSizeOfThis - value: 'true' - - key: bugprone-string-constructor.WarnOnLargeLength - value: 'true' - - key: hicpp-no-malloc.Allocations - value: '::malloc;::calloc' - - key: cppcoreguidelines-explicit-virtual-functions.OverrideSpelling - value: override - - key: hicpp-use-noexcept.UseNoexceptFalse - value: 'true' - - key: readability-uppercase-literal-suffix.IgnoreMacros - value: 'true' - - key: cert-dcl59-cpp.HeaderFileExtensions - value: ';h;hh;hpp;hxx' - - key: bugprone-dynamic-static-initializers.HeaderFileExtensions - value: ';h;hh;hpp;hxx' - - key: modernize-make-shared.IgnoreMacros - value: 'true' - - key: bugprone-suspicious-enum-usage.StrictMode - value: 'false' - - key: performance-unnecessary-copy-initialization.AllowedTypes - value: '' - - key: bugprone-suspicious-missing-comma.MaxConcatenatedTokens - value: '5' - - key: readability-suspicious-call-argument.Levenshtein - value: 'true' - - key: modernize-use-transparent-functors.SafeMode - value: 'false' - - key: bugprone-not-null-terminated-result.WantToUseSafeFunctions - value: 'true' - - key: misc-throw-by-value-catch-by-reference.CheckThrowTemporaries - value: 'true' - - key: bugprone-string-constructor.LargeLengthThreshold - value: '8388608' - - key: readability-simplify-boolean-expr.ChainedConditionalAssignment - value: 'false' - - key: cppcoreguidelines-avoid-magic-numbers.IgnoreAllFloatingPointValues - value: 'false' - - key: cert-oop54-cpp.WarnOnlyIfThisHasSuspiciousField - value: 'false' - - key: cert-err09-cpp.CheckThrowTemporaries - value: 'true' - - key: performance-inefficient-vector-operation.EnableProto - value: 'false' - - key: bugprone-exception-escape.FunctionsThatShouldNotThrow - value: '' - - key: modernize-loop-convert.MaxCopySize - value: '16' - - key: readability-suspicious-call-argument.PrefixDissimilarBelow - value: '25' - - key: readability-function-size.LineThreshold - value: '4294967295' - - key: bugprone-easily-swappable-parameters.MinimumLength - value: '2' - - key: portability-simd-intrinsics.Suggest - value: 'false' - - key: cppcoreguidelines-pro-bounds-constant-array-index.GslHeader - value: '' - - key: modernize-use-override.IgnoreDestructors - value: 'false' - - key: modernize-make-shared.MakeSmartPtrFunctionHeader - value: memory - - key: modernize-make-shared.MakeSmartPtrFunction - value: 'std::make_shared' - - key: misc-non-private-member-variables-in-classes.IgnorePublicMemberVariables - value: 'false' - - key: bugprone-sizeof-expression.WarnOnSizeOfConstant - value: 'true' - - key: readability-redundant-string-init.StringNames - value: '::std::basic_string_view;::std::basic_string' - - key: modernize-make-unique.IgnoreDefaultInitialization - value: 'true' - - key: modernize-use-emplace.ContainersWithPushBack - value: '::std::vector;::std::list;::std::deque' - - key: readability-magic-numbers.IgnoreBitFieldsWidths - value: 'true' - - key: modernize-make-unique.IncludeStyle - value: llvm - - key: modernize-use-override.OverrideSpelling - value: override - - key: readability-suspicious-call-argument.LevenshteinDissimilarBelow - value: '50' - - key: bugprone-argument-comment.CommentStringLiterals - value: '0' - - key: concurrency-mt-unsafe.FunctionSet - value: any - - key: google-readability-braces-around-statements.ShortStatementLines - value: '1' - - key: bugprone-reserved-identifier.AllowedIdentifiers - value: '' - - key: cppcoreguidelines-pro-type-member-init.IgnoreArrays - value: 'false' - - key: readability-else-after-return.WarnOnUnfixable - value: 'true' - - key: cppcoreguidelines-avoid-magic-numbers.IgnoredFloatingPointValues - value: '1.0;100.0;' - - key: modernize-use-emplace.IgnoreImplicitConstructors - value: 'false' - - key: cppcoreguidelines-macro-usage.IgnoreCommandLineMacros - value: 'true' - - key: readability-suspicious-call-argument.Substring - value: 'true' - - key: objc-forbidden-subclassing.ForbiddenSuperClassNames - value: 'ABNewPersonViewController;ABPeoplePickerNavigationController;ABPersonViewController;ABUnknownPersonViewController;NSHashTable;NSMapTable;NSPointerArray;NSPointerFunctions;NSTimer;UIActionSheet;UIAlertView;UIImagePickerController;UITextInputMode;UIWebView' - - key: modernize-use-equals-delete.IgnoreMacros - value: 'true' - - key: readability-magic-numbers.IgnoreAllFloatingPointValues - value: 'false' - - key: cppcoreguidelines-pro-bounds-constant-array-index.IncludeStyle - value: llvm - - key: hicpp-use-auto.RemoveStars - value: 'false' - - key: readability-suspicious-call-argument.Abbreviations - value: 'arr=array;cnt=count;idx=index;src=source;stmt=statement;cpy=copy;dest=destination;dist=distancedst=distance;ptr=pointer;wdth=width;str=string;ln=line;srv=server;attr=attribute;ref=reference;buf=buffer;col=column;nr=number;vec=vector;len=length;elem=element;val=value;i=index;var=variable;hght=height;cl=client;num=number;pos=position;lst=list;addr=address' - - key: bugprone-misplaced-widening-cast.CheckImplicitCasts - value: 'false' - - key: readability-uppercase-literal-suffix.NewSuffixes - value: '' - - key: modernize-loop-convert.MinConfidence - value: reasonable - - key: performance-unnecessary-value-param.AllowedTypes - value: '' - - key: readability-uniqueptr-delete-release.PreferResetCall - value: 'false' - - key: misc-throw-by-value-catch-by-reference.MaxSize - value: '64' - - key: misc-definitions-in-headers.UseHeaderFileExtension - value: 'true' - - key: google-readability-namespace-comments.SpacesBeforeComments - value: '2' - - key: cppcoreguidelines-avoid-magic-numbers.IgnoreBitFieldsWidths - value: 'true' - - key: cert-err61-cpp.CheckThrowTemporaries - value: 'true' - - key: cppcoreguidelines-avoid-magic-numbers.IgnoredIntegerValues - value: '1;2;3;4;' - - key: cppcoreguidelines-no-malloc.Allocations - value: '::malloc;::calloc' - - key: cppcoreguidelines-avoid-magic-numbers.IgnorePowersOf2IntegerValues - value: 'false' - - key: bugprone-narrowing-conversions.IgnoreConversionFromTypes - value: '' - - key: readability-function-size.BranchThreshold - value: '4294967295' - - key: bugprone-suspicious-missing-comma.RatioThreshold - value: '0.200000' - - key: hicpp-function-size.LineThreshold - value: '4294967295' - - key: readability-implicit-bool-conversion.AllowIntegerConditions - value: 'false' - - key: readability-function-size.StatementThreshold - value: '800' - - key: hicpp-use-noexcept.ReplacementString - value: '' - - key: readability-identifier-naming.IgnoreMainLikeFunctions - value: 'false' - - key: cppcoreguidelines-init-variables.MathHeader - value: math.h - - key: google-readability-function-size.StatementThreshold - value: '800' - - key: cert-msc51-cpp.DisallowedSeedTypes - value: 'time_t,std::time_t' - - key: hicpp-use-emplace.TupleMakeFunctions - value: '::std::make_pair;::std::make_tuple' - - key: bugprone-reserved-identifier.AggressiveDependentMemberLookup - value: 'false' - - key: readability-suspicious-call-argument.DiceSimilarAbove - value: '70' - - key: modernize-use-equals-default.IgnoreMacros - value: 'true' - - key: readability-suspicious-call-argument.Abbreviation - value: 'true' - - key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor - value: 'false' - - key: cert-dcl37-c.AggressiveDependentMemberLookup - value: 'false' - - key: modernize-use-emplace.SmartPointers - value: '::std::shared_ptr;::std::unique_ptr;::std::auto_ptr;::std::weak_ptr' - - key: cppcoreguidelines-no-malloc.Deallocations - value: '::free' - - key: bugprone-dangling-handle.HandleClasses - value: 'std::basic_string_view;std::experimental::basic_string_view' - - key: readability-magic-numbers.IgnorePowersOf2IntegerValues - value: 'false' - - key: misc-unused-parameters.StrictMode - value: 'false' - - key: readability-suspicious-call-argument.JaroWinklerSimilarAbove - value: '85' - - key: readability-simplify-subscript-expr.Types - value: '::std::basic_string;::std::basic_string_view;::std::vector;::std::array' - - key: performance-unnecessary-copy-initialization.ExcludedContainerTypes - value: '' - - key: modernize-replace-auto-ptr.IncludeStyle - value: llvm - - key: performance-move-const-arg.CheckTriviallyCopyableMove - value: 'true' - - key: hicpp-move-const-arg.CheckTriviallyCopyableMove - value: 'true' - - key: readability-function-size.VariableThreshold - value: '4294967295' - - key: readability-static-accessed-through-instance.NameSpecifierNestingThreshold - value: '3' - - key: cert-dcl16-c.NewSuffixes - value: 'L;LL;LU;LLU' - - key: bugprone-narrowing-conversions.WarnOnFloatingPointNarrowingConversion - value: 'true' - - key: readability-identifier-naming.GetConfigPerFile - value: 'true' - - key: cert-err61-cpp.MaxSize - value: '64' - - key: cert-sig30-c.AsyncSafeFunctionSet - value: POSIX - - key: hicpp-member-init.UseAssignment - value: 'false' - - key: modernize-use-default-member-init.UseAssignment - value: 'false' - - key: readability-function-size.NestingThreshold - value: '4294967295' - - key: modernize-use-override.AllowOverrideAndFinal - value: 'false' - - key: cppcoreguidelines-narrowing-conversions.IgnoreConversionFromTypes - value: '' - - key: readability-function-size.ParameterThreshold - value: '4294967295' - - key: hicpp-function-size.NestingThreshold - value: '4294967295' - - key: modernize-pass-by-value.ValuesOnly - value: 'false' - - key: modernize-loop-convert.IncludeStyle - value: llvm - - key: cert-str34-c.DiagnoseSignedUnsignedCharComparisons - value: 'false' - - key: bugprone-narrowing-conversions.WarnWithinTemplateInstantiation - value: 'false' - - key: bugprone-suspicious-string-compare.WarnOnLogicalNotComparison - value: 'false' - - key: cppcoreguidelines-explicit-virtual-functions.AllowOverrideAndFinal - value: 'false' - - key: hicpp-braces-around-statements.ShortStatementLines - value: '0' - - key: readability-redundant-smartptr-get.IgnoreMacros - value: 'true' - - key: readability-identifier-naming.AggressiveDependentMemberLookup - value: 'false' - - key: cert-err61-cpp.WarnOnLargeObjects - value: 'false' - - key: modernize-use-emplace.TupleTypes - value: '::std::pair;::std::tuple' - - key: hicpp-use-emplace.IgnoreImplicitConstructors - value: 'false' - - key: modernize-use-emplace.TupleMakeFunctions - value: '::std::make_pair;::std::make_tuple' - - key: cppcoreguidelines-owning-memory.LegacyResourceProducers - value: '::malloc;::aligned_alloc;::realloc;::calloc;::fopen;::freopen;::tmpfile' - - key: hicpp-uppercase-literal-suffix.IgnoreMacros - value: 'true' - - key: bugprone-easily-swappable-parameters.SuppressParametersUsedTogether - value: 'true' - - key: bugprone-argument-comment.StrictMode - value: '0' - - key: misc-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic - value: 'false' - - key: modernize-replace-random-shuffle.IncludeStyle - value: llvm - - key: modernize-use-bool-literals.IgnoreMacros - value: 'true' - - key: bugprone-easily-swappable-parameters.NamePrefixSuffixSilenceDissimilarityTreshold - value: '1' - - key: bugprone-unhandled-self-assignment.WarnOnlyIfThisHasSuspiciousField - value: 'true' - - key: google-readability-namespace-comments.ShortNamespaceLines - value: '10' - - key: readability-suspicious-call-argument.JaroWinklerDissimilarBelow - value: '75' - - key: bugprone-suspicious-string-compare.StringCompareLikeFunctions - value: '' - - key: modernize-avoid-bind.PermissiveParameterList - value: 'false' - - key: readability-suspicious-call-argument.Suffix - value: 'true' - - key: cert-err09-cpp.MaxSize - value: '64' - - key: modernize-use-override.FinalSpelling - value: final - - key: hicpp-use-equals-default.IgnoreMacros - value: 'true' - - key: modernize-use-noexcept.ReplacementString - value: '' - - key: hicpp-use-override.OverrideSpelling - value: override - - key: modernize-use-using.IgnoreMacros - value: 'true' - - key: hicpp-use-override.FinalSpelling - value: final - - key: cppcoreguidelines-explicit-virtual-functions.FinalSpelling - value: final - - key: readability-suspicious-call-argument.MinimumIdentifierNameLength - value: '3' - - key: bugprone-narrowing-conversions.WarnOnIntegerNarrowingConversion - value: 'true' - - key: modernize-loop-convert.NamingStyle - value: CamelCase - - key: cppcoreguidelines-pro-type-member-init.UseAssignment - value: 'false' - - key: bugprone-suspicious-include.HeaderFileExtensions - value: ';h;hh;hpp;hxx' - - key: hicpp-function-size.StatementThreshold - value: '800' - - key: readability-suspicious-call-argument.SubstringDissimilarBelow - value: '40' - - key: hicpp-no-malloc.Reallocations - value: '::realloc' - - key: performance-no-automatic-move.AllowedTypes - value: '' - - key: performance-for-range-copy.WarnOnAllAutoCopies - value: 'false' - - key: bugprone-argument-comment.CommentIntegerLiterals - value: '0' - - key: modernize-pass-by-value.IncludeStyle - value: llvm - - key: bugprone-argument-comment.CommentFloatLiterals - value: '0' - - key: bugprone-too-small-loop-variable.MagnitudeBitsUpperLimit - value: '16' - - key: readability-simplify-boolean-expr.ChainedConditionalReturn - value: 'false' - - key: readability-else-after-return.WarnOnConditionVariables - value: 'true' - - key: modernize-use-nullptr.NullMacros - value: 'NULL' - - key: readability-suspicious-call-argument.SuffixDissimilarBelow - value: '25' - - key: bugprone-argument-comment.CommentCharacterLiterals - value: '0' - - key: cppcoreguidelines-macro-usage.AllowedRegexp - value: '^DEBUG_*' - - key: readability-suspicious-call-argument.LevenshteinSimilarAbove - value: '66' - - key: cppcoreguidelines-narrowing-conversions.PedanticMode - value: 'false' - - key: modernize-make-shared.IgnoreDefaultInitialization - value: 'true' - - key: readability-suspicious-call-argument.JaroWinkler - value: 'true' - - key: bugprone-implicit-widening-of-multiplication-result.UseCXXHeadersInCppSources - value: 'true' - - key: modernize-make-shared.IncludeStyle - value: llvm - - key: readability-suspicious-call-argument.Prefix - value: 'true' - - key: hicpp-special-member-functions.AllowMissingMoveFunctions - value: 'false' - - key: cppcoreguidelines-special-member-functions.AllowMissingMoveFunctions - value: 'false' - - key: bugprone-implicit-widening-of-multiplication-result.UseCXXStaticCastsInCppSources - value: 'true' - - key: bugprone-signed-char-misuse.CharTypdefsToIgnore - value: '' - - key: cert-dcl51-cpp.Invert - value: 'false' - - key: hicpp-special-member-functions.AllowSoleDefaultDtor - value: 'false' - - key: cppcoreguidelines-explicit-virtual-functions.IgnoreDestructors - value: 'true' - - key: modernize-make-unique.IgnoreMacros - value: 'true' - - key: performance-for-range-copy.AllowedTypes - value: '' - - key: hicpp-function-size.BranchThreshold - value: '4294967295' - - key: bugprone-argument-comment.CommentBoolLiterals - value: '0' - - key: readability-braces-around-statements.ShortStatementLines - value: '0' - - key: bugprone-argument-comment.CommentUserDefinedLiterals - value: '0' - - key: hicpp-use-emplace.ContainersWithPushBack - value: '::std::vector;::std::list;::std::deque' - - key: hicpp-special-member-functions.AllowMissingMoveFunctionsWhenCopyIsDeleted - value: 'false' - - key: readability-magic-numbers.IgnoredFloatingPointValues - value: '1.0;100.0;' - - key: performance-inefficient-string-concatenation.StrictMode - value: 'false' - - key: readability-redundant-declaration.IgnoreMacros - value: 'true' - - key: bugprone-easily-swappable-parameters.IgnoredParameterTypeSuffixes - value: 'bool;Bool;_Bool;it;It;iterator;Iterator;inputit;InputIt;forwardit;FowardIt;bidirit;BidirIt;constiterator;const_iterator;Const_Iterator;Constiterator;ConstIterator;RandomIt;randomit;random_iterator;ReverseIt;reverse_iterator;reverse_const_iterator;ConstReverseIterator;Const_Reverse_Iterator;const_reverse_iterator;Constreverseiterator;constreverseiterator' - - key: modernize-make-unique.MakeSmartPtrFunction - value: 'std::make_unique' - - key: hicpp-function-size.ParameterThreshold - value: '4294967295' - - key: portability-restrict-system-includes.Includes - value: '*' - - key: cert-dcl51-cpp.AllowedIdentifiers - value: '' - - key: cert-oop57-cpp.MemCpyNames - value: '' - - key: modernize-make-unique.MakeSmartPtrFunctionHeader - value: memory - - key: bugprone-signal-handler.AsyncSafeFunctionSet - value: POSIX - - key: bugprone-easily-swappable-parameters.ModelImplicitConversions - value: 'true' - - key: readability-suspicious-call-argument.SubstringSimilarAbove - value: '50' - - key: cppcoreguidelines-narrowing-conversions.WarnWithinTemplateInstantiation - value: 'false' - - key: readability-implicit-bool-conversion.AllowPointerConditions - value: 'false' - - key: cppcoreguidelines-narrowing-conversions.WarnOnEquivalentBitWidth - value: 'true' - - key: hicpp-use-emplace.TupleTypes - value: '::std::pair;::std::tuple' - - key: cppcoreguidelines-non-private-member-variables-in-classes.IgnorePublicMemberVariables - value: 'false' - - key: cppcoreguidelines-special-member-functions.AllowMissingMoveFunctionsWhenCopyIsDeleted - value: 'false' - - key: cert-oop57-cpp.MemCmpNames - value: '' - - key: modernize-use-noexcept.UseNoexceptFalse - value: 'true' - - key: cppcoreguidelines-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic - value: 'true' - - key: bugprone-argument-comment.IgnoreSingleArgument - value: '0' - - key: bugprone-narrowing-conversions.WarnOnEquivalentBitWidth - value: 'true' - - key: bugprone-sizeof-expression.WarnOnSizeOfIntegerExpression - value: 'false' - - key: performance-faster-string-find.StringLikeClasses - value: '::std::basic_string;::std::basic_string_view' - - key: bugprone-assert-side-effect.CheckFunctionCalls - value: 'false' - - key: bugprone-string-constructor.StringNames - value: '::std::basic_string;::std::basic_string_view' - - key: bugprone-assert-side-effect.AssertMacros - value: assert - - key: bugprone-exception-escape.IgnoredExceptions - value: '' - - key: bugprone-signed-char-misuse.DiagnoseSignedUnsignedCharComparisons - value: 'true' - - key: modernize-use-default-member-init.IgnoreMacros - value: 'true' - - key: llvm-qualified-auto.AddConstToQualified - value: 'false' - - key: cert-str34-c.CharTypdefsToIgnore - value: '' - - key: llvm-else-after-return.WarnOnConditionVariables - value: 'false' - - key: bugprone-sizeof-expression.WarnOnSizeOfCompareToConstant - value: 'true' - - key: modernize-raw-string-literal.DelimiterStem - value: lit - - key: readability-suspicious-call-argument.Dice - value: 'true' - - key: misc-throw-by-value-catch-by-reference.WarnOnLargeObjects - value: 'false' - - key: cert-dcl37-c.Invert - value: 'false' - - key: modernize-raw-string-literal.ReplaceShorterLiterals - value: 'false' - - key: performance-inefficient-vector-operation.VectorLikeClasses - value: '::std::vector' - - key: readability-magic-numbers.IgnoredIntegerValues - value: '1;2;3;4;5;6' - - key: modernize-use-auto.RemoveStars - value: 'false' - - key: bugprone-implicit-widening-of-multiplication-result.IncludeStyle - value: llvm - - key: portability-simd-intrinsics.Std - value: '' - - key: readability-redundant-member-init.IgnoreBaseInCopyConstructors - value: 'false' - - key: hicpp-use-nullptr.NullMacros - value: '' - - key: cert-dcl16-c.IgnoreMacros - value: 'true' - - key: performance-unnecessary-value-param.IncludeStyle - value: llvm - - key: llvm-else-after-return.WarnOnUnfixable - value: 'false' - - key: modernize-replace-disallow-copy-and-assign-macro.MacroName - value: DISALLOW_COPY_AND_ASSIGN + readability-suspicious-call-argument.JaroWinklerSimilarAbove: '85' + cppcoreguidelines-pro-bounds-constant-array-index.IncludeStyle: llvm + performance-inefficient-vector-operation.VectorLikeClasses: '::std::vector' + misc-const-correctness.TransformValues: true + cppcoreguidelines-narrowing-conversions.IgnoreConversionFromTypes: '' + readability-identifier-naming.IgnoreMainLikeFunctions: false + bugprone-argument-comment.CommentFloatLiterals: '0' + readability-magic-numbers.IgnoredIntegerValues: '1;2;3;4;5;6' + modernize-use-emplace.ContainersWithPush: '::std::stack;::std::queue;::std::priority_queue' + misc-const-correctness.AnalyzeValues: true + hicpp-special-member-functions.AllowMissingMoveFunctionsWhenCopyIsDeleted: 'false' + modernize-use-bool-literals.IgnoreMacros: true + bugprone-sizeof-expression.WarnOnSizeOfThis: true + performance-unnecessary-value-param.AllowedTypes: '' + modernize-replace-auto-ptr.IncludeStyle: llvm + modernize-use-std-print.PrintfLikeFunctions: '::printf;absl::PrintF' + bugprone-suspicious-missing-comma.MaxConcatenatedTokens: 5 + portability-simd-intrinsics.Std: '' + modernize-make-unique.IgnoreMacros: true + modernize-use-emplace.ContainersWithPushFront: '::std::forward_list;::std::list;::std::deque' + modernize-use-std-print.ReplacementPrintlnFunction: 'std::println' + misc-definitions-in-headers.HeaderFileExtensions: ';h;hh;hpp;hxx' + cppcoreguidelines-pro-type-member-init.UseAssignment: false + cppcoreguidelines-narrowing-conversions.WarnOnEquivalentBitWidth: true + cppcoreguidelines-owning-memory.LegacyResourceConsumers: '::free;::realloc;::freopen;::fclose' + cppcoreguidelines-narrowing-conversions.PedanticMode: false + bugprone-dangling-handle.HandleClasses: 'std::basic_string_view;std::experimental::basic_string_view' + readability-suspicious-call-argument.SuffixDissimilarBelow: '25' + cppcoreguidelines-avoid-magic-numbers.IgnoredFloatingPointValues: '1.0;100.0;' + readability-uppercase-literal-suffix.IgnoreMacros: true + readability-suspicious-call-argument.Dice: 'true' + llvm-else-after-return.WarnOnUnfixable: 'false' + readability-container-data-pointer.IgnoredContainers: '' + bugprone-exception-escape.IgnoredExceptions: '' + readability-suspicious-call-argument.DiceDissimilarBelow: '60' + hicpp-use-noexcept.UseNoexceptFalse: 'true' + hicpp-use-emplace.SmartPointers: '::std::shared_ptr;::std::unique_ptr;::std::auto_ptr;::std::weak_ptr' + cppcoreguidelines-no-malloc.Deallocations: '::free' + hicpp-use-override.FinalSpelling: final + modernize-deprecated-headers.CheckHeaderFile: false + modernize-make-unique.MakeSmartPtrFunction: 'std::make_unique' + cppcoreguidelines-avoid-magic-numbers.IgnoreUserDefinedLiterals: 'false' + readability-simplify-boolean-expr.ChainedConditionalReturn: false + bugprone-reserved-identifier.Invert: false + concurrency-mt-unsafe.FunctionSet: any + cppcoreguidelines-avoid-magic-numbers.IgnoredIntegerValues: '1;2;3;4;' + bugprone-string-constructor.LargeLengthThreshold: 8388608 + cert-str34-c.DiagnoseSignedUnsignedCharComparisons: 'false' + bugprone-too-small-loop-variable.MagnitudeBitsUpperLimit: '16' + hicpp-braces-around-statements.ShortStatementLines: '0' + cppcoreguidelines-owning-memory.LegacyResourceProducers: '::malloc;::aligned_alloc;::realloc;::calloc;::fopen;::freopen;::tmpfile' + cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor: false + cert-err09-cpp.CheckThrowTemporaries: 'true' + cppcoreguidelines-no-malloc.Allocations: '::malloc;::calloc' + bugprone-suspicious-missing-comma.RatioThreshold: '0.200000' + modernize-use-override.FinalSpelling: final + cert-msc32-c.DisallowedSeedTypes: 'time_t,std::time_t' + cppcoreguidelines-avoid-do-while.IgnoreMacros: 'false' + modernize-loop-convert.MakeReverseRangeFunction: '' + google-readability-namespace-comments.ShortNamespaceLines: '10' + modernize-replace-disallow-copy-and-assign-macro.MacroName: DISALLOW_COPY_AND_ASSIGN + bugprone-argument-comment.CommentStringLiterals: '0' + readability-operators-representation.BinaryOperators: '' + modernize-make-shared.IgnoreDefaultInitialization: 1 + cppcoreguidelines-rvalue-reference-param-not-moved.AllowPartialMove: 'false' + bugprone-sizeof-expression.WarnOnSizeOfConstant: true + bugprone-implicit-widening-of-multiplication-result.IncludeStyle: llvm + modernize-use-equals-default.IgnoreMacros: true + readability-suspicious-call-argument.MinimumIdentifierNameLength: 3 + bugprone-suspicious-include.HeaderFileExtensions: ';h;hh;hpp;hxx' + hicpp-use-noexcept.ReplacementString: '' + readability-else-after-return.WarnOnConditionVariables: true + modernize-use-std-print.ReplacementPrintFunction: 'std::print' + modernize-use-override.IgnoreDestructors: false + bugprone-empty-catch.AllowEmptyCatchForExceptions: '' + performance-inefficient-vector-operation.EnableProto: false + hicpp-use-override.IgnoreTemplateInstantiations: 'false' + cppcoreguidelines-rvalue-reference-param-not-moved.IgnoreUnnamedParams: 'false' + bugprone-unchecked-optional-access.IgnoreSmartPointerDereference: 'false' + misc-unused-parameters.IgnoreVirtual: 'false' + modernize-use-std-print.StrictMode: 'false' + performance-for-range-copy.AllowedTypes: '' + hicpp-use-emplace.ContainersWithPush: '::std::stack;::std::queue;::std::priority_queue' + bugprone-easily-swappable-parameters.MinimumLength: 2 + cert-dcl16-c.IgnoreMacros: 'true' + readability-suspicious-call-argument.LevenshteinSimilarAbove: '66' + cert-oop57-cpp.MemSetNames: '' + readability-redundant-declaration.IgnoreMacros: true + modernize-replace-random-shuffle.IncludeStyle: llvm + readability-operators-representation.OverloadedOperators: '' + misc-throw-by-value-catch-by-reference.WarnOnLargeObjects: 'false' + cert-err09-cpp.MaxSize: '64' + bugprone-suspicious-enum-usage.StrictMode: 0 + readability-suspicious-call-argument.LevenshteinDissimilarBelow: '50' + objc-forbidden-subclassing.ForbiddenSuperClassNames: 'ABNewPersonViewController;ABPeoplePickerNavigationController;ABPersonViewController;ABUnknownPersonViewController;NSHashTable;NSMapTable;NSPointerArray;NSPointerFunctions;NSTimer;UIActionSheet;UIAlertView;UIImagePickerController;UITextInputMode;UIWebView' + modernize-use-emplace.TupleTypes: '::std::pair;::std::tuple' + bugprone-sizeof-expression.WarnOnSizeOfIntegerExpression: false + readability-qualified-auto.AddConstToQualified: true + bugprone-unhandled-self-assignment.WarnOnlyIfThisHasSuspiciousField: true + readability-simplify-boolean-expr.SimplifyDeMorganRelaxed: false + readability-magic-numbers.IgnoreAllFloatingPointValues: false + misc-throw-by-value-catch-by-reference.CheckThrowTemporaries: true + performance-unnecessary-copy-initialization.AllowedTypes: '' + readability-uniqueptr-delete-release.PreferResetCall: false + hicpp-use-auto.MinTypeNameLength: '5' + hicpp-use-equals-delete.IgnoreMacros: 'true' + cert-msc33-c.ReportMoreUnsafeFunctions: 'true' + cert-dcl59-cpp.HeaderFileExtensions: ';h;hh;hpp;hxx' + misc-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic: false + readability-magic-numbers.IgnorePowersOf2IntegerValues: false + bugprone-suspicious-missing-comma.SizeThreshold: 5 + readability-suspicious-call-argument.Equality: 'true' + modernize-use-std-print.FprintfLikeFunctions: '::fprintf;absl::FPrintF' + readability-identifier-length.MinimumParameterNameLength: 3 + hicpp-use-emplace.ContainersWithPushFront: '::std::forward_list;::std::list;::std::deque' + readability-braces-around-statements.ShortStatementLines: 0 + performance-unnecessary-value-param.IncludeStyle: llvm + modernize-use-auto.MinTypeNameLength: 5 + hicpp-use-equals-default.IgnoreMacros: 'true' + readability-simplify-boolean-expr.SimplifyDeMorgan: true + readability-magic-numbers.IgnoreTypeAliases: 'false' + android-comparison-in-temp-failure-retry.RetryMacros: TEMP_FAILURE_RETRY + modernize-use-override.IgnoreTemplateInstantiations: 'false' + bugprone-easily-swappable-parameters.NamePrefixSuffixSilenceDissimilarityTreshold: 1 + misc-definitions-in-headers.UseHeaderFileExtension: true + bugprone-suspicious-string-compare.WarnOnLogicalNotComparison: false + cert-dcl51-cpp.AggressiveDependentMemberLookup: 'false' + readability-suspicious-call-argument.Prefix: 'true' + readability-suspicious-call-argument.SubstringSimilarAbove: '50' + readability-identifier-naming.GetConfigPerFile: true + bugprone-unsafe-functions.ReportMoreUnsafeFunctions: 'true' + readability-magic-numbers.IgnoreBitFieldsWidths: true + misc-uniqueptr-reset-release.IncludeStyle: llvm + readability-redundant-string-init.StringNames: '::std::basic_string_view;::std::basic_string' + cppcoreguidelines-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic: 'true' + cert-oop57-cpp.MemCpyNames: '' + cppcoreguidelines-special-member-functions.AllowMissingMoveFunctionsWhenCopyIsDeleted: false + google-readability-function-size.StatementThreshold: '800' + hicpp-member-init.UseAssignment: 'false' + readability-suspicious-call-argument.DiceSimilarAbove: '70' + performance-faster-string-find.StringLikeClasses: '::std::basic_string;::std::basic_string_view' + modernize-use-emplace.EmplacyFunctions: 'vector::emplace_back;vector::emplace;deque::emplace;deque::emplace_front;deque::emplace_back;forward_list::emplace_after;forward_list::emplace_front;list::emplace;list::emplace_back;list::emplace_front;set::emplace;set::emplace_hint;map::emplace;map::emplace_hint;multiset::emplace;multiset::emplace_hint;multimap::emplace;multimap::emplace_hint;unordered_set::emplace;unordered_set::emplace_hint;unordered_map::emplace;unordered_map::emplace_hint;unordered_multiset::emplace;unordered_multiset::emplace_hint;unordered_multimap::emplace;unordered_multimap::emplace_hint;stack::emplace;queue::emplace;priority_queue::emplace' + cert-err33-c.CheckedFunctions: '::aligned_alloc;::asctime_s;::at_quick_exit;::atexit;::bsearch;::bsearch_s;::btowc;::c16rtomb;::c32rtomb;::calloc;::clock;::cnd_broadcast;::cnd_init;::cnd_signal;::cnd_timedwait;::cnd_wait;::ctime_s;::fclose;::fflush;::fgetc;::fgetpos;::fgets;::fgetwc;::fopen;::fopen_s;::fprintf;::fprintf_s;::fputc;::fputs;::fputwc;::fputws;::fread;::freopen;::freopen_s;::fscanf;::fscanf_s;::fseek;::fsetpos;::ftell;::fwprintf;::fwprintf_s;::fwrite;::fwscanf;::fwscanf_s;::getc;::getchar;::getenv;::getenv_s;::gets_s;::getwc;::getwchar;::gmtime;::gmtime_s;::localtime;::localtime_s;::malloc;::mbrtoc16;::mbrtoc32;::mbsrtowcs;::mbsrtowcs_s;::mbstowcs;::mbstowcs_s;::memchr;::mktime;::mtx_init;::mtx_lock;::mtx_timedlock;::mtx_trylock;::mtx_unlock;::printf_s;::putc;::putwc;::raise;::realloc;::remove;::rename;::scanf;::scanf_s;::setlocale;::setvbuf;::signal;::snprintf;::snprintf_s;::sprintf;::sprintf_s;::sscanf;::sscanf_s;::strchr;::strerror_s;::strftime;::strpbrk;::strrchr;::strstr;::strtod;::strtof;::strtoimax;::strtok;::strtok_s;::strtol;::strtold;::strtoll;::strtoul;::strtoull;::strtoumax;::strxfrm;::swprintf;::swprintf_s;::swscanf;::swscanf_s;::thrd_create;::thrd_detach;::thrd_join;::thrd_sleep;::time;::timespec_get;::tmpfile;::tmpfile_s;::tmpnam;::tmpnam_s;::tss_create;::tss_get;::tss_set;::ungetc;::ungetwc;::vfprintf;::vfprintf_s;::vfscanf;::vfscanf_s;::vfwprintf;::vfwprintf_s;::vfwscanf;::vfwscanf_s;::vprintf_s;::vscanf;::vscanf_s;::vsnprintf;::vsnprintf_s;::vsprintf;::vsprintf_s;::vsscanf;::vsscanf_s;::vswprintf;::vswprintf_s;::vswscanf;::vswscanf_s;::vwprintf_s;::vwscanf;::vwscanf_s;::wcrtomb;::wcschr;::wcsftime;::wcspbrk;::wcsrchr;::wcsrtombs;::wcsrtombs_s;::wcsstr;::wcstod;::wcstof;::wcstoimax;::wcstok;::wcstok_s;::wcstol;::wcstold;::wcstoll;::wcstombs;::wcstombs_s;::wcstoul;::wcstoull;::wcstoumax;::wcsxfrm;::wctob;::wctrans;::wctype;::wmemchr;::wprintf_s;::wscanf;::wscanf_s;' + bugprone-assert-side-effect.IgnoredFunctions: __builtin_expect + hicpp-signed-bitwise.IgnorePositiveIntegerLiterals: true + bugprone-string-constructor.StringNames: '::std::basic_string;::std::basic_string_view' + bugprone-exception-escape.FunctionsThatShouldNotThrow: '' + hicpp-member-init.IgnoreArrays: 'false' + misc-throw-by-value-catch-by-reference.MaxSize: 64 + cppcoreguidelines-narrowing-conversions.WarnOnIntegerToFloatingPointNarrowingConversion: true + readability-identifier-length.MinimumLoopCounterNameLength: 2 + readability-static-accessed-through-instance.NameSpecifierNestingThreshold: '3' + cert-err09-cpp.WarnOnLargeObjects: 'false' + performance-unnecessary-copy-initialization.ExcludedContainerTypes: '' + bugprone-non-zero-enum-to-bool-conversion.EnumIgnoreList: '' + readability-else-after-return.WarnOnUnfixable: true + bugprone-narrowing-conversions.IgnoreConversionFromTypes: '' + modernize-raw-string-literal.ReplaceShorterLiterals: 'false' + modernize-use-emplace.TupleMakeFunctions: '::std::make_pair;::std::make_tuple' + cppcoreguidelines-explicit-virtual-functions.OverrideSpelling: override + cppcoreguidelines-avoid-magic-numbers.IgnoreAllFloatingPointValues: 'false' + cppcoreguidelines-pro-bounds-constant-array-index.GslHeader: '' + cppcoreguidelines-init-variables.IncludeStyle: llvm + cppcoreguidelines-rvalue-reference-param-not-moved.IgnoreNonDeducedTemplateTypes: 'false' + performance-for-range-copy.WarnOnAllAutoCopies: false + cert-err61-cpp.CheckThrowTemporaries: 'true' + modernize-use-nodiscard.ReplacementString: '[[nodiscard]]' + cppcoreguidelines-narrowing-conversions.WarnWithinTemplateInstantiation: false + bugprone-assert-side-effect.CheckFunctionCalls: false + hicpp-use-emplace.IgnoreImplicitConstructors: 'false' + cert-dcl37-c.AllowedIdentifiers: '' + modernize-loop-convert.NamingStyle: CamelCase + modernize-make-shared.MakeSmartPtrFunction: 'std::make_shared' + modernize-avoid-bind.PermissiveParameterList: false + readability-suspicious-call-argument.Levenshtein: 'true' + readability-suspicious-call-argument.JaroWinklerDissimilarBelow: '75' + cppcoreguidelines-prefer-member-initializer.UseAssignment: 'false' + hicpp-special-member-functions.AllowSoleDefaultDtor: 'false' + hicpp-deprecated-headers.CheckHeaderFile: 'false' + cppcoreguidelines-narrowing-conversions.WarnOnIntegerNarrowingConversion: true + modernize-use-override.OverrideSpelling: override + hicpp-uppercase-literal-suffix.IgnoreMacros: 'true' + readability-const-return-type.IgnoreMacros: 'true' + modernize-use-transparent-functors.SafeMode: false + readability-identifier-naming.IgnoreFailedSplit: 'false' + bugprone-argument-comment.CommentUserDefinedLiterals: '0' + cert-dcl51-cpp.Invert: 'false' + portability-simd-intrinsics.Suggest: false + hicpp-use-emplace.ContainersWithPushBack: '::std::vector;::std::list;::std::deque' + misc-header-include-cycle.IgnoredFilesList: '' + readability-simplify-subscript-expr.Types: '::std::basic_string;::std::basic_string_view;::std::vector;::std::array' + cppcoreguidelines-special-member-functions.AllowMissingMoveFunctions: false + bugprone-signed-char-misuse.CharTypdefsToIgnore: '' + cert-dcl37-c.AggressiveDependentMemberLookup: 'false' + readability-magic-numbers.IgnoredFloatingPointValues: '1.0;100.0;' + readability-identifier-length.IgnoredVariableNames: '' + performance-move-const-arg.CheckTriviallyCopyableMove: true + hicpp-no-malloc.Allocations: '::malloc;::calloc' + modernize-make-shared.IgnoreMacros: true + readability-suspicious-call-argument.SuffixSimilarAbove: '30' + modernize-use-noexcept.ReplacementString: '' + readability-suspicious-call-argument.SubstringDissimilarBelow: '40' + misc-const-correctness.TransformReferences: true + readability-implicit-bool-conversion.AllowPointerConditions: false + misc-unused-parameters.StrictMode: false + modernize-loop-convert.MakeReverseRangeHeader: '' + hicpp-use-nullptr.NullMacros: '' + hicpp-move-const-arg.CheckTriviallyCopyableMove: 'true' + bugprone-signal-handler.AsyncSafeFunctionSet: POSIX + modernize-loop-convert.MaxCopySize: '16' + cppcoreguidelines-no-malloc.Reallocations: '::realloc' + readability-suspicious-call-argument.JaroWinkler: 'true' + bugprone-argument-comment.CommentIntegerLiterals: '0' + bugprone-argument-comment.CommentNullPtrs: '0' + cert-oop54-cpp.WarnOnlyIfThisHasSuspiciousField: 'false' + modernize-make-unique.MakeSmartPtrFunctionHeader: memory + readability-suspicious-call-argument.Abbreviations: 'lst=list;arr=array;num=number;ptr=pointer;i=index;val=value;ln=line;cpy=copy;idx=index;vec=vector;src=source;dest=destination;wdth=width;nr=number;elem=element;srv=server;pos=position;cl=client;stmt=statement;len=length;buf=buffer;dist=distancedst=distance;hght=height;var=variable;ref=reference;col=column;attr=attribute;str=string;cnt=count;addr=address' + cppcoreguidelines-avoid-magic-numbers.IgnoreTypeAliases: 'false' + misc-use-anonymous-namespace.HeaderFileExtensions: ';h;hh;hpp;hxx' + bugprone-easily-swappable-parameters.ModelImplicitConversions: true + readability-redundant-member-init.IgnoreBaseInCopyConstructors: false + readability-suspicious-call-argument.PrefixDissimilarBelow: '25' + bugprone-narrowing-conversions.WarnOnIntegerNarrowingConversion: 'true' + bugprone-argument-comment.CommentCharacterLiterals: '0' + modernize-make-unique.IncludeStyle: llvm + cert-msc24-c.ReportMoreUnsafeFunctions: 'true' + readability-uppercase-literal-suffix.NewSuffixes: '' + cert-dcl16-c.NewSuffixes: 'L;LL;LU;LLU' + misc-const-correctness.WarnPointersAsValues: false + hicpp-uppercase-literal-suffix.NewSuffixes: '' + hicpp-no-malloc.Reallocations: '::realloc' + readability-suspicious-call-argument.Suffix: 'true' + cert-msc54-cpp.AsyncSafeFunctionSet: POSIX + bugprone-string-constructor.WarnOnLargeLength: true + cert-oop57-cpp.MemCmpNames: '' + modernize-use-std-print.IncludeStyle: llvm + modernize-use-emplace.SmartPointers: '::std::shared_ptr;::std::unique_ptr;::std::auto_ptr;::std::weak_ptr' + modernize-loop-convert.UseCxx20ReverseRanges: true + modernize-use-std-print.PrintHeader: '' + bugprone-argument-comment.StrictMode: false + bugprone-unused-return-value.CheckedFunctions: '::std::async;::std::launder;::std::remove;::std::remove_if;::std::unique;::std::unique_ptr::release;::std::basic_string::empty;::std::vector::empty;::std::back_inserter;::std::distance;::std::find;::std::find_if;::std::inserter;::std::lower_bound;::std::make_pair;::std::map::count;::std::map::find;::std::map::lower_bound;::std::multimap::equal_range;::std::multimap::upper_bound;::std::set::count;::std::set::find;::std::setfill;::std::setprecision;::std::setw;::std::upper_bound;::std::vector::at;::bsearch;::ferror;::feof;::isalnum;::isalpha;::isblank;::iscntrl;::isdigit;::isgraph;::islower;::isprint;::ispunct;::isspace;::isupper;::iswalnum;::iswprint;::iswspace;::isxdigit;::memchr;::memcmp;::strcmp;::strcoll;::strncmp;::strpbrk;::strrchr;::strspn;::strstr;::wcscmp;::access;::bind;::connect;::difftime;::dlsym;::fnmatch;::getaddrinfo;::getopt;::htonl;::htons;::iconv_open;::inet_addr;::isascii;::isatty;::mmap;::newlocale;::openat;::pathconf;::pthread_equal;::pthread_getspecific;::pthread_mutex_trylock;::readdir;::readlink;::recvmsg;::regexec;::scandir;::semget;::setjmp;::shm_open;::shmget;::sigismember;::strcasecmp;::strsignal;::ttyname' + bugprone-reserved-identifier.AggressiveDependentMemberLookup: 'false' + hicpp-use-override.IgnoreDestructors: 'false' + hicpp-special-member-functions.AllowMissingMoveFunctions: 'false' + misc-non-private-member-variables-in-classes.IgnorePublicMemberVariables: false + cppcoreguidelines-init-variables.MathHeader: math.h + modernize-make-unique.IgnoreDefaultInitialization: 1 + cppcoreguidelines-explicit-virtual-functions.FinalSpelling: final + modernize-loop-convert.IncludeStyle: llvm + cert-str34-c.CharTypdefsToIgnore: '' + modernize-use-nullptr.NullMacros: 'NULL' + cppcoreguidelines-macro-usage.AllowedRegexp: '^DEBUG_*' + readability-identifier-length.IgnoredLoopCounterNames: '^[ijk_]$' + cppcoreguidelines-pro-type-member-init.IgnoreArrays: false + cppcoreguidelines-explicit-virtual-functions.AllowOverrideAndFinal: 'false' + bugprone-argument-comment.CommentBoolLiterals: false + modernize-type-traits.IgnoreMacros: 'false' + bugprone-narrowing-conversions.WarnOnEquivalentBitWidth: 'true' + hicpp-use-auto.RemoveStars: 'false' + readability-identifier-length.IgnoredParameterNames: '^[n]$' + readability-identifier-length.IgnoredExceptionVariableNames: '^[e]$' + readability-magic-numbers.IgnoreUserDefinedLiterals: 'false' + bugprone-stringview-nullptr.IncludeStyle: llvm + cppcoreguidelines-explicit-virtual-functions.IgnoreTemplateInstantiations: 'false' + bugprone-narrowing-conversions.PedanticMode: 'false' + modernize-raw-string-literal.DelimiterStem: lit + cppcoreguidelines-explicit-virtual-functions.IgnoreDestructors: 'true' + performance-no-automatic-move.AllowedTypes: '' + hicpp-multiway-paths-covered.WarnOnMissingElse: false + llvm-qualified-auto.AddConstToQualified: 'false' + cppcoreguidelines-avoid-magic-numbers.IgnorePowersOf2IntegerValues: 'false' + bugprone-suspicious-include.ImplementationFileExtensions: 'c;cc;cpp;cxx' + readability-suspicious-call-argument.PrefixSimilarAbove: '30' + bugprone-dynamic-static-initializers.HeaderFileExtensions: ';h;hh;hpp;hxx' + google-readability-braces-around-statements.ShortStatementLines: '1' + hicpp-use-emplace.TupleTypes: '::std::pair;::std::tuple' + bugprone-easily-swappable-parameters.SuppressParametersUsedTogether: true + bugprone-narrowing-conversions.WarnWithinTemplateInstantiation: 'false' + bugprone-argument-comment.IgnoreSingleArgument: false + bugprone-narrowing-conversions.WarnOnFloatingPointNarrowingConversion: 'true' + modernize-use-using.IgnoreMacros: true + cppcoreguidelines-macro-usage.IgnoreCommandLineMacros: true + performance-inefficient-string-concatenation.StrictMode: false + bugprone-easily-swappable-parameters.QualifiersMix: false + bugprone-narrowing-conversions.WarnOnIntegerToFloatingPointNarrowingConversion: 'true' + hicpp-use-override.OverrideSpelling: override + cppcoreguidelines-use-default-member-init.IgnoreMacros: 'true' + misc-const-correctness.TransformPointersAsValues: false + cppcoreguidelines-use-default-member-init.UseAssignment: 'false' + modernize-loop-convert.MinConfidence: reasonable + modernize-make-shared.MakeSmartPtrFunctionHeader: memory + modernize-use-emplace.IgnoreImplicitConstructors: false + bugprone-implicit-widening-of-multiplication-result.UseCXXHeadersInCppSources: true + readability-simplify-boolean-expr.ChainedConditionalAssignment: false + readability-inconsistent-declaration-parameter-name.Strict: false + portability-restrict-system-includes.Includes: '*' + bugprone-suspicious-string-compare.StringCompareLikeFunctions: '' + performance-move-const-arg.CheckMoveToConstRef: true + misc-const-correctness.AnalyzeReferences: true + modernize-make-shared.IncludeStyle: llvm + bugprone-suspicious-string-compare.WarnOnImplicitComparison: true + bugprone-implicit-widening-of-multiplication-result.UseCXXStaticCastsInCppSources: true + cppcoreguidelines-narrowing-conversions.WarnOnFloatingPointNarrowingConversion: true + bugprone-unused-return-value.CheckedReturnTypes: '::std::error_code;::std::error_condition;::std::errc;::std::expected;::boost::system::error_code' + openmp-exception-escape.IgnoredExceptions: '' + hicpp-use-emplace.EmplacyFunctions: 'vector::emplace_back;vector::emplace;deque::emplace;deque::emplace_front;deque::emplace_back;forward_list::emplace_after;forward_list::emplace_front;list::emplace;list::emplace_back;list::emplace_front;set::emplace;set::emplace_hint;map::emplace;map::emplace_hint;multiset::emplace;multiset::emplace_hint;multimap::emplace;multimap::emplace_hint;unordered_set::emplace;unordered_set::emplace_hint;unordered_map::emplace;unordered_map::emplace_hint;unordered_multiset::emplace;unordered_multiset::emplace_hint;unordered_multimap::emplace;unordered_multimap::emplace_hint;stack::emplace;queue::emplace;priority_queue::emplace' + cert-sig30-c.AsyncSafeFunctionSet: POSIX + readability-avoid-const-params-in-decls.IgnoreMacros: true + readability-suspicious-call-argument.Abbreviation: 'true' + readability-implicit-bool-conversion.AllowIntegerConditions: false + readability-redundant-smartptr-get.IgnoreMacros: true + bugprone-misplaced-widening-cast.CheckImplicitCasts: false + cert-err61-cpp.WarnOnLargeObjects: 'false' + modernize-use-equals-delete.IgnoreMacros: true + modernize-use-emplace.ContainersWithPushBack: '::std::vector;::std::list;::std::deque' + modernize-use-default-member-init.IgnoreMacros: true + cert-msc51-cpp.DisallowedSeedTypes: 'time_t,std::time_t' + readability-inconsistent-declaration-parameter-name.IgnoreMacros: true + modernize-use-noexcept.UseNoexceptFalse: 'true' + modernize-use-override.AllowOverrideAndFinal: false + cppcoreguidelines-avoid-magic-numbers.IgnoreBitFieldsWidths: 'true' + bugprone-not-null-terminated-result.WantToUseSafeFunctions: true + modernize-pass-by-value.ValuesOnly: false + hicpp-use-emplace.TupleMakeFunctions: '::std::make_pair;::std::make_tuple' + bugprone-assert-side-effect.AssertMacros: assert + cert-err33-c.CheckedReturnTypes: '::std::error_code;::std::error_condition;::std::errc;::std::expected;::boost::system::error_code' + bugprone-easily-swappable-parameters.IgnoredParameterTypeSuffixes: 'bool;Bool;_Bool;it;It;iterator;Iterator;inputit;InputIt;forwardit;FowardIt;bidirit;BidirIt;constiterator;const_iterator;Const_Iterator;Constiterator;ConstIterator;RandomIt;randomit;random_iterator;ReverseIt;reverse_iterator;reverse_const_iterator;ConstReverseIterator;Const_Reverse_Iterator;const_reverse_iterator;Constreverseiterator;constreverseiterator' + cppcoreguidelines-non-private-member-variables-in-classes.IgnorePublicMemberVariables: 'false' + readability-identifier-length.MinimumVariableNameLength: 3 + bugprone-sizeof-expression.WarnOnSizeOfCompareToConstant: true + bugprone-empty-catch.IgnoreCatchWithKeywords: '@TODO;@FIXME' + modernize-pass-by-value.IncludeStyle: llvm + google-readability-namespace-comments.SpacesBeforeComments: '2' + bugprone-easily-swappable-parameters.IgnoredParameterNames: '"";iterator;Iterator;begin;Begin;end;End;first;First;last;Last;lhs;LHS;rhs;RHS' + bugprone-sizeof-expression.WarnOnSizeOfPointerToAggregate: true + modernize-use-auto.RemoveStars: false + bugprone-reserved-identifier.AllowedIdentifiers: '' + hicpp-move-const-arg.CheckMoveToConstRef: 'true' + hicpp-no-malloc.Deallocations: '::free' + cert-dcl51-cpp.AllowedIdentifiers: '' + modernize-use-default-member-init.UseAssignment: false + cert-err61-cpp.MaxSize: '64' + readability-suspicious-call-argument.Substring: 'true' + hicpp-use-override.AllowOverrideAndFinal: 'false' + readability-container-size-empty.ExcludedComparisonTypes: '::std::array' + bugprone-signed-char-misuse.DiagnoseSignedUnsignedCharComparisons: true + readability-identifier-length.MinimumExceptionNameLength: 2 + readability-identifier-naming.AggressiveDependentMemberLookup: false + llvm-else-after-return.WarnOnConditionVariables: 'false' + performance-type-promotion-in-math-fn.IncludeStyle: llvm + cert-dcl37-c.Invert: 'false' + cppcoreguidelines-macro-usage.CheckCapsOnly: false +SystemHeaders: false ... diff --git a/.cmake-format.yaml b/.cmake-format.yaml index 81d764ed04..c142149de1 100644 --- a/.cmake-format.yaml +++ b/.cmake-format.yaml @@ -16,3 +16,6 @@ max_pargs_hwrap: 3 separate_ctrl_name_with_space: false separate_fn_name_with_space: false tab_size: 2 + +markup: + enable_markup: false \ No newline at end of file diff --git a/.codecov.yml b/.codecov.yml index eff7966e73..7279b43224 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -12,8 +12,8 @@ coverage: project: default: threshold: 3% - patch: no - changes: no + patch: off + changes: false notify: gitter: diff --git a/.coderabbit.yaml b/.coderabbit.yaml new file mode 100644 index 0000000000..d723ee5b1c --- /dev/null +++ b/.coderabbit.yaml @@ -0,0 +1,77 @@ +language: en-US +tone_instructions: 'Be constructive and professional. Focus on technical accuracy while maintaining a friendly tone.' +early_access: true +enable_free_tier: true +reviews: + profile: assertive + request_changes_workflow: true + high_level_summary: true + high_level_summary_placeholder: '@coderabbitai summary' + auto_title_placeholder: '@coderabbitai' + review_status: true + commit_status: true + poem: true + collapse_walkthrough: false + sequence_diagrams: true + changed_files_summary: true + labeling_instructions: [] + path_filters: [] + path_instructions: + - path: 'src/**/*.cpp' + instructions: 'Focus on performance, memory management, and RAII principles' + - path: 'include/**/*.hpp' + instructions: 'Focus on design patterns, interfaces, and encapsulation' + - path: 'test/**/*.cpp' + instructions: | + Review the following unit test code written using doctest. Ensure the following: + - Comprehensive test coverage and proper test organization. + - The code adheres to best practices using doctest. + - Descriptive test names are used to clearly convey the intent of each test. + abort_on_close: true + auto_review: + enabled: true + auto_incremental_review: true + ignore_title_keywords: [] + labels: [] + drafts: true + base_branches: [] + tools: + shellcheck: + enabled: true + markdownlint: + enabled: true + github-checks: + enabled: true + timeout_ms: 90000 + gitleaks: + enabled: true + cppcheck: + enabled: true + languagetool: + enabled: true + enabled_only: false + level: default + hadolint: + enabled: true + yamllint: + enabled: true + actionlint: + enabled: true + pmd: + enabled: true + semgrep: + enabled: true +chat: + auto_reply: true +knowledge_base: + opt_out: false + learnings: + scope: auto + issues: + scope: auto + jira: + project_keys: [] + linear: + team_keys: [] + pull_requests: + scope: auto diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 4c1dfe2ca5..8c0d4b3b98 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -2,9 +2,9 @@ First, thank you! -Writing esoteric scientific software can be it's own reward, but it's not for the faint of heart. +Writing esoteric scientific software can be its own reward, but it's not for the faint of heart. -If you want a general overview as to why this software package exists, please look at the [Wiki], or my talk +If you want a general overview as to why this software package exists, please look at the [Wiki] or my talk [Causal Dynamical Triangulations with CGAL][slides]. Second, here are some simple guidelines that will make it easier on me to process and accept your contributions. @@ -16,16 +16,16 @@ New [releases] are periodically made from [develop], then merged back into [mast which is the stable work history. [Tagged] versions are [releases] at a point in time, citable via [ORCID]. for reproducibility. -3. Familiarize yourself with [Catch] and the [Gherkin] syntax. +3. Familiarize yourself with [doctest] and the [Gherkin] syntax. 4. Write a unit test for your proposed contribution. Unit tests go in the `tests` directory and are named -\{YourContribution\}_test.cpp, so that they can be automatically built. +\{YourContribution\}_test.cpp, don't forget to add to `/tests/CMakeLists.txt`. All proposed features of your contribution should have a corresponding test in \{YourContribution\}_test.cpp. -Consult the [Catch Test cases and sections] if you are unsure, or consult existing tests for examples. +Consult the [doctest test cases] if you are unsure, or consult existing tests for examples. 5. I highly recommend writing your tests first, before your contribution, as this helps to think about how the rest of the program will use your functions and/or classes. -[Test-Driven Development] (and [BDD]) has saved me quite a lot from various mistakes. +[Test-Driven Development] (and [BDD]) has saved me quite a bit from various mistakes. 6. Project source files go into the `src` directory; header files go into `include`. This makes integration into various tests and the main program easy and modular, and follows convention. @@ -43,12 +43,15 @@ Consult existing code for examples. 11. Run `clang-tidy` using the project's [clang-tidy.sh]. 12. Open a pull request against the develop branch of the main repository (which is the default). -[Travis-CI] will test it against combinations of Linux (Ubuntu 18.04) and MacOS with clang and gcc. -[AppVeyor] will test it against Visual Studio 2017 with `clang-cl` (version 9). Ensure that +[Travis-CI] will test it against combinations of Linux (Ubuntu 22.04) with clang and gcc. [GitHub Actions] will test +against macOS and run various other checks. +[AppVeyor] will test it against Visual Studio 2019 with `clang-cl` (version 14.0.6). Ensure that your code compiles on Windows, macOS, and Linux with `msvc`, `gcc`, and `clang`. 13. All pull requests must pass [Travis-CI] and [AppVeyor] to be accepted. -In particular, look at results from [Cppcheck], [Valgrind], [ASAN], [MSAN], [ClangTidy], , and [LGTM]. +In particular, look at results from [Cppcheck], [Valgrind], [ASAN], [LSAN], [MSAN], and [TSAN], because simulations may +run for a long time so memory leaks will be eventually fatal. +[GitHub Actions] also has a lot of useful checks that will help fix your code. 14. I will get to your change as soon as I can. Feel free to ping me on [Gitter] with any questions. @@ -77,10 +80,10 @@ Most editors/IDEs have plugins for `clang-format` and `clang-tidy`. [cpp-core]: https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md [clang-tidy.sh]: https://github.com/acgetchell/CDT-plusplus/blob/develop/clang-tidy.sh [AppVeyor]: https://ci.appveyor.com/project/acgetchell/cdt-plusplus -[Catch]: https://github.com/catchorg/Catch2/blob/master/docs/Readme.md +[doctest]: https://github.com/doctest/doctest [Gherkin]: https://www.tutorialspoint.com/behavior_driven_development/behavior_driven_development_gherkin.htm [BDD]: https://en.wikipedia.org/wiki/Behavior-driven_development -[Catch Test cases and sections]: https://github.com/catchorg/Catch2/blob/master/docs/test-cases-and-sections.md +[doctest test cases]: https://github.com/doctest/doctest/blob/master/doc/markdown/testcases.md [Gitter]: https://gitter.im/acgetchell/CDT-plusplus [ClangTidy]: https://releases.llvm.org/6.0.1/tools/clang/tools/extra/docs/clang-tidy/index.html [LGTM]: https://lgtm.com/projects/g/acgetchell/CDT-plusplus/ @@ -94,4 +97,7 @@ Most editors/IDEs have plugins for `clang-format` and `clang-tidy`. [ORCID]: https://orcid.org/ [Cppcheck]: http://cppcheck.sourceforge.net [ASAN]: https://github.com/google/sanitizers/wiki/AddressSanitizer -[MSAN]: https://github.com/google/sanitizers/wiki/MemorySanitizer \ No newline at end of file +[MSAN]: https://github.com/google/sanitizers/wiki/MemorySanitizer +[GitHub Actions]: https://github.com/acgetchell/CDT-plusplus/actions +[LSAN]: https://github.com/google/sanitizers/wiki/AddressSanitizerLeakSanitizer +[TSAN]: https://github.com/google/sanitizers/wiki/ThreadSanitizerCppManual \ No newline at end of file diff --git a/.github/workflows/asan.yml b/.github/workflows/asan.yml new file mode 100644 index 0000000000..5bfb71609d --- /dev/null +++ b/.github/workflows/asan.yml @@ -0,0 +1,68 @@ +name: Address Sanitizer + +on: + push: + branches: + - main + - develop + pull_request: + branches: + - develop + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + submodules: true + + - name: Setup + run: | + sudo apt update + sudo apt-get install build-essential automake autoconf autoconf-archive texinfo libtool-bin yasm ninja-build ccache + + - name: Setup Clang + uses: pkgxdev/setup@v2 + with: + +: clang@19 + + - run: clang --version + + - name: Restore artifacts or setup vcpkg + uses: lukka/run-vcpkg@v11 + with: + vcpkgGitCommitId: ${{ vars.VCPKG_GIT_COMMIT_ID }} + + - name: Configure + run: cmake --preset=asan + + - name: Build + run: cmake --build build -j 2 + + - name: Run tests + working-directory: build + continue-on-error: true + run: ctest -VV + + - name: Run ASAN on initialize + working-directory: build/src + continue-on-error: true + run: ./initialize --s -n32000 -t11 -o + + - name: Run ASAN on cdt-opt + working-directory: build/src + continue-on-error: true + run: ./cdt-opt + + - name: Run ASAN on cdt + working-directory: build/src + continue-on-error: true + run: ./cdt --s -n64 -t3 -a.6 -k1.1 -l.1 -p10 \ No newline at end of file diff --git a/.github/workflows/clang-format-check.yml b/.github/workflows/clang-format-check.yml index 022cd7ae13..300122dbe4 100644 --- a/.github/workflows/clang-format-check.yml +++ b/.github/workflows/clang-format-check.yml @@ -1,5 +1,14 @@ name: clang-format Check -on: [push, pull_request] +on: + push: + branches: + - main + - develop + pull_request: + branches: + - develop + workflow_dispatch: + jobs: formatting-check: name: Formatting Check @@ -10,10 +19,10 @@ jobs: - 'src' - 'include' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Run clang-format style check for C/C++/Protobuf programs. - uses: jidicula/clang-format-action@v4.5.0 + uses: jidicula/clang-format-action@v4.13.0 with: - clang-format-version: '14' + clang-format-version: '18' check-path: ${{ matrix.path }} fallback-style: 'Google' # optional \ No newline at end of file diff --git a/.github/workflows/codecov-upload.yml b/.github/workflows/codecov-upload.yml index c789e3562e..ce21b5194d 100644 --- a/.github/workflows/codecov-upload.yml +++ b/.github/workflows/codecov-upload.yml @@ -1,44 +1,75 @@ name: CodeCov -on: [push, pull_request, workflow_dispatch] +on: + push: + branches: + - main + - develop + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: codecov: name: CodeCov - runs-on: macos-latest + runs-on: ubuntu-latest + steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Setup run: | - brew install automake autoconf autoconf-archive libtool texinfo yasm ninja python ccache lcov + sudo apt update + sudo apt upgrade + sudo apt-get install build-essential automake autoconf autoconf-archive texinfo libtool-bin yasm ninja-build ccache - - name: Restore artifacts, or setup vcpkg - uses: lukka/run-vcpkg@v10 + - name: Setup GCC + uses: pkgxdev/setup@v2 with: - vcpkgGitCommitId: 47cbed88514c0e48fdb72ddc29545f35136f5461 + +: gcc@14 - - name: Install vcpkg packages and configure CMake - shell: bash - run: | - vcpkg install - cmake -G Ninja -D CMAKE_BUILD_TYPE=RelWithDebInfo -D ENABLE_TESTING:BOOL=TRUE -D ENABLE_COVERAGE:BOOL=TRUE -S . -B build + - run: gcc --version + + - name: Restore artifacts or setup vcpkg + uses: lukka/run-vcpkg@v11 + with: + vcpkgGitCommitId: ${{ vars.VCPKG_GIT_COMMIT_ID }} + + - name: Configure + run: cmake -G Ninja -D CMAKE_BUILD_TYPE=RelWithDebInfo -D ENABLE_TESTING:BOOL=TRUE -D ENABLE_COVERAGE:BOOL=TRUE -S . -B build - name: Build - run: cmake --build build + run: cmake --build build -j 2 - - name: Generate coverage + - name: Test + working-directory: build continue-on-error: true - run: | - cd $GITHUB_WORKSPACE/build - ctest --schedule-random --rerun-failed --output-on-failure -j2 - lcov --capture --directory . --output-file coverage.info - lcov --remove coverage.info '/usr/*' '*/usr/include/*' '*/vcpkg_installed/*' --output-file coverage.info - lcov --list coverage.info + run: ctest --rerun-failed --output-on-failure -j 2 + +# - name: collect code coverage +# run: bash <(curl -s https://codecov.io/bash) || echo "Codecov did not collect coverage reports" - - name: Upload coverage to CodeCov + - name: Generate coverage info + working-directory: build + continue-on-error: true run: | - cd $GITHUB_WORKSPACE/build - bash <(curl -s https://codecov.io/bash) -f coverage.info || echo "Codecov did not collect coverage reports" \ No newline at end of file + mkdir gcov-reports + pushd gcov-reports + for f in `find ../tests/CMakeFiles/CDT_test.dir -name '*.o'`; do + echo "Processing $f file..." + gcov -o ${f} x + done + ls | wc -l + popd + + - name: Submit to codecov.io + uses: codecov/codecov-action@v4 + with: +# directory: build/gcov-reports + fail_ci_if_error: false + verbose: true + gcov: true \ No newline at end of file diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 42f570ae2e..c2e5aa7afa 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -13,14 +13,20 @@ name: "CodeQL" on: push: - branches: [ develop ] + branches: + - main + - develop pull_request: - # The branches below must be a subset of the branches above - branches: [ develop ] + branches: + - develop schedule: - cron: '26 7 * * 0' workflow_dispatch: +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: analyze: name: Analyze @@ -40,11 +46,11 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -52,30 +58,28 @@ jobs: # Prefix the list here with "+" to use these queries and those in the config file. # queries: ./path/to/local/query, your-org/your-repo/queries@main - - name: Setup GCC - uses: egor-tensin/setup-gcc@v1 - with: - version: 11 - platform: x64 - - name: Setup run: | sudo apt update sudo apt-get install build-essential automake autoconf autoconf-archive texinfo libtool-bin yasm ninja-build ccache - - name: Restore artifacts, or setup vcpkg - uses: lukka/run-vcpkg@v10 + - name: Setup GCC + uses: pkgxdev/setup@v2 with: - vcpkgGitCommitId: 47cbed88514c0e48fdb72ddc29545f35136f5461 + +: gcc@14 - - name: Install vcpkg and configure CMake - shell: bash - run: | - vcpkg install - cmake -G Ninja -D CMAKE_BUILD_TYPE=RelWithDebInfo -D ENABLE_TESTING:BOOL=TRUE -S . -B build + - run: gcc --version + + - name: Restore artifacts or setup vcpkg + uses: lukka/run-vcpkg@v11 + with: + vcpkgGitCommitId: ${{ vars.VCPKG_GIT_COMMIT_ID }} + + - name: Configure + run: cmake --preset=build - name: Build - run: cmake --build build + run: cmake --build build -j 2 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/cpp-linter.yml b/.github/workflows/cpp-linter.yml new file mode 100644 index 0000000000..085d207f96 --- /dev/null +++ b/.github/workflows/cpp-linter.yml @@ -0,0 +1,31 @@ +name: cpp-linter + +on: + push: + branches: + - main + - develop + pull_request: + branches: + - develop + workflow_dispatch: + +jobs: + cpp-linter: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: cpp-linter/cpp-linter-action@v2 + id: linter + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + style: file + tidy-checks: '' + version: 19 + + - name: Fail fast?! + if: steps.linter.outputs.checks-failed > 0 + run: echo "Some files failed the linting checks!" + # for actual deployment + # run: exit 1 \ No newline at end of file diff --git a/.github/workflows/cppcheck.yml b/.github/workflows/cppcheck.yml new file mode 100644 index 0000000000..bc07a97004 --- /dev/null +++ b/.github/workflows/cppcheck.yml @@ -0,0 +1,48 @@ +name: Cppcheck + +on: + push: + branches: + - main + - develop + pull_request: + branches: + - develop + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + submodules: true + + - name: Setup + run: | + sudo apt update + sudo apt-get install build-essential automake autoconf autoconf-archive texinfo libtool-bin yasm ninja-build ccache cppcheck + + - name: Setup GCC + uses: pkgxdev/setup@v2 + with: + +: gcc@14 + + - run: gcc --version + + - name: Restore artifacts or setup vcpkg + uses: lukka/run-vcpkg@v11 + with: + vcpkgGitCommitId: ${{ vars.VCPKG_GIT_COMMIT_ID }} + + - name: Configure + run: cmake --preset=cppcheck + + - name: Build + run: cmake --build build -j 2 \ No newline at end of file diff --git a/.github/workflows/doxygen.yml b/.github/workflows/doxygen.yml new file mode 100644 index 0000000000..c5faed7067 --- /dev/null +++ b/.github/workflows/doxygen.yml @@ -0,0 +1,19 @@ +name: Doxygen GitHub Pages Deploy Action + +on: + push: + branches: + - main + - develop + workflow_dispatch: + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: DenverCoder1/doxygen-github-pages-action@v2.0.0 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + branch: gh-pages + folder: docs/html + config_file: docs/Doxyfile \ No newline at end of file diff --git a/.github/workflows/linux-clang.yml b/.github/workflows/linux-clang.yml index 73d737b71f..b9e22caa02 100644 --- a/.github/workflows/linux-clang.yml +++ b/.github/workflows/linux-clang.yml @@ -1,38 +1,53 @@ name: Linux Clang -on: [push, pull_request, workflow_dispatch] +on: + push: + branches: + - main + - develop + pull_request: + branches: + - develop + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: fetch-depth: 0 submodules: true - - name: Setup Clang - uses: egor-tensin/setup-clang@v1 - with: - version: 13 - platform: x64 - - name: Setup run: | sudo apt update sudo apt-get install build-essential automake autoconf autoconf-archive texinfo libtool-bin yasm ninja-build ccache - - name: Restore artifacts, or setup vcpkg - uses: lukka/run-vcpkg@v10 + - name: Setup Clang + uses: pkgxdev/setup@v2 with: - vcpkgGitCommitId: 47cbed88514c0e48fdb72ddc29545f35136f5461 + +: clang@19 - - name: Install vcpkg packages and configure CMake - shell: bash - run: | - vcpkg install - cmake -G Ninja -D CMAKE_BUILD_TYPE=RelWithDebInfo -D ENABLE_TESTING:BOOL=TRUE -S . -B build + - run: clang --version + + - name: Restore artifacts or setup vcpkg + uses: lukka/run-vcpkg@v11 + with: + vcpkgGitCommitId: ${{ vars.VCPKG_GIT_COMMIT_ID }} + + - name: Configure + run: cmake --preset=build - name: Build - run: cmake --build build \ No newline at end of file + run: cmake --build build -j 2 + + - name: Test + working-directory: build + continue-on-error: true + run: ctest --rerun-failed --output-on-failure -j 2 \ No newline at end of file diff --git a/.github/workflows/linux-gcc.yml b/.github/workflows/linux-gcc.yml index a33c46e562..7008a6cd31 100644 --- a/.github/workflows/linux-gcc.yml +++ b/.github/workflows/linux-gcc.yml @@ -1,38 +1,53 @@ name: Linux GCC -on: [push, pull_request, workflow_dispatch] +on: + push: + branches: + - main + - develop + pull_request: + branches: + - develop + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: fetch-depth: 0 submodules: true - - name: Setup GCC - uses: egor-tensin/setup-gcc@v1 - with: - version: 11 - platform: x64 - - name: Setup run: | sudo apt update sudo apt-get install build-essential automake autoconf autoconf-archive texinfo libtool-bin yasm ninja-build ccache - - name: Restore artifacts, or setup vcpkg - uses: lukka/run-vcpkg@v10 + - name: Setup GCC + uses: pkgxdev/setup@v2 with: - vcpkgGitCommitId: 47cbed88514c0e48fdb72ddc29545f35136f5461 + +: gcc@14 - - name: Install vcpkg packages and configure CMake - shell: bash - run: | - vcpkg install - cmake -G Ninja -D CMAKE_BUILD_TYPE=RelWithDebInfo -D ENABLE_TESTING:BOOL=TRUE -S . -B build + - run: gcc --version + + - name: Restore artifacts or setup vcpkg + uses: lukka/run-vcpkg@v11 + with: + vcpkgGitCommitId: ${{ vars.VCPKG_GIT_COMMIT_ID }} + + - name: Configure + run: cmake --preset=build - name: Build - run: cmake --build build \ No newline at end of file + run: cmake --build build -j 2 + + - name: Test + working-directory: build + continue-on-error: true + run: ctest --rerun-failed --output-on-failure -j 2 \ No newline at end of file diff --git a/.github/workflows/lsan.yml b/.github/workflows/lsan.yml new file mode 100644 index 0000000000..34fbf21161 --- /dev/null +++ b/.github/workflows/lsan.yml @@ -0,0 +1,68 @@ +name: Leak Sanitizer + +on: + push: + branches: + - main + - develop + pull_request: + branches: + - develop + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + submodules: true + + - name: Setup + run: | + sudo apt update + sudo apt-get install build-essential automake autoconf autoconf-archive texinfo libtool-bin yasm ninja-build ccache + + - name: Setup Clang + uses: pkgxdev/setup@v2 + with: + +: clang@19 + + - run: clang --version + + - name: Restore artifacts or setup vcpkg + uses: lukka/run-vcpkg@v11 + with: + vcpkgGitCommitId: ${{ vars.VCPKG_GIT_COMMIT_ID }} + + - name: Configure + run: cmake --preset=lsan + + - name: Build + run: cmake --build build -j 2 + + - name: Run tests + working-directory: build + continue-on-error: true + run: ctest -VV + + - name: Run LSAN on initialize + working-directory: build/src + continue-on-error: true + run: ./initialize --s -n32000 -t11 -o + + - name: Run LSAN on cdt-opt + working-directory: build/src + continue-on-error: true + run: ./cdt-opt + + - name: Run LSAN on cdt + working-directory: build/src + continue-on-error: true + run: ./cdt --s -n64 -t3 -a.6 -k1.1 -l.1 -p10 \ No newline at end of file diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 8740624e34..f8ef356272 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -1,31 +1,47 @@ name: macOS -on: [push, pull_request, workflow_dispatch] +on: [workflow_dispatch] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: build: runs-on: macos-latest steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 0 - submodules: true - - - name: Setup - run: | - brew install automake autoconf autoconf-archive libtool texinfo yasm ninja python ccache - - - name: Restore artifacts, or setup vcpkg - uses: lukka/run-vcpkg@v10 - with: - vcpkgGitCommitId: 47cbed88514c0e48fdb72ddc29545f35136f5461 - - - name: Install vcpkg packages and configure CMake - shell: bash - run: | - vcpkg install - cmake -G Ninja -D CMAKE_BUILD_TYPE=RelWithDebInfo -D ENABLE_TESTING:BOOL=TRUE -S . -B build - - - name: Build - run: cmake --build build \ No newline at end of file + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + submodules: true + + - name: Setup + run: | + declare -a python_files=("2to3", "idle3", "pydoc3", "python3", "python3-config") + declare -a python_versions=("3.11" "3.12") + echo "Checking and removing Python symlinks..." + for base in "${python_files[@]}"; do + for version in "${python_versions[@]}"; do + file="/usr/local/bin/${base}${version}" + [ -L "$file" ] && sudo rm "$file" || true + done + done + echo "Python symlink cleanup completed." + brew install automake autoconf autoconf-archive libtool texinfo yasm ninja python ccache pkg-config + + - name: Restore artifacts or setup vcpkg + uses: lukka/run-vcpkg@v11 + with: + vcpkgGitCommitId: ${{ vars.VCPKG_GIT_COMMIT_ID }} + + - name: Configure + run: cmake --preset=build + + - name: Build + run: cmake --build build -j 2 + + - name: Test + working-directory: build + continue-on-error: true + run: ctest --rerun-failed --output-on-failure -j 2 \ No newline at end of file diff --git a/.github/workflows/msan.yml b/.github/workflows/msan.yml new file mode 100644 index 0000000000..d65f217561 --- /dev/null +++ b/.github/workflows/msan.yml @@ -0,0 +1,68 @@ +name: Memory Sanitizer + +on: + push: + branches: + - main + - develop + pull_request: + branches: + - develop + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + submodules: true + + - name: Setup + run: | + sudo apt update + sudo apt-get install build-essential automake autoconf autoconf-archive texinfo libtool-bin yasm ninja-build ccache + + - name: Setup Clang + uses: pkgxdev/setup@v2 + with: + +: clang@19 + + - run: clang --version + + - name: Restore artifacts or setup vcpkg + uses: lukka/run-vcpkg@v11 + with: + vcpkgGitCommitId: ${{ vars.VCPKG_GIT_COMMIT_ID }} + + - name: Configure + run: cmake --preset=msan + + - name: Build + run: cmake --build build -j 2 + + - name: Run tests + working-directory: build + continue-on-error: true + run: ctest -VV + + - name: Run MSAN on initialize + working-directory: build/src + continue-on-error: true + run: ./initialize --s -n32000 -t11 -o + + - name: Run MSAN on cdt-opt + working-directory: build/src + continue-on-error: true + run: ./cdt-opt + + - name: Run MSAN on cdt + working-directory: build/src + continue-on-error: true + run: ./cdt --s -n64 -t3 -a.6 -k1.1 -l.1 -p10 \ No newline at end of file diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index eec19befb8..0127cac56d 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -1,87 +1,64 @@ name: SonarCloud -on: [push, pull_request, workflow_dispatch] +on: + push: + branches: + - main + - develop + pull_request: + types: [opened, synchronize, reopened] + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: sonarcloud: name: SonarCloud runs-on: ubuntu-latest env: - SONAR_SCANNER_VERSION: 4.7.0.2747 # Find the latest version in the "Linux" link on this page: - # https://docs.sonarqube.org/latest/analysis/scan/sonarscanner/ - SONAR_SERVER_URL: "https://sonarcloud.io" BUILD_WRAPPER_OUT_DIR: build_wrapper_output_directory # Directory where build-wrapper output will be placed steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: fetch-depth: 0 - - name: Setup GCC - uses: egor-tensin/setup-gcc@v1 - with: - version: 11 - platform: x64 - - name: Setup run: | sudo apt update sudo apt-get install build-essential automake autoconf autoconf-archive texinfo libtool-bin yasm ninja-build ccache lcov - - name: Restore artifacts, or setup vcpkg - uses: lukka/run-vcpkg@v10 + - name: Setup GCC + uses: pkgxdev/setup@v2 with: - vcpkgGitCommitId: 47cbed88514c0e48fdb72ddc29545f35136f5461 - - - name: Install vcpkg packages and configure CMake - shell: bash - run: | - vcpkg install - cmake -G Ninja -D CMAKE_BUILD_TYPE=RelWithDebInfo -D ENABLE_TESTING:BOOL=TRUE -D ENABLE_COVERAGE:BOOL=TRUE -S . -B build + +: gcc@14 - - name: Cache SonarCloud packages and analysis - uses: actions/cache@v2 - id: sonarcloud-cache - with: - path: ~/.sonar - key: ${{ runner.os }}-sonar-v${{ env.SONAR_SCANNER_VERSION }} - restore-keys: ${{ runner.os }}-sonar-v${{ env.SONAR_SCANNER_VERSION }} + - run: gcc --version - - name: Download and setup sonar-scanner - shell: bash - env: - SONAR_SCANNER_DOWNLOAD_URL: https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${{ env.SONAR_SCANNER_VERSION }}-linux.zip - if: steps.sonarcloud-cache.outputs.cache-hit != 'true' - run: | - mkdir -p $HOME/.sonar - curl -sSLo $HOME/.sonar/sonar-scanner.zip ${{ env.SONAR_SCANNER_DOWNLOAD_URL }} - unzip -o $HOME/.sonar/sonar-scanner.zip -d $HOME/.sonar/ + - name: Install sonar-scanner and build-wrapper + uses: sonarsource/sonarcloud-github-c-cpp@v3 - - name: Download and setup build-wrapper - shell: bash - env: - BUILD_WRAPPER_DOWNLOAD_URL: ${{ env.SONAR_SERVER_URL }}/static/cpp/build-wrapper-linux-x86.zip - if: steps.sonarcloud-cache.outputs.cache-hit != 'true' - run: | - curl -sSLo $HOME/.sonar/build-wrapper-linux-x86.zip ${{ env.BUILD_WRAPPER_DOWNLOAD_URL }} - unzip -o $HOME/.sonar/build-wrapper-linux-x86.zip -d $HOME/.sonar/ + - name: Restore artifacts or setup vcpkg + uses: lukka/run-vcpkg@v11 + with: + vcpkgGitCommitId: ${{ vars.VCPKG_GIT_COMMIT_ID }} - - name: Set build-wrapper and sonar-scanner paths - run: | - echo "$HOME/.sonar/build-wrapper-linux-x86" >> $GITHUB_PATH - echo "$HOME/.sonar/sonar-scanner-${{ env.SONAR_SCANNER_VERSION }}-linux/bin" >> $GITHUB_PATH + - name: Configure + run: cmake -G Ninja -D CMAKE_BUILD_TYPE=RelWithDebInfo -D ENABLE_TESTING:BOOL=TRUE -D ENABLE_COVERAGE:BOOL=TRUE -S . -B build - name: Build run: build-wrapper-linux-x86-64 --out-dir ${{ env.BUILD_WRAPPER_OUT_DIR }} cmake --build build - - name: Test and generate coverage + - name: Test + working-directory: build continue-on-error: true - run: | - cd $GITHUB_WORKSPACE/build - ctest --schedule-random -j2 + run: ctest --rerun-failed --output-on-failure -j 2 - - name: Compile coverage reports + - name: Generate coverage info + working-directory: build + continue-on-error: true run: | - cd $GITHUB_WORKSPACE/build mkdir gcov-reports pushd gcov-reports for f in `find ../tests/CMakeFiles/CDT_test.dir -name '*.o'`; do @@ -95,4 +72,4 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR info, if any SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - run: sonar-scanner --define sonar.host.url="${{ env.SONAR_SERVER_URL }}" --define sonar.cfamily.build-wrapper-output="${{ env.BUILD_WRAPPER_OUT_DIR }}" \ No newline at end of file + run: sonar-scanner --define sonar.cfamily.build-wrapper-output="${{ env.BUILD_WRAPPER_OUT_DIR }}" \ No newline at end of file diff --git a/.github/workflows/tsan.yml b/.github/workflows/tsan.yml new file mode 100644 index 0000000000..8ea6f9320a --- /dev/null +++ b/.github/workflows/tsan.yml @@ -0,0 +1,68 @@ +name: Thread Sanitizer + +on: + push: + branches: + - main + - develop + pull_request: + branches: + - develop + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + submodules: true + + - name: Setup + run: | + sudo apt update + sudo apt-get install build-essential automake autoconf autoconf-archive texinfo libtool-bin yasm ninja-build ccache + + - name: Setup Clang + uses: pkgxdev/setup@v2 + with: + +: clang@19 + + - run: clang --version + + - name: Restore artifacts or setup vcpkg + uses: lukka/run-vcpkg@v11 + with: + vcpkgGitCommitId: ${{ vars.VCPKG_GIT_COMMIT_ID }} + + - name: Configure + run: cmake --preset=tsan + + - name: Build + run: cmake --build build -j 2 + + - name: Run tests + working-directory: build + continue-on-error: true + run: ctest -VV + + - name: Run TSAN on initialize + working-directory: build/src + continue-on-error: true + run: ./initialize --s -n32000 -t11 -o + + - name: Run TSAN on cdt-opt + working-directory: build/src + continue-on-error: true + run: ./cdt-opt + + - name: Run TSAN on cdt + working-directory: build/src + continue-on-error: true + run: ./cdt --s -n64 -t3 -a.6 -k1.1 -l.1 -p10 \ No newline at end of file diff --git a/.github/workflows/valgrind.yml b/.github/workflows/valgrind.yml new file mode 100644 index 0000000000..dcfe3441c5 --- /dev/null +++ b/.github/workflows/valgrind.yml @@ -0,0 +1,69 @@ +name: Valgrind + +on: + push: + branches: + - main + - develop + pull_request: + branches: + - develop + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + submodules: true + + - name: Setup + run: | + sudo apt update + sudo apt-get install build-essential automake autoconf autoconf-archive texinfo libtool-bin yasm ninja-build ccache valgrind + + - name: Setup GCC + uses: pkgxdev/setup@v2 + with: + +: gcc@14 + + - run: gcc --version + + - name: Restore artifacts or setup vcpkg + uses: lukka/run-vcpkg@v11 + with: + vcpkgGitCommitId: ${{ vars.VCPKG_GIT_COMMIT_ID }} + + - name: Configure + run: cmake --preset=valgrind + + - name: Build + run: cmake --build build -j 2 + + - name: Run memcheck + working-directory: build + continue-on-error: true + run: | + ctest -VV -T memcheck --verbose + + - name: Run Valgrind on initialize + working-directory: build/src + continue-on-error: true + run: valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --verbose ./initialize --s -n32000 -t11 -o + + - name: Run Valgrind on cdt-opt + working-directory: build/src + continue-on-error: true + run: valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --verbose ./cdt-opt + + - name: Run Valgrind on cdt + working-directory: build/src + continue-on-error: true + run: valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --verbose ./cdt --s -n64 -t3 -a.6 -k1.1 -l.1 -p10 \ No newline at end of file diff --git a/.github/workflows/whitespace.yml b/.github/workflows/whitespace.yml index 768fc4f389..60f2f0119c 100644 --- a/.github/workflows/whitespace.yml +++ b/.github/workflows/whitespace.yml @@ -1,11 +1,19 @@ name: Whitespace -on: [workflow_dispatch] +on: + push: + branches: + - main + - develop + pull_request: + branches: + - develop + workflow_dispatch: jobs: whitespace: name: Find Trailing Whitespace runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: harupy/find-trailing-whitespace@master \ No newline at end of file + - uses: actions/checkout@v4 + - uses: erictleung/find-file-whitespace@main \ No newline at end of file diff --git a/.github/workflows/windows-msvc.yml b/.github/workflows/windows-msvc.yml index 793a12620b..97cacc50fb 100644 --- a/.github/workflows/windows-msvc.yml +++ b/.github/workflows/windows-msvc.yml @@ -2,12 +2,20 @@ name: Windows on: [workflow_dispatch] +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: build: runs-on: windows-latest + env: + VCPKG_DEFAULT_TRIPLET: x64-windows + VCPKG_ROOT: C:\vcpkg + steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: fetch-depth: 0 submodules: true @@ -15,13 +23,12 @@ jobs: - name: Setup Clang uses: egor-tensin/setup-clang@v1 with: - version: 13 platform: x64 - name: Restore artifacts, or setup vcpkg - uses: lukka/run-vcpkg@v10 + uses: lukka/run-vcpkg@v11 with: - vcpkgGitCommitId: 47cbed88514c0e48fdb72ddc29545f35136f5461 + vcpkgGitCommitId: ${{ vars.VCPKG_GIT_COMMIT_ID }} - name: Install vcpkg packages and configure CMake run: | @@ -29,9 +36,4 @@ jobs: cmake -D CMAKE_BUILD_TYPE=RelWithDebInfo -D ENABLE_TESTING:BOOL=TRUE -D ENABLE_CACHE:BOOL=FALSE -S . -B build - name: Build - run: cmake --build build - - - name: Test - run: | - cd build - ctest -C Debug -j2 --output-on-failure + run: cmake --build build \ No newline at end of file diff --git a/.github/workflows/windows-pkgx.yml b/.github/workflows/windows-pkgx.yml new file mode 100644 index 0000000000..9d37437d88 --- /dev/null +++ b/.github/workflows/windows-pkgx.yml @@ -0,0 +1,39 @@ +name: Windows pkgx + +on: [workflow_dispatch] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + runs-on: windows-latest + + env: + VCPKG_DEFAULT_TRIPLET: x64-windows + VCPKG_ROOT: C:\vcpkg + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + submodules: true + + - name: Setup Clang + uses: pkgxdev/setup@v2 + with: + +: clang@19 + + - name: Restore artifacts, or setup vcpkg + uses: lukka/run-vcpkg@v11 + with: + vcpkgGitCommitId: ${{ vars.VCPKG_GIT_COMMIT_ID }} + + - name: Install vcpkg packages and configure CMake + run: | + vcpkg install + cmake -D CMAKE_BUILD_TYPE=RelWithDebInfo -D ENABLE_TESTING:BOOL=TRUE -D ENABLE_CACHE:BOOL=FALSE -S . -B build + + - name: Build + run: cmake --build build \ No newline at end of file diff --git a/.gitignore b/.gitignore index 3e2e00721d..e166a2a304 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,7 @@ html/ /cmake-build* cov-int /out/* +/Testing # Editor files .clang_complete diff --git a/.gitpod.yml b/.gitpod.yml index 2134098eb9..cee0c0acc8 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -2,6 +2,6 @@ image: acgetchell/vcpkg-image tasks: - init: > - vcpkg install --feature-flags=manifests + vcpkg install && cd scripts && ./fast-build.sh diff --git a/.lgtm.yml b/.lgtm.yml deleted file mode 100644 index 0c331dba95..0000000000 --- a/.lgtm.yml +++ /dev/null @@ -1,45 +0,0 @@ -path_classifiers: - test: - - tests - docs: - - docs - library: - - external -extraction: - cpp: - prepare: - packages: - - build-essential - - automake - - autoconf - - autoconf-archive - - libtool-bin - - texinfo - - yasm - - ninja-build -# - g++-10 - - ccache -# after_prepare: -# - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-10 100 --slave /usr/bin/g++ g++ /usr/bin/g++-10 -# - export CXX="g++-10" CC="gcc-10" - configure: - command: - - git clone https://github.com/Microsoft/vcpkg.git - - cd vcpkg - - export VCPKG_ROOT="$LGTM_SRC/vcpkg" - - export PATH="$VCPKG_ROOT:$PATH" - - git pull origin master --no-rebase - - ./bootstrap-vcpkg.sh - - vcpkg integrate install - - cmake --version - - cd $LGTM_SRC - - pwd - - ps aux | grep vcpkg - - vcpkg install --feature-flags=manifests - index: - build_command: - - cd $LGTM_SRC - - mkdir build - - cd build || exit - - cmake -G Ninja -D CMAKE_BUILD_TYPE=RelWithDebInfo -D ENABLE_TESTING:BOOL=TRUE .. - - cmake --build . \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 74006acf0a..1bce3fb680 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ version: ~> 1.0 language: cpp -dist: focal +dist: jammy cache: - ccache @@ -8,8 +8,14 @@ cache: - $TRAVIS_BUILD_DIR/vcpkg - $HOME/.cache/vcpkg/archives +# Don't double build branch PRs +branches: + except: + - /^pr\..*/ + addons: apt: + update: true packages: - build-essential - automake @@ -18,129 +24,137 @@ addons: - libtool-bin - texinfo - yasm - - gcc-10 - - g++-10 - - clang-10 + - gcc-12 + - g++-12 - ninja-build - cppcheck - doxygen - graphviz - lcov - valgrind + - ccache + - wget os: - linux compiler: - - g++-10 - - clang-10 + - g++-12 + - clang-19 jobs: fast_finish: true - include: - # CppCheck - - os: linux - compiler: g++-10 - env: CPPCHECK=true - before_script: - - export CMAKE_ARGS="-D ENABLE_CPPCHECK:BOOL=TRUE" - after_success: skip - # Valgrind - - os: linux - compiler: g++-10 - env: VALGRIND=true - before_script: - - export CMAKE_ARGS="-D ENABLE_IPO:BOOL=FALSE -D ENABLE_VALGRIND:BOOL=TRUE" - after_success: - - cd $TRAVIS_BUILD_DIR/build/ - - travis_wait 60 ctest -VV -T memcheck --verbose - - cd src - - travis_wait 30 valgrind --leak-check=full --show-leak-kinds=all --verbose ./initialize --s -n32000 -t11 -o - - travis_wait 30 valgrind --leak-check=full --show-leak-kinds=all --verbose ./cdt-opt - - travis_wait 30 valgrind --leak-check=full --show-leak-kinds=all --verbose ./cdt --s -n64 -t3 -a0.6 -k1.1 -l0.1 -p10 - # AddressSanitizer and UndefinedBehaviorSanitizer - - os: linux - compiler: clang-10 - env: ASAN=true - before_script: - - export CMAKE_ARGS="-D ENABLE_SANITIZER_ADDRESS:BOOL=TRUE -D ENABLE_SANITIZER_UNDEFINED_BEHAVIOR:BOOL=TRUE" - after_success: - - cd $TRAVIS_BUILD_DIR/build/ - - ctest -VV - - cd src - - ./initialize --s -n32000 -t11 -o - # LeakSanitizer - - os: linux - compiler: clang-10 - env: LSAN=true - before_script: - - export CMAKE_ARGS="-D ENABLE_SANITIZER_LEAK:BOOL=TRUE" - after_success: - - cd $TRAVIS_BUILD_DIR/build/ - - ctest -VV - - cd src - - ./initialize --s -n32000 -t11 -o - # MemorySanitizer - - os: linux - compiler: clang-10 - env: MSAN=true - before_script: - - export CMAKE_ARGS="-D ENABLE_SANITIZER_MEMORY:BOOL=TRUE" - after_success: - - cd $TRAVIS_BUILD_DIR/build/ - - ctest -VV - - cd src - - ./initialize --s -n32000 -t11 -o - # ThreadSanitizer - - os: linux - compiler: clang-10 - env: TSAN=true - before_script: - - export CMAKE_ARGS="-D ENABLE_SANITIZER_THREAD:BOOL=TRUE" - after_success: - - cd $TRAVIS_BUILD_DIR/build/ - - travis_wait 90 ctest -VV - - cd src - - ./initialize --s -n32000 -t11 -o - # Doxygen - - os: linux - compiler: g++-10 - env: DOXYGEN=true - install: skip - script: - - cd $TRAVIS_BUILD_DIR - - doxygen docs/Doxyfile - - touch docs/html/.nojekyll - after_success: - # Overwrite usual after_success step without canceling it (which would cause deploy to not run) - - pwd - deploy: - provider: pages - skip_cleanup: true - local_dir: docs/html - github_token: $GITHUB_TOKEN - keep_history: true - verbose: true - edge: true - on: - all_branches: true - condition: $TRAVIS_BRANCH =~ ^(master|develop)$ - allow_failures: - - os: linux - compiler: g++-10 - env: DOXYGEN=true - - os: linux - compiler: clang-10 - env: ASAN=true - - os: linux - compiler: clang-10 - env: TSAN=true +# include: +# # CppCheck +# - os: linux +# compiler: g++12 +# env: CPPCHECK=true +# before_script: +# - export CMAKE_ARGS="-D ENABLE_CPPCHECK:BOOL=TRUE" +# after_success: skip +# # Valgrind +# - os: linux +# compiler: g++12 +# env: VALGRIND=true +# before_script: +# - export CMAKE_ARGS="-D ENABLE_IPO:BOOL=FALSE -D ENABLE_VALGRIND:BOOL=TRUE" +# after_success: +# - cd $TRAVIS_BUILD_DIR/build/ +# - travis_wait 60 ctest -VV -T memcheck --verbose +# - cd src +# - travis_wait 30 valgrind --leak-check=full --show-leak-kinds=all --verbose ./initialize --s -n32000 -t11 -o +# - travis_wait 30 valgrind --leak-check=full --show-leak-kinds=all --verbose ./cdt-opt +# - travis_wait 30 valgrind --leak-check=full --show-leak-kinds=all --verbose ./cdt --s -n64 -t3 -a0.6 -k1.1 -l0.1 -p10 +# # AddressSanitizer and UndefinedBehaviorSanitizer +# - os: linux +# compiler: clang-15 +# env: ASAN=true +# before_script: +# - export CMAKE_ARGS="-D ENABLE_SANITIZER_ADDRESS:BOOL=TRUE -D ENABLE_SANITIZER_UNDEFINED_BEHAVIOR:BOOL=TRUE" +# after_success: +# - cd $TRAVIS_BUILD_DIR/build/ +# - ctest -VV +# - cd src +# - ./initialize --s -n32000 -t11 -o +# # LeakSanitizer +# - os: linux +# compiler: clang-15 +# env: LSAN=true +# before_script: +# - export CMAKE_ARGS="-D ENABLE_SANITIZER_LEAK:BOOL=TRUE" +# after_success: +# - cd $TRAVIS_BUILD_DIR/build/ +# - ctest -VV +# - cd src +# - ./initialize --s -n32000 -t11 -o +# # MemorySanitizer +# - os: linux +# compiler: clang-15 +# env: MSAN=true +# before_script: +# - export CMAKE_ARGS="-D ENABLE_SANITIZER_MEMORY:BOOL=TRUE" +# after_success: +# - cd $TRAVIS_BUILD_DIR/build/ +# - ctest -VV +# - cd src +# - ./initialize --s -n32000 -t11 -o +# # ThreadSanitizer +# - os: linux +# compiler: clang-15 +# env: TSAN=true +# before_script: +# - export CMAKE_ARGS="-D ENABLE_SANITIZER_THREAD:BOOL=TRUE" +# after_success: +# - cd $TRAVIS_BUILD_DIR/build/ +# - travis_wait 90 ctest -VV +# - cd src +# - ./initialize --s -n32000 -t11 -o +# # Doxygen +# - os: linux +# compiler: g++12 +# env: DOXYGEN=true +# install: skip +# script: +# - cd $TRAVIS_BUILD_DIR +# - doxygen docs/Doxyfile +# - touch docs/html/.nojekyll +# after_success: +# # Overwrite usual after_success step without canceling it (which would cause deploy to not run) +# - pwd +# deploy: +# provider: pages +# skip_cleanup: true +# local_dir: docs/html +# github_token: $GITHUB_TOKEN +# keep_history: true +# verbose: true +# edge: true +# on: +# all_branches: true +# condition: $TRAVIS_BRANCH =~ ^(master|develop)$ +# allow_failures: +# - os: linux +# compiler: g++12 +# env: DOXYGEN=true before_install: - - if [[ "$CXX" == "g++" ]]; then export CXX="g++-10" CC="gcc-10"; fi - - if [[ "$CXX" == "clang++" ]]; then export CXX="clang++-10" CC="clang-10"; fi -# - sudo apt-get install -yq --allow-downgrades libc6=2.31-0ubuntu9.2 libc6-dev=2.31-0ubuntu9.2 -# - sudo -E apt-get -yq --no-install-suggests --no-install-recommends --allow-downgrades --allow-remove-essential --allow-change-held-packages install build-essential automake autoconf autoconf-archive libtool-bin texinfo yasm gcc-10 g++-10 clang-10 clang-format-10 clang-tidy-10 ninja-build cppcheck valgrind doxygen graphviz lcov -o Debug::pkgProblemResolver=yes + - | + if [[ "$CXX" == "g++" ]]; then + sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-12 90 + sudo update-alternatives --config gcc + export CXX="g++-12" CC="gcc-12" + fi + - | + if [[ "$CXX" == "clang++" ]]; then + wget https://apt.llvm.org/llvm.sh + chmod +x llvm.sh + sudo ./llvm.sh 19 + sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-19 100 + sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-19 100 + export CXX="clang++-19" CC="clang-19" + clang --version + clang++ --version + fi install: # vcpkg should be cached, but clone it if not @@ -153,13 +167,12 @@ install: - cd $VCPKG_ROOT - git pull origin master --no-rebase - ./bootstrap-vcpkg.sh - - vcpkg integrate install - rm -rf downloads buildtrees packages - cmake --version - cd .. - pwd # Install required libraries (these are often cached, so you must remove old dependencies first, as above) - - travis_wait 120 vcpkg install --feature-flags=manifests + - travis_wait 120 vcpkg install - vcpkg list script: @@ -168,4 +181,4 @@ script: after_success: - cd build - - travis_wait 90 ctest --output-on-failure \ No newline at end of file + - travis_wait 90 ctest --output-on-failure -j2 \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index b721d76b0e..9ee624d63d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.16...3.23) +cmake_minimum_required(VERSION 3.16) # vcpkg settings must be set before project() if(DEFINED ENV{VCPKG_ROOT} AND NOT DEFINED CMAKE_TOOLCHAIN_FILE) @@ -19,6 +19,7 @@ set(_VCPKG_INSTALLED_DIR project( CDT-plusplus VERSION 0.1.8 + DESCRIPTION "Fast Causal Dynamical Triangulations in C++" LANGUAGES CXX) # Project settings @@ -29,7 +30,7 @@ include(cmake/PreventInSourceBuilds.cmake) # Link this 'library' to set the c++ standard / compile-time options requested add_library(project_options INTERFACE) -target_compile_features(project_options INTERFACE cxx_std_17) +target_compile_features(project_options INTERFACE cxx_std_23) if(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") option(ENABLE_BUILD_WITH_TIME_TRACE "Enable -ftime-trace to generate time tracing .json files on clang" OFF) @@ -66,16 +67,28 @@ include(CMakeDependentOption) # Set CGAL_DATA_DIR to the location of the CGAL data files set(ENV{CGAL_DATA_DIR} CMAKE_BINARY_DIR/Data) -# Set NOMINMAX to avoid min/max macro errors on Windows in date.h -if(WIN32) - # Workaround for https://github.com/CGAL/cgal/issues/4665 and https://github.com/microsoft/vcpkg/issues/23572 - add_compile_options(/DNOMINMAX) +# Minimum compiler versions required for C++23 support: +# - MSVC 19.34 (Visual Studio 2022 version 17.4) +# - GCC 12.2 +# - AppleClang 14.0 +# - Clang 16.0 +if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "19.34") + message(FATAL_ERROR "MSVC 19.34 or higher required for C++23 support") +elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "12.2") + message(FATAL_ERROR "GCC 12.2 or higher required for C++23 support") +elseif(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "14.0") + message(FATAL_ERROR "AppleClang 14.0 or higher required for C++23 support") +elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "16.0") + message(FATAL_ERROR "Clang 16.0 or higher required for C++23 support") endif() -# Project vcpkg dependencies +# Set NOMINMAX to avoid min/max macro errors on Windows in date.h +#if(WIN32) +# # Workaround for https://github.com/CGAL/cgal/issues/4665 and https://github.com/microsoft/vcpkg/issues/23572 +# add_compile_options(/DNOMINMAX) +#endif() -# https://github.com/catchorg/Catch2 -find_package(Catch2 CONFIG REQUIRED) +# Project vcpkg dependencies # https://github.com/CGAL/cgal find_package(CGAL CONFIG REQUIRED) @@ -88,8 +101,8 @@ set(CGAL_DO_NOT_WARN_ABOUT_CMAKE_BUILD_TYPE TRUE) # https://howardhinnant.github.io/date/date.html find_package(date CONFIG REQUIRED) -# https://github.com/docopt/docopt.cpp -find_package(docopt CONFIG REQUIRED) +# https://github.com/doctest/doctest +find_package(doctest CONFIG REQUIRED) # https://eigen.tuxfamily.org/index.php?title=Main_Page find_package(Eigen3 CONFIG REQUIRED) @@ -103,22 +116,21 @@ find_package(Microsoft.GSL CONFIG REQUIRED) # https://www.pcg-random.org find_path(PCG_INCLUDE_DIRS "pcg_extras.hpp") +find_package(Boost REQUIRED COMPONENTS program_options) + # https://github.com/gabime/spdlog find_package(spdlog CONFIG REQUIRED) # https://github.com/intel/tbb find_package(TBB CONFIG REQUIRED) -# https://github.com/TartanLlama/expected -find_package(tl-expected CONFIG REQUIRED) - # https://github.com/TartanLlama/function_ref find_package(tl-function-ref CONFIG REQUIRED) # Header files include_directories(BEFORE ${PROJECT_SOURCE_DIR}/include) -# Catch +# doctest if(ENABLE_TESTING) enable_testing() message(STATUS "Building tests. Look at /tests for unit tests.") diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000000..a1d79cb672 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,426 @@ +{ + "version": 3, + "cmakeMinimumRequired": { + "major": 3, + "minor": 21, + "patch": 0 + }, + "configurePresets": [ + { + "name": "cmake-pedantic", + "hidden": true, + "warnings": { + "dev": true, + "deprecated": true, + "uninitialized": true, + "unusedCli": true, + "systemVars": false + },"errors": { + "dev": false, + "deprecated": true + } + }, + { + "name": "dev-mode", + "hidden": true, + "inherits": "cmake-pedantic", + "cacheVariables": { + "executable_DEVELOPER_MODE": "ON" + } + }, + { + "name": "ci-std", + "description": "This preset makes sure the project actually builds with at least the specified standard", + "hidden": true, + "binaryDir": "${sourceDir}/build", + "cacheVariables": { + "CMAKE_CXX_EXTENSIONS": "OFF", + "CMAKE_CXX_STANDARD": "23", + "CMAKE_CXX_STANDARD_REQUIRED": "ON" + } + }, + { + "name": "flags-unix", + "hidden": true, + "cacheVariables": { + "CMAKE_CXX_FLAGS": "-Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion -Wcast-qual -Wformat=2 -Werror=float-equal -Wshadow -Wcast-align -Wunused -Wnull-dereference -Wdouble-promotion -Wimplicit-fallthrough -Wextra-semi -Woverloaded-virtual -Wnon-virtual-dtor -Wold-style-cast" + } + }, + { + "name": "ci-unix", + "generator": "Ninja", + "hidden": true, + "inherits": ["flags-unix", "ci-std"], + "cacheVariables": { + "CMAKE_C_COMPILER": "clang", + "CMAKE_CXX_COMPILER": "clang++", + "CMAKE_BUILD_TYPE": "Release" + } + }, + { + "name": "coverage-unix", + "binaryDir": "${sourceDir}/build/coverage", + "inherits": "ci-unix", + "hidden": true, + "cacheVariables": { + "ENABLE_COVERAGE": "ON", + "CMAKE_BUILD_TYPE": "Coverage", + "CMAKE_CXX_FLAGS_COVERAGE": "-Og -g --coverage", + "CMAKE_EXE_LINKER_FLAGS_COVERAGE": "--coverage", + "CMAKE_SHARED_LINKER_FLAGS_COVERAGE": "--coverage" + } + }, + { + "name": "ci-coverage", + "inherits": ["coverage-unix", "dev-mode"], + "cacheVariables": { + "COVERAGE_HTML_COMMAND": "" + } + }, + { + "name": "fast-build", + "inherits": "ci-std", + "description" : "This preset is used for fast builds", + "generator": "Ninja", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "ENABLE_TESTING": false, + "ENABLE_CACHE": "ON" + } + }, + { + "name": "build", + "inherits": "ci-std", + "description": "This preset is used for builds with tests", + "generator": "Ninja", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "RelWithDebInfo", + "ENABLE_TESTING": true, + "ENABLE_CACHE": "ON" + } + }, + { + "name": "debug", + "inherits": "ci-std", + "description": "This preset is used for debug builds", + "generator": "Ninja", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "ENABLE_TESTING": true, + "ENABLE_CACHE": "OFF" + } + }, + { + "name": "appveyor", + "inherits": "ci-unix", + "description": "This preset is used for AppVeyor CI", + "generator": "Ninja", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "ENABLE_TESTING": true, + "ENABLE_CACHE": "OFF", + "CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" + } + }, + { + "name": "valgrind", + "inherits": "ci-std", + "description": "This preset is used for valgrind builds", + "generator": "Ninja", + "cacheVariables": { + "CMAKE_CXX_COMPILER": "g++", + "CMAKE_BUILD_TYPE": "Debug", + "ENABLE_VALGRIND": true, + "ENABLE_TESTING": true, + "ENABLE_CACHE": "OFF" + } + }, + { + "name": "asan", + "inherits": "ci-unix", + "description": "This preset is used for ASAN/UBSAN builds", + "cacheVariables": { + "ENABLE_SANITIZER_ADDRESS": true, + "ENABLE_SANITIZER_UNDEFINED_BEHAVIOR": true + } + }, + { + "name": "cppcheck", + "inherits": "ci-std", + "description": "This preset is used for cppcheck", + "cacheVariables": { + "CMAKE_CXX_COMPILER": "g++", + "CMAKE_BUILD_TYPE": "Debug", + "ENABLE_CPPCHECK": true + } + }, + { + "name": "lsan", + "inherits": "ci-unix", + "description": "This preset is used for LSAN builds", + "cacheVariables": { + "ENABLE_SANITIZER_LEAK": true + } + }, + { + "name": "msan", + "inherits": "ci-unix", + "description": "This preset is used for MSAN builds", + "cacheVariables": { + "ENABLE_SANITIZER_MEMORY": true + } + }, + { + "name": "tsan", + "inherits": "ci-unix", + "description": "This preset is used for TSAN builds", + "cacheVariables": { + "ENABLE_SANITIZER_THREAD": true + } + }, + { + "name": "conf-common", + "description": "General settings that apply to all configurations", + "hidden": true, + "generator": "Ninja", + "binaryDir": "${sourceDir}/out/build/${presetName}", + "installDir": "${sourceDir}/out/install/${presetName}" + }, + { + "name": "conf-windows-common", + "description": "Windows settings for MSBuild toolchain that apply to msvc and clang", + "hidden": true, + "inherits": "conf-common", + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + }, + "architecture": { + "value": "x64", + "strategy": "external" + }, + "toolset": { + "value": "host=x64", + "strategy": "external" + }, + "cacheVariables": { + "ENABLE_CPPCHECK_DEFAULT": "FALSE", + "ENABLE_CLANG_TIDY_DEFAULT": "FALSE" + } + }, + { + "name": "conf-unixlike-common", + "description": "Unix-like OS settings for gcc and clang toolchains", + "hidden": true, + "inherits": "conf-common", + "condition": { + "type": "inList", + "string": "${hostSystemName}", + "list": [ + "Linux", + "Darwin" + ] + }, + "vendor": { + "microsoft.com/VisualStudioRemoteSettings/CMake/1.0": { + "sourceDir": "$env{HOME}/.vs/$ms{projectDirName}" + } + } + }, + { + "name": "windows-msvc-debug-developer-mode", + "displayName": "msvc Debug (Developer Mode)", + "description": "Target Windows with the msvc compiler, debug build type", + "inherits": "conf-windows-common", + "cacheVariables": { + "CMAKE_C_COMPILER": "cl", + "CMAKE_CXX_COMPILER": "cl", + "CMAKE_BUILD_TYPE": "Debug", + "ENABLE_DEVELOPER_MODE": "ON" + } + }, + { + "name": "windows-msvc-release-developer-mode", + "displayName": "msvc Release (Developer Mode)", + "description": "Target Windows with the msvc compiler, release build type", + "inherits": "conf-windows-common", + "cacheVariables": { + "CMAKE_C_COMPILER": "cl", + "CMAKE_CXX_COMPILER": "cl", + "CMAKE_BUILD_TYPE": "RelWithDebInfo", + "ENABLE_DEVELOPER_MODE": "ON" + } + }, + { + "name": "windows-msvc-debug-user-mode", + "displayName": "msvc Debug (User Mode)", + "description": "Target Windows with the msvc compiler, debug build type", + "inherits": "conf-windows-common", + "cacheVariables": { + "CMAKE_C_COMPILER": "cl", + "CMAKE_CXX_COMPILER": "cl", + "CMAKE_BUILD_TYPE": "Debug", + "ENABLE_DEVELOPER_MODE": "OFF" + } + }, + { + "name": "windows-msvc-release-user-mode", + "displayName": "msvc Release (User Mode)", + "description": "Target Windows with the msvc compiler, release build type", + "inherits": "conf-windows-common", + "cacheVariables": { + "CMAKE_C_COMPILER": "cl", + "CMAKE_CXX_COMPILER": "cl", + "CMAKE_BUILD_TYPE": "RelWithDebInfo", + "ENABLE_DEVELOPER_MODE": "OFF" + } + }, + { + "name": "windows-clang-debug", + "displayName": "clang Debug", + "description": "Target Windows with the clang compiler, debug build type", + "inherits": "conf-windows-common", + "cacheVariables": { + "CMAKE_C_COMPILER": "clang-cl", + "CMAKE_CXX_COMPILER": "clang-cl", + "CMAKE_BUILD_TYPE": "Debug" + }, + "vendor": { + "microsoft.com/VisualStudioSettings/CMake/1.0": { + "intelliSenseMode": "windows-clang-x64" + } + } + }, + { + "name": "windows-clang-release", + "displayName": "clang Release", + "description": "Target Windows with the clang compiler, release build type", + "inherits": "conf-windows-common", + "cacheVariables": { + "CMAKE_C_COMPILER": "clang-cl", + "CMAKE_CXX_COMPILER": "clang-cl", + "CMAKE_BUILD_TYPE": "RelWithDebInfo" + }, + "vendor": { + "microsoft.com/VisualStudioSettings/CMake/1.0": { + "intelliSenseMode": "windows-clang-x64" + } + } + }, + { + "name": "unixlike-gcc-debug", + "displayName": "gcc Debug", + "description": "Target Unix-like OS with the gcc compiler, debug build type", + "inherits": "conf-unixlike-common", + "cacheVariables": { + "CMAKE_C_COMPILER": "gcc", + "CMAKE_CXX_COMPILER": "g++", + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "unixlike-gcc-release", + "displayName": "gcc Release", + "description": "Target Unix-like OS with the gcc compiler, release build type", + "inherits": "conf-unixlike-common", + "cacheVariables": { + "CMAKE_C_COMPILER": "gcc", + "CMAKE_CXX_COMPILER": "g++", + "CMAKE_BUILD_TYPE": "RelWithDebInfo" + } + }, + { + "name": "unixlike-clang-debug", + "displayName": "clang Debug", + "description": "Target Unix-like OS with the clang compiler, debug build type", + "inherits": "conf-unixlike-common", + "cacheVariables": { + "CMAKE_C_COMPILER": "clang", + "CMAKE_CXX_COMPILER": "clang++", + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "unixlike-clang-release", + "displayName": "clang Release", + "description": "Target Unix-like OS with the clang compiler, release build type", + "inherits": "conf-unixlike-common", + "cacheVariables": { + "CMAKE_C_COMPILER": "clang", + "CMAKE_CXX_COMPILER": "clang++", + "CMAKE_BUILD_TYPE": "RelWithDebInfo" + } + } + ], + "testPresets": [ + { + "name": "test-common", + "description": "Test CMake settings that apply to all configurations", + "hidden": true, + "output": { + "outputOnFailure": true + }, + "execution": { + "noTestsAction": "error", + "stopOnFailure": true + } + }, + { + "name": "test-windows-msvc-debug-developer-mode", + "displayName": "Strict", + "description": "Enable output and stop on failure", + "inherits": "test-common", + "configurePreset": "windows-msvc-debug-developer-mode" + }, + { + "name": "test-windows-msvc-release-developer-mode", + "displayName": "Strict", + "description": "Enable output and stop on failure", + "inherits": "test-common", + "configurePreset": "windows-msvc-release-developer-mode" + }, + { + "name": "test-windows-clang-debug", + "displayName": "Strict", + "description": "Enable output and stop on failure", + "inherits": "test-common", + "configurePreset": "windows-clang-debug" + }, + { + "name": "test-windows-clang-release", + "displayName": "Strict", + "description": "Enable output and stop on failure", + "inherits": "test-common", + "configurePreset": "windows-clang-release" + }, + { + "name": "test-unixlike-gcc-debug", + "displayName": "Strict", + "description": "Enable output and stop on failure", + "inherits": "test-common", + "configurePreset": "unixlike-gcc-debug" + }, + { + "name": "test-unixlike-gcc-release", + "displayName": "Strict", + "description": "Enable output and stop on failure", + "inherits": "test-common", + "configurePreset": "unixlike-gcc-release" + }, + { + "name": "test-unixlike-clang-debug", + "displayName": "Strict", + "description": "Enable output and stop on failure", + "inherits": "test-common", + "configurePreset": "unixlike-clang-debug" + }, + { + "name": "test-unixlike-clang-release", + "displayName": "Strict", + "description": "Enable output and stop on failure", + "inherits": "test-common", + "configurePreset": "unixlike-clang-release" + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md index 006d8df79f..24775d5efc 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,42 @@ # CDT-plusplus +**Quantize spacetime on your laptop.** -[![Build Status](https://img.shields.io/travis/acgetchell/CDT-plusplus.svg?label=Linux)](https://app.travis-ci.com/acgetchell/CDT-plusplus) -[![Windows Build status](https://img.shields.io/appveyor/ci/acgetchell/cdt-plusplus.svg?label=Windows)](https://ci.appveyor.com/project/acgetchell/cdt-plusplus) -[![](https://github.com/acgetchell/CDT-plusplus/workflows/macOS/badge.svg?label=Actions)](https://github.com/acgetchell/CDT-plusplus/actions) -[![Language grade: C/C++](https://img.shields.io/lgtm/grade/cpp/g/acgetchell/CDT-plusplus.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/acgetchell/CDT-plusplus/context:cpp) -[![Language grade: Python](https://img.shields.io/lgtm/grade/python/g/acgetchell/CDT-plusplus.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/acgetchell/CDT-plusplus/context:python) +[![Linux Clang](https://github.com/acgetchell/CDT-plusplus/actions/workflows/linux-clang.yml/badge.svg)](https://github.com/acgetchell/CDT-plusplus/actions/workflows/linux-clang.yml) +[![Linux GCC](https://github.com/acgetchell/CDT-plusplus/actions/workflows/linux-gcc.yml/badge.svg)](https://github.com/acgetchell/CDT-plusplus/actions/workflows/linux-gcc.yml) +[![macOS](https://github.com/acgetchell/CDT-plusplus/actions/workflows/macos.yml/badge.svg)](https://github.com/acgetchell/CDT-plusplus/actions/workflows/macos.yml) +[![Build status](https://ci.appveyor.com/api/projects/status/qjvbk6u86sp6cm59?svg=true&passingText=Windows)](https://ci.appveyor.com/project/acgetchell/cdt-plusplus) [![CodeQL](https://github.com/acgetchell/CDT-plusplus/actions/workflows/codeql-analysis.yml/badge.svg?branch=develop)](https://github.com/acgetchell/CDT-plusplus/actions/workflows/codeql-analysis.yml) [![codecov](https://codecov.io/gh/acgetchell/CDT-plusplus/branch/develop/graph/badge.svg)](https://codecov.io/gh/acgetchell/CDT-plusplus) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=acgetchell_CDT-plusplus&metric=alert_status)](https://sonarcloud.io/dashboard?id=acgetchell_CDT-plusplus) +[![cpp-linter](https://github.com/acgetchell/CDT-plusplus/actions/workflows/cpp-linter.yml/badge.svg)](https://github.com/acgetchell/CDT-plusplus/actions/workflows/cpp-linter.yml) +[![Whitespace](https://github.com/acgetchell/CDT-plusplus/actions/workflows/whitespace.yml/badge.svg)](https://github.com/acgetchell/CDT-plusplus/actions/workflows/whitespace.yml) [![Open Issues](https://img.shields.io/github/issues-raw/acgetchell/CDT-plusplus.svg)](https://github.com/acgetchell/CDT-plusplus/issues) [![Join the chat at https://gitter.im/acgetchell/CDT-plusplus](https://img.shields.io/badge/gitter-join%20chat%20→-brightgreen.svg)](https://gitter.im/acgetchell/CDT-plusplus) [![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/acgetchell/CDT-plusplus) -**Quantize spacetime on your laptop.** - -![Small foliated Delaunay triangulation](docs/images/t8-v68-s298.png "8 timeslices 68 vertices 298 simplices") +![Small foliated Delaunay triangulation](docs/images/S3-7-27528-I1-R1.png "7 timeslices 27528 simplices") + +## Table of contents + +- [CDT-plusplus](#cdt-plusplus) + - [Introduction](#introduction) + - [Roadmap](#roadmap) + - [Quickstart](#quickstart) + - [Setup](#setup) + - [Short](#short) + - [Long](#long) + - [Build](#build) + - [Project Layout](#project-layout) + - [Run](#run) + - [Usage](#usage) + - [Documentation](#documentation) + - [Testing](#testing) + - [Static Analysis](#static-analysis) + - [Sanitizers](#sanitizers) + - [Optimizing Parameters](#optimizing-parameters) + - [Visualization](#visualization) + - [Contributing](#contributing) + - [Issues](#issues) ## Introduction @@ -24,32 +46,30 @@ including the foundations and recent results, please see the [wiki](https://gith [Causal Dynamical Triangulations][CDT] in [C++] uses the [Computational Geometry Algorithms Library][CGAL], [Boost], [TBB], and [Eigen]. Arbitrary-precision numbers and functions are by [MPFR] and [GMP]. -[Docopt] provides a beautiful command-line interface. [Melissa E. O'Neill's Permuted Congruential Generators][PCG] library provides high-quality RNGs that pass L'Ecuyer's [TestU01] statistical tests. -[Catch] provides [BDD]/[TDD]. +[doctest] provides [BDD]/[TDD]. [vcpkg] provides library management and building. [Doxygen] provides automated document generation. [{fmt}] provides a safe and fast alternative to `iostream`. [spdlog] provides fast, multithreaded logging. -[PVS-Studio] and [LGTM] provide commercial-grade static analysis and security checks. +[PVS-Studio] and [CodeQL] provide commercial-grade static analysis and security checks. [CometML] provides machine learning for model building. -## Goals +## Roadmap - [x] Cross-platform support on Linux, macOS (x64 & arm64), and Windows - [x] Cross-compiler support on gcc, clang, and MSVC - [x] Develop with [literate programming] using [Doxygen] - [x] [Efficient Pure Functional Programming in C++ Using Move Semantics][functional] - [x] Test using [CTest] -- [x] Develop using Behavior-driven development ([BDD]) with [Catch] +- [x] Develop using Behavior-driven development ([BDD]) with [doctest] - [x] Continuous integration by [Travis-CI] on macOS and Linux with [gcc]/[Clang] - [x] Continuous integration by [AppVeyor] on Windows with [MSVC] - [x] Continuous integration by [Github Actions] on the leading edge - [x] 3D Simplex - [x] 3D Spherical triangulation - [x] 2+1 foliation -- [x] Integrate [Docopt] CLI - [x] S3 Bulk action - [x] 3D Ergodic moves - [x] High-quality Random Number Generation with M.E. O'Neill's [PCG] library @@ -60,8 +80,8 @@ Arbitrary-precision numbers and functions are by [MPFR] and [GMP]. - [x] Static code analysis with [PVS-Studio] - [x] 3D Metropolis algorithm - [x] Multithreaded logging with [spdlog] +- [x] Visualization with [Qt] - [ ] Output via [HDF5] -- [ ] A [Surface mesh] manifold of 3D Triangulation - [ ] 4D Simplex - [ ] 4D Spherical triangulation - [ ] 3+1 foliation @@ -80,8 +100,8 @@ Arbitrary-precision numbers and functions are by [MPFR] and [GMP]. ## Setup -This project uses [CMake]+[Ninja] to build and [vcpkg] to manage C++ libraries. Using [C++]20 features, it successfully -builds with AppleClang, [gcc-10], [clang-10], and [Visual Studio 2019]. +This project uses [CMake]+[Ninja] to build and [vcpkg] to manage C++ libraries. Using [C++20] features, it successfully +builds with [AppleClang-14], [gcc-12], [clang-15], and [Visual Studio 2019]. ### Short @@ -92,7 +112,7 @@ docker pull acgetchell/cdt-plusplus docker run -it --name cdt cdt-plusplus ``` Binaries will be in `/CDT-plusplus/build/src`. -Proceed to [Use](#use). +Proceed to [Use](#usage). ### Long @@ -108,6 +128,7 @@ package manager (e.g. [homebrew] or [apt]): - texinfo - yasm - ninja (macOS) or ninja-build (Linux) +- pkg-config (macOS) Next, install [vcpkg]: @@ -125,7 +146,7 @@ Proceed to [Build](#build). ## Build -You'll need a reasonably modern compiler that supports C++20 features. +You'll need a reasonably modern compiler that supports [C++20] features. Clone the repo: @@ -172,27 +193,31 @@ The project is similar to [PitchFork Layout], as follows: - tests - Unit tests ### Run -If you want to get started right away, run `fast-build.sh` or `fast-build.bat`, depending on your operating system, -from `scripts`. This will compile the appropriate executables in `RELEASE` mode with no tests. +Run one of the following in `scripts`, depending on your operating system and environment: + +- No unit tests, `Release` mode - `fast-build.sh` or `fast-build.bat` +- Unit tests, `RelWithDebInfo` mode - `build.sh` or `build.bat` +- On an HPC cluster with [SLURM], [modules], and [spack] - `slurm.sh` +- Full debugging mode with asserts and tests, `debug.sh` (this will take some time, ~280 seconds on my current laptop) This should result in the main program executable, `cdt` in `build/src` or `build\Debug`, along with several others. - `cdt-opt` is a simplified version with hard-coded inputs, mainly useful for debugging and scripting -- `initialize` is used by [CometML] to run [parameter optimization](#optimize-parameters) +- `cdt-viewer` (macOS only) is a simple Qt-based viewer for the output of `cdt` +- `initialize` is used by [CometML] to run [parameter optimization](#optimizing-parameters) -## Use +## Usage -CDT-plusplus uses [Docopt] to parse options from the help message, and so +CDT-plusplus uses [program_options] to parse options from the help message, and so understands long or short argument formats, provided the short argument given is an unambiguous match to a longer one. The help message should be instructive: ~~~text ./build/src/cdt --help -cdt started at 2021-08-08.20:18:58PDT Causal Dynamical Triangulations in C++ using CGAL. -Copyright (c) 2014-2021 Adam Getchell +Copyright (c) 2013 Adam Getchell A program that generates d-dimensional triangulated spacetimes with a defined causal structure and evolves them according @@ -200,30 +225,38 @@ to the Metropolis algorithm. Specify the number of passes to control how much evolution is desired. Each pass attempts a number of ergodic moves equal to the number of simplices in the simulation. -Usage:./cdt (--spherical | --toroidal) -n SIMPLICES -t TIMESLICES [-d DIM] - [--init INITIAL] [--foliate FOLIATION] -k K --alpha ALPHA - --lambda LAMBDA [-p PASSES] [-c CHECKPOINT] +Usage:./cdt (--spherical | --toroidal) -n SIMPLICES -t TIMESLICES + [-d DIM] + [--init INITIAL RADIUS] + [--foliate FOLIATION SPACING] + -k K + --alpha ALPHA + --lambda LAMBDA + [-p PASSES] + [-c CHECKPOINT] + +Optional arguments are in square brackets. Examples: ./cdt --spherical -n 32000 -t 11 --alpha 0.6 -k 1.1 --lambda 0.1 --passes 1000 -./cdt --s -n32000 -t11 -a.6 -k1.1 -l.1 -p1000 +./cdt -s -n32000 -t11 -a.6 -k1.1 -l.1 -p1000 Options: - -h --help Show this message - --version Show program version - -n SIMPLICES Approximate number of simplices - -t TIMESLICES Number of timeslices - -d DIM Dimensionality [default: 3] - -i --init INITIAL Initial radius [default: 1] - --foliate FOLIATION Foliation spacing between timeslices [default: 1] - -a --alpha ALPHA Negative squared geodesic length of 1-d - timelike edges - -k K K = 1/(8*pi*G_newton) - -l --lambda LAMBDA K * Cosmological constant - -p --passes PASSES Number of passes [default: 100] - -c --checkpoint CHECKPOINT Checkpoint every n passes [default: 10] - - + -h [ --help ] Show this message + -v [ --version ] Show program version + -s [ --spherical ] Spherical topology + -e [ --toroidal ] Toroidal topology + -n [ --simplices ] arg Approximate number of simplices + -t [ --timeslices ] arg Number of timeslices + -d [ --dimensions ] arg (=3) Dimensionality + -i [ --init ] arg (=1) Initial radius + -f [ --foliate ] arg (=1) Foliation spacing + -a [ --alpha ] arg Negative squared geodesic length of 1-d + timelike edges + -k [ --k ] arg K = 1/(8*pi*G_newton) + -l [ --lambda ] arg K * Cosmological constant + -p [ --passes ] arg (=100) Number of passes + -c [ --checkpoint ] arg (=10) Checkpoint every n passes ~~~ The dimensionality of the spacetime is such that each slice of spacetime is @@ -234,10 +267,9 @@ spacelike (all on the same timeslice) as well as some that are timelike (span two timeslices). In [CDT] we actually care more about the timelike links (in 2+1 spacetime), and the timelike faces (in 3+1 spacetime). -## Document +## Documentation -Online documentation is at automatically -generated by [Travis-CI]. +Online documentation is at . If you have [Doxygen] installed you can generate the same information locally using the configuration file in `docs\Doxyfile` by simply typing at the top @@ -255,23 +287,23 @@ various graphs to be autogenerated by [Doxygen] using [GraphViz]. If you do not have GraphViz installed, set this option to **NO** (along with `UML_LOOK`). -## Test +## Testing In the `scripts` directory, run `build.sh` or `build.bat` depending on your operating system. -Unit tests run (in `build/tests` or `build\tests\Debug`) via `CDT_test`, the [Catch] executable: +Unit tests run (in `build/tests` or `build\tests\Debug`) via `CDT_unit_tests`, the [doctest] executable: ~~~bash -./CDT_test +./CDT_unit_tests ~~~ or (Windows): ~~~cmd -CDT_test.exe +CDT_unit_tests.exe ~~~ -You can also run both [CTest] integration and [Catch] unit tests in the `build` directory with: +You can also run both [CTest] integration and [doctest] unit tests in the `build` directory with: ~~~bash cmake --build . --target test @@ -313,16 +345,24 @@ cd scripts but slower static analysis integrated with [CMake] and [Ninja]. ~~~bash +cd scripts ./scan.sh ~~~ +[PVS-Studio] - static analyzer for C, C++, C#, and Java code. + +~~~bash +cd scripts +./pvs-studio.sh +~~~ + ### Sanitizers [AddressSanitizer] + [UndefinedBehaviorSanitizer], [LeakSanitizer], [MemorySanitizer], and [ThreadSanitizer] are run with `scripts/asan.sh`, `scripts/lsan.sh`, `scripts/msan.sh`, and `scripts/tsan.sh`. They are also checked by [Travis-CI] during commits. -## Optimize Parameters +## Optimizing Parameters [CometML] is used to record [Experiments] which conduct [Model Optimization]. The script to do this is `optimize-initialize.py`. In order for this to work, you must install the following @@ -335,12 +375,11 @@ pip install comet-ml You can then run experiments and look at results on https://www.comet.ml! -## Visualize +## Visualization -Currently, looking for a good visualization library. [GeomView] has been deprecated -and the interface with [CGAL] is not maintained. +[Qt] is used to visualize 3D triangulations via `cdt-viewer`. -## Contribute +## Contributing Please see [CONTRIBUTING.md] and our [CODE_OF_CONDUCT.md]. @@ -362,9 +401,6 @@ Your code should pass Continuous Integration: - [ThreadSanitizer] test with [tsan.sh] -- [LGTM] check to ensure you haven't introduced a security vulnerability. Look at the [query console] for - more details. - - [Sonarcloud] provides a lot of good suggestions. Optional: @@ -375,28 +411,17 @@ Optional: ## Issues -[vcpkg]'s version of [date] has an unfixed bug [#23637] which produces `use-of-uninitialized-value` in [MemorySanitizer]. - -[Catch] also produces ([#2395]) `use-of-uninitialized-value` in [MemorySanitizer], but this won't be addressed. - -[Docopt] also has a `use-of-uninitialized-value` bug ([#149]). - -[GMP] won't build on `arm64-windows` so that platform is not supported [#24323]. - -`harupy/find-trailing-whitespace` has a pull-request to only check changed files [#18], until then it fails from finding -whitespace in graphic files for [Doxygen]. +- [vcpkg]'s version of [date] has an unfixed bug [#23637] which produces `use-of-uninitialized-value` in [MemorySanitizer]. +- [vcpkg] has issues with `fontconfig:arm64-osx` [#40623]. -[#18]: https://github.com/harupy/find-trailing-whitespace/pull/18 -[#24323]: https://github.com/microsoft/vcpkg/issues/24323 +[#40623]: https://github.com/microsoft/vcpkg/issues/40623 [#23637]: https://github.com/microsoft/vcpkg/issues/23637 -[#2395]: https://github.com/catchorg/Catch2/issues/2395 -[#149]: https://github.com/docopt/docopt.cpp/issues/149 [CDT]: https://arxiv.org/abs/hep-th/0105267 [CGAL]: https://www.cgal.org [CMake]: https://www.cmake.org [Clang]: https://clang.llvm.org [gcc]: https://gcc.gnu.org/ -[Catch]: https://github.com/catchorg/Catch2/blob/master/docs/Readme.md +[doctest]: https://github.com/doctest/doctest [guidelines]: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines [clang-tidy.sh]: https://github.com/acgetchell/CDT-plusplus/blob/master/clang-tidy.sh [CTest]: https://gitlab.kitware.com/cmake/community/wikis/doc/ctest/Testing-With-CTest @@ -405,7 +430,7 @@ whitespace in graphic files for [Doxygen]. [Doxygen]: http://www.doxygen.org [Homebrew]: https://brew.sh [Ninja]: https://ninja-build.org -[Docopt]: https://github.com/docopt/docopt.cpp +[program_options]: https://www.boost.org/doc/libs/1_85_0/doc/html/program_options/tutorial.html [Mathjax]: https://www.mathjax.org [GraphViz]: https://www.graphviz.org [Eigen]: http://eigen.tuxfamily.org/index.php?title=Main_Page @@ -432,19 +457,17 @@ whitespace in graphic files for [Doxygen]. [BDD]: https://en.wikipedia.org/wiki/Behavior-driven_development [TDD]: https://en.wikipedia.org/wiki/Test-driven_development [.appveyor.yml]: https://github.com/acgetchell/CDT-plusplus/blob/master/.appveyor.yml -[LGTM]: https://lgtm.com/projects/g/acgetchell/CDT-plusplus/ [CometML]: https://www.comet.ml/ [Experiments]: https://www.comet.ml/acgetchell/cdt-plusplus [Model Optimization]: https://www.comet.ml/parameter-optimization [virtual environment]: https://docs.python.org/3/tutorial/venv.html [vcpkg]: https://github.com/Microsoft/vcpkg -[clang-10]: https://releases.llvm.org/10.0.0/tools/clang/docs/ReleaseNotes.html -[gcc-10]: https://gcc.gnu.org/gcc-10/ +[clang-15]: https://releases.llvm.org/15.0.0/tools/clang/docs/ReleaseNotes.html +[gcc-12]: https://gcc.gnu.org/gcc-12/ [C++]: https://isocpp.org/ -[GeomView]: https://www.geomview.org +[C++20]: https://en.cppreference.com/w/cpp/20 [development]: https://github.com/acgetchell/CDT-plusplus [Pitchfork Layout]: https://api.csswg.org/bikeshed/?force=1&url=https://raw.githubusercontent.com/vector-of-bool/pitchfork/develop/data/spec.bs#tld.docs -[Surface mesh]: https://doc.cgal.org/latest/Surface_mesher/index.html [PCG]: http://www.pcg-random.org/paper.html [TestU01]: http://simul.iro.umontreal.ca/testu01/tu01.html [apt]: https://wiki.debian.org/Apt @@ -457,7 +480,6 @@ whitespace in graphic files for [Doxygen]. [3]: https://github.com/microsoft/vcpkg/issues/8627 [CONTRIBUTING.md]: https://github.com/acgetchell/CDT-plusplus/blob/develop/.github/CONTRIBUTING.md [CODE_OF_CONDUCT.md]: https://github.com/acgetchell/CDT-plusplus/blob/develop/.github/CODE_OF_CONDUCT.md -[query console]: https://lgtm.com/query/lang:cpp/ [Github Actions]: https://github.com/features/actions [Visual Studio 2019]: https://visualstudio.microsoft.com/vs/ [{fmt}]: https://github.com/fmtlib/fmt @@ -472,7 +494,7 @@ whitespace in graphic files for [Doxygen]. [lsan.sh]: https://github.com/acgetchell/CDT-plusplus/blob/develop/scripts/lsan.sh [msan.sh]: https://github.com/acgetchell/CDT-plusplus/blob/develop/scripts/msan.sh [tsan.sh]: https://github.com/acgetchell/CDT-plusplus/blob/develop/scripts/tsan.sh -[PVS-Studio]: https://www.viva64.com/en/pvs-studio/ +[PVS-Studio]: https://pvs-studio.com/en/pvs-studio/?utm_source=github&utm_medium=organic&utm_campaign=open_source [pvs-studio.sh]: https://github.com/acgetchell/CDT-plusplus/blob/develop/scripts/pvs-studio.sh [CLion]: https://www.jetbrains.com/clion/ [Docker]: https://www.docker.com/ @@ -481,4 +503,12 @@ whitespace in graphic files for [Doxygen]. [vcpkg.json]: https://github.com/acgetchell/CDT-plusplus/blob/develop/vcpkg.json [Sonarcloud]: https://sonarcloud.io/project/overview?id=acgetchell_CDT-plusplus [spdlog]: https://github.com/gabime/spdlog -[triplet]: https://vcpkg.readthedocs.io/en/latest/users/triplets/#additional-remarks \ No newline at end of file +[triplet]: https://vcpkg.readthedocs.io/en/latest/users/triplets/#additional-remarks +[AppleClang-14]: https://developer.apple.com/documentation/xcode-release-notes/xcode-14-release-notes +[spack]: https://spack.io +[modules]: https://hpc-wiki.info/hpc/Modules +[SLURM]: https://hpc-wiki.info/hpc/SLURM +[Qt]: https://www.qt.io +[CodeQL]: https://codeql.github.com +[CodeCov]: https://app.codecov.io/gh/acgetchell/CDT-plusplus +[gcov]: https://gcc.gnu.org/onlinedocs/gcc/Gcov.html \ No newline at end of file diff --git a/cmake/.clang-tidy b/cmake/.clang-tidy new file mode 100644 index 0000000000..3b209bfbc3 --- /dev/null +++ b/cmake/.clang-tidy @@ -0,0 +1,2 @@ +# Disable all checks in folder +Checks: '-*' \ No newline at end of file diff --git a/cmake/CompilerWarnings.cmake b/cmake/CompilerWarnings.cmake index 12bdf54c23..59ef0394df 100644 --- a/cmake/CompilerWarnings.cmake +++ b/cmake/CompilerWarnings.cmake @@ -47,6 +47,10 @@ function(set_project_warnings project_name) -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) + -Wsign-conversion # warn on sign conversions + -Wcast-qual # warn on casts which remove qualifiers + -Wimplicit-fallthrough # warn on implicit fallthrough in unreachable code + -Wextra-semi # warn on extra semicolons ) if(WARNINGS_AS_ERRORS) diff --git a/cmake/Sanitizers.cmake b/cmake/Sanitizers.cmake index 350397d6dd..0c179c4441 100644 --- a/cmake/Sanitizers.cmake +++ b/cmake/Sanitizers.cmake @@ -16,8 +16,8 @@ function(enable_sanitizers project_name) option(ENABLE_VALGRIND "Enable Valgrind" OFF) if(ENABLE_VALGRIND) - target_compile_options(project_options INTERFACE -g -O0 -fsanitize=address) - target_link_libraries(project_options INTERFACE -fsanitize=address) + target_compile_options(project_options INTERFACE -g -O0) + target_link_libraries(project_options INTERFACE -fsanitize=address -static-libasan) message(STATUS "Valgrind enabled.") set(MEMORYCHECK_COMMAND_OPTIONS "${MEMORYCHECK_COMMAND_OPTIONS} --leak-check=full") set(MEMORYCHECK_COMMAND_OPTIONS "${MEMORYCHECK_COMMAND_OPTIONS} --track-fds=yes") diff --git a/cmake/StandardProjectSettings.cmake b/cmake/StandardProjectSettings.cmake index 71f29a8530..83923a05e7 100644 --- a/cmake/StandardProjectSettings.cmake +++ b/cmake/StandardProjectSettings.cmake @@ -38,8 +38,8 @@ endif() # Set minimum Boost version set(BOOST_MIN_VERSION "1.75.0") -# Use C++20 -set(CMAKE_CXX_STANDARD 20) +# Use C++23 +set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) @@ -54,3 +54,10 @@ add_definitions(-DCGAL_TRIANGULATION_NO_ASSERTIONS -DCGAL_TRIANGULATION_NO_POSTC # Easier navigation in an IDE when projects are organized in folders. set_property(GLOBAL PROPERTY USE_FOLDERS ON) + +# Deal with UTF-8 encoding +if (MSVC) + add_compile_options(/utf-8) +else() + add_compile_options(-finput-charset=UTF-8) +endif() diff --git a/docs/images/S3-7-27528-I1-R1.png b/docs/images/S3-7-27528-I1-R1.png new file mode 100644 index 0000000000..a510b34566 Binary files /dev/null and b/docs/images/S3-7-27528-I1-R1.png differ diff --git a/include/Apply_move.hpp b/include/Apply_move.hpp index a34b75885a..a9fd4627e6 100644 --- a/include/Apply_move.hpp +++ b/include/Apply_move.hpp @@ -13,28 +13,27 @@ #include -#include +#include #include -#include #include -/// @brief An applicative function similar to std::apply, but on manifolds -/// @tparam ManifoldType The type (topology, dimensionality) of manifold -/// @tparam ExpectedType The result of the move on the manifold -/// @tparam FunctionType The type of move applied to the manifold -/// @param t_manifold The manifold on which to make the Pachner move -/// @param t_move The Pachner move -/// @return The expected or unexpected result in a tl::expected -/// @see https://tl.tartanllama.xyz/en/latest/api/function_ref.html -/// @see https://tl.tartanllama.xyz/en/latest/api/expected.html +/** + * \brief An applicative function similar to std::apply on a manifold + * \tparam ManifoldType The type (topology, dimensionality) of manifold + * \tparam ExpectedType The result type of the move on the manifold + * \tparam FunctionType The type of move applied to the manifold + * \param t_manifold The manifold on which to make the Pachner move + * \param t_move The Pachner move + * \return The expected or unexpected result in a std::expected + */ template , + typename ExpectedType = std::expected, typename FunctionType = tl::function_ref> -constexpr auto apply_move(ManifoldType&& t_manifold, +auto constexpr apply_move(ManifoldType&& t_manifold, FunctionType t_move) noexcept -> decltype(auto) { if (auto result = std::invoke(t_move, std::forward(t_manifold)); - result) + result.has_value()) { return result; } diff --git a/include/Ergodic_moves_3.hpp b/include/Ergodic_moves_3.hpp index 51e3b56093..6629b0e756 100644 --- a/include/Ergodic_moves_3.hpp +++ b/include/Ergodic_moves_3.hpp @@ -7,25 +7,37 @@ /// @file Ergodic_moves_3.hpp /// @brief Pachner moves on 2+1 dimensional foliated Delaunay triangulations /// @author Adam Getchell +/// @details Pachner moves operate on the level of the Manifold_3. +/// The helper functions for the moves operate on the level of the +/// Delaunay_Triangulation_3. +/// C++23 support is required for std::expected. #ifndef CDT_PLUSPLUS_ERGODIC_MOVES_3_HPP #define CDT_PLUSPLUS_ERGODIC_MOVES_3_HPP -#include +#include +#include "Manifold.hpp" #include "Move_tracker.hpp" -using Expected = tl::expected; - namespace ergodic_moves { + using Manifold = manifolds::Manifold_3; + using Expected = std::expected; + using Cell_handle = Cell_handle_t<3>; + using Cell_container = std::vector; + using Edge_handle = Edge_handle_t<3>; + using Edge_container = std::vector; + using Vertex_handle = Vertex_handle_t<3>; + using Vertex_container = std::vector; + using Delaunay = Delaunay_t<3>; /// @brief Perform a null move /// /// @param t_manifold The simplicial manifold - /// @return The null-moved manifold - [[nodiscard]] inline auto null_move( - manifolds::Manifold3 const& t_manifold) noexcept -> Expected + /// @returns The null-moved manifold + [[nodiscard]] inline auto null_move(Manifold const& t_manifold) noexcept + -> Expected { return t_manifold; } // null_move @@ -33,12 +45,11 @@ namespace ergodic_moves /// @brief Perform a TriangulationDataStructure_3::flip on a facet /// @param t_manifold The manifold containing the cell to flip /// @param to_be_moved The cell on which to try the move - /// @return If move succeeded + /// @returns True if move succeeded /// @see /// https://doc.cgal.org/latest/TDS_3/classTriangulationDataStructure__3.html#a2ad2941984c1eac5561665700bfd60b4 - [[nodiscard]] inline auto try_23_move( - manifolds::Manifold3& t_manifold, - Cell_handle_t<3> const& to_be_moved) noexcept -> bool + [[nodiscard]] inline auto try_23_move(Manifold& t_manifold, + Cell_handle const& to_be_moved) -> bool { if (to_be_moved->info() != 22) { return false; } // NOLINT auto flipped = false; @@ -71,9 +82,8 @@ namespace ergodic_moves /// If successful, the triangulation is no longer Delaunay. /// /// @param t_manifold The simplicial manifold - /// @return The (2,3) moved manifold - [[nodiscard]] inline auto do_23_move( - manifolds::Manifold3& t_manifold) noexcept -> Expected + /// @returns The Expected (2,3) moved manifold or an Unexpected + [[nodiscard]] inline auto do_23_move(Manifold& t_manifold) -> Expected { #ifndef NDEBUG spdlog::debug("{} called.\n", __PRETTY_FUNCTION__); @@ -81,43 +91,31 @@ namespace ergodic_moves auto two_two = t_manifold.get_triangulation().get_two_two(); // Shuffle the container to create a random sequence of (2,2) cells - std::shuffle(two_two.begin(), two_two.end(), - utilities::make_random_generator()); + std::ranges::shuffle(two_two, utilities::make_random_generator()); // Try a (2,3) move on successive cells in the sequence - if (std::any_of(two_two.begin(), two_two.end(), - [&](auto& cell) { return try_23_move(t_manifold, cell); })) + if (std::ranges::any_of( + two_two, [&](auto& cell) { return try_23_move(t_manifold, cell); })) { return t_manifold; } // We've run out of (2,2) cells - std::string msg = "No (2,3) move possible.\n"; + std::string const msg = "No (2,3) move possible.\n"; spdlog::warn(msg); - return tl::make_unexpected(msg); + return std::unexpected(msg); } /// @brief Perform a TriangulationDataStructure_3::flip on an edge /// @param t_manifold The manifold containing the edge to flip /// @param to_be_moved The edge on which to try the move - /// @return If move succeeded + /// @returns True if move succeeded /// @see /// https://doc.cgal.org/latest/TDS_3/classTriangulationDataStructure__3.html#a5837d666e4198f707f862003c1ffa033 - [[nodiscard]] inline auto try_32_move( - manifolds::Manifold3& t_manifold, - Edge_handle_t<3> const& to_be_moved) noexcept -> bool + [[nodiscard]] inline auto try_32_move(Manifold& t_manifold, + Edge_handle const& to_be_moved) -> bool { - if (t_manifold.triangulation().flip(to_be_moved.first, to_be_moved.second, - to_be_moved.third)) - { -#ifndef NDEBUG - spdlog::trace("Edge was flippable.\n"); -#endif - return true; - } -#ifndef NDEBUG - spdlog::trace("Edge not flippable.\n"); -#endif - return false; - } + return t_manifold.triangulation().flip( + to_be_moved.first, to_be_moved.second, to_be_moved.third); + } // try_32_move /// @brief Perform a (3,2) move /// @details A (3,2) move "flips" a timelike edge into a timelike face. @@ -126,36 +124,35 @@ namespace ergodic_moves /// randomly shuffled container until it succeeds or runs out of edges. /// If successful, the triangulation is no longer Delaunay. /// @param t_manifold The simplicial manifold - /// @return The (3,2) moved manifold - [[nodiscard]] inline auto do_32_move( - manifolds::Manifold3& t_manifold) noexcept -> Expected + /// @returns The Expected (3,2) moved manifold or an Unexpected + [[nodiscard]] inline auto do_32_move(Manifold& t_manifold) -> Expected { #ifndef NDEBUG spdlog::debug("{} called.\n", __PRETTY_FUNCTION__); #endif auto timelike_edges = t_manifold.get_timelike_edges(); // Shuffle the container to create a random sequence of edges - std::shuffle(timelike_edges.begin(), timelike_edges.end(), - utilities::make_random_generator()); + std::ranges::shuffle(timelike_edges, utilities::make_random_generator()); // Try a (3,2) move on successive timelike edges in the sequence - if (std::any_of(timelike_edges.begin(), timelike_edges.end(), - [&](auto& edge) { return try_32_move(t_manifold, edge); })) + if (std::ranges::any_of(timelike_edges, [&](auto& edge) { + return try_32_move(t_manifold, edge); + })) { return t_manifold; } // We've run out of edges to try - std::string msg = "No (3,2) move possible.\n"; + std::string const msg = "No (3,2) move possible.\n"; spdlog::warn(msg); - return tl::make_unexpected(msg); + return std::unexpected(msg); } // do_32_move() /// @brief Find a (2,6) move location /// @details This function checks to see if a (2,6) move is possible. Starting /// with a (1,3) simplex, it checks neighbors for a (3,1) simplex. /// @param t_cell The (1,3) simplex that is checked - /// @return The integer of the neighboring (3,1) simplex if there is one - [[nodiscard]] inline auto find_adjacent_31_cell( - Cell_handle_t<3> const& t_cell) noexcept -> std::optional + /// @returns The integer of the neighboring (3,1) simplex or nullopt + [[nodiscard]] inline auto find_adjacent_31_cell(Cell_handle const& t_cell) + -> std::optional { if (t_cell->info() != 13) { return std::nullopt; } // NOLINT for (auto i = 0; i < 4; ++i) @@ -186,9 +183,8 @@ namespace ergodic_moves /// @image html 26.png /// @image latex 26.eps width=7cm /// @param t_manifold The simplicial manifold - /// @return The (2,6) moved manifold - [[nodiscard]] inline auto do_26_move( - manifolds::Manifold3& t_manifold) noexcept -> Expected + /// @returns The Expected (2,6) moved manifold or an Unexpected + [[nodiscard]] inline auto do_26_move(Manifold& t_manifold) -> Expected { #ifndef NDEBUG spdlog::debug("{} called.\n", __PRETTY_FUNCTION__); @@ -196,8 +192,7 @@ namespace ergodic_moves static auto constexpr INCIDENT_CELLS_FOR_6_2_MOVE = 6; auto one_three = t_manifold.get_triangulation().get_one_three(); // Shuffle the container to pick a random sequence of (1,3) cells to try - std::shuffle(one_three.begin(), one_three.end(), - utilities::make_random_generator()); + std::ranges::shuffle(one_three, utilities::make_random_generator()); for (auto const& bottom : one_three) { if (auto neighboring_31_index = find_adjacent_31_cell(bottom); @@ -206,17 +201,16 @@ namespace ergodic_moves #ifndef NDEBUG spdlog::trace("neighboring_31_index is {}.\n", *neighboring_31_index); #endif - Cell_handle_t<3> const top = - bottom->neighbor(neighboring_31_index.value()); + Cell_handle const top = bottom->neighbor(neighboring_31_index.value()); // Calculate the common face with respect to the bottom cell auto common_face_index = std::numeric_limits::max(); if (!bottom->has_neighbor(top, common_face_index)) { - std::string msg = "Bottom cell does not have a neighbor.\n"; + std::string const msg = "Bottom cell does not have a neighbor.\n"; #ifndef NDEBUG spdlog::trace(msg); #endif - return tl::make_unexpected(msg); + return std::unexpected(msg); } // Get indices of vertices of common face with respect to bottom cell @@ -237,56 +231,58 @@ namespace ergodic_moves // Timeslice of vertices should be same if (v_1->info() != v_2->info() || v_2->info() != v_3->info()) { - std::string msg = "Vertices have different timeslices.\n"; + std::string const msg = "Vertices have different timeslices.\n"; #ifndef NDEBUG spdlog::trace(msg); #endif - return tl::make_unexpected(msg); + return std::unexpected(msg); } // Do the (2,6) move // Insert new vertex - Vertex_handle_t<3> v_center = + Vertex_handle const v_center = t_manifold.triangulation().delaunay().tds().insert_in_facet( bottom, *neighboring_31_index); // Checks - std::vector> incident_cells; + Cell_container incident_cells; t_manifold.triangulation().delaunay().tds().incident_cells( v_center, std::back_inserter(incident_cells)); // the (2,6) center vertex should be bounded by 6 simplices if (incident_cells.size() != INCIDENT_CELLS_FOR_6_2_MOVE) { - std::string msg = "Center vertex is not bounded by 6 simplices.\n"; + std::string const msg = + "Center vertex is not bounded by 6 simplices.\n"; #ifndef NDEBUG spdlog::trace(msg); #endif - return tl::make_unexpected(msg); + return std::unexpected(msg); } // Each incident cell should be combinatorially and geometrically valid if (auto check_cells = - std::all_of(incident_cells.begin(), incident_cells.end(), - [&t_manifold](auto const& cell) { - return t_manifold.get_triangulation() - .get_delaunay() - .tds() - .is_cell(cell); - }); + std::ranges::all_of(incident_cells, + [&t_manifold](auto const& cell) { + return t_manifold.get_triangulation() + .get_delaunay() + .tds() + .is_cell(cell); + }); !check_cells) { - std::string msg = "A cell is invalid.\n"; + std::string const msg = "A cell is invalid.\n"; #ifndef NDEBUG spdlog::trace(msg); #endif - return tl::make_unexpected(msg); + return std::unexpected(msg); } // Now assign a geometric point to the center vertex auto center_point = CGAL::centroid(v_1->point(), v_2->point(), v_3->point()); #ifndef NDEBUG - spdlog::trace("Center point is: ({}).\n", center_point); + spdlog::trace("Center point is: ({}).\n", + utilities::point_to_str(center_point)); #endif v_center->set_point(center_point); @@ -302,19 +298,19 @@ namespace ergodic_moves else { spdlog::trace("It's not a vertex in the TDS.\n"); } spdlog::trace("Spacelike face timevalue is {}.\n", timevalue); spdlog::trace("Inserted vertex ({}) with timevalue {}.\n", - v_center->point(), v_center->info()); + utilities::point_to_str(v_center->point()), + v_center->info()); #endif // Final checks // is_valid() checks for combinatorial and geometric validity - if (!t_manifold.get_triangulation().get_delaunay().tds().is_valid( - v_center, true, 1)) + if (!t_manifold.get_delaunay().tds().is_valid(v_center, true, 1)) { - std::string msg = "v_center is invalid.\n"; + std::string const msg = "v_center is invalid.\n"; #ifndef NDEBUG spdlog::trace(msg); #endif - return tl::make_unexpected(msg); + return std::unexpected(msg); } return t_manifold; @@ -325,9 +321,9 @@ namespace ergodic_moves #endif } // We've run out of (1,3) simplices to try - std::string msg = "No (2,6) move possible.\n"; + std::string const msg = "No (2,6) move possible.\n"; spdlog::warn(msg); - return tl::make_unexpected(msg); + return std::unexpected(msg); } // do_26_move() /// @brief Find a (6,2) move location @@ -337,10 +333,9 @@ namespace ergodic_moves /// and there should be no (2,2) simplices. /// @param manifold The simplicial manifold /// @param candidate The vertex to check - /// @return If (6,2) move is possible + /// @returns True if (6,2) move is possible [[nodiscard]] inline auto is_62_movable( - manifolds::Manifold3 const& manifold, - Vertex_handle_t<3> const& candidate) noexcept -> bool + Manifold const& manifold, Vertex_handle const& candidate) -> bool { if (manifold.dimensionality() != 3) { @@ -394,21 +389,24 @@ namespace ergodic_moves // Run until all vertices are fixed while (foliated_triangulations::fix_vertices<3>( - manifold.get_triangulation().get_delaunay(), manifold.initial_radius(), + manifold.get_delaunay(), manifold.initial_radius(), manifold.foliation_spacing())) { spdlog::warn("Fixing vertices found by is_62_movable().\n"); } - // Run until all cells fixed or 10 passes - for (auto passes = 1; passes < 11; ++passes) // NOLINT - { - if (foliated_triangulations::fix_cells<3>( - manifold.get_triangulation().get_delaunay())) - { - spdlog::warn("Fixing cells found by is_62_movable() pass {}.\n", - passes); - } - } + + // Run until all cells fixed or 50 passes + // for (auto passes = 1; passes < foliated_triangulations::MAX_FIX_PASSES + // + 1; + // ++passes) + // { + // if (!foliated_triangulations::fix_cells<3>(manifold.get_delaunay())) + // { + // break; + // } + // spdlog::warn("Fixing cells found by is_62_movable() pass {}.\n", + // passes); + // } auto const incident_31 = foliated_triangulations::filter_cells<3>( incident_cells, Cell_type::THREE_ONE); @@ -418,7 +416,7 @@ namespace ergodic_moves incident_cells, Cell_type::ONE_THREE); // All cells should be classified - if ((incident_13.size() + incident_22.size() + incident_31.size()) != + if (incident_13.size() + incident_22.size() + incident_31.size() != 6) // NOLINT { spdlog::warn("Some incident cells on this vertex need to be fixed.\n"); @@ -432,8 +430,8 @@ namespace ergodic_moves incident_13.size()); foliated_triangulations::debug_print_cells<3>(std::span{incident_cells}); #endif - return ((incident_31.size() == 3) && (incident_22.empty()) && - (incident_13.size() == 3)); + return incident_31.size() == 3 && incident_22.empty() && + incident_13.size() == 3; } // find_62_moves() @@ -454,334 +452,335 @@ namespace ergodic_moves /// change this, however.) /// /// @param t_manifold The simplicial manifold - /// @return The (6,2) moved manifold - [[nodiscard]] inline auto do_62_move( - manifolds::Manifold3& t_manifold) noexcept -> Expected + /// @returns The Expected (6,2) moved manifold or Unexpected + [[nodiscard]] inline auto do_62_move(Manifold& t_manifold) -> Expected { #ifndef NDEBUG spdlog::debug("{} called.\n", __PRETTY_FUNCTION__); #endif auto vertices = t_manifold.get_vertices(); // Shuffle the container to create a random sequence of vertices - std::shuffle(vertices.begin(), vertices.end(), - utilities::make_random_generator()); + std::ranges::shuffle(vertices, utilities::make_random_generator()); // Try a (6,2) move on successive vertices in the sequence - if (auto movable_vertex_iterator = - std::find_if(vertices.begin(), vertices.end(), - [&](auto const& vertex) { - return is_62_movable(t_manifold, vertex); - }); + if (auto const movable_vertex_iterator = + std::ranges::find_if(vertices, + [&](auto const& vertex) { + return is_62_movable(t_manifold, vertex); + }); movable_vertex_iterator != vertices.end()) { t_manifold.triangulation().delaunay().remove(*movable_vertex_iterator); return t_manifold; } // We've run out of vertices to try - std::string msg = "No (6,2) move possible.\n"; + std::string const msg = "No (6,2) move possible.\n"; spdlog::warn(msg); - return tl::make_unexpected(msg); + return std::unexpected(msg); } // do_62_move() - /// @brief Find a (4,4) move location - /// @details This function checks to see if a (4,4) move is possible. Starting - /// with a spacelike edge, it checks all incident cells. There must be 4 - /// incident cells; 2 should be (3,1) simplices, 2 should be (1,3) simplices, - /// and there should be no (2,2) simplices. - /// @param t_manifold The simplicial manifold - /// @param t_edge_candidate The edge to check - /// @return A container of incident cells if there are exactly 4 of them - [[nodiscard]] inline auto find_44_move( - manifolds::Manifold3 const& t_manifold, - Edge_handle_t<3> const& t_edge_candidate) noexcept - -> std::optional>> + /// @brief Find all cells incident to the edge + /// @param triangulation The Delaunay triangulation + /// @param edge The edge + /// @returns A container of cells incident to the edge or nullopt + /// @see + /// https://github.com/CGAL/cgal/blob/8430d04539179f25fb8e716f99e19d28589beeda/TDS_3/include/CGAL/Triangulation_data_structure_3.h#L2094 + [[nodiscard]] inline auto incident_cells_from_edge( + Delaunay_t<3> const& triangulation, + Edge_handle const& edge) -> std::optional { - if (!t_manifold.is_edge(t_edge_candidate)) { return std::nullopt; } - + if (!triangulation.tds().is_edge(edge.first, edge.second, edge.third)) + { + return std::nullopt; + } // Create the circulator of cells around the edge, starting with the cell // containing the edge - auto circulator = - t_manifold.incident_cells(t_edge_candidate, t_edge_candidate.first); - - std::vector> incident_cells; - do { - // filter out boundary edges with incident infinite cells - if (!t_manifold.get_triangulation().is_infinite(circulator)) - { - incident_cells.emplace_back(circulator); - } + auto circulator = triangulation.incident_cells(edge, edge.first); + Cell_container incident_cells; + // Add cells to the container until we get back to the first one in the + // circulator + do { // NOLINT(cppcoreguidelines-avoid-do-while) + // Ignore cells containing the infinite vertex + if (triangulation.is_infinite(circulator)) { continue; } + incident_cells.emplace_back(circulator); } - while (++circulator != t_edge_candidate.first); + while (++circulator != edge.first); #ifndef NDEBUG - spdlog::trace("Edge has {} incident cells.\n", incident_cells.size()); + spdlog::trace("Found {} incident cells on edge.\n", incident_cells.size()); #endif + return incident_cells; + } // incident_cells_from_edge() - if (incident_cells.size() == 4) { return incident_cells; } - + /// @brief Find a bistellar flip location + /// @details This function checks to see if a bistellar flip is possible. + /// Starting with an edge, it checks all incident cells. There must be 4 + /// incident cells; 2 should be (3,1) simplices, 2 should be (1,3) simplices, + /// and there should be no (2,2) simplices. + /// @param triangulation The simplicial manifold + /// @param t_edge_candidate The edge to check + /// @returns A container of incident cells if there are exactly 4 or nullopt + [[nodiscard]] inline auto find_bistellar_flip_location( + Delaunay const& triangulation, + Edge_handle const& t_edge_candidate) -> std::optional + { + if (auto incident_cells = + incident_cells_from_edge(triangulation, t_edge_candidate); + incident_cells.has_value() && incident_cells->size() == 4) + { + return incident_cells.value(); + } return std::nullopt; - - } // find_44_move() - - struct [[nodiscard("This contains data!")]] bistellar_flip_arguments + } // find_bistellar_flip_location() + + /// @brief Return a container of cells incident to an edge. + /// @param triangulation The triangulation with the cells. + /// @param edge The edge to find the incident cells of. + /// @returns A container of cells incident to the edge or nullopt + [[nodiscard]] inline auto get_incident_cells(Delaunay const& triangulation, + Edge_handle const edge) + -> std::optional { - using Delaunay_3 = Delaunay_t<3>; - using Cell_handle = Cell_handle_t<3>; - using Vertex_handle = Vertex_handle_t<3>; - - /// @brief The Delaunay triangulation in which to perform the flip - Delaunay_3 triangulation; - - /// @brief The first incident cell of the edge to flip - Cell_handle before_flip_cell_1; + // Check that the edge is valid + if (!triangulation.tds().is_edge(edge.first, edge.second, edge.third)) + { + return std::nullopt; + } - /// @brief The second incident cell of the edge to flip - Cell_handle before_flip_cell_2; + Cell_container incident_cells; + auto circulator = triangulation.incident_cells(edge, edge.first); + do { // NOLINT(cppcoreguidelines-avoid-do-while) + // filter out boundary edges with incident infinite cells + if (triangulation.is_infinite(circulator)) { continue; } + incident_cells.emplace_back(circulator); + } + while (++circulator != edge.first); + + return incident_cells; + } // get_incident_cells() + + /// @brief Perform a bistellar flip on triangulation via the given edge + /// @details Pass by value to avoid modifying the original triangulation + /// in the event that the flip is unsuccessful. + /// @param triangulation The triangulation to flip + /// @param edge The edge to pivot on + /// @param top Top vertex of the cells being flipped + /// @param bottom Bottom vertex of the cells being flipped + /// @returns A flipped triangulation or nullopt + [[nodiscard]] inline auto bistellar_flip( + Delaunay triangulation, Edge_handle edge, Vertex_handle top, + Vertex_handle bottom) -> std::optional + { + // Get the cells incident to the edge + auto incident_cells = get_incident_cells(triangulation, edge); - /// @brief The third incident cell of the edge to flip - Cell_handle before_flip_cell_3; + // Check that there are exactly 4 incident cells + if (!incident_cells || incident_cells->size() != 4) { return std::nullopt; } - /// @brief The last incident cell of the edge to flip - Cell_handle before_flip_cell_4; + // Check incident cells are valid + if (std::ranges::any_of(*incident_cells, + [](auto const& cell) { return !cell->is_valid(); })) + { + return std::nullopt; + } - /// @brief The first vertex of the edge to flip - Vertex_handle pivot_from_vertex_1; + // Get vertices from pivot edge + auto const& pivot_from_1 = edge.first->vertex(edge.second); + auto const& pivot_from_2 = edge.first->vertex(edge.third); - /// @brief The second vertex of the edge to flip - Vertex_handle pivot_from_vertex_2; + // Get vertices from cells + auto vertices = foliated_triangulations::get_vertices_from_cells<3>( + incident_cells.value()); - /// @brief The first vertex of the new edge - Vertex_handle pivot_to_vertex_1; + // Get vertices for new pivot edge + Vertex_container new_pivot_vertices; + std::ranges::copy_if(vertices, std::back_inserter(new_pivot_vertices), + [&](auto const& vertex) { + return vertex != pivot_from_1 && + vertex != pivot_from_2 && vertex != top && + vertex != bottom; + }); - /// @brief The second vertex of the new edge - Vertex_handle pivot_to_vertex_2; + // Check that there are exactly 2 new pivot vertices + if (new_pivot_vertices.size() != 2) { return std::nullopt; } - /// @brief A vertex unaffected by the flip - Vertex_handle top_vertex; + // Label the vertices in the new pivot edge + auto const& pivot_to_1 = new_pivot_vertices[0]; + auto const& pivot_to_2 = new_pivot_vertices[1]; - /// @brief A vertex unaffected by the flip - Vertex_handle bottom_vertex; - }; // struct bistellar_flip_arguments + // Now we need to classify the cells by the vertices they contain + Cell_handle before_1; // top, pivot_from_1, pivot_from_2, pivot_to_1 + Cell_handle before_2; // top, pivot_from_1, pivot_from_2, pivot_to_2 + Cell_handle before_3; // bottom, pivot_from_1, pivot_from_2, pivot_to_1 + Cell_handle before_4; // bottom, pivot_from_1, pivot_from_2, pivot_to_2 + for (auto const& cell : incident_cells.value()) + { + if (cell->has_vertex(top)) + { + if (cell->has_vertex(pivot_to_1)) { before_1 = cell; } + else { before_2 = cell; } + } + else + { + if (cell->has_vertex(pivot_to_1)) { before_3 = cell; } + else { before_4 = cell; } + } + } - /// @brief Perform a bistellar flip - /// @details This function performs a bistellar flip on a complex of - /// 4 cells sharing a common edge. The 6 vertices of the complex remain the - /// same, but the common edge is rotated from the pair of vertices denoted by - /// pivot_from_1 and pivot_from_2 to the pair of vertices denoted by - /// pivot_to_1 and pivot_to_2. The external neighbors of the complex should be - /// preserved. - /// Ideally this should be a function in CGAL::Triangulation_data_structure_3 - /// @image html 44.png - /// @param args A struct containing the arguments for the bistellar flip - /// @return A delaunay triangulation with the bistellar flip performed - /// @see bistellar.cpp - /// @see - /// https://doc.cgal.org/latest/TDS_3/classTriangulationDataStructure__3_1_1Cell.html#a1276d9e37a1460e81f88f4ae33295cb8 - /// @see - /// https://doc.cgal.org/latest/TDS_3/classTriangulationDataStructure__3.html#aec0d8528e29ce73226d66d44237cf8c7 - /// @see - /// https://doc.cgal.org/latest/TDS_3/classTriangulationDataStructure__3_1_1Cell.html#ace214d6e7a06de2976adbbc18c90a0d1 - /// @see - /// https://github.com/CGAL/cgal/blob/master/TDS_3/include/CGAL/Triangulation_data_structure_3.h#L639 - [[nodiscard]] inline auto bistellar_flip_really(bistellar_flip_arguments args) - -> std::optional> - { - // Parse input - auto triangulation = std::move(args.triangulation); - auto b_1 = args.before_flip_cell_1; - auto b_2 = args.before_flip_cell_2; - auto b_3 = args.before_flip_cell_3; - auto b_4 = args.before_flip_cell_4; - auto pivot_from_1 = args.pivot_from_vertex_1; - auto pivot_from_2 = args.pivot_from_vertex_2; - auto pivot_to_1 = args.pivot_to_vertex_1; - auto pivot_to_2 = args.pivot_to_vertex_2; - auto top = args.top_vertex; - auto bottom = args.bottom_vertex; - - // Check if the cells are valid - if (!b_1->is_valid() || !b_2->is_valid() || !b_3->is_valid() || - !b_4->is_valid()) + // Verify these cells are valid + if (!before_1->is_valid() || !before_2->is_valid() || + !before_3->is_valid() || !before_4->is_valid()) { return std::nullopt; } - // Now, find the exterior neighbors of the cells - // https://doc.cgal.org/latest/TDS_3/classTriangulationDataStructure__3_1_1Cell.html#a1276d9e37a1460e81f88f4ae33295cb8 - Cell_handle_t<3> n_1 = b_1->neighbor(b_1->index(pivot_from_2)); - Cell_handle_t<3> n_2 = b_1->neighbor(b_1->index(pivot_from_1)); - Cell_handle_t<3> n_3 = b_2->neighbor(b_2->index(pivot_from_1)); - Cell_handle_t<3> n_4 = b_2->neighbor(b_2->index(pivot_from_2)); - Cell_handle_t<3> n_5 = b_3->neighbor(b_3->index(pivot_from_2)); - Cell_handle_t<3> n_6 = b_3->neighbor(b_3->index(pivot_from_1)); - Cell_handle_t<3> n_7 = b_4->neighbor(b_4->index(pivot_from_1)); - Cell_handle_t<3> n_8 = b_4->neighbor(b_4->index(pivot_from_2)); + +#ifndef NDEBUG + fmt::print("Cells in the triangulation before deleting old cells: {}\n", + triangulation.number_of_cells()); +#endif + + // Now find the exterior neighbors of the cells + Cell_handle const n_1 = before_1->neighbor(before_1->index(pivot_from_2)); + Cell_handle const n_2 = before_1->neighbor(before_1->index(pivot_from_1)); + Cell_handle const n_3 = before_2->neighbor(before_2->index(pivot_from_1)); + Cell_handle const n_4 = before_2->neighbor(before_2->index(pivot_from_2)); + Cell_handle const n_5 = before_3->neighbor(before_3->index(pivot_from_2)); + Cell_handle const n_6 = before_3->neighbor(before_3->index(pivot_from_1)); + Cell_handle const n_7 = before_4->neighbor(before_4->index(pivot_from_1)); + Cell_handle const n_8 = before_4->neighbor(before_4->index(pivot_from_2)); // Next, delete the old cells - triangulation.tds().delete_cell(b_1); - triangulation.tds().delete_cell(b_2); - triangulation.tds().delete_cell(b_3); - triangulation.tds().delete_cell(b_4); + triangulation.tds().delete_cell(before_1); + triangulation.tds().delete_cell(before_2); + triangulation.tds().delete_cell(before_3); + triangulation.tds().delete_cell(before_4); + +#ifndef NDEBUG + fmt::print("Cells in the triangulation after deleting old cells: {}\n", + triangulation.number_of_cells()); +#endif // Now create the new cells - // https://doc.cgal.org/latest/TDS_3/classTriangulationDataStructure__3.html#aec0d8528e29ce73226d66d44237cf8c7 - Cell_handle_t<3> a_1 = triangulation.tds().create_cell( + Cell_handle const after_1 = triangulation.tds().create_cell( top, pivot_from_1, pivot_to_1, pivot_to_2); - Cell_handle_t<3> a_2 = triangulation.tds().create_cell( + Cell_handle const after_2 = triangulation.tds().create_cell( top, pivot_from_2, pivot_to_1, pivot_to_2); - Cell_handle_t<3> a_3 = triangulation.tds().create_cell( + Cell_handle const after_3 = triangulation.tds().create_cell( bottom, pivot_from_1, pivot_to_1, pivot_to_2); - Cell_handle_t<3> a_4 = triangulation.tds().create_cell( + Cell_handle const after_4 = triangulation.tds().create_cell( bottom, pivot_from_2, pivot_to_1, pivot_to_2); - // Now, set the neighbors - // https://doc.cgal.org/latest/TDS_3/classTriangulationDataStructure__3_1_1Cell.html#ace214d6e7a06de2976adbbc18c90a0d1 - a_1->set_neighbors(n_1, n_4, a_2, a_3); - a_2->set_neighbors(n_2, n_3, a_1, a_4); - a_3->set_neighbors(n_5, n_8, a_4, a_1); - a_4->set_neighbors(n_6, n_7, a_2, a_3); - - // Fix any cell orientation issues - // If this function becomes a part of Triangulation_data_structure_3, - // we can call change_orientation on just the effected cells instead - // https://github.com/CGAL/cgal/blob/master/TDS_3/include/CGAL/Triangulation_data_structure_3.h#L639 - if (!triangulation.is_valid()) { triangulation.tds().reorient(); } - - // Check validity of cells - if (a_1->is_valid() && a_2->is_valid() && a_3->is_valid() && - a_4->is_valid()) - { - return std::make_optional(triangulation); - } - - // Invalid result - return std::nullopt; - } // bistellar_flip_really + // Now set the neighbors of the new cells + after_1->set_neighbors(n_1, n_4, after_2, after_3); + after_2->set_neighbors(n_2, n_3, after_1, after_4); + after_3->set_neighbors(n_5, n_8, after_4, after_1); + after_4->set_neighbors(n_6, n_7, after_2, after_3); + + // Now set the neighboring cells to the new cells + n_1->set_neighbor(n_1->index(triangulation.tds().mirror_vertex( + after_1, after_1->index(pivot_to_2))), + after_1); + n_2->set_neighbor(n_2->index(triangulation.tds().mirror_vertex( + after_2, after_2->index(pivot_to_2))), + after_2); + n_3->set_neighbor(n_3->index(triangulation.tds().mirror_vertex( + after_2, after_2->index(pivot_to_1))), + after_2); + n_4->set_neighbor(n_4->index(triangulation.tds().mirror_vertex( + after_1, after_1->index(pivot_to_1))), + after_1); + n_5->set_neighbor(n_5->index(triangulation.tds().mirror_vertex( + after_3, after_3->index(pivot_to_2))), + after_3); + n_6->set_neighbor(n_6->index(triangulation.tds().mirror_vertex( + after_4, after_4->index(pivot_to_2))), + after_4); + n_7->set_neighbor(n_7->index(triangulation.tds().mirror_vertex( + after_4, after_4->index(pivot_to_1))), + after_4); + n_8->set_neighbor(n_8->index(triangulation.tds().mirror_vertex( + after_3, after_3->index(pivot_to_1))), + after_3); + + // Alternative way to set the neighbors + // auto mirror_index = triangulation.tds().mirror_index(before_1, + // before_1->index(pivot_from_2)); n_1->set_neighbor(mirror_index, + // after_1); n_1->set_neighbor(triangulation.tds().mirror_index(before_1, + // before_1->index(pivot_from_2)), after_1); + // n_2->set_neighbor(triangulation.tds().mirror_index(before_1, + // before_1->index(pivot_from_1)), after_2); + // n_3->set_neighbor(triangulation.tds().mirror_index(before_2, + // before_2->index(pivot_from_1)), after_2); + // n_4->set_neighbor(triangulation.tds().mirror_index(before_2, + // before_2->index(pivot_from_2)), after_1); + // n_5->set_neighbor(triangulation.tds().mirror_index(before_3, + // before_3->index(pivot_from_2)), after_3); + // n_6->set_neighbor(triangulation.tds().mirror_index(before_3, + // before_3->index(pivot_from_1)), after_4); + // n_7->set_neighbor(triangulation.tds().mirror_index(before_4, + // before_4->index(pivot_from_1)), after_4); + // n_8->set_neighbor(triangulation.tds().mirror_index(before_4, + // before_4->index(pivot_from_2)), after_3); + + // Okay, we'll need to test each before and after cell to see if we + // get the n_x's right - /// @brief Perform bistellar flip - /// @details This function performs a 3D bistellar flip on 4 cells with - /// a common edge. - /// @param t_edge The common edge among the 4 simplices to flip - /// @param t_cells The 4 cells common to the edge - /// @param t_manifold The simplicial manifold - /// @return A manifold with the flip applied if successful - /// @see https://dl.acm.org/doi/10.1145/777792.777821 - /// @see - /// https://github.com/CGAL/cgal/blob/master/TDS_3/include/CGAL/Triangulation_data_structure_3.h - /// @see - /// https://doc.cgal.org/latest/TDS_3/classTriangulationDataStructure__3.html#a646b6bd66cd85422f294e60068629d3a - /// @see - /// https://doc.cgal.org/latest/TDS_3/classTriangulationDataStructure__3.html#aee7bebae22e4fe9094b744d8ea54d28b - [[nodiscard]] inline auto bistellar_flip( - Edge_handle_t<3> const& t_edge, - std::vector> const& t_cells, - manifolds::Manifold3 const& t_manifold) - -> std::optional - { #ifndef NDEBUG - fmt::print("Attempting (4,4) move ...\n"); - fmt::print("Pivot edge: \n"); - foliated_triangulations::print_edge<3>(t_edge); + spdlog::info("Cells in the triangulation after adding new cells: {}\n", + triangulation.number_of_cells()); #endif - // Get vertices from pivot edge - auto const& pivot_from_vertex_1 = t_edge.first->vertex(t_edge.second); - auto const& pivot_from_vertex_2 = t_edge.first->vertex(t_edge.third); + // Fix any cell orientation issues + if (!triangulation.is_valid()) { triangulation.tds().reorient(); } - // Get vertices from cells - auto all_vertices = - foliated_triangulations::get_vertices_from_cells<3>(t_cells); - // Make sure they're correct - // Run until all vertices are fixed - while (foliated_triangulations::fix_vertices<3>( - t_cells, t_manifold.initial_radius(), t_manifold.foliation_spacing())) - { - spdlog::warn("Fixing vertices in bistellar_flip.\n"); - } - // Run until all cells fixed or 10 passes - for (auto passes = 1; passes < 11; ++passes) // NOLINT +#ifndef NDEBUG + if (!triangulation.tds().is_valid(true, 1)) { - if (foliated_triangulations::fix_cells<3>( - t_manifold.get_triangulation().get_delaunay())) - { - spdlog::warn("Fixing cells in bistellar_flip pass {}.\n", passes); - } + spdlog::warn("Triangulation is not valid.\n"); } +#endif - // Get vertices for new pivot edge - std::vector> new_pivot_vertices; - std::copy_if(all_vertices.begin(), all_vertices.end(), - std::back_inserter(new_pivot_vertices), - [&](auto const& vertex) { - return (vertex->info() == pivot_from_vertex_1->info() && - vertex != pivot_from_vertex_1 && - vertex != pivot_from_vertex_2); - }); - if (new_pivot_vertices.size() != 2) + // Check validity of cells + if (after_1->is_valid() && after_2->is_valid() && after_3->is_valid() && + after_4->is_valid()) { - spdlog::warn("Could not find new pivot vertices.\n"); - return std::nullopt; + return std::make_optional(triangulation); } + return std::nullopt; + } // bistellar_flip() - // Label the vertices in the new pivot edge - auto pivot_to_vertex_1 = new_pivot_vertices[0]; - auto pivot_to_vertex_2 = new_pivot_vertices[1]; - - // Find the vertex at top - auto const& top_vertex = *std::find_if( - all_vertices.begin(), all_vertices.end(), [&](auto const& vertex) { - return vertex->info() > pivot_from_vertex_1->info(); - }); - // Find the vertex at bottom - auto const& bottom_vertex = *std::find_if( - all_vertices.begin(), all_vertices.end(), [&](auto const& vertex) { - return vertex->info() < pivot_from_vertex_2->info(); - }); - - // Now we need to classify the cells by the vertices they contain - Cell_handle_t<3> before_1; - Cell_handle_t<3> before_2; - Cell_handle_t<3> before_3; - Cell_handle_t<3> before_4; - for (auto const& cell : t_cells) + /// @return The center edge of a 4-cell complex + [[nodiscard]] inline auto find_pivot_edge(Delaunay const& triangulation, + Edge_container const& edges) + -> std::optional + { + for (auto const& edge : edges) { - if (cell->has_vertex(top_vertex)) - { - if (cell->has_vertex(pivot_to_vertex_1)) { before_1 = cell; } - else { before_2 = cell; } - } - else - { - if (cell->has_vertex(pivot_to_vertex_1)) { before_3 = cell; } - else { before_4 = cell; } + auto circulator = triangulation.incident_cells(edge, edge.first); + Cell_container incident_cells; + do { // NOLINT(cppcoreguidelines-avoid-do-while) + // filter out boundary edges with incident infinite cells + if (triangulation.is_infinite(circulator)) { continue; } + incident_cells.emplace_back(circulator); } - } - - auto delaunay_triangulation = t_manifold.get_triangulation().get_delaunay(); - - // Now really flip the cells - bistellar_flip_arguments arguments{ - .triangulation = delaunay_triangulation, - .before_flip_cell_1 = before_1, - .before_flip_cell_2 = before_2, - .before_flip_cell_3 = before_3, - .before_flip_cell_4 = before_4, - .pivot_from_vertex_1 = pivot_from_vertex_1, - .pivot_from_vertex_2 = pivot_from_vertex_2, - .pivot_to_vertex_1 = pivot_to_vertex_1, - .pivot_to_vertex_2 = pivot_to_vertex_2, - .top_vertex = top_vertex, - .bottom_vertex = bottom_vertex}; - - // Currently, invalidates the TriangulationDataStructure_3 - if (auto result = bistellar_flip_really(arguments); result) - { - auto foliated_triangulation = - foliated_triangulations::FoliatedTriangulation3{ - result.value(), t_manifold.initial_radius(), - t_manifold.foliation_spacing()}; - auto manifold = manifolds::Manifold3{foliated_triangulation}; - return manifold; + while (++circulator != edge.first); + fmt::print("Edge has {} incident finite cells\n", incident_cells.size()); + if (incident_cells.size() == 4) { return edge; } } return std::nullopt; - } // bistellar_flip + } // find_pivot_edge() + + /// @brief Return a container of all vertices in a container of cells. + /// @param cells The cells to find the vertices of. + /// @return A container of vertices in the cells + [[nodiscard]] inline auto get_vertices(Cell_container const& cells) + { + std::unordered_set vertices; + auto get_vertices = [&vertices](auto const& cell) { + for (int i = 0; i < 4; ++i) { vertices.emplace(cell->vertex(i)); } + }; + std::ranges::for_each(cells, get_vertices); + Vertex_container result(vertices.begin(), vertices.end()); + return result; + } // get_vertices() /// @brief Perform a (4,4) move /// @details This is a bistellar flip pivoting the internal spacelike edge @@ -801,22 +800,22 @@ namespace ergodic_moves /// change this, however.) /// /// @param t_manifold The simplicial manifold - /// @return The (4,4) moved manifold + /// @return The Expected (4,4) moved manifold or Unexpected /// @todo Need to debug bistellar_flip_really() - [[nodiscard]] inline auto do_44_move(manifolds::Manifold3 const& t_manifold) - -> Expected + [[nodiscard]] inline auto do_44_move(Manifold const& t_manifold) -> Expected { #ifndef NDEBUG spdlog::debug("{} called.\n", __PRETTY_FUNCTION__); #endif auto spacelike_edges = t_manifold.get_spacelike_edges(); // Shuffle the container to pick a random sequence of edges to try - std::shuffle(spacelike_edges.begin(), spacelike_edges.end(), - utilities::make_random_generator()); + std::ranges::shuffle(spacelike_edges, utilities::make_random_generator()); for (auto const& edge : spacelike_edges) { // Obtain all incident cells - if (auto incident_cells = find_44_move(t_manifold, edge); incident_cells) + if (auto const incident_cells = find_bistellar_flip_location( + t_manifold.get_triangulation().get_delaunay(), edge); + incident_cells) { #ifndef NDEBUG for (auto const& cell : *incident_cells) @@ -824,15 +823,6 @@ namespace ergodic_moves spdlog::trace("Incident cell is of type {}.\n", cell->info()); } #endif - // Debug this - // if (auto result = bistellar_flip(edge, *incident_cells, - // t_manifold); - // result) - // { - // fmt::print("After (4,4) move:\n"); - // result.value().print_cells(); - // return result.value(); - // } // For now, just return the manifold - essentially a null move return t_manifold; @@ -840,9 +830,9 @@ namespace ergodic_moves // Try next edge } // We've run out of edges to try - std::string msg = "No (4,4) move possible.\n"; + std::string const msg = "No (4,4) move possible.\n"; spdlog::warn(msg); - return tl::make_unexpected(msg); + return std::unexpected(msg); } // do_44_move() /// @brief Check move correctness @@ -850,73 +840,71 @@ namespace ergodic_moves /// @param t_after The manifold after the move /// @param t_move The type of move /// @return True if the move correctly changed the triangulation - [[nodiscard]] inline auto check_move(manifolds::Manifold3 const& t_before, - manifolds::Manifold3 const& t_after, - move_tracker::move_type const& t_move) - -> bool + [[nodiscard]] inline auto check_move( + Manifold const& t_before, Manifold const& t_after, + move_tracker::move_type const& t_move) -> bool { switch (t_move) { case move_tracker::move_type::FOUR_FOUR: - return (t_after.is_valid() && t_after.N3() == t_before.N3() && - t_after.N3_31() == t_before.N3_31() && - t_after.N3_22() == t_before.N3_22() && - t_after.N3_13() == t_before.N3_13() && - t_after.N2() == t_before.N2() && - t_after.N1() == t_before.N1() && - t_after.N1_TL() == t_before.N1_TL() && - t_after.N1_SL() == t_before.N1_SL() && - t_after.N0() == t_before.N0() && - t_after.max_time() == t_before.max_time() && - t_after.min_time() == t_before.min_time()); + return t_after.is_valid() && t_after.N3() == t_before.N3() && + t_after.N3_31() == t_before.N3_31() && + t_after.N3_22() == t_before.N3_22() && + t_after.N3_13() == t_before.N3_13() && + t_after.N2() == t_before.N2() && t_after.N1() == t_before.N1() && + t_after.N1_TL() == t_before.N1_TL() && + t_after.N1_SL() == t_before.N1_SL() && + t_after.N0() == t_before.N0() && + t_after.max_time() == t_before.max_time() && + t_after.min_time() == t_before.min_time(); case move_tracker::move_type::TWO_THREE: - return (t_after.is_valid() && t_after.N3() == t_before.N3() + 1 && - t_after.N3_31() == t_before.N3_31() && - t_after.N3_22() == t_before.N3_22() + 1 && - t_after.N3_13() == t_before.N3_13() && - t_after.N2() == t_before.N2() + 2 && - t_after.N1() == t_before.N1() + 1 && - t_after.N1_TL() == t_before.N1_TL() + 1 && - t_after.N1_SL() == t_before.N1_SL() && - t_after.N0() == t_before.N0() && - t_after.max_time() == t_before.max_time() && - t_after.min_time() == t_before.min_time()); + return t_after.is_valid() && t_after.N3() == t_before.N3() + 1 && + t_after.N3_31() == t_before.N3_31() && + t_after.N3_22() == t_before.N3_22() + 1 && + t_after.N3_13() == t_before.N3_13() && + t_after.N2() == t_before.N2() + 2 && + t_after.N1() == t_before.N1() + 1 && + t_after.N1_TL() == t_before.N1_TL() + 1 && + t_after.N1_SL() == t_before.N1_SL() && + t_after.N0() == t_before.N0() && + t_after.max_time() == t_before.max_time() && + t_after.min_time() == t_before.min_time(); case move_tracker::move_type::THREE_TWO: - return (t_after.is_valid() && t_after.N3() == t_before.N3() - 1 && - t_after.N3_31() == t_before.N3_31() && - t_after.N3_22() == t_before.N3_22() - 1 && - t_after.N3_13() == t_before.N3_13() && - t_after.N2() == t_before.N2() - 2 && - t_after.N1() == t_before.N1() - 1 && - t_after.N1_TL() == t_before.N1_TL() - 1 && - t_after.N1_SL() == t_before.N1_SL() && - t_after.N0() == t_before.N0() && - t_after.max_time() == t_before.max_time() && - t_after.min_time() == t_before.min_time()); + return t_after.is_valid() && t_after.N3() == t_before.N3() - 1 && + t_after.N3_31() == t_before.N3_31() && + t_after.N3_22() == t_before.N3_22() - 1 && + t_after.N3_13() == t_before.N3_13() && + t_after.N2() == t_before.N2() - 2 && + t_after.N1() == t_before.N1() - 1 && + t_after.N1_TL() == t_before.N1_TL() - 1 && + t_after.N1_SL() == t_before.N1_SL() && + t_after.N0() == t_before.N0() && + t_after.max_time() == t_before.max_time() && + t_after.min_time() == t_before.min_time(); case move_tracker::move_type::TWO_SIX: - return (t_after.is_valid() && t_after.N3() == t_before.N3() + 4 && - t_after.N3_31() == t_before.N3_31() + 2 && - t_after.N3_22() == t_before.N3_22() && - t_after.N3_13() == t_before.N3_13() + 2 && - t_after.N2() == t_before.N2() + 8 && // NOLINT - t_after.N1() == t_before.N1() + 5 && // NOLINT - t_after.N1_TL() == t_before.N1_TL() + 2 && - t_after.N1_SL() == t_before.N1_SL() + 3 && - t_after.N0() == t_before.N0() + 1 && - t_after.max_time() == t_before.max_time() && - t_after.min_time() == t_before.min_time()); + return t_after.is_valid() && t_after.N3() == t_before.N3() + 4 && + t_after.N3_31() == t_before.N3_31() + 2 && + t_after.N3_22() == t_before.N3_22() && + t_after.N3_13() == t_before.N3_13() + 2 && + t_after.N2() == t_before.N2() + 8 && // NOLINT + t_after.N1() == t_before.N1() + 5 && // NOLINT + t_after.N1_TL() == t_before.N1_TL() + 2 && + t_after.N1_SL() == t_before.N1_SL() + 3 && + t_after.N0() == t_before.N0() + 1 && + t_after.max_time() == t_before.max_time() && + t_after.min_time() == t_before.min_time(); case move_tracker::move_type::SIX_TWO: - return (t_after.is_valid() && t_after.N3() == t_before.N3() - 4 && - t_after.N3_31() == t_before.N3_31() - 2 && - t_after.N3_22() == t_before.N3_22() && - t_after.N3_13() == t_before.N3_13() - 2 && - t_after.N2() == t_before.N2() - 8 && // NOLINT - t_after.N1() == t_before.N1() - 5 && // NOLINT - t_after.N1_TL() == t_before.N1_TL() - 2 && - t_after.N1_SL() == t_before.N1_SL() - 3 && - t_after.N0() == t_before.N0() - 1 && - t_after.max_time() == t_before.max_time() && - t_after.min_time() == t_before.min_time()); + return t_after.is_valid() && t_after.N3() == t_before.N3() - 4 && + t_after.N3_31() == t_before.N3_31() - 2 && + t_after.N3_22() == t_before.N3_22() && + t_after.N3_13() == t_before.N3_13() - 2 && + t_after.N2() == t_before.N2() - 8 && // NOLINT + t_after.N1() == t_before.N1() - 5 && // NOLINT + t_after.N1_TL() == t_before.N1_TL() - 2 && + t_after.N1_SL() == t_before.N1_SL() - 3 && + t_after.N0() == t_before.N0() - 1 && + t_after.max_time() == t_before.max_time() && + t_after.min_time() == t_before.min_time(); default: return false; } } // check_move() diff --git a/include/Foliated_triangulation.hpp b/include/Foliated_triangulation.hpp index 62f93e7b00..4b5e23849b 100644 --- a/include/Foliated_triangulation.hpp +++ b/include/Foliated_triangulation.hpp @@ -21,8 +21,6 @@ #ifndef CDT_PLUSPLUS_FOLIATEDTRIANGULATION_HPP #define CDT_PLUSPLUS_FOLIATEDTRIANGULATION_HPP -#include - #include "Triangulation_traits.hpp" #include "Utilities.hpp" @@ -56,15 +54,11 @@ using Spherical_points_generator_t = typename TriangulationTraits::Spherical_points_generator; /// @concept ContainerType -/// @brief This is equivalent to std::movable from +/// @brief This is std::movable from /// @details Right now the real restriction on Containers is that elements must -/// be swappable in order for std::shuffle to work. However, std::movable -/// doesn't seem to be in yet. -/// @see https://en.cppreference.com/w/cpp/concept/Movable +/// be swappable in order for std::shuffle to work. template -concept ContainerType = - std::is_object_v && std::is_move_constructible_v && - std::is_assignable_v && std::is_swappable_v; +concept ContainerType = std::movable; /// (n,m) is number of vertices on (lower, higher) timeslice enum class Cell_type @@ -84,14 +78,42 @@ enum class Cell_type namespace foliated_triangulations { + static int constexpr MAX_FIX_PASSES = 50; + + /// @brief Create causal vertices from vertices and timevalues + /// @tparam dimension Dimensionality of the manifold + /// @param vertices The vertices of the manifold + /// @param timevalues The timevalue of each vertex + /// @return A container of vertices that have an associated timevalue + template + auto make_causal_vertices(std::span const> vertices, + std::span timevalues) + -> Causal_vertices_t + { + if (vertices.size() != timevalues.size()) + { + throw std::length_error("Vertices and timevalues must be the same size."); + } + Causal_vertices_t causal_vertices; + causal_vertices.reserve(vertices.size()); + std::ranges::transform(vertices, timevalues, + std::back_inserter(causal_vertices), + [](Point_t point, size_t time) { + return std::make_pair(point, time); + }); + return causal_vertices; + } + /// @brief Returns a container of all the finite edges in the triangulation + /// @details Regardless of the dimensionality of the triangulation, the edges + /// are 1-d simplices connecting 0-d vertices. /// @tparam dimension The dimensionality of the triangulation /// @param delaunay The triangulation - /// @return Container of all the finite edges in the triangulation + /// @returns Container of all the finite edges in the triangulation template [[nodiscard]] auto collect_edges(Delaunay_t const& delaunay) { - Expects(delaunay.is_valid()); + assert(delaunay.is_valid()); std::vector> init_edges; init_edges.reserve(delaunay.number_of_finite_edges()); for (auto eit = delaunay.finite_edges_begin(); @@ -101,11 +123,11 @@ namespace foliated_triangulations Edge_handle_t<3> thisEdge{cell, cell->index(cell->vertex(eit->second)), cell->index(cell->vertex(eit->third))}; // Each edge is valid in the triangulation - Ensures(delaunay.tds().is_valid(thisEdge.first, thisEdge.second, - thisEdge.third)); + assert(delaunay.tds().is_valid(thisEdge.first, thisEdge.second, + thisEdge.third)); init_edges.emplace_back(thisEdge); } - Ensures(init_edges.size() == delaunay.number_of_finite_edges()); + assert(init_edges.size() == delaunay.number_of_finite_edges()); return init_edges; } // collect_edges @@ -113,7 +135,7 @@ namespace foliated_triangulations /// @tparam dimension The dimensionality of the triangulation /// @param delaunay The triangulation /// @param point The point to find the vertex for - /// @return The vertex containing the given point + /// @returns The vertex containing the given point /// @see /// https://doc.cgal.org/latest/Triangulation_3/classCGAL_1_1Triangulation__3.html#a5b45572c663e5d2c10f26e7be421e140 template @@ -136,7 +158,7 @@ namespace foliated_triangulations /// @param vh2 The second vertex /// @param vh3 The third vertex /// @param vh4 The fourth vertex - /// @return The cell containing the vertices + /// @returns The cell containing the vertices /// @see /// https://doc.cgal.org/latest/Triangulation_3/classCGAL_1_1Triangulation__3.html#a8766c9a0c2a84203be31537e5e015646 template @@ -156,7 +178,7 @@ namespace foliated_triangulations } // find_cell /// @tparam dimension The dimensionality of the simplices - /// @return True if timevalue of lhs is less than rhs + /// @returns True if timevalue of lhs is less than rhs template auto constexpr compare_v_info = [](Vertex_handle_t const& lhs, Vertex_handle_t const& rhs) { @@ -165,34 +187,34 @@ namespace foliated_triangulations /// @tparam dimension The dimensionality of the simplices /// @param t_vertices The container of vertices - /// @return The maximum timevalue in the container + /// @returns The maximum timevalue in the container template [[nodiscard]] auto find_max_timevalue(Container&& t_vertices) -> Int_precision { auto vertices = std::forward(t_vertices); - Expects(!vertices.empty()); + assert(!vertices.empty()); auto max_element = std::max_element(vertices.begin(), vertices.end(), compare_v_info); auto result_index = std::distance(vertices.begin(), max_element); // std::distance may be negative if random-access iterators are used and // first is reachable from last - Ensures(result_index >= 0); + assert(result_index >= 0); auto const index = static_cast(std::abs(result_index)); return vertices[index]->info(); } // find_max_timevalue /// @tparam dimension The dimensionality of the simplices /// @param t_vertices The container of vertices - /// @return The minimum timevalue in the container + /// @returns The minimum timevalue in the container template [[nodiscard]] auto find_min_timevalue(Container&& t_vertices) -> Int_precision { auto vertices = std::forward(t_vertices); - Expects(!vertices.empty()); + assert(!vertices.empty()); auto min_element = std::min_element(vertices.begin(), vertices.end(), compare_v_info); auto result_index = std::distance(vertices.begin(), min_element); - Ensures(result_index >= 0); + assert(result_index >= 0); auto const index = static_cast(std::abs(result_index)); return vertices[index]->info(); } // find_min_timevalue @@ -200,8 +222,7 @@ namespace foliated_triangulations /// @brief Predicate to classify edge as timelike or spacelike /// @tparam dimension The dimensionality of the simplices /// @param t_edge The Edge_handle to classify - /// @param t_debug_flag Debugging info toggle - /// @return True if timelike and false if spacelike + /// @returns True if timelike and false if spacelike template [[nodiscard]] auto classify_edge(Edge_handle_t const& t_edge) -> bool @@ -224,7 +245,7 @@ namespace foliated_triangulations /// @tparam dimension The dimensionality of the simplices /// @param t_edges The container of edges to filter /// @param t_is_Timelike_pred The predicate to filter by - /// @return A container of is_Timelike edges + /// @returns A container of is_Timelike edges template [[nodiscard]] auto filter_edges( std::vector> const& t_edges, @@ -233,18 +254,17 @@ namespace foliated_triangulations std::vector> filtered_edges; // Short-circuit if no edges if (t_edges.empty()) { return filtered_edges; } - std::copy_if( - t_edges.begin(), t_edges.end(), std::back_inserter(filtered_edges), - [&](auto const& edge) { - return (t_is_Timelike_pred == classify_edge(edge)); - }); + std::copy_if(t_edges.begin(), t_edges.end(), + std::back_inserter(filtered_edges), [&](auto const& edge) { + return t_is_Timelike_pred == classify_edge(edge); + }); return filtered_edges; } // filter_edges /// @tparam dimension The dimensionality of the simplices /// @param t_cells The container of simplices /// @param t_cell_type The type of simplex to filter by - /// @return A container of simplices filtered by type + /// @returns A container of simplices filtered by type template [[nodiscard]] auto filter_cells( std::vector> const& t_cells, @@ -264,16 +284,13 @@ namespace foliated_triangulations /// @brief Calculate the squared radius from the origin /// @tparam dimension The dimensionality of the simplices /// @param t_vertex The vertex to check - /// @return The squared radial distance of the vertex from the origin + /// @returns The squared radial distance of the vertex from the origin template [[nodiscard]] auto squared_radius(Vertex_handle_t const& t_vertex) -> double { - typename TriangulationTraits::squared_distance r_2; - - if (dimension == 3) { return r_2(t_vertex->point(), Point_t<3>(0, 0, 0)); } - - return 0; + typename TriangulationTraits::squared_distance const r_2; + return r_2(t_vertex->point(), TriangulationTraits::ORIGIN_POINT); } // squared_radius /// @brief Find the expected timevalue for a vertex @@ -287,7 +304,7 @@ namespace foliated_triangulations /// @param t_vertex The vertex /// @param t_initial_radius The initial radius of the radial foliation /// @param t_foliation_spacing The spacing between successive leaves - /// @return The effective radius of the vertex + /// @returns The effective radius of the vertex template [[nodiscard]] auto expected_timevalue( Vertex_handle_t const& t_vertex, double t_initial_radius, @@ -304,17 +321,18 @@ namespace foliated_triangulations /// @param t_vertex The vertex /// @param t_initial_radius The initial radius of the radial foliation /// @param t_foliation_spacing The spacing between successive leaves - /// @return True if the timevalue of the vertex matches its effective radius + /// @returns True if the timevalue of the vertex matches its effective radius template [[nodiscard]] auto is_vertex_timevalue_correct( - Vertex_handle_t const& t_vertex, double t_initial_radius, - double t_foliation_spacing) -> bool + Vertex_handle_t const& t_vertex, double const t_initial_radius, + double const t_foliation_spacing) -> bool { auto const timevalue = expected_timevalue( t_vertex, t_initial_radius, t_foliation_spacing); #ifndef NDEBUG spdlog::trace("Vertex({}) timevalue {} has expected timevalue == {}\n", - t_vertex->point(), t_vertex->info(), timevalue); + utilities::point_to_str(t_vertex->point()), t_vertex->info(), + timevalue); #endif return timevalue == t_vertex->info(); } // is_vertex_timevalue_correct @@ -322,9 +340,9 @@ namespace foliated_triangulations /// @brief Obtain all finite vertices in the Delaunay triangulation /// @tparam dimension Dimensionality of the Delaunay triangulation /// @param t_triangulation The Delaunay triangulation - /// @return A container of finite vertices + /// @returns A container of finite vertices template - [[nodiscard]] auto get_all_finite_vertices( + [[nodiscard]] auto collect_vertices( Delaunay_t const& t_triangulation) { std::vector> vertices; @@ -332,11 +350,11 @@ namespace foliated_triangulations vit != t_triangulation.finite_vertices_end(); ++vit) { // Each vertex is valid - Ensures(t_triangulation.tds().is_vertex(vit)); + assert(t_triangulation.tds().is_vertex(vit)); vertices.emplace_back(vit); } return vertices; - } // get_all_finite_vertices + } // collect_vertices /// @brief Check if vertices have the correct timevalues /// @tparam dimension Dimensionality of the vertices and Delaunay @@ -344,13 +362,13 @@ namespace foliated_triangulations /// @param t_triangulation The Delaunay triangulation /// @param t_initial_radius The initial radius of the radial foliation /// @param t_foliation_spacing The spacing between successive leaves - /// @return True if all vertices have correct timevalues + /// @returns True if all vertices have correct timevalues template [[nodiscard]] auto check_vertices( Delaunay_t const& t_triangulation, double t_initial_radius, double t_foliation_spacing) { - auto checked_vertices = get_all_finite_vertices(t_triangulation); + auto checked_vertices = collect_vertices(t_triangulation); return std::all_of(checked_vertices.begin(), checked_vertices.end(), [&](auto const& vertex) { return is_vertex_timevalue_correct( @@ -361,10 +379,9 @@ namespace foliated_triangulations /// @brief Obtain all finite cells in the Delaunay triangulation /// @tparam dimension Dimensionality of the Delaunay triangulation /// @param t_triangulation The triangulation - /// @return A container of finite vertices + /// @returns A container of finite cells template - [[nodiscard]] auto get_all_finite_cells( - Delaunay_t const& t_triangulation) + [[nodiscard]] auto collect_cells(Delaunay_t const& t_triangulation) -> std::vector> { std::vector> cells; @@ -372,17 +389,17 @@ namespace foliated_triangulations cit != t_triangulation.finite_cells_end(); ++cit) { // Each cell is valid - Ensures(t_triangulation.tds().is_cell(cit)); + assert(t_triangulation.tds().is_cell(cit)); cells.emplace_back(cit); } return cells; - } // get_all_finite_cells + } // collect_cells /// @brief Extracts vertices from cells /// @param t_cells The cells from which to extract vertices - /// @return All of the vertices contained in the cells + /// @returns All of the vertices contained in the cells template - [[nodiscard]] inline auto get_vertices_from_cells( + [[nodiscard]] auto get_vertices_from_cells( std::vector> const& t_cells) { std::unordered_set> cell_vertices; @@ -433,7 +450,7 @@ namespace foliated_triangulations Delaunay_t const& t_triangulation, double t_initial_radius, double t_foliation_spacing) { - auto cells_to_check = get_all_finite_cells(t_triangulation); + auto cells_to_check = collect_cells(t_triangulation); return find_incorrect_vertices(cells_to_check, t_initial_radius, t_foliation_spacing); } // find_incorrect_vertices @@ -466,21 +483,21 @@ namespace foliated_triangulations /// @tparam dimension Dimensionality of the vertices and Delaunay /// triangulation /// @param t_triangulation The triangulation + /// @param t_initial_radius The initial radius of the radial foliation + /// @param t_foliation_spacing The spacing between successive leaves /// @return True if any vertex->info() was fixed template [[nodiscard]] auto fix_vertices(Delaunay_t const& t_triangulation, - double t_initial_radius, - double t_foliation_spacing) -> bool + double const t_initial_radius, + double const t_foliation_spacing) -> bool { - return fix_vertices( - get_all_finite_cells(t_triangulation), t_initial_radius, - t_foliation_spacing); + return fix_vertices(collect_cells(t_triangulation), + t_initial_radius, t_foliation_spacing); } // fix_vertices /// @brief Classifies cells by their timevalues /// @tparam dimension The dimensionality of the simplices /// @param t_cell The simplex to check - /// @param t_debug_flag Toggle for detailed debugging /// @return The type of the simplex template [[nodiscard]] auto expected_cell_type(Cell_handle_t const& t_cell) @@ -567,13 +584,12 @@ namespace foliated_triangulations [[nodiscard]] auto check_cells(Delaunay_t const& t_triangulation) -> bool { - auto checked_cells = get_all_finite_cells(t_triangulation); - return (checked_cells.empty()) - ? true - : std::all_of(checked_cells.begin(), checked_cells.end(), - [&](auto const& cell) { - return is_cell_type_correct(cell); - }); + auto checked_cells = collect_cells(t_triangulation); + return checked_cells.empty() || + std::all_of(checked_cells.begin(), checked_cells.end(), + [&](auto const& cell) { + return is_cell_type_correct(cell); + }); } // check_cells /// @brief Check all finite cells in the Delaunay triangulation @@ -584,7 +600,7 @@ namespace foliated_triangulations [[nodiscard]] auto find_incorrect_cells( Delaunay_t const& t_triangulation) { - auto checked_cells = get_all_finite_cells(t_triangulation); + auto checked_cells = collect_cells(t_triangulation); std::vector> incorrect_cells; std::copy_if(checked_cells.begin(), checked_cells.end(), @@ -611,6 +627,23 @@ namespace foliated_triangulations return !incorrect_cells.empty(); } // fix_cells + /// @brief Print a cell in the triangulation + /// @tparam dimension The dimensionality of the triangulation + /// @param cell The cell to print + template + void print_cell(Cell_handle_t cell) + { + fmt::print("Cell info => {}\n", cell->info()); + // There are d+1 vertices in a d-dimensional simplex + for (int j = 0; j < dimension + 1; ++j) + { + fmt::print("Vertex({}) Point: ({}) Timevalue: {}\n", j, + utilities::point_to_str(cell->vertex(j)->point()), + cell->vertex(j)->info()); + } + fmt::print("---\n"); + } // print_cell + /// @brief Print timevalues of each vertex in the cell and the resulting /// cell->info() /// @tparam dimension The dimensionality of the simplices @@ -621,20 +654,14 @@ namespace foliated_triangulations for (auto cells = std::forward(t_cells); auto const& cell : cells) { - fmt::print("Cell info => {}\n", cell->info()); - // There are d+1 vertices in a d-dimensional simplex - for (int j = 0; j < dimension + 1; ++j) - { - fmt::print("Vertex({}) Point: ({}) Timevalue: {}\n", j, - cell->vertex(j)->point(), cell->vertex(j)->info()); - } - fmt::print("---\n"); + print_cell(cell); } } // print_cells /// @brief Write to debug log timevalues of each vertex in the cell and the /// resulting cell->info /// @tparam dimension The dimensionality of the simplices + /// @tparam Container The type of container /// @param t_cells The cells to write to debug log template void debug_print_cells(Container&& t_cells) @@ -646,12 +673,26 @@ namespace foliated_triangulations for (int j = 0; j < dimension + 1; ++j) { spdlog::debug("Vertex({}) Point: ({}) Timevalue: {}\n", j, - cell->vertex(j)->point(), cell->vertex(j)->info()); + utilities::point_to_str(cell->vertex(j)->point()), + cell->vertex(j)->info()); } spdlog::debug("---\n"); } } // debug_print_cells + /// @brief Print neighboring cells + /// @tparam dimension The dimensionality of the simplices + /// @param cell The cell to print neighbors of + template + void print_neighboring_cells(Cell_handle_t cell) + { + for (int j = 0; j < dimension + 1; ++j) + { + fmt::print("Neighboring cell {}:", j); + print_cell(cell->neighbor(j)); + } + } // print_neighboring_cells + /// @brief Print edge /// @details An edge is represented by a cell and two indices which refer /// to the vertices of the cell connected by the edge. The data is stored @@ -666,9 +707,10 @@ namespace foliated_triangulations fmt::print( "Edge: Vertex({}) Point({}) Timevalue: {} -> Vertex({}) Point({}) " "Timevalue: {}\n", - t_edge.second, t_edge.first->vertex(t_edge.second)->point(), + t_edge.second, + utilities::point_to_str(t_edge.first->vertex(t_edge.second)->point()), t_edge.first->vertex(t_edge.second)->info(), t_edge.third, - t_edge.first->vertex(t_edge.third)->point(), + utilities::point_to_str(t_edge.first->vertex(t_edge.third)->point()), t_edge.first->vertex(t_edge.third)->info()); } // print_edge @@ -677,7 +719,6 @@ namespace foliated_triangulations /// of logs. /// @tparam dimension The dimensionality of the simplices /// @param t_facets A container of facets - /// @param t_debug_flag Debugging info toggle /// @return Container with spacelike facets per timeslice template [[nodiscard]] auto volume_per_timeslice(Container&& t_facets) @@ -690,8 +731,8 @@ namespace foliated_triangulations for (auto facets = std::forward(t_facets); auto const& face : facets) { - Cell_handle_t cell = face.first; - auto index_of_facet = face.second; + Cell_handle_t const cell = face.first; + auto index_of_facet = face.second; #ifndef NDEBUG spdlog::trace("Facet index is {}\n", index_of_facet); #endif @@ -745,8 +786,9 @@ namespace foliated_triangulations template [[nodiscard]] auto check_timevalues( Delaunay_t const& t_triangulation) + -> std::optional>> { - auto const& cells = get_all_finite_cells(t_triangulation); + auto const& cells = collect_cells(t_triangulation); std::vector> invalid_cells; std::copy_if( cells.begin(), cells.end(), std::back_inserter(invalid_cells), @@ -754,8 +796,8 @@ namespace foliated_triangulations return expected_cell_type(cell) == Cell_type::ACAUSAL || expected_cell_type(cell) == Cell_type::UNCLASSIFIED; }); - auto result = (invalid_cells.empty()) ? std::nullopt - : std::make_optional(invalid_cells); + auto result = invalid_cells.empty() ? std::nullopt + : std::make_optional(invalid_cells); return result; } // check_timevalues @@ -788,8 +830,8 @@ namespace foliated_triangulations // vertices with lower values. Note that we preferentially return higher // timeslice vertices because there are typically more cells at higher // timeslices (see expected_points_per_timeslice()) - return (minvalue_count >= maxvalue_count) ? vertices.rbegin()->second - : vertices.begin()->second; + return minvalue_count >= maxvalue_count ? vertices.rbegin()->second + : vertices.begin()->second; } // find_bad_vertex /// @brief Fix the vertices of a cell to be consistent with the foliation @@ -813,11 +855,14 @@ namespace foliated_triangulations std::inserter(vertices_to_remove, vertices_to_remove.begin()), find_bad_vertex); // Remove the vertices - fmt::print("There are {} invalid vertices.\n", vertices_to_remove.size()); +#ifndef NDEBUG + spdlog::warn("There are {} invalid vertices.\n", + vertices_to_remove.size()); +#endif t_triangulation.remove(vertices_to_remove.begin(), vertices_to_remove.end()); - Ensures(t_triangulation.tds().is_valid()); - Ensures(t_triangulation.is_valid()); + assert(t_triangulation.tds().is_valid()); + assert(t_triangulation.is_valid()); return true; } return false; @@ -835,15 +880,15 @@ namespace foliated_triangulations /// @return A container of (vertex, timevalue) pairs template [[nodiscard]] auto make_foliated_ball( - Int_precision t_simplices, Int_precision t_timeslices, - double initial_radius = INITIAL_RADIUS, - double foliation_spacing = FOLIATION_SPACING) + Int_precision const t_simplices, Int_precision const t_timeslices, + double const initial_radius = INITIAL_RADIUS, + double const foliation_spacing = FOLIATION_SPACING) { Causal_vertices_t causal_vertices; causal_vertices.reserve(static_cast(t_simplices)); auto const points_per_timeslice = utilities::expected_points_per_timeslice( dimension, t_simplices, t_timeslices); - Expects(points_per_timeslice >= 2); + assert(points_per_timeslice >= 2); for (gsl::index i = 0; i < t_timeslices; ++i) { @@ -856,7 +901,7 @@ namespace foliated_triangulations { causal_vertices.emplace_back(*gen++, i + 1); } // j - } // i + } // i return causal_vertices; } // make_foliated_ball @@ -869,9 +914,10 @@ namespace foliated_triangulations /// @return A Delaunay triangulation with a timevalue for each vertex template [[nodiscard]] auto make_triangulation( - Int_precision t_simplices, Int_precision t_timeslices, - double initial_radius = INITIAL_RADIUS, - double foliation_spacing = FOLIATION_SPACING) -> Delaunay_t + Int_precision const t_simplices, Int_precision t_timeslices, + double const initial_radius = INITIAL_RADIUS, + double const foliation_spacing = FOLIATION_SPACING) + -> Delaunay_t { #ifndef NDEBUG spdlog::debug("{} called.\n", __PRETTY_FUNCTION__); @@ -898,38 +944,38 @@ namespace foliated_triangulations triangulation.insert(causal_vertices.begin(), causal_vertices.end()); // Fix vertices - auto vertex_fix_passes = 1; - while (fix_vertices(triangulation, initial_radius, - foliation_spacing)) + for (auto passes = 1; passes < MAX_FIX_PASSES + 1; ++passes) { + if (!fix_vertices(triangulation, initial_radius, + foliation_spacing)) + { + break; + } #ifndef NDEBUG - spdlog::warn("Deleting incorrect vertices pass #{}\n", vertex_fix_passes); + spdlog::warn("Deleting incorrect vertices pass #{}\n", passes); #endif - ++vertex_fix_passes; } // Fix timeslices - auto passes = 1; - while (fix_timevalues(triangulation)) + for (auto passes = 1; passes < MAX_FIX_PASSES + 1; ++passes) { + if (!fix_timevalues(triangulation)) { break; } #ifndef NDEBUG spdlog::warn("Fixing timeslices pass #{}\n", passes); #endif - ++passes; } // Fix cells - auto cell_fix_passes = 1; - while (fix_cells(triangulation)) + for (auto i = 1; i < MAX_FIX_PASSES + 1; ++i) { + if (!fix_cells(triangulation)) { break; } #ifndef NDEBUG - spdlog::warn("Fixing incorrect cells pass #{}\n", cell_fix_passes); + spdlog::warn("Fixing incorrect cells pass #{}\n", i); #endif - ++cell_fix_passes; } utilities::print_delaunay(triangulation); - Ensures(!check_timevalues(triangulation)); + assert(!check_timevalues(triangulation)); return triangulation; } // make_triangulation @@ -938,19 +984,31 @@ namespace foliated_triangulations template class FoliatedTriangulation; - /// 3D Triangulation + /// @brief 3D Foliated triangulation + /// @details This class is a wrapper around a Delaunay triangulation. + /// The Delaunay triangulation is augmented with a timevalue for each + /// vertex and a simplex type for each cell. + /// The FoliatedTriangulation class invariant is that the Delaunay + /// triangulation has validly foliated vertices and cells, and has further + /// containers for the various sub-simplicial complexes of the triangulation. template <> class [[nodiscard("This contains data!")]] FoliatedTriangulation<3> // NOLINT { - using Cell_container = std::vector>; + using Delaunay = Delaunay_t<3>; + using Cell_handle = Cell_handle_t<3>; + using Cell_container = std::vector; using Face_container = std::vector>; using Edge_container = std::vector>; - using Vertex_container = std::vector>; + using Vertex_handle = Vertex_handle_t<3>; + using Vertex_container = std::vector; using Volume_by_timeslice = std::multimap>; /// Data members initialized in order of declaration (Working Draft, - /// Standard for C++ Programming Language, 12.6.2 section 13.3) - Delaunay_t<3> m_triangulation{Delaunay_t<3>{}}; + /// Standard for C++ Programming Language, 11.9.3 section 13.3) + Delaunay m_triangulation{Delaunay{}}; + double m_initial_radius{INITIAL_RADIUS}; + double m_foliation_spacing{FOLIATION_SPACING}; + Vertex_container m_vertices; Cell_container m_cells; Cell_container m_three_one; Cell_container m_two_two; @@ -960,11 +1018,8 @@ namespace foliated_triangulations Edge_container m_edges; Edge_container m_timelike_edges; Edge_container m_spacelike_edges; - Vertex_container m_points; Int_precision m_max_timevalue{0}; Int_precision m_min_timevalue{0}; - double m_initial_radius{INITIAL_RADIUS}; - double m_foliation_spacing{FOLIATION_SPACING}; public: /// @brief Default dtor @@ -976,18 +1031,20 @@ namespace foliated_triangulations /// @brief Copy Constructor FoliatedTriangulation(FoliatedTriangulation const& other) noexcept : FoliatedTriangulation( - static_cast const&>(other.get_delaunay())) + static_cast(other.get_delaunay()), + other.m_initial_radius, other.m_foliation_spacing) {} /// @brief Copy/Move Assignment operator - auto operator=(FoliatedTriangulation other) noexcept->FoliatedTriangulation& + auto operator=(FoliatedTriangulation other) noexcept + -> FoliatedTriangulation& { swap(other, *this); return *this; } /// @brief Move ctor - FoliatedTriangulation(FoliatedTriangulation && other) noexcept + FoliatedTriangulation(FoliatedTriangulation&& other) noexcept : FoliatedTriangulation{} { swap(other, *this); @@ -999,18 +1056,21 @@ namespace foliated_triangulations /// is discarded after it is swapped into the second one. /// @param swap_from The value to be swapped from. Assumed to be discarded. /// @param swap_into The value to be swapped into. - friend void swap(FoliatedTriangulation<3> & swap_from, - FoliatedTriangulation<3> & swap_into) noexcept + friend void swap(FoliatedTriangulation& swap_from, + FoliatedTriangulation& swap_into) noexcept { #ifndef NDEBUG spdlog::debug("{} called.\n", __PRETTY_FUNCTION__); #endif // Uses the triangulation swap method in CGAL - // This assumes that the first triangulation is not used afterwards! + // This assumes that the first triangulation is not used afterward! // See // https://doc.cgal.org/latest/Triangulation_3/classCGAL_1_1Triangulation__3.html#a767066a964b4d7b14376e5f5d1a04b34 swap_into.m_triangulation.swap(swap_from.m_triangulation); using std::swap; + swap(swap_from.m_initial_radius, swap_into.m_initial_radius); + swap(swap_from.m_foliation_spacing, swap_into.m_foliation_spacing); + swap(swap_from.m_vertices, swap_into.m_vertices); swap(swap_from.m_cells, swap_into.m_cells); swap(swap_from.m_three_one, swap_into.m_three_one); swap(swap_from.m_two_two, swap_into.m_two_two); @@ -1020,22 +1080,25 @@ namespace foliated_triangulations swap(swap_from.m_edges, swap_into.m_edges); swap(swap_from.m_timelike_edges, swap_into.m_timelike_edges); swap(swap_from.m_spacelike_edges, swap_into.m_spacelike_edges); - swap(swap_from.m_points, swap_into.m_points); swap(swap_from.m_max_timevalue, swap_into.m_max_timevalue); swap(swap_from.m_min_timevalue, swap_into.m_min_timevalue); - swap(swap_from.m_initial_radius, swap_into.m_initial_radius); - swap(swap_from.m_foliation_spacing, swap_into.m_foliation_spacing); + } // swap /// @brief Constructor using delaunay triangulation /// Pass-by-value-then-move. - /// Delaunay3 is the ctor for the Delaunay triangulation. + /// Delaunay is the ctor for the Delaunay triangulation. /// @param triangulation Delaunay triangulation - explicit FoliatedTriangulation(Delaunay_t<3> triangulation, - double initial_radius = INITIAL_RADIUS, - double foliation_spacing = FOLIATION_SPACING) + /// @param initial_radius Radius of first timeslice + /// @param foliation_spacing Radial separation between timeslices + explicit FoliatedTriangulation( + Delaunay triangulation, double const initial_radius = INITIAL_RADIUS, + double const foliation_spacing = FOLIATION_SPACING) : m_triangulation{std::move(triangulation)} - , m_cells{classify_cells(get_all_finite_cells<3>(m_triangulation))} + , m_initial_radius{initial_radius} + , m_foliation_spacing{foliation_spacing} + , m_vertices{classify_vertices(collect_vertices<3>(m_triangulation))} + , m_cells{classify_cells(collect_cells<3>(m_triangulation))} , m_three_one{filter_cells<3>(m_cells, Cell_type::THREE_ONE)} , m_two_two{filter_cells<3>(m_cells, Cell_type::TWO_TWO)} , m_one_three{filter_cells<3>(m_cells, Cell_type::ONE_THREE)} @@ -1044,11 +1107,8 @@ namespace foliated_triangulations , m_edges{collect_edges()} , m_timelike_edges{filter_edges<3>(m_edges, true)} , m_spacelike_edges{filter_edges<3>(m_edges, false)} - , m_points{get_all_finite_vertices<3>(m_triangulation)} - , m_max_timevalue{find_max_timevalue<3>(std::span{m_points})} - , m_min_timevalue{find_min_timevalue<3>(std::span{m_points})} - , m_initial_radius{initial_radius} - , m_foliation_spacing{foliation_spacing} + , m_max_timevalue{find_max_timevalue<3>(std::span{m_vertices})} + , m_min_timevalue{find_min_timevalue<3>(std::span{m_vertices})} {} /// @brief Constructor with parameters @@ -1056,9 +1116,10 @@ namespace foliated_triangulations /// @param t_timeslices Number of desired timeslices /// @param t_initial_radius Radius of first timeslice /// @param t_foliation_spacing Radial separation between timeslices - FoliatedTriangulation(Int_precision t_simplices, Int_precision t_timeslices, - double t_initial_radius = INITIAL_RADIUS, - double t_foliation_spacing = FOLIATION_SPACING) + FoliatedTriangulation(Int_precision const t_simplices, + Int_precision const t_timeslices, + double const t_initial_radius = INITIAL_RADIUS, + double const t_foliation_spacing = FOLIATION_SPACING) : FoliatedTriangulation{ make_triangulation<3>(t_simplices, t_timeslices, t_initial_radius, t_foliation_spacing), @@ -1068,12 +1129,14 @@ namespace foliated_triangulations /// @brief Constructor from Causal_vertices /// @param causal_vertices Causal_vertices to place into the /// FoliatedTriangulation + /// @param t_initial_radius Radius of first timeslice + /// @param t_foliation_spacing Radial separation between timeslices explicit FoliatedTriangulation( Causal_vertices_t<3> const& causal_vertices, - double t_initial_radius = INITIAL_RADIUS, - double t_foliation_spacing = FOLIATION_SPACING) + double const t_initial_radius = INITIAL_RADIUS, + double const t_foliation_spacing = FOLIATION_SPACING) : FoliatedTriangulation{ - Delaunay_t<3>{causal_vertices.begin(), causal_vertices.end()}, + Delaunay{causal_vertices.begin(), causal_vertices.end()}, t_initial_radius, t_foliation_spacing } {} @@ -1084,52 +1147,53 @@ namespace foliated_triangulations /// constructed (i.e. not in make_triangulation) /// /// @return True if foliated correctly - [[nodiscard]] auto is_foliated() const->bool + [[nodiscard]] auto is_foliated() const -> bool { return !static_cast(check_timevalues<3>(this->get_delaunay())); } // is_foliated /// @return True if the triangulation is Delaunay - [[nodiscard]] auto is_delaunay() const->bool + [[nodiscard]] auto is_delaunay() const -> bool { return get_delaunay().is_valid(); } // is_delaunay /// @return True if the triangulation data structure is valid - [[nodiscard]] auto is_tds_valid() const->bool + [[nodiscard]] auto is_tds_valid() const -> bool { return get_delaunay().tds().is_valid(); } // is_tds_valid /// @return True if the Foliated Triangulation class invariants hold - [[nodiscard]] auto is_correct() const->bool + [[nodiscard]] auto is_correct() const -> bool { return is_foliated() && is_tds_valid() && check_all_cells(); } // is_correct /// @return True if the Foliated Triangulation has been initialized /// correctly - [[nodiscard]] auto is_initialized() const->bool + [[nodiscard]] auto is_initialized() const -> bool { return is_correct() && is_delaunay(); } // is_initialized /// @return True if fixes were done on the Delaunay triangulation - [[nodiscard]] auto is_fixed()->bool + [[nodiscard]] auto is_fixed() -> bool { - auto fixed_vertices = foliated_triangulations::fix_vertices<3>( + auto const fixed_vertices = foliated_triangulations::fix_vertices<3>( m_triangulation, m_initial_radius, m_foliation_spacing); - auto fixed_cells = foliated_triangulations::fix_cells<3>(m_triangulation); - auto fixed_timeslices = + auto const fixed_cells = + foliated_triangulations::fix_cells<3>(m_triangulation); + auto const fixed_timeslices = foliated_triangulations::fix_timevalues<3>(m_triangulation); return fixed_vertices || fixed_cells || fixed_timeslices; } // is_fixed /// @return A mutable reference to the Delaunay triangulation - [[nodiscard]] auto delaunay()->Delaunay_t<3>& { return m_triangulation; } + [[nodiscard]] auto delaunay() -> Delaunay& { return m_triangulation; } /// @return A read-only reference to the Delaunay triangulation - [[nodiscard]] auto get_delaunay() const->Delaunay_t<3> const& + [[nodiscard]] auto get_delaunay() const -> Delaunay const& { return std::cref(m_triangulation); } // get_delaunay @@ -1161,7 +1225,7 @@ namespace foliated_triangulations /// @return If a cell or vertex contains or is the infinite vertex /// Forward parameters (see F.19 of C++ Core Guidelines) template - [[nodiscard]] auto is_infinite(VertexHandle && t_vertex) const + [[nodiscard]] auto is_infinite(VertexHandle&& t_vertex) const { return m_triangulation.is_infinite(std::forward(t_vertex)); } // is_infinite @@ -1175,7 +1239,7 @@ namespace foliated_triangulations /// @param args Parameter pack of arguments to TDS3.flip /// @return True if the flip occurred template - [[nodiscard]] auto flip(Ts && ... args) + [[nodiscard]] auto flip(Ts&&... args) { return m_triangulation.flip(std::forward(args)...); } // flip @@ -1190,9 +1254,8 @@ namespace foliated_triangulations [[nodiscard]] auto dimension() const { return m_triangulation.dimension(); } /// @return Container of spacelike facets indexed by time value - [[nodiscard]] auto N2_SL() - const->std::multimap::Facet> const& + [[nodiscard]] auto N2_SL() const + -> std::multimap::Facet> const& { return m_spacelike_facets; } // N2_SL @@ -1210,24 +1273,31 @@ namespace foliated_triangulations } // N1_SL /// @return Container of timelike edges - [[nodiscard]] auto get_timelike_edges() - const noexcept->Edge_container const& + [[nodiscard]] auto get_timelike_edges() const noexcept + -> Edge_container const& { return m_timelike_edges; } // get_timelike_edges /// @return Container of spacelike edges - [[nodiscard]] auto get_spacelike_edges() const->Edge_container const& + [[nodiscard]] auto get_spacelike_edges() const -> Edge_container const& { return m_spacelike_edges; } // get_spacelike_edges /// @return Container of vertices - [[nodiscard]] auto get_vertices() const noexcept->Vertex_container const& + [[nodiscard]] auto get_vertices() const noexcept -> Vertex_container const& { - return m_points; + return m_vertices; } // get_vertices + /// @return A span of vertices + [[nodiscard]] auto get_vertices_span() const noexcept + -> std::span + { + return std::span{m_vertices}; + } // get_vertices_span + /// @return Maximum time value in triangulation [[nodiscard]] auto max_time() const { return m_max_timevalue; } @@ -1245,7 +1315,7 @@ namespace foliated_triangulations /// @see /// https://doc.cgal.org/latest/TDS_3/classTriangulationDataStructure__3.html#a51fce32aa7abf3d757bcabcebd22f2fe template - [[nodiscard]] auto degree(VertexHandle && t_vertex) const + [[nodiscard]] auto degree(VertexHandle&& t_vertex) const { return m_triangulation.degree(std::forward(t_vertex)); } // degree @@ -1259,8 +1329,8 @@ namespace foliated_triangulations /// @see /// https://doc.cgal.org/latest/TDS_3/classTriangulationDataStructure__3.html#a93f8ab30228b2a515a5c9cdacd9d4d36 template - [[nodiscard]] auto incident_cells(VertexHandle && t_vh) - const noexcept->decltype(auto) + [[nodiscard]] auto incident_cells(VertexHandle&& t_vh) const noexcept + -> decltype(auto) { Cell_container inc_cells; get_delaunay().tds().incident_cells(std::forward(t_vh), @@ -1276,8 +1346,8 @@ namespace foliated_triangulations /// @see /// https://doc.cgal.org/latest/TDS_3/classTriangulationDataStructure__3.html#a93f8ab30228b2a515a5c9cdacd9d4d36 template - [[nodiscard]] auto incident_cells(Ts && ... args) - const noexcept->decltype(auto) + [[nodiscard]] auto incident_cells(Ts&&... args) const noexcept + -> decltype(auto) { return get_delaunay().tds().incident_cells(std::forward(args)...); } // incident_cells @@ -1287,14 +1357,14 @@ namespace foliated_triangulations /// @return True if the effective radial distance squared matches timevalue /// squared [[nodiscard]] auto does_vertex_radius_match_timevalue( - Vertex_handle_t<3> const t_vertex) const->bool + Vertex_handle_t<3> const t_vertex) const -> bool { auto const actual_radius_squared = squared_radius<3>(t_vertex); auto const radius = expected_radius(t_vertex); auto const expected_radius_squared = std::pow(radius, 2); - return ( - actual_radius_squared > expected_radius_squared * (1 - TOLERANCE) && - actual_radius_squared < expected_radius_squared * (1 + TOLERANCE)); + return actual_radius_squared > + expected_radius_squared * (1 - TOLERANCE) && + actual_radius_squared < expected_radius_squared * (1 + TOLERANCE); } // does_vertex_radius_match_timevalue /// @brief Calculates the expected radial distance of a vertex @@ -1306,8 +1376,8 @@ namespace foliated_triangulations /// /// @param t_vertex The vertex to check /// @return The expected radial distance of the vertex with that timevalue - [[nodiscard]] auto expected_radius(Vertex_handle_t<3> const& t_vertex) - const->double + [[nodiscard]] auto expected_radius(Vertex_handle_t<3> const& t_vertex) const + -> double { auto const timevalue = t_vertex->info(); return m_initial_radius + m_foliation_spacing * (timevalue - 1); @@ -1316,15 +1386,15 @@ namespace foliated_triangulations /// @brief Calculate the expected timevalue for a vertex /// @param t_vertex The vertex to check /// @return The expected timevalue of the vertex - [[nodiscard]] auto expected_timevalue(Vertex_handle_t<3> const& t_vertex) - const->int + [[nodiscard]] auto expected_timevalue( + Vertex_handle_t<3> const& t_vertex) const -> int { return foliated_triangulations::expected_timevalue<3>( t_vertex, m_initial_radius, m_foliation_spacing); } // expected_timevalue /// @return True if all vertices have correct timevalues - [[nodiscard]] auto check_all_vertices() const->bool + [[nodiscard]] auto check_all_vertices() const -> bool { return foliated_triangulations::check_vertices<3>( m_triangulation, m_initial_radius, m_foliation_spacing); @@ -1338,8 +1408,7 @@ namespace foliated_triangulations } // find_incorrect_vertices /// @brief Fix vertices with wrong timevalues after foliation - /// @param incorrect_vertices The container of incorrect vertices - [[nodiscard]] auto fix_vertices() const->bool + [[nodiscard]] auto fix_vertices() const -> bool { return foliated_triangulations::fix_vertices<3>( m_triangulation, m_initial_radius, m_foliation_spacing); @@ -1348,10 +1417,11 @@ namespace foliated_triangulations /// @brief Print values of a vertex void print_vertices() const { - for (auto const& vertex : m_points) + for (auto const& vertex : m_vertices) { fmt::print("Vertex Point: ({}) Timevalue: {} Expected Timevalue: {}\n", - vertex->point(), vertex->info(), expected_timevalue(vertex)); + utilities::point_to_str(vertex->point()), vertex->info(), + expected_timevalue(vertex)); } } // print_vertices @@ -1377,26 +1447,27 @@ namespace foliated_triangulations } // print_volume_per_timeslice /// @return Container of cells - [[nodiscard]] auto get_cells() const->Cell_container const& + [[nodiscard]] auto get_cells() const -> Cell_container const& { - Ensures(m_cells.size() == number_of_finite_cells()); + assert(m_cells.size() == number_of_finite_cells()); return m_cells; } // get_cells /// @return Container of (3,1) cells - [[nodiscard]] auto get_three_one() const->Cell_container const& + [[nodiscard]] auto get_three_one() const noexcept + -> std::span { - return m_three_one; + return std::span{m_three_one}; } // get_three_one /// @return Container of (2,2) cells - [[nodiscard]] auto get_two_two() const noexcept->Cell_container const& + [[nodiscard]] auto get_two_two() const noexcept -> Cell_container const& { return m_two_two; } // get_two_two /// @return Container of (1,3) cells - [[nodiscard]] auto get_one_three() const noexcept->Cell_container const& + [[nodiscard]] auto get_one_three() const noexcept -> Cell_container const& { return m_one_three; } // get_one_three @@ -1406,13 +1477,13 @@ namespace foliated_triangulations /// the triangulation is correctly classified. A triangulation with cells /// will have them checked via check_cells. /// @return True if there are no cells or all cells are validly classified - [[nodiscard]] auto check_all_cells() const->bool + [[nodiscard]] auto check_all_cells() const -> bool { return foliated_triangulations::check_cells<3>(get_delaunay()); } // check_all_cells /// @brief Fix all cells in the triangulation - auto fix_cells() const->bool + auto fix_cells() const -> bool { return foliated_triangulations::fix_cells<3>(get_delaunay()); } // fix_cells @@ -1435,14 +1506,24 @@ namespace foliated_triangulations } private: + [[nodiscard]] auto classify_vertices(Vertex_container const& vertices) const + -> Vertex_container + { + assert(vertices.size() == number_of_vertices()); + for (auto const& vertex : vertices) + { + vertex->info() = expected_timevalue(vertex); + } + return vertices; + } // classify_vertices + /// @brief Classify cells /// @param cells The container of simplices to classify - /// @param t_debug_flag Debugging info toggle /// @return A container of simplices with Cell_type written to cell->info() - [[nodiscard]] auto classify_cells(Cell_container const& cells) - const->Cell_container + [[nodiscard]] auto classify_cells(Cell_container const& cells) const + -> Cell_container { - Expects(cells.size() == number_of_finite_cells()); + assert(cells.size() == number_of_finite_cells()); for (auto const& cell : cells) { cell->info() = static_cast(expected_cell_type<3>(cell)); @@ -1451,54 +1532,56 @@ namespace foliated_triangulations } // classify_cells /// @return Container of all the finite facets in the triangulation - [[nodiscard]] auto collect_faces() const->Face_container + [[nodiscard]] auto collect_faces() const -> Face_container { // Somewhere in bistellar_flip_really a vertex is rendered invalid - Expects(is_tds_valid()); + assert(is_tds_valid()); Face_container init_faces; init_faces.reserve(get_delaunay().number_of_finite_facets()); for (auto fit = get_delaunay().finite_facets_begin(); fit != get_delaunay().finite_facets_end(); ++fit) { - Cell_handle_t<3> cell = fit->first; + Cell_handle_t<3> const cell = fit->first; // Each face is valid in the triangulation - Ensures(get_delaunay().tds().is_facet(cell, fit->second)); - Face_handle_t<3> thisFacet{std::make_pair(cell, fit->second)}; + assert(get_delaunay().tds().is_facet(cell, fit->second)); + Face_handle_t<3> const thisFacet{std::make_pair(cell, fit->second)}; init_faces.emplace_back(thisFacet); } - Ensures(init_faces.size() == get_delaunay().number_of_finite_facets()); + assert(init_faces.size() == get_delaunay().number_of_finite_facets()); return init_faces; } // collect_faces /// @return Container of all the finite edges in the triangulation - [[nodiscard]] auto collect_edges() const->Edge_container + [[nodiscard]] auto collect_edges() const -> Edge_container { - Expects(is_tds_valid()); + assert(is_tds_valid()); Edge_container init_edges; init_edges.reserve(number_of_finite_edges()); for (auto eit = get_delaunay().finite_edges_begin(); eit != get_delaunay().finite_edges_end(); ++eit) { Cell_handle_t<3> const cell = eit->first; - Edge_handle_t<3> thisEdge{cell, cell->index(cell->vertex(eit->second)), - cell->index(cell->vertex(eit->third))}; + Edge_handle_t<3> const thisEdge{cell, + cell->index(cell->vertex(eit->second)), + cell->index(cell->vertex(eit->third))}; // Each edge is valid in the triangulation - Ensures(get_delaunay().tds().is_valid(thisEdge.first, thisEdge.second, - thisEdge.third)); + assert(get_delaunay().tds().is_valid(thisEdge.first, thisEdge.second, + thisEdge.third)); init_edges.emplace_back(thisEdge); } - Ensures(init_edges.size() == number_of_finite_edges()); + assert(init_edges.size() == number_of_finite_edges()); return init_edges; } // collect_edges }; - using FoliatedTriangulation3 = FoliatedTriangulation<3>; + using FoliatedTriangulation_3 = FoliatedTriangulation<3>; /// 4D Triangulation template <> - class [[nodiscard("This contains data!")]] FoliatedTriangulation<4>{}; + class [[nodiscard("This contains data!")]] FoliatedTriangulation<4> + {}; - using FoliatedTriangulation4 = FoliatedTriangulation<4>; + using FoliatedTriangulation_4 = FoliatedTriangulation<4>; } // namespace foliated_triangulations diff --git a/include/Geometry.hpp b/include/Geometry.hpp index ab3623f481..192f98da8c 100644 --- a/include/Geometry.hpp +++ b/include/Geometry.hpp @@ -5,7 +5,10 @@ ******************************************************************************/ /// @file Geometry.hpp -/// @brief Geometric quantities of Manifold used by MoveAlgorithm. +/// @brief Geometric scalars of the Manifold used to calculate the Regge action +/// @details This is a data structure to hold the geometric information of the +/// Manifold for quick access in calculation of the Regge action. There are +/// no class invariants, so it is a simple struct. /// @author Adam Getchell #ifndef CDT_PLUSPLUS_GEOMETRY_HPP @@ -59,7 +62,7 @@ struct [[nodiscard("This contains data!")]] Geometry<3> /// @param triangulation Triangulation for which Geometry is being /// calculated explicit Geometry( - foliated_triangulations::FoliatedTriangulation3 const& triangulation) + foliated_triangulations::FoliatedTriangulation_3 const& triangulation) : N3{static_cast(triangulation.number_of_finite_cells())} , N3_31{static_cast(triangulation.get_three_one().size())} @@ -79,7 +82,7 @@ struct [[nodiscard("This contains data!")]] Geometry<3> /// Usually called from a Manifold swap. /// @param swap_from The value to be swapped from. Assumed to be discarded. /// @param swap_into The value to be swapped into. - friend void swap(Geometry<3> & swap_from, Geometry<3> & swap_into) noexcept + friend void swap(Geometry& swap_from, Geometry& swap_into) noexcept { #ifndef NDEBUG spdlog::debug("{} called.\n", __PRETTY_FUNCTION__); @@ -96,9 +99,9 @@ struct [[nodiscard("This contains data!")]] Geometry<3> swap(swap_from.N1_SL, swap_into.N1_SL); swap(swap_from.N0, swap_into.N0); } // swap -}; // struct Geometry<3> +}; // struct Geometry<3> -using Geometry3 = Geometry<3>; +using Geometry_3 = Geometry<3>; template <> struct [[nodiscard("This contains data!")]] Geometry<4> @@ -110,6 +113,6 @@ struct [[nodiscard("This contains data!")]] Geometry<4> Int_precision N0{0}; }; // struct Geometry<4> -using Geometry4 = Geometry<4>; +using Geometry_4 = Geometry<4>; #endif // CDT_PLUSPLUS_GEOMETRY_HPP diff --git a/include/Manifold.hpp b/include/Manifold.hpp index 1769898102..6644f6de26 100644 --- a/include/Manifold.hpp +++ b/include/Manifold.hpp @@ -13,12 +13,25 @@ #include #include -#include #include "Geometry.hpp" namespace manifolds { + /// @brief Create Causal vertices + /// @tparam dimension Dimensionality of vertices + /// @param vertices A container of vertices + /// @param timevalues A container of matching timevalues + /// @return A container of Causal_vertices + template + auto make_causal_vertices(std::span const> vertices, + std::span const timevalues) + -> Causal_vertices_t + { + return foliated_triangulations::make_causal_vertices(vertices, + timevalues); + } + /// Manifold class template /// @tparam dimension Dimensionality of manifold template @@ -28,8 +41,8 @@ namespace manifolds template <> class [[nodiscard("This contains data!")]] Manifold<3> { - using Triangulation = foliated_triangulations::FoliatedTriangulation3; - using Geometry = Geometry3; + using Triangulation = foliated_triangulations::FoliatedTriangulation_3; + using Geometry = Geometry_3; /// @brief The data structure of geometric and combinatorial relationships Triangulation m_triangulation; @@ -40,31 +53,34 @@ namespace manifolds public: /// @brief Dimensionality of the manifold /// @details Used to determine the manifold dimension at compile-time - static int constexpr dimension = 3; + static int constexpr dimension = 3; + + /// @brief Topology of the manifold + static topology_type constexpr topology = topology_type::SPHERICAL; /// @brief Default dtor - ~Manifold() = default; + ~Manifold() = default; /// @brief Default ctor - Manifold() = default; + Manifold() = default; /// @brief Default copy ctor - Manifold(Manifold const& other) = default; + Manifold(Manifold const& other) = default; /// @brief Default copy assignment - auto operator=(Manifold const& other)->Manifold& = default; + auto operator=(Manifold const& other) -> Manifold& = default; /// @brief Default move ctor - Manifold(Manifold && other) = default; + Manifold(Manifold&& other) = default; /// @brief Default move assignment - auto operator=(Manifold&& other)->Manifold& = default; + auto operator=(Manifold&& other) -> Manifold& = default; /// @brief Non-member swap function for Manifolds. /// @details Used for no-except updates of manifolds after moves. /// @param swap_from The value to be swapped from. Assumed to be discarded. /// @param swap_into The value to be swapped into. - friend void swap(Manifold<3> & swap_from, Manifold<3> & swap_into) noexcept + friend void swap(Manifold& swap_from, Manifold& swap_into) noexcept { #ifndef NDEBUG spdlog::debug("{} called.\n", __PRETTY_FUNCTION__); @@ -86,10 +102,10 @@ namespace manifolds /// @param t_desired_timeslices Number of desired timeslices /// @param t_initial_radius Radius of first timeslice /// @param t_foliation_spacing Radial separation between timeslices - Manifold(Int_precision t_desired_simplices, - Int_precision t_desired_timeslices, - double t_initial_radius = INITIAL_RADIUS, - double t_foliation_spacing = FOLIATION_SPACING) + Manifold(Int_precision const t_desired_simplices, + Int_precision const t_desired_timeslices, + double const t_initial_radius = INITIAL_RADIUS, + double const t_foliation_spacing = FOLIATION_SPACING) : Manifold{ Triangulation{t_desired_simplices, t_desired_timeslices, t_initial_radius, t_foliation_spacing} @@ -102,8 +118,8 @@ namespace manifolds /// @param t_initial_radius Radius of first timeslice /// @param t_foliation_spacing Radial separation between timeslices explicit Manifold(Causal_vertices_t<3> const& causal_vertices, - double t_initial_radius = INITIAL_RADIUS, - double t_foliation_spacing = FOLIATION_SPACING) + double const t_initial_radius = INITIAL_RADIUS, + double const t_foliation_spacing = FOLIATION_SPACING) : Manifold{ Triangulation{causal_vertices, t_initial_radius, t_foliation_spacing} @@ -125,74 +141,81 @@ namespace manifolds spdlog::trace("Exception thrown: {}\n", ex.what()); } // update - /// @return A read-only reference to the triangulation - [[nodiscard]] auto get_triangulation() const noexcept->Triangulation const& + /// @returns A read-only reference to the triangulation + [[nodiscard]] auto get_triangulation() const noexcept + -> Triangulation const& { return std::cref(m_triangulation); } // get_triangulation - /// @return A mutable reference to the triangulation - [[nodiscard]] auto triangulation()->Triangulation& + /// @returns A read-only reference to the Delaunay triangulation + [[nodiscard]] auto get_delaunay() const noexcept + { + return get_triangulation().get_delaunay(); + } // get_delaunay + + /// @returns A mutable reference to the triangulation + [[nodiscard]] auto triangulation() -> Triangulation& { return m_triangulation; } // triangulation - /// @return A read-only reference to the Geometry - [[nodiscard]] auto get_geometry() const->Geometry const& + /// @returns A read-only reference to the Geometry + [[nodiscard]] auto get_geometry() const -> Geometry const& { return m_geometry; } // get_geometry - /// @brief Forwarding to FoliatedTriangulation3.is_foliated() - /// @return True if the Manifold triangulation is foliated - [[nodiscard]] auto is_foliated() const->bool + /// @brief Forwarding to FoliatedTriangulation_3.is_foliated() + /// @returns True if the Manifold triangulation is foliated + [[nodiscard]] auto is_foliated() const -> bool { return m_triangulation.is_foliated(); } // is_foliated /// @brief Forwarding to FoliatedTriangulation.is_delaunay() - /// @return True if the Manifold triangulation is Delaunay - [[nodiscard]] auto is_delaunay() const->bool + /// @returns True if the Manifold triangulation is Delaunay + [[nodiscard]] auto is_delaunay() const -> bool { return m_triangulation.is_delaunay(); } // is_delaunay /// @brief Forwarding to FoliatedTriangulation.is_tds_valid() - /// @return True if the TriangulationDataStructure is valid - [[nodiscard]] auto is_valid() const->bool + /// @returns True if the TriangulationDataStructure is valid + [[nodiscard]] auto is_valid() const -> bool { return m_triangulation.is_tds_valid(); } // is_valid - /// @return If base data structures are correct - [[nodiscard]] auto is_correct() const->bool + /// @returns If base data structures are correct + [[nodiscard]] auto is_correct() const -> bool { return m_triangulation.is_correct(); } // is_correct - /// @brief Perfect forwarding to FoliatedTriangulation3.is_vertex() + /// @brief Perfect forwarding to FoliatedTriangulation_3.is_vertex() /// @tparam VertexType The vertex type /// @param t_vertex_candidate The vertex to check - /// @return True if the vertex candidate is a vertex + /// @returns True if the vertex candidate is a vertex template - [[nodiscard]] auto is_vertex(VertexType && t_vertex_candidate) const->bool + [[nodiscard]] auto is_vertex(VertexType&& t_vertex_candidate) const -> bool { return m_triangulation.get_delaunay().is_vertex( std::forward(t_vertex_candidate)); } // is_vertex - /// @brief Forwarding to FoliatedTriangulation3.is_edge() + /// @brief Forwarding to FoliatedTriangulation_3.is_edge() /// @param t_edge_candidate The edge to test - /// @return True if the candidate is an edge - [[nodiscard]] auto is_edge(Edge_handle_t<3> const& t_edge_candidate) - const noexcept->bool + /// @returns True if the candidate is an edge + [[nodiscard]] auto is_edge( + Edge_handle_t<3> const& t_edge_candidate) const noexcept -> bool { return m_triangulation.get_delaunay().tds().is_edge( t_edge_candidate.first, t_edge_candidate.second, t_edge_candidate.third); } // is_edge - /// @return Run-time dimensionality of the triangulation data structure + /// @returns Run-time dimensionality of the triangulation data structure [[nodiscard]] auto dimensionality() const { return m_triangulation.dimension(); @@ -210,119 +233,126 @@ namespace manifolds return m_triangulation.foliation_spacing(); } - /// @return Number of 3D simplices in geometry data structure + /// @returns Number of 3D simplices in geometry data structure [[nodiscard]] auto N3() const { return m_geometry.N3; } - /// @return Number of (3,1) simplices in geometry data structure + /// @returns Number of (3,1) simplices in geometry data structure [[nodiscard]] auto N3_31() const { return m_geometry.N3_31; } - /// @return Number of (2,2) simplices in geometry data structure + /// @returns Number of (2,2) simplices in geometry data structure [[nodiscard]] auto N3_22() const { return m_geometry.N3_22; } - /// @return Number of (1,3) simplices in geometry data structure + /// @returns Number of (1,3) simplices in geometry data structure [[nodiscard]] auto N3_13() const { return m_geometry.N3_13; } - /// @return Number of (3,1) and (1,3) simplices in geometry data structure + /// @returns Number of (3,1) and (1,3) simplices in geometry data structure [[nodiscard]] auto N3_31_13() const { return m_geometry.N3_31_13; } - /// @return Number of 3D simplices in triangulation data structure + /// @returns Number of 3D simplices in triangulation data structure [[nodiscard]] auto simplices() const { return static_cast(m_triangulation.get_cells().size()); } // number_of_simplices - /// @return Number of 2D faces in geometry data structure + /// @returns Number of 2D faces in geometry data structure [[nodiscard]] auto N2() const { return m_geometry.N2; } - /// @return An associative container of spacelike faces indexed by timevalue - [[nodiscard]] auto N2_SL() const->auto const& + /// @returns An associative container of spacelike faces indexed by + /// timevalue + [[nodiscard]] auto N2_SL() const -> auto const& { return m_triangulation.N2_SL(); } // N2_SL - /// @return Number of 2D faces in triangulation data structure + /// @returns Number of 2D faces in triangulation data structure [[nodiscard]] auto faces() const { return static_cast( m_triangulation.number_of_finite_facets()); } // faces - /// @return Number of 1D edges in geometry data structure + /// @returns Number of 1D edges in geometry data structure [[nodiscard]] auto N1() const { return m_geometry.N1; } - /// @return Number of spacelike edges in triangulation data structure + /// @returns Number of spacelike edges in triangulation data structure [[nodiscard]] auto N1_SL() const { return m_triangulation.N1_SL(); } - /// @return Number of timelike edges in triangulation data structure + /// @returns Number of timelike edges in triangulation data structure [[nodiscard]] auto N1_TL() const { return m_triangulation.N1_TL(); } - /// @return Number of 1D edges in triangulation data structure + /// @returns Number of 1D edges in triangulation data structure [[nodiscard]] auto edges() const { return static_cast( m_triangulation.number_of_finite_edges()); } // edges - /// @return Number of vertices in geometry data structure + /// @returns Number of vertices in geometry data structure [[nodiscard]] auto N0() const { return m_geometry.N0; } - /// @return Number of vertices in triangulation data structure + /// @returns Number of vertices in triangulation data structure [[nodiscard]] auto vertices() const { return static_cast(m_triangulation.number_of_vertices()); } // vertices - /// @return Minimum timeslice value in triangulation data structure + /// @returns Minimum timeslice value in triangulation data structure [[nodiscard]] auto min_time() const { return m_triangulation.min_time(); } // min_time - /// @return Maximum timeslice value in triangulation data structure + /// @returns Maximum timeslice value in triangulation data structure [[nodiscard]] auto max_time() const { return m_triangulation.max_time(); } // max_time - /// @brief Perfect forwarding to FoliatedTriangulation3.degree() + /// @brief Perfect forwarding to FoliatedTriangulation_3.degree() template - [[nodiscard]] auto degree(VertexHandle && t_vertex) const->decltype(auto) + [[nodiscard]] auto degree(VertexHandle&& t_vertex) const -> decltype(auto) { return m_triangulation.degree(std::forward(t_vertex)); } // degree - /// @brief Perfect forwarding to FoliatedTriangulation3.incident_cells() + /// @brief Perfect forwarding to FoliatedTriangulation_3.incident_cells() template - [[nodiscard]] auto incident_cells(Ts && ... args) - const noexcept->decltype(auto) + [[nodiscard]] auto incident_cells(Ts&&... args) const noexcept + -> decltype(auto) { return m_triangulation.incident_cells(std::forward(args)...); } // incident_cells /// @brief Call to triangulation_.get_timelike_edges() - [[nodiscard]] auto get_timelike_edges() const noexcept->auto const& + [[nodiscard]] auto get_timelike_edges() const noexcept -> auto const& { return m_triangulation.get_timelike_edges(); } // get_timelike_edges /// @brief Call triangulation.get_spacelike_edges() - [[nodiscard]] auto get_spacelike_edges() const->auto const& + [[nodiscard]] auto get_spacelike_edges() const -> auto const& { return m_triangulation.get_spacelike_edges(); } // get_spacelike_edges - /// @brief Call FoliatedTriangulation3.get_vertices() - [[nodiscard]] auto get_vertices() const noexcept->auto const& + /// @brief Call FoliatedTriangulation_3.get_vertices() + [[nodiscard]] auto get_vertices() const noexcept -> auto const& { return m_triangulation.get_vertices(); } // get_vertices - /// @return True if all cells in triangulation are classified and match + /// @brief Call FoliatedTriangulation_3.get_vertices_span() + [[nodiscard]] auto get_vertices_span() const noexcept -> auto + { + return m_triangulation.get_vertices_span(); + } // get_vertices_span + + /// @returns True if all cells in triangulation are classified and match /// number in geometry - [[nodiscard]] auto check_simplices() const->bool + [[nodiscard]] auto check_simplices() const -> bool { - return (this->simplices() == this->N3() && - m_triangulation.check_all_cells()); + return this->simplices() == this->N3() && + m_triangulation.check_all_cells(); } // check_simplices /// @brief Print the codimension 1 volume of simplices (faces) per timeslice @@ -370,6 +400,31 @@ namespace manifolds throw; } // print_details + /// @brief Obtains a vertex handle from a point + /// @param point The point to search for + /// @return The vertex handle to the vertex containing the point + auto get_vertex(Point_t<3> const& point) const -> Vertex_handle_t<3> + { + Vertex_handle_t<3> result; + m_triangulation.get_delaunay().is_vertex(point, result); + return result; + } // get_vertex + + /// @brief Get a cell handle from 4 vertex handles + /// @param vh1 The first vertex handle + /// @param vh2 The second vertex handle + /// @param vh3 The third vertex handle + /// @param vh4 The fourth vertex handle + /// @return The cell handle containing the vertex handles + auto get_cell(Vertex_handle_t<3> const& vh1, Vertex_handle_t<3> const& vh2, + Vertex_handle_t<3> const& vh3, + Vertex_handle_t<3> const& vh4) const -> Cell_handle_t<3> + { + Cell_handle_t<3> result; + m_triangulation.get_delaunay().is_cell(vh1, vh2, vh3, vh4, result); + return result; + } // get_cell_handle + private: /// @brief Update the triangulation void update_triangulation() @@ -378,8 +433,9 @@ namespace manifolds #ifndef NDEBUG spdlog::debug("{} called.\n", __PRETTY_FUNCTION__); #endif - Triangulation const local_triangulation(m_triangulation.get_delaunay()); - m_triangulation = local_triangulation; + // Constructing a new triangulation updates all data structures + Triangulation local_triangulation(m_triangulation.get_delaunay()); + swap(local_triangulation, m_triangulation); } catch (std::system_error const& ex) { @@ -393,27 +449,31 @@ namespace manifolds #ifndef NDEBUG spdlog::debug("{} called.\n", __PRETTY_FUNCTION__); #endif + // constructing a new geometry updates all data structures Geometry geom(m_triangulation); swap(geom, m_geometry); } // update_geometry }; - using Manifold3 = Manifold<3>; + using Manifold_3 = Manifold<3>; /// 4D Manifold template <> class [[nodiscard("This contains data!")]] Manifold<4> { /// @brief The data structure of scalar values for computations - Geometry4 m_geometry; + Geometry_4 m_geometry; public: /// @brief Dimensionality of the manifold /// @details Used to determine the manifold dimension at compile-time - static int constexpr dimension = 4; + static int constexpr dimension = 4; + + /// @brief Topology of the manifold + static topology_type constexpr topology = topology_type::SPHERICAL; }; - using Manifold4 = Manifold<4>; + using Manifold_4 = Manifold<4>; } // namespace manifolds diff --git a/include/Metropolis.hpp b/include/Metropolis.hpp index e110b87328..3c2def28fd 100644 --- a/include/Metropolis.hpp +++ b/include/Metropolis.hpp @@ -18,6 +18,7 @@ #define INCLUDE_METROPOLIS_HPP_ // CDT headers +#include "Move_command.hpp" #include "Move_strategy.hpp" #include "S3Action.hpp" @@ -93,9 +94,10 @@ class MoveStrategy /// Cosmological constant. /// @param passes Number of passes of ergodic moves on triangulation. /// @param checkpoint Print/write output for every n=checkpoint passes. - [[maybe_unused]] MoveStrategy(long double Alpha, long double K, - long double Lambda, Int_precision passes, - Int_precision checkpoint) + [[maybe_unused]] MoveStrategy(long double const Alpha, long double const K, + long double const Lambda, + Int_precision const passes, + Int_precision const checkpoint) : m_Alpha(Alpha) , m_K(K) , m_Lambda(Lambda) @@ -107,37 +109,37 @@ class MoveStrategy #endif } - /// @return The length of the timelike edge + /// @returns The length of the timelike edge [[nodiscard]] auto Alpha() const noexcept { return m_Alpha; } - /// @return The normalized Newton's constant + /// @returns The normalized Newton's constant [[nodiscard]] auto K() const noexcept { return m_K; } - /// @return The normalized Cosmological constant + /// @returns The normalized Cosmological constant [[nodiscard]] auto Lambda() const noexcept { return m_Lambda; } - /// @return The number of passes to make + /// @returns The number of passes to make [[nodiscard]] auto passes() const noexcept { return m_passes; } - /// @return The number of passes before writing a checkpoint file + /// @returns The number of passes before writing a checkpoint file [[nodiscard]] auto checkpoint() const noexcept { return m_checkpoint; } - /// @return The container of trial moves + /// @returns The container of trial moves auto get_proposed() const { return m_proposed_moves; } - /// @return The container of accepted moves + /// @returns The container of accepted moves auto get_accepted() const { return m_accepted_moves; } - /// @return The container of rejected moves + /// @returns The container of rejected moves auto get_rejected() const { return m_rejected_moves; } - /// @return The container of attempted moves + /// @returns The container of attempted moves auto get_attempted() const { return m_attempted_moves; } - /// @return The container of successful moves + /// @returns The container of successful moves auto get_succeeded() const { return m_succeeded_moves; } - /// @return The container of failed moves + /// @returns The container of failed moves auto get_failed() const { return m_failed_moves; } /// @brief Calculate A1 @@ -146,7 +148,7 @@ class MoveStrategy /// \f[a_1=\frac{move[i]}{\sum\limits_{i}move[i]}\f] /// /// @param move The type of move - /// @return \f$a_1=\frac{move[i]}{\sum\limits_{i}move[i]}\f$ + /// @returns \f$a_1=\frac{move[i]}{\sum\limits_{i}move[i]}\f$ auto CalculateA1(move_tracker::move_type move) const noexcept { auto all_moves = m_proposed_moves.total(); @@ -185,9 +187,9 @@ class MoveStrategy /// @details Calculate \f$a_2=e^{\Delta S}\f$ /// @tparam dimension The dimensionality of the triangulation /// @param move The type of move - /// @return \f$a_2=e^{-\Delta S}\f$ + /// @returns \f$a_2=e^{-\Delta S}\f$ template - auto CalculateA2(move_tracker::move_type move) const noexcept + auto CalculateA2(move_tracker::move_type const move) const noexcept { if (dimension == 3) { @@ -276,11 +278,11 @@ class MoveStrategy /// algorithm by generating a random number and comparing with the results /// of CalculateA1 and CalculateA2. /// @param move The type of move - /// @return True if the move is accepted - auto try_move(move_tracker::move_type move) -> bool + /// @returns True if the move is accepted + auto try_move(move_tracker::move_type const move) -> bool { // Record the proposed move - m_proposed_moves[move_tracker::as_integer(move)]++; + ++m_proposed_moves[as_integer(move)]; // Calculate probability auto a_1 = CalculateA1(move); @@ -294,21 +296,21 @@ class MoveStrategy #ifndef NDEBUG spdlog::debug("{} called.\n", __PRETTY_FUNCTION__); spdlog::trace("Trying move.\n"); - spdlog::trace("Move type = {}\n", move_tracker::as_integer(move)); + spdlog::trace("Move type = {}\n", as_integer(move)); spdlog::trace("Trial_value = {}\n", trial_value); spdlog::trace("A1 = {}\n", a_1); spdlog::trace("A2 = {}\n", a_2); spdlog::trace("A1*A2 = {}\n", a_1 * a_2); - spdlog::trace("{}\n", (trial_value <= a_1 * a_2) ? "Move accepted." - : "Move rejected."); + spdlog::trace("{}\n", trial_value <= a_1 * a_2 ? "Move accepted." + : "Move rejected."); #endif // Accept the move - m_accepted_moves[move_tracker::as_integer(move)]++; + ++m_accepted_moves[as_integer(move)]; return true; } // Reject the move - m_rejected_moves[move_tracker::as_integer(move)]++; + ++m_rejected_moves[as_integer(move)]; return false; } // try_move() @@ -318,7 +320,7 @@ class MoveStrategy /// making a move of each type, so that when A1 is calculated /// we don't have divide by zero /// @param t_manifold Manifold on which to operate - /// @return A manifold with a move of each type completed + /// @returns A manifold with a move of each type completed [[nodiscard]] auto initialize(ManifoldType t_manifold) -> std::optional> try @@ -335,8 +337,8 @@ class MoveStrategy spdlog::trace("Making move {} ...\n", move); #endif command.enqueue(move_tracker::as_move(move)); - m_proposed_moves[move]++; - m_accepted_moves[move]++; + ++m_proposed_moves[move]; + ++m_accepted_moves[move]; } // Execute the initial moves @@ -362,7 +364,7 @@ class MoveStrategy catch (std::system_error const& SystemError) { spdlog::debug("Metropolis initialization failed with {} ... exiting.\n", - SystemError.code()); + SystemError.what()); spdlog::trace("{}\n", SystemError.code().message()); return std::nullopt; } @@ -373,7 +375,7 @@ class MoveStrategy /// operator conducts all of the algorithmic work for Metropolis-Hastings on /// the manifold. /// @param t_manifold Manifold on which to operate - /// @return The manifold upon which the passes have been completed + /// @returns The manifold upon which the passes have been completed auto operator()(ManifoldType const& t_manifold) -> ManifoldType { #ifndef NDEBUG @@ -398,9 +400,11 @@ class MoveStrategy ++move_attempt) { // Pick a move to attempt - auto move = move_tracker::generate_random_move_3(); - if (try_move(move)) { command.enqueue(move); } + if (auto move = move_tracker::generate_random_move_3(); try_move(move)) + { + command.enqueue(move); + } } // Ends loop through CurrentTotalSimplices // Do the moves @@ -412,15 +416,13 @@ class MoveStrategy this->m_failed_moves += command.get_failed(); // Do stuff on checkpoint - if ((pass_number % m_checkpoint) == 0) + if (pass_number % m_checkpoint == 0) { fmt::print("=== Pass {} ===\n", pass_number); fmt::print("Writing to file.\n"); print_results(); auto result = command.get_results(); - utilities::write_file( - result, topology_type::SPHERICAL, ManifoldType::dimension, - result.N3(), result.max_time(), INITIAL_RADIUS, FOLIATION_SPACING); + utilities::write_file(result); } } // Ends loop through m_passes @@ -491,9 +493,11 @@ class MoveStrategy m_failed_moves.four_four_moves()); } } // print_results -}; // Metropolis +}; // Metropolis -using Metropolis3 = MoveStrategy; -using Metropolis4 = MoveStrategy; +using Metropolis_3 = + MoveStrategy; +using Metropolis_4 = + MoveStrategy; #endif // INCLUDE_METROPOLIS_HPP_ diff --git a/include/Move_always.hpp b/include/Move_always.hpp index 9955b99afd..bddeb22388 100644 --- a/include/Move_always.hpp +++ b/include/Move_always.hpp @@ -14,10 +14,10 @@ #ifndef INCLUDE_MOVE_ALWAYS_HPP_ #define INCLUDE_MOVE_ALWAYS_HPP_ +#include "Move_command.hpp" #include "Move_strategy.hpp" /// @brief The Move Always algorithm -/// @tparam dimension The dimensionality of the algorithm's triangulation template class MoveStrategy // NOLINT { @@ -50,24 +50,24 @@ class MoveStrategy // NOLINT /// @brief Constructor for MoveAlways /// @param t_number_of_passes Number of passes to run /// @param t_checkpoint Number of passes per checkpoint - [[maybe_unused]] MoveStrategy(Int_precision t_number_of_passes, - Int_precision t_checkpoint) + [[maybe_unused]] MoveStrategy(Int_precision const t_number_of_passes, + Int_precision const t_checkpoint) : m_passes{t_number_of_passes}, m_checkpoint{t_checkpoint} {} - /// @return The number of passes made on a triangulation + /// @returns The number of passes made on a triangulation [[nodiscard]] auto passes() const { return m_passes; } - /// @return The number of passes per checkpoint + /// @returns The number of passes per checkpoint [[nodiscard]] auto checkpoint() const { return m_checkpoint; } - /// @return The MoveTracker of attempted moves + /// @returns The MoveTracker of attempted moves auto get_attempted() const { return m_attempted_moves; } - /// @return The MoveTracker of successful moves + /// @returns The MoveTracker of successful moves auto get_succeeded() const { return m_successful_moves; } - /// @return The array of failed moves + /// @returns The array of failed moves auto get_failed() const { return m_failed_moves; } /// @brief Call operator @@ -163,7 +163,9 @@ class MoveStrategy // NOLINT } }; -using MoveAlways3 = MoveStrategy; -using MoveAlways4 = MoveStrategy; +using MoveAlways_3 = + MoveStrategy; +using MoveAlways_4 = + MoveStrategy; #endif // INCLUDE_MOVE_ALWAYS_HPP_ diff --git a/include/Move_command.hpp b/include/Move_command.hpp index 32af0b7b54..beca603c64 100644 --- a/include/Move_command.hpp +++ b/include/Move_command.hpp @@ -15,72 +15,100 @@ #include "Ergodic_moves_3.hpp" template , + typename ExpectedType = std::expected, typename FunctionType = tl::function_ref> class MoveCommand { using Queue = std::deque; using Counter = move_tracker::MoveTracker; - /// @brief The Manifold on which to make the move + /** + * \brief The manifold on which to perform moves + */ ManifoldType m_manifold; - /// @brief The queue of moves to make + /** + * \brief The queue of moves to perform + */ Queue m_moves; - /// @brief The queue of moves to retry + /** + * \brief The queue of moves to retry + */ Queue m_moves_to_retry; - /// @brief Track attempted moves + /** + * \brief The counter of attempted moves + */ Counter m_attempted; - /// @brief Track successful moves + /** + * \brief The counter of successful moves + */ Counter m_succeeded; - /// @brief Track failed moves + /** + * \brief The counter of failed moves + */ Counter m_failed; public: - /// @brief No default ctor + /** + * \brief Remove default ctor + */ MoveCommand() = delete; - /// @brief MoveCommand ctor - /// @details The manifold to be moved should be copied by value into the - /// MoveCommand to ensure moves are done atomically and either succeed - /// or fail and can be discarded without affecting the original. - /// @param t_manifold The manifold to perform moves upon + /** + * \brief MoveCommand ctor + * \param t_manifold The manifold to perform moves on + * \details The manifold to perform moves upon should be copied by value into + * the MoveCommand to ensure moves are executed atomically and either succeed + * or fail and can be discarded without affecting the original manifold. + */ explicit MoveCommand(ManifoldType t_manifold) : m_manifold{std::move(t_manifold)} {} - /// @return A read-only reference to the manifold + /** + * \brief A read-only reference to the manifold + */ auto get_const_results() const -> ManifoldType const& { return std::cref(m_manifold); } // get_const_results - /// @return The results of the moves invoked by MoveCommand + /** + * \brief Results of the moves invoked by MoveCommand + */ [[nodiscard]] auto get_results() -> ManifoldType& { return m_manifold; } - /// @return Attempted moves by MoveCommand + /** + * \brief Attempted moves by MoveCommand + */ [[nodiscard]] auto get_attempted() const -> Counter const& { return m_attempted; } // get_attempts - /// @return Successful moves by MoveCommand + /** + * \brief Successful moves by MoveCommand + */ [[nodiscard]] auto get_succeeded() const { return m_succeeded; } // get_succeeded - /// @return Failed moves by MoveCommand + /** + * \brief Failed moves by MoveCommand + */ [[nodiscard]] auto get_failed() const -> Counter const& { return m_failed; } // get_errors - /// @brief Reset the counters + /** + * \brief Reset counters + */ void reset_counters() { m_attempted.reset(); @@ -88,13 +116,23 @@ class MoveCommand m_failed.reset(); } - /// @brief Push a Pachner move onto the move queue - /// @param t_move The move function object to do on the manifold - void enqueue(move_tracker::move_type t_move) { m_moves.push_front(t_move); } + /** + * \brief Push a Pachner move onto the move queue + * \param t_move The move to add + */ + void enqueue(move_tracker::move_type const t_move) + { + m_moves.push_front(t_move); + } + /** + * \brief The number of moves on the queue + */ auto size() const { return m_moves.size(); } - /// Execute all moves in the queue on the manifold + /** + * \brief Execute all moves in the queue on the manifold + */ void execute() { #ifndef NDEBUG @@ -107,7 +145,7 @@ class MoveCommand { auto move_type = m_moves.back(); // Record attempted move - m_attempted[move_tracker::as_integer(move_type)]++; + ++m_attempted[as_integer(move_type)]; // Convert move_type to function auto move_function = as_move_function(move_type); // Execute move @@ -115,13 +153,13 @@ class MoveCommand { result->update(); swap(result.value(), m_manifold); - m_succeeded[move_tracker::as_integer(move_type)]++; + ++m_succeeded[as_integer(move_type)]; } else { fmt::print("{}\n", result.error()); // Track failed moves - m_failed[move_tracker::as_integer(move_type)]++; + ++m_failed[as_integer(move_type)]; m_moves_to_retry.push_front(move_type); } // Remove move from queue @@ -136,7 +174,13 @@ class MoveCommand #endif } // execute - auto as_move_function(move_tracker::move_type move) -> FunctionType + /** + * \brief Execute a move function on a manifold + * \param move The move to execute + * \return The move function to execute + */ + static auto as_move_function(move_tracker::move_type const move) + -> FunctionType { switch (move) { @@ -148,7 +192,9 @@ class MoveCommand } } // move_function - /// @brief Print attempted moves + /** + * \brief Print attempted moves + */ void print_attempts() const { if (ManifoldType::dimension == 3) @@ -179,7 +225,9 @@ class MoveCommand } } - /// @brief Print successful moves + /** + * \brief Print successful moves + */ void print_successful() const { if (ManifoldType::dimension == 3) @@ -213,7 +261,9 @@ class MoveCommand } } - /// @brief Print Move errors + /** + * \brief Print move errors + */ void print_errors() const { if (std::all_of(m_failed.moves_view().begin(), m_failed.moves_view().end(), diff --git a/include/Move_strategy.hpp b/include/Move_strategy.hpp index 44be768429..9706b013a3 100644 --- a/include/Move_strategy.hpp +++ b/include/Move_strategy.hpp @@ -13,18 +13,20 @@ #ifndef INCLUDE_MOVE_ALGORITHM_HPP_ #define INCLUDE_MOVE_ALGORITHM_HPP_ -#include "Move_command.hpp" - -/// @brief The algorithms available to make ergodic moves +/** + * \brief The algorithms available to make ergodic moves on triangulations + */ enum class Strategies { MOVE_ALWAYS, METROPOLIS }; -/// @brief Select an algorithm to make ergodic moves upon triangulations -/// @tparam strategies The algorithm that chooses ergodic moves -/// @tparam dimension The dimensionality of the triangulation +/** + * \brief Select a move algorithm + * \tparam strategies The move algorithm to use + * \tparam ManifoldType The manifold to perform moves on + */ template class MoveStrategy {}; diff --git a/include/Move_tracker.hpp b/include/Move_tracker.hpp index 8427090a51..c8616e2119 100644 --- a/include/Move_tracker.hpp +++ b/include/Move_tracker.hpp @@ -11,33 +11,43 @@ #ifndef CDT_PLUSPLUS_MOVE_TRACKER_HPP #define CDT_PLUSPLUS_MOVE_TRACKER_HPP -#include "Manifold.hpp" +#include +#include +#include +#include + +#include "Settings.hpp" +#include "Utilities.hpp" namespace move_tracker { static inline Int_precision constexpr NUMBER_OF_3D_MOVES = 5; static inline Int_precision constexpr NUMBER_OF_4D_MOVES = 7; - enum class move_type - { - TWO_THREE = 0, - THREE_TWO = 1, - TWO_SIX = 2, - SIX_TWO = 3, - FOUR_FOUR = 4 - }; - - /// @brief Convert enumeration to underlying integer - /// @details Used to convert move_type to integer + /** + * \brief The types of 3D ergodic moves + */ + enum class [[nodiscard("This contains data!")]] move_type{ + TWO_THREE = 0, THREE_TWO = 1, TWO_SIX = 2, SIX_TWO = 3, FOUR_FOUR = 4}; + + /** + * \brief Convert enum to integer + * \tparam Enumeration The enum type + * \param value The enum + * \return The integer value of the enum + */ template - auto as_integer(Enumeration value) -> - typename std::underlying_type_t + auto as_integer(Enumeration value) -> std::underlying_type_t { - return static_cast>(value); + return static_cast>(value); } // as_integer - // @brief Convert an integer to move_type - inline auto as_move(int move_choice) -> move_type + /** + * \brief Convert integer to move_type + * \param move_choice The move choice integer + * \return The move_type + */ + inline auto as_move(int const move_choice) -> move_type { if (move_choice == 0) { return move_type::TWO_THREE; } if (move_choice == 1) { return move_type::THREE_TWO; } @@ -46,7 +56,10 @@ namespace move_tracker return move_type::FOUR_FOUR; } // as_move - /// @brief Generate random ergodic move + /** + * \brief Generate random 3D ergodic move + * \return The move_type to be performed + */ [[nodiscard]] inline auto generate_random_move_3() -> move_type { auto move_choice = utilities::generate_random_int(0, 4); @@ -56,18 +69,22 @@ namespace move_tracker return as_move(move_choice); } // generate_random_move_3 - /// @brief Determine ergodic moves for a given dimension at compile-time - /// @param dim Dimensionality of the triangulation - /// @return The number of ergodic moves for that dimensionality - constexpr auto moves_per_dimension(Int_precision dim) -> Int_precision + /** + * \brief Determine the ergodic moves for a given dimensionality + * \param dim Dimensionality of the manifold + * \return The number of ergodic moves for that dimensionality + */ + auto constexpr moves_per_dimension(Int_precision const dim) -> Int_precision { if (dim == 3) { return NUMBER_OF_3D_MOVES; } if (dim == 4) { return NUMBER_OF_4D_MOVES; } return 0; // Error condition - } // moves_per_dimension + } // moves_per_dimension - /// @brief The data and methods to track ergodic moves - /// @tparam ManifoldType The type of manifold on which moves are made + /** + * \brief The data and methods to track ergodic moves + * \tparam ManifoldType The type of manifold on which moves are made + */ template class MoveTracker { @@ -77,23 +94,37 @@ namespace move_tracker Container moves = {0}; // NOLINT public: - /// @return Read-only container of moves + /** + * \brief Get a view of the moves + * \return Read-only container of moves + */ auto moves_view() const { return std::span(moves); } - /// @param index The index of the element to be accessed - /// @return The number of moves at the index - auto operator[](gsl::index index) -> auto& { return gsl::at(moves, index); } + /** + * \brief The [] operator for MoveTracker + * \param index The index of the element to be accessed + * \return The number of moves at the index + */ + auto operator[](gsl::index index) -> auto& + { + return gsl::at(moves, index); + } // operator[] - /// @param move The move_type to be accessed - /// @return The number of moves of that move_type - auto operator[](move_type move) const -> auto& + /** + * \brief The [] operator for MoveTracker + * \param move The move type to be accessed + * \return The number of moves of that type + */ + auto operator[](move_type const move) const -> auto& { return gsl::at(moves, as_integer(move)); } // operator[] - /// @param rhs The Move_tracker to add - /// @return The sum of the individual elements of the left and right - /// Move_trackers + /** + * \brief The += operator for MoveTracker + * \param rhs The MoveTracker to be added + * \return The sum of the individual elements of the MoveTrackers + */ auto operator+=(MoveTracker const& rhs) { for (std::size_t i = 0; i < moves.size(); ++i) @@ -103,45 +134,81 @@ namespace move_tracker return *this; } // operator+= - /// @return The total moves in the MoveTracker + /** + * \brief Total moves + * \return The total number of moves in the MoveTracker + */ auto total() const noexcept { return std::accumulate(moves.begin(), moves.end(), 0); } // total - /// @return Size of container of moves + /** + * \brief Container size + * \return The size of the container of moves + */ auto size() const noexcept { return moves.size(); } // 3D - /// @brief Write access to (2,3) moves + /** + * \brief Write access to (2,3) moves + * \return Reference to number of (2,3) moves + */ auto two_three_moves() -> auto& { return gsl::at(moves, 0); } - /// @brief Read-only access to (2,3) moves + /** + * \brief Read access to (2,3) moves + * \return Value of number of (2,3) moves + */ auto two_three_moves() const { return gsl::at(moves, 0); } - /// @brief Writeable access to (3,2) moves + /** + * \brief Write access to (3,2) moves + * \return Reference to number of (3,2) moves + */ auto three_two_moves() -> auto& { return gsl::at(moves, 1); } - /// @brief Read-only access to (3,2) moves + /** + * \brief Read access to (3,2) moves + * \return Value of number of (3,2) moves + */ auto three_two_moves() const { return gsl::at(moves, 1); } - /// @brief Write access to (2,6) moves + /** + * \brief Write access to (2,6) moves + * \return Reference to number of (2,6) moves + */ auto two_six_moves() -> auto& { return gsl::at(moves, 2); } - /// @brief Read-only access to (2,6) moves + /** + * \brief Read access to (2,6) moves + * \return Value of number of (2,6) moves + */ auto two_six_moves() const { return gsl::at(moves, 2); } - /// @brief Write access to (6,2) moves + /** + * \brief Write access to (6,2) moves + * \return Reference to number of (6,2) moves + */ auto six_two_moves() -> auto& { return gsl::at(moves, 3); } - /// @brief Read access to (6,2) moves + /** + * \brief Read access to (6,2) moves + * \return Value of number of (6,2) moves + */ auto six_two_moves() const { return gsl::at(moves, 3); } - /// @brief Write access to (4,4) moves + /** + * \brief Write access to (4,4) moves + * \return Reference to number of (4,4) moves + */ auto four_four_moves() -> auto& { return gsl::at(moves, 4); } - /// @brief Read access to (4,4) moves + /** + * \brief Read access to (4,4) moves + * \return Value of number of (4,4) moves + */ auto four_four_moves() const { return gsl::at(moves, 4); } // 4D diff --git a/include/Periodic_3_complex.hpp b/include/Periodic_3_complex.hpp index 7e7dd3eb42..098bd80e09 100644 --- a/include/Periodic_3_complex.hpp +++ b/include/Periodic_3_complex.hpp @@ -1,6 +1,6 @@ /// Causal Dynamical Triangulations in C++ using CGAL /// -/// Copyright © 2013-2017 Adam Getchell +/// Copyright © 2013 Adam Getchell /// /// Periodic (toroidal) simplicial complexes diff --git a/include/Periodic_3_triangulations.hpp b/include/Periodic_3_triangulations.hpp index 24a1badcdb..d03797f328 100644 --- a/include/Periodic_3_triangulations.hpp +++ b/include/Periodic_3_triangulations.hpp @@ -1,6 +1,6 @@ /// Causal Dynamical Triangulations in C++ using CGAL /// -/// Copyright © 2013-2017 Adam Getchell +/// Copyright © 2014 Adam Getchell /// /// Periodic (toroidal) 3D triangulations @@ -54,7 +54,7 @@ void make_random_T3_triangulation(T* T3, int simplices, int timeslices) noexcept int points_per_timeslice = simplices_per_timeslice / 6; /// We're working on 2 dimensional random points with the z component /// fixed by the timeslice - const int dim = 2; + int const dim = 2; std::vector v; v.reserve(points); diff --git a/include/S3Action.hpp b/include/S3Action.hpp index ab1b034c93..19e465ccc4 100644 --- a/include/S3Action.hpp +++ b/include/S3Action.hpp @@ -21,6 +21,9 @@ #include +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcomment" + /// @brief Calculates S3 bulk action for \f$\alpha\f$=-1. /// /// This result is i* the action for Euclidean dynamically triangulated @@ -38,12 +41,13 @@ /// @param K \f$k=\frac{1}{8\pi G_{Newton}}\f$ /// @param Lambda \f$\lambda=k*\Lambda\f$ where \f$\Lambda\f$ is the /// Cosmological constant -/// @return \f$S^{(3)}(\alpha=-1)\f$ as a +/// @returns \f$S^{(3)}(\alpha=-1)\f$ as a /// Gmpzf /// value [[nodiscard]] inline auto S3_bulk_action_alpha_minus_one( - Int_precision N1_TL, Int_precision N3_31_13, Int_precision N3_22, - long double K, long double Lambda) noexcept -> Gmpzf + Int_precision const N1_TL, Int_precision const N3_31_13, + Int_precision const N3_22, long double const K, + long double const Lambda) noexcept -> Gmpzf { // Set precision for initialization and assignment functions mpfr_set_default_prec(PRECISION); @@ -134,12 +138,13 @@ /// @param K \f$k=\frac{1}{8\pi G_{Newton}}\f$ /// @param Lambda \f$\lambda=k*\Lambda\f$ where \f$\Lambda\f$ is the /// Cosmological constant -/// @return \f$S^{(3)}(\alpha=1)\f$ as a +/// @returns \f$S^{(3)}(\alpha=1)\f$ as a /// Gmpzf /// value [[nodiscard]] inline auto S3_bulk_action_alpha_one( - Int_precision N1_TL, Int_precision N3_31_13, Int_precision N3_22, - long double K, long double Lambda) noexcept -> Gmpzf + Int_precision const N1_TL, Int_precision const N3_31_13, + Int_precision const N3_22, long double const K, + long double const Lambda) noexcept -> Gmpzf { // Set precision for initialization and assignment functions mpfr_set_default_prec(PRECISION); @@ -241,14 +246,13 @@ /// @param K \f$k=\frac{1}{8\pi G_{Newton}}\f$ /// @param Lambda \f$\lambda=k*\Lambda\f$ where \f$\Lambda\f$ is the /// Cosmological constant -/// @return \f$S^{(3)}(\alpha)\f$ as a +/// @returns \f$S^{(3)}(\alpha)\f$ as a /// Gmpzf /// value -[[nodiscard]] inline auto S3_bulk_action(Int_precision N1_TL, - Int_precision N3_31_13, - Int_precision N3_22, long double Alpha, - long double K, - long double Lambda) noexcept -> Gmpzf +[[nodiscard]] inline auto S3_bulk_action( + Int_precision const N1_TL, Int_precision const N3_31_13, + Int_precision const N3_22, long double const Alpha, long double const K, + long double const Lambda) noexcept -> Gmpzf { // Set precision for initialization and assignment functions mpfr_set_default_prec(PRECISION); @@ -427,4 +431,6 @@ return result; } // Gmpzf S3_bulk_action() +#pragma GCC diagnostic pop + #endif // INCLUDE_S3ACTION_HPP_ diff --git a/include/Settings.hpp b/include/Settings.hpp index 8559fef3b4..9fe1339931 100644 --- a/include/Settings.hpp +++ b/include/Settings.hpp @@ -12,7 +12,7 @@ #ifndef INCLUDE_SETTINGS_HPP_ #define INCLUDE_SETTINGS_HPP_ -#include +#include #include @@ -35,28 +35,29 @@ using Int_precision = std::int_fast32_t; #define __PRETTY_FUNCTION__ __FUNCSIG__ #endif -//#define CGAL_LINKED_WITH_TBB +// #define CGAL_LINKED_WITH_TBB /// Correctly declare global constants /// See Jonathan Boccara's C++ Pitfalls, January 2021 /// Sets the precision for MPFR. -static inline Int_precision constexpr PRECISION = 256; +static inline Int_precision constexpr PRECISION = 256; /// Default foliated triangulation spacings -static inline double constexpr INITIAL_RADIUS = 1.0; -static inline double constexpr FOLIATION_SPACING = 1.0; +static inline double constexpr INITIAL_RADIUS = 1.0; +static inline double constexpr FOLIATION_SPACING = 1.0; /// Sets epsilon values for floating point comparisons -static inline double constexpr TOLERANCE = 0.01; +static inline double constexpr TOLERANCE = 0.01; /// Depends on INITIAL_RADIUS and RADIAL_FACTOR -static inline Int_precision constexpr GV_BOUNDING_BOX_SIZE = 100; +[[maybe_unused]] static inline Int_precision constexpr GV_BOUNDING_BOX_SIZE = + 100; /// Aligns data for ease of access on 64-bit CPUs at the expense of padding -static inline int constexpr ALIGNMENT_64_BIT = 64; +static inline int constexpr ALIGNMENT_64_BIT = 64; /// Except when we only need 32 bits -static inline int constexpr ALIGNMENT_32_BIT = 32; +static inline int constexpr ALIGNMENT_32_BIT = 32; #endif // INCLUDE_SETTINGS_HPP_ diff --git a/include/Torus_d.hpp b/include/Torus_d.hpp index c80fad31b4..a7356ed3a8 100644 --- a/include/Torus_d.hpp +++ b/include/Torus_d.hpp @@ -1,7 +1,7 @@ /******************************************************************************* Causal Dynamical Triangulations in C++ using CGAL - Copyright © 2014 Adam Getchell + Copyright © 2017 Adam Getchell ******************************************************************************/ /// @file Torus_d.hpp @@ -24,23 +24,25 @@ using Kd = CGAL::Cartesian_d; using Point = Kd::Point_d; using Creator_d = CGAL::Creator_uniform_d::iterator, Point>; -/// @brief Make a d-dimensional toroid of points -/// @param t_points The type (dimensionality) of points -/// @param t_number_of_points The number of points -/// @param t_dimension Dimensionality of the toroid -/// @return A d-dimensional toroid +/** + * \brief Make a d-dimensional torus + * \param t_points The container of points + * \param t_number_of_points The number of points to insert + * \param t_dimension The dimensionality of the torus + * \return A d-dimensional torus + */ inline auto make_d_cube(std::vector t_points, std::size_t t_number_of_points, int t_dimension) { - double const size = 1.0; + double constexpr size = 1.0; fmt::print("Generating {} grid points in {}D\n", t_number_of_points, t_dimension); t_points.reserve(t_number_of_points); - return CGAL::points_on_cube_grid_d(t_dimension, size, t_number_of_points, - std::back_inserter(t_points), - Creator_d(t_dimension)); + return points_on_cube_grid_d(t_dimension, size, t_number_of_points, + std::back_inserter(t_points), + Creator_d(t_dimension)); } #endif // INCLUDE_TORUS_D_HPP_ diff --git a/include/Triangulation_traits.hpp b/include/Triangulation_traits.hpp index f9aa209554..cf174a4f4e 100644 --- a/include/Triangulation_traits.hpp +++ b/include/Triangulation_traits.hpp @@ -34,8 +34,7 @@ struct TriangulationTraits<3> using Tds = CGAL::Triangulation_data_structure_3; #else - using Tds = CGAL::Triangulation_data_structure_3; + using Tds = CGAL::Triangulation_data_structure_3; #endif using Delaunay = CGAL::Delaunay_triangulation_3; @@ -50,12 +49,12 @@ struct TriangulationTraits<3> /// @brief CGAL::squared_distance /// See /// https://doc.cgal.org/latest/Kernel_23/group__squared__distance__grp.html#ga1ff73525660a052564d33fbdd61a4f71 - /// @return Square of Euclidean distance between two geometric objects + /// @returns Square of Euclidean distance between two geometric objects using squared_distance = Kernel::Compute_squared_distance_3; using Spherical_points_generator = CGAL::Random_points_on_sphere_3; - static constexpr int origin = 0; -}; + static inline Point const ORIGIN_POINT = Point{0, 0, 0}; +}; // TriangulationTraits<3> #endif // CDT_PLUSPLUS_TRIANGULATION_TRAITS_HPP diff --git a/include/Utilities.hpp b/include/Utilities.hpp index de62f17912..a65dc13593 100644 --- a/include/Utilities.hpp +++ b/include/Utilities.hpp @@ -1,7 +1,7 @@ /******************************************************************************* Causal Dynamical Triangulations in C++ using CGAL - Copyright © 2013 Adam Getchell + Copyright © 2017 Adam Getchell ******************************************************************************/ /// @file Utilities.hpp @@ -11,29 +11,24 @@ #ifndef INCLUDE_UTILITIES_HPP_ #define INCLUDE_UTILITIES_HPP_ -#include - -#include -#include -#include #include #include #include -#include #include #include #include #include #include -#include // H. Hinnant date and time library #include +/// clang-15 does not support std::format +// #include + // M. O'Neill Permutation Congruential Generator library #include "pcg_random.hpp" // V. Zverovich {fmt} library -#include #include // G. Melman spdlog library @@ -53,9 +48,9 @@ enum class topology_type /// @brief Convert topology_type to string output /// @param t_os The output stream /// @param t_topology The topology -/// @return An output string of the topology -inline auto operator<<(std::ostream& t_os, topology_type const& t_topology) - -> std::ostream& +/// @returns An output string of the topology +inline auto operator<<(std::ostream& t_os, + topology_type const& t_topology) -> std::ostream& { switch (t_topology) { @@ -72,14 +67,19 @@ namespace utilities /// Use Howard Hinnant C++11/14 data and time library and Time Zone Database /// Parser. std::chrono::zoned_time would be a replacement if supported by /// current compilers. - /// @return A formatted string with the system local time + /// @returns A formatted string with the system local time /// @see https://github.com/HowardHinnant/date /// @see https://en.cppreference.com/w/cpp/chrono/zoned_time [[nodiscard]] inline auto current_date_time() { - using namespace std::chrono; - date::zoned_time const time(date::current_zone(), system_clock::now()); - return date::format("%Y-%m-%d.%X%Z", time); + /// When AppleClang fully supports std::chrono and std::format, use this: + // auto time = std::chrono::zoned_time(std::chrono::current_zone(), + // std::chrono::system_clock::now()); + // return std::formatter::format(time, "{:%Y-%m-%d.%X%Z}"); + date::zoned_time const time(date::current_zone(), + std::chrono::system_clock::now()); + return format("%Y-%m-%d.%X%Z", time); } // current_date_time /// @brief Generate useful filenames @@ -89,11 +89,12 @@ namespace utilities /// @param t_number_of_timeslices The number of time foliations /// @param t_initial_radius The radius of the first foliation t=1 /// @param t_foliation_spacing The spacing between foliations - /// @return A filename - [[nodiscard]] inline auto generate_filename( + /// @returns A filename + [[nodiscard]] inline auto make_filename( topology_type const& t_topology, Int_precision t_dimension, Int_precision t_number_of_simplices, Int_precision t_number_of_timeslices, - double t_initial_radius, double t_foliation_spacing) noexcept + double t_initial_radius, + double t_foliation_spacing) noexcept -> std::filesystem::path { std::string filename; if (t_topology == topology_type::SPHERICAL) { filename += "S"; } @@ -124,7 +125,16 @@ namespace utilities // Append .off file extension filename += ".off"; return filename; - } // generate_filename + } // make_filename + + template + [[nodiscard]] auto make_filename(ManifoldType const& manifold) + { + return make_filename(ManifoldType::topology, ManifoldType::dimension, + manifold.N3(), manifold.max_time(), + manifold.initial_radius(), + manifold.foliation_spacing()); + } // make_filename /// @brief Print triangulation statistics /// @tparam TriangulationType The triangulation type @@ -142,50 +152,66 @@ namespace utilities t_triangulation.number_of_finite_cells()); } // print_delaunay - /// @brief Writes the runtime results to a file - /// - /// This function writes the Delaunay triangulation in the manifold to an OFF - /// file. http://www.geomview.org/docs/html/OFF.html#OFF The filename is - /// generated by the **generate_filename()** function. Provides strong - /// exception-safety. - /// - /// @tparam ManifoldType The manifold type - /// @param t_universe A simplicial manifold - /// @param t_topology The topology type from the scoped enum topology_type - /// @param t_dimension The dimensionality of the triangulation - /// @param t_number_of_simplices The number of simplices in the triangulation - /// @param t_number_of_timeslices The number of foliated timeslices - /// @param t_initial_radius The radius of the first foliation t=1 - /// @param t_foliation_spacing The spacing between foliations - template - void write_file(ManifoldType const& t_universe, - topology_type const& t_topology, Int_precision t_dimension, - Int_precision t_number_of_simplices, - Int_precision t_number_of_timeslices, double t_initial_radius, - double t_foliation_spacing) + /// @brief Write triangulation to file + /// @details This function writes the Delaunay triangulation in the manifold + /// to an OFF file. http://www.geomview.org/docs/html/OFF.html#OFF Provides + /// strong exception-safety. + /// @tparam TriangulationType The type of triangulation + /// @param filename The filename to write to + /// @param triangulation The triangulation to write + template + void write_file(std::filesystem::path const& filename, + TriangulationType triangulation) { - // mutex to protect file access across threads static std::mutex mutex; - - std::string filename; - filename.assign(generate_filename( - t_topology, t_dimension, t_number_of_simplices, t_number_of_timeslices, - t_initial_radius, t_foliation_spacing)); - fmt::print("Writing to file {}\n", filename); - + fmt::print("Writing to file {}\n", filename.string()); std::scoped_lock const lock(mutex); - std::ofstream file(filename, std::ios::out); if (!file.is_open()) { throw std::filesystem::filesystem_error( - "Unable to open file.", + "Could not open file for writing", filename, std::make_error_code(std::errc::bad_file_descriptor)); } + file << triangulation; + } // write_file - file << t_universe.get_triangulation().get_delaunay(); + /// @brief Write the runtime results to a file + /// @details The filename is generated by the **make_filename()** and + /// writen using another **write_file()** function, which is currently + /// implemented using the << operator for triangulations. + /// @tparam ManifoldType The manifold type + /// @param t_universe The simplicial manifold + template + void write_file(ManifoldType const& t_universe) + { + std::filesystem::path filename; + filename.assign(make_filename(t_universe)); + write_file(filename, t_universe.get_delaunay()); } // write_file + /// @brief Read triangulation from file + /// @tparam TriangulationType The type of triangulation + /// @param filename The file to read from + /// @returns A Delaunay triangulation + template + auto read_file(std::filesystem::path const& filename) -> TriangulationType + { + static std::mutex mutex; + fmt::print("Reading from file {}\n", filename.string()); + std::scoped_lock const lock(mutex); + std::ifstream file(filename, std::ios::in); + if (!file.is_open()) + { + throw std::filesystem::filesystem_error( + "Could not open file for reading", filename, + std::make_error_code(std::errc::bad_file_descriptor)); + } + TriangulationType triangulation; + file >> triangulation; + return triangulation; + } // read_file + /// @brief Roll a die with PCG [[nodiscard]] inline auto die_roll() noexcept { @@ -211,7 +237,8 @@ namespace utilities /// @tparam Distribution The distribution type, usually uniform /// @param t_min_value The minimum value /// @param t_max_value The maximum value - /// @return A random value in the distribution between min_value and max_value + /// @returns A random value in the distribution between min_value and + /// max_value template [[nodiscard]] auto generate_random(NumberType t_min_value, NumberType t_max_value) noexcept @@ -224,7 +251,7 @@ namespace utilities } // generate_random() /// @brief Make a high-quality random number generator usable by std::shuffle - /// @return A RNG + /// @returns A RNG inline auto make_random_generator() noexcept { pcg_extras::seed_seq_from seed_source; @@ -281,11 +308,10 @@ namespace utilities /// @param t_dimension Number of dimensions /// @param t_number_of_simplices Number of desired simplices /// @param t_number_of_timeslices Number of desired timeslices - /// @param t_output_flag Toggles output - /// @return The number of points per timeslice to obtain + /// @returns The number of points per timeslice to obtain /// the desired number of simplices - [[nodiscard]] inline auto expected_points_per_timeslice( - Int_precision t_dimension, Int_precision t_number_of_simplices, + inline auto expected_points_per_timeslice( + Int_precision const t_dimension, Int_precision t_number_of_simplices, Int_precision t_number_of_timeslices) { #ifndef NDEBUG @@ -335,7 +361,7 @@ namespace utilities /// this function can be expanded. /// /// @param t_value An exact Gmpzf multiple-precision floating point number - /// @return The double conversion + /// @returns The double conversion [[nodiscard]] inline auto Gmpzf_to_double(Gmpzf const& t_value) -> double { return t_value.to_double(); @@ -368,20 +394,21 @@ namespace utilities inline void create_logger() try { - auto console_sink = std::make_shared(); + auto const console_sink = + std::make_shared(); console_sink->set_level(spdlog::level::info); - auto debug_sink = std::make_shared( + auto const debug_sink = std::make_shared( "logs/debug-log.txt", true); debug_sink->set_level(spdlog::level::debug); - auto trace_sink = std::make_shared( + auto const trace_sink = std::make_shared( "logs/trace-log.txt", true); trace_sink->set_level(spdlog::level::trace); spdlog::sinks_init_list sink_list = {console_sink, debug_sink, trace_sink}; - auto logger = std::make_shared( + auto const logger = std::make_shared( "multi_sink", sink_list.begin(), sink_list.end()); // This allows the logger to capture all events logger->set_level(spdlog::level::trace); @@ -393,8 +420,8 @@ namespace utilities "You must build in Debug mode for anything to be recorded in this " "file.\n"); - spdlog::register_logger(logger); - spdlog::set_default_logger(logger); + register_logger(logger); + set_default_logger(logger); } catch (spdlog::spdlog_ex const& ex) { @@ -403,5 +430,27 @@ namespace utilities spdlog::warn("Default logger set.\n"); } // create_logger + + /// @brief Covert a CGAL point to a string + /// @tparam Point The type of point (e.g. 3D, 4D) + /// @param t_point The point + /// @returns A string representation of the point + template + auto point_to_str(Point const& t_point) -> std::string + { + std::stringstream stream; + stream << t_point; + return stream.str(); + } // point_to_str + + /// @brief Convert a topology to a string using it's << operator + /// @param t_topology The topology_type to convert + /// @returns A string representation of the topology_type + inline auto topology_to_str(topology_type const& t_topology) -> std::string + { + std::stringstream stream; + stream << t_topology; + return stream.str(); + } // topology_to_str } // namespace utilities #endif // INCLUDE_UTILITIES_HPP_ diff --git a/scripts/asan.sh b/scripts/asan.sh index 3e6f5e5124..df24e50649 100755 --- a/scripts/asan.sh +++ b/scripts/asan.sh @@ -11,4 +11,4 @@ pwd cd build/src || exit ./initialize --s -n32000 -t11 -o cd .. || exit -ctest --output-on-failure -j2 \ No newline at end of file +ctest --rerun-failed --output-on-failure -j2 \ No newline at end of file diff --git a/scripts/build.bat b/scripts/build.bat index 0126902282..9b4504d96a 100644 --- a/scripts/build.bat +++ b/scripts/build.bat @@ -13,7 +13,7 @@ set VCPKG_PATH=%HOMEPATH%\Projects\vcpkg set PATH=%PATH%;%VCPKG_PATH% :: Change to your version of Visual Studio -cmake -G "Visual Studio 16 2019" -A x64 -D CMAKE_BUILD_TYPE=Debug -D ENABLE_TESTING:BOOL=TRUE -D ENABLE_CACHE:BOOL=FALSE -D CMAKE_TOOLCHAIN_FILE=%VCPKG_PATH%/scripts/buildsystems/vcpkg.cmake -S . -B build +cmake -G "Visual Studio 16 2019" -A x64 -D CMAKE_BUILD_TYPE=RelWithDebInfo -D ENABLE_TESTING:BOOL=TRUE -D ENABLE_CACHE:BOOL=FALSE -D CMAKE_TOOLCHAIN_FILE=%VCPKG_PATH%/scripts/buildsystems/vcpkg.cmake -S . -B build cmake --build build :: Executables are in \build\src\Debug diff --git a/scripts/build.sh b/scripts/build.sh index 36ee49c10f..6bc6af067f 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -5,7 +5,7 @@ cd .. rm -rf build/ -cmake -G Ninja -D CMAKE_BUILD_TYPE=RelWithDebInfo -D ENABLE_TESTING:BOOL=TRUE -D ENABLE_IPO=ON -S . -B build +cmake --preset build cmake --build build cd build || exit ctest --output-on-failure -j2 \ No newline at end of file diff --git a/scripts/clang-tidy.sh b/scripts/clang-tidy.sh index 8762025ddb..bb1c7ce850 100755 --- a/scripts/clang-tidy.sh +++ b/scripts/clang-tidy.sh @@ -6,4 +6,7 @@ cd .. rm -rf build/ cmake -S . -B build -G Ninja -D ENABLE_CLANG_TIDY=ON +# Make blank .clang-tidy into build directory to tell clang-tidy to ignore the Qt files which cause a lot of warnings +touch build/.clang-tidy +#echo "Checks: '-*'" >> build/.clang-tidy cmake --build build diff --git a/scripts/debug.sh b/scripts/debug.sh index 4b88aa3054..f8b8ee558c 100755 --- a/scripts/debug.sh +++ b/scripts/debug.sh @@ -5,5 +5,7 @@ cd .. rm -rf build/ -cmake -G Ninja -D CMAKE_BUILD_TYPE=RelWithDebInfo -D ENABLE_TESTING:BOOL=TRUE -S . -B build +cmake --preset debug cmake --build build +cd build || exit +ctest --output-on-failure -j2 diff --git a/scripts/fast-build.sh b/scripts/fast-build.sh index 6ddbd8be52..a7e864a4b9 100755 --- a/scripts/fast-build.sh +++ b/scripts/fast-build.sh @@ -5,7 +5,7 @@ cd .. rm -rf build/ -cmake -G Ninja -D CMAKE_BUILD_TYPE=Release -D ENABLE_TESTING:BOOL=FALSE -D ENABLE_IPO=ON -S . -B build +cmake --preset fast-build cmake --build build cd build || exit ctest --output-on-failure -j2 \ No newline at end of file diff --git a/scripts/lsan.sh b/scripts/lsan.sh index fedace6007..3c8951ed74 100755 --- a/scripts/lsan.sh +++ b/scripts/lsan.sh @@ -11,4 +11,4 @@ pwd cd build/src || exit ./initialize --s -n32000 -t11 -o cd .. || exit -ctest --output-on-failure -j2 \ No newline at end of file +ctest --rerun-failed --output-on-failure -j2 \ No newline at end of file diff --git a/scripts/msan.sh b/scripts/msan.sh index 31a5f6636c..cfb8d896a9 100755 --- a/scripts/msan.sh +++ b/scripts/msan.sh @@ -11,4 +11,4 @@ pwd cd build/src || exit ./initialize --s -n32000 -t11 -o cd .. || exit -ctest --output-on-failure -j2 \ No newline at end of file +ctest --rerun-failed --output-on-failure -j2 \ No newline at end of file diff --git a/scripts/pvs-studio.sh b/scripts/pvs-studio.sh index 23e7229ba6..17b6806445 100755 --- a/scripts/pvs-studio.sh +++ b/scripts/pvs-studio.sh @@ -9,10 +9,10 @@ cd .. rm -rf build/ -cmake -S . -B build -G Ninja -D CMAKE_BUILD_TYPE=Debug +cmake --preset debug cmake --build build cd build || exit -pvs-studio-analyzer analyze -o pvsreport.log -j8 +pvs-studio-analyzer analyze -o pvsreport.log -e ../vcpkg_installed -j8 # Filter warning 521 pvs-studio-analyzer suppress -v521 pvsreport.log pvs-studio-analyzer filter-suppressed pvsreport.log diff --git a/scripts/slurm.sh b/scripts/slurm.sh index 11e3c3ad5f..f97cc4c95e 100755 --- a/scripts/slurm.sh +++ b/scripts/slurm.sh @@ -1,13 +1,17 @@ #!/bin/bash -l # For use on Slurm # sbatch -p high -t 60 slurm.sh -module load cmake/3.18.0 -module load spack/gcc/10.3.0 -module load spack/autoconf-archive/2019.01.06 +# Or, interactively, +# srun -p med2 -t 1-00 --mem=100G --ntasks 12 --pty /bin/bash -il +module load cmake/3.28.1 +module load gcc/13.2.0 +module load autoconf-archive/2022.02.11 cd .. rm -rf build/ -cmake -G Ninja -D CMAKE_BUILD_TYPE=RelWithDebInfo -S . -B build +cmake -D CMAKE_BUILD_TYPE=RelWithDebInfo -D ENABLE_TESTING:BOOL=TRUE -S . -B build cmake --build build +cd build || exit +ctest --output-on-failure -j2 #mkdir "$HOME"/data/"$(date +%Y-%m-%d.%R%Z)" #cp cdt-opt "$HOME"/data/"$(date +%Y-%m-%d.%R%Z)"/ #cd "$HOME"/data/"$(date +%Y-%m-%d.%R%Z)" || exit diff --git a/scripts/tsan.sh b/scripts/tsan.sh index 9461511d24..d970a4ca81 100755 --- a/scripts/tsan.sh +++ b/scripts/tsan.sh @@ -11,4 +11,4 @@ pwd cd build/src || exit ./initialize --s -n32000 -t11 -o cd .. || exit -ctest --output-on-failure -j2 \ No newline at end of file +ctest --rerun-failed --output-on-failure -j2 \ No newline at end of file diff --git a/sonar-project.properties b/sonar-project.properties index bad13d4e95..9fdb36345e 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -15,8 +15,8 @@ sonar.organization=acgetchell-github sonar.sources=src,include sonar.tests=tests sonar.cfamily.build-wrapper-output=build_wrapper_output_directory -sonar.cfamily.cache.enabled=true -sonar.cfamily.cache.path=.sonar +#sonar.cfamily.cache.enabled=true +#sonar.cfamily.cache.path=.sonar sonar.cfamily.threads=2 sonar.cfamily.gcov.reportsPath=build/gcov-reports sonar.python.version=2.7, 3.9 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e5de318e02..22ef11b73a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,126 +1,111 @@ add_executable(initialize ${PROJECT_SOURCE_DIR}/src/initialize.cpp) - -# On macOS and Linux docopt builds an intermediate object, thus different targets than Windows See -# https://github.com/microsoft/vcpkg/issues/8666 -if(WIN32) - target_link_libraries( - initialize - PRIVATE project_options - project_warnings - date::date-tz - docopt - fmt::fmt-header-only - Eigen3::Eigen - spdlog::spdlog_header_only - TBB::tbb - CGAL::CGAL) -else() - target_link_libraries( - initialize - PRIVATE project_options - project_warnings - date::date-tz - docopt_s - fmt::fmt-header-only - Eigen3::Eigen - spdlog::spdlog_header_only - TBB::tbb - CGAL::CGAL) -endif() -target_compile_features(initialize PRIVATE cxx_std_20) +target_link_libraries( + initialize + PRIVATE project_options + project_warnings + date::date-tz + Boost::program_options + fmt::fmt-header-only + Eigen3::Eigen + spdlog::spdlog_header_only + TBB::tbb + CGAL::CGAL) +target_compile_features(initialize PRIVATE cxx_std_23) add_executable(cdt-opt ${PROJECT_SOURCE_DIR}/src/cdt-opt.cpp) -if(WIN32) - target_link_libraries( - cdt-opt - PRIVATE project_options - project_warnings - date::date-tz - docopt - fmt::fmt-header-only - Eigen3::Eigen - spdlog::spdlog_header_only - TBB::tbb - CGAL::CGAL) -else() - target_link_libraries( - cdt-opt - PRIVATE project_options - project_warnings - date::date-tz - docopt_s - fmt::fmt-header-only - Eigen3::Eigen - spdlog::spdlog_header_only - TBB::tbb - CGAL::CGAL) -endif() -target_compile_features(cdt-opt PRIVATE cxx_std_20) +target_link_libraries( + cdt-opt + PRIVATE project_options + project_warnings + date::date-tz + fmt::fmt-header-only + Eigen3::Eigen + spdlog::spdlog_header_only + TBB::tbb + CGAL::CGAL) +target_compile_features(cdt-opt PRIVATE cxx_std_23) add_executable(cdt ${PROJECT_SOURCE_DIR}/src/cdt.cpp) -if(WIN32) - target_link_libraries( - cdt - PRIVATE project_options - project_warnings - date::date-tz - docopt - fmt::fmt-header-only - Eigen3::Eigen - spdlog::spdlog_header_only - TBB::tbb - CGAL::CGAL) -else() - target_link_libraries( - cdt - PRIVATE project_options - project_warnings - date::date-tz - docopt_s - fmt::fmt-header-only - Eigen3::Eigen - spdlog::spdlog_header_only - TBB::tbb - CGAL::CGAL) -endif() -target_compile_features(cdt PRIVATE cxx_std_20) +target_link_libraries( + cdt + PRIVATE project_options + project_warnings + date::date-tz + Boost::program_options + fmt::fmt-header-only + Eigen3::Eigen + spdlog::spdlog_header_only + TBB::tbb + CGAL::CGAL) +target_compile_features(cdt PRIVATE cxx_std_23) + +# Build cdt-viewer locally, but not in CI since QT takes more than an hour to build there +#if(APPLE AND NOT ($ENV{CI})) +# add_executable(cdt-viewer ${PROJECT_SOURCE_DIR}/src/cdt-viewer.cpp) +# target_link_libraries( +# cdt-viewer +# PRIVATE project_options +# project_warnings +# date::date-tz +# Boost::program_options +# fmt::fmt-header-only +# Eigen3::Eigen +# spdlog::spdlog_header_only +# TBB::tbb +# CGAL::CGAL_Basic_viewer) +# target_compile_features(cdt-viewer PRIVATE cxx_std_23) +#endif() -add_executable(bistellar ${PROJECT_SOURCE_DIR}/src/bistellar.cpp) -target_link_libraries(bistellar PRIVATE project_options project_warnings fmt::fmt-header-only CGAL::CGAL) -target_compile_features(bistellar PRIVATE cxx_std_20) +# Build bistellar-flip locally, but not in CI since QT takes more than an hour to build there +#if(APPLE AND NOT ($ENV{CI})) +# add_executable(bistellar-flip ${PROJECT_SOURCE_DIR}/src/bistellar-flip.cpp) +# target_link_libraries( +# bistellar-flip +# PRIVATE project_options +# project_warnings +# date::date-tz +# fmt::fmt-header-only +# Eigen3::Eigen +# spdlog::spdlog_header_only +# TBB::tbb) +# CGAL::CGAL_Basic_viewer) +# target_compile_features(bistellar-flip PRIVATE cxx_std_23) +#endif() # # Tests ## # -add_test(NAME cdt COMMAND $ --s -n64 -t3 -a0.6 -k1.1 -l0.1 -p10) -set_tests_properties(cdt PROPERTIES PASS_REGULAR_EXPRESSION "Writing to file S3-3-") +add_test(NAME cdt COMMAND $ -s -n127 -t4 -a0.6 -k1.1 -l0.1 -p10) +set_tests_properties(cdt PROPERTIES PASS_REGULAR_EXPRESSION "Writing to file S3-4-") -add_test(NAME cdt-triangle-inequalities COMMAND $ --s -n64 -t3 -a0.4 -k1.1 -l0.1) +add_test(NAME cdt-triangle-inequalities COMMAND $ -s -n64 -t3 -a0.4 -k1.1 -l0.1) set_tests_properties(cdt-triangle-inequalities PROPERTIES PASS_REGULAR_EXPRESSION "Triangle inequalities violated") -add_test(NAME cdt-dimensionality COMMAND $ --s -n64 -t3 -d4 -a0.4 -k1.1 -l0.1) +add_test(NAME cdt-dimensionality COMMAND $ -s -n64 -t3 -d4 -a0.4 -k1.1 -l0.1) set_tests_properties(cdt-dimensionality PROPERTIES PASS_REGULAR_EXPRESSION "Currently, dimensions cannot be >3.") -add_test(NAME cdt-toroidal COMMAND $ --t -n64 -t3 -a0.6 -k1.1 -l0.1) +add_test(NAME cdt-toroidal COMMAND $ -e -n64 -t3 -a0.6 -k1.1 -l0.1) set_tests_properties(cdt-toroidal PROPERTIES PASS_REGULAR_EXPRESSION "Toroidal triangulations not yet supported.") -add_test(NAME initialize COMMAND $ --s -n640 -t4 -o) +add_test(NAME initialize COMMAND $ -s -n640 -t4 -o) set_tests_properties(initialize PROPERTIES PASS_REGULAR_EXPRESSION "Writing to file S3-4") -add_test(NAME initialize-minimum-simplices COMMAND $ --s -n1 -t1 -o) +add_test(NAME initialize-minimum-simplices COMMAND $ -s -n1 -t1 -o) set_tests_properties(initialize-minimum-simplices PROPERTIES PASS_REGULAR_EXPRESSION "Simplices and timeslices should be greater or equal to 2.") -add_test(NAME initialize-dimensionality COMMAND $ --s -n64 -t3 -d4 -o) +add_test(NAME initialize-dimensionality COMMAND $ -s -n64 -t3 -d4 -o) set_tests_properties(initialize-dimensionality PROPERTIES PASS_REGULAR_EXPRESSION "Currently, dimensions cannot be >3.") -add_test(NAME initialize-toroidal COMMAND $ --t -n64 -t3 -o) +add_test(NAME initialize-toroidal COMMAND $ -e -n64 -t3 -o) set_tests_properties(initialize-toroidal PROPERTIES PASS_REGULAR_EXPRESSION "Toroidal triangulations not yet supported.") add_test(NAME cdt-opt COMMAND $) set_tests_properties(cdt-opt PROPERTIES PASS_REGULAR_EXPRESSION "cdt-opt started at") -add_test(NAME bistellar COMMAND $) -set_tests_properties(bistellar PROPERTIES WILL_FAIL FALSE) +#if(APPLE AND NOT ($ENV{CI})) +# add_test(NAME cdt-viewer COMMAND $ --dry-run test.off) +# set_tests_properties(cdt-viewer PROPERTIES PASS_REGULAR_EXPRESSION "Dry run. Exiting.") +#endif() diff --git a/src/bistellar-flip.cpp b/src/bistellar-flip.cpp new file mode 100644 index 0000000000..bb246094d5 --- /dev/null +++ b/src/bistellar-flip.cpp @@ -0,0 +1,131 @@ +/******************************************************************************* +Causal Dynamical Triangulations in C++ using CGAL +Copyright © 2023 Adam Getchell +******************************************************************************/ + +/// @file bistellar-flip.cpp +/// @brief Tests and views bistellar flips +/// @details This is a unit test of the bistellar flip algorithm. +/// The reason it is not in /tests is because it uses CGAL::draw() to +/// display the results, which requires Qt5. Qt5 is extremely heavy +/// and not needed for the other unit tests, so I don't want to require/link +/// it to the test binary. +/// @author Adam Getchell + +#ifdef NDEBUG +#define DOCTEST_CONFIG_DISABLE +#include +#endif + +#define DOCTEST_CONFIG_IMPLEMENT + +#include +#include + +#include + +#include "Ergodic_moves_3.hpp" + +static inline std::floating_point auto constexpr SQRT_2 = + std::numbers::sqrt2_v; +static inline std::floating_point auto constexpr INV_SQRT_2 = 1 / SQRT_2; + +auto bistellar_triangulation_vertices() -> std::vector> +{ + std::vector vertices{ + Point_t<3>{ 0, 0, 0}, + Point_t<3>{ INV_SQRT_2, 0, INV_SQRT_2}, + Point_t<3>{ 0, INV_SQRT_2, INV_SQRT_2}, + Point_t<3>{-INV_SQRT_2, 0, INV_SQRT_2}, + Point_t<3>{ 0, -INV_SQRT_2, INV_SQRT_2}, + Point_t<3>{ 0, 0, 2} + }; + return vertices; +} + +auto main(int const argc, char* argv[]) -> int +try +{ + // Doctest integration into code + doctest::Context context; + context.setOption("no-breaks", + true); // don't break in debugger when assertions fail + context.applyCommandLine(argc, argv); + + int const res = context.run(); // run tests unless --no-run is specified + if (context.shouldExit()) + { // important - query flags (and --exit) rely on the user doing this + return res; // propagate the result of the tests + } + + context.clearFilters(); // important - otherwise the context filters will be + // used during the next evaluation of RUN_ALL_TESTS, + // which will lead to wrong results + +#ifdef NDEBUG + fmt::print("Before bistellar flip.\n"); + auto vertices = bistellar_triangulation_vertices(); + ergodic_moves::Delaunay const dt{vertices.begin(), vertices.end()}; + manifolds::Manifold_3 const manifold{ + foliated_triangulations::FoliatedTriangulation_3{dt, 0, 1} + }; +#ifdef ENABLE_VISUALIZATION + CGAL::draw(manifold.get_delaunay()); +#endif + fmt::print("After bistellar flip.\n"); + manifold.print_cells(); + utilities::print_delaunay(dt); +#endif +} +catch (std::exception const& e) +{ + spdlog::critical("Error: {}\n", e.what()); + return EXIT_FAILURE; +} + +catch (...) +{ + spdlog::critical("Something went wrong ... Exiting.\n"); + return EXIT_FAILURE; +} + +SCENARIO("Perform bistellar flip on Delaunay triangulation" * + doctest::test_suite("bistellar")) +{ + GIVEN("A triangulation setup for a bistellar flip") + { + auto vertices = bistellar_triangulation_vertices(); + ergodic_moves::Delaunay triangulation(vertices.begin(), vertices.end()); + WHEN("We have a valid triangulation") + { + CHECK(triangulation.is_valid()); + THEN("We can perform a bistellar flip") + { + // Obtain top and bottom vertices by re-inserting, which returns the + // Vertex_handle + auto top = triangulation.insert(Point_t<3>{0, 0, 2}); + auto bottom = triangulation.insert(Point_t<3>{0, 0, 0}); + auto edges = foliated_triangulations::collect_edges<3>(triangulation); + auto pivot_edge = ergodic_moves::find_pivot_edge(triangulation, edges); + REQUIRE_MESSAGE(pivot_edge, "No pivot edge found."); + + // Check this didn't actually change vertices in the triangulation + REQUIRE_EQ(vertices.size(), 6); + + if (pivot_edge) + { + auto flipped_triangulation = ergodic_moves::bistellar_flip( + triangulation, pivot_edge.value(), top, bottom); + + REQUIRE_MESSAGE(flipped_triangulation, "Bistellar flip failed."); + if (flipped_triangulation) + { + /// FIXME: This fails because the triangulation is not valid after + /// the flip neighbor of c has not c as neighbor + WARN(flipped_triangulation->is_valid()); + } + } + } + } + } +} \ No newline at end of file diff --git a/src/bistellar.cpp b/src/bistellar.cpp deleted file mode 100644 index ab1e9123a6..0000000000 --- a/src/bistellar.cpp +++ /dev/null @@ -1,231 +0,0 @@ -/// @file bistellar.cpp -/// @brief Example bistellar fip -/// @author Adam Getchell -/// @details Show how to use the bistellar_flip functions on a 3D triangulation. -/// Some convenience functions are defined here because the internal -/// functions of the Triangulation_3 class are not currently accessible to -/// the bistellar_flip functions. -/// @date Created: 2021-04-21 - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "Ergodic_moves_3.hpp" -#include "Foliated_triangulation.hpp" - -using K = CGAL::Exact_predicates_inexact_constructions_kernel; -using Vb = CGAL::Triangulation_vertex_base_with_info_3; -using Cb = CGAL::Triangulation_cell_base_with_info_3; -using Tds = CGAL::Triangulation_data_structure_3; -using Delaunay = CGAL::Delaunay_triangulation_3; -using Cell_handle = Delaunay::Cell_handle; -using Edge_handle = CGAL::Triple; -using Vertex_handle = Delaunay::Vertex_handle; -using Vertex = Tds::Vertex; -using Point = Delaunay::Point; -using Cell_container = std::vector; -using Edge_container = std::vector; -using Vertex_container = std::vector; - -static inline double const INV_SQRT_2 = 1 / sqrt(2); - -/// @return A container of all finite cells in the triangulation. -[[nodiscard]] auto get_cells(Delaunay const& triangulation) -> Cell_container -{ - Cell_container cells; - for (auto cit = triangulation.finite_cells_begin(); - cit != triangulation.finite_cells_end(); ++cit) - { - // Each cell handle is valid - assert(triangulation.tds().is_cell(cit)); - cells.push_back(cit); - } - return cells; -} // get_cells - -/// @return A container of all finite edges in the triangulation -[[nodiscard]] auto get_edges(Delaunay const& triangulation) -> Edge_container -{ - Edge_container edges; - for (auto eit = triangulation.finite_edges_begin(); - eit != triangulation.finite_edges_end(); ++eit) - { - Cell_handle cell = eit->first; - Edge_handle edge{cell, cell->index(cell->vertex(eit->second)), - cell->index(cell->vertex(eit->third))}; - // Each edge is valid in the triangulation - assert(triangulation.tds().is_valid(edge.first, edge.second, edge.third)); - edges.emplace_back(edge); - } - return edges; -} // get_edges - -/// @return The center edge of all 4 cells -[[nodiscard]] auto find_pivot(Delaunay const& triangulation, - Edge_container const& edges) - -> std::optional -{ - for (auto const& edge : edges) - { - auto circulator = triangulation.incident_cells(edge, edge.first); - Cell_container incident_cells; - do { - // filter out boundary edges with incident infinite cells - if (!triangulation.is_infinite(circulator)) - { - incident_cells.emplace_back(circulator); - } - } - while (++circulator != edge.first); - fmt::print("Edge has {} incident finite cells\n", incident_cells.size()); - if (incident_cells.size() == 4) { return edge; } - } - return std::nullopt; -} // find_pivot - -/// @return The vertices of the new edge of the bistellar flip -[[nodiscard]] auto find_new_pivot(Cell_container const& cells, - Edge_handle const& pivot_edge, - Vertex_handle const& v_top, - Vertex_handle const& v_bottom) - -> Vertex_container -{ - // Get vertices from cells - auto cell_vertices = - foliated_triangulations::get_vertices_from_cells<3>(cells); - Vertex_container new_pivot_vertices; - std::copy_if( - cell_vertices.begin(), cell_vertices.end(), - std::back_inserter(new_pivot_vertices), [&](auto const& vertex) { - return (vertex != pivot_edge.first->vertex(pivot_edge.second) && - vertex != pivot_edge.first->vertex(pivot_edge.third) && - vertex != v_top && vertex != v_bottom); - }); - assert(new_pivot_vertices.size() == 2); - return new_pivot_vertices; -} // find_new_pivot - -/// @brief Build a Delaunay triangulation and test a bistellar flip -auto main() -> int -try -{ - // Create a Delaunay triangulation - std::vector> vertices{ - Point_t<3>{ 0, 0, 0}, - Point_t<3>{ INV_SQRT_2, 0, INV_SQRT_2}, - Point_t<3>{ 0, INV_SQRT_2, INV_SQRT_2}, - Point_t<3>{-INV_SQRT_2, 0, INV_SQRT_2}, - Point_t<3>{ 0, -INV_SQRT_2, INV_SQRT_2}, - Point_t<3>{ 0, 0, 2} - }; - Delaunay dt(vertices.begin(), vertices.end()); - - fmt::print("Before bistellar flip:\n"); - fmt::print("dt.dimension(): {}\n", dt.dimension()); - fmt::print("dt.number_of_vertices(): {}\n", dt.number_of_vertices()); - fmt::print("dt.number_of_finite_cells(): {}\n", dt.number_of_finite_cells()); - fmt::print("dt.number_of_finite_facets(): {}\n", - dt.number_of_finite_facets()); - fmt::print("dt.number_of_finite_edges(): {}\n", dt.number_of_finite_edges()); - fmt::print("dt.is_valid(): {}\n", dt.is_valid()); - - // Get the cells - auto cells = get_cells(dt); - assert(cells.size() == dt.number_of_finite_cells()); - foliated_triangulations::print_cells<3>(cells); - - // Get the edges - auto edges = get_edges(dt); - assert(edges.size() == dt.number_of_finite_edges()); - - // Get top and bottom vertices - auto vh_top = - foliated_triangulations::find_vertex<3>(dt, Point_t<3>{0, 0, 2}).value(); - auto vh_bottom = - foliated_triangulations::find_vertex<3>(dt, Point_t<3>{0, 0, 0}).value(); - - // Flip the pivot - if (auto pivot = find_pivot(dt, edges); pivot) - { - fmt::print("Flipping the pivot\n"); - foliated_triangulations::print_edge<3>(pivot.value()); - auto new_pivot = find_new_pivot(cells, pivot.value(), vh_top, vh_bottom); - fmt::print("The new edge will be from ({}) -> ({})\n", - new_pivot[0]->point(), new_pivot[1]->point()); - - // Calculate the cells that will be flipped - auto b_1 = foliated_triangulations::find_cell<3>( - dt, vh_top, pivot->first->vertex(pivot->second), - pivot->first->vertex(pivot->third), new_pivot[0]) - .value(); - auto b_2 = foliated_triangulations::find_cell<3>( - dt, vh_top, pivot->first->vertex(pivot->second), - pivot->first->vertex(pivot->third), new_pivot[1]) - .value(); - auto b_3 = foliated_triangulations::find_cell<3>( - dt, vh_bottom, pivot->first->vertex(pivot->second), - pivot->first->vertex(pivot->third), new_pivot[0]) - .value(); - auto b_4 = foliated_triangulations::find_cell<3>( - dt, vh_bottom, pivot->first->vertex(pivot->second), - pivot->first->vertex(pivot->third), new_pivot[1]) - .value(); - - // Flip the cells - ergodic_moves::bistellar_flip_arguments arguments{ - .triangulation = dt, - .before_flip_cell_1 = b_1, - .before_flip_cell_2 = b_2, - .before_flip_cell_3 = b_3, - .before_flip_cell_4 = b_4, - .pivot_from_vertex_1 = pivot->first->vertex(pivot->second), - .pivot_from_vertex_2 = pivot->first->vertex(pivot->third), - .pivot_to_vertex_1 = new_pivot[0], - .pivot_to_vertex_2 = new_pivot[1], - .top_vertex = vh_top, - .bottom_vertex = vh_bottom}; - auto result = ergodic_moves::bistellar_flip_really(arguments); - - if (result) - { - fmt::print("Flipped the cells\n"); - dt = result.value(); - fmt::print("After bistellar flip.\n"); - fmt::print("dt.dimension(): {}\n", dt.dimension()); - fmt::print("dt.number_of_vertices(): {}\n", dt.number_of_vertices()); - fmt::print("dt.number_of_finite_cells(): {}\n", - dt.number_of_finite_cells()); - fmt::print("dt.number_of_finite_facets(): {}\n", - dt.number_of_finite_facets()); - fmt::print("dt.number_of_finite_edges(): {}\n", - dt.number_of_finite_edges()); - fmt::print("dt.is_valid(): {}\n", dt.is_valid()); - assert(dt.is_valid()); - auto new_cells = foliated_triangulations::get_all_finite_cells<3>(dt); - foliated_triangulations::print_cells<3>(new_cells); - return EXIT_SUCCESS; - } - - fmt::print("Failed to flip the cells\n"); - return EXIT_FAILURE; - } - - fmt::print("No pivot found\n"); - return EXIT_FAILURE; -} -catch (std::exception const& e) -{ - fmt::print("Error: {}\n", e.what()); - return EXIT_FAILURE; -} diff --git a/src/cdt-opt.cpp b/src/cdt-opt.cpp index 1646827398..c46f45f646 100644 --- a/src/cdt-opt.cpp +++ b/src/cdt-opt.cpp @@ -20,28 +20,28 @@ using namespace std; -auto main() -> int // NOLINT +auto main() -> int try { fmt::print("cdt-opt started at {}\n", utilities::current_date_time()); - constexpr Int_precision simplices = 64; - constexpr Int_precision timeslices = 3; + Int_precision constexpr simplices = 256; + Int_precision constexpr timeslices = 4; /// @brief Constants in units of \f$c=G=\hbar=1 \alpha\approx 0.0397887\f$ - constexpr auto alpha = static_cast(0.6); - constexpr auto k = static_cast(1.1); + auto constexpr alpha = static_cast(0.6); + auto constexpr k = static_cast(1.1); // NOLINT /// @brief \f$\Lambda=2.036\times 10^{-35} s^{-2}\approx 0\f$ - constexpr auto lambda = static_cast(0.1); - constexpr Int_precision passes = 10; - constexpr Int_precision checkpoint = 10; + auto constexpr lambda = static_cast(0.1); + Int_precision constexpr passes = 10; + Int_precision constexpr checkpoint = 10; // Create logs utilities::create_logger(); // Initialize the Metropolis algorithm - Metropolis3 run(alpha, k, lambda, passes, checkpoint); + Metropolis_3 run(alpha, k, lambda, passes, checkpoint); // Make a triangulation - manifolds::Manifold3 universe(simplices, timeslices); + manifolds::Manifold_3 const universe(simplices, timeslices); // Look at triangulation universe.print(); @@ -49,7 +49,7 @@ try universe.print_volume_per_timeslice(); // Run algorithm on triangulation - auto result = run(universe); + auto const result = run(universe); if (auto max_timevalue = result.max_time(); max_timevalue < timeslices) { @@ -57,7 +57,7 @@ try max_timevalue); } - Ensures(result.is_valid()); + if (!result.is_valid()) { throw runtime_error("Result is invalid!\n"); } // Print results fmt::print("=== Run Results ===\n"); @@ -67,6 +67,11 @@ try return EXIT_SUCCESS; } +catch (runtime_error const& RuntimeError) +{ + spdlog::critical("{}\n", RuntimeError.what()); + return EXIT_FAILURE; +} catch (...) { spdlog::critical("Something went wrong ... Exiting.\n"); diff --git a/src/cdt-viewer.cpp b/src/cdt-viewer.cpp new file mode 100644 index 0000000000..f2a4984902 --- /dev/null +++ b/src/cdt-viewer.cpp @@ -0,0 +1,163 @@ +/******************************************************************************* +Causal Dynamical Triangulations in C++ using CGAL +Copyright © 2022 Adam Getchell +******************************************************************************/ + +/// @file cdt-viewer.cpp +/// @brief Views 3D spacetimes +/// @author Adam Getchell + +#ifdef NDEBUG +#define DOCTEST_CONFIG_DISABLE +#endif + +#include + +#define DOCTEST_CONFIG_IMPLEMENT + +#include +#include + +#include + +#include "Manifold.hpp" +#include "Utilities.hpp" + +namespace po = boost::program_options; + +static auto constexpr USAGE = + R"(Causal Dynamical Triangulations in C++ using CGAL. + +Copyright (c) 2022 Adam Getchell + +A program that views 3D triangulated spacetimes with a defined causal +structure. Specify the filename of the triangulation to view. + +Usage: + cdt-viewer -f FILENAME + +Options)"; + +auto main(int const argc, char* const argv[]) -> int +try +{ + // Doctest integration into code + doctest::Context context; + context.setOption("no-breaks", + true); // don't break in debugger when assertions fail + context.applyCommandLine(argc, argv); + + int const res = context.run(); // run tests unless --no-run is specified + if (context.shouldExit()) + { // important - query flags (and --exit) rely on the user doing this + return res; // propagate the result of the tests + } + + context.clearFilters(); // important - otherwise the context filters will be + // used during the next evaluation of RUN_ALL_TESTS, + // which will lead to wrong results + + std::string const intro{USAGE}; + // Parsed arguments + std::string filename; + + po::options_description description(intro); + description.add_options()("help,h", "Show this message")( + "version,v", "Show program version")("dry-run", + "Don't actually do anything")( + "filename,f", po::value(&filename), + "Filename of triangulation to view"); + + po::variables_map args; + po::store(po::parse_command_line(argc, argv, description), args); + po::notify(args); + + if (args.count("help")) + { + std::cout << description << "\n"; + return res + EXIT_SUCCESS; + } + + if (args.count("version")) + { + fmt::print("cdt-viewer 1.0\n"); + return res + EXIT_SUCCESS; + } + + if (args.count("dry-run")) + { + fmt::print("Dry run. Exiting.\n"); + return res + EXIT_SUCCESS; + } + + fmt::print("cdt-viewer started at {}\n", utilities::current_date_time()); + fmt::print("Reading triangulation from file {}\n", + std::string_view(filename)); + + // Read from file + auto const dt_in = utilities::read_file>(filename); + + // Draw triangulation + fmt::print("Drawing {}\n", filename); + draw(dt_in); + + return res + EXIT_SUCCESS; +} + +catch (std::exception const& e) +{ + spdlog::critical("Error: {}\n", e.what()); + return EXIT_FAILURE; +} + +catch (...) +{ + spdlog::critical("Something went wrong ... Exiting.\n"); + return EXIT_FAILURE; +} + +SCENARIO("Given a 3D Manifold, it can be written to file and read back in." * + doctest::test_suite("cdt-viewer")) +{ + GIVEN("A 3D Manifold.") + { + auto constexpr simplices = 640; + auto constexpr timeslices = 4; + manifolds::Manifold_3 const manifold(simplices, timeslices); + + WHEN("It is written to file.") + { + auto const filename = utilities::make_filename(manifold); + utilities::write_file(manifold); + + THEN("It can be read back in.") + { + auto dt_in = utilities::read_file>(filename); + REQUIRE(dt_in.is_valid(true)); + REQUIRE_EQ(dt_in.dimension(), manifold.dimensionality()); + REQUIRE_EQ(dt_in.number_of_finite_cells(), manifold.N3()); + REQUIRE_EQ(dt_in.number_of_finite_facets(), manifold.N2()); + REQUIRE_EQ(dt_in.number_of_finite_edges(), manifold.N1()); + REQUIRE_EQ(dt_in.number_of_vertices(), manifold.N0()); + } + THEN("It can be drawn.") + { + auto const dt_in = utilities::read_file>(filename); + CGAL::draw(dt_in); + // Cleanup test file + REQUIRE_NOTHROW(std::filesystem::remove(filename)); + } + } + } + GIVEN("A non-existent filename.") + { + WHEN("It is read back in.") + { + THEN("An exception is thrown.") + { + REQUIRE_THROWS_AS(utilities::read_file>("unused.off"), + std::filesystem::filesystem_error); + } + } + } +} diff --git a/src/cdt.cpp b/src/cdt.cpp index ecc2795234..f37f9d55d3 100644 --- a/src/cdt.cpp +++ b/src/cdt.cpp @@ -1,7 +1,7 @@ /******************************************************************************* Causal Dynamical Triangulations in C++ using CGAL - Copyright © 2014 Adam Getchell + Copyright © 2013 Adam Getchell ******************************************************************************/ /// @file cdt.cpp @@ -11,19 +11,20 @@ /// https://github.com/ucdavis/CDT. #include -#include +#include #include using Timer = CGAL::Real_timer; using namespace std; +namespace po = boost::program_options; /// Help message parsed by docopt into options -static constexpr string_view USAGE{ +static string_view constexpr USAGE{ R"(Causal Dynamical Triangulations in C++ using CGAL. -Copyright (c) 2014-2021 Adam Getchell +Copyright (c) 2013 Adam Getchell A program that generates d-dimensional triangulated spacetimes with a defined causal structure and evolves them according @@ -31,74 +32,114 @@ to the Metropolis algorithm. Specify the number of passes to control how much evolution is desired. Each pass attempts a number of ergodic moves equal to the number of simplices in the simulation. -Usage:./cdt (--spherical | --toroidal) -n SIMPLICES -t TIMESLICES [-d DIM] - [--init INITIAL] [--foliate FOLIATION] -k K --alpha ALPHA - --lambda LAMBDA [-p PASSES] [-c CHECKPOINT] +Usage:./cdt (--spherical | --toroidal) -n SIMPLICES -t TIMESLICES + [-d DIM] + [--init INITIAL RADIUS] + [--foliate FOLIATION SPACING] + -k K + --alpha ALPHA + --lambda LAMBDA + [-p PASSES] + [-c CHECKPOINT] + +Optional arguments are in square brackets. Examples: ./cdt --spherical -n 32000 -t 11 --alpha 0.6 -k 1.1 --lambda 0.1 --passes 1000 -./cdt --s -n32000 -t11 -a.6 -k1.1 -l.1 -p1000 - -Options: - -h --help Show this message - --version Show program version - -n SIMPLICES Approximate number of simplices - -t TIMESLICES Number of timeslices - -d DIM Dimensionality [default: 3] - -i --init INITIAL Initial radius [default: 1] - --foliate FOLIATION Foliation spacing between timeslices [default: 1] - -a --alpha ALPHA Negative squared geodesic length of 1-d - timelike edges - -k K K = 1/(8*pi*G_newton) - -l --lambda LAMBDA K * Cosmological constant - -p --passes PASSES Number of passes [default: 100] - -c --checkpoint CHECKPOINT Checkpoint every n passes [default: 10] -)"}; +./cdt -s -n32000 -t11 -a.6 -k1.1 -l.1 -p1000 + +Options)"}; /// @brief The main path of the CDT++ program /// @param argc Argument count = 1 + number of arguments /// @param argv Argument vector (array) to be passed to docopt /// @return Integer value 0 if successful, 1 on failure -auto main(int argc, char* const argv[]) -> int // NOLINT +auto main(int const argc, char* const argv[]) -> int try { - // Start running time - Timer timer; - timer.start(); - fmt::print("cdt started at {}\n", utilities::current_date_time()); + std::string const intro{USAGE}; + // Parsed arguments + topology_type topology; + long long simplices; + long long timeslices; + long long dimensions; + double initial_radius; + double foliation_spacing; + long double alpha; + long double k; + long double lambda; + long long passes; + long long checkpoint; + + po::options_description description(intro); + description.add_options()("help,h", "Show this message")( + "version,v", "Show program version")("spherical,s", "Spherical topology")( + "toroidal,e", "Toroidal topology")("simplices,n", + po::value(&simplices), + "Approximate number of simplices")( + "timeslices,t", po::value(×lices), + "Number of timeslices")( + "dimensions,d", po::value(&dimensions)->default_value(3), + "Dimensionality")("init,i", + po::value(&initial_radius)->default_value(1.0), + "Initial radius")( + "foliate,f", po::value(&foliation_spacing)->default_value(1.0), + "Foliation spacing")( + "alpha,a", po::value(&alpha), + "Negative squared geodesic length of 1-d timelike edges")( + "k,k", po::value(&k), "K = 1/(8*pi*G_newton)")( + "lambda,l", po::value(&lambda), "K * Cosmological constant")( + "passes,p", po::value(&passes)->default_value(100), + "Number of passes")("checkpoint,c", + po::value(&checkpoint)->default_value(10), + "Checkpoint every n passes"); + + po::variables_map args; + po::store(po::parse_command_line(argc, argv, description), args); + po::notify(args); - // docopt option parser - std::string usage_string{USAGE}; - std::map> args = - docopt::docopt(usage_string, {argv + 1, argv + argc}, - true, // print help message automatically - "CDT 0.1.8"); // Version + if (args.count("help")) + { + cout << description << "\n"; + return EXIT_SUCCESS; + } + + if (args.count("version")) + { + fmt::print("CDT++ version 0.1.8\n"); + return EXIT_SUCCESS; + } -#ifndef NDEBUG - for (auto const& [key, value] : args) + if (args.count("spherical")) { topology = topology_type::SPHERICAL; } + else if (args.count("toroidal")) { topology = topology_type::TOROIDAL; } + else { - fmt::print("Key: {} Value: {}\n", key, value); + fmt::print("Topology not specified.\n"); + return EXIT_FAILURE; + } + + if (args.count("simplices")) + { + simplices = args["simplices"].as(); + } + else + { + fmt::print("Number of simplices not specified.\n"); + return EXIT_FAILURE; + } + + if (args.count("timeslices")) + { + timeslices = args["timeslices"].as(); + } + else + { + fmt::print("Number of timeslices not specified.\n"); + return EXIT_FAILURE; } -#endif - - // Parse docopt::values in args map - auto simplices = stoll(args["-n"].asString()); - auto timeslices = stoll(args["-t"].asString()); - auto dimensions = stoll(args["-d"].asString()); - auto initial_radius = stod(args["--init"].asString()); - auto foliation_spacing = stod(args["--foliate"].asString()); - auto alpha = stold(args["--alpha"].asString()); - auto k = stold(args["-k"].asString()); - auto lambda = stold(args["--lambda"].asString()); - auto passes = stoll(args["--passes"].asString()); - auto checkpoint = stoll(args["--checkpoint"].asString()); - - // Topology of simulation - auto topology = (args["--spherical"].asBool()) ? topology_type::SPHERICAL - : topology_type::TOROIDAL; // Display job parameters - fmt::print("Topology is {}\n", topology); + fmt::print("Topology is {}\n", utilities::topology_to_str(topology)); fmt::print("Dimensionality: {}+{}\n", dimensions - 1, 1); fmt::print("Initial radius: {}\n", initial_radius); fmt::print("Foliation spacing: {}\n", foliation_spacing); @@ -111,6 +152,11 @@ try fmt::print("K: {}\n", k); fmt::print("Lambda: {}\n", lambda); + // Start running time + Timer timer; + timer.start(); + fmt::print("cdt started at {}\n", utilities::current_date_time()); + if (simplices < 2 || timeslices < 2) { timer.stop(); @@ -127,18 +173,18 @@ try } // Initialize the Metropolis algorithm - Metropolis3 run(alpha, k, lambda, static_cast(passes), - static_cast(checkpoint)); + Metropolis_3 run(alpha, k, lambda, static_cast(passes), + static_cast(checkpoint)); // Make a triangulation - manifolds::Manifold3 universe; + manifolds::Manifold_3 universe; switch (topology) { case topology_type::SPHERICAL: if (dimensions == 3) { - manifolds::Manifold3 populated_universe( + manifolds::Manifold_3 populated_universe( static_cast(simplices), static_cast(timeslices), initial_radius, foliation_spacing); @@ -154,7 +200,6 @@ try case topology_type::TOROIDAL: timer.stop(); // End running time counter throw invalid_argument("Toroidal triangulations not yet supported."); - default: throw domain_error("Simulation topology not parsed."); } // Look at triangulation @@ -163,7 +208,7 @@ try universe.print_volume_per_timeslice(); // The main work of the program - auto result = run(universe); + auto const result = run(universe); // Do we have enough timeslices? if (auto max_timevalue = result.max_time(); max_timevalue < timeslices) @@ -172,9 +217,9 @@ try max_timevalue); } - Ensures(result.is_valid()); + if (!result.is_valid()) { throw runtime_error("Result is invalid!\n"); } - // Output results + // Print results timer.stop(); // End running time counter fmt::print("=== Run Results ===\n"); fmt::print("Running time is {} seconds.\n", timer.time()); @@ -183,10 +228,7 @@ try result.print_volume_per_timeslice(); // Write results to file - // Strong exception-safety guarantee - utilities::write_file(result, topology, - static_cast(dimensions), result.N3(), - static_cast(timeslices), 1.0, 1.0); + utilities::write_file(result); return EXIT_SUCCESS; } @@ -208,6 +250,11 @@ catch (logic_error const& LogicError) spdlog::critical("Simulation startup failed ... Exiting.\n"); return EXIT_FAILURE; } +catch (runtime_error const& RuntimeError) +{ + spdlog::critical("{}\n", RuntimeError.what()); + return EXIT_FAILURE; +} catch (...) { spdlog::critical("Something went wrong ... Exiting.\n"); diff --git a/src/initialize.cpp b/src/initialize.cpp index 572e248ebe..0ac7b47c30 100644 --- a/src/initialize.cpp +++ b/src/initialize.cpp @@ -1,72 +1,124 @@ /******************************************************************************* Causal Dynamical Triangulations in C++ using CGAL - Copyright © 2014 Adam Getchell + Copyright © 2018 Adam Getchell ******************************************************************************/ /// @file initialize.cpp /// @brief Generates initial spacetimes /// @author Adam Getchell -#include +#include #include "Manifold.hpp" using namespace std; +namespace po = boost::program_options; -/// Help message parsed by docopt into options -static constexpr string_view USAGE{ +static string_view constexpr USAGE{ R"(Causal Dynamical Triangulations in C++ using CGAL. -Copyright (c) 2014-2021 Adam Getchell +Copyright (c) 2014 Adam Getchell A program that generates d-dimensional triangulated spacetimes with a defined causal structure. Specify the topology of the triangulation (spherical or toroidal), the desired number of simplices, and the -desired number of timeslices. Optionally, the spacetime dimension may -also be given. +desired number of timeslices. -Usage:./initialize (--spherical | --toroidal) -n SIMPLICES -t TIMESLICES [-d DIM] [-i INIT] [-f FOL] [-o] +Usage:./initialize (--spherical | --toroidal) -n SIMPLICES -t TIMESLICES + [-d DIM] + [--init INITIAL RADIUS] + [--foliate FOLIATION SPACING] + [--output] + +Optional arguments are in square brackets. Examples: -./initialize --spherical -n 32000 -t 11 --init 1 --foliate 1 -./initialize --s -n32000 -t11 - -Options: - -h --help Show this message - --version Show program version - -n SIMPLICES Approximate number of simplices - -t TIMESLICES Number of timeslices - -d DIM Dimensionality [default: 3] - -i --init INIT Initial radius [default: 1] - -f --foliate FOL Foliation spacing [default: 1] - -o --output Save triangulation into OFF file -)"}; - -auto main(int argc, char* const argv[]) -> int // NOLINT +./initialize --spherical --simplices 32000 --timeslices 11 --init 1.0 --foliate 1.0 --output +./initialize -s -n32000 -t11 -i1.0 -f1.0 -o + +Options)"}; + +auto main(int const argc, char* const argv[]) -> int try { - // docopt option parser - std::string usage_string{USAGE}; - std::map> args = docopt::docopt( - usage_string, {argv + 1, argv + argc}, true, "initializer 1.0"); - - auto simplices = stoll(args["-n"].asString()); - auto timeslices = stoll(args["-t"].asString()); - auto dimensions = stoll(args["-d"].asString()); - auto initial_radius = stod(args["--init"].asString()); - auto foliation_spacing = stod(args["--foliate"].asString()); - auto save_file = args["--output"].asBool(); + std::string const intro{USAGE}; + // Parsed arguments + topology_type topology; + long long simplices; + long long timeslices; + long long dimensions; + double initial_radius; + double foliation_spacing; + bool save_file; + + po::options_description description(intro); + description.add_options()("help,h", "Show this message")( + "version,v", "Show program version")("spherical,s", "Spherical topology")( + "toroidal,e", "Toroidal topology")("simplices,n", + po::value(&simplices), + "Approximate number of simplices")( + "timeslices,t", po::value(×lices), + "Number of timeslices")( + "dimensions,d", po::value(&dimensions)->default_value(3), + "Dimensionality")("init,i", + po::value(&initial_radius)->default_value(1.0), + "Initial radius")( + "foliate,f", po::value(&foliation_spacing)->default_value(1.0), + "Foliation spacing")("output,o", "Save triangulation into OFF file"); + + po::variables_map args; + po::store(po::parse_command_line(argc, argv, description), args); + po::notify(args); + + if (args.count("help")) + { + cout << description << "\n"; + return EXIT_SUCCESS; + } - // Initialize triangulation - manifolds::Manifold3 universe; + if (args.count("version")) + { + fmt::print("CDT initializer version 1.0\n"); + return EXIT_SUCCESS; + } + + if (args.count("spherical")) { topology = topology_type::SPHERICAL; } + else if (args.count("toroidal")) { topology = topology_type::TOROIDAL; } + else + { + fmt::print("Topology not specified.\n"); + return EXIT_FAILURE; + } + + if (args.count("simplices")) + { + simplices = args["simplices"].as(); + } + else + { + fmt::print("Number of simplices not specified.\n"); + return EXIT_FAILURE; + } - // Topology of simulation - auto topology = (args["--spherical"].asBool()) ? topology_type::SPHERICAL - : topology_type::TOROIDAL; + if (args.count("timeslices")) + { + timeslices = args["timeslices"].as(); + } + else + { + fmt::print("Number of timeslices not specified.\n"); + return EXIT_FAILURE; + } + + if (args.count("output")) { save_file = true; } + else { save_file = false; } + + // Initialize triangulation + manifolds::Manifold_3 universe; // Display job parameters - fmt::print("Topology is {}\n", topology); + fmt::print("Topology is {}\n", utilities::topology_to_str(topology)); fmt::print("Number of dimensions = {}\n", dimensions); fmt::print("Number of desired simplices = {}\n", simplices); fmt::print("Number of desired timeslices = {}\n", timeslices); @@ -87,7 +139,7 @@ try if (dimensions == 3) { // Start your run - manifolds::Manifold3 populated_universe( + manifolds::Manifold_3 populated_universe( static_cast(simplices), static_cast(timeslices), initial_radius, foliation_spacing); @@ -97,19 +149,11 @@ try break; case topology_type::TOROIDAL: throw invalid_argument("Toroidal triangulations not yet supported."); - break; - default: throw domain_error("Simulation topology not parsed."); } universe.print(); universe.print_volume_per_timeslice(); fmt::print("Final number of simplices: {}\n", universe.N3()); - if (save_file) - { - utilities::write_file(universe, topology, - static_cast(dimensions), universe.N3(), - static_cast(timeslices), - initial_radius, foliation_spacing); - } + if (save_file) { utilities::write_file(universe); } return EXIT_SUCCESS; } catch (invalid_argument const& InvalidArgument) diff --git a/src/optimize-initialize.py b/src/optimize-initialize.py index a674fe688c..1fb745032d 100644 --- a/src/optimize-initialize.py +++ b/src/optimize-initialize.py @@ -1,6 +1,6 @@ # Causal Dynamical Triangulations in C++ using CGAL # -# Copyright © 2018-2019 Adam Getchell +# Copyright © 2018 Adam Getchell # # A program that optimizes spacetime generation parameters diff --git a/src/test.py b/src/test.py index 43e4b9c13d..a5c6f5c1e9 100644 --- a/src/test.py +++ b/src/test.py @@ -1,3 +1,16 @@ +# Causal Dynamical Triangulations in C++ using CGAL +# +# Copyright © 2021 Adam Getchell +# +# First pass at ML for CDT++ + +# @file test.py +# @brief ML using TensorFlow +# @author Adam Getchell + +# Usage: python test.py +# + import tensorflow as tf mnist = tf.keras.datasets.mnist diff --git a/tests/Apply_move_test.cpp b/tests/Apply_move_test.cpp index 521656cdda..4949a28a3b 100644 --- a/tests/Apply_move_test.cpp +++ b/tests/Apply_move_test.cpp @@ -11,19 +11,20 @@ #include "Apply_move.hpp" -#include +#include #include "Ergodic_moves_3.hpp" using namespace std; -SCENARIO("Apply an ergodic move to 2+1 manifolds", "[apply move][!mayfail]") +SCENARIO("Apply an ergodic move to 2+1 manifolds" * + doctest::test_suite("apply")) { GIVEN("A 2+1 dimensional spherical manifold.") { - constexpr auto desired_simplices = 9600; - constexpr auto desired_timeslices = 7; - manifolds::Manifold3 manifold(desired_simplices, desired_timeslices); + auto constexpr desired_simplices = 9600; + auto constexpr desired_timeslices = 7; + manifolds::Manifold_3 manifold(desired_simplices, desired_timeslices); REQUIRE(manifold.is_correct()); // Copy of manifold auto manifold_before = manifold; @@ -44,10 +45,10 @@ SCENARIO("Apply an ergodic move to 2+1 manifolds", "[apply move][!mayfail]") THEN("The resulting manifold is valid and unchanged.") { CHECK(manifold.is_valid()); - CHECK(manifold_before.simplices() == manifold.simplices()); - CHECK(manifold_before.faces() == manifold.faces()); - CHECK(manifold_before.edges() == manifold.edges()); - CHECK(manifold_before.vertices() == manifold.vertices()); + CHECK_EQ(manifold_before.simplices(), manifold.simplices()); + CHECK_EQ(manifold_before.faces(), manifold.faces()); + CHECK_EQ(manifold_before.edges(), manifold.edges()); + CHECK_EQ(manifold_before.vertices(), manifold.vertices()); // Human verification fmt::print("Old manifold.\n"); manifold_before.print_details(); @@ -144,8 +145,7 @@ SCENARIO("Apply an ergodic move to 2+1 manifolds", "[apply move][!mayfail]") else { spdlog::debug("{}", result.error()); - // Stop further tests - REQUIRE(result.has_value()); + CHECK(result.has_value()); } THEN("The resulting manifold has the applied move.") { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 5b59341617..d5f9f79c53 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,5 +1,5 @@ add_executable( - CDT_test + CDT_unit_tests ${PROJECT_SOURCE_DIR}/tests/main.cpp Apply_move_test.cpp Ergodic_moves_3_test.cpp @@ -12,24 +12,21 @@ add_executable( Move_command_test.cpp Move_tracker_test.cpp S3Action_test.cpp + Settings_test.cpp Tetrahedron_test.cpp Torus_test.cpp Utilities_test.cpp - Vertex_test.cpp - Settings_test.cpp) - + Vertex_test.cpp) +# Activate C++23 features +target_compile_features(CDT_unit_tests PRIVATE cxx_std_23) target_link_libraries( - CDT_test + CDT_unit_tests PRIVATE project_options project_warnings - Catch2::Catch2 date::date-tz fmt::fmt-header-only TBB::tbb CGAL::CGAL) -# Activate C++20 features -target_compile_features(CDT_test PRIVATE cxx_std_20) - # Run unit tests -add_test(NAME cdt-unit-tests COMMAND $) +add_test(NAME cdt-unit-tests COMMAND $) diff --git a/tests/Ergodic_moves_3_test.cpp b/tests/Ergodic_moves_3_test.cpp index f067dddaab..9e5a784912 100644 --- a/tests/Ergodic_moves_3_test.cpp +++ b/tests/Ergodic_moves_3_test.cpp @@ -11,51 +11,99 @@ #include "Ergodic_moves_3.hpp" -#include +#include + +#include using namespace std; using namespace manifolds; -static inline double const RADIUS_2 = sqrt(4.0 / 3.0); // NOLINT -static inline double const SQRT_2 = sqrt(2); // NOLINT -static inline double const INV_SQRT_2 = 1 / sqrt(2); // NOLINT -static inline double const INV_SQRT_3 = 1 / sqrt(3); // NOLINT -static inline double const SQRT_3 = sqrt(3); // NOLINT +static inline std::floating_point auto constexpr RADIUS_2 = + 2.0 * std::numbers::inv_sqrt3_v; +static inline std::floating_point auto constexpr SQRT_2 = + std::numbers::sqrt2_v; +static inline std::floating_point auto constexpr INV_SQRT_2 = 1 / SQRT_2; + +SCENARIO("Use check_move to validate successful move" * + doctest::test_suite("ergodic")) +{ + spdlog::debug("Use check_move to validate successful move.\n"); + GIVEN("A triangulation setup for a (2,3) move") + { + vector vertices{ + Point_t<3>{ 1, 0, 0}, + Point_t<3>{ 0, 1, 0}, + Point_t<3>{ 0, 0, 1}, + Point_t<3>{RADIUS_2, RADIUS_2, RADIUS_2}, + Point_t<3>{ SQRT_2, SQRT_2, 0} + }; + vector timevalues{1, 1, 1, 2, 2}; + auto causal_vertices = make_causal_vertices<3>(vertices, timevalues); + Manifold_3 manifold(causal_vertices); + WHEN("A correct (2,3) move is performed.") + { + spdlog::debug("When a correct (2,3) move is performed.\n"); + // Copy manifold + auto manifold_before = manifold; + if (auto result = ergodic_moves::do_23_move(manifold); result) + { + manifold = result.value(); + // Update geometry with new triangulation + manifold.update(); + } + else + { + spdlog::debug("{}", result.error()); + REQUIRE(result.has_value()); + } + // Manual check + REQUIRE(manifold.is_correct()); + CHECK_EQ(manifold.vertices(), 5); + CHECK_EQ(manifold.edges(), 10); // +1 timelike edge + CHECK_EQ(manifold.faces(), 9); // +2 faces + CHECK_EQ(manifold.simplices(), 3); // +1 (2,2) simplex + CHECK_EQ(manifold.N3_31(), 1); + CHECK_EQ(manifold.N3_22(), 2); + CHECK_EQ(manifold.N1_SL(), 4); + CHECK_EQ(manifold.N1_TL(), 6); + // CHECK(manifold.is_delaunay()); + THEN("check_move returns true") + { + CHECK(ergodic_moves::check_move(manifold_before, manifold, + move_tracker::move_type::TWO_THREE)); + } + } + } +} SCENARIO( - "Perform ergodic moves on the minimal manifold necessary for that move", - "[ergodic moves]") + "Perform ergodic moves on the minimal manifold necessary for (2,3) and (3,2) moves" * + doctest::test_suite("ergodic")) { spdlog::debug( - "Perform ergodic moves on the minimal simplicial complex necessary for " - "that move.\n"); + "Perform ergodic moves on the minimal manifold necessary for (2,3) and (3,2) moves.\n"); GIVEN("A triangulation setup for (2,3) moves") { - vector> vertices{ + vector vertices{ Point_t<3>{ 1, 0, 0}, Point_t<3>{ 0, 1, 0}, Point_t<3>{ 0, 0, 1}, Point_t<3>{RADIUS_2, RADIUS_2, RADIUS_2}, Point_t<3>{ SQRT_2, SQRT_2, 0} }; - vector timevalue{1, 1, 1, 2, 2}; - Causal_vertices_t<3> causal_vertices; - causal_vertices.reserve(vertices.size()); - transform( - vertices.begin(), vertices.end(), timevalue.begin(), - back_inserter(causal_vertices), - [](Point_t<3> point, size_t time) { return make_pair(point, time); }); - Manifold3 manifold(causal_vertices); + vector timevalues{1, 1, 1, 2, 2}; + auto causal_vertices = make_causal_vertices<3>(vertices, timevalues); + Manifold_3 manifold(causal_vertices); REQUIRE(manifold.is_correct()); - REQUIRE(manifold.vertices() == 5); - REQUIRE(manifold.edges() == 9); - REQUIRE(manifold.faces() == 7); - REQUIRE(manifold.simplices() == 2); - REQUIRE(manifold.N3_31() == 1); - REQUIRE(manifold.N3_22() == 1); - REQUIRE(manifold.N1_SL() == 4); - REQUIRE(manifold.N1_TL() == 5); + REQUIRE_EQ(manifold.vertices(), 5); + REQUIRE_EQ(manifold.edges(), 9); + REQUIRE_EQ(manifold.faces(), 7); + REQUIRE_EQ(manifold.simplices(), 2); + REQUIRE_EQ(manifold.N3_31(), 1); + REQUIRE_EQ(manifold.N3_22(), 1); + REQUIRE_EQ(manifold.N1_SL(), 4); + REQUIRE_EQ(manifold.N1_TL(), 5); REQUIRE(manifold.is_delaunay()); WHEN("A (2,3) move is performed") { @@ -80,15 +128,15 @@ SCENARIO( move_tracker::move_type::TWO_THREE)); // Manual check REQUIRE(manifold.is_correct()); - CHECK(manifold.vertices() == 5); - CHECK(manifold.edges() == 10); // +1 timelike edge - CHECK(manifold.faces() == 9); // +2 faces - CHECK(manifold.simplices() == 3); // +1 (2,2) simplex - CHECK(manifold.N3_31() == 1); - CHECK(manifold.N3_22() == 2); - CHECK(manifold.N1_SL() == 4); - CHECK(manifold.N1_TL() == 6); - CHECK_FALSE(manifold.is_delaunay()); + CHECK_EQ(manifold.vertices(), 5); + CHECK_EQ(manifold.edges(), 10); // +1 timelike edge + CHECK_EQ(manifold.faces(), 9); // +2 faces + CHECK_EQ(manifold.simplices(), 3); // +1 (2,2) simplex + CHECK_EQ(manifold.N3_31(), 1); + CHECK_EQ(manifold.N3_22(), 2); + CHECK_EQ(manifold.N1_SL(), 4); + CHECK_EQ(manifold.N1_TL(), 6); + // CHECK(manifold.is_delaunay()); // Human-readable output manifold.print_details(); manifold.print_cells(); @@ -113,14 +161,14 @@ SCENARIO( REQUIRE(start.has_value()); } // Verify we have 1 (3,1) simplex and 2 (2,2) simplices, etc. - REQUIRE(manifold.vertices() == 5); - REQUIRE(manifold.edges() == 10); - REQUIRE(manifold.faces() == 9); - REQUIRE(manifold.simplices() == 3); - REQUIRE(manifold.N3_31() == 1); - REQUIRE(manifold.N3_22() == 2); - REQUIRE(manifold.N1_SL() == 4); - REQUIRE(manifold.N1_TL() == 6); + REQUIRE_EQ(manifold.vertices(), 5); + REQUIRE_EQ(manifold.edges(), 10); + REQUIRE_EQ(manifold.faces(), 9); + REQUIRE_EQ(manifold.simplices(), 3); + REQUIRE_EQ(manifold.N3_31(), 1); + REQUIRE_EQ(manifold.N3_22(), 2); + REQUIRE_EQ(manifold.N1_SL(), 4); + REQUIRE_EQ(manifold.N1_TL(), 6); // Copy manifold auto manifold_before = manifold; @@ -143,14 +191,14 @@ SCENARIO( move_tracker::move_type::THREE_TWO)); // Manual check REQUIRE(manifold.is_correct()); - CHECK(manifold.vertices() == 5); - CHECK(manifold.edges() == 9); - CHECK(manifold.faces() == 7); - CHECK(manifold.simplices() == 2); - CHECK(manifold.N3_31() == 1); - CHECK(manifold.N3_22() == 1); - CHECK(manifold.N1_SL() == 4); - CHECK(manifold.N1_TL() == 5); + CHECK_EQ(manifold.vertices(), 5); + CHECK_EQ(manifold.edges(), 9); + CHECK_EQ(manifold.faces(), 7); + CHECK_EQ(manifold.simplices(), 2); + CHECK_EQ(manifold.N3_31(), 1); + CHECK_EQ(manifold.N3_22(), 1); + CHECK_EQ(manifold.N1_SL(), 4); + CHECK_EQ(manifold.N1_TL(), 5); CHECK(manifold.is_delaunay()); // Human-readable output manifold.print_details(); @@ -163,39 +211,41 @@ SCENARIO( THEN("The move is not performed") { CHECK_FALSE(result); - CHECK(result.error() == "No (3,2) move possible.\n"); + CHECK_EQ(result.error(), "No (3,2) move possible.\n"); } } } +} +SCENARIO( + "Perform ergodic moves on the minimal manifold necessary (2,6) and (6,2) moves" * + doctest::test_suite("ergodic")) +{ + spdlog::debug( + "Perform ergodic moves on the minimal manifold necessary (2,6) and (6,2) moves.\n"); GIVEN("A triangulation setup for a (2,6) move") { - vector> vertices{ + vector vertices{ Point_t<3>{ 0, 0, 0}, Point_t<3>{ 1, 0, 0}, Point_t<3>{ 0, 1, 0}, Point_t<3>{ 0, 0, 1}, Point_t<3>{RADIUS_2, RADIUS_2, RADIUS_2} }; - vector timevalue{0, 1, 1, 1, 2}; - Causal_vertices_t<3> causal_vertices; - causal_vertices.reserve(vertices.size()); - transform( - vertices.begin(), vertices.end(), timevalue.begin(), - back_inserter(causal_vertices), - [](Point_t<3> point, size_t time) { return make_pair(point, time); }); - Manifold3 manifold(causal_vertices); + vector timevalues{0, 1, 1, 1, 2}; + auto causal_vertices = make_causal_vertices<3>(vertices, timevalues); + Manifold_3 manifold(causal_vertices); REQUIRE(manifold.is_correct()); - REQUIRE(manifold.vertices() == 5); - REQUIRE(manifold.edges() == 9); - REQUIRE(manifold.faces() == 7); - REQUIRE(manifold.simplices() == 2); - REQUIRE(manifold.N3_31() == 1); - REQUIRE(manifold.N3_22() == 0); - REQUIRE(manifold.N3_13() == 1); - REQUIRE(manifold.N3_31_13() == 2); - REQUIRE(manifold.N1_SL() == 3); - REQUIRE(manifold.N1_TL() == 6); + REQUIRE_EQ(manifold.vertices(), 5); + REQUIRE_EQ(manifold.edges(), 9); + REQUIRE_EQ(manifold.faces(), 7); + REQUIRE_EQ(manifold.simplices(), 2); + REQUIRE_EQ(manifold.N3_31(), 1); + REQUIRE_EQ(manifold.N3_22(), 0); + REQUIRE_EQ(manifold.N3_13(), 1); + REQUIRE_EQ(manifold.N3_31_13(), 2); + REQUIRE_EQ(manifold.N1_SL(), 3); + REQUIRE_EQ(manifold.N1_TL(), 6); REQUIRE(manifold.is_delaunay()); WHEN("A (2,6) move is performed") { @@ -221,16 +271,16 @@ SCENARIO( move_tracker::move_type::TWO_SIX)); // Manual check REQUIRE(manifold.is_correct()); - CHECK(manifold.vertices() == 6); // +1 vertex - CHECK(manifold.edges() == 14); // +3 spacelike and +2 timelike edges - CHECK(manifold.faces() == 15); // +8 faces - CHECK(manifold.simplices() == 6); // +2 (3,1) and +2 (1,3) simplices - CHECK(manifold.N3_31() == 3); - CHECK(manifold.N3_22() == 0); - CHECK(manifold.N3_13() == 3); - CHECK(manifold.N3_31_13() == 6); - CHECK(manifold.N1_SL() == 6); // +3 spacelike edges - CHECK(manifold.N1_TL() == 8); // +2 timelike edges + CHECK_EQ(manifold.vertices(), 6); // +1 vertex + CHECK_EQ(manifold.edges(), 14); // +3 spacelike and +2 timelike edges + CHECK_EQ(manifold.faces(), 15); // +8 faces + CHECK_EQ(manifold.simplices(), 6); // +2 (3,1) and +2 (1,3) simplices + CHECK_EQ(manifold.N3_31(), 3); + CHECK_EQ(manifold.N3_22(), 0); + CHECK_EQ(manifold.N3_13(), 3); + CHECK_EQ(manifold.N3_31_13(), 6); + CHECK_EQ(manifold.N1_SL(), 6); // +3 spacelike edges + CHECK_EQ(manifold.N1_TL(), 8); // +2 timelike edges CHECK(manifold.is_delaunay()); // Human-readable output fmt::print("Manifold before (2,6):\n"); @@ -260,16 +310,16 @@ SCENARIO( REQUIRE(start.has_value()); } // Verify we have 3 (3,1) simplices and 3 (1,3) simplices, etc. - REQUIRE(manifold.vertices() == 6); - REQUIRE(manifold.edges() == 14); - REQUIRE(manifold.faces() == 15); - REQUIRE(manifold.simplices() == 6); - REQUIRE(manifold.N3_31() == 3); - REQUIRE(manifold.N3_22() == 0); - REQUIRE(manifold.N3_13() == 3); - REQUIRE(manifold.N3_31_13() == 6); - REQUIRE(manifold.N1_SL() == 6); - REQUIRE(manifold.N1_TL() == 8); + REQUIRE_EQ(manifold.vertices(), 6); + REQUIRE_EQ(manifold.edges(), 14); + REQUIRE_EQ(manifold.faces(), 15); + REQUIRE_EQ(manifold.simplices(), 6); + REQUIRE_EQ(manifold.N3_31(), 3); + REQUIRE_EQ(manifold.N3_22(), 0); + REQUIRE_EQ(manifold.N3_13(), 3); + REQUIRE_EQ(manifold.N3_31_13(), 6); + REQUIRE_EQ(manifold.N1_SL(), 6); + REQUIRE_EQ(manifold.N1_TL(), 8); REQUIRE(manifold.is_delaunay()); // Copy manifold @@ -290,16 +340,16 @@ SCENARIO( CHECK(manifold.get_triangulation().is_foliated()); CHECK(manifold.get_triangulation().is_tds_valid()); CHECK(manifold.get_triangulation().check_all_cells()); - CHECK(manifold.vertices() == 5); - CHECK(manifold.edges() == 9); - CHECK(manifold.faces() == 7); - CHECK(manifold.simplices() == 2); - CHECK(manifold.N3_31() == 1); - CHECK(manifold.N3_22() == 0); - CHECK(manifold.N3_13() == 1); - CHECK(manifold.N3_31_13() == 2); - CHECK(manifold.N1_SL() == 3); - CHECK(manifold.N1_TL() == 6); + CHECK_EQ(manifold.vertices(), 5); + CHECK_EQ(manifold.edges(), 9); + CHECK_EQ(manifold.faces(), 7); + CHECK_EQ(manifold.simplices(), 2); + CHECK_EQ(manifold.N3_31(), 1); + CHECK_EQ(manifold.N3_22(), 0); + CHECK_EQ(manifold.N3_13(), 1); + CHECK_EQ(manifold.N3_31_13(), 2); + CHECK_EQ(manifold.N1_SL(), 3); + CHECK_EQ(manifold.N1_TL(), 6); CHECK(manifold.is_delaunay()); // Human-readable output fmt::print("Manifold before (6,2):\n"); @@ -316,13 +366,19 @@ SCENARIO( THEN("The move is not performed") { CHECK_FALSE(result); - CHECK(result.error() == "No (6,2) move possible.\n"); + CHECK_EQ(result.error(), "No (6,2) move possible.\n"); } } } +} +SCENARIO("Perform ergodic moves on the minimal manifold necessary (4,4) moves" * + doctest::test_suite("ergodic") * doctest::skip()) +{ + spdlog::debug( + "Perform ergodic moves on the minimal manifold necessary (4,4) moves.\n"); GIVEN("A triangulation setup for a (4,4) move") { - vector> vertices{ + vector vertices{ Point_t<3>{ 0, 0, 0}, Point_t<3>{ INV_SQRT_2, 0, INV_SQRT_2}, Point_t<3>{ 0, INV_SQRT_2, INV_SQRT_2}, @@ -330,41 +386,22 @@ SCENARIO( Point_t<3>{ 0, -INV_SQRT_2, INV_SQRT_2}, Point_t<3>{ 0, 0, 2} }; - vector timevalue{1, 2, 2, 2, 2, 3}; - - // Manifold3 manifold(causal_vertices, 0, 1); - // produces a bug where at some point during copy construction of - // manifolds initial radius defaults to 1 - - // vector> vertices{ - // Point_t<3>{INV_SQRT_3, INV_SQRT_3, INV_SQRT_3}, - // Point_t<3>{SQRT_2, 0, SQRT_2}, - // Point_t<3>{0, SQRT_2, SQRT_2}, - // Point_t<3>{-SQRT_2, 0, SQRT_2}, - // Point_t<3>{0, -SQRT_2, SQRT_2}, - // Point_t<3>{SQRT_3, SQRT_3, SQRT_3 } - // }; - // vector timevalue{1, 2, 2, 2, 2, 3}; - Causal_vertices_t<3> causal_vertices; - causal_vertices.reserve(vertices.size()); - transform( - vertices.begin(), vertices.end(), timevalue.begin(), - back_inserter(causal_vertices), - [](Point_t<3> point, size_t time) { return make_pair(point, time); }); - Manifold3 manifold(causal_vertices, 0, 1); + vector timevalues{0, 1, 1, 1, 1, 2}; + auto causal_vertices = make_causal_vertices<3>(vertices, timevalues); + Manifold_3 manifold(causal_vertices, 0, 1); // Verify we have 4 vertices, 4 edges, 4 faces, and 4 simplices - REQUIRE(manifold.vertices() == 6); - REQUIRE(manifold.edges() == 13); - REQUIRE(manifold.faces() == 12); - REQUIRE(manifold.simplices() == 4); - REQUIRE(manifold.N3_31() == 2); - REQUIRE(manifold.N3_22() == 0); - REQUIRE(manifold.N3_13() == 2); - REQUIRE(manifold.N3_31_13() == 4); - REQUIRE(manifold.N1_SL() == 5); - REQUIRE(manifold.N1_TL() == 8); - CHECK(manifold.initial_radius() == 0); - CHECK(manifold.foliation_spacing() == 1); + REQUIRE_EQ(manifold.vertices(), 6); + REQUIRE_EQ(manifold.edges(), 13); + REQUIRE_EQ(manifold.faces(), 12); + REQUIRE_EQ(manifold.simplices(), 4); + REQUIRE_EQ(manifold.N3_31(), 2); + REQUIRE_EQ(manifold.N3_22(), 0); + REQUIRE_EQ(manifold.N3_13(), 2); + REQUIRE_EQ(manifold.N3_31_13(), 4); + REQUIRE_EQ(manifold.N1_SL(), 5); + REQUIRE_EQ(manifold.N1_TL(), 8); + CHECK_EQ(manifold.initial_radius(), 0); + CHECK_EQ(manifold.foliation_spacing(), 1); REQUIRE(manifold.is_delaunay()); REQUIRE(manifold.is_correct()); @@ -383,6 +420,9 @@ SCENARIO( manifold.update(); } else { spdlog::info("The (4,4) move failed.\n"); } + fmt::print("Manifold after (4,4):\n"); + manifold.print_details(); + manifold.print_cells(); THEN("The move is correct and the manifold invariants are maintained") { // Check the move @@ -392,3 +432,133 @@ SCENARIO( } } } + +SCENARIO("Test convenience functions needed for bistellar flip" * + doctest::test_suite("ergodic")) +{ + GIVEN("A triangulation setup for a bistellar flip") + { + vector vertices{ + Point_t<3>{ 0, 0, 0}, + Point_t<3>{ INV_SQRT_2, 0, INV_SQRT_2}, + Point_t<3>{ 0, INV_SQRT_2, INV_SQRT_2}, + Point_t<3>{-INV_SQRT_2, 0, INV_SQRT_2}, + Point_t<3>{ 0, -INV_SQRT_2, INV_SQRT_2}, + Point_t<3>{ 0, 0, 2} + }; + ergodic_moves::Delaunay triangulation(vertices.begin(), vertices.end()); + CHECK(triangulation.is_valid()); + auto edges = foliated_triangulations::collect_edges<3>(triangulation); + WHEN("We get all the finite cells in the triangulation") + { + auto cells = foliated_triangulations::collect_cells<3>(triangulation); + THEN("We have 4 cells") { REQUIRE_EQ(cells.size(), 4); } + } + WHEN("We get all finite edges in the triangulation") + { + THEN("We have 13 edges") { REQUIRE_EQ(edges.size(), 13); } + } + WHEN("We find the pivot edge in the triangulation") + { + auto pivot_edge = ergodic_moves::find_pivot_edge(triangulation, edges); + REQUIRE_MESSAGE(pivot_edge, "No pivot edge found."); + + auto Contains = [&vertices](Point_t<3> point) { + return ranges::any_of(vertices, [&point](Point_t<3> const& test) { + return test == point; + }); + }; + + if (pivot_edge) + { + auto incident_cells = ergodic_moves::get_incident_cells( + triangulation, pivot_edge.value()); + REQUIRE_MESSAGE(incident_cells, "No incident cells found."); + THEN("We have a pivot edge") + { + CHECK_MESSAGE(pivot_edge, "Pivot edge found"); + REQUIRE(triangulation.tds().is_edge( + pivot_edge->first, pivot_edge->second, pivot_edge->third)); + auto pivot_from_1 = + pivot_edge->first->vertex(pivot_edge->second)->point(); + auto pivot_from_2 = + pivot_edge->first->vertex(pivot_edge->third)->point(); + // Verify Contains + REQUIRE_FALSE(Contains(Point_t<3>{0, 0, 1})); + REQUIRE(Contains(pivot_from_1)); + REQUIRE(Contains(pivot_from_2)); + } + if (incident_cells) + { + THEN("We can obtain the cells incident to that edge") + { + REQUIRE_EQ(incident_cells->size(), 4); + } + AND_THEN("We can obtain the vertices from the incident cells") + { + auto incident_vertices = + ergodic_moves::get_vertices(incident_cells.value()); + REQUIRE_EQ(incident_vertices.size(), 6); + } + } + } + } + WHEN("We get all finite vertices in the triangulation") + { + THEN("We have 6 vertices") + { + auto all_finite_vertices = + foliated_triangulations::collect_vertices<3>(triangulation); + REQUIRE_EQ(all_finite_vertices.size(), 6); + } + } + } +} + +SCENARIO("Perform bistellar flip on Delaunay triangulation" * + doctest::test_suite("ergodic")) +{ + GIVEN("A triangulation setup for a bistellar flip") + { + vector vertices{ + Point_t<3>{ 0, 0, 0}, + Point_t<3>{ INV_SQRT_2, 0, INV_SQRT_2}, + Point_t<3>{ 0, INV_SQRT_2, INV_SQRT_2}, + Point_t<3>{-INV_SQRT_2, 0, INV_SQRT_2}, + Point_t<3>{ 0, -INV_SQRT_2, INV_SQRT_2}, + Point_t<3>{ 0, 0, 2} + }; + ergodic_moves::Delaunay triangulation(vertices.begin(), vertices.end()); + WHEN("We have a valid triangulation") + { + CHECK(triangulation.is_valid()); + THEN("We can perform a bistellar flip") + { + // Obtain top and bottom vertices by re-inserting, which returns the + // Vertex_handle + auto top = triangulation.insert(Point_t<3>{0, 0, 2}); + auto bottom = triangulation.insert(Point_t<3>{0, 0, 0}); + auto edges = foliated_triangulations::collect_edges<3>(triangulation); + auto pivot_edge = ergodic_moves::find_pivot_edge(triangulation, edges); + REQUIRE_MESSAGE(pivot_edge, "No pivot edge found."); + + // Check this didn't actually change vertices in the triangulation + REQUIRE_EQ(vertices.size(), 6); + + if (pivot_edge) + { + auto flipped_triangulation = ergodic_moves::bistellar_flip( + triangulation, pivot_edge.value(), top, bottom); + + REQUIRE_MESSAGE(flipped_triangulation, "Bistellar flip failed."); + if (flipped_triangulation) + { + /// FIXME: This fails because the triangulation is not valid after + /// the flip neighbor of c has not c as neighbor + WARN(flipped_triangulation->is_valid()); + } + } + } + } + } +} \ No newline at end of file diff --git a/tests/Foliated_triangulation_test.cpp b/tests/Foliated_triangulation_test.cpp index cdf6f8ebd0..5cf7181e67 100644 --- a/tests/Foliated_triangulation_test.cpp +++ b/tests/Foliated_triangulation_test.cpp @@ -12,36 +12,40 @@ #include "Foliated_triangulation.hpp" -#include +#include + +#include using namespace std; using namespace foliated_triangulations; -static inline double const RADIUS_2 = std::sqrt(4.0 / 3.0); // NOLINT -static inline double const INV_SQRT_2 = 1.0 / std::sqrt(2.0); // NOLINT +static inline auto constexpr RADIUS_2 = 2.0 * std::numbers::inv_sqrt3_v; +static inline std::floating_point auto constexpr SQRT_2 = + std::numbers::sqrt2_v; +static inline auto constexpr INV_SQRT_2 = 1.0 / SQRT_2; -SCENARIO("FoliatedTriangulation special member and swap properties", - "[triangulation]") +SCENARIO("FoliatedTriangulation special member and swap properties" * + doctest::test_suite("foliated_triangulation")) { spdlog::debug("FoliatedTriangulation special member and swap properties.\n"); - GIVEN("A FoliatedTriangulation3 class.") + GIVEN("A FoliatedTriangulation_3 class.") { WHEN("It's properties are examined.") { THEN("It is no-throw destructible.") { - REQUIRE(is_nothrow_destructible_v); + REQUIRE(is_nothrow_destructible_v); spdlog::debug("It is no-throw destructible.\n"); } THEN("It is default constructible.") { - REQUIRE(is_default_constructible_v); + REQUIRE(is_default_constructible_v); spdlog::debug("It is default destructible.\n"); } THEN("It is NOT trivially default constructible.") { CHECK_FALSE( - is_trivially_default_constructible_v); + is_trivially_default_constructible_v); } THEN("Delaunay_triangulation_3 is NOT no-throw default constructible.") { @@ -51,54 +55,55 @@ SCENARIO("FoliatedTriangulation special member and swap properties", "Therefore FoliatedTriangulation is NOT no-throw default " "constructible.") { - CHECK_FALSE(is_nothrow_default_constructible_v); + CHECK_FALSE( + is_nothrow_default_constructible_v); } THEN("It is no-throw copy constructible.") { - REQUIRE(is_nothrow_copy_constructible_v); + REQUIRE(is_nothrow_copy_constructible_v); spdlog::debug("It is no-throw copy constructible.\n"); } THEN("It is no-throw copy assignable.") { - REQUIRE(is_nothrow_copy_assignable_v); + REQUIRE(is_nothrow_copy_assignable_v); spdlog::debug("It is no-throw copy assignable.\n"); } THEN("It is no-throw move constructible.") { - REQUIRE(is_nothrow_move_constructible_v); + REQUIRE(is_nothrow_move_constructible_v); spdlog::debug("It is no-throw move constructible.\n"); } THEN("It is no-throw move assignable.") { - REQUIRE(is_nothrow_move_assignable_v); + REQUIRE(is_nothrow_move_assignable_v); spdlog::debug("It is no-throw move assignable.\n"); } THEN("It is no-throw swappable.") { - REQUIRE(is_nothrow_swappable_v); + REQUIRE(is_nothrow_swappable_v); spdlog::debug("It is no-throw swappable.\n"); } THEN("It is constructible from a Delaunay triangulation.") { - REQUIRE(is_constructible_v>); + REQUIRE(is_constructible_v>); spdlog::debug("It is constructible from a Delaunay triangulation.\n"); } THEN("It is constructible from parameters.") { - REQUIRE(is_constructible_v); spdlog::debug("It is constructible from parameters.\n"); } THEN("It is constructible from Causal_vertices.") { REQUIRE( - is_constructible_v>); + is_constructible_v>); spdlog::debug("It is constructible from Causal_vertices.\n"); } THEN("It is constructible from Causal_vertices and INITIAL_RADIUS.") { - REQUIRE(is_constructible_v, - double>); + REQUIRE(is_constructible_v, double>); spdlog::debug( "It is constructible from Causal_vertices and INITIAL_RADIUS.\n"); } @@ -106,8 +111,8 @@ SCENARIO("FoliatedTriangulation special member and swap properties", "It is constructible from Causal_vertices, INITIAL_RADIUS, and " "RADIAL_SEPARATION.") { - REQUIRE(is_constructible_v, - double, double>); + REQUIRE(is_constructible_v, double, double>); spdlog::debug( "It is constructible from Causal_vertices, INITIAL_RADIUS, and " "RADIAL_SEPARATION.\n"); @@ -116,33 +121,68 @@ SCENARIO("FoliatedTriangulation special member and swap properties", } } -SCENARIO("FoliatedTriangulation free functions", "[triangulation]") +SCENARIO("FoliatedTriangulation free functions" * + doctest::test_suite("foliated_triangulation")) { - spdlog::debug("foliated_triangulation:: functions.\n"); - GIVEN("A small foliated triangulation.") + spdlog::debug("foliated_triangulations:: free functions.\n"); + + GIVEN("A vector of points and timevalues.") + { + vector const Vertices{Point_t<3>(1, 0, 0), Point_t<3>(0, 1, 0), + Point_t<3>(0, 0, 1), + Point_t<3>(RADIUS_2, RADIUS_2, RADIUS_2)}; + vector const Timevalues{1, 1, 1, 2}; + WHEN("Causal vertices are created.") + { + auto causal_vertices = make_causal_vertices<3>(Vertices, Timevalues); + THEN("They are correct.") + { + REQUIRE_EQ(causal_vertices.size(), 4); + REQUIRE_EQ(causal_vertices[0].first, Point_t<3>(1, 0, 0)); + REQUIRE_EQ(causal_vertices[0].second, 1); + REQUIRE_EQ(causal_vertices[1].first, Point_t<3>(0, 1, 0)); + REQUIRE_EQ(causal_vertices[1].second, 1); + REQUIRE_EQ(causal_vertices[2].first, Point_t<3>(0, 0, 1)); + REQUIRE_EQ(causal_vertices[2].second, 1); + REQUIRE_EQ(causal_vertices[3].first, + Point_t<3>(RADIUS_2, RADIUS_2, RADIUS_2)); + REQUIRE_EQ(causal_vertices[3].second, 2); + } + } + } + GIVEN("A mismatched set of points and timevalues.") + { + vector const Vertices{Point_t<3>(1, 0, 0), Point_t<3>(0, 1, 0), + Point_t<3>(0, 0, 1), + Point_t<3>(RADIUS_2, RADIUS_2, RADIUS_2)}; + vector const Timevalues{1, 1, 1}; + WHEN("Causal vertices are created.") + { + THEN("An exception is thrown.") + { + REQUIRE_THROWS(make_causal_vertices<3>(Vertices, Timevalues)); + } + } + } + + GIVEN("A small foliated 3D triangulation.") { - vector> Vertices{ + vector Vertices{ Point_t<3>{ 1, 0, 0}, Point_t<3>{ 0, 1, 0}, Point_t<3>{ 0, 0, 1}, Point_t<3>{RADIUS_2, RADIUS_2, RADIUS_2} }; - vector timevalues{1, 1, 1, 2}; - Causal_vertices_t<3> vertices; - vertices.reserve(Vertices.size()); - std::transform(Vertices.begin(), Vertices.end(), timevalues.begin(), - std::back_inserter(vertices), - [](Point_t<3> vertex, std::size_t timevalue) { - return std::make_pair(vertex, timevalue); - }); - FoliatedTriangulation3 triangulation(vertices); - auto print = [&triangulation](auto& vertex) { + vector timevalues{1, 1, 1, 2}; + auto vertices = make_causal_vertices<3>(Vertices, timevalues); + FoliatedTriangulation_3 triangulation(vertices); + auto print = [&triangulation](auto& vertex) { fmt::print( - "Vertex: ({}) Timevalue: {} is a vertex: {} and is " - "infinite: {}\n", - vertex->point(), vertex->info(), - triangulation.get_delaunay().tds().is_vertex(vertex), - triangulation.is_infinite(vertex)); + "Vertex: ({}) Timevalue: {} is a vertex: {} and is " + "infinite: {}\n", + utilities::point_to_str(vertex->point()), vertex->info(), + triangulation.get_delaunay().tds().is_vertex(vertex), + triangulation.is_infinite(vertex)); }; REQUIRE(triangulation.is_initialized()); @@ -165,15 +205,23 @@ SCENARIO("FoliatedTriangulation free functions", "[triangulation]") } } + WHEN("We print a cell in the triangulation.") + { + THEN("A cell is printed correctly.") + { + foliated_triangulations::print_cell<3>(triangulation.get_cells().at(0)); + } + } + WHEN("We ask for a container of vertices given a container of cells.") { auto&& all_vertices = get_vertices_from_cells<3>(triangulation.get_cells()); THEN("We get back the correct number of vertices.") { - REQUIRE(all_vertices.size() == 4); + REQUIRE_EQ(all_vertices.size(), 4); // Human verification - for_each(all_vertices.begin(), all_vertices.end(), print); + ranges::for_each(all_vertices, print); } } } @@ -181,20 +229,21 @@ SCENARIO("FoliatedTriangulation free functions", "[triangulation]") "A minimal triangulation with non-default initial radius and radial " "separation.") { - constexpr auto desired_simplices = 2; - constexpr auto desired_timeslices = 2; - constexpr auto initial_radius = 3.0; - constexpr auto foliation_spacing = 2.0; - FoliatedTriangulation3 triangulation(desired_simplices, desired_timeslices, - initial_radius, foliation_spacing); + auto constexpr desired_simplices = 2; + auto constexpr desired_timeslices = 2; + auto constexpr initial_radius = 3.0; + auto constexpr foliation_spacing = 2.0; + FoliatedTriangulation_3 const triangulation( + desired_simplices, desired_timeslices, initial_radius, + foliation_spacing); THEN("The triangulation is initialized correctly.") { REQUIRE(triangulation.is_initialized()); } THEN("The initial radius and radial separation are correct.") { - REQUIRE(triangulation.initial_radius() == initial_radius); - REQUIRE(triangulation.foliation_spacing() == foliation_spacing); + REQUIRE_EQ(triangulation.initial_radius(), initial_radius); + REQUIRE_EQ(triangulation.foliation_spacing(), foliation_spacing); // Human verification fmt::print( "The triangulation has an initial radius of {} and a radial " @@ -203,14 +252,15 @@ SCENARIO("FoliatedTriangulation free functions", "[triangulation]") } THEN("Each vertex has a valid timevalue.") { - for (std::span checked_vertices(triangulation.get_vertices()); - auto const& vertex : checked_vertices) + for (std::span const checked_vertices(triangulation.get_vertices()); + auto const& vertex : checked_vertices) { CHECK(triangulation.does_vertex_radius_match_timevalue(vertex)); fmt::print( "Vertex ({}) with timevalue of {} has a squared radius of {} and a " "squared expected radius of {} with an expected timevalue of {}.\n", - vertex->point(), vertex->info(), squared_radius<3>(vertex), + utilities::point_to_str(vertex->point()), vertex->info(), + squared_radius<3>(vertex), std::pow(triangulation.expected_radius(vertex), 2), triangulation.expected_timevalue(vertex)); } @@ -218,7 +268,7 @@ SCENARIO("FoliatedTriangulation free functions", "[triangulation]") } GIVEN("A triangulation setup for a (4,4) move") { - vector> vertices{ + vector vertices{ Point_t<3>{ 0, 0, 0}, Point_t<3>{ INV_SQRT_2, 0, INV_SQRT_2}, Point_t<3>{ 0, INV_SQRT_2, INV_SQRT_2}, @@ -226,28 +276,23 @@ SCENARIO("FoliatedTriangulation free functions", "[triangulation]") Point_t<3>{ 0, -INV_SQRT_2, INV_SQRT_2}, Point_t<3>{ 0, 0, 2} }; - vector timevalue{1, 2, 2, 2, 2, 3}; - Causal_vertices_t<3> causal_vertices; - causal_vertices.reserve(vertices.size()); - transform( - vertices.begin(), vertices.end(), timevalue.begin(), - back_inserter(causal_vertices), - [](Point_t<3> point, size_t time) { return make_pair(point, time); }); - FoliatedTriangulation3 triangulation(causal_vertices, 0, 1); + vector timevalue{1, 2, 2, 2, 2, 3}; + auto causal_vertices = make_causal_vertices<3>(vertices, timevalue); + FoliatedTriangulation_3 const triangulation(causal_vertices, 0, 1); // Verify we have 6 vertices, 13 edges, 12 facets, and 4 cells - REQUIRE(triangulation.number_of_vertices() == 6); - REQUIRE(triangulation.number_of_finite_edges() == 13); - REQUIRE(triangulation.number_of_finite_facets() == 12); + REQUIRE_EQ(triangulation.number_of_vertices(), 6); + REQUIRE_EQ(triangulation.number_of_finite_edges(), 13); + REQUIRE_EQ(triangulation.number_of_finite_facets(), 12); REQUIRE(triangulation.number_of_finite_cells() == 4); - CHECK(triangulation.initial_radius() == 0); - CHECK(triangulation.foliation_spacing() == 1); + CHECK_EQ(triangulation.initial_radius(), 0); + CHECK_EQ(triangulation.foliation_spacing(), 1); REQUIRE(triangulation.is_delaunay()); REQUIRE(triangulation.is_correct()); WHEN("We collect edges.") { auto edges = foliated_triangulations::collect_edges<3>( triangulation.get_delaunay()); - THEN("We have 13 edges.") { REQUIRE(edges.size() == 13); } + THEN("We have 13 edges.") { REQUIRE_EQ(edges.size(), 13); } } WHEN("We have a point in the triangulation.") { @@ -255,13 +300,17 @@ SCENARIO("FoliatedTriangulation free functions", "[triangulation]") { auto vertex = foliated_triangulations::find_vertex<3>( triangulation.get_delaunay(), Point_t<3>{0, 0, 0}); - REQUIRE(vertex); - CHECK(vertex.value()->point() == Point_t<3>{0, 0, 0}); - CHECK(vertex.value()->info() == 1); - // Human verification - fmt::print( - "Point(0,0,0) was found as vertex ({}) with a timevalue of {}.\n", - vertex.value()->point(), vertex.value()->info()); + REQUIRE_MESSAGE(vertex, "Vertex not found."); + if (vertex) + { + CHECK_EQ(vertex.value()->point(), Point_t<3>{0, 0, 0}); + CHECK_EQ(vertex.value()->info(), 1); + // Human verification + fmt::print( + "Point(0,0,0) was found as vertex ({}) with a timevalue of {}.\n", + utilities::point_to_str(vertex.value()->point()), + vertex.value()->info()); + } } WHEN("We choose a point not in the triangulation.") { @@ -278,94 +327,119 @@ SCENARIO("FoliatedTriangulation free functions", "[triangulation]") { THEN("The correct vertices yields the correct cell.") { - auto v1 = foliated_triangulations::find_vertex<3>( + auto v_1 = foliated_triangulations::find_vertex<3>( triangulation.get_delaunay(), Point_t<3>{0, 0, 0}); - auto v2 = foliated_triangulations::find_vertex<3>( + auto v_2 = foliated_triangulations::find_vertex<3>( triangulation.get_delaunay(), Point_t<3>{0, INV_SQRT_2, INV_SQRT_2}); - auto v3 = foliated_triangulations::find_vertex<3>( + auto v_3 = foliated_triangulations::find_vertex<3>( triangulation.get_delaunay(), Point_t<3>{0, -INV_SQRT_2, INV_SQRT_2}); - auto v4 = foliated_triangulations::find_vertex<3>( + auto v_4 = foliated_triangulations::find_vertex<3>( triangulation.get_delaunay(), Point_t<3>{-INV_SQRT_2, 0, INV_SQRT_2}); - auto cell = foliated_triangulations::find_cell<3>( - triangulation.get_delaunay(), v1.value(), v2.value(), v3.value(), - v4.value()); - CHECK(cell); - // Human verification - triangulation.print_cells(); + REQUIRE_MESSAGE(v_1, "Vertex v_1 not found."); + REQUIRE_MESSAGE(v_2, "Vertex v_2 not found."); + REQUIRE_MESSAGE(v_3, "Vertex v_3 not found."); + REQUIRE_MESSAGE(v_4, "Vertex v_4 not found."); + if (v_1 && v_2 && v_3 && v_4) + { + auto cell = foliated_triangulations::find_cell<3>( + triangulation.get_delaunay(), v_1.value(), v_2.value(), + v_3.value(), v_4.value()); + CHECK(cell); + // Human verification + triangulation.print_cells(); + } } - THEN("The incorrect vertices does not return a cell.") + THEN("The incorrect vertices do not return a cell.") { - auto v1 = foliated_triangulations::find_vertex<3>( + auto v_1 = foliated_triangulations::find_vertex<3>( triangulation.get_delaunay(), Point_t<3>{0, 0, 0}); - auto v2 = foliated_triangulations::find_vertex<3>( + auto v_2 = foliated_triangulations::find_vertex<3>( triangulation.get_delaunay(), Point_t<3>{INV_SQRT_2, 0, INV_SQRT_2}); - auto v3 = foliated_triangulations::find_vertex<3>( + auto v_3 = foliated_triangulations::find_vertex<3>( triangulation.get_delaunay(), Point_t<3>{0, INV_SQRT_2, INV_SQRT_2}); - auto v4 = foliated_triangulations::find_vertex<3>( + auto v_4 = foliated_triangulations::find_vertex<3>( triangulation.get_delaunay(), Point_t<3>{0, 0, 2}); - auto cell = foliated_triangulations::find_cell<3>( - triangulation.get_delaunay(), v1.value(), v2.value(), v3.value(), - v4.value()); - REQUIRE_FALSE(cell); + REQUIRE_MESSAGE(v_1, "Vertex v_1 not found."); + REQUIRE_MESSAGE(v_2, "Vertex v_2 not found."); + REQUIRE_MESSAGE(v_3, "Vertex v_3 not found."); + REQUIRE_MESSAGE(v_4, "Vertex v_4 not found."); + if (v_1 && v_2 && v_3 && v_4) + { + auto cell = foliated_triangulations::find_cell<3>( + triangulation.get_delaunay(), v_1.value(), v_2.value(), + v_3.value(), v_4.value()); + REQUIRE_FALSE(cell); + } } } } + WHEN("A container of cells is printed.") + { + THEN("The container is printed correctly.") + { + foliated_triangulations::print_cells<3>(triangulation.get_cells()); + } + } + WHEN("We choose a cell in the triangulation.") + { + auto cell = triangulation.get_cells().at(0); + THEN("We can print it's neighbors.") + { + foliated_triangulations::print_cell<3>(cell); + foliated_triangulations::print_neighboring_cells<3>(cell); + } + } } } -SCENARIO("FoliatedTriangulation3 initialization", "[triangulation]") +SCENARIO("FoliatedTriangulation_3 initialization" * + doctest::test_suite("foliated_triangulation")) { spdlog::debug("FoliatedTriangulation initialization.\n"); GIVEN("A 3D foliated triangulation.") { WHEN("It is default constructed.") { - FoliatedTriangulation3 triangulation; THEN("The default Delaunay triangulation is valid.") { + FoliatedTriangulation_3 const triangulation; REQUIRE(triangulation.is_initialized()); - REQUIRE(triangulation.max_time() == 0); - REQUIRE(triangulation.min_time() == 0); - REQUIRE(triangulation.initial_radius() == INITIAL_RADIUS); - REQUIRE(triangulation.foliation_spacing() == FOLIATION_SPACING); + REQUIRE_EQ(triangulation.max_time(), 0); + REQUIRE_EQ(triangulation.min_time(), 0); + REQUIRE_EQ(triangulation.initial_radius(), INITIAL_RADIUS); + REQUIRE_EQ(triangulation.foliation_spacing(), FOLIATION_SPACING); } } WHEN( "It is constructed from a Delaunay triangulation with 4 causal " "vertices.") { - vector> Vertices{ + vector Vertices{ Point_t<3>{ 1, 0, 0}, Point_t<3>{ 0, 1, 0}, Point_t<3>{ 0, 0, 1}, Point_t<3>{RADIUS_2, RADIUS_2, RADIUS_2} }; - vector timevalues{1, 1, 1, 2}; - Causal_vertices_t<3> vertices; - vertices.reserve(Vertices.size()); - std::transform(Vertices.begin(), Vertices.end(), timevalues.begin(), - std::back_inserter(vertices), - [](auto vertex, std::size_t timevalue) { - return std::make_pair(vertex, timevalue); - }); - FoliatedTriangulation3 triangulation(vertices); + vector timevalues{1, 1, 1, 2}; + auto vertices = make_causal_vertices<3>(Vertices, timevalues); + FoliatedTriangulation_3 const triangulation(vertices); THEN("Triangulation is valid and foliated.") { REQUIRE(triangulation.is_initialized()); - REQUIRE(triangulation.dimension() == 3); - REQUIRE(triangulation.number_of_vertices() == 4); - REQUIRE(triangulation.number_of_finite_edges() == 6); - REQUIRE(triangulation.number_of_finite_facets() == 4); - REQUIRE(triangulation.number_of_finite_cells() == 1); - REQUIRE(triangulation.max_time() == 2); - REQUIRE(triangulation.min_time() == 1); - REQUIRE(triangulation.initial_radius() == INITIAL_RADIUS); - REQUIRE(triangulation.foliation_spacing() == FOLIATION_SPACING); + REQUIRE_EQ(triangulation.dimension(), 3); + REQUIRE_EQ(triangulation.number_of_vertices(), 4); + REQUIRE_EQ(triangulation.number_of_finite_edges(), 6); + REQUIRE_EQ(triangulation.number_of_finite_facets(), 4); + REQUIRE_EQ(triangulation.number_of_finite_cells(), 1); + REQUIRE_EQ(triangulation.max_time(), 2); + REQUIRE_EQ(triangulation.min_time(), 1); + REQUIRE_EQ(triangulation.initial_radius(), INITIAL_RADIUS); + REQUIRE_EQ(triangulation.foliation_spacing(), FOLIATION_SPACING); REQUIRE(triangulation.is_foliated()); // Human verification triangulation.print_cells(); @@ -373,32 +447,25 @@ SCENARIO("FoliatedTriangulation3 initialization", "[triangulation]") } WHEN("Constructing the minimum triangulation.") { - constexpr auto desired_simplices = 2; - constexpr auto desired_timeslices = 2; - FoliatedTriangulation3 triangulation(desired_simplices, - desired_timeslices); + auto constexpr desired_simplices = 2; + auto constexpr desired_timeslices = 2; + FoliatedTriangulation_3 triangulation(desired_simplices, + desired_timeslices); THEN("Triangulation is valid and foliated.") { REQUIRE(triangulation.is_initialized()); } THEN("The triangulation has sensible values.") { - using Catch::Matchers::Predicate; - - // We have 1 to 8 vertex_count + // // We have 1 to 8 vertex_count auto vertex_count{triangulation.number_of_vertices()}; - CHECK_THAT(vertex_count, Predicate( - [](int const count) -> bool { - return (1 <= count && count <= 8); - }, - "There should be 1 to 8 vertices.")); - // We have 1 to 12 simplex_count + CHECK_GE(vertex_count, 1); + CHECK_LE(vertex_count, 8); + // // We have 1 to 12 simplex_count auto simplex_count{triangulation.number_of_finite_cells()}; - CHECK_THAT(simplex_count, Predicate( - [](int const count) -> bool { - return (1 <= count && count <= 12); - }, - "There should be 1 to 12 simplices.")); + CHECK_GE(simplex_count, 1); + CHECK_LE(simplex_count, 12); + // Human verification triangulation.print(); } @@ -407,31 +474,30 @@ SCENARIO("FoliatedTriangulation3 initialization", "[triangulation]") auto check = [&triangulation](Vertex_handle_t<3> const& vertex) { CHECK(triangulation.does_vertex_radius_match_timevalue(vertex)); }; - for_each(triangulation.get_vertices().begin(), - triangulation.get_vertices().end(), check); + ranges::for_each(triangulation.get_vertices(), check); // Human verification auto print = [&triangulation](Vertex_handle_t<3> const& vertex) { fmt::print( "Vertex: ({}) Timevalue: {} has a squared radius of {} and " "a squared expected radius of {} with an expected timevalue of " "{}.\n", - vertex->point(), vertex->info(), squared_radius<3>(vertex), + utilities::point_to_str(vertex->point()), vertex->info(), + squared_radius<3>(vertex), std::pow(triangulation.expected_radius(vertex), 2), triangulation.expected_timevalue(vertex)); }; - for_each(triangulation.get_vertices().begin(), - triangulation.get_vertices().end(), print); + ranges::for_each(triangulation.get_vertices(), print); } } WHEN( "Constructing the minimal triangulation with non-default initial " "radius and separation.") { - constexpr auto desired_simplices = 2; - constexpr auto desired_timeslices = 2; - constexpr auto initial_radius = 3.0; - constexpr auto radial_factor = 2.0; - FoliatedTriangulation3 triangulation( + auto constexpr desired_simplices = 2; + auto constexpr desired_timeslices = 2; + auto constexpr initial_radius = 3.0; + auto constexpr radial_factor = 2.0; + FoliatedTriangulation_3 const triangulation( desired_simplices, desired_timeslices, initial_radius, radial_factor); THEN("The triangulation is initialized correctly.") { @@ -439,19 +505,19 @@ SCENARIO("FoliatedTriangulation3 initialization", "[triangulation]") } THEN("The initial radius and radial separation are correct.") { - REQUIRE(triangulation.initial_radius() == initial_radius); - REQUIRE(triangulation.foliation_spacing() == radial_factor); + REQUIRE_EQ(triangulation.initial_radius(), initial_radius); + REQUIRE_EQ(triangulation.foliation_spacing(), radial_factor); } } WHEN( "Constructing a small triangulation with fractional initial radius and " "separation.") { - constexpr auto desired_simplices = 24; - constexpr auto desired_timeslices = 3; - constexpr auto initial_radius = 1.5; - constexpr auto radial_factor = 1.1; - FoliatedTriangulation3 triangulation( + auto constexpr desired_simplices = 24; + auto constexpr desired_timeslices = 3; + auto constexpr initial_radius = 1.5; + auto constexpr radial_factor = 1.1; + FoliatedTriangulation_3 const triangulation( desired_simplices, desired_timeslices, initial_radius, radial_factor); THEN("The triangulation is initialized correctly.") { @@ -459,23 +525,23 @@ SCENARIO("FoliatedTriangulation3 initialization", "[triangulation]") } THEN("The initial radius and radial separation are correct.") { - REQUIRE(triangulation.initial_radius() == initial_radius); - REQUIRE(triangulation.foliation_spacing() == radial_factor); + REQUIRE_EQ(triangulation.initial_radius(), initial_radius); + REQUIRE_EQ(triangulation.foliation_spacing(), radial_factor); } } WHEN("Constructing a medium triangulation.") { - constexpr auto desired_simplices = 6400; - constexpr auto desired_timeslices = 7; - FoliatedTriangulation3 triangulation(desired_simplices, - desired_timeslices); + auto constexpr desired_simplices = 6400; + auto constexpr desired_timeslices = 7; + FoliatedTriangulation_3 const triangulation(desired_simplices, + desired_timeslices); THEN("Triangulation is valid and foliated.") { REQUIRE(triangulation.is_initialized()); } THEN("The triangulation has sensible values.") { - REQUIRE(triangulation.min_time() == 1); + REQUIRE_EQ(triangulation.min_time(), 1); // Human verification triangulation.print(); } @@ -483,29 +549,27 @@ SCENARIO("FoliatedTriangulation3 initialization", "[triangulation]") { triangulation.print(); // Every cell is classified as (3,1), (2,2), or (1,3) - CHECK(triangulation.get_cells().size() == - (triangulation.get_three_one().size() + - triangulation.get_two_two().size() + - triangulation.get_one_three().size())); + CHECK_EQ(triangulation.get_cells().size(), + triangulation.get_three_one().size() + + triangulation.get_two_two().size() + + triangulation.get_one_three().size()); // Every cell is properly labelled CHECK(triangulation.check_all_cells()); CHECK_FALSE(triangulation.N2_SL().empty()); - CHECK(triangulation.max_time() > 0); - CHECK(triangulation.min_time() > 0); - CHECK(triangulation.max_time() > triangulation.min_time()); + CHECK_GT(triangulation.max_time(), 0); + CHECK_GT(triangulation.min_time(), 0); + CHECK_GT(triangulation.max_time(), triangulation.min_time()); auto check_timelike = [](Edge_handle_t<3> const& edge) { CHECK(classify_edge<3>(edge)); }; - for_each(triangulation.get_timelike_edges().begin(), - triangulation.get_timelike_edges().end(), check_timelike); + ranges::for_each(triangulation.get_timelike_edges(), check_timelike); auto check_spacelike = [](Edge_handle_t<3> const& edge) { CHECK(!classify_edge<3>(edge)); }; - for_each(triangulation.get_spacelike_edges().begin(), - triangulation.get_spacelike_edges().end(), check_spacelike); + ranges::for_each(triangulation.get_spacelike_edges(), check_spacelike); // Human verification fmt::print("There are {} edges.\n", triangulation.number_of_finite_edges()); @@ -522,14 +586,16 @@ SCENARIO("FoliatedTriangulation3 initialization", "[triangulation]") } } -SCENARIO("FoliatedTriangulation3 copying", "[triangulation]") +SCENARIO("FoliatedTriangulation_3 copying" * + doctest::test_suite("foliated_triangulation")) { - spdlog::debug("FoliatedTriangulation3 copying.\n"); - GIVEN("A FoliatedTriangulation3") + spdlog::debug("FoliatedTriangulation_3 copying.\n"); + GIVEN("A FoliatedTriangulation_3") { - constexpr auto desired_simplices = 6400; - constexpr auto desired_timeslices = 7; - FoliatedTriangulation3 triangulation(desired_simplices, desired_timeslices); + auto constexpr desired_simplices = 6400; + auto constexpr desired_timeslices = 7; + FoliatedTriangulation_3 triangulation(desired_simplices, + desired_timeslices); WHEN("It is copied") { auto ft2 = triangulation; @@ -537,49 +603,43 @@ SCENARIO("FoliatedTriangulation3 copying", "[triangulation]") { auto* ft_ptr = &triangulation; auto* ft2_ptr = &ft2; - CHECK_FALSE(ft_ptr == ft2_ptr); + CHECK_NE(ft_ptr, ft2_ptr); } THEN("The foliated triangulations have identical properties.") { - CHECK(triangulation.is_initialized() == ft2.is_initialized()); - CHECK(triangulation.number_of_finite_cells() == - ft2.number_of_finite_cells()); - CHECK(triangulation.min_time() == ft2.min_time()); - CHECK(triangulation.get_cells().size() == ft2.get_cells().size()); - CHECK(triangulation.get_three_one().size() == - ft2.get_three_one().size()); - CHECK(triangulation.get_two_two().size() == ft2.get_two_two().size()); - CHECK(triangulation.get_one_three().size() == - ft2.get_one_three().size()); - CHECK(triangulation.N2_SL().size() == ft2.N2_SL().size()); + CHECK_EQ(triangulation.is_initialized(), ft2.is_initialized()); + CHECK_EQ(triangulation.number_of_finite_cells(), + ft2.number_of_finite_cells()); + CHECK_EQ(triangulation.min_time(), ft2.min_time()); + CHECK_EQ(triangulation.get_cells().size(), ft2.get_cells().size()); + CHECK_EQ(triangulation.get_three_one().size(), + ft2.get_three_one().size()); + CHECK_EQ(triangulation.get_two_two().size(), ft2.get_two_two().size()); + CHECK_EQ(triangulation.get_one_three().size(), + ft2.get_one_three().size()); + CHECK_EQ(triangulation.N2_SL().size(), ft2.N2_SL().size()); } } } } -SCENARIO("Detecting and fixing problems with vertices and cells", - "[triangulation]") +SCENARIO("Detecting and fixing problems with vertices and cells" * + doctest::test_suite("foliated_triangulation")) { spdlog::debug("Detecting and fixing problems with vertices and cells.\n"); - GIVEN("A FoliatedTriangulation3.") + GIVEN("A FoliatedTriangulation_3.") { WHEN("Constructing a triangulation with 4 correct vertices.") { - vector> Vertices{ + vector Vertices{ Point_t<3>{ 1, 0, 0}, Point_t<3>{ 0, 1, 0}, Point_t<3>{ 0, 0, 1}, Point_t<3>{RADIUS_2, RADIUS_2, RADIUS_2} }; - vector timevalues{1, 1, 1, 2}; - Causal_vertices_t<3> vertices; - vertices.reserve(Vertices.size()); - std::transform(Vertices.begin(), Vertices.end(), timevalues.begin(), - std::back_inserter(vertices), - [](auto vertex, std::size_t timevalue) { - return std::make_pair(vertex, timevalue); - }); - FoliatedTriangulation3 triangulation(vertices); + vector timevalues{1, 1, 1, 2}; + auto vertices = make_causal_vertices<3>(Vertices, timevalues); + FoliatedTriangulation_3 triangulation(vertices); THEN("No errors in the vertices are detected.") { CHECK(triangulation.check_all_vertices()); @@ -605,8 +665,7 @@ SCENARIO("Detecting and fixing problems with vertices and cells", auto break_vertices = [](Vertex_handle_t<3> const& vertex) { vertex->info() = 0; }; - for_each(triangulation.get_vertices().begin(), - triangulation.get_vertices().end(), break_vertices); + ranges::for_each(triangulation.get_vertices(), break_vertices); THEN("The incorrect vertex labelling is identified.") { @@ -634,8 +693,7 @@ SCENARIO("Detecting and fixing problems with vertices and cells", auto break_cells = [](Cell_handle_t<3> const& cell) { cell->info() = 0; }; - for_each(triangulation.get_cells().begin(), - triangulation.get_cells().end(), break_cells); + ranges::for_each(triangulation.get_cells(), break_cells); THEN("The incorrect cell labelling is identified.") { CHECK_FALSE(triangulation.check_all_cells()); @@ -658,181 +716,91 @@ SCENARIO("Detecting and fixing problems with vertices and cells", "Constructing a triangulation with an incorrect high timevalue " "vertex.") { - vector> vertices{ + vector vertices{ Point_t<3>{ 1, 0, 0}, Point_t<3>{ 0, 1, 0}, Point_t<3>{ 0, 0, 1}, Point_t<3>{RADIUS_2, RADIUS_2, RADIUS_2} }; - vector timevalues{1, 1, 1, std::numeric_limits::max()}; - Causal_vertices_t<3> causal_vertices; - causal_vertices.reserve(vertices.size()); - std::transform(vertices.begin(), vertices.end(), timevalues.begin(), - std::back_inserter(causal_vertices), - [](auto vertex, std::size_t timevalue) { - return std::make_pair(vertex, timevalue); - }); - FoliatedTriangulation3 triangulation(causal_vertices); - THEN("The vertex error is detected.") + vector timevalues{1, 1, 1, std::numeric_limits::max()}; + auto causal_vertices = make_causal_vertices<3>(vertices, timevalues); + FoliatedTriangulation_3 const triangulation(causal_vertices); + THEN("The vertex is fixed on construction.") { - CHECK_FALSE(triangulation.is_initialized()); - auto cell = triangulation.get_delaunay().finite_cells_begin(); - CHECK(expected_cell_type<3>(cell) == Cell_type::ACAUSAL); - // Human verification - fmt::print("Incorrect high timevalues vertex:\n"); - triangulation.print_vertices(); - fmt::print("Causes incorrect cell:\n"); - triangulation.print_cells(); - } - AND_THEN("The vertex error is fixed.") - { - CHECK(triangulation.fix_vertices()); - triangulation.print_vertices(); - fmt::print("But the cell is still incorrect.\n"); - CHECK_FALSE(triangulation.is_initialized()); - triangulation.print_cells(); - } - AND_THEN("The cell error is fixed.") - { - CHECK(triangulation.fix_vertices()); - fmt::print("Before fix_cells()\n"); - triangulation.print_cells(); - CHECK(triangulation.fix_cells()); - fmt::print("After fix_cells()\n"); - triangulation.print_cells(); + CHECK_FALSE(triangulation.fix_vertices()); CHECK(triangulation.is_initialized()); + triangulation.print_cells(); } } WHEN("Constructing a triangulation with an incorrect low value vertex.") { - vector> vertices{ + vector vertices{ Point_t<3>{0, 0, 0}, Point_t<3>{0, 1, 0}, Point_t<3>{1, 0, 0}, Point_t<3>{0, 0, 1} }; - vector timevalues{0, 2, 2, 2}; - Causal_vertices_t<3> causal_vertices; - causal_vertices.reserve(vertices.size()); - std::transform(vertices.begin(), vertices.end(), timevalues.begin(), - std::back_inserter(causal_vertices), - [](auto vertex, std::size_t timevalue) { - return std::make_pair(vertex, timevalue); - }); - FoliatedTriangulation3 triangulation(causal_vertices); - THEN("The vertex error is detected.") + vector timevalues{0, 2, 2, 2}; + auto causal_vertices = make_causal_vertices<3>(vertices, timevalues); + FoliatedTriangulation_3 const triangulation(causal_vertices); + THEN("The vertex is fixed on construction.") { - CHECK_FALSE(triangulation.is_initialized()); - auto cell = triangulation.get_delaunay().finite_cells_begin(); - CHECK(expected_cell_type<3>(cell) == Cell_type::ACAUSAL); - // Human verification - fmt::print("Incorrect low timevalues vertex:\n"); - triangulation.print_vertices(); - fmt::print("Causes incorrect cell:\n"); - triangulation.print_cells(); - } - AND_THEN("The vertex error is fixed.") - { - CHECK(triangulation.fix_vertices()); - triangulation.print_vertices(); - fmt::print("But the cell is still incorrect.\n"); - CHECK_FALSE(triangulation.is_initialized()); - triangulation.print_cells(); - } - AND_THEN("The cell error is fixed.") - { - CHECK(triangulation.fix_vertices()); - fmt::print("Before fix_cells()\n"); - triangulation.print_cells(); - CHECK(triangulation.fix_cells()); - fmt::print("After fix_cells()\n"); - triangulation.print_cells(); + CHECK_FALSE(triangulation.fix_vertices()); CHECK(triangulation.is_initialized()); + triangulation.print_cells(); } } WHEN( "Constructing a triangulation with two incorrect low values and two " "incorrect high values.") { - vector> vertices{ + vector vertices{ Point_t<3>{0, 0, 0}, Point_t<3>{0, 1, 0}, Point_t<3>{1, 0, 0}, Point_t<3>{0, 0, 1} }; - vector timevalues{0, 0, 2, 2}; - Causal_vertices_t<3> causal_vertices; - causal_vertices.reserve(vertices.size()); - std::transform(vertices.begin(), vertices.end(), timevalues.begin(), - std::back_inserter(causal_vertices), - [](auto vertex, std::size_t timevalue) { - return std::make_pair(vertex, timevalue); - }); - FoliatedTriangulation3 triangulation(causal_vertices); - THEN("Timevalue errors are detected.") - { - auto invalid_cells = foliated_triangulations::check_timevalues<3>( - triangulation.get_delaunay()); - CHECK_FALSE(invalid_cells->empty()); - } - THEN("The vertex errors are detected.") + vector timevalues{0, 0, 2, 2}; + auto causal_vertices = make_causal_vertices<3>(vertices, timevalues); + FoliatedTriangulation_3 const triangulation(causal_vertices); + THEN("The vertices are fixed on construction.") { - CHECK_FALSE(triangulation.is_initialized()); - // Human verification - fmt::print("Incorrect high timevalues vertex:\n"); - triangulation.print_vertices(); - fmt::print("Causes incorrect cell:\n"); - triangulation.print_cells(); - } - AND_THEN("The vertex errors are fixed.") - { - CHECK(triangulation.fix_vertices()); - triangulation.print_vertices(); - fmt::print("But the cell is still incorrect.\n"); - CHECK_FALSE(triangulation.is_initialized()); + CHECK_FALSE(triangulation.fix_vertices()); + CHECK(triangulation.is_initialized()); triangulation.print_cells(); } - AND_THEN("The cell error is fixed.") + AND_THEN("The cell type is correct.") { - CHECK(triangulation.fix_vertices()); - fmt::print("Before fix_cells()\n"); - triangulation.print_cells(); - CHECK(triangulation.fix_cells()); - fmt::print("After fix_cells()\n"); - triangulation.print_cells(); + CHECK_FALSE(triangulation.fix_vertices()); + CHECK_FALSE(triangulation.fix_cells()); CHECK(triangulation.is_initialized()); + triangulation.print_cells(); } } WHEN( "Constructing a triangulation with all vertices on the same timeslice.") { - vector> vertices{ + vector vertices{ Point_t<3>{1, 0, 0}, Point_t<3>{0, 1, 0}, Point_t<3>{0, 0, 1}, Point_t<3>{0, 0, -1} }; - vector timevalues{1, 1, 1, 1}; - Causal_vertices_t<3> causal_vertices; - causal_vertices.reserve(vertices.size()); - std::transform(vertices.begin(), vertices.end(), timevalues.begin(), - std::back_inserter(causal_vertices), - [](auto vertex, std::size_t timevalue) { - return std::make_pair(vertex, timevalue); - }); - FoliatedTriangulation3 triangulation(causal_vertices); + vector timevalues{1, 1, 1, 1}; + auto causal_vertices = make_causal_vertices<3>(vertices, timevalues); + FoliatedTriangulation_3 const triangulation(causal_vertices); THEN("The vertex error is detected.") { CHECK_FALSE(triangulation.is_initialized()); auto cell = triangulation.get_delaunay().finite_cells_begin(); - CHECK(expected_cell_type<3>(cell) == Cell_type::ACAUSAL); + CHECK_EQ(expected_cell_type<3>(cell), Cell_type::ACAUSAL); // Human verification triangulation.print_cells(); } } WHEN("Constructing a triangulation with an unfixable vertex.") { - vector> vertices{ + vector vertices{ Point_t<3>{1, 0, 0}, Point_t<3>{0, 1, 0}, Point_t<3>{0, 0, 1}, @@ -840,34 +808,36 @@ SCENARIO("Detecting and fixing problems with vertices and cells", Point_t<3>{2, 0, 0}, Point_t<3>{0, 3, 0} }; - vector timevalues{1, 1, 1, 2, 2, 3}; - Causal_vertices_t<3> causal_vertices; - causal_vertices.reserve(vertices.size()); - std::transform(vertices.begin(), vertices.end(), timevalues.begin(), - std::back_inserter(causal_vertices), - [](auto vertex, std::size_t timevalue) { - return std::make_pair(vertex, timevalue); - }); - Delaunay_t<3> delaunay_triangulation{causal_vertices.begin(), - causal_vertices.end()}; + vector timevalues{1, 1, 1, 2, 2, 3}; + auto causal_vertices = make_causal_vertices<3>(vertices, timevalues); + Delaunay_t<3> const delaunay_triangulation{causal_vertices.begin(), + causal_vertices.end()}; // Passing in a Delaunay triangulation directly allows us to skip the // normal construction process with sanity checks on the triangulation, // which is what we're testing here individually. - FoliatedTriangulation3 triangulation(delaunay_triangulation); + FoliatedTriangulation_3 triangulation(delaunay_triangulation); THEN("The incorrect cell can be identified.") { auto bad_cells = check_timevalues<3>(delaunay_triangulation); - CHECK(bad_cells.has_value()); - fmt::print("Bad cells:\n"); - print_cells<3>(bad_cells.value()); + CHECK_MESSAGE(bad_cells.has_value(), "No bad cells found."); + if (bad_cells) + { + fmt::print("Bad cells:\n"); + print_cells<3>(bad_cells.value()); + } } AND_THEN("The incorrect vertex can be identified.") { - auto bad_cells = check_timevalues<3>(delaunay_triangulation).value(); - auto bad_vertex = find_bad_vertex<3>(bad_cells.front()); - fmt::print("Bad vertex ({}) has timevalues {}.\n", bad_vertex->point(), - bad_vertex->info()); - CHECK(bad_vertex->info() == 3); + auto bad_cells = check_timevalues<3>(delaunay_triangulation); + CHECK_MESSAGE(bad_cells.has_value(), "No bad cells found."); + if (bad_cells) + { + auto bad_vertex = find_bad_vertex<3>(bad_cells->front()); + fmt::print("Bad vertex ({}) has timevalues {}.\n", + utilities::point_to_str(bad_vertex->point()), + bad_vertex->info()); + CHECK_EQ(bad_vertex->info(), 3); + } } AND_THEN("The triangulation is fixed.") { @@ -877,20 +847,21 @@ SCENARIO("Detecting and fixing problems with vertices and cells", triangulation.delaunay())); CHECK(triangulation.is_initialized()); fmt::print("Fixed triangulation:\n"); - print_cells<3>(get_all_finite_cells<3>(triangulation.delaunay())); + print_cells<3>(collect_cells<3>(triangulation.delaunay())); } } } } -SCENARIO("FoliatedTriangulation3 functions from Delaunay3", "[triangulation]") +SCENARIO("FoliatedTriangulation_3 functions from Delaunay3" * + doctest::test_suite("foliated_triangulation")) { - spdlog::debug("FoliatedTriangulation3 functions from Delaunay3.\n"); - GIVEN("A FoliatedTriangulation3.") + spdlog::debug("FoliatedTriangulation_3 functions from Delaunay3.\n"); + GIVEN("A FoliatedTriangulation_3.") { WHEN("Constructing a small triangulation.") { - vector> vertices{ + vector vertices{ Point_t<3>{1, 0, 0}, Point_t<3>{0, 1, 0}, Point_t<3>{0, 0, 1}, @@ -898,85 +869,76 @@ SCENARIO("FoliatedTriangulation3 functions from Delaunay3", "[triangulation]") Point_t<3>{2, 0, 0}, Point_t<3>{0, 3, 0} }; - vector timevalues{1, 1, 1, 2, 2, 3}; - Causal_vertices_t<3> causal_vertices; - causal_vertices.reserve(vertices.size()); - std::transform(vertices.begin(), vertices.end(), timevalues.begin(), - std::back_inserter(causal_vertices), - [](auto vertex, std::size_t timevalue) { - return std::make_pair(vertex, timevalue); - }); - FoliatedTriangulation3 triangulation(causal_vertices); + vector timevalues{1, 1, 1, 2, 2, 3}; + auto causal_vertices = make_causal_vertices<3>(vertices, timevalues); + FoliatedTriangulation_3 triangulation(causal_vertices); THEN("The Foliated triangulation is initially wrong.") { CHECK_FALSE(triangulation.is_initialized()); // Human verification +#ifndef NDEBUG fmt::print("Unfixed triangulation:\n"); triangulation.print_cells(); +#endif } THEN("After being fixed, Delaunay3 functions work as expected.") { // Fix the triangulation CHECK(triangulation.is_fixed()); - CHECK(triangulation.number_of_finite_cells() == 2); + CHECK_EQ(triangulation.number_of_finite_cells(), 2); fmt::print("Base Delaunay number of cells: {}\n", triangulation.number_of_finite_cells()); - CHECK(triangulation.number_of_finite_facets() == 7); + CHECK_EQ(triangulation.number_of_finite_facets(), 7); fmt::print("Base Delaunay number of faces: {}\n", triangulation.number_of_finite_facets()); triangulation.print_volume_per_timeslice(); - CHECK(triangulation.number_of_finite_edges() == 9); + CHECK_EQ(triangulation.number_of_finite_edges(), 9); fmt::print("Base Delaunay number of edges: {}\n", triangulation.number_of_finite_edges()); triangulation.print_edges(); - CHECK(triangulation.number_of_vertices() == 5); + CHECK_EQ(triangulation.number_of_vertices(), 5); fmt::print("Base Delaunay number of vertices: {}\n", triangulation.number_of_vertices()); - CHECK(triangulation.dimension() == 3); + CHECK_EQ(triangulation.dimension(), 3); fmt::print("Base Delaunay dimension is: {}\n", triangulation.dimension()); // Human verification +#ifndef NDEBUG utilities::print_delaunay(triangulation.delaunay()); +#endif } } WHEN("Constructing the default triangulation.") { - FoliatedTriangulation3 triangulation; + FoliatedTriangulation_3 const triangulation; REQUIRE(triangulation.is_initialized()); THEN("is_infinite() identifies a single infinite vertex.") { auto&& vertices = triangulation.get_delaunay().tds().vertices(); auto&& vertex = vertices.begin(); - CHECK(vertices.size() == 1); + CHECK_EQ(vertices.size(), 1); CHECK(triangulation.get_delaunay().tds().is_vertex(vertex)); CHECK(triangulation.is_infinite(vertex)); } } WHEN("Constructing a triangulation with 4 causal vertices.") { - vector> vertices{ + vector vertices{ Point_t<3>{ 1, 0, 0}, Point_t<3>{ 0, 1, 0}, Point_t<3>{ 0, 0, 1}, Point_t<3>{RADIUS_2, RADIUS_2, RADIUS_2} }; - vector timevalues{1, 1, 1, 2}; - Causal_vertices_t<3> causal_vertices; - causal_vertices.reserve(vertices.size()); - std::transform(vertices.begin(), vertices.end(), timevalues.begin(), - std::back_inserter(causal_vertices), - [](Point_t<3> vertex, std::size_t timevalue) { - return std::make_pair(vertex, timevalue); - }); - FoliatedTriangulation3 triangulation(causal_vertices); + vector timevalues{1, 1, 1, 2}; + auto causal_vertices = make_causal_vertices<3>(vertices, timevalues); + FoliatedTriangulation_3 triangulation(causal_vertices); REQUIRE(triangulation.is_initialized()); THEN("The degree of each vertex is 4 (including infinite vertex).") { auto check = [&triangulation](Vertex_handle_t<3> const& vertex) { - CHECK(triangulation.degree(vertex) == 4); + CHECK_EQ(triangulation.degree(vertex), 4); }; - for_each(triangulation.get_vertices().begin(), - triangulation.get_vertices().end(), check); + ranges::for_each(triangulation.get_vertices(), check); } } } diff --git a/tests/Function_ref_test.cpp b/tests/Function_ref_test.cpp index bb18ea9d97..67d816b768 100644 --- a/tests/Function_ref_test.cpp +++ b/tests/Function_ref_test.cpp @@ -10,7 +10,8 @@ /// @details Tests for lambdas and function_refs to store function objects for /// delayed calls -#include +#include + #include #include "Ergodic_moves_3.hpp" @@ -18,39 +19,39 @@ using namespace std; using namespace manifolds; -SCENARIO("Simple Lambda operations", "[function-ref]") +SCENARIO("Simple Lambda operations" * doctest::test_suite("function_ref")) { - constexpr auto increment_lambda = [](int a) { return ++a; }; + auto constexpr increment_lambda = [](int a) { return ++a; }; // NOLINT GIVEN("A simple lambda.") { WHEN("Lambda is called with 0.") { - THEN("We should get 1.") { REQUIRE(increment_lambda(0) == 1); } + THEN("We should get 1.") { REQUIRE_EQ(increment_lambda(0), 1); } } WHEN("Lambda is called with 1.") { - THEN("We should get 2.") { REQUIRE(increment_lambda(1) == 2); } + THEN("We should get 2.") { REQUIRE_EQ(increment_lambda(1), 2); } } WHEN("Lambda is called with 5.") { - THEN("We should get 6.") { REQUIRE(increment_lambda(5) == 6); } + THEN("We should get 6.") { REQUIRE_EQ(increment_lambda(5), 6); } } } } -SCENARIO("Complex lambda operations", "[function-ref]") +SCENARIO("Complex lambda operations" * doctest::test_suite("function_ref")) { GIVEN("A lambda storing a move.") { auto constexpr desired_simplices = 640; auto constexpr desired_timeslices = 4; - Manifold3 manifold(desired_simplices, desired_timeslices); + Manifold_3 manifold(desired_simplices, desired_timeslices); REQUIRE(manifold.is_correct()); WHEN("A lambda is constructed for a move.") { - auto const move23 = [](Manifold3& m) { + auto const move23 = [](Manifold_3& m) { // NOLINT return ergodic_moves::do_23_move(m).value(); }; THEN("Running the lambda makes the move.") @@ -69,25 +70,24 @@ SCENARIO("Complex lambda operations", "[function-ref]") } } -SCENARIO("Function_ref operations", "[function-ref]") +SCENARIO("Function_ref operations" * doctest::test_suite("function_ref")) { GIVEN("A simple lambda stored in a function_ref.") { - auto const increment = [](int incr) { return ++incr; }; - tl::function_ref lambda_ref(increment); + auto const increment = [](int incr) { return ++incr; }; + tl::function_ref const lambda_ref(increment); WHEN("Function_ref is called with 0.") { - THEN("We should get 1.") { REQUIRE(lambda_ref(1) == 2); } + THEN("We should get 1.") { REQUIRE_EQ(lambda_ref(1), 2); } } } GIVEN("A function pointer to a move stored in a function_ref.") { auto constexpr desired_simplices = 640; auto constexpr desired_timeslices = 4; - Manifold3 manifold(desired_simplices, desired_timeslices); + Manifold_3 manifold(desired_simplices, desired_timeslices); REQUIRE(manifold.is_correct()); - tl::function_ref(Manifold3&)> - complex_ref(ergodic_moves::do_23_move); + tl::function_ref const complex_ref(ergodic_moves::do_23_move); WHEN("The function_ref is invoked.") { auto result = complex_ref(manifold); @@ -108,12 +108,12 @@ SCENARIO("Function_ref operations", "[function-ref]") { auto constexpr desired_simplices = 640; auto constexpr desired_timeslices = 4; - Manifold3 manifold(desired_simplices, desired_timeslices); + Manifold_3 manifold(desired_simplices, desired_timeslices); REQUIRE(manifold.is_correct()); - auto const move23 = [](Manifold3& m) { - return ergodic_moves::do_23_move(m).value(); + auto const move23 = [](Manifold_3& t_manifold) { + return ergodic_moves::do_23_move(t_manifold).value(); }; - tl::function_ref complex_ref(move23); + tl::function_ref const complex_ref(move23); WHEN("The function_ref is invoked.") { auto result = complex_ref(manifold); diff --git a/tests/Geometry_test.cpp b/tests/Geometry_test.cpp index d31f65639b..be95082436 100644 --- a/tests/Geometry_test.cpp +++ b/tests/Geometry_test.cpp @@ -10,12 +10,13 @@ #include "Geometry.hpp" -#include +#include using namespace std; using namespace foliated_triangulations; -SCENARIO("Geometry special member and swap properties", "[geometry]") +SCENARIO("Geometry special member and swap properties" * + doctest::test_suite("geometry")) { spdlog::debug("Geometry special member and swap properties.\n"); GIVEN("A 3-dimensional geometry.") @@ -24,44 +25,44 @@ SCENARIO("Geometry special member and swap properties", "[geometry]") { THEN("It is trivially destructible.") { - REQUIRE(is_trivially_destructible_v); + REQUIRE(is_trivially_destructible_v); spdlog::debug("It is trivially destructible.\n"); } THEN("It is no-throw default constructible.") { - REQUIRE(is_nothrow_default_constructible_v); + REQUIRE(is_nothrow_default_constructible_v); spdlog::debug("It is no-throw default constructible.\n"); } THEN("It is no-throw copy constructible.") { - REQUIRE(is_nothrow_copy_constructible_v); + REQUIRE(is_nothrow_copy_constructible_v); spdlog::debug("It is no-throw copy constructible.\n"); } THEN("It is no-throw copy assignable.") { - REQUIRE(is_nothrow_copy_assignable_v); + REQUIRE(is_nothrow_copy_assignable_v); spdlog::debug("It is no-throw copy assignable.\n"); } THEN("It is no-throw move constructible.") { - REQUIRE(is_nothrow_move_constructible_v); + REQUIRE(is_nothrow_move_constructible_v); spdlog::debug("It is no-throw move constructible.\n"); } THEN("It is no-throw move assignable.") { - REQUIRE(is_nothrow_move_assignable_v); + REQUIRE(is_nothrow_move_assignable_v); spdlog::debug("It is no-throw move assignable.\n"); } THEN("It is no-throw swappable.") { - REQUIRE(is_nothrow_swappable_v); + REQUIRE(is_nothrow_swappable_v); spdlog::debug("It is no-throw swappable.\n"); } } } } -SCENARIO("3-Geometry classification", "[geometry]") +SCENARIO("3-Geometry classification" * doctest::test_suite("geometry")) { spdlog::debug("3-Geometry classification.\n"); GIVEN("A small 3-dimensional geometry.") @@ -70,9 +71,9 @@ SCENARIO("3-Geometry classification", "[geometry]") { auto constexpr desired_simplices = 72; auto constexpr desired_timeslices = 3; - FoliatedTriangulation3 triangulation(desired_simplices, - desired_timeslices); - Geometry3 geometry(triangulation); + FoliatedTriangulation_3 const triangulation(desired_simplices, + desired_timeslices); + Geometry_3 geometry(triangulation); THEN("The Delaunay triangulation is described by the geometry.") { fmt::print("There are {} simplices ...\n", geometry.N3); @@ -80,32 +81,34 @@ SCENARIO("3-Geometry classification", "[geometry]") "There are {} (3,1) simplices and {} (2,2) simplices and {} (1,3) " "simplices.\n", geometry.N3_31, geometry.N3_22, geometry.N3_13); - CHECK(geometry.N3 > 2); - CHECK(geometry.N3 == static_cast( - triangulation.number_of_finite_cells())); - CHECK(geometry.N3_31 == - static_cast(triangulation.get_three_one().size())); - CHECK(geometry.N3_13 == - static_cast(triangulation.get_one_three().size())); - CHECK(geometry.N3_31 + geometry.N3_22 + geometry.N3_13 == geometry.N3); - CHECK(geometry.N3_22 == - static_cast(triangulation.get_two_two().size())); - CHECK(geometry.N2 == static_cast( - triangulation.number_of_finite_facets())); - CHECK(geometry.N1 == static_cast( - triangulation.number_of_finite_edges())); - CHECK_FALSE(geometry.N1_TL == 0); - CHECK_FALSE(geometry.N1_SL == 0); - CHECK(geometry.N1 == geometry.N1_TL + geometry.N1_SL); - CHECK(geometry.N0 == - static_cast(triangulation.number_of_vertices())); + CHECK_GT(geometry.N3, 2); + CHECK_EQ(geometry.N3, static_cast( + triangulation.number_of_finite_cells())); + CHECK_EQ(geometry.N3_31, static_cast( + triangulation.get_three_one().size())); + CHECK_EQ(geometry.N3_13, static_cast( + triangulation.get_one_three().size())); + CHECK_EQ(geometry.N3_31 + geometry.N3_22 + geometry.N3_13, geometry.N3); + CHECK_EQ(geometry.N3_22, static_cast( + triangulation.get_two_two().size())); + CHECK_EQ(geometry.N2, static_cast( + triangulation.number_of_finite_facets())); + CHECK_EQ(geometry.N1, static_cast( + triangulation.number_of_finite_edges())); + CHECK_NE(geometry.N1_TL, 0); + CHECK_NE(geometry.N1_SL, 0); + CHECK_EQ(geometry.N1, geometry.N1_TL + geometry.N1_SL); + CHECK_EQ(geometry.N0, static_cast( + triangulation.number_of_vertices())); // Human verification - triangulation.print_cells(); fmt::print("There are {} edges.\n", geometry.N1); fmt::print("There are {} timelike edges and {} spacelike edges.\n", geometry.N1_TL, geometry.N1_SL); +#ifndef NDEBUG + triangulation.print_cells(); triangulation.print_edges(); +#endif fmt::print( "There are {} vertices with a max timevalue of {} and a min " "timevalue of {}.\n", @@ -116,56 +119,56 @@ SCENARIO("3-Geometry classification", "[geometry]") } } -SCENARIO("3-Geometry initialization", "[geometry]") +SCENARIO("3-Geometry initialization" * doctest::test_suite("geometry")) { spdlog::debug("3-Geometry initialization.\n"); GIVEN("A 3-dimensional geometry.") { WHEN("It is default constructed.") { - Geometry3 geometry; THEN("All data members are zero-initialized.") { - REQUIRE(geometry.N3 == 0); - REQUIRE(geometry.N3_31 == 0); - REQUIRE(geometry.N3_13 == 0); - REQUIRE(geometry.N3_22 == 0); - REQUIRE(geometry.N2 == 0); - REQUIRE(geometry.N1 == 0); - REQUIRE(geometry.N1_TL == 0); - REQUIRE(geometry.N1_SL == 0); - REQUIRE(geometry.N0 == 0); + Geometry_3 constexpr geometry; + REQUIRE_EQ(geometry.N3, 0); + REQUIRE_EQ(geometry.N3_31, 0); + REQUIRE_EQ(geometry.N3_13, 0); + REQUIRE_EQ(geometry.N3_22, 0); + REQUIRE_EQ(geometry.N2, 0); + REQUIRE_EQ(geometry.N1, 0); + REQUIRE_EQ(geometry.N1_TL, 0); + REQUIRE_EQ(geometry.N1_SL, 0); + REQUIRE_EQ(geometry.N0, 0); } } WHEN("It is constructed with a triangulation.") { auto constexpr desired_simplices = 640; auto constexpr desired_timeslices = 4; - FoliatedTriangulation3 triangulation(desired_simplices, - desired_timeslices); - Geometry3 geometry(triangulation); + FoliatedTriangulation_3 const triangulation(desired_simplices, + desired_timeslices); + Geometry_3 const geometry(triangulation); THEN( "The properties of the Delaunay triangulation are saved in geometry " "info.") { - CHECK(geometry.N3 == static_cast( - triangulation.number_of_finite_cells())); - CHECK(geometry.N3_31 == - static_cast(triangulation.get_three_one().size())); - CHECK(geometry.N3_13 == - static_cast(triangulation.get_one_three().size())); - CHECK(geometry.N3_31 + geometry.N3_22 + geometry.N3_13 == geometry.N3); - CHECK(geometry.N3_22 == - static_cast(triangulation.get_two_two().size())); - CHECK(geometry.N2 == static_cast( - triangulation.number_of_finite_facets())); - CHECK(geometry.N1 == static_cast( - triangulation.number_of_finite_edges())); - CHECK_FALSE(geometry.N1_TL == 0); - CHECK_FALSE(geometry.N1_SL == 0); - CHECK(geometry.N1_TL + geometry.N1_SL == geometry.N1); - CHECK(geometry.N0 == - static_cast(triangulation.number_of_vertices())); + CHECK_EQ(geometry.N3, static_cast( + triangulation.number_of_finite_cells())); + CHECK_EQ(geometry.N3_31, static_cast( + triangulation.get_three_one().size())); + CHECK_EQ(geometry.N3_13, static_cast( + triangulation.get_one_three().size())); + CHECK_EQ(geometry.N3_31 + geometry.N3_22 + geometry.N3_13, geometry.N3); + CHECK_EQ(geometry.N3_22, static_cast( + triangulation.get_two_two().size())); + CHECK_EQ(geometry.N2, static_cast( + triangulation.number_of_finite_facets())); + CHECK_EQ(geometry.N1, static_cast( + triangulation.number_of_finite_edges())); + CHECK_NE(geometry.N1_TL, 0); + CHECK_NE(geometry.N1_SL, 0); + CHECK_EQ(geometry.N1_TL + geometry.N1_SL, geometry.N1); + CHECK_EQ(geometry.N0, static_cast( + triangulation.number_of_vertices())); triangulation.print(); triangulation.print_volume_per_timeslice(); } diff --git a/tests/Manifold_test.cpp b/tests/Manifold_test.cpp index 67d342faa6..886c0bd4e0 100644 --- a/tests/Manifold_test.cpp +++ b/tests/Manifold_test.cpp @@ -10,14 +10,17 @@ #include "Manifold.hpp" -#include +#include + +#include using namespace std; using namespace manifolds; -static inline double const RADIUS_2 = std::sqrt(4.0 / 3.0); // NOLINT +static inline auto constexpr RADIUS_2 = 2.0 * std::numbers::inv_sqrt3_v; -SCENARIO("Manifold special member and swap properties", "[manifold]") +SCENARIO("Manifold special member and swap properties" * + doctest::test_suite("manifold")) { spdlog::debug("Manifold special member and swap properties.\n"); GIVEN("A 3-dimensional manifold.") @@ -26,72 +29,72 @@ SCENARIO("Manifold special member and swap properties", "[manifold]") { THEN("It is no-throw destructible.") { - REQUIRE(is_nothrow_destructible_v); + REQUIRE(is_nothrow_destructible_v); spdlog::debug("It is no-throw destructible.\n"); } THEN("It is default constructible.") { - REQUIRE(is_default_constructible_v); + REQUIRE(is_default_constructible_v); spdlog::debug("It is default constructible.\n"); } THEN("It is NOT trivially constructible.") { - CHECK_FALSE(is_trivially_constructible_v); + CHECK_FALSE(is_trivially_constructible_v); } THEN("It is NOT trivially default constructible.") { - CHECK_FALSE(is_trivially_default_constructible_v); + CHECK_FALSE(is_trivially_default_constructible_v); } THEN("It is no-throw copy constructible.") { - REQUIRE(is_nothrow_copy_constructible_v); + REQUIRE(is_nothrow_copy_constructible_v); spdlog::debug("It is no-throw copy constructible.\n"); } THEN("It is no-throw copy assignable.") { - REQUIRE(is_nothrow_copy_assignable_v); + REQUIRE(is_nothrow_copy_assignable_v); spdlog::debug("It is no-throw copy assignable.\n"); } THEN("It is no-throw move constructible.") { - REQUIRE(is_nothrow_move_constructible_v); + REQUIRE(is_nothrow_move_constructible_v); spdlog::debug("It is no-throw move constructible.\n"); } THEN("It is no-throw move assignable.") { - REQUIRE(is_nothrow_move_assignable_v); + REQUIRE(is_nothrow_move_assignable_v); spdlog::debug("It is no-throw move assignable.\n"); } THEN("It is no-throw swappable.") { - REQUIRE(is_nothrow_swappable_v); + REQUIRE(is_nothrow_swappable_v); spdlog::debug("It is no-throw swappable.\n"); } THEN("It is constructible from a FoliatedTriangulation.") { REQUIRE(is_constructible_v< - Manifold3, foliated_triangulations::FoliatedTriangulation3>); + Manifold_3, foliated_triangulations::FoliatedTriangulation_3>); spdlog::debug("It is constructible from a FoliatedTriangulation.\n"); } THEN("It is constructible from 2 parameters.") { - REQUIRE(is_constructible_v); + REQUIRE(is_constructible_v); spdlog::debug("It is constructible from 2 parameters.\n"); } THEN("It is constructible from 4 parameters.") { - REQUIRE(is_constructible_v); spdlog::debug("It is constructible from 4 parameters.\n"); } THEN("It is constructible from Causal_vertices.") { - REQUIRE(is_constructible_v>); + REQUIRE(is_constructible_v>); spdlog::debug("It is constructible from Causal_vertices.\n"); } THEN("It is constructible from Causal_vertices and INITIAL_RADIUS.") { - REQUIRE(is_constructible_v, double>); + REQUIRE(is_constructible_v, double>); spdlog::debug( "It is constructible from Causal_vertices and INITIAL_RADIUS.\n"); } @@ -99,7 +102,7 @@ SCENARIO("Manifold special member and swap properties", "[manifold]") "It is constructible from Causal_vertices, INITIAL_RADIUS, and " "RADIAL_SEPARATION.") { - REQUIRE(is_constructible_v, double, + REQUIRE(is_constructible_v, double, double>); spdlog::debug( "It is constructible from Causal_vertices, INITIAL_RADIUS, and " @@ -109,40 +112,136 @@ SCENARIO("Manifold special member and swap properties", "[manifold]") } } -SCENARIO("Manifold static members", "[manifold]") +SCENARIO("Manifold free functions" * doctest::test_suite("manifold")) +{ + spdlog::debug("manifolds:: functions.\n"); + + GIVEN("A vector of points and timevalues.") + { + vector const Vertices{Point_t<3>(1, 0, 0), Point_t<3>(0, 1, 0), + Point_t<3>(0, 0, 1), + Point_t<3>(RADIUS_2, RADIUS_2, RADIUS_2)}; + vector const Timevalues{1, 1, 1, 2}; + WHEN("Causal vertices are created.") + { + auto causal_vertices = + manifolds::make_causal_vertices<3>(Vertices, Timevalues); + THEN("They are correct.") + { + REQUIRE_EQ(causal_vertices.size(), 4); + REQUIRE_EQ(causal_vertices[0].first, Point_t<3>(1, 0, 0)); + REQUIRE_EQ(causal_vertices[0].second, 1); + REQUIRE_EQ(causal_vertices[1].first, Point_t<3>(0, 1, 0)); + REQUIRE_EQ(causal_vertices[1].second, 1); + REQUIRE_EQ(causal_vertices[2].first, Point_t<3>(0, 0, 1)); + REQUIRE_EQ(causal_vertices[2].second, 1); + REQUIRE_EQ(causal_vertices[3].first, + Point_t<3>(RADIUS_2, RADIUS_2, RADIUS_2)); + REQUIRE_EQ(causal_vertices[3].second, 2); + } + } + } + GIVEN("A mismatched set of points and timevalues.") + { + vector const Vertices{Point_t<3>(1, 0, 0), Point_t<3>(0, 1, 0), + Point_t<3>(0, 0, 1), + Point_t<3>(RADIUS_2, RADIUS_2, RADIUS_2)}; + vector const Timevalues{1, 1, 1}; + WHEN("Causal vertices are created.") + { + THEN("An exception is thrown.") + { + REQUIRE_THROWS( + manifolds::make_causal_vertices<3>(Vertices, Timevalues)); + } + } + } + GIVEN("4 points.") + { + using Point = Point_t<3>; + auto p_1 = Point(1, 0, 0); + auto p_2 = Point(0, 1, 0); + auto p_3 = Point(0, 0, 1); + auto p_4 = Point(RADIUS_2, RADIUS_2, RADIUS_2); + vector const Vertices{p_1, p_2, p_3, p_4}; + vector const Timevalues{1, 1, 1, 2}; + auto causal_vertices = + manifolds::make_causal_vertices<3>(Vertices, Timevalues); + + WHEN("The manifold is constructed.") + { + Manifold_3 const manifold(causal_vertices, 1, 1.0); + THEN("It is correct.") + { + REQUIRE(manifold.is_correct()); + manifold.print(); + manifold.print_details(); + manifold.print_vertices(); + } + THEN("We can obtain the vertices from the points.") + { + Vertex_handle_t<3> const v_1 = manifold.get_vertex(p_1); + CHECK(v_1->is_valid()); + cout << "v_1 contains point " << v_1->point() << '\n'; + /// @todo Why is this false? + // CHECK(manifold.get_delaunay().is_vertex(v_1)); + } + THEN("We can obtain the cell from the vertices.") + { + Vertex_handle_t<3> const v_1 = manifold.get_vertex(p_1); + Vertex_handle_t<3> const v_2 = manifold.get_vertex(p_2); + Vertex_handle_t<3> const v_3 = manifold.get_vertex(p_3); + Vertex_handle_t<3> const v_4 = manifold.get_vertex(p_4); + auto const& cell = manifold.get_cell(v_1, v_2, v_3, v_4); + CHECK(cell->is_valid()); + /// @todo Why is this false? + // CHECK(manifold.get_delaunay().is_cell(cell)); + // We have to have a valid Cell handle to obtain a tetrahedron + auto tetrahedron = manifold.get_delaunay().tetrahedron(cell); + CHECK_FALSE(tetrahedron.is_degenerate()); + cout << "Vertex 0 of tetrahedron is " << tetrahedron.vertex(0) << '\n'; + cout << "Vertex 1 of tetrahedron is " << tetrahedron.vertex(1) << '\n'; + cout << "Vertex 2 of tetrahedron is " << tetrahedron.vertex(2) << '\n'; + cout << "Vertex 3 of tetrahedron is " << tetrahedron.vertex(3) << '\n'; + } + } + } +} + +SCENARIO("Manifold static members" * doctest::test_suite("manifold")) { spdlog::debug("Manifold static members.\n"); - GIVEN("A default constructed Manifold3") + GIVEN("A default constructed Manifold_3") { - Manifold3 test{}; + Manifold_3 const test{}; WHEN("The dimensionality of the manifold is queried.") { THEN("The correct dimensionality is returned.") { - REQUIRE(test.dimension == 3); + REQUIRE_EQ(test.dimension, 3); } } } } -SCENARIO("Manifold functions", "[manifold]") +SCENARIO("Manifold functions" * doctest::test_suite("manifold")) { spdlog::debug("Manifold functions.\n"); - GIVEN("A manifold with four vertices.") + GIVEN("A 3-manifold with four vertices.") { Causal_vertices_t<3> causal_vertices; causal_vertices.emplace_back(Point_t<3>(1, 0, 0), 1); causal_vertices.emplace_back(Point_t<3>(0, 1, 0), 1); causal_vertices.emplace_back(Point_t<3>(0, 0, 1), 1); causal_vertices.emplace_back(Point_t<3>(RADIUS_2, RADIUS_2, RADIUS_2), 2); - Manifold3 manifold(causal_vertices); + Manifold_3 const manifold(causal_vertices); REQUIRE(manifold.is_correct()); WHEN("are_vertex_timevalues_valid() is called.") { THEN("The vertices have valid timevalues.") { - REQUIRE(manifold.N0() == 4); + REQUIRE_EQ(manifold.N0(), 4); CHECK(manifold.is_correct()); // Human verification manifold.print_vertices(); @@ -150,8 +249,8 @@ SCENARIO("Manifold functions", "[manifold]") } AND_WHEN("The vertices are mis-labelled.") { - for (std::span vertices(manifold.get_vertices()); - auto const& vertex : vertices) + for (std::span const vertices(manifold.get_vertices()); + auto const& vertex : vertices) { vertex->info() = std::numeric_limits::max(); } @@ -165,66 +264,77 @@ SCENARIO("Manifold functions", "[manifold]") } } -SCENARIO("3-Manifold initialization", "[manifold]") +SCENARIO("3-Manifold initialization" * doctest::test_suite("manifold")) { spdlog::debug("Manifold initialization.\n"); + using Point = Point_t<3>; GIVEN("A 3-manifold.") { WHEN("It is default constructed.") { - Manifold3 manifold; + Manifold_3 const manifold; THEN("The triangulation is valid.") { - REQUIRE_THAT(typeid(manifold.get_triangulation()).name(), - Catch::Contains("FoliatedTriangulation")); + auto const& manifold_type = typeid(manifold.get_triangulation()).name(); + std::string manifold_string{manifold_type}; + CHECK_NE(manifold_string.find("FoliatedTriangulation"), + std::string::npos); fmt::print("The triangulation data structure is of type {}\n", - typeid(manifold.get_triangulation()).name()); + manifold_string); REQUIRE(manifold.is_delaunay()); REQUIRE(manifold.is_valid()); } THEN("The geometry is of type geometry class.") { - REQUIRE_THAT(typeid(manifold.get_geometry()).name(), - Catch::Contains("Geometry")); + auto const& geometry_type = typeid(manifold.get_geometry()).name(); + std::string geometry_string{geometry_type}; + CHECK_NE(geometry_string.find("Geometry"), std::string::npos); fmt::print("The Geometry data structure is of type {}\n", - typeid(manifold.get_geometry()).name()); + geometry_string); } } WHEN("It is constructed from causal vertices.") { - Causal_vertices_t<3> causal_vertices; - causal_vertices.emplace_back(Point_t<3>(0, 0, 0), 1); - causal_vertices.emplace_back(Point_t<3>(1, 0, 0), 2); - causal_vertices.emplace_back(Point_t<3>(0, 1, 0), 2); - causal_vertices.emplace_back(Point_t<3>(0, 0, 1), 2); - causal_vertices.emplace_back(Point_t<3>(RADIUS_2, RADIUS_2, RADIUS_2), 3); - Manifold3 manifold(causal_vertices, 0, 1.0); - + vector const Vertices{Point(0, 0, 0), Point(1, 0, 0), Point(0, 1, 0), + Point(0, 0, 1), + Point(RADIUS_2, RADIUS_2, RADIUS_2)}; + vector const Timevalues{1, 2, 2, 2, 3}; + auto causal_vertices = + manifolds::make_causal_vertices<3>(Vertices, Timevalues); + Manifold_3 const manifold(causal_vertices, 0, 1.0); THEN("The triangulation is valid.") { - REQUIRE_THAT(typeid(manifold.get_triangulation()).name(), - Catch::Contains("FoliatedTriangulation")); - REQUIRE(manifold.is_correct()); + auto const& manifold_type = typeid(manifold.get_triangulation()).name(); + std::string manifold_string{manifold_type}; + CHECK_NE(manifold_string.find("FoliatedTriangulation"), + std::string::npos); + fmt::print("The triangulation data structure is of type {}\n", + manifold_string); + REQUIRE(manifold.is_delaunay()); + REQUIRE(manifold.is_valid()); } THEN("The geometry is of type geometry class.") { - REQUIRE_THAT(typeid(manifold.get_geometry()).name(), - Catch::Contains("Geometry")); + auto const& geometry_type = typeid(manifold.get_geometry()).name(); + std::string geometry_string{geometry_type}; + CHECK_NE(geometry_string.find("Geometry"), std::string::npos); + fmt::print("The Geometry data structure is of type {}\n", + geometry_string); } THEN("The geometry matches the triangulation.") { REQUIRE(manifold.is_foliated()); - REQUIRE(manifold.N0() == 5); - REQUIRE(manifold.N1_SL() == 3); - REQUIRE(manifold.N1_TL() == 6); + REQUIRE_EQ(manifold.N0(), 5); + REQUIRE_EQ(manifold.N1_SL(), 3); + REQUIRE_EQ(manifold.N1_TL(), 6); // How many spacelike facets have a timevalue of 2? Should be 1. - REQUIRE(manifold.N2_SL().count(2) == 1); + REQUIRE_EQ(manifold.N2_SL().count(2), 1); // There shouldn't be spacelike facets with other time values. - REQUIRE(manifold.N2_SL().count(1) == 0); - REQUIRE(manifold.N2_SL().count(3) == 0); - REQUIRE(manifold.N3() == 2); - REQUIRE(manifold.min_time() == 1); - REQUIRE(manifold.max_time() == 3); + REQUIRE_EQ(manifold.N2_SL().count(1), 0); + REQUIRE_EQ(manifold.N2_SL().count(3), 0); + REQUIRE_EQ(manifold.N3(), 2); + REQUIRE_EQ(manifold.min_time(), 1); + REQUIRE_EQ(manifold.max_time(), 3); REQUIRE(manifold.check_simplices()); // Human verification manifold.print(); @@ -233,39 +343,49 @@ SCENARIO("3-Manifold initialization", "[manifold]") } WHEN("It is constructed from a Foliated triangulation.") { - Causal_vertices_t<3> causal_vertices; - causal_vertices.emplace_back(Point_t<3>(0, 0, 0), 1); - causal_vertices.emplace_back(Point_t<3>(1, 0, 0), 2); - causal_vertices.emplace_back(Point_t<3>(0, 1, 0), 2); - causal_vertices.emplace_back(Point_t<3>(0, 0, 1), 2); - causal_vertices.emplace_back(Point_t<3>(RADIUS_2, RADIUS_2, RADIUS_2), 3); - Manifold3 manifold(causal_vertices, 0, 1.0); - + vector const Vertices{Point(0, 0, 0), Point(1, 0, 0), Point(0, 1, 0), + Point(0, 0, 1), + Point(RADIUS_2, RADIUS_2, RADIUS_2)}; + vector const Timevalues{1, 2, 2, 2, 3}; + auto causal_vertices = + manifolds::make_causal_vertices<3>(Vertices, Timevalues); + foliated_triangulations::FoliatedTriangulation_3 const + foliated_triangulation(causal_vertices, 0, 1.0); + Manifold_3 const manifold(foliated_triangulation); + CHECK_EQ(manifold.get_delaunay(), foliated_triangulation.get_delaunay()); THEN("The triangulation is valid.") { - REQUIRE_THAT(typeid(manifold.get_triangulation()).name(), - Catch::Contains("FoliatedTriangulation")); - REQUIRE(manifold.is_correct()); + auto const& manifold_type = typeid(manifold.get_triangulation()).name(); + std::string manifold_string{manifold_type}; + CHECK_NE(manifold_string.find("FoliatedTriangulation"), + std::string::npos); + fmt::print("The triangulation data structure is of type {}\n", + manifold_string); + REQUIRE(manifold.is_delaunay()); + REQUIRE(manifold.is_valid()); } THEN("The geometry is of type geometry class.") { - REQUIRE_THAT(typeid(manifold.get_geometry()).name(), - Catch::Contains("Geometry")); + auto const& geometry_type = typeid(manifold.get_geometry()).name(); + std::string geometry_string{geometry_type}; + CHECK_NE(geometry_string.find("Geometry"), std::string::npos); + fmt::print("The Geometry data structure is of type {}\n", + geometry_string); } THEN("The geometry matches the triangulation.") { REQUIRE(manifold.is_foliated()); - REQUIRE(manifold.N0() == 5); - REQUIRE(manifold.N1_SL() == 3); - REQUIRE(manifold.N1_TL() == 6); + REQUIRE_EQ(manifold.N0(), 5); + REQUIRE_EQ(manifold.N1_SL(), 3); + REQUIRE_EQ(manifold.N1_TL(), 6); // How many spacelike facets have a timevalue of 2? Should be 1. - REQUIRE(manifold.N2_SL().count(2) == 1); + CHECK_EQ(manifold.N2_SL().count(2), 1); // There shouldn't be spacelike facets with other time values. - REQUIRE(manifold.N2_SL().count(1) == 0); - REQUIRE(manifold.N2_SL().count(3) == 0); - REQUIRE(manifold.N3() == 2); - REQUIRE(manifold.min_time() == 1); - REQUIRE(manifold.max_time() == 3); + CHECK_EQ(manifold.N2_SL().count(1), 0); + REQUIRE_EQ(manifold.N2_SL().count(3), 0); + REQUIRE_EQ(manifold.N3(), 2); + CHECK_EQ(manifold.min_time(), 1); + CHECK_EQ(manifold.max_time(), 3); REQUIRE(manifold.check_simplices()); // Human verification manifold.print(); @@ -276,34 +396,26 @@ SCENARIO("3-Manifold initialization", "[manifold]") { auto constexpr desired_simplices = 2; auto constexpr desired_timeslices = 2; - Manifold3 manifold(desired_simplices, desired_timeslices); + Manifold_3 const manifold(desired_simplices, desired_timeslices); THEN("Triangulation is valid.") { REQUIRE(manifold.is_correct()); } THEN("The geometry matches the triangulation.") { - using Catch::Matchers::Predicate; - REQUIRE(manifold.is_foliated()); - REQUIRE(manifold.vertices() == manifold.N0()); - REQUIRE(manifold.edges() == manifold.N1()); - REQUIRE(manifold.faces() == manifold.N2()); + REQUIRE_EQ(manifold.vertices(), manifold.N0()); + REQUIRE_EQ(manifold.edges(), manifold.N1()); + REQUIRE_EQ(manifold.faces(), manifold.N2()); REQUIRE(manifold.check_simplices()); // We have 1 to 8 vertices auto number_of_vertices{manifold.N0()}; - CHECK_THAT(number_of_vertices, Predicate( - [](int const value) -> bool { - return (1 <= value && value <= 8); - }, - "There should be 1 to 8 vertices.")); + CHECK_GE(number_of_vertices, 1); + CHECK_LE(number_of_vertices, 8); // We have 1 to 12 number_of_cells auto number_of_cells{manifold.N3()}; - CHECK_THAT(number_of_cells, Predicate( - [](int const value) -> bool { - return (1 <= value && value <= 12); - }, - "There should be 1 to 12 cells.")); + CHECK_GE(number_of_cells, 1); + CHECK_LE(number_of_cells, 12); // We have all the time values - CHECK(manifold.min_time() == 1); - CHECK(manifold.max_time() == desired_timeslices); + CHECK_EQ(manifold.min_time(), 1); + CHECK_EQ(manifold.max_time(), desired_timeslices); // Human verification manifold.print(); manifold.print_volume_per_timeslice(); @@ -313,14 +425,14 @@ SCENARIO("3-Manifold initialization", "[manifold]") { auto constexpr desired_simplices = 640; auto constexpr desired_timeslices = 4; - Manifold3 manifold(desired_simplices, desired_timeslices); + Manifold_3 const manifold(desired_simplices, desired_timeslices); THEN("Triangulation is valid.") { REQUIRE(manifold.is_correct()); } THEN("The geometry matches the triangulation.") { REQUIRE(manifold.is_foliated()); - REQUIRE(manifold.vertices() == manifold.N0()); - REQUIRE(manifold.edges() == manifold.N1()); - REQUIRE(manifold.faces() == manifold.N2()); + REQUIRE_EQ(manifold.vertices(), manifold.N0()); + REQUIRE_EQ(manifold.edges(), manifold.N1()); + REQUIRE_EQ(manifold.faces(), manifold.N2()); REQUIRE(manifold.check_simplices()); // Human verification manifold.print(); @@ -331,14 +443,14 @@ SCENARIO("3-Manifold initialization", "[manifold]") { auto constexpr desired_simplices = 6400; auto constexpr desired_timeslices = 7; - Manifold3 manifold(desired_simplices, desired_timeslices); + Manifold_3 const manifold(desired_simplices, desired_timeslices); THEN("Triangulation is valid.") { REQUIRE(manifold.is_correct()); } THEN("The geometry matches the triangulation.") { REQUIRE(manifold.is_foliated()); - REQUIRE(manifold.vertices() == manifold.N0()); - REQUIRE(manifold.edges() == manifold.N1()); - REQUIRE(manifold.faces() == manifold.N2()); + REQUIRE_EQ(manifold.vertices(), manifold.N0()); + REQUIRE_EQ(manifold.edges(), manifold.N1()); + REQUIRE_EQ(manifold.faces(), manifold.N2()); REQUIRE(manifold.check_simplices()); // Human verification manifold.print(); @@ -348,19 +460,19 @@ SCENARIO("3-Manifold initialization", "[manifold]") } } -SCENARIO("3-Manifold function checks", "[manifold]") +SCENARIO("3-Manifold function checks" * doctest::test_suite("manifold")) { spdlog::debug("3-Manifold function checks.\n"); GIVEN("The default manifold from the default triangulation") { - Manifold3 manifold; THEN("There is only one vertex, the infinite vertex.") { - auto&& vertices = + Manifold_3 const manifold; + auto&& vertices = manifold.get_triangulation().get_delaunay().tds().vertices(); auto&& vertex = vertices.begin(); - CHECK(vertices.size() == 1); + CHECK_EQ(vertices.size(), 1); CHECK(manifold.is_vertex(vertex)); CHECK(manifold.get_triangulation().is_infinite(vertex)); } @@ -368,35 +480,35 @@ SCENARIO("3-Manifold function checks", "[manifold]") GIVEN("A 3-manifold") { - auto constexpr desired_simplices = 640; - auto constexpr desired_timeslices = 4; WHEN("It is initialized.") { - Manifold3 manifold(desired_simplices, desired_timeslices); + auto constexpr desired_timeslices = 4; + auto constexpr desired_simplices = 640; + Manifold_3 const manifold(desired_simplices, desired_timeslices); THEN("Functions referencing geometry data are accurate") { - CHECK(manifold.N3() == manifold.get_geometry().N3); - CHECK(manifold.N3_31() == manifold.get_geometry().N3_31); - CHECK(manifold.N3_13() == manifold.get_geometry().N3_13); - CHECK(manifold.N3_31_13() == manifold.get_geometry().N3_31_13); - CHECK(manifold.N3_22() == manifold.get_geometry().N3_22); - CHECK(manifold.N2() == manifold.get_geometry().N2); - CHECK(manifold.N1() == manifold.get_geometry().N1); - CHECK(manifold.N1_TL() == manifold.get_geometry().N1_TL); - CHECK(manifold.N1_SL() == manifold.get_geometry().N1_SL); - CHECK(manifold.N0() == manifold.get_geometry().N0); + CHECK_EQ(manifold.N3(), manifold.get_geometry().N3); + CHECK_EQ(manifold.N3_31(), manifold.get_geometry().N3_31); + CHECK_EQ(manifold.N3_13(), manifold.get_geometry().N3_13); + CHECK_EQ(manifold.N3_31_13(), manifold.get_geometry().N3_31_13); + CHECK_EQ(manifold.N3_22(), manifold.get_geometry().N3_22); + CHECK_EQ(manifold.N2(), manifold.get_geometry().N2); + CHECK_EQ(manifold.N1(), manifold.get_geometry().N1); + CHECK_EQ(manifold.N1_TL(), manifold.get_geometry().N1_TL); + CHECK_EQ(manifold.N1_SL(), manifold.get_geometry().N1_SL); + CHECK_EQ(manifold.N0(), manifold.get_geometry().N0); } } } } -SCENARIO("3-Manifold copying", "[manifold]") +SCENARIO("3-Manifold copying" * doctest::test_suite("manifold")) { spdlog::debug("3-Manifold copying.\n"); GIVEN("A 3-manifold.") { auto constexpr desired_simplices = 640; auto constexpr desired_timeslices = 4; - Manifold3 manifold(desired_simplices, desired_timeslices); + Manifold_3 manifold(desired_simplices, desired_timeslices); WHEN("It is copied.") { auto manifold2 = manifold; @@ -405,27 +517,27 @@ SCENARIO("3-Manifold copying", "[manifold]") { auto* manifold_ptr = &manifold; auto* manifold2_ptr = &manifold2; - CHECK_FALSE(manifold_ptr == manifold2_ptr); + CHECK_NE(manifold_ptr, manifold2_ptr); } THEN("The manifolds have identical properties.") { - CHECK(manifold2.N3() == manifold.N3()); - CHECK(manifold2.N3_31() == manifold.N3_31()); - CHECK(manifold2.N3_22() == manifold.N3_22()); - CHECK(manifold2.N3_13() == manifold.N3_13()); - CHECK(manifold2.N3_31_13() == manifold.N3_31_13()); - CHECK(manifold2.N2() == manifold.N2()); - CHECK(manifold2.N1() == manifold.N1()); - CHECK(manifold2.N1_TL() == manifold.N1_TL()); - CHECK(manifold2.N1_SL() == manifold.N1_SL()); - CHECK(manifold2.N0() == manifold.N0()); - CHECK(manifold2.max_time() == manifold.max_time()); - CHECK(manifold2.min_time() == manifold.min_time()); + CHECK_EQ(manifold2.N3(), manifold.N3()); + CHECK_EQ(manifold2.N3_31(), manifold.N3_31()); + CHECK_EQ(manifold2.N3_22(), manifold.N3_22()); + CHECK_EQ(manifold2.N3_13(), manifold.N3_13()); + CHECK_EQ(manifold2.N3_31_13(), manifold.N3_31_13()); + CHECK_EQ(manifold2.N2(), manifold.N2()); + CHECK_EQ(manifold2.N1(), manifold.N1()); + CHECK_EQ(manifold2.N1_TL(), manifold.N1_TL()); + CHECK_EQ(manifold2.N1_SL(), manifold.N1_SL()); + CHECK_EQ(manifold2.N0(), manifold.N0()); + CHECK_EQ(manifold2.max_time(), manifold.max_time()); + CHECK_EQ(manifold2.min_time(), manifold.min_time()); // Human verification fmt::print("Manifold properties:\n"); manifold.print(); manifold.print_volume_per_timeslice(); - auto cells = manifold.get_triangulation().get_delaunay().tds().cells(); + auto cells = manifold.get_delaunay().tds().cells(); fmt::print("Cell compact container size == {}\n", cells.size()); fmt::print("Now compact container size == {}\n", cells.size()); fmt::print("Vertex compact container size == {}\n", @@ -442,14 +554,14 @@ SCENARIO("3-Manifold copying", "[manifold]") } } -SCENARIO("3-Manifold update geometry", "[manifold]") +SCENARIO("3-Manifold update geometry" * doctest::test_suite("manifold")) { spdlog::debug("3-Manifold update geometry.\n"); GIVEN("A 3-manifold.") { auto constexpr desired_simplices = 640; auto constexpr desired_timeslices = 4; - Manifold3 manifold(desired_simplices, desired_timeslices); + Manifold_3 manifold(desired_simplices, desired_timeslices); WHEN("We call update().") { // Get values for manifold1 @@ -466,27 +578,27 @@ SCENARIO("3-Manifold update geometry", "[manifold]") THEN("We get back the same values.") { fmt::print("Manifold N3 is still {}\n", manifold.N3()); - CHECK(manifold.N3() == manifold_N3); + CHECK_EQ(manifold.N3(), manifold_N3); fmt::print("Manifold N2 is still {}\n", manifold.N2()); - CHECK(manifold.N2() == manifold_N2); + CHECK_EQ(manifold.N2(), manifold_N2); fmt::print("Manifold N1 is still {}\n", manifold.N1()); - CHECK(manifold.N1() == manifold_N1); + CHECK_EQ(manifold.N1(), manifold_N1); fmt::print("Manifold N0 is still {}\n", manifold.N0()); - CHECK(manifold.N0() == manifold_N0); + CHECK_EQ(manifold.N0(), manifold_N0); } } } } -SCENARIO("3-Manifold mutation", "[manifold]") +SCENARIO("3-Manifold mutation" * doctest::test_suite("manifold")) { spdlog::debug("3-Manifold mutation.\n"); GIVEN("A pair of 3-manifolds.") { auto constexpr desired_simplices = 640; auto constexpr desired_timeslices = 4; - Manifold3 manifold1(desired_simplices, desired_timeslices); - Manifold3 manifold2(desired_simplices, desired_timeslices); + Manifold_3 manifold1(desired_simplices, desired_timeslices); + Manifold_3 const manifold2(desired_simplices, desired_timeslices); WHEN("We swap the triangulation of one manifold for another.") { // Get values for manifold1 @@ -512,10 +624,10 @@ SCENARIO("3-Manifold mutation", "[manifold]") fmt::print("Manifolds swapped.\n"); THEN("Not calling update() gives old values.") { - CHECK(manifold1.N3() == manifold1_N3); - CHECK(manifold1.N2() == manifold1_N2); - CHECK(manifold1.N1() == manifold1_N1); - CHECK(manifold1.N0() == manifold1_N0); + CHECK_EQ(manifold1.N3(), manifold1_N3); + CHECK_EQ(manifold1.N2(), manifold1_N2); + CHECK_EQ(manifold1.N1(), manifold1_N1); + CHECK_EQ(manifold1.N0(), manifold1_N0); AND_WHEN("We call update().") { @@ -524,13 +636,13 @@ SCENARIO("3-Manifold mutation", "[manifold]") THEN("The geometry matches the new triangulation.") { fmt::print("Manifold 1 N3 is now {}\n", manifold1.N3()); - CHECK(manifold1.N3() == manifold2_N3); + CHECK_EQ(manifold1.N3(), manifold2_N3); fmt::print("Manifold 1 N2 is now {}\n", manifold1.N2()); - CHECK(manifold1.N2() == manifold2_N2); + CHECK_EQ(manifold1.N2(), manifold2_N2); fmt::print("Manifold 1 N1 is now {}\n", manifold1.N1()); - CHECK(manifold1.N1() == manifold2_N1); + CHECK_EQ(manifold1.N1(), manifold2_N1); fmt::print("Manifold 1 N0 is now {}\n", manifold1.N0()); - CHECK(manifold1.N0() == manifold2_N0); + CHECK_EQ(manifold1.N0(), manifold2_N0); } } } @@ -538,32 +650,33 @@ SCENARIO("3-Manifold mutation", "[manifold]") } } -SCENARIO("3-Manifold validation and fixing", "[manifold][!mayfail]") +SCENARIO("3-Manifold validation and fixing" * doctest::test_suite("manifold")) { spdlog::debug("3-Manifold validation and fixing.\n"); + using Point = Point_t<3>; GIVEN("A (1,3) and (3,1) stacked on each other.") { - Causal_vertices_t<3> causal_vertices; - causal_vertices.emplace_back(Point_t<3>(0, 0, 0), 1); - causal_vertices.emplace_back(Point_t<3>(1, 0, 0), 2); - causal_vertices.emplace_back(Point_t<3>(0, 1, 0), 2); - causal_vertices.emplace_back(Point_t<3>(0, 0, 1), 2); - causal_vertices.emplace_back(Point_t<3>(RADIUS_2, RADIUS_2, RADIUS_2), 3); - Manifold3 manifold(causal_vertices, 0.0, 1.0); - auto print = [&manifold](auto& vertex) { + vector const Vertices{Point(0, 0, 0), Point(1, 0, 0), Point(0, 1, 0), + Point(0, 0, 1), Point(RADIUS_2, RADIUS_2, RADIUS_2)}; + vector const Timevalues{1, 2, 2, 2, 3}; + auto causal_vertices = + manifolds::make_causal_vertices<3>(Vertices, Timevalues); + Manifold_3 manifold(causal_vertices, 0.0, 1.0); + auto print = [&manifold](auto& vertex) { fmt::print( - "Vertex: ({}) Timevalue: {} is a vertex: {} and is " - "infinite: {}\n", - vertex->point(), vertex->info(), manifold.is_vertex(vertex), - manifold.get_triangulation().is_infinite(vertex)); + "Vertex: ({}) Timevalue: {} is a vertex: {} and is " + "infinite: {}\n", + utilities::point_to_str(vertex->point()), vertex->info(), + manifold.is_vertex(vertex), + manifold.get_triangulation().is_infinite(vertex)); }; WHEN("It is constructed.") { THEN("The number of timeslices is correct.") { - REQUIRE(manifold.min_time() == 1); - REQUIRE(manifold.max_time() == 3); + REQUIRE_EQ(manifold.min_time(), 1); + REQUIRE_EQ(manifold.max_time(), 3); } THEN("Every vertex in the manifold has a correct timevalue.") { @@ -590,23 +703,24 @@ SCENARIO("3-Manifold validation and fixing", "[manifold][!mayfail]") // Human verification auto bad_vertices = manifold.get_triangulation().find_incorrect_vertices(); - for_each(bad_vertices.begin(), bad_vertices.end(), print); + ranges::for_each(bad_vertices, print); } - THEN("We can detect invalid cells.") + THEN("But the invalid cell is fixed on update.") { + CHECK_FALSE(manifold.check_simplices()); manifold.update(); manifold.print_cells(); - CHECK_FALSE(manifold.check_simplices()); + CHECK(manifold.check_simplices()); } } } GIVEN("A medium sized manifold.") { - auto constexpr desired_simplices = 6400; - auto constexpr desired_timeslices = 7; WHEN("It is constructed.") { - Manifold3 manifold(desired_simplices, desired_timeslices); + auto constexpr desired_timeslices = 7; + auto constexpr desired_simplices = 6400; + Manifold_3 const manifold(desired_simplices, desired_timeslices); THEN("The triangulation is valid and Delaunay.") { REQUIRE(manifold.is_correct()); @@ -614,15 +728,15 @@ SCENARIO("3-Manifold validation and fixing", "[manifold][!mayfail]") THEN("The geometry matches the triangulation.") { REQUIRE(manifold.is_foliated()); - REQUIRE(manifold.vertices() == manifold.N0()); - REQUIRE(manifold.edges() == manifold.N1()); - REQUIRE(manifold.faces() == manifold.N2()); - REQUIRE(manifold.simplices() == manifold.N3()); + REQUIRE_EQ(manifold.vertices(), manifold.N0()); + REQUIRE_EQ(manifold.edges(), manifold.N1()); + REQUIRE_EQ(manifold.faces(), manifold.N2()); + REQUIRE_EQ(manifold.simplices(), manifold.N3()); } THEN("The number of timeslices is correct.") { - REQUIRE(manifold.min_time() == 1); - REQUIRE(manifold.max_time() == desired_timeslices); + REQUIRE_EQ(manifold.min_time(), 1); + REQUIRE_EQ(manifold.max_time(), desired_timeslices); } THEN("Every vertex in the manifold has a correct timevalue.") { diff --git a/tests/Metropolis_test.cpp b/tests/Metropolis_test.cpp index 133115b1c0..8c67f7ebac 100644 --- a/tests/Metropolis_test.cpp +++ b/tests/Metropolis_test.cpp @@ -7,17 +7,16 @@ /// @file Metropolis_test.cpp /// @brief Tests for the Metropolis-Hastings algorithm /// @author Adam Getchell -/// @todo Debug accepted moves != attempted moves (should be equal) #include "Metropolis.hpp" -#include +#include using namespace std; using namespace manifolds; -SCENARIO("MoveStrategy special member and swap properties", - "[metropolis]") +SCENARIO("MoveStrategy special member and swap properties" * + doctest::test_suite("metropolis")) { spdlog::debug( "MoveStrategy special member and swap properties.\n"); @@ -27,51 +26,51 @@ SCENARIO("MoveStrategy special member and swap properties", { THEN("It is no-throw destructible.") { - REQUIRE(is_nothrow_destructible_v); - REQUIRE(is_nothrow_destructible_v); + REQUIRE(is_nothrow_destructible_v); + REQUIRE(is_nothrow_destructible_v); spdlog::debug("It is no-throw destructible.\n"); } THEN("It is no-throw default constructible.") { - REQUIRE(is_nothrow_default_constructible_v); - REQUIRE(is_nothrow_default_constructible_v); + REQUIRE(is_nothrow_default_constructible_v); + REQUIRE(is_nothrow_default_constructible_v); spdlog::debug("It is no-throw default constructible.\n"); } THEN("It is no-throw copy constructible.") { - REQUIRE(is_nothrow_copy_constructible_v); - REQUIRE(is_nothrow_copy_constructible_v); + REQUIRE(is_nothrow_copy_constructible_v); + REQUIRE(is_nothrow_copy_constructible_v); spdlog::debug("It is no-throw copy constructible.\n"); } THEN("It is no-throw copy assignable.") { - REQUIRE(is_nothrow_copy_assignable_v); - REQUIRE(is_nothrow_copy_assignable_v); + REQUIRE(is_nothrow_copy_assignable_v); + REQUIRE(is_nothrow_copy_assignable_v); spdlog::debug("It is no-throw copy assignable.\n"); } THEN("It is no-throw move constructible.") { - REQUIRE(is_nothrow_move_constructible_v); - REQUIRE(is_nothrow_move_constructible_v); + REQUIRE(is_nothrow_move_constructible_v); + REQUIRE(is_nothrow_move_constructible_v); spdlog::debug("It is no-throw move constructible.\n"); } THEN("It is no-throw move assignable.") { - REQUIRE(is_nothrow_move_assignable_v); - REQUIRE(is_nothrow_move_assignable_v); + REQUIRE(is_nothrow_move_assignable_v); + REQUIRE(is_nothrow_move_assignable_v); spdlog::debug("It is no-throw move assignable.\n"); } THEN("It is no-throw swappable.") { - REQUIRE(is_nothrow_swappable_v); - REQUIRE(is_nothrow_swappable_v); + REQUIRE(is_nothrow_swappable_v); + REQUIRE(is_nothrow_swappable_v); spdlog::debug("It is no-throw swappable.\n"); } THEN("It is constructible from 5 parameters.") { - REQUIRE(is_constructible_v); - REQUIRE(is_constructible_v); spdlog::debug("It is constructible from 5 parameters.\n"); } @@ -79,36 +78,36 @@ SCENARIO("MoveStrategy special member and swap properties", } } -SCENARIO("Metropolis member functions", "[metropolis]") +SCENARIO("Metropolis member functions" * doctest::test_suite("metropolis")) { - auto constexpr Alpha = static_cast(0.6); - auto constexpr K = static_cast(1.1); - auto constexpr Lambda = static_cast(0.1); - auto constexpr passes = 10; - auto constexpr output_every_n_passes = 1; - GIVEN("A correctly-constructed Manifold3.") + auto constexpr Alpha = static_cast(0.6); + auto constexpr K = static_cast(1.1); // NOLINT + auto constexpr Lambda = static_cast(0.1); + GIVEN("A correctly-constructed Manifold_3.") { auto constexpr simplices = 640; auto constexpr timeslices = 4; - Manifold3 universe(simplices, timeslices); + Manifold_3 const universe(simplices, timeslices); // It is correctly constructed REQUIRE(universe.is_correct()); WHEN("A Metropolis function object is constructed.") { - Metropolis3 testrun(Alpha, K, Lambda, passes, output_every_n_passes); + auto constexpr output_every_n_passes = 1; + auto constexpr passes = 10; + Metropolis_3 testrun(Alpha, K, Lambda, passes, output_every_n_passes); THEN("The Metropolis function object is initialized correctly.") { - CHECK(testrun.Alpha() == Alpha); - CHECK(testrun.K() == K); - CHECK(testrun.Lambda() == Lambda); - CHECK(testrun.passes() == passes); - CHECK(testrun.checkpoint() == output_every_n_passes); - CHECK(testrun.get_proposed().total() == 0); - CHECK(testrun.get_accepted().total() == 0); - CHECK(testrun.get_rejected().total() == 0); - CHECK(testrun.get_attempted().total() == 0); - CHECK(testrun.get_succeeded().total() == 0); - CHECK(testrun.get_failed().total() == 0); + CHECK_EQ(testrun.Alpha(), Alpha); + CHECK_EQ(testrun.K(), K); + CHECK_EQ(testrun.Lambda(), Lambda); + CHECK_EQ(testrun.passes(), passes); + CHECK_EQ(testrun.checkpoint(), output_every_n_passes); + CHECK_EQ(testrun.get_proposed().total(), 0); + CHECK_EQ(testrun.get_accepted().total(), 0); + CHECK_EQ(testrun.get_rejected().total(), 0); + CHECK_EQ(testrun.get_attempted().total(), 0); + CHECK_EQ(testrun.get_succeeded().total(), 0); + CHECK_EQ(testrun.get_failed().total(), 0); } THEN("The initial moves are made correctly.") { @@ -120,50 +119,54 @@ SCENARIO("Metropolis member functions", "[metropolis]") // Initialization proposes one move of each type for (auto i = 0; i < move_tracker::NUMBER_OF_3D_MOVES; ++i) { - CHECK(testrun.get_proposed()[i] == 1); + CHECK_EQ(testrun.get_proposed()[i], 1); } // Initialization accepts one move of each type for (auto i = 0; i < move_tracker::NUMBER_OF_3D_MOVES; ++i) { - CHECK(testrun.get_accepted()[i] == 1); + CHECK_EQ(testrun.get_accepted()[i], 1); } // Initialization does not reject any moves - CHECK(total_rejected == 0); + CHECK_EQ(total_rejected, 0); // Initialization attempts one move of each type for (auto i = 0; i < move_tracker::NUMBER_OF_3D_MOVES; ++i) { - CHECK(testrun.get_attempted()[i] == 1); + CHECK_EQ(testrun.get_attempted()[i], 1); } - CHECK(total_attempted == total_successful + total_failed); + CHECK_EQ(total_attempted, total_successful + total_failed); // Human verification - result->print_attempts(); - result->print_successful(); - result->print_errors(); + REQUIRE_MESSAGE(result, + "The Metropolis function object failed to " + "initialize the universe."); + if (result) + { + result->print_attempts(); + result->print_successful(); + result->print_errors(); + } } } } } -// This may take a while, so the scenario is tagged with [.] -// to disable by default -SCENARIO("Using the Metropolis algorithm", "[metropolis][.]") +SCENARIO("Using the Metropolis algorithm" * doctest::test_suite("metropolis")) { - auto constexpr Alpha = static_cast(0.6); - auto constexpr K = static_cast(1.1); - auto constexpr Lambda = static_cast(0.1); - auto constexpr passes = 1; - auto constexpr output_every_n_passes = 1; - GIVEN("A correctly-constructed Manifold3.") + auto constexpr Alpha = static_cast(0.6); + auto constexpr K = static_cast(1.1); // NOLINT + auto constexpr Lambda = static_cast(0.1); + GIVEN("A correctly-constructed Manifold_3.") { auto constexpr simplices = 640; auto constexpr timeslices = 4; - Manifold3 universe(simplices, timeslices); + Manifold_3 const universe(simplices, timeslices); // It is correctly constructed REQUIRE(universe.is_correct()); WHEN("A Metropolis function object is constructed.") { - Metropolis3 testrun(Alpha, K, Lambda, passes, output_every_n_passes); + auto constexpr output_every_n_passes = 1; + auto constexpr passes = 1; + Metropolis_3 testrun(Alpha, K, Lambda, passes, output_every_n_passes); THEN("A lot of moves are done.") { auto result = testrun(universe); @@ -179,14 +182,13 @@ SCENARIO("Using the Metropolis algorithm", "[metropolis][.]") auto total_failed = testrun.get_failed().total(); // We should have at least a trial move per simplex on average // per pass, times the number of passes - CHECK(total_proposed > universe.N3() * passes); - CHECK(total_proposed == total_accepted + total_rejected); + CHECK_GT(total_proposed, universe.N3() * passes); + CHECK_EQ(total_proposed, total_accepted + total_rejected); // We should attempt a move for each accepted move - // Why does this fail? - CHECK(total_attempted == total_accepted); - CHECK(total_successful > 0); - CHECK(total_failed >= 0); - CHECK(total_attempted == total_successful + total_failed); + CHECK_EQ(total_attempted, total_accepted); + CHECK_GT(total_successful, 0); + CHECK_GE(total_failed, 0); + CHECK_EQ(total_attempted, total_successful + total_failed); // Human verification testrun.print_results(); } diff --git a/tests/Move_always_test.cpp b/tests/Move_always_test.cpp index bef69ee35e..925168d824 100644 --- a/tests/Move_always_test.cpp +++ b/tests/Move_always_test.cpp @@ -1,7 +1,7 @@ /******************************************************************************* Causal Dynamical Triangulations in C++ using CGAL - Copyright © 2015 Adam Getchell + Copyright © 2021 Adam Getchell ******************************************************************************/ /// @file Move_always_test.cpp @@ -10,13 +10,13 @@ #include "Move_always.hpp" -#include +#include using namespace std; using namespace manifolds; -SCENARIO("MoveStrategy special member and swap properties", - "[move always]") +SCENARIO("MoveStrategy special member and swap properties" * + doctest::test_suite("move_always")) { spdlog::debug( "MoveStrategy special member and swap properties.\n"); @@ -26,118 +26,119 @@ SCENARIO("MoveStrategy special member and swap properties", { THEN("It is no-throw destructible.") { - REQUIRE(is_nothrow_destructible_v); - REQUIRE(is_nothrow_destructible_v); + REQUIRE(is_nothrow_destructible_v); + REQUIRE(is_nothrow_destructible_v); spdlog::debug("It is no-throw destructible.\n"); } THEN("It is no-throw default constructible.") { - REQUIRE(is_nothrow_default_constructible_v); - REQUIRE(is_nothrow_default_constructible_v); + REQUIRE(is_nothrow_default_constructible_v); + REQUIRE(is_nothrow_default_constructible_v); spdlog::debug("It is no-throw default constructible.\n"); } THEN("It is no-throw copy constructible.") { - REQUIRE(is_nothrow_copy_constructible_v); - REQUIRE(is_nothrow_copy_constructible_v); + REQUIRE(is_nothrow_copy_constructible_v); + REQUIRE(is_nothrow_copy_constructible_v); spdlog::debug("It is no-throw copy constructible.\n"); } THEN("It is no-throw copy assignable.") { - REQUIRE(is_nothrow_copy_assignable_v); - REQUIRE(is_nothrow_copy_assignable_v); + REQUIRE(is_nothrow_copy_assignable_v); + REQUIRE(is_nothrow_copy_assignable_v); spdlog::debug("It is no-throw copy assignable.\n"); } THEN("It is no-throw move constructible.") { - REQUIRE(is_nothrow_move_constructible_v); - REQUIRE(is_nothrow_move_constructible_v); + REQUIRE(is_nothrow_move_constructible_v); + REQUIRE(is_nothrow_move_constructible_v); spdlog::debug("It is no-throw move constructible.\n"); } THEN("It is no-throw move assignable.") { - REQUIRE(is_nothrow_move_assignable_v); - REQUIRE(is_nothrow_move_assignable_v); + REQUIRE(is_nothrow_move_assignable_v); + REQUIRE(is_nothrow_move_assignable_v); spdlog::debug("It is no-throw move assignable.\n"); } THEN("It is no-throw swappable.") { - REQUIRE(is_nothrow_swappable_v); - REQUIRE(is_nothrow_swappable_v); + REQUIRE(is_nothrow_swappable_v); + REQUIRE(is_nothrow_swappable_v); spdlog::debug("It is no-throw swappable.\n"); } THEN("It is constructible from 2 parameters.") { - REQUIRE(is_constructible_v); - REQUIRE(is_constructible_v); + REQUIRE(is_constructible_v); + REQUIRE(is_constructible_v); spdlog::debug("It is constructible from 2 parameters.\n"); } } } } -SCENARIO("MoveAlways member functions", "[move always]") +SCENARIO("MoveAlways member functions" * doctest::test_suite("move_always")) { spdlog::debug("MoveAlways member functions.\n"); - GIVEN("A correctly-constructed Manifold3.") + GIVEN("A correctly-constructed Manifold_3.") { auto constexpr simplices = 640; auto constexpr timeslices = 4; - Manifold3 manifold(simplices, timeslices); + Manifold_3 const manifold(simplices, timeslices); REQUIRE(manifold.is_correct()); - WHEN("A MoveAlways3 is constructed.") + WHEN("A MoveAlways_3 is constructed.") { auto constexpr passes = 10; auto constexpr checkpoint = 5; - MoveAlways3 mover(passes, checkpoint); + MoveAlways_3 const mover(passes, checkpoint); THEN("The correct passes and checkpoints are instantiated.") { - CHECK(mover.passes() == passes); - CHECK(mover.checkpoint() == checkpoint); + CHECK_EQ(mover.passes(), passes); + CHECK_EQ(mover.checkpoint(), checkpoint); } THEN("Attempted, successful, and failed moves are zero-initialized.") { - CHECK(mover.get_attempted().total() == 0); - CHECK(mover.get_succeeded().total() == 0); - CHECK(mover.get_failed().total() == 0); + CHECK_EQ(mover.get_attempted().total(), 0); + CHECK_EQ(mover.get_succeeded().total(), 0); + CHECK_EQ(mover.get_failed().total(), 0); } } - WHEN("A MoveAlways3 algorithm is instantiated.") + WHEN("A MoveAlways_3 algorithm is instantiated.") { auto constexpr passes = 1; auto constexpr checkpoint = 1; - MoveAlways3 mover(passes, checkpoint); + MoveAlways_3 const mover(passes, checkpoint); THEN("The correct passes and checkpoints are instantiated.") { - CHECK(mover.passes() == passes); - CHECK(mover.checkpoint() == checkpoint); + CHECK_EQ(mover.passes(), passes); + CHECK_EQ(mover.checkpoint(), checkpoint); } THEN("Attempted moves and successful moves are zero-initialized.") { - CHECK(mover.get_attempted().total() == 0); - CHECK(mover.get_succeeded().total() == 0); - CHECK(mover.get_failed().total() == 0); + CHECK_EQ(mover.get_attempted().total(), 0); + CHECK_EQ(mover.get_succeeded().total(), 0); + CHECK_EQ(mover.get_failed().total(), 0); } } } } -// This may take a while, so the scenario is tagged with [.] +// This may take a while, so the scenario decorated with doctest::skip() // to disable by default -SCENARIO("Using the MoveAlways algorithm", "[.]") +SCENARIO("Using the MoveAlways algorithm" * doctest::test_suite("move_always") * + doctest::skip()) { spdlog::debug("Using the MoveAlways algorithm.\n"); - GIVEN("A correctly-constructed Manifold3.") + GIVEN("A correctly-constructed Manifold_3.") { - auto constexpr simplices = 640; - auto constexpr timeslices = 4; - Manifold3 manifold(simplices, timeslices); + auto constexpr simplices = 64; + auto constexpr timeslices = 3; + Manifold_3 const manifold(simplices, timeslices); REQUIRE(manifold.is_correct()); - WHEN("A MoveAlways3 algorithm is used.") + WHEN("A MoveAlways_3 algorithm is used.") { auto constexpr passes = 1; auto constexpr checkpoint = 1; - MoveAlways3 mover(passes, checkpoint); + MoveAlways_3 mover(passes, checkpoint); THEN("A lot of moves are made.") { auto result = mover(manifold); @@ -147,8 +148,8 @@ SCENARIO("Using the MoveAlways algorithm", "[.]") "The correct number of attempted, successful, and failed moves are " "made.") { - CHECK(mover.get_attempted().total() == - mover.get_succeeded().total() + mover.get_failed().total()); + CHECK_EQ(mover.get_attempted().total(), + mover.get_succeeded().total() + mover.get_failed().total()); // Human verification mover.print_results(); } @@ -161,16 +162,16 @@ SCENARIO("Using the MoveAlways algorithm", "[.]") { auto constexpr passes = 1; auto constexpr checkpoint = 1; - MoveAlways4 mover(passes, checkpoint); + MoveAlways_4 const mover(passes, checkpoint); THEN("The correct passes and checkpoints are instantiated.") { - CHECK(mover.passes() == passes); - CHECK(mover.checkpoint() == checkpoint); + CHECK_EQ(mover.passes(), passes); + CHECK_EQ(mover.checkpoint(), checkpoint); } THEN("Attempted moves and successful moves are zero-initialized.") { - CHECK(mover.get_attempted().two_four_moves() == 0); - CHECK(mover.get_failed().two_four_moves() == 0); + CHECK_EQ(mover.get_attempted().two_four_moves(), 0); + CHECK_EQ(mover.get_failed().two_four_moves(), 0); } } } diff --git a/tests/Move_command_test.cpp b/tests/Move_command_test.cpp index 2112171a17..2bd25ede17 100644 --- a/tests/Move_command_test.cpp +++ b/tests/Move_command_test.cpp @@ -1,7 +1,7 @@ /******************************************************************************* Causal Dynamical Triangulations in C++ using CGAL - Copyright © 2014 Adam Getchell + Copyright © 2018 Adam Getchell ******************************************************************************/ /// @file Move_command_test.cpp @@ -10,14 +10,13 @@ #include "Move_command.hpp" +#include #include -#include - using namespace std; using namespace manifolds; -SCENARIO("MoveCommand special members", "[move command]") +SCENARIO("MoveCommand special members" * doctest::test_suite("move_command")) { spdlog::debug("MoveCommand special members.\n"); GIVEN("A MoveCommand.") @@ -26,56 +25,57 @@ SCENARIO("MoveCommand special members", "[move command]") { THEN("It is no-throw destructible.") { - REQUIRE(is_nothrow_destructible_v>); + REQUIRE(is_nothrow_destructible_v>); spdlog::debug("It is no-throw destructible.\n"); } THEN("It is not default constructible.") { - CHECK_FALSE(is_default_constructible_v>); + CHECK_FALSE(is_default_constructible_v>); } THEN("It is copy constructible.") { - REQUIRE(is_copy_constructible_v>); + REQUIRE(is_copy_constructible_v>); spdlog::debug("It is copy constructible.\n"); } THEN("It is copy assignable.") { - REQUIRE(is_copy_assignable_v>); + REQUIRE(is_copy_assignable_v>); spdlog::debug("It is copy assignable.\n"); } THEN("It is no-throw move constructible.") { - REQUIRE(is_nothrow_move_constructible_v>); + REQUIRE(is_nothrow_move_constructible_v>); spdlog::debug("Small function optimization supported."); spdlog::debug("It is no-throw move constructible.\n"); } THEN("It is no-throw move assignable.") { - REQUIRE(is_nothrow_move_assignable_v>); + REQUIRE(is_nothrow_move_assignable_v>); spdlog::debug("It is no-throw move assignable.\n"); } THEN("It is no-throw swappable") { - REQUIRE(is_nothrow_swappable_v>); + REQUIRE(is_nothrow_swappable_v>); spdlog::debug("It is no-throw swappable.\n"); } THEN("It is constructible from a Manifold.") { - REQUIRE(is_constructible_v, Manifold3>); + REQUIRE(is_constructible_v, Manifold_3>); spdlog::debug("It is constructible from a Manifold.\n"); } } } } -SCENARIO("Invoking a move with a function pointer", "[move command]") +SCENARIO("Invoking a move with a function pointer" * + doctest::test_suite("move_command")) { spdlog::debug("Invoking a move with a function pointer.\n"); GIVEN("A valid manifold.") { auto constexpr desired_simplices = 640; auto constexpr desired_timeslices = 4; - Manifold3 manifold(desired_simplices, desired_timeslices); + Manifold_3 manifold(desired_simplices, desired_timeslices); REQUIRE(manifold.is_correct()); WHEN("A function pointer is constructed for a move.") { @@ -96,18 +96,18 @@ SCENARIO("Invoking a move with a function pointer", "[move command]") } } -SCENARIO("Invoking a move with a lambda", "[move command][!mayfail]") +SCENARIO("Invoking a move with a lambda" * doctest::test_suite("move_command")) { spdlog::debug("Invoking a move with a lambda.\n"); GIVEN("A valid manifold.") { auto constexpr desired_simplices = 640; auto constexpr desired_timeslices = 4; - Manifold3 manifold(desired_simplices, desired_timeslices); + Manifold_3 manifold(desired_simplices, desired_timeslices); REQUIRE(manifold.is_correct()); WHEN("A lambda is constructed for a move.") { - auto const move23 = [](Manifold3& manifold_3) { + auto const move23 = [](Manifold_3& manifold_3) { return ergodic_moves::do_23_move(manifold_3).value(); }; THEN("Running the lambda makes the move.") @@ -126,15 +126,15 @@ SCENARIO("Invoking a move with a lambda", "[move command][!mayfail]") } } -SCENARIO("Invoking a move with apply_move and a function pointer", - "[move command]") +SCENARIO("Invoking a move with apply_move and a function pointer" * + doctest::test_suite("move_command")) { spdlog::debug("Invoking a move with apply_move and a function pointer.\n"); GIVEN("A valid manifold.") { auto constexpr desired_simplices = 640; auto constexpr desired_timeslices = 4; - Manifold3 manifold(desired_simplices, desired_timeslices); + Manifold_3 manifold(desired_simplices, desired_timeslices); REQUIRE(manifold.is_correct()); WHEN("Apply_move is used for a move.") { @@ -155,18 +155,18 @@ SCENARIO("Invoking a move with apply_move and a function pointer", } } -SCENARIO("MoveCommand initialization", "[move command]") +SCENARIO("MoveCommand initialization" * doctest::test_suite("move_command")) { spdlog::debug("MoveCommand initialization.\n"); GIVEN("A valid manifold.") { auto constexpr desired_simplices = 640; auto constexpr desired_timeslices = 4; - Manifold3 manifold(desired_simplices, desired_timeslices); + Manifold_3 manifold(desired_simplices, desired_timeslices); REQUIRE(manifold.is_correct()); WHEN("A Command is constructed with a manifold.") { - MoveCommand command(manifold); + MoveCommand const command(manifold); THEN("The original is still valid.") { REQUIRE(manifold.is_correct()); @@ -175,18 +175,18 @@ SCENARIO("MoveCommand initialization", "[move command]") } THEN("It contains the manifold.") { - CHECK(manifold.N3() == command.get_const_results().N3()); - CHECK(manifold.N3_31() == command.get_const_results().N3_31()); - CHECK(manifold.N3_22() == command.get_const_results().N3_22()); - CHECK(manifold.N3_13() == command.get_const_results().N3_13()); - CHECK(manifold.N3_31_13() == command.get_const_results().N3_31_13()); - CHECK(manifold.N2() == command.get_const_results().N2()); - CHECK(manifold.N1() == command.get_const_results().N1()); - CHECK(manifold.N1_TL() == command.get_const_results().N1_TL()); - CHECK(manifold.N1_SL() == command.get_const_results().N1_SL()); - CHECK(manifold.N0() == command.get_const_results().N0()); - CHECK(manifold.max_time() == command.get_const_results().max_time()); - CHECK(manifold.min_time() == command.get_const_results().min_time()); + CHECK_EQ(manifold.N3(), command.get_const_results().N3()); + CHECK_EQ(manifold.N3_31(), command.get_const_results().N3_31()); + CHECK_EQ(manifold.N3_22(), command.get_const_results().N3_22()); + CHECK_EQ(manifold.N3_13(), command.get_const_results().N3_13()); + CHECK_EQ(manifold.N3_31_13(), command.get_const_results().N3_31_13()); + CHECK_EQ(manifold.N2(), command.get_const_results().N2()); + CHECK_EQ(manifold.N1(), command.get_const_results().N1()); + CHECK_EQ(manifold.N1_TL(), command.get_const_results().N1_TL()); + CHECK_EQ(manifold.N1_SL(), command.get_const_results().N1_SL()); + CHECK_EQ(manifold.N0(), command.get_const_results().N0()); + CHECK_EQ(manifold.max_time(), command.get_const_results().max_time()); + CHECK_EQ(manifold.min_time(), command.get_const_results().min_time()); // Human verification fmt::print("Manifold properties:\n"); manifold.print_details(); @@ -199,13 +199,13 @@ SCENARIO("MoveCommand initialization", "[move command]") { auto* manifold_ptr = &manifold; auto const* manifold2_ptr = &command.get_const_results(); - CHECK_FALSE(manifold_ptr == manifold2_ptr); + CHECK_NE(manifold_ptr, manifold2_ptr); } THEN("Attempted, succeeded, and failed moves are initialized to 0.") { - CHECK(command.get_attempted().total() == 0); - CHECK(command.get_succeeded().total() == 0); - CHECK(command.get_failed().total() == 0); + CHECK_EQ(command.get_attempted().total(), 0); + CHECK_EQ(command.get_succeeded().total(), 0); + CHECK_EQ(command.get_failed().total(), 0); // Human verification fmt::print("Attempted moves are {}\n", @@ -218,14 +218,14 @@ SCENARIO("MoveCommand initialization", "[move command]") } } -SCENARIO("Queueing and executing moves", "[move command][!mayfail]") +SCENARIO("Queueing and executing moves" * doctest::test_suite("move_command")) { spdlog::debug("Queueing and executing moves.\n"); GIVEN("A valid manifold.") { auto constexpr desired_simplices = 9600; auto constexpr desired_timeslices = 7; - Manifold3 manifold(desired_simplices, desired_timeslices); + Manifold_3 manifold(desired_simplices, desired_timeslices); REQUIRE(manifold.is_correct()); WHEN("Move_command copies the manifold and applies the move.") { @@ -241,13 +241,13 @@ SCENARIO("Queueing and executing moves", "[move command][!mayfail]") command.execute(); // An attempted move was recorded - CHECK(command.get_attempted().three_two_moves() == 1); + CHECK_EQ(command.get_attempted().three_two_moves(), 1); // A successful move was recorded - CHECK(command.get_succeeded().three_two_moves() == 1); + CHECK_EQ(command.get_succeeded().three_two_moves(), 1); // No failures - CHECK(command.get_failed().three_two_moves() == 0); + CHECK_EQ(command.get_failed().three_two_moves(), 0); // Get the results auto result = command.get_results(); @@ -261,8 +261,8 @@ SCENARIO("Queueing and executing moves", "[move command][!mayfail]") "pointers.\n"); // The move should not change the original manifold - CHECK_FALSE(manifold.N3_22() == result.N3_22()); - CHECK_FALSE(manifold.N1_TL() == result.N1_TL()); + CHECK_NE(manifold.N3_22(), result.N3_22()); + CHECK_NE(manifold.N1_TL(), result.N1_TL()); fmt::print("The original manifold is unchanged by Move_command.\n"); } } @@ -276,20 +276,20 @@ SCENARIO("Queueing and executing moves", "[move command][!mayfail]") command.execute(); // An attempted move was recorded - CHECK(command.get_attempted().four_four_moves() == 1); + CHECK_EQ(command.get_attempted().four_four_moves(), 1); // A successful move was recorded - CHECK(command.get_succeeded().four_four_moves() == 1); + CHECK_EQ(command.get_succeeded().four_four_moves(), 1); // No failures - CHECK(command.get_failed().four_four_moves() == 0); + CHECK_EQ(command.get_failed().four_four_moves(), 0); // Get the results auto const& result = command.get_results(); // Triangulation shouldn't have changed - CHECK(result.get_triangulation().number_of_finite_cells() == - manifold.get_triangulation().number_of_finite_cells()); + CHECK_EQ(result.get_triangulation().number_of_finite_cells(), + manifold.get_triangulation().number_of_finite_cells()); REQUIRE(ergodic_moves::check_move(manifold, result, move_tracker::move_type::FOUR_FOUR)); fmt::print("Move left triangulation unchanged.\n"); @@ -305,20 +305,20 @@ SCENARIO("Queueing and executing moves", "[move command][!mayfail]") command.execute(); // An attempted move was recorded - CHECK(command.get_attempted().two_three_moves() == 1); + CHECK_EQ(command.get_attempted().two_three_moves(), 1); // A successful move was recorded - CHECK(command.get_succeeded().two_three_moves() == 1); + CHECK_EQ(command.get_succeeded().two_three_moves(), 1); // No failures - CHECK(command.get_failed().two_three_moves() == 0); + CHECK_EQ(command.get_failed().two_three_moves(), 0); // Get the results auto const& result = command.get_const_results(); // Did the triangulation actually change? We should have +1 cell - CHECK(result.get_triangulation().number_of_finite_cells() == - manifold.get_triangulation().number_of_finite_cells() + 1); + CHECK_EQ(result.get_triangulation().number_of_finite_cells(), + manifold.get_triangulation().number_of_finite_cells() + 1); REQUIRE(ergodic_moves::check_move(manifold, result, move_tracker::move_type::TWO_THREE)); fmt::print("Triangulation added a finite cell.\n"); @@ -334,20 +334,20 @@ SCENARIO("Queueing and executing moves", "[move command][!mayfail]") command.execute(); // An attempted move was recorded - CHECK(command.get_attempted().three_two_moves() == 1); + CHECK_EQ(command.get_attempted().three_two_moves(), 1); // A successful move was recorded - CHECK(command.get_succeeded().three_two_moves() == 1); + CHECK_EQ(command.get_succeeded().three_two_moves(), 1); // No failures - CHECK(command.get_failed().three_two_moves() == 0); + CHECK_EQ(command.get_failed().three_two_moves(), 0); // Get the results auto const& result = command.get_const_results(); // Did the triangulation actually change? We should have -1 cell - CHECK(result.get_triangulation().number_of_finite_cells() == - manifold.get_triangulation().number_of_finite_cells() - 1); + CHECK_EQ(result.get_triangulation().number_of_finite_cells(), + manifold.get_triangulation().number_of_finite_cells() - 1); REQUIRE(ergodic_moves::check_move(manifold, result, move_tracker::move_type::THREE_TWO)); fmt::print("Triangulation removed a finite cell.\n"); @@ -363,20 +363,20 @@ SCENARIO("Queueing and executing moves", "[move command][!mayfail]") command.execute(); // An attempted move was recorded - CHECK(command.get_attempted().two_six_moves() == 1); + CHECK_EQ(command.get_attempted().two_six_moves(), 1); // A successful move was recorded - CHECK(command.get_succeeded().two_six_moves() == 1); + CHECK_EQ(command.get_succeeded().two_six_moves(), 1); // No failures - CHECK(command.get_failed().two_six_moves() == 0); + CHECK_EQ(command.get_failed().two_six_moves(), 0); // Get the results auto const& result = command.get_const_results(); // Did the triangulation actually change? We should have +4 cell - CHECK(result.get_triangulation().number_of_finite_cells() == - manifold.get_triangulation().number_of_finite_cells() + 4); + CHECK_EQ(result.get_triangulation().number_of_finite_cells(), + manifold.get_triangulation().number_of_finite_cells() + 4); REQUIRE(ergodic_moves::check_move(manifold, result, move_tracker::move_type::TWO_SIX)); fmt::print("Triangulation added 4 finite cells.\n"); @@ -392,73 +392,74 @@ SCENARIO("Queueing and executing moves", "[move command][!mayfail]") command.execute(); // An attempted move was recorded - CHECK(command.get_attempted().six_two_moves() == 1); + CHECK_EQ(command.get_attempted().six_two_moves(), 1); // A successful move was recorded - CHECK(command.get_succeeded().six_two_moves() == 1); + CHECK_EQ(command.get_succeeded().six_two_moves(), 1); // No failures - CHECK(command.get_failed().six_two_moves() == 0); + CHECK_EQ(command.get_failed().six_two_moves(), 0); // Get the results auto const& result = command.get_const_results(); // Did the triangulation actually change? We should have -1 cell - CHECK(result.get_triangulation().number_of_finite_cells() == - manifold.get_triangulation().number_of_finite_cells() - 4); - REQUIRE(ergodic_moves::check_move(manifold, result, - move_tracker::move_type::SIX_TWO)); + CHECK_EQ(result.get_triangulation().number_of_finite_cells(), + manifold.get_triangulation().number_of_finite_cells() - 4); + CHECK(ergodic_moves::check_move(manifold, result, + move_tracker::move_type::SIX_TWO)); fmt::print("Triangulation removed 4 finite cells.\n"); } } } } -SCENARIO("Executing multiple moves on the queue", "[move command]") +SCENARIO("Executing multiple moves on the queue" * + doctest::test_suite("move_command")) { spdlog::debug("Executing multiple moves on the queue.\n"); GIVEN("A valid manifold") { auto constexpr desired_simplices = 9600; auto constexpr desired_timeslices = 7; - Manifold3 manifold(desired_simplices, desired_timeslices); + Manifold_3 const manifold(desired_simplices, desired_timeslices); REQUIRE(manifold.is_correct()); WHEN("(2,3) and (3,2) moves are queued.") { MoveCommand command(manifold); command.enqueue(move_tracker::move_type::TWO_THREE); command.enqueue(move_tracker::move_type::THREE_TWO); - THEN("There are two moves in the queue.") { CHECK(command.size() == 2); } + THEN("There are two moves in the queue.") { CHECK_EQ(command.size(), 2); } THEN("The moves are executed correctly.") { // Execute the moves command.execute(); // There should be 2 attempted moves - CHECK(command.get_attempted().total() == 2); + CHECK_EQ(command.get_attempted().total(), 2); command.print_attempts(); // There should be a successful (2,3) move auto successful_23_moves = command.get_succeeded().two_three_moves(); - CHECK(successful_23_moves == 1); + CHECK_EQ(successful_23_moves, 1); fmt::print("There was {} successful (2,3) move.\n", successful_23_moves); // There should be a successful (3,2) move auto successful_32_moves = command.get_succeeded().three_two_moves(); - CHECK(successful_32_moves == 1); + CHECK_EQ(successful_32_moves, 1); fmt::print("There was {} successful (3,2) move.\n", successful_32_moves); // There should be no failed moves - CHECK(command.get_failed().total() == 0); + CHECK_EQ(command.get_failed().total(), 0); command.print_errors(); // Get the results auto const& result = command.get_const_results(); // The moves should cancel out - CHECK(result.get_triangulation().number_of_finite_cells() == - manifold.get_triangulation().number_of_finite_cells()); + CHECK_EQ(result.get_triangulation().number_of_finite_cells(), + manifold.get_triangulation().number_of_finite_cells()); REQUIRE(ergodic_moves::check_move(manifold, result, move_tracker::move_type::FOUR_FOUR)); fmt::print("Triangulation moves cancelled out."); @@ -472,56 +473,59 @@ SCENARIO("Executing multiple moves on the queue", "[move command]") command.enqueue(move_tracker::move_type::FOUR_FOUR); command.enqueue(move_tracker::move_type::SIX_TWO); command.enqueue(move_tracker::move_type::THREE_TWO); - THEN("There are five moves in the queue.") { CHECK(command.size() == 5); } + THEN("There are five moves in the queue.") + { + CHECK_EQ(command.size(), 5); + } THEN("The moves are executed correctly.") { // Execute the moves command.execute(); // There should be 5 attempted moves - CHECK(command.get_attempted().total() == 5); + CHECK_EQ(command.get_attempted().total(), 5); command.print_attempts(); // There should be a successful (2,3) move auto successful_23_moves = command.get_succeeded().two_three_moves(); - CHECK(successful_23_moves == 1); + CHECK_EQ(successful_23_moves, 1); fmt::print("There was {} successful (2,3) move.\n", successful_23_moves); // There should be a successful (2,6) move auto successful_26_moves = command.get_succeeded().two_six_moves(); - CHECK(successful_26_moves == 1); + CHECK_EQ(successful_26_moves, 1); fmt::print("There was {} successful (2,6) move.\n", successful_26_moves); // There should be a successful (4,4) move auto successful_44_moves = command.get_succeeded().four_four_moves(); - CHECK(successful_44_moves == 1); + CHECK_EQ(successful_44_moves, 1); fmt::print("There was {} successful (4,4) move.\n", successful_44_moves); // There should be a successful (6,2) move auto successful_62_moves = command.get_succeeded().six_two_moves(); - CHECK(successful_62_moves == 1); + CHECK_EQ(successful_62_moves, 1); fmt::print("There was {} successful (6,2) move.\n", successful_62_moves); // There should be a successful (3,2) move auto successful_32_moves = command.get_succeeded().three_two_moves(); - CHECK(successful_32_moves == 1); + CHECK_EQ(successful_32_moves, 1); fmt::print("There was {} successful (3,2) move.\n", successful_32_moves); // There should be no failed moves - CHECK(command.get_failed().total() == 0); + CHECK_EQ(command.get_failed().total(), 0); command.print_errors(); // Get the results auto const& result = command.get_const_results(); // The moves should cancel out - CHECK(result.get_triangulation().number_of_finite_cells() == - manifold.get_triangulation().number_of_finite_cells()); + CHECK_EQ(result.get_triangulation().number_of_finite_cells(), + manifold.get_triangulation().number_of_finite_cells()); REQUIRE(ergodic_moves::check_move(manifold, result, move_tracker::move_type::FOUR_FOUR)); fmt::print("Triangulation moves cancelled out."); diff --git a/tests/Move_tracker_test.cpp b/tests/Move_tracker_test.cpp index 577d875f61..738b0875fb 100644 --- a/tests/Move_tracker_test.cpp +++ b/tests/Move_tracker_test.cpp @@ -10,13 +10,15 @@ #include "Move_tracker.hpp" -#include +#include + +#include "Manifold.hpp" using namespace std; using namespace manifolds; using namespace move_tracker; -SCENARIO("MoveTracker special members", "[move tracker]") +SCENARIO("MoveTracker special members" * doctest::test_suite("move_tracker")) { spdlog::debug("MoveTracker special members.\n"); GIVEN("A MoveTracker.") @@ -25,75 +27,95 @@ SCENARIO("MoveTracker special members", "[move tracker]") { THEN("It is no-throw destructible.") { - REQUIRE(is_nothrow_destructible_v>); + REQUIRE(is_nothrow_destructible_v>); spdlog::debug("It is no-throw destructible.\n"); } THEN("It is no-throw default constructible.") { - REQUIRE(is_nothrow_default_constructible_v>); + REQUIRE(is_nothrow_default_constructible_v>); spdlog::debug("It is no-throw default constructible.\n"); } THEN("It is copy constructible.") { - REQUIRE(is_copy_constructible_v>); + REQUIRE(is_copy_constructible_v>); spdlog::debug("It is copy constructible.\n"); } THEN("It is copy assignable.") { - REQUIRE(is_copy_assignable_v>); + REQUIRE(is_copy_assignable_v>); spdlog::debug("It is copy assignable.\n"); } THEN("It is no-throw move constructible.") { - REQUIRE(is_nothrow_move_constructible_v>); + REQUIRE(is_nothrow_move_constructible_v>); spdlog::debug("Small function optimization supported."); spdlog::debug("It is no-throw move constructible.\n"); } THEN("It is no-throw move assignable.") { - REQUIRE(is_nothrow_move_assignable_v>); + REQUIRE(is_nothrow_move_assignable_v>); spdlog::debug("It is no-throw move assignable.\n"); } THEN("It is no-throw swappable") { - REQUIRE(is_nothrow_swappable_v>); + REQUIRE(is_nothrow_swappable_v>); spdlog::debug("It is no-throw swappable.\n"); } } } } -SCENARIO("Move type to integer conversion", "[move tracker]") +SCENARIO("Move type to integer conversion" * + doctest::test_suite("move_tracker")) { spdlog::debug("Move type to integer conversion.\n"); GIVEN("A move type.") { auto move23 = move_type::TWO_THREE; - REQUIRE(as_integer(move23) == 0); + REQUIRE_EQ(as_integer(move23), 0); auto move32 = move_type::THREE_TWO; - REQUIRE(as_integer(move32) == 1); + REQUIRE_EQ(as_integer(move32), 1); auto move26 = move_type::TWO_SIX; - REQUIRE(as_integer(move26) == 2); + REQUIRE_EQ(as_integer(move26), 2); auto move62 = move_type::SIX_TWO; - REQUIRE(as_integer(move62) == 3); + REQUIRE_EQ(as_integer(move62), 3); auto move44 = move_type::FOUR_FOUR; - REQUIRE(as_integer(move44) == 4); + REQUIRE_EQ(as_integer(move44), 4); + } +} + +SCENARIO("Integer to move type conversion" * + doctest::test_suite("move_tracker")) +{ + spdlog::debug("Integer to move type conversion.\n"); + GIVEN("An integer.") + { + auto move_choice = 0; + REQUIRE_EQ(as_move(move_choice), move_type::TWO_THREE); + move_choice = 1; + REQUIRE_EQ(as_move(move_choice), move_type::THREE_TWO); + move_choice = 2; + REQUIRE_EQ(as_move(move_choice), move_type::TWO_SIX); + move_choice = 3; + REQUIRE_EQ(as_move(move_choice), move_type::SIX_TWO); + move_choice = 4; + REQUIRE_EQ(as_move(move_choice), move_type::FOUR_FOUR); } } -SCENARIO("MoveTracker functionality", "[move tracker]") +SCENARIO("MoveTracker functionality" * doctest::test_suite("move_tracker")) { spdlog::debug("MoveTracker functionality.\n"); GIVEN("A 3D Move_tracker.") { - MoveTracker tracked_moves; + MoveTracker tracked_moves; THEN("There are the correct number of elements.") { - REQUIRE(tracked_moves.size() == NUMBER_OF_3D_MOVES); + REQUIRE_EQ(tracked_moves.size(), NUMBER_OF_3D_MOVES); } THEN("Each element is zero-initialized.") { - REQUIRE(tracked_moves.total() == 0); + REQUIRE_EQ(tracked_moves.total(), 0); } THEN("Moves can be added.") { @@ -104,7 +126,7 @@ SCENARIO("MoveTracker functionality", "[move tracker]") tracked_moves.six_two_moves()++; tracked_moves.four_four_moves()++; // Now check that it's added - for (auto move : tracked_moves.moves_view()) { REQUIRE(move == 1); } + for (auto move : tracked_moves.moves_view()) { REQUIRE_EQ(move, 1); } } THEN("Two move trackers can be added.") { @@ -114,7 +136,7 @@ SCENARIO("MoveTracker functionality", "[move tracker]") tracked_moves.two_six_moves() += 1; tracked_moves.six_two_moves() += 1; tracked_moves.four_four_moves() += 1; - MoveTracker added_moves; + MoveTracker added_moves; added_moves.two_three_moves() += 2; added_moves.three_two_moves() += 2; added_moves.two_six_moves() += 2; @@ -124,19 +146,19 @@ SCENARIO("MoveTracker functionality", "[move tracker]") tracked_moves += added_moves; // Now check - for (auto move : tracked_moves.moves_view()) { REQUIRE(move == 3); } + for (auto move : tracked_moves.moves_view()) { REQUIRE_EQ(move, 3); } } } GIVEN("A 4D Move_tracker.") { - MoveTracker tracked_moves; + MoveTracker tracked_moves; THEN("There are the correct number of elements.") { - REQUIRE(tracked_moves.size() == NUMBER_OF_4D_MOVES); + REQUIRE_EQ(tracked_moves.size(), NUMBER_OF_4D_MOVES); } THEN("Each element is zero-initialized.") { - REQUIRE(tracked_moves.total() == 0); + REQUIRE_EQ(tracked_moves.total(), 0); } THEN("Moves can be added.") { @@ -148,7 +170,7 @@ SCENARIO("MoveTracker functionality", "[move tracker]") tracked_moves.six_four_moves()++; tracked_moves.two_eight_moves()++; tracked_moves.eight_two_moves()++; - for (auto move : tracked_moves.moves_view()) { REQUIRE(move == 1); } + for (auto move : tracked_moves.moves_view()) { REQUIRE_EQ(move, 1); } } THEN("Two move trackers can be added.") { @@ -160,7 +182,7 @@ SCENARIO("MoveTracker functionality", "[move tracker]") tracked_moves.six_four_moves() += 1; tracked_moves.two_eight_moves() += 1; tracked_moves.eight_two_moves() += 1; - MoveTracker added_moves; + MoveTracker added_moves; added_moves.two_four_moves() += 2; added_moves.four_two_moves() += 2; added_moves.three_three_moves() += 2; @@ -172,7 +194,7 @@ SCENARIO("MoveTracker functionality", "[move tracker]") tracked_moves += added_moves; // Now check - for (auto move : tracked_moves.moves_view()) { REQUIRE(move == 3); } + for (auto move : tracked_moves.moves_view()) { REQUIRE_EQ(move, 3); } } } } \ No newline at end of file diff --git a/tests/S3Action_test.cpp b/tests/S3Action_test.cpp index dd8990fcd1..27c57a523c 100644 --- a/tests/S3Action_test.cpp +++ b/tests/S3Action_test.cpp @@ -12,43 +12,45 @@ #include "S3Action.hpp" -#include +#include #include "Manifold.hpp" using namespace std; using namespace manifolds; -SCENARIO("Calculate the bulk action on S3 triangulations", "[action]") +SCENARIO("Calculate the bulk action on S3 triangulations" * + doctest::test_suite("s3action")) { spdlog::debug("Calculate the bulk action on S3 triangulations.\n"); GIVEN("A 3D 2-sphere foliated triangulation.") { - constexpr auto simplices = 6400; - constexpr auto timeslices = 7; - constexpr auto K = 1.1L; - constexpr auto Lambda = 0.1L; - Manifold3 universe(simplices, timeslices); + auto constexpr simplices = 6400; + auto constexpr timeslices = 7; + auto constexpr K = 1.1L; // NOLINT + auto constexpr Lambda = 0.1L; + Manifold_3 const universe(simplices, timeslices); // Verify triangulation - CHECK(universe.N3() == universe.simplices()); - CHECK(universe.N1() == universe.edges()); - CHECK(universe.N0() == universe.vertices()); - CHECK(universe.dimensionality() == 3); + CHECK_EQ(universe.N3(), universe.simplices()); + CHECK_EQ(universe.N1(), universe.edges()); + CHECK_EQ(universe.N0(), universe.vertices()); + CHECK_EQ(universe.dimensionality(), 3); CHECK(universe.is_correct()); universe.print_volume_per_timeslice(); - CHECK(universe.max_time() == timeslices); - CHECK(universe.min_time() == 1); + CHECK_EQ(universe.max_time(), timeslices); + CHECK_EQ(universe.min_time(), 1); WHEN("The alpha=-1 Bulk Action is calculated.") { auto Bulk_action = S3_bulk_action_alpha_minus_one( universe.N1_TL(), universe.N3_31_13(), universe.N3_22(), K, Lambda); THEN("The action falls within accepted values.") { - spdlog::debug("S3_bulk_action_alpha_minus_one() = {}\n", Bulk_action); - REQUIRE(3500 <= Bulk_action); - REQUIRE(Bulk_action <= 4500); + spdlog::debug("S3_bulk_action_alpha_minus_one() = {}\n", + Bulk_action.to_double()); + REQUIRE_LE(3500, Bulk_action); + REQUIRE_LE(Bulk_action, 4500); } } WHEN("The alpha=1 Bulk Action is calculated.") @@ -57,29 +59,30 @@ SCENARIO("Calculate the bulk action on S3 triangulations", "[action]") universe.N1_TL(), universe.N3_31_13(), universe.N3_22(), K, Lambda); THEN("The action falls within accepted values.") { - spdlog::debug("S3_bulk_action_alpha_one() = {}\n", Bulk_action); - REQUIRE(2000 <= Bulk_action); - REQUIRE(Bulk_action <= 3000); + spdlog::debug("S3_bulk_action_alpha_one() = {}\n", + Bulk_action.to_double()); + REQUIRE_LE(2000, Bulk_action); + REQUIRE_LE(Bulk_action, 3000); } } WHEN("The generalized Bulk Action is calculated.") { - constexpr auto Alpha = 0.6L; + auto constexpr Alpha = 0.6L; spdlog::debug("(Long double) Alpha = {}\n", Alpha); auto Bulk_action = S3_bulk_action(universe.N1_TL(), universe.N3_31_13(), universe.N3_22(), Alpha, K, Lambda); THEN("The action falls within accepted values.") { - spdlog::debug("S3_bulk_action() = {}\n", Bulk_action); - REQUIRE(2700 <= Bulk_action); - REQUIRE(Bulk_action <= 3700); + spdlog::debug("S3_bulk_action() = {}\n", Bulk_action.to_double()); + REQUIRE_LE(2700, Bulk_action); + REQUIRE_LE(Bulk_action, 3700); } } WHEN( "S3_bulk_action(alpha=1) and S3_bulk_action_alpha_one() are " "calculated.") { - constexpr auto Alpha = 1.0L; + auto constexpr Alpha = 1.0L; auto Bulk_action = S3_bulk_action(universe.N1_TL(), universe.N3_31_13(), universe.N3_22(), Alpha, K, Lambda); auto Bulk_action_one = S3_bulk_action_alpha_one( @@ -88,11 +91,12 @@ SCENARIO("Calculate the bulk action on S3 triangulations", "[action]") "S3_bulk_action(alpha=1) == S3_bulk_action_alpha_one() within " "tolerances.") { - spdlog::debug("S3_bulk_action() = {}\n", Bulk_action); - spdlog::debug("S3_bulk_action_alpha_one() = {}\n", Bulk_action_one); - Approx target = - Approx(utilities::Gmpzf_to_double(Bulk_action)).epsilon(TOLERANCE); - REQUIRE(utilities::Gmpzf_to_double(Bulk_action_one) == target); + spdlog::debug("S3_bulk_action() = {}\n", Bulk_action.to_double()); + spdlog::debug("S3_bulk_action_alpha_one() = {}\n", + Bulk_action_one.to_double()); + REQUIRE(utilities::Gmpzf_to_double(Bulk_action_one) == + doctest::Approx(utilities::Gmpzf_to_double(Bulk_action)) + .epsilon(TOLERANCE)); } } } diff --git a/tests/Settings_test.cpp b/tests/Settings_test.cpp index 1a60b37387..ec96ab4b78 100644 --- a/tests/Settings_test.cpp +++ b/tests/Settings_test.cpp @@ -11,14 +11,12 @@ #include "Settings.hpp" -#include - -#include -#include +#include +#include using namespace std; -SCENARIO("Check settings", "[settings]") +SCENARIO("Check settings" * doctest::test_suite("settings")) { GIVEN("Settings are retrieved.") { @@ -28,7 +26,7 @@ SCENARIO("Check settings", "[settings]") THEN("The value is std::int_fast32_t.") { fmt::print("TypeID of Int_precision is {}.\n", int_precision); - REQUIRE(int_precision == typeid(std::int_fast32_t).name()); + CHECK_EQ(int_precision, typeid(std::int_fast32_t).name()); } } WHEN("MPFR precision is queried.") @@ -37,22 +35,22 @@ SCENARIO("Check settings", "[settings]") THEN("The value is 256 bits.") { fmt::print("MPFR precision set to {}.\n", precision); - REQUIRE(precision == 256); + REQUIRE_EQ(precision, 256); } } WHEN("Memory alignment is queried.") { - auto const align_64 = ALIGNMENT_64_BIT; + auto constexpr align_64 = ALIGNMENT_64_BIT; THEN("The value is 64 bits.") { fmt::print("Memory alignment is set to {}.\n", align_64); - REQUIRE(align_64 == 64); + REQUIRE_EQ(align_64, 64); } - auto const align_32 = ALIGNMENT_32_BIT; + auto constexpr align_32 = ALIGNMENT_32_BIT; THEN("The value is 32 bits.") { fmt::print("Memory alignment is set to {}.\n", align_32); - REQUIRE(align_32 == 32); + REQUIRE_EQ(align_32, 32); } } } diff --git a/tests/Tetrahedron_test.cpp b/tests/Tetrahedron_test.cpp index 77a108123a..67b4e2c68a 100644 --- a/tests/Tetrahedron_test.cpp +++ b/tests/Tetrahedron_test.cpp @@ -10,53 +10,58 @@ /// @details Tests that 3D triangulated and foliated tetrahedrons are /// constructed correctly. -#include +#include + +#include #include "Foliated_triangulation.hpp" using namespace std; using namespace foliated_triangulations; -static inline double const RADIUS_2 = std::sqrt(4.0 / 3.0); // NOLINT +static inline auto constexpr RADIUS_2 = 2.0 * std::numbers::inv_sqrt3_v; -SCENARIO("Construct a tetrahedron in a Delaunay triangulation", "[tetrahedron]") +SCENARIO("Construct a tetrahedron in a Delaunay triangulation" * + doctest::test_suite("tetrahedron")) { - using Causal_vertices = Causal_vertices_t<3>; - using Point = Point_t<3>; + using Point = Point_t<3>; GIVEN("A vector of 4 vertices.") { - Causal_vertices causal_vertices; - causal_vertices.emplace_back(Point(0, 0, 0), 1); - causal_vertices.emplace_back(Point(1, 0, 0), 2); - causal_vertices.emplace_back(Point(0, 1, 0), 2); - causal_vertices.emplace_back(Point(0, 0, 1), 2); + vector vertices{ + Point{0, 0, 0}, + Point{1, 0, 0}, + Point{0, 1, 0}, + Point{0, 0, 1} + }; + vector timevalues{1, 2, 2, 2}; + auto causal_vertices = make_causal_vertices<3>(vertices, timevalues); WHEN("A triangulation is constructed using the vector.") { - FoliatedTriangulation3 triangulation(causal_vertices, 0, 1); + FoliatedTriangulation_3 const triangulation(causal_vertices, 0, 1); THEN("The triangulation has dimension 3.") { - REQUIRE(triangulation.dimension() == 3); + REQUIRE_EQ(triangulation.dimension(), 3); } THEN("The triangulation has 4 vertices.") { - REQUIRE(triangulation.number_of_vertices() == 4); + REQUIRE_EQ(triangulation.number_of_vertices(), 4); } THEN("The triangulation has 6 edges.") { - REQUIRE(triangulation.number_of_finite_edges() == 6); + REQUIRE_EQ(triangulation.number_of_finite_edges(), 6); } THEN("The triangulation has 4 faces.") { - REQUIRE(triangulation.number_of_finite_facets() == 4); + REQUIRE_EQ(triangulation.number_of_finite_facets(), 4); } THEN("The triangulation has 1 cell.") { - REQUIRE(triangulation.number_of_finite_cells() == 1); + REQUIRE_EQ(triangulation.number_of_finite_cells(), 1); } THEN("The triangulation is Delaunay.") @@ -77,11 +82,12 @@ SCENARIO("Construct a tetrahedron in a Delaunay triangulation", "[tetrahedron]") } } -SCENARIO("Find distances between points of the tetrahedron", "[tetrahedron]") +SCENARIO("Find distances between points of the tetrahedron" * + doctest::test_suite("tetrahedron")) { using Point = Point_t<3>; using Causal_vertices = Causal_vertices_t<3>; - using FoliatedTriangulation = FoliatedTriangulation3; + using FoliatedTriangulation = FoliatedTriangulation_3; using squared_distance = TriangulationTraits<3>::squared_distance; GIVEN("Points in a tetrahedron.") { @@ -99,79 +105,78 @@ SCENARIO("Find distances between points of the tetrahedron", "[tetrahedron]") WHEN("The Foliated triangulation is constructed with these points.") { FoliatedTriangulation triangulation(causal_vertices); - squared_distance r_2; + squared_distance constexpr r_2; THEN("The triangulation is initialized correctly.") { REQUIRE(triangulation.is_initialized()); } THEN("The squared distances of vertices from origin are correct.") { - fmt::print("v_1 is {}\n", v_1); - fmt::print("v_2 is {}\n", v_2); - fmt::print("v_3 is {}\n", v_3); - fmt::print("v_4 is {}\n", v_4); + fmt::print("v_1 is {}\n", utilities::point_to_str(v_1)); + fmt::print("v_2 is {}\n", utilities::point_to_str(v_2)); + fmt::print("v_3 is {}\n", utilities::point_to_str(v_3)); + fmt::print("v_4 is {}\n", utilities::point_to_str(v_4)); auto d_1 = r_2(origin, v_1); fmt::print("The squared distance between v_1 and the origin is {}\n", d_1); - CHECK(d_1 == 1); + CHECK_EQ(d_1, doctest::Approx(1.0)); auto d_2 = r_2(origin, v_2); fmt::print("The squared distance between v_2 and the origin is {}\n", d_2); - CHECK(d_2 == 1); + CHECK_EQ(d_2, doctest::Approx(1.0)); auto d_3 = r_2(origin, v_3); fmt::print("The squared distance between v_3 and the origin is {}\n", d_3); - CHECK(d_3 == 1); + CHECK_EQ(d_3, doctest::Approx(1.0)); - auto d4 = r_2(origin, v_4); + auto d_4 = r_2(origin, v_4); fmt::print("The squared distance between v_4 and the origin is {}\n", - d4); - CHECK(d4 == 4); + d_4); + CHECK_EQ(d_4, doctest::Approx(4.0)); } THEN("The squared distance between radius=1 vertices are 2.") { auto d_1 = r_2(v_1, v_2); - CHECK(d_1 == 2); + CHECK_EQ(d_1, doctest::Approx(2.0)); fmt::print("The squared distance between v_1 and v_2 is {}\n", d_1); auto d_2 = r_2(v_1, v_3); - CHECK(d_2 == 2); + CHECK_EQ(d_2, doctest::Approx(2.0)); fmt::print("The squared distance between v_1 and v_3 is {}\n", d_2); auto d_3 = r_2(v_2, v_3); - CHECK(d_3 == 2); + CHECK_EQ(d_3, doctest::Approx(2.0)); fmt::print("The squared distance between v_2 and v_3 is {}\n", d_3); } THEN("All vertices have correct timevalues.") { CHECK(triangulation.check_all_vertices()); // Human verification - auto print = [&triangulation](Vertex_handle_t<3> const& v) { + auto print = [&triangulation](Vertex_handle_t<3> const& vertex) { fmt::print( "Vertex ({}) with timevalue of {} has a squared radius of {} and " "a squared expected radius of {} with an expected timevalue of " "{}.\n", - v->point(), v->info(), squared_radius<3>(v), - std::pow(triangulation.expected_radius(v), 2), - triangulation.expected_timevalue(v)); + utilities::point_to_str(vertex->point()), vertex->info(), + squared_radius<3>(vertex), + std::pow(triangulation.expected_radius(vertex), 2), + triangulation.expected_timevalue(vertex)); }; - std::for_each(triangulation.get_vertices().begin(), - triangulation.get_vertices().end(), print); + ranges::for_each(triangulation.get_vertices(), print); } } } } -SCENARIO("Construct a foliated tetrahedron in a foliated triangulation", - "[tetrahedron]") +SCENARIO("Construct a foliated tetrahedron in a foliated triangulation" * + doctest::test_suite("tetrahedron")) { using Point = Point_t<3>; - using Causal_vertices = Causal_vertices_t<3>; - using FoliatedTriangulation = FoliatedTriangulation3; + using FoliatedTriangulation = FoliatedTriangulation_3; GIVEN("A vector of vertices and a vector of timevalues.") { - vector Vertices{ + vector Vertices{ Point{ 1, 0, 0}, Point{ 0, 1, 0}, Point{ 0, 0, 1}, @@ -181,15 +186,8 @@ SCENARIO("Construct a foliated tetrahedron in a foliated triangulation", WHEN("A foliated triangulation is constructed using the vectors.") { - // This is a complicated way to make Causal_vertices but is left - // here for reference - Causal_vertices causal_vertices; - causal_vertices.reserve(Vertices.size()); - std::transform( - Vertices.begin(), Vertices.end(), timevalue.begin(), - std::back_inserter(causal_vertices), - [](Point a, std::size_t b) { return std::make_pair(a, b); }); - FoliatedTriangulation triangulation(causal_vertices); + auto causal_vertices = make_causal_vertices<3>(Vertices, timevalue); + FoliatedTriangulation const triangulation(causal_vertices); THEN("The triangulation is initialized correctly.") { @@ -198,27 +196,27 @@ SCENARIO("Construct a foliated tetrahedron in a foliated triangulation", THEN("The triangulation has dimension 3.") { - REQUIRE(triangulation.dimension() == 3); + REQUIRE_EQ(triangulation.dimension(), 3); } THEN("The triangulation has 4 vertices.") { - REQUIRE(triangulation.number_of_vertices() == 4); + REQUIRE_EQ(triangulation.number_of_vertices(), 4); } THEN("The triangulation has 6 edges.") { - REQUIRE(triangulation.number_of_finite_edges() == 6); + REQUIRE_EQ(triangulation.number_of_finite_edges(), 6); } THEN("The triangulation has 4 faces.") { - REQUIRE(triangulation.number_of_finite_facets() == 4); + REQUIRE_EQ(triangulation.number_of_finite_facets(), 4); } THEN("The triangulation has 1 cell.") { - REQUIRE(triangulation.number_of_finite_cells() == 1); + REQUIRE_EQ(triangulation.number_of_finite_cells(), 1); } THEN("Timevalues are correct.") @@ -229,14 +227,14 @@ SCENARIO("Construct a foliated tetrahedron in a foliated triangulation", THEN("The cell info is correct.") { auto cell = triangulation.get_delaunay().finite_cells_begin(); - CHECK(expected_cell_type<3>(cell) == Cell_type::THREE_ONE); + CHECK_EQ(expected_cell_type<3>(cell), Cell_type::THREE_ONE); // Human verification triangulation.print_cells(); } THEN("There is one (3,1) simplex.") { - REQUIRE(triangulation.get_three_one().size() == 1); + REQUIRE_EQ(triangulation.get_three_one().size(), 1); } THEN("There are no (2,2) simplices.") @@ -251,12 +249,12 @@ SCENARIO("Construct a foliated tetrahedron in a foliated triangulation", THEN("There are 3 timelike edges.") { - REQUIRE(triangulation.N1_TL() == 3); + REQUIRE_EQ(triangulation.N1_TL(), 3); } THEN("There are 3 spacelike edges.") { - REQUIRE(triangulation.N1_SL() == 3); + REQUIRE_EQ(triangulation.N1_SL(), 3); } } } diff --git a/tests/Torus_test.cpp b/tests/Torus_test.cpp index cb9cc354a6..d1726648e0 100644 --- a/tests/Torus_test.cpp +++ b/tests/Torus_test.cpp @@ -1,6 +1,6 @@ /// Causal Dynamical Triangulations in C++ using CGAL /// -/// Copyright © 2017-2021 Adam Getchell +/// Copyright © 2017 Adam Getchell /// /// Tests that 2-tori and 3-tori are correctly constructed in 3D and 4D. /// @@ -8,29 +8,29 @@ /// @brief Tests for wraparound grids /// @author Adam Getchell -#include +#include #include "Torus_d.hpp" -SCENARIO("Torus construction", "[torus]") +SCENARIO("Torus construction" * doctest::test_suite("torus")) { std::size_t constexpr NUMBER_OF_POINTS = 250; std::vector points; points.reserve(NUMBER_OF_POINTS); GIVEN("A 2-torus") { - int dim = 3; WHEN("A 2-torus is constructed.") { THEN("It should not throw.") { + int constexpr dim = 3; REQUIRE_NOTHROW(make_d_cube(points, NUMBER_OF_POINTS, dim)); } } } - AND_GIVEN("A constructed 2-torus") + GIVEN("A constructed 2-torus") { - int dim = 3; + int constexpr dim = 3; make_d_cube(points, NUMBER_OF_POINTS, dim); WHEN("The type is queried") { @@ -44,11 +44,11 @@ SCENARIO("Torus construction", "[torus]") } GIVEN("A 3-torus") { - int dim = 4; WHEN("A 3-torus is constructed.") { THEN("It should not throw.") { + int constexpr dim = 4; REQUIRE_NOTHROW(make_d_cube(points, NUMBER_OF_POINTS, dim)); } } diff --git a/tests/Utilities_test.cpp b/tests/Utilities_test.cpp index 060ff9644f..a8773ca1ae 100644 --- a/tests/Utilities_test.cpp +++ b/tests/Utilities_test.cpp @@ -9,13 +9,17 @@ /// @author Adam Getchell /// @details Tests for random, conversion, and datetime functions. -#include +#include +#include + +#include #include using namespace std; using namespace utilities; -SCENARIO("Various string/stream/time utilities", "[utility]") +SCENARIO("Various string/stream/time utilities" * + doctest::test_suite("utilities")) { spdlog::debug("Various string/stream/time utilities.\n"); GIVEN("A topology_type.") @@ -23,22 +27,22 @@ SCENARIO("Various string/stream/time utilities", "[utility]") auto constexpr this_topology = topology_type::SPHERICAL; WHEN("Operator<< is invoked.") { - stringstream buffer; - std::streambuf* backup = cout.rdbuf(buffer.rdbuf()); + stringstream const buffer; + std::streambuf* backup = cout.rdbuf(buffer.rdbuf()); cout << this_topology; cout.rdbuf(backup); THEN("The output is correct.") { - CHECK_THAT(buffer.str(), Catch::Equals("spherical")); + CHECK_EQ(buffer.str(), "spherical"); spdlog::debug("buffer.str() contents: {}.\n", buffer.str()); } WHEN("fmt::print is invoked.") { THEN("The output is correct.") { - auto s = fmt::format("Topology type is: {}.\n", this_topology); - CHECK_THAT(s, Catch::Equals("Topology type is: spherical.\n")); - spdlog::debug("Topology type is: {}.\n", this_topology); + auto result = fmt::format("Topology type is: {}.\n", buffer.str()); + CHECK_EQ(result, "Topology type is: spherical.\n"); + spdlog::debug("Topology type is: {}.\n", buffer.str()); } } } @@ -51,7 +55,9 @@ SCENARIO("Various string/stream/time utilities", "[utility]") THEN("The output is correct.") { // Update test yearly - CHECK_THAT(current_date_time(), Catch::Contains("2022")); + auto const result = current_date_time(); + auto const year = result.find("2024"); + CHECK_NE(year, std::string::npos); // Human verification fmt::print("Current date and time is: {}\n", current_date_time()); } @@ -63,23 +69,29 @@ SCENARIO("Various string/stream/time utilities", "[utility]") auto constexpr simplices = 6700; auto constexpr timeslices = 16; auto const filename = - generate_filename(this_topology, dimensions, simplices, timeslices, - INITIAL_RADIUS, FOLIATION_SPACING); + make_filename(this_topology, dimensions, simplices, timeslices, + INITIAL_RADIUS, FOLIATION_SPACING); THEN("The output is correct.") { - CHECK_THAT(filename, - Catch::Contains("S3") && Catch::Contains("16") && - Catch::Contains("6700") && Catch::Contains("1.0") && - Catch::Contains("2022") && Catch::Contains("off")); + auto const topology = filename.string().find("S3"); + CHECK_NE(topology, std::string::npos); + auto const time = filename.string().find("16"); + CHECK_NE(time, std::string::npos); + auto const cells = filename.string().find("6700"); + CHECK_NE(cells, std::string::npos); + auto const initial_radius = filename.string().find("1.0"); + CHECK_NE(initial_radius, std::string::npos); + auto const file_suffix = filename.string().find("off"); + CHECK_NE(file_suffix, std::string::npos); // Human verification - fmt::print("Filename is: {}\n", filename); + fmt::print("Filename is: {}\n", filename.string()); } } } #endif } -SCENARIO("Printing Delaunay triangulations", "[utility]") +SCENARIO("Printing Delaunay triangulations" * doctest::test_suite("utilities")) { spdlog::debug("Printing Delaunay triangulations.\n"); GIVEN("A Delaunay_t<3> triangulation.") @@ -99,7 +111,60 @@ SCENARIO("Printing Delaunay triangulations", "[utility]") } } -SCENARIO("Randomizing functions", "[utility][!mayfail]") +SCENARIO("Reading and writing Delaunay triangulations to files" * + doctest::test_suite("utilities")) +{ + spdlog::debug("Reading and writing Delaunay triangulations to files.\n"); + GIVEN("A Manifold3 constructed from a Delaunay_t<3> triangulation") + { + Delaunay_t<3> triangulation; + triangulation.insert(Point_t<3>(0, 0, 0)); + triangulation.insert(Point_t<3>(1, 0, 0)); + triangulation.insert(Point_t<3>(0, 1, 0)); + triangulation.insert(Point_t<3>(0, 0, 1)); + // Construct a manifold from a Delaunay triangulation + manifolds::Manifold_3 const manifold( + foliated_triangulations::FoliatedTriangulation_3(triangulation, 0, 1)); + auto filename = make_filename(manifold); + WHEN("Writing to a file") + { + write_file(manifold); + THEN("The file should exist") + { + CHECK(std::filesystem::exists(filename)); + } + } + WHEN("Reading from a file") + { + auto triangulation_from_file = + utilities::read_file>(filename); + THEN("The file should contain the triangulation") + { + REQUIRE(triangulation_from_file.is_valid(true)); + REQUIRE_EQ(triangulation_from_file.dimension(), + manifold.dimensionality()); + REQUIRE_EQ(triangulation_from_file.number_of_finite_cells(), + manifold.N3()); + REQUIRE_EQ(triangulation_from_file.number_of_finite_facets(), + manifold.N2()); + REQUIRE_EQ(triangulation_from_file.number_of_finite_edges(), + manifold.N1()); + REQUIRE_EQ(triangulation_from_file.number_of_vertices(), manifold.N0()); + CHECK_EQ(triangulation_from_file, triangulation); + } + } + WHEN("Deleting a file") + { + std::filesystem::remove(filename); + THEN("The file should not exist") + { + CHECK_FALSE(std::filesystem::exists(filename)); + } + } + } +} + +SCENARIO("Randomizing functions" * doctest::test_suite("utilities")) { spdlog::debug("Randomizing functions.\n"); GIVEN("A PCG die roller") @@ -108,26 +173,23 @@ SCENARIO("Randomizing functions", "[utility][!mayfail]") { auto const roll1 = die_roll(); auto const roll2 = die_roll(); - THEN("They should probably be different.") - { - CHECK_FALSE(roll1 == roll2); - } + THEN("They should probably be different.") { WARN_NE(roll1, roll2); } } } GIVEN("A container of ints") { - Int_precision constexpr VECTOR_TEST_SIZE = 16; - vector v(VECTOR_TEST_SIZE); - iota(v.begin(), v.end(), 0); + Int_precision constexpr VECTOR_TEST_SIZE = 100; + array container{}; + iota(container.begin(), container.end(), 0); WHEN("The container is shuffled.") { - std::shuffle(v.begin(), v.end(), make_random_generator()); + ranges::shuffle(container, make_random_generator()); THEN("We get back the elements in random order.") { - auto j = 0; - for (auto i : v) { CHECK(i != j++); } + auto j = 0; // NOLINT + for (auto i : container) { WARN_NE(i, j++); } // NOLINT fmt::print("\nShuffled container verification:\n"); - fmt::print("{}\n", fmt::join(v, " ")); + fmt::print("{}\n", fmt::join(container, " ")); } } } @@ -135,43 +197,30 @@ SCENARIO("Randomizing functions", "[utility][!mayfail]") { WHEN("We generate six different random integers within the range.") { - auto constexpr min = 64; - auto constexpr max = 6400; - auto const value1 = generate_random_int(min, max); - auto const value2 = generate_random_int(min, max); - auto const value3 = generate_random_int(min, max); - auto const value4 = generate_random_int(min, max); - auto const value5 = generate_random_int(min, max); - auto const value6 = generate_random_int(min, max); + auto constexpr min = 64; + auto constexpr max = 6400; + auto const value1 = generate_random_int(min, max); + auto const value2 = generate_random_int(min, max); + auto const value3 = generate_random_int(min, max); + auto const value4 = generate_random_int(min, max); + auto const value5 = generate_random_int(min, max); + auto const value6 = generate_random_int(min, max); + array container = {value1, value2, value3, value4, value5, value6}; THEN("They should all fall within the range and all be different.") { - CHECK(value1 >= min); - CHECK(value1 <= max); - CHECK(value2 >= min); - CHECK(value2 <= max); - CHECK(value3 >= min); - CHECK(value3 <= max); - CHECK(value4 >= min); - CHECK(value4 <= max); - CHECK(value5 >= min); - CHECK(value5 <= max); - CHECK(value6 >= min); - CHECK(value6 <= max); - CHECK_FALSE(value1 == value2); - CHECK_FALSE(value1 == value3); - CHECK_FALSE(value1 == value4); - CHECK_FALSE(value1 == value5); - CHECK_FALSE(value1 == value6); - CHECK_FALSE(value2 == value3); - CHECK_FALSE(value2 == value4); - CHECK_FALSE(value2 == value5); - CHECK_FALSE(value2 == value6); - CHECK_FALSE(value3 == value4); - CHECK_FALSE(value3 == value5); - CHECK_FALSE(value3 == value6); - CHECK_FALSE(value4 == value5); - CHECK_FALSE(value4 == value6); - CHECK_FALSE(value5 == value6); + // All elements are >= min + CHECK_GE(*ranges::min_element(container), min); + + // All elements are <= max + CHECK_LE(*ranges::max_element(container), max); + + // All elements are different + ranges::sort(container); + CHECK(ranges::is_sorted(container)); + auto adjacent_iterator = ranges::adjacent_find(container); + + // If the iterator is equal to the end, then all elements are different + CHECK_EQ(adjacent_iterator, container.end()); } } } @@ -179,42 +228,30 @@ SCENARIO("Randomizing functions", "[utility][!mayfail]") { WHEN("We generate six different timeslices within the range.") { - auto constexpr max = 256; - auto const value1 = generate_random_timeslice(max); - auto const value2 = generate_random_timeslice(max); - auto const value3 = generate_random_timeslice(max); - auto const value4 = generate_random_timeslice(max); - auto const value5 = generate_random_timeslice(max); - auto const value6 = generate_random_timeslice(max); + auto constexpr max = 256; + auto const value1 = generate_random_timeslice(max); + auto const value2 = generate_random_timeslice(max); + auto const value3 = generate_random_timeslice(max); + auto const value4 = generate_random_timeslice(max); + auto const value5 = generate_random_timeslice(max); + auto const value6 = generate_random_timeslice(max); + array container = {value1, value2, value3, value4, value5, value6}; THEN("They should all fall within the range and be different.") { - CHECK(value1 >= 1); - CHECK(value1 <= max); - CHECK(value2 >= 1); - CHECK(value2 <= max); - CHECK(value3 >= 1); - CHECK(value3 <= max); - CHECK(value4 >= 1); - CHECK(value4 <= max); - CHECK(value5 >= 1); - CHECK(value5 <= max); - CHECK(value6 >= 1); - CHECK(value6 <= max); - CHECK_FALSE(value1 == value2); - CHECK_FALSE(value1 == value3); - CHECK_FALSE(value1 == value4); - CHECK_FALSE(value1 == value5); - CHECK_FALSE(value1 == value6); - CHECK_FALSE(value2 == value3); - CHECK_FALSE(value2 == value4); - CHECK_FALSE(value2 == value5); - CHECK_FALSE(value2 == value6); - CHECK_FALSE(value3 == value4); - CHECK_FALSE(value3 == value5); - CHECK_FALSE(value3 == value6); - CHECK_FALSE(value4 == value5); - CHECK_FALSE(value4 == value6); - CHECK_FALSE(value5 == value6); + auto constexpr min = 1; + // All elements are >= min + CHECK_GE(*ranges::min_element(container), min); + + // All elements are <= max + CHECK_LE(*ranges::max_element(container), max); + + // All elements are different + ranges::sort(container); + CHECK(ranges::is_sorted(container)); + auto adjacent_iterator = ranges::adjacent_find(container); + + // If the iterator is equal to the end, then all elements are different + CHECK_EQ(adjacent_iterator, container.end()); } } } @@ -227,8 +264,8 @@ SCENARIO("Randomizing functions", "[utility][!mayfail]") auto const value = generate_random_real(min, max); THEN("The real number should lie within that range.") { - REQUIRE(min <= value); - REQUIRE(value <= max); + REQUIRE_LE(min, value); + REQUIRE_LE(value, max); } } } @@ -236,36 +273,28 @@ SCENARIO("Randomizing functions", "[utility][!mayfail]") { WHEN("We generate six probabilities.") { - auto const value1 = generate_probability(); - auto const value2 = generate_probability(); - auto const value3 = generate_probability(); - auto const value4 = generate_probability(); - auto const value5 = generate_probability(); - auto const value6 = generate_probability(); + auto const value1 = generate_probability(); + auto const value2 = generate_probability(); + auto const value3 = generate_probability(); + auto const value4 = generate_probability(); + auto const value5 = generate_probability(); + auto const value6 = generate_probability(); + array container = {value1, value2, value3, value4, value5, value6}; THEN("They should all be different.") { - CHECK_FALSE(value1 == value2); - CHECK_FALSE(value1 == value3); - CHECK_FALSE(value1 == value4); - CHECK_FALSE(value1 == value5); - CHECK_FALSE(value1 == value6); - CHECK_FALSE(value2 == value3); - CHECK_FALSE(value2 == value4); - CHECK_FALSE(value2 == value5); - CHECK_FALSE(value2 == value6); - CHECK_FALSE(value3 == value4); - CHECK_FALSE(value3 == value5); - CHECK_FALSE(value3 == value6); - CHECK_FALSE(value4 == value5); - CHECK_FALSE(value4 == value6); - CHECK_FALSE(value5 == value6); + ranges::sort(container); + CHECK(ranges::is_sorted(container)); + auto adjacent_iterator = ranges::adjacent_find(container); + + // If the iterator is equal to the end, then all elements are different + CHECK_EQ(adjacent_iterator, container.end()); } } } } -SCENARIO("Expected points per timeslice", "[utility]") +SCENARIO("Expected points per timeslice" * doctest::test_suite("utilities")) { spdlog::debug("Expected points per timeslice.\n"); GIVEN("Simplices and timeslices for various foliations") @@ -274,35 +303,35 @@ SCENARIO("Expected points per timeslice", "[utility]") { THEN("The results are correct.") { - REQUIRE(expected_points_per_timeslice(3, 2, 2) == 2); + REQUIRE_EQ(expected_points_per_timeslice(3, 2, 2), 2); } } WHEN("We request 500 simplices on 4 timeslices.") { THEN("The results are correct.") { - REQUIRE(expected_points_per_timeslice(3, 500, 4) == 50); + REQUIRE_EQ(expected_points_per_timeslice(3, 500, 4), 50); } } WHEN("We request 5000 simplices on 8 timeslices.") { THEN("The results are correct.") { - REQUIRE(expected_points_per_timeslice(3, 5000, 8) == 125); + REQUIRE_EQ(expected_points_per_timeslice(3, 5000, 8), 125); } } WHEN("We request 64,000 simplices on 16 timeslices.") { THEN("The results are correct.") { - REQUIRE(expected_points_per_timeslice(3, 64000, 16) == 600); + REQUIRE_EQ(expected_points_per_timeslice(3, 64000, 16), 600); } } WHEN("We request 640,000 simplices on 64 timeslices.") { THEN("The results are correct.") { - REQUIRE(expected_points_per_timeslice(3, 640000, 64) == 1000); + REQUIRE_EQ(expected_points_per_timeslice(3, 640000, 64), 1000); } } WHEN("We specify 4 dimensions") @@ -316,7 +345,7 @@ SCENARIO("Expected points per timeslice", "[utility]") } } -SCENARIO("Exact number (Gmpzf) conversion", "[utility]") +SCENARIO("Exact number (Gmpzf) conversion" * doctest::test_suite("utilities")) { spdlog::debug("Exact number (Gmpzf) conversion.\n"); GIVEN("A number not exactly representable in binary.") @@ -327,7 +356,7 @@ SCENARIO("Exact number (Gmpzf) conversion", "[utility]") auto const converted_value = Gmpzf_to_double(TEST_VALUE); THEN("It should be exact when converted back from double to Gmpzf.") { - REQUIRE(TEST_VALUE == Gmpzf(converted_value)); + REQUIRE_EQ(TEST_VALUE, Gmpzf(converted_value)); } } } diff --git a/tests/Vertex_test.cpp b/tests/Vertex_test.cpp index 53d87456df..cf84a64f13 100644 --- a/tests/Vertex_test.cpp +++ b/tests/Vertex_test.cpp @@ -9,34 +9,37 @@ /// @author Adam Getchell /// @details Tests for inserting and deleting vertices. -#include +#include + +#include +#include #include "Manifold.hpp" using namespace manifolds; -static inline double const RADIUS_2 = std::sqrt(4.0 / 3.0); // NOLINT +static inline auto constexpr RADIUS_2 = 2.0 * std::numbers::inv_sqrt3_v; -SCENARIO("Point operations", "[vertex]") +SCENARIO("Point operations" * doctest::test_suite("vertex")) { using Point = Point_t<3>; GIVEN("Some points.") { - auto point_1 = Point(0, 0, 0); - auto point_2 = Point(0, 0.0, 0.0); - auto point_3 = Point(1, 1, 1); + auto const point_1 = Point(0, 0, 0); + auto const point_2 = Point(0, 0.0, 0.0); + auto const point_3 = Point(1, 1, 1); WHEN("They are compared.") { - THEN("Similar points are equal.") { REQUIRE(point_1 == point_2); } - THEN("Dissimilar points are not equal.") { REQUIRE(point_1 != point_3); } + THEN("Similar points are equal.") { REQUIRE_EQ(point_1, point_2); } + THEN("Dissimilar points are not equal.") { REQUIRE_NE(point_1, point_3); } } } } -SCENARIO("Vertex operations", "[vertex]") +SCENARIO("Vertex operations" * doctest::test_suite("vertex")) { using Causal_vertices = Causal_vertices_t<3>; - using Manifold = Manifold3; + using Manifold = Manifold_3; using Point = Point_t<3>; GIVEN("A foliated Delaunay triangulation.") { @@ -44,10 +47,11 @@ SCENARIO("Vertex operations", "[vertex]") WHEN("A vertex is inserted.") { causal_vertices.emplace_back(Point(0, 0, 0), 1); - Manifold manifold(causal_vertices, 0, 1); + Manifold const manifold(causal_vertices, 0, 1); THEN("The vertex is in the manifold.") { - auto vertex = manifold.get_vertices(); + // auto vertex = manifold.get_vertices(); + auto vertex = manifold.get_vertices_span(); REQUIRE(manifold.is_vertex(vertex.front())); } @@ -56,13 +60,13 @@ SCENARIO("Vertex operations", "[vertex]") REQUIRE(manifold.is_valid()); } - THEN("There is 1 vertex.") { REQUIRE(manifold.N0() == 1); } + THEN("There is 1 vertex.") { REQUIRE_EQ(manifold.N0(), 1); } - THEN("There are no edges.") { REQUIRE(manifold.N1() == 0); } + THEN("There are no edges.") { REQUIRE_EQ(manifold.N1(), 0); } THEN("A 1 vertex manifold has dimension 0.") { - REQUIRE(manifold.dimensionality() == 0); + REQUIRE_EQ(manifold.dimensionality(), 0); } THEN("The vertex is valid.") @@ -77,11 +81,11 @@ SCENARIO("Vertex operations", "[vertex]") { causal_vertices.emplace_back(Point(0, 0, 0), 1); causal_vertices.emplace_back(Point(1, 0, 0), 2); - Manifold manifold(causal_vertices, 0, 1); + Manifold const manifold(causal_vertices, 0, 1); THEN("The vertices are in the manifold.") { - auto vertex = manifold.get_vertices(); + auto vertex = manifold.get_vertices_span(); REQUIRE(manifold.is_vertex(vertex.front())); REQUIRE(manifold.is_vertex(vertex.back())); } @@ -91,15 +95,15 @@ SCENARIO("Vertex operations", "[vertex]") REQUIRE(manifold.is_valid()); } - THEN("There are 2 vertices.") { REQUIRE(manifold.N0() == 2); } + THEN("There are 2 vertices.") { REQUIRE_EQ(manifold.N0(), 2); } - THEN("There is 1 edge.") { REQUIRE(manifold.N1() == 1); } + THEN("There is 1 edge.") { REQUIRE_EQ(manifold.N1(), 1); } - THEN("There are no faces.") { REQUIRE(manifold.N2() == 0); } + THEN("There are no faces.") { REQUIRE_EQ(manifold.N2(), 0); } THEN("A 2 vertex manifold has dimension 1.") { - REQUIRE(manifold.dimensionality() == 1); + REQUIRE_EQ(manifold.dimensionality(), 1); } THEN("The vertices are valid.") @@ -119,9 +123,11 @@ SCENARIO("Vertex operations", "[vertex]") THEN("The vertices are in the manifold.") { - auto vertices = manifold.get_vertices(); - auto require = [&manifold](auto& v) { REQUIRE(manifold.is_vertex(v)); }; - std::for_each(vertices.begin(), vertices.end(), require); + auto vertices = manifold.get_vertices_span(); + auto require = [&manifold](auto& vertex) { + REQUIRE(manifold.is_vertex(vertex)); + }; + std::ranges::for_each(vertices, require); } THEN("The Delaunay triangulation is valid.") @@ -129,17 +135,17 @@ SCENARIO("Vertex operations", "[vertex]") REQUIRE(manifold.is_valid()); } - THEN("There are 3 vertices.") { REQUIRE(manifold.N0() == 3); } + THEN("There are 3 vertices.") { REQUIRE_EQ(manifold.N0(), 3); } - THEN("There are 3 edges.") { REQUIRE(manifold.N1() == 3); } + THEN("There are 3 edges.") { REQUIRE_EQ(manifold.N1(), 3); } - THEN("There is 1 face.") { REQUIRE(manifold.N2() == 1); } + THEN("There is 1 face.") { REQUIRE_EQ(manifold.N2(), 1); } - THEN("There are no simplices.") { REQUIRE(manifold.N3() == 0); } + THEN("There are no simplices.") { REQUIRE_EQ(manifold.N3(), 0); } THEN("A 3 vertex manifold has dimension 2.") { - REQUIRE(manifold.dimensionality() == 2); + REQUIRE_EQ(manifold.dimensionality(), 2); } THEN("The vertices are valid.") @@ -160,9 +166,11 @@ SCENARIO("Vertex operations", "[vertex]") THEN("The vertices are in the manifold.") { - auto vertices = manifold.get_vertices(); - auto require = [&manifold](auto& v) { REQUIRE(manifold.is_vertex(v)); }; - std::for_each(vertices.begin(), vertices.end(), require); + auto vertices = manifold.get_vertices_span(); + auto require = [&manifold](auto& vertex) { + REQUIRE(manifold.is_vertex(vertex)); + }; + std::ranges::for_each(vertices, require); } THEN("The Delaunay triangulation is valid.") @@ -170,17 +178,17 @@ SCENARIO("Vertex operations", "[vertex]") REQUIRE(manifold.is_valid()); } - THEN("There are 4 vertices.") { REQUIRE(manifold.N0() == 4); } + THEN("There are 4 vertices.") { REQUIRE_EQ(manifold.N0(), 4); } - THEN("There are 6 edges.") { REQUIRE(manifold.N1() == 6); } + THEN("There are 6 edges.") { REQUIRE_EQ(manifold.N1(), 6); } - THEN("There are 4 faces.") { REQUIRE(manifold.N2() == 4); } + THEN("There are 4 faces.") { REQUIRE_EQ(manifold.N2(), 4); } - THEN("There is a simplex.") { REQUIRE(manifold.N3() == 1); } + THEN("There is a simplex.") { REQUIRE_EQ(manifold.N3(), 1); } THEN("A 4 vertex manifold has dimension 3.") { - REQUIRE(manifold.dimensionality() == 3); + REQUIRE_EQ(manifold.dimensionality(), 3); } THEN("The vertices are valid.") @@ -203,11 +211,11 @@ SCENARIO("Vertex operations", "[vertex]") THEN("The vertices are in the manifold.") { - auto vertices = manifold.get_vertices(); + auto vertices = manifold.get_vertices_span(); auto require = [&manifold](auto& vertex_candidate) { REQUIRE(manifold.is_vertex(vertex_candidate)); }; - std::for_each(vertices.begin(), vertices.end(), require); + std::ranges::for_each(vertices, require); } THEN("The Delaunay triangulation is valid.") @@ -215,17 +223,17 @@ SCENARIO("Vertex operations", "[vertex]") REQUIRE(manifold.is_valid()); } - THEN("There are 5 vertices.") { REQUIRE(manifold.N0() == 5); } + THEN("There are 5 vertices.") { REQUIRE_EQ(manifold.N0(), 5); } - THEN("There are 9 edges.") { REQUIRE(manifold.N1() == 9); } + THEN("There are 9 edges.") { REQUIRE_EQ(manifold.N1(), 9); } - THEN("There are 7 faces.") { REQUIRE(manifold.N2() == 7); } + THEN("There are 7 faces.") { REQUIRE_EQ(manifold.N2(), 7); } - THEN("There are 2 simplexes.") { REQUIRE(manifold.N3() == 2); } + THEN("There are 2 simplexes.") { REQUIRE_EQ(manifold.N3(), 2); } THEN("A 5 vertex manifold still has dimension 3.") { - REQUIRE(manifold.dimensionality() == 3); + REQUIRE_EQ(manifold.dimensionality(), 3); } THEN("The vertices are valid.") diff --git a/tests/main.cpp b/tests/main.cpp index db4e31ed3b..8af31242af 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -5,49 +5,10 @@ ******************************************************************************/ /// @file main.cpp -/// @brief Catch test driver -/// @author https://github.com/catchorg and Adam Getchell -/// @details Main Catch test and spdlog driver +/// @brief doctest driver +/// @author https://github.com/doctest/doctest +/// @details Main doctest driver -#define CATCH_CONFIG_RUNNER -#define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS -#define CATCH_CONFIG_CPP17_STRING_VIEW -#define CATCH_CONFIG_CPP17_VARIANT -#define CATCH_CONFIG_CPP17_OPTIONAL -#define CATCH_CONFIG_CPP17_BYTE -#define CATCH_CONFIG_NO_NOMINMAX -#include - -#include "Utilities.hpp" - -auto main(int argc, char* argv[]) -> int // NOLINT -try -{ - Catch::Session session; // There must be exactly one instance - - utilities::create_logger(); - - // writing to session.configData() here sets defaults - // this is the preferred way to set them - - if (auto returnCode = session.applyCommandLine(argc, argv); returnCode != 0) - { // Indicates a command line error - return returnCode; - } - - // writing to session.configData() or session.Config() here - // overrides command line args - // only do this if you know you need to - - int numFailed = session.run(); - - // numFailed is clamped to 255 as some unixes only use the lower 8 bits. - // This clamping has already been applied, so just return it here - // You can also do any post run clean-up here - return numFailed; -} -catch (...) -{ - spdlog::critical("Exception thrown in CDT_test ... Exiting.\n"); - return EXIT_FAILURE; -} +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#define DOCTEST_CONFIG_SUPER_FAST_ASSERTS +#include diff --git a/vcpkg.json b/vcpkg.json index aaf72d2610..90b0879396 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -2,16 +2,15 @@ "name": "cdt-plusplus", "version": "0.1.8", "dependencies": [ - "catch2", + "boost-program-options", + "doctest", "date", - "docopt", "fmt", "ms-gsl", "eigen3", "pcg", "spdlog", "tbb", - "tl-expected", "tl-function-ref", { "name": "yasm-tool",