1
0
mirror of https://git.dev.opencascade.org/repos/occt.git synced 2025-07-15 12:35:51 +03:00

Foundation Classes, math_DoubleTab - Rework to NCollection container (#607)

- Refactors `math_DoubleTab` to use `NCollection_Array2` as its underlying container
- Eemoves the old C‐array implementation, and updates collection classes to improve move semantics and bounds manipulation. 
- Rework `math_DoubleTab` to wrap `NCollection_Array2`, with buffer optimization for small sizes  
- Enhance `NCollection_Array2` and `NCollection_Array1` with proper move semantics and bound‐updating methods  
- Remove legacy `.lxx`/`.cxx` implementations, add tests and update CMake file lists
This commit is contained in:
Pasukhin Dmitry 2025-07-13 10:58:44 +01:00 committed by GitHub
parent 878cd2f6d6
commit ceafdb0436
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 724 additions and 233 deletions

View File

@ -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
)

View File

@ -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 <math_DoubleTab.hxx>
#include <gtest/gtest.h>
#include <Standard_Real.hxx>
#include <Standard_Integer.hxx>
#include <Precision.hxx>
// 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);
}

View File

@ -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

View File

@ -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 <math_DoubleTab.hxx>
#include <Standard_OutOfRange.hxx>
// 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;
}

View File

@ -20,60 +20,104 @@
#include <Standard.hxx>
#include <Standard_DefineAlloc.hxx>
#include <Standard_Handle.hxx>
#include <NCollection_Array2.hxx>
#include <Standard_Real.hxx>
#include <Standard_Boolean.hxx>
#include <array>
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<Standard_Real>(*myBuffer.data(),
theLowerRow,
theUpperRow,
theLowerCol,
theUpperCol)
: NCollection_Array2<Standard_Real>(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<const Standard_Real*>(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<Standard_Real, THE_BUFFER_SIZE> myBuffer;
NCollection_Array2<Standard_Real> myArray;
};
#include <math_DoubleTab.lxx>
#endif // _math_DoubleTab_HeaderFile

View File

@ -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 <string.h>
#include <Standard_OutOfRange.hxx>
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)));
}

View File

@ -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 <Row>
//! and <Col> of a matrix.
//! An exception is raised if <Row> and <Col> 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 <Row>
//! and <Col> of a matrix.
//! An exception is raised if <Row> and <Col> 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);
}

View File

@ -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

View File

@ -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

View File

@ -16,16 +16,7 @@
#include <gtest/gtest.h>
// 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<Standard_Integer> 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<Standard_Integer> 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<Standard_Integer> 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<Standard_Integer> anArray(1, 5);
@ -78,7 +69,7 @@ TEST_F(NCollection_Array1Test, AssignmentValue)
}
}
TEST_F(NCollection_Array1Test, CopyConstructor)
TEST(NCollection_Array1Test, CopyConstructor)
{
NCollection_Array1<Standard_Integer> 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<Standard_Integer> anArray(1, 5);
@ -124,7 +115,7 @@ TEST_F(NCollection_Array1Test, ValueAccess)
}
}
TEST_F(NCollection_Array1Test, ChangeValueAccess)
TEST(NCollection_Array1Test, ChangeValueAccess)
{
NCollection_Array1<Standard_Integer> anArray(1, 5);
@ -153,7 +144,7 @@ TEST_F(NCollection_Array1Test, ChangeValueAccess)
}
}
TEST_F(NCollection_Array1Test, AssignmentOperator)
TEST(NCollection_Array1Test, AssignmentOperator)
{
NCollection_Array1<Standard_Integer> anArray1(1, 5);
@ -179,7 +170,7 @@ TEST_F(NCollection_Array1Test, AssignmentOperator)
}
}
TEST_F(NCollection_Array1Test, Move)
TEST(NCollection_Array1Test, Move)
{
NCollection_Array1<Standard_Integer> 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<Standard_Integer> anArray(1, 5);
@ -221,7 +209,7 @@ TEST_F(NCollection_Array1Test, Init)
}
}
TEST_F(NCollection_Array1Test, SetValue)
TEST(NCollection_Array1Test, SetValue)
{
NCollection_Array1<Standard_Integer> 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<Standard_Integer> 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<Standard_Integer> 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<Standard_Integer> 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<Standard_Integer> 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<Standard_Integer> anArray(1, 5);
for (Standard_Integer i = 1; i <= 5; i++)

View File

@ -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 <NCollection_Array2.hxx>
#include <Standard_Integer.hxx>
#include <gtest/gtest.h>
// --- Constructor Tests ---
TEST(NCollection_Array2Test, DefaultConstructor)
{
NCollection_Array2<Standard_Integer> anArray;
EXPECT_EQ(0, anArray.Length());
EXPECT_EQ(0, anArray.NbRows());
EXPECT_EQ(0, anArray.NbColumns());
}
TEST(NCollection_Array2Test, ConstructorWithBounds)
{
NCollection_Array2<Standard_Integer> 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<Standard_Integer> 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<Standard_Integer> 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<Standard_Integer> 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<Standard_Integer> 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<Standard_Integer> anArray1(1, 3, 1, 4);
anArray1.Init(123);
NCollection_Array2<Standard_Integer> 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<Standard_Integer> anArray1(1, 3, 1, 4);
anArray1.Init(123);
NCollection_Array2<Standard_Integer> 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<Standard_Integer> anArray1(1, 5, 1, 10);
anArray1.SetValue(3, 7, 123);
// Move construct
NCollection_Array2<Standard_Integer> 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<Standard_Integer> anArray1(1, 5, 1, 10);
anArray1.SetValue(3, 7, 123);
NCollection_Array2<Standard_Integer> 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<Standard_Integer> 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<Standard_Integer> 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<Standard_Integer> 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<Standard_Integer> 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<Standard_Integer> 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<NCollection_Array1<Standard_Integer>&>(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<Standard_Integer> 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());
}

View File

@ -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;
}

View File

@ -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<int>(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<TheItemType>::Assign(theOther);
// Current implementation disable changing bounds by assigning
return *this;
}
@ -216,10 +239,14 @@ public:
return *this;
}
NCollection_Array1<TheItemType>::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<TheItemType>::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<TheItemType> aTmpMovedCopy(std::move(*this));
const size_t aNewNbRows = theRowUpper - theRowLower + 1;
const size_t aNewNbCols = theColUpper - theColLower + 1;
const size_t aNbRowsToCopy = std::min<size_t>(mySizeRow, aNewNbRows);
const size_t aNbColsToCopy = std::min<size_t>(mySizeCol, aNewNbCols);
NCollection_Array2<TheItemType> aTmpMovedCopy(std::move(*this));
TheItemType* anOldPointer = &aTmpMovedCopy.ChangeFirst();
NCollection_Array1<TheItemType>::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<size_t>(mySizeRow, aNewNbRows);
const size_t aNbColsToCopy = std::min<size_t>(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)