diff --git a/src/FoundationClasses/TKMath/GTests/FILES.cmake b/src/FoundationClasses/TKMath/GTests/FILES.cmake index 1c049c271a..ab7d9a4b08 100644 --- a/src/FoundationClasses/TKMath/GTests/FILES.cmake +++ b/src/FoundationClasses/TKMath/GTests/FILES.cmake @@ -4,5 +4,6 @@ set(OCCT_TKMath_GTests_FILES_LOCATION "${CMAKE_CURRENT_LIST_DIR}") set(OCCT_TKMath_GTests_FILES Bnd_BoundSortBox_Test.cxx ElCLib_Test.cxx + math_DoubleTab_Test.cxx math_Matrix_Test.cxx ) diff --git a/src/FoundationClasses/TKMath/GTests/math_DoubleTab_Test.cxx b/src/FoundationClasses/TKMath/GTests/math_DoubleTab_Test.cxx new file mode 100644 index 0000000000..8f5f29473b --- /dev/null +++ b/src/FoundationClasses/TKMath/GTests/math_DoubleTab_Test.cxx @@ -0,0 +1,215 @@ +// Copyright (c) 2025 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include + +#include + +#include +#include +#include + +// Tests for constructors +TEST(MathDoubleTabTest, DefaultConstructor) +{ + // Test standard constructor with ranges + math_DoubleTab aTab(1, 3, 1, 3); + + // Initialize with test values + aTab.Init(5.0); + + // Check all values are set to 5.0 + for (Standard_Integer anI = 1; anI <= 3; anI++) + { + for (Standard_Integer aJ = 1; aJ <= 3; aJ++) + { + EXPECT_DOUBLE_EQ(aTab.Value(anI, aJ), 5.0); + EXPECT_DOUBLE_EQ(aTab(anI, aJ), 5.0); // Test operator() + } + } +} + +TEST(MathDoubleTabTest, ExternalArrayConstructor) +{ + // Create external array + Standard_Real anArray[9] = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0}; + + // Create math_DoubleTab from external array + math_DoubleTab aTab(anArray, 1, 3, 1, 3); + + // Check values are correctly accessed + Standard_Integer anIndex = 0; + for (Standard_Integer anI = 1; anI <= 3; anI++) + { + for (Standard_Integer aJ = 1; aJ <= 3; aJ++) + { + EXPECT_DOUBLE_EQ(aTab.Value(anI, aJ), anArray[anIndex]); + anIndex++; + } + } +} + +TEST(MathDoubleTabTest, CopyConstructor) +{ + // Create original array + math_DoubleTab aOriginal(1, 3, 1, 3); + aOriginal.Init(10.0); + + // Set some specific values + aOriginal.Value(1, 1) = 1.5; + aOriginal.Value(2, 2) = 2.5; + aOriginal.Value(3, 3) = 3.5; + + // Create copy + math_DoubleTab aCopy(aOriginal); + + // Check values are copied correctly + EXPECT_DOUBLE_EQ(aCopy.Value(1, 1), 1.5); + EXPECT_DOUBLE_EQ(aCopy.Value(2, 2), 2.5); + EXPECT_DOUBLE_EQ(aCopy.Value(3, 3), 3.5); + EXPECT_DOUBLE_EQ(aCopy.Value(1, 2), 10.0); // Check other values too +} + +// Tests for basic operations +TEST(MathDoubleTabTest, InitOperation) +{ + math_DoubleTab aTab(0, 2, 0, 2); + + // Initialize with specific value + aTab.Init(7.5); + + // Check all elements are set + for (Standard_Integer anI = 0; anI <= 2; anI++) + { + for (Standard_Integer aJ = 0; aJ <= 2; aJ++) + { + EXPECT_DOUBLE_EQ(aTab.Value(anI, aJ), 7.5); + } + } +} + +TEST(MathDoubleTabTest, ValueAccess) +{ + math_DoubleTab aTab(1, 2, 1, 2); + + // Set values using Value method + aTab.Value(1, 1) = 11.0; + aTab.Value(1, 2) = 12.0; + aTab.Value(2, 1) = 21.0; + aTab.Value(2, 2) = 22.0; + + // Check values using operator() + EXPECT_DOUBLE_EQ(aTab(1, 1), 11.0); + EXPECT_DOUBLE_EQ(aTab(1, 2), 12.0); + EXPECT_DOUBLE_EQ(aTab(2, 1), 21.0); + EXPECT_DOUBLE_EQ(aTab(2, 2), 22.0); +} + +TEST(MathDoubleTabTest, OperatorAccess) +{ + math_DoubleTab aTab(-1, 1, -1, 1); + + // Set values using operator() + aTab(-1, -1) = -11.0; + aTab(-1, 0) = -10.0; + aTab(-1, 1) = -9.0; + aTab(0, -1) = -1.0; + aTab(0, 0) = 0.0; + aTab(0, 1) = 1.0; + aTab(1, -1) = 9.0; + aTab(1, 0) = 10.0; + aTab(1, 1) = 11.0; + + // Check values using Value method + EXPECT_DOUBLE_EQ(aTab.Value(-1, -1), -11.0); + EXPECT_DOUBLE_EQ(aTab.Value(-1, 0), -10.0); + EXPECT_DOUBLE_EQ(aTab.Value(-1, 1), -9.0); + EXPECT_DOUBLE_EQ(aTab.Value(0, -1), -1.0); + EXPECT_DOUBLE_EQ(aTab.Value(0, 0), 0.0); + EXPECT_DOUBLE_EQ(aTab.Value(0, 1), 1.0); + EXPECT_DOUBLE_EQ(aTab.Value(1, -1), 9.0); + EXPECT_DOUBLE_EQ(aTab.Value(1, 0), 10.0); + EXPECT_DOUBLE_EQ(aTab.Value(1, 1), 11.0); +} + +TEST(MathDoubleTabTest, CopyMethod) +{ + // Create source array + math_DoubleTab aSource(1, 2, 1, 2); + aSource.Value(1, 1) = 100.0; + aSource.Value(1, 2) = 200.0; + aSource.Value(2, 1) = 300.0; + aSource.Value(2, 2) = 400.0; + + // Create destination array + math_DoubleTab aDest(1, 2, 1, 2); + aDest.Init(0.0); + + // Copy from source to destination + aSource.Copy(aDest); + + // Check values are copied + EXPECT_DOUBLE_EQ(aDest.Value(1, 1), 100.0); + EXPECT_DOUBLE_EQ(aDest.Value(1, 2), 200.0); + EXPECT_DOUBLE_EQ(aDest.Value(2, 1), 300.0); + EXPECT_DOUBLE_EQ(aDest.Value(2, 2), 400.0); +} + +// Tests for buffer optimization +TEST(MathDoubleTabTest, SmallArrayOptimization) +{ + // Test that small arrays (<=16 elements) use buffer optimization + // 4x4 = 16 elements, should use buffer + math_DoubleTab aSmallTab(1, 4, 1, 4); + aSmallTab.Init(42.0); + + // Set and read values to ensure it works correctly + aSmallTab.Value(2, 3) = 123.45; + EXPECT_DOUBLE_EQ(aSmallTab.Value(2, 3), 123.45); + EXPECT_DOUBLE_EQ(aSmallTab.Value(1, 1), 42.0); +} + +TEST(MathDoubleTabTest, LargeArrayAllocation) +{ + // Test that large arrays (>16 elements) allocate memory + // 5x5 = 25 elements, should allocate + math_DoubleTab aLargeTab(1, 5, 1, 5); + aLargeTab.Init(99.99); + + // Set and read values to ensure it works correctly + aLargeTab.Value(3, 4) = 987.65; + EXPECT_DOUBLE_EQ(aLargeTab.Value(3, 4), 987.65); + EXPECT_DOUBLE_EQ(aLargeTab.Value(1, 1), 99.99); +} + +// Tests for edge cases +TEST(MathDoubleTabTest, SingleElement) +{ + // Test with single element array + math_DoubleTab aSingle(5, 5, 10, 10); + aSingle.Value(5, 10) = 777.0; + + EXPECT_DOUBLE_EQ(aSingle.Value(5, 10), 777.0); + EXPECT_DOUBLE_EQ(aSingle(5, 10), 777.0); +} + +TEST(MathDoubleTabTest, NegativeIndices) +{ + // Test with negative indices + math_DoubleTab aNegTab(-5, -1, -3, -1); + aNegTab.Init(-1.0); + + aNegTab.Value(-3, -2) = -999.0; + EXPECT_DOUBLE_EQ(aNegTab.Value(-3, -2), -999.0); + EXPECT_DOUBLE_EQ(aNegTab.Value(-5, -3), -1.0); +} diff --git a/src/FoundationClasses/TKMath/math/FILES.cmake b/src/FoundationClasses/TKMath/math/FILES.cmake index 31f30cffde..a5865e1f4a 100644 --- a/src/FoundationClasses/TKMath/math/FILES.cmake +++ b/src/FoundationClasses/TKMath/math/FILES.cmake @@ -31,9 +31,7 @@ set(OCCT_math_FILES math_DirectPolynomialRoots.cxx math_DirectPolynomialRoots.hxx math_DirectPolynomialRoots.lxx - math_DoubleTab.cxx math_DoubleTab.hxx - math_DoubleTab.lxx math_EigenValuesSearcher.cxx math_EigenValuesSearcher.hxx math_FRPR.cxx diff --git a/src/FoundationClasses/TKMath/math/math_DoubleTab.cxx b/src/FoundationClasses/TKMath/math/math_DoubleTab.cxx deleted file mode 100644 index ffe45a0581..0000000000 --- a/src/FoundationClasses/TKMath/math/math_DoubleTab.cxx +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright (c) 1997-1999 Matra Datavision -// Copyright (c) 1999-2014 OPEN CASCADE SAS -// -// This file is part of Open CASCADE Technology software library. -// -// This library is free software; you can redistribute it and/or modify it under -// the terms of the GNU Lesser General Public License version 2.1 as published -// by the Free Software Foundation, with special exception defined in the file -// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT -// distribution for complete text of the license and disclaimer of any warranty. -// -// Alternatively, this file may be used under the terms of Open CASCADE -// commercial license or contractual agreement. - -// Lpa, le 7/02/92 - -#include -#include - -// macro to get size of C array -#define CARRAY_LENGTH(arr) (int)(sizeof(arr) / sizeof(arr[0])) - -void math_DoubleTab::Allocate() -{ - Standard_Integer RowNumber = UppR - LowR + 1; - Standard_Integer ColNumber = UppC - LowC + 1; - - if (isAllocated) - Addr = (Standard_Real*)Standard::Allocate(RowNumber * ColNumber * sizeof(Standard_Real)); -} - -math_DoubleTab::math_DoubleTab(const Standard_Integer LowerRow, - const Standard_Integer UpperRow, - const Standard_Integer LowerCol, - const Standard_Integer UpperCol) - : Addr(Buf), - isAllocated((UpperRow - LowerRow + 1) * (UpperCol - LowerCol + 1) > CARRAY_LENGTH(Buf)), - LowR(LowerRow), - UppR(UpperRow), - LowC(LowerCol), - UppC(UpperCol) -{ - Allocate(); -} - -math_DoubleTab::math_DoubleTab(const Standard_Address Tab, - const Standard_Integer LowerRow, - const Standard_Integer UpperRow, - const Standard_Integer LowerCol, - const Standard_Integer UpperCol) - : Addr(Tab), - isAllocated(Standard_False), - LowR(LowerRow), - UppR(UpperRow), - LowC(LowerCol), - UppC(UpperCol) -{ - Allocate(); -} - -void math_DoubleTab::Init(const Standard_Real InitValue) -{ - for (Standard_Integer anIndex = 0; anIndex < (UppR - LowR + 1) * (UppC - LowC + 1); anIndex++) - { - ((Standard_Real*)Addr)[anIndex] = InitValue; - } -} - -math_DoubleTab::math_DoubleTab(const math_DoubleTab& Other) - : Addr(Buf), - isAllocated((Other.UppR - Other.LowR + 1) * (Other.UppC - Other.LowC + 1) - > CARRAY_LENGTH(Buf)), - LowR(Other.LowR), - UppR(Other.UppR), - LowC(Other.LowC), - UppC(Other.UppC) -{ - Allocate(); - memmove(Addr, Other.Addr, (int)((UppR - LowR + 1) * (UppC - LowC + 1) * sizeof(Standard_Real))); -} - -void math_DoubleTab::Free() -{ - // free the data - if (isAllocated) - { - Standard::Free(Addr); - } - - Addr = 0; -} - -void math_DoubleTab::SetLowerRow(const Standard_Integer LowerRow) -{ - UppR = UppR - LowR + LowerRow; - LowR = LowerRow; -} - -void math_DoubleTab::SetLowerCol(const Standard_Integer LowerCol) -{ - UppC = UppC - LowC + LowerCol; - LowC = LowerCol; -} diff --git a/src/FoundationClasses/TKMath/math/math_DoubleTab.hxx b/src/FoundationClasses/TKMath/math/math_DoubleTab.hxx index 2c6fc3b37e..be337d45de 100644 --- a/src/FoundationClasses/TKMath/math/math_DoubleTab.hxx +++ b/src/FoundationClasses/TKMath/math/math_DoubleTab.hxx @@ -20,60 +20,104 @@ #include #include #include +#include #include #include +#include + class math_DoubleTab { + static const Standard_Integer THE_BUFFER_SIZE = 16; + public: - DEFINE_STANDARD_ALLOC + DEFINE_STANDARD_ALLOC; + DEFINE_NCOLLECTION_ALLOC; - Standard_EXPORT math_DoubleTab(const Standard_Integer LowerRow, - const Standard_Integer UpperRow, - const Standard_Integer LowerCol, - const Standard_Integer UpperCol); - - Standard_EXPORT math_DoubleTab(const Standard_Address Tab, - const Standard_Integer LowerRow, - const Standard_Integer UpperRow, - const Standard_Integer LowerCol, - const Standard_Integer UpperCol); - - Standard_EXPORT void Init(const Standard_Real InitValue); - - Standard_EXPORT math_DoubleTab(const math_DoubleTab& Other); - - void Copy(math_DoubleTab& Other) const; - - Standard_EXPORT void SetLowerRow(const Standard_Integer LowerRow); - - Standard_EXPORT void SetLowerCol(const Standard_Integer LowerCol); - - Standard_Real& Value(const Standard_Integer RowIndex, const Standard_Integer ColIndex) const; - - Standard_Real& operator()(const Standard_Integer RowIndex, const Standard_Integer ColIndex) const +public: + //! Constructor for ranges [theLowerRow..theUpperRow, theLowerCol..theUpperCol] + math_DoubleTab(const Standard_Integer theLowerRow, + const Standard_Integer theUpperRow, + const Standard_Integer theLowerCol, + const Standard_Integer theUpperCol) + : myBuffer{}, + myArray( + (theUpperRow - theLowerRow + 1) * (theUpperCol - theLowerCol + 1) <= THE_BUFFER_SIZE + ? NCollection_Array2(*myBuffer.data(), + theLowerRow, + theUpperRow, + theLowerCol, + theUpperCol) + : NCollection_Array2(theLowerRow, theUpperRow, theLowerCol, theUpperCol)) { - return Value(RowIndex, ColIndex); } - Standard_EXPORT void Free(); +public: + //! Constructor from external data array + math_DoubleTab(const Standard_Address theTab, + const Standard_Integer theLowerRow, + const Standard_Integer theUpperRow, + const Standard_Integer theLowerCol, + const Standard_Integer theUpperCol) + : myArray(*static_cast(theTab), + theLowerRow, + theUpperRow, + theLowerCol, + theUpperCol) + { + } - ~math_DoubleTab() { Free(); } + //! Initialize all elements with theInitValue + void Init(const Standard_Real theInitValue) { myArray.Init(theInitValue); } + + //! Copy constructor + math_DoubleTab(const math_DoubleTab& theOther) + : myArray(theOther.myArray) + { + } + + //! Copy data to theOther + void Copy(math_DoubleTab& theOther) const { theOther.myArray.Assign(myArray); } + + //! Set lower row index + void SetLowerRow(const Standard_Integer theLowerRow) { myArray.UpdateLowerRow(theLowerRow); } + + //! Set lower column index + void SetLowerCol(const Standard_Integer theLowerCol) { myArray.UpdateLowerCol(theLowerCol); } + + //! Access element at (theRowIndex, theColIndex) + const Standard_Real& Value(const Standard_Integer theRowIndex, + const Standard_Integer theColIndex) const + { + return myArray.Value(theRowIndex, theColIndex); + } + + //! Change element at (theRowIndex, theColIndex) + Standard_Real& Value(const Standard_Integer theRowIndex, const Standard_Integer theColIndex) + { + return myArray.ChangeValue(theRowIndex, theColIndex); + } + + //! Operator() - alias to Value + const Standard_Real& operator()(const Standard_Integer theRowIndex, + const Standard_Integer theColIndex) const + { + return Value(theRowIndex, theColIndex); + } + + //! Operator() - alias to ChangeValue + Standard_Real& operator()(const Standard_Integer theRowIndex, const Standard_Integer theColIndex) + { + return Value(theRowIndex, theColIndex); + } + + //! Destructor + ~math_DoubleTab() = default; -protected: private: - Standard_EXPORT void Allocate(); - - Standard_Address Addr; - Standard_Real Buf[16]{}; - Standard_Boolean isAllocated; - Standard_Integer LowR; - Standard_Integer UppR; - Standard_Integer LowC; - Standard_Integer UppC; + std::array myBuffer; + NCollection_Array2 myArray; }; -#include - #endif // _math_DoubleTab_HeaderFile diff --git a/src/FoundationClasses/TKMath/math/math_DoubleTab.lxx b/src/FoundationClasses/TKMath/math/math_DoubleTab.lxx deleted file mode 100644 index 294efd09a3..0000000000 --- a/src/FoundationClasses/TKMath/math/math_DoubleTab.lxx +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) 1997-1999 Matra Datavision -// Copyright (c) 1999-2014 OPEN CASCADE SAS -// -// This file is part of Open CASCADE Technology software library. -// -// This library is free software; you can redistribute it and/or modify it under -// the terms of the GNU Lesser General Public License version 2.1 as published -// by the Free Software Foundation, with special exception defined in the file -// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT -// distribution for complete text of the license and disclaimer of any warranty. -// -// Alternatively, this file may be used under the terms of Open CASCADE -// commercial license or contractual agreement. - -// Lpa, le 7/02/92 - -#include - -#include - -inline Standard_Real& math_DoubleTab::Value(const Standard_Integer RowIndex, - const Standard_Integer ColIndex) const -{ - return ((Standard_Real*)Addr)[(UppC - LowC + 1) * (RowIndex - LowR) + (ColIndex - LowC)]; -} - -inline void math_DoubleTab::Copy(math_DoubleTab& Other) const -{ - memmove(Other.Addr, Addr, (int)((UppR - LowR + 1) * (UppC - LowC + 1) * sizeof(Standard_Real))); -} diff --git a/src/FoundationClasses/TKMath/math/math_Matrix.hxx b/src/FoundationClasses/TKMath/math/math_Matrix.hxx index e05b4e917e..bacdfd38b4 100644 --- a/src/FoundationClasses/TKMath/math/math_Matrix.hxx +++ b/src/FoundationClasses/TKMath/math/math_Matrix.hxx @@ -349,13 +349,24 @@ public: //! An exception is raised if the dimensions are different. Standard_EXPORT void Subtract(const math_Matrix& Left, const math_Matrix& Right); + //! Accesses the value of index + //! and of a matrix. + //! An exception is raised if and are not + //! in the correct range. + const Standard_Real& Value(const Standard_Integer Row, const Standard_Integer Col) const; + //! Accesses (in read or write mode) the value of index //! and of a matrix. //! An exception is raised if and are not //! in the correct range. - Standard_Real& Value(const Standard_Integer Row, const Standard_Integer Col) const; + Standard_Real& Value(const Standard_Integer Row, const Standard_Integer Col); - Standard_Real& operator()(const Standard_Integer Row, const Standard_Integer Col) const + const Standard_Real& operator()(const Standard_Integer Row, const Standard_Integer Col) const + { + return Value(Row, Col); + } + + Standard_Real& operator()(const Standard_Integer Row, const Standard_Integer Col) { return Value(Row, Col); } diff --git a/src/FoundationClasses/TKMath/math/math_Matrix.lxx b/src/FoundationClasses/TKMath/math/math_Matrix.lxx index 07cbca89f1..aa11c220a0 100644 --- a/src/FoundationClasses/TKMath/math/math_Matrix.lxx +++ b/src/FoundationClasses/TKMath/math/math_Matrix.lxx @@ -27,14 +27,23 @@ inline math_Matrix operator*(const Standard_Real Left, const math_Matrix& Right) return Right.Multiplied(Left); } -inline Standard_Real& math_Matrix::Value(const Standard_Integer Row, - const Standard_Integer Col) const +inline const Standard_Real& math_Matrix::Value(const Standard_Integer Row, + const Standard_Integer Col) const { Standard_RangeError_Raise_if(((Row < LowerRowIndex) || (Row > UpperRowIndex) || (Col < LowerColIndex) || (Col > UpperColIndex)), " "); - return Array(Row, Col); + return Array.Value(Row, Col); +} + +inline Standard_Real& math_Matrix::Value(const Standard_Integer Row, const Standard_Integer Col) +{ + Standard_RangeError_Raise_if(((Row < LowerRowIndex) || (Row > UpperRowIndex) + || (Col < LowerColIndex) || (Col > UpperColIndex)), + " "); + + return Array.Value(Row, Col); } inline Standard_Integer math_Matrix::RowNumber() const diff --git a/src/FoundationClasses/TKernel/GTests/FILES.cmake b/src/FoundationClasses/TKernel/GTests/FILES.cmake index e11faf7f6f..84d82d1f06 100644 --- a/src/FoundationClasses/TKernel/GTests/FILES.cmake +++ b/src/FoundationClasses/TKernel/GTests/FILES.cmake @@ -3,6 +3,7 @@ set(OCCT_TKernel_GTests_FILES_LOCATION "${CMAKE_CURRENT_LIST_DIR}") set(OCCT_TKernel_GTests_FILES NCollection_Array1_Test.cxx + NCollection_Array2_Test.cxx NCollection_BaseAllocator_Test.cxx NCollection_DataMap_Test.cxx NCollection_DoubleMap_Test.cxx diff --git a/src/FoundationClasses/TKernel/GTests/NCollection_Array1_Test.cxx b/src/FoundationClasses/TKernel/GTests/NCollection_Array1_Test.cxx index 01e9a74b35..9995c3e652 100644 --- a/src/FoundationClasses/TKernel/GTests/NCollection_Array1_Test.cxx +++ b/src/FoundationClasses/TKernel/GTests/NCollection_Array1_Test.cxx @@ -16,16 +16,7 @@ #include -// Test fixture for NCollection_Array1 tests -class NCollection_Array1Test : public testing::Test -{ -protected: - void SetUp() override {} - - void TearDown() override {} -}; - -TEST_F(NCollection_Array1Test, DefaultConstructor) +TEST(NCollection_Array1Test, DefaultConstructor) { // Default constructor should not compile as it's explicitly deleted // NCollection_Array1 anArray; @@ -37,7 +28,7 @@ TEST_F(NCollection_Array1Test, DefaultConstructor) EXPECT_EQ(10, anArray.Upper()); } -TEST_F(NCollection_Array1Test, ConstructorWithBounds) +TEST(NCollection_Array1Test, ConstructorWithBounds) { // Test constructor with explicit bounds NCollection_Array1 anArray(1, 5); @@ -46,7 +37,7 @@ TEST_F(NCollection_Array1Test, ConstructorWithBounds) EXPECT_EQ(5, anArray.Upper()); } -TEST_F(NCollection_Array1Test, ConstructorWithNegativeBounds) +TEST(NCollection_Array1Test, ConstructorWithNegativeBounds) { // Test constructor with negative bounds NCollection_Array1 anArray(-3, 2); @@ -55,7 +46,7 @@ TEST_F(NCollection_Array1Test, ConstructorWithNegativeBounds) EXPECT_EQ(2, anArray.Upper()); } -TEST_F(NCollection_Array1Test, AssignmentValue) +TEST(NCollection_Array1Test, AssignmentValue) { NCollection_Array1 anArray(1, 5); @@ -78,7 +69,7 @@ TEST_F(NCollection_Array1Test, AssignmentValue) } } -TEST_F(NCollection_Array1Test, CopyConstructor) +TEST(NCollection_Array1Test, CopyConstructor) { NCollection_Array1 anArray1(1, 5); @@ -106,7 +97,7 @@ TEST_F(NCollection_Array1Test, CopyConstructor) EXPECT_NE(anArray1(3), anArray2(3)); } -TEST_F(NCollection_Array1Test, ValueAccess) +TEST(NCollection_Array1Test, ValueAccess) { NCollection_Array1 anArray(1, 5); @@ -124,7 +115,7 @@ TEST_F(NCollection_Array1Test, ValueAccess) } } -TEST_F(NCollection_Array1Test, ChangeValueAccess) +TEST(NCollection_Array1Test, ChangeValueAccess) { NCollection_Array1 anArray(1, 5); @@ -153,7 +144,7 @@ TEST_F(NCollection_Array1Test, ChangeValueAccess) } } -TEST_F(NCollection_Array1Test, AssignmentOperator) +TEST(NCollection_Array1Test, AssignmentOperator) { NCollection_Array1 anArray1(1, 5); @@ -179,7 +170,7 @@ TEST_F(NCollection_Array1Test, AssignmentOperator) } } -TEST_F(NCollection_Array1Test, Move) +TEST(NCollection_Array1Test, Move) { NCollection_Array1 anArray1(1, 5); @@ -199,15 +190,12 @@ TEST_F(NCollection_Array1Test, Move) EXPECT_EQ(1, anArray2.Lower()); EXPECT_EQ(5, anArray2.Upper()); - // Original array is keep referecing the same data - EXPECT_EQ(anArray1.Lower(), anArray2.Lower()); - EXPECT_EQ(anArray1.Upper(), anArray2.Upper()); - EXPECT_EQ(anArray1(1), anArray2(1)); - EXPECT_EQ(anArray1(2), anArray2(2)); - EXPECT_EQ(5, anArray1.Length()); + // Original array is not keep referecing the same data + EXPECT_EQ(anArray1.Length(), 0); + EXPECT_EQ(anArray1.Lower(), 1); } -TEST_F(NCollection_Array1Test, Init) +TEST(NCollection_Array1Test, Init) { NCollection_Array1 anArray(1, 5); @@ -221,7 +209,7 @@ TEST_F(NCollection_Array1Test, Init) } } -TEST_F(NCollection_Array1Test, SetValue) +TEST(NCollection_Array1Test, SetValue) { NCollection_Array1 anArray(1, 5); @@ -232,7 +220,7 @@ TEST_F(NCollection_Array1Test, SetValue) EXPECT_EQ(123, anArray(3)); } -TEST_F(NCollection_Array1Test, FirstLast) +TEST(NCollection_Array1Test, FirstLast) { NCollection_Array1 anArray(5, 10); @@ -251,7 +239,7 @@ TEST_F(NCollection_Array1Test, FirstLast) EXPECT_EQ(10101, anArray.Last()); } -TEST_F(NCollection_Array1Test, STLIteration) +TEST(NCollection_Array1Test, STLIteration) { NCollection_Array1 anArray(1, 5); for (Standard_Integer i = anArray.Lower(); i <= anArray.Upper(); i++) @@ -268,7 +256,7 @@ TEST_F(NCollection_Array1Test, STLIteration) } } -TEST_F(NCollection_Array1Test, Resize) +TEST(NCollection_Array1Test, Resize) { NCollection_Array1 anArray(1, 5); for (Standard_Integer i = anArray.Lower(); i <= anArray.Upper(); i++) @@ -311,7 +299,7 @@ TEST_F(NCollection_Array1Test, Resize) } } -TEST_F(NCollection_Array1Test, ChangeValue) +TEST(NCollection_Array1Test, ChangeValue) { NCollection_Array1 anArray(1, 5); anArray.Init(42); @@ -329,7 +317,7 @@ TEST_F(NCollection_Array1Test, ChangeValue) EXPECT_EQ(42, anArray(5)); } -TEST_F(NCollection_Array1Test, IteratorAccess) +TEST(NCollection_Array1Test, IteratorAccess) { NCollection_Array1 anArray(1, 5); for (Standard_Integer i = 1; i <= 5; i++) diff --git a/src/FoundationClasses/TKernel/GTests/NCollection_Array2_Test.cxx b/src/FoundationClasses/TKernel/GTests/NCollection_Array2_Test.cxx new file mode 100644 index 0000000000..9e88e76677 --- /dev/null +++ b/src/FoundationClasses/TKernel/GTests/NCollection_Array2_Test.cxx @@ -0,0 +1,321 @@ +// Copyright (c) 2025 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include +#include + +#include + +// --- Constructor Tests --- + +TEST(NCollection_Array2Test, DefaultConstructor) +{ + NCollection_Array2 anArray; + EXPECT_EQ(0, anArray.Length()); + EXPECT_EQ(0, anArray.NbRows()); + EXPECT_EQ(0, anArray.NbColumns()); +} + +TEST(NCollection_Array2Test, ConstructorWithBounds) +{ + NCollection_Array2 anArray(1, 5, 1, 10); + EXPECT_EQ(50, anArray.Length()); + EXPECT_EQ(5, anArray.NbRows()); + EXPECT_EQ(10, anArray.NbColumns()); + EXPECT_EQ(1, anArray.LowerRow()); + EXPECT_EQ(5, anArray.UpperRow()); + EXPECT_EQ(1, anArray.LowerCol()); + EXPECT_EQ(10, anArray.UpperCol()); +} + +TEST(NCollection_Array2Test, ConstructorWithNegativeBounds) +{ + NCollection_Array2 anArray(-2, 2, -5, 5); // 5 rows, 11 cols + EXPECT_EQ(55, anArray.Length()); + EXPECT_EQ(5, anArray.NbRows()); + EXPECT_EQ(11, anArray.NbColumns()); + EXPECT_EQ(-2, anArray.LowerRow()); + EXPECT_EQ(2, anArray.UpperRow()); + EXPECT_EQ(-5, anArray.LowerCol()); + EXPECT_EQ(5, anArray.UpperCol()); +} + +// --- Data Access and Manipulation --- + +TEST(NCollection_Array2Test, ValueAccess) +{ + NCollection_Array2 anArray(1, 3, 1, 4); + for (Standard_Integer aRowIter = anArray.LowerRow(); aRowIter <= anArray.UpperRow(); ++aRowIter) + { + for (Standard_Integer aColIter = anArray.LowerCol(); aColIter <= anArray.UpperCol(); ++aColIter) + { + anArray.SetValue(aRowIter, aColIter, aRowIter * 100 + aColIter); + } + } + + for (Standard_Integer aRowIter = anArray.LowerRow(); aRowIter <= anArray.UpperRow(); ++aRowIter) + { + for (Standard_Integer aColIter = anArray.LowerCol(); aColIter <= anArray.UpperCol(); ++aColIter) + { + EXPECT_EQ(aRowIter * 100 + aColIter, anArray.Value(aRowIter, aColIter)); + EXPECT_EQ(aRowIter * 100 + aColIter, anArray(aRowIter, aColIter)); // Using operator() + } + } +} + +TEST(NCollection_Array2Test, ChangeValueAccess) +{ + NCollection_Array2 anArray(0, 2, 0, 3); + for (Standard_Integer aRowIter = anArray.LowerRow(); aRowIter <= anArray.UpperRow(); ++aRowIter) + { + for (Standard_Integer aColIter = anArray.LowerCol(); aColIter <= anArray.UpperCol(); ++aColIter) + { + anArray.ChangeValue(aRowIter, aColIter) = aRowIter * 100 + aColIter; + } + } + + // Verify initial values + EXPECT_EQ(102, anArray(1, 2)); + + // Modify a value + anArray.ChangeValue(1, 2) = 999; + EXPECT_EQ(999, anArray(1, 2)); +} + +TEST(NCollection_Array2Test, Init) +{ + NCollection_Array2 anArray(1, 5, 1, 5); + anArray.Init(42); + for (Standard_Integer aRowIter = anArray.LowerRow(); aRowIter <= anArray.UpperRow(); ++aRowIter) + { + for (Standard_Integer aColIter = anArray.LowerCol(); aColIter <= anArray.UpperCol(); ++aColIter) + { + EXPECT_EQ(42, anArray(aRowIter, aColIter)); + } + } +} + +// --- Copy and Move Semantics --- + +TEST(NCollection_Array2Test, CopyConstructor) +{ + NCollection_Array2 anArray1(1, 3, 1, 4); + anArray1.Init(123); + + NCollection_Array2 anArray2(anArray1); + + // Verify dimensions and data are copied + EXPECT_EQ(anArray1.Length(), anArray2.Length()); + EXPECT_EQ(anArray1.NbRows(), anArray2.NbRows()); + EXPECT_EQ(anArray1.NbColumns(), anArray2.NbColumns()); + EXPECT_EQ(anArray1(2, 3), anArray2(2, 3)); + + // Modify original to ensure it was a deep copy + anArray1.SetValue(2, 3, 999); + EXPECT_EQ(123, anArray2(2, 3)); + EXPECT_NE(anArray1(2, 3), anArray2(2, 3)); +} + +TEST(NCollection_Array2Test, AssignmentOperator) +{ + NCollection_Array2 anArray1(1, 3, 1, 4); + anArray1.Init(123); + + NCollection_Array2 anArray2(1, 3, 1, 4); + anArray2.Init(0); + + anArray2 = anArray1; // Assign + + // Verify data is copied + EXPECT_EQ(123, anArray2(2, 3)); + + // Modify original to ensure it was a deep copy + anArray1.SetValue(2, 3, 999); + EXPECT_EQ(123, anArray2(2, 3)); +} + +TEST(NCollection_Array2Test, MoveConstructor) +{ + NCollection_Array2 anArray1(1, 5, 1, 10); + anArray1.SetValue(3, 7, 123); + + // Move construct + NCollection_Array2 anArray2(std::move(anArray1)); + + // Verify new array has the data and dimensions + EXPECT_EQ(50, anArray2.Length()); + EXPECT_EQ(5, anArray2.NbRows()); + EXPECT_EQ(10, anArray2.NbColumns()); + EXPECT_EQ(1, anArray2.LowerRow()); + EXPECT_EQ(10, anArray2.UpperCol()); + EXPECT_EQ(123, anArray2(3, 7)); + + // Verify the moved-from array is empty + EXPECT_EQ(0, anArray1.Length()); + EXPECT_EQ(0, anArray1.NbRows()); +} + +TEST(NCollection_Array2Test, MoveAssignment) +{ + NCollection_Array2 anArray1(1, 5, 1, 10); + anArray1.SetValue(3, 7, 123); + + NCollection_Array2 anArray2; + anArray2 = std::move(anArray1); // Move assignment + + // Verify new array has the data and dimensions + EXPECT_EQ(50, anArray2.Length()); + EXPECT_EQ(5, anArray2.NbRows()); + EXPECT_EQ(123, anArray2(3, 7)); + + // Verify the moved-from array is empty + EXPECT_EQ(0, anArray1.Length()); +} + +// --- Resizing and Re-indexing --- + +TEST(NCollection_Array2Test, Resize) +{ + NCollection_Array2 anArray(1, 4, 1, 5); // 4x5 array + for (Standard_Integer aRowIter = 1; aRowIter <= 4; ++aRowIter) + { + for (Standard_Integer aColIter = 1; aColIter <= 5; ++aColIter) + { + anArray(aRowIter, aColIter) = aRowIter * 100 + aColIter; + } + } + + // Resize to be larger, keeping data + anArray.Resize(0, 5, 0, 6, Standard_True); // New size 6x7 + + // Verify new dimensions + EXPECT_EQ(6, anArray.NbRows()); + EXPECT_EQ(7, anArray.NbColumns()); + EXPECT_EQ(0, anArray.LowerRow()); + EXPECT_EQ(6, anArray.UpperCol()); + + // Verify original data is preserved in the correct locations + for (Standard_Integer aRowIter = 0; aRowIter <= 3; ++aRowIter) + { + for (Standard_Integer aColIter = 0; aColIter <= 4; ++aColIter) + { + EXPECT_EQ((aRowIter + 1) * 100 + (aColIter + 1), anArray(aRowIter, aColIter)); + } + } +} + +TEST(NCollection_Array2Test, ReIndex_UpdateBounds) +{ + NCollection_Array2 anArray(1, 5, 1, 10); // 5x10 array + + // Test updating lower bounds + anArray.UpdateLowerRow(0); + anArray.UpdateLowerCol(0); + EXPECT_EQ(0, anArray.LowerRow()); + EXPECT_EQ(4, anArray.UpperRow()); // 0 + 5 - 1 + EXPECT_EQ(0, anArray.LowerCol()); + EXPECT_EQ(9, anArray.UpperCol()); // 0 + 10 - 1 + EXPECT_EQ(5, anArray.NbRows()); // Size should not change + EXPECT_EQ(10, anArray.NbColumns()); + + // Test updating upper bounds (which shifts the lower bounds) + anArray.UpdateUpperRow(10); // Request upper row to be 10 + anArray.UpdateUpperCol(20); // Request upper col to be 20 + EXPECT_EQ(10, anArray.UpperRow()); + EXPECT_EQ(20, anArray.UpperCol()); + EXPECT_EQ(6, anArray.LowerRow()); // Lower is now 10 - 5 + 1 + EXPECT_EQ(11, anArray.LowerCol()); // Lower is now 20 - 10 + 1 + EXPECT_EQ(5, anArray.NbRows()); // Size should not change + EXPECT_EQ(10, anArray.NbColumns()); +} + +// --- Iteration --- + +TEST(NCollection_Array2Test, STLIteration) +{ + NCollection_Array2 anArray(1, 2, 1, 3); // 2x3 array + for (Standard_Integer aRowIter = 1; aRowIter <= 2; ++aRowIter) + { + for (Standard_Integer aColIter = 1; aColIter <= 3; ++aColIter) + { + anArray(aRowIter, aColIter) = aRowIter * 10 + aColIter; // 11, 12, 13, 21, 22, 23 + } + } + + // Test range-based for loop + std::vector aExpectedValues = {11, 12, 13, 21, 22, 23}; + int anIndex = 0; + for (const auto& aValue : anArray) + { + EXPECT_EQ(aExpectedValues[anIndex++], aValue); + } + EXPECT_EQ(6, anIndex); // Ensure all elements were visited +} + +TEST(NCollection_Array2Test, Resize_ChangeShapeSameSize) +{ + // This test checks for data scrambling when resizing to a different shape + // with the same total number of elements. + NCollection_Array2 anArray(1, 4, 1, 6); // 4x6 = 24 elements + Standard_Integer anExpectedValue = 0; + for (Standard_Integer aRowIter = 1; aRowIter <= 4; ++aRowIter) + { + for (Standard_Integer aColIter = 1; aColIter <= 6; ++aColIter) + { + anArray(aRowIter, aColIter) = anExpectedValue++; + } + } + + // Resize to 6x4 (24 elements), copying data + anArray.Resize(1, 6, 1, 4, Standard_True); + + // Verify new dimensions + EXPECT_EQ(6, anArray.NbRows()); + EXPECT_EQ(4, anArray.NbColumns()); + EXPECT_EQ(24, anArray.Length()); + + // Verify the common 4x4 sub-matrix was not scrambled. + // This will fail if the copy logic in Resize is incorrect. + for (Standard_Integer anElemInd = anArray.Lower(); anElemInd < anArray.Lower() + 16; ++anElemInd) + { + EXPECT_EQ(anElemInd - anArray.Lower(), + static_cast&>(anArray).Value(anElemInd)); + } +} + +TEST(NCollection_Array2Test, ReIndex_Interference) +{ + // This test explicitly verifies that UpdateUpperRow modifies the LowerRow, + // showing how the separate update methods can interfere with each other. + NCollection_Array2 anArray(1, 10, 1, 1); // A 10x1 array + const Standard_Integer anInitialNbRows = anArray.NbRows(); + + // 1. User sets a new lower bound. + anArray.UpdateLowerRow(5); + EXPECT_EQ(5, anArray.LowerRow()); + EXPECT_EQ(14, anArray.UpperRow()); // Upper bound is now 5 + 10 - 1 + + // 2. User then sets a new upper bound, expecting the lower bound to stay at 5. + anArray.UpdateUpperRow(12); + + // 3. Verify the final state. + EXPECT_EQ(12, anArray.UpperRow()); // The upper bound is set as requested. + + // Verify that the lower bound was changed to accommodate the new upper bound, + // undoing the previous call to UpdateLowerRow. + EXPECT_EQ(3, anArray.LowerRow()); // Lower bound is now 12 - 10 + 1 = 3 + EXPECT_NE(5, anArray.LowerRow()); // It is no longer 5. + + // The total number of rows should never change during these operations. + EXPECT_EQ(anInitialNbRows, anArray.NbRows()); +} \ No newline at end of file diff --git a/src/FoundationClasses/TKernel/NCollection/NCollection_Array1.hxx b/src/FoundationClasses/TKernel/NCollection/NCollection_Array1.hxx index 6659d8d13b..374795b087 100644 --- a/src/FoundationClasses/TKernel/NCollection/NCollection_Array1.hxx +++ b/src/FoundationClasses/TKernel/NCollection/NCollection_Array1.hxx @@ -171,7 +171,10 @@ public: myPointer(theOther.myPointer), myIsOwner(theOther.myIsOwner) { - theOther.myIsOwner = false; + theOther.myIsOwner = false; + theOther.myPointer = nullptr; + theOther.mySize = 0; + theOther.myLowerBound = 1; } virtual ~NCollection_Array1() @@ -241,11 +244,14 @@ public: destroy(myPointer, 0, mySize); myAllocator.deallocate(myPointer, mySize); } - myLowerBound = theOther.myLowerBound; - mySize = theOther.mySize; - myPointer = theOther.myPointer; - myIsOwner = theOther.myIsOwner; - theOther.myIsOwner = false; + myLowerBound = theOther.myLowerBound; + mySize = theOther.mySize; + myPointer = theOther.myPointer; + myIsOwner = theOther.myIsOwner; + theOther.myIsOwner = false; + theOther.myPointer = nullptr; + theOther.mySize = 0; + theOther.myLowerBound = 1; return *this; } diff --git a/src/FoundationClasses/TKernel/NCollection/NCollection_Array2.hxx b/src/FoundationClasses/TKernel/NCollection/NCollection_Array2.hxx index 74c53ee13e..a008927594 100644 --- a/src/FoundationClasses/TKernel/NCollection/NCollection_Array2.hxx +++ b/src/FoundationClasses/TKernel/NCollection/NCollection_Array2.hxx @@ -146,6 +146,10 @@ public: myLowerCol(theOther.LowerCol()), mySizeCol(theOther.NbColumns()) { + theOther.myLowerRow = 1; + theOther.mySizeRow = 0; + theOther.myLowerCol = 1; + theOther.mySizeCol = 0; } //! C array-based constructor @@ -195,6 +199,24 @@ public: //! UpperCol Standard_Integer UpperCol() const { return myLowerCol + static_cast(mySizeCol) - 1; } + //! Updates lower row + void UpdateLowerRow(const Standard_Integer theLowerRow) { myLowerRow = theLowerRow; } + + //! Updates lower column + void UpdateLowerCol(const Standard_Integer theLowerCol) { myLowerCol = theLowerCol; } + + //! Updates upper row + void UpdateUpperRow(const Standard_Integer theUpperRow) + { + myLowerRow = myLowerRow - UpperRow() + theUpperRow; + } + + //! Updates upper column + void UpdateUpperCol(const Standard_Integer theUpperCol) + { + myLowerCol = myLowerCol - UpperCol() + theUpperCol; + } + //! Assignment NCollection_Array2& Assign(const NCollection_Array2& theOther) { @@ -203,6 +225,7 @@ public: return *this; } NCollection_Array1::Assign(theOther); + // Current implementation disable changing bounds by assigning return *this; } @@ -216,10 +239,14 @@ public: return *this; } NCollection_Array1::Move(theOther); - myLowerRow = theOther.myLowerRow; - mySizeRow = theOther.mySizeRow; - myLowerCol = theOther.myLowerCol; - mySizeCol = theOther.mySizeCol; + myLowerRow = theOther.myLowerRow; + mySizeRow = theOther.mySizeRow; + myLowerCol = theOther.myLowerCol; + mySizeCol = theOther.mySizeCol; + theOther.myLowerRow = 1; + theOther.mySizeRow = 0; + theOther.myLowerCol = 1; + theOther.mySizeCol = 0; return *this; } @@ -296,31 +323,34 @@ public: { Standard_RangeError_Raise_if(theRowUpper < theRowLower || theColUpper < theColLower, "NCollection_Array2::Resize"); - myLowerRow = theRowLower; - myLowerCol = theColLower; if (!theToCopyData) { NCollection_Array1::Resize( BeginPosition(theRowLower, theRowUpper, theColLower, theColUpper), LastPosition(theRowLower, theRowUpper, theColLower, theColUpper), false); - mySizeRow = theRowUpper - theRowLower + 1; - mySizeCol = theColUpper - theColLower + 1; + mySizeRow = theRowUpper - theRowLower + 1; + mySizeCol = theColUpper - theColLower + 1; + myLowerRow = theRowLower; + myLowerCol = theColLower; return; } - NCollection_Array1 aTmpMovedCopy(std::move(*this)); + const size_t aNewNbRows = theRowUpper - theRowLower + 1; + const size_t aNewNbCols = theColUpper - theColLower + 1; + const size_t aNbRowsToCopy = std::min(mySizeRow, aNewNbRows); + const size_t aNbColsToCopy = std::min(mySizeCol, aNewNbCols); + + NCollection_Array2 aTmpMovedCopy(std::move(*this)); TheItemType* anOldPointer = &aTmpMovedCopy.ChangeFirst(); NCollection_Array1::Resize( BeginPosition(theRowLower, theRowUpper, theColLower, theColUpper), LastPosition(theRowLower, theRowUpper, theColLower, theColUpper), false); - const size_t aNewNbRows = theRowUpper - theRowLower + 1; - const size_t aNewNbCols = theColUpper - theColLower + 1; - const size_t aNbRowsToCopy = std::min(mySizeRow, aNewNbRows); - const size_t aNbColsToCopy = std::min(mySizeCol, aNewNbCols); - mySizeRow = aNewNbRows; - mySizeCol = aNewNbCols; - size_t aOldInter = 0; + mySizeRow = aNewNbRows; + mySizeCol = aNewNbCols; + myLowerRow = theRowLower; + myLowerCol = theColLower; + size_t aOldInter = 0; for (size_t aRowIter = 0; aRowIter < aNbRowsToCopy; ++aRowIter) { for (size_t aColIter = 0; aColIter < aNbColsToCopy; ++aColIter)