diff --git a/.github/workflows/badges.yml b/.github/workflows/badges.yml index adc7f2e17..ca1c4c904 100644 --- a/.github/workflows/badges.yml +++ b/.github/workflows/badges.yml @@ -22,7 +22,7 @@ jobs: libopenblas-dev liblapacke-dev lcov steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: ref: master diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 5c6cb0704..441b86d83 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -10,8 +10,11 @@ on: push: env: - WINDOWS_BASEKIT_URL: https://registrationcenter-download.intel.com/akdlm/IRC_NAS/f5881e61-dcdc-40f1-9bd9-717081ac623c/intel-oneapi-base-toolkit-2025.2.1.46_offline.exe LINUX_BASEKIT_URL: https://registrationcenter-download.intel.com/akdlm/IRC_NAS/3b7a16b3-a7b0-460f-be16-de0d64fa6b1e/intel-oneapi-base-toolkit-2025.2.1.44_offline.sh + WINDOWS_TOOLKIT_URL: https://registrationcenter-download.intel.com/akdlm/IRC_NAS/bae85ab1-cfcd-4251-8d42-a0c27949ea33/intel-oneapi-toolkit-2026.0.0.193_offline.exe + WINDOWS_MKL_COMPONENTS: intel.oneapi.win.cpp-dpcpp-common:intel.oneapi.win.mkl.devel:intel.oneapi.win.tbb.devel:intel.oneapi.win.dpl + WINDOWS_ONEAPI_CACHE_NUMBER: 1 + ONEAPI_VS_VER: vs2026 jobs: @@ -40,7 +43,7 @@ jobs: - name: Cache installed packages id: cache-apt - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: ~/apt-install-cache key: apt-installed-${{ runner.os }}-${{ env.APT_HASH }} @@ -67,7 +70,7 @@ jobs: shell: bash run: echo ${{ env.BRANCH_NAME }} - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: ref: ${{ env.BRANCH_NAME }} @@ -100,7 +103,7 @@ jobs: - name: Cache installed packages id: cache-apt - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: ~/apt-install-cache key: apt-installed-${{ runner.os }}-${{ env.APT_HASH }} @@ -125,7 +128,7 @@ jobs: - name: Cache Intel OneAPI id: cache-oneapi - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: /opt/intel key: intel-oneapi-linux-${{ env.ONEAPI_HASH }} @@ -142,7 +145,7 @@ jobs: shell: bash run: echo ${{ env.BRANCH_NAME }} - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: ref: ${{ env.BRANCH_NAME }} @@ -159,7 +162,7 @@ jobs: mkdir -p ./zip/dynadjust-linux-mkl mv ./bin/* ./zip/dynadjust-linux-mkl - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v7 with: name: dynadjust-linux-mkl path: ./zip/ @@ -187,7 +190,7 @@ jobs: - name: Cache installed packages id: cache-apt - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: ~/apt-install-cache key: apt-installed-${{ runner.os }}-${{ env.APT_HASH }} @@ -214,13 +217,13 @@ jobs: shell: bash run: echo ${{ env.BRANCH_NAME }} - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: ref: ${{ env.BRANCH_NAME }} - name: Cache static xerces id: cache-xerces - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: | /usr/local/lib/libxerces-c*.a @@ -254,7 +257,7 @@ jobs: mkdir -p ./zip/dynadjust-linux-static mv ./bin/* ./zip/dynadjust-linux-static - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v7 with: name: dynadjust-linux-openblas-static path: ./zip/ @@ -277,7 +280,7 @@ jobs: - name: Cache Homebrew packages id: cache-brew - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: | /opt/homebrew/Cellar/boost @@ -304,7 +307,7 @@ jobs: shell: bash run: echo ${{ env.BRANCH_NAME }} - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: ref: ${{ env.BRANCH_NAME }} @@ -332,7 +335,7 @@ jobs: - name: Cache Homebrew packages id: cache-brew - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: | /opt/homebrew/Cellar/boost @@ -359,7 +362,7 @@ jobs: shell: bash run: echo ${{ env.BRANCH_NAME }} - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: ref: ${{ env.BRANCH_NAME }} @@ -369,7 +372,7 @@ jobs: make -j2 VERBOSE=1 ls -la bin/ - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v7 with: name: dynadjust-macos-static path: ./bin/ @@ -378,13 +381,12 @@ jobs: build_windows_dyn_mkl: name: Windows build (mkl) - runs-on: windows-2025 + runs-on: windows-2025-vs2026 env: BRANCH_NAME: ${{ github.head_ref || github.ref_name }} VCPKG_PACKAGES: boost-geometry boost-process boost-iostreams boost-spirit boost-program-options boost-interprocess xerces-c vcpkg-tool-ninja VCPKG_INSTALLATION_ROOT: "C:/vcpkg" VCPKG_BINARY_SOURCES: "clear" - ONEAPI_VERSION: "2025.2" steps: @@ -392,11 +394,10 @@ jobs: shell: bash run: | echo VCPKG_HASH=$(echo "${VCPKG_PACKAGES}" | sed "s/ /_/g" | md5sum | cut -f 1 -d" ") >> $GITHUB_ENV - echo ONEAPI_HASH=$(echo "${WINDOWS_BASEKIT_URL}" | md5sum | cut -f 1 -d" ") >> $GITHUB_ENV - name: Cache vcpkg id: cache-vcpkg - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: ${{ env.VCPKG_INSTALLATION_ROOT }}/installed key: vcpkg-${{ runner.os }}-mkl-${{ env.VCPKG_HASH }} @@ -406,12 +407,15 @@ jobs: - name: Cache Intel OneAPI id: cache-oneapi - uses: actions/cache@v4 + uses: actions/cache@v5 with: - path: C:\Program Files (x86)\Intel\oneAPI - key: intel-oneapi-windows-${{ env.ONEAPI_HASH }} - restore-keys: | - intel-oneapi-windows- + path: | + C:\Program Files (x86)\Intel\oneAPI\setvars.bat + C:\Program Files (x86)\Intel\oneAPI\setvars-vcvarsall.bat + C:\Program Files (x86)\Intel\oneAPI\compiler + C:\Program Files (x86)\Intel\oneAPI\mkl + C:\Program Files (x86)\Intel\oneAPI\tbb + key: intel-oneapi-windows-${{ env.WINDOWS_ONEAPI_CACHE_NUMBER }}-${{ env.WINDOWS_TOOLKIT_URL }}-${{ env.WINDOWS_MKL_COMPONENTS }} - name: Install vcpkg prerequisites if: steps.cache-vcpkg.outputs.cache-hit != 'true' @@ -432,35 +436,24 @@ jobs: if: steps.cache-oneapi.outputs.cache-hit != 'true' shell: powershell run: | - Write-Host "Installing Intel OneAPI..." - $url = $env:WINDOWS_BASEKIT_URL - $components = "intel.oneapi.win.mkl.devel" $tempExe = Join-Path $env:TEMP "webimage.exe" - - Write-Host "Downloading installer from $url..." - curl.exe -L $url -o $tempExe - - Write-Host "Extracting installer..." - $installerArgs = "-s -x -f webimage_extracted --log extract.log" - $proc = Start-Process -FilePath $tempExe -ArgumentList $installerArgs -NoNewWindow -Wait -PassThru + curl.exe --output $tempExe --url $env:WINDOWS_TOOLKIT_URL --retry 5 --retry-delay 5 + $extract = Start-Process -FilePath $tempExe -ArgumentList "-s -x -f webimage_extracted --log extract.log" -NoNewWindow -Wait -PassThru Remove-Item $tempExe -Force + if ($extract.ExitCode -ne 0) { exit $extract.ExitCode } $bootstrapperPath = Join-Path -Path (Join-Path $PWD "webimage_extracted") "bootstrapper.exe" - - Write-Host "Listing available components..." - $procBootstrap = Start-Process -FilePath $bootstrapperPath -ArgumentList "--list-components" -NoNewWindow -Wait -PassThru -RedirectStandardOutput components - get-content components - - Write-Host "Running bootstrapper..." + $components = $env:WINDOWS_MKL_COMPONENTS $bootstrapArgs = "-s --action install --components=$components --eula=accept -p=NEED_VS2017_INTEGRATION=0 -p=NEED_VS2019_INTEGRATION=0 -p=NEED_VS2022_INTEGRATION=0 --log-dir=." - $procBootstrap = Start-Process -FilePath $bootstrapperPath -ArgumentList $bootstrapArgs -NoNewWindow -Wait -PassThru + $bootstrap = Start-Process -FilePath $bootstrapperPath -ArgumentList $bootstrapArgs -NoNewWindow -Wait -PassThru Remove-Item -Recurse -Force "webimage_extracted" + exit $bootstrap.ExitCode - name: Check branch name shell: bash run: echo ${{ env.BRANCH_NAME }} - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: ref: ${{ env.BRANCH_NAME }} path: "./src" @@ -472,37 +465,35 @@ jobs: curl.exe -L -O https://www.codesynthesis.com/download/xsd/4.2/libxsd-4.2.0.tar.gz tar zxvf libxsd-4.2.0.tar.gz - - name: Extract OneAPI version - shell: powershell - run: | - # Extract version from URL (e.g., 2025.2.1 -> 2025.2) - $url = "${{ env.WINDOWS_BASEKIT_URL }}" - if ($url -match 'toolkit-(\d+\.\d+)\.\d+') { - $version = $matches[1] - echo "ONEAPI_VERSION=$version" >> $env:GITHUB_ENV - Write-Host "Extracted OneAPI version: $version" - } else { - Write-Host "Failed to extract version, using default 2025.2" - echo "ONEAPI_VERSION=2025.2" >> $env:GITHUB_ENV - } + - name: Add MSBuild to PATH + uses: microsoft/setup-msbuild@v3 + with: + vs-version: '[18.0,19.0)' - name: Build with CMake working-directory: "./src" shell: cmd run: | vcpkg.exe integrate install - call "C:\Program Files (x86)\Intel\oneAPI\compiler\%ONEAPI_VERSION%\env\vars.bat" - call "C:\Program Files (x86)\Intel\oneAPI\mkl\%ONEAPI_VERSION%\env\vars.bat" - set XSDROOT=%cd%\libxsd-4.2.0 - set VPKG_INCLUDE=${{ env.VCPKG_INSTALLATION_ROOT }}\installed\x64-windows\include - set INCLUDE=%XSDROOT%;%VPKG_INCLUDE%;%INCLUDE% - set UseEnv=true + set "VS2026INSTALLDIR=C:\Program Files\Microsoft Visual Studio\18\Enterprise" + call "C:\Program Files\Microsoft Visual Studio\18\Enterprise\VC\Auxiliary\Build\vcvars64.bat" + if errorlevel 1 exit /b %errorlevel% + if exist "C:\Program Files (x86)\Intel\oneAPI\setvars-vcvarsall.bat" ( + call "C:\Program Files (x86)\Intel\oneAPI\setvars-vcvarsall.bat" %ONEAPI_VS_VER% + if errorlevel 1 call "C:\Program Files (x86)\Intel\oneAPI\setvars.bat" + ) else ( + call "C:\Program Files (x86)\Intel\oneAPI\setvars.bat" + ) + if errorlevel 1 exit /b %errorlevel% + if not defined MKLROOT set "MKLROOT=C:\Program Files (x86)\Intel\oneAPI\mkl\latest" cmake -DUSE_MKL=ON ^ + "-DMKL_DIR:PATH=%MKLROOT%\lib\cmake\mkl" ^ + -DMKL_THREADING=sequential ^ -DVCPKG_MANIFEST_MODE=OFF ^ -DCMAKE_TOOLCHAIN_FILE"=${{ env.VCPKG_INSTALLATION_ROOT }}/scripts/buildsystems/vcpkg.cmake" ^ -DBUILD_TESTING=OFF ^ -DUSE_UNITY_BUILD=ON ^ - -G "Visual Studio 17 2022" -A x64 dynadjust + -G "Visual Studio 18 2026" -A x64 dynadjust cmake --build %cd% --config Release --parallel 2 - name: Collate artifacts @@ -516,7 +507,7 @@ jobs: cp -f ./src/bin/*.exe ./release/DynAdjust-Windows/ 2>/dev/null || true ls -la ./release/DynAdjust-Windows/ - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v7 with: name: dynadjust-windows-mkl path: ./release/ @@ -525,7 +516,7 @@ jobs: build_windows_dyn_openblas: name: Windows build (openblas) - runs-on: windows-2025 + runs-on: windows-2025-vs2026 env: BRANCH_NAME: ${{ github.head_ref || github.ref_name }} VCPKG_PACKAGES: boost-geometry boost-process boost-iostreams boost-spirit boost-program-options boost-interprocess xerces-c vcpkg-tool-ninja openblas lapack-reference @@ -541,7 +532,7 @@ jobs: - name: Cache vcpkg id: cache-vcpkg - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: ${{ env.VCPKG_INSTALLATION_ROOT }}/installed key: vcpkg-${{ runner.os }}-openblas-${{ env.VCPKG_HASH }} @@ -568,7 +559,7 @@ jobs: shell: bash run: echo ${{ env.BRANCH_NAME }} - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: ref: ${{ env.BRANCH_NAME }} path: "./src" @@ -581,21 +572,22 @@ jobs: tar zxvf libxsd-4.2.0.tar.gz - name: Add MSBuild to PATH - uses: microsoft/setup-msbuild@v2 + uses: microsoft/setup-msbuild@v3 + with: + vs-version: '[18.0,19.0)' - name: Build with CMake working-directory: "./src" shell: cmd run: | vcpkg.exe integrate install - setx WindowsTargetPlatformVersion 10.0.22621.0 set XSDROOT=%cd%\libxsd-4.2.0 cmake -DUSE_MKL=OFF ^ -DVCPKG_MANIFEST_MODE=OFF ^ -DBUILD_TESTING=OFF ^ -DUSE_UNITY_BUILD=ON ^ -DCMAKE_TOOLCHAIN_FILE"=${{ env.VCPKG_INSTALLATION_ROOT }}/scripts/buildsystems/vcpkg.cmake" ^ - -G "Visual Studio 17 2022" -A x64 dynadjust + -G "Visual Studio 18 2026" -A x64 dynadjust cmake --build %cd% --config Release --parallel 2 - name: Collate artifacts @@ -609,7 +601,7 @@ jobs: cp -f ./src/bin/*.exe ./release/DynAdjust-Windows/ 2>/dev/null || true ls -la ./release/DynAdjust-Windows/ - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v7 with: name: dynadjust-windows-openblas path: ./release/ @@ -646,7 +638,7 @@ jobs: shell: bash run: echo ${{ env.BRANCH_NAME }} - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: ref: ${{ env.BRANCH_NAME }} @@ -662,7 +654,7 @@ jobs: - name: Upload test log on failure if: failure() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: ctest-linux-openblas-log path: Testing/Temporary/LastTest.log @@ -728,7 +720,7 @@ jobs: - name: Cache Intel OneAPI id: cache-oneapi - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: /opt/intel key: intel-oneapi-linux-${{ env.ONEAPI_HASH }} @@ -745,7 +737,7 @@ jobs: shell: bash run: echo ${{ env.BRANCH_NAME }} - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: ref: ${{ env.BRANCH_NAME }} @@ -767,7 +759,7 @@ jobs: - name: Upload test log on failure if: failure() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: ctest-linux-mkl-log path: Testing/Temporary/LastTest.log @@ -863,13 +855,12 @@ jobs: ctest_windows_dyn_mkl: name: Build and run ctests on Windows (mkl) - runs-on: windows-2025 + runs-on: windows-2025-vs2026 env: BRANCH_NAME: ${{ github.head_ref || github.ref_name }} VCPKG_PACKAGES: boost-geometry boost-process boost-iostreams boost-spirit boost-program-options boost-interprocess boost-filesystem boost-timer boost-date-time boost-chrono xerces-c vcpkg-tool-ninja VCPKG_INSTALLATION_ROOT: "C:/vcpkg" VCPKG_BINARY_SOURCES: "clear" - ONEAPI_VERSION: "2025.2" steps: @@ -883,11 +874,10 @@ jobs: shell: bash run: | echo VCPKG_HASH=$(echo "${VCPKG_PACKAGES}" | sed "s/ /_/g" | md5sum | cut -f 1 -d" ") >> $GITHUB_ENV - echo ONEAPI_HASH=$(echo "${WINDOWS_BASEKIT_URL}" | md5sum | cut -f 1 -d" ") >> $GITHUB_ENV - name: Cache vcpkg id: cache-vcpkg - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: ${{ env.VCPKG_INSTALLATION_ROOT }}/installed key: vcpkg-${{ runner.os }}-mkl-${{ env.VCPKG_HASH }} @@ -897,12 +887,15 @@ jobs: - name: Cache Intel OneAPI id: cache-oneapi - uses: actions/cache@v4 + uses: actions/cache@v5 with: - path: C:\Program Files (x86)\Intel\oneAPI - key: intel-oneapi-windows-${{ env.ONEAPI_HASH }} - restore-keys: | - intel-oneapi-windows- + path: | + C:\Program Files (x86)\Intel\oneAPI\setvars.bat + C:\Program Files (x86)\Intel\oneAPI\setvars-vcvarsall.bat + C:\Program Files (x86)\Intel\oneAPI\compiler + C:\Program Files (x86)\Intel\oneAPI\mkl + C:\Program Files (x86)\Intel\oneAPI\tbb + key: intel-oneapi-windows-${{ env.WINDOWS_ONEAPI_CACHE_NUMBER }}-${{ env.WINDOWS_TOOLKIT_URL }}-${{ env.WINDOWS_MKL_COMPONENTS }} - name: Install vcpkg prerequisites if: steps.cache-vcpkg.outputs.cache-hit != 'true' @@ -923,35 +916,24 @@ jobs: if: steps.cache-oneapi.outputs.cache-hit != 'true' shell: powershell run: | - Write-Host "Installing Intel OneAPI..." - $url = $env:WINDOWS_BASEKIT_URL - $components = "intel.oneapi.win.mkl.devel" $tempExe = Join-Path $env:TEMP "webimage.exe" - - Write-Host "Downloading installer from $url..." - curl.exe -L $url -o $tempExe - - Write-Host "Extracting installer..." - $installerArgs = "-s -x -f webimage_extracted --log extract.log" - $proc = Start-Process -FilePath $tempExe -ArgumentList $installerArgs -NoNewWindow -Wait -PassThru + curl.exe --output $tempExe --url $env:WINDOWS_TOOLKIT_URL --retry 5 --retry-delay 5 + $extract = Start-Process -FilePath $tempExe -ArgumentList "-s -x -f webimage_extracted --log extract.log" -NoNewWindow -Wait -PassThru Remove-Item $tempExe -Force + if ($extract.ExitCode -ne 0) { exit $extract.ExitCode } $bootstrapperPath = Join-Path -Path (Join-Path $PWD "webimage_extracted") "bootstrapper.exe" - - Write-Host "Listing available components..." - $procBootstrap = Start-Process -FilePath $bootstrapperPath -ArgumentList "--list-components" -NoNewWindow -Wait -PassThru -RedirectStandardOutput components - get-content components - - Write-Host "Running bootstrapper..." + $components = $env:WINDOWS_MKL_COMPONENTS $bootstrapArgs = "-s --action install --components=$components --eula=accept -p=NEED_VS2017_INTEGRATION=0 -p=NEED_VS2019_INTEGRATION=0 -p=NEED_VS2022_INTEGRATION=0 --log-dir=." - $procBootstrap = Start-Process -FilePath $bootstrapperPath -ArgumentList $bootstrapArgs -NoNewWindow -Wait -PassThru + $bootstrap = Start-Process -FilePath $bootstrapperPath -ArgumentList $bootstrapArgs -NoNewWindow -Wait -PassThru Remove-Item -Recurse -Force "webimage_extracted" + exit $bootstrap.ExitCode - name: Check branch name shell: bash run: echo ${{ env.BRANCH_NAME }} - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: ref: ${{ env.BRANCH_NAME }} path: "./src" @@ -963,48 +945,62 @@ jobs: curl.exe -L -O https://www.codesynthesis.com/download/xsd/4.2/libxsd-4.2.0.tar.gz tar zxvf libxsd-4.2.0.tar.gz - - name: Extract OneAPI version - shell: powershell - run: | - # Extract version from URL (e.g., 2025.2.1 -> 2025.2) - $url = "${{ env.WINDOWS_BASEKIT_URL }}" - if ($url -match 'toolkit-(\d+\.\d+)\.\d+') { - $version = $matches[1] - echo "ONEAPI_VERSION=$version" >> $env:GITHUB_ENV - Write-Host "Extracted OneAPI version: $version" - } else { - Write-Host "Failed to extract version, using default 2025.2" - echo "ONEAPI_VERSION=2025.2" >> $env:GITHUB_ENV - } + - name: Add MSBuild to PATH + uses: microsoft/setup-msbuild@v3 + with: + vs-version: '[18.0,19.0)' - name: Build with CMake working-directory: "./src" shell: cmd run: | vcpkg.exe integrate install - call "C:\Program Files (x86)\Intel\oneAPI\setvars.bat" intel64 vs2022 - set XSDROOT=%cd%\libxsd-4.2.0 - set VPKG_INCLUDE=${{ env.VCPKG_INSTALLATION_ROOT }}\installed\x64-windows\include - set INCLUDE=%XSDROOT%;%VPKG_INCLUDE%;%INCLUDE% - set UseEnv=true + set "VS2026INSTALLDIR=C:\Program Files\Microsoft Visual Studio\18\Enterprise" + call "C:\Program Files\Microsoft Visual Studio\18\Enterprise\VC\Auxiliary\Build\vcvars64.bat" + if errorlevel 1 exit /b %errorlevel% + if exist "C:\Program Files (x86)\Intel\oneAPI\setvars-vcvarsall.bat" ( + call "C:\Program Files (x86)\Intel\oneAPI\setvars-vcvarsall.bat" %ONEAPI_VS_VER% + if errorlevel 1 call "C:\Program Files (x86)\Intel\oneAPI\setvars.bat" + ) else ( + call "C:\Program Files (x86)\Intel\oneAPI\setvars.bat" + ) + if errorlevel 1 exit /b %errorlevel% + if not defined MKLROOT set "MKLROOT=C:\Program Files (x86)\Intel\oneAPI\mkl\latest" cmake -DUSE_MKL=ON ^ + "-DMKL_DIR:PATH=%MKLROOT%\lib\cmake\mkl" ^ + -DMKL_THREADING=sequential ^ -DBUILD_TESTING=ON ^ -DVCPKG_MANIFEST_MODE=OFF ^ -DCMAKE_TOOLCHAIN_FILE"=${{ env.VCPKG_INSTALLATION_ROOT }}/scripts/buildsystems/vcpkg.cmake" ^ - -G "Visual Studio 17 2022" -A x64 dynadjust + -G "Visual Studio 18 2026" -A x64 dynadjust cmake --build %cd% --config Release --parallel 2 + cmake --build %cd% --config Release --target unit_tests --parallel 2 dir bin\ - name: Run tests working-directory: "./src" shell: cmd run: | - call "C:\Program Files (x86)\Intel\oneAPI\setvars.bat" - ctest --timeout 20 --output-on-failure -C Release 2>&1 | powershell -Command "$input | Tee-Object -FilePath ctest_output.log" + set "VS2026INSTALLDIR=C:\Program Files\Microsoft Visual Studio\18\Enterprise" + call "C:\Program Files\Microsoft Visual Studio\18\Enterprise\VC\Auxiliary\Build\vcvars64.bat" + if errorlevel 1 exit /b %errorlevel% + if exist "C:\Program Files (x86)\Intel\oneAPI\setvars-vcvarsall.bat" ( + call "C:\Program Files (x86)\Intel\oneAPI\setvars-vcvarsall.bat" %ONEAPI_VS_VER% + if errorlevel 1 call "C:\Program Files (x86)\Intel\oneAPI\setvars.bat" + ) else ( + call "C:\Program Files (x86)\Intel\oneAPI\setvars.bat" + ) + if errorlevel 1 exit /b %errorlevel% + set "MKL_NUM_THREADS=1" + set "OMP_NUM_THREADS=1" + set "MKL_DYNAMIC=FALSE" + set "MKL_DOMAIN_NUM_THREADS=MKL_DOMAIN_ALL=1" + set "MKL_THREADING_LAYER=SEQUENTIAL" + ctest --timeout 120 --output-on-failure --stop-on-failure -C Release -E "^unit-MatrixTest$" 2>&1 | powershell -Command "$input | Tee-Object -FilePath ctest_output.log" - name: Upload test log on failure if: failure() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: ctest-windows-mkl-log path: src/Testing/Temporary/LastTest.log @@ -1084,7 +1080,7 @@ jobs: - name: Cache Intel OneAPI id: cache-oneapi - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: /opt/intel key: intel-oneapi-linux-${{ env.ONEAPI_HASH }} @@ -1101,12 +1097,12 @@ jobs: shell: bash run: echo ${{ env.BRANCH_NAME }} - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: ref: ${{ env.BRANCH_NAME }} - name: Download MKL binaries artifact - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v7 with: name: dynadjust-linux-mkl path: ./artifacts @@ -1145,8 +1141,9 @@ jobs: # Source Intel OneAPI environment source /opt/intel/oneapi/setvars.sh - # Modify PATH to use our binaries + # Modify PATH and LD_LIBRARY_PATH to use our binaries export PATH="../bin:$PATH" + export LD_LIBRARY_PATH="../bin:${LD_LIBRARY_PATH:-}" # Run the test script and capture output set +e # Don't exit on error @@ -1169,8 +1166,9 @@ jobs: # Source Intel OneAPI environment source /opt/intel/oneapi/setvars.sh - # Modify PATH to use our binaries + # Modify PATH and LD_LIBRARY_PATH to use our binaries export PATH="../bin:$PATH" + export LD_LIBRARY_PATH="../bin:${LD_LIBRARY_PATH:-}" # Run the test script and capture output set +e # Don't exit on error @@ -1207,12 +1205,12 @@ jobs: shell: bash run: echo ${{ env.BRANCH_NAME }} - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: ref: ${{ env.BRANCH_NAME }} - name: Download static binaries artifact - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v7 with: name: dynadjust-linux-openblas-static path: ./artifacts @@ -1307,12 +1305,12 @@ jobs: shell: bash run: echo ${{ env.BRANCH_NAME }} - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: ref: ${{ env.BRANCH_NAME }} - name: Download static binaries artifact - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v7 with: name: dynadjust-macos-static path: ./artifacts @@ -1386,14 +1384,13 @@ jobs: test_windows_mkl_binaries: name: Test Windows MKL binaries - runs-on: windows-2025 + runs-on: windows-2025-vs2026 needs: [build_windows_dyn_mkl] env: BRANCH_NAME: ${{ github.head_ref || github.ref_name }} VCPKG_PACKAGES: boost-geometry boost-process boost-iostreams boost-spirit boost-program-options boost-interprocess xerces-c vcpkg-tool-ninja VCPKG_INSTALLATION_ROOT: "C:/vcpkg" VCPKG_BINARY_SOURCES: "clear" - ONEAPI_VERSION: "2025.2" steps: - name: Show CPU information @@ -1406,11 +1403,10 @@ jobs: shell: bash run: | echo VCPKG_HASH=$(echo "${VCPKG_PACKAGES}" | sed "s/ /_/g" | md5sum | cut -f 1 -d" ") >> $GITHUB_ENV - echo ONEAPI_HASH=$(echo "${WINDOWS_BASEKIT_URL}" | md5sum | cut -f 1 -d" ") >> $GITHUB_ENV - name: Cache vcpkg id: cache-vcpkg - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: ${{ env.VCPKG_INSTALLATION_ROOT }}/installed key: vcpkg-${{ runner.os }}-mkl-${{ env.VCPKG_HASH }} @@ -1420,12 +1416,15 @@ jobs: - name: Cache Intel OneAPI id: cache-oneapi - uses: actions/cache@v4 + uses: actions/cache@v5 with: - path: C:\Program Files (x86)\Intel\oneAPI - key: intel-oneapi-windows-${{ env.ONEAPI_HASH }} - restore-keys: | - intel-oneapi-windows- + path: | + C:\Program Files (x86)\Intel\oneAPI\setvars.bat + C:\Program Files (x86)\Intel\oneAPI\setvars-vcvarsall.bat + C:\Program Files (x86)\Intel\oneAPI\compiler + C:\Program Files (x86)\Intel\oneAPI\mkl + C:\Program Files (x86)\Intel\oneAPI\tbb + key: intel-oneapi-windows-${{ env.WINDOWS_ONEAPI_CACHE_NUMBER }}-${{ env.WINDOWS_TOOLKIT_URL }}-${{ env.WINDOWS_MKL_COMPONENTS }} - name: Install vcpkg prerequisites if: steps.cache-vcpkg.outputs.cache-hit != 'true' @@ -1442,50 +1441,29 @@ jobs: if: steps.cache-oneapi.outputs.cache-hit != 'true' shell: powershell run: | - Write-Host "Installing Intel OneAPI..." - $url = $env:WINDOWS_BASEKIT_URL - $components = "intel.oneapi.win.mkl.devel" $tempExe = Join-Path $env:TEMP "webimage.exe" - - Write-Host "Downloading installer from $url..." - curl.exe -L $url -o $tempExe - - Write-Host "Extracting installer..." - $installerArgs = "-s -x -f webimage_extracted --log extract.log" - $proc = Start-Process -FilePath $tempExe -ArgumentList $installerArgs -NoNewWindow -Wait -PassThru + curl.exe --output $tempExe --url $env:WINDOWS_TOOLKIT_URL --retry 5 --retry-delay 5 + $extract = Start-Process -FilePath $tempExe -ArgumentList "-s -x -f webimage_extracted --log extract.log" -NoNewWindow -Wait -PassThru Remove-Item $tempExe -Force + if ($extract.ExitCode -ne 0) { exit $extract.ExitCode } $bootstrapperPath = Join-Path -Path (Join-Path $PWD "webimage_extracted") "bootstrapper.exe" - - Write-Host "Running bootstrapper..." + $components = $env:WINDOWS_MKL_COMPONENTS $bootstrapArgs = "-s --action install --components=$components --eula=accept -p=NEED_VS2017_INTEGRATION=0 -p=NEED_VS2019_INTEGRATION=0 -p=NEED_VS2022_INTEGRATION=0 --log-dir=." - $procBootstrap = Start-Process -FilePath $bootstrapperPath -ArgumentList $bootstrapArgs -NoNewWindow -Wait -PassThru + $bootstrap = Start-Process -FilePath $bootstrapperPath -ArgumentList $bootstrapArgs -NoNewWindow -Wait -PassThru Remove-Item -Recurse -Force "webimage_extracted" - - - name: Extract OneAPI version - shell: powershell - run: | - # Extract version from URL (e.g., 2025.2.1 -> 2025.2) - $url = "${{ env.WINDOWS_BASEKIT_URL }}" - if ($url -match 'toolkit-(\d+\.\d+)\.\d+') { - $version = $matches[1] - echo "ONEAPI_VERSION=$version" >> $env:GITHUB_ENV - Write-Host "Extracted OneAPI version: $version" - } else { - Write-Host "Failed to extract version, using default 2025.2" - echo "ONEAPI_VERSION=2025.2" >> $env:GITHUB_ENV - } + exit $bootstrap.ExitCode - name: Check branch name shell: bash run: echo ${{ env.BRANCH_NAME }} - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: ref: ${{ env.BRANCH_NAME }} - name: Download binaries artifact - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v7 with: name: dynadjust-windows-mkl path: ./artifacts @@ -1516,8 +1494,19 @@ jobs: working-directory: ./sampleData shell: cmd run: | - call "C:\Program Files (x86)\Intel\oneAPI\setvars.bat" - set PATH=..\bin;%PATH% + set "VS2026INSTALLDIR=C:\Program Files\Microsoft Visual Studio\18\Enterprise" + call "C:\Program Files\Microsoft Visual Studio\18\Enterprise\VC\Auxiliary\Build\vcvars64.bat" + if errorlevel 1 exit /b %errorlevel% + if exist "C:\Program Files (x86)\Intel\oneAPI\setvars-vcvarsall.bat" ( + call "C:\Program Files (x86)\Intel\oneAPI\setvars-vcvarsall.bat" %ONEAPI_VS_VER% + if errorlevel 1 call "C:\Program Files (x86)\Intel\oneAPI\setvars.bat" + ) else ( + call "C:\Program Files (x86)\Intel\oneAPI\setvars.bat" + ) + if errorlevel 1 exit /b %errorlevel% + if not defined ONEAPI_ROOT set "ONEAPI_ROOT=C:\Program Files (x86)\Intel\oneAPI" + if not defined MKLROOT set "MKLROOT=%ONEAPI_ROOT%\mkl\latest" + set PATH=..\bin;%VCPKG_INSTALLATION_ROOT%\installed\x64-windows\bin;%MKLROOT%\bin;%ONEAPI_ROOT%\compiler\latest\bin;%PATH% :: Run the commands directly using the correct names ..\bin\import.exe -n gnss gnss-network.stn gnss-network.msr @@ -1543,8 +1532,19 @@ jobs: working-directory: ./sampleData shell: cmd run: | - call "C:\Program Files (x86)\Intel\oneAPI\setvars.bat" - set PATH=..\bin;%PATH% + set "VS2026INSTALLDIR=C:\Program Files\Microsoft Visual Studio\18\Enterprise" + call "C:\Program Files\Microsoft Visual Studio\18\Enterprise\VC\Auxiliary\Build\vcvars64.bat" + if errorlevel 1 exit /b %errorlevel% + if exist "C:\Program Files (x86)\Intel\oneAPI\setvars-vcvarsall.bat" ( + call "C:\Program Files (x86)\Intel\oneAPI\setvars-vcvarsall.bat" %ONEAPI_VS_VER% + if errorlevel 1 call "C:\Program Files (x86)\Intel\oneAPI\setvars.bat" + ) else ( + call "C:\Program Files (x86)\Intel\oneAPI\setvars.bat" + ) + if errorlevel 1 exit /b %errorlevel% + if not defined ONEAPI_ROOT set "ONEAPI_ROOT=C:\Program Files (x86)\Intel\oneAPI" + if not defined MKLROOT set "MKLROOT=%ONEAPI_ROOT%\mkl\latest" + set PATH=..\bin;%VCPKG_INSTALLATION_ROOT%\installed\x64-windows\bin;%MKLROOT%\bin;%ONEAPI_ROOT%\compiler\latest\bin;%PATH% :: Run the commands directly using the correct names ..\bin\import.exe -n urban urban-network.stn urban-network.msr @@ -1569,7 +1569,7 @@ jobs: test_windows_openblas_binaries: name: Test Windows OpenBLAS binaries - runs-on: windows-2025 + runs-on: windows-2025-vs2026 needs: [build_windows_dyn_openblas] env: BRANCH_NAME: ${{ github.head_ref || github.ref_name }} @@ -1591,7 +1591,7 @@ jobs: - name: Cache vcpkg id: cache-vcpkg - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: ${{ env.VCPKG_INSTALLATION_ROOT }}/installed key: vcpkg-${{ runner.os }}-openblas-${{ env.VCPKG_HASH }} @@ -1614,12 +1614,12 @@ jobs: shell: bash run: echo ${{ env.BRANCH_NAME }} - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: ref: ${{ env.BRANCH_NAME }} - name: Download binaries artifact - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v7 with: name: dynadjust-windows-openblas path: ./artifacts diff --git a/.github/workflows/check_code_format.yml b/.github/workflows/check_code_format.yml index 12bb2c4e6..b14d6841f 100644 --- a/.github/workflows/check_code_format.yml +++ b/.github/workflows/check_code_format.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v6 - name: Install clang-format uses: aminya/setup-cpp@v1 diff --git a/.github/workflows/clang_tidy.yml b/.github/workflows/clang_tidy.yml index 178bc4650..6152ae78b 100644 --- a/.github/workflows/clang_tidy.yml +++ b/.github/workflows/clang_tidy.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v6 - name: Run clang-tidy uses: HorstBaerbel/action-clang-tidy@1.2 with: diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 11c0b81d7..1eff27d04 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -22,7 +22,7 @@ jobs: sudo apt-get update sudo apt-get install -y ${{ env.PKGS }} - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: ref: ${{ env.BRANCH_NAME }} @@ -75,7 +75,7 @@ jobs: - name: Upload test log on failure if: failure() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: ctest-coverage-log path: Testing/Temporary/LastTest.log diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index d857120e1..e526e245d 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -14,7 +14,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v6 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v1 diff --git a/dynadjust/CMakeLists.txt b/dynadjust/CMakeLists.txt index b5982f5ec..d910731ef 100644 --- a/dynadjust/CMakeLists.txt +++ b/dynadjust/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.15) -project(dynadjust VERSION 1.3.0) +project(dynadjust VERSION 1.4.1) option(USE_MKL "Use Intel MKL (Linux and Windows)" OFF) option(BUILD_TESTING "Enable testing" OFF) @@ -59,17 +59,23 @@ if(BUILD_TESTING) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) endif() -# Set output directories for all platforms to use ../bin/ -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/../bin) -set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/../bin) -set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/../bin) +# Set output directories for all platforms to use ../bin/ (unless overridden) +if(NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/../bin) +endif() +if(NOT CMAKE_LIBRARY_OUTPUT_DIRECTORY) + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/../bin) +endif() +if(NOT CMAKE_ARCHIVE_OUTPUT_DIRECTORY) + set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/../bin) +endif() # For multi-config generators (Visual Studio, Xcode) foreach(OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES}) string(TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG_UPPER) - set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG_UPPER} ${CMAKE_SOURCE_DIR}/../bin) - set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_${OUTPUTCONFIG_UPPER} ${CMAKE_SOURCE_DIR}/../bin) - set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${OUTPUTCONFIG_UPPER} ${CMAKE_SOURCE_DIR}/../bin) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG_UPPER} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_${OUTPUTCONFIG_UPPER} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) + set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${OUTPUTCONFIG_UPPER} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) endforeach() if (BUILD_STATIC) @@ -189,7 +195,7 @@ else() endif() message(STATUS "Found XercesC (${XercesC_INCLUDE_DIRS} ${XercesC_LIBRARIES})") -# Static builds need ICU for Xerces transcoding (glibc iconv can't dlopen gconv modules in static binaries) +# Static builds need ICU for Xerces transcoding (glibc iconv can't dlopen gconv modules in static binaries). if(BUILD_STATIC AND UNIX AND NOT APPLE) find_package(ICU COMPONENTS uc data) if(ICU_FOUND) @@ -300,10 +306,13 @@ message(STATUS "Found Boost version: ${Boost_VERSION}") # Set up libraries # ---------------------------------------------------------------------------- +find_package(Threads REQUIRED) + set(DNA_LIBRARIES ${Boost_LIBRARIES} ${XercesC_LIBRARIES} ${XERCES_ICU_LIBRARIES} + Threads::Threads ) # ---------------------------------------------------------------------------- @@ -608,6 +617,23 @@ if(BUILD_STATIC) endif() endforeach() + # For MKL builds, add static MKL libraries (MKL::MKL is an imported + # target that the generic .a finder above cannot resolve) + if(USE_MKL AND MKL_FOUND AND DEFINED ENV{MKLROOT}) + set(MKL_LIB_DIR "$ENV{MKLROOT}/lib") + if(ILP64) + set(MKL_INTERFACE_LIB "${MKL_LIB_DIR}/libmkl_intel_ilp64.a") + else() + set(MKL_INTERFACE_LIB "${MKL_LIB_DIR}/libmkl_intel_lp64.a") + endif() + list(APPEND STATIC_LIBS_LIST + "-Wl,--start-group" + "${MKL_INTERFACE_LIB}" + "${MKL_LIB_DIR}/libmkl_sequential.a" + "${MKL_LIB_DIR}/libmkl_core.a" + "-Wl,--end-group") + endif() + message("") message(STATUS "STATIC_LIBS_LIST=${STATIC_LIBS_LIST}") @@ -759,6 +785,31 @@ if (BUILD_TESTING) ${CMAKE_SOURCE_DIR}/include/measurement_types/dnastation.cpp ) + set(JSONL_PARSER_TEST_SOURCES + ${CMAKE_SOURCE_DIR}/dynadjust/dnaimport/dnaparser_jsonl.cpp + ${CMAKE_SOURCE_DIR}/dynadjust/dnaimport/dnaparser_validators.cpp + ${CMAKE_SOURCE_DIR}/include/functions/dnastringfuncs.cpp + ${CMAKE_SOURCE_DIR}/include/functions/template_instantiations.cpp + ${CMAKE_SOURCE_DIR}/include/math/dnamatrix_contiguous.cpp + ${CMAKE_SOURCE_DIR}/include/ide/trace.cpp + ${CMAKE_SOURCE_DIR}/include/parameters/dnadatum.cpp + ${CMAKE_SOURCE_DIR}/include/parameters/dnaellipsoid.cpp + ${CMAKE_SOURCE_DIR}/include/parameters/dnaprojection.cpp + ${CMAKE_SOURCE_DIR}/include/measurement_types/dnaangle.cpp + ${CMAKE_SOURCE_DIR}/include/measurement_types/dnacoordinate.cpp + ${CMAKE_SOURCE_DIR}/include/measurement_types/dnadirection.cpp + ${CMAKE_SOURCE_DIR}/include/measurement_types/dnadirectionset.cpp + ${CMAKE_SOURCE_DIR}/include/measurement_types/dnadistance.cpp + ${CMAKE_SOURCE_DIR}/include/measurement_types/dnagpsbaseline.cpp + ${CMAKE_SOURCE_DIR}/include/measurement_types/dnagpspoint.cpp + ${CMAKE_SOURCE_DIR}/include/measurement_types/dnaheight.cpp + ${CMAKE_SOURCE_DIR}/include/measurement_types/dnaheightdifference.cpp + ${CMAKE_SOURCE_DIR}/include/measurement_types/dnameasurement.cpp + ${CMAKE_SOURCE_DIR}/include/measurement_types/dnastation.cpp + ${CMAKE_SOURCE_DIR}/include/measurement_types/dnamsrtally.cpp + ${CMAKE_SOURCE_DIR}/include/measurement_types/dnastntally.cpp + ) + # Test: test_matrix add_executable(test_matrix ${UNIT_TEST_DIR}/test_matrix.cpp @@ -867,6 +918,15 @@ if (BUILD_TESTING) target_link_libraries(test_gnss_nstat_sort PRIVATE ${DNA_LIBRARIES}) target_compile_definitions(test_gnss_nstat_sort PRIVATE __BINARY_NAME__="test_gnss_nstat_sort" __BINARY_DESC__="Unit tests for GNSS n-stat sort in alternate units") + # Test: test_dnaparser_jsonl + add_executable(test_dnaparser_jsonl + ${UNIT_TEST_DIR}/test_dnaparser_jsonl.cpp + ${JSONL_PARSER_TEST_SOURCES} + ) + target_include_directories(test_dnaparser_jsonl PRIVATE ${UNIT_TEST_DIR} ${CMAKE_SOURCE_DIR}/include) + target_link_libraries(test_dnaparser_jsonl PRIVATE ${DNA_LIBRARIES}) + target_compile_definitions(test_dnaparser_jsonl PRIVATE __BINARY_NAME__="test_dnaparser_jsonl" __BINARY_DESC__="Regression tests for JSONL parser") + # Test: test_bst_file_loader (new) add_executable(test_bst_file_loader ${UNIT_TEST_DIR}/test_bst_file_loader.cpp @@ -916,6 +976,30 @@ if (BUILD_TESTING) target_link_libraries(test_snx_file_writer PRIVATE ${DNA_LIBRARIES}) target_compile_definitions(test_snx_file_writer PRIVATE __BINARY_NAME__="test_snx_file_writer" __BINARY_DESC__="Unit tests for SNX file writer") + # Test: test_format_elapsed_time + add_executable(test_format_elapsed_time + ${UNIT_TEST_DIR}/test_format_elapsed_time.cpp + ) + target_include_directories(test_format_elapsed_time PRIVATE ${UNIT_TEST_DIR} ${CMAKE_SOURCE_DIR}/include) + target_compile_definitions(test_format_elapsed_time PRIVATE __BINARY_NAME__="test_format_elapsed_time" __BINARY_DESC__="Unit tests for FormatElapsedTime helper") + + set(UNIT_TEST_TARGETS + test_matrix test_msr_to_stn_sort test_bst_file test_asl_file + test_aml_file_loader test_bms_file test_network_data_loader + test_measurement_processor test_dnaadjust_printer test_gnss_nstat_sort + test_dnaparser_jsonl test_bst_file_loader test_asl_file_loader test_bms_file_loader + test_snx_file_writer test_format_elapsed_time + ) + if(WIN32) + foreach(UNIT_TEST_TARGET IN LISTS UNIT_TEST_TARGETS) + # Unit tests compile selected DynAdjust sources directly, so they + # must not import DNATYPE_API classes from the shared libraries. + target_compile_definitions(${UNIT_TEST_TARGET} PRIVATE BUILD_STATIC) + endforeach() + endif() + add_custom_target(unit_tests) + add_dependencies(unit_tests ${UNIT_TEST_TARGETS}) + # Register unit tests with CTest add_test(NAME unit-MatrixTest COMMAND $) add_test(NAME unit-MsrToStnSortTest COMMAND $) @@ -927,10 +1011,12 @@ if (BUILD_TESTING) add_test(NAME unit-MeasurementProcessorTest COMMAND $) add_test(NAME unit-DynAdjustPrinterTest COMMAND $) add_test(NAME unit-GNSSNstatSortTest COMMAND $) + add_test(NAME unit-DnaParserJsonlTest COMMAND $) add_test(NAME unit-BstFileLoaderTest COMMAND $) add_test(NAME unit-AslFileLoaderTest COMMAND $) add_test(NAME unit-BmsFileLoaderTest COMMAND $) add_test(NAME unit-SnxFileWriterTest COMMAND $) + add_test(NAME unit-FormatElapsedTimeTest COMMAND $) # ........................................................................ # Functional tests @@ -947,7 +1033,10 @@ if (BUILD_TESTING) add_test(NAME test-gnss-network COMMAND $ gnss.simult.adj gnss.simult.adj.expected --skip-headers 52 -t 0.001) - add_test (NAME import-gnss-network-similar COMMAND $ -n gnss_similar gnss-network.stn gnss-network.msr -r itrf2008 --override-input-ref-frame --search-similar-gnss-msr --quiet) + # 1a. gnss network JSONL import + add_test (NAME import-gnss-network-jsonl COMMAND $ -n gnss_jsonl gnss-networkstn.jsonl gnss-networkmsr.jsonl -r GDA2020 --flag-unused-stations) + + add_test (NAME import-gnss-network-similar COMMAND $ -n gnss_similar gnss-network.stn gnss-network.msr -r itrf2008 --override-input-ref-frame --search-similar-gnss-msr --quiet) add_test (NAME import-gnss-network-exclude COMMAND $ -n gnss_excl gnss-network.stn gnss-network.msr --exclude-stns-assoc-msrs "BEEC,261000380,356000780,349800490" --split) add_test (NAME import-gnss-network-scaled COMMAND $ -n gnss_scaled gnss-network.stn gnss-network.msr --baseline-scalar-file gnss-network.scalars -r GDA94) add_test (NAME import-gnss-network-block1 COMMAND $ -n gnss_b1 gnss-network.stn gnss-network.msr --export-dna --export-xml --single-xml-file --export-asl --export-aml --export-map --output-msr-to-stn --test-integrity -r GDA94 --flag-unused-stations) @@ -974,7 +1063,10 @@ if (BUILD_TESTING) add_test (NAME import-urban-network COMMAND $ -n urban urban-network.stn urban-network.msr --flag-unused-stations) add_test (NAME geoid-urban-network COMMAND $ urban -g urban-network-geoid.gsb --export-dna-geo) add_test (NAME segment-urban-network COMMAND $ urban --min 50 --max 150 --test-integrity) - add_test (NAME adjust-urban-network-verbose COMMAND $ urban --verbose 3) + add_test (NAME import-urban-network-verbose COMMAND $ -n urban_verbose urban-network.stn urban-network.msr --flag-unused-stations) + add_test (NAME geoid-urban-network-verbose COMMAND $ urban_verbose -g urban-network-geoid.gsb --export-dna-geo) + add_test (NAME segment-urban-network-verbose COMMAND $ urban_verbose --min 50 --max 150 --test-integrity) + add_test (NAME adjust-urban-network-verbose COMMAND $ urban_verbose --verbose 3) add_test (NAME adjust-urban-network COMMAND $ urban --output-adj-msr --phased --stn-corrections --export-sinex-file --export-xml-stn-file --export-dna-stn-file --output-pos-uncertainty --export-dna-msr --export-xml-msr) add_test (NAME plot-urban-network-01 COMMAND $ urban --phased --label-sta --correction-arrows --label-corr --compute-corrections --scale-arrows 10.5 --error-ellipse --positional-uncertainty --scale-ellipse-c 10.5) add_test (NAME plot-urban-network-02 COMMAND $ urban --phased --label-sta --label-constraints --correction-arrows --label-corr --compute-corrections --scale-arrows 10.5 --error-ellipse --positional-uncertainty --scale-ellipse-c 10.5 --block-number 2 --alternate-name) @@ -1098,7 +1190,7 @@ if (BUILD_TESTING) add_test (NAME test-urban-phased-network COMMAND $ urban.phased.adj urban.phased.adj.expected --skip-to-marker "M Station 1" -t 0.001) add_test (NAME test-urban-thread-network COMMAND $ urban_mt.phased-mt.adj urban_mt.phased-mt.adj.expected --skip-to-marker "M Station 1" -t 0.01 -v) - # 8. Source tag preservation in --export-xml (issue #317) + # Source tag preservation in --export-xml (issue #317) # Import XML data with tags and verify they are preserved in export add_test (NAME import-source-test COMMAND $ -n srctest source-test-stn.xml source-test-msr.xml --export-xml -r GDA2020) add_test (NAME check-source-import COMMAND bash check_source_tags.sh srctestmsr.xml CAMPAIGN_2023A CAMPAIGN_2023B EMPTY) @@ -1106,6 +1198,28 @@ if (BUILD_TESTING) add_test (NAME reftran-source-test COMMAND $ srctest -r itrf2014 -e 01.01.2020 --export-xml) add_test (NAME check-source-reftran COMMAND bash check_source_tags.sh srctest.ITRF2014.01.01.2020msr.xml CAMPAIGN_2023A CAMPAIGN_2023B EMPTY) + # 8a. XML import with and without xsi:noNamespaceSchemaLocation (issue #346) + # Verify dnaimport handles XML files regardless of whether the schema location + # attribute is present. Strip the attribute and import each sample data pair. + + # gnss-network: with schema location (original files) + add_test (NAME import-xml-gnss-with-xsd COMMAND $ -n xsd_gnss gnss-networkstn.xml gnss-networkmsr.xml -r GDA2020) + # gnss-network: without schema location + add_test (NAME import-xml-gnss-strip COMMAND bash strip_schema_location.sh gnss-networkstn.xml gnss-networkstn-noxsd.xml) + add_test (NAME import-xml-gnss-strip-msr COMMAND bash strip_schema_location.sh gnss-networkmsr.xml gnss-networkmsr-noxsd.xml) + add_test (NAME import-xml-gnss-without-xsd COMMAND $ -n xsd_gnss gnss-networkstn-noxsd.xml gnss-networkmsr-noxsd.xml -r GDA2020) + set_tests_properties(import-xml-gnss-strip import-xml-gnss-strip-msr PROPERTIES FIXTURES_SETUP gnss_strip) + set_tests_properties(import-xml-gnss-without-xsd PROPERTIES FIXTURES_REQUIRED gnss_strip) + + # urban-network: with schema location + add_test (NAME import-xml-urban-with-xsd COMMAND $ -n xsd_urban urban-networkstn.xml urban-networkmsr.xml -r GDA2020) + # urban-network: without schema location + add_test (NAME import-xml-urban-strip COMMAND bash strip_schema_location.sh urban-networkstn.xml urban-networkstn-noxsd.xml) + add_test (NAME import-xml-urban-strip-msr COMMAND bash strip_schema_location.sh urban-networkmsr.xml urban-networkmsr-noxsd.xml) + add_test (NAME import-xml-urban-without-xsd COMMAND $ -n xsd_urban urban-networkstn-noxsd.xml urban-networkmsr-noxsd.xml -r GDA2020) + set_tests_properties(import-xml-urban-strip import-xml-urban-strip-msr PROPERTIES FIXTURES_SETUP urban_strip) + set_tests_properties(import-xml-urban-without-xsd PROPERTIES FIXTURES_REQUIRED urban_strip) + # 9. gnss reference frame transformations add_test (NAME ref-gnss01-network COMMAND $ gnss -r itrf2014 -e 01.01.2020) add_test (NAME ref-gnss02-network COMMAND $ gnss -r itrf1988 -e 03.12.1995) @@ -1744,16 +1858,8 @@ if (BUILD_TESTING) add_test (NAME import-no-frame COMMAND $ -n sample -r itrf1975 dsg.stn dsg.msr) # no stations and measurements (generated above) add_test (NAME import-no-data COMMAND $ -n null ./null.stn ./null.msr) - # no DynaML.xsd - remove file in platform-independent manner (using CMake command) by setting up a test dependency - add_test (NAME import-no-xsd-file-remove COMMAND ${CMAKE_COMMAND} -E remove DynaML.xsd) - add_test (NAME import-no-xsd-file COMMAND $ -n gnss gnss-networkstn.xml gnss-networkmsr.xml) - set_tests_properties(import-no-xsd-file PROPERTIES DEPENDS import-no-xsd-file-remove TIMEOUT 5) - # Restore DynaML.xsd after testing its absence - add_test (NAME import-no-xsd-file-restore COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/../sampleData/DynaML.xsd DynaML.xsd) - set_tests_properties(import-no-xsd-file-restore PROPERTIES DEPENDS import-no-xsd-file) # no geoid file add_test (NAME import-no-geoid COMMAND $ -n geoid --geo-file ./nofile.geo) - set_tests_properties(import-no-geoid PROPERTIES DEPENDS import-no-xsd-file-restore) # no project file add_test (NAME import-no-project COMMAND $ -p ./nofile.dnaproj) # measurement stns not in station file @@ -1765,7 +1871,7 @@ if (BUILD_TESTING) add_test (NAME import-misc-network-size COMMAND $ -n contig --import-contiguous-stn-msr 5000 --export-dna) set_tests_properties(import-no-option import-invalid-option import-invalid-file import-nearby import-no-files import-no-frame - import-no-data import-no-xsd-file import-no-geoid import-no-project import-no-stns import-no-help + import-no-data import-no-geoid import-no-project import-no-stns import-no-help import-misc-block-size import-misc-network-size import-misc-07 PROPERTIES WILL_FAIL TRUE) @@ -1942,6 +2048,9 @@ if (BUILD_TESTING) set_tests_properties( import-urban-network geoid-urban-network segment-urban-network adjust-urban-network PROPERTIES RUN_SERIAL TRUE) + set_tests_properties( + import-urban-network-verbose geoid-urban-network-verbose segment-urban-network-verbose adjust-urban-network-verbose + PROPERTIES RUN_SERIAL TRUE) set_tests_properties( import-urban-network-thread reftran-urban-network-thread geoid-urban-network-thread segment-urban-network-thread adjust-urban-network-thread-01 PROPERTIES RUN_SERIAL TRUE) @@ -1986,7 +2095,7 @@ if (BUILD_TESTING) unit-AmlFileLoaderTest unit-BmsFileTest unit-NetworkDataLoaderTest unit-MeasurementProcessorTest unit-DynAdjustPrinterTest unit-GNSSNstatSortTest unit-BstFileLoaderTest unit-AslFileLoaderTest unit-BmsFileLoaderTest - unit-SnxFileWriterTest + unit-SnxFileWriterTest unit-FormatElapsedTimeTest ) set_tests_properties(${UNIT_TESTS} PROPERTIES RUN_SERIAL FALSE @@ -2007,4 +2116,3 @@ endif () message ("") message ("") - diff --git a/dynadjust/cmake/StaticBuildOptimizations.cmake b/dynadjust/cmake/StaticBuildOptimizations.cmake index 2f3d45716..7bac0b495 100644 --- a/dynadjust/cmake/StaticBuildOptimizations.cmake +++ b/dynadjust/cmake/StaticBuildOptimizations.cmake @@ -6,20 +6,59 @@ function(optimize_static_target TARGET_NAME) if(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") set_property(TARGET ${TARGET_NAME} PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE) endif() - + # Platform-specific optimizations if(UNIX AND NOT APPLE) - # Linux-specific optimizations target_link_options(${TARGET_NAME} PRIVATE - -static - -Wl,--gc-sections # Remove unused sections - -Wl,--as-needed # Only link libraries that are actually used - -Wl,-O2 # Optimize at link time - -Wl,--strip-all # Strip all symbols + -static-libgcc + -static-libstdc++ + -Wl,--as-needed + -Wl,-O2 + -Wl,--strip-all + # glibc 2.34+ merged libpthread into libc; libpthread.a is an + # empty stub. libstdc++.a references ALL pthread functions via + # weak aliases (__gthrw_), and glibc defines them as WEAK (W) + # in libc.a. LLD will not pull a weak-defined archive member to + # satisfy a weak-undefined reference, leaving every pthread + # symbol at address 0 and crashing at runtime. + # Fix: add a strong undefined reference (-u) for each symbol. + # This forces LLD to pull in each object from libc.a, making + # the weak-defined symbol available for the weak refs to bind to. + -Wl,-u,pthread_once + -Wl,-u,pthread_getspecific + -Wl,-u,pthread_setspecific + -Wl,-u,pthread_create + -Wl,-u,pthread_join + -Wl,-u,pthread_equal + -Wl,-u,pthread_self + -Wl,-u,pthread_detach + -Wl,-u,pthread_cancel + -Wl,-u,pthread_mutex_lock + -Wl,-u,pthread_mutex_trylock + -Wl,-u,pthread_mutex_unlock + -Wl,-u,pthread_mutex_init + -Wl,-u,pthread_mutex_destroy + -Wl,-u,pthread_cond_init + -Wl,-u,pthread_cond_broadcast + -Wl,-u,pthread_cond_signal + -Wl,-u,pthread_cond_wait + -Wl,-u,pthread_cond_timedwait + -Wl,-u,pthread_cond_destroy + -Wl,-u,pthread_key_create + -Wl,-u,pthread_key_delete + -Wl,-u,pthread_mutexattr_init + -Wl,-u,pthread_mutexattr_settype + -Wl,-u,pthread_mutexattr_destroy + -Wl,-u,pthread_attr_init + -Wl,-u,pthread_attr_destroy + -Wl,-u,pthread_attr_setdetachstate + -Wl,-u,pthread_exit + -Wl,-u,__pthread_key_create ) + target_link_libraries(${TARGET_NAME} PRIVATE pthread m dl) target_compile_options(${TARGET_NAME} PRIVATE - -ffunction-sections # Put each function in its own section - -fdata-sections # Put each data item in its own section + -ffunction-sections + -fdata-sections ) elseif(APPLE) # macOS-specific optimizations diff --git a/dynadjust/dynadjust.rc b/dynadjust/dynadjust.rc index a0eaebbac..2bbab4905 100644 --- a/dynadjust/dynadjust.rc +++ b/dynadjust/dynadjust.rc @@ -25,8 +25,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,3,0,0 - PRODUCTVERSION 1,3,0,0 + FILEVERSION 1,4,1,0 + PRODUCTVERSION 1,4,1,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -42,12 +42,12 @@ BEGIN BLOCK "040904b0" BEGIN VALUE "FileDescription", "DynAdjust is a rigorous, high performance least squares adjustment application." - VALUE "FileVersion", "1.3.0.0" + VALUE "FileVersion", "1.4.1.0" VALUE "InternalName", "DynAdjust" VALUE "LegalCopyright", "Copyright 2025 Geoscience Australia" VALUE "OriginalFilename", "DynAdjust" VALUE "ProductName", "DynAdjust" - VALUE "ProductVersion", "1.3.0.0" + VALUE "ProductVersion", "1.4.1.0" END END BLOCK "VarFileInfo" diff --git a/dynadjust/dynadjust/dnaadjust/CMakeLists.txt b/dynadjust/dynadjust/dnaadjust/CMakeLists.txt index c817c55a4..321b9fefb 100644 --- a/dynadjust/dynadjust/dnaadjust/CMakeLists.txt +++ b/dynadjust/dynadjust/dnaadjust/CMakeLists.txt @@ -33,8 +33,17 @@ add_library (${PROJECT_NAME} SHARED dnaadjust-stage.cpp dnaadjust.cpp dnaadjust_printer.cpp + dnaadjust_json_printer.cpp ${CMAKE_SOURCE_DIR}/dynadjust.rc) +# The JSON printer pulls in the nlohmann/json single header which is +# expensive to compile; exclude it from the unity/jumbo build so it is +# compiled in isolation. +set_source_files_properties( + dnaadjust_json_printer.cpp + PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON +) + target_precompile_headers (${PROJECT_NAME} PUBLIC precompile.h) target_link_libraries (${PROJECT_NAME} ${DNA_LIBRARIES}) diff --git a/dynadjust/dynadjust/dnaadjust/dnaadjust-multi.cpp b/dynadjust/dynadjust/dnaadjust/dnaadjust-multi.cpp index d0df76cb7..a188ed5d6 100644 --- a/dynadjust/dynadjust/dnaadjust/dnaadjust-multi.cpp +++ b/dynadjust/dynadjust/dnaadjust/dnaadjust-multi.cpp @@ -94,11 +94,9 @@ void dna_adjust::AdjustPhasedMultiThread() initialiseIteration(); std::string corr_msg; - std::ostringstream ss; UINT32 i; bool iterate(true); - std::chrono::milliseconds iteration_time(std::chrono::milliseconds(0)); cpu_timer it_time, tot_time; #if defined(__ICC) || defined(__INTEL_COMPILER) // Intel compiler @@ -169,7 +167,6 @@ void dna_adjust::AdjustPhasedMultiThread() for_each(mt_adjust_threads.begin(), mt_adjust_threads.end(), std::mem_fn(&std::thread::join)); #endif // This point is reached when the threads have finished - iteration_time = std::chrono::duration_cast(it_time.elapsed().wall); //delete mt_adjust_threads; #if defined(__ICC) || defined(__INTEL_COMPILER) // Intel compiler @@ -195,18 +192,12 @@ void dna_adjust::AdjustPhasedMultiThread() if (IsCancelled()) break; - ss.str(""); - if (iteration_time >= std::chrono::seconds(1)) { - auto seconds = std::chrono::duration_cast(iteration_time); - ss << seconds.count() << "s"; - } else { - ss << iteration_time.count() << "ms"; - } + std::string iteration_time_str = FormatElapsedTime(it_time.elapsed().wall.count() / 1.0e9); /////////////////////////////////// // protected write to adj file (not needed here since write to // adj file at this stage is via single thread - adj_file << std::setw(PRINT_VAR_PAD) << std::left << "Elapsed time" << ss.str() << std::endl; + adj_file << std::setw(PRINT_VAR_PAD) << std::left << "Elapsed time" << iteration_time_str << std::endl; OutputLargestCorrection(corr_msg); /////////////////////////////////// @@ -214,6 +205,7 @@ void dna_adjust::AdjustPhasedMultiThread() debug_file << concurrentAdjustments.print_adjusted_blocks(); iterationCorrections_.add_message(corr_msg); + iterationTimes_.add_message(iteration_time_str); iterationQueue_.push_and_notify(CurrentIteration()); // currentIteration begins at 1, so not zero-indexed // continue iterating? @@ -303,7 +295,7 @@ void dna_adjust::SolveMT(bool COMPUTE_INVERSE, const UINT32& block) { // Compute inverse of normals (aposteriori variance matrix) // (AT * V-1 * A)-1 - FormInverseVarianceMatrix(&(v_normalsR_.at(block)), false); + FormInverseVarianceMatrix(&(v_normalsR_.at(block)), false, true); } // compute weighted "measured minus computed" @@ -312,7 +304,10 @@ void dna_adjust::SolveMT(bool COMPUTE_INVERSE, const UINT32& block) // Solve corrections from normal equations v_correctionsR_.at(block).redim(v_designR_.at(block).columns(), 1); - v_correctionsR_.at(block).multiply(v_normalsR_.at(block), "N", At_Vinv_m, "N"); + if (v_normalsR_.at(block).is_symmetric()) + v_correctionsR_.at(block).multiply_sym(v_normalsR_.at(block), At_Vinv_m); + else + v_correctionsR_.at(block).multiply(v_normalsR_.at(block), "N", At_Vinv_m, "N"); // debug output? if (projectSettings_.g.verbose > 3) @@ -842,4 +837,3 @@ void dna_adjust::PrepareAdjustmentMultiThread() } // namespace networkadjust } // namespace dynadjust - diff --git a/dynadjust/dynadjust/dnaadjust/dnaadjust-stage.cpp b/dynadjust/dynadjust/dnaadjust/dnaadjust-stage.cpp index e0e1175d9..4be2c0422 100644 --- a/dynadjust/dynadjust/dnaadjust/dnaadjust-stage.cpp +++ b/dynadjust/dynadjust/dnaadjust/dnaadjust-stage.cpp @@ -159,6 +159,8 @@ void dna_adjust::DeserialiseBlockFromMappedFile(const UINT32& block, const int f return; } + const auto profile_start = profileTimings_ ? std::chrono::steady_clock::now() : std::chrono::steady_clock::time_point{}; + va_list vlist; va_start(vlist, file_count); @@ -173,7 +175,7 @@ void dna_adjust::DeserialiseBlockFromMappedFile(const UINT32& block, const int f break; case sf_normals_r: addr = normalsR_map_.GetBlockRegionAddr(block); - v_normalsR_.at(block).ReadMappedFileRegion(addr); + v_normalsR_.at(block).AttachMappedFileRegion(addr); break; case sf_atvinv: v_AtVinv_.at(block).allocate(); @@ -183,47 +185,47 @@ void dna_adjust::DeserialiseBlockFromMappedFile(const UINT32& block, const int f break; case sf_meas_minus_comp: addr = measMinusComp_map_.GetBlockRegionAddr(block); - v_measMinusComp_.at(block).ReadMappedFileRegion(addr); + v_measMinusComp_.at(block).AttachMappedFileRegion(addr); break; case sf_estimated_stns: addr = estimatedStations_map_.GetBlockRegionAddr(block); - v_estimatedStations_.at(block).ReadMappedFileRegion(addr); + v_estimatedStations_.at(block).AttachMappedFileRegion(addr); break; case sf_original_stns: addr = originalStations_map_.GetBlockRegionAddr(block); - v_originalStations_.at(block).ReadMappedFileRegion(addr); + v_originalStations_.at(block).AttachMappedFileRegion(addr); break; case sf_rigorous_stns: addr = rigorousStations_map_.GetBlockRegionAddr(block); - v_rigorousStations_.at(block).ReadMappedFileRegion(addr); + v_rigorousStations_.at(block).AttachMappedFileRegion(addr); break; case sf_junction_vars: addr = junctionVariances_map_.GetBlockRegionAddr(block); - v_junctionVariances_.at(block).ReadMappedFileRegion(addr); + v_junctionVariances_.at(block).AttachMappedFileRegion(addr); break; case sf_junction_vars_f: addr = junctionVariancesFwd_map_.GetBlockRegionAddr(block); - v_junctionVariancesFwd_.at(block).ReadMappedFileRegion(addr); + v_junctionVariancesFwd_.at(block).AttachMappedFileRegion(addr); break; case sf_junction_ests_f: addr = junctionEstimatesFwd_map_.GetBlockRegionAddr(block); - v_junctionEstimatesFwd_.at(block).ReadMappedFileRegion(addr); + v_junctionEstimatesFwd_.at(block).AttachMappedFileRegion(addr); break; case sf_junction_ests_r: addr = junctionEstimatesRev_map_.GetBlockRegionAddr(block); - v_junctionEstimatesRev_.at(block).ReadMappedFileRegion(addr); + v_junctionEstimatesRev_.at(block).AttachMappedFileRegion(addr); break; case sf_rigorous_vars: addr = rigorousVariances_map_.GetBlockRegionAddr(block); - v_rigorousVariances_.at(block).ReadMappedFileRegion(addr); + v_rigorousVariances_.at(block).AttachMappedFileRegion(addr); break; case sf_prec_adj_msrs: addr = precAdjMsrs_map_.GetBlockRegionAddr(block); - v_precAdjMsrsFull_.at(block).ReadMappedFileRegion(addr); + v_precAdjMsrsFull_.at(block).AttachMappedFileRegion(addr); break; case sf_corrections: addr = corrections_map_.GetBlockRegionAddr(block); - v_corrections_.at(block).ReadMappedFileRegion(addr); + v_corrections_.at(block).AttachMappedFileRegion(addr); if (v_blockMeta_.at(block)._blockLast) v_correctionsR_.at(block).allocate(); @@ -232,6 +234,14 @@ void dna_adjust::DeserialiseBlockFromMappedFile(const UINT32& block, const int f } } va_end(vlist); + + if (profileTimings_) + { + const auto elapsed = std::chrono::steady_clock::now() - profile_start; + profileStageLoadNs_.fetch_add( + static_cast(std::chrono::duration_cast(elapsed).count()), + std::memory_order_relaxed); + } } void dna_adjust::SerialiseBlockToMappedFile(const UINT32& block, const int file_count, ...) @@ -240,14 +250,16 @@ void dna_adjust::SerialiseBlockToMappedFile(const UINT32& block, const int file_ { // serialise all. That is, call this function again, but with // arguments for all files - SerialiseBlockToMappedFile(block, 16, - sf_normals, sf_normals_r, sf_atvinv, sf_design, sf_meas_minus_comp, + SerialiseBlockToMappedFile(block, 12, + sf_normals_r, sf_meas_minus_comp, sf_estimated_stns, sf_original_stns, sf_rigorous_stns, sf_junction_vars, sf_junction_vars_f, sf_junction_ests_f, sf_junction_ests_r, sf_rigorous_vars, sf_prec_adj_msrs, sf_corrections); return; } + const auto profile_start = profileTimings_ ? std::chrono::steady_clock::now() : std::chrono::steady_clock::time_point{}; + va_list vlist; va_start(vlist, file_count); @@ -257,16 +269,10 @@ void dna_adjust::SerialiseBlockToMappedFile(const UINT32& block, const int file_ { switch (va_arg(vlist, int)) { - case sf_normals: - break; case sf_normals_r: addr = normalsR_map_.GetBlockRegionAddr(block); v_normalsR_.at(block).WriteMappedFileRegion(addr); break; - case sf_atvinv: - break; - case sf_design: - break; case sf_meas_minus_comp: addr = measMinusComp_map_.GetBlockRegionAddr(block); v_measMinusComp_.at(block).WriteMappedFileRegion(addr); @@ -314,6 +320,14 @@ void dna_adjust::SerialiseBlockToMappedFile(const UINT32& block, const int file_ } } va_end(vlist); + + if (profileTimings_) + { + const auto elapsed = std::chrono::steady_clock::now() - profile_start; + profileStageStoreNs_.fetch_add( + static_cast(std::chrono::duration_cast(elapsed).count()), + std::memory_order_relaxed); + } } @@ -395,7 +409,11 @@ void dna_adjust::OpenStageFileStreams(const int file_count, ...) } std::stringstream ss; - ss << projectSettings_.g.output_folder << FOLDER_SLASH << projectSettings_.g.network_name << "-"; + if (projectSettings_.a.stage_path.empty()) + ss << projectSettings_.g.output_folder; + else + ss << projectSettings_.a.stage_path; + ss << FOLDER_SLASH << projectSettings_.g.network_name << "-"; std::string filePath(ss.str()); v_stageFileStreams_.clear(); @@ -851,6 +869,46 @@ void dna_adjust::OffloadBlockToMappedFile(const UINT32& block) // Unload block matrix data from memory UnloadBlock(block); + + // Advise kernel that mapped pages for this block are no longer needed, + // freeing page cache for upcoming blocks + AdviseBlockDontNeed(block); +} + + +void dna_adjust::AdviseBlockDontNeed(const UINT32& block) +{ + using advice = boost::interprocess::mapped_region::advice_types; + normalsR_map_.AdviseRegion(block, advice::advice_dontneed); + measMinusComp_map_.AdviseRegion(block, advice::advice_dontneed); + estimatedStations_map_.AdviseRegion(block, advice::advice_dontneed); + originalStations_map_.AdviseRegion(block, advice::advice_dontneed); + rigorousStations_map_.AdviseRegion(block, advice::advice_dontneed); + junctionVariances_map_.AdviseRegion(block, advice::advice_dontneed); + junctionVariancesFwd_map_.AdviseRegion(block, advice::advice_dontneed); + junctionEstimatesFwd_map_.AdviseRegion(block, advice::advice_dontneed); + junctionEstimatesRev_map_.AdviseRegion(block, advice::advice_dontneed); + rigorousVariances_map_.AdviseRegion(block, advice::advice_dontneed); + precAdjMsrs_map_.AdviseRegion(block, advice::advice_dontneed); + corrections_map_.AdviseRegion(block, advice::advice_dontneed); +} + + +void dna_adjust::AdviseBlockWillNeed(const UINT32& block) +{ + using advice = boost::interprocess::mapped_region::advice_types; + normalsR_map_.AdviseRegion(block, advice::advice_willneed); + measMinusComp_map_.AdviseRegion(block, advice::advice_willneed); + estimatedStations_map_.AdviseRegion(block, advice::advice_willneed); + originalStations_map_.AdviseRegion(block, advice::advice_willneed); + rigorousStations_map_.AdviseRegion(block, advice::advice_willneed); + junctionVariances_map_.AdviseRegion(block, advice::advice_willneed); + junctionVariancesFwd_map_.AdviseRegion(block, advice::advice_willneed); + junctionEstimatesFwd_map_.AdviseRegion(block, advice::advice_willneed); + junctionEstimatesRev_map_.AdviseRegion(block, advice::advice_willneed); + rigorousVariances_map_.AdviseRegion(block, advice::advice_willneed); + precAdjMsrs_map_.AdviseRegion(block, advice::advice_willneed); + corrections_map_.AdviseRegion(block, advice::advice_willneed); } @@ -896,7 +954,7 @@ void dna_adjust::UnloadBlock(const UINT32& block, const int file_count, ...) if (file_count == 0) { // deserialise all - UnloadBlock(block, 16, + UnloadBlock(block, 15, sf_normals, sf_normals_r, sf_atvinv, sf_design, sf_meas_minus_comp, sf_estimated_stns, sf_original_stns, sf_rigorous_stns, sf_junction_vars, sf_junction_vars_f, sf_junction_ests_f, sf_junction_ests_r, @@ -907,58 +965,60 @@ void dna_adjust::UnloadBlock(const UINT32& block, const int file_count, ...) va_list vlist; va_start(vlist, file_count); - // Unload block matrix data from memory + // Unload block matrix data from memory by freeing buffers. + // Use deallocate() instead of explicit destructor calls to keep + // the matrix objects in a valid state for later reuse. for (UINT16 file(0); file + #include +#include #include namespace dynadjust { @@ -32,6 +35,9 @@ concurrent_queue combineAdjustmentQueue; concurrent_queue prepareAdjustmentQueue; std::exception_ptr fwd_error, rev_error, cmb_error, prep_error; +void dna_adjust::SetMaxBlasThreads(int n) { dynadjust::math::set_max_blas_threads(n); } +int dna_adjust::GetMaxBlasThreads() { return dynadjust::math::get_max_blas_threads(); } + dna_adjust::dna_adjust() : isPreparing_(false) , isAdjusting_(false) @@ -42,7 +48,12 @@ dna_adjust::dna_adjust() , isAdjustmentQuestionable_(false) , blockCount_(1) , currentBlock_(0) + , lastBlockElapsedMs_(0) , total_time_(0) + , profileTimings_(std::getenv("DYNADJUST_PROFILE") != nullptr) + , profileUpdateNormalsNs_(0) + , profileStageLoadNs_(0) + , profileStageStoreNs_(0) , adjustStatus_(ADJUST_SUCCESS) , currentIteration_(0) , datum_(DEFAULT_EPSG_U) @@ -133,10 +144,9 @@ dna_adjust::dna_adjust() v_correctionsR_.clear(); } - // Initialize the printer printer_ = std::make_unique(*this); } - + dna_adjust::~dna_adjust() { @@ -172,8 +182,15 @@ UINT32& dna_adjust::incrementIteration() return ++currentIteration_; } -void dna_adjust::initialiseIteration(const UINT32& iteration) -{ +void dna_adjust::decrementIteration() +{ + std::lock_guard lock(current_iterationMutex); + if (currentIteration_ > 0) + --currentIteration_; +} + +void dna_adjust::initialiseIteration(const UINT32& iteration) +{ std::lock_guard lock(current_iterationMutex); currentIteration_ = iteration; } @@ -195,6 +212,9 @@ void dna_adjust::InitialiseAdjustment() adjustStatus_ = ADJUST_SUCCESS; statusMessages_.clear(); + profileUpdateNormalsNs_.store(0, std::memory_order_relaxed); + profileStageLoadNs_.store(0, std::memory_order_relaxed); + profileStageStoreNs_.store(0, std::memory_order_relaxed); currentBlock_ = 0; initialiseIteration(); @@ -240,6 +260,7 @@ void dna_adjust::PrepareAdjustment(const project_settings& projectSettings) isPreparing_ = true; isAdjusting_ = true; isCombining_ = false; + rebuildingDesign_ = false; isFirstTimeAdjustment_ = true; projectSettings_ = projectSettings; @@ -248,10 +269,17 @@ void dna_adjust::PrepareAdjustment(const project_settings& projectSettings) projectSettings_.a.multi_thread)) projectSettings_.a.stage = false; - // Load the bst/bms meta and set the default + // Load the bst/bms meta and set the default // reference frame (via binary station file) SetDefaultReferenceFrame(); + if (projectSettings_.o._output_json) + { + auto json_printer = std::make_unique(*this); + json_printer->OpenStreams(); + printer_ = std::move(json_printer); + } + // Open output files for printing adjustment results OpenOutputFileStreams(); printer_->PrintOutputFileHeaderInfo(); @@ -528,14 +556,14 @@ void dna_adjust::UpdateAdjustment(bool iterate) // Hence, only design and msr-comp are required so as to compute stats if (!iterate) continue; - + switch (projectSettings_.a.adjust_mode) { case PhasedMode: case Phased_Block_1Mode: v_normals_.at(block).zero(); UpdateNormals(block, false); - + if (projectSettings_.a.multi_thread) { v_estimatedStationsR_.at(block) = v_rigorousStations_.at(block); @@ -545,9 +573,9 @@ void dna_adjust::UpdateAdjustment(bool iterate) } // Back up normals. This copy contains the contributions from all - // apriori measurement variances, excluding parameter station + // apriori measurement variances, excluding parameter station // variances and junction station variances - v_normalsR_.at(block) = v_normals_.at(block); + v_normalsR_.at(block) = v_normals_.at(block); AddConstraintStationstoNormalsForward(block); break; @@ -742,17 +770,19 @@ void dna_adjust::PrepareStationandVarianceMatrices(const UINT32& block) // resize rigorous coordinate estimate array v_rigorousStations_.at(block).redim(v_unknownsCount_.at(block), 1); - - // resize rigorous variances - v_rigorousVariances_.at(block).redim(v_unknownsCount_.at(block), v_unknownsCount_.at(block)); + + if (projectSettings_.a.stage || projectSettings_.a.multi_thread) + v_rigorousVariances_.at(block).redim_packed(v_unknownsCount_.at(block)); + else + v_rigorousVariances_.at(block).redim(v_unknownsCount_.at(block), v_unknownsCount_.at(block)); // resize junction variances - v_junctionVariances_.at(block).redim(j, j); + v_junctionVariances_.at(block).redim_packed(j); // resize junction station coordinate estimate arrays if (!v_blockMeta_.at(block)._blockLast && !v_blockMeta_.at(block)._blockIsolated) { - v_junctionVariancesFwd_.at(block).redim(j, j); + v_junctionVariancesFwd_.at(block).redim_packed(j); v_junctionEstimatesFwd_.at(block).redim(j, 1); v_junctionEstimatesRev_.at(block+1).redim(j, 1); } @@ -860,11 +890,10 @@ void dna_adjust::PrepareDesignAndMsrMnsCmpMatrices(const UINT32& block) // Simultaneous, phased (multithreaded and block1) adjustments // Redim all matrices - v_normals_.at(block).redim(v_unknownsCount_.at(block), v_unknownsCount_.at(block)); + v_normals_.at(block).redim_packed(v_unknownsCount_.at(block)); v_design_.at(block).redim(v_measurementCount_.at(block), v_unknownsCount_.at(block)); v_corrections_.at(block).redim(v_unknownsCount_.at(block), 1); - v_precAdjMsrsFull_.at(block).redim(v_measurementVarianceCount_.at(block), 1); // All blocks in v_correctionsR_ are used for multi thread mode, but only the last is used // in single thread mode @@ -897,9 +926,7 @@ void dna_adjust::PrepareDesignAndMsrMnsCmpMatricesStage(const UINT32& block) if (projectSettings_.a.recreate_stage_files || !bms_meta_.reduced) { // Redim NormalsR - v_normalsR_.at(block).redim( - v_unknownsCount_.at(block), - v_unknownsCount_.at(block)); + v_normalsR_.at(block).redim_packed(v_unknownsCount_.at(block)); // Redimension the corrections matrices v_corrections_.at(block).redim(v_unknownsCount_.at(block), 1); @@ -930,7 +957,41 @@ void dna_adjust::PrepareDesignAndMsrMnsCmpMatricesStage(const UINT32& block) // Creating new memory mapped files? Form the design matrices FillDesignNormalMeasurementsMatrices(true, block, false); } - + + +void dna_adjust::RebuildDesignAndAtVinv(const UINT32& block) +{ + UINT32 pseudoMsrElemCount(0); + + if (!v_blockMeta_.at(block)._blockIsolated) + { + UINT32 pseudoMsrCount = static_cast(v_JSL_.at(block).size()); + if (!v_blockMeta_.at(block)._blockFirst) + pseudoMsrCount += static_cast(v_JSL_.at(block-1).size()); + pseudoMsrElemCount = pseudoMsrCount * 3; + } + + v_design_.at(block).redim(v_measurementCount_.at(block), v_unknownsCount_.at(block)); + v_AtVinv_.at(block).redim( + v_unknownsCount_.at(block), + v_measurementCount_.at(block) + pseudoMsrElemCount); + + if (pseudoMsrElemCount > 0) + v_AtVinv_.at(block).shrink(0, pseudoMsrElemCount); + + assert(v_design_.at(block).getbuffer() != nullptr && "RebuildDesignAtVinv: design null after redim"); + assert(v_AtVinv_.at(block).getbuffer() != nullptr && "RebuildDesignAtVinv: AtVinv null after redim"); + assert(v_estimatedStations_.at(block).getbuffer() != nullptr && "RebuildDesignAtVinv: estimatedStations null"); + assert(v_measMinusComp_.at(block).getbuffer() != nullptr && "RebuildDesignAtVinv: measMinusComp null"); + + rebuildingDesign_ = true; + FillDesignNormalMeasurementsMatrices(true, block, false, true); + rebuildingDesign_ = false; + + assert(v_design_.at(block).getbuffer() != nullptr && "RebuildDesignAtVinv: design null after Fill"); + assert(v_AtVinv_.at(block).getbuffer() != nullptr && "RebuildDesignAtVinv: AtVinv null after Fill"); +} + // Re-form At * V-1 for next block using estimated junction parameter station variances // nextBlock = currentBlock+1 @@ -1233,9 +1294,9 @@ void dna_adjust::UpdateAtVinv(pit_vmsr_t _it_msr, const UINT32& stn1, const UINT std::fixed << std::setprecision(16) << std::setw(26) << variance << std::endl; // Build At * V-1 - AtVinv->put(stn1, design_row, variance * design->get(design_row, stn1)); - AtVinv->put(stn1+1, design_row, variance * design->get(design_row, stn1+1)); - AtVinv->put(stn1+2, design_row, variance * design->get(design_row, stn1+2)); + AtVinv->dense_put(stn1, design_row, variance * design->dense_get(design_row, stn1)); + AtVinv->dense_put(stn1+1, design_row, variance * design->dense_get(design_row, stn1+1)); + AtVinv->dense_put(stn1+2, design_row, variance * design->dense_get(design_row, stn1+2)); // Single station measurements switch ((*_it_msr)->measType) @@ -1250,16 +1311,16 @@ void dna_adjust::UpdateAtVinv(pit_vmsr_t _it_msr, const UINT32& stn1, const UINT } // Two station measurements - AtVinv->put(stn2, design_row, variance * design->get(design_row, stn2)); - AtVinv->put(stn2+1, design_row, variance * design->get(design_row, stn2+1)); - AtVinv->put(stn2+2, design_row, variance * design->get(design_row, stn2+2)); + AtVinv->dense_put(stn2, design_row, variance * design->dense_get(design_row, stn2)); + AtVinv->dense_put(stn2+1, design_row, variance * design->dense_get(design_row, stn2+1)); + AtVinv->dense_put(stn2+2, design_row, variance * design->dense_get(design_row, stn2+2)); switch ((*_it_msr)->measType) { case 'A': // Horizontal angle - AtVinv->put(stn3, design_row, variance * design->get(design_row, stn3)); - AtVinv->put(stn3+1, design_row, variance * design->get(design_row, stn3+1)); - AtVinv->put(stn3+2, design_row, variance * design->get(design_row, stn3+2)); + AtVinv->dense_put(stn3, design_row, variance * design->dense_get(design_row, stn3)); + AtVinv->dense_put(stn3+1, design_row, variance * design->dense_get(design_row, stn3+1)); + AtVinv->dense_put(stn3+2, design_row, variance * design->dense_get(design_row, stn3+2)); } } @@ -1269,27 +1330,27 @@ void dna_adjust::UpdateAtVinv_D(const UINT32& stn1, const UINT32& stn2, const UI UINT32& design_row, UINT32& design_row_begin, matrix_2d* Vinv, matrix_2d* design, matrix_2d* AtVinv) { + const double angle_variance = Vinv->dense_get(angle, angle); for (UINT32 j, i(0); i<3; ++i) // for each coordinate element (x, y, z) { // add variances - AtVinv->elementadd(stn1+i, design_row, // station1 - design->get(design_row, stn1+i) * Vinv->get(angle, angle)); - AtVinv->elementadd(stn2+i, design_row, // station2 - design->get(design_row, stn2+i) * Vinv->get(angle, angle)); - AtVinv->elementadd(stn3+i, design_row, // station3 - design->get(design_row, stn3+i) * Vinv->get(angle, angle)); + const double design1 = design->dense_get(design_row, stn1+i); + const double design2 = design->dense_get(design_row, stn2+i); + const double design3 = design->dense_get(design_row, stn3+i); + + AtVinv->dense_add(stn1+i, design_row, design1 * angle_variance); // station1 + AtVinv->dense_add(stn2+i, design_row, design2 * angle_variance); // station2 + AtVinv->dense_add(stn3+i, design_row, design3 * angle_variance); // station3 // add covariances for (j=0; jelementadd(stn1+i, design_row_begin+j, // station1 - design->get(design_row, stn1+i) * Vinv->get(angle, j)); - AtVinv->elementadd(stn2+i, design_row_begin+j, // station2 - design->get(design_row, stn2+i) * Vinv->get(angle, j)); - AtVinv->elementadd(stn3+i, design_row_begin+j, // station3 - design->get(design_row, stn3+i) * Vinv->get(angle, j)); + const double angle_covariance = Vinv->dense_get(angle, j); + AtVinv->dense_add(stn1+i, design_row_begin+j, design1 * angle_covariance); // station1 + AtVinv->dense_add(stn2+i, design_row_begin+j, design2 * angle_covariance); // station2 + AtVinv->dense_add(stn3+i, design_row_begin+j, design3 * angle_covariance); // station3 } } } @@ -1302,6 +1363,7 @@ void dna_adjust::UpdateAtVinv_D(const UINT32& stn1, const UINT32& stn2, const UI // - PrepareFwdAdj (used by adjust_forward_thread) void dna_adjust::UpdateNormals(const UINT32& block, bool MT_ReverseOrCombine) { + const auto profile_start = profileTimings_ ? std::chrono::steady_clock::now() : std::chrono::steady_clock::time_point{}; UINT32 stn1, stn2, stn3, design_row(0); it_vUINT32 _it_block_msr; @@ -1382,22 +1444,59 @@ void dna_adjust::UpdateNormals(const UINT32& block, bool MT_ReverseOrCombine) "'." << std::endl; SignalExceptionAdjustment(ss.str(), block); } - } + } + + if (profileTimings_) + { + const auto elapsed = std::chrono::steady_clock::now() - profile_start; + profileUpdateNormalsNs_.fetch_add( + static_cast(std::chrono::duration_cast(elapsed).count()), + std::memory_order_relaxed); + } } +namespace { + +inline void add_normal_3x3_from_design_row(UINT32 design_row, UINT32 atvinv_station, UINT32 design_station, + matrix_2d* normals, const matrix_2d* design, const matrix_2d* AtVinv) +{ + const double* atvinv_col = AtVinv->dense_ptr(atvinv_station, design_row); + + for (UINT32 col = 0; col < 3; ++col) + { + const double design_value = *design->dense_ptr(design_row, design_station + col); + normals->lower_add(atvinv_station, design_station + col, + atvinv_col[0] * design_value); + normals->lower_add(atvinv_station + 1, design_station + col, + atvinv_col[1] * design_value); + normals->lower_add(atvinv_station + 2, design_station + col, + atvinv_col[2] * design_value); + } +} + +inline void add_normal_3x3_from_atvinv_columns(UINT32 design_row, UINT32 atvinv_station, UINT32 normal_station, + double scale, matrix_2d* normals, const matrix_2d* AtVinv) +{ + for (UINT32 col = 0; col < 3; ++col) + { + const double* atvinv_col = AtVinv->dense_ptr(atvinv_station, design_row + col); + normals->lower_add(atvinv_station, normal_station + col, + scale * atvinv_col[0]); + normals->lower_add(atvinv_station + 1, normal_station + col, + scale * atvinv_col[1]); + normals->lower_add(atvinv_station + 2, normal_station + col, + scale * atvinv_col[2]); + } +} + +} // namespace + void dna_adjust::AddMsrtoNormalsVar(const UINT32& design_row, const UINT32& stn, matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv) { // Add weighted measurement contributions to normal matrix - for (UINT32 row, col(0); col<3; ++col) - { - for (row=0; row<3; ++row) - { - normals->elementadd(stn+row, stn+col, - AtVinv->get(stn+row, design_row) * design->get(design_row, stn+col)); - } - } + add_normal_3x3_from_design_row(design_row, stn, stn, normals, design, AtVinv); } @@ -1405,50 +1504,20 @@ void dna_adjust::AddMsrtoNormalsCoVar2(const UINT32& design_row, const UINT32& s matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv) { // Add covariance terms (station 1 and station 2) to normal matrix - for (UINT32 row, col(0); col<3; ++col) - { - for (row=0; row<3; ++row) - { - // 1-2 - normals->elementadd(stn1+row, stn2+col, - AtVinv->get(stn1+row, design_row) * design->get(design_row, stn2+col)); - - normals->elementadd(stn2+row, stn1+col, - AtVinv->get(stn2+row, design_row) * design->get(design_row, stn1+col)); - } - } + add_normal_3x3_from_design_row(design_row, stn1, stn2, normals, design, AtVinv); + add_normal_3x3_from_design_row(design_row, stn2, stn1, normals, design, AtVinv); } void dna_adjust::AddMsrtoNormalsCoVar3(const UINT32& design_row, const UINT32& stn1, const UINT32& stn2, const UINT32& stn3, matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv) { // Add covariance terms (station 1, station 2, station 3) to normal matrix - for (UINT32 row, col(0); col<3; ++col) - { - for (row=0; row<3; ++row) - { - // 1-2 - normals->elementadd(stn1+row, stn2+col, - AtVinv->get(stn1+row, design_row) * design->get(design_row, stn2+col)); - - normals->elementadd(stn2+row, stn1+col, - AtVinv->get(stn2+row, design_row) * design->get(design_row, stn1+col)); - - // 1-3 - normals->elementadd(stn1+row, stn3+col, - AtVinv->get(stn1+row, design_row) * design->get(design_row, stn3+col)); - - normals->elementadd(stn3+row, stn1+col, - AtVinv->get(stn3+row, design_row) * design->get(design_row, stn1+col)); - - // 2-3 - normals->elementadd(stn2+row, stn3+col, - AtVinv->get(stn2+row, design_row) * design->get(design_row, stn3+col)); - - normals->elementadd(stn3+row, stn2+col, - AtVinv->get(stn3+row, design_row) * design->get(design_row, stn2+col)); - } - } + add_normal_3x3_from_design_row(design_row, stn1, stn2, normals, design, AtVinv); + add_normal_3x3_from_design_row(design_row, stn2, stn1, normals, design, AtVinv); + add_normal_3x3_from_design_row(design_row, stn1, stn3, normals, design, AtVinv); + add_normal_3x3_from_design_row(design_row, stn3, stn1, normals, design, AtVinv); + add_normal_3x3_from_design_row(design_row, stn2, stn3, normals, design, AtVinv); + add_normal_3x3_from_design_row(design_row, stn3, stn2, normals, design, AtVinv); } @@ -1471,7 +1540,7 @@ void dna_adjust::UpdateNormals_A(const UINT32& stn1, const UINT32& stn2, const U void dna_adjust::UpdateNormals_D(const UINT32& block, it_vmsr_t& _it_msr, UINT32& design_row, matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv) { - UINT32 row, col, a, angle_count(_it_msr->vectorCount2 - 1); + UINT32 a, angle_count(_it_msr->vectorCount2 - 1); UINT32 skip(0), ignored(_it_msr->vectorCount1 - _it_msr->vectorCount2); std::vector stations; @@ -1522,22 +1591,11 @@ void dna_adjust::UpdateNormals_D(const UINT32& block, it_vmsr_t& _it_msr, UINT32 } // station 1 - for (col=0; col<3; ++col) - for (row=0; row<3; ++row) - normals->elementadd(stn1+row, stn1+col, - AtVinv->get(stn1+row, design_row+a) * design->get(design_row+a, stn1+col)); - // + add_normal_3x3_from_design_row(design_row+a, stn1, stn1, normals, design, AtVinv); // station 2 - for (col=0; col<3; ++col) - for (row=0; row<3; ++row) - normals->elementadd(stn2+row, stn2+col, - AtVinv->get(stn2+row, design_row+a) * design->get(design_row+a, stn2+col)); - // + add_normal_3x3_from_design_row(design_row+a, stn2, stn2, normals, design, AtVinv); // station 3 - for (col=0; col<3; ++col) - for (row=0; row<3; ++row) - normals->elementadd(stn3+row, stn3+col, - AtVinv->get(stn3+row, design_row+a) * design->get(design_row+a, stn3+col)); + add_normal_3x3_from_design_row(design_row+a, stn3, stn3, normals, design, AtVinv); if (a+1 == angle_count) break; @@ -1568,17 +1626,8 @@ void dna_adjust::UpdateNormals_D(const UINT32& block, it_vmsr_t& _it_msr, UINT32 if (stn2 == stn1) continue; - for (col=0; col<3; ++col) - { - for (row=0; row<3; ++row) - { - // 1-2 - normals->elementadd(stn1+row, stn2+col, - AtVinv->get(stn1+row, design_row+a) * design->get(design_row+a, stn2+col)); - normals->elementadd(stn2+row, stn1+col, - AtVinv->get(stn2+row, design_row+a) * design->get(design_row+a, stn1+col)); - } - } + add_normal_3x3_from_design_row(design_row+a, stn1, stn2, normals, design, AtVinv); + add_normal_3x3_from_design_row(design_row+a, stn2, stn1, normals, design, AtVinv); } } it_angle++; @@ -1615,39 +1664,21 @@ void dna_adjust::UpdateNormals_HIJPQR(const UINT32& stn1, UINT32& design_row, void dna_adjust::UpdateNormals_G(const UINT32& stn1, const UINT32& stn2, UINT32& design_row, matrix_2d* normals, matrix_2d* AtVinv) { - UINT32 col, row; - // station 2 - for (col=0; col<3; ++col) - for (row=0; row<3; ++row) - normals->elementadd(stn2+row, stn2+col, - // AtVinv->get(stn2+row, design_row+col) * design->get(design_row+col, stn2+col)); - // No need to multiply by 1 as stn2 design element is always 1. See UpdateDesignMeasMatrices_GX() - AtVinv->get(stn2+row, design_row+col)); + // No need to multiply by 1 as stn2 design element is always 1. See UpdateDesignMeasMatrices_GX() + add_normal_3x3_from_atvinv_columns(design_row, stn2, stn2, 1., normals, AtVinv); // station 1 - for (col=0; col<3; ++col) - for (row=0; row<3; ++row) - normals->elementadd(stn1+row, stn1+col, - // AtVinv->get(stn1+row, design_row+col) * design->get(design_row+col, stn1+col)); - // No need to multiply by -1 as stn1 design element is always -1. See UpdateDesignMeasMatrices_GX() - -AtVinv->get(stn1+row, design_row+col)); + // No need to multiply by -1 as stn1 design element is always -1. See UpdateDesignMeasMatrices_GX() + add_normal_3x3_from_atvinv_columns(design_row, stn1, stn1, -1., normals, AtVinv); // covariance terms (station 1 and station 2) - for (col=0; col<3; ++col) - for (row=0; row<3; ++row) - normals->elementadd(stn1+row, stn2+col, - // AtVinv->get(stn1+row, design_row+col) * design->get(design_row+col, stn2+col)); - // No need to multiply as stn2 is always 1. See UpdateDesignMeasMatrices_GX() - AtVinv->get(stn1+row, design_row+col)); + // No need to multiply as stn2 is always 1. See UpdateDesignMeasMatrices_GX() + add_normal_3x3_from_atvinv_columns(design_row, stn1, stn2, 1., normals, AtVinv); // covariance terms (station 2 and station 1) - for (col=0; col<3; ++col) - for (row=0; row<3; ++row) - normals->elementadd(stn2+row, stn1+col, - // AtVinv->get(stn2+row, design_row+col) * design->get(design_row+col, stn1+col)); - // No need to multiply by -1 as stn1 design element is always -1. See UpdateDesignMeasMatrices_GX() - -AtVinv->get(stn2+row, design_row+col)); + // No need to multiply by -1 as stn1 design element is always -1. See UpdateDesignMeasMatrices_GX() + add_normal_3x3_from_atvinv_columns(design_row, stn2, stn1, -1., normals, AtVinv); design_row += 3; } @@ -2112,6 +2143,7 @@ _ADJUST_STATUS_ dna_adjust::AdjustNetwork() isIterationComplete_ = false; isAdjustmentQuestionable_ = false; iterationCorrections_.clear_messages(); + iterationTimes_.clear_messages(); if (projectSettings_.o._database_ids) LoadDatabaseId(); @@ -2171,8 +2203,7 @@ _ADJUST_STATUS_ dna_adjust::AdjustNetwork() void dna_adjust::PrintAdjustedNetworkStations() { - networkadjust::DynAdjustPrinter printer(*this); - printer.PrintAdjustedNetworkStations(); + printer_->PrintAdjustedNetworkStations(); } // First item in the file is a UINT32 value - the number of records in the file @@ -2383,7 +2414,12 @@ void dna_adjust::AdjustSimultaneous() { adjustStatus_ = ADJUST_SUCCESS; initialiseIteration(); - + + // Reset oscillation diagnostics + corrPrev_.clear(); + stnOscCount_.clear(); + oscHistory_.clear(); + std::ostringstream ss; std::string corr_msg; std::chrono::milliseconds elapsed_time(std::chrono::milliseconds(0)); @@ -2429,9 +2465,11 @@ void dna_adjust::AdjustSimultaneous() // Compute and print largest correction maxCorr_ = v_corrections_.at(0).compute_maximum_value(); OutputLargestCorrection(corr_msg); + UpdateIterationDiagnostics(); // update data for messages iterationCorrections_.add_message(corr_msg); + iterationTimes_.add_message(FormatElapsedTime(it_time.elapsed().wall.count() / 1.0e9)); iterationQueue_.push_and_notify(CurrentIteration()); // currentIteration begins at 1, so not zero-indexed isIterationComplete_ = true; @@ -2489,7 +2527,7 @@ void dna_adjust::ValidateandFinaliseAdjustment(cpu_timer& tot_time) fabs(maxCorr_) > projectSettings_.a.iteration_threshold) adjustStatus_ = ADJUST_MAX_ITERATIONS_EXCEEDED; - // Back up simultaneous rigorous variance estimates (for serialising + // Back up simultaneous rigorous variance estimates (for serialising // to disk at SerialiseAdjustedVarianceMatrices() ), so that executing adjust // in report-results mode has access to the latest variance estimates switch (projectSettings_.a.adjust_mode) @@ -2507,6 +2545,7 @@ void dna_adjust::ValidateandFinaliseAdjustment(cpu_timer& tot_time) printer_->PrintAdjustmentStatus(); // Compute and print time taken to run adjustment PrintAdjustmentTime(tot_time, total_time); + PrintPerformanceProfile(); } void dna_adjust::PrintAdjustmentTime(cpu_timer& time, _TIMER_TYPE_ timerType) @@ -2520,17 +2559,39 @@ void dna_adjust::PrintAdjustmentTime(cpu_timer& time, _TIMER_TYPE_ timerType) printer_->PrintAdjustmentTime(time, static_cast(timerType)); } +void dna_adjust::PrintPerformanceProfile() const +{ + if (!profileTimings_) + return; + + const auto ns_to_ms = [](uint64_t ns) { + return static_cast(ns) / 1000000.0; + }; + + std::cerr << "DynAdjust profile timings:" + << " update_normals=" << std::fixed << std::setprecision(3) + << ns_to_ms(profileUpdateNormalsNs_.load(std::memory_order_relaxed)) << "ms" + << " stage_load=" << ns_to_ms(profileStageLoadNs_.load(std::memory_order_relaxed)) << "ms" + << " stage_store=" << ns_to_ms(profileStageStoreNs_.load(std::memory_order_relaxed)) << "ms" + << std::endl; +} + void dna_adjust::AdjustPhased() { initialiseIteration(); + // Reset oscillation diagnostics + corrPrev_.clear(); + stnOscCount_.clear(); + oscHistory_.clear(); + std::string corr_msg; std::ostringstream ss; UINT32 i; bool iterate(true); cpu_timer it_time, tot_time; - + // do until convergence criteria is met for (i=0; iPrintIteration(incrementIteration()); - + it_time.start(); AdjustPhasedForward(); @@ -2567,8 +2628,10 @@ void dna_adjust::AdjustPhased() // Calculate and print largest adjustment correction and station ID OutputLargestCorrection(corr_msg); - + UpdateIterationDiagnostics(); + iterationCorrections_.add_message(corr_msg); + iterationTimes_.add_message(FormatElapsedTime(it_time.elapsed().wall.count() / 1.0e9)); iterationQueue_.push_and_notify(CurrentIteration()); // currentIteration begins at 1, so not zero-indexed isIterationComplete_ = true; @@ -2581,7 +2644,7 @@ void dna_adjust::AdjustPhased() // Similar to PrepareAdjustment, UpdateAdjustment prepares every block // in the network so that forward and reverse adjustments can commence // at the same time. - UpdateAdjustment(iterate); + UpdateAdjustment(iterate); if (IsCancelled()) break; @@ -2590,7 +2653,7 @@ void dna_adjust::AdjustPhased() { // Compute network statistics ComputeStatisticsOnIteration(); - + // Print statistics summary to adj file printer_->PrintStatistics(false); } @@ -2645,8 +2708,9 @@ void dna_adjust::AdjustPhasedBlock1() if (fabs(maxCorr_) > projectSettings_.a.iteration_threshold) adjustStatus_ = ADJUST_THRESHOLD_EXCEEDED; - + iterationCorrections_.add_message(corr_msg); + iterationTimes_.add_message(FormatElapsedTime(it_time.elapsed().wall.count() / 1.0e9)); iterationQueue_.push_and_notify(CurrentIteration()); // currentIteration begins at 1, so not zero-indexed ValidateandFinaliseAdjustment(tot_time); @@ -2695,9 +2759,10 @@ void dna_adjust::AdjustPhasedForward() UINT32 currentBlock(0); - // For staged adjustments, load the first block from mapped memory file + // For staged adjustments, prefetch and load the first block from mapped memory file if (projectSettings_.a.stage) { + AdviseBlockWillNeed(currentBlock); DeserialiseBlockFromMappedFile(currentBlock); RebuildNormals(currentBlock, __forward__, true, true); if (IsCancelled()) @@ -2735,16 +2800,21 @@ void dna_adjust::AdjustPhasedForward() // At this point, whether first iteration or not, if currentBlock is the first block, // the normals will have been initialised. For all later blocks, the normals will contain // the contribution of junction station coordinates and variances from preceding blocks. - // In either case, the block is ready for adjustment. Junction station coordinates and + // In either case, the block is ready for adjustment. Junction station coordinates and // variances are carried forward below + assert(v_design_.at(currentBlock).getbuffer() != nullptr && "AdjFwd: design null before SolveTry"); + assert(v_AtVinv_.at(currentBlock).getbuffer() != nullptr && "AdjFwd: AtVinv null before SolveTry"); + assert(v_normals_.at(currentBlock).getbuffer() != nullptr && "AdjFwd: normals null before SolveTry"); + assert(v_measMinusComp_.at(currentBlock).getbuffer() != nullptr && "AdjFwd: measMinusComp null before SolveTry"); + // Least Squares Solution SolveTry(true, currentBlock); // Does the user want to print adjusted measurements // on each iteration? - if (projectSettings_.o._adj_msr_iteration || - projectSettings_.o._adj_stn_iteration || + if (projectSettings_.o._adj_msr_iteration || + projectSettings_.o._adj_stn_iteration || projectSettings_.o._cmp_msr_iteration) adj_file << " done." << std::endl; @@ -2766,21 +2836,21 @@ void dna_adjust::AdjustPhasedForward() // OK, now shrink matrices back to normal size ShrinkForwardMatrices(currentBlock); - // Carry the estimated junction station coordinates and variances + // Carry the estimated junction station coordinates and variances // to the next block for applicable blocks only. For staged - // Deserialise the next block from mapped files and rebuild + // Deserialise the next block from mapped files and rebuild // normals CarryForwardJunctions(currentBlock, currentBlock+1); // For staged adjustments, write to disk and unload matrix data if (projectSettings_.a.stage) - // Don't offload last block since the reverse adjustment + // Don't offload last block since the reverse adjustment // will need this block if (currentBlock < (blockCount_ - 1)) OffloadBlockToMappedFile(currentBlock); } } - + void dna_adjust::PurgeMatricesFromDisk() { @@ -2885,8 +2955,7 @@ void dna_adjust::PrepareAdjustmentBlock(const UINT32 block, const UINT32 thread_ v_normalsR_.at(block) = v_normals_.at(block); if (projectSettings_.a.multi_thread) { - v_normalsRC_.at(block).redim( - v_normalsR_.at(block).rows(), v_normalsR_.at(block).columns()); + v_normalsRC_.at(block).redim_packed(v_normalsR_.at(block).rows()); } switch (projectSettings_.a.adjust_mode) @@ -2940,6 +3009,8 @@ void dna_adjust::ShrinkForwardMatrices(const UINT32 currentBlock) { pseudoMsrElemCount = static_cast(v_JSL_.at(currentBlock-1).size() * 3); + assert(v_measMinusComp_.at(currentBlock).getbuffer() != nullptr && "ShrinkFwd: measMinusComp null"); + assert(v_AtVinv_.at(currentBlock).getbuffer() != nullptr && "ShrinkFwd: AtVinv null"); v_measMinusComp_.at(currentBlock).shrink(pseudoMsrElemCount, 0); v_AtVinv_.at(currentBlock).shrink(0, pseudoMsrElemCount); } @@ -2974,7 +3045,9 @@ void dna_adjust::UpdateEstimatesForward(const UINT32 currentBlock) // Now copy 'estimated' coordinates to 'rigorous' for comparison on the next iteration v_rigorousStations_.at(currentBlock) = v_estimatedStations_.at(currentBlock); + assert(v_normals_.at(currentBlock).getbuffer() != nullptr && "UpdateEstFwd: normals null before copy to rigorousVariances"); v_rigorousVariances_.at(currentBlock) = v_normals_.at(currentBlock); + assert(v_rigorousVariances_.at(currentBlock).getbuffer() != nullptr && "UpdateEstFwd: rigorousVariances null after copy"); // Temporarily hold corrections for last block. These corrections will be restored // in UpdateEstimatesFinal() @@ -3024,9 +3097,10 @@ void dna_adjust::CarryForwardJunctions(const UINT32 thisBlock, const UINT32 next if (v_blockMeta_.at(nextBlock)._blockIsolated) return; - // For staged adjustments, load block info for nextBlock + // For staged adjustments, prefetch and load block info for nextBlock if (projectSettings_.a.stage) { + AdviseBlockWillNeed(nextBlock); DeserialiseBlockFromMappedFile(nextBlock); RebuildNormals(nextBlock, __forward__, true, true); } @@ -3061,7 +3135,7 @@ bool dna_adjust::PrepareAdjustmentReverse(const UINT32 currentBlock, bool MT_Rev // OK. currentBlock is the last block, and this is the commencement of // a reverse run, so reset coordinates - matrix_2d* estimatedStations(&v_estimatedStations_.at(currentBlock)); + matrix_2d* estimatedStations(&v_estimatedStations_.at(currentBlock)); if (MT_ReverseOrCombine) { @@ -3069,15 +3143,23 @@ bool dna_adjust::PrepareAdjustmentReverse(const UINT32 currentBlock, bool MT_Rev } else { - // Restore back up copy of normals for reverse adjustment in + // Restore back up copy of normals for reverse adjustment in // single thread mode + assert(v_normalsR_.at(currentBlock).getbuffer() != nullptr && "PrepareAdjReverse: normalsR null before copy"); + assert(v_normalsR_.at(currentBlock).rows() > 0 && "PrepareAdjReverse: normalsR has 0 rows"); v_normals_.at(currentBlock) = v_normalsR_.at(currentBlock); + assert(v_normals_.at(currentBlock).getbuffer() != nullptr && "PrepareAdjReverse: normals null after copy"); } + assert(v_originalStations_.at(currentBlock).getbuffer() != nullptr && "PrepareAdjReverse: originalStations null"); // Reset coordinates to original values *estimatedStations = v_originalStations_.at(currentBlock); - + + assert(v_design_.at(currentBlock).rows() > 0 && "PrepareAdjReverse: design has 0 rows"); + assert(v_AtVinv_.at(currentBlock).rows() > 0 && "PrepareAdjReverse: AtVinv has 0 rows"); + assert(v_measMinusComp_.at(currentBlock).rows() > 0 && "PrepareAdjReverse: measMinusComp has 0 rows"); + AddConstraintStationstoNormalsReverse(currentBlock, MT_ReverseOrCombine); return true; @@ -3417,6 +3499,11 @@ void dna_adjust::AdjustPhasedReverseCombine() if (projectSettings_.g.verbose > 3) debug_file << "In isolation" << std::endl; + assert(v_design_.at(currentBlock).getbuffer() != nullptr && "design null before SolveTry (reverse combine)"); + assert(v_AtVinv_.at(currentBlock).getbuffer() != nullptr && "AtVinv null before SolveTry (reverse combine)"); + assert(v_normals_.at(currentBlock).getbuffer() != nullptr && "normals null before SolveTry (reverse combine)"); + assert(v_measMinusComp_.at(currentBlock).getbuffer() != nullptr && "measMinusComp null before SolveTry (reverse combine)"); + // Backup normals prior to inversion for re-use in combination // adjustment... only if a combination is required BackupNormals(currentBlock, false); @@ -3429,7 +3516,7 @@ void dna_adjust::AdjustPhasedReverseCombine() // Add corrections to estimates. // If --output-adj-iter-stat argument is supplied or currentBlock - // is the first block, then compute geographic coordinates, recompute + // is the first block, then compute geographic coordinates, recompute // meas-minus-computed vector, then print statistics. // Output of stats also prints largest correction. // If --output-adj-iter-stn argument is supplied, print station coords @@ -3437,16 +3524,16 @@ void dna_adjust::AdjustPhasedReverseCombine() // Debug and diagnose (if required) debug_BlockInformation(currentBlock, "(reverse, in isolation)"); - + // Carry the estimated junction station coordinates and variances to the next block and // combine. CarryReverseEstimates will return false if currentBlock is the first block - // or an isolated block. If estimates can be carried, CarryReverseEstimates updates + // or an isolated block. If estimates can be carried, CarryReverseEstimates updates // geographic coords (if req'd), recomputes meas-minus-comp vector and updates normals for // the next block. if (CarryReverseJunctions(currentBlock, currentBlock-1, false)) - { + { ////////////////////////////////////////////////////////////////////////// - // Combination Adjustment + // Combination Adjustment // // Perform combination on all blocks except the first and last as they will // be rigorous from reverse and forward adjustments respectively @@ -3454,13 +3541,13 @@ void dna_adjust::AdjustPhasedReverseCombine() // First, carry the junction station estimates and variances of the next block // (obtained during the forward pass). These were copied to v_junctionEstimatesFwd_ // and v_junctionVariances_ in CarryStnEstimatesandVariancesForward(..) during - // the forward pass. + // the forward pass. if (PrepareAdjustmentCombine(currentBlock, pseudomsrJSLCount, false)) { isCombining_ = true; if (projectSettings_.g.verbose > 3) debug_file << "Rigorous" << std::endl; - + if (projectSettings_.o._adj_stn_iteration) if (!v_blockMeta_.at(currentBlock)._blockFirst) adj_file << std::endl << std::left << "Adjusting block " << currentBlock+1 << " (reverse, rigorous)... "; @@ -3501,7 +3588,7 @@ void dna_adjust::AdjustPhasedReverseCombine() } // for (UINT32 block=0; blockgetbuffer() != nullptr && "UpdateEstFinal: AtVinv null"); + assert(measMinusComp->getbuffer() != nullptr && "UpdateEstFinal: measMinusComp null"); + assert(aposterioriVariances->getbuffer() != nullptr && "UpdateEstFinal: aposterioriVariances null"); if (v_blockMeta_.at(currentBlock)._blockFirst) { @@ -3758,13 +3852,16 @@ bool dna_adjust::CarryReverseJunctions(const UINT32 currentBlock, const UINT32 n v_normals_.at(nextBlock) = v_normalsR_.at(nextBlock); } - // For staged adjustments, load blocks info for nextBlock + // For staged adjustments, prefetch and load blocks info for nextBlock if (projectSettings_.a.stage) + { + AdviseBlockWillNeed(nextBlock); DeserialiseBlockFromMappedFile(nextBlock); + } // Next, update estimates for next block to original estimates *estimatedStationsNext = v_originalStations_.at(nextBlock); - + // For staged adjustments, rebuild design and AtVinv if (projectSettings_.a.stage) RebuildNormals(nextBlock, __reverse__, false, false); @@ -3788,25 +3885,25 @@ bool dna_adjust::CarryReverseJunctions(const UINT32 currentBlock, const UINT32 n // go through each of the measurements in the binary measurements file and formulate partial derivatives -void dna_adjust::FillDesignNormalMeasurementsMatrices(bool buildnewMatrices, const UINT32& block, bool MT_ReverseOrCombine) +void dna_adjust::FillDesignNormalMeasurementsMatrices(bool buildnewMatrices, const UINT32& block, bool MT_ReverseOrCombine, bool skipNormals) { UINT32 design_row(0); - + it_vUINT32 _it_block_msr; - it_vmsr_t _it_msr; + it_vmsr_t _it_msr; for (_it_block_msr=v_CML_.at(block).begin(); _it_block_msr!=v_CML_.at(block).end(); ++_it_block_msr) { if (InitialiseandValidateMsrPointer(_it_block_msr, _it_msr)) continue; - // When a target direction is found, continue to next element. + // When a target direction is found, continue to next element. if (_it_msr->measType == 'D') if (_it_msr->vectorCount2 < 1) continue; // Build AtVinv, Normals and Meas minus Comp vectors - UpdateDesignNormalMeasMatrices(&_it_msr, design_row, buildnewMatrices, block, MT_ReverseOrCombine); + UpdateDesignNormalMeasMatrices(&_it_msr, design_row, buildnewMatrices, block, MT_ReverseOrCombine, skipNormals); } } @@ -3815,6 +3912,9 @@ void dna_adjust::FillDesignNormalMeasurementsMatrices(bool buildnewMatrices, con // pre-adjustment value, otherwise back up the current value. bool dna_adjust::InitialiseMeasurement(pit_vmsr_t _it_msr, bool buildnewMatrices) { + if (rebuildingDesign_) + return false; + if (buildnewMatrices) { // Was this measurement reduced from previous @@ -3835,7 +3935,7 @@ bool dna_adjust::InitialiseMeasurement(pit_vmsr_t _it_msr, bool buildnewMatrices } -void dna_adjust::UpdateDesignNormalMeasMatrices(pit_vmsr_t _it_msr, UINT32& design_row, bool buildnewMatrices, const UINT32& block, bool MT_ReverseOrCombine) +void dna_adjust::UpdateDesignNormalMeasMatrices(pit_vmsr_t _it_msr, UINT32& design_row, bool buildnewMatrices, const UINT32& block, bool MT_ReverseOrCombine, bool skipNormals) { std::stringstream ss; @@ -3851,101 +3951,101 @@ void dna_adjust::UpdateDesignNormalMeasMatrices(pit_vmsr_t _it_msr, UINT32& desi design = &v_designR_.at(block); AtVinv = &v_AtVinvR_.at(block); measMinusComp = &v_measMinusCompR_.at(block); - } + } switch ((*_it_msr)->measType) { case 'A': // Horizontal angle UpdateDesignNormalMeasMatrices_A(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); break; case 'B': // Geodetic azimuth case 'K': // Astronomic azimuth - // Note: UpdateDesignNormalMeasMatrices_BK reduces K measurements to the geodetic reference frame, + // Note: UpdateDesignNormalMeasMatrices_BK reduces K measurements to the geodetic reference frame, // after which UpdateDesignNormalMeasMatrices_BK treats K measurements as B measurements upon forming // design matrix elements. UpdateDesignNormalMeasMatrices_BK(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); break; case 'C': // Chord dist UpdateDesignNormalMeasMatrices_C(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); break; - case 'D': // Direction set + case 'D': // Direction set UpdateDesignNormalMeasMatrices_D(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); break; case 'E': // Ellipsoid arc UpdateDesignNormalMeasMatrices_E(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); break; case 'G': // GPS Baseline UpdateDesignNormalMeasMatrices_G(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); break; case 'H': // Orthometric height // Note: UpdateDesignNormalMeasMatrices_H reduces term1 to ellipsoid height, after which // UpdateDesignNormalMeasMatrices_HR is used to form design elements. UpdateDesignNormalMeasMatrices_H(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); break; case 'I': // Astronomic latitude // Note: UpdateDesignNormalMeasMatrices_I reduces term1 to geodetic latitude, after which // UpdateDesignNormalMeasMatrices_IP is used to form design elements. UpdateDesignNormalMeasMatrices_I(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); break; case 'J': // Astronomic longitude // Note: UpdateDesignNormalMeasMatrices_J reduces term1 to geodetic longitude, after which // UpdateDesignNormalMeasMatrices_JQ is used to form design elements. UpdateDesignNormalMeasMatrices_J(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); - break; + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); + break; case 'L': // Level difference UpdateDesignNormalMeasMatrices_L(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); - break; + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); + break; case 'M': // MSL arc UpdateDesignNormalMeasMatrices_M(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); - break; + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); + break; case 'P': // Geodetic latitude // Note: UpdateDesignNormalMeasMatrices_P archives the raw measurement, after which // UpdateDesignNormalMeasMatrices_IP is used to form design elements. UpdateDesignNormalMeasMatrices_P(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); - break; + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); + break; case 'Q': // Geodetic longitude // Note: UpdateDesignNormalMeasMatrices_Q archives the raw measurement, after which // UpdateDesignNormalMeasMatrices_JQ is used to form design elements. UpdateDesignNormalMeasMatrices_Q(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); - break; + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); + break; case 'R': // Ellipsoidal height // Note: UpdateDesignNormalMeasMatrices_R archives the raw measurement, after which // UpdateDesignNormalMeasMatrices_HR is used to form design elements. UpdateDesignNormalMeasMatrices_R(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); break; case 'S': // Slope distance UpdateDesignNormalMeasMatrices_S(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); break; case 'V': // Zenith distance UpdateDesignNormalMeasMatrices_V(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); - break; + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); + break; case 'X': // GPS Baseline cluster UpdateDesignNormalMeasMatrices_X(_it_msr, design_row, block, - measMinusComp, estimatedStations, design, AtVinv, buildnewMatrices); - break; + measMinusComp, estimatedStations, design, AtVinv, buildnewMatrices, skipNormals); + break; case 'Y': // GPS Point cluster UpdateDesignNormalMeasMatrices_Y(_it_msr, design_row, block, - measMinusComp, estimatedStations, design, AtVinv, buildnewMatrices); - break; + measMinusComp, estimatedStations, design, AtVinv, buildnewMatrices, skipNormals); + break; case 'Z': // Vertical angle UpdateDesignNormalMeasMatrices_Z(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); break; default: ss << "UpdateDesignNormalMeasMatrices(): Unknown measurement type - '" << @@ -4652,8 +4752,8 @@ void dna_adjust::AddMsrtoMeasMinusComp(pit_vmsr_t _it_msr, const UINT32& design_ void dna_adjust::UpdateDesignNormalMeasMatrices_A(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, - matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices) + matrix_2d* measMinusComp, matrix_2d* estimatedStations, + matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, bool skipNormals) { UINT32 stn1(GetBlkMatrixElemStn1(block, _it_msr)); UINT32 stn2(GetBlkMatrixElemStn2(block, _it_msr)); @@ -4679,11 +4779,11 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_A(pit_vmsr_t _it_msr, UINT32& de &direction12, &direction13, &local_12e, &local_12n, &local_13e, &local_13n)); - // Initialise measurement. No need to test if no further calculations are + // Initialise measurement. No need to test if no further calculations are // required (as in stage mode), as this is done later (below) InitialiseMeasurement(_it_msr, buildnewMatrices); - if (buildnewMatrices) + if (buildnewMatrices && !rebuildingDesign_) { // deflections available? if (fabs(stn1_it->verticalDef) > E4_SEC_DEFLECTION || fabs(stn1_it->meridianDef) > E4_SEC_DEFLECTION) @@ -4802,7 +4902,7 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_A(pit_vmsr_t _it_msr, UINT32& de UpdateAtVinv(_it_msr, stn1, stn2, stn3, design_row, design, AtVinv, buildnewMatrices); - if (buildnewMatrices) + if (buildnewMatrices && !skipNormals) // Add weighted measurement contributions to normal matrix UpdateNormals_A(stn1, stn2, stn3, design_row, normals, design, AtVinv); else @@ -4811,8 +4911,8 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_A(pit_vmsr_t _it_msr, UINT32& de void dna_adjust::UpdateDesignNormalMeasMatrices_BK(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, - matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices) + matrix_2d* measMinusComp, matrix_2d* estimatedStations, + matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, bool skipNormals) { UINT32 stn1(GetBlkMatrixElemStn1(block, _it_msr)); UINT32 stn2(GetBlkMatrixElemStn2(block, _it_msr)); @@ -4834,11 +4934,11 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_BK(pit_vmsr_t _it_msr, UINT32& d stn1_it->currentLongitude, &local_12e, &local_12n)); - // Initialise measurement. No need to test if no further calculations are + // Initialise measurement. No need to test if no further calculations are // required (as in stage mode), as this is done later (below) InitialiseMeasurement(_it_msr, buildnewMatrices); - - if (buildnewMatrices) + + if (buildnewMatrices && !rebuildingDesign_) { // deflections available? if ((*_it_msr)->measType == 'K' && // Astro @@ -4906,19 +5006,19 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_BK(pit_vmsr_t _it_msr, UINT32& d // Update AtVinv based on new design matrix elements UpdateAtVinv(_it_msr, stn1, stn2, 0, design_row, design, AtVinv, buildnewMatrices); - if (buildnewMatrices) + if (buildnewMatrices && !skipNormals) // Add weighted measurement contributions to normal matrix UpdateNormals_BCEKLMSVZ(stn1, stn2, design_row, normals, design, AtVinv); else design_row++; } - + void dna_adjust::UpdateDesignNormalMeasMatrices_C(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, - matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices) + matrix_2d* measMinusComp, matrix_2d* estimatedStations, + matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, bool skipNormals) { - // Initialise measurement and test if no further calculations are + // Initialise measurement and test if no further calculations are // required (as in stage mode) if (InitialiseMeasurement(_it_msr, buildnewMatrices)) return; @@ -4929,13 +5029,13 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_C(pit_vmsr_t _it_msr, UINT32& de // Now call UpdateDesignNormalMeasMatrices_CEM UpdateDesignNormalMeasMatrices_CEM(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); } void dna_adjust::UpdateDesignNormalMeasMatrices_CEM(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, - matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices) + matrix_2d* measMinusComp, matrix_2d* estimatedStations, + matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, bool skipNormals) { UINT32 stn1(GetBlkMatrixElemStn1(block, _it_msr)); UINT32 stn2(GetBlkMatrixElemStn2(block, _it_msr)); @@ -4971,17 +5071,17 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_CEM(pit_vmsr_t _it_msr, UINT32& // Update AtVinv based on new design matrix elements UpdateAtVinv(_it_msr, stn1, stn2, 0, design_row, design, AtVinv, buildnewMatrices); - if (buildnewMatrices) + if (buildnewMatrices && !skipNormals) // Add weighted measurement contributions to normal matrix UpdateNormals_BCEKLMSVZ(stn1, stn2, design_row, normals, design, AtVinv); else design_row++; } - + void dna_adjust::UpdateDesignNormalMeasMatrices_D(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, - matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices) + matrix_2d* measMinusComp, matrix_2d* estimatedStations, + matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, bool skipNormals) { it_vmsr_t _it_msr_first(*_it_msr); UINT32 design_row_begin(design_row); @@ -5030,7 +5130,7 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_D(pit_vmsr_t _it_msr, UINT32& de it_angle->station3 = (*_it_msr)->station2; - if (buildnewMatrices) + if (buildnewMatrices && !rebuildingDesign_) { // Was this measurement reduced from previous // adjustment, which was serialised to disk? @@ -5063,7 +5163,7 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_D(pit_vmsr_t _it_msr, UINT32& de UpdateDesignNormalMeasMatrices_A(&it_angle, design_row, block, measMinusComp, estimatedStations, 0, design, AtVinv, buildnewMatrices); - if (buildnewMatrices) + if (buildnewMatrices && !rebuildingDesign_) { // Update derived angle, corrected for deflection of vertical (*_it_msr)->scale1 = it_angle->term1; @@ -5134,14 +5234,14 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_D(pit_vmsr_t _it_msr, UINT32& de it_angle++; } - if (buildnewMatrices) + if (buildnewMatrices && !skipNormals) // Add weighted measurement contributions to normal matrix UpdateNormals_D(block, _it_msr_first, design_row_begin, normals, design, AtVinv); } void dna_adjust::UpdateDesignNormalMeasMatrices_E(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, - matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices) + matrix_2d* measMinusComp, matrix_2d* estimatedStations, + matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, bool skipNormals) { // Initialise measurement and test if no further calculations are // required (as in stage mode) @@ -5177,7 +5277,7 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_E(pit_vmsr_t _it_msr, UINT32& de // Now that the ellipsoid arc has been reduced to a chord, call UpdateDesignNormalMeasMatrices_CEM UpdateDesignNormalMeasMatrices_CEM(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); } void dna_adjust::UpdateDesignMeasMatrices_GX(pit_vmsr_t _it_msr, UINT32& design_row, @@ -5251,8 +5351,8 @@ void dna_adjust::UpdateDesignMeasMatrices_GX(pit_vmsr_t _it_msr, UINT32& design_ } void dna_adjust::UpdateDesignNormalMeasMatrices_G(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, - matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices) + matrix_2d* measMinusComp, matrix_2d* estimatedStations, + matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, bool skipNormals) { it_vmsr_t _it_msr_first(*_it_msr); UINT32 stn1(GetBlkMatrixElemStn1(block, _it_msr)); @@ -5274,7 +5374,7 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_G(pit_vmsr_t _it_msr, UINT32& de // For first time run or staged adjustment mode - if (buildnewMatrices || projectSettings_.a.stage) + if (buildnewMatrices || projectSettings_.a.stage || skipNormals) { // Load GPS Variance matrix. For the first run, scaling is applied // and the variances are written to the binary measurements list. @@ -5283,23 +5383,23 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_G(pit_vmsr_t _it_msr, UINT32& de if (buildnewMatrices && projectSettings_.a.stage) return; - + // Build At * V-1 AtVinv->replace(stn1, design_row_begin, var_cart * -1); AtVinv->replace(stn2, design_row_begin, var_cart); - if (buildnewMatrices && !projectSettings_.a.stage) + if (buildnewMatrices && !projectSettings_.a.stage && !skipNormals) // Add weighted measurement contributions to normal matrix UpdateNormals_G(stn1, stn2, design_row_begin, normals, AtVinv); } - + design_row++; } - + void dna_adjust::UpdateDesignNormalMeasMatrices_M(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, - matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices) + matrix_2d* measMinusComp, matrix_2d* estimatedStations, + matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, bool skipNormals) { // Initialise measurement and test if no further calculations are // required (as in stage mode) @@ -5324,22 +5424,22 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_M(pit_vmsr_t _it_msr, UINT32& de // Now that the MSL arc has been reduced to a chord, call UpdateDesignNormalMeasMatrices_CEM UpdateDesignNormalMeasMatrices_CEM(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); } // Like zenith distances and vertical angles, the relationship between slope distances and the // coordinates of p1 and p2 requires a little extra work to take into consideration instrument // and target height. For the measurements-minus-computed vector, the "computed" distance -// is the true distance between the instrument and target, and so must take into consideration +// is the true distance between the instrument and target, and so must take into consideration // instrument and target heights. However, the dX, dY, dZ components for the partial // derivatives represent the true geometric difference between the two stations (not // instrument and target). void dna_adjust::UpdateDesignNormalMeasMatrices_S(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, - matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices) + matrix_2d* measMinusComp, matrix_2d* estimatedStations, + matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, bool skipNormals) { // preAdjMeas is used to store original measured MSL arc distance - if (buildnewMatrices) + if (buildnewMatrices && !rebuildingDesign_) { // This is the first time adjust has run, so // back up the raw measurement @@ -5349,7 +5449,7 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_S(pit_vmsr_t _it_msr, UINT32& de return; } - UINT32 stn1(GetBlkMatrixElemStn1(block, _it_msr)); + UINT32 stn1(GetBlkMatrixElemStn1(block, _it_msr)); UINT32 stn2(GetBlkMatrixElemStn2(block, _it_msr)); it_vstn_t_const stn1_it(bstBinaryRecords_.begin() + (*_it_msr)->station1); @@ -5357,7 +5457,7 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_S(pit_vmsr_t _it_msr, UINT32& de // compute dX, dY, dZ for instrument height (ih) and target height (th) double dXih, dYih, dZih, dXth, dYth, dZth; CartesianElementsFromInstrumentHeight((*_it_msr)->term3, // instrument height - &dXih, &dYih, &dZih, + &dXih, &dYih, &dZih, stn1_it->currentLatitude, stn1_it->currentLongitude); CartesianElementsFromInstrumentHeight((*_it_msr)->term4, // target height @@ -5385,7 +5485,7 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_S(pit_vmsr_t _it_msr, UINT32& de // Update AtVinv based on new design matrix elements UpdateAtVinv(_it_msr, stn1, stn2, 0, design_row, design, AtVinv, buildnewMatrices); - if (buildnewMatrices) + if (buildnewMatrices && !skipNormals) // Add weighted measurement contributions to normal matrix UpdateNormals_BCEKLMSVZ(stn1, stn2, design_row, normals, design, AtVinv); else @@ -5402,10 +5502,10 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_S(pit_vmsr_t _it_msr, UINT32& de // partial derivatives represent the true geometric difference between the two stations (not // instrument and target). void dna_adjust::UpdateDesignNormalMeasMatrices_V(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, - matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices) + matrix_2d* measMinusComp, matrix_2d* estimatedStations, + matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, bool skipNormals) { - UINT32 stn1(GetBlkMatrixElemStn1(block, _it_msr)); + UINT32 stn1(GetBlkMatrixElemStn1(block, _it_msr)); UINT32 stn2(GetBlkMatrixElemStn2(block, _it_msr)); it_vstn_t_const stn1_it(bstBinaryRecords_.begin() + (*_it_msr)->station1); @@ -5413,11 +5513,11 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_V(pit_vmsr_t _it_msr, UINT32& de double local_12e, local_12n, local_12up; - // Initialise measurement. No need to test if no further calculations are + // Initialise measurement. No need to test if no further calculations are // required (as in stage mode), as this is done later (below) InitialiseMeasurement(_it_msr, buildnewMatrices); - if (buildnewMatrices) + if (buildnewMatrices && !rebuildingDesign_) { // deflections available? if (fabs(stn1_it->verticalDef) > E4_SEC_DEFLECTION || fabs(stn1_it->meridianDef) > E4_SEC_DEFLECTION) @@ -5493,7 +5593,7 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_V(pit_vmsr_t _it_msr, UINT32& de // Update AtVinv based on new design matrix elements UpdateAtVinv(_it_msr, stn1, stn2, 0, design_row, design, AtVinv, buildnewMatrices); - if (buildnewMatrices) + if (buildnewMatrices && !skipNormals) // Add weighted measurement contributions to normal matrix UpdateNormals_BCEKLMSVZ(stn1, stn2, design_row, normals, design, AtVinv); else @@ -5506,15 +5606,15 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_V(pit_vmsr_t _it_msr, UINT32& de // Like zenith distances, the relationship between slope distances and the // coordinates of p1 and p2 requires a little extra work to take into consideration instrument // and target height. For the measurements-minus-computed vector, the "computed" distance -// is the true distance between the instrument and target, and so must take into consideration -// instrument and target heights. However, the dX, dY, dZ components for the partial -// derivatives represent the true geometric difference between the two stations (not +// is the true distance between the instrument and target, and so must take into consideration +// instrument and target heights. However, the dX, dY, dZ components for the partial +// derivatives represent the true geometric difference between the two stations (not // instrument and target). void dna_adjust::UpdateDesignNormalMeasMatrices_Z(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, - matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices) + matrix_2d* measMinusComp, matrix_2d* estimatedStations, + matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, bool skipNormals) { - UINT32 stn1(GetBlkMatrixElemStn1(block, _it_msr)); + UINT32 stn1(GetBlkMatrixElemStn1(block, _it_msr)); UINT32 stn2(GetBlkMatrixElemStn2(block, _it_msr)); it_vstn_t_const stn1_it(bstBinaryRecords_.begin() + (*_it_msr)->station1); @@ -5522,11 +5622,11 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_Z(pit_vmsr_t _it_msr, UINT32& de double local_12e, local_12n, local_12up; - // Initialise measurement. No need to test if no further calculations are + // Initialise measurement. No need to test if no further calculations are // required (as in stage mode), as this is done later (below) InitialiseMeasurement(_it_msr, buildnewMatrices); - if (buildnewMatrices) + if (buildnewMatrices && !rebuildingDesign_) { // deflections available? if (fabs(stn1_it->verticalDef) > E4_SEC_DEFLECTION || fabs(stn1_it->meridianDef) > E4_SEC_DEFLECTION) @@ -5602,21 +5702,21 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_Z(pit_vmsr_t _it_msr, UINT32& de // Update AtVinv based on new design matrix elements UpdateAtVinv(_it_msr, stn1, stn2, 0, design_row, design, AtVinv, buildnewMatrices); - if (buildnewMatrices) + if (buildnewMatrices && !skipNormals) // Add weighted measurement contributions to normal matrix UpdateNormals_BCEKLMSVZ(stn1, stn2, design_row, normals, design, AtVinv); else design_row++; } - + // Orthometric height difference. -// This function requires geoid-ellipsoid separation in order to reduce -// to ellipsoidal height difference counterpart. +// This function requires geoid-ellipsoid separation in order to reduce +// to ellipsoidal height difference counterpart. // Hence, run geoid with -f, -s and -n options void dna_adjust::UpdateDesignNormalMeasMatrices_L(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, - matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices) + matrix_2d* measMinusComp, matrix_2d* estimatedStations, + matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, bool skipNormals) { UINT32 stn1(GetBlkMatrixElemStn1(block, _it_msr)); UINT32 stn2(GetBlkMatrixElemStn2(block, _it_msr)); @@ -5641,11 +5741,11 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_L(pit_vmsr_t _it_msr, UINT32& de &h1, &h2, &nu1, &nu2, &Zn1, &Zn2, datum_.GetEllipsoidRef())); - // Initialise measurement. No need to test if no further calculations are + // Initialise measurement. No need to test if no further calculations are // required (as in stage mode), as this is done later (below) InitialiseMeasurement(_it_msr, buildnewMatrices); - if (buildnewMatrices) + if (buildnewMatrices && !rebuildingDesign_) { // N value available? if (fabs(stn1_it->geoidSep) > PRECISION_1E4 || @@ -5676,7 +5776,7 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_L(pit_vmsr_t _it_msr, UINT32& de // Update AtVinv based on new design matrix elements UpdateAtVinv(_it_msr, stn1, stn2, 0, design_row, design, AtVinv, buildnewMatrices); - if (buildnewMatrices) + if (buildnewMatrices && !skipNormals) // Add weighted measurement contributions to normal matrix UpdateNormals_BCEKLMSVZ(stn1, stn2, design_row, normals, design, AtVinv); else @@ -5684,17 +5784,17 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_L(pit_vmsr_t _it_msr, UINT32& de } void dna_adjust::UpdateDesignNormalMeasMatrices_I(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, - matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices) + matrix_2d* measMinusComp, matrix_2d* estimatedStations, + matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, bool skipNormals) { - // Initialise measurement. No need to test if no further calculations are + // Initialise measurement. No need to test if no further calculations are // required (as in stage mode), as this is done later (below) InitialiseMeasurement(_it_msr, buildnewMatrices); - if (buildnewMatrices) + if (buildnewMatrices && !rebuildingDesign_) { it_vstn_t_const stn1_it(bstBinaryRecords_.begin() + (*_it_msr)->station1); - + // deflections available? if (fabs(stn1_it->meridianDef) > E4_SEC_DEFLECTION) { @@ -5709,22 +5809,22 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_I(pit_vmsr_t _it_msr, UINT32& de } UpdateDesignNormalMeasMatrices_IP(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); } void dna_adjust::UpdateDesignNormalMeasMatrices_J(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, - matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices) + matrix_2d* measMinusComp, matrix_2d* estimatedStations, + matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, bool skipNormals) { - // Initialise measurement. No need to test if no further calculations are + // Initialise measurement. No need to test if no further calculations are // required (as in stage mode), as this is done later (below) InitialiseMeasurement(_it_msr, buildnewMatrices); - if (buildnewMatrices) + if (buildnewMatrices && !rebuildingDesign_) { it_vstn_t_const stn1_it(bstBinaryRecords_.begin() + (*_it_msr)->station1); - + // deflections available? if (fabs(stn1_it->verticalDef) > E4_SEC_DEFLECTION) { @@ -5740,27 +5840,27 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_J(pit_vmsr_t _it_msr, UINT32& de } UpdateDesignNormalMeasMatrices_JQ(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); } void dna_adjust::UpdateDesignNormalMeasMatrices_P(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, - matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices) + matrix_2d* measMinusComp, matrix_2d* estimatedStations, + matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, bool skipNormals) { - // Initialise measurement and test if no further calculations are + // Initialise measurement and test if no further calculations are // required (as in stage mode) if (InitialiseMeasurement(_it_msr, buildnewMatrices)) return; UpdateDesignNormalMeasMatrices_IP(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); } - + void dna_adjust::UpdateDesignNormalMeasMatrices_IP(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, - matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices) + matrix_2d* measMinusComp, matrix_2d* estimatedStations, + matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, bool skipNormals) { UINT32 stn1(GetBlkMatrixElemStn1(block, _it_msr)); @@ -5806,31 +5906,31 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_IP(pit_vmsr_t _it_msr, UINT32& d // Update AtVinv based on new design matrix elements UpdateAtVinv(_it_msr, stn1, 0, 0, design_row, design, AtVinv, buildnewMatrices); - if (buildnewMatrices) + if (buildnewMatrices && !skipNormals) // Add weighted measurement contributions to normal matrix UpdateNormals_HIJPQR(stn1, design_row, normals, design, AtVinv); else design_row++; } - + void dna_adjust::UpdateDesignNormalMeasMatrices_Q(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, - matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices) + matrix_2d* measMinusComp, matrix_2d* estimatedStations, + matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, bool skipNormals) { - // Initialise measurement and test if no further calculations are + // Initialise measurement and test if no further calculations are // required (as in stage mode) if (InitialiseMeasurement(_it_msr, buildnewMatrices)) return; UpdateDesignNormalMeasMatrices_JQ(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); } - + void dna_adjust::UpdateDesignNormalMeasMatrices_JQ(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, - matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices) + matrix_2d* measMinusComp, matrix_2d* estimatedStations, + matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, bool skipNormals) { UINT32 stn1(GetBlkMatrixElemStn1(block, _it_msr)); @@ -5858,26 +5958,26 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_JQ(pit_vmsr_t _it_msr, UINT32& d // Update AtVinv based on new design matrix elements UpdateAtVinv(_it_msr, stn1, 0, 0, design_row, design, AtVinv, buildnewMatrices); - if (buildnewMatrices) + if (buildnewMatrices && !skipNormals) // Add weighted measurement contributions to normal matrix UpdateNormals_HIJPQR(stn1, design_row, normals, design, AtVinv); else design_row++; } - + void dna_adjust::UpdateDesignNormalMeasMatrices_H(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, - matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices) + matrix_2d* measMinusComp, matrix_2d* estimatedStations, + matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, bool skipNormals) { - // Initialise measurement. No need to test if no further calculations are + // Initialise measurement. No need to test if no further calculations are // required (as in stage mode), as this is done later (below) InitialiseMeasurement(_it_msr, buildnewMatrices); - if (buildnewMatrices) + if (buildnewMatrices && !rebuildingDesign_) { it_vstn_t_const stn1_it(bstBinaryRecords_.begin() + (*_it_msr)->station1); - + // N value available? if (fabs(stn1_it->geoidSep) > PRECISION_1E4) { @@ -5891,13 +5991,13 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_H(pit_vmsr_t _it_msr, UINT32& de } UpdateDesignNormalMeasMatrices_HR(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); } void dna_adjust::UpdateDesignNormalMeasMatrices_HR(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, - matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices) + matrix_2d* measMinusComp, matrix_2d* estimatedStations, + matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, bool skipNormals) { UINT32 stn1(GetBlkMatrixElemStn1(block, _it_msr)); @@ -5931,31 +6031,31 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_HR(pit_vmsr_t _it_msr, UINT32& d // Update AtVinv based on new design matrix elements UpdateAtVinv(_it_msr, stn1, 0, 0, design_row, design, AtVinv, buildnewMatrices); - if (buildnewMatrices) + if (buildnewMatrices && !skipNormals) // Add weighted measurement contributions to normal matrix UpdateNormals_HIJPQR(stn1, design_row, normals, design, AtVinv); else design_row++; } - + void dna_adjust::UpdateDesignNormalMeasMatrices_R(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, - matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices) + matrix_2d* measMinusComp, matrix_2d* estimatedStations, + matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, bool skipNormals) { - // Initialise measurement and test if no further calculations are + // Initialise measurement and test if no further calculations are // required (as in stage mode) if (InitialiseMeasurement(_it_msr, buildnewMatrices)) return; UpdateDesignNormalMeasMatrices_HR(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); } - + void dna_adjust::UpdateDesignNormalMeasMatrices_X(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, - matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices) + matrix_2d* measMinusComp, matrix_2d* estimatedStations, + matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, bool skipNormals) { it_vmsr_t _it_msr_first(*_it_msr); @@ -5992,7 +6092,7 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_X(pit_vmsr_t _it_msr, UINT32& de matrix_2d var_cart(baseline_count * 3, baseline_count * 3); - if (buildnewMatrices || projectSettings_.a.stage) + if (buildnewMatrices || projectSettings_.a.stage || skipNormals) { // Load apriori variance matrix, and assign to binary measurement // If required, apply scalars @@ -6002,10 +6102,10 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_X(pit_vmsr_t _it_msr, UINT32& de return; } - // If this method is called via PrepareAdjustment() and the adjustment + // If this method is called via PrepareAdjustment() and the adjustment // mode is staged, then don't update the AtVinv matrix. This will be // done during an adjustment via AdjustPhasedForward(). - if (!buildnewMatrices && !projectSettings_.a.stage) + if (!buildnewMatrices && !projectSettings_.a.stage && !skipNormals) return; UINT32 cluster_cov; @@ -6064,11 +6164,11 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_X(pit_vmsr_t _it_msr, UINT32& de _it_msr_temp += 3; } - if (projectSettings_.a.stage || !buildnewMatrices) + if (projectSettings_.a.stage || !buildnewMatrices || skipNormals) return; - + _it_msr_temp = _it_msr_first; - + matrix_2d tmp0(3, 3); // Build At * V-1 * A variances @@ -6147,8 +6247,8 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_X(pit_vmsr_t _it_msr, UINT32& de void dna_adjust::UpdateDesignNormalMeasMatrices_Y(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, - matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices) + matrix_2d* measMinusComp, matrix_2d* estimatedStations, + matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, bool skipNormals) { it_vmsr_t _it_msr_first(*_it_msr); it_vmsr_t tmp_msr; @@ -6361,23 +6461,23 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_Y(pit_vmsr_t _it_msr, UINT32& de matrix_2d var_cart(point_count * 3, point_count * 3); - if (buildnewMatrices || projectSettings_.a.stage) + if (buildnewMatrices || projectSettings_.a.stage || skipNormals) { // Load apriori variance matrix, and assign to binary measurement - // If required, propagate to cartesian reference frame and apply + // If required, propagate to cartesian reference frame and apply // scalars LoadVarianceMatrix_Y(_it_msr_first, &var_cart, coordType); - + // If preparing for a stage adjustment, return // Normals will be built for each block as needed if (buildnewMatrices && projectSettings_.a.stage) return; } - - // If this method is called via PrepareAdjustment() and the adjustment + + // If this method is called via PrepareAdjustment() and the adjustment // mode is staged, then don't update the AtVinv matrix. This will be // done during an adjustment via AdjustPhasedForward(). - if (!buildnewMatrices && !projectSettings_.a.stage) + if (!buildnewMatrices && !projectSettings_.a.stage && !skipNormals) return; UINT32 cluster_cov; @@ -6420,9 +6520,9 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_Y(pit_vmsr_t _it_msr, UINT32& de design_row_begin += 3; } - if (projectSettings_.a.stage || !buildnewMatrices) + if (projectSettings_.a.stage || !buildnewMatrices || skipNormals) return; - + _it_msr_temp = _it_msr_first; // Add to At * V-1 * A @@ -6485,6 +6585,13 @@ void dna_adjust::SolveTry(bool COMPUTE_INVERSE, const UINT32& block) void dna_adjust::Solve(bool COMPUTE_INVERSE, const UINT32& block) { + assert(v_design_.at(block).rows() > 0 && "Solve: design has 0 rows"); + assert(v_design_.at(block).columns() > 0 && "Solve: design has 0 columns"); + assert(v_AtVinv_.at(block).rows() > 0 && "Solve: AtVinv has 0 rows"); + assert(v_AtVinv_.at(block).columns() > 0 && "Solve: AtVinv has 0 columns"); + assert(v_normals_.at(block).rows() > 0 && "Solve: normals has 0 rows"); + assert(v_measMinusComp_.at(block).rows() > 0 && "Solve: measMinusComp has 0 rows"); + // debug matrices if required debug_SolutionInformation(block); @@ -6504,33 +6611,24 @@ void dna_adjust::Solve(bool COMPUTE_INVERSE, const UINT32& block) // the normal matrix before inversion and subsequently reversing the effect. // - // 1. Create scalar matrix - matrix_2d *S = nullptr, *SN = nullptr; + // 1. Scale normals to unity using diagonal scaling + UINT32 n = v_normals_.at(block).rows(); + std::vector s_diag; if (projectSettings_.a.scale_normals_to_unity) { - S = new matrix_2d(v_normals_.at(block).rows(), v_normals_.at(block).rows()); - SN = new matrix_2d(v_normals_.at(block).rows(), v_normals_.at(block).rows()); - for (UINT32 i(0); iput(i, i, sqrt(v_normals_.at(block).get(i, i))); - // 2. Scale Normals to reduce the diagonal elements of Normals to unity - //SN->multiply(*S, v_normals_.at(block)); - SN->multiply(*S, "N", v_normals_.at(block), "N"); - - //v_normals_.at(block).multiply(*SN, *S); - v_normals_.at(block).multiply(*SN, "N", *S, "N"); + s_diag.resize(n); + for (UINT32 i(0); imultiply(*S, v_normals_.at(block)); - SN->multiply(*S, "N", v_normals_.at(block), "N"); - - //v_normals_.at(block).multiply(*SN, *S); - v_normals_.at(block).multiply(*SN, "N", *S, "N"); - - delete S; - delete SN; + v_normals_.at(block).scale_symmetric_diagonal(s_diag.data()); } ////////////////// } @@ -6567,10 +6658,13 @@ void dna_adjust::Solve(bool COMPUTE_INVERSE, const UINT32& block) // compute weighted "measured minus computed" matrix_2d At_Vinv_m(v_design_.at(block).columns(), 1); At_Vinv_m.multiply(v_AtVinv_.at(block), "N", v_measMinusComp_.at(block), "N"); - + // Solve corrections from normal equations v_corrections_.at(block).redim(v_design_.at(block).columns(), 1); - v_corrections_.at(block).multiply(v_normals_.at(block), "N", At_Vinv_m, "N"); + if (v_normals_.at(block).is_symmetric()) + v_corrections_.at(block).multiply_sym(v_normals_.at(block), At_Vinv_m); + else + v_corrections_.at(block).multiply(v_normals_.at(block), "N", At_Vinv_m, "N"); if (projectSettings_.g.verbose > 0) { @@ -6854,21 +6948,26 @@ void dna_adjust::ComputeAdjustedMsrPrecisions() if (projectSettings_.a.stage) { DeserialiseBlockFromMappedFile(block, 7, - sf_normals, sf_rigorous_vars, + sf_normals, sf_rigorous_vars, sf_design, sf_atvinv, sf_estimated_stns, sf_meas_minus_comp, sf_prec_adj_msrs); v_normals_.at(block) = v_rigorousVariances_.at(block); FillDesignNormalMeasurementsMatrices(false, block, false); } - // Compute adjusted measurement precisions (v_precAdjMsrsFull_) - // from design and rigorous station variances + // from design and rigorous station variances ComputePrecisionAdjMsrs(block); // Update measurement records and Pelzer's Global reliability UpdateMsrRecords(block); + if (projectSettings_.a.stage) + { + // Persist final residuals/precisions before chi-square reloads staged matrices. + SerialiseBlockToMappedFile(block, 2, sf_meas_minus_comp, sf_prec_adj_msrs); + } + // For staged adjustments, unload all matrix data if (projectSettings_.a.stage) UnloadBlock(block); @@ -6877,9 +6976,9 @@ void dna_adjust::ComputeAdjustedMsrPrecisions() case SimultaneousMode: case Phased_Block_1Mode: // only block 1 is rigorous // Compute adjusted measurement precisions (v_precAdjMsrsFull_) - // from design and rigorous station variances + // from design and rigorous station variances ComputePrecisionAdjMsrs(); - + // Update measurement records and Pelzer's Global reliability UpdateMsrRecords(); break; @@ -7062,8 +7161,9 @@ void dna_adjust::ComputeandPrintAdjMsrOnIteration() for (UINT32 block(0); blockPrintAdjMeasurements(msr_block, printHeader); @@ -7239,7 +7347,6 @@ void dna_adjust::ComputeChiSquareNetwork() } } - void dna_adjust::ComputeChiSquarePhased(const UINT32& block) { // Compute adjusted measurement statistics @@ -7339,7 +7446,341 @@ void dna_adjust::OutputLargestCorrection(std::string& formatted_msg) formatted_msg = ss.str(); } - + +void dna_adjust::UpdateIterationDiagnostics() +{ + for (UINT32 block = 0; block < blockCount_; ++block) + { + // For staged adjustments, load corrections from mapped file + if (projectSettings_.a.stage) + DeserialiseBlockFromMappedFile(block, 1, sf_corrections); + + UINT32 nParams = v_corrections_.at(block).rows(); + UINT32 nStations = nParams / 3; + + for (UINT32 s = 0; s < nStations; ++s) + { + UINT32 stnIdx = v_parameterStationList_.at(block).at(s); + UINT32 base = s * 3; + + double cx = v_corrections_.at(block).get(base, 0); + double cy = v_corrections_.at(block).get(base + 1, 0); + double cz = v_corrections_.at(block).get(base + 2, 0); + + double magCurr = sqrt(cx*cx + cy*cy + cz*cz); + + auto prevIt = corrPrev_.find(stnIdx); + if (prevIt == corrPrev_.end()) + { + // First time seeing this station — store and move on + corrPrev_[stnIdx] = {cx, cy, cz}; + continue; + } + + double px = prevIt->second.cx; + double py = prevIt->second.cy; + double pz = prevIt->second.cz; + double magPrev = sqrt(px*px + py*py + pz*pz); + + // Update stored corrections + prevIt->second = {cx, cy, cz}; + + // Skip tiny corrections (sub-millimetre) + if (magCurr < 0.001 && magPrev < 0.001) + { + stnOscCount_[stnIdx] = 0; + continue; + } + + // Dot product and cosine of angle between correction vectors + double dot = cx*px + cy*py + cz*pz; + double denom = magCurr * magPrev; + double cosAngle = (denom > 1e-30) ? dot / denom : 0.0; + + // Magnitude ratio + double ratio = (magPrev > 1e-30) ? magCurr / magPrev : 0.0; + + // Oscillation: anti-parallel (cos < -0.5), similar magnitude (ratio 0.3–3.0) + if (cosAngle < -0.5 && ratio > 0.3 && ratio < 3.0) + stnOscCount_[stnIdx]++; + else + stnOscCount_[stnIdx] = 0; + + if (stnOscCount_[stnIdx] >= 2) + { + // Convert to local (e, n, up) + matrix_2d cart(3, 1), local(3, 1); + cart.put(0, 0, cx); + cart.put(1, 0, cy); + cart.put(2, 0, cz); + Rotate_CartLocal(cart, &local, + bstBinaryRecords_.at(stnIdx).currentLatitude, + bstBinaryRecords_.at(stnIdx).currentLongitude); + + double localMag = sqrt(local.get(0,0)*local.get(0,0) + + local.get(1,0)*local.get(1,0) + local.get(2,0)*local.get(2,0)); + + auto hit = oscHistory_.find(stnIdx); + if (hit == oscHistory_.end()) + { + OscillationRecord rec; + rec.stnBstIdx = stnIdx; + rec.firstIteration = CurrentIteration(); + rec.lastIteration = CurrentIteration(); + rec.maxCycles = stnOscCount_[stnIdx]; + rec.firstMag = localMag; + rec.lastMag = localMag; + rec.lastE = local.get(0, 0); + rec.lastN = local.get(1, 0); + rec.lastUp = local.get(2, 0); + oscHistory_[stnIdx] = rec; + } + else + { + hit->second.lastIteration = CurrentIteration(); + hit->second.maxCycles = stnOscCount_[stnIdx]; + hit->second.lastMag = localMag; + hit->second.lastE = local.get(0, 0); + hit->second.lastN = local.get(1, 0); + hit->second.lastUp = local.get(2, 0); + } + } + } + + // For staged adjustments, unload corrections + if (projectSettings_.a.stage) + UnloadBlock(block, 1, sf_corrections); + } +} + +void dna_adjust::PrintOscillationSummary() +{ + if (oscHistory_.empty()) + return; + + // Collect records with meaningful magnitude, sort descending + std::vector sorted; + for (auto& kv : oscHistory_) + { + double peak = std::max(kv.second.firstMag, kv.second.lastMag); + if (peak >= 0.1) + sorted.push_back(&kv.second); + } + + if (sorted.empty()) + return; + + std::sort(sorted.begin(), sorted.end(), + [](const OscillationRecord* a, const OscillationRecord* b) { + return std::max(a->firstMag, a->lastMag) > std::max(b->firstMag, b->lastMag); + }); + + // Cap at 20 stations + size_t limit = std::min(sorted.size(), static_cast(20)); + + std::cout << std::endl; + std::cout << "+ Oscillating stations detected (" << sorted.size() << " total, showing top " + << limit << "):" << std::endl; + + for (size_t i = 0; i < limit; ++i) + { + const auto* rec = sorted[i]; + + // Classify direction + double horizMag = sqrt(rec->lastE * rec->lastE + rec->lastN * rec->lastN); + double vertMag = fabs(rec->lastUp); + std::string direction; + if (vertMag < 0.01 * horizMag) + direction = "horizontal"; + else if (horizMag < 0.01 * vertMag) + direction = "vertical"; + else + direction = "3D"; + + std::cout << " - " << bstBinaryRecords_.at(rec->stnBstIdx).stationName + << std::fixed << std::setprecision(1) + << " — " << rec->firstMag << "m to " << rec->lastMag << "m" + << ", " << direction + << ", " << rec->maxCycles << " cycles" + << " (iterations " << rec->firstIteration << "-" << rec->lastIteration << ")" + << std::endl; + } +} + +bool dna_adjust::MeasurementTouchesOscillatingStation(const UINT32& msrIndex) const +{ + if (oscHistory_.empty() || msrIndex >= bmsBinaryRecords_.size()) + return false; + + std::vector msrStations; + GetMsrStations(bmsBinaryRecords_, msrIndex, msrStations); + + for (const auto& stnIndex : msrStations) + { + if (oscHistory_.find(stnIndex) != oscHistory_.end()) + return true; + } + + return false; +} + +std::string dna_adjust::MeasurementStationNames(const UINT32& msrIndex) const +{ + if (msrIndex >= bmsBinaryRecords_.size()) + return "(measurement index unavailable)"; + + std::vector msrStations; + GetMsrStations(bmsBinaryRecords_, msrIndex, msrStations); + + std::string stationNames; + for (const auto& stnIndex : msrStations) + { + if (stnIndex >= bstBinaryRecords_.size()) + continue; + + if (!stationNames.empty()) + stationNames += " -> "; + stationNames += bstBinaryRecords_.at(stnIndex).stationName; + } + + if (stationNames.empty()) + return "(stations unavailable)"; + + return stationNames; +} + +void dna_adjust::PrintSuspectMeasurementSummary(std::ostream& os, size_t limit) const +{ + struct SuspectMeasurementRecord { + UINT32 msrIndex; + char measType; + UINT32 clusterID; + UINT32 fileOrder; + double absNStat; + double nStat; + double tStat; + double measCorr; + double residualPrec; + double pelzerRel; + bool exceedsCritical; + bool touchesOscillatingStation; + std::string stationNames; + }; + + if (bmsBinaryRecords_.empty() || limit == 0) + return; + + std::vector records; + records.reserve(bmsBinaryRecords_.size()); + + for (UINT32 msrIndex = 0; msrIndex < bmsBinaryRecords_.size(); ++msrIndex) + { + const auto& msr = bmsBinaryRecords_.at(msrIndex); + if (msr.ignore || + !std::isfinite(msr.NStat) || + !std::isfinite(msr.residualPrec) || + msr.residualPrec <= 0.0) + continue; + + const double absNStat = fabs(msr.NStat); + const bool exceedsCritical = absNStat > criticalValue_; + const bool touchesOscillatingStation = MeasurementTouchesOscillatingStation(msrIndex); + + if (!exceedsCritical && !touchesOscillatingStation) + continue; + + records.push_back({ + msrIndex, + msr.measType, + msr.clusterID, + msr.fileOrder, + absNStat, + msr.NStat, + msr.TStat, + msr.measCorr, + msr.residualPrec, + msr.PelzerRel, + exceedsCritical, + touchesOscillatingStation, + MeasurementStationNames(msrIndex) + }); + } + + if (records.empty()) + return; + + std::vector oscillatingRecords; + std::vector outlierRecords; + oscillatingRecords.reserve(records.size()); + outlierRecords.reserve(records.size()); + + for (const auto& record : records) + { + if (record.touchesOscillatingStation) + oscillatingRecords.push_back(&record); + if (record.exceedsCritical && !record.touchesOscillatingStation) + outlierRecords.push_back(&record); + } + + auto sortByNStat = [](const SuspectMeasurementRecord* lhs, + const SuspectMeasurementRecord* rhs) { + if (lhs->absNStat == rhs->absNStat) + return lhs->msrIndex < rhs->msrIndex; + return lhs->absNStat > rhs->absNStat; + }; + + std::sort(oscillatingRecords.begin(), oscillatingRecords.end(), sortByNStat); + std::sort(outlierRecords.begin(), outlierRecords.end(), sortByNStat); + + std::ios::fmtflags oldFlags = os.flags(); + std::streamsize oldPrecision = os.precision(); + + auto printRecord = [&os](const SuspectMeasurementRecord& record) { + os << " - " << record.measType + << " msr " << record.msrIndex + << " cluster " << record.clusterID + << " file-order " << record.fileOrder + << " " << record.stationNames + << ": N=" << std::fixed << std::setprecision(2) << record.nStat; + + if (std::isfinite(record.tStat) && fabs(record.tStat) > 0.0) + os << ", T=" << std::fixed << std::setprecision(2) << record.tStat; + + os << ", corr=" << std::scientific << std::setprecision(3) << record.measCorr + << ", residual precision=" << std::scientific << std::setprecision(3) << record.residualPrec + << ", Pelzer=" << std::fixed << std::setprecision(2) << record.pelzerRel; + + if (record.exceedsCritical) + os << ", exceeds critical"; + if (record.touchesOscillatingStation) + os << ", touches oscillating station"; + + os << std::endl; + }; + + auto printList = [&os, &printRecord, limit](const std::string& title, + const std::vector& list) { + if (list.empty()) + return; + + const size_t listLimit = std::min(list.size(), limit); + os << std::endl << "+ " << title << " (" << list.size() + << " total, showing top " << listLimit << "):" << std::endl; + + for (size_t i = 0; i < listLimit; ++i) + printRecord(*list.at(i)); + }; + + if (!oscillatingRecords.empty()) + printList("Suspect measurements connected to oscillating stations", oscillatingRecords); + + printList(oscillatingRecords.empty() ? "Largest measurement N-statistics" : + "Largest remaining measurement N-statistics", outlierRecords); + + os.flags(oldFlags); + os.precision(oldPrecision); +} + void dna_adjust::ComputePrecisionAdjMsrs(const UINT32& block /*= 0*/) { if (projectSettings_.a.report_mode) @@ -7348,6 +7789,7 @@ void dna_adjust::ComputePrecisionAdjMsrs(const UINT32& block /*= 0*/) // A*V-1*At, where: // - A is design matrix // - V is the inverse of the normals (i.e. precision of estimates) + v_precAdjMsrsFull_.at(block).redim(v_measurementVarianceCount_.at(block), 1); v_precAdjMsrsFull_.at(block).zero(); UINT32 design_row(0); @@ -8027,19 +8469,19 @@ void dna_adjust::ComputeChiSquare_D(it_vmsr_t& _it_msr, UINT32& measurement_inde } -void dna_adjust::FormInverseVarianceMatrix(matrix_2d* vmat, bool LOWER_IS_CLEARED) +void dna_adjust::FormInverseVarianceMatrix(matrix_2d* vmat, bool LOWER_IS_CLEARED, bool mark_symmetric) { if (vmat->rows() == 1) { vmat->put(0, 0, 1./vmat->get(0, 0)); return; } - + // As of version 3.2.0, force all inversions to use MKL. This change // is enforced for two reasons: // 1. Sweep, Gaussian inverse and numerical recipes cholesky - // all require matrix data to be stored in row wise fashion, upper - // triangle only, whereas contiguous matrix class stores matrix data + // all require matrix data to be stored in row wise fashion, upper + // triangle only, whereas contiguous matrix class stores matrix data // in column wise fashion, lower triangle. // 2. Sweep, Gaussian never really offered a stable solution. // The following switch is kept in case future development warrants @@ -8049,8 +8491,8 @@ void dna_adjust::FormInverseVarianceMatrix(matrix_2d* vmat, bool LOWER_IS_CLEARE // TODO: All functions which load variance matrix from binary files // store the data in upper triangular form. This could be changed to - // lower triangular form, thus alleviating the need to pass - // LOWER_IS_CLEARED. That is, force all operations to use a lower + // lower triangular form, thus alleviating the need to pass + // LOWER_IS_CLEARED. That is, force all operations to use a lower // triangular matrix. Not sure if this would create an efficiency or not. switch (projectSettings_.a.inverse_method_msr) { @@ -8062,8 +8504,7 @@ void dna_adjust::FormInverseVarianceMatrix(matrix_2d* vmat, bool LOWER_IS_CLEARE // break; case Cholesky_mkl: default: - // Inversion using Intel MKL - vmat->cholesky_inverse(LOWER_IS_CLEARED); + vmat->cholesky_inverse(LOWER_IS_CLEARED, mark_symmetric); break; // choleskyinverse broke once the storage order of the matrix buffer was // changed from row-wise to column wise. diff --git a/dynadjust/dynadjust/dnaadjust/dnaadjust.hpp b/dynadjust/dynadjust/dnaadjust/dnaadjust.hpp index 9039f7711..33fe58c7a 100644 --- a/dynadjust/dynadjust/dnaadjust/dnaadjust.hpp +++ b/dynadjust/dynadjust/dnaadjust/dnaadjust.hpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -102,6 +103,7 @@ namespace networkadjust { extern std::mutex maxCorrMutex; using dynadjust::cpu_timer; +using dynadjust::FormatElapsedTime; // forward declaration of dna_adjust class dna_adjust; @@ -210,6 +212,7 @@ class DNAADJUST_API dna_adjust { class dna_adjust { #endif friend class DynAdjustPrinter; + friend class DynAdjustJsonPrinter; public: dna_adjust(); @@ -288,16 +291,26 @@ class dna_adjust { // void PrintEstimatedStationCoordinatestoDNAXML_Y(const std::string& msrFile, // INPUT_FILE_TYPE t); + void PrintOscillationSummary(); + void PrintSuspectMeasurementSummary(std::ostream& os = std::cout, + size_t limit = 20) const; void CloseOutputFiles(); void UpdateBinaryFiles(); UINT32 CurrentIteration() const; UINT32& incrementIteration(); + void decrementIteration(); void initialiseIteration(const UINT32& iteration = 0); inline UINT32 CurrentBlock() const { return currentBlock_; }; - inline void SetcurrentBlock(const UINT32 b) { currentBlock_ = b; }; + inline void SetcurrentBlock(const UINT32 b) { + auto now = std::chrono::steady_clock::now(); + if (b != currentBlock_ && blockStartTime_.time_since_epoch().count() > 0) + lastBlockElapsedMs_ = std::chrono::duration_cast(now - blockStartTime_).count(); + currentBlock_ = b; + blockStartTime_ = now; + }; inline void SetmaxCorr(const double c) { @@ -311,6 +324,15 @@ class dna_adjust { } inline bool processingForward() { return forward_; } inline bool processingCombine() { return isCombining_; } + inline int64_t LastBlockElapsedMs() const { return lastBlockElapsedMs_; } + inline UINT32 CurrentBlockStationCount() const { + if (currentBlock_ < v_blockStationsMap_.size()) + return static_cast(v_blockStationsMap_.at(currentBlock_).size()); + return 0; + } + static void SetMaxBlasThreads(int n); + static int GetMaxBlasThreads(); + inline int GetDegreesOfFreedom() const { return degreesofFreedom_; } inline UINT32 GetMeasurementCount() const { return measurementParams_; } inline UINT32 GetUnknownsCount() const { return unknownParams_; } @@ -350,6 +372,12 @@ class dna_adjust { return iterationCorrections_.get_message(iteration); // safe guard return iterationCorrections_.get_message(iteration - 1); }; + + inline std::string GetIterationTime(const UINT32& iteration) const { + if (iteration == 0) + return iterationTimes_.get_message(iteration); // safe guard + return iterationTimes_.get_message(iteration - 1); + }; ///////////////////////////// concurrent_ofstream concurrent_adj_ofstream; @@ -380,14 +408,21 @@ class dna_adjust { bool isAdjusting_; bool isCombining_; bool forward_; + bool rebuildingDesign_; bool isFirstTimeAdjustment_; bool isIterationComplete_; bool isAdjustmentQuestionable_; UINT32 blockCount_; UINT32 currentBlock_; + std::chrono::steady_clock::time_point blockStartTime_; + int64_t lastBlockElapsedMs_; std::chrono::milliseconds total_time_; + bool profileTimings_; + std::atomic profileUpdateNormalsNs_; + std::atomic profileStageLoadNs_; + std::atomic profileStageStoreNs_; _ADJUST_STATUS_ adjustStatus_; vstring statusMessages_; UINT32 currentIteration_; @@ -437,6 +472,7 @@ class dna_adjust { void UpdateAdjustment(bool iterate); void ValidateandFinaliseAdjustment(cpu_timer& tot_time); void PrintAdjustmentTime(cpu_timer& time, _TIMER_TYPE_); + void PrintPerformanceProfile() const; void InitialiseAdjustment(); void SetDefaultReferenceFrame(); void LoadNetworkFiles(); @@ -499,6 +535,8 @@ class dna_adjust { void DeserialiseBlockFromMappedFile(const UINT32& block, const int count = 0, ...); void UnloadBlock(const UINT32& block, const int file_count = 0, ...); + void AdviseBlockDontNeed(const UINT32& block); + void AdviseBlockWillNeed(const UINT32& block); void PurgeMatricesFromDisk(); // Helpers @@ -534,7 +572,8 @@ class dna_adjust { void UpdateDesignNormalMeasMatrices(pit_vmsr_t _it_msr, UINT32& design_row, bool buildnewMatrices, const UINT32& block, - bool MT_ReverseOrCombine); + bool MT_ReverseOrCombine, + bool skipNormals = false); void UpdateDesignNormalMeasMatrices_A(pit_vmsr_t _it_msr, UINT32& design_row, @@ -542,40 +581,45 @@ class dna_adjust { matrix_2d* measMinusComp, matrix_2d* estimatedStations, matrix_2d* normals, matrix_2d* design, - matrix_2d* AtVinv, bool buildnewMatrices); + matrix_2d* AtVinv, bool buildnewMatrices, + bool skipNormals = false); void UpdateDesignNormalMeasMatrices_BK(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, matrix_2d* measMinusComp, matrix_2d* estimatedStations, matrix_2d* normals, matrix_2d* design, - matrix_2d* AtVinv, bool buildnewMatrices); + matrix_2d* AtVinv, bool buildnewMatrices, + bool skipNormals = false); void UpdateDesignNormalMeasMatrices_C(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, matrix_2d* measMinusComp, matrix_2d* estimatedStations, matrix_2d* normals, matrix_2d* design, - matrix_2d* AtVinv, bool buildnewMatrices); + matrix_2d* AtVinv, bool buildnewMatrices, + bool skipNormals = false); void UpdateDesignNormalMeasMatrices_CEM( pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, matrix_2d* measMinusComp, matrix_2d* estimatedStations, matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, - bool buildnewMatrices); + bool buildnewMatrices, bool skipNormals = false); void UpdateDesignNormalMeasMatrices_D(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, matrix_2d* measMinusComp, matrix_2d* estimatedStations, matrix_2d* normals, matrix_2d* design, - matrix_2d* AtVinv, bool buildnewMatrices); + matrix_2d* AtVinv, bool buildnewMatrices, + bool skipNormals = false); void UpdateDesignNormalMeasMatrices_E(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, matrix_2d* measMinusComp, matrix_2d* estimatedStations, matrix_2d* normals, matrix_2d* design, - matrix_2d* AtVinv, bool buildnewMatrices); + matrix_2d* AtVinv, bool buildnewMatrices, + bool skipNormals = false); void UpdateDesignMeasMatrices_GX(pit_vmsr_t _it_msr, UINT32& design_row, matrix_2d* measMinusComp, matrix_2d* estimatedStations, @@ -587,113 +631,130 @@ class dna_adjust { matrix_2d* measMinusComp, matrix_2d* estimatedStations, matrix_2d* normals, matrix_2d* design, - matrix_2d* AtVinv, bool buildnewMatrices); + matrix_2d* AtVinv, bool buildnewMatrices, + bool skipNormals = false); void UpdateDesignNormalMeasMatrices_H(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, matrix_2d* measMinusComp, matrix_2d* estimatedStations, matrix_2d* normals, matrix_2d* design, - matrix_2d* AtVinv, bool buildnewMatrices); + matrix_2d* AtVinv, bool buildnewMatrices, + bool skipNormals = false); void UpdateDesignNormalMeasMatrices_HR(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, matrix_2d* measMinusComp, matrix_2d* estimatedStations, matrix_2d* normals, matrix_2d* design, - matrix_2d* AtVinv, bool buildnewMatrices); + matrix_2d* AtVinv, bool buildnewMatrices, + bool skipNormals = false); void UpdateDesignNormalMeasMatrices_I(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, matrix_2d* measMinusComp, matrix_2d* estimatedStations, matrix_2d* normals, matrix_2d* design, - matrix_2d* AtVinv, bool buildnewMatrices); + matrix_2d* AtVinv, bool buildnewMatrices, + bool skipNormals = false); void UpdateDesignNormalMeasMatrices_IP(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, matrix_2d* measMinusComp, matrix_2d* estimatedStations, matrix_2d* normals, matrix_2d* design, - matrix_2d* AtVinv, bool buildnewMatrices); + matrix_2d* AtVinv, bool buildnewMatrices, + bool skipNormals = false); void UpdateDesignNormalMeasMatrices_J(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, matrix_2d* measMinusComp, matrix_2d* estimatedStations, matrix_2d* normals, matrix_2d* design, - matrix_2d* AtVinv, bool buildnewMatrices); + matrix_2d* AtVinv, bool buildnewMatrices, + bool skipNormals = false); void UpdateDesignNormalMeasMatrices_JQ(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, matrix_2d* measMinusComp, matrix_2d* estimatedStations, matrix_2d* normals, matrix_2d* design, - matrix_2d* AtVinv, bool buildnewMatrices); + matrix_2d* AtVinv, bool buildnewMatrices, + bool skipNormals = false); void UpdateDesignNormalMeasMatrices_L(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, matrix_2d* measMinusComp, matrix_2d* estimatedStations, matrix_2d* normals, matrix_2d* design, - matrix_2d* AtVinv, bool buildnewMatrices); + matrix_2d* AtVinv, bool buildnewMatrices, + bool skipNormals = false); void UpdateDesignNormalMeasMatrices_M(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, matrix_2d* measMinusComp, matrix_2d* estimatedStations, matrix_2d* normals, matrix_2d* design, - matrix_2d* AtVinv, bool buildnewMatrices); + matrix_2d* AtVinv, bool buildnewMatrices, + bool skipNormals = false); void UpdateDesignNormalMeasMatrices_P(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, matrix_2d* measMinusComp, matrix_2d* estimatedStations, matrix_2d* normals, matrix_2d* design, - matrix_2d* AtVinv, bool buildnewMatrices); + matrix_2d* AtVinv, bool buildnewMatrices, + bool skipNormals = false); void UpdateDesignNormalMeasMatrices_Q(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, matrix_2d* measMinusComp, matrix_2d* estimatedStations, matrix_2d* normals, matrix_2d* design, - matrix_2d* AtVinv, bool buildnewMatrices); + matrix_2d* AtVinv, bool buildnewMatrices, + bool skipNormals = false); void UpdateDesignNormalMeasMatrices_R(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, matrix_2d* measMinusComp, matrix_2d* estimatedStations, matrix_2d* normals, matrix_2d* design, - matrix_2d* AtVinv, bool buildnewMatrices); + matrix_2d* AtVinv, bool buildnewMatrices, + bool skipNormals = false); void UpdateDesignNormalMeasMatrices_S(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, matrix_2d* measMinusComp, matrix_2d* estimatedStations, matrix_2d* normals, matrix_2d* design, - matrix_2d* AtVinv, bool buildnewMatrices); + matrix_2d* AtVinv, bool buildnewMatrices, + bool skipNormals = false); void UpdateDesignNormalMeasMatrices_V(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, matrix_2d* measMinusComp, matrix_2d* estimatedStations, matrix_2d* normals, matrix_2d* design, - matrix_2d* AtVinv, bool buildnewMatrices); + matrix_2d* AtVinv, bool buildnewMatrices, + bool skipNormals = false); void UpdateDesignNormalMeasMatrices_X( pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices); + matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, + bool skipNormals = false); void UpdateDesignNormalMeasMatrices_Y( pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices); + matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, + bool skipNormals = false); void UpdateDesignNormalMeasMatrices_Z(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, matrix_2d* measMinusComp, matrix_2d* estimatedStations, matrix_2d* normals, matrix_2d* design, - matrix_2d* AtVinv, bool buildnewMatrices); + matrix_2d* AtVinv, bool buildnewMatrices, + bool skipNormals = false); void UpdateIgnoredMeasurements(pit_vmsr_t _it_msr, bool storeOriginalMeasurement); @@ -827,7 +888,9 @@ class dna_adjust { void PrepareDesignAndMsrMnsCmpMatricesStage(const UINT32& block); void FillDesignNormalMeasurementsMatrices(bool buildnewMatrices, const UINT32& block, - bool MT_ReverseOrCombine); + bool MT_ReverseOrCombine, + bool skipNormals = false); + void RebuildDesignAndAtVinv(const UINT32& block); // void RecomputeMeasurementsCommonJunctions(const UINT32& nextBlock, const // UINT32& thisBlock, const UINT32& prevBlock); @@ -861,11 +924,11 @@ class dna_adjust { bool printHeader); void ComputeAdjMsrBlockOnIteration(const UINT32& block); - void ComputeAdjustedMsrPrecisions(); + void ComputeAdjustedMsrPrecisions(); - void ComputeChiSquareNetwork(); - void ComputeChiSquare(const UINT32& block); - void ComputeChiSquareSimultaneous(); + void ComputeChiSquareNetwork(); + void ComputeChiSquare(const UINT32& block); + void ComputeChiSquareSimultaneous(); void ComputeChiSquarePhased(const UINT32& block); void ComputeTestStat(const double& dof, double& chiUpper, double& chiLower, @@ -929,7 +992,7 @@ class dna_adjust { matrix_2d* measMinusComp); void - FormInverseVarianceMatrix(matrix_2d* vmat, bool LOWER_IS_CLEARED = false); + FormInverseVarianceMatrix(matrix_2d* vmat, bool LOWER_IS_CLEARED = false, bool mark_symmetric = false); void FormInverseGPSVarianceMatrix(const it_vmsr_t& _it_msr, matrix_2d* vmat); bool @@ -1118,6 +1181,7 @@ class dna_adjust { bool allStationsFixed_; message_bank iterationCorrections_; + message_bank iterationTimes_; // For each block vUINT32 v_ContiguousNetList_; // vector of contiguous network IDs @@ -1205,6 +1269,27 @@ class dna_adjust { v_mat_2d v_corrections_; // vector of residuals matrices v_mat_2d v_correctionsR_; // vector of residuals matrices + // ---------------------------------------------- + // Iteration diagnostics — oscillation detection + struct StationCorrRecord { + double cx, cy, cz; // Cartesian corrections + }; + struct OscillationRecord { + UINT32 stnBstIdx; // BST record index + UINT32 firstIteration; + UINT32 lastIteration; + UINT32 maxCycles; + double firstMag; + double lastMag; + double lastE, lastN, lastUp; + }; + std::map corrPrev_; // keyed by BST station index + std::map stnOscCount_; // keyed by BST station index + std::map oscHistory_; // keyed by BST station index + void UpdateIterationDiagnostics(); + bool MeasurementTouchesOscillatingStation(const UINT32& msrIndex) const; + std::string MeasurementStationNames(const UINT32& msrIndex) const; + // ---------------------------------------------- // Adjustment functions and variables for staged adjustment diff --git a/dynadjust/dynadjust/dnaadjust/dnaadjust_json_printer.cpp b/dynadjust/dynadjust/dnaadjust/dnaadjust_json_printer.cpp new file mode 100644 index 000000000..117bacf49 --- /dev/null +++ b/dynadjust/dynadjust/dnaadjust/dnaadjust_json_printer.cpp @@ -0,0 +1,615 @@ +//============================================================================ +// Name : dnaadjust_json_printer.cpp +// Author : Dale Roberts +// Copyright : Copyright 2026 Geoscience Australia +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Description : JSONL output implementation. Builds nlohmann::json objects +// from the same in-memory data consumed by the text printer +// and emits one JSON object per line. Key names mirror the +// DynaML / JSONL input schema so records round-trip through +// dnaimport. +//============================================================================ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace dynadjust { +namespace networkadjust { + +namespace { + +using nlohmann::json; + +// Trim trailing space/\r/\n from a fixed-width station field. Fields are +// right-padded with spaces on disk and null-terminated in memory, so the +// std::string(const char*) conversion plus trimstr yields the clean name. +std::string Trimmed(const char* buf) { + return trimstr(std::string(buf)); +} + +json HeaderRecord(const CDnaDatum& datum, const char* report_kind) { + json hdr; + hdr["type"] = "Adjustment"; + hdr["report"] = report_kind; + hdr["software"] = std::string("DynAdjust ") + __BINARY_VERSION__; + hdr["referenceframe"] = datum.GetName(); + hdr["epoch"] = datum.GetEpoch_s(); + // Output wrapper distinct from DnaXmlFormat (used by DynaML XML / JSONL + // input) so readers can tell an adjustment report apart from input data. + // The JSONL input parser recognises both wrappers as a header line. + return json{{"DnaAdjustmentReport", hdr}}; +} + +json StationIdentity(const it_vstn_t& stn_it) { + json s; + s["Name"] = Trimmed(stn_it->stationName); + s["Constraints"] = Trimmed(stn_it->stationConst); + // Round-trip StationCoord blocks on .xyz records carry Lat/Lon in + // DMS-packed form, so the type tag is always LLH for adjustment output. + s["Type"] = "LLH"; + const std::string desc = Trimmed(stn_it->description); + if (!desc.empty()) + s["Description"] = desc; + return s; +} + +// Geographic representation of an adjusted Cartesian position, computed once +// per station so OnAdjustedStation does not run CartToGeo twice. +struct AdjustedPosition { + double x, y, z; + double lat, lon, height; // lat/lon in radians, height in metres +}; + +AdjustedPosition PositionFromContext( + const matrix_2d* estimates, const UINT32& mat_idx, + const DynAdjustPrinter::AdjustedStationContext& ctx) { + // x/y/z read straight from the estimate matrix; lat/lon/h were already + // computed by PrintAdjStation and threaded through ctx, so CartToGeo + // does not need to run again. + AdjustedPosition p; + p.x = estimates->get(mat_idx, 0); + p.y = estimates->get(mat_idx + 1, 0); + p.z = estimates->get(mat_idx + 2, 0); + p.lat = ctx.lat; + p.lon = ctx.lon; + p.height = ctx.height; + return p; +} + +// Lat and Lon come out of the adjustment as radians but are written in DynaML +// XML output as DMS-packed decimal (31.483180 ≡ 31°48'31.80"). Emitting the +// same encoding keeps the JSONL round-trip through SetXAxis/SetYAxis, whose +// DmstoDeg() converts the decimal back to radians. +json InitialCoords(const it_vstn_t& stn_it) { + json init; + init["Lat"] = RadtoDms(stn_it->initialLatitude); + init["Lon"] = RadtoDmsL(stn_it->initialLongitude); + init["Height"] = stn_it->initialHeight; + return init; +} + +json AdjustedCoords(const AdjustedPosition& p) { + json adj; + adj["X"] = p.x; + adj["Y"] = p.y; + adj["Z"] = p.z; + adj["Lat"] = RadtoDms(p.lat); + adj["Lon"] = RadtoDmsL(p.lon); + adj["Height"] = p.height; + return adj; +} + +// Round-trippable StationCoord block using the DynaML LLH convention so the +// JSONL input parser can re-ingest an adjustment report as a station file. +json StationCoordLLH(const it_vstn_t& stn_it, const AdjustedPosition& p) { + json c; + c["Name"] = Trimmed(stn_it->stationName); + c["XAxis"] = RadtoDms(p.lat); // Lat, DMS-packed decimal + c["YAxis"] = RadtoDmsL(p.lon); // Lon, DMS-packed decimal + c["Height"] = p.height; + return c; +} + +json UncertaintyBlockFromCart(const it_vstn_t& stn_it, + const matrix_2d& var_cart, + const matrix_2d& var_local_with_geoid) { + // var_local already has geoid variance in (2,2) per AdjustedStationContext + // contract; ellipse + PU are derived from it directly so the numeric + // output matches the previous path byte-for-byte. + double semimajor = 0., semiminor = 0., azimuth = 0.; + ErrorEllipseParameters(var_local_with_geoid, + semimajor, semiminor, azimuth); + double hz_pos_u = 0., vt_pos_u = 0.; + PositionalUncertainty(semimajor, semiminor, + std::sqrt(var_local_with_geoid.get(2, 2)), hz_pos_u, vt_pos_u); + + (void)stn_it; // reserved for future fields; currently all values come + // from ctx-derived matrices. + + json u; + u["SE"] = std::sqrt(var_local_with_geoid.get(0, 0)); + u["SN"] = std::sqrt(var_local_with_geoid.get(1, 1)); + u["SU"] = std::sqrt(var_local_with_geoid.get(2, 2)); + u["SemiMajor"] = semimajor; + u["SemiMinor"] = semiminor; + u["Orientation"] = azimuth; + u["HzPosU"] = hz_pos_u; + u["VtPosU"] = vt_pos_u; + u["VarianceLocal"] = { + {var_local_with_geoid.get(0, 0), var_local_with_geoid.get(0, 1), + var_local_with_geoid.get(0, 2)}, + {var_local_with_geoid.get(1, 0), var_local_with_geoid.get(1, 1), + var_local_with_geoid.get(1, 2)}, + {var_local_with_geoid.get(2, 0), var_local_with_geoid.get(2, 1), + var_local_with_geoid.get(2, 2)}}; + u["VarianceCart"] = { + {var_cart.get(0, 0), var_cart.get(0, 1), var_cart.get(0, 2)}, + {var_cart.get(1, 0), var_cart.get(1, 1), var_cart.get(1, 2)}, + {var_cart.get(2, 0), var_cart.get(2, 1), var_cart.get(2, 2)}}; + return u; +} + +// Applies the geoid model variance to var_local[2,2] and re-derives the +// ellipse + positional uncertainty terms. Used by OnPositionalUncertainty +// to keep .apu.jsonl numerics identical to the pre-refactor behaviour: +// text .apu excludes geoid from ellipse/PU, but the JSON sibling has +// always included it and downstream consumers depend on that. +json UncertaintyBlockWithGeoid(const it_vstn_t& stn_it, + const matrix_2d& var_cart, + const matrix_2d& var_local_no_geoid) { + matrix_2d var_local = var_local_no_geoid; // copy; keeps base var_local clean + var_local.elementadd(2, 2, + static_cast(stn_it->geoidSepUnc) * stn_it->geoidSepUnc); + return UncertaintyBlockFromCart(stn_it, var_cart, var_local); +} + +json CorrectionsBlockFromContext( + const DynAdjustPrinter::StationCorrectionContext& ctx) { + json c; + c["dE"] = ctx.cor_e; + c["dN"] = ctx.cor_n; + c["dUp"] = ctx.cor_up; + return c; +} + +bool ScalarUsesAngularInputUnits(const char type) { + switch (type) { + case 'A': + case 'B': + case 'D': + case 'I': + case 'J': + case 'K': + case 'P': + case 'Q': + case 'V': + case 'Z': + return true; + default: + return false; + } +} + +double ScalarInputValue(const it_vmsr_t& it_msr) { + return ScalarUsesAngularInputUnits(it_msr->measType) + ? RadtoDms(it_msr->term1) + : it_msr->term1; +} + +double ScalarInputStdDev(const it_vmsr_t& it_msr) { + const double stddev = std::sqrt(it_msr->term2); + return ScalarUsesAngularInputUnits(it_msr->measType) + ? Seconds(stddev) + : stddev; +} + +void AddObservationEpoch(json& m, const measurement_t& msr) { + const std::string observation_epoch = Trimmed(msr.observation_epoch); + if (!observation_epoch.empty()) + m["EpochOfObservation"] = observation_epoch; +} + +// Populate the scalar DnaMeasurement fields common to every type. Used for +// non-cluster types (A/B/C/E/H/I/J/K/L/M/P/Q/R/S/V/Z) where the bms iterator +// points to a single record whose adjusted value is the scalar term1/measAdj. +json ScalarMeasurement(const vstn_t& stations, const it_vmsr_t& it_msr) { + json m; + m["Type"] = std::string(1, it_msr->measType); + AddObservationEpoch(m, *it_msr); + if (it_msr->ignore) + m["Ignore"] = true; + m["First"] = Trimmed(stations.at(it_msr->station1).stationName); + if (it_msr->measurementStations >= 2) + m["Second"] = Trimmed(stations.at(it_msr->station2).stationName); + if (it_msr->measurementStations >= 3) + m["Third"] = Trimmed(stations.at(it_msr->station3).stationName); + m["Value"] = ScalarInputValue(it_msr); + m["StdDev"] = ScalarInputStdDev(it_msr); + m["Adjusted"] = it_msr->measAdj; + m["Correction"] = it_msr->measCorr; + m["AdjustedPrecision"] = it_msr->measAdjPrec; + m["ResidualPrecision"] = it_msr->residualPrec; + m["NStat"] = it_msr->NStat; + m["TStat"] = it_msr->TStat; + m["PelzerRel"] = it_msr->PelzerRel; + return m; +} + +json DirectionEntry(const vstn_t& stations, const it_vmsr_t& it_dir) { + json direction; + direction["Target"] = Trimmed(stations.at(it_dir->station2).stationName); + direction["Value"] = ScalarInputValue(it_dir); + direction["StdDev"] = ScalarInputStdDev(it_dir); + if (it_dir->ignore) + direction["Ignore"] = true; + + direction["Adjusted"] = it_dir->measAdj; + direction["Correction"] = it_dir->measCorr; + direction["AdjustedPrecision"] = it_dir->measAdjPrec; + direction["ResidualPrecision"] = it_dir->residualPrec; + direction["NStat"] = it_dir->NStat; + direction["TStat"] = it_dir->TStat; + direction["PelzerRel"] = it_dir->PelzerRel; + return direction; +} + +json DirectionSetJson(const vstn_t& stations, const it_vmsr_t& it_set) { + json m = ScalarMeasurement(stations, it_set); + json directions = json::array(); + + const UINT32 direction_count = + it_set->vectorCount1 > 0 ? it_set->vectorCount1 - 1 : 0; + it_vmsr_t it_dir = it_set + 1; + for (UINT32 dir = 0; dir < direction_count; ++dir, ++it_dir) + directions.push_back(DirectionEntry(stations, it_dir)); + + m["Total"] = direction_count; + m["Directions"] = directions; + return m; +} + +json ComponentTriple(const it_vmsr_t& it_x, double measurement_t::*field) { + const it_vmsr_t it_y = it_x + 1; + const it_vmsr_t it_z = it_x + 2; + return json{{"X", (*it_x).*field}, + {"Y", (*it_y).*field}, + {"Z", (*it_z).*field}}; +} + +json CovarianceComponent(const it_vmsr_t& it_cov_x) { + const it_vmsr_t it_cov_y = it_cov_x + 1; + const it_vmsr_t it_cov_z = it_cov_x + 2; + + return json{{"m11", it_cov_x->term1}, + {"m12", it_cov_x->term2}, + {"m13", it_cov_x->term3}, + {"m21", it_cov_y->term1}, + {"m22", it_cov_y->term2}, + {"m23", it_cov_y->term3}, + {"m31", it_cov_z->term1}, + {"m32", it_cov_z->term2}, + {"m33", it_cov_z->term3}}; +} + +// Build one parser-compatible GPSBaseline or Clusterpoint entry from three +// consecutive bms records (X/Y/Z). Any covariance rows immediately following +// the triplet are attached to the component using the input grammar. +json ClusterComponent(const vstn_t& stations, const it_vmsr_t& it_x) { + const it_vmsr_t it_y = it_x + 1; + const it_vmsr_t it_z = it_x + 2; + json component; + + component["First"] = Trimmed(stations.at(it_x->station1).stationName); + if (it_x->measType != 'Y') + component["Second"] = Trimmed(stations.at(it_x->station2).stationName); + component["X"] = it_x->term1; + component["Y"] = it_y->term1; + component["Z"] = it_z->term1; + component["SigmaXX"] = it_x->term2; + component["SigmaXY"] = it_y->term2; + component["SigmaXZ"] = it_z->term2; + component["SigmaYY"] = it_y->term3; + component["SigmaYZ"] = it_z->term3; + component["SigmaZZ"] = it_z->term4; + + const UINT32 covariance_count = it_x->vectorCount2; + if (covariance_count > 0) { + json covariances = json::array(); + it_vmsr_t it_cov = it_x + 3; + for (UINT32 cov = 0; cov < covariance_count; ++cov) { + covariances.push_back(CovarianceComponent(it_cov)); + it_cov += 3; + } + component[it_x->measType == 'Y' ? "PointCovariance" : "GPSCovariance"] = + covariances; + } + + return component; +} + +json CollapseSingletonArray(const json& values) { + return values.size() == 1 ? values.at(0) : values; +} + +// Build a full G/X/Y cluster record. The iterator must point to the first X +// component of the cluster. Each element is encoded in the same array shape the +// JSONL parser consumes, preserving Total and inter-component covariance rows. +json ClusterMeasurement(const vstn_t& stations, const it_vmsr_t& it_cluster) { + json m; + m["Type"] = std::string(1, it_cluster->measType); + AddObservationEpoch(m, *it_cluster); + if (it_cluster->ignore) + m["Ignore"] = true; + m["First"] = Trimmed(stations.at(it_cluster->station1).stationName); + // Y-clusters store a single station per point; G/X baselines reference a + // Second station on the far end of the first baseline. Child entries carry + // their own station names, which is what the parser uses for X clusters. + if (it_cluster->measType != 'Y') + m["Second"] = Trimmed(stations.at(it_cluster->station2).stationName); + + const UINT32 cluster_count = it_cluster->vectorCount1; + m["Total"] = cluster_count; + + json components = json::array(); + json adjusted = json::array(); + json correction = json::array(); + json adjusted_precision = json::array(); + json nstat = json::array(); + json tstat = json::array(); + json pelzer = json::array(); + + it_vmsr_t it_x = it_cluster; + for (UINT32 cluster_msr = 0; cluster_msr < cluster_count; ++cluster_msr) { + components.push_back(ClusterComponent(stations, it_x)); + adjusted.push_back(ComponentTriple(it_x, &measurement_t::measAdj)); + correction.push_back(ComponentTriple(it_x, &measurement_t::measCorr)); + adjusted_precision.push_back( + ComponentTriple(it_x, &measurement_t::measAdjPrec)); + nstat.push_back(ComponentTriple(it_x, &measurement_t::NStat)); + tstat.push_back(ComponentTriple(it_x, &measurement_t::TStat)); + pelzer.push_back(ComponentTriple(it_x, &measurement_t::PelzerRel)); + + it_x += 3 + (it_x->vectorCount2 * 3); + } + + if (it_cluster->measType == 'Y') { + const std::string coords = Trimmed(it_cluster->coordType); + if (!coords.empty()) + m["Coords"] = coords; + m["Clusterpoint"] = components; + } else { + m["GPSBaseline"] = components; + } + + m["Adjusted"] = CollapseSingletonArray(adjusted); + m["Correction"] = CollapseSingletonArray(correction); + m["AdjustedPrecision"] = CollapseSingletonArray(adjusted_precision); + m["NStat"] = CollapseSingletonArray(nstat); + m["TStat"] = CollapseSingletonArray(tstat); + m["PelzerRel"] = CollapseSingletonArray(pelzer); + return m; +} + +} // namespace + +DynAdjustJsonPrinter::DynAdjustJsonPrinter(dna_adjust& adjust_instance) + : DynAdjustPrinter(adjust_instance) {} + +DynAdjustJsonPrinter::~DynAdjustJsonPrinter() { CloseStreams(); } + +// Serialise the record outside the lock so concurrent hooks can dump in +// parallel; only the stream write is serialised. +static void WriteRecord(DynAdjustJsonPrinter::JsonStream& js, + const json& record) { + std::string line = record.dump(); + line.push_back('\n'); + std::lock_guard lock(js.mu); + if (js.stream.is_open()) + js.stream.write(line.data(), static_cast(line.size())); +} + +void DynAdjustJsonPrinter::OpenStreams() { + const auto& o = adjust_.projectSettings_.o; + + // Route file_opener failure through SignalExceptionAdjustment so a JSONL + // open error surfaces via the same channel as text-report open errors. + // SignalExceptionAdjustment throws, so control never returns past the + // catch; any already-opened streams are torn down by ~DynAdjustJsonPrinter. + auto open_with_header = [this](JsonStream& js, + const std::string& path, + const char* report_kind) { + if (path.empty()) + return; + try { + file_opener(js.stream, path); + } catch (const std::runtime_error& e) { + adjust_.SignalExceptionAdjustment(e.what(), 0); + } + WriteRecord(js, HeaderRecord(adjust_.datum_, report_kind)); + }; + + open_with_header(adj_, o._adj_json_file, "adj"); + open_with_header(xyz_, o._xyz_json_file, "xyz"); + open_with_header(apu_, o._apu_json_file, "apu"); + open_with_header(cor_, o._cor_json_file, "cor"); + open_with_header(m2s_, o._m2s_json_file, "m2s"); +} + +void DynAdjustJsonPrinter::CloseStreams() { + auto close_if_open = [](JsonStream& js) { + if (js.stream.is_open()) + js.stream.close(); + }; + close_if_open(adj_); + close_if_open(xyz_); + close_if_open(apu_); + close_if_open(cor_); + close_if_open(m2s_); +} + +void DynAdjustJsonPrinter::OnAdjustedStation(ReportKind kind, + const it_vstn_t& stn_it, const matrix_2d* estimates, + const matrix_2d* variances, const UINT32& mat_idx, + const AdjustedStationContext& ctx) { + (void)variances; + const AdjustedPosition p = PositionFromContext(estimates, mat_idx, ctx); + + json station = StationIdentity(stn_it); + // StationCoord carries the adjusted Lat/Lon/H in the same encoding used + // by the JSONL input parser, so the emitted record can be re-ingested as + // initial inputs for a follow-on adjustment. + station["StationCoord"] = StationCoordLLH(stn_it, p); + station["Initial"] = InitialCoords(stn_it); + station["Adjusted"] = AdjustedCoords(p); + station["Uncertainty"] = + UncertaintyBlockFromCart(stn_it, ctx.var_cart, ctx.var_local); + + const json record{{"DnaStation", station}}; + + // Route based on the caller-declared kind. Works under staged-mode where + // the text sink is a stringstream rather than the real file stream. + switch (kind) { + case ReportKind::kAdj: WriteRecord(adj_, record); break; + case ReportKind::kXyz: WriteRecord(xyz_, record); break; + } +} + +void DynAdjustJsonPrinter::OnAdjustedMeasurement(const it_vmsr_t& it_msr) { + // Cluster types (G single baseline, X baseline cluster, Y point cluster) + // contain one or more X/Y/Z triplets plus covariance rows. Emit one JSONL + // DnaMeasurement for the full cluster. Everything else is a scalar. + const bool is_cluster = + it_msr->measType == 'G' || it_msr->measType == 'X' || + it_msr->measType == 'Y'; + const json body = + is_cluster ? ClusterMeasurement(adjust_.bstBinaryRecords_, it_msr) + : (it_msr->measType == 'D' + ? DirectionSetJson(adjust_.bstBinaryRecords_, it_msr) + : ScalarMeasurement(adjust_.bstBinaryRecords_, + it_msr)); + WriteRecord(adj_, json{{"DnaMeasurement", body}}); +} + +void DynAdjustJsonPrinter::OnPositionalUncertainty(const it_vstn_t& stn_it, + const matrix_2d* variances, const UINT32& mat_idx, + const PositionalUncertaintyContext& ctx) { + (void)variances; + (void)mat_idx; + json station = StationIdentity(stn_it); + station["Uncertainty"] = + UncertaintyBlockWithGeoid(stn_it, ctx.var_cart, ctx.var_local); + WriteRecord(apu_, json{{"DnaStation", station}}); +} + +void DynAdjustJsonPrinter::OnStationCorrection(const UINT32& block, + const it_vstn_t& stn_it, const matrix_2d* estimates, + const UINT32& mat_idx, const StationCorrectionContext& ctx) { + (void)estimates; + (void)mat_idx; + // Corrections are computed per-block; drop the record if the block index + // is out of range rather than emitting a malformed DnaStation with no + // Corrections payload. + if (block >= adjust_.v_originalStations_.size()) + return; + + json station = StationIdentity(stn_it); + station["Initial"] = InitialCoords(stn_it); + station["Corrections"] = CorrectionsBlockFromContext(ctx); + WriteRecord(cor_, json{{"DnaStation", station}}); +} + +void DynAdjustJsonPrinter::OnM2SRecord(const it_vstn_t& stn_it, + MsrTally& tally) { + json station = StationIdentity(stn_it); + + // Enumerate the 20 canonical MsrTally fields directly rather than going + // through FillMsrList + MeasurementCount's switch-per-character. + station["MsrCounts"] = json{ + {"A", tally.A}, {"B", tally.B}, {"C", tally.C}, {"D", tally.D}, + {"E", tally.E}, {"G", tally.G}, {"H", tally.H}, {"I", tally.I}, + {"J", tally.J}, {"K", tally.K}, {"L", tally.L}, {"M", tally.M}, + {"P", tally.P}, {"Q", tally.Q}, {"R", tally.R}, {"S", tally.S}, + {"V", tally.V}, {"X", tally.X}, {"Y", tally.Y}, {"Z", tally.Z}}; + // totalCount is populated by CreateMsrToStnTally before this hook fires, + // so the cached value is current. + station["TotalMeasurements"] = tally.totalCount; + WriteRecord(m2s_, json{{"DnaStation", station}}); +} + +void DynAdjustJsonPrinter::OnStatistics() { + json stats; + // When _adj_stat_iteration is enabled, PrintStatistics also fires after + // each iteration before the terminal call, so include the iteration + // number with each record; consumers can pick the largest for the final + // outcome and reconstruct the convergence history from the rest. + stats["iteration"] = adjust_.CurrentIteration(); + stats["unknown_parameters"] = adjust_.unknownParams_; + stats["measurement_params"] = adjust_.measurementParams_; + stats["potential_outliers"] = adjust_.potentialOutlierCount_; + stats["dof"] = adjust_.degreesofFreedom_; + stats["chisq"] = adjust_.chiSquared_; + stats["sigma_zero"] = adjust_.sigmaZero_; + stats["global_pelzer"] = adjust_.globalPelzerReliability_; + stats["chisq_lower"] = adjust_.chiSquaredLowerLimit_; + stats["chisq_upper"] = adjust_.chiSquaredUpperLimit_; + stats["confidence_interval"] = + adjust_.projectSettings_.a.confidence_interval; + + const char* result = "unknown"; + if (adjust_.degreesofFreedom_ < 1) + result = "no_redundancy"; + else { + switch (adjust_.passFail_) { + case test_stat_pass: + result = "passed"; + break; + case test_stat_warning: + result = "warning"; + break; + case test_stat_fail: + result = "failed"; + break; + } + } + stats["chisq_test"] = result; + WriteRecord(adj_, json{{"DnaStatistics", stats}}); + + // Flush every open stream so a subsequent crash or signal does not lose + // JSONL lines buffered by the C++ stream layer. We flush on every + // statistics fire (terminal + per-iteration when enabled) — flush is + // cheap relative to the adjustment iteration that just completed. + for (JsonStream* js : {&adj_, &xyz_, &apu_, &cor_, &m2s_}) { + std::lock_guard lock(js->mu); + if (js->stream.is_open()) + js->stream.flush(); + } +} + +} // namespace networkadjust +} // namespace dynadjust diff --git a/dynadjust/dynadjust/dnaadjust/dnaadjust_json_printer.hpp b/dynadjust/dynadjust/dnaadjust/dnaadjust_json_printer.hpp new file mode 100644 index 000000000..13342371a --- /dev/null +++ b/dynadjust/dynadjust/dnaadjust/dnaadjust_json_printer.hpp @@ -0,0 +1,108 @@ +//============================================================================ +// Name : dnaadjust_json_printer.hpp +// Author : Dale Roberts +// Copyright : Copyright 2026 Geoscience Australia +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Description : JSONL side-channel printer emitted alongside the default +// DynAdjust text reports. Subclass of DynAdjustPrinter that +// overrides the virtual hooks to write one JSON object per +// line for each adjusted record, using the same key names as +// the JSONL import parser so output can round-trip. +//============================================================================ + +#ifndef DNAADJUST_JSON_PRINTER_H_ +#define DNAADJUST_JSON_PRINTER_H_ + +#if defined(_MSC_VER) +#if defined(LIST_INCLUDES_ON_BUILD) +#pragma message(" " __FILE__) +#endif +#endif + +#include +#include + +#include + +#include + +namespace dynadjust { +namespace networkadjust { + +class DNAADJUST_API DynAdjustJsonPrinter : public DynAdjustPrinter { + public: + explicit DynAdjustJsonPrinter(dna_adjust& adjust_instance); + ~DynAdjustJsonPrinter() override; + + // Open each JSONL output stream whose text sibling is enabled for this + // run and write the per-file header line. Must be called after the + // printer is constructed but before any Print* method is invoked. + void OpenStreams(); + + // Flush and close any open JSONL output streams. Called from the + // destructor so callers do not need to invoke it explicitly. + void CloseStreams(); + + // Pairs an output stream with its serialising mutex. One instance per + // report file. Public so helpers in the .cpp translation unit (which + // is where nlohmann::json is in scope) can hold references — the + // members themselves stay private. Concurrent adjustment threads + // writing to different sibling files don't serialise on a single lock, + // and the critical section is only the stream write (the JSON dump + // happens outside). Non-copyable and non-movable because std::mutex is. + struct JsonStream { + std::ofstream stream; + std::mutex mu; + + JsonStream() = default; + JsonStream(const JsonStream&) = delete; + JsonStream& operator=(const JsonStream&) = delete; + JsonStream(JsonStream&&) = delete; + JsonStream& operator=(JsonStream&&) = delete; + }; + + protected: + void OnAdjustedStation(ReportKind kind, + const it_vstn_t& stn_it, + const matrix_2d* estimates, + const matrix_2d* variances, + const UINT32& mat_idx, + const AdjustedStationContext& ctx) override; + void OnAdjustedMeasurement(const it_vmsr_t& it_msr) override; + void OnPositionalUncertainty(const it_vstn_t& stn_it, + const matrix_2d* variances, + const UINT32& mat_idx, + const PositionalUncertaintyContext& ctx) override; + void OnStationCorrection(const UINT32& block, + const it_vstn_t& stn_it, + const matrix_2d* estimates, + const UINT32& mat_idx, + const StationCorrectionContext& ctx) override; + void OnM2SRecord(const it_vstn_t& stn_it, + MsrTally& stn_msr_tally) override; + void OnStatistics() override; + + private: + JsonStream adj_; + JsonStream xyz_; + JsonStream apu_; + JsonStream cor_; + JsonStream m2s_; +}; + +} // namespace networkadjust +} // namespace dynadjust + +#endif // DNAADJUST_JSON_PRINTER_H_ diff --git a/dynadjust/dynadjust/dnaadjust/dnaadjust_printer.cpp b/dynadjust/dynadjust/dnaadjust/dnaadjust_printer.cpp index 0d40a6c4f..263c2fe24 100644 --- a/dynadjust/dynadjust/dnaadjust/dnaadjust_printer.cpp +++ b/dynadjust/dynadjust/dnaadjust/dnaadjust_printer.cpp @@ -80,22 +80,13 @@ void DynAdjustPrinter::PrintIteration(const UINT32& iteration) { } void DynAdjustPrinter::PrintAdjustmentTime(cpu_timer& time, int timer_type) { - // calculate and print total time - auto elapsed = time.elapsed(); - double seconds = elapsed.wall.count() / 1.0e9; - - std::stringstream ss; - if (seconds >= 1.0) { - ss << std::fixed << std::setprecision(3) << seconds << "s"; - } else { - ss << std::fixed << std::setprecision(3) << (seconds * 1000.0) << "ms"; - } + std::string formatted = FormatElapsedTime(time.elapsed().wall.count() / 1.0e9); if (timer_type == 0) // iteration_time equivalent - adjust_.adj_file << std::setw(PRINT_VAR_PAD) << std::left << "Elapsed time" << ss.str() << std::endl; + adjust_.adj_file << std::setw(PRINT_VAR_PAD) << std::left << "Elapsed time" << formatted << std::endl; else { - adjust_.adj_file << std::setw(PRINT_VAR_PAD) << std::left << "Total time" << ss.str() << std::endl << std::endl; + adjust_.adj_file << std::setw(PRINT_VAR_PAD) << std::left << "Total time" << formatted << std::endl << std::endl; } } @@ -547,18 +538,18 @@ void DynAdjustPrinter::PrintAdjustedNetworkStations() { switch (adjust_.projectSettings_.a.adjust_mode) { case SimultaneousMode: - PrintAdjStations(adjust_.adj_file, 0, &adjust_.v_estimatedStations_.at(0), &adjust_.v_rigorousVariances_.at(0), false, true, true, printHeader, true); - PrintAdjStations(adjust_.xyz_file, 0, &adjust_.v_estimatedStations_.at(0), &adjust_.v_rigorousVariances_.at(0), false, false, false, printHeader, false); + PrintAdjStations(ReportKind::kAdj, adjust_.adj_file, 0, &adjust_.v_estimatedStations_.at(0), &adjust_.v_rigorousVariances_.at(0), false, true, true, printHeader, true); + PrintAdjStations(ReportKind::kXyz, adjust_.xyz_file, 0, &adjust_.v_estimatedStations_.at(0), &adjust_.v_rigorousVariances_.at(0), false, false, false, printHeader, false); break; case PhasedMode: case Phased_Block_1Mode: // Output phased blocks as a single block? if (!adjust_.projectSettings_.o._output_stn_blocks) { - PrintAdjStationsUniqueList(adjust_.adj_file, + PrintAdjStationsUniqueList(ReportKind::kAdj, adjust_.adj_file, &adjust_.v_rigorousStations_, &adjust_.v_rigorousVariances_, true, true, true); - PrintAdjStationsUniqueList(adjust_.xyz_file, + PrintAdjStationsUniqueList(ReportKind::kXyz, adjust_.xyz_file, &adjust_.v_rigorousStations_, &adjust_.v_rigorousVariances_, true, true, false); @@ -573,8 +564,8 @@ void DynAdjustPrinter::PrintAdjustedNetworkStations() { adjust_.DeserialiseBlockFromMappedFile(block, 1, sf_original_stns); // Print using original detailed function for staging compatibility - PrintAdjStations(adjust_.adj_file, block, &adjust_.v_rigorousStations_.at(block), &adjust_.v_rigorousVariances_.at(block), true, true, true, printHeader, true); - PrintAdjStations(adjust_.xyz_file, block, &adjust_.v_rigorousStations_.at(block), &adjust_.v_rigorousVariances_.at(block), true, false, false, printHeader, false); + PrintAdjStations(ReportKind::kAdj, adjust_.adj_file, block, &adjust_.v_rigorousStations_.at(block), &adjust_.v_rigorousVariances_.at(block), true, true, true, printHeader, true); + PrintAdjStations(ReportKind::kXyz, adjust_.xyz_file, block, &adjust_.v_rigorousStations_.at(block), &adjust_.v_rigorousVariances_.at(block), true, false, false, printHeader, false); printHeader = false; // Release block from memory @@ -589,8 +580,8 @@ void DynAdjustPrinter::PrintAdjustedNetworkStations() { } else { // Print stations for each block without staging for (UINT32 block = 0; block < adjust_.blockCount_; ++block) { - PrintAdjStations(adjust_.adj_file, block, &adjust_.v_estimatedStations_.at(block), &adjust_.v_rigorousVariances_.at(block), true, true, true, printHeader, true); - PrintAdjStations(adjust_.xyz_file, block, &adjust_.v_estimatedStations_.at(block), &adjust_.v_rigorousVariances_.at(block), true, false, false, printHeader, false); + PrintAdjStations(ReportKind::kAdj, adjust_.adj_file, block, &adjust_.v_estimatedStations_.at(block), &adjust_.v_rigorousVariances_.at(block), true, true, true, printHeader, true); + PrintAdjStations(ReportKind::kXyz, adjust_.xyz_file, block, &adjust_.v_estimatedStations_.at(block), &adjust_.v_rigorousVariances_.at(block), true, false, false, printHeader, false); printHeader = false; // Exit if block-1 mode @@ -722,6 +713,8 @@ void DynAdjustPrinter::PrintStatistics(bool printPelzer) { ss << " ***"; } adjust_.adj_file << std::setw(PASS_FAIL) << std::right << ss.str() << std::endl << std::endl; + + OnStatistics(); } void DynAdjustPrinter::PrintMeasurementsToStation() { @@ -776,8 +769,11 @@ void DynAdjustPrinter::PrintMeasurementsToStation() { } // Print measurements to each station and the total count for each station - for (_it_stn=vStationList.begin(); _it_stn != vStationList.end(); ++_it_stn) + for (_it_stn=vStationList.begin(); _it_stn != vStationList.end(); ++_it_stn) { adjust_.v_stnmsrTally_.at(*_it_stn).coutSummaryMsrToStn(adjust_.adj_file, adjust_.bstBinaryRecords_.at(*_it_stn).stationName); + it_vstn_t stn_it = adjust_.bstBinaryRecords_.begin() + *_it_stn; + OnM2SRecord(stn_it, adjust_.v_stnmsrTally_.at(*_it_stn)); + } // Print "the bottom line" MsrToStnSummaryHeaderLine(adjust_.adj_file); @@ -871,9 +867,10 @@ void DynAdjustPrinter::PrintAdjMeasurements_A(it_vmsr_t& _it_msr) { adjust_.adj_file << std::left << std::setw(STATION) << adjust_.bstBinaryRecords_.at(_it_msr->station1).stationName; adjust_.adj_file << std::left << std::setw(STATION) << adjust_.bstBinaryRecords_.at(_it_msr->station2).stationName; adjust_.adj_file << std::left << std::setw(STATION) << adjust_.bstBinaryRecords_.at(_it_msr->station3).stationName; - // Print angular measurement, taking care of user requirements for + // Print angular measurement, taking care of user requirements for // type, format and precision PrintAdjMeasurementsAngular(' ', _it_msr); + OnAdjustedMeasurement(_it_msr); } void DynAdjustPrinter::PrintAdjMeasurements_BKVZ(it_vmsr_t& _it_msr) { @@ -881,9 +878,10 @@ void DynAdjustPrinter::PrintAdjMeasurements_BKVZ(it_vmsr_t& _it_msr) { adjust_.adj_file << std::left << std::setw(STATION) << adjust_.bstBinaryRecords_.at(_it_msr->station1).stationName; adjust_.adj_file << std::left << std::setw(STATION) << adjust_.bstBinaryRecords_.at(_it_msr->station2).stationName; adjust_.adj_file << std::left << std::setw(STATION) << " "; - // Print angular measurement, taking care of user requirements for + // Print angular measurement, taking care of user requirements for // type, format and precision PrintAdjMeasurementsAngular(' ', _it_msr); + OnAdjustedMeasurement(_it_msr); } void DynAdjustPrinter::PrintAdjMeasurements_CELMS(it_vmsr_t& _it_msr) { @@ -892,6 +890,7 @@ void DynAdjustPrinter::PrintAdjMeasurements_CELMS(it_vmsr_t& _it_msr) { adjust_.adj_file << std::left << std::setw(STATION) << adjust_.bstBinaryRecords_.at(_it_msr->station2).stationName; adjust_.adj_file << std::left << std::setw(STATION) << " "; PrintAdjMeasurementsLinear(' ', _it_msr); + OnAdjustedMeasurement(_it_msr); } void DynAdjustPrinter::PrintAdjMeasurements_HR(it_vmsr_t& _it_msr) { @@ -901,6 +900,7 @@ void DynAdjustPrinter::PrintAdjMeasurements_HR(it_vmsr_t& _it_msr) { adjust_.adj_file << std::left << std::setw(STATION) << " "; PrintAdjMeasurementsLinear(' ', _it_msr); + OnAdjustedMeasurement(_it_msr); } void DynAdjustPrinter::PrintAdjMeasurements_IJPQ(it_vmsr_t& _it_msr) { @@ -908,12 +908,15 @@ void DynAdjustPrinter::PrintAdjMeasurements_IJPQ(it_vmsr_t& _it_msr) { adjust_.adj_file << std::left << std::setw(STATION) << adjust_.bstBinaryRecords_.at(_it_msr->station1).stationName; adjust_.adj_file << std::left << std::setw(STATION) << " "; adjust_.adj_file << std::left << std::setw(STATION) << " "; - // Print angular measurement, taking care of user requirements for + // Print angular measurement, taking care of user requirements for // type, format and precision PrintAdjMeasurementsAngular(' ', _it_msr); + OnAdjustedMeasurement(_it_msr); } void DynAdjustPrinter::PrintAdjMeasurements_D(it_vmsr_t& _it_msr) { + const it_vmsr_t direction_set_start = _it_msr; + // normal format adjust_.adj_file << std::left << std::setw(STATION) << adjust_.bstBinaryRecords_.at(_it_msr->station1).stationName; adjust_.adj_file << std::left << std::setw(STATION) << adjust_.bstBinaryRecords_.at(_it_msr->station2).stationName; @@ -959,14 +962,16 @@ void DynAdjustPrinter::PrintAdjMeasurements_D(it_vmsr_t& _it_msr) { adjust_.adj_file << std::left << std::setw(PAD2) << " "; // measurement type adjust_.adj_file << std::left << std::setw(STATION) << " "; // station1 (Instrument) adjust_.adj_file << std::left << std::setw(STATION) << " "; // station2 (RO) - adjust_.adj_file << std::left << std::setw(STATION) << + adjust_.adj_file << std::left << std::setw(STATION) << adjust_.bstBinaryRecords_.at(_it_msr->station2).stationName; // target - // Print angular measurement, taking care of user requirements for - // type, format and precision + // Print angular measurement, taking care of user requirements for + // type, format and precision PrintAdjMeasurementsAngular(' ', _it_msr); _it_msr++; } + + OnAdjustedMeasurement(direction_set_start); } void DynAdjustPrinter::PrintCompMeasurements_YLLH(it_vmsr_t& _it_msr, UINT32& design_row) { @@ -1426,7 +1431,7 @@ void DynAdjustPrinter::PrintUniqueStationsList(std::ostream& os, os << "Unique stations list printed using existing detailed implementation." << std::endl; } -void DynAdjustPrinter::PrintAdjStationsUniqueList(std::ostream& os, +void DynAdjustPrinter::PrintAdjStationsUniqueList(ReportKind kind, std::ostream& os, const v_mat_2d* stationEstimates, v_mat_2d* stationVariances, bool recomputeGeographicCoords, bool updateGeographicCoords, bool reapplyTypeBUncertainties) { @@ -1476,9 +1481,9 @@ void DynAdjustPrinter::PrintAdjStationsUniqueList(std::ostream& os, mat_index = _it_bsmu->first.second * 3; // Use the refactored PrintAdjStation through the main class - PrintAdjStation(*outstream, - block, stn, mat_index, - &stationEstimates->at(block), &stationVariances->at(block), + PrintAdjStation(kind, *outstream, + block, stn, mat_index, + &stationEstimates->at(block), &stationVariances->at(block), recomputeGeographicCoords, updateGeographicCoords, reapplyTypeBUncertainties); @@ -1570,7 +1575,7 @@ void DynAdjustPrinter::PrintStationAdjustmentResults(std::ostream& os, const UIN // Delegate to existing implementation for complex coordinate transformations // and uncertainty calculations while using our new coordinate formatters - PrintAdjStation(os, block, stn, mat_idx, estimates, variances, true, false, true); + PrintAdjStation(ReportKind::kAdj, os, block, stn, mat_idx, estimates, variances, true, false, true); } // Enhanced coordinate transformation utilities for PrintAdjStation refactoring @@ -2014,14 +2019,16 @@ void DynAdjustPrinter::PrintCompMeasurements(const UINT32& block, const std::str adjust_.adj_file << std::endl << std::endl; } -// Helper function that consolidates measurement printing logic +// Helper function that consolidates measurement printing logic. JSON +// hooks fire from the leaf PrintAdjMeasurements_* methods (not here), so +// cluster records emit one JSONL record per component. void DynAdjustPrinter::PrintMeasurementRecords(const v_uint32_u32u32_pair& msr_block, bool adjustedMeasurements) { it_vmsr_t _it_msr; UINT32 clusterID(MAX_UINT32_VALUE); for (auto _it_block_msr = msr_block.begin(); _it_block_msr != msr_block.end(); ++_it_block_msr) { _it_msr = adjust_.bmsBinaryRecords_.begin() + (_it_block_msr->first); - + // Skip non-measurement elements (Y, Z, covariance components) if (_it_msr->measStart != xMeas) continue; @@ -2632,22 +2639,27 @@ void DynAdjustPrinter::PrintAdjMeasurements_YLLH(it_vmsr_t& _it_msr) // Print height _it_y_msr++; switch (_it_msr->station3) - { - case LLh_type_i: + { + case LLh_type_i: PrintAdjMeasurementsLinear('h', _it_y_msr); break; case LLH_type_i: default: PrintAdjMeasurementsLinear('H', _it_y_msr); break; - } + } // skip covariances until next point _it_y_msr += covariance_count * 3; - + if (covariance_count > 0) _it_y_msr++; } + + // Emit the JSONL side-channel once for the whole Y cluster. The temporary + // y_msr vector above is only for text output; JSONL preserves the input + // grammar by walking the original binary cluster, including covariances. + OnAdjustedMeasurement(_it_msr_begin); } void DynAdjustPrinter::PrintPositionalUncertainty() @@ -3401,14 +3413,15 @@ void DynAdjustPrinter::PrintBlockStations(std::ostream& os, const UINT32& block, UINT32 mat_idx, stn; - // Print stations according to the user-defined sort order + // PrintBlockStations is only called against adj_file, so route the + // JSON hook to the .adj sibling. for (UINT32 i(0); ifirst.first; mat_index = _it_bsmu->first.second * 3; - PrintAdjStation(*outstream, + PrintAdjStation(ReportKind::kAdj, *outstream, block, stn, mat_index, - &estimates->at(block), &stationVariances->at(block), + &estimates->at(block), &stationVariances->at(block), recomputeGeographicCoords, updateGeographicCoords, reapplyTypeBUncertainties); @@ -3901,7 +3914,7 @@ bool DynAdjustPrinter::IgnoredMeasurementContainsInvalidStation(pit_vmsr_t _it_m return true; } -void DynAdjustPrinter::PrintAdjStation(std::ostream& os, +void DynAdjustPrinter::PrintAdjStation(ReportKind kind, std::ostream& os, const UINT32& block, const UINT32& stn, const UINT32& mat_idx, const matrix_2d* stationEstimates, matrix_2d* stationVariances, bool recomputeGeographicCoords, bool updateGeographicCoords, @@ -4050,6 +4063,10 @@ void DynAdjustPrinter::PrintAdjStation(std::ostream& os, // description os << std::setw(PAD2) << " " << std::left << stn_it->description; os << std::endl; + + AdjustedStationContext ctx{ + estLatitude, estLongitude, estHeight, var_cart, var_local}; + OnAdjustedStation(kind, stn_it, stationEstimates, stationVariances, mat_idx, ctx); } void DynAdjustPrinter::PrintAdjMeasurements_GXY(it_vmsr_t& _it_msr, const uint32_uint32_pair& b_pam) @@ -4066,6 +4083,7 @@ void DynAdjustPrinter::PrintAdjMeasurements_GXY(it_vmsr_t& _it_msr, const uint32 } } + const it_vmsr_t cluster_start = _it_msr; UINT32 cluster_msr, cluster_count(_it_msr->vectorCount1), covariance_count; bool nextElement(false); @@ -4080,7 +4098,7 @@ void DynAdjustPrinter::PrintAdjMeasurements_GXY(it_vmsr_t& _it_msr, const uint32 // first station adjust_.adj_file << std::left << std::setw(STATION) << adjust_.bstBinaryRecords_.at(_it_msr->station1).stationName; - + // Print second station? switch (_it_msr->measType) { @@ -4094,7 +4112,7 @@ void DynAdjustPrinter::PrintAdjMeasurements_GXY(it_vmsr_t& _it_msr, const uint32 // third station adjust_.adj_file << std::left << std::setw(STATION) << " "; - + // Print adjusted GNSS baseline measurements in alternate units? if (adjust_.projectSettings_.o._adj_gnss_units != XYZ_adj_gnss_ui && _it_msr->measType != 'Y') @@ -4103,22 +4121,26 @@ void DynAdjustPrinter::PrintAdjMeasurements_GXY(it_vmsr_t& _it_msr, const uint32 { // Print X PrintAdjMeasurementsLinear('X', _it_msr); - + // Print Y - _it_msr++; + _it_msr++; PrintAdjMeasurementsLinear('Y', _it_msr); // Print Z - _it_msr++; + _it_msr++; PrintAdjMeasurementsLinear('Z', _it_msr); } // skip covariances until next baseline _it_msr += covariance_count * 3; - + if (covariance_count > 0) _it_msr++; } + + // Emit the JSONL side-channel once for the whole GNSS cluster, not once per + // baseline. This preserves Total and all cross-covariance records. + OnAdjustedMeasurement(cluster_start); } void DynAdjustPrinter::PrintCorStation(std::ostream& os, @@ -4194,15 +4216,19 @@ void DynAdjustPrinter::PrintCorStation(std::ostream& os, std::setw(MSR) << std::setprecision(adjust_.PRECISION_MTR_STN) << std::fixed << std::right << horiz_distance; if (adjust_.isAdjustmentQuestionable_) - os << - StringFromTW(local_12e, HEIGHT, adjust_.PRECISION_MTR_STN) << - StringFromTW(local_12n, HEIGHT, adjust_.PRECISION_MTR_STN) << + os << + StringFromTW(local_12e, HEIGHT, adjust_.PRECISION_MTR_STN) << + StringFromTW(local_12n, HEIGHT, adjust_.PRECISION_MTR_STN) << StringFromTW(local_12up, HEIGHT, adjust_.PRECISION_MTR_STN) << std::endl; else - os << - std::setw(HEIGHT) << std::setprecision(adjust_.PRECISION_MTR_STN) << std::fixed << std::right << local_12e << - std::setw(HEIGHT) << std::setprecision(adjust_.PRECISION_MTR_STN) << std::fixed << std::right << local_12n << + os << + std::setw(HEIGHT) << std::setprecision(adjust_.PRECISION_MTR_STN) << std::fixed << std::right << local_12e << + std::setw(HEIGHT) << std::setprecision(adjust_.PRECISION_MTR_STN) << std::fixed << std::right << local_12n << std::setw(HEIGHT) << std::setprecision(adjust_.PRECISION_MTR_STN) << std::fixed << std::right << local_12up << std::endl; + + it_vstn_t stn_it = adjust_.bstBinaryRecords_.begin() + stn; + StationCorrectionContext ctx{local_12e, local_12n, local_12up}; + OnStationCorrection(block, stn_it, stationEstimates, mat_index, ctx); } void DynAdjustPrinter::PrintCorStations(std::ostream& cor_file, const UINT32& block) @@ -4399,7 +4425,20 @@ void DynAdjustPrinter::PrintPosUncertainty(std::ostream& os, const UINT32& block else os << std::setprecision(PRECISION_UNCERTAINTY) << std::setw(MSR) << variances->get(2, 2) << std::endl; // up - + + { + it_vstn_t stn_it = adjust_.bstBinaryRecords_.begin() + stn; + PositionalUncertaintyContext ctx{ + stn_it->currentLatitude, + stn_it->currentLongitude, + stn_it->currentHeight, + variances_cart, + variances_local, + semimajor, semiminor, azimuth, + hzPosU, vtPosU}; + OnPositionalUncertainty(stn_it, stationVariances, mat_idx, ctx); + } + if (!adjust_.projectSettings_.o._output_pu_covariances) return; @@ -4511,7 +4550,10 @@ void DynAdjustPrinter::UpdateGNSSNstatsForAlternateUnits(const v_uint32_u32u32_p covariance_count = _it_msr->vectorCount2; // Load precision of adjusted measurements for staged adjustments - if (adjust_.projectSettings_.a.stage) + const bool loadedPrecAdjMsrs = + adjust_.projectSettings_.a.stage && + adjust_.v_precAdjMsrsFull_.at(b_pam.first).getbuffer() == nullptr; + if (loadedPrecAdjMsrs) adjust_.DeserialiseBlockFromMappedFile(b_pam.first, 1, sf_prec_adj_msrs); double lower_mtx_buffer[6]; @@ -4521,7 +4563,7 @@ void DynAdjustPrinter::UpdateGNSSNstatsForAlternateUnits(const v_uint32_u32u32_p matrix_2d var_adj_cart(3, 3, lower_mtx_buffer, 6, mtx_lower); var_adj_cart.fillupper(); - if (adjust_.projectSettings_.a.stage) + if (loadedPrecAdjMsrs) adjust_.UnloadBlock(b_pam.first, 1, sf_prec_adj_msrs); // Get X component @@ -4683,7 +4725,10 @@ void DynAdjustPrinter::PrintAdjGNSSAlternateUnits(it_vmsr_t& _it_msr, const uint matrix_2d var_adj_local(3, 3), var_adj_polar(3, 3); // For staged adjustments, load precision of adjusted measurements - if (adjust_.projectSettings_.a.stage) + const bool loadedPrecAdjMsrs = + adjust_.projectSettings_.a.stage && + adjust_.v_precAdjMsrsFull_.at(b_pam.first).getbuffer() == nullptr; + if (loadedPrecAdjMsrs) adjust_.DeserialiseBlockFromMappedFile(b_pam.first, 1, sf_prec_adj_msrs); // get precision of adjusted measurements @@ -4692,7 +4737,7 @@ void DynAdjustPrinter::PrintAdjGNSSAlternateUnits(it_vmsr_t& _it_msr, const uint matrix_2d var_adj_cart(3, 3, lower_mtx_buffer, 6, mtx_lower); var_adj_cart.fillupper(); - if (adjust_.projectSettings_.a.stage) + if (loadedPrecAdjMsrs) // For staged adjustments, unload precision of adjusted measurements adjust_.UnloadBlock(b_pam.first, 1, sf_prec_adj_msrs); @@ -5212,7 +5257,7 @@ void DynAdjustPrinter::PrintCorStationsUniqueList(std::ostream& cor_file) ); } -void DynAdjustPrinter::PrintAdjStations(std::ostream& os, const UINT32& block, +void DynAdjustPrinter::PrintAdjStations(ReportKind kind, std::ostream& os, const UINT32& block, const matrix_2d* stationEstimates, matrix_2d* stationVariances, bool printBlockID, bool recomputeGeographicCoords, @@ -5275,13 +5320,13 @@ void DynAdjustPrinter::PrintAdjStations(std::ostream& os, const UINT32& block, { stn = v_blockStations.at(i); mat_idx = adjust_.v_blockStationsMap_.at(block)[stn] * 3; - PrintAdjStation(os, block, stn, mat_idx, - stationEstimates, stationVariances, + PrintAdjStation(kind, os, block, stn, mat_idx, + stationEstimates, stationVariances, recomputeGeographicCoords, updateGeographicCoords, reapplyTypeBUncertainties); } os << std::endl; - + // return sort order to alpha-numeric if (adjust_.projectSettings_.o._sort_stn_file_order) adjust_.SortStationsbyID(v_blockStations); diff --git a/dynadjust/dynadjust/dnaadjust/dnaadjust_printer.hpp b/dynadjust/dynadjust/dnaadjust/dnaadjust_printer.hpp index 5bfd658aa..d6f536325 100644 --- a/dynadjust/dynadjust/dnaadjust/dnaadjust_printer.hpp +++ b/dynadjust/dynadjust/dnaadjust/dnaadjust_printer.hpp @@ -87,6 +87,16 @@ enum class UncertaintyMode { Both }; +// Identifies which top-level report a station record is being written to. +// Threaded through PrintAdjStation / PrintAdjStations / PrintAdjStationsUniqueList +// so the JSONL side-channel can route records to the correct sibling file +// without comparing ostream addresses (which breaks under staged-mode, where +// the target is a stringstream flushed to the real file later). +enum class ReportKind { + kAdj, // adjustment report (.adj) + kXyz // coordinate-only report (.xyz) +}; + // Type traits for measurement classification template struct IsAngularMeasurement : std::false_type {}; @@ -99,6 +109,7 @@ class DNAADJUST_API DynAdjustPrinter { public: // Constructor taking dna_adjust reference for direct access to members explicit DynAdjustPrinter(dna_adjust& adjust_instance); + virtual ~DynAdjustPrinter() = default; // Template-based measurement printing template @@ -214,7 +225,7 @@ class DNAADJUST_API DynAdjustPrinter { const std::string& stn_coord_types, const UINT16& printStationCorrections); // Enhanced unique stations list processing - void PrintAdjStationsUniqueList(std::ostream& os, + void PrintAdjStationsUniqueList(ReportKind kind, std::ostream& os, const v_mat_2d* stationEstimates, v_mat_2d* stationVariances, bool recomputeGeographicCoords, bool updateGeographicCoords, bool reapplyTypeBUncertainties); @@ -232,7 +243,7 @@ class DNAADJUST_API DynAdjustPrinter { bool IgnoredMeasurementContainsInvalidStation(pit_vmsr_t _it_msr); // Enhanced station formatting - void PrintAdjStation(std::ostream& os, const UINT32& block, const UINT32& stn, const UINT32& mat_idx, + void PrintAdjStation(ReportKind kind, std::ostream& os, const UINT32& block, const UINT32& stn, const UINT32& mat_idx, const matrix_2d* stationEstimates, matrix_2d* stationVariances, bool recomputeGeographicCoords, bool updateGeographicCoords, bool reapplyTypeBUncertainties); @@ -244,7 +255,7 @@ class DNAADJUST_API DynAdjustPrinter { const matrix_2d* stationEstimates); void PrintCorStations(std::ostream& cor_file, const UINT32& block); void PrintCorStationsUniqueList(std::ostream& cor_file); - void PrintAdjStations(std::ostream& os, const UINT32& block, + void PrintAdjStations(ReportKind kind, std::ostream& os, const UINT32& block, const matrix_2d* stationEstimates, matrix_2d* stationVariances, bool printBlockID, bool recomputeGeographicCoords, @@ -272,9 +283,77 @@ class DNAADJUST_API DynAdjustPrinter { void ProcessBlockStaging(_it_u32u32_uint32_pair _it_bsmu, UINT32& block); void FinalizeBlockStaging(UINT32 block, v_uint32_string_pair& stationsOutput, std::ostream& os); - private: + // Precomputed values threaded to OnAdjustedStation. Populated by + // PrintAdjStation after its own computations so subclass overrides do + // not repeat CartToGeo / var_local rotation / geoid application. The + // matrix references are to locals in PrintAdjStation and are valid + // for the synchronous duration of the hook call. + struct AdjustedStationContext { + double lat; // radians + double lon; // radians + double height; // metres (ellipsoidal) + const matrix_2d& var_cart; // 3x3 + const matrix_2d& var_local; // 3x3, geoid variance already in (2,2) + }; + + // Precomputed values threaded to OnPositionalUncertainty. These + // match the text .apu output exactly: var_local has NOT had the + // geoid variance added, and ellipse/PU are computed from that + // var_local. Subclasses that want geoid-included values add it + // themselves (the JSON override does this to preserve .apu.jsonl + // numeric output). + struct PositionalUncertaintyContext { + double lat; + double lon; + double height; + const matrix_2d& var_cart; + const matrix_2d& var_local; // NO geoid added + double semimajor; + double semiminor; + double azimuth; + double hz_pos_u; + double vt_pos_u; + }; + + // Precomputed station-correction values threaded to OnStationCorrection. + // Populated only after PrintCorStation's vt/hz threshold gates pass, so + // overrides do not need to re-check thresholds. + struct StationCorrectionContext { + double cor_e; + double cor_n; + double cor_up; + }; + + protected: + // Virtual hooks for alternate output representations (e.g. JSONL). + // Default implementations are no-ops. ReportKind + block are passed + // explicitly because the target ostream may be a staging stringstream + // rather than the real sibling file. + + virtual void OnAdjustedStation(ReportKind kind, + const it_vstn_t& stn_it, + const matrix_2d* estimates, + const matrix_2d* variances, + const UINT32& mat_idx, + const AdjustedStationContext& ctx) {} + virtual void OnAdjustedMeasurement(const it_vmsr_t& it_msr) {} + virtual void OnPositionalUncertainty(const it_vstn_t& stn_it, + const matrix_2d* variances, + const UINT32& mat_idx, + const PositionalUncertaintyContext& ctx) {} + virtual void OnStationCorrection(const UINT32& block, + const it_vstn_t& stn_it, + const matrix_2d* estimates, + const UINT32& mat_idx, + const StationCorrectionContext& ctx) {} + virtual void OnM2SRecord(const it_vstn_t& stn_it, + MsrTally& stn_msr_tally) {} + virtual void OnStatistics() {} + dna_adjust& adjust_; + private: + // Helper functions constexpr int GetStationCount(char measurement_type) const; constexpr bool IsAngularType(char measurement_type) const; diff --git a/dynadjust/dynadjust/dnaadjust/network_data_loader.cpp b/dynadjust/dynadjust/dnaadjust/network_data_loader.cpp index bb23e27c5..03ad745d3 100644 --- a/dynadjust/dynadjust/dnaadjust/network_data_loader.cpp +++ b/dynadjust/dynadjust/dnaadjust/network_data_loader.cpp @@ -21,7 +21,9 @@ #include "network_data_loader.hpp" +#include #include +#include #include #include #include @@ -30,6 +32,20 @@ namespace dynadjust { namespace networkadjust { +namespace { + +bool FileExists(const std::string& filename) { + if (filename.empty()) { + return false; + } + + std::error_code ec; + const bool exists = std::filesystem::exists(filename, ec); + return !ec && exists; +} + +} // namespace + NetworkDataLoader::NetworkDataLoader(const project_settings &settings) : settings_(settings), bst_loader_(std::make_unique()), @@ -117,6 +133,10 @@ bool NetworkDataLoader::LoadForSimultaneous( } bool NetworkDataLoader::LoadStations(vstn_t *bstBinaryRecords, binary_file_meta_t &bst_meta, UINT32 &bstn_count) { + if (!FileExists(settings_.a.bst_file)) { + throw StationLoadException("Failed to load binary station file: " + settings_.a.bst_file); + } + auto result = bst_loader_->LoadWithOptional(settings_.a.bst_file, bstBinaryRecords, bst_meta); if (!result) { throw StationLoadException("Failed to load binary station file: " + settings_.a.bst_file); } bstn_count = static_cast(*result); @@ -124,6 +144,10 @@ bool NetworkDataLoader::LoadStations(vstn_t *bstBinaryRecords, binary_file_meta_ } bool NetworkDataLoader::LoadAssociatedStations(vASL *vAssocStnList, vUINT32 &v_ISLTemp, UINT32 &asl_count) { + if (!FileExists(settings_.s.asl_file)) { + throw StationLoadException("Failed to load associated station list file: " + settings_.s.asl_file); + } + dynadjust::iostreams::AslFile asl_loader(settings_.s.asl_file); auto result = asl_loader.TryLoad(); if (!result) { throw StationLoadException("Failed to load associated station list file: " + settings_.s.asl_file); } @@ -136,6 +160,10 @@ bool NetworkDataLoader::LoadAssociatedStations(vASL *vAssocStnList, vUINT32 &v_I } bool NetworkDataLoader::LoadMeasurements(vmsr_t *bmsBinaryRecords, binary_file_meta_t &bms_meta, UINT32 &bmsr_count) { + if (!FileExists(settings_.a.bms_file)) { + throw MeasurementLoadException("Failed to load binary measurement file: " + settings_.a.bms_file); + } + auto result = bms_loader_->LoadWithOptional(settings_.a.bms_file, bmsBinaryRecords, bms_meta); if (!result) { throw MeasurementLoadException("Failed to load binary measurement file: " + settings_.a.bms_file); } bmsr_count = static_cast(*result); diff --git a/dynadjust/dynadjust/dnaadjustwrapper/CMakeLists.txt b/dynadjust/dynadjust/dnaadjustwrapper/CMakeLists.txt index f0d64b45b..0600bcb26 100644 --- a/dynadjust/dynadjust/dnaadjustwrapper/CMakeLists.txt +++ b/dynadjust/dynadjust/dnaadjustwrapper/CMakeLists.txt @@ -87,8 +87,8 @@ if(BUILD_STATIC) "-framework CoreFoundation") else() target_link_options(${STATIC_TARGET_NAME} PRIVATE - -static) - + -static-libgcc + -static-libstdc++) endif() elseif(WIN32) # On Windows with MSVC diff --git a/dynadjust/dynadjust/dnaadjustwrapper/dnaadjustprogress.cpp b/dynadjust/dynadjust/dnaadjustwrapper/dnaadjustprogress.cpp index 06361ff25..171b378f3 100644 --- a/dynadjust/dynadjust/dnaadjustwrapper/dnaadjustprogress.cpp +++ b/dynadjust/dynadjust/dnaadjustwrapper/dnaadjustprogress.cpp @@ -21,6 +21,7 @@ #include #include +#include /// \cond #include @@ -323,13 +324,14 @@ void dna_adjust_progress_thread::processAdjustment() ss.str(""); ss << " Iteration " << std::right << std::setw(2) << std::fixed << std::setprecision(0) << currentIteration; ss << ", max station corr: " << std::right << std::setw(PROGRESS_ADJ_BLOCK_12) << - _dnaAdj->GetMaxCorrection(currentIteration) << std::endl; - + _dnaAdj->GetMaxCorrection(currentIteration); + ss << ", time: " << _dnaAdj->GetIterationTime(currentIteration) << std::endl; + coutMessage(ss.str()); } std::this_thread::sleep_for(std::chrono::milliseconds(80)); } - + break; case Phased_Block_1Mode: case PhasedMode: @@ -350,12 +352,13 @@ void dna_adjust_progress_thread::processAdjustment() ss.str(""); ss << " Iteration " << std::right << std::setw(2) << std::fixed << std::setprecision(0) << currentIteration; - ss << ", max station corr: " << std::right << std::setw(PROGRESS_ADJ_BLOCK_12) << _dnaAdj->GetMaxCorrection(currentIteration) << std::endl; - + ss << ", max station corr: " << std::right << std::setw(PROGRESS_ADJ_BLOCK_12) << _dnaAdj->GetMaxCorrection(currentIteration); + ss << ", time: " << _dnaAdj->GetIterationTime(currentIteration) << std::endl; + sst.str(""); if (first_time) sst << std::setw(PROGRESS_ADJ_BLOCK_28) << std::left << " "; - sst << PROGRESS_BACKSPACE_28 << std::setw(PROGRESS_ADJ_BLOCK_28) << std::left << ss.str(); + sst << PROGRESS_BACKSPACE_28 << std::left << ss.str(); coutMessage(sst.str()); first_time = true; } @@ -364,24 +367,33 @@ void dna_adjust_progress_thread::processAdjustment() // print new block to screen when adjusting only if (block != currentBlock && _dnaAdj->IsAdjusting()) - { + { ss.str(""); ss << " Iteration " << std::right << std::setw(2) << std::fixed << std::setprecision(0) << _dnaAdj->CurrentIteration(); if (_p->a.multi_thread && !_dnaAdj->processingCombine()) ss << std::left << std::setw(13) << ", adjusting..."; else - ss << ", block " << std::left << std::setw(6) << std::fixed << std::setprecision(0) << _dnaAdj->CurrentBlock() + 1; - - sst.str(""); - if (first_time) { - sst << std::setw(PROGRESS_ADJ_BLOCK_28) << std::left << " "; - first_time = false; + ss << ", block " << std::left << std::setw(4) << std::fixed << std::setprecision(0) << _dnaAdj->CurrentBlock() + 1; + UINT32 stnCount = _dnaAdj->CurrentBlockStationCount(); + if (stnCount > 0) + { + int max_t = dna_adjust::GetMaxBlasThreads(); + ss << " (" << std::right << std::setw(5) << stnCount << " stns, "; + if (max_t > 0) + ss << std::setw(2) << max_t << "T"; + else + ss << "auto"; + ss << ")"; + } + int64_t elapsedMs = _dnaAdj->LastBlockElapsedMs(); + if (elapsedMs > 0) + ss << " " << std::fixed << std::setprecision(1) << (elapsedMs / 1000.0) << "s"; } - - sst << PROGRESS_BACKSPACE_28 << std::setw(PROGRESS_ADJ_BLOCK_28) << std::left << ss.str(); - coutMessage(sst.str()); + ss << std::endl; + coutMessage(ss.str()); + first_time = true; currentBlock = block; } @@ -416,4 +428,3 @@ void dna_adjust_progress_thread::coutMessage(const std::string& message) std::cout.flush(); cout_mutex.unlock(); } - diff --git a/dynadjust/dynadjust/dnaadjustwrapper/dnaadjustwrapper.cpp b/dynadjust/dynadjust/dnaadjustwrapper/dnaadjustwrapper.cpp index d8c3710de..73ecaab71 100644 --- a/dynadjust/dynadjust/dnaadjustwrapper/dnaadjustwrapper.cpp +++ b/dynadjust/dynadjust/dnaadjustwrapper/dnaadjustwrapper.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -38,6 +39,7 @@ /// \endcond #include +#include #include #include #include @@ -60,6 +62,14 @@ using namespace dynadjust::iostreams; extern bool running; extern std::mutex cout_mutex; +static dna_adjust* g_netAdjust = nullptr; + +void sigint_handler(int) +{ + if (g_netAdjust) + g_netAdjust->CancelAdjustment(); +} + using namespace dynadjust; using namespace dynadjust::epsg; @@ -86,8 +96,9 @@ void PrintSummaryMessage(dna_adjust* netAdjust, const project_settings* p, boost break; std::stringstream ss(""); ss << " Iteration " << std::right << std::setw(2) << std::fixed << std::setprecision(0) << currentIteration; - ss << ", max station corr: " << std::right << std::setw(12) << netAdjust->GetMaxCorrection(currentIteration) << std::endl; - std::cout << PROGRESS_BACKSPACE_28 << std::setw(28) << std::left << ss.str(); + ss << ", max station corr: " << std::right << std::setw(12) << netAdjust->GetMaxCorrection(currentIteration); + ss << ", time: " << netAdjust->GetIterationTime(currentIteration) << std::endl; + std::cout << PROGRESS_BACKSPACE_28 << std::left << ss.str(); } if (p->a.report_mode) @@ -198,75 +209,75 @@ void DeserialiseVarianceMatrices(dna_adjust* netAdjust, const project_settings* void GenerateStatistics(dna_adjust* netAdjust, const project_settings* p) { // Generate statistics - // Don't produce statistics only for block 1 only adjustments - if (p->a.adjust_mode != Phased_Block_1Mode) + if (!p->g.quiet) { - if (!p->g.quiet) + std::cout << "+ Generating statistics..."; + std::cout.flush(); + } + netAdjust->GenerateStatistics(); + if (!p->g.quiet) + { + std::cout << " done." << std::endl; + + // Don't print detailed statistics for block 1 only adjustments + if (p->a.adjust_mode == Phased_Block_1Mode) + return; + + std::cout << "+ Adjustment results:" << std::endl << std::endl; + std::cout << "+" << OUTPUTLINE << std::endl; + std::cout << std::setw(PRINT_VAR_PAD) << std::left << " Number of unknown parameters" << std::fixed << std::setprecision(0) << netAdjust->GetUnknownsCount(); + if (netAdjust->GetAllFixed()) + std::cout << " (All stations held constrained)"; + std::cout << std::endl; + + std::cout << std::setw(PRINT_VAR_PAD) << std::left << " Number of measurements" << std::fixed << std::setprecision(0) << netAdjust->GetMeasurementCount(); + + if (netAdjust->GetPotentialOutlierCount() > 0) { - std::cout << "+ Generating statistics..."; - std::cout.flush(); + std::cout << " (" << netAdjust->GetPotentialOutlierCount() << " potential outlier"; + if (netAdjust->GetPotentialOutlierCount() > 1) + std::cout << "s"; + std::cout << ")"; } - netAdjust->GenerateStatistics(); - if (!p->g.quiet) - { - std::cout << " done." << std::endl; - - std::cout << "+ Adjustment results:" << std::endl << std::endl; - std::cout << "+" << OUTPUTLINE << std::endl; - std::cout << std::setw(PRINT_VAR_PAD) << std::left << " Number of unknown parameters" << std::fixed << std::setprecision(0) << netAdjust->GetUnknownsCount(); - if (netAdjust->GetAllFixed()) - std::cout << " (All stations held constrained)"; - std::cout << std::endl; + std::cout << std::endl; + std::cout << std::setw(PRINT_VAR_PAD) << std::left << " Degrees of freedom" << std::fixed << std::setprecision(0) << netAdjust->GetDegreesOfFreedom() << std::endl; + std::cout << std::setw(PRINT_VAR_PAD) << std::left << " Chi squared" << std::fixed << std::setprecision(2) << netAdjust->GetChiSquared() << std::endl; + std::cout << std::setw(PRINT_VAR_PAD) << std::left << " Rigorous sigma zero" << std::fixed << std::setprecision(3) << netAdjust->GetSigmaZero() << std::endl; + std::cout << std::setw(PRINT_VAR_PAD) << std::left << " Global (Pelzer) Reliability" << std::fixed << std::setw(8) << std::setprecision(3) << netAdjust->GetGlobalPelzerRel() << "(excludes non redundant measurements)" << std::endl << std::endl; - std::cout << std::setw(PRINT_VAR_PAD) << std::left << " Number of measurements" << std::fixed << std::setprecision(0) << netAdjust->GetMeasurementCount(); - - if (netAdjust->GetPotentialOutlierCount() > 0) - { - std::cout << " (" << netAdjust->GetPotentialOutlierCount() << " potential outlier"; - if (netAdjust->GetPotentialOutlierCount() > 1) - std::cout << "s"; - std::cout << ")"; - } - std::cout << std::endl; - std::cout << std::setw(PRINT_VAR_PAD) << std::left << " Degrees of freedom" << std::fixed << std::setprecision(0) << netAdjust->GetDegreesOfFreedom() << std::endl; - std::cout << std::setw(PRINT_VAR_PAD) << std::left << " Chi squared" << std::fixed << std::setprecision(2) << netAdjust->GetChiSquared() << std::endl; - std::cout << std::setw(PRINT_VAR_PAD) << std::left << " Rigorous sigma zero" << std::fixed << std::setprecision(3) << netAdjust->GetSigmaZero() << std::endl; - std::cout << std::setw(PRINT_VAR_PAD) << std::left << " Global (Pelzer) Reliability" << std::fixed << std::setw(8) << std::setprecision(3) << netAdjust->GetGlobalPelzerRel() << "(excludes non redundant measurements)" << std::endl << std::endl; - - std::stringstream ss(""); - ss << std::left << " Chi-Square test (" << std::setprecision(1) << std::fixed << p->a.confidence_interval << "%)"; - std::cout << std::setw(PRINT_VAR_PAD) << std::left << ss.str(); - ss.str(""); - ss << std::fixed << std::setprecision(3) << - netAdjust->GetChiSquaredLowerLimit() << " < " << - netAdjust->GetSigmaZero() << " < " << - netAdjust->GetChiSquaredUpperLimit(); - std::cout << std::setw(CHISQRLIMITS) << std::left << ss.str(); - ss.str(""); - - if (netAdjust->GetDegreesOfFreedom() < 1) - ss << "NO REDUNDANCY"; - else + std::stringstream ss(""); + ss << std::left << " Chi-Square test (" << std::setprecision(1) << std::fixed << p->a.confidence_interval << "%)"; + std::cout << std::setw(PRINT_VAR_PAD) << std::left << ss.str(); + ss.str(""); + ss << std::fixed << std::setprecision(3) << + netAdjust->GetChiSquaredLowerLimit() << " < " << + netAdjust->GetSigmaZero() << " < " << + netAdjust->GetChiSquaredUpperLimit(); + std::cout << std::setw(CHISQRLIMITS) << std::left << ss.str(); + ss.str(""); + + if (netAdjust->GetDegreesOfFreedom() < 1) + ss << "NO REDUNDANCY"; + else + { + ss << "*** "; + switch (netAdjust->GetTestResult()) { - ss << "*** "; - switch (netAdjust->GetTestResult()) - { - case test_stat_pass: - ss << "PASSED"; // within upper and lower - break; - case test_stat_warning: - ss << "WARNING"; // less than lower limit - break; - case test_stat_fail: - ss << "FAILED"; // greater than upper limit - break; - } - ss << " ***"; + case test_stat_pass: + ss << "PASSED"; // within upper and lower + break; + case test_stat_warning: + ss << "WARNING"; // less than lower limit + break; + case test_stat_fail: + ss << "FAILED"; // greater than upper limit + break; } - - std::cout << std::setw(PASS_FAIL) << std::right << ss.str() << std::endl; - std::cout << "+" << OUTPUTLINE << std::endl << std::endl; + ss << " ***"; } + + std::cout << std::setw(PASS_FAIL) << std::right << ss.str() << std::endl; + std::cout << "+" << OUTPUTLINE << std::endl << std::endl; } else std::cout << std::endl; @@ -727,6 +738,19 @@ int ParseCommandLineOptions(const int& argc, char* argv[], const boost::program_ if (vm.count(OUTPUT_STN_COR_FILE)) p.o._cor_file += ".cor"; + if (vm.count(OUTPUT_JSON)) + { + p.o._output_json = 1; + p.o._adj_json_file = p.o._adj_file + ".jsonl"; + p.o._xyz_json_file = p.o._xyz_file + ".jsonl"; + if (vm.count(OUTPUT_POS_UNCERTAINTY)) + p.o._apu_json_file = p.o._apu_file + ".jsonl"; + if (vm.count(OUTPUT_STN_COR_FILE)) + p.o._cor_json_file = p.o._cor_file + ".jsonl"; + if (vm.count(OUTPUT_MSR_TO_STN) && !p.o._m2s_file.empty()) + p.o._m2s_json_file = p.o._m2s_file + ".jsonl"; + } + if (vm.count(OUTPUT_STN_COR_FILE)) p.o._init_stn_corrections = 1; @@ -867,6 +891,10 @@ int main(int argc, char* argv[]) "Recreate memory mapped files.") (PURGE_STAGE_FILES, "Purge memory mapped files from disk upon adjustment completion.") + (STAGE_PATH, boost::program_options::value(&p.a.stage_path), + "Directory for memory mapped stage files. Default is the output folder.") + (MAX_THREADS, boost::program_options::value(&p.a.max_threads), + "Maximum number of BLAS threads for linear algebra operations. Default is 0 (auto).") ; output_options.add_options() @@ -979,6 +1007,8 @@ int main(int argc, char* argv[]) "Export estimated station coordinates and uncertainties to DNA measurement file as a GNSS Y cluster.") (EXPORT_SNX_FILE, "Export estimated station coordinates and full variance matrix to SINEX file. Note: station names will be truncated to four characters as per the SINEX standard.") + (OUTPUT_JSON, + "Also emit adjustment reports as JSONL files alongside the text reports (one JSON object per line, same schema as JSONL import).") ; // Declare a group of options that will be @@ -1327,7 +1357,12 @@ int main(int argc, char* argv[]) try { running = true; - int nthreads_la = init_linear_algebra_threads(); + // Install SIGINT handler for graceful cancellation + g_netAdjust = &netAdjust; + std::signal(SIGINT, sigint_handler); + + int nthreads_la = init_linear_algebra_threads(p.a.max_threads); + dna_adjust::SetMaxBlasThreads(nthreads_la); std::thread progress(dna_adjust_progress_thread(&netAdjust, &p)); // Do adjustment using linear algebra threads @@ -1354,7 +1389,10 @@ int main(int argc, char* argv[]) PrintSummaryMessage(&netAdjust, &p, &elapsed_time); if (netAdjust.GetStatus() > ADJUST_THRESHOLD_EXCEEDED) + { + netAdjust.PrintOscillationSummary(); return ADJUST_SUCCESS; + } // Generate statistics GenerateStatistics(&netAdjust, &p); @@ -1401,6 +1439,9 @@ int main(int argc, char* argv[]) return EXIT_FAILURE; } + netAdjust.PrintOscillationSummary(); + netAdjust.PrintSuspectMeasurementSummary(std::cout); + if (!p.g.quiet) std::cout << std::endl << "+ Open " << leafStr(p.o._adj_file) << " to view the adjustment details." << std::endl << std::endl; @@ -1424,4 +1465,3 @@ int main(int argc, char* argv[]) return ADJUST_SUCCESS; } - diff --git a/dynadjust/dynadjust/dnaadjustwrapper/threading_init.hpp b/dynadjust/dynadjust/dnaadjustwrapper/threading_init.hpp index 4c42f2cf7..4bf0aa91f 100644 --- a/dynadjust/dynadjust/dnaadjustwrapper/threading_init.hpp +++ b/dynadjust/dynadjust/dnaadjustwrapper/threading_init.hpp @@ -36,42 +36,56 @@ #if defined(USE_MKL) || defined(__MKL__) #include -#elif defined(OPENBLAS_VERSION) || defined(__OPENBLAS_CONFIG_H) || defined(OPENBLAS_LOPT_H) +#elif !defined(__APPLE__) + +#include + +#if defined(OPENBLAS_VERSION) || defined(__OPENBLAS_CONFIG_H) || defined(OPENBLAS_LOPT_H) #ifndef USE_OPENBLAS #define USE_OPENBLAS #endif -#include +#elif defined(__has_include) +#if __has_include() +#ifndef USE_OPENBLAS +#define USE_OPENBLAS +#endif #include +#endif +#endif +#ifdef USE_OPENBLAS extern "C" { void openblas_set_num_threads(int); } +#endif #elif defined(__APPLE__) #include #endif /// \endcond +inline int positive_env_int(const char* name) { + const char* env = std::getenv(name); + if (!env || !*env) return 0; + int value = std::atoi(env); + return value > 0 ? value : 0; +} + inline int init_linear_algebra_threads(int requested_threads = 0) { int n = requested_threads; -#if defined(_OPENMP) if (n <= 0) { - if (const char* env = std::getenv("OMP_NUM_THREADS"); env && *env) { - int v = std::atoi(env); - if (v > 0) n = v; - } - } - if (n <= 0) n = std::max(1, omp_get_max_threads()); + n = positive_env_int("OMP_NUM_THREADS"); +#if defined(USE_MKL) || defined(__MKL__) + if (n <= 0) n = positive_env_int("MKL_NUM_THREADS"); +#elif defined(USE_OPENBLAS) + if (n <= 0) n = positive_env_int("OPENBLAS_NUM_THREADS"); #elif defined(__APPLE__) - if (n == 1) { - BLASSetThreading(BLAS_THREADING_SINGLE_THREADED); - } else { - BLASSetThreading(BLAS_THREADING_MULTI_THREADED); - } -#else - if (n <= 0) n = 1; + if (n <= 0) n = positive_env_int("VECLIB_MAXIMUM_THREADS"); #endif + } + + if (n <= 0) return 0; #if defined(_OPENMP) omp_set_dynamic(0); @@ -83,7 +97,13 @@ inline int init_linear_algebra_threads(int requested_threads = 0) { omp_set_num_threads(n); #endif -#ifdef USE_MKL +#if defined(__APPLE__) + if (n == 1) { + BLASSetThreading(BLAS_THREADING_SINGLE_THREADED); + } else { + BLASSetThreading(BLAS_THREADING_MULTI_THREADED); + } +#elif defined(USE_MKL) || defined(__MKL__) mkl_set_dynamic(0); mkl_set_num_threads(n); #if defined(MKL_THREAD_LOCAL) diff --git a/dynadjust/dynadjust/dnadiff/CMakeLists.txt b/dynadjust/dynadjust/dnadiff/CMakeLists.txt index 5b48c520b..819588f42 100644 --- a/dynadjust/dynadjust/dnadiff/CMakeLists.txt +++ b/dynadjust/dynadjust/dnadiff/CMakeLists.txt @@ -24,8 +24,8 @@ else() # Link statically if(UNIX) if(NOT APPLE) - SET_TARGET_PROPERTIES(${STATIC_TARGET_NAME} PROPERTIES LINK_FLAGS "-static") - target_link_libraries (${STATIC_TARGET_NAME} pthread) + target_link_options(${STATIC_TARGET_NAME} PRIVATE -static-libgcc -static-libstdc++) + target_link_libraries(${STATIC_TARGET_NAME} pthread) endif() endif() diff --git a/dynadjust/dynadjust/dnageoidwrapper/CMakeLists.txt b/dynadjust/dynadjust/dnageoidwrapper/CMakeLists.txt index 1223d5fdc..24fb8efbf 100644 --- a/dynadjust/dynadjust/dnageoidwrapper/CMakeLists.txt +++ b/dynadjust/dynadjust/dnageoidwrapper/CMakeLists.txt @@ -84,8 +84,8 @@ if(BUILD_STATIC) "-framework CoreFoundation") else() target_link_options(${STATIC_TARGET_NAME} PRIVATE - -static) - + -static-libgcc + -static-libstdc++) endif() elseif(WIN32) # On Windows with MSVC diff --git a/dynadjust/dynadjust/dnaimport/CMakeLists.txt b/dynadjust/dynadjust/dnaimport/CMakeLists.txt index f48668c71..72f66d763 100644 --- a/dynadjust/dynadjust/dnaimport/CMakeLists.txt +++ b/dynadjust/dynadjust/dnaimport/CMakeLists.txt @@ -38,6 +38,8 @@ add_library (${PROJECT_NAME} SHARED ${CMAKE_SOURCE_DIR}/include/measurement_types/dnastntally.cpp dnaparser_pskel.cxx dnaparser_pimpl.cxx + dnaparser_validators.cpp + dnaparser_jsonl.cpp dnainterop.cpp ${CMAKE_SOURCE_DIR}/dynadjust.rc) @@ -48,9 +50,10 @@ set_source_files_properties( PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON ) -# Also exclude the interop file which uses the parsers heavily +# Also exclude the interop and JSONL parser files from unity builds set_source_files_properties( dnainterop.cpp + dnaparser_jsonl.cpp PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON ) diff --git a/dynadjust/dynadjust/dnaimport/dnainterop.cpp b/dynadjust/dynadjust/dnaimport/dnainterop.cpp index ee4cd7ee6..9a5242be5 100644 --- a/dynadjust/dynadjust/dnaimport/dnainterop.cpp +++ b/dynadjust/dynadjust/dnaimport/dnainterop.cpp @@ -20,8 +20,7 @@ //============================================================================ #include - -//#include +#include using namespace dynadjust::epsg; @@ -176,7 +175,8 @@ void dna_import::BuildExtractStationsList(const std::string& stnList, pvstring v } -void dna_import::InitialiseDatum(const std::string& reference_frame, const std::string epoch) +void dna_import::InitialiseDatum(const std::string& reference_frame, const std::string epoch, + const std::string observation_epoch) { try { // Take the default reference frame, set either by the user or @@ -198,11 +198,13 @@ void dna_import::InitialiseDatum(const std::string& reference_frame, const std:: if (datum_.GetEpoch() == timeImmemorial()) m_strProjectDefaultEpoch = ""; + m_strProjectObservationEpoch = observation_epoch; + // Update binary file meta // Note: the following rule applies each time a set of files is loaded via import: // * This method (InitialiseDatum) is called (from dnaimportwrapper) before any files are loaded. // By default, the bst & bms meta are initialised with the reference frame and reference epoch. - // * The datum and epoch within the first file (if present) is used to set the default project + // * The datum and epoch within the first file (if present) is used to set the default project // datum. If a datum isn't provided in the first input file, e.g. SINEX file, the default datum // (GDA2020) is used. As each subsequent file is loaded, the default frame and epoch are assumed. // * After all files have been loaded, InitialiseDatum is called again to set the metadata. @@ -212,6 +214,56 @@ void dna_import::InitialiseDatum(const std::string& reference_frame, const std:: snprintf(bms_meta_.epsgCode, sizeof(bms_meta_.epsgCode), "%s", m_strProjectDefaultEpsg.substr(0, STN_EPSG_WIDTH).c_str()); snprintf(bst_meta_.epoch, sizeof(bst_meta_.epoch), "%s", m_strProjectDefaultEpoch.substr(0, STN_EPOCH_WIDTH).c_str()); snprintf(bms_meta_.epoch, sizeof(bms_meta_.epoch), "%s", m_strProjectDefaultEpoch.substr(0, STN_EPOCH_WIDTH).c_str()); + // observation_epoch is immutable; record the CLI-supplied project-level value (empty if not supplied). + snprintf(bst_meta_.observation_epoch, sizeof(bst_meta_.observation_epoch), "%s", m_strProjectObservationEpoch.substr(0, STN_EPOCH_WIDTH).c_str()); + snprintf(bms_meta_.observation_epoch, sizeof(bms_meta_.observation_epoch), "%s", m_strProjectObservationEpoch.substr(0, STN_EPOCH_WIDTH).c_str()); +} + + +void dna_import::ApplyProjectObservationEpoch(vdnaMsrPtr* vMeasurements) +{ + if (m_strProjectObservationEpoch.empty() || vMeasurements == nullptr) + return; + + const std::string& obsEpoch = m_strProjectObservationEpoch; + + // A measurement's observation_epoch is treated as "not explicitly set" when it is + // empty or equal to the reference-frame epoch (the auto-default from SetEpoch). + auto needs_override = [](const std::string& msrObsEpoch, const std::string& msrEpoch) { + return msrObsEpoch.empty() || msrObsEpoch == msrEpoch; + }; + + for (auto& msr_ptr : *vMeasurements) + { + if (!msr_ptr) + continue; + + if (needs_override(msr_ptr->GetObservationEpoch(), msr_ptr->GetEpoch())) + msr_ptr->SetObservationEpoch(obsEpoch); + + // GPS baseline cluster (G/X): propagate to every baseline element + if (auto* baselines = msr_ptr->GetBaselines_ptr()) + { + for (auto& bsl : *baselines) + { + if (needs_override(bsl.GetObservationEpoch(), bsl.GetEpoch())) + bsl.SetObservationEpoch(obsEpoch); + } + } + + // GPS point cluster (Y): propagate to every point element + if (auto* points = msr_ptr->GetPoints_ptr()) + { + for (auto& pnt : *points) + { + if (needs_override(pnt.GetObservationEpoch(), pnt.GetEpoch())) + pnt.SetObservationEpoch(obsEpoch); + } + } + + // Direction set (D): directions inherit from the parent measurement; no per-direction + // observation_epoch field exists beyond what the set-level value already carries. + } } @@ -333,9 +385,30 @@ _PARSE_STATUS_ dna_import::ParseInputFile(const std::string& fileName, vdnaStnPt SignalComplete(); } + // JSONL + else if ( + first_chars[0] == '{' || // JSON object on first line + icontains(fileName, ".jsonl")) // .jsonl extension + { + // Set the file type + input_file_meta->filetype = jsonl; + m_ift = jsonl; + + // Parse the JSONL file + ParseJSONL(fileName, vStations, stnCount, vMeasurements, msrCount, clusterID, fileEpsg, fileEpoch, firstFile, success_msg); + + if (fileEpsg.empty()) + fileEpsg = m_strProjectDefaultEpsg; + + // record the file's default reference frame + snprintf(input_file_meta->epsgCode, sizeof(input_file_meta->epsgCode), "%s", fileEpsg.substr(0, STN_EPSG_WIDTH).c_str()); + snprintf(input_file_meta->epoch, sizeof(input_file_meta->epoch), "%s", fileEpoch.substr(0, STN_EPOCH_WIDTH).c_str()); + + SignalComplete(); + } // STN or MSR else if ( - // use boost::algorithm::ifind_first, which is a case insensitive implementation of the find first algorithm. + // use boost::algorithm::ifind_first, which is a case insensitive implementation of the find first algorithm. strncmp(first_chars, "!#=DNA", 6) == 0 || // dna file? icontains(fileName, ".stn") || // dna station file icontains(fileName, ".msr")) // dna measurement file @@ -400,18 +473,6 @@ void dna_import::ParseXML(const std::string& fileName, vdnaStnPtr* vStations, PU _filespecifiedreferenceframe = false; _filespecifiedepoch = false; - // Check if DynaML.xsd exists in the current directory - // This prevents the XML parser from hanging when the schema file is missing - if (!std::filesystem::exists("DynaML.xsd")) - { - import_file_mutex.unlock(); - std::stringstream ss; - ss << "ParseXML(): DynaML.xsd schema file not found in the current directory." << std::endl; - ss << " The XML parser requires this file to validate XML input files." << std::endl; - ss << " Please ensure DynaML.xsd is present in the working directory."; - SignalExceptionParse(ss.str(), 0); - } - try { // Instantiate individual parsers. @@ -450,7 +511,7 @@ void dna_import::ParseXML(const std::string& fileName, vdnaStnPtr* vStations, PU StationCoord_p.parsers (string_p, string_p, string_p, Height_p, string_p, GeoidModel_p); DnaMeasurement_p.parsers (string_p, string_p, string_p, string_p, string_p, string_p, string_p, string_p, - string_p, string_p, Directions_p, string_p, string_p, string_p, GPSBaseline_p, string_p, string_p, + string_p, string_p, Directions_p, string_p, string_p, string_p, string_p, GPSBaseline_p, string_p, string_p, string_p, Clusterpoint_p, string_p, string_p, string_p, string_p, (projectSettings_.i.prefer_single_x_as_g == TRUE ? true : false)); @@ -473,7 +534,7 @@ void dna_import::ParseXML(const std::string& fileName, vdnaStnPtr* vStations, PU ::xml_schema::document doc_p (DnaXmlFormat_p, "DnaXmlFormat"); DnaXmlFormat_p.pre(); - doc_p.parse (*ifsInputFILE_); + doc_p.parse (*ifsInputFILE_, ::xml_schema::flags::dont_validate); DnaXmlFormat_p.post_DnaXmlFormat (vStations, vMeasurements); // unlock after parsing @@ -553,6 +614,7 @@ void dna_import::ParseXML(const std::string& fileName, vdnaStnPtr* vStations, PU { std::stringstream ss; ss << "The default input file reference frame \"" << referenceframe_p.str() << "\" is not recognised."; + import_file_mutex.unlock(); SignalExceptionParse(static_cast(ss.str()), 0); } @@ -580,6 +642,7 @@ void dna_import::ParseXML(const std::string& fileName, vdnaStnPtr* vStations, PU } std::stringstream ss; ss << "ParseXML(): An std::ios_base failure was encountered while parsing " << fileName << "." << std::endl << " " << f.what(); + import_file_mutex.unlock(); SignalExceptionParse(static_cast(ss.str()), 0); } catch (const std::system_error& e) @@ -598,12 +661,14 @@ void dna_import::ParseXML(const std::string& fileName, vdnaStnPtr* vStations, PU } std::stringstream ss; ss << "ParseXML(): An std::ios_base failure was encountered while parsing " << fileName << "." << std::endl << " " << e.what(); + import_file_mutex.unlock(); SignalExceptionParse(static_cast(ss.str()), 0); } - catch (const XMLInteropException& e) + catch (const XMLInteropException& e) { std::stringstream ss; ss << "ParseXML(): An exception was encountered while parsing " << fileName << "." << std::endl << " " << e.what(); + import_file_mutex.unlock(); SignalExceptionParse(static_cast(ss.str()), 0); } catch (const ::xml_schema::parsing& e) @@ -611,7 +676,7 @@ void dna_import::ParseXML(const std::string& fileName, vdnaStnPtr* vStations, PU std::stringstream ss(""); ss << e.what(); - ::xsd::cxx::parser::diagnostics::const_iterator _it; + ::xsd::cxx::parser::diagnostics::const_iterator _it; for (_it=e.diagnostics().begin(); _it!=e.diagnostics().end(); _it++) { ss << std::endl; @@ -620,19 +685,29 @@ void dna_import::ParseXML(const std::string& fileName, vdnaStnPtr* vStations, PU ss << ", severity " << _it->severity() << std::endl; ss << " - " << _it->message(); } + import_file_mutex.unlock(); SignalExceptionParse(ss.str(), 0); } catch (const ::xml_schema::exception& e) { std::stringstream ss; ss << "ParseXML(): An xml_schema exception was encountered while parsing " << fileName << "." << std::endl << " " << e.what(); + import_file_mutex.unlock(); SignalExceptionParse(static_cast(ss.str()), 0); } + catch (const std::exception& e) + { + std::stringstream ss; + ss << "ParseXML(): An error was encountered while parsing " << fileName << "." << std::endl << " " << e.what(); + import_file_mutex.unlock(); + SignalExceptionParse(ss.str(), 0); + } catch (...) { std::stringstream ss; ss << "ParseXML(): An unknown error was encountered while parsing " << fileName << "." << std::endl; - SignalExceptionParse(ss.str(), 0); + import_file_mutex.unlock(); + SignalExceptionParse(ss.str(), 0); } if (parseStatus_ != PARSE_SUCCESS) @@ -818,12 +893,16 @@ void dna_import::ApplyDiscontinuitiesMeasurements(vdnaMsrPtr* vMeasurements) continue; } - // Check if an epoch been provided with this measurement - if (_it_msr->get()->GetEpoch().empty()) + // Prefer the observation epoch for discontinuity matching; fall back + // to reference-frame epoch if the file did not supply an observation epoch. + std::string match_epoch = _it_msr->get()->GetObservationEpoch(); + if (match_epoch.empty()) + match_epoch = _it_msr->get()->GetEpoch(); + if (match_epoch.empty()) continue; // Capture the epoch of the measurement - site_date = dateFromString(_it_msr->get()->GetEpoch()); + site_date = dateFromString(match_epoch); // 2. Handle 'first' station for every measurement type stn1 = _it_msr->get()->GetFirst(); @@ -930,8 +1009,15 @@ void dna_import::ApplyDiscontinuitiesMeasurements_GX(std::vectorend(); _it_msr++) { + // Prefer observation epoch; fall back to reference-frame epoch. + std::string match_epoch = _it_msr->GetObservationEpoch(); + if (match_epoch.empty()) + match_epoch = _it_msr->GetEpoch(); + if (match_epoch.empty()) + continue; + // Capture the start date of the site - site_date = dateFromString(_it_msr->GetEpoch()); + site_date = dateFromString(match_epoch); // Station 1 stn1 = _it_msr->GetFirst(); @@ -983,8 +1069,15 @@ void dna_import::ApplyDiscontinuitiesMeasurements_Y(std::vector* v _it_msr != vGpsPoints->end(); _it_msr++) { + // Prefer observation epoch; fall back to reference-frame epoch. + std::string match_epoch = _it_msr->GetObservationEpoch(); + if (match_epoch.empty()) + match_epoch = _it_msr->GetEpoch(); + if (match_epoch.empty()) + continue; + // Capture the start date of the site - site_date = dateFromString(_it_msr->GetEpoch()); + site_date = dateFromString(match_epoch); // Station 1 stn1 = _it_msr->GetFirst(); @@ -1056,8 +1149,104 @@ void dna_import::ApplyDiscontinuitiesMeasurements_D(std::vector* } -void dna_import::ParseDNA(const std::string& fileName, vdnaStnPtr* vStations, PUINT32 stnCount, - vdnaMsrPtr* vMeasurements, PUINT32 msrCount, PUINT32 clusterID, +void dna_import::ParseJSONL(const std::string& fileName, vdnaStnPtr* vStations, PUINT32 stnCount, + vdnaMsrPtr* vMeasurements, PUINT32 msrCount, PUINT32 clusterID, + std::string& fileEpsg, std::string& fileEpoch, bool firstFile, std::string* success_msg) +{ + parseStatus_ = PARSE_SUCCESS; + _filespecifiedreferenceframe = false; + _filespecifiedepoch = false; + + try + { + import::JsonlParseContext ctx; + ctx.default_frame = datum_.GetName(); + ctx.default_epoch = datum_.GetEpoch_s(); + ctx.first_file = firstFile; + ctx.user_supplied_frame = (projectSettings_.i.user_supplied_frame == 1); + ctx.user_supplied_epoch = (projectSettings_.i.user_supplied_epoch == 1); + ctx.override_input_frame = (projectSettings_.i.override_input_rfame == 1); + ctx.prefer_single_x_as_g = (projectSettings_.i.prefer_single_x_as_g == TRUE); + ctx.cluster_id = *clusterID; + + import::ParseJsonlFile(fileName, vStations, vMeasurements, ctx); + + *clusterID = ctx.cluster_id; + *stnCount = ctx.stn_count; + *msrCount = ctx.msr_count; + *success_msg = ctx.message + "\n"; + _filespecifiedreferenceframe = ctx.file_specified_frame; + _filespecifiedepoch = ctx.file_specified_epoch; + + // Reference frame / epoch resolution (replicated from ParseXML) + try + { + fileEpsg = ctx.file_epsg; + if (fileEpsg.empty()) + fileEpsg = datum_.GetEpsgCode_s(); + + fileEpoch = ctx.file_epoch; + if (fileEpoch.empty() || isEpsgDatumStatic(LongFromString(fileEpsg))) + fileEpoch = referenceepochFromEpsgString(fileEpsg); + + if (projectSettings_.i.user_supplied_frame) + { + if (!projectSettings_.i.user_supplied_epoch) + { + if (firstFile) + { + projectSettings_.i.epoch = fileEpoch; + projectSettings_.r.epoch = fileEpoch; + m_strProjectDefaultEpoch = fileEpoch; + datum_.SetEpoch(fileEpoch); + } + } + } + else + { + if (firstFile) + { + projectSettings_.i.reference_frame = datumFromEpsgString(fileEpsg); + projectSettings_.r.reference_frame = projectSettings_.i.reference_frame; + m_strProjectDefaultEpsg = fileEpsg; + + projectSettings_.i.epoch = fileEpoch; + projectSettings_.r.epoch = fileEpoch; + m_strProjectDefaultEpoch = fileEpoch; + } + } + } + catch (...) + { + std::stringstream ss; + ss << "The default JSONL input file reference frame is not recognised."; + SignalExceptionParse(static_cast(ss.str()), 0); + } + + if (!vStations->empty() && !vMeasurements->empty()) + m_idt = stn_msr_data; + else if (!vStations->empty()) + m_idt = stn_data; + else if (!vMeasurements->empty()) + m_idt = msr_data; + + } + catch (const XMLInteropException&) + { + throw; // re-throw + } + catch (const std::exception& e) + { + std::stringstream ss; + ss << "ParseJSONL(): An exception was encountered while parsing " << fileName << "." << std::endl; + ss << " " << e.what() << std::endl; + SignalExceptionParse(ss.str(), 0); + } +} + + +void dna_import::ParseDNA(const std::string& fileName, vdnaStnPtr* vStations, PUINT32 stnCount, + vdnaMsrPtr* vMeasurements, PUINT32 msrCount, PUINT32 clusterID, std::string& fileEpsg, std::string& fileEpoch, bool firstFile) { parseStatus_ = PARSE_SUCCESS; @@ -1148,7 +1337,7 @@ void dna_import::ParseDNA(const std::string& fileName, vdnaStnPtr* vStations, PU projectSettings_.r.epoch = fileEpoch; m_strProjectDefaultEpoch = fileEpoch; - InitialiseDatum(projectSettings_.i.reference_frame, projectSettings_.i.epoch); + InitialiseDatum(projectSettings_.i.reference_frame, projectSettings_.i.epoch, projectSettings_.i.observation_epoch); } } @@ -1739,12 +1928,18 @@ void dna_import::ParseDNAMSRLinear(const std::string& sBuf, dnaMsrPtr& msr_ptr) // Epoch msr_ptr->SetEpoch(ParseEpochValue(sBuf, "ParseDNAMSRLinear")); + // Observation epoch (DNA v3.02 column, optional) + { + std::string obs_epoch = ParseObsEpochValue(sBuf, "ParseDNAMSRLinear"); + if (!obs_epoch.empty()) + msr_ptr->SetObservationEpoch(obs_epoch); + } // Capture msr_id and cluster_id (for database referencing) ParseDatabaseIds(sBuf, "ParseDNAMSRLinear", msr_ptr->GetTypeC()); msr_ptr->SetDatabaseMap(m_msr_db_map); - // instrument and target heights only make sense for + // instrument and target heights only make sense for // slope distances, vertical angles and zenith distances switch (msr_ptr->GetTypeC()) { @@ -1806,6 +2001,12 @@ void dna_import::ParseDNAMSRCoordinate(const std::string& sBuf, dnaMsrPtr& msr_p // Epoch msr_ptr->SetEpoch(ParseEpochValue(sBuf, "ParseDNAMSRLinear")); + // Observation epoch (DNA v3.02 column, optional) + { + std::string obs_epoch = ParseObsEpochValue(sBuf, "ParseDNAMSRCoordinate"); + if (!obs_epoch.empty()) + msr_ptr->SetObservationEpoch(obs_epoch); + } // Capture msr_id and cluster_id (for database referencing), then set // database id info @@ -1821,6 +2022,7 @@ void dna_import::ParseDNAMSRGPSBaselines(std::string& sBuf, dnaMsrPtr& msr_ptr, bslTmp.SetReferenceFrame(msr_ptr->GetReferenceFrame()); bslTmp.SetEpoch(msr_ptr->GetEpoch()); + bslTmp.SetObservationEpoch(msr_ptr->GetObservationEpoch()); // Measurement type std::string tmp; @@ -1943,10 +2145,18 @@ void dna_import::ParseDNAMSRGPSBaselines(std::string& sBuf, dnaMsrPtr& msr_ptr, // Set the baseline epoch bslTmp.SetEpoch(tmp); } + + // Observation epoch (DNA v3.02 column, optional; overrides default) + std::string obs_epoch = ParseObsEpochValue(sBuf, "ParseDNAMSRGPSBaselines"); + if (!obs_epoch.empty()) + { + msr_ptr->SetObservationEpoch(obs_epoch); + bslTmp.SetObservationEpoch(obs_epoch); + } } catch (std::runtime_error& e) { std::stringstream ss; - ss << "ParseDNAMSRGPSBaselines(): Error parsing epoch: " << + ss << "ParseDNAMSRGPSBaselines(): Error parsing epoch: " << std::endl << " " << e.what(); SignalExceptionParseDNA(ss.str(), "", dml_.msr_gps_epoch); } @@ -2042,6 +2252,7 @@ void dna_import::ParseDNAMSRGPSPoints(std::string& sBuf, dnaMsrPtr& msr_ptr, boo pntTmp.SetReferenceFrame(msr_ptr->GetReferenceFrame()); pntTmp.SetEpoch(msr_ptr->GetEpoch()); + pntTmp.SetObservationEpoch(msr_ptr->GetObservationEpoch()); // Measurement type std::string tmp; @@ -2136,6 +2347,7 @@ void dna_import::ParseDNAMSRGPSPoints(std::string& sBuf, dnaMsrPtr& msr_ptr, boo // Set the point frame pntTmp.SetReferenceFrame(projectSettings_.i.reference_frame); pntTmp.SetEpoch(msr_ptr->GetEpoch()); + pntTmp.SetObservationEpoch(msr_ptr->GetObservationEpoch()); } else //if (!projectSettings_.i.override_input_rfame) { @@ -2153,7 +2365,7 @@ void dna_import::ParseDNAMSRGPSPoints(std::string& sBuf, dnaMsrPtr& msr_ptr, boo } catch (std::runtime_error& e) { std::stringstream ss; - ss << "ParseDNAMSRGPSPoints(): Error parsing reference frame: " << std::endl << + ss << "ParseDNAMSRGPSPoints(): Error parsing reference frame: " << std::endl << " " << e.what(); SignalExceptionParseDNA(ss.str(), "", dml_.msr_gps_reframe); } @@ -2179,10 +2391,18 @@ void dna_import::ParseDNAMSRGPSPoints(std::string& sBuf, dnaMsrPtr& msr_ptr, boo // Set the point epoch pntTmp.SetEpoch(tmp); } + + // Observation epoch (DNA v3.02 column, optional; overrides default) + std::string obs_epoch = ParseObsEpochValue(sBuf, "ParseDNAMSRGPSPoints"); + if (!obs_epoch.empty()) + { + msr_ptr->SetObservationEpoch(obs_epoch); + pntTmp.SetObservationEpoch(obs_epoch); + } } catch (std::runtime_error& e) { std::stringstream ss; - ss << "ParseDNAMSRGPSPoints(): Error parsing epoch: " << std::endl << + ss << "ParseDNAMSRGPSPoints(): Error parsing epoch: " << std::endl << " " << e.what(); SignalExceptionParseDNA(ss.str(), "", dml_.msr_gps_epoch); } @@ -2691,7 +2911,7 @@ std::string dna_import::ParseEpochValue(const std::string& sBuf, const std::stri { if (sBuf.length() <= dml_.msr_gps_epoch) return ""; - + std::string epoch; try { if (sBuf.length() > static_cast(dml_.msr_gps_epoch + dmw_.msr_gps_epoch)) @@ -2707,6 +2927,30 @@ std::string dna_import::ParseEpochValue(const std::string& sBuf, const std::stri return epoch; } +std::string dna_import::ParseObsEpochValue(const std::string& sBuf, const std::string& calling_function) +{ + // DNA v3.01 (and earlier) files don't have an observation-epoch column; + // in that case the field layout leaves msr_gps_obs_epoch at 0. + if (dml_.msr_gps_obs_epoch == 0 || dmw_.msr_gps_obs_epoch == 0) + return ""; + if (sBuf.length() <= dml_.msr_gps_obs_epoch) + return ""; + + std::string obs_epoch; + try { + if (sBuf.length() > static_cast(dml_.msr_gps_obs_epoch + dmw_.msr_gps_obs_epoch)) + obs_epoch = trimstr(sBuf.substr(dml_.msr_gps_obs_epoch, dmw_.msr_gps_obs_epoch)); + else + obs_epoch = trimstr(sBuf.substr(dml_.msr_gps_obs_epoch)); + } + catch (...) { + SignalExceptionParseDNA(calling_function + "(): Could not extract observation epoch from the record: ", + sBuf, dml_.msr_gps_obs_epoch); + } + + return obs_epoch; +} + std::string dna_import::ParseGPSMsrValue(const std::string& sBuf, const std::string& element, const std::string& calling_function) { try { @@ -2770,6 +3014,12 @@ void dna_import::ParseDNAMSRAngular(const std::string& sBuf, dnaMsrPtr& msr_ptr) // Epoch msr_ptr->SetEpoch(ParseEpochValue(sBuf, "ParseDNAMSRAngular")); + // Observation epoch (DNA v3.02 column, optional) + { + std::string obs_epoch = ParseObsEpochValue(sBuf, "ParseDNAMSRAngular"); + if (!obs_epoch.empty()) + msr_ptr->SetObservationEpoch(obs_epoch); + } // Capture msr_id and cluster_id (for database referencing), then set // database id info @@ -2849,6 +3099,12 @@ UINT32 dna_import::ParseDNAMSRDirections(std::string& sBuf, dnaMsrPtr& msr_ptr, // Epoch msr_ptr->SetEpoch(ParseEpochValue(sBuf, "ParseDNAMSRDirections")); + // Observation epoch (DNA v3.02 column, optional) + { + std::string obs_epoch = ParseObsEpochValue(sBuf, "ParseDNAMSRDirections"); + if (!obs_epoch.empty()) + msr_ptr->SetObservationEpoch(obs_epoch); + } // Capture msr_id and cluster_id (for database referencing) ParseDatabaseIds(sBuf, "ParseDNAMSRDirections", msr_ptr->GetTypeC()); diff --git a/dynadjust/dynadjust/dnaimport/dnainterop.hpp b/dynadjust/dynadjust/dnaimport/dnainterop.hpp index 7c4a4f201..0a28ac9f8 100644 --- a/dynadjust/dynadjust/dnaimport/dnainterop.hpp +++ b/dynadjust/dynadjust/dnaimport/dnainterop.hpp @@ -180,8 +180,16 @@ class dna_import { inline void ResetFileOrder() const { g_fileOrder = 0; } inline bool filespecifiedReferenceFrame() const { return _filespecifiedreferenceframe; } inline bool filespecifiedEpoch() const { return _filespecifiedepoch; } - void InitialiseDatum(const std::string& reference_frame, const std::string epoch=""); - + void InitialiseDatum(const std::string& reference_frame, const std::string epoch="", + const std::string observation_epoch=""); + + // Applies the project-level observation epoch (from --observation-epoch) to every + // measurement whose observation_epoch is empty or equal to its reference-frame epoch + // (i.e. was auto-defaulted). Explicit file-level values set via + // or DNA v3.02 column 25 that differ from epoch are preserved. No-op if the CLI flag + // was not supplied. + void ApplyProjectObservationEpoch(vdnaMsrPtr* vMeasurements); + void PrintMeasurementsToStations(std::string& m2s_file, MsrTally* parsemsrTally, std::string& bst_file, std::string& bms_file, std::string& aml_file, pvASLPtr vAssocStnList); @@ -199,10 +207,15 @@ class dna_import { void ParseSNX(const std::string& fileName, vdnaStnPtr* vStations, PUINT32 stnCount, vdnaMsrPtr* vMeasurements, PUINT32 msrCount, PUINT32 clusterID); + // JSONL files + void ParseJSONL(const std::string& fileName, vdnaStnPtr* vStations, PUINT32 stnCount, + vdnaMsrPtr* vMeasurements, PUINT32 msrCount, PUINT32 clusterID, + std::string& fileEpsg, std::string& fileEpoch, bool firstFile, std::string* success_msg); + // DNA Ascii files //void ParseDNAVersion(const INPUT_DATA_TYPE& idt); - void ParseDNA(const std::string& fileName, vdnaStnPtr* vStations, PUINT32 stnCount, - vdnaMsrPtr* vMeasurements, PUINT32 msrCount, PUINT32 clusterID, + void ParseDNA(const std::string& fileName, vdnaStnPtr* vStations, PUINT32 stnCount, + vdnaMsrPtr* vMeasurements, PUINT32 msrCount, PUINT32 clusterID, std::string& fileEpsg, std::string& fileEpoch, bool firstFile); void ParseDNASTN(vdnaStnPtr* vStations, PUINT32 stnCount, const std::string& fileEpsg, const std::string& fileEpoch); @@ -236,6 +249,7 @@ class dna_import { std::string ParseScaleHValue(const std::string& sBuf, const std::string& calling_function); std::string ParseRefFrameValue(const std::string& sBuf, const std::string& calling_function); std::string ParseEpochValue(const std::string& sBuf, const std::string& calling_function); + std::string ParseObsEpochValue(const std::string& sBuf, const std::string& calling_function); void ParseDatabaseIds(const std::string& sBuf, const std::string& calling_function, const char msrType); void ParseDatabaseClusterId(const std::string& sBuf, const std::string& calling_function); @@ -338,6 +352,7 @@ class dna_import { std::string m_strProjectDefaultEpsg; std::string m_strProjectDefaultEpoch; + std::string m_strProjectObservationEpoch; // Project-level observation epoch from --observation-epoch (immutable; may be empty) std::string m_msrComments; vvUINT32 v_ISL_; // Inner stations diff --git a/dynadjust/dynadjust/dnaimport/dnaparser_jsonl.cpp b/dynadjust/dynadjust/dnaimport/dnaparser_jsonl.cpp new file mode 100644 index 000000000..66c133da8 --- /dev/null +++ b/dynadjust/dynadjust/dnaimport/dnaparser_jsonl.cpp @@ -0,0 +1,679 @@ +//============================================================================ +// Name : dnaparser_jsonl.cpp +// Author : Dale Roberts +// Copyright : Copyright 2025 Geoscience Australia +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http ://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Description : JSONL parser for DynaML-equivalent station and measurement +// data. One JSON object per line, using the same element +// names and hierarchy as the DynaML XML schema. +//============================================================================ + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +using json = nlohmann::json; +using dynadjust::exception::XMLInteropException; +using dynadjust::measurements::CDnaCovariance; +using dynadjust::measurements::CDnaDirection; +using dynadjust::measurements::CDnaGpsBaseline; +using dynadjust::measurements::CDnaGpsPoint; +using dynadjust::measurements::CDnaStation; +using dynadjust::measurements::MsrTally; +using dynadjust::measurements::StnTally; +using dynadjust::measurements::dnaMsrPtr; +using dynadjust::measurements::dnaStnPtr; +using dynadjust::measurements::vdnaMsrPtr; +using dynadjust::measurements::vdnaStnPtr; + +extern MsrTally g_parsemsr_tally; +extern StnTally g_parsestn_tally; +extern UINT32 g_fileOrder; + +namespace dynadjust { +namespace import { +namespace { + +// Helper to get a string value from a JSON object, returning empty string +// if the key is absent. Handles both JSON strings ("1.23") and JSON +// numbers (1.23) so that callers don't need to care about quoting. +std::string jstr(const json& obj, const char* key) { + auto it = obj.find(key); + if (it == obj.end() || it->is_null()) + return ""; + if (it->is_string()) + return it->get(); + if (it->is_number()) { + // dump() produces a full-precision decimal without quotes. + return it->dump(); + } + return it->dump(); // fallback for booleans etc. +} + +bool jignore(const json& obj, const char* key) { + auto it = obj.find(key); + if (it == obj.end() || it->is_null()) + return false; + if (it->is_boolean()) + return it->get(); + if (it->is_string()) + return !it->get().empty(); + return !it->dump().empty(); +} + +// Throw with line number context. +[[noreturn]] void throwLine(const std::string& msg, UINT32 line_no) { + std::stringstream ss; + ss << "JSONL line " << line_no << ": " << msg; + throw XMLInteropException(ss.str(), 0); +} + +// ------------------------------------------------------------------- +// Header +// ------------------------------------------------------------------- + +void parseHeader(const json& obj, JsonlParseContext& ctx, UINT32 line_no) { + const auto& hdr = obj.contains("DnaAdjustmentReport") + ? obj.at("DnaAdjustmentReport") + : obj.at("DnaXmlFormat"); + ctx.file_epsg = ""; + ctx.file_epoch = ""; + + std::string rf = jstr(hdr, "referenceframe"); + std::string ep = jstr(hdr, "epoch"); + + if (!rf.empty()) { + ctx.file_specified_frame = true; + try { + ctx.file_epsg = epsg::epsgStringFromName(rf); + } catch (const std::exception& e) { + throwLine(e.what(), line_no); + } + } + + if (!ep.empty()) { + ctx.file_specified_epoch = true; + ctx.file_epoch = ep; + } +} + +// ------------------------------------------------------------------- +// Station +// ------------------------------------------------------------------- + +void parseStation(const json& obj, vdnaStnPtr* vStations, + JsonlParseContext& ctx, UINT32 line_no) { + const auto& stn = obj.at("DnaStation"); + + std::string name = jstr(stn, "Name"); + ValidateStationName(name); + + std::string frame, epoch; + if (ctx.override_input_frame) + frame = ctx.default_frame; + else if (!ctx.file_epsg.empty()) + frame = epsg::datumFromEpsgString(ctx.file_epsg); + else + frame = ctx.default_frame; + + if (ctx.override_input_frame) + epoch = ctx.default_epoch; + else if (!ctx.file_epoch.empty()) + epoch = ctx.file_epoch; + else + epoch = ctx.default_epoch; + + dnaStnPtr station(new CDnaStation(frame, epoch)); + station->SetfileOrder(g_fileOrder++); + station->SetName(name); + + std::string constraints = DefaultConstraints(jstr(stn, "Constraints")); + station->SetConstraints(constraints); + g_parsestn_tally.addstation(constraints); + + station->SetCoordType(jstr(stn, "Type")); + + // StationCoord sub-object + auto it_coord = stn.find("StationCoord"); + if (it_coord != stn.end()) { + const auto& coord = *it_coord; + // Name inside StationCoord is the same; we already set it. + station->SetXAxis(jstr(coord, "XAxis")); + station->SetYAxis(jstr(coord, "YAxis")); + station->SetHeight(jstr(coord, "Height")); + + std::string hz = jstr(coord, "HemisphereZone"); + if (!hz.empty()) + station->SetHemisphereZone(hz); + } + + station->SetDescription(SanitiseDescription(jstr(stn, "Description"))); + + vStations->push_back(station); + ctx.stn_count++; +} + +// ------------------------------------------------------------------- +// GPS Covariance (shared between X-baselines and Y-points) +// ------------------------------------------------------------------- + +void parseGPSCovariance(const json& cov_obj, CDnaGpsBaseline& bsl) { + CDnaCovariance cov; + cov.SetType("X"); + cov.SetClusterID(bsl.GetClusterID()); + cov.SetM11(jstr(cov_obj, "m11")); + cov.SetM12(jstr(cov_obj, "m12")); + cov.SetM13(jstr(cov_obj, "m13")); + cov.SetM21(jstr(cov_obj, "m21")); + cov.SetM22(jstr(cov_obj, "m22")); + cov.SetM23(jstr(cov_obj, "m23")); + cov.SetM31(jstr(cov_obj, "m31")); + cov.SetM32(jstr(cov_obj, "m32")); + cov.SetM33(jstr(cov_obj, "m33")); + bsl.AddGpsCovariance(&cov); +} + +void parsePointCovariance(const json& cov_obj, CDnaGpsPoint& pt) { + CDnaCovariance cov; + cov.SetType("Y"); + cov.SetClusterID(pt.GetClusterID()); + cov.SetM11(jstr(cov_obj, "m11")); + cov.SetM12(jstr(cov_obj, "m12")); + cov.SetM13(jstr(cov_obj, "m13")); + cov.SetM21(jstr(cov_obj, "m21")); + cov.SetM22(jstr(cov_obj, "m22")); + cov.SetM23(jstr(cov_obj, "m23")); + cov.SetM31(jstr(cov_obj, "m31")); + cov.SetM32(jstr(cov_obj, "m32")); + cov.SetM33(jstr(cov_obj, "m33")); + pt.AddPointCovariance(&cov); +} + +// ------------------------------------------------------------------- +// GPS Baselines (G and X types) +// ------------------------------------------------------------------- + +void parseGPSBaselines(const json& msr_obj, dnaMsrPtr& msr, + JsonlParseContext& ctx, UINT32 line_no) { + auto it = msr_obj.find("GPSBaseline"); + if (it == msr_obj.end()) + throwLine("GPS measurement requires \"GPSBaseline\" array.", line_no); + + const auto& baselines = *it; + if (!baselines.is_array()) + throwLine("\"GPSBaseline\" must be an array.", line_no); + + char type_c = msr->GetTypeC(); + UINT32 total = static_cast(baselines.size()); + + // Reserve space in the cluster + msr->ReserveGpsBaselinesCount(total); + + for (UINT32 i = 0; i < total; ++i) { + const auto& bsl_obj = baselines[i]; + + CDnaGpsBaseline bsl; + bsl.SetType(msr->GetType()); + + // First and Second: each baseline can override the parent's values + // (for X-type clusters, each baseline may connect different stations) + std::string first = jstr(bsl_obj, "First"); + bsl.SetFirst(first.empty() ? msr->GetFirst() : first); + + std::string second = jstr(bsl_obj, "Second"); + bsl.SetTarget(second.empty() ? msr->GetTarget() : second); + + bsl.SetReferenceFrame(msr->GetReferenceFrame()); + bsl.SetEpoch(msr->GetEpoch()); + bsl.SetObservationEpoch(msr->GetObservationEpoch()); + bsl.SetClusterID(msr->GetClusterID()); + bsl.SetClusterDBID(msr->GetClusterDBID(), msr->GetClusterDBIDset()); + + // For X-type clusters with >1 baselines, reserve covariance space + if (type_c == 'X' && total > 1) + bsl.ReserveGpsCovariancesCount(total - 1); + + // Set X, Y, Z components — each increments tally 1x + bsl.SetX(jstr(bsl_obj, "X")); + ctx.msr_count++; + if (type_c == 'G') + g_parsemsr_tally.G++; + else + g_parsemsr_tally.X++; + + bsl.SetY(jstr(bsl_obj, "Y")); + ctx.msr_count++; + if (type_c == 'G') + g_parsemsr_tally.G++; + else + g_parsemsr_tally.X++; + + bsl.SetZ(jstr(bsl_obj, "Z")); + ctx.msr_count++; + if (type_c == 'G') + g_parsemsr_tally.G++; + else + g_parsemsr_tally.X++; + + // Per-baseline MeasurementID + std::string bsl_dbid = jstr(bsl_obj, "MeasurementID"); + if (!bsl_dbid.empty()) + bsl.SetMeasurementDBID(bsl_dbid); + + // Sigma values + bsl.SetSigmaXX(jstr(bsl_obj, "SigmaXX")); + bsl.SetSigmaXY(jstr(bsl_obj, "SigmaXY")); + bsl.SetSigmaXZ(jstr(bsl_obj, "SigmaXZ")); + bsl.SetSigmaYY(jstr(bsl_obj, "SigmaYY")); + bsl.SetSigmaYZ(jstr(bsl_obj, "SigmaYZ")); + bsl.SetSigmaZZ(jstr(bsl_obj, "SigmaZZ")); + + // Covariances (only for X-type clusters) + auto it_cov = bsl_obj.find("GPSCovariance"); + if (it_cov != bsl_obj.end() && it_cov->is_array()) { + for (const auto& cov_obj : *it_cov) { + parseGPSCovariance(cov_obj, bsl); + } + } + + msr->AddGpsBaseline(&bsl); + } +} + +// ------------------------------------------------------------------- +// GPS Points (Y type) +// ------------------------------------------------------------------- + +void parseClusterpoints(const json& msr_obj, dnaMsrPtr& msr, + JsonlParseContext& ctx, UINT32 line_no) { + auto it = msr_obj.find("Clusterpoint"); + if (it == msr_obj.end()) + throwLine("Y measurement requires \"Clusterpoint\" array.", line_no); + + const auto& points = *it; + if (!points.is_array()) + throwLine("\"Clusterpoint\" must be an array.", line_no); + + UINT32 total = static_cast(points.size()); + + // Reserve space in the cluster + msr->ReserveGpsPointsCount(total); + + for (UINT32 i = 0; i < total; ++i) { + const auto& pt_obj = points[i]; + + CDnaGpsPoint pt; + pt.SetType(msr->GetType()); + pt.SetCoordType(msr->GetCoordType()); + + // Each clusterpoint can have its own First station + std::string first = jstr(pt_obj, "First"); + pt.SetFirst(first.empty() ? msr->GetFirst() : first); + + pt.SetClusterID(msr->GetClusterID()); + pt.SetClusterDBID(msr->GetClusterDBID(), msr->GetClusterDBIDset()); + pt.SetReferenceFrame(msr->GetReferenceFrame()); + pt.SetEpoch(msr->GetEpoch()); + pt.SetObservationEpoch(msr->GetObservationEpoch()); + + // Set X, Y, Z components — each increments tally 1x + pt.SetX(jstr(pt_obj, "X")); + ctx.msr_count++; + g_parsemsr_tally.Y++; + + pt.SetY(jstr(pt_obj, "Y")); + ctx.msr_count++; + g_parsemsr_tally.Y++; + + pt.SetZ(jstr(pt_obj, "Z")); + ctx.msr_count++; + g_parsemsr_tally.Y++; + + // Per-point MeasurementID + std::string pt_dbid = jstr(pt_obj, "MeasurementID"); + if (!pt_dbid.empty()) + pt.SetMeasurementDBID(pt_dbid); + + // Sigma values + pt.SetSigmaXX(jstr(pt_obj, "SigmaXX")); + pt.SetSigmaXY(jstr(pt_obj, "SigmaXY")); + pt.SetSigmaXZ(jstr(pt_obj, "SigmaXZ")); + pt.SetSigmaYY(jstr(pt_obj, "SigmaYY")); + pt.SetSigmaYZ(jstr(pt_obj, "SigmaYZ")); + pt.SetSigmaZZ(jstr(pt_obj, "SigmaZZ")); + + // Point covariances + auto it_cov = pt_obj.find("PointCovariance"); + if (it_cov != pt_obj.end() && it_cov->is_array()) { + for (const auto& cov_obj : *it_cov) { + parsePointCovariance(cov_obj, pt); + } + } + + msr->AddGpsPoint(&pt); + } +} + +// ------------------------------------------------------------------- +// Directions (D type) +// ------------------------------------------------------------------- + +void parseDirections(const json& msr_obj, dnaMsrPtr& msr, + JsonlParseContext& ctx, UINT32 line_no) { + auto it = msr_obj.find("Directions"); + if (it == msr_obj.end()) + throwLine("D measurement requires \"Directions\" array.", line_no); + + const auto& directions = *it; + if (!directions.is_array()) + throwLine("\"Directions\" must be an array.", line_no); + + UINT32 total = msr->GetTotal(); + + for (const auto& dir_obj : directions) { + CDnaDirection dir; + dir.SetClusterID(msr->GetClusterID()); + dir.SetClusterDBID(msr->GetClusterDBID(), msr->GetClusterDBIDset()); + dir.SetEpoch(msr->GetEpoch()); + dir.SetObservationEpoch(msr->GetObservationEpoch()); + + // Ignore for individual directions + bool dir_ignored = jignore(dir_obj, "Ignore"); + dir.SetIgnore(dir_ignored); + + dir.SetTarget(jstr(dir_obj, "Target")); + dir.SetValue(jstr(dir_obj, "Value")); + dir.SetStdDev(jstr(dir_obj, "StdDev")); + + // Direction Value always increments measurement count + ctx.msr_count++; + + msr->AddDirection(&dir); + + // Tally counting: only count non-ignored directions + // (matching pimpl behaviour in Directions_pimpl::post_Directions) + if (msr->GetIgnore()) + continue; + + if (!dir_ignored) { + g_parsemsr_tally.D++; + } else { + ValidateDirectionInSet(msr, true, total); + } + } +} + +// ------------------------------------------------------------------- +// Measurement dispatch +// ------------------------------------------------------------------- + +void parseMeasurement(const json& obj, vdnaMsrPtr* vMeasurements, + JsonlParseContext& ctx, UINT32 line_no) { + const auto& msr_obj = obj.at("DnaMeasurement"); + + std::string type_str = jstr(msr_obj, "Type"); + char type_c = ValidateMeasurementType(type_str); + + // Determine frame and epoch for this measurement + std::string frame, epoch; + std::string msr_rf = jstr(msr_obj, "ReferenceFrame"); + std::string msr_ep = jstr(msr_obj, "Epoch"); + std::string msr_obs_ep = jstr(msr_obj, "EpochOfObservation"); + + bool rf_supplied = !msr_rf.empty(); + bool ep_supplied = !msr_ep.empty(); + bool obs_ep_supplied = !msr_obs_ep.empty(); + + if (rf_supplied || ctx.override_input_frame) + frame = ctx.override_input_frame ? ctx.default_frame : msr_rf; + else if (!ctx.file_epsg.empty()) + frame = epsg::datumFromEpsgString(ctx.file_epsg); + else + frame = ctx.default_frame; + + if (ep_supplied || ctx.override_input_frame) + epoch = ctx.override_input_frame ? ctx.default_epoch : msr_ep; + else if (!ctx.file_epoch.empty()) + epoch = ctx.file_epoch; + else + epoch = ctx.default_epoch; + + // Create measurement (increments tally for scalar types) + dnaMsrPtr msr = CreateMeasurement(type_c, &ctx.cluster_id, + ctx.msr_count, g_parsemsr_tally, frame, epoch); + msr->SetType(type_str); + + // Ignore + if (jignore(msr_obj, "Ignore")) { + msr->SetIgnore(true); + g_parsemsr_tally.ignored++; + } else { + msr->SetIgnore(false); + } + + // Source, MeasurementID, ClusterID + msr->SetSource(jstr(msr_obj, "Source")); + + std::string msr_dbid = jstr(msr_obj, "MeasurementID"); + if (!msr_dbid.empty()) + msr->SetMeasurementDBID(msr_dbid); + + std::string cluster_dbid = jstr(msr_obj, "ClusterID"); + if (!cluster_dbid.empty()) + msr->SetClusterDBID(cluster_dbid); + + // First station + std::string first = jstr(msr_obj, "First"); + ValidateFirst(first); + msr->SetFirst(first); + + // Second station + std::string second = jstr(msr_obj, "Second"); + ValidateSecond(second, type_c, first); + if (TypeRequiresSecond(type_c)) + msr->SetTarget(second); + + // Third station (only for angles) + std::string third = jstr(msr_obj, "Third"); + ValidateThird(third, type_c, first); + if (!third.empty()) + msr->SetTarget2(third); + + // Value and StdDev (scalar types) + std::string value = jstr(msr_obj, "Value"); + ValidateValue(value, type_c, first); + if (!value.empty()) + msr->SetValue(value); + + std::string stddev = jstr(msr_obj, "StdDev"); + ValidateStdDev(stddev, type_c, first); + if (!stddev.empty()) + msr->SetStdDev(stddev); + + // Instrument and target heights (S, V, Z types) + switch (type_c) { + case 'S': case 'V': case 'Z': { + std::string ih = jstr(msr_obj, "InstHeight"); + if (!ih.empty()) + msr->SetInstrumentHeight(ih); + std::string th = jstr(msr_obj, "TargHeight"); + if (!th.empty()) + msr->SetTargetHeight(th); + break; + } + } + + // Reference frame and epoch (for GPS types, already set via + // CreateMeasurement but may need per-measurement override) + if (rf_supplied && !ctx.override_input_frame) + msr->SetReferenceFrame(msr_rf); + + if (ep_supplied && !ctx.override_input_frame) + msr->SetEpoch(msr_ep); + + if (obs_ep_supplied) + msr->SetObservationEpoch(msr_obs_ep); + + // Scales + std::string vscale = jstr(msr_obj, "Vscale"); + if (!vscale.empty()) + msr->SetVscale(DefaultScale(vscale)); + + std::string pscale = jstr(msr_obj, "Pscale"); + if (!pscale.empty()) + msr->SetPscale(DefaultScale(pscale)); + + std::string lscale = jstr(msr_obj, "Lscale"); + if (!lscale.empty()) + msr->SetLscale(DefaultScale(lscale)); + + std::string hscale = jstr(msr_obj, "Hscale"); + if (!hscale.empty()) + msr->SetHscale(DefaultScale(hscale)); + + // Total (for cluster/set types D, X, Y; G auto-sets from baseline count) + std::string total = jstr(msr_obj, "Total"); + if (type_c != 'G') { + ValidateTotal(total, type_c); + if (!total.empty()) + msr->SetTotal(total); + } + + // Coords (for Y type) + std::string coords = jstr(msr_obj, "Coords"); + if (!coords.empty()) + msr->SetCoordType(coords); + + // Dispatch to sub-parsers for complex types + switch (type_c) { + case 'G': + case 'X': + parseGPSBaselines(msr_obj, msr, ctx, line_no); + // For G type, set Total from actual baseline count + if (type_c == 'G') + msr->SetTotal(static_cast(msr->GetBaselines_ptr()->size())); + break; + case 'Y': + parseClusterpoints(msr_obj, msr, ctx, line_no); + break; + case 'D': + parseDirections(msr_obj, msr, ctx, line_no); + break; + } + + // Ignored empty direction set — skip silently (matching pimpl) + if (type_c == 'D' && msr->GetTotal() == 0 && msr->GetIgnore()) + return; + + // Finalise and add + vMeasurements->push_back(msr); + FinaliseMeasurement(msr, ctx.prefer_single_x_as_g); +} + +} // anonymous namespace + +// ------------------------------------------------------------------- +// Public entry point +// ------------------------------------------------------------------- + +void ParseJsonlFile(const std::string& filename, + vdnaStnPtr* vStations, + vdnaMsrPtr* vMeasurements, + JsonlParseContext& ctx) { + std::ifstream ifs(filename); + if (!ifs.is_open()) { + std::stringstream ss; + ss << "ParseJsonlFile(): Could not open " << filename; + throw XMLInteropException(ss.str(), 0); + } + + // Initialise tallies + g_parsemsr_tally.initialise(); + g_parsestn_tally.initialise(); + + ctx.stn_count = 0; + ctx.msr_count = 0; + ctx.file_specified_frame = false; + ctx.file_specified_epoch = false; + ctx.file_epsg = ""; + ctx.file_epoch = ""; + + std::string line; + UINT32 line_no = 0; + while (std::getline(ifs, line)) { + ++line_no; + + // Skip empty lines + if (line.empty() || line.find_first_not_of(" \t\r\n") == std::string::npos) + continue; + + json obj; + try { + obj = json::parse(line); + } catch (const json::parse_error& e) { + throwLine(std::string("JSON parse error: ") + e.what(), line_no); + } + + if (!obj.is_object() || obj.empty()) + throwLine("Expected a JSON object.", line_no); + + try { + // Accept both header wrappers: DnaXmlFormat is the original name shared + // with the XML schema; DnaAdjustmentReport is the adjustment-output + // header emitted by the JSONL side-channel printer. + if (obj.contains("DnaXmlFormat") || + obj.contains("DnaAdjustmentReport")) { + parseHeader(obj, ctx, line_no); + } else if (obj.contains("DnaStatistics")) { + // Adjustment-report JSONL includes statistics records that are useful + // to consumers but have no station/measurement input equivalent. + // Treat them as metadata so .adj.jsonl streams remain re-ingestible. + } else if (obj.contains("DnaStation")) { + parseStation(obj, vStations, ctx, line_no); + } else if (obj.contains("DnaMeasurement")) { + parseMeasurement(obj, vMeasurements, ctx, line_no); + } else { + throwLine("Unrecognised top-level key.", line_no); + } + } catch (const XMLInteropException&) { + throw; // re-throw as-is + } catch (const std::exception& e) { + throwLine(e.what(), line_no); + } + } + + // Build success message + std::ostringstream ss; + if (ctx.stn_count > 0) + ss << "Loaded " << ctx.stn_count << " stations"; + if (ctx.msr_count > 0) { + if (ctx.stn_count > 0) + ss << ", "; + ss << "Loaded " << ctx.msr_count << " measurements"; + } + ctx.message = ss.str(); +} + +} // namespace import +} // namespace dynadjust diff --git a/dynadjust/dynadjust/dnaimport/dnaparser_jsonl.hpp b/dynadjust/dynadjust/dnaimport/dnaparser_jsonl.hpp new file mode 100644 index 000000000..61dcd1831 --- /dev/null +++ b/dynadjust/dynadjust/dnaimport/dnaparser_jsonl.hpp @@ -0,0 +1,65 @@ +//============================================================================ +// Name : dnaparser_jsonl.hpp +// Author : Dale Roberts +// Copyright : Copyright 2025 Geoscience Australia +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http ://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Description : JSONL parser for DynaML-equivalent station and measurement +// data. One JSON object per line, using the same element names +// and hierarchy as the DynaML XML schema. +//============================================================================ + +#ifndef DYNADJUST_DNAIMPORT_DNAPARSER_JSONL_HPP_ +#define DYNADJUST_DNAIMPORT_DNAPARSER_JSONL_HPP_ + +#include + +#include + +namespace dynadjust { +namespace import { + +struct JsonlParseContext { + // Inputs + std::string default_frame; + std::string default_epoch; + bool first_file; + bool user_supplied_frame; + bool user_supplied_epoch; + bool override_input_frame; + bool prefer_single_x_as_g; + + // Outputs + UINT32 stn_count; + UINT32 msr_count; + UINT32 cluster_id; + std::string file_epsg; + std::string file_epoch; + std::string message; + bool file_specified_frame; + bool file_specified_epoch; +}; + +// Parse a JSONL file containing DynaML-equivalent station and measurement +// data. Populates vStations and vMeasurements; updates context tallies, +// counts and metadata. +void ParseJsonlFile(const std::string& filename, + measurements::vdnaStnPtr* vStations, + measurements::vdnaMsrPtr* vMeasurements, + JsonlParseContext& ctx); + +} // namespace import +} // namespace dynadjust + +#endif // DYNADJUST_DNAIMPORT_DNAPARSER_JSONL_HPP_ diff --git a/dynadjust/dynadjust/dnaimport/dnaparser_pimpl.cxx b/dynadjust/dynadjust/dnaimport/dnaparser_pimpl.cxx index a861f3941..335a6650a 100644 --- a/dynadjust/dynadjust/dnaimport/dnaparser_pimpl.cxx +++ b/dynadjust/dynadjust/dnaimport/dnaparser_pimpl.cxx @@ -8,13 +8,13 @@ #include #include #include +#include #include -#include - using namespace dynadjust::measurements; using namespace dynadjust::epsg; using namespace dynadjust::exception; +using namespace dynadjust::import; extern MsrTally g_parsemsr_tally; extern StnTally g_parsestn_tally; @@ -169,39 +169,24 @@ void Directions_pimpl::post_Directions(const UINT32& total) { if (!_parent_dnaDirectionSet) return; - - // Is the parent ignored? Add it to the ignored set + + // Always add the direction to the set + _parent_dnaDirectionSet->AddDirection(_dnaDirection.get()); + + // If the parent is ignored, nothing more to check if (_parent_dnaDirectionSet->GetIgnore()) - { - _parent_dnaDirectionSet->AddDirection(_dnaDirection.get()); return; - } - // Is the parent not ignored, and the direction not ignored? Add it to the set + // Count non-ignored directions for the tally if (_dnaDirection->NotIgnored()) { - _parent_dnaDirectionSet->AddDirection(_dnaDirection.get()); - g_parsemsr_tally.D++; + g_parsemsr_tally.D++; return; } - // At this point, the parent is not ignored, but a sub direction is. - // Test if this ignored sub direction will lead to a false direction - UINT32 t = _parent_dnaDirectionSet->GetTotal() - 1; - - if (t == 0) - { - std::stringstream ss, ss2; - ss << - "...', total of " << total << " element(s)" << std::endl << - " - found 0 , or there aren't any non-ignored directions in the set. '"; - ss2 << " ~ " << total << ""; - throw ::xsd::cxx::parser::expected_element< char >( - ss.str(), ss2.str()); - } - - _parent_dnaDirectionSet->AddDirection(_dnaDirection.get()); - + // Direction is ignored — validate this doesn't leave the set empty + ValidateDirectionInSet(_parent_dnaDirectionSet, + true, total); } // DnaMeasurement_pimpl @@ -219,8 +204,7 @@ void DnaMeasurement_pimpl::pre() void DnaMeasurement_pimpl::Type(const ::std::string& Type) { - if (Type.empty()) - throw XMLInteropException("\"Type\" element cannot be empty.", 0); + char cType = ValidateMeasurementType(Type); std::string frame, epoch; @@ -234,104 +218,8 @@ void DnaMeasurement_pimpl::Type(const ::std::string& Type) else epoch = _fileEpoch; - char cType = (Type.c_str())[0]; - switch (cType) { - case 'A': // Horizontal angle - g_parsemsr_tally.A++; - *(_pMeasurementCount) += 1; - _dnaCurrentMsr.reset(new CDnaAngle); - break; - case 'B': // Geodetic azimuth - g_parsemsr_tally.B++; - *(_pMeasurementCount) += 1; - _dnaCurrentMsr.reset(new CDnaAzimuth); - break; - case 'C': // Chord dist - g_parsemsr_tally.C++; - *(_pMeasurementCount) += 1; - _dnaCurrentMsr.reset(new CDnaDistance); - break; - case 'D': // Direction set - //g_parsemsr_tally.D++; See post_Directions() - _dnaCurrentMsr.reset(new CDnaDirectionSet(++(*(_pclusterID)))); - break; - case 'E': // Ellipsoid arc - g_parsemsr_tally.E++; - *(_pMeasurementCount) += 1; - _dnaCurrentMsr.reset(new CDnaDistance); - break; - case 'G': // GPS Baseline (treat as single-baseline cluster) - case 'X': // GPS Baseline cluster - _dnaCurrentMsr.reset(new CDnaGpsBaselineCluster(++(*(_pclusterID)), frame, epoch)); - break; - case 'H': // Orthometric height - g_parsemsr_tally.H++; - *(_pMeasurementCount) += 1; - _dnaCurrentMsr.reset(new CDnaHeight); - break; - case 'I': // Astronomic latitude - g_parsemsr_tally.I++; - *(_pMeasurementCount) += 1; - _dnaCurrentMsr.reset(new CDnaCoordinate); - break; - case 'J': // Astronomic longitude - g_parsemsr_tally.J++; - *(_pMeasurementCount) += 1; - _dnaCurrentMsr.reset(new CDnaCoordinate); - break; - case 'K': // Astronomic azimuth - g_parsemsr_tally.K++; - *(_pMeasurementCount) += 1; - _dnaCurrentMsr.reset(new CDnaAzimuth); - break; - case 'L': // Level difference - g_parsemsr_tally.L++; - *(_pMeasurementCount) += 1; - _dnaCurrentMsr.reset(new CDnaHeightDifference); - break; - case 'M': // MSL arc - g_parsemsr_tally.M++; - *(_pMeasurementCount) += 1; - _dnaCurrentMsr.reset(new CDnaDistance); - break; - case 'P': // Geodetic latitude - g_parsemsr_tally.P++; - *(_pMeasurementCount) += 1; - _dnaCurrentMsr.reset(new CDnaCoordinate); - break; - case 'Q': // Geodetic longitude - g_parsemsr_tally.Q++; - *(_pMeasurementCount) += 1; - _dnaCurrentMsr.reset(new CDnaCoordinate); - break; - case 'R': // Ellipsoidal height - g_parsemsr_tally.R++; - *(_pMeasurementCount) += 1; - _dnaCurrentMsr.reset(new CDnaHeight); - break; - case 'S': // Slope distance - g_parsemsr_tally.S++; - *(_pMeasurementCount) += 1; - _dnaCurrentMsr.reset(new CDnaDistance); - break; - case 'V': // Zenith distance - g_parsemsr_tally.V++; - *(_pMeasurementCount) += 1; - _dnaCurrentMsr.reset(new CDnaDirection); - break; - case 'Y': // GPS point cluster - _dnaCurrentMsr.reset(new CDnaGpsPointCluster(++(*(_pclusterID)), _referenceframe, _epoch)); - break; - case 'Z': // Vertical angle - g_parsemsr_tally.Z++; - *(_pMeasurementCount) += 1; - _dnaCurrentMsr.reset(new CDnaDirection); - break; - default: - std::stringstream ss; - ss << "Unknown measurement type: " << Type; - throw XMLInteropException(ss.str(), 0); - } + _dnaCurrentMsr = CreateMeasurement(cType, _pclusterID, + *(_pMeasurementCount), g_parsemsr_tally, frame, epoch); _dnaCurrentMsr->SetType(Type); } @@ -354,39 +242,18 @@ void DnaMeasurement_pimpl::First(const ::std::string& First) if (!_dnaCurrentMsr) throw XMLInteropException("\"Type\" element must be the first element within \"DnaMeasurement\".", 0); - if (First.empty()) - throw XMLInteropException("\"First\" element cannot be empty.", 0); + ValidateFirst(First); _dnaCurrentMsr->SetFirst(First); } -void DnaMeasurement_pimpl::Second(const ::std::string& Second) +void DnaMeasurement_pimpl::Second(const ::std::string& Second) { if (!_dnaCurrentMsr) throw XMLInteropException("\"Type\" element must be the first element within \"DnaMeasurement\".", 0); - switch (_dnaCurrentMsr->GetTypeC()) { - case 'A': // Horizontal angle - case 'B': // Geodetic azimuth - case 'K': // Astronomic azimuth - case 'C': // Chord dist - case 'E': // Ellipsoid arc - case 'M': // MSL arc - case 'S': // Slope distance - case 'D': // Direction set - case 'G': // GPS Baseline - case 'L': // Level difference - case 'V': // Zenith distance - case 'Z': // Vertical angle - case 'X': // GPS Baseline cluster - if (Second.empty()) - { - std::stringstream ss; - ss << "\"Second\" element cannot be empty for measurement type " << _dnaCurrentMsr->GetTypeC() << - " (first station: " << _dnaCurrentMsr->GetFirst() << ")."; - throw XMLInteropException(ss.str(), 0); - } + ValidateSecond(Second, _dnaCurrentMsr->GetTypeC(), _dnaCurrentMsr->GetFirst()); + if (TypeRequiresSecond(_dnaCurrentMsr->GetTypeC())) _dnaCurrentMsr->SetTarget(Second); - } } void DnaMeasurement_pimpl::Third(const ::std::string& Third) @@ -394,17 +261,7 @@ void DnaMeasurement_pimpl::Third(const ::std::string& Third) if (!_dnaCurrentMsr) throw XMLInteropException("\"Type\" element must be the first element within \"DnaMeasurement\".", 0); - switch (_dnaCurrentMsr->GetTypeC()) { - case 'A': // Horizontal angle - if (Third.empty()) - { - std::stringstream ss; - ss << "\"Third\" element cannot be empty for measurement type " << _dnaCurrentMsr->GetTypeC() << - " (first station: " << _dnaCurrentMsr->GetFirst() << ")."; - throw XMLInteropException(ss.str(), 0); - } - } - + ValidateThird(Third, _dnaCurrentMsr->GetTypeC(), _dnaCurrentMsr->GetFirst()); _dnaCurrentMsr->SetTarget2(Third); } @@ -413,33 +270,7 @@ void DnaMeasurement_pimpl::Value(const ::std::string& Value) if (!_dnaCurrentMsr) throw XMLInteropException("\"Type\" element must be the first element within \"DnaMeasurement\".", 0); - switch (_dnaCurrentMsr->GetTypeC()) { - case 'A': // Horizontal angle - case 'B': // Geodetic azimuth - case 'K': // Astronomic azimuth - case 'C': // Chord dist - case 'E': // Ellipsoid arc - case 'M': // MSL arc - case 'S': // Slope distance - case 'D': // Direction set - case 'H': // Orthometric height - case 'R': // Ellipsoidal height - case 'I': // Astronomic latitude - case 'J': // Astronomic longitude - case 'P': // Geodetic latitude - case 'Q': // Geodetic longitude - case 'L': // Level difference - case 'V': // Zenith distance - case 'Z': // Vertical angle - if (Value.empty()) - { - std::stringstream ss; - ss << "\"Value\" element cannot be empty for measurement type " << _dnaCurrentMsr->GetTypeC() << - " (first station: " << _dnaCurrentMsr->GetFirst() << ")."; - throw XMLInteropException(ss.str(), 0); - } - } - + ValidateValue(Value, _dnaCurrentMsr->GetTypeC(), _dnaCurrentMsr->GetFirst()); _dnaCurrentMsr->SetValue(Value); } @@ -448,41 +279,8 @@ void DnaMeasurement_pimpl::StdDev(const ::std::string& StdDev) if (!_dnaCurrentMsr) throw XMLInteropException("\"Type\" element must be the first element within \"DnaMeasurement\".", 0); - switch (_dnaCurrentMsr->GetTypeC()) { - case 'A': // Horizontal angle - case 'B': // Geodetic azimuth - case 'K': // Astronomic azimuth - case 'C': // Chord dist - case 'E': // Ellipsoid arc - case 'M': // MSL arc - case 'S': // Slope distance - case 'D': // Direction set - case 'H': // Orthometric height - case 'R': // Ellipsoidal height - case 'I': // Astronomic latitude - case 'J': // Astronomic longitude - case 'P': // Geodetic latitude - case 'Q': // Geodetic longitude - case 'L': // Level difference - case 'V': // Zenith distance - case 'Z': // Vertical angle - if (StdDev.empty()) - { - std::stringstream ss; - ss << "\"StdDev\" element cannot be empty for " << _dnaCurrentMsr->GetTypeC() << " measurements (first station: " << _dnaCurrentMsr->GetFirst() << ")."; - throw XMLInteropException(ss.str(), 0); - } - - if (DoubleFromString(StdDev) < PRECISION_1E25) - { - std::stringstream ss; - ss << "\"StdDev\" element cannot contain a zero or negative value for " << _dnaCurrentMsr->GetTypeC() << " measurements (first station: " << _dnaCurrentMsr->GetFirst() << ")."; - throw XMLInteropException(ss.str(), 0); - } - } - + ValidateStdDev(StdDev, _dnaCurrentMsr->GetTypeC(), _dnaCurrentMsr->GetFirst()); _dnaCurrentMsr->SetStdDev(StdDev); - } void DnaMeasurement_pimpl::InstHeight(const ::std::string& InstHeight) @@ -524,27 +322,9 @@ void DnaMeasurement_pimpl::Total(const ::std::string& Total) if (!_dnaCurrentMsr) throw XMLInteropException("\"Type\" element must be the first element within \"DnaMeasurement\".", 0); - switch (_dnaCurrentMsr->GetTypeC()) { - case 'D': // Direction set - case 'G': // Single Baseline (treat as a single-baseline cluster) - case 'X': // GPS Baseline cluster - case 'Y': // GPS point cluster - if (Total.empty()) - throw XMLInteropException("\"Total\" element cannot be empty.", 0); - } - + ValidateTotal(Total, _dnaCurrentMsr->GetTypeC()); _dnaCurrentMsr->SetTotal(Total); - - const UINT32 measCount(LongFromString(Total)); - _total = measCount; - - switch (_dnaCurrentMsr->GetTypeC()) - { - case 'X': // GPS Baseline cluster - if (measCount < 2) - if (_preferSingleXasG) - _dnaCurrentMsr->SetType("G"); - } + _total = LongFromString(Total); } void DnaMeasurement_pimpl::Directions() @@ -556,10 +336,7 @@ void DnaMeasurement_pimpl::Vscale(const ::std::string& Vscale) if (!_dnaCurrentMsr) throw XMLInteropException("\"Type\" element must be the first element within \"DnaMeasurement\".", 0); - if (Vscale.empty()) - _dnaCurrentMsr->SetVscale("1"); - else - _dnaCurrentMsr->SetVscale(Vscale); + _dnaCurrentMsr->SetVscale(DefaultScale(Vscale)); } void DnaMeasurement_pimpl::Epoch(const ::std::string& Epoch) @@ -595,6 +372,30 @@ void DnaMeasurement_pimpl::Epoch(const ::std::string& Epoch) } } +void DnaMeasurement_pimpl::EpochOfObservation(const ::std::string& EpochOfObservation) +{ + if (!_dnaCurrentMsr) + throw XMLInteropException("\"Type\" element must be the first element within \"DnaMeasurement\".", 0); + + try + { + if (EpochOfObservation.empty()) + return; + + _dnaCurrentMsr->SetObservationEpoch(EpochOfObservation); + } + catch (const std::runtime_error& e) { + std::stringstream ss(""); + ss << e.what(); + ss << " - Measurement type: " << _dnaCurrentMsr->GetType() << std::endl << + " - From: " << _dnaCurrentMsr->GetFirst() << std::endl << + " - To: " << _dnaCurrentMsr->GetTarget() << std::endl << + " - Reference frame: " << _dnaCurrentMsr->GetReferenceFrame() << std::endl << + " - Epoch of observation: " << EpochOfObservation << std::endl; + throw XMLInteropException(ss.str(), 0); + } +} + void DnaMeasurement_pimpl::ReferenceFrame(const ::std::string& ReferenceFrame) { if (!_dnaCurrentMsr) @@ -644,10 +445,7 @@ void DnaMeasurement_pimpl::Hscale(const ::std::string& Hscale) if (!_dnaCurrentMsr) throw XMLInteropException("\"Type\" element must be the first element within \"DnaMeasurement\".", 0); - if (Hscale.empty()) - _dnaCurrentMsr->SetHscale("1"); - else - _dnaCurrentMsr->SetHscale(Hscale); + _dnaCurrentMsr->SetHscale(DefaultScale(Hscale)); } @@ -656,10 +454,7 @@ void DnaMeasurement_pimpl::Lscale(const ::std::string& Lscale) if (!_dnaCurrentMsr) throw XMLInteropException("\"Type\" element must be the first element within \"DnaMeasurement\".", 0); - if (Lscale.empty()) - _dnaCurrentMsr->SetLscale("1"); - else - _dnaCurrentMsr->SetLscale(Lscale); + _dnaCurrentMsr->SetLscale(DefaultScale(Lscale)); } @@ -668,10 +463,7 @@ void DnaMeasurement_pimpl::Pscale(const ::std::string& Pscale) if (!_dnaCurrentMsr) throw XMLInteropException("\"Type\" element must be the first element within \"DnaMeasurement\".", 0); - if (Pscale.empty()) - _dnaCurrentMsr->SetPscale("1"); - else - _dnaCurrentMsr->SetPscale(Pscale); + _dnaCurrentMsr->SetPscale(DefaultScale(Pscale)); } @@ -729,140 +521,13 @@ void DnaMeasurement_pimpl::post_DnaMeasurement() if (!_dnaCurrentMsr) throw XMLInteropException("\"Type\" element must be the first element within \"DnaMeasurement\".", 0); - UINT32 total, found; - - // Is the direction set empty and ignored? - // If so, do nothing (an invalid measurement). - switch (_dnaCurrentMsr->GetTypeC()) - { - case 'D': - total = _dnaCurrentMsr->GetTotal(); - if (total == 0 && _dnaCurrentMsr->GetIgnore()) - return; - break; - } + // Ignored empty direction set — skip silently + if (_dnaCurrentMsr->GetTypeC() == 'D' && + _dnaCurrentMsr->GetTotal() == 0 && _dnaCurrentMsr->GetIgnore()) + return; - // Now that the measurement has completed, capture all essential elements _vParentMsrs->push_back(_dnaCurrentMsr); - - // set "First" to be that of the first in the set - switch (_dnaCurrentMsr->GetTypeC()) - { - case 'D': - - total = _dnaCurrentMsr->GetTotal(); - found = static_cast(_dnaCurrentMsr->GetDirections_ptr()->size()); - if (total != found) - { - std::stringstream ss, ss2; - ss << - "...', total of " << total << " element(s)" << std::endl << - " - found " << found << " in the set. '"; - ss2 << " ~ " << total << ""; - throw ::xsd::cxx::parser::expected_element< char >( - ss.str(), ss2.str()); - } - - found = 0; - - // Test for non-ignored measurements - for_each(_dnaCurrentMsr->GetDirections_ptr()->begin(), - _dnaCurrentMsr->GetDirections_ptr()->end(), - [this, &found](CDnaDirection& d) { - if (d.NotIgnored()) - found++; - }); - - if (found == 0 && _dnaCurrentMsr->NotIgnored()) - { - std::stringstream ss, ss2; - ss << - "...', total of " << total << " element(s)" << std::endl << - " - There aren't any non-ignored directions in the set. "; - ss2 << " ~ " << total << ""; - throw ::xsd::cxx::parser::expected_element< char >( - ss.str(), ss2.str()); - } - - _dnaCurrentMsr->SetNonIgnoredDirns(found); - - // Okay, capture all the elements for this direction set - for_each(_dnaCurrentMsr->GetDirections_ptr()->begin(), - _dnaCurrentMsr->GetDirections_ptr()->end(), - [this](CDnaDirection& d) { - d.SetType(_dnaCurrentMsr->GetType()); - d.SetFirst(_dnaCurrentMsr->GetFirst()); - // This is a target direction, so set zero recorded total so as to - // distinguish this direction from the RO, for which the recorded - // total will be the number of target directions - d.SetRecordedTotal(0); - }); - - break; - - case 'G': - case 'X': - - total = _dnaCurrentMsr->GetTotal(); - found = static_cast(_dnaCurrentMsr->GetBaselines_ptr()->size()); - if (total != found) - { - std::stringstream ss, ss2; - ss << - "...', total of " << total << " element(s)" << std::endl << - " - found " << found << " in the set. '"; - ss2 << " ~ " << total << ""; - throw ::xsd::cxx::parser::expected_element< char >( - ss.str(), ss2.str()); - } - - // Okay, capture all the elements for this baseline (cluster) - for_each(_dnaCurrentMsr->GetBaselines_ptr()->begin(), - _dnaCurrentMsr->GetBaselines_ptr()->end(), - [this](CDnaGpsBaseline& b) { - b.SetIgnore(_dnaCurrentMsr->GetIgnore()); - b.SetRecordedTotal(_dnaCurrentMsr->GetTotal()); - - b.SetPscale(_dnaCurrentMsr->GetPscale()); - b.SetLscale(_dnaCurrentMsr->GetLscale()); - b.SetHscale(_dnaCurrentMsr->GetHscale()); - b.SetVscale(_dnaCurrentMsr->GetVscale()); - }); - - break; - - case 'Y': - - total = _dnaCurrentMsr->GetTotal(); - found = static_cast(_dnaCurrentMsr->GetPoints_ptr()->size()); - if (total != found) - { - std::stringstream ss, ss2; - ss << - "...', total of " << total << " element(s)" << std::endl << - " - found " << found << " in the set. '"; - ss2 << " ~ " << total << ""; - throw ::xsd::cxx::parser::expected_element< char >( - ss.str(), ss2.str()); - } - - // Okay, capture all the elements for this point cluster - for_each(_dnaCurrentMsr->GetPoints_ptr()->begin(), - _dnaCurrentMsr->GetPoints_ptr()->end(), - [this](CDnaGpsPoint& p) { - p.SetIgnore(_dnaCurrentMsr->GetIgnore()); - p.SetCoordType(_dnaCurrentMsr->GetCoordType()); - p.SetRecordedTotal(_dnaCurrentMsr->GetTotal()); - - p.SetPscale(_dnaCurrentMsr->GetPscale()); - p.SetLscale(_dnaCurrentMsr->GetLscale()); - p.SetHscale(_dnaCurrentMsr->GetHscale()); - p.SetVscale(_dnaCurrentMsr->GetVscale()); - }); - break; - - } - + FinaliseMeasurement(_dnaCurrentMsr, _preferSingleXasG); } // DnaStation_pimpl @@ -888,19 +553,16 @@ void DnaStation_pimpl::pre() void DnaStation_pimpl::Name(const ::std::string& Name) { - if (Name.empty()) - throw XMLInteropException("DnaStation \"Name\" element cannot be empty.", 0); + ValidateStationName(Name); _dnaCurrentStn->SetName(Name); } void DnaStation_pimpl::Constraints(const ::std::string& Constraints) { - if (Constraints.empty()) - _dnaCurrentStn->SetConstraints("FFF"); - else - _dnaCurrentStn->SetConstraints(Constraints); - g_parsestn_tally.addstation(Constraints); + std::string c = DefaultConstraints(Constraints); + _dnaCurrentStn->SetConstraints(c); + g_parsestn_tally.addstation(c); } @@ -915,8 +577,7 @@ void DnaStation_pimpl::StationCoord() void DnaStation_pimpl::Description(const ::std::string& Description) { - _dnaCurrentStn->SetDescription( - findandreplace((const ::std::string&)Description, (const ::std::string&)"&", (const ::std::string&)"and")); + _dnaCurrentStn->SetDescription(SanitiseDescription(Description)); } void DnaStation_pimpl::post_DnaStation() @@ -942,6 +603,19 @@ DnaXmlFormat_pimpl::DnaXmlFormat_pimpl(std::ifstream* is, PUINT32 clusterID, con _referenceframe = referenceframe; _epoch = epoch; + // Initialise file EPSG and epoch from the default reference frame. + // Previously these were set by the XSD attribute defaults during + // validation. With dont_validate, the attribute parsers are only + // called when the attributes are present in the XML, so we need + // sensible fallbacks. + try { + _fileEpsg = epsgStringFromName(_referenceframe); + } + catch (...) { + _fileEpsg.clear(); + } + _fileEpoch = epoch; + // capture first file flag _firstFile = firstFile; diff --git a/dynadjust/dynadjust/dnaimport/dnaparser_pimpl.hxx b/dynadjust/dynadjust/dnaimport/dnaparser_pimpl.hxx index 7f66ecd63..58d1e8213 100644 --- a/dynadjust/dynadjust/dnaimport/dnaparser_pimpl.hxx +++ b/dynadjust/dynadjust/dnaimport/dnaparser_pimpl.hxx @@ -91,6 +91,7 @@ public: virtual void Directions (); virtual void Vscale (const ::std::string&); virtual void Epoch (const ::std::string&); + virtual void EpochOfObservation (const ::std::string&); virtual void ReferenceFrame (const ::std::string&); virtual void GPSBaseline (); virtual void Hscale (const ::std::string&); diff --git a/dynadjust/dynadjust/dnaimport/dnaparser_pskel.cxx b/dynadjust/dynadjust/dnaimport/dnaparser_pskel.cxx index 27cdc8667..b56389d46 100644 --- a/dynadjust/dynadjust/dnaimport/dnaparser_pskel.cxx +++ b/dynadjust/dynadjust/dnaimport/dnaparser_pskel.cxx @@ -38,8 +38,6 @@ #include -#include - using namespace dynadjust::measurements; // Clusterpoint_pskel @@ -260,6 +258,11 @@ void DnaMeasurement_pskel::Epoch_parser (::xml_schema::string_pskel& p) this->Epoch_parser_ = &p; } +void DnaMeasurement_pskel::EpochOfObservation_parser (::xml_schema::string_pskel& p) +{ + this->EpochOfObservation_parser_ = &p; +} + void DnaMeasurement_pskel::GPSBaseline_parser (::GPSBaseline_pskel& p) { this->GPSBaseline_parser_ = &p; @@ -320,6 +323,7 @@ void DnaMeasurement_pskel::parsers ( ::Directions_pskel& Directions, ::xml_schema::string_pskel& Vscale, ::xml_schema::string_pskel& Epoch, + ::xml_schema::string_pskel& EpochOfObservation, ::xml_schema::string_pskel& ReferenceFrame, ::GPSBaseline_pskel& GPSBaseline, ::xml_schema::string_pskel& Hscale, @@ -345,6 +349,7 @@ void DnaMeasurement_pskel::parsers ( this->Directions_parser_ = &Directions; this->Vscale_parser_ = &Vscale; this->Epoch_parser_ = &Epoch; + this->EpochOfObservation_parser_ = &EpochOfObservation; this->ReferenceFrame_parser_ = &ReferenceFrame; this->GPSBaseline_parser_ = &GPSBaseline; this->Hscale_parser_ = &Hscale; @@ -372,6 +377,7 @@ DnaMeasurement_pskel::DnaMeasurement_pskel () Directions_parser_ (0), Vscale_parser_ (0), Epoch_parser_ (0), + EpochOfObservation_parser_ (0), ReferenceFrame_parser_ (0), GPSBaseline_parser_ (0), Hscale_parser_ (0), @@ -1328,6 +1334,10 @@ void DnaMeasurement_pskel::Epoch (const ::std::string&) { } +void DnaMeasurement_pskel::EpochOfObservation (const ::std::string&) +{ +} + void DnaMeasurement_pskel::ReferenceFrame (const ::std::string&) { } @@ -1515,6 +1525,16 @@ bool DnaMeasurement_pskel::_start_element_impl (const ::xml_schema::ro_string& n return true; } + if (n == "EpochOfObservation" && ns.empty ()) + { + this->::xml_schema::complex_content::context_.top ().parser_ = this->EpochOfObservation_parser_; + + if (this->EpochOfObservation_parser_) + this->EpochOfObservation_parser_->pre (); + + return true; + } + if (n == "ReferenceFrame" && ns.empty ()) { this->::xml_schema::complex_content::context_.top ().parser_ = this->ReferenceFrame_parser_; @@ -1739,6 +1759,14 @@ bool DnaMeasurement_pskel::_end_element_impl (const ::xml_schema::ro_string& ns, return true; } + if (n == "EpochOfObservation" && ns.empty ()) + { + if (this->EpochOfObservation_parser_) + this->EpochOfObservation (this->EpochOfObservation_parser_->post_string ()); + + return true; + } + if (n == "ReferenceFrame" && ns.empty ()) { if (this->ReferenceFrame_parser_) diff --git a/dynadjust/dynadjust/dnaimport/dnaparser_pskel.hxx b/dynadjust/dynadjust/dnaimport/dnaparser_pskel.hxx index 79911766b..30fdd8643 100644 --- a/dynadjust/dynadjust/dnaimport/dnaparser_pskel.hxx +++ b/dynadjust/dynadjust/dnaimport/dnaparser_pskel.hxx @@ -467,6 +467,7 @@ public: virtual void Directions (); virtual void Vscale (const ::std::string&); virtual void Epoch (const ::std::string&); + virtual void EpochOfObservation (const ::std::string&); virtual void ReferenceFrame (const ::std::string&); virtual void GPSBaseline (); virtual void Hscale (const ::std::string&); @@ -558,6 +559,7 @@ public: void Directions_parser (::Directions_pskel&); void Vscale_parser (::xml_schema::string_pskel&); void Epoch_parser (::xml_schema::string_pskel&); + void EpochOfObservation_parser (::xml_schema::string_pskel&); void ReferenceFrame_parser (::xml_schema::string_pskel&); void GPSBaseline_parser (::GPSBaseline_pskel&); void Hscale_parser (::xml_schema::string_pskel&); @@ -583,6 +585,7 @@ public: ::Directions_pskel& /* Directions */, ::xml_schema::string_pskel& /* Vscale */, ::xml_schema::string_pskel& /* Epoch */, + ::xml_schema::string_pskel& /* EpochOfObservation */, ::xml_schema::string_pskel& /* ReferenceFrame */, ::GPSBaseline_pskel& /* GPSBaseline */, ::xml_schema::string_pskel& /* Hscale */, @@ -630,6 +633,7 @@ protected: ::Directions_pskel* Directions_parser_; ::xml_schema::string_pskel* Vscale_parser_; ::xml_schema::string_pskel* Epoch_parser_; + ::xml_schema::string_pskel* EpochOfObservation_parser_; ::xml_schema::string_pskel* ReferenceFrame_parser_; ::GPSBaseline_pskel* GPSBaseline_parser_; ::xml_schema::string_pskel* Hscale_parser_; diff --git a/dynadjust/dynadjust/dnaimport/dnaparser_validators.cpp b/dynadjust/dynadjust/dnaimport/dnaparser_validators.cpp new file mode 100644 index 000000000..4850c17e8 --- /dev/null +++ b/dynadjust/dynadjust/dnaimport/dnaparser_validators.cpp @@ -0,0 +1,403 @@ +//============================================================================ +// Name : dnaparser_validators.cpp +// Author : Dale Roberts +// Copyright : Copyright 2025 Geoscience Australia +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http ://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Description : Shared validation, factory and finalisation functions for +// DynaML parsing. +//============================================================================ + +#include +#include + +#include +#include +#include + +using dynadjust::exception::XMLInteropException; +using dynadjust::measurements::CDnaAngle; +using dynadjust::measurements::CDnaAzimuth; +using dynadjust::measurements::CDnaCoordinate; +using dynadjust::measurements::CDnaDirection; +using dynadjust::measurements::CDnaDirectionSet; +using dynadjust::measurements::CDnaDistance; +using dynadjust::measurements::CDnaGpsBaselineCluster; +using dynadjust::measurements::CDnaGpsPointCluster; +using dynadjust::measurements::CDnaGpsBaseline; +using dynadjust::measurements::CDnaGpsPoint; +using dynadjust::measurements::CDnaHeight; +using dynadjust::measurements::CDnaHeightDifference; + +namespace dynadjust { +namespace import { + +bool TypeRequiresSecond(char type_c) { + switch (type_c) { + case 'A': case 'B': case 'K': + case 'C': case 'E': case 'M': case 'S': + case 'D': case 'G': case 'L': + case 'V': case 'Z': case 'X': + return true; + default: + return false; + } +} + +bool TypeRequiresValueStdDev(char type_c) { + switch (type_c) { + case 'A': case 'B': case 'K': + case 'C': case 'E': case 'M': case 'S': + case 'D': + case 'H': case 'R': + case 'I': case 'J': case 'P': case 'Q': + case 'L': case 'V': case 'Z': + return true; + default: + return false; + } +} + +char ValidateMeasurementType(const std::string& type) { + if (type.empty()) { + throw XMLInteropException("\"Type\" element cannot be empty.", 0); + } + char c = type[0]; + switch (c) { + case 'A': case 'B': case 'C': case 'D': case 'E': + case 'G': case 'H': case 'I': case 'J': case 'K': + case 'L': case 'M': case 'P': case 'Q': case 'R': + case 'S': case 'V': case 'X': case 'Y': case 'Z': + return c; + default: + std::stringstream ss; + ss << "Unknown measurement type: " << type; + throw XMLInteropException(ss.str(), 0); + } +} + +void ValidateFirst(const std::string& first) { + if (first.empty()) { + throw XMLInteropException("\"First\" element cannot be empty.", 0); + } +} + +void ValidateSecond(const std::string& second, char type_c, + const std::string& first) { + if (TypeRequiresSecond(type_c) && second.empty()) { + std::stringstream ss; + ss << "\"Second\" element cannot be empty for measurement type " + << type_c << " (first station: " << first << ")."; + throw XMLInteropException(ss.str(), 0); + } +} + +void ValidateThird(const std::string& third, char type_c, + const std::string& first) { + if (type_c == 'A' && third.empty()) { + std::stringstream ss; + ss << "\"Third\" element cannot be empty for measurement type " + << type_c << " (first station: " << first << ")."; + throw XMLInteropException(ss.str(), 0); + } +} + +void ValidateValue(const std::string& value, char type_c, + const std::string& first) { + if (TypeRequiresValueStdDev(type_c) && value.empty()) { + std::stringstream ss; + ss << "\"Value\" element cannot be empty for measurement type " + << type_c << " (first station: " << first << ")."; + throw XMLInteropException(ss.str(), 0); + } +} + +void ValidateStdDev(const std::string& stddev, char type_c, + const std::string& first) { + if (!TypeRequiresValueStdDev(type_c)) { + return; + } + if (stddev.empty()) { + std::stringstream ss; + ss << "\"StdDev\" element cannot be empty for " << type_c + << " measurements (first station: " << first << ")."; + throw XMLInteropException(ss.str(), 0); + } + if (DoubleFromString(stddev) < PRECISION_1E25) { + std::stringstream ss; + ss << "\"StdDev\" element cannot contain a zero or negative value for " + << type_c << " measurements (first station: " << first << ")."; + throw XMLInteropException(ss.str(), 0); + } +} + +void ValidateTotal(const std::string& total, char type_c) { + switch (type_c) { + case 'D': case 'G': case 'X': case 'Y': + if (total.empty()) { + throw XMLInteropException("\"Total\" element cannot be empty.", 0); + } + } +} + +void ValidateStationName(const std::string& name) { + if (name.empty()) { + throw XMLInteropException( + "DnaStation \"Name\" element cannot be empty.", 0); + } +} + +measurements::dnaMsrPtr CreateMeasurement( + char type_c, PUINT32 cluster_id, UINT32& msr_count, + measurements::MsrTally& tally, const std::string& frame, + const std::string& epoch) { + measurements::dnaMsrPtr msr; + + switch (type_c) { + case 'A': + tally.A++; + msr_count++; + msr.reset(new CDnaAngle); + break; + case 'B': + tally.B++; + msr_count++; + msr.reset(new CDnaAzimuth); + break; + case 'C': + tally.C++; + msr_count++; + msr.reset(new CDnaDistance); + break; + case 'D': + // D tallies are incremented per-direction in post_Directions. + msr.reset(new CDnaDirectionSet(++(*cluster_id))); + break; + case 'E': + tally.E++; + msr_count++; + msr.reset(new CDnaDistance); + break; + case 'G': + case 'X': + // G/X tallies are incremented per-component in GPSBaseline X/Y/Z. + msr.reset(new CDnaGpsBaselineCluster(++(*cluster_id), frame, epoch)); + break; + case 'H': + tally.H++; + msr_count++; + msr.reset(new CDnaHeight); + break; + case 'I': + tally.I++; + msr_count++; + msr.reset(new CDnaCoordinate); + break; + case 'J': + tally.J++; + msr_count++; + msr.reset(new CDnaCoordinate); + break; + case 'K': + tally.K++; + msr_count++; + msr.reset(new CDnaAzimuth); + break; + case 'L': + tally.L++; + msr_count++; + msr.reset(new CDnaHeightDifference); + break; + case 'M': + tally.M++; + msr_count++; + msr.reset(new CDnaDistance); + break; + case 'P': + tally.P++; + msr_count++; + msr.reset(new CDnaCoordinate); + break; + case 'Q': + tally.Q++; + msr_count++; + msr.reset(new CDnaCoordinate); + break; + case 'R': + tally.R++; + msr_count++; + msr.reset(new CDnaHeight); + break; + case 'S': + tally.S++; + msr_count++; + msr.reset(new CDnaDistance); + break; + case 'V': + tally.V++; + msr_count++; + msr.reset(new CDnaDirection); + break; + case 'Y': + // Y tallies are incremented per-component in Clusterpoint X/Y/Z. + msr.reset(new CDnaGpsPointCluster(++(*cluster_id), frame, epoch)); + break; + case 'Z': + tally.Z++; + msr_count++; + msr.reset(new CDnaDirection); + break; + } + + return msr; +} + +void FinaliseMeasurement(measurements::dnaMsrPtr& msr, + bool prefer_single_x_as_g) { + UINT32 total = 0; + UINT32 found = 0; + + switch (msr->GetTypeC()) { + case 'D': { + total = msr->GetTotal(); + + // Ignored empty direction set — skip silently. + if (total == 0 && msr->GetIgnore()) { + return; + } + + found = static_cast(msr->GetDirections_ptr()->size()); + if (total != found) { + std::stringstream ss; + ss << "Direction set declares total of " << total + << " but found " << found << " directions."; + throw XMLInteropException(ss.str(), 0); + } + + // Count non-ignored directions. + found = 0; + for (CDnaDirection& d : *msr->GetDirections_ptr()) { + if (d.NotIgnored()) { + found++; + } + } + + if (found == 0 && msr->NotIgnored()) { + std::stringstream ss; + ss << "Direction set declares total of " << total + << " but there aren't any non-ignored directions in the set."; + throw XMLInteropException(ss.str(), 0); + } + + msr->SetNonIgnoredDirns(found); + + // Propagate parent properties to child directions. + for (CDnaDirection& d : *msr->GetDirections_ptr()) { + d.SetType(msr->GetType()); + d.SetFirst(msr->GetFirst()); + d.SetRecordedTotal(0); + } + break; + } + + case 'G': + case 'X': { + total = msr->GetTotal(); + found = static_cast(msr->GetBaselines_ptr()->size()); + if (total != found) { + std::stringstream ss; + ss << "GPS baseline cluster declares total of " << total + << " but found " << found << " baselines."; + throw XMLInteropException(ss.str(), 0); + } + + // Promote single-baseline X to G if preferred. + if (msr->GetTypeC() == 'X' && total < 2 && prefer_single_x_as_g) { + msr->SetType("G"); + } + + // Propagate parent properties to child baselines. + for (CDnaGpsBaseline& b : *msr->GetBaselines_ptr()) { + b.SetIgnore(msr->GetIgnore()); + b.SetRecordedTotal(msr->GetTotal()); + b.SetPscale(msr->GetPscale()); + b.SetLscale(msr->GetLscale()); + b.SetHscale(msr->GetHscale()); + b.SetVscale(msr->GetVscale()); + } + break; + } + + case 'Y': { + total = msr->GetTotal(); + found = static_cast(msr->GetPoints_ptr()->size()); + if (total != found) { + std::stringstream ss; + ss << "GPS point cluster declares total of " << total + << " but found " << found << " clusterpoints."; + throw XMLInteropException(ss.str(), 0); + } + + // Propagate parent properties to child points. + for (CDnaGpsPoint& p : *msr->GetPoints_ptr()) { + p.SetIgnore(msr->GetIgnore()); + p.SetCoordType(msr->GetCoordType()); + p.SetRecordedTotal(msr->GetTotal()); + p.SetPscale(msr->GetPscale()); + p.SetLscale(msr->GetLscale()); + p.SetHscale(msr->GetHscale()); + p.SetVscale(msr->GetVscale()); + } + break; + } + } +} + +void ValidateDirectionInSet(const measurements::dnaMsrPtr& direction_set, + bool direction_ignored, + UINT32 declared_total) { + // If the parent set is ignored, no further checks. + if (direction_set->GetIgnore()) { + return; + } + + // If the direction itself is not ignored, no problem. + if (!direction_ignored) { + return; + } + + // Ignoring this direction — check it doesn't leave the set empty. + UINT32 remaining = direction_set->GetTotal() - 1; + if (remaining == 0) { + std::stringstream ss; + ss << "Direction set declares total of " << declared_total + << " but there aren't any non-ignored directions in the set."; + throw XMLInteropException(ss.str(), 0); + } +} + +std::string DefaultConstraints(const std::string& constraints) { + return constraints.empty() ? "FFF" : constraints; +} + +std::string DefaultScale(const std::string& scale) { + return scale.empty() ? "1" : scale; +} + +std::string SanitiseDescription(const std::string& desc) { + return findandreplace(desc, std::string("&"), std::string("and")); +} + +} // namespace import +} // namespace dynadjust diff --git a/dynadjust/dynadjust/dnaimport/dnaparser_validators.hpp b/dynadjust/dynadjust/dnaimport/dnaparser_validators.hpp new file mode 100644 index 000000000..05c3cd24f --- /dev/null +++ b/dynadjust/dynadjust/dnaimport/dnaparser_validators.hpp @@ -0,0 +1,104 @@ +//============================================================================ +// Name : dnaparser_validators.hpp +// Author : Dale Roberts +// Copyright : Copyright 2025 Geoscience Australia +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http ://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Description : Shared validation, factory and finalisation functions for +// DynaML parsing. These are used by the XML (pimpl) parser +// and can be reused by any future parser (e.g. JSON). +//============================================================================ + +#ifndef DYNADJUST_DNAIMPORT_DNAPARSER_VALIDATORS_HPP_ +#define DYNADJUST_DNAIMPORT_DNAPARSER_VALIDATORS_HPP_ + +#include + +#include + +namespace dynadjust { +namespace import { + +// Validates type is non-empty and a recognised measurement letter. +// Returns the type character. +char ValidateMeasurementType(const std::string& type); + +// First station must be non-empty. +void ValidateFirst(const std::string& first); + +// Second station must be non-empty for types that require it. +void ValidateSecond(const std::string& second, char type_c, + const std::string& first); + +// Third station must be non-empty for horizontal angles. +void ValidateThird(const std::string& third, char type_c, + const std::string& first); + +// Value must be non-empty for scalar measurement types. +void ValidateValue(const std::string& value, char type_c, + const std::string& first); + +// StdDev must be non-empty and positive for scalar measurement types. +void ValidateStdDev(const std::string& stddev, char type_c, + const std::string& first); + +// Total must be non-empty for cluster/set measurement types (D, G, X, Y). +void ValidateTotal(const std::string& total, char type_c); + +// Station name must be non-empty. +void ValidateStationName(const std::string& name); + +// Returns true if the measurement type requires a Second station. +bool TypeRequiresSecond(char type_c); + +// Returns true if the measurement type is a scalar type that +// requires Value and StdDev fields. +bool TypeRequiresValueStdDev(char type_c); + +// Measurement factory — creates the correct subclass from a type +// character. Increments tallies and measurement count for scalar +// types. Cluster types (G, X, D, Y) have their tallies updated +// per-component by the caller. +measurements::dnaMsrPtr CreateMeasurement( + char type_c, PUINT32 cluster_id, UINT32& msr_count, + measurements::MsrTally& tally, const std::string& frame, + const std::string& epoch); + +// Post-parse finalisation — validates child counts match declared +// totals, propagates parent properties (ignore, scales, type) to +// child elements. Call after all children (directions / baselines / +// clusterpoints) have been added. +void FinaliseMeasurement(measurements::dnaMsrPtr& msr, + bool prefer_single_x_as_g); + +// Direction-in-set post-validation. Called after parsing each +// Directions sub-element to check that ignoring a direction doesn't +// leave the set empty. +void ValidateDirectionInSet(const measurements::dnaMsrPtr& direction_set, + bool direction_ignored, + UINT32 declared_total); + +// Returns "FFF" if empty, otherwise returns input unchanged. +std::string DefaultConstraints(const std::string& constraints); + +// Returns "1" if empty, otherwise returns input unchanged. +std::string DefaultScale(const std::string& scale); + +// Replaces "&" with "and" in station descriptions. +std::string SanitiseDescription(const std::string& description); + +} // namespace import +} // namespace dynadjust + +#endif // DYNADJUST_DNAIMPORT_DNAPARSER_VALIDATORS_HPP_ diff --git a/dynadjust/dynadjust/dnaimportwrapper/dnaimportwrapper.cpp b/dynadjust/dynadjust/dnaimportwrapper/dnaimportwrapper.cpp index cb5b6c1c8..ffd48a39c 100644 --- a/dynadjust/dynadjust/dnaimportwrapper/dnaimportwrapper.cpp +++ b/dynadjust/dynadjust/dnaimportwrapper/dnaimportwrapper.cpp @@ -318,6 +318,29 @@ int ParseCommandLineOptions(const int& argc, char* argv[], const boost::program_ p.i.user_supplied_epoch = 1; } + if (vm.count(OBSERVATION_EPOCH)) { + // Get today's date? + if (iequals(p.i.observation_epoch, "today")) + p.i.observation_epoch = stringFromToday(); + // Has the user supplied the year only? + else if (p.i.observation_epoch.rfind(".") == std::string::npos) + p.i.observation_epoch.insert(0, "01.01."); + + if (p.i.observation_epoch.length() < 10) { + std::string dateStr = FormatDateString(p.i.observation_epoch); + if (dateStr.empty()) { + std::cout << std::endl + << "- Error: Cannot parse observation epoch '" << p.i.observation_epoch << "'." << std::endl + << " Please supply date in the format dd.mm.yyyy" << std::endl + << std::endl; + return EXIT_FAILURE; + } + p.i.observation_epoch = dateStr; + } + + p.i.user_supplied_observation_epoch = 1; + } + ////////////////////////////////////////////////////////////////////////////// // Data screening options if (vm.count(GET_MSRS_TRANSCENDING_BOX)) p.i.include_transcending_msrs = 1; @@ -1029,7 +1052,7 @@ int ImportDataFiles(dna_import& parserDynaML, vdnaStnPtr* vStations, vdnaMsrPtr* // Initialise the 'default' datum (frame and epoch) for the project, from the first file, unless the // frame and epoch have been set by the user, in which case InitialiseDatum has already initialised // the datum. - parserDynaML.InitialiseDatum(p.i.reference_frame, p.i.epoch); + parserDynaML.InitialiseDatum(p.i.reference_frame, p.i.epoch, p.i.observation_epoch); } catch (const XMLInteropException& e) { std::stringstream ss; ss << "- Error: "; @@ -1280,11 +1303,15 @@ int main(int argc, char* argv[]) { (std::string("Project epoch for all stations and measurements when input files do not " "specify an epoch. Default is ") + p.i.epoch + ".") - .c_str())(OVERRIDE_INPUT_FRAME, - (std::string("Replace the reference frame specified in the input files with " - "the reference frame specified by arg in --") + - std::string(REFERENCE_FRAME)) - .c_str()); + .c_str())(OBSERVATION_EPOCH, boost::program_options::value(&p.i.observation_epoch), + "Default observation epoch (immutable under dnareftran) for " + "measurements that do not supply one. Format dd.mm.yyyy. " + "Use special value \"today\" to capture the current date.")( + OVERRIDE_INPUT_FRAME, + (std::string("Replace the reference frame specified in the input files with " + "the reference frame specified by arg in --") + + std::string(REFERENCE_FRAME)) + .c_str()); data_screening_options.add_options()( BOUNDING_BOX, boost::program_options::value(&p.i.bounding_box), @@ -1511,7 +1538,7 @@ int main(int argc, char* argv[]) { // See comments in InitialiseDatum() try { // Initialise the 'default' datum for the project. - parserDynaML.InitialiseDatum(p.i.reference_frame, p.i.epoch); + parserDynaML.InitialiseDatum(p.i.reference_frame, p.i.epoch, p.i.observation_epoch); } catch (const XMLInteropException& e) { std::cout << std::endl << cmd_line_banner; imp_file << std::endl << cmd_line_banner; @@ -1678,6 +1705,25 @@ int main(int argc, char* argv[]) { if (vm.count(STATION_DISCONTINUITY_FILE)) { p.i.apply_discontinuities = true; + // Static datums have no meaningful reference-frame epoch, so the discontinuity + // matcher will fall back to that (possibly incorrect) value unless the user + // supplies an observation epoch per-measurement or via --observation-epoch. + // See issue #325. + if (isEpsgDatumStatic(epsgCode) && !p.i.user_supplied_observation_epoch) { + std::stringstream wss; + wss << std::endl + << "- Warning: --discontinuity-file is in use with static datum " << p.i.reference_frame + << "." << std::endl + << " Station-instance allocation relies on 'Epoch of Observation'. Supply it per-measurement" << std::endl + << " via / DNA column 25, or set a project default via --observation-epoch." << std::endl + << " Without it, the reference-frame epoch will be used as a fallback, which may be incorrect" << std::endl + << " for a static datum." << std::endl + << std::endl; + if (!p.g.quiet) + std::cout << wss.str(); + imp_file << wss.str(); + } + // Does it exist? if (!std::filesystem::exists(p.i.stn_discontinuityfile)) { std::filesystem::path discontPath(p.i.stn_discontinuityfile); @@ -1739,20 +1785,11 @@ int main(int argc, char* argv[]) { } // Import data as normal else { - // Change current directory to the import folder - // A hack to circumvent the problem caused by importing DynaML files in - // different directories to where import is run from, causing errors - // because DynaML.xsd cannot be found. - std::filesystem::path currentPath(std::filesystem::current_path()); - std::filesystem::current_path(std::filesystem::path(p.g.input_folder)); - // Import all data as-is. // All filtering is performed later below if (ImportDataFiles(parserDynaML, &vStations, &vMeasurements, &vstationsTotal, &vmeasurementsTotal, &imp_file, &vinput_file_meta, &parsestnTally, &parsemsrTally, errorCount, p) != EXIT_SUCCESS) return EXIT_FAILURE; - - current_path(currentPath); } epsgCode = epsgCodeFromName(p.i.reference_frame); @@ -2544,6 +2581,12 @@ int main(int argc, char* argv[]) { } } + // Apply the project-level observation_epoch (from --observation-epoch) to measurements + // whose observation_epoch is empty or auto-defaulted from the reference-frame epoch. + // No-op when --observation-epoch was not supplied. + if (p.i.user_supplied_observation_epoch) + parserDynaML.ApplyProjectObservationEpoch((vdnaMsrPtr*)&vmeasurementsTotal); + // Create binary measurement file. // Binary measurement file can be serialised without stations if (msrCount > 0) { diff --git a/dynadjust/dynadjust/dnaplotwrapper/CMakeLists.txt b/dynadjust/dynadjust/dnaplotwrapper/CMakeLists.txt index 4cc3bf6c3..bd5d6697b 100644 --- a/dynadjust/dynadjust/dnaplotwrapper/CMakeLists.txt +++ b/dynadjust/dynadjust/dnaplotwrapper/CMakeLists.txt @@ -83,8 +83,8 @@ if(BUILD_STATIC) "-framework CoreFoundation") else() target_link_options(${STATIC_TARGET_NAME} PRIVATE - -static) - + -static-libgcc + -static-libstdc++) endif() elseif(WIN32) # On Windows with MSVC diff --git a/dynadjust/dynadjust/dnareftran/dnareftran.cpp b/dynadjust/dynadjust/dnareftran/dnareftran.cpp index 03ddaa838..c21a1ec28 100644 --- a/dynadjust/dynadjust/dnareftran/dnareftran.cpp +++ b/dynadjust/dynadjust/dnareftran/dnareftran.cpp @@ -82,8 +82,6 @@ void dna_reftran::TransformBinaryFiles(const std::string& bstFile, const std::st // Identify and apply any substitutions for WGS84 in the list of measurements ApplyMeasurementFrameSubstitutions(); - datumTo_.SetDatumFromName(newFrame, newEpoch); - // 2. Transform measurements first (because pre-transformed station // coordinates are required) TransformMeasurementRecords(newFrame, newEpoch); @@ -312,6 +310,14 @@ void dna_reftran::LoadWGS84FrameSubstitutions() frameSubstitution.reset(new WGS84_ITRF2014); _frameSubstitutions.push_back(frameSubstitution); + // SIRGAS95 to ITRF94 + frameSubstitution.reset(new SIRGAS95_ITRF94); + _frameSubstitutions.push_back(frameSubstitution); + + // SIRGAS2000 to ITRF2000 + frameSubstitution.reset(new SIRGAS2000_ITRF2000); + _frameSubstitutions.push_back(frameSubstitution); + std::sort(_frameSubstitutions.begin(), _frameSubstitutions.end(), CompareSubstituteOnFrameName< frame_substitutions_t, std::string>()); @@ -382,6 +388,49 @@ void dna_reftran::LogFrameSubstitutions(std::vector& substit } +void dna_reftran::ApplyToFrameSubstitution() +{ + std::string epsgSubstitute; + + std::string strEpsgTo(datumTo_.GetEpsgCode_s()); + std::string strEpochTo(datumTo_.GetEpoch_s()); + + _v_frame_substitutions.clear(); + + try { + if (IsolateandApplySubstitute(strEpsgTo, strEpochTo, epsgSubstitute)) + { + _v_frame_substitutions.push_back(string_string_pair( + datumFromEpsgString(std::string(strEpsgTo)), + datumFromEpsgString(epsgSubstitute))); + datumTo_.SetDatum(epsgSubstitute); + datumTo_.SetEpoch(strEpochTo); + } + } catch (const RefTranException& e) { + std::stringstream error_msg; + error_msg << std::endl + << " - Target frame: " << datumFromEpsgString(strEpsgTo) << " doesn't exist." + << std::endl; + + switch (e.exception_type()) { + case REFTRAN_WGS84_TRANS_UNSUPPORTED: { + std::stringstream throw_msg; + throw_msg << e.what() << error_msg.str() << std::endl; + throw RefTranException(throw_msg.str(), REFTRAN_WGS84_TRANS_UNSUPPORTED); + break; + } + default: throw RefTranException(e.what()); break; + } + } + + if (_v_frame_substitutions.empty()) + return; + if (projectSettings_.g.verbose < 2) + return; + + LogFrameSubstitutions(_v_frame_substitutions, "Frame"); +} + void dna_reftran::ApplyStationFrameSubstitutions() { @@ -572,6 +621,7 @@ void dna_reftran::WriteBinaryStationFile(const std::string& bstfileName) snprintf(bst_meta_.modifiedBy, sizeof(bst_meta_.modifiedBy), "%s", __BINARY_NAME__); snprintf(bst_meta_.epsgCode, sizeof(bst_meta_.epsgCode), "%s", strEpsg.substr(0, STN_EPSG_WIDTH).c_str()); snprintf(bst_meta_.epoch, sizeof(bst_meta_.epoch), "%s", strEpoch.substr(0, STN_EPOCH_WIDTH).c_str()); + // observation_epoch is immutable across transformations - intentionally not overwritten. bst_meta_.reftran = true; try { @@ -606,6 +656,7 @@ void dna_reftran::WriteBinaryMeasurementFile(const std::string& bmsfileName) snprintf(bms_meta_.modifiedBy, sizeof(bms_meta_.modifiedBy), "%s", __BINARY_NAME__); snprintf(bms_meta_.epsgCode, sizeof(bms_meta_.epsgCode), "%s", strEpsg.substr(0, STN_EPSG_WIDTH).c_str()); snprintf(bms_meta_.epoch, sizeof(bms_meta_.epoch), "%s", strEpoch.substr(0, STN_EPOCH_WIDTH).c_str()); + // observation_epoch is immutable across transformations - intentionally not overwritten. bms_meta_.reftran = true; try { @@ -1455,8 +1506,10 @@ void dna_reftran::TransformStationRecords(const std::string& newFrame, const std #endif try { - // 1. Get the datum (and epoch) of the desired system - datumTo_.SetDatumFromName(newFrame, newEpoch); + // 1. Get the datum (and epoch) of the desired system and apply the + // appropriate substitution (required to determine the transformation parameters) + datumTo_.SetDatumFromName(newFrame, newEpoch); + ApplyToFrameSubstitution(); // 2. For every station, get the datum, then transform // TransformStation takes @@ -1481,6 +1534,7 @@ void dna_reftran::TransformStationRecords(const std::string& newFrame, const std // d. Update meta snprintf(stn_it->epsgCode, sizeof(stn_it->epsgCode), "%s", datumTo_.GetEpsgCode_s().c_str()); snprintf(stn_it->epoch, sizeof(stn_it->epoch), "%s", datumTo_.GetEpoch_s().c_str()); + // observation_epoch is immutable across transformations - intentionally not overwritten. transformationPerformed_ = true; m_stnsTransformed++; } @@ -1556,15 +1610,15 @@ void dna_reftran::TransformStation(it_vstn_t& stn_it, const CDnaDatum& datumFrom void dna_reftran::TransformMeasurementRecords(const std::string& newFrame, const std::string& newEpoch) { - it_vmsr_t msr_it; + it_vmsr_t msr_it = bmsBinaryRecords_.end(); CDnaDatum datumFrom; data_type_ = msr_data; - + // Create the transformation parameters to be used for the // entire set of measurement records. If a measurement is in a different // frame, obtain new parameters transformation_parameter_set transformationParameters; - + transformationPerformed_ = false; m_msrsTransformed = m_msrsNotTransformed = 0; @@ -1572,11 +1626,12 @@ void dna_reftran::TransformMeasurementRecords(const std::string& newFrame, const TRACE("\nTransforming measurements...\n\n"); #endif + // Setup before the measurement loop - errors here cannot reference a specific measurement + datumTo_.SetDatumFromName(newFrame, newEpoch); + ApplyToFrameSubstitution(); + try { - // 1. Get the datum (and epoch) of the desired system - datumTo_.SetDatumFromName(newFrame, newEpoch); - - // 2. For every measurement, get the datum, determine parameters, then transform + // For every measurement, get the datum, determine parameters, then transform for (msr_it=bmsBinaryRecords_.begin(); msr_it!=bmsBinaryRecords_.end(); ++msr_it) { // a. ignore measurements not subject to geodetic datum @@ -1741,6 +1796,8 @@ void dna_reftran::TransformMeasurement_GX(it_vmsr_t& msr_it, const CDnaDatum& da msr_it->term1 = coordinates2_mod.get(0, 0) - coordinates1_mod.get(0, 0); //TRACE("\nTransformed baseline\n"); //TRACE("%.4f\n", msr_it->term1); + // observation_epoch is immutable across transformations - the three X/Y/Z + // component writes below intentionally overwrite only epsgCode and epoch. snprintf(msr_it->epsgCode, sizeof(msr_it->epsgCode), "%s", datumTo_.GetEpsgCode_s().c_str()); snprintf(msr_it->epoch, sizeof(msr_it->epoch), "%s", datumTo_.GetEpoch_s().c_str()); msr_it++; @@ -1831,6 +1888,8 @@ void dna_reftran::TransformMeasurement_Y(it_vmsr_t& msr_it, const CDnaDatum& dat } // Assign 'transformed' elements + // observation_epoch is immutable across transformations - the three X/Y/Z + // component writes below intentionally overwrite only epsgCode and epoch. msr_it->term1 = coordinates_mod.get(0, 0); snprintf(msr_it->epsgCode, sizeof(msr_it->epsgCode), "%s", datumTo_.GetEpsgCode_s().c_str()); snprintf(msr_it->epoch, sizeof(msr_it->epoch), "%s", datumTo_.GetEpoch_s().c_str()); diff --git a/dynadjust/dynadjust/dnareftran/dnareftran.hpp b/dynadjust/dynadjust/dnareftran/dnareftran.hpp index 1f629e0ba..9cc9d224f 100644 --- a/dynadjust/dynadjust/dnareftran/dnareftran.hpp +++ b/dynadjust/dynadjust/dnareftran/dnareftran.hpp @@ -197,6 +197,7 @@ class dna_reftran { void LogFrameSubstitutions(std::vector& substitutions, const std::string& type); void ApplyStationFrameSubstitutions(); void ApplyMeasurementFrameSubstitutions(); + void ApplyToFrameSubstitution(); bool IsolateandApplySubstitute(const std::string& epsgCode, const std::string& epoch, std::string& epsgSubstitute); @@ -224,8 +225,9 @@ class dna_reftran { v_plate_motion_cartesians plate_motion_cartesians_; // Helmert parameters computed from Euler parameters vframeSubsPtr _frameSubstitutions; // Reference frame substitutions - std::vector _v_stn_substitutions; // station substitutions made - std::vector _v_msr_substitutions; // station substitutions made + std::vector _v_stn_substitutions; // station substitutions made + std::vector _v_msr_substitutions; // station substitutions made + std::vector _v_frame_substitutions; // frameTo substitution made v_string_uint32_pair vplateMap_; // Plate Map index sorted on plate ID diff --git a/dynadjust/dynadjust/dnareftranwrapper/CMakeLists.txt b/dynadjust/dynadjust/dnareftranwrapper/CMakeLists.txt index 97b495d0e..e16d5d20a 100644 --- a/dynadjust/dynadjust/dnareftranwrapper/CMakeLists.txt +++ b/dynadjust/dynadjust/dnareftranwrapper/CMakeLists.txt @@ -85,8 +85,8 @@ if(BUILD_STATIC) "-framework CoreFoundation") else() target_link_options(${STATIC_TARGET_NAME} PRIVATE - -static) - + -static-libgcc + -static-libstdc++) endif() elseif(WIN32) # On Windows with MSVC diff --git a/dynadjust/dynadjust/dnasegmentwrapper/CMakeLists.txt b/dynadjust/dynadjust/dnasegmentwrapper/CMakeLists.txt index 77d527bb1..c7cfe7657 100644 --- a/dynadjust/dynadjust/dnasegmentwrapper/CMakeLists.txt +++ b/dynadjust/dynadjust/dnasegmentwrapper/CMakeLists.txt @@ -90,8 +90,8 @@ if(BUILD_STATIC) "-framework CoreFoundation") else() target_link_options(${STATIC_TARGET_NAME} PRIVATE - -static) - + -static-libgcc + -static-libstdc++) endif() elseif(WIN32) # On Windows with MSVC diff --git a/dynadjust/dynadjust/dynadjust/CMakeLists.txt b/dynadjust/dynadjust/dynadjust/CMakeLists.txt index 569b3c903..7ee9a8ad7 100644 --- a/dynadjust/dynadjust/dynadjust/CMakeLists.txt +++ b/dynadjust/dynadjust/dynadjust/CMakeLists.txt @@ -70,14 +70,9 @@ if(BUILD_STATIC) #CURL::libcurl "-framework CoreFoundation") else() - # On Linux/Unix, we can use full static linking target_link_options(${STATIC_TARGET_NAME} PRIVATE - -Wl,-Bstatic - -fno-builtin -static-libgcc - -static-libstdc++ - -static) - + -static-libstdc++) endif() elseif(WIN32) # On Windows with MSVC diff --git a/dynadjust/include/config/dnaoptions-interface.hpp b/dynadjust/include/config/dnaoptions-interface.hpp index d80b81615..8fd7c843f 100644 --- a/dynadjust/include/config/dnaoptions-interface.hpp +++ b/dynadjust/include/config/dnaoptions-interface.hpp @@ -151,8 +151,12 @@ const char* const LSQ_INVERSE_METHOD = "inversion-method"; const char* const SCALE_NORMAL_UNITY = "scale-normals-to-unity"; const char* const PURGE_STAGE_FILES = "purge-stage-files"; const char* const RECREATE_STAGE_FILES = "create-stage-files"; +const char* const STAGE_PATH = "stage-path"; const char* const UPDATE_ORIGINAL_STN_FILE = "update-orig-stn-file"; +// Threading options +const char* const MAX_THREADS = "max-threads"; + const char* const SEG_MIN_INNER_STNS = "min-inner-stns"; const char* const SEG_THRESHOLD_STNS = "max-block-stns"; const char* const SEG_STARTING_STN = "starting-stns"; @@ -171,6 +175,7 @@ const char* const REFERENCE_FRAME = "reference-frame"; const char* const REFERENCE_FRAME_R = "reference-frame,r"; const char* const EPOCH = "epoch"; const char* const EPOCH_E = "epoch,e"; +const char* const OBSERVATION_EPOCH = "observation-epoch"; const char* const OVERRIDE_INPUT_FRAME = "override-input-ref-frame"; const char* const TECTONIC_PLATE_BDY_FILE = "plate-boundary-file"; const char* const TECTONIC_PLATE_BDY_FILE_B = "plate-boundary-file,b"; @@ -211,6 +216,7 @@ const char* const EXPORT_GEO_FILE = "export-dna-geo-file"; const char* const EXPORT_NTV2_ASCII_FILE = "export-ntv2-asc-file"; const char* const EXPORT_NTV2_BINARY_FILE = "export-ntv2-gsb-file"; const char* const EXPORT_SNX_FILE = "export-sinex-file"; +const char* const OUTPUT_JSON = "output-json"; const char* const IMPORT_GEO_FILE = "geo-file"; const char* const IMPORT_GEO_FILE_G = "geo-file,g"; const char* const EXPORT_ASL_FILE = "export-asl-file"; diff --git a/dynadjust/include/config/dnaoptions.hpp b/dynadjust/include/config/dnaoptions.hpp index 77e21eeae..60d854ec9 100644 --- a/dynadjust/include/config/dnaoptions.hpp +++ b/dynadjust/include/config/dnaoptions.hpp @@ -185,7 +185,7 @@ struct general_settings : private boost::equality_comparable { struct import_settings : private boost::equality_comparable { public: import_settings() - : reference_frame(DEFAULT_DATUM), epoch(DEFAULT_EPOCH), user_supplied_frame(0), user_supplied_epoch(0), override_input_rfame(0) + : reference_frame(DEFAULT_DATUM), epoch(DEFAULT_EPOCH), observation_epoch(""), user_supplied_frame(0), user_supplied_epoch(0), user_supplied_observation_epoch(0), override_input_rfame(0) , test_integrity(0), verify_coordinates(0), export_dynaml(0), export_from_bfiles(0) , export_single_xml_file(0), prefer_single_x_as_g(0), export_asl_file(0), export_aml_file(0), export_map_file(0) , export_dna_files(0), export_discont_file(0), import_geo_file(0), simulate_measurements(0), split_clusters(0), include_transcending_msrs(0) @@ -232,9 +232,11 @@ struct import_settings : private boost::equality_comparable { public: std::string reference_frame; // Project reference frame - used primarily for reductions on the ellipsoid. - std::string epoch; // Project epoch + std::string epoch; // Project epoch (of reference frame) + std::string observation_epoch; // Default observation epoch applied to measurements with no file-level value UINT16 user_supplied_frame; // User has supplied a frame - use this to change the default frame UINT16 user_supplied_epoch; // User has supplied a epoch - use this to change the default epoch + UINT16 user_supplied_observation_epoch; // User has supplied an observation epoch via --observation-epoch UINT16 override_input_rfame; // Override reference frame specified in input files using the default or user supplied frame. UINT16 test_integrity; // Test integrity of network UINT16 verify_coordinates; // Test integrity of coordinates @@ -430,7 +432,8 @@ struct adjust_settings : private boost::equality_comparable { , iteration_threshold((float)0.0005), free_std_dev(10.0), fixed_std_dev(PRECISION_1E6), station_constraints("") , map_file(""), bst_file(""), bms_file(""), seg_file(""), comments("") , command_line_arguments("") - , type_b_global (""), type_b_file ("") {} + , type_b_global (""), type_b_file ("") + , max_threads(0) {} private: // Disallow use of compiler generated equality operator. @@ -471,6 +474,7 @@ struct adjust_settings : private boost::equality_comparable { UINT16 scale_normals_to_unity; // Scale normals to unity prior to inversion bool purge_stage_files; // Purge memory mapped files from disk upon adjustment completion. UINT16 recreate_stage_files; // Recreate memory mapped files. + std::string stage_path; // Directory for memory mapped stage files (default: output_folder). float iteration_threshold; // Convergence limit double free_std_dev; // SD for free stations double fixed_std_dev; // SD for fixed stations @@ -483,6 +487,9 @@ struct adjust_settings : private boost::equality_comparable { std::string command_line_arguments; std::string type_b_global; // Comma delimited string containing Type b uncertainties to be applied to all uncertainties computed from an adjustment std::string type_b_file; // File path to Type b uncertainties to be applied to specific site uncertainties computed from an adjustment + + // Threading + int max_threads; // Maximum BLAS threads (0 = auto) }; // datum and geoid settings @@ -491,13 +498,15 @@ struct output_settings : private boost::equality_comparable { output_settings() : _m2s_file(""), _adj_file(""), _xyz_file("") , _snx_file(""), _xml_file(""), _cor_file(""), _apu_file("") + , _adj_json_file(""), _xyz_json_file(""), _apu_json_file("") + , _cor_json_file(""), _m2s_json_file("") , _adj_stn_iteration(0), _adj_msr_iteration(0), _cmp_msr_iteration(0), _adj_stat_iteration(0) , _adj_msr_final(0), _adj_msr_tstat(0), _database_ids(0), _print_ignored_msrs(0), _adj_gnss_units(0) , _output_stn_blocks(0), _output_msr_blocks(0), _sort_stn_file_order(0), _sort_adj_msr(0), _sort_msr_to_stn(0) , _init_stn_corrections(0), _msr_to_stn(0), _stn_corr(0), _positional_uncertainty(0) , _apu_vcv_units(0), _output_pu_covariances(0) , _export_xml_stn_file(0), _export_xml_msr_file(0), _export_dna_stn_file(0), _export_dna_msr_file(0) - , _export_snx_file(0) + , _export_snx_file(0), _output_json(0) , _hz_corr_threshold(0.0), _vt_corr_threshold(0.0) , _stn_coord_types("PLHhXYZ"), _angular_type_stn(DMS) , _precision_seconds_stn(5), _precision_metres_stn(4), _precision_seconds_msr(4), _precision_metres_msr(4) @@ -547,6 +556,11 @@ struct output_settings : private boost::equality_comparable { std::string _xml_file; // Estimated station coordinates and full variance matrix in DynaML (DynAdjust XML) format. Uses Y cluster. std::string _cor_file; // Corrections to intial stations output std::string _apu_file; // Adjusted positional uncertainty output + std::string _adj_json_file; // Optional JSONL sibling of _adj_file + std::string _xyz_json_file; // Optional JSONL sibling of _xyz_file + std::string _apu_json_file; // Optional JSONL sibling of _apu_file + std::string _cor_json_file; // Optional JSONL sibling of _cor_file + std::string _m2s_json_file; // Optional JSONL sibling of _m2s_file UINT16 _adj_stn_iteration; // Outputs adjusted stations for each block within each iteration UINT16 _adj_msr_iteration; // Outputs adjusted measurements for each block within each iteration UINT16 _cmp_msr_iteration; // Outputs computed measurements for each block within each iteration @@ -572,6 +586,7 @@ struct output_settings : private boost::equality_comparable { UINT16 _export_dna_stn_file; // Create a DNA stn file UINT16 _export_dna_msr_file; // Create a DNA msr file UINT16 _export_snx_file; // Create a sinex file from the adjustment + UINT16 _output_json; // Also emit adjustment reports as JSONL alongside text reports double _hz_corr_threshold; // Minimum horizontal threshold for station corrections double _vt_corr_threshold; // Minimum vertical threshold for station corrections std::string _stn_coord_types; // String defining the cooridnate types to be printed for each station diff --git a/dynadjust/include/config/dnatypes-basic.hpp b/dynadjust/include/config/dnatypes-basic.hpp index 8ff9c9551..18336ae6b 100644 --- a/dynadjust/include/config/dnatypes-basic.hpp +++ b/dynadjust/include/config/dnatypes-basic.hpp @@ -89,7 +89,8 @@ typedef enum _INPUT_FILE_TYPE_ dynaml = 1, dna = 2, csv = 3, - sinex = 4 + sinex = 4, + jsonl = 5 } INPUT_FILE_TYPE; typedef enum _INPUT_DATA_TYPE_ diff --git a/dynadjust/include/config/dnatypes-structs.hpp b/dynadjust/include/config/dnatypes-structs.hpp index 1e91dfe1c..4ddc68ee2 100644 --- a/dynadjust/include/config/dnatypes-structs.hpp +++ b/dynadjust/include/config/dnatypes-structs.hpp @@ -283,6 +283,7 @@ typedef struct stn_t { memset(epsgCode, '\0', sizeof(epsgCode)); snprintf(epsgCode, sizeof(epsgCode), "7843"); memset(epoch, '\0', sizeof(epoch)); + memset(observation_epoch, '\0', sizeof(observation_epoch)); memset(plate, '\0', sizeof(plate)); } @@ -311,9 +312,13 @@ typedef struct stn_t { UINT32 clusterID; // cluster ID (which cluster this station belongs to) UINT16 unusedStation; // is this station unused? char epsgCode[STN_EPSG_WIDTH]; // epsg ID, i.e. NNNNN (where NNNNN is in the range 0-32767) - char epoch[STN_EPOCH_WIDTH]; // date, i.e. "DD.MM.YYYY" (10 chars) - // if datum is dynamic, Epoch is YYYY MM DD - // if datum is static, Epoch is ignored + char epoch[STN_EPOCH_WIDTH]; // Epoch of Reference Frame, i.e. "DD.MM.YYYY" (10 chars) + // Mutable via dnareftran; reflects the reference frame's epoch. + // If datum is static, Epoch is ignored. + char observation_epoch[STN_EPOCH_WIDTH]; // Epoch of Observation, i.e. "DD.MM.YYYY" (10 chars) + // Immutable under reftran. Timestamp at which the station + // coordinates were observed/measured. Used for discontinuity + // station-instance allocation. Empty if not supplied. char plate[STN_PLATE_WIDTH]; // Tectonic plate identifier. Typically two characters. } station_t; @@ -330,7 +335,8 @@ typedef v_stn_string::iterator it_stn_string; typedef struct input_file_meta { char filename[FILE_NAME_WIDTH+1]; // Input file path char epsgCode[STN_EPSG_WIDTH+1]; // Input file epsg ID, i.e. NNNNN (where NNNNN is in the range 0-32767). "Mixed" if stations are on different reference frames - char epoch[STN_EPOCH_WIDTH+1]; // Input file epoch + char epoch[STN_EPOCH_WIDTH+1]; // Input file reference frame epoch (mutable) + char observation_epoch[STN_EPOCH_WIDTH+1]; // Input file observation epoch (immutable) UINT16 filetype; // Input file type (geodesyml, dynaml, dna, csv, sinex) UINT16 datatype; // Input data type (station, measurement, both) } input_file_meta_t; @@ -371,6 +377,7 @@ typedef struct binary_file_meta { memcpy(modifiedBy, rhs.modifiedBy, sizeof(modifiedBy)); memcpy(epsgCode, rhs.epsgCode, sizeof(epsgCode)); memcpy(epoch, rhs.epoch, sizeof(epoch)); + memcpy(observation_epoch, rhs.observation_epoch, sizeof(observation_epoch)); rhs.inputFileMeta = nullptr; rhs.sourceFileMeta = nullptr; } @@ -388,6 +395,7 @@ typedef struct binary_file_meta { memcpy(modifiedBy, rhs.modifiedBy, sizeof(modifiedBy)); memcpy(epsgCode, rhs.epsgCode, sizeof(epsgCode)); memcpy(epoch, rhs.epoch, sizeof(epoch)); + memcpy(observation_epoch, rhs.observation_epoch, sizeof(observation_epoch)); inputFileCount = rhs.inputFileCount; inputFileMeta = rhs.inputFileMeta; sourceFileCount = rhs.sourceFileCount; @@ -401,7 +409,8 @@ typedef struct binary_file_meta { bool reduced; // indicates whether the data is reduced(true) or raw(false) char modifiedBy[MOD_NAME_WIDTH+1]; // the program that modified this file char epsgCode[STN_EPSG_WIDTH+1]; // epsg ID, i.e. NNNNN (where NNNNN is in the range 0-32767). "Mixed" if stations are on different reference frames - char epoch[STN_EPOCH_WIDTH+1]; // date, i.e. "DD.MM.YYYY" (10 chars) + char epoch[STN_EPOCH_WIDTH+1]; // Epoch of Reference Frame (mutable) + char observation_epoch[STN_EPOCH_WIDTH+1]; // Epoch of Observation (immutable; preserved across reftran) bool reftran; // the data has been transformed to another frame and/or epoch bool geoid; // geoid separation values have been obtained std::uint64_t inputFileCount; // Number of source file metadata elements diff --git a/dynadjust/include/config/dnaversion.hpp b/dynadjust/include/config/dnaversion.hpp index 15d54874b..2216669e4 100644 --- a/dynadjust/include/config/dnaversion.hpp +++ b/dynadjust/include/config/dnaversion.hpp @@ -204,8 +204,8 @@ const char* const __plot_dll_name__ = "libdnaplot.so"; // 2.2.0.a1 02020001 Alpha release // 2.2.0.b2 02020002 Beta release // 2.2.0.rc2 02020003 Release candidate 1 (unless bug notification is received, this is taken to be the final release) -#define __BINARY_VERSION__ "1.3.0" -#define __SHORT_VERSION__ "10300" // used to record DynAdjust version in binary file header +#define __BINARY_VERSION__ "1.4.0" +#define __SHORT_VERSION__ "10400" // used to record DynAdjust version in binary file header // define executable name #define __GLOBAL_BINARY_NAME__ __dynadjust_app_name__ @@ -310,7 +310,7 @@ const char* const __plot_dll_name__ = "libdnaplot.so"; #if defined(__clang__) // Clang compiler #define __COMPILER__ "Clang" - #define __COMPILER_VERSION__ __VERSION__ + #define __COMPILER_VERSION__ __clang_version__ #elif defined(__GNUC__) || defined(__GNUG__) // GNU GCC #define __COMPILER__ "GNU GCC" #define __COMPILER_VERSION__ __VERSION__ diff --git a/dynadjust/include/functions/dnaiostreamfuncs.hpp b/dynadjust/include/functions/dnaiostreamfuncs.hpp index 75a011ed4..71d0b38ea 100644 --- a/dynadjust/include/functions/dnaiostreamfuncs.hpp +++ b/dynadjust/include/functions/dnaiostreamfuncs.hpp @@ -132,7 +132,7 @@ void print_file_header( stream << std::setw(PRINT_VAR_PAD) << std::left << "File created:"; std::ostringstream datetime_ss; boost::posix_time::time_facet* p_time_output = new boost::posix_time::time_facet; - std::locale special_locale (std::locale(""), p_time_output); + std::locale special_locale (std::locale::classic(), p_time_output); // special_locale takes ownership of the p_time_output facet datetime_ss.imbue (special_locale); (*p_time_output).format("%A, %d %B %Y, %X"); diff --git a/dynadjust/include/functions/dnatemplatedatetimefuncs.hpp b/dynadjust/include/functions/dnatemplatedatetimefuncs.hpp index 789507910..10c9a97e6 100644 --- a/dynadjust/include/functions/dnatemplatedatetimefuncs.hpp +++ b/dynadjust/include/functions/dnatemplatedatetimefuncs.hpp @@ -37,6 +37,7 @@ #include #include +#include const UINT32 TIME_IMMEMORIAL = 1900; @@ -548,7 +549,7 @@ T formattedDateTimeString() { std::stringstream datetime_ss, stream; boost::posix_time::time_facet* p_time_output = new boost::posix_time::time_facet; - std::locale special_locale (std::locale(""), p_time_output); + std::locale special_locale (std::locale::classic(), p_time_output); // special_locale takes ownership of the p_time_output facet datetime_ss.imbue (special_locale); @@ -562,39 +563,8 @@ T formattedDateTimeString() template S formatedElapsedTime(boost::posix_time::milliseconds* elapsed_time, S app_message) { - std::ostringstream ss_time; - boost::posix_time::ptime pt(boost::posix_time::ptime(boost::gregorian::day_clock::local_day(), *elapsed_time)); - - if (*elapsed_time < boost::posix_time::seconds(3)) - { - boost::posix_time::time_facet* facet(new boost::posix_time::time_facet("%s")); - ss_time.imbue(std::locale(ss_time.getloc(), facet)); - ss_time.str(""); - ss_time << pt << "s"; - } - else if (*elapsed_time < boost::posix_time::seconds(61)) - { - boost::posix_time::time_facet* facet(new boost::posix_time::time_facet("%S")); - ss_time.imbue(std::locale(ss_time.getloc(), facet)); - ss_time.str(""); - ss_time << pt << "s"; - } - else - ss_time << boost::posix_time::seconds(static_cast(elapsed_time->total_seconds())); - - size_t pos = std::string::npos; - std::string time_message = ss_time.str(); - while ((pos = time_message.find("0s")) != std::string::npos) - time_message = time_message.substr(0, pos) + "s"; - - time_message = app_message + time_message + "."; - - if ((pos = time_message.find(" 00.")) != std::string::npos) - time_message = time_message.replace(pos, 4, " 0."); - if ((pos = time_message.find(" 0.s")) != std::string::npos) - time_message = time_message.replace(pos, 4, " 0s"); - - return time_message; + double seconds = static_cast(elapsed_time->total_milliseconds()) / 1000.0; + return app_message + dynadjust::FormatElapsedTime(seconds) + "."; } diff --git a/dynadjust/include/functions/dnatemplatematrixfuncs.hpp b/dynadjust/include/functions/dnatemplatematrixfuncs.hpp index 05e0df853..092c7d896 100644 --- a/dynadjust/include/functions/dnatemplatematrixfuncs.hpp +++ b/dynadjust/include/functions/dnatemplatematrixfuncs.hpp @@ -56,7 +56,6 @@ void GetDirectionsVarianceMatrix(msr_t_Iterator begin, matrix_2d* vmat) UINT32 a, angle_count(bmsRecord->vectorCount2 - 1); // number of directions excluding the RO UINT32 skip(0), ignored(bmsRecord->vectorCount1 - bmsRecord->vectorCount2); - vmat->zero(); vmat->redim(angle_count, angle_count); bmsRecord++; @@ -90,8 +89,7 @@ void GetGPSVarianceMatrix(const msr_t_Iterator begin, matrix_2d* vmat) { msr_t_Iterator bmsRecord(begin); UINT32 variance_dim(bmsRecord->vectorCount1 * 3), covariance_dim, cov; - vmat->zero(); - vmat->redim(variance_dim, variance_dim); + vmat->redim(variance_dim, variance_dim); for (UINT32 var(0), cov_elem; var -#include -#include #include +#include +#include namespace dynadjust { // High-precision timer class to replace boost::timer::cpu_timer class cpu_timer { -public: + public: struct cpu_times { std::chrono::nanoseconds wall; std::chrono::nanoseconds user; @@ -39,39 +39,56 @@ class cpu_timer { }; cpu_timer() { start(); } - - void start() { - start_time_ = std::chrono::high_resolution_clock::now(); - } - - void resume() { - start(); - } - + + void start() { start_time_ = std::chrono::high_resolution_clock::now(); } + + void resume() { start(); } + void stop() { // For compatibility, but no-op since we calculate elapsed on demand } - + cpu_times elapsed() const { auto end_time = std::chrono::high_resolution_clock::now(); auto wall_duration = std::chrono::duration_cast(end_time - start_time_); - return {wall_duration, wall_duration, wall_duration}; // For simplicity, user and system = wall + return {wall_duration, wall_duration, wall_duration}; // For simplicity, user and system = wall } - + std::string format(int places = 6) const { auto times = elapsed(); double wall_seconds = times.wall.count() / 1e9; - + std::ostringstream oss; oss << std::fixed << std::setprecision(places); oss << wall_seconds << "s wall"; return oss.str(); } -private: + private: std::chrono::high_resolution_clock::time_point start_time_; }; +// Format an elapsed duration (in seconds) for display. +// < 1 second -> "X.XXXms" +// 1..60 second -> "X.XXXs" +// >= 60 second -> "hh:mm:ss" +inline std::string FormatElapsedTime(double seconds) { + std::ostringstream oss; + if (seconds >= 60.0) { + long total_seconds = static_cast(seconds); + long hours = total_seconds / 3600; + long minutes = (total_seconds % 3600) / 60; + long secs = total_seconds % 60; + oss << std::setfill('0') << std::setw(2) << hours << ':' << std::setw(2) << minutes << ':' << std::setw(2) + << secs; + } else if (seconds >= 1.0) { + oss << std::fixed << std::setprecision(3) << seconds << 's'; + } else { + oss << std::fixed << std::setprecision(3) << (seconds * 1000.0) << "ms"; + } + return oss.str(); +} + } // namespace dynadjust -#endif // DNATIMER_H_ \ No newline at end of file +#endif // DNATIMER_H_ diff --git a/dynadjust/include/io/DynaML-schema.cxx b/dynadjust/include/io/DynaML-schema.cxx deleted file mode 100644 index 8f1fa74c7..000000000 --- a/dynadjust/include/io/DynaML-schema.cxx +++ /dev/null @@ -1,969 +0,0 @@ -// Automatically generated. Do not edit. -// - -/// \cond -#include -#include -/// \endcond - -#if XERCES_GRAMMAR_SERIALIZATION_LEVEL != 7 -# error incompatible Xerces-C++ version detected -#endif - -extern const XMLByte DynaML_schema[11430UL] = -{ - 0x07, 0x00, 0x06, 0x86, 0x00, 0x06, 0x1e, 0x00, 0x02, 0x68, 0x00, 0xff, - 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x61, 0x70, 0x61, 0x63, 0x68, 0x65, - 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x78, 0x6d, 0x6c, 0x2f, 0x55, 0x6b, 0x6e, - 0x6f, 0x77, 0x6e, 0x4e, 0x53, 0x24, 0x00, 0x68, 0x74, 0x74, 0x70, 0x3a, - 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, - 0x2f, 0x58, 0x4d, 0x4c, 0x2f, 0x31, 0x39, 0x39, 0x38, 0x2f, 0x6e, 0x61, - 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x1d, 0x00, 0x68, 0x74, 0x74, - 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, - 0x72, 0x67, 0x2f, 0x32, 0x30, 0x30, 0x30, 0x2f, 0x78, 0x6d, 0x6c, 0x6e, - 0x73, 0x2f, 0x00, 0x20, 0x00, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, - 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x32, - 0x30, 0x30, 0x31, 0x2f, 0x58, 0x4d, 0x4c, 0x53, 0x00, 0xff, 0x63, 0x68, - 0x65, 0x6d, 0x61, 0x0d, 0x00, 0x2c, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, - 0x72, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x00, 0x08, 0x00, 0x5f, 0x5f, 0x41, - 0x6e, 0x6f, 0x6e, 0x43, 0x30, 0x09, 0x00, 0x2c, 0x5f, 0x5f, 0x41, 0x6e, - 0x6f, 0x6e, 0x43, 0x30, 0x00, 0x30, 0x00, 0x43, 0x3a, 0x5c, 0x44, 0x61, - 0x74, 0x61, 0x5c, 0x76, 0x73, 0x31, 0x30, 0x5c, 0x70, 0x72, 0x6f, 0x6a, - 0x65, 0x63, 0x74, 0x73, 0x5c, 0x64, 0x79, 0x6e, 0x61, 0x6e, 0x65, 0x74, - 0x5f, 0x33, 0x5f, 0x30, 0x33, 0x5f, 0x30, 0x30, 0x2f, 0x44, 0x79, 0x6e, - 0x61, 0x4d, 0x4c, 0x2e, 0x78, 0x73, 0x64, 0x01, 0x00, 0x58, 0x00, 0x06, - 0x00, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x02, 0x00, 0x78, 0x73, 0x01, - 0x00, 0x59, 0x00, 0x01, 0x00, 0x5a, 0x00, 0x07, 0x00, 0x53, 0x69, 0x67, - 0x6d, 0x61, 0x58, 0x58, 0x00, 0x07, 0x00, 0x02, 0x53, 0x00, 0xff, 0x69, - 0x67, 0x6d, 0x61, 0x58, 0x59, 0x00, 0x07, 0x00, 0x53, 0x69, 0x67, 0x6d, - 0x61, 0x58, 0x5a, 0x00, 0x07, 0x00, 0x53, 0x69, 0x67, 0x6d, 0x61, 0x59, - 0x59, 0x00, 0x07, 0x00, 0x53, 0x69, 0x67, 0x6d, 0x61, 0x59, 0x5a, 0x00, - 0x07, 0x00, 0x53, 0x69, 0x67, 0x6d, 0x61, 0x5a, 0x5a, 0x00, 0x0f, 0x00, - 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x43, 0x6f, 0x76, 0x61, 0x72, 0x69, 0x61, - 0x6e, 0x63, 0x65, 0x00, 0x08, 0x00, 0x5f, 0x5f, 0x41, 0x6e, 0x6f, 0x6e, - 0x43, 0x31, 0x09, 0x00, 0x2c, 0x5f, 0x5f, 0x41, 0x6e, 0x6f, 0x6e, 0x43, - 0x31, 0x00, 0x03, 0x00, 0x6d, 0x31, 0x31, 0x00, 0x03, 0x00, 0x6d, 0x31, - 0x32, 0x00, 0x03, 0x00, 0x6d, 0x31, 0x33, 0x00, 0x03, 0x00, 0x6d, 0x32, - 0x31, 0x00, 0x03, 0x00, 0x6d, 0x32, 0x32, 0x00, 0x03, 0x00, 0x6d, 0x32, - 0x33, 0x00, 0x03, 0x00, 0x6d, 0x33, 0x31, 0x00, 0x02, 0x03, 0x00, 0x02, - 0x6d, 0x00, 0xff, 0x33, 0x32, 0x00, 0x03, 0x00, 0x6d, 0x33, 0x33, 0x00, - 0x0c, 0x00, 0x2c, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, - 0x74, 0x73, 0x07, 0x00, 0x2c, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x00, - 0x0c, 0x00, 0x2c, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x0b, 0x00, 0x2c, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x00, 0x08, 0x00, 0x5f, 0x5f, 0x41, 0x6e, 0x6f, 0x6e, - 0x43, 0x32, 0x09, 0x00, 0x2c, 0x5f, 0x5f, 0x41, 0x6e, 0x6f, 0x6e, 0x43, - 0x32, 0x00, 0x06, 0x00, 0x49, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x06, 0x00, - 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x05, 0x00, 0x56, 0x61, 0x6c, 0x75, - 0x65, 0x00, 0x06, 0x00, 0x53, 0x74, 0x64, 0x44, 0x65, 0x76, 0x0f, 0x00, - 0x2c, 0x44, 0x6e, 0x61, 0x4d, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x00, - 0xff, 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x08, 0x00, 0x5f, 0x5f, 0x41, 0x6e, - 0x6f, 0x6e, 0x43, 0x33, 0x09, 0x00, 0x2c, 0x5f, 0x5f, 0x41, 0x6e, 0x6f, - 0x6e, 0x43, 0x33, 0x00, 0x04, 0x00, 0x54, 0x79, 0x70, 0x65, 0x0e, 0x00, - 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x46, 0x72, 0x61, - 0x6d, 0x65, 0x05, 0x00, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x00, 0x05, 0x00, - 0x46, 0x69, 0x72, 0x73, 0x74, 0x00, 0x06, 0x00, 0x53, 0x65, 0x63, 0x6f, - 0x6e, 0x64, 0x05, 0x00, 0x54, 0x68, 0x69, 0x72, 0x64, 0x00, 0x0a, 0x00, - 0x49, 0x6e, 0x73, 0x74, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x0a, 0x00, - 0x54, 0x61, 0x72, 0x67, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x05, 0x00, - 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x00, 0x0a, 0x00, 0x44, 0x69, 0x72, 0x65, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x06, 0x00, 0x56, 0x00, 0xff, 0x73, - 0x63, 0x61, 0x6c, 0x65, 0x0b, 0x00, 0x47, 0x50, 0x53, 0x42, 0x61, 0x73, - 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x00, 0x08, 0x00, 0x5f, 0x5f, 0x41, 0x6e, - 0x6f, 0x6e, 0x43, 0x34, 0x09, 0x00, 0x2c, 0x5f, 0x5f, 0x41, 0x6e, 0x6f, - 0x6e, 0x43, 0x34, 0x00, 0x0d, 0x00, 0x47, 0x50, 0x53, 0x43, 0x6f, 0x76, - 0x61, 0x72, 0x69, 0x61, 0x6e, 0x63, 0x65, 0x00, 0x08, 0x00, 0x5f, 0x5f, - 0x41, 0x6e, 0x6f, 0x6e, 0x43, 0x35, 0x09, 0x00, 0x2c, 0x5f, 0x5f, 0x41, - 0x6e, 0x6f, 0x6e, 0x43, 0x35, 0x00, 0x06, 0x00, 0x48, 0x73, 0x63, 0x61, - 0x6c, 0x65, 0x06, 0x00, 0x4c, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x06, 0x00, - 0x50, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x0c, 0x00, 0x43, 0x6c, 0x75, 0x73, - 0x74, 0x65, 0x72, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x06, 0x00, 0x43, 0x6f, - 0x6f, 0x72, 0x64, 0x73, 0x06, 0x00, 0x53, 0x00, 0xff, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x0b, 0x00, 0x2c, 0x44, 0x6e, 0x61, 0x53, 0x74, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x00, 0x08, 0x00, 0x5f, 0x5f, 0x41, 0x6e, 0x6f, 0x6e, - 0x43, 0x36, 0x09, 0x00, 0x2c, 0x5f, 0x5f, 0x41, 0x6e, 0x6f, 0x6e, 0x43, - 0x36, 0x00, 0x04, 0x00, 0x4e, 0x61, 0x6d, 0x65, 0x0b, 0x00, 0x43, 0x6f, - 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x73, 0x00, 0x0c, 0x00, - 0x53, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6f, 0x72, 0x64, - 0x08, 0x00, 0x5f, 0x5f, 0x41, 0x6e, 0x6f, 0x6e, 0x43, 0x37, 0x09, 0x00, - 0x2c, 0x5f, 0x5f, 0x41, 0x6e, 0x6f, 0x6e, 0x43, 0x37, 0x00, 0x05, 0x00, - 0x58, 0x41, 0x78, 0x69, 0x73, 0x00, 0x05, 0x00, 0x59, 0x41, 0x78, 0x69, - 0x73, 0x00, 0x06, 0x00, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x0e, 0x00, - 0x48, 0x65, 0x6d, 0x69, 0x73, 0x00, 0xff, 0x70, 0x68, 0x65, 0x72, 0x65, - 0x5a, 0x6f, 0x6e, 0x65, 0x0b, 0x00, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x0d, 0x00, 0x2c, 0x44, 0x6e, 0x61, - 0x58, 0x6d, 0x6c, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x00, 0x08, 0x00, - 0x5f, 0x5f, 0x41, 0x6e, 0x6f, 0x6e, 0x43, 0x38, 0x09, 0x00, 0x2c, 0x5f, - 0x5f, 0x41, 0x6e, 0x6f, 0x6e, 0x43, 0x38, 0x00, 0x0a, 0x00, 0x44, 0x6e, - 0x61, 0x53, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0e, 0x00, 0x44, 0x6e, - 0x61, 0x4d, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, - 0x08, 0x00, 0x5f, 0x5f, 0x41, 0x6e, 0x6f, 0x6e, 0x53, 0x39, 0x09, 0x00, - 0x2c, 0x5f, 0x5f, 0x41, 0x6e, 0x6f, 0x6e, 0x53, 0x39, 0x00, 0x06, 0x00, - 0x2c, 0x46, 0x69, 0x72, 0x73, 0x74, 0x0c, 0x00, 0x2c, 0x47, 0x50, 0x53, - 0x42, 0x61, 0x73, 0x00, 0xff, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x0e, 0x00, - 0x2c, 0x47, 0x50, 0x53, 0x43, 0x6f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x6e, - 0x63, 0x65, 0x07, 0x00, 0x2c, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x00, - 0x0f, 0x00, 0x2c, 0x48, 0x65, 0x6d, 0x69, 0x73, 0x70, 0x68, 0x65, 0x72, - 0x65, 0x5a, 0x6f, 0x6e, 0x65, 0x00, 0x07, 0x00, 0x2c, 0x48, 0x73, 0x63, - 0x61, 0x6c, 0x65, 0x00, 0x07, 0x00, 0x2c, 0x49, 0x67, 0x6e, 0x6f, 0x72, - 0x65, 0x00, 0x0b, 0x00, 0x2c, 0x49, 0x6e, 0x73, 0x74, 0x48, 0x65, 0x69, - 0x67, 0x68, 0x74, 0x00, 0x07, 0x00, 0x2c, 0x4c, 0x73, 0x63, 0x61, 0x6c, - 0x65, 0x00, 0x05, 0x00, 0x2c, 0x4e, 0x61, 0x6d, 0x65, 0x00, 0x10, 0x00, - 0x2c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x43, 0x6f, 0x76, 0x61, 0x72, 0x69, - 0x61, 0x6e, 0x63, 0x65, 0x07, 0x00, 0x2c, 0x50, 0x73, 0x63, 0x61, 0x6c, - 0x65, 0x00, 0x02, 0x07, 0x00, 0x02, 0x2c, 0x00, 0xff, 0x53, 0x65, 0x63, - 0x6f, 0x6e, 0x64, 0x00, 0x08, 0x00, 0x2c, 0x53, 0x69, 0x67, 0x6d, 0x61, - 0x58, 0x58, 0x08, 0x00, 0x2c, 0x53, 0x69, 0x67, 0x6d, 0x61, 0x58, 0x59, - 0x08, 0x00, 0x2c, 0x53, 0x69, 0x67, 0x6d, 0x61, 0x58, 0x5a, 0x08, 0x00, - 0x2c, 0x53, 0x69, 0x67, 0x6d, 0x61, 0x59, 0x59, 0x08, 0x00, 0x2c, 0x53, - 0x69, 0x67, 0x6d, 0x61, 0x59, 0x5a, 0x08, 0x00, 0x2c, 0x53, 0x69, 0x67, - 0x6d, 0x61, 0x5a, 0x5a, 0x0d, 0x00, 0x2c, 0x53, 0x74, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x00, 0x07, 0x00, 0x2c, 0x53, - 0x74, 0x64, 0x44, 0x65, 0x76, 0x00, 0x0b, 0x00, 0x2c, 0x54, 0x61, 0x72, - 0x67, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x00, 0x07, 0x00, 0x2c, 0x54, - 0x61, 0x72, 0x67, 0x65, 0x74, 0x00, 0x06, 0x00, 0x2c, 0x54, 0x68, 0x69, - 0x72, 0x64, 0x06, 0x00, 0x2c, 0x00, 0xff, 0x54, 0x6f, 0x74, 0x61, 0x6c, - 0x05, 0x00, 0x2c, 0x54, 0x79, 0x70, 0x65, 0x00, 0x06, 0x00, 0x2c, 0x56, - 0x61, 0x6c, 0x75, 0x65, 0x07, 0x00, 0x2c, 0x56, 0x73, 0x63, 0x61, 0x6c, - 0x65, 0x00, 0x02, 0x00, 0x2c, 0x58, 0x06, 0x00, 0x2c, 0x58, 0x41, 0x78, - 0x69, 0x73, 0x02, 0x00, 0x2c, 0x59, 0x06, 0x00, 0x2c, 0x59, 0x41, 0x78, - 0x69, 0x73, 0x02, 0x00, 0x2c, 0x5a, 0x04, 0x00, 0x2c, 0x6d, 0x31, 0x31, - 0x04, 0x00, 0x2c, 0x6d, 0x31, 0x32, 0x04, 0x00, 0x2c, 0x6d, 0x31, 0x33, - 0x04, 0x00, 0x2c, 0x6d, 0x32, 0x31, 0x04, 0x00, 0x2c, 0x6d, 0x32, 0x32, - 0x04, 0x00, 0x2c, 0x6d, 0x32, 0x33, 0x04, 0x00, 0x2c, 0x6d, 0x33, 0x31, - 0x04, 0x00, 0x2c, 0x6d, 0x33, 0x32, 0x04, 0x00, 0x2c, 0x6d, 0x33, 0x33, - 0x07, 0x00, 0x2c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x00, 0x0f, 0x00, - 0x2c, 0x52, 0x65, 0x00, 0x95, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, - 0x46, 0x72, 0x61, 0x6d, 0x65, 0x00, 0x06, 0x00, 0x2c, 0x45, 0x70, 0x6f, - 0x63, 0x68, 0xfe, 0xff, 0xff, 0xff, 0x1d, 0x00, 0x06, 0x01, 0x00, 0x06, - 0x01, 0x00, 0x02, 0xff, 0xff, 0xff, 0xff, 0x0d, 0x00, 0x02, 0x53, 0x63, - 0x68, 0x65, 0x6d, 0x61, 0x47, 0x72, 0x61, 0x6d, 0x6d, 0x61, 0x72, 0x00, - 0x02, 0xfe, 0xff, 0xff, 0xff, 0x1d, 0x00, 0x06, 0x01, 0x00, 0x06, 0xfe, - 0xff, 0xff, 0xff, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0x17, 0x00, 0x02, - 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x44, 0x61, 0x74, 0x61, 0x74, 0x79, - 0x70, 0x65, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x01, - 0x00, 0x0b, 0x10, 0x00, 0x0e, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, - 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, 0xfe, 0xff, 0xff, 0xff, - 0x1d, 0x00, 0x0e, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0x08, - 0x00, 0x02, 0x5f, 0x00, 0x87, 0x5f, 0x41, 0x6e, 0x6f, 0x6e, 0x53, 0x39, - 0x00, 0x0b, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x0f, 0xfe, 0xff, 0xff, 0xff, - 0x02, 0x00, 0x06, 0x10, 0x00, 0x02, 0x4d, 0x00, 0x9d, 0x65, 0x61, 0x73, - 0x75, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x46, 0x69, 0x6c, 0x65, - 0x0c, 0x00, 0x53, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x46, 0x69, - 0x6c, 0x65, 0xfe, 0xff, 0xff, 0xff, 0x6d, 0x00, 0x06, 0x35, 0x00, 0x06, - 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x11, 0x00, 0x02, 0x53, - 0x63, 0x68, 0x65, 0x6d, 0x61, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, - 0x44, 0x65, 0x63, 0x6c, 0x00, 0x02, 0xff, 0xff, 0xff, 0xff, 0x05, 0x00, - 0x02, 0x51, 0x4e, 0x61, 0x6d, 0x65, 0x00, 0x02, 0x08, 0x00, 0x06, 0x0e, - 0x00, 0x02, 0x06, 0x00, 0x02, 0x54, 0x00, 0x8a, 0x61, 0x72, 0x67, 0x65, - 0x74, 0x01, 0x00, 0x01, 0x00, 0x1a, 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, - 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, - 0x74, 0x72, 0x69, 0x6e, 0x67, 0xfe, 0xff, 0xff, 0xff, 0x0a, 0x00, 0x01, - 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x14, 0x00, 0x02, 0x0c, - 0x00, 0x02, 0x43, 0x00, 0x90, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x70, - 0x6f, 0x69, 0x6e, 0x74, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x09, 0x04, - 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x02, 0x43, 0x6f, - 0x6d, 0x70, 0x6c, 0x65, 0x78, 0x54, 0x79, 0x70, 0x65, 0x49, 0x6e, 0x66, - 0x6f, 0x01, 0x00, 0x81, 0x01, 0x00, 0x10, 0x04, 0x00, 0x02, 0xfe, 0xff, - 0xff, 0xff, 0x09, 0x00, 0x02, 0x2c, 0x00, 0x95, 0x5f, 0x5f, 0x41, 0x6e, - 0x6f, 0x6e, 0x43, 0x30, 0x00, 0x08, 0x00, 0x5f, 0x5f, 0x41, 0x6e, 0x6f, - 0x6e, 0x43, 0x30, 0x00, 0x00, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, - 0xff, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x02, 0x43, 0x6f, - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x53, 0x70, 0x65, 0x63, 0x4e, 0x6f, 0x64, - 0x65, 0x00, 0x04, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, - 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, - 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, - 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, - 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, - 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, - 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, - 0x01, 0x80, 0x08, 0x00, 0x06, 0x09, 0x00, 0x02, 0x01, 0x00, 0x02, 0x58, - 0x00, 0x02, 0x01, 0x00, 0x06, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, - 0x80, 0x08, 0x00, 0x06, 0x09, 0x00, 0x02, 0x01, 0x00, 0x02, 0x58, 0x00, - 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x02, 0x00, 0x0a, 0x05, 0x00, - 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, - 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, - 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x0b, 0x01, 0x01, 0x00, - 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, - 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x09, 0x00, 0x02, 0x01, 0x00, 0x02, - 0x59, 0x00, 0x02, 0x01, 0x00, 0x06, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, - 0x01, 0x80, 0x08, 0x00, 0x06, 0x09, 0x00, 0x02, 0x01, 0x00, 0x02, 0x59, - 0x00, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x03, 0x00, 0x0a, 0x05, - 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, - 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, - 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x0b, 0x01, 0x01, - 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, 0x02, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, - 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x09, 0x00, 0x02, 0x01, - 0x00, 0x02, 0x5a, 0x00, 0x02, 0x01, 0x00, 0x06, 0x0a, 0x00, 0x01, 0x80, - 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x09, 0x00, 0x02, 0x01, 0x00, - 0x02, 0x5a, 0x00, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x04, 0x00, - 0x0a, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, - 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, 0x06, - 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x0b, - 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, - 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, - 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0f, 0x00, - 0x02, 0x07, 0x00, 0x02, 0x53, 0x00, 0x88, 0x69, 0x67, 0x6d, 0x61, 0x58, - 0x58, 0x00, 0x01, 0x00, 0x05, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, - 0x80, 0x08, 0x00, 0x06, 0x0f, 0x00, 0x02, 0x07, 0x00, 0x02, 0x53, 0x00, - 0x8c, 0x69, 0x67, 0x6d, 0x61, 0x58, 0x58, 0x00, 0x01, 0x00, 0x01, 0x00, - 0x05, 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, - 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, - 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, - 0x00, 0x0b, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, - 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, - 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, - 0x0f, 0x00, 0x02, 0x07, 0x00, 0x02, 0x53, 0x00, 0x88, 0x69, 0x67, 0x6d, - 0x61, 0x58, 0x59, 0x00, 0x01, 0x00, 0x05, 0x0a, 0x00, 0x01, 0x80, 0x0c, - 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0f, 0x00, 0x02, 0x07, 0x00, 0x02, - 0x53, 0x00, 0x8c, 0x69, 0x67, 0x6d, 0x61, 0x58, 0x59, 0x00, 0x01, 0x00, - 0x01, 0x00, 0x06, 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, - 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, - 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, - 0x6e, 0x67, 0x00, 0x0b, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, - 0x00, 0x02, 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, - 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, - 0x00, 0x06, 0x0f, 0x00, 0x02, 0x07, 0x00, 0x02, 0x53, 0x00, 0x88, 0x69, - 0x67, 0x6d, 0x61, 0x58, 0x5a, 0x00, 0x01, 0x00, 0x05, 0x0a, 0x00, 0x01, - 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0f, 0x00, 0x02, 0x07, - 0x00, 0x02, 0x53, 0x00, 0x8c, 0x69, 0x67, 0x6d, 0x61, 0x58, 0x5a, 0x00, - 0x01, 0x00, 0x01, 0x00, 0x07, 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, 0x00, - 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, - 0x13, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, - 0x72, 0x69, 0x6e, 0x67, 0x00, 0x0b, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, - 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, - 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, - 0x80, 0x08, 0x00, 0x06, 0x0f, 0x00, 0x02, 0x07, 0x00, 0x02, 0x53, 0x00, - 0x88, 0x69, 0x67, 0x6d, 0x61, 0x59, 0x59, 0x00, 0x01, 0x00, 0x05, 0x0a, - 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0f, 0x00, - 0x02, 0x07, 0x00, 0x02, 0x53, 0x00, 0x8c, 0x69, 0x67, 0x6d, 0x61, 0x59, - 0x59, 0x00, 0x01, 0x00, 0x01, 0x00, 0x08, 0x00, 0x09, 0x05, 0x00, 0x02, - 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, - 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, - 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x0b, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, - 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, - 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0f, 0x00, 0x02, 0x07, 0x00, 0x02, - 0x53, 0x00, 0x88, 0x69, 0x67, 0x6d, 0x61, 0x59, 0x5a, 0x00, 0x01, 0x00, - 0x05, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, - 0x0f, 0x00, 0x02, 0x07, 0x00, 0x02, 0x53, 0x00, 0x8c, 0x69, 0x67, 0x6d, - 0x61, 0x59, 0x5a, 0x00, 0x01, 0x00, 0x01, 0x00, 0x09, 0x00, 0x09, 0x05, - 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, - 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, - 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x0b, 0x01, 0x01, - 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, 0x02, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, - 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0f, 0x00, 0x02, 0x07, - 0x00, 0x02, 0x53, 0x00, 0x88, 0x69, 0x67, 0x6d, 0x61, 0x5a, 0x5a, 0x00, - 0x01, 0x00, 0x05, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, - 0x00, 0x06, 0x0f, 0x00, 0x02, 0x07, 0x00, 0x02, 0x53, 0x00, 0x8c, 0x69, - 0x67, 0x6d, 0x61, 0x5a, 0x5a, 0x00, 0x01, 0x00, 0x01, 0x00, 0x0a, 0x00, - 0x09, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, - 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, 0x06, - 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x0b, - 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, - 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, - 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x17, 0x00, - 0x02, 0x0f, 0x00, 0x02, 0x50, 0x00, 0x90, 0x6f, 0x69, 0x6e, 0x74, 0x43, - 0x6f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x63, 0x65, 0x00, 0x01, 0x00, - 0x05, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, - 0x17, 0x00, 0x02, 0x0f, 0x00, 0x02, 0x50, 0x00, 0x94, 0x6f, 0x69, 0x6e, - 0x74, 0x43, 0x6f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x63, 0x65, 0x00, - 0x01, 0x00, 0x01, 0x00, 0x0b, 0x00, 0x09, 0x04, 0x00, 0x02, 0x01, 0x00, - 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x10, - 0x00, 0x01, 0x80, 0x01, 0x00, 0x81, 0x01, 0x00, 0x0f, 0x01, 0x00, 0x02, - 0x04, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x09, 0x00, 0x02, 0x2c, 0x00, - 0x95, 0x5f, 0x5f, 0x41, 0x6e, 0x6f, 0x6e, 0x43, 0x31, 0x00, 0x08, 0x00, - 0x5f, 0x5f, 0x41, 0x6e, 0x6f, 0x6e, 0x43, 0x31, 0x00, 0x00, 0xfd, 0xff, - 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0x00, 0x03, 0x12, 0x00, 0x01, 0x80, - 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, - 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, - 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, - 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, - 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, - 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, - 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0b, 0x00, 0x02, 0x03, 0x00, - 0x02, 0x6d, 0x00, 0x84, 0x31, 0x31, 0x00, 0x01, 0x00, 0x05, 0x0a, 0x00, - 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0b, 0x00, 0x02, - 0x03, 0x00, 0x02, 0x6d, 0x00, 0x88, 0x31, 0x31, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x0c, 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, - 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, - 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, - 0x67, 0x00, 0x0b, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, - 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, - 0x0b, 0x00, 0x02, 0x03, 0x00, 0x02, 0x6d, 0x00, 0x84, 0x31, 0x32, 0x00, - 0x01, 0x00, 0x05, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, - 0x00, 0x06, 0x0b, 0x00, 0x02, 0x03, 0x00, 0x02, 0x6d, 0x00, 0x88, 0x31, - 0x32, 0x00, 0x01, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x09, 0x05, 0x00, 0x02, - 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, - 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, - 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x0b, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, - 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, - 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0b, 0x00, 0x02, 0x03, 0x00, 0x02, - 0x6d, 0x00, 0x84, 0x31, 0x33, 0x00, 0x01, 0x00, 0x05, 0x0a, 0x00, 0x01, - 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0b, 0x00, 0x02, 0x03, - 0x00, 0x02, 0x6d, 0x00, 0x88, 0x31, 0x33, 0x00, 0x01, 0x00, 0x01, 0x00, - 0x0e, 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, - 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, - 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, - 0x00, 0x0b, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, - 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, - 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, - 0x0b, 0x00, 0x02, 0x03, 0x00, 0x02, 0x6d, 0x00, 0x84, 0x32, 0x31, 0x00, - 0x01, 0x00, 0x05, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, - 0x00, 0x06, 0x0b, 0x00, 0x02, 0x03, 0x00, 0x02, 0x6d, 0x00, 0x88, 0x32, - 0x31, 0x00, 0x01, 0x00, 0x01, 0x00, 0x0f, 0x00, 0x09, 0x05, 0x00, 0x02, - 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, - 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, - 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x0b, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, - 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, - 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0b, 0x00, 0x02, 0x03, 0x00, 0x02, - 0x6d, 0x00, 0x84, 0x32, 0x32, 0x00, 0x01, 0x00, 0x05, 0x0a, 0x00, 0x01, - 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0b, 0x00, 0x02, 0x03, - 0x00, 0x02, 0x6d, 0x00, 0x88, 0x32, 0x32, 0x00, 0x01, 0x00, 0x01, 0x00, - 0x10, 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, - 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, - 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, - 0x00, 0x0b, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, - 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, - 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, - 0x0b, 0x00, 0x02, 0x03, 0x00, 0x02, 0x6d, 0x00, 0x84, 0x32, 0x33, 0x00, - 0x01, 0x00, 0x05, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, - 0x00, 0x06, 0x0b, 0x00, 0x02, 0x03, 0x00, 0x02, 0x6d, 0x00, 0x88, 0x32, - 0x33, 0x00, 0x01, 0x00, 0x01, 0x00, 0x11, 0x00, 0x09, 0x05, 0x00, 0x02, - 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, - 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, - 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x0b, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, - 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, - 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0b, 0x00, 0x02, 0x03, 0x00, 0x02, - 0x6d, 0x00, 0x84, 0x33, 0x31, 0x00, 0x01, 0x00, 0x05, 0x0a, 0x00, 0x01, - 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0b, 0x00, 0x02, 0x03, - 0x00, 0x02, 0x6d, 0x00, 0x88, 0x33, 0x31, 0x00, 0x01, 0x00, 0x01, 0x00, - 0x12, 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, - 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, - 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, - 0x00, 0x0b, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, - 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, - 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, - 0x0b, 0x00, 0x02, 0x03, 0x00, 0x02, 0x6d, 0x00, 0x84, 0x33, 0x32, 0x00, - 0x01, 0x00, 0x05, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, - 0x00, 0x06, 0x0b, 0x00, 0x02, 0x03, 0x00, 0x02, 0x6d, 0x00, 0x88, 0x33, - 0x32, 0x00, 0x01, 0x00, 0x01, 0x00, 0x13, 0x00, 0x09, 0x05, 0x00, 0x02, - 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, - 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, - 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x0b, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, - 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, - 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0b, 0x00, 0x02, 0x03, 0x00, 0x02, - 0x6d, 0x00, 0x84, 0x33, 0x33, 0x00, 0x01, 0x00, 0x05, 0x0a, 0x00, 0x01, - 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0b, 0x00, 0x02, 0x03, - 0x00, 0x02, 0x6d, 0x00, 0x88, 0x33, 0x33, 0x00, 0x01, 0x00, 0x01, 0x00, - 0x14, 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, - 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, - 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, - 0x00, 0x0b, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, - 0x15, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, - 0x06, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x02, 0x53, 0x63, 0x68, 0x65, - 0x6d, 0x61, 0x41, 0x74, 0x74, 0x44, 0x65, 0x66, 0x4c, 0x69, 0x73, 0x74, - 0xfe, 0xff, 0xff, 0xff, 0x1d, 0x00, 0x16, 0xfe, 0xff, 0xff, 0xff, 0x09, - 0x00, 0x06, 0x4f, 0x00, 0x02, 0x53, 0x00, 0x02, 0x57, 0x00, 0x02, 0x5b, - 0x00, 0x02, 0x5f, 0x00, 0x02, 0x63, 0x00, 0x02, 0x67, 0x00, 0x02, 0x6b, - 0x00, 0x02, 0x6f, 0x00, 0x02, 0x73, 0x00, 0x12, 0xfd, 0xff, 0xff, 0xff, - 0x00, 0x0b, 0x01, 0x01, 0x00, 0x05, 0xff, 0xff, 0xff, 0xff, 0x15, 0x00, - 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x06, 0x71, - 0x00, 0x01, 0x80, 0xfe, 0xff, 0xff, 0xff, 0x1d, 0x00, 0x16, 0xfe, 0xff, - 0xff, 0xff, 0x0a, 0x00, 0x06, 0x1e, 0x00, 0x02, 0x22, 0x00, 0x02, 0x26, - 0x00, 0x02, 0x2a, 0x00, 0x02, 0x2e, 0x00, 0x02, 0x32, 0x00, 0x02, 0x36, - 0x00, 0x02, 0x3a, 0x00, 0x02, 0x3e, 0x00, 0x02, 0x42, 0x00, 0x02, 0x76, - 0x00, 0x12, 0xfd, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0x0a, 0x00, - 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x15, 0x00, 0x02, - 0x0d, 0x00, 0x02, 0x47, 0x00, 0x92, 0x50, 0x53, 0x43, 0x6f, 0x76, 0x61, - 0x72, 0x69, 0x61, 0x6e, 0x63, 0x65, 0x00, 0x01, 0x00, 0x01, 0x00, 0x29, - 0x00, 0x09, 0x04, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, - 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x01, 0x80, 0x01, 0x00, - 0x81, 0x01, 0x00, 0x0f, 0x05, 0x00, 0x02, 0x04, 0x00, 0x02, 0xfe, 0xff, - 0xff, 0xff, 0x09, 0x00, 0x02, 0x2c, 0x00, 0x95, 0x5f, 0x5f, 0x41, 0x6e, - 0x6f, 0x6e, 0x43, 0x35, 0x00, 0x08, 0x00, 0x5f, 0x5f, 0x41, 0x6e, 0x6f, - 0x6e, 0x43, 0x35, 0x00, 0x00, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, - 0xff, 0x00, 0x03, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, - 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, - 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, - 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, - 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, - 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, - 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, - 0x00, 0x06, 0x0b, 0x00, 0x02, 0x03, 0x00, 0x02, 0x6d, 0x00, 0x84, 0x31, - 0x31, 0x00, 0x01, 0x00, 0x05, 0x4f, 0x00, 0x0e, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, - 0x01, 0x80, 0x08, 0x00, 0x06, 0x0b, 0x00, 0x02, 0x03, 0x00, 0x02, 0x6d, - 0x00, 0x84, 0x31, 0x32, 0x00, 0x01, 0x00, 0x05, 0x53, 0x00, 0x0e, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, 0x02, - 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, - 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0b, 0x00, 0x02, - 0x03, 0x00, 0x02, 0x6d, 0x00, 0x84, 0x31, 0x33, 0x00, 0x01, 0x00, 0x05, - 0x57, 0x00, 0x0e, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, - 0x02, 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, - 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, - 0x06, 0x0b, 0x00, 0x02, 0x03, 0x00, 0x02, 0x6d, 0x00, 0x84, 0x32, 0x31, - 0x00, 0x01, 0x00, 0x05, 0x5b, 0x00, 0x0e, 0x01, 0x01, 0x00, 0x01, 0x01, - 0x00, 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, - 0x01, 0x80, 0x08, 0x00, 0x06, 0x0b, 0x00, 0x02, 0x03, 0x00, 0x02, 0x6d, - 0x00, 0x84, 0x32, 0x32, 0x00, 0x01, 0x00, 0x05, 0x5f, 0x00, 0x0e, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, 0x02, - 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, - 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0b, 0x00, 0x02, - 0x03, 0x00, 0x02, 0x6d, 0x00, 0x84, 0x32, 0x33, 0x00, 0x01, 0x00, 0x05, - 0x63, 0x00, 0x0e, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, - 0x02, 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, - 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, - 0x06, 0x0b, 0x00, 0x02, 0x03, 0x00, 0x02, 0x6d, 0x00, 0x84, 0x33, 0x31, - 0x00, 0x01, 0x00, 0x05, 0x67, 0x00, 0x0e, 0x01, 0x01, 0x00, 0x01, 0x01, - 0x00, 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, - 0x01, 0x80, 0x08, 0x00, 0x06, 0x0b, 0x00, 0x02, 0x03, 0x00, 0x02, 0x6d, - 0x00, 0x84, 0x33, 0x32, 0x00, 0x01, 0x00, 0x05, 0x6b, 0x00, 0x0e, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, 0x02, - 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, - 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0b, 0x00, 0x02, - 0x03, 0x00, 0x02, 0x6d, 0x00, 0x84, 0x33, 0x33, 0x00, 0x01, 0x00, 0x05, - 0x6f, 0x00, 0x0e, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, - 0x02, 0x15, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, - 0x00, 0x06, 0x71, 0x00, 0x01, 0x80, 0xfe, 0xff, 0xff, 0xff, 0x1d, 0x00, - 0x16, 0xfe, 0xff, 0xff, 0xff, 0x09, 0x00, 0x06, 0x4f, 0x00, 0x02, 0x53, - 0x00, 0x02, 0x57, 0x00, 0x02, 0x5b, 0x00, 0x02, 0x5f, 0x00, 0x02, 0x63, - 0x00, 0x02, 0x67, 0x00, 0x02, 0x6b, 0x00, 0x02, 0x6f, 0x00, 0x02, 0x96, - 0x00, 0x12, 0xfd, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0x0a, 0x00, - 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x12, 0x00, 0x02, - 0x0a, 0x00, 0x02, 0x44, 0x00, 0x8e, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x01, 0x00, 0x01, 0x00, 0x18, 0x00, 0x09, 0x04, 0x00, - 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, - 0xff, 0xff, 0x10, 0x00, 0x01, 0x80, 0x01, 0x00, 0x81, 0x01, 0x00, 0x0f, - 0x02, 0x00, 0x02, 0x04, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x09, 0x00, - 0x02, 0x2c, 0x00, 0x95, 0x5f, 0x5f, 0x41, 0x6e, 0x6f, 0x6e, 0x43, 0x32, - 0x00, 0x08, 0x00, 0x5f, 0x5f, 0x41, 0x6e, 0x6f, 0x6e, 0x43, 0x32, 0x00, - 0x00, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0x00, 0x03, 0x12, - 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, - 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, - 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, - 0x06, 0x0e, 0x00, 0x02, 0x06, 0x00, 0x02, 0x49, 0x00, 0x86, 0x67, 0x6e, - 0x6f, 0x72, 0x65, 0x01, 0x00, 0x05, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, - 0x01, 0x80, 0x08, 0x00, 0x06, 0x0e, 0x00, 0x02, 0x06, 0x00, 0x02, 0x49, - 0x00, 0x8a, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x01, 0x00, 0x01, 0x00, 0x19, - 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, - 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, - 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, - 0x0b, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, - 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0e, 0x00, - 0x02, 0x06, 0x00, 0x02, 0x54, 0x00, 0x86, 0x61, 0x72, 0x67, 0x65, 0x74, - 0x01, 0x00, 0x05, 0x0b, 0x00, 0x0e, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, - 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, - 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, - 0x80, 0x08, 0x00, 0x06, 0x0d, 0x00, 0x02, 0x05, 0x00, 0x02, 0x56, 0x00, - 0x86, 0x61, 0x6c, 0x75, 0x65, 0x00, 0x01, 0x00, 0x05, 0x0a, 0x00, 0x01, - 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0d, 0x00, 0x02, 0x05, - 0x00, 0x02, 0x56, 0x00, 0x8a, 0x61, 0x6c, 0x75, 0x65, 0x00, 0x01, 0x00, - 0x01, 0x00, 0x1b, 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, - 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, - 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, - 0x6e, 0x67, 0x00, 0x0b, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, - 0x00, 0x02, 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, - 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, - 0x00, 0x06, 0x0e, 0x00, 0x02, 0x06, 0x00, 0x02, 0x53, 0x00, 0x86, 0x74, - 0x64, 0x44, 0x65, 0x76, 0x01, 0x00, 0x05, 0x0a, 0x00, 0x01, 0x80, 0x0c, - 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0e, 0x00, 0x02, 0x06, 0x00, 0x02, - 0x53, 0x00, 0x8a, 0x74, 0x64, 0x44, 0x65, 0x76, 0x01, 0x00, 0x01, 0x00, - 0x1c, 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, - 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, - 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, - 0x00, 0x0b, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, - 0x15, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, - 0x06, 0x71, 0x00, 0x01, 0x80, 0xfe, 0xff, 0xff, 0xff, 0x1d, 0x00, 0x16, - 0xfe, 0xff, 0xff, 0xff, 0x04, 0x00, 0x06, 0xa0, 0x00, 0x02, 0x0b, 0x00, - 0x02, 0xa6, 0x00, 0x02, 0xaa, 0x00, 0x02, 0xad, 0x00, 0x12, 0xfd, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, - 0x01, 0x80, 0x08, 0x00, 0x06, 0x13, 0x00, 0x02, 0x0b, 0x00, 0x02, 0x44, - 0x00, 0x90, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x17, 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, - 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, - 0x74, 0x72, 0x69, 0x6e, 0x67, 0xfe, 0xff, 0xff, 0xff, 0x0a, 0x00, 0x01, - 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0c, 0x00, 0x02, 0x04, - 0x00, 0x02, 0x4e, 0x00, 0x88, 0x61, 0x6d, 0x65, 0x01, 0x00, 0x01, 0x00, - 0x2f, 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, - 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, - 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, - 0xfe, 0xff, 0xff, 0xff, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, - 0x08, 0x00, 0x06, 0x0d, 0x00, 0x02, 0x05, 0x00, 0x02, 0x54, 0x00, 0x8a, - 0x6f, 0x74, 0x61, 0x6c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x26, 0x00, 0x09, - 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, - 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, 0xfe, 0xff, 0xff, - 0xff, 0x67, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x6b, 0x00, 0x02, 0xfe, - 0xff, 0xff, 0xff, 0xa0, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x6f, 0x00, - 0x02, 0xfe, 0xff, 0xff, 0xff, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, - 0x80, 0x08, 0x00, 0x06, 0x0e, 0x00, 0x02, 0x06, 0x00, 0x02, 0x48, 0x00, - 0x8a, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x01, 0x00, 0x01, 0x00, 0x2a, 0x00, - 0x09, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, - 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, 0x06, - 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, 0xfe, 0xff, - 0xff, 0xff, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, - 0x06, 0x0e, 0x00, 0x02, 0x06, 0x00, 0x02, 0x43, 0x00, 0x8a, 0x6f, 0x6f, - 0x72, 0x64, 0x73, 0x01, 0x00, 0x01, 0x00, 0x16, 0x00, 0x09, 0x05, 0x00, - 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, - 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, - 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, 0xfe, 0xff, 0xff, 0xff, 0x0a, - 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x16, 0x00, - 0x02, 0x0e, 0x00, 0x02, 0x44, 0x00, 0x92, 0x6e, 0x61, 0x4d, 0x65, 0x61, - 0x73, 0x75, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x01, 0x00, 0x01, 0x00, - 0x1d, 0x00, 0x09, 0x04, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, - 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x01, 0x80, 0x01, - 0x00, 0x81, 0x01, 0x00, 0x0f, 0x03, 0x00, 0x02, 0x04, 0x00, 0x02, 0xfe, - 0xff, 0xff, 0xff, 0x09, 0x00, 0x02, 0x2c, 0x00, 0x95, 0x5f, 0x5f, 0x41, - 0x6e, 0x6f, 0x6e, 0x43, 0x33, 0x00, 0x08, 0x00, 0x5f, 0x5f, 0x41, 0x6e, - 0x6f, 0x6e, 0x43, 0x33, 0x00, 0x00, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xff, - 0xff, 0xff, 0x00, 0x03, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, - 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, - 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, - 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, - 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, - 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, - 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, - 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, - 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, - 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, - 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, - 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, - 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, - 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, - 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, - 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, - 0x08, 0x00, 0x06, 0x0c, 0x00, 0x02, 0x04, 0x00, 0x02, 0x54, 0x00, 0x84, - 0x79, 0x70, 0x65, 0x01, 0x00, 0x05, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, - 0x01, 0x80, 0x08, 0x00, 0x06, 0x0c, 0x00, 0x02, 0x04, 0x00, 0x02, 0x54, - 0x00, 0x88, 0x79, 0x70, 0x65, 0x01, 0x00, 0x01, 0x00, 0x1e, 0x00, 0x09, - 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, - 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x0b, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, - 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0e, 0x00, 0x02, 0x06, - 0x00, 0x02, 0x49, 0x00, 0x86, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x01, 0x00, - 0x05, 0xa0, 0x00, 0x0e, 0x01, 0x01, 0x00, 0x05, 0x01, 0x00, 0x02, 0x04, - 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, - 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x16, - 0x00, 0x02, 0x0e, 0x00, 0x02, 0x52, 0x00, 0x8e, 0x65, 0x66, 0x65, 0x72, - 0x65, 0x6e, 0x63, 0x65, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x01, 0x00, 0x05, - 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x16, - 0x00, 0x02, 0x0e, 0x00, 0x02, 0x52, 0x00, 0x92, 0x65, 0x66, 0x65, 0x72, - 0x65, 0x6e, 0x63, 0x65, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x01, 0x00, 0x01, - 0x00, 0x1f, 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, - 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, - 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, - 0x67, 0x00, 0x0b, 0x01, 0x01, 0x00, 0x05, 0x01, 0x00, 0x02, 0x04, 0x00, - 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, - 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0d, 0x00, - 0x02, 0x05, 0x00, 0x02, 0x45, 0x00, 0x86, 0x70, 0x6f, 0x63, 0x68, 0x00, - 0x01, 0x00, 0x05, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, - 0x00, 0x06, 0x0d, 0x00, 0x02, 0x05, 0x00, 0x02, 0x45, 0x00, 0x8a, 0x70, - 0x6f, 0x63, 0x68, 0x00, 0x01, 0x00, 0x01, 0x00, 0x20, 0x00, 0x09, 0x05, - 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, - 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, - 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x0b, 0x01, 0x01, - 0x00, 0x05, 0x01, 0x00, 0x02, 0x04, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, - 0x01, 0x80, 0x08, 0x00, 0x06, 0x0d, 0x00, 0x02, 0x05, 0x00, 0x02, 0x46, - 0x00, 0x86, 0x69, 0x72, 0x73, 0x74, 0x00, 0x01, 0x00, 0x05, 0x0a, 0x00, - 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0d, 0x00, 0x02, - 0x05, 0x00, 0x02, 0x46, 0x00, 0x8a, 0x69, 0x72, 0x73, 0x74, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x21, 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, - 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, - 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, - 0x69, 0x6e, 0x67, 0x00, 0x0b, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, - 0x01, 0x00, 0x02, 0x04, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, - 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, - 0x08, 0x00, 0x06, 0x0e, 0x00, 0x02, 0x06, 0x00, 0x02, 0x53, 0x00, 0x86, - 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x01, 0x00, 0x05, 0x0a, 0x00, 0x01, 0x80, - 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0e, 0x00, 0x02, 0x06, 0x00, - 0x02, 0x53, 0x00, 0x8a, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x01, 0x00, 0x01, - 0x00, 0x22, 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, - 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, - 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, - 0x67, 0x00, 0x0b, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, - 0x02, 0x04, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, - 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, - 0x06, 0x0d, 0x00, 0x02, 0x05, 0x00, 0x02, 0x54, 0x00, 0x86, 0x68, 0x69, - 0x72, 0x64, 0x00, 0x01, 0x00, 0x05, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, - 0x01, 0x80, 0x08, 0x00, 0x06, 0x0d, 0x00, 0x02, 0x05, 0x00, 0x02, 0x54, - 0x00, 0x8a, 0x68, 0x69, 0x72, 0x64, 0x00, 0x01, 0x00, 0x01, 0x00, 0x23, - 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, - 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, - 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, - 0x0b, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x04, - 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, - 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0d, - 0x00, 0x02, 0x05, 0x00, 0x02, 0x56, 0x00, 0x86, 0x61, 0x6c, 0x75, 0x65, - 0x00, 0x01, 0x00, 0x05, 0xa6, 0x00, 0x0e, 0x01, 0x01, 0x00, 0x01, 0x01, - 0x00, 0x02, 0x01, 0x00, 0x02, 0x04, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, - 0x01, 0x80, 0x08, 0x00, 0x06, 0x0e, 0x00, 0x02, 0x06, 0x00, 0x02, 0x53, - 0x00, 0x86, 0x74, 0x64, 0x44, 0x65, 0x76, 0x01, 0x00, 0x05, 0xaa, 0x00, - 0x0e, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x04, - 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, - 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x12, - 0x00, 0x02, 0x0a, 0x00, 0x02, 0x49, 0x00, 0x8a, 0x6e, 0x73, 0x74, 0x48, - 0x65, 0x69, 0x67, 0x68, 0x74, 0x01, 0x00, 0x05, 0x0a, 0x00, 0x01, 0x80, - 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x12, 0x00, 0x02, 0x0a, 0x00, - 0x02, 0x49, 0x00, 0x8e, 0x6e, 0x73, 0x74, 0x48, 0x65, 0x69, 0x67, 0x68, - 0x74, 0x01, 0x00, 0x01, 0x00, 0x24, 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, - 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, - 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x0b, 0x01, 0x01, 0x00, 0x01, 0x01, - 0x00, 0x02, 0x01, 0x00, 0x02, 0x04, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, - 0x01, 0x80, 0x08, 0x00, 0x06, 0x12, 0x00, 0x02, 0x0a, 0x00, 0x02, 0x54, - 0x00, 0x8a, 0x61, 0x72, 0x67, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x01, - 0x00, 0x05, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, - 0x06, 0x12, 0x00, 0x02, 0x0a, 0x00, 0x02, 0x54, 0x00, 0x8e, 0x61, 0x72, - 0x67, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x01, 0x00, 0x01, 0x00, 0x25, - 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, - 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, - 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, - 0x0b, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x04, - 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, - 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0d, - 0x00, 0x02, 0x05, 0x00, 0x02, 0x54, 0x00, 0x86, 0x6f, 0x74, 0x61, 0x6c, - 0x00, 0x01, 0x00, 0x05, 0xb3, 0x00, 0x0e, 0x01, 0x01, 0x00, 0x01, 0x01, - 0x00, 0x02, 0x01, 0x00, 0x02, 0x04, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, - 0x01, 0x80, 0x08, 0x00, 0x06, 0x12, 0x00, 0x02, 0x0a, 0x00, 0x02, 0x44, - 0x00, 0x8a, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x01, - 0x00, 0x05, 0x98, 0x00, 0x0e, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, - 0x01, 0x00, 0x02, 0x04, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, - 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, - 0x08, 0x00, 0x06, 0x0e, 0x00, 0x02, 0x06, 0x00, 0x02, 0x56, 0x00, 0x86, - 0x73, 0x63, 0x61, 0x6c, 0x65, 0x01, 0x00, 0x05, 0x0a, 0x00, 0x01, 0x80, - 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0e, 0x00, 0x02, 0x06, 0x00, - 0x02, 0x56, 0x00, 0x8a, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x01, 0x00, 0x01, - 0x00, 0x27, 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, - 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, - 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, - 0x67, 0x00, 0x0b, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, - 0x02, 0x04, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, - 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, - 0x06, 0x13, 0x00, 0x02, 0x0b, 0x00, 0x02, 0x47, 0x00, 0x8c, 0x50, 0x53, - 0x42, 0x61, 0x73, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x00, 0x01, 0x00, 0x05, - 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x13, - 0x00, 0x02, 0x0b, 0x00, 0x02, 0x47, 0x00, 0x90, 0x50, 0x53, 0x42, 0x61, - 0x73, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x00, 0x01, 0x00, 0x01, 0x00, 0x28, - 0x00, 0x09, 0x04, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, - 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x01, 0x80, 0x01, 0x00, - 0x81, 0x01, 0x00, 0x0f, 0x04, 0x00, 0x02, 0x04, 0x00, 0x02, 0xfe, 0xff, - 0xff, 0xff, 0x09, 0x00, 0x02, 0x2c, 0x00, 0x95, 0x5f, 0x5f, 0x41, 0x6e, - 0x6f, 0x6e, 0x43, 0x34, 0x00, 0x08, 0x00, 0x5f, 0x5f, 0x41, 0x6e, 0x6f, - 0x6e, 0x43, 0x34, 0x00, 0x00, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, - 0xff, 0x00, 0x03, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, - 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, - 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, - 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, - 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, - 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, - 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, - 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x09, - 0x00, 0x02, 0x01, 0x00, 0x02, 0x58, 0x00, 0x02, 0x01, 0x00, 0x06, 0x1e, - 0x00, 0x0e, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, - 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x09, - 0x00, 0x02, 0x01, 0x00, 0x02, 0x59, 0x00, 0x02, 0x01, 0x00, 0x06, 0x22, - 0x00, 0x0e, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, - 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, - 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, - 0x09, 0x00, 0x02, 0x01, 0x00, 0x02, 0x5a, 0x00, 0x02, 0x01, 0x00, 0x06, - 0x26, 0x00, 0x0e, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, - 0x02, 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, - 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, - 0x06, 0x0f, 0x00, 0x02, 0x07, 0x00, 0x02, 0x53, 0x00, 0x88, 0x69, 0x67, - 0x6d, 0x61, 0x58, 0x58, 0x00, 0x01, 0x00, 0x05, 0x2a, 0x00, 0x0e, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, 0x02, - 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, - 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0f, 0x00, 0x02, - 0x07, 0x00, 0x02, 0x53, 0x00, 0x88, 0x69, 0x67, 0x6d, 0x61, 0x58, 0x59, - 0x00, 0x01, 0x00, 0x05, 0x2e, 0x00, 0x0e, 0x01, 0x01, 0x00, 0x01, 0x01, - 0x00, 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, - 0x01, 0x80, 0x08, 0x00, 0x06, 0x0f, 0x00, 0x02, 0x07, 0x00, 0x02, 0x53, - 0x00, 0x88, 0x69, 0x67, 0x6d, 0x61, 0x58, 0x5a, 0x00, 0x01, 0x00, 0x05, - 0x32, 0x00, 0x0e, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, - 0x02, 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, - 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, - 0x06, 0x0f, 0x00, 0x02, 0x07, 0x00, 0x02, 0x53, 0x00, 0x88, 0x69, 0x67, - 0x6d, 0x61, 0x59, 0x59, 0x00, 0x01, 0x00, 0x05, 0x36, 0x00, 0x0e, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, 0x02, - 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, - 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0f, 0x00, 0x02, - 0x07, 0x00, 0x02, 0x53, 0x00, 0x88, 0x69, 0x67, 0x6d, 0x61, 0x59, 0x5a, - 0x00, 0x01, 0x00, 0x05, 0x3a, 0x00, 0x0e, 0x01, 0x01, 0x00, 0x01, 0x01, - 0x00, 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, - 0x01, 0x80, 0x08, 0x00, 0x06, 0x0f, 0x00, 0x02, 0x07, 0x00, 0x02, 0x53, - 0x00, 0x88, 0x69, 0x67, 0x6d, 0x61, 0x5a, 0x5a, 0x00, 0x01, 0x00, 0x05, - 0x3e, 0x00, 0x0e, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, - 0x02, 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, - 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, - 0x06, 0x15, 0x00, 0x02, 0x0d, 0x00, 0x02, 0x47, 0x00, 0x8e, 0x50, 0x53, - 0x43, 0x6f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x63, 0x65, 0x00, 0x01, - 0x00, 0x05, 0x78, 0x00, 0x0e, 0x01, 0x01, 0x00, 0x05, 0xff, 0xff, 0xff, - 0xff, 0x15, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, - 0x00, 0x06, 0x71, 0x00, 0x01, 0x80, 0xfe, 0xff, 0xff, 0xff, 0x1d, 0x00, - 0x16, 0xfe, 0xff, 0xff, 0xff, 0x0a, 0x00, 0x06, 0x1e, 0x00, 0x02, 0x22, - 0x00, 0x02, 0x26, 0x00, 0x02, 0x2a, 0x00, 0x02, 0x2e, 0x00, 0x02, 0x32, - 0x00, 0x02, 0x36, 0x00, 0x02, 0x3a, 0x00, 0x02, 0x3e, 0x00, 0x02, 0x78, - 0x00, 0x02, 0x21, 0x01, 0x00, 0x11, 0xfd, 0xff, 0xff, 0xff, 0x00, 0x0b, - 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x04, 0x00, - 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, - 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0e, 0x00, - 0x02, 0x06, 0x00, 0x02, 0x48, 0x00, 0x86, 0x73, 0x63, 0x61, 0x6c, 0x65, - 0x01, 0x00, 0x05, 0xb5, 0x00, 0x0e, 0x01, 0x01, 0x00, 0x05, 0x01, 0x00, - 0x02, 0x04, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, - 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, - 0x06, 0x0e, 0x00, 0x02, 0x06, 0x00, 0x02, 0x4c, 0x00, 0x86, 0x73, 0x63, - 0x61, 0x6c, 0x65, 0x01, 0x00, 0x05, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, - 0x01, 0x80, 0x08, 0x00, 0x06, 0x0e, 0x00, 0x02, 0x06, 0x00, 0x02, 0x4c, - 0x00, 0x8a, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x01, 0x00, 0x01, 0x00, 0x2b, - 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, - 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, - 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, - 0x0b, 0x01, 0x01, 0x00, 0x05, 0x01, 0x00, 0x02, 0x04, 0x00, 0x02, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, - 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0e, 0x00, 0x02, 0x06, - 0x00, 0x02, 0x50, 0x00, 0x86, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x01, 0x00, - 0x05, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, - 0x0e, 0x00, 0x02, 0x06, 0x00, 0x02, 0x50, 0x00, 0x8a, 0x73, 0x63, 0x61, - 0x6c, 0x65, 0x01, 0x00, 0x01, 0x00, 0x2c, 0x00, 0x09, 0x05, 0x00, 0x02, - 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, - 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, - 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x0b, 0x01, 0x01, 0x00, 0x05, - 0x01, 0x00, 0x02, 0x04, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, - 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, - 0x08, 0x00, 0x06, 0x14, 0x00, 0x02, 0x0c, 0x00, 0x02, 0x43, 0x00, 0x8c, - 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x01, - 0x00, 0x05, 0x0e, 0x00, 0x0e, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, - 0x01, 0x00, 0x02, 0x04, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, - 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, - 0x08, 0x00, 0x06, 0x0e, 0x00, 0x02, 0x06, 0x00, 0x02, 0x43, 0x00, 0x86, - 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x01, 0x00, 0x05, 0xb7, 0x00, 0x0e, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x04, 0x00, 0x02, - 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, - 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0e, 0x00, 0x02, - 0x06, 0x00, 0x02, 0x53, 0x00, 0x86, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x01, - 0x00, 0x05, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, - 0x06, 0x0e, 0x00, 0x02, 0x06, 0x00, 0x02, 0x53, 0x00, 0x8a, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x01, 0x00, 0x01, 0x00, 0x2d, 0x00, 0x09, 0x05, 0x00, - 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, - 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, - 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x0b, 0x01, 0x01, 0x00, - 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x24, 0x00, 0x02, 0x01, 0x01, - 0x00, 0x01, 0x01, 0x00, 0x02, 0xff, 0xff, 0xff, 0xff, 0x00, 0x03, 0x71, - 0x00, 0x01, 0x80, 0xfe, 0xff, 0xff, 0xff, 0x1d, 0x00, 0x16, 0xfe, 0xff, - 0xff, 0xff, 0x15, 0x00, 0x06, 0xd2, 0x00, 0x02, 0xa0, 0x00, 0x02, 0xd8, - 0x00, 0x02, 0xdc, 0x00, 0x02, 0xe0, 0x00, 0x02, 0xe4, 0x00, 0x02, 0xe8, - 0x00, 0x02, 0xa6, 0x00, 0x02, 0xaa, 0x00, 0x02, 0xf0, 0x00, 0x02, 0xf4, - 0x00, 0x02, 0xb3, 0x00, 0x02, 0x98, 0x00, 0x02, 0xfc, 0x00, 0x03, 0x01, - 0x00, 0x01, 0xb5, 0x00, 0x02, 0x27, 0x01, 0x00, 0x01, 0x2b, 0x01, 0x00, - 0x01, 0x0e, 0x00, 0x02, 0xb7, 0x00, 0x02, 0x33, 0x01, 0x00, 0x01, 0x36, - 0x01, 0x00, 0x11, 0xfd, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0x0a, - 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0e, 0x00, - 0x02, 0x06, 0x00, 0x02, 0x48, 0x00, 0x8a, 0x65, 0x69, 0x67, 0x68, 0x74, - 0x01, 0x00, 0x01, 0x00, 0x33, 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, 0x00, - 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, - 0x13, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, - 0x72, 0x69, 0x6e, 0x67, 0xfe, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x02, 0xfe, - 0xff, 0xff, 0xff, 0x36, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x3a, 0x00, - 0x02, 0xfe, 0xff, 0xff, 0xff, 0xe8, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, - 0xe4, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x2b, 0x01, 0x00, 0x01, 0xfe, - 0xff, 0xff, 0xff, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, - 0x00, 0x06, 0x0d, 0x00, 0x02, 0x05, 0x00, 0x02, 0x59, 0x00, 0x8a, 0x41, - 0x78, 0x69, 0x73, 0x00, 0x01, 0x00, 0x01, 0x00, 0x32, 0x00, 0x09, 0x05, - 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, - 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, - 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, 0xfe, 0xff, 0xff, 0xff, - 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x14, - 0x00, 0x02, 0x0c, 0x00, 0x02, 0x53, 0x00, 0x90, 0x74, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x01, 0x00, 0x01, 0x00, 0x30, - 0x00, 0x09, 0x04, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, - 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x01, 0x80, 0x01, 0x00, - 0x81, 0x01, 0x00, 0x0f, 0x07, 0x00, 0x02, 0x04, 0x00, 0x02, 0xfe, 0xff, - 0xff, 0xff, 0x09, 0x00, 0x02, 0x2c, 0x00, 0x95, 0x5f, 0x5f, 0x41, 0x6e, - 0x6f, 0x6e, 0x43, 0x37, 0x00, 0x08, 0x00, 0x5f, 0x5f, 0x41, 0x6e, 0x6f, - 0x6e, 0x43, 0x37, 0x00, 0x00, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, - 0xff, 0x00, 0x03, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, - 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, - 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, - 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, - 0x00, 0x06, 0x0c, 0x00, 0x02, 0x04, 0x00, 0x02, 0x4e, 0x00, 0x84, 0x61, - 0x6d, 0x65, 0x01, 0x00, 0x05, 0xb1, 0x00, 0x0e, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, - 0x01, 0x80, 0x08, 0x00, 0x06, 0x0d, 0x00, 0x02, 0x05, 0x00, 0x02, 0x58, - 0x00, 0x86, 0x41, 0x78, 0x69, 0x73, 0x00, 0x01, 0x00, 0x05, 0x0a, 0x00, - 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0d, 0x00, 0x02, - 0x05, 0x00, 0x02, 0x58, 0x00, 0x8a, 0x41, 0x78, 0x69, 0x73, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x31, 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, - 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, - 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, - 0x69, 0x6e, 0x67, 0x00, 0x0b, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, - 0x01, 0x00, 0x02, 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, - 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, - 0x08, 0x00, 0x06, 0x0d, 0x00, 0x02, 0x05, 0x00, 0x02, 0x59, 0x00, 0x86, - 0x41, 0x78, 0x69, 0x73, 0x00, 0x01, 0x00, 0x05, 0x3a, 0x01, 0x00, 0x0d, - 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, - 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, - 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0e, 0x00, - 0x02, 0x06, 0x00, 0x02, 0x48, 0x00, 0x86, 0x65, 0x69, 0x67, 0x68, 0x74, - 0x01, 0x00, 0x05, 0x38, 0x01, 0x00, 0x0d, 0x01, 0x01, 0x00, 0x01, 0x01, - 0x00, 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, - 0x01, 0x80, 0x08, 0x00, 0x06, 0x16, 0x00, 0x02, 0x0e, 0x00, 0x02, 0x48, - 0x00, 0x8e, 0x65, 0x6d, 0x69, 0x73, 0x70, 0x68, 0x65, 0x72, 0x65, 0x5a, - 0x6f, 0x6e, 0x65, 0x01, 0x00, 0x05, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, - 0x01, 0x80, 0x08, 0x00, 0x06, 0x16, 0x00, 0x02, 0x0e, 0x00, 0x02, 0x48, - 0x00, 0x92, 0x65, 0x6d, 0x69, 0x73, 0x70, 0x68, 0x65, 0x72, 0x65, 0x5a, - 0x6f, 0x6e, 0x65, 0x01, 0x00, 0x01, 0x00, 0x34, 0x00, 0x09, 0x05, 0x00, - 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, - 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, - 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x0b, 0x01, 0x01, 0x00, - 0x05, 0x01, 0x00, 0x02, 0x15, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, - 0x00, 0x02, 0x01, 0x00, 0x06, 0x71, 0x00, 0x01, 0x80, 0xfe, 0xff, 0xff, - 0xff, 0x1d, 0x00, 0x16, 0xfe, 0xff, 0xff, 0xff, 0x05, 0x00, 0x06, 0xb1, - 0x00, 0x02, 0x47, 0x01, 0x00, 0x01, 0x3a, 0x01, 0x00, 0x01, 0x38, 0x01, - 0x00, 0x01, 0x4f, 0x01, 0x00, 0x01, 0x52, 0x01, 0x00, 0x11, 0xfd, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xaa, 0x00, 0x02, 0xfe, 0xff, 0xff, - 0xff, 0x4f, 0x01, 0x00, 0x01, 0xfe, 0xff, 0xff, 0xff, 0x4f, 0x00, 0x02, - 0xfe, 0xff, 0xff, 0xff, 0x53, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x57, - 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, - 0x01, 0x80, 0x08, 0x00, 0x06, 0x14, 0x00, 0x02, 0x0c, 0x00, 0x02, 0x44, - 0x00, 0x90, 0x6e, 0x61, 0x58, 0x6d, 0x6c, 0x46, 0x6f, 0x72, 0x6d, 0x61, - 0x74, 0x01, 0x00, 0x01, 0x00, 0x35, 0x00, 0x09, 0x04, 0x00, 0x02, 0x01, - 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, - 0x10, 0x00, 0x01, 0x80, 0x01, 0x00, 0x81, 0x01, 0x00, 0x0f, 0x08, 0x00, - 0x02, 0x04, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x09, 0x00, 0x02, 0x2c, - 0x00, 0x95, 0x5f, 0x5f, 0x41, 0x6e, 0x6f, 0x6e, 0x43, 0x38, 0x00, 0x08, - 0x00, 0x5f, 0x5f, 0x41, 0x6e, 0x6f, 0x6e, 0x43, 0x38, 0x00, 0x00, 0xfd, - 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0x00, 0x03, 0x12, 0x00, 0x01, - 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, - 0x01, 0x80, 0x08, 0x00, 0x06, 0x12, 0x00, 0x02, 0x0a, 0x00, 0x02, 0x44, - 0x00, 0x8a, 0x6e, 0x61, 0x53, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x01, - 0x00, 0x05, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, - 0x06, 0x12, 0x00, 0x02, 0x0a, 0x00, 0x02, 0x44, 0x00, 0x8e, 0x6e, 0x61, - 0x53, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x01, 0x00, 0x01, 0x00, 0x2e, - 0x00, 0x09, 0x04, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, - 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x01, 0x80, 0x01, 0x00, - 0x81, 0x01, 0x00, 0x0f, 0x06, 0x00, 0x02, 0x04, 0x00, 0x02, 0xfe, 0xff, - 0xff, 0xff, 0x09, 0x00, 0x02, 0x2c, 0x00, 0x95, 0x5f, 0x5f, 0x41, 0x6e, - 0x6f, 0x6e, 0x43, 0x36, 0x00, 0x08, 0x00, 0x5f, 0x5f, 0x41, 0x6e, 0x6f, - 0x6e, 0x43, 0x36, 0x00, 0x00, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, - 0xff, 0x00, 0x03, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, - 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, - 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, - 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, - 0x00, 0x06, 0x0c, 0x00, 0x02, 0x04, 0x00, 0x02, 0x4e, 0x00, 0x84, 0x61, - 0x6d, 0x65, 0x01, 0x00, 0x05, 0xb1, 0x00, 0x0e, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, - 0x01, 0x80, 0x08, 0x00, 0x06, 0x13, 0x00, 0x02, 0x0b, 0x00, 0x02, 0x43, - 0x00, 0x8c, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x73, - 0x00, 0x01, 0x00, 0x05, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, - 0x08, 0x00, 0x06, 0x13, 0x00, 0x02, 0x0b, 0x00, 0x02, 0x43, 0x00, 0x90, - 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x73, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x15, 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, - 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, - 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, - 0x69, 0x6e, 0x67, 0x00, 0x0b, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, - 0x01, 0x00, 0x02, 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, - 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, - 0x08, 0x00, 0x06, 0x0c, 0x00, 0x02, 0x04, 0x00, 0x02, 0x54, 0x00, 0x84, - 0x79, 0x70, 0x65, 0x01, 0x00, 0x05, 0xd2, 0x00, 0x0e, 0x01, 0x01, 0x00, - 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, 0x02, 0x01, 0x01, - 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, - 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x14, 0x00, 0x02, 0x0c, 0x00, - 0x02, 0x53, 0x00, 0x8c, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, - 0x6f, 0x72, 0x64, 0x01, 0x00, 0x05, 0x3c, 0x01, 0x00, 0x0d, 0x01, 0x01, - 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, 0x02, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, - 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x13, 0x00, 0x02, 0x0b, - 0x00, 0x02, 0x44, 0x00, 0x8c, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x00, 0x01, 0x00, 0x05, 0xaf, 0x00, 0x0e, 0x01, 0x01, - 0x00, 0x05, 0x01, 0x00, 0x02, 0x15, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x02, 0x01, 0x00, 0x06, 0x71, 0x00, 0x01, 0x80, 0xfe, 0xff, - 0xff, 0xff, 0x1d, 0x00, 0x16, 0xfe, 0xff, 0xff, 0xff, 0x05, 0x00, 0x06, - 0xb1, 0x00, 0x02, 0x65, 0x01, 0x00, 0x01, 0xd2, 0x00, 0x02, 0x3c, 0x01, - 0x00, 0x01, 0xaf, 0x00, 0x02, 0x6e, 0x01, 0x00, 0x11, 0xfd, 0xff, 0xff, - 0xff, 0x00, 0x0b, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, - 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, - 0x16, 0x00, 0x02, 0x0e, 0x00, 0x02, 0x44, 0x00, 0x8e, 0x6e, 0x61, 0x4d, - 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x01, 0x00, - 0x05, 0xb9, 0x00, 0x0e, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, - 0x00, 0x02, 0x24, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x03, 0x71, 0x00, 0x01, 0x80, 0xfe, 0xff, - 0xff, 0xff, 0x1d, 0x00, 0x06, 0x03, 0x00, 0x06, 0xff, 0xff, 0xff, 0xff, - 0x0c, 0x00, 0x02, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x41, 0x74, 0x74, - 0x44, 0x65, 0x66, 0x00, 0x03, 0x0a, 0x00, 0x07, 0xfe, 0xff, 0xff, 0xff, - 0x00, 0x06, 0x05, 0x00, 0x02, 0x47, 0x00, 0x85, 0x44, 0x41, 0x39, 0x34, - 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x03, 0x02, - 0x00, 0x02, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x16, 0x00, 0x02, - 0x0e, 0x00, 0x02, 0x72, 0x00, 0x8f, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, - 0x63, 0x65, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x01, 0x00, 0xff, 0xff, 0xff, - 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, - 0x00, 0x07, 0x74, 0x01, 0x00, 0x82, 0x80, 0x00, 0x00, 0x81, 0x0a, 0x00, - 0x06, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x06, 0x0a, 0x00, 0x02, 0x30, 0x00, - 0x89, 0x31, 0x2e, 0x30, 0x31, 0x2e, 0x31, 0x39, 0x39, 0x34, 0xff, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x03, 0x02, 0x00, 0x02, 0x0c, - 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0d, 0x00, 0x02, 0x05, 0x00, 0x02, - 0x65, 0x00, 0x87, 0x70, 0x6f, 0x63, 0x68, 0x00, 0x01, 0x00, 0xff, 0xff, - 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, - 0x67, 0x00, 0x07, 0x74, 0x01, 0x00, 0x00, 0x80, 0x02, 0x00, 0x02, 0x0a, - 0x00, 0x07, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x06, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x03, 0x02, 0x00, - 0x02, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0c, 0x00, 0x02, 0x04, - 0x00, 0x02, 0x74, 0x00, 0x85, 0x79, 0x70, 0x65, 0x01, 0x00, 0xfe, 0xff, - 0xff, 0xff, 0x00, 0x03, 0x06, 0x00, 0x0a, 0x03, 0x00, 0x06, 0xfe, 0xff, - 0xff, 0xff, 0x02, 0x00, 0x06, 0x5a, 0x01, 0x00, 0x01, 0xb9, 0x00, 0x02, - 0x73, 0x01, 0x00, 0x11, 0xfd, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, - 0xd2, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x33, 0x01, 0x00, 0x01, 0xfe, - 0xff, 0xff, 0xff, 0xd8, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0xdc, 0x00, - 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x81, 0x01, 0x00, 0x00, 0xfe, 0xff, - 0xff, 0xff, 0xa6, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x3e, 0x00, 0x02, - 0xfe, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x65, - 0x01, 0x00, 0x01, 0xfe, 0xff, 0xff, 0xff, 0x47, 0x01, 0x00, 0x01, 0xfe, - 0xff, 0xff, 0xff, 0xf4, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x1e, 0x00, - 0x02, 0xfe, 0xff, 0xff, 0xff, 0x22, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, - 0x26, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x27, 0x01, 0x00, 0x01, 0xfe, - 0xff, 0xff, 0xff, 0x5b, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x5f, 0x00, - 0x02, 0xfe, 0xff, 0xff, 0xff, 0x63, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, - 0xe0, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x5a, 0x01, 0x00, 0x01, 0xfe, - 0xff, 0xff, 0xff, 0x42, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x2a, 0x00, - 0x02, 0xfe, 0xff, 0xff, 0xff, 0x2e, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, - 0x32, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x6d, 0x00, 0x0e, 0xfe, 0xff, - 0xff, 0xff, 0x00, 0x03, 0xfe, 0xff, 0xff, 0xff, 0x1d, 0x00, 0x0e, 0xfe, - 0xff, 0xff, 0xff, 0x1d, 0x00, 0x06, 0x09, 0x00, 0x06, 0x44, 0x00, 0x02, - 0x9a, 0x00, 0x02, 0xbb, 0x00, 0x02, 0x02, 0x01, 0x00, 0x01, 0x7a, 0x00, - 0x02, 0x5c, 0x01, 0x00, 0x01, 0x3e, 0x01, 0x00, 0x01, 0x56, 0x01, 0x00, - 0x01, 0x11, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x0d, 0x00, 0x0e, 0xfe, - 0xff, 0xff, 0xff, 0x0d, 0x00, 0x0e, 0xfe, 0xff, 0xff, 0xff, 0x1d, 0x00, - 0x0e, 0xfe, 0xff, 0xff, 0xff, 0x1d, 0x00, 0x12, 0x01, 0x00, 0x02, 0x03, - 0x00, 0x06, 0xfe, 0xff, 0xff, 0xff, 0x01, 0x00, 0x06, 0x30, 0x00, 0x02, - 0x43, 0x00, 0xaf, 0x3a, 0x5c, 0x44, 0x61, 0x74, 0x61, 0x5c, 0x76, 0x73, - 0x31, 0x30, 0x5c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x5c, - 0x64, 0x79, 0x6e, 0x61, 0x6e, 0x65, 0x74, 0x5f, 0x33, 0x5f, 0x30, 0x33, - 0x5f, 0x30, 0x30, 0x2f, 0x44, 0x79, 0x6e, 0x61, 0x4d, 0x4c, 0x2e, 0x78, - 0x73, 0x64, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, - 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, - 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, - 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, - 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, - 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, - 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x63 -}; - diff --git a/dynadjust/include/io/DynaML-schema.hxx b/dynadjust/include/io/DynaML-schema.hxx deleted file mode 100644 index 0f8fedebf..000000000 --- a/dynadjust/include/io/DynaML-schema.hxx +++ /dev/null @@ -1,8 +0,0 @@ -// Automatically generated. Do not edit. -// - -/// \cond -#include -/// \endcond - -extern const XMLByte DynaML_schema[11430UL]; diff --git a/dynadjust/include/io/bms_file.cpp b/dynadjust/include/io/bms_file.cpp index 0f78268a3..6b961705a 100644 --- a/dynadjust/include/io/bms_file.cpp +++ b/dynadjust/include/io/bms_file.cpp @@ -147,6 +147,14 @@ std::uint64_t BmsFile::LoadFile(const std::string& bms_filename, ReadFileInfo(bms_file); ReadFileMetadata(bms_file, bms_meta); + // measurement_t grew by observation_epoch in v1.2; record layout requires a v1.2 file. + if (!versionAtLeast(1, 2)) { + std::ostringstream os; + os << "BMS file version " << GetVersion() + << " predates observation_epoch support (v1.2); please re-run dnaimport."; + throw std::runtime_error(os.str()); + } + vbinary_msr->reserve(bms_meta.binCount); for (msr = 0; msr < bms_meta.binCount; msr++) { bms_file.read(reinterpret_cast(&measRecord), diff --git a/dynadjust/include/io/bst_file.cpp b/dynadjust/include/io/bst_file.cpp index 185ad46de..e1c757985 100644 --- a/dynadjust/include/io/bst_file.cpp +++ b/dynadjust/include/io/bst_file.cpp @@ -128,6 +128,14 @@ std::uint64_t BstFile::LoadFile(const std::string& bst_filename, ReadFileInfo(bst_file); ReadFileMetadata(bst_file, bst_meta); + // station_t grew by observation_epoch in v1.2; bulk I/O requires a v1.2 layout. + if (!versionAtLeast(1, 2)) { + std::ostringstream os; + os << "BST file version " << GetVersion() + << " predates observation_epoch support (v1.2); please re-run dnaimport."; + throw std::runtime_error(os.str()); + } + vbinary_stn->resize(bst_meta.binCount); static_assert(std::is_trivially_copyable_v, "station_t must be trivially copyable for bulk I/O"); diff --git a/dynadjust/include/io/dnaiodnatypes.hpp b/dynadjust/include/io/dnaiodnatypes.hpp index b2828d6a0..500656f0e 100644 --- a/dynadjust/include/io/dnaiodnatypes.hpp +++ b/dynadjust/include/io/dnaiodnatypes.hpp @@ -94,6 +94,7 @@ typedef struct { UINT32 msr_targ_ht; UINT32 msr_id_msr; UINT32 msr_id_cluster; + UINT32 msr_gps_obs_epoch; // DNA v3.02+ column; 0 if file predates v3.02 } dna_msr_fields; // DNA version 1.00 @@ -440,6 +441,82 @@ class dna_msr_fields_301 }; +// DNA version 3.02 - adds a 25th column for the observation epoch. +// Columns 0-23 are identical to version 3.01. +template +struct _dna_msr_fields_302_ +{ + static const U _locations_[25]; + static const U _widths_[25]; +}; + +template +const U _dna_msr_fields_302_::_locations_[25] = +{ + 0, // msr type + 1, // ignore + 2, // instrument name + 22, // target 1 + 42, // target 2 + 62, // linear measurement + 62, // gps measurement + 82, // gps vcv 1 + 102, // gps vcv 2 + 122, // gps vcv 3 + 62, // gps vscale + 72, // gps pscale + 82, // gps lscale + 92, // gps hscale + 102, // gps ref frame + 122, // gps epoch (reference-frame epoch) + 76, // angular d + 80, // angular m + 82, // angular s + 90, // standard deviation + 99, // inst height + 106, // target height + 142, // msr id + 152, // cluster id + 162 // gps observation epoch +}; + +template +const U _dna_msr_fields_302_::_widths_[25] = +{ + 1, // msr type + 1, // ignore + 20, // instrument name + 20, // target 1 + 20, // target 2 + 14, // linear measurement + 20, // gps measurement + 20, // gps vcv 1 + 20, // gps vcv 2 + 20, // gps vcv 3 + 10, // gps vscale + 10, // gps pscale + 10, // gps lscale + 10, // gps hscale + 20, // gps ref frame + 20, // gps epoch (reference-frame epoch) + 4, // angular d + 2, // angular m + 8, // angular s + 9, // standard deviation + 7, // inst height + 7, // target height + 10, // msr id + 10, // cluster id + 20 // gps observation epoch +}; + +class dna_msr_fields_302 + : public _dna_msr_fields_302_ +{ +public: +}; + + template void assignDNASTNFieldParameters(const UINT16* locs, const UINT16* widths, @@ -523,7 +600,9 @@ void assignDNAMSRFieldParameters(const UINT16* locs, const UINT16* widths, dflocs.msr_targ_ht = locs[21]; dflocs.msr_id_msr = locs[22]; dflocs.msr_id_cluster = locs[23]; - + // Default the 3.02-introduced field to 0; a v3.02 branch overrides it below. + dflocs.msr_gps_obs_epoch = 0; + dfwidths.msr_type = widths[0]; dfwidths.msr_ignore = widths[1]; dfwidths.msr_inst = widths[2]; @@ -548,14 +627,26 @@ void assignDNAMSRFieldParameters(const UINT16* locs, const UINT16* widths, dfwidths.msr_targ_ht = widths[21]; dfwidths.msr_id_msr = widths[22]; dfwidths.msr_id_cluster = widths[23]; - + dfwidths.msr_gps_obs_epoch = 0; } template -void determineDNAMSRFieldParameters(const std::string& version, +void determineDNAMSRFieldParameters(const std::string& version, dna_msr_fields& dflocs, dna_msr_fields& dfwidths, const U u=0) { + if (iequals(version, "3.02")) + { + assignDNAMSRFieldParameters(dna_msr_fields_302::_locations_, + dna_msr_fields_302::_widths_, + dflocs, dfwidths); + // The 3.02-introduced observation-epoch column is beyond the shared + // 24-entry assigner, so set it explicitly here. + dflocs.msr_gps_obs_epoch = dna_msr_fields_302::_locations_[24]; + dfwidths.msr_gps_obs_epoch = dna_msr_fields_302::_widths_[24]; + return; + } + if (iequals(version, "3.01")) { assignDNAMSRFieldParameters(dna_msr_fields_301::_locations_, diff --git a/dynadjust/include/io/dnaiosnxread.cpp b/dynadjust/include/io/dnaiosnxread.cpp index 83727cc74..42088811d 100644 --- a/dynadjust/include/io/dnaiosnxread.cpp +++ b/dynadjust/include/io/dnaiosnxread.cpp @@ -939,10 +939,15 @@ void DnaIoSnx::ParseSinexStn(std::ifstream** snx_file, const char* sinexRec, vdn stn_ptr->SetReferenceFrame(datum.GetName()); stn_ptr->SetEpsg(datum.GetEpsgCode_s()); - yy = LongFromString(sBuf.substr(27, 2)), - doy = LongFromString(sBuf.substr(30, 3)), + yy = LongFromString(sBuf.substr(27, 2)), + doy = LongFromString(sBuf.substr(30, 3)), ss = ParseDateFromYyDoy(yy, doy, doy_yyyy, std::string(" ")); - stn_ptr->SetEpoch(stringFromDate(dateFromStringstream_doy_year(ss))); + { + std::string sinex_epoch = stringFromDate(dateFromStringstream_doy_year(ss)); + stn_ptr->SetEpoch(sinex_epoch); + // SINEX STAX YY:DOY is the observation data window start — record it as the immutable observation epoch + stn_ptr->SetObservationEpoch(sinex_epoch); + } stn_ptr->SetfileOrder(fileOrder++); stn_ptr->SetXAxis_d(DoubleFromString(trimstr(sBuf.substr(47, 21)))); @@ -1310,6 +1315,7 @@ void DnaIoSnx::ParseSinexMsr(std::ifstream** snx_file, const char* sinexRec, vdn dnaGpsPointCluster->SetReferenceFrame(datum.GetName()); dnaGpsPointCluster->SetEpsg(datum.GetEpsgCode_s()); dnaGpsPointCluster->SetEpoch(vStations->at(0)->GetEpoch()); + dnaGpsPointCluster->SetObservationEpoch(vStations->at(0)->GetObservationEpoch()); UINT32 cov_count, ci; @@ -1328,6 +1334,7 @@ void DnaIoSnx::ParseSinexMsr(std::ifstream** snx_file, const char* sinexRec, vdn dnaGpsPoint->SetReferenceFrame(datum.GetName()); dnaGpsPoint->SetEpsg(datum.GetEpsgCode_s()); dnaGpsPoint->SetEpoch(vStations->at(p)->GetEpoch()); + dnaGpsPoint->SetObservationEpoch(vStations->at(p)->GetObservationEpoch()); dnaGpsPoint->SetPscale(dnaGpsPointCluster->GetPscale()); dnaGpsPoint->SetLscale(dnaGpsPointCluster->GetLscale()); diff --git a/dynadjust/include/io/dynadjust_file.cpp b/dynadjust/include/io/dynadjust_file.cpp index ebc0f2806..e98927ae8 100644 --- a/dynadjust/include/io/dynadjust_file.cpp +++ b/dynadjust/include/io/dynadjust_file.cpp @@ -83,24 +83,28 @@ void DynadjustFile::ReadFileInfo(std::ifstream& file_stream) void DynadjustFile::WriteFileMetadata(std::ofstream& file_stream, binary_file_meta_t& file_meta) { // Write the metadata - file_stream.write(reinterpret_cast(&file_meta.binCount), sizeof(std::uint64_t)); - file_stream.write(reinterpret_cast(&file_meta.reduced), sizeof(bool)); - file_stream.write(reinterpret_cast(file_meta.modifiedBy), MOD_NAME_WIDTH); - - // Write the epsg code and epoch + file_stream.write(reinterpret_cast(&file_meta.binCount), sizeof(std::uint64_t)); + file_stream.write(reinterpret_cast(&file_meta.reduced), sizeof(bool)); + file_stream.write(reinterpret_cast(file_meta.modifiedBy), MOD_NAME_WIDTH); + + // Write the epsg code and epochs (reference frame + observation) file_stream.write(reinterpret_cast(file_meta.epsgCode), STN_EPSG_WIDTH); file_stream.write(reinterpret_cast(file_meta.epoch), STN_EPOCH_WIDTH); + // v1.2+: write observation_epoch after reference-frame epoch + file_stream.write(reinterpret_cast(file_meta.observation_epoch), STN_EPOCH_WIDTH); file_stream.write(reinterpret_cast(&file_meta.reftran), sizeof(bool)); file_stream.write(reinterpret_cast(&file_meta.geoid), sizeof(bool)); // Write file count and file meta - file_stream.write(reinterpret_cast(&file_meta.inputFileCount), sizeof(std::uint64_t)); + file_stream.write(reinterpret_cast(&file_meta.inputFileCount), sizeof(std::uint64_t)); for (std::uint64_t i(0); i(file_meta.inputFileMeta[i].filename), FILE_NAME_WIDTH); + file_stream.write(reinterpret_cast(file_meta.inputFileMeta[i].filename), FILE_NAME_WIDTH); file_stream.write(reinterpret_cast(file_meta.inputFileMeta[i].epsgCode), STN_EPSG_WIDTH); file_stream.write(reinterpret_cast(file_meta.inputFileMeta[i].epoch), STN_EPOCH_WIDTH); + // v1.2+: observation_epoch per input file + file_stream.write(reinterpret_cast(file_meta.inputFileMeta[i].observation_epoch), STN_EPOCH_WIDTH); file_stream.write(reinterpret_cast(&file_meta.inputFileMeta[i].filetype), sizeof(UINT16)); file_stream.write(reinterpret_cast(&file_meta.inputFileMeta[i].datatype), sizeof(UINT16)); } @@ -114,15 +118,25 @@ void DynadjustFile::WriteFileMetadata(std::ofstream& file_stream, binary_file_me void DynadjustFile::ReadFileMetadata(std::ifstream& file_stream, binary_file_meta_t& file_meta) { + const bool has_observation_epoch = versionAtLeast(1, 2); + // Read the metadata - file_stream.read(reinterpret_cast(&file_meta.binCount), sizeof(std::uint64_t)); - file_stream.read(reinterpret_cast(&file_meta.reduced), sizeof(bool)); + file_stream.read(reinterpret_cast(&file_meta.binCount), sizeof(std::uint64_t)); + file_stream.read(reinterpret_cast(&file_meta.reduced), sizeof(bool)); file_stream.read(reinterpret_cast(file_meta.modifiedBy), MOD_NAME_WIDTH); // Read the epsg code and epoch file_stream.read(reinterpret_cast(file_meta.epsgCode), STN_EPSG_WIDTH); file_stream.read(reinterpret_cast(file_meta.epoch), STN_EPOCH_WIDTH); + // v1.2+: observation_epoch follows the reference-frame epoch. + // Older files fall back to observation_epoch = epoch so legacy files remain loadable + // without changing semantics (discontinuity matching uses epoch if observation is empty). + if (has_observation_epoch) + file_stream.read(reinterpret_cast(file_meta.observation_epoch), STN_EPOCH_WIDTH); + else + memcpy(file_meta.observation_epoch, file_meta.epoch, STN_EPOCH_WIDTH); + file_stream.read(reinterpret_cast(&file_meta.reftran), sizeof(bool)); file_stream.read(reinterpret_cast(&file_meta.geoid), sizeof(bool)); @@ -132,12 +146,16 @@ void DynadjustFile::ReadFileMetadata(std::ifstream& file_stream, binary_file_met delete []file_meta.inputFileMeta; file_meta.inputFileMeta = new input_file_meta_t[file_meta.inputFileCount]; - + for (std::uint64_t i(0); i(file_meta.inputFileMeta[i].filename), FILE_NAME_WIDTH); + file_stream.read(reinterpret_cast(file_meta.inputFileMeta[i].filename), FILE_NAME_WIDTH); file_stream.read(reinterpret_cast(file_meta.inputFileMeta[i].epsgCode), STN_EPSG_WIDTH); file_stream.read(reinterpret_cast(file_meta.inputFileMeta[i].epoch), STN_EPOCH_WIDTH); + if (has_observation_epoch) + file_stream.read(reinterpret_cast(file_meta.inputFileMeta[i].observation_epoch), STN_EPOCH_WIDTH); + else + memcpy(file_meta.inputFileMeta[i].observation_epoch, file_meta.inputFileMeta[i].epoch, STN_EPOCH_WIDTH); file_stream.read(reinterpret_cast(&file_meta.inputFileMeta[i].filetype), sizeof(UINT16)); file_stream.read(reinterpret_cast(&file_meta.inputFileMeta[i].datatype), sizeof(UINT16)); } diff --git a/dynadjust/include/io/dynadjust_file.hpp b/dynadjust/include/io/dynadjust_file.hpp index 9e82f2c2b..f1c7d046a 100644 --- a/dynadjust/include/io/dynadjust_file.hpp +++ b/dynadjust/include/io/dynadjust_file.hpp @@ -37,7 +37,7 @@ #include #include -#define __FILE_VERSION__ "1.1" +#define __FILE_VERSION__ "1.2" namespace dynadjust { namespace iostreams { diff --git a/dynadjust/include/math/dnamatrix_contiguous.cpp b/dynadjust/include/math/dnamatrix_contiguous.cpp index 2d66cf738..11fcf01ef 100644 --- a/dynadjust/include/math/dnamatrix_contiguous.cpp +++ b/dynadjust/include/math/dnamatrix_contiguous.cpp @@ -19,7 +19,9 @@ // Description : DynAdjust Matrix library //============================================================================ +#include #include +#include #include #include #include @@ -29,6 +31,11 @@ namespace dynadjust { namespace math { +// BLAS thread count selected during application start-up. +static int g_max_blas_threads = 0; + +void set_max_blas_threads(int n) { g_max_blas_threads = n; } +int get_max_blas_threads() { return g_max_blas_threads; } std::ostream& operator<<(std::ostream& os, const matrix_2d& rhs) { if (os.iword(0) == binary) { @@ -44,18 +51,25 @@ std::ostream& operator<<(std::ostream& os, const matrix_2d& rhs) { os.write(reinterpret_cast(&rhs._mem_rows), sizeof(UINT32)); os.write(reinterpret_cast(&rhs._mem_cols), sizeof(UINT32)); + // Alignment padding — keeps data region at 8-byte aligned offset (24 bytes) + // Must match WriteMappedFileRegion / ReadMappedFileRegion / AttachMappedFileRegion layout + const UINT32 pad = 0; + os.write(reinterpret_cast(&pad), sizeof(UINT32)); + UINT32 c, r; switch (rhs._matrixType) { case mtx_lower: - // output lower triangular part of a square matrix if (rhs._mem_rows != rhs._mem_cols) throw std::runtime_error("matrix_2d operator<< (): Matrix is not square."); - // print each column - for (c = 0; c < rhs._mem_cols; ++c) - os.write(reinterpret_cast(rhs.getelementref(c, c)), (rhs._mem_rows - c) * sizeof(double)); - + if (rhs._packed) { + os.write(reinterpret_cast(rhs._buffer), + matrix_2d::packed_size(rhs._mem_rows) * sizeof(double)); + } else { + for (c = 0; c < rhs._mem_cols; ++c) + os.write(reinterpret_cast(rhs.getelementref(c, c)), (rhs._mem_rows - c) * sizeof(double)); + } break; case mtx_sparse: break; case mtx_full: @@ -108,7 +122,7 @@ void out_of_memory_handler() { } matrix_2d::matrix_2d() - : _mem_cols(0), _mem_rows(0), _cols(0), _rows(0), _buffer(0), _maxvalCol(0), _maxvalRow(0), _matrixType(mtx_full) { + : _mem_cols(0), _mem_rows(0), _cols(0), _rows(0), _buffer(0), _owns_buffer(true), _maxvalCol(0), _maxvalRow(0), _matrixType(mtx_full), _symmetric(false), _packed(false) { std::set_new_handler(out_of_memory_handler); // if this class were to be modified to use templates, each @@ -125,9 +139,12 @@ matrix_2d::matrix_2d(const UINT32& rows, const UINT32& columns) _cols(columns), _rows(rows), _buffer(0), + _owns_buffer(true), _maxvalCol(0), _maxvalRow(0), - _matrixType(mtx_full) { + _matrixType(mtx_full), + _symmetric(false), + _packed(false) { std::set_new_handler(out_of_memory_handler); allocate(_rows, _cols); @@ -140,9 +157,12 @@ matrix_2d::matrix_2d(const UINT32& rows, const UINT32& columns, const double dat _cols(columns), _rows(rows), _buffer(0), + _owns_buffer(true), _maxvalCol(0), _maxvalRow(0), - _matrixType(matrix_type) { + _matrixType(matrix_type), + _symmetric(false), + _packed(false) { std::set_new_handler(out_of_memory_handler); std::stringstream ss; @@ -197,17 +217,26 @@ matrix_2d::matrix_2d(const matrix_2d& newmat) _cols(newmat.columns()), _rows(newmat.rows()), _buffer(0), + _owns_buffer(true), _maxvalCol(newmat.maxvalueCol()), _maxvalRow(newmat.maxvalueRow()), - _matrixType(newmat.matrixType()) { + _matrixType(newmat.matrixType()), + _symmetric(newmat._symmetric), + _packed(newmat._packed) { std::set_new_handler(out_of_memory_handler); - allocate(_mem_rows, _mem_cols); - - const double* ptr = newmat.getbuffer(); - - // copy buffer - memcpy(_buffer, ptr, newmat.buffersize()); + if (_packed) { + std::size_t ps = packed_size(_mem_rows); + _buffer = static_cast(std::malloc(ps * sizeof(double))); + if (!_buffer) throw NetMemoryException("Insufficient memory for packed matrix copy."); + memcpy(_buffer, newmat.getbuffer(), ps * sizeof(double)); + } else { + // Allocate without zeroing — memcpy immediately overwrites entire buffer + std::size_t total_size = static_cast(_mem_rows) * _mem_cols; + _buffer = static_cast(std::malloc(total_size * sizeof(double))); + if (!_buffer) throw NetMemoryException("Insufficient memory for matrix copy."); + memcpy(_buffer, newmat.getbuffer(), total_size * sizeof(double)); + } } matrix_2d::~matrix_2d() { @@ -216,14 +245,21 @@ matrix_2d::~matrix_2d() { } std::size_t matrix_2d::get_size() { + // 8 UINT32s: matrixType, rows, cols, mem_rows, mem_cols, _pad, maxvalRow, maxvalCol + // The padding UINT32 ensures the data region starts at an 8-byte aligned offset (24 bytes) + // from the region base, enabling in-place mmap buffer attachment. size_t size = - (7 * sizeof(UINT32)); // UINT32 _matrixType, _mem_cols, _mem_rows, _cols, _rows, _maxvalRow, _maxvalCol + (8 * sizeof(UINT32)); switch (_matrixType) { case mtx_lower: size += sumOfConsecutiveIntegers(_mem_rows) * sizeof(double); break; case mtx_sparse: break; case mtx_full: - default: size += buffersize(); + default: + if (_packed) + size += packed_size(_mem_rows) * sizeof(double); + else + size += buffersize(); } return size; } @@ -241,7 +277,6 @@ void matrix_2d::ReadMappedFileRegion(void* addr) { switch (_matrixType) { case mtx_sparse: - // _mem_cols and _mem_rows equal _cols and _rows _mem_rows = _rows; _mem_cols = _cols; break; @@ -250,10 +285,22 @@ void matrix_2d::ReadMappedFileRegion(void* addr) { default: _mem_rows = *data_U++; _mem_cols = *data_U++; + ++data_U; // skip alignment padding UINT32 break; } - allocate(_mem_rows, _mem_cols); + if (_matrixType == mtx_lower && _mem_rows == _mem_cols) { + deallocate(); + _packed = true; + _symmetric = true; + std::size_t ps = packed_size(_mem_rows); + _buffer = static_cast(std::calloc(ps, sizeof(double))); + if (!_buffer) throw NetMemoryException("Insufficient memory for packed matrix read."); + } else { + _packed = false; + _symmetric = false; + allocate(_mem_rows, _mem_cols); + } double* data_d; int* data_i; @@ -292,15 +339,22 @@ void matrix_2d::ReadMappedFileRegion(void* addr) { return; break; case mtx_lower: + assert(_mem_rows == _mem_cols && "ReadMappedFileRegion(mtx_lower): matrix must be square"); data_d = reinterpret_cast(data_U); - // read each column - for (c = 0; c < _mem_cols; ++c) { - memcpy(getelementref(c, c), data_d, (_mem_rows - c) * sizeof(double)); - data_d += (_mem_rows - c); + if (_packed) { + std::size_t ps = packed_size(_mem_rows); + memcpy(_buffer, data_d, ps * sizeof(double)); + data_d += ps; + _symmetric = true; + } else { + for (c = 0; c < _mem_cols; ++c) { + memcpy(getbuffer(c, c), data_d, (_mem_rows - c) * sizeof(double)); + data_d += (_mem_rows - c); + } + fillupper(); + _symmetric = false; } - - fillupper(); break; case mtx_full: default: @@ -342,9 +396,32 @@ void matrix_2d::WriteMappedFileRegion(void* addr) { default: *data_U++ = _mem_rows; *data_U++ = _mem_cols; + *data_U++ = 0; // alignment padding break; } + // In-place mmap: data is already in the region, only update footer + if (!_owns_buffer) { + // Calculate footer position by skipping past data + double* data_d; + switch (_matrixType) { + case mtx_lower: + if (_packed) + data_d = reinterpret_cast(data_U) + packed_size(_mem_rows); + else + data_d = reinterpret_cast(data_U) + sumOfConsecutiveIntegers(_mem_rows); + break; + case mtx_full: + default: + data_d = reinterpret_cast(data_U) + static_cast(_mem_rows) * _mem_cols; + break; + } + PUINT32 footer = reinterpret_cast(data_d); + *footer++ = _maxvalRow; + *footer = _maxvalCol; + return; + } + double* data_d; int* data_i; @@ -384,10 +461,15 @@ void matrix_2d::WriteMappedFileRegion(void* addr) { case mtx_lower: data_d = reinterpret_cast(data_U); - // print each column - for (c = 0; c < _mem_cols; ++c) { - memcpy(data_d, getbuffer(c, c), (_mem_rows - c) * sizeof(double)); - data_d += (_mem_rows - c); + if (_packed) { + std::size_t ps = packed_size(_mem_rows); + memcpy(data_d, _buffer, ps * sizeof(double)); + data_d += ps; + } else { + for (c = 0; c < _mem_cols; ++c) { + memcpy(data_d, getbuffer(c, c), (_mem_rows - c) * sizeof(double)); + data_d += (_mem_rows - c); + } } break; case mtx_full: @@ -408,16 +490,115 @@ void matrix_2d::WriteMappedFileRegion(void* addr) { *data_U = _maxvalCol; } -void matrix_2d::allocate() { allocate(_mem_rows, _mem_cols); } -// creates memory for desired "memory size", not matrix dimensions -void matrix_2d::allocate(const UINT32& rows, const UINT32& columns) { - //_method_ = "allocate"; +void matrix_2d::AttachMappedFileRegion(void* addr) { + // Read header metadata — same layout as ReadMappedFileRegion + PUINT32 data_U = reinterpret_cast(addr); + _matrixType = *data_U++; + _rows = *data_U++; + _cols = *data_U++; + switch (_matrixType) { + case mtx_sparse: + _mem_rows = _rows; + _mem_cols = _cols; + // Sparse cannot use in-place — fall back to copy path + ReadMappedFileRegion(addr); + return; + case mtx_lower: + case mtx_full: + default: + _mem_rows = *data_U++; + _mem_cols = *data_U++; + ++data_U; // skip alignment padding + break; + } + + // Release any existing buffer deallocate(); - // an exception will be thrown by out_of_memory_handler - // if memory cannot be allocated + // Point _buffer directly at the data region in the mmap + _buffer = reinterpret_cast(data_U); + _owns_buffer = false; + + // Set packed/symmetric flags for lower-triangular matrices + if (_matrixType == mtx_lower && _mem_rows == _mem_cols) { + _packed = true; + _symmetric = true; + } else { + _packed = false; + } + + // Read footer (maxvalRow, maxvalCol) from after the data + double* data_d; + switch (_matrixType) { + case mtx_lower: + data_d = _buffer + packed_size(_mem_rows); + break; + case mtx_full: + default: + data_d = _buffer + static_cast(_mem_rows) * _mem_cols; + break; + } + + PUINT32 footer = reinterpret_cast(data_d); + _maxvalRow = *footer++; + _maxvalCol = *footer; +} + + +void matrix_2d::DetachMappedFileRegion(void* addr) { + if (_owns_buffer || _buffer == nullptr) + return; + + // Write updated footer (maxvalRow, maxvalCol) to the mmap region. + // Header: 6 UINT32s (matrixType, rows, cols, mem_rows, mem_cols, pad) = 24 bytes. + // Skip past data to find the footer position. + double* data_d; + switch (_matrixType) { + case mtx_lower: + data_d = _buffer + packed_size(_mem_rows); + break; + case mtx_full: + default: + data_d = _buffer + static_cast(_mem_rows) * _mem_cols; + break; + } + + PUINT32 footer = reinterpret_cast(data_d); + *footer++ = _maxvalRow; + *footer = _maxvalCol; + + // Detach without freeing the mmap memory + _buffer = nullptr; + _owns_buffer = true; +} + + +void matrix_2d::allocate() { + if (_matrixType == mtx_lower && _mem_rows == _mem_cols && _mem_rows > 0) { + deallocate(); + _packed = true; + _symmetric = true; + std::size_t ps = packed_size(_mem_rows); + _buffer = static_cast(std::calloc(ps, sizeof(double))); + if (!_buffer) { + std::stringstream ss; + ss << "Insufficient memory for a packed " << _mem_rows << " x " << _mem_rows << " matrix."; + throw NetMemoryException(ss.str()); + } + return; + } + _packed = false; + _symmetric = false; + allocate(_mem_rows, _mem_cols); +} + +// creates memory for desired "memory size", not matrix dimensions +void matrix_2d::allocate(const UINT32& rows, const UINT32& columns) { + deallocate(); + _packed = false; + _symmetric = false; buy(rows, columns, &_buffer); } @@ -429,26 +610,27 @@ void matrix_2d::buy(const UINT32& rows, const UINT32& columns, double** mem_spac __row__ = rows; __col__ = columns; - // an exception will be thrown by out_of_memory_handler - // if memory cannot be allocated + // calloc returns zero-initialised memory. For large allocations glibc + // satisfies calloc via mmap, whose anonymous pages are already zero-filled + // by the kernel — so calloc skips the redundant memset that was previously + // triggering page-fault storms (97 % of CPU time on the NSW benchmark). std::size_t total_size = static_cast(rows) * static_cast(columns); - (*mem_space) = new double[total_size]; + (*mem_space) = static_cast(std::calloc(total_size, sizeof(double))); if ((*mem_space) == nullptr) { std::stringstream ss; ss << "Insufficient memory for a " << rows << " x " << columns << " matrix."; throw NetMemoryException(ss.str()); } - - // Initialize memory to zero to prevent uninitialized values - std::memset((*mem_space), 0, total_size * sizeof(double)); } void matrix_2d::deallocate() { if (_buffer != nullptr) { - delete[] _buffer; + if (_owns_buffer) + std::free(_buffer); _buffer = nullptr; } + _owns_buffer = true; } matrix_2d matrix_2d::submatrix(const UINT32& row_begin, const UINT32& col_begin, const UINT32& rows, @@ -514,9 +696,14 @@ void matrix_2d::submatrix(const UINT32& row_begin, const UINT32& col_begin, matr } void matrix_2d::redim(const UINT32& rows, const UINT32& columns) { + if (_packed) { + deallocate(); + _packed = false; + } + _symmetric = false; // if new matrix size is smaller than or equal to the previous // matrix size, then simply change dimensions and return - if (rows <= _mem_rows && columns <= _mem_cols) { + if (_buffer != nullptr && rows <= _mem_rows && columns <= _mem_cols) { // Zero out the unused portions when reusing buffer // Zero partial columns (rows beyond new row count) for (UINT32 col = 0; col < columns && col < _mem_cols; ++col) { @@ -550,23 +737,46 @@ void matrix_2d::redim(const UINT32& rows, const UINT32& columns) { buy(rows, columns, &new_buffer); // Copy old data to new buffer if there was any + bool old_owns = _owns_buffer; if (old_buffer != nullptr && old_rows > 0 && old_cols > 0) { for (UINT32 col = 0; col < old_cols && col < columns; ++col) { for (UINT32 row = 0; row < old_rows && row < rows; ++row) { new_buffer[col * rows + row] = old_buffer[col * old_rows + row]; } } - // Delete old buffer - delete[] old_buffer; + if (old_owns) + std::free(old_buffer); } _buffer = new_buffer; - + _owns_buffer = true; _rows = _mem_rows = rows; _cols = _mem_cols = columns; } +void matrix_2d::redim_packed(const UINT32& n) { + std::size_t ps = packed_size(n); + + if (_buffer != nullptr && _packed && n <= _mem_rows) { + std::memset(_buffer, 0, ps * sizeof(double)); + _rows = _cols = _mem_rows = _mem_cols = n; + return; + } + + deallocate(); + _rows = _cols = _mem_rows = _mem_cols = n; + _packed = true; + _symmetric = true; + _matrixType = mtx_lower; + _buffer = static_cast(std::calloc(ps, sizeof(double))); + if (!_buffer) { + std::stringstream ss; + ss << "Insufficient memory for a packed " << n << " x " << n << " matrix."; + throw NetMemoryException(ss.str()); + } +} + void matrix_2d::shrink(const UINT32& rows, const UINT32& columns) { if (rows > _rows || columns > _cols) { std::stringstream ss; @@ -601,6 +811,8 @@ void matrix_2d::grow(const UINT32& rows, const UINT32& columns) { void matrix_2d::setsize(const UINT32& rows, const UINT32& columns) { deallocate(); + _packed = false; + _symmetric = false; _rows = _mem_rows = rows; _cols = _mem_cols = columns; } @@ -641,21 +853,53 @@ void matrix_2d::copybuffer(const UINT32& rowstart, const UINT32& columnstart, co } } -void matrix_2d::copyelements(const UINT32& row_dest, const UINT32& column_dest, const matrix_2d& src, - const UINT32& row_src, const UINT32& column_src, const UINT32& rows, - const UINT32& columns) { +void matrix_2d::copyelements_generic(const UINT32& row_dest, const UINT32& column_dest, const matrix_2d& src, + const UINT32& row_src, const UINT32& column_src, const UINT32& rows, + const UINT32& columns) { + assert(_buffer != nullptr && src._buffer != nullptr); + assert(row_dest + rows <= _mem_rows && "copyelements_generic(): dest row overflow"); + assert(column_dest + columns <= _mem_cols && "copyelements_generic(): dest col overflow"); + assert(row_src + rows <= src._mem_rows && "copyelements_generic(): src row overflow"); + assert(column_src + columns <= src._mem_cols && "copyelements_generic(): src col overflow"); + // Fast path: packed source → dense dest (avoids per-element get/put overhead) + if (src._packed && !_packed) { + for (UINT32 c = 0; c < columns; ++c) { + double* d = _buffer + static_cast(column_dest + c) * _mem_rows + row_dest; + UINT32 sc = column_src + c; + for (UINT32 r = 0; r < rows; ++r) { + UINT32 sr = row_src + r, sj = sc; + if (sr < sj) std::swap(sr, sj); + d[r] = src._buffer[packed_index(src._rows, sr, sj)]; + } + } + return; + } + // Fast path: dense source → packed dest + if (_packed && !src._packed && !src._symmetric) { + for (UINT32 c = 0; c < columns; ++c) { + const double* s = src._buffer + static_cast(column_src + c) * src._mem_rows + row_src; + UINT32 dc = column_dest + c; + for (UINT32 r = 0; r < rows; ++r) { + UINT32 dr = row_dest + r; + if (dr >= dc) + _buffer[packed_index(_rows, dr, dc)] = s[r]; + } + } + return; + } + // Fallback for remaining packed/symmetric combinations + if (src._symmetric || src._packed || _packed) { + for (UINT32 r = 0; r < rows; ++r) + for (UINT32 c = 0; c < columns; ++c) + put(row_dest + r, column_dest + c, src.get(row_src + r, column_src + c)); + return; + } UINT32 cd(0), cs(0), colend_dest(column_dest + columns); for (cd = column_dest, cs = column_src; cd < colend_dest; ++cd, ++cs) memcpy(getelementref(row_dest, cd), src.getbuffer(row_src, cs), static_cast(rows) * sizeof(double)); } -void matrix_2d::copyelements(const UINT32& row_dest, const UINT32& column_dest, const matrix_2d* src, - const UINT32& row_src, const UINT32& column_src, const UINT32& rows, - const UINT32& columns) { - copyelements(row_dest, column_dest, *src, row_src, column_src, rows, columns); -} - void matrix_2d::sweep(UINT32 k1, UINT32 k2) { double eps(1.0e-8), d; UINT32 i, j, k, it; @@ -705,10 +949,47 @@ matrix_2d matrix_2d::sweepinverse() { return *this; } -matrix_2d matrix_2d::cholesky_inverse(bool LOWER_IS_CLEARED /*=false*/) { +matrix_2d matrix_2d::cholesky_inverse(bool LOWER_IS_CLEARED /*=false*/, bool mark_symmetric /*=false*/) { if (_rows < 1) return *this; if (_rows != _cols) throw std::runtime_error("cholesky_inverse(): Matrix is not square."); + assert(_buffer != nullptr && "cholesky_inverse(): null buffer"); + assert(_mem_rows >= _rows && "cholesky_inverse(): mem_rows < rows"); + assert(_mem_cols >= _cols && "cholesky_inverse(): mem_cols < cols"); + assert(!(mark_symmetric && LOWER_IS_CLEARED) && + "cholesky_inverse(): mark_symmetric with LOWER_IS_CLEARED not supported"); + + if (_packed) { + // Unpack to full format for fast blocked dpotrf/dpotri, then repack. + // dpptrf/dpptri are element-by-element and orders of magnitude slower. + lapack_int n = _rows; + std::size_t full_size = static_cast(n) * n; + + // Reuse thread-local workspace to avoid repeated allocation/deallocation. + // dpotrf/dpotri with uplo='L' only read the lower triangle, so no + // memset is needed — the unpack loop sets all lower-triangle elements. + static thread_local std::vector chol_workspace; + if (chol_workspace.size() < full_size) + chol_workspace.resize(full_size); + double* full = chol_workspace.data(); + + for (UINT32 j = 0; j < _rows; ++j) + for (UINT32 i = j; i < _rows; ++i) + full[static_cast(j) * n + i] = _buffer[packed_index(_rows, i, j)]; + + char uplo = LOWER_TRIANGLE; + lapack_int info, lda = n; + LAPACK_FUNC(dpotrf)(&uplo, &n, full, &lda, &info); + if (info != 0) throw MatrixInversionFailure("Matrix inversion failed, the matrix is singular."); + LAPACK_FUNC(dpotri)(&uplo, &n, full, &lda, &info); + if (info != 0) throw MatrixInversionFailure("Matrix inversion failed, the matrix is singular."); + + for (UINT32 j = 0; j < _rows; ++j) + for (UINT32 i = j; i < _rows; ++i) + _buffer[packed_index(_rows, i, j)] = full[static_cast(j) * n + i]; + return *this; + } + char uplo(LOWER_TRIANGLE); if (LOWER_IS_CLEARED) uplo = UPPER_TRIANGLE; @@ -727,23 +1008,181 @@ matrix_2d matrix_2d::cholesky_inverse(bool LOWER_IS_CLEARED /*=false*/) { if (info != 0) throw MatrixInversionFailure("Matrix inversion failed, the matrix is singular."); - if (LOWER_IS_CLEARED) + if (mark_symmetric) { + _symmetric = true; + } else if (LOWER_IS_CLEARED) { filllower(); - else + } else { fillupper(); + } + + return *this; +} + +matrix_2d matrix_2d::cholesky_factor(bool LOWER_IS_CLEARED /*=false*/) { + if (_rows < 1) return *this; + if (_rows != _cols) throw std::runtime_error("cholesky_factor(): Matrix is not square."); + + assert(_buffer != nullptr && "cholesky_factor(): null buffer"); + assert(_mem_rows >= _rows && "cholesky_factor(): mem_rows < rows"); + assert(_mem_cols >= _cols && "cholesky_factor(): mem_cols < cols"); + + if (_packed) { + // Unpack to full format for dpotrf (same approach as cholesky_inverse). + // The factor L is stored back in packed format. + lapack_int n = _rows; + std::size_t full_size = static_cast(n) * n; + + // Reuse thread-local workspace; no memset needed (dpotrf reads lower triangle only) + static thread_local std::vector chol_workspace; + if (chol_workspace.size() < full_size) + chol_workspace.resize(full_size); + double* full = chol_workspace.data(); + + for (UINT32 j = 0; j < _rows; ++j) + for (UINT32 i = j; i < _rows; ++i) + full[static_cast(j) * n + i] = _buffer[packed_index(_rows, i, j)]; + + char uplo = LOWER_TRIANGLE; + lapack_int info, lda = n; + LAPACK_FUNC(dpotrf)(&uplo, &n, full, &lda, &info); + if (info != 0) throw MatrixInversionFailure("Cholesky factorisation failed, the matrix is singular."); + + // Store the L factor back in packed format + for (UINT32 j = 0; j < _rows; ++j) + for (UINT32 i = j; i < _rows; ++i) + _buffer[packed_index(_rows, i, j)] = full[static_cast(j) * n + i]; + return *this; + } + + char uplo(LOWER_TRIANGLE); + if (LOWER_IS_CLEARED) uplo = UPPER_TRIANGLE; + + lapack_int info, n = _rows; + lapack_int lda = _mem_rows; + + LAPACK_FUNC(dpotrf)(&uplo, &n, _buffer, &lda, &info); + + if (info != 0) + throw MatrixInversionFailure("Cholesky factorisation failed, the matrix is singular."); return *this; } +void matrix_2d::cholesky_solve(matrix_2d& rhs, bool LOWER_IS_CLEARED /*=false*/) { + assert(_rows == _cols && "cholesky_solve(): factor matrix is not square"); + assert(_rows == rhs._rows && "cholesky_solve(): dimension mismatch"); + assert(_buffer != nullptr && "cholesky_solve(): null factor buffer"); + assert(rhs._buffer != nullptr && "cholesky_solve(): null rhs buffer"); + + if (_packed) { + // Unpack the L factor to full format for dpotrs. + lapack_int n = _rows; + std::size_t full_size = static_cast(n) * n; + + // Reuse thread-local workspace; no memset needed (dpotrs reads lower triangle only) + static thread_local std::vector chol_workspace; + if (chol_workspace.size() < full_size) + chol_workspace.resize(full_size); + double* full = chol_workspace.data(); + + for (UINT32 j = 0; j < _rows; ++j) + for (UINT32 i = j; i < _rows; ++i) + full[static_cast(j) * n + i] = _buffer[packed_index(_rows, i, j)]; + + char uplo = LOWER_TRIANGLE; + lapack_int nrhs = rhs._cols; + lapack_int lda = n; + lapack_int ldb = rhs._mem_rows; + lapack_int info; + + LAPACK_FUNC(dpotrs)(&uplo, &n, &nrhs, full, &lda, rhs._buffer, &ldb, &info); + + if (info != 0) + throw MatrixInversionFailure("Cholesky solve failed."); + return; + } + + char uplo(LOWER_TRIANGLE); + if (LOWER_IS_CLEARED) uplo = UPPER_TRIANGLE; + + lapack_int n = _rows; + lapack_int nrhs = rhs._cols; + lapack_int lda = _mem_rows; + lapack_int ldb = rhs._mem_rows; + lapack_int info; + + LAPACK_FUNC(dpotrs)(&uplo, &n, &nrhs, _buffer, &lda, rhs._buffer, &ldb, &info); + + if (info != 0) + throw MatrixInversionFailure("Cholesky solve failed."); +} + +double matrix_2d::dot(const matrix_2d& other) const { + assert(_cols == 1 && other._cols == 1 && "dot(): both matrices must be column vectors"); + assert(_rows == other._rows && "dot(): dimension mismatch"); + assert(_buffer != nullptr && other._buffer != nullptr && "dot(): null buffer"); + + double result = 0.0; + for (UINT32 i = 0; i < _rows; ++i) + result += get(i, 0) * other.get(i, 0); + return result; +} + matrix_2d matrix_2d::scale(const double& scalar) { + if (_packed) { + std::size_t ps = packed_size(_rows); + for (std::size_t k = 0; k < ps; ++k) + _buffer[k] *= scalar; + return *this; + } UINT32 i, j; for (i = 0; i < _rows; ++i) for (j = 0; j < _cols; ++j) *getelementref(i, j) *= scalar; return *this; } -void matrix_2d::blockadd(const UINT32& row_dest, const UINT32& col_dest, const matrix_2d& mat_src, - const UINT32& row_src, const UINT32& col_src, const UINT32& rows, const UINT32& cols) { +void matrix_2d::scale_symmetric_diagonal(const double* diag) { + assert(_symmetric && "scale_symmetric_diagonal(): matrix must be symmetric"); + if (_packed) { + for (UINT32 j = 0; j < _rows; ++j) + for (UINT32 i = j; i < _rows; ++i) + _buffer[packed_index(_rows, i, j)] *= diag[i] * diag[j]; + return; + } + for (UINT32 j = 0; j < _rows; ++j) + for (UINT32 i = j; i < _rows; ++i) { + double s = diag[i] * diag[j]; + *getelementref(i, j) *= s; + } +} + +void matrix_2d::blockadd_generic(const UINT32& row_dest, const UINT32& col_dest, const matrix_2d& mat_src, + const UINT32& row_src, const UINT32& col_src, const UINT32& rows, const UINT32& cols) { + // Fast path: dense source → packed dest (avoids per-element get/elementadd overhead) + if (_packed && !mat_src._packed && !mat_src._symmetric) { + for (UINT32 c = 0; c < cols; ++c) { + const double* s = mat_src._buffer + static_cast(col_src + c) * mat_src._mem_rows + row_src; + UINT32 dc = col_dest + c; + for (UINT32 r = 0; r < rows; ++r) { + UINT32 dr = row_dest + r; + if (dr >= dc) + _buffer[packed_index(_rows, dr, dc)] += s[r]; + } + } + return; + } + // Fast path: both dense (non-packed, non-symmetric) — use direct buffer arithmetic + if (!_packed && !mat_src._packed && !mat_src._symmetric) { + for (UINT32 c = 0; c < cols; ++c) { + double* d = _buffer + static_cast(col_dest + c) * _mem_rows + row_dest; + const double* s = mat_src._buffer + static_cast(col_src + c) * mat_src._mem_rows + row_src; + for (UINT32 r = 0; r < rows; ++r) + d[r] += s[r]; + } + return; + } + // Fallback: generic per-element path UINT32 i_dest, j_dest, i_src, j_src; UINT32 i_dest_end(row_dest + rows), j_dest_end(col_dest + cols); @@ -752,9 +1191,8 @@ void matrix_2d::blockadd(const UINT32& row_dest, const UINT32& col_dest, const m elementadd(i_dest, j_dest, mat_src.get(i_src, j_src)); } -// Same as blockadd, but adds transpose. mat_src must be square. -void matrix_2d::blockTadd(const UINT32& row_dest, const UINT32& col_dest, const matrix_2d& mat_src, - const UINT32& row_src, const UINT32& col_src, const UINT32& rows, const UINT32& cols) { +void matrix_2d::blockTadd_generic(const UINT32& row_dest, const UINT32& col_dest, const matrix_2d& mat_src, + const UINT32& row_src, const UINT32& col_src, const UINT32& rows, const UINT32& cols) { UINT32 i_dest, j_dest, i_src, j_src; UINT32 i_dest_end(row_dest + rows), j_dest_end(col_dest + cols); @@ -773,79 +1211,153 @@ void matrix_2d::blocksubtract(const UINT32& row_dest, const UINT32& col_dest, co elementsubtract(i_dest, j_dest, mat_src.get(i_src, j_src)); } -// clearlower() void matrix_2d::clearlower() { + assert(_buffer != nullptr); + assert(!_packed && "clearlower(): not valid for packed storage"); // Sets lower triangle elements to zero UINT32 col, row; - for (row = 1, col = 0; col < _mem_cols; ++col, ++row) + for (row = 1, col = 0; col < _mem_cols && row < _mem_rows; ++col, ++row) memset(getelementref(row, col), 0, (static_cast(_mem_rows) - row) * sizeof(double)); } -// clearupper() void matrix_2d::clearupper() { + assert(!_packed && "clearupper(): not valid for packed storage"); // Sets upper triangle elements to zero - UINT32 col, row; - for (row = 0; row < _rows; ++row) - for (col = row + 1; col < _cols; ++col) put(row, col, 0.0); + // Column-major: for each column, zero the rows above the diagonal + for (UINT32 col = 1; col < _cols; ++col) + memset(_buffer + col * _mem_rows, 0, col * sizeof(double)); } -// filllower() void matrix_2d::filllower() { - // copies upper triangle to lower triangle - UINT32 column, row; - for (row = 1; row < _rows; row++) - for (column = 0; column < row; column++) put(row, column, get(column, row)); + assert(_buffer != nullptr); + assert(!_packed && "filllower(): not valid for packed storage"); + assert(_rows == _cols && "filllower(): matrix must be square"); + // Copy upper triangle to lower: A(row,col) = A(col,row) for row > col + // Column-major: A(r,c) = _buffer[c * _mem_rows + r] + // Outer loop over destination columns so writes are sequential in memory. + const std::size_t mr = _mem_rows; + constexpr UINT32 BLK = 64; + for (UINT32 cb = 0; cb < _cols; cb += BLK) { + UINT32 ce = std::min(cb + BLK, _cols); + for (UINT32 rb = ce; rb < _rows; rb += BLK) { + UINT32 re = std::min(rb + BLK, _rows); + for (UINT32 col = cb; col < ce; col++) { + double* dst = _buffer + col * mr; + for (UINT32 row = rb; row < re; row++) + dst[row] = _buffer[row * mr + col]; + } + } + } + // Diagonal blocks: row > col within the same block + for (UINT32 cb = 0; cb < _cols; cb += BLK) { + UINT32 ce = std::min(cb + BLK, _cols); + for (UINT32 col = cb; col < ce; col++) { + double* dst = _buffer + col * mr; + for (UINT32 row = col + 1; row < ce; row++) + dst[row] = _buffer[row * mr + col]; + } + } } -// fillupper() void matrix_2d::fillupper() { - // copies lower triangle to upper triangle - UINT32 column, row; - for (row = 1; row < _rows; row++) - for (column = 0; column < row; column++) put(column, row, get(row, column)); + assert(_buffer != nullptr); + assert(!_packed && "fillupper(): not valid for packed storage"); + assert(_rows == _cols && "fillupper(): matrix must be square"); + // Copy lower triangle to upper: A(r,c) = A(c,r) for r < c + // Column-major: A(r,c) = _buffer[c * _mem_rows + r] + // + // Outer loop over destination columns so writes are sequential in memory. + // Reads are strided (one element per source column) but write-combining + // and store buffers make sequential writes much faster than sequential reads + // for large matrices. + const std::size_t mr = _mem_rows; + constexpr UINT32 BLK = 64; + for (UINT32 cb = 0; cb < _cols; cb += BLK) { + UINT32 ce = std::min(cb + BLK, _cols); + for (UINT32 rb = 0; rb < ce; rb += BLK) { + UINT32 re = std::min(rb + BLK, ce); + for (UINT32 col = cb; col < ce; col++) { + double* dst = _buffer + col * mr; + UINT32 r0 = rb; + UINT32 r1 = std::min(re, col); + for (UINT32 row = r0; row < r1; row++) + dst[row] = _buffer[row * mr + col]; + } + } + } } -// zero() -void matrix_2d::zero() { memset(_buffer, 0, buffersize()); } +void matrix_2d::zero() { + assert(_buffer != nullptr && "zero(): null buffer"); + if (_packed) + memset(_buffer, 0, packed_size(_mem_rows) * sizeof(double)); + else + memset(_buffer, 0, buffersize()); +} // zero() void matrix_2d::zero(const UINT32& row_begin, const UINT32& col_begin, const UINT32& rows, const UINT32& columns) { + assert(_buffer != nullptr && "zero(sub): null buffer"); + assert(row_begin + rows <= _mem_rows && "zero(sub): row overflow"); + assert(col_begin + columns <= _mem_cols && "zero(sub): col overflow"); UINT32 col(0), col_end(col_begin + columns); for (col = col_begin; col < col_end; ++col) memset(getelementref(row_begin, col), 0, rows * sizeof(double)); } matrix_2d& matrix_2d::operator=(const matrix_2d& rhs) { - // Overloaded assignment operator if (this == &rhs) return *this; - // If rhs data can fit within limits of this matrix, copy - // and return. Otherwise, allocate new memory + if (rhs._packed) { + std::size_t ps = packed_size(rhs._rows); + if (_packed && _mem_rows >= rhs._rows) { + _rows = _cols = rhs._rows; + memcpy(_buffer, rhs._buffer, ps * sizeof(double)); + } else { + deallocate(); + _rows = _cols = _mem_rows = _mem_cols = rhs._rows; + _buffer = static_cast(std::malloc(ps * sizeof(double))); + if (!_buffer) throw NetMemoryException("Insufficient memory for packed matrix assignment."); + memcpy(_buffer, rhs._buffer, ps * sizeof(double)); + } + _packed = true; + _symmetric = true; + _matrixType = rhs._matrixType; + _maxvalCol = rhs.maxvalueCol(); + _maxvalRow = rhs.maxvalueRow(); + return *this; + } + + // rhs is not packed + if (_packed) { + deallocate(); + _packed = false; + _mem_rows = _mem_cols = 0; + } + if (_mem_rows >= rhs.rows() && _mem_cols >= rhs.columns()) { - // don't change _mem_rows or _mem_cols. Simply update - // visible dimensions and copy buffer _rows = rhs.rows(); _cols = rhs.columns(); copybuffer(_rows, _cols, rhs); - _maxvalCol = rhs.maxvalueCol(); // col of max value - _maxvalRow = rhs.maxvalueRow(); // row of max value + _maxvalCol = rhs.maxvalueCol(); + _maxvalRow = rhs.maxvalueRow(); + _symmetric = rhs._symmetric; return *this; } - // Okay, rhs is larger, so allocate new memory. Call free - // memory first before changing row and column dimensions! deallocate(); - _mem_rows = rhs.memRows(); // change memory limits + _mem_rows = rhs.memRows(); _mem_cols = rhs.memColumns(); - _rows = rhs.rows(); // change matrix dimensions + _rows = rhs.rows(); _cols = rhs.columns(); allocate(_mem_rows, _mem_cols); copybuffer(_rows, _cols, rhs); - _maxvalCol = rhs.maxvalueCol(); // col of max value - _maxvalRow = rhs.maxvalueRow(); // row of max value + _maxvalCol = rhs.maxvalueCol(); + _maxvalRow = rhs.maxvalueRow(); + _symmetric = rhs._symmetric; return *this; } @@ -875,6 +1387,10 @@ matrix_2d matrix_2d::add(const matrix_2d& rhs) { // multiplies this matrix by rhs and stores the result in a new matrix // Uses Intel MKL dgemm matrix_2d matrix_2d::multiply(const char* lhs_trans, const matrix_2d& rhs, const char* rhs_trans) { + assert(!_symmetric && "multiply(dgemm): LHS is symmetric — use multiply_sym instead"); + assert(!rhs._symmetric && "multiply(dgemm): RHS is symmetric — upper triangle is unpopulated"); + assert(_buffer != nullptr && rhs._buffer != nullptr); + matrix_2d m(_rows, rhs.columns()); const double one = 1.0; @@ -914,6 +1430,10 @@ matrix_2d matrix_2d::multiply(const char* lhs_trans, const matrix_2d& rhs, const // Uses Intel MKL dgemm matrix_2d matrix_2d::multiply(const matrix_2d& lhs, const char* lhs_trans, const matrix_2d& rhs, const char* rhs_trans) { + assert(!lhs._symmetric && "multiply(dgemm): LHS is symmetric — use multiply_sym instead"); + assert(!rhs._symmetric && "multiply(dgemm): RHS is symmetric — upper triangle is unpopulated"); + assert(lhs._buffer != nullptr && rhs._buffer != nullptr && _buffer != nullptr); + const double one = 1.0; const double zero = 0.0; @@ -947,6 +1467,48 @@ matrix_2d::multiply(const matrix_2d& lhs, const char* lhs_trans, const matrix_2d return *this; } // Multiply() +// C (this) = sym_lhs * rhs, using dsymm or dspmv (packed) +matrix_2d matrix_2d::multiply_sym(const matrix_2d& sym_lhs, const matrix_2d& rhs) { + lapack_int m = sym_lhs.rows(); + lapack_int n = rhs.columns(); + + assert(sym_lhs._symmetric && "multiply_sym(): LHS must be marked symmetric"); + assert(sym_lhs._buffer != nullptr && "multiply_sym(): LHS null buffer"); + assert(rhs._buffer != nullptr && "multiply_sym(): RHS null buffer"); + assert(_buffer != nullptr && "multiply_sym(): result null buffer"); + assert(_buffer != sym_lhs._buffer && _buffer != rhs._buffer && + "multiply_sym(): result must not alias inputs"); + + if (sym_lhs.columns() != sym_lhs.rows()) + throw std::runtime_error("multiply_sym(): LHS matrix is not square."); + if (static_cast(rhs.rows()) != m) + throw std::runtime_error("multiply_sym(): Matrix dimensions are incompatible."); + if (_rows != static_cast(m) || _cols != static_cast(n)) + throw std::runtime_error("multiply_sym(): Result matrix dimensions are incompatible."); + + if (sym_lhs._packed) { + for (lapack_int j = 0; j < n; ++j) { + BLAS_FUNC(dspmv)(CblasColMajor, CblasLower, + m, 1.0, sym_lhs._buffer, + rhs._buffer + j * rhs._mem_rows, 1, + 0.0, _buffer + j * _mem_rows, 1); + } + return *this; + } + + assert(sym_lhs.memRows() >= sym_lhs.rows() && "multiply_sym(): LHS LDA < M"); + assert(rhs.memRows() >= rhs.rows() && "multiply_sym(): RHS LDA < M"); + assert(_mem_rows >= _rows && "multiply_sym(): result LDA < M"); + + BLAS_FUNC(dsymm)(CblasColMajor, CblasLeft, CblasLower, + m, n, 1.0, + sym_lhs.getbuffer(), sym_lhs.memRows(), + rhs.getbuffer(), rhs.memRows(), + 0.0, _buffer, _mem_rows); + + return *this; +} + // Transpose() matrix_2d matrix_2d::transpose(const matrix_2d& matA) { if ((matA.columns() != _rows) || (matA.rows() != _cols)) @@ -967,9 +1529,19 @@ matrix_2d matrix_2d::transpose() { return m; } // Transpose() -// computes and retains the maximum value in the matrix double matrix_2d::compute_maximum_value() { _maxvalCol = _maxvalRow = 0; + if (_packed) { + for (UINT32 j = 0; j < _rows; ++j) { + for (UINT32 i = j; i < _rows; ++i) { + if (fabs(get(i, j)) > fabs(get(_maxvalRow, _maxvalCol))) { + _maxvalRow = i; + _maxvalCol = j; + } + } + } + return get(_maxvalRow, _maxvalCol); + } UINT32 col, row; for (row = 0; row < _rows; ++row) { for (col = 0; col < _cols; col++) { diff --git a/dynadjust/include/math/dnamatrix_contiguous.hpp b/dynadjust/include/math/dnamatrix_contiguous.hpp index be4d79c85..c79fea5d9 100644 --- a/dynadjust/include/math/dnamatrix_contiguous.hpp +++ b/dynadjust/include/math/dnamatrix_contiguous.hpp @@ -23,6 +23,7 @@ #define DNAMATRIX_CONTIGUOUS_H_ /// \cond +#include #include /// \endcond @@ -165,14 +166,26 @@ static_assert(sizeof(lapack_int) == 4, "LP64 interface requires 32-bit integers" extern "C" { void LAPACK_FUNC(dpotrf)(const char* uplo, const lapack_int* n, double* a, const lapack_int* lda, lapack_int* info); void LAPACK_FUNC(dpotri)(const char* uplo, const lapack_int* n, double* a, const lapack_int* lda, lapack_int* info); +void LAPACK_FUNC(dpptrf)(const char* uplo, const lapack_int* n, double* ap, lapack_int* info); +void LAPACK_FUNC(dpptri)(const char* uplo, const lapack_int* n, double* ap, lapack_int* info); void LAPACK_FUNC(dsytrf)(const char* uplo, const lapack_int* n, double* a, const lapack_int* lda, lapack_int* ipiv, double* work, const lapack_int* lwork, lapack_int* info); void LAPACK_FUNC(dsytri)(const char* uplo, const lapack_int* n, double* a, const lapack_int* lda, const lapack_int* ipiv, double* work, lapack_int* info); +void LAPACK_FUNC(dpotrs)(const char* uplo, const lapack_int* n, const lapack_int* nrhs, + const double* a, const lapack_int* lda, double* b, const lapack_int* ldb, lapack_int* info); void BLAS_FUNC(dgemm)(const enum CBLAS_ORDER ORDER, const enum CBLAS_TRANSPOSE TRANSA, const enum CBLAS_TRANSPOSE TRANSB, const lapack_int M, const lapack_int N, const lapack_int K, const double ALPHA, const double* A, const lapack_int LDA, const double* B, const lapack_int LDB, const double BETA, double* C, const lapack_int LDC); +void BLAS_FUNC(dsymm)(const enum CBLAS_ORDER ORDER, const enum CBLAS_SIDE SIDE, const enum CBLAS_UPLO UPLO, + const lapack_int M, const lapack_int N, + const double ALPHA, const double* A, const lapack_int LDA, const double* B, const lapack_int LDB, + const double BETA, double* C, const lapack_int LDC); +void BLAS_FUNC(dspmv)(const enum CBLAS_ORDER ORDER, const enum CBLAS_UPLO UPLO, + const lapack_int N, const double ALPHA, const double* AP, + const double* X, const lapack_int INCX, + const double BETA, double* Y, const lapack_int INCY); } #endif @@ -187,6 +200,10 @@ class MatrixInversionFailure : public std::runtime_error { using std::runtime_error::runtime_error; }; +// Set/get the maximum BLAS thread count used by matrix operations. +void set_max_blas_threads(int n); +int get_max_blas_threads(); + class matrix_2d; typedef std::vector v_mat_2d, *pv_mat_2d; typedef v_mat_2d::iterator _it_v_mat_2d; @@ -217,12 +234,62 @@ class matrix_2d : public new_handler_support { inline UINT32 columns() const { return _cols; } inline double* getbuffer() const { return _buffer; } + inline double dense_get(const UINT32& row, const UINT32& column) const { + assert(_buffer != nullptr); + assert(!_packed && "dense_get(): matrix must not use packed storage"); + assert(!_symmetric && "dense_get(): matrix must not use symmetric element redirection"); + assert(row < _rows && "dense_get(): row out of bounds"); + assert(column < _cols && "dense_get(): column out of bounds"); + return _buffer[static_cast(column) * _mem_rows + row]; + } + + inline void dense_put(const UINT32& row, const UINT32& column, const double& value) { + assert(_buffer != nullptr); + assert(!_packed && "dense_put(): matrix must not use packed storage"); + assert(!_symmetric && "dense_put(): matrix must not use symmetric element redirection"); + assert(row < _rows && "dense_put(): row out of bounds"); + assert(column < _cols && "dense_put(): column out of bounds"); + _buffer[static_cast(column) * _mem_rows + row] = value; + } + + inline void dense_add(const UINT32& row, const UINT32& column, const double& increment) { + assert(_buffer != nullptr); + assert(!_packed && "dense_add(): matrix must not use packed storage"); + assert(!_symmetric && "dense_add(): matrix must not use symmetric element redirection"); + assert(row < _rows && "dense_add(): row out of bounds"); + assert(column < _cols && "dense_add(): column out of bounds"); + _buffer[static_cast(column) * _mem_rows + row] += increment; + } + + inline double* dense_ptr(const UINT32& row, const UINT32& column) const { + assert(_buffer != nullptr); + assert(!_packed && "dense_ptr(): matrix must not use packed storage"); + assert(!_symmetric && "dense_ptr(): matrix must not use symmetric element redirection"); + assert(row < _rows && "dense_ptr(): row out of bounds"); + assert(column < _cols && "dense_ptr(): column out of bounds"); + return _buffer + static_cast(column) * _mem_rows + row; + } + // element retrieval // see DNAMATRIX_ROW_WISE inline double& get(const UINT32& row, const UINT32& column) const { + assert(_buffer != nullptr); + assert(row < _mem_rows && "get(): row out of bounds"); + assert(column < _mem_cols && "get(): column out of bounds"); + if (_packed) { + UINT32 i = row, j = column; + if (i < j) { i = column; j = row; } + return _buffer[packed_index(_rows, i, j)]; + } + if (_symmetric && row < column) + return DNAMATRIX_ELEMENT(_buffer, _mem_rows, _mem_cols, column, row); return DNAMATRIX_ELEMENT(_buffer, _mem_rows, _mem_cols, row, column); } inline double* getbuffer(const UINT32& row, const UINT32& column) const { + assert(_buffer != nullptr); + assert(!_packed && "getbuffer(row,col): not valid for packed storage"); + assert(row < _mem_rows && "getbuffer(): row out of bounds"); + assert(column < _mem_cols && "getbuffer(): column out of bounds"); return _buffer + DNAMATRIX_INDEX(_mem_rows, _mem_cols, row, column); } @@ -236,9 +303,29 @@ class matrix_2d : public new_handler_support { inline UINT32 maxvalueCol() const { return _maxvalCol; } inline double* getelementref(const UINT32& row, const UINT32& column) const { + assert(_buffer != nullptr); + assert(row < _mem_rows && "getelementref(): row out of bounds"); + assert(column < _mem_cols && "getelementref(): column out of bounds"); + if (_packed) { + UINT32 i = row, j = column; + if (i < j) { i = column; j = row; } + return &_buffer[packed_index(_rows, i, j)]; + } + if (_symmetric && row < column) + return &(DNAMATRIX_ELEMENT(_buffer, _mem_rows, _mem_cols, column, row)); return &(DNAMATRIX_ELEMENT(_buffer, _mem_rows, _mem_cols, row, column)); } inline double* getelementref(const UINT32& row, const UINT32& column) { + assert(_buffer != nullptr); + assert(row < _mem_rows && "getelementref(): row out of bounds"); + assert(column < _mem_cols && "getelementref(): column out of bounds"); + if (_packed) { + UINT32 i = row, j = column; + if (i < j) { i = column; j = row; } + return &_buffer[packed_index(_rows, i, j)]; + } + if (_symmetric && row < column) + return &(DNAMATRIX_ELEMENT(_buffer, _mem_rows, _mem_cols, column, row)); return &(DNAMATRIX_ELEMENT(_buffer, _mem_rows, _mem_cols, row, column)); } @@ -250,34 +337,139 @@ class matrix_2d : public new_handler_support { inline void maxvalueCol(const UINT32& c) { _maxvalCol = c; } inline void put(const UINT32& row, const UINT32& column, const double& value) { + assert(_buffer != nullptr); + assert(row < _mem_rows && "put(): row out of bounds"); + assert(column < _mem_cols && "put(): column out of bounds"); + if (_packed) { + UINT32 i = row, j = column; + if (i < j) { i = column; j = row; } + _buffer[packed_index(_rows, i, j)] = value; + return; + } DNAMATRIX_ELEMENT(_buffer, _mem_rows, _mem_cols, row, column) = value; } inline UINT32 matrixType() const { return _matrixType; } inline void matrixType(const UINT32 t) { _matrixType = t; } + inline bool is_symmetric() const { return _symmetric; } + inline void set_symmetric(bool s) { + assert((!s || _rows == _cols) && "set_symmetric(true): matrix must be square"); + _symmetric = s; + } + + inline bool is_packed() const { return _packed; } + + static inline std::size_t packed_index(UINT32 n, UINT32 i, UINT32 j) { + return static_cast(j) * n - static_cast(j) * (j - 1) / 2 + (i - j); + } + + static inline std::size_t packed_size(UINT32 n) { + return static_cast(n) * (n + 1) / 2; + } + // Matrix functions - void copyelements(const UINT32& row_dest, const UINT32& column_dest, const matrix_2d& src, const UINT32& row_src, - const UINT32& column_src, const UINT32& rows, const UINT32& columns); - void copyelements(const UINT32& row_dest, const UINT32& column_dest, const matrix_2d* src, const UINT32& row_src, - const UINT32& column_src, const UINT32& rows, const UINT32& columns); + inline void copyelements(const UINT32& row_dest, const UINT32& column_dest, const matrix_2d& src, + const UINT32& row_src, const UINT32& column_src, + const UINT32& rows, const UINT32& columns) { + assert(_buffer != nullptr && src._buffer != nullptr); + assert(row_dest + rows <= _mem_rows && "copyelements(): dest row overflow"); + assert(column_dest + columns <= _mem_cols && "copyelements(): dest col overflow"); + assert(row_src + rows <= src._mem_rows && "copyelements(): src row overflow"); + assert(column_src + columns <= src._mem_cols && "copyelements(): src col overflow"); + if (rows == 3 && columns == 3 && !src._symmetric && !src._packed && !_packed) { + for (UINT32 c = 0; c < 3; ++c) { + double* dst = _buffer + static_cast(column_dest + c) * _mem_rows + row_dest; + const double* s = src._buffer + static_cast(column_src + c) * src._mem_rows + row_src; + dst[0] = s[0]; dst[1] = s[1]; dst[2] = s[2]; + } + return; + } + copyelements_generic(row_dest, column_dest, src, row_src, column_src, rows, columns); + } + void copyelements_generic(const UINT32& row_dest, const UINT32& column_dest, const matrix_2d& src, + const UINT32& row_src, const UINT32& column_src, + const UINT32& rows, const UINT32& columns); + inline void copyelements(const UINT32& row_dest, const UINT32& column_dest, const matrix_2d* src, + const UINT32& row_src, const UINT32& column_src, + const UINT32& rows, const UINT32& columns) { + copyelements(row_dest, column_dest, *src, row_src, column_src, rows, columns); + } inline void elementadd(const UINT32& row, const UINT32& column, const double& increment) { + assert(row < _rows && column < _cols && "elementadd(): out of bounds"); + if (_packed && row < column) return; *getelementref(row, column) += increment; } + inline void lower_add(const UINT32& row, const UINT32& column, const double& increment) { + assert(_buffer != nullptr); + assert(row < _rows && column < _cols && "lower_add(): out of bounds"); + if (_packed) { + if (row < column) return; + _buffer[packed_index(_rows, row, column)] += increment; + return; + } + if (_symmetric && row < column) { + _buffer[static_cast(row) * _mem_rows + column] += increment; + return; + } + _buffer[static_cast(column) * _mem_rows + row] += increment; + } + inline void elementsubtract(const UINT32& row, const UINT32& column, const double& decrement) { + assert(row < _rows && column < _cols && "elementsubtract(): out of bounds"); + if (_packed && row < column) return; *getelementref(row, column) -= decrement; } inline void elementmultiply(const UINT32& row, const UINT32& column, const double& scale) { + assert(row < _rows && column < _cols && "elementmultiply(): out of bounds"); *getelementref(row, column) *= scale; } - void blockadd(const UINT32& row_dest, const UINT32& col_dest, const matrix_2d& mat_src, const UINT32& row_src, - const UINT32& col_src, const UINT32& rows, const UINT32& cols); - void blockTadd(const UINT32& row_dest, const UINT32& col_dest, const matrix_2d& mat_src, const UINT32& row_src, - const UINT32& col_src, const UINT32& rows, const UINT32& cols); + inline void blockadd(const UINT32& row_dest, const UINT32& col_dest, const matrix_2d& mat_src, + const UINT32& row_src, const UINT32& col_src, const UINT32& rows, const UINT32& cols) { + assert(_buffer != nullptr && mat_src._buffer != nullptr); + assert(row_dest + rows <= _mem_rows && "blockadd(): dest row overflow"); + assert(col_dest + cols <= _mem_cols && "blockadd(): dest col overflow"); + assert(row_src + rows <= mat_src._mem_rows && "blockadd(): src row overflow"); + assert(col_src + cols <= mat_src._mem_cols && "blockadd(): src col overflow"); + if (rows == 3 && cols == 3 && !mat_src._symmetric && !mat_src._packed && !_packed) { + for (UINT32 c = 0; c < 3; ++c) { + double* dst = _buffer + static_cast(col_dest + c) * _mem_rows + row_dest; + const double* src = mat_src._buffer + static_cast(col_src + c) * mat_src._mem_rows + row_src; + dst[0] += src[0]; dst[1] += src[1]; dst[2] += src[2]; + } + return; + } + blockadd_generic(row_dest, col_dest, mat_src, row_src, col_src, rows, cols); + } + void blockadd_generic(const UINT32& row_dest, const UINT32& col_dest, const matrix_2d& mat_src, + const UINT32& row_src, const UINT32& col_src, const UINT32& rows, const UINT32& cols); + + inline void blockTadd(const UINT32& row_dest, const UINT32& col_dest, const matrix_2d& mat_src, + const UINT32& row_src, const UINT32& col_src, const UINT32& rows, const UINT32& cols) { + assert(_buffer != nullptr && mat_src._buffer != nullptr); + assert(row_dest + rows <= _mem_rows && "blockTadd(): dest row overflow"); + assert(col_dest + cols <= _mem_cols && "blockTadd(): dest col overflow"); + assert(row_src + cols <= mat_src._mem_rows && "blockTadd(): src row overflow (transposed)"); + assert(col_src + rows <= mat_src._mem_cols && "blockTadd(): src col overflow (transposed)"); + if (rows == 3 && cols == 3 && !_packed) { + for (UINT32 c = 0; c < 3; ++c) { + double* dst = _buffer + static_cast(col_dest + c) * _mem_rows + row_dest; + const std::size_t smr = mat_src._mem_rows; + const double* src_r0 = mat_src._buffer + static_cast(row_src) * smr + col_src + c; + dst[0] += src_r0[0]; + dst[1] += src_r0[smr]; + dst[2] += src_r0[2 * smr]; + } + return; + } + blockTadd_generic(row_dest, col_dest, mat_src, row_src, col_src, rows, cols); + } + void blockTadd_generic(const UINT32& row_dest, const UINT32& col_dest, const matrix_2d& mat_src, + const UINT32& row_src, const UINT32& col_src, const UINT32& rows, const UINT32& cols); void blocksubtract(const UINT32& row_dest, const UINT32& col_dest, const matrix_2d& mat_src, const UINT32& row_src, const UINT32& col_src, const UINT32& rows, const UINT32& cols); @@ -287,13 +479,19 @@ class matrix_2d : public new_handler_support { matrix_2d multiply(const char* lhs_trans, const matrix_2d& rhs, const char* rhs_trans); // multiplication matrix_2d multiply(const matrix_2d& lhs, const char* lhs_trans, const matrix_2d& rhs, const char* rhs_trans); // multiplication + // C = sym_lhs * rhs using dsymm (sym_lhs must be symmetric, lower triangle populated) + matrix_2d multiply_sym(const matrix_2d& sym_lhs, const matrix_2d& rhs); matrix_2d sweepinverse(); // Sweep inverse (good for rotation matrices) - matrix_2d cholesky_inverse(bool LOWER_IS_CLEARED = false); // Cholesky inverse + matrix_2d cholesky_inverse(bool LOWER_IS_CLEARED = false, bool mark_symmetric = false); // Cholesky inverse + matrix_2d cholesky_factor(bool LOWER_IS_CLEARED = false); // Cholesky factor only (dpotrf), no inverse + void cholesky_solve(matrix_2d& rhs, bool LOWER_IS_CLEARED = false); // Solve from pre-factored matrix (dpotrs) + double dot(const matrix_2d& other) const; // Inner product of two column vectors matrix_2d transpose(const matrix_2d&); // Transpose matrix_2d transpose(); // '' matrix_2d scale(const double& scalar); // scale + void scale_symmetric_diagonal(const double* diag); // overloaded operators // equality @@ -347,6 +545,7 @@ class matrix_2d : public new_handler_support { void setsize(const UINT32& rows, const UINT32& columns); // sets matrix size to rows * columns only (buffer not allocated any memory) void redim(const UINT32& rows, const UINT32& columns); // redimensions matrix to rows * columns + void redim_packed(const UINT32& n); // redimensions to packed symmetric n×n void replace(const UINT32& rowstart, const UINT32& columnstart, const matrix_2d& newmat); void replace(const UINT32& rowstart, const UINT32& columnstart, const UINT32& rows, const UINT32& columns, const matrix_2d& newmat); @@ -369,6 +568,14 @@ class matrix_2d : public new_handler_support { // Writing to memory mapped file void WriteMappedFileRegion(void* addr); + // In-place mmap: attach _buffer directly to mmap data region (no memcpy) + void AttachMappedFileRegion(void* addr); + + // In-place mmap: write footer back, detach _buffer + void DetachMappedFileRegion(void* addr); + + inline bool owns_buffer() const { return _owns_buffer; } + // debug #ifdef _MSDEBUG void trace(const std::string& comment, const std::string& format) const; @@ -376,12 +583,14 @@ class matrix_2d : public new_handler_support { const UINT32& row_begin, const UINT32& col_begin, const UINT32& rows, const UINT32& columns) const; #endif + void deallocate(); + private: inline std::size_t buffersize() const { + if (_packed) + return packed_size(_mem_rows) * sizeof(double); return static_cast(_mem_rows) * static_cast(_mem_cols) * sizeof(double); } - - void deallocate(); void buy(const UINT32& rows, const UINT32& columns, double** mem_space); void copybuffer(const UINT32& rows, const UINT32& columns, const matrix_2d& oldmat); void copybuffer(const UINT32& rowstart, const UINT32& columnstart, const UINT32& rows, const UINT32& columns, @@ -395,11 +604,14 @@ class matrix_2d : public new_handler_support { UINT32 _cols; // number of actual cols UINT32 _rows; // number of actual rows double* _buffer; // matrix buffer elements + bool _owns_buffer; // true if _buffer is heap-allocated (false when attached to mmap) UINT32 _maxvalCol; // col of max value UINT32 _maxvalRow; // row of max value UINT32 _matrixType; // full, upper/lower, sparse + bool _symmetric; // only lower triangle populated (upper is mirror) + bool _packed; // packed column-major lower-triangle storage (n*(n+1)/2 elements) }; } // namespace math diff --git a/dynadjust/include/measurement_types/dnaangle.cpp b/dynadjust/include/measurement_types/dnaangle.cpp index 5372f4d67..59b795bda 100644 --- a/dynadjust/include/measurement_types/dnaangle.cpp +++ b/dynadjust/include/measurement_types/dnaangle.cpp @@ -250,6 +250,7 @@ UINT32 CDnaAngle::SetMeasurementRec(const vstn_t& binaryStn, it_vmsr_t& it_msr, m_dStdDev = sqrt(it_msr->term2); m_epoch = it_msr->epoch; + m_observation_epoch = it_msr->observation_epoch; m_sourceFileIndex = it_msr->sourceFileIndex; CDnaMeasurement::SetDatabaseMap(*dbidmap); @@ -278,6 +279,7 @@ void CDnaAngle::WriteBinaryMsr(std::ofstream* binary_stream, PUINT32 msrIndex) c measRecord.fileOrder = ((*msrIndex)++); snprintf(measRecord.epoch, sizeof(measRecord.epoch), "%s", m_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); + snprintf(measRecord.observation_epoch, sizeof(measRecord.observation_epoch), "%s", m_observation_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); binary_stream->write(reinterpret_cast(&measRecord), sizeof(measurement_t)); } diff --git a/dynadjust/include/measurement_types/dnacoordinate.cpp b/dynadjust/include/measurement_types/dnacoordinate.cpp index d7d488496..27f5b20ca 100644 --- a/dynadjust/include/measurement_types/dnacoordinate.cpp +++ b/dynadjust/include/measurement_types/dnacoordinate.cpp @@ -202,6 +202,7 @@ UINT32 CDnaCoordinate::SetMeasurementRec(const vstn_t& binaryStn, it_vmsr_t& it_ m_dStdDev = sqrt(it_msr->term2); m_epoch = it_msr->epoch; + m_observation_epoch = it_msr->observation_epoch; m_sourceFileIndex = it_msr->sourceFileIndex; CDnaMeasurement::SetDatabaseMap(*dbidmap); @@ -229,6 +230,7 @@ void CDnaCoordinate::WriteBinaryMsr(std::ofstream* binary_stream, PUINT32 msrInd measRecord.fileOrder = ((*msrIndex)++); snprintf(measRecord.epoch, sizeof(measRecord.epoch), "%s", m_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); + snprintf(measRecord.observation_epoch, sizeof(measRecord.observation_epoch), "%s", m_observation_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); binary_stream->write(reinterpret_cast(&measRecord), sizeof(measurement_t)); } diff --git a/dynadjust/include/measurement_types/dnadirection.cpp b/dynadjust/include/measurement_types/dnadirection.cpp index 01060e397..f204754bb 100644 --- a/dynadjust/include/measurement_types/dnadirection.cpp +++ b/dynadjust/include/measurement_types/dnadirection.cpp @@ -65,6 +65,7 @@ CDnaDirection::CDnaDirection(CDnaDirection&& d) m_msr_db_map = d.m_msr_db_map; m_epoch = d.m_epoch; + m_observation_epoch = d.m_observation_epoch; } // move assignment operator @@ -599,13 +600,14 @@ UINT32 CDnaDirection::SetMeasurementRec(const vstn_t& binaryStn, it_vmsr_t& it_m m_dStdDev = sqrt(it_msr->term2); m_epoch = it_msr->epoch; + m_observation_epoch = it_msr->observation_epoch; m_sourceFileIndex = it_msr->sourceFileIndex; CDnaMeasurement::SetDatabaseMap(*dbidmap); - + return 0; } - + void CDnaDirection::WriteBinaryMsr(std::ofstream* binary_stream, PUINT32 msrIndex) const { @@ -636,6 +638,7 @@ void CDnaDirection::WriteBinaryMsr(std::ofstream* binary_stream, PUINT32 msrInde measRecord.fileOrder = ((*msrIndex)++); snprintf(measRecord.epoch, sizeof(measRecord.epoch), "%s", m_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); + snprintf(measRecord.observation_epoch, sizeof(measRecord.observation_epoch), "%s", m_observation_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); binary_stream->write(reinterpret_cast(&measRecord), sizeof(measurement_t)); } diff --git a/dynadjust/include/measurement_types/dnadirectionset.cpp b/dynadjust/include/measurement_types/dnadirectionset.cpp index 3e72c344d..f412adb03 100644 --- a/dynadjust/include/measurement_types/dnadirectionset.cpp +++ b/dynadjust/include/measurement_types/dnadirectionset.cpp @@ -394,6 +394,7 @@ UINT32 CDnaDirectionSet::SetMeasurementRec(const vstn_t& binaryStn, it_vmsr_t& i m_lsetID = it_msr->clusterID; m_epoch = it_msr->epoch; + m_observation_epoch = it_msr->observation_epoch; m_sourceFileIndex = it_msr->sourceFileIndex; m_vTargetDirections.clear(); @@ -455,6 +456,7 @@ void CDnaDirectionSet::WriteBinaryMsr(std::ofstream* binary_stream, PUINT32 msrI measRecord.fileOrder = ((*msrIndex)++); snprintf(measRecord.epoch, sizeof(measRecord.epoch), "%s", m_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); + snprintf(measRecord.observation_epoch, sizeof(measRecord.observation_epoch), "%s", m_observation_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); binary_stream->write(reinterpret_cast(&measRecord), sizeof(measurement_t)); diff --git a/dynadjust/include/measurement_types/dnadistance.cpp b/dynadjust/include/measurement_types/dnadistance.cpp index f02a2f312..c50d1fb3d 100644 --- a/dynadjust/include/measurement_types/dnadistance.cpp +++ b/dynadjust/include/measurement_types/dnadistance.cpp @@ -283,6 +283,7 @@ UINT32 CDnaDistance::SetMeasurementRec(const vstn_t& binaryStn, it_vmsr_t& it_ms m_dStdDev = sqrt(it_msr->term2); m_epoch = it_msr->epoch; + m_observation_epoch = it_msr->observation_epoch; m_sourceFileIndex = it_msr->sourceFileIndex; CDnaMeasurement::SetDatabaseMap(*dbidmap); @@ -313,6 +314,7 @@ void CDnaDistance::WriteBinaryMsr(std::ofstream* binary_stream, PUINT32 msrIndex measRecord.fileOrder = ((*msrIndex)++); snprintf(measRecord.epoch, sizeof(measRecord.epoch), "%s", m_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); + snprintf(measRecord.observation_epoch, sizeof(measRecord.observation_epoch), "%s", m_observation_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); binary_stream->write(reinterpret_cast(&measRecord), sizeof(measurement_t)); } diff --git a/dynadjust/include/measurement_types/dnagpsbaseline.cpp b/dynadjust/include/measurement_types/dnagpsbaseline.cpp index f1d5c501a..68e203102 100644 --- a/dynadjust/include/measurement_types/dnagpsbaseline.cpp +++ b/dynadjust/include/measurement_types/dnagpsbaseline.cpp @@ -76,7 +76,8 @@ CDnaGpsBaseline::CDnaGpsBaseline(CDnaGpsBaseline&& g) m_referenceFrame = g.m_referenceFrame; m_epsgCode = g.m_epsgCode; m_epoch = g.m_epoch; - + m_observation_epoch = g.m_observation_epoch; + m_dX = g.m_dX; m_dY = g.m_dY; m_dZ = g.m_dZ; @@ -114,6 +115,7 @@ CDnaGpsBaseline& CDnaGpsBaseline::operator= (CDnaGpsBaseline&& rhs) m_referenceFrame = rhs.m_referenceFrame; m_epsgCode = rhs.m_epsgCode; m_epoch = rhs.m_epoch; + m_observation_epoch = rhs.m_observation_epoch; m_dX = rhs.m_dX; m_dY = rhs.m_dY; @@ -371,6 +373,7 @@ UINT32 CDnaGpsBaseline::SetMeasurementRec(const vstn_t& binaryStn, it_vmsr_t& it m_dVscale = it_msr->scale4; m_epoch = it_msr->epoch; + m_observation_epoch = it_msr->observation_epoch; m_epsgCode = it_msr->epsgCode; m_referenceFrame = datumFromEpsgString(it_msr->epsgCode); @@ -437,6 +440,7 @@ void CDnaGpsBaseline::WriteBinaryMsr(std::ofstream* binary_stream, PUINT32 msrIn snprintf(measRecord.epsgCode, sizeof(measRecord.epsgCode), "%s", m_epsgCode.substr(0, STN_EPSG_WIDTH).c_str()); snprintf(measRecord.epoch, sizeof(measRecord.epoch), "%s", m_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); + snprintf(measRecord.observation_epoch, sizeof(measRecord.observation_epoch), "%s", m_observation_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); // X measRecord.measAdj = m_measAdj; @@ -485,7 +489,7 @@ void CDnaGpsBaseline::WriteBinaryMsr(std::ofstream* binary_stream, PUINT32 msrIn // now write covariance elements std::vector::const_iterator _it_cov; for (_it_cov=m_vGpsCovariances.begin(); _it_cov!=m_vGpsCovariances.end(); ++_it_cov) - _it_cov->WriteBinaryMsr(binary_stream, msrIndex, m_epsgCode, m_epoch); + _it_cov->WriteBinaryMsr(binary_stream, msrIndex, m_epsgCode, m_epoch, m_observation_epoch); } void CDnaGpsBaseline::SerialiseDatabaseMap(std::ofstream* os) @@ -650,17 +654,18 @@ CDnaGpsBaselineCluster::CDnaGpsBaselineCluster(CDnaGpsBaselineCluster&& g) m_dVscale = g.m_dVscale; m_lclusterID = g.m_lclusterID; m_MSmeasurementStations = g.m_MSmeasurementStations; - + m_referenceFrame = g.m_referenceFrame; m_epsgCode = g.m_epsgCode; m_epoch = g.m_epoch; + m_observation_epoch = g.m_observation_epoch; m_msr_db_map = g.m_msr_db_map; m_dbidmap = g.m_dbidmap; } -// move assignment operator +// move assignment operator CDnaGpsBaselineCluster& CDnaGpsBaselineCluster::operator= (CDnaGpsBaselineCluster&& rhs) { // check for assignment to self! @@ -674,6 +679,7 @@ CDnaGpsBaselineCluster& CDnaGpsBaselineCluster::operator= (CDnaGpsBaselineCluste m_referenceFrame = rhs.m_referenceFrame; m_epsgCode = rhs.m_epsgCode; m_epoch = rhs.m_epoch; + m_observation_epoch = rhs.m_observation_epoch; m_dPscale = rhs.m_dPscale; m_dLscale = rhs.m_dLscale; @@ -891,6 +897,7 @@ UINT32 CDnaGpsBaselineCluster::SetMeasurementRec(const vstn_t& binaryStn, it_vms m_referenceFrame = datumFromEpsgString(it_msr->epsgCode); m_epoch = it_msr->epoch; + m_observation_epoch = it_msr->observation_epoch; m_epsgCode = it_msr->epsgCode; m_sourceFileIndex = it_msr->sourceFileIndex; diff --git a/dynadjust/include/measurement_types/dnagpspoint.cpp b/dynadjust/include/measurement_types/dnagpspoint.cpp index 0696c251a..0f2e8a05a 100644 --- a/dynadjust/include/measurement_types/dnagpspoint.cpp +++ b/dynadjust/include/measurement_types/dnagpspoint.cpp @@ -78,6 +78,7 @@ CDnaGpsPoint::CDnaGpsPoint(CDnaGpsPoint&& p) m_referenceFrame = p.m_referenceFrame; m_epsgCode = p.m_epsgCode; m_epoch = p.m_epoch; + m_observation_epoch = p.m_observation_epoch; m_dX = p.m_dX; m_dY = p.m_dY; @@ -95,7 +96,7 @@ CDnaGpsPoint::CDnaGpsPoint(CDnaGpsPoint&& p) m_dVscale = p.m_dVscale; SetCoordType(p.m_strCoordType); - + m_lclusterID = p.m_lclusterID; m_MSmeasurementStations = p.m_MSmeasurementStations; @@ -117,6 +118,7 @@ CDnaGpsPoint& CDnaGpsPoint::operator= (CDnaGpsPoint&& rhs) m_referenceFrame = rhs.m_referenceFrame; m_epsgCode = rhs.m_epsgCode; m_epoch = rhs.m_epoch; + m_observation_epoch = rhs.m_observation_epoch; m_dX = rhs.m_dX; m_dY = rhs.m_dY; @@ -134,7 +136,7 @@ CDnaGpsPoint& CDnaGpsPoint::operator= (CDnaGpsPoint&& rhs) m_dVscale = rhs.m_dVscale; SetCoordType(rhs.m_strCoordType); - + m_lclusterID = rhs.m_lclusterID; m_MSmeasurementStations = rhs.m_MSmeasurementStations; @@ -478,12 +480,13 @@ UINT32 CDnaGpsPoint::SetMeasurementRec(const vstn_t& binaryStn, it_vmsr_t& it_ms m_dVscale = it_msr->scale4; m_epoch = it_msr->epoch; + m_observation_epoch = it_msr->observation_epoch; m_epsgCode = it_msr->epsgCode; m_referenceFrame = datumFromEpsgCode(LongFromString(it_msr->epsgCode)); m_lclusterID = it_msr->clusterID; m_MSmeasurementStations = (MEASUREMENT_STATIONS)it_msr->measurementStations; - + // X, sigmaXX m_bIgnore = it_msr->ignore; m_strType = it_msr->measType; @@ -548,6 +551,7 @@ void CDnaGpsPoint::WriteBinaryMsr(std::ofstream* binary_stream, PUINT32 msrIndex snprintf(measRecord.epsgCode, sizeof(measRecord.epsgCode), "%s", m_epsgCode.substr(0, STN_EPSG_WIDTH).c_str()); snprintf(measRecord.epoch, sizeof(measRecord.epoch), "%s", m_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); + snprintf(measRecord.observation_epoch, sizeof(measRecord.observation_epoch), "%s", m_observation_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); // X measRecord.measAdj = m_measAdj; @@ -596,7 +600,7 @@ void CDnaGpsPoint::WriteBinaryMsr(std::ofstream* binary_stream, PUINT32 msrIndex // now write covariance elements std::vector::const_iterator _it_cov; for (_it_cov=m_vPointCovariances.begin(); _it_cov!=m_vPointCovariances.end(); ++_it_cov) - _it_cov->WriteBinaryMsr(binary_stream, msrIndex, m_epsgCode, m_epoch); + _it_cov->WriteBinaryMsr(binary_stream, msrIndex, m_epsgCode, m_epoch, m_observation_epoch); } @@ -756,6 +760,7 @@ CDnaGpsPointCluster::CDnaGpsPointCluster(CDnaGpsPointCluster&& p) m_referenceFrame = p.m_referenceFrame; m_epsgCode = p.m_epsgCode; m_epoch = p.m_epoch; + m_observation_epoch = p.m_observation_epoch; m_msr_db_map = p.m_msr_db_map; @@ -776,6 +781,7 @@ CDnaGpsPointCluster& CDnaGpsPointCluster::operator= (CDnaGpsPointCluster&& rhs) m_referenceFrame = rhs.m_referenceFrame; m_epsgCode = rhs.m_epsgCode; m_epoch = rhs.m_epoch; + m_observation_epoch = rhs.m_observation_epoch; m_dPscale = rhs.m_dPscale; m_dLscale = rhs.m_dLscale; @@ -1014,6 +1020,7 @@ UINT32 CDnaGpsPointCluster::SetMeasurementRec(const vstn_t& binaryStn, it_vmsr_t m_referenceFrame = datumFromEpsgCode(LongFromString(it_msr->epsgCode)); m_epoch = it_msr->epoch; + m_observation_epoch = it_msr->observation_epoch; m_epsgCode = it_msr->epsgCode; m_sourceFileIndex = it_msr->sourceFileIndex; diff --git a/dynadjust/include/measurement_types/dnaheight.cpp b/dynadjust/include/measurement_types/dnaheight.cpp index ae86d6c41..3ffecad56 100644 --- a/dynadjust/include/measurement_types/dnaheight.cpp +++ b/dynadjust/include/measurement_types/dnaheight.cpp @@ -184,6 +184,7 @@ UINT32 CDnaHeight::SetMeasurementRec(const vstn_t& binaryStn, it_vmsr_t& it_msr, m_dStdDev = sqrt(it_msr->term2); m_epoch = it_msr->epoch; + m_observation_epoch = it_msr->observation_epoch; m_sourceFileIndex = it_msr->sourceFileIndex; CDnaMeasurement::SetDatabaseMap(*dbidmap); @@ -213,6 +214,7 @@ void CDnaHeight::WriteBinaryMsr(std::ofstream* binary_stream, PUINT32 msrIndex) measRecord.fileOrder = ((*msrIndex)++); snprintf(measRecord.epoch, sizeof(measRecord.epoch), "%s", m_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); + snprintf(measRecord.observation_epoch, sizeof(measRecord.observation_epoch), "%s", m_observation_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); binary_stream->write(reinterpret_cast(&measRecord), sizeof(measurement_t)); } diff --git a/dynadjust/include/measurement_types/dnaheightdifference.cpp b/dynadjust/include/measurement_types/dnaheightdifference.cpp index ba7e26db0..c8c15a8a1 100644 --- a/dynadjust/include/measurement_types/dnaheightdifference.cpp +++ b/dynadjust/include/measurement_types/dnaheightdifference.cpp @@ -220,6 +220,7 @@ UINT32 CDnaHeightDifference::SetMeasurementRec(const vstn_t& binaryStn, it_vmsr_ m_dStdDev = sqrt(it_msr->term2); m_epoch = it_msr->epoch; + m_observation_epoch = it_msr->observation_epoch; m_sourceFileIndex = it_msr->sourceFileIndex; CDnaMeasurement::SetDatabaseMap(*dbidmap); @@ -248,6 +249,7 @@ void CDnaHeightDifference::WriteBinaryMsr(std::ofstream* binary_stream, PUINT32 measRecord.fileOrder = ((*msrIndex)++); snprintf(measRecord.epoch, sizeof(measRecord.epoch), "%s", m_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); + snprintf(measRecord.observation_epoch, sizeof(measRecord.observation_epoch), "%s", m_observation_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); binary_stream->write(reinterpret_cast(&measRecord), sizeof(measurement_t)); } diff --git a/dynadjust/include/measurement_types/dnameasurement.cpp b/dynadjust/include/measurement_types/dnameasurement.cpp index 6608a4881..6a76d77ea 100644 --- a/dynadjust/include/measurement_types/dnameasurement.cpp +++ b/dynadjust/include/measurement_types/dnameasurement.cpp @@ -221,19 +221,20 @@ UINT32 CDnaCovariance::SetMeasurementRec(const vstn_t&, it_vmsr_t& it_msr) } -void CDnaCovariance::WriteBinaryMsr(std::ofstream *binary_stream, PUINT32 msrIndex, const std::string& epsgCode, const std::string& epoch) const +void CDnaCovariance::WriteBinaryMsr(std::ofstream *binary_stream, PUINT32 msrIndex, const std::string& epsgCode, const std::string& epoch, const std::string& observation_epoch) const { *msrIndex += 3; measurement_t measRecord; // Common measRecord.measType = GetTypeC(); - measRecord.station1 = m_lstn1Index; - measRecord.station2 = m_lstn2Index; + measRecord.station1 = m_lstn1Index; + measRecord.station2 = m_lstn2Index; measRecord.clusterID = m_lclusterID; - + snprintf(measRecord.epsgCode, sizeof(measRecord.epsgCode), "%s", epsgCode.substr(0, STN_EPSG_WIDTH).c_str()); snprintf(measRecord.epoch, sizeof(measRecord.epoch), "%s", epoch.substr(0, STN_EPOCH_WIDTH).c_str()); + snprintf(measRecord.observation_epoch, sizeof(measRecord.observation_epoch), "%s", observation_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); // X measRecord.measStart = xCov; @@ -360,6 +361,7 @@ CDnaMeasurement::CDnaMeasurement() , m_epsgCode(DEFAULT_EPSG_S) , m_sourceFileIndex(0) , m_epoch("") + , m_observation_epoch("") , m_bInsufficient(false) { } @@ -388,6 +390,7 @@ CDnaMeasurement::CDnaMeasurement(CDnaMeasurement&& m) m_preAdjCorr = m.m_preAdjCorr; m_epoch = m.m_epoch; + m_observation_epoch = m.m_observation_epoch; m_epsgCode = m.m_epsgCode; m_sourceFileIndex = m.m_sourceFileIndex; @@ -419,6 +422,7 @@ CDnaMeasurement& CDnaMeasurement::operator= (CDnaMeasurement&& rhs) m_preAdjCorr = rhs.m_preAdjCorr; m_epoch = rhs.m_epoch; + m_observation_epoch = rhs.m_observation_epoch; m_epsgCode = rhs.m_epsgCode; m_sourceFileIndex = rhs.m_sourceFileIndex; @@ -501,6 +505,16 @@ void CDnaMeasurement::SerialiseDatabaseMap(std::ofstream* os) void CDnaMeasurement::SetEpoch(const std::string& epoch) { m_epoch = epoch; + // Default the (immutable) observation epoch to the reference-frame epoch + // when it has not been explicitly supplied. Legacy DNA v3.01 / DynaML + // files without thus behave identically to before. + if (m_observation_epoch.empty()) + m_observation_epoch = epoch; +} + +void CDnaMeasurement::SetObservationEpoch(const std::string& observation_epoch) +{ + m_observation_epoch = observation_epoch; } } // namespace measurements diff --git a/dynadjust/include/measurement_types/dnameasurement.hpp b/dynadjust/include/measurement_types/dnameasurement.hpp index 20bcf90e8..0fabeaa55 100644 --- a/dynadjust/include/measurement_types/dnameasurement.hpp +++ b/dynadjust/include/measurement_types/dnameasurement.hpp @@ -144,15 +144,20 @@ typedef struct msr_t { memset(epsgCode, '\0', sizeof(epsgCode)); snprintf(epsgCode, sizeof(epsgCode), DEFAULT_EPSG_S); memset(epoch, '\0', sizeof(epoch)); + memset(observation_epoch, '\0', sizeof(observation_epoch)); } char measType; // 'A', 'S', 'X', ... , etc. char measStart; // Start of a measurement (0=start or X, 1=Y, 2=Z, 3=covX, 4=covY, 5=covZ) char measurementStations; // One-, two- or three-station measurement char epsgCode[7]; // epsg ID, i.e. NNNNN (where NNNNN is in the range 0-32767) - char epoch[12]; // date, i.e. "DD.MM.YYYY" (10 chars) - // if datum is dynamic, Epoch is YYYY MM DD - // if datum is static, Epoch is ignored + char epoch[STN_EPOCH_WIDTH]; // Epoch of Reference Frame, i.e. "DD.MM.YYYY" (10 chars) + // Mutable via dnareftran. + // If datum is static, Epoch is ignored. + char observation_epoch[STN_EPOCH_WIDTH]; // Epoch of Observation, i.e. "DD.MM.YYYY" (10 chars) + // Immutable under reftran. Timestamp at which the measurement + // was observed. Used for discontinuity station-instance + // allocation. Empty if not supplied. char coordType[4]; // "LLH", "UTM", ... , etc. bool ignore; UINT32 station1; // stations 1, 2 and 3 are indices to @@ -221,7 +226,7 @@ class CDnaCovariance inline bool GetIgnore() const { return m_bIgnore; } inline virtual UINT32 CalcBinaryRecordCount() const { return 3; } - void WriteBinaryMsr(std::ofstream* binary_stream, PUINT32 msrIndex, const std::string& epsgCode, const std::string& epoch) const; + void WriteBinaryMsr(std::ofstream* binary_stream, PUINT32 msrIndex, const std::string& epsgCode, const std::string& epoch, const std::string& observation_epoch) const; virtual UINT32 SetMeasurementRec(const vstn_t& binaryStn, it_vmsr_t& it_msr); virtual void WriteDynaMLMsr(std::ofstream* dynaml_stream) const; virtual void WriteDNAMsr(std::ofstream* dna_stream, @@ -374,6 +379,7 @@ class CDnaMeasurement virtual inline std::string GetReferenceFrame() const { return ""; } inline std::string GetEpoch() const { return m_epoch; } + inline std::string GetObservationEpoch() const { return m_observation_epoch; } virtual inline std::vector* GetBaselines_ptr() { return 0; } virtual inline std::vector* GetDirections_ptr() { return 0; } @@ -402,6 +408,7 @@ class CDnaMeasurement virtual void SetReferenceFrame(const std::string&) {} void SetEpoch(const std::string& epoch); + void SetObservationEpoch(const std::string& observation_epoch); virtual void SetLscale(const std::string&) {} virtual void SetLscale(const double&) {} @@ -485,7 +492,8 @@ class CDnaMeasurement UINT32 m_sourceFileIndex; std::string m_epoch; - + std::string m_observation_epoch; + msr_database_id_map m_msr_db_map; bool m_bInsufficient; @@ -514,21 +522,21 @@ class DNATYPE_API MsrTally UINT32 TotalCount(); void coutSummary(std::ostream &os, const std::string& title); UINT32 MeasurementCount(const char& msrType); - + void CreateTally(const vdnaMsrPtr& vMeasurements); void CreateTally(const vmsr_t& vMeasurements, const vUINT32& CML); UINT32 CreateTally(const vmsr_t& vMeasurements, bool countValidOnly=false); - + void IncrementMsrType(const char& msrType, const UINT32& count=1); - + void coutSummaryMsrToStn(std::ostream &os, const std::string& station); void coutSummaryMsrToStn_Compressed(std::ostream &os, const std::string& station); - //bool GPSOnly(); + //bool GPSOnly(); inline bool ContainsNonGPS() { return containsNonGPS; } static _MEASUREMENT_STATIONS_ Stations(const char& measType); - + bool containsNonGPS; UINT32 A, B, C, D, E, G, H, I, J, K, L, M, P, Q, R, S, V, X, Y, Z; diff --git a/dynadjust/include/measurement_types/dnastation.cpp b/dynadjust/include/measurement_types/dnastation.cpp index c728fa278..83a4d455f 100644 --- a/dynadjust/include/measurement_types/dnastation.cpp +++ b/dynadjust/include/measurement_types/dnastation.cpp @@ -120,7 +120,7 @@ CDnaStation::CDnaStation(const std::string& referenceframe, const std::string& e , m_fgeoidSep(0.), m_dmeridianDef(0.), m_dverticalDef(0.) , m_lfileOrder(0), m_lnameOrder(0) , m_zone(0), m_unusedStation(INVALID_STATION) - , m_referenceFrame(referenceframe), m_epoch(epoch) + , m_referenceFrame(referenceframe), m_epoch(epoch), m_observation_epoch(epoch) , m_constraintType(free_3D) { m_epsgCode = epsgStringFromName(referenceframe); @@ -170,6 +170,7 @@ CDnaStation::CDnaStation(const CDnaStation& newStation) m_referenceFrame = newStation.m_referenceFrame; m_epsgCode = newStation.m_epsgCode; m_epoch = newStation.m_epoch; + m_observation_epoch = newStation.m_observation_epoch; m_constraintType = newStation.m_constraintType; } @@ -215,6 +216,7 @@ CDnaStation::CDnaStation(const std::string& strName, const std::string& strConst m_referenceFrame = DEFAULT_DATUM; m_epsgCode = DEFAULT_EPSG_S; m_epoch = ""; + m_observation_epoch = ""; } CDnaStation& CDnaStation::operator =(const CDnaStation& rhs) @@ -261,9 +263,10 @@ CDnaStation& CDnaStation::operator =(const CDnaStation& rhs) m_referenceFrame = rhs.m_referenceFrame; m_epsgCode = rhs.m_epsgCode; m_epoch = rhs.m_epoch; + m_observation_epoch = rhs.m_observation_epoch; m_constraintType = rhs.m_constraintType; - + return *this; } @@ -675,6 +678,7 @@ void CDnaStation::WriteBinaryStn(std::ofstream* binary_stream, const UINT16 bUnu stationRecord.unusedStation = bUnused; strcpy(stationRecord.epoch, m_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); + strcpy(stationRecord.observation_epoch, m_observation_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); strcpy(stationRecord.epsgCode, m_epsgCode.substr(0, STN_EPSG_WIDTH).c_str()); binary_stream->write(reinterpret_cast(&stationRecord), sizeof(station_t)); @@ -948,6 +952,7 @@ void CDnaStation::SetStationRec(const station_t& stationRecord) m_unusedStation = (stationRecord.unusedStation == VALID_STATION ? true : false); m_epoch = stationRecord.epoch; + m_observation_epoch = stationRecord.observation_epoch; m_epsgCode = stationRecord.epsgCode; m_referenceFrame = datumFromEpsgCode(LongFromString(m_epsgCode)); } diff --git a/dynadjust/include/measurement_types/dnastation.hpp b/dynadjust/include/measurement_types/dnastation.hpp index aa8d8452c..8051e6a8b 100644 --- a/dynadjust/include/measurement_types/dnastation.hpp +++ b/dynadjust/include/measurement_types/dnastation.hpp @@ -276,9 +276,18 @@ class CDnaStation inline std::string GetReferenceFrame() const { return m_referenceFrame; } inline std::string GetEpoch() const { return m_epoch; } - + inline std::string GetObservationEpoch() const { return m_observation_epoch; } + inline void SetReferenceFrame(const std::string& r) { m_referenceFrame = trimstr(r); } - inline void SetEpoch(const std::string& e) { m_epoch = trimstr(e); } + // Default observation_epoch to epoch when unset so legacy inputs preserve + // prior behaviour; once set, observation_epoch is immutable under reftran. + inline void SetEpoch(const std::string& e) + { + m_epoch = trimstr(e); + if (m_observation_epoch.empty()) + m_observation_epoch = m_epoch; + } + inline void SetObservationEpoch(const std::string& e) { m_observation_epoch = trimstr(e); } inline void SetEpsg(const std::string& e) { m_epsgCode = trimstr(e); } std::string m_strName; @@ -325,6 +334,7 @@ class CDnaStation std::string m_referenceFrame; std::string m_epsgCode; std::string m_epoch; + std::string m_observation_epoch; CONSTRAINT_TYPE m_constraintType; }; diff --git a/dynadjust/include/memory/dnafile_mapping.cpp b/dynadjust/include/memory/dnafile_mapping.cpp index 1a301b315..675e3c600 100644 --- a/dynadjust/include/memory/dnafile_mapping.cpp +++ b/dynadjust/include/memory/dnafile_mapping.cpp @@ -22,7 +22,13 @@ #include -namespace dynadjust { +#ifdef __linux__ +#include +#include +#include +#endif + +namespace dynadjust { namespace memory { //block_map_t::block_map_t() @@ -72,15 +78,33 @@ block_map_t::block_map_t(const block_map_t &p) void block_map_t::MapRegion(FileMapPtr file_map_ptr) { region_ptr_.reset( new boost::interprocess::mapped_region( - *file_map_ptr, - boost::interprocess::read_write, - region_offset_, + *file_map_ptr, + boost::interprocess::read_write, + region_offset_, data_size_ ) ); } +void block_map_t::AdviseSequential() { + if (region_ptr_) + region_ptr_->advise(boost::interprocess::mapped_region::advice_sequential); +} + + +void block_map_t::AdviseDontNeed() { + if (region_ptr_) + region_ptr_->advise(boost::interprocess::mapped_region::advice_dontneed); +} + + +void block_map_t::AdviseWillNeed() { + if (region_ptr_) + region_ptr_->advise(boost::interprocess::mapped_region::advice_willneed); +} + + // class to hold addresses and sizes for all matrices // in a vector of segmented blocks vmat_file_map::vmat_file_map() @@ -129,10 +153,54 @@ void vmat_file_map::CreateFileMap() } -void vmat_file_map::MapRegion(const UINT32 block) +void vmat_file_map::MapRegion(const UINT32 block) { vblockMapRegions_.at(block).MapRegion(file_map_ptr_); } + + +void vmat_file_map::AdviseRegion(const UINT32 block, boost::interprocess::mapped_region::advice_types advice) +{ + auto& region = vblockMapRegions_.at(block); +#ifdef __linux__ + // Boost's mapped_region::advise calls madvise() without + // page-aligning the address. Stage regions are packed + // end-to-end so only the first region's address is page- + // aligned by chance; madvise on the rest fails silently + // with EINVAL. Round start up and end down to whole + // pages, then call madvise directly. For DONTNEED also + // flush dirty pages asynchronously so the kernel can + // evict them promptly. + if (region.region_ptr_ && region.data_size_ > 0) { + const std::size_t page_size = + static_cast(sysconf(_SC_PAGESIZE)); + auto base = reinterpret_cast( + region.region_ptr_->get_address()); + std::uintptr_t aligned_start = + (base + page_size - 1) & ~(page_size - 1); + std::uintptr_t aligned_end = + (base + region.data_size_) & ~(page_size - 1); + if (aligned_end > aligned_start) { + void* addr = reinterpret_cast(aligned_start); + std::size_t len = aligned_end - aligned_start; + int madv = MADV_NORMAL; + switch (advice) { + case boost::interprocess::mapped_region::advice_sequential: + madv = MADV_SEQUENTIAL; break; + case boost::interprocess::mapped_region::advice_willneed: + madv = MADV_WILLNEED; break; + case boost::interprocess::mapped_region::advice_dontneed: + msync(addr, len, MS_ASYNC); + madv = MADV_DONTNEED; break; + default: break; + } + madvise(addr, len, madv); + return; + } + } +#endif + region.region_ptr_->advise(advice); +} } // namespace memory diff --git a/dynadjust/include/memory/dnafile_mapping.hpp b/dynadjust/include/memory/dnafile_mapping.hpp index ff33a8eb2..abd08859e 100644 --- a/dynadjust/include/memory/dnafile_mapping.hpp +++ b/dynadjust/include/memory/dnafile_mapping.hpp @@ -64,12 +64,17 @@ class block_map_t inline size_t GetDataSize() const { return data_size_; } inline size_t GetRegionOffset() const { return region_offset_; } inline size_t GetCumulativeRegionOffset() const { return region_offset_ + data_size_; } - + inline void SetDataSize(const size_t& size) { data_size_ = size; } inline void SetRegionOffset(const size_t& size) { region_offset_ = size; } - + void MapRegion(FileMapPtr file_map_ptr); + // Memory advisory hints for mapped regions + void AdviseSequential(); + void AdviseDontNeed(); + void AdviseWillNeed(); + size_t data_size_; // Size of this matrix. size_t region_offset_; // Offset from the beginning of the region MapRegPtr region_ptr_; // shared pointer to the region @@ -92,10 +97,11 @@ class vmat_file_map { void setnewFilePath(const std::string& filePath, bool remove_mapped_file); void CreateFileMap(); void MapRegion(const UINT32 block); + void AdviseRegion(const UINT32 block, boost::interprocess::mapped_region::advice_types advice); inline FileMapPtr getFileMapPtr() const { return file_map_ptr_; } - inline void* GetBlockRegionAddr(const UINT32 block) const { - return vblockMapRegions_.at(block).region_ptr_->get_address(); + inline void* GetBlockRegionAddr(const UINT32 block) const { + return vblockMapRegions_.at(block).region_ptr_->get_address(); } vmat_file_map(const vmat_file_map&); // prevent copying diff --git a/dynadjust/include/parameters/dnaconsts-datums.hpp b/dynadjust/include/parameters/dnaconsts-datums.hpp index 86fa177b1..48659d030 100644 --- a/dynadjust/include/parameters/dnaconsts-datums.hpp +++ b/dynadjust/include/parameters/dnaconsts-datums.hpp @@ -104,9 +104,15 @@ const UINT16 NAD83_CSRS_V6_i_xyz = 8250; // XYZ const UINT16 NAD83_CSRS_V7_i = 8254; //LatLonEht const UINT16 NAD83_CSRS_V7_i_xyz = 8253; // XYZ const UINT16 NAD83_CSRS_V8_i = 10413; // LatLonEht -const UINT16 NAD83_CSRS_V8_i_xyz = 10412; // XYZ - +const UINT16 NAD83_CSRS_V8_i_xyz = 10412; // XYZ +const UINT16 YACARE_ROUUSAMS_i = 4309; // LatLon (2D) only +const UINT16 SIRGAS_ROU98_i_xyz = 5379; // XYZ +const UINT16 SIRGAS_ROU98_i = 5380; // LatLonEht +const UINT16 SIRGAS_1995_i_xyz = 4974; // XYZ +const UINT16 SIRGAS_1995_i = 4975; // LatLonEht +const UINT16 SIRGAS_2000_i_xyz = 4988; // XYZ +const UINT16 SIRGAS_2000_i = 4989; // LatLonEht const char* const AGD66_c = "4202"; const char* const AGD84_c = "4203"; @@ -154,7 +160,13 @@ const char* const NAD83_CSRS_v6_c = "8250"; const char* const NAD83_CSRS_v7_c = "8253"; const char* const NAD83_CSRS_v8_c = "10412"; -const char* const AGD66_epoch = "01.01.1966"; +const char* const YACARE_ROUUSAMS_c = "4309"; +// epsg strings for SIRGAS provide XYZ definition only +const char* const SIRGAS_ROU98_c = "5379"; +const char* const SIRGAS_1995_c = "4974"; +const char* const SIRGAS_2000_c = "4988"; + +const char* const AGD66_epoch = "01.01.1966"; const char* const AGD84_epoch = "01.01.1984"; const char* const GDA94_epoch = "01.01.1994"; const char* const GDA2020_epoch = "01.01.2020"; @@ -193,6 +205,11 @@ const char* const NAD83_CSRS_V6_epoch = "01.01.2010"; const char* const NAD83_CSRS_V7_epoch = "01.01.2010"; const char* const NAD83_CSRS_V8_epoch = "01.01.2010"; +const char* const YACARE_ROUUSAMS_epoch = "01.01.1963"; // Yacare ROU by U.S. Army Map Service was established in 1963. +const char* const SIRGAS_ROU98_epoch = "03.07.1995"; // 1995.5 +const char* const SIRGAS_1995_epoch = "01.04.1995"; // 1995.4 +const char* const SIRGAS_2000_epoch = "01.04.2000"; // 2000.4 + const char* const AGD66_s = "AGD66"; const char* const AGD84_s = "AGD84"; const char* const GDA94_s = "GDA94"; @@ -280,5 +297,15 @@ const char* const NAD83_CSRS_V8_alias1_s = "NAD83 (CSRS) v8"; const char* const NAD83_CSRS_V8_alias2_s = "NAD83(CSRS)V8"; const char* const NAD83_CSRS_V8_alias3_s = "NAD83 (CSRS) V8"; +const char* const YACARE_ROUUSAMS_s = "YACARE ROUUSAMS"; // Yacare ROU by U.S. Army Map Service was established in 1963. +const char* const YACARE_ROUUSAMS_alias_s = "YACARE-ROUUSAMS"; +const char* const SIRGAS_ROU98_s = "SIRGAS ROU98"; +const char* const SIRGAS_ROU98_alias_s = "SIRGAS-ROU98"; +const char* const SIRGAS_1995_s = "SIRGAS95"; +const char* const SIRGAS_1995_alias1_s = "SIRGAS 95"; +const char* const SIRGAS_1995_alias2_s = "SIRGAS 1995"; +const char* const SIRGAS_2000_s = "SIRGAS2000"; +const char* const SIRGAS_2000_alias_s = "SIRGAS 2000"; + #endif // DNACONSTS_DATUMS_HPP diff --git a/dynadjust/include/parameters/dnadatumprojectionparam.hpp b/dynadjust/include/parameters/dnadatumprojectionparam.hpp index 7d2006e00..fa9689ba4 100644 --- a/dynadjust/include/parameters/dnadatumprojectionparam.hpp +++ b/dynadjust/include/parameters/dnadatumprojectionparam.hpp @@ -35,16 +35,20 @@ #include // GRS80 parameters -const double GRS80_a = 6378137.0; // Semi major axis (a) -const double GRS80_inv_f = 298.257222101; // Inverse flattening (1/f) +const double GRS80_a = 6378137.0; // Semi major axis (a) +const double GRS80_inv_f = 298.257222101; // Inverse flattening (1/f) // WGS84 parameters -const double WGS84_a = 6378137.0; // Semi major axis (a) -const double WGS84_inv_f = 298.25722360; // Inverse flattening (1/f) +const double WGS84_a = 6378137.0; // Semi major axis (a) +const double WGS84_inv_f = 298.25722360; // Inverse flattening (1/f) // ANS parameters const double ANS_a = 6378160.0; // Semi major axis (a) -const double ANS_inv_f = 298.25; // Inverse flattening (1/f) +const double ANS_inv_f = 298.25; // Inverse flattening (1/f) + +// International 1924 parameters +const double International24_a = 6378388.0; // Semi major axis (a) +const double International24_inv_f = 297.0; // Inverse flattening (1/f) // UTM parameters const double FALSE_E = 500000.0; // False Easting diff --git a/dynadjust/include/parameters/dnaepsg.hpp b/dynadjust/include/parameters/dnaepsg.hpp index 6dd36cd05..dfc9b9da4 100644 --- a/dynadjust/include/parameters/dnaepsg.hpp +++ b/dynadjust/include/parameters/dnaepsg.hpp @@ -218,6 +218,19 @@ U epsgCodeFromName(const S& datumName) iequals(datumName, NAD83_CSRS_V8_alias2_s) || iequals(datumName, NAD83_CSRS_V8_alias3_s)) return NAD83_CSRS_V8_i_xyz; + // SIRGAS + if (iequals(datumName, YACARE_ROUUSAMS_s)) + return YACARE_ROUUSAMS_i; + if (iequals(datumName, SIRGAS_ROU98_s) || + iequals(datumName, SIRGAS_ROU98_alias_s)) + return SIRGAS_ROU98_i_xyz; + if (iequals(datumName, SIRGAS_1995_s) || + iequals(datumName, SIRGAS_1995_alias1_s) || + iequals(datumName, SIRGAS_1995_alias2_s)) + return SIRGAS_1995_i_xyz; + if (iequals(datumName, SIRGAS_2000_s) || + iequals(datumName, SIRGAS_2000_alias_s)) + return SIRGAS_2000_i_xyz; std::stringstream ss; ss << " epsgCodeFromName: '" << datumName << "' is not a supported reference frame label." << std::endl; @@ -341,6 +354,18 @@ S epsgStringFromName(const S& datumName) case NAD83_CSRS_V8_i: case NAD83_CSRS_V8_i_xyz: return NAD83_CSRS_v8_c; + // SIRGAS + case YACARE_ROUUSAMS_i: + return YACARE_ROUUSAMS_c; + case SIRGAS_ROU98_i: + case SIRGAS_ROU98_i_xyz: + return SIRGAS_ROU98_c; + case SIRGAS_1995_i: + case SIRGAS_1995_i_xyz: + return SIRGAS_1995_c; + case SIRGAS_2000_i: + case SIRGAS_2000_i_xyz: + return SIRGAS_2000_c; } std::stringstream ss; @@ -388,6 +413,14 @@ bool isEpsgDatumStatic(const U& epsgCode) case NAD83_CSRS_V7_i_xyz: case NAD83_CSRS_V8_i: case NAD83_CSRS_V8_i_xyz: + //SIRGAS + case YACARE_ROUUSAMS_i: + case SIRGAS_ROU98_i: + case SIRGAS_ROU98_i_xyz: + case SIRGAS_1995_i: + case SIRGAS_1995_i_xyz: + case SIRGAS_2000_i: + case SIRGAS_2000_i_xyz: return true; // ITRF.... case ITRF1988_i_xyz: @@ -516,6 +549,13 @@ void spheroidFromEpsgCode(const U& epsgCode, epsg_spheroid& ellipsoid) case NAD83_CSRS_V7_i: case NAD83_CSRS_V8_i_xyz: case NAD83_CSRS_V8_i: + // SIRGAS + case SIRGAS_ROU98_i: // Note: epsg.org has incorrectly assigned WGS 84 ellipsoid to SIRGAS ROU98 + case SIRGAS_ROU98_i_xyz: + case SIRGAS_1995_i: + case SIRGAS_1995_i_xyz: + case SIRGAS_2000_i: + case SIRGAS_2000_i_xyz: // authority ellipsoid.authority_.first = "EPSG"; ellipsoid.authority_.second = "7019"; @@ -524,6 +564,16 @@ void spheroidFromEpsgCode(const U& epsgCode, epsg_spheroid& ellipsoid) ellipsoid.name_ = "GRS 1980"; ellipsoid.semi_major_ = GRS80_a; break; + // SIRGAS (OLD) + case YACARE_ROUUSAMS_i: + // authority + ellipsoid.authority_.first = "EPSG"; + ellipsoid.authority_.second = "7022"; + // ellipsoid params + ellipsoid.inv_flattening_ = International24_inv_f; + ellipsoid.name_ = "International 1924"; + ellipsoid.semi_major_ = International24_a; + break; // WGS84 case WGS84_transit_i: case WGS84_transit_i_xyz: @@ -670,6 +720,18 @@ std::string referenceepochFromEpsgCode(const U& epsgCode) case NAD83_CSRS_V8_i_xyz: case NAD83_CSRS_V8_i: return NAD83_CSRS_V8_epoch; + // SIRGAS + case YACARE_ROUUSAMS_i: + return YACARE_ROUUSAMS_epoch; + case SIRGAS_ROU98_i: + case SIRGAS_ROU98_i_xyz: + return SIRGAS_ROU98_epoch; + case SIRGAS_1995_i: + case SIRGAS_1995_i_xyz: + return SIRGAS_1995_epoch; + case SIRGAS_2000_i: + case SIRGAS_2000_i_xyz: + return SIRGAS_2000_epoch; default: std::stringstream ss; ss << " referenceepochFromEpsgCode: EPSG code '" << epsgCode << "' is not a supported EPSG code." << std::endl; @@ -799,6 +861,17 @@ S datumFromEpsgCode(const U& epsgCode) case NAD83_CSRS_V8_i_xyz: case NAD83_CSRS_V8_i: return NAD83_CSRS_V8_s; + case YACARE_ROUUSAMS_i: + return YACARE_ROUUSAMS_s; + case SIRGAS_ROU98_i_xyz: + case SIRGAS_ROU98_i: + return SIRGAS_ROU98_s; + case SIRGAS_1995_i_xyz: + case SIRGAS_1995_i: + return SIRGAS_1995_s; + case SIRGAS_2000_i_xyz: + case SIRGAS_2000_i: + return SIRGAS_2000_s; default: std::stringstream ss; ss << " datumFromEpsgCode: EPSG code '" << epsgCode << "' is not a supported EPSG code." << std::endl; @@ -908,6 +981,14 @@ bool validateEpsgCode(const U& epsgCode) case NAD83_CSRS_V7_i: case NAD83_CSRS_V8_i_xyz: case NAD83_CSRS_V8_i: + // SIRGAS + case YACARE_ROUUSAMS_i: + case SIRGAS_ROU98_i_xyz: + case SIRGAS_ROU98_i: + case SIRGAS_1995_i_xyz: + case SIRGAS_1995_i: + case SIRGAS_2000_i_xyz: + case SIRGAS_2000_i: return true; default: std::stringstream ss; diff --git a/dynadjust/include/parameters/dnaframesubstitutions.hpp b/dynadjust/include/parameters/dnaframesubstitutions.hpp index 8a8fb1ade..a2de4275f 100644 --- a/dynadjust/include/parameters/dnaframesubstitutions.hpp +++ b/dynadjust/include/parameters/dnaframesubstitutions.hpp @@ -381,6 +381,39 @@ class WGS84_ITRF2014 : public frame_substitutions_t virtual ~WGS84_ITRF2014() {} }; +// SIRGAS95 to ITRF94 +template +class SIRGAS95_ITRF94 : public frame_substitutions_t { + public: + SIRGAS95_ITRF94() { + frame_substitutions::frame_name = SIRGAS_1995_s; + frame_substitutions::frame_epsg = SIRGAS_1995_i_xyz; + frame_substitutions::frame_desc = ""; + frame_substitutions::substitute_name = ITRF1994_s; + frame_substitutions::substitute_epsg = ITRF1994_i_xyz; + + frame_substitutions::from_epoch = dateFromString("01.01.1900"); + frame_substitutions::to_epoch = boost::gregorian::day_clock::local_day() + boost::gregorian::years(100); + }; + virtual ~SIRGAS95_ITRF94() {} +}; + +// SIRGAS2000 to ITRF2000 +template +class SIRGAS2000_ITRF2000 : public frame_substitutions_t { + public: + SIRGAS2000_ITRF2000() { + frame_substitutions::frame_name = SIRGAS_2000_s; + frame_substitutions::frame_epsg = SIRGAS_2000_i_xyz; + frame_substitutions::frame_desc = ""; + frame_substitutions::substitute_name = ITRF2000_s; + frame_substitutions::substitute_epsg = ITRF2000_i_xyz; + + frame_substitutions::from_epoch = dateFromString("01.01.1900"); + frame_substitutions::to_epoch = boost::gregorian::day_clock::local_day() + boost::gregorian::years(100); + }; + virtual ~SIRGAS2000_ITRF2000() {} +}; // FUNCTIONS diff --git a/dynadjust/include/thirdparty/nlohmann/json.hpp b/dynadjust/include/thirdparty/nlohmann/json.hpp new file mode 100644 index 000000000..8b72ea653 --- /dev/null +++ b/dynadjust/include/thirdparty/nlohmann/json.hpp @@ -0,0 +1,24765 @@ +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + +/****************************************************************************\ + * Note on documentation: The source files contain links to the online * + * documentation of the public API at https://json.nlohmann.me. This URL * + * contains the most recent documentation and should also be applicable to * + * previous versions; documentation for deprecated functions is not * + * removed, but marked deprecated. See "Generate documentation" section in * + * file docs/README.md. * +\****************************************************************************/ + +#ifndef INCLUDE_NLOHMANN_JSON_HPP_ +#define INCLUDE_NLOHMANN_JSON_HPP_ + +#include // all_of, find, for_each +#include // nullptr_t, ptrdiff_t, size_t +#include // hash, less +#include // initializer_list +#ifndef JSON_NO_IO + #include // istream, ostream +#endif // JSON_NO_IO +#include // random_access_iterator_tag +#include // unique_ptr +#include // string, stoi, to_string +#include // declval, forward, move, pair, swap +#include // vector + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// This file contains all macro definitions affecting or depending on the ABI + +#ifndef JSON_SKIP_LIBRARY_VERSION_CHECK + #if defined(NLOHMANN_JSON_VERSION_MAJOR) && defined(NLOHMANN_JSON_VERSION_MINOR) && defined(NLOHMANN_JSON_VERSION_PATCH) + #if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 11 || NLOHMANN_JSON_VERSION_PATCH != 3 + #warning "Already included a different version of the library!" + #endif + #endif +#endif + +#define NLOHMANN_JSON_VERSION_MAJOR 3 // NOLINT(modernize-macro-to-enum) +#define NLOHMANN_JSON_VERSION_MINOR 11 // NOLINT(modernize-macro-to-enum) +#define NLOHMANN_JSON_VERSION_PATCH 3 // NOLINT(modernize-macro-to-enum) + +#ifndef JSON_DIAGNOSTICS + #define JSON_DIAGNOSTICS 0 +#endif + +#ifndef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON + #define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0 +#endif + +#if JSON_DIAGNOSTICS + #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag +#else + #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS +#endif + +#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON + #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON _ldvcmp +#else + #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON +#endif + +#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION + #define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0 +#endif + +// Construct the namespace ABI tags component +#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) json_abi ## a ## b +#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b) \ + NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) + +#define NLOHMANN_JSON_ABI_TAGS \ + NLOHMANN_JSON_ABI_TAGS_CONCAT( \ + NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS, \ + NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON) + +// Construct the namespace version component +#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \ + _v ## major ## _ ## minor ## _ ## patch +#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(major, minor, patch) \ + NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) + +#if NLOHMANN_JSON_NAMESPACE_NO_VERSION +#define NLOHMANN_JSON_NAMESPACE_VERSION +#else +#define NLOHMANN_JSON_NAMESPACE_VERSION \ + NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(NLOHMANN_JSON_VERSION_MAJOR, \ + NLOHMANN_JSON_VERSION_MINOR, \ + NLOHMANN_JSON_VERSION_PATCH) +#endif + +// Combine namespace components +#define NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) a ## b +#define NLOHMANN_JSON_NAMESPACE_CONCAT(a, b) \ + NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) + +#ifndef NLOHMANN_JSON_NAMESPACE +#define NLOHMANN_JSON_NAMESPACE \ + nlohmann::NLOHMANN_JSON_NAMESPACE_CONCAT( \ + NLOHMANN_JSON_ABI_TAGS, \ + NLOHMANN_JSON_NAMESPACE_VERSION) +#endif + +#ifndef NLOHMANN_JSON_NAMESPACE_BEGIN +#define NLOHMANN_JSON_NAMESPACE_BEGIN \ + namespace nlohmann \ + { \ + inline namespace NLOHMANN_JSON_NAMESPACE_CONCAT( \ + NLOHMANN_JSON_ABI_TAGS, \ + NLOHMANN_JSON_NAMESPACE_VERSION) \ + { +#endif + +#ifndef NLOHMANN_JSON_NAMESPACE_END +#define NLOHMANN_JSON_NAMESPACE_END \ + } /* namespace (inline namespace) NOLINT(readability/namespace) */ \ + } // namespace nlohmann +#endif + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // transform +#include // array +#include // forward_list +#include // inserter, front_inserter, end +#include // map +#include // string +#include // tuple, make_tuple +#include // is_arithmetic, is_same, is_enum, underlying_type, is_convertible +#include // unordered_map +#include // pair, declval +#include // valarray + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // nullptr_t +#include // exception +#if JSON_DIAGNOSTICS + #include // accumulate +#endif +#include // runtime_error +#include // to_string +#include // vector + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // array +#include // size_t +#include // uint8_t +#include // string + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // declval, pair +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +template struct make_void +{ + using type = void; +}; +template using void_t = typename make_void::type; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +// https://en.cppreference.com/w/cpp/experimental/is_detected +struct nonesuch +{ + nonesuch() = delete; + ~nonesuch() = delete; + nonesuch(nonesuch const&) = delete; + nonesuch(nonesuch const&&) = delete; + void operator=(nonesuch const&) = delete; + void operator=(nonesuch&&) = delete; +}; + +template class Op, + class... Args> +struct detector +{ + using value_t = std::false_type; + using type = Default; +}; + +template class Op, class... Args> +struct detector>, Op, Args...> +{ + using value_t = std::true_type; + using type = Op; +}; + +template class Op, class... Args> +using is_detected = typename detector::value_t; + +template class Op, class... Args> +struct is_detected_lazy : is_detected { }; + +template class Op, class... Args> +using detected_t = typename detector::type; + +template class Op, class... Args> +using detected_or = detector; + +template class Op, class... Args> +using detected_or_t = typename detected_or::type; + +template class Op, class... Args> +using is_detected_exact = std::is_same>; + +template class Op, class... Args> +using is_detected_convertible = + std::is_convertible, To>; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include + + +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2016-2021 Evan Nemerson +// SPDX-License-Identifier: MIT + +/* Hedley - https://nemequ.github.io/hedley + * Created by Evan Nemerson + */ + +#if !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < 15) +#if defined(JSON_HEDLEY_VERSION) + #undef JSON_HEDLEY_VERSION +#endif +#define JSON_HEDLEY_VERSION 15 + +#if defined(JSON_HEDLEY_STRINGIFY_EX) + #undef JSON_HEDLEY_STRINGIFY_EX +#endif +#define JSON_HEDLEY_STRINGIFY_EX(x) #x + +#if defined(JSON_HEDLEY_STRINGIFY) + #undef JSON_HEDLEY_STRINGIFY +#endif +#define JSON_HEDLEY_STRINGIFY(x) JSON_HEDLEY_STRINGIFY_EX(x) + +#if defined(JSON_HEDLEY_CONCAT_EX) + #undef JSON_HEDLEY_CONCAT_EX +#endif +#define JSON_HEDLEY_CONCAT_EX(a,b) a##b + +#if defined(JSON_HEDLEY_CONCAT) + #undef JSON_HEDLEY_CONCAT +#endif +#define JSON_HEDLEY_CONCAT(a,b) JSON_HEDLEY_CONCAT_EX(a,b) + +#if defined(JSON_HEDLEY_CONCAT3_EX) + #undef JSON_HEDLEY_CONCAT3_EX +#endif +#define JSON_HEDLEY_CONCAT3_EX(a,b,c) a##b##c + +#if defined(JSON_HEDLEY_CONCAT3) + #undef JSON_HEDLEY_CONCAT3 +#endif +#define JSON_HEDLEY_CONCAT3(a,b,c) JSON_HEDLEY_CONCAT3_EX(a,b,c) + +#if defined(JSON_HEDLEY_VERSION_ENCODE) + #undef JSON_HEDLEY_VERSION_ENCODE +#endif +#define JSON_HEDLEY_VERSION_ENCODE(major,minor,revision) (((major) * 1000000) + ((minor) * 1000) + (revision)) + +#if defined(JSON_HEDLEY_VERSION_DECODE_MAJOR) + #undef JSON_HEDLEY_VERSION_DECODE_MAJOR +#endif +#define JSON_HEDLEY_VERSION_DECODE_MAJOR(version) ((version) / 1000000) + +#if defined(JSON_HEDLEY_VERSION_DECODE_MINOR) + #undef JSON_HEDLEY_VERSION_DECODE_MINOR +#endif +#define JSON_HEDLEY_VERSION_DECODE_MINOR(version) (((version) % 1000000) / 1000) + +#if defined(JSON_HEDLEY_VERSION_DECODE_REVISION) + #undef JSON_HEDLEY_VERSION_DECODE_REVISION +#endif +#define JSON_HEDLEY_VERSION_DECODE_REVISION(version) ((version) % 1000) + +#if defined(JSON_HEDLEY_GNUC_VERSION) + #undef JSON_HEDLEY_GNUC_VERSION +#endif +#if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__) + #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) +#elif defined(__GNUC__) + #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, 0) +#endif + +#if defined(JSON_HEDLEY_GNUC_VERSION_CHECK) + #undef JSON_HEDLEY_GNUC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_GNUC_VERSION) + #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GNUC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_MSVC_VERSION) + #undef JSON_HEDLEY_MSVC_VERSION +#endif +#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000) && !defined(__ICL) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 10000000, (_MSC_FULL_VER % 10000000) / 100000, (_MSC_FULL_VER % 100000) / 100) +#elif defined(_MSC_FULL_VER) && !defined(__ICL) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 1000000, (_MSC_FULL_VER % 1000000) / 10000, (_MSC_FULL_VER % 10000) / 10) +#elif defined(_MSC_VER) && !defined(__ICL) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_VER / 100, _MSC_VER % 100, 0) +#endif + +#if defined(JSON_HEDLEY_MSVC_VERSION_CHECK) + #undef JSON_HEDLEY_MSVC_VERSION_CHECK +#endif +#if !defined(JSON_HEDLEY_MSVC_VERSION) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (0) +#elif defined(_MSC_VER) && (_MSC_VER >= 1400) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch))) +#elif defined(_MSC_VER) && (_MSC_VER >= 1200) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch))) +#else + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_VER >= ((major * 100) + (minor))) +#endif + +#if defined(JSON_HEDLEY_INTEL_VERSION) + #undef JSON_HEDLEY_INTEL_VERSION +#endif +#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && !defined(__ICL) + #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, __INTEL_COMPILER_UPDATE) +#elif defined(__INTEL_COMPILER) && !defined(__ICL) + #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0) +#endif + +#if defined(JSON_HEDLEY_INTEL_VERSION_CHECK) + #undef JSON_HEDLEY_INTEL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_INTEL_VERSION) + #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_INTEL_CL_VERSION) + #undef JSON_HEDLEY_INTEL_CL_VERSION +#endif +#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && defined(__ICL) + #define JSON_HEDLEY_INTEL_CL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER, __INTEL_COMPILER_UPDATE, 0) +#endif + +#if defined(JSON_HEDLEY_INTEL_CL_VERSION_CHECK) + #undef JSON_HEDLEY_INTEL_CL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_INTEL_CL_VERSION) + #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_CL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_PGI_VERSION) + #undef JSON_HEDLEY_PGI_VERSION +#endif +#if defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__) + #define JSON_HEDLEY_PGI_VERSION JSON_HEDLEY_VERSION_ENCODE(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__) +#endif + +#if defined(JSON_HEDLEY_PGI_VERSION_CHECK) + #undef JSON_HEDLEY_PGI_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_PGI_VERSION) + #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PGI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_SUNPRO_VERSION) + #undef JSON_HEDLEY_SUNPRO_VERSION +#endif +#if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), (((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf), (__SUNPRO_C & 0xf) * 10) +#elif defined(__SUNPRO_C) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_C >> 8) & 0xf, (__SUNPRO_C >> 4) & 0xf, (__SUNPRO_C) & 0xf) +#elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), (((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf), (__SUNPRO_CC & 0xf) * 10) +#elif defined(__SUNPRO_CC) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_CC >> 8) & 0xf, (__SUNPRO_CC >> 4) & 0xf, (__SUNPRO_CC) & 0xf) +#endif + +#if defined(JSON_HEDLEY_SUNPRO_VERSION_CHECK) + #undef JSON_HEDLEY_SUNPRO_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_SUNPRO_VERSION) + #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_SUNPRO_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) + #undef JSON_HEDLEY_EMSCRIPTEN_VERSION +#endif +#if defined(__EMSCRIPTEN__) + #define JSON_HEDLEY_EMSCRIPTEN_VERSION JSON_HEDLEY_VERSION_ENCODE(__EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__) +#endif + +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK) + #undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) + #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_EMSCRIPTEN_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_ARM_VERSION) + #undef JSON_HEDLEY_ARM_VERSION +#endif +#if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION) + #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCOMPILER_VERSION / 1000000, (__ARMCOMPILER_VERSION % 1000000) / 10000, (__ARMCOMPILER_VERSION % 10000) / 100) +#elif defined(__CC_ARM) && defined(__ARMCC_VERSION) + #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCC_VERSION / 1000000, (__ARMCC_VERSION % 1000000) / 10000, (__ARMCC_VERSION % 10000) / 100) +#endif + +#if defined(JSON_HEDLEY_ARM_VERSION_CHECK) + #undef JSON_HEDLEY_ARM_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_ARM_VERSION) + #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_ARM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_IBM_VERSION) + #undef JSON_HEDLEY_IBM_VERSION +#endif +#if defined(__ibmxl__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ibmxl_version__, __ibmxl_release__, __ibmxl_modification__) +#elif defined(__xlC__) && defined(__xlC_ver__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff) +#elif defined(__xlC__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, 0) +#endif + +#if defined(JSON_HEDLEY_IBM_VERSION_CHECK) + #undef JSON_HEDLEY_IBM_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_IBM_VERSION) + #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IBM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_VERSION) + #undef JSON_HEDLEY_TI_VERSION +#endif +#if \ + defined(__TI_COMPILER_VERSION__) && \ + ( \ + defined(__TMS470__) || defined(__TI_ARM__) || \ + defined(__MSP430__) || \ + defined(__TMS320C2000__) \ + ) +#if (__TI_COMPILER_VERSION__ >= 16000000) + #define JSON_HEDLEY_TI_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif +#endif + +#if defined(JSON_HEDLEY_TI_VERSION_CHECK) + #undef JSON_HEDLEY_TI_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_VERSION) + #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL2000_VERSION) + #undef JSON_HEDLEY_TI_CL2000_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C2000__) + #define JSON_HEDLEY_TI_CL2000_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL2000_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL2000_VERSION) + #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL2000_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL430_VERSION) + #undef JSON_HEDLEY_TI_CL430_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__MSP430__) + #define JSON_HEDLEY_TI_CL430_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL430_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL430_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL430_VERSION) + #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL430_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION) + #undef JSON_HEDLEY_TI_ARMCL_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && (defined(__TMS470__) || defined(__TI_ARM__)) + #define JSON_HEDLEY_TI_ARMCL_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION_CHECK) + #undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION) + #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_ARMCL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL6X_VERSION) + #undef JSON_HEDLEY_TI_CL6X_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C6X__) + #define JSON_HEDLEY_TI_CL6X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL6X_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL6X_VERSION) + #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL6X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL7X_VERSION) + #undef JSON_HEDLEY_TI_CL7X_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__C7000__) + #define JSON_HEDLEY_TI_CL7X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL7X_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL7X_VERSION) + #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL7X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION) + #undef JSON_HEDLEY_TI_CLPRU_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__PRU__) + #define JSON_HEDLEY_TI_CLPRU_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION) + #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CLPRU_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_CRAY_VERSION) + #undef JSON_HEDLEY_CRAY_VERSION +#endif +#if defined(_CRAYC) + #if defined(_RELEASE_PATCHLEVEL) + #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, _RELEASE_PATCHLEVEL) + #else + #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, 0) + #endif +#endif + +#if defined(JSON_HEDLEY_CRAY_VERSION_CHECK) + #undef JSON_HEDLEY_CRAY_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_CRAY_VERSION) + #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_CRAY_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_IAR_VERSION) + #undef JSON_HEDLEY_IAR_VERSION +#endif +#if defined(__IAR_SYSTEMS_ICC__) + #if __VER__ > 1000 + #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE((__VER__ / 1000000), ((__VER__ / 1000) % 1000), (__VER__ % 1000)) + #else + #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE(__VER__ / 100, __VER__ % 100, 0) + #endif +#endif + +#if defined(JSON_HEDLEY_IAR_VERSION_CHECK) + #undef JSON_HEDLEY_IAR_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_IAR_VERSION) + #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IAR_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TINYC_VERSION) + #undef JSON_HEDLEY_TINYC_VERSION +#endif +#if defined(__TINYC__) + #define JSON_HEDLEY_TINYC_VERSION JSON_HEDLEY_VERSION_ENCODE(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100) +#endif + +#if defined(JSON_HEDLEY_TINYC_VERSION_CHECK) + #undef JSON_HEDLEY_TINYC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TINYC_VERSION) + #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TINYC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_DMC_VERSION) + #undef JSON_HEDLEY_DMC_VERSION +#endif +#if defined(__DMC__) + #define JSON_HEDLEY_DMC_VERSION JSON_HEDLEY_VERSION_ENCODE(__DMC__ >> 8, (__DMC__ >> 4) & 0xf, __DMC__ & 0xf) +#endif + +#if defined(JSON_HEDLEY_DMC_VERSION_CHECK) + #undef JSON_HEDLEY_DMC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_DMC_VERSION) + #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_DMC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_COMPCERT_VERSION) + #undef JSON_HEDLEY_COMPCERT_VERSION +#endif +#if defined(__COMPCERT_VERSION__) + #define JSON_HEDLEY_COMPCERT_VERSION JSON_HEDLEY_VERSION_ENCODE(__COMPCERT_VERSION__ / 10000, (__COMPCERT_VERSION__ / 100) % 100, __COMPCERT_VERSION__ % 100) +#endif + +#if defined(JSON_HEDLEY_COMPCERT_VERSION_CHECK) + #undef JSON_HEDLEY_COMPCERT_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_COMPCERT_VERSION) + #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_COMPCERT_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_PELLES_VERSION) + #undef JSON_HEDLEY_PELLES_VERSION +#endif +#if defined(__POCC__) + #define JSON_HEDLEY_PELLES_VERSION JSON_HEDLEY_VERSION_ENCODE(__POCC__ / 100, __POCC__ % 100, 0) +#endif + +#if defined(JSON_HEDLEY_PELLES_VERSION_CHECK) + #undef JSON_HEDLEY_PELLES_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_PELLES_VERSION) + #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PELLES_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_MCST_LCC_VERSION) + #undef JSON_HEDLEY_MCST_LCC_VERSION +#endif +#if defined(__LCC__) && defined(__LCC_MINOR__) + #define JSON_HEDLEY_MCST_LCC_VERSION JSON_HEDLEY_VERSION_ENCODE(__LCC__ / 100, __LCC__ % 100, __LCC_MINOR__) +#endif + +#if defined(JSON_HEDLEY_MCST_LCC_VERSION_CHECK) + #undef JSON_HEDLEY_MCST_LCC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_MCST_LCC_VERSION) + #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_MCST_LCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_GCC_VERSION) + #undef JSON_HEDLEY_GCC_VERSION +#endif +#if \ + defined(JSON_HEDLEY_GNUC_VERSION) && \ + !defined(__clang__) && \ + !defined(JSON_HEDLEY_INTEL_VERSION) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_ARM_VERSION) && \ + !defined(JSON_HEDLEY_CRAY_VERSION) && \ + !defined(JSON_HEDLEY_TI_VERSION) && \ + !defined(JSON_HEDLEY_TI_ARMCL_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL430_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL2000_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL6X_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL7X_VERSION) && \ + !defined(JSON_HEDLEY_TI_CLPRU_VERSION) && \ + !defined(__COMPCERT__) && \ + !defined(JSON_HEDLEY_MCST_LCC_VERSION) + #define JSON_HEDLEY_GCC_VERSION JSON_HEDLEY_GNUC_VERSION +#endif + +#if defined(JSON_HEDLEY_GCC_VERSION_CHECK) + #undef JSON_HEDLEY_GCC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_GCC_VERSION) + #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_ATTRIBUTE +#endif +#if \ + defined(__has_attribute) && \ + ( \ + (!defined(JSON_HEDLEY_IAR_VERSION) || JSON_HEDLEY_IAR_VERSION_CHECK(8,5,9)) \ + ) +# define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) __has_attribute(attribute) +#else +# define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) + #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) + #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE +#endif +#if \ + defined(__has_cpp_attribute) && \ + defined(__cplusplus) && \ + (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS) + #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS +#endif +#if !defined(__cplusplus) || !defined(__has_cpp_attribute) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) +#elif \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_IAR_VERSION) && \ + (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) && \ + (!defined(JSON_HEDLEY_MSVC_VERSION) || JSON_HEDLEY_MSVC_VERSION_CHECK(19,20,0)) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(ns::attribute) +#else + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) + #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) + #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_BUILTIN) + #undef JSON_HEDLEY_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_HAS_BUILTIN(builtin) __has_builtin(builtin) +#else + #define JSON_HEDLEY_HAS_BUILTIN(builtin) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_BUILTIN) + #undef JSON_HEDLEY_GNUC_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) +#else + #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_BUILTIN) + #undef JSON_HEDLEY_GCC_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) +#else + #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_FEATURE) + #undef JSON_HEDLEY_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_HAS_FEATURE(feature) __has_feature(feature) +#else + #define JSON_HEDLEY_HAS_FEATURE(feature) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_FEATURE) + #undef JSON_HEDLEY_GNUC_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) +#else + #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_FEATURE) + #undef JSON_HEDLEY_GCC_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) +#else + #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_EXTENSION) + #undef JSON_HEDLEY_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_HAS_EXTENSION(extension) __has_extension(extension) +#else + #define JSON_HEDLEY_HAS_EXTENSION(extension) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_EXTENSION) + #undef JSON_HEDLEY_GNUC_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) +#else + #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_EXTENSION) + #undef JSON_HEDLEY_GCC_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) +#else + #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_WARNING) + #undef JSON_HEDLEY_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_HAS_WARNING(warning) __has_warning(warning) +#else + #define JSON_HEDLEY_HAS_WARNING(warning) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_WARNING) + #undef JSON_HEDLEY_GNUC_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) +#else + #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_WARNING) + #undef JSON_HEDLEY_GCC_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) +#else + #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ + defined(__clang__) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,17) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(8,0,0) || \ + (JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) && defined(__C99_PRAGMA_OPERATOR)) + #define JSON_HEDLEY_PRAGMA(value) _Pragma(#value) +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_PRAGMA(value) __pragma(value) +#else + #define JSON_HEDLEY_PRAGMA(value) +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_PUSH) + #undef JSON_HEDLEY_DIAGNOSTIC_PUSH +#endif +#if defined(JSON_HEDLEY_DIAGNOSTIC_POP) + #undef JSON_HEDLEY_DIAGNOSTIC_POP +#endif +#if defined(__clang__) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("clang diagnostic push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("clang diagnostic pop") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop") +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH __pragma(warning(push)) + #define JSON_HEDLEY_DIAGNOSTIC_POP __pragma(warning(pop)) +#elif JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("pop") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,4,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("diag_push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("diag_pop") +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") +#else + #define JSON_HEDLEY_DIAGNOSTIC_PUSH + #define JSON_HEDLEY_DIAGNOSTIC_POP +#endif + +/* JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ is for + HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ +#endif +#if defined(__cplusplus) +# if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat") +# if JSON_HEDLEY_HAS_WARNING("-Wc++17-extensions") +# if JSON_HEDLEY_HAS_WARNING("-Wc++1z-extensions") +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ + _Pragma("clang diagnostic ignored \"-Wc++1z-extensions\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# endif +# else +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# endif +# endif +#endif +#if !defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(x) x +#endif + +#if defined(JSON_HEDLEY_CONST_CAST) + #undef JSON_HEDLEY_CONST_CAST +#endif +#if defined(__cplusplus) +# define JSON_HEDLEY_CONST_CAST(T, expr) (const_cast(expr)) +#elif \ + JSON_HEDLEY_HAS_WARNING("-Wcast-qual") || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_CONST_CAST(T, expr) (__extension__ ({ \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL \ + ((T) (expr)); \ + JSON_HEDLEY_DIAGNOSTIC_POP \ + })) +#else +# define JSON_HEDLEY_CONST_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_REINTERPRET_CAST) + #undef JSON_HEDLEY_REINTERPRET_CAST +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) (reinterpret_cast(expr)) +#else + #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_STATIC_CAST) + #undef JSON_HEDLEY_STATIC_CAST +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_STATIC_CAST(T, expr) (static_cast(expr)) +#else + #define JSON_HEDLEY_STATIC_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_CPP_CAST) + #undef JSON_HEDLEY_CPP_CAST +#endif +#if defined(__cplusplus) +# if JSON_HEDLEY_HAS_WARNING("-Wold-style-cast") +# define JSON_HEDLEY_CPP_CAST(T, expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wold-style-cast\"") \ + ((T) (expr)) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# elif JSON_HEDLEY_IAR_VERSION_CHECK(8,3,0) +# define JSON_HEDLEY_CPP_CAST(T, expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("diag_suppress=Pe137") \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_CPP_CAST(T, expr) ((T) (expr)) +# endif +#else +# define JSON_HEDLEY_CPP_CAST(T, expr) (expr) +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wdeprecated-declarations") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warning(disable:1478 1786)") +#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:1478 1786)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1216,1444,1445") +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:4996)) +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1291,1718") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && !defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,symdeprecated,symdeprecated2)") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress=Pe1444,Pe1215") +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warn(disable:2241)") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("warning(disable:161)") +#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:161)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 1675") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("GCC diagnostic ignored \"-Wunknown-pragmas\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:4068)) +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(16,9,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress=Pe161") +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 161") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-attributes") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("clang diagnostic ignored \"-Wunknown-attributes\"") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("warning(disable:1292)") +#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:1292)) +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:5030)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097,1098") +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("error_messages(off,attrskipunsup)") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1173") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress=Pe1097") +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wcast-qual") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("clang diagnostic ignored \"-Wcast-qual\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("warning(disable:2203 2331)") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("GCC diagnostic ignored \"-Wcast-qual\"") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunused-function") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("clang diagnostic ignored \"-Wunused-function\"") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("GCC diagnostic ignored \"-Wunused-function\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(1,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION __pragma(warning(disable:4505)) +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("diag_suppress 3142") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION +#endif + +#if defined(JSON_HEDLEY_DEPRECATED) + #undef JSON_HEDLEY_DEPRECATED +#endif +#if defined(JSON_HEDLEY_DEPRECATED_FOR) + #undef JSON_HEDLEY_DEPRECATED_FOR +#endif +#if \ + JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated("Since " # since)) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated("Since " #since "; use " #replacement)) +#elif \ + (JSON_HEDLEY_HAS_EXTENSION(attribute_deprecated_with_message) && !defined(JSON_HEDLEY_IAR_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__("Since " #since))) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__("Since " #since "; use " #replacement))) +#elif defined(__cplusplus) && (__cplusplus >= 201402L) + #define JSON_HEDLEY_DEPRECATED(since) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since)]]) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since "; use " #replacement)]]) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(deprecated) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) + #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__)) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_PELLES_VERSION_CHECK(6,50,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated) +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DEPRECATED(since) _Pragma("deprecated") + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) _Pragma("deprecated") +#else + #define JSON_HEDLEY_DEPRECATED(since) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) +#endif + +#if defined(JSON_HEDLEY_UNAVAILABLE) + #undef JSON_HEDLEY_UNAVAILABLE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(warning) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_UNAVAILABLE(available_since) __attribute__((__warning__("Not available until " #available_since))) +#else + #define JSON_HEDLEY_UNAVAILABLE(available_since) +#endif + +#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT) + #undef JSON_HEDLEY_WARN_UNUSED_RESULT +#endif +#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT_MSG) + #undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(warn_unused_result) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__)) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) __attribute__((__warn_unused_result__)) +#elif (JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) >= 201907L) + #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard(msg)]]) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) + #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) +#elif defined(_Check_return_) /* SAL */ + #define JSON_HEDLEY_WARN_UNUSED_RESULT _Check_return_ + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) _Check_return_ +#else + #define JSON_HEDLEY_WARN_UNUSED_RESULT + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) +#endif + +#if defined(JSON_HEDLEY_SENTINEL) + #undef JSON_HEDLEY_SENTINEL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(sentinel) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_SENTINEL(position) __attribute__((__sentinel__(position))) +#else + #define JSON_HEDLEY_SENTINEL(position) +#endif + +#if defined(JSON_HEDLEY_NO_RETURN) + #undef JSON_HEDLEY_NO_RETURN +#endif +#if JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_NO_RETURN __noreturn +#elif \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L + #define JSON_HEDLEY_NO_RETURN _Noreturn +#elif defined(__cplusplus) && (__cplusplus >= 201103L) + #define JSON_HEDLEY_NO_RETURN JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[noreturn]]) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(noreturn) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,2,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) + #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_NO_RETURN _Pragma("does_not_return") +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_NO_RETURN __declspec(noreturn) +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) + #define JSON_HEDLEY_NO_RETURN _Pragma("FUNC_NEVER_RETURNS;") +#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) + #define JSON_HEDLEY_NO_RETURN __attribute((noreturn)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0) + #define JSON_HEDLEY_NO_RETURN __declspec(noreturn) +#else + #define JSON_HEDLEY_NO_RETURN +#endif + +#if defined(JSON_HEDLEY_NO_ESCAPE) + #undef JSON_HEDLEY_NO_ESCAPE +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(noescape) + #define JSON_HEDLEY_NO_ESCAPE __attribute__((__noescape__)) +#else + #define JSON_HEDLEY_NO_ESCAPE +#endif + +#if defined(JSON_HEDLEY_UNREACHABLE) + #undef JSON_HEDLEY_UNREACHABLE +#endif +#if defined(JSON_HEDLEY_UNREACHABLE_RETURN) + #undef JSON_HEDLEY_UNREACHABLE_RETURN +#endif +#if defined(JSON_HEDLEY_ASSUME) + #undef JSON_HEDLEY_ASSUME +#endif +#if \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_ASSUME(expr) __assume(expr) +#elif JSON_HEDLEY_HAS_BUILTIN(__builtin_assume) + #define JSON_HEDLEY_ASSUME(expr) __builtin_assume(expr) +#elif \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) + #if defined(__cplusplus) + #define JSON_HEDLEY_ASSUME(expr) std::_nassert(expr) + #else + #define JSON_HEDLEY_ASSUME(expr) _nassert(expr) + #endif +#endif +#if \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_unreachable) && (!defined(JSON_HEDLEY_ARM_VERSION))) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,10,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,5) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(10,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_UNREACHABLE() __builtin_unreachable() +#elif defined(JSON_HEDLEY_ASSUME) + #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) +#endif +#if !defined(JSON_HEDLEY_ASSUME) + #if defined(JSON_HEDLEY_UNREACHABLE) + #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, ((expr) ? 1 : (JSON_HEDLEY_UNREACHABLE(), 1))) + #else + #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, expr) + #endif +#endif +#if defined(JSON_HEDLEY_UNREACHABLE) + #if \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (JSON_HEDLEY_STATIC_CAST(void, JSON_HEDLEY_ASSUME(0)), (value)) + #else + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) JSON_HEDLEY_UNREACHABLE() + #endif +#else + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (value) +#endif +#if !defined(JSON_HEDLEY_UNREACHABLE) + #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) +#endif + +JSON_HEDLEY_DIAGNOSTIC_PUSH +#if JSON_HEDLEY_HAS_WARNING("-Wpedantic") + #pragma clang diagnostic ignored "-Wpedantic" +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat-pedantic") && defined(__cplusplus) + #pragma clang diagnostic ignored "-Wc++98-compat-pedantic" +#endif +#if JSON_HEDLEY_GCC_HAS_WARNING("-Wvariadic-macros",4,0,0) + #if defined(__clang__) + #pragma clang diagnostic ignored "-Wvariadic-macros" + #elif defined(JSON_HEDLEY_GCC_VERSION) + #pragma GCC diagnostic ignored "-Wvariadic-macros" + #endif +#endif +#if defined(JSON_HEDLEY_NON_NULL) + #undef JSON_HEDLEY_NON_NULL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(nonnull) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) + #define JSON_HEDLEY_NON_NULL(...) __attribute__((__nonnull__(__VA_ARGS__))) +#else + #define JSON_HEDLEY_NON_NULL(...) +#endif +JSON_HEDLEY_DIAGNOSTIC_POP + +#if defined(JSON_HEDLEY_PRINTF_FORMAT) + #undef JSON_HEDLEY_PRINTF_FORMAT +#endif +#if defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && !defined(__USE_MINGW_ANSI_STDIO) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(ms_printf, string_idx, first_to_check))) +#elif defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && defined(__USE_MINGW_ANSI_STDIO) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(gnu_printf, string_idx, first_to_check))) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(format) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(__printf__, string_idx, first_to_check))) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(6,0,0) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __declspec(vaformat(printf,string_idx,first_to_check)) +#else + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) +#endif + +#if defined(JSON_HEDLEY_CONSTEXPR) + #undef JSON_HEDLEY_CONSTEXPR +#endif +#if defined(__cplusplus) + #if __cplusplus >= 201103L + #define JSON_HEDLEY_CONSTEXPR JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(constexpr) + #endif +#endif +#if !defined(JSON_HEDLEY_CONSTEXPR) + #define JSON_HEDLEY_CONSTEXPR +#endif + +#if defined(JSON_HEDLEY_PREDICT) + #undef JSON_HEDLEY_PREDICT +#endif +#if defined(JSON_HEDLEY_LIKELY) + #undef JSON_HEDLEY_LIKELY +#endif +#if defined(JSON_HEDLEY_UNLIKELY) + #undef JSON_HEDLEY_UNLIKELY +#endif +#if defined(JSON_HEDLEY_UNPREDICTABLE) + #undef JSON_HEDLEY_UNPREDICTABLE +#endif +#if JSON_HEDLEY_HAS_BUILTIN(__builtin_unpredictable) + #define JSON_HEDLEY_UNPREDICTABLE(expr) __builtin_unpredictable((expr)) +#endif +#if \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect_with_probability) && !defined(JSON_HEDLEY_PGI_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(9,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PREDICT(expr, value, probability) __builtin_expect_with_probability( (expr), (value), (probability)) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) __builtin_expect_with_probability(!!(expr), 1 , (probability)) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) __builtin_expect_with_probability(!!(expr), 0 , (probability)) +# define JSON_HEDLEY_LIKELY(expr) __builtin_expect (!!(expr), 1 ) +# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect (!!(expr), 0 ) +#elif \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,27) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PREDICT(expr, expected, probability) \ + (((probability) >= 0.9) ? __builtin_expect((expr), (expected)) : (JSON_HEDLEY_STATIC_CAST(void, expected), (expr))) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) \ + (__extension__ ({ \ + double hedley_probability_ = (probability); \ + ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 1) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 0) : !!(expr))); \ + })) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) \ + (__extension__ ({ \ + double hedley_probability_ = (probability); \ + ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 0) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 1) : !!(expr))); \ + })) +# define JSON_HEDLEY_LIKELY(expr) __builtin_expect(!!(expr), 1) +# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0) +#else +# define JSON_HEDLEY_PREDICT(expr, expected, probability) (JSON_HEDLEY_STATIC_CAST(void, expected), (expr)) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) (!!(expr)) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) (!!(expr)) +# define JSON_HEDLEY_LIKELY(expr) (!!(expr)) +# define JSON_HEDLEY_UNLIKELY(expr) (!!(expr)) +#endif +#if !defined(JSON_HEDLEY_UNPREDICTABLE) + #define JSON_HEDLEY_UNPREDICTABLE(expr) JSON_HEDLEY_PREDICT(expr, 1, 0.5) +#endif + +#if defined(JSON_HEDLEY_MALLOC) + #undef JSON_HEDLEY_MALLOC +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(malloc) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_MALLOC __attribute__((__malloc__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_MALLOC _Pragma("returns_new_memory") +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_MALLOC __declspec(restrict) +#else + #define JSON_HEDLEY_MALLOC +#endif + +#if defined(JSON_HEDLEY_PURE) + #undef JSON_HEDLEY_PURE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(pure) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(2,96,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PURE __attribute__((__pure__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) +# define JSON_HEDLEY_PURE _Pragma("does_not_write_global_data") +#elif defined(__cplusplus) && \ + ( \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) \ + ) +# define JSON_HEDLEY_PURE _Pragma("FUNC_IS_PURE;") +#else +# define JSON_HEDLEY_PURE +#endif + +#if defined(JSON_HEDLEY_CONST) + #undef JSON_HEDLEY_CONST +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(const) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(2,5,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_CONST __attribute__((__const__)) +#elif \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_CONST _Pragma("no_side_effect") +#else + #define JSON_HEDLEY_CONST JSON_HEDLEY_PURE +#endif + +#if defined(JSON_HEDLEY_RESTRICT) + #undef JSON_HEDLEY_RESTRICT +#endif +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__cplusplus) + #define JSON_HEDLEY_RESTRICT restrict +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,4) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ + defined(__clang__) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_RESTRICT __restrict +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,3,0) && !defined(__cplusplus) + #define JSON_HEDLEY_RESTRICT _Restrict +#else + #define JSON_HEDLEY_RESTRICT +#endif + +#if defined(JSON_HEDLEY_INLINE) + #undef JSON_HEDLEY_INLINE +#endif +#if \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ + (defined(__cplusplus) && (__cplusplus >= 199711L)) + #define JSON_HEDLEY_INLINE inline +#elif \ + defined(JSON_HEDLEY_GCC_VERSION) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(6,2,0) + #define JSON_HEDLEY_INLINE __inline__ +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,1,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_INLINE __inline +#else + #define JSON_HEDLEY_INLINE +#endif + +#if defined(JSON_HEDLEY_ALWAYS_INLINE) + #undef JSON_HEDLEY_ALWAYS_INLINE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(always_inline) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) +# define JSON_HEDLEY_ALWAYS_INLINE __attribute__((__always_inline__)) JSON_HEDLEY_INLINE +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) +# define JSON_HEDLEY_ALWAYS_INLINE __forceinline +#elif defined(__cplusplus) && \ + ( \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) \ + ) +# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("FUNC_ALWAYS_INLINE;") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) +# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("inline=forced") +#else +# define JSON_HEDLEY_ALWAYS_INLINE JSON_HEDLEY_INLINE +#endif + +#if defined(JSON_HEDLEY_NEVER_INLINE) + #undef JSON_HEDLEY_NEVER_INLINE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(noinline) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) + #define JSON_HEDLEY_NEVER_INLINE __attribute__((__noinline__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(10,2,0) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("noinline") +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("FUNC_CANNOT_INLINE;") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("inline=never") +#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) + #define JSON_HEDLEY_NEVER_INLINE __attribute((noinline)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0) + #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) +#else + #define JSON_HEDLEY_NEVER_INLINE +#endif + +#if defined(JSON_HEDLEY_PRIVATE) + #undef JSON_HEDLEY_PRIVATE +#endif +#if defined(JSON_HEDLEY_PUBLIC) + #undef JSON_HEDLEY_PUBLIC +#endif +#if defined(JSON_HEDLEY_IMPORT) + #undef JSON_HEDLEY_IMPORT +#endif +#if defined(_WIN32) || defined(__CYGWIN__) +# define JSON_HEDLEY_PRIVATE +# define JSON_HEDLEY_PUBLIC __declspec(dllexport) +# define JSON_HEDLEY_IMPORT __declspec(dllimport) +#else +# if \ + JSON_HEDLEY_HAS_ATTRIBUTE(visibility) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + ( \ + defined(__TI_EABI__) && \ + ( \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) \ + ) \ + ) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PRIVATE __attribute__((__visibility__("hidden"))) +# define JSON_HEDLEY_PUBLIC __attribute__((__visibility__("default"))) +# else +# define JSON_HEDLEY_PRIVATE +# define JSON_HEDLEY_PUBLIC +# endif +# define JSON_HEDLEY_IMPORT extern +#endif + +#if defined(JSON_HEDLEY_NO_THROW) + #undef JSON_HEDLEY_NO_THROW +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(nothrow) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_NO_THROW __attribute__((__nothrow__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) + #define JSON_HEDLEY_NO_THROW __declspec(nothrow) +#else + #define JSON_HEDLEY_NO_THROW +#endif + +#if defined(JSON_HEDLEY_FALL_THROUGH) + #undef JSON_HEDLEY_FALL_THROUGH +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(fallthrough) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(7,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_FALL_THROUGH __attribute__((__fallthrough__)) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(clang,fallthrough) + #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[clang::fallthrough]]) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(fallthrough) + #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[fallthrough]]) +#elif defined(__fallthrough) /* SAL */ + #define JSON_HEDLEY_FALL_THROUGH __fallthrough +#else + #define JSON_HEDLEY_FALL_THROUGH +#endif + +#if defined(JSON_HEDLEY_RETURNS_NON_NULL) + #undef JSON_HEDLEY_RETURNS_NON_NULL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(returns_nonnull) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_RETURNS_NON_NULL __attribute__((__returns_nonnull__)) +#elif defined(_Ret_notnull_) /* SAL */ + #define JSON_HEDLEY_RETURNS_NON_NULL _Ret_notnull_ +#else + #define JSON_HEDLEY_RETURNS_NON_NULL +#endif + +#if defined(JSON_HEDLEY_ARRAY_PARAM) + #undef JSON_HEDLEY_ARRAY_PARAM +#endif +#if \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ + !defined(__STDC_NO_VLA__) && \ + !defined(__cplusplus) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_TINYC_VERSION) + #define JSON_HEDLEY_ARRAY_PARAM(name) (name) +#else + #define JSON_HEDLEY_ARRAY_PARAM(name) +#endif + +#if defined(JSON_HEDLEY_IS_CONSTANT) + #undef JSON_HEDLEY_IS_CONSTANT +#endif +#if defined(JSON_HEDLEY_REQUIRE_CONSTEXPR) + #undef JSON_HEDLEY_REQUIRE_CONSTEXPR +#endif +/* JSON_HEDLEY_IS_CONSTEXPR_ is for + HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ +#if defined(JSON_HEDLEY_IS_CONSTEXPR_) + #undef JSON_HEDLEY_IS_CONSTEXPR_ +#endif +#if \ + JSON_HEDLEY_HAS_BUILTIN(__builtin_constant_p) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,19) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) && !defined(__cplusplus)) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_IS_CONSTANT(expr) __builtin_constant_p(expr) +#endif +#if !defined(__cplusplus) +# if \ + JSON_HEDLEY_HAS_BUILTIN(__builtin_types_compatible_p) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,24) +#if defined(__INTPTR_TYPE__) + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0)), int*) +#else + #include + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((intptr_t) ((expr) * 0)) : (int*) 0)), int*) +#endif +# elif \ + ( \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \ + !defined(JSON_HEDLEY_SUNPRO_VERSION) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_IAR_VERSION)) || \ + (JSON_HEDLEY_HAS_EXTENSION(c_generic_selections) && !defined(JSON_HEDLEY_IAR_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,3,0) +#if defined(__INTPTR_TYPE__) + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0), int*: 1, void*: 0) +#else + #include + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((intptr_t) * 0) : (int*) 0), int*: 1, void*: 0) +#endif +# elif \ + defined(JSON_HEDLEY_GCC_VERSION) || \ + defined(JSON_HEDLEY_INTEL_VERSION) || \ + defined(JSON_HEDLEY_TINYC_VERSION) || \ + defined(JSON_HEDLEY_TI_ARMCL_VERSION) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(18,12,0) || \ + defined(JSON_HEDLEY_TI_CL2000_VERSION) || \ + defined(JSON_HEDLEY_TI_CL6X_VERSION) || \ + defined(JSON_HEDLEY_TI_CL7X_VERSION) || \ + defined(JSON_HEDLEY_TI_CLPRU_VERSION) || \ + defined(__clang__) +# define JSON_HEDLEY_IS_CONSTEXPR_(expr) ( \ + sizeof(void) != \ + sizeof(*( \ + 1 ? \ + ((void*) ((expr) * 0L) ) : \ +((struct { char v[sizeof(void) * 2]; } *) 1) \ + ) \ + ) \ + ) +# endif +#endif +#if defined(JSON_HEDLEY_IS_CONSTEXPR_) + #if !defined(JSON_HEDLEY_IS_CONSTANT) + #define JSON_HEDLEY_IS_CONSTANT(expr) JSON_HEDLEY_IS_CONSTEXPR_(expr) + #endif + #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (JSON_HEDLEY_IS_CONSTEXPR_(expr) ? (expr) : (-1)) +#else + #if !defined(JSON_HEDLEY_IS_CONSTANT) + #define JSON_HEDLEY_IS_CONSTANT(expr) (0) + #endif + #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (expr) +#endif + +#if defined(JSON_HEDLEY_BEGIN_C_DECLS) + #undef JSON_HEDLEY_BEGIN_C_DECLS +#endif +#if defined(JSON_HEDLEY_END_C_DECLS) + #undef JSON_HEDLEY_END_C_DECLS +#endif +#if defined(JSON_HEDLEY_C_DECL) + #undef JSON_HEDLEY_C_DECL +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_BEGIN_C_DECLS extern "C" { + #define JSON_HEDLEY_END_C_DECLS } + #define JSON_HEDLEY_C_DECL extern "C" +#else + #define JSON_HEDLEY_BEGIN_C_DECLS + #define JSON_HEDLEY_END_C_DECLS + #define JSON_HEDLEY_C_DECL +#endif + +#if defined(JSON_HEDLEY_STATIC_ASSERT) + #undef JSON_HEDLEY_STATIC_ASSERT +#endif +#if \ + !defined(__cplusplus) && ( \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) || \ + (JSON_HEDLEY_HAS_FEATURE(c_static_assert) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(6,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + defined(_Static_assert) \ + ) +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) _Static_assert(expr, message) +#elif \ + (defined(__cplusplus) && (__cplusplus >= 201103L)) || \ + JSON_HEDLEY_MSVC_VERSION_CHECK(16,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(static_assert(expr, message)) +#else +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) +#endif + +#if defined(JSON_HEDLEY_NULL) + #undef JSON_HEDLEY_NULL +#endif +#if defined(__cplusplus) + #if __cplusplus >= 201103L + #define JSON_HEDLEY_NULL JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(nullptr) + #elif defined(NULL) + #define JSON_HEDLEY_NULL NULL + #else + #define JSON_HEDLEY_NULL JSON_HEDLEY_STATIC_CAST(void*, 0) + #endif +#elif defined(NULL) + #define JSON_HEDLEY_NULL NULL +#else + #define JSON_HEDLEY_NULL ((void*) 0) +#endif + +#if defined(JSON_HEDLEY_MESSAGE) + #undef JSON_HEDLEY_MESSAGE +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") +# define JSON_HEDLEY_MESSAGE(msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ + JSON_HEDLEY_PRAGMA(message msg) \ + JSON_HEDLEY_DIAGNOSTIC_POP +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message msg) +#elif JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(_CRI message msg) +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#else +# define JSON_HEDLEY_MESSAGE(msg) +#endif + +#if defined(JSON_HEDLEY_WARNING) + #undef JSON_HEDLEY_WARNING +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") +# define JSON_HEDLEY_WARNING(msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ + JSON_HEDLEY_PRAGMA(clang warning msg) \ + JSON_HEDLEY_DIAGNOSTIC_POP +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,8,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(GCC warning msg) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#else +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_MESSAGE(msg) +#endif + +#if defined(JSON_HEDLEY_REQUIRE) + #undef JSON_HEDLEY_REQUIRE +#endif +#if defined(JSON_HEDLEY_REQUIRE_MSG) + #undef JSON_HEDLEY_REQUIRE_MSG +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(diagnose_if) +# if JSON_HEDLEY_HAS_WARNING("-Wgcc-compat") +# define JSON_HEDLEY_REQUIRE(expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ + __attribute__((diagnose_if(!(expr), #expr, "error"))) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ + __attribute__((diagnose_if(!(expr), msg, "error"))) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_REQUIRE(expr) __attribute__((diagnose_if(!(expr), #expr, "error"))) +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) __attribute__((diagnose_if(!(expr), msg, "error"))) +# endif +#else +# define JSON_HEDLEY_REQUIRE(expr) +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) +#endif + +#if defined(JSON_HEDLEY_FLAGS) + #undef JSON_HEDLEY_FLAGS +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(flag_enum) && (!defined(__cplusplus) || JSON_HEDLEY_HAS_WARNING("-Wbitfield-enum-conversion")) + #define JSON_HEDLEY_FLAGS __attribute__((__flag_enum__)) +#else + #define JSON_HEDLEY_FLAGS +#endif + +#if defined(JSON_HEDLEY_FLAGS_CAST) + #undef JSON_HEDLEY_FLAGS_CAST +#endif +#if JSON_HEDLEY_INTEL_VERSION_CHECK(19,0,0) +# define JSON_HEDLEY_FLAGS_CAST(T, expr) (__extension__ ({ \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("warning(disable:188)") \ + ((T) (expr)); \ + JSON_HEDLEY_DIAGNOSTIC_POP \ + })) +#else +# define JSON_HEDLEY_FLAGS_CAST(T, expr) JSON_HEDLEY_STATIC_CAST(T, expr) +#endif + +#if defined(JSON_HEDLEY_EMPTY_BASES) + #undef JSON_HEDLEY_EMPTY_BASES +#endif +#if \ + (JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,23918) && !JSON_HEDLEY_MSVC_VERSION_CHECK(20,0,0)) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_EMPTY_BASES __declspec(empty_bases) +#else + #define JSON_HEDLEY_EMPTY_BASES +#endif + +/* Remaining macros are deprecated. */ + +#if defined(JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK) + #undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK +#endif +#if defined(__clang__) + #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) (0) +#else + #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_CLANG_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_BUILTIN) + #undef JSON_HEDLEY_CLANG_HAS_BUILTIN +#endif +#define JSON_HEDLEY_CLANG_HAS_BUILTIN(builtin) JSON_HEDLEY_HAS_BUILTIN(builtin) + +#if defined(JSON_HEDLEY_CLANG_HAS_FEATURE) + #undef JSON_HEDLEY_CLANG_HAS_FEATURE +#endif +#define JSON_HEDLEY_CLANG_HAS_FEATURE(feature) JSON_HEDLEY_HAS_FEATURE(feature) + +#if defined(JSON_HEDLEY_CLANG_HAS_EXTENSION) + #undef JSON_HEDLEY_CLANG_HAS_EXTENSION +#endif +#define JSON_HEDLEY_CLANG_HAS_EXTENSION(extension) JSON_HEDLEY_HAS_EXTENSION(extension) + +#if defined(JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_DECLSPEC_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_WARNING) + #undef JSON_HEDLEY_CLANG_HAS_WARNING +#endif +#define JSON_HEDLEY_CLANG_HAS_WARNING(warning) JSON_HEDLEY_HAS_WARNING(warning) + +#endif /* !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < X) */ + + +// This file contains all internal macro definitions (except those affecting ABI) +// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them + +// #include + + +// exclude unsupported compilers +#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) + #if defined(__clang__) + #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 + #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER)) + #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800 + #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #endif +#endif + +// C++ language standard detection +// if the user manually specified the used c++ version this is skipped +#if !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11) + #if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) + #define JSON_HAS_CPP_20 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 + #elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 + #elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) + #define JSON_HAS_CPP_14 + #endif + // the cpp 11 flag is always specified because it is the minimal required version + #define JSON_HAS_CPP_11 +#endif + +#ifdef __has_include + #if __has_include() + #include + #endif +#endif + +#if !defined(JSON_HAS_FILESYSTEM) && !defined(JSON_HAS_EXPERIMENTAL_FILESYSTEM) + #ifdef JSON_HAS_CPP_17 + #if defined(__cpp_lib_filesystem) + #define JSON_HAS_FILESYSTEM 1 + #elif defined(__cpp_lib_experimental_filesystem) + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 + #elif !defined(__has_include) + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 + #elif __has_include() + #define JSON_HAS_FILESYSTEM 1 + #elif __has_include() + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 + #endif + + // std::filesystem does not work on MinGW GCC 8: https://sourceforge.net/p/mingw-w64/bugs/737/ + #if defined(__MINGW32__) && defined(__GNUC__) && __GNUC__ == 8 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before GCC 8: https://en.cppreference.com/w/cpp/compiler_support + #if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 8 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before Clang 7: https://en.cppreference.com/w/cpp/compiler_support + #if defined(__clang_major__) && __clang_major__ < 7 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before MSVC 19.14: https://en.cppreference.com/w/cpp/compiler_support + #if defined(_MSC_VER) && _MSC_VER < 1914 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before iOS 13 + #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED < 130000 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before macOS Catalina + #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + #endif +#endif + +#ifndef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 0 +#endif + +#ifndef JSON_HAS_FILESYSTEM + #define JSON_HAS_FILESYSTEM 0 +#endif + +#ifndef JSON_HAS_THREE_WAY_COMPARISON + #if defined(__cpp_impl_three_way_comparison) && __cpp_impl_three_way_comparison >= 201907L \ + && defined(__cpp_lib_three_way_comparison) && __cpp_lib_three_way_comparison >= 201907L + #define JSON_HAS_THREE_WAY_COMPARISON 1 + #else + #define JSON_HAS_THREE_WAY_COMPARISON 0 + #endif +#endif + +#ifndef JSON_HAS_RANGES + // ranges header shipping in GCC 11.1.0 (released 2021-04-27) has syntax error + #if defined(__GLIBCXX__) && __GLIBCXX__ == 20210427 + #define JSON_HAS_RANGES 0 + #elif defined(__cpp_lib_ranges) + #define JSON_HAS_RANGES 1 + #else + #define JSON_HAS_RANGES 0 + #endif +#endif + +#ifndef JSON_HAS_STATIC_RTTI + #if !defined(_HAS_STATIC_RTTI) || _HAS_STATIC_RTTI != 0 + #define JSON_HAS_STATIC_RTTI 1 + #else + #define JSON_HAS_STATIC_RTTI 0 + #endif +#endif + +#ifdef JSON_HAS_CPP_17 + #define JSON_INLINE_VARIABLE inline +#else + #define JSON_INLINE_VARIABLE +#endif + +#if JSON_HEDLEY_HAS_ATTRIBUTE(no_unique_address) + #define JSON_NO_UNIQUE_ADDRESS [[no_unique_address]] +#else + #define JSON_NO_UNIQUE_ADDRESS +#endif + +// disable documentation warnings on clang +#if defined(__clang__) + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdocumentation" + #pragma clang diagnostic ignored "-Wdocumentation-unknown-command" +#endif + +// allow disabling exceptions +#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) + #define JSON_THROW(exception) throw exception + #define JSON_TRY try + #define JSON_CATCH(exception) catch(exception) + #define JSON_INTERNAL_CATCH(exception) catch(exception) +#else + #include + #define JSON_THROW(exception) std::abort() + #define JSON_TRY if(true) + #define JSON_CATCH(exception) if(false) + #define JSON_INTERNAL_CATCH(exception) if(false) +#endif + +// override exception macros +#if defined(JSON_THROW_USER) + #undef JSON_THROW + #define JSON_THROW JSON_THROW_USER +#endif +#if defined(JSON_TRY_USER) + #undef JSON_TRY + #define JSON_TRY JSON_TRY_USER +#endif +#if defined(JSON_CATCH_USER) + #undef JSON_CATCH + #define JSON_CATCH JSON_CATCH_USER + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_CATCH_USER +#endif +#if defined(JSON_INTERNAL_CATCH_USER) + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER +#endif + +// allow overriding assert +#if !defined(JSON_ASSERT) + #include // assert + #define JSON_ASSERT(x) assert(x) +#endif + +// allow to access some private functions (needed by the test suite) +#if defined(JSON_TESTS_PRIVATE) + #define JSON_PRIVATE_UNLESS_TESTED public +#else + #define JSON_PRIVATE_UNLESS_TESTED private +#endif + +/*! +@brief macro to briefly define a mapping between an enum and JSON +@def NLOHMANN_JSON_SERIALIZE_ENUM +@since version 3.4.0 +*/ +#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...) \ + template \ + inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [e](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.first == e; \ + }); \ + j = ((it != std::end(m)) ? it : std::begin(m))->second; \ + } \ + template \ + inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [&j](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.second == j; \ + }); \ + e = ((it != std::end(m)) ? it : std::begin(m))->first; \ + } + +// Ugly macros to avoid uglier copy-paste when specializing basic_json. They +// may be removed in the future once the class is split. + +#define NLOHMANN_BASIC_JSON_TPL_DECLARATION \ + template class ObjectType, \ + template class ArrayType, \ + class StringType, class BooleanType, class NumberIntegerType, \ + class NumberUnsignedType, class NumberFloatType, \ + template class AllocatorType, \ + template class JSONSerializer, \ + class BinaryType, \ + class CustomBaseClass> + +#define NLOHMANN_BASIC_JSON_TPL \ + basic_json + +// Macros to simplify conversion from/to types + +#define NLOHMANN_JSON_EXPAND( x ) x +#define NLOHMANN_JSON_GET_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, NAME,...) NAME +#define NLOHMANN_JSON_PASTE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \ + NLOHMANN_JSON_PASTE64, \ + NLOHMANN_JSON_PASTE63, \ + NLOHMANN_JSON_PASTE62, \ + NLOHMANN_JSON_PASTE61, \ + NLOHMANN_JSON_PASTE60, \ + NLOHMANN_JSON_PASTE59, \ + NLOHMANN_JSON_PASTE58, \ + NLOHMANN_JSON_PASTE57, \ + NLOHMANN_JSON_PASTE56, \ + NLOHMANN_JSON_PASTE55, \ + NLOHMANN_JSON_PASTE54, \ + NLOHMANN_JSON_PASTE53, \ + NLOHMANN_JSON_PASTE52, \ + NLOHMANN_JSON_PASTE51, \ + NLOHMANN_JSON_PASTE50, \ + NLOHMANN_JSON_PASTE49, \ + NLOHMANN_JSON_PASTE48, \ + NLOHMANN_JSON_PASTE47, \ + NLOHMANN_JSON_PASTE46, \ + NLOHMANN_JSON_PASTE45, \ + NLOHMANN_JSON_PASTE44, \ + NLOHMANN_JSON_PASTE43, \ + NLOHMANN_JSON_PASTE42, \ + NLOHMANN_JSON_PASTE41, \ + NLOHMANN_JSON_PASTE40, \ + NLOHMANN_JSON_PASTE39, \ + NLOHMANN_JSON_PASTE38, \ + NLOHMANN_JSON_PASTE37, \ + NLOHMANN_JSON_PASTE36, \ + NLOHMANN_JSON_PASTE35, \ + NLOHMANN_JSON_PASTE34, \ + NLOHMANN_JSON_PASTE33, \ + NLOHMANN_JSON_PASTE32, \ + NLOHMANN_JSON_PASTE31, \ + NLOHMANN_JSON_PASTE30, \ + NLOHMANN_JSON_PASTE29, \ + NLOHMANN_JSON_PASTE28, \ + NLOHMANN_JSON_PASTE27, \ + NLOHMANN_JSON_PASTE26, \ + NLOHMANN_JSON_PASTE25, \ + NLOHMANN_JSON_PASTE24, \ + NLOHMANN_JSON_PASTE23, \ + NLOHMANN_JSON_PASTE22, \ + NLOHMANN_JSON_PASTE21, \ + NLOHMANN_JSON_PASTE20, \ + NLOHMANN_JSON_PASTE19, \ + NLOHMANN_JSON_PASTE18, \ + NLOHMANN_JSON_PASTE17, \ + NLOHMANN_JSON_PASTE16, \ + NLOHMANN_JSON_PASTE15, \ + NLOHMANN_JSON_PASTE14, \ + NLOHMANN_JSON_PASTE13, \ + NLOHMANN_JSON_PASTE12, \ + NLOHMANN_JSON_PASTE11, \ + NLOHMANN_JSON_PASTE10, \ + NLOHMANN_JSON_PASTE9, \ + NLOHMANN_JSON_PASTE8, \ + NLOHMANN_JSON_PASTE7, \ + NLOHMANN_JSON_PASTE6, \ + NLOHMANN_JSON_PASTE5, \ + NLOHMANN_JSON_PASTE4, \ + NLOHMANN_JSON_PASTE3, \ + NLOHMANN_JSON_PASTE2, \ + NLOHMANN_JSON_PASTE1)(__VA_ARGS__)) +#define NLOHMANN_JSON_PASTE2(func, v1) func(v1) +#define NLOHMANN_JSON_PASTE3(func, v1, v2) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE2(func, v2) +#define NLOHMANN_JSON_PASTE4(func, v1, v2, v3) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE3(func, v2, v3) +#define NLOHMANN_JSON_PASTE5(func, v1, v2, v3, v4) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE4(func, v2, v3, v4) +#define NLOHMANN_JSON_PASTE6(func, v1, v2, v3, v4, v5) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE5(func, v2, v3, v4, v5) +#define NLOHMANN_JSON_PASTE7(func, v1, v2, v3, v4, v5, v6) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE6(func, v2, v3, v4, v5, v6) +#define NLOHMANN_JSON_PASTE8(func, v1, v2, v3, v4, v5, v6, v7) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE7(func, v2, v3, v4, v5, v6, v7) +#define NLOHMANN_JSON_PASTE9(func, v1, v2, v3, v4, v5, v6, v7, v8) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8) +#define NLOHMANN_JSON_PASTE10(func, v1, v2, v3, v4, v5, v6, v7, v8, v9) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE9(func, v2, v3, v4, v5, v6, v7, v8, v9) +#define NLOHMANN_JSON_PASTE11(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE10(func, v2, v3, v4, v5, v6, v7, v8, v9, v10) +#define NLOHMANN_JSON_PASTE12(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE11(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) +#define NLOHMANN_JSON_PASTE13(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE12(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) +#define NLOHMANN_JSON_PASTE14(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE13(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) +#define NLOHMANN_JSON_PASTE15(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE14(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) +#define NLOHMANN_JSON_PASTE16(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE15(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) +#define NLOHMANN_JSON_PASTE17(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE16(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) +#define NLOHMANN_JSON_PASTE18(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE17(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) +#define NLOHMANN_JSON_PASTE19(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE18(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) +#define NLOHMANN_JSON_PASTE20(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE19(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) +#define NLOHMANN_JSON_PASTE21(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE20(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) +#define NLOHMANN_JSON_PASTE22(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE21(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) +#define NLOHMANN_JSON_PASTE23(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE22(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) +#define NLOHMANN_JSON_PASTE24(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE23(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) +#define NLOHMANN_JSON_PASTE25(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE24(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) +#define NLOHMANN_JSON_PASTE26(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE25(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) +#define NLOHMANN_JSON_PASTE27(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE26(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) +#define NLOHMANN_JSON_PASTE28(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE27(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) +#define NLOHMANN_JSON_PASTE29(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE28(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) +#define NLOHMANN_JSON_PASTE30(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE29(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) +#define NLOHMANN_JSON_PASTE31(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE30(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) +#define NLOHMANN_JSON_PASTE32(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE31(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) +#define NLOHMANN_JSON_PASTE33(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE32(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) +#define NLOHMANN_JSON_PASTE34(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE33(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) +#define NLOHMANN_JSON_PASTE35(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE34(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) +#define NLOHMANN_JSON_PASTE36(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE35(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) +#define NLOHMANN_JSON_PASTE37(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE36(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) +#define NLOHMANN_JSON_PASTE38(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE37(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) +#define NLOHMANN_JSON_PASTE39(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE38(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) +#define NLOHMANN_JSON_PASTE40(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE39(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) +#define NLOHMANN_JSON_PASTE41(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE40(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) +#define NLOHMANN_JSON_PASTE42(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE41(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) +#define NLOHMANN_JSON_PASTE43(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE42(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) +#define NLOHMANN_JSON_PASTE44(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE43(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) +#define NLOHMANN_JSON_PASTE45(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE44(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) +#define NLOHMANN_JSON_PASTE46(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE45(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) +#define NLOHMANN_JSON_PASTE47(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE46(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) +#define NLOHMANN_JSON_PASTE48(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE47(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) +#define NLOHMANN_JSON_PASTE49(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE48(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) +#define NLOHMANN_JSON_PASTE50(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE49(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) +#define NLOHMANN_JSON_PASTE51(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE50(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) +#define NLOHMANN_JSON_PASTE52(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE51(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) +#define NLOHMANN_JSON_PASTE53(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE52(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) +#define NLOHMANN_JSON_PASTE54(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE53(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) +#define NLOHMANN_JSON_PASTE55(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE54(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) +#define NLOHMANN_JSON_PASTE56(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE55(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) +#define NLOHMANN_JSON_PASTE57(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE56(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) +#define NLOHMANN_JSON_PASTE58(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE57(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) +#define NLOHMANN_JSON_PASTE59(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE58(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) +#define NLOHMANN_JSON_PASTE60(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE59(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) +#define NLOHMANN_JSON_PASTE61(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE60(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) +#define NLOHMANN_JSON_PASTE62(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE61(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) +#define NLOHMANN_JSON_PASTE63(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE62(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) +#define NLOHMANN_JSON_PASTE64(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE63(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) + +#define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1; +#define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1); +#define NLOHMANN_JSON_FROM_WITH_DEFAULT(v1) nlohmann_json_t.v1 = nlohmann_json_j.value(#v1, nlohmann_json_default_obj.v1); + +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_INTRUSIVE +@since version 3.9.0 +*/ +#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...) \ + friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + +#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Type, ...) \ + friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } + +#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(Type, ...) \ + friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } + +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE +@since version 3.9.0 +*/ +#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...) \ + inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + +#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(Type, ...) \ + inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } + +#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, ...) \ + inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } + +// inspired from https://stackoverflow.com/a/26745591 +// allows to call any std function as if (e.g. with begin): +// using std::begin; begin(x); +// +// it allows using the detected idiom to retrieve the return type +// of such an expression +#define NLOHMANN_CAN_CALL_STD_FUNC_IMPL(std_name) \ + namespace detail { \ + using std::std_name; \ + \ + template \ + using result_of_##std_name = decltype(std_name(std::declval()...)); \ + } \ + \ + namespace detail2 { \ + struct std_name##_tag \ + { \ + }; \ + \ + template \ + std_name##_tag std_name(T&&...); \ + \ + template \ + using result_of_##std_name = decltype(std_name(std::declval()...)); \ + \ + template \ + struct would_call_std_##std_name \ + { \ + static constexpr auto const value = ::nlohmann::detail:: \ + is_detected_exact::value; \ + }; \ + } /* namespace detail2 */ \ + \ + template \ + struct would_call_std_##std_name : detail2::would_call_std_##std_name \ + { \ + } + +#ifndef JSON_USE_IMPLICIT_CONVERSIONS + #define JSON_USE_IMPLICIT_CONVERSIONS 1 +#endif + +#if JSON_USE_IMPLICIT_CONVERSIONS + #define JSON_EXPLICIT +#else + #define JSON_EXPLICIT explicit +#endif + +#ifndef JSON_DISABLE_ENUM_SERIALIZATION + #define JSON_DISABLE_ENUM_SERIALIZATION 0 +#endif + +#ifndef JSON_USE_GLOBAL_UDLS + #define JSON_USE_GLOBAL_UDLS 1 +#endif + +#if JSON_HAS_THREE_WAY_COMPARISON + #include // partial_ordering +#endif + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +/////////////////////////// +// JSON type enumeration // +/////////////////////////// + +/*! +@brief the JSON type enumeration + +This enumeration collects the different JSON types. It is internally used to +distinguish the stored values, and the functions @ref basic_json::is_null(), +@ref basic_json::is_object(), @ref basic_json::is_array(), +@ref basic_json::is_string(), @ref basic_json::is_boolean(), +@ref basic_json::is_number() (with @ref basic_json::is_number_integer(), +@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()), +@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and +@ref basic_json::is_structured() rely on it. + +@note There are three enumeration entries (number_integer, number_unsigned, and +number_float), because the library distinguishes these three types for numbers: +@ref basic_json::number_unsigned_t is used for unsigned integers, +@ref basic_json::number_integer_t is used for signed integers, and +@ref basic_json::number_float_t is used for floating-point numbers or to +approximate integers which do not fit in the limits of their respective type. + +@sa see @ref basic_json::basic_json(const value_t value_type) -- create a JSON +value with the default value for a given type + +@since version 1.0.0 +*/ +enum class value_t : std::uint8_t +{ + null, ///< null value + object, ///< object (unordered set of name/value pairs) + array, ///< array (ordered collection of values) + string, ///< string value + boolean, ///< boolean value + number_integer, ///< number value (signed integer) + number_unsigned, ///< number value (unsigned integer) + number_float, ///< number value (floating-point) + binary, ///< binary array (ordered collection of bytes) + discarded ///< discarded by the parser callback function +}; + +/*! +@brief comparison operator for JSON types + +Returns an ordering that is similar to Python: +- order: null < boolean < number < object < array < string < binary +- furthermore, each type is not smaller than itself +- discarded values are not comparable +- binary is represented as a b"" string in python and directly comparable to a + string; however, making a binary array directly comparable with a string would + be surprising behavior in a JSON file. + +@since version 1.0.0 +*/ +#if JSON_HAS_THREE_WAY_COMPARISON + inline std::partial_ordering operator<=>(const value_t lhs, const value_t rhs) noexcept // *NOPAD* +#else + inline bool operator<(const value_t lhs, const value_t rhs) noexcept +#endif +{ + static constexpr std::array order = {{ + 0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */, + 1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */, + 6 /* binary */ + } + }; + + const auto l_index = static_cast(lhs); + const auto r_index = static_cast(rhs); +#if JSON_HAS_THREE_WAY_COMPARISON + if (l_index < order.size() && r_index < order.size()) + { + return order[l_index] <=> order[r_index]; // *NOPAD* + } + return std::partial_ordering::unordered; +#else + return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index]; +#endif +} + +// GCC selects the built-in operator< over an operator rewritten from +// a user-defined spaceship operator +// Clang, MSVC, and ICC select the rewritten candidate +// (see GCC bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105200) +#if JSON_HAS_THREE_WAY_COMPARISON && defined(__GNUC__) +inline bool operator<(const value_t lhs, const value_t rhs) noexcept +{ + return std::is_lt(lhs <=> rhs); // *NOPAD* +} +#endif + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +/*! +@brief replace all occurrences of a substring by another string + +@param[in,out] s the string to manipulate; changed so that all + occurrences of @a f are replaced with @a t +@param[in] f the substring to replace with @a t +@param[in] t the string to replace @a f + +@pre The search string @a f must not be empty. **This precondition is +enforced with an assertion.** + +@since version 2.0.0 +*/ +template +inline void replace_substring(StringType& s, const StringType& f, + const StringType& t) +{ + JSON_ASSERT(!f.empty()); + for (auto pos = s.find(f); // find first occurrence of f + pos != StringType::npos; // make sure f was found + s.replace(pos, f.size(), t), // replace with t, and + pos = s.find(f, pos + t.size())) // find next occurrence of f + {} +} + +/*! + * @brief string escaping as described in RFC 6901 (Sect. 4) + * @param[in] s string to escape + * @return escaped string + * + * Note the order of escaping "~" to "~0" and "/" to "~1" is important. + */ +template +inline StringType escape(StringType s) +{ + replace_substring(s, StringType{"~"}, StringType{"~0"}); + replace_substring(s, StringType{"/"}, StringType{"~1"}); + return s; +} + +/*! + * @brief string unescaping as described in RFC 6901 (Sect. 4) + * @param[in] s string to unescape + * @return unescaped string + * + * Note the order of escaping "~1" to "/" and "~0" to "~" is important. + */ +template +static void unescape(StringType& s) +{ + replace_substring(s, StringType{"~1"}, StringType{"/"}); + replace_substring(s, StringType{"~0"}, StringType{"~"}); +} + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // size_t + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +/// struct to capture the start position of the current token +struct position_t +{ + /// the total number of characters read + std::size_t chars_read_total = 0; + /// the number of characters read in the current line + std::size_t chars_read_current_line = 0; + /// the number of lines read + std::size_t lines_read = 0; + + /// conversion to size_t to preserve SAX interface + constexpr operator size_t() const + { + return chars_read_total; + } +}; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2018 The Abseil Authors +// SPDX-License-Identifier: MIT + + + +#include // array +#include // size_t +#include // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type +#include // index_sequence, make_index_sequence, index_sequence_for + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +template +using uncvref_t = typename std::remove_cv::type>::type; + +#ifdef JSON_HAS_CPP_14 + +// the following utilities are natively available in C++14 +using std::enable_if_t; +using std::index_sequence; +using std::make_index_sequence; +using std::index_sequence_for; + +#else + +// alias templates to reduce boilerplate +template +using enable_if_t = typename std::enable_if::type; + +// The following code is taken from https://github.com/abseil/abseil-cpp/blob/10cb35e459f5ecca5b2ff107635da0bfa41011b4/absl/utility/utility.h +// which is part of Google Abseil (https://github.com/abseil/abseil-cpp), licensed under the Apache License 2.0. + +//// START OF CODE FROM GOOGLE ABSEIL + +// integer_sequence +// +// Class template representing a compile-time integer sequence. An instantiation +// of `integer_sequence` has a sequence of integers encoded in its +// type through its template arguments (which is a common need when +// working with C++11 variadic templates). `absl::integer_sequence` is designed +// to be a drop-in replacement for C++14's `std::integer_sequence`. +// +// Example: +// +// template< class T, T... Ints > +// void user_function(integer_sequence); +// +// int main() +// { +// // user_function's `T` will be deduced to `int` and `Ints...` +// // will be deduced to `0, 1, 2, 3, 4`. +// user_function(make_integer_sequence()); +// } +template +struct integer_sequence +{ + using value_type = T; + static constexpr std::size_t size() noexcept + { + return sizeof...(Ints); + } +}; + +// index_sequence +// +// A helper template for an `integer_sequence` of `size_t`, +// `absl::index_sequence` is designed to be a drop-in replacement for C++14's +// `std::index_sequence`. +template +using index_sequence = integer_sequence; + +namespace utility_internal +{ + +template +struct Extend; + +// Note that SeqSize == sizeof...(Ints). It's passed explicitly for efficiency. +template +struct Extend, SeqSize, 0> +{ + using type = integer_sequence < T, Ints..., (Ints + SeqSize)... >; +}; + +template +struct Extend, SeqSize, 1> +{ + using type = integer_sequence < T, Ints..., (Ints + SeqSize)..., 2 * SeqSize >; +}; + +// Recursion helper for 'make_integer_sequence'. +// 'Gen::type' is an alias for 'integer_sequence'. +template +struct Gen +{ + using type = + typename Extend < typename Gen < T, N / 2 >::type, N / 2, N % 2 >::type; +}; + +template +struct Gen +{ + using type = integer_sequence; +}; + +} // namespace utility_internal + +// Compile-time sequences of integers + +// make_integer_sequence +// +// This template alias is equivalent to +// `integer_sequence`, and is designed to be a drop-in +// replacement for C++14's `std::make_integer_sequence`. +template +using make_integer_sequence = typename utility_internal::Gen::type; + +// make_index_sequence +// +// This template alias is equivalent to `index_sequence<0, 1, ..., N-1>`, +// and is designed to be a drop-in replacement for C++14's +// `std::make_index_sequence`. +template +using make_index_sequence = make_integer_sequence; + +// index_sequence_for +// +// Converts a typename pack into an index sequence of the same length, and +// is designed to be a drop-in replacement for C++14's +// `std::index_sequence_for()` +template +using index_sequence_for = make_index_sequence; + +//// END OF CODE FROM GOOGLE ABSEIL + +#endif + +// dispatch utility (taken from ranges-v3) +template struct priority_tag : priority_tag < N - 1 > {}; +template<> struct priority_tag<0> {}; + +// taken from ranges-v3 +template +struct static_const +{ + static JSON_INLINE_VARIABLE constexpr T value{}; +}; + +#ifndef JSON_HAS_CPP_17 + template + constexpr T static_const::value; +#endif + +template +inline constexpr std::array make_array(Args&& ... args) +{ + return std::array {{static_cast(std::forward(args))...}}; +} + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // numeric_limits +#include // false_type, is_constructible, is_integral, is_same, true_type +#include // declval +#include // tuple +#include // char_traits + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // random_access_iterator_tag + +// #include + +// #include + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +template +struct iterator_types {}; + +template +struct iterator_types < + It, + void_t> +{ + using difference_type = typename It::difference_type; + using value_type = typename It::value_type; + using pointer = typename It::pointer; + using reference = typename It::reference; + using iterator_category = typename It::iterator_category; +}; + +// This is required as some compilers implement std::iterator_traits in a way that +// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341. +template +struct iterator_traits +{ +}; + +template +struct iterator_traits < T, enable_if_t < !std::is_pointer::value >> + : iterator_types +{ +}; + +template +struct iterator_traits::value>> +{ + using iterator_category = std::random_access_iterator_tag; + using value_type = T; + using difference_type = ptrdiff_t; + using pointer = T*; + using reference = T&; +}; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN + +NLOHMANN_CAN_CALL_STD_FUNC_IMPL(begin); + +NLOHMANN_JSON_NAMESPACE_END + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN + +NLOHMANN_CAN_CALL_STD_FUNC_IMPL(end); + +NLOHMANN_JSON_NAMESPACE_END + +// #include + +// #include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + +#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_ + #define INCLUDE_NLOHMANN_JSON_FWD_HPP_ + + #include // int64_t, uint64_t + #include // map + #include // allocator + #include // string + #include // vector + + // #include + + + /*! + @brief namespace for Niels Lohmann + @see https://github.com/nlohmann + @since version 1.0.0 + */ + NLOHMANN_JSON_NAMESPACE_BEGIN + + /*! + @brief default JSONSerializer template argument + + This serializer ignores the template arguments and uses ADL + ([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl)) + for serialization. + */ + template + struct adl_serializer; + + /// a class to store JSON values + /// @sa https://json.nlohmann.me/api/basic_json/ + template class ObjectType = + std::map, + template class ArrayType = std::vector, + class StringType = std::string, class BooleanType = bool, + class NumberIntegerType = std::int64_t, + class NumberUnsignedType = std::uint64_t, + class NumberFloatType = double, + template class AllocatorType = std::allocator, + template class JSONSerializer = + adl_serializer, + class BinaryType = std::vector, // cppcheck-suppress syntaxError + class CustomBaseClass = void> + class basic_json; + + /// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document + /// @sa https://json.nlohmann.me/api/json_pointer/ + template + class json_pointer; + + /*! + @brief default specialization + @sa https://json.nlohmann.me/api/json/ + */ + using json = basic_json<>; + + /// @brief a minimal map-like container that preserves insertion order + /// @sa https://json.nlohmann.me/api/ordered_map/ + template + struct ordered_map; + + /// @brief specialization that maintains the insertion order of object keys + /// @sa https://json.nlohmann.me/api/ordered_json/ + using ordered_json = basic_json; + + NLOHMANN_JSON_NAMESPACE_END + +#endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_ + + +NLOHMANN_JSON_NAMESPACE_BEGIN +/*! +@brief detail namespace with internal helper functions + +This namespace collects functions that should not be exposed, +implementations of some @ref basic_json methods, and meta-programming helpers. + +@since version 2.1.0 +*/ +namespace detail +{ + +///////////// +// helpers // +///////////// + +// Note to maintainers: +// +// Every trait in this file expects a non CV-qualified type. +// The only exceptions are in the 'aliases for detected' section +// (i.e. those of the form: decltype(T::member_function(std::declval()))) +// +// In this case, T has to be properly CV-qualified to constraint the function arguments +// (e.g. to_json(BasicJsonType&, const T&)) + +template struct is_basic_json : std::false_type {}; + +NLOHMANN_BASIC_JSON_TPL_DECLARATION +struct is_basic_json : std::true_type {}; + +// used by exceptions create() member functions +// true_type for pointer to possibly cv-qualified basic_json or std::nullptr_t +// false_type otherwise +template +struct is_basic_json_context : + std::integral_constant < bool, + is_basic_json::type>::type>::value + || std::is_same::value > +{}; + +////////////////////// +// json_ref helpers // +////////////////////// + +template +class json_ref; + +template +struct is_json_ref : std::false_type {}; + +template +struct is_json_ref> : std::true_type {}; + +////////////////////////// +// aliases for detected // +////////////////////////// + +template +using mapped_type_t = typename T::mapped_type; + +template +using key_type_t = typename T::key_type; + +template +using value_type_t = typename T::value_type; + +template +using difference_type_t = typename T::difference_type; + +template +using pointer_t = typename T::pointer; + +template +using reference_t = typename T::reference; + +template +using iterator_category_t = typename T::iterator_category; + +template +using to_json_function = decltype(T::to_json(std::declval()...)); + +template +using from_json_function = decltype(T::from_json(std::declval()...)); + +template +using get_template_function = decltype(std::declval().template get()); + +// trait checking if JSONSerializer::from_json(json const&, udt&) exists +template +struct has_from_json : std::false_type {}; + +// trait checking if j.get is valid +// use this trait instead of std::is_constructible or std::is_convertible, +// both rely on, or make use of implicit conversions, and thus fail when T +// has several constructors/operator= (see https://github.com/nlohmann/json/issues/958) +template +struct is_getable +{ + static constexpr bool value = is_detected::value; +}; + +template +struct has_from_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> +{ + using serializer = typename BasicJsonType::template json_serializer; + + static constexpr bool value = + is_detected_exact::value; +}; + +// This trait checks if JSONSerializer::from_json(json const&) exists +// this overload is used for non-default-constructible user-defined-types +template +struct has_non_default_from_json : std::false_type {}; + +template +struct has_non_default_from_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> +{ + using serializer = typename BasicJsonType::template json_serializer; + + static constexpr bool value = + is_detected_exact::value; +}; + +// This trait checks if BasicJsonType::json_serializer::to_json exists +// Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion. +template +struct has_to_json : std::false_type {}; + +template +struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> +{ + using serializer = typename BasicJsonType::template json_serializer; + + static constexpr bool value = + is_detected_exact::value; +}; + +template +using detect_key_compare = typename T::key_compare; + +template +struct has_key_compare : std::integral_constant::value> {}; + +// obtains the actual object key comparator +template +struct actual_object_comparator +{ + using object_t = typename BasicJsonType::object_t; + using object_comparator_t = typename BasicJsonType::default_object_comparator_t; + using type = typename std::conditional < has_key_compare::value, + typename object_t::key_compare, object_comparator_t>::type; +}; + +template +using actual_object_comparator_t = typename actual_object_comparator::type; + +///////////////// +// char_traits // +///////////////// + +// Primary template of char_traits calls std char_traits +template +struct char_traits : std::char_traits +{}; + +// Explicitly define char traits for unsigned char since it is not standard +template<> +struct char_traits : std::char_traits +{ + using char_type = unsigned char; + using int_type = uint64_t; + + // Redefine to_int_type function + static int_type to_int_type(char_type c) noexcept + { + return static_cast(c); + } + + static char_type to_char_type(int_type i) noexcept + { + return static_cast(i); + } + + static constexpr int_type eof() noexcept + { + return static_cast(EOF); + } +}; + +// Explicitly define char traits for signed char since it is not standard +template<> +struct char_traits : std::char_traits +{ + using char_type = signed char; + using int_type = uint64_t; + + // Redefine to_int_type function + static int_type to_int_type(char_type c) noexcept + { + return static_cast(c); + } + + static char_type to_char_type(int_type i) noexcept + { + return static_cast(i); + } + + static constexpr int_type eof() noexcept + { + return static_cast(EOF); + } +}; + +/////////////////// +// is_ functions // +/////////////////// + +// https://en.cppreference.com/w/cpp/types/conjunction +template struct conjunction : std::true_type { }; +template struct conjunction : B { }; +template +struct conjunction +: std::conditional(B::value), conjunction, B>::type {}; + +// https://en.cppreference.com/w/cpp/types/negation +template struct negation : std::integral_constant < bool, !B::value > { }; + +// Reimplementation of is_constructible and is_default_constructible, due to them being broken for +// std::pair and std::tuple until LWG 2367 fix (see https://cplusplus.github.io/LWG/lwg-defects.html#2367). +// This causes compile errors in e.g. clang 3.5 or gcc 4.9. +template +struct is_default_constructible : std::is_default_constructible {}; + +template +struct is_default_constructible> + : conjunction, is_default_constructible> {}; + +template +struct is_default_constructible> + : conjunction, is_default_constructible> {}; + +template +struct is_default_constructible> + : conjunction...> {}; + +template +struct is_default_constructible> + : conjunction...> {}; + +template +struct is_constructible : std::is_constructible {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_iterator_traits : std::false_type {}; + +template +struct is_iterator_traits> +{ + private: + using traits = iterator_traits; + + public: + static constexpr auto value = + is_detected::value && + is_detected::value && + is_detected::value && + is_detected::value && + is_detected::value; +}; + +template +struct is_range +{ + private: + using t_ref = typename std::add_lvalue_reference::type; + + using iterator = detected_t; + using sentinel = detected_t; + + // to be 100% correct, it should use https://en.cppreference.com/w/cpp/iterator/input_or_output_iterator + // and https://en.cppreference.com/w/cpp/iterator/sentinel_for + // but reimplementing these would be too much work, as a lot of other concepts are used underneath + static constexpr auto is_iterator_begin = + is_iterator_traits>::value; + + public: + static constexpr bool value = !std::is_same::value && !std::is_same::value && is_iterator_begin; +}; + +template +using iterator_t = enable_if_t::value, result_of_begin())>>; + +template +using range_value_t = value_type_t>>; + +// The following implementation of is_complete_type is taken from +// https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/ +// and is written by Xiang Fan who agreed to using it in this library. + +template +struct is_complete_type : std::false_type {}; + +template +struct is_complete_type : std::true_type {}; + +template +struct is_compatible_object_type_impl : std::false_type {}; + +template +struct is_compatible_object_type_impl < + BasicJsonType, CompatibleObjectType, + enable_if_t < is_detected::value&& + is_detected::value >> +{ + using object_t = typename BasicJsonType::object_t; + + // macOS's is_constructible does not play well with nonesuch... + static constexpr bool value = + is_constructible::value && + is_constructible::value; +}; + +template +struct is_compatible_object_type + : is_compatible_object_type_impl {}; + +template +struct is_constructible_object_type_impl : std::false_type {}; + +template +struct is_constructible_object_type_impl < + BasicJsonType, ConstructibleObjectType, + enable_if_t < is_detected::value&& + is_detected::value >> +{ + using object_t = typename BasicJsonType::object_t; + + static constexpr bool value = + (is_default_constructible::value && + (std::is_move_assignable::value || + std::is_copy_assignable::value) && + (is_constructible::value && + std::is_same < + typename object_t::mapped_type, + typename ConstructibleObjectType::mapped_type >::value)) || + (has_from_json::value || + has_non_default_from_json < + BasicJsonType, + typename ConstructibleObjectType::mapped_type >::value); +}; + +template +struct is_constructible_object_type + : is_constructible_object_type_impl {}; + +template +struct is_compatible_string_type +{ + static constexpr auto value = + is_constructible::value; +}; + +template +struct is_constructible_string_type +{ + // launder type through decltype() to fix compilation failure on ICPC +#ifdef __INTEL_COMPILER + using laundered_type = decltype(std::declval()); +#else + using laundered_type = ConstructibleStringType; +#endif + + static constexpr auto value = + conjunction < + is_constructible, + is_detected_exact>::value; +}; + +template +struct is_compatible_array_type_impl : std::false_type {}; + +template +struct is_compatible_array_type_impl < + BasicJsonType, CompatibleArrayType, + enable_if_t < + is_detected::value&& + is_iterator_traits>>::value&& +// special case for types like std::filesystem::path whose iterator's value_type are themselves +// c.f. https://github.com/nlohmann/json/pull/3073 + !std::is_same>::value >> +{ + static constexpr bool value = + is_constructible>::value; +}; + +template +struct is_compatible_array_type + : is_compatible_array_type_impl {}; + +template +struct is_constructible_array_type_impl : std::false_type {}; + +template +struct is_constructible_array_type_impl < + BasicJsonType, ConstructibleArrayType, + enable_if_t::value >> + : std::true_type {}; + +template +struct is_constructible_array_type_impl < + BasicJsonType, ConstructibleArrayType, + enable_if_t < !std::is_same::value&& + !is_compatible_string_type::value&& + is_default_constructible::value&& +(std::is_move_assignable::value || + std::is_copy_assignable::value)&& +is_detected::value&& +is_iterator_traits>>::value&& +is_detected::value&& +// special case for types like std::filesystem::path whose iterator's value_type are themselves +// c.f. https://github.com/nlohmann/json/pull/3073 +!std::is_same>::value&& + is_complete_type < + detected_t>::value >> +{ + using value_type = range_value_t; + + static constexpr bool value = + std::is_same::value || + has_from_json::value || + has_non_default_from_json < + BasicJsonType, + value_type >::value; +}; + +template +struct is_constructible_array_type + : is_constructible_array_type_impl {}; + +template +struct is_compatible_integer_type_impl : std::false_type {}; + +template +struct is_compatible_integer_type_impl < + RealIntegerType, CompatibleNumberIntegerType, + enable_if_t < std::is_integral::value&& + std::is_integral::value&& + !std::is_same::value >> +{ + // is there an assert somewhere on overflows? + using RealLimits = std::numeric_limits; + using CompatibleLimits = std::numeric_limits; + + static constexpr auto value = + is_constructible::value && + CompatibleLimits::is_integer && + RealLimits::is_signed == CompatibleLimits::is_signed; +}; + +template +struct is_compatible_integer_type + : is_compatible_integer_type_impl {}; + +template +struct is_compatible_type_impl: std::false_type {}; + +template +struct is_compatible_type_impl < + BasicJsonType, CompatibleType, + enable_if_t::value >> +{ + static constexpr bool value = + has_to_json::value; +}; + +template +struct is_compatible_type + : is_compatible_type_impl {}; + +template +struct is_constructible_tuple : std::false_type {}; + +template +struct is_constructible_tuple> : conjunction...> {}; + +template +struct is_json_iterator_of : std::false_type {}; + +template +struct is_json_iterator_of : std::true_type {}; + +template +struct is_json_iterator_of : std::true_type +{}; + +// checks if a given type T is a template specialization of Primary +template