diff --git a/.github/workflows/dockercentos.yml b/.github/workflows/dockercentos.yml index 28967dfd..770f2b30 100644 --- a/.github/workflows/dockercentos.yml +++ b/.github/workflows/dockercentos.yml @@ -1,23 +1,42 @@ # This files generates Python wheels for Linux on: push: - branches: 'master' + branches: + - codac1 + - codac2 + - codac2_codac4matlab tags: '' # Restrict to blank tags pull_request: jobs: dockercentos: - name: CentOS Docker runs-on: ubuntu-latest + defaults: + run: + shell: ${{ matrix.cfg.shell }} + strategy: + fail-fast: false + matrix: + cfg: + - { img: 'lebarsfa/manylinux2014_x86_64-for-codac', shell: bash, arch: x86_64, bitness: 64, runtime: manylinux2014, desc: 'CentOS manylinux2014 x86_64' } + # - { img: 'lebarsfa/manylinux2014_aarch64-for-codac', shell: bash, arch: aarch64, bitness: 64, runtime: manylinux2014, docker_flags: '--platform linux/arm64', desc: 'CentOS manylinux2014 aarch64' } + name: ${{ matrix.cfg.desc }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: true fetch-depth: 0 clean: false - run: | - chmod a+x scripts/docker/build_pybinding.sh - docker run --rm -v `pwd`:/io lebarsfa/manylinux2010_x86_64-ibex /io/scripts/docker/build_pybinding.sh + sudo apt-get -y install qemu binfmt-support qemu-user-static || true + #docker run --rm --privileged multiarch/qemu-user-static:register --reset + docker run --rm --privileged multiarch/qemu-user-static --reset -p yes + if: (matrix.cfg.arch!='x86_64')&&(matrix.cfg.arch!='i386') + - run: | + chmod a+x scripts/docker/*.sh + if [ ${{ github.ref_name }} = 'codac2_codac4matlab' ] || [ ${{ github.event.pull_request.base.ref }} = 'codac2_codac4matlab' ]; then docker run ${{ matrix.cfg.docker_flags }} --rm -v `pwd`:/io ${{ matrix.cfg.img }} /io/scripts/docker/build_pybinding_codac4matlab.sh + else docker run ${{ matrix.cfg.docker_flags }} --rm -v `pwd`:/io ${{ matrix.cfg.img }} /io/scripts/docker/build_pybinding.sh + fi ls wheelhouse - uses: xresloader/upload-to-github-release@v1 env: @@ -26,4 +45,4 @@ jobs: file: "wheelhouse/*.whl" overwrite: true tag_name: autotagname-${{ github.sha }} - if: (github.event_name != 'pull_request')&&(github.ref_name == 'master') + if: (github.event_name!='pull_request')&&((github.ref_name=='codac1')||(github.ref_name=='codac2')||(github.ref_name=='codac2_codac4matlab')) diff --git a/.github/workflows/dockermatrix.yml b/.github/workflows/dockermatrix.yml new file mode 100644 index 00000000..2ce5e272 --- /dev/null +++ b/.github/workflows/dockermatrix.yml @@ -0,0 +1,119 @@ +# This file checks that the lib runs on ARM +on: + push: + branches: + - codac1 + - codac2 + # The following is implied by above selection... + #branches-ignore: + #- codac2_codac4matlab + tags: '' # Restrict to blank tags + pull_request: + branches-ignore: + - codac2_codac4matlab + +jobs: + dockermatrix: + runs-on: ${{ matrix.cfg.os }} + defaults: + run: + shell: ${{ matrix.cfg.shell }} + strategy: + fail-fast: false + matrix: + cfg: + - { img: 'lebarsfa/manylinux2014_x86_64-for-codac', os: ubuntu-latest, shell: bash, arch: x86_64, bitness: 64, runtime: manylinux2014, cmake_flags: '-fPIC', desc: 'CentOS manylinux2014 x86_64' } + - { img: 'lebarsfa/manylinux2014_aarch64-for-codac', os: ubuntu-24.04-arm, shell: bash, arch: aarch64, bitness: 64, runtime: manylinux2014, cmake_flags: '-fPIC', docker_flags: '--platform linux/arm64', desc: 'CentOS manylinux2014 aarch64' } + - { img: 'lebarsfa/pi-64:noble-for-codac', os: ubuntu-24.04-arm, shell: bash, arch: arm64, bitness: 64, runtime: noble, cmake_flags: '-fPIC', deb: true, desc: 'Ubuntu 24.04 arm64' } + - { img: 'lebarsfa/pi-64:jammy-for-codac', os: ubuntu-24.04-arm, shell: bash, arch: arm64, bitness: 64, runtime: jammy, cmake_flags: '-fPIC', deb: true, desc: 'Ubuntu 22.04 arm64' } + - { img: 'lebarsfa/pi-64:focal-for-codac', os: ubuntu-24.04-arm, shell: bash, arch: arm64, bitness: 64, runtime: focal, cmake_flags: '-fPIC', deb: true, desc: 'Ubuntu 20.04 arm64' } + - { img: 'lebarsfa/amd64:bookworm-for-codac', os: ubuntu-latest, shell: bash, arch: amd64, bitness: 64, runtime: bookworm, cmake_flags: '-fPIC', deb: true, desc: 'Debian Bookworm amd64' } + - { img: 'lebarsfa/pi-64:bookworm-for-codac', os: ubuntu-24.04-arm, shell: bash, arch: arm64, bitness: 64, runtime: bookworm, cmake_flags: '-fPIC', deb: true, desc: 'Debian Bookworm arm64' } + - { img: 'lebarsfa/pi:bookworm-for-codac', os: ubuntu-24.04-arm, shell: bash, arch: armhf, bitness: 32, runtime: bookworm, cmake_flags: '-fPIC', deb: true, desc: 'Raspbian Bookworm armv6hf' } + - { img: 'lebarsfa/amd64:bullseye-for-codac', os: ubuntu-latest, shell: bash, arch: amd64, bitness: 64, runtime: bullseye, cmake_flags: '-fPIC', deb: true, desc: 'Debian Bullseye amd64' } + - { img: 'lebarsfa/pi-64:bullseye-for-codac', os: ubuntu-24.04-arm, shell: bash, arch: arm64, bitness: 64, runtime: bullseye, cmake_flags: '-fPIC', deb: true, desc: 'Debian Bullseye arm64' } + - { img: 'lebarsfa/pi:bullseye-for-codac', os: ubuntu-24.04-arm, shell: bash, arch: armhf, bitness: 32, runtime: bullseye, cmake_flags: '-fPIC', deb: true, desc: 'Raspbian Bullseye armv6hf' } + - { img: 'lebarsfa/amd64:buster-for-codac', os: ubuntu-latest, shell: bash, arch: amd64, bitness: 64, runtime: buster, cmake_flags: '-fPIC', deb: true, desc: 'Debian Buster amd64' } + - { img: 'lebarsfa/pi-64:buster-for-codac', os: ubuntu-24.04-arm, shell: bash, arch: arm64, bitness: 64, runtime: buster, cmake_flags: '-fPIC', deb: true, desc: 'Debian Buster arm64' } + - { img: 'lebarsfa/pi:buster-for-codac', os: ubuntu-24.04-arm, shell: bash, arch: armhf, bitness: 32, runtime: buster, cmake_flags: '-fPIC', deb: true, desc: 'Raspbian Buster armv6hf' } + name: ${{ matrix.cfg.desc }} + steps: + - uses: actions/checkout@v4 + with: + submodules: true + fetch-depth: 0 + clean: false + # From https://github.com/Munkei/VersionFromGit.cmake/blob/master/VersionFromGit.cmake + - run: echo "git_tag=`git describe --tags --abbrev=0`" >> $GITHUB_ENV + shell: bash + # See https://askubuntu.com/questions/620533/what-is-the-meaning-of-the-xubuntuy-string-in-ubuntu-package-names + - run: | + echo "SOFTWARE_VERSION=${git_tag:1}" >> $GITHUB_ENV + echo "DEBIAN_PACKAGE_REV=0" >> $GITHUB_ENV + echo "PACKAGE_REV=0" >> $GITHUB_ENV + echo "CHOCO_PACKAGE_REV=" >> $GITHUB_ENV + echo "VERBOSE=1" >> $GITHUB_ENV + shell: bash + - run: echo "PACKAGE_VERSION=$SOFTWARE_VERSION-${DEBIAN_PACKAGE_REV}${{ matrix.cfg.runtime }}$PACKAGE_REV" >> $GITHUB_ENV + shell: bash + if: matrix.cfg.deb==true + #- run: | + # sudo apt-get -y install qemu binfmt-support qemu-user-static || true + # #docker run --rm --privileged multiarch/qemu-user-static:register --reset + # docker run --rm --privileged multiarch/qemu-user-static --reset -p yes + # if: (matrix.cfg.arch!='amd64')&&(matrix.cfg.arch!='x86_64')&&(matrix.cfg.arch!='i386') + - run: | + docker run ${{ matrix.cfg.docker_flags }} -i -v "${PWD}/..:${PWD}/.." ${{ matrix.cfg.img }} /bin/bash -c "uname -a ; cat /etc/os-release ; lsb_release -a ; cd ${PWD} && pwd && \ + git config --global --add safe.directory ${PWD} && \ + if [ \"${{ matrix.cfg.deb }}\" = \"true\" ]; then \ + #sudo sh -c 'echo \"deb [trusted=yes] https://packages.ensta-bretagne.fr/\$(if [ -z \"\$(. /etc/os-release && echo \$UBUNTU_CODENAME)\" ]; then echo debian/\$(. /etc/os-release && echo \$VERSION_CODENAME); else echo ubuntu/\$(. /etc/os-release && echo \$UBUNTU_CODENAME); fi) ./\" > /etc/apt/sources.list.d/ensta-bretagne.list' && \\ + sudo apt-get -q update ; sudo apt-get -y install libeigen3-dev dpkg-dev || true && \ + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241117/libibex-dev-2.8.9.20241117-0${{ matrix.cfg.runtime }}0_\$(dpkg --print-architecture).deb --no-check-certificate -nv && \ + sudo dpkg -i libibex-dev-2.8.9.20241117-0${{ matrix.cfg.runtime }}0_\$(dpkg --print-architecture).deb && \ + rm -Rf libibex-dev-2.8.9.20241117-0${{ matrix.cfg.runtime }}0_\$(dpkg --print-architecture).deb ; \ + else \ + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241117/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip --no-check-certificate -nv && \ + unzip -q ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip && \ + rm -Rf ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip && \ + sudo cp -Rf ibex/* /usr/ ; \ + fi && \ + mkdir build ; cd build && \ + cmake -E env CXXFLAGS="${{ matrix.cfg.cmake_flags }}" CFLAGS="${{ matrix.cfg.cmake_flags }}" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_INSTALL_PREFIX="../codac" .. && \ + cmake --build . -j 4 --config Debug --target install && \ + cd .. && \ + zip -q -r codac_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip codac && \ + mkdir -p codac_standalone/example ; cd codac_standalone && \ + if [ \"${{ matrix.cfg.deb }}\" = \"true\" ]; then mkdir -p ibex/include ; mkdir -p ibex/lib ; mkdir -p ibex/share ; mkdir -p ibex/bin ; cp -Rf /usr/include/ibex* ibex/include/ ; cp -Rf /usr/lib/*ibex* ibex/lib/ ; cp -Rf /usr/share/*ibex* ibex/share/ ; cp -Rf /usr/share/pkgconfig ibex/share/ ; cp -Rf /usr/bin/ibex* ibex/bin/ ; \ + else cp -Rf ../ibex . ; \ + fi && \ + cp -Rf ../codac . ; cp -Rf ../tests/test_codac/* ./example/ ; cd .. ; zip -q -r codac_standalone_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip codac_standalone && \ + cd codac_standalone/example && \ + cmake ${{ matrix.cfg.cmake_params }} . && \ + cmake --build . --config Release && \ + file ./${{ matrix.cfg.test_config }}my_project && \ + ./${{ matrix.cfg.test_config }}my_project && \ + cd ../.. && \ + if [ \"${{ matrix.cfg.deb }}\" = \"true\" ]; then \ + cd packages && \ + chmod +x ./genlibcodac-dev.sh && \ + ./genlibcodac-dev.sh \$(if [ -z \"\$(. /etc/os-release && echo \$UBUNTU_CODENAME)\" ]; then echo debian; else echo ubuntu; fi) ${{ matrix.cfg.runtime }} \$(dpkg --print-architecture) $SOFTWARE_VERSION $DEBIAN_PACKAGE_REV $PACKAGE_REV && \ + cd .. && \ + sudo dpkg -i libcodac-dev-$PACKAGE_VERSION\_\$(dpkg --print-architecture).deb ; \ + else \ + sudo cp -Rf codac/* /usr/local/ ; \ + fi && \ + rm -Rf codac && \ + cd tests/test_codac && \ + cmake ${{ matrix.cfg.cmake_params }} . && \ + cmake --build . --config Release && \ + file ./${{ matrix.cfg.test_config }}my_project && \ + ./${{ matrix.cfg.test_config }}my_project && \ + cd ../.." + - uses: xresloader/upload-to-github-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + file: "*.zip;*.deb" + overwrite: true + tag_name: autotagname-${{ github.sha }} + if: (github.event_name!='pull_request')&&((github.ref_name=='codac1')||(github.ref_name=='codac2')||(github.ref_name=='codac2_codac4matlab')) diff --git a/.github/workflows/dockerpi.yml b/.github/workflows/dockerpi.yml deleted file mode 100644 index 9f45370d..00000000 --- a/.github/workflows/dockerpi.yml +++ /dev/null @@ -1,27 +0,0 @@ -# This file checks that the lib runs on ARM -on: - push: - branches: 'master' - tags: '' # Restrict to blank tags - pull_request: - -jobs: - dockerpi: - name: Raspbian Buster pi Docker - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - with: - submodules: true - fetch-depth: 0 - clean: false - - run: docker run --rm --privileged multiarch/qemu-user-static:register --reset - - run: docker run -i -v "${PWD}/..:${PWD}/.." lebarsfa/pi:buster-ibex /bin/bash -c "uname -a && cat /etc/os-release && cd ${PWD} && lsb_release -a && sudo apt-get -q update --allow-releaseinfo-change ; sudo apt-get -y install libeigen3-dev zip ; mkdir build ; cd build && cmake -D CMAKE_INSTALL_PREFIX=../codac .. && cmake --build . --target install && cd .. && mkdir -p codac_standalone/example ; cd codac_standalone ; wget https://community.chocolatey.org/api/v2/package/eigen/3.4.0 --no-check-certificate -nv ; unzip -q 3.4.0 -d eigen ; rm -Rf 3.4.0 eigen/*.xml eigen/*.nuspec eigen/_* eigen/package eigen/tools ; mkdir -p ibex/include ; mkdir -p ibex/lib ; mkdir -p ibex/share ; mkdir -p ibex/bin ; cp -Rf /usr/local/include/ibex* ibex/include/ ; cp -Rf /usr/local/lib/*ibex* ibex/lib/ ; cp -Rf /usr/local/share/*ibex* ibex/share/ ; cp -Rf /usr/local/share/pkgconfig ibex/share/ ; cp -Rf /usr/local/bin/ibex* ibex/bin/ ; cp -Rf ../codac . ; cp -Rf ../tests/test_codac/* ./example/ ; cd .. ; zip -q -r codac_standalone_armv6hf_buster.zip codac_standalone ; cd codac_standalone/example && cmake . && cmake --build . && ./my_project" - - uses: xresloader/upload-to-github-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - file: "*.zip" - overwrite: true - tag_name: autotagname-${{ github.sha }} - if: (github.event_name != 'pull_request')&&(github.ref_name == 'master') diff --git a/.github/workflows/macosmatrix.yml b/.github/workflows/macosmatrix.yml index 6f0cb2de..532a1637 100644 --- a/.github/workflows/macosmatrix.yml +++ b/.github/workflows/macosmatrix.yml @@ -1,53 +1,76 @@ -# This file generates Python wheels for Windows -# (and zip for having Codac and IBEX binaries for several Visual Studio versions) +# This file generates Python wheels for macOS on: push: - branches: 'master' + branches: + - codac1 + - codac2 + - codac2_codac4matlab tags: '' # Restrict to blank tags pull_request: jobs: - vcmatrix: + macosmatrix: runs-on: ${{ matrix.cfg.os }} + env: + MACOSX_DEPLOYMENT_TARGET: ${{ matrix.cfg.trgt }} + _PYTHON_HOST_PLATFORM: macosx-${{ matrix.cfg.trgt }}-${{ matrix.cfg.arch }} defaults: run: shell: ${{ matrix.cfg.shell }} strategy: + fail-fast: false matrix: cfg: -# - { os: macos-11, shell: bash, arch: x64, runtime: bigsur, cmake_flags: '-fPIC', cpcfg: '-macosx_11_0_x86_64', py_v_maj: 3, py_v_min: 10, desc: 'macOS Big Sur Python 3.10' } - - { os: macos-10.15, shell: bash, arch: x64, runtime: catalina, cmake_flags: '-fPIC', cpcfg: '-macosx_10_15_x86_64', py_v_maj: 3, py_v_min: 10, desc: 'macOS Catalina Python 3.10' } - - { os: macos-10.15, shell: bash, arch: x64, runtime: catalina, cmake_flags: '-fPIC', cpcfg: '-macosx_10_15_x86_64', py_v_maj: 3, py_v_min: 9, desc: 'macOS Catalina Python 3.9' } - - { os: macos-10.15, shell: bash, arch: x64, runtime: catalina, cmake_flags: '-fPIC', cpcfg: '-macosx_10_15_x86_64', py_v_maj: 3, py_v_min: 8, desc: 'macOS Catalina Python 3.8' } - - { os: macos-10.15, shell: bash, arch: x64, runtime: catalina, cmake_flags: '-fPIC', cpcfg: '-macosx_10_15_x86_64', py_v_maj: 3, py_v_min: 7, desc: 'macOS Catalina Python 3.7' } - - { os: macos-10.15, shell: bash, arch: x64, runtime: catalina, cmake_flags: '-fPIC', cpcfg: '-macosx_10_15_x86_64', py_v_maj: 3, py_v_min: 6, desc: 'macOS Catalina Python 3.6' } + - { os: macos-14, shell: bash, arch: arm64, runtime: sonoma, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 13, desc: 'macOS Sonoma Python 3.13 arm64' } + - { os: macos-14, shell: bash, arch: arm64, runtime: sonoma, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 12, desc: 'macOS Sonoma Python 3.12 arm64' } + - { os: macos-14, shell: bash, arch: arm64, runtime: sonoma, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 11, desc: 'macOS Sonoma Python 3.11 arm64' } + - { os: macos-13, shell: bash, arch: arm64, runtime: ventura, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 10, cross: true, desc: 'macOS Ventura Python 3.10 arm64 (cross)' } + - { os: macos-13, shell: bash, arch: arm64, runtime: ventura, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 9, cross: true, desc: 'macOS Ventura Python 3.9 arm64 (cross)' } + - { os: macos-13, shell: bash, arch: arm64, runtime: ventura, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 8, cross: true, desc: 'macOS Ventura Python 3.8 arm64 (cross)' } + - { os: macos-13, shell: bash, arch: arm64, runtime: ventura, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: 'm-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 7, cross: true, desc: 'macOS Ventura Python 3.7 arm64 (cross)' } + - { os: macos-13, shell: bash, arch: arm64, runtime: ventura, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: 'm-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 6, cross: true, desc: 'macOS Ventura Python 3.6 arm64 (cross)' } + - { os: macos-13, shell: bash, arch: x86_64, runtime: ventura, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 13, desc: 'macOS Ventura Python 3.13 x86_64' } + - { os: macos-13, shell: bash, arch: x86_64, runtime: ventura, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 12, desc: 'macOS Ventura Python 3.12 x86_64' } + - { os: macos-13, shell: bash, arch: x86_64, runtime: ventura, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 11, desc: 'macOS Ventura Python 3.11 x86_64' } + - { os: macos-13, shell: bash, arch: x86_64, runtime: ventura, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 10, desc: 'macOS Ventura Python 3.10 x86_64' } + - { os: macos-13, shell: bash, arch: x86_64, runtime: ventura, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 9, desc: 'macOS Ventura Python 3.9 x86_64' } + - { os: macos-13, shell: bash, arch: x86_64, runtime: ventura, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 8, desc: 'macOS Ventura Python 3.8 x86_64' } + - { os: macos-13, shell: bash, arch: x86_64, runtime: ventura, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: 'm-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 7, desc: 'macOS Ventura Python 3.7 x86_64' } + - { os: macos-13, shell: bash, arch: x86_64, runtime: ventura, cmake_flags: '-fPIC', trgt: '10.14', cpcfg: 'm-macosx_10_14_x86_64', py_v_maj: 3, py_v_min: 6, desc: 'macOS Ventura Python 3.6 x86_64' } # 10.14 because of error $MACOSX_DEPLOYMENT_TARGET mismatch: now "10.9" but "10.14" during configure. name: ${{ matrix.cfg.desc }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: true fetch-depth: 0 clean: false - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v5 with: python-version: ${{ matrix.cfg.py_v_maj }}.${{ matrix.cfg.py_v_min }} - architecture: ${{ matrix.cfg.arch }} + architecture: x64 if: matrix.cfg.py_v_maj!='' - run: | python -c "import sys; print(sys.version)" echo ${{ matrix.cfg.py_v_maj }}.${{ matrix.cfg.py_v_min }} if: matrix.cfg.py_v_maj!='' + - run: echo "VERBOSE=1" >> $GITHUB_ENV + shell: bash - run: brew install eigen if: runner.os=='macOS' - - run: brew install doxygen ; brew install graphviz ; python -m pip install --upgrade pip ; pip install --upgrade wheel setuptools sphinx breathe sphinx_rtd_theme sphinx-tabs sphinx-issues sphinx-reredirects + # doxygen v1.9.7 causes issues... + - run: brew install graphviz ; wget https://github.com/Homebrew/homebrew-core/raw/d2267b9f2ad247bc9c8273eb755b39566a474a70/Formula/doxygen.rb ; brew reinstall --formula ./doxygen.rb ; brew pin doxygen ; python -m pip install --upgrade pip ; pip install --upgrade wheel setuptools sphinx breathe sphinx_rtd_theme sphinx-tabs sphinx-issues sphinx-reredirects if: runner.os=='macOS' - - run: git clone -b actions https://github.com/lebarsfa/ibex-lib.git ; cd ibex-lib ; mkdir build ; cd build ; cmake -E env CXXFLAGS="${{ matrix.cfg.cmake_flags }}" CFLAGS="${{ matrix.cfg.cmake_flags }}" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_INSTALL_PREFIX="../../ibex" .. ; cmake --build . --config Release --target install ; cd ../.. + - run: | + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241117/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip --no-check-certificate -nv + unzip -q ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip + rm -Rf ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip + sudo cp -Rf ibex/* /usr/local/ shell: bash - run: | mkdir build ; cd build - cmake -E env CXXFLAGS="${{ matrix.cfg.cmake_flags }}" CFLAGS="${{ matrix.cfg.cmake_flags }}" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_PREFIX_PATH="ibex" -D CMAKE_INSTALL_PREFIX="../codac" -D WITH_PYTHON=ON .. - cmake --build . --config Release --target install - cmake --build . --config Release --target pip_package ; cp `ls *.whl` ../`ls *.whl | sed "s/py3-none-any/cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}-cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}${{ matrix.cfg.cpcfg }}/"` ; pip install ../*.whl ; python -c "import sys; print(sys.version)" ; python ../examples/tuto/01_getting_started/01_getting_started.py + cmake -E env CXXFLAGS="${{ matrix.cfg.cmake_flags }}" CFLAGS="${{ matrix.cfg.cmake_flags }}" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=${{ matrix.cfg.arch }} -D CMAKE_INSTALL_PREFIX="../codac" -D WITH_PYTHON=ON .. + cmake --build . -j 4 --config Release --target install + cmake --build . --config Release --target pip_package ; cp `ls *.whl` ../`ls *.whl | sed "s/py3-none-any/cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}-cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}${{ matrix.cfg.cpcfg }}/"` cd .. shell: bash - uses: xresloader/upload-to-github-release@v1 @@ -57,4 +80,13 @@ jobs: file: "*.whl" overwrite: true tag_name: autotagname-${{ github.sha }} - if: (github.event_name != 'pull_request')&&(github.ref_name == 'master') + if: (github.event_name!='pull_request')&&((github.ref_name=='codac1')||(github.ref_name=='codac2')||(github.ref_name=='codac2_codac4matlab')) + - run: | + pip install --no-deps --no-index *.whl + python -c "import sys; print(sys.version)" ; python examples/tuto/01_getting_started/01_getting_started.py + pip install numpy --prefer-binary + python -m unittest discover codac.tests + #cd build && ctest -C Release -V --output-on-failure + #cd .. + shell: bash + if: (matrix.cfg.cross!=true)&&(github.ref_name!='codac2_codac4matlab')&&(github.event.pull_request.base.ref!='codac2_codac4matlab') diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 3f1c68c3..93e3b198 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,33 +1,42 @@ # Unit tests on: push: - branches: '**' + branches-ignore: + - codac2_codac4matlab tags: '' # Restrict to blank tags pull_request: + branches-ignore: + - codac2_codac4matlab jobs: tests: runs-on: ${{ matrix.cfg.os }} strategy: + fail-fast: false matrix: cfg: - - { os: ubuntu-18.04, gcc_v: 8, py_v_maj: 3, py_v_min: 6, desc: 'Ubuntu 18.04 GCC 8 Python 3.6 tests' } + - { os: ubuntu-24.04, gcc_v: 11, py_v_maj: 3, py_v_min: 10, desc: 'Ubuntu 24.04 GCC 11 Python 3.10 tests' } name: ${{ matrix.cfg.desc }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: true fetch-depth: 0 clean: false - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v5 with: python-version: ${{ matrix.cfg.py_v_maj }}.${{ matrix.cfg.py_v_min }} - run: | + # Installing gcc + sudo apt install build-essential manpages-dev software-properties-common + sudo add-apt-repository ppa:ubuntu-toolchain-r/test + sudo apt-get -q update ; sudo apt-get -y install gcc-${{ matrix.cfg.gcc_v }} g++-${{ matrix.cfg.gcc_v }} || true sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-${{ matrix.cfg.gcc_v }} 100 --slave /usr/bin/g++ g++ /usr/bin/g++-${{ matrix.cfg.gcc_v }} gcc --version g++ --version + pip install --upgrade --force-reinstall setuptools python -c "import sys; print(sys.version)" pip --version @@ -55,7 +64,7 @@ jobs: bash scripts/dependencies/install_ibex.sh # CAPD - bash scripts/dependencies/install_capd.sh + # cancelled on 2023/05/09: bash scripts/dependencies/install_capd.sh # Environment variables export CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH:$HOME/codac/build_install @@ -75,9 +84,9 @@ jobs: # Without synthesis tree # Building lib + tests - cmake -DCMAKE_INSTALL_PREFIX=$HOME/codac/build_install -DCMAKE_PREFIX_PATH=$HOME/ibex-lib/build_install -DWITH_PYTHON=OFF -DBUILD_TESTS=ON -DWITH_TUBE_TREE=OFF -DWITH_CAPD=ON -DTEST_EXAMPLES=ON .. + cmake -DCMAKE_INSTALL_PREFIX=$HOME/codac/build_install -DCMAKE_PREFIX_PATH=$HOME/ibex-lib/build_install -DWITH_PYTHON=OFF -DBUILD_TESTS=ON -DWITH_TUBE_TREE=OFF -DWITH_CAPD=OFF -DTEST_EXAMPLES=ON .. ##-DPYTHON_EXECUTABLE=/usr/bin/python3.5 .. - make + make -j 4 #make doc # todo make install #cd python/python_package @@ -112,7 +121,7 @@ jobs: # With synthesis tree for all created tubes # Building lib + tests - cmake -DCMAKE_INSTALL_PREFIX=$HOME/codac/build_install -DCMAKE_PREFIX_PATH=$HOME/ibex-lib/build_install -DWITH_PYTHON=OFF -DBUILD_TESTS=ON -DWITH_TUBE_TREE=ON -DWITH_CAPD=ON -DTEST_EXAMPLES=ON .. + cmake -DCMAKE_INSTALL_PREFIX=$HOME/codac/build_install -DCMAKE_PREFIX_PATH=$HOME/ibex-lib/build_install -DWITH_PYTHON=OFF -DBUILD_TESTS=ON -DWITH_TUBE_TREE=ON -DWITH_CAPD=OFF -DTEST_EXAMPLES=ON .. ##-DPYTHON_EXECUTABLE=/usr/bin/python3.5 .. make #make doc diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index 5fee6b49..c1190be2 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -1,4 +1,4 @@ -# This file generates .deb (Unix) and .nupkg (Windows) files +# This file generates .deb (Unix) and .nupkg (Windows) packages (and zip for having Codac and IBEX binaries for several Visual Studio versions) on: push: branches: '**' @@ -6,46 +6,56 @@ on: pull_request: jobs: - # This job may be commented if a new release should not be created... deploy: runs-on: ubuntu-latest + if: (github.event_name!='pull_request')&&((github.ref_name=='codac1')||(github.ref_name=='codac2')||(github.ref_name=='codac2_codac4matlab')) steps: - - uses: actions/create-release@v1 + - uses: softprops/action-gh-release@v2 id: create_release env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: draft: true tag_name: autotagname-${{ github.sha }} - release_name: autotagname-${{ github.sha }} - if: (github.event_name != 'pull_request')&&(github.ref_name == 'master') unixmatrix: runs-on: ${{ matrix.cfg.os }} + if: (github.ref_name!='codac2_codac4matlab')&&(github.event.pull_request.base.ref!='codac2_codac4matlab') defaults: run: shell: ${{ matrix.cfg.shell }} strategy: + fail-fast: false matrix: cfg: - - { os: windows-2022, shell: cmd, arch: x64, runtime: vc17, cmake_params: '-G "Visual Studio 17" -A x64', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', cmake_config: '--config Release', cmake_codac_config: '--config Debug', test_config: 'Release/', desc: 'Windows Visual Studio 2022 x64' } - - { os: windows-2022, shell: cmd, arch: x86, runtime: vc17, cmake_params: '-G "Visual Studio 17" -A Win32', choco_flags: '--x86', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', cmake_config: '--config Release', cmake_codac_config: '--config Debug', test_config: 'Release/', desc: 'Windows Visual Studio 2022 x86' } - - { os: windows-2019, shell: cmd, arch: x64, runtime: vc16, cmake_params: '-G "Visual Studio 16" -A x64', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', cmake_config: '--config Release', cmake_codac_config: '--config Debug', test_config: 'Release/', desc: 'Windows Visual Studio 2019 x64' } - - { os: windows-2019, shell: cmd, arch: x86, runtime: vc16, cmake_params: '-G "Visual Studio 16" -A Win32', choco_flags: '--x86', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', cmake_config: '--config Release', cmake_codac_config: '--config Debug', test_config: 'Release/', desc: 'Windows Visual Studio 2019 x86' } - - { os: windows-2019, shell: cmd, arch: x64, runtime: vc15, cmake_params: '-G "Visual Studio 16" -T v141 -A x64', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', cmake_config: '--config Release', cmake_codac_config: '--config Debug', test_config: 'Release/', desc: 'Windows Visual Studio 2017 x64' } - - { os: windows-2019, shell: cmd, arch: x86, runtime: vc15, cmake_params: '-G "Visual Studio 16" -T v141 -A Win32', choco_flags: '--x86', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', cmake_config: '--config Release', cmake_codac_config: '--config Debug', test_config: 'Release/', desc: 'Windows Visual Studio 2017 x86' } - - { os: windows-2019, shell: cmd, arch: x64, runtime: mingw8, cmake_params: '-G "MSYS Makefiles"', cmake_flags: '-fPIC', desc: 'Windows MinGW 8.1.0 x64' } - - { os: windows-2019, shell: cmd, arch: x86, runtime: mingw8, cmake_params: '-G "MSYS Makefiles"', cmake_flags: '-fPIC', choco_flags: '--x86', desc: 'Windows MinGW 8.1.0 x86' } - - { os: windows-2019, shell: cmd, arch: x86, runtime: mingw7, cmake_params: '-G "MSYS Makefiles"', cmake_flags: '-fPIC', choco_flags: '--x86', desc: 'Windows Qt 5.12.6 MinGW 7.3.0 x86' } - - { os: ubuntu-22.04, shell: bash, arch: amd64, runtime: jammy, cmake_flags: '-fPIC', desc: 'Ubuntu 22.04' } - - { os: ubuntu-20.04, shell: bash, arch: amd64, runtime: focal, cmake_flags: '-fPIC', desc: 'Ubuntu 20.04' } - - { os: ubuntu-18.04, shell: bash, arch: amd64, runtime: bionic, cmake_flags: '-fPIC', desc: 'Ubuntu 18.04' } - - { os: macos-12, shell: bash, arch: x64, runtime: monterey, cmake_flags: '-fPIC', desc: 'macOS Monterey' } - - { os: macos-11, shell: bash, arch: x64, runtime: bigsur, cmake_flags: '-fPIC', desc: 'macOS Big Sur' } - - { os: macos-10.15, shell: bash, arch: x64, runtime: catalina, cmake_flags: '-fPIC', desc: 'macOS Catalina' } + - { os: windows-2022, shell: cmd, arch: x64, bitness: 64, runtime: vc17, cmake_params: '-G "Visual Studio 17" -T v143 -A x64', cmake_flags: ' /MP4 /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2022 x64' } + - { os: windows-2022, shell: cmd, arch: x86, bitness: 32, runtime: vc17, cmake_params: '-G "Visual Studio 17" -T v143 -A Win32', cmake_flags: ' /MP4 /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', choco_flags: '--x86', desc: 'Windows Visual Studio 2022 x86' } + - { os: windows-2022, shell: cmd, arch: x64, bitness: 64, runtime: vc16, cmake_params: '-G "Visual Studio 17" -T v142 -A x64', cmake_flags: ' /MP4 /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2019 x64' } + - { os: windows-2022, shell: cmd, arch: x86, bitness: 32, runtime: vc16, cmake_params: '-G "Visual Studio 17" -T v142 -A Win32', cmake_flags: ' /MP4 /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', choco_flags: '--x86', desc: 'Windows Visual Studio 2019 x86' } + - { os: windows-2019, shell: cmd, arch: x64, bitness: 64, runtime: vc15, cmake_params: '-G "Visual Studio 16" -T v141 -A x64', cmake_flags: ' /MP4 /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2017 x64' } + - { os: windows-2019, shell: cmd, arch: x86, bitness: 32, runtime: vc15, cmake_params: '-G "Visual Studio 16" -T v141 -A Win32', cmake_flags: ' /MP4 /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', choco_flags: '--x86', desc: 'Windows Visual Studio 2017 x86' } + - { os: windows-2022, shell: cmd, arch: x64, bitness: 64, runtime: mingw13, cmake_params: '-G "MinGW Makefiles"', cmake_flags: '-fPIC', desc: 'Windows MinGW 13.2.0 x64' } + - { os: windows-2022, shell: cmd, arch: x86, bitness: 32, runtime: mingw13, cmake_params: '-G "MinGW Makefiles"', cmake_flags: '-fPIC', choco_flags: '--x86', desc: 'Windows MinGW 13.2.0 x86' } + - { os: windows-2022, shell: cmd, arch: x64, bitness: 64, runtime: mingw12, cmake_params: '-G "MinGW Makefiles"', cmake_flags: '-fPIC', desc: 'Windows MinGW 12.2.0 x64' } + - { os: windows-2022, shell: cmd, arch: x86, bitness: 32, runtime: mingw12, cmake_params: '-G "MinGW Makefiles"', cmake_flags: '-fPIC', choco_flags: '--x86', desc: 'Windows MinGW 12.2.0 x86' } + - { os: windows-2022, shell: cmd, arch: x64, bitness: 64, runtime: mingw11, cmake_params: '-G "MinGW Makefiles"', cmake_flags: '-fPIC', desc: 'Windows MinGW 11.2.0 x64' } + - { os: windows-2022, shell: cmd, arch: x86, bitness: 32, runtime: mingw11, cmake_params: '-G "MinGW Makefiles"', cmake_flags: '-fPIC', choco_flags: '--x86', desc: 'Windows MinGW 11.2.0 x86' } + - { os: windows-2019, shell: cmd, arch: x64, bitness: 64, runtime: mingw8, cmake_params: '-G "MinGW Makefiles"', cmake_flags: '-fPIC', desc: 'Windows MinGW 8.1.0 x64' } + - { os: windows-2019, shell: cmd, arch: x86, bitness: 32, runtime: mingw8, cmake_params: '-G "MinGW Makefiles"', cmake_flags: '-fPIC', choco_flags: '--x86', desc: 'Windows MinGW 8.1.0 x86' } + - { os: windows-2019, shell: cmd, arch: x64, bitness: 64, runtime: mingw7, cmake_params: '-G "MinGW Makefiles"', cmake_flags: '-fPIC', desc: 'Windows MinGW 7.3.0 x64' } + - { os: windows-2019, shell: cmd, arch: x86, bitness: 32, runtime: mingw7, cmake_params: '-G "MinGW Makefiles"', cmake_flags: '-fPIC', choco_flags: '--x86', desc: 'Windows MinGW 7.3.0 x86' } + - { os: ubuntu-24.04, shell: bash, arch: amd64, bitness: 64, runtime: noble, cmake_flags: '-fPIC', deb: true, desc: 'Ubuntu 24.04 amd64' } + - { os: ubuntu-22.04, shell: bash, arch: amd64, bitness: 64, runtime: jammy, cmake_flags: '-fPIC', deb: true, desc: 'Ubuntu 22.04 amd64' } + - { os: ubuntu-20.04, shell: bash, arch: amd64, bitness: 64, runtime: focal, cmake_flags: '-fPIC', deb: true, desc: 'Ubuntu 20.04 amd64' } + - { os: macos-15, shell: bash, arch: arm64, bitness: 64, runtime: sequoia, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=arm64', cmake_flags: '-fPIC', desc: 'macOS Sequoia arm64' } + - { os: macos-15, shell: bash, arch: x86_64, bitness: 64, runtime: sequoia, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=x86_64', cmake_flags: '-fPIC', cross: true, desc: 'macOS Sequoia x86_64 (cross)' } + - { os: macos-14, shell: bash, arch: arm64, bitness: 64, runtime: sonoma, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=arm64', cmake_flags: '-fPIC', desc: 'macOS Sonoma arm64' } + - { os: macos-14, shell: bash, arch: x86_64, bitness: 64, runtime: sonoma, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=x86_64', cmake_flags: '-fPIC', cross: true, desc: 'macOS Sonoma x86_64 (cross)' } + - { os: macos-13, shell: bash, arch: arm64, bitness: 64, runtime: ventura, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=arm64', cmake_flags: '-fPIC', cross: true, desc: 'macOS Ventura arm64 (cross)' } + - { os: macos-13, shell: bash, arch: x86_64, bitness: 64, runtime: ventura, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=x86_64', cmake_flags: '-fPIC', desc: 'macOS Ventura x86_64' } name: ${{ matrix.cfg.desc }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: true fetch-depth: 0 @@ -61,104 +71,138 @@ jobs: echo "CHOCO_PACKAGE_REV=" >> $GITHUB_ENV echo "VERBOSE=1" >> $GITHUB_ENV shell: bash - - run: if [ -z "$CHOCO_PACKAGE_REV" ]; then echo "PACKAGE_VERSION=$SOFTWARE_VERSION" >> $GITHUB_ENV ; else echo "PACKAGE_VERSION=$SOFTWARE_VERSION.$CHOCO_PACKAGE_REV" >> $GITHUB_ENV ; fi + - run: if [ -z "$CHOCO_PACKAGE_REV" ]; then echo "PACKAGE_VERSION=${SOFTWARE_VERSION/.dev*/-dev.$(date +%Y%m%d)}" >> $GITHUB_ENV ; else echo "PACKAGE_VERSION=$SOFTWARE_VERSION.$CHOCO_PACKAGE_REV" >> $GITHUB_ENV ; fi shell: bash if: runner.os=='Windows' - run: echo "PACKAGE_VERSION=$SOFTWARE_VERSION-${DEBIAN_PACKAGE_REV}${{ matrix.cfg.runtime }}$PACKAGE_REV" >> $GITHUB_ENV shell: bash - if: runner.os=='Linux' -# - run: | -# choco install -y -r --no-progress wget -# wget http://download.qt.io/archive/qt/5.12/5.12.6/qt-opensource-windows-x86-5.12.6.exe --no-check-certificate -nv -# move /Y qt-opensource-windows-x86-5.12.6.exe %SystemDrive%\ -# wget http://www.ensta-bretagne.fr/lebars/Share/qt-installer-5.12.6-mingw73_32.qs --no-check-certificate -nv -# move /Y qt-installer-5.12.6-mingw73_32.qs %SystemDrive%\ -# netsh advfirewall set allprofiles state on -# netsh advfirewall firewall add rule name="Qt offline installer" dir=out action=block program="%SystemDrive%\qt-opensource-windows-x86-5.12.6.exe" enable=yes -# rem Take several min... -# %SystemDrive%\qt-opensource-windows-x86-5.12.6.exe --script %SystemDrive%\qt-installer-5.12.6-mingw73_32.qs -# netsh advfirewall firewall del rule name="Qt offline installer" -# netsh advfirewall set allprofiles state off -# del /f /q %SystemDrive%\qt-opensource-windows-x86-5.12.6.exe -# echo C:\Qt\Qt5.12.6\5.12.6\mingw73_32\bin;C:\Qt\Qt5.12.6\Tools\mingw730_32\bin>>%GITHUB_PATH% -# if: (runner.os=='Windows')&&(matrix.cfg.runtime=='mingw7') + if: matrix.cfg.deb==true + - run: | + choco install -y -r --no-progress checksum wget zip + rem Workaround to try solve some random package download failures... + wget https://www.ensta-bretagne.fr/packages/choco/winflexbison.2.4.9.20170215.nupkg --no-check-certificate -nv + wget https://www.ensta-bretagne.fr/packages/choco/patch.2.5.9.nupkg --no-check-certificate -nv + choco install -y -r --no-progress -s . winflexbison patch ${{ matrix.cfg.choco_flags }} + del /f /q winflexbison.2.4.9.20170215.nupkg patch.2.5.9.nupkg + wget http://www.ensta-bretagne.fr/lebars/Share/cmake_extra_tools.zip --no-check-certificate -nv + 7z x cmake_extra_tools.zip -o"%SystemDrive%" -y + del /f /q cmake_extra_tools.zip + wget https://gist.github.com/lebarsfa/237841f9e5dad55ef192713b3b1b2f16/raw/04d77ced3457346c55f183ca12a10dbcb850e6d5/refreshenv.bashrc --no-check-certificate -nv + move /y refreshenv.bashrc %USERPROFILE% + if: runner.os=='Windows' - run: | choco install -y -r --no-progress mingw --version=7.3.0 --force ${{ matrix.cfg.choco_flags }} - echo C:\ProgramData\chocolatey\lib\mingw\tools\install\mingw32\bin>>%GITHUB_PATH% - choco install -y -r --no-progress ibex --version=2.8.9.20220413 --ignore-dependencies ${{ matrix.cfg.choco_flags }} - if: (runner.os=='Windows')&&(matrix.cfg.runtime=='mingw7')&&(matrix.cfg.arch=='x86') + %SystemDrive%\cmake_extra_tools\pathman /as C:\ProgramData\chocolatey\lib\mingw\tools\install\mingw${{ matrix.cfg.bitness }}\bin & cd. & rem Non-zero exit code...? + echo export BASHMINGWPATH=/c/ProgramData/chocolatey/lib/mingw/tools/install/mingw${{ matrix.cfg.bitness }}/bin>>%USERPROFILE%\.bashrc + if: (matrix.cfg.runtime=='mingw7') - run: | choco install -y -r --no-progress mingw --version=8.1.0 --force ${{ matrix.cfg.choco_flags }} - echo C:\ProgramData\chocolatey\lib\mingw\tools\install\mingw32\bin>>%GITHUB_PATH% - if: (runner.os=='Windows')&&(matrix.cfg.runtime=='mingw8')&&(matrix.cfg.arch=='x86') + %SystemDrive%\cmake_extra_tools\pathman /as C:\ProgramData\chocolatey\lib\mingw\tools\install\mingw${{ matrix.cfg.bitness }}\bin & cd. & rem Non-zero exit code...? + echo export BASHMINGWPATH=/c/ProgramData/chocolatey/lib/mingw/tools/install/mingw${{ matrix.cfg.bitness }}/bin>>%USERPROFILE%\.bashrc + if: (matrix.cfg.runtime=='mingw8') + - run: | + choco install -y -r --no-progress mingw --version=11.2.0.07112021 --force ${{ matrix.cfg.choco_flags }} + %SystemDrive%\cmake_extra_tools\pathman /as C:\ProgramData\chocolatey\lib\mingw\tools\install\mingw${{ matrix.cfg.bitness }}\bin & cd. & rem Non-zero exit code...? + echo export BASHMINGWPATH=/c/ProgramData/chocolatey/lib/mingw/tools/install/mingw${{ matrix.cfg.bitness }}/bin>>%USERPROFILE%\.bashrc + if: (matrix.cfg.runtime=='mingw11') + - run: | + choco install -y -r --no-progress mingw --version=12.2.0.03042023 --force ${{ matrix.cfg.choco_flags }} + %SystemDrive%\cmake_extra_tools\pathman /as C:\ProgramData\chocolatey\lib\mingw\tools\install\mingw${{ matrix.cfg.bitness }}\bin & cd. & rem Non-zero exit code...? + echo export BASHMINGWPATH=/c/ProgramData/chocolatey/lib/mingw/tools/install/mingw${{ matrix.cfg.bitness }}/bin>>%USERPROFILE%\.bashrc + if: (matrix.cfg.runtime=='mingw12') - run: | - choco install -y -r --no-progress checksum wget winflexbison make patch zip ${{ matrix.cfg.choco_flags }} - choco install -y -r --no-progress eigen --version=3.4.0 ${{ matrix.cfg.choco_flags }} - choco install -y -r --no-progress ibex --version=2.8.9.20220413 ${{ matrix.cfg.choco_flags }} + choco install -y -r --no-progress mingw --version=13.2.0 --force ${{ matrix.cfg.choco_flags }} + %SystemDrive%\cmake_extra_tools\pathman /as C:\ProgramData\mingw64\mingw${{ matrix.cfg.bitness }}\bin & cd. & rem Non-zero exit code...? + echo export BASHMINGWPATH=/c/ProgramData/mingw64/mingw${{ matrix.cfg.bitness }}/bin>>%USERPROFILE%\.bashrc + if: (matrix.cfg.runtime=='mingw13') + - run: | + choco install -y -r --no-progress eigen --version=3.4.0.20240224 ${{ matrix.cfg.choco_flags }} + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241117/ibex.2.8.9.20241117.nupkg --no-check-certificate -nv + choco install -y -r --no-progress --ignore-dependencies -s . ibex --version=2.8.9.20241117 ${{ matrix.cfg.choco_flags }} --params "'/url:https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241117/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip'" + del /f /q ibex.2.8.9.20241117.nupkg if: runner.os=='Windows' -# - run: sudo apt-get -q update ; sudo apt-get -y install flex bison libeigen3-dev || true -# shell: bash -# if: runner.os=='Linux' - run: | - sudo sh -c 'echo "deb [trusted=yes] https://www.ensta-bretagne.fr/packages/`lsb_release --id -s | tr [:upper:] [:lower:]`/`lsb_release -cs` ./" > /etc/apt/sources.list.d/ensta-bretagne.list' - sudo apt-get -q update ; sudo apt-get -y install libibex-dev libeigen3-dev dpkg-dev || true + # Replace these 2 lines by the next ones to test a specific binary package of IBEX. + #sudo sh -c 'echo "deb [trusted=yes] https://packages.ensta-bretagne.fr/$(if [ -z "$(. /etc/os-release && echo $UBUNTU_CODENAME)" ]; then echo debian/$(. /etc/os-release && echo $VERSION_CODENAME); else echo ubuntu/$(. /etc/os-release && echo $UBUNTU_CODENAME); fi) ./" > /etc/apt/sources.list.d/ensta-bretagne.list' + #sudo apt-get -q update ; sudo apt-get -y install libibex-dev libeigen3-dev dpkg-dev || true + sudo apt-get -q update ; sudo apt-get -y install libeigen3-dev dpkg-dev || true + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241117/libibex-dev-2.8.9.20241117-0${{ matrix.cfg.runtime }}0_$(dpkg --print-architecture).deb --no-check-certificate -nv + sudo dpkg -i libibex-dev-2.8.9.20241117-0${{ matrix.cfg.runtime }}0_$(dpkg --print-architecture).deb + rm -Rf libibex-dev-2.8.9.20241117-0${{ matrix.cfg.runtime }}0_$(dpkg --print-architecture).deb shell: bash - if: runner.os=='Linux' - - run: brew install eigen + if: matrix.cfg.deb==true + - run: | + brew install eigen + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241117/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip --no-check-certificate -nv + unzip -q ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip + rm -Rf ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip + sudo cp -Rf ibex/* /usr/local/ if: runner.os=='macOS' - - run: git clone -b actions https://github.com/lebarsfa/ibex-lib.git ; cd ibex-lib ; mkdir build ; cd build ; cmake -E env CXXFLAGS="${{ matrix.cfg.cmake_flags }}" CFLAGS="${{ matrix.cfg.cmake_flags }}" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_INSTALL_PREFIX="../../ibex" .. ; cmake --build . ${{ matrix.cfg.cmake_config }} --target install ; cd ../.. - shell: bash - if: ((runner.os=='Windows')&&(matrix.cfg.runtime!='mingw8'))||((runner.os!='Windows')&&(runner.os!='Linux')) +# - run: git clone --depth 1 -b master https://github.com/lebarsfa/ibex-lib.git ; cd ibex-lib ; mkdir build ; cd build ; cmake -E env CXXFLAGS="${{ matrix.cfg.cmake_flags }}" CFLAGS="${{ matrix.cfg.cmake_flags }}" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_INSTALL_PREFIX="../../ibex" .. ; cmake --build . --config Release --target install ; cd ../.. +# shell: bash - run: | + if [ ${{ runner.os }} = Windows ]; then source ~/refreshenv.bashrc ; refreshenv ; export PATH=$BASHMINGWPATH:$PATH ; fi mkdir build ; cd build - cmake -E env CXXFLAGS="${{ matrix.cfg.cmake_flags }}" CFLAGS="${{ matrix.cfg.cmake_flags }}" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_PREFIX_PATH="ibex" -D CMAKE_INSTALL_PREFIX="../codac" .. - cmake --build . ${{ matrix.cfg.cmake_codac_config }} --target install + cmake -E env CXXFLAGS="${{ matrix.cfg.cmake_flags }}" CFLAGS="${{ matrix.cfg.cmake_flags }}" cmake ${{ matrix.cfg.cmake_params }} -D BUILD_TESTS=ON -D CMAKE_INSTALL_PREFIX="../codac" .. + cmake --build . -j 4 --config Debug --target install cd .. + sed_param=s/PATH_SUFFIXES\ /PATHS\ \$\{CMAKE_CURRENT_LIST_FILE\}\\/..\\/..\\/..\\/..\\/\ PATH_SUFFIXES\ / + if [ ${{ runner.os }} = Windows ]; then sed -i "$sed_param" codac/share/codac/cmake/*.cmake ; fi + zip -q -r codac_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip codac shell: bash - run: | + if [ ${{ runner.os }} = Windows ]; then source ~/refreshenv.bashrc ; refreshenv ; export PATH=$BASHMINGWPATH:$PATH ; fi mkdir -p codac_standalone/example ; cd codac_standalone - wget https://community.chocolatey.org/api/v2/package/eigen/3.4.0 --no-check-certificate -nv ; unzip -q 3.4.0 -d eigen ; rm -Rf 3.4.0 eigen/*.xml eigen/*.nuspec eigen/_* eigen/package eigen/tools - cp -Rf ../ibex . ; cp -Rf ../codac . ; cp -Rf ../tests/test_codac/* ./example/ ; cd .. ; zip -q -r codac_standalone_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip codac_standalone ; cd codac_standalone/example + wget https://community.chocolatey.org/api/v2/package/eigen/3.4.0.20240224 --no-check-certificate -nv ; unzip -q 3.4.0.20240224 -d eigen ; rm -Rf 3.4.0.20240224 eigen/*.xml eigen/*.nuspec eigen/_* eigen/package eigen/tools + if [ ${{ runner.os }} = Windows ]; then cp -Rf /C/ProgramData/chocolatey/lib/ibex . ; rm -Rf ibex/tools ibex/ibex.* + elif [ ${{ matrix.cfg.deb }} = true ]; then mkdir -p ibex/include ; mkdir -p ibex/lib ; mkdir -p ibex/share ; mkdir -p ibex/bin ; cp -Rf /usr/include/ibex* ibex/include/ ; cp -Rf /usr/lib/*ibex* ibex/lib/ ; cp -Rf /usr/share/*ibex* ibex/share/ ; cp -Rf /usr/share/pkgconfig ibex/share/ ; cp -Rf /usr/bin/ibex* ibex/bin/ + else cp -Rf ../ibex . + fi + cp -Rf ../codac . ; cp -Rf ../tests/test_codac/* ./example/ ; cd .. ; zip -q -r codac_standalone_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip codac_standalone + shell: bash + - run: | + if [ ${{ runner.os }} = Windows ]; then source ~/refreshenv.bashrc ; refreshenv ; export PATH=$BASHMINGWPATH:$PATH ; fi + cd codac_standalone/example cmake ${{ matrix.cfg.cmake_params }} . - cmake --build . ${{ matrix.cfg.cmake_config }} + cmake --build . --config Release + file ./${{ matrix.cfg.test_config }}my_project ./${{ matrix.cfg.test_config }}my_project cd ../.. shell: bash - if: ((runner.os=='Windows')&&(matrix.cfg.runtime!='mingw8'))||((runner.os!='Windows')&&(runner.os!='Linux')) + if: (matrix.cfg.cross!=true) - run: | + source ~/refreshenv.bashrc ; refreshenv ; export PATH=$BASHMINGWPATH:$PATH cd packages/choco - cp -Rf ../../codac/* codac sed_param=s/\1\<\\/version\>/\${PACKAGE_VERSION}\<\\/version\>/ sed -i "$sed_param" codac/codac.nuspec - sed_param=s/PATH_SUFFIXES\ /PATHS\ \$\{CMAKE_CURRENT_LIST_FILE\}\\/..\\/..\\/..\\/..\\/\ PATH_SUFFIXES\ / - sed -i "$sed_param" codac/share/codac/cmake/*.cmake + sed_param=s/download\\/v1\\/codac/download\\/v${PACKAGE_VERSION}\\/codac/ + sed -i "$sed_param" codac/codac.nuspec + sed -i "$sed_param" codac/tools/chocolateyinstall.ps1 + sed_param=s/\\$CMakePackageVer\ =\ \"1\"/\\$CMakePackageVer\ =\ \"${SOFTWARE_VERSION}\"/ + sed -i "$sed_param" codac/tools/sharedVars.ps1 mv -f codac codac.$PACKAGE_VERSION cd codac.$PACKAGE_VERSION choco pack mv -f codac.$PACKAGE_VERSION.nupkg ../../../ cd ../../.. checksum -f=codac.$PACKAGE_VERSION.nupkg -t=sha256 - choco source add -n=current-directory -s . --priority=100 - choco install -y -r --no-progress codac --version=$PACKAGE_VERSION ${{ matrix.cfg.choco_flags }} - mv -f codac.$PACKAGE_VERSION.nupkg codac.$PACKAGE_VERSION${{ matrix.cfg.choco_flags }}.nupkg + choco install -y -r --no-progress --ignore-dependencies -s . codac --params "'/url:./codac_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip'" + if [ ${{ matrix.cfg.runtime }} != mingw11 ] || [ ${{ matrix.cfg.arch }} != x64 ]; then rm -Rf codac.$PACKAGE_VERSION.nupkg ; fi # To avoid upload conflicts of the same file... + checksum -f=codac_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip -t=sha256 shell: bash - if: (runner.os=='Windows')&&(matrix.cfg.runtime=='mingw8') + if: runner.os=='Windows' - run: | cd packages chmod +x ./genlibcodac-dev.sh - ./genlibcodac-dev.sh ubuntu ${{ matrix.cfg.runtime }} ${{ matrix.cfg.arch }} $SOFTWARE_VERSION $DEBIAN_PACKAGE_REV $PACKAGE_REV + ./genlibcodac-dev.sh $(if [ -z "$(. /etc/os-release && echo $UBUNTU_CODENAME)" ]; then echo debian; else echo ubuntu; fi) ${{ matrix.cfg.runtime }} $(dpkg --print-architecture) $SOFTWARE_VERSION $DEBIAN_PACKAGE_REV $PACKAGE_REV cd .. - sudo dpkg -i libcodac-dev-$PACKAGE_VERSION\_${{ matrix.cfg.arch }}.deb + sudo dpkg -i libcodac-dev-$PACKAGE_VERSION\_$(dpkg --print-architecture).deb shell: bash - if: runner.os=='Linux' + if: matrix.cfg.deb==true - run: | - rm -Rf codac - cd tests/test_codac - cmake ${{ matrix.cfg.cmake_params }} . - cmake --build . ${{ matrix.cfg.cmake_config }} - ./${{ matrix.cfg.test_config }}my_project + sudo cp -Rf codac/* /usr/local/ shell: bash - if: ((runner.os=='Windows')&&(matrix.cfg.runtime=='mingw8'))||(runner.os=='Linux') + if: runner.os=='macOS' - uses: xresloader/upload-to-github-release@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -166,4 +210,21 @@ jobs: file: "*.zip;*.nupkg;*.deb" overwrite: true tag_name: autotagname-${{ github.sha }} - if: (github.event_name != 'pull_request')&&(github.ref_name == 'master') + if: (github.event_name!='pull_request')&&((github.ref_name=='codac1')||(github.ref_name=='codac2')||(github.ref_name=='codac2_codac4matlab')) + - run: | + if [ ${{ runner.os }} = Windows ]; then source ~/refreshenv.bashrc ; refreshenv ; export PATH=$BASHMINGWPATH:$PATH ; fi + cd build && ctest -C Debug -V --output-on-failure + cd .. + shell: bash + if: (matrix.cfg.cross!=true) + - run: | + if [ ${{ runner.os }} = Windows ]; then source ~/refreshenv.bashrc ; refreshenv ; export PATH=$BASHMINGWPATH:$PATH ; fi + rm -Rf codac + cd tests/test_codac + cmake ${{ matrix.cfg.cmake_params }} . + cmake --build . --config Release + file ./${{ matrix.cfg.test_config }}my_project + ./${{ matrix.cfg.test_config }}my_project + cd ../.. + shell: bash + if: (matrix.cfg.cross!=true) diff --git a/.github/workflows/vcmatrix.yml b/.github/workflows/vcmatrix.yml index 5830b2af..ae9ee2de 100644 --- a/.github/workflows/vcmatrix.yml +++ b/.github/workflows/vcmatrix.yml @@ -1,8 +1,10 @@ # This file generates Python wheels for Windows -# (and zip for having Codac and IBEX binaries for several Visual Studio versions) on: push: - branches: 'master' + branches: + - codac1 + - codac2 + - codac2_codac4matlab tags: '' # Restrict to blank tags pull_request: @@ -13,14 +15,21 @@ jobs: run: shell: ${{ matrix.cfg.shell }} strategy: + fail-fast: false matrix: cfg: - - { os: windows-2019, shell: cmd, arch: x86, runtime: vc16, cmake_params: '-G "Visual Studio 16" -A Win32', choco_flags: '--x86', cpcfg: '-win32', py_v_maj: 3, py_v_min: 10, desc: 'Windows Visual Studio 2019 x86 Python 3.10' } - - { os: windows-2019, shell: cmd, arch: x86, runtime: vc16, cmake_params: '-G "Visual Studio 16" -A Win32', choco_flags: '--x86', cpcfg: '-win32', py_v_maj: 3, py_v_min: 9, desc: 'Windows Visual Studio 2019 x86 Python 3.9' } - - { os: windows-2019, shell: cmd, arch: x86, runtime: vc16, cmake_params: '-G "Visual Studio 16" -A Win32', choco_flags: '--x86', cpcfg: '-win32', py_v_maj: 3, py_v_min: 8, desc: 'Windows Visual Studio 2019 x86 Python 3.8' } - - { os: windows-2019, shell: cmd, arch: x64, runtime: vc16, cmake_params: '-G "Visual Studio 16" -A x64', cpcfg: '-win_amd64', py_v_maj: 3, py_v_min: 10, desc: 'Windows Visual Studio 2019 x64 Python 3.10' } - - { os: windows-2019, shell: cmd, arch: x64, runtime: vc16, cmake_params: '-G "Visual Studio 16" -A x64', cpcfg: '-win_amd64', py_v_maj: 3, py_v_min: 9, desc: 'Windows Visual Studio 2019 x64 Python 3.9' } - - { os: windows-2019, shell: cmd, arch: x64, runtime: vc16, cmake_params: '-G "Visual Studio 16" -A x64', cpcfg: '-win_amd64', py_v_maj: 3, py_v_min: 8, desc: 'Windows Visual Studio 2019 x64 Python 3.8' } + - { os: windows-2022, shell: cmd, arch: x86, runtime: vc17, cmake_params: '-G "Visual Studio 17" -T v143 -A Win32', choco_flags: '--x86', cpcfg: '-win32', py_v_maj: 3, py_v_min: 13, desc: 'Windows Visual Studio 2022 x86 Python 3.13' } + - { os: windows-2022, shell: cmd, arch: x86, runtime: vc17, cmake_params: '-G "Visual Studio 17" -T v143 -A Win32', choco_flags: '--x86', cpcfg: '-win32', py_v_maj: 3, py_v_min: 12, desc: 'Windows Visual Studio 2022 x86 Python 3.12' } + - { os: windows-2022, shell: cmd, arch: x86, runtime: vc17, cmake_params: '-G "Visual Studio 17" -T v143 -A Win32', choco_flags: '--x86', cpcfg: '-win32', py_v_maj: 3, py_v_min: 11, desc: 'Windows Visual Studio 2022 x86 Python 3.11' } + - { os: windows-2022, shell: cmd, arch: x86, runtime: vc16, cmake_params: '-G "Visual Studio 17" -T v142 -A Win32', choco_flags: '--x86', cpcfg: '-win32', py_v_maj: 3, py_v_min: 10, desc: 'Windows Visual Studio 2019 x86 Python 3.10' } + - { os: windows-2022, shell: cmd, arch: x86, runtime: vc16, cmake_params: '-G "Visual Studio 17" -T v142 -A Win32', choco_flags: '--x86', cpcfg: '-win32', py_v_maj: 3, py_v_min: 9, desc: 'Windows Visual Studio 2019 x86 Python 3.9' } + - { os: windows-2022, shell: cmd, arch: x86, runtime: vc16, cmake_params: '-G "Visual Studio 17" -T v142 -A Win32', choco_flags: '--x86', cpcfg: '-win32', py_v_maj: 3, py_v_min: 8, desc: 'Windows Visual Studio 2019 x86 Python 3.8' } + - { os: windows-2022, shell: cmd, arch: x64, runtime: vc17, cmake_params: '-G "Visual Studio 17" -T v143 -A x64', cpcfg: '-win_amd64', py_v_maj: 3, py_v_min: 13, desc: 'Windows Visual Studio 2022 x64 Python 3.13' } + - { os: windows-2022, shell: cmd, arch: x64, runtime: vc17, cmake_params: '-G "Visual Studio 17" -T v143 -A x64', cpcfg: '-win_amd64', py_v_maj: 3, py_v_min: 12, desc: 'Windows Visual Studio 2022 x64 Python 3.12' } + - { os: windows-2022, shell: cmd, arch: x64, runtime: vc17, cmake_params: '-G "Visual Studio 17" -T v143 -A x64', cpcfg: '-win_amd64', py_v_maj: 3, py_v_min: 11, desc: 'Windows Visual Studio 2022 x64 Python 3.11' } + - { os: windows-2022, shell: cmd, arch: x64, runtime: vc16, cmake_params: '-G "Visual Studio 17" -T v142 -A x64', cpcfg: '-win_amd64', py_v_maj: 3, py_v_min: 10, desc: 'Windows Visual Studio 2019 x64 Python 3.10' } + - { os: windows-2022, shell: cmd, arch: x64, runtime: vc16, cmake_params: '-G "Visual Studio 17" -T v142 -A x64', cpcfg: '-win_amd64', py_v_maj: 3, py_v_min: 9, desc: 'Windows Visual Studio 2019 x64 Python 3.9' } + - { os: windows-2022, shell: cmd, arch: x64, runtime: vc16, cmake_params: '-G "Visual Studio 17" -T v142 -A x64', cpcfg: '-win_amd64', py_v_maj: 3, py_v_min: 8, desc: 'Windows Visual Studio 2019 x64 Python 3.8' } # Should be Visual Studio 2015 for Python 3.5-3.7, but need Visual Studio 2017 for C++17 compatibility...? - { os: windows-2019, shell: cmd, arch: x86, runtime: vc15, cmake_params: '-G "Visual Studio 16" -T v141 -A Win32', choco_flags: '--x86', cpcfg: 'm-win32', py_v_maj: 3, py_v_min: 7, desc: 'Windows Visual Studio 2017 x86 Python 3.7' } - { os: windows-2019, shell: cmd, arch: x86, runtime: vc15, cmake_params: '-G "Visual Studio 16" -T v141 -A Win32', choco_flags: '--x86', cpcfg: 'm-win32', py_v_maj: 3, py_v_min: 6, desc: 'Windows Visual Studio 2017 x86 Python 3.6' } @@ -28,12 +37,12 @@ jobs: - { os: windows-2019, shell: cmd, arch: x64, runtime: vc15, cmake_params: '-G "Visual Studio 16" -T v141 -A x64', cpcfg: 'm-win_amd64', py_v_maj: 3, py_v_min: 6, desc: 'Windows Visual Studio 2017 x64 Python 3.6' } name: ${{ matrix.cfg.desc }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: true fetch-depth: 0 clean: false - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v5 with: python-version: ${{ matrix.cfg.py_v_maj }}.${{ matrix.cfg.py_v_min }} architecture: ${{ matrix.cfg.arch }} @@ -42,25 +51,26 @@ jobs: python -c "import sys; print(sys.version)" echo ${{ matrix.cfg.py_v_maj }}.${{ matrix.cfg.py_v_min }} if: matrix.cfg.py_v_maj!='' + - run: echo "VERBOSE=1" >> $GITHUB_ENV + shell: bash - run: | (New-Object System.Net.WebClient).DownloadFile("http://www.ensta-bretagne.fr/lebars/Share/windows_extra_tools.zip", "C:\Windows\Temp\windows_extra_tools.zip") 7z x C:\Windows\Temp\windows_extra_tools.zip -o"C:\Windows" -y shell: pwsh if: runner.os=='Windows' - - run: choco install -y -r --no-progress winflexbison patch - if: startsWith(matrix.cfg.runtime, 'vc') - - run: choco install -y -r --no-progress eigen --version=3.4.0 ${{ matrix.cfg.choco_flags }} - shell: cmd + - run: choco install -y -r --no-progress eigen --version=3.4.0.20240224 ${{ matrix.cfg.choco_flags }} if: runner.os=='Windows' - - run: choco install -y -r --no-progress doxygen.install graphviz & python -m pip install --upgrade pip & pip install --upgrade wheel setuptools & git clone -b v3.1.1 https://github.com/sphinx-doc/sphinx & cd sphinx & pip install . & pip install --upgrade breathe sphinx-issues sphinx-tabs sphinx_rtd_theme sphinx-reredirects + - run: wget https://packages.ensta-bretagne.fr/choco/doxygen.install.1.9.6.nupkg --no-check-certificate -nv & choco upgrade -y -r --no-progress -s . doxygen.install --version=1.9.6 & del /f /q doxygen.install.1.9.6.nupkg & choco install -y -r --no-progress graphviz & python -m pip install --upgrade pip & pip install --upgrade wheel setuptools sphinx breathe sphinx-issues sphinx-tabs sphinx_rtd_theme sphinx-reredirects if: runner.os=='Windows' - - run: git clone -b actions https://github.com/lebarsfa/ibex-lib.git ; cd ibex-lib ; mkdir build ; cd build ; cmake -E env CXXFLAGS=" /wd4267 /wd4244 /wd4305 /wd4996" CFLAGS=" /wd4267 /wd4244 /wd4305 /wd4996" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_INSTALL_PREFIX="../../ibex" .. ; cmake --build . --config Release --target install ; cd ../.. - shell: bash + - run: | + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241117/ibex.2.8.9.20241117.nupkg --no-check-certificate -nv + choco install -y -r --no-progress --ignore-dependencies -s . ibex --version=2.8.9.20241117 ${{ matrix.cfg.choco_flags }} --params "'/url:https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241117/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip'" + del /f /q ibex.2.8.9.20241117.nupkg - run: | mkdir build ; cd build - cmake -E env CXXFLAGS=" /wd4267 /wd4244 /wd4305 /wd4996" CFLAGS=" /wd4267 /wd4244 /wd4305 /wd4996" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_PREFIX_PATH="ibex" -D CMAKE_INSTALL_PREFIX="../codac" -D WITH_PYTHON=ON .. - cmake --build . --config Release --target install - cmake --build . --config Release --target pip_package ; cp `ls *.whl` ../`ls *.whl | sed "s/py3-none-any/cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}-cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}${{ matrix.cfg.cpcfg }}/"` ; pip install ../*.whl ; python -c "import sys; print(sys.version)" ; python ../examples/tuto/01_getting_started/01_getting_started.py + cmake -E env CXXFLAGS=" /MP4 /wd4267 /wd4244 /wd4305 /wd4996" CFLAGS=" /MP4 /wd4267 /wd4244 /wd4305 /wd4996" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_INSTALL_PREFIX="../codac" -D WITH_PYTHON=ON .. + cmake --build . -j 4 --config Release --target install + cmake --build . --config Release --target pip_package ; cp `ls *.whl` ../`ls *.whl | sed "s/py3-none-any/cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}-cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}${{ matrix.cfg.cpcfg }}/"` cd .. shell: bash - uses: xresloader/upload-to-github-release@v1 @@ -70,4 +80,13 @@ jobs: file: "*.whl" overwrite: true tag_name: autotagname-${{ github.sha }} - if: (github.event_name != 'pull_request')&&(github.ref_name == 'master') + if: (github.event_name!='pull_request')&&((github.ref_name=='codac1')||(github.ref_name=='codac2')||(github.ref_name=='codac2_codac4matlab')) + - run: | + pip install --no-deps --no-index *.whl + python -c "import sys; print(sys.version)" ; python examples/tuto/01_getting_started/01_getting_started.py + pip install numpy --prefer-binary + python -m unittest discover codac.tests + #cd build && ctest -C Release -V --output-on-failure + #cd .. + shell: bash + if: (github.ref_name!='codac2_codac4matlab')&&(github.event.pull_request.base.ref!='codac2_codac4matlab') diff --git a/CHANGELOG b/CHANGELOG index 8f52ce77..034e579b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -37,6 +37,99 @@ Codac releases +----------------------------------------------------------------- + +Codac release 1.2.0 (released `Jun 6, 2022 `_) +======================================================================================================= + +`Commits since previous release 1.1.0 `_ + +Features added +-------------- + +* :commit:`54b5f45d50f73fa4f2c0eb4ee937f1d3968fadb0`: added ``SepBox`` +* :commit:`0a4f3ad7861d9dba3fbd418e75e3d9d71d761d6c`: ``SepProj``, ``SepFixPoint``, ``SepCtcPairProj``, ``QInterProjF`` now in core + +Documentation +------------- + +* :commit:`a5a483e84f5a8c3fb7eecda68cc1394adbd9c517`: string formatting for run-time ``Function`` definition +* :commit:`a5a483e84f5a8c3fb7eecda68cc1394adbd9c517`: string formatting for run-time ``Function`` definition + +Python binding +-------------- + +* :commit:`7cbb1eb06ef35d6f75df06cd1b82d5017bfe0edc`: SepFixPoint documentation +* :commit:`0aeb37f8fa6a93b60c588982c557dc1aa25471d8`: SepProj documentation +* :commit:`490dd0d5b872e1d4186c15c7b02a98b8edab086b`: SepCtcPairProj documentation +* :commit:`31727f5f889c121d4d9a7d1f48c99670ae034add`: QInterProjF documentation +* :commit:`9000cc63169dd4937ac3a20726f5290882a8aaed`: constructor for creating an ``IntervalMatrix`` from NumPy objects + + +----------------------------------------------------------------- + +Codac release 1.1.0 (released `May 17, 2022 `_) +======================================================================================================== + +`Commits since previous release 1.0.0 `_ + +Features added +-------------- + +* :commit:`b6f79a06aa0d3fe94072aba2168dfd523d1ef123`: added ``CtcCartProd`` +* :commit:`9382404ef967cdf77e20568a5c57e32fab9e9463`: added ``CtcBox`` +* :commit:`e014ae05c0f45f4bd357f3cd047e3fd475761428`: added ``SepFunction`` + +Changes +------- + +* :commit:`4357e3d50b192935a7da9a6f785e6ef7e11f9658`: improving SIVIA interface + compatibility with former pySIVIA + +Documentation +------------- + +* :commit:`a9cefd2b9a90503c09eb765ee916e7e4077aa89b`: Lie symmetries page +* :commit:`b534e48a71ac6657e0c6cdbb06738e2be547cbd0`: updated installation information +* :commit:`f04180d7517d6169ebdcff42159357a5ea171f15`: how to build ``Trajectory`` from ``npz`` file +* :commit:`cc350cb32e54b663c3a7e0ce71f76fcf194f2535`: codac-unsupported + +Python binding +-------------- + +* :commit:`eea406bb88e95f42c601d7643f1f81a08f900718`: now available for MacOS +* :commit:`ad2b7fac70d72832057d2a2af13dd09d8ea0f898`: corrected bug in ``__invert__`` of a ``Sep`` + + +----------------------------------------------------------------- + +Codac release 1.0.0 (released `Apr 21, 2022 `_) +======================================================================================================== + +`Commits since previous release 0.1.14 `_ + +Changes +------- + +* :commit:`ad73493012207bacc191a9261058f2af4b4a61d4`: C++17 + +Bugs fixed +---------- + +* :commit:`3c268eb9e0f93b21d1d81907bac23f64400d2cbc`: fix encoding problems on Windows with Python 3.10 +* :commit:`4e5d21637d17efe97175c7e026457cb8a62128e2`: updated pybind11 to v2.9.2 for Python 3.10 support +* :commit:`03889ad182cf24d41a6c001a90e20bed15a0b1f2`: ``const`` for ``TFnc`` parameter in ``CtcPicard`` constructor + +Documentation +------------- + +* :commit:`b1617459d2acc216fa5db854d7ba09affe279aff`: set-inversion and separators + +Python binding +-------------- + +* :commit:`5fcaebf959e4ab454818e6921d278fdf78ab38cc`: added ``__hash__`` for ``Interval``, ``IntervalVar``, ``IntervalVectorVar`` + + ----------------------------------------------------------------- Codac release 0.1.14 (released `Apr 14, 2022 `_) diff --git a/CMakeLists.txt b/CMakeLists.txt index bd208680..27cc9f9f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ # Codac - cmake configuration file # ================================================================== - cmake_minimum_required(VERSION 3.0.2) + cmake_minimum_required(VERSION 3.8) list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/scripts/CMakeModules/) include(version_from_git) @@ -38,7 +38,6 @@ endif() endif() - ################################################################################ # Options for directories ################################################################################ @@ -63,11 +62,6 @@ # add_compile_options(-std=c++17) # else() # message(FATAL_ERROR "Codac needs a compiler with C++17 support") -# endif() - -# if(WIN32) -# # We need this for strdup under Windows (see issue #287 of ibex-lib repo) -# add_definitions(-U__STRICT_ANSI__) # endif() #if(NOT CMAKE_CXX_STANDARD) @@ -75,6 +69,10 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) #endif() + if(MSVC) + add_definitions(-D_ENABLE_EXTENDED_ALIGNED_STORAGE) # Due to a known issue in older versions of Visual Studio 2017... + endif() + ################################################################################ # Optional binary tree activation for all tubes (for tests purposes mainly) @@ -105,7 +103,7 @@ ################################################################################ find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") add_definitions(${EIGEN3_DEFINITIONS}) include_directories(${EIGEN3_INCLUDE_DIRS}) diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 85b09f4b..0d9dff3b 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -10,8 +10,8 @@ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/mathjax_stmaryrd.js # Technical API documentation (Doxygen) if(WITH_PYTHON) - - find_package(Doxygen) + + find_package(Doxygen 1.0.0...1.9.6) if(NOT DOXYGEN_FOUND) @@ -19,6 +19,12 @@ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/mathjax_stmaryrd.js else() + if(NOT DOXYGEN_DIR) + message(STATUS "Doxygen ${DOXYGEN_VERSION} found.") + else() + message(STATUS "Doxygen ${DOXYGEN_VERSION} found in ${DOXYGEN_DIR}.") + endif() + # Includes CMake commands in config file: configure_file(api/Doxyfile.in api/Doxyfile) diff --git a/doc/doc/_static/custom-sphinx.css b/doc/doc/_static/custom-sphinx.css index 46f62eb3..37441c72 100644 --- a/doc/doc/_static/custom-sphinx.css +++ b/doc/doc/_static/custom-sphinx.css @@ -97,6 +97,17 @@ } +/* Right alignment + gray color (for right-align notes) */ + + .right-aligned-note + { + margin-right: 10px; + display: block; + float: right; + color: gray; + } + + /* */ .wy-table-responsive table td diff --git a/doc/doc/conf.py.in b/doc/doc/conf.py.in index 9161a5ad..29eb7c9f 100644 --- a/doc/doc/conf.py.in +++ b/doc/doc/conf.py.in @@ -45,11 +45,13 @@ extensions = [ redirects = { "lohner": "manual/05-dynamic-contractors/03-ctc-lohner.html", - "slam": "tutorial/08-rangeonly-slam/index.html", + "slam": "use-cases/slam/index.html", "lie-symmetries": "use-cases/lie-symmetries/index.html", "sivia": "manual/11-separators/index.html", "installation": "install/01-installation.html", "icra2023-tutorial": "tutorial/icra-2023.html", + "brunovsky": "use-cases/brunovsky/index.html", + "jnrr": "tutorial-jnrr23/index.html", } breathe_projects = { @@ -60,6 +62,11 @@ breathe_projects = { rst_prolog = """ .. role:: underline :class: underline +.. role:: right-aligned-note + :class: right-aligned-note + +.. seealso:: + This manual refers to Codac v1, but a new v2 implementation is currently in progress... an update of this manual will be available soon. `See more `_. """ # GitHub repo @@ -112,7 +119,7 @@ version = release # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. -language = None +language = 'en' # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: @@ -385,3 +392,11 @@ texinfo_documents = [ # If true, do not generate a @detailmenu in the "Top" node's menu. # # texinfo_no_detailmenu = False + + + +# TO BE IMPROVED: +# Add +# LEXER_MAP['matlab'] = "MATLAB" +# in ~/.local/lib/python3.6/site-packages/sphinx_tabs/tabs.py +# for allowing MATLAB code tabs \ No newline at end of file diff --git a/doc/doc/dev/info_dev.rst b/doc/doc/dev/info_dev.rst index 4b5797e3..233f3cff 100644 --- a/doc/doc/dev/info_dev.rst +++ b/doc/doc/dev/info_dev.rst @@ -53,7 +53,12 @@ In case you are willing to contribute to Codac, here are some information that m This configuration generates header files containing docstrings for Python, based on the content of XML files made by Doxygen. The documentation of any C++/Python function is then located in the C++ header files of the :file:`/src` directory. + ---------------------- -------------------------------------------------------------------------------------- + PYTHON_EXECUTABLE (optional) Specifies the executable (and version) of Python. For instance: + + .. code-block:: bash + cmake -DPYTHON_EXECUTABLE=/usr/bin/python3.10 .. ====================== ====================================================================================== @@ -165,7 +170,7 @@ Get Pybind11 as submodule: git submodule init git submodule update -Then, configure ``cmake`` with custom options and ``-DWITH_PYTHON=ON``: +Then, configure ``cmake`` with your custom options ``<...>`` and ``-DWITH_PYTHON=ON``: .. code-block:: bash @@ -175,10 +180,13 @@ This configuration generates header files containing docstrings for Python, base the content of XML files made by Doxygen. The documentation of any C++/Python function is then located in the C++ header files of the :file:`/src` directory. -Finally, after the compilation: +Note that you also have to configure IBEX with the ``-DCMAKE_CXX_FLAGS="-fPIC" -DCMAKE_C_FLAGS="-fPIC"`` flag. + +Finally, after the compilation of Codac (and IBEX): .. code-block:: bash + # from codac repository cd build/python/python_package python3 setup.py develop --user @@ -202,15 +210,54 @@ Tag the current version: git tag -a v3.0.0-beta1 git push origin v3.0.0-beta1 -Create the *wheels* with a Docker: +.. warning:: + + | ``-beta1`` part in the version might not be always supported. + +Get all the *wheels* generated by GitHub Actions (the generated release needs to be explicitely set as latest): .. code-block:: bash - docker pull benensta/pyibex-docker - docker run --rm -v `pwd`:/io benensta/pyibex-docker /io/scripts/docker/build_pybinding.sh + sudo apt install curl wget sed + cd scripts/wheels + chmod a+x *.sh + ./getlatestcodacwhl.sh + rm lk_codac_whl.ver # Optional, contain the version number that was last downloaded. Upload the *wheels* on PyPi: .. code-block:: bash - python3 -m twine upload --repository pypi * \ No newline at end of file + python3 -m twine upload --repository pypi * + + +.. rubric:: Testing the Linux *wheels* with Docker + +In the :file:`codac` directory, test the x86_64 Linux configuration locally using Docker: + +.. code-block:: bash + + chmod a+x scripts/docker/build_pybinding.sh + docker pull lebarsfa/manylinux2014_x86_64-for-codac + docker run --rm -v `pwd`:/io lebarsfa/manylinux2014_x86_64-for-codac /io/scripts/docker/build_pybinding.sh + +The same can be done for the ARM Linux configurations (preferably from a powerful ARM computer, such as a Mac with Apple Silicon), see ``packages/temporary`` folder. + + +.. rubric:: [For admins] Upload Ubuntu packages + +Get access to https://packages.ensta-bretagne.fr/ server as a network drive from an Ubuntu computer. If not already done, run ``sudo apt install dpkg-dev curl`` to install some prerequisites. Then, run ``sudo ./getlatestcodacrelease.sh`` to download automatically the ``.deb`` files from the latest GitHub release for all the supported configurations and update the :ref:`repository database ` (or just ``sudo ./gencodac.sh`` to download automatically a specific version without updating the database). If you downloaded manually the ``.deb`` files, you can update the repository database with ``sudo ./scanpackages.sh``. + + +.. rubric:: [For admins] Upload Windows packages + +Get access to https://packages.ensta-bretagne.fr/ server as a network drive from a Windows computer. If not already done, install `Chocolatey `_ and then run ``choco install -y wget curl 7z checksum`` to install some prerequisites. Then, in ``choco`` folder, run from the Command Prompt ``getlatestcodacrelease.bat`` to generate a ``.nupkg`` from the latest GitHub release (or just ``gencodacpackages.bat`` to be able to test a specific version), then ``choco push codac.X.Y.Z.nupkg`` (need logged in Chocolatey maintainer) when the version is considered as OK to upload to the `Chocolatey repository `_. + + +.. rubric:: MATLAB compatibility + +Some operators or special functions need special attention: + +* Square bracket operator, see e.g. ``getitem`` (``__setitem__``) and ``setitem`` (``__getitem__``) in :commit:`65784a201dda9841194fb581e689247adab07ef7`. +* ``inter`` (``py::self & py::self``, ``__and__``, ``__rand__``), ``union`` (``py::self | py::self``, ``__or__``, ``__ror__``), ``inter_self`` (``py::self &= py::self``, ``__iand__``), ``union_self`` (``py::self |= py::self``, ``__ior__``), ``abs`` (``__abs__``), ``pow`` (``__pow__``), ``get_item`` (``__get_item__``), ``hash`` (``__hash__``) in :commit:`54966a6be0ddd6fafe64bd90fe6401822ba49587` and :commit:`a4500627cafd21a45b1d15bcb37352746482d33a` (similarly: :commit:`50b138694a2f89bca1b7d6dbff190ac364082e68`). +* See also the bottom of https://fr.mathworks.com/help/matlab/matlab_external/differences-between-matlab-python.html and `Start a MATLAB project <../install/04-start-matlab-project.html>`_. diff --git a/doc/doc/faq.rst b/doc/doc/faq.rst index 225eb550..5196d529 100644 --- a/doc/doc/faq.rst +++ b/doc/doc/faq.rst @@ -204,7 +204,7 @@ A contractor is an operator that *contracts* (reduces) a domain (a box, for inst When it is used together with other contractors, there may be interactions between the contractors: a contraction from one contractor may *activate* another one. It becomes necessary to call all the contractors several times in order to converge to the best contraction of the domains. This number of contracting iterations cannot be known in advance. It depends on the contractors at stake, their efficiency and their sequencing. -One can implement a loop of contractions in order to process the contractors as long as their is a contraction on one of the domains. The iteration stops when a fixed point has been reached: when nothing can be contracted anymore. +One can implement a loop of contractions in order to process the contractors as long as there is a contraction on one of the domains. The iteration stops when a fixed point has been reached: when nothing can be contracted anymore. Because a computer computes with floating point numbers, the fixed point will be reached in a finite number of steps. In practice, we may stop the iteration as soon as the contractions are not significant anymore. diff --git a/doc/doc/index.rst b/doc/doc/index.rst index 738a3aab..cdf52216 100644 --- a/doc/doc/index.rst +++ b/doc/doc/index.rst @@ -60,7 +60,7 @@ In a nutshell, Codac is a **constraint programming framework** providing tools t .. **Mobile robotics** has been the initial motivation of this project: the mathematical tools provided in Codac come together with robotic applications. -.. Computations stands on the `IBEX library `_ that provides reliable tools for static systems. +.. Computations stands on the `IBEX library `_ that provides reliable tools for static systems. Getting started: 2 minutes to Codac @@ -227,7 +227,7 @@ The distance function :math:`g(\mathbf{x},\mathbf{b})` between the robot and a l .. figure:: img/rangeonly-nox0.png | *You just solved a non-linear state-estimation without knowledge about initial condition.* -| See the full example on Github: `in C++ `_ or `in Python `_. +| See the full example on Github: `in C++ `_, `in Python `__ or `in MATLAB `_. In the tutorial and in the examples folder of this library, you will find more advanced problems such as Simultaneous Localization And Mapping (SLAM), data association problems or delayed systems. @@ -254,8 +254,8 @@ Then you have two options: read the details about the features of Codac (domains .. Figure:: img/logo_ibex.jpg :align: center - | Note that Codac stands on the `IBEX library `_ for interval analysis computations and static contractors on boxes. - | `Read the IBEX documentation. `_ + | Note that Codac stands on the `IBEX library `_ for interval analysis computations and static contractors on boxes. + | `Read the IBEX documentation. `_ .. .. rubric:: pyIbex .. @@ -359,7 +359,7 @@ Contributors * `Peter Franek `_ * `Gilles Trombettoni `_ * Verlein Radwan - * `Mohamed Saad Ibn Seddik `_ + * Joris Tillet Main related publications @@ -432,7 +432,7 @@ Main related publications .. |desrochers-phd-year| replace:: 2018 .. |damers-phd-title| replace:: Lie Groups applied to localisation of mobile robots -.. _damers-phd-title: https://julien-damers.fr/phd/complete.pdf +.. _damers-phd-title: https://julien-damers.fr/phd/Lie_Groups_applied_to_localisation_of_mobile_robots.pdf .. |damers-phd-authors| replace:: Damers .. |damers-phd-journal| replace:: PhD thesis .. |damers-phd-year| replace:: 2022 @@ -463,9 +463,16 @@ We suggest the following BibTeX template to cite Codac in scientific discourse: .. code-block:: none - @misc{codac, - author = {Rohou, Simon and Desrochers, Benoit and others}, - year = {2022}, - note = {http://codac.io}, - title = {The {Codac} library -- {C}onstraint-programming for robotics} - } \ No newline at end of file + @article{codac_lib, + title={The {C}odac Library}, + url={https://cyber.bibl.u-szeged.hu/index.php/actcybern/article/view/4388}, + DOI={10.14232/actacyb.302772}, + journal={Acta Cybernetica}, + volume={26}, + number={4}, + series = {Special Issue of {SWIM} 2022}, + author={Rohou, Simon and Desrochers, Benoit and {Le Bars}, Fabrice}, + year={2024}, + month={Mar.}, + pages={871-887} + } \ No newline at end of file diff --git a/doc/doc/install/01-installation-full-linux.rst b/doc/doc/install/01-installation-full-linux.rst index f2fc1572..a2518901 100644 --- a/doc/doc/install/01-installation-full-linux.rst +++ b/doc/doc/install/01-installation-full-linux.rst @@ -2,27 +2,43 @@ .. _sec-installation-full-linux: -##################################### -Installing Codac on Linux for C++ use -##################################### +######################################## +Installing Codac v1 on Linux for C++ use +######################################## -Install from package (latest release, for Ubuntu amd64) ---------------------------------------------------------- +Install from package (latest release, for Ubuntu (amd64, arm64), Debian (arm64, armhf) and possibly others) +----------------------------------------------------------------------------------------------------------- -A Debian package is available for the last release |version| of the library: +A Debian package is available for the last release 1.6 of the library: .. code-block:: bash - sudo sh -c 'echo "deb [trusted=yes] https://www.ensta-bretagne.fr/packages/`lsb_release --id -s | tr [:upper:] [:lower:]`/`lsb_release -cs` ./" > /etc/apt/sources.list.d/ensta-bretagne.list' + sudo sh -c 'echo "deb [trusted=yes] https://packages.ensta-bretagne.fr/$(if [ -z "$(. /etc/os-release && echo $UBUNTU_CODENAME)" ]; then echo debian/$(. /etc/os-release && echo $VERSION_CODENAME); else echo ubuntu/$(. /etc/os-release && echo $UBUNTU_CODENAME); fi) ./" > /etc/apt/sources.list.d/ensta-bretagne.list' sudo apt update sudo apt install libcodac-dev -Then, check your installation `with the instructions of this page <03-start-cpp-project.html>`_. +Then, check your installation :ref:`with the instructions of this page `. + +.. warning:: + + | **URL changed**: Please uninstall before. .. note:: - For a Raspberry Pi running Raspbian Buster, download and extract ``codac_standalone_armv6hf_buster.zip`` from ``_, then in the ``example`` folder run: + To uninstall Codac, you might want to do the following: + + .. code-block:: bash + + sudo apt remove libcodac-dev libibex-dev + sudo rm -f /etc/apt/sources.list.d/ensta-bretagne.list + sudo apt update + + Note also that ``libeigen3-dev`` might have been installed as a dependency of Codac but might be also used by other software. You might want to keep it. + +.. note:: + + Standalone archives exist also for all the supported configurations, e.g. for a Raspberry Pi running Raspberry Pi OS Bookworm 32 bit, download and extract ``codac_standalone_armhf_bookworm.zip`` from ``_, then in the ``example`` folder run: .. code-block:: bash @@ -31,15 +47,15 @@ Then, check your installation `with the instructions of this page <03-start-cpp- and check that "My first tube:Tube [0, 10]" appears. -Install from sources (latest development) ------------------------------------------ +Install from sources (latest development of v1) +----------------------------------------------- In case you prefer the latest development version, Codac can be installed by compiling the sources. Requirements ^^^^^^^^^^^^ -Codac uses several features of the `IBEX library `_ that you have to install first. The last version of IBEX is maintained on `this unofficial development repository `_: +Codac uses several features of the `IBEX library `_ that you have to install first. The last version of IBEX is maintained on `this unofficial development repository `_: .. code-block:: bash @@ -47,7 +63,7 @@ Codac uses several features of the `IBEX library `_ and `GAOL `_ with CMake and `specify where they are `_ to build IBEX successfully and have accurate computations. + .. admonition:: Debug/development mode Note that the :code:`-DCMAKE_BUILD_TYPE=Debug` option will slightly slow down your computations, but display useful error messages in case of failure conditions such as access violations. **It is highly recommended** for your developments. To use it: @@ -80,6 +100,7 @@ The last sources are available on `the official Codac development repository ` before running your program. (for experts) Additional installation options @@ -159,4 +180,4 @@ Do not forget to launch the VIBes viewer before running your program. export CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH:$HOME/ibex-lib/build_install export CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH:$HOME/codac/build_install -See also `Information for developers `_. +See also :ref:`Information for developers `. diff --git a/doc/doc/install/01-installation-full-macos.rst b/doc/doc/install/01-installation-full-macos.rst index f8cfe3b3..c052a4bd 100644 --- a/doc/doc/install/01-installation-full-macos.rst +++ b/doc/doc/install/01-installation-full-macos.rst @@ -2,23 +2,23 @@ .. _sec-installation-full-macos: -##################################### -Installing Codac on macOS for C++ use -##################################### +######################################## +Installing Codac v1 on macOS for C++ use +######################################## Quick start ----------- -Install Homebrew package manager and build tools: +Install `Homebrew package manager `_ and build tools: .. code-block:: bash - /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" + /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" brew install wget autoconf automake libtool brew install --cask cmake -Download and extract e.g. ``codac_standalone_x64_bigsur.zip`` (for macOS 11 Big Sur) from ``_, then in ``example`` folder run: +Download and extract *e.g.* ``codac_standalone_arm64_monterey.zip`` (for macOS 12 Monterey on a Mac with Apple silicon (arm64 processor), use ``codac_standalone_x86_64_monterey.zip`` for a Mac with an Intel processor (x86_64 processor), see https://support.apple.com/en-us/116943) from ``_, then in ``example`` folder run: .. code-block:: bash @@ -26,16 +26,25 @@ Download and extract e.g. ``codac_standalone_x64_bigsur.zip`` (for macOS 11 Big and check that "My first tube:Tube [0, 10]" appears. -Optionally, download and run ``_ before running the project, and check that a tube appears in VIBes window. +Optionally, download and run ``_ (see also https://support.apple.com/HT211861) before running the project, and check that a tube appears in :ref:`VIBes viewer `. Building from sources --------------------- -You will probably need to install those prerequisites (assuming you installed `Homebrew package manager `_): +You will probably need to install those prerequisites: .. code-block:: bash brew install eigen -The logic to follow will then be similar to `Linux <01-installation-full-linux.html>`_. See also `Information for developers `_. +Optionally, for Python and documentation: + +.. code-block:: bash + + wget https://github.com/Homebrew/homebrew-core/raw/d2267b9f2ad247bc9c8273eb755b39566a474a70/Formula/doxygen.rb ; brew reinstall ./doxygen.rb ; brew pin doxygen + brew install graphviz + python -m pip install --upgrade pip + pip install --upgrade wheel setuptools sphinx breathe sphinx-issues sphinx-tabs sphinx_rtd_theme sphinx-reredirects + +The logic to follow will then be similar to :ref:`Linux `. See also :ref:`Information for developers `. \ No newline at end of file diff --git a/doc/doc/install/01-installation-full-windows.rst b/doc/doc/install/01-installation-full-windows.rst index 5d271b2c..d2ac53b1 100644 --- a/doc/doc/install/01-installation-full-windows.rst +++ b/doc/doc/install/01-installation-full-windows.rst @@ -2,9 +2,9 @@ .. _sec-installation-full-windows: -####################################### -Installing Codac on Windows for C++ use -####################################### +########################################## +Installing Codac v1 on Windows for C++ use +########################################## Quick start @@ -17,9 +17,9 @@ Check https://community.chocolatey.org/packages/codac. .. rubric:: Using Visual Studio -Download and extract *e.g.* ``codac_standalone_x64_vc16.zip`` (for Visual Studio 2019) from ``_, open ``example\CMakelists.txt``, choose ``x64-Release`` configuration in Visual Studio (instead of ``x64-Debug``), double-click on ``main.cpp`` in the Solution Explorer and then click on the green Start button, finally check that "My first tube:Tube [0, 10]" appears. +Download and extract *e.g.* ``codac_standalone_x64_vc17.zip`` (for Visual Studio 2022) from ``_, open ``example\CMakelists.txt``, choose ``x64-Release`` configuration in Visual Studio (instead of ``x64-Debug``), double-click on ``main.cpp`` in the Solution Explorer and then click on the green Start button, finally check that "My first tube:Tube [0, 10]" appears. -Optionally, download and run ``_ before running the project, and check that a tube appears in VIBes window. +Optionally, download and run ``_ before running the project, and check that a tube appears in :ref:`VIBes viewer `. Building from sources @@ -29,27 +29,87 @@ You will probably need to install these prerequisites (assuming you installed `C .. code-block:: bat - choco install cmake git make patch winflexbison - choco install eigen - -Then, install the desired compiler (*e.g.* ``choco install mingw --version=8.1.0``). + choco install -y cmake.install --installargs 'ADD_CMAKE_TO_PATH=System' + choco install -y git wget make patch winflexbison + choco install -y eigen -Optionally, for Python (*e.g.* ``choco install python --version=3.8.2``) and documentation: +.. note:: + + | All the commands that install packages need to be run from an `elevated administrator terminal `_. See also `sudo command availability `_. + +Then, install the desired compiler (*e.g.* ``choco install -y mingw --version=11.2.0.07112021``). + +Optionally, for Python (*e.g.* ``choco install -y python --version=3.10.4``) and documentation: .. code-block:: bat - choco install doxygen.install graphviz + choco install -y doxygen.install --version=1.9.6 + choco install -y graphviz python -m pip install --upgrade pip - pip install --upgrade wheel setuptools - git clone -b v3.1.1 https://github.com/sphinx-doc/sphinx - cd sphinx - pip install . - pip install --upgrade breathe sphinx-issues sphinx-tabs sphinx_rtd_theme sphinx-reredirects + pip install --upgrade wheel setuptools sphinx breathe sphinx-issues sphinx-tabs sphinx_rtd_theme sphinx-reredirects -The logic to follow will then be similar to `Linux <01-installation-full-linux.html>`_ (note that for Visual Studio, commands such as ``make install`` need to be replaced with something similar to: +The logic to follow will then be similar to :ref:`Linux `. You might want to right-click in your desired folder and choose ``Git Bash Here`` to run the commands related to Git and compilation. Note that if using Visual Studio build tools, commands such as ``make`` and ``make install`` need to be replaced with something similar to: .. code-block:: bash + cmake --build . --config Release cmake --build . --config Release --target install -See also `Information for developers `_. +or + +.. code-block:: bash + + cmake --build . -j 4 --config Release + cmake --build . -j 4 --config Release --target install + +to enable parallel compilation on 4 cores. + +.. warning:: + + | Dependencies of Codac need to built with the same compiler as Codac itself. First, you will need to recompile IBEX (from https://github.com/lebarsfa/ibex-lib) and its dependencies GAOL (https://github.com/lebarsfa/GAOL) and mathlib (https://github.com/lebarsfa/mathlib), which need to be installed manually via CMake with the options ``-D GAOL_ENABLE_PRESERVE_ROUNDING=OFF -D GAOL_ENABLE_OPTIMIZE=ON -D GAOL_ENABLE_VERBOSE_MODE=OFF``. During the compilation of IBEX, specify where they are installed if necessary using the CMake options, e.g., ``-D INTERVAL_LIB=gaol -D MATHLIB_DIR=../mathlib -D GAOL_DIR=../gaol``). + +.. warning:: + + | You might need to replace all occurences of :literal:`PATH_SUFFIXES \ ` with something similar to :literal:`PATHS ${CMAKE_CURRENT_LIST_FILE}/../../../../ PATH_SUFFIXES \ ` in all ``.cmake`` in ``codac/share/codac/cmake/`` (where Codac was installed) if a CMake project that tries to use Codac appears to find its installation location but fails to configure the project properly. + +.. warning:: + + | If using the MinGW version installed using ``choco install -y mingw --version=11.2.0.07112021``, you might need to install the following patch for debugging to work correctly: + + .. code-block:: bat + + wget.exe https://packages.ensta-bretagne.fr/choco/mingw-patch.11.2.0.20230603.nupkg --no-check-certificate -nv + choco install -y -s . mingw-patch --version=11.2.0.20230603 + +.. note:: + + | If using CLion IDE, it might be difficult to set CMake options such as ``-DBUILD_TESTS=ON`` from inside the IDE (it seems related to Ninja support). As a workaround, try to set these options, assuming the MinGW version suggested above was installed: + + .. image:: img/clion_1.png + :width: 800px + + | + + .. image:: img/clion_2.png + :width: 800px + + | + + .. image:: img/clion_3.png + :width: 800px + +.. tip:: + + | If using Visual Studio IDE or build tools, you might want to add the ``/MP`` compiler flag to enable more parallelization possibilities, in *e.g.* + + .. code-block:: bash + + cmake -G "Visual Studio 17 2022" -A x64 -D CMAKE_PREFIX_PATH="../ibex" -D CMAKE_INSTALL_PREFIX="../codac" -D CMAKE_C_FLAGS="/MP4" -D CMAKE_CXX_FLAGS="/MP4" .. + + Then, opening the generated ``.sln`` file and building the ``INSTALL`` project from inside Visual Studio or from the command-line (*e.g.* ``cmake --build . -j 4 --config Debug --target install``) should be faster (change ``4`` in ``-j 4`` and ``/MP4`` to another number of cores if needed). + +.. tip:: + + | To run the tests, the generic command is ``ctest -C Debug -V --output-on-failure`` or ``ctest -C Release -V --output-on-failure`` (assuming the CMake option ``-DBUILD_TESTS=ON`` was specified). + +See also :ref:`Information for developers `. diff --git a/doc/doc/install/01-installation-python.rst b/doc/doc/install/01-installation-python.rst index 803d24e5..19d34ab4 100644 --- a/doc/doc/install/01-installation-python.rst +++ b/doc/doc/install/01-installation-python.rst @@ -2,9 +2,9 @@ .. _sec-installation-py: -############################### -Installing Codac for Python use -############################### +################################## +Installing Codac v1 for Python use +################################## .. note:: @@ -19,7 +19,8 @@ In case you want to use Codac only with Python, then the installation procedure # You may have to upgrade pip (19.0.0 required at least) pip3 install --upgrade pip - pip3 install codac + pip3 install codac==1.6 + # latest release of Codac v1 is 1.6 .. role:: gbg @@ -35,36 +36,50 @@ In case you want to use Codac only with Python, then the installation procedure The :gbg:`✓` configurations are officially supported at the moment: -+---------------+----------------+-----------------+-----------------+----------------+----------------+ -|Language |Linux (amd64) |Windows (x64) |Windows (x86) |macOS (x86_64) |Online | -+===============+================+=================+=================+================+================+ -|Python 3.6 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` ||online-py|_ | -+---------------+----------------+-----------------+-----------------+----------------+ + -|Python 3.7 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | -+---------------+----------------+-----------------+-----------------+----------------+ + -|Python 3.8 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | -+---------------+----------------+-----------------+-----------------+----------------+ + -|Python 3.9 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | -+---------------+----------------+-----------------+-----------------+----------------+ + -|Python 3.10 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | -+---------------+----------------+-----------------+-----------------+----------------+----------------+ ++---------------+----------------+----------------+-----------------+-----------------+----------------+----------------+----------------+ +|Language |Linux (amd64) |Linux (arm64) |Windows (x64) |Windows (x86) |macOS (arm64) |macOS (x86_64) |Online | ++===============+================+================+=================+=================+================+================+================+ +|Python 3.6 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` ||online-py|_ | ++---------------+----------------+----------------+-----------------+-----------------+----------------+----------------+ + +|Python 3.7 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | ++---------------+----------------+----------------+-----------------+-----------------+----------------+----------------+ + +|Python 3.8 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | ++---------------+----------------+----------------+-----------------+-----------------+----------------+----------------+ + +|Python 3.9 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | ++---------------+----------------+----------------+-----------------+-----------------+----------------+----------------+ + +|Python 3.10 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | ++---------------+----------------+----------------+-----------------+-----------------+----------------+----------------+ + +|Python 3.11 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | ++---------------+----------------+----------------+-----------------+-----------------+----------------+----------------+----------------+ +|Python 3.12 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | ++---------------+----------------+----------------+-----------------+-----------------+----------------+----------------+----------------+ If a configuration in this table does not work, please `contact us `_. .. warning:: - | **macOS Big Sur and Monterey (amd64):** - | `Python wheels provided for Catalina may still be installed `_, try something similar to: + | **Debian Bookworm and possibly other configurations:** + | ``pip install ...`` or ``pip3 install ...`` commands may only work inside `virtual environments `_ or with ``--break-system-packages`` parameter, e.g.: .. code-block:: bash - sudo pip3 install --platform macosx_10_15_x86_64 --only-binary=:all: --target=/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/site-packages codac + pip3 install --break-system-packages codac - Depending on the way Python was installed, the path to specify after ``--target`` may differ, *e.g.* if Python was installed from https://www.python.org/ftp/python/3.10.4/python-3.10.4-macos11.pkg, it may be ``/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages``. Otherwise, run ``python3 -m site`` to check the ``site-packages`` full path in ``sys.path list``. +.. warning:: + + | **macOS Big Sur and later (x86_64):** + | `Python wheels provided for Catalina or earlier may need to be installed with something similar to `_: + + .. code-block:: bash + + sudo pip3 install --upgrade vibes # Add here any other non-binary wheels dependencies... + sudo pip3 install --upgrade --nodeps --platform macosx_10_9_x86_64 --only-binary=:all: --target=/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/site-packages codac + + Depending on the way Python was installed, the path to specify after ``--target`` may differ, *e.g.* if Python was installed from https://www.python.org/ftp/python/3.10.4/python-3.10.4-macos11.pkg, it may be ``/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages``. Otherwise, run ``python3 -m site`` to check the ``site-packages`` full path in ``sys.path list``. Also, the value ``10_9`` may need to be changed to ``10_14`` (or possibly another value) for some Python versions. .. note:: - ARM-based computers can still probably follow `Installing local Python binding <../dev/info_dev.html>`_ (not tested). + Unsupported computers can still probably follow :ref:`Installing local Python binding `. Update your Codac Python package diff --git a/doc/doc/install/01-installation.rst b/doc/doc/install/01-installation.rst index e350fd97..c7898f72 100644 --- a/doc/doc/install/01-installation.rst +++ b/doc/doc/install/01-installation.rst @@ -4,7 +4,7 @@ Installing the Codac library ############################ -Codac is available in both C++17 and Python3. Note that you can also :ref:`use Codac online in Python `, without having to install the library on your machine. +Codac is available in both C++17 and Python3 (:ref:`as well as MATLAB through its Python interface `). Note that you can also :ref:`use Codac online in Python `, without having to install the library on your machine. .. role:: gbg @@ -37,26 +37,30 @@ Codac is available in both C++17 and Python3. Note that you can also :ref:`use C The :gbg:`✓` configurations are officially supported at the moment: -+---------------+----------------+-----------------+-----------------+----------------+----------------+ -|Language |Linux (amd64) |Windows (x64) |Windows (x86) |macOS (x86_64) |Online | -+===============+================+=================+=================+================+================+ -|C++17 ||linux-cpp|_ ||win-cpp|_ ||win-cpp|_ ||macos-cpp|_ | | -+---------------+----------------+-----------------+-----------------+----------------+----------------+ -|Python 3.6 ||linux-py|_ ||win-py|_ ||win-py|_ ||macos-py|_ ||online-py|_ | -+---------------+----------------+-----------------+-----------------+----------------+ + -|Python 3.7 ||linux-py|_ ||win-py|_ ||win-py|_ ||macos-py|_ | | -+---------------+----------------+-----------------+-----------------+----------------+ + -|Python 3.8 ||linux-py|_ ||win-py|_ ||win-py|_ ||macos-py|_ | | -+---------------+----------------+-----------------+-----------------+----------------+ + -|Python 3.9 ||linux-py|_ ||win-py|_ ||win-py|_ ||macos-py|_ | | -+---------------+----------------+-----------------+-----------------+----------------+ + -|Python 3.10 ||linux-py|_ ||win-py|_ ||win-py|_ ||macos-py|_ | | -+---------------+----------------+-----------------+-----------------+----------------+----------------+ ++---------------+----------------+----------------+-----------------+-----------------+----------------+----------------+----------------+ +|Language |Linux (amd64) |Linux (arm64) |Windows (x64) |Windows (x86) |macOS (arm64) |macOS (x86_64) |Online | ++===============+================+================+=================+=================+================+================+================+ +|C++17 ||linux-cpp|_ ||linux-cpp|_ ||win-cpp|_ ||win-cpp|_ ||macos-cpp|_ ||macos-cpp|_ | | ++---------------+----------------+----------------+-----------------+-----------------+----------------+----------------+----------------+ +|Python 3.6 ||linux-py|_ ||linux-py|_ ||win-py|_ ||win-py|_ ||macos-py|_ ||macos-py|_ ||online-py|_ | ++---------------+----------------+----------------+-----------------+-----------------+----------------+----------------+ + +|Python 3.7 ||linux-py|_ ||linux-py|_ ||win-py|_ ||win-py|_ ||macos-py|_ ||macos-py|_ | | ++---------------+----------------+----------------+-----------------+-----------------+----------------+----------------+ + +|Python 3.8 ||linux-py|_ ||linux-py|_ ||win-py|_ ||win-py|_ ||macos-py|_ ||macos-py|_ | | ++---------------+----------------+----------------+-----------------+-----------------+----------------+----------------+ + +|Python 3.9 ||linux-py|_ ||linux-py|_ ||win-py|_ ||win-py|_ ||macos-py|_ ||macos-py|_ | | ++---------------+----------------+----------------+-----------------+-----------------+----------------+----------------+ + +|Python 3.10 ||linux-py|_ ||linux-py|_ ||win-py|_ ||win-py|_ ||macos-py|_ ||macos-py|_ | | ++---------------+----------------+----------------+-----------------+-----------------+----------------+----------------+ + +|Python 3.11 ||linux-py|_ ||linux-py|_ ||win-py|_ ||win-py|_ ||macos-py|_ ||macos-py|_ | | ++---------------+----------------+----------------+-----------------+-----------------+----------------+----------------+----------------+ +|Python 3.12 ||linux-py|_ ||linux-py|_ ||win-py|_ ||win-py|_ ||macos-py|_ ||macos-py|_ | | ++---------------+----------------+----------------+-----------------+-----------------+----------------+----------------+----------------+ | **Click on the links in the table to access the related installation procedures.** | If a configuration in this table does not work, please `contact us `_. -Note that if you want to contribute to Codac, you have to make the full C++ installation. +Note that if you want to contribute to Codac, you have to make the full C++ installation from sources. diff --git a/doc/doc/install/02-start-py-project.rst b/doc/doc/install/02-start-py-project.rst index 5f120567..7e9ee6c1 100644 --- a/doc/doc/install/02-start-py-project.rst +++ b/doc/doc/install/02-start-py-project.rst @@ -8,8 +8,7 @@ Start a Python project | You are using C++? | :ref:`sec-start-cpp-project` -| Codac is ready to be used on your computer. -| You can now import the ``codac`` package and start using it: +| Assuming :ref:`Codac Python package is installed `, you can import the ``codac`` package and start using it: .. code-block:: py @@ -30,11 +29,7 @@ Start a Python project python3 myscript.py -In order to visualize the tube, you need to launch the VIBes viewer independently. On Linux, you can for instance execute: - -.. code-block:: bash - - VIBes-viewer +In order to visualize the tube, you need to launch before the :ref:`VIBes viewer ` independently. If everything is well installed on your computer, you should see the following window appear: diff --git a/doc/doc/install/03-start-cpp-project.rst b/doc/doc/install/03-start-cpp-project.rst index b2d469dd..335b9c3d 100644 --- a/doc/doc/install/03-start-cpp-project.rst +++ b/doc/doc/install/03-start-cpp-project.rst @@ -8,8 +8,7 @@ Start a C++ project | You are using Python? | :ref:`sec-start-py-project` -| Codac is ready to be used on your computer. -| You can now copy-paste the following example code in a file named :file:`main.cpp`: +| Assuming :ref:`Codac library is installed `, you can copy-paste the following example code in a file named :file:`main.cpp`: .. code-block:: c++ @@ -56,7 +55,7 @@ For the compilation of your project, you can use CMake with the following file : # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac @@ -113,11 +112,7 @@ Lastly, the project can be run with: ./build/my_project | This script will create a simple tube and display it. -| In order to visualize the tube, you need to launch the VIBes viewer independently. On Linux, you can for instance execute: - -.. code-block:: bash - - VIBes-viewer +| In order to visualize the tube, you need to launch before the :ref:`VIBes viewer ` independently. If everything is well installed on your computer, you should see the following window appear: diff --git a/doc/doc/install/04-start-matlab-project.rst b/doc/doc/install/04-start-matlab-project.rst new file mode 100644 index 00000000..dc853e1f --- /dev/null +++ b/doc/doc/install/04-start-matlab-project.rst @@ -0,0 +1,75 @@ +.. _sec-start-matlab-project: + +###################### +Start a MATLAB project +###################### + +.. tip:: + | You are using Python? + | :ref:`sec-start-py-project` + +.. warning:: + + | MATLAB/Octave support is preliminary, limited tests have been made for now. See `this example `_. + +| Codac is ready to be used if you have at least MATLAB R2019b, `a supported version of Python `_ and :ref:`installed Codac Python package `. + +.. admonition:: MATLAB specificities + + | Integers in function calls from the Codac library should be cast to `int32`, e.g. `4` would be `int32(4)` (however do not change those that are in strings), otherwise remember that all numbers are `double` by default in MATLAB. + | The operator `[index]` for a Codac object should be replaced with `.getitem(int32(index))` when getting its value while it should be replaced with `.setitem(int32(index))` when setting its value (please note that indices of Codac objects start at `0` contrary to typical MATLAB objects such as MATLAB arrays or cell arrays). + | The operators/functions `x&y` (intersection) for a Codac object should be replaced with `x.inter(y)`, `x|y` (union) with `x.union(y)`, `x&=y` (intersection and update of x) with `x.inter_self(y)`, `x|=y` (union and update of x) with `x.union_self(y)`, `x**y` (power) with `x.pow(y)`, `abs(x)` (absolute) with `x.abs()`. + | Python lists of objects should be converted to MATLAB cell arrays. + | Also, when a Codac function needs a `py.list` or `Vector` parameter, the corresponding MATLAB cell array should be given as `py.list(...)` (however when the Codac function do not need a `py.list` or `Vector` parameter but just an element of a MATLAB cell array, do not convert with `py.list(...)` and be sure to get the cell array element with `{}` operator). + | Be sure that Python multiline strings are correctly converted to multiline MATLAB strings between `'`. Remember that multiline statements in MATLAB need `...` before next line. + | Please also convert Python `for` loops to typical MATLAB `for` loops, same for `if-else`, `+=` statements. + +.. admonition:: Automating Python to MATLAB conversions + + | `Bing Chat with GPT-4 `_ in `Precise` conversation style has been used successfully to help convert Python examples to MATLAB, using the description above and by providing `01_getting_started.py` and the corresponding `a01_getting_started.m` as example (about 80% of the code for `03_static_rangebearing.m` and `05_dyn_rangebearing.m` was correctly converted this way). + +.. code-block:: matlab + + import py.codac.* + + x = Tube(Interval(0,10), 0.01, TFunction("cos(t)+abs(t-5)*[-0.1,0.1]")); + + beginDrawing(); + fig = VIBesFigTube("My first tube"); + fig.add_tube(x, "x"); + fig.show(); + endDrawing(); + +| This script will create a simple tube and display it. + +In order to visualize the tube, you need to launch before the :ref:`VIBes viewer ` independently. + +If everything is well installed on your computer, you should see the following window appear: + +.. Figure:: img/helloworld.png + +.. warning:: + + Octave support has been only briefly tested on Ubuntu 22.04 for now. `Pythonic `_ can be used to be able to call Python code from Octave. Some known necessary adaptations (see also https://wiki.octave.org/Pythonic) are: + + * Missing `import` command. https://gist.github.com/lebarsfa/ebc19b143df77753842f920b4b680b84#file-import-m can be loaded in Octave as a workaround. To always load it, try to add its content to `~/.octaverc` (e.g. with `cat import.m>>~/.octaverc`). + * In at least Octave v6.4.0 and below, operators such as `+`, `-`, `*`, `/`, `==`, `!=`, etc. are not overloaded (note that these operators are supported by MATLAB, but some others might not be supported by MATLAB either, see the bottom of https://fr.mathworks.com/help/matlab/matlab_external/differences-between-matlab-python.html). The internal methods `__add__()`, `__sub__()`, `__neg__()`, `__mul__()`, `__truediv__()`, `__eq__()`, `__ne__()`, etc. can be used instead. A modified version of Pythonic is currently being developed to support these operators (see https://gitlab.com/gnu-octave/octave-pythonic/-/issues/129) and can be installed as follows: + + .. code-block:: bash + + sudo apt install octave liboctave-dev libpython3-dev build-essential make git + git clone https://gitlab.com/gnu-octave/octave-pythonic.git + cd octave-pythonic + git fetch https://gitlab.com/Vipul-Cariappa/octave-pythonic.git operators + git checkout -b octave-pythonic-operators FETCH_HEAD + make + make check + octave --path $PWD/inst --path $PWD/src + + Then in Octave: + + .. code-block:: octave + + pkg install . + + Then, Codac should be ready to be used in Octave as in MATLAB. diff --git a/doc/doc/install/img/clion_1.png b/doc/doc/install/img/clion_1.png new file mode 100644 index 00000000..f4fc4754 Binary files /dev/null and b/doc/doc/install/img/clion_1.png differ diff --git a/doc/doc/install/img/clion_2.png b/doc/doc/install/img/clion_2.png new file mode 100644 index 00000000..2b7428cb Binary files /dev/null and b/doc/doc/install/img/clion_2.png differ diff --git a/doc/doc/install/img/clion_3.png b/doc/doc/install/img/clion_3.png new file mode 100644 index 00000000..252f96c6 Binary files /dev/null and b/doc/doc/install/img/clion_3.png differ diff --git a/doc/doc/manual/01-introduction/index.rst b/doc/doc/manual/01-introduction/index.rst index 2daf4c40..ec6af2c5 100644 --- a/doc/doc/manual/01-introduction/index.rst +++ b/doc/doc/manual/01-introduction/index.rst @@ -41,7 +41,7 @@ What about the IBEX library? .. Figure:: ../../img/logo_ibex.jpg :align: left -The `IBEX library `_ is a C++ software for constraint processing over real numbers. +The `IBEX library `_ is a C++ software for constraint processing over real numbers. As for Codac, it stands on Constraint Programming but focuses on static contexts, providing reliable algorithms for handling non-linear constraints. It also proposes various tools such as the *IbexSolve* and *IbexOpt* plugins that are dedicated to system solving and optimization, and come both with a default black-box solver and global optimizer for immediate usage. diff --git a/doc/doc/manual/02-variables/01-var-static.rst b/doc/doc/manual/02-variables/01-var-static.rst index aa3aaea8..809d86d3 100644 --- a/doc/doc/manual/02-variables/01-var-static.rst +++ b/doc/doc/manual/02-variables/01-var-static.rst @@ -16,7 +16,7 @@ Note also that they are not involved during the process of constraint propagatio .. Figure:: ../../img/logo_ibex.jpg :align: right - These static variables come from the `IBEX library `_. They are briefly presented here for the sake of consistency. For more information, please refer to the `IBEX documentation `_ for C++ use. + These static variables come from the `IBEX library `_. They are briefly presented here for the sake of consistency. For more information, please refer to the `IBEX documentation `_ for C++ use. .. _sec-manual-varstatic-vectors: diff --git a/doc/doc/manual/02-variables/02-var-dynamic.rst b/doc/doc/manual/02-variables/02-var-dynamic.rst index d728a8b3..b93688dd 100644 --- a/doc/doc/manual/02-variables/02-var-dynamic.rst +++ b/doc/doc/manual/02-variables/02-var-dynamic.rst @@ -35,7 +35,7 @@ A simple temporal function [#f1]_ can be defined and used for building a traject Interval tdomain(0.,10.); // temporal domain: [t_0,t_f] Trajectory x(tdomain, TFunction("cos(t)+sin(2*t)")); // defining x(·) as: t ↦ cos(t)+sin(2t) -Usual functions such as :math:`\cos`, :math:`\log`, *etc.* can be used to define a ``TFunction`` object. The convention used for these definitions is the one of IBEX (`read more `_). Note that the system variable :math:`t` is a predefined variable of ``TFunction`` objects, that does not appear in IBEX's objects. +Usual functions such as :math:`\cos`, :math:`\log`, *etc.* can be used to define a ``TFunction`` object. The convention used for these definitions is the one of IBEX (`read more `_). Note that the system variable :math:`t` is a predefined variable of ``TFunction`` objects, that does not appear in IBEX's objects. The evaluation of a trajectory at some time :math:`t`, for instance :math:`z=x(t)`, is performed with parentheses: @@ -472,7 +472,7 @@ Next pages will present several methods to use *tubes* that are envelopes of tra .. rubric:: Footnotes -.. [#f1] In Codac, a ``codac::TFunction`` is the extension of IBEX's ``Function`` objects, for the dynamical case (see more `about IBEX's functions `_). +.. [#f1] In Codac, a ``codac::TFunction`` is the extension of IBEX's ``Function`` objects, for the dynamical case (see more `about IBEX's functions `_). .. admonition:: Technical documentation diff --git a/doc/doc/manual/03-domains/01-dom-intervals.rst b/doc/doc/manual/03-domains/01-dom-intervals.rst index 10d9dc54..dbcd2dec 100644 --- a/doc/doc/manual/03-domains/01-dom-intervals.rst +++ b/doc/doc/manual/03-domains/01-dom-intervals.rst @@ -14,7 +14,7 @@ The vectors :math:`\mathbf{x}` and matrices :math:`\mathbf{X}`, presented :ref:` .. Figure:: ../../img/logo_ibex.jpg :align: right - These interval variables come from the `IBEX library `_. They are briefly presented here for the sake of consistency. For more information, please refer to the `IBEX documentation `_ for C++ use. + These interval variables come from the `IBEX library `_. They are briefly presented here for the sake of consistency. For more information, please refer to the `IBEX documentation `_ for C++ use. .. _sec-manual-intervals-domains: @@ -169,7 +169,7 @@ Intervals, boxes and interval matrices .. _sec-manual-intervals-matrices: * | ``IntervalMatrix`` is also available. - | One can refer to the `documentation of IBEX `_ for more information. + | One can refer to the `documentation of IBEX `_ for more information. .. note:: @@ -227,7 +227,7 @@ For boxes (interval vectors), we have to specify their dimension even in case of Set operations -------------- -Set operations are available for ``Interval``, ``IntervalVector`` and ``IntervalMatrix`` objects (see the `official reference `_). In the following table, if :math:`[x]` is an interval object, :math:`d` is a real value. +Set operations are available for ``Interval``, ``IntervalVector`` and ``IntervalMatrix`` objects (see the `official reference `_). In the following table, if :math:`[x]` is an interval object, :math:`d` is a real value. ==================================== ======================================================= Code Meaning diff --git a/doc/doc/manual/04-static-contractors/01-ctc-function.rst b/doc/doc/manual/04-static-contractors/01-ctc-function.rst index 9b61811c..30517200 100644 --- a/doc/doc/manual/04-static-contractors/01-ctc-function.rst +++ b/doc/doc/manual/04-static-contractors/01-ctc-function.rst @@ -53,7 +53,7 @@ Definition .. Figure:: ../../img/logo_ibex.jpg :align: right - This contractor originates from the `IBEX library `_. It is briefly presented here for the sake of consistency. For more information, please refer to the `IBEX documentation `_ for C++. + This contractor originates from the `IBEX library `_. It is briefly presented here for the sake of consistency. For more information, please refer to the `IBEX documentation `_ for C++. .. rubric:: Optimality @@ -221,7 +221,7 @@ The above illustration reveals several contracted boxes with the new ``ctc_f`` c Going further ------------- -This ``CtcFunction`` class is a generic shortcut to deal with :math:`\mathbf{f}(\mathbf{x})=\mathbf{0}` or :math:`\mathbf{f}(\mathbf{x})\in[\mathbf{y}]`. However, several algorithms exist to optimally deal with different classes of problems. A list of static contractors is provided in the IBEX library: `see more `_. +This ``CtcFunction`` class is a generic shortcut to deal with :math:`\mathbf{f}(\mathbf{x})=\mathbf{0}` or :math:`\mathbf{f}(\mathbf{x})\in[\mathbf{y}]`. However, several algorithms exist to optimally deal with different classes of problems. A list of static contractors is provided in the IBEX library: `see more `_. The user is invited to use an appropriate tool to deal with the constraint at stake. The IBEX contractor behind ``CtcFunction`` is a ``CtcFwdBwd`` coupled with a ``Ctc3BCid``. diff --git a/doc/doc/manual/11-separators/index.rst b/doc/doc/manual/11-separators/index.rst index 1a8c0b54..7b6f1682 100644 --- a/doc/doc/manual/11-separators/index.rst +++ b/doc/doc/manual/11-separators/index.rst @@ -26,16 +26,13 @@ Separators can be combined and involved in an algorithm for set-inversion such a f = Function('x', 'y', 'x*cos(x-y)+y') # Build the separator associated to the constraint f(x,y) < 0 - sep = SepFunction(f, Interval(-oo,0)) - - # Setup the initial box - box = IntervalVector(2, [-10, 10]) + sep = SepFunction(f, [-oo,0]) # Graphics vibes.beginDrawing() vibes.newFigure("Set inversion") vibes.setFigureProperties({"x":100, "y":100, "width":500, "height":500}) - SIVIA(box, sep, 0.21, fig_name="Set inversion") + SIVIA([[-10, 10],[-10, 10]], sep, 0.21, fig_name="Set inversion") vibes.endDrawing() .. code-tab:: c++ @@ -52,14 +49,11 @@ Separators can be combined and involved in an algorithm for set-inversion such a // Build the separator associated to the constraint f(x,y) < 0 SepFunction sep(f, Interval(-oo,0)); - // Setup the initial box - IntervalVector box(2, {-10, 10}); - // Graphics vibes::beginDrawing(); vibes::newFigure("Set inversion"); vibes::setFigureProperties(vibesParams("x",100, "y",100, "width",500, "height",500)); - SIVIA(box, sep, 0.21, "Set inversion"); + SIVIA({{-10,10},{-10,10}}, sep, 0.21, "Set inversion"); vibes::endDrawing(); return EXIT_SUCCESS; @@ -82,6 +76,7 @@ The ``SIVIA(..)`` function allows several parameters for specifying result outpu SIVIA(box, # initial domain to be bisected sep, # related separator (or contractor if provided) 0.21, # precision parameter (stopping condition) + regular_paving=False, # keep a regular paving (bisection rule) display_result=True, # displaying boxes in a VIBes figure fig_name="SIVIA", # name of the VIBes figure in case of display return_result=True, # returning a map of lists of boxes @@ -93,6 +88,7 @@ The ``SIVIA(..)`` function allows several parameters for specifying result outpu SIVIA(box, // initial domain to be bisected sep, // related separator (or contractor if provided) 0.21, // precision parameter (stopping condition) + false, // keep a regular paving (bisection rule) true, // boolean for displaying boxes in a VIBes figure "SIVIA", // name of the VIBes figure in case of display true, // boolean for returning a map of lists of boxes diff --git a/doc/doc/tmp/jnrr-2023/exercice.py b/doc/doc/tmp/jnrr-2023/exercice.py new file mode 100644 index 00000000..66ee813f --- /dev/null +++ b/doc/doc/tmp/jnrr-2023/exercice.py @@ -0,0 +1,26 @@ +from codac import * +import math +import random +import time +import numpy as np + +dt = 0.02 # temporal discretization +tdomain = Interval(0,15) # [t0,tf] + +# System input +u = Trajectory(tdomain, TFunction("3*(sin(t)^2)+t/100"), dt) + +# Actual state trajectory +# Note that this trajectory is unknown of the resolution +x_truth = TrajectoryVector(3) +x_truth[2] = u.primitive() +x_truth[0] = (10*cos(x_truth[2])).primitive() +x_truth[1] = (10*sin(x_truth[2])).primitive() + +beginDrawing() +fig_map = VIBesFigMap("Top view") +fig_map.set_properties(50, 50, 800, 600) +fig_map.add_trajectory(x_truth, "x*", 0, 1, "black") +fig_map.smooth_tube_drawing(True) +fig_map.show(1.) + diff --git a/doc/doc/tmp/jnrr-2023/exercice_slam.pdf b/doc/doc/tmp/jnrr-2023/exercice_slam.pdf new file mode 100644 index 00000000..7ae3cf75 Binary files /dev/null and b/doc/doc/tmp/jnrr-2023/exercice_slam.pdf differ diff --git a/doc/doc/tmp/rencontres-rob/exercice.py b/doc/doc/tmp/rencontres-rob/exercice.py new file mode 100644 index 00000000..66ee813f --- /dev/null +++ b/doc/doc/tmp/rencontres-rob/exercice.py @@ -0,0 +1,26 @@ +from codac import * +import math +import random +import time +import numpy as np + +dt = 0.02 # temporal discretization +tdomain = Interval(0,15) # [t0,tf] + +# System input +u = Trajectory(tdomain, TFunction("3*(sin(t)^2)+t/100"), dt) + +# Actual state trajectory +# Note that this trajectory is unknown of the resolution +x_truth = TrajectoryVector(3) +x_truth[2] = u.primitive() +x_truth[0] = (10*cos(x_truth[2])).primitive() +x_truth[1] = (10*sin(x_truth[2])).primitive() + +beginDrawing() +fig_map = VIBesFigMap("Top view") +fig_map.set_properties(50, 50, 800, 600) +fig_map.add_trajectory(x_truth, "x*", 0, 1, "black") +fig_map.smooth_tube_drawing(True) +fig_map.show(1.) + diff --git a/doc/doc/toctree.rst b/doc/doc/toctree.rst index 771b77de..5825ce4d 100644 --- a/doc/doc/toctree.rst +++ b/doc/doc/toctree.rst @@ -13,6 +13,7 @@ Codac: constraint-programming for robotics /install/01-installation /install/02-start-py-project /install/03-start-cpp-project + /install/04-start-matlab-project .. toctree:: @@ -36,14 +37,21 @@ Codac: constraint-programming for robotics .. toctree:: - :caption: Use cases + :caption: Set4MOST :maxdepth: 2 :titlesonly: - Lie symmetries for guaranteed integ. + Examples -.. /use-cases/set-inversion/index -.. /use-cases/loops/index + +.. toctree:: + :caption: Tutorial JNRR23 + :maxdepth: 2 + :titlesonly: + + JNRR-1. Intervals and contractors + JNRR-2. Static range-only localization + JNRR-3. Towards SLAM .. toctree:: @@ -67,6 +75,18 @@ Codac: constraint-programming for robotics H. Range-only SLAM +.. toctree:: + :caption: Use cases + :maxdepth: 2 + :titlesonly: + + Lie symmetries for guaranteed integ. + Pose estimation with range-only obs. + +.. /use-cases/set-inversion/index +.. /use-cases/loops/index + + .. toctree:: :caption: Development :maxdepth: 1 diff --git a/doc/doc/tutorial-jnrr23/01-basics/img/ctc_dist.png b/doc/doc/tutorial-jnrr23/01-basics/img/ctc_dist.png new file mode 100644 index 00000000..a6fb379a Binary files /dev/null and b/doc/doc/tutorial-jnrr23/01-basics/img/ctc_dist.png differ diff --git a/doc/doc/tutorial-jnrr23/01-basics/index.rst b/doc/doc/tutorial-jnrr23/01-basics/index.rst new file mode 100644 index 00000000..0d4b30ff --- /dev/null +++ b/doc/doc/tutorial-jnrr23/01-basics/index.rst @@ -0,0 +1,462 @@ +.. _sec-tuto-jnrr23-01: + +Lesson JNRR-1: Getting started with intervals and contractors +============================================================= + +The goal of this first lesson is to install Codac on your computer (or use it online), and get used to intervals, constraints and networks of contractors. +This will allow you to perform the state estimation of a static robot between some landmarks by the end of :ref:`Lesson JNRR-2 `. + +.. contents:: Content of this lesson + + +Getting started +--------------- + +To get ready for the tutorial, you need to install the Codac library on your computer. +Please follow the related page of the manual to see how to do it: + +.. toctree:: + :maxdepth: 1 + + /install/01-installation + +Then, depending on your preference between C++ or Python (Matlab now available), you can run some *Hello World!* program to be sure everything is working well: + +.. toctree:: + :maxdepth: 1 + + /install/02-start-py-project + /install/03-start-cpp-project + /install/04-start-matlab-project + + +Start a new project +------------------- + +Start a new project as explained in: + +* :ref:`sec-start-py-project` +* or :ref:`sec-start-cpp-project` +* or :ref:`sec-start-matlab-project` + +.. admonition:: Exercise + + **1.0.** Check that it is displaying a tube in a graphical view and print the tube in the terminal using: + + .. tabs:: + + .. code-tab:: py + + print(x) + + .. code-tab:: c++ + + cout << x << endl; + + .. code-tab:: matlab + + x + + You should see the following output: + + .. code-block:: bash + + Tube [0, 10]↦([-1.448469806203122, 1.500000000000001]), 1000 slices + + | This was to be sure that everything is working well in your environment. + | Now, we will start from the following code: + + .. tabs:: + + .. code-tab:: py + + from codac import * + + # .. next questions will be here + + .. code-tab:: c++ + + #include + + using namespace std; + using namespace codac; + + int main() + { + // .. next questions will be here + } + + .. code-tab:: matlab + + import py.codac.* + + % .. next questions will be here + + +Using intervals for handling uncertainties +------------------------------------------ + +The values involved in robotic problems will be represented by **sets**. This allows to hold in the very same structure both the value (a measurement, or a model parameter) together with the related uncertainty. Therefore, a measurement :math:`x` will be handled by a set, more precisely an **interval**, denoted between brackets: :math:`[x]`. :math:`[x]` is made of two real bounds, :math:`x^-` and :math:`x^+`, and we say that a value :math:`x\in\mathbb{R}` belongs to :math:`[x]=[x^-,x^+]` iff :math:`x^-\leqslant x\leqslant x^+`. + +This can be extended to other types of values such as vectors, matrices or trajectories. Then, + +* reals :math:`x` of :math:`\mathbb{R}` will be enclosed in intervals: :math:`[x]` +* vectors :math:`\mathbf{x}` of :math:`\mathbb{R}^n` will be enclosed in interval-vectors (also called boxes): :math:`[\mathbf{x}]` +* later on, trajectories :math:`x(t)` will belong to tubes: :math:`[x](t)` + +The initial definition of the bounds of these sets will be done according to the **amount of uncertainties** we are considering. For measurements, we will rely on the datasheet of the sensor to define for instance that a measurement :math:`y` will be represented by the interval :math:`[y − 2\sigma, y + 2\sigma]`, where :math:`\sigma` is the standard deviation coming from sensors specifications. In this case, we assume that the interval :math:`[y]` is **guaranteed to contain** the actual but unknown value with a 95% confidence rate. + +The main advantage of this representation is that we will be able to apply lot of **reliable** operations on these sets while preserving the actual but unknown values. This means that we will never lose a feasible solution in the initial sets throughout the operations we will perform. This is done by performing the computations on the bounds of the sets. For instance, the difference of two intervals is also an interval defined by: :math:`[x]-[y]=[x^--y^+,x^+-y^-]`. Mathematically, we can prove that :math:`\forall x\in[x]` and :math:`\forall y\in[y]`, we have :math:`(x-y)\in([x]-[y])`. + +| *Example:* :math:`[3,4]-[2,6]=[-3,2]`. +| If we take :math:`x=3\in[3,4]` and :math:`y=5\in[2,6]`, we check that :math:`-2\in[-3,2]`. + +These simple operations on intervals can be extended to elementary functions such as :math:`\cos`, :math:`\exp`, :math:`\tan`, *etc*. +It must be emphasized that there is no need to make linearizations when dealing with **non-linear functions**. +Sometimes, when functions are monotonic, the computation is simple: :math:`\exp([x])=[\exp(x^-),\exp(x^+)]`. Otherwise, several algorithms and libraries exist to allow any mathematical operations on intervals such as :math:`\sin([x])`, :math:`\sqrt{([x])}`, *etc*. + +The asset of reliability coming with interval analysis will help us to estimate difficult solutions and **make proofs**. + + +Hello Interval Analysis! +------------------------ + +Codac is using C++/Python objects to represent intervals and boxes [#f1]_: + +* ``Interval(lb, ub)`` will be used to create an interval :math:`[x]=[\textrm{lb},\textrm{ub}]`. There exists predefined values for intervals. Here are some examples of ``Interval`` objects: + + .. tabs:: + + .. code-tab:: py + + x = Interval() # [-∞,∞] (default value) + x = Interval(0, 10) # [0,10] + x = Interval(1, oo) # [1,∞] + x = Interval(-oo,3) # [-∞,3] + x = Interval.EMPTY_SET # ∅ + # ... + + .. code-tab:: c++ + + Interval x; // [-∞,∞] (default value) + Interval x(0, 10); // [0,10] + Interval x(1, oo); // [1,∞] + Interval x(-oo, 3); // [-∞,3] + Interval x = Interval::EMPTY_SET; // ∅ + // ... + + +* | ``IntervalVector(n)`` is used for :math:`n`-d vectors of intervals, also called *boxes*. + | For instance: + + .. tabs:: + + .. code-tab:: py + + x = IntervalVector(2, [-1,3]) # creates [x]=[-1,3]×[-1,3]=[-1,3]^2 + y = IntervalVector([[3,4],[4,6]]) # creates [y]= [3,4]×[4,6] + z = IntervalVector(3, [0,oo]) # creates [z]=[0,∞]^3 + w = IntervalVector(y) # creates a copy: [w]=[y] + + v = (0.42,0.42,0.42) # one vector (0.42;0.42;0.42) + iv = IntervalVector(v) # creates one box that wraps v: + # [0.42,0.42]×[0.42,0.42]×[0.42,0.42] + + .. code-tab:: c++ + + IntervalVector x(2, Interval(-1,3)); // creates [x]=[-1,3]×[-1,3]=[-1,3]^2 + IntervalVector y{{3,4},{4,6}}; // creates [y]= [3,4]×[4,6] + IntervalVector z(3, Interval(0,oo)); // creates [z]=[0,∞]^3 + IntervalVector w(y); // creates a copy: [w]=[y] + + Vector v(3, 0.42); // one vector (0.42;0.42;0.42) + IntervalVector iv(v); // creates one box that wraps v: + // [0.42,0.42]×[0.42,0.42]×[0.42,0.42] + + One can access vector components as we do classically: + + .. tabs:: + + .. code-tab:: py + + x[1] = Interval(0,10) # updates to [x]=[-1,3]×[0,10] + + .. code-tab:: c++ + + x[1] = Interval(0,10); // updates to [x]=[-1,3]×[0,10] + + +.. admonition:: Technical documentation + + For full details about ``Interval`` and ``IntervalVector`` objects, please read the :ref:`sec-manual-intervals` page of the user manual. + + +.. admonition:: Exercise + + **1.1.** Let us consider two intervals :math:`[x]=[8,10]` and :math:`[y]=[1,2]`. Without coding the operation, what would be the result of :math:`[x]/[y]` (:math:`[x]` divided by :math:`[y]`)? Remember that the result of this interval-division is also an interval enclosing all feasible divisions. + + **1.2.** In your new project, compute and print the following simple operations on intervals: + + * :math:`[-2,4]\cdot[1,3]` + * :math:`[8,10]/[-1,0]` + * :math:`[-2,4]\sqcup[6,7]` with operator ``|`` + * :math:`\max([2,7],[1,9])` + * :math:`\max(\varnothing,[1,2])` + * :math:`\cos([-\infty,\infty])` + * :math:`[-1,4]^2` with function ``sqr()`` + * :math:`([1,2]\cdot[-1,3]) + \max([1,3]\cap[6,7],[1,2])` + + | Note that :math:`\sqcup` is the hull union (``|``), *i.e.*, :math:`[x]\sqcup[y] = [[x]\cup[y]]`. + | *For instance:* :math:`[-1,2]\sqcup[4,6]=[-1,6]` + + + **1.3.** Create a 2d box :math:`[\mathbf{y}]=[0,\pi]\times[-\pi/6,\pi/6]` and print the result of :math:`|[\mathbf{y}]|` with ``abs()``. + +.. hint:: + + .. rubric:: How to use :math:`\pi`? + + .. tabs:: + + .. code-tab:: py + + # In Python, you can use the math module: + import math + x = math.pi + + .. code-tab:: c++ + + // In C++, you can use : + #include + double x = M_PI; + + Note that in this code, the variable ``x`` is not the exact :math:`\pi`! Of course, the mathematical one cannot be represented in a computer. But with intervals, we can manage reliable representations of floating point numbers. :ref:`See more `. + + +Functions, :math:`f([x])` +------------------------- + +Custom functions can be used on sets. For instance, to compute: + +.. math:: + + f(x)=x^2+2x-\exp(x), + +a ``Function`` object can be created by ``Function("", "", ..., "")`` and then evaluated over the set :math:`[x]`: + +.. tabs:: + + .. code-tab:: py + + x = Interval(-2,2) + f = Function("x", "x^2+2*x-exp(x)") + y = f.eval(x) + + .. code-tab:: c++ + + Interval x(-2,2); + Function f("x", "x^2+2*x-exp(x)"); + Interval y = f.eval(x); + +The first arguments of the function (only one in the above example) are its input variables. The last argument is the expression of the output. The result is the set of images of all defined inputs through the function: :math:`[f]([x])=[\{f(x)\mid x\in[x]\}]`. + +We can also define vector input variables and access their components in the function definition: + +.. tabs:: + + .. code-tab:: py + + f = Function("x[2]", "cos(x[0])^2+sin(x[1])^2") # the input x is a 2d vector + + .. code-tab:: c++ + + Function f("x[2]", "cos(x[0])^2+sin(x[1])^2"); // the input x is a 2d vector + +.. admonition:: Exercise + + **1.4.** For our robotic applications, we often need to define the distance function :math:`g`: + + .. math:: + + g(\mathbf{x},\mathbf{b})=\sqrt{\displaystyle(x_1-b_1)^2+(x_2-b_2)^2}, + + where :math:`\mathbf{x}\in\mathbb{R}^2` would represent for instance the 2d position of a robot, and :math:`\mathbf{b}\in\mathbb{R}^2` the 2d location of some landmark. Create :math:`g` and compute the interval distance :math:`[d]` between the boxes :math:`[\mathbf{x}]=[0,0]\times[0,0]` and :math:`[\mathbf{b}]=[3,4]\times[2,3]`. Note that in the library, the ``.eval()`` of functions only takes one argument: we have to concatenate the boxes :math:`[\mathbf{x}]` and :math:`[\mathbf{b}]` into one 4d interval-vector :math:`[\mathbf{c}]` and then compute :math:`g([\mathbf{c}])`. The concatenation can be done with the ``cart_prod`` function, :ref:`see an example here `. + + Print the result that you obtain for :math:`[d]=g([\mathbf{x}],[\mathbf{b}])`. + + +Graphics +-------- + +The graphical tool `VIBes `_ has been created to Visualize Intervals and BoxES. It is compatible with simple objects such as ``Interval`` and ``IntervalVector``. Its features have been extended in the Codac library with objects such as ``VIBesFigMap``. + +.. admonition:: Exercise + + **1.5.** Create a view with: + + .. tabs:: + + .. code-tab:: py + + beginDrawing() + fig = VIBesFigMap("Map") + fig.set_properties(50, 50, 400, 400) # position and size + + # ... draw objects here + + fig.show() # display all items of the figure + endDrawing() + + .. code-tab:: c++ + + vibes::beginDrawing(); + VIBesFigMap fig("Map"); + fig.set_properties(50, 50, 400, 400); // position and size + + // ... draw objects here + + fig.show(); // display all items of the figure + vibes::endDrawing(); + + | **1.6.** Before the ``.show()`` method, draw the boxes :math:`[\mathbf{x}]` and :math:`[\mathbf{b}]` with the ``fig.draw_box(..)`` method. The computed interval range :math:`[d]` can be displayed as a ring centered on :math:`\mathbf{x}=(0,0)`. The ring will contain the set of all positions that are :math:`d`-distant from :math:`\mathbf{x}=(0,0)`, with :math:`d\in[d]`. + + To display each bound of the ring, you can use ``fig.draw_circle(x, y, rad)`` where ``x``, ``y``, ``rad`` are *double* values. + + .. hint:: + + To access *double* bounds of an interval object ``x``, you can use the ``x.lb()``/``x.ub()`` methods for lower and upper bounds. + + | **1.7.** Now, repeat the operation with :math:`[\mathbf{x}]=[-0.1,0.1]\times[-0.1,0.1]`. You can for instance use the ``.inflate(0.1)`` method on ``x``. + | Is the result reliable, according to the sets :math:`[\mathbf{x}]` and :math:`[\mathbf{b}]`? You may display the box :math:`([\mathbf{x}]+[\mathbf{b}])` to understand how the reliable interval distance is computed. + + +.. from codac import * +.. +.. x = IntervalVector([[0,0],[0,0]]) +.. b = IntervalVector([[3,4],[2,3]]) +.. print(b) +.. +.. x.inflate(0.1) +.. +.. f = Function("a[2]", "b[2]", "sqrt((a[0]-b[0])^2+(a[1]-b[1])^2)") +.. +.. box = cart_prod(x,b) +.. r = f.eval(box) +.. +.. beginDrawing() +.. fig = VIBesFigMap("Map") +.. fig.set_properties(50, 50, 400, 400) # position and size +.. fig.draw_box(x, "red") +.. fig.draw_box(b) +.. fig.draw_box(b+x, "blue") +.. fig.draw_ring(0,0,r) +.. fig.show() # display all items of the figure +.. endDrawing() + + +.. admonition:: Technical documentation + + For full details about graphical features, please read the :ref:`sec-manual-vibes` page of the user manual. + + .. rubric:: Want to use colors? Here is an example you can try: + + .. tabs:: + + .. code-tab:: py + + fig.draw_box(x, "red[yellow]") # red: edge color of the box, yellow: fill color + + .. code-tab:: c++ + + fig.draw_box(x, "red[yellow]"); // red: edge color of the box, yellow: fill color + + +Contractors, :math:`\mathcal{C}([x])` +------------------------------------- + +This was an initial overview of what is Interval Analysis. Now, we will introduce concepts from Constraint Programming and see how the two approaches can be coupled for solving problems. + +In robotics, **constraints** are coming from the equations of the robot. They can be for instance the evolution function :math:`\mathbf{f}` or the observation equation with :math:`\mathbf{g}`. In the case of :abbr:`SLAM (Simultaneous Localization And Mapping)`, we may also define a constraint to express the inter-temporal relation between different states :math:`\mathbf{x}_1`, :math:`\mathbf{x}_2` at times :math:`t_1`, :math:`t_2`, for instance when a landmark has been seen two times. + +Now, we want to apply the constraints in order to solve our problem. In the Constraint Programming community, we apply constraints on **domains** that represent sets of feasible values. The previously mentioned sets (intervals, boxes, tubes) will be used as domains. + +We will use **contractors** to implement constraints on sets. They are mathematical operators used to *contract* (reduce) a set, for instance a box, without losing any feasible solution. This way, contractors can be applied safely any time we want on our domains. + +In Codac, the contractors are also defined by C++/Python objects and are prefixed with ``Ctc``. For this lesson, we will use the ``CtcFunction`` class to define a contractor according to a function :math:`f`. Note that the resulting contractor will aim at solving a constraint in the form :math:`f(\mathbf{x})=0`. This contractor has to be instantiated from a ``Function`` object defining the constraint. For instance, the simple constraint :math:`(x+y=a)` is expressed as :math:`f(x,y,a)=x+y-a=0`, and can be implemented as a contractor :math:`\mathcal{C}_+` with: + +.. tabs:: + + .. code-tab:: py + + ctc_add = CtcFunction(Function("x", "y", "a", "x+y-a")) + + .. code-tab:: c++ + + CtcFunction ctc_add(Function("x", "y", "a", "x+y-a")); + +.. admonition:: Exercise + + **1.8.** Define a contractor :math:`\mathcal{C}_\textrm{dist}` related to the distance constraint between two 2d positions :math:`\mathbf{x}` and :math:`\mathbf{b}\in\mathbb{R}^2`. We will use the distance function previously defined, but in the form :math:`f(\mathbf{x},\mathbf{b},d)=0`. + +| The contractor is then simply added to a **Contractor Network** (CN) that will manage the constraints on the different variables for solving the problem. +| For instance, we can use the previously defined :math:`\mathcal{C}_+` as: + +.. tabs:: + + .. code-tab:: py + + x = Interval(0,1) + y = Interval(-2,3) + a = Interval(1,20) + + cn = ContractorNetwork() # Creating a Contractor Network + cn.add(ctc_add, [x, y, a]) # Adding the C+ contractor to the network, + # applied on three domains listed between braces + cn.contract() + + # The three domains are then contracted as: + # x=[0, 1], y=[0, 3], a=[1, 4] + + .. code-tab:: c++ + + Interval x(0,1), y(-2,3), a(1,20); + + ContractorNetwork cn; // Creating a Contractor Network + cn.add(ctc_add, {x, y, a}); // Adding the C+ contractor to the network, + // applied on three domains listed between braces + cn.contract(); + + // The three domains are then contracted as: + // x=[0, 1], y=[0, 3], a=[1, 4] + +Note that one contractor can be added several times in the CN. This is useful to apply several constraints implemented by the same operator, on different sets of variables. + + +.. admonition:: Exercise + + | **1.9.** Define a Contractor Network to implement three distance constraints. + | Check the results with :math:`\mathcal{C}_\textrm{dist}([\mathbf{x}],[\mathbf{b}^i],[d])`, :math:`i\in\{1,2,3\}` and + + * :math:`[d]=[7,8]` + * :math:`[\mathbf{x}]=[0,0]^2` + * :math:`[\mathbf{b}^1]=[1.5,2.5]\times[4,11]` + * :math:`[\mathbf{b}^2]=[3,4]\times[4,6.5]` + * :math:`[\mathbf{b}^3]=[5,7]\times[5.5,8]` + + We recall that the same :math:`\mathcal{C}_\textrm{dist}` object can appear several times in the CN. + + Draw the :math:`[\mathbf{b}^i]` boxes (``.draw_box()``) and :math:`[d]` (``.draw_circle()``) before and after the contractions, in order to assess the contracting effects. + You should obtain this figure: + + .. figure:: img/ctc_dist.png + :width: 500px + + As you can see, the four domains have been contracted after the ``.contract()`` method: even the bounded range :math:`[d]` has been reduced thanks to the knowledge provided by the boxes. In Constraint Programming, we only define the constraints of the problem and let the resolution propagate the information as much as possible. + + +We now have all the material to compute a solver for state estimation in the next section. + + +.. rubric:: Footnotes + +.. [#f1] C++ objects originates from the `IBEX library `_. \ No newline at end of file diff --git a/doc/doc/tutorial-jnrr23/02-static-rangeonly/img/final_result.png b/doc/doc/tutorial-jnrr23/02-static-rangeonly/img/final_result.png new file mode 100644 index 00000000..edbc192f Binary files /dev/null and b/doc/doc/tutorial-jnrr23/02-static-rangeonly/img/final_result.png differ diff --git a/doc/doc/tutorial-jnrr23/02-static-rangeonly/img/fixedpoint_animation.gif b/doc/doc/tutorial-jnrr23/02-static-rangeonly/img/fixedpoint_animation.gif new file mode 100644 index 00000000..fd701742 Binary files /dev/null and b/doc/doc/tutorial-jnrr23/02-static-rangeonly/img/fixedpoint_animation.gif differ diff --git a/doc/doc/tutorial-jnrr23/02-static-rangeonly/img/prior_result.png b/doc/doc/tutorial-jnrr23/02-static-rangeonly/img/prior_result.png new file mode 100644 index 00000000..27feaeea Binary files /dev/null and b/doc/doc/tutorial-jnrr23/02-static-rangeonly/img/prior_result.png differ diff --git a/doc/doc/tutorial-jnrr23/02-static-rangeonly/index.rst b/doc/doc/tutorial-jnrr23/02-static-rangeonly/index.rst new file mode 100644 index 00000000..f38a0e54 --- /dev/null +++ b/doc/doc/tutorial-jnrr23/02-static-rangeonly/index.rst @@ -0,0 +1,211 @@ +.. _sec-tuto-jnrr23-02: + +Lesson JNRR-2: Static range-only localization +============================================= + +We now have all the material to compute a solver for state estimation. + +.. contents:: Content of this lesson + + +Problem statement +----------------- + +Our goal is to deal with the problem of localizing a robot of state :math:`\mathbf{x}\in\mathbb{R}^2` that measures distances :math:`d^{k}` from three landmarks :math:`\mathbf{b}^{k}`, :math:`k\in\{1,2,3\}`. We consider uncertainties on both the measurements and the location of the landmarks, which means that :math:`d^{k}\in[d^{k}]` and :math:`\mathbf{b}^{k}\in[\mathbf{b}^{k}]`. For now, the robot does not move. + +.. figure:: img/prior_result.png + :width: 500px + + The :math:`\mathbf{b}^{k}` landmarks are depicted in orange while range-only measurements with bounded uncertainties are drawn by rings. + +Our problem corresponds to the following Constraint Network: + +.. math:: + + \left\{ + \begin{array}{l} + \textrm{Variables:}~~ \mathbf{x}, \mathbf{b}^{1}, \mathbf{b}^{2}, \mathbf{b}^{3}, d^{1}, d^{2}, d^{3}\\ + \textrm{Constraints:}~~ \\ + -~ \mathcal{L}_{\textrm{dist}}^{1}\left(\mathbf{x},\mathbf{b}^{1},d^{1}\right) \\ + -~ \mathcal{L}_{\textrm{dist}}^{2}\left(\mathbf{x},\mathbf{b}^{2},d^{2}\right) \\ + -~ \mathcal{L}_{\textrm{dist}}^{3}\left(\mathbf{x},\mathbf{b}^{3},d^{3}\right) \\ + \textrm{Domains:}~~ [\mathbf{x}], [\mathbf{b}^{1}], [\mathbf{b}^{2}], [\mathbf{b}^{3}], [d^{1}], [d^{2}], [d^{3}] + \end{array}\right. + +This formalization can be seen in the literature and summarizes the problem in terms of variables, constraints on the variables, and domains for their sets of feasible values. As explained in the previous section, we will represent domains by means of intervals and boxes (interval vectors). In addition, constraints will be applied with contractors. + + +The :math:`\mathcal{L}_{\textrm{dist}}^{k}` is the distance constraint from a landmark :math:`\mathbf{b}^{k}`. It links a measurement :math:`d^{k}` to the state :math:`\mathbf{x}` with: + +.. math:: + + \mathcal{L}_{\textrm{dist}}^{k}:~d^{k}=\sqrt{\left(x_1-b_1^{k}\right)^2+\left(x_2-b_2^{k}\right)^2}. + + +Solving the problem +------------------- + +The following code provides a simulation of random landmarks and related range-only measurements: + +.. tabs:: + + .. code-tab:: py + + from codac import * + import math + + # Truth (unknown pose) + x_truth = [0,0,math.pi/6] # (x,y,heading) + + # Creating random map of landmarks + map_area = IntervalVector(2, [-8,8]) + v_b = DataLoader.generate_landmarks_boxes(map_area, nb_landmarks = 3) + + # The following function generates a set of [range]x[bearing] values + v_obs = DataLoader.generate_static_observations(x_truth, v_b, False) + + # We keep range-only observations from v_obs, and add uncertainties + v_d = [] + for obs in v_obs: + d = obs[0].inflate(0.1) # adding uncertainties: [-0.1,0.1] + v_d.append(d) + + # Set of feasible positions for x: x ϵ [-∞,∞]×[-∞,∞] + x = IntervalVector(2) # this is equivalent to: IntervalVector([[-oo,oo],[-oo,oo]]) + + # ... + + .. code-tab:: c++ + + #include + + using namespace std; + using namespace codac; + + int main() + { + // Truth (unknown pose) + Vector x_truth({0.,0.,M_PI/6.}); // (x,y,heading) + + // Creating random map of landmarks + int nb_landmarks = 3; + IntervalVector map_area(2, Interval(-8.,8.)); + vector v_b = + DataLoader::generate_landmarks_boxes(map_area, nb_landmarks); + + // The following function generates a set of [range]x[bearing] values + vector v_obs = + DataLoader::generate_static_observations(x_truth, v_b, false); + + // We keep range-only observations from v_obs, and add uncertainties + vector v_d; + for(auto& obs : v_obs) + v_d.push_back(obs[0].inflate(0.1)); // adding uncertainties: [-0.1,0.1] + + // Set of feasible positions for x: x ϵ [-∞,∞]×[-∞,∞] + IntervalVector x(2); + + // ... + + +Finally, the graphical functions are given by: + +.. tabs:: + + .. code-tab:: py + + # ... + + beginDrawing() + + fig = VIBesFigMap("Map") + fig.set_properties(50, 50, 600, 600) + + for b in v_b: + fig.add_beacon(b.mid(), 0.2) + + for i in range(0,len(v_d)): + fig.draw_ring(v_b[i][0].mid(), v_b[i][1].mid(), v_d[i], "gray") + + fig.draw_vehicle(x_truth, size=0.7) + fig.draw_box(x) # estimated position + fig.show() + + endDrawing() + + .. code-tab:: c++ + + // ... + + vibes::beginDrawing(); + + VIBesFigMap fig("Map"); + fig.set_properties(50, 50, 600, 600); + + for(const auto& b : v_b) + fig.add_beacon(b.mid(), 0.2); + + for(int i = 0 ; i < nb_landmarks ; i++) + fig.draw_ring(v_b[i][0].mid(), v_b[i][1].mid(), v_d[i], "gray"); + + fig.draw_vehicle(x_truth, 0.7); // last param: vehicle size + fig.draw_box(x); // estimated position + fig.show(); + + vibes::endDrawing(); + } + + +.. admonition:: Exercise + + **2.1.** Before the code related to the graphical part, compute the state estimation of the robot by contracting the box :math:`[\mathbf{x}]` initialized to :math:`[-\infty,\infty]^2` with a Contractor Network: + + * :math:`[\mathbf{x}]` represents the unknown 2d position of the robot + * ``v_d`` is the set of bounded measurements :math:`\{[d^{1}],[d^{2}],[d^{3}]\}` + * ``v_b`` is the set of landmarks with bounded positions :math:`\{[\mathbf{b}^{1}],[\mathbf{b}^{2}],[\mathbf{b}^{3}]\}` + + For this, you can use the :math:`\mathcal{C}_{\textrm{dist}}` contractor you defined in the previous section. + + You should obtain a figure similar to this: + + .. figure:: img/final_result.png + :width: 500px + + Range-only localization: expected result. The black painted box represents the set of feasible positions for our robot. + + + Due to the randomness of the landmarks, the geometry is sometimes bad and does not allow an accurate contraction: symmetrical solutions are possible, and the box :math:`[\mathbf{x}]` encloses them all. You can execute the code several times to see how the geometry influences the result. + + +How does it work? +----------------- + +.. rubric:: Combining the constraints + +The Contractor Network you have defined managed the contractions provided by the three :math:`\mathcal{C}_{\textrm{dist}}` contractors. + +Each constraint alone would not allow a good contraction, since it would contract :math:`[\mathbf{x}]` to the box enclosing the circle corresponding to :math:`d^k`. It is the intersection of the three constraints that makes the approach powerful. + +.. rubric:: Fixed point resolution + +There are **dependencies between the constraints** that all act on the same variable :math:`\mathbf{x}`. +The Contractor Network has then made a **fixed point resolution method** for solving the problem. + +When a :math:`\mathcal{C}_{\textrm{dist}}` contractor reduces the box :math:`[\mathbf{x}]`, it may raise new contraction possibilities coming from the other constraints. It becomes interesting to call again the previous contractors (start another iteration) in order to take benefit from any contraction. An iterative resolution process is then used, where the contractors are called until a fixed point has been reached. By *fixed point* we mean that none of the domains :math:`[\mathbf{x}]` and :math:`[d^{k}]` has been contracted during a complete iteration. + +The following figure provides the synoptic of this state estimation, performed by the Contractor Network. In this example, constraints have been propagated over 7 iterations in a very short amount of time. + +.. figure:: img/fixedpoint_animation.gif + :width: 500px + + + + +End of first step! +------------------ + +That's about all for static localization! + +.. You can submit your answers for the questions of Lessons A and B to the `MOOC platform `_ so that we can .. evaluate them for the diploma. + +Next lesson will introduce other concepts related to trajectories and differential equations. Our goal will be to build a SLAM solver. \ No newline at end of file diff --git a/doc/doc/tutorial-jnrr23/03-towards-slam/img/final_result.png b/doc/doc/tutorial-jnrr23/03-towards-slam/img/final_result.png new file mode 100644 index 00000000..edbc192f Binary files /dev/null and b/doc/doc/tutorial-jnrr23/03-towards-slam/img/final_result.png differ diff --git a/doc/doc/tutorial-jnrr23/03-towards-slam/img/fixedpoint_animation.gif b/doc/doc/tutorial-jnrr23/03-towards-slam/img/fixedpoint_animation.gif new file mode 100644 index 00000000..fd701742 Binary files /dev/null and b/doc/doc/tutorial-jnrr23/03-towards-slam/img/fixedpoint_animation.gif differ diff --git a/doc/doc/tutorial-jnrr23/03-towards-slam/img/prior_result.png b/doc/doc/tutorial-jnrr23/03-towards-slam/img/prior_result.png new file mode 100644 index 00000000..27feaeea Binary files /dev/null and b/doc/doc/tutorial-jnrr23/03-towards-slam/img/prior_result.png differ diff --git a/doc/doc/tutorial-jnrr23/03-towards-slam/index.rst b/doc/doc/tutorial-jnrr23/03-towards-slam/index.rst new file mode 100644 index 00000000..2e9aa4d4 --- /dev/null +++ b/doc/doc/tutorial-jnrr23/03-towards-slam/index.rst @@ -0,0 +1,8 @@ +.. _sec-tuto-jnrr23-03: + +Lesson JNRR-3: towards SLAM +=========================== + +The last lesson is available in the following PDF file: + +http://codac.io/tmp/jnrr-2023/exercice_slam.pdf \ No newline at end of file diff --git a/doc/doc/tutorial-jnrr23/index.rst b/doc/doc/tutorial-jnrr23/index.rst new file mode 100644 index 00000000..a70be4b1 --- /dev/null +++ b/doc/doc/tutorial-jnrr23/index.rst @@ -0,0 +1,15 @@ +.. _sec-tuto-jnrr23-main-page: + +################ +Tutorial: JNRR23 +################ + + +For this tutorial, we propose three lessons: + +.. toctree:: + :maxdepth: 1 + + /tutorial-jnrr23/01-basics/index + /tutorial-jnrr23/02-static-rangeonly/index + /tutorial-jnrr23/03-towards-slam/index \ No newline at end of file diff --git a/doc/doc/tutorial-set4most/01-examples/index.rst b/doc/doc/tutorial-set4most/01-examples/index.rst new file mode 100644 index 00000000..9c54f843 --- /dev/null +++ b/doc/doc/tutorial-set4most/01-examples/index.rst @@ -0,0 +1,10 @@ +.. _sec-tuto-set4most-01-main-page: + +############################# +Tutorial: Set4MOST - Examples +############################# + +* `Triskelion `_ +* `Range-only localization `_ +* `Walls localization `_ +* `SLAM `_ \ No newline at end of file diff --git a/doc/doc/tutorial-set4most/01-examples/range-only-loc.py b/doc/doc/tutorial-set4most/01-examples/range-only-loc.py new file mode 100644 index 00000000..25b85a28 --- /dev/null +++ b/doc/doc/tutorial-set4most/01-examples/range-only-loc.py @@ -0,0 +1,74 @@ +from codac import * +import math +import random +import time +import numpy as np + + +dt = 0.02 +iteration_dt = 0.2 +tdomain = Interval(0,15) # [t0,tf] + +# System input +u = Trajectory(tdomain, TFunction("3*(sin(t)^2)+t/100"), dt) + +# Actual trajectories (state + derivative) +# Note that these trajectories are unknown of the resolution +v_truth = TrajectoryVector(3) +x_truth = TrajectoryVector(3) +v_truth[2] = u +x_truth[2] = v_truth[2].primitive() +v_truth[0] = 10*cos(x_truth[2]) +v_truth[1] = 10*sin(x_truth[2]) +x_truth[0] = v_truth[0].primitive() +x_truth[1] = v_truth[1].primitive() + +beginDrawing() +fig_map = VIBesFigMap("slam") +fig_map.set_properties(50, 50, 1200, 600) +#fig_map.axis_limits(IntervalVector([[-34.8, 40.2],[-17.3,20.2]])) +fig_map.add_trajectory(x_truth, "truth", 0, 1, "black") +fig_map.smooth_tube_drawing(True) +fig_map.show(1.) + +########################################### +# Section C: Deadreckoning # +########################################### + +# Bounded trajectories (dead reckoning) +v = TubeVector(tdomain, dt, 3) +x = TubeVector(tdomain, dt, 3) + +# Heading measurement with bounded uncertainties +x[2] = Tube(x_truth[2], dt) + Interval(-0.03,0.03) +x.set([0,0,0], 0.) # setting a vector value at t=0 + +ctc_f = CtcFunction(Function("x[3]", "v[3]", "(v[0]-10*cos(x[2]) ; v[1]-10*sin(x[2]))")) +ctc.deriv # object already instanciated in the library + +cn = ContractorNetwork() +cn.add(ctc_f, [x,v]) +cn.add(ctc.deriv, [x,v]) + +cn.contract(True) + +fig_map.add_tube(x, "x", 0, 1) +fig_map.show(1.) + +########################################### +# Section D: Range-only localisation # +########################################### + +v_b = [ (6,12), (-2,-5), (-3,20), (3,4), (-10,0) ] + +for ti in np.arange(0,15): + k = random.randint(0,len(v_b)-1) # a random landmark is perceived + d = sqrt(sqr(x_truth(ti)[0]-v_b[k][0])+sqr(x_truth(ti)[1]-v_b[k][1])) + d += Interval(-0.03,0.03) + pi = IntervalVector(3) + cn.add(ctc.eval, [ti,pi,x,v]) + cn.add(ctc.dist, [pi[0],pi[1],v_b[k][0],v_b[k][1],d]) + +cn.contract(True) +fig_map.add_landmarks(v_b, 0.4) +fig_map.show(1.) \ No newline at end of file diff --git a/doc/doc/tutorial-set4most/01-examples/slam.py b/doc/doc/tutorial-set4most/01-examples/slam.py new file mode 100644 index 00000000..63352a61 --- /dev/null +++ b/doc/doc/tutorial-set4most/01-examples/slam.py @@ -0,0 +1,112 @@ +from codac import * +import math +import random +import time + + +# =========== CREATING DATA =========== + +dt = 0.02 +iteration_dt = 0.2 +tdomain = Interval(0,15) # [t0,tf] + +# Initial pose x0=(0,0,2) +x0 = (0,0,2) + +# System input +u = Trajectory(tdomain, TFunction("3*(sin(t)^2)+t/100"), dt) + +# Noise +#i_n = Interval(-0.03,0.03) # the noises are known to be bounded by i_n + +# Actual trajectories (state + derivative) +v_truth = TrajectoryVector(3) +x_truth = TrajectoryVector(3) +v_truth[2] = u +x_truth[2] = v_truth[2].primitive() + x0[2] +v_truth[0] = 10*cos(x_truth[2]) +v_truth[1] = 10*sin(x_truth[2]) +x_truth[0] = v_truth[0].primitive() + x0[0] +x_truth[1] = v_truth[1].primitive() + x0[1] + +# Bounded trajectories (dead reckoning) +v = TubeVector(tdomain, dt, 3) +x = TubeVector(tdomain, dt, 3) +#v[2] = Tube(u, dt).inflate(i_n.rad()) # command u with bounded uncertainties +x[2] = Tube(x_truth[2], dt) + Interval(-0.03,0.03) # heading measurement with bounded uncertainties +#v[0] = 10*cos(x[2]) +#v[1] = 10*sin(x[2]) +#x = v.primitive()+IntervalVector(x0) # dead reckoning +x.set([0,0,2],0.) +print(x_truth(0.)) +# Set of landmarks +v_m = [ (6,12), (-2,-5), (-3,20), (3,4), (-10,0) ] + +ctc_f = CtcFunction( + Function("v[3]", "x[3]", + "(v[0]-10*cos(x[2]) ; v[1]-10*sin(x[2]) )")) + +# =========== GRAPHICS =========== + +beginDrawing() + +fig_map = VIBesFigMap("slam") +fig_map.set_properties(50, 50, 1200, 600) +fig_map.add_tube(x, "x", 0, 1) +fig_map.add_trajectory(x_truth, "truth", 0, 1)#, "white") +fig_map.smooth_tube_drawing(True) +fig_map.add_landmarks(v_m, 0.4) +fig_map.show(1.) + + +# =========== CONTRACTOR NETWORK =========== + +v_m_boxes = [IntervalVector(2) for _ in v_m] + +# Contractor Network: + +cn = ContractorNetwork() +cn.add(ctc_f, [v,x]) +cn.add(ctc.deriv, [x,v]) + +t = tdomain.lb() +prev_t_obs = t + +while t < tdomain.ub(): + + if t-prev_t_obs > 5*dt: # new observation each 2*delta + + # Creating new observation to a random landmark + + landmark_id = random.randint(0,len(v_m)-1) # a random landmark is perceived + + pos_x = x_truth(t)[0:2] + pos_b = v_m[landmark_id] + + yi = Interval(sqrt(pow(pos_x[0]-pos_b[0],2)+pow(pos_x[1]-pos_b[1],2))) + yi.inflate(0.03) # adding range bounded uncertainty + + prev_t_obs = t + + # Adding related observation constraints to the network + + # Alias (for ease of reading) + b = v_m_boxes[landmark_id] + + # Intermediate variables + ti = Interval(t) + xi = IntervalVector(3) + + # Contractors + cn.add(ctc.eval, [ti, xi, x, v]) + cn.add(ctc.dist, [xi[0], xi[1], b[0], b[1], yi]) + + t+=dt + +cn.contract(True) # lets the solver run the remaining contractions + +fig_map.show() +for b in v_m_boxes: + fig_map.draw_box(b) + +endDrawing() \ No newline at end of file diff --git a/doc/doc/tutorial-set4most/01-examples/triskelion.py b/doc/doc/tutorial-set4most/01-examples/triskelion.py new file mode 100644 index 00000000..0ba38714 --- /dev/null +++ b/doc/doc/tutorial-set4most/01-examples/triskelion.py @@ -0,0 +1,53 @@ +from codac import * +from codac.unsupported import * +from math import pi +from vibes import vibes + +class SepDist(SepFwdBwd): + + def __init__(self, c, intv_r): + SepFwdBwd.__init__(self, + Function('x[2]', 'sqrt(sqr(x[0]-'+str(c[0])+')+sqr(x[1]-'+str(c[1])+'))'), + intv_r) + +class Sep2dRot(SepInverse): + + def __init__(self, sep, a): + SepInverse.__init__(self, sep, + Function('x[2]', '(x[0]*cos('+str(a)+')-x[1]*sin('+str(a)+');'+ + 'x[0]*sin('+str(a)+')+x[1]*cos('+str(a)+'))')) + +class SepTranslate(SepInverse): + + def __init__(self, sep, c): + SepInverse.__init__(self, sep, + Function('x[2]', '(x[0]-'+str(c[0])+';x[1]-'+str(c[1])+')')) + + +a1,a2 = 0, 2*pi/3 +c1 = [cos(a1)*3.9,sin(a1)*3.9] +c2 = [cos(a1)*5.3,sin(a1)*5.3] +c3 = [cos(a2)*3.9,sin(a2)*3.9] + +s_legs = SepDist(c1, Interval(0,4.8)) +s_legs &= SepDist(c2, Interval(3,oo)) +s_ = SepPolarXY(Interval(4.8,6), Interval(a1).inflate(0.5)) +s_legs &= SepNot(Sep2dRot(SepTranslate(s_,c3), a1)) +s_legs &= SepNot(SepDist(c3, Interval(0,5))) + +s_heart = SepDist(c3, Interval(0,4.8)) + +for i in range(2,5,2): + s_legs |= Sep2dRot(s_legs,i*pi/3) + s_heart &= Sep2dRot(s_heart,i*pi/3) + +s_triskel = s_heart | s_legs + + +x = IntervalVector([[-10,10],[-10,10]]) + +vibes.beginDrawing() +vibes.newFigure("Triskelion") +SIVIA(x, s_triskel, 0.05, fig_name="Triskelion") +vibes.setFigureProperties({"x":100, "y":100, "width":900, "height":900}) +endDrawing() \ No newline at end of file diff --git a/doc/doc/tutorial-set4most/01-examples/walls-loc.py b/doc/doc/tutorial-set4most/01-examples/walls-loc.py new file mode 100644 index 00000000..2e2a5aeb --- /dev/null +++ b/doc/doc/tutorial-set4most/01-examples/walls-loc.py @@ -0,0 +1,117 @@ +from codac import * +import numpy as np + +# =================== Parameters, truth and data =================== + +beginDrawing() + +v_corners = [ + [-4.5,6],[2.5,6.5],[2.8,-1.2],[4.5,-0.8],[4.8,4], + [11.5,6],[19,4],[19.5,-1],[25,-1.5],[24.5,-4.5], + [16.8,-5],[16.5,1.2],[14.7,1],[14.3,-5],[8.7,-4.5], + [8.5,1.2],[6.5,1],[6.3,-4.8],[3,-5],[-4,1] +] + +v_walls = [] # building walls from corners +for i in range(len(v_corners)): + v_walls.append(Wall(v_corners[i],v_corners[(i+1)%len(v_corners)])) + +dt = 0.01 # timestep for tubes accuracy +dt_traj = 0.1*dt # timestep for simulation +tdomain = Interval(0,10) # temporal limits [t_0,t_f] + +# Analytical true trajectory (not used for resolution) +x_truth = TrajectoryVector(tdomain, TFunction("( \ + 2*t+(1+cos(t*pi))/2 ; \ + 3*tanh(4*(1+cos((t+0.5)/2*pi))/2-2) ; \ + atan2( \ + -3*pi*(1-sqr(tanh(2*cos((2*t+1)*pi/4))))*sin((2*t+1)*pi/4), \ + 2-pi*sin(pi*t)/2) ; \ + sqrt( \ + sqr( -3*pi*(1-sqr(tanh(2*cos((2*t+1)*pi/4))))*sin((2*t+1)*pi/4) ) + \ + sqr( 2-pi*sin(pi*t)/2 ) \ + ))")) + +# Continuous measurements coming from the truth +measured_psi = x_truth[2].sample(dt_traj).make_continuous() +measured_psi += RandTrajectory(tdomain, dt_traj, Interval(-0.05,0.05)) # adding some noise +measured_speed = x_truth[3].sample(dt_traj) +measured_speed += RandTrajectory(tdomain, dt_traj, Interval(-0.1,0.1)) # adding some noise + +# =============== Defining domains for the variables =============== + +x = TubeVector(tdomain, dt, 4) # 4d tube for state vectors +v = TubeVector(tdomain, dt, 4) # 4d tube for derivatives of the states +u = TubeVector(tdomain, dt, 2) # 2d tube for inputs of the system + +x[2] = Tube(measured_psi, dt).inflate(0.05) # measured_psi/speed are continuous measurements.. +x[3] = Tube(measured_speed, dt).inflate(0.1) # ..of the headings and speeds + +# =============== Defining contractors to deal with equations =============== + +ctc_f = CtcFunction( # constraint v=f(x,u) <=> v-f(x,u)=0 + Function("v[4]", "x[4]", "u[2]", + "(v[0]-x[3]*cos(x[2]) ; v[1]-x[3]*sin(x[2]) ; v[2]-u[0] ; v[3]-u[1])")) + +ctc_plus = CtcFunction(Function("a", "b", "c", "a+b-c")) # constraint a+b-c=0 <=> a+c=c +ctc_minus = CtcFunction(Function("a", "b", "c", "a-b-c")) # constraint a-b-c=0 <=> a-b=c + +v_ctc_segm = [] +ctc_walls = CtcUnion(2) # contractor for the constraint \mathbf{q} \in \matbb{W} with \mathbb{W} the set of walls +for wi in v_walls: + v_ctc_segm.append(CtcSegment(wi.c1[0],wi.c1[1],wi.c2[0],wi.c2[1])) + ctc_walls |= v_ctc_segm[-1] + +fig = VIBesFigMap("Set inversion") +SIVIA([[-5,26],[-8.5,8.5]], ctc_walls, 0.5, fig_name="Set inversion") +fig.set_properties(50, 50, 900, 500) +fig.axis_limits(-5,26,-8.5,8.5) + +# We also use the predefined contractors ctc::polar, ctc::eval, ctc::dist, no need to build them + +# =============== Adding the contractors to a network =============== + +cn = ContractorNetwork() # creating a network +cn.add(ctc_f, [v, x, u]) # adding the f contractor, that is common over [t0,tf] + +fig = VIBesFigMap("Map") +fig.set_properties(50, 50, 900, 500) +fig.add_tube(x, "x", 0, 1) +fig.add_trajectory(x_truth, "x*", 0, 1, 2) +fig.smooth_tube_drawing(True) +fig.show(0.) +fig.axis_limits(-5,26,-8.5,8.5) + +for wi in v_walls: # drawing the map of walls + fig.draw_line([wi.c1[0],wi.c2[0]], [wi.c1[1],wi.c2[1]], "lightGray") + +# For each observation of the environment: +for t in np.arange(tdomain.lb(), tdomain.ub(), 0.8): + pi_truth = x_truth(t)[0:2] + ti = Interval(t) + pi = IntervalVector(4) + qi = IntervalVector(2) + cn.add(ctc.eval, [ti, pi, x, v]) # <=> x(ti)=pi + cn.add(ctc_walls, [qi]) # <=> \mathbf{q]_i \in \mathbb{W} + + di = IntervalVector(2) + yi = IntervalVector(2) + yi[0] = Interval(shorter_dist_to_walls(v_walls, pi_truth, x_truth(t)[2])) + yi[1] = Interval(x_truth(t)[2]) # bearing is zero, azimuth is the heading + + yi[0].inflate(0.02) + yi[1].inflate(0.05) + + cn.add(ctc_minus, [qi, cn.subvector(pi,0,1), di]) # <=> qi-pi[0,1]=di + cn.add(ctc.polar, [di, yi]) # di[0]=y1[0]*cos(yi[1]), di[1]=y1[0]*sin(yi[1]) + + fig.draw_pie(pi_truth[0], pi_truth[1], yi[0] | Interval(0), yi[1], "lightGray[#FFFFFF88]") + fig.draw_pie(pi_truth[0], pi_truth[1], yi[0], yi[1]) + + fig.draw_vehicle([x_truth(t)[0], x_truth(t)[1], x_truth(t)[2]], 0.7) + +cn.contract(True) + +fig.show(0.) +fig.axis_limits(-5,26,-8.5,8.5) +endDrawing() \ No newline at end of file diff --git a/doc/doc/tutorial-set4most/index.rst b/doc/doc/tutorial-set4most/index.rst new file mode 100644 index 00000000..9d96010e --- /dev/null +++ b/doc/doc/tutorial-set4most/index.rst @@ -0,0 +1,10 @@ +.. _sec-tuto-set4most-main-page: + +################## +Tutorial: Set4MOST +################## + +* `Triskelion `_ +* `Range-only localization `_ +* `Walls localization `_ +* `SLAM `_ \ No newline at end of file diff --git a/doc/doc/tutorial/01-basics/index.rst b/doc/doc/tutorial/01-basics/index.rst index 97cc17f6..d78c1035 100644 --- a/doc/doc/tutorial/01-basics/index.rst +++ b/doc/doc/tutorial/01-basics/index.rst @@ -3,8 +3,13 @@ Lesson A: Getting started with intervals and contractors ======================================================== +.. # define a hard line break for HTML +.. |br| raw:: html + +
+ Now that Codac has been installed on your computer or usable online, we will get used to intervals, constraints and networks of contractors. -This will allow use to perform the state estimation of a static robot between some landmarks by the end of :ref:`Lesson B `. +This will allow you to perform the state estimation of a static robot between some landmarks by the end of :ref:`Lesson B `. .. contents:: Content of this lesson @@ -12,7 +17,11 @@ This will allow use to perform the state estimation of a static robot between so Start a new project ------------------- -Start a new project as explained in :ref:`sec-start-py-project` or :ref:`sec-start-cpp-project`. +Start a new project as explained in: + +* :ref:`sec-start-py-project` +* or :ref:`sec-start-cpp-project` +* or :ref:`sec-start-matlab-project` .. admonition:: Exercise @@ -28,6 +37,10 @@ Start a new project as explained in :ref:`sec-start-py-project` or :ref:`sec-sta cout << x << endl; + .. code-tab:: matlab + + x + You should see the following output: .. code-block:: bash @@ -57,6 +70,12 @@ Start a new project as explained in :ref:`sec-start-py-project` or :ref:`sec-sta // .. next questions will be here } + .. code-tab:: matlab + + import py.codac.* + + % .. next questions will be here + Using intervals for handling uncertainties ------------------------------------------ @@ -155,21 +174,21 @@ Codac is using C++/Python objects to represent intervals and boxes [#f1]_: For full details about ``Interval`` and ``IntervalVector`` objects, please read the :ref:`sec-manual-intervals` page of the user manual. - .. admonition:: Exercise **A.1.** Let us consider two intervals :math:`[x]=[8,10]` and :math:`[y]=[1,2]`. Without coding the operation, what would be the result of :math:`[x]/[y]` (:math:`[x]` divided by :math:`[y]`)? Remember that the result of this interval-division is also an interval enclosing all feasible divisions. - **A.2.** In your new project, compute and print the following simple operations on intervals: + **A.2.** In your new project, compute and print the following simple operations on intervals: |br| + :right-aligned-note:`Solutions are given below` |br| - * :math:`[-2,4]\cdot[1,3]` - * :math:`[8,10]/[-1,0]` - * :math:`[-2,4]\sqcup[6,7]` with operator ``|`` - * :math:`\max([2,7],[1,9])` - * :math:`\max(\varnothing,[1,2])` - * :math:`\cos([-\infty,\infty])` - * :math:`[-1,4]^2` with function ``sqr()`` - * :math:`([1,2]\cdot[-1,3]) + \max([1,3]\cap[6,7],[1,2])` + * :math:`[-2,4]\cdot[1,3]` :right-aligned-note:`[-6,12]` + * :math:`[8,10]/[-1,0]` :right-aligned-note:`[-∞,-8]` + * :math:`[-2,4]\sqcup[6,7]` with operator ``|`` :right-aligned-note:`[-2,7]` + * :math:`\max([2,7],[1,9])` :right-aligned-note:`[2,9]` + * :math:`\max(\varnothing,[1,2])` :right-aligned-note:`∅` + * :math:`\cos([-\infty,\infty])` :right-aligned-note:`[-1,1]` + * :math:`[-1,4]^2` with function ``sqr()`` :right-aligned-note:`[0,16]` + * :math:`([1,2]\cdot[-1,3]) + \max([1,6]\cap[5,7],[1,2])` :right-aligned-note:`[3,12]` | Note that :math:`\sqcup` is the hull union (``|``), *i.e.*, :math:`[x]\sqcup[y] = [[x]\cup[y]]`. | *For instance:* :math:`[-1,2]\sqcup[4,6]=[-1,6]` @@ -285,11 +304,11 @@ The graphical tool `VIBes `_ has | **A.6.** Before the ``.show()`` method, draw the boxes :math:`[\mathbf{x}]` and :math:`[\mathbf{b}]` with the ``fig.draw_box(..)`` method. The computed interval range :math:`[d]` can be displayed as a ring centered on :math:`\mathbf{x}=(0,0)`. The ring will contain the set of all positions that are :math:`d`-distant from :math:`\mathbf{x}=(0,0)`, with :math:`d\in[d]`. - To display each bound of the ring, you can use ``fig.draw_circle(x, y, rad)`` where ``x``, ``y``, ``rad`` are *double* values. + To display each bound of the ring, you can use ``fig.draw_circle(x, y, rad)`` where ``x``, ``y``, ``rad`` are real values. .. hint:: - To access *double* bounds of an interval object ``x``, you can use the ``x.lb()``/``x.ub()`` methods for lower and upper bounds. + To access real bounds of an ``Interval`` object ``x``, you can use the ``x.lb()``/``x.ub()`` methods for lower and upper bounds. This also works with ``IntervalVector``, returning vector items. | **A.7.** Now, repeat the operation with :math:`[\mathbf{x}]=[-0.1,0.1]\times[-0.1,0.1]`. You can for instance use the ``.inflate(0.1)`` method on ``x``. | Is the result reliable, according to the sets :math:`[\mathbf{x}]` and :math:`[\mathbf{b}]`? You may display the box :math:`([\mathbf{x}]+[\mathbf{b}])` to understand how the reliable interval distance is computed. @@ -424,4 +443,4 @@ We now have all the material to compute a solver for state estimation in the nex .. rubric:: Footnotes -.. [#f1] C++ objects originates from the `IBEX library `_. \ No newline at end of file +.. [#f1] C++ objects originates from the `IBEX library `_. \ No newline at end of file diff --git a/doc/doc/tutorial/03-static-rangebearing/index.rst b/doc/doc/tutorial/03-static-rangebearing/index.rst index e0d5bef3..cf79598c 100644 --- a/doc/doc/tutorial/03-static-rangebearing/index.rst +++ b/doc/doc/tutorial/03-static-rangebearing/index.rst @@ -78,7 +78,7 @@ Problems involving complex equations can be broken down into a set of **primitiv where :math:`a,b,\dots,e` are intermediate variables used for the decomposition. This constitutes a network made of the :math:`\mathcal{L}_{-}`, :math:`\mathcal{L}_{+}`, :math:`\mathcal{L}_{(\cdot)^2}`, and :math:`\mathcal{L}_{\sqrt{\cdot}}` elementary constraints. -| The library (more precisely: `the IBEX library `_) provides a way to automatically make this decomposition, select already existing **elementary contractors** such as :math:`\mathcal{C}_{+}`, :math:`\mathcal{C}_{(\cdot)^2}`, :math:`\mathcal{C}_{\sqrt{\cdot}}` and build the complex contractor modeling the constraint :math:`\mathcal{L}_{\textrm{dist}}`. +| The library (more precisely: `the IBEX library `_) provides a way to automatically make this decomposition, select already existing **elementary contractors** such as :math:`\mathcal{C}_{+}`, :math:`\mathcal{C}_{(\cdot)^2}`, :math:`\mathcal{C}_{\sqrt{\cdot}}` and build the complex contractor modeling the constraint :math:`\mathcal{L}_{\textrm{dist}}`. | This was implicitly done in the previous :ref:`Lesson A ` with: .. tabs:: diff --git a/doc/doc/tutorial/05-tubes/index.rst b/doc/doc/tutorial/05-tubes/index.rst index 20cc27a1..3e248a27 100644 --- a/doc/doc/tutorial/05-tubes/index.rst +++ b/doc/doc/tutorial/05-tubes/index.rst @@ -245,7 +245,7 @@ Tubes can also be built from trajectories. In this example, we could have define Is the actual trajectory :math:`\mathbf{x}^*(\cdot)` enclosed in :math:`[\mathbf{x}](\cdot)` at any time? - **E.9.** Create a tube :math:`[y](\cdot)` for enclosing the trajectory of distances between the robot and the landmark. + **E.9.** Create a tube :math:`[y](\cdot)` for enclosing the actual trajectory :math:`y^{*}(\cdot)` of distances between the robot and the landmark. For now, we will not consider uncertainties on :math:`y^{*}(\cdot)`. Therefore, the tube :math:`[y](\cdot)` should enclose :math:`y^{*}(\cdot)` in a minimal way according to the discretization step ``dt``. Note that all the tubes of this lesson have to share the same ``tdomain`` and ``dt`` parameters. @@ -299,7 +299,7 @@ For instance, one can contract three tubes :math:`[a](\cdot)`, :math:`[b](\cdot) \mathbf{y}(t)=\mathbf{g}\big(\mathbf{x}(t)\big) & & \textrm{(observation equation)} \end{array}\right. - **E.10.** We first focus on the observation equation :math:`\mathbf{y}(t)=\mathbf{g}\big(\mathbf{x}(t)\big)`. Build a contractor network and contract the tube :math:`[\mathbf{x}](\cdot)` with the distance contractor, that expresses :math:`\mathbf{g}`. Note that this contractor is already defined in the library. You developed your own version as an exercise in :ref:`sec-tuto-01`, but you can also use: + **E.10.** We first focus on the observation equation :math:`\mathbf{y}(t)=\mathbf{g}\big(\mathbf{x}(t)\big)`. Build a contractor network and contract the tube :math:`[\mathbf{x}](\cdot)` with the distance contractor, that expresses :math:`\mathbf{g}`. Note that this contractor is already defined in the library. You developed your own version as an exercise in :ref:`Lesson A `, but you can also use: .. tabs:: diff --git a/doc/doc/tutorial/08-rangeonly-slam/solution.m b/doc/doc/tutorial/08-rangeonly-slam/solution.m new file mode 100644 index 00000000..5560d2cd --- /dev/null +++ b/doc/doc/tutorial/08-rangeonly-slam/solution.m @@ -0,0 +1,125 @@ +% Codac - Examples +% Dynamic range-bearing localization +% ---------------------------------------------------------------------------- + +import py.codac.* + +% =========== CREATING DATA =========== + +dt = 0.05; +iteration_dt = 0.2; +tdomain = Interval(0,15); % [t0,tf] + +% Initial pose x0=(0,0,2) +x0 = [0, 0, 2]; + +% System input +u = Trajectory(tdomain, TFunction('3*(sin(t)^2)+t/100'), dt); + +% Noise +i_n = Interval(-0.03,0.03); % the noises are known to be bounded by i_n + +n_u = RandTrajectory(tdomain, dt, i_n); % input noise +n_theta = RandTrajectory(tdomain, dt, i_n); % heading noise + +% Actual trajectories (state + derivative) +v_truth = TrajectoryVector(int32(3)); +x_truth = TrajectoryVector(int32(3)); +v_truth.setitem(int32(2), u + n_u); +x_truth.setitem(int32(2), v_truth.getitem(int32(2)).primitive() + x0(3)); +v_truth.setitem(int32(0), 10*cos(x_truth.getitem(int32(2)))); +v_truth.setitem(int32(1), 10*sin(x_truth.getitem(int32(2)))); +x_truth.setitem(int32(0), v_truth.getitem(int32(0)).primitive() + x0(1)); +x_truth.setitem(int32(1), v_truth.getitem(int32(1)).primitive() + x0(2)); + +% Bounded trajectories (dead reckoning) +v = TubeVector(tdomain, dt, int32(3)); +x = TubeVector(tdomain, dt, int32(3)); +v.setitem(int32(2), Tube(u, dt).inflate(i_n.rad())); % command u with bounded uncertainties +x.setitem(int32(2), Tube(x_truth.getitem(int32(2))+n_theta, dt).inflate(i_n.rad())); % heading measurement with bounded uncertainties +v.setitem(int32(0), 10*cos(x.getitem(int32(2)))); +v.setitem(int32(1), 10*sin(x.getitem(int32(2)))); +x = v.primitive()+IntervalVector(x0); % dead reckoning + +% Set of landmarks +v_m = { py.list([6,12]), py.list([-2,-5]), py.list([-3,20]), py.list([3,4]) }; + +% =========== GRAPHICS =========== + +beginDrawing(); + +fig_map = VIBesFigMap('slam'); +fig_map.set_properties(int32(50), int32(50), int32(1200), int32(600)); +fig_map.add_tube(x, 'x', int32(0), int32(1)); +fig_map.add_trajectory(x_truth, 'truth', int32(0), int32(1), 'white'); +fig_map.smooth_tube_drawing(true); +fig_map.add_landmarks(py.list(v_m), single(0.4)); +fig_map.show(double(1)); + +% =========== CONTRACTOR NETWORK =========== + +v_m_boxes = cell(size(v_m)); +for i=1:length(v_m) + v_m_boxes(i) = {IntervalVector(int32(2))}; +end + +% Contractor Network: + +cn = ContractorNetwork(); + +t = tdomain.lb(); +prev_t_obs = t; + +while t < tdomain.ub() + + if t-prev_t_obs > 2*dt % new observation each 2*delta + + % Creating new observation to a random landmark + + landmark_id = randi([1 length(v_m)]); % a random landmark is perceived + + xt = double(x_truth(t)); + pos_x = [xt(1), xt(2)]; + pos_b = double(v_m{landmark_id}); + + yi = Interval(sqrt((pos_x(1)-pos_b(1))^2+(pos_x(2)-pos_b(2))^2)); + yi.inflate(0.03); % adding range bounded uncertainty + + prev_t_obs = t; + + % Adding related observation constraints to the network + + % Alias (for ease of reading) + b = v_m_boxes{landmark_id}; + + % Intermediate variables + ti = Interval(t); + xi = IntervalVector(int32(3)); + + % Contractors + cn.add(CtcEval(), py.list({ti, xi, x, v})); + cn.add(CtcDist(), py.list({xi.getitem(int32(0)), xi.getitem(int32(1)), b.getitem(int32(0)), b.getitem(int32(1)), yi})); + + end + + contraction_dt = cn.contract_during(iteration_dt); + if iteration_dt>contraction_dt + pause(iteration_dt-contraction_dt); % iteration delay + end + + % Display the current slice x + fig_map.draw_box(x(t).subvector(int32(0),int32(1))); + + t = t + dt; + +end + +cn.contract(true); % lets the solver run the remaining contractions + +fig_map.show(); +for i=1:length(v_m_boxes) + b = v_m_boxes{i}; + fig_map.draw_box(b); +end + +endDrawing(); diff --git a/doc/doc/use-cases/brunovsky/index.rst b/doc/doc/use-cases/brunovsky/index.rst new file mode 100644 index 00000000..167b6a64 --- /dev/null +++ b/doc/doc/use-cases/brunovsky/index.rst @@ -0,0 +1,18 @@ +.. _sec-usecases-brunovsky: + +.. warning:: + + This page is related to a paper that is not published yet. + More content will appear soon. + +############################################ +Pose estimation with range-only observations +############################################ + +In the paper *Brunovsky decomposition for dynamic interval localization*, a new set-membership method has been proposed for estimating the trajectories of dynamical systems, when the states are completely unknown and only non-linear observations are available. + +The first part of the proposed method is symbolic and follows the decomposition of Brunovsky, *i.e.*, it decomposes the set of differential equations describing the dynamical system into two blocks of constraints: one block gathers non-linear analytical equations that do not involve differential operators, while the other block is composed of linear chains of integrators. The second part of the method, that relies on the symbolic decomposition, is numerical and based on a contractor approach. It involves a specific optimal operator for narrowing the sets of feasible solutions. + +This approach is shown to be efficient on a difficult problem of dynamical localization of a mobile robot, without any prior knowledge about its states. + +*More content coming soon.* \ No newline at end of file diff --git a/doc/doc/use-cases/lie-symmetries/index.rst b/doc/doc/use-cases/lie-symmetries/index.rst index 74880c69..11aa5567 100644 --- a/doc/doc/use-cases/lie-symmetries/index.rst +++ b/doc/doc/use-cases/lie-symmetries/index.rst @@ -161,7 +161,7 @@ Related content .. _lie-pdf: http://julien-damers.fr/publis/lie_groups_applied_to_guaranteed_integration.pdf .. |thesis-pdf| replace:: **Download the thesis** -.. _thesis-pdf: https://julien-damers.fr/phd/complete.pdf +.. _thesis-pdf: https://julien-damers.fr/phd/Lie_Groups_applied_to_localisation_of_mobile_robots.pdf .. admonition:: Related publication diff --git a/doc/doc/use-cases/slam/index.rst b/doc/doc/use-cases/slam/index.rst new file mode 100644 index 00000000..d193e0a8 --- /dev/null +++ b/doc/doc/use-cases/slam/index.rst @@ -0,0 +1,8 @@ +.. _sec-usecases-slam: + +##################################### +Simultaneous Localization And Mapping +##################################### + +* :ref:`Link to the tutorial page (Lesson H) about How to build a SLAM solver with Codac`. +* `Link to the source code of the solution of Lesson H `_. \ No newline at end of file diff --git a/examples/basics/01_arithmetic/CMakeLists.txt b/examples/basics/01_arithmetic/CMakeLists.txt index 1891afbb..d3a1c1d4 100644 --- a/examples/basics/01_arithmetic/CMakeLists.txt +++ b/examples/basics/01_arithmetic/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(codac_basics_01 LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need @@ -22,7 +25,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/basics/02_simple_ctc/CMakeLists.txt b/examples/basics/02_simple_ctc/CMakeLists.txt index 6c9f32d3..9195d1da 100644 --- a/examples/basics/02_simple_ctc/CMakeLists.txt +++ b/examples/basics/02_simple_ctc/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(codac_basics_02 LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need @@ -22,7 +25,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/basics/03_ivp_msinx/CMakeLists.txt b/examples/basics/03_ivp_msinx/CMakeLists.txt index f4d63550..88247e97 100644 --- a/examples/basics/03_ivp_msinx/CMakeLists.txt +++ b/examples/basics/03_ivp_msinx/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(codac_basics_03 LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need @@ -22,7 +25,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/basics/04_simple_eval/CMakeLists.txt b/examples/basics/04_simple_eval/CMakeLists.txt index 189540e7..6e2d6bd0 100644 --- a/examples/basics/04_simple_eval/CMakeLists.txt +++ b/examples/basics/04_simple_eval/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(codac_basics_04 LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need @@ -22,7 +25,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/basics/05_graphics/CMakeLists.txt b/examples/basics/05_graphics/CMakeLists.txt index b03a3066..64ad08a2 100644 --- a/examples/basics/05_graphics/CMakeLists.txt +++ b/examples/basics/05_graphics/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(codac_basics_05 LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need @@ -22,7 +25,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/basics/06_ctc_comparisons/CMakeLists.txt b/examples/basics/06_ctc_comparisons/CMakeLists.txt index 885a8e84..4226954b 100644 --- a/examples/basics/06_ctc_comparisons/CMakeLists.txt +++ b/examples/basics/06_ctc_comparisons/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(codac_basics_06 LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need @@ -22,7 +25,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/basics/07_temporal_ctc/CMakeLists.txt b/examples/basics/07_temporal_ctc/CMakeLists.txt index acfb11f3..12c2f26b 100644 --- a/examples/basics/07_temporal_ctc/CMakeLists.txt +++ b/examples/basics/07_temporal_ctc/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(codac_basics_07 LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need @@ -22,7 +25,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/basics/08_tube_paving/CMakeLists.txt b/examples/basics/08_tube_paving/CMakeLists.txt index a78dc899..264683be 100644 --- a/examples/basics/08_tube_paving/CMakeLists.txt +++ b/examples/basics/08_tube_paving/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(codac_basics_08 LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need @@ -22,7 +25,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/basics/09_cn_paving/CMakeLists.txt b/examples/basics/09_cn_paving/CMakeLists.txt index bdc4b90c..2a7a6b9c 100644 --- a/examples/basics/09_cn_paving/CMakeLists.txt +++ b/examples/basics/09_cn_paving/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(codac_basics_09 LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need @@ -22,7 +25,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/basics/10_simple_sivia/main.cpp b/examples/basics/10_simple_sivia/main.cpp index b7175164..699a6016 100644 --- a/examples/basics/10_simple_sivia/main.cpp +++ b/examples/basics/10_simple_sivia/main.cpp @@ -36,25 +36,25 @@ int main(int argc, char** argv) figname = "Sep regular"; newFigure(figname); cout << figname << endl; SIVIA(X0, sep, 0.1, true, true, figname, false, custom_colormap); - setFigureProperties(vibesParams("x",100, "y",100, "width",500, "height",500)); + setFigureProperties(vibesParams("x",10, "y",10, "width",500, "height",500)); axisAuto(); figname = "Sep diff"; newFigure(figname); cout << figname << endl; SIVIA(X0, sep, 0.1, false, true, figname, false, custom_colormap); - setFigureProperties(vibesParams("x",150, "y",150, "width",500, "height",500)); + setFigureProperties(vibesParams("x",510, "y",10, "width",500, "height",500)); axisAuto(); figname = "Ctc regular"; newFigure(figname); cout << figname << endl; SIVIA(X0, ctc, 0.1, true, true, figname, false, custom_colormap); - setFigureProperties(vibesParams("x",200, "y",200, "width",500, "height",500)); + setFigureProperties(vibesParams("x",10, "y",510, "width",500, "height",500)); axisAuto(); figname = "Ctc diff"; newFigure(figname); cout << figname << endl; SIVIA(X0, ctc, 0.1, false, true, figname, false, custom_colormap); - setFigureProperties(vibesParams("x",250, "y",250, "width",500, "height",500)); + setFigureProperties(vibesParams("x",510, "y",510, "width",500, "height",500)); axisAuto(); endDrawing(); diff --git a/examples/brunovsky/CMakeLists.txt b/examples/brunovsky/CMakeLists.txt index e4e4bd37..cc01b52f 100755 --- a/examples/brunovsky/CMakeLists.txt +++ b/examples/brunovsky/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(codac_brunov LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Compilation options set(CMAKE_CXX_STANDARD 17) @@ -33,7 +36,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac @@ -46,6 +49,6 @@ # Compilation - add_executable(${PROJECT_NAME} main.cpp) + add_executable(${PROJECT_NAME} main_debug.cpp) target_include_directories(${PROJECT_NAME} SYSTEM PUBLIC ${CODAC_INCLUDE_DIRS} ${EIGEN3_INCLUDE_DIRS}) target_link_libraries(${PROJECT_NAME} PUBLIC ${CODAC_LIBRARIES} Ibex::ibex ${CODAC_LIBRARIES}) \ No newline at end of file diff --git a/examples/brunovsky/main.cpp b/examples/brunovsky/main.cpp index cd018fcc..93d0d846 100755 --- a/examples/brunovsky/main.cpp +++ b/examples/brunovsky/main.cpp @@ -22,7 +22,7 @@ int main() { /* =========== TRUTH =========== */ - double dt = 0.003; // tubes timestep + double dt = 0.01; // tubes timestep Interval tdomain(0.,3.); // temporal domain // Unknown truth @@ -53,7 +53,7 @@ int main() TubeVector u(u_truth, dt); // centered on analytical expression // Intermediate variables: - TubeVector v(tdomain, dt, 4), a(tdomain, dt, 4); + TubeVector v(tdomain, dt, 4), a(tdomain, dt, 2); /* =========== CREATING CONTRACTORS =========== */ @@ -66,7 +66,7 @@ int main() v[2]-u[0] ; \ v[3]-u[1])")); - CtcFunction ctc_a(Function("a[4]", "x[4]", "u[2]", + CtcFunction ctc_a(Function("a[2]", "x[4]", "u[2]", "(u[1]*cos(x[2])-x[3]*sin(x[2])*u[0]-a[0] ; \ u[1]*sin(x[2])+x[3]*cos(x[2])*u[0]-a[1])")); diff --git a/examples/codac2/01/CMakeLists.txt b/examples/codac2/01/CMakeLists.txt new file mode 100644 index 00000000..58675521 --- /dev/null +++ b/examples/codac2/01/CMakeLists.txt @@ -0,0 +1,44 @@ +# ================================================================== +# codac / basics example - cmake configuration file +# ================================================================== + + cmake_minimum_required(VERSION 3.0.2) + project(codac_basics_01 LANGUAGES CXX) + + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# Adding IBEX + + # In case you installed IBEX in a local directory, you need + # to specify its path with the CMAKE_PREFIX_PATH option. + # set(CMAKE_PREFIX_PATH "~/ibex-lib/build_install") + + find_package(IBEX REQUIRED) + ibex_init_common() # IBEX should have installed this function + message(STATUS "Found IBEX version ${IBEX_VERSION}") + +# Adding Eigen3 + + # In case you installed Eigen3 in a local directory, you need + # to specify its path with the CMAKE_PREFIX_PATH option, e.g. + # set(CMAKE_PREFIX_PATH "~/eigen/build_install") + + find_package(Eigen3 REQUIRED NO_MODULE) + message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + +# Adding Codac + + # In case you installed Codac in a local directory, you need + # to specify its path with the CMAKE_PREFIX_PATH option. + # set(CMAKE_PREFIX_PATH "~/codac/build_install") + + find_package(CODAC REQUIRED) + message(STATUS "Found Codac version ${CODAC_VERSION}") + +# Compilation + + add_executable(${PROJECT_NAME} main.cpp) + target_compile_options(${PROJECT_NAME} PUBLIC ${CODAC_CXX_FLAGS}) + target_include_directories(${PROJECT_NAME} SYSTEM PUBLIC ${CODAC_INCLUDE_DIRS} ${EIGEN3_INCLUDE_DIRS}) + target_link_libraries(${PROJECT_NAME} PUBLIC ${CODAC_LIBRARIES} Ibex::ibex ${CODAC_LIBRARIES} Ibex::ibex) \ No newline at end of file diff --git a/examples/codac2/01/main.cpp b/examples/codac2/01/main.cpp new file mode 100644 index 00000000..a3572375 --- /dev/null +++ b/examples/codac2/01/main.cpp @@ -0,0 +1,31 @@ +#include + +using namespace std; +using namespace codac; + +int main() +{ + auto tdomain = codac2::create_tdomain(Interval(0,10), 0.01, true); // last argument creates "gates" (degenerated slices at scalar timesteps) + codac2::Tube x(tdomain, + TFunction("(sin(sqrt(t)+((t-5)^2)*[-0.01,0.01]) ; cos(t)+sin(t/0.2)*[-0.1,0.1])")); + codac2::Tube u(tdomain); + + // If you want to iterate each slice (including gates = degenerate slices) + for(auto& sx : x) + { + //cout << sx << endl; + } + + vibes::beginDrawing(); + + codac::TubeVector x_codac1 = codac2::to_codac1(x); // may take time + + VIBesFigTube fig("Tube"); + fig.set_properties(100, 100, 600, 300); + fig.add_tube(&x_codac1[1], "x"); + fig.show(true); + + vibes::endDrawing(); + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/examples/codac2/01/main_test_sample.cpp b/examples/codac2/01/main_test_sample.cpp new file mode 100644 index 00000000..e7ce7c26 --- /dev/null +++ b/examples/codac2/01/main_test_sample.cpp @@ -0,0 +1,33 @@ +#include + +using namespace std; +using namespace codac; + +int main() +{ + { + auto tdomain = codac2::create_tdomain(Interval(0,10), 1., false); + codac2::Tube x(tdomain, IntervalVector(2)); + + for(auto& sx : x) + { + if(sx.t0_tf().contains(5.2)) + { + cout << "sample" << endl; + tdomain->sample(5.2); + } + cout << sx << endl; + } + } + + { + TFunction f("x1", "x2", "u1", "u2", "(t+u1+x1;t+u2+x2)"); + IntervalVector x({{2,3},{5,6}}); + IntervalVector u({{0,0.1},{0,0.1}}); + Interval t(5.); + cout << f.eval_vector(t,x,u) << endl; + } + + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/examples/lie_group/05_loc/CMakeLists.txt b/examples/lie_group/05_loc/CMakeLists.txt index 2be59bfb..c5f3e728 100644 --- a/examples/lie_group/05_loc/CMakeLists.txt +++ b/examples/lie_group/05_loc/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(codac_lie_05 LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need @@ -22,7 +25,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/linobs/01_paper/CMakeLists.txt b/examples/linobs/01_paper/CMakeLists.txt index 8e7e1516..c35026eb 100644 --- a/examples/linobs/01_paper/CMakeLists.txt +++ b/examples/linobs/01_paper/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(codac_linobs LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Compilation options set(CMAKE_CXX_STANDARD 17) @@ -32,7 +35,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/robotics/01_causal_chain/CMakeLists.txt b/examples/robotics/01_causal_chain/CMakeLists.txt index 5bc663c2..fa6fa792 100644 --- a/examples/robotics/01_causal_chain/CMakeLists.txt +++ b/examples/robotics/01_causal_chain/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(codac_rob_01 LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need @@ -22,7 +25,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/robotics/03_drifting_clock/CMakeLists.txt b/examples/robotics/03_drifting_clock/CMakeLists.txt index 547b492f..5b597dbe 100644 --- a/examples/robotics/03_drifting_clock/CMakeLists.txt +++ b/examples/robotics/03_drifting_clock/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(codac_rob_03 LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need @@ -22,7 +25,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/robotics/04_redermor_traj/CMakeLists.txt b/examples/robotics/04_redermor_traj/CMakeLists.txt index 48564d05..a515bea5 100644 --- a/examples/robotics/04_redermor_traj/CMakeLists.txt +++ b/examples/robotics/04_redermor_traj/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(codac_rob_04 LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need @@ -22,7 +25,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/robotics/05_loops_detec/CMakeLists.txt b/examples/robotics/05_loops_detec/CMakeLists.txt index e87c167a..7fd175de 100644 --- a/examples/robotics/05_loops_detec/CMakeLists.txt +++ b/examples/robotics/05_loops_detec/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(codac_rob_05 LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need @@ -22,7 +25,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/robotics/06_loops_proofs/CMakeLists.txt b/examples/robotics/06_loops_proofs/CMakeLists.txt index a96881fb..4ed28e57 100644 --- a/examples/robotics/06_loops_proofs/CMakeLists.txt +++ b/examples/robotics/06_loops_proofs/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(codac_rob_06 LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need @@ -22,7 +25,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/robotics/07_dynloc/CMakeLists.txt b/examples/robotics/07_dynloc/CMakeLists.txt index 93be85b8..47f513e8 100644 --- a/examples/robotics/07_dynloc/CMakeLists.txt +++ b/examples/robotics/07_dynloc/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(codac_rob_07 LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need @@ -22,7 +25,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/robotics/08_tubepaving/CMakeLists.txt b/examples/robotics/08_tubepaving/CMakeLists.txt index 68bb93c1..ef1b7ea4 100644 --- a/examples/robotics/08_tubepaving/CMakeLists.txt +++ b/examples/robotics/08_tubepaving/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(codac_rob_08 LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need @@ -22,7 +25,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/robotics/09_lissajous/CMakeLists.txt b/examples/robotics/09_lissajous/CMakeLists.txt index 58cee6e1..94283644 100644 --- a/examples/robotics/09_lissajous/CMakeLists.txt +++ b/examples/robotics/09_lissajous/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(codac_rob_09 LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need @@ -22,7 +25,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/robotics/10_datasso/CMakeLists.txt b/examples/robotics/10_datasso/CMakeLists.txt index 94196110..db71e3eb 100644 --- a/examples/robotics/10_datasso/CMakeLists.txt +++ b/examples/robotics/10_datasso/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(codac_rob_10 LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need @@ -22,7 +25,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/robotics/11_explored_area/CMakeLists.txt b/examples/robotics/11_explored_area/CMakeLists.txt index 02561f5f..ccbf375d 100644 --- a/examples/robotics/11_explored_area/CMakeLists.txt +++ b/examples/robotics/11_explored_area/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(codac_rob_11 LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need @@ -22,7 +25,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/robotics/12_walls/CMakeLists.txt b/examples/robotics/12_walls/CMakeLists.txt new file mode 100644 index 00000000..d9ae4264 --- /dev/null +++ b/examples/robotics/12_walls/CMakeLists.txt @@ -0,0 +1,45 @@ +# ================================================================== +# codac / basics example - cmake configuration file +# ================================================================== + + cmake_minimum_required(VERSION 3.0.2) + project(codac_robotics_12 LANGUAGES CXX) + + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# Adding IBEX + + # In case you installed IBEX in a local directory, you need + # to specify its path with the CMAKE_PREFIX_PATH option. + # set(CMAKE_PREFIX_PATH "~/ibex-lib/build_install") + + find_package(IBEX REQUIRED) + ibex_init_common() # IBEX should have installed this function + message(STATUS "Found IBEX version ${IBEX_VERSION}") + +# Adding Eigen3 + + # In case you installed Eigen3 in a local directory, you need + # to specify its path with the CMAKE_PREFIX_PATH option, e.g. + # set(CMAKE_PREFIX_PATH "~/eigen/build_install") + + find_package(Eigen3 REQUIRED NO_MODULE) + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") + +# Adding Codac + + # In case you installed Codac in a local directory, you need + # to specify its path with the CMAKE_PREFIX_PATH option. + # set(CMAKE_PREFIX_PATH "~/codac/build_install") + + set(CMAKE_PREFIX_PATH "~/codac-lille/build_install") + find_package(CODAC REQUIRED) + message(STATUS "Found Codac version ${CODAC_VERSION}") + +# Compilation + + add_executable(${PROJECT_NAME} main.cpp) + target_compile_options(${PROJECT_NAME} PUBLIC ${CODAC_CXX_FLAGS}) + target_include_directories(${PROJECT_NAME} SYSTEM PUBLIC ${CODAC_INCLUDE_DIRS} ${EIGEN3_INCLUDE_DIRS}) + target_link_libraries(${PROJECT_NAME} PUBLIC ${CODAC_LIBRARIES} Ibex::ibex ${CODAC_LIBRARIES}) \ No newline at end of file diff --git a/examples/robotics/12_walls/build.sh b/examples/robotics/12_walls/build.sh new file mode 100755 index 00000000..3fa90fc6 --- /dev/null +++ b/examples/robotics/12_walls/build.sh @@ -0,0 +1,11 @@ +# ================================================================== +# Codac - build script +# ================================================================== + +#!/bin/bash + +mkdir build -p +cd build +cmake .. -DCMAKE_BUILD_TYPE=Debug +make +cd .. \ No newline at end of file diff --git a/examples/robotics/12_walls/main.cpp b/examples/robotics/12_walls/main.cpp new file mode 100644 index 00000000..b72adcd5 --- /dev/null +++ b/examples/robotics/12_walls/main.cpp @@ -0,0 +1,155 @@ +/** + * Codac - Examples + * Localization of a robot in a map of walls, based on two kinds of observations: + * - range-and-bearing: the robot is equipPed with a forward looking sonar + * - range-only: the robot only measures its distance to the closest wall + * ---------------------------------------------------------------------------- + * + * \date 2024 + * \author Simon Rohou + * \copyright Copyright 2024 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#include + +using namespace std; +using namespace codac; + +int main() +{ + /* =================== Parameters, truth and data =================== */ + + vector v_corners { + {-4,6},{2.5,6},{2.5,-1},{4.5,-1},{4.5,4}, + {11.5,6},{19,4},{19,-1},{25,-1},{25,-5}, + {16.5,-5},{16.5,1},{14.5,1},{14.5,-5},{8.5,-5}, + {8.5,1},{6.5,1},{6.5,-5},{3,-5},{-4,1} + }; + + vector v_walls; // building walls from corners + for(size_t i = 0 ; i < v_corners.size() ; i++) + v_walls.push_back({v_corners[i],v_corners[(i+1)%v_corners.size()]}); + + float dt = 0.01; // timestep for tubes accuracy + float dt_traj = 0.1*dt; // timestep for simulation + Interval tdomain(0,10); // temporal limits [t_0,t_f] + + // Analytical true trajectory (not used for resolution) + TrajectoryVector x_truth(tdomain, TFunction("( \ + 2*t+(1+cos(t*pi))/2 ; \ + 3*tanh(4*(1+cos((t+0.5)/2*pi))/2-2) ; \ + atan2( \ + -3*pi*(1-sqr(tanh(2*cos((2*t+1)*pi/4))))*sin((2*t+1)*pi/4), \ + 2-pi*sin(pi*t)/2) ; \ + sqrt( \ + sqr( -3*pi*(1-sqr(tanh(2*cos((2*t+1)*pi/4))))*sin((2*t+1)*pi/4) ) + \ + sqr( 2-pi*sin(pi*t)/2 ) \ + ))")); + + // Continuous measurements coming from the truth + Trajectory& measured_psi = x_truth[2].sample(dt_traj).make_continuous(); + measured_psi += RandTrajectory(tdomain, dt_traj, Interval(-0.01,0.01)); // adding some noise + Trajectory& measured_speed = x_truth[3].sample(dt_traj); + measured_speed += RandTrajectory(tdomain, dt_traj, Interval(-0.01,0.01)); // adding some noise + + /* =============== Defining domains for the variables =============== */ + + TubeVector x(tdomain, dt, 4); // 4d tube for state vectors + TubeVector v(tdomain, dt, 4); // 4d tube for derivatives of the states + TubeVector u(tdomain, dt, 2); // 2d tube for inputs of the system + + x[2] = Tube(measured_psi, dt).inflate(0.05); // measured_psi/speed are continuous measurements.. + x[3] = Tube(measured_speed, dt).inflate(0.1); // ..of the headings and speeds + + /* =============== Defining contractors to deal with equations =============== */ + + CtcFunction ctc_f( + Function("v[4]", "x[4]", "u[2]", + "(v[0]-x[3]*cos(x[2]) ; v[1]-x[3]*sin(x[2]) ; v[2]-u[0] ; v[3]-u[1])")); + + CtcFunction ctc_plus(Function("a", "b", "c", "a+b-c")); // constraint a+b-c=0 <=> a+c=c + CtcFunction ctc_minus(Function("a", "b", "c", "a-b-c")); // constraint a-b-c=0 <=> a-b=c + + CtcUnion ctc_walls(2); // contractor for the constraint \mathbf{q} \in \matbb{W} with \mathbb{W} the set of walls + for(const auto& wi : v_walls) + ctc_walls |= CtcSegment(wi.c1[0],wi.c1[1],wi.c2[0],wi.c2[1]); + + // We also use the predefined contractors ctc::polar, ctc::eval, ctc::dist, no need to build them + + /* =============== Adding the contractors to a network =============== */ + + ContractorNetwork cn; // creating a network + cn.add(ctc_f, {v, x, u}); // adding the f contractor, that is common over [t0,tf] + + vibes::beginDrawing(); + VIBesFigMap fig("Map"); + fig.set_properties(50, 50, 900, 500); + fig.add_tube(&x, "x", 0, 1); + fig.add_trajectory(&x_truth, "x*", 0, 1, 2); + fig.smooth_tube_drawing(true); + fig.show(0.); + fig.axis_limits(-5,26,-8.5,8.5); + + for(const auto& wi : v_walls) // drawing the map of walls + fig.draw_line({wi.c1[0],wi.c2[0]}, {wi.c1[1],wi.c2[1]}, "lightGray"); + + bool dist_only = !true; // in order to test two scenarii + + if(dist_only) // For this scenario we assume that \mathbf{x}(0) is known + { + Interval& t0 = cn.create_interm_var(0.); + IntervalVector& p0 = cn.create_interm_var(x_truth(0.)); + cn.add(ctc::eval, {t0, p0, x, v}); // <=> x(t0)=p0 + } + + // For each observation of the environment: + for(double t = tdomain.lb() ; t < tdomain.ub() ; t += 0.8) + { + Vector pi_truth = x_truth(t).subvector(0,1); + + Interval& ti = cn.create_interm_var(Interval(t)); + IntervalVector& pi = cn.create_interm_var(IntervalVector(4)); + IntervalVector& qi = cn.create_interm_var(IntervalVector(2)); + + cn.add(ctc::eval, {ti, pi, x, v}); // <=> x(ti)=pi + cn.add(ctc_walls, {qi}); // <=> \mathbf{q]_i \in \mathbb{W} + + if(!dist_only) // range-and-bearing observation (forward-looking sonar) + { + IntervalVector& di = cn.create_interm_var(IntervalVector(2)); + IntervalVector& yi = cn.create_interm_var(IntervalVector(2)); + yi[0] = shorter_dist_to_walls(v_walls, pi_truth, x_truth(t)[2]); + yi[1] = x_truth(t)[2]; // bearing is zero, azimuth is the heading + + yi[0].inflate(0.02); yi[1].inflate(0.05); + + cn.add(ctc_minus, {qi, cn.subvector(pi,0,1), di}); // <=> qi-pi[0,1]=di + cn.add(ctc::polar, {di, yi}); // di[0]=y1[0]*cos(yi[1]), di[1]=y1[0]*sin(yi[1]) + + fig.draw_pie(pi_truth[0], pi_truth[1], yi[0] | 0., yi[1], "lightGray[#FFFFFF88]"); + fig.draw_pie(pi_truth[0], pi_truth[1], yi[0], yi[1]); + } + + else // range-only observation + { + Interval& yi = cn.create_interm_var(shorter_contact_to_walls(v_walls, pi_truth)[0]); + yi.inflate(0.02); + + cn.add(ctc::dist, {cn.subvector(pi,0,1), qi, yi}); // <=> (pi[0]-qi[0])^2+(pi[1]-qi[1])^2=yi^2 + + fig.draw_circle(pi_truth[0], pi_truth[1], yi.mid(), "black[#80808088]"); + } + + vibes::drawVehicle(x_truth(t)[0], x_truth(t)[1], x_truth(t)[2]*180/M_PI, 0.7, "black[yellow]"); + } + + cn.contract(true); + + fig.show(0.); + fig.axis_limits(-5,26,-8.5,8.5); + + vibes::endDrawing(); + return x.contains(x_truth) != BoolInterval::NO ? EXIT_SUCCESS : EXIT_FAILURE; +} \ No newline at end of file diff --git a/examples/robotics/12_walls/main.py b/examples/robotics/12_walls/main.py new file mode 100644 index 00000000..5689a7b2 --- /dev/null +++ b/examples/robotics/12_walls/main.py @@ -0,0 +1,144 @@ +# +# Codac - Examples +# Localization of a robot in a map of walls, based on two kinds of observations: +# - range-and-bearing: the robot is equipPed with a forward looking sonar +# - range-only: the robot only measures its distance to the closest wall +# ---------------------------------------------------------------------------- +# +# \date 2024 +# \author Simon Rohou +# \copyright Copyright 2024 Codac Team +# \license This program is distributed under the terms of +# the GNU Lesser General Public License (LGPL). + +from codac import * +import numpy as np + +# =================== Parameters, truth and data =================== + +beginDrawing() + +v_corners = [ + [-4.5,6],[2.5,6.5],[2.8,-1.2],[4.5,-0.8],[4.8,4], + [11.5,6],[19,4],[19.5,-1],[25,-1.5],[24.5,-4.5], + [16.8,-5],[16.5,1.2],[14.7,1],[14.3,-5],[8.7,-4.5], + [8.5,1.2],[6.5,1],[6.3,-4.8],[3,-5],[-4,1] +] + +v_walls = [] # building walls from corners +for i in range(len(v_corners)): + v_walls.append(Wall(v_corners[i],v_corners[(i+1)%len(v_corners)])) + +dt = 0.01 # timestep for tubes accuracy +dt_traj = 0.1*dt # timestep for simulation +tdomain = Interval(0,10) # temporal limits [t_0,t_f] + +# Analytical true trajectory (not used for resolution) +x_truth = TrajectoryVector(tdomain, TFunction("( \ + 2*t+(1+cos(t*pi))/2 ; \ + 3*tanh(4*(1+cos((t+0.5)/2*pi))/2-2) ; \ + atan2( \ + -3*pi*(1-sqr(tanh(2*cos((2*t+1)*pi/4))))*sin((2*t+1)*pi/4), \ + 2-pi*sin(pi*t)/2) ; \ + sqrt( \ + sqr( -3*pi*(1-sqr(tanh(2*cos((2*t+1)*pi/4))))*sin((2*t+1)*pi/4) ) + \ + sqr( 2-pi*sin(pi*t)/2 ) \ + ))")) + +# Continuous measurements coming from the truth +measured_psi = x_truth[2].sample(dt_traj).make_continuous() +measured_psi += RandTrajectory(tdomain, dt_traj, Interval(-0.05,0.05)) # adding some noise +measured_speed = x_truth[3].sample(dt_traj) +measured_speed += RandTrajectory(tdomain, dt_traj, Interval(-0.1,0.1)) # adding some noise + +# =============== Defining domains for the variables =============== + +x = TubeVector(tdomain, dt, 4) # 4d tube for state vectors +v = TubeVector(tdomain, dt, 4) # 4d tube for derivatives of the states +u = TubeVector(tdomain, dt, 2) # 2d tube for inputs of the system + +x[2] = Tube(measured_psi, dt).inflate(0.05) # measured_psi/speed are continuous measurements.. +x[3] = Tube(measured_speed, dt).inflate(0.1) # ..of the headings and speeds + +# =============== Defining contractors to deal with equations =============== + +ctc_f = CtcFunction( + Function("v[4]", "x[4]", "u[2]", + "(v[0]-x[3]*cos(x[2]) ; v[1]-x[3]*sin(x[2]) ; v[2]-u[0] ; v[3]-u[1])")) + +ctc_plus = CtcFunction(Function("a", "b", "c", "a+b-c")) # constraint a+b-c=0 <=> a+c=c +ctc_minus = CtcFunction(Function("a", "b", "c", "a-b-c")) # constraint a-b-c=0 <=> a-b=c + +v_ctc_segm = [] +ctc_walls = CtcUnion(2) # contractor for the constraint \mathbf{q} \in \matbb{W} with \mathbb{W} the set of walls +for wi in v_walls: + v_ctc_segm.append(CtcSegment(wi.c1[0],wi.c1[1],wi.c2[0],wi.c2[1])) + ctc_walls |= v_ctc_segm[-1] + +fig = VIBesFigMap("Set inversion") +SIVIA([[-5,26],[-8.5,8.5]], ctc_walls, 0.5, fig_name="Set inversion") +fig.set_properties(50, 50, 900, 500) +fig.axis_limits(-5,26,-8.5,8.5) + +# We also use the predefined contractors ctc::polar, ctc::eval, ctc::dist, no need to build them + +# =============== Adding the contractors to a network =============== + +cn = ContractorNetwork() # creating a network +cn.add(ctc_f, [v, x, u]) # adding the f contractor, that is common over [t0,tf] + +fig = VIBesFigMap("Map") +fig.set_properties(50, 50, 900, 500) +fig.add_tube(x, "x", 0, 1) +fig.add_trajectory(x_truth, "x*", 0, 1, 2) +fig.smooth_tube_drawing(True) +fig.show(0.) +fig.axis_limits(-5,26,-8.5,8.5) + +for wi in v_walls: # drawing the map of walls + fig.draw_line([wi.c1[0],wi.c2[0]], [wi.c1[1],wi.c2[1]], "lightGray") + +dist_only = not True # in order to test two scenarii + +if dist_only: # For this scenario we assume that \mathbf{x}(0) is known + t0 = Interval(0.) + p0 = IntervalVector(x_truth(0.)) + cn.add(ctc.eval, [t0, p0, x, v]) # <=> x(t0)=p0 + +# For each observation of the environment: +for t in np.arange(tdomain.lb(), tdomain.ub(), 0.8): + pi_truth = x_truth(t)[0:2] + ti = Interval(t) + pi = IntervalVector(4) + qi = IntervalVector(2) + cn.add(ctc.eval, [ti, pi, x, v]) # <=> x(ti)=pi + cn.add(ctc_walls, [qi]) # <=> \mathbf{q]_i \in \mathbb{W} + + if not dist_only: # range-and-bearing observation (forward-looking sonar) + di = IntervalVector(2) + yi = IntervalVector(2) + yi[0] = Interval(shorter_dist_to_walls(v_walls, pi_truth, x_truth(t)[2])) + yi[1] = Interval(x_truth(t)[2]) # bearing is zero, azimuth is the heading + + yi[0].inflate(0.02) + yi[1].inflate(0.05) + + cn.add(ctc_minus, [qi, cn.subvector(pi,0,1), di]) # <=> qi-pi[0,1]=di + cn.add(ctc.polar, [di, yi]) # di[0]=y1[0]*cos(yi[1]), di[1]=y1[0]*sin(yi[1]) + + fig.draw_pie(pi_truth[0], pi_truth[1], yi[0] | Interval(0), yi[1], "lightGray[#FFFFFF88]") + fig.draw_pie(pi_truth[0], pi_truth[1], yi[0], yi[1]) + + else: # range-only observation + yi = Interval(shorter_contact_to_walls(v_walls, pi_truth)[0]) + yi.inflate(0.02) + cn.add(ctc.dist, [cn.subvector(pi,0,1), qi, yi]) # <=> (pi[0]-qi[0])^2+(pi[1]-qi[1])^2=yi^2 + fig.draw_circle(pi_truth[0], pi_truth[1], yi.mid(), "black[#80808088]") + + fig.draw_vehicle([x_truth(t)[0], x_truth(t)[1], x_truth(t)[2]], 0.7) + +cn.contract(True) + +fig.show(0.) +fig.axis_limits(-5,26,-8.5,8.5) +endDrawing() \ No newline at end of file diff --git a/examples/robotics/old_ex_02_lowcost_beacons/CMakeLists.txt b/examples/robotics/old_ex_02_lowcost_beacons/CMakeLists.txt index 15e033f3..01db341e 100644 --- a/examples/robotics/old_ex_02_lowcost_beacons/CMakeLists.txt +++ b/examples/robotics/old_ex_02_lowcost_beacons/CMakeLists.txt @@ -22,7 +22,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/tuto/01_getting_started/01_getting_started.py b/examples/tuto/01_getting_started/01_getting_started.py index f6f1c537..692375f1 100644 --- a/examples/tuto/01_getting_started/01_getting_started.py +++ b/examples/tuto/01_getting_started/01_getting_started.py @@ -1,5 +1,5 @@ # Codac - Examples -# Dynamic range-only localization +# Getting started: 2 minutes to Codac # ---------------------------------------------------------------------------- from codac import * diff --git a/examples/tuto/01_getting_started/CMakeLists.txt b/examples/tuto/01_getting_started/CMakeLists.txt index e4c08cf5..a8e4ae9e 100644 --- a/examples/tuto/01_getting_started/CMakeLists.txt +++ b/examples/tuto/01_getting_started/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(01_getting_started LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need @@ -22,7 +25,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/tuto/01_getting_started/a01_getting_started.m b/examples/tuto/01_getting_started/a01_getting_started.m new file mode 100644 index 00000000..e593481f --- /dev/null +++ b/examples/tuto/01_getting_started/a01_getting_started.m @@ -0,0 +1,90 @@ +% Codac - Examples +% Getting started: 2 minutes to Codac +% ---------------------------------------------------------------------------- + +import py.codac.* + + +% =================== 0. Parameters, truth and data ==================== + +dt = 0.01; % timestep for tubes accuracy +tdomain = Interval(0, 3); % temporal limits [t_0,t_f]=[0,3] + +x = TubeVector(tdomain, dt, int32(4)); % 4d tube for state vectors +v = TubeVector(tdomain, dt, int32(4)); % 4d tube for derivatives of the states +u = TubeVector(tdomain, dt, int32(2)); % 2d tube for inputs of the system + +x_truth = TrajectoryVector(tdomain, TFunction(['(' ... + '10*cos(t)+t ;' ... + '5*sin(2*t)+t ;' ... + 'atan2((10*cos(2*t)+1),(-10*sin(t)+1)) ;' ... + 'sqrt((-10*sin(t)+1)^2+(10*cos(2*t)+1)^2))'])); % actual trajectory + +% Continuous measurements coming from the truth +measured_psi = x_truth.getitem(int32(2)).sample(dt).make_continuous(); +measured_psi = measured_psi + RandTrajectory(tdomain, dt, Interval(-0.01,0.01)); % adding some noise +measured_speed = x_truth.getitem(int32(3)).sample(dt); +measured_speed = measured_speed + RandTrajectory(tdomain, dt, Interval(-0.01,0.01)); % adding some noise + + +% =============== 1. Defining domains for our variables ================ + +x.setitem(int32(2), Tube(measured_psi, dt).inflate(0.01)); % measured_psi is a set of measurements +x.setitem(int32(3), Tube(measured_speed, dt).inflate(0.01)); + +e_y = Interval(-0.1,0.1); +y = {Interval(1.9+e_y), Interval(3.6+e_y), ... + Interval(2.8+e_y)}; % set of range-only observations +b = {[8,3],[0,5],[-2,1]}; % positions of the three 2d landmarks +t = [0.3, 1.5, 2.0]; % times of measurements + + +% =========== 2. Defining contractors to deal with equations =========== + +ctc_f = CtcFunction(Function('v[4]', 'x[4]', 'u[2]', '(v[0]-x[3]*cos(x[2]) ; v[1]-x[3]*sin(x[2]) ; v[2]-u[0] ; v[3]-u[1])')); + + +% =============== 3. Adding the contractors to a network =============== + +cn = ContractorNetwork(); % creating a network + +cn.add(ctc_f, py.list({v, x, u})); % adding the f constraint + +for i=1:length(y) % we add the observ. constraint for each range-only measurement + + p = cn.create_interm_var(IntervalVector(int32(4))); % intermed. variable (state at t_i) + + % Distance constraint: relation between the state at t_i and the ith beacon position + cn.add(CtcDist(), py.list({cn.subvector(p,int32(0),int32(1)), py.list(b{i}), y{i}})); + + % Eval constraint: relation between the state at t_i and all the states over [t_0,t_f]= + cn.add(CtcEval(), py.list({t(i), p, x, v})); +end + + +% ======================= 4. Solving the problem ======================= + +cn.contract(true); + + +% ============================ 5. Graphics ============================= + +beginDrawing(); +fig = VIBesFigMap('fig'); +fig.set_properties(int32(50), int32(50), int32(900), int32(550)); +fig.add_trajectory(x_truth, 'xtruth', int32(0), int32(1), 'white'); +fig.add_tube(x, 'x', int32(0), int32(1)); +fig.smooth_tube_drawing(true); + +for i=1:3 + fig.add_beacon(py.list(b{i}), 0.2); % drawing beacons + fig.draw_ring(b{i}(1), b{i}(2), y{i}, 'darkGray'); % drawing range-only measurements + fig.draw_vehicle(t(i), x_truth, 0.7); % drawing robot position at t +end + +fig.show(0); +endDrawing(); + + +% Checking if this example still works: +assert(x.volume() < 5) % todo: x.contains(x_truth) diff --git a/examples/tuto/02_static_rangeonly/CMakeLists.txt b/examples/tuto/02_static_rangeonly/CMakeLists.txt index da0ba5d9..3c1599bd 100644 --- a/examples/tuto/02_static_rangeonly/CMakeLists.txt +++ b/examples/tuto/02_static_rangeonly/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(02_static_rangeonly LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need @@ -22,7 +25,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/tuto/03_static_rangebearing/CMakeLists.txt b/examples/tuto/03_static_rangebearing/CMakeLists.txt index 5ee96f0d..bafc4fd0 100644 --- a/examples/tuto/03_static_rangebearing/CMakeLists.txt +++ b/examples/tuto/03_static_rangebearing/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(03_static_rangebearing LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need @@ -22,7 +25,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/tuto/03_static_rangebearing/a03_static_rangebearing.m b/examples/tuto/03_static_rangebearing/a03_static_rangebearing.m new file mode 100644 index 00000000..95abb361 --- /dev/null +++ b/examples/tuto/03_static_rangebearing/a03_static_rangebearing.m @@ -0,0 +1,86 @@ +% Codac - Examples +% Static range-bearing localization +% ---------------------------------------------------------------------------- + +import py.codac.* + +% =================== 0. Parameters, truth and data ==================== + +% Truth (unknown pose) +x_truth = [0,0,pi/6]; % (x,y,heading) + +% Creating random map of landmarks +map_area = IntervalVector(int32(2), [-8,8]); +v_map = DataLoader().generate_landmarks_boxes(map_area, int32(1)); + +% The following function generates a set of [range]x[bearing] values +v_obs = DataLoader().generate_static_observations(py.list(x_truth), v_map, false); + +% Adding uncertainties on the measurements +for i=1:length(v_obs) % for each observation: + v_obs{i}.getitem(int32(0)).inflate(0.3); % range + v_obs{i}.getitem(int32(1)).inflate(0.1); % bearing +end + + +% =============== 1. Defining domains for our variables ================ + +x = IntervalVector(int32(2)); % unknown position +heading = Interval(x_truth(3)).inflate(0.01); % measured heading + + +% =========== 2. Defining contractors to deal with equations =========== + +ctc_plus = CtcFunction(Function('a', 'b', 'c', 'a+b-c')); % a+b=c +ctc_minus = CtcFunction(Function('a', 'b', 'c', 'a-b-c')); % a-b=c +% We also use the predefined contractor CtcPolar(), no need to build it + + +% =============== 3. Adding the contractors to a network =============== + +cn = ContractorNetwork(); + +for i=1:length(v_obs) + + % Intermediate variables + alpha = cn.create_interm_var(Interval()); + d = cn.create_interm_var(IntervalVector(int32(2))); + + cn.add(ctc_plus, py.list({v_obs{i}.getitem(int32(1)), heading, alpha})); + cn.add(ctc_minus, py.list({v_map{i}, x, d})); + cn.add(CtcPolar(), py.list({d, v_obs{i}.getitem(int32(0)), alpha})); +end + + +% ======================= 4. Solving the problem ======================= + +cn.contract(); + + +% ============================ 5. Graphics ============================= + +beginDrawing(); + +fig = VIBesFigMap('Map'); +fig.set_properties(int32(50), int32(50), int32(600), int32(600)); + +for i=1:length(v_map) + iv = v_map{i}; + fig.add_beacon(iv.mid(), 0.2); +end + +for i=1:length(v_obs) + y = v_obs{i}; + fig.draw_pie(x_truth(1), x_truth(2), y.getitem(int32(0)).union(Interval(0)), heading+y.getitem(int32(1)), 'lightGray'); + fig.draw_pie(x_truth(1), x_truth(2), y.getitem(int32(0)), heading+y.getitem(int32(1)), 'gray'); +end + +fig.draw_vehicle(py.list(x_truth),0.5); +fig.draw_box(x); % estimated position +fig.show(); + +endDrawing(); + + +% Checking if this example still works: +assert(x.contains(py.list(x_truth(1:2)))) diff --git a/examples/tuto/04_dyn_rangeonly/CMakeLists.txt b/examples/tuto/04_dyn_rangeonly/CMakeLists.txt index 7bae7641..7dfa4812 100644 --- a/examples/tuto/04_dyn_rangeonly/CMakeLists.txt +++ b/examples/tuto/04_dyn_rangeonly/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(04_dyn_rangeonly LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need @@ -22,7 +25,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/tuto/05_dyn_rangebearing/05_dyn_rangebearing.py b/examples/tuto/05_dyn_rangebearing/05_dyn_rangebearing.py index 0104b19e..3e4256fa 100644 --- a/examples/tuto/05_dyn_rangebearing/05_dyn_rangebearing.py +++ b/examples/tuto/05_dyn_rangebearing/05_dyn_rangebearing.py @@ -1,5 +1,5 @@ # Codac - Examples -# Dynamic range-only localization +# Dynamic range-bearing localization # ---------------------------------------------------------------------------- from codac import * diff --git a/examples/tuto/05_dyn_rangebearing/CMakeLists.txt b/examples/tuto/05_dyn_rangebearing/CMakeLists.txt index 8b512ca5..6086e6b1 100644 --- a/examples/tuto/05_dyn_rangebearing/CMakeLists.txt +++ b/examples/tuto/05_dyn_rangebearing/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(05_dyn_rangebearing LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need @@ -22,7 +25,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/tuto/05_dyn_rangebearing/a05_dyn_rangebearing.m b/examples/tuto/05_dyn_rangebearing/a05_dyn_rangebearing.m new file mode 100644 index 00000000..1bf05875 --- /dev/null +++ b/examples/tuto/05_dyn_rangebearing/a05_dyn_rangebearing.m @@ -0,0 +1,111 @@ +% Codac - Examples +% Dynamic range-bearing localization +% ---------------------------------------------------------------------------- + +import py.codac.* + + +% =================== 0. Parameters, truth and data ==================== + +dt = 0.05; % timestep for tubes accuracy +tdomain = Interval(0,3); % temporal limits [t_0,t_f]=[0,3] + +x_truth = TrajectoryVector(tdomain, TFunction(['(' ... + '10*cos(t)+t ;' ... + '5*sin(2*t)+t ;' ... + 'atan2((10*cos(2*t)+1),(-10*sin(t)+1)) ;' ... + 'sqrt((-10*sin(t)+1)^2+(10*cos(2*t)+1)^2))'])); % actual trajectory + +% Continuous measurements coming from the truth +measured_psi = x_truth.getitem(int32(2)).sample(dt).make_continuous(); +measured_psi = measured_psi + RandTrajectory(tdomain, dt, Interval(-0.01,0.01)); % adding some noise +measured_speed = x_truth.getitem(int32(3)).sample(dt); +measured_speed = measured_speed + RandTrajectory(tdomain, dt, Interval(-0.01,0.01)); % adding some noise + +% Creating random map of landmarks +map_area = IntervalVector(int32(2), [-8,8]); +v_map = DataLoader().generate_landmarks_boxes(map_area, int32(30)); + +% The following function generates a set of [range]x[bearing] values +v_obs = DataLoader().generate_observations(x_truth, v_map, int32(10)); + +% Adding uncertainties on the measurements +for i=1:length(v_obs) % for each observation: + obs = v_obs{i}; + obs.getitem(int32(1)).inflate(0.3); % range + obs.getitem(int32(2)).inflate(0.1); % bearing +end + +% =============== 1. Defining domains for our variables ================ + +x = TubeVector(tdomain, dt, int32(4)); % 4d tube for state vectors +v = TubeVector(tdomain, dt, int32(4)); % 4d tube for derivatives of the states +u = TubeVector(tdomain, dt, int32(2)); % 2d tube for inputs of the system + +x.setitem(int32(2), Tube(measured_psi, dt).inflate(0.01)); % measured_psi is a set of measurements +x.setitem(int32(3), Tube(measured_speed, dt).inflate(0.01)); + + +% =========== 2. Defining contractors to deal with equations =========== + +ctc_f = CtcFunction(Function('v[4]', 'x[4]', 'u[2]', ... + '(v[0]-x[3]*cos(x[2]) ; v[1]-x[3]*sin(x[2]) ; v[2]-u[0] ; v[3]-u[1])')); +ctc_plus = CtcFunction(Function('a', 'b', 'c', 'a+b-c')); % a+b=c +ctc_minus = CtcFunction(Function('a', 'b', 'c', 'a-b-c')); % a-b=c +% We also use the predefined contractors CtcPolar(), CtcEval(), no need to build them + + +% =============== 3. Adding the contractors to a network =============== + +cn = ContractorNetwork(); % creating a network +cn.add(ctc_f, py.list({v, x, u})); % adding the f constraint + +for i=1:length(v_obs) % we add the observ. constraint for each range-only measurement + y = v_obs{i}; + + % Intermediate variables + alpha = cn.create_interm_var(Interval()); % absolute angle robot-landmark + d = cn.create_interm_var(IntervalVector(int32(2))); % dist robot-landmark + p = cn.create_interm_var(IntervalVector(int32(4))); % state at t_i + + cn.add(ctc_plus, py.list({y.getitem(int32(2)), p.getitem(int32(2)), alpha})); + cn.add(ctc_minus, py.list({cn.subvector(y,int32(3),int32(4)), cn.subvector(p,int32(0),int32(1)), d})); + cn.add(CtcPolar(), py.list({d, y.getitem(int32(1)), alpha})); + cn.add(CtcEval(), py.list({y.getitem(int32(0)), p, x, v})); +end + + +% ======================= 4. Solving the problem ======================= + +cn.contract(true); + + +% ============================ 5. Graphics ============================= + +beginDrawing(); +fig = VIBesFigMap('fig'); +fig.set_properties(int32(50), int32(50), int32(900), int32(550)); +fig.add_trajectory(x_truth, 'xtruth', int32(0), int32(1), int32(2)); +fig.add_tube(x, 'x', int32(0), int32(1)); +fig.smooth_tube_drawing(true); + +for i=1:length(v_map) + b = v_map{i}; + fig.add_beacon(b.mid(), 0.2); % drawing beacons +end + +for i=1:length(v_obs) + y = v_obs{i}; + t_obs = y.getitem(int32(0)).mid(); + t_state = x_truth(t_obs); + fig.draw_pie(t_state{1}, t_state{2}, y.getitem(int32(1)).union(Interval(0.01)), t_state{3} + y.getitem(int32(2)), 'lightGray'); % drawing range-bearing measurements + fig.draw_pie(t_state{1}, t_state{2}, y.getitem(int32(1)), t_state{3} + y.getitem(int32(2)), 'darkGray'); % drawing range-bearing measurements + fig.draw_vehicle(t_obs, x_truth, 0.7); +end + +fig.show(double(0)); +endDrawing(); + + +% Checking if this example still works: +assert(x.contains(x_truth) == py.codac.core.BoolInterval(int32(2))); diff --git a/packages/bionic/armhf/libcodac-dev/DEBIAN/control b/packages/bionic/armhf/libcodac-dev/DEBIAN/control deleted file mode 100644 index fc115664..00000000 --- a/packages/bionic/armhf/libcodac-dev/DEBIAN/control +++ /dev/null @@ -1,9 +0,0 @@ -Package: libcodac-dev -Version: 1 -Architecture: armhf -Depends: libeigen3-dev, libibex-dev -Section: math -Priority: optional -Description: Codac is a library providing tools for constraint programming over reals, trajectories and sets -Maintainer: codac.io -Homepage: codac.io diff --git a/packages/choco/codac/codac.nuspec b/packages/choco/codac/codac.nuspec index 98ca2ac4..650a4e12 100644 --- a/packages/choco/codac/codac.nuspec +++ b/packages/choco/codac/codac.nuspec @@ -21,15 +21,31 @@ Codac is a library providing tools for constraint programming over reals, trajec - Optionally, download and run https://github.com/ENSTABretagneRobotics/VIBES/releases/download/0.2.3/VIBes-0.2.3-win32.exe before running the project, and check that a tube appears in VIBes window. ## Troubleshooting -- Check that all the packages and their dependencies were installed, if one failed (e.g. due to network-related errors) try to reinstall it using --force or try a previous version... -- 32 bit versions of latest Qt Creator do not seem available, if needed use `choco install -y qtcreator --version=4.13.3 --x86 --force` +- Check that all the packages and their dependencies were installed, if one failed (e.g. due to network-related errors) try to reinstall it using `--force` or try a previous version... +- 32 bit versions of Qt Creator do not seem available any more, see https://github.com/AdmiringWorm/chocolatey-packages/issues/362. - If multiple compilers are already installed, Qt Creator might show multiple possibilities in the Configure Project panel, ensure you choose one compatible with https://chocolatey.org/packages/codac#dependencies. + +## Package parameters +The following package parameters can be set: +- `/url:URL` - Will install the specified binary package (e.g. built for Visual Studio), see versions from https://github.com/codac-team/codac/releases (the Windows `PATH` might need to be updated manually with e.g. `C:\ProgramData\chocolatey\lib\ibex\bin`, etc.). By default, only the MinGW libraries compatible with the corresponding MinGW Chocolatey package dependency are installed. Use the standard parameter `choco install --ignore-dependencies ...` to avoid installing the default MinGW and IBEX Chocolatey package dependencies if needed (you might want to install manually [IBEX](https://community.chocolatey.org/packages/ibex) package with the corresponding parameters, as well as the corresponding compiler and the Eigen package). +- `/checksum:SHA256` - SHA256 checksum of the binary package specified by the `/url` parameter. If needed, use the standard parameter `choco install --ignore-checksums ...` for trusted sources. +- `/urlX:URL` - Same as above, with X in [1,99], except this will not disable the installation of the MinGW libraries compatible with the corresponding MinGW Chocolatey package dependency. +- `/checksumX:SHA256` - SHA256 checksum of the binary package specified by the `/urlX` parameter. If needed, use the standard parameter `choco install --ignore-checksums ...` for trusted sources. +- `/InstallDir:INSTALLDIR` - Installation directory. +- `/Path` - Will try to update Windows `PATH`. +- `/NoRegistry` - Will not try to update Windows registry. +To pass package parameters, use `--params "''"` (e.g. `choco install codac --params "'/Path /NoRegistry'"`), and to install another binary package, try e.g. +``` +choco install -y chocolatey-core.extension +choco install -y --ignore-dependencies codac --params "'/url:https://github.com/codac-team/codac/releases/download/v1/codac_x64_vc17.zip'" +``` https://github.com/codac-team/codac/releases - + + - + diff --git a/packages/choco/codac/tools/LICENSE.txt b/packages/choco/codac/tools/LICENSE.txt deleted file mode 100644 index dc1e48ed..00000000 --- a/packages/choco/codac/tools/LICENSE.txt +++ /dev/null @@ -1,170 +0,0 @@ - -From: https://github.com/codac-team/codac/blob/master/COPYING.LESSER - -LICENSE - -GNU LESSER GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - - This version of the GNU Lesser General Public License incorporates -the terms and conditions of version 3 of the GNU General Public -License, supplemented by the additional permissions listed below. - - 0. Additional Definitions. - - As used herein, "this License" refers to version 3 of the GNU Lesser -General Public License, and the "GNU GPL" refers to version 3 of the GNU -General Public License. - - "The Library" refers to a covered work governed by this License, -other than an Application or a Combined Work as defined below. - - An "Application" is any work that makes use of an interface provided -by the Library, but which is not otherwise based on the Library. -Defining a subclass of a class defined by the Library is deemed a mode -of using an interface provided by the Library. - - A "Combined Work" is a work produced by combining or linking an -Application with the Library. The particular version of the Library -with which the Combined Work was made is also called the "Linked -Version". - - The "Minimal Corresponding Source" for a Combined Work means the -Corresponding Source for the Combined Work, excluding any source code -for portions of the Combined Work that, considered in isolation, are -based on the Application, and not on the Linked Version. - - The "Corresponding Application Code" for a Combined Work means the -object code and/or source code for the Application, including any data -and utility programs needed for reproducing the Combined Work from the -Application, but excluding the System Libraries of the Combined Work. - - 1. Exception to Section 3 of the GNU GPL. - - You may convey a covered work under sections 3 and 4 of this License -without being bound by section 3 of the GNU GPL. - - 2. Conveying Modified Versions. - - If you modify a copy of the Library, and, in your modifications, a -facility refers to a function or data to be supplied by an Application -that uses the facility (other than as an argument passed when the -facility is invoked), then you may convey a copy of the modified -version: - - a) under this License, provided that you make a good faith effort to - ensure that, in the event an Application does not supply the - function or data, the facility still operates, and performs - whatever part of its purpose remains meaningful, or - - b) under the GNU GPL, with none of the additional permissions of - this License applicable to that copy. - - 3. Object Code Incorporating Material from Library Header Files. - - The object code form of an Application may incorporate material from -a header file that is part of the Library. You may convey such object -code under terms of your choice, provided that, if the incorporated -material is not limited to numerical parameters, data structure -layouts and accessors, or small macros, inline functions and templates -(ten or fewer lines in length), you do both of the following: - - a) Give prominent notice with each copy of the object code that the - Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the object code with a copy of the GNU GPL and this license - document. - - 4. Combined Works. - - You may convey a Combined Work under terms of your choice that, -taken together, effectively do not restrict modification of the -portions of the Library contained in the Combined Work and reverse -engineering for debugging such modifications, if you also do each of -the following: - - a) Give prominent notice with each copy of the Combined Work that - the Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the Combined Work with a copy of the GNU GPL and this license - document. - - c) For a Combined Work that displays copyright notices during - execution, include the copyright notice for the Library among - these notices, as well as a reference directing the user to the - copies of the GNU GPL and this license document. - - d) Do one of the following: - - 0) Convey the Minimal Corresponding Source under the terms of this - License, and the Corresponding Application Code in a form - suitable for, and under terms that permit, the user to - recombine or relink the Application with a modified version of - the Linked Version to produce a modified Combined Work, in the - manner specified by section 6 of the GNU GPL for conveying - Corresponding Source. - - 1) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (a) uses at run time - a copy of the Library already present on the user's computer - system, and (b) will operate properly with a modified version - of the Library that is interface-compatible with the Linked - Version. - - e) Provide Installation Information, but only if you would otherwise - be required to provide such information under section 6 of the - GNU GPL, and only to the extent that such information is - necessary to install and execute a modified version of the - Combined Work produced by recombining or relinking the - Application with a modified version of the Linked Version. (If - you use option 4d0, the Installation Information must accompany - the Minimal Corresponding Source and Corresponding Application - Code. If you use option 4d1, you must provide the Installation - Information in the manner specified by section 6 of the GNU GPL - for conveying Corresponding Source.) - - 5. Combined Libraries. - - You may place library facilities that are a work based on the -Library side by side in a single library together with other library -facilities that are not Applications and are not covered by this -License, and convey such a combined library under terms of your -choice, if you do both of the following: - - a) Accompany the combined library with a copy of the same work based - on the Library, uncombined with any other library facilities, - conveyed under the terms of this License. - - b) Give prominent notice with the combined library that part of it - is a work based on the Library, and explaining where to find the - accompanying uncombined form of the same work. - - 6. Revised Versions of the GNU Lesser General Public License. - - The Free Software Foundation may publish revised and/or new versions -of the GNU Lesser General Public License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - - Each version is given a distinguishing version number. If the -Library as you received it specifies that a certain numbered version -of the GNU Lesser General Public License "or any later version" -applies to it, you have the option of following the terms and -conditions either of that published version or of any later version -published by the Free Software Foundation. If the Library as you -received it does not specify a version number of the GNU Lesser -General Public License, you may choose any version of the GNU Lesser -General Public License ever published by the Free Software Foundation. - - If the Library as you received it specifies that a proxy can decide -whether future versions of the GNU Lesser General Public License shall -apply, that proxy's public statement of acceptance of any version is -permanent authorization for you to choose that version for the -Library. diff --git a/packages/choco/codac/tools/VERIFICATION.txt b/packages/choco/codac/tools/VERIFICATION.txt deleted file mode 100644 index f7ec2e81..00000000 --- a/packages/choco/codac/tools/VERIFICATION.txt +++ /dev/null @@ -1,6 +0,0 @@ - -VERIFICATION -Verification is intended to assist the Chocolatey moderators and community -in verifying that this package's contents are trustworthy. - -This package is generated from https://github.com/codac-team/codac (the official Codac GitHub source repository) or https://github.com/lebarsfa/codac/master, which is a fork (the modifications made can be listed on https://github.com/codac-team/codac/compare/master...lebarsfa:master). The automated build log can be seen by clicking on "Windows MinGW 8.1.0 x64" and "Windows MinGW 8.1.0 x86" jobs on https://github.com/codac-team/codac/actions/workflows/unixmatrix.yml or https://github.com/lebarsfa/codac/actions/workflows/unixmatrix.yml, and signing in with GitHub. In particular, a sha256 checksum is visible for each generated .nupkg, as it is computed in .github/workflows/unixmatrix.yml using checksum command. Those temporary .nupkg are available on https://github.com/codac-team/codac/releases or https://github.com/lebarsfa/codac/releases and are used to create this package : it can be checked e.g. using WinMerge that x86 folder from this package contains the bin, include, lib, share folders from the "--x86" package on GitHub and all the other folders are from the non-"--x86" package. diff --git a/packages/choco/codac/tools/chocolateybeforemodify.ps1 b/packages/choco/codac/tools/chocolateybeforemodify.ps1 new file mode 100644 index 00000000..b8b12186 --- /dev/null +++ b/packages/choco/codac/tools/chocolateybeforemodify.ps1 @@ -0,0 +1,34 @@ +$ErrorActionPreference = 'Stop'; # Stop on all errors. + +# Source variables which are shared between install and uninstall. +. $PSScriptRoot\sharedVars.ps1 + +$toolsDir = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)" + +$pp = Get-PackageParameters +$packageDir = Join-Path "$toolsDir" ".." -Resolve +$installDir = Join-Path "$packageDir" ".." -Resolve +if ($pp.InstallDir -or $pp.InstallationPath) { + $installDir = $pp.InstallDir + $pp.InstallationPath +} +Write-Host "Codac is going to be uninstalled from '$installDir'" + +$root = Join-Path $installDir "codac" + +$newpath = [environment]::GetEnvironmentVariable("Path","Machine") +$newpath = ($newpath.Split(';') | Where-Object { $_ -ne "$root\bin" }) -join ';' +[environment]::SetEnvironmentVariable("Path",$newpath,"Machine") + +try { + Get-ItemProperty -Path $CMakeSystemRepositoryPath\$CMakePackageName | Select-Object -ExpandProperty "$CMakePackageName$CMakePackageVer`_$arch" -ErrorAction Stop | Out-Null + Remove-ItemProperty -Path $CMakeSystemRepositoryPath\$CMakePackageName -Name "$CMakePackageName$CMakePackageVer`_$arch" +} +catch { + +} + +if (Test-Path $root) { + if ((Resolve-Path $root).Path -notcontains (Resolve-Path $packageDir).Path) { + Remove-Item -Recurse -Force $root + } +} diff --git a/packages/choco/codac/tools/chocolateyinstall.ps1 b/packages/choco/codac/tools/chocolateyinstall.ps1 index 1f8d009f..3c59891f 100644 --- a/packages/choco/codac/tools/chocolateyinstall.ps1 +++ b/packages/choco/codac/tools/chocolateyinstall.ps1 @@ -1,19 +1,172 @@ $ErrorActionPreference = 'Stop'; # Stop on all errors. -# Source registry key values which are shared between install and uninstall. -. $PSScriptRoot\regKeys.ps1 +# Source variables which are shared between install and uninstall. +. $PSScriptRoot\sharedVars.ps1 -New-Item "$CMakeSystemRepositoryPath\$CMakePackageName" -ItemType directory -Force -New-ItemProperty -Name "CMakePackageDir" -PropertyType String -Value "$env:ChocolateyPackageFolder\share\$CMakePackageName\cmake" -Path "$CMakeSystemRepositoryPath\$CMakePackageName" -Force +$toolsDir = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)" -# Get 32 bit binaries if needed and delete unnecessary files... -if (Test-Path "$env:ChocolateyPackageFolder\x86") { - if ((Get-ProcessorBits 32) -or $env:ChocolateyForceX86 -eq $true) { - Copy-Item -Recurse -Force -Path "$env:ChocolateyPackageFolder\x86\*" -Destination "$env:ChocolateyPackageFolder" +$pp = Get-PackageParameters +$packageDir = Join-Path "$toolsDir" ".." -Resolve +$installDir = Join-Path "$packageDir" ".." -Resolve +if ($pp.InstallDir -or $pp.InstallationPath) { + $installDir = $pp.InstallDir + $pp.InstallationPath +} +Write-Host "Codac is going to be installed in '$installDir'" + +$root = Join-Path $installDir "codac" +New-Item -ItemType Directory -Force -Path $root | Out-Null + +if (!$pp['url']) { + $url = 'https://github.com/codac-team/codac/releases/download/v1/codac_x86_mingw11.zip' + $checksum = 'EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE' + $url64 = 'https://github.com/codac-team/codac/releases/download/v1/codac_x64_mingw11.zip' + $checksum64 = 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' + $packageArgs = @{ + packageName = $env:ChocolateyPackageName + unzipLocation = Join-Path "$root" ".." + url = $url + url64bit = $url64 + checksum = $checksum + checksumType = 'sha256' + checksum64 = $checksum64 + checksumType64= 'sha256' } - Remove-Item -Recurse -Force "$env:ChocolateyPackageFolder\x86" + Install-ChocolateyZipPackage @packageArgs + + $runtime = "mingw" } +else { + $url = $pp['url'] + $checksum = $pp['checksum'] + $packageArgs = @{ + packageName = $env:ChocolateyPackageName + unzipLocation = Join-Path "$root" ".." + url = $url + url64bit = $url + checksum = $checksum + checksumType = 'sha256' + checksum64 = $checksum + checksumType64= 'sha256' + } + Install-ChocolateyZipPackage @packageArgs -#Install-BinFile -Name libcodac.a -Path "$env:ChocolateyPackageFolder\lib" -#Install-BinFile -Name libcodac-rob.a -Path "$env:ChocolateyPackageFolder\lib" -#Install-BinFile -Name libcodac-pybex.a -Path "$env:ChocolateyPackageFolder\lib" + # Analyze url to guess what to add to Windows PATH... + if ($url -match "x86") { + $arch = "x86" + } + else { + $arch = "x64" + } + if ($url -match "vc8") { + $runtime = "vc8" + } + if ($url -match "vc9") { + $runtime = "vc9" + } + elseif ($url -match "vc10") { + $runtime = "vc10" + } + elseif ($url -match "vc11") { + $runtime = "vc11" + } + elseif ($url -match "vc12") { + $runtime = "vc12" + } + elseif ($url -match "vc14") { + $runtime = "vc14" + } + elseif ($url -match "vc15") { + $runtime = "vc15" + } + elseif ($url -match "vc16") { + $runtime = "vc16" + } + elseif ($url -match "vc17") { + $runtime = "vc17" + } + elseif ($url -match "vc18") { + $runtime = "vc18" + } + else { + $runtime = "mingw" + } +} + +if (!$pp['NoRegistry']) { + New-Item "$CMakeSystemRepositoryPath\$CMakePackageName" -ItemType directory -Force + New-ItemProperty -Name "$CMakePackageName$CMakePackageVer`_$arch" -PropertyType String -Value "$root\share\$CMakePackageName\cmake" -Path "$CMakeSystemRepositoryPath\$CMakePackageName" -Force +} +$pathtoadd = "$root\bin" +if (($pp['Path']) -and !([environment]::GetEnvironmentVariable("Path","Machine") -match [regex]::escape($pathtoadd))) { + $newpath = [environment]::GetEnvironmentVariable("Path","Machine") + ";$pathtoadd" + [environment]::SetEnvironmentVariable("Path",$newpath,"Machine") +} + +for ($i = 1; $i -le 99; $i++) { + if ($pp['url'+$i]) { + $url = $pp['url'+$i] + $checksum = $pp['checksum'+$i] + $packageArgs = @{ + packageName = $env:ChocolateyPackageName + unzipLocation = Join-Path "$root" ".." + url = $url + url64bit = $url + checksum = $checksum + checksumType = 'sha256' + checksum64 = $checksum + checksumType64= 'sha256' + } + Install-ChocolateyZipPackage @packageArgs + + # Analyze url to guess what to add to Windows PATH... + if ($url -match "x86") { + $arch = "x86" + } + else { + $arch = "x64" + } + if ($url -match "vc8") { + $runtime = "vc8" + } + if ($url -match "vc9") { + $runtime = "vc9" + } + elseif ($url -match "vc10") { + $runtime = "vc10" + } + elseif ($url -match "vc11") { + $runtime = "vc11" + } + elseif ($url -match "vc12") { + $runtime = "vc12" + } + elseif ($url -match "vc14") { + $runtime = "vc14" + } + elseif ($url -match "vc15") { + $runtime = "vc15" + } + elseif ($url -match "vc16") { + $runtime = "vc16" + } + elseif ($url -match "vc17") { + $runtime = "vc17" + } + elseif ($url -match "vc18") { + $runtime = "vc18" + } + else { + $runtime = "mingw" + } + + if (!$pp['NoRegistry']) { + New-Item "$CMakeSystemRepositoryPath\$CMakePackageName" -ItemType directory -Force + New-ItemProperty -Name "$CMakePackageName$CMakePackageVer`_$arch" -PropertyType String -Value "$root\share\$CMakePackageName\cmake" -Path "$CMakeSystemRepositoryPath\$CMakePackageName" -Force + } + $pathtoadd = "$root\bin" + if (($pp['Path']) -and !([environment]::GetEnvironmentVariable("Path","Machine") -match [regex]::escape($pathtoadd))) { + $newpath = [environment]::GetEnvironmentVariable("Path","Machine") + ";$pathtoadd" + [environment]::SetEnvironmentVariable("Path",$newpath,"Machine") + } + } +} diff --git a/packages/choco/codac/tools/chocolateyuninstall.ps1 b/packages/choco/codac/tools/chocolateyuninstall.ps1 index 2bd9f0c1..b8b12186 100644 --- a/packages/choco/codac/tools/chocolateyuninstall.ps1 +++ b/packages/choco/codac/tools/chocolateyuninstall.ps1 @@ -1,13 +1,34 @@ $ErrorActionPreference = 'Stop'; # Stop on all errors. -# Source registry key values which are shared between install and uninstall. -. $PSScriptRoot\regKeys.ps1 +# Source variables which are shared between install and uninstall. +. $PSScriptRoot\sharedVars.ps1 -#Uninstall-BinFile -Name libcodac-rob.a -#Uninstall-BinFile -Name libcodac.a +$toolsDir = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)" -if (Test-Path $CMakeRegistryPath) { - if (Test-Path $CMakeSystemRepositoryPath) { - Remove-Item "$CMakeSystemRepositoryPath\$CMakePackageName" - } +$pp = Get-PackageParameters +$packageDir = Join-Path "$toolsDir" ".." -Resolve +$installDir = Join-Path "$packageDir" ".." -Resolve +if ($pp.InstallDir -or $pp.InstallationPath) { + $installDir = $pp.InstallDir + $pp.InstallationPath +} +Write-Host "Codac is going to be uninstalled from '$installDir'" + +$root = Join-Path $installDir "codac" + +$newpath = [environment]::GetEnvironmentVariable("Path","Machine") +$newpath = ($newpath.Split(';') | Where-Object { $_ -ne "$root\bin" }) -join ';' +[environment]::SetEnvironmentVariable("Path",$newpath,"Machine") + +try { + Get-ItemProperty -Path $CMakeSystemRepositoryPath\$CMakePackageName | Select-Object -ExpandProperty "$CMakePackageName$CMakePackageVer`_$arch" -ErrorAction Stop | Out-Null + Remove-ItemProperty -Path $CMakeSystemRepositoryPath\$CMakePackageName -Name "$CMakePackageName$CMakePackageVer`_$arch" +} +catch { + +} + +if (Test-Path $root) { + if ((Resolve-Path $root).Path -notcontains (Resolve-Path $packageDir).Path) { + Remove-Item -Recurse -Force $root + } } diff --git a/packages/choco/codac/tools/regKeys.ps1 b/packages/choco/codac/tools/regKeys.ps1 deleted file mode 100644 index bd2b0262..00000000 --- a/packages/choco/codac/tools/regKeys.ps1 +++ /dev/null @@ -1,3 +0,0 @@ -$CMakeRegistryPath = "HKCU:\SOFTWARE\Kitware\CMake" -$CMakeSystemRepositoryPath = "HKLM:\SOFTWARE\Kitware\CMake\Packages" -$CMakePackageName = "Codac" \ No newline at end of file diff --git a/packages/choco/codac/tools/sharedVars.ps1 b/packages/choco/codac/tools/sharedVars.ps1 new file mode 100644 index 00000000..c401315a --- /dev/null +++ b/packages/choco/codac/tools/sharedVars.ps1 @@ -0,0 +1,7 @@ +# Some of these variables might not be used in links to simplify parsing of files... +if ((Get-ProcessorBits 32) -or $env:ChocolateyForceX86 -eq $true) { $arch = "x86" } else { $arch = "x64" } +$MinGWMVer = "11" +$CMakeRegistryPath = "HKCU:\SOFTWARE\Kitware\CMake" +$CMakeSystemRepositoryPath = "HKLM:\SOFTWARE\Kitware\CMake\Packages" +$CMakePackageName = "Codac" +$CMakePackageVer = "1" diff --git a/packages/bionic/amd64/libcodac-dev/DEBIAN/control b/packages/deb/control similarity index 100% rename from packages/bionic/amd64/libcodac-dev/DEBIAN/control rename to packages/deb/control diff --git a/packages/focal/amd64/libcodac-dev/DEBIAN/control b/packages/focal/amd64/libcodac-dev/DEBIAN/control deleted file mode 100644 index 1a587f0c..00000000 --- a/packages/focal/amd64/libcodac-dev/DEBIAN/control +++ /dev/null @@ -1,9 +0,0 @@ -Package: libcodac-dev -Version: 1 -Architecture: amd64 -Depends: libeigen3-dev, libibex-dev -Section: math -Priority: optional -Description: Codac is a library providing tools for constraint programming over reals, trajectories and sets -Maintainer: codac.io -Homepage: codac.io diff --git a/packages/focal/armhf/libcodac-dev/DEBIAN/control b/packages/focal/armhf/libcodac-dev/DEBIAN/control deleted file mode 100644 index fc115664..00000000 --- a/packages/focal/armhf/libcodac-dev/DEBIAN/control +++ /dev/null @@ -1,9 +0,0 @@ -Package: libcodac-dev -Version: 1 -Architecture: armhf -Depends: libeigen3-dev, libibex-dev -Section: math -Priority: optional -Description: Codac is a library providing tools for constraint programming over reals, trajectories and sets -Maintainer: codac.io -Homepage: codac.io diff --git a/packages/genlibcodac-dev.sh b/packages/genlibcodac-dev.sh index 4442d640..c49fa6bd 100644 --- a/packages/genlibcodac-dev.sh +++ b/packages/genlibcodac-dev.sh @@ -12,9 +12,12 @@ REV=$6 #sudo apt-get -y install dpkg-dev +mkdir -p $DIST/$ARCH cd $DIST/$ARCH mkdir -p libcodac-dev/usr cp -Rf ../../../codac/* libcodac-dev/usr/ +mkdir -p libcodac-dev/DEBIAN +cp -Rf ../../deb/control libcodac-dev/DEBIAN/control sed_param=s/Version:\ .*/Version:\ ${VER}/ sed -i "$sed_param" libcodac-dev/DEBIAN/control sed_param=s/Architecture:\ .*/Architecture:\ ${ARCH}/ diff --git a/packages/jammy/amd64/libcodac-dev/DEBIAN/control b/packages/jammy/amd64/libcodac-dev/DEBIAN/control deleted file mode 100644 index 1a587f0c..00000000 --- a/packages/jammy/amd64/libcodac-dev/DEBIAN/control +++ /dev/null @@ -1,9 +0,0 @@ -Package: libcodac-dev -Version: 1 -Architecture: amd64 -Depends: libeigen3-dev, libibex-dev -Section: math -Priority: optional -Description: Codac is a library providing tools for constraint programming over reals, trajectories and sets -Maintainer: codac.io -Homepage: codac.io diff --git a/packages/jammy/armhf/libcodac-dev/DEBIAN/control b/packages/jammy/armhf/libcodac-dev/DEBIAN/control deleted file mode 100644 index fc115664..00000000 --- a/packages/jammy/armhf/libcodac-dev/DEBIAN/control +++ /dev/null @@ -1,9 +0,0 @@ -Package: libcodac-dev -Version: 1 -Architecture: armhf -Depends: libeigen3-dev, libibex-dev -Section: math -Priority: optional -Description: Codac is a library providing tools for constraint programming over reals, trajectories and sets -Maintainer: codac.io -Homepage: codac.io diff --git a/packages/temporary/gennewcodacmanylinux2014_aarch64.sh b/packages/temporary/gennewcodacmanylinux2014_aarch64.sh new file mode 100644 index 00000000..d0f4d138 --- /dev/null +++ b/packages/temporary/gennewcodacmanylinux2014_aarch64.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +set -e -x + +mkdir -p ~/Downloads/newcodac + +cd ~/Downloads/newcodac +rm -Rf codac* +#git clone https://github.com/lebarsfa/codac +git clone https://github.com/codac-team/codac +cd codac +git submodule init +git submodule update +docker pull lebarsfa/manylinux2014_aarch64-for-codac + +chmod a+x scripts/docker/build_pybinding.sh +docker run --rm -v `pwd`:/io lebarsfa/manylinux2014_aarch64-for-codac /io/scripts/docker/build_pybinding.sh +ls wheelhouse + +cd .. diff --git a/packages/temporary/gennewcodacpi_armhf.sh b/packages/temporary/gennewcodacpi_armhf.sh new file mode 100644 index 00000000..9f7ef3d5 --- /dev/null +++ b/packages/temporary/gennewcodacpi_armhf.sh @@ -0,0 +1,77 @@ +#!/bin/bash + +set -e -x + +mkdir -p ~/Downloads/newcodac + +cd ~/Downloads/newcodac +sudo rm -Rf codac* +#git clone https://github.com/lebarsfa/codac +git clone https://github.com/codac-team/codac +cd codac +git submodule init +git submodule update + +for DIST in bookworm bullseye buster; do + +docker pull lebarsfa/pi:$DIST-for-codac +docker run --rm -v `pwd`:/io lebarsfa/pi:$DIST-for-codac /bin/bash -c "uname -a ; cat /etc/os-release ; lsb_release -a ; \ +if [ \"\$(lsb_release -cs)\" = \"bookworm\" ]; then \ + PIP_OPTIONS=--break-system-packages ; \ + #python3 -m pip install \$PIP_OPTIONS --upgrade \"auditwheel\" ; \\ +else \ + PIP_OPTIONS= ; \ + #python3 -m pip install \$PIP_OPTIONS --upgrade \"auditwheel==5.1.2\" ; \\ +fi && \ +sudo apt-get -q update --allow-releaseinfo-change ; sudo apt-get -y install libeigen3-dev python3-dev patchelf || true && \ +python3 -m pip install \$PIP_OPTIONS --upgrade patchelf --prefer-binary --extra-index-url https://www.piwheels.org/simple && \ +python3 -m pip install \$PIP_OPTIONS --upgrade auditwheel --prefer-binary --extra-index-url https://www.piwheels.org/simple && \ +# wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241117/ibex_armhf_\$(lsb_release -cs).zip --no-check-certificate -nv is causing illegal instruction on a Mac M1... \\ +curl -L -O https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241117/ibex_armhf_\$(lsb_release -cs).zip --insecure && \ +unzip -q ibex_armhf_\$(lsb_release -cs).zip && \ +rm -Rf ibex_armhf_\$(lsb_release -cs).zip && \ +sudo cp -Rf ibex/* /usr/local/ && \ +\ +git config --global --add safe.directory /io && \ +cd /io && \ +\ +python3 -m pip install \$PIP_OPTIONS --upgrade pip && \ +python3 -m pip install \$PIP_OPTIONS --upgrade wheel setuptools && \ +mkdir -p build_dir_\$(lsb_release -cs) && cd build_dir_\$(lsb_release -cs) && \ +cmake -E env CXXFLAGS=\"-fPIC\" CFLAGS=\"-fPIC\" cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=ON -DWITH_TUBE_TREE=OFF -DWITH_CAPD=OFF -DWITH_PYTHON=ON .. && \ +make -j4 && \ +\ +make pip_package && \ +echo \"copy wheel and clean build_dir_\$(lsb_release -cs)\" && \ +for whl in *.whl; do \ + # auditwheel might fail, so instead just copy... \\ + auditwheel repair \"\$whl\" -w /io/wheelhouse/ || cp -f \"\$whl\" /io/wheelhouse/\"\$whl\" ; \ +done ; \ +#for file in /io/wheelhouse/*armv6l.whl; do \\ +# cp \"\$file\" \"\${file/armv6l/armv7l}\" ; \\ +#done ; \\ +#for file in /io/wheelhouse/*armv7l.whl; do \\ +# cp \"\$file\" \"\${file/armv7l/armv6l}\" ; \\ +#done ; \\ +\ +python3 -m pip install \$PIP_OPTIONS codac --no-deps --no-index -f /io/wheelhouse && \ +python3 ../examples/tuto/01_getting_started/01_getting_started.py && \ +# Prerequisites for numpy. \\ +sudo apt-get -y install libatlas3-base || true && \ +sudo apt-get -y install libopenblas0-pthread || true && \ +sudo apt-get -y install libgfortran5 || true && \ +python3 -m pip install \$PIP_OPTIONS numpy --prefer-binary --extra-index-url https://www.piwheels.org/simple && \ +python3 -m unittest discover codac.tests && \ +\ +make test ARGS=\"-V --output-on-failure\" && \ +echo \"start of Testing/Temporary/LastTest.log\" && \ +cat Testing/Temporary/LastTest.log && \ +echo \"end of Testing/Temporary/LastTest.log\" ; \ +\ +cd /io && \ +rm -fr build_dir_\$(lsb_release -cs)" +ls wheelhouse + +done + +cd .. diff --git a/packages/xenial/amd64/libcodac-dev/DEBIAN/control b/packages/xenial/amd64/libcodac-dev/DEBIAN/control deleted file mode 100644 index 1a587f0c..00000000 --- a/packages/xenial/amd64/libcodac-dev/DEBIAN/control +++ /dev/null @@ -1,9 +0,0 @@ -Package: libcodac-dev -Version: 1 -Architecture: amd64 -Depends: libeigen3-dev, libibex-dev -Section: math -Priority: optional -Description: Codac is a library providing tools for constraint programming over reals, trajectories and sets -Maintainer: codac.io -Homepage: codac.io diff --git a/packages/xenial/armhf/libcodac-dev/DEBIAN/control b/packages/xenial/armhf/libcodac-dev/DEBIAN/control deleted file mode 100644 index fc115664..00000000 --- a/packages/xenial/armhf/libcodac-dev/DEBIAN/control +++ /dev/null @@ -1,9 +0,0 @@ -Package: libcodac-dev -Version: 1 -Architecture: armhf -Depends: libeigen3-dev, libibex-dev -Section: math -Priority: optional -Description: Codac is a library providing tools for constraint programming over reals, trajectories and sets -Maintainer: codac.io -Homepage: codac.io diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 10ee27ba..4d4cfb4d 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -56,7 +56,9 @@ src/core/cn/codac_py_Variable.cpp src/core/contractors/static/codac_py_Ctc.cpp + src/core/contractors/static/codac_py_Ctc3BCid.cpp src/core/contractors/static/codac_py_CtcBox.cpp + src/core/contractors/static/codac_py_CtcCN.cpp src/core/contractors/static/codac_py_CtcCartProd.cpp src/core/contractors/static/codac_py_CtcDist.cpp src/core/contractors/static/codac_py_CtcFunction.cpp @@ -98,7 +100,9 @@ src/core/geometry/codac_py_geometry.cpp src/core/paving/codac_py_Paving.cpp + src/core/paving/codac_py_SIVIAPaving.cpp src/core/paving/codac_py_Set.cpp + src/core/paving/codac_py_ConnectedSubset.cpp src/core/sivia/codac_py_sivia.cpp @@ -110,6 +114,7 @@ src/robotics/codac_py_DataLoader.cpp src/robotics/codac_py_TPlane.cpp + src/robotics/codac_py_Wall.cpp ) target_include_directories(core @@ -160,6 +165,29 @@ COMMAND ${CMAKE_COMMAND} -E copy "$" "${PYTHON_PACKAGE_DIR}/${PYTHON_PACKAGE_NAME}" ) + # Generating a library codac2.so containing the python binding: + + pybind11_add_module(codac2 SHARED + codac_py_codac2.cpp + src/core/2/actions/codac2_py_Action.cpp + src/core/2/contractors/codac2_py_CtcAction.cpp + ) + + target_include_directories(codac2 + PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/../include + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/ + ${CMAKE_CURRENT_BINARY_DIR}/docstring + ) + + target_link_libraries(codac2 + PRIVATE core codac core ${LIBS} core + ) + + # Copy the generated library in the package folder + add_custom_command(TARGET codac2 POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy "$" "${PYTHON_PACKAGE_DIR}/${PYTHON_PACKAGE_NAME}" + ) + #pybind11_add_module(graphics SHARED # codac_py_VIBesFig.cpp @@ -181,7 +209,7 @@ ################################################################################ if(BUILD_TESTS) - include(CTest) + file(GLOB TESTS_FILES "${PYTHON_PACKAGE_DIR}/${PYTHON_PACKAGE_NAME}/tests/test_*.py" "${PYTHON_PACKAGE_DIR}/${PYTHON_PACKAGE_NAME}/tests/tests_from_pyibex/test_*.py" diff --git a/python/codac/__init__.py b/python/codac/__init__.py index e2dee98b..9d205fdf 100644 --- a/python/codac/__init__.py +++ b/python/codac/__init__.py @@ -26,7 +26,7 @@ def pySIVIA(X0, ops, epsilon, figure_name='', draw_boxes=True, save_result=True, print('\nWarning: pySIVIA(..) is deprecated and has been replaced by SIVIA(..).') print('More information can be found with help(SIVIA).\n') print('Example of use:\n') - print(' SIVIA(x0, sep, 0.05, display_result=True, fig_name="SIVIA", return_result=True, color_map={SetValue.IN:"k[r]", SetValue.OUT:"k[b]", SetValue.UNKNOWN:"k[y]"})\n') + print(' SIVIA(x0, sep, 0.05, regular_paving=False, display_result=True, fig_name="SIVIA", return_result=True, color_map={SetValue.IN:"k[r]", SetValue.OUT:"k[b]", SetValue.UNKNOWN:"k[y]"})\n') cmap = {} if color_in: @@ -36,4 +36,4 @@ def pySIVIA(X0, ops, epsilon, figure_name='', draw_boxes=True, save_result=True, if color_maybe: cmap[SetValue.UNKNOWN] = color_maybe - return SIVIA(X0, ops, epsilon, display_result=draw_boxes, fig_name=figure_name, return_result=save_result, color_map=cmap) \ No newline at end of file + return SIVIA(X0, ops, epsilon, regular_paving=False, display_result=draw_boxes, fig_name=figure_name, return_result=save_result, color_map=cmap) \ No newline at end of file diff --git a/python/codac/tests/test_actions.py b/python/codac/tests/test_actions.py new file mode 100644 index 00000000..cca74b28 --- /dev/null +++ b/python/codac/tests/test_actions.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python + +# Codac tests +# --------------------------------------------------------------------------- +# \date 2023 +# \author Simon Rohou +# \copyright Copyright 2023 Codac Team +# \license This program is distributed under the terms of +# the GNU Lesser General Public License (LGPL). + +import unittest +try: + import numpy as np +except: + print("NUMPY UNAVAILABLE") +from codac import * +from codac.codac2 import * + +class TestActions(unittest.TestCase): + + def tests_action(self): + + x = IntervalVector([[-1,0],[5,6]]) + a = OctaSym([2,-1]) + + print(x) # Result: ([-1, 0] ; [5, 6]) + print(a(x)) # Result: ([5, 6] ; [-0, 1]) + print(a.invert()(a(x))) # Result: ([-1, 0] ; [5, 6]) + + print(a) # Result: (2 -1) + print(a.invert()) # Result: (-2 1) + print(a*a) # Result: (-1 -2) + + self.assertEqual(a(x), IntervalVector([[5,6],[-0,1]])) + self.assertEqual(a.invert()(a(x)), IntervalVector([[-1,0],[5,6]])) + self.assertEqual(a, OctaSym([2,-1])) + self.assertEqual(a.invert(), OctaSym([-2,1])) + self.assertEqual(a*a, OctaSym([-1,-2])) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/python/codac/tests/test_definition.py b/python/codac/tests/test_definition.py index 0ac06d30..d001dbe6 100644 --- a/python/codac/tests/test_definition.py +++ b/python/codac/tests/test_definition.py @@ -8,6 +8,8 @@ # \license This program is distributed under the terms of # the GNU Lesser General Public License (LGPL). +import os +from tempfile import gettempdir import unittest from codac import * import codac as codac @@ -24,7 +26,7 @@ def test_Init(self): #self.assertTrue(cos(self.tube).codomain() == cos(Interval(10.,11.))) def test_Serialize(self): - self.tube.serialize("/tmp/x.tube") + self.tube.serialize(os.path.join(gettempdir(), "x.tube")) def test_Slice_class(self): diff --git a/python/codac/tests/test_numpy.py b/python/codac/tests/test_numpy.py new file mode 100644 index 00000000..99f2a7d0 --- /dev/null +++ b/python/codac/tests/test_numpy.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python + +# Codac tests +# --------------------------------------------------------------------------- +# \date 2023 +# \author Simon Rohou +# \copyright Copyright 2023 Codac Team +# \license This program is distributed under the terms of +# the GNU Lesser General Public License (LGPL). + +import unittest +import numpy as np +from codac import * + +class TestNumpyMatrices(unittest.TestCase): + + def test_numpytocodac(self): + + cdA=IntervalMatrix(2,6) + cdA[0][0]=Interval(1) + cdA[0][1]=Interval(3) + cdA[0][2]=Interval(5) + cdA[0][3]=Interval(7) + cdA[0][4]=Interval(9) + cdA[0][5]=Interval(11) + cdA[1][0]=Interval(2) + cdA[1][1]=Interval(4) + cdA[1][2]=Interval(6) + cdA[1][3]=Interval(8) + cdA[1][4]=Interval(10) + cdA[1][5]=Interval(12) + + npA=np.array([[1.,3.,5.,7.,9.,11.],[2.,4.,6.,8.,10.,12.]]) + self.assertEqual(IntervalMatrix(npA), cdA) + + def test_numpytocodac_withtranspose(self): + + cdA=IntervalMatrix(2,6) + cdA[0][0]=Interval(1) + cdA[0][1]=Interval(3) + cdA[0][2]=Interval(5) + cdA[0][3]=Interval(7) + cdA[0][4]=Interval(9) + cdA[0][5]=Interval(11) + cdA[1][0]=Interval(2) + cdA[1][1]=Interval(4) + cdA[1][2]=Interval(6) + cdA[1][3]=Interval(8) + cdA[1][4]=Interval(10) + cdA[1][5]=Interval(12) + + npA=np.array([[1.,2.],[3.,4.],[5.,6.],[7.,8.],[9.,10.],[11.,12.]]).T + self.assertEqual(IntervalMatrix(npA), cdA) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/python/codac/tests/tests_from_pyibex/test_image.py b/python/codac/tests/tests_from_pyibex/test_image.py index f39efb29..72757eeb 100644 --- a/python/codac/tests/tests_from_pyibex/test_image.py +++ b/python/codac/tests/tests_from_pyibex/test_image.py @@ -10,15 +10,15 @@ # the GNU Lesser General Public License (LGPL). import unittest -from codac import * -from codac.unsupported import * -import sys try: import numpy as np has_np = True -except ImportError: - print("NUMPY NOT FOUND") +except: + print("NUMPY UNAVAILABLE") has_np = False +from codac import * +from codac.unsupported import * +import sys # Interval.__str__=lambda x: "("+str(x[0])+', '+str(x[1])+")" # Interval.__repr__=lambda x: "("+x[0]+', '+x[1]+")" @@ -51,7 +51,7 @@ def test_points_upperLeft(self): IntervalVector([[-5, -4], [4, 5]])) -@unittest.skipUnless(has_np, "Numpy not found") +@unittest.skipUnless(has_np, "Numpy unavailable") class TestImage(unittest.TestCase): def test_world_to_grid(self): @@ -85,7 +85,7 @@ def test_1(self): # print(im.pixelAt(i,j), end=' ') # print('') -@unittest.skipUnless(has_np, "Numpy not found") +@unittest.skipUnless(has_np, "Numpy unavailable") class TestWorldToGrid(unittest.TestCase): def setUp(self): @@ -142,7 +142,7 @@ def test_boundingBox(self): # X0 = IntervalVector([[-7, -6], [0,1]]) # self.assertEqual(self.im.test(X0), MAYBE) -@unittest.skipUnless(has_np, "Numpy not found") +@unittest.skipUnless(has_np, "Numpy unavailable") class TestCtcRaster(unittest.TestCase): def setUp(self): diff --git a/python/codac/tests/tests_from_pyibex/test_thickImage.py b/python/codac/tests/tests_from_pyibex/test_thickImage.py index f18f582e..ec459a77 100644 --- a/python/codac/tests/tests_from_pyibex/test_thickImage.py +++ b/python/codac/tests/tests_from_pyibex/test_thickImage.py @@ -10,17 +10,16 @@ # the GNU Lesser General Public License (LGPL). import unittest -from codac import * -from codac.unsupported import * - try: import numpy as np has_np = True -except ImportError: - print("NUMPY NOT FOUND") +except: + print("NUMPY UNAVAILABLE") has_np = False +from codac import * +from codac.unsupported import * -@unittest.skipUnless(has_np, "Numpy not found") +@unittest.skipUnless(has_np, "Numpy unavailable") class TestThickImage(unittest.TestCase): def setUp(self): diff --git a/python/codac_py_codac2.cpp b/python/codac_py_codac2.cpp new file mode 100644 index 00000000..c49aea98 --- /dev/null +++ b/python/codac_py_codac2.cpp @@ -0,0 +1,32 @@ +/** + * \file + * Codac binding (codac2) + * ---------------------------------------------------------------------------- + * \date 2023 + * \author Simon Rohou + * \copyright Copyright 2023 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#include +#include +#include "src/core/contractors/static/codac_py_Ctc.h" + +#include + +using namespace codac; +namespace py = pybind11; + +void export_Action(py::module& m); +void export_CtcAction(py::module& m, py::class_& ctc); + +PYBIND11_MODULE(codac2, m) +{ + m.doc() = "Python binding of Codac (codac2)"; + + py::class_ ctc = (py::class_)py::module::import("codac").attr("Ctc"); + + export_Action(m); + export_CtcAction(m, ctc); +} \ No newline at end of file diff --git a/python/codac_py_core.cpp b/python/codac_py_core.cpp index 14667439..625014e0 100644 --- a/python/codac_py_core.cpp +++ b/python/codac_py_core.cpp @@ -32,7 +32,9 @@ void export_IntervalVar(py::module& m); void export_IntervalVectorVar(py::module& m); py::class_ export_Ctc(py::module& m); +void export_Ctc3BCid(py::module& m, py::class_& ctc); void export_CtcBox(py::module& m, py::class_& ctc); +void export_CtcCN(py::module& m, py::class_& ctc); void export_CtcCartProd(py::module& m, py::class_& ctc); void export_CtcDist(py::module& m, py::class_& ctc); void export_CtcFunction(py::module& m, py::class_& ctc); @@ -79,11 +81,14 @@ void export_VIBesFigPaving(py::module& m); void export_geometry(py::module& m, py::class_& ctc, py::class_& sep); -void export_Paving(py::module& m); void export_Set(py::module& m); +void export_Paving(py::module& m); +void export_SIVIAPaving(py::module& m); +void export_ConnectedSubset(py::module& m); void export_DataLoader(py::module& m); void export_TPlane(py::module& m); +void export_Wall(py::module& m); void export_sivia(py::module& m, py::class_& ctc, py::class_& sep); @@ -98,7 +103,9 @@ PYBIND11_MODULE(core, m) export_IntervalVectorVar(m); py::class_ ctc = export_Ctc(m); + export_Ctc3BCid(m, ctc); export_CtcBox(m, ctc); + export_CtcCN(m, ctc); export_CtcCartProd(m, ctc); export_CtcDist(m, ctc); export_CtcFunction(m, ctc); @@ -145,10 +152,13 @@ PYBIND11_MODULE(core, m) export_geometry(m, ctc, sep); export_Paving(m); + export_SIVIAPaving(m); export_Set(m); + export_ConnectedSubset(m); export_DataLoader(m); export_TPlane(m); + export_Wall(m); export_sivia(m, ctc, sep); diff --git a/python/pybind11 b/python/pybind11 index e8e229fa..1a0ff405 160000 --- a/python/pybind11 +++ b/python/pybind11 @@ -1 +1 @@ -Subproject commit e8e229fa0b486118bf91321b923f8158055c053c +Subproject commit 1a0ff405498b6aac4b57cf0d95670010e5e37973 diff --git a/python/setup.py.in b/python/setup.py.in index 21f49147..49da05ea 100644 --- a/python/setup.py.in +++ b/python/setup.py.in @@ -25,6 +25,7 @@ setup( '${PYTHON_PACKAGE_NAME}': [ 'core${PYTHON_MODULE_EXTENSION}', 'unsupported${PYTHON_MODULE_EXTENSION}', + 'codac2${PYTHON_MODULE_EXTENSION}', #'graphics${PYTHON_MODULE_EXTENSION}', ], }, diff --git a/python/src/core/2/actions/codac2_py_Action.cpp b/python/src/core/2/actions/codac2_py_Action.cpp new file mode 100644 index 00000000..73d41688 --- /dev/null +++ b/python/src/core/2/actions/codac2_py_Action.cpp @@ -0,0 +1,77 @@ +/** + * \file + * Action Python binding + * ---------------------------------------------------------------------------- + * \date 2020 + * \author Simon Rohou, Benoît Desrochers + * \copyright Copyright 2021 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#include +#include +#include +#include +#include +#include "codac_type_caster.h" + +#include "codac2_Action.h" +#include "codac2_CtcAction.h" + +using namespace std; +using namespace codac; +using namespace codac2; +namespace py = pybind11; +using namespace pybind11::literals; + + +void export_Action(py::module& m) +{ + py::class_ octasym(m, "OctaSym", "todo"); + octasym + + .def(py::init([](py::array_t s) + { + // Request a buffer descriptor from Python + py::buffer_info info = s.request(); + + // Some sanity checks... + // TEMPORARILY REMOVED (not supported on Windows): if(info.format != py::format_descriptor::format()) + // TEMPORARILY REMOVED (not supported on Windows): throw std::runtime_error("Incompatible format: expected a int array"); + + //if(info.ndim == 1 || // e.g.: a=np.array([1,0,0]) + // (info.ndim == 2 && (int)info.shape[1] == 1)) // e.g.: a=np.array([[1],[0],[0]]) + { + //ibex::Vector m(, static_cast(info.ptr)); + + int len = (int)info.shape[0]; + int* w = static_cast(info.ptr); + + vector s; + s.assign(w, w + len); + return OctaSym(s); + } + })) + + .def(py::self == py::self) + + .def("__call__", [](OctaSym& s,const codac::IntervalVector& x) { return s(x); }, + "todo", + py::return_value_policy::reference_internal) + + .def("__call__", [](OctaSym& s,codac::Ctc& ctc) { return s(ctc); }, + "todo", + py::return_value_policy::reference_internal) + + .def("invert", &OctaSym::invert, + "todo") + + .def(py::self * py::self) + + .def("__repr__", [](const OctaSym& s) { ostringstream str; str << s; return str.str(); }) + ; + + // Automatic cast from lists to OctaSym + py::implicitly_convertible(); +} \ No newline at end of file diff --git a/python/src/core/2/contractors/codac2_py_CtcAction.cpp b/python/src/core/2/contractors/codac2_py_CtcAction.cpp new file mode 100644 index 00000000..de98fba7 --- /dev/null +++ b/python/src/core/2/contractors/codac2_py_CtcAction.cpp @@ -0,0 +1,41 @@ +/** + * \file + * CtcBox Python binding + * ---------------------------------------------------------------------------- + * \date 2020 + * \author Simon Rohou, Benoît Desrochers + * \copyright Copyright 2021 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#include +#include +#include +#include +#include "codac_type_caster.h" + +#include "../../contractors/static/codac_py_Ctc.h" +#include "codac2_CtcAction.h" + +using namespace std; +using namespace codac; +using namespace codac2; +namespace py = pybind11; +using namespace pybind11::literals; + + +void export_CtcAction(py::module& m, py::class_& ctc) +{ + py::class_ ctc_action(m, "CtcAction", ctc, "todo"); + ctc_action + + .def(py::init(), + "todo", + "ctc"_a, "s"_a) + + .def("contract", &CtcAction::contract, + "todo", + "x"_a.noconvert()) + ; +} \ No newline at end of file diff --git a/python/src/core/cn/codac_py_ContractorNetwork.cpp b/python/src/core/cn/codac_py_ContractorNetwork.cpp index 6eda95f9..51495f2b 100644 --- a/python/src/core/cn/codac_py_ContractorNetwork.cpp +++ b/python/src/core/cn/codac_py_ContractorNetwork.cpp @@ -243,7 +243,7 @@ void export_ContractorNetwork(py::module& m) .def("contract", [](ContractorNetwork& cn, py::dict var_dom, bool verbose) { - cn.contract(pydict_to_unorderedmapdomains(var_dom), verbose); + return cn.contract(pydict_to_unorderedmapdomains(var_dom), verbose); }, CONTRACTORNETWORK_DOUBLE_CONTRACT_UNORDEREDMAPDOMAINDOMAIN_BOOL, "var_dom"_a, "verbose"_a=false) diff --git a/python/src/core/cn/codac_py_Variable.cpp b/python/src/core/cn/codac_py_Variable.cpp index bd576255..2b6a4402 100644 --- a/python/src/core/cn/codac_py_Variable.cpp +++ b/python/src/core/cn/codac_py_Variable.cpp @@ -63,6 +63,15 @@ void export_IntervalVectorVar(py::module& m) "todo", py::return_value_policy::reference_internal) + .def("getitem", [](IntervalVectorVar& s, size_t index) -> IntervalVar& + { + if(index >= static_cast(s.size())) + throw py::index_error(); + return s[static_cast(index)]; + }, + "todo", + py::return_value_policy::reference_internal) + .def("__eq__", [](const IntervalVectorVar& s1, const IntervalVectorVar& s2) { return reinterpret_cast(&s1) == reinterpret_cast(&s2); }) .def("__hash__", [](const IntervalVectorVar& s1) { return reinterpret_cast(&s1); }) diff --git a/python/src/core/contractors/dyn/codac_py_CtcLohner.cpp b/python/src/core/contractors/dyn/codac_py_CtcLohner.cpp index 89a5b135..889de3ed 100644 --- a/python/src/core/contractors/dyn/codac_py_CtcLohner.cpp +++ b/python/src/core/contractors/dyn/codac_py_CtcLohner.cpp @@ -28,8 +28,8 @@ using namespace pybind11::literals; void export_CtcLohner(py::module& m, py::class_& dyn_ctc) { - py::class_ ctc_picard(m, "CtcLohner", dyn_ctc, CTCLOHNER_MAIN); - ctc_picard + py::class_ ctc_lohner(m, "CtcLohner", dyn_ctc, CTCLOHNER_MAIN); + ctc_lohner .def(py::init(), CTCLOHNER_CTCLOHNER_FUNCTION_INT_DOUBLE, diff --git a/python/src/core/contractors/static/codac_py_Ctc.cpp b/python/src/core/contractors/static/codac_py_Ctc.cpp index d798bf5e..894816d8 100644 --- a/python/src/core/contractors/static/codac_py_Ctc.cpp +++ b/python/src/core/contractors/static/codac_py_Ctc.cpp @@ -49,8 +49,24 @@ py::class_ export_Ctc(py::module& m) .def("__or__", [](Ctc& c1, Ctc& c2) { - return new CtcUnion(c1, c2); + CtcUnion *cu = new CtcUnion(2); + cu->add_raw_ptr(&c1); + cu->add_raw_ptr(&c2); // todo: manage delete + return cu; + }, + DOC_CTC_OR, + py::return_value_policy::take_ownership, + py::keep_alive<0,1>(), py::keep_alive<0,2>()) + + // For MATLAB compatibility. + .def("union", [](Ctc& c1, Ctc& c2) + { + CtcUnion *cu = new CtcUnion(2); + cu->add_raw_ptr(&c1); + cu->add_raw_ptr(&c2); + // todo: manage delete + return cu; }, DOC_CTC_OR, py::return_value_policy::take_ownership, @@ -64,6 +80,16 @@ py::class_ export_Ctc(py::module& m) DOC_CTC_AND, py::return_value_policy::take_ownership, py::keep_alive<0,1>(), py::keep_alive<0,2>()) + + // For MATLAB compatibility. + .def("inter", [](Ctc& c1, Ctc& c2) + { + return new CtcCompo(c1, c2); + // todo: manage delete + }, + DOC_CTC_AND, + py::return_value_policy::take_ownership, + py::keep_alive<0,1>(), py::keep_alive<0,2>()) ; // Export comparaison constant @@ -77,9 +103,36 @@ py::class_ export_Ctc(py::module& m) ; // Export CtcUnion - py::class_(m, "CtcUnion", ctc, DOC_CTCUNION_TYPE) - .def(py::init>(), py::keep_alive<1,2>(), "list"_a) + py::class_(m, "CtcUnion", ctc, "todo") + .def(py::init(), "nb_var"_a) + .def(py::init([](Ctc& c1) + { + CtcUnion *cu = new CtcUnion(c1.nb_var); + cu->add_raw_ptr(&c1); + return cu; + }), + py::keep_alive<0,1>(), "c1"_a) + .def(py::init([](Ctc& c1, Ctc& c2) + { + CtcUnion *cu = new CtcUnion(c1.nb_var); + cu->add_raw_ptr(&c1); + cu->add_raw_ptr(&c2); + return cu; + }), + py::keep_alive<0,1>(), py::keep_alive<0,2>(), "c1"_a, "c2"_a) + .def(py::init([](Ctc& c1, Ctc& c2, Ctc& c3) + { + CtcUnion *cu = new CtcUnion(c1.nb_var); + cu->add_raw_ptr(&c1); + cu->add_raw_ptr(&c2); + cu->add_raw_ptr(&c3); + return cu; + }), + py::keep_alive<0,1>(), py::keep_alive<0,2>(), py::keep_alive<0,3>(), "c1"_a, "c2"_a, "c3"_a) .def("contract", (void (Ctc::*)(IntervalVector&)) &CtcUnion::contract) + .def("__ior__", [](CtcUnion& cu, Ctc& c) { return cu.add_raw_ptr(&c); }, py::keep_alive<1,2>(), py::return_value_policy::take_ownership) + // For MATLAB compatibility. + .def("union_self", [](CtcUnion& cu, Ctc& c) { return cu.add_raw_ptr(&c); }, py::keep_alive<1,2>(), py::return_value_policy::take_ownership) ; // Export CtcCompo diff --git a/python/src/core/contractors/static/codac_py_Ctc3BCid.cpp b/python/src/core/contractors/static/codac_py_Ctc3BCid.cpp new file mode 100644 index 00000000..376125fc --- /dev/null +++ b/python/src/core/contractors/static/codac_py_Ctc3BCid.cpp @@ -0,0 +1,45 @@ +/** + * \file + * Ctc3BCid Python binding + * ---------------------------------------------------------------------------- + * \date 2023 + * \author Simon Rohou, Benoît Desrochers + * \copyright Copyright 2023 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#include +#include +#include +#include +#include "codac_type_caster.h" + +#include "codac_py_Ctc.h" +#include "codac_Ctc.h" +#include "ibex_Ctc3BCid.h" +// Generated file from Doxygen XML (doxygen2docstring.py): +//#include "codac_py_Ctc3BCid_docs.h" + +using namespace std; +using namespace codac; +namespace py = pybind11; +using namespace pybind11::literals; + + +void export_Ctc3BCid(py::module& m, py::class_& ctc) +{ + py::class_ ctc_3bcid(m, "Ctc3BCid", ctc, "todo"); + ctc_3bcid + + .def(py::init(), + py::keep_alive<1,2>(), + "ctc"_a.noconvert(), + "s3b"_a.noconvert()=ibex::Ctc3BCid::default_s3b, + "scid"_a.noconvert()=ibex::Ctc3BCid::default_scid, + "vhandled"_a.noconvert()=-1, + "var_min_width"_a.noconvert()=ibex::Ctc3BCid::default_var_min_width) + + .def("contract", (void (Ctc::*) (IntervalVector&)) &ibex::Ctc3BCid::contract, "todo") + ; +} \ No newline at end of file diff --git a/python/src/core/contractors/static/codac_py_CtcCN.cpp b/python/src/core/contractors/static/codac_py_CtcCN.cpp index 4d688223..0168aa72 100644 --- a/python/src/core/contractors/static/codac_py_CtcCN.cpp +++ b/python/src/core/contractors/static/codac_py_CtcCN.cpp @@ -31,12 +31,12 @@ void export_CtcCN(py::module& m, py::class_& ctc) py::class_ ctc_cn(m, "CtcCN", ctc, CTCCN_MAIN); ctc_cn - .def(py::init(), - CTCCN_CTCCN_CONTRACTORNETWORK_INTERVALVECTORVAR, - "cn"_a.noconvert(),"box"_a.noconvert()) + .def(py::init(), + CTCCN_CTCCN_CONTRACTORNETWORK_INTERVALVECTORVAR, + "cn"_a.noconvert(), "box"_a.noconvert()) .def("contract", &CtcCN::contract, - CTCCN_VOID_CONTRACT_INTERVALVECTOR, - "box"_a.noconvert()) + CTCCN_VOID_CONTRACT_INTERVALVECTOR, + "x"_a.noconvert()) ; } \ No newline at end of file diff --git a/python/src/core/contractors/static/codac_py_CtcCartProd.cpp b/python/src/core/contractors/static/codac_py_CtcCartProd.cpp index 14578b6a..61296356 100644 --- a/python/src/core/contractors/static/codac_py_CtcCartProd.cpp +++ b/python/src/core/contractors/static/codac_py_CtcCartProd.cpp @@ -42,7 +42,11 @@ void export_CtcCartProd(py::module& m, py::class_& ctc) "x"_a.noconvert()) ; - m.def("cart_prod", [](ibex::Array a) + m.def("cart_prod", [](Ctc& c1, Ctc& c2) + { return cart_prod(c1, c2); }, + "c1"_a.noconvert(), "c2"_a.noconvert()); + + m.def("cart_prod", [](const ibex::Array& a) { return cart_prod(a); }, "array"_a.noconvert()); } \ No newline at end of file diff --git a/python/src/core/domains/interval/codac_py_Interval.cpp b/python/src/core/domains/interval/codac_py_Interval.cpp index c82b00f5..616e82d9 100644 --- a/python/src/core/domains/interval/codac_py_Interval.cpp +++ b/python/src/core/domains/interval/codac_py_Interval.cpp @@ -87,7 +87,7 @@ void export_Interval(py::module& m) .def(py::init(), "build a copy of an interval", "x"_a.noconvert()) .def(py::init(), "build the interval [lb,ub]", "lb"_a, "ub"_a) .def(py::init(), "build singleton [x,x]", "x"_a) - //.def(py::init(&build_from_list)) + .def(py::init(&build_from_list)) //.def(py::init([](array& bounds) {return new Interval(bounds[0], bounds[1]);})) //.def(py::init([](array& bounds) {return new Interval(bounds[0], bounds[1]);})) //.def(py::init([](pair& bounds) {return new Interval(bounds.first, bounds.second);})) @@ -111,6 +111,20 @@ void export_Interval(py::module& m) else return NAN; }, py::return_value_policy::reference_internal) + + .def("getitem", [](Interval& s, size_t index) -> double + { + if(index < 0 || index > 1) + throw py::index_error(); + + cout << "Warning: indexing on intervals is deprecated." << endl + << " Use .lb(), .ub() methods instead of []." << endl; + + if(index == 0) return s.lb(); + else if(index == 1) return s.ub(); + else return NAN; + }, + py::return_value_policy::reference_internal) // Arithmetic @@ -120,7 +134,15 @@ void export_Interval(py::module& m) .def(py::self * py::self) .def(py::self / py::self) .def(py::self & py::self) + // For MATLAB compatibility. + //.def_static("inter", [](const Interval& x, const Interval& y) { return x&y; }) + // For MATLAB compatibility. + .def("inter", [](const Interval& s, const Interval& y) { return s&y; }) .def(py::self | py::self) + // For MATLAB compatibility. + //.def_static("union", [](const Interval& x, const Interval& y) { return x|y; }) + // For MATLAB compatibility. + .def("union", [](const Interval& s, const Interval& y) { return s|y; }) .def("__iadd__", [](Interval& x, Interval& o) { return x += o; }) .def("__isub__", [](Interval& x, Interval& o) { return x -= o; }) @@ -129,7 +151,11 @@ void export_Interval(py::module& m) .def("__ifloordiv__", [](Interval& x, Interval& o) { return x /= o; }) .def(py::self &= py::self) + // For MATLAB compatibility. + .def("inter_self", [](Interval& s, const Interval& a) { return s&=a; }) .def(py::self |= py::self) + // For MATLAB compatibility. + .def("union_self", [](Interval& s, const Interval& a) { return s|=a; }) .def(py::self + double()) .def(py::self += double()) @@ -146,9 +172,25 @@ void export_Interval(py::module& m) .def(-py::self) .def("__abs__", [](const Interval& a) { return ibex::abs(a); }) + // For MATLAB compatibility. + //.def_static("abs", [](const Interval& a) { return ibex::abs(a); }) + // For MATLAB compatibility. + .def("abs", [](const Interval& s) { return ibex::abs(s); }) .def("__pow__", [](const Interval& x, int n) { return ibex::pow(x, n); }) + // For MATLAB compatibility. + //.def_static("pow", [](const Interval& x, int n) { return ibex::pow(x, n); }) + // For MATLAB compatibility. + .def("pow", [](const Interval& s, int n) { return ibex::pow(s, n); }) .def("__pow__", [](const Interval& x, double d) { return ibex::pow(x, d); }) - .def("__pow__", [](const Interval &x, const Interval &y) { return ibex::pow(x, y); }) + // For MATLAB compatibility. + //.def_static("pow", [](const Interval& x, double d) { return ibex::pow(x, d); }) + // For MATLAB compatibility. + .def("pow", [](const Interval& s, double d) { return ibex::pow(s, d); }) + .def("__pow__", [](const Interval& x, const Interval& y) { return ibex::pow(x, y); }) + // For MATLAB compatibility. + //.def_static("pow", [](const Interval& x, const Interval& y) { return ibex::pow(x, y); }) + // For MATLAB compatibility. + .def("pow", [](const Interval& s, const Interval& y) { return ibex::pow(s, y); }) .def("lb", &Interval::lb, "return the upper bound") .def("ub", &Interval::ub, "return the lower bound") @@ -198,8 +240,12 @@ void export_Interval(py::module& m) .def("bisect", &Interval::bisect, DOCS_INTERVAL_BISECT, py::arg("ratio")=0.5) .def("__get_item__", get_item, "x[0] returns the lb and x[1] returns ub") + // For MATLAB compatibility. + .def_static("get_item", [](Interval& x, size_t i) { return get_item(x, i); }) .def("copy", &interval_copy, "return a new object which is the copy of x") .def("__hash__", [](const Interval& s1) { return reinterpret_cast(&s1); }) + // For MATLAB compatibility. + .def("hash", [](const Interval& s1) { return reinterpret_cast(&s1); }) //.def( "__pow__", pow__) // Constants @@ -293,4 +339,7 @@ void export_Interval(py::module& m) // sbool (*bwd_pow_2)(const Interval&, int , Interval&) = &ibex::bwd_pow; m.attr("oo") = POS_INFINITY; + + // Automatic cast from lists to IntervalVectors (used for instance in SIVIA calls) + py::implicitly_convertible(); }; \ No newline at end of file diff --git a/python/src/core/domains/interval/codac_py_IntervalMatrix.cpp b/python/src/core/domains/interval/codac_py_IntervalMatrix.cpp index 0552b874..6892720c 100644 --- a/python/src/core/domains/interval/codac_py_IntervalMatrix.cpp +++ b/python/src/core/domains/interval/codac_py_IntervalMatrix.cpp @@ -69,6 +69,16 @@ string to_string(const IntervalMatrix& x) return ss.str(); } +// Inspired by Numpy documentation +// See: https://pybind11.readthedocs.io/en/stable/advanced/pycpp/numpy.html + + #include "codac2_IntervalMatrix.h" + /* Bind MatrixXd (or some other Eigen type) to Python */ + typedef Eigen::MatrixXd Matrix; + + //typedef Eigen::Matrix::Scalar Scalar; + constexpr bool rowMajor = !true;//Eigen::Matrix::Flags & Eigen::RowMajorBit; + void export_IntervalMatrix(py::module& m) { py::class_ interval_matrix(m, "IntervalMatrix"); @@ -79,20 +89,34 @@ void export_IntervalMatrix(py::module& m) .def(py::init()) .def(py::init(&create_from_list)) - .def(py::init([](py::buffer b) // create for instance an IntervalMatrix from a NumPy matrix + .def(py::init([](py::buffer b) { + // Inspired by Numpy documentation + // See: https://pybind11.readthedocs.io/en/stable/advanced/pycpp/numpy.html + + // Note: use raw data does not work because of strides, + // see for instance transpose operations on numpy matrices + + typedef Eigen::Stride Strides; + // Request a buffer descriptor from Python py::buffer_info info = b.request(); - // Some sanity checks... + // Some basic validation checks... if(info.format != py::format_descriptor::format()) - throw std::runtime_error("Incompatible format: expected a double array"); + throw std::runtime_error("Incompatible format: expected a double array!"); if(info.ndim != 2) - throw std::runtime_error("Incompatible buffer dimension"); + throw std::runtime_error("Incompatible buffer dimension!"); - ibex::Matrix m((int)info.shape[0], (int)info.shape[1], static_cast(info.ptr)); - return IntervalMatrix(m); + auto strides = Strides( + info.strides[rowMajor ? 0 : 1] / (py::ssize_t)sizeof(double), + info.strides[rowMajor ? 1 : 0] / (py::ssize_t)sizeof(double)); + + auto map = Eigen::Map( + static_cast(info.ptr), info.shape[0], info.shape[1], strides); + + return codac2::to_codac1(codac2::Matrix(map)); })) .def("__getitem__", [](IntervalMatrix& s, size_t index) -> IntervalVector& @@ -103,6 +127,14 @@ void export_IntervalMatrix(py::module& m) }, py::return_value_policy::reference_internal) + .def("getitem", [](IntervalMatrix& s, size_t index) -> IntervalVector& + { + if(index < 0 || index >= static_cast(s.nb_rows())) + throw py::index_error(); + return s[static_cast(index)]; + }, + py::return_value_policy::reference_internal) + .def("__setitem__", [](IntervalMatrix& s, size_t index, IntervalVector& t) { if(index < 0 || index >= static_cast(s.nb_rows())) @@ -110,6 +142,13 @@ void export_IntervalMatrix(py::module& m) s[static_cast(index)] = t; }) + .def("setitem", [](IntervalMatrix& s, size_t index, IntervalVector& t) + { + if(index < 0 || index >= static_cast(s.nb_rows())) + throw py::index_error(); + s[static_cast(index)] = t; + }) + .def("assign", &assign_IntervalMatrix) .def(py::self == py::self) .def(py::self != py::self) @@ -117,6 +156,8 @@ void export_IntervalMatrix(py::module& m) .def(py::self - py::self) .def(py::self * py::self) .def(py::self &= py::self) + // For MATLAB compatibility. + .def("inter_self", [](IntervalMatrix& s, const IntervalMatrix& a) { return s&=a; }) .def(-py::self) .def(py::self += py::self) // todo: .def(py::self += other()) diff --git a/python/src/core/domains/interval/codac_py_IntervalVector.cpp b/python/src/core/domains/interval/codac_py_IntervalVector.cpp index 01b6ca28..1d7670dc 100644 --- a/python/src/core/domains/interval/codac_py_IntervalVector.cpp +++ b/python/src/core/domains/interval/codac_py_IntervalVector.cpp @@ -39,7 +39,7 @@ IntervalVector* create_from_pylist(const vector& lst) { if(lst[i].size() != 2) { - delete tmp; + delete[] tmp; throw invalid_argument("sub list must contain only two elements"); } @@ -183,12 +183,27 @@ void export_IntervalVector(py::module& m) }, py::return_value_policy::reference_internal) + .def("getitem", [](IntervalVector& s, size_t index) -> Interval& + { + if(index < 0 || index >= static_cast(s.size())) + throw py::index_error(); + return s[static_cast(index)]; + }, + py::return_value_policy::reference_internal) + .def("__setitem__", [](IntervalVector& s, size_t index, Interval& t) { if(index < 0 || index >= static_cast(s.size())) throw py::index_error(); s[static_cast(index)] = t; }) + + .def("setitem", [](IntervalVector& s, size_t index, Interval& t) + { + if(index < 0 || index >= static_cast(s.size())) + throw py::index_error(); + s[static_cast(index)] = t; + }) .def("__iter__", [](const IntervalVector& s) @@ -228,7 +243,15 @@ void export_IntervalVector(py::module& m) .def(py::self - py::self) .def(py::self * py::self) .def(py::self & py::self) + // For MATLAB compatibility. + //.def_static("inter", [](const IntervalVector& x, const IntervalVector& y) { return x&y; }) + // For MATLAB compatibility. + .def("inter", [](const IntervalVector& s, const IntervalVector& y) { return s&y; }) .def(py::self | py::self) + // For MATLAB compatibility. + //.def_static("union", [](const IntervalVector& x, const IntervalVector& y) { return x|y; }) + // For MATLAB compatibility. + .def("union", [](const IntervalVector& s, const IntervalVector& y) { return s|y; }) .def(-py::self) .def(py::self += py::self) @@ -242,10 +265,14 @@ void export_IntervalVector(py::module& m) .def("__rmul__", [](IntervalVector& a, const Interval& x) { return x*a; }) .def(py::self &= py::self) + // For MATLAB compatibility. + .def("inter_self", [](IntervalVector& s, const IntervalVector& a) { return s&=a; }) .def(py::self |= py::self) + // For MATLAB compatibility. + .def("union_self", [](IntervalVector& s, const IntervalVector& a) { return s|=a; }) .def("__add__", [](IntervalVector& a, const Vector& x) { return a+x; }) - .def("__iadd__", [](IntervalVector& a, const Vector& x) { return a+x; }) + .def("__iadd__", [](IntervalVector& a, const Vector& x) { return a+=x; }) .def("__radd__", [](IntervalVector& a, const Vector& x) { return a+x; }) .def("__sub__", [](IntervalVector& a, const Vector& x) { return a-x; }) @@ -336,4 +363,7 @@ void export_IntervalVector(py::module& m) m.def("bwd_mul", (bool (*) (const Interval&, IntervalVector&, IntervalVector&)) &ibex::bwd_mul); m.def("max", (IntervalVector(*) (const IntervalVector&, const IntervalVector&)) &max_IntevalVector); + + // Automatic cast from lists to IntervalVectors (used for instance in SIVIA calls) + py::implicitly_convertible(); }; \ No newline at end of file diff --git a/python/src/core/domains/interval/codac_py_IntervalVector_docs.h b/python/src/core/domains/interval/codac_py_IntervalVector_docs.h index 500cc19f..385254b9 100644 --- a/python/src/core/domains/interval/codac_py_IntervalVector_docs.h +++ b/python/src/core/domains/interval/codac_py_IntervalVector_docs.h @@ -13,7 +13,7 @@ const char* DOCS_INTERVALVECTOR_TYPE = R"doc_itv(An IntervalVector is a cartesian product of Intervals The docs string is taken from ibex_IntervalVector.h source file -For more information read doc from http://www.ibex-lib.org/ +For more information read doc from https://github.com/ibex-team/ibex-lib/ An IntervalVector can be initialized with: diff --git a/python/src/core/domains/interval/codac_py_Interval_docs.h b/python/src/core/domains/interval/codac_py_Interval_docs.h index aba26665..6097bdc6 100644 --- a/python/src/core/domains/interval/codac_py_Interval_docs.h +++ b/python/src/core/domains/interval/codac_py_Interval_docs.h @@ -14,7 +14,7 @@ const char* DOCS_INTERVAL_TYPE = R"doc_itv(An Interval represents a closed sub set of R The docs string is taken from ibex_Interval.h source file -For more information read doc from http://www.ibex-lib.org/ +For more information read doc from https://github.com/ibex-team/ibex-lib/ Examples: >>> a = Interval(1,2) diff --git a/python/src/core/domains/interval/codac_py_bisectors_docs.h b/python/src/core/domains/interval/codac_py_bisectors_docs.h index b67714b9..e29827b7 100644 --- a/python/src/core/domains/interval/codac_py_bisectors_docs.h +++ b/python/src/core/domains/interval/codac_py_bisectors_docs.h @@ -25,7 +25,7 @@ R"_docs(Generic bisector \link ibex::Bsc::prec precision \endlink parameter of this class. The docs string is taken from ibex_Bsc.h source file - For more information read doc from http://www.ibex-lib.org/ + For more information read doc from https://github.com/ibex-team/ibex-lib/ )_docs"; diff --git a/python/src/core/domains/tube/codac_py_Tube.cpp b/python/src/core/domains/tube/codac_py_Tube.cpp index 9bff4710..a77f3a8d 100644 --- a/python/src/core/domains/tube/codac_py_Tube.cpp +++ b/python/src/core/domains/tube/codac_py_Tube.cpp @@ -370,21 +370,45 @@ void export_Tube(py::module& m) .def("__ior__", [](Tube& s,const Interval& o) { return s |= o; }, TUBE_CONSTTUBE_OPERATORUNIEQ_INTERVAL) + // For MATLAB compatibility. + .def("union_self", [](Tube& s,const Interval& o) { return s |= o; }, + TUBE_CONSTTUBE_OPERATORUNIEQ_INTERVAL) + .def("__ior__", [](Tube& s,const Trajectory& o) { return s |= o; }, TUBE_CONSTTUBE_OPERATORUNIEQ_TRAJECTORY) + // For MATLAB compatibility. + .def("union_self", [](Tube& s,const Trajectory& o) { return s |= o; }, + TUBE_CONSTTUBE_OPERATORUNIEQ_TRAJECTORY) + .def("__ior__", [](Tube& s,const Tube& o) { return s |= o; }, TUBE_CONSTTUBE_OPERATORUNIEQ_TUBE) + // For MATLAB compatibility. + .def("union_self", [](Tube& s,const Tube& o) { return s |= o; }, + TUBE_CONSTTUBE_OPERATORUNIEQ_TUBE) + .def("__iand__", [](Tube& s,const Interval& o) { return s &= o; }, TUBE_CONSTTUBE_OPERATORINTEQ_INTERVAL) + // For MATLAB compatibility. + .def("inter_self", [](Tube& s,const Interval& o) { return s &= o; }, + TUBE_CONSTTUBE_OPERATORINTEQ_INTERVAL) + .def("__iand__", [](Tube& s,const Trajectory& o) { return s &= o; }, TUBE_CONSTTUBE_OPERATORINTEQ_TRAJECTORY) + // For MATLAB compatibility. + .def("inter_self", [](Tube& s,const Trajectory& o) { return s &= o; }, + TUBE_CONSTTUBE_OPERATORINTEQ_TRAJECTORY) + .def("__iand__", [](Tube& s,const Tube& o) { return s &= o; }, TUBE_CONSTTUBE_OPERATORINTEQ_TUBE) + // For MATLAB compatibility. + .def("inter_self", [](Tube& s,const Tube& o) { return s &= o; }, + TUBE_CONSTTUBE_OPERATORINTEQ_TUBE) + // String .def("class_name", &Tube::class_name, @@ -493,21 +517,49 @@ void export_Tube(py::module& m) .def("__rtruediv__", [](const Tube& y, const TubeVector& x) { return x/y; }) .def("__or__", [](const Tube& x, const Tube& y) { return x|y; }) + // For MATLAB compatibility. + .def("union", [](const Tube& x, const Tube& y) { return x|y; }) .def("__or__", [](const Tube& x, double y) { return x|y; }) + // For MATLAB compatibility. + .def("union", [](const Tube& x, double y) { return x|y; }) .def("__or__", [](const Tube& x, const Interval& y) { return x|y; }) + // For MATLAB compatibility. + .def("union", [](const Tube& x, const Interval& y) { return x|y; }) .def("__or__", [](const Tube& x, const Trajectory& y) { return x|y; }) + // For MATLAB compatibility. + .def("union", [](const Tube& x, const Trajectory& y) { return x|y; }) .def("__ror__", [](const Tube& y, double x) { return x|y; }) + // For MATLAB compatibility. + .def("union", [](double x, const Tube& y) { return x|y; }) .def("__ror__", [](const Tube& y, const Interval& x) { return x|y; }) + // For MATLAB compatibility. + .def("union", [](const Interval& x, const Tube& y) { return x|y; }) .def("__ror__", [](const Tube& y, const Trajectory& x) { return x|y; }) + // For MATLAB compatibility. + .def("union", [](const Trajectory& x, const Tube& y) { return x|y; }) .def("__and__", [](const Tube& x, const Tube& y) { return x&y; }) + // For MATLAB compatibility. + .def("inter", [](const Tube& x, const Tube& y) { return x&y; }) .def("__and__", [](const Tube& x, double y) { return x&y; }) + // For MATLAB compatibility. + .def("inter", [](const Tube& x, double y) { return x&y; }) .def("__and__", [](const Tube& x, const Interval& y) { return x&y; }) + // For MATLAB compatibility. + .def("inter", [](const Tube& x, const Interval& y) { return x&y; }) .def("__and__", [](const Tube& x, const Trajectory& y) { return x&y; }) + // For MATLAB compatibility. + .def("inter", [](const Tube& x, const Trajectory& y) { return x&y; }) .def("__rand__", [](const Tube& y, double x) { return x&y; }) + // For MATLAB compatibility. + .def("inter", [](double x, const Tube& y) { return x&y; }) .def("__rand__", [](const Tube& y, const Interval& x) { return x&y; }) + // For MATLAB compatibility. + .def("inter", [](const Interval& x, const Tube& y) { return x&y; }) .def("__rand__", [](const Tube& y, const Trajectory& x) { return x&y; }) + // For MATLAB compatibility. + .def("inter", [](const Trajectory& x, const Tube& y) { return x&y; }) ; } \ No newline at end of file diff --git a/python/src/core/domains/tube/codac_py_TubeVector.cpp b/python/src/core/domains/tube/codac_py_TubeVector.cpp index b0f11fc5..06078f9a 100644 --- a/python/src/core/domains/tube/codac_py_TubeVector.cpp +++ b/python/src/core/domains/tube/codac_py_TubeVector.cpp @@ -394,21 +394,45 @@ void export_TubeVector(py::module& m) .def("__ior__", [](TubeVector& s,const IntervalVector& o) { return s |= o;}, TUBEVECTOR_CONSTTUBEVECTOR_OPERATORUNIEQ_INTERVALVECTOR) + // For MATLAB compatibility. + .def("union_self", [](TubeVector& s,const IntervalVector& o) { return s |= o;}, + TUBEVECTOR_CONSTTUBEVECTOR_OPERATORUNIEQ_INTERVALVECTOR) + .def("__ior__", [](TubeVector& s,const TrajectoryVector& o) { return s |= o;}, TUBEVECTOR_CONSTTUBEVECTOR_OPERATORUNIEQ_TRAJECTORYVECTOR) + // For MATLAB compatibility. + .def("union_self", [](TubeVector& s,const TrajectoryVector& o) { return s |= o;}, + TUBEVECTOR_CONSTTUBEVECTOR_OPERATORUNIEQ_TRAJECTORYVECTOR) + .def("__ior__", [](TubeVector& s,const TubeVector& o) { return s |= o;}, TUBEVECTOR_CONSTTUBEVECTOR_OPERATORUNIEQ_TUBEVECTOR) + // For MATLAB compatibility. + .def("union_self", [](TubeVector& s,const TubeVector& o) { return s |= o;}, + TUBEVECTOR_CONSTTUBEVECTOR_OPERATORUNIEQ_TUBEVECTOR) + .def("__iand__", [](TubeVector& s,const IntervalVector& o) { return s &= o;}, TUBEVECTOR_CONSTTUBEVECTOR_OPERATORINTEQ_INTERVALVECTOR) + // For MATLAB compatibility. + .def("inter_self", [](TubeVector& s,const IntervalVector& o) { return s &= o;}, + TUBEVECTOR_CONSTTUBEVECTOR_OPERATORINTEQ_INTERVALVECTOR) + .def("__iand__", [](TubeVector& s,const TrajectoryVector& o) { return s &= o;}, TUBEVECTOR_CONSTTUBEVECTOR_OPERATORINTEQ_TRAJECTORYVECTOR) + // For MATLAB compatibility. + .def("inter_self", [](TubeVector& s,const TrajectoryVector& o) { return s &= o;}, + TUBEVECTOR_CONSTTUBEVECTOR_OPERATORINTEQ_TRAJECTORYVECTOR) + .def("__iand__", [](TubeVector& s,const TubeVector& o) { return s &= o;}, TUBEVECTOR_CONSTTUBEVECTOR_OPERATORINTEQ_TUBEVECTOR) + // For MATLAB compatibility. + .def("inter_self", [](TubeVector& s,const TubeVector& o) { return s &= o;}, + TUBEVECTOR_CONSTTUBEVECTOR_OPERATORINTEQ_TUBEVECTOR) + // String .def("class_name", &TubeVector::class_name, @@ -479,6 +503,15 @@ void export_TubeVector(py::module& m) TUBEVECTOR_TUBE_OPERATORB_INT, py::return_value_policy::reference_internal) + .def("getitem", [](TubeVector& s, size_t index) -> Tube& + { + if(index >= static_cast(s.size())) + throw py::index_error(); + return s[static_cast(index)]; + }, + TUBEVECTOR_TUBE_OPERATORB_INT, + py::return_value_policy::reference_internal) + .def("__getitem__", [](const TubeVector& s, py::slice slice) -> TubeVector { size_t start, stop, step, slicelength; @@ -495,6 +528,22 @@ void export_TubeVector(py::module& m) }, TUBEVECTOR_CONSTTUBE_OPERATORB_INT) + .def("getitem", [](const TubeVector& s, py::slice slice) -> TubeVector + { + size_t start, stop, step, slicelength; + + if(!slice.compute(s.size(), &start, &stop, &step, &slicelength)) + throw py::error_already_set(); + + if(step != 1) + cout << "Warning slice step must be equal to 1\n"; + + // To respect the python convention, the stop index + // is not included in slice + return s.subvector(start, start+slicelength-1); + }, + TUBEVECTOR_CONSTTUBE_OPERATORB_INT) + .def("__setitem__", [](TubeVector& s, size_t index, Tube& t) { if(index >= static_cast(s.size())) @@ -503,6 +552,14 @@ void export_TubeVector(py::module& m) }, TUBEVECTOR_TUBE_OPERATORB_INT) + .def("setitem", [](TubeVector& s, size_t index, Tube& t) + { + if(index >= static_cast(s.size())) + throw py::index_error(); + s[static_cast(index)] = t; + }, + TUBEVECTOR_TUBE_OPERATORB_INT) + // Operators .def("__add__", [](const TubeVector& x) { return +x; }) @@ -535,17 +592,37 @@ void export_TubeVector(py::module& m) // todo .def("__truediv__", [](const IntervalVector& x, const Tube& y); .def("__or__", [](const TubeVector& x, const TubeVector& y) { return x|y; }) + // For MATLAB compatibility. + .def("union", [](const TubeVector& x, const TubeVector& y) { return x|y; }) .def("__or__", [](const TubeVector& x, const IntervalVector& y) { return x|y; }) + // For MATLAB compatibility. + .def("union", [](const TubeVector& x, const IntervalVector& y) { return x|y; }) .def("__or__", [](const TubeVector& x, const TrajectoryVector& y) { return x|y; }) + // For MATLAB compatibility. + .def("union", [](const TubeVector& x, const TrajectoryVector& y) { return x|y; }) .def("__ror__", [](const TubeVector& y, const IntervalVector& x) { return x|y; }) + // For MATLAB compatibility. + .def("union", [](const IntervalVector& x, const TubeVector& y) { return x|y; }) .def("__ror__", [](const TubeVector& y, const TrajectoryVector& x) { return x|y; }) + // For MATLAB compatibility. + .def("union", [](const TrajectoryVector& x, const TubeVector& y) { return x|y; }) .def("__and__", [](const TubeVector& x, const TubeVector& y) { return x&y; }) + // For MATLAB compatibility. + .def("inter", [](const TubeVector& x, const TubeVector& y) { return x&y; }) .def("__and__", [](const TubeVector& x, const IntervalVector& y) { return x&y; }) + // For MATLAB compatibility. + .def("inter", [](const TubeVector& x, const IntervalVector& y) { return x&y; }) .def("__and__", [](const TubeVector& x, const TrajectoryVector& y) { return x&y; }) + // For MATLAB compatibility. + .def("inter", [](const TubeVector& x, const TrajectoryVector& y) { return x&y; }) .def("__rand__", [](const TubeVector& y, const IntervalVector& x) { return x&y; }) + // For MATLAB compatibility. + .def("inter", [](const IntervalVector& x, const TubeVector& y) { return x&y; }) .def("__rand__", [](const TubeVector& y, const TrajectoryVector& x) { return x&y; }) + // For MATLAB compatibility. + .def("inter", [](const TrajectoryVector& x, const TubeVector& y) { return x&y; }) ; } \ No newline at end of file diff --git a/python/src/core/functions/codac_py_Function.cpp b/python/src/core/functions/codac_py_Function.cpp index a550bddf..a6da9311 100644 --- a/python/src/core/functions/codac_py_Function.cpp +++ b/python/src/core/functions/codac_py_Function.cpp @@ -61,6 +61,18 @@ void export_Function(py::module& m) .def(py::init()) .def(py::init()) .def(py::init()) + .def(py::init()) + .def(py::init()) + .def(py::init()) + .def(py::init()) + .def(py::init()) + .def(py::init()) + .def(py::init()) + .def(py::init()) + .def(py::init()) + .def(py::init()) + .def(py::init()) + .def(py::init()) .def("nb_arg", &Function::nb_arg) .def("diff", &Function::diff) diff --git a/python/src/core/functions/codac_py_TFunction.cpp b/python/src/core/functions/codac_py_TFunction.cpp index 9a78ffae..63c55e1b 100644 --- a/python/src/core/functions/codac_py_TFunction.cpp +++ b/python/src/core/functions/codac_py_TFunction.cpp @@ -150,5 +150,15 @@ void export_TFunction(py::module& m, py::class_& fnc) }, TFUNCTION_CONSTTFUNCTION_OPERATORB_INT, py::return_value_policy::reference_internal) + + .def("getitem", [](TFunction& s, size_t index) + { + if((int)index >= s.nb_var()) + throw py::index_error(); + + return s[static_cast(index)]; + }, + TFUNCTION_CONSTTFUNCTION_OPERATORB_INT, + py::return_value_policy::reference_internal) ; } diff --git a/python/src/core/geometry/codac_py_geometry.cpp b/python/src/core/geometry/codac_py_geometry.cpp index 2d2ed71f..742d9b81 100644 --- a/python/src/core/geometry/codac_py_geometry.cpp +++ b/python/src/core/geometry/codac_py_geometry.cpp @@ -17,6 +17,7 @@ #include #include "../contractors/static/codac_py_Ctc.h" #include "../separators/codac_py_Sep.h" +#include "codac_Polygon.h" // Generated file from Doxygen XML (doxygen2docstring.py): #include "codac_py_CtcSegment_docs.h" @@ -28,6 +29,34 @@ namespace py = pybind11; using namespace ibex; using namespace codac; +Polygon* create_polygon_from_pylist(const vector& lst) +{ + vector v_pts; + + if(lst.size() < 1) + throw invalid_argument("size of the input list is 0"); + + //double (*tmp)[2] = new double[lst.size()][2]; + for(size_t i = 0; i < lst.size(); i++) + { + if(lst[i].size() != 2) + { + //delete[] tmp; + throw invalid_argument("sub list must contain only two elements"); + } + + //tmp[i][0] = lst[i][0].cast(); + //tmp[i][1] = lst[i][1].cast(); + v_pts.push_back({ lst[i][0].cast(), lst[i][1].cast() }); + } + + //IntervalVector *instance = new IntervalVector(lst.size(), tmp); + //delete[] tmp; + //return instance; + return new Polygon(v_pts); + // todo: manage delete +} + SepPolygon* SepPolygonFromList(std::vector< std::array >& lst){ size_t n = lst.size(); std::vector ax(n), ay(n),bx(n),by(n); @@ -66,4 +95,10 @@ void export_geometry(py::module& m, py::class_& ctc, py::class_(m, "Polygon") + .def(py::init(&create_polygon_from_pylist), "list"_a) + .def(py::init>()) + ; + } diff --git a/python/src/core/graphics/codac_py_ColorMap.cpp b/python/src/core/graphics/codac_py_ColorMap.cpp index 7de0db22..cb55a7ed 100644 --- a/python/src/core/graphics/codac_py_ColorMap.cpp +++ b/python/src/core/graphics/codac_py_ColorMap.cpp @@ -32,5 +32,11 @@ void export_ColorMap(py::module& m) .def("is_opaque", &ColorMap::is_opaque, COLORMAP_BOOL_IS_OPAQUE) + + .def_property_readonly_static("HAXBY", [](py::object) { return ColorMap::HAXBY; }) + .def_property_readonly_static("DEFAULT", [](py::object) { return ColorMap::DEFAULT; }) + .def_property_readonly_static("BLUE_TUBE", [](py::object) { return ColorMap::BLUE_TUBE; }) + .def_property_readonly_static("RED_TUBE", [](py::object) { return ColorMap::RED_TUBE; }) + .def_property_readonly_static("RAINBOW", [](py::object) { return ColorMap::RAINBOW; }) ; } \ No newline at end of file diff --git a/python/src/core/graphics/codac_py_VIBesFig.cpp b/python/src/core/graphics/codac_py_VIBesFig.cpp index 61a69a21..4ff9ebe4 100644 --- a/python/src/core/graphics/codac_py_VIBesFig.cpp +++ b/python/src/core/graphics/codac_py_VIBesFig.cpp @@ -92,5 +92,13 @@ void export_VIBesFig(py::module& m) .def("draw_vehicle", (void (VIBesFig::*)(double,double,double,double,const string&,const vibes::Params &))&VIBesFig::draw_vehicle, VIBESFIG_VOID_DRAW_VEHICLE_DOUBLE_DOUBLE_DOUBLE_DOUBLE_STRING_VIBESPARAMS, "x"_a, "y"_a, "heading"_a, "size"_a, "color"_a="", "params"_a=vibes::Params()) + + .def("draw_line", (void (VIBesFig::*)(const std::vector&,const std::vector&,const string&,const vibes::Params &))&VIBesFig::draw_line, + "todo", + "v_x"_a, "v_y"_a, "color"_a="", "params"_a=vibes::Params()) + + .def("draw_polygon", (void (VIBesFig::*)(const Polygon&,const string&,const vibes::Params &))&VIBesFig::draw_polygon, + VIBESFIG_VOID_DRAW_POLYGON_POLYGON_STRING_VIBESPARAMS, + "p"_a, "color"_a="", "params"_a=vibes::Params()) ; } \ No newline at end of file diff --git a/python/src/core/graphics/codac_py_VIBesFigPaving.cpp b/python/src/core/graphics/codac_py_VIBesFigPaving.cpp index 5fe980ac..37d30e55 100644 --- a/python/src/core/graphics/codac_py_VIBesFigPaving.cpp +++ b/python/src/core/graphics/codac_py_VIBesFigPaving.cpp @@ -32,7 +32,8 @@ void export_VIBesFigPaving(py::module& m) .def(py::init(), VIBESFIGPAVING_CONSTPAVING_M_PAVING, - "fig_name"_a, "paving"_a) + "fig_name"_a, "paving"_a.noconvert(), + py::keep_alive<1,3>()) .def("show", (void (VIBesFigPaving::*)())&VIBesFigPaving::show, VIBESFIGPAVING_VOID_SHOW) diff --git a/python/src/core/paving/codac_py_ConnectedSubset.cpp b/python/src/core/paving/codac_py_ConnectedSubset.cpp new file mode 100644 index 00000000..a1b0d233 --- /dev/null +++ b/python/src/core/paving/codac_py_ConnectedSubset.cpp @@ -0,0 +1,45 @@ +/** + * \file + * Paving Python binding + * ---------------------------------------------------------------------------- + * \date 2021 + * \author Simon Rohou + * \copyright Copyright 2021 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#include +#include +#include +#include +#include "codac_type_caster.h" + +#include "codac_Vector.h" +#include "codac_ConnectedSubset.h" +// Generated file from Doxygen XML (doxygen2docstring.py): +#include "codac_py_ConnectedSubset_docs.h" + +using namespace std; +using namespace codac; +namespace py = pybind11; +using namespace pybind11::literals; + + +void export_ConnectedSubset(py::module& m) +{ + py::class_ connectedsubset(m, "ConnectedSubset", CONNECTEDSUBSET_MAIN); + connectedsubset + + .def("get_boxes", &ConnectedSubset::get_boxes, + CONNECTEDSUBSET_VECTORINTERVALVECTOR_GET_BOXES) + + .def("is_strictly_included_in_paving", &ConnectedSubset::is_strictly_included_in_paving, + CONNECTEDSUBSET_BOOL_IS_STRICTLY_INCLUDED_IN_PAVING) + + .def("contains", &ConnectedSubset::contains, + CONNECTEDSUBSET_BOOL_CONTAINS_VECTOR, + "p"_a.noconvert()) + + ; +} \ No newline at end of file diff --git a/python/src/core/paving/codac_py_Paving.cpp b/python/src/core/paving/codac_py_Paving.cpp index f82bf4c9..49f73642 100644 --- a/python/src/core/paving/codac_py_Paving.cpp +++ b/python/src/core/paving/codac_py_Paving.cpp @@ -14,6 +14,7 @@ #include #include +#include "codac_Set.h" #include "codac_Paving.h" // Generated file from Doxygen XML (doxygen2docstring.py): #include "codac_py_Paving_docs.h" @@ -26,5 +27,12 @@ using namespace pybind11::literals; void export_Paving(py::module& m) { - py::class_ tplane(m, "Paving", PAVING_MAIN); + py::class_ paving(m, "Paving", PAVING_MAIN); + paving + + .def("get_connected_subsets", &Paving::get_connected_subsets, + PAVING_VECTORCONNECTEDSUBSET_GET_CONNECTED_SUBSETS_BOOL_SETVALUE, + "sort_by_size"_a=false, "val"_a) + + ; } \ No newline at end of file diff --git a/python/src/core/paving/codac_py_SIVIAPaving.cpp b/python/src/core/paving/codac_py_SIVIAPaving.cpp new file mode 100644 index 00000000..cc152623 --- /dev/null +++ b/python/src/core/paving/codac_py_SIVIAPaving.cpp @@ -0,0 +1,47 @@ +/** + * \file + * Paving Python binding + * ---------------------------------------------------------------------------- + * \date 2021 + * \author Simon Rohou + * \copyright Copyright 2021 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#include +#include +#include +#include + +#include "codac_SIVIAPaving.h" +// Generated file from Doxygen XML (doxygen2docstring.py): +#include "codac_py_SIVIAPaving_docs.h" + +using namespace std; +using namespace codac; +namespace py = pybind11; +using namespace pybind11::literals; + + +void export_SIVIAPaving(py::module& m) +{ + py::class_ paving(m, "SIVIAPaving", SIVIAPAVING_MAIN); + paving + + .def(py::init(), + SIVIAPAVING_SIVIAPAVING_INTERVALVECTOR) + + .def("compute", (void (SIVIAPaving::*)(const Function&,const IntervalVector&,float))&SIVIAPaving::compute, + SIVIAPAVING_VOID_COMPUTE_FUNCTION_INTERVALVECTOR_FLOAT, + "f"_a, "y"_a, "precision"_a) + + .def("compute", (void (SIVIAPaving::*)(Ctc&,float))&SIVIAPaving::compute, + SIVIAPAVING_VOID_COMPUTE_CTC_FLOAT, + "ctc"_a, "precision"_a) + + .def("compute", (void (SIVIAPaving::*)(ibex::Sep&,float))&SIVIAPaving::compute, + SIVIAPAVING_VOID_COMPUTE_SEP_FLOAT, + "sep"_a, "precision"_a) + ; +} \ No newline at end of file diff --git a/python/src/core/paving/codac_py_Set.cpp b/python/src/core/paving/codac_py_Set.cpp index d6f99b47..96e862ef 100644 --- a/python/src/core/paving/codac_py_Set.cpp +++ b/python/src/core/paving/codac_py_Set.cpp @@ -31,5 +31,8 @@ void export_Set(py::module& m) .value("OUT", SetValue::OUT) .value("IN", SetValue::IN) .value("PENUMBRA", SetValue::PENUMBRA) + + .def("__or__", [](SetValue v1, SetValue v2) { return v1|v2; }) + .def("__and__", [](SetValue v1, SetValue v2) { return v1&v2; }) ; } \ No newline at end of file diff --git a/python/src/core/separators/codac_py_Sep.cpp b/python/src/core/separators/codac_py_Sep.cpp index bd128e53..806a6374 100644 --- a/python/src/core/separators/codac_py_Sep.cpp +++ b/python/src/core/separators/codac_py_Sep.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -124,10 +125,10 @@ py::class_ export_Sep(py::module& m) "f"_a.noconvert()) .def(py::init(), SEPFUNCTION_SEPFUNCTION_FUNCTION_INTERVAL, - "f"_a.noconvert(), "y"_a.noconvert()) + "f"_a.noconvert(), "y"_a) .def(py::init(), SEPFUNCTION_SEPFUNCTION_FUNCTION_INTERVALVECTOR, - "f"_a.noconvert(), "y"_a.noconvert()) + "f"_a.noconvert(), "y"_a) .def("separate", &SepFunction::separate, SEPFUNCTION_VOID_SEPARATE_INTERVALVECTOR_INTERVALVECTOR, "x_in"_a.noconvert(), "x_out"_a.noconvert()) @@ -165,6 +166,11 @@ py::class_ export_Sep(py::module& m) .def_property("q", py::cpp_function(&SepQInterProjF::get_q), py::cpp_function(&SepQInterProjF::set_q)) ; + // Export SepTransform + py::class_(m, "SepTransform", sep, "todo") + .def(py::init(), py::keep_alive<1,2>(), py::keep_alive<1,3>(), py::keep_alive<1,4>()) + .def("separate", &SepTransform::separate, py::call_guard()) + ; // Export SepProj py::class_(m, "SepProj", sep, __DOC_SEP_SEPPROJ) diff --git a/python/src/core/sivia/codac_py_sivia.cpp b/python/src/core/sivia/codac_py_sivia.cpp index 62ec1257..97b0b7dc 100644 --- a/python/src/core/sivia/codac_py_sivia.cpp +++ b/python/src/core/sivia/codac_py_sivia.cpp @@ -29,19 +29,19 @@ using namespace pybind11::literals; void export_sivia(py::module& m, py::class_& ctc, py::class_& sep) { - m.def("SIVIA", [](const IntervalVector& x, Ctc& ctc, float precision, bool regular, + m.def("SIVIA", [](const IntervalVector& x, Ctc& ctc, float precision, bool regular_paving, bool display_result, const string& fig_name, bool return_result, const SetColorMap& color_map) { - return SIVIA(x, ctc, precision, regular, display_result, fig_name, return_result, color_map); + return SIVIA(x, ctc, precision, regular_paving, display_result, fig_name, return_result, color_map); }, - "x"_a.noconvert(), "ctc"_a.noconvert(), "precision"_a.noconvert(), "regular"_a.noconvert() = false, + "x"_a, "ctc"_a.noconvert(), "precision"_a.noconvert(), "regular_paving"_a.noconvert() = false, "display_result"_a.noconvert() = true, "fig_name"_a.noconvert() = "", "return_result"_a.noconvert() = false, "color_map"_a.noconvert() = DEFAULT_SET_COLOR_MAP); - m.def("SIVIA", [](const IntervalVector& x, ibex::Sep& sep, float precision, bool regular, + m.def("SIVIA", [](const IntervalVector& x, ibex::Sep& sep, float precision, bool regular_paving, bool display_result, const string& fig_name, bool return_result, const SetColorMap& color_map) { - return SIVIA(x, sep, precision, regular, display_result, fig_name, return_result, color_map); + return SIVIA(x, sep, precision, regular_paving, display_result, fig_name, return_result, color_map); }, - "x"_a.noconvert(), "sep"_a.noconvert(), "precision"_a.noconvert(), "regular"_a.noconvert() = false, + "x"_a, "sep"_a.noconvert(), "precision"_a.noconvert(), "regular_paving"_a.noconvert() = false, "display_result"_a.noconvert() = true, "fig_name"_a.noconvert() = "", "return_result"_a.noconvert() = false, "color_map"_a.noconvert() = DEFAULT_SET_COLOR_MAP); } \ No newline at end of file diff --git a/python/src/core/variables/trajectory/codac_py_TrajectoryVector.cpp b/python/src/core/variables/trajectory/codac_py_TrajectoryVector.cpp index b5835d84..af9b59c5 100644 --- a/python/src/core/variables/trajectory/codac_py_TrajectoryVector.cpp +++ b/python/src/core/variables/trajectory/codac_py_TrajectoryVector.cpp @@ -273,6 +273,15 @@ void export_TrajectoryVector(py::module& m) TRAJECTORYVECTOR_TRAJECTORY_OPERATORB_INT, py::return_value_policy::reference_internal) + .def("getitem", [](TrajectoryVector& s, size_t index) -> Trajectory& + { + if(index >= static_cast(s.size())) + throw py::index_error(); + return s[static_cast(index)]; + }, + TRAJECTORYVECTOR_TRAJECTORY_OPERATORB_INT, + py::return_value_policy::reference_internal) + .def("__getitem__", [](const TrajectoryVector& s, py::slice slice) -> TrajectoryVector { size_t start, stop, step, slicelength; @@ -289,6 +298,22 @@ void export_TrajectoryVector(py::module& m) }, TRAJECTORYVECTOR_CONSTTRAJECTORYVECTOR_SUBVECTOR_INT_INT) + .def("getitem", [](const TrajectoryVector& s, py::slice slice) -> TrajectoryVector + { + size_t start, stop, step, slicelength; + + if(!slice.compute(s.size(), &start, &stop, &step, &slicelength)) + throw py::error_already_set(); + + if(step != 1) + cout << "Warning slice step must be equal to 1\n"; + + // To respect the python convention, the stop index + // is not included in slice + return s.subvector(start, start+slicelength-1); + }, + TRAJECTORYVECTOR_CONSTTRAJECTORYVECTOR_SUBVECTOR_INT_INT) + .def("__setitem__", [](TrajectoryVector& s, size_t index, Trajectory& t) { if(index >= static_cast(s.size())) @@ -297,6 +322,14 @@ void export_TrajectoryVector(py::module& m) }, TRAJECTORYVECTOR_TRAJECTORY_OPERATORB_INT) + .def("setitem", [](TrajectoryVector& s, size_t index, Trajectory& t) + { + if(index >= static_cast(s.size())) + throw py::index_error(); + s[static_cast(index)] = t; + }, + TRAJECTORYVECTOR_TRAJECTORY_OPERATORB_INT) + // Operators .def("__add__", [](const TrajectoryVector& x) { return +x; }) diff --git a/python/src/robotics/codac_py_Wall.cpp b/python/src/robotics/codac_py_Wall.cpp new file mode 100644 index 00000000..cce059b7 --- /dev/null +++ b/python/src/robotics/codac_py_Wall.cpp @@ -0,0 +1,46 @@ +/** + * \file + * Wall Python binding + * ---------------------------------------------------------------------------- + * \date 2024 + * \author Simon Rohou, Benoît Desrochers + * \copyright Copyright 2024 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#include +#include +#include +#include +#include "codac_type_caster.h" + +#include "codac_Wall.h" +// Generated file from Doxygen XML (doxygen2docstring.py): +#include "codac_py_Wall_docs.h" + +using namespace std; +using namespace codac; +namespace py = pybind11; +using namespace pybind11::literals; + + +void export_Wall(py::module& m) +{ + py::class_ wall(m, "Wall", WALL_MAIN); + wall + + .def(py::init(), "todo") + .def("contains", &Wall::contains, "todo", "p"_a) + .def(py::self & py::self) + // For MATLAB compatibility. + //.def_static("inter", [](const Wall& x, const Wall& y) { return x&y; }) + // For MATLAB compatibility. + .def("inter", [](const Wall& s, const Wall& y) { return s&y; }) + .def_readwrite("c1", &Wall::c1) + .def_readwrite("c2", &Wall::c2) + ; + + m.def("shorter_dist_to_walls", &shorter_dist_to_walls, "todo", "v_walls"_a, "p"_a, "bearing"_a); + m.def("shorter_contact_to_walls", &shorter_contact_to_walls, "todo", "v_walls"_a, "p"_a); +} \ No newline at end of file diff --git a/python/src/unsupported/image/codac_CtcRaster.h b/python/src/unsupported/image/codac_CtcRaster.h index a955cac0..fbf5baaf 100644 --- a/python/src/unsupported/image/codac_CtcRaster.h +++ b/python/src/unsupported/image/codac_CtcRaster.h @@ -27,13 +27,13 @@ using codac::IntervalVector; using codac::Interval; -class CtcRaster : public codac::Ctc, public GeoImage { +class CtcRaster : public codac::Ctc, public from_pyibex::GeoImage { // Georeferenced image with integral image inclusion test public: CtcRaster(py::array_t data, double x0, double y0, double dx, double dy, bool inner) : Ctc(2), - GeoImage(data, x0, y0, dx, dy), inner(inner) + from_pyibex::GeoImage(data, x0, y0, dx, dy), inner(inner) { // constructor } @@ -51,7 +51,7 @@ class CtcRaster : public codac::Ctc, public GeoImage { //############################################################################## inline int CtcRaster::enclosed_pixels(PixelCoords& pixel_coords){ - return GeoImage::enclosed_pixels(pixel_coords[0],pixel_coords[1],pixel_coords[2],pixel_coords[3]); + return from_pyibex::GeoImage::enclosed_pixels(pixel_coords[0],pixel_coords[1],pixel_coords[2],pixel_coords[3]); } diff --git a/python/src/unsupported/image/codac_GeoImage.h b/python/src/unsupported/image/codac_GeoImage.h index 54d31100..d3050d9c 100644 --- a/python/src/unsupported/image/codac_GeoImage.h +++ b/python/src/unsupported/image/codac_GeoImage.h @@ -24,115 +24,118 @@ using codac::Interval; #define DATA_TYPE unsigned long -class GeoImage { - - // Georeferenced image with integral image inclusion test -public: - GeoImage(py::array_t data, double x0, double y0, double dx, double dy) : - // II(data), - info_i(data.request()), - boundingBox(2), - coordMapper(x0, y0, data.request().shape[0], data.request().shape[1], dx, dy) - { - if( info_i.ndim != 2 ) - throw std::runtime_error("Number of dimensions must be one"); - boundingBox = getBoundingBox(); - } +namespace from_pyibex +{ + class GeoImage { + + // Georeferenced image with integral image inclusion test + public: + GeoImage(py::array_t data, double x0, double y0, double dx, double dy) : + // II(data), + info_i(data.request()), + boundingBox(2), + coordMapper(x0, y0, data.request().shape[0], data.request().shape[1], dx, dy) + { + if( info_i.ndim != 2 ) + throw std::runtime_error("Number of dimensions must be one"); + boundingBox = getBoundingBox(); + } - DATA_TYPE pixelAt( int x, int y ); - DATA_TYPE enclosed_pixels( int xmin,int xmax,int ymin,int ymax); - IntervalVector getBoundingBox(); + DATA_TYPE pixelAt( int x, int y ); + DATA_TYPE enclosed_pixels( int xmin,int xmax,int ymin,int ymax); + IntervalVector getBoundingBox(); - PixelCoords world_to_grid(IntervalVector box) { return coordMapper.world_to_grid(box); } - IntervalVector grid_to_world(const PixelCoords& pixel_coords) {return coordMapper.grid_to_world(pixel_coords); } + PixelCoords world_to_grid(IntervalVector box) { return coordMapper.world_to_grid(box); } + IntervalVector grid_to_world(const PixelCoords& pixel_coords) {return coordMapper.grid_to_world(pixel_coords); } - bool checkBorder(PixelCoords& c, const std::vector& limit, const bool & b); + bool checkBorder(PixelCoords& c, const std::vector& limit, const bool & b); - IntervalVector boundingBox; + IntervalVector boundingBox; -protected: - // py::array_t II; - GeoMapper coordMapper; - py::buffer_info info_i; - // DATA_TYPE * img_ptr; -}; + protected: + // py::array_t II; + GeoMapper coordMapper; + py::buffer_info info_i; + // DATA_TYPE * img_ptr; + }; -//############################################################################## -//################# INLINE IMPLEMENTATION ################################## -//############################################################################## + //############################################################################## + //################# INLINE IMPLEMENTATION ################################## + //############################################################################## -inline DATA_TYPE GeoImage::pixelAt( int x, int y){ - // std::cerr << x << " " << y << "\n"; - if (x < 0 || y < 0) - return 0; - if ( !(x < info_i.shape[0]) ){ - std::cout << "x " << x << " " << info_i.shape[0] << "\n"; - assert(x < info_i.shape[0]); + inline DATA_TYPE GeoImage::pixelAt( int x, int y){ + // std::cerr << x << " " << y << "\n"; + if (x < 0 || y < 0) + return 0; + if ( !(x < info_i.shape[0]) ){ + std::cout << "x " << x << " " << info_i.shape[0] << "\n"; + assert(x < info_i.shape[0]); + } + if ( !(y < info_i.shape[1]) ) { + std::cout << "y " << y << " " << info_i.shape[1] << "\n"; + assert(y < info_i.shape[1]); + } + DATA_TYPE* ptr = (DATA_TYPE*) info_i.ptr; + long idx = (x*info_i.strides[0] + y*info_i.strides[1] ) / info_i.itemsize; //sizeof(DATA_TYPE); + // std::cout << idx << " "; + return ((DATA_TYPE*)info_i.ptr)[idx]; } - if ( !(y < info_i.shape[1]) ) { - std::cout << "y " << y << " " << info_i.shape[1] << "\n"; - assert(y < info_i.shape[1]); + // + inline DATA_TYPE GeoImage::enclosed_pixels( int xmin,int xmax,int ymin,int ymax) { + // std::cerr << "enclosed_pixels" << ymin << " " << ymin -1 << "\n"; + DATA_TYPE b1 = pixelAt(xmax,ymax); + DATA_TYPE b2 = pixelAt(xmax,ymin-1); + DATA_TYPE b3 = pixelAt(xmin-1,ymax); + DATA_TYPE b4 = pixelAt(xmin-1,ymin-1); + return b1 - b2 - b3 + b4; } - DATA_TYPE* ptr = (DATA_TYPE*) info_i.ptr; - long idx = (x*info_i.strides[0] + y*info_i.strides[1] ) / info_i.itemsize; //sizeof(DATA_TYPE); - // std::cout << idx << " "; - return ((DATA_TYPE*)info_i.ptr)[idx]; -} -// -inline DATA_TYPE GeoImage::enclosed_pixels( int xmin,int xmax,int ymin,int ymax) { - // std::cerr << "enclosed_pixels" << ymin << " " << ymin -1 << "\n"; - DATA_TYPE b1 = pixelAt(xmax,ymax); - DATA_TYPE b2 = pixelAt(xmax,ymin-1); - DATA_TYPE b3 = pixelAt(xmin-1,ymax); - DATA_TYPE b4 = pixelAt(xmin-1,ymin-1); - return b1 - b2 - b3 + b4; -} - -inline int boxSize(int xmin, int xmax, int ymin, int ymax){ - return (xmax+1 - xmin) * (ymax+1 - ymin); -} - -inline bool GeoImage::checkBorder(PixelCoords& c, const std::vector& limit, const bool & b){ - // b initialy a ThickBoolean IN == true - // Check the border to see if it is a pixel limit - int val, bound, nPix; - for(int i = 0; i < limit.size(); i++){ - if (limit[i] == false) continue; - switch (i) { - case 0: - bound = std::max(0,c[0]-1); - val = ( b == true ) ? boxSize(bound, bound, c[2], c[3]) : 0; - nPix = enclosed_pixels(bound, bound, c[2], c[3]); - break; - case 1: - bound = std::min(info_i.shape[0]-1,c[1]+1); - val = ( b == true ) ? boxSize(bound, bound, c[2], c[3]) : 0; - nPix = enclosed_pixels(bound, bound, c[2], c[3]); - break; - case 2: - bound = std::max(0,c[2]-1); - val = ( b == true ) ? boxSize(c[0], c[1], bound, bound) : 0; - nPix = enclosed_pixels(c[0], c[1], bound, bound); - break; - case 3: - bound = std::min(info_i.shape[1]-1,c[3]+1); - val = ( b == true ) ? boxSize(c[0], c[1], bound, bound) : 0; - nPix = enclosed_pixels(c[0], c[1], bound, bound); - break; - } - if (nPix != val) return false; + inline int boxSize(int xmin, int xmax, int ymin, int ymax){ + return (xmax+1 - xmin) * (ymax+1 - ymin); } - return true; -} -inline IntervalVector GeoImage::getBoundingBox(){ - int xmax = info_i.shape[0]-1; - int ymax = info_i.shape[1]-1; - return grid_to_world({ 0, xmax, 0, ymax}); -} + inline bool GeoImage::checkBorder(PixelCoords& c, const std::vector& limit, const bool & b){ + // b initialy a ThickBoolean IN == true + // Check the border to see if it is a pixel limit + int val, bound, nPix; + for(int i = 0; i < limit.size(); i++){ + if (limit[i] == false) continue; + switch (i) { + case 0: + bound = std::max(0,c[0]-1); + val = ( b == true ) ? boxSize(bound, bound, c[2], c[3]) : 0; + nPix = enclosed_pixels(bound, bound, c[2], c[3]); + break; + case 1: + bound = std::min(info_i.shape[0]-1,c[1]+1); + val = ( b == true ) ? boxSize(bound, bound, c[2], c[3]) : 0; + nPix = enclosed_pixels(bound, bound, c[2], c[3]); + break; + case 2: + bound = std::max(0,c[2]-1); + val = ( b == true ) ? boxSize(c[0], c[1], bound, bound) : 0; + nPix = enclosed_pixels(c[0], c[1], bound, bound); + break; + case 3: + bound = std::min(info_i.shape[1]-1,c[3]+1); + val = ( b == true ) ? boxSize(c[0], c[1], bound, bound) : 0; + nPix = enclosed_pixels(c[0], c[1], bound, bound); + break; + } + + if (nPix != val) return false; + } + return true; + } + + inline IntervalVector GeoImage::getBoundingBox(){ + int xmax = info_i.shape[0]-1; + int ymax = info_i.shape[1]-1; + return grid_to_world({ 0, xmax, 0, ymax}); + } +} // end of namespace from_pyibex -#endif //__PYIBEX_GEOIMAGE_H__ +#endif //__PYIBEX_GEOIMAGE_H__ \ No newline at end of file diff --git a/python/src/unsupported/image/codac_images.cpp b/python/src/unsupported/image/codac_images.cpp index 8d772869..b1ef08e5 100644 --- a/python/src/unsupported/image/codac_images.cpp +++ b/python/src/unsupported/image/codac_images.cpp @@ -43,18 +43,18 @@ using namespace pybind11::literals; void export_GeoImage(py::module& m) { - class_ geo_image(m, "GeoImage"); + class_ geo_image(m, "GeoImage"); geo_image .def(init , double, double , double , double >(), __DOC_GEOIMAGE_CONSTRUCTOR, py::keep_alive<1,2>(), "img"_a, "x0"_a, "y0"_a, "dx"_a, "dy"_a) - .def("world_to_grid", &GeoImage::world_to_grid, __DOC_GEOIMAGE_WORLD_TO_GRID, "box"_a) - .def("grid_to_world", &GeoImage::grid_to_world, __DOC_GEOIMAGE_GRID_TO_WORLD, "pixel_coords"_a) - .def("pixelAt", &GeoImage::pixelAt, __DOC_GEOIMAGE_PIXELAT, "x"_a, "y"_a) - .def("enclosed_pixels", &GeoImage::enclosed_pixels, __DOC_GEOIMAGE_ENCLOSED_PIXELS, + .def("world_to_grid", &from_pyibex::GeoImage::world_to_grid, __DOC_GEOIMAGE_WORLD_TO_GRID, "box"_a) + .def("grid_to_world", &from_pyibex::GeoImage::grid_to_world, __DOC_GEOIMAGE_GRID_TO_WORLD, "pixel_coords"_a) + .def("pixelAt", &from_pyibex::GeoImage::pixelAt, __DOC_GEOIMAGE_PIXELAT, "x"_a, "y"_a) + .def("enclosed_pixels", &from_pyibex::GeoImage::enclosed_pixels, __DOC_GEOIMAGE_ENCLOSED_PIXELS, "xmin"_a, "xmax"_a, "ymin"_a, "ymax"_a ) // .def("test", &GeoImage::test, __DOC_GEOIMAGE_TEST, "box"_a) - .def_property_readonly("boundingBox", [](GeoImage& self){return self.boundingBox;}) + .def_property_readonly("boundingBox", [](from_pyibex::GeoImage& self){return self.boundingBox;}) ; } diff --git a/python/src/unsupported/separators/codac_py_Sep_unsupported.cpp b/python/src/unsupported/separators/codac_py_Sep_unsupported.cpp index f5342925..ef1aa250 100644 --- a/python/src/unsupported/separators/codac_py_Sep_unsupported.cpp +++ b/python/src/unsupported/separators/codac_py_Sep_unsupported.cpp @@ -26,7 +26,6 @@ using namespace pybind11::literals; #include "ibex_Sep.h" #include "codac_QInterProjF.h" -#include "codac_SepTransform.h" #include "codac_SepFixPoint.h" #include "codac_SepProj.h" #include "codac_SepCtcPairProj.h" @@ -93,12 +92,6 @@ void export_unsupported_sep(py::module& m, py::class_& sep){ // .def("separate", &SepCtcPairProj::separate, py::call_guard()) // ; - // Export SepTransform - py::class_(m, "SepTransform", sep) - .def(py::init(), py::keep_alive<1,2>(), py::keep_alive<1,3>(), py::keep_alive<1,4>()) - .def("separate", &SepTransform::separate, py::call_guard()) - ; - // // Export SepFixPoint // py::class_(m, "SepFixPoint", sep) // .def(py::init(), py::keep_alive<1,2>(), "sep"_a, "ratio"_a=0.01) diff --git a/python/src/unsupported/thickset/codac_ThickInterval.cpp b/python/src/unsupported/thickset/codac_ThickInterval.cpp index 0caf35ef..795d365e 100644 --- a/python/src/unsupported/thickset/codac_ThickInterval.cpp +++ b/python/src/unsupported/thickset/codac_ThickInterval.cpp @@ -96,10 +96,18 @@ void export_thickInterval(py::module& m) if (idx > self.size()) throw py::index_error(); return self[idx]; }) + .def("getitem", [](ThickBox& self, size_t idx){ + if (idx > self.size()) throw py::index_error(); + return self[idx]; + }) .def("__setitem__", [](ThickBox& self, size_t idx, ThickInterval& itv){ if (idx > self.size()) throw py::index_error(); self[idx]=itv; }) + .def("setitem", [](ThickBox& self, size_t idx, ThickInterval& itv){ + if (idx > self.size()) throw py::index_error(); + self[idx]=itv; + }) .def("superset", &ThickBox::superset) .def("to_string", &ThickBox::to_string) .def("__repr__", &ThickBox::to_string) diff --git a/scripts/dependencies/install_ibex.sh b/scripts/dependencies/install_ibex.sh index 186fbbfd..40c682d4 100644 --- a/scripts/dependencies/install_ibex.sh +++ b/scripts/dependencies/install_ibex.sh @@ -3,11 +3,12 @@ cd $HOME echo 'Installing IBEX in ' $HOME '...'; if [ ! -e "ibex-lib/README.md" ]; then - git clone -b actions https://github.com/lebarsfa/ibex-lib.git ; + #git clone -b master https://github.com/lebarsfa/ibex-lib.git ; + git clone -b ibex-2.8.9.20241117 https://github.com/lebarsfa/ibex-lib.git ; # To test a specific version of IBEX... cd ibex-lib ; mkdir build && cd build ; cmake -E env CXXFLAGS="-fPIC" CFLAGS="-fPIC" cmake -DCMAKE_INSTALL_PREFIX=$HOME/ibex-lib/build_install -DCMAKE_BUILD_TYPE=Debug .. ; - make ; + make -j 4 ; else echo 'Using cached directory.' ; fi diff --git a/scripts/docker/build_pybinding.sh b/scripts/docker/build_pybinding.sh old mode 100644 new mode 100755 index 9119972f..9e8b9563 --- a/scripts/docker/build_pybinding.sh +++ b/scripts/docker/build_pybinding.sh @@ -2,6 +2,11 @@ set -e -x +wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241117/ibex_$(uname -m)_manylinux2014.zip --no-check-certificate -nv +unzip -q ibex_$(uname -m)_manylinux2014.zip +rm -Rf ibex_$(uname -m)_manylinux2014.zip +sudo cp -Rf ibex/* /usr/local/ + git config --global --add safe.directory /io cd /io for PYBIN in /opt/python/cp3*/bin; do @@ -11,11 +16,11 @@ for PYBIN in /opt/python/cp3*/bin; do #fi "${PYBIN}/python" -m pip install --upgrade pip + "${PYBIN}/python" -m pip install --upgrade wheel setuptools mkdir -p build_dir && cd build_dir cmake -E env CXXFLAGS="-fPIC" CFLAGS="-fPIC" cmake -DPYTHON_EXECUTABLE=${PYBIN}/python -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=ON -DWITH_TUBE_TREE=OFF -DWITH_CAPD=OFF -DWITH_PYTHON=ON .. - make -j2 + make -j4 - make test ARGS="-V"s make pip_package echo "copy wheel and clean build_dir" for whl in *.whl; do @@ -23,7 +28,15 @@ for PYBIN in /opt/python/cp3*/bin; do done "${PYBIN}/python" -m pip install codac --no-deps --no-index -f /io/wheelhouse - (cd "$HOME"; "${PYBIN}/python" -m unittest discover codac.tests) + "${PYBIN}/python" ../examples/tuto/01_getting_started/01_getting_started.py + "${PYBIN}/python" -m pip install numpy --prefer-binary + "${PYBIN}/python" -m unittest discover codac.tests + + make test ARGS="-V --output-on-failure" + echo "start of Testing/Temporary/LastTest.log" + cat Testing/Temporary/LastTest.log + echo "end of Testing/Temporary/LastTest.log" + cd /io rm -fr build_dir diff --git a/scripts/wheels/gencodacwhl.sh b/scripts/wheels/gencodacwhl.sh new file mode 100644 index 00000000..014e0812 --- /dev/null +++ b/scripts/wheels/gencodacwhl.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +read -p "Please enter the codac package version (X.Y.Z[.D]) : " PACKAGE_VERSION + +PY_V_MAJ=3 + +CPCFG=m-macosx_10_14_x86_64 PY_V_MIN=6 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv + +CPCFG=m-macosx_10_9_x86_64 PY_V_MIN=7 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv + +for PY_V_MIN in 6 7; do + for CPCFG in "m-macosx_11_0_arm64" "m-manylinux_2_17_x86_64.manylinux2014_x86_64" "m-win32" "m-win_amd64"; do + rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv + done +done + +for PY_V_MIN in 8 9 10 11 12 13; do + for CPCFG in "-macosx_10_9_x86_64" "-macosx_11_0_arm64" "-manylinux_2_17_x86_64.manylinux2014_x86_64" "-win32" "-win_amd64"; do + rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv + done +done diff --git a/scripts/wheels/getlatestcodacwhl.sh b/scripts/wheels/getlatestcodacwhl.sh new file mode 100644 index 00000000..57d87717 --- /dev/null +++ b/scripts/wheels/getlatestcodacwhl.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +export LATEST_TAG=`curl -IkLs -o /dev/null -w %{url_effective} "https://github.com/codac-team/codac/releases/latest" | sed 's#.*tag/\(.*\).*#\1#'` +export LATEST_VER="${LATEST_TAG:1}" + +export LK_VER=`cat lk_codac_whl.ver` + +echo Latest: $LATEST_VER +echo Last known: $LK_VER + +if [[ -z "$LK_VER" || $(printf "%s\n%s" "$LATEST_VER" "$LK_VER" | sort -V | head -n 1) != "$LATEST_VER" && "$LATEST_VER" != "$LK_VER" ]]; then + echo $LATEST_VER is newer than $LK_VER + echo $LATEST_VER>lk_codac_whl.ver && cat lk_codac_whl.ver | ./gencodacwhl.sh +fi diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 518cd8b6..b1fe449f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -72,16 +72,6 @@ set(CODAC_C_FLAGS \"\") set(CODAC_CXX_FLAGS \"\") ") -if((CMAKE_BUILD_TYPE MATCHES Debug) AND (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")) -# If Codac is built in Debug, -pg (added by Ibex at the time of Codac -# compilation) might be also necessary to be able to successfully build -# programs in Release (otherwise it would be only added by Ibex if the program -# is built in Debug)... -file(APPEND ${CODAC_CMAKE_CONFIG_FILE} " -set(CMAKE_EXE_LINKER_FLAGS \"${CMAKE_EXE_LINKER_FLAGS} -pg\") -") -endif() - if(WITH_CAPD) file(APPEND ${CODAC_CMAKE_CONFIG_FILE} " diff --git a/src/core/2/3rd/codac2_eigen.h b/src/core/2/3rd/codac2_eigen.h new file mode 100644 index 00000000..0f26dbce --- /dev/null +++ b/src/core/2/3rd/codac2_eigen.h @@ -0,0 +1,48 @@ +#ifndef EIGEN_NO_DEBUG +/* Disables Eigen's assertions if defined. + * Not defined by default, unless the NDEBUG macro is defined + * (this is a standard C++ macro which disables all asserts). + * https://eigen.tuxfamily.org/dox/TopicPreprocessorDirectives.html + */ +#define EIGEN_NO_DEBUG +#endif + +#ifndef __CODAC2_EIGEN_H__ +#define __CODAC2_EIGEN_H__ + +#include +#include +#include "codac2_Interval.h" + +namespace Eigen +{ + template<> struct NumTraits + : NumTraits // permits to get the epsilon, dummy_precision, lowest, highest functions + { + typedef codac2::Interval Real; + typedef codac2::Interval NonInteger; + typedef codac2::Interval Nested; + + enum { + IsComplex = 0, + IsInteger = 0, + IsSigned = 1, + RequireInitialization = 1, + ReadCost = 1, + AddCost = 3, + MulCost = 3 + }; + }; +} + +namespace codac2 +{ + inline const Interval& conj(const Interval& x) { return x; } + inline const Interval& real(const Interval& x) { return x; } + inline Interval imag(const Interval&) { return 0.; } + inline Interval abs(const Interval& x) { return ibex::abs(x); } + inline Interval abs2(const Interval& x) { return ibex::sqr(x); } + +} // namespace codac + +#endif \ No newline at end of file diff --git a/src/core/2/actions/codac2_Action.cpp b/src/core/2/actions/codac2_Action.cpp new file mode 100644 index 00000000..33749d50 --- /dev/null +++ b/src/core/2/actions/codac2_Action.cpp @@ -0,0 +1,79 @@ +/** + * OctaSym class + * ---------------------------------------------------------------------------- + * \date 2023 + * \author Simon Rohou, Luc Jaulin + * \copyright Copyright 2021 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#include "codac2_Action.h" +#include "codac2_CtcAction.h" + +using namespace std; +using namespace codac; + +namespace codac2 +{ + Interval sign(int a) + { + return (a > 0) ? 1 : ((a < 0) ? -1 : 0); + } + + int isign(int a) + { + return (a > 0) ? 1 : ((a < 0) ? -1 : 0); + } + + OctaSym::OctaSym(const vector& s) : vector(s) + { + for(const auto& i : s) + { + assert((size_t)abs(i) > 0 && (size_t)abs(i) <= size()); + } + } + + OctaSym::OctaSym(initializer_list s) + : OctaSym(vector(s)) + { } + + IntervalVector OctaSym::operator()(const IntervalVector& x) const + { + assert((size_t)x.size() == size()); + IntervalVector xs(size()); + for(size_t i = 0 ; i < size() ; i++) + xs[i] = sign((*this)[i])*x[abs((*this)[i])-1]; + return xs; + } + + OctaSym OctaSym::invert() const + { + OctaSym inv(*this); + for(size_t i = 0 ; i < size() ; i++) + inv[abs((int)(*this)[i])-1] = (abs((int)i)+1)*((*this)[i] >= 0 ? 1. : -1.); + return inv; + } + + CtcAction OctaSym::operator()(Ctc& ctc) const + { + return CtcAction(ctc, *this); + } + + OctaSym operator*(const OctaSym& s1, const OctaSym& s2) + { + OctaSym s3(s1); + for(size_t i = 0 ; i < s3.size() ; i++) + s3[i] = isign(s2[i])*s1[abs((int)s2[i])-1]; + return s3; + } + + ostream& operator<<(ostream& str, const OctaSym& s) + { + str << "("; + for(size_t i = 0 ; i < s.size() ; i++) + str << (i != 0 ? " " : "") << s[i]; + str << ")" << flush; + return str; + } +} \ No newline at end of file diff --git a/src/core/2/actions/codac2_Action.h b/src/core/2/actions/codac2_Action.h new file mode 100644 index 00000000..eb3f57ef --- /dev/null +++ b/src/core/2/actions/codac2_Action.h @@ -0,0 +1,50 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2023 + * \author Simon Rohou, Luc Jaulin + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifndef __CODAC2_ACTION_H__ +#define __CODAC2_ACTION_H__ + +#include +#include "codac_IntervalVector.h" +#include "codac_Ctc.h" + +namespace codac2 +{ + class CtcAction; + + /** + * \class Action + */ + class Action + { + + }; + + /** + * \class OctaSym + */ + class OctaSym : public std::vector, public Action + { + public: + + OctaSym(std::initializer_list s); + OctaSym(const std::vector& s); + CtcAction operator()(codac::Ctc& ctc) const; + codac::IntervalVector operator()(const codac::IntervalVector& x) const; + OctaSym invert() const; + + friend std::ostream& operator<<(std::ostream& str, const OctaSym& s); + }; + + OctaSym operator*(const OctaSym& s1, const OctaSym& s2); +} + +#endif \ No newline at end of file diff --git a/src/core/2/contractors/codac2_CtcAction.cpp b/src/core/2/contractors/codac2_CtcAction.cpp new file mode 100644 index 00000000..c6311768 --- /dev/null +++ b/src/core/2/contractors/codac2_CtcAction.cpp @@ -0,0 +1,30 @@ +/** + * CtcAction class + * ---------------------------------------------------------------------------- + * \date 2023 + * \author Simon Rohou, Luc Jaulin + * \copyright Copyright 2021 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#include "codac2_CtcAction.h" + +using namespace std; +using namespace codac; + +namespace codac2 +{ + CtcAction::CtcAction(Ctc& ctc, const OctaSym& s) + : Ctc(2), _ctc(ctc), _s(s), __s(s.invert()) + { + + } + + void CtcAction::contract(IntervalVector& x) + { + IntervalVector _x(_s(x)); + _ctc.contract(_x); + x &= __s(_x); + } +} \ No newline at end of file diff --git a/src/core/2/contractors/codac2_CtcAction.h b/src/core/2/contractors/codac2_CtcAction.h new file mode 100644 index 00000000..f6816c33 --- /dev/null +++ b/src/core/2/contractors/codac2_CtcAction.h @@ -0,0 +1,36 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2023 + * \author Simon Rohou, Luc Jaulin + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifndef __CODAC2_CTCACTION_H__ +#define __CODAC2_CTCACTION_H__ + +#include "codac2_Action.h" + +namespace codac2 +{ + /** + * \class CtcAction + */ + class CtcAction : public codac::Ctc + { + public: + + CtcAction(Ctc& ctc, const OctaSym& s); + void contract(codac::IntervalVector& x); + + protected: + + codac::Ctc& _ctc; + const OctaSym _s, __s; + }; +} + +#endif \ No newline at end of file diff --git a/src/core/2/contractors/codac2_CtcDiffInclusion.cpp b/src/core/2/contractors/codac2_CtcDiffInclusion.cpp new file mode 100644 index 00000000..04e189c6 --- /dev/null +++ b/src/core/2/contractors/codac2_CtcDiffInclusion.cpp @@ -0,0 +1,91 @@ +/** + * CtcDiffInclusion class + * ---------------------------------------------------------------------------- + * \date 2022 + * \author + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#include "codac2_CtcDiffInclusion.h" + +using namespace std; +using namespace ibex; + +namespace codac2 +{ + CtcDiffInclusion::CtcDiffInclusion(const TFunction& f) + : _f(f) + { + + } + + const TFunction& CtcDiffInclusion::f() const + { + return _f; + } + + void CtcDiffInclusion::contract(Tube& x, const Tube& u, TimePropag t_propa) + { + // Verifying that x and u share exactly the same tdomain and slicing: + assert(x.tdomain() == u.tdomain()); + // Verifying that the provided tubes are consistent with the function + assert((size_t)_f.nb_var() == 2); + assert((size_t)_f.image_dim() == x.size()); + + for(auto& sx : x) // sx is a SliceVector of the TubeVector x + { + if(sx.is_gate()) // the slice may be on a degenerated temporal domain, i.e. a gate + continue; + + // su is a SliceVector of the TubeVector u: + const shared_ptr> su = static_pointer_cast>(sx.tslice().slices().at(&u)); + + //const double dt = sx.t0_tf().diam(); + + + // sx.set(su.codomain()); + // cout << sx << " " << su << endl; + + // ... + + if(t_propa & TimePropag::FORWARD) + { + // Computations related to forward propagation + // ... + } + + if(t_propa & TimePropag::BACKWARD) + { + // Computations related to backward propagation + // ... + } + } + } + + void CtcDiffInclusion::contract(Slice& x, const Slice& u, TimePropag t_propa) + { + // Verifying that x and u share exactly the same tdomain + assert(&x.tslice() == &u.tslice()); + // Verifying that the provided slices are consistent with the function + assert((size_t)_f.nb_var() == 2); + assert((size_t)_f.image_dim() == x.size()); + + //const double dt = x.t0_tf().diam(); + + // ... + + if(t_propa & TimePropag::FORWARD) + { + // Computations related to forward propagation + // ... + } + + if(t_propa & TimePropag::BACKWARD) + { + // Computations related to backward propagation + // ... + } + } +} \ No newline at end of file diff --git a/src/core/2/contractors/codac2_CtcDiffInclusion.h b/src/core/2/contractors/codac2_CtcDiffInclusion.h new file mode 100644 index 00000000..ca1da53e --- /dev/null +++ b/src/core/2/contractors/codac2_CtcDiffInclusion.h @@ -0,0 +1,45 @@ +/** + * \file + * CtcDiffInclusion class + * ---------------------------------------------------------------------------- + * \date 2022 + * \author + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifndef __CODAC2_CTCDIFFINCLUSION_H__ +#define __CODAC2_CTCDIFFINCLUSION_H__ + +#include "codac_TFunction.h" +#include "codac_DynCtc.h" +#include "codac2_Tube.h" +#include "codac2_Slice.h" + +namespace codac2 +{ + using codac::TFunction; + using codac::TimePropag; + + /** + * \class CtcDiffInclusion + * \brief ... + */ + class CtcDiffInclusion + { + public: + + CtcDiffInclusion(const TFunction& t); + void contract(codac2::Tube& x, const codac2::Tube& u, TimePropag t_propa = TimePropag::FORWARD | TimePropag::BACKWARD); + void contract(Slice& x, const Slice& u, TimePropag t_propa = TimePropag::FORWARD | TimePropag::BACKWARD); + const TFunction& f() const; + + protected: + + //friend class Slice; // to be removed + const TFunction _f; + }; +} + +#endif \ No newline at end of file diff --git a/src/core/2/contractors/codac2_CtcLinobs.cpp b/src/core/2/contractors/codac2_CtcLinobs.cpp new file mode 100644 index 00000000..d314696e --- /dev/null +++ b/src/core/2/contractors/codac2_CtcLinobs.cpp @@ -0,0 +1,160 @@ +/** + * CtcLinobs class + * ---------------------------------------------------------------------------- + * \date 2020 + * \author Simon Rohou + * \copyright Copyright 2021 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#include "codac2_CtcLinobs.h" +#include "codac_Domain.h" +#include "codac_polygon_arithmetic.h" +#include "codac_DomainsTypeException.h" +#include // for computing e^At + +using namespace std; +using namespace ibex; +using namespace codac; + +namespace codac2 +{ + IntervalMatrix exp_At(const Matrix& A, const Interval& t) // computes e^At + { + assert(A.nb_rows() == 2 && A.nb_cols() == 2); + IntervalMatrix A_exp(2, 2, Interval::EMPTY_SET); + vector v_t({t.lb(), t.ub()}); + + for(const auto& t : v_t) + { + Eigen::MatrixXd eigen_A(2,2); + eigen_A << A[0][0], A[0][1], + A[1][0], A[1][1]; + eigen_A = eigen_A * t; + Eigen::MatrixXd eigen_A_exp = eigen_A.exp(); + + A_exp[0][0] |= eigen_A_exp(0,0); A_exp[0][1] |= eigen_A_exp(0,1); + A_exp[1][0] |= eigen_A_exp(1,0); A_exp[1][1] |= eigen_A_exp(1,1); + } + + return A_exp; + } + + CtcLinobs::CtcLinobs(const Matrix& A, const Vector& b) + : DynCtc(), _A(A), _b(b) + { + assert(A.nb_cols() == 2 && A.nb_rows() == 2); + assert(b.size() == 2); + } + + CtcLinobs::~CtcLinobs() + { + + } + + // Static members for contractor signature (mainly used for CN Exceptions) + const string CtcLinobs::m_ctc_name = "CtcLinobs"; + vector CtcLinobs::m_str_expected_doms( + { + "TubeVector, Tube" + }); + + void CtcLinobs::contract(vector& v_domains) + { + /*if(v_domains.size() != 2 + || v_domains[0]->type() != Domain::Type::T_TUBE_VECTOR + || v_domains[1]->type() != Domain::Type::T_TUBE) + throw DomainsTypeException(m_ctc_name, v_domains, m_str_expected_doms); + + contract(v_domains[0]->tube_vector(), v_domains[1]->tube());*/ + } + + void CtcLinobs::contract(codac::TubeVector& x, const codac::Tube& u, TimePropag t_propa) + { + auto tdomain = codac2::create_tdomain(x.tdomain(), x[0].slice(0)->tdomain().diam(), true); + Tube _x(tdomain, ConvexPolygon()); + Tube _u(tdomain, Interval()); + + } + + void CtcLinobs::contract(Tube& x, const Tube& u, TimePropag t_propa, bool compute_envelopes) + { + assert(x.tdomain() == u.tdomain()); + assert(x.tdomain()->all_gates_defined()); + + if(t_propa & TimePropag::FORWARD) + for(auto it = x.begin(); it != x.end(); ++it) + { + if((*it).is_gate()) continue; + if((*it).t0_tf().is_unbounded()) continue; + const shared_ptr> su = static_pointer_cast>((*it).tslice().slices().at(&u)); + contract(*it, *su, TimePropag::FORWARD, compute_envelopes && !(t_propa & TimePropag::BACKWARD)); + // Envelopes are contracted in the bwd iteration if selected + } + + if(t_propa & TimePropag::BACKWARD) + for(auto it = x.rbegin(); it != x.rend(); ++it) + { + if((*it).is_gate()) continue; + if((*it).t0_tf().is_unbounded()) continue; + const shared_ptr> su = static_pointer_cast>((*it).tslice().slices().at(&u)); + contract(*it, *su, TimePropag::BACKWARD, compute_envelopes); + } + } + + void CtcLinobs::contract(Slice& x, const Slice& u, TimePropag t_propa, bool compute_envelope) + { + if(x.is_empty() || u.is_empty()) + { + x.set_empty(); + return; + } + + if(x.is_gate()) + return; + + auto next = x.next_slice_ptr(); + + if((t_propa & TimePropag::FORWARD) && next) + { + assert(next->is_gate()); + ConvexPolygon output_gate = next->codomain(); + ctc_fwd_gate(output_gate, x.input_gate(), x.t0_tf().diam(), u.codomain()); + next->set(output_gate); + } + + auto prev = x.prev_slice_ptr(); + + if((t_propa & TimePropag::BACKWARD) && prev) + { + assert(prev->is_gate()); + ConvexPolygon input_gate = prev->codomain(); + ctc_bwd_gate(input_gate, x.output_gate(), x.t0_tf().diam(), u.codomain()); + prev->set(input_gate); + } + + if(compute_envelope) + x.set(polygon_envelope(prev->codomain(), x.t0_tf().diam(), u.codomain())); + } + + void CtcLinobs::ctc_fwd_gate(ConvexPolygon& p_k, const ConvexPolygon& p_km1, + double dt_km1_k, const Interval& u_km1) + { + p_k &= exp_At(_A,dt_km1_k)*p_km1 + dt_km1_k*exp_At(_A,Interval(0.,dt_km1_k))*(u_km1*_b); + p_k.simplify(m_polygon_max_edges); + } + + void CtcLinobs::ctc_bwd_gate(ConvexPolygon& p_k, const ConvexPolygon& p_kp1, + double dt_k_kp1, const Interval& u_k) + { + p_k &= exp_At(-_A,dt_k_kp1)*p_kp1 - dt_k_kp1*exp_At(-_A,Interval(0.,dt_k_kp1))*(u_k*_b); + p_k.simplify(m_polygon_max_edges); + } + + ConvexPolygon CtcLinobs::polygon_envelope(const ConvexPolygon& p_k, + double dt_k_kp1, const Interval& u_k) + { + return exp_At(_A,Interval(0.,dt_k_kp1))*p_k + Interval(0.,dt_k_kp1)*exp_At(_A,Interval(0.,dt_k_kp1))*(u_k*_b); + } +} \ No newline at end of file diff --git a/src/core/2/contractors/codac2_CtcLinobs.h b/src/core/2/contractors/codac2_CtcLinobs.h new file mode 100644 index 00000000..feee4c54 --- /dev/null +++ b/src/core/2/contractors/codac2_CtcLinobs.h @@ -0,0 +1,63 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2022 + * \author Simon Rohou + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifndef __CODAC2_CTCLINOBS_H__ +#define __CODAC2_CTCLINOBS_H__ + +#include +#include +#include +#include "codac_DynCtc.h" +#include "codac2_Tube.h" +#include "codac_ConvexPolygon.h" +#include "codac_IntervalMatrix.h" + +namespace codac2 +{ + /** + * \class CtcLinobs + */ + class CtcLinobs : public codac::DynCtc + { + public: + + CtcLinobs(const codac::Matrix& A, const codac::Vector& b); // /!\ auto evaluation of e^At not reliable + ~CtcLinobs(); + + // to be removed: + void contract(codac::TubeVector& x, const codac::Tube& u, codac::TimePropag t_propa); + + void contract(std::vector& v_domains); + void contract(Tube& x, const Tube& u, codac::TimePropag t_propa = codac::TimePropag::FORWARD | codac::TimePropag::BACKWARD, bool compute_envelopes = true); + void contract(Slice& x, const Slice& u, codac::TimePropag t_propa = codac::TimePropag::FORWARD | codac::TimePropag::BACKWARD, bool compute_envelope = true); + + + protected: + + void ctc_fwd_gate(codac::ConvexPolygon& p_k, const codac::ConvexPolygon& p_km1, double dt_km1_k, const codac::Interval& u_km1); + void ctc_bwd_gate(codac::ConvexPolygon& p_k, const codac::ConvexPolygon& p_kp1, double dt_k_kp1, const codac::Interval& u_k); + codac::ConvexPolygon polygon_envelope(const codac::ConvexPolygon& p_k, double dt_k_kp1, const codac::Interval& u_k); + + + protected: + + const codac::Matrix _A; + const codac::Vector _b; + + const int m_polygon_max_edges = 15; + + static const std::string m_ctc_name; //!< class name (mainly used for CN Exceptions) + static std::vector m_str_expected_doms; //!< allowed domains signatures (mainly used for CN Exceptions) + friend class ContractorNetwork; + }; +} + +#endif \ No newline at end of file diff --git a/src/core/2/domains/interval/codac2_Interval.h b/src/core/2/domains/interval/codac2_Interval.h new file mode 100644 index 00000000..5e436057 --- /dev/null +++ b/src/core/2/domains/interval/codac2_Interval.h @@ -0,0 +1,25 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2023 + * \author Simon Rohou + * \copyright Copyright 2023 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifndef __CODAC2_INTERVAL_H__ +#define __CODAC2_INTERVAL_H__ + +#include + +namespace codac2 +{ + const double oo = POS_INFINITY; + + using codac::Interval; + +} // namespace codac + +#endif \ No newline at end of file diff --git a/src/core/2/domains/interval/codac2_IntervalMatrix.h b/src/core/2/domains/interval/codac2_IntervalMatrix.h new file mode 100644 index 00000000..865fc1ef --- /dev/null +++ b/src/core/2/domains/interval/codac2_IntervalMatrix.h @@ -0,0 +1,624 @@ +/** + * \file + * + * This class reuses many of the functions developed for ibex::IntervalMatrix. + * The original IBEX code is revised in modern C++ and adapted to the template + * structure proposed in Codac, based on the Eigen library. + * See ibex::IntervalMatrix (IBEX lib, author: G. Chabert) + * + * ---------------------------------------------------------------------------- + * \date 2023 + * \author Simon Rohou + * \copyright Copyright 2023 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifndef __CODAC2_INTERVALMATRIX_H__ +#define __CODAC2_INTERVALMATRIX_H__ + +#include +#include +#include +#include +#include + +namespace codac2 +{ + using Eigen::Dynamic; + + template + class IntervalMatrix_ : public Eigen::Matrix + { + public: + + IntervalMatrix_() + : Eigen::Matrix() + { + + } + + explicit IntervalMatrix_(size_t nb_rows, size_t nb_cols) + : Eigen::Matrix(nb_rows, nb_cols) + { + assert(R == Dynamic || R == (int)nb_rows); + assert(C == Dynamic || C == (int)nb_cols); + } + + explicit IntervalMatrix_(size_t nb_rows, size_t nb_cols, const Interval& x) + : IntervalMatrix_(nb_rows, nb_cols) + { + init(x); + } + + explicit IntervalMatrix_(size_t nb_rows, size_t nb_cols, const double bounds[][2]) + : IntervalMatrix_(nb_rows, nb_cols) + { + size_t k = 0; + for(size_t i = 0 ; i < nb_rows ; i++) + for(size_t j = 0 ; j < nb_cols ; j++) + { + if(bounds == 0) // in case the user called IntervalMatrix_(r,c,0) and 0 is interpreted as NULL! + (*this)(i,j) = Interval::zero(); + else + (*this)(i,j) = Interval(bounds[k][0],bounds[k][1]); + k++; + } + assert(k == this->size()); + } + + explicit IntervalMatrix_(const double bounds[][2]) + : IntervalMatrix_(R, C, bounds) + { } + + explicit IntervalMatrix_(const Matrix_& lb, const Matrix_& ub) + : IntervalMatrix_(lb.rows(), lb.cols()) + { + assert(lb.rows() == ub.rows() && lb.cols() == ub.cols()); + for(size_t i = 0 ; i < this->rows() ; i++) + for(size_t j = 0 ; j < this->cols() ; j++) + (*this)(i,j) = Interval(lb(i,j),ub(i,j)); + } + + explicit IntervalMatrix_(const IntervalMatrix_& x) + : IntervalMatrix_(x.rows(), x.cols()) + { + for(size_t i = 0 ; i < size() ; i++) + *(this->data()+i) = *(x.data()+i); + } + + IntervalMatrix_(std::initializer_list> l) + : IntervalMatrix_() + { + assert((R == Dynamic || (int)l.size() == R) && "ill-formed matrix"); + int cols = -1; + for(const auto& ri : l) { + assert((cols == -1 || cols == (int)ri.size()) && "ill-formed matrix"); + cols = (int)ri.size(); + } + this->resize(l.size(),cols); + size_t i = 0; + for(const auto& ri : l) + { + size_t j = 0; + for(const auto& ci : ri) + (*this)(i,j++) = ci; + i++; + } + } + + // This constructor allows you to construct IntervalMatrix_ from Eigen expressions + template + IntervalMatrix_(const Eigen::MatrixBase& other) + : Eigen::Matrix(other.template cast()) + { } + + // This method allows you to assign Eigen expressions to IntervalMatrix_ + template + IntervalMatrix_& operator=(const Eigen::MatrixBase& other) + { + this->Eigen::Matrix::operator=(other); + return *this; + } + + constexpr size_t size() const + { + return this->Eigen::Matrix::size(); + } + + void resize(size_t nb_rows, size_t nb_cols) + { + // With resize of Eigen, the data is reallocated and all previous values are lost. + auto copy = *this; + this->Eigen::Matrix::resize(nb_rows, nb_cols); + for(size_t i = 0 ; i < std::min((size_t)copy.rows(),nb_rows) ; i++) + for(size_t j = 0 ; j < std::min((size_t)copy.cols(),nb_cols) ; j++) + (*this)(i,j) = copy(i,j); + } + + bool is_empty() const + { + for(size_t i = 0 ; i < size() ; i++) + if((this->data()+i)->is_empty()) + return true; + return false; + } + + static IntervalMatrix_ empty_set(size_t nb_rows = R, size_t nb_cols = C) + { + return IntervalMatrix_(nb_rows, nb_cols, Interval::empty_set()); + } + + static IntervalMatrix_ eye() + { + return Eigen::Matrix::Identity(); + } + + bool is_flat() const + { + if(is_empty()) return true; + for(size_t i = 0 ; i < size() ; i++) + if((this->data()+i)->is_degenerated()) // don't use diam() because of roundoff + return true; + return false; + } + + bool is_unbounded() const + { + if(is_empty()) return false; + for(size_t i = 0 ; i < size() ; i++) + if((this->data()+i)->is_unbounded()) + return true; + return false; + } + + bool is_subset(const IntervalMatrix_& x) const + { + if(is_empty()) return true; + if(x.is_empty()) return false; + for(size_t i = 0 ; i < size() ; i++) + if(!(this->data()+i)->is_subset(*(x.data()+i))) + return false; + return true; + } + + bool is_strict_subset(const IntervalMatrix_& x) const + { + if(x.is_empty()) return false; + if(is_empty()) return true; + bool one_dim_strict_subset = false; + for(size_t i = 0 ; i < size() ; i++) + { + if((this->data()+i)->is_strict_subset(*(x.data()+i))) + one_dim_strict_subset = true; + if(!(this->data()+i)->is_subset(*(x.data()+i))) + return false; + } + return one_dim_strict_subset; + } + + bool is_superset(const IntervalMatrix_& x) const + { + return x.is_subset(*this); + } + + bool is_strict_superset(const IntervalMatrix_& x) const + { + return x.is_strict_subset(*this); + } + + bool contains(const Matrix_& x) const + { + if(is_empty()) + return false; + for(size_t i = 0 ; i < size() ; i++) + if(!(this->data()+i)->contains(*(x.data()+i))) + return false; + return true; + } + + bool interior_contains(const IntervalMatrix_& x) const + { + if(is_empty()) + return false; + for(size_t i = 0 ; i < size() ; i++) + if(!(this->data()+i)->interior_contains(*(x.data()+i))) + return false; + return true; + } + + bool intersects(const IntervalMatrix_& x) const + { + if(is_empty() || x.is_empty()) + return false; + for(size_t i = 0 ; i < size() ; i++) + if(!(this->data()+i)->intersects(*(x.data()+i))) + return false; + return true; + } + + bool overlaps(const IntervalMatrix_& x) const + { + if(is_empty() || x.is_empty()) + return false; + for(size_t i = 0 ; i < size() ; i++) + if(!(this->data()+i)->overlaps(*(x.data()+i))) + return false; + return true; + } + + bool is_disjoint(const IntervalMatrix_& x) const + { + if(is_empty() || x.is_empty()) + return true; + for(size_t i = 0 ; i < size() ; i++) + if((this->data()+i)->is_disjoint(*(x.data()+i))) + return true; + return false; + } + + bool is_bisectable() const + { + for(size_t i = 0 ; i < size() ; i++) + if((this->data()+i)->is_bisectable()) + return true; + return false; + } + + double min_diam() const + { + return (this->data()+extr_diam_index(true))->diam(); + } + + double max_diam() const + { + return (this->data()+extr_diam_index(false))->diam(); + } + + size_t thinnest_diam_index() const + { + return extr_diam_index(true); + } + + size_t largest_diam_index() const + { + return extr_diam_index(false); + } + + size_t extr_diam_index(bool min) const + { + // This code originates from the ibex-lib + // See: ibex_TemplateVector.h + // Author: Gilles Chabert + + double d = min ? POS_INFINITY : -1; // -1 to be sure that even a 0-diameter interval can be selected + int selected_index = -1; + bool unbounded = false; + assert(!this->is_empty() && "Diameter of an empty IntervalVector is undefined"); + + size_t i; + + for(i = 0 ; i < this->size() ; i++) + { + if((this->data()+i)->is_unbounded()) + { + unbounded = true; + if(!min) break; + } + else + { + double w = (this->data()+i)->diam(); + if(min ? wd) + { + selected_index = i; + d = w; + } + } + } + + if(min && selected_index == -1) + { + assert(unbounded); + // the selected interval is the first one. + i = 0; + } + + // The unbounded intervals are not considered if we look for the minimal diameter + // and some bounded intervals have been found (selected_index!=-1) + if(unbounded && (!min || selected_index == -1)) + { + double pt = min ? NEG_INFINITY : POS_INFINITY; // keep the point less/most distant from +oo (we normalize if the lower bound is -oo) + selected_index = i; + for(; i < this->size() ; i++) + { + if((this->data()+i)->lb() == NEG_INFINITY) + { + if((this->data()+i)->ub() == POS_INFINITY) + { + if(!min) + { + selected_index = i; + break; + } + } + if((min && (-(this->data()+i)->ub() > pt)) || (!min && (-(this->data()+i)->ub() < pt))) + { + selected_index = i; + pt = -(this->data()+i)->ub(); + } + } + else if((this->data()+i)->ub() == POS_INFINITY) + { + if((min && ((this->data()+i)->lb() > pt)) || (!min && ((this->data()+i)->lb() < pt))) + { + selected_index = i; + pt = (this->data()+i)->lb(); + } + } + } + } + + return selected_index; + } + + auto bisect(float ratio = 0.49) const + { + size_t i = largest_diam_index(); + assert((this->data()+i)->is_bisectable()); + assert(Interval(0,1).interior_contains(ratio)); + + auto p = std::make_pair(*this,*this); + auto pi = (this->data()+i)->bisect(ratio); + *(p.first.data()+i) = pi.first; + *(p.second.data()+i) = pi.second; + return p; + } + + double volume() const + { + if(this->is_empty()) + return 0.; + double v = 0.; + for(size_t i = 0 ; i < this->size() ; i++) + { + if((this->data()+i)->is_unbounded()) return POS_INFINITY; + if((this->data()+i)->is_degenerated()) return 0.; + v += std::log((this->data()+i)->diam()); + } + return std::exp(v); + } + + Matrix_ lb() const + { + assert(!this->is_empty()); // todo: use nan instead of assert? + Matrix_ lb(this->rows(), this->cols()); + for(size_t i = 0 ; i < this->size() ; i++) + *(lb.data()+i) = (this->data()+i)->lb(); + return lb; + } + + Matrix_ ub() const + { + assert(!this->is_empty()); // todo: use nan instead of assert? + Matrix_ ub(this->rows(), this->cols()); + for(size_t i = 0 ; i < this->size() ; i++) + *(ub.data()+i) = (this->data()+i)->ub(); + return ub; + } + + Matrix_ mid() const + { + assert(!this->is_empty()); // todo: use nan instead of assert? + Matrix_ mid(this->rows(), this->cols()); + for(size_t i = 0 ; i < this->size() ; i++) + *(mid.data()+i) = (this->data()+i)->mid(); + return mid; + } + + Matrix_ rad() const + { + assert(!this->is_empty()); // todo: use nan instead of assert? + Matrix_ rad(this->rows(), this->cols()); + for(size_t i = 0 ; i < this->size() ; i++) + *(rad.data()+i) = (this->data()+i)->rad(); + return rad; + } + + Matrix_ diam() const + { + assert(!this->is_empty()); // todo: use nan instead of assert? + Matrix_ diam(this->rows(), this->cols()); + for(size_t i = 0 ; i < this->size() ; i++) + *(diam.data()+i) = (this->data()+i)->diam(); + return diam; + } + + void init(const Interval& x) + { + for(size_t i = 0 ; i < this->size() ; i++) + *(this->data()+i) = x; + } + + void set_empty() + { + init(Interval::empty_set()); + } + + auto& inflate(double r) + { + assert(r >= 0.); + for(size_t i = 0 ; i < this->size() ; i++) + (this->data()+i)->inflate(r); + return *this; + } + + auto& inflate(const Matrix_& r) + { + assert(r.minCoeff() >= 0.); + for(size_t i = 0 ; i < this->size() ; i++) + (this->data()+i)->inflate(*(r.data()+i)); + return *this; + } + + bool operator==(const IntervalMatrix_& x) const + { + if(x.size() != this->size() || x.rows() != this->rows() || x.cols() != this->cols()) + return false; + if(is_empty() || x.is_empty()) + return is_empty() && x.is_empty(); + for(size_t i = 0 ; i < this->size() ; i++) + if(*(this->data()+i) != *(x.data()+i)) + return false; + return true; + } + + bool operator!=(const IntervalMatrix_& x) const + { + return !(*this == x); + } + + auto& operator&=(const IntervalMatrix_& x) + { + if(!this->is_empty()) + { + if(x.is_empty()) + this->set_empty(); + else + for(size_t i = 0 ; i < this->size() ; i++) + *(this->data()+i) &= *(x.data()+i); + } + return *this; + } + + auto& operator|=(const IntervalMatrix_& x) + { + if(!x.is_empty()) + { + if(this->is_empty()) + *this = x; + else + for(size_t i = 0 ; i < this->size() ; i++) + *(this->data()+i) |= *(x.data()+i); + } + return *this; + } + + auto operator+(const IntervalMatrix_& x) const + { + auto y = *this; + return y += x; + } + + auto operator-(const IntervalMatrix_& x) const + { + auto y = *this; + return y -= x; + } + + auto operator&(const IntervalMatrix_& x) const + { + auto y = *this; + return y &= x; + } + + auto operator|(const IntervalMatrix_& x) const + { + auto y = *this; + return y |= x; + } + + auto& operator+=(const IntervalMatrix_& x) + { + (*this).noalias() += x;//.template cast(); + return *this; + } + + auto& operator-=(const IntervalMatrix_& x) + { + (*this).noalias() -= x;//.template cast(); + return *this; + } + + auto& operator+=(const Matrix_& x) + { + (*this).noalias() += x.template cast(); + return *this; + } + + auto& operator-=(const Matrix_& x) + { + (*this).noalias() -= x;//.template cast(); + return *this; + } + }; + + template + std::ostream& operator<<(std::ostream& os, const IntervalMatrix_& x) + { + if(x.is_empty()) return os << "empty matrix"; + os << "("; + for(int i = 0 ; i < x.rows() ; i++) + { + os << "("; + for(int j = 0 ; j < x.cols() ; j++) + os << x(i,j) << (j + auto operator-(const IntervalMatrix_& x) + { + auto y = x; + y.init(0.); + return y -= x; + } + + template + auto operator*(double a, const IntervalMatrix_& x) + { + return Interval(a)*x; + } + + class IntervalMatrix : public IntervalMatrix_<> + { + public: + + explicit IntervalMatrix(size_t nb_rows, size_t nb_cols) + : IntervalMatrix_<>(nb_rows, nb_cols) + { } + + + explicit IntervalMatrix(size_t nb_rows, size_t nb_cols, const Interval& x) + : IntervalMatrix_<>(nb_rows, nb_cols, x) + { } + + explicit IntervalMatrix(size_t nb_rows, size_t nb_cols, const double bounds[][2]) + : IntervalMatrix_<>(nb_rows, nb_cols, bounds) + { } + + IntervalMatrix(const IntervalMatrix_<>& x) + : IntervalMatrix_<>(x) + { } + + IntervalMatrix(std::initializer_list> l) + : IntervalMatrix_<>(l) + { } + + template + explicit IntervalMatrix(const Matrix_& v) + : IntervalMatrix_<>(v) + { } + + static IntervalMatrix empty_set(size_t nb_rows, size_t nb_cols) + { + return IntervalMatrix_<>::empty_set(nb_rows,nb_cols); + } + }; + +} // namespace codac + +#endif \ No newline at end of file diff --git a/src/core/2/domains/interval/codac2_IntervalVector.h b/src/core/2/domains/interval/codac2_IntervalVector.h new file mode 100644 index 00000000..61398726 --- /dev/null +++ b/src/core/2/domains/interval/codac2_IntervalVector.h @@ -0,0 +1,316 @@ +/** + * \file + * + * This class reuses many of the functions developed for ibex::IntervalVector. + * The original IBEX code is revised in modern C++ and adapted to the template + * structure proposed in Codac, based on the Eigen library. + * See ibex::IntervalVector (IBEX lib, author: G. Chabert) + * + * ---------------------------------------------------------------------------- + * \date 2023 + * \author Simon Rohou + * \copyright Copyright 2023 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifndef __CODAC2_INTERVALVECTOR_H__ +#define __CODAC2_INTERVALVECTOR_H__ + +#include +#include +#include +#include +#include +#include "codac2_Interval.h" +#include "codac2_IntervalMatrix.h" +#include "codac2_Vector.h" +#include + +namespace codac2 +{ + template + class Vector_; + + using Eigen::Dynamic; + + template + class IntervalVector_ : public IntervalMatrix_ + { + public: + + IntervalVector_() + : IntervalMatrix_() + { } + + explicit IntervalVector_(size_t n) + : IntervalMatrix_(n,1) + { + assert(N == Dynamic || N == (int)n); + } + + explicit IntervalVector_(size_t n, const Interval& x) + : IntervalMatrix_(n,1,x) + { + assert(N == Dynamic || N == (int)n); + } + + explicit IntervalVector_(const Interval& x) + : IntervalMatrix_(N,1,x) + { } + + template + explicit IntervalVector_(const Matrix_& v) + : IntervalMatrix_(v.size(),1) + { + static_assert(N == M || N == -1 || M == -1); + for(size_t i = 0 ; i < IntervalMatrix_::size() ; i++) + (*this)[i] = Interval(v[i]); + } + + explicit IntervalVector_(size_t n, const double bounds[][2]) + : IntervalMatrix_(n,1,bounds) + { } + + explicit IntervalVector_(const double bounds[][2]) + : IntervalVector_(this->size(), bounds) + { } + + explicit IntervalVector_(const Vector_& lb, const Vector_& ub) + : IntervalMatrix_(lb, ub) + { } + + IntervalVector_(std::initializer_list l) + : IntervalMatrix_(l.size(),1) + { + assert(N == Dynamic || (int)l.size() == N); + size_t i = 0; + for(const auto& li : l) + (*this)[i++] = li; + // todo: use thias as faster? std::copy(l.begin(), l.end(), vec); + } + + template + explicit IntervalVector_(const IntervalMatrix_& x) + : IntervalMatrix_(x) + { + assert(M == Dynamic || M == N); + } + + // This constructor allows you to construct IntervalVector_ from Eigen expressions + template + IntervalVector_(const Eigen::MatrixBase& other) + : IntervalMatrix_(other) + { } + + // This method allows you to assign Eigen expressions to IntervalVector_ + template + IntervalVector_& operator=(const Eigen::MatrixBase& other) + { + this->IntervalMatrix_::operator=(other); + return *this; + } + + void resize(size_t n) + { + this->IntervalMatrix_::resize(n,1); + } + + template + IntervalVector_ subvector() const + { + assert(N1 >= 0 && N1 < N && N2 >= 0 && N2 < N && N1 <= N2); + return this->template block(N1,0); + } + + IntervalVector_<> subvector(size_t start_index, size_t end_index) const + { + assert(end_index >= 0 && start_index >= 0); + assert(end_index < this->size() && start_index <= end_index); + + IntervalVector_<> s(end_index-start_index+1); + for(size_t i = 0 ; i < s.size() ; i++) + s[i] = (*this)[i+start_index]; + return s; + } + + template + void put(const IntervalVector_& x) + { + assert(I >= 0 && I < N && M+I <= N); + this->template block(I,0) << x; + } + + auto& operator+=(const IntervalVector_& x) + { + (*this).noalias() += x;//.template cast(); + return *this; + } + + auto& operator-=(const IntervalVector_& x) + { + (*this).noalias() -= x;//.template cast(); + return *this; + } + + std::list> complementary() const + { + return IntervalVector_(this->size()).diff(*this); + } + + std::list> diff(const IntervalVector_& y, bool compactness = true) const + { + // This code originates from the ibex-lib + // See: ibex_TemplateVector.h + // Author: Gilles Chabert + // It has been revised with modern C++ and templated types + + const size_t n = this->size(); + assert(y.size() == n); + + if(y == *this) + return { IntervalVector_::empty_set(n) }; + + IntervalVector_ x = *this; + IntervalVector_ z = x & y; + + if(z.is_empty()) + return { x }; + + else + { + // Check if in one dimension y is flat and x not, + // in which case the diff returns also x directly + if(compactness) + for(size_t i = 0 ; i < n ; i++) + if(z[i].is_degenerated() && !x[i].is_degenerated()) + return { x }; + } + + std::list> l; + + for(size_t var = 0 ; var < n ; var++) + { + Interval c1, c2; + x[var].diff(y[var], c1, c2, compactness); + + if(!c1.is_empty()) + { + IntervalVector_ v(n); + for(size_t i = 0 ; i < var ; i++) + v[i] = x[i]; + v[var] = c1; + for(size_t i = var+1 ; i < n ; i++) + v[i] = x[i]; + l.push_back(v); + + if(!c2.is_empty()) + { + IntervalVector_ w(n); + for(size_t i = 0 ; i < var ; i++) + w[i] = x[i]; + w[var] = c2; + for(size_t i = var+1 ; i + std::ostream& operator<<(std::ostream& os, const IntervalVector_& x) + { + if(x.is_empty()) return os << "empty vector"; + os << "("; + for(size_t i = 0 ; i < x.size() ; i++) + os << x[i] << (i + codac::IntervalVector to_codac1(const IntervalVector_& x) + { + ibex::IntervalVector x_(x.size()); + for(size_t i = 0 ; i < x.size() ; i++) + x_[i] = x[i]; + return x_; + } + + template + IntervalVector_ to_codac2(const codac::IntervalVector& x) + { + assert(x.size() == N); + IntervalVector_ x_(x.size()); + for(size_t i = 0 ; i < x.size() ; i++) + x_[i] = x[i]; + return x_; + } + + class IntervalVector : public IntervalVector_<> + { + public: + + explicit IntervalVector(size_t n) + : IntervalVector_<>(n) + { } + + explicit IntervalVector(size_t n, const Interval& x) + : IntervalVector_<>(n, x) + { } + + explicit IntervalVector(const Interval& x) + : IntervalVector_<>({x}) + { } + + explicit IntervalVector(const IntervalVector_<>& x) + : IntervalVector_<>(x) + { } + + template + explicit IntervalVector(const Vector_& v) + : IntervalVector_<>(v) + { } + + explicit IntervalVector(size_t n, const double bounds[][2]) + : IntervalVector_<>(n, bounds) + { } + + IntervalVector(std::initializer_list l) + : IntervalVector_<>(l) + { } + + // This constructor allows you to construct IntervalVector from Eigen expressions + template + IntervalVector(const Eigen::MatrixBase& other) + : IntervalVector_<>(other) + { } + + // This method allows you to assign Eigen expressions to IntervalVector + template + IntervalVector& operator=(const Eigen::MatrixBase& other) + { + this->IntervalVector_<>::operator=(other); + return *this; + } + + void resize(size_t n) + { + this->IntervalVector_<>::resize(n); + } + + static IntervalVector empty_set(size_t n) + { + return IntervalMatrix_<>::empty_set(n,1); + } + }; + +} // namespace codac + +#endif \ No newline at end of file diff --git a/src/core/2/domains/interval/codac2_cart_prod.cpp b/src/core/2/domains/interval/codac2_cart_prod.cpp new file mode 100644 index 00000000..b154fc36 --- /dev/null +++ b/src/core/2/domains/interval/codac2_cart_prod.cpp @@ -0,0 +1,50 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2022 + * \author Simon Rohou + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#include +#include "codac2_cart_prod.h" + +using namespace std; + +namespace codac2 +{ + IntervalVector cart_prod_dyn(const Interval& x1, const Interval& x2) + { + return IntervalVector({x1,x2}); + } + + IntervalVector_<2> cart_prod_static(const Interval& x1, const Interval& x2) + { + return IntervalVector_<2>({x1,x2}); + } + + IntervalVector cart_prod_dyn(const IntervalVector& x1, const Interval& x2) + { + IntervalVector x(x1.size()+1); + x << x1,x2; + return x; + } + + IntervalVector cart_prod_dyn(const Interval& x1, const IntervalVector& x2) + { + IntervalVector x(x2.size()+1); + x << x1,x2; + return x; + } + + IntervalVector cart_prod_dyn(const IntervalVector& x1, const IntervalVector& x2) + { + IntervalVector x(x1.size()+x2.size()); + x << x1,x2; + return x; + } + +} // namespace codac \ No newline at end of file diff --git a/src/core/2/domains/interval/codac2_cart_prod.h b/src/core/2/domains/interval/codac2_cart_prod.h new file mode 100644 index 00000000..f9a939b6 --- /dev/null +++ b/src/core/2/domains/interval/codac2_cart_prod.h @@ -0,0 +1,74 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2023 + * \author Simon Rohou + * \copyright Copyright 2023 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifndef __CODAC2_CARTPROD_H__ +#define __CODAC2_CARTPROD_H__ + +#include "codac2_Interval.h" +#include "codac2_IntervalVector.h" + +namespace codac2 +{ + IntervalVector cart_prod_dyn(const Interval& x1, const Interval& x2); + IntervalVector_<2> cart_prod_static(const Interval& x1, const Interval& x2); + IntervalVector cart_prod_dyn(const IntervalVector& x1, const Interval& x2); + + template + IntervalVector_ cart_prod_static(const IntervalVector_& x1, const Interval& x2) + { + IntervalVector_ x; + x << x1,x2; + return x; + } + + IntervalVector cart_prod_dyn(const Interval& x1, const IntervalVector& x2); + + template + IntervalVector_ cart_prod_static(const Interval& x1, const IntervalVector_& x2) + { + IntervalVector_ x; + x << x1,x2; + return x; + } + + IntervalVector cart_prod_dyn(const IntervalVector& x1, const IntervalVector& x2); + + template + IntervalVector_ cart_prod_static(const IntervalVector_& x1, const IntervalVector_& x2) + { + IntervalVector_ x; + x << x1,x2; + return x; + } + + template + IntervalVector_ cart_prod(const T1& x1, const T2& x2, const Args&... xi) // recursive variadic function + { + auto x_ = cart_prod_static(x1, x2); + if constexpr(sizeof...(xi) > 0) + return cart_prod(x_, xi...); + else + return x_; + } + + template + IntervalVector cart_prod(const T1& x1, const T2& x2, const Args&... xi) // recursive variadic function + { + IntervalVector x_ = cart_prod_dyn(IntervalVector(x1), IntervalVector(x2)); + if constexpr(sizeof...(xi) > 0) + return cart_prod(x_, xi...); + else + return x_; + } + +} // namespace codac + +#endif \ No newline at end of file diff --git a/src/core/2/domains/paving/codac2_Paving.h b/src/core/2/domains/paving/codac2_Paving.h new file mode 100644 index 00000000..0e137b0e --- /dev/null +++ b/src/core/2/domains/paving/codac2_Paving.h @@ -0,0 +1,156 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2023 + * \author Simon Rohou + * \copyright Copyright 2023 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifndef __CODAC2_PAVING_H__ +#define __CODAC2_PAVING_H__ + +#include +#include +#include +#include "codac2_Interval.h" +#include "codac2_IntervalVector.h" + +namespace codac2 +{ + template + class PavingBase + { + public: + + explicit PavingBase(const IntervalVector_& x) + : _x(x) + { + + } + + virtual ~PavingBase() = default; + + const IntervalVector_& box() const + { + return _x; + } + + std::shared_ptr

left() + { + return _left; + } + + std::shared_ptr

right() + { + return _right; + } + + bool is_empty() const + { + if(is_leaf()) + return _x.is_empty(); + + else + return (_left && _left->is_empty()) && (_right && _right->is_empty()); + } + + bool is_leaf() const + { + return not _left && not _right; + } + + double volume() const + { + if(is_leaf()) + return _x.volume(); + double v = 0.; + if(_left) v += _left->volume(); + if(_right) v += _right->volume(); + return v; + } + + virtual void bisect(float ratio = 0.49) + { + assert(Interval(0.,1.).interior_contains(ratio)); + assert(is_leaf() && "only leaves can be bisected"); + assert(_x.is_bisectable()); + auto p = _x.bisect(ratio); + _left = std::make_shared

(p.first); + _right = std::make_shared

(p.second); + } + + IntervalVector_ hull_box() const + { + if(is_leaf()) + return _x; + auto hull = IntervalVector_::empty_set(); + if(_left) hull |= _left->hull_box(); + if(_right) hull |= _right->hull_box(); + return hull; + } + + std::list>> boxes_list(const IntervalVector_& intersect = IntervalVector_()) const + { + std::list>> l; + boxes_list_push(l, intersect); + return l; + } + + std::list leaves_list() + { + std::list l; + leaves_list_push(l); + return l; + } + + protected: + + void boxes_list_push(std::list>>& l, const IntervalVector_& intersect = IntervalVector_()) const + { + if(is_leaf() && !_x.is_empty() && _x.intersects(intersect)) + l.push_back(std::cref(_x)); + else + { + if(_left) _left->boxes_list_push(l); + if(_right) _right->boxes_list_push(l); + } + } + + void leaves_list_push(std::list& l) + { + if(is_leaf() && !_x.is_empty()) + l.push_back(dynamic_cast(this)); + else + { + if(_left) _left->leaves_list_push(l); + if(_right) _right->leaves_list_push(l); + } + } + + public: // todo + + IntervalVector_ _x; + std::shared_ptr

_left = nullptr, _right = nullptr; + }; + + template + class Paving : public PavingBase,N> + { + public: + + explicit Paving(size_t n) + : PavingBase,N>(IntervalVector_(n)) + { } + + explicit Paving(const IntervalVector_& x) + : PavingBase,N>(x) + { } + }; + + +} // namespace codac + +#endif \ No newline at end of file diff --git a/src/core/2/domains/tube/codac2_AbstractConstTube.h b/src/core/2/domains/tube/codac2_AbstractConstTube.h new file mode 100644 index 00000000..15501829 --- /dev/null +++ b/src/core/2/domains/tube/codac2_AbstractConstTube.h @@ -0,0 +1,68 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2022 + * \author Simon Rohou + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifndef __CODAC2_ABSTRACTCONSTTUBE_H__ +#define __CODAC2_ABSTRACTCONSTTUBE_H__ + +#include +#include + +#include "codac_Interval.h" +#include "codac_IntervalVector.h" +#include "codac_TrajectoryVector.h" +#include "codac_BoolInterval.h" + +namespace codac2 +{ + using codac::Trajectory; + using codac::TrajectoryVector; + using codac::Interval; + using codac::BoolInterval; + + template + class AbstractConstTube + { + public: + + AbstractConstTube() + { + + } + + AbstractConstTube(const T& x); + + virtual ~AbstractConstTube() + { + + } + + virtual size_t size() const = 0; + virtual BoolInterval contains(const TrajectoryVector& value) const = 0; + virtual Interval t0_tf() const = 0; + virtual W codomain() const = 0; + // virtual W operator()(double t) const = 0; + //virtual W operator()(const Interval& t) const = 0; + + //TubeVectorComponent operator[](size_t index); + //const TubeVectorComponent operator[](size_t index) const; + + //friend std::ostream& operator<<(std::ostream& os, const TubeVector_& x); + void print(std::ostream& os) const + { + os << t0_tf() + << "↦" << codomain() + << std::flush; + } + }; + +} // namespace codac + +#endif \ No newline at end of file diff --git a/src/core/2/domains/tube/codac2_AbstractSlice.cpp b/src/core/2/domains/tube/codac2_AbstractSlice.cpp new file mode 100644 index 00000000..b5c71cb2 --- /dev/null +++ b/src/core/2/domains/tube/codac2_AbstractSlice.cpp @@ -0,0 +1,50 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2022 + * \author Simon Rohou + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#include "codac2_AbstractSlice.h" +#include "codac2_TSlice.h" +#include "codac_Exception.h" + +using namespace std; + +namespace codac2 +{ + AbstractSlice::AbstractSlice(const AbstractSlicedTube& tubevector, const list::iterator& it_tslice) : + _tubevector(tubevector), _it_tslice(it_tslice) + { + + } + + const Interval& AbstractSlice::t0_tf() const + { + return _it_tslice->t0_tf(); + } + + const TSlice& AbstractSlice::tslice() const + { + return *_it_tslice; + } + + const std::shared_ptr AbstractSlice::prev_abstract_slice_ptr() const + { + if(&(*_tubevector.first_abstract_slice_ptr()) == this) + return nullptr; + return prev(_it_tslice)->slices().at(&_tubevector); + } + + const std::shared_ptr AbstractSlice::next_abstract_slice_ptr() const + { + if(&(*_tubevector.last_abstract_slice_ptr()) == this) + return nullptr; + return next(_it_tslice)->slices().at(&_tubevector); + } + +} // namespace codac \ No newline at end of file diff --git a/src/core/2/domains/tube/codac2_AbstractSlice.h b/src/core/2/domains/tube/codac2_AbstractSlice.h new file mode 100644 index 00000000..5ad21e0b --- /dev/null +++ b/src/core/2/domains/tube/codac2_AbstractSlice.h @@ -0,0 +1,56 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2022 + * \author Simon Rohou + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifndef __CODAC2_ABSTRACTSLICE_H__ +#define __CODAC2_ABSTRACTSLICE_H__ + +#include +#include +#include +#include "codac_Interval.h" +#include "codac_IntervalVector.h" +#include "codac_TrajectoryVector.h" +#include "codac_Exception.h" +#include "codac2_AbstractSlicedTube.h" + +namespace codac2 +{ + using codac::Interval; + using codac::TrajectoryVector; + + class TSlice; + + class AbstractSlice + { + public: + + AbstractSlice(const AbstractSlicedTube& tubevector, const std::list::iterator& _it_tslice); + virtual std::shared_ptr duplicate() const = 0; + virtual size_t size() const = 0; + virtual void set_unbounded() = 0; + + const Interval& t0_tf() const; + const TSlice& tslice() const; + + const std::shared_ptr prev_abstract_slice_ptr() const; + const std::shared_ptr next_abstract_slice_ptr() const; + + + protected: + + const AbstractSlicedTube& _tubevector; + std::list::iterator _it_tslice; + + friend class TDomain; + }; +} + +#endif \ No newline at end of file diff --git a/src/core/2/domains/tube/codac2_AbstractSlicedTube.cpp b/src/core/2/domains/tube/codac2_AbstractSlicedTube.cpp new file mode 100644 index 00000000..0c29243e --- /dev/null +++ b/src/core/2/domains/tube/codac2_AbstractSlicedTube.cpp @@ -0,0 +1,39 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2022 + * \author Simon Rohou + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#include "codac2_AbstractSlicedTube.h" + +using namespace std; + +namespace codac2 +{ + AbstractSlicedTube::AbstractSlicedTube(const shared_ptr& tdomain) : + _tdomain(tdomain) + { + + } + + shared_ptr& AbstractSlicedTube::tdomain() + { + return const_cast&>( + static_cast(*this).tdomain()); + } + + const shared_ptr& AbstractSlicedTube::tdomain() const + { + return _tdomain; + } + + Interval AbstractSlicedTube::t0_tf() const + { + return _tdomain->t0_tf(); + } +} // namespace codac \ No newline at end of file diff --git a/src/core/2/domains/tube/codac2_AbstractSlicedTube.h b/src/core/2/domains/tube/codac2_AbstractSlicedTube.h new file mode 100644 index 00000000..84041ff5 --- /dev/null +++ b/src/core/2/domains/tube/codac2_AbstractSlicedTube.h @@ -0,0 +1,41 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2022 + * \author Simon Rohou + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifndef __CODAC2_ABSTRACTTUBE_H__ +#define __CODAC2_ABSTRACTTUBE_H__ + +#include "codac2_TDomain.h" + +namespace codac2 +{ + class AbstractSlice; + + class AbstractSlicedTube + { + public: + + AbstractSlicedTube(const std::shared_ptr& tdomain); + + virtual const std::shared_ptr& first_abstract_slice_ptr() const = 0; + virtual const std::shared_ptr& last_abstract_slice_ptr() const = 0; + + std::shared_ptr& tdomain(); + const std::shared_ptr& tdomain() const; + Interval t0_tf() const; + + + protected: + + std::shared_ptr _tdomain; + }; +} // namespace codac + +#endif \ No newline at end of file diff --git a/src/core/2/domains/tube/codac2_Slice.h b/src/core/2/domains/tube/codac2_Slice.h new file mode 100644 index 00000000..7d54928c --- /dev/null +++ b/src/core/2/domains/tube/codac2_Slice.h @@ -0,0 +1,363 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2022 + * \author Simon Rohou + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifndef __CODAC2_SLICE_H__ +#define __CODAC2_SLICE_H__ + +#include +#include +#include +#include "codac_Interval.h" +#include "codac_IntervalVector.h" +#include "codac_TrajectoryVector.h" +#include "codac_Exception.h" +#include "codac2_AbstractSlice.h" +#include "codac_BoolInterval.h" +#include "codac_ConvexPolygon.h" +#include "codac_DynCtc.h" + +#define EPSILON_CONTAINS ibex::next_float(0.) * 1000. //!< epsilon limit of the contains() algorithm + +namespace codac2 +{ + using codac::Interval; + using codac::TrajectoryVector; + using codac::BoolInterval; + + class AbstractSlicedTube; + class TSlice; + + template + class Slice : public AbstractSlice + { + public: + + explicit Slice(size_t n, const AbstractSlicedTube& tube_vector, const std::list::iterator& it_tslice) : + Slice(T(n), tube_vector, it_tslice) + { + + } + + explicit Slice(const T& codomain, const AbstractSlicedTube& tube_vector, const std::list::iterator& it_tslice) : + AbstractSlice(tube_vector,it_tslice), _codomain(codomain) + { + + } + + Slice(const Slice& s) : + Slice(s, s._tubevector) + { + + } + + Slice(const Slice& s, const AbstractSlicedTube& tubevector) : + AbstractSlice(tubevector, s._it_tslice), _codomain(s._codomain) + { + + } + + ~Slice() + { + + } + + // Slice objects cannot be copyable or movable, + // as they are supposed to be connected to other Slice objects + Slice& operator=(const Slice&) = delete; + Slice(Slice&&) = delete; + Slice& operator=(Slice&&) = delete; + + const AbstractSlicedTube& tube_vector() const + { + return _tubevector; + } + + virtual std::shared_ptr duplicate() const + { + return std::make_shared(*this); + } + + virtual size_t size() const + { + // todo: define size() method in Interval class + if constexpr(std::is_same::value) + return 1; + else + return codomain().size(); + } + + double volume() const + { + if constexpr(std::is_same::value) + return codomain().diam(); + else + return codomain().volume(); + } + + bool is_gate() const + { + return t0_tf().is_degenerated(); + } + + bool is_empty() const + { + if(is_gate()) + return _codomain.is_empty(); + else + return input_gate().is_empty() || output_gate().is_empty(); + } + + bool is_unbounded() const + { + return codomain().is_unbounded(); + } + + BoolInterval contains(const TrajectoryVector& x) const + { + if constexpr(!std::is_same::value) + { + assert(false && "not implemented"); + return BoolInterval::MAYBE; + } + + else + { + if(!t0_tf().intersects(x.tdomain())) + return BoolInterval::MAYBE; + + BoolInterval is_contained = BoolInterval::YES; + if(!t0_tf().is_subset(x.tdomain())) + is_contained = BoolInterval::MAYBE; + + Interval t_ = t0_tf() & x.tdomain(); + T traj_tdomain(x(t_)); + // Using x(Interval(double)) for reliable evaluation: + T traj_input(x(Interval(t_.lb()))); + T traj_output(x(Interval(t_.ub()))); + + if(_codomain.intersects(traj_tdomain) == BoolInterval::NO + || input_gate().intersects(traj_input) == BoolInterval::NO + || output_gate().intersects(traj_output) == BoolInterval::NO) + return BoolInterval::NO; + + else + { + if(!traj_input.is_subset(input_gate()) || !traj_output.is_subset(output_gate())) + return BoolInterval::MAYBE; + + else if(traj_tdomain.is_subset(_codomain)) + return is_contained; + + else // too much pessimism for the trajectory evaluation on t0_tf() + { + // Bisections are performed to reach an accurate evaluation + + std::list s_subtdomains; + s_subtdomains.push_front(t_); + + while(!s_subtdomains.empty()) + { + Interval t = s_subtdomains.front(); + s_subtdomains.pop_front(); + + T thinner_eval(x(t)); + + if(!_codomain.intersects(thinner_eval)) + { + return BoolInterval::NO; + } + + else if(!thinner_eval.is_subset(_codomain)) + { + if(t.diam() < EPSILON_CONTAINS) + return BoolInterval::MAYBE; + + s_subtdomains.push_front(Interval(t.lb(), t.lb() + t.diam() / 2.)); + s_subtdomains.push_front(Interval(t.lb() + t.diam() / 2., t.ub())); + } + } + + return is_contained; + } + } + } + } + + const std::shared_ptr> prev_slice_ptr() const + { + return std::static_pointer_cast>(prev_abstract_slice_ptr()); + } + + std::shared_ptr> prev_slice_ptr() + { + return std::const_pointer_cast>( + static_cast(*this).prev_slice_ptr()); + } + + const std::shared_ptr> next_slice_ptr() const + { + return std::static_pointer_cast>(next_abstract_slice_ptr()); + } + + std::shared_ptr> next_slice_ptr() + { + return std::const_pointer_cast>( + static_cast(*this).next_slice_ptr()); + } + + const T& codomain() const + { + return _codomain; + } + + T input_gate() const + { + if(!prev_slice_ptr()) + return codomain(); + + else + { + if(prev_slice_ptr()->is_gate()) + return prev_slice_ptr()->codomain(); + else + return codomain() & prev_slice_ptr()->codomain(); + } + } + + T output_gate() const + { + if(!next_slice_ptr()) + return codomain(); + + else + { + if(next_slice_ptr()->is_gate()) + return next_slice_ptr()->codomain(); + else + return codomain() & next_slice_ptr()->codomain(); + } + } + + void set(const T& x, bool propagate = true) + { + if constexpr(!std::is_same::value) { // 'if' to be removed with virtual set classes + assert((size_t)codomain().size() == size()); + } + + _codomain = x; + + if(prev_slice_ptr()) + { + if constexpr(!std::is_same::value) { // 'if' to be removed with virtual set classes + assert((size_t)prev_slice_ptr()->codomain().size() == size()); + } + if(is_gate()) + _codomain &= prev_slice_ptr()->codomain(); + else if(prev_slice_ptr()->is_gate()) + prev_slice_ptr()->_codomain &= _codomain; + } + + if(next_slice_ptr()) + { + if constexpr(!std::is_same::value) { // 'if' to be removed with virtual set classes + assert((size_t)next_slice_ptr()->codomain().size() == size()); + } + if(is_gate()) + _codomain &= next_slice_ptr()->codomain(); + else if(next_slice_ptr()->is_gate()) + next_slice_ptr()->_codomain &= _codomain; + } + + if(propagate && is_empty()) + set_empty(true, codac::TimePropag::FORWARD | codac::TimePropag::BACKWARD); + } + + void set_empty(bool propagate = true, codac::TimePropag t_propa = codac::TimePropag::FORWARD | codac::TimePropag::BACKWARD) + { + _codomain.set_empty(); + + if(propagate) + { + if(t_propa & codac::TimePropag::BACKWARD && prev_slice_ptr()) + prev_slice_ptr()->set_empty(true, codac::TimePropag::BACKWARD); + if(t_propa & codac::TimePropag::FORWARD && next_slice_ptr()) + next_slice_ptr()->set_empty(true, codac::TimePropag::FORWARD); + } + + else if(!is_gate()) + { + if(prev_slice_ptr() && prev_slice_ptr()->is_gate()) + prev_slice_ptr()->set_empty(); + if(next_slice_ptr() && next_slice_ptr()->is_gate()) + next_slice_ptr()->set_empty(); + } + } + + void set_unbounded() + { + if constexpr(std::is_same::value) + _codomain = T(size()); + else + _codomain = T(); + + //if constexpr(std::is_same::value || std::is_same::value) // 'if' to be removed with virtual set classes + // _codomain = T(); + //else + // _codomain = T(size()); + } + + void set_component(size_t i, const Interval& xi) + { + assert((size_t)codomain().size() == size()); + _codomain[i] = xi; + if(is_gate()) + { + if(prev_slice_ptr()) + _codomain[i] &= prev_slice_ptr()->codomain()[i]; + if(next_slice_ptr()) + _codomain[i] &= next_slice_ptr()->codomain()[i]; + } + } + + const Slice& inflate(double rad) + { + assert(rad >= 0. && "cannot inflate negative value"); + _codomain.inflate(rad); + return *this; + } + + bool operator==(const Slice& x) const + { + return _codomain == x._codomain; + } + + bool operator!=(const Slice& x) const + { + return _codomain != x._codomain; + } + + friend std::ostream& operator<<(std::ostream& os, const Slice& x) + { + os << x.t0_tf() + << "↦" << x.codomain() + << std::flush; + return os; + } + + + protected: + + T _codomain; + }; + +} // namespace codac + +#endif \ No newline at end of file diff --git a/src/core/2/domains/tube/codac2_TDomain.cpp b/src/core/2/domains/tube/codac2_TDomain.cpp new file mode 100644 index 00000000..001d1cf1 --- /dev/null +++ b/src/core/2/domains/tube/codac2_TDomain.cpp @@ -0,0 +1,262 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2022 + * \author Simon Rohou + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#include +#include "codac2_TSlice.h" +#include "codac2_TDomain.h" +#include "codac2_Slice.h" +#include "codac_predef_values.h" + +using namespace std; +using namespace codac; + +namespace codac2 +{ + TDomain::TDomain(const Interval& t0_tf) + : _tslices({ TSlice(t0_tf) }) + { + + } + + TDomain::TDomain(const Interval& t0_tf, double dt, bool with_gates) + { + assert(!t0_tf.is_unbounded() && !t0_tf.is_degenerated()); + assert(!t0_tf.is_empty()); + assert(dt > 0.); + + for(double t = t0_tf.lb() ; t < t0_tf.ub()+dt ; t=t+dt) + { + double t_next = min(t0_tf.ub(),t+dt); + if(with_gates) + _tslices.push_back(TSlice(Interval(t))); + _tslices.push_back(TSlice(Interval(t,t_next))); + if(t_next == t0_tf.ub()) + break; + } + + if(with_gates) + _tslices.push_back(TSlice(Interval(t0_tf.ub()))); + } + + const Interval TDomain::t0_tf() const + { + return Interval(_tslices.front().t0_tf().lb(), + prev(_tslices.end())->t0_tf().ub()); + } + + size_t TDomain::nb_tslices() const + { + return _tslices.size(); + } + + size_t TDomain::nb_tubes() const + { + return _tslices.front().slices().size(); + } + + bool TDomain::all_gates_defined() const + { + if(t0_tf().is_degenerated()) + return true; + + else if(nb_tslices() == 1) + return false; + + else + { + list::const_iterator it = std::next(_tslices.begin()); + while(it != _tslices.end()) + { + if(it->t0_tf().is_degenerated()) + return false; + + it++; + + if(it != _tslices.end()) + { + if(!it->t0_tf().is_degenerated()) + return false; + it++; + } + } + + return true; + } + } + + list::iterator TDomain::iterator_tslice(double t) + { + if(!t0_tf().contains(t)) + return _tslices.end(); + + list::iterator it; + for(it = _tslices.begin(); it != _tslices.end(); ++it) + { + const Interval& tdom = it->t0_tf(); + if((tdom.is_degenerated() && tdom.lb() == t) // gate + || (tdom.lb() <= t && tdom.ub() > t)) // slice + return it; + } + + it = _tslices.end(); it--; + return it; + } + + list::iterator TDomain::sample(double t, bool with_gates) + { + assert(!isnan(t)); + list::iterator it; + + if(t <= t0_tf().lb()) // if outside the already defined tdomain + { + it = _tslices.begin(); + + if(it->is_gate() && it->t0_tf().lb() == t) + return it; + + TSlice ts(*it, Interval(t, t0_tf().lb())); // duplicate with different tdomain + it = _tslices.insert(it, ts); + for(auto& [k,s] : it->_slices) + { + s->_it_tslice = it; + s->set_unbounded(); // reinitialization + } + + if(with_gates) + { + if(it->t0_tf().is_degenerated()) // iterator already pointing to a gate + return it; + else + return sample(t, true); // recursive + } + else + return it; + } + + else if(t > t0_tf().ub()) // if outside the already defined tdomain + { + it = _tslices.end(); + TSlice ts(*std::prev(it), Interval(t0_tf().ub(),t)); // duplicate with different tdomain + it = _tslices.insert(it, ts); + for(auto& [k,s] : it->_slices) + { + s->_it_tslice = it; + s->set_unbounded(); // reinitialization + } + + if(with_gates) + return sample(t, true); // recursive + else + return it; + } + + else // inside [t0,tf] + { + it = iterator_tslice(t); + assert(it != _tslices.end()); + const Interval tdomain_it = it->t0_tf(); + assert(tdomain_it.contains(t)); + + if(tdomain_it.is_degenerated() + || (!with_gates && (tdomain_it.lb() == t || t == t0_tf().ub()))) + return it; // nothing more to do + + // Else, performing sampling + it->set_tdomain(Interval(tdomain_it.lb(), t)); + bool new_gate_added = it->t0_tf().is_degenerated(); + list::iterator it_gate = it; + TSlice ts(*it, Interval(t, tdomain_it.ub())); // duplicate with different tdomain + // (*it) is the TSlice to copy + + // From C++ insert() doc: the container is extended by inserting new elements before the element at the specified position + ++it; // we will insert the new tslice before the next TSlice [t.ub(),..] + it = _tslices.insert(it, ts); // then, it points to the newly inserted element + for(auto& [k,s] : it->_slices) // adding the new iterator pointer to the new slices + s->_it_tslice = it; + + // In case the sampling includes the creation of a gate, the method is called again at same t + if(new_gate_added) + return it_gate; + else if(with_gates && !new_gate_added) + return sample(t, true); + else + return it; + } + } + + void TDomain::sample(const Interval& t0_tf, double dt, bool with_gates) + { + assert(dt >= 0.); + assert(!t0_tf.is_degenerated()); + for(double t = t0_tf.lb() ; t < t0_tf.ub()+dt ; t=t+dt) + sample(min(t0_tf.ub(),t), with_gates); + } + + const list& TDomain::tslices() const + { + return _tslices; + } + + list& TDomain::tslices() + { + return const_cast&>( + static_cast(*this).tslices()); + } + + void TDomain::delete_gates() + { + list::iterator it = _tslices.begin(); + while(it != _tslices.end()) + { + if(it->t0_tf().is_degenerated()) + _tslices.erase(it++); + + else + ++it; + } + } + + ostream& operator<<(ostream& os, const TDomain& x) + { + os << x.t0_tf() + << ", " << x.nb_tslices() + << " slice" << (x.nb_tslices() > 1 ? "s" : "") + << ", " << x.nb_tubes() + << " tube" << (x.nb_tubes() > 1 ? "s" : "") + << flush; + return os; + } + + shared_ptr create_tdomain(const Interval& t0_tf) + { + return make_shared(t0_tf); + } + + shared_ptr create_tdomain(const Interval& t0_tf, double dt, bool with_gates) + { + return make_shared(t0_tf, dt, with_gates); + } + + bool TDomain::are_same(const shared_ptr& tdom1, const shared_ptr& tdom2) + { + if(tdom1 == tdom2) + return true; + if(tdom1->nb_tslices() != tdom2->nb_tslices()) + return false; + list::const_iterator it1 = tdom1->tslices().cbegin(), it2 = tdom2->tslices().cbegin(); + while(it1 != tdom1->tslices().cend()) + { + if(*it1 != *it2) return false; + it1++; it2++; + } + return true; + } +} // namespace codac \ No newline at end of file diff --git a/src/core/2/domains/tube/codac2_TDomain.h b/src/core/2/domains/tube/codac2_TDomain.h new file mode 100644 index 00000000..cfd8a920 --- /dev/null +++ b/src/core/2/domains/tube/codac2_TDomain.h @@ -0,0 +1,61 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2022 + * \author Simon Rohou + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifndef __CODAC2_TDOMAIN_H__ +#define __CODAC2_TDOMAIN_H__ + +#include +#include +#include +#include + +#include "codac_Interval.h" +#include "codac_predef_values.h" + +namespace codac2 +{ + using codac::Interval; + class TSlice; + + class TDomain + { + public: + + explicit TDomain(const Interval& t0_tf); + explicit TDomain(const Interval& t0_tf, double dt, bool with_gates = false); + const Interval t0_tf() const; // todo: keep this method? + std::list::iterator iterator_tslice(double t); // returns it on last slice if t==t_f, not end + size_t nb_tslices() const; + size_t nb_tubes() const; + bool all_gates_defined() const; + std::list::iterator sample(double t, bool with_gate = false); + void sample(const Interval& t0_tf, double dt, bool with_gates = false); + friend std::ostream& operator<<(std::ostream& os, const TDomain& x); + const std::list& tslices() const; + std::list& tslices(); + void delete_gates(); + static bool are_same(const std::shared_ptr& tdom1, const std::shared_ptr& tdom2); + + + protected: + + std::list _tslices; + + template + friend class Tube; + }; + + std::shared_ptr create_tdomain(const Interval& t0_tf = Interval(-oo,oo)); + std::shared_ptr create_tdomain(const Interval& t0_tf, double dt, bool with_gates = false); + +} // namespace codac + +#endif \ No newline at end of file diff --git a/src/core/2/domains/tube/codac2_TSlice.cpp b/src/core/2/domains/tube/codac2_TSlice.cpp new file mode 100644 index 00000000..1c89ab5f --- /dev/null +++ b/src/core/2/domains/tube/codac2_TSlice.cpp @@ -0,0 +1,70 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2022 + * \author Simon Rohou + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#include "codac2_TSlice.h" +#include "codac2_Slice.h" + +using namespace std; +using namespace codac; + +namespace codac2 +{ + TSlice::TSlice(const Interval& tdomain) : + _slices(map>()) + { + set_tdomain(tdomain); + } + + TSlice::TSlice(const TSlice& tslice, const Interval& tdomain) : + TSlice(tdomain) + { + for(const auto&[k,s] : tslice._slices) + _slices.insert(pair>( + k, s->duplicate())); + } + + const Interval& TSlice::t0_tf() const + { + return _t0_tf; + } + + bool TSlice::is_gate() const + { + return _t0_tf.is_degenerated(); + } + + void TSlice::set_tdomain(const Interval& tdomain) + { + assert(!tdomain.is_empty()); + _t0_tf = tdomain; + } + + const map>& TSlice::slices() const + { + return _slices; + } + + bool TSlice::operator==(const TSlice& x) const + { + return _t0_tf == x._t0_tf; + } + + bool TSlice::operator!=(const TSlice& x) const + { + return !operator==(x); + } + + ostream& operator<<(ostream& os, const TSlice& x) + { + os << x._t0_tf; + return os; + } +} // namespace codac \ No newline at end of file diff --git a/src/core/2/domains/tube/codac2_TSlice.h b/src/core/2/domains/tube/codac2_TSlice.h new file mode 100644 index 00000000..fc0223a3 --- /dev/null +++ b/src/core/2/domains/tube/codac2_TSlice.h @@ -0,0 +1,57 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2022 + * \author Simon Rohou + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifndef __CODAC2_TSLICE_H__ +#define __CODAC2_TSLICE_H__ + +#include +#include +#include +#include + +#include "codac_Interval.h" +#include "codac2_Slice.h" + +namespace codac2 +{ + using codac::Interval; + + class TDomain; + class AbstractSlice; + class AbstractSlicedTube; + + class TSlice + { + public: + + explicit TSlice(const Interval& tdomain); + TSlice(const TSlice& tslice, const Interval& tdomain); // performs a deep copy on slices + const Interval& t0_tf() const; + bool is_gate() const; + const std::map>& slices() const; + bool operator==(const TSlice& x) const; + bool operator!=(const TSlice& x) const; + friend std::ostream& operator<<(std::ostream& os, const TSlice& x); + + protected: + + void set_tdomain(const Interval& tdomain); + + Interval _t0_tf; + std::map> _slices; + + friend class TDomain; + template + friend class Tube; + }; +} // namespace codac + +#endif \ No newline at end of file diff --git a/src/core/2/domains/tube/codac2_Tube.cpp b/src/core/2/domains/tube/codac2_Tube.cpp new file mode 100644 index 00000000..55085eb8 --- /dev/null +++ b/src/core/2/domains/tube/codac2_Tube.cpp @@ -0,0 +1,102 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2022 + * \author Simon Rohou + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#include "codac2_Tube.h" +#include "codac_polygon_arithmetic.h" + +using namespace std; +using namespace codac; + +namespace codac2 +{ + codac::Tube to_codac1(const Tube& x) + { + codac::Tube x_(x.t0_tf()); + for(const auto& s : x) + if(!s.t0_tf().is_unbounded()) // temporaly unbounded tslices not supported in codac1 + x_.set(s.codomain(), s.t0_tf()); + for(const auto& s : x) // setting gate (were overwritten) + if(s.t0_tf().is_degenerated()) + x_.set(s.codomain(), s.t0_tf()); + return x_; + } + + codac::TubeVector to_codac1(const Tube& x) + { + codac::TubeVector x_(x.t0_tf(), x.size()); + for(const auto& s : x) + if(!s.t0_tf().is_unbounded()) // temporaly unbounded tslices not supported in codac1 + x_.set(s.codomain(), s.t0_tf()); + for(const auto& s : x) // setting gate (were overwritten) + if(s.t0_tf().is_degenerated()) + x_.set(s.codomain(), s.t0_tf()); + return x_; + } + + codac::TubeVector to_codac1_poly(const Tube& x) + { + codac::TubeVector x_(x.t0_tf(), x.size()); + for(const auto& s : x) + if(!s.t0_tf().is_unbounded()) // temporaly unbounded tslices not supported in codac1 + x_.set(s.codomain().box(), s.t0_tf()); + for(const auto& s : x) // setting gate (were overwritten) + if(s.t0_tf().is_degenerated()) + x_.set(s.codomain().box(), s.t0_tf()); + return x_; + } + + codac2::Tube to_codac2(const codac::Tube& x_) + { + auto tdomain = create_tdomain(x_.tdomain()); + for(const codac::Slice *s = x_.first_slice() ; s != nullptr ; s=s->next_slice()) + tdomain->sample(s->tdomain().lb(), true); // with gate + tdomain->sample(x_.tdomain().ub(), true); // with gate + codac2::Tube x(tdomain, codac::Interval()); + for(auto& s : x) // includes gates + s.set(x_(s.t0_tf())); + return x; + } + + codac2::Tube to_codac2(const codac::TubeVector& x_) + { + auto tdomain = create_tdomain(x_[0].tdomain()); + for(const codac::Slice *s = x_[0].first_slice() ; s != nullptr ; s=s->next_slice()) + tdomain->sample(s->tdomain().lb(), true); // with gate + tdomain->sample(x_[0].tdomain().ub(), true); // with gate + codac2::Tube x(tdomain, codac::IntervalVector(x_.size())); + for(auto& s : x) // includes gates + s.set(x_(s.t0_tf())); + return x; + } + + codac2::Tube to_codac2_poly(const codac::TubeVector& x_) + { + assert(x_.size() == 2); + auto tdomain = create_tdomain(x_[0].tdomain()); + for(const codac::Slice *s = x_[0].first_slice() ; s != nullptr ; s=s->next_slice()) + tdomain->sample(s->tdomain().lb(), true); // with gate + tdomain->sample(x_[0].tdomain().ub(), true); // with gate + codac2::Tube x(tdomain, ConvexPolygon()); + for(auto& s : x) // includes gates + s.set(ConvexPolygon(x_(s.t0_tf()))); + return x; + } + + template <> + Tube::Tube(const std::shared_ptr& tdomain, const TFnc& f) : + Tube(tdomain, IntervalVector(f.image_dim())) + { + assert(f.nb_var() == 0 && "function's inputs must be limited to system variable"); + + for(auto& s : *this) + s.set(f.eval_vector(s.t0_tf())); + } +} // namespace codac \ No newline at end of file diff --git a/src/core/2/domains/tube/codac2_Tube.h b/src/core/2/domains/tube/codac2_Tube.h new file mode 100644 index 00000000..6f94728a --- /dev/null +++ b/src/core/2/domains/tube/codac2_Tube.h @@ -0,0 +1,492 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2022 + * \author Simon Rohou + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifndef __CODAC2_TUBE_H__ +#define __CODAC2_TUBE_H__ + +#include +#include +#include "codac_TFnc.h" +#include "codac2_TSlice.h" +#include "codac_Tube.h" // to be removed +#include "codac_TubeVector.h" // to be removed +#include "codac2_AbstractSlicedTube.h" +#include "codac2_AbstractConstTube.h" +#include "codac2_TDomain.h" +#include "codac_ConvexPolygon.h" + +namespace codac2 +{ + using codac::TFnc; + using codac::BoolInterval; + + template + class Slice; + template + class TubeEvaluation; + template + class ConstTubeEvaluation; + template + class TubeComponent; + + + template + class Tube : public AbstractSlicedTube, public AbstractConstTube> + { + public: + + explicit Tube(const std::shared_ptr& tdomain) : + Tube(tdomain, T()) + { } + + explicit Tube(const std::shared_ptr& tdomain, const TFnc& f) : + Tube(tdomain, (std::is_same::value ? T() : T(f.image_dim()))) + { + assert(f.nb_var() == 0 && "function's inputs must be limited to system variable"); + if constexpr(std::is_same::value) { + assert(f.image_dim() == 1); + } + + for(auto& s : *this) + { + if constexpr(std::is_same::value) + s.set(f.eval(s.t0_tf())); + + else + s.set(f.eval_vector(s.t0_tf())); + + if(s.is_empty()) + std::cout << "IS EMPTY: " << s << std::endl; + } + } + + explicit Tube(const std::shared_ptr& tdomain, const T& default_value) : + AbstractSlicedTube(tdomain) + { + for(std::list::iterator it = _tdomain->_tslices.begin(); + it != _tdomain->_tslices.end(); ++it) + { + it->_slices.insert( + std::pair>>(this, + std::make_shared>(default_value, *this, it))); + } + } + + Tube(const Tube& x) : + AbstractSlicedTube(x.tdomain()) + { + for(std::list::iterator it = _tdomain->_tslices.begin(); + it != _tdomain->_tslices.end(); ++it) + { + std::shared_ptr> s_ptr = std::make_shared>(x(it), *this); + it->_slices.insert(std::pair>>(this, s_ptr)); + } + } + + ~Tube() + { + for(auto& s : _tdomain->_tslices) + s._slices.erase(this); + } + + Tube& operator=(const Tube& x) + { + if(_tdomain != x._tdomain) + throw std::exception(); // todo: better exception + + for(auto it = _tdomain->_tslices.begin(); + it != _tdomain->_tslices.end(); ++it) + (*this)(it).set(x(it).codomain()); + + return *this; + } + + virtual Interval t0_tf() const + { + return AbstractSlicedTube::t0_tf(); + } + + size_t size() const + { + // todo: define size() method in Interval class + if constexpr(std::is_same::value) + return 1; + else + return first_slice().size(); + } + + size_t nb_slices() const + { + return _tdomain->nb_tslices(); + } + + double volume() const + { + double volume = 0.; + for(const auto& s : *this) + volume += s.volume(); + return volume; + } + + virtual const std::shared_ptr& first_abstract_slice_ptr() const + { + return _tdomain->tslices().front().slices().at(this); + } + + virtual const std::shared_ptr& last_abstract_slice_ptr() const + { + return _tdomain->tslices().back().slices().at(this); + } + + const std::shared_ptr> first_slice_ptr() const + { + return std::static_pointer_cast>(first_abstract_slice_ptr()); + } + + const Slice& first_slice() const + { + return *first_slice_ptr(); + } + + Slice& first_slice() + { + return const_cast&>( + static_cast(*this).first_slice()); + } + + const std::shared_ptr> last_slice_ptr() const + { + return std::static_pointer_cast>(last_abstract_slice_ptr()); + } + + const Slice& last_slice() const + { + return *last_slice_ptr(); + } + + Slice& last_slice() + { + return const_cast&>( + static_cast(*this).last_slice()); + } + + bool is_empty() const + { + // Fast evaluation by considering gates first, then envelopes, + // which allows to quickly identify an empty set + for(const auto& s : *this) + if(s.is_gate() && s.is_empty()) + return true; + for(const auto& s : *this) + if(!s.is_gate() && s.is_empty()) + return true; + return false; + } + + bool is_unbounded() const + { + for(const auto& s : *this) + if(s.is_unbounded()) + return true; + return false; + } + + BoolInterval contains(const TrajectoryVector& x) const + { + assert(x.tdomain() == tdomain()->t0_tf()); + + BoolInterval result = BoolInterval::YES; + for(const auto& s : *this) + { + if(s.is_gate()) continue; + BoolInterval b = s.contains(x); + if(b == BoolInterval::NO) return BoolInterval::NO; + else if(b == BoolInterval::MAYBE) result = BoolInterval::MAYBE; + } + + return result; + } + + T codomain() const + { + T codomain = first_slice().codomain(); + codomain.set_empty(); + for(const auto& s : *this) + codomain |= s.codomain(); + return codomain; + } + + // Remove this? (direct access with () ) + std::shared_ptr> slice_ptr(const std::list::iterator& it) + { + return std::static_pointer_cast>(it->slices().at(this)); + } + + Slice& operator()(const std::list::iterator& it) + { + return const_cast&>( + static_cast(*this).operator()(it)); + } + + const Slice& operator()(const std::list::iterator& it) const + { + return *std::static_pointer_cast>(it->slices().at(this)); + } + + TubeEvaluation operator()(double t) + { + return TubeEvaluation(this, t); + } + + const TubeEvaluation operator()(double t) const + { + return ConstTubeEvaluation(this, t); + } + + TubeEvaluation operator()(const Interval& t) + { + return TubeEvaluation(this, t); + } + + const TubeEvaluation operator()(const Interval& t) const + { + return TubeEvaluation(this, t); + } + + T eval(double t) const + { + if(!tdomain()->t0_tf().contains(t)) + { + if constexpr(!std::is_same::value) + return T(); + else + return T(size()); + } + std::list::iterator it_t = _tdomain->iterator_tslice(t); + assert(it_t != _tdomain->_tslices.end()); + T x = std::static_pointer_cast>(it_t->_slices.at(this))->codomain(); + if(!it_t->is_gate() && t==it_t->t0_tf().lb() && it_t!=_tdomain->_tslices.begin()) + x &= std::static_pointer_cast>((--it_t)->_slices.at(this))->codomain(); + return x; + } + + T eval(const Interval& t) const + { + if(!tdomain()->t0_tf().is_superset(t)) + { + if constexpr(!std::is_same::value) + return T(); + else + return T(size()); + } + + if(t.is_degenerated()) + return eval(t.lb()); + + std::list::iterator it = _tdomain->iterator_tslice(t.lb()); + T codomain = std::static_pointer_cast>(it->_slices.at(this))->codomain(); + + while(it != std::next(_tdomain->iterator_tslice(t.ub()))) + { + if(it->t0_tf().lb() == t.ub()) break; + codomain |= std::static_pointer_cast>(it->_slices.at(this))->codomain(); + it++; + } + + return codomain; + } + + void set(const T& codomain) + { + if constexpr(std::is_same::value) { + assert((size_t)codomain.size() == size()); + } + for(auto& s : *this) + if(!s.is_gate()) + s.set(codomain); + for(auto& s : *this) + if(s.is_gate()) + s.set(codomain); + } + + void set(const T& codomain, double t) + { + if constexpr(std::is_same::value) { + assert((size_t)codomain.size() == size()); + } + std::list::iterator it = _tdomain->sample(t,true); + (*this)(it).set(codomain); + } + + const Tube& inflate(double rad) + { + for(auto& s : *this) + if(!s.is_gate()) + s.inflate(rad); + for(auto& s : *this) + if(s.is_gate()) + s.inflate(rad); + return *this; + } + + TubeComponent operator[](size_t i) + { + assert(i >= 0 && i < size()); + return TubeComponent(*this, i); + } + + bool operator==(const Tube& x) const + { + if(!TDomain::are_same(tdomain(), x.tdomain())) + return false; + + std::list::iterator it_this = _tdomain->_tslices.begin(); + std::list::const_iterator it_x = x.tdomain()->tslices().cbegin(); + + while(it_this != _tdomain->tslices().end()) + { + if(*std::static_pointer_cast>(it_this->_slices.at(this)) != + *std::static_pointer_cast>(it_x->slices().at(&x))) + return false; + it_this++; it_x++; + } + + return true; + } + + Tube operator&=(const Tube& x) + { + assert(TDomain::are_same(tdomain(), x.tdomain())); + std::list::iterator it_this = _tdomain->_tslices.begin(); + std::list::const_iterator it_x = x.tdomain()->tslices().cbegin(); + + while(it_this != _tdomain->tslices().end()) + { + std::shared_ptr> s = std::static_pointer_cast>(it_this->_slices.at(this)); + s->set(s->codomain() & std::static_pointer_cast>(it_x->slices().at(&x))->codomain()); + it_this++; it_x++; + } + + assert(it_x == x.tdomain()->tslices().cend()); + return *this; + } + + friend std::ostream& operator<<(std::ostream& os, const Tube& x) + { + x.AbstractConstTube>::print(os); + // Adding information related to sliced structure + os << ", " << x.nb_slices() + << " slice" << (x.nb_slices() > 1 ? "s" : "") + << std::flush; + return os; + } + + + public: + + using base_container = std::list; + + struct iterator : public base_container::iterator + { + using iterator_category = typename base_container::iterator::iterator_category; + using difference_type = typename base_container::iterator::difference_type; + + using value_type = Slice; + using pointer = Slice*; + using reference = Slice&; + + public: + + iterator(const Tube& x, base_container::iterator it) : + base_container::iterator(it), _x(x) { } + + reference operator*() + { + return static_cast(*((*this)->_slices.at(&_x))); + } + + protected: + + const Tube& _x; + }; + + iterator begin() { return iterator(*this, _tdomain->_tslices.begin()); } + iterator end() { return iterator(*this, _tdomain->_tslices.end()); } + + struct reverse_iterator : public base_container::reverse_iterator + { + using iterator_category = typename base_container::reverse_iterator::iterator_category; + using difference_type = typename base_container::reverse_iterator::difference_type; + + using value_type = Slice; + using pointer = Slice*; + using reference = Slice&; + + public: + + reverse_iterator(const Tube& x, base_container::reverse_iterator it) : + base_container::reverse_iterator(it), _x(x) { } + + reference operator*() + { + return static_cast(*((*this)->_slices.at(&_x))); + } + + protected: + + const Tube& _x; + }; + + reverse_iterator rbegin() { return reverse_iterator(*this, _tdomain->_tslices.rbegin()); } + reverse_iterator rend() { return reverse_iterator(*this, _tdomain->_tslices.rend()); } + + struct const_iterator : public base_container::const_iterator + { + using iterator_category = typename base_container::const_iterator::iterator_category; + using difference_type = typename base_container::const_iterator::difference_type; + + using value_type = Slice; + using pointer = const Slice*; + using reference = const Slice&; + + public: + + const_iterator(const Tube& x, base_container::const_iterator it) : + base_container::const_iterator(it), _x(x) { } + + reference operator*() const + { + return static_cast(*((*this)->_slices.at(&_x))); + } + + protected: + + const Tube& _x; + }; + + const_iterator begin() const { return const_iterator(*this, _tdomain->_tslices.cbegin()); } + const_iterator end() const { return const_iterator(*this, _tdomain->_tslices.cend()); } + }; + + codac::Tube to_codac1(const codac2::Tube& x); + codac::TubeVector to_codac1(const codac2::Tube& x); + codac::TubeVector to_codac1_poly(const codac2::Tube& x); + codac2::Tube to_codac2(const codac::Tube& x); + codac2::Tube to_codac2(const codac::TubeVector& x); + codac2::Tube to_codac2_poly(const codac::TubeVector& x); + + + #include "codac2_TubeEvaluation.h" + #include "codac2_TubeComponent.h" + +} // namespace codac + +#endif \ No newline at end of file diff --git a/src/core/2/domains/tube/codac2_TubeComponent.h b/src/core/2/domains/tube/codac2_TubeComponent.h new file mode 100644 index 00000000..895d892b --- /dev/null +++ b/src/core/2/domains/tube/codac2_TubeComponent.h @@ -0,0 +1,121 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2022 + * \author Simon Rohou + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifndef __CODAC2_TUBECOMPONENT_H__ +#define __CODAC2_TUBECOMPONENT_H__ + +template +class TubeComponent //: public AbstractConstTube> +{ + protected: + + TubeComponent(Tube& tubevector, size_t i) : + _i(i), _tubevector(tubevector) + { + assert(i >= 0 && i < tubevector.size()); + } + + public: + + TubeComponent(const TubeComponent& tubevector_i) : + _i(tubevector_i._i), _tubevector(tubevector_i._tubevector) + { + + } + + size_t size() const + { + return 1; + } + + const std::shared_ptr& tdomain() const + { + return _tubevector.tdomain(); + } + + Interval t0_tf() const + { + return _tubevector.t0_tf(); + } + + Interval codomain() const + { + Interval codomain(Interval::EMPTY_SET); + for(const auto& s : _tubevector) + codomain |= s.codomain()[_i]; + return codomain; + } + + bool contains(const Trajectory& value) const + { + assert(false); + return true; + } + + void set(const Interval& codomain) + { + for(auto& s : _tubevector) + s.set_component(_i, codomain); + } + + const TubeComponent& operator=(const TubeComponent& x) + { + assert(x.tdomain() == tdomain()); + for(auto& s : _tubevector) + s.set_component(_i, std::static_pointer_cast>(s._it_tslice->_slices.at(&x._tubevector))->codomain()[x._i]); + return *this; + } + + const TubeComponent& operator=(std::pair,const TubeComponent> rel) + { + assert(rel.second.tdomain() == tdomain()); + for(auto& s : _tubevector) + s.set_component(_i, rel.first(std::static_pointer_cast>(s._it_tslice->_slices.at(&rel.second._tubevector))->codomain()[rel.second._i])); + return *this; + } + + friend std::ostream& operator<<(std::ostream& os, const TubeComponent& x) + { + os << "Component " << x._i << " of: " << x._tubevector << std::flush; + return os; + } + + std::pair,const TubeComponent> cos(const TubeComponent& x) + { + return std::make_pair(static_cast(ibex::cos), x); + } + + codac::Tube to_codac1() const + { + codac::Tube x(t0_tf()); + for(const auto& s : _tubevector) + if(!s.t0_tf().is_unbounded()) + x.set(s.codomain()[_i], s.t0_tf()); + for(const auto& s : _tubevector) // setting gate (were overwritten) + if(s.t0_tf().is_degenerated()) + x.set(s.codomain()[_i], s.t0_tf()); + return x; + } + + + protected: + + size_t _i; + Tube& _tubevector; + + template + friend class Tube; +}; + +template +std::pair,const TubeComponent> cos(const TubeComponent& x); + +#endif \ No newline at end of file diff --git a/src/core/2/domains/tube/codac2_TubeEvaluation.h b/src/core/2/domains/tube/codac2_TubeEvaluation.h new file mode 100644 index 00000000..eafc7453 --- /dev/null +++ b/src/core/2/domains/tube/codac2_TubeEvaluation.h @@ -0,0 +1,116 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2022 + * \author Simon Rohou + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifndef __CODAC2_TUBEEVALUATION_H__ +#define __CODAC2_TUBEEVALUATION_H__ + +template +class TubeEvaluation +{ + public: + + TubeEvaluation& operator=(const T& x) + { + // Sampling the tube only if affectation is performed + // (i.e. this is not done in the constructor) + std::list::iterator it_lb = _tube->tdomain()->sample(_t.lb(), _t.is_degenerated()); + std::list::iterator it_ub; + + if(!_t.is_degenerated()) + { + it_ub = _tube->tdomain()->sample(_t.ub(), false); + it_ub--; // pointing to the tslice [..,_t.ub()] + + if(it_lb->t0_tf().ub() == _t.lb()) + it_lb++; + } + + else + it_ub = it_lb; + + do + { + _tube->operator()(it_lb).set(x); + } while(it_lb != it_ub && (++it_lb) != _tube->tdomain()->tslices().end()); + + return *this; + } + + explicit operator T() const + { + return _tube->eval(_t); + } + + friend std::ostream& operator<<(std::ostream& os, const TubeEvaluation& x) + { + os << x._tube->eval(x._t) << std::flush; + return os; + } + + + protected: + + explicit TubeEvaluation(Tube *tubevector, double t) : + _t(Interval(t)), _tube(tubevector) + { + + } + + explicit TubeEvaluation(Tube *tubevector, const Interval& t) : + _t(t), _tube(tubevector) + { + + } + + const Interval _t; + Tube* _tube; + template + friend class Tube; +}; + +template +class ConstTubeEvaluation +{ + public: + + explicit operator T() const + { + return _tube->eval(_t); + } + + friend std::ostream& operator<<(std::ostream& os, const ConstTubeEvaluation& x) + { + os << x._tube->eval(x._t) << std::flush; + return os; + } + + + protected: + + explicit ConstTubeEvaluation(const Tube *tubevector, double t) : + _t(Interval(t)), _tube(tubevector) + { + + } + + explicit ConstTubeEvaluation(const Tube *tubevector, const Interval& t) : + _t(t), _tube(tubevector) + { + + } + + const Interval _t; + const Tube* _tube; + template + friend class Tube; +}; + +#endif \ No newline at end of file diff --git a/src/core/2/integration/codac2_AbstractDomain.cpp b/src/core/2/integration/codac2_AbstractDomain.cpp new file mode 100644 index 00000000..cd6c9214 --- /dev/null +++ b/src/core/2/integration/codac2_AbstractDomain.cpp @@ -0,0 +1,27 @@ +/** + * AbstractDomain class + * ---------------------------------------------------------------------------- + * \date 2022 + * \author Damien Massé + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#include "codac2_AbstractDomain.h" + +using namespace std; +using namespace codac; + +namespace codac2 +{ + AbstractDomain::AbstractDomain() + { + + } + + IntervalVector AbstractDomain::box() const + { + return IntervalVector(0); // todo + } +} \ No newline at end of file diff --git a/src/core/2/integration/codac2_AbstractDomain.h b/src/core/2/integration/codac2_AbstractDomain.h new file mode 100644 index 00000000..550dfcb5 --- /dev/null +++ b/src/core/2/integration/codac2_AbstractDomain.h @@ -0,0 +1,35 @@ +/** + * \file + * AbstractDomain class + * ---------------------------------------------------------------------------- + * \date 2022 + * \author Damien Massé + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifndef __CODAC2_ABSTRACTDOMAIN_H__ +#define __CODAC2_ABSTRACTDOMAIN_H__ + +#include "codac_IntervalVector.h" + +namespace codac2 +{ + /** + * \class AbstractDomain + * \brief ... + */ + class AbstractDomain + { + public: + + AbstractDomain(); + codac::IntervalVector box() const; + + protected: + + }; +} + +#endif \ No newline at end of file diff --git a/src/core/2/variables/codac2_Matrix.h b/src/core/2/variables/codac2_Matrix.h new file mode 100644 index 00000000..b3e8fa03 --- /dev/null +++ b/src/core/2/variables/codac2_Matrix.h @@ -0,0 +1,267 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2023 + * \author Simon Rohou + * \copyright Copyright 2023 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifndef __CODAC2_MATRIX_H__ +#define __CODAC2_MATRIX_H__ + +#include +#include + +namespace codac2 +{ + using Eigen::Dynamic; + + template + class Matrix_ : public Eigen::Matrix + { + public: + + Matrix_() + : Eigen::Matrix() + { } + + Matrix_(int nb_rows, int nb_cols) + : Eigen::Matrix(nb_rows, nb_cols) + { + assert(R == Dynamic || R == (int)nb_rows); + assert(C == Dynamic || C == (int)nb_cols); + } + + Matrix_(int nb_rows, int nb_cols, double x) + : Eigen::Matrix(nb_rows, nb_cols) + { + assert(R == Dynamic || R == (int)nb_rows); + assert(C == Dynamic || C == (int)nb_cols); + init(x); + } + + explicit Matrix_(int nb_rows, int nb_cols, const double values[]) + : Matrix_(nb_rows, nb_cols) + { + int k = 0; + for(int i = 0 ; i < nb_rows ; i++) + for(int j = 0 ; j < nb_cols ; j++) + { + if(values == 0) // in case the user called Matrix_(r,c,0) and 0 is interpreted as NULL! + (*this)(i,j) = 0.; + else + (*this)(i,j) = values[k]; + k++; + } + } + + explicit Matrix_(const double values[]) + : Matrix_(R, C, values) + { } + + Matrix_(std::initializer_list> l) + : Matrix_() + { + assert((R == Dynamic || (int)l.size() == R) && "ill-formed matrix"); + int cols = -1; + for(const auto& ri : l) { + assert(cols == -1 || cols == (int)ri.size() && "ill-formed matrix"); + cols = (int)ri.size(); + } + this->resize(l.size(),cols); + size_t i = 0; + for(const auto& ri : l) + { + size_t j = 0; + for(const auto& ci : ri) + (*this)(i,j++) = ci; + i++; + } + // todo: use thias as faster? std::copy(l.begin(), l.end(), vec); + } + + template + Matrix_(const Eigen::MatrixBase& other) + : Eigen::Matrix(other) + { } + + // This method allows you to assign Eigen expressions to Matrix_ + template + Matrix_& operator=(const Eigen::MatrixBase& other) + { + this->Eigen::Matrix::operator=(other); + return *this; + } + + void init(double x) + { + for(size_t i = 0 ; i < this->size() ; i++) + *(this->data()+i) = x; + } + + static Matrix_ eye() + { + return Eigen::Matrix::Identity(); + } + + double min() const + { + return Eigen::Matrix::minCoeff(); + } + + double max() const + { + return Eigen::Matrix::maxCoeff(); + } + + auto operator+(const Matrix_& x) const + { + auto y = *this; + return y += x; + } + + auto operator-(const Matrix_& x) const + { + auto y = *this; + return y -= x; + } + + auto operator-() const + { + return Eigen::Matrix::operator-(); + } + + auto operator&(const Matrix_& x) const + { + auto y = *this; + return y &= x; + } + + auto operator|(const Matrix_& x) const + { + auto y = *this; + return y |= x; + } + + auto& operator+=(const Matrix_& x) + { + (*this).noalias() += x;//.template cast(); + return *this; + } + + auto& operator-=(const Matrix_& x) + { + (*this).noalias() -= x;//.template cast(); + return *this; + } + + static Matrix_ zeros() + { + return Eigen::Matrix::Zero(); + } + + static Matrix_ ones() + { + return Eigen::Matrix::Ones(); + } + }; + + template + std::ostream& operator<<(std::ostream& os, const Matrix_& x) + { + os << "("; + for(size_t i = 0 ; i < x.rows() ; i++) + { + os << "("; + for(size_t j = 0 ; j < x.cols() ; j++) + os << x(i,j) << (j + ibex::Matrix to_codac1(const Matrix_& x) + { + ibex::Matrix x_(x.rows(), x.cols()); + for(size_t i = 0 ; i < x.rows() ; i++) + for(size_t j = 0 ; j < x.cols() ; j++) + x_[i][j] = x(i,j); + return x_; + } + + template + Matrix_ floor(const Matrix_& x) + { + Matrix_ f(x.rows(), x.cols()); + for(size_t i = 0 ; i < x.size() ; i++) + *(f.data()+i) = std::floor(*(x.data()+i)); + return f; + } + + template + Matrix_ round(const Matrix_& x) + { + Matrix_ f(x.rows(), x.cols()); + for(size_t i = 0 ; i < x.size() ; i++) + *(f.data()+i) = std::round(*(x.data()+i)); + return f; + } + + template + Matrix_ ceil(const Matrix_& x) + { + Matrix_ f(x.rows(), x.cols()); + for(size_t i = 0 ; i < x.size() ; i++) + *(f.data()+i) = std::ceil(*(x.data()+i)); + return f; + } + + template + Matrix_ abs(const Matrix_& x) + { + Matrix_ f(x.rows(), x.cols()); + for(size_t i = 0 ; i < x.size() ; i++) + *(f.data()+i) = std::fabs(*(x.data()+i)); + return f; + } + + class Matrix : public Matrix_<> + { + public: + + explicit Matrix(size_t nb_rows, size_t nb_cols) + : Matrix_<>(nb_rows, nb_cols) + { } + + + explicit Matrix(size_t nb_rows, size_t nb_cols, double x) + : Matrix_<>(nb_rows, nb_cols, x) + { } + + explicit Matrix(size_t nb_rows, size_t nb_cols, const double values[]) + : Matrix_<>(nb_rows, nb_cols, values) + { } + + Matrix(const Matrix_<>& x) + : Matrix_<>(x) + { } + + Matrix(std::initializer_list> l) + : Matrix_<>(l) + { } + + template + explicit Matrix(const Matrix_& v) + : Matrix_<>(v) + { } + }; +} // namespace codac + +#endif \ No newline at end of file diff --git a/src/core/2/variables/codac2_Vector.h b/src/core/2/variables/codac2_Vector.h new file mode 100644 index 00000000..7c30cd75 --- /dev/null +++ b/src/core/2/variables/codac2_Vector.h @@ -0,0 +1,172 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2023 + * \author Simon Rohou + * \copyright Copyright 2023 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifndef __CODAC2_VECTOR_H__ +#define __CODAC2_VECTOR_H__ + +#include +#include +#include + +namespace codac2 +{ + using Eigen::Dynamic; + + template + class Vector_ : public Matrix_ + { + public: + + Vector_() + { } + + Vector_(size_t n) + : Matrix_(n,1) + { + assert(N == Dynamic || N == (int)n); + } + + Vector_(size_t n, double x) + : Matrix_(n,1,x) + { + assert(N == Dynamic || N == (int)n); + } + + Vector_(std::initializer_list l) : Matrix_(l.size(),1) + { + assert(N == (int)l.size() || N == -1); + size_t i = 0; + for(double li : l) + (*this)(i++,0) = li; + } + + template + Vector_(const Matrix_& x) + : Matrix_(x) + { + static_assert(M == Dynamic || M == N); + } + + explicit Vector_(size_t n, const double values[]) + : Matrix_(n,1,values) + { } + + explicit Vector_(const double values[]) + : Matrix_(N,1,values) + { } + + template + explicit Vector_(const std::array& array) + : Matrix_(N,1) + { + static_assert(N == Dynamic || N == (int)M); + for(size_t i = 0 ; i < M ; i++) + *(this->data()+i) = array[i]; + } + + template + Vector_(const Eigen::MatrixBase& other) + : Matrix_(other) + { } + + // This method allows you to assign Eigen expressions to Vector_ + template + Vector_& operator=(const Eigen::MatrixBase& other) + { + this->Eigen::Matrix::operator=(other); + return *this; + } + + Matrix_ as_diag() const + { + return Matrix_(Eigen::Matrix(this->asDiagonal())); + } + + Matrix_<1,N> transpose() const + { + return Matrix_<1,N>(Eigen::Matrix::transpose()); + } + + // todo: place this in common inheritance with IntervalVector_ + template + Vector_ subvector() const + { + assert(N1 >= 0 && N1 < N && N2 >= 0 && N2 < N && N1 <= N2); + return this->template block(N1,0); + } + }; + + template + std::ostream& operator<<(std::ostream& os, const Vector_& x) + { + os << "("; + for(size_t i = 0 ; i < x.size() ; i++) + os << x[i] << (i + Matrix_ diag(const Vector_ v) + { + return v.as_diag(); + } + + template + codac::Vector to_codac1(const Vector_& x) + { + ibex::Vector x_(x.size()); + for(size_t i = 0 ; i < x.size() ; i++) + x_[i] = x[i]; + return x_; + } + + template + Vector_ to_codac2(const codac::Vector& x) + { + assert(x.size() == N); + Vector_ x_; + for(size_t i = 0 ; i < N ; i++) + x_[i] = x[i]; + return x_; + } + + class Vector : public Vector_<> + { + public: + + explicit Vector(int n) + : Vector_<>(n) + { } + + Vector(const Vector& x) + : Vector_<>(x) + { } + + explicit Vector(std::initializer_list l) + : Vector_<>(l) + { } + + template + Vector(const Vector_& v) + : Vector_<>(v) + { } + + template + Vector(const Matrix_& v) + : Vector_<>(v) + { } + + }; + +} // namespace codac + +#endif \ No newline at end of file diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 7fe51f4e..2717733c 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -2,7 +2,8 @@ # Codac - cmake configuration file # ================================================================== - list(APPEND SRC ${CMAKE_CURRENT_SOURCE_DIR}/functions/codac_Function.h + list(APPEND CODAC1_SRC + ${CMAKE_CURRENT_SOURCE_DIR}/functions/codac_Function.h ${CMAKE_CURRENT_SOURCE_DIR}/functions/codac_TFnc.h ${CMAKE_CURRENT_SOURCE_DIR}/functions/codac_TFnc.cpp ${CMAKE_CURRENT_SOURCE_DIR}/functions/codac_TFunction.h @@ -167,14 +168,56 @@ ${CMAKE_CURRENT_SOURCE_DIR}/separators/codac_SepFixPoint.cpp ${CMAKE_CURRENT_SOURCE_DIR}/separators/codac_SepProj.h ${CMAKE_CURRENT_SOURCE_DIR}/separators/codac_SepProj.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/separators/codac_CtcTransform.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/separators/codac_CtcTransform.h + ${CMAKE_CURRENT_SOURCE_DIR}/separators/codac_SepTransform.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/separators/codac_SepTransform.h ${CMAKE_CURRENT_SOURCE_DIR}/tools/codac_Tools.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tools/codac_Tools.h ${CMAKE_CURRENT_SOURCE_DIR}/tools/codac_Eigen.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tools/codac_Eigen.h ${CMAKE_CURRENT_SOURCE_DIR}/sivia/codac_sivia.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/sivia/codac_sivia.h + ${CMAKE_CURRENT_SOURCE_DIR}/sivia/codac_sivia.h) + + list(APPEND CODAC2_SRC # Files related to codac2 + ${CMAKE_CURRENT_SOURCE_DIR}/2/3rd/codac2_eigen.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/interval/codac2_Interval.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/interval/codac2_IntervalMatrix.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/interval/codac2_IntervalVector.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/interval/codac2_cart_prod.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/interval/codac2_cart_prod.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_AbstractConstTube.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_AbstractSlice.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_AbstractSlice.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_AbstractSlicedTube.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_AbstractSlicedTube.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_Slice.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_TDomain.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_TDomain.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_TSlice.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_TSlice.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_Tube.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_Tube.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_TubeComponent.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_TubeEvaluation.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/paving/codac2_Paving.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/contractors/codac2_CtcDiffInclusion.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/2/contractors/codac2_CtcDiffInclusion.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/contractors/codac2_CtcLinobs.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/2/contractors/codac2_CtcLinobs.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/integration/codac2_AbstractDomain.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/2/integration/codac2_AbstractDomain.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/variables/codac2_Matrix.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/variables/codac2_Vector.h + # Actions + ${CMAKE_CURRENT_SOURCE_DIR}/2/actions/codac2_Action.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/2/actions/codac2_Action.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/contractors/codac2_CtcAction.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/2/contractors/codac2_CtcAction.h ) + set(SRC ${CODAC1_SRC} ${CODAC2_SRC}) + ################################################################################ # Create the target for libcodac @@ -200,7 +243,15 @@ ${CMAKE_CURRENT_SOURCE_DIR}/contractors/dyn ${CMAKE_CURRENT_SOURCE_DIR}/cn ${CMAKE_CURRENT_SOURCE_DIR}/tools - ${CMAKE_CURRENT_SOURCE_DIR}/sivia) + ${CMAKE_CURRENT_SOURCE_DIR}/sivia + ${CMAKE_CURRENT_SOURCE_DIR}/2/3rd + ${CMAKE_CURRENT_SOURCE_DIR}/2/contractors + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/interval + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/paving + ${CMAKE_CURRENT_SOURCE_DIR}/2/integration/ + ${CMAKE_CURRENT_SOURCE_DIR}/2/actions/ + ${CMAKE_CURRENT_SOURCE_DIR}/2/variables/) target_link_libraries(codac PUBLIC Ibex::ibex) @@ -210,9 +261,9 @@ # Getting header files from sources - foreach(srcfile ${SRC}) + foreach(srcfile ${CODAC1_SRC}) if(srcfile MATCHES "\\.h$" OR srcfile MATCHES "\\.hpp$") - list(APPEND HDR ${srcfile}) + list(APPEND CODAC_HDR ${srcfile}) # Copying header files for other Codac libraries compiled in the same time file(COPY ${srcfile} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/../../include) endif() @@ -224,15 +275,39 @@ file(WRITE ${CODAC_MAIN_HEADER} "/* This file is generated by CMake */\n\n") file(APPEND ${CODAC_MAIN_HEADER} "#ifndef __CODAC_H__\n#define __CODAC_H__\n\n") file(APPEND ${CODAC_MAIN_HEADER} "\n#include \n") # todo: clean this organization - foreach(header_path ${HDR}) + foreach(header_path ${CODAC_HDR}) get_filename_component(header_name ${header_path} NAME) file(APPEND ${CODAC_MAIN_HEADER} "#include <${header_name}>\n") endforeach() file(APPEND ${CODAC_MAIN_HEADER} "\n#endif /* __CODAC_H__ */\n") file(COPY ${CODAC_MAIN_HEADER} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/../../include) + +# Getting header files from sources + + foreach(srcfile ${CODAC2_SRC}) + if(srcfile MATCHES "\\.h$" OR srcfile MATCHES "\\.hpp$") + list(APPEND CODAC2_HDR ${srcfile}) + # Copying header files for other Codac libraries compiled in the same time + file(COPY ${srcfile} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/../../include) + endif() + endforeach() + +# Generating a codac.h file + + set(CODAC2_MAIN_HEADER ${CMAKE_CURRENT_BINARY_DIR}/codac2.h) + file(WRITE ${CODAC2_MAIN_HEADER} "/* This file is generated by CMake */\n\n") + file(APPEND ${CODAC2_MAIN_HEADER} "#ifndef __CODAC2_H__\n#define __CODAC2_H__\n\n") + foreach(header_path ${CODAC2_HDR}) + get_filename_component(header_name ${header_path} NAME) + file(APPEND ${CODAC2_MAIN_HEADER} "#include <${header_name}>\n") + endforeach() + file(APPEND ${CODAC2_MAIN_HEADER} "\n#endif /* __CODAC_H__ */\n") + file(COPY ${CODAC2_MAIN_HEADER} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/../../include) # Install files in system directories install(TARGETS codac DESTINATION ${CMAKE_INSTALL_LIBDIR}) - install(FILES ${HDR} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/codac) - install(FILES ${CODAC_MAIN_HEADER} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/codac) \ No newline at end of file + install(FILES ${CODAC_HDR} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/codac) + install(FILES ${CODAC_MAIN_HEADER} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/codac) + install(FILES ${CODAC2_HDR} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/codac) + install(FILES ${CODAC2_MAIN_HEADER} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/codac) \ No newline at end of file diff --git a/src/core/arithmetic/codac_polygon_arithmetic.cpp b/src/core/arithmetic/codac_polygon_arithmetic.cpp index b3ae3692..cb7394de 100644 --- a/src/core/arithmetic/codac_polygon_arithmetic.cpp +++ b/src/core/arithmetic/codac_polygon_arithmetic.cpp @@ -67,7 +67,7 @@ namespace codac return ConvexPolygon(v_result_thick_pts); } - const ConvexPolygon operator&(const ConvexPolygon& p1, const ConvexPolygon& p2) + vector inter_thickpoints(const ConvexPolygon& p1, const ConvexPolygon& p2) { vector v_pts; @@ -75,7 +75,7 @@ namespace codac for(const auto& pt_ : p1.vertices()) { ThickPoint pt(pt_); - if(p2.encloses(pt) != NO) + if(p2.contains(pt) != NO) v_pts.push_back(pt); } @@ -83,7 +83,7 @@ namespace codac for(const auto& pt_ : p2.vertices()) { ThickPoint pt(pt_); - if(p1.encloses(pt) != NO) + if(p1.contains(pt) != NO) v_pts.push_back(pt); } @@ -116,7 +116,12 @@ namespace codac } } - return ConvexPolygon(v_pts); + return v_pts; + } + + const ConvexPolygon operator&(const ConvexPolygon& p1, const ConvexPolygon& p2) + { + return ConvexPolygon(inter_thickpoints(p1,p2)); } const ConvexPolygon operator&(const IntervalVector& p1, const ConvexPolygon& p2) @@ -130,4 +135,26 @@ namespace codac assert(p2.size() == 2 && "other dimensions not supported"); return operator&(p1, ConvexPolygon(p2)); } + + const ConvexPolygon operator|(const ConvexPolygon& p1, const ConvexPolygon& p2) + { + vector v_pts; + for(const auto& pt_ : p1.vertices()) + v_pts.push_back(ThickPoint(pt_)); + for(const auto& pt_ : p2.vertices()) + v_pts.push_back(ThickPoint(pt_)); + return ConvexPolygon(v_pts); + } + + const ConvexPolygon operator|(const IntervalVector& p1, const ConvexPolygon& p2) + { + assert(p1.size() == 2 && "other dimensions not supported"); + return operator|(p2, ConvexPolygon(p1)); + } + + const ConvexPolygon operator|(const ConvexPolygon& p1, const IntervalVector& p2) + { + assert(p2.size() == 2 && "other dimensions not supported"); + return operator|(p1, ConvexPolygon(p2)); + } } \ No newline at end of file diff --git a/src/core/arithmetic/codac_polygon_arithmetic.h b/src/core/arithmetic/codac_polygon_arithmetic.h index 09c2325b..fd1f2bdb 100644 --- a/src/core/arithmetic/codac_polygon_arithmetic.h +++ b/src/core/arithmetic/codac_polygon_arithmetic.h @@ -28,9 +28,14 @@ namespace codac const ConvexPolygon operator*(const IntervalMatrix& m, const ConvexPolygon& x); + std::vector inter_thickpoints(const ConvexPolygon& p1, const ConvexPolygon& p2); const ConvexPolygon operator&(const ConvexPolygon& p1, const ConvexPolygon& p2); const ConvexPolygon operator&(const IntervalVector& p1, const ConvexPolygon& p2); const ConvexPolygon operator&(const ConvexPolygon& p1, const IntervalVector& p2); + + const ConvexPolygon operator|(const ConvexPolygon& p1, const ConvexPolygon& p2); + const ConvexPolygon operator|(const IntervalVector& p1, const ConvexPolygon& p2); + const ConvexPolygon operator|(const ConvexPolygon& p1, const IntervalVector& p2); } #endif \ No newline at end of file diff --git a/src/core/arithmetic/codac_predef_values.h b/src/core/arithmetic/codac_predef_values.h index e5c294dc..41de0c03 100644 --- a/src/core/arithmetic/codac_predef_values.h +++ b/src/core/arithmetic/codac_predef_values.h @@ -19,4 +19,6 @@ namespace codac const double oo = POS_INFINITY; } +using codac::oo; + #endif \ No newline at end of file diff --git a/src/core/cn/codac_Variable.h b/src/core/cn/codac_Variable.h index 386c2efd..36af7641 100644 --- a/src/core/cn/codac_Variable.h +++ b/src/core/cn/codac_Variable.h @@ -62,7 +62,7 @@ namespace codac IntervalVectorVar(int n) : IntervalVector(n) { - + } /** diff --git a/src/core/contractors/dyn/codac_CtcChain.cpp b/src/core/contractors/dyn/codac_CtcChain.cpp index 3cbbc371..89dd63fb 100755 --- a/src/core/contractors/dyn/codac_CtcChain.cpp +++ b/src/core/contractors/dyn/codac_CtcChain.cpp @@ -45,4 +45,24 @@ namespace codac { CtcLinobs::contract(x, v, a, t_propa); } + + void CtcChain::contract(codac2::Tube& x, const codac2::Tube& a, TimePropag t_propa) + { + assert(x.size() == 2 && codac2::TDomain::are_same(x.tdomain(),a.tdomain())); + codac::TubeVector _x = to_codac1(x); + codac::Tube _a = to_codac1(a); + contract(_x[0], _x[1], _a, t_propa); + x &= codac2::to_codac2(_x); + } + + void CtcChain::contract(codac2::Tube& x, codac2::Tube& v, const codac2::Tube& a, TimePropag t_propa) + { + assert(codac2::TDomain::are_same(x.tdomain(),v.tdomain()) && codac2::TDomain::are_same(x.tdomain(),a.tdomain())); + codac::Tube _x = to_codac1(x); + codac::Tube _v = to_codac1(v); + codac::Tube _a = to_codac1(a); + contract(_x, _v, _a, t_propa); + x &= codac2::to_codac2(_x); + v &= codac2::to_codac2(_v); + } } \ No newline at end of file diff --git a/src/core/contractors/dyn/codac_CtcChain.h b/src/core/contractors/dyn/codac_CtcChain.h index f958937a..ae5ae66e 100755 --- a/src/core/contractors/dyn/codac_CtcChain.h +++ b/src/core/contractors/dyn/codac_CtcChain.h @@ -13,6 +13,7 @@ #define __CODAC_CTCCHAIN_H__ #include "codac_CtcLinobs.h" +#include "codac2_Tube.h" namespace codac { @@ -30,6 +31,8 @@ namespace codac void contract(std::vector& v_domains); void contract(Tube& x, Tube& v, const Tube& a, TimePropag t_propa = TimePropag::FORWARD | TimePropag::BACKWARD); + void contract(codac2::Tube& x, const codac2::Tube& a, TimePropag t_propa = TimePropag::FORWARD | TimePropag::BACKWARD); + void contract(codac2::Tube& x, codac2::Tube& v, const codac2::Tube& a, TimePropag t_propa = TimePropag::FORWARD | TimePropag::BACKWARD); protected: diff --git a/src/core/contractors/dyn/codac_CtcDeriv.cpp b/src/core/contractors/dyn/codac_CtcDeriv.cpp index 5970c1e5..15c2e304 100644 --- a/src/core/contractors/dyn/codac_CtcDeriv.cpp +++ b/src/core/contractors/dyn/codac_CtcDeriv.cpp @@ -116,7 +116,32 @@ namespace codac for(int i = 0 ; i < x.size() ; i++) contract(x[i], v[i], t_propa); } + + void CtcDeriv::contract(codac2::Tube& x, const codac2::Tube& v, TimePropag t_propa) + { + Tube _x = codac2::to_codac1(x); + Tube _v = codac2::to_codac1(v); + contract(_x,_v,t_propa); + x &= codac2::to_codac2(_x); + } + + void CtcDeriv::contract(codac2::Tube& xv, TimePropag t_propa) + { + Tube x = codac2::to_codac1(xv)[0]; + Tube v = codac2::to_codac1(xv)[1]; + contract(x,v,t_propa); + xv &= codac2::to_codac2(TubeVector({x,v})); + } + void CtcDeriv::contract(codac2::Tube& x, int i, codac2::Tube& v, int j, TimePropag t_propa) + { + TubeVector _x = codac2::to_codac1(x); + TubeVector _v = codac2::to_codac1(v); + contract(_x[i],_v[j],t_propa); + x &= codac2::to_codac2(_x); + v &= codac2::to_codac2(_v); + } + void CtcDeriv::contract(Slice& x, const Slice& v, TimePropag t_propa) { assert(x.tdomain() == v.tdomain()); diff --git a/src/core/contractors/dyn/codac_CtcDeriv.h b/src/core/contractors/dyn/codac_CtcDeriv.h index 356ab476..aaf53ccf 100644 --- a/src/core/contractors/dyn/codac_CtcDeriv.h +++ b/src/core/contractors/dyn/codac_CtcDeriv.h @@ -14,6 +14,7 @@ #include "codac_DynCtc.h" #include "codac_Slice.h" +#include "codac2_Tube.h" namespace codac { @@ -69,6 +70,9 @@ namespace codac * (forward or backward in time, both ways by default) */ void contract(TubeVector& x, const TubeVector& v, TimePropag t_propa = TimePropag::FORWARD | TimePropag::BACKWARD); + void contract(codac2::Tube& x, const codac2::Tube& v, TimePropag t_propa = TimePropag::FORWARD | TimePropag::BACKWARD); + void contract(codac2::Tube& xv, TimePropag t_propa = TimePropag::FORWARD | TimePropag::BACKWARD); + void contract(codac2::Tube& x, int i, codac2::Tube& v, int j, TimePropag t_propa = TimePropag::FORWARD | TimePropag::BACKWARD); /** * \brief \f$\mathcal{C}_{\frac{d}{dt}}\big(\llbracket x\rrbracket(\cdot),\llbracket v\rrbracket(\cdot)\big)\f$: diff --git a/src/core/contractors/dyn/codac_CtcLohner.cpp b/src/core/contractors/dyn/codac_CtcLohner.cpp index bedcec01..9888536b 100644 --- a/src/core/contractors/dyn/codac_CtcLohner.cpp +++ b/src/core/contractors/dyn/codac_CtcLohner.cpp @@ -12,7 +12,7 @@ #include #include -#include +#include #include #include diff --git a/src/core/contractors/dyn/codac_CtcPicard.h b/src/core/contractors/dyn/codac_CtcPicard.h index 4e2b0768..073f18c2 100644 --- a/src/core/contractors/dyn/codac_CtcPicard.h +++ b/src/core/contractors/dyn/codac_CtcPicard.h @@ -36,10 +36,11 @@ namespace codac int picard_iterations() const; + void guess_kth_slices_envelope(TubeVector& x, int k, TimePropag t_propa); + protected: void contract_kth_slices(TubeVector& x, int k, TimePropag t_propa); - void guess_kth_slices_envelope(TubeVector& x, int k, TimePropag t_propa); const TFunction* m_f_ptr = nullptr; const TFnc& m_f; diff --git a/src/core/contractors/dyn/codac_CtcStatic.cpp b/src/core/contractors/dyn/codac_CtcStatic.cpp index bd769390..b899b45c 100644 --- a/src/core/contractors/dyn/codac_CtcStatic.cpp +++ b/src/core/contractors/dyn/codac_CtcStatic.cpp @@ -54,7 +54,7 @@ namespace codac v_x_slices[i] = x[i].first_slice(); contract(v_x_slices, x.size()); - delete v_x_slices; + delete[] v_x_slices; } void CtcStatic::contract(Tube& x1) @@ -66,7 +66,7 @@ namespace codac v_x_slices[0] = x1.first_slice(); contract(v_x_slices, n); - delete v_x_slices; + delete[] v_x_slices; } void CtcStatic::contract(Tube& x1, Tube& x2) @@ -79,7 +79,7 @@ namespace codac v_x_slices[1] = x2.first_slice(); contract(v_x_slices, n); - delete v_x_slices; + delete[] v_x_slices; } void CtcStatic::contract(Tube& x1, Tube& x2, Tube& x3) @@ -93,7 +93,7 @@ namespace codac v_x_slices[2] = x3.first_slice(); contract(v_x_slices, n); - delete v_x_slices; + delete[] v_x_slices; } void CtcStatic::contract(Tube& x1, Tube& x2, Tube& x3, Tube& x4) @@ -108,7 +108,7 @@ namespace codac v_x_slices[3] = x4.first_slice(); contract(v_x_slices, n); - delete v_x_slices; + delete[] v_x_slices; } void CtcStatic::contract(Tube& x1, Tube& x2, Tube& x3, Tube& x4, Tube& x5) @@ -124,7 +124,7 @@ namespace codac v_x_slices[4] = x5.first_slice(); contract(v_x_slices, n); - delete v_x_slices; + delete[] v_x_slices; } void CtcStatic::contract(Tube& x1, Tube& x2, Tube& x3, Tube& x4, Tube& x5, Tube& x6) @@ -141,7 +141,7 @@ namespace codac v_x_slices[5] = x6.first_slice(); contract(v_x_slices, n); - delete v_x_slices; + delete[] v_x_slices; } void CtcStatic::contract(Slice **v_x_slices, int n) diff --git a/src/core/contractors/static/codac_CtcCartProd.cpp b/src/core/contractors/static/codac_CtcCartProd.cpp index a821791d..14b48bfd 100644 --- a/src/core/contractors/static/codac_CtcCartProd.cpp +++ b/src/core/contractors/static/codac_CtcCartProd.cpp @@ -27,6 +27,11 @@ namespace codac index += m_v[i].nb_var; } } + + CtcCartProd cart_prod(Ctc& c1, Ctc& c2) + { + return CtcCartProd(c1, c2); + } CtcCartProd cart_prod(const ibex::Array& array) { diff --git a/src/core/contractors/static/codac_CtcCartProd.h b/src/core/contractors/static/codac_CtcCartProd.h index 85e0d40e..15081e20 100644 --- a/src/core/contractors/static/codac_CtcCartProd.h +++ b/src/core/contractors/static/codac_CtcCartProd.h @@ -54,17 +54,13 @@ namespace codac }; /** - * \fn template CtcCartProd cart_prod(Args &...args) - * \brief Cartesian product of contractors + * \brief Cartesian product of contractors from two Ctc objects * - * \param args list of contractors + * \param c1 first Ctc contractor + * \param c2 second Ctc contractor * \return the Cartesian product of the contractors \f$\mathcal{C}_1\times\dots\times\mathcal{C}_n\f$ */ - template - CtcCartProd cart_prod(Args&...args) - { - return CtcCartProd(args...); - } + CtcCartProd cart_prod(Ctc& c1, Ctc& c2); /** * \brief Cartesian product of contractors from an ibex::Array diff --git a/src/core/contractors/static/codac_CtcFunction.cpp b/src/core/contractors/static/codac_CtcFunction.cpp index f1adde21..93c880dc 100644 --- a/src/core/contractors/static/codac_CtcFunction.cpp +++ b/src/core/contractors/static/codac_CtcFunction.cpp @@ -54,7 +54,7 @@ namespace codac v_x_slices[i] = x[i].first_slice(); contract(v_x_slices); - delete v_x_slices; + delete[] v_x_slices; } void CtcFunction::contract(Tube& x1) @@ -65,7 +65,7 @@ namespace codac v_x_slices[0] = x1.first_slice(); contract(v_x_slices); - delete v_x_slices; + delete[] v_x_slices; } void CtcFunction::contract(Tube& x1, Tube& x2) @@ -77,7 +77,7 @@ namespace codac v_x_slices[1] = x2.first_slice(); contract(v_x_slices); - delete v_x_slices; + delete[] v_x_slices; } void CtcFunction::contract(Tube& x1, Tube& x2, Tube& x3) @@ -90,7 +90,7 @@ namespace codac v_x_slices[2] = x3.first_slice(); contract(v_x_slices); - delete v_x_slices; + delete[] v_x_slices; } void CtcFunction::contract(Tube& x1, Tube& x2, Tube& x3, Tube& x4) @@ -104,7 +104,7 @@ namespace codac v_x_slices[3] = x4.first_slice(); contract(v_x_slices); - delete v_x_slices; + delete[] v_x_slices; } void CtcFunction::contract(Tube& x1, Tube& x2, Tube& x3, Tube& x4, Tube& x5) @@ -119,7 +119,7 @@ namespace codac v_x_slices[4] = x5.first_slice(); contract(v_x_slices); - delete v_x_slices; + delete[] v_x_slices; } void CtcFunction::contract(Tube& x1, Tube& x2, Tube& x3, Tube& x4, Tube& x5, Tube& x6) @@ -135,7 +135,7 @@ namespace codac v_x_slices[5] = x6.first_slice(); contract(v_x_slices); - delete v_x_slices; + delete[] v_x_slices; } void CtcFunction::contract(Tube& x1, Tube& x2, Tube& x3, Tube& x4, Tube& x5, Tube& x6, Tube& x7) @@ -152,7 +152,7 @@ namespace codac v_x_slices[6] = x7.first_slice(); contract(v_x_slices); - delete v_x_slices; + delete[] v_x_slices; } void CtcFunction::contract(Tube& x1, Tube& x2, Tube& x3, Tube& x4, Tube& x5, Tube& x6, Tube& x7, Tube& x8) @@ -170,7 +170,7 @@ namespace codac v_x_slices[7] = x8.first_slice(); contract(v_x_slices); - delete v_x_slices; + delete[] v_x_slices; } void CtcFunction::contract(Tube& x1, Tube& x2, Tube& x3, Tube& x4, Tube& x5, Tube& x6, Tube& x7, Tube& x8, Tube& x9) @@ -189,7 +189,7 @@ namespace codac v_x_slices[8] = x9.first_slice(); contract(v_x_slices); - delete v_x_slices; + delete[] v_x_slices; } void CtcFunction::contract(Tube& x1, Tube& x2, Tube& x3, Tube& x4, Tube& x5, Tube& x6, Tube& x7, Tube& x8, Tube& x9, Tube& x10) @@ -209,7 +209,7 @@ namespace codac v_x_slices[9] = x10.first_slice(); contract(v_x_slices); - delete v_x_slices; + delete[] v_x_slices; } void CtcFunction::contract(Slice **v_x_slices) diff --git a/src/core/contractors/static/codac_CtcSegment.cpp b/src/core/contractors/static/codac_CtcSegment.cpp index 2a2000a6..2721281a 100644 --- a/src/core/contractors/static/codac_CtcSegment.cpp +++ b/src/core/contractors/static/codac_CtcSegment.cpp @@ -14,7 +14,8 @@ using namespace std; namespace codac { CtcSegment::CtcSegment(double ax, double ay, double bx, double by) : Ctc(2), - X_with_params(2+4) { + X_with_params(2+4) +{ init(); @@ -24,6 +25,11 @@ CtcSegment::CtcSegment(double ax, double ay, double bx, double by) : Ctc(2), X_with_params[5] = Interval(by); } +CtcSegment::CtcSegment(const CtcSegment& ctc) : CtcSegment(ctc.X_with_params[2].lb(),ctc.X_with_params[3].lb(),ctc.X_with_params[4].lb(),ctc.X_with_params[5].lb()) +{ + +} + CtcSegment::CtcSegment() : Ctc(6), X_with_params(0 /* unused */) { init(); } diff --git a/src/core/contractors/static/codac_CtcSegment.h b/src/core/contractors/static/codac_CtcSegment.h index 45659e47..5d6ea535 100644 --- a/src/core/contractors/static/codac_CtcSegment.h +++ b/src/core/contractors/static/codac_CtcSegment.h @@ -15,15 +15,15 @@ #include "ibex_CtcFwdBwd.h" +namespace codac { + + using ibex::Interval; using ibex::IntervalVector; using ibex::Ctc; using ibex::NumConstraint; using ibex::CtcFwdBwd; - -namespace codac { - /** * \ingroup geometry * @@ -58,6 +58,8 @@ class CtcSegment : public Ctc { */ CtcSegment(); + CtcSegment(const CtcSegment& ctc); + /** * \brief Contract a box. * \param box to be contracted diff --git a/src/core/contractors/static/codac_CtcUnion.h b/src/core/contractors/static/codac_CtcUnion.h index b9c73b41..336162db 100644 --- a/src/core/contractors/static/codac_CtcUnion.h +++ b/src/core/contractors/static/codac_CtcUnion.h @@ -2,9 +2,9 @@ * \file * CtcUnion class * ---------------------------------------------------------------------------- - * \date 2022 + * \date 2024 * \author Simon Rohou - * \copyright Copyright 2022 Codac Team + * \copyright Copyright 2024 Codac Team * \license This program is distributed under the terms of * the GNU Lesser General Public License (LGPL). */ @@ -12,11 +12,72 @@ #ifndef __CODAC_CTCUNION_H__ #define __CODAC_CTCUNION_H__ -#include "ibex_CtcUnion.h" +#include +//#include "ibex_CtcUnion.h" +#include "codac_Ctc.h" namespace codac { - using ibex::CtcUnion; + class CtcUnion : public Ctc + { + public: + + CtcUnion(int nb_var) : Ctc(nb_var) + { } + + template + CtcUnion(const C1& c1) : Ctc(c1.nb_var) + { + _v_ctc.push_back(std::make_shared(c1)); + } + + template + CtcUnion(const C1& c1, const C&... c) : CtcUnion(c1) + { + (_v_ctc.push_back(std::make_shared(c)), ...); + for(const auto& ci : _v_ctc) { assert(ci->nb_var == nb_var); } + } + + void contract(IntervalVector& x) + { + IntervalVector result(nb_var, Interval::empty_set()); + + for(auto& ci : _v_ctc) + { + IntervalVector saved_x = x; + ci->contract(saved_x); + result |= saved_x; + } + + for(auto& ci : _v_ctc_ptrs) + { + IntervalVector saved_x = x; + ci->contract(saved_x); + result |= saved_x; + } + + x = result; + } + + template + CtcUnion& operator|=(const C& c) + { + assert(c.nb_var == nb_var); + _v_ctc.push_back(std::make_shared(c)); + return *this; + } + + CtcUnion& add_raw_ptr(Ctc *c) + { + _v_ctc_ptrs.push_back(c); + return *this; + } + + protected: + + std::vector> _v_ctc; + std::vector _v_ctc_ptrs; + }; } #endif \ No newline at end of file diff --git a/src/core/domains/tube/codac_Tube.cpp b/src/core/domains/tube/codac_Tube.cpp index 725e91be..b897bb7d 100644 --- a/src/core/domains/tube/codac_Tube.cpp +++ b/src/core/domains/tube/codac_Tube.cpp @@ -1251,7 +1251,7 @@ namespace codac assert(tdomain().is_superset(t)); // The first slice is the slice containing t.lb() - while(!m_first_slice->tdomain().contains(t.lb())) + while(!m_first_slice->tdomain().contains(t.lb()) || (t & m_first_slice->tdomain()).is_degenerated()) { Slice *s_next = m_first_slice->next_slice(); delete m_first_slice; @@ -1262,7 +1262,7 @@ namespace codac // After this iteration, the last slice will be the one containing t.ub() Slice *s_last = last_slice(); - while(!s_last->tdomain().contains(t.ub())) + while(!s_last->tdomain().contains(t.ub()) || (t & s_last->tdomain()).is_degenerated()) { Slice *s_prev = s_last->prev_slice(); delete s_last; diff --git a/src/core/functions/codac_TFunction.cpp b/src/core/functions/codac_TFunction.cpp index 2d4efe75..e7c8ee6a 100644 --- a/src/core/functions/codac_TFunction.cpp +++ b/src/core/functions/codac_TFunction.cpp @@ -418,6 +418,11 @@ namespace codac return y; } + const IntervalVector TFunction::eval_vector(const IntervalVector& x1, const IntervalVector& x2) const + { + return eval_vector(cart_prod(x1,x2)); + } + const TrajectoryVector TFunction::traj_eval_vector(const TrajectoryVector& x) const { // Faster evaluation than the generic Fnc::eval method diff --git a/src/core/functions/codac_TFunction.h b/src/core/functions/codac_TFunction.h index 84d581e3..b768bef2 100644 --- a/src/core/functions/codac_TFunction.h +++ b/src/core/functions/codac_TFunction.h @@ -68,6 +68,17 @@ namespace codac const IntervalVector eval_vector(const IntervalVector& x) const; const IntervalVector eval_vector(int slice_id, const TubeVector& x) const; const IntervalVector eval_vector(const Interval& t, const TubeVector& x) const; + const IntervalVector eval_vector(const IntervalVector& x1, const IntervalVector& x2) const; + + template + const IntervalVector eval_vector(const IntervalVector& x1, const IntervalVector& x2, FirstArg& xi, Args&... xs) // recursive variadic function + { + IntervalVector x_ = cart_prod(x1,x2,xi); + if constexpr(sizeof...(xs) > 0) + return eval_vector(x_, xs...); + else + return eval_vector(x_); + } const TFunction diff() const; diff --git a/src/core/geometry/codac_ConvexPolygon.cpp b/src/core/geometry/codac_ConvexPolygon.cpp index 94cf8be1..17b59396 100644 --- a/src/core/geometry/codac_ConvexPolygon.cpp +++ b/src/core/geometry/codac_ConvexPolygon.cpp @@ -14,6 +14,7 @@ #include "codac_ConvexPolygon.h" #include "codac_GrahamScan.h" #include "codac_VIBesFig.h" +#include "codac_polygon_arithmetic.h" using namespace std; using namespace ibex; @@ -23,7 +24,7 @@ namespace codac // Definition ConvexPolygon::ConvexPolygon() - : Polygon() + : ConvexPolygon(IntervalVector(2,Interval(-9999.,9999.))) { } @@ -138,15 +139,33 @@ namespace codac for(const auto& pt : vertices()) { - is_subset = is_subset && p.encloses(ThickPoint(pt)); + is_subset = is_subset && p.contains(ThickPoint(pt)); if(is_subset == NO) return NO; } return is_subset; } + + bool ConvexPolygon::is_unbounded() const + { + return false; // todo + } const BoolInterval ConvexPolygon::encloses(const ThickPoint& p) const + { + cout << "encloses(): deprecated. Use contains() instead." << endl; + return contains(p); + } + + const BoolInterval ConvexPolygon::intersects(const ConvexPolygon& p) const + { + // todo: optimize this + ConvexPolygon inter = *this & p; + return inter.is_empty() ? BoolInterval::NO : BoolInterval::MAYBE; + } + + const BoolInterval ConvexPolygon::contains(const ThickPoint& p) const { if(p.does_not_exist() || is_empty()) return NO; @@ -236,6 +255,14 @@ namespace codac assert(!inter.is_unbounded()); assert(e1 != e2); assert(!e1.does_not_exist() && !e2.does_not_exist()); + if(inter.does_not_exist()) + { + cout << "inter.does_not_exist():" << endl; + cout << "inter=" << inter << ", e1=" << e1 << ", e2=" << e2 << endl; + cout << box_limit << endl; + *this = ConvexPolygon(box_limit); + return *this; + } assert(!inter.does_not_exist()); if(!box_limit.contains(inter.mid())) @@ -325,9 +352,23 @@ namespace codac ThickPoint::push(reduced_x, v_x_vertices); for(const auto& vertex : v_x_vertices) - if(encloses(vertex) != NO) + if(contains(vertex) != NO) inter |= vertex.box(); return inter; } + + const ConvexPolygon& ConvexPolygon::operator&=(const ConvexPolygon& x) + { + // todo: optimize this? + *this = *this & x; + return *this; + } + + const ConvexPolygon& ConvexPolygon::operator|=(const ConvexPolygon& x) + { + // todo: optimize this? + *this = *this | x; + return *this; + } } \ No newline at end of file diff --git a/src/core/geometry/codac_ConvexPolygon.h b/src/core/geometry/codac_ConvexPolygon.h index cfdf8a01..228fc58f 100644 --- a/src/core/geometry/codac_ConvexPolygon.h +++ b/src/core/geometry/codac_ConvexPolygon.h @@ -35,7 +35,10 @@ namespace codac /// @{ const BoolInterval is_subset(const ConvexPolygon& p) const; + bool is_unbounded() const; + const BoolInterval contains(const ThickPoint& p) const; const BoolInterval encloses(const ThickPoint& p) const; + const BoolInterval intersects(const ConvexPolygon& p) const; /// @} /// \name Setting values @@ -50,6 +53,8 @@ namespace codac /// @{ const IntervalVector fast_intersection(const IntervalVector& x) const; + const ConvexPolygon& operator&=(const ConvexPolygon& x); + const ConvexPolygon& operator|=(const ConvexPolygon& x); /// @} }; diff --git a/src/core/geometry/codac_GrahamScan.h b/src/core/geometry/codac_GrahamScan.h index cc3a5ba6..2f76c01e 100644 --- a/src/core/geometry/codac_GrahamScan.h +++ b/src/core/geometry/codac_GrahamScan.h @@ -27,11 +27,14 @@ namespace codac // Prints convex hull of a set of n points. static const std::vector convex_hull(const std::vector& v_points); - - protected: - // A utility function to find next to top in a stack static const Vector next_to_top(const std::stack& s); + + // To find orientation of ordered triplet (p, q, r). + static OrientationInterval orientation(const IntervalVector& p0, const IntervalVector& p1, const IntervalVector& p2); + + + protected: // A utility function to swap two points static void swap(Vector& p1, Vector& p2); @@ -39,9 +42,6 @@ namespace codac // A utility function to return square of distance between p1 and p2 static const Interval dist(const IntervalVector& p1, const IntervalVector& p2); - // To find orientation of ordered triplet (p, q, r). - static OrientationInterval orientation(const IntervalVector& p0, const IntervalVector& p1, const IntervalVector& p2); - friend class ThickPointsSorter; friend class ConvexPolygon; }; diff --git a/src/core/geometry/codac_Polygon.cpp b/src/core/geometry/codac_Polygon.cpp index 09d07d4a..eaf139a6 100644 --- a/src/core/geometry/codac_Polygon.cpp +++ b/src/core/geometry/codac_Polygon.cpp @@ -38,6 +38,11 @@ namespace codac { } + + void Polygon::set_empty() + { + m_v_floating_pts.clear(); + } // Accessing values @@ -110,6 +115,11 @@ namespace codac return 0.5*a; } + double Polygon::volume() const + { + return area().ub(); + } + // Tests diff --git a/src/core/geometry/codac_Polygon.h b/src/core/geometry/codac_Polygon.h index a7f54019..c7cd4540 100644 --- a/src/core/geometry/codac_Polygon.h +++ b/src/core/geometry/codac_Polygon.h @@ -29,6 +29,7 @@ namespace codac Polygon(); Polygon(const Polygon& p); Polygon(const std::vector& v_floating_pts); + void set_empty(); /// @} /// \name Accessing values @@ -43,6 +44,7 @@ namespace codac const IntervalVector box() const; const ThickPoint center() const; const Interval area() const; + double volume() const; /// @} /// \name Tests diff --git a/src/core/graphics/codac_ColorMap.cpp b/src/core/graphics/codac_ColorMap.cpp index 43e1317d..f7554131 100644 --- a/src/core/graphics/codac_ColorMap.cpp +++ b/src/core/graphics/codac_ColorMap.cpp @@ -216,6 +216,16 @@ namespace codac const ColorMap ColorMap::BLUE_TUBE = make_blue_tube(); + ColorMap make_red_tube() + { + ColorMap map(InterpolMode::RGB); + map.add_color_point(make_rgb(169,55,0), 0.); + map.add_color_point(make_rgb(241,140,54), 1.); + return map; + } + + const ColorMap ColorMap::RED_TUBE = make_red_tube(); + ColorMap make_rainbow() { ColorMap map(InterpolMode::HSV); diff --git a/src/core/graphics/codac_ColorMap.h b/src/core/graphics/codac_ColorMap.h index 73fe741b..c6199bf4 100644 --- a/src/core/graphics/codac_ColorMap.h +++ b/src/core/graphics/codac_ColorMap.h @@ -121,6 +121,7 @@ namespace codac static const ColorMap HAXBY; //!< predefined HAXBY color map (mainly used for DEM) static const ColorMap DEFAULT; //!< a predefined default color map static const ColorMap BLUE_TUBE; //!< a predefined color map for tubes + static const ColorMap RED_TUBE; //!< a predefined color map for tubes static const ColorMap RAINBOW; //!< a predefined color map diff --git a/src/core/graphics/codac_VIBesFig.cpp b/src/core/graphics/codac_VIBesFig.cpp index 7afecb2a..be802eef 100644 --- a/src/core/graphics/codac_VIBesFig.cpp +++ b/src/core/graphics/codac_VIBesFig.cpp @@ -9,6 +9,7 @@ */ #include "codac_VIBesFig.h" +#include "codac2_Tube.h" using namespace std; using namespace ibex; @@ -278,6 +279,16 @@ namespace codac for(int i = v_p.size()-1 ; i >= 0 ; i--) // we usually prefer to display last poylgons first, that may be larger draw_polygon(v_p[i], rgb2hex(color_map.color(i*1./(v_p.size()-1)))); } + + void VIBesFig::draw_polygon_tube(const codac2::Tube& x, const ColorMap& color_map, const vibes::Params& params) + { + int i = -1; + for(const auto& s : x) + { + if(!s.codomain().box().is_unbounded()) + draw_polygon(s.codomain(), rgb2hex(color_map.color((++i)*1./(x.nb_slices()-1)))); + } + } void VIBesFig::draw_point(const ThickPoint& p, float size, const vibes::Params& params) { diff --git a/src/core/graphics/codac_VIBesFig.h b/src/core/graphics/codac_VIBesFig.h index 1d2073c1..21be6ce3 100644 --- a/src/core/graphics/codac_VIBesFig.h +++ b/src/core/graphics/codac_VIBesFig.h @@ -18,8 +18,16 @@ #include "codac_Polygon.h" #include "codac_ConvexPolygon.h" #include "codac_ColorMap.h" +#include "codac_ConvexPolygon.h" +#include "codac_polygon_arithmetic.h" #include "vibes.h" +namespace codac2 +{ + template + class Tube; +} + namespace codac { /** @@ -323,7 +331,8 @@ namespace codac * \param params VIBes parameters related to the polygons (none by default) */ void draw_polygons(const std::vector& v_p, const ColorMap& color_map, const vibes::Params& params = vibes::Params()); - + void draw_polygon_tube(const codac2::Tube& x, const ColorMap& color_map, const vibes::Params& params = vibes::Params()); + /** * \brief Draws a point * diff --git a/src/core/paving/codac_ConnectedSubset.cpp b/src/core/paving/codac_ConnectedSubset.cpp index 81799fa5..f1029dd3 100644 --- a/src/core/paving/codac_ConnectedSubset.cpp +++ b/src/core/paving/codac_ConnectedSubset.cpp @@ -36,6 +36,14 @@ namespace codac return box().is_strict_interior_subset(get_paving()->box()); } + bool ConnectedSubset::contains(const Vector& p) const + { + for(const auto& subset_item : m_v_subset_items) + if(subset_item->box().contains(p)) + return true; + return false; + } + const Paving* ConnectedSubset::get_paving() const { assert(!m_v_subset_items.empty() && "no items in ConnectedSubset (empty items vector)"); diff --git a/src/core/paving/codac_ConnectedSubset.h b/src/core/paving/codac_ConnectedSubset.h index c4591b61..6eb6956a 100644 --- a/src/core/paving/codac_ConnectedSubset.h +++ b/src/core/paving/codac_ConnectedSubset.h @@ -14,6 +14,7 @@ #include #include "codac_IntervalMatrix.h" +#include "codac_Vector.h" #include "codac_Set.h" namespace codac @@ -56,6 +57,14 @@ namespace codac */ bool is_strictly_included_in_paving() const; + /** + * \brief Tests if p is contained in the connected subset + * + * \param p vector to be tested + * \return `true` if p is inside this + */ + bool contains(const Vector& p) const; + /** * \brief Returns a const pointer to the paving structure * diff --git a/src/core/paving/codac_Paving.cpp b/src/core/paving/codac_Paving.cpp index 7a0f1c72..307cb1e9 100644 --- a/src/core/paving/codac_Paving.cpp +++ b/src/core/paving/codac_Paving.cpp @@ -21,10 +21,22 @@ namespace codac // Basics Paving::Paving(const IntervalVector& box, SetValue value) - : Set(box, value), m_root(this) + : Set(box, value), m_flag(false), m_root(this), m_first_subpaving(nullptr), m_second_subpaving(nullptr) { } + + Paving::Paving(const Paving& p) + : Set(p), m_flag(p.m_flag), m_root(p.m_root), m_first_subpaving(nullptr), m_second_subpaving(nullptr) + { + if(p.m_first_subpaving) + { + m_first_subpaving = new Paving(p.m_first_subpaving->m_box, p.m_first_subpaving->m_value); + *m_first_subpaving = *p.m_first_subpaving; + m_second_subpaving = new Paving(p.m_second_subpaving->m_box, p.m_second_subpaving->m_value); + *m_second_subpaving = *p.m_second_subpaving; + } + } Paving::~Paving() { @@ -34,6 +46,23 @@ namespace codac delete m_second_subpaving; } } + + Paving& Paving::operator=(const Paving& p) + { + Set::operator = (p); + m_flag = p.m_flag; + m_root = p.m_root; + m_first_subpaving = nullptr; + m_second_subpaving = nullptr; + if(p.m_first_subpaving) + { + m_first_subpaving = new Paving(p.m_first_subpaving->m_box, p.m_first_subpaving->m_value); + *m_first_subpaving = *p.m_first_subpaving; + m_second_subpaving = new Paving(p.m_second_subpaving->m_box, p.m_second_subpaving->m_value); + *m_second_subpaving = *p.m_second_subpaving; + } + return *this; + } // Binary tree structure @@ -128,10 +157,49 @@ namespace codac if(m_second_subpaving) m_second_subpaving->reset_flags(); } + void Paving::reset_paving(SetValue value) + { + m_value = value; + if(m_first_subpaving) + { + delete m_first_subpaving; m_first_subpaving = nullptr; + delete m_second_subpaving; m_second_subpaving = nullptr; + } + } + // Extract methods + + void Paving::get_boxes(list& l_subpavings, SetValue val, SetValue neg_val) const + { + assert(!(val & neg_val) && "val and neg_val intersect"); + + if(neg_val != SetValue::DEFAULT && (m_value & neg_val) && !(m_value & val)) + { + // The current node and its leaves do not contain solutions + // So we stop here + } + + else + { + if(is_leaf()) + { + if(m_value & val) + l_subpavings.push_back(box()); + } + + else + { + m_first_subpaving->get_boxes(l_subpavings, val, neg_val); + m_second_subpaving->get_boxes(l_subpavings, val, neg_val); + } + } + } void Paving::get_pavings_intersecting(SetValue val, const IntervalVector& box_to_intersect, vector& v_subpavings, bool no_degenerated_intersection) const { + // todo: use the negation of val for a faster reading of the tree? + // See Paving::get_boxes + assert(box_to_intersect.size() == 2); IntervalVector inter = box_to_intersect & m_box; @@ -169,12 +237,11 @@ namespace codac // todo: return x1->get_items().size() > x2->get_items().size(); // todo: } - vector Paving::get_connected_subsets(bool sort_by_size) const + vector Paving::get_connected_subsets(bool sort_by_size, SetValue val) const { reset_flags(); const Paving *p; - SetValue val = SetValue::UNKNOWN | SetValue::IN; vector v_connected_subsets; while((p = get_first_leaf(val, true))) diff --git a/src/core/paving/codac_Paving.h b/src/core/paving/codac_Paving.h index d1163eab..674b18c5 100644 --- a/src/core/paving/codac_Paving.h +++ b/src/core/paving/codac_Paving.h @@ -12,6 +12,8 @@ #ifndef __CODAC_PAVING_H__ #define __CODAC_PAVING_H__ +#include +#include #include "codac_Set.h" #include "codac_ConnectedSubset.h" @@ -39,11 +41,15 @@ namespace codac */ Paving(const IntervalVector& box, SetValue value = SetValue::UNKNOWN); + Paving(const Paving& p); + /** * \brief Paving destructor */ ~Paving(); + Paving& operator=(const Paving& p); + /// @} /// \name Binary tree structure /// @{ @@ -145,10 +151,26 @@ namespace codac */ void reset_flags() const; + /** + * \brief Same as building a new Paving object + * + * \param value integer of the set, `SetValue::UNKNOWN` by default + */ + void reset_paving(SetValue value = SetValue::UNKNOWN); + /// @} /// \name Extract methods /// @{ + /** + * \brief Returns a set of boxes leaves of some value + * + * \param l_subpavings the set of returned objects + * \param val the value of the leaves (boxes of the paving) we are looking for + * \param neg_val the value for which we reject the leaves, optional argument used for faster execution along the tree + */ + void get_boxes(std::list& l_subpavings, SetValue val, SetValue neg_val = SetValue::DEFAULT) const; + /** * \brief Returns a set of Paving leaves of some value and intersecting a given box * @@ -184,15 +206,16 @@ namespace codac * * \param sort_by_size (optional) if `true` then the subsets will be * sort by the number of boxes they are made of + * \param val the value of the leaves defining the connected items * \return the set of connected subsets */ - std::vector get_connected_subsets(bool sort_by_size = false) const; + std::vector get_connected_subsets(bool sort_by_size = false, SetValue val = SetValue::UNKNOWN | SetValue::IN) const; /// @} - protected: + public: - mutable bool m_flag; //!< optional flag, can be used by search algorithms + mutable bool m_flag = false; //!< optional flag, can be used by search algorithms Paving *m_root = nullptr; //!< pointer to the root Paving *m_first_subpaving = nullptr, *m_second_subpaving = nullptr; //!< tree structure }; diff --git a/src/core/paving/codac_SIVIAPaving.cpp b/src/core/paving/codac_SIVIAPaving.cpp index c21af1fa..bbbe99dd 100644 --- a/src/core/paving/codac_SIVIAPaving.cpp +++ b/src/core/paving/codac_SIVIAPaving.cpp @@ -47,7 +47,7 @@ namespace codac } } - void SIVIAPaving::compute(Ctc &ctc, float precision) + void SIVIAPaving::compute(Ctc& ctc, float precision) { assert(precision > 0.); assert(ctc.nb_var == box().size()); @@ -58,7 +58,7 @@ namespace codac if(result.is_empty()) set_value(SetValue::OUT); - else if(result.max_diam() < precision) + else if(box().max_diam() < precision) set_value(SetValue::UNKNOWN); else diff --git a/src/core/paving/codac_Set.h b/src/core/paving/codac_Set.h index 46bd575f..df9094d7 100644 --- a/src/core/paving/codac_Set.h +++ b/src/core/paving/codac_Set.h @@ -25,6 +25,8 @@ namespace codac */ enum class SetValue { + DEFAULT = 0x00, ///< does not have a meaning, only used for default values of arguments + UNKNOWN = 0x01, ///< unable to conclude OUT = 0x02, ///< outside the solution set IN = 0x04, ///< inside the solution set diff --git a/src/unsupported/separators/codac_CtcTransform.cpp b/src/core/separators/codac_CtcTransform.cpp similarity index 100% rename from src/unsupported/separators/codac_CtcTransform.cpp rename to src/core/separators/codac_CtcTransform.cpp diff --git a/src/unsupported/separators/codac_CtcTransform.h b/src/core/separators/codac_CtcTransform.h similarity index 100% rename from src/unsupported/separators/codac_CtcTransform.h rename to src/core/separators/codac_CtcTransform.h diff --git a/src/core/separators/codac_SepProj.cpp b/src/core/separators/codac_SepProj.cpp index cf3f2df4..54a9d996 100644 --- a/src/core/separators/codac_SepProj.cpp +++ b/src/core/separators/codac_SepProj.cpp @@ -56,6 +56,8 @@ bool SepProj::process(IntervalVector& x_in, IntervalVector& x_out, IntervalVecto // Separate the product [x].[y] IntervalVector XinFull = cart_prod(x, y); IntervalVector XoutFull = cart_prod(x, y); + //IntervalVector _XinFull = cart_prod(x, y); + //IntervalVector _XoutFull = cart_prod(x, y); // IntervalVector XinFull0(XinFull); // IntervalVector XoutFull0(XoutFull); @@ -79,7 +81,9 @@ bool SepProj::process(IntervalVector& x_in, IntervalVector& x_out, IntervalVecto // Handle error case if (XinFull.is_empty() && XoutFull.is_empty()){ - cout << "Erreur !!!! line" << __LINE__ << "\n "; + cout << "Error: XinFull.is_empty() && XoutFull.is_empty()" << endl; + cout << " found on codac_SepProj.cpp" << endl; + //cout << " " << x << ", " << y << ", " << _XinFull << ", " << _XoutFull << endl; exit(-1); } @@ -98,11 +102,11 @@ bool SepProj::process(IntervalVector& x_in, IntervalVector& x_out, IntervalVecto x_out.set_empty(); if( use_point == false) impact.setCoutFlags(x_out, x); - y.set_empty(); +// y.set_empty(); return true; } else { x_out = XoutFull.subvector(0, x_out.size()-1); - y = XoutFull.subvector(x_out.size(), XoutFull.size()-1); +// y = XoutFull.subvector(x_out.size(), XoutFull.size()-1); if( use_point == false) impact.setCoutFlags(x_out, x); } diff --git a/src/core/separators/codac_SepProj.h b/src/core/separators/codac_SepProj.h index d10d0b4b..4b85201f 100644 --- a/src/core/separators/codac_SepProj.h +++ b/src/core/separators/codac_SepProj.h @@ -239,7 +239,7 @@ typedef struct ImpactStatus_{ * The separator algorythm is inspired from the ibexlib ones * but performs the inner and outer contraction concurently. * - * See Ibexlib (CtcForAll and CtcExist)[http://www.ibex-lib.org/doc/contractor.html#exists-and-forall] + * See Ibexlib (CtcForAll and CtcExist)[https://ibex-team.github.io/ibex-lib/contractor.html#exists-and-forall] * documentation for more details. */ class SepProj : public Sep { diff --git a/src/unsupported/separators/codac_SepTransform.cpp b/src/core/separators/codac_SepTransform.cpp similarity index 100% rename from src/unsupported/separators/codac_SepTransform.cpp rename to src/core/separators/codac_SepTransform.cpp diff --git a/src/unsupported/separators/codac_SepTransform.h b/src/core/separators/codac_SepTransform.h similarity index 100% rename from src/unsupported/separators/codac_SepTransform.h rename to src/core/separators/codac_SepTransform.h diff --git a/src/core/sivia/codac_sivia.cpp b/src/core/sivia/codac_sivia.cpp index f6a5310d..ca79ffdd 100644 --- a/src/core/sivia/codac_sivia.cpp +++ b/src/core/sivia/codac_sivia.cpp @@ -2,7 +2,7 @@ * SIVIA * ---------------------------------------------------------------------------- * \date 2022 - * \author Simon Rohou (revised by Julien Damers) + * \author Simon Rohou, Julien Damers * \copyright Copyright 2022 Codac Team * \license This program is distributed under the terms of * the GNU Lesser General Public License (LGPL). @@ -21,371 +21,317 @@ using namespace std; namespace codac { - static bool _vibes_initialized = false; - - bool check_empty(const IntervalVector& x0) + static bool _vibes_initialized = false; + + vector box_diff(const IntervalVector& x0, const IntervalVector& x) + { + vector v; + IntervalVector* boxes; + int n = x0.diff(x, boxes); + v.assign(boxes, boxes + n); + return v; + } + + map> _SIVIA( + const IntervalVector& x0, const IntervalVector* y0, Ctc& ctc, float precision, bool regular_paving, + bool display_result, const string& fig_name, bool return_result, const SetColorMap& color_map) + { + assert(x0.size() >= 2); + + if(display_result) { - bool empty = false; - for (int i=0; i box_diff(const IntervalVector &x0, const IntervalVector &x) - { - vector v; - IntervalVector *boxes; - int n = x0.diff(x, boxes); - v.assign(boxes, boxes + n); - return v; + if(!fig_name.empty()) + vibes::newFigure(fig_name); + + vibes::drawBox(x0.subvector(0,1)); + vibes::newGroup("boxes_out", cm.at(SetValue::OUT)); + vibes::newGroup("boxes_unknown", cm.at(SetValue::UNKNOWN)); + + vibes::drawBox(x0.subvector(0,1), vibesParams("figure", fig_name)); + vibes::axisAuto(); } + map n_boxes; + map> boxes{ + // SetValue::IN is not possible for SIVIA using Ctc + {SetValue::OUT, {}}, + {SetValue::UNKNOWN, {}}, + }; - map> - SIVIA(const IntervalVector &x0, Ctc &ctc, float precision, - bool regular, bool display_result, - const string &fig_name, bool return_result, - const SetColorMap &color_map) - { - assert(x0.size() >= 2); + ibex::LargestFirst bisector(0.); + deque stack = { y0 ? cart_prod(x0,*y0) : x0 }; + int k = 0; - if ( display_result ) - { - if ( !_vibes_initialized ) - { - _vibes_initialized = true; - vibes::beginDrawing(); - vibes::axisAuto(); - // will not be ended in case the init has been done outside this SIVIA function - } + clock_t t_start = clock(); + + while(!stack.empty()) + { + k++; + IntervalVector x_before_ctc = stack.front(); + stack.pop_front(); + IntervalVector x(x_before_ctc); - if ( !fig_name.empty()) - vibes::selectFigure(fig_name); + ctc.contract(x); - vibes::drawBox(x0); - } + // Out values - map n_boxes; - map> boxes{ - {SetValue::OUT, {}}, - {SetValue::UNKNOWN, {}} - }; + vector x_out_l; - ibex::LargestFirst bisector(0.); - deque stack = {x0}; - int k = 0; + if(regular_paving) + { + if(x.is_empty()) + x_out_l.push_back(x_before_ctc); + } - // Some values in the desired color map may not have been defined by the user - // We select default colors in this case + else + x_out_l = box_diff(x_before_ctc, x); - SetColorMap cm = DEFAULT_SET_COLOR_MAP; - - if ( display_result ) + for(const auto& o : x_out_l) { + if(display_result) + { + vibes::drawBox(o.subvector(0,1), vibesParams("group", "boxes_out")); + n_boxes[SetValue::OUT] ++; + } - if ( color_map.find(SetValue::OUT) != color_map.end()) - cm[SetValue::OUT] = color_map.at(SetValue::OUT); - - if ( color_map.find(SetValue::UNKNOWN) != color_map.end()) - cm[SetValue::UNKNOWN] = color_map.at(SetValue::UNKNOWN); + if(return_result) + boxes[SetValue::OUT].push_front(o); } - bool empty = false; - clock_t t_start = clock(); + // Remaining solutions + + if(x.is_empty()) + continue; - while ( !stack.empty()) + else { - empty = false; - k++; - IntervalVector x = stack.front(); - stack.pop_front(); - IntervalVector x_before_ctc(x); + IntervalVector& x_remaining = regular_paving ? x_before_ctc : x; - ctc.contract(x); - empty = check_empty(x); + if((!y0 && x_remaining.max_diam() < precision) || + ( y0 && x_remaining.subvector(0,x0.size()-1).max_diam() < precision)) + { + if(display_result) + { + vibes::drawBox(x_remaining.subvector(0,1), vibesParams("group", "boxes_unknown")); + n_boxes[SetValue::UNKNOWN] ++; + } + if(return_result) + boxes[SetValue::UNKNOWN].push_front(x_remaining); + } - if ( regular ) + else + { + if(y0) { - if ( empty ) - { - if ( display_result ) - { - vibes::drawBox(x_before_ctc.subvector(0, 1), cm.at(SetValue::OUT)); - n_boxes[SetValue::OUT]++; - } - - if ( return_result ) - boxes[SetValue::OUT].push_front(x_before_ctc); - } - else - { - if (x_before_ctc.max_diam()>precision) - { - int i = x_before_ctc.subvector(0,1).extr_diam_index(false); - pair p = x_before_ctc.bisect(i); - stack.push_back(p.first); - stack.push_back(p.second); - } - else - { - if ( display_result ) - { - vibes::drawBox(x_before_ctc.subvector(0, 1), cm.at(SetValue::UNKNOWN)); - n_boxes[SetValue::UNKNOWN]++; - } - - if ( return_result ) - boxes[SetValue::UNKNOWN].push_front(x_before_ctc); - } - } - + pair p = bisector.bisect(x_remaining.subvector(0,x0.size()-1)); + stack.push_back(cart_prod(p.first,*y0)); + stack.push_back(cart_prod(p.second,*y0)); } + else { - vector x_out_l = box_diff(x_before_ctc, x); - for ( const auto &o: x_out_l ) - { - if ( display_result ) - { - vibes::drawBox(o.subvector(0, 1), cm.at(SetValue::OUT)); - n_boxes[SetValue::OUT]++; - } - - if ( return_result ) - boxes[SetValue::OUT].push_front(o); - } - - if ( empty ) - { - continue; - } - - else - { - if ( x.max_diam() < precision ) - { - if ( display_result ) - { - vibes::drawBox(x.subvector(0, 1), cm.at(SetValue::UNKNOWN)); - n_boxes[SetValue::UNKNOWN]++; - } - - if ( return_result ) - { - boxes[SetValue::UNKNOWN].push_front(x); - } - } - - else - { - pair p = bisector.bisect(x); - stack.push_back(p.first); - stack.push_back(p.second); - } - - } + pair p = bisector.bisect(x_remaining); + stack.push_back(p.first); + stack.push_back(p.second); } - + } } + } + + if(display_result) + { + printf( "Computation time: %.2fs\n", (double)(clock() - t_start)/CLOCKS_PER_SEC); + cout << " Contractions: " << k << endl; + cout << " OUT boxes: " << n_boxes[SetValue::OUT] << endl; + cout << " UNKNOWN boxes: " << n_boxes[SetValue::UNKNOWN] << endl; + } - if ( display_result ) + return boxes; + } + + map> SIVIA( + const IntervalVector& x0, const IntervalVector& y0, Ctc& ctc, float precision, bool regular_paving, + bool display_result, const string& fig_name, bool return_result, const SetColorMap& color_map) + { + return _SIVIA(x0, &y0, ctc, precision, regular_paving, display_result, fig_name, return_result, color_map); + } + + map> SIVIA( + const IntervalVector& x0, Ctc& ctc, float precision, bool regular_paving, + bool display_result, const string& fig_name, bool return_result, const SetColorMap& color_map) + { + return _SIVIA(x0, nullptr, ctc, precision, regular_paving, display_result, fig_name, return_result, color_map); + } + + map> SIVIA( + const IntervalVector& x0, ibex::Sep& sep, float precision, bool regular_paving, + bool display_result, const string& fig_name, bool return_result, const SetColorMap& color_map) + { + assert(x0.size() >= 2); + + if(display_result) + { + // Some values in the desired color map may not have been defined by the user + // We select default colors in this case + + SetColorMap cm = DEFAULT_SET_COLOR_MAP; + + if(display_result) { - printf("Computation time: %.2fs\n", (double) (clock() - t_start) / CLOCKS_PER_SEC); - cout << " Contractions: " << k << endl; - cout << " OUT boxes: " << n_boxes[SetValue::OUT] << endl; - cout << " UNKNOWN boxes: " << n_boxes[SetValue::UNKNOWN] << endl; + if(color_map.find(SetValue::OUT) != color_map.end()) + cm[SetValue::OUT] = color_map.at(SetValue::OUT); + + if(color_map.find(SetValue::UNKNOWN) != color_map.end()) + cm[SetValue::UNKNOWN] = color_map.at(SetValue::UNKNOWN); + + if(color_map.find(SetValue::IN) != color_map.end()) + cm[SetValue::IN] = color_map.at(SetValue::IN); } - return boxes; + if(!_vibes_initialized) + { + _vibes_initialized = true; + vibes::beginDrawing(); + // will not be ended in case the init has been done outside this SIVIA function + } + + if(!fig_name.empty()) + vibes::newFigure(fig_name); + + vibes::drawBox(x0.subvector(0,1)); + vibes::newGroup("boxes_out", cm.at(SetValue::OUT)); + vibes::newGroup("boxes_unknown", cm.at(SetValue::UNKNOWN)); + vibes::newGroup("boxes_in", cm.at(SetValue::IN)); + vibes::axisAuto(); } + map n_boxes; + map> boxes{ + {SetValue::IN, {}}, + {SetValue::OUT, {}}, + {SetValue::UNKNOWN, {}}, + }; - map> SIVIA(const IntervalVector &x0, ibex::Sep &sep, float precision, - bool regular, bool display_result, const string &fig_name, bool return_result, - const SetColorMap &color_map) - { - assert(x0.size() >= 2); + ibex::LargestFirst bisector(0.); + deque stack = { x0 }; + int k = 0; - if ( display_result ) - { - if ( !_vibes_initialized ) - { - _vibes_initialized = true; - vibes::beginDrawing(); - vibes::axisAuto(); - // will not be ended in case the init has been done outside this SIVIA function - } + clock_t t_start = clock(); - if ( !fig_name.empty()) - vibes::selectFigure(fig_name); + while(!stack.empty()) + { + k++; + IntervalVector x_before_ctc = stack.front(); + stack.pop_front(); + IntervalVector x_in(x_before_ctc), x_out(x_before_ctc); - vibes::drawBox(x0); - } + sep.separate(x_in, x_out); - map n_boxes; - map> boxes{ - {SetValue::IN, {}}, - {SetValue::OUT, {}}, - {SetValue::UNKNOWN, {}}, - }; + IntervalVector x = x_in & x_out; - ibex::LargestFirst bisector(0.); - deque stack = {x0}; - int k = 0; + // Out and In values - // Some values in the desired color map may not have been defined by the user - // We select default colors in this case + vector x_in_l, x_out_l; - SetColorMap cm = DEFAULT_SET_COLOR_MAP; + if(regular_paving) + { + if(x_in.is_empty()) + x_in_l.push_back(x_before_ctc); + if(x_out.is_empty()) + x_out_l.push_back(x_before_ctc); + } - if ( display_result ) + else { - if ( color_map.find(SetValue::IN) != color_map.end()) - cm[SetValue::IN] = color_map.at(SetValue::IN); + x_in_l = box_diff(x_before_ctc, x_in); + x_out_l = box_diff(x_before_ctc, x_out); + } - if ( color_map.find(SetValue::OUT) != color_map.end()) - cm[SetValue::OUT] = color_map.at(SetValue::OUT); + for(const auto& i : x_in_l) + { + if(display_result) + { + vibes::drawBox(i.subvector(0,1), vibesParams("group", "boxes_in")); + n_boxes[SetValue::IN] ++; + } - if ( color_map.find(SetValue::UNKNOWN) != color_map.end()) - cm[SetValue::UNKNOWN] = color_map.at(SetValue::UNKNOWN); + if(return_result) + boxes[SetValue::IN].push_front(i); } - bool empty = false; - clock_t t_start = clock(); - - while ( !stack.empty()) + for(const auto& o : x_out_l) { - k++; - empty = false; - IntervalVector x_before_ctc = stack.front(); - stack.pop_front(); - IntervalVector x_in(x_before_ctc), x_out(x_before_ctc); + if(display_result) + { + vibes::drawBox(o.subvector(0,1), vibesParams("group", "boxes_out")); + n_boxes[SetValue::OUT] ++; + } - sep.separate(x_in, x_out); + if(return_result) + boxes[SetValue::OUT].push_front(o); + } - if (regular) - { - if ( check_empty(x_out)) - { - if ( display_result ) - { - vibes::drawBox(x_before_ctc, cm.at(SetValue::OUT)); - n_boxes[SetValue::OUT]++; - } - - if ( return_result ) - { - boxes[SetValue::OUT].push_front(x_before_ctc); - } - - } - else if(check_empty(x_in)) - { - if ( display_result ) - { - vibes::drawBox(x_before_ctc, cm.at(SetValue::IN)); - n_boxes[SetValue::IN]++; - } - - if ( return_result ) - { - boxes[SetValue::IN].push_front(x_before_ctc); - } - } - else - { - if (x_before_ctc.max_diam()>precision) - { - int i = x_before_ctc.subvector(0,1).extr_diam_index(false); - pair p = x_before_ctc.bisect(i); - stack.push_back(p.first); - stack.push_back(p.second); - } - else - { - if ( display_result ) - { - vibes::drawBox(x_before_ctc.subvector(0, 1), cm.at(SetValue::UNKNOWN)); - n_boxes[SetValue::UNKNOWN]++; - } - - if ( return_result ) - boxes[SetValue::UNKNOWN].push_front(x_before_ctc); - } - } - } - else + // Remaining values + + if(x.is_empty()) + continue; + + else + { + IntervalVector& x_remaining = regular_paving ? x_before_ctc : x; + + if(x_remaining.max_diam() < precision) + { + if(display_result) { - IntervalVector x = x_in & x_out; - empty = check_empty(x); - - vector x_in_l = box_diff(x_before_ctc, x_in); - for ( const auto &i: x_in_l ) - { - if ( display_result ) - { - vibes::drawBox(i, cm.at(SetValue::IN)); - n_boxes[SetValue::IN]++; - } - - if ( return_result ) - boxes[SetValue::IN].push_front(i); - } - - vector x_out_l = box_diff(x_before_ctc, x_out); - for ( const auto &o: x_out_l ) - { - if ( display_result ) - { - vibes::drawBox(o, cm.at(SetValue::OUT)); - n_boxes[SetValue::OUT]++; - } - - if ( return_result ) - boxes[SetValue::OUT].push_front(o); - } - if ( empty) - continue; - - else - { - if ( x.max_diam() < precision ) - { - if ( display_result ) - { - vibes::drawBox(x.subvector(0, 1), cm.at(SetValue::UNKNOWN)); - n_boxes[SetValue::UNKNOWN]++; - } - - if ( return_result ) - boxes[SetValue::UNKNOWN].push_front(x); - } - - else - { - pair p = bisector.bisect(x); - stack.push_back(p.first); - stack.push_back(p.second); - } - } + vibes::drawBox(x_remaining.subvector(0,1), vibesParams("group", "boxes_unknown")); + n_boxes[SetValue::UNKNOWN] ++; } + + if(return_result) + boxes[SetValue::UNKNOWN].push_front(x_remaining); + } + + else + { + pair p = bisector.bisect(x_remaining); + stack.push_back(p.first); + stack.push_back(p.second); + } } - if ( display_result ) - { - printf("Computation time: %.2fs\n", (double) (clock() - t_start) / CLOCKS_PER_SEC); - cout << " Contractions: " << k << endl; - cout << " IN boxes: " << n_boxes[SetValue::IN] << endl; - cout << " OUT boxes: " << n_boxes[SetValue::OUT] << endl; - cout << " UNKNOWN boxes: " << n_boxes[SetValue::UNKNOWN] << endl; - } + } - return boxes; + if(display_result) + { + printf( "Computation time: %.2fs\n", (double)(clock() - t_start)/CLOCKS_PER_SEC); + cout << " Contractions: " << k << endl; + cout << " IN boxes: " << n_boxes[SetValue::IN] << endl; + cout << " OUT boxes: " << n_boxes[SetValue::OUT] << endl; + cout << " UNKNOWN boxes: " << n_boxes[SetValue::UNKNOWN] << endl; } + + return boxes; + } } \ No newline at end of file diff --git a/src/core/sivia/codac_sivia.h b/src/core/sivia/codac_sivia.h index 35d32341..260a6dd4 100644 --- a/src/core/sivia/codac_sivia.h +++ b/src/core/sivia/codac_sivia.h @@ -24,6 +24,28 @@ namespace codac /// \name SIVIA for contractors /// @{ + /** + * \brief Executes a SIVIA algorithm from a contractor, and displays the result. + * SIVIA: Set Inversion Via Interval Analysis. + * + * The result is displayed in the current VIBes figure. + * + * \param x initial box (part that will be bisected) + * \param y initial box (part that will not be bisected) + * \param ctc Contractor operator for the set inversion + * \param precision accuracy of the paving algorithm + * \param regular_paving regular bisection rule + * \param display_result display information if true + * \param fig_name name of the figure on which boxes are drawn. If empty, default figure is used + * \param return_result if true, boxes will be stored in the returned map + * \param color_map color map used to draw boxes, see SetColorMap + * \return return a map of lists of boxes. Keys of the map are IN/OUT/UNKNOWN. The lists are empty if return_result if false. + */ + std::map> SIVIA(const IntervalVector& x, const IntervalVector& y, Ctc& ctc, float precision, + bool regular_paving = false, bool display_result = true, + const std::string& fig_name = "", bool return_result = false, + const SetColorMap& color_map = DEFAULT_SET_COLOR_MAP); + /** * \brief Executes a SIVIA algorithm from a contractor, and displays the result. * SIVIA: Set Inversion Via Interval Analysis. @@ -33,7 +55,7 @@ namespace codac * \param x initial box * \param ctc Contractor operator for the set inversion * \param precision accuracy of the paving algorithm - * \param regular regular bisection rule + * \param regular_paving regular bisection rule * \param display_result display information if true * \param fig_name name of the figure on which boxes are drawn. If empty, default figure is used * \param return_result if true, boxes will be stored in the returned map @@ -41,7 +63,7 @@ namespace codac * \return return a map of lists of boxes. Keys of the map are IN/OUT/UNKNOWN. The lists are empty if return_result if false. */ std::map> SIVIA(const IntervalVector& x, Ctc& ctc, float precision, - bool regular = false, bool display_result = true, + bool regular_paving = false, bool display_result = true, const std::string& fig_name = "", bool return_result = false, const SetColorMap& color_map = DEFAULT_SET_COLOR_MAP); @@ -58,7 +80,7 @@ namespace codac * \param x initial box * \param sep Separator operator for the set inversion * \param precision accuracy of the paving algorithm - * \param regular regular bisection rule + * \param regular_paving regular bisection rule * \param display_result display information if true * \param fig_name name of the figure if None use the current figure. If empty, default figure is used * \param return_result if true, boxes will be stored in the returned map @@ -66,7 +88,7 @@ namespace codac * \return return a map of lists of boxes. Keys of the map are IN/OUT/UNKNOWN. The lists are empty if return_result if false. */ std::map> SIVIA(const IntervalVector& x, ibex::Sep& sep, float precision, - bool regular = false, bool display_result = true, + bool regular_paving = false, bool display_result = true, const std::string& fig_name = "", bool return_result = false, const SetColorMap& color_map = DEFAULT_SET_COLOR_MAP); diff --git a/src/core/tools/codac_Eigen.cpp b/src/core/tools/codac_Eigen.cpp index 54dc187b..f746c5f7 100644 --- a/src/core/tools/codac_Eigen.cpp +++ b/src/core/tools/codac_Eigen.cpp @@ -15,7 +15,7 @@ Eigen::MatrixXd EigenHelpers::i2e(const Matrix &x) { Eigen::MatrixXd EigenHelpers::i2e(const Vector &x) { Eigen::MatrixXd m(x.size(), 1); for (int i = 0; i < x.size(); ++i) { - m(i, 1) = x[i]; + m(i, 0) = x[i]; } return m; } diff --git a/src/robotics/CMakeLists.txt b/src/robotics/CMakeLists.txt index c75f3049..6d551d21 100644 --- a/src/robotics/CMakeLists.txt +++ b/src/robotics/CMakeLists.txt @@ -6,6 +6,8 @@ ${CMAKE_CURRENT_SOURCE_DIR}/graphics/codac_VIBesFigMap.h ${CMAKE_CURRENT_SOURCE_DIR}/objects/codac_Beacon.cpp ${CMAKE_CURRENT_SOURCE_DIR}/objects/codac_Beacon.h + ${CMAKE_CURRENT_SOURCE_DIR}/objects/codac_Wall.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/objects/codac_Wall.h ${CMAKE_CURRENT_SOURCE_DIR}/loops/codac_TPlane.h ${CMAKE_CURRENT_SOURCE_DIR}/loops/codac_TPlane.cpp ${CMAKE_CURRENT_SOURCE_DIR}/data/codac_DataLoaderLissajous.cpp diff --git a/src/robotics/contractors/codac_CtcConstell.cpp b/src/robotics/contractors/codac_CtcConstell.cpp index feb2f0f2..407a2771 100644 --- a/src/robotics/contractors/codac_CtcConstell.cpp +++ b/src/robotics/contractors/codac_CtcConstell.cpp @@ -40,7 +40,7 @@ namespace codac assert(a.size() == 2); IntervalVector union_result(2, Interval::EMPTY_SET); - for(const auto mj : m_map) + for(const auto& mj : m_map) union_result |= a & mj.subvector(0,1); a = union_result; } diff --git a/src/robotics/graphics/codac_VIBesFigMap.cpp b/src/robotics/graphics/codac_VIBesFigMap.cpp index b8efd5f9..a2bfc8b8 100644 --- a/src/robotics/graphics/codac_VIBesFigMap.cpp +++ b/src/robotics/graphics/codac_VIBesFigMap.cpp @@ -50,6 +50,11 @@ namespace codac m_draw_tubes_backgrounds = enable; } + void VIBesFigMap::no_axis_limits() + { + _no_axis_limits = true; + } + void VIBesFigMap::show() { typename map::const_iterator it_tubes; @@ -61,7 +66,8 @@ namespace codac for(it_trajs = m_map_trajs.begin(); it_trajs != m_map_trajs.end(); it_trajs++) m_view_box |= draw_trajectory(it_trajs->first); - axis_limits(m_view_box, true, 0.02); + if(!_no_axis_limits) + axis_limits(m_view_box, true, 0.02); } void VIBesFigMap::show(float robot_size) @@ -710,7 +716,8 @@ namespace codac assert(pose.size() == 2 || pose.size() == 3); float robot_size = size == -1 ? m_robot_size : size; double robot_heading = pose.size() == 3 ? pose[2] : 0.; - axis_limits(m_view_box | pose.subvector(0,1), true); + if(!_no_axis_limits) + axis_limits(m_view_box | pose.subvector(0,1), true); //vibes::drawTank(pose[0], pose[1], robot_heading * 180. / M_PI, robot_size, "black[yellow]", params); vibes::drawAUV(pose[0], pose[1], robot_heading * 180. / M_PI, robot_size, "black[yellow]", params); } diff --git a/src/robotics/graphics/codac_VIBesFigMap.h b/src/robotics/graphics/codac_VIBesFigMap.h index 5663e682..42f06769 100644 --- a/src/robotics/graphics/codac_VIBesFigMap.h +++ b/src/robotics/graphics/codac_VIBesFigMap.h @@ -77,6 +77,8 @@ namespace codac */ void enable_tubes_backgrounds(bool enable = true); + void no_axis_limits(); + /** * \brief Displays this figure */ @@ -559,6 +561,7 @@ namespace codac bool m_draw_tubes_backgrounds = true; //!< if `true`, will highlight tubes contractions bool m_smooth_drawing = false; //!< if `true`, a smooth rendering of tubes will be done float m_robot_size = 5.5; //!< if `0`, no robot display + bool _no_axis_limits = false; unsigned int m_tube_max_nb_disp_slices = TUBE_MAX_NB_DISPLAYED_SLICES; //!< limit for slices display unsigned int m_traj_max_nb_disp_points = TRAJ_MAX_NB_DISPLAYED_POINTS; //!< limit for traj points display diff --git a/src/robotics/loops/codac_TPlane.cpp b/src/robotics/loops/codac_TPlane.cpp index c2a63ef4..a24025cd 100644 --- a/src/robotics/loops/codac_TPlane.cpp +++ b/src/robotics/loops/codac_TPlane.cpp @@ -17,10 +17,77 @@ using namespace ibex; namespace codac { TPlane::TPlane(const Interval& tdomain) - : Paving(IntervalVector(2, tdomain), SetValue::UNKNOWN) + : Paving(IntervalVector(2, tdomain), SetValue::UNKNOWN), m_precision(0), m_v_detected_loops(), m_v_proven_loops(), + m_first_subtplane(nullptr), m_second_subtplane(nullptr) { } + + TPlane::TPlane(const TPlane& t) + : Paving(t), m_precision(t.m_precision), m_v_detected_loops(t.m_v_detected_loops), m_v_proven_loops(t.m_v_proven_loops), + m_first_subtplane(nullptr), m_second_subtplane(nullptr) + { + if(t.m_first_subtplane) + { + m_first_subtplane = new TPlane(t.m_first_subtplane->m_box[0]); + *m_first_subtplane = *t.m_first_subtplane; + } + if(t.m_second_subtplane) + { + m_second_subtplane = new TPlane(t.m_second_subtplane->m_box[0]); + *m_second_subtplane = *t.m_second_subtplane; + } + } + + TPlane::TPlane(const TPlane* t, const Paving* p) + : Paving(*p), m_precision(t->m_precision), m_v_detected_loops(t->m_v_detected_loops), m_v_proven_loops(t->m_v_proven_loops), + m_first_subtplane(nullptr), m_second_subtplane(nullptr) + { + m_verbose = t->m_verbose; + if(t->m_first_subtplane) + { + m_first_subtplane = new TPlane(t->m_first_subtplane->m_box[0]); + *m_first_subtplane = *t->m_first_subtplane; + } + if(t->m_second_subtplane) + { + m_second_subtplane = new TPlane(t->m_second_subtplane->m_box[0]); + *m_second_subtplane = *t->m_second_subtplane; + } + } + + TPlane::~TPlane() + { + if(m_first_subtplane) + { + delete m_first_subtplane; + } + if(m_second_subtplane) + { + delete m_second_subtplane; + } + } + + TPlane& TPlane::operator=(const TPlane& t) + { + Paving::operator = (t); + m_precision = t.m_precision; + m_v_detected_loops = t.m_v_detected_loops; + m_v_proven_loops = t.m_v_proven_loops; + m_first_subtplane = nullptr; + m_second_subtplane = nullptr; + if(t.m_first_subtplane) + { + m_first_subtplane = new TPlane(t.m_first_subtplane->m_box[0]); + *m_first_subtplane = *t.m_first_subtplane; + } + if(t.m_second_subtplane) + { + m_second_subtplane = new TPlane(t.m_second_subtplane->m_box[0]); + *m_second_subtplane = *t.m_second_subtplane; + } + return *this; + } void TPlane::compute_loops(float precision, const TubeVector& p, const TubeVector& v) { @@ -58,8 +125,16 @@ namespace codac else if(!is_leaf()) { - ((TPlane*)m_first_subpaving)->compute_detections(precision, p, v, with_derivative, false); - ((TPlane*)m_second_subpaving)->compute_detections(precision, p, v, with_derivative, false); + //((TPlane*)m_first_subpaving)->compute_detections(precision, p, v, with_derivative, false); + //((TPlane*)m_second_subpaving)->compute_detections(precision, p, v, with_derivative, false); + m_first_subtplane = new TPlane(this, m_first_subpaving); + m_first_subtplane->compute_detections(precision, p, v, with_derivative, false); + *m_first_subpaving = *m_first_subtplane; + delete m_first_subtplane; m_first_subtplane = nullptr; + m_second_subtplane = new TPlane(this, m_second_subpaving); + m_second_subtplane->compute_detections(precision, p, v, with_derivative, false); + *m_second_subpaving = *m_second_subtplane; + delete m_second_subtplane; m_second_subtplane = nullptr; } else @@ -120,8 +195,16 @@ namespace codac else { bisect(); - ((TPlane*)m_first_subpaving)->compute_detections(precision, p, v, with_derivative, false); - ((TPlane*)m_second_subpaving)->compute_detections(precision, p, v, with_derivative, false); + //((TPlane*)m_first_subpaving)->compute_detections(precision, p, v, with_derivative, false); + //((TPlane*)m_second_subpaving)->compute_detections(precision, p, v, with_derivative, false); + m_first_subtplane = new TPlane(this, m_first_subpaving); + m_first_subtplane->compute_detections(precision, p, v, with_derivative, false); + *m_first_subpaving = *m_first_subtplane; + delete m_first_subtplane; m_first_subtplane = nullptr; + m_second_subtplane = new TPlane(this, m_second_subpaving); + m_second_subtplane->compute_detections(precision, p, v, with_derivative, false); + *m_second_subpaving = *m_second_subtplane; + delete m_second_subtplane; m_second_subtplane = nullptr; } } diff --git a/src/robotics/loops/codac_TPlane.h b/src/robotics/loops/codac_TPlane.h index fb1833b6..65d5140b 100644 --- a/src/robotics/loops/codac_TPlane.h +++ b/src/robotics/loops/codac_TPlane.h @@ -51,6 +51,17 @@ namespace codac */ TPlane(const Interval& tdomain); + TPlane(const TPlane& t); + + TPlane(const TPlane* t, const Paving* p); + + /** + * \brief TPlane destructor + */ + ~TPlane(); + + TPlane& operator=(const TPlane& t); + /** * \brief Computes the loops (detections and proofs) as a subpaving, from the tube of * positions \f$[\mathbf{p}](\cdot)\f$ and the tube of velocities \f$[\mathbf{v}](\cdot)\f$. @@ -169,8 +180,6 @@ namespace codac */ static void verbose(bool verbose = true); - protected: - /** * \brief Tries to prove the existence of loops in each detection set * @@ -180,6 +189,8 @@ namespace codac */ void compute_proofs(const std::function& f); + protected: + /** * \brief Recursive computation of the tplane, from the tube of positions \f$[\mathbf{p}](\cdot)\f$ * and the tube of velocities \f$[\mathbf{v}](\cdot)\f$. @@ -197,6 +208,8 @@ namespace codac float m_precision = 0.; //!< precision of the SIVIA algorithm, used later on in traj_loops_summary() std::vector m_v_detected_loops; //!< set of loops detections std::vector m_v_proven_loops; //!< set of loops proofs + + TPlane *m_first_subtplane, *m_second_subtplane; static bool m_verbose; }; diff --git a/src/robotics/objects/codac_Wall.cpp b/src/robotics/objects/codac_Wall.cpp new file mode 100644 index 00000000..83e48201 --- /dev/null +++ b/src/robotics/objects/codac_Wall.cpp @@ -0,0 +1,90 @@ +/** + * Wall class + * ---------------------------------------------------------------------------- + * \date 2024 + * \author Simon Rohou + * \copyright Copyright 2024 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifdef _MSC_VER +// Enable additional features in math.h. +#ifndef _USE_MATH_DEFINES +#define _USE_MATH_DEFINES +#endif // _USE_MATH_DEFINES +#include +#endif // _MSC_VER + +#include +#include "codac_Wall.h" +#include "codac_predef_values.h" + +using namespace std; +using namespace ibex; + +namespace codac +{ + Wall::Wall(const Vector& c1_, const Vector& c2_) : c1(c1_), c2(c2_) + { + } + + bool Wall::contains(const Vector& p) const + { + Vector ab { c2[0]-c1[0], c2[1]-c1[1] }; + Vector ac { p[0]-c1[0], p[1]-c1[1] }; + + double dp_AB = ab[0]*ab[0] + ab[1]*ab[1]; + double dp_AC = ab[0]*ac[0] + ab[1]*ac[1]; + return Interval(0.,dp_AB).contains(dp_AC); + } + + Vector operator&(const Wall& w1, const Wall& w2) + { + const double& x1 = w1.c1[0]; const double& x2 = w1.c2[0]; + const double& x3 = w2.c1[0]; const double& x4 = w2.c2[0]; + + const double& y1 = w1.c1[1]; const double& y2 = w1.c2[1]; + const double& y3 = w2.c1[1]; const double& y4 = w2.c2[1]; + + return { + ((x1*y2-y1*x2)*(x3-x4)-(x1-x2)*(x3*y4-y3*x4))/((x1-x2)*(y3-y4)-(y1-y2)*(x3-x4)), + ((x1*y2-y1*x2)*(y3-y4)-(y1-y2)*(x3*y4-y3*x4))/((x1-x2)*(y3-y4)-(y1-y2)*(x3-x4)) + }; + } + + double shorter_dist_to_walls(const std::vector& v_walls, const Vector& p, double bearing) + { + Wall w0 { p, { p[0]+999999.*cos(bearing), p[1]+999999.*sin(bearing) }}; + double min_dist = oo; + + for(const auto& wi : v_walls) + { + Vector pi = w0 & wi; + if(!wi.contains(pi) || !w0.contains(pi)) + continue; + double dist = pow(p[0]-pi[0],2) + pow(p[1]-pi[1],2); + min_dist = dist < min_dist ? dist : min_dist; + } + + return sqrt(min_dist); + } + + Vector shorter_contact_to_walls(const std::vector& v_walls, const Vector& p) + { + double min_dist = oo; + double bearing; + + for(double a = 0. ; a < 2.*M_PI ; a+=0.1) + { + double dist = shorter_dist_to_walls(v_walls, p, a); + if(dist < min_dist) + { + min_dist = dist; + bearing = a; + } + } + + return { min_dist, bearing }; + } +} \ No newline at end of file diff --git a/src/robotics/objects/codac_Wall.h b/src/robotics/objects/codac_Wall.h new file mode 100644 index 00000000..af7de4ca --- /dev/null +++ b/src/robotics/objects/codac_Wall.h @@ -0,0 +1,37 @@ +/** + * Beacon class + * ---------------------------------------------------------------------------- + * \date 2024 + * \author Simon Rohou + * \copyright Copyright 2024 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifndef __CODAC_WALL_H__ +#define __CODAC_WALL_H__ + +#include "codac_Vector.h" +#include "codac_IntervalVector.h" + +namespace codac +{ + class Wall + { + public: + + Wall(const Vector& c1, const Vector& c2); + bool contains(const Vector& p) const; + + //protected: + + Vector c1, c2; + }; + + Vector operator&(const Wall& w1, const Wall& w2); + + double shorter_dist_to_walls(const std::vector& v_walls, const Vector& p, double bearing); + Vector shorter_contact_to_walls(const std::vector& v_walls, const Vector& p); +} + +#endif \ No newline at end of file diff --git a/src/unsupported/CMakeLists.txt b/src/unsupported/CMakeLists.txt index 8d8af433..aab7fda0 100644 --- a/src/unsupported/CMakeLists.txt +++ b/src/unsupported/CMakeLists.txt @@ -26,8 +26,6 @@ # Separators ${CMAKE_CURRENT_SOURCE_DIR}/separators/codac_CtcHull.cpp ${CMAKE_CURRENT_SOURCE_DIR}/separators/codac_CtcHull.h - ${CMAKE_CURRENT_SOURCE_DIR}/separators/codac_CtcTransform.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/separators/codac_CtcTransform.h # ${CMAKE_CURRENT_SOURCE_DIR}/separators/codac_QInterProjF.cpp # ${CMAKE_CURRENT_SOURCE_DIR}/separators/codac_QInterProjF.h # ${CMAKE_CURRENT_SOURCE_DIR}/separators/codac_SepCtcPairProj.cpp @@ -36,8 +34,6 @@ # ${CMAKE_CURRENT_SOURCE_DIR}/separators/codac_SepFixPoint.h # ${CMAKE_CURRENT_SOURCE_DIR}/separators/codac_SepProj.cpp # ${CMAKE_CURRENT_SOURCE_DIR}/separators/codac_SepProj.h - ${CMAKE_CURRENT_SOURCE_DIR}/separators/codac_SepTransform.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/separators/codac_SepTransform.h ${CMAKE_CURRENT_SOURCE_DIR}/separators/codac_SepUnionBbox.cpp ${CMAKE_CURRENT_SOURCE_DIR}/separators/codac_SepUnionBbox.h @@ -66,7 +62,7 @@ ################################################################################ add_library(codac-unsupported ${SRC}) - target_compile_options(codac-unsupported PUBLIC -w) + #target_compile_options(codac-unsupported PUBLIC -w) # todo: find a clean way to access codac header files? set(CODAC_HEADERS_DIR ${CMAKE_CURRENT_BINARY_DIR}/../../include) diff --git a/tests/catch/catch.hpp b/tests/catch/catch.hpp index ca410f41..ad8e1cd4 100644 --- a/tests/catch/catch.hpp +++ b/tests/catch/catch.hpp @@ -1,6 +1,6 @@ /* - * Catch v1.5.6 - * Generated: 2016-06-09 19:20:41.460328 + * Catch v1.12.2 + * Generated: 2025-01-20 23:39:35.565000 * ---------------------------------------------------------- * This file has been merged from multiple headers. Please don't edit it directly * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. @@ -40,6 +40,8 @@ #elif defined __GNUC__ # pragma GCC diagnostic ignored "-Wvariadic-macros" # pragma GCC diagnostic ignored "-Wunused-variable" +# pragma GCC diagnostic ignored "-Wparentheses" + # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wpadded" #endif @@ -60,21 +62,6 @@ // #included from: catch_common.h #define TWOBLUECUBES_CATCH_COMMON_H_INCLUDED -#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line -#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) -#ifdef CATCH_CONFIG_COUNTER -# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) -#else -# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) -#endif - -#define INTERNAL_CATCH_STRINGIFY2( expr ) #expr -#define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr ) - -#include -#include -#include - // #included from: catch_compiler_capabilities.h #define TWOBLUECUBES_CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED @@ -89,11 +76,15 @@ // CATCH_CONFIG_CPP11_LONG_LONG : is long long supported? // CATCH_CONFIG_CPP11_OVERRIDE : is override supported? // CATCH_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr) +// CATCH_CONFIG_CPP11_SHUFFLE : is std::shuffle supported? +// CATCH_CONFIG_CPP11_TYPE_TRAITS : are type_traits and enable_if supported? // CATCH_CONFIG_CPP11_OR_GREATER : Is C++11 supported? // CATCH_CONFIG_VARIADIC_MACROS : are variadic macros supported? // CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? +// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? +// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? // **************** // Note to maintainers: if new toggles are added please document them // in configuration.md, too @@ -129,11 +120,46 @@ # endif # if defined(CATCH_CPP11_OR_GREATER) -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) +# define CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ + _Pragma( "clang diagnostic push" ) \ + _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) +# define CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \ + _Pragma( "clang diagnostic pop" ) + +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + _Pragma( "clang diagnostic push" ) \ + _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) +# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ + _Pragma( "clang diagnostic pop" ) # endif #endif // __clang__ +//////////////////////////////////////////////////////////////////////////////// +// We know some environments not to support full POSIX signals +#if defined(__CYGWIN__) || defined(__QNX__) + +# if !defined(CATCH_CONFIG_POSIX_SIGNALS) +# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +# endif + +#endif + +#ifdef __OS400__ +# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +# define CATCH_CONFIG_COLOUR_NONE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Cygwin +#ifdef __CYGWIN__ + +// Required for some versions of Cygwin to declare gettimeofday +// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin +# define _BSD_SOURCE + +#endif // __CYGWIN__ + //////////////////////////////////////////////////////////////////////////////// // Borland #ifdef __BORLANDC__ @@ -160,10 +186,6 @@ # define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR # endif -# if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) && defined(CATCH_CPP11_OR_GREATER) -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS _Pragma( "GCC diagnostic ignored \"-Wparentheses\"" ) -# endif - // - otherwise more recent versions define __cplusplus >= 201103L // and will get picked up below @@ -173,6 +195,8 @@ // Visual C++ #ifdef _MSC_VER +#define CATCH_INTERNAL_CONFIG_WINDOWS_SEH + #if (_MSC_VER >= 1600) # define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR # define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR @@ -181,6 +205,8 @@ #if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015)) #define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT #define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS +#define CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE +#define CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS #endif #endif // _MSC_VER @@ -188,7 +214,7 @@ //////////////////////////////////////////////////////////////////////////////// // Use variadic macros if the compiler supports them -#if ( defined _MSC_VER && _MSC_VER > 1400 && !defined __EDGE__) || \ +#if ( defined _MSC_VER && _MSC_VER >= 1400 && !defined __EDGE__) || \ ( defined __WAVE__ && __WAVE_HAS_VARIADICS ) || \ ( defined __GNUC__ && __GNUC__ >= 3 ) || \ ( !defined __cplusplus && __STDC_VERSION__ >= 199901L || __cplusplus >= 201103L ) @@ -199,10 +225,15 @@ // Use __COUNTER__ if the compiler supports it #if ( defined _MSC_VER && _MSC_VER >= 1300 ) || \ - ( defined __GNUC__ && __GNUC__ >= 4 && __GNUC_MINOR__ >= 3 ) || \ + ( defined __GNUC__ && ( __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3 )) ) || \ ( defined __clang__ && __clang_major__ >= 3 ) -#define CATCH_INTERNAL_CONFIG_COUNTER +// Use of __COUNTER__ is suppressed during code analysis in CLion/AppCode 2017.2.x and former, +// because __COUNTER__ is not properly handled by it. +// This does not affect compilation +#if ( !defined __JETBRAINS_IDE__ || __JETBRAINS_IDE__ >= 20170300L ) + #define CATCH_INTERNAL_CONFIG_COUNTER +#endif #endif @@ -246,6 +277,12 @@ # if !defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) # define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR # endif +# if !defined(CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE) +# define CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE +# endif +# if !defined(CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS) +# define CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS +# endif #endif // __cplusplus >= 201103L @@ -268,21 +305,39 @@ #if defined(CATCH_INTERNAL_CONFIG_VARIADIC_MACROS) && !defined(CATCH_CONFIG_NO_VARIADIC_MACROS) && !defined(CATCH_CONFIG_VARIADIC_MACROS) # define CATCH_CONFIG_VARIADIC_MACROS #endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_CPP11) +#if defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_NO_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_CPP11) # define CATCH_CONFIG_CPP11_LONG_LONG #endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_CPP11) +#if defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_NO_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_CPP11) # define CATCH_CONFIG_CPP11_OVERRIDE #endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_CPP11) +#if defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_NO_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_CPP11) # define CATCH_CONFIG_CPP11_UNIQUE_PTR #endif #if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) # define CATCH_CONFIG_COUNTER #endif +#if defined(CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE) && !defined(CATCH_CONFIG_CPP11_NO_SHUFFLE) && !defined(CATCH_CONFIG_CPP11_SHUFFLE) && !defined(CATCH_CONFIG_NO_CPP11) +# define CATCH_CONFIG_CPP11_SHUFFLE +#endif +# if defined(CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS) && !defined(CATCH_CONFIG_CPP11_NO_TYPE_TRAITS) && !defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) && !defined(CATCH_CONFIG_NO_CPP11) +# define CATCH_CONFIG_CPP11_TYPE_TRAITS +# endif +#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) +# define CATCH_CONFIG_WINDOWS_SEH +#endif +// This is set by default, because we assume that unix compilers are posix-signal-compatible by default. +#if !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) +# define CATCH_CONFIG_POSIX_SIGNALS +#endif #if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS +# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS +# define CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS #endif // noexcept support: @@ -315,6 +370,20 @@ # define CATCH_AUTO_PTR( T ) std::auto_ptr #endif +#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line +#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) +#ifdef CATCH_CONFIG_COUNTER +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) +#else +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) +#endif + +#define INTERNAL_CATCH_STRINGIFY2( expr ) #expr +#define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr ) + +#include +#include + namespace Catch { struct IConfig; @@ -352,14 +421,14 @@ namespace Catch { }; template - inline void deleteAll( ContainerT& container ) { + void deleteAll( ContainerT& container ) { typename ContainerT::const_iterator it = container.begin(); typename ContainerT::const_iterator itEnd = container.end(); for(; it != itEnd; ++it ) delete *it; } template - inline void deleteAllValues( AssociativeContainerT& container ) { + void deleteAllValues( AssociativeContainerT& container ) { typename AssociativeContainerT::const_iterator it = container.begin(); typename AssociativeContainerT::const_iterator itEnd = container.end(); for(; it != itEnd; ++it ) @@ -367,7 +436,9 @@ namespace Catch { } bool startsWith( std::string const& s, std::string const& prefix ); + bool startsWith( std::string const& s, char prefix ); bool endsWith( std::string const& s, std::string const& suffix ); + bool endsWith( std::string const& s, char suffix ); bool contains( std::string const& s, std::string const& infix ); void toLowerInPlace( std::string& s ); std::string toLower( std::string const& s ); @@ -387,8 +458,8 @@ namespace Catch { SourceLineInfo(); SourceLineInfo( char const* _file, std::size_t _line ); - SourceLineInfo( SourceLineInfo const& other ); # ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS + SourceLineInfo(SourceLineInfo const& other) = default; SourceLineInfo( SourceLineInfo && ) = default; SourceLineInfo& operator = ( SourceLineInfo const& ) = default; SourceLineInfo& operator = ( SourceLineInfo && ) = default; @@ -397,7 +468,7 @@ namespace Catch { bool operator == ( SourceLineInfo const& other ) const; bool operator < ( SourceLineInfo const& other ) const; - std::string file; + char const* file; std::size_t line; }; @@ -431,15 +502,12 @@ namespace Catch { #define CATCH_INTERNAL_LINEINFO ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) #define CATCH_INTERNAL_ERROR( msg ) ::Catch::throwLogicError( msg, CATCH_INTERNAL_LINEINFO ); -#include - namespace Catch { class NotImplementedException : public std::exception { public: NotImplementedException( SourceLineInfo const& lineInfo ); - NotImplementedException( NotImplementedException const& ) {} virtual ~NotImplementedException() CATCH_NOEXCEPT {} @@ -565,10 +633,6 @@ namespace Catch { #pragma clang diagnostic pop #endif -#include -#include -#include - namespace Catch { class TestCase; @@ -669,7 +733,7 @@ struct NameAndDesc { void registerTestCase ( ITestCase* testCase, - char const* class_name, + char const* className, NameAndDesc const& nameAndDesc, SourceLineInfo const& lineInfo ); @@ -683,13 +747,13 @@ struct AutoReg { template AutoReg ( void (C::*method)(), - char const* class_name, + char const* className, NameAndDesc const& nameAndDesc, SourceLineInfo const& lineInfo ) { registerTestCase ( new MethodTestCase( method ), - class_name, + className, nameAndDesc, lineInfo ); } @@ -712,59 +776,76 @@ void registerTestCaseFunction /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \ static void TestName(); \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); }\ + CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); } /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \ static void TestName() #define INTERNAL_CATCH_TESTCASE( ... ) \ INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); } + CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); } /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\ + CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ namespace{ \ struct TestName : ClassName{ \ void test(); \ }; \ - Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestName::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestName::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); /* NOLINT */ \ } \ + CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \ void TestName::test() #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \ INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \ - Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); + CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ + Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS #else /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TESTCASE2( TestName, Name, Desc ) \ static void TestName(); \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); }\ + CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); } /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \ static void TestName() #define INTERNAL_CATCH_TESTCASE( Name, Desc ) \ INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), Name, Desc ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, Name, Desc ) \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( Name, Desc ), CATCH_INTERNAL_LINEINFO ); } + CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( Name, Desc ), CATCH_INTERNAL_LINEINFO ); } /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestCaseName, ClassName, TestName, Desc )\ + CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ namespace{ \ struct TestCaseName : ClassName{ \ void test(); \ }; \ - Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestCaseName::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestCaseName::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); /* NOLINT */ \ } \ + CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \ void TestCaseName::test() #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, TestName, Desc )\ INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, TestName, Desc ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, Name, Desc ) \ - Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); + CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ + Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS + #endif // #included from: internal/catch_capture.hpp @@ -832,27 +913,83 @@ namespace Catch { namespace Catch { + struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison; + + struct DecomposedExpression + { + virtual ~DecomposedExpression() {} + virtual bool isBinaryExpression() const { + return false; + } + virtual void reconstructExpression( std::string& dest ) const = 0; + + // Only simple binary comparisons can be decomposed. + // If more complex check is required then wrap sub-expressions in parentheses. + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator + ( T const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator - ( T const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator * ( T const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator / ( T const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator % ( T const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( T const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( T const& ); + + private: + DecomposedExpression& operator = (DecomposedExpression const&); + }; + struct AssertionInfo { - AssertionInfo() {} - AssertionInfo( std::string const& _macroName, + AssertionInfo(); + AssertionInfo( char const * _macroName, SourceLineInfo const& _lineInfo, - std::string const& _capturedExpression, - ResultDisposition::Flags _resultDisposition ); + char const * _capturedExpression, + ResultDisposition::Flags _resultDisposition, + char const * _secondArg = ""); - std::string macroName; + char const * macroName; SourceLineInfo lineInfo; - std::string capturedExpression; + char const * capturedExpression; ResultDisposition::Flags resultDisposition; + char const * secondArg; }; struct AssertionResultData { - AssertionResultData() : resultType( ResultWas::Unknown ) {} + AssertionResultData() : decomposedExpression( CATCH_NULL ) + , resultType( ResultWas::Unknown ) + , negated( false ) + , parenthesized( false ) {} + + void negate( bool parenthesize ) { + negated = !negated; + parenthesized = parenthesize; + if( resultType == ResultWas::Ok ) + resultType = ResultWas::ExpressionFailed; + else if( resultType == ResultWas::ExpressionFailed ) + resultType = ResultWas::Ok; + } + + std::string const& reconstructExpression() const { + if( decomposedExpression != CATCH_NULL ) { + decomposedExpression->reconstructExpression( reconstructedExpression ); + if( parenthesized ) { + reconstructedExpression.insert( 0, 1, '(' ); + reconstructedExpression.append( 1, ')' ); + } + if( negated ) { + reconstructedExpression.insert( 0, 1, '!' ); + } + decomposedExpression = CATCH_NULL; + } + return reconstructedExpression; + } - std::string reconstructedExpression; + mutable DecomposedExpression const* decomposedExpression; + mutable std::string reconstructedExpression; std::string message; ResultWas::OfType resultType; + bool negated; + bool parenthesized; }; class AssertionResult { @@ -879,6 +1016,8 @@ namespace Catch { std::string getMessage() const; SourceLineInfo getSourceInfo() const; std::string getTestMacroName() const; + void discardDecomposedExpression() const; + void expandDecomposedExpression() const; protected: AssertionInfo m_info; @@ -894,313 +1033,161 @@ namespace Catch { namespace Matchers { namespace Impl { - namespace Generic { - template class AllOf; - template class AnyOf; - template class Not; - } - - template - struct Matcher : SharedImpl - { - typedef ExpressionT ExpressionType; - - virtual ~Matcher() {} - virtual Ptr clone() const = 0; - virtual bool match( ExpressionT const& expr ) const = 0; - virtual std::string toString() const = 0; - - Generic::AllOf operator && ( Matcher const& other ) const; - Generic::AnyOf operator || ( Matcher const& other ) const; - Generic::Not operator ! () const; - }; - - template - struct MatcherImpl : Matcher { - - virtual Ptr > clone() const { - return Ptr >( new DerivedT( static_cast( *this ) ) ); - } - }; + template struct MatchAllOf; + template struct MatchAnyOf; + template struct MatchNotOf; - namespace Generic { - template - class Not : public MatcherImpl, ExpressionT> { + class MatcherUntypedBase { public: - explicit Not( Matcher const& matcher ) : m_matcher(matcher.clone()) {} - Not( Not const& other ) : m_matcher( other.m_matcher ) {} - - virtual bool match( ExpressionT const& expr ) const CATCH_OVERRIDE { - return !m_matcher->match( expr ); + std::string toString() const { + if( m_cachedToString.empty() ) + m_cachedToString = describe(); + return m_cachedToString; } - virtual std::string toString() const CATCH_OVERRIDE { - return "not " + m_matcher->toString(); - } + protected: + virtual ~MatcherUntypedBase(); + virtual std::string describe() const = 0; + mutable std::string m_cachedToString; private: - Ptr< Matcher > m_matcher; + MatcherUntypedBase& operator = ( MatcherUntypedBase const& ); }; - template - class AllOf : public MatcherImpl, ExpressionT> { - public: + template + struct MatcherMethod { + virtual bool match( ObjectT const& arg ) const = 0; + }; + template + struct MatcherMethod { + virtual bool match( PtrT* arg ) const = 0; + }; - AllOf() {} - AllOf( AllOf const& other ) : m_matchers( other.m_matchers ) {} + template + struct MatcherBase : MatcherUntypedBase, MatcherMethod { - AllOf& add( Matcher const& matcher ) { - m_matchers.push_back( matcher.clone() ); - return *this; - } - virtual bool match( ExpressionT const& expr ) const - { - for( std::size_t i = 0; i < m_matchers.size(); ++i ) - if( !m_matchers[i]->match( expr ) ) + MatchAllOf operator && ( MatcherBase const& other ) const; + MatchAnyOf operator || ( MatcherBase const& other ) const; + MatchNotOf operator ! () const; + }; + + template + struct MatchAllOf : MatcherBase { + virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE { + for( std::size_t i = 0; i < m_matchers.size(); ++i ) { + if (!m_matchers[i]->match(arg)) return false; + } return true; } - virtual std::string toString() const { - std::ostringstream oss; - oss << "( "; + virtual std::string describe() const CATCH_OVERRIDE { + std::string description; + description.reserve( 4 + m_matchers.size()*32 ); + description += "( "; for( std::size_t i = 0; i < m_matchers.size(); ++i ) { if( i != 0 ) - oss << " and "; - oss << m_matchers[i]->toString(); + description += " and "; + description += m_matchers[i]->toString(); } - oss << " )"; - return oss.str(); + description += " )"; + return description; } - AllOf operator && ( Matcher const& other ) const { - AllOf allOfExpr( *this ); - allOfExpr.add( other ); - return allOfExpr; + MatchAllOf& operator && ( MatcherBase const& other ) { + m_matchers.push_back( &other ); + return *this; } - private: - std::vector > > m_matchers; + std::vector const*> m_matchers; }; + template + struct MatchAnyOf : MatcherBase { - template - class AnyOf : public MatcherImpl, ExpressionT> { - public: - - AnyOf() {} - AnyOf( AnyOf const& other ) : m_matchers( other.m_matchers ) {} - - AnyOf& add( Matcher const& matcher ) { - m_matchers.push_back( matcher.clone() ); - return *this; - } - virtual bool match( ExpressionT const& expr ) const - { - for( std::size_t i = 0; i < m_matchers.size(); ++i ) - if( m_matchers[i]->match( expr ) ) + virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE { + for( std::size_t i = 0; i < m_matchers.size(); ++i ) { + if (m_matchers[i]->match(arg)) return true; + } return false; } - virtual std::string toString() const { - std::ostringstream oss; - oss << "( "; + virtual std::string describe() const CATCH_OVERRIDE { + std::string description; + description.reserve( 4 + m_matchers.size()*32 ); + description += "( "; for( std::size_t i = 0; i < m_matchers.size(); ++i ) { if( i != 0 ) - oss << " or "; - oss << m_matchers[i]->toString(); + description += " or "; + description += m_matchers[i]->toString(); } - oss << " )"; - return oss.str(); - } - - AnyOf operator || ( Matcher const& other ) const { - AnyOf anyOfExpr( *this ); - anyOfExpr.add( other ); - return anyOfExpr; - } - - private: - std::vector > > m_matchers; - }; - - } // namespace Generic - - template - Generic::AllOf Matcher::operator && ( Matcher const& other ) const { - Generic::AllOf allOfExpr; - allOfExpr.add( *this ); - allOfExpr.add( other ); - return allOfExpr; - } - - template - Generic::AnyOf Matcher::operator || ( Matcher const& other ) const { - Generic::AnyOf anyOfExpr; - anyOfExpr.add( *this ); - anyOfExpr.add( other ); - return anyOfExpr; - } - - template - Generic::Not Matcher::operator ! () const { - return Generic::Not( *this ); - } - - namespace StdString { - - inline std::string makeString( std::string const& str ) { return str; } - inline std::string makeString( const char* str ) { return str ? std::string( str ) : std::string(); } - - struct CasedString - { - CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ) - : m_caseSensitivity( caseSensitivity ), - m_str( adjustString( str ) ) - {} - std::string adjustString( std::string const& str ) const { - return m_caseSensitivity == CaseSensitive::No - ? toLower( str ) - : str; - - } - std::string toStringSuffix() const - { - return m_caseSensitivity == CaseSensitive::No - ? " (case insensitive)" - : ""; + description += " )"; + return description; } - CaseSensitive::Choice m_caseSensitivity; - std::string m_str; - }; - - struct Equals : MatcherImpl { - Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) - : m_data( str, caseSensitivity ) - {} - Equals( Equals const& other ) : m_data( other.m_data ){} - virtual ~Equals(); - - virtual bool match( std::string const& expr ) const { - return m_data.m_str == m_data.adjustString( expr );; - } - virtual std::string toString() const { - return "equals: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); + MatchAnyOf& operator || ( MatcherBase const& other ) { + m_matchers.push_back( &other ); + return *this; } - CasedString m_data; + std::vector const*> m_matchers; }; - struct Contains : MatcherImpl { - Contains( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) - : m_data( substr, caseSensitivity ){} - Contains( Contains const& other ) : m_data( other.m_data ){} + template + struct MatchNotOf : MatcherBase { - virtual ~Contains(); + MatchNotOf( MatcherBase const& underlyingMatcher ) : m_underlyingMatcher( underlyingMatcher ) {} - virtual bool match( std::string const& expr ) const { - return m_data.adjustString( expr ).find( m_data.m_str ) != std::string::npos; - } - virtual std::string toString() const { - return "contains: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); + virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE { + return !m_underlyingMatcher.match( arg ); } - CasedString m_data; - }; - - struct StartsWith : MatcherImpl { - StartsWith( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) - : m_data( substr, caseSensitivity ){} - - StartsWith( StartsWith const& other ) : m_data( other.m_data ){} - - virtual ~StartsWith(); - - virtual bool match( std::string const& expr ) const { - return startsWith( m_data.adjustString( expr ), m_data.m_str ); - } - virtual std::string toString() const { - return "starts with: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); + virtual std::string describe() const CATCH_OVERRIDE { + return "not " + m_underlyingMatcher.toString(); } - - CasedString m_data; + MatcherBase const& m_underlyingMatcher; }; - struct EndsWith : MatcherImpl { - EndsWith( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) - : m_data( substr, caseSensitivity ){} - EndsWith( EndsWith const& other ) : m_data( other.m_data ){} - - virtual ~EndsWith(); - - virtual bool match( std::string const& expr ) const { - return endsWith( m_data.adjustString( expr ), m_data.m_str ); - } - virtual std::string toString() const { - return "ends with: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); - } + template + MatchAllOf MatcherBase::operator && ( MatcherBase const& other ) const { + return MatchAllOf() && *this && other; + } + template + MatchAnyOf MatcherBase::operator || ( MatcherBase const& other ) const { + return MatchAnyOf() || *this || other; + } + template + MatchNotOf MatcherBase::operator ! () const { + return MatchNotOf( *this ); + } - CasedString m_data; - }; - } // namespace StdString } // namespace Impl // The following functions create the actual matcher objects. // This allows the types to be inferred - template - inline Impl::Generic::Not Not( Impl::Matcher const& m ) { - return Impl::Generic::Not( m ); - } - - template - inline Impl::Generic::AllOf AllOf( Impl::Matcher const& m1, - Impl::Matcher const& m2 ) { - return Impl::Generic::AllOf().add( m1 ).add( m2 ); - } - template - inline Impl::Generic::AllOf AllOf( Impl::Matcher const& m1, - Impl::Matcher const& m2, - Impl::Matcher const& m3 ) { - return Impl::Generic::AllOf().add( m1 ).add( m2 ).add( m3 ); - } - template - inline Impl::Generic::AnyOf AnyOf( Impl::Matcher const& m1, - Impl::Matcher const& m2 ) { - return Impl::Generic::AnyOf().add( m1 ).add( m2 ); - } - template - inline Impl::Generic::AnyOf AnyOf( Impl::Matcher const& m1, - Impl::Matcher const& m2, - Impl::Matcher const& m3 ) { - return Impl::Generic::AnyOf().add( m1 ).add( m2 ).add( m3 ); - } - - inline Impl::StdString::Equals Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) { - return Impl::StdString::Equals( str, caseSensitivity ); - } - inline Impl::StdString::Equals Equals( const char* str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) { - return Impl::StdString::Equals( Impl::StdString::makeString( str ), caseSensitivity ); - } - inline Impl::StdString::Contains Contains( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) { - return Impl::StdString::Contains( substr, caseSensitivity ); - } - inline Impl::StdString::Contains Contains( const char* substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) { - return Impl::StdString::Contains( Impl::StdString::makeString( substr ), caseSensitivity ); + // - deprecated: prefer ||, && and ! + template + Impl::MatchNotOf Not( Impl::MatcherBase const& underlyingMatcher ) { + return Impl::MatchNotOf( underlyingMatcher ); } - inline Impl::StdString::StartsWith StartsWith( std::string const& substr ) { - return Impl::StdString::StartsWith( substr ); + template + Impl::MatchAllOf AllOf( Impl::MatcherBase const& m1, Impl::MatcherBase const& m2 ) { + return Impl::MatchAllOf() && m1 && m2; } - inline Impl::StdString::StartsWith StartsWith( const char* substr ) { - return Impl::StdString::StartsWith( Impl::StdString::makeString( substr ) ); + template + Impl::MatchAllOf AllOf( Impl::MatcherBase const& m1, Impl::MatcherBase const& m2, Impl::MatcherBase const& m3 ) { + return Impl::MatchAllOf() && m1 && m2 && m3; } - inline Impl::StdString::EndsWith EndsWith( std::string const& substr ) { - return Impl::StdString::EndsWith( substr ); + template + Impl::MatchAnyOf AnyOf( Impl::MatcherBase const& m1, Impl::MatcherBase const& m2 ) { + return Impl::MatchAnyOf() || m1 || m2; } - inline Impl::StdString::EndsWith EndsWith( const char* substr ) { - return Impl::StdString::EndsWith( Impl::StdString::makeString( substr ) ); + template + Impl::MatchAnyOf AnyOf( Impl::MatcherBase const& m1, Impl::MatcherBase const& m2, Impl::MatcherBase const& m3 ) { + return Impl::MatchAnyOf() || m1 || m2 || m3; } } // namespace Matchers using namespace Matchers; +using Matchers::Impl::MatcherBase; } // namespace Catch @@ -1210,28 +1197,27 @@ namespace Catch { template class ExpressionLhs; - struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison; - struct CopyableStream { CopyableStream() {} CopyableStream( CopyableStream const& other ) { oss << other.oss.str(); } CopyableStream& operator=( CopyableStream const& other ) { - oss.str(""); + oss.str(std::string()); oss << other.oss.str(); return *this; } std::ostringstream oss; }; - class ResultBuilder { + class ResultBuilder : public DecomposedExpression { public: ResultBuilder( char const* macroName, SourceLineInfo const& lineInfo, char const* capturedExpression, ResultDisposition::Flags resultDisposition, char const* secondArg = "" ); + ~ResultBuilder(); template ExpressionLhs operator <= ( T const& operand ); @@ -1239,46 +1225,60 @@ namespace Catch { template ResultBuilder& operator << ( T const& value ) { - m_stream.oss << value; + stream().oss << value; return *this; } - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& ); - ResultBuilder& setResultType( ResultWas::OfType result ); ResultBuilder& setResultType( bool result ); - ResultBuilder& setLhs( std::string const& lhs ); - ResultBuilder& setRhs( std::string const& rhs ); - ResultBuilder& setOp( std::string const& op ); - void endExpression(); + void endExpression( DecomposedExpression const& expr ); + + virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE; - std::string reconstructExpression() const; AssertionResult build() const; + AssertionResult build( DecomposedExpression const& expr ) const; void useActiveException( ResultDisposition::Flags resultDisposition = ResultDisposition::Normal ); void captureResult( ResultWas::OfType resultType ); void captureExpression(); void captureExpectedException( std::string const& expectedMessage ); - void captureExpectedException( Matchers::Impl::Matcher const& matcher ); + void captureExpectedException( Matchers::Impl::MatcherBase const& matcher ); void handleResult( AssertionResult const& result ); void react(); bool shouldDebugBreak() const; bool allowThrows() const; + template + void captureMatch( ArgT const& arg, MatcherT const& matcher, char const* matcherString ); + + void setExceptionGuard(); + void unsetExceptionGuard(); + private: AssertionInfo m_assertionInfo; AssertionResultData m_data; - struct ExprComponents { - ExprComponents() : testFalse( false ) {} - bool testFalse; - std::string lhs, rhs, op; - } m_exprComponents; - CopyableStream m_stream; + + CopyableStream &stream() + { + if(!m_usedStream) + { + m_usedStream = true; + m_stream().oss.str(""); + } + return m_stream(); + } + + static CopyableStream &m_stream() + { + static CopyableStream s; + return s; + } bool m_shouldDebugBreak; bool m_shouldThrow; + bool m_guardException; + bool m_usedStream; }; } // namespace Catch @@ -1293,6 +1293,8 @@ namespace Catch { #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable:4389) // '==' : signed/unsigned mismatch +#pragma warning(disable:4018) // more "signed/unsigned mismatch" +#pragma warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform) #endif #include @@ -1318,7 +1320,7 @@ namespace Internal { template<> struct OperatorTraits{ static const char* getName(){ return ">="; } }; template - inline T& opCast(T const& t) { return const_cast(t); } + T& opCast(T const& t) { return const_cast(t); } // nullptr_t support based on pull request #154 from Konstantin Baumann #ifdef CATCH_CONFIG_CPP11_NULLPTR @@ -1328,7 +1330,7 @@ namespace Internal { // So the compare overloads can be operator agnostic we convey the operator as a template // enum, which is used to specialise an Evaluator for doing the comparison. template - class Evaluator{}; + struct Evaluator{}; template struct Evaluator { @@ -1594,7 +1596,7 @@ std::string toString( std::nullptr_t ); #ifdef __OBJC__ std::string toString( NSString const * const& nsstring ); - std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ); + std::string toString( NSString * CATCH_ARC_STRONG & nsstring ); std::string toString( NSObject* const& nsObject ); #endif @@ -1602,6 +1604,7 @@ namespace Detail { extern const std::string unprintableString; + #if !defined(CATCH_CONFIG_CPP11_STREAM_INSERTABLE_CHECK) struct BorgType { template BorgType( T const& ); }; @@ -1620,6 +1623,20 @@ namespace Detail { static T const&t; enum { value = sizeof( testStreamable(s << t) ) == sizeof( TrueType ) }; }; +#else + template + class IsStreamInsertable { + template + static auto test(int) + -> decltype( std::declval() << std::declval(), std::true_type() ); + + template + static auto test(...) -> std::false_type; + + public: + static const bool value = decltype(test(0))::value; + }; +#endif #if defined(CATCH_CONFIG_CPP11_IS_ENUM) template - inline std::string rawMemoryToString( const T& object ) { + std::string rawMemoryToString( const T& object ) { return rawMemoryToString( &object, sizeof(object) ); } @@ -1796,90 +1813,159 @@ std::string toString( T const& value ) { namespace Catch { -// Wraps the LHS of an expression and captures the operator and RHS (if any) - -// wrapping them all in a ResultBuilder object -template -class ExpressionLhs { - ExpressionLhs& operator = ( ExpressionLhs const& ); -# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - ExpressionLhs& operator = ( ExpressionLhs && ) = delete; -# endif +template +class BinaryExpression; + +template +class MatchExpression; +// Wraps the LHS of an expression and overloads comparison operators +// for also capturing those and RHS (if any) +template +class ExpressionLhs : public DecomposedExpression { public: - ExpressionLhs( ResultBuilder& rb, T lhs ) : m_rb( rb ), m_lhs( lhs ) {} -# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - ExpressionLhs( ExpressionLhs const& ) = default; - ExpressionLhs( ExpressionLhs && ) = default; -# endif + ExpressionLhs( ResultBuilder& rb, T lhs ) : m_rb( rb ), m_lhs( lhs ), m_truthy(false) {} + + ExpressionLhs& operator = ( const ExpressionLhs& ); template - ResultBuilder& operator == ( RhsT const& rhs ) { + BinaryExpression + operator == ( RhsT const& rhs ) { return captureExpression( rhs ); } template - ResultBuilder& operator != ( RhsT const& rhs ) { + BinaryExpression + operator != ( RhsT const& rhs ) { return captureExpression( rhs ); } template - ResultBuilder& operator < ( RhsT const& rhs ) { + BinaryExpression + operator < ( RhsT const& rhs ) { return captureExpression( rhs ); } template - ResultBuilder& operator > ( RhsT const& rhs ) { + BinaryExpression + operator > ( RhsT const& rhs ) { return captureExpression( rhs ); } template - ResultBuilder& operator <= ( RhsT const& rhs ) { + BinaryExpression + operator <= ( RhsT const& rhs ) { return captureExpression( rhs ); } template - ResultBuilder& operator >= ( RhsT const& rhs ) { + BinaryExpression + operator >= ( RhsT const& rhs ) { return captureExpression( rhs ); } - ResultBuilder& operator == ( bool rhs ) { + BinaryExpression operator == ( bool rhs ) { return captureExpression( rhs ); } - ResultBuilder& operator != ( bool rhs ) { + BinaryExpression operator != ( bool rhs ) { return captureExpression( rhs ); } void endExpression() { - bool value = m_lhs ? true : false; + m_truthy = m_lhs ? true : false; m_rb - .setLhs( Catch::toString( value ) ) - .setResultType( value ) - .endExpression(); + .setResultType( m_truthy ) + .endExpression( *this ); } - // Only simple binary expressions are allowed on the LHS. - // If more complex compositions are required then place the sub expression in parentheses - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator + ( RhsT const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator - ( RhsT const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator / ( RhsT const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator * ( RhsT const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& ); + virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE { + dest = Catch::toString( m_lhs ); + } private: template - ResultBuilder& captureExpression( RhsT const& rhs ) { - return m_rb - .setResultType( Internal::compare( m_lhs, rhs ) ) - .setLhs( Catch::toString( m_lhs ) ) - .setRhs( Catch::toString( rhs ) ) - .setOp( Internal::OperatorTraits::getName() ); + BinaryExpression captureExpression( RhsT& rhs ) const { + return BinaryExpression( m_rb, m_lhs, rhs ); + } + + template + BinaryExpression captureExpression( bool rhs ) const { + return BinaryExpression( m_rb, m_lhs, rhs ); } private: ResultBuilder& m_rb; T m_lhs; + bool m_truthy; +}; + +template +class BinaryExpression : public DecomposedExpression { +public: + BinaryExpression( ResultBuilder& rb, LhsT lhs, RhsT rhs ) + : m_rb( rb ), m_lhs( lhs ), m_rhs( rhs ) {} + + BinaryExpression& operator = ( BinaryExpression& ); + + void endExpression() const { + m_rb + .setResultType( Internal::compare( m_lhs, m_rhs ) ) + .endExpression( *this ); + } + + virtual bool isBinaryExpression() const CATCH_OVERRIDE { + return true; + } + + virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE { + std::string lhs = Catch::toString( m_lhs ); + std::string rhs = Catch::toString( m_rhs ); + char delim = lhs.size() + rhs.size() < 40 && + lhs.find('\n') == std::string::npos && + rhs.find('\n') == std::string::npos ? ' ' : '\n'; + dest.reserve( 7 + lhs.size() + rhs.size() ); + // 2 for spaces around operator + // 2 for operator + // 2 for parentheses (conditionally added later) + // 1 for negation (conditionally added later) + dest = lhs; + dest += delim; + dest += Internal::OperatorTraits::getName(); + dest += delim; + dest += rhs; + } + +private: + ResultBuilder& m_rb; + LhsT m_lhs; + RhsT m_rhs; +}; + +template +class MatchExpression : public DecomposedExpression { +public: + MatchExpression( ArgT arg, MatcherT matcher, char const* matcherString ) + : m_arg( arg ), m_matcher( matcher ), m_matcherString( matcherString ) {} + + virtual bool isBinaryExpression() const CATCH_OVERRIDE { + return true; + } + + virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE { + std::string matcherAsString = m_matcher.toString(); + dest = Catch::toString( m_arg ); + dest += ' '; + if( matcherAsString == Detail::unprintableString ) + dest += m_matcherString; + else + dest += matcherAsString; + } + +private: + ArgT m_arg; + MatcherT m_matcher; + char const* m_matcherString; }; } // end namespace Catch @@ -1888,7 +1974,7 @@ class ExpressionLhs { namespace Catch { template - inline ExpressionLhs ResultBuilder::operator <= ( T const& operand ) { + ExpressionLhs ResultBuilder::operator <= ( T const& operand ) { return ExpressionLhs( *this, operand ); } @@ -1896,6 +1982,14 @@ namespace Catch { return ExpressionLhs( *this, value ); } + template + void ResultBuilder::captureMatch( ArgT const& arg, MatcherT const& matcher, + char const* matcherString ) { + MatchExpression expr( arg, matcher, matcherString ); + setResultType( matcher.match( arg ) ); + endExpression( expr ); + } + } // namespace Catch // #included from: catch_message.h @@ -1985,7 +2079,13 @@ namespace Catch { virtual std::string getCurrentTestName() const = 0; virtual const AssertionResult* getLastResult() const = 0; + virtual void exceptionEarlyReported() = 0; + virtual void handleFatalErrorCondition( std::string const& message ) = 0; + + virtual bool lastAssertionPassed() = 0; + virtual void assertionPassed() = 0; + virtual void assertionRun() = 0; }; IResultCapture& getResultCapture(); @@ -1998,11 +2098,19 @@ namespace Catch { #define TWOBLUECUBES_CATCH_PLATFORM_H_INCLUDED #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) -#define CATCH_PLATFORM_MAC +# define CATCH_PLATFORM_MAC #elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED) -#define CATCH_PLATFORM_IPHONE +# define CATCH_PLATFORM_IPHONE +#elif defined(linux) || defined(__linux) || defined(__linux__) +# define CATCH_PLATFORM_LINUX #elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) -#define CATCH_PLATFORM_WINDOWS +# define CATCH_PLATFORM_WINDOWS +# if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX) +# define CATCH_DEFINES_NOMINMAX +# endif +# if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN) +# define CATCH_DEFINES_WIN32_LEAN_AND_MEAN +# endif #endif #include @@ -2017,27 +2125,39 @@ namespace Catch{ // The following code snippet based on: // http://cocoawithlove.com/2008/03/break-into-debugger.html - #ifdef DEBUG - #if defined(__ppc64__) || defined(__ppc__) - #define CATCH_BREAK_INTO_DEBUGGER() \ - if( Catch::isDebuggerActive() ) { \ - __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \ - : : : "memory","r0","r3","r4" ); \ - } - #else - #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) {__asm__("int $3\n" : : );} - #endif + #if defined(__ppc64__) || defined(__ppc__) + #define CATCH_TRAP() \ + __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \ + : : : "memory","r0","r3","r4" ) /* NOLINT */ + #elif defined(__aarch64__) + // Backport of https://github.com/catchorg/Catch2/commit/a25c1a24af8bffd35727a888a307ff0280cf9387 + #define CATCH_TRAP() __asm__(".inst 0xd4200000") + #else + #define CATCH_TRAP() __asm__("int $3\n" : : /* NOLINT */ ) #endif +#elif defined(CATCH_PLATFORM_LINUX) + // If we can use inline assembler, do it because this allows us to break + // directly at the location of the failing check instead of breaking inside + // raise() called from it, i.e. one stack frame below. + #if defined(__GNUC__) && (defined(__i386) || defined(__x86_64)) + #define CATCH_TRAP() asm volatile ("int $3") /* NOLINT */ + #else // Fall back to the generic way. + #include + + #define CATCH_TRAP() raise(SIGTRAP) + #endif #elif defined(_MSC_VER) - #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { __debugbreak(); } + #define CATCH_TRAP() __debugbreak() #elif defined(__MINGW32__) extern "C" __declspec(dllimport) void __stdcall DebugBreak(); - #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { DebugBreak(); } + #define CATCH_TRAP() DebugBreak() #endif -#ifndef CATCH_BREAK_INTO_DEBUGGER -#define CATCH_BREAK_INTO_DEBUGGER() Catch::alwaysTrue(); +#ifdef CATCH_TRAP + #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { CATCH_TRAP(); } +#else + #define CATCH_BREAK_INTO_DEBUGGER() Catch::alwaysTrue(); #endif // #included from: catch_interfaces_runner.h @@ -2052,45 +2172,89 @@ namespace Catch { }; } +#if !defined(CATCH_CONFIG_DISABLE_STRINGIFICATION) +# define CATCH_INTERNAL_STRINGIFY(expr) #expr +#else +# define CATCH_INTERNAL_STRINGIFY(expr) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION" +#endif + +#if defined(CATCH_CONFIG_FAST_COMPILE) /////////////////////////////////////////////////////////////////////////////// -// In the event of a failure works out if the debugger needs to be invoked -// and/or an exception thrown and takes appropriate action. -// This needs to be done as a macro so the debugger will stop in the user -// source code rather than in Catch library code +// We can speedup compilation significantly by breaking into debugger lower in +// the callstack, because then we don't have to expand CATCH_BREAK_INTO_DEBUGGER +// macro in each assertion #define INTERNAL_CATCH_REACT( resultBuilder ) \ - if( resultBuilder.shouldDebugBreak() ) CATCH_BREAK_INTO_DEBUGGER(); \ resultBuilder.react(); /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ) \ +// Another way to speed-up compilation is to omit local try-catch for REQUIRE* +// macros. +// This can potentially cause false negative, if the test code catches +// the exception before it propagates back up to the runner. +#define INTERNAL_CATCH_TEST_NO_TRY( macroName, resultDisposition, expr ) \ do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ - try { \ - CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ - ( __catchResult <= expr ).endExpression(); \ - } \ - catch( ... ) { \ - __catchResult.useActiveException( Catch::ResultDisposition::Normal ); \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr), resultDisposition ); \ + __catchResult.setExceptionGuard(); \ + CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + ( __catchResult <= expr ).endExpression(); \ + CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ + __catchResult.unsetExceptionGuard(); \ + INTERNAL_CATCH_REACT( __catchResult ) \ + } while( Catch::isTrue( false && static_cast( !!(expr) ) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look +// The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&. + +#define INTERNAL_CHECK_THAT_NO_TRY( macroName, matcher, resultDisposition, arg ) \ + do { \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(arg) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ + __catchResult.setExceptionGuard(); \ + __catchResult.captureMatch( arg, matcher, CATCH_INTERNAL_STRINGIFY(matcher) ); \ + __catchResult.unsetExceptionGuard(); \ + INTERNAL_CATCH_REACT( __catchResult ) \ + } while( Catch::alwaysFalse() ) + +#else +/////////////////////////////////////////////////////////////////////////////// +// In the event of a failure works out if the debugger needs to be invoked +// and/or an exception thrown and takes appropriate action. +// This needs to be done as a macro so the debugger will stop in the user +// source code rather than in Catch library code +#define INTERNAL_CATCH_REACT( resultBuilder ) \ + if( resultBuilder.shouldDebugBreak() ) CATCH_BREAK_INTO_DEBUGGER(); \ + resultBuilder.react(); +#endif + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_TEST( macroName, resultDisposition, expr ) \ + do { \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr), resultDisposition ); \ + try { \ + CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + ( __catchResult <= expr ).endExpression(); \ + CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ + } \ + catch( ... ) { \ + __catchResult.useActiveException( resultDisposition ); \ } \ INTERNAL_CATCH_REACT( __catchResult ) \ - } while( Catch::isTrue( false && !!(expr) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look + } while( Catch::isTrue( false && static_cast( !!(expr) ) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look + // The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&. /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_IF( expr, resultDisposition, macroName ) \ - INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \ - if( Catch::getResultCapture().getLastResult()->succeeded() ) +#define INTERNAL_CATCH_IF( macroName, resultDisposition, expr ) \ + INTERNAL_CATCH_TEST( macroName, resultDisposition, expr ); \ + if( Catch::getResultCapture().lastAssertionPassed() ) /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_ELSE( expr, resultDisposition, macroName ) \ - INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \ - if( !Catch::getResultCapture().getLastResult()->succeeded() ) +#define INTERNAL_CATCH_ELSE( macroName, resultDisposition, expr ) \ + INTERNAL_CATCH_TEST( macroName, resultDisposition, expr ); \ + if( !Catch::getResultCapture().lastAssertionPassed() ) /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_NO_THROW( expr, resultDisposition, macroName ) \ +#define INTERNAL_CATCH_NO_THROW( macroName, resultDisposition, expr ) \ do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr), resultDisposition ); \ try { \ - expr; \ + static_cast(expr); \ __catchResult.captureResult( Catch::ResultWas::Ok ); \ } \ catch( ... ) { \ @@ -2100,12 +2264,12 @@ namespace Catch { } while( Catch::alwaysFalse() ) /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_THROWS( expr, resultDisposition, matcher, macroName ) \ +#define INTERNAL_CATCH_THROWS( macroName, resultDisposition, matcher, expr ) \ do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition, #matcher ); \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr), resultDisposition, CATCH_INTERNAL_STRINGIFY(matcher) ); \ if( __catchResult.allowThrows() ) \ try { \ - expr; \ + static_cast(expr); \ __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \ } \ catch( ... ) { \ @@ -2117,12 +2281,12 @@ namespace Catch { } while( Catch::alwaysFalse() ) /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_THROWS_AS( expr, exceptionType, resultDisposition, macroName ) \ +#define INTERNAL_CATCH_THROWS_AS( macroName, exceptionType, resultDisposition, expr ) \ do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr) ", " CATCH_INTERNAL_STRINGIFY(exceptionType), resultDisposition ); \ if( __catchResult.allowThrows() ) \ try { \ - expr; \ + static_cast(expr); \ __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \ } \ catch( exceptionType ) { \ @@ -2138,7 +2302,7 @@ namespace Catch { /////////////////////////////////////////////////////////////////////////////// #ifdef CATCH_CONFIG_VARIADIC_MACROS - #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, ... ) \ + #define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, ... ) \ do { \ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ __catchResult << __VA_ARGS__ + ::Catch::StreamEndStop(); \ @@ -2146,7 +2310,7 @@ namespace Catch { INTERNAL_CATCH_REACT( __catchResult ) \ } while( Catch::alwaysFalse() ) #else - #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, log ) \ + #define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, log ) \ do { \ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ __catchResult << log + ::Catch::StreamEndStop(); \ @@ -2156,21 +2320,15 @@ namespace Catch { #endif /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_INFO( log, macroName ) \ +#define INTERNAL_CATCH_INFO( macroName, log ) \ Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage ) = Catch::MessageBuilder( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log; /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CHECK_THAT( arg, matcher, resultDisposition, macroName ) \ +#define INTERNAL_CHECK_THAT( macroName, matcher, resultDisposition, arg ) \ do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg ", " #matcher, resultDisposition ); \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(arg) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ try { \ - std::string matcherAsString = (matcher).toString(); \ - __catchResult \ - .setLhs( Catch::toString( arg ) ) \ - .setRhs( matcherAsString == Catch::Detail::unprintableString ? #matcher : matcherAsString ) \ - .setOp( "matches" ) \ - .setResultType( (matcher).match( arg ) ); \ - __catchResult.captureExpression(); \ + __catchResult.captureMatch( arg, matcher, CATCH_INTERNAL_STRINGIFY(matcher) ); \ } catch( ... ) { \ __catchResult.useActiveException( resultDisposition | Catch::ResultDisposition::ContinueOnFailure ); \ } \ @@ -2253,6 +2411,8 @@ namespace Catch { }; } +#include + namespace Catch { struct SectionInfo { @@ -2281,14 +2441,19 @@ namespace Catch { // #included from: catch_timer.h #define TWOBLUECUBES_CATCH_TIMER_H_INCLUDED -#ifdef CATCH_PLATFORM_WINDOWS -typedef unsigned long long uint64_t; +#ifdef _MSC_VER + +namespace Catch { + typedef unsigned long long UInt64; +} #else #include +namespace Catch { + typedef uint64_t UInt64; +} #endif namespace Catch { - class Timer { public: Timer() : m_ticks( 0 ) {} @@ -2298,7 +2463,7 @@ namespace Catch { double getElapsedSeconds() const; private: - uint64_t m_ticks; + UInt64 m_ticks; }; } // namespace Catch @@ -2337,7 +2502,6 @@ namespace Catch { // #included from: internal/catch_generators.hpp #define TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED -#include #include #include #include @@ -2451,7 +2615,7 @@ class CompositeGenerator { private: void move( CompositeGenerator& other ) { - std::copy( other.m_composed.begin(), other.m_composed.end(), std::back_inserter( m_composed ) ); + m_composed.insert( m_composed.end(), other.m_composed.begin(), other.m_composed.end() ); m_totalSize += other.m_totalSize; other.m_composed.clear(); } @@ -2533,12 +2697,15 @@ namespace Catch { struct IExceptionTranslator; struct IReporterRegistry; struct IReporterFactory; + struct ITagAliasRegistry; struct IRegistryHub { virtual ~IRegistryHub(); virtual IReporterRegistry const& getReporterRegistry() const = 0; virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0; + virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0; + virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() = 0; }; @@ -2548,6 +2715,7 @@ namespace Catch { virtual void registerListener( Ptr const& factory ) = 0; virtual void registerTest( TestCase const& testInfo ) = 0; virtual void registerTranslator( const IExceptionTranslator* translator ) = 0; + virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0; }; IRegistryHub& getRegistryHub(); @@ -2623,6 +2791,10 @@ namespace Catch { #include #include +#if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) +#include +#endif + namespace Catch { namespace Detail { @@ -2630,30 +2802,112 @@ namespace Detail { public: explicit Approx ( double value ) : m_epsilon( std::numeric_limits::epsilon()*100 ), + m_margin( 0.0 ), m_scale( 1.0 ), m_value( value ) {} - Approx( Approx const& other ) - : m_epsilon( other.m_epsilon ), - m_scale( other.m_scale ), - m_value( other.m_value ) - {} - static Approx custom() { return Approx( 0 ); } +#if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) + + template ::value>::type> + Approx operator()( T value ) { + Approx approx( static_cast(value) ); + approx.epsilon( m_epsilon ); + approx.margin( m_margin ); + approx.scale( m_scale ); + return approx; + } + + template ::value>::type> + explicit Approx( T value ): Approx(static_cast(value)) + {} + + template ::value>::type> + friend bool operator == ( const T& lhs, Approx const& rhs ) { + // Thanks to Richard Harris for his help refining this formula + auto lhs_v = double(lhs); + bool relativeOK = std::fabs(lhs_v - rhs.m_value) < rhs.m_epsilon * (rhs.m_scale + (std::max)(std::fabs(lhs_v), std::fabs(rhs.m_value))); + if (relativeOK) { + return true; + } + + return std::fabs(lhs_v - rhs.m_value) <= rhs.m_margin; + } + + template ::value>::type> + friend bool operator == ( Approx const& lhs, const T& rhs ) { + return operator==( rhs, lhs ); + } + + template ::value>::type> + friend bool operator != ( T lhs, Approx const& rhs ) { + return !operator==( lhs, rhs ); + } + + template ::value>::type> + friend bool operator != ( Approx const& lhs, T rhs ) { + return !operator==( rhs, lhs ); + } + + template ::value>::type> + friend bool operator <= ( T lhs, Approx const& rhs ) { + return double(lhs) < rhs.m_value || lhs == rhs; + } + + template ::value>::type> + friend bool operator <= ( Approx const& lhs, T rhs ) { + return lhs.m_value < double(rhs) || lhs == rhs; + } + + template ::value>::type> + friend bool operator >= ( T lhs, Approx const& rhs ) { + return double(lhs) > rhs.m_value || lhs == rhs; + } + + template ::value>::type> + friend bool operator >= ( Approx const& lhs, T rhs ) { + return lhs.m_value > double(rhs) || lhs == rhs; + } + + template ::value>::type> + Approx& epsilon( T newEpsilon ) { + m_epsilon = double(newEpsilon); + return *this; + } + + template ::value>::type> + Approx& margin( T newMargin ) { + m_margin = double(newMargin); + return *this; + } + + template ::value>::type> + Approx& scale( T newScale ) { + m_scale = double(newScale); + return *this; + } + +#else + Approx operator()( double value ) { Approx approx( value ); approx.epsilon( m_epsilon ); + approx.margin( m_margin ); approx.scale( m_scale ); return approx; } friend bool operator == ( double lhs, Approx const& rhs ) { // Thanks to Richard Harris for his help refining this formula - return fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( fabs(lhs), fabs(rhs.m_value) ) ); + bool relativeOK = std::fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( std::fabs(lhs), std::fabs(rhs.m_value) ) ); + if (relativeOK) { + return true; + } + return std::fabs(lhs - rhs.m_value) <= rhs.m_margin; } friend bool operator == ( Approx const& lhs, double rhs ) { @@ -2668,15 +2922,37 @@ namespace Detail { return !operator==( rhs, lhs ); } + friend bool operator <= ( double lhs, Approx const& rhs ) { + return lhs < rhs.m_value || lhs == rhs; + } + + friend bool operator <= ( Approx const& lhs, double rhs ) { + return lhs.m_value < rhs || lhs == rhs; + } + + friend bool operator >= ( double lhs, Approx const& rhs ) { + return lhs > rhs.m_value || lhs == rhs; + } + + friend bool operator >= ( Approx const& lhs, double rhs ) { + return lhs.m_value > rhs || lhs == rhs; + } + Approx& epsilon( double newEpsilon ) { m_epsilon = newEpsilon; return *this; } + Approx& margin( double newMargin ) { + m_margin = newMargin; + return *this; + } + Approx& scale( double newScale ) { m_scale = newScale; return *this; } +#endif std::string toString() const { std::ostringstream oss; @@ -2686,6 +2962,7 @@ namespace Detail { private: double m_epsilon; + double m_margin; double m_scale; double m_value; }; @@ -2698,6 +2975,153 @@ inline std::string toString( Detail::Approx const& value ) { } // end namespace Catch +// #included from: internal/catch_matchers_string.h +#define TWOBLUECUBES_CATCH_MATCHERS_STRING_H_INCLUDED + +namespace Catch { +namespace Matchers { + + namespace StdString { + + struct CasedString + { + CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ); + std::string adjustString( std::string const& str ) const; + std::string caseSensitivitySuffix() const; + + CaseSensitive::Choice m_caseSensitivity; + std::string m_str; + }; + + struct StringMatcherBase : MatcherBase { + StringMatcherBase( std::string const& operation, CasedString const& comparator ); + virtual std::string describe() const CATCH_OVERRIDE; + + CasedString m_comparator; + std::string m_operation; + }; + + struct EqualsMatcher : StringMatcherBase { + EqualsMatcher( CasedString const& comparator ); + virtual bool match( std::string const& source ) const CATCH_OVERRIDE; + }; + struct ContainsMatcher : StringMatcherBase { + ContainsMatcher( CasedString const& comparator ); + virtual bool match( std::string const& source ) const CATCH_OVERRIDE; + }; + struct StartsWithMatcher : StringMatcherBase { + StartsWithMatcher( CasedString const& comparator ); + virtual bool match( std::string const& source ) const CATCH_OVERRIDE; + }; + struct EndsWithMatcher : StringMatcherBase { + EndsWithMatcher( CasedString const& comparator ); + virtual bool match( std::string const& source ) const CATCH_OVERRIDE; + }; + + } // namespace StdString + + // The following functions create the actual matcher objects. + // This allows the types to be inferred + + StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + +} // namespace Matchers +} // namespace Catch + +// #included from: internal/catch_matchers_vector.h +#define TWOBLUECUBES_CATCH_MATCHERS_VECTOR_H_INCLUDED + +namespace Catch { +namespace Matchers { + + namespace Vector { + + template + struct ContainsElementMatcher : MatcherBase, T> { + + ContainsElementMatcher(T const &comparator) : m_comparator( comparator) {} + + bool match(std::vector const &v) const CATCH_OVERRIDE { + return std::find(v.begin(), v.end(), m_comparator) != v.end(); + } + + virtual std::string describe() const CATCH_OVERRIDE { + return "Contains: " + Catch::toString( m_comparator ); + } + + T const& m_comparator; + }; + + template + struct ContainsMatcher : MatcherBase, std::vector > { + + ContainsMatcher(std::vector const &comparator) : m_comparator( comparator ) {} + + bool match(std::vector const &v) const CATCH_OVERRIDE { + // !TBD: see note in EqualsMatcher + if (m_comparator.size() > v.size()) + return false; + for (size_t i = 0; i < m_comparator.size(); ++i) + if (std::find(v.begin(), v.end(), m_comparator[i]) == v.end()) + return false; + return true; + } + virtual std::string describe() const CATCH_OVERRIDE { + return "Contains: " + Catch::toString( m_comparator ); + } + + std::vector const& m_comparator; + }; + + template + struct EqualsMatcher : MatcherBase, std::vector > { + + EqualsMatcher(std::vector const &comparator) : m_comparator( comparator ) {} + + bool match(std::vector const &v) const CATCH_OVERRIDE { + // !TBD: This currently works if all elements can be compared using != + // - a more general approach would be via a compare template that defaults + // to using !=. but could be specialised for, e.g. std::vector etc + // - then just call that directly + if (m_comparator.size() != v.size()) + return false; + for (size_t i = 0; i < v.size(); ++i) + if (m_comparator[i] != v[i]) + return false; + return true; + } + virtual std::string describe() const CATCH_OVERRIDE { + return "Equals: " + Catch::toString( m_comparator ); + } + std::vector const& m_comparator; + }; + + } // namespace Vector + + // The following functions create the actual matcher objects. + // This allows the types to be inferred + + template + Vector::ContainsMatcher Contains( std::vector const& comparator ) { + return Vector::ContainsMatcher( comparator ); + } + + template + Vector::ContainsElementMatcher VectorContains( T const& comparator ) { + return Vector::ContainsElementMatcher( comparator ); + } + + template + Vector::EqualsMatcher Equals( std::vector const& comparator ) { + return Vector::EqualsMatcher( comparator ); + } + +} // namespace Matchers +} // namespace Catch + // #included from: internal/catch_interfaces_tag_alias_registry.h #define TWOBLUECUBES_CATCH_INTERFACES_TAG_ALIAS_REGISTRY_H_INCLUDED @@ -2709,7 +3133,7 @@ inline std::string toString( Detail::Approx const& value ) { namespace Catch { struct TagAlias { - TagAlias( std::string _tag, SourceLineInfo _lineInfo ) : tag( _tag ), lineInfo( _lineInfo ) {} + TagAlias( std::string const& _tag, SourceLineInfo _lineInfo ) : tag( _tag ), lineInfo( _lineInfo ) {} std::string tag; SourceLineInfo lineInfo; @@ -2781,8 +3205,18 @@ namespace Catch { } private: - T* nullableValue; - char storage[sizeof(T)]; + T *nullableValue; + union { + char storage[sizeof(T)]; + + // These are here to force alignment for the storage + long double dummy1; + void (*dummy2)(); + long double dummy3; +#ifdef CATCH_CONFIG_CPP11_LONG_LONG + long long dummy4; +#endif + }; }; } // end namespace Catch @@ -2822,11 +3256,12 @@ namespace Catch { IsHidden = 1 << 1, ShouldFail = 1 << 2, MayFail = 1 << 3, - Throws = 1 << 4 + Throws = 1 << 4, + NonPortable = 1 << 5 }; TestCaseInfo( std::string const& _name, - std::string const& _class_name, + std::string const& _className, std::string const& _description, std::set const& _tags, SourceLineInfo const& _lineInfo ); @@ -2841,7 +3276,7 @@ namespace Catch { bool expectedToFail() const; std::string name; - std::string class_name; + std::string className; std::string description; std::set tags; std::set lcaseTags; @@ -2872,7 +3307,7 @@ namespace Catch { }; TestCase makeTestCase( ITestCase* testCase, - std::string const& class_name, + std::string const& className, std::string const& name, std::string const& description, SourceLineInfo const& lineInfo ); @@ -2964,9 +3399,9 @@ namespace Catch { std::string testCaseName = methodName.substr( 15 ); std::string name = Detail::getAnnotation( cls, "Name", testCaseName ); std::string desc = Detail::getAnnotation( cls, "Description", testCaseName ); - const char* class_name = class_getName( cls ); + const char* className = class_getName( cls ); - getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), class_name, name.c_str(), desc.c_str(), SourceLineInfo() ) ); + getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, name.c_str(), desc.c_str(), SourceLineInfo() ) ); noTestMethods++; } } @@ -2980,64 +3415,67 @@ namespace Catch { namespace Impl { namespace NSStringMatchers { - template - struct StringHolder : MatcherImpl{ + struct StringHolder : MatcherBase{ StringHolder( NSString* substr ) : m_substr( [substr copy] ){} StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){} StringHolder() { arcSafeRelease( m_substr ); } + virtual bool match( NSString* arg ) const CATCH_OVERRIDE { + return false; + } + NSString* m_substr; }; - struct Equals : StringHolder { + struct Equals : StringHolder { Equals( NSString* substr ) : StringHolder( substr ){} - virtual bool match( ExpressionType const& str ) const { + virtual bool match( NSString* str ) const CATCH_OVERRIDE { return (str != nil || m_substr == nil ) && [str isEqualToString:m_substr]; } - virtual std::string toString() const { + virtual std::string describe() const CATCH_OVERRIDE { return "equals string: " + Catch::toString( m_substr ); } }; - struct Contains : StringHolder { + struct Contains : StringHolder { Contains( NSString* substr ) : StringHolder( substr ){} - virtual bool match( ExpressionType const& str ) const { + virtual bool match( NSString* str ) const { return (str != nil || m_substr == nil ) && [str rangeOfString:m_substr].location != NSNotFound; } - virtual std::string toString() const { + virtual std::string describe() const CATCH_OVERRIDE { return "contains string: " + Catch::toString( m_substr ); } }; - struct StartsWith : StringHolder { + struct StartsWith : StringHolder { StartsWith( NSString* substr ) : StringHolder( substr ){} - virtual bool match( ExpressionType const& str ) const { + virtual bool match( NSString* str ) const { return (str != nil || m_substr == nil ) && [str rangeOfString:m_substr].location == 0; } - virtual std::string toString() const { + virtual std::string describe() const CATCH_OVERRIDE { return "starts with: " + Catch::toString( m_substr ); } }; - struct EndsWith : StringHolder { + struct EndsWith : StringHolder { EndsWith( NSString* substr ) : StringHolder( substr ){} - virtual bool match( ExpressionType const& str ) const { + virtual bool match( NSString* str ) const { return (str != nil || m_substr == nil ) && [str rangeOfString:m_substr].location == [str length] - [m_substr length]; } - virtual std::string toString() const { + virtual std::string describe() const CATCH_OVERRIDE { return "ends with: " + Catch::toString( m_substr ); } }; @@ -3078,6 +3516,29 @@ return @ desc; \ #endif #ifdef CATCH_IMPL + +// !TBD: Move the leak detector code into a separate header +#ifdef CATCH_CONFIG_WINDOWS_CRTDBG +#include +class LeakDetector { +public: + LeakDetector() { + int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); + flag |= _CRTDBG_LEAK_CHECK_DF; + flag |= _CRTDBG_ALLOC_MEM_DF; + _CrtSetDbgFlag(flag); + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); + _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); + // Change this to leaking allocation's number to break there + _CrtSetBreakAlloc(-1); + } +}; +#else +class LeakDetector {}; +#endif + +LeakDetector leakDetector; + // #included from: internal/catch_impl.hpp #define TWOBLUECUBES_CATCH_IMPL_HPP_INCLUDED @@ -3117,6 +3578,8 @@ return @ desc; \ // #included from: catch_wildcard_pattern.hpp #define TWOBLUECUBES_CATCH_WILDCARD_PATTERN_HPP_INCLUDED +#include + namespace Catch { class WildcardPattern { @@ -3134,11 +3597,11 @@ namespace Catch m_wildcard( NoWildcard ), m_pattern( adjustCase( pattern ) ) { - if( startsWith( m_pattern, "*" ) ) { + if( startsWith( m_pattern, '*' ) ) { m_pattern = m_pattern.substr( 1 ); m_wildcard = WildcardAtStart; } - if( endsWith( m_pattern, "*" ) ) { + if( endsWith( m_pattern, '*' ) ) { m_pattern = m_pattern.substr( 0, m_pattern.size()-1 ); m_wildcard = static_cast( m_wildcard | WildcardAtEnd ); } @@ -3223,10 +3686,11 @@ namespace Catch { bool matches( TestCaseInfo const& testCase ) const { // All patterns in a filter must match for the filter to be a match - for( std::vector >::const_iterator it = m_patterns.begin(), itEnd = m_patterns.end(); it != itEnd; ++it ) + for( std::vector >::const_iterator it = m_patterns.begin(), itEnd = m_patterns.end(); it != itEnd; ++it ) { if( !(*it)->matches( testCase ) ) return false; - return true; + } + return true; } }; @@ -3256,23 +3720,25 @@ namespace Catch { namespace Catch { class TestSpecParser { - enum Mode{ None, Name, QuotedName, Tag }; + enum Mode{ None, Name, QuotedName, Tag, EscapedName }; Mode m_mode; bool m_exclusion; std::size_t m_start, m_pos; std::string m_arg; + std::vector m_escapeChars; TestSpec::Filter m_currentFilter; TestSpec m_testSpec; ITagAliasRegistry const* m_tagAliases; public: - TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {} + TestSpecParser( ITagAliasRegistry const& tagAliases ) :m_mode(None), m_exclusion(false), m_start(0), m_pos(0), m_tagAliases( &tagAliases ) {} TestSpecParser& parse( std::string const& arg ) { m_mode = None; m_exclusion = false; m_start = std::string::npos; m_arg = m_tagAliases->expandAliases( arg ); + m_escapeChars.clear(); for( m_pos = 0; m_pos < m_arg.size(); ++m_pos ) visitChar( m_arg[m_pos] ); if( m_mode == Name ) @@ -3291,6 +3757,7 @@ namespace Catch { case '~': m_exclusion = true; return; case '[': return startNewMode( Tag, ++m_pos ); case '"': return startNewMode( QuotedName, ++m_pos ); + case '\\': return escape(); default: startNewMode( Name, m_pos ); break; } } @@ -3306,7 +3773,11 @@ namespace Catch { addPattern(); startNewMode( Tag, ++m_pos ); } + else if( c == '\\' ) + escape(); } + else if( m_mode == EscapedName ) + m_mode = Name; else if( m_mode == QuotedName && c == '"' ) addPattern(); else if( m_mode == Tag && c == ']' ) @@ -3316,10 +3787,19 @@ namespace Catch { m_mode = mode; m_start = start; } + void escape() { + if( m_mode == None ) + m_start = m_pos; + m_mode = EscapedName; + m_escapeChars.push_back( m_pos ); + } std::string subString() const { return m_arg.substr( m_start, m_pos - m_start ); } template void addPattern() { std::string token = subString(); + for( size_t i = 0; i < m_escapeChars.size(); ++i ) + token = token.substr( 0, m_escapeChars[i]-m_start-i ) + token.substr( m_escapeChars[i]-m_start-i+1 ); + m_escapeChars.clear(); if( startsWith( token, "exclude:" ) ) { m_exclusion = true; token = token.substr( 8 ); @@ -3353,7 +3833,7 @@ namespace Catch { // #included from: catch_interfaces_config.h #define TWOBLUECUBES_CATCH_INTERFACES_CONFIG_H_INCLUDED -#include +#include #include #include @@ -3385,6 +3865,12 @@ namespace Catch { Yes, No }; }; + struct WaitForKeypress { enum When { + Never, + BeforeStart = 1, + BeforeExit = 2, + BeforeStartAndExit = BeforeStart | BeforeExit + }; }; class TestSpec; @@ -3405,6 +3891,8 @@ namespace Catch { virtual RunTests::InWhatOrder runOrder() const = 0; virtual unsigned int rngSeed() const = 0; virtual UseColour::YesOrNo useColour() const = 0; + virtual std::vector const& getSectionsToRun() const = 0; + }; } @@ -3427,11 +3915,13 @@ namespace Catch { #include #include #include +#include namespace Catch { std::ostream& cout(); std::ostream& cerr(); + std::ostream& clog(); struct IStream { virtual ~IStream() CATCH_NOEXCEPT; @@ -3472,8 +3962,7 @@ namespace Catch { #include #include #include -#include -#include +#include #ifndef CATCH_CONFIG_CONSOLE_WIDTH #define CATCH_CONFIG_CONSOLE_WIDTH 80 @@ -3488,25 +3977,29 @@ namespace Catch { listTags( false ), listReporters( false ), listTestNamesOnly( false ), + listExtraInfo( false ), showSuccessfulTests( false ), shouldDebugBreak( false ), noThrow( false ), showHelp( false ), showInvisibles( false ), filenamesAsTags( false ), + libIdentify( false ), abortAfter( -1 ), rngSeed( 0 ), verbosity( Verbosity::Normal ), warnings( WarnAbout::Nothing ), showDurations( ShowDurations::DefaultForReporter ), runOrder( RunTests::InDeclarationOrder ), - useColour( UseColour::Auto ) + useColour( UseColour::Auto ), + waitForKeypress( WaitForKeypress::Never ) {} bool listTests; bool listTags; bool listReporters; bool listTestNamesOnly; + bool listExtraInfo; bool showSuccessfulTests; bool shouldDebugBreak; @@ -3514,6 +4007,7 @@ namespace Catch { bool showHelp; bool showInvisibles; bool filenamesAsTags; + bool libIdentify; int abortAfter; unsigned int rngSeed; @@ -3523,6 +4017,7 @@ namespace Catch { ShowDurations::OrNot showDurations; RunTests::InWhatOrder runOrder; UseColour::YesOrNo useColour; + WaitForKeypress::When waitForKeypress; std::string outputFilename; std::string name; @@ -3530,6 +4025,7 @@ namespace Catch { std::vector reporterNames; std::vector testsOrTags; + std::vector sectionsToRun; }; class Config : public SharedImpl { @@ -3554,8 +4050,7 @@ namespace Catch { } } - virtual ~Config() { - } + virtual ~Config() {} std::string const& getFilename() const { return m_data.outputFilename ; @@ -3565,30 +4060,30 @@ namespace Catch { bool listTestNamesOnly() const { return m_data.listTestNamesOnly; } bool listTags() const { return m_data.listTags; } bool listReporters() const { return m_data.listReporters; } + bool listExtraInfo() const { return m_data.listExtraInfo; } std::string getProcessName() const { return m_data.processName; } - bool shouldDebugBreak() const { return m_data.shouldDebugBreak; } - - std::vector getReporterNames() const { return m_data.reporterNames; } - - int abortAfter() const { return m_data.abortAfter; } + std::vector const& getReporterNames() const { return m_data.reporterNames; } + std::vector const& getSectionsToRun() const CATCH_OVERRIDE { return m_data.sectionsToRun; } - TestSpec const& testSpec() const { return m_testSpec; } + virtual TestSpec const& testSpec() const CATCH_OVERRIDE { return m_testSpec; } bool showHelp() const { return m_data.showHelp; } - bool showInvisibles() const { return m_data.showInvisibles; } // IConfig interface - virtual bool allowThrows() const { return !m_data.noThrow; } - virtual std::ostream& stream() const { return m_stream->stream(); } - virtual std::string name() const { return m_data.name.empty() ? m_data.processName : m_data.name; } - virtual bool includeSuccessfulResults() const { return m_data.showSuccessfulTests; } - virtual bool warnAboutMissingAssertions() const { return m_data.warnings & WarnAbout::NoAssertions; } - virtual ShowDurations::OrNot showDurations() const { return m_data.showDurations; } - virtual RunTests::InWhatOrder runOrder() const { return m_data.runOrder; } - virtual unsigned int rngSeed() const { return m_data.rngSeed; } - virtual UseColour::YesOrNo useColour() const { return m_data.useColour; } + virtual bool allowThrows() const CATCH_OVERRIDE { return !m_data.noThrow; } + virtual std::ostream& stream() const CATCH_OVERRIDE { return m_stream->stream(); } + virtual std::string name() const CATCH_OVERRIDE { return m_data.name.empty() ? m_data.processName : m_data.name; } + virtual bool includeSuccessfulResults() const CATCH_OVERRIDE { return m_data.showSuccessfulTests; } + virtual bool warnAboutMissingAssertions() const CATCH_OVERRIDE { return m_data.warnings & WarnAbout::NoAssertions; } + virtual ShowDurations::OrNot showDurations() const CATCH_OVERRIDE { return m_data.showDurations; } + virtual RunTests::InWhatOrder runOrder() const CATCH_OVERRIDE { return m_data.runOrder; } + virtual unsigned int rngSeed() const CATCH_OVERRIDE { return m_data.rngSeed; } + virtual UseColour::YesOrNo useColour() const CATCH_OVERRIDE { return m_data.useColour; } + virtual bool shouldDebugBreak() const CATCH_OVERRIDE { return m_data.shouldDebugBreak; } + virtual int abortAfter() const CATCH_OVERRIDE { return m_data.abortAfter; } + virtual bool showInvisibles() const CATCH_OVERRIDE { return m_data.showInvisibles; } private: @@ -3653,6 +4148,7 @@ namespace Catch { #include #include #include +#include // Use optional outer namespace #ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE @@ -3762,7 +4258,7 @@ namespace Tbc { return oss.str(); } - inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) { + friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) { for( Text::const_iterator it = _text.begin(), itEnd = _text.end(); it != itEnd; ++it ) { if( it != _text.begin() ) @@ -3994,9 +4490,12 @@ namespace Clara { inline void convertInto( std::string const& _source, std::string& _dest ) { _dest = _source; } + char toLowerCh(char c) { + return static_cast( std::tolower( c ) ); + } inline void convertInto( std::string const& _source, bool& _dest ) { std::string sourceLC = _source; - std::transform( sourceLC.begin(), sourceLC.end(), sourceLC.begin(), ::tolower ); + std::transform( sourceLC.begin(), sourceLC.end(), sourceLC.begin(), toLowerCh ); if( sourceLC == "y" || sourceLC == "1" || sourceLC == "true" || sourceLC == "yes" || sourceLC == "on" ) _dest = true; else if( sourceLC == "n" || sourceLC == "0" || sourceLC == "false" || sourceLC == "no" || sourceLC == "off" ) @@ -4146,12 +4645,13 @@ namespace Clara { } void parseIntoTokens( std::string const& arg, std::vector& tokens ) { - for( std::size_t i = 0; i <= arg.size(); ++i ) { + for( std::size_t i = 0; i < arg.size(); ++i ) { char c = arg[i]; if( c == '"' ) inQuotes = !inQuotes; mode = handleMode( i, c, arg, tokens ); } + mode = handleMode( arg.size(), '\0', arg, tokens ); } Mode handleMode( std::size_t i, char c, std::string const& arg, std::vector& tokens ) { switch( mode ) { @@ -4184,6 +4684,7 @@ namespace Clara { default: from = i; return ShortOpt; } } + Mode handleOpt( std::size_t i, char c, std::string const& arg, std::vector& tokens ) { if( std::string( ":=\0", 3 ).find( c ) == std::string::npos ) return mode; @@ -4515,7 +5016,7 @@ namespace Clara { } std::vector parseInto( std::vector const& args, ConfigT& config ) const { - std::string processName = args[0]; + std::string processName = args.empty() ? std::string() : args[0]; std::size_t lastSlash = processName.find_last_of( "/\\" ); if( lastSlash != std::string::npos ) processName = processName.substr( lastSlash+1 ); @@ -4647,6 +5148,7 @@ STITCH_CLARA_CLOSE_NAMESPACE #endif #include +#include namespace Catch { @@ -4657,13 +5159,14 @@ namespace Catch { config.abortAfter = x; } inline void addTestOrTags( ConfigData& config, std::string const& _testSpec ) { config.testsOrTags.push_back( _testSpec ); } + inline void addSectionToRun( ConfigData& config, std::string const& sectionName ) { config.sectionsToRun.push_back( sectionName ); } inline void addReporterName( ConfigData& config, std::string const& _reporterName ) { config.reporterNames.push_back( _reporterName ); } inline void addWarning( ConfigData& config, std::string const& _warning ) { if( _warning == "NoAssertions" ) config.warnings = static_cast( config.warnings | WarnAbout::NoAssertions ); else - throw std::runtime_error( "Unrecognised warning: '" + _warning + "'" ); + throw std::runtime_error( "Unrecognised warning: '" + _warning + '\'' ); } inline void setOrder( ConfigData& config, std::string const& order ) { if( startsWith( "declared", order ) ) @@ -4673,7 +5176,7 @@ namespace Catch { else if( startsWith( "random", order ) ) config.runOrder = RunTests::InRandomOrder; else - throw std::runtime_error( "Unrecognised ordering: '" + order + "'" ); + throw std::runtime_error( "Unrecognised ordering: '" + order + '\'' ); } inline void setRngSeed( ConfigData& config, std::string const& seed ) { if( seed == "time" ) { @@ -4684,7 +5187,7 @@ namespace Catch { ss << seed; ss >> config.rngSeed; if( ss.fail() ) - throw std::runtime_error( "Argment to --rng-seed should be the word 'time' or a number" ); + throw std::runtime_error( "Argument to --rng-seed should be the word 'time' or a number" ); } } inline void setVerbosity( ConfigData& config, int level ) { @@ -4708,6 +5211,18 @@ namespace Catch { else throw std::runtime_error( "colour mode must be one of: auto, yes or no" ); } + inline void setWaitForKeypress( ConfigData& config, std::string const& keypress ) { + std::string keypressLc = toLower( keypress ); + if( keypressLc == "start" ) + config.waitForKeypress = WaitForKeypress::BeforeStart; + else if( keypressLc == "exit" ) + config.waitForKeypress = WaitForKeypress::BeforeExit; + else if( keypressLc == "both" ) + config.waitForKeypress = WaitForKeypress::BeforeStartAndExit; + else + throw std::runtime_error( "keypress argument must be one of: start, exit or both. '" + keypress + "' not recognised" ); + }; + inline void forceColour( ConfigData& config ) { config.useColour = UseColour::Yes; } @@ -4719,8 +5234,11 @@ namespace Catch { std::string line; while( std::getline( f, line ) ) { line = trim(line); - if( !line.empty() && !startsWith( line, "#" ) ) - addTestOrTags( config, "\"" + line + "\"," ); + if( !line.empty() && !startsWith( line, '#' ) ) { + if( !startsWith( line, '"' ) ) + line = '"' + line + '"'; + addTestOrTags( config, line + ',' ); + } } } @@ -4807,11 +5325,19 @@ namespace Catch { .describe( "adds a tag for the filename" ) .bind( &ConfigData::filenamesAsTags ); + cli["-c"]["--section"] + .describe( "specify section to run" ) + .bind( &addSectionToRun, "section name" ); + // Less common commands which don't have a short form cli["--list-test-names-only"] .describe( "list all/matching test cases names only" ) .bind( &ConfigData::listTestNamesOnly ); + cli["--list-extra-info"] + .describe( "list all/matching test cases with more info" ) + .bind( &ConfigData::listExtraInfo ); + cli["--list-reporters"] .describe( "list all reporters" ) .bind( &ConfigData::listReporters ); @@ -4832,6 +5358,14 @@ namespace Catch { .describe( "should output be colourised" ) .bind( &setUseColour, "yes|no" ); + cli["--libidentify"] + .describe( "report name and version according to libidentify standard" ) + .bind( &ConfigData::libIdentify ); + + cli["--wait-for-keypress"] + .describe( "waits for a keypress before exiting" ) + .bind( &setWaitForKeypress, "start|exit|both" ); + return cli; } @@ -4879,19 +5413,16 @@ namespace Tbc { TextAttributes() : initialIndent( std::string::npos ), indent( 0 ), - width( consoleWidth-1 ), - tabChar( '\t' ) + width( consoleWidth-1 ) {} TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; } TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; } TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; } - TextAttributes& setTabChar( char _value ) { tabChar = _value; return *this; } std::size_t initialIndent; // indent of first line, or npos std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos std::size_t width; // maximum width of text, including indent. Longer text will wrap - char tabChar; // If this char is seen the indent is changed to current pos }; class Text { @@ -4899,64 +5430,78 @@ namespace Tbc { Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() ) : attr( _attr ) { - std::string wrappableChars = " [({.,/|\\-"; - std::size_t indent = _attr.initialIndent != std::string::npos - ? _attr.initialIndent - : _attr.indent; - std::string remainder = _str; + const std::string wrappableBeforeChars = "[({<\t"; + const std::string wrappableAfterChars = "])}>-,./|\\"; + const std::string wrappableInsteadOfChars = " \n\r"; + std::string indent = _attr.initialIndent != std::string::npos + ? std::string( _attr.initialIndent, ' ' ) + : std::string( _attr.indent, ' ' ); + + typedef std::string::const_iterator iterator; + iterator it = _str.begin(); + const iterator strEnd = _str.end(); + + while( it != strEnd ) { - while( !remainder.empty() ) { if( lines.size() >= 1000 ) { lines.push_back( "... message truncated due to excessive size" ); return; } - std::size_t tabPos = std::string::npos; - std::size_t width = (std::min)( remainder.size(), _attr.width - indent ); - std::size_t pos = remainder.find_first_of( '\n' ); - if( pos <= width ) { - width = pos; - } - pos = remainder.find_last_of( _attr.tabChar, width ); - if( pos != std::string::npos ) { - tabPos = pos; - if( remainder[width] == '\n' ) - width--; - remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 ); + + std::string suffix; + std::size_t width = (std::min)( static_cast( strEnd-it ), _attr.width-static_cast( indent.size() ) ); + iterator itEnd = it+width; + iterator itNext = _str.end(); + + iterator itNewLine = std::find( it, itEnd, '\n' ); + if( itNewLine != itEnd ) + itEnd = itNewLine; + + if( itEnd != strEnd ) { + bool foundWrapPoint = false; + iterator findIt = itEnd; + do { + if( wrappableAfterChars.find( *findIt ) != std::string::npos && findIt != itEnd ) { + itEnd = findIt+1; + itNext = findIt+1; + foundWrapPoint = true; + } + else if( findIt > it && wrappableBeforeChars.find( *findIt ) != std::string::npos ) { + itEnd = findIt; + itNext = findIt; + foundWrapPoint = true; + } + else if( wrappableInsteadOfChars.find( *findIt ) != std::string::npos ) { + itNext = findIt+1; + itEnd = findIt; + foundWrapPoint = true; + } + if( findIt == it ) + break; + else + --findIt; + } + while( !foundWrapPoint ); + + if( !foundWrapPoint ) { + // No good wrap char, so we'll break mid word and add a hyphen + --itEnd; + itNext = itEnd; + suffix = "-"; + } + else { + while( itEnd > it && wrappableInsteadOfChars.find( *(itEnd-1) ) != std::string::npos ) + --itEnd; + } } + lines.push_back( indent + std::string( it, itEnd ) + suffix ); - if( width == remainder.size() ) { - spliceLine( indent, remainder, width ); - } - else if( remainder[width] == '\n' ) { - spliceLine( indent, remainder, width ); - if( width <= 1 || remainder.size() != 1 ) - remainder = remainder.substr( 1 ); - indent = _attr.indent; - } - else { - pos = remainder.find_last_of( wrappableChars, width ); - if( pos != std::string::npos && pos > 0 ) { - spliceLine( indent, remainder, pos ); - if( remainder[0] == ' ' ) - remainder = remainder.substr( 1 ); - } - else { - spliceLine( indent, remainder, width-1 ); - lines.back() += "-"; - } - if( lines.size() == 1 ) - indent = _attr.indent; - if( tabPos != std::string::npos ) - indent += tabPos; - } + if( indent.size() != _attr.indent ) + indent = std::string( _attr.indent, ' ' ); + it = itNext; } } - void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) { - lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) ); - _remainder = _remainder.substr( _pos ); - } - typedef std::vector::const_iterator const_iterator; const_iterator begin() const { return lines.begin(); } @@ -5063,7 +5608,6 @@ namespace Catch { #include #include #include -#include namespace Catch { @@ -5330,8 +5874,9 @@ namespace Catch { } std::size_t matchedTests = 0; - TextAttributes nameAttr, tagsAttr; + TextAttributes nameAttr, descAttr, tagsAttr; nameAttr.setInitialIndent( 2 ).setIndent( 4 ); + descAttr.setIndent( 4 ); tagsAttr.setIndent( 6 ); std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); @@ -5346,14 +5891,21 @@ namespace Catch { Colour colourGuard( colour ); Catch::cout() << Text( testCaseInfo.name, nameAttr ) << std::endl; + if( config.listExtraInfo() ) { + Catch::cout() << " " << testCaseInfo.lineInfo << std::endl; + std::string description = testCaseInfo.description; + if( description.empty() ) + description = "(NO DESCRIPTION)"; + Catch::cout() << Text( description, descAttr ) << std::endl; + } if( !testCaseInfo.tags.empty() ) Catch::cout() << Text( testCaseInfo.tagsAsString, tagsAttr ) << std::endl; } if( !config.testSpec().hasFilters() ) - Catch::cout() << pluralise( matchedTests, "test case" ) << "\n" << std::endl; + Catch::cout() << pluralise( matchedTests, "test case" ) << '\n' << std::endl; else - Catch::cout() << pluralise( matchedTests, "matching test case" ) << "\n" << std::endl; + Catch::cout() << pluralise( matchedTests, "matching test case" ) << '\n' << std::endl; return matchedTests; } @@ -5368,7 +5920,13 @@ namespace Catch { ++it ) { matchedTests++; TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); - Catch::cout() << testCaseInfo.name << std::endl; + if( startsWith( testCaseInfo.name, '#' ) ) + Catch::cout() << '"' << testCaseInfo.name << '"'; + else + Catch::cout() << testCaseInfo.name; + if ( config.listExtraInfo() ) + Catch::cout() << "\t@" << testCaseInfo.lineInfo; + Catch::cout() << std::endl; } return matchedTests; } @@ -5429,9 +5987,9 @@ namespace Catch { .setInitialIndent( 0 ) .setIndent( oss.str().size() ) .setWidth( CATCH_CONFIG_CONSOLE_WIDTH-10 ) ); - Catch::cout() << oss.str() << wrapper << "\n"; + Catch::cout() << oss.str() << wrapper << '\n'; } - Catch::cout() << pluralise( tagCounts.size(), "tag" ) << "\n" << std::endl; + Catch::cout() << pluralise( tagCounts.size(), "tag" ) << '\n' << std::endl; return tagCounts.size(); } @@ -5450,9 +6008,9 @@ namespace Catch { .setWidth( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 ) ); Catch::cout() << " " << it->first - << ":" + << ':' << std::string( maxNameLen - it->first.size() + 2, ' ' ) - << wrapper << "\n"; + << wrapper << '\n'; } Catch::cout() << std::endl; return factories.size(); @@ -5460,7 +6018,7 @@ namespace Catch { inline Option list( Config const& config ) { Option listedCount; - if( config.listTests() ) + if( config.listTests() || ( config.listExtraInfo() && !config.listTestNamesOnly() ) ) listedCount = listedCount.valueOr(0) + listTests( config ); if( config.listTestNamesOnly() ) listedCount = listedCount.valueOr(0) + listTestsNamesOnly( config ); @@ -5479,19 +6037,32 @@ namespace Catch { // #included from: catch_test_case_tracker.hpp #define TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED -#include +#include #include #include #include +#include + +CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS namespace Catch { namespace TestCaseTracking { + struct NameAndLocation { + std::string name; + SourceLineInfo location; + + NameAndLocation( std::string const& _name, SourceLineInfo const& _location ) + : name( _name ), + location( _location ) + {} + }; + struct ITracker : SharedImpl<> { virtual ~ITracker(); // static queries - virtual std::string name() const = 0; + virtual NameAndLocation const& nameAndLocation() const = 0; // dynamic queries virtual bool isComplete() const = 0; // Successfully completed or failed @@ -5507,7 +6078,7 @@ namespace TestCaseTracking { virtual void markAsNeedingAnotherRun() = 0; virtual void addChild( Ptr const& child ) = 0; - virtual ITracker* findChild( std::string const& name ) = 0; + virtual ITracker* findChild( NameAndLocation const& nameAndLocation ) = 0; virtual void openChild() = 0; // Debug/ checking @@ -5515,7 +6086,7 @@ namespace TestCaseTracking { virtual bool isIndexTracker() const = 0; }; - class TrackerContext { + class TrackerContext { enum RunState { NotStarted, @@ -5577,30 +6148,32 @@ namespace TestCaseTracking { Failed }; class TrackerHasName { - std::string m_name; + NameAndLocation m_nameAndLocation; public: - TrackerHasName( std::string const& name ) : m_name( name ) {} + TrackerHasName( NameAndLocation const& nameAndLocation ) : m_nameAndLocation( nameAndLocation ) {} bool operator ()( Ptr const& tracker ) { - return tracker->name() == m_name; + return + tracker->nameAndLocation().name == m_nameAndLocation.name && + tracker->nameAndLocation().location == m_nameAndLocation.location; } }; typedef std::vector > Children; - std::string m_name; + NameAndLocation m_nameAndLocation; TrackerContext& m_ctx; ITracker* m_parent; Children m_children; CycleState m_runState; public: - TrackerBase( std::string const& name, TrackerContext& ctx, ITracker* parent ) - : m_name( name ), + TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) + : m_nameAndLocation( nameAndLocation ), m_ctx( ctx ), m_parent( parent ), m_runState( NotStarted ) {} virtual ~TrackerBase(); - virtual std::string name() const CATCH_OVERRIDE { - return m_name; + virtual NameAndLocation const& nameAndLocation() const CATCH_OVERRIDE { + return m_nameAndLocation; } virtual bool isComplete() const CATCH_OVERRIDE { return m_runState == CompletedSuccessfully || m_runState == Failed; @@ -5619,8 +6192,8 @@ namespace TestCaseTracking { m_children.push_back( child ); } - virtual ITracker* findChild( std::string const& name ) CATCH_OVERRIDE { - Children::const_iterator it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( name ) ); + virtual ITracker* findChild( NameAndLocation const& nameAndLocation ) CATCH_OVERRIDE { + Children::const_iterator it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( nameAndLocation ) ); return( it != m_children.end() ) ? it->get() : CATCH_NULL; @@ -5698,32 +6271,56 @@ namespace TestCaseTracking { }; class SectionTracker : public TrackerBase { + std::vector m_filters; public: - SectionTracker( std::string const& name, TrackerContext& ctx, ITracker* parent ) - : TrackerBase( name, ctx, parent ) - {} + SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) + : TrackerBase( nameAndLocation, ctx, parent ) + { + if( parent ) { + while( !parent->isSectionTracker() ) + parent = &parent->parent(); + + SectionTracker& parentSection = static_cast( *parent ); + addNextFilters( parentSection.m_filters ); + } + } virtual ~SectionTracker(); virtual bool isSectionTracker() const CATCH_OVERRIDE { return true; } - static SectionTracker& acquire( TrackerContext& ctx, std::string const& name ) { + static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) { SectionTracker* section = CATCH_NULL; ITracker& currentTracker = ctx.currentTracker(); - if( ITracker* childTracker = currentTracker.findChild( name ) ) { + if( ITracker* childTracker = currentTracker.findChild( nameAndLocation ) ) { assert( childTracker ); assert( childTracker->isSectionTracker() ); section = static_cast( childTracker ); } else { - section = new SectionTracker( name, ctx, ¤tTracker ); + section = new SectionTracker( nameAndLocation, ctx, ¤tTracker ); currentTracker.addChild( section ); } - if( !ctx.completedCycle() && !section->isComplete() ) { + if( !ctx.completedCycle() ) + section->tryOpen(); + return *section; + } - section->open(); + void tryOpen() { + if( !isComplete() && (m_filters.empty() || m_filters[0].empty() || m_filters[0] == m_nameAndLocation.name ) ) + open(); + } + + void addInitialFilters( std::vector const& filters ) { + if( !filters.empty() ) { + m_filters.push_back(""); // Root - should never be consulted + m_filters.push_back(""); // Test Case - not a section filter + m_filters.insert( m_filters.end(), filters.begin(), filters.end() ); } - return *section; + } + void addNextFilters( std::vector const& filters ) { + if( filters.size() > 1 ) + m_filters.insert( m_filters.end(), ++filters.begin(), filters.end() ); } }; @@ -5731,8 +6328,8 @@ namespace TestCaseTracking { int m_size; int m_index; public: - IndexTracker( std::string const& name, TrackerContext& ctx, ITracker* parent, int size ) - : TrackerBase( name, ctx, parent ), + IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size ) + : TrackerBase( nameAndLocation, ctx, parent ), m_size( size ), m_index( -1 ) {} @@ -5740,17 +6337,17 @@ namespace TestCaseTracking { virtual bool isIndexTracker() const CATCH_OVERRIDE { return true; } - static IndexTracker& acquire( TrackerContext& ctx, std::string const& name, int size ) { + static IndexTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ) { IndexTracker* tracker = CATCH_NULL; ITracker& currentTracker = ctx.currentTracker(); - if( ITracker* childTracker = currentTracker.findChild( name ) ) { + if( ITracker* childTracker = currentTracker.findChild( nameAndLocation ) ) { assert( childTracker ); assert( childTracker->isIndexTracker() ); tracker = static_cast( childTracker ); } else { - tracker = new IndexTracker( name, ctx, ¤tTracker, size ); + tracker = new IndexTracker( nameAndLocation, ctx, ¤tTracker, size ); currentTracker.addChild( tracker ); } @@ -5778,7 +6375,7 @@ namespace TestCaseTracking { }; inline ITracker& TrackerContext::startRun() { - m_rootTracker = new SectionTracker( "{root}", *this, CATCH_NULL ); + m_rootTracker = new SectionTracker( NameAndLocation( "{root}", CATCH_INTERNAL_LINEINFO ), *this, CATCH_NULL ); m_currentTracker = CATCH_NULL; m_runState = Executing; return *m_rootTracker; @@ -5793,40 +6390,212 @@ using TestCaseTracking::IndexTracker; } // namespace Catch +CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS + // #included from: catch_fatal_condition.hpp #define TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED +#include +#include + namespace Catch { - // Report the error condition then exit the process - inline void fatal( std::string const& message, int exitCode ) { + //! Signals fatal error message to the run context + inline void reportFatal( std::string const& message ) { IContext& context = Catch::getCurrentContext(); IResultCapture* resultCapture = context.getResultCapture(); resultCapture->handleFatalErrorCondition( message ); - - if( Catch::alwaysTrue() ) // avoids "no return" warnings - exit( exitCode ); } } // namespace Catch #if defined ( CATCH_PLATFORM_WINDOWS ) ///////////////////////////////////////// +// #included from: catch_windows_h_proxy.h + +#define TWOBLUECUBES_CATCH_WINDOWS_H_PROXY_H_INCLUDED + +#ifdef CATCH_DEFINES_NOMINMAX +# define NOMINMAX +#endif +#ifdef CATCH_DEFINES_WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif + +#ifdef __AFXDLL +#include +#else +#include +#endif + +#ifdef CATCH_DEFINES_NOMINMAX +# undef NOMINMAX +#endif +#ifdef CATCH_DEFINES_WIN32_LEAN_AND_MEAN +# undef WIN32_LEAN_AND_MEAN +#endif + + +# if !defined ( CATCH_CONFIG_WINDOWS_SEH ) + +namespace Catch { + class FatalConditionHandler { + bool m_started; + + // Install/disengage implementation for specific platform. + // Should be if-defed to work on current platform, can assume + // engage-disengage 1:1 pairing. + void engage_platform() {} + void disengage_platform() {} + + public: + // Should also have platform-specific implementations as needed + FatalConditionHandler() : m_started(false) {} + ~FatalConditionHandler() {} + + void engage() { + assert(!m_started && "Handler cannot be installed twice."); + m_started = true; + engage_platform(); + } + + void disengage() { + assert(m_started && "Handler cannot be uninstalled without being installed first"); + m_started = false; + disengage_platform(); + } + }; +} + +# else // CATCH_CONFIG_WINDOWS_SEH is defined namespace Catch { - struct FatalConditionHandler { - void reset() {} - }; + struct SignalDefs { DWORD id; const char* name; }; + extern SignalDefs signalDefs[]; + // There is no 1-1 mapping between signals and windows exceptions. + // Windows can easily distinguish between SO and SigSegV, + // but SigInt, SigTerm, etc are handled differently. + SignalDefs signalDefs[] = { + { EXCEPTION_ILLEGAL_INSTRUCTION, "SIGILL - Illegal instruction signal" }, + { EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow" }, + { EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal" }, + { EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" }, + }; + + static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) { + for (int i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) { + if (ExceptionInfo->ExceptionRecord->ExceptionCode == signalDefs[i].id) { + reportFatal(signalDefs[i].name); + } + } + // If its not an exception we care about, pass it along. + // This stops us from eating debugger breaks etc. + return EXCEPTION_CONTINUE_SEARCH; + } + + // Since we do not support multiple instantiations, we put these + // into global variables and rely on cleaning them up in outlined + // constructors/destructors + static PVOID exceptionHandlerHandle = CATCH_NULL; + + class FatalConditionHandler { + bool m_started; + + // Install/disengage implementation for specific platform. + // Should be if-defed to work on current platform, can assume + // engage-disengage 1:1 pairing. + + void engage_platform() { + // Register as first handler in current chain + exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException); + if (!exceptionHandlerHandle) { + throw std::runtime_error("Could not register vectored exception handler"); + } + } + + void disengage_platform() { + if (!RemoveVectoredExceptionHandler(exceptionHandlerHandle)) { + throw std::runtime_error("Could not unregister vectored exception handler"); + } + exceptionHandlerHandle = CATCH_NULL; + } + + public: + FatalConditionHandler() : m_started(false) { + ULONG guaranteeSize = static_cast(32 * 1024); + if (!SetThreadStackGuarantee(&guaranteeSize)) { + // We do not want to fully error out, because needing + // the stack reserve should be rare enough anyway. + Catch::cerr() + << "Failed to reserve piece of stack." + << " Stack overflows will not be reported successfully."; + } + } + + // We do not attempt to unset the stack guarantee, because + // Windows does not support lowering the stack size guarantee. + ~FatalConditionHandler() {} + + void engage() { + assert(!m_started && "Handler cannot be installed twice."); + m_started = true; + engage_platform(); + } + + void disengage() { + assert(m_started && "Handler cannot be uninstalled without being installed first"); + m_started = false; + disengage_platform(); + } + }; } // namespace Catch +# endif // CATCH_CONFIG_WINDOWS_SEH + #else // Not Windows - assumed to be POSIX compatible ////////////////////////// +# if !defined(CATCH_CONFIG_POSIX_SIGNALS) + +namespace Catch { + class FatalConditionHandler { + bool m_started; + + // Install/disengage implementation for specific platform. + // Should be if-defed to work on current platform, can assume + // engage-disengage 1:1 pairing. + void engage_platform() {} + void disengage_platform() {} + + public: + // Should also have platform-specific implementations as needed + FatalConditionHandler() : m_started(false) {} + ~FatalConditionHandler() {} + + void engage() { + assert(!m_started && "Handler cannot be installed twice."); + m_started = true; + engage_platform(); + } + + void disengage() { + assert(m_started && "Handler cannot be uninstalled without being installed first"); + m_started = false; + disengage_platform(); + } + }; +} + +# else // CATCH_CONFIG_POSIX_SIGNALS is defined + #include namespace Catch { - struct SignalDefs { int id; const char* name; }; + struct SignalDefs { + int id; + const char* name; + }; extern SignalDefs signalDefs[]; SignalDefs signalDefs[] = { { SIGINT, "SIGINT - Terminal interrupt signal" }, @@ -5835,39 +6604,133 @@ namespace Catch { { SIGSEGV, "SIGSEGV - Segmentation violation signal" }, { SIGTERM, "SIGTERM - Termination request signal" }, { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" } - }; + }; + +// Older GCCs trigger -Wmissing-field-initializers for T foo = {} +// which is zero initialization, but not explicit. We want to avoid +// that. +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif + + static char* altStackMem = CATCH_NULL; + static std::size_t altStackSize = 0; + static stack_t oldSigStack; + static struct sigaction oldSigActions[sizeof(signalDefs) / sizeof(SignalDefs)]; + + static void restorePreviousSignalHandlers() { + // We set signal handlers back to the previous ones. Hopefully + // nobody overwrote them in the meantime, and doesn't expect + // their signal handlers to live past ours given that they + // installed them after ours.. + for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) { + sigaction(signalDefs[i].id, &oldSigActions[i], CATCH_NULL); + } + // Return the old stack + sigaltstack(&oldSigStack, CATCH_NULL); + } + + static void handleSignal( int sig ) { + char const * name = ""; + for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) { + SignalDefs &def = signalDefs[i]; + if (sig == def.id) { + name = def.name; + break; + } + } + // We need to restore previous signal handlers and let them do + // their thing, so that the users can have the debugger break + // when a signal is raised, and so on. + restorePreviousSignalHandlers(); + reportFatal( name ); + raise( sig ); + } + + class FatalConditionHandler { + bool m_started; + + // Install/disengage implementation for specific platform. + // Should be if-defed to work on current platform, can assume + // engage-disengage 1:1 pairing. - struct FatalConditionHandler { + void engage_platform() { + stack_t sigStack; + sigStack.ss_sp = altStackMem; + sigStack.ss_size = SIGSTKSZ; + sigStack.ss_flags = 0; + sigaltstack(&sigStack, &oldSigStack); + struct sigaction sa = { 0 }; - static void handleSignal( int sig ) { - for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) - if( sig == signalDefs[i].id ) - fatal( signalDefs[i].name, -sig ); - fatal( "", -sig ); + sa.sa_handler = handleSignal; + sa.sa_flags = SA_ONSTACK; + for (std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i) { + sigaction(signalDefs[i].id, &sa, &oldSigActions[i]); + } + } + + void disengage_platform() { + restorePreviousSignalHandlers(); } - FatalConditionHandler() : m_isSet( true ) { - for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) - signal( signalDefs[i].id, handleSignal ); + public: + FatalConditionHandler() : m_started(false) { + assert(!altStackMem && "Cannot initialize POSIX signal handler when one already exists"); + if (altStackSize == 0) { + altStackSize = SIGSTKSZ; + } + altStackMem = new char[altStackSize](); } + ~FatalConditionHandler() { - reset(); + delete[] altStackMem; + // We signal that another instance can be constructed by zeroing + // out the pointer. + altStackMem = CATCH_NULL; } - void reset() { - if( m_isSet ) { - for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) - signal( signalDefs[i].id, SIG_DFL ); - m_isSet = false; - } + + void engage() { + assert(!m_started && "Handler cannot be installed twice."); + m_started = true; + engage_platform(); } - bool m_isSet; + void disengage() { + assert(m_started && "Handler cannot be uninstalled without being installed first"); + m_started = false; + disengage_platform(); + } }; +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + } // namespace Catch +# endif // CATCH_CONFIG_POSIX_SIGNALS + #endif // not Windows +namespace Catch { + + //! Simple RAII guard for (dis)engaging the FatalConditionHandler + class FatalConditionHandlerGuard { + FatalConditionHandler* m_handler; + public: + FatalConditionHandlerGuard(FatalConditionHandler* handler): + m_handler(handler) { + m_handler->engage(); + } + ~FatalConditionHandlerGuard() { + m_handler->disengage(); + } + }; + +} // end namespace Catch + +#include #include #include @@ -5896,6 +6759,29 @@ namespace Catch { std::string& m_targetString; }; + // StdErr has two constituent streams in C++, std::cerr and std::clog + // This means that we need to redirect 2 streams into 1 to keep proper + // order of writes and cannot use StreamRedirect on its own + class StdErrRedirect { + public: + StdErrRedirect(std::string& targetString) + :m_cerrBuf( cerr().rdbuf() ), m_clogBuf(clog().rdbuf()), + m_targetString(targetString){ + cerr().rdbuf(m_oss.rdbuf()); + clog().rdbuf(m_oss.rdbuf()); + } + ~StdErrRedirect() { + m_targetString += m_oss.str(); + cerr().rdbuf(m_cerrBuf); + clog().rdbuf(m_clogBuf); + } + private: + std::streambuf* m_cerrBuf; + std::streambuf* m_clogBuf; + std::ostringstream m_oss; + std::string& m_targetString; + }; + /////////////////////////////////////////////////////////////////////////// class RunContext : public IResultCapture, public IRunner { @@ -5910,7 +6796,8 @@ namespace Catch { m_context( getCurrentMutableContext() ), m_activeTestCase( CATCH_NULL ), m_config( _config ), - m_reporter( reporter ) + m_reporter( reporter ), + m_shouldReportUnexpected ( true ) { m_context.setRunner( this ); m_context.setConfig( m_config ); @@ -5942,10 +6829,12 @@ namespace Catch { m_activeTestCase = &testCase; do { - m_trackerContext.startRun(); + ITracker& rootTracker = m_trackerContext.startRun(); + assert( rootTracker.isSectionTracker() ); + static_cast( rootTracker ).addInitialFilters( m_config->getSectionsToRun() ); do { m_trackerContext.startCycle(); - m_testCaseTracker = &SectionTracker::acquire( m_trackerContext, testInfo.name ); + m_testCaseTracker = &SectionTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( testInfo.name, testInfo.lineInfo ) ); runCurrentTest( redirectedCout, redirectedCerr ); } while( !m_testCaseTracker->isSuccessfullyCompleted() && !aborting() ); @@ -5983,26 +6872,44 @@ namespace Catch { m_totals.assertions.passed++; } else if( !result.isOk() ) { - m_totals.assertions.failed++; + if( m_activeTestCase->getTestCaseInfo().okToFail() ) + m_totals.assertions.failedButOk++; + else + m_totals.assertions.failed++; } - if( m_reporter->assertionEnded( AssertionStats( result, m_messages, m_totals ) ) ) - m_messages.clear(); + // We have no use for the return value (whether messages should be cleared), because messages were made scoped + // and should be let to clear themselves out. + static_cast(m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals))); // Reset working state m_lastAssertionInfo = AssertionInfo( "", m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}" , m_lastAssertionInfo.resultDisposition ); m_lastResult = result; } + virtual bool lastAssertionPassed() + { + return m_totals.assertions.passed == (m_prevPassed + 1); + } + + virtual void assertionPassed() + { + m_totals.assertions.passed++; + m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}"; + m_lastAssertionInfo.macroName = ""; + } + + virtual void assertionRun() + { + m_prevPassed = m_totals.assertions.passed; + } + virtual bool sectionStarted ( SectionInfo const& sectionInfo, Counts& assertions ) { - std::ostringstream oss; - oss << sectionInfo.name << "@" << sectionInfo.lineInfo; - - ITracker& sectionTracker = SectionTracker::acquire( m_trackerContext, oss.str() ); + ITracker& sectionTracker = SectionTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( sectionInfo.name, sectionInfo.lineInfo ) ); if( !sectionTracker.isOpen() ) return false; m_activeSections.push_back( §ionTracker ); @@ -6061,18 +6968,26 @@ namespace Catch { virtual std::string getCurrentTestName() const { return m_activeTestCase ? m_activeTestCase->getTestCaseInfo().name - : ""; + : std::string(); } virtual const AssertionResult* getLastResult() const { return &m_lastResult; } + virtual void exceptionEarlyReported() { + m_shouldReportUnexpected = false; + } + virtual void handleFatalErrorCondition( std::string const& message ) { - ResultBuilder resultBuilder = makeUnexpectedResultBuilder(); - resultBuilder.setResultType( ResultWas::FatalErrorCondition ); - resultBuilder << message; - resultBuilder.captureExpression(); + // Don't rebuild the result -- the stringification itself can cause more fatal errors + // Instead, fake a result data. + AssertionResultData tempResult; + tempResult.resultType = ResultWas::FatalErrorCondition; + tempResult.message = message; + AssertionResult result(m_lastAssertionInfo, tempResult); + + getResultCapture().assertionEnded(result); handleUnfinishedSections(); @@ -6089,13 +7004,14 @@ namespace Catch { Totals deltaTotals; deltaTotals.testCases.failed = 1; + deltaTotals.assertions.failed = 1; m_reporter->testCaseEnded( TestCaseStats( testInfo, deltaTotals, - "", - "", + std::string(), + std::string(), false ) ); m_totals.testCases.failed++; - testGroupEnded( "", m_totals, 1, 1 ); + testGroupEnded( std::string(), m_totals, 1, 1 ); m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, false ) ); } @@ -6113,6 +7029,7 @@ namespace Catch { m_reporter->sectionStarting( testCaseSection ); Counts prevAssertions = m_totals.assertions; double duration = 0; + m_shouldReportUnexpected = true; try { m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal ); @@ -6122,7 +7039,7 @@ namespace Catch { timer.start(); if( m_reporter->getPreferences().shouldRedirectStdOut ) { StreamRedirect coutRedir( Catch::cout(), redirectedCout ); - StreamRedirect cerrRedir( Catch::cerr(), redirectedCerr ); + StdErrRedirect errRedir( redirectedCerr ); invokeActiveTestCase(); } else { @@ -6134,7 +7051,11 @@ namespace Catch { // This just means the test was aborted due to failure } catch(...) { - makeUnexpectedResultBuilder().useActiveException(); + // Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions + // are reported without translation at the point of origin. + if (m_shouldReportUnexpected) { + makeUnexpectedResultBuilder().useActiveException(); + } } m_testCaseTracker->close(); handleUnfinishedSections(); @@ -6143,28 +7064,21 @@ namespace Catch { Counts assertions = m_totals.assertions - prevAssertions; bool missingAssertions = testForMissingAssertions( assertions ); - if( testCaseInfo.okToFail() ) { - std::swap( assertions.failedButOk, assertions.failed ); - m_totals.assertions.failed -= assertions.failedButOk; - m_totals.assertions.failedButOk += assertions.failedButOk; - } - SectionStats testCaseSectionStats( testCaseSection, assertions, duration, missingAssertions ); m_reporter->sectionEnded( testCaseSectionStats ); } void invokeActiveTestCase() { - FatalConditionHandler fatalConditionHandler; // Handle signals + FatalConditionHandlerGuard _(&m_fatalConditionhandler); m_activeTestCase->invoke(); - fatalConditionHandler.reset(); } private: ResultBuilder makeUnexpectedResultBuilder() const { - return ResultBuilder( m_lastAssertionInfo.macroName.c_str(), + return ResultBuilder( m_lastAssertionInfo.macroName, m_lastAssertionInfo.lineInfo, - m_lastAssertionInfo.capturedExpression.c_str(), + m_lastAssertionInfo.capturedExpression, m_lastAssertionInfo.resultDisposition ); } @@ -6194,6 +7108,9 @@ namespace Catch { std::vector m_unfinishedSections; std::vector m_activeSections; TrackerContext m_trackerContext; + FatalConditionHandler m_fatalConditionhandler; + size_t m_prevPassed; + bool m_shouldReportUnexpected; }; IResultCapture& getResultCapture() { @@ -6215,7 +7132,7 @@ namespace Catch { Version( unsigned int _majorVersion, unsigned int _minorVersion, unsigned int _patchNumber, - std::string const& _branchName, + char const * const _branchName, unsigned int _buildNumber ); unsigned int const majorVersion; @@ -6223,7 +7140,7 @@ namespace Catch { unsigned int const patchNumber; // buildNumber is only used if branchName is not null - std::string const branchName; + char const * const branchName; unsigned int const buildNumber; friend std::ostream& operator << ( std::ostream& os, Version const& version ); @@ -6232,7 +7149,7 @@ namespace Catch { void operator=( Version const& ); }; - extern Version libraryVersion; + inline Version libraryVersion(); } #include @@ -6251,10 +7168,14 @@ namespace Catch { return reporter; } +#if !defined(CATCH_CONFIG_DEFAULT_REPORTER) +#define CATCH_CONFIG_DEFAULT_REPORTER "console" +#endif + Ptr makeReporter( Ptr const& config ) { std::vector reporters = config->getReporterNames(); if( reporters.empty() ) - reporters.push_back( "console" ); + reporters.push_back( CATCH_CONFIG_DEFAULT_REPORTER ); Ptr reporter; for( std::vector::const_iterator it = reporters.begin(), itEnd = reporters.end(); @@ -6314,11 +7235,11 @@ namespace Catch { if( lastSlash != std::string::npos ) filename = filename.substr( lastSlash+1 ); - std::string::size_type lastDot = filename.find_last_of( "." ); + std::string::size_type lastDot = filename.find_last_of( '.' ); if( lastDot != std::string::npos ) filename = filename.substr( 0, lastDot ); - tags.insert( "#" + filename ); + tags.insert( '#' + filename ); setTags( test, tags ); } } @@ -6344,11 +7265,18 @@ namespace Catch { } void showHelp( std::string const& processName ) { - Catch::cout() << "\nCatch v" << libraryVersion << "\n"; + Catch::cout() << "\nCatch v" << libraryVersion() << "\n"; m_cli.usage( Catch::cout(), processName ); Catch::cout() << "For more detail usage please see the project docs\n" << std::endl; } + void libIdentify() { + Catch::cout() + << std::left << std::setw(16) << "description: " << "A Catch test executable\n" + << std::left << std::setw(16) << "category: " << "testframework\n" + << std::left << std::setw(16) << "framework: " << "Catch Test\n" + << std::left << std::setw(16) << "version: " << libraryVersion() << std::endl; + } int applyCommandLine( int argc, char const* const* const argv, OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) { try { @@ -6356,6 +7284,8 @@ namespace Catch { m_unusedTokens = m_cli.parseInto( Clara::argsToVector( argc, argv ), m_configData ); if( m_configData.showHelp ) showHelp( m_configData.processName ); + if( m_configData.libIdentify ) + libIdentify(); m_config.reset(); } catch( std::exception& ex ) { @@ -6385,29 +7315,43 @@ namespace Catch { return returnCode; } - int run() { - if( m_configData.showHelp ) - return 0; + #if defined(WIN32) && defined(UNICODE) + int run( int argc, wchar_t const* const* const argv ) { - try - { - config(); // Force config to be constructed + char **utf8Argv = new char *[ argc ]; - seedRng( *m_config ); + for ( int i = 0; i < argc; ++i ) { + int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, NULL, 0, NULL, NULL ); - if( m_configData.filenamesAsTags ) - applyFilenamesAsTags( *m_config ); + utf8Argv[ i ] = new char[ bufSize ]; - // Handle list request - if( Option listed = list( config() ) ) - return static_cast( *listed ); + WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, NULL, NULL ); + } - return static_cast( runTests( m_config ).assertions.failed ); + int returnCode = applyCommandLine( argc, utf8Argv ); + if( returnCode == 0 ) + returnCode = run(); + + for ( int i = 0; i < argc; ++i ) + delete [] utf8Argv[ i ]; + + delete [] utf8Argv; + + return returnCode; + } + #endif + + int run() { + if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeStart ) != 0 ) { + Catch::cout() << "...waiting for enter/ return before starting" << std::endl; + static_cast(std::getchar()); } - catch( std::exception& ex ) { - Catch::cerr() << ex.what() << std::endl; - return (std::numeric_limits::max)(); + int exitCode = runInternal(); + if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeExit ) != 0 ) { + Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << std::endl; + static_cast(std::getchar()); } + return exitCode; } Clara::CommandLine const& cli() const { @@ -6425,6 +7369,32 @@ namespace Catch { return *m_config; } private: + + int runInternal() { + if( m_configData.showHelp || m_configData.libIdentify ) + return 0; + + try + { + config(); // Force config to be constructed + + seedRng( *m_config ); + + if( m_configData.filenamesAsTags ) + applyFilenamesAsTags( *m_config ); + + // Handle list request + if( Option listed = list( config() ) ) + return static_cast( *listed ); + + return static_cast( runTests( m_config ).assertions.failed ); + } + catch( std::exception& ex ) { + Catch::cerr() << ex.what() << std::endl; + return (std::numeric_limits::max)(); + } + } + Clara::CommandLine m_cli; std::vector m_unusedTokens; ConfigData m_configData; @@ -6444,29 +7414,24 @@ namespace Catch { #include #include #include -#include #include -#ifdef CATCH_CPP14_OR_GREATER -#include -#endif - namespace Catch { struct RandomNumberGenerator { - typedef int result_type; + typedef unsigned int result_type; result_type operator()( result_type n ) const { return std::rand() % n; } -#ifdef CATCH_CPP14_OR_GREATER - static constexpr result_type min() { return 0; } - static constexpr result_type max() { return 1000000; } - result_type operator()() const { return std::rand() % max(); } +#ifdef CATCH_CONFIG_CPP11_SHUFFLE + static constexpr result_type (min)() { return 0; } + static constexpr result_type (max)() { return 1000000; } + result_type operator()() const { return std::rand() % (max)(); } #endif template static void shuffle( V& vector ) { RandomNumberGenerator rng; -#ifdef CATCH_CPP14_OR_GREATER +#ifdef CATCH_CONFIG_CPP11_SHUFFLE std::shuffle( vector.begin(), vector.end(), rng ); #else std::random_shuffle( vector.begin(), vector.end(), rng ); @@ -6509,7 +7474,7 @@ namespace Catch { ss << Colour( Colour::Red ) << "error: TEST_CASE( \"" << it->name << "\" ) already defined.\n" - << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n" + << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << '\n' << "\tRedefined at " << it->getTestCaseInfo().lineInfo << std::endl; throw std::runtime_error(ss.str()); @@ -6541,7 +7506,7 @@ namespace Catch { virtual void registerTest( TestCase const& testCase ) { std::string name = testCase.getTestCaseInfo().name; - if( name == "" ) { + if( name.empty() ) { std::ostringstream oss; oss << "Anonymous test case " << ++m_unnamedCount; return registerTest( testCase.withName( oss.str() ) ); @@ -6589,16 +7554,16 @@ namespace Catch { }; inline std::string extractClassName( std::string const& classOrQualifiedMethodName ) { - std::string class_name = classOrQualifiedMethodName; - if( startsWith( class_name, "&" ) ) + std::string className = classOrQualifiedMethodName; + if( startsWith( className, '&' ) ) { - std::size_t lastColons = class_name.rfind( "::" ); - std::size_t penultimateColons = class_name.rfind( "::", lastColons-1 ); + std::size_t lastColons = className.rfind( "::" ); + std::size_t penultimateColons = className.rfind( "::", lastColons-1 ); if( penultimateColons == std::string::npos ) penultimateColons = 1; - class_name = class_name.substr( penultimateColons, lastColons-penultimateColons ); + className = className.substr( penultimateColons, lastColons-penultimateColons ); } - return class_name; + return className; } void registerTestCase @@ -6737,6 +7702,26 @@ namespace Catch { }; } +// #included from: catch_tag_alias_registry.h +#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_H_INCLUDED + +#include + +namespace Catch { + + class TagAliasRegistry : public ITagAliasRegistry { + public: + virtual ~TagAliasRegistry(); + virtual Option find( std::string const& alias ) const; + virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const; + void add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ); + + private: + std::map m_registry; + }; + +} // end namespace Catch + namespace Catch { namespace { @@ -6758,6 +7743,9 @@ namespace Catch { virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() CATCH_OVERRIDE { return m_exceptionTranslatorRegistry; } + virtual ITagAliasRegistry const& getTagAliasRegistry() const CATCH_OVERRIDE { + return m_tagAliasRegistry; + } public: // IMutableRegistryHub virtual void registerReporter( std::string const& name, Ptr const& factory ) CATCH_OVERRIDE { @@ -6772,11 +7760,15 @@ namespace Catch { virtual void registerTranslator( const IExceptionTranslator* translator ) CATCH_OVERRIDE { m_exceptionTranslatorRegistry.registerTranslator( translator ); } + virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) CATCH_OVERRIDE { + m_tagAliasRegistry.add( alias, tag, lineInfo ); + } private: TestRegistry m_testCaseRegistry; ReporterRegistry m_reporterRegistry; ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; + TagAliasRegistry m_tagAliasRegistry; }; // Single, global, instance @@ -6808,7 +7800,7 @@ namespace Catch { // #included from: catch_notimplemented_exception.hpp #define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_HPP_INCLUDED -#include +#include namespace Catch { @@ -6880,7 +7872,7 @@ namespace Catch { m_ofs.open( filename.c_str() ); if( m_ofs.fail() ) { std::ostringstream oss; - oss << "Unable to open file: '" << filename << "'"; + oss << "Unable to open file: '" << filename << '\''; throw std::domain_error( oss.str() ); } } @@ -6922,6 +7914,9 @@ namespace Catch { std::ostream& cerr() { return std::cerr; } + std::ostream& clog() { + return std::clog; + } #endif } @@ -6933,6 +7928,11 @@ namespace Catch { Context( Context const& ); void operator=( Context const& ); + public: + virtual ~Context() { + deleteAllValues( m_generatorsByTestName ); + } + public: // IContext virtual IResultCapture* getResultCapture() { return m_resultCapture; @@ -7016,6 +8016,23 @@ namespace Catch { // #included from: catch_console_colour_impl.hpp #define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_IMPL_HPP_INCLUDED +// #included from: catch_errno_guard.hpp +#define TWOBLUECUBES_CATCH_ERRNO_GUARD_HPP_INCLUDED + +#include + +namespace Catch { + + class ErrnoGuard { + public: + ErrnoGuard():m_oldErrno(errno){} + ~ErrnoGuard() { errno = m_oldErrno; } + private: + int m_oldErrno; + }; + +} + namespace Catch { namespace { @@ -7046,16 +8063,6 @@ namespace Catch { #if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) ///////////////////////////////////////// -#ifndef NOMINMAX -#define NOMINMAX -#endif - -#ifdef __AFXDLL -#include -#else -#include -#endif - namespace Catch { namespace { @@ -7136,7 +8143,7 @@ namespace { case Colour::White: return setColour( "[0m" ); case Colour::Red: return setColour( "[0;31m" ); case Colour::Green: return setColour( "[0;32m" ); - case Colour::Blue: return setColour( "[0:34m" ); + case Colour::Blue: return setColour( "[0;34m" ); case Colour::Cyan: return setColour( "[0;36m" ); case Colour::Yellow: return setColour( "[0;33m" ); case Colour::Grey: return setColour( "[1;30m" ); @@ -7161,6 +8168,7 @@ namespace { }; IColourImpl* platformColourInstance() { + ErrnoGuard guard; Ptr config = getCurrentContext().getConfig(); UseColour::YesOrNo colourMode = config ? config->useColour() @@ -7279,14 +8287,18 @@ namespace Catch { namespace Catch { - AssertionInfo::AssertionInfo( std::string const& _macroName, + AssertionInfo::AssertionInfo():macroName(""), capturedExpression(""), resultDisposition(ResultDisposition::Normal), secondArg(""){} + + AssertionInfo::AssertionInfo( char const * _macroName, SourceLineInfo const& _lineInfo, - std::string const& _capturedExpression, - ResultDisposition::Flags _resultDisposition ) + char const * _capturedExpression, + ResultDisposition::Flags _resultDisposition, + char const * _secondArg) : macroName( _macroName ), lineInfo( _lineInfo ), capturedExpression( _capturedExpression ), - resultDisposition( _resultDisposition ) + resultDisposition( _resultDisposition ), + secondArg( _secondArg ) {} AssertionResult::AssertionResult() {} @@ -7313,24 +8325,30 @@ namespace Catch { } bool AssertionResult::hasExpression() const { - return !m_info.capturedExpression.empty(); + return m_info.capturedExpression[0] != 0; } bool AssertionResult::hasMessage() const { return !m_resultData.message.empty(); } + std::string capturedExpressionWithSecondArgument( char const * capturedExpression, char const * secondArg ) { + return (secondArg[0] == 0 || secondArg[0] == '"' && secondArg[1] == '"') + ? capturedExpression + : std::string(capturedExpression) + ", " + secondArg; + } + std::string AssertionResult::getExpression() const { if( isFalseTest( m_info.resultDisposition ) ) - return "!" + m_info.capturedExpression; + return "!(" + capturedExpressionWithSecondArgument(m_info.capturedExpression, m_info.secondArg) + ")"; else - return m_info.capturedExpression; + return capturedExpressionWithSecondArgument(m_info.capturedExpression, m_info.secondArg); } std::string AssertionResult::getExpressionInMacro() const { - if( m_info.macroName.empty() ) - return m_info.capturedExpression; + if( m_info.macroName[0] == 0 ) + return capturedExpressionWithSecondArgument(m_info.capturedExpression, m_info.secondArg); else - return m_info.macroName + "( " + m_info.capturedExpression + " )"; + return std::string(m_info.macroName) + "( " + capturedExpressionWithSecondArgument(m_info.capturedExpression, m_info.secondArg) + " )"; } bool AssertionResult::hasExpandedExpression() const { @@ -7338,7 +8356,7 @@ namespace Catch { } std::string AssertionResult::getExpandedExpression() const { - return m_resultData.reconstructedExpression; + return m_resultData.reconstructExpression(); } std::string AssertionResult::getMessage() const { @@ -7352,15 +8370,25 @@ namespace Catch { return m_info.macroName; } + void AssertionResult::discardDecomposedExpression() const { + m_resultData.decomposedExpression = CATCH_NULL; + } + + void AssertionResult::expandDecomposedExpression() const { + m_resultData.reconstructExpression(); + } + } // end namespace Catch // #included from: catch_test_case_info.hpp #define TWOBLUECUBES_CATCH_TEST_CASE_INFO_HPP_INCLUDED +#include + namespace Catch { inline TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) { - if( startsWith( tag, "." ) || + if( startsWith( tag, '.' ) || tag == "hide" || tag == "!hide" ) return TestCaseInfo::IsHidden; @@ -7370,30 +8398,28 @@ namespace Catch { return TestCaseInfo::ShouldFail; else if( tag == "!mayfail" ) return TestCaseInfo::MayFail; + else if( tag == "!nonportable" ) + return TestCaseInfo::NonPortable; else return TestCaseInfo::None; } inline bool isReservedTag( std::string const& tag ) { - return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !isalnum( tag[0] ); + return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !std::isalnum( tag[0] ); } inline void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) { if( isReservedTag( tag ) ) { - { - Colour colourGuard( Colour::Red ); - Catch::cerr() - << "Tag name [" << tag << "] not allowed.\n" - << "Tag names starting with non alpha-numeric characters are reserved\n"; - } - { - Colour colourGuard( Colour::FileName ); - Catch::cerr() << _lineInfo << std::endl; - } - exit(1); + std::ostringstream ss; + ss << Colour(Colour::Red) + << "Tag name [" << tag << "] not allowed.\n" + << "Tag names starting with non alpha-numeric characters are reserved\n" + << Colour(Colour::FileName) + << _lineInfo << '\n'; + throw std::runtime_error(ss.str()); } } TestCase makeTestCase( ITestCase* _testCase, - std::string const& _class_name, + std::string const& _className, std::string const& _name, std::string const& _descOrTags, SourceLineInfo const& _lineInfo ) @@ -7433,7 +8459,7 @@ namespace Catch { tags.insert( "." ); } - TestCaseInfo info( _name, _class_name, desc, tags, _lineInfo ); + TestCaseInfo info( _name, _className, desc, tags, _lineInfo ); return TestCase( _testCase, info ); } @@ -7444,7 +8470,7 @@ namespace Catch { std::ostringstream oss; for( std::set::const_iterator it = tags.begin(), itEnd = tags.end(); it != itEnd; ++it ) { - oss << "[" << *it << "]"; + oss << '[' << *it << ']'; std::string lcaseTag = toLower( *it ); testCaseInfo.properties = static_cast( testCaseInfo.properties | parseSpecialTag( lcaseTag ) ); testCaseInfo.lcaseTags.insert( lcaseTag ); @@ -7453,12 +8479,12 @@ namespace Catch { } TestCaseInfo::TestCaseInfo( std::string const& _name, - std::string const& _class_name, + std::string const& _className, std::string const& _description, std::set const& _tags, SourceLineInfo const& _lineInfo ) : name( _name ), - class_name( _class_name ), + className( _className ), description( _description ), lineInfo( _lineInfo ), properties( None ) @@ -7468,7 +8494,7 @@ namespace Catch { TestCaseInfo::TestCaseInfo( TestCaseInfo const& other ) : name( other.name ), - class_name( other.class_name ), + className( other.className ), description( other.description ), tags( other.tags ), lcaseTags( other.lcaseTags ), @@ -7506,7 +8532,7 @@ namespace Catch { void TestCase::swap( TestCase& other ) { test.swap( other.test ); name.swap( other.name ); - class_name.swap( other.class_name ); + className.swap( other.className ); description.swap( other.description ); tags.swap( other.tags ); lcaseTags.swap( other.lcaseTags ); @@ -7522,7 +8548,7 @@ namespace Catch { bool TestCase::operator == ( TestCase const& other ) const { return test.get() == other.test.get() && name == other.name && - class_name == other.class_name; + className == other.className; } bool TestCase::operator < ( TestCase const& other ) const { @@ -7550,7 +8576,7 @@ namespace Catch { ( unsigned int _majorVersion, unsigned int _minorVersion, unsigned int _patchNumber, - std::string const& _branchName, + char const * const _branchName, unsigned int _buildNumber ) : majorVersion( _majorVersion ), minorVersion( _minorVersion ), @@ -7560,18 +8586,21 @@ namespace Catch { {} std::ostream& operator << ( std::ostream& os, Version const& version ) { - os << version.majorVersion << "." - << version.minorVersion << "." + os << version.majorVersion << '.' + << version.minorVersion << '.' << version.patchNumber; - - if( !version.branchName.empty() ) { - os << "-" << version.branchName - << "." << version.buildNumber; + // branchName is never null -> 0th char is \0 if it is empty + if (version.branchName[0]) { + os << '-' << version.branchName + << '.' << version.buildNumber; } return os; } - Version libraryVersion( 1, 5, 6, "", 0 ); + inline Version libraryVersion() { + static Version version( 1, 12, 2, "", 0 ); + return version; + } } @@ -7604,9 +8633,18 @@ namespace Catch { : m_info( other.m_info ) {} +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4996) // std::uncaught_exception is deprecated in C++17 +#endif ScopedMessage::~ScopedMessage() { - getResultCapture().popScopedMessage( m_info ); + if ( !std::uncaught_exception() ){ + getResultCapture().popScopedMessage(m_info); + } } +#if defined(_MSC_VER) +#pragma warning(pop) +#endif } // end namespace Catch @@ -7742,30 +8780,32 @@ namespace Catch #endif #ifdef CATCH_PLATFORM_WINDOWS -#include + #else + #include + #endif namespace Catch { namespace { #ifdef CATCH_PLATFORM_WINDOWS - uint64_t getCurrentTicks() { - static uint64_t hz=0, hzo=0; + UInt64 getCurrentTicks() { + static UInt64 hz=0, hzo=0; if (!hz) { QueryPerformanceFrequency( reinterpret_cast( &hz ) ); QueryPerformanceCounter( reinterpret_cast( &hzo ) ); } - uint64_t t; + UInt64 t; QueryPerformanceCounter( reinterpret_cast( &t ) ); return ((t-hzo)*1000000)/hz; } #else - uint64_t getCurrentTicks() { + UInt64 getCurrentTicks() { timeval t; gettimeofday(&t,CATCH_NULL); - return static_cast( t.tv_sec ) * 1000000ull + static_cast( t.tv_usec ); + return static_cast( t.tv_sec ) * 1000000ull + static_cast( t.tv_usec ); } #endif } @@ -7791,19 +8831,31 @@ namespace Catch { // #included from: catch_common.hpp #define TWOBLUECUBES_CATCH_COMMON_HPP_INCLUDED +#include +#include + namespace Catch { bool startsWith( std::string const& s, std::string const& prefix ) { - return s.size() >= prefix.size() && s.substr( 0, prefix.size() ) == prefix; + return s.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), s.begin()); + } + bool startsWith( std::string const& s, char prefix ) { + return !s.empty() && s[0] == prefix; } bool endsWith( std::string const& s, std::string const& suffix ) { - return s.size() >= suffix.size() && s.substr( s.size()-suffix.size(), suffix.size() ) == suffix; + return s.size() >= suffix.size() && std::equal(suffix.rbegin(), suffix.rend(), s.rbegin()); + } + bool endsWith( std::string const& s, char suffix ) { + return !s.empty() && s[s.size()-1] == suffix; } bool contains( std::string const& s, std::string const& infix ) { return s.find( infix ) != std::string::npos; } + char toLowerCh(char c) { + return static_cast( std::tolower( c ) ); + } void toLowerInPlace( std::string& s ) { - std::transform( s.begin(), s.end(), s.begin(), ::tolower ); + std::transform( s.begin(), s.end(), s.begin(), toLowerCh ); } std::string toLower( std::string const& s ) { std::string lc = s; @@ -7815,7 +8867,7 @@ namespace Catch { std::string::size_type start = str.find_first_not_of( whitespaceChars ); std::string::size_type end = str.find_last_not_of( whitespaceChars ); - return start != std::string::npos ? str.substr( start, 1+end-start ) : ""; + return start != std::string::npos ? str.substr( start, 1+end-start ) : std::string(); } bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) { @@ -7838,29 +8890,25 @@ namespace Catch { {} std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) { - os << pluraliser.m_count << " " << pluraliser.m_label; + os << pluraliser.m_count << ' ' << pluraliser.m_label; if( pluraliser.m_count != 1 ) - os << "s"; + os << 's'; return os; } - SourceLineInfo::SourceLineInfo() : line( 0 ){} + SourceLineInfo::SourceLineInfo() : file(""), line( 0 ){} SourceLineInfo::SourceLineInfo( char const* _file, std::size_t _line ) : file( _file ), line( _line ) {} - SourceLineInfo::SourceLineInfo( SourceLineInfo const& other ) - : file( other.file ), - line( other.line ) - {} bool SourceLineInfo::empty() const { - return file.empty(); + return file[0] == '\0'; } bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const { - return line == other.line && file == other.file; + return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0); } bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const { - return line < other.line || ( line == other.line && file < other.file ); + return line < other.line || ( line == other.line && (std::strcmp(file, other.file) < 0)); } void seedRng( IConfig const& config ) { @@ -7873,16 +8921,16 @@ namespace Catch { std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) { #ifndef __GNUG__ - os << info.file << "(" << info.line << ")"; + os << info.file << '(' << info.line << ')'; #else - os << info.file << ":" << info.line; + os << info.file << ':' << info.line; #endif return os; } void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ) { std::ostringstream oss; - oss << locationInfo << ": Internal Catch error: '" << message << "'"; + oss << locationInfo << ": Internal Catch error: '" << message << '\''; if( alwaysTrue() ) throw std::logic_error( oss.str() ); } @@ -7909,6 +8957,10 @@ namespace Catch { m_timer.start(); } +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4996) // std::uncaught_exception is deprecated in C++17 +#endif Section::~Section() { if( m_sectionIncluded ) { SectionEndInfo endInfo( m_info, m_assertions, m_timer.getElapsedSeconds() ); @@ -7918,6 +8970,9 @@ namespace Catch { getResultCapture().sectionEnded( endInfo ); } } +#if defined(_MSC_VER) +#pragma warning(pop) +#endif // This indicates whether the section should be executed or not Section::operator bool() const { @@ -7929,8 +8984,6 @@ namespace Catch { // #included from: catch_debugger.hpp #define TWOBLUECUBES_CATCH_DEBUGGER_HPP_INCLUDED -#include - #ifdef CATCH_PLATFORM_MAC #include @@ -7979,6 +9032,36 @@ namespace Catch { } } // namespace Catch +#elif defined(CATCH_PLATFORM_LINUX) + #include + #include + + namespace Catch{ + // The standard POSIX way of detecting a debugger is to attempt to + // ptrace() the process, but this needs to be done from a child and not + // this process itself to still allow attaching to this process later + // if wanted, so is rather heavy. Under Linux we have the PID of the + // "debugger" (which doesn't need to be gdb, of course, it could also + // be strace, for example) in /proc/$PID/status, so just get it from + // there instead. + bool isDebuggerActive(){ + // Libstdc++ has a bug, where std::ifstream sets errno to 0 + // This way our users can properly assert over errno values + ErrnoGuard guard; + std::ifstream in("/proc/self/status"); + for( std::string line; std::getline(in, line); ) { + static const int PREFIX_LEN = 11; + if( line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0 ) { + // We're traced if the PID is not 0 and no other PID starts + // with 0 digit, so it's enough to check for just a single + // character. + return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0'; + } + } + + return false; + } + } // namespace Catch #elif defined(_MSC_VER) extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); namespace Catch { @@ -8000,7 +9083,7 @@ namespace Catch { #endif // Platform #ifdef CATCH_PLATFORM_WINDOWS - extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA( const char* ); + namespace Catch { void writeToDebugConsole( std::string const& text ) { ::OutputDebugStringA( text.c_str() ); @@ -8076,7 +9159,7 @@ std::string toString( std::string const& value ) { } } } - return "\"" + s + "\""; + return '"' + s + '"'; } std::string toString( std::wstring const& value ) { @@ -8097,19 +9180,19 @@ std::string toString( char* const value ) { std::string toString( const wchar_t* const value ) { - return value ? Catch::toString( std::wstring(value) ) : std::string( "{null string}" ); + return value ? Catch::toString( std::wstring(value) ) : std::string( "{null string}" ); } std::string toString( wchar_t* const value ) { - return Catch::toString( static_cast( value ) ); + return Catch::toString( static_cast( value ) ); } std::string toString( int value ) { std::ostringstream oss; oss << value; if( value > Detail::hexThreshold ) - oss << " (0x" << std::hex << value << ")"; + oss << " (0x" << std::hex << value << ')'; return oss.str(); } @@ -8117,7 +9200,7 @@ std::string toString( unsigned long value ) { std::ostringstream oss; oss << value; if( value > Detail::hexThreshold ) - oss << " (0x" << std::hex << value << ")"; + oss << " (0x" << std::hex << value << ')'; return oss.str(); } @@ -8145,7 +9228,7 @@ std::string toString( const double value ) { return fpToString( value, 10 ); } std::string toString( const float value ) { - return fpToString( value, 5 ) + "f"; + return fpToString( value, 5 ) + 'f'; } std::string toString( bool value ) { @@ -8153,9 +9236,19 @@ std::string toString( bool value ) { } std::string toString( char value ) { - return value < ' ' - ? toString( static_cast( value ) ) - : Detail::makeString( value ); + if ( value == '\r' ) + return "'\\r'"; + if ( value == '\f' ) + return "'\\f'"; + if ( value == '\n' ) + return "'\\n'"; + if ( value == '\t' ) + return "'\\t'"; + if ( '\0' <= value && value < ' ' ) + return toString( static_cast( value ) ); + char chstr[] = "' '"; + chstr[1] = value; + return chstr; } std::string toString( signed char value ) { @@ -8171,14 +9264,14 @@ std::string toString( long long value ) { std::ostringstream oss; oss << value; if( value > Detail::hexThreshold ) - oss << " (0x" << std::hex << value << ")"; + oss << " (0x" << std::hex << value << ')'; return oss.str(); } std::string toString( unsigned long long value ) { std::ostringstream oss; oss << value; if( value > Detail::hexThreshold ) - oss << " (0x" << std::hex << value << ")"; + oss << " (0x" << std::hex << value << ')'; return oss.str(); } #endif @@ -8195,7 +9288,7 @@ std::string toString( std::nullptr_t ) { return "nil"; return "@" + toString([nsstring UTF8String]); } - std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ) { + std::string toString( NSString * CATCH_ARC_STRONG & nsstring ) { if( !nsstring ) return "nil"; return "@" + toString([nsstring UTF8String]); @@ -8210,23 +9303,32 @@ std::string toString( std::nullptr_t ) { // #included from: catch_result_builder.hpp #define TWOBLUECUBES_CATCH_RESULT_BUILDER_HPP_INCLUDED +#include + namespace Catch { - std::string capturedExpressionWithSecondArgument( std::string const& capturedExpression, std::string const& secondArg ) { - return secondArg.empty() || secondArg == "\"\"" - ? capturedExpression - : capturedExpression + ", " + secondArg; - } ResultBuilder::ResultBuilder( char const* macroName, SourceLineInfo const& lineInfo, char const* capturedExpression, ResultDisposition::Flags resultDisposition, char const* secondArg ) - : m_assertionInfo( macroName, lineInfo, capturedExpressionWithSecondArgument( capturedExpression, secondArg ), resultDisposition ), + : m_assertionInfo( macroName, lineInfo, capturedExpression, resultDisposition, secondArg ), m_shouldDebugBreak( false ), - m_shouldThrow( false ) + m_shouldThrow( false ), + m_guardException( false ), + m_usedStream( false ) {} + ResultBuilder::~ResultBuilder() { +#if defined(CATCH_CONFIG_FAST_COMPILE) + if ( m_guardException ) { + stream().oss << "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"; + captureResult( ResultWas::ThrewException ); + getCurrentContext().getResultCapture()->exceptionEarlyReported(); + } +#endif + } + ResultBuilder& ResultBuilder::setResultType( ResultWas::OfType result ) { m_data.resultType = result; return *this; @@ -8235,27 +9337,27 @@ namespace Catch { m_data.resultType = result ? ResultWas::Ok : ResultWas::ExpressionFailed; return *this; } - ResultBuilder& ResultBuilder::setLhs( std::string const& lhs ) { - m_exprComponents.lhs = lhs; - return *this; - } - ResultBuilder& ResultBuilder::setRhs( std::string const& rhs ) { - m_exprComponents.rhs = rhs; - return *this; - } - ResultBuilder& ResultBuilder::setOp( std::string const& op ) { - m_exprComponents.op = op; - return *this; - } - void ResultBuilder::endExpression() { - m_exprComponents.testFalse = isFalseTest( m_assertionInfo.resultDisposition ); - captureExpression(); + void ResultBuilder::endExpression( DecomposedExpression const& expr ) { + // Flip bool results if FalseTest flag is set + if( isFalseTest( m_assertionInfo.resultDisposition ) ) { + m_data.negate( expr.isBinaryExpression() ); + } + + getResultCapture().assertionRun(); + + if(getCurrentContext().getConfig()->includeSuccessfulResults() || m_data.resultType != ResultWas::Ok) + { + AssertionResult result = build( expr ); + handleResult( result ); + } + else + getResultCapture().assertionPassed(); } void ResultBuilder::useActiveException( ResultDisposition::Flags resultDisposition ) { m_assertionInfo.resultDisposition = resultDisposition; - m_stream.oss << Catch::translateActiveException(); + stream().oss << Catch::translateActiveException(); captureResult( ResultWas::ThrewException ); } @@ -8263,19 +9365,20 @@ namespace Catch { setResultType( resultType ); captureExpression(); } + void ResultBuilder::captureExpectedException( std::string const& expectedMessage ) { if( expectedMessage.empty() ) - captureExpectedException( Matchers::Impl::Generic::AllOf() ); + captureExpectedException( Matchers::Impl::MatchAllOf() ); else captureExpectedException( Matchers::Equals( expectedMessage ) ); } - void ResultBuilder::captureExpectedException( Matchers::Impl::Matcher const& matcher ) { + void ResultBuilder::captureExpectedException( Matchers::Impl::MatcherBase const& matcher ) { - assert( m_exprComponents.testFalse == false ); + assert( !isFalseTest( m_assertionInfo.resultDisposition ) ); AssertionResultData data = m_data; data.resultType = ResultWas::Ok; - data.reconstructedExpression = m_assertionInfo.capturedExpression; + data.reconstructedExpression = capturedExpressionWithSecondArgument(m_assertionInfo.capturedExpression, m_assertionInfo.secondArg); std::string actualMessage = Catch::translateActiveException(); if( !matcher.match( actualMessage ) ) { @@ -8290,6 +9393,7 @@ namespace Catch { AssertionResult result = build(); handleResult( result ); } + void ResultBuilder::handleResult( AssertionResult const& result ) { getResultCapture().assertionEnded( result ); @@ -8301,7 +9405,17 @@ namespace Catch { m_shouldThrow = true; } } + void ResultBuilder::react() { +#if defined(CATCH_CONFIG_FAST_COMPILE) + if (m_shouldDebugBreak) { + /////////////////////////////////////////////////////////////////// + // To inspect the state during test, you need to go one level up the callstack + // To go back to the test and change execution, jump over the throw statement + /////////////////////////////////////////////////////////////////// + CATCH_BREAK_INTO_DEBUGGER(); + } +#endif if( m_shouldThrow ) throw Catch::TestFailureException(); } @@ -8311,43 +9425,35 @@ namespace Catch { AssertionResult ResultBuilder::build() const { - assert( m_data.resultType != ResultWas::Unknown ); + return build( *this ); + } + // CAVEAT: The returned AssertionResult stores a pointer to the argument expr, + // a temporary DecomposedExpression, which in turn holds references to + // operands, possibly temporary as well. + // It should immediately be passed to handleResult; if the expression + // needs to be reported, its string expansion must be composed before + // the temporaries are destroyed. + AssertionResult ResultBuilder::build( DecomposedExpression const& expr ) const + { + assert( m_data.resultType != ResultWas::Unknown ); AssertionResultData data = m_data; - // Flip bool results if testFalse is set - if( m_exprComponents.testFalse ) { - if( data.resultType == ResultWas::Ok ) - data.resultType = ResultWas::ExpressionFailed; - else if( data.resultType == ResultWas::ExpressionFailed ) - data.resultType = ResultWas::Ok; - } - - data.message = m_stream.oss.str(); - data.reconstructedExpression = reconstructExpression(); - if( m_exprComponents.testFalse ) { - if( m_exprComponents.op == "" ) - data.reconstructedExpression = "!" + data.reconstructedExpression; - else - data.reconstructedExpression = "!(" + data.reconstructedExpression + ")"; - } + if(m_usedStream) + data.message = m_stream().oss.str(); + data.decomposedExpression = &expr; // for lazy reconstruction return AssertionResult( m_assertionInfo, data ); } - std::string ResultBuilder::reconstructExpression() const { - if( m_exprComponents.op == "" ) - return m_exprComponents.lhs.empty() ? m_assertionInfo.capturedExpression : m_exprComponents.op + m_exprComponents.lhs; - else if( m_exprComponents.op == "matches" ) - return m_exprComponents.lhs + " " + m_exprComponents.rhs; - else if( m_exprComponents.op != "!" ) { - if( m_exprComponents.lhs.size() + m_exprComponents.rhs.size() < 40 && - m_exprComponents.lhs.find("\n") == std::string::npos && - m_exprComponents.rhs.find("\n") == std::string::npos ) - return m_exprComponents.lhs + " " + m_exprComponents.op + " " + m_exprComponents.rhs; - else - return m_exprComponents.lhs + "\n" + m_exprComponents.op + "\n" + m_exprComponents.rhs; - } - else - return "{can't expand - use " + m_assertionInfo.macroName + "_FALSE( " + m_assertionInfo.capturedExpression.substr(1) + " ) instead of " + m_assertionInfo.macroName + "( " + m_assertionInfo.capturedExpression + " ) for better diagnostics}"; + + void ResultBuilder::reconstructExpression( std::string& dest ) const { + dest = capturedExpressionWithSecondArgument(m_assertionInfo.capturedExpression, m_assertionInfo.secondArg); + } + + void ResultBuilder::setExceptionGuard() { + m_guardException = true; + } + void ResultBuilder::unsetExceptionGuard() { + m_guardException = false; } } // end namespace Catch @@ -8355,30 +9461,6 @@ namespace Catch { // #included from: catch_tag_alias_registry.hpp #define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_HPP_INCLUDED -// #included from: catch_tag_alias_registry.h -#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_H_INCLUDED - -#include - -namespace Catch { - - class TagAliasRegistry : public ITagAliasRegistry { - public: - virtual ~TagAliasRegistry(); - virtual Option find( std::string const& alias ) const; - virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const; - void add( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); - static TagAliasRegistry& get(); - - private: - std::map m_registry; - }; - -} // end namespace Catch - -#include -#include - namespace Catch { TagAliasRegistry::~TagAliasRegistry() {} @@ -8406,44 +9488,120 @@ namespace Catch { return expandedTestSpec; } - void TagAliasRegistry::add( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) { + void TagAliasRegistry::add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) { - if( !startsWith( alias, "[@" ) || !endsWith( alias, "]" ) ) { + if( !startsWith( alias, "[@" ) || !endsWith( alias, ']' ) ) { std::ostringstream oss; - oss << "error: tag alias, \"" << alias << "\" is not of the form [@alias name].\n" << lineInfo; + oss << Colour( Colour::Red ) + << "error: tag alias, \"" << alias << "\" is not of the form [@alias name].\n" + << Colour( Colour::FileName ) + << lineInfo << '\n'; throw std::domain_error( oss.str().c_str() ); } if( !m_registry.insert( std::make_pair( alias, TagAlias( tag, lineInfo ) ) ).second ) { std::ostringstream oss; - oss << "error: tag alias, \"" << alias << "\" already registered.\n" - << "\tFirst seen at " << find(alias)->lineInfo << "\n" - << "\tRedefined at " << lineInfo; + oss << Colour( Colour::Red ) + << "error: tag alias, \"" << alias << "\" already registered.\n" + << "\tFirst seen at " + << Colour( Colour::Red ) << find(alias)->lineInfo << '\n' + << Colour( Colour::Red ) << "\tRedefined at " + << Colour( Colour::FileName) << lineInfo << '\n'; throw std::domain_error( oss.str().c_str() ); } } - TagAliasRegistry& TagAliasRegistry::get() { - static TagAliasRegistry instance; - return instance; + ITagAliasRegistry::~ITagAliasRegistry() {} + + ITagAliasRegistry const& ITagAliasRegistry::get() { + return getRegistryHub().getTagAliasRegistry(); + } + RegistrarForTagAliases::RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) { + getMutableRegistryHub().registerTagAlias( alias, tag, lineInfo ); } - ITagAliasRegistry::~ITagAliasRegistry() {} - ITagAliasRegistry const& ITagAliasRegistry::get() { return TagAliasRegistry::get(); } +} // end namespace Catch - RegistrarForTagAliases::RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) { - try { - TagAliasRegistry::get().add( alias, tag, lineInfo ); +// #included from: catch_matchers_string.hpp + +namespace Catch { +namespace Matchers { + + namespace StdString { + + CasedString::CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ) + : m_caseSensitivity( caseSensitivity ), + m_str( adjustString( str ) ) + {} + std::string CasedString::adjustString( std::string const& str ) const { + return m_caseSensitivity == CaseSensitive::No + ? toLower( str ) + : str; } - catch( std::exception& ex ) { - Colour colourGuard( Colour::Red ); - Catch::cerr() << ex.what() << std::endl; - exit(1); + std::string CasedString::caseSensitivitySuffix() const { + return m_caseSensitivity == CaseSensitive::No + ? " (case insensitive)" + : std::string(); } - } -} // end namespace Catch + StringMatcherBase::StringMatcherBase( std::string const& operation, CasedString const& comparator ) + : m_comparator( comparator ), + m_operation( operation ) { + } + + std::string StringMatcherBase::describe() const { + std::string description; + description.reserve(5 + m_operation.size() + m_comparator.m_str.size() + + m_comparator.caseSensitivitySuffix().size()); + description += m_operation; + description += ": \""; + description += m_comparator.m_str; + description += "\""; + description += m_comparator.caseSensitivitySuffix(); + return description; + } + + EqualsMatcher::EqualsMatcher( CasedString const& comparator ) : StringMatcherBase( "equals", comparator ) {} + + bool EqualsMatcher::match( std::string const& source ) const { + return m_comparator.adjustString( source ) == m_comparator.m_str; + } + + ContainsMatcher::ContainsMatcher( CasedString const& comparator ) : StringMatcherBase( "contains", comparator ) {} + + bool ContainsMatcher::match( std::string const& source ) const { + return contains( m_comparator.adjustString( source ), m_comparator.m_str ); + } + + StartsWithMatcher::StartsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "starts with", comparator ) {} + + bool StartsWithMatcher::match( std::string const& source ) const { + return startsWith( m_comparator.adjustString( source ), m_comparator.m_str ); + } + + EndsWithMatcher::EndsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "ends with", comparator ) {} + + bool EndsWithMatcher::match( std::string const& source ) const { + return endsWith( m_comparator.adjustString( source ), m_comparator.m_str ); + } + + } // namespace StdString + + StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity ) { + return StdString::EqualsMatcher( StdString::CasedString( str, caseSensitivity) ); + } + StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity ) { + return StdString::ContainsMatcher( StdString::CasedString( str, caseSensitivity) ); + } + StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) { + return StdString::EndsWithMatcher( StdString::CasedString( str, caseSensitivity) ); + } + StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) { + return StdString::StartsWithMatcher( StdString::CasedString( str, caseSensitivity) ); + } +} // namespace Matchers +} // namespace Catch // #included from: ../reporters/catch_reporter_multi.hpp #define TWOBLUECUBES_CATCH_REPORTER_MULTI_HPP_INCLUDED @@ -8587,9 +9745,34 @@ Ptr addReporter( Ptr const& existingRepo #define TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED #include +#include +#include +#include namespace Catch { + namespace { + // Because formatting using c++ streams is stateful, drop down to C is required + // Alternatively we could use stringstream, but its performance is... not good. + std::string getFormattedDuration( double duration ) { + // Max exponent + 1 is required to represent the whole part + // + 1 for decimal point + // + 3 for the 3 decimal places + // + 1 for null terminator + const size_t maxDoubleSize = DBL_MAX_10_EXP + 1 + 1 + 3 + 1; + char buffer[maxDoubleSize]; + + // Save previous errno, to prevent sprintf from overwriting it + ErrnoGuard guard; +#ifdef _MSC_VER + sprintf_s(buffer, "%.3f", duration); +#else + sprintf(buffer, "%.3f", duration); +#endif + return std::string(buffer); + } + } + struct StreamingReporterBase : SharedImpl { StreamingReporterBase( ReporterConfig const& _config ) @@ -8684,12 +9867,13 @@ namespace Catch { struct BySectionInfo { BySectionInfo( SectionInfo const& other ) : m_other( other ) {} - BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {} + BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {} bool operator() ( Ptr const& node ) const { - return node->stats.sectionInfo.lineInfo == m_other.lineInfo; + return ((node->stats.sectionInfo.name == m_other.name) && + (node->stats.sectionInfo.lineInfo == m_other.lineInfo)); } private: - void operator=( BySectionInfo const& ); + void operator=( BySectionInfo const& ); SectionInfo const& m_other; }; @@ -8745,6 +9929,12 @@ namespace Catch { assert( !m_sectionStack.empty() ); SectionNode& sectionNode = *m_sectionStack.back(); sectionNode.assertions.push_back( assertionStats ); + // AssertionResult holds a pointer to a temporary DecomposedExpression, + // which getExpandedExpression() calls to build the expression string. + // Our section stack copy of the assertionResult will likely outlive the + // temporary, so it must be expanded or discarded now to avoid calling + // a destroyed object later. + prepareExpandedExpression( sectionNode.assertions.back().assertionResult ); return true; } virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE { @@ -8779,6 +9969,13 @@ namespace Catch { virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE {} + virtual void prepareExpandedExpression( AssertionResult& result ) const { + if( result.isOk() ) + result.discardDecomposedExpression(); + else + result.expandDecomposedExpression(); + } + Ptr m_config; std::ostream& stream; std::vector m_assertions; @@ -8799,7 +9996,7 @@ namespace Catch { char const* getLineOfChars() { static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0}; if( !*line ) { - memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 ); + std::memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 ); line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0; } return line; @@ -8884,7 +10081,7 @@ namespace Catch { return new T( config ); } virtual std::string getDescription() const { - return ""; + return std::string(); } }; @@ -8902,9 +10099,13 @@ namespace Catch { #define INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) \ namespace{ Catch::ReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } +// Deprecated - use the form without INTERNAL_ #define INTERNAL_CATCH_REGISTER_LISTENER( listenerType ) \ namespace{ Catch::ListenerRegistrar catch_internal_RegistrarFor##listenerType; } +#define CATCH_REGISTER_LISTENER( listenerType ) \ + namespace{ Catch::ListenerRegistrar catch_internal_RegistrarFor##listenerType; } + // #included from: ../internal/catch_xmlwriter.hpp #define TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED @@ -8951,9 +10152,13 @@ namespace Catch { break; default: - // Escape control chars - based on contribution by @espenalb in PR #465 - if ( ( c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' ) - os << "&#x" << std::uppercase << std::hex << static_cast( c ); + // Escape control chars - based on contribution by @espenalb in PR #465 and + // by @mrpi PR #588 + if ( ( c >= 0 && c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' ) { + // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 + os << "\\x" << std::uppercase << std::hex << std::setfill('0') << std::setw(2) + << static_cast( c ); + } else os << c; } @@ -9007,14 +10212,18 @@ namespace Catch { XmlWriter() : m_tagIsOpen( false ), m_needsNewline( false ), - m_os( &Catch::cout() ) - {} + m_os( Catch::cout() ) + { + writeDeclaration(); + } XmlWriter( std::ostream& os ) : m_tagIsOpen( false ), m_needsNewline( false ), - m_os( &os ) - {} + m_os( os ) + { + writeDeclaration(); + } ~XmlWriter() { while( !m_tags.empty() ) @@ -9024,7 +10233,7 @@ namespace Catch { XmlWriter& startElement( std::string const& name ) { ensureTagClosed(); newlineIfNecessary(); - stream() << m_indent << "<" << name; + m_os << m_indent << '<' << name; m_tags.push_back( name ); m_indent += " "; m_tagIsOpen = true; @@ -9041,24 +10250,25 @@ namespace Catch { newlineIfNecessary(); m_indent = m_indent.substr( 0, m_indent.size()-2 ); if( m_tagIsOpen ) { - stream() << "/>\n"; + m_os << "/>"; m_tagIsOpen = false; } else { - stream() << m_indent << "\n"; + m_os << m_indent << ""; } + m_os << std::endl; m_tags.pop_back(); return *this; } XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ) { if( !name.empty() && !attribute.empty() ) - stream() << " " << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << "\""; + m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"'; return *this; } XmlWriter& writeAttribute( std::string const& name, bool attribute ) { - stream() << " " << name << "=\"" << ( attribute ? "true" : "false" ) << "\""; + m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"'; return *this; } @@ -9074,8 +10284,8 @@ namespace Catch { bool tagWasOpen = m_tagIsOpen; ensureTagClosed(); if( tagWasOpen && indent ) - stream() << m_indent; - stream() << XmlEncode( text ); + m_os << m_indent; + m_os << XmlEncode( text ); m_needsNewline = true; } return *this; @@ -9083,39 +10293,39 @@ namespace Catch { XmlWriter& writeComment( std::string const& text ) { ensureTagClosed(); - stream() << m_indent << ""; + m_os << m_indent << ""; m_needsNewline = true; return *this; } + void writeStylesheetRef( std::string const& url ) { + m_os << "\n"; + } + XmlWriter& writeBlankLine() { ensureTagClosed(); - stream() << "\n"; + m_os << '\n'; return *this; } - void setStream( std::ostream& os ) { - m_os = &os; + void ensureTagClosed() { + if( m_tagIsOpen ) { + m_os << ">" << std::endl; + m_tagIsOpen = false; + } } private: XmlWriter( XmlWriter const& ); void operator=( XmlWriter const& ); - std::ostream& stream() { - return *m_os; - } - - void ensureTagClosed() { - if( m_tagIsOpen ) { - stream() << ">\n"; - m_tagIsOpen = false; - } + void writeDeclaration() { + m_os << "\n"; } void newlineIfNecessary() { if( m_needsNewline ) { - stream() << "\n"; + m_os << std::endl; m_needsNewline = false; } } @@ -9124,30 +10334,17 @@ namespace Catch { bool m_needsNewline; std::vector m_tags; std::string m_indent; - std::ostream* m_os; + std::ostream& m_os; }; } -// #included from: catch_reenable_warnings.h - -#define TWOBLUECUBES_CATCH_REENABLE_WARNINGS_H_INCLUDED - -#ifdef __clang__ -# ifdef __ICC // icpc defines the __clang__ macro -# pragma warning(pop) -# else -# pragma clang diagnostic pop -# endif -#elif defined __GNUC__ -# pragma GCC diagnostic pop -#endif - namespace Catch { class XmlReporter : public StreamingReporterBase { public: XmlReporter( ReporterConfig const& _config ) : StreamingReporterBase( _config ), + m_xml(_config.stream()), m_sectionDepth( 0 ) { m_reporterPrefs.shouldRedirectStdOut = true; @@ -9159,6 +10356,16 @@ namespace Catch { return "Reports test results as an XML document"; } + virtual std::string getStylesheetRef() const { + return std::string(); + } + + void writeSourceInfo( SourceLineInfo const& sourceInfo ) { + m_xml + .writeAttribute( "filename", sourceInfo.file ) + .writeAttribute( "line", sourceInfo.line ); + } + public: // StreamingReporterBase virtual void noMatchingTestCases( std::string const& s ) CATCH_OVERRIDE { @@ -9167,7 +10374,9 @@ namespace Catch { virtual void testRunStarting( TestRunInfo const& testInfo ) CATCH_OVERRIDE { StreamingReporterBase::testRunStarting( testInfo ); - m_xml.setStream( stream ); + std::string stylesheetRef = getStylesheetRef(); + if( !stylesheetRef.empty() ) + m_xml.writeStylesheetRef( stylesheetRef ); m_xml.startElement( "Catch" ); if( !m_config->name().empty() ) m_xml.writeAttribute( "name", m_config->name() ); @@ -9181,10 +10390,16 @@ namespace Catch { virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { StreamingReporterBase::testCaseStarting(testInfo); - m_xml.startElement( "TestCase" ).writeAttribute( "name", trim( testInfo.name ) ); + m_xml.startElement( "TestCase" ) + .writeAttribute( "name", trim( testInfo.name ) ) + .writeAttribute( "description", testInfo.description ) + .writeAttribute( "tags", testInfo.tagsAsString ); + + writeSourceInfo( testInfo.lineInfo ); if ( m_config->showDurations() == ShowDurations::Always ) m_testCaseTimer.start(); + m_xml.ensureTagClosed(); } virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE { @@ -9193,77 +10408,84 @@ namespace Catch { m_xml.startElement( "Section" ) .writeAttribute( "name", trim( sectionInfo.name ) ) .writeAttribute( "description", sectionInfo.description ); + writeSourceInfo( sectionInfo.lineInfo ); + m_xml.ensureTagClosed(); } } virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE { } virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { - const AssertionResult& assertionResult = assertionStats.assertionResult; - // Print any info messages in tags. - if( assertionStats.assertionResult.getResultType() != ResultWas::Ok ) { + AssertionResult const& result = assertionStats.assertionResult; + + bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); + + if( includeResults || result.getResultType() == ResultWas::Warning ) { + // Print any info messages in tags. for( std::vector::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end(); - it != itEnd; - ++it ) { - if( it->type == ResultWas::Info ) { + it != itEnd; + ++it ) { + if( it->type == ResultWas::Info && includeResults ) { m_xml.scopedElement( "Info" ) - .writeText( it->message ); + .writeText( it->message ); } else if ( it->type == ResultWas::Warning ) { m_xml.scopedElement( "Warning" ) - .writeText( it->message ); + .writeText( it->message ); } } } // Drop out if result was successful but we're not printing them. - if( !m_config->includeSuccessfulResults() && isOk(assertionResult.getResultType()) ) + if( !includeResults && result.getResultType() != ResultWas::Warning ) return true; // Print the expression if there is one. - if( assertionResult.hasExpression() ) { + if( result.hasExpression() ) { m_xml.startElement( "Expression" ) - .writeAttribute( "success", assertionResult.succeeded() ) - .writeAttribute( "type", assertionResult.getTestMacroName() ) - .writeAttribute( "filename", assertionResult.getSourceInfo().file ) - .writeAttribute( "line", assertionResult.getSourceInfo().line ); + .writeAttribute( "success", result.succeeded() ) + .writeAttribute( "type", result.getTestMacroName() ); + + writeSourceInfo( result.getSourceInfo() ); m_xml.scopedElement( "Original" ) - .writeText( assertionResult.getExpression() ); + .writeText( result.getExpression() ); m_xml.scopedElement( "Expanded" ) - .writeText( assertionResult.getExpandedExpression() ); + .writeText( result.getExpandedExpression() ); } // And... Print a result applicable to each result type. - switch( assertionResult.getResultType() ) { + switch( result.getResultType() ) { case ResultWas::ThrewException: - m_xml.scopedElement( "Exception" ) - .writeAttribute( "filename", assertionResult.getSourceInfo().file ) - .writeAttribute( "line", assertionResult.getSourceInfo().line ) - .writeText( assertionResult.getMessage() ); + m_xml.startElement( "Exception" ); + writeSourceInfo( result.getSourceInfo() ); + m_xml.writeText( result.getMessage() ); + m_xml.endElement(); break; case ResultWas::FatalErrorCondition: - m_xml.scopedElement( "Fatal Error Condition" ) - .writeAttribute( "filename", assertionResult.getSourceInfo().file ) - .writeAttribute( "line", assertionResult.getSourceInfo().line ) - .writeText( assertionResult.getMessage() ); + m_xml.startElement( "FatalErrorCondition" ); + writeSourceInfo( result.getSourceInfo() ); + m_xml.writeText( result.getMessage() ); + m_xml.endElement(); break; case ResultWas::Info: m_xml.scopedElement( "Info" ) - .writeText( assertionResult.getMessage() ); + .writeText( result.getMessage() ); break; case ResultWas::Warning: // Warning will already have been written break; case ResultWas::ExplicitFailure: - m_xml.scopedElement( "Failure" ) - .writeText( assertionResult.getMessage() ); + m_xml.startElement( "Failure" ); + writeSourceInfo( result.getSourceInfo() ); + m_xml.writeText( result.getMessage() ); + m_xml.endElement(); break; default: break; } - if( assertionResult.hasExpression() ) + if( result.hasExpression() ) m_xml.endElement(); return true; @@ -9292,6 +10514,11 @@ namespace Catch { if ( m_config->showDurations() == ShowDurations::Always ) e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() ); + if( !testCaseStats.stdOut.empty() ) + m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), false ); + if( !testCaseStats.stdErr.empty() ) + m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), false ); + m_xml.endElement(); } @@ -9331,11 +10558,42 @@ namespace Catch { namespace Catch { + namespace { + std::string getCurrentTimestamp() { + // Beware, this is not reentrant because of backward compatibility issues + // Also, UTC only, again because of backward compatibility (%z is C++11) + time_t rawtime; + std::time(&rawtime); + const size_t timeStampSize = sizeof("2017-01-16T17:06:45Z"); + +#ifdef _MSC_VER + std::tm timeInfo = {}; + gmtime_s(&timeInfo, &rawtime); +#else + std::tm* timeInfo; + timeInfo = std::gmtime(&rawtime); +#endif + + char timeStamp[timeStampSize]; + const char * const fmt = "%Y-%m-%dT%H:%M:%SZ"; + +#ifdef _MSC_VER + std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); +#else + std::strftime(timeStamp, timeStampSize, fmt, timeInfo); +#endif + return std::string(timeStamp); + } + + } + class JunitReporter : public CumulativeReporterBase { public: JunitReporter( ReporterConfig const& _config ) : CumulativeReporterBase( _config ), - xml( _config.stream() ) + xml( _config.stream() ), + unexpectedExceptions( 0 ), + m_okToFail( false ) { m_reporterPrefs.shouldRedirectStdOut = true; } @@ -9361,8 +10619,11 @@ namespace Catch { CumulativeReporterBase::testGroupStarting( groupInfo ); } + virtual void testCaseStarting( TestCaseInfo const& testCaseInfo ) CATCH_OVERRIDE { + m_okToFail = testCaseInfo.okToFail(); + } virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { - if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException ) + if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException && !m_okToFail ) unexpectedExceptions++; return CumulativeReporterBase::assertionEnded( assertionStats ); } @@ -9395,7 +10656,7 @@ namespace Catch { xml.writeAttribute( "time", "" ); else xml.writeAttribute( "time", suiteTime ); - xml.writeAttribute( "timestamp", "tbd" ); // !TBD + xml.writeAttribute( "timestamp", getCurrentTimestamp() ); // Write test cases for( TestGroupNode::ChildNodes::const_iterator @@ -9416,32 +10677,32 @@ namespace Catch { assert( testCaseNode.children.size() == 1 ); SectionNode const& rootSection = *testCaseNode.children.front(); - std::string class_name = stats.testInfo.class_name; + std::string className = stats.testInfo.className; - if( class_name.empty() ) { + if( className.empty() ) { if( rootSection.childSections.empty() ) - class_name = "global"; + className = "global"; } - writeSection( class_name, "", rootSection ); + writeSection( className, "", rootSection ); } - void writeSection( std::string const& class_name, + void writeSection( std::string const& className, std::string const& rootName, SectionNode const& sectionNode ) { std::string name = trim( sectionNode.stats.sectionInfo.name ); if( !rootName.empty() ) - name = rootName + "/" + name; + name = rootName + '/' + name; if( !sectionNode.assertions.empty() || !sectionNode.stdOut.empty() || !sectionNode.stdErr.empty() ) { XmlWriter::ScopedElement e = xml.scopedElement( "testcase" ); - if( class_name.empty() ) { + if( className.empty() ) { xml.writeAttribute( "classname", name ); xml.writeAttribute( "name", "root" ); } else { - xml.writeAttribute( "classname", class_name ); + xml.writeAttribute( "classname", className ); xml.writeAttribute( "name", name ); } xml.writeAttribute( "time", Catch::toString( sectionNode.stats.durationInSeconds ) ); @@ -9458,10 +10719,10 @@ namespace Catch { itEnd = sectionNode.childSections.end(); it != itEnd; ++it ) - if( class_name.empty() ) + if( className.empty() ) writeSection( name, "", **it ); else - writeSection( class_name, name, **it ); + writeSection( className, name, **it ); } void writeAssertions( SectionNode const& sectionNode ) { @@ -9508,14 +10769,14 @@ namespace Catch { std::ostringstream oss; if( !result.getMessage().empty() ) - oss << result.getMessage() << "\n"; + oss << result.getMessage() << '\n'; for( std::vector::const_iterator it = stats.infoMessages.begin(), itEnd = stats.infoMessages.end(); it != itEnd; ++it ) if( it->type == ResultWas::Info ) - oss << it->message << "\n"; + oss << it->message << '\n'; oss << "at " << result.getSourceInfo(); xml.writeText( oss.str(), false ); @@ -9527,6 +10788,7 @@ namespace Catch { std::ostringstream stdOutForSuite; std::ostringstream stdErrForSuite; unsigned int unexpectedExceptions; + bool m_okToFail; }; INTERNAL_CATCH_REGISTER_REPORTER( "junit", JunitReporter ) @@ -9536,6 +10798,10 @@ namespace Catch { // #included from: ../reporters/catch_reporter_console.hpp #define TWOBLUECUBES_CATCH_REPORTER_CONSOLE_HPP_INCLUDED +#include +#include +#include + namespace Catch { struct ConsoleReporter : StreamingReporterBase { @@ -9550,7 +10816,7 @@ namespace Catch { } virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE { - stream << "No test cases matched '" << spec << "'" << std::endl; + stream << "No test cases matched '" << spec << '\'' << std::endl; } virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE { @@ -9559,18 +10825,15 @@ namespace Catch { virtual bool assertionEnded( AssertionStats const& _assertionStats ) CATCH_OVERRIDE { AssertionResult const& result = _assertionStats.assertionResult; - bool printInfoMessages = true; + bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); - // Drop out if result was successful and we're not printing those - if( !m_config->includeSuccessfulResults() && result.isOk() ) { - if( result.getResultType() != ResultWas::Warning ) - return false; - printInfoMessages = false; - } + // Drop out if result was successful but we're not printing them. + if( !includeResults && result.getResultType() != ResultWas::Warning ) + return false; lazyPrint(); - AssertionPrinter printer( stream, _assertionStats, printInfoMessages ); + AssertionPrinter printer( stream, _assertionStats, includeResults ); printer.print(); stream << std::endl; return true; @@ -9590,15 +10853,12 @@ namespace Catch { stream << "\nNo assertions in test case"; stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl; } + if( m_config->showDurations() == ShowDurations::Always ) { + stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl; + } if( m_headerPrinted ) { - if( m_config->showDurations() == ShowDurations::Always ) - stream << "Completed in " << _sectionStats.durationInSeconds << "s" << std::endl; m_headerPrinted = false; } - else { - if( m_config->showDurations() == ShowDurations::Always ) - stream << _sectionStats.sectionInfo.name << " completed in " << _sectionStats.durationInSeconds << "s" << std::endl; - } StreamingReporterBase::sectionEnded( _sectionStats ); } @@ -9611,7 +10871,7 @@ namespace Catch { printSummaryDivider(); stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n"; printTotals( _testGroupStats.totals ); - stream << "\n" << std::endl; + stream << '\n' << std::endl; } StreamingReporterBase::testGroupEnded( _testGroupStats ); } @@ -9663,7 +10923,11 @@ namespace Catch { case ResultWas::ThrewException: colour = Colour::Error; passOrFail = "FAILED"; - messageLabel = "due to unexpected exception with message"; + messageLabel = "due to unexpected exception with "; + if (_stats.infoMessages.size() == 1) + messageLabel += "message"; + if (_stats.infoMessages.size() > 1) + messageLabel += "messages"; break; case ResultWas::FatalErrorCondition: colour = Colour::Error; @@ -9703,13 +10967,13 @@ namespace Catch { printSourceInfo(); if( stats.totals.assertions.total() > 0 ) { if( result.isOk() ) - stream << "\n"; + stream << '\n'; printResultType(); printOriginalExpression(); printReconstructedExpression(); } else { - stream << "\n"; + stream << '\n'; } printMessage(); } @@ -9726,25 +10990,25 @@ namespace Catch { Colour colourGuard( Colour::OriginalExpression ); stream << " "; stream << result.getExpressionInMacro(); - stream << "\n"; + stream << '\n'; } } void printReconstructedExpression() const { if( result.hasExpandedExpression() ) { stream << "with expansion:\n"; Colour colourGuard( Colour::ReconstructedExpression ); - stream << Text( result.getExpandedExpression(), TextAttributes().setIndent(2) ) << "\n"; + stream << Text( result.getExpandedExpression(), TextAttributes().setIndent(2) ) << '\n'; } } void printMessage() const { if( !messageLabel.empty() ) - stream << messageLabel << ":" << "\n"; + stream << messageLabel << ':' << '\n'; for( std::vector::const_iterator it = messages.begin(), itEnd = messages.end(); it != itEnd; ++it ) { // If this assertion is a warning ignore any INFO messages if( printInfoMessages || it->type != ResultWas::Info ) - stream << Text( it->message, TextAttributes().setIndent(2) ) << "\n"; + stream << Text( it->message, TextAttributes().setIndent(2) ) << '\n'; } } void printSourceInfo() const { @@ -9776,10 +11040,10 @@ namespace Catch { } } void lazyPrintRunInfo() { - stream << "\n" << getLineOfChars<'~'>() << "\n"; + stream << '\n' << getLineOfChars<'~'>() << '\n'; Colour colour( Colour::SecondaryText ); stream << currentTestRunInfo->name - << " is a Catch v" << libraryVersion << " host application.\n" + << " is a Catch v" << libraryVersion() << " host application.\n" << "Run with -? for options\n\n"; if( m_config->rngSeed() != 0 ) @@ -9807,22 +11071,22 @@ namespace Catch { printHeaderString( it->name, 2 ); } - SourceLineInfo lineInfo = m_sectionStack.front().lineInfo; + SourceLineInfo lineInfo = m_sectionStack.back().lineInfo; if( !lineInfo.empty() ){ - stream << getLineOfChars<'-'>() << "\n"; + stream << getLineOfChars<'-'>() << '\n'; Colour colourGuard( Colour::FileName ); - stream << lineInfo << "\n"; + stream << lineInfo << '\n'; } - stream << getLineOfChars<'.'>() << "\n" << std::endl; + stream << getLineOfChars<'.'>() << '\n' << std::endl; } void printClosedHeader( std::string const& _name ) { printOpenHeader( _name ); - stream << getLineOfChars<'.'>() << "\n"; + stream << getLineOfChars<'.'>() << '\n'; } void printOpenHeader( std::string const& _name ) { - stream << getLineOfChars<'-'>() << "\n"; + stream << getLineOfChars<'-'>() << '\n'; { Colour colourGuard( Colour::Headers ); printHeaderString( _name ); @@ -9839,7 +11103,7 @@ namespace Catch { i = 0; stream << Text( _string, TextAttributes() .setIndent( indent+i) - .setInitialIndent( indent ) ) << "\n"; + .setInitialIndent( indent ) ) << '\n'; } struct SummaryColumn { @@ -9854,9 +11118,9 @@ namespace Catch { std::string row = oss.str(); for( std::vector::iterator it = rows.begin(); it != rows.end(); ++it ) { while( it->size() < row.size() ) - *it = " " + *it; + *it = ' ' + *it; while( it->size() > row.size() ) - row = " " + row; + row = ' ' + row; } rows.push_back( row ); return *this; @@ -9876,8 +11140,8 @@ namespace Catch { stream << Colour( Colour::ResultSuccess ) << "All tests passed"; stream << " (" << pluralise( totals.assertions.passed, "assertion" ) << " in " - << pluralise( totals.testCases.passed, "test case" ) << ")" - << "\n"; + << pluralise( totals.testCases.passed, "test case" ) << ')' + << '\n'; } else { @@ -9912,10 +11176,10 @@ namespace Catch { else if( value != "0" ) { stream << Colour( Colour::LightGrey ) << " | "; stream << Colour( it->colour ) - << value << " " << it->label; + << value << ' ' << it->label; } } - stream << "\n"; + stream << '\n'; } static std::size_t makeRatio( std::size_t number, std::size_t total ) { @@ -9951,10 +11215,10 @@ namespace Catch { else { stream << Colour( Colour::Warning ) << std::string( CATCH_CONFIG_CONSOLE_WIDTH-1, '=' ); } - stream << "\n"; + stream << '\n'; } void printSummaryDivider() { - stream << getLineOfChars<'-'>() << "\n"; + stream << getLineOfChars<'-'>() << '\n'; } private: @@ -9989,11 +11253,10 @@ namespace Catch { } virtual void noMatchingTestCases( std::string const& spec ) { - stream << "No test cases matched '" << spec << "'" << std::endl; + stream << "No test cases matched '" << spec << '\'' << std::endl; } - virtual void assertionStarting( AssertionInfo const& ) { - } + virtual void assertionStarting( AssertionInfo const& ) {} virtual bool assertionEnded( AssertionStats const& _assertionStats ) { AssertionResult const& result = _assertionStats.assertionResult; @@ -10014,9 +11277,15 @@ namespace Catch { return true; } + virtual void sectionEnded(SectionStats const& _sectionStats) CATCH_OVERRIDE { + if (m_config->showDurations() == ShowDurations::Always) { + stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl; + } + } + virtual void testRunEnded( TestRunStats const& _testRunStats ) { printTotals( _testRunStats.totals ); - stream << "\n" << std::endl; + stream << '\n' << std::endl; StreamingReporterBase::testRunEnded( _testRunStats ); } @@ -10116,26 +11385,26 @@ namespace Catch { void printSourceInfo() const { Colour colourGuard( Colour::FileName ); - stream << result.getSourceInfo() << ":"; + stream << result.getSourceInfo() << ':'; } - void printResultType( Colour::Code colour, std::string passOrFail ) const { + void printResultType( Colour::Code colour, std::string const& passOrFail ) const { if( !passOrFail.empty() ) { { Colour colourGuard( colour ); - stream << " " << passOrFail; + stream << ' ' << passOrFail; } - stream << ":"; + stream << ':'; } } - void printIssue( std::string issue ) const { - stream << " " << issue; + void printIssue( std::string const& issue ) const { + stream << ' ' << issue; } void printExpressionWas() { if( result.hasExpression() ) { - stream << ";"; + stream << ';'; { Colour colour( dimColour() ); stream << " expression was:"; @@ -10146,7 +11415,7 @@ namespace Catch { void printOriginalExpression() const { if( result.hasExpression() ) { - stream << " " << result.getExpression(); + stream << ' ' << result.getExpression(); } } @@ -10162,7 +11431,7 @@ namespace Catch { void printMessage() { if ( itMessage != messages.end() ) { - stream << " '" << itMessage->message << "'"; + stream << " '" << itMessage->message << '\''; ++itMessage; } } @@ -10177,13 +11446,13 @@ namespace Catch { { Colour colourGuard( colour ); - stream << " with " << pluralise( N, "message" ) << ":"; + stream << " with " << pluralise( N, "message" ) << ':'; } for(; itMessage != itEnd; ) { // If this assertion is a warning ignore any INFO messages if( printInfoMessages || itMessage->type != ResultWas::Info ) { - stream << " '" << itMessage->message << "'"; + stream << " '" << itMessage->message << '\''; if ( ++itMessage != itEnd ) { Colour colourGuard( dimColour() ); stream << " and"; @@ -10209,7 +11478,7 @@ namespace Catch { // - green: Passed [both/all] N tests cases with M assertions. std::string bothOrAll( std::size_t count ) const { - return count == 1 ? "" : count == 2 ? "both " : "all " ; + return count == 1 ? std::string() : count == 2 ? "both " : "all " ; } void printTotals( const Totals& totals ) const { @@ -10220,12 +11489,12 @@ namespace Catch { Colour colour( Colour::ResultError ); const std::string qualify_assertions_failed = totals.assertions.failed == totals.assertions.total() ? - bothOrAll( totals.assertions.failed ) : ""; + bothOrAll( totals.assertions.failed ) : std::string(); stream << "Failed " << bothOrAll( totals.testCases.failed ) << pluralise( totals.testCases.failed, "test case" ) << ", " "failed " << qualify_assertions_failed << - pluralise( totals.assertions.failed, "assertion" ) << "."; + pluralise( totals.assertions.failed, "assertion" ) << '.'; } else if( totals.assertions.total() == 0 ) { stream << @@ -10237,14 +11506,14 @@ namespace Catch { Colour colour( Colour::ResultError ); stream << "Failed " << pluralise( totals.testCases.failed, "test case" ) << ", " - "failed " << pluralise( totals.assertions.failed, "assertion" ) << "."; + "failed " << pluralise( totals.assertions.failed, "assertion" ) << '.'; } else { Colour colour( Colour::ResultSuccess ); stream << "Passed " << bothOrAll( totals.testCases.passed ) << pluralise( totals.testCases.passed, "test case" ) << - " with " << pluralise( totals.assertions.passed, "assertion" ) << "."; + " with " << pluralise( totals.assertions.passed, "assertion" ) << '.'; } } }; @@ -10300,11 +11569,7 @@ namespace Catch { TestSpec::NamePattern::~NamePattern() {} TestSpec::TagPattern::~TagPattern() {} TestSpec::ExcludedPattern::~ExcludedPattern() {} - - Matchers::Impl::StdString::Equals::~Equals() {} - Matchers::Impl::StdString::Contains::~Contains() {} - Matchers::Impl::StdString::StartsWith::~StartsWith() {} - Matchers::Impl::StdString::EndsWith::~EndsWith() {} + Matchers::Impl::MatcherUntypedBase::~MatcherUntypedBase() {} void Config::dummy() {} @@ -10328,9 +11593,16 @@ namespace Catch { #ifndef __OBJC__ +#if defined(WIN32) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN) +// Standard C/C++ Win32 Unicode wmain entry point +extern "C" int wmain (int argc, wchar_t * argv[], wchar_t * []) { +#else // Standard C/C++ main entry point int main (int argc, char * argv[]) { - return Catch::Session().run( argc, argv ); +#endif + + int result = Catch::Session().run( argc, argv ); + return ( result < 0xff ? result : 0xff ); } #else // __OBJC__ @@ -10348,7 +11620,7 @@ int main (int argc, char * const argv[]) { [pool drain]; #endif - return result; + return ( result < 0xff ? result : 0xff ); } #endif // __OBJC__ @@ -10364,50 +11636,62 @@ int main (int argc, char * const argv[]) { // If this config identifier is defined then all CATCH macros are prefixed with CATCH_ #ifdef CATCH_CONFIG_PREFIX_ALL -#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE" ) -#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "CATCH_REQUIRE_FALSE" ) +#if defined(CATCH_CONFIG_FAST_COMPILE) +#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, expr ) +#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr ) +#else +#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, expr ) +#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr ) +#endif -#define CATCH_REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "", "CATCH_REQUIRE_THROWS" ) -#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THROWS_AS" ) -#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, matcher, "CATCH_REQUIRE_THROWS_WITH" ) -#define CATCH_REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_NOTHROW" ) +#define CATCH_REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS", Catch::ResultDisposition::Normal, "", expr ) +#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr ) +#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr ) +#define CATCH_REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "CATCH_REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, expr ) -#define CATCH_CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK" ) -#define CATCH_CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, "CATCH_CHECK_FALSE" ) -#define CATCH_CHECKED_IF( expr ) INTERNAL_CATCH_IF( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_IF" ) -#define CATCH_CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_ELSE" ) -#define CATCH_CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CATCH_CHECK_NOFAIL" ) +#define CATCH_CHECK( expr ) INTERNAL_CATCH_TEST( "CATCH_CHECK", Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CATCH_CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( "CATCH_CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, expr ) +#define CATCH_CHECKED_IF( expr ) INTERNAL_CATCH_IF( "CATCH_CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CATCH_CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( "CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CATCH_CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( "CATCH_CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, expr ) -#define CATCH_CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS" ) -#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS_AS" ) -#define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, matcher, "CATCH_CHECK_THROWS_WITH" ) -#define CATCH_CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_NOTHROW" ) +#define CATCH_CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, "", expr ) +#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#define CATCH_CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "CATCH_CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, expr ) -#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THAT" ) -#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THAT" ) +#define CATCH_CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg ) + +#if defined(CATCH_CONFIG_FAST_COMPILE) +#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT_NO_TRY( "CATCH_REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) +#else +#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) +#endif -#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" ) -#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "CATCH_WARN", msg ) -#define CATCH_SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" ) -#define CATCH_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" ) -#define CATCH_SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" ) +#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg ) +#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( "CATCH_WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) +#define CATCH_SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg ) +#define CATCH_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CATCH_CAPTURE", #msg " := " << Catch::toString(msg) ) +#define CATCH_SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CATCH_CAPTURE", #msg " := " << Catch::toString(msg) ) #ifdef CATCH_CONFIG_VARIADIC_MACROS #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) - #define CATCH_TEST_CASE_METHOD( class_name, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( class_name, __VA_ARGS__ ) + #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) #define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) - #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", __VA_ARGS__ ) - #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", __VA_ARGS__ ) + #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ) + #define CATCH_FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) + #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) #else #define CATCH_TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description ) - #define CATCH_TEST_CASE_METHOD( class_name, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( class_name, name, description ) + #define CATCH_TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description ) #define CATCH_METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description ) #define CATCH_REGISTER_TEST_CASE( function, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( function, name, description ) #define CATCH_SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description ) - #define CATCH_FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", msg ) - #define CATCH_SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", msg ) + #define CATCH_FAIL( msg ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, msg ) + #define CATCH_FAIL_CHECK( msg ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, msg ) + #define CATCH_SUCCEED( msg ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, msg ) #endif #define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" ) @@ -10419,10 +11703,10 @@ int main (int argc, char * const argv[]) { // "BDD-style" convenience wrappers #ifdef CATCH_CONFIG_VARIADIC_MACROS #define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ ) -#define CATCH_SCENARIO_METHOD( class_name, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( class_name, "Scenario: " __VA_ARGS__ ) +#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) #else #define CATCH_SCENARIO( name, tags ) CATCH_TEST_CASE( "Scenario: " name, tags ) -#define CATCH_SCENARIO_METHOD( class_name, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( class_name, "Scenario: " name, tags ) +#define CATCH_SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags ) #endif #define CATCH_GIVEN( desc ) CATCH_SECTION( std::string( "Given: ") + desc, "" ) #define CATCH_WHEN( desc ) CATCH_SECTION( std::string( " When: ") + desc, "" ) @@ -10433,50 +11717,63 @@ int main (int argc, char * const argv[]) { // If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required #else -#define REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "REQUIRE" ) -#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "REQUIRE_FALSE" ) +#if defined(CATCH_CONFIG_FAST_COMPILE) +#define REQUIRE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "REQUIRE", Catch::ResultDisposition::Normal, expr ) +#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr ) + +#else +#define REQUIRE( expr ) INTERNAL_CATCH_TEST( "REQUIRE", Catch::ResultDisposition::Normal, expr ) +#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr ) +#endif + +#define REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS", Catch::ResultDisposition::Normal, "", expr ) +#define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr ) +#define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr ) +#define REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, expr ) -#define REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "", "REQUIRE_THROWS" ) -#define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "REQUIRE_THROWS_AS" ) -#define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, matcher, "REQUIRE_THROWS_WITH" ) -#define REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "REQUIRE_NOTHROW" ) +#define CHECK( expr ) INTERNAL_CATCH_TEST( "CHECK", Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( "CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, expr ) +#define CHECKED_IF( expr ) INTERNAL_CATCH_IF( "CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( "CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( "CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, expr ) -#define CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK" ) -#define CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, "CHECK_FALSE" ) -#define CHECKED_IF( expr ) INTERNAL_CATCH_IF( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_IF" ) -#define CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_ELSE" ) -#define CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CHECK_NOFAIL" ) +#define CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( "CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, "", expr ) +#define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#define CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, expr ) -#define CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "", "CHECK_THROWS" ) -#define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THROWS_AS" ) -#define CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, matcher, "CHECK_THROWS_WITH" ) -#define CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK_NOTHROW" ) +#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg ) -#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THAT" ) -#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "REQUIRE_THAT" ) +#if defined(CATCH_CONFIG_FAST_COMPILE) +#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT_NO_TRY( "REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) +#else +#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) +#endif -#define INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" ) -#define WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "WARN", msg ) -#define SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" ) -#define CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" ) -#define SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" ) +#define INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg ) +#define WARN( msg ) INTERNAL_CATCH_MSG( "WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) +#define SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg ) +#define CAPTURE( msg ) INTERNAL_CATCH_INFO( "CAPTURE", #msg " := " << Catch::toString(msg) ) +#define SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CAPTURE", #msg " := " << Catch::toString(msg) ) #ifdef CATCH_CONFIG_VARIADIC_MACROS - #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) - #define TEST_CASE_METHOD( class_name, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( class_name, __VA_ARGS__ ) - #define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) - #define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) - #define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) - #define FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", __VA_ARGS__ ) - #define SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", __VA_ARGS__ ) +#define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) +#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) +#define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) +#define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) +#define FAIL( ... ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ) +#define FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define SUCCEED( ... ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) #else - #define TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description ) - #define TEST_CASE_METHOD( class_name, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( class_name, name, description ) +#define TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description ) + #define TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description ) #define METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description ) #define REGISTER_TEST_CASE( method, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( method, name, description ) #define SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description ) - #define FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", msg ) - #define SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", msg ) + #define FAIL( msg ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, msg ) + #define FAIL_CHECK( msg ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, msg ) + #define SUCCEED( msg ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, msg ) #endif #define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" ) @@ -10492,10 +11789,10 @@ int main (int argc, char * const argv[]) { // "BDD-style" convenience wrappers #ifdef CATCH_CONFIG_VARIADIC_MACROS #define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ ) -#define SCENARIO_METHOD( class_name, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( class_name, "Scenario: " __VA_ARGS__ ) +#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) #else #define SCENARIO( name, tags ) TEST_CASE( "Scenario: " name, tags ) -#define SCENARIO_METHOD( class_name, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( class_name, "Scenario: " name, tags ) +#define SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags ) #endif #define GIVEN( desc ) SECTION( std::string(" Given: ") + desc, "" ) #define WHEN( desc ) SECTION( std::string(" When: ") + desc, "" ) @@ -10505,5 +11802,19 @@ int main (int argc, char * const argv[]) { using Catch::Detail::Approx; +// #included from: internal/catch_reenable_warnings.h + +#define TWOBLUECUBES_CATCH_REENABLE_WARNINGS_H_INCLUDED + +#ifdef __clang__ +# ifdef __ICC // icpc defines the __clang__ macro +# pragma warning(pop) +# else +# pragma clang diagnostic pop +# endif +#elif defined __GNUC__ +# pragma GCC diagnostic pop +#endif + #endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED diff --git a/tests/core/CMakeLists.txt b/tests/core/CMakeLists.txt index a68f0f21..220a3b62 100644 --- a/tests/core/CMakeLists.txt +++ b/tests/core/CMakeLists.txt @@ -5,35 +5,40 @@ set(TESTS_NAME codac-tests-core) list(APPEND SRC_TESTS ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_predefined_tubes.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_predefined_tubes.h - ${CMAKE_CURRENT_SOURCE_DIR}/tests_arithmetic.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_cn.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_box.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_cart_prod.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_delay.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_deriv.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_chain.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_eval.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_picard.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_lohner.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_static.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_cn.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_definition.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_functions.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_integration.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_operators.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_geometry.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_polygons.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_serialization.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_slices_structure.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_trajectory.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_values.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_sep_polygon.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_sep_qinterprojf.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_sep_fixpoint_proj.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_sep_polar.cpp - ) + + #${CMAKE_CURRENT_SOURCE_DIR}/tests_codac2_tubes.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_codac2_intervalvector.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_codac2_intervalmatrix.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_codac2_tubes_templated_types.cpp + + ${CMAKE_CURRENT_SOURCE_DIR}/tests_predefined_tubes.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_predefined_tubes.h + ${CMAKE_CURRENT_SOURCE_DIR}/tests_arithmetic.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_cn.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_box.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_cart_prod.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_delay.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_deriv.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_chain.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_eval.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_picard.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_lohner.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_static.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_definition.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_functions.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_integration.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_operators.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_geometry.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_polygons.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_serialization.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_slices_structure.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_trajectory.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_values.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_sep_polygon.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_sep_qinterprojf.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_sep_fixpoint_proj.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_sep_polar.cpp +) add_executable(${TESTS_NAME} ${SRC_TESTS}) # todo: find a clean way to access codac header files? diff --git a/tests/core/tests_arithmetic.cpp b/tests/core/tests_arithmetic.cpp index 79c742aa..e47d52e3 100644 --- a/tests/core/tests_arithmetic.cpp +++ b/tests/core/tests_arithmetic.cpp @@ -696,7 +696,7 @@ TEST_CASE("Arithmetic on trajs") Interval domain(0.,10.); TrajectoryVector trajx(3), trajy(3), trajz(3); - Vector vx(3), vy(3); + codac::Vector vx(3), vy(3); vx[0] = 1.; vx[1] = 2.; vx[2] = 3.; vy[0] = 10.; vy[1] = 20.; vy[2] = 30.; @@ -752,11 +752,11 @@ TEST_CASE("Arithmetic on trajs") //const Trajectory& operator+=(double x); trajz = trajx; trajz += vy[1]; - CHECK(ApproxIntvVector(trajz.codomain()) == IntervalVector(vx+Vector(3,vy[1]))); + CHECK(ApproxIntvVector(trajz.codomain()) == IntervalVector(vx+codac::Vector(3,vy[1]))); //const Trajectory& operator+=(const Trajectory& x); trajz = trajx; trajz += trajy[1]; - CHECK(ApproxIntvVector(trajz.codomain()) == IntervalVector(vx+Vector(3,vy[1]))); + CHECK(ApproxIntvVector(trajz.codomain()) == IntervalVector(vx+codac::Vector(3,vy[1]))); //const TrajectoryVector& operator+=(const Vector& x); trajz = trajx; trajz += vy; @@ -768,11 +768,11 @@ TEST_CASE("Arithmetic on trajs") //const Trajectory& operator-=(double x); trajz = trajx; trajz -= vy[1]; - CHECK(ApproxIntvVector(trajz.codomain()) == IntervalVector(vx-Vector(3,vy[1]))); + CHECK(ApproxIntvVector(trajz.codomain()) == IntervalVector(vx-codac::Vector(3,vy[1]))); //const Trajectory& operator-=(const Trajectory& x); trajz = trajx; trajz -= trajy[1]; - CHECK(ApproxIntvVector(trajz.codomain()) == IntervalVector(vx-Vector(3,vy[1]))); + CHECK(ApproxIntvVector(trajz.codomain()) == IntervalVector(vx-codac::Vector(3,vy[1]))); //const TrajectoryVector& operator-=(const Vector& x); trajz = trajx; trajz -= vy; diff --git a/tests/core/tests_cn.cpp b/tests/core/tests_cn.cpp index 81f37036..aabec427 100644 --- a/tests/core/tests_cn.cpp +++ b/tests/core/tests_cn.cpp @@ -206,7 +206,7 @@ TEST_CASE("CN simple") { Interval x(0,1), y(-2,3), a(1,20); IntervalVector vx(2,x), vy(2,y), va(2,a); - Vector vec(4,0.5); + codac::Vector vec(4,0.5); ContractorNetwork cn; cn.add(ctc_add, {vx,vy,va}); @@ -295,7 +295,7 @@ TEST_CASE("CN simple") { Interval x(0,1), a(1,20); - Vector vector_y(2, 1.); + codac::Vector vector_y(2, 1.); IntervalVector ivx(2,x), iva(2,a); ContractorNetwork cn; @@ -306,7 +306,7 @@ TEST_CASE("CN simple") cn.contract(); CHECK(ivx == IntervalVector(2,Interval(0,1))); - CHECK(vector_y == Vector(2,1.)); + CHECK(vector_y == codac::Vector(2,1.)); CHECK(iva == IntervalVector(2,Interval(1,2))); // todo: reactivate this test: CHECK(cn.nb_dom() == 3*3); CHECK(cn.nb_ctc() == 3+2); diff --git a/tests/core/tests_codac2_intervalmatrix.cpp b/tests/core/tests_codac2_intervalmatrix.cpp new file mode 100644 index 00000000..8b8640a2 --- /dev/null +++ b/tests/core/tests_codac2_intervalmatrix.cpp @@ -0,0 +1,483 @@ +#include "catch_interval.hpp" +#include "vibes.h" + +#include "codac2_Matrix.h" +#include "codac2_IntervalVector.h" +#include "codac2_IntervalMatrix.h" + +using namespace Catch; +using namespace Detail; +using namespace std; +using namespace codac2; + +// Most of these tests come from the IBEX library (G. Chabert) +// They have been revised to fit the codac2::IntervalMatrix class + +IntervalMatrix M1() +{ + IntervalMatrix m(2,3); + double _r1[][2]={{0,1},{0,2},{0,3}}; + double _r2[][2]={{-1,0},{-2,0},{-3,0}}; + IntervalVector r1(3,_r1); + IntervalVector r2(3,_r2); + m.row(0)=r1; + m.row(1)=r2; + return m; +} + + +IntervalMatrix M2() // the transpose of M1 +{ + IntervalMatrix m(3,2); + double _c1[][2]={{0,1},{-1,0}}; + double _c2[][2]={{0,2},{-2,0}}; + double _c3[][2]={{0,3},{-3,0}}; + IntervalVector c1(2,_c1); + IntervalVector c2(2,_c2); + IntervalVector c3(2,_c3); + m.row(0)=c1; + m.row(1)=c2; + m.row(2)=c3; + return m; +} + +IntervalMatrix M3() // non-null intersection with M1 +{ + IntervalMatrix m(2,3); + double _r1[][2]={{1,2},{1,2},{2,4}}; + double _r2[][2]={{-2,-1},{-2,-1},{-4,-2}}; + IntervalVector r1(3,_r1); + IntervalVector r2(3,_r2); + m.row(0)=r1; + m.row(1)=r2; + return m; +} + + +TEST_CASE("Tests from IBEX IntervalMatrix") +{ + SECTION("eq01") + { + IntervalMatrix m(2,3); + IntervalMatrix m2(3,2); + CHECK(m!=m2); + CHECK(!(m==m2)); + } + + SECTION("eq02") + { + IntervalMatrix m(3,2); + IntervalMatrix m2(2,2); + CHECK(m!=m2); + CHECK(!(m==m2)); + } + + SECTION("eq03") + { + IntervalMatrix m(2,3); + IntervalMatrix m2(2,3); + + CHECK(m.rows()==2); + CHECK(m.cols()==3); + CHECK(m2.rows()==2); + CHECK(m2.cols()==3); + + m(0,0)=1; + m(0,1)=2; + m(0,2)=3; + m(1,0)=4; + m(1,1)=5; + m(1,2)=6; + m2(0,0)=1; + m2(0,1)=2; + m2(0,2)=3; + m2(1,0)=4; + m2(1,1)=5; + m2(1,2)=6; + + CHECK(m==m2); + CHECK(!(m!=m2)); + + m2(1,2)=7; + CHECK(m!=m2); + CHECK(!(m==m2)); + } + + SECTION("eq04") + { + IntervalMatrix m(2,3); + IntervalMatrix m2(2,3); + m(1,1)=-1; + m2(1,1)=-2; + CHECK(m!=m2); + CHECK(!(m==m2)); + m.set_empty(); + m2.set_empty(); + CHECK(m==m2); + CHECK(!(m!=m2)); + } + + SECTION("cons01") + { + IntervalMatrix m(2,3); + CHECK(m.rows()==2); + CHECK(m.cols()==3); + CHECK(m(0,0)==Interval::all_reals()); + CHECK(m(0,1)==Interval::all_reals()); + CHECK(m(0,2)==Interval::all_reals()); + CHECK(m(1,0)==Interval::all_reals()); + CHECK(m(1,1)==Interval::all_reals()); + CHECK(m(1,2)==Interval::all_reals()); + CHECK(m==IntervalMatrix(m)); + CHECK(m==(IntervalMatrix(2,3)=m)); + } + + SECTION("cons02") + { + IntervalMatrix m(2,3); + double _r1[][2]={{0,1},{0,2},{0,3}}; + double _r2[][2]={{-1,0},{-2,0},{-3,0}}; + IntervalVector r1(3,_r1); + IntervalVector r2(3,_r2); + m.row(0) = r1; + m.row(1) = r2; + + double _c1[][2]={{0,1},{-1,0}}; + double _c2[][2]={{0,2},{-2,0}}; + double _c3[][2]={{0,3},{-3,0}}; + IntervalVector c1(2,_c1); + IntervalVector c2(2,_c2); + IntervalVector c3(2,_c3); + + CHECK(m.rows()==2); + CHECK(m.cols()==3); + // not supported CHECK(m[0]==r1); + // not supported CHECK(m[1]==r2); +// not working CHECK(m.row(0)==r1); +// not working CHECK(m.row(1)==r2); + CHECK(m.col(0)==c1); + CHECK(m.col(1)==c2); + CHECK(m.col(2)==c3); + CHECK(m(0,0)==Interval(0,1)); + CHECK(m(0,1)==Interval(0,2)); + CHECK(m(0,2)==Interval(0,3)); + CHECK(m(1,0)==Interval(-1,0)); + CHECK(m(1,1)==Interval(-2,0)); + CHECK(m(1,2)==Interval(-3,0)); + CHECK(m==IntervalMatrix(m)); + CHECK(m==(IntervalMatrix(2,3)=m)); + } + + SECTION("cons03") + { + Interval x(-1,2); + IntervalMatrix m(2,3,x); + + CHECK(m.rows()==2); + CHECK(m.cols()==3); + for (int i=0; i<2; i++) { + for (int j=0; j<3; j++) + CHECK(m(i,j)==x); + } + + CHECK(m==IntervalMatrix(m)); + CHECK(m==(IntervalMatrix(2,3)=m)); + } + + SECTION("cons04") + { + double _m[][2]={ {0,1}, {0,2}, {0,3}, + {-1,0},{-2,0},{-3,0} }; + IntervalMatrix m(2,3,_m); + CHECK(m==M1()); + } + + SECTION("consInitList") + { + IntervalMatrix m{ + {{0,1}, {0,2}, {0,3}}, + {{-1,0},{-2,0},{-3,0}} + }; + CHECK(m == M1()); + } + + SECTION("empty01") + { + CHECK(IntervalMatrix::empty_set(2,3).rows()==2); + CHECK(IntervalMatrix::empty_set(2,3).cols()==3); + CHECK(IntervalMatrix(IntervalMatrix::empty_set(2,3))==IntervalMatrix::empty_set(2,3)); + CHECK((IntervalMatrix(2,3)=IntervalMatrix::empty_set(2,3))==IntervalMatrix::empty_set(2,3)); + } + + SECTION("is_empty01") + { + CHECK(!IntervalMatrix(2,3).is_empty()); + } + + SECTION("is_empty02") + { + CHECK(IntervalMatrix::empty_set(2,3).is_empty()); + } + + SECTION("set_empty01") + { + IntervalMatrix m(2,3); + m.set_empty(); + CHECK(m.is_empty()); + } + + // intersection of a matrix with itself + SECTION("inter01") + { + CHECK((M1()&=M1())==M1()); + } + + // intersection of two overlapping matrices + SECTION("inter02") + { + double _m[][2]={{1,1}, {1,2}, {2,3}, + {-1,-1},{-2,-1},{-3,-2}}; + + CHECK((M1()&=M3())==IntervalMatrix(2,3,_m)); + } + + // intersection of two non-overlapping matrices + SECTION("inter03") + { + IntervalMatrix m3(M3()); + m3(1,2)=Interval(-5,-4); + CHECK((M1()&=m3).is_empty()); + } + + SECTION("set_col01") + { + IntervalMatrix m(M1()); + + IntervalVector v(2); + v[0]=Interval(1,2); + v[1]=Interval(-2,-1); + + m.col(1)=v; + + double _m2[][2]={ {0,1}, {1,2}, {0,3}, + {-1,0},{-2,-1},{-3,0} }; + IntervalMatrix m2(2,3,_m2); + + CHECK(m==m2); + } + + SECTION("rows01") + { + CHECK(M1().block(0,0,2,3)==M1()); + } + + SECTION("rows02") + { + double _r0[][2]={ {0,1}, {0,2}, {0,3} }; + CHECK(M1().block(0,0,1,3)==IntervalMatrix(1,3,_r0)); + } + + SECTION("rows03") + { + double _r1[][2]={ {-1,0},{-2,0},{-3,0} }; + CHECK(M1().block(1,0,1,3)==IntervalMatrix(1,3,_r1)); + } + + SECTION("cols01") + { + CHECK(M1().block(0,0,2,3)==M1()); + } + + SECTION("cols02") + { + double _c0[][2]={ {0,1}, {-1,0} }; + CHECK(M1().block(0,0,2,1)==IntervalMatrix(2,1,_c0)); + } + + SECTION("cols03") + { + double _c1[][2]={ {0,2}, {-2,0} }; + CHECK(M1().block(0,1,2,1)==IntervalMatrix(2,1,_c1)); + } + + SECTION("cols04") + { + double _c2[][2]={ {0,3}, {-3,0} }; + CHECK(M1().block(0,2,2,1)==IntervalMatrix(2,1,_c2)); + } + + SECTION("cols04") + { + double _c12[][2]={ {0,2}, {0,3}, {-2,0}, {-3,0} }; + CHECK(M1().block(0,1,2,2)==IntervalMatrix(2,2,_c12)); + } + + SECTION("resize01") + { + IntervalMatrix m(2,2); + double _r1[][2]={{0,1},{0,2}}; + double _r2[][2]={{-1,0},{-2,0}}; + IntervalVector r1(2,_r1); + IntervalVector r2(2,_r2); + m.row(0)=r1; + m.row(1)=r2; + m.resize(2,3); + m(0,2)=Interval(0,3); + m(1,2)=Interval(-3,0); + + CHECK(m==M1()); + } + + SECTION("resize02") + { + IntervalMatrix m(1,3); + double _r1[][2]={{0,1},{0,2},{0,3}}; + IntervalVector r1(3,_r1); + m.row(0)=r1; + m.resize(2,3); + m(1,0)=Interval(-1,0); + m(1,1)=Interval(-2,0); + m(1,2)=Interval(-3,0); + + CHECK(m==M1()); + } + + SECTION("resize03") + { + IntervalMatrix e(IntervalMatrix::empty_set(1,1)); + e.resize(2,3); + CHECK(e.is_empty()); + } + + SECTION("minus01") + { + IntervalMatrix m(M1()); + IntervalMatrix m2(-m); + for (int i=0; i<2; i++) { + for (int j=0; j<3; j++) { + CHECK(m2(i,j)==-m(i,j)); + } + } + } + + SECTION("minus02") + { + CHECK(-IntervalMatrix::empty_set(2,3).is_empty()); + } + + SECTION("add01") + { + IntervalMatrix m(M1()); + IntervalMatrix m2(m+m); + + for (int i=0; i<2; i++) { + for (int j=0; j<3; j++) { + CHECK(m2(i,j)==m(i,j)+m(i,j)); + } + } + + CHECK(m2==(IntervalMatrix(m)+=m)); + } + + SECTION("add02") + { + IntervalMatrix m1(IntervalMatrix::empty_set(2,3)); + IntervalMatrix m2(2,3); + + CHECK((m1+m2).is_empty()); + CHECK((m1+=m2).is_empty()); + CHECK((m2+=m1).is_empty()); + } + + SECTION("sub01") + { + IntervalMatrix m(M1()); + IntervalMatrix m2(m-m); + for (int i=0; i<2; i++) { + for (int j=0; j<3; j++) { + CHECK(m2(i,j)==m(i,j)-m(i,j)); + } + } + + CHECK(m2==(IntervalMatrix(m)-=m)); + } + + SECTION("sub02") + { + IntervalMatrix m1(IntervalMatrix::empty_set(2,3)); + IntervalMatrix m2(2,3); + + CHECK((m1-m2).is_empty()); + CHECK((m1-=m2).is_empty()); + CHECK((m2-=m1).is_empty()); + } + + SECTION("mul01") + { + IntervalMatrix m(M1()); + IntervalMatrix m2(M2()); + IntervalMatrix m3(m*m2); + CHECK(m3.rows()==2); + CHECK(m3.cols()==2); + + for (int i=0; i<2; i++) { + for (int j=0; j<2; j++) + CHECK(m3(i,j)==m(i,0)*m2(0,j)+m(i,1)*m2(1,j)+m(i,2)*m2(2,j)); + } + + CHECK(m3==(IntervalMatrix(m)*=m2)); + } + + SECTION("mul02") + { + IntervalMatrix m1(IntervalMatrix::empty_set(2,3)); + IntervalMatrix m2(3,2); + + CHECK(IntervalMatrix(m1*m2).is_empty()); + CHECK(IntervalMatrix(m1*=m2).is_empty()); + CHECK(IntervalMatrix(m2*=m1).is_empty()); + } + + SECTION("put01") + { + // deprecated in codac (use Eigen instead) IntervalMatrix M1=2*Matrix::eye(3); + // deprecated in codac (use Eigen instead) IntervalVector V1(3); + // deprecated in codac (use Eigen instead) V1[0]=3; V1[1]=4; V1[2]=5; + // deprecated in codac (use Eigen instead) IntervalMatrix res(4,4); + // deprecated in codac (use Eigen instead) res.put(0,0,M1); + // deprecated in codac (use Eigen instead) res.put(0,3,V1,false); + // deprecated in codac (use Eigen instead) res.put(3,0,Vector::ones(3),true); + // deprecated in codac (use Eigen instead) res[3][3]=6; + // deprecated in codac (use Eigen instead) double _expected[16] = { 2,0,0,3, + // deprecated in codac (use Eigen instead) 0,2,0,4, + // deprecated in codac (use Eigen instead) 0,0,2,5, + // deprecated in codac (use Eigen instead) 1,1,1,6 }; + // deprecated in codac (use Eigen instead) CHECK(res==(Matrix(4,4,_expected))); + } +} + +#if 0 + +// Tests from IBEX that are not (yet) considered in Codac: + +void TestIntervalMatrix::rad01() { + RNG::srand(1); + IntervalMatrix M=Matrix::rand(2); + Matrix R=M.rad(); + CHECK(R[0][0]==M[0][0].rad()); + CHECK(R[0][1]==M[0][1].rad()); + CHECK(R[1][0]==M[1][0].rad()); + CHECK(R[1][1]==M[1][1].rad()); +} + +void TestIntervalMatrix::diam01() { + RNG::srand(1); + IntervalMatrix M=Matrix::rand(2); + Matrix R=M.diam(); + CHECK(R[0][0]==M[0][0].diam()); + CHECK(R[0][1]==M[0][1].diam()); + CHECK(R[1][0]==M[1][0].diam()); + CHECK(R[1][1]==M[1][1].diam()); +} + +#endif \ No newline at end of file diff --git a/tests/core/tests_codac2_intervalvector.cpp b/tests/core/tests_codac2_intervalvector.cpp new file mode 100644 index 00000000..865738ab --- /dev/null +++ b/tests/core/tests_codac2_intervalvector.cpp @@ -0,0 +1,1128 @@ +#include "catch_interval.hpp" +#include "vibes.h" + +#include "codac2_Vector.h" +#include "codac2_IntervalVector.h" +#include "codac2_cart_prod.h" + +using namespace Catch; +using namespace Detail; +using namespace std; +using namespace codac2; + +// Most of these tests come from the IBEX library (G. Chabert) +// They have been revised to fit the codac2::IntervalVector class + +TEST_CASE("Tests from IBEX IntervalVector") +{ + SECTION("cons01") + { + IntervalVector x(2); + x[0]=Interval::all_reals(); + x[1]=Interval::all_reals(); + CHECK(x==IntervalVector(2)); + CHECK(x==IntervalVector(x)); + IntervalVector y(2); + y = x; + CHECK(x==y); + } + + SECTION("cons02") + { + IntervalVector x(2); + x[0]=Interval(0,1); + x[1]=Interval(0,1); + CHECK(x==IntervalVector(2,Interval(0,1))); + CHECK(x==IntervalVector(x)); + IntervalVector y(2); + y = x; + CHECK(x==y); + } + + SECTION("cons03") + { + IntervalVector x(2); + x[0]=Interval(0,1); + x[1]=Interval(2,3); + CHECK(x==IntervalVector(x)); + IntervalVector y(2); + y = x; + CHECK(x==y); + } + + SECTION("cons04") + { + double bounds[][2] = {{0,1},{2,3}}; + IntervalVector x(2); + x[0]=Interval(0,1); + x[1]=Interval(2,3); + CHECK(x==IntervalVector(2,bounds)); + IntervalVector y(2,bounds); + y = x; + CHECK(x==y); + } + + SECTION("cons05") + { + IntervalVector x(2); + x[0].set_empty(); + x[1].set_empty(); + CHECK(x==IntervalVector::empty_set(2)); + CHECK(x.is_empty()); + } + + SECTION("consInitList") + { + IntervalVector x{ + {1.0, 2.0}, + {2.0, 3.0}, + {4} + }; + CHECK(x.size() == 3); + CHECK(x[0] == Interval(1.0, 2.0)); + CHECK(x[1] == Interval(2.0, 3.0)); + CHECK(x[2] == Interval(4.0, 4.0)); + } + + SECTION("set_empty01") + { + IntervalVector x(2); + CHECK(!x.is_empty()); + x.set_empty(); + CHECK(x.is_empty()); + } + + SECTION("is_empty01") + { + CHECK(IntervalVector::empty_set(2).is_empty()); + } + + SECTION("is_empty02") + { + CHECK(!IntervalVector(2).is_empty()); + } + + SECTION("resize01") + { + IntervalVector x(1); + x[0]=Interval(1,2); + x.resize(3); + CHECK(x.size()==3); + CHECK(x[0]==Interval(1,2)); + CHECK(x[1]==Interval::all_reals()); + CHECK(x[2]==Interval::all_reals()); + } + + SECTION("resize02") + { + IntervalVector x(1); + x[0]=Interval(1,2); + x.resize(1); + CHECK(x.size()==1); + CHECK(x[0]==Interval(1,2)); + } + + SECTION("resize03") + { + IntervalVector x(2); + x[0]=Interval(1,2); + x.set_empty(); + x.resize(3); + CHECK(x.size()==3); + CHECK(x.is_empty()); + CHECK(x[2]==Interval::all_reals()); + } + + SECTION("resize04") + { + IntervalVector x(5); + x[0]=Interval(1,2); + x[1]=Interval(3,4); + x.resize(2); + CHECK(x.size()==2); + CHECK(x[0]==Interval(1,2)); + CHECK(x[1]==Interval(3,4)); + } + + static double _x[][2]={{0,1},{2,3},{4,5}}; + + SECTION("subvector01") + { + double _x01[][2]={{0,1},{2,3}}; + CHECK(IntervalVector(3,_x).subvector(0,1)==IntervalVector(2,_x01)); + } + + SECTION("subvector02") + { + double _x12[][2]={{2,3},{4,5}}; + CHECK(IntervalVector(3,_x).subvector(1,2)==IntervalVector(2,_x12)); + } + + SECTION("subvector03") + { + double _x11[][2]={{2,3}}; + CHECK(IntervalVector(3,_x).subvector(1,1)==IntervalVector(1,_x11)); + } + + SECTION("subvector04") + { + double _x22[][2]={{4,5}}; + CHECK(IntervalVector(3,_x).subvector(2,2)==IntervalVector(1,_x22)); + } + + SECTION("subvector05") + { + CHECK(IntervalVector(3,_x).subvector(0,2)==IntervalVector(3,_x)); + } + + SECTION("subvector06") + { + CHECK(IntervalVector::empty_set(3).subvector(1,2).is_empty()); + } + + SECTION("cart_prod01") + { + CHECK(codac2::cart_prod(IntervalVector(3,_x),IntervalVector::empty_set(3)).is_empty()); + CHECK(codac2::cart_prod(IntervalVector::empty_set(3),IntervalVector(3,_x)).is_empty()); + CHECK(codac2::cart_prod(Interval(1,2),Interval(2,3),IntervalVector({{5,6},{8,9}}),Interval(5,6)) == IntervalVector({{1,2},{2,3},{5,6},{8,9},{5,6}})); + } + + SECTION("inter01") + { + double _x1[][2]={{0,2},{4,6}}; + double _x2[][2]={{1,3},{5,7}}; + double _res[][2]={{1,2},{5,6}}; + CHECK(((IntervalVector(2,_x1)) &=IntervalVector(2,_x2))==IntervalVector(2,_res)); + CHECK(((IntervalVector(2,_x1)) & IntervalVector(2,_x2))==IntervalVector(2,_res)); + } + + SECTION("staticcartprod01") + { + IntervalVector_<2> x{{1,2},{3,5}}; + IntervalVector_<3> y{{1,2},{3,5},{-oo,oo}}; + Interval z{6,7}; + CHECK(cart_prod<6>(x,y,z)==IntervalVector({{1,2},{3,5},{1,2},{3,5},{-oo,oo},{6,7}})); + CHECK(cart_prod<2>(z,z)==IntervalVector({{6,7},{6,7}})); + } + + SECTION("inter02") + { + double _x1[][2]={{0,2},{4,6}}; + double _x2[][2]={{1,3},{7,8}}; + CHECK(((IntervalVector(2,_x1)) &=IntervalVector(2,_x2)).is_empty()); + CHECK(((IntervalVector(2,_x1)) & IntervalVector(2,_x2)).is_empty()); + } + + SECTION("inter03") + { + double _x1[][2]={{0,2},{4,6}}; + CHECK(((IntervalVector(2,_x1)) &=IntervalVector::empty_set(2)).is_empty()); + CHECK(((IntervalVector(2,_x1)) & IntervalVector::empty_set(2)).is_empty()); + } + + SECTION("inter04") + { + double _x1[][2]={{0,2},{4,6}}; + CHECK(((IntervalVector::empty_set(2)) &=IntervalVector(2,_x1)).is_empty()); + CHECK(((IntervalVector::empty_set(2)) & IntervalVector(2,_x1)).is_empty()); + } + + SECTION("hull01") + { + double _x1[][2]={{0,1},{4,5}}; + double _x2[][2]={{2,3},{6,7}}; + double _res[][2]={{0,3},{4,7}}; + CHECK(((IntervalVector(2,_x1)) |=IntervalVector(2,_x2))==IntervalVector(2,_res)); + CHECK(((IntervalVector(2,_x1)) | IntervalVector(2,_x2))==IntervalVector(2,_res)); + } + + SECTION("hull02") + { + double _x1[][2]={{0,1},{4,5}}; + IntervalVector x1(2,_x1); + CHECK((x1 |= x1)==x1); + CHECK((x1 | x1)==x1); + } + + SECTION("hull03") + { + double _x1[][2]={{0,2},{4,6}}; + IntervalVector x1(2,_x1); + CHECK((x1 |=IntervalVector::empty_set(2))==x1); + CHECK((x1 | IntervalVector::empty_set(2))==x1); + } + + SECTION("hull04") + { + double _x1[][2]={{0,2},{4,6}}; + IntervalVector x1(2,_x1); + CHECK(((IntervalVector::empty_set(2)) |= x1)==x1); + CHECK(((IntervalVector::empty_set(2)) | x1)==x1); + } + + SECTION("eq01") + { + IntervalVector x(3,_x); + CHECK(x==x); + CHECK(!(x!=x)); + } + + SECTION("eq02") + { + IntervalVector x(3,_x); + double _x01[][2]={{0,1},{2,3}}; + IntervalVector x1(2,_x01); + CHECK(!(x==x1)); + CHECK(x!=x1); + } + + SECTION("eq03") + { + double _x1[][2]={{0,1},{4,5}}; + double _x2[][2]={{2,3},{6,7}}; + IntervalVector x1(2,_x1); + IntervalVector x2(2,_x2); + x1.set_empty(); + x2.set_empty(); + CHECK(x1==x2); + CHECK(!(x1!=x2)); + } + + SECTION("eq04") + { + CHECK(IntervalVector::empty_set(2)==IntervalVector::empty_set(2)); + CHECK(IntervalVector::empty_set(2)!=IntervalVector::empty_set(3)); + IntervalVector x(2); + x.set_empty(); + CHECK(IntervalVector::empty_set(2)==x); + } + + SECTION("mid01") + { + IntervalVector x(3,_x); + codac2::Vector m=x.mid(); + CHECK(m[0]==0.5); + CHECK(m[1]==2.5); + CHECK(m[2]==4.5); + } + + SECTION("is_flat01") + { + CHECK(!IntervalVector(3,_x).is_flat()); + } + + SECTION("is_flat02") + { + CHECK(IntervalVector::empty_set(3).is_flat()); + } + + SECTION("is_flat03") + { + CHECK(IntervalVector(1,Interval(0,0)).is_flat()); + CHECK(!IntervalVector(1,Interval(0,1)).is_flat()); + } + + SECTION("is_flat04") + { + double _x1[][2]={{0,1},{2,2},{3,4}}; + CHECK(IntervalVector(3,_x1).is_flat()); + } + + SECTION("is_flat05") + { + double _x1[][2]={{0,1},{2,3},{4,4}}; + CHECK(IntervalVector(3,_x1).is_flat()); + } + + SECTION("is_unbounded01") + { + CHECK(!IntervalVector::empty_set(3).is_unbounded()); + } + + SECTION("is_unbounded02") + { + double _x1[][2]={{0,1},{0,2},{-oo,0}}; + CHECK(IntervalVector(3,_x1).is_unbounded()); + } + + SECTION("is_unbounded03") + { + double _x1[][2]={{0,1},{0,2}}; + CHECK(!IntervalVector(2,_x1).is_unbounded()); + } + + SECTION("is_unbounded04") + { + CHECK(IntervalVector(1).is_unbounded()); + } + + SECTION("is_subset01") + { + double _x1[][2]={{0,2},{2,4}}; + double _x2[][2]={{0,1},{3,4}}; + IntervalVector x1(2,_x1); + IntervalVector x2(2,_x2); + + CHECK(x1.is_superset(x2)); + CHECK(x2.is_subset(x1)); + CHECK(x1.is_strict_superset(x2)); + //CHECK(!x2.is_strict_interior_subset(x1)); + } + + SECTION("is_subset02") + { + double _x1[][2]={{0,2},{2,4}}; + double _x2[][2]={{1,1},{3,4}}; + IntervalVector x1(2,_x1); + IntervalVector x2(2,_x2); + + CHECK(x1.is_superset(x2)); + CHECK(x2.is_subset(x1)); + CHECK(x1.is_strict_superset(x2)); + //CHECK(!x2.is_strict_interior_subset(x1)); + } + + SECTION("is_subset03") + { + double _x1[][2]={{0,2},{2,4}}; + double _x2[][2]={{0,1},{3,3}}; + IntervalVector x1(2,_x1); + IntervalVector x2(2,_x2); + + CHECK(x1.is_superset(x2)); + CHECK(x2.is_subset(x1)); + CHECK(x1.is_strict_superset(x2)); + //CHECK(!x2.is_strict_interior_subset(x1)); + } + + SECTION("is_subset04") + { + double _x1[][2]={{0,2},{2,4}}; + double _x2[][2]={{1,1},{3,3}}; + IntervalVector x1(2,_x1); + IntervalVector x2(2,_x2); + + CHECK(x1.is_superset(x2)); + CHECK(x2.is_subset(x1)); + CHECK(x1.is_strict_superset(x2)); + //CHECK(x2.is_strict_interior_subset(x1)); + } + + SECTION("is_subset05") + { + double _x1[][2]={{0,2},{2,4}}; + IntervalVector x1(2,_x1); + IntervalVector x2(IntervalVector::empty_set(2)); + + CHECK(x1.is_superset(x2)); + CHECK(x2.is_subset(x1)); + CHECK(x1.is_strict_superset(x2)); + //CHECK(x2.is_strict_interior_subset(x1)); + } + + SECTION("is_subset06") + { + double _x2[][2]={{1,1},{3,3}}; + + IntervalVector x1(IntervalVector::empty_set(2)); + IntervalVector x2(2,_x2); + + CHECK(!x1.is_superset(x2)); + CHECK(!x2.is_subset(x1)); + CHECK(!x1.is_strict_superset(x2)); + //CHECK(!x2.is_strict_interior_subset(x1)); + } + + SECTION("is_subset07") + { + double _x1[][2]={{0,2},{2,4}}; + double _x2[][2]={{1,1},{3,5}}; + + IntervalVector x1(2,_x1); + IntervalVector x2(2,_x2); + + CHECK(!x1.is_superset(x2)); + CHECK(!x2.is_subset(x1)); + CHECK(!x1.is_strict_superset(x2)); + //CHECK(!x2.is_strict_interior_subset(x1)); + } + + SECTION("extr_diam_index01") + { + double _x1[][2]={{0,2},{0,1},{0,3}}; + IntervalVector x1(3,_x1); + CHECK(x1.extr_diam_index(true)==1); + CHECK(x1.extr_diam_index(false)==2); + CHECK(x1.min_diam()==1); + CHECK(x1.max_diam()==3); + } + + SECTION("extr_diam_index02") + { + double _x1[][2]={{0,1},{0,3},{0,2}}; + IntervalVector x1(3,_x1); + CHECK(x1.extr_diam_index(true)==0); + CHECK(x1.extr_diam_index(false)==1); + CHECK(x1.min_diam()==1); + CHECK(x1.max_diam()==3); + } + + SECTION("extr_diam_index03") + { + double _x1[][2]={{0,3},{0,2},{0,1}}; + IntervalVector x1(3,_x1); + CHECK(x1.extr_diam_index(true)==2); + CHECK(x1.extr_diam_index(false)==0); + CHECK(x1.min_diam()==1); + CHECK(x1.max_diam()==3); + } + + SECTION("extr_diam_index04") + { + double _x1[][2]={{0,1},{0,2},{-oo,0}}; + IntervalVector x1(3,_x1); + CHECK(x1.extr_diam_index(true)==0); + CHECK(x1.extr_diam_index(false)==2); + CHECK(x1.min_diam()==1); + CHECK(x1.max_diam()==oo); + } + + SECTION("extr_diam_index05") + { + double _x1[][2]={{-oo,0}}; + IntervalVector x1(1,_x1); + CHECK(x1.extr_diam_index(true)==0); + CHECK(x1.extr_diam_index(false)==0); + CHECK(x1.min_diam()==oo); + CHECK(x1.max_diam()==oo); + } + + SECTION("extr_diam_index06") + { + double _x1[][2]={{-oo,0},{0,1},{-oo,1},{1,3}}; + IntervalVector x1(4,_x1); + CHECK(x1.extr_diam_index(true)==1); + CHECK(x1.extr_diam_index(false)==2); + CHECK(x1.min_diam()==1); + CHECK(x1.max_diam()==oo); + } + + SECTION("extr_diam_index07") + { + double _x1[][2]={{-oo,0},{-2,oo},{-oo,1}}; + IntervalVector x1(3,_x1); + CHECK(x1.extr_diam_index(true)==0); + CHECK(x1.extr_diam_index(false)==1); + CHECK(x1.min_diam()==oo); + CHECK(x1.max_diam()==oo); + } + + SECTION("extr_diam_index08") + { + double _x1[][2]={{-oo,0},{-oo,1},{-2,oo}}; + IntervalVector x1(3,_x1); + CHECK(x1.extr_diam_index(true)==0); + CHECK(x1.extr_diam_index(false)==2); + CHECK(x1.min_diam()==oo); + CHECK(x1.max_diam()==oo); + } + + SECTION("extr_diam_index09") + { + double _x1[][2]={{-2,oo},{-oo,0},{-oo,1}}; + IntervalVector x1(3,_x1); + CHECK(x1.extr_diam_index(true)==1); + CHECK(x1.extr_diam_index(false)==0); + CHECK(x1.min_diam()==oo); + CHECK(x1.max_diam()==oo); + } + + SECTION("extr_diam_index10") + { + double _x1[][2]={{-2,oo},{-oo,1},{-oo,0}}; + IntervalVector x1(3,_x1); + CHECK(x1.extr_diam_index(true)==2); + CHECK(x1.extr_diam_index(false)==0); + CHECK(x1.min_diam()==oo); + CHECK(x1.max_diam()==oo); + } + + SECTION("volume01") + { + double _x1[][2]={{0,1},{0,oo}}; + CHECK(IntervalVector(2,_x1).volume()==oo); + } + + SECTION("volume02") + { + double _x1[][2]={{0,1},{1,1}}; + CHECK(IntervalVector(2,_x1).volume()==0); + } + + SECTION("volume03") + { + double _x1[][2]={{0,2},{2,5},{4,8}}; + CHECK(Approx(24.0)==IntervalVector(3,_x1).volume()); + } + + SECTION("minus01") + { + double _x1[][2]={{0,3},{0,2},{0,1}}; + double _x2[][2]={{-3,0},{-2,0},{-1,0}}; + CHECK((-IntervalVector(3,_x1))==IntervalVector(3,_x2)); + } + + SECTION("minus02") + { + double _x1[][2]={{0,1},{0,oo}}; + double _x2[][2]={{-1,0},{-oo,0}}; + CHECK(-IntervalVector(2,_x1)==IntervalVector(2,_x2)); + } + + SECTION("minus03") + { + CHECK(-IntervalVector::empty_set(2)==IntervalVector::empty_set(2)); + } + + SECTION("add01") + { + double _x1[][2]={{0,3},{0,2},{0,1}}; + double _x2[][2]={{0,1},{0,1},{0,1}}; + double _x3[][2]={{0,4},{0,3},{0,2}}; + + IntervalVector x1(3,_x1); + IntervalVector x2(3,_x2); + IntervalVector x3(3,_x3); + IntervalVector e(IntervalVector::empty_set(3)); + + CHECK(x1+x2==x3); + CHECK((x1+e)==e); + CHECK((x1+e).is_empty()); + CHECK((IntervalVector(x1)+=e)==e); + CHECK((IntervalVector(x1)+=e).is_empty()); + + CHECK((e+x1)==e); + CHECK((e+x1).is_empty()); + CHECK((e+=x1)==e); + CHECK((e+=x1).is_empty()); + CHECK((e+e)==e); + CHECK((e+e).is_empty()); + CHECK((e+=e)==e); + CHECK((e+=e).is_empty()); + + CHECK((IntervalVector(x1)+=x2)==x3); + CHECK((IntervalVector(x1)+=e)==e); + CHECK((IntervalVector(x1)+=e).is_empty()); + + CHECK((IntervalVector(x2)+=x1)==x3); + } + + SECTION("sub01") + { + double _x1[][2]={{0,3},{0,2},{0,1}}; + double _x2[][2]={{0,1},{0,1},{0,1}}; + double _x3[][2]={{-1,3},{-1,2},{-1,1}}; + IntervalVector x1(3,_x1); + IntervalVector x2(3,_x2); + IntervalVector x3(3,_x3); + IntervalVector e(IntervalVector::empty_set(3)); + + CHECK(x1-x2==x3); + CHECK(x2-x1==-x3); + CHECK((x1-e)==e); + CHECK((x1-e).is_empty()); + CHECK((IntervalVector(x1)-=e)==e); + CHECK((IntervalVector(x1)-=e).is_empty()); + + CHECK((e-x1)==e); + CHECK((e-x1).is_empty()); + CHECK((e-=x1)==e); + CHECK((e-=x1).is_empty()); + CHECK((e-e)==e); + CHECK((e-e).is_empty()); + CHECK((e-=e)==e); + CHECK((e-=e).is_empty()); + + CHECK((IntervalVector(x1)-=x2)==x3); + CHECK((IntervalVector(x2)-=x1)==-x3); + } +} + +bool test_diff(const IntervalVector& x, const IntervalVector& y, const std::list& expected, bool compactness = true) +{ + auto c = x.diff(y, compactness); + + CHECK(!c.empty()); + CHECK(c.size()==expected.size()); + CHECK(c.front().size()==x.size()); + + auto it = c.begin(); + while(it != c.end()) + { + bool is_same = false; + for(const auto& ri : expected) + if(ri == *it) + { + is_same = true; + break; + } + + if(is_same) + it = c.erase(it); + else + it++; + } + + CHECK(c.empty()); // all complementary boxes have been checked + return c.empty(); +} + +TEST_CASE("Tests from IBEX IntervalVector::diff") +{ + SECTION("compl01") + { + double _b[][2]={{0,1},{0,1}}; + IntervalVector b(2,_b); + auto c = b.complementary(); + + CHECK(c.size()==4); + CHECK(c.front().size()==2); + + for(size_t i = 0 ; i < 4 ; i++) + { + if(c.front() == IntervalVector({Interval::neg_reals(),Interval::all_reals()}) + || c.front() == IntervalVector({Interval(1,oo),Interval::all_reals()}) + || c.front() == IntervalVector({Interval(0,1),Interval::neg_reals()}) + || c.front() == IntervalVector({Interval(0,1),Interval(1,oo)})) + c.pop_front(); + } + + CHECK(c.empty()); // all complementary boxes have been checked + } + + SECTION("compl02") + { + // complementary of an empty box = (-oo,oo)x...(-oo,oo) + auto c = IntervalVector::empty_set(2).complementary(); + CHECK(c.size()==1); + CHECK(c.front().size()==2); + CHECK(c.front()[0]==Interval::all_reals()); + CHECK(c.front()[1]==Interval::all_reals()); + } + + SECTION("diff01") + { + CHECK(test_diff({{-2,2},{-2,2},{-2,2}}, IntervalVector::empty_set(3), { {{-2,2},{-2,2},{-2,2}} })); + } + + SECTION("diff02") + { + CHECK(test_diff(IntervalVector::empty_set(3), {{-2,2},{-2,2},{-2,2}}, { IntervalVector::empty_set(3) })); + } + + SECTION("diff03") + { + CHECK(test_diff({{-2,2},{-2,2},{-2,2}}, {{-1,1},{3,4},{-1,1}}, { {{-2,2},{-2,2},{-2,2}} })); + } + + SECTION("diff04") + { + CHECK(test_diff({{-2,2},{-2,2},{-2,2}}, {{-1,1},{-1,-1},{-1,1}}, { {{-2,2},{-2,2},{-2,2}} })); + } + + SECTION("diff05") + { + CHECK(test_diff({{-2,2},{-2,2},{-2,2}}, {{-1,1},{-1,1},{-1,1}}, { + {{-2,-1},{-2,2},{-2,2}}, + {{1,2},{-2,2},{-2,2}}, + {{-1,1},{-2,-1},{-2,2}}, + {{-1,1},{1,2},{-2,2}}, + {{-1,1},{-1,1},{-2,-1}}, + {{-1,1},{-1,1},{1,2}} + })); + } + + SECTION("diff06") + { + CHECK(test_diff({{-2,2},{-2,2},{-2,2}}, {{-1,1},{-1,1},{-2,1}}, { + {{-2,-1},{-2,2},{-2,2}}, + {{1,2},{-2,2},{-2,2}}, + {{-1,1},{-2,-1},{-2,2}}, + {{-1,1},{1,2},{-2,2}}, + {{-1,1},{-1,1},{1,2}} + })); + } + + SECTION("diff07") + { + CHECK(test_diff({{-2,2},{-2,2},{-2,2}}, {{-1,1},{-1,1},{-2,2}}, { + {{-2,-1},{-2,2},{-2,2}}, + {{1,2},{-2,2},{-2,2}}, + {{-1,1},{-2,-1},{-2,2}}, + {{-1,1},{1,2},{-2,2}} + })); + } + + SECTION("diff08") + { + CHECK(test_diff({{-2,2},{-2,2},{-2,2}}, {{-1,1},{-2,1},{-1,1}}, { + {{-2,-1},{-2,2},{-2,2}}, + {{1,2},{-2,2},{-2,2}}, + {{-1,1},{1,2},{-2,2}}, + {{-1,1},{-2,1},{-2,-1}}, + {{-1,1},{-2,1},{1,2}} + })); + } + + SECTION("diff09") + { + CHECK(test_diff({{-2,2},{-2,2},{-2,2}}, {{-1,1},{-2,2},{-1,1}}, { + {{-2,-1},{-2,2},{-2,2}}, + {{1,2},{-2,2},{-2,2}}, + {{-1,1},{-2,2},{-2,-1}}, + {{-1,1},{-2,2},{1,2}} + })); + } + + SECTION("diff10") + { + CHECK(test_diff({{-2,2},{-2,2},{-2,2}}, {{-2,1},{-1,1},{-1,1}}, { + {{1,2},{-2,2},{-2,2}}, + {{-2,1},{-2,-1},{-2,2}}, + {{-2,1},{1,2},{-2,2}}, + {{-2,1},{-1,1},{-2,-1}}, + {{-2,1},{-1,1},{1,2}} + })); + } + + SECTION("diff11") + { + CHECK(test_diff({{-2,2},{-2,2},{-2,2}}, {{-2,2},{-1,2},{-1,1}}, { + {{-2,2},{-2,-1},{-2,2}}, + {{-2,2},{-1,2},{-2,-1}}, + {{-2,2},{-1,2},{1,2}} + })); + + } + + SECTION("diff12") + { + CHECK(test_diff({{-2,2},{-2,2},{-2,2}}, {{-1,1},{-2,1},{-2,1}}, { + {{-2,-1},{-2,2},{-2,2}}, + {{1,2},{-2,2},{-2,2}}, + {{-1,1},{1,2},{-2,2}}, + {{-1,1},{-2,1},{1,2}} + })); + } + + SECTION("diff13") + { + CHECK(test_diff({{-2,2},{-2,2},{-2,2}}, {{-1,1},{-2,1},{-2,2}}, { + {{-2,-1},{-2,2},{-2,2}}, + {{1,2},{-2,2},{-2,2}}, + {{-1,1},{1,2},{-2,2}} + })); + } + + SECTION("diff14") + { + CHECK(test_diff({{-2,2},{-2,2},{-2,2}}, {{-2,1},{-1,1},{-2,1}}, { + {{1,2},{-2,2},{-2,2}}, + {{-2,1},{-2,-1},{-2,2}}, + {{-2,1},{1,2},{-2,2}}, + {{-2,1},{-1,1},{1,2}} + })); + } + + SECTION("diff15") + { + CHECK(test_diff({{-2,2},{-2,2},{-2,2}}, {{-2,1},{-1,1},{-2,2}}, { + {{1,2},{-2,2},{-2,2}}, + {{-2,1},{-2,-1},{-2,2}}, + {{-2,1},{1,2},{-2,2}} + })); + } + + SECTION("diff16") + { + CHECK(test_diff({{-2,2},{-2,2},{-2,2}}, {{-2,1},{-2,1},{-1,1}}, { + {{1,2},{-2,2},{-2,2}}, + {{-2,1},{1,2},{-2,2}}, + {{-2,1},{-2,1},{-2,-1}}, + {{-2,1},{-2,1},{1,2}} + })); + } + + SECTION("diff17") + { + CHECK(test_diff({{-2,2},{-2,2},{-2,2}}, {{-2,1},{-2,2},{-1,1}}, { + {{1,2},{-2,2},{-2,2}}, + {{-2,1},{-2,2},{-2,-1}}, + {{-2,1},{-2,2},{1,2}} + })); + } + + SECTION("diff18") + { + CHECK(test_diff({{-2,2},{-2,2},{-2,2}}, {{-2,1},{-2,1},{-2,1}}, { + {{1,2},{-2,2},{-2,2}}, + {{-2,1},{1,2},{-2,2}}, + {{-2,1},{-2,1},{1,2}} + })); + } + + SECTION("diff19") + { + CHECK(test_diff({{-2,2},{-2,2},{-2,2}}, {{-2,1},{-2,1},{-2,2}}, { + {{1,2},{-2,2},{-2,2}}, + {{-2,1},{1,2},{-2,2}} + })); + } + + SECTION("diff20") + { + CHECK(test_diff({{-2,2},{-2,2},{-2,2}}, {{-2,1},{-2,2},{-2,1}}, { + {{1,2},{-2,2},{-2,2}}, + {{-2,1},{-2,2},{1,2}} + })); + } + + SECTION("diff21") + { + CHECK(test_diff({{-2,2},{-2,2},{-2,2}}, {{-2,2},{-2,1},{-2,1}}, { + {{-2,2},{1,2},{-2,2}}, + {{-2,2},{-2,1},{1,2}} + })); + } + + SECTION("diff22") + { + CHECK(test_diff({{-2,2},{-2,2},{-2,2}}, {{-2,1},{-2,2},{-2,2}}, { + {{1,2},{-2,2},{-2,2}} + })); + } + + SECTION("diff23") + { + CHECK(test_diff({{-2,2},{-2,2},{-2,2}}, {{-2,2},{-2,2},{-2,1}}, { + {{-2,2},{-2,2},{1,2}} + })); + } + + SECTION("diff24") + { + CHECK(test_diff({{-2,2},{-2,2},{-2,2}}, {{-2,2},{-2,1},{-2,2}}, { + {{-2,2},{1,2},{-2,2}} + })); + } + + SECTION("diff25") + { + CHECK(test_diff({{-2,2},{-2,2},{-2,2}}, {{-2,2},{-2,2},{-2,2}}, { + IntervalVector::empty_set(3) + })); + } + + SECTION("diff30") + { + CHECK(test_diff({{0,0},{-2,2},{-2,2}}, {{0,0},{-1,1},{-1,1}}, { + {{0,0},{-2,-1},{-2,2}}, + {{0,0},{1,2},{-2,2}}, + {{0,0},{-1,1},{-2,-1}}, + {{0,0},{-1,1},{1,2}} + })); + } + + SECTION("diff31") + { + CHECK(test_diff({{0,0},{0,0},{-2,2}}, {{0,0},{0,0},{-1,1}}, { + {{0,0},{0,0},{-2,-1}}, + {{0,0},{0,0},{1,2}} + })); + } + + SECTION("diff32") + { + CHECK(test_diff({{0,0},{-2,2},{0,0}}, {{0,0},{-1,1},{0,0}}, { + {{0,0},{-2,-1},{0,0}}, + {{0,0},{1,2},{0,0}} + })); + } + + SECTION("diff33") + { + CHECK(test_diff({{-2,2},{0,0},{0,0}}, {{-1,1},{0,0},{0,0}}, { + {{-2,-1},{0,0},{0,0}}, + {{1,2},{0,0},{0,0}} + })); + } + + SECTION("diff34") + { + CHECK(test_diff({{0,0},{-2,2},{-2,2}}, {{-1,1},{-1,1},{-1,1}}, { + {{0,0},{-2,-1},{-2,2}}, + {{0,0},{1,2},{-2,2}}, + {{0,0},{-1,1},{-2,-1}}, + {{0,0},{-1,1},{1,2}} + })); + } + + SECTION("diff35") + { + CHECK(test_diff({{-2,2},{-2,2},{-2,2}}, {{2,4},{-2,2},{-2,2}}, { + {{-2,2},{-2,2},{-2,2}} + })); + } + + SECTION("diff36") + { + CHECK(test_diff({{-2,2},{-2,2},{-2,2}}, {{2,4},{-1,1},{-1,1}}, { + {{-2,2},{-2,2},{-2,2}} + })); + } + + SECTION("diff37") + { + CHECK(test_diff({{-2,2},{-2,2}}, {{-2,2},{1,1}}, { + {{-2,2},{-2,1}}, + {{-2,2},{1,2}} + }, false)); + } + + SECTION("diff38") + { + CHECK(test_diff({{-2,2},{1,1}}, {{0,2},{-2,2}}, { + {{-2,0},{1,1}} + }, false)); + } + + SECTION("issue228") + { + CHECK(test_diff({{-1,-1},{-1,1},{-1,1}}, {{0,2},{0,2},{0,2}}, { + {{-1,-1},{-1,1},{-1,1}} + })); + } +} + + +#if 0 + +// Tests from IBEX that are not (yet) considered in Codac: + +void TestIntervalVector::is_relative_interior01() { + double _x1[][2]={{1,1},{3,4}}; + double _x2[][2]={{1,1},{2,5}}; + + IntervalVector x1(2,_x1); + IntervalVector x2(2,_x2); + + CHECK(x1.is_relative_interior_subset(x2)); +} + +void TestIntervalVector::is_relative_interior02() { + double _x1[][2]={{3,4},{3,3}}; + double _x2[][2]={{2,5},{3,3}}; + + IntervalVector x1(2,_x1); + IntervalVector x2(2,_x2); + + CHECK(x1.is_relative_interior_subset(x2)); +} + +void TestIntervalVector::is_relative_interior03() { + double _x1[][2]={{0,0},{1,1}}; + double _x2[][2]={{1,1},{1,1}}; + + IntervalVector x1(2,_x1); + IntervalVector x2(2,_x2); + + CHECK(!x1.is_relative_interior_subset(x2)); +} + +void TestIntervalVector::is_relative_interior04() { + double _x1[][2]={{0,0},{1,1}}; + double _x2[][2]={{0,0},{1,1}}; + + IntervalVector x1(2,_x1); + IntervalVector x2(2,_x2); + + CHECK(x1.is_relative_interior_subset(x2)); +} + +void TestIntervalVector::is_relative_interior05() { + CHECK(IntervalVector::empty_set(2).is_relative_interior_subset(IntervalVector(2))); +} + +void TestIntervalVector::sort_indices01() { + double _x[][2]={{0,2},{-oo,0},{0,1},{3,3},{-10,10}}; + IntervalVector x(5,_x); + int tab[5]; + x.sort_indices(true,tab); + CHECK(tab[0]==3); + CHECK(tab[1]==2); + CHECK(tab[2]==0); + CHECK(tab[3]==4); + CHECK(tab[4]==1); +} + +void TestIntervalVector::sort_indices02() { + double _x[][2]={{0,2},{-oo,0},{0,1},{3,3},{-10,10}}; + IntervalVector x(5,_x); + int tab[5]; + x.sort_indices(false,tab); + CHECK(tab[0]==1); + CHECK(tab[1]==4); + CHECK(tab[2]==0); + CHECK(tab[3]==2); + CHECK(tab[4]==3); +} + +void TestIntervalVector::rel_distance01() { + IntervalVector box1(3); + IntervalVector box2(3); + box1[0]=Interval(0,0); + box2[0]=Interval(0,0); + box1[1]=Interval(-1,0); + box2[1]=Interval(-1,0); + box1[2]=Interval(1,4); + box2[2]=Interval(1.5,3); + CHECK_DOUBLES_EQUAL(1.0/3.0,box1.rel_distance(box2),ERROR); +} + +void TestIntervalVector::perimeter01() { + IntervalVector box1(3); + IntervalVector box2(3); + box1[0]=Interval(0,0); + box2[0]=Interval(0,0); + box1[1]=Interval(-1,0); + box2[1]=Interval(-1,0); + box1[2]=Interval(1,4); + box2[2]=Interval(1.5,3); + CHECK_DOUBLES_EQUAL(4.0,box1.perimeter(),ERROR); + CHECK_DOUBLES_EQUAL(2.5,box2.perimeter(),ERROR); +} + +void TestIntervalVector::perimeter02() { + double _x1[][2]={{0,1},{0,oo}}; + CHECK(IntervalVector(2,_x1).perimeter()==oo); +} + +void TestIntervalVector::random01() { + double _b[][2]={{0,1},{0,1}}; + IntervalVector b(2,_b); + IntervalVector r=b.random(); + + CHECK(r.size()==2); + CHECK(r.max_diam()==0); + CHECK(b.is_superset(r)); +} + +void TestIntervalVector::random02() { + double _b[][2]={{1,1},{2,2}}; + IntervalVector b(2,_b); + IntervalVector r=b.random(); + + CHECK(b==r); +} + +#endif \ No newline at end of file diff --git a/tests/core/tests_codac2_tubes.cpp b/tests/core/tests_codac2_tubes.cpp new file mode 100644 index 00000000..18bc6659 --- /dev/null +++ b/tests/core/tests_codac2_tubes.cpp @@ -0,0 +1,609 @@ +#include "catch_interval.hpp" +#include "vibes.h" + +// Using #define so that we can access protected methods +// of the class for tests purposes +#define protected public +#include "codac2_TDomain.h" + +#include "codac_predef_values.h" +#include "codac2_Tube.h" +#include "codac2_CtcDiffInclusion.h" + +using namespace Catch; +using namespace Detail; +using namespace std; +using namespace ibex; +using namespace codac2; +using codac::oo; + +Tube return_a_tube() +{ + return Tube( + create_tdomain(Interval(0,2),0.5), + IntervalVector(3,Interval(-1.5,1))); +} + +TEST_CASE("Test codac2::tubes") +{ + SECTION("Test TDomain") + { + auto tdomain = create_tdomain(); + tdomain->sample(Interval(0,1), 0.5); + CHECK(tdomain->nb_tslices() == 4); + CHECK(tdomain->t0_tf() == Interval(-oo,oo)); + + const list& tslices = tdomain->tslices(); + vector vector_tslices{ + make_move_iterator(tslices.begin()), + make_move_iterator(tslices.end()) }; + + CHECK(vector_tslices.size() == 4); + CHECK(vector_tslices[0].t0_tf() == Interval(-oo,0)); + CHECK(vector_tslices[1].t0_tf() == Interval(0,0.5)); + CHECK(vector_tslices[2].t0_tf() == Interval(0.5,1)); + CHECK(vector_tslices[3].t0_tf() == Interval(1,oo)); + + CHECK(tdomain->iterator_tslice(-10.)->t0_tf() == Interval(-oo,0)); + CHECK(tdomain->iterator_tslice(-120.)->t0_tf() == Interval(-oo,0)); + CHECK(tdomain->iterator_tslice(0.2)->t0_tf() == Interval(0,0.5)); + CHECK(tdomain->iterator_tslice(5540.2)->t0_tf() == Interval(1,oo)); + + CHECK(tdomain->nb_tubes() == 0); + Tube x(tdomain, IntervalVector(2)); // adding a tubevector to the tdomain + CHECK(tdomain->nb_tubes() == 1); + + { // new scope + Tube v(tdomain, IntervalVector(3)); + CHECK(tdomain->nb_tubes() == 2); + } // end of scope, auto removing the tube + + CHECK(tdomain->nb_tubes() == 1); + } + + // should assert SECTION("Test degenerated TDomain") + // should assert { + // should assert auto tdomain = create_tdomain(Interval(1), 0.5); + // should assert CHECK(tdomain->nb_tslices() == 2); + // should assert CHECK(tdomain->t0_tf() == Interval(1)); + // should assert CHECK(tdomain->nb_tubes() == 0); + // should assert + // should assert const list& tslices = tdomain->tslices(); + // should assert vector vector_tslices{ + // should assert make_move_iterator(tslices.begin()), + // should assert make_move_iterator(tslices.end()) }; + // should assert + // should assert CHECK(vector_tslices.size() == 2); + // should assert CHECK(vector_tslices[0].t0_tf() == Interval(-oo,1)); + // should assert CHECK(vector_tslices[1].t0_tf() == Interval(1,oo)); + // should assert } + + SECTION("Test TDomain with gates") + { + auto tdomain = create_tdomain(Interval(0,1), 0.5, true); + CHECK(tdomain->nb_tslices() == 5); + CHECK(tdomain->t0_tf() == Interval(0,1)); + CHECK(tdomain->nb_tubes() == 0); + + const list& tslices = tdomain->tslices(); + vector vector_tslices{ + make_move_iterator(tslices.begin()), + make_move_iterator(tslices.end()) }; + + CHECK(vector_tslices.size() == 5); + CHECK(vector_tslices[0].t0_tf() == Interval(0)); + CHECK(vector_tslices[1].t0_tf() == Interval(0,0.5)); + CHECK(vector_tslices[2].t0_tf() == Interval(0.5)); + CHECK(vector_tslices[3].t0_tf() == Interval(0.5,1)); + CHECK(vector_tslices[4].t0_tf() == Interval(1,1)); + + CHECK(tdomain->iterator_tslice(0.)->t0_tf() == Interval(0)); + CHECK(tdomain->iterator_tslice(0.1)->t0_tf() == Interval(0,0.5)); + CHECK(tdomain->iterator_tslice(0.5)->t0_tf() == Interval(0.5)); + CHECK(tdomain->iterator_tslice(0.6)->t0_tf() == Interval(0.5,1)); + CHECK(tdomain->iterator_tslice(1.)->t0_tf() == Interval(1)); + } + + SECTION("Test TDomain with sampling") + { + auto tdomain = create_tdomain(); + tdomain->sample(1.); + CHECK(tdomain->nb_tslices() == 2); + tdomain->sample(10.,false); // no gate + CHECK(tdomain->nb_tslices() == 3); + tdomain->sample(10.,true); // second sampling with gate + CHECK(tdomain->nb_tslices() == 4); + tdomain->sample(10.,true); // no more action + CHECK(tdomain->nb_tslices() == 4); + + const list& tslices = tdomain->tslices(); + vector vector_tslices{ + make_move_iterator(tslices.begin()), + make_move_iterator(tslices.end()) }; + + CHECK(vector_tslices.size() == 4); + CHECK(vector_tslices[0].t0_tf() == Interval(-oo,1)); + CHECK(vector_tslices[1].t0_tf() == Interval(1,10)); + CHECK(vector_tslices[2].t0_tf() == Interval(10)); + CHECK(vector_tslices[3].t0_tf() == Interval(10,oo)); + } + + SECTION("Test unbounded TDomain") + { + auto tdomain = create_tdomain(); + CHECK(tdomain->nb_tslices() == 1); + CHECK(tdomain->t0_tf() == Interval(-oo,oo)); + const list& tslices = tdomain->tslices(); + vector vector_tslices{ + make_move_iterator(tslices.begin()), + make_move_iterator(tslices.end()) }; + CHECK(vector_tslices.size() == 1); + CHECK(vector_tslices[0].t0_tf() == Interval(-oo,oo)); + } + + SECTION("Test TDomain with sampling and values") + { + auto tdomain = create_tdomain(); + CHECK(tdomain->nb_tslices() == 1); + CHECK(tdomain->t0_tf() == Interval(-oo,oo)); + Tube x(tdomain, IntervalVector(1)); + //x.set(Interval(4)); + x(Interval(0,1)) = IntervalVector({{1,5}}); + x(Interval(1,2)) = IntervalVector({{2,8}}); + x(Interval(2,3)) = IntervalVector({{6,9}}); + // Checking structure + vector*> v; + for(const auto& s : x) + v.push_back(&s); + CHECK(v[0]->t0_tf() == Interval(-oo,0)); + CHECK(v[0]->codomain() == IntervalVector({{-oo,oo}})); + CHECK(v[1]->t0_tf() == Interval(0,1)); + CHECK(v[1]->codomain() == IntervalVector({{1,5}})); + CHECK(v[2]->t0_tf() == Interval(1,2)); + CHECK(v[2]->codomain() == IntervalVector({{2,8}})); + CHECK(v[3]->t0_tf() == Interval(2,3)); + CHECK(v[3]->codomain() == IntervalVector({{6,9}})); + CHECK(v[4]->t0_tf() == Interval(3,oo)); + CHECK(v[4]->codomain() == IntervalVector({{-oo,oo}})); + + CHECK(tdomain->iterator_tslice(-1.)->t0_tf() == Interval(-oo,0)); + CHECK(tdomain->iterator_tslice(0.)->t0_tf() == Interval(0,1)); + CHECK(tdomain->iterator_tslice(0.01)->t0_tf() == Interval(0,1)); + CHECK(tdomain->iterator_tslice(1)->t0_tf() == Interval(1,2)); + CHECK(tdomain->iterator_tslice(2)->t0_tf() == Interval(2,3)); + CHECK(tdomain->iterator_tslice(ibex::previous_float(3.))->t0_tf() == Interval(2,3)); + CHECK(tdomain->iterator_tslice(3)->t0_tf() == Interval(3,oo)); + CHECK(tdomain->iterator_tslice(ibex::next_float(3.))->t0_tf() == Interval(3,oo)); + + CHECK(tdomain->nb_tslices() == 5); // with [-oo,0] and [3,oo] + CHECK(static_cast(x(Interval(0,3))) == IntervalVector({{1,9}})); + CHECK(static_cast(x(-1)) == IntervalVector(1)); + CHECK(static_cast(x(0.5)) == IntervalVector({{1,5}})); + CHECK(static_cast(x(1.5)) == IntervalVector({{2,8}})); + CHECK(static_cast(x(2.5)) == IntervalVector({{6,9}})); + // No gates: testing values between slices + CHECK(static_cast(x(1.)) == IntervalVector({{2,5}})); + CHECK(static_cast(x(2.)) == IntervalVector({{6,8}})); + CHECK(static_cast(x(3.)) == IntervalVector({{6,9}})); + CHECK(static_cast(x(999.)) == IntervalVector(1)); + + const std::shared_ptr> s0 = x.first_slice_ptr(); + CHECK(s0->t0_tf() == Interval(-oo,0)); + CHECK(s0->codomain() == IntervalVector({{-oo,oo}})); + const std::shared_ptr> s1 = s0->next_slice_ptr(); + CHECK(s1->t0_tf() == Interval(0,1)); + CHECK(s1->codomain() == IntervalVector({{1,5}})); + const std::shared_ptr> s2 = s1->next_slice_ptr(); + CHECK(s2->t0_tf() == Interval(1,2)); + CHECK(s2->codomain() == IntervalVector({{2,8}})); + const std::shared_ptr> s3 = s2->next_slice_ptr(); + CHECK(s3->t0_tf() == Interval(2,3)); + CHECK(s3->codomain() == IntervalVector({{6,9}})); + const std::shared_ptr> s4 = s3->next_slice_ptr(); + CHECK(s4->t0_tf() == Interval(3,oo)); + CHECK(s4->codomain() == IntervalVector({{-oo,oo}})); + + CHECK(tdomain->nb_tslices() == 5); + tdomain->sample(1.3); + CHECK(tdomain->nb_tslices() == 6); + CHECK(s2->t0_tf() == Interval(1,1.3)); + CHECK(s2->codomain() == IntervalVector({{2,8}})); + const std::shared_ptr> s2bis = s2->next_slice_ptr(); + CHECK(s2bis->t0_tf() == Interval(1.3,2)); + CHECK(s2bis->codomain() == IntervalVector({{2,8}})); + CHECK(s3->t0_tf() == Interval(2,3)); + CHECK(s3->codomain() == IntervalVector({{6,9}})); + } + + SECTION("Sampling inside tdomain") + { + auto tdomain = create_tdomain(); + CHECK(tdomain->t0_tf() == Interval(-oo,oo)); + CHECK(tdomain->nb_tslices() == 1); + tdomain->sample(1., false); + CHECK(tdomain->t0_tf() == Interval(-oo,oo)); + CHECK(tdomain->nb_tslices() == 2); + tdomain->sample(1., false); + CHECK(tdomain->t0_tf() == Interval(-oo,oo)); + CHECK(tdomain->nb_tslices() == 2); + tdomain->sample(1., true); + CHECK(tdomain->t0_tf() == Interval(-oo,oo)); + CHECK(tdomain->nb_tslices() == 3); + std::list::iterator it = tdomain->sample(10., true); + CHECK(tdomain->t0_tf() == Interval(-oo,oo)); + CHECK(tdomain->nb_tslices() == 5); + CHECK(it->t0_tf() == Interval(10.)); + it = tdomain->sample(15., false); + CHECK(tdomain->t0_tf() == Interval(-oo,oo)); + CHECK(tdomain->nb_tslices() == 6); + CHECK(it->t0_tf() == Interval(15.,oo)); + } + + SECTION("Sampling outside tdomain") + { + auto tdomain = create_tdomain(Interval(0,0.5)); + CHECK(tdomain->t0_tf() == Interval(0,0.5)); + CHECK(tdomain->nb_tslices() == 1); + tdomain->sample(1., false); + CHECK(tdomain->t0_tf() == Interval(0,1)); + CHECK(tdomain->nb_tslices() == 2); + tdomain->sample(1., false); + CHECK(tdomain->t0_tf() == Interval(0,1)); + CHECK(tdomain->nb_tslices() == 2); // + tdomain->sample(1., false); + CHECK(tdomain->t0_tf() == Interval(0,1)); + CHECK(tdomain->nb_tslices() == 2); + tdomain->sample(1., true); + CHECK(tdomain->t0_tf() == Interval(0,1)); + CHECK(tdomain->nb_tslices() == 3); + std::list::iterator it = tdomain->sample(10., true); + CHECK(tdomain->t0_tf() == Interval(0,10)); + CHECK(tdomain->nb_tslices() == 5); + CHECK(it->t0_tf() == Interval(10.)); + it = tdomain->sample(15., false); + CHECK(tdomain->t0_tf() == Interval(0,15)); + CHECK(tdomain->nb_tslices() == 6); + CHECK(it->t0_tf() == Interval(10,15)); + } + + SECTION("Test basic Tube") + { + auto tdomain = create_tdomain(Interval(0,1), 0.1, false); + Tube x(tdomain, IntervalVector(3)); + + CHECK(x.size() == 3); + CHECK(x.tdomain() == tdomain); + CHECK(x.t0_tf() == Interval(0,1)); + CHECK(x.nb_slices() == tdomain->nb_tslices()); + CHECK(x.nb_slices() == 10); + CHECK(x.first_slice_ptr()->t0_tf() == Interval(0,0.1)); + CHECK(ApproxIntv(x.last_slice_ptr()->t0_tf()) == Interval(0.9,1)); + CHECK(x.codomain() == IntervalVector(3)); + x.set(IntervalVector(3, Interval(-10,10))); + CHECK(x.codomain() == IntervalVector(3, Interval(-10,10))); + + // TubeComponent + CHECK(x[0].codomain() == Interval(-10,10)); + CHECK(x[0].t0_tf() == Interval(0,1)); + CHECK(x[0].tdomain() == tdomain); + x[0].set(Interval(-20,20)); + CHECK(x[0].codomain() == Interval(-20,20)); + CHECK(x.codomain() == IntervalVector({Interval(-20,20),Interval(-10,10),Interval(-10,10)})); + x[1] = x[0]; + CHECK(x[1].codomain() == Interval(-20,20)); + CHECK(x.codomain() == IntervalVector({Interval(-20,20),Interval(-20,20),Interval(-10,10)})); + + // Eval + CHECK(tdomain->nb_tubes() == 1); + CHECK(static_cast(x(Interval(-oo,oo))) == IntervalVector(3)); + CHECK(static_cast(x(Interval(-1,1))) == IntervalVector(3)); + CHECK(static_cast(x(tdomain->t0_tf())) == x.codomain()); + CHECK(static_cast(x(-42.)) == IntervalVector(3)); + + // Eval: affectation at scalar t + CHECK(tdomain->nb_tslices() == 10); + x(-42.) = IntervalVector(3,Interval(2.,3.)); + CHECK(tdomain->nb_tslices() == 12); + + // Checking structure + vector*> v; + for(const auto& s : x) + v.push_back(&s); + CHECK(v[0]->t0_tf() == Interval(-42.)); + CHECK(v[0]->codomain() == IntervalVector(3,Interval(2.,3.))); + CHECK(v[1]->t0_tf() == Interval(-42.,0.)); + CHECK(v[1]->codomain() == IntervalVector(3)); + CHECK(v[2]->t0_tf() == Interval(0.,0.1)); + CHECK(v[2]->codomain() == IntervalVector({Interval(-20,20),Interval(-20,20),Interval(-10,10)})); + CHECK(v[3]->t0_tf() == Interval(0.1,0.2)); + CHECK(v[3]->codomain() == IntervalVector({Interval(-20,20),Interval(-20,20),Interval(-10,10)})); + + CHECK(static_cast(x(-42.)) == IntervalVector(3,Interval(2.,3.))); + CHECK(static_cast(x(ibex::previous_float(-42.))) == IntervalVector(3)); + CHECK(static_cast(x(ibex::next_float(-42.))) == IntervalVector(3)); + + // Eval: affectation at interval t + CHECK(x.codomain() == IntervalVector(3)); + CHECK(tdomain->nb_tslices() == 12); + x(Interval(44,55)) = IntervalVector(3,Interval(9.,10.)); + CHECK(tdomain->nb_tslices() == 14); + + v.clear(); + for(const auto& s : x) + v.push_back(&s); + CHECK(ApproxIntv(v[11]->t0_tf()) == Interval(0.9,1)); + CHECK(v[11]->codomain() == IntervalVector({Interval(-20,20),Interval(-20,20),Interval(-10,10)})); + CHECK(v[12]->t0_tf() == Interval(1,44)); + CHECK(v[12]->codomain() == IntervalVector(3)); + CHECK(v[13]->t0_tf() == Interval(44,55)); + CHECK(v[13]->codomain() == IntervalVector(3,Interval(9.,10.))); + + CHECK(static_cast(x(Interval(44,55))) == IntervalVector(3,Interval(9.,10.))); + CHECK(static_cast(x(ibex::previous_float(44.))) == IntervalVector(3)); + CHECK(static_cast(x(ibex::next_float(55.))) == IntervalVector(3)); + + // Iterators tests + { + shared_ptr> s_ = x.first_slice_ptr(); + for(auto& s : x) + { + CHECK(&s == &(*s_)); + s_ = s_->next_slice_ptr(); + } + } + + // Iterators tests (const) + { + const Tube y(x); // copy constructor + shared_ptr> s_ = x.first_slice_ptr(); + for(const auto& s : x) + { + CHECK(&s == &(*s_)); + s_ = s_->next_slice_ptr(); + } + } + } + + SECTION("Test SliceVector") + { + auto tdomain = create_tdomain(Interval(0,1), 0.1); + Tube x(tdomain, IntervalVector(2)); + CHECK(x.nb_slices() == 10); + CHECK(tdomain->iterator_tslice(-oo) == tdomain->_tslices.end()); + CHECK(tdomain->iterator_tslice(oo) == tdomain->_tslices.end()); + CHECK(x.first_slice_ptr() == tdomain->iterator_tslice(0.)->_slices.at(&x)); + CHECK(x.last_slice_ptr() == tdomain->iterator_tslice(1.)->_slices.at(&x)); + + for(auto& s : x) + s.set(IntervalVector(2,s.t0_tf())); + + vector*> v; + for(const auto& s : x) + v.push_back(&s); + + CHECK(v[0]->t0_tf() == Interval(0,0.1)); + CHECK(v[0]->input_gate() == IntervalVector(2,Interval(0.,0.1))); // nothing before + CHECK(v[0]->codomain() == IntervalVector(2,Interval(0,0.1))); + CHECK(v[0]->output_gate() == IntervalVector(2,Interval(0.1))); + + CHECK(ApproxIntv(v[9]->t0_tf()) == Interval(0.9,1.)); + CHECK(v[9]->input_gate() == v[8]->output_gate()); + CHECK(ApproxIntvVector(v[9]->codomain()) == IntervalVector(2,Interval(0.9,1.))); + CHECK(ApproxIntvVector(v[9]->input_gate()) == IntervalVector(2,Interval(0.9))); + CHECK(ApproxIntvVector(v[9]->output_gate()) == IntervalVector(2,Interval(0.9,1.))); // nothing after + } + + SECTION("Test again 1") + { + auto tdomain = create_tdomain(Interval(1,10), 0.01, true); // last argument creates "gates" (degenerated slices at scalar timesteps) + Tube x(tdomain, + codac::TFunction("(sin(sqrt(t)+((t-5)^2)*[-0.01,0.01]) ; cos(t)+sin(t/0.2)*[-0.1,0.1])")); + Tube u(tdomain, IntervalVector(2)); + + CHECK(x.size() == 2); + CHECK(u.size() == 2); + + codac::TFunction tf("x[2]", "u[2]", "(sin(x[1]) ; -sin(x[0]))"); + CtcDiffInclusion ctc_diffincl(tf); + ctc_diffincl.contract(x,u); + + //vibes::beginDrawing(); + + codac::TubeVector x_codac1 = to_codac1(x); // may take time + codac::Tube xi_codac1 = to_codac1(x)[1]; // may take time + + //codac::VIBesFigTube fig("Tube"); + //fig.set_properties(100, 100, 600, 300); + //fig.add_tube(&xi_codac1, "x"); + //fig.show(true); + + //vibes::endDrawing(); + } + + SECTION("Test again 2") + { + /*{ + auto tdomain = create_tdomain(Interval(0,10), 1., false); + Tube x(tdomain, IntervalVector(2)); + + for(auto& sx : x) + { + if(sx.t0_tf().contains(5.2)) + { + cout << "sample" << endl; + tdomain->sample(5.2); + } + cout << sx << endl; + } + }*/ + + { + codac::TFunction f("x1", "x2", "u1", "u2", "(t+u1+x1;t+u2+x2)"); + IntervalVector x({{2,3},{5,6}}); + IntervalVector u({{0,0.1},{0,0.1}}); + Interval t(5.); + //cout << f.eval_vector(t,x,u) << endl; + CHECK(f.eval_vector(t,x,u) == ApproxIntvVector(IntervalVector({{7, 8.100000000000002},{10, 11.10000000000001}}))); + } + } + + SECTION("Local TDomain, tube still valid after") + { + Tube *x; + + { + auto tdomain = create_tdomain(Interval(0,1), 0.1); + x = new Tube(tdomain, Interval()); + CHECK(x->tdomain() == tdomain); + } + + CHECK(x->tdomain()->t0_tf() == Interval(0,1)); // should not segfault + delete x; + } + + SECTION("Function returning a tube") + { + Tube x = return_a_tube(); + CHECK(x.tdomain()->t0_tf() == Interval(0,2)); + CHECK(x.size() == 3); + CHECK(x.codomain()[1] == Interval(-1.5,1)); + } + + SECTION("Reverse iterator") + { + auto tdomain = create_tdomain(Interval(0,1), 0.5); + Tube x(tdomain, Interval()); + + auto it1 = x.begin(); + CHECK(it1->t0_tf() == Interval(0,0.5)); ++it1; + CHECK(it1->t0_tf() == Interval(0.5,1)); ++it1; + CHECK(it1 == x.end()); + + auto it2 = x.rbegin(); + CHECK(it2->t0_tf() == Interval(0.5,1)); ++it2; + CHECK(it2->t0_tf() == Interval(0,0.5)); ++it2; + CHECK(it2 == x.rend()); + } + + SECTION("Conversion of tubes: codac1-codac2") + { + codac::Tube codac1(Interval(0,10),1.,Interval(-3,6)); + auto tdomain = create_tdomain(Interval(0,10),1.,true); // with gates + codac2::Tube codac2(tdomain, Interval(-3,6)); + CHECK(to_codac1(codac2) == codac1); + CHECK(to_codac2(codac1) == codac2); + CHECK(to_codac1(to_codac2(codac1)) == codac1); + CHECK(to_codac2(to_codac1(codac2)) == codac2); + + codac::TubeVector codac1_vector(Interval(0,10),1.,IntervalVector({{-1,2},{6,8}})); + codac2::Tube codac2_vector(tdomain, IntervalVector({{-1,2},{6,8}})); + codac1_vector.set({{1.2,1.3},{6.8,6.9}}, 0.58); + codac2_vector.set({{1.2,1.3},{6.8,6.9}}, 0.58); + codac1_vector.set({{1.6},{7.2}}, 10.); + codac2_vector.set({{1.6},{7.2}}, 10.); + CHECK(tdomain->t0_tf().ub() == 10.); + CHECK(codac1_vector(0.58) == IntervalVector({{1.2,1.3},{6.8,6.9}})); + CHECK(IntervalVector(codac2_vector(0.58)) == IntervalVector({{1.2,1.3},{6.8,6.9}})); + + CHECK(to_codac1(codac2_vector) == codac1_vector); + CHECK(to_codac2(codac1_vector) == codac2_vector); + CHECK(to_codac1(to_codac2(codac1_vector)) == codac1_vector); + CHECK(to_codac2(to_codac1(codac2_vector)) == codac2_vector); + + codac::TubeVector to_codac1_codac2_vector = to_codac1(codac2_vector); + codac2::Tube to_codac2_codac1_vector = to_codac2(codac1_vector); + // t=0.58 + CHECK(to_codac1_codac2_vector(ibex::previous_float(0.58)) == IntervalVector({{-1,2},{6,8}})); + CHECK(to_codac1_codac2_vector(0.58) == IntervalVector({{1.2,1.3},{6.8,6.9}})); + CHECK(to_codac1_codac2_vector(ibex::next_float(0.58)) == IntervalVector({{-1,2},{6,8}})); + CHECK(IntervalVector(to_codac2_codac1_vector(ibex::previous_float(0.58))) == IntervalVector({{-1,2},{6,8}})); + CHECK(IntervalVector(to_codac2_codac1_vector(0.58)) == IntervalVector({{1.2,1.3},{6.8,6.9}})); + CHECK(IntervalVector(to_codac2_codac1_vector(ibex::next_float(0.58))) == IntervalVector({{-1,2},{6,8}})); + // t=10 + CHECK(to_codac1_codac2_vector(ibex::previous_float(10.)) == IntervalVector({{-1,2},{6,8}})); + CHECK(to_codac1_codac2_vector(10.) == IntervalVector({{1.6},{7.2}})); + CHECK(to_codac1_codac2_vector(ibex::next_float(10.)) == IntervalVector(2)); + CHECK(IntervalVector(to_codac2_codac1_vector(ibex::previous_float(10.))) == IntervalVector({{-1,2},{6,8}})); + CHECK(IntervalVector(to_codac2_codac1_vector(10.)) == IntervalVector({{1.6},{7.2}})); + CHECK(IntervalVector(to_codac2_codac1_vector(ibex::next_float(10.))) == IntervalVector(2)); + } + + SECTION("Testing setting values") + { + auto tdomain = create_tdomain(Interval(0,10),1.,true); // with gates + Tube x(tdomain, Interval(-10,10)); + CHECK(x.codomain() == Interval(-10,10)); + + std::list::iterator it = tdomain->tslices().begin(); + CHECK(it->t0_tf() == Interval(0)); + CHECK(x(it).codomain() == Interval(-10,10)); + it++; + CHECK(it->t0_tf() == Interval(0,1)); + CHECK(x(it).codomain() == Interval(-10,10)); + it++; + CHECK(it->t0_tf() == Interval(1,1)); + CHECK(x(it).codomain() == Interval(-10,10)); + it = tdomain->tslices().begin(); + it++; + x(it).set(Interval(2,8)); + CHECK(x(it).codomain() == Interval(2,8)); + CHECK(x(std::prev(it)).codomain() == Interval(2,8)); + CHECK(x(std::next(it)).codomain() == Interval(2,8)); + CHECK(x(std::next(std::next(it))).codomain() == Interval(-10,10)); + CHECK(x.codomain() == Interval(-10,10)); + it++; it++; it++; it++; it++; + CHECK(it->t0_tf() == Interval(3)); + x(it).set(Interval(3,5)); + CHECK(x(it).codomain() == Interval(3,5)); + CHECK(x(std::prev(it)).codomain() == Interval(-10,10)); + CHECK(x(std::next(it)).codomain() == Interval(-10,10)); + CHECK(x(std::next(std::next(it))).codomain() == Interval(-10,10)); + CHECK(x.codomain() == Interval(-10,10)); + } + + SECTION("Testing validity of copy of tubes") + { + auto tdomain = create_tdomain(Interval(0,5), 0.01, true); + + Tube y(tdomain, Interval(2.)), x1(tdomain, Interval(-1,1)), x2(tdomain, Interval(1)); + Tube cx1(x1), cx2(x2); // copy + + for(std::list::iterator it = cx1.tdomain()->tslices().begin(); + it != cx1.tdomain()->tslices().end(); ++it) + { + Interval ix1 = cx1(it).codomain(), ix2 = cx2(it).codomain(); + ibex::bwd_add(y(it).codomain(), ix1, ix2); + cx1(it).set(ix1); + cx2(it).set(ix2); + } + + CHECK(cx1.codomain() == Interval(1)); + CHECK(cx2.codomain() == Interval(1)); + CHECK(y.codomain() == Interval(2)); + } + + SECTION("Tube not empty if built from a Function") + { + auto tdomain = create_tdomain(Interval(0,5), 0.01, true); + Tube aa1(tdomain, TFunction("5*sin(2*t)+t")); + CHECK(!aa1.is_empty()); + } + + SECTION("Testing tube evaluation") + { + auto tdomain = create_tdomain(Interval(0,5), 0.1, true); + Tube a(tdomain, TFunction("10*cos(t)+t")); + codac::Tube a_codac1 = codac2::to_codac1(a); + //vibes::beginDrawing(); + //codac::VIBesFigTube fig("Tube"); + //fig.set_properties(100, 100, 600, 300); + //fig.add_tube(&a_codac1, "a_codac1", "blue[#7EC8FF88]"); + //fig.draw_box({{1,2},a.eval(Interval(1,2))}); + //fig.draw_box({{1,2},a_codac1(Interval(1,2))}, "red"); + //fig.show(); + //vibes::endDrawing(); + + CHECK(ApproxIntv(tdomain->iterator_tslice(2.)->t0_tf()) == Interval(1.900000000000001, 2.000000000000002)); + CHECK(ApproxIntv(a.eval(Interval(1,2))) == Interval(-2.26146836547144, 7.216099682706644)); + } +} \ No newline at end of file diff --git a/tests/core/tests_codac2_tubes_templated_types.cpp b/tests/core/tests_codac2_tubes_templated_types.cpp new file mode 100644 index 00000000..a27f1a98 --- /dev/null +++ b/tests/core/tests_codac2_tubes_templated_types.cpp @@ -0,0 +1,32 @@ +#include "catch_interval.hpp" +#include "vibes.h" + +#include "codac2_Tube.h" +#include "codac2_Interval.h" +#include "codac2_IntervalMatrix.h" + +using namespace Catch; +using namespace Detail; +using namespace std; +using namespace codac2; + +TEST_CASE("Test codac2::tubes templated") +{ + SECTION("Testing tube of templated IntervalMatrix") + { + auto tdomain = create_tdomain(); + Tube> a(tdomain); + a(2.3) = IntervalMatrix_<2,3>({{1.,2.,3.},{4.,5.,6.}}); + CHECK(a.nb_slices() == 3); + + vector>*> v; + for(const auto& s : a) + v.push_back(&s); + CHECK(v[0]->t0_tf() == Interval(-codac2::oo,2.3)); + CHECK(v[0]->codomain() == (IntervalMatrix_<2,3>())); + CHECK(v[1]->t0_tf() == Interval(2.3)); + CHECK(v[1]->codomain() == (IntervalMatrix_<2,3>({{1.,2.,3.},{4.,5.,6.}}))); + CHECK(v[2]->t0_tf() == Interval(2.3,codac2::oo)); + CHECK(v[2]->codomain() == (IntervalMatrix_<2,3>())); + } +} \ No newline at end of file diff --git a/tests/core/tests_ctc_eval.cpp b/tests/core/tests_ctc_eval.cpp index dd719781..1e4f0c6b 100644 --- a/tests/core/tests_ctc_eval.cpp +++ b/tests/core/tests_ctc_eval.cpp @@ -1334,7 +1334,7 @@ TEST_CASE("CtcEval (other tests)") TubeVector x(tdomain, dt, TFunction("(sin(t) ; -sin(t))")); CtcEval ctc_eval; ctc_eval.contract(t, b, x); - CHECK(b.contains(Vector(2,0.))); + CHECK(b.contains(codac::Vector(2,0.))); CHECK(b.max_diam() < 0.02); CHECK(t == Interval(x[0].slice(0.)->tdomain().lb(),x[0].slice(3.*M_PI)->tdomain().ub())); } diff --git a/tests/core/tests_ctc_picard.cpp b/tests/core/tests_ctc_picard.cpp index d7c77720..84dc6910 100644 --- a/tests/core/tests_ctc_picard.cpp +++ b/tests/core/tests_ctc_picard.cpp @@ -32,7 +32,7 @@ TEST_CASE("CtcPicard") TFunction f2("x", "3."); CtcPicard ctc_picard_f2(f2, delta); Interval dtest = tube[0].slice_tdomain(0); - IntervalVector test = f2.eval_vector(dtest, tube); + IntervalVector test = f2.eval_vector((const Interval)dtest, (const TubeVector)tube); ctc_picard_f2.guess_kth_slices_envelope(tube, 0, TimePropag::FORWARD); CHECK(tube(0).is_superset(1.5 + Interval(0.,0.5) * 3.)); diff --git a/tests/core/tests_operators.cpp b/tests/core/tests_operators.cpp index e14c1c8b..fe1873a9 100644 --- a/tests/core/tests_operators.cpp +++ b/tests/core/tests_operators.cpp @@ -74,7 +74,7 @@ TEST_CASE("Operators") tube1.set(IntervalVector(2, Interval(-1.,1.))); tube1 += TrajectoryVector(domain, TFunction("(2.;2.)")); TrajectoryVector traj(domain, TFunction("(2.;2.)")); - CHECK(traj(0.) == Vector(2, 2.)); + CHECK(traj(0.) == codac::Vector(2, 2.)); CHECK(tube1.codomain() == IntervalVector(2, Interval(1.,3.))); CHECK(tube1(0.) == IntervalVector(2, Interval(1.,3.))); @@ -274,13 +274,13 @@ TEST_CASE("Operators") tube1.set(IntervalVector(2, Interval(-1.,1.))); result = tube1 + TrajectoryVector(domain, TFunction("(2.;2.)")); TrajectoryVector traj(domain, TFunction("(2.;2.)")); - CHECK(traj(0.) == Vector(2, 2.)); + CHECK(traj(0.) == codac::Vector(2, 2.)); CHECK(result.codomain() == IntervalVector(2, Interval(1.,3.))); CHECK(result(0.) == IntervalVector(2, Interval(1.,3.))); tube1.set(IntervalVector(2, Interval(-1.,1.))); result = TrajectoryVector(domain, TFunction("(2.;2.)")) + tube1; - CHECK(traj(0.) == Vector(2, 2.)); + CHECK(traj(0.) == codac::Vector(2, 2.)); CHECK(result.codomain() == IntervalVector(2, Interval(1.,3.))); CHECK(result(0.) == IntervalVector(2, Interval(1.,3.))); diff --git a/tests/core/tests_polygons.cpp b/tests/core/tests_polygons.cpp index a0c95b51..0befa6c2 100644 --- a/tests/core/tests_polygons.cpp +++ b/tests/core/tests_polygons.cpp @@ -29,10 +29,10 @@ TEST_CASE("Polygons") ConvexPolygon p(iv); CHECK(p.nb_vertices() == 4); CHECK(p.box() == iv); - CHECK(p[0] == Vector({-1.,10.})); - CHECK(p[1] == Vector({5.,10.})); - CHECK(p[2] == Vector({5.,11.})); - CHECK(p[3] == Vector({-1.,11.})); + CHECK(p[0] == codac::Vector({-1.,10.})); + CHECK(p[1] == codac::Vector({5.,10.})); + CHECK(p[2] == codac::Vector({5.,11.})); + CHECK(p[3] == codac::Vector({-1.,11.})); } SECTION("Polygon from IntervalVector (unbounded case)") @@ -302,34 +302,34 @@ TEST_CASE("Polygons (intersections)") ConvexPolygon p(v_p); IntervalVector x(2), box_inter(2); - CHECK(p.encloses(ThickPoint(7.,7.)) == YES); - CHECK(p.encloses(ThickPoint(2.,7.)) == YES); - CHECK(p.encloses(ThickPoint(10.,3.)) == YES); - CHECK(p.encloses(ThickPoint(11.,9.)) == YES); - CHECK(p.encloses(ThickPoint(13.5,9.)) == YES); - CHECK(p.encloses(ThickPoint(5.8,12.2)) == YES); - - CHECK(p.encloses(ThickPoint(1.,4.)) == MAYBE); - CHECK(p.encloses(ThickPoint(3.,10.)) == MAYBE); - CHECK(p.encloses(ThickPoint(8.,14.)) == MAYBE); - CHECK(p.encloses(ThickPoint(2.,8.)) == MAYBE); - CHECK(p.encloses(ThickPoint(2.5,9.)) == MAYBE); - CHECK(p.encloses(ThickPoint(5.5,12.5)) == MAYBE); - CHECK(p.encloses(ThickPoint(1.,5.)) == MAYBE); - CHECK(p.encloses(ThickPoint(1.,1.)) == MAYBE); - - CHECK(p.encloses(ThickPoint(10.,2.)) == NO); - CHECK(p.encloses(ThickPoint(0.0,0.0)) == NO); - CHECK(p.encloses(ThickPoint(0.0,0.9)) == NO); - CHECK(p.encloses(ThickPoint(0.9,0.0)) == NO); - CHECK(p.encloses(ThickPoint(0.9,0.9)) == NO); - CHECK(p.encloses(ThickPoint(0.5,1.)) == NO); - CHECK(p.encloses(ThickPoint(5.2,12.8)) == NO); - CHECK(p.encloses(ThickPoint(1.,14.)) == NO); - CHECK(p.encloses(ThickPoint(1.,13.5)) == NO); - CHECK(p.encloses(ThickPoint(14.,1.)) == NO); - CHECK(p.encloses(ThickPoint(14.,14.)) == NO); - CHECK(p.encloses(ThickPoint(5.,14.)) == NO); + CHECK(p.contains(ThickPoint(7.,7.)) == YES); + CHECK(p.contains(ThickPoint(2.,7.)) == YES); + CHECK(p.contains(ThickPoint(10.,3.)) == YES); + CHECK(p.contains(ThickPoint(11.,9.)) == YES); + CHECK(p.contains(ThickPoint(13.5,9.)) == YES); + CHECK(p.contains(ThickPoint(5.8,12.2)) == YES); + + CHECK(p.contains(ThickPoint(1.,4.)) == MAYBE); + CHECK(p.contains(ThickPoint(3.,10.)) == MAYBE); + CHECK(p.contains(ThickPoint(8.,14.)) == MAYBE); + CHECK(p.contains(ThickPoint(2.,8.)) == MAYBE); + CHECK(p.contains(ThickPoint(2.5,9.)) == MAYBE); + CHECK(p.contains(ThickPoint(5.5,12.5)) == MAYBE); + CHECK(p.contains(ThickPoint(1.,5.)) == MAYBE); + CHECK(p.contains(ThickPoint(1.,1.)) == MAYBE); + + CHECK(p.contains(ThickPoint(10.,2.)) == NO); + CHECK(p.contains(ThickPoint(0.0,0.0)) == NO); + CHECK(p.contains(ThickPoint(0.0,0.9)) == NO); + CHECK(p.contains(ThickPoint(0.9,0.0)) == NO); + CHECK(p.contains(ThickPoint(0.9,0.9)) == NO); + CHECK(p.contains(ThickPoint(0.5,1.)) == NO); + CHECK(p.contains(ThickPoint(5.2,12.8)) == NO); + CHECK(p.contains(ThickPoint(1.,14.)) == NO); + CHECK(p.contains(ThickPoint(1.,13.5)) == NO); + CHECK(p.contains(ThickPoint(14.,1.)) == NO); + CHECK(p.contains(ThickPoint(14.,14.)) == NO); + CHECK(p.contains(ThickPoint(5.,14.)) == NO); x[0] = Interval(0.,2.); x[1] = Interval(0.,2.); box_inter = p.fast_intersection(x); @@ -434,7 +434,7 @@ TEST_CASE("Polygons from Slice") // not supported anymore ConvexPolygon p(v_p); // not supported anymore IntervalVector x(2), box_inter(2); // not supported anymore - // not supported anymore CHECK(p.encloses(ThickPoint(3.5,8.))); + // not supported anymore CHECK(p.contains(ThickPoint(3.5,8.))); // not supported anymore // not supported anymore x[0] = Interval(0.5,4.); x[1] = Interval(-1.,1.); // not supported anymore box_inter = p & x; @@ -919,49 +919,49 @@ TEST_CASE("Polygons (Graham scan)") SECTION("Polygons, Graham scan") { - vector v_pts; - v_pts.push_back(Vector({0.,3.})); - v_pts.push_back(Vector({1.,1.})); - v_pts.push_back(Vector({2.,2.})); - v_pts.push_back(Vector({4.,4.})); - v_pts.push_back(Vector({0.,0.})); - v_pts.push_back(Vector({1.,2.})); - v_pts.push_back(Vector({3.,1.})); - v_pts.push_back(Vector({3.,3.})); + vector v_pts; + v_pts.push_back(codac::Vector({0.,3.})); + v_pts.push_back(codac::Vector({1.,1.})); + v_pts.push_back(codac::Vector({2.,2.})); + v_pts.push_back(codac::Vector({4.,4.})); + v_pts.push_back(codac::Vector({0.,0.})); + v_pts.push_back(codac::Vector({1.,2.})); + v_pts.push_back(codac::Vector({3.,1.})); + v_pts.push_back(codac::Vector({3.,3.})); ConvexPolygon hull = GrahamScan::convex_hull(v_pts); - CHECK(hull.vertices()[0] == Vector({0.,0.})); - CHECK(hull.vertices()[1] == Vector({3.,1.})); - CHECK(hull.vertices()[2] == Vector({4.,4.})); - CHECK(hull.vertices()[3] == Vector({0.,3.})); + CHECK(hull.vertices()[0] == codac::Vector({0.,0.})); + CHECK(hull.vertices()[1] == codac::Vector({3.,1.})); + CHECK(hull.vertices()[2] == codac::Vector({4.,4.})); + CHECK(hull.vertices()[3] == codac::Vector({0.,3.})); v_pts.clear(); - v_pts.push_back(Vector({1.,3.})); - v_pts.push_back(Vector({1.,4.})); - v_pts.push_back(Vector({1.5,2.})); - v_pts.push_back(Vector({2.,1.})); - v_pts.push_back(Vector({2.,2.})); - v_pts.push_back(Vector({3.,0.})); - v_pts.push_back(Vector({3.,3.})); - v_pts.push_back(Vector({3.,4.5})); - v_pts.push_back(Vector({4.,2.5})); - v_pts.push_back(Vector({4.,4.})); - v_pts.push_back(Vector({5.,1.})); - v_pts.push_back(Vector({5.,2.})); - v_pts.push_back(Vector({4.,0.})); - v_pts.push_back(Vector({5.,0.})); - v_pts.push_back(Vector({5.,5.})); - v_pts.push_back(Vector({6.,0.})); - v_pts.push_back(Vector({7.,2.})); + v_pts.push_back(codac::Vector({1.,3.})); + v_pts.push_back(codac::Vector({1.,4.})); + v_pts.push_back(codac::Vector({1.5,2.})); + v_pts.push_back(codac::Vector({2.,1.})); + v_pts.push_back(codac::Vector({2.,2.})); + v_pts.push_back(codac::Vector({3.,0.})); + v_pts.push_back(codac::Vector({3.,3.})); + v_pts.push_back(codac::Vector({3.,4.5})); + v_pts.push_back(codac::Vector({4.,2.5})); + v_pts.push_back(codac::Vector({4.,4.})); + v_pts.push_back(codac::Vector({5.,1.})); + v_pts.push_back(codac::Vector({5.,2.})); + v_pts.push_back(codac::Vector({4.,0.})); + v_pts.push_back(codac::Vector({5.,0.})); + v_pts.push_back(codac::Vector({5.,5.})); + v_pts.push_back(codac::Vector({6.,0.})); + v_pts.push_back(codac::Vector({7.,2.})); hull = GrahamScan::convex_hull(v_pts); - CHECK(hull.vertices()[0] == Vector({3.,0.})); - CHECK(hull.vertices()[1] == Vector({6.,0.})); - CHECK(hull.vertices()[2] == Vector({7.,2.})); - CHECK(hull.vertices()[3] == Vector({5.,5.})); - CHECK(hull.vertices()[4] == Vector({3.,4.5})); - CHECK(hull.vertices()[5] == Vector({1.,4.})); - CHECK(hull.vertices()[6] == Vector({1.,3.})); + CHECK(hull.vertices()[0] == codac::Vector({3.,0.})); + CHECK(hull.vertices()[1] == codac::Vector({6.,0.})); + CHECK(hull.vertices()[2] == codac::Vector({7.,2.})); + CHECK(hull.vertices()[3] == codac::Vector({5.,5.})); + CHECK(hull.vertices()[4] == codac::Vector({3.,4.5})); + CHECK(hull.vertices()[5] == codac::Vector({1.,4.})); + CHECK(hull.vertices()[6] == codac::Vector({1.,3.})); //vibes::beginDrawing(); //VIBesFig fig("poly"); @@ -1021,7 +1021,7 @@ TEST_CASE("Polygons (Graham scan)") // vibes::endDrawing(); //#endif - vector v_pts = p.vertices(); + vector v_pts = p.vertices(); bool contains = true; for(const auto& pt : v_pts) @@ -1040,7 +1040,7 @@ TEST_CASE("Polygons (operations)") v_pts.push_back(ThickPoint(2.,4.)); ConvexPolygon p(v_pts); - p.rotate(-M_PI/2., Vector({3.,1.})); + p.rotate(-M_PI/2., codac::Vector({3.,1.})); // Rotated polygon truth vector v_pts_rot_truth; @@ -1103,22 +1103,22 @@ TEST_CASE("Polygons (Graham scan, again)") { SECTION("Polygons, Graham scan, step by step") { - vector v_pts; - v_pts.push_back(Vector({0.,0.})); - v_pts.push_back(Vector({8.,8.})); - v_pts.push_back(Vector({10.,1.})); - v_pts.push_back(Vector({4.,4.})); - v_pts.push_back(Vector({-10.,1.})); - v_pts.push_back(Vector({2.,2.})); - v_pts.push_back(Vector({6.,3.})); - v_pts.push_back(Vector({6.,1.})); - v_pts.push_back(Vector({10.,4.})); - - CHECK(GrahamScan::orientation(Vector({0.,0.}), Vector({2.,2.}), Vector({2.,2.})) == OrientationInterval::UNDEFINED); - CHECK(GrahamScan::orientation(Vector({0.,0.}), Vector({2.,2.}), Vector({4.,4.})) == OrientationInterval::UNDEFINED); - CHECK(GrahamScan::orientation(Vector({0.,0.}), Vector({8.,8.}), Vector({4.,4.})) == OrientationInterval::UNDEFINED); - CHECK(GrahamScan::orientation(Vector({0.,0.}), Vector({10.,1.}), Vector({4.,4.})) == OrientationInterval::COUNTERCLOCKWISE); - CHECK(GrahamScan::orientation(Vector({0.,0.}), Vector({2.,2.}), Vector({10.,1.})) == OrientationInterval::CLOCKWISE); + vector v_pts; + v_pts.push_back(codac::Vector({0.,0.})); + v_pts.push_back(codac::Vector({8.,8.})); + v_pts.push_back(codac::Vector({10.,1.})); + v_pts.push_back(codac::Vector({4.,4.})); + v_pts.push_back(codac::Vector({-10.,1.})); + v_pts.push_back(codac::Vector({2.,2.})); + v_pts.push_back(codac::Vector({6.,3.})); + v_pts.push_back(codac::Vector({6.,1.})); + v_pts.push_back(codac::Vector({10.,4.})); + + CHECK(GrahamScan::orientation(codac::Vector({0.,0.}), codac::Vector({2.,2.}), codac::Vector({2.,2.})) == OrientationInterval::UNDEFINED); + CHECK(GrahamScan::orientation(codac::Vector({0.,0.}), codac::Vector({2.,2.}), codac::Vector({4.,4.})) == OrientationInterval::UNDEFINED); + CHECK(GrahamScan::orientation(codac::Vector({0.,0.}), codac::Vector({8.,8.}), codac::Vector({4.,4.})) == OrientationInterval::UNDEFINED); + CHECK(GrahamScan::orientation(codac::Vector({0.,0.}), codac::Vector({10.,1.}), codac::Vector({4.,4.})) == OrientationInterval::COUNTERCLOCKWISE); + CHECK(GrahamScan::orientation(codac::Vector({0.,0.}), codac::Vector({2.,2.}), codac::Vector({10.,1.})) == OrientationInterval::CLOCKWISE); // Sort n-1 points with respect to the first point. @@ -1126,19 +1126,19 @@ TEST_CASE("Polygons (Graham scan, again)") // has larger polar angle (in counterclockwise // direction) than p1 - Vector p0 = v_pts[0]; + codac::Vector p0 = v_pts[0]; sort(v_pts.begin(), v_pts.end(), ThickPointsSorter(p0)); CHECK(v_pts.size() == 9); - CHECK(v_pts[0] == Vector({0.,0.})); - CHECK(v_pts[1] == Vector({10.,1.})); - CHECK(v_pts[2] == Vector({6.,1.})); - CHECK(v_pts[3] == Vector({10.,4.})); - CHECK(v_pts[4] == Vector({6.,3.})); - CHECK(v_pts[5] == Vector({2.,2.})); - CHECK(v_pts[6] == Vector({4.,4.})); - CHECK(v_pts[7] == Vector({8.,8.})); - CHECK(v_pts[8] == Vector({-10.,1.})); + CHECK(v_pts[0] == codac::Vector({0.,0.})); + CHECK(v_pts[1] == codac::Vector({10.,1.})); + CHECK(v_pts[2] == codac::Vector({6.,1.})); + CHECK(v_pts[3] == codac::Vector({10.,4.})); + CHECK(v_pts[4] == codac::Vector({6.,3.})); + CHECK(v_pts[5] == codac::Vector({2.,2.})); + CHECK(v_pts[6] == codac::Vector({4.,4.})); + CHECK(v_pts[7] == codac::Vector({8.,8.})); + CHECK(v_pts[8] == codac::Vector({-10.,1.})); // If two or more points make same angle with p0, // remove all but the one that is farthest from p0 @@ -1158,19 +1158,19 @@ TEST_CASE("Polygons (Graham scan, again)") } CHECK(m == 7); - CHECK(v_pts[0] == Vector({0.,0.})); - CHECK(v_pts[1] == Vector({10.,1.})); - CHECK(v_pts[2] == Vector({6.,1.})); - CHECK(v_pts[3] == Vector({10.,4.})); - CHECK(v_pts[4] == Vector({6.,3.})); - CHECK(v_pts[5] == Vector({8.,8.})); - CHECK(v_pts[6] == Vector({-10.,1.})); + CHECK(v_pts[0] == codac::Vector({0.,0.})); + CHECK(v_pts[1] == codac::Vector({10.,1.})); + CHECK(v_pts[2] == codac::Vector({6.,1.})); + CHECK(v_pts[3] == codac::Vector({10.,4.})); + CHECK(v_pts[4] == codac::Vector({6.,3.})); + CHECK(v_pts[5] == codac::Vector({8.,8.})); + CHECK(v_pts[6] == codac::Vector({-10.,1.})); // Create an empty stack and push first three points to it. - vector v_hull; + vector v_hull; - stack s; + stack s; s.push(v_pts[0]); s.push(v_pts[1]); s.push(v_pts[2]); @@ -1202,11 +1202,11 @@ TEST_CASE("Polygons (Graham scan, again)") SECTION("Polygons, Graham scan, other example") { - vector v_pts; + vector v_pts; - Vector p1({-4041.935273669676917052129283547401428223,-5492.667604696881426207255572080612182617}); - Vector p2({9206.843580880462468485347926616668701172,6551.674997467660432448610663414001464844}); - Vector p3({-4041.935273669676917052129283547401428223,-5492.667604696874150249641388654708862305}); + codac::Vector p1({-4041.935273669676917052129283547401428223,-5492.667604696881426207255572080612182617}); + codac::Vector p2({9206.843580880462468485347926616668701172,6551.674997467660432448610663414001464844}); + codac::Vector p3({-4041.935273669676917052129283547401428223,-5492.667604696874150249641388654708862305}); CHECK(p1[0] == p3[0]); CHECK(ThickPoint::aligned(ThickPoint(p1), ThickPoint(p2), ThickPoint(p3)) == NO); @@ -1214,25 +1214,25 @@ TEST_CASE("Polygons (Graham scan, again)") // 0 v_pts.push_back(p1); // 1 - v_pts.push_back(Vector({-2103.177277725693329557543620467185974121,-5492.667604696881426207255572080612182617})); + v_pts.push_back(codac::Vector({-2103.177277725693329557543620467185974121,-5492.667604696881426207255572080612182617})); // 2 - v_pts.push_back(Vector({5720.923292917194885376375168561935424805,-975.4210340695084369144751690328121185303})); + v_pts.push_back(codac::Vector({5720.923292917194885376375168561935424805,-975.4210340695084369144751690328121185303})); // 3 - v_pts.push_back(Vector({9206.843580880462468485347926616668701172,5062.370015818080901226494461297988891602})); + v_pts.push_back(codac::Vector({9206.843580880462468485347926616668701172,5062.370015818080901226494461297988891602})); // 4 - v_pts.push_back(Vector({52.79381299725321952109879930503666400909,5062.370015818080901226494461297988891602})); + v_pts.push_back(codac::Vector({52.79381299725321952109879930503666400909,5062.370015818080901226494461297988891602})); // 5 v_pts.push_back(p3); // 6 v_pts.push_back(p2); // 7 - v_pts.push_back(Vector({52.79381299725321952109879930503666400909,6551.674997467660432448610663414001464844})); + v_pts.push_back(codac::Vector({52.79381299725321952109879930503666400909,6551.674997467660432448610663414001464844})); // 8 - v_pts.push_back(Vector({-4041.935273669676917052129283547401428223,-540.603823869623056452837772667407989502})); + v_pts.push_back(codac::Vector({-4041.935273669676917052129283547401428223,-540.603823869623056452837772667407989502})); - vector v_save(v_pts); + vector v_save(v_pts); - vector v_pts_bis; + vector v_pts_bis; v_pts_bis.push_back(v_pts[4]); v_pts_bis.push_back(v_pts[6]); v_pts_bis.push_back(v_pts[3]); @@ -1300,13 +1300,13 @@ TEST_CASE("Polygons (simplification)") { SECTION("Polygons, simplification, test1") { - vector v_pts; - v_pts.push_back(Vector({2.,0.})); - v_pts.push_back(Vector({6.,4.})); - v_pts.push_back(Vector({6.,5.})); - v_pts.push_back(Vector({5.,6.})); - v_pts.push_back(Vector({4.,6.})); - v_pts.push_back(Vector({2.,3.})); + vector v_pts; + v_pts.push_back(codac::Vector({2.,0.})); + v_pts.push_back(codac::Vector({6.,4.})); + v_pts.push_back(codac::Vector({6.,5.})); + v_pts.push_back(codac::Vector({5.,6.})); + v_pts.push_back(codac::Vector({4.,6.})); + v_pts.push_back(codac::Vector({2.,3.})); ConvexPolygon p(v_pts); ConvexPolygon simple_p5(p), simple_p4(p), simple_p3(p); @@ -1394,13 +1394,13 @@ TEST_CASE("Polygons (simplification)") srand(time(nullptr)); - vector v_pts; + vector v_pts; for(int i = 0 ; i < 500 ; i++) { - Vector pt(2); + codac::Vector pt(2); pt[0] = (rand()/double(RAND_MAX))*box[0].diam()/2.; pt[1] = (rand()/double(RAND_MAX))*2.*M_PI; - v_pts.push_back(Vector({box[0].mid()+pt[0]*cos(pt[1]),box[1].mid()+pt[0]*sin(pt[1])})); + v_pts.push_back(codac::Vector({box[0].mid()+pt[0]*cos(pt[1]),box[1].mid()+pt[0]*sin(pt[1])})); } ConvexPolygon p(v_pts); diff --git a/tests/core/tests_serialization.cpp b/tests/core/tests_serialization.cpp index 340676cd..52b23102 100644 --- a/tests/core/tests_serialization.cpp +++ b/tests/core/tests_serialization.cpp @@ -68,7 +68,7 @@ TEST_CASE("serialization/deserialization of Tube") tube1.serialize(filename); Trajectory *traj; - CHECK_THROWS(Tube tube2(filename, traj);); + CHECK_THROWS([&]() { Tube tube2(filename, traj); }()); Tube tube4(filename); remove(filename.c_str()); @@ -131,7 +131,7 @@ TEST_CASE("serialization/deserialization of Tube") tube1.serialize(filename); TrajectoryVector *traj; - CHECK_THROWS(TubeVector tube2(filename, traj);); + CHECK_THROWS([&]() {TubeVector tube2(filename, traj);}()); TubeVector tube4(filename); remove(filename.c_str()); CHECK(tube1 == tube4); diff --git a/tests/core/tests_slices_structure.cpp b/tests/core/tests_slices_structure.cpp index cf39e395..1649458d 100644 --- a/tests/core/tests_slices_structure.cpp +++ b/tests/core/tests_slices_structure.cpp @@ -402,4 +402,16 @@ TEST_CASE("Tube slices structure") CHECK(tube.nb_slices() == 1); CHECK(tube[0].slice(0)->tdomain() == Interval(8.2,8.3)); } + + SECTION("truncate_tdomain, test 5") + { + TubeVector tube(Interval(0.,10.), 1., 2); + CHECK(tube.nb_slices() == 10); + tube.truncate_tdomain(Interval(3.,6.)); + CHECK(tube.tdomain() == Interval(3.,6.)); + CHECK(tube.nb_slices() == 3); + CHECK(tube[0].slice(0)->tdomain() == Interval(3.,4.)); + CHECK(tube[0].slice(1)->tdomain() == Interval(4.,5.)); + CHECK(tube[0].slice(2)->tdomain() == Interval(5.,6.)); + } } diff --git a/tests/core/tests_trajectory.cpp b/tests/core/tests_trajectory.cpp index 818ec91b..a298b394 100644 --- a/tests/core/tests_trajectory.cpp +++ b/tests/core/tests_trajectory.cpp @@ -150,14 +150,14 @@ TEST_CASE("Trajectory base") SECTION("Trajectory vector") { // Defined by maps of values - map map_values; + map map_values; for(double t = 0. ; t <= 10. ; t++) - map_values.insert(make_pair(t, Vector(4,t))); + map_values.insert(make_pair(t, codac::Vector(4,t))); TrajectoryVector test(map_values); CHECK(test.codomain() == IntervalVector(4,Interval(0.,10.))); - CHECK(test.first_value() == Vector(4,0.)); - CHECK(test.last_value() == Vector(4,10.)); + CHECK(test.first_value() == codac::Vector(4,0.)); + CHECK(test.last_value() == codac::Vector(4,10.)); CHECK(test.size() == 4); } @@ -165,9 +165,9 @@ TEST_CASE("Trajectory base") { // Defined by maps of values - map vector_map_values; + map vector_map_values; for(double t = 0. ; t <= 10. ; t++) - vector_map_values.insert(make_pair(t, Vector(4,t))); + vector_map_values.insert(make_pair(t, codac::Vector(4,t))); TrajectoryVector test1(vector_map_values); map scalar_map_values; diff --git a/tests/test_codac/CMakeLists.txt b/tests/test_codac/CMakeLists.txt index a58e96bb..2d5ee432 100644 --- a/tests/test_codac/CMakeLists.txt +++ b/tests/test_codac/CMakeLists.txt @@ -23,7 +23,7 @@ cmake_minimum_required(VERSION 3.0.2) set(CMAKE_PREFIX_PATH "../eigen") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac