diff --git a/testing/algorithms/CMakeLists.txt b/testing/algorithms/CMakeLists.txt index e7909b8..ee7f4f9 100644 --- a/testing/algorithms/CMakeLists.txt +++ b/testing/algorithms/CMakeLists.txt @@ -1,22 +1,22 @@ #----------------------------------------------------------------------------- # Setup the system information test. Write out some basic failsafe # information in case the test doesn't run. #----------------------------------------------------------------------------- SET(ALGORITHMS_TESTS ${EXECUTABLE_OUTPUT_PATH}/rttbAlgorithmsTests) SET(ALGORITHMS_HEADER_TEST ${EXECUTABLE_OUTPUT_PATH}/rttbAlgorithmsHeaderTest) SET(TEST_DATA_ROOT ${RTTBTesting_SOURCE_DIR}/data) SET(TEMP ${RTTBTesting_BINARY_DIR}/temporary) #----------------------------------------------------------------------------- ADD_TEST(DoseStatisticsTest ${ALGORITHMS_TESTS} DoseStatisticsTest) ADD_TEST(ArithmeticTest ${ALGORITHMS_TESTS} ArithmeticTest) -ADD_TEST(DoseStatisticsCalculatorTest ${ALGORITHMS_TESTS} DoseStatisticsCalculatorTest) +ADD_TEST(DoseStatisticsCalculatorTest ${ALGORITHMS_TESTS} DoseStatisticsCalculatorTest "${TEST_DATA_ROOT}/DoseStatistics/XML/dicom_heartComplex.xml" "${TEST_DATA_ROOT}/Dose/DICOM/dicompylerTestDose.dcm" "${TEST_DATA_ROOT}/StructureSet/DICOM/rtss.dcm") ADD_TEST(BinaryFunctorAccessorTest ${ALGORITHMS_TESTS} BinaryFunctorAccessorTest "${TEST_DATA_ROOT}/Dose/DICOM/ConstantTwo.dcm" "${TEST_DATA_ROOT}/Dose/DICOM/dicompylerTestDose.dcm") -RTTB_CREATE_TEST_MODULE(rttbAlgorithms DEPENDS RTTBAlgorithms RTTBMasks RTTBDicomIO PACKAGE_DEPENDS Boost Litmus DCMTK) +RTTB_CREATE_TEST_MODULE(rttbAlgorithms DEPENDS RTTBAlgorithms RTTBMasks RTTBBoostMask RTTBDicomIO PACKAGE_DEPENDS Boost Litmus DCMTK) diff --git a/testing/algorithms/DoseStatisticsCalculatorTest.cpp b/testing/algorithms/DoseStatisticsCalculatorTest.cpp index 5b5df7e..22cbd7e 100644 --- a/testing/algorithms/DoseStatisticsCalculatorTest.cpp +++ b/testing/algorithms/DoseStatisticsCalculatorTest.cpp @@ -1,326 +1,374 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #include #include #include "litCheckMacros.h" #include "rttbBaseType.h" #include "rttbGenericDoseIterator.h" #include "rttbDoseIteratorInterface.h" #include "rttbNullPointerException.h" #include "rttbDoseStatisticsCalculator.h" #include "rttbInvalidDoseException.h" #include "rttbInvalidParameterException.h" #include "rttbDataNotAvailableException.h" +#include "rttbDicomFileDoseAccessorGenerator.h" +#include "rttbDicomFileStructureSetGenerator.h" +#include "rttbVOIindexIdentifier.h" +#include "rttbBoostMaskAccessor.h" +#include "rttbGenericMaskedDoseIterator.h" +#include "../io/other/CompareDoseStatistic.h" +#include "../../code/io/other/rttbDoseStatisticsXMLReader.h" + #include "../core/DummyDoseAccessor.h" namespace rttb { namespace testing { typedef core::GenericDoseIterator::DoseAccessorPointer DoseAccessorPointer; typedef core::DoseIteratorInterface::DoseIteratorPointer DoseIteratorPointer; typedef rttb::algorithms::DoseStatistics::ResultListPointer ResultListPointer; typedef rttb::algorithms::DoseStatistics::DoseStatisticsPointer DoseStatisticsPointer; /*! @brief DoseStatisticsCalculatorTest - test the API of DoseStatisticsCalculator 1) test constructors 2) test setDoseIterator 3) test calculateDoseSatistics 4) get statistical values */ int DoseStatisticsCalculatorTest(int argc, char* argv[]) { PREPARE_DEFAULT_TEST_REPORTING; + std::string referenceXMLFilename; + std::string doseFilename, structFilename; + boost::shared_ptr spTestDoseAccessor = boost::make_shared(); DoseAccessorPointer spDoseAccessor(spTestDoseAccessor); const std::vector* doseVals = spTestDoseAccessor->getDoseVector(); boost::shared_ptr spTestDoseIterator = - boost::make_shared(spDoseAccessor); + boost::make_shared(spDoseAccessor); DoseIteratorPointer spDoseIterator(spTestDoseIterator); DoseIteratorPointer spDoseIteratorNull; + if (argc > 3) + { + referenceXMLFilename = argv[1]; + doseFilename = argv[2]; + structFilename = argv[3]; + } + //1) test constructors // the values cannot be accessed from outside, therefore correct default values are not tested CHECK_THROW_EXPLICIT(rttb::algorithms::DoseStatisticsCalculator myDoseStatsCalculator( - spDoseIteratorNull), - core::NullPointerException); + spDoseIteratorNull), + core::NullPointerException); CHECK_NO_THROW(rttb::algorithms::DoseStatisticsCalculator myDoseStatsCalculator(spDoseIterator)); rttb::algorithms::DoseStatisticsCalculator myDoseStatsCalculator(spDoseIterator); //2) test setDoseIterator //3) test calculateDoseStatistics DoseStatisticsPointer theStatistics; //simple dose statistics CHECK_NO_THROW(theStatistics = myDoseStatsCalculator.calculateDoseStatistics()); CHECK_EQUAL(theStatistics->getMinimumVoxelPositions()->empty(), false); CHECK_EQUAL(theStatistics->getMaximumVoxelPositions()->empty(), false); CHECK_EQUAL(theStatistics->getAllVx().empty(), true); CHECK_EQUAL(theStatistics->getAllDx().empty(), true); CHECK_EQUAL(theStatistics->getAllVx().empty(), true); CHECK_EQUAL(theStatistics->getAllMaxOHx().empty(), true); CHECK_EQUAL(theStatistics->getAllMOHx().empty(), true); CHECK_EQUAL(theStatistics->getAllMOCx().empty(), true); CHECK_EQUAL(theStatistics->getAllMinOCx().empty(), true); //check default values for computeComplexMeasures=true DoseStatisticsPointer theStatisticsDefault; myDoseStatsCalculator.setMultiThreading(true); CHECK_NO_THROW(theStatisticsDefault = myDoseStatsCalculator.calculateDoseStatistics(true)); CHECK_NO_THROW(theStatisticsDefault->getVx(0.02 * theStatisticsDefault->getMaximum())); CHECK_NO_THROW(theStatisticsDefault->getVx(0.05 * theStatisticsDefault->getMaximum())); CHECK_NO_THROW(theStatisticsDefault->getVx(0.1 * theStatisticsDefault->getMaximum())); CHECK_NO_THROW(theStatisticsDefault->getVx(0.9 * theStatisticsDefault->getMaximum())); CHECK_NO_THROW(theStatisticsDefault->getVx(0.95 * theStatisticsDefault->getMaximum())); CHECK_NO_THROW(theStatisticsDefault->getVx(0.98 * theStatisticsDefault->getMaximum())); CHECK_NO_THROW(theStatisticsDefault->getDx(0.02 * theStatisticsDefault->getVolume())); CHECK_NO_THROW(theStatisticsDefault->getDx(0.05 * theStatisticsDefault->getVolume())); CHECK_NO_THROW(theStatisticsDefault->getDx(0.1 * theStatisticsDefault->getVolume())); CHECK_NO_THROW(theStatisticsDefault->getDx(0.9 * theStatisticsDefault->getVolume())); CHECK_NO_THROW(theStatisticsDefault->getDx(0.95 * theStatisticsDefault->getVolume())); CHECK_NO_THROW(theStatisticsDefault->getDx(0.98 * theStatisticsDefault->getVolume())); //check manually set reference dose and the default x values CHECK_NO_THROW(theStatistics = myDoseStatsCalculator.calculateDoseStatistics(100.0)); CHECK_THROW_EXPLICIT(theStatistics->getVx(0.1 * theStatistics->getMaximum()), - core::DataNotAvailableException); + core::DataNotAvailableException); CHECK_NO_THROW(theStatistics->getVx(0.1 * 100.0)); CHECK_NO_THROW(theStatistics->getDx(0.1 * theStatistics->getVolume())); CHECK_NO_THROW(theStatistics->getDx(0.9 * theStatistics->getVolume())); CHECK_NO_THROW(theStatistics->getMOHx(0.95 * theStatistics->getVolume())); CHECK_NO_THROW(theStatistics->getMOCx(0.98 * theStatistics->getVolume())); CHECK_EQUAL(theStatistics->getReferenceDose(), 100.0); //check manually set x values std::vector precomputeDoseValues, precomputeVolumeValues; precomputeDoseValues.push_back(0.01); precomputeDoseValues.push_back(0.02); precomputeDoseValues.push_back(0.05); precomputeVolumeValues.push_back(0.9); precomputeVolumeValues.push_back(0.95); precomputeVolumeValues.push_back(0.99); CHECK_NO_THROW(theStatistics = myDoseStatsCalculator.calculateDoseStatistics(precomputeDoseValues, - precomputeVolumeValues)); + precomputeVolumeValues)); CHECK_NO_THROW(theStatistics->getVx(0.01 * theStatistics->getMaximum())); CHECK_NO_THROW(theStatistics->getVx(0.02 * theStatistics->getMaximum())); CHECK_NO_THROW(theStatistics->getVx(0.05 * theStatistics->getMaximum())); CHECK_THROW_EXPLICIT(theStatistics->getVx(0.03 * theStatistics->getMaximum()), - core::DataNotAvailableException); + core::DataNotAvailableException); CHECK_NO_THROW(theStatistics->getDx(0.9 * theStatistics->getVolume())); CHECK_NO_THROW(theStatistics->getDx(0.95 * theStatistics->getVolume())); CHECK_NO_THROW(theStatistics->getDx(0.99 * theStatistics->getVolume())); CHECK_THROW_EXPLICIT(theStatistics->getDx(0.03 * theStatistics->getVolume()), - core::DataNotAvailableException); + core::DataNotAvailableException); CHECK_EQUAL(theStatistics->getVx(0.02 * theStatistics->getMaximum()), - theStatisticsDefault->getVx(0.02 * theStatistics->getMaximum())); + theStatisticsDefault->getVx(0.02 * theStatistics->getMaximum())); CHECK_EQUAL(theStatistics->getVx(0.05 * theStatistics->getMaximum()), - theStatisticsDefault->getVx(0.05 * theStatistics->getMaximum())); + theStatisticsDefault->getVx(0.05 * theStatistics->getMaximum())); CHECK_EQUAL(theStatistics->getDx(0.9 * theStatistics->getVolume()), - theStatisticsDefault->getDx(0.9 * theStatistics->getVolume())); + theStatisticsDefault->getDx(0.9 * theStatistics->getVolume())); CHECK_EQUAL(theStatistics->getDx(0.95 * theStatistics->getVolume()), - theStatisticsDefault->getDx(0.95 * theStatistics->getVolume())); + theStatisticsDefault->getDx(0.95 * theStatistics->getVolume())); //check manually set reference dose and x values CHECK_NO_THROW(theStatistics = myDoseStatsCalculator.calculateDoseStatistics(precomputeDoseValues, - precomputeVolumeValues, 100.0)); + precomputeVolumeValues, 100.0)); CHECK_THROW_EXPLICIT(theStatistics->getVx(0.01 * theStatistics->getMaximum()), - core::DataNotAvailableException); + core::DataNotAvailableException); CHECK_NO_THROW(theStatistics->getVx(0.01 * 100.0)); CHECK_NO_THROW(theStatistics->getDx(0.9 * theStatistics->getVolume())); CHECK_EQUAL(theStatistics->getReferenceDose(), 100.0); //MOHx, MOCx, MaxOHx and MinOCx are computed analogous to Dx, they will not be checked. //4) get statistical values CHECK_EQUAL(theStatistics->getNumberOfVoxels(), doseVals->size()); //compute simple statistical values (min, mean, max, stddev) for comparison DoseStatisticType maximum = 0; DoseStatisticType minimum = 1000000; DoseStatisticType mean = 0; DoseStatisticType variance = 0; std::vector::const_iterator doseIt = doseVals->begin(); while (doseIt != doseVals->end()) { if (maximum < *doseIt) { maximum = *doseIt; } if (minimum > *doseIt) { minimum = *doseIt; } mean += *doseIt; ++doseIt; } mean /= doseVals->size(); DoseTypeGy compMean = (int(mean * 100)) / 100; doseIt = doseVals->begin(); while (doseIt != doseVals->end()) { variance += pow(*doseIt - mean, 2); ++doseIt; } variance /= doseVals->size(); DoseStatisticType stdDev = pow(variance, 0.5); //we have some precision problems here... double errorConstantLarger = 1e-2; CHECK_EQUAL(theStatistics->getMaximum(), maximum); CHECK_EQUAL(theStatistics->getMinimum(), minimum); CHECK_CLOSE(theStatistics->getMean(), mean, errorConstantLarger); CHECK_CLOSE(theStatistics->getStdDeviation(), stdDev, errorConstantLarger); CHECK_CLOSE(theStatistics->getVariance(), variance, errorConstantLarger); //check for complex doseStatistics (maximumPositions, minimumPositions, Vx, Dx, MOHx, MOCx, MAXOHx, MinOCx) unsigned int nMax = 0, nMin = 0; doseIt = doseVals->begin(); while (doseIt != doseVals->end()) { if (*doseIt == theStatistics->getMaximum()) { nMax++; } if (*doseIt == theStatistics->getMinimum()) { nMin++; } ++doseIt; } //only 100 positions are stored if (nMax > 100) { nMax = 100; } if (nMin > 100) { nMin = 100; } auto maximaPositions = theStatistics->getMaximumVoxelPositions(); auto minimaPositions = theStatistics->getMinimumVoxelPositions(); CHECK_EQUAL(maximaPositions->size(), nMax); CHECK_EQUAL(minimaPositions->size(), nMin); for (auto maximaPositionsIterator = std::begin(*maximaPositions); - maximaPositionsIterator != std::end(*maximaPositions); - ++maximaPositionsIterator) + maximaPositionsIterator != std::end(*maximaPositions); + ++maximaPositionsIterator) { CHECK_EQUAL(maximaPositionsIterator->first, theStatistics->getMaximum()); } for (auto minimaPositionsIterator = std::begin(*minimaPositions); - minimaPositionsIterator != std::end(*minimaPositions); - ++minimaPositionsIterator) + minimaPositionsIterator != std::end(*minimaPositions); + ++minimaPositionsIterator) { CHECK_EQUAL(minimaPositionsIterator->first, theStatistics->getMinimum()); } //generate specific example dose maximum = 9.5; minimum = 2.5; mean = 6; int sizeTemplate = 500; std::vector aDoseVector; for (int i = 0; i < sizeTemplate; i++) { aDoseVector.push_back(maximum); aDoseVector.push_back(minimum); } core::GeometricInfo geoInfo = spTestDoseAccessor->getGeometricInfo(); geoInfo.setNumRows(20); geoInfo.setNumColumns(10); geoInfo.setNumSlices(5); boost::shared_ptr spTestDoseAccessor2 = - boost::make_shared(aDoseVector, geoInfo); + boost::make_shared(aDoseVector, geoInfo); DoseAccessorPointer spDoseAccessor2(spTestDoseAccessor2); boost::shared_ptr spTestDoseIterator2 = - boost::make_shared(spDoseAccessor2); + boost::make_shared(spDoseAccessor2); DoseIteratorPointer spDoseIterator2(spTestDoseIterator2); rttb::algorithms::DoseStatisticsCalculator myDoseStatsCalculator2(spDoseIterator2); DoseStatisticsPointer theStatistics3 = myDoseStatsCalculator2.calculateDoseStatistics(); CHECK_EQUAL(theStatistics3->getMaximum(), maximum); CHECK_EQUAL(theStatistics3->getMinimum(), minimum); CHECK_EQUAL(theStatistics3->getMean(), mean); maximaPositions = theStatistics3->getMaximumVoxelPositions(); minimaPositions = theStatistics3->getMinimumVoxelPositions(); CHECK_EQUAL(maximaPositions->empty(), false); CHECK_EQUAL(minimaPositions->empty(), false); for (auto maximaPositionsIterator = std::begin(*maximaPositions); - maximaPositionsIterator != std::end(*maximaPositions); - ++maximaPositionsIterator) + maximaPositionsIterator != std::end(*maximaPositions); + ++maximaPositionsIterator) { CHECK_EQUAL(maximaPositionsIterator->first, theStatistics3->getMaximum()); } for (auto minimaPositionsIterator = std::begin(*minimaPositions); - minimaPositionsIterator != std::end(*minimaPositions); - ++minimaPositionsIterator) + minimaPositionsIterator != std::end(*minimaPositions); + ++minimaPositionsIterator) { CHECK_EQUAL(minimaPositionsIterator->first, theStatistics3->getMinimum()); } + // compare with actual XML + io::dicom::DicomFileDoseAccessorGenerator doseAccessorGenerator(doseFilename.c_str()); + core::DoseAccessorInterface::DoseAccessorPointer doseAccessorPointer(doseAccessorGenerator.generateDoseAccessor()); + + rttb::io::dicom::DicomFileStructureSetGenerator structAccessorGenerator(structFilename.c_str()); + core::StructureSetGeneratorInterface::StructureSetPointer structerSetGeneratorPointer = structAccessorGenerator.generateStructureSet(); + + std::vector foundIndices = rttb::masks::VOIindexIdentifier::getIndicesByVoiRegex( + structerSetGeneratorPointer, "Heart"); + + CHECK_EQUAL(foundIndices.size(), 1); + + core::MaskAccessorInterface::MaskAccessorPointer maskAccessorPointer = boost::make_shared + (structerSetGeneratorPointer->getStructure(foundIndices.at(0)), doseAccessorPointer->getGeometricInfo(), true); + maskAccessorPointer->updateMask(); + + boost::shared_ptr maskedDoseIterator = + boost::make_shared(maskAccessorPointer, doseAccessorPointer); + rttb::core::DoseIteratorInterface::DoseIteratorPointer doseIteratorPointer(maskedDoseIterator); + + rttb::algorithms::DoseStatisticsCalculator doseStatisticsCalculator(doseIteratorPointer); + + DoseStatisticsPointer doseStatisticsActual = doseStatisticsCalculator.calculateDoseStatistics(14.0); + + io::other::DoseStatisticsXMLReader readerDefaultExpected(referenceXMLFilename); + auto doseStatisticsExpected = readerDefaultExpected.generateDoseStatistic(); + + CHECK(checkEqualDoseStatistic(doseStatisticsExpected, doseStatisticsActual)); + + RETURN_AND_REPORT_TEST_SUCCESS; } }//end namespace testing }//end namespace rttb diff --git a/testing/algorithms/files.cmake b/testing/algorithms/files.cmake index 01b0cac..1bb9327 100644 --- a/testing/algorithms/files.cmake +++ b/testing/algorithms/files.cmake @@ -1,20 +1,25 @@ SET(CPP_FILES DoseStatisticsTest.cpp DoseStatisticsCalculatorTest.cpp ArithmeticTest.cpp BinaryFunctorAccessorTest.cpp rttbAlgorithmsTests.cpp + ../io/other/CompareDoseStatistic.cpp + ../../code/io/other/rttbDoseStatisticsXMLReader.cpp #include dummy accessor files ../core/DummyDoseAccessor.cpp ../core/DummyMaskAccessor.cpp ../core/DummyMutableDoseAccessor.cpp ) SET(H_FILES + ../io/other/CompareDoseStatistic.h + ../../code/io/other/rttbDoseStatisticsXMLReader.h + #include dummy accessor files ../core/DummyDoseAccessor.h ../core/DummyMaskAccessor.h ../core/DummyMutableDoseAccessor.h ) diff --git a/testing/io/other/CompareDoseStatistic.cpp b/testing/io/other/CompareDoseStatistic.cpp index 0692244..6c556b5 100644 --- a/testing/io/other/CompareDoseStatistic.cpp +++ b/testing/io/other/CompareDoseStatistic.cpp @@ -1,116 +1,116 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision: 1221 $ (last changed revision) // @date $Date: 2015-12-01 13:43:31 +0100 (Di, 01 Dez 2015) $ (last change date) // @author $Author: hentsch $ (last changed by) */ #include "CompareDoseStatistic.h" namespace rttb { namespace testing { const double errorConstant = 1e-3;// errorConstant is so small because the double are castet to float when they are written bool checkEqualDoseStatistic(DoseStatisticsPtr aDoseStatistc1, DoseStatisticsPtr aDoseStatistic2){ bool result = lit::AreClose(aDoseStatistc1->getNumberOfVoxels(), aDoseStatistic2->getNumberOfVoxels(), 0.01); - result = result && lit::AreClose(aDoseStatistc1->getVolume(), aDoseStatistc1->getVolume(), errorConstant); + result = result && lit::AreClose(aDoseStatistc1->getVolume(), aDoseStatistic2->getVolume(), errorConstant); result = result && lit::AreClose(aDoseStatistc1->getMinimum(), aDoseStatistic2->getMinimum(), errorConstant); result = result && lit::AreClose(aDoseStatistc1->getMaximum(), aDoseStatistic2->getMaximum(), errorConstant); result = result && lit::AreClose(aDoseStatistc1->getMean(), aDoseStatistic2->getMean(), errorConstant); result = result && lit::AreClose(aDoseStatistc1->getStdDeviation(), aDoseStatistic2->getStdDeviation(), errorConstant); result = result && mapCompare(aDoseStatistc1->getAllDx(), aDoseStatistic2->getAllDx()); result = result && mapCompare(aDoseStatistc1->getAllVx(), aDoseStatistic2->getAllVx()); result = result && mapCompare(aDoseStatistc1->getAllMaxOHx(), aDoseStatistic2->getAllMaxOHx()); result = result && mapCompare(aDoseStatistc1->getAllMinOCx(), aDoseStatistic2->getAllMinOCx()); result = result && mapCompare(aDoseStatistc1->getAllMOCx(), aDoseStatistic2->getAllMOCx()); result = result && mapCompare(aDoseStatistc1->getAllMOHx(), aDoseStatistic2->getAllMOHx()); return result; } std::map::const_iterator findNearestKeyInMap(const std::map& aMap, double key) { double minDistance = 1e19; double minDistanceLast = 1e20; auto iterator = std::begin(aMap); while (iterator != std::end(aMap)) { minDistanceLast = minDistance; minDistance = fabs(iterator->first - key); if (minDistanceLast > minDistance) { ++iterator; } else { if (iterator != std::begin(aMap)) { --iterator; return iterator; } else { return std::begin(aMap); } } } --iterator; return iterator; } double getValue(const std::map& aMap, double key) { if (aMap.find(key) != std::end(aMap)) { return aMap.find(key)->second; } else { auto iterator = findNearestKeyInMap(aMap, key); return iterator->second; } } bool mapCompare(const std::map& lhs, const std::map& rhs) { if (lhs.size() != rhs.size()){ return false; } for (std::map::const_iterator i = rhs.cbegin(); i != rhs.cend(); ++i) { double a = i->second; double b = getValue(lhs, i->first) ; if (std::abs(a - b ) > errorConstant){// errorConstant is 1e-3 because the double-->float cast when they are written return false; } } return true; } }//testing }//rttb