diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml deleted file mode 100644 index af64045c..00000000 --- a/.github/workflows/build.yaml +++ /dev/null @@ -1,72 +0,0 @@ -name: build_and_test - -on: - - push - - pull_request - -env: - # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) - BUILD_TYPE: Release - -jobs: - build_and_test: - runs-on: ubuntu-latest - strategy: - matrix: - python-version: ["3.10", "3.11", "3.12", "3.13"] - steps: - - uses: actions/checkout@v2 - - uses: conda-incubator/setup-miniconda@v3 - with: - auto-update-conda: true - miniconda-version: "latest" - channel-priority: strict - channels: conda-forge - python-version: ${{ matrix.python-version }} - environment-file: etc/conda-forge-testing.yaml - activate-environment: ndarray - - - name: Print conda environment - run: conda list -n ndarray - - - name: Create Build Environment - # Some projects don't allow in-source building, so create a separate build directory - # We'll use this as our working directory for all subsequent commands - run: cmake -E make_directory ${{github.workspace}}/build - - - name: Configure CMake - # Use a bash shell so we can use the same syntax for environment variable - # access regardless of the host operating system - shell: bash -l {0} - working-directory: ${{github.workspace}}/build - # Note the current convention is to use the -S and -B options here to specify source - # and build directories, but this is only available with CMake 3.13 and higher. - # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 - run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DNDARRAY_PYBIND11=ON -DCMAKE_PREFIX_PATH=$CONDA_PREFIX -DPYTHON_VERSION=${{matrix.python-version}} - - - name: Build - working-directory: ${{github.workspace}}/build - shell: bash -l {0} - # Execute the build. You can specify a specific target with "--target " - run: cmake --build . --config $BUILD_TYPE - - - name: Test - working-directory: ${{github.workspace}}/build - shell: bash -l {0} - # Execute tests defined by the CMake configuration. - # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail - run: ctest -C $BUILD_TYPE - - - name: Build Documentation - working-directory: ${{github.workspace}}/build - shell: bash -l {0} - run: make doc - - - name: Deploy Documentation (master/py3.8 only) - uses: JamesIves/github-pages-deploy-action@3.7.1 - if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && matrix.python-version == 3.8 }} - with: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - BRANCH: gh-pages # The branch the action should deploy to. - FOLDER: build/doc/html # The folder the action should deploy. - CLEAN: true # Automatically remove deleted files from the deploy branch diff --git a/.gitignore b/.gitignore index 567609b1..f00517fa 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,5 @@ -build/ +*.dylib +*.o +*.os +*.so +build diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 33a8de5e..00000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,38 +0,0 @@ -# ndarray change log - -## 1.6.5 - -### Bug fixes - -Fix compilation issues with newer compiler and boost versions. - -## 1.5.1 - -### Bug fixes - -Reverted a non-portable workaround for bad module suffixes with pybind11 2.1.x. Please just use pybind11 2.2.x instead. - -## 1.5.0 - -### New features - -`ndarray::EigenView` is not compatible with Eigen 3.3 and fixing this appears to be more work than it's worth, -so this version deprecates `ndarray::EigenView` and adds new free functions `asEigen`, `asEigenArray` -and `asEigenMatrix` which return an `eigen::Map` view of an ndarray array. Unlike `ndarray::EigenView`, -the returned eigen map does not own the memory, so you must be careful to keep the ndarray array around -until you are done with the map. - -Added build option `-DNDARRAY_EIGENVIEW` which controls whether to build ndarray::EigenView. The default is OFF. - -Added build option `-DNDARRAY_STDPYBIND11EIGEN` which, if ON, imports `pybind11/eigen.h` into `ndarray/eigen.h`. -The intent is to make it easier to port old pybind11 wrappers that used EigenView. The default is `OFF`. -`-DNDARRAY_EIGENVIEW` and `-DNDARRAY_STDPYBIND11EIGEN` cannot both be `ON`. - -### Deprecations and removals - -Support for SWIG has been dropped as of the 1.5.0 release. - -### Bug fixes - -Fixed a race condition in the cmake build files: ArrayBaseN.h.m4 might not be processed before other -m4 include files that rely on it. This may fix ndarray issue 71. diff --git a/CMakeLists.txt b/CMakeLists.txt index 477666c7..57a392d0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,37 +1,104 @@ -cmake_minimum_required(VERSION 3.15) -project(ndarray LANGUAGES CXX) -set (CMAKE_CXX_STANDARD 11) - -# Build is split into two broad passes: -# (1) Generation, which performs macro-expansion and header installation. -# (2) Test, which builds & executes zero-dependency core and (optional) dependent tests. -# -# The generation phase does not rely on the presence of external libraries, -# though the installed headers may be dependent on these libraries. This allows -# generation & installation of ndarray as a vendorized dependency of external -# projects without ndarry's optional dependencies. -# -# The test phase may rely on external libraries, which are enabled via the -# options below. Library resolution is deferred to tests/CMakeLists.txt and -# only executed if NDARRAY_TEST is enabled. - -option(NDARRAY_TEST "Enable tests?" ON) -option(NDARRAY_EIGEN "Enable Eigen tests?" ON) -option(NDARRAY_FFTW "Enable FFTW tests?" ON) -option(NDARRAY_PYBIND11 "Enable Pybind11 tests?" OFF) - -add_subdirectory(include) - -find_package(Doxygen) +cmake_minimum_required(VERSION 3.5) +project(ndarray VERSION 2.0.0 LANGUAGES CXX) + +set(CMAKE_EXPORT_COMPILE_COMMANDS 1) # used to obtain build commands for expected compile failure tests + +option(TEST_THREADS "Build and run tests with threads enabled" ON) +option(TEST_CXX17 "Build and run tests with C++17 features" ON) + +find_package(fmt 4.1 REQUIRED) +find_package(Catch2 2.2.3 REQUIRED) +find_package(pybind11 2.2) +find_package(Doxygen 1.8) + if(DOXYGEN_FOUND) - add_subdirectory(doc) + set(DOXYGEN_IN ${CMAKE_CURRENT_SOURCE_DIR}/doc/doxygen.conf.in) + set(DOXYGEN_OUT ${CMAKE_CURRENT_BINARY_DIR}/doxygen.conf) + configure_file(${DOXYGEN_IN} ${DOXYGEN_OUT} @ONLY) + add_custom_target(doc + COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_OUT} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Generating API documentation with Doxygen" + VERBATIM + ) +else(DOXYGEN_FOUND) + message("doc target disabled; Doxygen not found.") endif(DOXYGEN_FOUND) -if(NDARRAY_TEST) - enable_testing() - add_subdirectory(tests) -endif(NDARRAY_TEST) +add_library(ndarray INTERFACE) +target_include_directories(ndarray + INTERFACE + $ + $ +) +target_link_libraries(ndarray INTERFACE fmt::fmt) +if(TEST_CXX17) + target_compile_features(ndarray + INTERFACE + cxx_std_17 + ) +else(TEST_CXX17) + target_compile_features(ndarray + INTERFACE + cxx_std_14 + ) +endif(TEST_CXX17) + +enable_testing() + +add_executable(tests_cpp + tests/main.cpp + tests/Layout.cpp + tests/ArrayImpl.cpp + tests/Array.cpp + tests/views.cpp + tests/expressions.cpp +) +target_link_libraries(tests_cpp Catch2::Catch2 ndarray) +include(Catch) +catch_discover_tests(tests_cpp) + +if(TEST_THREADS) + set(CMAKE_THREAD_PREFER_PTHREAD TRUE) + set(THREADS_PREFER_PTHREAD_FLAG TRUE) + find_package(Threads REQUIRED) + target_link_libraries(tests_cpp Threads::Threads) + target_compile_definitions(tests_cpp PRIVATE NDARRAY_TEST_WITH_THREADS=1) +endif(TEST_THREADS) + + +if(pybind11_FOUND) + + function(ndarray_add_python_test_module MODULE_NAME) + configure_file(tests/${MODULE_NAME}.py tests/${MODULE_NAME}.py COPYONLY) + endfunction(ndarray_add_python_test_module) + + # TODO: use unittest discovery and TEST_INCLUDE_FILES to add finer-grained + # tests dynamically + + function(ndarray_add_python_test TEST_NAME) + set(FULL_TEST_NAME ${TEST_NAME}_Python) + ndarray_add_python_test_module(test_${TEST_NAME}) + add_test( + NAME + ${FULL_TEST_NAME} + COMMAND + ${PYTHON_EXECUTABLE} -m unittest tests.test_${TEST_NAME} + WORKING_DIRECTORY + ${CMAKE_CURRENT_BINARY_DIR} + ) + set_tests_properties( + ${FULL_TEST_NAME} + PROPERTIES LABELS "python;${TEST_NAME}" + ) + endfunction(ndarray_add_python_test) + + ndarray_add_python_test_module(__init__) + ndarray_add_python_test_module(compilation) + ndarray_add_python_test(Array) + +else() + + message("pybind11 not found; not running Python tests.") -# installation -install(DIRECTORY include/ DESTINATION include/ - FILES_MATCHING PATTERN "*.h") +endif() diff --git a/LICENSE b/LICENSE index a0f2feea..1b26ac3f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2010, 2011, 2012, Jim Bosch +Copyright (c) 2010-2018, Jim Bosch All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/README.md b/README.md index 2be217b7..4f6bbd5a 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,11 @@ -ndarray: NumPy-friendly multidimensional arrays in C++ -====================================================== -![Build Status](https://github.com/ndarray/ndarray/workflows/build_and_test/badge.svg) +This branch is a from-the-ground rewrite of the ndarray library, with the following goals: -ndarray is a template library that provides multidimensional array -objects in C++, with an interface and features designed to mimic the -Python 'numpy' package as much as possible. + - Simplify the codebase and its dependencies by leveraging C++11/14 features. Ideally this will include removing Boost as a dependency. -More information can be found in the [documentation at -ndarray.github.io/ndarray](https://ndarray.github.io/ndarray/). + - Support only Python 3 via pybind11. + - Add support for non-POD arrays. -Installation ------------- + - Possibly remove internal expression template implementations in favor of delegating more work to Eigen (which may become a required dependency). -ndarray can be built and tested with CMake: - - mkdir build - cd build - cmake .. - make - make test - -Inclusion and testing of optional dependencies is controlled by NDARRAY_* cmake -options. Dependency resolution can be controlled by the PYBIND11_DIR, -EIGEN_DIR, and FFTW_DIR environment variables. For example, to build with an -alternate Eigen3 install location and disable FFTW testing replace `cmake ..` -with `EIGEN_DIR=/opt/local cmake -DNDARRY_FFTW=OFF ..`. - -ndarray's build system does not produce the correct suffixes for pybind11 -outputs under pybind11 2.1.x (due to a bug in pybind11 itself). To avoid this -problem, please upgrade to pybind11 2.2.x, or try the (now reverted) patch from -ndarray commit f46c0f0ff876ceab5aaa3286e5f6e86902e72feb. - -Version 1.4.2 of ndarray is the last version to support SWIG. - -Version 1.5.3 of ndarray is the last verison to support Boost.Python. +When the rewrite reaches a usable state, the current master version will be archived to a stable branch and the devel branch will become the new master. diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt deleted file mode 100644 index 5f154054..00000000 --- a/doc/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/doxygen.conf.in - ${CMAKE_CURRENT_BINARY_DIR}/doxygen.conf @ONLY) -add_custom_target(doc ${DOXYGEN_EXECUTABLE} - ${CMAKE_CURRENT_BINARY_DIR}/doxygen.conf - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - COMMENT "Running Doxygen" VERBATIM) - diff --git a/doc/doxygen.conf.in b/doc/doxygen.conf.in index d33176d1..4ff37fa9 100644 --- a/doc/doxygen.conf.in +++ b/doc/doxygen.conf.in @@ -1,4 +1,4 @@ -# Doxyfile 1.8.5 +# Doxyfile 1.8.14 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. @@ -20,8 +20,8 @@ # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all text # before the first occurrence of this tag. Doxygen uses libiconv (or the iconv -# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv -# for the list of possible encodings. +# built into libc) for the transcoding. See +# https://www.gnu.org/software/libiconv/ for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 @@ -38,18 +38,18 @@ PROJECT_NAME = "ndarray" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = +PROJECT_NUMBER = 2.x # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. -PROJECT_BRIEF = "NumPy-friendly multidimensional arrays in C++" +PROJECT_BRIEF = "NumPy-like strided arrays in C++" -# With the PROJECT_LOGO tag one can specify an logo or icon that is included in -# the documentation. The maximum height of the logo should not exceed 55 pixels -# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo -# to the output directory. +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. PROJECT_LOGO = @@ -58,9 +58,9 @@ PROJECT_LOGO = # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. -OUTPUT_DIRECTORY = +OUTPUT_DIRECTORY = @CMAKE_CURRENT_BINARY_DIR@/doc/ -# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub- +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and # will distribute the generated files over these directories. Enabling this # option can be useful when feeding doxygen a huge amount of source files, where @@ -70,27 +70,37 @@ OUTPUT_DIRECTORY = CREATE_SUBDIRS = NO +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. -# Possible values are: Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese- -# Traditional, Croatian, Czech, Danish, Dutch, English, Esperanto, Farsi, -# Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en, -# Korean, Korean-en, Latvian, Norwegian, Macedonian, Persian, Polish, -# Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish, -# Turkish, Ukrainian and Vietnamese. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English -# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. BRIEF_MEMBER_DESC = YES -# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief # description of a member or function before the detailed description # # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the @@ -108,7 +118,17 @@ REPEAT_BRIEF = YES # the entity):The $name class, The $name widget, The $name file, is, provides, # specifies, contains, represents, a, an and the. -ABBREVIATE_BRIEF = +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # doxygen will generate a detailed section even if there is only a brief @@ -125,7 +145,7 @@ ALWAYS_DETAILED_SEC = NO INLINE_INHERITED_MEMB = NO -# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the # shortest path that makes the file name unique will be used # The default value is: YES. @@ -195,9 +215,9 @@ MULTILINE_CPP_IS_BRIEF = NO INHERIT_DOCS = YES -# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a -# new page for each member. If set to NO, the documentation of a member will be -# part of the file/class/namespace that contains it. +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. # The default value is: NO. SEPARATE_MEMBER_PAGES = NO @@ -216,7 +236,8 @@ TAB_SIZE = 4 # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading # "Side Effects:". You can put \n's in the value part of an alias to insert -# newlines. +# newlines (in the resulting output). You can put ^^ in the value part of an +# alias to insert a newline as if a physical newline was in the original file. ALIASES = @@ -259,11 +280,14 @@ OPTIMIZE_OUTPUT_VHDL = NO # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by doxygen: IDL, Java, Javascript, -# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make -# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C -# (default is Fortran), use: inc=Fortran f=C. +# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: +# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: +# Fortran. In the later case the parser tries to guess whether the code is fixed +# or free formatted code, this is the default for Fortran type files), VHDL. For +# instance to make doxygen treat .inc files as Fortran files (default is PHP), +# and .f files as C (default is Fortran), use: inc=Fortran f=C. # -# Note For files without extension you can use no_extension as a placeholder. +# Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise # the files are not read by doxygen. @@ -280,10 +304,19 @@ EXTENSION_MAPPING = MARKDOWN_SUPPORT = YES +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 0. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 0 + # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can -# be prevented in individual cases by by putting a % sign in front of the word -# or globally by setting AUTOLINK_SUPPORT to NO. +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. # The default value is: YES. AUTOLINK_SUPPORT = YES @@ -296,7 +329,7 @@ AUTOLINK_SUPPORT = YES # diagrams that involve STL classes more complete and accurate. # The default value is: NO. -BUILTIN_STL_SUPPORT = NO +BUILTIN_STL_SUPPORT = YES # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. @@ -305,7 +338,7 @@ BUILTIN_STL_SUPPORT = NO CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: -# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen # will parse them like normal C++ but will assume all classes use public instead # of private inheritance when no explicit protection keyword is present. # The default value is: NO. @@ -320,16 +353,23 @@ SIP_SUPPORT = NO # should set this option to NO. # The default value is: YES. -IDL_PROPERTY_SUPPORT = NO +IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES, then doxygen will reuse the documentation of the first +# tag is set to YES then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. # The default value is: NO. DISTRIBUTE_GROUP_DOC = NO +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + # Set the SUBGROUPING tag to YES to allow class member groups of the same type # (for instance a group of public functions) to be put as a subgroup of that # type (e.g. under the Public Functions section). Set it to NO to prevent @@ -388,7 +428,7 @@ LOOKUP_CACHE_SIZE = 0 # Build related configuration options #--------------------------------------------------------------------------- -# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in # documentation are documented, even if no documentation was available. Private # class members and static file members will be hidden unless the # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. @@ -398,35 +438,35 @@ LOOKUP_CACHE_SIZE = 0 EXTRACT_ALL = NO -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will # be included in the documentation. # The default value is: NO. EXTRACT_PRIVATE = NO -# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. # The default value is: NO. EXTRACT_PACKAGE = NO -# If the EXTRACT_STATIC tag is set to YES all static members of a file will be +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be # included in the documentation. # The default value is: NO. EXTRACT_STATIC = NO -# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined -# locally in source files will be included in the documentation. If set to NO +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, # only classes defined in header files are included. Does not have any effect # for Java sources. # The default value is: YES. EXTRACT_LOCAL_CLASSES = YES -# This flag is only useful for Objective-C code. When set to YES local methods, +# This flag is only useful for Objective-C code. If set to YES, local methods, # which are defined in the implementation section but not in the interface are -# included in the documentation. If set to NO only methods in the interface are +# included in the documentation. If set to NO, only methods in the interface are # included. # The default value is: NO. @@ -451,21 +491,21 @@ HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set -# to NO these classes will be included in the various overviews. This option has -# no effect if EXTRACT_ALL is enabled. +# to NO, these classes will be included in the various overviews. This option +# has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend -# (class|struct|union) declarations. If set to NO these declarations will be +# (class|struct|union) declarations. If set to NO, these declarations will be # included in the documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any -# documentation blocks found inside the body of a function. If set to NO these +# documentation blocks found inside the body of a function. If set to NO, these # blocks will be appended to the function's detailed documentation block. # The default value is: NO. @@ -479,7 +519,7 @@ HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file -# names in lower-case letters. If set to YES upper-case letters are also +# names in lower-case letters. If set to YES, upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. @@ -488,18 +528,32 @@ INTERNAL_DOCS = NO CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with -# their full class and namespace scopes in the documentation. If set to YES the +# their full class and namespace scopes in the documentation. If set to YES, the # scope will be hidden. # The default value is: NO. HIDE_SCOPE_NAMES = NO +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. SHOW_INCLUDE_FILES = YES +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + # If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include # files with double quotes in the documentation rather than with sharp brackets. # The default value is: NO. @@ -514,14 +568,15 @@ INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the # (detailed) documentation of file and class members alphabetically by member -# name. If set to NO the members will appear in declaration order. +# name. If set to NO, the members will appear in declaration order. # The default value is: YES. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member -# name. If set to NO the members will appear in declaration order. +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. # The default value is: NO. SORT_BRIEF_DOCS = NO @@ -565,27 +620,25 @@ SORT_BY_SCOPE_NAME = NO STRICT_PROTO_MATCHING = NO -# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the -# todo list. This list is created by putting \todo commands in the -# documentation. +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. # The default value is: YES. GENERATE_TODOLIST = YES -# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the -# test list. This list is created by putting \test commands in the -# documentation. +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. # The default value is: YES. GENERATE_TESTLIST = YES -# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug # list. This list is created by putting \bug commands in the documentation. # The default value is: YES. GENERATE_BUGLIST = YES -# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO) +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) # the deprecated list. This list is created by putting \deprecated commands in # the documentation. # The default value is: YES. @@ -610,8 +663,8 @@ ENABLED_SECTIONS = MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated at -# the bottom of the documentation of classes and structs. If set to YES the list -# will mention the files that were used to generate the documentation. +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. # The default value is: YES. SHOW_USED_FILES = YES @@ -656,11 +709,10 @@ LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool -# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the -# search path. Do not use file names with spaces, bibtex cannot handle them. See -# also \cite for info how to create references. +# search path. See also \cite for info how to create references. CITE_BIB_FILES = @@ -676,7 +728,7 @@ CITE_BIB_FILES = QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are -# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES # this implies that the warnings are on. # # Tip: Turn warnings on while writing the documentation. @@ -684,7 +736,7 @@ QUIET = NO WARNINGS = YES -# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: YES. @@ -701,12 +753,18 @@ WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return -# value. If set to NO doxygen will only warn about wrong or incomplete parameter -# documentation, but not about the absence of documentation. +# value. If set to NO, doxygen will only warn about wrong or incomplete +# parameter documentation, but not about the absence of documentation. # The default value is: NO. WARN_NO_PARAMDOC = NO +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. +# The default value is: NO. + +WARN_AS_ERROR = NO + # The WARN_FORMAT tag determines the format of the warning messages that doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated @@ -730,15 +788,15 @@ WARN_LOGFILE = # The INPUT tag is used to specify the files and/or directories that contain # documented source files. You may enter file names like myfile.cpp or # directories like /usr/src/myproject. Separate the files or directories with -# spaces. +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = @CMAKE_CURRENT_SOURCE_DIR@/../include/ +INPUT = @CMAKE_CURRENT_SOURCE_DIR@/include/ @CMAKE_CURRENT_SOURCE_DIR@/../doc # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv -# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# documentation (see: https://www.gnu.org/software/libiconv/) for the list of # possible encodings. # The default value is: UTF-8. @@ -746,14 +804,62 @@ INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and -# *.h) to filter out the source-files in the directories. If left blank the -# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, -# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, -# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, -# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, -# *.qsf, *.as and *.js. - -FILE_PATTERNS = *.h +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, +# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, +# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, +# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, +# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf. + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.idl \ + *.ddl \ + *.odl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.cs \ + *.d \ + *.php \ + *.php4 \ + *.php5 \ + *.phtml \ + *.inc \ + *.m \ + *.markdown \ + *.md \ + *.mm \ + *.dox \ + *.py \ + *.pyw \ + *.f90 \ + *.f95 \ + *.f03 \ + *.f08 \ + *.f \ + *.for \ + *.tcl \ + *.vhd \ + *.vhdl \ + *.ucf \ + *.qsf # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. @@ -795,7 +901,7 @@ EXCLUDE_PATTERNS = # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories use the pattern */test/* -EXCLUDE_SYMBOLS = +EXCLUDE_SYMBOLS = detail # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include @@ -808,7 +914,7 @@ EXAMPLE_PATH = # *.h) to filter out the source-files in the directories. If left blank all # files are included. -EXAMPLE_PATTERNS = +EXAMPLE_PATTERNS = * # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude commands @@ -837,6 +943,10 @@ IMAGE_PATH = # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. INPUT_FILTER = @@ -846,11 +956,15 @@ INPUT_FILTER = # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER ) will also be used to filter the input files that are used for +# INPUT_FILTER) will also be used to filter the input files that are used for # producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). # The default value is: NO. @@ -910,7 +1024,7 @@ REFERENCED_BY_RELATION = NO REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set -# to YES, then the hyperlinks from functions in REFERENCES_RELATION and +# to YES then the hyperlinks from functions in REFERENCES_RELATION and # REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will # link to the documentation. # The default value is: YES. @@ -930,7 +1044,7 @@ SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will # point to the HTML generated by the htags(1) tool instead of doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system -# (see http://www.gnu.org/software/global/global.html). You will need version +# (see https://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: @@ -987,7 +1101,7 @@ IGNORE_PREFIX = # Configuration options related to the HTML output #--------------------------------------------------------------------------- -# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output # The default value is: YES. GENERATE_HTML = YES @@ -1049,13 +1163,15 @@ HTML_FOOTER = HTML_STYLESHEET = -# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user- -# defined cascading style sheet that is included after the standard style sheets +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets # created by doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the -# standard style sheet and is therefor more robust against future updates. -# Doxygen will copy the style sheet file to the output directory. For an example -# see the documentation. +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = @@ -1071,9 +1187,9 @@ HTML_EXTRA_STYLESHEET = HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen -# will adjust the colors in the stylesheet and background images according to +# will adjust the colors in the style sheet and background images according to # this color. Hue is specified as an angle on a colorwheel, see -# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# https://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. @@ -1102,11 +1218,23 @@ HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting this -# to NO can help when comparing the output of multiple runs. +# to YES can help to show when doxygen was last run and thus if the +# documentation is up to date. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = NO + +# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML +# documentation will contain a main index with vertical navigation menus that +# are dynamically created via Javascript. If disabled, the navigation index will +# consists of multiple levels of tabs that are statically embedded in every HTML +# page. Disable this option to support browsers that do not have Javascript, +# like the Qt help browser. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_TIMESTAMP = YES +HTML_DYNAMIC_MENUS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the @@ -1131,12 +1259,12 @@ HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development -# environment (see: http://developer.apple.com/tools/xcode/), introduced with +# environment (see: https://developer.apple.com/tools/xcode/), introduced with # OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a # Makefile in the HTML output directory. Running make will produce the docset in # that directory and running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at -# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# startup. See https://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1199,28 +1327,29 @@ GENERATE_HTMLHELP = NO CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path -# including file name) of the HTML help compiler ( hhc.exe). If non-empty +# including file name) of the HTML help compiler (hhc.exe). If non-empty, # doxygen will try to run the HTML help compiler on the generated index.hhp. # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. HHC_LOCATION = -# The GENERATE_CHI flag controls if a separate .chi index file is generated ( -# YES) or that it should be included in the master .chm file ( NO). +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the master .chm file (NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO -# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc) +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) # and project file content. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_INDEX_ENCODING = -# The BINARY_TOC flag controls whether a binary table of contents is generated ( -# YES) or a normal table of contents ( NO) in the .chm file. +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. @@ -1251,7 +1380,7 @@ QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace -# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# (see: http://doc.qt.io/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1259,8 +1388,7 @@ QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual -# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- -# folders). +# Folders (see: http://doc.qt.io/qt-4.8/qthelpproject.html#virtual-folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1268,23 +1396,21 @@ QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- -# filters). +# Filters (see: http://doc.qt.io/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- -# filters). +# Filters (see: http://doc.qt.io/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: -# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# http://doc.qt.io/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = @@ -1333,7 +1459,7 @@ DISABLE_INDEX = NO # index structure (just like the one that is generated for HTML Help). For this # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the -# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can # further fine-tune the look of the index. As an example, the default style # sheet generated by doxygen has an example that shows how to put an image at # the root of the tree instead of the PROJECT_NAME. Since the tree basically has @@ -1361,7 +1487,7 @@ ENUM_VALUES_PER_LINE = 4 TREEVIEW_WIDTH = 250 -# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to # external symbols imported via tag files in a separate window. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1377,7 +1503,7 @@ EXT_LINKS_IN_WINDOW = NO FORMULA_FONTSIZE = 10 -# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# Use the FORMULA_TRANSPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are not # supported properly for IE 6.0, but are supported on all modern browsers. # @@ -1389,8 +1515,8 @@ FORMULA_FONTSIZE = 10 FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see -# http://www.mathjax.org) which uses client side Javascript for the rendering -# instead of using prerendered bitmaps. Use this if you do not have LaTeX +# https://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path # to it using the MATHJAX_RELPATH option. @@ -1416,11 +1542,11 @@ MATHJAX_FORMAT = HTML-CSS # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of -# MathJax from http://www.mathjax.org before deployment. -# The default value is: http://cdn.mathjax.org/mathjax/latest. +# MathJax from https://www.mathjax.org before deployment. +# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/. # This tag requires that the tag USE_MATHJAX is set to YES. -MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest +MATHJAX_RELPATH = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/ # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example @@ -1460,11 +1586,11 @@ SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a web server instead of a web client using Javascript. There -# are two flavours of web server based searching depending on the -# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for -# searching and an index file used by the script. When EXTERNAL_SEARCH is -# enabled the indexing and searching needs to be provided by external tools. See -# the section "External Indexing and Searching" for details. +# are two flavors of web server based searching depending on the EXTERNAL_SEARCH +# setting. When disabled, doxygen will generate a PHP script for searching and +# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing +# and searching needs to be provided by external tools. See the section +# "External Indexing and Searching" for details. # The default value is: NO. # This tag requires that the tag SEARCHENGINE is set to YES. @@ -1476,9 +1602,9 @@ SERVER_BASED_SEARCH = NO # external search engine pointed to by the SEARCHENGINE_URL option to obtain the # search results. # -# Doxygen ships with an example indexer ( doxyindexer) and search engine +# Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: http://xapian.org/). +# Xapian (see: https://xapian.org/). # # See the section "External Indexing and Searching" for details. # The default value is: NO. @@ -1489,9 +1615,9 @@ EXTERNAL_SEARCH = NO # The SEARCHENGINE_URL should point to a search engine hosted by a web server # which will return the search results when EXTERNAL_SEARCH is enabled. # -# Doxygen ships with an example indexer ( doxyindexer) and search engine +# Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: http://xapian.org/). See the section "External Indexing and +# Xapian (see: https://xapian.org/). See the section "External Indexing and # Searching" for details. # This tag requires that the tag SEARCHENGINE is set to YES. @@ -1527,10 +1653,10 @@ EXTRA_SEARCH_MAPPINGS = # Configuration options related to the LaTeX output #--------------------------------------------------------------------------- -# If the GENERATE_LATEX tag is set to YES doxygen will generate LaTeX output. +# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output. # The default value is: YES. -GENERATE_LATEX = NO +GENERATE_LATEX = YES # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of @@ -1558,7 +1684,7 @@ LATEX_CMD_NAME = latex MAKEINDEX_CMD_NAME = makeindex -# If the COMPACT_LATEX tag is set to YES doxygen generates more compact LaTeX +# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX # documents. This may be useful for small projects and may help to save some # trees in general. # The default value is: NO. @@ -1576,9 +1702,12 @@ COMPACT_LATEX = NO PAPER_TYPE = a4 # The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names -# that should be included in the LaTeX output. To get the times font for -# instance you can specify -# EXTRA_PACKAGES=times +# that should be included in the LaTeX output. The package can be specified just +# by its name or with the correct syntax as to be used with the LaTeX +# \usepackage command. To get the times font for instance you can specify : +# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times} +# To use the option intlimits with the amsmath package you can specify: +# EXTRA_PACKAGES=[intlimits]{amsmath} # If left blank no extra packages will be included. # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -1592,23 +1721,36 @@ EXTRA_PACKAGES = # # Note: Only use a user-defined header if you know what you are doing! The # following commands have a special meaning inside the header: $title, -# $datetime, $date, $doxygenversion, $projectname, $projectnumber. Doxygen will -# replace them by respectively the title of the page, the current date and time, -# only the current date, the version number of doxygen, the project name (see -# PROJECT_NAME), or the project number (see PROJECT_NUMBER). +# $datetime, $date, $doxygenversion, $projectname, $projectnumber, +# $projectbrief, $projectlogo. Doxygen will replace $title with the empty +# string, for the replacement values of the other commands the user is referred +# to HTML_HEADER. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_HEADER = # The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the # generated LaTeX document. The footer should contain everything after the last -# chapter. If it is left blank doxygen will generate a standard footer. +# chapter. If it is left blank doxygen will generate a standard footer. See +# LATEX_HEADER for more information on how to generate a default footer and what +# special commands can be used inside the footer. # # Note: Only use a user-defined footer if you know what you are doing! # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_FOOTER = +# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# LaTeX style sheets that are included after the standard style sheets created +# by doxygen. Using this option one can overrule certain style aspects. Doxygen +# will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EXTRA_STYLESHEET = + # The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the LATEX_OUTPUT output # directory. Note that the files will be copied as-is; there are no commands or @@ -1626,8 +1768,8 @@ LATEX_EXTRA_FILES = PDF_HYPERLINKS = YES -# If the LATEX_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate -# the PDF file directly from the LaTeX files. Set this option to YES to get a +# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate +# the PDF file directly from the LaTeX files. Set this option to YES, to get a # higher quality PDF documentation. # The default value is: YES. # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -1662,17 +1804,25 @@ LATEX_SOURCE_CODE = NO # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. See -# http://en.wikipedia.org/wiki/BibTeX and \cite for more info. +# https://en.wikipedia.org/wiki/BibTeX and \cite for more info. # The default value is: plain. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_BIB_STYLE = plain +# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated +# page will contain the date and time when the page was generated. Setting this +# to NO can help when comparing the output of multiple runs. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_TIMESTAMP = NO + #--------------------------------------------------------------------------- # Configuration options related to the RTF output #--------------------------------------------------------------------------- -# If the GENERATE_RTF tag is set to YES doxygen will generate RTF output. The +# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The # RTF output is optimized for Word 97 and may not look too pretty with other RTF # readers/editors. # The default value is: NO. @@ -1687,7 +1837,7 @@ GENERATE_RTF = NO RTF_OUTPUT = rtf -# If the COMPACT_RTF tag is set to YES doxygen generates more compact RTF +# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF # documents. This may be useful for small projects and may help to save some # trees in general. # The default value is: NO. @@ -1724,11 +1874,21 @@ RTF_STYLESHEET_FILE = RTF_EXTENSIONS_FILE = +# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code +# with syntax highlighting in the RTF output. +# +# Note that which sources are shown also depends on other settings such as +# SOURCE_BROWSER. +# The default value is: NO. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_SOURCE_CODE = NO + #--------------------------------------------------------------------------- # Configuration options related to the man page output #--------------------------------------------------------------------------- -# If the GENERATE_MAN tag is set to YES doxygen will generate man pages for +# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for # classes and files. # The default value is: NO. @@ -1752,6 +1912,13 @@ MAN_OUTPUT = man MAN_EXTENSION = .3 +# The MAN_SUBDIR tag determines the name of the directory created within +# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by +# MAN_EXTENSION with the initial . removed. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_SUBDIR = + # If the MAN_LINKS tag is set to YES and doxygen generates man output, then it # will generate one additional man file for each entity documented in the real # man page(s). These additional files only source the real man page, but without @@ -1765,7 +1932,7 @@ MAN_LINKS = NO # Configuration options related to the XML output #--------------------------------------------------------------------------- -# If the GENERATE_XML tag is set to YES doxygen will generate an XML file that +# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that # captures the structure of the code including all documentation. # The default value is: NO. @@ -1779,19 +1946,7 @@ GENERATE_XML = NO XML_OUTPUT = xml -# The XML_SCHEMA tag can be used to specify a XML schema, which can be used by a -# validating XML parser to check the syntax of the XML files. -# This tag requires that the tag GENERATE_XML is set to YES. - -XML_SCHEMA = - -# The XML_DTD tag can be used to specify a XML DTD, which can be used by a -# validating XML parser to check the syntax of the XML files. -# This tag requires that the tag GENERATE_XML is set to YES. - -XML_DTD = - -# If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program +# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program # listings (including syntax highlighting and cross-referencing information) to # the XML output. Note that enabling this will significantly increase the size # of the XML output. @@ -1804,7 +1959,7 @@ XML_PROGRAMLISTING = YES # Configuration options related to the DOCBOOK output #--------------------------------------------------------------------------- -# If the GENERATE_DOCBOOK tag is set to YES doxygen will generate Docbook files +# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files # that can be used to generate PDF. # The default value is: NO. @@ -1818,14 +1973,23 @@ GENERATE_DOCBOOK = NO DOCBOOK_OUTPUT = docbook +# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the +# program listings (including syntax highlighting and cross-referencing +# information) to the DOCBOOK output. Note that enabling this will significantly +# increase the size of the DOCBOOK output. +# The default value is: NO. +# This tag requires that the tag GENERATE_DOCBOOK is set to YES. + +DOCBOOK_PROGRAMLISTING = NO + #--------------------------------------------------------------------------- # Configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- -# If the GENERATE_AUTOGEN_DEF tag is set to YES doxygen will generate an AutoGen -# Definitions (see http://autogen.sf.net) file that captures the structure of -# the code including all documentation. Note that this feature is still -# experimental and incomplete at the moment. +# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an +# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures +# the structure of the code including all documentation. Note that this feature +# is still experimental and incomplete at the moment. # The default value is: NO. GENERATE_AUTOGEN_DEF = NO @@ -1834,7 +1998,7 @@ GENERATE_AUTOGEN_DEF = NO # Configuration options related to the Perl module output #--------------------------------------------------------------------------- -# If the GENERATE_PERLMOD tag is set to YES doxygen will generate a Perl module +# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module # file that captures the structure of the code including all documentation. # # Note that this feature is still experimental and incomplete at the moment. @@ -1842,7 +2006,7 @@ GENERATE_AUTOGEN_DEF = NO GENERATE_PERLMOD = NO -# If the PERLMOD_LATEX tag is set to YES doxygen will generate the necessary +# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary # Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI # output from the Perl module output. # The default value is: NO. @@ -1850,9 +2014,9 @@ GENERATE_PERLMOD = NO PERLMOD_LATEX = NO -# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be nicely +# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely # formatted so it can be parsed by a human reader. This is useful if you want to -# understand what is going on. On the other hand, if this tag is set to NO the +# understand what is going on. On the other hand, if this tag is set to NO, the # size of the Perl module output will be much smaller and Perl will parse it # just the same. # The default value is: YES. @@ -1872,14 +2036,14 @@ PERLMOD_MAKEVAR_PREFIX = # Configuration options related to the preprocessor #--------------------------------------------------------------------------- -# If the ENABLE_PREPROCESSING tag is set to YES doxygen will evaluate all +# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all # C-preprocessor directives found in the sources and include files. # The default value is: YES. ENABLE_PREPROCESSING = YES -# If the MACRO_EXPANSION tag is set to YES doxygen will expand all macro names -# in the source code. If set to NO only conditional compilation will be +# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names +# in the source code. If set to NO, only conditional compilation will be # performed. Macro expansion can be done in a controlled way by setting # EXPAND_ONLY_PREDEF to YES. # The default value is: NO. @@ -1895,7 +2059,7 @@ MACRO_EXPANSION = NO EXPAND_ONLY_PREDEF = NO -# If the SEARCH_INCLUDES tag is set to YES the includes files in the +# If the SEARCH_INCLUDES tag is set to YES, the include files in the # INCLUDE_PATH will be searched if a #include is found. # The default value is: YES. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. @@ -1937,9 +2101,9 @@ PREDEFINED = EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will -# remove all refrences to function-like macros that are alone on a line, have an -# all uppercase name, and do not end with a semicolon. Such function macros are -# typically used for boiler-plate code, and will confuse the parser if not +# remove all references to function-like macros that are alone on a line, have +# an all uppercase name, and do not end with a semicolon. Such function macros +# are typically used for boiler-plate code, and will confuse the parser if not # removed. # The default value is: YES. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. @@ -1959,7 +2123,7 @@ SKIP_FUNCTION_MACROS = YES # where loc1 and loc2 can be relative or absolute paths or URLs. See the # section "Linking to external documentation" for more information about the use # of tag files. -# Note: Each tag file must have an unique name (where the name does NOT include +# Note: Each tag file must have a unique name (where the name does NOT include # the path). If a tag file is not located in the directory in which doxygen is # run, you must also specify the path to the tagfile here. @@ -1971,20 +2135,21 @@ TAGFILES = GENERATE_TAGFILE = -# If the ALLEXTERNALS tag is set to YES all external class will be listed in the -# class index. If set to NO only the inherited external classes will be listed. +# If the ALLEXTERNALS tag is set to YES, all external class will be listed in +# the class index. If set to NO, only the inherited external classes will be +# listed. # The default value is: NO. ALLEXTERNALS = NO -# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed in -# the modules index. If set to NO, only the current project's groups will be +# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will be # listed. # The default value is: YES. EXTERNAL_GROUPS = YES -# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed in +# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in # the related pages index. If set to NO, only the current project's pages will # be listed. # The default value is: YES. @@ -2001,7 +2166,7 @@ PERL_PATH = /usr/bin/perl # Configuration options related to the dot tool #--------------------------------------------------------------------------- -# If the CLASS_DIAGRAMS tag is set to YES doxygen will generate a class diagram +# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram # (in HTML and LaTeX) for classes with base or super classes. Setting the tag to # NO turns the diagrams off. Note that this option also works with HAVE_DOT # disabled, but it is recommended to install and use dot, since it yields more @@ -2019,7 +2184,14 @@ CLASS_DIAGRAMS = YES MSCGEN_PATH = -# If set to YES, the inheritance and collaboration graphs will hide inheritance +# You can include diagrams made with dia in doxygen documentation. Doxygen will +# then run dia to produce the diagram and insert it in the documentation. The +# DIA_PATH tag allows you to specify the directory where the dia binary resides. +# If left empty dia is assumed to be found in the default search path. + +DIA_PATH = + +# If set to YES the inheritance and collaboration graphs will hide inheritance # and usage relations if the target is undocumented or is not a class. # The default value is: YES. @@ -2044,7 +2216,7 @@ HAVE_DOT = NO DOT_NUM_THREADS = 0 -# When you want a differently looking font n the dot files that doxygen +# When you want a differently looking font in the dot files that doxygen # generates you can specify the font name using DOT_FONTNAME. You need to make # sure dot is able to find the font, which can be done by putting it in a # standard location or by setting the DOTFONTPATH environment variable or by @@ -2092,7 +2264,7 @@ COLLABORATION_GRAPH = YES GROUP_GRAPHS = YES -# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. # The default value is: NO. @@ -2144,7 +2316,8 @@ INCLUDED_BY_GRAPH = YES # # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable call graphs for selected -# functions only using the \callgraph command. +# functions only using the \callgraph command. Disabling a call graph can be +# accomplished by means of the command \hidecallgraph. # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2155,7 +2328,8 @@ CALL_GRAPH = NO # # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable caller graphs for selected -# functions only using the \callergraph command. +# functions only using the \callergraph command. Disabling a caller graph can be +# accomplished by means of the command \hidecallergraph. # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2178,11 +2352,15 @@ GRAPHICAL_HIERARCHY = YES DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images -# generated by dot. +# generated by dot. For an explanation of the image formats see the section +# output formats in the documentation of the dot tool (Graphviz (see: +# http://www.graphviz.org/)). # Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order # to make the SVG files visible in IE 9+ (other browsers do not have this # requirement). -# Possible values are: png, jpg, gif and svg. +# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo, +# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and +# png:gdiplus:gdiplus. # The default value is: png. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2219,6 +2397,30 @@ DOTFILE_DIRS = MSCFILE_DIRS = +# The DIAFILE_DIRS tag can be used to specify one or more directories that +# contain dia files that are included in the documentation (see the \diafile +# command). + +DIAFILE_DIRS = + +# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the +# path where java can find the plantuml.jar file. If left blank, it is assumed +# PlantUML is not used or called during a preprocessing step. Doxygen will +# generate a warning when it encounters a \startuml command in this case and +# will not generate output for the diagram. + +PLANTUML_JAR_PATH = + +# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a +# configuration file for plantuml. + +PLANTUML_CFG_FILE = + +# When using plantuml, the specified paths are searched for files specified by +# the !include statement in a plantuml block. + +PLANTUML_INCLUDE_PATH = + # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes # that will be shown in the graph. If the number of nodes in a graph becomes # larger than this value, doxygen will truncate the graph, which is visualized @@ -2255,7 +2457,7 @@ MAX_DOT_GRAPH_DEPTH = 0 DOT_TRANSPARENT = NO -# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) support # this, this feature is disabled by default. @@ -2272,7 +2474,7 @@ DOT_MULTI_TARGETS = NO GENERATE_LEGEND = YES -# If the DOT_CLEANUP tag is set to YES doxygen will remove the intermediate dot +# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot # files that are used to generate the various graphs. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. diff --git a/etc/conda-forge-testing.yaml b/etc/conda-forge-testing.yaml deleted file mode 100644 index 2a4f1a3d..00000000 --- a/etc/conda-forge-testing.yaml +++ /dev/null @@ -1,13 +0,0 @@ -name: ndarray -channels: - - conda-forge -dependencies: - - doxygen - - boost - - fftw - - numpy - - pybind11 - - c-compiler - - eigen - - cmake - - pkg-config diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt deleted file mode 100644 index bf93579c..00000000 --- a/include/CMakeLists.txt +++ /dev/null @@ -1,19 +0,0 @@ -add_library(${PROJECT_NAME} INTERFACE) - -target_include_directories( - ${PROJECT_NAME} INTERFACE $) -# add a target to generate API documentation with Doxygen - -# installation -include(GNUInstallDirs) - -install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME}-config) - -install( - EXPORT ${PROJECT_NAME}-config - NAMESPACE ${PROJECT_NAME}:: - DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/cmake/${PROJECT_NAME}) - -install(DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/${PROJECT_NAME} - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ) - diff --git a/include/ndarray.h b/include/ndarray.h deleted file mode 100644 index e76d189f..00000000 --- a/include/ndarray.h +++ /dev/null @@ -1,392 +0,0 @@ -// -*- c++ -*- -/* - * Copyright (c) 2010-2012, Jim Bosch - * All rights reserved. - * - * ndarray is distributed under a simple BSD-like license; - * see the LICENSE file that should be present in the root - * of the source distribution, or alternately available at: - * https://github.com/ndarray/ndarray - */ -#ifndef NDARRAY_ndarray_h_INCLUDED -#define NDARRAY_ndarray_h_INCLUDED - -/** - * @file ndarray.h - * @brief Main public header file for ndarray. - */ - -#include "ndarray/Array.h" -#include "ndarray/ArrayRef.h" -#include "ndarray/initialization.h" -#ifndef GCC_45 -#include "ndarray/operators.h" -#include "ndarray/arange.h" -#endif -#include "ndarray/casts.h" -#include "ndarray/formatting.h" - -namespace ndarray { - -/** - * @mainpage ndarray; Multidimensional Arrays in C++ - * - * %ndarray is a template library that provides multidimensional array objects in C++, with - * an interface and features designed to mimic the Python 'numpy' package as much as possible. - * - * A tutorial can be found \ref ndarrayTutorial "here". - * - * @section comparisons Other Multidimensional Array Libraries - * - * A number of other public C++ multidimensional array libraries already exist, such as - * boost.MultiArray, Blitz++, and xtensor. Much of the architecture and some of the interface of the - * %ndarray templates is built on ideas from both of these, particularly boost.MultiArray. - * %ndarray supports shared ownership of data, lazy evaluation of mathematical - * operations using expression templates, and the ability to use externally - * allocated memory. It also supports optimized, natural, nested iteration and - * preserve const-correctness (albeit with different semantics). - * - * While %ndarray can perform numerical operations with broadcasting, it makes - * no attempt to perform explicit vectorization, and libraries that focus more - * on computational performance will be better at it in almost every context. - * %ndarray's goal is to provide lightweight, flexible data structures with a - * more Python-friendly ownership model, while maintaining enough static typing - * to allow optimized libraries (such as Eigen) to work effectively on - * ndarray-managed arrays. - * - * @section semantics Copy and Constness Semantics - * - * The memory used by %ndarray objects is reference counted, and can be allocated - * using any STL-compatible allocator. Arrays can also be constructed from external - * memory buffers, with full reference counting for any external memory owned by - * an object that participates in any reference-counting scheme (most notably - * memory belonging to Python Numpy arrays in C++ Python extensions). Reference counting - * can also be disabled for individual arrays that reference external memory that - * is not reference-counted. - * - * Array objects in %ndarray preserve constness in the same way as most C++ - * smart pointers: there is a distinction between an array with const elements - * (Array) and a const array value or reference (Array const). - * An array with const elements does not support operations that change element - * values ("deep" operations), while a const array object or reference does not - * allow the array's data pointer, shape, or strides to be changed ("shallow" - * operations). As a result, functions should generally use const references - * for both input and output array arguments (with const and non-const - * elements, respectively). Arrays with non-const elements are implicitly (and - * efficiently) convertible to arrays with const elements, while an array with - * const elements can be cast to one with non-const elements using the - * const_array_cast function. - * - * @section contiguousness The Row-Major Contiguous Template Parameter - * - * In addition to being templated on its element type and total number of - * dimensions, Array is parameterized by the number of guaranteed row-major - * contiguous (RMC) dimensions, starting from the end. This parameter defaults - * to zero. For example: - * - * - Array is a fully row-major contiguous matrix. Each row of the - * matrix is a vector with contiguous elements, and there is no space between - * rows. - * - * - Array is a row-major matrix in which each row is a vector with - * contiguous elements, but there may be space between the rows (or there may - * not be; the template parameter indicates only the guaranteed number of RMC - * dimensions). - * - * - Array is a matrix that can have any strides, including - * column-major. - * - * An Array with M RMC dimensions can be implicitly converted to an Array with - * N<=M RMC dimensions. The `static_dimension_cast` and - * `dynamic_dimension_cast` functions can be used to create arrays with M1 behaves like largely like an STL container of - * Array of dimension N-1. An Array with dimension 1 behaves like a simple - * container of elements. Iterators over an Array with N>1 thus dereference to - * Arrays with dimension N-1, and standard [] indexing also yields the expected - * lower-dimensional array. - * - * Arbitrary views can be retrieved by passing a view definition to an Array's - * bracket indexing operators. These view definitions are temporaries created - * by the view() function: - * @code - * // let 'a' be a 3 dimensional array with dimensions (3,5,4) - * - * // equivalent to a[1:3,:,2] in numpy: - * Array subset1 = a[view(1,3)()(2)]; - * - * // equivalent to a[:,:,0:4:2] in numpy in numpy: - * Array subset2 = a[view()()(0,4,2)]; - * @endcode - * Supplying a single integer indexes a dimension by a scalar (which reduces - * the dimension of output), supplying a pair of integers indicates a - * contiguous range, and supplying three integers indicates a slice. - * Specifying no arguments for a dimension includes the entire dimension. Not - * indexing all dimensions is equivalent to including empty parentheses for the - * remaining dimensions. - * - * @section downloads Downloads - * https://github.com/ndarray/ndarray/releases - * - * @section License - * %ndarray is distributed under a simple BSD-like license: - * https://github.com/ndarray/ndarray/blob/master/LICENSE - */ - -/** - * @page ndarrayTutorial Tutorial - * - * @section installation Installation - * - * %ndarray is a header-only library; after downloading and unpacking the - * source, you can start using it immediately simply by adding it to your - * compiler's include path. - * - * For tests, we use the Cmake build system, but CMake is not necessary to make - * use of the library. - * - * @section construction Creating New Arrays - * - * Array objects have two normal constructors intended for public use, the - * default constructor and a converting copy constructor. The default - * constructor creates an "empty" array, with a null data pointer and zero - * shape. The copy constructor creates a shared view into an existing array. - * - * @subsection new_arrays New Memory - * - * To create a new non-trivial array, one can use the allocate function, which - * returns a temporary object implicitly convertible to Array: - * @code - * Array a = allocate(makeVector(3,4,5)); - * // equivalent to - * // >>> a = numpy.empty((3,4,5),dtype=float) - * // in Python - * @endcode - * The makeVector function here is a variable-argument-length constructor for - * the Vector object, a fixed-size array class whose int variant is used to - * specify shape and strides for Array. The appropriate Vector template for a - * particular Array template is available as the Index typedef within the Array - * class. - * - * The allocate function can also take an STL allocator as a template argument - * and/or regular argument: - * @code - * Array a = allocate< std::allocator >(makeVector(3,4,5)); - * @endcode - * @code - * Array a = allocate(makeVector(3,4,5), std::allocator()); - * @endcode - * Note that the type of the allocator does not have to match the type of the - * array; the allocator's "rebind" member will be used to create the correct - * allocator when the array is constructed. Furthermore, unlike standard - * library containers, Array is not templated on its allocator type; after - * construction, it is impossible to determine how an Array's memory was - * allocated. An Array constructed by allocate is not generally initialized to - * any value (do not assume it contains zeros). - * @subsection external_memory External Memory - * Arrays can also be constructed that point to external data: - * @code - * #include - * Array::Owner owner(std::malloc(sizeof(double)*5), std::free); - * Array::Index shape = makeVector(5); - * Array::Index strides = makeVector(1); - * Array a = external(owner.get(), shape, strides, owner); - * @endcode - * The 'strides' vector here specifies the space between elements in each - * dimension; the dot product of the strides vector with an index vector should - * give the offset of the element with that index from the first element of the - * array. - * The 'Owner' type here is a typedef to a boost::shared_ptr, which can take an - * arbitrary functor as a custom deleter (here, std::free). By defining an - * appropriate deleter, an array can manage virtually any kind of memory. - * However, it is also possible to create an array with no reference counting - * by passing an empty owner (or passing none at all): - * @code - * #include - * double data[] = { 5.3, 1.2, 6.3, 2.8, 7.0 }; - * Array::Index shape = makeVector(5); - * Array::Index strides = makeVector(1); - * Array a = external(data, shape, strides); - * @endcode - * In this case, the user is responsible for ensuring that the data pointer - * provided to the array remains valid during the array's lifetime, and is - * eventually deallocated later. - * - * @section assignment Assignment - * - * Direct Array assignment is shallow: - * @code - * Array a = allocate(makeVector(5)); - * Array b; - * b = a; // the two arrays now share data. - * @endcode - * To actually set the elements of an array, we can use Array::deep(): - * @code - * Array b = allocate(a.getShape()); - * b.deep() = a; // 'b' is now a deep copy of 'a'. - * @endcode - * Scalar assignment and augmented assignment operations are also supported: - * @code - * b.deep() = 5.2; - * b.deep() += a; - * @endcode - * The deep() method returns a proxy ArrayRef object, which behaves just like - * an Array aside from its assignment operators, and is implicitly convertible - * to Array. - * - * @section indexing_and_iteration Indexing and Iteration - * - * A multidimensional Array behaves like a container of Arrays with lower - * dimensions, while a one-dimensional Array behaves like a container of - * elements: - * @code - * int data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }; - * Array a = external(data, makeVector(4,3)); - * // make 'b' a view into the second row of 'a' (Reference is a typedef to Array): - * Array::Reference br = a[1]; // br is an ArrayRef. - * Array::Value b = a[1]; // b is an Array - * Array::Element b0 = b[0]; // b0 == 6; Element is a typedef to int. - * Array::Reference::Value b1 = b[1]; // b1 == 7; Reference::Value is also int. - * Array::Reference::Reference b2 = b[2]; // b2 == 8; Reference::Reference is int&. - * @endcode - * Indexing operations return ArrayRef objects, not Arrays. This allows them to be - * assigned to without manually calling deep(): - * @code - * a[1] -= 3; // subtract three from the entire second row. - * @endcode - * - * For one dimensional arrays, the "Reference" typedef is equivalent to - * "Element &", while the "Value" typedef is equivalent to "Element". For - * multidimensional arrays, "Reference" is a lower-dimensional ArrayRef, while - * "Value" is a lower-dimensional Array. - * - * Array is designed to have lightweight nested iterators, with types provided - * by the Array::Iterator typedef. For contiguous one-dimensional arrays - * (Array), this is a typedef to a simple pointer. The typical pattern - * to iterate over a 3-dimensional array looks like the following: - * @code - * Array a = allocate(makeVector(5,6,8)); - * for (Array::Iterator i = a.begin(); i != a.end(); ++i) { - * for (Array::Reference::Iterator j = i->begin(); j != i->end(); ++j) { - * for (Array::Reference::Reference::Iterator k = j->begin(); k != j->end(); ++k) { - * // *k == a[i - a.begin()][j - i->begin()][k - j->begin()]; - * } - * } - * } - * @endcode - * As expected, the iterators of multidimensional arrays dereference to - * lower-dimensional arrays, and the iterators of one-dimensional arrays - * dereference to elements. With some compilers, it may be advantageous to - * move the calls to end() outside their loops. - * - * Just like direct indexing, multidimensional array iterators dereference to - * ArrayRef, not Array. - * - * STL-compliant typedefs "iterator", "const_iterator", "reference", - * "const_reference", and "value" are also provided, though the const variants - * are not actually const (because Array provides smart-pointer - * const-correctness rather than container const-correctness). - * - * Single elements can be extracted from multidimensional arrays by indexing - * with ndarray::Vector: - * @code - * a[makeVector(3,2,1)] == a[3][2][1]; - * @endcode - * - * @section ndarrayTutorialViews Views - * - * General views into an Array are created by passing a ViewDef object to the - * [] operators of Array, returning a new Array that shares data and owns a - * reference to the original. - * - * ViewDef involves a lot of template metaprogramming, so the actual template - * class is an internal %detail, and ViewDefs are constructed by calls to the - * view() function function followed by chained calls to the function call - * operator, resulting in a syntax that looks like this: - * @code - * Array a = allocate(makeVector(3,5,2,6,4)); - * b = a[view(1)(0,3)()(4)]; - * @endcode - * which is equivalent to the Python code: - * @code - * a = numpy.empty((3,5,2,6,4),dtype=float) - * b = a[1,0:3,:,4] - * @endcode - * The value passed to each call specifies how to extract values from that - * dimension: - * - * - A single integer selects a single subarray from that dimension, reducing - * the overall dimensionality of the view relative to the parent array by - * one. - * - An empty call selects the entire dimension. - * - A pair of integers selects a contiguous subset of subarrays from that - * dimension. - * - A triplet of integers selects a noncontiguous subset of subarrays from - * that dimension. - * - * Any dimensions which are not specified because the length of the ViewDef expression is smaller - * than the dimensionality of the parent array will be considered full-dimension selections: - * @code - * a[view(3)] == a[view(3)()()()()]; - * @endcode - * - * @section operators Arithmetic Operators and Comparison - * - * Arrays provide element-wise arithmetic operations that make use of - * expression templates: - * @code - * Array a = allocate(makeVector(3,4)); - * // initialize the elements of 'a' - * Array b = allocate(a.getShape()); - * b = a * 3 + 2; // this expression is lazy, and never allocates a temporary array - * @endcode - * We can simplify the previous example by initializing 'b' with the copy() - * function, which is simply a shortcut for allocate and assign: - * @code - * Array a = allocate(makeVector(3,4)); - * // initialize the elements of 'a' - * Array b = copy(a * 3 + 2); - * @endcode - * As a rule, %ndarray never allocates memory for a new array unless you - * explicitly tell it to with the allocate() or copy() functions. - * - * Element-wise comparisons are also supported, but not via overloaded - * operators: - * @code - * Array a = allocate(makeVector(3,4)); - * // initialize the elements of 'a' - * Array b = allocate(makeVector(3,4)); - * // initialize the elements of 'b' - * Array c = copy(equal(a, b)); - * @endcode - * The element-wise comparison functions (equal, not_equal, less, less_equal, - * greater, greater_equal) and logical operators (logical_and, logical_or, - * logical_not) are also lazy, and are most useful when used in conjunction - * with the reduction functions all() and any(): - * @code - * Array a = allocate(makeVector(3,4)); - * // initialize the elements of 'a' - * Array b = allocate(makeVector(3,4)); - * // initialize the elements of 'b' - * bool v1 = all(logical_or(greater(a, b), greater(a, 3.0))); - * bool v2 = any(less(b, a)); - * @endcode - * - * Array does overload the equality and inequality operators, but these compare - * for "shallow" equality, not element-wise equality: - * @code - * Array a = allocate(makeVector(3,4)); - * Array b = copy(a); - * bool v1 = (a == b); // false, because 'a' and 'b' do not share data. - * Array c(a); - * bool v2 = (a == c); // true, because 'a' and 'c' have the same data, shape, and strides. - * @endcode - */ - -} // namespace ndarray - -#endif // !NDARRAY_ndarray_h_INCLUDED diff --git a/include/ndarray/Array.h b/include/ndarray/Array.h deleted file mode 100644 index 8c464a95..00000000 --- a/include/ndarray/Array.h +++ /dev/null @@ -1,193 +0,0 @@ -// -*- c++ -*- -/* - * Copyright (c) 2010-2012, Jim Bosch - * All rights reserved. - * - * ndarray is distributed under a simple BSD-like license; - * see the LICENSE file that should be present in the root - * of the source distribution, or alternately available at: - * https://github.com/ndarray/ndarray - */ -#ifndef NDARRAY_Array_h_INCLUDED -#define NDARRAY_Array_h_INCLUDED - -/** - * @file ndarray/Array.h - * - * @brief Definitions for Array. - */ - -#include "ndarray_fwd.h" -#include "ndarray/ArrayTraits.h" -#include "ndarray/ArrayBaseN.h" -#include "ndarray/Vector.h" -#include "ndarray/detail/Core.h" -#include "ndarray/views.h" - -namespace ndarray { - -/** - * @brief A multidimensional strided array. - * - * Array is the workhorse class of the ndarray library. - */ -template -class Array : public ArrayBaseN< Array > { - typedef ArrayBaseN Super; - typedef typename Super::Core Core; - typedef typename Super::CorePtr CorePtr; -public: - - /** - * @brief Default constructor. - * - * Creates an empty array with zero dimensions and null memory. - */ - Array() : Super(0, Core::create()) {} - - /** - * @brief Non-converting copy constructor. - */ - Array(Array const & other) : Super(other._data, other._core) {} - - /** - * @brief Converting copy constructor. - * - * Implicit conversion is allowed for non-const to const and for - * more guaranteed RMC to less guaranteed RMC (see \ref index). - */ - template - Array( - Array const & other -#ifndef DOXYGEN - , typename boost::enable_if,void*>::type=0 -#endif - ) : Super(other._data, other._core) {} - - /** - * @brief Converting copy constructor. - * - * Implicit conversion is allowed for non-const to const and for - * more guaranteed RMC to less guaranteed RMC (see \ref index). - */ - template - Array( - ArrayRef const & other -#ifndef DOXYGEN - , typename boost::enable_if,void*>::type=0 -#endif - ) : Super(other._data, other._core) {} - - /** - * @brief Construct an array with the given dimensions and allocated but uninitialized memory. - * - * Unspecified dimensions will have unit size, and if the number of argmuments is greater - * than the number of dimensions of the array, the extra arguments will be silently ignored. - * - * This is implemented in initialization.h. - */ - explicit Array(Size n1, Size n2=1, Size n3=1, Size n4=1, Size n5=1, Size n6=1, Size n7=1, Size n8=1); - - /** - * @brief Construct an array with the given dimensions and allocated but uninitialized memory. - * - * This is implemented in initialization.h. - */ - template - explicit Array(Vector const & shape); - - /** - * @brief Non-converting shallow assignment. - */ - Array & operator=(Array const & other) { - if (&other != this) { - this->_data = other._data; - this->_core = other._core; - } - return *this; - } - - /** - * @brief Converting shallow assignment. - * - * Implicit conversion is allowed for non-const -> const and for - * more guaranteed RMC -> less guaranteed RMC (see \ref index). - */ - template -#ifndef DOXYGEN - typename boost::enable_if, Array &>::type -#else - Array & -#endif - operator=(Array const & other) { - this->_data = other._data; - this->_core = other._core; - return *this; - } - - /** - * @brief Converting shallow assignment. - * - * Implicit conversion is allowed for non-const -> const and for - * more guaranteed RMC -> less guaranteed RMC (see \ref index). - */ - template -#ifndef DOXYGEN - typename boost::enable_if, Array &>::type -#else - Array & -#endif - operator=(ArrayRef const & other) { - this->_data = other._data; - this->_core = other._core; - return *this; - } - - /** - * @brief Shallow equality comparison: return true if the arrays share data and - * have the same shape and strides. - */ - template - bool operator==(Array const & other) const { - return this->getData() == other.getData() - && this->getShape() == other.getShape() - && this->getStrides() == other.getStrides(); - } - - /** - * @brief Shallow inequality comparison. - */ - template - bool operator!=(Array const & other) const { - return !this->operator==(other); - } - - /// @brief Lightweight shallow swap. - void swap(Array & other) { - std::swap(this->_data, other._data); - this->_core.swap(other._core); - } - - /** - * @brief Return true if the Array is definitely unique. - * - * This will only return true if the manager overrides Manager::isUnique(); - * this is true for the SimpleManager used by ndarray::allocate, but it is - * not true for ExternalManager. - */ - bool isUnique() const { return this->_core->isUnique(); } - -private: - template friend class Array; - template friend class ArrayRef; - template friend struct ArrayTraits; - template friend class ArrayBase; - template friend class detail::ArrayAccess; - - /// @internal @brief Construct an Array from a pointer and Core. - Array(T * data, CorePtr const & core) : Super(data, core) {} -}; - -} // namespace ndarray - -#endif // !NDARRAY_Array_h_INCLUDED diff --git a/include/ndarray/Array.hpp b/include/ndarray/Array.hpp new file mode 100644 index 00000000..2ce1301a --- /dev/null +++ b/include/ndarray/Array.hpp @@ -0,0 +1,219 @@ +// -*- c++ -*- +/* + * Copyright (c) 2010-2018, Jim Bosch + * All rights reserved. + * + * ndarray is distributed under a simple BSD-like license; + * see the LICENSE file that should be present in the root + * of the source distribution, or alternately available at: + * https://github.com/ndarray/ndarray + */ +#ifndef NDARRAY_Array_hpp_INCLUDED +#define NDARRAY_Array_hpp_INCLUDED + +#include "ndarray/common.hpp" +#include "ndarray/IndexVectorTraits.hpp" +#include "ndarray/ArrayInterfaceN.hpp" +#include "ndarray/detail/ArrayImpl.hpp" + +namespace ndarray { + +namespace detail { + +constexpr bool contiguousness_convertible(Size n, Offset in, Offset out) { + return (in >= 0 && out >= 0 && in >= out) || + (in <= 0 && out <= 0 && in <= out) || + (n == 1 && (in == 1 || in == -1) && (out == 1 || out == -1)); +} + +} // namespace detail + + +template +class Array : public ArrayInterfaceN, T const, N_, C_> { +public: + + using Element = T const; + static constexpr Size N = N_; + static constexpr Offset C = C_; + + Array() noexcept = default; + + Array(Array const &) noexcept = default; + + Array(Array &&) noexcept = default; + + template + Array(Array const & other) noexcept : _impl(other._impl) { + static_assert(detail::contiguousness_convertible(N, D, C), "invalid contiguousness conversion"); + } + + template + Array(Array && other) noexcept : _impl(other._impl) { + static_assert(detail::contiguousness_convertible(N, D, C), "invalid contiguousness conversion"); + } + + template + explicit Array( + ShapeVector const & shape, + EnableIfIndexVector order=MemoryOrder::ROW_MAJOR + ) : _impl(shape, order, detail::TypeTag()) {} + + explicit Array(std::initializer_list shape, MemoryOrder order=MemoryOrder::ROW_MAJOR) : + _impl(shape, order, detail::TypeTag()) {} + + template + Array( + std::shared_ptr data, + ShapeVector const & shape, + EnableIfIndexVector order=MemoryOrder::ROW_MAJOR + ) : _impl(std::const_pointer_cast(std::move(data)), shape, order) {} + + Array(std::shared_ptr data, std::initializer_list shape, + MemoryOrder order=MemoryOrder::ROW_MAJOR) : + _impl(std::const_pointer_cast(std::move(data)), shape, order) {} + + template + Array( + EnableIfIndexVector, ShapeVector, StridesVector> data, + ShapeVector const & shape, + StridesVector const & strides + ) : _impl(std::const_pointer_cast(std::move(data)), shape, strides) { + _impl.layout->template check_contiguousness(sizeof(T)); + } + + template + Array( + std::shared_ptr data, + std::initializer_list shape, + std::initializer_list strides + ) : _impl(std::const_pointer_cast(std::move(data)), shape, strides) { + _impl.layout->template check_contiguousness(sizeof(T)); + } + + Array(std::shared_ptr buffer, std::shared_ptr const> layout) : + _impl(std::const_pointer_cast(std::move(buffer)), std::move(layout)) + { + _impl.layout->template check_contiguousness(sizeof(T)); + } + + Array & operator=(Array const &) noexcept = default; + + Array & operator=(Array &&) noexcept = default; + + template + bool operator==(Array const & rhs) const { return _impl == rhs._impl; } + + template + bool operator!=(Array const & rhs) const { return !(*this == rhs); } + + Deref> operator*() const; + + bool empty() const noexcept { return (!_impl.layout) || _impl.layout->size() == 0u; } + + Size size() const noexcept { return _impl.layout ? _impl.layout->size() : 0u; } + + Offset stride() const { return _impl.layout->stride(); } + + std::array shape() const { return _impl.layout->shape(); } + + std::array strides() const { return _impl.layout->strides(); } + + Size full_size() const { return _impl.layout->full_size(); } + + T const * data() const noexcept { return reinterpret_cast(_impl.buffer.get()); } + +protected: + + template friend class Array; + template friend class ArrayInterfaceN; + template friend class NestedIterator; + + detail::ArrayImpl _impl; +}; + + +template +class Array : public ArrayInterfaceN, T, N_, C_>, public Array { + using Base = Array; + using Interface = ArrayInterfaceN, T, N_, C_>; +public: + + using Element = T; + static constexpr Size N = N_; + static constexpr Offset C = C_; + + Array() noexcept = default; + + Array(Array const &) noexcept = default; + + Array(Array &&) noexcept = default; + + template + Array(Array const & other) noexcept : Base(other) {} + + template + Array(Array && other) noexcept : Base(other) {} + + template + explicit Array( + ShapeVector const & shape, + EnableIfIndexVector order=MemoryOrder::ROW_MAJOR + ) : Base(shape, order) {} + + explicit Array(std::initializer_list shape, MemoryOrder order=MemoryOrder::ROW_MAJOR) : + Base(shape, order) {} + + template + Array( + std::shared_ptr data, + ShapeVector const & shape, + EnableIfIndexVector order=MemoryOrder::ROW_MAJOR + ) : Base(std::move(data), shape, order) {} + + Array(std::shared_ptr data, std::initializer_list shape, + MemoryOrder order=MemoryOrder::ROW_MAJOR) : + Base(std::move(data), shape, order) {} + + template + Array( + EnableIfIndexVector, ShapeVector, StridesVector> data, + ShapeVector const & shape, + StridesVector const & strides + ) : Base(std::move(data), shape, strides) {} + + template + Array( + std::shared_ptr data, + std::initializer_list shape, + std::initializer_list strides + ) : Base(std::move(data), shape, strides) {} + + Array(std::shared_ptr buffer, std::shared_ptr const> layout) : + Base(std::move(buffer), std::move(layout)) {} + + Array & operator=(Array const &) noexcept = default; + + Array & operator=(Array &&) noexcept = default; + + void swap(Array & other) noexcept { this->_impl.swap(other._impl); } + + Deref> operator*() const; + + T * data() const noexcept { return reinterpret_cast(this->_impl.buffer.get()); } + + using Interface::operator[]; + using Interface::begin; + using Interface::end; +}; + +template +void swap(Array & a, Array & b) { + static_assert(std::is_same::value, + "Cannot swap arrays with different types."); + a.swap(b); +} + +} // namespace ndarray + +#endif // !NDARRAY_Array_hpp_INCLUDED diff --git a/include/ndarray/ArrayBase.h b/include/ndarray/ArrayBase.h deleted file mode 100644 index 085a44c0..00000000 --- a/include/ndarray/ArrayBase.h +++ /dev/null @@ -1,230 +0,0 @@ -// -*- c++ -*- -/* - * Copyright (c) 2010-2012, Jim Bosch - * All rights reserved. - * - * ndarray is distributed under a simple BSD-like license; - * see the LICENSE file that should be present in the root - * of the source distribution, or alternately available at: - * https://github.com/ndarray/ndarray - */ -#ifndef NDARRAY_ArrayBase_h_INCLUDED -#define NDARRAY_ArrayBase_h_INCLUDED - -/** - * @file ndarray/ArrayBase.h - * - * @brief Definitions for ArrayBase. - */ - - -#include - -#include "ndarray/ExpressionBase.h" -#include "ndarray/Vector.h" -#include "ndarray/detail/Core.h" -#include "ndarray/detail/NestedIterator.h" -#include "ndarray/detail/StridedIterator.h" -#include "ndarray/detail/ArrayAccess.h" -#include "ndarray/detail/ViewBuilder.h" -#include "ndarray/ArrayTraits.h" - -namespace ndarray { - -/** - * @class ArrayBase - * @brief CRTP implementation for Array and ArrayRef. - * - * @ingroup MainGroup - * - * Implements member functions that need specialization for 1D arrays. - */ -template -class ArrayBase : public ExpressionBase { -protected: - typedef ExpressionTraits Traits; - typedef typename Traits::Core Core; - typedef typename Traits::CorePtr CorePtr; -public: - /// @brief Data type of array elements. - typedef typename Traits::Element Element; - /// @brief Nested array or element iterator. - typedef typename Traits::Iterator Iterator; - /// @brief Nested array or element reference. - typedef typename Traits::Reference Reference; - /// @brief Nested array or element value type. - typedef typename Traits::Value Value; - /// @brief Number of dimensions (boost::mpl::int_). - typedef typename Traits::ND ND; - /// @brief Number of guaranteed row-major contiguous dimensions, counted from the end (boost::mpl::int_). - typedef typename Traits::RMC RMC; - /// @brief Vector type for N-dimensional indices and shapes. - typedef Vector Index; - /// @brief Vector type for N-dimensional offsets and strides. - typedef Vector Strides; - /// @brief ArrayRef to a reverse-ordered contiguous array; the result of a call to transpose(). - typedef ArrayRef FullTranspose; - /// @brief ArrayRef to a noncontiguous array; the result of a call to transpose(...). - typedef ArrayRef Transpose; - /// @brief The corresponding Array type. - typedef Array Shallow; - /// @brief The corresponding ArrayRef type. - typedef ArrayRef Deep; - - /// @brief Return a single subarray. - Reference operator[](Size n) const { - return Traits::makeReference( - this->_data + n * this-> - #ifndef _MSC_VER - template - #endif - getStride<0>(), - this->_core - ); - } - - /// @brief Return a single element from the array. - Element & operator[](Index const & i) const { - return *(this->_data + this->_core->computeOffset(i)); - } - - /// @brief Return an Iterator to the beginning of the array. - Iterator begin() const { - return Traits::makeIterator( - this->_data, - this->_core, - this-> - #ifndef _MSC_VER - template - #endif - getStride<0>() - ); - } - - /// @brief Return an Iterator to one past the end of the array. - Iterator end() const { - return Traits::makeIterator( - this->_data + this-> - #ifndef _MSC_VER - template - #endif - getSize<0>() * this-> - #ifndef _MSC_VER - template - #endif - getStride<0>(), - this->_core, - this-> - #ifndef _MSC_VER - template - #endif - getStride<0>() - ); - } - - /// @brief Return a raw pointer to the first element of the array. - Element * getData() const { return _data; } - - /// @brief Return true if the array has a null data point. - bool isEmpty() const { return _data == 0; } - - /// @brief Return the opaque object responsible for memory management. - Manager::Ptr getManager() const { return this->_core->getManager(); } - - /// @brief Return the size of a specific dimension. - template Size getSize() const { - return detail::getDimension

(*this->_core).getSize(); - } - - /// @brief Return the stride in a specific dimension. - template Offset getStride() const { - return detail::getDimension

(*this->_core).getStride(); - } - - /// @brief Return a Vector of the sizes of all dimensions. - Index getShape() const { Index r; this->_core->fillShape(r); return r; } - - /// @brief Return a Vector of the strides of all dimensions. - Strides getStrides() const { Strides r; this->_core->fillStrides(r); return r; } - - /// @brief Return the total number of elements in the array. - Size getNumElements() const { return this->_core->getNumElements(); } - - /// @brief Return a view of the array with the order of the dimensions reversed. - FullTranspose transpose() const { - Index shape = getShape(); - Strides strides = getStrides(); - for (int n=0; n < ND::value / 2; ++n) { - std::swap(shape[n], shape[ND::value-n-1]); - std::swap(strides[n], strides[ND::value-n-1]); - } - return FullTranspose( - getData(), - Core::create(shape, strides, getManager()) - ); - } - - /// @brief Return a view of the array with the dimensions permuted. - Transpose transpose(Index const & order) const { - Index newShape; - Strides newStrides; - Index oldShape = getShape(); - Strides oldStrides = getStrides(); - for (int n=0; n < ND::value; ++n) { - newShape[n] = oldShape[order[n]]; - newStrides[n] = oldStrides[order[n]]; - } - return Transpose( - getData(), - Core::create(newShape, newStrides, getManager()) - ); - } - - /// @brief Return a Array view to this. - Shallow const shallow() const { return Shallow(this->getSelf()); } - - /// @brief Return an ArrayRef view to this. - Deep const deep() const { return Deep(this->getSelf()); } - - /// @brief A template metafunction class to determine the result of a view indexing operation. - template - struct ResultOf { - typedef Element Element_; - typedef typename detail::ViewTraits::ND ND_; - typedef typename detail::ViewTraits::RMC RMC_; - typedef ArrayRef Type; - typedef Array Value; - }; - - /// @brief Return a general view into this array (see @ref ndarrayTutorial). - template - typename ResultOf< View >::Type - operator[](View const & def) const { - return detail::buildView(this->getSelf(), def._seq); - } - -protected: - template friend class Array; - template friend class ArrayRef; - template friend struct ArrayTraits; - template friend class detail::NestedIterator; - template friend class ArrayBase; - template friend class detail::ArrayAccess; - - Element * _data; - CorePtr _core; - - void operator=(ArrayBase const & other) { - _data = other._data; - _core = other._core; - } - - template - ArrayBase(ArrayBase const & other) : _data(other._data), _core(other._core) {} - - ArrayBase(Element * data, CorePtr const & core) : _data(data), _core(core) {} -}; - -} // namespace ndarray - -#endif // !NDARRAY_ArrayBase_h_INCLUDED diff --git a/include/ndarray/ArrayBaseN.h b/include/ndarray/ArrayBaseN.h deleted file mode 100644 index 182e5670..00000000 --- a/include/ndarray/ArrayBaseN.h +++ /dev/null @@ -1,229 +0,0 @@ -// -*- c++ -*- -/* - * Copyright (c) 2010-2012, Jim Bosch - * All rights reserved. - * - * ndarray is distributed under a simple BSD-like license; - * see the LICENSE file that should be present in the root - * of the source distribution, or alternately available at: - * https://github.com/ndarray/ndarray - */ - -#ifndef NDARRAY_ArrayBaseN_h_INCLUDED -#define NDARRAY_ArrayBaseN_h_INCLUDED - -/** - * @file ndarray/ArrayBaseN.h - * - * @brief Definition of ArrayBaseN, a dimension-specialized CRTP base class for Array and ArrayRef. - */ - -#include "ndarray/ArrayBase.h" - -namespace ndarray { - -/** - * @brief An intermediate CRTP base class for Array and ArrayRef. - */ -template ::ND::value> -class ArrayBaseN : public ArrayBase< Derived > { - typedef ArrayBase Super; -protected: - typedef typename Super::Core Core; - typedef typename Super::CorePtr CorePtr; -public: - typedef typename Super::Element Element; -private: - template friend class Array; - template friend class ArrayRef; - void operator=(ArrayBaseN const & other) { - Super::operator=(other); - } - /// @internal @brief Construct an ArrayRef from a pointer and Core. - ArrayBaseN(Element * data, CorePtr const & core) : Super(data, core) {} -}; - - -/** - * @brief An intermediate CRTP base class for Array and ArrayRef (specialization for 1). - */ -template -class ArrayBaseN : public ArrayBase< Derived > { - typedef ArrayBase Super; -protected: - typedef typename Super::Core Core; - typedef typename Super::CorePtr CorePtr; -public: - typedef typename Super::Element Element; - - Element & operator()(int n0) const { - return this->operator[](makeVector(n0)); - } - -private: - template friend class Array; - template friend class ArrayRef; - - void operator=(ArrayBaseN const & other) { - Super::operator=(other); - } - - template - ArrayBaseN(ArrayBaseN const & other) : Super(other) {} - - ArrayBaseN(Element * data, CorePtr const & core) : Super(data, core) {} -}; - -/** - * @brief An intermediate CRTP base class for Array and ArrayRef (specialization for 2). - */ -template -class ArrayBaseN : public ArrayBase< Derived > { - typedef ArrayBase Super; -protected: - typedef typename Super::Core Core; - typedef typename Super::CorePtr CorePtr; -public: - typedef typename Super::Element Element; - - Element & operator()(int n0, int n1) const { - return this->operator[](makeVector(n0, n1)); - } - -private: - template friend class Array; - template friend class ArrayRef; - - void operator=(ArrayBaseN const & other) { - Super::operator=(other); - } - - template - ArrayBaseN(ArrayBaseN const & other) : Super(other) {} - - ArrayBaseN(Element * data, CorePtr const & core) : Super(data, core) {} -}; - -/** - * @brief An intermediate CRTP base class for Array and ArrayRef (specialization for 3). - */ -template -class ArrayBaseN : public ArrayBase< Derived > { - typedef ArrayBase Super; -protected: - typedef typename Super::Core Core; - typedef typename Super::CorePtr CorePtr; -public: - typedef typename Super::Element Element; - - Element & operator()(int n0, int n1, int n2) const { - return this->operator[](makeVector(n0, n1, n2)); - } - -private: - template friend class Array; - template friend class ArrayRef; - - void operator=(ArrayBaseN const & other) { - Super::operator=(other); - } - - template - ArrayBaseN(ArrayBaseN const & other) : Super(other) {} - - ArrayBaseN(Element * data, CorePtr const & core) : Super(data, core) {} -}; - -/** - * @brief An intermediate CRTP base class for Array and ArrayRef (specialization for 4). - */ -template -class ArrayBaseN : public ArrayBase< Derived > { - typedef ArrayBase Super; -protected: - typedef typename Super::Core Core; - typedef typename Super::CorePtr CorePtr; -public: - typedef typename Super::Element Element; - - Element & operator()(int n0, int n1, int n2, int n3) const { - return this->operator[](makeVector(n0, n1, n2, n3)); - } - -private: - template friend class Array; - template friend class ArrayRef; - - void operator=(ArrayBaseN const & other) { - Super::operator=(other); - } - - template - ArrayBaseN(ArrayBaseN const & other) : Super(other) {} - - ArrayBaseN(Element * data, CorePtr const & core) : Super(data, core) {} -}; - -/** - * @brief An intermediate CRTP base class for Array and ArrayRef (specialization for 5). - */ -template -class ArrayBaseN : public ArrayBase< Derived > { - typedef ArrayBase Super; -protected: - typedef typename Super::Core Core; - typedef typename Super::CorePtr CorePtr; -public: - typedef typename Super::Element Element; - - Element & operator()(int n0, int n1, int n2, int n3, int n4) const { - return this->operator[](makeVector(n0, n1, n2, n3, n4)); - } - -private: - template friend class Array; - template friend class ArrayRef; - - void operator=(ArrayBaseN const & other) { - Super::operator=(other); - } - - template - ArrayBaseN(ArrayBaseN const & other) : Super(other) {} - - ArrayBaseN(Element * data, CorePtr const & core) : Super(data, core) {} -}; - -/** - * @brief An intermediate CRTP base class for Array and ArrayRef (specialization for 6). - */ -template -class ArrayBaseN : public ArrayBase< Derived > { - typedef ArrayBase Super; -protected: - typedef typename Super::Core Core; - typedef typename Super::CorePtr CorePtr; -public: - typedef typename Super::Element Element; - - Element & operator()(int n0, int n1, int n2, int n3, int n4, int n5) const { - return this->operator[](makeVector(n0, n1, n2, n3, n4, n5)); - } - -private: - template friend class Array; - template friend class ArrayRef; - - void operator=(ArrayBaseN const & other) { - Super::operator=(other); - } - - template - ArrayBaseN(ArrayBaseN const & other) : Super(other) {} - - ArrayBaseN(Element * data, CorePtr const & core) : Super(data, core) {} -}; - -} // namespace ndarray - -#endif // !NDARRAY_ArrayBaseN_h_INCLUDED diff --git a/include/ndarray/ArrayInterfaceN.hpp b/include/ndarray/ArrayInterfaceN.hpp new file mode 100644 index 00000000..4d167cf1 --- /dev/null +++ b/include/ndarray/ArrayInterfaceN.hpp @@ -0,0 +1,140 @@ +// -*- c++ -*- +/* + * Copyright (c) 2010-2018, Jim Bosch + * All rights reserved. + * + * ndarray is distributed under a simple BSD-like license; + * see the LICENSE file that should be present in the root + * of the source distribution, or alternately available at: + * https://github.com/ndarray/ndarray + */ +#ifndef NDARRAY_ArrayInterfaceN_hpp_INCLUDED +#define NDARRAY_ArrayInterfaceN_hpp_INCLUDED + +#include "ndarray/common.hpp" +#include "ndarray/errors.hpp" +#include "ndarray/detail/ArrayImpl.hpp" +#include "ndarray/StridedIterator.hpp" +#include "ndarray/NestedIterator.hpp" + +namespace ndarray { + +namespace detail { + +constexpr Offset nested_contiguousness(Size n, Offset c) { + return (c > 0 && c == n) ? c - 1 : (c < 0 && -c == n) ? c + 1 : c; +} + +} // namespace detail + + +template +class ArrayInterfaceN { +public: + + using Reference = Element &; + using Iterator = Element *; + + Iterator begin() const { + return reinterpret_cast(impl().buffer.get()); + } + + Iterator end() const { + NDARRAY_ASSERT_AUDIT(impl().buffer != nullptr, Error::UNINITIALIZED, "buffer is null"); + NDARRAY_ASSERT_AUDIT(impl().layout != nullptr, Error::UNINITIALIZED, "layout is null"); + return reinterpret_cast(impl().buffer.get() + + impl().layout->size()*impl().layout->stride()); + } + + Reference operator[](Size n) const { + NDARRAY_ASSERT_AUDIT(impl().buffer != nullptr, Error::UNINITIALIZED, "buffer is null"); + NDARRAY_ASSERT_AUDIT(impl().layout != nullptr, Error::UNINITIALIZED, "layout is null"); + NDARRAY_ASSERT_AUDIT(n < impl().layout->size(), Error::OUT_OF_BOUNDS, + "array index {:d} out of bounds {:d}", n, impl().layout->size()); + return *reinterpret_cast(impl().buffer.get() + n*impl().layout->stride()); + } + +private: + + detail::ArrayImpl<1> const & impl() const { + return static_cast(*this)._impl; + } + +}; + + +template +class ArrayInterfaceN { +public: + + using Reference = Element &; + using Iterator = StridedIterator; + + Iterator begin() const { + NDARRAY_ASSERT_AUDIT(impl().layout != nullptr, Error::UNINITIALIZED, "layout is null"); + return Iterator(impl().buffer.get(), impl().layout->stride()); + } + + Iterator end() const { + NDARRAY_ASSERT_AUDIT(impl().buffer != nullptr, Error::UNINITIALIZED, "buffer is null"); + NDARRAY_ASSERT_AUDIT(impl().layout != nullptr, Error::UNINITIALIZED, "layout is null"); + return Iterator(impl().buffer.get() + impl().layout->size()*impl().layout->stride(), + impl().layout->stride()); + } + + Reference operator[](Size n) const { + NDARRAY_ASSERT_AUDIT(impl().buffer != nullptr, Error::UNINITIALIZED, "buffer is null"); + NDARRAY_ASSERT_AUDIT(impl().layout != nullptr, Error::UNINITIALIZED, "layout is null"); + NDARRAY_ASSERT_AUDIT(n < impl().layout->size(), Error::OUT_OF_BOUNDS, + "array index {:d} out of bounds {:d}", n, impl().layout->size()); + return *reinterpret_cast(impl().buffer.get() + n*impl().layout->stride()); + } + +private: + + detail::ArrayImpl<1> const & impl() const { + return static_cast(*this)._impl; + } + +}; + + +template +class ArrayInterfaceN { +public: + + using Reference = Array; + using Iterator = NestedIterator; + + Iterator begin() const { + return Iterator(Reference(impl().buffer, impl().layout)); + } + + Iterator end() const { + NDARRAY_ASSERT_AUDIT(impl().layout != nullptr, Error::UNINITIALIZED, "layout is null"); + return Iterator((*this)[impl().layout->size()]); + } + + Reference operator[](Size n) const { + NDARRAY_ASSERT_AUDIT(impl().layout != nullptr, Error::UNINITIALIZED, "layout is null"); + NDARRAY_ASSERT_AUDIT(impl().buffer != nullptr, Error::UNINITIALIZED, "buffer is null"); + return Reference( + std::shared_ptr( + impl().buffer, + impl().buffer.get() + n*impl().layout->stride() + ), + impl().layout + ); + } + +private: + + detail::ArrayImpl const & impl() const { + return static_cast(*this)._impl; + } + +}; + +} // namespace ndarray + +#endif // !NDARRAY_ArrayInterfaceN_hpp_INCLUDED diff --git a/include/ndarray/ArrayRef.h b/include/ndarray/ArrayRef.h deleted file mode 100644 index 0fec2c0a..00000000 --- a/include/ndarray/ArrayRef.h +++ /dev/null @@ -1,401 +0,0 @@ -// -*- c++ -*- -/* - * Copyright (c) 2010-2012, Jim Bosch - * All rights reserved. - * - * ndarray is distributed under a simple BSD-like license; - * see the LICENSE file that should be present in the root - * of the source distribution, or alternately available at: - * https://github.com/ndarray/ndarray - */ -#ifndef NDARRAY_ArrayRef_h_INCLUDED -#define NDARRAY_ArrayRef_h_INCLUDED - -/** - * @file ndarray/ArrayRef.h - * - * @brief Definitions for ArrayRef. - */ - -#include "ndarray_fwd.h" -#include "ndarray/ArrayTraits.h" -#include "ndarray/ArrayBaseN.h" -#include "ndarray/detail/ArrayAccess.h" -#include "ndarray/Vector.h" -#include "ndarray/detail/Core.h" -#include "ndarray/views.h" - -namespace ndarray { - -/** - * @brief A proxy class for Array with deep assignment operators. - */ -template -class ArrayRef : public ArrayBaseN< ArrayRef > { - typedef ArrayBaseN Super; - typedef typename Super::Core Core; - typedef typename Super::CorePtr CorePtr; -public: - typedef typename Super::Iterator Iterator; - - /** - * @brief Construct an array with the given dimensions and allocated but uninitialized memory. - * - * Unspecified dimensions will have unit size, and if the number of argmuments is greater - * than the number of dimensions of the array, the extra arguments will be silently ignored. - * - * This is implemented in initialization.h. - */ - explicit ArrayRef(Size n1, Size n2=1, Size n3=1, Size n4=1, Size n5=1, Size n6=1, Size n7=1, Size n8=1); - - /** - * @brief Construct an array with the given dimensions and allocated but uninitialized memory. - * - * This is implemented in initialization.h. - */ - template - explicit ArrayRef(Vector const & shape); - - /** - * @brief Non-converting copy constructor. - */ - ArrayRef(ArrayRef const & other) : Super(other._data, other._core) {} - - /** - * @brief Converting copy constructor. - * - * Implicit conversion is allowed for non-const to const and for - * more guaranteed RMC to less guaranteed RMC (see \ref ndarrayTutorial). - */ - template - explicit ArrayRef( - Array const & other -#ifndef DOXYGEN - , typename boost::enable_if,void*>::type=0 -#endif - ) : Super(other._data, other._core) {} - - /** - * @brief Converting copy constructor. - * - * Implicit conversion is allowed for non-const to const and for - * more guaranteed RMC to less guaranteed RMC (see \ref ndarrayTutorial). - */ - template - ArrayRef( - ArrayRef const & other -#ifndef DOXYGEN - , typename boost::enable_if,void*>::type=0 -#endif - ) : Super(other._data, other._core) {} - - /** - * @name Assignment and Augmented Assignment Operators - * - * ArrayRef assignment is deep, and requires that - * the ArrayRef being assigned to has the same shape as - * the input array expression. Scalar assignment sets - * all elements of the ArrayRef to a single value. - */ - /// @{ - ArrayRef const & operator=(Array const & other) const { - NDARRAY_ASSERT(other.getShape() == this->getShape()); - std::copy(other.begin(), other.end(), this->begin()); - return *this; - } - - ArrayRef const & operator=(ArrayRef const & other) const { - NDARRAY_ASSERT(other.getShape() == this->getShape()); - std::copy(other.begin(), other.end(), this->begin()); - return *this; - } - - - /// \brief = assignment of arrays and array expressions. - template - ArrayRef const & - operator =(ExpressionBase const & expr) const { - NDARRAY_ASSERT(expr.getShape() - == this->getShape().template first::ND::value>()); - std::copy(expr.begin(),expr.end(),this->begin()); - return *this; - } - - /// \brief = assignment of scalars. - template -#ifndef DOXYGEN - typename boost::enable_if, ArrayRef const &>::type -#else - ArrayRef const & -#endif - operator =(Scalar const & scalar) const { - Super::Traits::fill(this->begin(),this->end(),scalar); - return *this; - } - - /// \brief += assignment of arrays and array expressions. - template - ArrayRef const & - operator +=(ExpressionBase const & expr) const { - NDARRAY_ASSERT(expr.getShape() - == this->getShape().template first::ND::value>()); - Iterator const i_end = this->end(); - typename Other::Iterator j = expr.begin(); - for (Iterator i = this->begin(); i != i_end; ++i, ++j) (*i) += (*j); - return *this; - } - - /// \brief += assignment of scalars. - template -#ifndef DOXYGEN - typename boost::enable_if, ArrayRef const &>::type -#else - ArrayRef const & -#endif - operator +=(Scalar const & scalar) const { - Iterator const i_end = this->end(); - for (Iterator i = this->begin(); i != i_end; ++i) (*i) += scalar; - return *this; - } - - /// \brief -= assignment of arrays and array expressions. - template - ArrayRef const & - operator -=(ExpressionBase const & expr) const { - NDARRAY_ASSERT(expr.getShape() - == this->getShape().template first::ND::value>()); - Iterator const i_end = this->end(); - typename Other::Iterator j = expr.begin(); - for (Iterator i = this->begin(); i != i_end; ++i, ++j) (*i) -= (*j); - return *this; - } - - /// \brief -= assignment of scalars. - template -#ifndef DOXYGEN - typename boost::enable_if, ArrayRef const &>::type -#else - ArrayRef const & -#endif - operator -=(Scalar const & scalar) const { - Iterator const i_end = this->end(); - for (Iterator i = this->begin(); i != i_end; ++i) (*i) -= scalar; - return *this; - } - - /// \brief *= assignment of arrays and array expressions. - template - ArrayRef const & - operator *=(ExpressionBase const & expr) const { - NDARRAY_ASSERT(expr.getShape() - == this->getShape().template first::ND::value>()); - Iterator const i_end = this->end(); - typename Other::Iterator j = expr.begin(); - for (Iterator i = this->begin(); i != i_end; ++i, ++j) (*i) *= (*j); - return *this; - } - - /// \brief *= assignment of scalars. - template -#ifndef DOXYGEN - typename boost::enable_if, ArrayRef const &>::type -#else - ArrayRef const & -#endif - operator *=(Scalar const & scalar) const { - Iterator const i_end = this->end(); - for (Iterator i = this->begin(); i != i_end; ++i) (*i) *= scalar; - return *this; - } - - /// \brief /= assignment of arrays and array expressions. - template - ArrayRef const & - operator /=(ExpressionBase const & expr) const { - NDARRAY_ASSERT(expr.getShape() - == this->getShape().template first::ND::value>()); - Iterator const i_end = this->end(); - typename Other::Iterator j = expr.begin(); - for (Iterator i = this->begin(); i != i_end; ++i, ++j) (*i) /= (*j); - return *this; - } - - /// \brief /= assignment of scalars. - template -#ifndef DOXYGEN - typename boost::enable_if, ArrayRef const &>::type -#else - ArrayRef const & -#endif - operator /=(Scalar const & scalar) const { - Iterator const i_end = this->end(); - for (Iterator i = this->begin(); i != i_end; ++i) (*i) /= scalar; - return *this; - } - - /// \brief %= assignment of arrays and array expressions. - template - ArrayRef const & - operator %=(ExpressionBase const & expr) const { - NDARRAY_ASSERT(expr.getShape() - == this->getShape().template first::ND::value>()); - Iterator const i_end = this->end(); - typename Other::Iterator j = expr.begin(); - for (Iterator i = this->begin(); i != i_end; ++i, ++j) (*i) %= (*j); - return *this; - } - - /// \brief %= assignment of scalars. - template -#ifndef DOXYGEN - typename boost::enable_if, ArrayRef const &>::type -#else - ArrayRef const & -#endif - operator %=(Scalar const & scalar) const { - Iterator const i_end = this->end(); - for (Iterator i = this->begin(); i != i_end; ++i) (*i) %= scalar; - return *this; - } - - /// \brief ^= assignment of arrays and array expressions. - template - ArrayRef const & - operator ^=(ExpressionBase const & expr) const { - NDARRAY_ASSERT(expr.getShape() - == this->getShape().template first::ND::value>()); - Iterator const i_end = this->end(); - typename Other::Iterator j = expr.begin(); - for (Iterator i = this->begin(); i != i_end; ++i, ++j) (*i) ^= (*j); - return *this; - } - - /// \brief ^= assignment of scalars. - template -#ifndef DOXYGEN - typename boost::enable_if, ArrayRef const &>::type -#else - ArrayRef const & -#endif - operator ^=(Scalar const & scalar) const { - Iterator const i_end = this->end(); - for (Iterator i = this->begin(); i != i_end; ++i) (*i) ^= scalar; - return *this; - } - - /// \brief &= assignment of arrays and array expressions. - template - ArrayRef const & - operator &=(ExpressionBase const & expr) const { - NDARRAY_ASSERT(expr.getShape() - == this->getShape().template first::ND::value>()); - Iterator const i_end = this->end(); - typename Other::Iterator j = expr.begin(); - for (Iterator i = this->begin(); i != i_end; ++i, ++j) (*i) &= (*j); - return *this; - } - - /// \brief &= assignment of scalars. - template -#ifndef DOXYGEN - typename boost::enable_if, ArrayRef const &>::type -#else - ArrayRef const & -#endif - operator &=(Scalar const & scalar) const { - Iterator const i_end = this->end(); - for (Iterator i = this->begin(); i != i_end; ++i) (*i) &= scalar; - return *this; - } - - /// \brief |= assignment of arrays and array expressions. - template - ArrayRef const & - operator |=(ExpressionBase const & expr) const { - NDARRAY_ASSERT(expr.getShape() - == this->getShape().template first::ND::value>()); - Iterator const i_end = this->end(); - typename Other::Iterator j = expr.begin(); - for (Iterator i = this->begin(); i != i_end; ++i, ++j) (*i) |= (*j); - return *this; - } - - /// \brief |= assignment of scalars. - template -#ifndef DOXYGEN - typename boost::enable_if, ArrayRef const &>::type -#else - ArrayRef const & -#endif - operator |=(Scalar const & scalar) const { - Iterator const i_end = this->end(); - for (Iterator i = this->begin(); i != i_end; ++i) (*i) |= scalar; - return *this; - } - - /// \brief <<= assignment of arrays and array expressions. - template - ArrayRef const & - operator <<=(ExpressionBase const & expr) const { - NDARRAY_ASSERT(expr.getShape() - == this->getShape().template first::ND::value>()); - Iterator const i_end = this->end(); - typename Other::Iterator j = expr.begin(); - for (Iterator i = this->begin(); i != i_end; ++i, ++j) (*i) <<= (*j); - return *this; - } - - /// \brief <<= assignment of scalars. - template -#ifndef DOXYGEN - typename boost::enable_if, ArrayRef const &>::type -#else - ArrayRef const & -#endif - operator <<=(Scalar const & scalar) const { - Iterator const i_end = this->end(); - for (Iterator i = this->begin(); i != i_end; ++i) (*i) <<= scalar; - return *this; - } - - /// \brief >>= assignment of arrays and array expressions. - template - ArrayRef const & - operator >>=(ExpressionBase const & expr) const { - NDARRAY_ASSERT(expr.getShape() - == this->getShape().template first::ND::value>()); - Iterator const i_end = this->end(); - typename Other::Iterator j = expr.begin(); - for (Iterator i = this->begin(); i != i_end; ++i, ++j) (*i) >>= (*j); - return *this; - } - - /// \brief >>= assignment of scalars. - template -#ifndef DOXYGEN - typename boost::enable_if, ArrayRef const &>::type -#else - ArrayRef const & -#endif - operator >>=(Scalar const & scalar) const { - Iterator const i_end = this->end(); - for (Iterator i = this->begin(); i != i_end; ++i) (*i) >>= scalar; - return *this; - } - ///@} - -private: - template friend class Array; - template friend class ArrayRef; - template friend struct ArrayTraits; - template friend class ArrayBase; - template friend class detail::ArrayAccess; - - /// @internal @brief Construct an ArrayRef from a pointer and Core. - ArrayRef(T * data, CorePtr const & core) : Super(data, core) {} - -}; - -} // namespace ndarray - -#endif // !NDARRAY_ArrayRef_h_INCLUDED diff --git a/include/ndarray/ArrayTraits.h b/include/ndarray/ArrayTraits.h deleted file mode 100644 index 1c4c3e2e..00000000 --- a/include/ndarray/ArrayTraits.h +++ /dev/null @@ -1,152 +0,0 @@ -// -*- c++ -*- -/* - * Copyright (c) 2010-2012, Jim Bosch - * All rights reserved. - * - * ndarray is distributed under a simple BSD-like license; - * see the LICENSE file that should be present in the root - * of the source distribution, or alternately available at: - * https://github.com/ndarray/ndarray - */ -#ifndef NDARRAY_ArrayTraits_h_INCLUDED -#define NDARRAY_ArrayTraits_h_INCLUDED - -/** - * @file ndarray/ArrayTraits.h - * - * @brief Traits for Array. - */ - -#include "ndarray_fwd.h" -#include "ndarray/ExpressionTraits.h" -#include "ndarray/detail/Core.h" -#include -#include - -namespace ndarray { -namespace detail { - -template -struct Convertible : public boost::mpl::bool_< - (((C2>=C1 && C1>=0) || (C2<=C1 && C1<=0) || (N == 1 && C2 == -C1)) - && boost::is_convertible::value) -> {}; - -} // namespace detail - - -/** - * @brief Dimension-specialized traits shared by Array and ArrayRef. - * - * @ingroup MainGroup - */ -template -struct ArrayTraits { - typedef T Element; - typedef boost::mpl::int_ ND; - typedef boost::mpl::int_ RMC; - typedef detail::NestedIterator Iterator; - typedef ArrayRef0)?C:0)> Reference; - typedef Array0)?C:0)> Value; - typedef detail::Core Core; - typedef typename Core::ConstPtr CorePtr; - - static Reference makeReference(Element * data, CorePtr const & core) { - return Reference(data, core); - } - static Iterator makeIterator(Element * data, CorePtr const & core, Offset stride) { - return Iterator(Reference(data, core), stride); - } - static void fill(Iterator iter, Iterator const & end, Element value) { - // We can't use std::fill here because NestedIterator is not formally an STL ForwardIterator; - // it has random access traversal, but it does not dereference to an addressable type (see - // http://www.boost.org/doc/libs/1_55_0/libs/iterator/doc/new-iter-concepts.html#motivation) - // Most C++ standard libraries have a fill implementation that will accept NestedIterator - // anyway, but Clang's libc++ is more strictly compliant and does not. - for (; iter != end; ++iter) { - *iter = value; - } - } -}; - -template -struct ArrayTraits { - typedef T Element; - typedef boost::mpl::int_<1> ND; - typedef boost::mpl::int_<0> RMC; - typedef detail::StridedIterator Iterator; - typedef Element & Reference; - typedef Element Value; - typedef detail::Core<1> Core; - typedef typename Core::ConstPtr CorePtr; - - static Reference makeReference(Element * data, CorePtr const & core) { - return *data; - } - static Iterator makeIterator(Element * data, CorePtr const & core, Offset stride) { - return Iterator(data, stride); - } - static void fill(Iterator iter, Iterator const & end, Element value) { - std::fill(iter, end, value); - } -}; - -template -struct ArrayTraits { - typedef T Element; - typedef boost::mpl::int_<1> ND; - typedef boost::mpl::int_<1> RMC; - typedef Element * Iterator; - typedef Element & Reference; - typedef Element Value; - typedef detail::Core<1> Core; - typedef typename Core::ConstPtr CorePtr; - - static Reference makeReference(Element * data, CorePtr const & core) { - return *data; - } - static Iterator makeIterator(Element * data, CorePtr const & core, Offset stride) { - return data; - } - static void fill(Iterator iter, Iterator const & end, Element value) { - std::fill(iter, end, value); - } -}; - -template -struct ArrayTraits { - typedef T Element; - typedef boost::mpl::int_<1> ND; - typedef boost::mpl::int_<-1> RMC; - typedef Element * Iterator; - typedef Element & Reference; - typedef Element Value; - typedef detail::Core<1> Core; - typedef typename Core::ConstPtr CorePtr; - - static Reference makeReference(Element * data, CorePtr const & core) { - return *data; - } - static Iterator makeIterator(Element * data, CorePtr const & core, Offset stride) { - return data; - } - static void fill(Iterator iter, Iterator const & end, Element value) { - std::fill(iter, end, value); - } -}; - -template -struct ExpressionTraits< Array > : public ArrayTraits { - typedef Array Self; - typedef boost::mpl::false_ IsScalar; -}; - -template -struct ExpressionTraits< ArrayRef > : public ArrayTraits { - typedef ArrayRef Self; - typedef boost::mpl::false_ IsScalar; -}; - -} // namespace ndarray - -#endif // !NDARRAY_ArrayTraits_h_INCLUDED diff --git a/include/ndarray/ExpressionBase.h b/include/ndarray/ExpressionBase.h deleted file mode 100644 index 965b8a0b..00000000 --- a/include/ndarray/ExpressionBase.h +++ /dev/null @@ -1,107 +0,0 @@ -// -*- c++ -*- -/* - * Copyright (c) 2010-2012, Jim Bosch - * All rights reserved. - * - * ndarray is distributed under a simple BSD-like license; - * see the LICENSE file that should be present in the root - * of the source distribution, or alternately available at: - * https://github.com/ndarray/ndarray - */ -#ifndef NDARRAY_ExpressionBase_h_INCLUDED -#define NDARRAY_ExpressionBase_h_INCLUDED - -/** - * @file ndarray/ExpressionBase.h - * - * @brief Definitions for ExpressionBase. - */ - -#include "ndarray/ExpressionTraits.h" -#include "ndarray/Vector.h" - -namespace ndarray { - -/** - * @class ExpressionBase - * @brief CRTP base class for all multidimensional expressions. - * - * @ingroup MainGroup - * - * ExpressionBase is a CRTP base class for both true array objects (subclasses of - * ArrayBase) and lazy array expressions, which are created by most arithmetic, - * bitwise, and logical operations on arrays. These lazy expressions have most - * of the features of a true array to a const data type. - * - * ExpressionBase also provides implementations for a few STL compatibility and - * convenience member functions. - */ -template -class ExpressionBase { -public: - /// @brief Data type of expression elements. - typedef typename ExpressionTraits::Element Element; - /// @brief Number of dimensions (boost::mpl::int_). - typedef typename ExpressionTraits::ND ND; - /// @brief Nested expression or element iterator. - typedef typename ExpressionTraits::Iterator Iterator; - /// @brief Nested expression or element reference. - typedef typename ExpressionTraits::Reference Reference; - /// @brief Nested expression or element value type. - typedef typename ExpressionTraits::Value Value; - /// @brief Vector type for N-dimensional indices. - typedef Vector Index; - /// @brief CRTP derived type. - typedef Derived Self; - - /// @brief Return a single nested expression or element. - Reference operator[](Size n) const { return getSelf().operator[](n); } - - /// @brief Return the first nested expression or element. - Reference front() const { return this->operator[](0); } - - /// @brief Return the last nested expression or element. - Reference back() const { return this->operator[](this->template getSize<0>()-1); } - - /// @brief Return an Iterator to the beginning of the expression. - Iterator begin() const { return getSelf().begin(); } - - /// @brief Return an Iterator to one past the end of the expression. - Iterator end() const { return getSelf().end(); } - - /// @brief Return the size of a specific dimension. - template Size getSize() const { return getSelf().template getSize

(); } - - /// @brief Return a Vector of the sizes of all dimensions. - Index getShape() const { return getSelf().getShape(); } - - /// @brief Return the total number of elements in the expression. - Size getNumElements() const { return getSelf().getNumElements(); } - - /* ------------------------- STL Compatibility -------------------------- */ - - typedef Value value_type; - typedef Iterator iterator; - typedef Iterator const_iterator; - typedef Reference reference; - typedef Reference const_reference; - typedef Iterator pointer; - typedef Offset difference_type; - typedef Size size_type; - - /// @brief Return the size of the first dimension. - size_type size() const { return this->template getSize<0>(); } - - /// @brief Return true if the first dimension has no elements. - bool empty() const { return this->template getSize<0>() == 0; } - - /* ---------------------------------------------------------------------- */ - -protected: - Self & getSelf() { return *static_cast(this); } - Self const & getSelf() const { return *static_cast(this); } -}; - -} // namespace ndarray - -#endif // !NDARRAY_ExpressionBase_h_INCLUDED diff --git a/include/ndarray/ExpressionTraits.h b/include/ndarray/ExpressionTraits.h deleted file mode 100644 index 837ad64b..00000000 --- a/include/ndarray/ExpressionTraits.h +++ /dev/null @@ -1,107 +0,0 @@ -// -*- c++ -*- -/* - * Copyright (c) 2010-2012, Jim Bosch - * All rights reserved. - * - * ndarray is distributed under a simple BSD-like license; - * see the LICENSE file that should be present in the root - * of the source distribution, or alternately available at: - * https://github.com/ndarray/ndarray - */ -#ifndef NDARRAY_ExpressionTraits_h_INCLUDED -#define NDARRAY_ExpressionTraits_h_INCLUDED - -/** - * @file ndarray/ExpressionTraits.h - * - * @brief Traits for Expression. - */ - -#include "ndarray_fwd.h" -#include - -namespace ndarray { - -/** - * @brief Traits for expressions. - * - * @ingroup MainGroup - */ -template struct ExpressionTraits { - typedef boost::mpl::true_ IsScalar; -}; - -#ifndef GCC_45 - -/** - * @internal @brief ExpressionTraits specialization for UnaryOpExpression. - * - * @ingroup ndarrayInternalGroup - */ -template -struct ExpressionTraits< detail::UnaryOpExpression > { - typedef typename UnaryFunction::result_type Element; - typedef typename ExpressionTraits::ND ND; - typedef detail::UnaryOpIterator Iterator; - typedef detail::UnaryOpExpression< - typename ExpressionTraits::Reference,UnaryFunction,N-1 - > Value; - typedef Value Reference; - typedef boost::mpl::false_ IsScalar; -}; - -/** - * @internal @brief ExpressionTraits specialization for 1D UnaryOpExpression. - * - * @ingroup ndarrayInternalGroup - */ -template -struct ExpressionTraits< detail::UnaryOpExpression > { - typedef typename UnaryFunction::result_type Element; - typedef typename ExpressionTraits::ND ND; - typedef detail::UnaryOpIterator Iterator; - typedef typename boost::remove_const::type Value; - typedef Value const Reference; - typedef boost::mpl::false_ IsScalar; -}; - -/** - * @internal @brief ExpressionTraits specialization for BinaryOpExpression. - * - * @ingroup ndarrayInternalGroup - */ -template -struct ExpressionTraits< detail::BinaryOpExpression > { - typedef typename BinaryFunction::result_type Element; - typedef typename ExpressionTraits::ND ND; - typedef detail::BinaryOpIterator Iterator; - typedef detail::BinaryOpExpression< - typename ExpressionTraits::Reference, - typename ExpressionTraits::Reference, - BinaryFunction, N-1 > Reference; - typedef Reference Value; - typedef boost::mpl::false_ IsScalar; - BOOST_STATIC_ASSERT((ND::value == ExpressionTraits::ND::value)); -}; - -/** - * @internal @brief ExpressionTraits specialization for 1D BinaryOpExpression. - * - * @ingroup ndarrayInternalGroup - */ -template -struct ExpressionTraits< detail::BinaryOpExpression > { - typedef typename BinaryFunction::result_type Element; - typedef typename ExpressionTraits::ND ND; - typedef detail::BinaryOpIterator Iterator; - typedef typename boost::remove_const::type Value; - typedef Value const Reference; - typedef boost::mpl::false_ IsScalar; - BOOST_STATIC_ASSERT((ND::value == ExpressionTraits::ND::value)); -}; - -#endif // GCC_45 - -} // namespace ndarray - -#endif // !NDARRAY_ExpressionTraits_h_INCLUDED diff --git a/include/ndarray/IndexVectorTraits.hpp b/include/ndarray/IndexVectorTraits.hpp new file mode 100644 index 00000000..b78c59d3 --- /dev/null +++ b/include/ndarray/IndexVectorTraits.hpp @@ -0,0 +1,116 @@ +// -*- c++ -*- +/* + * Copyright (c) 2010-2016, Jim Bosch + * All rights reserved. + * + * ndarray is distributed under a simple BSD-like license; + * see the LICENSE file that should be present in the root + * of the source distribution, or alternately available at: + * https://github.com/ndarray/ndarray + */ +#ifndef NDARRAY_IndexVectorTraits_hpp_INCLUDED +#define NDARRAY_IndexVectorTraits_hpp_INCLUDED + +#include +#include +#include + +#include "ndarray/common.hpp" +#include "ndarray/errors.hpp" + +namespace ndarray { + +template +struct IndexVectorTraits { + + static constexpr bool is_specialized = false; + +}; + +template +struct IndexVectorTraits> { + + static constexpr bool is_specialized = true; + + template + static void check_dims(std::array const & v) { + static_assert( + M == N, + "Index vector has wrong number of elements." + ); + } + + static Size get_size(std::array const & v, Size n) { + return v[n]; + } + + static Offset get_offset(std::array const & v, Size n) { + return v[n]; + } + +}; + +template +struct IndexVectorTraits> { + + static constexpr bool is_specialized = true; + + template + static void check_dims(std::initializer_list const & v) { + NDARRAY_ASSERT_CHECK(M == v.size(), + "Shape vector size ({:d}) does not match number of dimensions ({:d}).", + v.size(), M); + } + + static Size get_size(std::initializer_list const & v, Size n) { + return v.begin()[n]; + } + + static Offset get_offset(std::initializer_list const & v, Size n) { + return v.begin()[n]; + } + +}; + +template +struct IndexVectorTraits> { + + static constexpr bool is_specialized = true; + + template + static void check_dims(std::vector const & v) { + NDARRAY_ASSERT_CHECK(M == v.size(), + "Shape vector size ({:d}) does not match number of dimensions ({:d}).", + v.size(), M); + } + + static Size get_size(std::vector const & v, Size n) { + return v[n]; + } + + static Offset get_offset(std::vector const & v, Size n) { + return v[n]; + } + +}; + + +template +struct IsIndexVector { + static constexpr bool value = IndexVectorTraits::is_specialized && IsIndexVector::value; +}; + + +template +struct IsIndexVector { + static constexpr bool value = IndexVectorTraits::is_specialized; +}; + + +template +using EnableIfIndexVector = typename std::enable_if::value, Arg>::type; + + +} // ndarray + +#endif // !NDARRAY_IndexVectorTraits_hpp_INCLUDED \ No newline at end of file diff --git a/include/ndarray/Manager.h b/include/ndarray/Manager.h deleted file mode 100644 index 4e4ea0ea..00000000 --- a/include/ndarray/Manager.h +++ /dev/null @@ -1,100 +0,0 @@ -// -*- c++ -*- -/* - * Copyright (c) 2010-2012, Jim Bosch - * All rights reserved. - * - * ndarray is distributed under a simple BSD-like license; - * see the LICENSE file that should be present in the root - * of the source distribution, or alternately available at: - * https://github.com/ndarray/ndarray - */ -#ifndef NDARRAY_Manager_h_INCLUDED -#define NDARRAY_Manager_h_INCLUDED - -/** - * @file ndarray/Manager.h - * - * @brief Definition of Manager, which manages the ownership of array data. - */ - -#include "ndarray_fwd.h" -#include -#include -#include - -namespace ndarray { - -class Manager : private boost::noncopyable { -public: - - typedef boost::intrusive_ptr Ptr; - - friend inline void intrusive_ptr_add_ref(Manager const * manager) { - ++manager->_rc; - } - - friend inline void intrusive_ptr_release(Manager const * manager) { - if ((--manager->_rc)==0) delete manager; - } - - int getRC() const { return _rc; } - - virtual bool isUnique() const { return false; } - -protected: - - virtual ~Manager() {} - - explicit Manager() : _rc(0) {} - -private: - mutable int _rc; -}; - -template -class SimpleManager : public Manager { - typedef typename boost::remove_const::type U; -public: - - static std::pair allocate(Size size) { - boost::intrusive_ptr r(new SimpleManager(size)); - return std::pair(r, r->_p.get()); - } - - virtual bool isUnique() const { return true; } - -private: - explicit SimpleManager(Size size) : _p() { - if (size > 0) _p.reset(new U[size]); - } - boost::scoped_array _p; -}; - -template Manager::Ptr makeManager(T const & owner); - -template -class ExternalManager : public Manager, private U { -public: - typedef U Owner; - - template friend Manager::Ptr makeManager(T const & owner); - - Owner const & getOwner() const { return *static_cast(this); } - -private: - explicit ExternalManager(Owner const & owner) : Owner(owner) {} -}; - -template -inline Manager::Ptr makeManager(T const & owner) { - return Manager::Ptr(new ExternalManager(owner)); -} - -// A no-op overload for makeManager to avoid unnecessary levels of indirection. -inline Manager::Ptr makeManager(Manager::Ptr const & owner) { - return owner; -} - -} // namespace ndarray - -#endif // !NDARRAY_Manager_h_INCLUDED diff --git a/include/ndarray/NestedIterator.hpp b/include/ndarray/NestedIterator.hpp new file mode 100644 index 00000000..94487230 --- /dev/null +++ b/include/ndarray/NestedIterator.hpp @@ -0,0 +1,164 @@ +// -*- c++ -*- +/* + * Copyright (c) 2010-2018, Jim Bosch + * All rights reserved. + * + * ndarray is distributed under a simple BSD-like license; + * see the LICENSE file that should be present in the root + * of the source distribution, or alternately available at: + * https://github.com/ndarray/ndarray + */ +#ifndef NDARRAY_NestedIterator_hpp_INCLUDED +#define NDARRAY_NestedIterator_hpp_INCLUDED + +#include +#include + +#include "ndarray/common.hpp" +#include "ndarray/errors.hpp" + +namespace ndarray { + +template +class NestedIterator { +public: + + using value_type = Array; + using reference = value_type const &; + using pointer = value_type const *; + using difference_type = Offset; + using iterator_category = std::input_iterator_tag; // would be random-access, but not multi-pass + + NestedIterator() : _array() {} + + explicit NestedIterator(Array array) : _array(std::move(array)) {} + + NestedIterator(NestedIterator const &) noexcept = default; + NestedIterator(NestedIterator &&) noexcept = default; + + NestedIterator & operator=(NestedIterator const &) noexcept = default; + NestedIterator & operator=(NestedIterator &&) noexcept = default; + + ~NestedIterator() noexcept = default; + + void swap(NestedIterator & other) noexcept { + using namespace std; + swap(_array, other._array); + } + + friend void swap(NestedIterator & a, NestedIterator & b) noexcept { + a.swap(b); + } + + template + bool operator==(NestedIterator const & rhs) const { return _array == rhs._array; } + + template + bool operator!=(NestedIterator const & rhs) const { return !(*this == rhs); } + + template + bool operator<(NestedIterator const & rhs) const { + NDARRAY_ASSERT_CHECK(_array.data() != nullptr, Error::UNINITIALIZED, "null iterator comparison"); + NDARRAY_ASSERT_CHECK(rhs._array.data() != nullptr, Error::UNINITIALIZED, "null iterator comparison"); + return _array.data() < rhs._array.data(); + } + + template + bool operator>(NestedIterator const & rhs) const { + NDARRAY_ASSERT_CHECK(_array.data() != nullptr, Error::UNINITIALIZED, "null iterator comparison"); + NDARRAY_ASSERT_CHECK(rhs._array.data() != nullptr, Error::UNINITIALIZED, "null iterator comparison"); + return _array.data() > rhs._array.data(); + } + + template + bool operator<=(NestedIterator const & rhs) const { + NDARRAY_ASSERT_CHECK(_array.data() != nullptr, Error::UNINITIALIZED, "null iterator comparison"); + NDARRAY_ASSERT_CHECK(rhs._array.data() != nullptr, Error::UNINITIALIZED, "null iterator comparison"); + return _array.data() <= rhs._array.data(); + } + + template + bool operator>=(NestedIterator const & rhs) const { + NDARRAY_ASSERT_CHECK(_array.data() != nullptr, Error::UNINITIALIZED, "null iterator comparison"); + NDARRAY_ASSERT_CHECK(rhs._array.data() != nullptr, Error::UNINITIALIZED, "null iterator comparison"); + return _array.data() >= rhs._array.data(); + } + + reference operator*() const { return _array; } + + pointer operator->() const { return &_array; } + + NestedIterator & operator++() { return _advance(1); } + + NestedIterator & operator--() { return _advance(-1); } + + NestedIterator operator++(int) { + NestedIterator copy(*this); + ++(*this); + return copy; + } + + NestedIterator operator--(int) { + NestedIterator copy(*this); + --(*this); + return copy; + } + + NestedIterator & operator+=(difference_type n) { return _advance(n); } + + NestedIterator & operator-=(difference_type n) { return _advance(-n); } + + NestedIterator operator+(difference_type n) const { + return NestedIterator(*this) += n; + } + + NestedIterator operator-(difference_type n) const { + return NestedIterator(*this) -= n; + } + + friend NestedIterator operator+(difference_type n, NestedIterator iter) { + return iter + n; + } + + template + difference_type operator-(NestedIterator const & rhs) const { + NDARRAY_ASSERT_CHECK(_array.data() != nullptr, Error::UNINITIALIZED, "null iterator comparison"); + NDARRAY_ASSERT_CHECK(rhs._array.data() != nullptr, Error::UNINITIALIZED, "null iterator comparison"); + NDARRAY_ASSERT_CHECK(_stride() == rhs._stride(), Error::INCOMPATIBLE_ARGUMENTS, + "iterators have different strides: {:d} != {:d}", _stride(), rhs._stride()); + NDARRAY_ASSERT_CHECK(_stride() != 0, Error::UNINITIALIZED, "iterator stride is zero"); + NDARRAY_ASSERT_CHECK((_array._impl.data() - rhs._array._impl.data()) % _stride() == 0, + Error::INCOMPATIBLE_ARGUMENTS, + "iterator pointer offset ({:d}) is not a multiple of stride ({:d})", + (_array._impl.data() - rhs._array._impl.data()), _stride()); + return (_array._impl.data() - rhs._array._impl.data())/_stride(); + } + + value_type operator[](difference_type n) const { + return *NestedIterator(*this)._advance(n); + } + +private: + + template friend class NestedIterator; + + Offset _stride() const { + return static_cast const &>(*_array._impl.layout).stride(); + } + + NestedIterator & _advance(Offset n) { + NDARRAY_ASSERT_CHECK(_array._impl.buffer != nullptr, Error::UNINITIALIZED, + "null iterator incremented"); + _array._impl.buffer = std::shared_ptr( + _array._impl.buffer, + _array._impl.buffer.get() + n*_stride() + ); + return *this; + } + + Array _array; +}; + +} // ndarray + +#endif // !NDARRAY_NestedIterator_hpp_INCLUDED diff --git a/include/ndarray/StridedIterator.hpp b/include/ndarray/StridedIterator.hpp new file mode 100644 index 00000000..2a946e4f --- /dev/null +++ b/include/ndarray/StridedIterator.hpp @@ -0,0 +1,173 @@ +// -*- c++ -*- +/* + * Copyright (c) 2010-2018, Jim Bosch + * All rights reserved. + * + * ndarray is distributed under a simple BSD-like license; + * see the LICENSE file that should be present in the root + * of the source distribution, or alternately available at: + * https://github.com/ndarray/ndarray + */ +#ifndef NDARRAY_StridedIterator_hpp_INCLUDED +#define NDARRAY_StridedIterator_hpp_INCLUDED + +#include +#include + +#include "ndarray/common.hpp" +#include "ndarray/errors.hpp" + +namespace ndarray { + +template +class StridedIterator { +public: + + using value_type = T; + using reference = T &; + using pointer = T *; + using difference_type = Offset; + using iterator_category = std::random_access_iterator_tag; + + StridedIterator() : _ptr(nullptr), _stride(0) {} + + StridedIterator(Byte * ptr, Offset stride) : _ptr(ptr), _stride(stride) {} + + StridedIterator(StridedIterator const &) noexcept = default; + StridedIterator(StridedIterator &&) noexcept = default; + + StridedIterator & operator=(StridedIterator const &) noexcept = default; + StridedIterator & operator=(StridedIterator &&) noexcept = default; + + ~StridedIterator() noexcept = default; + + void swap(StridedIterator & other) noexcept { + using namespace std; + swap(_ptr, other._ptr); + swap(_stride, other._stride); + } + + friend void swap(StridedIterator & a, StridedIterator & b) noexcept { + a.swap(b); + } + + template + bool operator==(StridedIterator const & rhs) const { return _ptr == rhs.ptr; } + + template + bool operator!=(StridedIterator const & rhs) const { return !(*this == rhs); } + + template + bool operator<(StridedIterator const & rhs) const { + NDARRAY_ASSERT_CHECK(_ptr != nullptr, Error::UNINITIALIZED, "null iterator comparison"); + NDARRAY_ASSERT_CHECK(rhs._ptr != nullptr, Error::UNINITIALIZED, "null iterator comparison"); + return _ptr < rhs._ptr; + } + + template + bool operator>(StridedIterator const & rhs) const { + NDARRAY_ASSERT_CHECK(_ptr != nullptr, Error::UNINITIALIZED, "null iterator comparison"); + NDARRAY_ASSERT_CHECK(rhs._ptr != nullptr, Error::UNINITIALIZED, "null iterator comparison"); + return _ptr > rhs._ptr; + } + + template + bool operator<=(StridedIterator const & rhs) const { + NDARRAY_ASSERT_CHECK(_ptr != nullptr, Error::UNINITIALIZED, "null iterator comparison"); + NDARRAY_ASSERT_CHECK(rhs._ptr != nullptr, Error::UNINITIALIZED, "null iterator comparison"); + return _ptr <= rhs._ptr; + } + + template + bool operator>=(StridedIterator const & rhs) const { + NDARRAY_ASSERT_CHECK(_ptr != nullptr, Error::UNINITIALIZED, "null iterator comparison"); + NDARRAY_ASSERT_CHECK(rhs._ptr != nullptr, Error::UNINITIALIZED, "null iterator comparison"); + return _ptr >= rhs._ptr; + } + + reference operator*() const { + NDARRAY_ASSERT_CHECK(_ptr != nullptr, Error::UNINITIALIZED, "null iterator dereferenced"); + return *reinterpret_cast(_ptr); + } + + pointer operator->() const { + NDARRAY_ASSERT_CHECK(_ptr != nullptr, Error::UNINITIALIZED, "null iterator dereferenced"); + return reinterpret_cast(_ptr); + } + + StridedIterator & operator++() { + NDARRAY_ASSERT_CHECK(_ptr != nullptr, Error::UNINITIALIZED, "null iterator incremented"); + _ptr += _stride; + return *this; + } + + StridedIterator & operator--() { + NDARRAY_ASSERT_CHECK(_ptr != nullptr, Error::UNINITIALIZED, "null iterator decremented"); + _ptr -= _stride; + return *this; + } + + StridedIterator operator++(int) { + StridedIterator copy(*this); + ++(*this); + return copy; + } + + StridedIterator operator--(int) { + StridedIterator copy(*this); + --(*this); + return copy; + } + + StridedIterator & operator+=(difference_type n) { + NDARRAY_ASSERT_CHECK(_ptr != nullptr, Error::UNINITIALIZED, "null iterator advanced"); + _ptr += _stride*n; + return *this; + } + + StridedIterator & operator-=(difference_type n) { + NDARRAY_ASSERT_CHECK(_ptr != nullptr, Error::UNINITIALIZED, "null iterator advanced"); + _ptr -= _stride*n; + return *this; + } + + StridedIterator operator+(difference_type n) const { + return StridedIterator(*this) += n; + } + + StridedIterator operator-(difference_type n) const { + return StridedIterator(*this) -= n; + } + + friend StridedIterator operator+(difference_type n, StridedIterator iter) { + return iter + n; + } + + template + difference_type operator-(StridedIterator const & rhs) const { + NDARRAY_ASSERT_CHECK(_stride == rhs._stride, Error::INCOMPATIBLE_ARGUMENTS, + "iterators have different strides: {:d} != {:d}", _stride, rhs._stride); + NDARRAY_ASSERT_CHECK(_stride != 0, Error::UNINITIALIZED, "iterator stride is zero"); + NDARRAY_ASSERT_CHECK((_ptr - rhs._ptr) % _stride == 0, + Error::INCOMPATIBLE_ARGUMENTS, + "iterator pointer offset ({:d}) is not a multiple of stride ({:d})", + (_ptr - rhs._ptr), _stride); + return (_ptr - rhs._ptr)/_stride; + } + + reference operator[](difference_type n) const { + NDARRAY_ASSERT_CHECK(_ptr != nullptr, Error::UNINITIALIZED, "null iterator dereferenced"); + return *reinterpret_cast(_ptr + n*_stride); + } + +private: + + template friend class StridedIterator; + + Byte * _ptr; + Offset _stride; +}; + +} // ndarray + +#endif // !NDARRAY_StridedIterator_hpp_INCLUDED diff --git a/include/ndarray/Vector.h b/include/ndarray/Vector.h deleted file mode 100644 index 13e6cb07..00000000 --- a/include/ndarray/Vector.h +++ /dev/null @@ -1,816 +0,0 @@ -// -*- c++ -*- -/* - * Copyright (c) 2010-2012, Jim Bosch - * All rights reserved. - * - * ndarray is distributed under a simple BSD-like license; - * see the LICENSE file that should be present in the root - * of the source distribution, or alternately available at: - * https://github.com/ndarray/ndarray - */ - -#ifndef NDARRAY_Vector_h_INCLUDED -#define NDARRAY_Vector_h_INCLUDED - -/// @file ndarray/Vector.h Definition for Vector. - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "ndarray_fwd.h" -#include "ndarray/types.h" - -/// \cond MACROS -#define NDARRAY_MAKE_VECTOR_MAX 8 - -#define NDARRAY_MAKE_VECTOR_ARG_SPEC(Z,I,DATA) T v ## I -#define NDARRAY_MAKE_VECTOR_SET_SPEC(Z,I,DATA) r[I] = v ## I; - -#define NDARRAY_MAKE_VECTOR_SPEC(Z,N,DATA) \ - template \ - inline Vector makeVector( \ - BOOST_PP_ENUM(N,NDARRAY_MAKE_VECTOR_ARG_SPEC,unused) \ - ) { \ - Vector r; \ - BOOST_PP_REPEAT(N,NDARRAY_MAKE_VECTOR_SET_SPEC,unused) \ - return r; \ - } - -/// \endcond - -namespace ndarray { - -namespace detail { - -template ::value> -struct DefaultValue { - static T get() { return T(); } -}; - -template -struct DefaultValue { - static T get() { return T(0); } -}; - -} // namespace detail - -/// \addtogroup ndarrayVectorGroup -/// @{ - -/** - * @class Vector - * @brief A fixed-size 1D array class. - * - * Vector is primarily used as the data type for the shape - * (with T==Size) and strides (with T==Offset) attributes of Array. - * - * Vector is implemented almost exactly as a non-aggregate - * boost::array, but with the addition of mathematical - * operators and a few other utility functions. - */ -template < - typename T, ///< Data type. - int N ///< Number of elements. - > -struct Vector { - - typedef T Element; - typedef T Value; - typedef T & Reference; - typedef T const & ConstReference; - typedef T * Iterator; - typedef T const * ConstIterator; - - typedef Value value_type; - typedef Iterator iterator; - typedef ConstIterator const_iterator; - typedef Reference reference; - typedef ConstReference const_reference; - typedef boost::reverse_iterator reverse_iterator; - typedef boost::reverse_iterator const_reverse_iterator; - typedef T * pointer; - typedef int difference_type; - typedef int size_type; - - - typedef boost::mpl::int_ ND; - /// @brief Return the size of the Vector. - size_type size() const { return N; } - /// @brief Return the size of the Vector. - size_type max_size() const { return N; } - ///< @brief Return true if size() == 0. - bool empty() const { return N==0; } - /// @brief Return an iterator to the beginning of the Vector. - iterator begin() { return elems; } - /// @brief Return a const_iterator to the beginning of the Vector. - const_iterator begin() const { return elems; } - /// @brief Return an iterator to the end of the Vector. - iterator end() { return elems+N; } - /// @brief Return a const_iterator to the end of the Vector. - const_iterator end() const { return elems+N; } - /// @brief Return a reverse_iterator to the beginning of the reversed Vector. - reverse_iterator rbegin() { return reverse_iterator(end()); } - /// @brief Return a const_reverse_iterator to the beginning of the reversed Vector. - const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } - /// @brief Return a reverse_iterator to the end of the reversed Vector. - reverse_iterator rend() { return reverse_iterator(begin()); } - /// @brief Return a const_reverse_iterator to the end of the reversed Vector. - const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } - - /// @brief Return a reference to the first element. - reference front() { return *elems; } - /// @brief Return a reference to the last element. - reference back() { return *(elems+N-1); } - /// @brief Return a const_reference to the first element. - const_reference front() const { return *elems; } - /// @brief Return a const_reference to the last element. - const_reference back() const { return *(elems+N-1); } - - /// @brief Return a reference to the element with the given index. - reference operator[](int i) { return elems[i]; } - /// @brief Return a const_reference to the element with the given index. - const_reference operator[](int i) const { return elems[i]; } - - /// @brief Create a new Vector that is a subset of this. - template - Vector getRange() const { - Vector r; - std::copy(begin() + Start, begin()+Stop, r.begin()); - return r; - } - - /// @brief Create a new Vector from the first M elements of this. - template Vector first() const { - Vector r; - std::copy(begin(), begin() + M, r.begin()); - return r; - } - - /// @brief Create a new Vector from the last M elements of this. - template Vector last() const { - Vector r; - std::copy(begin() + (N - M), begin() + N, r.begin()); - return r; - } - - /** @brief Stream output. */ - friend std::ostream& operator<<(std::ostream& os, Vector const & obj) { - os << "("; - std::copy(obj.begin(), obj.end(), std::ostream_iterator(os,",")); - return os << ")"; - } - - /** - * @brief Default constructor. - * - * Initializes the elements to zero. - */ - Vector() { this-> - #ifndef _MSC_VER - template - #endif - operator=(detail::DefaultValue::get()); } - - /// @brief Construct with copies of a scalar. - explicit Vector(T scalar) { - this->operator=(scalar); - } - - /// @brief Converting copy constructor. - template - Vector(Vector const & other) { - this-> - #ifndef _MSC_VER - template - #endif - operator=(other); - } - - /// @brief Return true if elements of other are equal to the elements of this. - bool operator==(Vector const & other) const { - return std::equal(begin(), end(), other.begin()); - } - - /// @brief Return false if any elements of other are not equal to the elements of this. - bool operator!=(Vector const & other) const { - return !(*this == other); - } - - /// @brief Return the sum of all elements. - T sum() const { - T r = 0; - for (ConstIterator i = begin(); i != end(); ++i) r += (*i); - return r; - } - - /// @brief Return the product of all elements. - T product() const { - T r = 1; - for (ConstIterator i = begin(); i != end(); ++i) r *= (*i); - return r; - } - - /// @brief Return a Vector with the elements reversed. - Vector reverse() const { - Vector r; - std::copy(begin(), end(), r.rbegin()); - return r; - } - - /// @brief Cast the vector element-wise to another type. - template - Vector cast() const { - Vector r; - for (int i = 0; i < N; ++i) { - r[i] = static_cast((*this)[i]); - } - return r; - } - - - /// @brief Augmented = assignment from another vector. - template - typename boost::enable_if,Vector&>::type - operator = (Vector const & other) { - typename Vector::ConstIterator j = other.begin(); - for (Iterator i = begin(); i != end(); ++i, ++j) (*i) = (*j); - return *this; - } - /// @brief Augmented = assignment from a scalar. - template - typename boost::enable_if,Vector&>::type - operator = (U scalar) { - for (Iterator i = begin(); i != end(); ++i) (*i) = scalar; - return *this; - } - - /// @brief Augmented += assignment from another vector. - template - typename boost::enable_if,Vector&>::type - operator += (Vector const & other) { - typename Vector::ConstIterator j = other.begin(); - for (Iterator i = begin(); i != end(); ++i, ++j) (*i) += (*j); - return *this; - } - /// @brief Augmented += assignment from a scalar. - template - typename boost::enable_if,Vector&>::type - operator += (U scalar) { - for (Iterator i = begin(); i != end(); ++i) (*i) += scalar; - return *this; - } - - /// @brief Augmented -= assignment from another vector. - template - typename boost::enable_if,Vector&>::type - operator -= (Vector const & other) { - typename Vector::ConstIterator j = other.begin(); - for (Iterator i = begin(); i != end(); ++i, ++j) (*i) -= (*j); - return *this; - } - /// @brief Augmented -= assignment from a scalar. - template - typename boost::enable_if,Vector&>::type - operator -= (U scalar) { - for (Iterator i = begin(); i != end(); ++i) (*i) -= scalar; - return *this; - } - - /// @brief Augmented *= assignment from another vector. - template - typename boost::enable_if,Vector&>::type - operator *= (Vector const & other) { - typename Vector::ConstIterator j = other.begin(); - for (Iterator i = begin(); i != end(); ++i, ++j) (*i) *= (*j); - return *this; - } - /// @brief Augmented *= assignment from a scalar. - template - typename boost::enable_if,Vector&>::type - operator *= (U scalar) { - for (Iterator i = begin(); i != end(); ++i) (*i) *= scalar; - return *this; - } - - /// @brief Augmented /= assignment from another vector. - template - typename boost::enable_if,Vector&>::type - operator /= (Vector const & other) { - typename Vector::ConstIterator j = other.begin(); - for (Iterator i = begin(); i != end(); ++i, ++j) (*i) /= (*j); - return *this; - } - /// @brief Augmented /= assignment from a scalar. - template - typename boost::enable_if,Vector&>::type - operator /= (U scalar) { - for (Iterator i = begin(); i != end(); ++i) (*i) /= scalar; - return *this; - } - - /// @brief Augmented %= assignment from another vector. - template - typename boost::enable_if,Vector&>::type - operator %= (Vector const & other) { - typename Vector::ConstIterator j = other.begin(); - for (Iterator i = begin(); i != end(); ++i, ++j) (*i) %= (*j); - return *this; - } - /// @brief Augmented %= assignment from a scalar. - template - typename boost::enable_if,Vector&>::type - operator %= (U scalar) { - for (Iterator i = begin(); i != end(); ++i) (*i) %= scalar; - return *this; - } - - /// @brief Augmented &= assignment from another vector. - template - typename boost::enable_if,Vector&>::type - operator &= (Vector const & other) { - typename Vector::ConstIterator j = other.begin(); - for (Iterator i = begin(); i != end(); ++i, ++j) (*i) &= (*j); - return *this; - } - /// @brief Augmented &= assignment from a scalar. - template - typename boost::enable_if,Vector&>::type - operator &= (U scalar) { - for (Iterator i = begin(); i != end(); ++i) (*i) &= scalar; - return *this; - } - - /// @brief Augmented ^= assignment from another vector. - template - typename boost::enable_if,Vector&>::type - operator ^= (Vector const & other) { - typename Vector::ConstIterator j = other.begin(); - for (Iterator i = begin(); i != end(); ++i, ++j) (*i) ^= (*j); - return *this; - } - /// @brief Augmented ^= assignment from a scalar. - template - typename boost::enable_if,Vector&>::type - operator ^= (U scalar) { - for (Iterator i = begin(); i != end(); ++i) (*i) ^= scalar; - return *this; - } - - /// @brief Augmented |= assignment from another vector. - template - typename boost::enable_if,Vector&>::type - operator |= (Vector const & other) { - typename Vector::ConstIterator j = other.begin(); - for (Iterator i = begin(); i != end(); ++i, ++j) (*i) |= (*j); - return *this; - } - /// @brief Augmented |= assignment from a scalar. - template - typename boost::enable_if,Vector&>::type - operator |= (U scalar) { - for (Iterator i = begin(); i != end(); ++i) (*i) |= scalar; - return *this; - } - - /// @brief Augmented <<= assignment from another vector. - template - typename boost::enable_if,Vector&>::type - operator <<= (Vector const & other) { - typename Vector::ConstIterator j = other.begin(); - for (Iterator i = begin(); i != end(); ++i, ++j) (*i) <<= (*j); - return *this; - } - /// @brief Augmented <<= assignment from a scalar. - template - typename boost::enable_if,Vector&>::type - operator <<= (U scalar) { - for (Iterator i = begin(); i != end(); ++i) (*i) <<= scalar; - return *this; - } - - /// @brief Augmented >>= assignment from another vector. - template - typename boost::enable_if,Vector&>::type - operator >>= (Vector const & other) { - typename Vector::ConstIterator j = other.begin(); - for (Iterator i = begin(); i != end(); ++i, ++j) (*i) >>= (*j); - return *this; - } - /// @brief Augmented >>= assignment from a scalar. - template - typename boost::enable_if,Vector&>::type - operator >>= (U scalar) { - for (Iterator i = begin(); i != end(); ++i) (*i) >>= scalar; - return *this; - } - - T elems[N]; -}; - -/// @brief PArtial specialization for zero-size vectors to avoid compiler errors on some platforms. -template -struct Vector { - - typedef T Element; - typedef T Value; - typedef T & Reference; - typedef T const & ConstReference; - typedef T * Iterator; - typedef T const * ConstIterator; - - typedef Value value_type; - typedef Iterator iterator; - typedef ConstIterator const_iterator; - typedef Reference reference; - typedef ConstReference const_reference; - typedef boost::reverse_iterator reverse_iterator; - typedef boost::reverse_iterator const_reverse_iterator; - typedef T * pointer; - typedef int difference_type; - typedef int size_type; - - - typedef boost::mpl::int_<0> ND; - - size_type size() const { return 0; } ///< @brief Return the size of the Vector. - size_type max_size() const { return 0; } ///< @brief Return the size of the Vector. - bool empty() const { return true; } ///< @brief Return true if size() == 0. - /// @brief Return an iterator to the beginning of the Vector. - iterator begin() { return 0; } - /// @brief Return a const_iterator to the beginning of the Vector. - const_iterator begin() const { return 0; } - /// @brief Return an iterator to the end of the Vector. - iterator end() { return 0; } - /// @brief Return a const_iterator to the end of the Vector. - const_iterator end() const { return 0; } - /// @brief Return a reverse_iterator to the beginning of the reversed Vector. - reverse_iterator rbegin() { return reverse_iterator(); } - /// @brief Return a const_reverse_iterator to the beginning of the reversed Vector. - const_reverse_iterator rbegin() const { return const_reverse_iterator(); } - /// @brief Return a reverse_iterator to the end of the reversed Vector. - reverse_iterator rend() { return reverse_iterator(); } - /// @brief Return a const_reverse_iterator to the end of the reversed Vector. - const_reverse_iterator rend() const { return const_reverse_iterator(); } - - /// @brief Return a reference to the first element. - reference front() { NDARRAY_ASSERT(false); return 0; } - /// @brief Return a reference to the last element. - reference back() { return NDARRAY_ASSERT(false); return 0; } - /// @brief Return a const_reference to the first element. - const_reference front() const { NDARRAY_ASSERT(false); return 0; } - /// @brief Return a const_reference to the last element. - const_reference back() const { NDARRAY_ASSERT(false); return 0; } - - /// @brief Return a reference to the element with the given index. - reference operator[](int i) { NDARRAY_ASSERT(false); return 0; } - /// @brief Return a const_reference to the element with the given index. - const_reference operator[](int i) const { NDARRAY_ASSERT(false); return 0; } - - /// @brief Create a new Vector that is a subset of this. - template - Vector getRange() const { - return Vector(); - } - - /// @brief Create a new Vector from the first M elements of this. - template Vector first() const { - return Vector(); - } - - /// @brief Create a new Vector from the last M elements of this. - template Vector last() const { - return Vector(); - } - - /** @brief Stream output. */ - friend std::ostream& operator<<(std::ostream& os, Vector const & obj) { - return os << "()"; - } - - /** - * @brief Default constructor. - * - * Initializes the elements to zero. - */ - Vector() {} - - /// @brief Construct with copies of a scalar. - explicit Vector(T scalar) {} - - /// @brief Converting copy constructor. - template - Vector(Vector const & other) {} - - /// @brief Return true if elements of other are equal to the elements of this. - bool operator==(Vector const & other) const { return true; } - - /// @brief Return false if any elements of other are not equal to the elements of this. - bool operator!=(Vector const & other) const { return false; } - - /// @brief Return the sum of all elements. - T sum() const { return 0; } - - /// @brief Return the product of all elements. - T product() const { return 1; } - - /// @brief Return a Vector with the elements reversed. - Vector reverse() const { return Vector(); } - - /// @brief Cast the vector element-wise to another type. - template - Vector cast() const { return Vector(); } - -}; - - -/// @brief Concatenate two Vectors into a single long Vector. -template -inline Vector concatenate(Vector const & a, Vector const & b) { - Vector r; - std::copy(a.begin(),a.end(),r.begin()); - std::copy(b.begin(),b.end(),r.begin()+N); - return r; -} - -/// @brief Return a new Vector with the given scalar appended to the original. -template -inline typename boost::enable_if,Vector >::type -concatenate(Vector const & a, U b) { - Vector r; - std::copy(a.begin(),a.end(),r.begin()); - r[N] = b; - return r; -} - -/// @brief Return a new Vector with the given scalar prepended to the original. -template -inline typename boost::enable_if,Vector >::type -concatenate(U a, Vector const & b) { - Vector r; - r[0] = a; - std::copy(b.begin(),b.end(),r.begin()+1); - return r; -} - -#ifndef DOXYGEN -BOOST_PP_REPEAT_FROM_TO(1, NDARRAY_MAKE_VECTOR_MAX, NDARRAY_MAKE_VECTOR_SPEC, unused) -#else -/** - * @brief Variadic constructor for Vector. - * - * Defined for N in [0 - NDARRAY_MAKE_VECTOR_MAX). - */ -template -Vector makeVector(T v1, T v2, ..., T vN); -#endif - -/** @brief Unary bitwise NOT for Vector. */ -template -inline Vector operator~(Vector const & vector) { - Vector r(vector); - for (typename Vector::Iterator i = r.begin(); i != r.end(); ++i) (*i) = ~(*i); - return r; -} - -/** @brief Unary negation for Vector. */ -template -inline Vector operator!(Vector const & vector) { - Vector r(vector); - for (typename Vector::Iterator i = r.begin(); i != r.end(); ++i) (*i) = !(*i); - return r; -} - - - /// @brief Operator overload for Vector + Vector. - template - Vector::Type,N> - operator +(Vector const & a, Vector const & b) { - Vector::Type,N> r(a); - return r += b; - } - /** @brief Operator overload for Vector + Scalar. */ - template - Vector::Type,N> - operator +(Vector const & a, U b) { - Vector::Type,N> r(a); - return r += b; - } - /** @brief Operator overload for Scalar + Vector. */ - template - Vector::Type,N> - operator +(U a, Vector const & b) { - Vector::Type,N> r(a); - return r += b; - } - - /// @brief Operator overload for Vector - Vector. - template - Vector::Type,N> - operator -(Vector const & a, Vector const & b) { - Vector::Type,N> r(a); - return r -= b; - } - /** @brief Operator overload for Vector - Scalar. */ - template - Vector::Type,N> - operator -(Vector const & a, U b) { - Vector::Type,N> r(a); - return r -= b; - } - /** @brief Operator overload for Scalar - Vector. */ - template - Vector::Type,N> - operator -(U a, Vector const & b) { - Vector::Type,N> r(a); - return r -= b; - } - - /// @brief Operator overload for Vector * Vector. - template - Vector::Type,N> - operator *(Vector const & a, Vector const & b) { - Vector::Type,N> r(a); - return r *= b; - } - /** @brief Operator overload for Vector * Scalar. */ - template - Vector::Type,N> - operator *(Vector const & a, U b) { - Vector::Type,N> r(a); - return r *= b; - } - /** @brief Operator overload for Scalar * Vector. */ - template - Vector::Type,N> - operator *(U a, Vector const & b) { - Vector::Type,N> r(a); - return r *= b; - } - - /// @brief Operator overload for Vector / Vector. - template - Vector::Type,N> - operator /(Vector const & a, Vector const & b) { - Vector::Type,N> r(a); - return r /= b; - } - /** @brief Operator overload for Vector / Scalar. */ - template - Vector::Type,N> - operator /(Vector const & a, U b) { - Vector::Type,N> r(a); - return r /= b; - } - /** @brief Operator overload for Scalar / Vector. */ - template - Vector::Type,N> - operator /(U a, Vector const & b) { - Vector::Type,N> r(a); - return r /= b; - } - - /// @brief Operator overload for Vector % Vector. - template - Vector::Type,N> - operator %(Vector const & a, Vector const & b) { - Vector::Type,N> r(a); - return r %= b; - } - /** @brief Operator overload for Vector % Scalar. */ - template - Vector::Type,N> - operator %(Vector const & a, U b) { - Vector::Type,N> r(a); - return r %= b; - } - /** @brief Operator overload for Scalar % Vector. */ - template - Vector::Type,N> - operator %(U a, Vector const & b) { - Vector::Type,N> r(a); - return r %= b; - } - - /// @brief Operator overload for Vector & Vector. - template - Vector::Type,N> - operator &(Vector const & a, Vector const & b) { - Vector::Type,N> r(a); - return r &= b; - } - /** @brief Operator overload for Vector & Scalar. */ - template - Vector::Type,N> - operator &(Vector const & a, U b) { - Vector::Type,N> r(a); - return r &= b; - } - /** @brief Operator overload for Scalar & Vector. */ - template - Vector::Type,N> - operator &(U a, Vector const & b) { - Vector::Type,N> r(a); - return r &= b; - } - - /// @brief Operator overload for Vector ^ Vector. - template - Vector::Type,N> - operator ^(Vector const & a, Vector const & b) { - Vector::Type,N> r(a); - return r ^= b; - } - /** @brief Operator overload for Vector ^ Scalar. */ - template - Vector::Type,N> - operator ^(Vector const & a, U b) { - Vector::Type,N> r(a); - return r ^= b; - } - /** @brief Operator overload for Scalar ^ Vector. */ - template - Vector::Type,N> - operator ^(U a, Vector const & b) { - Vector::Type,N> r(a); - return r ^= b; - } - - /// @brief Operator overload for Vector | Vector. - template - Vector::Type,N> - operator |(Vector const & a, Vector const & b) { - Vector::Type,N> r(a); - return r |= b; - } - /** @brief Operator overload for Vector | Scalar. */ - template - Vector::Type,N> - operator |(Vector const & a, U b) { - Vector::Type,N> r(a); - return r |= b; - } - /** @brief Operator overload for Scalar | Vector. */ - template - Vector::Type,N> - operator |(U a, Vector const & b) { - Vector::Type,N> r(a); - return r |= b; - } - - /// @brief Operator overload for Vector << Vector. - template - Vector::Type,N> - operator <<(Vector const & a, Vector const & b) { - Vector::Type,N> r(a); - return r <<= b; - } - /** @brief Operator overload for Vector << Scalar. */ - template - Vector::Type,N> - operator <<(Vector const & a, U b) { - Vector::Type,N> r(a); - return r <<= b; - } - /** @brief Operator overload for Scalar << Vector. */ - template - Vector::Type,N> - operator <<(U a, Vector const & b) { - Vector::Type,N> r(a); - return r <<= b; - } - - /// @brief Operator overload for Vector >> Vector. - template - Vector::Type,N> - operator >>(Vector const & a, Vector const & b) { - Vector::Type,N> r(a); - return r >>= b; - } - /** @brief Operator overload for Vector >> Scalar. */ - template - Vector::Type,N> - operator >>(Vector const & a, U b) { - Vector::Type,N> r(a); - return r >>= b; - } - /** @brief Operator overload for Scalar >> Vector. */ - template - Vector::Type,N> - operator >>(U a, Vector const & b) { - Vector::Type,N> r(a); - return r >>= b; - } - -/// @} - -} // namespace ndarray - -#endif // !NDARRAY_Vector_h_INCLUDED diff --git a/include/ndarray/arange.h b/include/ndarray/arange.h deleted file mode 100644 index 593beaed..00000000 --- a/include/ndarray/arange.h +++ /dev/null @@ -1,120 +0,0 @@ -// -*- c++ -*- -/* - * Copyright (c) 2010-2012, Jim Bosch - * All rights reserved. - * - * ndarray is distributed under a simple BSD-like license; - * see the LICENSE file that should be present in the root - * of the source distribution, or alternately available at: - * https://github.com/ndarray/ndarray - */ -#ifndef NDARRAY_arange_h_INCLUDED -#define NDARRAY_arange_h_INCLUDED - -/** - * @file ndarray/arange.h - * - * @brief Expression classes to generate regularly-spaced ranges of values. - */ - -#include "ndarray/vectorize.h" - -#include - -namespace ndarray { - -/** - * @internal @brief ExpressionTraits specialization for CountingExpression. - * - * @ingroup ndarrayInternalGroup - */ -template <> -struct ExpressionTraits { - typedef int Element; - typedef boost::mpl::int_<1> ND; - typedef boost::counting_iterator Iterator; - typedef int Value; - typedef int Reference; -}; - -namespace detail { - -/** - * @internal @class CountingExpression - * @brief Expression that simply iterates over integer values. - * - * @ingroup ndarrayInternalGroup - */ -class CountingExpression : public ExpressionBase { -public: - typedef ExpressionTraits::Element Element; - typedef ExpressionTraits::ND ND; - typedef ExpressionTraits::Iterator Iterator; - typedef ExpressionTraits::Value Value; - typedef ExpressionTraits::Reference Reference; - typedef Vector Index; - - CountingExpression(int stop=0) : _stop(stop) { NDARRAY_ASSERT(stop >= 0); } - - Reference operator[](int n) const { - return n; - } - - Iterator begin() const { - return Iterator(0); - } - - Iterator end() const { - return Iterator(_stop); - } - - template int getSize() const { - BOOST_STATIC_ASSERT(P==0); - return _stop; - } - - Index getShape() const { - return makeVector(_stop); - } - -private: - int _stop; -}; - -template -class RangeTransformer { - T _offset; - T _scale; -public: - typedef int argument_type; - typedef T result_type; - - explicit RangeTransformer(T const & offset, T const & scale) : _offset(offset), _scale(scale) {} - - T operator()(int n) const { return static_cast(n) * _scale + _offset; } -}; - -} // namespace detail - -/// @brief Create 1D Expression that contains integer values in the range [0,stop). -inline detail::CountingExpression arange(int stop) { - return detail::CountingExpression(stop); -} - -/// @brief Create 1D Expression that contains integer values in the range [start,stop) with increment step. -inline detail::UnaryOpExpression< detail::CountingExpression, detail::RangeTransformer > -arange(int start, int stop, int step = 1) { - NDARRAY_ASSERT(step != 0); - int size = stop - start; - if (step < -1) ++size; - if (step > 1) --size; - size /= step; - return vectorize( - detail::RangeTransformer(start,step), - detail::CountingExpression(size) - ); -} - -} // namespace ndarray - -#endif // !NDARRAY_arange_h_INCLUDED diff --git a/include/ndarray/casts.h b/include/ndarray/casts.h deleted file mode 100644 index d5d1e56e..00000000 --- a/include/ndarray/casts.h +++ /dev/null @@ -1,167 +0,0 @@ -// -*- c++ -*- -/* - * Copyright (c) 2010-2012, Jim Bosch - * All rights reserved. - * - * ndarray is distributed under a simple BSD-like license; - * see the LICENSE file that should be present in the root - * of the source distribution, or alternately available at: - * https://github.com/ndarray/ndarray - */ -#ifndef NDARRAY_casts_h_INCLUDED -#define NDARRAY_casts_h_INCLUDED - -/** - * @file ndarray/casts.h - * - * @brief Specialized casts for Array. - */ - -#include "ndarray/Array.h" -#include "ndarray/ArrayRef.h" -#include -#include -#include -#include -#include - -namespace ndarray { -namespace detail { - -template -struct ComplexExtractor { - typedef typename ExpressionTraits::Element ComplexElement; - typedef typename boost::remove_const::type ComplexValue; - typedef typename ExpressionTraits::ND ND; - BOOST_STATIC_ASSERT( boost::is_complex::value ); - typedef typename ComplexValue::value_type RealValue; - typedef typename boost::mpl::if_< - boost::is_const, RealValue const, RealValue - >::type RealElement; - typedef ArrayRef Result; - typedef detail::ArrayAccess Access; - typedef Vector Index; - - static inline Result apply(Array_ const & array, Offset offset) { - return Access::construct( - reinterpret_cast(array.getData()) + offset, - Access::Core::create(array.getShape(), array.getStrides() * 2, array.getManager()) - ); - } -}; - -} // namespace detail - -/// @addtogroup MainGroup -/// @{ - -/** - * Convert an Array with a const data type to an array - * with a non-const data type. - */ -template -Array -const_array_cast(Array const & array) { - return detail::ArrayAccess< Array >::construct( - const_cast(array.getData()), - detail::ArrayAccess< Array >::getCore(array) - ); -} - -/** - * Convert an Array to a type with more guaranteed - * row-major-contiguous dimensions with no checking. - */ -template -Array -static_dimension_cast(Array const & array) { - return detail::ArrayAccess< Array >::construct( - array.getData(), - detail::ArrayAccess< Array >::getCore(array) - ); -} - -/** - * Convert an Array to a type with more guaranteed - * row-major-contiguous dimensions, if the strides - * of the array match the desired number of RMC - * dimensions. If the cast fails, an empty Array - * is returned. - */ -template -Array -dynamic_dimension_cast(Array const & array) { - Vector shape = array.getShape(); - Vector strides = array.getStrides(); - if (C_ >= 0) { - Offset n = 1; - for (int i=1; i <= C_; ++i) { - if (strides[N-i] != n) return Array(); - n *= shape[N-i]; - } - } else { - Offset n = 1; - for (int i=0; i < -C_; ++i) { - if (strides[i] != n) return Array(); - n *= strides[i]; - } - } - return static_dimension_cast(array); -} - -/** - * @brief Return an ArrayRef view into the real part of a complex array. - */ -template -typename detail::ComplexExtractor::Result -getReal(Array_ const & array) { - return detail::ComplexExtractor::apply(array, 0); -} - -/** - * @brief Return an ArrayRef view into the imaginary part of a complex array. - */ -template -typename detail::ComplexExtractor::Result -getImag(Array_ const & array) { - return detail::ComplexExtractor::apply(array, 1); -} - -/** - * @brief Create a view into an array with trailing contiguous dimensions merged. - * - * The first template parameter sets the dimension of the output array and must - * be specified directly. Only row-major contiguous dimensions can be flattened. - */ -template -inline typename boost::enable_if_c< ((C+Nf-N)>=1), ArrayRef >::type -flatten(Array const & input) { - typedef detail::ArrayAccess< ArrayRef > Access; - typedef typename Access::Core Core; - BOOST_STATIC_ASSERT(C+Nf-N >= 1); - Vector oldShape = input.getShape(); - Vector newShape = oldShape.template first(); - for (int n=Nf; n newStrides = input.getStrides().template first(); - newStrides[Nf-1] = 1; - return Access::construct(input.getData(), Core::create(newShape, newStrides, input.getManager())); -} - -/** - * @brief Create a view into an array with trailing contiguous dimensions merged. - * - * The first template parameter sets the dimension of the output array and must - * be specified directly. Only row-major contiguous dimensions can be flattened. - */ -template -inline typename boost::enable_if_c< ((C+Nf-N)>=1), ArrayRef >::type -flatten(ArrayRef const & input) { - return flatten(input.shallow()); -} - -/// @} - -} // namespace ndarray - -#endif // !NDARRAY_casts_h_INCLUDED diff --git a/include/ndarray/common.hpp b/include/ndarray/common.hpp new file mode 100644 index 00000000..b92e2657 --- /dev/null +++ b/include/ndarray/common.hpp @@ -0,0 +1,51 @@ +// -*- c++ -*- +/* + * Copyright (c) 2010-2018, Jim Bosch + * All rights reserved. + * + * ndarray is distributed under a simple BSD-like license; + * see the LICENSE file that should be present in the root + * of the source distribution, or alternately available at: + * https://github.com/ndarray/ndarray + */ +#ifndef NDARRAY_common_hpp_INCLUDED +#define NDARRAY_common_hpp_INCLUDED + +#include + +namespace ndarray { + +typedef std::uint8_t Byte; +typedef std::size_t Size; +typedef std::ptrdiff_t Offset; + +enum class MemoryOrder { + ROW_MAJOR, + COL_MAJOR +}; + +template class Array; +template class ArrayInterfaceN; + +template class StridedIterator; +template class NestedIterator; + +template class Deref; + +namespace detail { + +template class Layout; + +template struct ArrayImpl; + +template class OrderTag {}; +using RowMajorTag = OrderTag; +using ColMajorTag = OrderTag; + +template class TypeTag {}; + +} // namespace detail + +} // ndarray + +#endif // !NDARRAY_common_hpp_INCLUDED diff --git a/include/ndarray/detail/ArrayAccess.h b/include/ndarray/detail/ArrayAccess.h deleted file mode 100644 index f62d32f6..00000000 --- a/include/ndarray/detail/ArrayAccess.h +++ /dev/null @@ -1,45 +0,0 @@ -// -*- c++ -*- -/* - * Copyright (c) 2010-2012, Jim Bosch - * All rights reserved. - * - * ndarray is distributed under a simple BSD-like license; - * see the LICENSE file that should be present in the root - * of the source distribution, or alternately available at: - * https://github.com/ndarray/ndarray - */ -#ifndef NDARRAY_DETAIL_ArrayAccess_h_INCLUDED -#define NDARRAY_DETAIL_ArrayAccess_h_INCLUDED - -/** - * @file ndarray/detail/ArrayAccess.h - * - * @brief Definitions for ArrayAccess - */ - -#include "ndarray/ExpressionTraits.h" - -namespace ndarray { -namespace detail { - -template -class ArrayAccess { -public: - typedef typename ExpressionTraits< Array_ >::Element Element; - typedef typename ExpressionTraits< Array_ >::Core Core; - typedef typename ExpressionTraits< Array_ >::CorePtr CorePtr; - - static CorePtr const & getCore(Array_ const & array) { - return array._core; - } - - static Array_ construct(Element * data, CorePtr const & core) { - return Array_(data, core); - } - -}; - -} // namespace detail -} // namespace ndarray - -#endif // !NDARRAY_DETAIL_ArrayAccess_h_INCLUDED diff --git a/include/ndarray/detail/ArrayImpl.hpp b/include/ndarray/detail/ArrayImpl.hpp new file mode 100644 index 00000000..f4913416 --- /dev/null +++ b/include/ndarray/detail/ArrayImpl.hpp @@ -0,0 +1,120 @@ +// -*- c++ -*- +/* + * Copyright (c) 2010-2016, Jim Bosch + * All rights reserved. + * + * ndarray is distributed under a simple BSD-like license; + * see the LICENSE file that should be present in the root + * of the source distribution, or alternately available at: + * https://github.com/ndarray/ndarray + */ +#ifndef NDARRAY_detail_ArrayImpl_hpp_INCLUDED +#define NDARRAY_detail_ArrayImpl_hpp_INCLUDED + +#include + +#include "ndarray/common.hpp" +#include "ndarray/detail/Layout.hpp" + +namespace ndarray { +namespace detail { + +template +struct ArrayImpl { + + constexpr static Size N = N_; + + ArrayImpl() noexcept = default; + + template + ArrayImpl( + ShapeVector const & shape, + MemoryOrder order, + TypeTag + ) : + buffer(nullptr), + layout(Layout::make(shape, sizeof(Element), order)) + { + std::shared_ptr tmp(new Element[layout->full_size()], std::default_delete()); + buffer = std::shared_ptr(tmp, reinterpret_cast(tmp.get())); + } + + template + ArrayImpl( + std::shared_ptr data, + ShapeVector const & shape, + MemoryOrder order + ) : + buffer(std::move(data), reinterpret_cast(data.get())), + layout(Layout::make(shape, sizeof(Element), order)) + {} + + template + ArrayImpl( + std::shared_ptr data, + ShapeVector const & shape, + StridesVector const & strides + ) : + buffer(std::move(data), reinterpret_cast(data.get())), + layout(Layout::make(shape, strides)) + {} + + ArrayImpl( + std::shared_ptr buffer_, + std::shared_ptr const> layout_ + ) noexcept : + buffer(std::move(buffer_)), + layout(std::move(layout_)) + {} + + ArrayImpl(ArrayImpl const &) noexcept = default; + ArrayImpl(ArrayImpl &&) noexcept = default; + + ArrayImpl & operator=(ArrayImpl const &) noexcept = default; + ArrayImpl & operator=(ArrayImpl &&) noexcept = default; + + ~ArrayImpl() noexcept = default; + + template + Byte * index(IndexVector const & indices) const { + IndexVectorTraits::template check_dims(indices); + Size n = 0; + Offset offset = 0; + layout->for_each_dim( + [&indices, &n, &offset](auto const & d) { + assert(IndexVectorTraits::get_size(indices, n) < d.size()); + offset += IndexVectorTraits::get_size(indices, n)*d.stride(); + ++n; + } + ); + return buffer.get() + offset; + } + + void swap(ArrayImpl & other) noexcept { + buffer.swap(other); + layout.swap(other.layout); + } + + bool operator==(ArrayImpl const & other) const noexcept { + return buffer == other.buffer && + (layout == other.layout + || (layout && other.layout && *layout == *other.layout)); + } + + bool operator!=(ArrayImpl const & other) const noexcept { + return !(*this == other); + } + + std::shared_ptr buffer; + std::shared_ptr const> layout; +}; + +template +void swap(ArrayImpl & a, ArrayImpl & b) noexcept { + a.swap(b); +} + +} // namespace detail +} // namespace ndarray + +#endif // !NDARRAY_detail_ArrayImpl_hpp_INCLUDED diff --git a/include/ndarray/detail/BinaryOp.h b/include/ndarray/detail/BinaryOp.h deleted file mode 100644 index e51beece..00000000 --- a/include/ndarray/detail/BinaryOp.h +++ /dev/null @@ -1,138 +0,0 @@ -// -*- c++ -*- -/* - * Copyright (c) 2010-2012, Jim Bosch - * All rights reserved. - * - * ndarray is distributed under a simple BSD-like license; - * see the LICENSE file that should be present in the root - * of the source distribution, or alternately available at: - * https://github.com/ndarray/ndarray - */ -#ifndef NDARRAY_DETAIL_BinaryOp_h_INCLUDED -#define NDARRAY_DETAIL_BinaryOp_h_INCLUDED - -/** - * @file ndarray/detail/BinaryOp.h - * - * @brief Lazy binary expression templates. - */ - -#include "ndarray/ExpressionBase.h" -#include "ndarray/vectorize.h" -#include -#include -#include - -namespace ndarray { -namespace detail { - -/** - * @internal @brief An iterator for binary expression templates. - * - * @ingroup ndarrayInternalGroup - * - * Acts as a combination "zip" and "transform" iterator. - */ -template -class BinaryOpIterator : public boost::iterator_adaptor< - BinaryOpIterator, - boost::zip_iterator< - boost::tuple< - typename ExpressionTraits::Iterator, - typename ExpressionTraits::Iterator - > - >, - typename ExpressionTraits< BinaryOpExpression >::Value, - boost::use_default, - typename ExpressionTraits< BinaryOpExpression >::Reference - > { - typedef BinaryOpExpression Operation; -public: - typedef typename ExpressionTraits::Iterator BaseIterator1; - typedef typename ExpressionTraits::Iterator BaseIterator2; - typedef typename ExpressionTraits::Value Value; - typedef typename ExpressionTraits::Reference Reference; - - BinaryOpIterator() : BinaryOpIterator::iterator_adaptor_(), _functor() {} - - BinaryOpIterator( - BaseIterator1 const & baseIter1, - BaseIterator2 const & baseIter2, - BinaryFunction const & functor - ) : - BinaryOpIterator::iterator_adaptor_(boost::make_tuple(baseIter1,baseIter2)), - _functor(functor) {} - - BinaryOpIterator(BinaryOpIterator const & other) : - BinaryOpIterator::iterator_adaptor_(other), _functor(other._functor) {} - -private: - friend class boost::iterator_core_access; - - Reference dereference() const { - return vectorize( - _functor, - this->base_reference()->template get<0>(), - this->base_reference()->template get<1>() - ); - } - - BinaryFunction _functor; -}; - -/** - * @internal @brief A binary expression template. - * - * @ingroup ndarrayInternalGroup - * - * Represents the lazy evaluation of a binary expression. - */ -template -class BinaryOpExpression : public ExpressionBase< BinaryOpExpression > { - typedef BinaryOpExpression Self; -public: - typedef typename ExpressionTraits::Element Element; - typedef typename ExpressionTraits::ND ND; - typedef typename ExpressionTraits::Iterator Iterator; - typedef typename ExpressionTraits::Value Value; - typedef typename ExpressionTraits::Reference Reference; - typedef Vector Index; - - BinaryOpExpression( - Operand1 const & operand1, - Operand2 const & operand2, - BinaryFunction const & functor - ) : - _operand1(operand1), _operand2(operand2), _functor(functor) { - NDARRAY_ASSERT(_operand1.getShape() == _operand2.getShape()); - } - - Reference operator[](Size n) const { - return Reference(_operand1[n],_operand2[n],_functor); - } - - Iterator begin() const { - return Iterator(_operand1.begin(),_operand2.begin(),_functor); - } - - Iterator end() const { - return Iterator(_operand1.end(),_operand2.end(),_functor); - } - - template Size getSize() const { - return _operand1.template getSize

(); - } - - Index getShape() const { - return _operand1.getShape(); - } - - Operand1 _operand1; - Operand2 _operand2; - BinaryFunction _functor; -}; - -} // namespace detail -} // namespace ndarray - -#endif // !NDARRAY_DETAIL_BinaryOp_h_INCLUDED diff --git a/include/ndarray/detail/Core.h b/include/ndarray/detail/Core.h deleted file mode 100644 index dd662073..00000000 --- a/include/ndarray/detail/Core.h +++ /dev/null @@ -1,274 +0,0 @@ -// -*- c++ -*- -/* - * Copyright (c) 2010-2012, Jim Bosch - * All rights reserved. - * - * ndarray is distributed under a simple BSD-like license; - * see the LICENSE file that should be present in the root - * of the source distribution, or alternately available at: - * https://github.com/ndarray/ndarray - */ -#ifndef NDARRAY_DETAIL_Core_h_INCLUDED -#define NDARRAY_DETAIL_Core_h_INCLUDED - -/** - * @file ndarray/detail/Core.h - * - * @brief Definitions for Core. - */ - -#include -#include -#include "ndarray/Vector.h" -#include "ndarray/Manager.h" - -namespace ndarray { -namespace detail { - -/** - * @internal - * @brief Internal data class for Array. - * - * @ingroup ndarrayInternalGroup - * - * Core holds the shape, stride, and ownership data for an Array. - * A Core maintains its own reference count and can be shared - * by multiple Arrays via a const boost::intrusive pointer. - * - * Because a Core with N dimensions inherits from - * a Core with N-1 dimensions, subarrays can share a Core with - * their parants. - * - * Core objects are never const; even an Array with a const - * template parameter holds a Core with a non-const template - * parameter. - */ -template -class Core : public Core { -public: - typedef boost::mpl::int_ ND; ///< number of dimensions - typedef Core Super; ///< base class - typedef boost::intrusive_ptr Ptr; ///< intrusive_ptr to Core - typedef boost::intrusive_ptr ConstPtr; ///< const intrusive_ptr to Core - - /// @brief Create a Core::Ptr with the given shape, strides, and manager. - template - static Ptr create( - Vector const & shape, - Vector const & strides, - Manager::Ptr const & manager = Manager::Ptr() - ) { - return Ptr(new Core(shape, strides, manager), false); - } - - /// @brief Create a Core::Ptr with the given shape and manager with contiguous strides. - template - static Ptr create( - Vector const & shape, - DataOrderEnum order, - Manager::Ptr const & manager = Manager::Ptr() - ) { - if (order == ROW_MAJOR) { - return Ptr(new Core(shape, manager), false); - } else { - return Ptr(new Core(shape, 1, manager), false); - } - } - - /// @brief Create a Core::Ptr with the given manager and zero shape and strides. - static Ptr create( - Manager::Ptr const & manager = Manager::Ptr() - ) { - return Ptr(new Core(manager), false); - } - - Ptr copy() const { return Ptr(new Core(*this)); } - - /// @brief Return the size of the Nth dimension. - Size getSize() const { return _size; } - - /// @brief Return the stride of the Nth dimension. - Offset getStride() const { return _stride; } - - /// @brief Set the size of the Nth dimension. - void setSize(Size size) { _size = size; } - - /// @brief Set the stride of the Nth dimension. - void setStride(Offset stride) { _stride = stride; } - - /// @brief Recursively compute the offset to an element. - template - Offset computeOffset(Vector const & index) const { - return index[M-N] * this->getStride() + Super::computeOffset(index); - } - - /// @brief Recursively fill a shape vector. - template - void fillShape(Vector & shape) const { - shape[M-N] = this->getSize(); - Super::fillShape(shape); - } - - /// @brief Recursively fill a strides vector. - template - void fillStrides(Vector & strides) const { - strides[M-N] = this->getStride(); - Super::fillStrides(strides); - } - - /// @brief Recursively determine the total number of elements. - Size getNumElements() const { - return getSize() * Super::getNumElements(); - } - -protected: - - // Explicit strides - template - Core ( - Vector const & shape, - Vector const & strides, - Manager::Ptr const & manager - ) : Super(shape, strides, manager), _size(shape[M-N]), _stride(strides[M-N]) {} - - // Row-major strides - template - Core ( - Vector const & shape, - Manager::Ptr const & manager - ) : Super(shape, manager), _size(shape[M-N]), _stride(Super::getStride() * Super::getSize()) {} - - // Column-major strides - template - Core ( - Vector const & shape, - Offset stride, - Manager::Ptr const & manager - ) : Super(shape, stride * shape[M-N], manager), _size(shape[M-N]), _stride(stride) {} - - // Zero shape and strides - Core ( - Manager::Ptr const & manager - ) : Super(manager), _size(0), _stride(0) {} - - Core(Core const & other) : Super(other), _size(other._size), _stride(other._stride) {} - -private: - Size _size; - Offset _stride; -}; - -/** - * @internal - * @brief Internal data class for Array, 0-D specialization. - * - * @ingroup ndarrayInternalGroup - * - * The 0-D Core has size and stride == 1 and holds the reference - * count and manager; it is the base class for all other Cores. - */ -template <> -class Core<0> { -public: - typedef boost::mpl::int_<0> ND; - typedef boost::intrusive_ptr Ptr; - typedef boost::intrusive_ptr ConstPtr; - - friend inline void intrusive_ptr_add_ref(Core const * core) { - ++core->_rc; - } - - friend inline void intrusive_ptr_release(Core const * core) { - if ((--core->_rc)==0) delete core; - } - - Ptr copy() const { return Ptr(new Core(*this)); } - - Size getSize() const { return 1; } - Offset getStride() const { return 1; } - - /// @brief Recursively compute the offset to an element. - template - Offset computeOffset(Vector const & index) const { return 0; } - - /// @brief Return the Manager that determines the lifetime of the array data. - Manager::Ptr getManager() const { return _manager; } - - /// @brief Set the Manager that determines the lifetime of the array data. - void setManager(Manager::Ptr const & manager) { _manager = manager; } - - /// @brief Recursively fill a shape vector. - template - void fillShape(Vector const & shape) const {} - - /// @brief Recursively fill a strides vector. - template - void fillStrides(Vector const & strides) const {} - - /// @brief Recursively determine the total number of elements. - Size getNumElements() const { return 1; } - - /// @brief Return the reference count (for debugging purposes). - int getRC() const { return _rc; } - - /// @brief Return true if the Core and Manager reference counts are 1 and the manager is unique. - bool isUnique() const { return (_rc == 1) && (_manager->getRC() == 1) && _manager->isUnique(); } - -protected: - - virtual ~Core() {} - - template - Core( - Vector const & shape, - Vector const & strides, - Manager::Ptr const & manager - ) : _manager(manager), _rc(1) {} - - template - Core( - Vector const & shape, - Manager::Ptr const & manager - ) : _manager(manager), _rc(1) {} - - template - Core( - Vector const & shape, - Offset stride, - Manager::Ptr const & manager - ) : _manager(manager), _rc(1) {} - - Core( - Manager::Ptr const & manager - ) : _manager(manager), _rc(1) {} - - Core(Core const & other) : _manager(other._manager), _rc(1) {} - -private: - Manager::Ptr _manager; - mutable int _rc; -}; - - -/** - * @internal @brief Cast a Core reference to a particular dimension. - * - * @ingroup ndarrayInternalGroup - */ -template -inline Core const & -getDimension(Core const & core) { return core; } - -/** - * @internal @brief Cast a Core smart pointer to a particular dimension. - * - * @ingroup ndarrayInternalGroup - */ -template -inline typename Core::Ptr -getDimension(typename Core::Ptr const & core) { return core; } - -} // namespace detail -} // namespace ndarray - -#endif // !NDARRAY_DETAIL_Core_h_INCLUDED diff --git a/include/ndarray/detail/Layout.hpp b/include/ndarray/detail/Layout.hpp new file mode 100644 index 00000000..c5ff0185 --- /dev/null +++ b/include/ndarray/detail/Layout.hpp @@ -0,0 +1,343 @@ +// -*- c++ -*- +/* + * Copyright (c) 2010-2018, Jim Bosch + * All rights reserved. + * + * ndarray is distributed under a simple BSD-like license; + * see the LICENSE file that should be present in the root + * of the source distribution, or alternately available at: + * https://github.com/ndarray/ndarray + */ +#ifndef NDARRAY_detail_Layout_hpp_INCLUDED +#define NDARRAY_detail_Layout_hpp_INCLUDED + +#include + +#include "ndarray/common.hpp" +#include "ndarray/errors.hpp" +#include "ndarray/IndexVectorTraits.hpp" + +namespace ndarray { +namespace detail { + +template <> class Layout<0>; + +// Storage for the shape and strides of an Array. +// +// Layout is a recursive container: Layout inherits from Layout, with +// 0-d specialized to break the recursion. Specializing 1-d would also have +// worked, but would require a bit more code duplication. +// +// Layouts are always held by shared_ptr. +// +// The dimensions of Layout are reversed relative to those of an Array: +// for a 3-d Array, the zeroth (outermost) Array dimension is held in a +// Layout<3>, while the last (innermost) dimension is held in a Layout<1>. +// The get_dim free functions can be used to obtain the Layout specialization +// for a particular dimension. +template +class Layout : public Layout { +public: + + using Base = Layout; + + // Public factory function from explicit shape and strides. + template + static std::shared_ptr make(ShapeVector const & shape, StridesVector const & strides) { + return std::make_shared(shape, strides); + } + + // Public factory function with explicit shape and automatic strides. + template + static std::shared_ptr make(ShapeVector const & shape, Size element_size, MemoryOrder order) { + switch (order) { + case MemoryOrder::ROW_MAJOR: + return std::make_shared(shape, element_size, RowMajorTag()); + case MemoryOrder::COL_MAJOR: + return std::make_shared(shape, element_size, ColMajorTag()); + default: + return nullptr; + } + } + + // Since Layouts are always passed/held by shared_ptr, an attempt to copy + // or move one is a mistake we'd like to catch. + Layout(Layout const &) = delete; + Layout(Layout &&) = delete; + + Layout & operator=(Layout const &) = delete; + Layout & operator=(Layout &&) = delete; + + ~Layout() noexcept = default; + + std::array shape() const noexcept { + std::array result; + fill_shape(result); + return result; + } + + std::array strides() const noexcept { + std::array result; + fill_strides(result); + return result; + } + + // Return the size of this dimension (in elements, not bytes). + Size size() const noexcept { return _size; } + + // Return the distance between elements in this dimension (in bytes). + Offset stride() const noexcept { return _stride; } + + // Return the combined size of this and all later (base class) dimensions. + Size full_size() const noexcept { return _size * Base::full_size(); } + + bool operator==(Layout const & other) const noexcept { + return _size == other._size && _stride == other._stride + && Base::operator==(other); + } + + bool operator!=(Layout const & other) const noexcept { + return !(*this == other); + } + + // Call the given function on this Layout and all nonzero base classes, + // starting with the outermost dimension (Layout) and ending with the + // innermost (Layout<1>). + template + void for_each_dim(F func) const { + func(*this); + Base::for_each_dim(func); + } + + // Call the given function on this Layout and all nonzero base classes, + // starting with the innermost dimension (Layout<0>) and ending with + // the outermost (Layout). + template + void for_each_dim_r(F func) const { + Base::for_each_dim_r(func); + func(*this); + } + + Size count_contiguous_dims(Size element_size, MemoryOrder order) const noexcept { + Size n_contiguous_dims = 0; + Offset contiguous_stride = element_size; + auto func = [&n_contiguous_dims, &contiguous_stride](auto const & layout) -> bool { + if (contiguous_stride == layout.stride()) { + ++n_contiguous_dims; + contiguous_stride *= layout.size(); + } else { + contiguous_stride = 0; // make sure the test never succeeds for any future dimension + } + }; + switch (order) { + case MemoryOrder::ROW_MAJOR: + for_each_dim_r(func); + break; + case MemoryOrder::COL_MAJOR: + for_each_dim(func); + break; + }; + return n_contiguous_dims; + } + + // Set Error::NONCONTIGUOUS if this does not have at least C row-major + // contiguous dimensions (starting from the innermost) or, if C is negative + // at least -C column-major contiguous dimensions (starting from the + // outermost). + template + void check_contiguousness(Size element_size) const { + static_assert( + static_cast(N) >= C && -static_cast(N) <= C, + "Cannot have more contiguous dimensions than total dimensions." + ); +#if NDARRAY_ASSERT_AUDIT_ENABLED + if (C == 0) return; + Size n_contiguous_dims = count_contiguous_dims( + element_size, + C > 0 ? MemoryOrder::ROW_MAJOR : MemoryOrder::COL_MAJOR + ); + if (std::abs(C) > n_contiguous_dims) { + NDARRAY_FAIL( + Error::NONCONTIGUOUS, + "At least {:d} {:s} contiguous dimensions required; array has {:d}.", + std::abs(C), C > 0 ? "row-major" : "column-major", n_contiguous_dims + ); + } +#endif + } + +protected: + + // Explicit shape and strides, called recursively. + template + Layout( + ShapeVector const & shape, + StridesVector const & strides, + std::integral_constant + ) : + Base(shape, strides, std::integral_constant()), + _size(IndexVectorTraits::get_size(shape, M-N)), + _stride(IndexVectorTraits::get_offset(strides, M-N)) + {} + + // Compute row-major strides, called recursively. + template + Layout( + ShapeVector const & shape, + Size element_size, + std::integral_constant, + RowMajorTag + ) : + Base(shape, element_size, std::integral_constant(), RowMajorTag()), + _size(IndexVectorTraits::get_size(shape, M-N)), + _stride(Base::stride() * Base::size() * Base::last_stride(element_size)) + {} + + // Compute column-major strides, called recursively. + template + Layout( + ShapeVector const & shape, + Offset stride, + std::integral_constant, + ColMajorTag + ) : + Base( + shape, + stride * IndexVectorTraits::get_size(shape, M-N), + std::integral_constant(), + ColMajorTag() + ), + _size(IndexVectorTraits::get_size(shape, M-N)), + _stride(stride) + {} + + static Size last_stride(Size element_size) noexcept { return 1; } + + // Write the full array shape into the given array. + template + void fill_shape(std::array & shape) const { + static_assert(M >= N, "Layout not large enough to fill shape vector."); + shape[M-N] = _size; + Base::fill_shape(shape); + } + + // Write strides for all dimensions into the given array. + template + void fill_strides(std::array & strides) const { + static_assert(M >= N, "Layout not large enough to fill strides vector."); + strides[M-N] = _stride; + Base::fill_strides(strides); + } + +private: + + // Private inner class that inherits from Layout and has public ctors. + // This lets us use make_shared in Layout::make without making Layout's + // own constructors public. + class ConstructionHelper; + + Size _size; + Offset _stride; +}; + + +// Trivial private subclass of Layout, with public ctors usable by make_shared. +template +class Layout::ConstructionHelper : public Layout { +public: + + // Constructor from explicit shape and strides. + // Only called by Layout::make(). + template + ConstructionHelper(ShapeVector const & shape, StridesVector const & strides) : + Layout(shape, strides, std::integral_constant()) + {} + + // Constructor from shape and automatic strides. + // Only called by Layout::make(). + template + ConstructionHelper(ShapeVector const & shape, Size element_size, Tag) : + Layout(shape, element_size, std::integral_constant(), Tag()) + {} + +}; + + +// Empty 0-d specialization of Layout to break template recursion. +template <> +class Layout<0> { +public: + + Layout(Layout const &) = delete; + Layout(Layout &&) = delete; + Layout & operator=(Layout const &) = delete; + Layout & operator=(Layout &&) = delete; + + ~Layout() noexcept = default; + + // Return the size of this dimension (in elements, not bytes). + Size size() const noexcept { return 1; } + + // Return the distance between elements in this dimension (in bytes). + Offset stride() const noexcept { return 1; } + + // Return the combined size of this and all later (base class) dimensions. + Size full_size() const noexcept { return 1; } + + bool operator==(Layout const & other) const noexcept { return true; } + bool operator!=(Layout const & other) const noexcept { return false; } + + template + bool for_each_dim(F) const noexcept { return true; } + + template + bool for_each_dim_r(F) const noexcept { return true; } + +protected: + + // Explicit shape and strides. Only called by Layout<1>. + template + Layout( + ShapeVector const & shape, + StridesVector const & strides, + std::integral_constant + ) noexcept {} + + // Automatic strides. Only called by Layout<1>. + template + Layout( + ShapeVector const & shape, + Size element_size, + std::integral_constant, + Tag + ) noexcept {} + + static Size last_stride(Size element_size) noexcept { return element_size; } + + template + void fill_shape(std::array & shape) const noexcept {} + + template + void fill_strides(std::array & strides) const noexcept {} + +}; + + +// Retrieve the Layout dimension specialization corresponding to the Pth +// dimension out of N total dimensions. +template +inline Layout const & +get_dim(Layout const & layout) { return layout; } + + +// Retrieve the Layout dimension specialization corresponding to the Pth +// dimension out of N total dimensions. +template +inline std::shared_ptr const> +get_dim(std::shared_ptr const> const & layout) { return layout; } + + +} // namespace detail +} // ndarray + +#endif // !NDARRAY_detail_Layout_hpp_INCLUDED diff --git a/include/ndarray/detail/NestedIterator.h b/include/ndarray/detail/NestedIterator.h deleted file mode 100644 index d13605c1..00000000 --- a/include/ndarray/detail/NestedIterator.h +++ /dev/null @@ -1,116 +0,0 @@ -// -*- c++ -*- -/* - * Copyright (c) 2010-2012, Jim Bosch - * All rights reserved. - * - * ndarray is distributed under a simple BSD-like license; - * see the LICENSE file that should be present in the root - * of the source distribution, or alternately available at: - * https://github.com/ndarray/ndarray - */ -#ifndef NDARRAY_DETAIL_NestedIterator_h_INCLUDED -#define NDARRAY_DETAIL_NestedIterator_h_INCLUDED - -/** - * @file ndarray/detail/NestedIterator.h - * - * @brief Definition of NestedIterator. - */ - -#include -#include "ndarray_fwd.h" - -namespace ndarray { -namespace detail { - -/** - * @internal @brief Nested-array iterator class for Array with ND > 1. - * - * While this iterator makes use of boost::iterator_facade, it - * reimplements the actual dereferencing operations (operator*, - * operator->) to return Reference const & and - * Reference const *, even though these are - * only convertible to the reference and - * pointer types associated with the iterator, - * not the types themselves. - * - * @ingroup ndarrayInternalGroup - */ -template -class NestedIterator : public boost::iterator_facade< - NestedIterator, - typename ArrayTraits::Value, - boost::random_access_traversal_tag, - typename ArrayTraits::Reference - > -{ -public: - typedef typename ArrayTraits::Value Value; - typedef typename ArrayTraits::Reference Reference; - - Reference operator[](Size n) const { - Reference r(_ref); - r._data += n * _stride; - return r; - } - - Reference const & operator*() const { return _ref; } - - Reference const * operator->() { return &_ref; } - - NestedIterator() : _ref(Value()), _stride(0) {} - - NestedIterator(Reference const & ref, Offset stride) : _ref(ref), _stride(stride) {} - - NestedIterator(NestedIterator const & other) : _ref(other._ref), _stride(other._stride) {} - - template - NestedIterator(NestedIterator const & other) : _ref(other._ref), _stride(other._stride) {} - - NestedIterator & operator=(NestedIterator const & other) { - if (&other != this) { - _ref._data = other._ref._data; - _ref._core = other._ref._core; - _stride = other._stride; - } - return *this; - } - - template - NestedIterator & operator=(NestedIterator const & other) { - _ref._data = other._ref._data; - _ref._core = other._ref._core; - _stride = other._stride; - return *this; - } - -private: - - friend class boost::iterator_core_access; - - template friend class NestedIterator; - - Reference const & dereference() const { return _ref; } - - void increment() { _ref._data += _stride; } - void decrement() { _ref._data -= _stride; } - void advance(Offset n) { _ref._data += _stride * n; } - - template - Offset distance_to(NestedIterator const & other) const { - return std::distance(_ref._data, other._ref._data) / _stride; - } - - template - bool equal(NestedIterator const & other) const { - return _ref._data == other._ref._data; - } - - Reference _ref; - Offset _stride; -}; - -} // namespace detail -} // namespace ndarray - -#endif // !NDARRAY_DETAIL_NestedIterator_h_INCLUDED diff --git a/include/ndarray/detail/StridedIterator.h b/include/ndarray/detail/StridedIterator.h deleted file mode 100644 index 2b8c2800..00000000 --- a/include/ndarray/detail/StridedIterator.h +++ /dev/null @@ -1,98 +0,0 @@ -// -*- c++ -*- -/* - * Copyright (c) 2010-2012, Jim Bosch - * All rights reserved. - * - * ndarray is distributed under a simple BSD-like license; - * see the LICENSE file that should be present in the root - * of the source distribution, or alternately available at: - * https://github.com/ndarray/ndarray - */ -#ifndef NDARRAY_DETAIL_StridedIterator_h_INCLUDED -#define NDARRAY_DETAIL_StridedIterator_h_INCLUDED - -/** - * @file ndarray/detail/StridedIterator.h - * - * @brief Definition of StridedIterator. - */ - -#include "ndarray_fwd.h" -#include - -namespace ndarray { -namespace detail { - -/** - * @internal @brief Strided iterator for noncontiguous 1D arrays. - * - * @ingroup ndarrayInternalGroup - */ -template -class StridedIterator : public boost::iterator_facade< - StridedIterator, - T, boost::random_access_traversal_tag - > -{ -public: - typedef T Value; - typedef T & Reference; - - StridedIterator() : _data(0), _stride(0) {} - - StridedIterator(T * data, Offset stride) : _data(data), _stride(stride) {} - - StridedIterator(StridedIterator const & other) : _data(other._data), _stride(other._stride) {} - - template - StridedIterator(StridedIterator const & other) : _data(other._data), _stride(other._stride) { - BOOST_STATIC_ASSERT((boost::is_convertible::value)); - } - - StridedIterator & operator=(StridedIterator const & other) { - if (&other != this) { - _data = other._data; - _stride = other._stride; - } - return *this; - } - - template - StridedIterator & operator=(StridedIterator const & other) { - BOOST_STATIC_ASSERT((boost::is_convertible::value)); - _data = other._data; - _stride = other._stride; - return *this; - } - -private: - - friend class boost::iterator_core_access; - - template friend class StridedIterator; - - Reference dereference() const { return *_data; } - - void increment() { _data += _stride; } - void decrement() { _data -= _stride; } - void advance(Offset n) { _data += _stride * n; } - - template - Offset distance_to(StridedIterator const & other) const { - return std::distance(_data, other._data) / _stride; - } - - template - bool equal(StridedIterator const & other) const { - return _data == other._data; - } - - T * _data; - Offset _stride; - -}; - -} // namespace detail -} // namespace ndarray - -#endif // !NDARRAY_DETAIL_StridedIterator_h_INCLUDED diff --git a/include/ndarray/detail/UnaryOp.h b/include/ndarray/detail/UnaryOp.h deleted file mode 100644 index e00a461d..00000000 --- a/include/ndarray/detail/UnaryOp.h +++ /dev/null @@ -1,114 +0,0 @@ -// -*- c++ -*- -/* - * Copyright (c) 2010-2012, Jim Bosch - * All rights reserved. - * - * ndarray is distributed under a simple BSD-like license; - * see the LICENSE file that should be present in the root - * of the source distribution, or alternately available at: - * https://github.com/ndarray/ndarray - */ -#ifndef NDARRAY_DETAIL_UnaryOp_h_INCLUDED -#define NDARRAY_DETAIL_UnaryOp_h_INCLUDED - -/** - * @file ndarray/detail/UnaryOp.h - * - * @brief Lazy unary expression templates. - */ - -#include "ndarray/ExpressionBase.h" -#include "ndarray/vectorize.h" -#include - -namespace ndarray { -namespace detail { - -/** - * @internal @brief An iterator for unary expression templates. - * - * @ingroup ndarrayInternalGroup - * - * Acts as a "transform" iterator. - */ -template -class UnaryOpIterator : public boost::iterator_adaptor< - UnaryOpIterator, - typename ExpressionTraits::Iterator, - typename ExpressionTraits< UnaryOpExpression >::Value, - boost::use_default, - typename ExpressionTraits< UnaryOpExpression >::Reference - > { - typedef UnaryOpExpression Operation; -public: - typedef typename ExpressionTraits::Iterator BaseIterator; - typedef typename ExpressionTraits::Value Value; - typedef typename ExpressionTraits::Reference Reference; - - UnaryOpIterator() : UnaryOpIterator::iterator_adaptor_(), _functor() {} - - UnaryOpIterator(BaseIterator const & baseIter, UnaryFunction const & functor) : - UnaryOpIterator::iterator_adaptor_(baseIter), _functor(functor) {} - - UnaryOpIterator(UnaryOpIterator const & other) : - UnaryOpIterator::iterator_adaptor_(other), _functor(other._functor) {} - -private: - friend class boost::iterator_core_access; - - Reference dereference() const { - return vectorize(_functor,*this->base_reference()); - } - - UnaryFunction _functor; -}; - -/** - * @internal @brief A unary expression template. - * - * @ingroup ndarrayInternalGroup - * - * Represents the lazy evaluation of a unary expression. - */ -template -class UnaryOpExpression : public ExpressionBase< UnaryOpExpression > { - typedef UnaryOpExpression Self; -public: - typedef typename ExpressionTraits::Element Element; - typedef typename ExpressionTraits::ND ND; - typedef typename ExpressionTraits::Iterator Iterator; - typedef typename ExpressionTraits::Value Value; - typedef typename ExpressionTraits::Reference Reference; - typedef Vector Index; - - UnaryOpExpression(Operand const & operand, UnaryFunction const & functor) : - _operand(operand), _functor(functor) {} - - Reference operator[](Size n) const { - return Reference(_operand[n],_functor); - } - - Iterator begin() const { - return Iterator(_operand.begin(),_functor); - } - - Iterator end() const { - return Iterator(_operand.end(),_functor); - } - - template Size getSize() const { - return _operand.template getSize

(); - } - - Index getShape() const { - return _operand.getShape(); - } - - Operand _operand; - UnaryFunction _functor; -}; - -} // namespace detail -} // namespace ndarray - -#endif // !NDARRAY_DETAIL_UnaryOp_h_INCLUDED diff --git a/include/ndarray/detail/ViewBuilder.h b/include/ndarray/detail/ViewBuilder.h deleted file mode 100644 index 67e140c8..00000000 --- a/include/ndarray/detail/ViewBuilder.h +++ /dev/null @@ -1,316 +0,0 @@ -// -*- c++ -*- -/* - * Copyright (c) 2010-2012, Jim Bosch - * All rights reserved. - * - * ndarray is distributed under a simple BSD-like license; - * see the LICENSE file that should be present in the root - * of the source distribution, or alternately available at: - * https://github.com/ndarray/ndarray - */ -#ifndef NDARRAY_DETAIL_ViewBuilder_h_INCLUDED -#define NDARRAY_DETAIL_ViewBuilder_h_INCLUDED - -/** - * \file ndarray/detail/ViewBuilder.h @brief Implementation of arbitrary views into arrays. - */ - -#include "ndarray/views.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace ndarray { -namespace detail { - -/** - * @internal - * @brief A temporary object used in constructing a Core object in a view operation. - */ -template -struct CoreTransformer { - T * _data; - typename Core::ConstPtr _input; - typename Core::Ptr _output; - - CoreTransformer( - T * data, - typename Core::ConstPtr const & input, - typename Core::Ptr const & output - ) : _data(data), _input(input), _output(output) {} - - template - CoreTransformer(CoreTransformer const & other) : - _data(other._data), _input(other._input), _output(other._output) {} -}; - -template -struct Dimensions { - typedef boost::mpl::int_ ND; // Number of dimensions in output array - typedef boost::mpl::int_ RMC; // Number of contiguous dimensions in output array, from end. - typedef boost::mpl::int_ IDX; // Current dimension of input array being processed. - typedef boost::mpl::int_ N_I; -}; - -template struct IndexTraits; - -template <> -struct IndexTraits { - - template - struct Append { - typedef Dimensions< - D::ND::value, - ((D::RMC::value < D::N_I::value) ? D::RMC::value : (D::N_I::value - 1)), - (D::IDX::value + 1) - > Type; - }; - - /// @brief Metafunction for the result type of transform(). - template struct TransformCoreResult { - typedef CoreTransformer Type; - }; - - /// @brief Apply a slice index. - template - static CoreTransformer transformCore( - ndarray::index::Slice const & index, CoreTransformer & t - ) { - NDARRAY_ASSERT(index.step > 0); - NDARRAY_ASSERT(index.start <= index.stop); - NDARRAY_ASSERT(index.start >= 0); - NDARRAY_ASSERT(index.stop <= t._input->getSize()); - t._data += index.start * t._input->getStride(); - t._output->setSize(index.computeSize()); - t._output->setStride(t._input->getStride() * index.step); - return t; - } - -}; - -template <> -struct IndexTraits { - - template - struct Append { - typedef Dimensions< - D::ND::value, - ((D::RMC::value < D::N_I::value) ? D::RMC::value : D::N_I::value), - (D::IDX::value + 1) - > Type; - }; - - /// @brief Metafunction for the result type of transform(). - template struct TransformCoreResult { - typedef CoreTransformer Type; - }; - - /// @brief Apply a range index. - template - static CoreTransformer transformCore( - ndarray::index::Range const & index, CoreTransformer & t - ) { - NDARRAY_ASSERT(index.start <= index.stop); - NDARRAY_ASSERT(index.start >= 0); - NDARRAY_ASSERT(index.stop <= t._input->getSize()); - t._data += index.start * t._input->getStride(); - t._output->setSize(index.stop - index.start); - t._output->setStride(t._input->getStride()); - return t; - } -}; - -template <> -struct IndexTraits { - - template - struct Append { - typedef Dimensions< - D::ND::value, - D::RMC::value, - (D::IDX::value + 1) - > Type; - }; - - /// @brief Metafunction for the result type of transform(). - template struct TransformCoreResult { - typedef CoreTransformer Type; - }; - - /// @brief Apply a full dimension index. - template - static CoreTransformer transformCore( - ndarray::index::Full const &, CoreTransformer & t - ) { - t._output->setSize(t._input->getSize()); - t._output->setStride(t._input->getStride()); - return t; - } -}; - -template <> -struct IndexTraits { - - template - struct Append { - typedef Dimensions< - (D::ND::value - 1), - ((D::RMC::value < (D::N_I::value - 1)) ? D::RMC::value : (D::N_I::value - 1)), - D::IDX::value - > Type; - }; - - /// @brief Metafunction for the result type of transform(). - template struct TransformCoreResult { - typedef CoreTransformer Type; - }; - - /// @brief Apply a scalar dimension index. - template - static CoreTransformer transformCore( - ndarray::index::Scalar const & index, CoreTransformer & t - ) { - NDARRAY_ASSERT(index.n >= 0); - NDARRAY_ASSERT(index.n < t._input->getSize()); - t._data += index.n * t._input->getStride(); - return t; - } -}; - -template -typename IndexTraits::template TransformCoreResult::Type -transformCore(Index const & index, CoreTransformer & t) { - return IndexTraits::transformCore(index, t); -} - -struct AppendIndex { - - template - struct apply { - typedef typename IndexTraits::template Append::Type type; - }; - -}; - -template -struct ViewTraits; - -template -struct ViewTraits { - - typedef typename boost::mpl::fold< Seq_, Dimensions, AppendIndex >::type Dims; - - typedef typename Dims::ND ND; - typedef typename Dims::RMC RMC; - -}; - -template -struct ViewTraits { - - typedef typename boost::mpl::fold< - boost::fusion::reverse_view< typename boost::fusion::result_of::as_vector::type >, - Dimensions, AppendIndex - >::type Dims; - - typedef typename Dims::ND ND; - typedef typename boost::mpl::negate::type RMC; - -}; - -/** - * @internal - * @brief Metafunction that pads a View with extra FullDim indices to make size::type::value == N. - */ -template ::type::value==N)> -struct ViewNormalizer { - - typedef typename boost::fusion::result_of::push_back::type Next; - - typedef typename ViewNormalizer::Output Output; - - static Output apply(Seq_ const & input) { - return ViewNormalizer::apply( - boost::fusion::push_back(input, index::Full()) - ); - } -}; - -template -struct ViewNormalizer { - typedef typename boost::fusion::result_of::as_vector::type Output; - static Output apply(Seq_ const & input) { return boost::fusion::as_vector(input); } -}; - -/** - * @internal @ingroup InternalGroup - * @brief Static function object that constructs a view Array. - */ -template -struct ViewBuilder { - typedef ExpressionTraits Traits; - typedef typename Traits::Element Element; - typedef typename Traits::ND InputND; - typedef typename Traits::RMC InputRMC; - typedef typename Traits::Core InputCore; - typedef boost::mpl::bool_<(InputRMC::value < 0)> IsColumnMajor; - - typedef ViewNormalizer Normalizer; - typedef typename ViewNormalizer::Output NormSeq; - typedef ViewTraits OutputTraits; - - typedef typename OutputTraits::ND OutputND; - typedef typename OutputTraits::RMC OutputRMC; - - typedef ArrayRef OutputArray; - typedef Core OutputCore; - - static OutputArray apply(Array_ const & array, InSeq const & seq) { - CoreTransformer initial( - array.getData(), - ArrayAccess< Array_>::getCore(array), - OutputCore::create(array.getManager()) - ); - NormSeq normSeq = Normalizer::apply(seq); - std::pair final = process(normSeq, initial); - return ArrayAccess< OutputArray >::construct(final.first, final.second); - } - - template - static std::pair - process(NormSeq const & seq, CoreTransformer t) { - return process(seq, transformCore(boost::fusion::at_c<(InputND::value-M)>(seq), t)); - } - - static std::pair - process(NormSeq const & seq, CoreTransformer t) { - return std::make_pair(t._data, boost::static_pointer_cast(t._output)); - } - -}; - -/** - * @internal @ingroup InternalGroup - * @brief Wrapper function for ViewBuilder that removes the need to specify its template parameters. - */ -template -typename ViewBuilder::OutputArray -buildView(Array_ const & array, Seq_ const & seq) { - return ViewBuilder::apply(array, seq); -}; - -} // namespace detail - -} // namespace ndarray - -#endif // !NDARRAY_DETAIL_ViewBuilder_h_INCLUDED diff --git a/include/ndarray/eigen.h b/include/ndarray/eigen.h deleted file mode 100644 index 26ee65a2..00000000 --- a/include/ndarray/eigen.h +++ /dev/null @@ -1,269 +0,0 @@ -// -*- c++ -*- -/* - * Copyright (c) 2010-2012, Jim Bosch - * All rights reserved. - * - * ndarray is distributed under a simple BSD-like license; - * see the LICENSE file that should be present in the root - * of the source distribution, or alternately available at: - * https://github.com/ndarray/ndarray - */ -#ifndef NDARRAY_eigen_h_INCLUDED -#define NDARRAY_eigen_h_INCLUDED - -/** - * @file ndarray/eigen.h - * @brief Functions that return an Eigen Map non-reference-counted view into an ndarray::Array. - * - * \note This file is not included by the main "ndarray.h" header file. - */ - -#if defined __GNUC__ && __GNUC__>=6 - #pragma GCC diagnostic ignored "-Wignored-attributes" - #pragma GCC diagnostic ignored "-Wdeprecated-declarations" -#endif - -#include "Eigen/Core" -#include "ndarray.h" -#include "ndarray/eigen_fwd.h" - -namespace ndarray { -namespace detail { - -template -struct SelectEigenPlainBase; - -template -struct SelectEigenPlainBase { - typedef typename boost::remove_const::type Scalar; - typedef Eigen::Matrix< - Scalar, Eigen::Dynamic, N == 1 ? 1 : Eigen::Dynamic, - Eigen::AutoAlign|(C >= 0 && N > 1 ? Eigen::RowMajor : Eigen::ColMajor) - > Type; -}; - -template -struct SelectEigenPlainBase { - typedef typename boost::remove_const::type Scalar; - typedef Eigen::Array< - Scalar, Eigen::Dynamic, N == 1 ? 1 : Eigen::Dynamic, - Eigen::AutoAlign|(C >= 0 && N > 1 ? Eigen::RowMajor : Eigen::ColMajor) - > Type; -}; - -template ::value> -struct SelectEigenPlain; - -template -struct SelectEigenPlain { - typedef typename SelectEigenPlainBase::Type const Type; -}; - -template -struct SelectEigenPlain { - typedef typename SelectEigenPlainBase::Type Type; -}; - -template -struct SelectEigenMap; // unspecialized template parameters means not supported. - -// 1-d, not contiguous -template -struct SelectEigenMap { - - typedef Eigen::Map< - typename SelectEigenPlain::Type, - Eigen::Unaligned, - Eigen::InnerStride<> - > Type; - - static Type apply(ndarray::Array const & array) { - return Type(array.getData(), array.template getSize<0>(), - Eigen::InnerStride<>(array.template getStride<0>())); - } - -}; - -// 1-d, row-major contiguous -template -struct SelectEigenMap { - - typedef Eigen::Map< - typename SelectEigenPlain::Type, - Eigen::Unaligned - > Type; - - static Type apply(ndarray::Array const & array) { - return Type(array.getData(), array.template getSize<0>()); - } - -}; - -// 1-d, column-major contiguous -template -struct SelectEigenMap { - - typedef Eigen::Map< - typename SelectEigenPlain::Type, - Eigen::Unaligned - > Type; - - static Type apply(ndarray::Array const & array) { - return Type(array.getData(), array.template getSize<0>()); - } - -}; - -// 2-d, not contiguous -template -struct SelectEigenMap { - - typedef Eigen::Map< - typename SelectEigenPlain::Type, - Eigen::Unaligned, - Eigen::Stride - > Type; - - static Type apply(ndarray::Array const & array) { - return Type(array.getData(), array.template getSize<0>(), array.template getSize<1>(), - Eigen::Stride( - array.template getStride<0>(), array.template getStride<1>() - )); - } - -}; - -// 2-d, row-major, contiguous within a row -template -struct SelectEigenMap { - - typedef Eigen::Map< - typename SelectEigenPlain::Type, - Eigen::Unaligned, - Eigen::OuterStride<> - > Type; - - static Type apply(ndarray::Array const & array) { - return Type(array.getData(), array.template getSize<0>(), array.template getSize<1>(), - Eigen::OuterStride<>(array.template getStride<0>())); - } - -}; - -// 2-d, row-major, fully contiguous -template -struct SelectEigenMap { - - typedef Eigen::Map< - typename SelectEigenPlain::Type, - Eigen::Unaligned - > Type; - - static Type apply(ndarray::Array const & array) { - return Type(array.getData(), array.template getSize<0>(), array.template getSize<1>()); - } - -}; - -// 2-d, column-major, contiguous within a column -template -struct SelectEigenMap { - - typedef Eigen::Map< - typename SelectEigenPlain::Type, - Eigen::Unaligned, - Eigen::OuterStride<> - > Type; - - static Type apply(ndarray::Array const & array) { - return Type(array.getData(), array.template getSize<0>(), array.template getSize<1>(), - Eigen::OuterStride<>(array.template getStride<1>())); - } - -}; - -// 2-d, column-major, fully contiguous -template -struct SelectEigenMap { - - typedef Eigen::Map< - typename SelectEigenPlain::Type, - Eigen::Unaligned - > Type; - - static Type apply(ndarray::Array const & array) { - return Type(array.getData(), array.template getSize<0>(), array.template getSize<1>()); - } - -}; - -} // namespace detail - - -//@{ -/** - * Use asEigenArray(array) or asEigen(array) - * to obtain an Eigen::Array-like view of an ndarray array. - * Use asEigenMatrix(array) or asEigen(array) - * to obtain an Eigen::Matrix-like view of an ndarray array. - * The returned view is an Eigen::Map whose memory is owned by the ndarray array. - * - * Be careful when calling these functions on a temporary ndarray array, - * as you must keep the temporary alive until you are done with the returned Eigen::Map. - * For example the following will have undefined behavior: - * - * auto eigenMap = asEigenMatrix(makeNdArray(...)); - * // at this point the temporary ndarray array is gone and eigenMap is broken - * processMatrix(eigenMap); - * - * This version is safe: - * - * processMatrix(asEigenMatrix(makeNdArray(...))); - * - * This version is also safe and allows you to do additional processing of the Eigen map: - * - * auto arr = makeNdArray(...); - * auto eigenMap = asEigenMatrix(arr); - * processMatrix(eigenMap); - * // ... do more with eigenMap - */ -template -typename detail::SelectEigenMap::Type -asEigen(Array const & a) { - return detail::SelectEigenMap::apply(a); -} - -template -typename detail::SelectEigenMap::Type -asEigen(ArrayRef const & a) { - return detail::SelectEigenMap::apply(a); -} - -template -typename detail::SelectEigenMap::Type -asEigenArray(Array const & a) { - return asEigen(a); -} - -template -typename detail::SelectEigenMap::Type -asEigenArray(ArrayRef const & a) { - return asEigen(a); -} - -template -typename detail::SelectEigenMap::Type -asEigenMatrix(Array const & a) { - return asEigen(a); -} - -template -typename detail::SelectEigenMap::Type -asEigenMatrix(ArrayRef const & a) { - return asEigen(a); -} -//@} - -} // namespace ndarray - -#endif // !NDARRAY_eigen_h_INCLUDED diff --git a/include/ndarray/eigen_fwd.h b/include/ndarray/eigen_fwd.h deleted file mode 100644 index fdb362fe..00000000 --- a/include/ndarray/eigen_fwd.h +++ /dev/null @@ -1,38 +0,0 @@ -// -*- c++ -*- -/* - * Copyright (c) 2010-2012, Jim Bosch - * All rights reserved. - * - * ndarray is distributed under a simple BSD-like license; - * see the LICENSE file that should be present in the root - * of the source distribution, or alternately available at: - * https://github.com/ndarray/ndarray - */ -#ifndef NDARRAY_eigen_fwd_h_INCLUDED -#define NDARRAY_eigen_fwd_h_INCLUDED - -/** - * @file ndarray/eigen_fwd.h - * @brief Forward declarations for ndarray/eigen interface. - */ - -/** - * \defgroup ndarrayEigenGroup Eigen - * Interoperability with the Eigen 3 linear algebra library. - */ - -namespace Eigen { - -struct MatrixXpr; - -} // namespace Eigen - -namespace ndarray { -namespace detail { - -template struct EigenStrideTraits; - -} // namespace detail -} // namespace ndarray - -#endif // !NDARRAY_eigen_fwd_h_INCLUDED diff --git a/include/ndarray/errors.hpp b/include/ndarray/errors.hpp new file mode 100644 index 00000000..a14e3a49 --- /dev/null +++ b/include/ndarray/errors.hpp @@ -0,0 +1,128 @@ +// -*- c++ -*- +/* + * Copyright (c) 2010-2018, Jim Bosch + * All rights reserved. + * + * ndarray is distributed under a simple BSD-like license; + * see the LICENSE file that should be present in the root + * of the source distribution, or alternately available at: + * https://github.com/ndarray/ndarray + */ +#ifndef NDARRAY_errors_hpp_INCLUDED +#define NDARRAY_errors_hpp_INCLUDED + +#include +#include +#include "fmt/format.h" +#include "ndarray/common.hpp" + + +namespace ndarray { + +class Error { +public: + + enum Category { NONCONTIGUOUS, UNINITIALIZED, OUT_OF_BOUNDS, INCOMPATIBLE_ARGUMENTS }; + + typedef void (*Handler)(Category, char const * file, int line, std::string const & message); + + class ScopedHandler { + public: + + explicit ScopedHandler(Handler handler) : _old(get_handler()) { + set_handler(handler); + } + + ScopedHandler(ScopedHandler const &) = delete; + ScopedHandler(ScopedHandler &&) = delete; + + ScopedHandler & operator=(ScopedHandler const &) = delete; + ScopedHandler & operator=(ScopedHandler &&) = delete; + + ~ScopedHandler() { set_handler(_old); } + + private: + Handler _old; + }; + + template + static void throw_handler(Category category, char const * file, int line, std::string const & message) { + throw Exception(message); + } + + static void set_handler(Handler handler=nullptr) { + get_handler() = handler; + } + + [[ noreturn ]] static void invoke ( + Category category, + char const * file, + int line, + std::string const & message + ) { + Handler handler = get_handler(); + if (handler) { + handler(category, file, line, message); + } else { + fmt::print(stderr, "{:s}:{:d}: {:s}", file, line, message); + } + std::abort(); + } + + template + [[ noreturn ]] static void invoke ( + Category category, + char const * file, + int line, + char const * tmpl, + Args && ...args + ) { + invoke(category, file, line, fmt::format(tmpl, std::forward(args)...)); + } + +private: + + static Handler & get_handler() { + static Handler handler = nullptr; + return handler; + } +}; + + +#define NDARRAY_FAIL(CATEGORY, ...) \ + Error::invoke(CATEGORY, __FILE__, __LINE__, __VA_ARGS__) + + +#ifndef NDARRAY_ASSERT_AUDIT_ENABLED + #define NDARRAY_ASSERT_AUDIT_ENABLED false +#endif + +#if NDARRAY_ASSERT_AUDIT_ENABLED + #define NDARRAY_ASSERT_CHECK_ENABLED true +#else + #ifndef NDARRAY_ASSERT_CHECK_ENABLED + #ifdef NDEBUG + #define NDARRAY_ASSERT_CHECK_ENABLED false + #else + #define NDARRAY_ASSERT_CHECK_ENABLED true + #endif + #endif +#endif + +#if NDARRAY_ASSERT_AUDIT_ENABLED + #define NDARRAY_ASSERT_AUDIT(CONDITION, CATEGORY, ...) \ + if (!(CONDITION)) NDARRAY_FAIL(CATEGORY, __VA_ARGS__) +#else + #define NDARRAY_ASSERT_AUDIT(CONDITION, CATEGORY, ...) ((void)0) +#endif + +#if NDARRAY_ASSERT_CHECK_ENABLED + #define NDARRAY_ASSERT_CHECK(CONDITION, CATEGORY, ...) \ + if (!(CONDITION)) NDARRAY_FAIL(CATEGORY, __VA_ARGS__) +#else + #define NDARRAY_ASSERT_CHECK(CONDITION, CATEGORY, ...) ((void)0) +#endif + +} // ndarray + +#endif // !NDARRAY_errors_hpp_INCLUDED diff --git a/include/ndarray/expressions/BulkParallel.hpp b/include/ndarray/expressions/BulkParallel.hpp new file mode 100644 index 00000000..bd9afeac --- /dev/null +++ b/include/ndarray/expressions/BulkParallel.hpp @@ -0,0 +1,132 @@ +// -*- c++ -*- +/* + * Copyright (c) 2010-2018, Jim Bosch + * All rights reserved. + * + * ndarray is distributed under a simple BSD-like license; + * see the LICENSE file that should be present in the root + * of the source distribution, or alternately available at: + * https://github.com/ndarray/ndarray + */ +#ifndef NDARRAY_EXPRESSIONS_BulkParallel_hpp_INCLUDED +#define NDARRAY_EXPRESSIONS_BulkParallel_hpp_INCLUDED + +#include +#include + +#include "ndarray/expressions/Executor.hpp" + +namespace ndarray { +namespace expressions { + +/** + * An Executor that traverses segments of its dimension in different threads. + * + * BulkParallelExecutor divides the dimension it is responsible for into + * roughly equal segments and uses std::async and std::future to traverse + * those segments in parallel while still guaranteeing that ReductionFunctions + * are invoked deterministically. Specifically, each segment/thread receives + * a copy of the ReductionFunction created by ReductionFunction::split(), and + * these are joined back together in the same order they were created. + * + * As this form of parallelization may be somewhat heavyweight, BulkParallel + * should generally be used with either large segments (i.e. shape much larger + * than `num_threads`) or with a nested SequentialExecutor for inner + * dimensions. + * + * @sa BulkParallel + */ +template +class BulkParallelExecutor : public Executor> { +public: + + static_assert(IsExecutor::value); + + BulkParallelExecutor(Next && next, Size num_threads=0) : + _num_threads(num_threads), + _next(std::move(next)) {} + + template + bool execute(Size const * shape, Traversal traversal, ReductionFunction & function) const { + Size const size = *shape; + ++shape; + + auto to_launch = [shape, this]( + Size start, Size stop, + Traversal && local_traversal, + ReductionFunction && local_function + ) -> ReductionFunction { + local_traversal += start; + for (Size k = start; k < stop; ++k, ++local_traversal) { + if (!_next.execute(shape, local_traversal.evaluate(), local_function)) { + break; + } + } + return local_function; + }; + + std::vector> futures; + if (_num_threads == 0) { + futures.reserve(size); + for (Size n = 0; n < size; ++n) { + futures.push_back( + std::async(to_launch, n, n + 1, Traversal(traversal), function.split()) + ); + } + } else { + futures.reserve(_num_threads);; + for (Size n_launched = 0, start = 0; n_launched < _num_threads; ++n_launched) { + Size stop = start + (size - start)/(_num_threads - n_launched); + futures.push_back( + std::async(to_launch, start, stop, Traversal(traversal), function.split()) + ); + start = stop; + } + } + + for (auto & future : futures) { + if (!function.join(future.get())) { + return false; + } + } + return true; + } + +private: + Size _num_threads; + Next _next; +}; + + +/** + * An ExecutorFactory for parallel evaluation, intended for use with outer + * dimensions. + * + * @sa BulkParallelExecutor + */ +class BulkParallel { +public: + + /** + * Construct a factory. + * + * @param[in] num_threads Number of threads to use, or zero to use one + * thread for every element in the traversed + * dimension. + */ + explicit BulkParallel(Size num_threads) : _num_threads(num_threads) {} + + template + auto makeExecutor(Next && next) const { + return BulkParallelExecutor(std::move(next), _num_threads); + } + +private: + Size _num_threads; +}; + + +} // namespace expressions +} // namespace ndarray + +#endif // !NDARRAY_EXPRESSIONS_BulkParallel_hpp_INCLUDED diff --git a/include/ndarray/expressions/Executor.hpp b/include/ndarray/expressions/Executor.hpp new file mode 100644 index 00000000..947199b3 --- /dev/null +++ b/include/ndarray/expressions/Executor.hpp @@ -0,0 +1,358 @@ +// -*- c++ -*- +/* + * Copyright (c) 2010-2018, Jim Bosch + * All rights reserved. + * + * ndarray is distributed under a simple BSD-like license; + * see the LICENSE file that should be present in the root + * of the source distribution, or alternately available at: + * https://github.com/ndarray/ndarray + */ +#ifndef NDARRAY_EXPRESSIONS_Executor_hpp_INCLUDED +#define NDARRAY_EXPRESSIONS_Executor_hpp_INCLUDED + +#include + +#include "ndarray/common.hpp" +#include "ndarray/expressions/Expression.hpp" + +namespace ndarray { +namespace expressions { + + +#ifdef DOXYGEN + +/** + * A specialized functor that summarizes expressions. + * + * ReductionFunction is an informal concept, not a true class; it exists only + * in documentation, as a way to specify the operations that should be + * supported by any type passed as the last argument to Executor::reduce(). + */ +class ReductionFunction { +public: + + /** + * Add the given scalar value from an Expression to the reduction. + * + * @return Return true if the expression should continue to be evaluated, + * false if it can be short-circuited. + */ + bool accumulate(Scalar x); + + /** + * Return a new RedunctionFunction with a copy of this' state. + * + * If evaluation is not short-circuited, the returned ReductionFunction + * will later be combined with this via a call to join(). + * + * It must be safe to call accumulate() on this and the returned + * ReductionFunction from different threads. + * + * @return A ReductionFunction of the same type as this. + */ + ReductionFunction split() const; + + /** + * Merge in results from a ReductionFunction created by split(). + * + * @return Return true if the expression should continue to be evaluated, + * false if it can be short-circuited. + */ + bool join(ReductionFunction && other); + + /** + * Extract final results from the ReductionFunction after expression + * evaluation is complete. + */ + Scalar finish() &&; + +}; + + +/** + * A factory for Executor objects of a particular type. + * + * ExecutorFactory is an informal concept, not a true class; it exists only + * in documentation. + */ +class ExecutorFactory { +public: + + /** + * Construct an Executor instance. + * + * @param[in] next An existing Executor or ExecutorLeaf instance that + * the returned Executor should call on every iteration. + */ + template + Executor makeExecutor(Next && next) const; + +}; + +#endif // DOXYGEN + + +/** + * A ReductionFunction that accumulates nothing and evaluates all expression + * elements. + */ +class DummyReductionFunction { +public: + + template + bool accumulate(T x) { return true; } + + DummyReductionFunction split() const {} + + bool join(DummyReductionFunction && other) { return true; } + + void finish() &&; + +}; + + +/** + * Sentinal class for nested expression execution. + * + * All Executors are templated on the Executor for the next dimension; + * ExecutorLeaf is used to break this chain for the last dimension. + */ +class ExecutorLeaf { +public: + + /** + * ExecutorLeaf is always applied after the last dimension; it does + * not advance a Traversal. + */ + static constexpr Size ndim = 0; + + /** + * Apply the pseudo-executor to a scalar value. + */ + template + bool execute(Size const *, Result && result, ReductionFunction & function) const { + return function.accumulate(std::forward(result)); + } + +}; + + +/** + * CRTP base class for objects that evaluate Expressions. + * + * Executor template classes are always templated on the Executor for their + * next dimension (which may be a completely different template class), until + * the chain is broken by ExecutionLeaf at dimension 0. + */ +template +class Executor { +public: + + /** + * The Expression dimensionality of Expressions or Traversals that can be + * evaluted by this object. + */ + static constexpr Size ndim = N; + + /** + * Summarize the given Traversal by applying a ReductionFunction to its + * elements. + * + * @param[in] shape Pointer to the number of elements in the current + * dimension, with size[1] the number of elements in + * the next dimension. + * + * @param[in] traversal Traversal this Executor should recursively + * evaluate. + * + * @param[in, out] function ReductionFunction to apply to each + * expression element. + * + * @return The result of calling `function.finish()` after processing all + * elements. + */ + template + auto reduce(Size const * shape, Traversal && traversal, ReductionFunction function) const { + static_assert(Traversal::ndim == ndim, + "Traversal dimensionality does not match Evaluator dimensionality."); + execute(shape, std::forward(traversal), function); + return std::move(function).finish(); + } + + /** + * Evaluate all elements of the given Traversal. + * + * @param[in] shape Pointer to the number of elements in the current + * dimension, with size[1] the number of elements in + * the next dimension. + * + * @param[in] traversal Traversal this Executor should recursively + * evaluate. + */ + template + void run(Size const * shape, Traversal && traversal) const { + reduce(shape, std::forward(traversal), DummyReductionFunction()); + } + + /** + * Summarize the given Expression by applying a ReductionFunction to its + * elements. + * + * @param[in] expression Expression this Executor should recursively + * evaluate. + * + * @param[in, out] function ReductionFunction to apply to each + * expression element. + * + * @return The result of calling `function.finish()` after processing all + * elements. + */ + template + auto reduce(Expression const & expression, ReductionFunction function) const { + std::array const shape = expression.full_shape(); + return reduce(shape.data(), expression.traverse(), function); + } + + /** + * Evaluate all elements of the given Expression. + * + * @param[in] expression Expression this Executor should recursively + * evaluate. + */ + template + void run(Expression const & expression) const { + reduce(expression, DummyReductionFunction()); + } + +protected: + + /** + * Recursively evaluate an Expression. + * + * This method must be implemented by all subclasses. While it is + * conceptually protected, the use of CRTP requires that subclasses either + * make it public or make Expression a friend. + * + * @param[in] shape Pointer to the number of elements in the current + * dimension, with size[1] the number of elements in + * the next dimension. + * + * @param[in] traversal Traversal this Executor should evaluate (passing + * the result to its nested Executor), and advance + * `*shape` times. + * + * @param[in, out] function ReductionFunction to apply to each expression + * element. + * + * @return Return true if the expression should continue to be evaluated, + * false if it can be short-circuited. + */ + template + bool execute(Size const * shape, Traversal && traversal, ReductionFunction & function) const { + return static_cast(*this).execute( + shape, + std::forward(traversal), + function + ); + } + +}; + +/** + * Type traits struct that checks whether a type is an Executor or + * ExecutorLeaf. + */ +template +using IsExecutor = std::integral_constant< + bool, + std::is_base_of, T>::value || std::is_same::value +>; + + +/** + * An ExecutorFactory that constructs multi-dimensional Executors of with + * potentially different types for different dimensions. + * + * The makeExecutor() free function (which delegates to this class) should + * generally be called instead of using MultiExecutorFactory director. + */ +template +class MultiExecutorFactory { + using Tuple = std::tuple; +public: + + /** + * Dimensionality of Executors produced by this factory. + */ + static constexpr Size ndim = sizeof...(Sequence); + + /** + * Construct from a sequence of single-dimension Executor factories. + */ + explicit MultiExecutorFactory(Sequence const & ...args) : _tuple(args...) {} + + /** + * Construct an Executor instance. + * + * @param[in] next An existing Executor or ExecutorLeaf instance that + * the returned Executor should call on every iteration. + */ + template + auto makeExecutor(Next && next) const { + return _makeExecutor(DimensionIndex<0>{}, std::move(next)); + } + +private: + + template + auto _makeExecutor(DimensionIndex, Next && next) const { + return next; + } + + template + auto _makeExecutor(DimensionIndex, Next && next) const { + return std::get(_tuple).makeExecutor( + _makeExecutor(DimensionIndex{}, std::move(next)) + ); + } + + Tuple _tuple; + +}; + + +//@{ +/** + * Combine a sequence of ExecutorFactory objects into a single one. + * + * @param[in] factories Sequence of ExecutorFactory objects, ordered + * from outer dimensions to inner dimensions. + */ +template +FactorySequence const & makeExecutorFactory(FactorySequence const & factories) { + return factories; +} +template +auto makeExecutorFactory(FactorySequence const & ...factories) { + return MultiExecutorFactory(factories...); +} +//@} + + +/** + * Create an Executor from a sequence of ExecutorFactories. + * + * @param[in] factories Sequence of ExecutorFactory objects, ordered + * from outer dimensions to inner dimensions. + */ +template +auto makeExecutor(FactorySequence const & ...factories) { + return makeExecutorFactory(factories...).makeExecutor(ExecutorLeaf()); +} + + +} // namespace expressions +} // namespace ndarray + +#endif // !NDARRAY_EXPRESSIONS_Executor_hpp_INCLUDED diff --git a/include/ndarray/expressions/Expression.hpp b/include/ndarray/expressions/Expression.hpp new file mode 100644 index 00000000..61454eb0 --- /dev/null +++ b/include/ndarray/expressions/Expression.hpp @@ -0,0 +1,182 @@ +// -*- c++ -*- +/* + * Copyright (c) 2010-2018, Jim Bosch + * All rights reserved. + * + * ndarray is distributed under a simple BSD-like license; + * see the LICENSE file that should be present in the root + * of the source distribution, or alternately available at: + * https://github.com/ndarray/ndarray + */ +#ifndef NDARRAY_EXPRESSIONS_Expression_hpp_INCLUDED +#define NDARRAY_EXPRESSIONS_Expression_hpp_INCLUDED + +#include +#include + +#include "ndarray/common.hpp" +#include "ndarray/errors.hpp" + +namespace ndarray { +namespace expressions { + +/** + * Integer tag class for recursive iteration over expression dimensions. + * + * Using a subclass of std::integral_constant provides a little type safety + * when multiple constexpr integers are in play in a single function. + */ +template +struct DimensionIndex : public std::integral_constant {}; + + +/** + * CRTP base class for expression templates. + * + * Unlike Arrays, Expressions are not expected to own their data; they are + * expected to be created, used, and destroyed with in a single statement. + * They should be be copyable and moveable, but with a pointer/iterator-like + * approach to constness and ownership; expressions are lightweight, and + * copies should copy pointers and references rather than their targets. + */ +template +class Expression { +public: + + /** + * The number of dimensions of the expression. + */ + static constexpr Size ndim = N; + + /** + * Return the size of the jth dimension. + * + * Must be implemented by all subclasses. + */ + template + Size shape_at(DimensionIndex j) const { + return static_cast(*this).shape_at(j); + } + + /** + * Return a version of the expression with the given shape. + * + * When the current expression has a dimension with a size of one, and the + * given shape has a larger size for that dimension, the returned + * expression should be able to use the larger size by repeating the + * single value. When the expression cannot be mapped to the given shape, + * subclasses should invoke Error::INCOMPATIBLE_ARGUMENTS. When the + * expression cannot be mapped to the given number of dimensions, + * subclasses should static_assert. + * + * Must be implemented by all subclasses. + * + * @param[in] shape Shape the returned expression must have. + * + * @returns A new Expression instance with unspecified exact type. + */ + template + decltype(auto) broadcast(std::array const & shape) && { + return static_cast(std::move(*this)).broadcast(shape); + } + + /** + * Return a Traversal for the first dimension of the expression. + * + * Must be implemented by all subclasses. + * + * @returns A new Traversal instance with unspecified exact type. + */ + decltype(auto) traverse() const { + return static_cast(*this).traverse(); + } + + /** + * Return the full shape of the expression. + */ + std::array full_shape() const { + std::array result = {0}; + _set_shape(result, DimensionIndex<0>{}); + return result; + } + +private: + + void _set_shape(std::array & out, DimensionIndex) const {} + + template + void _set_shape(std::array & out, DimensionIndex j) const { + out[J] = shape_at(j); + NDARRAY_ASSERT_CHECK(out[J] != 0, Error::INCOMPATIBLE_ARGUMENTS, + "Expression does not define a size for index {:d}.", J); + _set_shape(out, DimensionIndex{}); + } + +}; + + +#ifdef DOXYGEN + +/** + * A nested iterator-like quantity used to traverse expressions. + * + * Traversal is an informal concept, not a true class; it exists only in + * documentation, as a way to specify the operations that should be supported + * by any type returned by Expression::traverse(). + */ +class Traversal { +public: + + /** + * The type returned by evaluate(). + * + * When ndim > 1, this should be another Traversal type. When ndim == 1, + * this should be a scalar type. + */ + using Result = /* unspecified */; + + /** + * The dimension of the expression this traversal iterates over. + */ + static constexpr ndim; + + /** + * Advance the traversal by one element in its dimension. + */ + Traversal & operator++(); + + /** + * Advance the traversal by n elements in its dimension. + */ + Traversal & operator+=(Offset n); + + /** + * Return a Traversal to the next dimension (ndim > 1) or a scalar value + * or reference (ndim == 1). + */ + Result evaluate(); + +}; + +#endif // DOXYGEN + +/** + * Type traits struct that yields the outermost Traversal type for an + * Expression. + */ +template +struct GetTraversal { + using Type = decltype(std::declval().traversal()); +}; + +/** + * Helper declaration for GetTraversalType. + */ +template +using GetTraversalType = typename GetTraversal::Type; + + +} // namespace expressions +} // namespace ndarray + +#endif // !NDARRAY_EXPRESSIONS_Expression_hpp_INCLUDED diff --git a/include/ndarray/expressions/Sequential.hpp b/include/ndarray/expressions/Sequential.hpp new file mode 100644 index 00000000..ad9b34d0 --- /dev/null +++ b/include/ndarray/expressions/Sequential.hpp @@ -0,0 +1,74 @@ +// -*- c++ -*- +/* + * Copyright (c) 2010-2018, Jim Bosch + * All rights reserved. + * + * ndarray is distributed under a simple BSD-like license; + * see the LICENSE file that should be present in the root + * of the source distribution, or alternately available at: + * https://github.com/ndarray/ndarray + */ +#ifndef NDARRAY_EXPRESSIONS_Sequential_hpp_INCLUDED +#define NDARRAY_EXPRESSIONS_Sequential_hpp_INCLUDED + +#include "ndarray/expressions/Executor.hpp" + +namespace ndarray { +namespace expressions { + + +/** + * An Executor that simply iterates over its dimension sequentially with no + * parallelism or vectorization. + * + * @sa Sequential + */ +template +class SequentialExecutor : public Executor> { +public: + + static_assert(IsExecutor::value); + + explicit SequentialExecutor(Next && next) : _next(std::move(next)) {} + + template + bool execute(Size const * shape, Traversal traversal, ReductionFunction & function) const { + Size const size = *shape; + for (Size k = 0u; k < size; ++k, ++traversal) { + if (!_next.execute(shape + 1, traversal.evaluate(), function)) { + return false; + } + } + return true; + } + +private: + Next _next; +}; + + +/** + * An ExecutorFactory for simple, sequential iteration. + * + * @sa SequentialExecutor + */ +class Sequential { +public: + + /** + * Construct a factory. + */ + Sequential() = default; + + template + auto makeExecutor(Next && next) const { + return SequentialExecutor(std::move(next)); + } + +}; + + +} // namespace expressions +} // namespace ndarray + +#endif // !NDARRAY_EXPRESSIONS_Sequential_hpp_INCLUDED diff --git a/include/ndarray/expressions/Strided.hpp b/include/ndarray/expressions/Strided.hpp new file mode 100644 index 00000000..3c103667 --- /dev/null +++ b/include/ndarray/expressions/Strided.hpp @@ -0,0 +1,143 @@ +// -*- c++ -*- +/* + * Copyright (c) 2010-2018, Jim Bosch + * All rights reserved. + * + * ndarray is distributed under a simple BSD-like license; + * see the LICENSE file that should be present in the root + * of the source distribution, or alternately available at: + * https://github.com/ndarray/ndarray + */ +#ifndef NDARRAY_EXPRESSIONS_Strided_hpp_INCLUDED +#define NDARRAY_EXPRESSIONS_Strided_hpp_INCLUDED + +#include +#include + +#include "ndarray/expressions/Expression.hpp" + +namespace ndarray { +namespace expressions { + + +template +class StridedTraversal; + + +/** + * Sentinal 1-d specialization of StridedTraversal. + */ +template +class StridedTraversal { +public: + + static constexpr Size ndim = 1; + + using Result = T &; + + StridedTraversal(T * pointer, Offset const * strides) : _pointer(pointer), _strides(strides) {} + + StridedTraversal & operator++() { + _pointer += *_strides; + return *this; + } + + StridedTraversal & operator+=(Offset n) { + _pointer += (*_strides)*n; + return *this; + } + + Result evaluate() const { return *_pointer; } + +private: + T * _pointer; + Offset const * _strides; +}; + + +/** + * A Traversal for strided arrays. + */ +template +class StridedTraversal { +public: + + static constexpr Size ndim = N; + + using Result = StridedTraversal; + + StridedTraversal(T * pointer, Offset const * strides) : _pointer(pointer), _strides(strides) {} + + StridedTraversal & operator++() { + _pointer += *_strides; + return *this; + } + + StridedTraversal & operator+=(Offset n) { + _pointer += (*_strides)*n; + return *this; + } + + Result evaluate() const { return Result(_pointer, _strides + 1); } + +private: + T * _pointer; + Offset const * _strides; +}; + + +/** + * An Expression for strided arrays. + */ +template +class StridedExpression : public Expression> { +public: + + using Traversal = StridedTraversal; + + StridedExpression( + T * pointer, + std::array const & shape, + std::array const & strides + ) : _pointer(pointer), + _shape(shape), + _strides(strides) + {} + + template + Size shape_at(DimensionIndex j) const { return _shape[J]; } + + template + decltype(auto) broadcast(std::array const & shape) && { + static_assert(M >= N, "Cannot decrease dimensionality in broadcast."); + std::array new_strides = {0}; + for (Size j = 0u; j < N; ++j) { + if (_shape[j] != shape[j]) { + if (_shape[j] == 1u) { + new_strides[j] = 0; + } else { + NDARRAY_FAIL(Error::INCOMPATIBLE_ARGUMENTS, + "Shapes {:d} and {:d} in dimension {:d} cannot be broadcast together.", + _shape[j], shape[j], j); + } + } + } + return StridedExpression(_pointer, shape, new_strides); + } + + decltype(auto) traverse() const { + return Traversal(_pointer, _strides.data()); + } + +private: + T * _pointer; + std::array _shape; + std::array _strides; +}; + + + +} // namespace expressions +} // namespace ndarray + +#endif // !NDARRAY_EXPRESSIONS_Strided_hpp_INCLUDED diff --git a/include/ndarray/fft.h b/include/ndarray/fft.h deleted file mode 100644 index 95929dc6..00000000 --- a/include/ndarray/fft.h +++ /dev/null @@ -1,29 +0,0 @@ -// -*- c++ -*- -/* - * Copyright (c) 2010-2012, Jim Bosch - * All rights reserved. - * - * ndarray is distributed under a simple BSD-like license; - * see the LICENSE file that should be present in the root - * of the source distribution, or alternately available at: - * https://github.com/ndarray/ndarray - */ -#ifndef NDARRAY_fft_h_INCLUDED -#define NDARRAY_fft_h_INCLUDED - -/** - * @file ndarray/fft.h - * - * @brief Main public header file for ndarray FFT library. - * - * \note This file is not included by the main "ndarray.h" header file. - */ - -#include "ndarray.h" -#include "ndarray/fft/FourierTransform.h" -#include "ndarray/fft/FourierOps.h" -#ifndef NDARRAY_FFT_MANUAL_INCLUDE -#include "ndarray/fft/FourierTransform.cc" -#endif - -#endif // !NDARRAY_fft_h_INCLUDED diff --git a/include/ndarray/fft/FFTWTraits.h b/include/ndarray/fft/FFTWTraits.h deleted file mode 100644 index 87f94cdc..00000000 --- a/include/ndarray/fft/FFTWTraits.h +++ /dev/null @@ -1,326 +0,0 @@ -// -*- c++ -*- -/* - * Copyright (c) 2010-2012, Jim Bosch - * All rights reserved. - * - * ndarray is distributed under a simple BSD-like license; - * see the LICENSE file that should be present in the root - * of the source distribution, or alternately available at: - * https://github.com/ndarray/ndarray - */ -// THIS FILE IS MACHINE GENERATED BY SCONS. DO NOT EDIT MANUALLY. -#ifndef NDARRAY_FFT_FFTWTraits_h_INCLUDED -#define NDARRAY_FFT_FFTWTraits_h_INCLUDED - -/** - * @file ndarray/fft/FFTWTraits.h - * - * \brief Traits classes that wrap FFTW in a template-friendly interface. - */ - -#include -#include -#include "ndarray/fft/FourierTraits.h" - -namespace ndarray { -/// \cond INTERNAL -namespace detail { - -/** - * \internal \ingroup FFTInternalGroup - * \brief A traits class that maps C++ template types to FFTW types and wraps FFTW function calls. - */ -template struct FFTWTraits { BOOST_STATIC_ASSERT(sizeof(T) < 0); }; - -/// \cond SPECIALIZATIONS - - template <> struct FFTWTraits { - BOOST_STATIC_ASSERT((!boost::is_const::value)); - typedef fftwf_plan Plan; - typedef FourierTraits::ElementX ElementX; - typedef FourierTraits::ElementK ElementK; - typedef boost::shared_ptr OwnerX; - typedef boost::shared_ptr OwnerK; - static inline Plan forward(int rank, const int *n, int howmany, - ElementX *in, const int *inembed, int istride, int idist, - ElementK *out, const int *onembed, int ostride, int odist, - unsigned flags) { - return fftwf_plan_many_dft_r2c(rank, n, howmany, - in, inembed, istride, idist, - reinterpret_cast(out), - onembed, ostride, odist, - flags); - } - static inline Plan inverse(int rank, const int *n, int howmany, - ElementK *in, const int *inembed, int istride, int idist, - ElementX *out, const int *onembed, int ostride, int odist, - unsigned flags) { - return fftwf_plan_many_dft_c2r(rank, n, howmany, - reinterpret_cast(in), - inembed, istride, idist, - out, onembed, ostride, odist, - flags); - } - static inline void destroy(Plan p) { fftwf_destroy_plan(p); } - static inline void execute(Plan p) { fftwf_execute(p); } - static inline OwnerX allocateX(int n) { - return OwnerX( - reinterpret_cast( - fftwf_malloc(sizeof(ElementX)*n) - ), - fftwf_free - ); - } - static inline OwnerK allocateK(int n) { - return OwnerK( - reinterpret_cast( - fftwf_malloc(sizeof(ElementK)*n) - ), - fftwf_free - ); - } - }; - template <> struct FFTWTraits< std::complex > { - typedef fftwf_plan Plan; - typedef FourierTraits< std::complex >::ElementX ElementX; - typedef FourierTraits< std::complex >::ElementK ElementK; - typedef boost::shared_ptr OwnerX; - typedef boost::shared_ptr OwnerK; - static inline Plan forward(int rank, const int *n, int howmany, - ElementX *in, const int *inembed, int istride, int idist, - ElementK *out, const int *onembed, int ostride, int odist, - unsigned flags) { - return fftwf_plan_many_dft(rank, n, howmany, - reinterpret_cast(in), - inembed, istride, idist, - reinterpret_cast(out), - onembed, ostride, odist, - FFTW_FORWARD, flags); - } - static inline Plan inverse(int rank, const int *n, int howmany, - ElementK *in, const int *inembed, int istride, int idist, - ElementX *out, const int *onembed, int ostride, int odist, - unsigned flags) { - return fftwf_plan_many_dft(rank, n, howmany, - reinterpret_cast(in), - inembed, istride, idist, - reinterpret_cast(out), - onembed, ostride, odist, - FFTW_BACKWARD,flags); - } - static inline void destroy(Plan p) { fftwf_destroy_plan(p); } - static inline void execute(Plan p) { fftwf_execute(p); } - static inline OwnerX allocateX(int n) { - return OwnerX( - reinterpret_cast( - fftwf_malloc(sizeof(ElementX)*n) - ), - fftwf_free - ); - } - static inline OwnerK allocateK(int n) { - return OwnerK( - reinterpret_cast( - fftwf_malloc(sizeof(ElementK)*n) - ), - fftwf_free - ); - } - }; - - template <> struct FFTWTraits { - BOOST_STATIC_ASSERT((!boost::is_const::value)); - typedef fftw_plan Plan; - typedef FourierTraits::ElementX ElementX; - typedef FourierTraits::ElementK ElementK; - typedef boost::shared_ptr OwnerX; - typedef boost::shared_ptr OwnerK; - static inline Plan forward(int rank, const int *n, int howmany, - ElementX *in, const int *inembed, int istride, int idist, - ElementK *out, const int *onembed, int ostride, int odist, - unsigned flags) { - return fftw_plan_many_dft_r2c(rank, n, howmany, - in, inembed, istride, idist, - reinterpret_cast(out), - onembed, ostride, odist, - flags); - } - static inline Plan inverse(int rank, const int *n, int howmany, - ElementK *in, const int *inembed, int istride, int idist, - ElementX *out, const int *onembed, int ostride, int odist, - unsigned flags) { - return fftw_plan_many_dft_c2r(rank, n, howmany, - reinterpret_cast(in), - inembed, istride, idist, - out, onembed, ostride, odist, - flags); - } - static inline void destroy(Plan p) { fftw_destroy_plan(p); } - static inline void execute(Plan p) { fftw_execute(p); } - static inline OwnerX allocateX(int n) { - return OwnerX( - reinterpret_cast( - fftw_malloc(sizeof(ElementX)*n) - ), - fftw_free - ); - } - static inline OwnerK allocateK(int n) { - return OwnerK( - reinterpret_cast( - fftw_malloc(sizeof(ElementK)*n) - ), - fftw_free - ); - } - }; - template <> struct FFTWTraits< std::complex > { - typedef fftw_plan Plan; - typedef FourierTraits< std::complex >::ElementX ElementX; - typedef FourierTraits< std::complex >::ElementK ElementK; - typedef boost::shared_ptr OwnerX; - typedef boost::shared_ptr OwnerK; - static inline Plan forward(int rank, const int *n, int howmany, - ElementX *in, const int *inembed, int istride, int idist, - ElementK *out, const int *onembed, int ostride, int odist, - unsigned flags) { - return fftw_plan_many_dft(rank, n, howmany, - reinterpret_cast(in), - inembed, istride, idist, - reinterpret_cast(out), - onembed, ostride, odist, - FFTW_FORWARD, flags); - } - static inline Plan inverse(int rank, const int *n, int howmany, - ElementK *in, const int *inembed, int istride, int idist, - ElementX *out, const int *onembed, int ostride, int odist, - unsigned flags) { - return fftw_plan_many_dft(rank, n, howmany, - reinterpret_cast(in), - inembed, istride, idist, - reinterpret_cast(out), - onembed, ostride, odist, - FFTW_BACKWARD,flags); - } - static inline void destroy(Plan p) { fftw_destroy_plan(p); } - static inline void execute(Plan p) { fftw_execute(p); } - static inline OwnerX allocateX(int n) { - return OwnerX( - reinterpret_cast( - fftw_malloc(sizeof(ElementX)*n) - ), - fftw_free - ); - } - static inline OwnerK allocateK(int n) { - return OwnerK( - reinterpret_cast( - fftw_malloc(sizeof(ElementK)*n) - ), - fftw_free - ); - } - }; -#ifndef NDARRAY_FFT_NO_LONG_DOUBLE - - template <> struct FFTWTraits { - BOOST_STATIC_ASSERT((!boost::is_const::value)); - typedef fftwl_plan Plan; - typedef FourierTraits::ElementX ElementX; - typedef FourierTraits::ElementK ElementK; - typedef boost::shared_ptr OwnerX; - typedef boost::shared_ptr OwnerK; - static inline Plan forward(int rank, const int *n, int howmany, - ElementX *in, const int *inembed, int istride, int idist, - ElementK *out, const int *onembed, int ostride, int odist, - unsigned flags) { - return fftwl_plan_many_dft_r2c(rank, n, howmany, - in, inembed, istride, idist, - reinterpret_cast(out), - onembed, ostride, odist, - flags); - } - static inline Plan inverse(int rank, const int *n, int howmany, - ElementK *in, const int *inembed, int istride, int idist, - ElementX *out, const int *onembed, int ostride, int odist, - unsigned flags) { - return fftwl_plan_many_dft_c2r(rank, n, howmany, - reinterpret_cast(in), - inembed, istride, idist, - out, onembed, ostride, odist, - flags); - } - static inline void destroy(Plan p) { fftwl_destroy_plan(p); } - static inline void execute(Plan p) { fftwl_execute(p); } - static inline OwnerX allocateX(int n) { - return OwnerX( - reinterpret_cast( - fftwl_malloc(sizeof(ElementX)*n) - ), - fftwl_free - ); - } - static inline OwnerK allocateK(int n) { - return OwnerK( - reinterpret_cast( - fftwl_malloc(sizeof(ElementK)*n) - ), - fftwl_free - ); - } - }; - template <> struct FFTWTraits< std::complex > { - typedef fftwl_plan Plan; - typedef FourierTraits< std::complex >::ElementX ElementX; - typedef FourierTraits< std::complex >::ElementK ElementK; - typedef boost::shared_ptr OwnerX; - typedef boost::shared_ptr OwnerK; - static inline Plan forward(int rank, const int *n, int howmany, - ElementX *in, const int *inembed, int istride, int idist, - ElementK *out, const int *onembed, int ostride, int odist, - unsigned flags) { - return fftwl_plan_many_dft(rank, n, howmany, - reinterpret_cast(in), - inembed, istride, idist, - reinterpret_cast(out), - onembed, ostride, odist, - FFTW_FORWARD, flags); - } - static inline Plan inverse(int rank, const int *n, int howmany, - ElementK *in, const int *inembed, int istride, int idist, - ElementX *out, const int *onembed, int ostride, int odist, - unsigned flags) { - return fftwl_plan_many_dft(rank, n, howmany, - reinterpret_cast(in), - inembed, istride, idist, - reinterpret_cast(out), - onembed, ostride, odist, - FFTW_BACKWARD,flags); - } - static inline void destroy(Plan p) { fftwl_destroy_plan(p); } - static inline void execute(Plan p) { fftwl_execute(p); } - static inline OwnerX allocateX(int n) { - return OwnerX( - reinterpret_cast( - fftwl_malloc(sizeof(ElementX)*n) - ), - fftwl_free - ); - } - static inline OwnerK allocateK(int n) { - return OwnerK( - reinterpret_cast( - fftwl_malloc(sizeof(ElementK)*n) - ), - fftwl_free - ); - } - }; -#endif -/// \endcond - -} // namespace detail -/// \endcond -} // namespace ndarray - -#endif // !NDARRAY_FFT_FFTWTraits_h_INCLUDED diff --git a/include/ndarray/fft/FourierOps.h b/include/ndarray/fft/FourierOps.h deleted file mode 100644 index 6d3e8d9d..00000000 --- a/include/ndarray/fft/FourierOps.h +++ /dev/null @@ -1,168 +0,0 @@ -// -*- c++ -*- -/* - * Copyright (c) 2010-2012, Jim Bosch - * All rights reserved. - * - * ndarray is distributed under a simple BSD-like license; - * see the LICENSE file that should be present in the root - * of the source distribution, or alternately available at: - * https://github.com/ndarray/ndarray - */ -#ifndef NDARRAY_FFT_FourierOps_h_INCLUDED -#define NDARRAY_FFT_FourierOps_h_INCLUDED - -/** - * @file ndarray/fft/FourierOps.h - * - * @brief Common Fourier-space operations. - */ - -#include - -#include "ndarray.h" - -namespace ndarray { -/// \cond INTERNAL -namespace detail { - -#ifdef _MSC_VER -/** Portable double pi value. */ -static const double M_PI = 4. * atan(1.); -#endif - -/** - * @internal @ingroup FFTndarrayInternalGroup - * @brief Implementations for the shift() and differentiate() functions. - */ -template -struct FourierOps { - - template - static void shift( - T const * offset, - std::complex const & factor, - ArrayRef,N,C> const & array, - int const real_last_dim - ) { - typename ArrayRef,N,C>::Iterator iter = array.begin(); - T u = -2.0 * M_PI * (*offset) / array.size(); - int kMid = (array.size() + 1) / 2; - for (int k = 0; k < kMid; ++k, ++iter) { - FourierOps::shift(offset+1, factor * std::polar(static_cast(1), u * k), - *iter, real_last_dim); - } - if (array.size() % 2 == 0) { - FourierOps::shift(offset+1, factor * std::cos(u * kMid), *iter, real_last_dim); - ++iter; - ++kMid; - } - for (int k_n = kMid - array.size(); k_n < 0; ++k_n, ++iter) { - FourierOps::shift(offset+1, factor * std::polar(static_cast(1), u * k_n), - *iter, real_last_dim); - } - } - - template - static void differentiate(int m, ArrayRef,N,C> const & array, int const real_last_dim) { - typename ArrayRef,N,C>::Iterator iter = array.begin(); - int kMid = (array.size() + 1) / 2; - T u = 2.0 * M_PI / array.size(); - for (int k = 0; k < kMid; ++k, ++iter) { - if (m == N) (*iter) *= std::complex(static_cast(0), u * T(k)); - FourierOps::differentiate(m, *iter, real_last_dim); - } - if (array.size() % 2 == 0) { - (*iter) = static_cast(0); - ++iter; - ++kMid; - } - for (int k_n = kMid - array.size(); k_n < 0; ++k_n, ++iter) { - if (m == N) (*iter) *= std::complex(static_cast(0), u * T(k_n)); - FourierOps::differentiate(m, *iter, real_last_dim); - } - } - -}; - -/** - * @internal @ingroup FFTndarrayInternalGroup - * @brief Implementations for the shift() and differentiate() functions (1d specialization). - */ -template -struct FourierOps { - - template - static void shift( - T const * offset, - std::complex const & factor, - ArrayRef,1,C> const & array, - int const real_last_dim - ) { - typename ArrayRef,1,C>::Iterator iter = array.begin(); - T u = -2.0 * M_PI * (*offset) / real_last_dim; - int kMid = (real_last_dim + 1) / 2; - for (int k = 0; k < kMid; ++k, ++iter) { - (*iter) *= factor * std::polar(1.0, u * T(k)); - } - if (real_last_dim % 2 == 0) { - (*iter) *= factor * std::cos(u * kMid); - ++iter; - } - } - - template - static void differentiate(int m, ArrayRef,1,C> const & array, int const real_last_dim) { - typename ArrayRef,1,C>::Iterator iter = array.begin(); - int kMid = (real_last_dim + 1) / 2; - if (m == 1) { - T u = 2.0 * M_PI / real_last_dim; - for (int k = 0; k < kMid; ++k, ++iter) { - (*iter) *= std::complex(static_cast(0), u * T(k)); - } - } - if (real_last_dim % 2 == 0) { - array[kMid] = static_cast(0); - } - } - -}; - -} // namespace detail -/// \endcond - -/** - * @brief Perform a Fourier-space translation transform. - * - * @ingroup FFTGroup - */ -template -void shift( - Vector const & offset, - Array,N,C> const & array, - int const real_last_dim -) { - detail::FourierOps::shift( - offset.begin(), - static_cast< std::complex >(1), - array.deep(), - real_last_dim - ); -} - -/** - * @brief Numerically differentiate the array in Fourier-space in the given dimension. - * - * @ingroup FFTGroup - */ -template -void differentiate( - int n, - Array,N,C> const & array, - int const real_last_dim -) { - detail::FourierOps::differentiate(N-n, array.deep(), real_last_dim); -} - -} // namespace ndarray - -#endif // !NDARRAY_FFT_FourierOps_h_INCLUDED diff --git a/include/ndarray/fft/FourierTraits.h b/include/ndarray/fft/FourierTraits.h deleted file mode 100644 index 255e78d5..00000000 --- a/include/ndarray/fft/FourierTraits.h +++ /dev/null @@ -1,86 +0,0 @@ -// -*- c++ -*- -/* - * Copyright (c) 2010-2012, Jim Bosch - * All rights reserved. - * - * ndarray is distributed under a simple BSD-like license; - * see the LICENSE file that should be present in the root - * of the source distribution, or alternately available at: - * https://github.com/ndarray/ndarray - */ -#ifndef NDARRAY_FFT_FourierTraits_h_INCLUDED -#define NDARRAY_FFT_FourierTraits_h_INCLUDED - -/** - * @file ndarray/fft/FourierTraits.h - * - * @brief Traits classes to handle real-data and complex-data FFTs in a template-friendly way. - */ - -#include -#include - -#include "ndarray/fft_fwd.h" - -namespace ndarray { -/// \cond INTERNAL -namespace detail { - -/** - * @internal @ingroup FFTndarrayInternalGroup - * @brief A traits class that defines x- and k-space data types and k-space array sizes. - */ -template -struct FourierTraits { - BOOST_STATIC_ASSERT(sizeof(T) < 0); -}; - -/// \cond SPECIALIZATIONS - -template -struct FourierTraits { - typedef T ElementX; - typedef T ValueX; - typedef std::complex ElementK; - typedef std::complex ValueK; - - static inline Size computeLastDimensionSize(Size n) { return n/2 + 1; } -}; - -template -struct FourierTraits { - typedef T ElementX; - typedef typename boost::remove_const::type ValueX; - typedef std::complex ValueK; - typedef ValueK const ElementK; - - static inline Size computeLastDimensionSize(Size n) { return n/2 + 1; } -}; - -template -struct FourierTraits,false> { - typedef std::complex ElementX; - typedef std::complex ElementK; - typedef std::complex ValueX; - typedef std::complex ValueK; - - static inline Size computeLastDimensionSize(Size n) { return n; } -}; - -template -struct FourierTraits const,true> { - typedef std::complex const ElementX; - typedef std::complex const ElementK; - typedef std::complex ValueX; - typedef std::complex ValueK; - - static inline Size computeLastDimensionSize(Size n) { return n; } -}; - -/// \endcond - -} // namespace detail -/// \endcond -} // namespace ndarray - -#endif // !NDARRAY_FFT_FourierTraits_h_INCLUDED diff --git a/include/ndarray/fft/FourierTransform.cc b/include/ndarray/fft/FourierTransform.cc deleted file mode 100644 index eeedbfa2..00000000 --- a/include/ndarray/fft/FourierTransform.cc +++ /dev/null @@ -1,154 +0,0 @@ -// -*- c++ -*- -/* - * Copyright (c) 2010-2012, Jim Bosch - * All rights reserved. - * - * ndarray is distributed under a simple BSD-like license; - * see the LICENSE file that should be present in the root - * of the source distribution, or alternately available at: - * https://github.com/ndarray/ndarray - */ -#include "ndarray/fft/FFTWTraits.h" -#include "ndarray/fft/FourierTransform.h" - -namespace ndarray { - -template -template -Array::ElementX,M,M> -FourierTransform::initializeX(Vector const & shape) { - OwnerX xOwner = detail::FFTWTraits::allocateX(shape.product()); - return Array(external(xOwner.get(), shape, ROW_MAJOR, xOwner)); -} - -template -template -Array::ElementK,M,M> -FourierTransform::initializeK(Vector const & shape) { - Vector kShape(shape); - kShape[M-1] = detail::FourierTraits::computeLastDimensionSize(shape[M-1]); - OwnerK kOwner = detail::FFTWTraits::allocateK(kShape.product()); - return Array(external(kOwner.get(), kShape, ROW_MAJOR, kOwner)); -} - -template -template -void -FourierTransform::initialize( - Vector const & shape, - Array & x, - Array & k -) { - if (x.empty()) x = initializeX(shape); - if (k.empty()) k = initializeK(shape); - NDARRAY_ASSERT(x.getShape() == shape); - NDARRAY_ASSERT(std::equal(shape.begin(), shape.end()-1, k.getShape().begin())); -} - -template -typename FourierTransform::Ptr -FourierTransform::planForward( - Index const & shape, - typename FourierTransform::ArrayX & x, - typename FourierTransform::ArrayK & k -) { - initialize(shape,x,k); - Vector s = shape.template cast(); - return Ptr( - new FourierTransform( - detail::FFTWTraits::forward( - N, s.begin(), 1, - x.getData(), NULL, 1, 0, - k.getData(), NULL, 1, 0, - FFTW_MEASURE | FFTW_DESTROY_INPUT - ), - x.getManager(), - k.getManager() - ) - ); -} - -template -typename FourierTransform::Ptr -FourierTransform::planInverse( - Index const & shape, - typename FourierTransform::ArrayK & k, - typename FourierTransform::ArrayX & x -) { - initialize(shape,x,k); - Vector s = shape.template cast(); - return Ptr( - new FourierTransform( - detail::FFTWTraits::inverse( - N, s.begin(), 1, - k.getData(), NULL, 1, 0, - x.getData(), NULL, 1, 0, - FFTW_MEASURE | FFTW_DESTROY_INPUT - ), - x.getManager(), - k.getManager() - ) - ); -} - -template -typename FourierTransform::Ptr -FourierTransform::planMultiplexForward( - MultiplexIndex const & shape, - typename FourierTransform::MultiplexArrayX & x, - typename FourierTransform::MultiplexArrayK & k -) { - initialize(shape,x,k); - Vector s = shape.template cast(); - return Ptr( - new FourierTransform( - detail::FFTWTraits::forward( - N, s.begin()+1, s[0], - x.getData(), NULL, 1, x.template getStride<0>(), - k.getData(), NULL, 1, k.template getStride<0>(), - FFTW_MEASURE | FFTW_DESTROY_INPUT - ), - x.getManager(), - k.getManager() - ) - ); -} - -template -typename FourierTransform::Ptr -FourierTransform::planMultiplexInverse( - MultiplexIndex const & shape, - typename FourierTransform::MultiplexArrayK & k, - typename FourierTransform::MultiplexArrayX & x -) { - initialize(shape,x,k); - Vector s = shape.template cast(); - return Ptr( - new FourierTransform( - detail::FFTWTraits::inverse( - N, s.begin()+1, s[0], - k.getData(), NULL, 1, k.template getStride<0>(), - x.getData(), NULL, 1, x.template getStride<0>(), - FFTW_MEASURE | FFTW_DESTROY_INPUT - ), - x.getManager(), - k.getManager() - ) - ); -} - -template -void FourierTransform::execute() { - detail::FFTWTraits::execute( - reinterpret_cast::Plan>(_plan) - ); -} - -template -FourierTransform::~FourierTransform() { - detail::FFTWTraits::destroy( - reinterpret_cast::Plan>(_plan) - ); -} - -} // namespace ndarray diff --git a/include/ndarray/fft/FourierTransform.h b/include/ndarray/fft/FourierTransform.h deleted file mode 100644 index 678cebbe..00000000 --- a/include/ndarray/fft/FourierTransform.h +++ /dev/null @@ -1,141 +0,0 @@ -// -*- c++ -*- -/* - * Copyright (c) 2010-2012, Jim Bosch - * All rights reserved. - * - * ndarray is distributed under a simple BSD-like license; - * see the LICENSE file that should be present in the root - * of the source distribution, or alternately available at: - * https://github.com/ndarray/ndarray - */ -#ifndef NDARRAY_FFT_FourierTransform_h_INCLUDED -#define NDARRAY_FFT_FourierTransform_h_INCLUDED - -/** - * @file ndarray/fft/FourierTransform.h - * - * @brief Definitions for FourierTransform. - */ - -#include - -#include "ndarray.h" -#include "ndarray/fft/FourierTraits.h" - -namespace ndarray { - -/** - * @ingroup FFTGroup - * @brief A wrapper for FFTW plans for fast Fourier transforms. - * - * An instance of FourierTransform holds an FFTW "plan", providing repeated forward or - * inverse FFTs of predetermined arrays. - * - * Multiplex plans can also be generated to perform an N-dimensional FFT on the nested arrays - * of an (N+1)-dimensional array. - * - * Static member functions of FourierTransform are used to create instances, and optionally - * initialize the involved arrays. - * - */ -template -class FourierTransform : private boost::noncopyable { - BOOST_STATIC_ASSERT((!boost::is_const::value)); -public: - - typedef boost::shared_ptr Ptr; - - typedef typename detail::FourierTraits::ElementX ElementX; ///< Real-space array data type; - typedef typename detail::FourierTraits::ElementK ElementK; ///< Fourier-space array data type; - - typedef Vector Index; ///< Shape type for arrays. - typedef Array ArrayX; ///< Real-space array type. - typedef Array ArrayK; ///< Fourier-space array type. - typedef Vector MultiplexIndex; ///< Shape type for multiplexed arrays. - typedef Array MultiplexArrayX; ///< Real-space multiplexed array type. - typedef Array MultiplexArrayK; ///< Fourier-space multiplexed array type. - - /** - * @brief Create a plan for forward-transforming a single N-dimensional array. - * - * Arrays will be initialized with new memory if empty. If they are not empty, - * existing data may be overwritten when the plan is created. - */ - static Ptr planForward( - Index const & shape, ///< Shape of the real-space array. - ArrayX & x, ///< Input real-space array. - ArrayK & k ///< Output Fourier-space array. - ); - - /** - * @brief Create a plan for inverse-transforming a single N-dimensional array. - * - * Arrays will be initialized with new memory if empty. If they are not empty, - * existing data may be overwritten when the plan is created. - */ - static Ptr planInverse( - Index const & shape, ///< Shape of the real-space array. - ArrayK & k, ///< Input Fourier-space array. - ArrayX & x ///< Output real-space array. - ); - - /** - * @brief Create a plan for forward-transforming a sequence of nested N-dimensional arrays. - * - * Arrays will be initialized with new memory if empty. If they are not empty, - * existing data may be overwritten when the plan is created. - */ - static Ptr planMultiplexForward( - MultiplexIndex const & shape, ///< Shape of the real-space array. First dimension is multiplexed. - MultiplexArrayX & x, ///< Input real-space array. - MultiplexArrayK & k ///< Output Fourier-space array. - ); - - /** - * @brief Create a plan for inverse-transforming a sequence of nested N-dimensional arrays. - * - * Arrays will be initialized with new memory if empty. If they are not empty, - * existing data may be overwritten when the plan is created. - */ - static Ptr planMultiplexInverse( - MultiplexIndex const & shape, ///< Shape of the real-space array. First dimension is multiplexed. - MultiplexArrayK & k, ///< Input Fourier-space array. - MultiplexArrayX & x ///< Output real-space array. - ); - - /// @brief Create a new real-space array with the given real-space shape. - template - static Array initializeX(Vector const & shape); - - /// @brief Create a new Fourier-space array with the given real-space shape. - template - static Array initializeK(Vector const & shape); - - /** - * @brief Initialize, as necessary, a pair of arrays with the given real-space shape. - * - * If either array is not empty, it must be consistent with the given shape. - */ - template - static void initialize(Vector const & shape, Array & x, Array & k); - - /// @brief Execute the FFTW plan. - void execute(); - - ~FourierTransform(); - -private: - typedef boost::shared_ptr OwnerX; - typedef boost::shared_ptr OwnerK; - - FourierTransform(void * plan, Manager::Ptr const & x, Manager::Ptr const & k) - : _plan(plan), _x(x), _k(k) {} - - void * _plan; // 'void' so we don't have to include fftw3.h in the header file - Manager::Ptr _x; - Manager::Ptr _k; -}; - -} // namespace ndarray - -#endif // !NDARRAY_FFT_FourierTransform_h_INCLUDED diff --git a/include/ndarray/fft_fwd.h b/include/ndarray/fft_fwd.h deleted file mode 100644 index 7338c84f..00000000 --- a/include/ndarray/fft_fwd.h +++ /dev/null @@ -1,44 +0,0 @@ -// -*- c++ -*- -/* - * Copyright (c) 2010-2012, Jim Bosch - * All rights reserved. - * - * ndarray is distributed under a simple BSD-like license; - * see the LICENSE file that should be present in the root - * of the source distribution, or alternately available at: - * https://github.com/ndarray/ndarray - */ -#ifndef NDARRAY_FFT_fft_fwd_h_INCLUDED -#define NDARRAY_FFT_fft_fwd_h_INCLUDED - -/** - * @file ndarray/fft_fwd.h - * - * @brief Forward declarations and default template parameters for ndarray/fft. - * - * \note This file is not included by the main "ndarray.h" header file. - */ - -/** - * \defgroup FFTGroup Fourier Transforms - * - * @brief Fast fourier transforms using the FFTW library. - */ - -/// @internal \defgroup FFTndarrayInternalGroup Fourier Transform Internals - -#include "ndarray_fwd.h" - -namespace ndarray { -namespace detail { - -template ::value> struct FourierTraits; -template struct FFTWTraits; - -} // namespace detail - -template class FourierTransform; - -} // namespace ndarray - -#endif // !NDARRAY_FFT_fft_fwd_h_INCLUDED diff --git a/include/ndarray/formatting.h b/include/ndarray/formatting.h deleted file mode 100644 index d716033e..00000000 --- a/include/ndarray/formatting.h +++ /dev/null @@ -1,153 +0,0 @@ -// -*- c++ -*- -/* - * Copyright (c) 2010-2012, Jim Bosch - * All rights reserved. - * - * ndarray is distributed under a simple BSD-like license; - * see the LICENSE file that should be present in the root - * of the source distribution, or alternately available at: - * https://github.com/ndarray/ndarray - */ -#ifndef NDARRAY_formatting_h_INCLUDED -#define NDARRAY_formatting_h_INCLUDED - -/** - * @file ndarray/formatting.h - * - * @brief iostream output support for Expression. - */ - -#include "ndarray/ExpressionBase.h" - -#include -#include - -namespace ndarray { -namespace detail { -template class Formatter; -} // namespace detail - -/** - * @class FormatOptions - * @ingroup MainGroup - * @brief Options for controlling stream output of ExpressionBase. - */ -class FormatOptions { - int _width; - int _precision; - std::ios_base::fmtflags _flags; - std::string _delimiter; - std::string _open; - std::string _close; -public: - - /// @brief Standard constructor. - explicit FormatOptions( - int width = 8, - int precision = 6, - std::ios_base::fmtflags flags = std::ios_base::fmtflags(0), - std::string const & delimiter = ", ", - std::string const & open = "[", - std::string const & close = "]" - ) : - _width(width), - _precision(precision), - _flags(flags), - _delimiter(delimiter), - _open(open), - _close(close) - {} - - /// @brief Format the given expression into the given output stream. - template - void apply(std::ostream & os, ExpressionBase const & expr) { - detail::Formatter::apply(*this,os,expr,0); - } - - template friend class detail::Formatter; -}; - -/// @brief Stream output for ExpressionBase using default-constructed FormatOptions. -template -std::ostream & operator<<(std::ostream & os, ExpressionBase const & expr) { - FormatOptions options; - options.apply(os,expr); - return os; -} - -namespace detail { - -/// @brief Stream char as integer rather than as a nonprintable character -inline std::ostream &operator<<(std::ostream &os, std::int8_t value) { - return os << static_cast(value); -} - -/// @brief Stream uchar as integer rather than as a nonprintable character -inline std::ostream &operator<<(std::ostream &os, std::uint8_t value) { - return os << static_cast(value); -} - -/** - * @internal @ingroup ndarrayInternalGroup - * @brief Recursive metafunction used in stream output. - */ -template -class Formatter { -public: - static void apply( - FormatOptions const & options, - std::ostream & os, - ExpressionBase const & expr, - int level - ) { - os << options._open; - if (!expr.empty()) { - typename ExpressionBase::Iterator const end = expr.end(); - typename ExpressionBase::Iterator iter = expr.begin(); - Formatter::Reference>::apply(options,os,*iter,level+1); - for (++iter; iter != end; ++iter) { - os << options._delimiter; - os << std::endl << std::string(level,' '); - Formatter::Reference>::apply(options,os,*iter,level+1); - } - } - os << options._close; - } -}; - -/** - * @internal @ingroup ndarrayInternalGroup - * @brief Recursive metafunction used in stream output (1d specialization). - */ -template -class Formatter { -public: - static void apply( - FormatOptions const & options, - std::ostream & os, - ExpressionBase const & expr, - int level - ) { - os << options._open; - if (!expr.empty()) { - typename ExpressionBase::Iterator const end = expr.end(); - typename ExpressionBase::Iterator iter = expr.begin(); - int precision = os.precision(options._precision); - int width = os.width(options._width); - std::ios_base::fmtflags flags = os.setf(options._flags,std::ios_base::floatfield); - os << (*iter); - for (++iter; iter != end; ++iter) { - os << options._delimiter << (*iter); - } - os.precision(precision); - os.width(width); - os.setf(flags); - } - os << options._close; - } -}; - -} // namespace detail -} // namespace ndarray - -#endif // !NDARRAY_formatting_h_INCLUDED diff --git a/include/ndarray/initialization.h b/include/ndarray/initialization.h deleted file mode 100644 index ad0b8b75..00000000 --- a/include/ndarray/initialization.h +++ /dev/null @@ -1,309 +0,0 @@ -// -*- c++ -*- -/* - * Copyright (c) 2010-2012, Jim Bosch - * All rights reserved. - * - * ndarray is distributed under a simple BSD-like license; - * see the LICENSE file that should be present in the root - * of the source distribution, or alternately available at: - * https://github.com/ndarray/ndarray - */ -#ifndef NDARRAY_initialization_h_INCLUDED -#define NDARRAY_initialization_h_INCLUDED - -/** - * \file ndarray/initialization.h @brief Construction functions for array. - */ - -#include "ndarray/Array.h" -#include "ndarray/ArrayRef.h" -#include "ndarray/Manager.h" - -namespace ndarray { -namespace detail { - -struct NullOwner {}; - -template -class Initializer { -public: - - template - operator Array () const { - return static_cast(this)->template apply< Array >(); - } - - template - operator ArrayRef () const { - return static_cast(this)->template apply< ArrayRef >(); - } - -}; - -template -class SimpleInitializer : public Initializer< N, SimpleInitializer > { -public: - - template - Target apply() const { - typedef detail::ArrayAccess< Target > Access; - typedef typename Access::Core Core; - typedef typename Access::Element Element; - DataOrderEnum order = (ExpressionTraits< Target >::RMC::value < 0) ? COLUMN_MAJOR : ROW_MAJOR; - Size total = _shape.product(); - std::pair p = SimpleManager::allocate(total); - return Access::construct(p.second, Core::create(_shape, order, p.first)); - } - - explicit SimpleInitializer(Vector const & shape) : _shape(shape) {} - -private: - Vector _shape; -}; - -template -class ExternalInitializer : public Initializer< N, ExternalInitializer > { -public: - - template - Target apply() const { - typedef detail::ArrayAccess< Target > Access; - typedef typename Access::Core Core; - Manager::Ptr manager; - if (!boost::is_same::value) { - manager = makeManager(_owner); - } - return Access::construct(_data, Core::create(_shape, _strides, manager)); - } - - ExternalInitializer( - T * data, - Vector const & shape, - Vector const & strides, - Owner const & owner - ) : _data(data), _owner(owner), _shape(shape), _strides(strides) {} - -private: - T * _data; - Owner _owner; - Vector _shape; - Vector _strides; -}; - -} // namespace detail - -/// @addtogroup MainGroup -/// @{ - -/** - * @brief Create an expression that allocates uninitialized memory for an array. - * - * @returns A temporary object convertible to an Array with fully contiguous row-major strides. - */ -template -inline detail::SimpleInitializer allocate(Vector const & shape) { - return detail::SimpleInitializer(Vector(shape)); -} - -/** - * @brief Create an expression that allocates uninitialized memory for a 1-d array. - * - * @returns A temporary object convertible to an Array with fully contiguous row-major strides. - */ -inline detail::SimpleInitializer<1> allocate(Size n) { - return detail::SimpleInitializer<1>(ndarray::makeVector(n)); -} - -/** - * @brief Create an expression that allocates uninitialized memory for a 2-d array. - * - * @returns A temporary object convertible to an Array with fully contiguous row-major strides. - */ -inline detail::SimpleInitializer<2> allocate(Size n1, Size n2) { - return detail::SimpleInitializer<2>(ndarray::makeVector(n1, n2)); -} - -/** - * @brief Create an expression that allocates uninitialized memory for a 3-d array. - * - * @returns A temporary object convertible to an Array with fully contiguous row-major strides. - */ -inline detail::SimpleInitializer<3> allocate(Size n1, Size n2, Size n3) { - return detail::SimpleInitializer<3>(ndarray::makeVector(n1, n2, n3)); -} - -/** - * @brief Create a new Array by copying an Expression. - */ -template -inline ArrayRef::type, - Derived::ND::value, Derived::ND::value> -copy(ExpressionBase const & expr) { - ArrayRef::type, - Derived::ND::value,Derived::ND::value> r( - allocate(expr.getShape()) - ); - r = expr; - return r; -} - -/// @brief Compute row- or column-major strides for the given shape. -template -Vector computeStrides(Vector const & shape, DataOrderEnum order=ROW_MAJOR) { - Vector r(1); - if (order == ROW_MAJOR) { - for (int n=N-1; n > 0; --n) r[n-1] = r[n] * shape[n]; - } else { - for (int n=1; n < N; ++n) r[n] = r[n-1] * shape[n-1]; - } - return r; -} - -/** - * @brief Create an expression that initializes an Array with externally allocated memory. - * - * No checking is done to ensure the shape, strides, and data pointers are sensible. - * - * @param[in] data A raw pointer to the first element of the Array. - * @param[in] shape A Vector of dimensions for the new Array. - * @param[in] strides A Vector of strides for the new Array. - * @param[in] owner A copy-constructable object with an internal reference count - * that owns the memory pointed at by 'data'. - * - * @returns A temporary object convertible to an Array. - */ -template -inline detail::ExternalInitializer external( - T * data, - Vector const & shape, - Vector const & strides, - Owner const & owner -) { - return detail::ExternalInitializer( - data, - Vector(shape.template cast()), - Vector(strides.template cast()), - owner - ); -} - -/** - * @brief Create an expression that initializes an Array with externally allocated memory. - * - * No checking is done to ensure the shape, strides, and data pointers are sensible. Memory will not - * be managed at all; the user must ensure the data pointer remains valid for the lifetime of the array. - * - * @param[in] data A raw pointer to the first element of the Array. - * @param[in] shape A Vector of dimensions for the new Array. - * @param[in] strides A Vector of strides for the new Array. - * - * @returns A temporary object convertible to an Array. - */ -template -inline detail::ExternalInitializer external( - T * data, - Vector const & shape, - Vector const & strides -) { - return detail::ExternalInitializer( - data, - Vector(shape), - Vector(strides), - detail::NullOwner() - ); -} - -/** - * @brief Create an expression that initializes an Array with externally allocated memory. - * - * No checking is done to ensure the shape and data pointers are sensible. - * - * @param[in] data A raw pointer to the first element of the Array. - * @param[in] shape A Vector of dimensions for the new Array. - * @param[in] order Whether the strides are row- or column-major. - * @param[in] owner A copy-constructable object with an internal reference count - * that owns the memory pointed at by 'data'. - * - * @returns A temporary object convertible to an Array. - */ -template -inline detail::ExternalInitializer external( - T * data, - Vector const & shape, - DataOrderEnum order, - Owner const & owner -) { - return detail::ExternalInitializer( - data, - Vector(shape.template cast()), - computeStrides(shape.template cast(), order), - owner - ); -} - -/** - * @brief Create an expression that initializes an Array with externally allocated memory. - * - * No checking is done to ensure the shape and data pointers are sensible. Memory will not - * be managed at all; the user must ensure the data pointer remains valid for the lifetime of the array. - * - * @param[in] data A raw pointer to the first element of the Array. - * @param[in] shape A Vector of dimensions for the new Array. - * @param[in] order Whether the strides are row- or column-major. - * - * @returns A temporary object convertible to an Array. - */ -template -inline detail::ExternalInitializer external( - T * data, - Vector const & shape, - DataOrderEnum order = ROW_MAJOR -) { - return detail::ExternalInitializer( - data, - Vector(shape.template cast()), - computeStrides(shape.template cast(), order), - detail::NullOwner() - ); -} - -/// @} - -template -Array::Array(Size n1, Size n2, Size n3, Size n4, Size n5, Size n6, Size n7, Size n8) - : Super(0, CorePtr()) -{ - typename Super::Index shape; - if (N > 0) shape[0] = n1; - if (N > 1) shape[1] = n2; - if (N > 2) shape[2] = n3; - if (N > 3) shape[3] = n4; - if (N > 4) shape[4] = n5; - if (N > 5) shape[5] = n6; - if (N > 6) shape[6] = n7; - if (N > 7) shape[7] = n8; - this->operator=(ndarray::allocate(shape)); -} - -template -template -Array::Array(Vector const & shape) - : Super(0, CorePtr()) -{ - this->operator=(ndarray::allocate(shape.template cast())); -} - -template -ArrayRef::ArrayRef(Size n1, Size n2, Size n3, Size n4, Size n5, Size n6, Size n7, Size n8) - : Super(Array(n1, n2, n3, n4, n5, n6, n7, n8)) -{} - -template -template -ArrayRef::ArrayRef(Vector const & shape) - : Super(Array(shape)) -{} - -} // namespace ndarray - -#endif // !NDARRAY_initialization_h_INCLUDED diff --git a/include/ndarray/operators.h b/include/ndarray/operators.h deleted file mode 100644 index 5ff06cbe..00000000 --- a/include/ndarray/operators.h +++ /dev/null @@ -1,1318 +0,0 @@ -// -*- c++ -*- -/* - * Copyright (c) 2010-2012, Jim Bosch - * All rights reserved. - * - * ndarray is distributed under a simple BSD-like license; - * see the LICENSE file that should be present in the root - * of the source distribution, or alternately available at: - * https://github.com/ndarray/ndarray - */ - -#ifndef NDARRAY_operators_h_INCLUDED -#define NDARRAY_operators_h_INCLUDED - -/** - * \file ndarray/operators.h \brief Arithmetic and logical operators for Array. - */ - -#include "ndarray/Array.h" -#include -#include - -#include "ndarray/detail/UnaryOp.h" -#include "ndarray/detail/BinaryOp.h" -#include "ndarray/types.h" - -namespace ndarray { - -/// \cond INTERNAL -namespace detail { - -/** - * \internal @class PromotingBinaryFunction - * \brief A typedef-providing base class for binary functors with numeric type promotion. - * - * \ingroup InternalGroup - */ -template -struct PromotingBinaryFunction { - typedef A ElementA; - typedef B ElementB; - typedef A first_argument_type; - typedef B second_argument_type; - typedef typename boost::call_traits::param_type ParamA; - typedef typename boost::call_traits::param_type ParamB; - typedef typename Promote::Type result_type; -}; - -/** - * \internal @class BinaryPredicate - * \brief A typedef-providing base class for binary predicates. - * - * \ingroup InternalGroup - */ -template -struct BinaryPredicate { - typedef A ElementA; - typedef B ElementB; - typedef A first_argument_type; - typedef B second_argument_type; - typedef typename boost::call_traits::param_type ParamA; - typedef typename boost::call_traits::param_type ParamB; - typedef bool result_type; -}; - -// Local helper classes to avoid using the Boost ones, -// which inherit from boost::unary_function and boost::binary_function -// Boost might use the std implementations underneath, -// which are removed in the C++17 standard, but are still supported by some compilers -// in C++20 - -template -struct binary_traits -{ - typedef Operation function_type; - typedef const function_type & param_type; - typedef typename Operation::result_type result_type; - typedef typename Operation::first_argument_type first_argument_type; - typedef typename Operation::second_argument_type second_argument_type; -}; - -template -class _binder1st { -public: - using result_type = typename binary_traits::result_type; - - _binder1st(typename binary_traits::param_type x, - typename boost::call_traits::first_argument_type>::param_type y) - : - op(x), value(y) {} - - typename binary_traits::result_type - operator()( - typename boost::call_traits::second_argument_type>::param_type x) const { - return op(value, x); - } - -protected: - typename binary_traits::function_type op; - typename binary_traits::first_argument_type value; -}; - -template -class _binder2nd { -public: - using result_type = typename binary_traits::result_type; - - _binder2nd(typename binary_traits::param_type x, - typename boost::call_traits::second_argument_type>::param_type y) - : - op(x), value(y) {} - - typename binary_traits::result_type - operator()( - typename boost::call_traits::first_argument_type>::param_type x) const { - return op(x, value); - } - -protected: - typename binary_traits::function_type op; - typename binary_traits::second_argument_type value; -}; - -/** - * \internal @class AdaptableFunctionTag - * \brief A CRTP base class for non-template classes that contain a templated functor. - * - * \ingroup InternalGroup - */ -template -struct AdaptableFunctionTag { - - template - struct ScalarExpr { - typedef typename Derived::template ScalarFunction< - A, typename ExpressionTraits::Element - > BinaryFunction; - typedef _binder1st Bound; - static Bound bind(A const & scalar) { - return Bound(BinaryFunction(),scalar); - } - }; - - template - struct ExprScalar { - typedef typename Derived::template ScalarFunction< - typename ExpressionTraits::Element, B - > BinaryFunction; - typedef _binder2nd Bound; - static Bound bind(B const & scalar) { - return Bound(BinaryFunction(),scalar); - } - }; - - template - struct ExprExpr { - typedef typename Derived::template ScalarFunction< - typename ExpressionTraits::Element, - typename ExpressionTraits::Element - > BinaryFunction; - }; - -}; - -/** - * \internal @class BitwiseNot - * \ingroup InternalGroup - * \brief An STL Unary Function class for bitwise NOT (unary ~). - */ -template -struct BitwiseNot { - typedef T argument_type; - typedef T result_type; - - result_type operator()(argument_type arg) const { return ~arg; } -}; - - - struct PlusTag : public AdaptableFunctionTag { - template - struct ScalarFunction : public PromotingBinaryFunction { - typename PromotingBinaryFunction::result_type operator()( - typename PromotingBinaryFunction::ParamA a, - typename PromotingBinaryFunction::ParamB b - ) const { - return a + b; - } - }; - }; - - struct MinusTag : public AdaptableFunctionTag { - template - struct ScalarFunction : public PromotingBinaryFunction { - typename PromotingBinaryFunction::result_type operator()( - typename PromotingBinaryFunction::ParamA a, - typename PromotingBinaryFunction::ParamB b - ) const { - return a - b; - } - }; - }; - - struct MultipliesTag : public AdaptableFunctionTag { - template - struct ScalarFunction : public PromotingBinaryFunction { - typename PromotingBinaryFunction::result_type operator()( - typename PromotingBinaryFunction::ParamA a, - typename PromotingBinaryFunction::ParamB b - ) const { - return a * b; - } - }; - }; - - struct DividesTag : public AdaptableFunctionTag { - template - struct ScalarFunction : public PromotingBinaryFunction { - typename PromotingBinaryFunction::result_type operator()( - typename PromotingBinaryFunction::ParamA a, - typename PromotingBinaryFunction::ParamB b - ) const { - return a / b; - } - }; - }; - - struct ModulusTag : public AdaptableFunctionTag { - template - struct ScalarFunction : public PromotingBinaryFunction { - typename PromotingBinaryFunction::result_type operator()( - typename PromotingBinaryFunction::ParamA a, - typename PromotingBinaryFunction::ParamB b - ) const { - return a % b; - } - }; - }; - - struct BitwiseXorTag : public AdaptableFunctionTag { - template - struct ScalarFunction : public PromotingBinaryFunction { - typename PromotingBinaryFunction::result_type operator()( - typename PromotingBinaryFunction::ParamA a, - typename PromotingBinaryFunction::ParamB b - ) const { - return a ^ b; - } - }; - }; - - struct BitwiseOrTag : public AdaptableFunctionTag { - template - struct ScalarFunction : public PromotingBinaryFunction { - typename PromotingBinaryFunction::result_type operator()( - typename PromotingBinaryFunction::ParamA a, - typename PromotingBinaryFunction::ParamB b - ) const { - return a | b; - } - }; - }; - - struct BitwiseAndTag : public AdaptableFunctionTag { - template - struct ScalarFunction : public PromotingBinaryFunction { - typename PromotingBinaryFunction::result_type operator()( - typename PromotingBinaryFunction::ParamA a, - typename PromotingBinaryFunction::ParamB b - ) const { - return a & b; - } - }; - }; - - struct BitwiseLeftShiftTag : public AdaptableFunctionTag { - template - struct ScalarFunction : public PromotingBinaryFunction { - typename PromotingBinaryFunction::result_type operator()( - typename PromotingBinaryFunction::ParamA a, - typename PromotingBinaryFunction::ParamB b - ) const { - return a << b; - } - }; - }; - - struct BitwiseRightShiftTag : public AdaptableFunctionTag { - template - struct ScalarFunction : public PromotingBinaryFunction { - typename PromotingBinaryFunction::result_type operator()( - typename PromotingBinaryFunction::ParamA a, - typename PromotingBinaryFunction::ParamB b - ) const { - return a >> b; - } - }; - }; - - - struct EqualToTag : public AdaptableFunctionTag { - template - struct ScalarFunction : public BinaryPredicate { - typename BinaryPredicate::result_type operator()( - typename BinaryPredicate::ParamA a, - typename BinaryPredicate::ParamB b - ) const { - return a == b; - } - }; - }; - - struct NotEqualToTag : public AdaptableFunctionTag { - template - struct ScalarFunction : public BinaryPredicate { - typename BinaryPredicate::result_type operator()( - typename BinaryPredicate::ParamA a, - typename BinaryPredicate::ParamB b - ) const { - return a != b; - } - }; - }; - - struct LessTag : public AdaptableFunctionTag { - template - struct ScalarFunction : public BinaryPredicate { - typename BinaryPredicate::result_type operator()( - typename BinaryPredicate::ParamA a, - typename BinaryPredicate::ParamB b - ) const { - return a < b; - } - }; - }; - - struct GreaterTag : public AdaptableFunctionTag { - template - struct ScalarFunction : public BinaryPredicate { - typename BinaryPredicate::result_type operator()( - typename BinaryPredicate::ParamA a, - typename BinaryPredicate::ParamB b - ) const { - return a > b; - } - }; - }; - - struct LessEqualTag : public AdaptableFunctionTag { - template - struct ScalarFunction : public BinaryPredicate { - typename BinaryPredicate::result_type operator()( - typename BinaryPredicate::ParamA a, - typename BinaryPredicate::ParamB b - ) const { - return a <= b; - } - }; - }; - - struct GreaterEqualTag : public AdaptableFunctionTag { - template - struct ScalarFunction : public BinaryPredicate { - typename BinaryPredicate::result_type operator()( - typename BinaryPredicate::ParamA a, - typename BinaryPredicate::ParamB b - ) const { - return a >= b; - } - }; - }; - - struct LogicalAnd : public AdaptableFunctionTag { - template - struct ScalarFunction : public BinaryPredicate { - typename BinaryPredicate::result_type operator()( - typename BinaryPredicate::ParamA a, - typename BinaryPredicate::ParamB b - ) const { - return a && b; - } - }; - }; - - struct LogicalOr : public AdaptableFunctionTag { - template - struct ScalarFunction : public BinaryPredicate { - typename BinaryPredicate::result_type operator()( - typename BinaryPredicate::ParamA a, - typename BinaryPredicate::ParamB b - ) const { - return a || b; - } - }; - }; - -} // namespace detail -/// \endcond - -/// \addtogroup ndarrayOpGroup -/// @{ - - template -#ifndef DOXYGEN - typename boost::enable_if< - typename ExpressionTraits::IsScalar, - detail::UnaryOpExpression< Operand, typename detail::PlusTag::template ExprScalar::Bound > - >::type -#else - -#endif - operator+(ExpressionBase const & operand, Scalar const & scalar) { - return vectorize(detail::PlusTag::template ExprScalar::bind(scalar),operand); - } - - template -#ifndef DOXYGEN - typename boost::enable_if< - typename ExpressionTraits::IsScalar, - detail::UnaryOpExpression< Operand, typename detail::PlusTag::template ScalarExpr::Bound > - >::type -#else - -#endif - operator+(Scalar const & scalar, ExpressionBase const & operand) { - return vectorize(detail::PlusTag::template ScalarExpr::bind(scalar),operand); - } - - template -#ifndef DOXYGEN - detail::BinaryOpExpression< - Operand1, Operand2, - typename detail::PlusTag::template ExprExpr::BinaryFunction - > -#else - -#endif - operator+(ExpressionBase const & operand1, ExpressionBase const & operand2) { - return vectorize( - typename detail::PlusTag::template ExprExpr::BinaryFunction(), - operand1, - operand2 - ); - } - - template -#ifndef DOXYGEN - typename boost::enable_if< - typename ExpressionTraits::IsScalar, - detail::UnaryOpExpression< Operand, typename detail::MinusTag::template ExprScalar::Bound > - >::type -#else - -#endif - operator-(ExpressionBase const & operand, Scalar const & scalar) { - return vectorize(detail::MinusTag::template ExprScalar::bind(scalar),operand); - } - - template -#ifndef DOXYGEN - typename boost::enable_if< - typename ExpressionTraits::IsScalar, - detail::UnaryOpExpression< Operand, typename detail::MinusTag::template ScalarExpr::Bound > - >::type -#else - -#endif - operator-(Scalar const & scalar, ExpressionBase const & operand) { - return vectorize(detail::MinusTag::template ScalarExpr::bind(scalar),operand); - } - - template -#ifndef DOXYGEN - detail::BinaryOpExpression< - Operand1, Operand2, - typename detail::MinusTag::template ExprExpr::BinaryFunction - > -#else - -#endif - operator-(ExpressionBase const & operand1, ExpressionBase const & operand2) { - return vectorize( - typename detail::MinusTag::template ExprExpr::BinaryFunction(), - operand1, - operand2 - ); - } - - template -#ifndef DOXYGEN - typename boost::enable_if< - typename ExpressionTraits::IsScalar, - detail::UnaryOpExpression< Operand, typename detail::MultipliesTag::template ExprScalar::Bound > - >::type -#else - -#endif - operator*(ExpressionBase const & operand, Scalar const & scalar) { - return vectorize(detail::MultipliesTag::template ExprScalar::bind(scalar),operand); - } - - template -#ifndef DOXYGEN - typename boost::enable_if< - typename ExpressionTraits::IsScalar, - detail::UnaryOpExpression< Operand, typename detail::MultipliesTag::template ScalarExpr::Bound > - >::type -#else - -#endif - operator*(Scalar const & scalar, ExpressionBase const & operand) { - return vectorize(detail::MultipliesTag::template ScalarExpr::bind(scalar),operand); - } - - template -#ifndef DOXYGEN - detail::BinaryOpExpression< - Operand1, Operand2, - typename detail::MultipliesTag::template ExprExpr::BinaryFunction - > -#else - -#endif - operator*(ExpressionBase const & operand1, ExpressionBase const & operand2) { - return vectorize( - typename detail::MultipliesTag::template ExprExpr::BinaryFunction(), - operand1, - operand2 - ); - } - - template -#ifndef DOXYGEN - typename boost::enable_if< - typename ExpressionTraits::IsScalar, - detail::UnaryOpExpression< Operand, typename detail::DividesTag::template ExprScalar::Bound > - >::type -#else - -#endif - operator/(ExpressionBase const & operand, Scalar const & scalar) { - return vectorize(detail::DividesTag::template ExprScalar::bind(scalar),operand); - } - - template -#ifndef DOXYGEN - typename boost::enable_if< - typename ExpressionTraits::IsScalar, - detail::UnaryOpExpression< Operand, typename detail::DividesTag::template ScalarExpr::Bound > - >::type -#else - -#endif - operator/(Scalar const & scalar, ExpressionBase const & operand) { - return vectorize(detail::DividesTag::template ScalarExpr::bind(scalar),operand); - } - - template -#ifndef DOXYGEN - detail::BinaryOpExpression< - Operand1, Operand2, - typename detail::DividesTag::template ExprExpr::BinaryFunction - > -#else - -#endif - operator/(ExpressionBase const & operand1, ExpressionBase const & operand2) { - return vectorize( - typename detail::DividesTag::template ExprExpr::BinaryFunction(), - operand1, - operand2 - ); - } - - template -#ifndef DOXYGEN - typename boost::enable_if< - typename ExpressionTraits::IsScalar, - detail::UnaryOpExpression< Operand, typename detail::ModulusTag::template ExprScalar::Bound > - >::type -#else - -#endif - operator%(ExpressionBase const & operand, Scalar const & scalar) { - return vectorize(detail::ModulusTag::template ExprScalar::bind(scalar),operand); - } - - template -#ifndef DOXYGEN - typename boost::enable_if< - typename ExpressionTraits::IsScalar, - detail::UnaryOpExpression< Operand, typename detail::ModulusTag::template ScalarExpr::Bound > - >::type -#else - -#endif - operator%(Scalar const & scalar, ExpressionBase const & operand) { - return vectorize(detail::ModulusTag::template ScalarExpr::bind(scalar),operand); - } - - template -#ifndef DOXYGEN - detail::BinaryOpExpression< - Operand1, Operand2, - typename detail::ModulusTag::template ExprExpr::BinaryFunction - > -#else - -#endif - operator%(ExpressionBase const & operand1, ExpressionBase const & operand2) { - return vectorize( - typename detail::ModulusTag::template ExprExpr::BinaryFunction(), - operand1, - operand2 - ); - } - - template -#ifndef DOXYGEN - typename boost::enable_if< - typename ExpressionTraits::IsScalar, - detail::UnaryOpExpression< Operand, typename detail::BitwiseXorTag::template ExprScalar::Bound > - >::type -#else - -#endif - operator^(ExpressionBase const & operand, Scalar const & scalar) { - return vectorize(detail::BitwiseXorTag::template ExprScalar::bind(scalar),operand); - } - - template -#ifndef DOXYGEN - typename boost::enable_if< - typename ExpressionTraits::IsScalar, - detail::UnaryOpExpression< Operand, typename detail::BitwiseXorTag::template ScalarExpr::Bound > - >::type -#else - -#endif - operator^(Scalar const & scalar, ExpressionBase const & operand) { - return vectorize(detail::BitwiseXorTag::template ScalarExpr::bind(scalar),operand); - } - - template -#ifndef DOXYGEN - detail::BinaryOpExpression< - Operand1, Operand2, - typename detail::BitwiseXorTag::template ExprExpr::BinaryFunction - > -#else - -#endif - operator^(ExpressionBase const & operand1, ExpressionBase const & operand2) { - return vectorize( - typename detail::BitwiseXorTag::template ExprExpr::BinaryFunction(), - operand1, - operand2 - ); - } - - template -#ifndef DOXYGEN - typename boost::enable_if< - typename ExpressionTraits::IsScalar, - detail::UnaryOpExpression< Operand, typename detail::BitwiseOrTag::template ExprScalar::Bound > - >::type -#else - -#endif - operator|(ExpressionBase const & operand, Scalar const & scalar) { - return vectorize(detail::BitwiseOrTag::template ExprScalar::bind(scalar),operand); - } - - template -#ifndef DOXYGEN - typename boost::enable_if< - typename ExpressionTraits::IsScalar, - detail::UnaryOpExpression< Operand, typename detail::BitwiseOrTag::template ScalarExpr::Bound > - >::type -#else - -#endif - operator|(Scalar const & scalar, ExpressionBase const & operand) { - return vectorize(detail::BitwiseOrTag::template ScalarExpr::bind(scalar),operand); - } - - template -#ifndef DOXYGEN - detail::BinaryOpExpression< - Operand1, Operand2, - typename detail::BitwiseOrTag::template ExprExpr::BinaryFunction - > -#else - -#endif - operator|(ExpressionBase const & operand1, ExpressionBase const & operand2) { - return vectorize( - typename detail::BitwiseOrTag::template ExprExpr::BinaryFunction(), - operand1, - operand2 - ); - } - - template -#ifndef DOXYGEN - typename boost::enable_if< - typename ExpressionTraits::IsScalar, - detail::UnaryOpExpression< Operand, typename detail::BitwiseAndTag::template ExprScalar::Bound > - >::type -#else - -#endif - operator&(ExpressionBase const & operand, Scalar const & scalar) { - return vectorize(detail::BitwiseAndTag::template ExprScalar::bind(scalar),operand); - } - - template -#ifndef DOXYGEN - typename boost::enable_if< - typename ExpressionTraits::IsScalar, - detail::UnaryOpExpression< Operand, typename detail::BitwiseAndTag::template ScalarExpr::Bound > - >::type -#else - -#endif - operator&(Scalar const & scalar, ExpressionBase const & operand) { - return vectorize(detail::BitwiseAndTag::template ScalarExpr::bind(scalar),operand); - } - - template -#ifndef DOXYGEN - detail::BinaryOpExpression< - Operand1, Operand2, - typename detail::BitwiseAndTag::template ExprExpr::BinaryFunction - > -#else - -#endif - operator&(ExpressionBase const & operand1, ExpressionBase const & operand2) { - return vectorize( - typename detail::BitwiseAndTag::template ExprExpr::BinaryFunction(), - operand1, - operand2 - ); - } - - template -#ifndef DOXYGEN - typename boost::enable_if< - typename ExpressionTraits::IsScalar, - detail::UnaryOpExpression< Operand, typename detail::BitwiseLeftShiftTag::template ExprScalar::Bound > - >::type -#else - -#endif - operator<<(ExpressionBase const & operand, Scalar const & scalar) { - return vectorize(detail::BitwiseLeftShiftTag::template ExprScalar::bind(scalar),operand); - } - - template -#ifndef DOXYGEN - typename boost::enable_if< - typename ExpressionTraits::IsScalar, - detail::UnaryOpExpression< Operand, typename detail::BitwiseLeftShiftTag::template ScalarExpr::Bound > - >::type -#else - -#endif - operator<<(Scalar const & scalar, ExpressionBase const & operand) { - return vectorize(detail::BitwiseLeftShiftTag::template ScalarExpr::bind(scalar),operand); - } - - template -#ifndef DOXYGEN - detail::BinaryOpExpression< - Operand1, Operand2, - typename detail::BitwiseLeftShiftTag::template ExprExpr::BinaryFunction - > -#else - -#endif - operator<<(ExpressionBase const & operand1, ExpressionBase const & operand2) { - return vectorize( - typename detail::BitwiseLeftShiftTag::template ExprExpr::BinaryFunction(), - operand1, - operand2 - ); - } - - template -#ifndef DOXYGEN - typename boost::enable_if< - typename ExpressionTraits::IsScalar, - detail::UnaryOpExpression< Operand, typename detail::BitwiseRightShiftTag::template ExprScalar::Bound > - >::type -#else - -#endif - operator>>(ExpressionBase const & operand, Scalar const & scalar) { - return vectorize(detail::BitwiseRightShiftTag::template ExprScalar::bind(scalar),operand); - } - - template -#ifndef DOXYGEN - typename boost::enable_if< - typename ExpressionTraits::IsScalar, - detail::UnaryOpExpression< Operand, typename detail::BitwiseRightShiftTag::template ScalarExpr::Bound > - >::type -#else - -#endif - operator>>(Scalar const & scalar, ExpressionBase const & operand) { - return vectorize(detail::BitwiseRightShiftTag::template ScalarExpr::bind(scalar),operand); - } - - template -#ifndef DOXYGEN - detail::BinaryOpExpression< - Operand1, Operand2, - typename detail::BitwiseRightShiftTag::template ExprExpr::BinaryFunction - > -#else - -#endif - operator>>(ExpressionBase const & operand1, ExpressionBase const & operand2) { - return vectorize( - typename detail::BitwiseRightShiftTag::template ExprExpr::BinaryFunction(), - operand1, - operand2 - ); - } - - - template -#ifndef DOXYGEN - typename boost::enable_if< - typename ExpressionTraits::IsScalar, - detail::UnaryOpExpression< Operand, typename detail::EqualToTag::template ExprScalar::Bound > - >::type -#else - -#endif - equal(ExpressionBase const & operand, Scalar const & scalar) { - return vectorize(detail::EqualToTag::template ExprScalar::bind(scalar),operand); - } - - template -#ifndef DOXYGEN - typename boost::enable_if< - typename ExpressionTraits::IsScalar, - detail::UnaryOpExpression< Operand, typename detail::EqualToTag::template ScalarExpr::Bound > - >::type -#else - -#endif - equal(Scalar const & scalar, ExpressionBase const & operand) { - return vectorize(detail::EqualToTag::template ScalarExpr::bind(scalar),operand); - } - - template -#ifndef DOXYGEN - detail::BinaryOpExpression< - Operand1, Operand2, - typename detail::EqualToTag::template ExprExpr::BinaryFunction - > -#else - -#endif - equal(ExpressionBase const & operand1, ExpressionBase const & operand2) { - return vectorize( - typename detail::EqualToTag::template ExprExpr::BinaryFunction(), - operand1, - operand2 - ); - } - - template -#ifndef DOXYGEN - typename boost::enable_if< - typename ExpressionTraits::IsScalar, - detail::UnaryOpExpression< Operand, typename detail::NotEqualToTag::template ExprScalar::Bound > - >::type -#else - -#endif - not_equal(ExpressionBase const & operand, Scalar const & scalar) { - return vectorize(detail::NotEqualToTag::template ExprScalar::bind(scalar),operand); - } - - template -#ifndef DOXYGEN - typename boost::enable_if< - typename ExpressionTraits::IsScalar, - detail::UnaryOpExpression< Operand, typename detail::NotEqualToTag::template ScalarExpr::Bound > - >::type -#else - -#endif - not_equal(Scalar const & scalar, ExpressionBase const & operand) { - return vectorize(detail::NotEqualToTag::template ScalarExpr::bind(scalar),operand); - } - - template -#ifndef DOXYGEN - detail::BinaryOpExpression< - Operand1, Operand2, - typename detail::NotEqualToTag::template ExprExpr::BinaryFunction - > -#else - -#endif - not_equal(ExpressionBase const & operand1, ExpressionBase const & operand2) { - return vectorize( - typename detail::NotEqualToTag::template ExprExpr::BinaryFunction(), - operand1, - operand2 - ); - } - - template -#ifndef DOXYGEN - typename boost::enable_if< - typename ExpressionTraits::IsScalar, - detail::UnaryOpExpression< Operand, typename detail::LessTag::template ExprScalar::Bound > - >::type -#else - -#endif - less(ExpressionBase const & operand, Scalar const & scalar) { - return vectorize(detail::LessTag::template ExprScalar::bind(scalar),operand); - } - - template -#ifndef DOXYGEN - typename boost::enable_if< - typename ExpressionTraits::IsScalar, - detail::UnaryOpExpression< Operand, typename detail::LessTag::template ScalarExpr::Bound > - >::type -#else - -#endif - less(Scalar const & scalar, ExpressionBase const & operand) { - return vectorize(detail::LessTag::template ScalarExpr::bind(scalar),operand); - } - - template -#ifndef DOXYGEN - detail::BinaryOpExpression< - Operand1, Operand2, - typename detail::LessTag::template ExprExpr::BinaryFunction - > -#else - -#endif - less(ExpressionBase const & operand1, ExpressionBase const & operand2) { - return vectorize( - typename detail::LessTag::template ExprExpr::BinaryFunction(), - operand1, - operand2 - ); - } - - template -#ifndef DOXYGEN - typename boost::enable_if< - typename ExpressionTraits::IsScalar, - detail::UnaryOpExpression< Operand, typename detail::GreaterTag::template ExprScalar::Bound > - >::type -#else - -#endif - greater(ExpressionBase const & operand, Scalar const & scalar) { - return vectorize(detail::GreaterTag::template ExprScalar::bind(scalar),operand); - } - - template -#ifndef DOXYGEN - typename boost::enable_if< - typename ExpressionTraits::IsScalar, - detail::UnaryOpExpression< Operand, typename detail::GreaterTag::template ScalarExpr::Bound > - >::type -#else - -#endif - greater(Scalar const & scalar, ExpressionBase const & operand) { - return vectorize(detail::GreaterTag::template ScalarExpr::bind(scalar),operand); - } - - template -#ifndef DOXYGEN - detail::BinaryOpExpression< - Operand1, Operand2, - typename detail::GreaterTag::template ExprExpr::BinaryFunction - > -#else - -#endif - greater(ExpressionBase const & operand1, ExpressionBase const & operand2) { - return vectorize( - typename detail::GreaterTag::template ExprExpr::BinaryFunction(), - operand1, - operand2 - ); - } - - template -#ifndef DOXYGEN - typename boost::enable_if< - typename ExpressionTraits::IsScalar, - detail::UnaryOpExpression< Operand, typename detail::LessEqualTag::template ExprScalar::Bound > - >::type -#else - -#endif - less_equal(ExpressionBase const & operand, Scalar const & scalar) { - return vectorize(detail::LessEqualTag::template ExprScalar::bind(scalar),operand); - } - - template -#ifndef DOXYGEN - typename boost::enable_if< - typename ExpressionTraits::IsScalar, - detail::UnaryOpExpression< Operand, typename detail::LessEqualTag::template ScalarExpr::Bound > - >::type -#else - -#endif - less_equal(Scalar const & scalar, ExpressionBase const & operand) { - return vectorize(detail::LessEqualTag::template ScalarExpr::bind(scalar),operand); - } - - template -#ifndef DOXYGEN - detail::BinaryOpExpression< - Operand1, Operand2, - typename detail::LessEqualTag::template ExprExpr::BinaryFunction - > -#else - -#endif - less_equal(ExpressionBase const & operand1, ExpressionBase const & operand2) { - return vectorize( - typename detail::LessEqualTag::template ExprExpr::BinaryFunction(), - operand1, - operand2 - ); - } - - template -#ifndef DOXYGEN - typename boost::enable_if< - typename ExpressionTraits::IsScalar, - detail::UnaryOpExpression< Operand, typename detail::GreaterEqualTag::template ExprScalar::Bound > - >::type -#else - -#endif - great_equal(ExpressionBase const & operand, Scalar const & scalar) { - return vectorize(detail::GreaterEqualTag::template ExprScalar::bind(scalar),operand); - } - - template -#ifndef DOXYGEN - typename boost::enable_if< - typename ExpressionTraits::IsScalar, - detail::UnaryOpExpression< Operand, typename detail::GreaterEqualTag::template ScalarExpr::Bound > - >::type -#else - -#endif - great_equal(Scalar const & scalar, ExpressionBase const & operand) { - return vectorize(detail::GreaterEqualTag::template ScalarExpr::bind(scalar),operand); - } - - template -#ifndef DOXYGEN - detail::BinaryOpExpression< - Operand1, Operand2, - typename detail::GreaterEqualTag::template ExprExpr::BinaryFunction - > -#else - -#endif - great_equal(ExpressionBase const & operand1, ExpressionBase const & operand2) { - return vectorize( - typename detail::GreaterEqualTag::template ExprExpr::BinaryFunction(), - operand1, - operand2 - ); - } - - template -#ifndef DOXYGEN - typename boost::enable_if< - typename ExpressionTraits::IsScalar, - detail::UnaryOpExpression< Operand, typename detail::LogicalAnd::template ExprScalar::Bound > - >::type -#else - -#endif - logical_and(ExpressionBase const & operand, Scalar const & scalar) { - return vectorize(detail::LogicalAnd::template ExprScalar::bind(scalar),operand); - } - - template -#ifndef DOXYGEN - typename boost::enable_if< - typename ExpressionTraits::IsScalar, - detail::UnaryOpExpression< Operand, typename detail::LogicalAnd::template ScalarExpr::Bound > - >::type -#else - -#endif - logical_and(Scalar const & scalar, ExpressionBase const & operand) { - return vectorize(detail::LogicalAnd::template ScalarExpr::bind(scalar),operand); - } - - template -#ifndef DOXYGEN - detail::BinaryOpExpression< - Operand1, Operand2, - typename detail::LogicalAnd::template ExprExpr::BinaryFunction - > -#else - -#endif - logical_and(ExpressionBase const & operand1, ExpressionBase const & operand2) { - return vectorize( - typename detail::LogicalAnd::template ExprExpr::BinaryFunction(), - operand1, - operand2 - ); - } - - template -#ifndef DOXYGEN - typename boost::enable_if< - typename ExpressionTraits::IsScalar, - detail::UnaryOpExpression< Operand, typename detail::LogicalOr::template ExprScalar::Bound > - >::type -#else - -#endif - logical_or(ExpressionBase const & operand, Scalar const & scalar) { - return vectorize(detail::LogicalOr::template ExprScalar::bind(scalar),operand); - } - - template -#ifndef DOXYGEN - typename boost::enable_if< - typename ExpressionTraits::IsScalar, - detail::UnaryOpExpression< Operand, typename detail::LogicalOr::template ScalarExpr::Bound > - >::type -#else - -#endif - logical_or(Scalar const & scalar, ExpressionBase const & operand) { - return vectorize(detail::LogicalOr::template ScalarExpr::bind(scalar),operand); - } - - template -#ifndef DOXYGEN - detail::BinaryOpExpression< - Operand1, Operand2, - typename detail::LogicalOr::template ExprExpr::BinaryFunction - > -#else - -#endif - logical_or(ExpressionBase const & operand1, ExpressionBase const & operand2) { - return vectorize( - typename detail::LogicalOr::template ExprExpr::BinaryFunction(), - operand1, - operand2 - ); - } - - - template -#ifndef DOXYGEN - detail::UnaryOpExpression< Operand, std::negate::Element> > -#else - -#endif - operator-(ExpressionBase const & operand) { - return vectorize(std::negate::Element>(),operand); - } - - template -#ifndef DOXYGEN - detail::UnaryOpExpression< Operand, std::logical_not::Element> > -#else - -#endif - logical_not(ExpressionBase const & operand) { - return vectorize(std::logical_not::Element>(),operand); - } - - template -#ifndef DOXYGEN - detail::UnaryOpExpression< Operand, detail::BitwiseNot::Element> > -#else - -#endif - operator~(ExpressionBase const & operand) { - return vectorize(detail::BitwiseNot::Element>(),operand); - } -/// @} - -template -inline typename boost::enable_if::IsScalar, bool>::type -any(Scalar const & scalar) { - return bool(scalar); -} - -/** - * \brief Return true if any of the elements of the given expression are true. - * - * \ingroup MainGroup - */ -template -inline bool -any(ExpressionBase const & expr) { - typename Derived::Iterator const i_end = expr.end(); - for (typename Derived::Iterator i = expr.begin(); i != i_end; ++i) { - if (any(*i)) return true; - } - return false; -} - -template -inline typename boost::enable_if::IsScalar, bool>::type -all(Scalar const & scalar) { - return bool(scalar); -} - -/** - * \brief Return true if all of the elements of the given expression are true. - * - * \ingroup MainGroup - */ -template -inline bool -all(ExpressionBase const & expr) { - typename Derived::Iterator const i_end = expr.end(); - for (typename Derived::Iterator i = expr.begin(); i != i_end; ++i) { - if (!all(*i)) return false; - } - return true; -} - -template -inline typename boost::enable_if< - boost::mpl::and_< - typename ExpressionTraits::IsScalar, - typename ExpressionTraits::IsScalar - >, - bool ->::type -allclose(Scalar1 const & scalar1, Scalar2 const & scalar2, double tol=1E-8) { - ApproximatelyEqual func(tol); - return func(scalar1, scalar2); -} - -template -inline typename boost::enable_if::IsScalar,bool>::type -allclose(Scalar const & scalar, ExpressionBase const & expr, double tol=1E-8) { - ApproximatelyEqual func(tol); - typename Derived::Iterator const i_end = expr.end(); - for (typename Derived::Iterator i = expr.begin(); i != i_end; ++i) { - if (!allclose(scalar, *i, tol)) return false; - } - return true; -} - -template -inline typename boost::enable_if::IsScalar,bool>::type -allclose(ExpressionBase const & expr, Scalar const & scalar, double tol=1E-8) { - return allclose(scalar, expr, tol); -} - -template -inline bool -allclose(ExpressionBase const & expr1, ExpressionBase const & expr2, double tol=1E-8) { - typename Derived1::Iterator const i_end = expr1.end(); - typename Derived1::Iterator i = expr1.begin(); - typename Derived2::Iterator j = expr2.begin(); - for (; i != i_end; ++i, ++j) { - if (!allclose(*i, *j, tol)) return false; - } - return true; -} - - -template -inline typename boost::enable_if::IsScalar, Scalar>::type -sum(Scalar const & scalar) { return scalar; } - - -/** - * \brief Return the sum of all elements of the given expression. - * - * \ingroup MainGroup - */ -template -inline typename Derived::Element -sum(ExpressionBase const & expr) { - typename Derived::Iterator const i_end = expr.end(); - typename Derived::Element total = static_cast(0); - for (typename Derived::Iterator i = expr.begin(); i != i_end; ++i) { - total += sum(*i); - } - return total; -} - - -} // namespace ndarray - -#endif // !NDARRAY_operators_h_INCLUDED diff --git a/include/ndarray/pybind11.h b/include/ndarray/pybind11.h deleted file mode 100644 index bd1eaeda..00000000 --- a/include/ndarray/pybind11.h +++ /dev/null @@ -1,262 +0,0 @@ -/* - * LSST Data Management System - * Copyright 2008-2016 AURA/LSST. - * - * This product includes software developed by the - * LSST Project (http://www.lsst.org/). - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the LSST License Statement and - * the GNU General Public License along with this program. If not, - * see . - */ - -#ifndef NDARRAY_pybind11_h_INCLUDED -#define NDARRAY_pybind11_h_INCLUDED - -/** - * @file ndarray/pybind11.h - * @brief Public header file for pybind11-based Python support. - * - * \warning Both the Numpy C-API headers "arrayobject.h" and - * "ufuncobject.h" must be included before ndarray/python.hpp - * or any of the files in ndarray/python. - * - * \note This file is not included by the main "ndarray.h" header file. - */ - -/** \defgroup ndarrayPythonGroup Python Support - * - * The ndarray Python support module provides conversion - * functions between ndarray objects, notably Array and - * Vector, and Python Numpy objects. - */ - -#include "pybind11.h" -#include "pybind11/numpy.h" - -#include "ndarray.h" -#include "ndarray/eigen.h" - -namespace ndarray { - -namespace detail { - -inline void destroyCapsule(PyObject * p) { - void * m = PyCapsule_GetPointer(p, "ndarray.Manager"); - Manager::Ptr * b = reinterpret_cast(m); - delete b; -} - -} // namespace ndarray::detail - -inline PyObject* makePyManager(Manager::Ptr const & m) { - return PyCapsule_New( - new Manager::Ptr(m), - "ndarray.Manager", - detail::destroyCapsule - ); -} - -#if PYBIND11_VERSION_MAJOR == 2 && PYBIND11_VERSION_MINOR <= 1 -using pybind11_np_size_t = size_t; -#else -using pybind11_np_size_t = ssize_t; -#endif - -template -struct -#ifdef __GNUG__ -// pybind11 hides all symbols in its namespace only when this is set, -// and in that case we should hide these classes too. -__attribute__((visibility("hidden"))) -#endif -Pybind11Helper { - using Element = typename ndarray::Array::Element; - using Wrapper = pybind11::array_t::type, 0>; // 0: no ensurecopy - static constexpr bool isConst = std::is_const::value; - - Pybind11Helper() : isNone(false), wrapper() {} - - bool init(pybind11::handle src) { - isNone = src.is_none(); - if (isNone) { - return true; - } - if (!Wrapper::check_(src)) { - return false; - } - try { - wrapper = pybind11::reinterpret_borrow(src); - } catch (pybind11::error_already_set & err) { - return false; - } - return true; - } - - bool check() const { - if (isNone) { - return true; - } - if (!wrapper) { - return false; - } - if (wrapper.ndim() != N) { - return false; - } - if (!isConst && !wrapper.writeable()) { - return false; - } - pybind11_np_size_t const * shape = wrapper.shape(); - pybind11_np_size_t const * strides = wrapper.strides(); - pybind11_np_size_t const itemsize = wrapper.itemsize(); - if (C > 0) { - // If the shape is zero in any dimension, we don't - // worry about the strides. - for (int i = 0; i < C; ++i) { - if (shape[N-i-1] == 0) { - return true; - } - } - pybind11_np_size_t requiredStride = itemsize; - for (int i = 0; i < C; ++i) { - if (strides[N-i-1] != requiredStride) { - return false; - } - requiredStride *= shape[N-i-1]; - } - } else if (C < 0) { - // If the shape is zero in any dimension, we don't - // worry about the strides. - for (int i = 0; i < -C; ++i) { - if (shape[i] == 0) { - return true; - } - } - pybind11_np_size_t requiredStride = itemsize; - for (int i = 0; i < -C; ++i) { - if (strides[i] != requiredStride) { - return false; - } - requiredStride *= shape[i]; - } - } - return true; - } - - ndarray::Array convert() const { - if (isNone) { - return ndarray::Array(); - } - if (!pybind11::reinterpret_borrow(wrapper.dtype().attr("isnative"))) { - PyErr_SetString(PyExc_TypeError, "Only arrays with native byteorder can be converted to C++."); - throw pybind11::error_already_set(); - } - Vector nShape; - Vector nStrides; - pybind11_np_size_t const * pShape = wrapper.shape(); - pybind11_np_size_t const * pStrides = wrapper.strides(); - pybind11_np_size_t const itemsize = wrapper.itemsize(); - for (int i = 0; i < N; ++i) { - if (pStrides[i] % itemsize != 0) { - PyErr_SetString( - PyExc_TypeError, - "Cannot convert array to C++: strides must be an integer multiple of the element size" - ); - throw pybind11::error_already_set(); - } - nShape[i] = pShape[i]; - nStrides[i] = pStrides[i]/itemsize; - } - return ndarray::Array( - ndarray::external(const_cast(wrapper.data()), - nShape, nStrides, pybind11::object(wrapper)) - ); - } - - static pybind11::handle toPython(ndarray::Array const & src) { - Vector nShape = src.getShape(); - Vector nStrides = src.getStrides(); - std::vector pShape(N); - std::vector pStrides(N); - pybind11_np_size_t const itemsize = sizeof(Element); - for (int i = 0; i < N; ++i) { - pShape[i] = nShape[i]; - pStrides[i] = nStrides[i]*itemsize; - } - pybind11::object base; - if (src.getManager()) { - base = pybind11::reinterpret_steal(ndarray::makePyManager(src.getManager())); - } - Wrapper result(pShape, pStrides, src.getData(), base); - if (std::is_const::value) { - result.attr("flags")["WRITEABLE"] = false; - } - return result.release(); - } - - bool isNone; - Wrapper wrapper; -}; - -} // namespace ndarray - -namespace pybind11 { -namespace detail { - -/* @brief A pybind11 type_caster for ndarray::Array - */ -template -class type_caster< ndarray::Array > { - using Helper = ndarray::Pybind11Helper; -public: - - bool load(handle src, bool) { - return _helper.init(src) && _helper.check(); - } - - void set_value() { - _value = _helper.convert(); - } - - static handle cast(const ndarray::Array &src, return_value_policy /* policy */, handle /* parent */) { - return Helper::toPython(src); - } - - static constexpr auto name = _("numpy.ndarray"); - - static handle cast(const ndarray::Array *src, return_value_policy policy, handle parent) { - return cast(*src, policy, parent); - } - - operator ndarray::Array * () { - if (_helper.isNone) { - return nullptr; - } else { - set_value(); - return &_value; - } - } - - operator ndarray::Array & () { set_value(); return _value; } - - template using cast_op_type = pybind11::detail::cast_op_type<_T>; - -private: - ndarray::Array _value; - Helper _helper; -}; - -} // namespace detail -} // namespace pybind11 - -#endif // !NDARRAY_pybind11_h_INCLUDED diff --git a/include/ndarray/types.h b/include/ndarray/types.h deleted file mode 100644 index 09689b04..00000000 --- a/include/ndarray/types.h +++ /dev/null @@ -1,186 +0,0 @@ -// -*- c++ -*- -/* - * Copyright (c) 2010-2012, Jim Bosch - * All rights reserved. - * - * ndarray is distributed under a simple BSD-like license; - * see the LICENSE file that should be present in the root - * of the source distribution, or alternately available at: - * https://github.com/ndarray/ndarray - */ -#ifndef NDARRAY_types_h_INCLUDED -#define NDARRAY_types_h_INCLUDED - -/// @file ndarray/types.h @brief Numeric type traits. - -#include -#include - -#include - -namespace ndarray { - -/// @addtogroup MainGroup -/// @{ - -/** - * @class NumericTraits - * @brief Numeric type traits - * - * Defines Real and Complex versions of numeric types, along with - * priority values to aid in type promotion. - */ -template ::type, - typename is_complex=typename boost::is_complex::type, - typename is_arithmetic=typename boost::is_arithmetic::type> -struct NumericTraits {}; - -/// \cond SPECIALIZATIONS -template -struct NumericTraits { - typedef U Type; - typedef boost::true_type IsReal; - typedef U RealType; - typedef std::complex ComplexType; - typedef U ParamType; - - static const int PRIORITY = sizeof(U) + (sizeof(long long) * (!std::numeric_limits::is_integer)); -}; - -template -struct NumericTraits { - typedef U type; - typedef boost::false_type IsReal; - typedef typename U::value_type RealType; - typedef U ComplexType; - typedef U const & ParamType; - - static const int PRIORITY = NumericTraits::PRIORITY; -}; -/// \endcond - -/** - * @class Promote - * @brief Metafunction to compute numeric promotions. - */ -template ::PRIORITY > NumericTraits::PRIORITY), - bool is_complex=(NumericTraits::IsReal::value && NumericTraits::IsReal::value) - > -struct Promote { -}; - -/// \cond SPECIALIZATIONS - -// Real, T2 has priority -template -struct Promote { - typedef typename NumericTraits::Type Type; -}; - -// Real, T1 has priority -template -struct Promote { - typedef typename NumericTraits::Type Type; -}; - -// Complex, T2 has priority -template -struct Promote { - typedef typename NumericTraits::ComplexType Type; -}; - -// Complex, T1 has priority -template -struct Promote { - typedef typename NumericTraits::ComplexType Type; -}; - -/// \endcond - -namespace detail { - -/** - * @internal @ingroup ndarrayInternalGroup - * @brief Provides careful floating point operations for use in floating point comparisons. - * - * Implementation is roughly modeled after the floating point comparisons in the Boost.Test library. - * - * \sa ApproximatelyEqual - */ -template -struct SafeFloatingPointOps { - - static inline T divide(T a, T b) { - if (b < static_cast(1) && a > b*std::numeric_limits::max()) - return std::numeric_limits::max(); - if (a == static_cast(0) || (b > static_cast(1) && a < b*std::numeric_limits::min())) - return static_cast(0); - return a / b; - } - - static inline T abs(T a) { - return (a < static_cast(0)) ? -a : a; - } - -}; - -} // namespace detail - -/** - * @ingroup ndarrayMainGroup - * @brief Binary predicate for floating point equality comparison with tolerance. - * - * Implementation is roughly modeled after the floating point comparisons in the Boost.Test library. - */ -template -struct ApproximatelyEqual { - typedef T1 first_argument_type; - typedef T2 second_argument_type; - typedef bool result_type; - - typedef typename Promote::Type Promoted; - typedef detail::SafeFloatingPointOps Ops; - - result_type operator()(T1 a, T2 b) const { - Promoted diff = Ops::abs(a - b); - Promoted da = Ops::divide(diff,Ops::abs(a)); - Promoted db = Ops::divide(diff,Ops::abs(b)); - return db <= _tolerance && da <= _tolerance; - } - - explicit ApproximatelyEqual(Promoted tolerance) : _tolerance(Ops::abs(tolerance)) {} - -private: - Promoted _tolerance; -}; - -/** - * @ingroup ndarrayMainGroup - * @brief Binary predicate for complex floating point equality comparison with tolerance. - * - * Implementation is roughly modeled after the floating point comparisons in the Boost.Test library. - */ -template -struct ApproximatelyEqual< std::complex, std::complex > { - typedef std::complex first_argument_type; - typedef std::complex second_argument_type; - typedef bool result_type; - - typedef typename Promote::Type Promoted; - - result_type operator()(std::complex const & a, std::complex const & b) const { - return _real(a.real(),b.real()) && _real(a.imag(),b.imag()); - } - - explicit ApproximatelyEqual(Promoted tolerance) : _real(tolerance) {} - -private: - ApproximatelyEqual _real; -}; - -/// @} - -} // namespace ndarray - -#endif // !NDARRAY_types_h_INCLUDED diff --git a/include/ndarray/vectorize.h b/include/ndarray/vectorize.h deleted file mode 100644 index d8bd309e..00000000 --- a/include/ndarray/vectorize.h +++ /dev/null @@ -1,144 +0,0 @@ -// -*- c++ -*- -/* - * Copyright (c) 2010-2012, Jim Bosch - * All rights reserved. - * - * ndarray is distributed under a simple BSD-like license; - * see the LICENSE file that should be present in the root - * of the source distribution, or alternately available at: - * https://github.com/ndarray/ndarray - */ -#ifndef NDARRAY_vectorize_h_INCLUDED -#define NDARRAY_vectorize_h_INCLUDED - -/** - * \file ndarray/vectorize.h @brief Code to apply arbitrary scalar functors to arrays. - */ - -#include "ndarray_fwd.h" -#include "ndarray/detail/UnaryOp.h" - -#include -#include - -namespace ndarray { -namespace result_of { - -template -struct vectorize { - typedef T1 BinaryFunction; - typedef T2 Argument1; - typedef T3 Argument2; - - typename boost::mpl::if_< - boost::mpl::and_< - typename ExpressionTraits::IsScalar, - typename ExpressionTraits::IsScalar - >, - typename BinaryFunction::result_type, - detail::BinaryOpExpression - >::type type; - -}; - -template -struct vectorize { - typedef T1 UnaryFunction; - typedef T2 Argument; - - typedef typename boost::mpl::if_< - typename ExpressionTraits::IsScalar, - typename UnaryFunction::result_type, - detail::UnaryOpExpression - >::type type; -}; - -} // namespace result_of - -/// @addtogroup ndarrayMainGroup -/// @{ - -/** - * @brief Apply a non-mutating unary function object to a scalar. - * - * This overload exists to allow recursive usage of the Array-argument vectorize functions. - */ -template -#ifndef DOXYGEN -typename boost::enable_if::IsScalar, - typename UnaryFunction::result_type>::type -#else -typename UnaryFunction::result_type -#endif -vectorize( - UnaryFunction const & functor, - Scalar const & scalar -) { - return functor(scalar); -} - -/** - * @brief Apply a non-mutating unary function object to each element of a multidimensional Expression. - * - * Evaluation is lazy. - */ -template -detail::UnaryOpExpression -vectorize( - UnaryFunction const & functor, - ExpressionBase const & operand -) { - return detail::UnaryOpExpression( - static_cast(operand), - functor - ); -} - -/** - * @brief Apply a non-mutating binary function object to a pair of scalars. - * - * This overload exists to allow recursive usage of the Array-argument vectorize functions. - */ -template -#ifndef DOXYGEN -typename boost::enable_if_c< - (ExpressionTraits::IsScalar::value - && ExpressionTraits::IsScalar::value), - typename BinaryFunction::result_type - >::type -#else -typename BinaryFunction::result_type -#endif -vectorize( - BinaryFunction const & functor, - Scalar1 const & scalar1, - Scalar2 const & scalar2 -) { - return functor(scalar1,scalar2); -} - -/** - * @brief Apply a non-mutating binary function object pairwise to - * the elements of two multidimensional Expressions. - * - * Evaluation is lazy. - */ -template -detail::BinaryOpExpression -vectorize( - BinaryFunction const & functor, - ExpressionBase const & operand1, - ExpressionBase const & operand2 -) { - return detail::BinaryOpExpression( - static_cast(operand1), - static_cast(operand2), - functor - ); -} - -/// @} - -} // namespace ndarray - -#endif // !NDARRAY_vectorize_h_INCLUDED diff --git a/include/ndarray/views.h b/include/ndarray/views.h deleted file mode 100644 index c1fc3290..00000000 --- a/include/ndarray/views.h +++ /dev/null @@ -1,152 +0,0 @@ -// -*- c++ -*- -/* - * Copyright (c) 2010-2012, Jim Bosch - * All rights reserved. - * - * ndarray is distributed under a simple BSD-like license; - * see the LICENSE file that should be present in the root - * of the source distribution, or alternately available at: - * https://github.com/ndarray/ndarray - */ -#ifndef NDARRAY_views_h_INCLUDED -#define NDARRAY_views_h_INCLUDED - -/** - * \file ndarray/views.h @brief Public interface for arbitrary views into arrays. - */ - -#include -#include -#include -#include - -#include "ndarray_fwd.h" - -namespace ndarray { -namespace index { - -/** - * @brief Simple structure defining a noncontiguous range of indices. - */ -struct Slice { - Size start; - Size stop; - Offset step; - - Slice(Size start_, Size stop_, Offset step_) : start(start_), stop(stop_), step(step_) {} - - Size computeSize() const { return (step > 1) ? (stop - start + 1) / step : stop - start; } -}; - -/** - * @brief Simple structure defining a contiguous range of indices. - */ -struct Range { - Size start; - Size stop; - - Range(Size start_, Size stop_) : start(start_), stop(stop_) {} -}; - -/** - * @brief Empty structure marking a view of an entire dimension. - */ -struct Full {}; - -/** - * @brief Structure marking a single element of a dimension. - */ -struct Scalar { - Size n; - - explicit Scalar(Size n_) : n(n_) {} -}; - -} // namespace index - -/** - * @brief A template meta-sequence that defines an arbitrary view into an unspecified array. - * - * A View is constructed from a call to the global view() function - * and subsequent chained calls to operator(). - */ -template > -struct View { - typedef Seq_ Sequence; ///< A boost::fusion sequence type - Sequence _seq; ///< A boost::fusion sequence of index objects. - - explicit View(Sequence seq) : _seq(seq) {} - - template - explicit View(OtherSequence const & other) : _seq(other) {} - - template - View(View const & other) : _seq(other._seq) {} - - /// @brief The View that results from chaining an full dimension index () to this. - typedef View::type> Full; - - /// @brief The View that results from chaining a range (start,stop) to this. - typedef View::type> Range; - - /// @brief The View that results from chaining a slice (start,stop,step) to this. - typedef View::type> Slice; - - /// @brief The View that results from chaining a scalar (n) to this. - typedef View::type> Scalar; - - /// @brief Chain the full next dimension to this. - Full operator()() const { return Full(boost::fusion::push_back(_seq, index::Full())); } - - /// @brief Chain a contiguous range of the next dimension to this. - Range operator()(Size start, Size stop) const { - return Range(boost::fusion::push_back(_seq, index::Range(start, stop))); - } - - /// @brief Chain a noncontiguous slice of the next dimension to this. - Slice operator()(Size start, Size stop, Offset step) const { - return Slice(boost::fusion::push_back(_seq, index::Slice(start, stop, step))); - } - - /// @brief Chain a single element of the next dimension to this. - Scalar operator()(Size n) const { - return Scalar(boost::fusion::push_back(_seq, index::Scalar(n))); - } -}; - -/// @addtogroup ndarrayMainGroup -/// @{ - -/** @brief Start a view definition that includes the entire first dimension. */ -inline View< boost::fusion::vector1 > view() { - return View< boost::fusion::vector1 >( - boost::fusion::make_vector(index::Full()) - ); -} - -/** @brief Start a view definition that selects a contiguous range in the first dimension. */ -inline View< boost::fusion::vector1 > view(Size start, Size stop) { - return View< boost::fusion::vector1 >( - boost::fusion::make_vector(index::Range(start, stop)) - ); -} - -/** @brief Start a view definition that selects a noncontiguous slice of the first dimension. */ -inline View< boost::fusion::vector1 > view(Size start, Size stop, Offset step) { - return View< boost::fusion::vector1 >( - boost::fusion::make_vector(index::Slice(start, stop, step)) - ); -} - -/** @brief Start a view definition that selects single element from the first dimension. */ -inline View< boost::fusion::vector1 > view(Size n) { - return View< boost::fusion::vector1 >( - boost::fusion::make_vector(index::Scalar(n)) - ); -} - -/// @} - -} // namespace ndarray - -#endif // !NDARRAY_views_h_INCLUDED diff --git a/include/ndarray/views.hpp b/include/ndarray/views.hpp new file mode 100644 index 00000000..3e1a68b9 --- /dev/null +++ b/include/ndarray/views.hpp @@ -0,0 +1,162 @@ +// -*- c++ -*- +/* + * Copyright (c) 2010-2018, Jim Bosch + * All rights reserved. + * + * ndarray is distributed under a simple BSD-like license; + * see the LICENSE file that should be present in the root + * of the source distribution, or alternately available at: + * https://github.com/ndarray/ndarray + */ +#ifndef NDARRAY_views_hpp_INCLUDED +#define NDARRAY_views_hpp_INCLUDED + +#include "ndarray/common.hpp" + +namespace ndarray { +namespace views { + +struct Begin {}; +struct End {}; +struct All {}; + +struct NewAxis {}; + +struct NegUnit; + +struct PosUnit { + PosUnit operator+() const { return PosUnit(); } + NegUnit operator-() const; +}; + +struct NegUnit { + NegUnit operator+() const { return NegUnit(); } + PosUnit operator-() const; +}; + +inline NegUnit PosUnit::operator-() const { return NegUnit(); } +inline PosUnit NegUnit::operator-() const { return PosUnit(); } + +#if __cplusplus >= 201703L + inline Begin begin; + inline End end; + inline PosUnit unit; + inline NewAxis newaxis; + inline All all; +#endif + +namespace detail { + +struct Index { + Offset value; +}; + +template +struct Slice { + Start start; + Stop stop; + Step step; +}; + +inline Index interpret(Offset index) { return Index{index}; } +inline NewAxis interpret(NewAxis) { return NewAxis(); } +inline Slice interpret(All) { return Slice{Begin(), End(), PosUnit()}; } + +inline Slice interpret(Offset a, Offset b, Offset c) { return Slice{a, b, c}; } +inline Slice interpret(Offset a, End b, Offset c) { return Slice{a, b, c}; } +inline Slice< Begin, Offset, Offset> interpret( Begin a, Offset b, Offset c) { return Slice< Begin, Offset, Offset>{a, b, c}; } +inline Slice< Begin, End, Offset> interpret( Begin a, End b, Offset c) { return Slice< Begin, End, Offset>{a, b, c}; } + +inline Slice interpret(Offset a, Offset b, PosUnit c=PosUnit()) { return Slice{a, b, c}; } +inline Slice interpret(Offset a, End b, PosUnit c=PosUnit()) { return Slice{a, b, c}; } +inline Slice< Begin, Offset, PosUnit> interpret( Begin a, Offset b, PosUnit c=PosUnit()) { return Slice< Begin, Offset, PosUnit>{a, b, c}; } +inline Slice< Begin, End, PosUnit> interpret( Begin a, End b, PosUnit c=PosUnit()) { return Slice< Begin, End, PosUnit>{a, b, c}; } + +inline Slice interpret(Offset a, Offset b, NegUnit c) { return Slice{a, b, c}; } +inline Slice interpret(Offset a, End b, NegUnit c) { return Slice{a, b, c}; } +inline Slice< Begin, Offset, NegUnit> interpret( Begin a, Offset b, NegUnit c) { return Slice< Begin, Offset, NegUnit>{a, b, c}; } +inline Slice< Begin, End, NegUnit> interpret( Begin a, End b, NegUnit c) { return Slice< Begin, End, NegUnit>{a, b, c}; } + +template struct Sequence; + +template +class Sequence { +public: + + template + auto operator()(Index index) const { + return append(interpret(index)); + } + + template + auto operator()(Start start, Stop stop) const { + return append(interpret(start, stop)); + } + + template + auto operator()(Start start, Stop stop, Step step) const { + return append(interpret(start, stop, step)); + } + + template + Sequence append(Next const & next) const { + return Sequence{next, *this}; + } + + Current current; +}; + +template +class Sequence { +public: + + template + auto operator()(Index index) const { + return append(interpret(index)); + } + + template + auto operator()(Start start, Stop stop) const { + return append(interpret(start, stop)); + } + + template + auto operator()(Start start, Stop stop, Step step) const { + return append(interpret(start, stop, step)); + } + + template + Sequence append(Next const & next) const { + return Sequence{next, *this}; + } + + Current current; + Sequence previous; +}; + +template +Sequence make_sequence(Next const & next) { + return Sequence{next}; +} + +} // namespace detail + +template +inline auto view(Index index) { + return detail::make_sequence(detail::interpret(index)); +} + +template +inline auto view(Start start, Stop stop) { + return detail::make_sequence(detail::interpret(start, stop)); +} + +template +inline auto view(Start start, Stop stop, Step step) { + return detail::make_sequence(detail::interpret(start, stop, step)); +} + +} // namespace views +} // ndarray + +#endif // !NDARRAY_views_hpp_INCLUDED diff --git a/include/ndarray_fwd.h b/include/ndarray_fwd.h deleted file mode 100644 index 0b1b531b..00000000 --- a/include/ndarray_fwd.h +++ /dev/null @@ -1,110 +0,0 @@ -// -*- c++ -*- -/* - * Copyright (c) 2010-2012, Jim Bosch - * All rights reserved. - * - * ndarray is distributed under a simple BSD-like license; - * see the LICENSE file that should be present in the root - * of the source distribution, or alternately available at: - * https://github.com/ndarray/ndarray - */ -#ifndef NDARRAY_ndarray_fwd_h_INCLUDED -#define NDARRAY_ndarray_fwd_h_INCLUDED - -/** - * @file ndarray_fwd.h - * - * @brief Forward declarations and default template parameters for ndarray. - */ - -/// \defgroup ndarrayMainGroup Main - -/// \defgroup ndarrayOpGroup Operators - -/// \defgroup ndarrayVectorGroup Vectors - -/// @internal \defgroup ndarrayInternalGroup Internals - -#include - -#include -#include -#include -#include -#include - -#ifdef __GNUC__ -#if __GNUC__ == 4 && __GNUC_MINOR__ == 5 -#define GCC_45 -#endif -#endif - -#define NDARRAY_ASSERT(ARG) assert(ARG) - -namespace ndarray { - -template struct ArrayTraits; -template struct ExpressionTraits; -class Manager; - -/// @brief An enumeration for stride computation. -enum DataOrderEnum { ROW_MAJOR=1, COLUMN_MAJOR=2 }; - -typedef std::size_t Size; -typedef std::ptrdiff_t Offset; - -namespace detail { - -template class Core; - -class CountingExpression; - -template < - typename Operand, - typename UnaryFunction, - int N = ExpressionTraits::ND::value - > -class UnaryOpExpression; - -template < - typename Operand1, - typename Operand2, - typename BinaryFunction, - int N = ExpressionTraits::ND::value - > -class BinaryOpExpression; - -template struct IteratorTraits; - -template class NestedIterator; - -template class StridedIterator; - -#ifndef GCC_45 - -template < - typename Operand, - typename UnaryFunction - > -class UnaryOpIterator; - -template < - typename Operand1, - typename Operand2, - typename BinaryFunction - > -class BinaryOpIterator; - -#endif - -} // namespace detail - -template class ExpressionBase; -template class ArrayBase; -template class ArrayRef; -template class Array; -template struct Vector; - -} // namespace ndarray - -#endif // !NDARRAY_ndarray_fwd_h_INCLUDED diff --git a/tests/Array.cpp b/tests/Array.cpp new file mode 100644 index 00000000..c67918df --- /dev/null +++ b/tests/Array.cpp @@ -0,0 +1,185 @@ +// -*- c++ -*- +/* + * Copyright (c) 2010-2018, Jim Bosch + * All rights reserved. + * + * ndarray is distributed under a simple BSD-like license; + * see the LICENSE file that should be present in the root + * of the source distribution, or alternately available at: + * https://github.com/ndarray/ndarray + */ +#include + +#include "catch2/catch.hpp" + +#define NDARRAY_ASSERT_AUDIT_ENABLED true +#include "ndarray/Array.hpp" + +using namespace ndarray; + +namespace { + +template +class TestIndexVector { +public: + + TestIndexVector(std::initializer_list data) : _data(data) {} + + std::initializer_list initializer_list() const { return _data; } + + std::vector vector() const { return std::vector(_data.begin(), _data.end()); } + + std::array array() const { + std::array r; + std::copy(_data.begin(), _data.end(), r.begin()); + return r; + } + + T const & operator[](Size n) const { return _data.begin()[n]; } + +private: + std::initializer_list _data; +}; + +template +class TestStructure { +public: + + TestStructure(std::initializer_list shape_, std::initializer_list strides_) : + shape(shape_), strides(strides_), full_size(1) + { + for (Size i = 0; i < N; ++i) { + full_size *= shape[i]; + } + } + + template + void check(Array const & array) const { + REQUIRE(array.shape() == shape.array()); + REQUIRE(array.strides() == strides.array()); + REQUIRE(array.full_size() == full_size); + REQUIRE(array.size() == shape[0]); + REQUIRE(array.stride() == strides[0]); + REQUIRE(!array.empty()); + } + + template + void runContiguousConstructionTest(MemoryOrder order) const { + SECTION("Automatic strides with allocation") { + check(Array(shape.initializer_list(), order)); + check(Array(shape.vector(), order)); + check(Array(shape.array(), order)); + } + using U = typename std::remove_const::type; + std::shared_ptr data(new U[full_size], std::default_delete()); + SECTION("Automatic strides without allocation") { + check(Array(data, shape.initializer_list(), order)); + check(Array(data, shape.vector(), order)); + check(Array(data, shape.array(), order)); + } + SECTION("Explicit strides") { + check(Array(data, shape.initializer_list(), strides.initializer_list())); + check(Array(data, shape.vector(), strides.vector())); + check(Array(data, shape.vector(), strides.array())); + check(Array(data, shape.array(), strides.vector())); + check(Array(data, shape.array(), strides.array())); + } + } + + template + void runBadStrideTest() const { + Error::ScopedHandler errors(&Error::throw_handler); + using U = typename std::remove_const::type; + std::shared_ptr data(new U[full_size], std::default_delete()); + auto construct1 = [data, this]() { + return Array(data, shape.initializer_list(), strides.initializer_list()); + }; + auto construct2 = [data, this]() { + return Array(data, shape.vector(), strides.vector()); + }; + auto construct3 = [data, this]() { + return Array(data, shape.array(), strides.array()); + }; + REQUIRE_THROWS_AS(construct1(), std::logic_error); + REQUIRE_THROWS_AS(construct2(), std::logic_error); + REQUIRE_THROWS_AS(construct3(), std::logic_error); + } + + TestIndexVector shape; + TestIndexVector strides; + Size full_size; +}; + +} // + + +TEST_CASE("Array: construction", "[Array]") { + TestStructure<3> rmc = {{4, 3, 2}, {24, 8, 4}}; + SECTION("Row-major contiguous") { + rmc.runContiguousConstructionTest(MemoryOrder::ROW_MAJOR); + rmc.runContiguousConstructionTest(MemoryOrder::ROW_MAJOR); + } + TestStructure<3> cmc = {{4, 3, 2}, {8, 32, 96}}; + SECTION("Column-major contiguous") { + cmc.runContiguousConstructionTest(MemoryOrder::COL_MAJOR); + cmc.runContiguousConstructionTest(MemoryOrder::COL_MAJOR); + } + SECTION("Non-contiguous") { + // Row-major contiguous strides are not at all column-major contiguous + rmc.runBadStrideTest(); + rmc.runBadStrideTest(); + rmc.runBadStrideTest(); + // Colum-major contiguous strides are not at all row-major contiguous + cmc.runBadStrideTest(); + cmc.runBadStrideTest(); + cmc.runBadStrideTest(); + // Contiguous strides for double are not at contiguous for float + cmc.runBadStrideTest(); + cmc.runBadStrideTest(); + cmc.runBadStrideTest(); + cmc.runBadStrideTest(); + cmc.runBadStrideTest(); + cmc.runBadStrideTest(); + } +} + + +TEST_CASE("Array: indexing", "[Array]") { + Array array({4, 3, 2}); + std::iota(array.data(), array.data() + array.full_size(), 0); + int n = 0; + for (Size i = 0; i != array.size(); ++i) { + auto r1 = array[i]; + REQUIRE(Size(decltype(r1)::N) == 2u); // extra Size(...) cast to avoid taking address of constexpr + REQUIRE(Size(decltype(r1)::C) == 2u); + for (Size j = 0; j != r1.size(); ++j) { + auto r2 = r1[j]; + REQUIRE(Size(decltype(r2)::N) == 1u); // extra Size(...) cast to avoid taking address of constexpr + REQUIRE(Size(decltype(r2)::C) == 1u); + for (Size k = 0; k != r2.size(); ++k) { + int & v = r2[k]; + REQUIRE(v == n); + ++n; + } + } + } +} + + +TEST_CASE("Array: iterators", "[Array]") { + Array array({4, 3, 2}); + std::iota(array.data(), array.data() + array.full_size(), 0); + int n = 0; + for (auto r1 : array) { + REQUIRE(Size(decltype(r1)::N) == 2u); // extra Size(...) cast to avoid taking address of constexpr + REQUIRE(Size(decltype(r1)::C) == 2u); + for (auto r2 : r1) { + REQUIRE(Size(decltype(r2)::N) == 1u); // extra Size(...) cast to avoid taking address of constexpr + REQUIRE(Size(decltype(r2)::C) == 1u); + for (int & v : r2) { + REQUIRE(v == n); + ++n; + } + } + } +} diff --git a/tests/ArrayImpl.cpp b/tests/ArrayImpl.cpp new file mode 100644 index 00000000..af2b657e --- /dev/null +++ b/tests/ArrayImpl.cpp @@ -0,0 +1,111 @@ +// -*- c++ -*- +/* + * Copyright (c) 2010-2018, Jim Bosch + * All rights reserved. + * + * ndarray is distributed under a simple BSD-like license; + * see the LICENSE file that should be present in the root + * of the source distribution, or alternately available at: + * https://github.com/ndarray/ndarray + */ +#include +#include "catch2/catch.hpp" +#include "ndarray/detail/ArrayImpl.hpp" + +using namespace ndarray; + +namespace { + +struct CountedElement { + + static int next; + + static void reset() { next = 0; } + + CountedElement() : value(next++) {} + + int value; +}; + +int CountedElement::next = 0; + +template +void check_index(detail::ArrayImpl<2> const & a, std::initializer_list> const & b) { + auto shape = a.layout->shape(); + for (Size i = 0; i < shape[0]; ++i) { + for (Size j = 0; j < shape[1]; ++j) { + std::array indices = {i, j}; + T * v = reinterpret_cast(a.index(indices)); + REQUIRE(*v == b.begin()[i].begin()[j]); + } + } +} + +template +void check_comparisons(detail::ArrayImpl const & a, detail::ArrayImpl const & b) { + REQUIRE(a == a); + REQUIRE(b == b); + REQUIRE(a == detail::ArrayImpl(a)); + REQUIRE(b == detail::ArrayImpl(b)); + REQUIRE(a == detail::ArrayImpl(a.buffer, a.layout)); + REQUIRE(b == detail::ArrayImpl(b.buffer, b.layout)); + REQUIRE(a != detail::ArrayImpl()); + REQUIRE(b != detail::ArrayImpl()); + REQUIRE(a != detail::ArrayImpl(a.buffer, b.layout)); + REQUIRE(a != detail::ArrayImpl(b.buffer, a.layout)); + REQUIRE(b != detail::ArrayImpl(a.buffer, b.layout)); + REQUIRE(b != detail::ArrayImpl(b.buffer, a.layout)); +} + +void check_index_pair(detail::ArrayImpl<2> const & rm, detail::ArrayImpl<2> const & cm) { + check_index(rm, {{0, 1, 2}, + {3, 4, 5}}); + check_index(cm, {{0, 2, 4}, + {1, 3, 5}}); +} + +} // + +TEST_CASE("detail::ArrayImpl: construct and allocate", "[detail][ArrayImpl]") { + std::array shape = {2, 3}; + CountedElement::reset(); + detail::ArrayImpl<2> rm(shape, MemoryOrder::ROW_MAJOR, detail::TypeTag()); + CountedElement::reset(); + detail::ArrayImpl<2> cm(shape, MemoryOrder::COL_MAJOR, detail::TypeTag()); + check_index_pair(rm, cm); + check_comparisons(rm, cm); // requires buffers to differ, so we only run this test here +} + +TEST_CASE("detail::ArrayImpl: external data, automatic strides", "[detail][ArrayImpl]") { + std::array shape = {2, 3}; + CountedElement::reset(); + std::shared_ptr data(new CountedElement[6], std::default_delete()); + detail::ArrayImpl<2> rm(data, shape, MemoryOrder::ROW_MAJOR); + detail::ArrayImpl<2> cm(data, shape, MemoryOrder::COL_MAJOR); + check_index_pair(rm, cm); +} + +TEST_CASE("detail::ArrayImpl: external data, explicit strides", "[detail][ArrayImpl]") { + std::array shape = {2, 3}; + Offset s = sizeof(CountedElement); + std::array rm_strides = {s*3, s}; + std::array cm_strides = {s, s*2}; + CountedElement::reset(); + std::shared_ptr data(new CountedElement[6], std::default_delete()); + detail::ArrayImpl<2> rm(data, shape, rm_strides); + detail::ArrayImpl<2> cm(data, shape, cm_strides); + check_index_pair(rm, cm); +} + +TEST_CASE("detail::ArrayImpl: external buffer, Layout", "[detail][ArrayImpl]") { + std::array shape = {2, 3}; + auto rm_layout = detail::Layout<2>::make(shape, sizeof(CountedElement), MemoryOrder::ROW_MAJOR); + auto cm_layout = detail::Layout<2>::make(shape, sizeof(CountedElement), MemoryOrder::COL_MAJOR); + CountedElement::reset(); + std::shared_ptr data(new CountedElement[6], std::default_delete()); + std::shared_ptr buffer(data, reinterpret_cast(data.get())); + detail::ArrayImpl<2> rm(buffer, rm_layout); + detail::ArrayImpl<2> cm(buffer, cm_layout); + check_index_pair(rm, cm); +} + diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt deleted file mode 100644 index a36ae630..00000000 --- a/tests/CMakeLists.txt +++ /dev/null @@ -1,66 +0,0 @@ -# Tests are split by feature & dependency and are enabled on a -# per-dependency basis. -# -# A test is formatted as: -# (1) An if-guard over an appropriate option. -# (2) Resolution and inclusion of any required dependencies. -# (3) Build of the test executable. -# (4) Addition of the test executable via add_test. - -### Core tests, which rely only on boost-test and ndarray. -find_package(Boost COMPONENTS unit_test_framework REQUIRED) - -include_directories( ${PROJECT_SOURCE_DIR}/include) - -add_executable(ndarray_test ndarray.cc) - -target_link_libraries(ndarray_test ndarray Boost::unit_test_framework) -add_test(test_ndarray ndarray_test) - -add_executable(views views.cc) - -target_link_libraries(views Boost::unit_test_framework ) -add_test(test_views views) - - -### Eigen dependency tests -if(NDARRAY_EIGEN) - find_package(Eigen3 REQUIRED) - add_executable(ndarray-eigen ndarray-eigen.cc) - target_link_libraries(ndarray-eigen ndarray Eigen3::Eigen Boost::unit_test_framework) - add_test(test_ndarray_eigen ndarray-eigen) -endif(NDARRAY_EIGEN) - -# note: this is a workaround for -# https://github.com/FFTW/fftw3/issues/130 -# one would prefer to use find_package(fftw3) -### FFTW dependency tests -if(NDARRAY_FFTW) - include(FindPkgConfig) - pkg_search_module(FFTW3 REQUIRED fftw3) - include_directories( ${FFTW3_INCLUDES} ) - add_executable(ndarray-fft ndarray-fft.cc) - target_link_libraries(ndarray-fft ndarray Boost::unit_test_framework ${FFTW3_LIBRARIES}) - add_test(test_ndarray-fft ndarray-fft) -endif(NDARRAY_FFTW) - -### Python resolution -if(NDARRAY_PYBIND11) - find_package(Python ${PYTHON_VERSION} COMPONENTS Interpreter Development NumPy REQUIRED) -ENDIF() - -###Pybind11 dependency tests (also depend on Eigen) -if(NDARRAY_PYBIND11) - if(NDARRAY_EIGEN) - find_package(pybind11 REQUIRED) - - pybind11_add_module(pybind11_test_mod pybind11_test_mod.cc ) - target_link_libraries(pybind11_test_mod PRIVATE Eigen3::Eigen) - configure_file(pybind11_test.py pybind11_test.py COPYONLY) - add_test(NAME pybind11_test - COMMAND ${Python_EXECUTABLE} - ${CMAKE_CURRENT_BINARY_DIR}/pybind11_test.py) - else(NDARRAY_EIGEN) - message(STATUS "Skipping pybind11 tests as they depend on Eigen") - endif(NDARRAY_EIGEN) -endif(NDARRAY_PYBIND11) diff --git a/tests/Layout.cpp b/tests/Layout.cpp new file mode 100644 index 00000000..f75ceb9b --- /dev/null +++ b/tests/Layout.cpp @@ -0,0 +1,156 @@ +// -*- c++ -*- +/* + * Copyright (c) 2010-2018, Jim Bosch + * All rights reserved. + * + * ndarray is distributed under a simple BSD-like license; + * see the LICENSE file that should be present in the root + * of the source distribution, or alternately available at: + * https://github.com/ndarray/ndarray + */ +#include +#include "catch2/catch.hpp" + +#define NDARRAY_ASSERT_AUDIT_ENABLED true + +#include "ndarray/detail/Layout.hpp" + +using namespace ndarray; + +namespace { + +template +void check_get_dim( + detail::Layout const & layout, + std::array const & shape, + std::array const & strides, + std::integral_constant +) { + // empty specialization to break check_get_dim recursion (below) +} + +template +void check_get_dim( + detail::Layout const & layout, + std::array const & shape, + std::array const & strides, + std::integral_constant +) { + REQUIRE(detail::get_dim

(layout).size() == shape[P]); + REQUIRE(detail::get_dim

(layout).stride() == strides[P]); + check_get_dim(layout, shape, strides, std::integral_constant()); +} + +template +void check_layout( + detail::Layout const & layout, + std::array const & shape, + std::array const & strides +) { + REQUIRE(layout == *detail::Layout::make(shape, strides)); + REQUIRE(layout.shape() == shape); + REQUIRE(layout.strides() == strides); + REQUIRE(layout.full_size() == std::accumulate(shape.begin(), shape.end(), + static_cast(1), std::multiplies())); + Size i = 0; + layout.for_each_dim( + [&i, &shape, &strides](auto const & d) { + REQUIRE(d.size() == shape[i]); + REQUIRE(d.stride() == strides[i] ); + ++i; + return true; + } + ); + Size j = N - 1; + layout.for_each_dim_r( + [&j, &shape, &strides](auto const & d) { + REQUIRE(d.size() == shape[j]); + REQUIRE(d.stride() == strides[j]); + --j; + return true; + } + ); + check_get_dim(layout, shape, strides, std::integral_constant()); +} + +} // + +TEST_CASE("detail::Layout: automatic row-major contiguous strides", "[detail][Layout]") { + Error::ScopedHandler errors(&Error::throw_handler); + Size const element_size = 2; + std::array shape = {3, 4, 5}; + std::array strides = {40, 10, 2}; + auto layout = detail::Layout<3>::make(shape, element_size, MemoryOrder::ROW_MAJOR); + check_layout(*layout, shape, strides); + REQUIRE_NOTHROW(layout->check_contiguousness<3>(element_size)); + REQUIRE_NOTHROW(layout->check_contiguousness<2>(element_size)); + REQUIRE_NOTHROW(layout->check_contiguousness<1>(element_size)); + REQUIRE_NOTHROW(layout->check_contiguousness<0>(element_size)); + REQUIRE_THROWS_AS(layout->check_contiguousness<-1>(element_size), std::logic_error); + REQUIRE_THROWS_AS(layout->check_contiguousness<-2>(element_size), std::logic_error); + REQUIRE_THROWS_AS(layout->check_contiguousness<-3>(element_size), std::logic_error); +} + +TEST_CASE("detail::Layout: explicit partially row-major strides", "[detail][Layout]") { + Error::ScopedHandler errors(&Error::throw_handler); + Size const element_size = 2; + std::array shape = {3, 4, 5}; + std::array strides = {80, 10, 2}; + auto layout = detail::Layout<3>::make(shape, strides); + check_layout(*layout, shape, strides); + REQUIRE_THROWS_AS(layout->check_contiguousness<3>(element_size), std::logic_error); + REQUIRE_NOTHROW(layout->check_contiguousness<2>(element_size)); + REQUIRE_NOTHROW(layout->check_contiguousness<1>(element_size)); + REQUIRE_NOTHROW(layout->check_contiguousness<0>(element_size)); + REQUIRE_THROWS_AS(layout->check_contiguousness<-1>(element_size), std::logic_error); + REQUIRE_THROWS_AS(layout->check_contiguousness<-2>(element_size), std::logic_error); + REQUIRE_THROWS_AS(layout->check_contiguousness<-3>(element_size), std::logic_error); +} + +TEST_CASE("detail::Layout: automatic column-major contiguous strides", "[detail][Layout]") { + Error::ScopedHandler errors(&Error::throw_handler); + Size const element_size = 2; + std::array shape = {3, 4, 5}; + std::array strides = {2, 6, 24}; + auto layout = detail::Layout<3>::make(shape, element_size, MemoryOrder::COL_MAJOR); + check_layout(*layout, shape, strides); + REQUIRE_THROWS_AS(layout->check_contiguousness<3>(element_size), std::logic_error); + REQUIRE_THROWS_AS(layout->check_contiguousness<2>(element_size), std::logic_error); + REQUIRE_THROWS_AS(layout->check_contiguousness<1>(element_size), std::logic_error); + REQUIRE_NOTHROW(layout->check_contiguousness<0>(element_size)); + REQUIRE_NOTHROW(layout->check_contiguousness<-1>(element_size)); + REQUIRE_NOTHROW(layout->check_contiguousness<-2>(element_size)); + REQUIRE_NOTHROW(layout->check_contiguousness<-3>(element_size)); +} + +TEST_CASE("detail::Layout: explicit partially column-major strides", "[detail][Layout]") { + Error::ScopedHandler errors(&Error::throw_handler); + Size const element_size = 2; + std::array shape = {3, 4, 5}; + std::array strides = {2, 6, 48}; + auto layout = detail::Layout<3>::make(shape, strides); + check_layout(*layout, shape, strides); + REQUIRE_THROWS_AS(layout->check_contiguousness<3>(element_size), std::logic_error); + REQUIRE_THROWS_AS(layout->check_contiguousness<2>(element_size), std::logic_error); + REQUIRE_THROWS_AS(layout->check_contiguousness<1>(element_size), std::logic_error); + REQUIRE_NOTHROW(layout->check_contiguousness<0>(element_size)); + REQUIRE_NOTHROW(layout->check_contiguousness<-1>(element_size)); + REQUIRE_NOTHROW(layout->check_contiguousness<-2>(element_size)); + REQUIRE_THROWS_AS(layout->check_contiguousness<-3>(element_size), std::logic_error); +} + +TEST_CASE("detail::Layout: explicit noncontiguous strides", "[detail][Layout]") { + Error::ScopedHandler errors(&Error::throw_handler); + Size const element_size = 2; + std::array shape = {3, 4, 5}; + std::array strides = {4, 60, 12}; + auto layout = detail::Layout<3>::make(shape, strides); + check_layout(*layout, shape, strides); + REQUIRE_THROWS_AS(layout->check_contiguousness<3>(element_size), std::logic_error); + REQUIRE_THROWS_AS(layout->check_contiguousness<2>(element_size), std::logic_error); + REQUIRE_THROWS_AS(layout->check_contiguousness<1>(element_size), std::logic_error); + REQUIRE_NOTHROW(layout->check_contiguousness<0>(element_size)); + REQUIRE_THROWS_AS(layout->check_contiguousness<-1>(element_size), std::logic_error); + REQUIRE_THROWS_AS(layout->check_contiguousness<-2>(element_size), std::logic_error); + REQUIRE_THROWS_AS(layout->check_contiguousness<-3>(element_size), std::logic_error); +} diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/compilation.py b/tests/compilation.py new file mode 100644 index 00000000..ee0d0763 --- /dev/null +++ b/tests/compilation.py @@ -0,0 +1,320 @@ +# +# Copyright (c) 2010-2018, Jim Bosch +# All rights reserved. +# +# ndarray is distributed under a simple BSD-like license; +# see the LICENSE file that should be present in the root +# of the source distribution, or alternately available at: +# https://github.com/ndarray/ndarray +# + +import os +import io +import json +import inspect +import subprocess +import re +import textwrap + +__all__ = ("Compiler", "SnippetContext", "CompilationTestMixin") + + +COMPILE_COMMAND_CMAKE_TARGET = "tests/main.cpp" + +OUTPUT_DIR = "compile_tests" + + +class Compiler: + """An object that attempts to compile string code snippets. + + Parameters + ---------- + args : `list` + A command and command-line arguments, of the sort accepted as the + ``args`` argment to `subprocess.run`. + cwd : `str` + Working directory used to compile the target file. Should be passed + as the ``cwd`` argment to `subprocess.run`. + source_ext : `str` + Filename extension to use for source files. + output_ext : `str` + Filename extension to use for output files. + source_opt : `str` + Command-line option indicating the source file. + output_opt : `str` + Command-line option indicating the output file. + """ + def __init__(self, args, cwd, source_ext=".cpp", output_ext=".o", source_opt='-c', output_opt='-o'): + self.args = args + self.cwd = cwd + self.source_ext = source_ext + self.output_ext = output_ext + self.source_opt = source_opt + self.output_opt = output_opt + + @classmethod + def fromCMake(cls, filename, source_opt='-c', output_opt='-o'): + """Create a Compiler that uses CMake's compiler commands. + + This reads CMake's "compile_commands.json" (enabled with + CMAKE_EXPORT_COMPILE_COMMANDS, only available for Makefile or Ninja + builds), finds the source filename that ends with the given string (must + be unique), and removes the source and output arguments so they can be + specialized by __call__. + + Parameters + ---------- + filename : `str`, path-like + Name of a source file built by CMake whose build command we should + copy. + source_opt : `str` + Command-line option indicating the source file. + output_opt : `str` + Command-line option indicating the output file. + + Returns + ------- + compiler : `Compiler` + New Compiler instance. + """ + with open("compile_commands.json", "r") as f: + targets = json.load(f) + target, = [t for t in targets if t["file"].endswith(filename)] + args = [] + next_is_source = False + next_is_output = False + source_ext = "" + output_ext = ".o" + # discard any -o or -c arguments; we'll supply those ourselves + for arg in target["command"].split(): + if arg.startswith(output_opt): + if arg == output_opt: + next_is_output = True + _, output_ext = os.path.splitext(arg[2:]) + continue + if arg.startswith(source_opt): + if arg == source_opt: + next_is_source = True + _, source_ext = os.path.splitext(arg[2:]) + continue + if next_is_output: + next_is_output = False + _, output_ext = os.path.splitext(arg) + if next_is_source: + next_is_source = False + _, source_ext = os.path.splitext(arg) + continue + args.append(arg) + return cls(args=args, cwd=target["directory"], source_ext=source_ext, output_ext=output_ext, + source_opt=source_opt, output_opt=output_opt) + + def compile(self, name, code, output_dir=".", write_stderr=True, write_stdout=True): + """Attempt to compile the given code snippet. + + Parameters + ---------- + name : `str` + Name of the snippet, used to create source and output filenames. + code : `str` + Block of source code to compile. + output_dir : `str`, path-like + Directory for both source and output files. + write_stderr : `bool` + If True, write a stderr to [output_dir]/[name].stderr if it would be + non-empty. + write_stdout : `bool` + If True, write a stdout to [output_dir]/[name].stdout if it would be + non-empty. + + Returns + ------- + process : `subprocess.CompletedProcess` + A struct containing information about the compiler process, + including ``returncode`` (`int`), ``stdout`` (`str`), and + ``stderr`` (`str`). + output_file : `str`, path-like + Name of the output file that should have been generated if + the compiler was successful. + """ + source_file = os.path.join(output_dir, name + self.source_ext) + output_file = os.path.join(output_dir, name + self.output_ext) + if not os.path.isdir(os.path.dirname(source_file)): + os.makedirs(os.path.dirname(source_file)) + with open(source_file, 'w') as f: + f.write(code) + args = self.args + [self.source_opt, source_file, + self.output_opt, output_file] + process = subprocess.run(args, cwd=self.cwd, universal_newlines=True, stderr=subprocess.PIPE, + stdout=subprocess.PIPE) + + if process.stderr and write_stderr: + with open(os.path.join(output_dir, name + ".stderr"), "w") as f: + f.write(process.stderr) + if process.stdout and write_stdout: + with open(os.path.join(output_dir, name + ".stdout"), "w") as f: + f.write(process.stdout) + return process, output_file + + +compiler = Compiler.fromCMake(COMPILE_COMMAND_CMAKE_TARGET) + + +class SnippetContext: + """A helper for formatting code snippets into compileable files. + + Parameters + ---------- + preamble : `str` or `bool` + A block of code to add to the beginning of every snippet. + Typically #includes, using declarations, and common test classes. + The preamble is always dedented to make it more natural to write + as a multi-line string. + prefix : `str` or `bool` + A block of code inserted after ``preamble`` and before ``code``. + Defaults to the opening of the main() function. + suffix : `str` or `bool` + A block of code inserted after ``code``. + Defaults to the opening closing brace of the main() function + started by ``prefix``. + indent : `str` or `bool` + Indentation to add to every line in ``code``. + base : `SnippetFormatter` + Another `SnippetFormatter` from which to inherit values. + If provided, passing `True` (the default) to any other argument + will cause that argument to be coped from ``base``. + """ + + def __init__(self, preamble=True, prefix=True, suffix=True, indent=True, base=None): + self.preamble = preamble + self.prefix = prefix + self.suffix = suffix + self.indent = indent + + def inherit_arg(arg, default): + given = getattr(self, arg) + if given is True: + if base is not None: + setattr(self, arg, getattr(base, arg)) + else: + setattr(self, arg, default) + elif given is False or given is None: + setattr(self, arg, "") + + inherit_arg("preamble", "") + inherit_arg("prefix", "int main() {") + inherit_arg("indent", " ") + if isinstance(self.indent, int): + self.indent = self.indent * " " + inherit_arg("suffix", self.indent + "return 0;\n}") + self.preamble = textwrap.dedent(self.preamble) + + def __call__(self, code): + """Format the given code.""" + lines = textwrap.dedent(code).splitlines() + b = io.StringIO() + b.write(self.preamble) + b.write("\n") + b.write(self.prefix) + b.write("\n") + for line in lines: + b.write(self.indent) + b.write(line) + b.write("\n") + b.write("\n") + b.write(self.suffix) + b.write("\n") + return b.getvalue() + + +def make_snippet_name(frame): + module, _ = os.path.splitext(os.path.basename(frame.filename)) + return "{}_{}_{}".format(module, frame.lineno, frame.function) + + +class CompilationTestMixin: + """A unittest.TestCase mixin for testing code compilation. + """ + + def assertCompiles(self, code, name=None, output_dir=None, context=None, **kwds): + """Assert that a code snippet does not compile. + + Parameters + ---------- + code: `str`, `list`, or `tuple` + Code to be compiled. If a list or tuple, elements will be joined + by newlines to form a string. + name : `str`, optional + Name of the snippet; used to generate filenames for source and + output files. If not provided, the calling method's + module, line number, and name are used to construct a name. + output_dir : `str`, path-like + Directory for source and output files. + context : callable, optional + A callable to apply to the code before compiling it (typically + a `SnippetContext`). If provided, the original code snippet + will be included in any test failure messages. + + Additional keyword arguments are passed to Compiler.compile. + """ + if name is None: + name = make_snippet_name(inspect.stack()[1]) + if output_dir is None: + output_dir = OUTPUT_DIR + if isinstance(code, (list, tuple)): + code = "\n".join(code) + if context is not None: + code = context(code) + process, _ = compiler.compile(name, code, output_dir=output_dir, **kwds) + self.assertEqual(process.returncode, 0, msg="{} did not compile".format(name)) + + def assertDoesNotCompile(self, code, name=None, stderr_regex=None, stdout_regex=None, + output_dir=None, context=None, **kwds): + """Assert that a code snippet does not compile. + + Parameters + ---------- + code : `str`, `list`, or `tuple` + Code to be compiled. If a list or tuple, elements will be joined + by newlines to form a string. + name : `str`, optional + Name of the snippet; used to generate filenames for source and + output files. If not provided, the calling method's + module, line number, and name are used to construct a name. + stderr_regex : `str` or compiled regular expression + A regular expression to search for in the compiler's STDERR. If + provided, failure to find this expression will also result in + an assertion failure. + stdout_regex : `str` or compiled regular expression + Like ``stderr_regex``, but applied to STDOUT. + output_dir : `str`, path-like + Directory for source and output files. + context : callable, optional + A callable to apply to the code before compiling it (typically + a `SnippetFormatter`). + + Additional keyword arguments are passed to Compiler.compile. + """ + if name is None: + name = make_snippet_name(inspect.stack()[1]) + if output_dir is None: + output_dir = OUTPUT_DIR + if isinstance(code, (list, tuple)): + code = "\n".join(code) + if context is not None: + code = context(code) + process, _ = compiler.compile(name, code, output_dir=output_dir, **kwds) + self.assertNotEqual(process.returncode, 0, msg="{} unexpectedly compiled".format(name)) + if stderr_regex is not None: + self.assertTrue( + re.search(stderr_regex, process.stderr) is not None, + msg="'{}' not found in stderr (see {}.stderr).".format( + stderr_regex, os.path.join(output_dir, name) + ) + ) + if stdout_regex is not None: + self.assertTrue( + re.search(stdout_regex, process.stdout) is not None, + msg="'{}' not found in stdout (see {}.stdout).".format( + stdout_regex, os.path.join(output_dir, name) + ) + ) diff --git a/tests/expressions.cpp b/tests/expressions.cpp new file mode 100644 index 00000000..841e0610 --- /dev/null +++ b/tests/expressions.cpp @@ -0,0 +1,193 @@ +// -*- c++ -*- +/* + * Copyright (c) 2010-2018, Jim Bosch + * All rights reserved. + * + * ndarray is distributed under a simple BSD-like license; + * see the LICENSE file that should be present in the root + * of the source distribution, or alternately available at: + * https://github.com/ndarray/ndarray + */ +#include +#include +#include +#include +#include +#include "catch2/catch.hpp" + +#define NDARRAY_ASSERT_AUDIT_ENABLED true + +#include "ndarray/expressions/Strided.hpp" +#include "ndarray/expressions/Sequential.hpp" +#include "ndarray/expressions/BulkParallel.hpp" + +using namespace ndarray; +using namespace ndarray::expressions; + + +namespace { + +/* + * Data for the tests below: a strided, row-major contiguous 3-d array + * with shape (2, 3, 4) that starts from zero and increments by one to 23. + */ +struct StridedTestData { + + StridedTestData() { + std::iota(values.begin(), values.end(), 0); + } + + auto expr() const { + std::array strides = {12, 4, 1}; + return StridedExpression(values.data(), shape, strides); + } + + std::array shape = {2, 3, 4}; + std::array values; +}; + +/** + * A ReductionFunctor that records all values it sees in a std::vector, + * allowing test code to verify what an expression generated. + */ +template +struct Recorder { + + Recorder() = default; + + bool accumulate(T x) { + _values.push_back(x); + return true; + } + + Recorder split() const { + return Recorder(*this); + } + + bool join(Recorder && other) { + _values.insert(_values.end(), other._values.begin(), other._values.end()); + return true; + } + + std::vector finish() && { + return std::move(_values); + } + +private: + std::vector _values; +}; + + +/* + * A version of Recorder designed to test asynchronous expression execution. + * Instances operate on a shared std::vector, and values below the given + * cutoff value are not appended until the vector's size is above the given + * cutoff size. + */ +template +struct WaitingRecorder { + + explicit WaitingRecorder(T cutoff_value, Size cutoff_size) : + _cutoff_value(cutoff_value), + _cutoff_size(cutoff_size), + _data(std::make_shared()) + {} + + bool accumulate(T x) { + using namespace std::chrono_literals; + if (x < _cutoff_value) { + while (true) { + std::this_thread::sleep_for(5ms); + std::lock_guard guard(_data->mutex); + if (_data->values.size() >= _cutoff_size) { + break; + } + } + } + std::lock_guard guard(_data->mutex); + _data->values.push_back(x); + return true; + } + + WaitingRecorder split() const { + return WaitingRecorder(*this); + } + + bool join(WaitingRecorder && other) { + return true; + } + + std::vector finish() && { + return std::move(_data->values); + } + +private: + + struct Data { + std::mutex mutex; + std::vector values; + }; + + T _cutoff_value; + Size _cutoff_size; + std::shared_ptr _data; +}; + +} // anonymous + + +TEST_CASE("expressions: Strided", "[expressions][Strided]") { + StridedTestData data; + auto expr = data.expr(); + Size n = 0; + auto t0 = expr.traverse(); + for (Size k0 = 0; k0 < data.shape[0]; ++k0) { + auto t1 = t0.evaluate(); + for (Size k1 = 0; k1 < data.shape[1]; ++k1) { + auto t2 = t1.evaluate(); + for (Size k2 = 0; k2 < data.shape[2]; ++k2) { + REQUIRE(t2.evaluate() == n); + ++n; + ++t2; + } + ++t1; + } + ++t0; + } +} + + +TEST_CASE("expressions: Sequential", "[expressions][Sequential]") { + StridedTestData data; + auto expr = data.expr(); + auto executor = makeExecutor(Sequential(), Sequential(), Sequential()); + static_assert(decltype(executor)::ndim == 3); + auto values = executor.reduce(expr, Recorder()); + REQUIRE(values.size() == data.values.size()); + REQUIRE(std::equal(values.begin(), values.end(), data.values.begin())); +} + + +#if NDARRAY_TEST_WITH_THREADS + +TEST_CASE("expressions: BulkParallel", "[expressions][BulkParallel]") { + StridedTestData data; + auto expr = data.expr(); + auto executor = makeExecutor(BulkParallel(2), Sequential(), Sequential()); + static_assert(decltype(executor)::ndim == 3); + // Recorder properly copies its state in split() and integrates it in + // join(), which ensures that even parallel execution maintains the order + // in which expression values are seen. + auto values1 = executor.reduce(expr, Recorder()); + REQUIRE(values1.size() == data.values.size()); + REQUIRE(std::equal(values1.begin(), values1.end(), data.values.begin())); + // WaitingRecorder shares state in split() and waits in accumulate in a way + // that should guarantee that we record the values 12-23 before 0-11. + auto values2 = executor.reduce(expr, WaitingRecorder(12, 12)); + REQUIRE(values2.size() == data.values.size()); + auto mid = values2.begin() + 12; + REQUIRE(std::equal(values2.begin(), mid, data.values.begin() + 12)); + REQUIRE(std::equal(mid, values2.end(), data.values.begin())); +} + +#endif // NDARRAY_TEST_WITH_THREADS diff --git a/tests/main.cpp b/tests/main.cpp new file mode 100644 index 00000000..62bf7476 --- /dev/null +++ b/tests/main.cpp @@ -0,0 +1,2 @@ +#define CATCH_CONFIG_MAIN +#include "catch2/catch.hpp" diff --git a/tests/ndarray-eigen.cc b/tests/ndarray-eigen.cc deleted file mode 100644 index 7d4785dd..00000000 --- a/tests/ndarray-eigen.cc +++ /dev/null @@ -1,152 +0,0 @@ -// -*- c++ -*- -/* - * Copyright (c) 2010-2012, Jim Bosch - * All rights reserved. - * - * ndarray is distributed under a simple BSD-like license; - * see the LICENSE file that should be present in the root - * of the source distribution, or alternately available at: - * https://github.com/ndarray/ndarray - */ -#include "ndarray/eigen.h" -#include "Eigen/SVD" - -#define BOOST_TEST_DYN_LINK -#define BOOST_TEST_MODULE ndarray-eigen -#include "boost/test/unit_test.hpp" - -template -void testElements2(T const & a, U const & b) { - BOOST_CHECK( a.template getSize<0>() == b.rows() ); - BOOST_CHECK( a.template getSize<1>() == b.cols() ); - BOOST_CHECK( a.template getStride<0>() == b.rowStride() ); - BOOST_CHECK( a.template getStride<1>() == b.colStride() ); - for (int i = 0; i < b.rows(); ++i) { - for (int j = 0; j < b.cols(); ++j) { - BOOST_CHECK(&a[i][j] == &b.coeffRef(i,j)); - } - } -} - -template -void testElements1(T const & a, U const & b) { - BOOST_CHECK( a.template getSize<0>() == b.size() ); - BOOST_CHECK( a.template getStride<0>() == b.innerStride() ); - for (int i = 0; i < b.size(); ++i) { - BOOST_CHECK(&a[i] == &b.coeffRef(i)); - } -} - -template -ndarray::Array makeMutable(ndarray::Array const & a) { - return a; -} - -template -ndarray::ArrayRef makeMutable(ndarray::ArrayRef const & a) { - return a; -} - -template -ndarray::Array makeMutable(ndarray::Array const & a) { - return ndarray::const_array_cast(a); -} - -template -ndarray::ArrayRef makeMutable(ndarray::ArrayRef const & a) { - return ndarray::const_array_cast(a.shallow()).deep(); -} - -template -void testAsEigen1(A const & a) { - typedef typename boost::remove_const::type T; - ndarray::asEigen(makeMutable(a)).setRandom(); - int const n = asEigenMatrix(a).size(); - testElements1(a, ndarray::asEigen(a)); - Eigen::Matrix m1(n, 6); - m1.setRandom(n, 6); - Eigen::Matrix m2(6, 1); - m2.setRandom(6, 1); - asEigenMatrix(makeMutable(a)) = m1 * m2; - Eigen::Matrix m3 = m1 * m2; - for (int i = 0; i < n; ++i) { - BOOST_CHECK(a[i] == m3(i)); - } - Eigen::Array m4(n, 1); - m4.setRandom(); - Eigen::Array m5 = m4 * asEigenArray(a); - Eigen::Array m6 = m4 * m3.array(); - BOOST_CHECK((m5 == m6).all()); -} - -template -void testAsEigen2(A const & a) { - typedef typename boost::remove_const::type T; - ndarray::asEigen(makeMutable(a)).setRandom(); - int const m = asEigenMatrix(a).rows(); - int const n = asEigenMatrix(a).cols(); - testElements2(a, ndarray::asEigen(a)); - Eigen::Matrix m1(m, 6); - m1.setRandom(m, 6); - Eigen::Matrix m2(6, n); - m2.setRandom(6, n); - asEigenMatrix(makeMutable(a)) = m1 * m2; - Eigen::Matrix m3 = m1 * m2; - for (int i = 0; i < m; ++i) { - for (int j = 0; j < n; ++j) { - BOOST_CHECK(a[i][j] == m3(i, j)); - } - } - Eigen::Array m4(m, n); - m4.setRandom(); - Eigen::Array m5 = m4 * asEigenArray(a); - Eigen::Array m6 = m4 * m3.array(); - BOOST_CHECK((m5 == m6).all()); -} - -template -void invokeAsEigenTests() { - ndarray::Array a22(ndarray::allocate(5, 4)); - testAsEigen2<5, 4, XprKind>(a22); - - ndarray::Array a21(a22[ndarray::view()(0, 3)]); - testAsEigen2<5, 3, XprKind>(a21); - testAsEigen2<3, 5, XprKind>(a21.transpose()); - - ndarray::Array a20(a22[ndarray::view()(0, 4, 2)]); - testAsEigen2<5, 2, XprKind>(a20); - testAsEigen2<2, 5, XprKind>(a20.transpose()); - - ndarray::Array a11(ndarray::allocate(4)); - testAsEigen1<4, XprKind>(a11); - testAsEigen1<4, XprKind>(a11.transpose()); - ndarray::Array a10(a11[ndarray::view(0, 4, 2)]); - testAsEigen1<2, XprKind>(a10); - testAsEigen1<2, XprKind>(a10.transpose()); -} - -BOOST_AUTO_TEST_CASE(AsEigen) { - invokeAsEigenTests(); - invokeAsEigenTests(); - invokeAsEigenTests(); - invokeAsEigenTests(); -} - -template -void testSVD(Matrix const & a, Vector const & b, Vector & x) { - SVD svd(a, Eigen::ComputeThinU | Eigen::ComputeThinV); - x = svd.solve(b); - BOOST_CHECK((a.transpose() * a * x).isApprox(a.transpose() * b)); -} - -BOOST_AUTO_TEST_CASE(SVD) { - auto aArray = ndarray::Array(ndarray::allocate(8,5)); - auto bArray = ndarray::Array(ndarray::allocate(8)); - auto xArray = ndarray::Array(ndarray::allocate(5)); - auto a = ndarray::asEigenMatrix(aArray); - auto b = ndarray::asEigenMatrix(bArray); - auto x = ndarray::asEigenMatrix(xArray); - a.setRandom(); - b.setRandom(); - testSVD>(a, b, x); -} diff --git a/tests/ndarray-fft.cc b/tests/ndarray-fft.cc deleted file mode 100644 index 151f6dcc..00000000 --- a/tests/ndarray-fft.cc +++ /dev/null @@ -1,530 +0,0 @@ -// -*- c++ -*- -/* - * Copyright (c) 2010-2012, Jim Bosch - * All rights reserved. - * - * ndarray is distributed under a simple BSD-like license; - * see the LICENSE file that should be present in the root - * of the source distribution, or alternately available at: - * https://github.com/ndarray/ndarray - */ -#include - -#define BOOST_TEST_DYN_LINK -#define BOOST_TEST_MODULE ndarray-fft -#include "boost/test/unit_test.hpp" - -#include - -#ifndef GCC_45 - -template -static boost::test_tools::predicate_result -compareRelative( - ndarray::ExpressionBase const & a, - ndarray::ExpressionBase const & b, - double tolerance = 1E-4 -) { - ndarray::ApproximatelyEqual predicate(tolerance); - if (ndarray::all(ndarray::vectorize(predicate,a,b))) return true; - boost::test_tools::predicate_result r(false); - std::ostringstream oss; - oss << "\n" << a << "\n != \n" << b << "\n"; - r.message() << oss.str(); - return r; -}; - -template -static boost::test_tools::predicate_result -compareAbsolute( - ndarray::ExpressionBase const & a, - ndarray::ExpressionBase const & b, - double tolerance = 1E-4 -) { - if (ndarray::all(less(a-b, tolerance)) && ndarray::all(less(b-a, tolerance))) return true; - boost::test_tools::predicate_result r(false); - std::ostringstream oss; - oss << "\n" << a << "\n != \n" << b << "\n"; - r.message() << oss.str(); - return r; -}; - -template -struct FourierTransformTester { - typedef ndarray::FourierTransform FFT; - - static void testSingle( - typename FFT::Index const & shape, - typename FFT::ElementX const * xData, - typename FFT::ElementK const * kData - ) { - typename FFT::ArrayX xIn = FFT::initializeX(shape); - typename FFT::ArrayK kIn = FFT::initializeK(shape); - std::copy(xData,xData+xIn.getNumElements(),xIn.getData()); - std::copy(kData,kData+kIn.getNumElements(),kIn.getData()); - typename FFT::ArrayX x; - typename FFT::ArrayK k; - typename FFT::Ptr forward = FFT::planForward(shape,x,k); - typename FFT::Ptr inverse = FFT::planInverse(shape,k,x); - x.deep() = xIn; - forward->execute(); - BOOST_CHECK(compareRelative(k,kIn)); - x.deep() = 0.0; - inverse->execute(); - x.deep() /= shape.product(); - BOOST_CHECK(compareRelative(x,xIn)); - } - - static void testMultiplex( - typename FFT::MultiplexIndex const & shape, - typename FFT::ElementX const * xData, - typename FFT::ElementK const * kData - ) { - typename FFT::MultiplexArrayX xIn = FFT::initializeX(shape); - typename FFT::MultiplexArrayK kIn = FFT::initializeK(shape); - std::copy(xData,xData+xIn.getNumElements(),xIn.getData()); - std::copy(kData,kData+kIn.getNumElements(),kIn.getData()); - typename FFT::MultiplexArrayX x; - typename FFT::MultiplexArrayK k; - typename FFT::Ptr forward = FFT::planMultiplexForward(shape,x,k); - typename FFT::Ptr inverse = FFT::planMultiplexInverse(shape,k,x); - x.deep() = xIn; - forward->execute(); - BOOST_CHECK(compareRelative(k,kIn)); - x.deep() = 0.0; - inverse->execute(); - x.deep() /= (shape.product() / shape[0]); - BOOST_CHECK(compareRelative(x,xIn)); - } -}; - -BOOST_AUTO_TEST_CASE(real_1d) { - double xData1[] = { -0.28131077, -0.25505012, -0.35444799, 1.77553825, 1.655009 }; - std::complex kData1[] = { - std::complex(2.53973837,0.0), - std::complex(-0.99838585,3.06854867), - std::complex(-0.97476025,-0.90303271) - }; - FourierTransformTester::testSingle(ndarray::makeVector(5),xData1,kData1); - double xData2[] = { -0.42299104, 0.12242535, 0.37497334, 0.47846245, 1.19236641, -1.54989674 }; - std::complex kData2[] = { - std::complex(0.19533977,0.00000000), - std::complex(-2.39885906,-0.74039025), - std::complex(-0.01446277,-2.15615658), - std::complex(2.09335764,0.00000000), - }; - FourierTransformTester::testSingle(ndarray::makeVector(6),xData2,kData2); - double xData3[] = { - 1.23233014, 0.80816934, -0.89026449, 0.64365113, -0.95490008, - -2.31806551, 0.91599268, 0.48157162, -0.3389141 , 1.3208367 , - 0.24500594, 1.04908011, -0.47794142, -1.33640514, -0.49423689 - }; - std::complex kData3[] = { - std::complex(0.83898605,0.00000000), - std::complex(1.38650224,-0.77516568), - std::complex(1.27483008,-2.49514665), - std::complex(0.06142139,0.00000000), - std::complex(-1.74225958,-0.09723986), - std::complex(-4.08361489,1.01828964), - std::complex(-1.01449740,0.00000000), - std::complex(1.88429912,-1.97237400), - std::complex(-0.76453557,-0.09069146), - }; - FourierTransformTester::testMultiplex(ndarray::makeVector(3,5),xData3,kData3); - double xData4[] = { - 0.20943609, -0.70119705, -0.04658762, -0.31573777, 0.0884646 , - -0.53593713, -1.43900058, -0.60146942, -0.90063027, -1.85456039, - 1.82743304, -1.96060797, -1.13698948, 1.87844775, -0.20344457, - -0.15419973, 0.11970772, -1.23579322 - }; - std::complex kData4[] = { - std::complex(-1.30155887,0.00000000), - std::complex(-0.11433172,0.26007794), - std::complex(0.49132693,0.02616064), - std::complex(1.80418503,0.00000000), - std::complex(-4.92883560,0.00000000), - std::complex(-1.32888027,1.18552362), - std::complex(-2.47592366,-3.53962064), - std::complex(3.90443996,0.00000000), - std::complex(-0.73227153,0.00000000), - std::complex(-0.61959406,-2.41715370), - std::complex(-1.57064806,-2.97686989), - std::complex(-1.70918113,0.00000000), - }; - FourierTransformTester::testMultiplex(ndarray::makeVector(3,6),xData4,kData4); -} - -BOOST_AUTO_TEST_CASE(complex_1d) { - std::complex xData1[] = { - std::complex(-1.43357821,0.68468601), - std::complex(0.03127727,0.04293266), - std::complex(-1.24543586,-1.09965670), - std::complex(0.62473820,1.02028337), - std::complex(1.24406733,-0.10372565), - }; - std::complex kData1[] = { - std::complex(-0.77893128,0.54451968), - std::complex(-1.64390926,2.98280693), - std::complex(-0.55477338,-0.35644026), - std::complex(-4.75954620,1.77512198), - std::complex(0.56926906,-1.52257831), - }; - FourierTransformTester,1>::testSingle(ndarray::makeVector(5),xData1,kData1); - std::complex xData2[] = { - std::complex(1.11465199,0.04081224), - std::complex(-1.08053999,0.78927525), - std::complex(-2.05412193,-1.31663028), - std::complex(-0.12671457,0.16377679), - std::complex(2.01182088,-0.97858449), - std::complex(0.39217125,-0.92609902), - std::complex(-0.61199100,-0.16371873), - std::complex(-0.45056833,-1.23540146), - std::complex(0.18987288,0.07988060), - std::complex(0.43969961,-1.52021317), - std::complex(1.90211971,0.87354424), - std::complex(-2.12106807,-0.53018255), - std::complex(0.32101503,0.36269997), - std::complex(0.15932314,-0.08764936), - std::complex(-0.90755936,-1.64385208), - }; - std::complex kData2[] = { - std::complex(-0.13490361,-1.30135049), - std::complex(3.97794049,4.98890211), - std::complex(2.13438706,-0.17771391), - std::complex(-2.75975830,-0.14685547), - std::complex(2.35559432,-3.15892104), - std::complex(-0.04081559,-3.76555177), - std::complex(1.06683682,0.86503253), - std::complex(2.49923358,0.08822737), - std::complex(-1.59723642,0.07008247), - std::complex(0.03283785,-1.88828568), - std::complex(-0.64616954,-1.02543979), - std::complex(1.90149199,1.03828523), - std::complex(4.72705428,3.58443117), - std::complex(4.27447257,1.85030986), - std::complex(-0.74625074,-1.07986528), - }; - FourierTransformTester,1>::testMultiplex(ndarray::makeVector(3,5),xData2,kData2); -} - -BOOST_AUTO_TEST_CASE(real_2d) { - double xData1[] = { - 0.24691759, 1.23546604, 0.11002248, 0.57353155, -0.25387112, - -0.01661071, 0.77192489, 0.90072587, -0.39021137, -1.32054932, - -1.57642169, 1.14263038 - }; - std::complex kData1[] = { - std::complex(1.42355458,0.00000000), - std::complex(0.29730942,2.71858178), - std::complex(-3.60683302,0.00000000), - std::complex(2.53712918,-3.07155043), - std::complex(-1.28205146,-0.43653896), - std::complex(-0.37466977,-1.23205340), - std::complex(2.53712918,3.07155043), - std::complex(1.39542736,-4.26784629), - std::complex(-0.37466977,1.23205340), - }; - FourierTransformTester::testSingle(ndarray::makeVector(3,4),xData1,kData1); - double xData2[] = { - 0.61726814, 1.02478783, 1.05838231, -0.54398019, -0.22777548, - -0.52546761, -0.82971628, 1.95616799, 1.9787389 , -0.5170171 , - -1.0711432 , -1.35091919, 0.93562064, -0.90247789, 1.39481772 - }; - std::complex kData2[] = { - std::complex(2.99728660,0.00000000), - std::complex(-4.76203006,-0.29149833), - std::complex(0.81503011,4.31207205), - std::complex(1.39438062,-2.64727323), - std::complex(1.99531720,-0.42062879), - std::complex(-2.98139538,-3.46555052), - std::complex(1.39438062,2.64727323), - std::complex(4.10890818,-5.68718353), - std::complex(2.56065709,1.51657561), - }; - FourierTransformTester::testSingle(ndarray::makeVector(3,5),xData2,kData2); - double xData3[] = { - -1.40706583, -0.10390223, 1.06058569, -0.31086132, 1.05681052, - 0.51060026, 0.94256284, -2.12855291, -0.44127652, -1.68077709, - 0.47362511, -0.06821889, -0.22098813, -0.21520295, -0.00649069, - -1.15530166, -0.33289955, 0.49732109, 0.29713591, 0.95975404, - -0.19866032, 3.40890222, -0.92217484, -0.2164837 , 1.05600813, - -1.0990793 , 0.14508848, 0.20166354, -0.50525583, 0.40797194, - 1.16566663, -0.33373821, -0.39235002, 0.92020752, -0.35070829, - 0.91553651 - }; - std::complex kData3[] = { - std::complex(-2.09647038,0.00000000), - std::complex(-3.26830548,-1.23355405), - std::complex(5.46695397,0.00000000), - std::complex(-0.09363035,-1.81698026), - std::complex(-5.74941461,-0.58493104), - std::complex(-2.63105188,-1.59000658), - std::complex(-0.09363035,1.81698026), - std::complex(1.61476551,1.19760785), - std::complex(-2.63105188,1.59000658), - std::complex(1.89491143,0.00000000), - std::complex(-0.12101837,-4.10305168), - std::complex(-4.66306664,0.00000000), - std::complex(-3.34443085,0.56315196), - std::complex(3.27891803,1.81358643), - std::complex(4.04607201,-2.44255096), - std::complex(-3.34443085,-0.56315196), - std::complex(-3.80139196,-0.53083089), - std::complex(4.04607201,2.44255096), - std::complex(2.13101109,0.00000000), - std::complex(-0.80164455,0.55436168), - std::complex(0.10588709,0.00000000), - std::complex(-0.60998428,0.31007277), - std::complex(1.12890713,3.08493192), - std::complex(3.09482501,-2.74095257), - std::complex(-0.60998428,-0.31007277), - std::complex(2.40549637,0.26293493), - std::complex(3.09482501,2.74095257), - }; - FourierTransformTester::testMultiplex(ndarray::makeVector(3,3,4),xData3,kData3); - double xData4[] = { - -2.02786312, -0.3539932 , 0.55001132, 0.88336882, 0.43662574, - 2.01541172, -0.84654528, 0.01492169, 0.25803824, -0.94068415, - -0.14705738, -1.14940977, -0.39805096, 0.83987327, -1.5626161 , - -1.42810026, 0.40392084, 0.49272843, -0.63469147, -0.44005778, - -1.15325931, -0.27243829, -0.14669651, 0.43615407, -1.18495794, - -0.48243325, -1.07236569, 0.96292895, -2.46877351, -1.34336383, - -0.66991209, -1.19233445, -0.62648047, -0.52768348, 0.23126765, - 1.76633904, -0.65569182, 0.29814291, -0.09862708, 0.98570324, - 0.0580575 , -0.46466068, 0.35672026, 0.31357366, 0.22521739 - }; - std::complex kData4[] = { - std::complex(-2.42796916,0.00000000), - std::complex(-3.26222014,1.33588590), - std::complex(4.07743277,-1.55909118), - std::complex(0.44620892,-2.52741129), - std::complex(-3.35542310,-1.48469802), - std::complex(-3.53459988,-0.17856927), - std::complex(0.44620892,2.52741129), - std::complex(-2.86822821,2.99240988), - std::complex(-5.49815917,2.18067744), - std::complex(-8.33140554,0.00000000), - std::complex(-3.17289317,-4.26545758), - std::complex(-0.32088611,2.58992671), - std::complex(1.75640242,-1.80376581), - std::complex(1.11499523,1.51702830), - std::complex(-5.63675876,0.34121561), - std::complex(1.75640242,1.80376581), - std::complex(-1.91535212,-1.64762718), - std::complex(1.62944330,-1.20265674), - std::complex(-0.00036843,0.00000000), - std::complex(1.11553297,3.37059349), - std::complex(1.77086237,2.53148430), - std::complex(-4.17753005,-1.56487168), - std::complex(-0.00380698,-1.52895218), - std::complex(-0.48338932,-1.10597651), - std::complex(-4.17753005,1.56487168), - std::complex(-1.21120542,2.39435110), - std::complex(-2.03462006,0.80292460), - }; - FourierTransformTester::testMultiplex(ndarray::makeVector(3,3,5),xData4,kData4); -} - -BOOST_AUTO_TEST_CASE(complex_2d) { - std::complex xData1[] = { - std::complex(-0.92330789,-2.17834411), - std::complex(0.66032244,-0.73829353), - std::complex(-0.68029780,0.13129172), - std::complex(0.23413066,0.07680841), - std::complex(-0.44237642,0.55046543), - std::complex(1.74734692,0.15625388), - std::complex(1.00639051,-0.27572242), - std::complex(-1.84657822,-1.11254821), - std::complex(-0.68120146,-1.25764975), - std::complex(1.27376530,0.17496170), - std::complex(0.50483156,0.90637023), - std::complex(0.17423552,1.38104926), - }; - std::complex kData1[] = { - std::complex(1.02726111,-2.18535738), - std::complex(-3.63019744,-8.76711466), - std::complex(-3.45918412,-2.06182040), - std::complex(-2.12542261,1.47217875), - std::complex(-3.21092823,-2.27137660), - std::complex(0.65731688,-1.63596716), - std::complex(0.70037550,-3.02857437), - std::complex(6.67065478,-1.19040390), - std::complex(0.05620935,-3.66887857), - std::complex(-0.20145550,2.19559898), - std::complex(-4.73536776,0.93369296), - std::complex(-2.82895659,-5.93210699), - }; - FourierTransformTester,2>::testSingle(ndarray::makeVector(3,4),xData1,kData1); - std::complex xData2[] = { - std::complex(0.19529047,1.20435172), - std::complex(1.45906066,-0.97965946), - std::complex(-1.83174935,-0.36226429), - std::complex(0.11703370,-0.87754715), - std::complex(-0.52421348,0.93475249), - std::complex(-1.20346126,0.29011118), - std::complex(-1.48942112,-1.54360504), - std::complex(-0.42518103,-1.43737264), - std::complex(1.06970149,-0.99337823), - std::complex(-0.03119172,-0.05324573), - std::complex(-0.26718251,0.02763509), - std::complex(0.04403172,0.33843024), - std::complex(-0.27862895,0.11800338), - std::complex(-0.45012170,0.59134803), - std::complex(1.79455284,1.00858644), - std::complex(0.90559524,-0.70387881), - std::complex(-1.13560698,-0.07159557), - std::complex(0.17003962,1.91449110), - std::complex(-1.09018239,-0.14139606), - std::complex(-0.63102161,0.95133602), - std::complex(3.40992650,-0.77678722), - std::complex(-0.70506997,2.22309013), - std::complex(0.79939137,-0.43430236), - std::complex(1.25702706,-0.40232699), - std::complex(-0.05621207,-0.82483426), - std::complex(-0.45139484,-2.49048884), - std::complex(-0.58693292,0.05053400), - std::complex(-0.53649309,1.79843410), - std::complex(-0.29692612,0.16872552), - std::complex(1.07050210,-0.58501773), - std::complex(-0.17909989,-1.51092040), - std::complex(-0.77482019,-0.71248210), - std::complex(0.33169239,-0.33502900), - std::complex(-1.48887694,-2.10226979), - std::complex(0.17078066,0.72966237), - std::complex(0.83173292,-1.19673034), - }; - std::complex kData2[] = { - std::complex(-2.88728243,-3.45179183), - std::complex(5.56282699,2.53543695), - std::complex(-2.80786657,1.98677531), - std::complex(3.09543592,3.51248350), - std::complex(0.42163615,4.06364305), - std::complex(3.74538684,-2.44419993), - std::complex(-1.86528567,4.07284857), - std::complex(4.06768924,4.76385009), - std::complex(2.28455274,-3.65720875), - std::complex(-3.53343134,0.58253015), - std::complex(-4.96450748,2.03825824), - std::complex(-0.77566878,0.44959533), - std::complex(4.04590102,4.27656807), - std::complex(5.37572779,1.35348531), - std::complex(2.95300374,-4.87155087), - std::complex(-4.39187028,-3.68002016), - std::complex(2.70357575,5.83300183), - std::complex(-5.89071199,3.76064790), - std::complex(0.07349988,8.99019172), - std::complex(-0.10666298,-0.66887248), - std::complex(-0.83528451,-7.06739279), - std::complex(-1.81888066,-3.71873158), - std::complex(0.15484744,-0.40127904), - std::complex(-5.60669264,-2.39000736), - std::complex(-1.96604800,-7.01041645), - std::complex(-4.49319166,0.12977561), - std::complex(0.73265208,3.56669295), - std::complex(5.64080434,-0.65060303), - std::complex(-1.23431266,1.32790932), - std::complex(-4.62184457,-2.15880098), - std::complex(-3.08669912,-0.23413824), - std::complex(10.39353240,0.27589821), - std::complex(-1.69273809,1.28344216), - std::complex(-2.15957004,-0.85237418), - std::complex(3.38827586,-3.57929124), - std::complex(-1.57540539,-1.99610520), - }; - FourierTransformTester,2>::testMultiplex(ndarray::makeVector(3,3,4),xData2,kData2); -}; - -template -struct FourierOpsTester { - typedef ndarray::FourierTransform FFT; - - static void makeGaussian(T const * mu, T sigma, typename FFT::ArrayX const & array, int m=0) { - typename FFT::ArrayX::Iterator const iter_end = array.end(); - typename FFT::ArrayX::Iterator iter = array.begin(); - for (int x=0; iter != iter_end; ++iter, ++x) { - T z = (x-(*mu))/sigma; - (*iter) *= std::exp(-0.5*z*z); - if (m == N) (*iter) *= -z / sigma; - FourierOpsTester::makeGaussian(mu+1,sigma,*iter,m); - } - } - - static void testShift( - typename FFT::Index const & shape, - ndarray::Vector const & offset, - T sigma - ) { - typename FFT::ArrayX x1; - typename FFT::ArrayX x2; - typename FFT::ArrayK k; - typename FFT::Ptr forward = FFT::planForward(shape,x1,k); - typename FFT::Ptr inverse = FFT::planInverse(shape,k,x2); - x1.deep() = 1.0; - ndarray::Vector mu = (shape - offset) * 0.5; - makeGaussian(mu.begin(), sigma, x1); - forward->execute(); - ndarray::shift(offset, k, shape[N-1]); - inverse->execute(); - x2.deep() /= shape.product(); - x1.deep() = 1.0; - mu += offset; - makeGaussian(mu.begin(), sigma, x1); - BOOST_CHECK(compareAbsolute(x1, x2)); - } - - static void testDifferentiate( - typename FFT::Index const & shape, - T sigma, - int n - ) { - typename FFT::ArrayX x1; - typename FFT::ArrayX x2; - typename FFT::ArrayK k; - typename FFT::Ptr forward = FFT::planForward(shape,x1,k); - typename FFT::Ptr inverse = FFT::planInverse(shape,k,x2); - x1.deep() = 1.0; - ndarray::Vector mu = shape * 0.5; - makeGaussian(mu.begin(),sigma,x1,0); - forward->execute(); - ndarray::differentiate(n, k, shape[N-1]); - inverse->execute(); - x2.deep() /= shape.product(); - x1.deep() = 1.0; - makeGaussian(mu.begin(), sigma, x1, N-n); - BOOST_CHECK(compareAbsolute(x1,x2)); - } - -}; - -template -struct FourierOpsTester { - - static void makeGaussian(T const * mu, T sigma, T value, int m=0) {} - -}; - -BOOST_AUTO_TEST_CASE(ops) { - FourierOpsTester::testShift(ndarray::makeVector(256),ndarray::makeVector(7.25),10.0); - FourierOpsTester::testShift(ndarray::makeVector(255),ndarray::makeVector(7.25),10.0); - FourierOpsTester::testShift(ndarray::makeVector(256,256),ndarray::makeVector(7.25,6.4),10.0); - FourierOpsTester::testShift(ndarray::makeVector(256,255),ndarray::makeVector(7.25,6.4),10.0); - - FourierOpsTester::testDifferentiate(ndarray::makeVector(256),10.0,0); - FourierOpsTester::testDifferentiate(ndarray::makeVector(256),10.0,1); - FourierOpsTester::testDifferentiate(ndarray::makeVector(255),10.0,0); - FourierOpsTester::testDifferentiate(ndarray::makeVector(255),10.0,1); - FourierOpsTester::testDifferentiate(ndarray::makeVector(256,256),10.0,0); - FourierOpsTester::testDifferentiate(ndarray::makeVector(256,256),10.0,1); - FourierOpsTester::testDifferentiate(ndarray::makeVector(256,255),10.0,0); - FourierOpsTester::testDifferentiate(ndarray::makeVector(256,255),10.0,1); -} - -#else - -BOOST_AUTO_TEST_CASE(placeholder) { - - std::cerr << "WARNING: ndarray-fft test code disabled on gcc 4.5.\n"; - -} - -#endif diff --git a/tests/ndarray.cc b/tests/ndarray.cc deleted file mode 100644 index 7729e4ad..00000000 --- a/tests/ndarray.cc +++ /dev/null @@ -1,630 +0,0 @@ -// -*- c++ -*- -/* - * Copyright (c) 2010-2012, Jim Bosch - * All rights reserved. - * - * ndarray is distributed under a simple BSD-like license; - * see the LICENSE file that should be present in the root - * of the source distribution, or alternately available at: - * https://github.com/ndarray/ndarray - */ -#include "ndarray.h" - -#define BOOST_TEST_DYN_LINK -#define BOOST_TEST_MODULE ndarray -#include "boost/test/unit_test.hpp" - -BOOST_AUTO_TEST_CASE(sizes) { - std::cerr << "sizeof(int): " << sizeof(int) << "\n"; - std::cerr << "sizeof(double*): " << sizeof(double*) << "\n"; - std::cerr << "sizeof(shared_ptr): " << sizeof(boost::shared_ptr) << "\n"; - std::cerr << "sizeof(Core): " << sizeof(ndarray::detail::Core<1>) << "\n"; - std::cerr << "sizeof(Core): " << sizeof(ndarray::detail::Core<2>) << "\n"; - std::cerr << "sizeof(Core): " << sizeof(ndarray::detail::Core<3>) << "\n"; -} - -BOOST_AUTO_TEST_CASE(vectors) { - ndarray::Vector a = ndarray::makeVector(5,6,7); - BOOST_CHECK_EQUAL(a[0],5); - BOOST_CHECK_EQUAL(a[1],6); - BOOST_CHECK_EQUAL(a[2],7); - ndarray::Vector b(a); - BOOST_CHECK_EQUAL(a,b); - ndarray::Vector c(a); - BOOST_CHECK_EQUAL(c[0],5.0); - BOOST_CHECK_EQUAL(c[1],6.0); - BOOST_CHECK_EQUAL(c[2],7.0); - ndarray::Vector d(5.0); - BOOST_CHECK_EQUAL(d[0],5.0); - BOOST_CHECK_EQUAL(d[1],5.0); - BOOST_CHECK_EQUAL(d[2],5.0); - - ndarray::Vector e; - ndarray::Vector f = ndarray::concatenate(a, e); - BOOST_CHECK_EQUAL(a, f); - - e = a.last<0>(); - - // make sure we can default-construct whether or not T is a number - ndarray::Vector,2> g; - BOOST_CHECK_EQUAL(g, ndarray::makeVector(boost::shared_ptr(), boost::shared_ptr())); - ndarray::Vector h; - BOOST_CHECK_EQUAL(h, ndarray::makeVector(0, 0, 0)); -} - -BOOST_AUTO_TEST_CASE(cores) { - typedef ndarray::detail::Core<3> Core; - ndarray::Vector shape = ndarray::makeVector(4,3,2); - ndarray::Vector strides = ndarray::makeVector(6,2,1); - Core::Ptr core = Core::create(shape, strides); - BOOST_CHECK_EQUAL(core->getRC(),1); - Core::Ptr copy = core; - BOOST_CHECK_EQUAL(core->getRC(),2); - copy.reset(); - BOOST_CHECK_EQUAL(core->getRC(),1); -} - -BOOST_AUTO_TEST_CASE(allocation) { - ndarray::Vector shape = ndarray::makeVector(5,6,7); - ndarray::Array a = ndarray::allocate(shape); - BOOST_CHECK_EQUAL(a.getShape(), shape); - - ndarray::Array b = ndarray::allocate(shape); - BOOST_CHECK_EQUAL(b.getShape(), shape); - BOOST_CHECK_EQUAL(b.getSize<0>(), shape[0]); - BOOST_CHECK_EQUAL(b.getSize<1>(), shape[1]); - BOOST_CHECK_EQUAL(b.getSize<2>(), shape[2]); - BOOST_CHECK_EQUAL(b.getStride<0>(), 6*7); - BOOST_CHECK_EQUAL(b.getStride<1>(), 7); - BOOST_CHECK_EQUAL(b.getStride<2>(), 1); - BOOST_CHECK_EQUAL(b.getStrides(), ndarray::makeVector(6*7 ,7, 1)); - - ndarray::Array c = ndarray::allocate(5,6,7); - BOOST_CHECK_EQUAL(c.getShape(), shape); - BOOST_CHECK_EQUAL(c.getSize<0>(), shape[0]); - BOOST_CHECK_EQUAL(c.getSize<1>(), shape[1]); - BOOST_CHECK_EQUAL(c.getSize<2>(), shape[2]); - BOOST_CHECK_EQUAL(c.getStride<0>(), 1); - BOOST_CHECK_EQUAL(c.getStride<1>(), 5); - BOOST_CHECK_EQUAL(c.getStride<2>(), 5*6); - BOOST_CHECK_EQUAL(c.getStrides(), ndarray::makeVector(1, 5, 5*6)); - - ndarray::Array d(shape); - BOOST_CHECK_EQUAL(d.getShape(), shape); - BOOST_CHECK_EQUAL(d.getStrides(), a.getStrides()); - ndarray::Array e(shape); - BOOST_CHECK_EQUAL(e.getShape(), shape); - BOOST_CHECK_EQUAL(e.getStrides(), c.getStrides()); - ndarray::Vector shape2(shape); - ndarray::Array f(shape2); - BOOST_CHECK_EQUAL(f.getShape(), shape); - BOOST_CHECK_EQUAL(f.getStrides(), a.getStrides()); - ndarray::Array g(shape2); - BOOST_CHECK_EQUAL(g.getShape(), shape); - BOOST_CHECK_EQUAL(g.getStrides(), c.getStrides()); -} - -BOOST_AUTO_TEST_CASE(external) { - double data[3*4*2] = {0}; - ndarray::Vector shape = ndarray::makeVector(3,4,2); - ndarray::Vector strides = ndarray::makeVector(8,2,1); - ndarray::Array a = ndarray::external(data,shape,strides); - BOOST_CHECK_EQUAL(a.getData(), data); - BOOST_CHECK_EQUAL(a.getShape(), shape); - BOOST_CHECK_EQUAL(a.getStrides(), strides); -} - -BOOST_AUTO_TEST_CASE(conversion) { - double data[3*4*2] = {0}; - ndarray::Vector shape = ndarray::makeVector(3,4,2); - ndarray::Vector strides = ndarray::makeVector(8,2,1); - ndarray::Array a = ndarray::external(data,shape,strides); - ndarray::Array b = a; -} - -BOOST_AUTO_TEST_CASE(shallow) { - double data[3*4*2] = {0}; - ndarray::Vector shape = ndarray::makeVector(3,4,2); - ndarray::Vector strides = ndarray::makeVector(8,2,1); - ndarray::Array a = ndarray::external(data,shape,strides); - ndarray::Array b = ndarray::external(data,shape,strides); - BOOST_CHECK(a == b); - BOOST_CHECK(a[2].shallow() == b[2].shallow()); - BOOST_CHECK(a[0][1].shallow() == b[0][1].shallow()); - BOOST_CHECK(a[0][1].shallow() != b[1][2].shallow()); - ndarray::Array c; - c = a; - BOOST_CHECK_EQUAL(a.getData(), c.getData()); - BOOST_CHECK_EQUAL(a.getShape(), c.getShape()); - BOOST_CHECK_EQUAL(a.getStrides(), c.getStrides()); - BOOST_CHECK(a.shallow() == c.shallow()); - ndarray::Array d = c[1]; - BOOST_CHECK_EQUAL(d.getData(), c[1].getData()); - BOOST_CHECK_EQUAL(d.getShape(), c[1].getShape()); - BOOST_CHECK_EQUAL(d.getStrides(), c[1].getStrides()); - BOOST_CHECK(d.shallow() == c[1].shallow()); -} - -BOOST_AUTO_TEST_CASE(casts) { - double data[3*4*2] = {0}; - ndarray::Vector shape = ndarray::makeVector(3,4,2); - ndarray::Vector strides = ndarray::makeVector(8,2,1); - ndarray::Array a = ndarray::external(data,shape,strides); - ndarray::Array b = ndarray::static_dimension_cast<2>(a); - BOOST_CHECK(a == b); - ndarray::Array c = ndarray::const_array_cast(a); - BOOST_CHECK(a == c); - ndarray::Array d = ndarray::dynamic_dimension_cast<3>(a); - BOOST_CHECK(a == d); - ndarray::Array e = d[ndarray::view()(0,4,2)()]; - ndarray::Array f = ndarray::dynamic_dimension_cast<3>(e); - BOOST_CHECK(f.empty()); -} - -BOOST_AUTO_TEST_CASE(complex) { - std::complex data[3*4*2] = { std::complex(0.0,0.0) }; - ndarray::Vector shape = ndarray::makeVector(3,4,2); - ndarray::Vector strides = ndarray::makeVector(8,2,1); - ndarray::Array,3,3> a = ndarray::external(data,shape,strides); - ndarray::Array re(getReal(a)); - ndarray::Array im(getImag(a)); - a[1][2][0] = std::complex(4.5,1.2); - BOOST_CHECK(re[1][2][0] == 4.5); - BOOST_CHECK(im[1][2][0] == 1.2); -} - -BOOST_AUTO_TEST_CASE(indexing) { - double data[3*4*2] = { - 0, 1, 2, 3, 4, 5, 6, 7, - 8, 9,10,11,12,13,14,15, - 16,17,18,19,20,21,22,23, - }; - ndarray::Vector a_shape = ndarray::makeVector(4,3,2); - ndarray::Vector a_strides = ndarray::makeVector(6,2,1); - ndarray::Array a = ndarray::external(data, a_shape, a_strides); - BOOST_CHECK(a.front().shallow() == a[0].shallow()); - BOOST_CHECK(a.back().shallow() == a[a_shape[0]-1].shallow()); - ndarray::Size n = 0; - for (ndarray::Size i=0; i b_shape = ndarray::makeVector(8,3); - ndarray::Vector b_strides = ndarray::makeVector(1,8); - ndarray::Array b = ndarray::external(data, b_shape, b_strides); - for (ndarray::Size i=0; i c_shape = ndarray::makeVector(4,3); - ndarray::Vector c_strides = ndarray::makeVector(1,8); - ndarray::Array c = ndarray::external(data, c_shape, c_strides); - for (ndarray::Size i=0; i a_shape = ndarray::makeVector(4,3,2); - ndarray::Vector a_strides = ndarray::makeVector(6,2,1); - ndarray::Array a = ndarray::external(data, a_shape, a_strides); - ndarray::Array::Iterator ai_iter = a.begin(); - ndarray::Array::Iterator const ai_end = a.end(); - for (int i=0; ai_iter != ai_end; ++i, ++ai_iter) { - ndarray::Array::Reference::Iterator aj_iter = ai_iter->begin(); - ndarray::Array::Reference::Iterator const aj_end = ai_iter->end(); - for (int j=0; aj_iter != aj_end; ++j, ++aj_iter) { - ndarray::Array::Reference::Reference::Iterator ak_iter = aj_iter->begin(); - ndarray::Array::Reference::Reference::Iterator const ak_end = aj_iter->end(); - for (int k=0; ak_iter != ak_end; ++k, ++ak_iter) { - BOOST_CHECK_EQUAL(a[i][j][k], *ak_iter); - } - } - } - ndarray::Vector b_shape = ndarray::makeVector(4,3); - ndarray::Vector b_strides = ndarray::makeVector(1,8); - ndarray::Array b = ndarray::external(data, b_shape, b_strides); - ndarray::Array::Iterator bi_iter = b.begin(); - ndarray::Array::Iterator const bi_end = b.end(); - for (int i=0; bi_iter != bi_end; ++i, ++bi_iter) { - ndarray::Array::Reference::Iterator bj_iter = bi_iter->begin(); - ndarray::Array::Reference::Iterator const bj_end = bi_iter->end(); - for (int j=0; bj_iter != bj_end; ++j, ++bj_iter) { - BOOST_CHECK_EQUAL(b[i][j], *bj_iter); - } - } - -} - -BOOST_AUTO_TEST_CASE(views) { - ndarray::Vector shape = ndarray::makeVector(4,3,2); - ndarray::Array a = ndarray::allocate(shape); - BOOST_CHECK(a == a[ndarray::view()()].shallow()); - BOOST_CHECK(a == a[ndarray::view()].shallow()); - BOOST_CHECK(a[1].shallow() == a[ndarray::view(1)].shallow()); - BOOST_CHECK(a[1][2].shallow() == a[ndarray::view(1)(2)].shallow()); - BOOST_CHECK(a != a[ndarray::view(0,3)].shallow()); - ndarray::Array b = a[ndarray::view()(1,3)(0)]; - BOOST_CHECK(b.getShape() == ndarray::makeVector(4,2)); - BOOST_CHECK(b.getStrides() == ndarray::makeVector(6,2)); - BOOST_CHECK(b.getData() == a.getData() + 2); - ndarray::Array c = b[ndarray::view(0,4,2)()]; - BOOST_CHECK(c.getShape() == ndarray::makeVector(2,2)); - BOOST_CHECK(c.getStrides() == ndarray::makeVector(12,2)); - BOOST_CHECK(c.getData() == b.getData()); -} - -#ifndef GCC_45 - -BOOST_AUTO_TEST_CASE(predicates) { - double data1[3*4*2] = { - 0, 1, 2, 3, 4, 5, 6, 7, - 8, 9,10,11,12,13,14,15, - 16,17,18,19,20,21,22,23, - }; - double data2[3*4*2] = { - 0, 1, 2, 3, 4, 5, 6, 7, - 8, 9,10,11,12,13,14,15, - 16,17,18,19,20,21,22,23, - }; - ndarray::Vector shape = ndarray::makeVector(4,3,2); - ndarray::Vector strides = ndarray::makeVector(6,2,1); - ndarray::Array a = ndarray::external(data1, shape, strides); - ndarray::Array b = ndarray::allocate(shape); - ndarray::Array c = ndarray::allocate(shape); - ndarray::Array d = ndarray::external(data2,shape,strides); - b.deep() = equal(a, 3.0); - c.deep() = logical_not(b); - BOOST_CHECK(a != d); - BOOST_CHECK(all(equal(a, d))); - BOOST_CHECK(ndarray::any(equal(a, d))); - d[3][1][0] = 5.0; - BOOST_CHECK(!ndarray::all(equal(a, d))); - BOOST_CHECK(ndarray::any(equal(a, d))); - BOOST_CHECK(ndarray::any(not_equal(a, d))); - d.deep() = -5.0; - BOOST_CHECK(!ndarray::all(equal(a, d))); - BOOST_CHECK(ndarray::all(not_equal(a, d))); - BOOST_CHECK(ndarray::all(greater(a, d))); - BOOST_CHECK(!ndarray::any(equal(a, d))); - for (int i=0; i shape = ndarray::makeVector(4,3,2); - ndarray::Vector strides = ndarray::makeVector(6,2,1); - ndarray::Array a = ndarray::external(data,shape,strides); - ndarray::Array b = ndarray::allocate(ndarray::concatenate(shape, 3)); - ndarray::Array c = ndarray::allocate(shape); - c.deep() = a + 1.2; - b.deep() = a + 1.2; - BOOST_CHECK(ndarray::allclose(c, b)); - b.deep() += 1E-9; - BOOST_CHECK(ndarray::allclose(c, b, 1E-8)); - BOOST_CHECK(!ndarray::allclose(c, b, 1E-10)); -} - -BOOST_AUTO_TEST_CASE(binary_ops) { - float data[3*4*2] = { - 0, 1, 2, 3, 4, 5, 6, 7, - 8, 9,10,11,12,13,14,15, - 16,17,18,19,20,21,22,23, - }; - ndarray::Vector shape = ndarray::makeVector(4,3,2); - ndarray::Vector strides = ndarray::makeVector(6,2,1); - ndarray::Array a = ndarray::external(data,shape,strides); - ndarray::Array b = ndarray::allocate(shape); - ndarray::Array c = ndarray::allocate(shape); - c.deep() = 0.0; - double q = 1.2; - b.deep() = a + q; - c.deep() -= (a * b - q); - for (int i=0; i shape3 = ndarray::makeVector(3,4,2); - ndarray::Vector strides3 = ndarray::makeVector(8,2,1); - ndarray::Vector shape2 = ndarray::makeVector(3,4); - ndarray::Vector strides2 = ndarray::makeVector(4,1); - ndarray::Array a3 = ndarray::external(data3,shape3,strides3); - ndarray::Array a32 = ndarray::external(data32,shape3,strides3); - ndarray::Array a2 = ndarray::external(data2,shape2,strides2); - ndarray::Array b3 = ndarray::copy(a3); - ndarray::Array b32 = ndarray::copy(a32); - ndarray::Array b2 = ndarray::copy(a2); - ndarray::Array c3 = ndarray::allocate(shape3); - ndarray::Array c32 = ndarray::allocate(shape3); - c3.deep() = 0.0; - c3.deep() -= 2.0 * b2; - c32.deep() = 0.0; - c32.deep() -= 2.0 * b32; - BOOST_CHECK(all(equal(c3, c32))); - c3.deep() += b2; - c32.deep() += b32; - BOOST_CHECK(all(equal(c3, c32))); -} - -#endif - -BOOST_AUTO_TEST_CASE(assignment) { - double data[3*4*2] = { - 0, 1, 2, 3, 4, 5, 6, 7, - 8, 9,10,11,12,13,14,15, - 16,17,18,19,20,21,22,23, - }; - ndarray::Vector shape = ndarray::makeVector(3,4,2); - ndarray::Vector strides = ndarray::makeVector(8,2,1); - ndarray::Array a = ndarray::external(data,shape,strides); - ndarray::Array b = ndarray::allocate(shape); - b.deep() = a; - ndarray::Array c = ndarray::allocate(shape); - ndarray::Array d = c; - c.deep() = a; - BOOST_CHECK(a.shallow() != b.shallow()); - BOOST_CHECK(a.shallow() != c.shallow()); - int n = 0; - for (int i=0; i shape = ndarray::makeVector(3,4,2); - ndarray::Vector strides = ndarray::makeVector(8,2,1); - ndarray::Array a = ndarray::external(data,shape,strides); - ndarray::Array b = a.transpose(); - ndarray::Array c = a.transpose(ndarray::makeVector(1,0,2)); - for (int i=0; i a1 = a[ndarray::view(0)()()]; - ndarray::Array b1 = b[ndarray::view()()(0)]; - BOOST_CHECK(b1.transpose().shallow() == a1.shallow()); - BOOST_CHECK(a1.transpose().shallow() == b1.shallow()); - } - { - ndarray::Array a1 = a[ndarray::view(1,3)()()]; - ndarray::Array b1 = b[ndarray::view()()(1,3)]; - BOOST_CHECK(b1.transpose().shallow() == a1.shallow()); - BOOST_CHECK(a1.transpose().shallow() == b1.shallow()); - } - { - ndarray::Array a1 = a[ndarray::view(0,3,2)()()]; - ndarray::Array b1 = b[ndarray::view()()(0,3,2)]; - BOOST_CHECK(b1.transpose().shallow() == a1.shallow()); - BOOST_CHECK(a1.transpose().shallow() == b1.shallow()); - } - { - ndarray::Array a1 = a[ndarray::view()(1,3)()]; - ndarray::Array b1 = b[ndarray::view()(1,3)()]; - BOOST_CHECK(b1.transpose().shallow() == a1.shallow()); - BOOST_CHECK(a1.transpose().shallow() == b1.shallow()); - } - { - ndarray::Array a1 = a[ndarray::view()(0,4,2)()]; - ndarray::Array b1 = b[ndarray::view()(0,4,2)()]; - BOOST_CHECK(b1.transpose().shallow() == a1.shallow()); - BOOST_CHECK(a1.transpose().shallow() == b1.shallow()); - } - { - ndarray::Array a1 = a[0][0]; - ndarray::Array b1 = a1; - ndarray::Array c1 = b1; - } -} - -BOOST_AUTO_TEST_CASE(flatten) { - double data[3*4*2] = { - 0, 1, 2, 3, 4, 5, 6, 7, - 8, 9,10,11,12,13,14,15, - 16,17,18,19,20,21,22,23, - }; - ndarray::Vector a_shape = ndarray::makeVector(4,3,2); - ndarray::Vector a_strides = ndarray::makeVector(6,2,1); - ndarray::Array a = ndarray::external(data,a_shape,a_strides); - ndarray::Array b = ndarray::flatten<2>(a); - ndarray::Array b_check = ndarray::external( - data, ndarray::makeVector(4,6), ndarray::makeVector(6,1) - ); - BOOST_CHECK(b.shallow() == b_check.shallow()); - ndarray::Array c = ndarray::flatten<1>(a); - ndarray::Array c_check = ndarray::external( - data, ndarray::makeVector(24), ndarray::makeVector(1) - ); -} - -BOOST_AUTO_TEST_CASE(unique) { - ndarray::Array a = ndarray::allocate(5,4); - BOOST_CHECK(a.isUnique()); - ndarray::Array b(a); - BOOST_CHECK(!a.isUnique()); - BOOST_CHECK(!b.isUnique()); - a = ndarray::Array(); - BOOST_CHECK(b.isUnique()); - ndarray::Array c = b[ndarray::view(1,4)(1,3)]; - BOOST_CHECK(!c.isUnique()); - BOOST_CHECK(!b.isUnique()); - b = ndarray::Array(); - BOOST_CHECK(c.isUnique()); - ndarray::Array d = ndarray::allocate(6,3); - d.swap(c); - BOOST_CHECK(c.isUnique()); - BOOST_CHECK(d.isUnique()); -} - -BOOST_AUTO_TEST_CASE(ticket1720) { - ndarray::Array a = ndarray::allocate(5,4); - a.deep() = 0; - ndarray::Array b = ndarray::allocate(5,4); - b.deep() = 1; - a[0] = b[1]; - BOOST_CHECK_EQUAL(a[0][0], 1); - ndarray::Array c(b); - a[1] = c[1]; - BOOST_CHECK_EQUAL(a[1][0], 1); -} - -BOOST_AUTO_TEST_CASE(zeroSize) { - ndarray::Array a = ndarray::allocate(0); - BOOST_CHECK(!a.getData()); - BOOST_CHECK_EQUAL(a.getSize<0>(), 0); - ndarray::Array b = ndarray::allocate(0, 5); - BOOST_CHECK(!b.getData()); - BOOST_CHECK_EQUAL(b.getSize<0>(), 0); - BOOST_CHECK_EQUAL(b.getSize<1>(), 5); - ndarray::Array c = ndarray::allocate(5, 0); - BOOST_CHECK(!c.getData()); - BOOST_CHECK_EQUAL(c.getSize<0>(), 5); - BOOST_CHECK_EQUAL(c.getSize<1>(), 0); -} - -BOOST_AUTO_TEST_CASE(manager) { - ndarray::Array a = ndarray::allocate(5); - ndarray::Array b - = ndarray::external(a.getData(), a.getShape(), a.getStrides(), a.getManager()); - BOOST_CHECK_EQUAL(a.getManager(), b.getManager()); // no extra indirection in makeManager -} - -BOOST_AUTO_TEST_CASE(issue3) { - ndarray::Array a1(5); - ndarray::Array r1(5); - BOOST_CHECK_EQUAL(a1.getSize<0>(), 5); - BOOST_CHECK_EQUAL(r1.getSize<0>(), 5); - ndarray::Array a2(5); - ndarray::ArrayRef r2(5); - ndarray::Array b2(5, 4); - ndarray::ArrayRef s2(5, 4); - BOOST_CHECK_EQUAL(a2.getSize<0>(), 5); - BOOST_CHECK_EQUAL(a2.getSize<1>(), 1); - BOOST_CHECK_EQUAL(r2.getSize<0>(), 5); - BOOST_CHECK_EQUAL(r2.getSize<1>(), 1); - BOOST_CHECK_EQUAL(b2.getSize<0>(), 5); - BOOST_CHECK_EQUAL(b2.getSize<1>(), 4); - BOOST_CHECK_EQUAL(s2.getSize<0>(), 5); - BOOST_CHECK_EQUAL(s2.getSize<1>(), 4); - ndarray::Array a5(5); - ndarray::ArrayRef r5(5); - ndarray::Array b5(5, 4, 3, 2); - ndarray::ArrayRef s5(5, 4, 3, 2); - BOOST_CHECK_EQUAL(a5.getSize<0>(), 5); - BOOST_CHECK_EQUAL(a5.getSize<1>(), 1); - BOOST_CHECK_EQUAL(a5.getSize<2>(), 1); - BOOST_CHECK_EQUAL(a5.getSize<3>(), 1); - BOOST_CHECK_EQUAL(a5.getSize<4>(), 1); - BOOST_CHECK_EQUAL(r5.getSize<0>(), 5); - BOOST_CHECK_EQUAL(r5.getSize<1>(), 1); - BOOST_CHECK_EQUAL(r5.getSize<2>(), 1); - BOOST_CHECK_EQUAL(r5.getSize<3>(), 1); - BOOST_CHECK_EQUAL(r5.getSize<4>(), 1); - BOOST_CHECK_EQUAL(b5.getSize<0>(), 5); - BOOST_CHECK_EQUAL(b5.getSize<1>(), 4); - BOOST_CHECK_EQUAL(b5.getSize<2>(), 3); - BOOST_CHECK_EQUAL(b5.getSize<3>(), 2); - BOOST_CHECK_EQUAL(b5.getSize<4>(), 1); - BOOST_CHECK_EQUAL(s5.getSize<0>(), 5); - BOOST_CHECK_EQUAL(s5.getSize<1>(), 4); - BOOST_CHECK_EQUAL(s5.getSize<2>(), 3); - BOOST_CHECK_EQUAL(s5.getSize<3>(), 2); - BOOST_CHECK_EQUAL(s5.getSize<4>(), 1); -} diff --git a/tests/pybind11_test.py b/tests/pybind11_test.py deleted file mode 100644 index d6b2f719..00000000 --- a/tests/pybind11_test.py +++ /dev/null @@ -1,71 +0,0 @@ -# -*- python -*- -# -# Copyright (c) 2010-2012, Jim Bosch -# All rights reserved. -# -# ndarray is distributed under a simple BSD-like license; -# see the LICENSE file that should be present in the root -# of the source distribution, or alternately available at: -# https://github.com/ndarray/ndarray -# -import numpy -import unittest - -import pybind11_test_mod - - -class TestNumpyPybind11(unittest.TestCase): - - def testArray1(self): - a1 = pybind11_test_mod.returnArray1() - a2 = numpy.arange(6, dtype=float) - self.assertTrue((a1 == a2).all()) - self.assertTrue(pybind11_test_mod.acceptArray1(a2)) - a3 = pybind11_test_mod.returnConstArray1() - self.assertTrue((a1 == a3).all()) - self.assertFalse(a3.flags["WRITEABLE"]) - - def testArray3(self): - a1 = pybind11_test_mod.returnArray3() - a2 = numpy.arange(4*3*2, dtype=float).reshape(4, 3, 2) - self.assertTrue((a1 == a2).all()) - self.assertTrue(pybind11_test_mod.acceptArray3(a2)) - a3 = pybind11_test_mod.returnConstArray3() - self.assertTrue((a1 == a3).all()) - self.assertFalse(a3.flags["WRITEABLE"]) - - def testStrideHandling(self): - # in NumPy 1.8+ 1- and 0-sized arrays can have arbitrary strides; we should - # be able to handle those - array = numpy.zeros(1, dtype=float) - # the zero shape array tests are simply checking that pybind11 can handle - # arbitrary strides (non-zero for length 1 array, zero for length 0 array - # for numpy >= 1.23). - pybind11_test_mod.acceptAnyArray10(array) - pybind11_test_mod.acceptAnyArray11(array) - array = numpy.zeros(0, dtype=float) - pybind11_test_mod.acceptAnyArray10(array) - pybind11_test_mod.acceptAnyArray11(array) - # test that we gracefully fail when the strides are no multiples of the itemsize - dtype = numpy.dtype([("f1", numpy.float64), ("f2", numpy.int16)]) - table = numpy.zeros(3, dtype=dtype) - self.assertRaises(TypeError, pybind11_test_mod.acceptAnyArray10, table['f1']) - self.assertRaises(TypeError, pybind11_test_mod.acceptAnyArray11, table['f1']) - - def testNone(self): - array = numpy.zeros(10, dtype=float) - self.assertEqual(pybind11_test_mod.acceptNoneArray(array), 0) - self.assertEqual(pybind11_test_mod.acceptNoneArray(None), 1) - self.assertEqual(pybind11_test_mod.acceptNoneArray(), 1) - - def testNonNativeByteOrder(self): - d1 = numpy.dtype("f8") - nonnative = d2 if d1 == numpy.dtype(float) else d1 - a = numpy.zeros(5, dtype=nonnative) - self.assertRaises(TypeError, pybind11_test_mod.acceptAnyArray10, a) - self.assertRaises(TypeError, pybind11_test_mod.acceptAnyArray11, a) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/pybind11_test_mod.cc b/tests/pybind11_test_mod.cc deleted file mode 100644 index 464eb2cb..00000000 --- a/tests/pybind11_test_mod.cc +++ /dev/null @@ -1,78 +0,0 @@ -#include "pybind11/pybind11.h" - -#include "ndarray/pybind11.h" - -namespace py = pybind11; -using namespace py::literals; - -ndarray::Array returnArray1() { - ndarray::Array r(ndarray::allocate(ndarray::makeVector(6))); - for (int n = 0; n < r.getSize<0>(); ++n) { - r[n] = n; - } - return r; -} - -ndarray::Array returnConstArray1() { - return returnArray1(); -} - -ndarray::Array returnArray3() { - ndarray::Array r(ndarray::allocate(ndarray::makeVector(4,3,2))); - ndarray::Array f = ndarray::flatten<1>(r); - for (int n = 0; n < f.getSize<0>(); ++n) { - f[n] = n; - } - return r; -} - -ndarray::Array returnConstArray3() { - return returnArray3(); -} - -bool acceptArray1(ndarray::Array const & a1) { - ndarray::Array a2 = returnArray1(); -#ifndef GCC_45 - return ndarray::all(ndarray::equal(a1, a2)); -#else - return std::equal(a1.begin(), a1.end(), a2.begin()); -#endif -} - -void acceptAnyArray10(ndarray::Array const & a1) {} - -void acceptAnyArray11(ndarray::Array const & a1) {} - -bool acceptArray3(ndarray::Array const & a1) { - ndarray::Array a2 = returnArray3(); -#ifndef GCC_45 - return ndarray::all(ndarray::equal(a1, a2)); -#else - for (int i = 0; i < a1.getSize<0>(); ++i) { - for (int j = 0; j < a1.getSize<1>(); ++j) { - if (!std::equal(a1[i][j].begin(), a1[i][j].end(), a2[i][j].begin())) return false; - } - } - return true; -#endif -} - -int acceptNoneArray(ndarray::Array const * array = nullptr) { - if (array) { - return 0; - } else { - return 1; - } -} - -PYBIND11_MODULE(pybind11_test_mod, mod) { - mod.def("returnArray1", returnArray1); - mod.def("returnConstArray1", returnConstArray1); - mod.def("returnArray3", returnArray3); - mod.def("returnConstArray3", returnConstArray3); - mod.def("acceptArray1", acceptArray1); - mod.def("acceptAnyArray10", acceptAnyArray10); - mod.def("acceptAnyArray11", acceptAnyArray11); - mod.def("acceptArray3", acceptArray3); - mod.def("acceptNoneArray", acceptNoneArray, "array"_a = nullptr); -} diff --git a/tests/test_Array.py b/tests/test_Array.py new file mode 100644 index 00000000..0dde4350 --- /dev/null +++ b/tests/test_Array.py @@ -0,0 +1,149 @@ +# +# Copyright (c) 2010-2018, Jim Bosch +# All rights reserved. +# +# ndarray is distributed under a simple BSD-like license; +# see the LICENSE file that should be present in the root +# of the source distribution, or alternately available at: +# https://github.com/ndarray/ndarray +# + +import unittest +import itertools +from collections import namedtuple +from .compilation import CompilationTestMixin, SnippetContext + + +def load_tests(loader, tests, pattern): + suite = unittest.TestSuite() + suite.addTests(ArrayTestCase.makeSuite()) + return suite + + +class ParameterTuple(namedtuple("ParameterTuple", ("scalar", "const", "n", "c"))): + + @classmethod + def generate(cls, base=None, **kwds): + """Generate ParameterTuples from cartesian products of possible values. + + Parameters + ---------- + base : `ParameterTuple` + A tuple of parameter values or ranges to use as defaults. + + Keyword arguments with keys matching any of the fields in + ParameterTuple are accepted. THese may be scalar values or sequences + of values to include in the certesian product. "c" may have the + special value `range` (the built-in function), which generates "c" + values as `range(-n, n+1)` for every "n" value generated. + """ + param_range_list = [] + for field in cls._fields: + try: + v = kwds[field] + except KeyError: + assert base is not None + v = getattr(base, field) + if not isinstance(v, (list, tuple)): + v = (v,) + param_range_list.append(v) + for params in itertools.product(*param_range_list): + if params[-1] == range: + n = params[-2] + for c in range(-n, n + 1): + yield cls._make(params[:-1] + (c,)) + else: + yield cls._make(params) + + def __str__(self): + return f"<{self.scalar} {self.const}, {self.n}, {self.c}>" + + +class ArrayTestCase(unittest.TestCase, CompilationTestMixin): + + context = SnippetContext( + """ + #include "ndarray/Array.hpp" + + using namespace ndarray; + + namespace { + + template + bool accept_Array(Array const & a) { + return true; + } + + } // + """ + ) + + def __init__(self, method, parameters=None): + super().__init__(method) + self.parameters = parameters + + def runConversionTest(self, valid, invalid, stderr_regex=None): + # Try compiling expected failures separately, since otherwise they'd hide each other. + for out_params in invalid: + with self.subTest(**out_params._asdict()): + self.assertDoesNotCompile( + ["Array{} a;".format(self.parameters), + "accept_Array{}(a);".format(out_params)], + stderr_regex=stderr_regex, + context=self.context + ) + # Compile expected successes together to save compile time. + lines = [f"Array{self.parameters} a;"] + lines.extend(f"accept_Array{out_params}(a);" for out_params in valid) + self.assertCompiles(lines, context=self.context) + + def testContiguousConversions(self): + """Test that we can convert Arrays only when we do not increase + abs(C), and do not change the sign of C when C > 1. + """ + valid = [] + invalid = [] + with self.subTest(**self.parameters._asdict()): + for out_params in ParameterTuple.generate(self.parameters, c=range): + if out_params.n == 1: + if self.parameters.c == 0 and out_params.c != 0: + invalid.append(out_params) + else: + valid.append(out_params) + else: + if out_params.c > 0 and out_params.c > self.parameters.c: + invalid.append(out_params) + elif out_params.c < 0 and out_params.c < self.parameters.c: + invalid.append(out_params) + else: + valid.append(out_params) + self.runConversionTest(valid, invalid, stderr_regex="invalid contiguousness conversion") + + def testConstConversions(self): + """Test that we can convert Arrays from T to T const, but not the + reverse. + + Unlike contiguousness conversions, this should work even when trying + to match templated signatures, because Array inherits from + Array. + """ + valid = [] + invalid = [] + with self.subTest(**self.parameters._asdict()): + for out_params in ParameterTuple.generate(self.parameters, const=["const", ""]): + if self.parameters.const and not out_params.const: + invalid.append(out_params) + else: + valid.append(out_params) + # Tests with no template signature matching. + self.runConversionTest(valid, invalid) + # Test success with template signature matching. + self.assertCompiles([f"Array{self.parameters} a;", "accept_Array(a);"], context=self.context) + + @classmethod + def makeSuite(cls): + suite = unittest.TestSuite() + for p in ParameterTuple.generate(scalar="float", const=("const", ""), n=(1, 2), c=range): + suite.addTest(ArrayTestCase("testContiguousConversions", parameters=p)) + suite.addTest(ArrayTestCase("testConstConversions", parameters=p)) + return suite diff --git a/tests/views.cc b/tests/views.cc deleted file mode 100644 index 70dfd1c2..00000000 --- a/tests/views.cc +++ /dev/null @@ -1,671 +0,0 @@ -// -*- c++ -*- -/* - * Copyright (c) 2010-2012, Jim Bosch - * All rights reserved. - * - * ndarray is distributed under a simple BSD-like license; - * see the LICENSE file that should be present in the root - * of the source distribution, or alternately available at: - * https://github.com/ndarray/ndarray - */ -#include "ndarray.h" - -#define BOOST_TEST_DYN_LINK -#define BOOST_TEST_MODULE views -#include "boost/test/unit_test.hpp" - -template -int templateC(ndarray::ArrayRef const &) { return C; } - -template -int strideC(ndarray::ArrayRef const & v) { - if (C >= 0) { - int c = 0; - int stride = 1; - for (int n = N-1; n >= 0; --n, ++c) { - if (v.getStrides()[n] != stride) break; - stride *= v.getShape()[n]; - } - return c; - } else { - int c = 0; - int stride = 1; - for (int n = 0; n < N; ++n, --c) { - if (v.getStrides()[n] != stride) break; - stride *= v.getShape()[n]; - } - return c; - } -} - -#define CHECK_VIEW_RMC(V) \ - BOOST_CHECK_EQUAL(templateC(V), strideC(V)) - -BOOST_AUTO_TEST_CASE(view2p2) { - - ndarray::Array array = ndarray::allocate(5,4); - - CHECK_VIEW_RMC(array[ndarray::view(1,3,2)(0,2,2)]); // (slice, slice) - CHECK_VIEW_RMC(array[ndarray::view(1,3,2)(0,2)]); // (slice, range) - CHECK_VIEW_RMC(array[ndarray::view(1,3,2)()]); // (slice, full) - CHECK_VIEW_RMC(array[ndarray::view(1,3,2)(1)]); // (slice, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(1,3)(0,2,2)]); // (range, slice) - CHECK_VIEW_RMC(array[ndarray::view(1,3)(0,2)]); // (range, range) - CHECK_VIEW_RMC(array[ndarray::view(1,3)()]); // (range, full) - CHECK_VIEW_RMC(array[ndarray::view(1,3)(1)]); // (range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view()(0,2,2)]); // (range, slice) - CHECK_VIEW_RMC(array[ndarray::view()(0,2)]); // (range, range) - CHECK_VIEW_RMC(array[ndarray::view()()]); // (range, full) - CHECK_VIEW_RMC(array[ndarray::view()(1)]); // (range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(2)(0,2,2)]); // (scalar, slice) - CHECK_VIEW_RMC(array[ndarray::view(2)(0,2)]); // (scalar, range) - CHECK_VIEW_RMC(array[ndarray::view(2)()]); // (scalar, full) - -} - -BOOST_AUTO_TEST_CASE(view2p1) { - - ndarray::Array parent = ndarray::allocate(10,4); - ndarray::Array array = parent[ndarray::view(0,10,2)()]; - - CHECK_VIEW_RMC(array[ndarray::view(1,3,2)(0,2,2)]); // (slice, slice) - CHECK_VIEW_RMC(array[ndarray::view(1,3,2)(0,2)]); // (slice, range) - CHECK_VIEW_RMC(array[ndarray::view(1,3,2)()]); // (slice, full) - CHECK_VIEW_RMC(array[ndarray::view(1,3,2)(1)]); // (slice, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(1,3)(0,2,2)]); // (range, slice) - CHECK_VIEW_RMC(array[ndarray::view(1,3)(0,2)]); // (range, range) - CHECK_VIEW_RMC(array[ndarray::view(1,3)()]); // (range, full) - CHECK_VIEW_RMC(array[ndarray::view(1,3)(1)]); // (range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view()(0,2,2)]); // (range, slice) - CHECK_VIEW_RMC(array[ndarray::view()(0,2)]); // (range, range) - CHECK_VIEW_RMC(array[ndarray::view()()]); // (range, full) - CHECK_VIEW_RMC(array[ndarray::view()(1)]); // (range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(2)(0,2,2)]); // (scalar, slice) - CHECK_VIEW_RMC(array[ndarray::view(2)(0,2)]); // (scalar, range) - CHECK_VIEW_RMC(array[ndarray::view(2)()]); // (scalar, full) - -} - -BOOST_AUTO_TEST_CASE(view2) { - - ndarray::Array parent = ndarray::allocate(10,8); - ndarray::Array array = parent[ndarray::view(0,10,2)(0,8,2)]; - - CHECK_VIEW_RMC(array[ndarray::view(1,3,2)(0,2,2)]); // (slice, slice) - CHECK_VIEW_RMC(array[ndarray::view(1,3,2)(0,2)]); // (slice, range) - CHECK_VIEW_RMC(array[ndarray::view(1,3,2)()]); // (slice, full) - CHECK_VIEW_RMC(array[ndarray::view(1,3,2)(1)]); // (slice, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(1,3)(0,2,2)]); // (range, slice) - CHECK_VIEW_RMC(array[ndarray::view(1,3)(0,2)]); // (range, range) - CHECK_VIEW_RMC(array[ndarray::view(1,3)()]); // (range, full) - CHECK_VIEW_RMC(array[ndarray::view(1,3)(1)]); // (range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view()(0,2,2)]); // (range, slice) - CHECK_VIEW_RMC(array[ndarray::view()(0,2)]); // (range, range) - CHECK_VIEW_RMC(array[ndarray::view()()]); // (range, full) - CHECK_VIEW_RMC(array[ndarray::view()(1)]); // (range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(2)(0,2,2)]); // (scalar, slice) - CHECK_VIEW_RMC(array[ndarray::view(2)(0,2)]); // (scalar, range) - CHECK_VIEW_RMC(array[ndarray::view(2)()]); // (scalar, full) - -} - -BOOST_AUTO_TEST_CASE(view2m2) { - - ndarray::Array parent = ndarray::allocate(4,5); - ndarray::Array array = parent.transpose(); - - CHECK_VIEW_RMC(array[ndarray::view(1,3,2)(0,2,2)]); // (slice, slice) - CHECK_VIEW_RMC(array[ndarray::view(1,3,2)(0,2)]); // (slice, range) - CHECK_VIEW_RMC(array[ndarray::view(1,3,2)()]); // (slice, full) - CHECK_VIEW_RMC(array[ndarray::view(1,3,2)(1)]); // (slice, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(1,3)(0,2,2)]); // (range, slice) - CHECK_VIEW_RMC(array[ndarray::view(1,3)(0,2)]); // (range, range) - CHECK_VIEW_RMC(array[ndarray::view(1,3)()]); // (range, full) - CHECK_VIEW_RMC(array[ndarray::view(1,3)(1)]); // (range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view()(0,2,2)]); // (range, slice) - CHECK_VIEW_RMC(array[ndarray::view()(0,2)]); // (range, range) - CHECK_VIEW_RMC(array[ndarray::view()()]); // (range, full) - CHECK_VIEW_RMC(array[ndarray::view()(1)]); // (range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(2)(0,2,2)]); // (scalar, slice) - CHECK_VIEW_RMC(array[ndarray::view(2)(0,2)]); // (scalar, range) - CHECK_VIEW_RMC(array[ndarray::view(2)()]); // (scalar, full) - -} - -BOOST_AUTO_TEST_CASE(view2m1) { - - ndarray::Array parent1 = ndarray::allocate(8,5); - ndarray::Array parent2 = parent1.transpose(); - ndarray::Array array = parent2[ndarray::view()(0,8,2)]; - - CHECK_VIEW_RMC(array[ndarray::view(1,3,2)(0,2,2)]); // (slice, slice) - CHECK_VIEW_RMC(array[ndarray::view(1,3,2)(0,2)]); // (slice, range) - CHECK_VIEW_RMC(array[ndarray::view(1,3,2)()]); // (slice, full) - CHECK_VIEW_RMC(array[ndarray::view(1,3,2)(1)]); // (slice, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(1,3)(0,2,2)]); // (range, slice) - CHECK_VIEW_RMC(array[ndarray::view(1,3)(0,2)]); // (range, range) - CHECK_VIEW_RMC(array[ndarray::view(1,3)()]); // (range, full) - CHECK_VIEW_RMC(array[ndarray::view(1,3)(1)]); // (range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view()(0,2,2)]); // (range, slice) - CHECK_VIEW_RMC(array[ndarray::view()(0,2)]); // (range, range) - CHECK_VIEW_RMC(array[ndarray::view()()]); // (range, full) - CHECK_VIEW_RMC(array[ndarray::view()(1)]); // (range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(2)(0,2,2)]); // (scalar, slice) - CHECK_VIEW_RMC(array[ndarray::view(2)(0,2)]); // (scalar, range) - CHECK_VIEW_RMC(array[ndarray::view(2)()]); // (scalar, full) - -} - -BOOST_AUTO_TEST_CASE(view3p3) { - - ndarray::Array array = ndarray::allocate(6,5,4); - - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(1,3,2)(0,2,2)]); // (slice, slice, slice) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(1,3,2)(0,2)]); // (slice, slice, range) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(1,3,2)()]); // (slice, slice, full) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(1,3,2)(1)]); // (slice, slice, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(1,3)(0,2,2)]); // (slice, range, slice) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(1,3)(0,2)]); // (slice, range, range) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(1,3)()]); // (slice, range, full) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(1,3)(1)]); // (slice, range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)()(0,2,2)]); // (slice, range, slice) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)()(0,2)]); // (slice, range, range) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)()()]); // (slice, range, full) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)()(1)]); // (slice, range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(2)(0,2,2)]); // (slice, scalar, slice) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(2)(0,2)]); // (slice, scalar, range) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(2)()]); // (slice, scalar, full) - - CHECK_VIEW_RMC(array[ndarray::view(1,5)(1,3,2)(0,2,2)]); // (range, slice, slice) - CHECK_VIEW_RMC(array[ndarray::view(1,5)(1,3,2)(0,2)]); // (range, slice, range) - CHECK_VIEW_RMC(array[ndarray::view(1,5)(1,3,2)()]); // (range, slice, full) - CHECK_VIEW_RMC(array[ndarray::view(1,5)(1,3,2)(1)]); // (range, slice, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(1,5)(1,3)(0,2,2)]); // (range, range, slice) - CHECK_VIEW_RMC(array[ndarray::view(1,5)(1,3)(0,2)]); // (range, range, range) - CHECK_VIEW_RMC(array[ndarray::view(1,5)(1,3)()]); // (range, range, full) - CHECK_VIEW_RMC(array[ndarray::view(1,5)(1,3)(1)]); // (range, range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(1,5)()(0,2,2)]); // (range, range, slice) - CHECK_VIEW_RMC(array[ndarray::view(1,5)()(0,2)]); // (range, range, range) - CHECK_VIEW_RMC(array[ndarray::view(1,5)()()]); // (range, range, full) - CHECK_VIEW_RMC(array[ndarray::view(1,5)()(1)]); // (range, range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(1,5)(2)(0,2,2)]); // (range, scalar, slice) - CHECK_VIEW_RMC(array[ndarray::view(1,5)(2)(0,2)]); // (range, scalar, range) - CHECK_VIEW_RMC(array[ndarray::view(1,5)(2)()]); // (range, scalar, full) - - CHECK_VIEW_RMC(array[ndarray::view()(1,3,2)(0,2,2)]); // (full, slice, slice) - CHECK_VIEW_RMC(array[ndarray::view()(1,3,2)(0,2)]); // (full, slice, range) - CHECK_VIEW_RMC(array[ndarray::view()(1,3,2)()]); // (full, slice, full) - CHECK_VIEW_RMC(array[ndarray::view()(1,3,2)(1)]); // (full, slice, scalar) - - CHECK_VIEW_RMC(array[ndarray::view()(1,3)(0,2,2)]); // (full, range, slice) - CHECK_VIEW_RMC(array[ndarray::view()(1,3)(0,2)]); // (full, range, range) - CHECK_VIEW_RMC(array[ndarray::view()(1,3)()]); // (full, range, full) - CHECK_VIEW_RMC(array[ndarray::view()(1,3)(1)]); // (full, range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view()()(0,2,2)]); // (full, range, slice) - CHECK_VIEW_RMC(array[ndarray::view()()(0,2)]); // (full, range, range) - CHECK_VIEW_RMC(array[ndarray::view()()()]); // (full, range, full) - CHECK_VIEW_RMC(array[ndarray::view()()(1)]); // (full, range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view()(2)(0,2,2)]); // (full, scalar, slice) - CHECK_VIEW_RMC(array[ndarray::view()(2)(0,2)]); // (full, scalar, range) - CHECK_VIEW_RMC(array[ndarray::view()(2)()]); // (full, scalar, full) - - CHECK_VIEW_RMC(array[ndarray::view(3)(1,3,2)(0,2,2)]); // (scalar, slice, slice) - CHECK_VIEW_RMC(array[ndarray::view(3)(1,3,2)(0,2)]); // (scalar, slice, range) - CHECK_VIEW_RMC(array[ndarray::view(3)(1,3,2)()]); // (scalar, slice, full) - CHECK_VIEW_RMC(array[ndarray::view(3)(1,3,2)(1)]); // (scalar, slice, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(3)(1,3)(0,2,2)]); // (scalar, range, slice) - CHECK_VIEW_RMC(array[ndarray::view(3)(1,3)(0,2)]); // (scalar, range, range) - CHECK_VIEW_RMC(array[ndarray::view(3)(1,3)()]); // (scalar, range, full) - CHECK_VIEW_RMC(array[ndarray::view(3)(1,3)(1)]); // (scalar, range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(3)()(0,2,2)]); // (scalar, range, slice) - CHECK_VIEW_RMC(array[ndarray::view(3)()(0,2)]); // (scalar, range, range) - CHECK_VIEW_RMC(array[ndarray::view(3)()()]); // (scalar, range, full) - CHECK_VIEW_RMC(array[ndarray::view(3)()(1)]); // (scalar, range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(3)(2)(0,2,2)]); // (scalar, scalar, slice) - CHECK_VIEW_RMC(array[ndarray::view(3)(2)(0,2)]); // (scalar, scalar, range) - CHECK_VIEW_RMC(array[ndarray::view(3)(2)()]); // (scalar, scalar, full) - -} - -BOOST_AUTO_TEST_CASE(view3p2) { - - ndarray::Array parent = ndarray::allocate(12,5,4); - ndarray::Array array = parent[ndarray::view(0,12,2)()()]; - - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(1,3,2)(0,2,2)]); // (slice, slice, slice) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(1,3,2)(0,2)]); // (slice, slice, range) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(1,3,2)()]); // (slice, slice, full) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(1,3,2)(1)]); // (slice, slice, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(1,3)(0,2,2)]); // (slice, range, slice) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(1,3)(0,2)]); // (slice, range, range) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(1,3)()]); // (slice, range, full) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(1,3)(1)]); // (slice, range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)()(0,2,2)]); // (slice, range, slice) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)()(0,2)]); // (slice, range, range) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)()()]); // (slice, range, full) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)()(1)]); // (slice, range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(2)(0,2,2)]); // (slice, scalar, slice) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(2)(0,2)]); // (slice, scalar, range) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(2)()]); // (slice, scalar, full) - - CHECK_VIEW_RMC(array[ndarray::view(1,5)(1,3,2)(0,2,2)]); // (range, slice, slice) - CHECK_VIEW_RMC(array[ndarray::view(1,5)(1,3,2)(0,2)]); // (range, slice, range) - CHECK_VIEW_RMC(array[ndarray::view(1,5)(1,3,2)()]); // (range, slice, full) - CHECK_VIEW_RMC(array[ndarray::view(1,5)(1,3,2)(1)]); // (range, slice, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(1,5)(1,3)(0,2,2)]); // (range, range, slice) - CHECK_VIEW_RMC(array[ndarray::view(1,5)(1,3)(0,2)]); // (range, range, range) - CHECK_VIEW_RMC(array[ndarray::view(1,5)(1,3)()]); // (range, range, full) - CHECK_VIEW_RMC(array[ndarray::view(1,5)(1,3)(1)]); // (range, range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(1,5)()(0,2,2)]); // (range, range, slice) - CHECK_VIEW_RMC(array[ndarray::view(1,5)()(0,2)]); // (range, range, range) - CHECK_VIEW_RMC(array[ndarray::view(1,5)()()]); // (range, range, full) - CHECK_VIEW_RMC(array[ndarray::view(1,5)()(1)]); // (range, range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(1,5)(2)(0,2,2)]); // (range, scalar, slice) - CHECK_VIEW_RMC(array[ndarray::view(1,5)(2)(0,2)]); // (range, scalar, range) - CHECK_VIEW_RMC(array[ndarray::view(1,5)(2)()]); // (range, scalar, full) - - CHECK_VIEW_RMC(array[ndarray::view()(1,3,2)(0,2,2)]); // (full, slice, slice) - CHECK_VIEW_RMC(array[ndarray::view()(1,3,2)(0,2)]); // (full, slice, range) - CHECK_VIEW_RMC(array[ndarray::view()(1,3,2)()]); // (full, slice, full) - CHECK_VIEW_RMC(array[ndarray::view()(1,3,2)(1)]); // (full, slice, scalar) - - CHECK_VIEW_RMC(array[ndarray::view()(1,3)(0,2,2)]); // (full, range, slice) - CHECK_VIEW_RMC(array[ndarray::view()(1,3)(0,2)]); // (full, range, range) - CHECK_VIEW_RMC(array[ndarray::view()(1,3)()]); // (full, range, full) - CHECK_VIEW_RMC(array[ndarray::view()(1,3)(1)]); // (full, range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view()()(0,2,2)]); // (full, range, slice) - CHECK_VIEW_RMC(array[ndarray::view()()(0,2)]); // (full, range, range) - CHECK_VIEW_RMC(array[ndarray::view()()()]); // (full, range, full) - CHECK_VIEW_RMC(array[ndarray::view()()(1)]); // (full, range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view()(2)(0,2,2)]); // (full, scalar, slice) - CHECK_VIEW_RMC(array[ndarray::view()(2)(0,2)]); // (full, scalar, range) - CHECK_VIEW_RMC(array[ndarray::view()(2)()]); // (full, scalar, full) - - CHECK_VIEW_RMC(array[ndarray::view(3)(1,3,2)(0,2,2)]); // (scalar, slice, slice) - CHECK_VIEW_RMC(array[ndarray::view(3)(1,3,2)(0,2)]); // (scalar, slice, range) - CHECK_VIEW_RMC(array[ndarray::view(3)(1,3,2)()]); // (scalar, slice, full) - CHECK_VIEW_RMC(array[ndarray::view(3)(1,3,2)(1)]); // (scalar, slice, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(3)(1,3)(0,2,2)]); // (scalar, range, slice) - CHECK_VIEW_RMC(array[ndarray::view(3)(1,3)(0,2)]); // (scalar, range, range) - CHECK_VIEW_RMC(array[ndarray::view(3)(1,3)()]); // (scalar, range, full) - CHECK_VIEW_RMC(array[ndarray::view(3)(1,3)(1)]); // (scalar, range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(3)()(0,2,2)]); // (scalar, range, slice) - CHECK_VIEW_RMC(array[ndarray::view(3)()(0,2)]); // (scalar, range, range) - CHECK_VIEW_RMC(array[ndarray::view(3)()()]); // (scalar, range, full) - CHECK_VIEW_RMC(array[ndarray::view(3)()(1)]); // (scalar, range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(3)(2)(0,2,2)]); // (scalar, scalar, slice) - CHECK_VIEW_RMC(array[ndarray::view(3)(2)(0,2)]); // (scalar, scalar, range) - CHECK_VIEW_RMC(array[ndarray::view(3)(2)()]); // (scalar, scalar, full) - -} - -BOOST_AUTO_TEST_CASE(view3p1) { - - ndarray::Array parent = ndarray::allocate(12,10,4); - ndarray::Array array = parent[ndarray::view(0,12,2)(0,10,2)()]; - - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(1,3,2)(0,2,2)]); // (slice, slice, slice) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(1,3,2)(0,2)]); // (slice, slice, range) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(1,3,2)()]); // (slice, slice, full) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(1,3,2)(1)]); // (slice, slice, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(1,3)(0,2,2)]); // (slice, range, slice) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(1,3)(0,2)]); // (slice, range, range) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(1,3)()]); // (slice, range, full) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(1,3)(1)]); // (slice, range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)()(0,2,2)]); // (slice, range, slice) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)()(0,2)]); // (slice, range, range) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)()()]); // (slice, range, full) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)()(1)]); // (slice, range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(2)(0,2,2)]); // (slice, scalar, slice) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(2)(0,2)]); // (slice, scalar, range) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(2)()]); // (slice, scalar, full) - - CHECK_VIEW_RMC(array[ndarray::view(1,5)(1,3,2)(0,2,2)]); // (range, slice, slice) - CHECK_VIEW_RMC(array[ndarray::view(1,5)(1,3,2)(0,2)]); // (range, slice, range) - CHECK_VIEW_RMC(array[ndarray::view(1,5)(1,3,2)()]); // (range, slice, full) - CHECK_VIEW_RMC(array[ndarray::view(1,5)(1,3,2)(1)]); // (range, slice, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(1,5)(1,3)(0,2,2)]); // (range, range, slice) - CHECK_VIEW_RMC(array[ndarray::view(1,5)(1,3)(0,2)]); // (range, range, range) - CHECK_VIEW_RMC(array[ndarray::view(1,5)(1,3)()]); // (range, range, full) - CHECK_VIEW_RMC(array[ndarray::view(1,5)(1,3)(1)]); // (range, range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(1,5)()(0,2,2)]); // (range, range, slice) - CHECK_VIEW_RMC(array[ndarray::view(1,5)()(0,2)]); // (range, range, range) - CHECK_VIEW_RMC(array[ndarray::view(1,5)()()]); // (range, range, full) - CHECK_VIEW_RMC(array[ndarray::view(1,5)()(1)]); // (range, range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(1,5)(2)(0,2,2)]); // (range, scalar, slice) - CHECK_VIEW_RMC(array[ndarray::view(1,5)(2)(0,2)]); // (range, scalar, range) - CHECK_VIEW_RMC(array[ndarray::view(1,5)(2)()]); // (range, scalar, full) - - CHECK_VIEW_RMC(array[ndarray::view()(1,3,2)(0,2,2)]); // (full, slice, slice) - CHECK_VIEW_RMC(array[ndarray::view()(1,3,2)(0,2)]); // (full, slice, range) - CHECK_VIEW_RMC(array[ndarray::view()(1,3,2)()]); // (full, slice, full) - CHECK_VIEW_RMC(array[ndarray::view()(1,3,2)(1)]); // (full, slice, scalar) - - CHECK_VIEW_RMC(array[ndarray::view()(1,3)(0,2,2)]); // (full, range, slice) - CHECK_VIEW_RMC(array[ndarray::view()(1,3)(0,2)]); // (full, range, range) - CHECK_VIEW_RMC(array[ndarray::view()(1,3)()]); // (full, range, full) - CHECK_VIEW_RMC(array[ndarray::view()(1,3)(1)]); // (full, range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view()()(0,2,2)]); // (full, range, slice) - CHECK_VIEW_RMC(array[ndarray::view()()(0,2)]); // (full, range, range) - CHECK_VIEW_RMC(array[ndarray::view()()()]); // (full, range, full) - CHECK_VIEW_RMC(array[ndarray::view()()(1)]); // (full, range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view()(2)(0,2,2)]); // (full, scalar, slice) - CHECK_VIEW_RMC(array[ndarray::view()(2)(0,2)]); // (full, scalar, range) - CHECK_VIEW_RMC(array[ndarray::view()(2)()]); // (full, scalar, full) - - CHECK_VIEW_RMC(array[ndarray::view(3)(1,3,2)(0,2,2)]); // (scalar, slice, slice) - CHECK_VIEW_RMC(array[ndarray::view(3)(1,3,2)(0,2)]); // (scalar, slice, range) - CHECK_VIEW_RMC(array[ndarray::view(3)(1,3,2)()]); // (scalar, slice, full) - CHECK_VIEW_RMC(array[ndarray::view(3)(1,3,2)(1)]); // (scalar, slice, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(3)(1,3)(0,2,2)]); // (scalar, range, slice) - CHECK_VIEW_RMC(array[ndarray::view(3)(1,3)(0,2)]); // (scalar, range, range) - CHECK_VIEW_RMC(array[ndarray::view(3)(1,3)()]); // (scalar, range, full) - CHECK_VIEW_RMC(array[ndarray::view(3)(1,3)(1)]); // (scalar, range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(3)()(0,2,2)]); // (scalar, range, slice) - CHECK_VIEW_RMC(array[ndarray::view(3)()(0,2)]); // (scalar, range, range) - CHECK_VIEW_RMC(array[ndarray::view(3)()()]); // (scalar, range, full) - CHECK_VIEW_RMC(array[ndarray::view(3)()(1)]); // (scalar, range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(3)(2)(0,2,2)]); // (scalar, scalar, slice) - CHECK_VIEW_RMC(array[ndarray::view(3)(2)(0,2)]); // (scalar, scalar, range) - CHECK_VIEW_RMC(array[ndarray::view(3)(2)()]); // (scalar, scalar, full) - -} - -BOOST_AUTO_TEST_CASE(view3p0) { - - ndarray::Array parent = ndarray::allocate(12,10,8); - ndarray::Array array = parent[ndarray::view(0,12,2)(0,10,2)(0,8,2)]; - - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(1,3,2)(0,2,2)]); // (slice, slice, slice) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(1,3,2)(0,2)]); // (slice, slice, range) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(1,3,2)()]); // (slice, slice, full) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(1,3,2)(1)]); // (slice, slice, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(1,3)(0,2,2)]); // (slice, range, slice) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(1,3)(0,2)]); // (slice, range, range) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(1,3)()]); // (slice, range, full) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(1,3)(1)]); // (slice, range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)()(0,2,2)]); // (slice, range, slice) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)()(0,2)]); // (slice, range, range) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)()()]); // (slice, range, full) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)()(1)]); // (slice, range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(2)(0,2,2)]); // (slice, scalar, slice) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(2)(0,2)]); // (slice, scalar, range) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(2)()]); // (slice, scalar, full) - - CHECK_VIEW_RMC(array[ndarray::view(1,5)(1,3,2)(0,2,2)]); // (range, slice, slice) - CHECK_VIEW_RMC(array[ndarray::view(1,5)(1,3,2)(0,2)]); // (range, slice, range) - CHECK_VIEW_RMC(array[ndarray::view(1,5)(1,3,2)()]); // (range, slice, full) - CHECK_VIEW_RMC(array[ndarray::view(1,5)(1,3,2)(1)]); // (range, slice, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(1,5)(1,3)(0,2,2)]); // (range, range, slice) - CHECK_VIEW_RMC(array[ndarray::view(1,5)(1,3)(0,2)]); // (range, range, range) - CHECK_VIEW_RMC(array[ndarray::view(1,5)(1,3)()]); // (range, range, full) - CHECK_VIEW_RMC(array[ndarray::view(1,5)(1,3)(1)]); // (range, range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(1,5)()(0,2,2)]); // (range, range, slice) - CHECK_VIEW_RMC(array[ndarray::view(1,5)()(0,2)]); // (range, range, range) - CHECK_VIEW_RMC(array[ndarray::view(1,5)()()]); // (range, range, full) - CHECK_VIEW_RMC(array[ndarray::view(1,5)()(1)]); // (range, range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(1,5)(2)(0,2,2)]); // (range, scalar, slice) - CHECK_VIEW_RMC(array[ndarray::view(1,5)(2)(0,2)]); // (range, scalar, range) - CHECK_VIEW_RMC(array[ndarray::view(1,5)(2)()]); // (range, scalar, full) - - CHECK_VIEW_RMC(array[ndarray::view()(1,3,2)(0,2,2)]); // (full, slice, slice) - CHECK_VIEW_RMC(array[ndarray::view()(1,3,2)(0,2)]); // (full, slice, range) - CHECK_VIEW_RMC(array[ndarray::view()(1,3,2)()]); // (full, slice, full) - CHECK_VIEW_RMC(array[ndarray::view()(1,3,2)(1)]); // (full, slice, scalar) - - CHECK_VIEW_RMC(array[ndarray::view()(1,3)(0,2,2)]); // (full, range, slice) - CHECK_VIEW_RMC(array[ndarray::view()(1,3)(0,2)]); // (full, range, range) - CHECK_VIEW_RMC(array[ndarray::view()(1,3)()]); // (full, range, full) - CHECK_VIEW_RMC(array[ndarray::view()(1,3)(1)]); // (full, range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view()()(0,2,2)]); // (full, range, slice) - CHECK_VIEW_RMC(array[ndarray::view()()(0,2)]); // (full, range, range) - CHECK_VIEW_RMC(array[ndarray::view()()()]); // (full, range, full) - CHECK_VIEW_RMC(array[ndarray::view()()(1)]); // (full, range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view()(2)(0,2,2)]); // (full, scalar, slice) - CHECK_VIEW_RMC(array[ndarray::view()(2)(0,2)]); // (full, scalar, range) - CHECK_VIEW_RMC(array[ndarray::view()(2)()]); // (full, scalar, full) - - CHECK_VIEW_RMC(array[ndarray::view(3)(1,3,2)(0,2,2)]); // (scalar, slice, slice) - CHECK_VIEW_RMC(array[ndarray::view(3)(1,3,2)(0,2)]); // (scalar, slice, range) - CHECK_VIEW_RMC(array[ndarray::view(3)(1,3,2)()]); // (scalar, slice, full) - CHECK_VIEW_RMC(array[ndarray::view(3)(1,3,2)(1)]); // (scalar, slice, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(3)(1,3)(0,2,2)]); // (scalar, range, slice) - CHECK_VIEW_RMC(array[ndarray::view(3)(1,3)(0,2)]); // (scalar, range, range) - CHECK_VIEW_RMC(array[ndarray::view(3)(1,3)()]); // (scalar, range, full) - CHECK_VIEW_RMC(array[ndarray::view(3)(1,3)(1)]); // (scalar, range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(3)()(0,2,2)]); // (scalar, range, slice) - CHECK_VIEW_RMC(array[ndarray::view(3)()(0,2)]); // (scalar, range, range) - CHECK_VIEW_RMC(array[ndarray::view(3)()()]); // (scalar, range, full) - CHECK_VIEW_RMC(array[ndarray::view(3)()(1)]); // (scalar, range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(3)(2)(0,2,2)]); // (scalar, scalar, slice) - CHECK_VIEW_RMC(array[ndarray::view(3)(2)(0,2)]); // (scalar, scalar, range) - CHECK_VIEW_RMC(array[ndarray::view(3)(2)()]); // (scalar, scalar, full) - -} - -BOOST_AUTO_TEST_CASE(view3m2) { - - ndarray::Array parent1 = ndarray::allocate(8,5,6); - ndarray::Array parent2 = parent1.transpose(); - ndarray::Array array = parent2[ndarray::view()()(0,8,2)]; - - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(1,3,2)(0,2,2)]); // (slice, slice, slice) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(1,3,2)(0,2)]); // (slice, slice, range) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(1,3,2)()]); // (slice, slice, full) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(1,3,2)(1)]); // (slice, slice, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(1,3)(0,2,2)]); // (slice, range, slice) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(1,3)(0,2)]); // (slice, range, range) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(1,3)()]); // (slice, range, full) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(1,3)(1)]); // (slice, range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)()(0,2,2)]); // (slice, range, slice) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)()(0,2)]); // (slice, range, range) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)()()]); // (slice, range, full) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)()(1)]); // (slice, range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(2)(0,2,2)]); // (slice, scalar, slice) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(2)(0,2)]); // (slice, scalar, range) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(2)()]); // (slice, scalar, full) - - CHECK_VIEW_RMC(array[ndarray::view(1,5)(1,3,2)(0,2,2)]); // (range, slice, slice) - CHECK_VIEW_RMC(array[ndarray::view(1,5)(1,3,2)(0,2)]); // (range, slice, range) - CHECK_VIEW_RMC(array[ndarray::view(1,5)(1,3,2)()]); // (range, slice, full) - CHECK_VIEW_RMC(array[ndarray::view(1,5)(1,3,2)(1)]); // (range, slice, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(1,5)(1,3)(0,2,2)]); // (range, range, slice) - CHECK_VIEW_RMC(array[ndarray::view(1,5)(1,3)(0,2)]); // (range, range, range) - CHECK_VIEW_RMC(array[ndarray::view(1,5)(1,3)()]); // (range, range, full) - CHECK_VIEW_RMC(array[ndarray::view(1,5)(1,3)(1)]); // (range, range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(1,5)()(0,2,2)]); // (range, range, slice) - CHECK_VIEW_RMC(array[ndarray::view(1,5)()(0,2)]); // (range, range, range) - CHECK_VIEW_RMC(array[ndarray::view(1,5)()()]); // (range, range, full) - CHECK_VIEW_RMC(array[ndarray::view(1,5)()(1)]); // (range, range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(1,5)(2)(0,2,2)]); // (range, scalar, slice) - CHECK_VIEW_RMC(array[ndarray::view(1,5)(2)(0,2)]); // (range, scalar, range) - CHECK_VIEW_RMC(array[ndarray::view(1,5)(2)()]); // (range, scalar, full) - - CHECK_VIEW_RMC(array[ndarray::view()(1,3,2)(0,2,2)]); // (full, slice, slice) - CHECK_VIEW_RMC(array[ndarray::view()(1,3,2)(0,2)]); // (full, slice, range) - CHECK_VIEW_RMC(array[ndarray::view()(1,3,2)()]); // (full, slice, full) - CHECK_VIEW_RMC(array[ndarray::view()(1,3,2)(1)]); // (full, slice, scalar) - - CHECK_VIEW_RMC(array[ndarray::view()(1,3)(0,2,2)]); // (full, range, slice) - CHECK_VIEW_RMC(array[ndarray::view()(1,3)(0,2)]); // (full, range, range) - CHECK_VIEW_RMC(array[ndarray::view()(1,3)()]); // (full, range, full) - CHECK_VIEW_RMC(array[ndarray::view()(1,3)(1)]); // (full, range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view()()(0,2,2)]); // (full, range, slice) - CHECK_VIEW_RMC(array[ndarray::view()()(0,2)]); // (full, range, range) - CHECK_VIEW_RMC(array[ndarray::view()()()]); // (full, range, full) - CHECK_VIEW_RMC(array[ndarray::view()()(1)]); // (full, range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view()(2)(0,2,2)]); // (full, scalar, slice) - CHECK_VIEW_RMC(array[ndarray::view()(2)(0,2)]); // (full, scalar, range) - CHECK_VIEW_RMC(array[ndarray::view()(2)()]); // (full, scalar, full) - - CHECK_VIEW_RMC(array[ndarray::view(3)(1,3,2)(0,2,2)]); // (scalar, slice, slice) - CHECK_VIEW_RMC(array[ndarray::view(3)(1,3,2)(0,2)]); // (scalar, slice, range) - CHECK_VIEW_RMC(array[ndarray::view(3)(1,3,2)()]); // (scalar, slice, full) - CHECK_VIEW_RMC(array[ndarray::view(3)(1,3,2)(1)]); // (scalar, slice, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(3)(1,3)(0,2,2)]); // (scalar, range, slice) - CHECK_VIEW_RMC(array[ndarray::view(3)(1,3)(0,2)]); // (scalar, range, range) - CHECK_VIEW_RMC(array[ndarray::view(3)(1,3)()]); // (scalar, range, full) - CHECK_VIEW_RMC(array[ndarray::view(3)(1,3)(1)]); // (scalar, range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(3)()(0,2,2)]); // (scalar, range, slice) - CHECK_VIEW_RMC(array[ndarray::view(3)()(0,2)]); // (scalar, range, range) - CHECK_VIEW_RMC(array[ndarray::view(3)()()]); // (scalar, range, full) - CHECK_VIEW_RMC(array[ndarray::view(3)()(1)]); // (scalar, range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(3)(2)(0,2,2)]); // (scalar, scalar, slice) - CHECK_VIEW_RMC(array[ndarray::view(3)(2)(0,2)]); // (scalar, scalar, range) - CHECK_VIEW_RMC(array[ndarray::view(3)(2)()]); // (scalar, scalar, full) - -} - -BOOST_AUTO_TEST_CASE(view3m1) { - - ndarray::Array parent1 = ndarray::allocate(8,10,6); - ndarray::Array parent2 = parent1.transpose(); - ndarray::Array array = parent2[ndarray::view()(0,10,2)(0,8,2)]; - - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(1,3,2)(0,2,2)]); // (slice, slice, slice) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(1,3,2)(0,2)]); // (slice, slice, range) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(1,3,2)()]); // (slice, slice, full) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(1,3,2)(1)]); // (slice, slice, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(1,3)(0,2,2)]); // (slice, range, slice) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(1,3)(0,2)]); // (slice, range, range) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(1,3)()]); // (slice, range, full) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(1,3)(1)]); // (slice, range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)()(0,2,2)]); // (slice, range, slice) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)()(0,2)]); // (slice, range, range) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)()()]); // (slice, range, full) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)()(1)]); // (slice, range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(2)(0,2,2)]); // (slice, scalar, slice) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(2)(0,2)]); // (slice, scalar, range) - CHECK_VIEW_RMC(array[ndarray::view(2,6,2)(2)()]); // (slice, scalar, full) - - CHECK_VIEW_RMC(array[ndarray::view(1,5)(1,3,2)(0,2,2)]); // (range, slice, slice) - CHECK_VIEW_RMC(array[ndarray::view(1,5)(1,3,2)(0,2)]); // (range, slice, range) - CHECK_VIEW_RMC(array[ndarray::view(1,5)(1,3,2)()]); // (range, slice, full) - CHECK_VIEW_RMC(array[ndarray::view(1,5)(1,3,2)(1)]); // (range, slice, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(1,5)(1,3)(0,2,2)]); // (range, range, slice) - CHECK_VIEW_RMC(array[ndarray::view(1,5)(1,3)(0,2)]); // (range, range, range) - CHECK_VIEW_RMC(array[ndarray::view(1,5)(1,3)()]); // (range, range, full) - CHECK_VIEW_RMC(array[ndarray::view(1,5)(1,3)(1)]); // (range, range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(1,5)()(0,2,2)]); // (range, range, slice) - CHECK_VIEW_RMC(array[ndarray::view(1,5)()(0,2)]); // (range, range, range) - CHECK_VIEW_RMC(array[ndarray::view(1,5)()()]); // (range, range, full) - CHECK_VIEW_RMC(array[ndarray::view(1,5)()(1)]); // (range, range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(1,5)(2)(0,2,2)]); // (range, scalar, slice) - CHECK_VIEW_RMC(array[ndarray::view(1,5)(2)(0,2)]); // (range, scalar, range) - CHECK_VIEW_RMC(array[ndarray::view(1,5)(2)()]); // (range, scalar, full) - - CHECK_VIEW_RMC(array[ndarray::view()(1,3,2)(0,2,2)]); // (full, slice, slice) - CHECK_VIEW_RMC(array[ndarray::view()(1,3,2)(0,2)]); // (full, slice, range) - CHECK_VIEW_RMC(array[ndarray::view()(1,3,2)()]); // (full, slice, full) - CHECK_VIEW_RMC(array[ndarray::view()(1,3,2)(1)]); // (full, slice, scalar) - - CHECK_VIEW_RMC(array[ndarray::view()(1,3)(0,2,2)]); // (full, range, slice) - CHECK_VIEW_RMC(array[ndarray::view()(1,3)(0,2)]); // (full, range, range) - CHECK_VIEW_RMC(array[ndarray::view()(1,3)()]); // (full, range, full) - CHECK_VIEW_RMC(array[ndarray::view()(1,3)(1)]); // (full, range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view()()(0,2,2)]); // (full, range, slice) - CHECK_VIEW_RMC(array[ndarray::view()()(0,2)]); // (full, range, range) - CHECK_VIEW_RMC(array[ndarray::view()()()]); // (full, range, full) - CHECK_VIEW_RMC(array[ndarray::view()()(1)]); // (full, range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view()(2)(0,2,2)]); // (full, scalar, slice) - CHECK_VIEW_RMC(array[ndarray::view()(2)(0,2)]); // (full, scalar, range) - CHECK_VIEW_RMC(array[ndarray::view()(2)()]); // (full, scalar, full) - - CHECK_VIEW_RMC(array[ndarray::view(3)(1,3,2)(0,2,2)]); // (scalar, slice, slice) - CHECK_VIEW_RMC(array[ndarray::view(3)(1,3,2)(0,2)]); // (scalar, slice, range) - CHECK_VIEW_RMC(array[ndarray::view(3)(1,3,2)()]); // (scalar, slice, full) - CHECK_VIEW_RMC(array[ndarray::view(3)(1,3,2)(1)]); // (scalar, slice, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(3)(1,3)(0,2,2)]); // (scalar, range, slice) - CHECK_VIEW_RMC(array[ndarray::view(3)(1,3)(0,2)]); // (scalar, range, range) - CHECK_VIEW_RMC(array[ndarray::view(3)(1,3)()]); // (scalar, range, full) - CHECK_VIEW_RMC(array[ndarray::view(3)(1,3)(1)]); // (scalar, range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(3)()(0,2,2)]); // (scalar, range, slice) - CHECK_VIEW_RMC(array[ndarray::view(3)()(0,2)]); // (scalar, range, range) - CHECK_VIEW_RMC(array[ndarray::view(3)()()]); // (scalar, range, full) - CHECK_VIEW_RMC(array[ndarray::view(3)()(1)]); // (scalar, range, scalar) - - CHECK_VIEW_RMC(array[ndarray::view(3)(2)(0,2,2)]); // (scalar, scalar, slice) - CHECK_VIEW_RMC(array[ndarray::view(3)(2)(0,2)]); // (scalar, scalar, range) - CHECK_VIEW_RMC(array[ndarray::view(3)(2)()]); // (scalar, scalar, full) - -} diff --git a/tests/views.cpp b/tests/views.cpp new file mode 100644 index 00000000..768de9eb --- /dev/null +++ b/tests/views.cpp @@ -0,0 +1,27 @@ +// -*- c++ -*- +/* + * Copyright (c) 2010-2018, Jim Bosch + * All rights reserved. + * + * ndarray is distributed under a simple BSD-like license; + * see the LICENSE file that should be present in the root + * of the source distribution, or alternately available at: + * https://github.com/ndarray/ndarray + */ +#include +#include "catch2/catch.hpp" + +#define NDARRAY_ASSERT_AUDIT_ENABLED true + +#include "ndarray/views.hpp" + +using namespace ndarray::views; + + +TEST_CASE("views", "[views]") { +#if __cplusplus >= 201703L + auto v = view(all)(1, 2)(begin, 5)(3, end)(1, 2, -unit)(newaxis)(3, 4, 2); +#else + auto v = view(All{})(1, 2)(Begin{}, 5)(3, End{})(1, 2, NegUnit{})(NewAxis{})(3, 4, 2); +#endif +}