blob: 2f1268dd49d30954cfcc13cf8646e2fc4d30ba90 [file]
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2026 Pavel Guzenfeld
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include "main.h"
using namespace Eigen;
// ============================================================================
// Test 1: Dynamic-size matrix concatenation (basic correctness)
// ============================================================================
template <typename MatrixType>
void test_concat_dynamic(const MatrixType& m) {
typedef typename MatrixType::Scalar Scalar;
typedef Matrix<Scalar, Dynamic, Dynamic> MatrixX;
Index rows = m.rows();
Index cols = m.cols();
MatrixType a = MatrixType::Random(rows, cols);
MatrixType b = MatrixType::Random(rows, cols);
// Vertical concatenation: stack rows
{
MatrixX expected(2 * rows, cols);
expected.topRows(rows) = a;
expected.bottomRows(rows) = b;
VERIFY_IS_APPROX(expected, vcat(a, b).eval());
// Also verify through assignment to MatrixX
MatrixX result = vcat(a, b);
VERIFY_IS_APPROX(expected, result);
}
// Horizontal concatenation: stack columns
{
MatrixX expected(rows, 2 * cols);
expected.leftCols(cols) = a;
expected.rightCols(cols) = b;
VERIFY_IS_APPROX(expected, hcat(a, b).eval());
MatrixX result = hcat(a, b);
VERIFY_IS_APPROX(expected, result);
}
// Test with different-sized operands
{
MatrixX c = MatrixX::Random(rows + 2, cols);
MatrixX vresult = vcat(a, c);
VERIFY_IS_EQUAL(vresult.rows(), 2 * rows + 2);
VERIFY_IS_EQUAL(vresult.cols(), cols);
MatrixX vexpected(2 * rows + 2, cols);
vexpected.topRows(rows) = a;
vexpected.bottomRows(rows + 2) = c;
VERIFY_IS_APPROX(vexpected, vresult);
}
{
MatrixX d = MatrixX::Random(rows, cols + 3);
MatrixX hresult = hcat(a, d);
VERIFY_IS_EQUAL(hresult.rows(), rows);
VERIFY_IS_EQUAL(hresult.cols(), 2 * cols + 3);
MatrixX hexpected(rows, 2 * cols + 3);
hexpected.leftCols(cols) = a;
hexpected.rightCols(cols + 3) = d;
VERIFY_IS_APPROX(hexpected, hresult);
}
}
// ============================================================================
// Test 2: Expression inputs (not plain objects)
// Verifies that Concat works with CwiseBinaryOp, CwiseUnaryOp, etc.
// ============================================================================
template <typename MatrixType>
void test_concat_with_expressions(const MatrixType& m) {
typedef typename MatrixType::Scalar Scalar;
typedef Matrix<Scalar, Dynamic, Dynamic> MatrixX;
Index rows = m.rows();
Index cols = m.cols();
MatrixType a = MatrixType::Random(rows, cols);
MatrixType b = MatrixType::Random(rows, cols);
// Concat with scalar-multiply expressions
{
MatrixX expected(2 * rows, cols);
expected.topRows(rows) = 2.0 * a;
expected.bottomRows(rows) = 3.0 * b;
VERIFY_IS_APPROX(expected, vcat(2.0 * a, 3.0 * b).eval());
}
// Concat with sum/difference expressions
{
MatrixX expected(rows, 2 * cols);
expected.leftCols(cols) = a + b;
expected.rightCols(cols) = a - b;
VERIFY_IS_APPROX(expected, hcat(a + b, a - b).eval());
}
// Concat with transpose expressions
{
MatrixX expected(cols, 2 * rows);
expected.leftCols(rows) = a.transpose();
expected.rightCols(rows) = b.transpose();
VERIFY_IS_APPROX(expected, hcat(a.transpose(), b.transpose()).eval());
}
// Concat with block expressions
{
if (rows >= 2 && cols >= 2) {
auto block_a = a.topLeftCorner(rows - 1, cols);
auto block_b = b.bottomRightCorner(1, cols);
MatrixX expected(rows, cols);
expected.topRows(rows - 1) = block_a;
expected.bottomRows(1) = block_b;
VERIFY_IS_APPROX(expected, vcat(block_a, block_b).eval());
}
}
}
// ============================================================================
// Test 3: Fixed-size concat with compile-time dimension propagation
// ============================================================================
template <typename Scalar, int LhsRows, int CommonCols, int RhsRows>
void test_vcat_fixed() {
typedef Matrix<Scalar, LhsRows, CommonCols> LhsType;
typedef Matrix<Scalar, RhsRows, CommonCols> RhsType;
LhsType a = LhsType::Random();
RhsType b = RhsType::Random();
auto vc = vcat(a, b);
VERIFY_IS_EQUAL(vc.rows(), a.rows() + b.rows());
VERIFY_IS_EQUAL(vc.cols(), a.cols());
typedef Concat<Vertical, LhsType, RhsType> VConcatType;
VERIFY((int(VConcatType::RowsAtCompileTime) == LhsRows + RhsRows));
VERIFY((int(VConcatType::ColsAtCompileTime) == CommonCols));
VERIFY((int(VConcatType::MaxRowsAtCompileTime) == LhsRows + RhsRows));
VERIFY((int(VConcatType::MaxColsAtCompileTime) == CommonCols));
typedef Matrix<Scalar, Dynamic, Dynamic> MatrixX;
MatrixX expected(a.rows() + b.rows(), a.cols());
expected.topRows(a.rows()) = a;
expected.bottomRows(b.rows()) = b;
VERIFY_IS_APPROX(expected, MatrixX(vc));
}
template <typename Scalar, int CommonRows, int LhsCols, int RhsCols>
void test_hcat_fixed() {
typedef Matrix<Scalar, CommonRows, LhsCols> LhsType;
typedef Matrix<Scalar, CommonRows, RhsCols> RhsType;
LhsType a = LhsType::Random();
RhsType b = RhsType::Random();
auto hc = hcat(a, b);
VERIFY_IS_EQUAL(hc.rows(), a.rows());
VERIFY_IS_EQUAL(hc.cols(), a.cols() + b.cols());
typedef Concat<Horizontal, LhsType, RhsType> HConcatType;
VERIFY((int(HConcatType::RowsAtCompileTime) == CommonRows));
VERIFY((int(HConcatType::ColsAtCompileTime) == LhsCols + RhsCols));
VERIFY((int(HConcatType::MaxRowsAtCompileTime) == CommonRows));
VERIFY((int(HConcatType::MaxColsAtCompileTime) == LhsCols + RhsCols));
typedef Matrix<Scalar, Dynamic, Dynamic> MatrixX;
MatrixX expected(a.rows(), a.cols() + b.cols());
expected.leftCols(a.cols()) = a;
expected.rightCols(b.cols()) = b;
VERIFY_IS_APPROX(expected, MatrixX(hc));
}
// ============================================================================
// Test 4: Mixed fixed/dynamic dimension concat
// Verifies that compile-time dimensions become Dynamic when appropriate
// ============================================================================
template <typename Scalar>
void test_concat_mixed_fixed_dynamic() {
typedef Matrix<Scalar, 3, 3> Fixed33;
typedef Matrix<Scalar, Dynamic, Dynamic> MatrixX;
typedef Matrix<Scalar, Dynamic, 3> MatrixX3;
Fixed33 a = Fixed33::Random();
MatrixX b = MatrixX::Random(4, 3);
// Vertical: fixed + dynamic => dynamic rows, but cols known at compile time
{
auto vc = vcat(a, b);
VERIFY_IS_EQUAL(vc.rows(), 7);
VERIFY_IS_EQUAL(vc.cols(), 3);
// Compile-time: rows should be Dynamic (fixed + dynamic = dynamic)
typedef decltype(vc) VCType;
VERIFY((int(VCType::RowsAtCompileTime) == Dynamic));
// Cols: fixed 3 and dynamic => 3 (size_prefer_fixed picks the fixed one)
VERIFY((int(VCType::ColsAtCompileTime) == 3));
MatrixX expected(7, 3);
expected.topRows(3) = a;
expected.bottomRows(4) = b;
VERIFY_IS_APPROX(expected, MatrixX(vc));
}
// Vertical: fixed + MatrixX3 (cols both = 3) => dynamic rows
{
MatrixX3 c = MatrixX3::Random(5, 3);
auto vc = vcat(a, c);
VERIFY_IS_EQUAL(vc.rows(), 8);
VERIFY_IS_EQUAL(vc.cols(), 3);
typedef decltype(vc) VCType;
VERIFY((int(VCType::RowsAtCompileTime) == Dynamic));
MatrixX expected(8, 3);
expected.topRows(3) = a;
expected.bottomRows(5) = c;
VERIFY_IS_APPROX(expected, MatrixX(vc));
}
// Horizontal: fixed + dynamic
{
MatrixX d = MatrixX::Random(3, 5);
auto hc = hcat(a, d);
VERIFY_IS_EQUAL(hc.rows(), 3);
VERIFY_IS_EQUAL(hc.cols(), 8);
typedef decltype(hc) HCType;
VERIFY((int(HCType::ColsAtCompileTime) == Dynamic));
MatrixX expected(3, 8);
expected.leftCols(3) = a;
expected.rightCols(5) = d;
VERIFY_IS_APPROX(expected, MatrixX(hc));
}
// Dynamic + fixed (reversed order)
{
MatrixX3 e = MatrixX3::Random(5, 3);
auto vc = vcat(e, a);
VERIFY_IS_EQUAL(vc.rows(), 8);
VERIFY_IS_EQUAL(vc.cols(), 3);
MatrixX expected(8, 3);
expected.topRows(5) = e;
expected.bottomRows(3) = a;
VERIFY_IS_APPROX(expected, MatrixX(vc));
}
}
// ============================================================================
// Test 5: Rvalue temporary lifetime safety
// Verifies ref_selector properly copies temporaries instead of dangling refs
// ============================================================================
template <typename Scalar>
void test_concat_rvalue_temporaries() {
typedef Matrix<Scalar, Dynamic, Dynamic> MatrixX;
MatrixX a = MatrixX::Random(3, 3);
MatrixX b = MatrixX::Random(3, 3);
// Test 5a: Product temporaries (Product expression creates temporaries)
{
MatrixX result = vcat(a * MatrixX::Identity(3, 3), b * MatrixX::Identity(3, 3));
MatrixX expected(6, 3);
expected.topRows(3) = a;
expected.bottomRows(3) = b;
VERIFY_IS_APPROX(expected, result);
}
// Test 5b: Additive temporaries
{
MatrixX result = hcat(a + MatrixX::Zero(3, 3), b + MatrixX::Zero(3, 3));
MatrixX expected(3, 6);
expected.leftCols(3) = a;
expected.rightCols(3) = b;
VERIFY_IS_APPROX(expected, result);
}
// Test 5c: Store expression with auto and evaluate later
// This is the critical rvalue lifetime test: the expression must
// keep the temporary alive until evaluation
{
auto expr = vcat(a * MatrixX::Identity(3, 3), b * MatrixX::Identity(3, 3));
// At this point, the temporaries from a*I and b*I must still be valid
MatrixX result = expr;
MatrixX expected(6, 3);
expected.topRows(3) = a;
expected.bottomRows(3) = b;
VERIFY_IS_APPROX(expected, result);
}
// Test 5d: Auto with hcat
{
auto expr = hcat(a + b, a - b);
MatrixX result = expr;
MatrixX expected(3, 6);
expected.leftCols(3) = a + b;
expected.rightCols(3) = a - b;
VERIFY_IS_APPROX(expected, result);
}
// Test 5e: Nested auto expressions (chained concat of temporaries)
{
MatrixX c = MatrixX::Random(3, 3);
auto inner = vcat(a + b, c * MatrixX::Identity(3, 3));
auto outer = vcat(inner, a - b);
MatrixX result = outer;
VERIFY_IS_EQUAL(result.rows(), 9);
VERIFY_IS_EQUAL(result.cols(), 3);
MatrixX expected(9, 3);
expected.topRows(3) = a + b;
expected.middleRows(3, 3) = c;
expected.bottomRows(3) = a - b;
VERIFY_IS_APPROX(expected, result);
}
}
// ============================================================================
// Test 6: Chained concatenation (binary API, no variadic needed)
// ============================================================================
template <typename Scalar>
void test_concat_chained() {
typedef Matrix<Scalar, Dynamic, Dynamic> MatrixX;
MatrixX a = MatrixX::Random(2, 3);
MatrixX b = MatrixX::Random(2, 3);
MatrixX c = MatrixX::Random(2, 3);
// Chain: vcat(vcat(a, b), c) should be 6x3
{
MatrixX result = vcat(vcat(a, b), c);
VERIFY_IS_EQUAL(result.rows(), 6);
VERIFY_IS_EQUAL(result.cols(), 3);
MatrixX expected(6, 3);
expected.topRows(2) = a;
expected.middleRows(2, 2) = b;
expected.bottomRows(2) = c;
VERIFY_IS_APPROX(expected, result);
}
// Chain: hcat(hcat(a, b), c) should be 2x9
{
MatrixX result = hcat(hcat(a, b), c);
VERIFY_IS_EQUAL(result.rows(), 2);
VERIFY_IS_EQUAL(result.cols(), 9);
MatrixX expected(2, 9);
expected.leftCols(3) = a;
expected.middleCols(3, 3) = b;
expected.rightCols(3) = c;
VERIFY_IS_APPROX(expected, result);
}
// Mixed chain: vcat then hcat
{
MatrixX e = MatrixX::Random(4, 5);
MatrixX vert = vcat(a, b); // 4x3
MatrixX result = hcat(vert, e);
VERIFY_IS_EQUAL(result.rows(), 4);
VERIFY_IS_EQUAL(result.cols(), 8);
MatrixX expected(4, 8);
expected.leftCols(3) = vert;
expected.rightCols(5) = e;
VERIFY_IS_APPROX(expected, result);
}
// 4-way chain
{
MatrixX d = MatrixX::Random(2, 3);
MatrixX result = vcat(vcat(vcat(a, b), c), d);
VERIFY_IS_EQUAL(result.rows(), 8);
VERIFY_IS_EQUAL(result.cols(), 3);
MatrixX expected(8, 3);
expected.block(0, 0, 2, 3) = a;
expected.block(2, 0, 2, 3) = b;
expected.block(4, 0, 2, 3) = c;
expected.block(6, 0, 2, 3) = d;
VERIFY_IS_APPROX(expected, result);
}
}
// ============================================================================
// Test 7: Vector concatenation
// ============================================================================
template <typename Scalar>
void test_concat_vectors() {
typedef Matrix<Scalar, Dynamic, 1> VectorX;
typedef Matrix<Scalar, 1, Dynamic> RowVectorX;
typedef Matrix<Scalar, 3, 1> Vector3;
typedef Matrix<Scalar, 4, 1> Vector4;
typedef Matrix<Scalar, 1, 3> RowVector3;
typedef Matrix<Scalar, 1, 4> RowVector4;
// Column vectors: vcat
{
VectorX a = VectorX::Random(3);
VectorX b = VectorX::Random(5);
VectorX result = vcat(a, b);
VERIFY_IS_EQUAL(result.rows(), 8);
VERIFY_IS_EQUAL(result.cols(), 1);
VectorX expected(8);
expected.head(3) = a;
expected.tail(5) = b;
VERIFY_IS_APPROX(expected, result);
}
// Row vectors: hcat
{
RowVectorX a = RowVectorX::Random(3);
RowVectorX b = RowVectorX::Random(5);
RowVectorX result = hcat(a, b);
VERIFY_IS_EQUAL(result.rows(), 1);
VERIFY_IS_EQUAL(result.cols(), 8);
RowVectorX expected(8);
expected.head(3) = a;
expected.tail(5) = b;
VERIFY_IS_APPROX(expected, result);
}
// Fixed-size column vectors: compile-time dimensions
{
Vector3 a = Vector3::Random();
Vector4 b = Vector4::Random();
auto result = vcat(a, b);
typedef Concat<Vertical, Vector3, Vector4> VConcatType;
VERIFY((int(VConcatType::RowsAtCompileTime) == 7));
VERIFY((int(VConcatType::ColsAtCompileTime) == 1));
typedef Matrix<Scalar, Dynamic, 1> VecX;
VecX expected(7);
expected.head(3) = a;
expected.tail(4) = b;
VecX actual(result);
VERIFY_IS_APPROX(expected, actual);
}
// Fixed-size row vectors: compile-time dimensions
{
RowVector3 a = RowVector3::Random();
RowVector4 b = RowVector4::Random();
auto result = hcat(a, b);
typedef Concat<Horizontal, RowVector3, RowVector4> HConcatType;
VERIFY((int(HConcatType::RowsAtCompileTime) == 1));
VERIFY((int(HConcatType::ColsAtCompileTime) == 7));
typedef Matrix<Scalar, 1, Dynamic> RowVecX;
RowVecX expected(7);
expected.head(3) = a;
expected.tail(4) = b;
RowVecX actual(result);
VERIFY_IS_APPROX(expected, actual);
}
}
// ============================================================================
// Test 8: Array concatenation (verifies XprKind propagation)
// ============================================================================
template <typename Scalar>
void test_concat_array() {
typedef Array<Scalar, Dynamic, Dynamic> ArrayX;
typedef Array<Scalar, 2, 3> FixedArray23;
// Dynamic arrays
{
ArrayX a = ArrayX::Random(3, 4);
ArrayX b = ArrayX::Random(3, 4);
// vcat with arrays
ArrayX expected(6, 4);
expected.topRows(3) = a;
expected.bottomRows(3) = b;
ArrayX result = vcat(a, b);
VERIFY_IS_APPROX(expected, result);
}
{
ArrayX a = ArrayX::Random(3, 4);
ArrayX b = ArrayX::Random(3, 4);
// hcat with arrays
ArrayX expected(3, 8);
expected.leftCols(4) = a;
expected.rightCols(4) = b;
ArrayX result = hcat(a, b);
VERIFY_IS_APPROX(expected, result);
}
// Fixed-size arrays
{
FixedArray23 a = FixedArray23::Random();
FixedArray23 b = FixedArray23::Random();
typedef Concat<Vertical, FixedArray23, FixedArray23> VConcatType;
VERIFY((int(VConcatType::RowsAtCompileTime) == 4));
VERIFY((int(VConcatType::ColsAtCompileTime) == 3));
ArrayX expected(4, 3);
expected.topRows(2) = a;
expected.bottomRows(2) = b;
ArrayX result = vcat(a, b);
VERIFY_IS_APPROX(expected, result);
}
// Array expressions (coefficient-wise operations)
{
ArrayX a = ArrayX::Random(3, 4);
ArrayX b = ArrayX::Random(3, 4);
ArrayX result = vcat(a * b, a + b);
VERIFY_IS_EQUAL(result.rows(), 6);
VERIFY_IS_EQUAL(result.cols(), 4);
ArrayX expected(6, 4);
expected.topRows(3) = a * b;
expected.bottomRows(3) = a + b;
VERIFY_IS_APPROX(expected, result);
}
}
// ============================================================================
// Test 9: Coefficient-level access verification
// ============================================================================
template <typename Scalar>
void test_concat_coeff_access() {
typedef Matrix<Scalar, Dynamic, Dynamic> MatrixX;
MatrixX a = MatrixX::Random(3, 4);
MatrixX b = MatrixX::Random(2, 4);
// Verify each coefficient of vcat
{
auto vc = vcat(a, b);
for (Index j = 0; j < 4; ++j) {
for (Index i = 0; i < 3; ++i) {
VERIFY_IS_APPROX(vc(i, j), a(i, j));
}
for (Index i = 0; i < 2; ++i) {
VERIFY_IS_APPROX(vc(3 + i, j), b(i, j));
}
}
}
// Verify each coefficient of hcat
{
MatrixX c = MatrixX::Random(3, 2);
auto hc = hcat(a, c);
for (Index i = 0; i < 3; ++i) {
for (Index j = 0; j < 4; ++j) {
VERIFY_IS_APPROX(hc(i, j), a(i, j));
}
for (Index j = 0; j < 2; ++j) {
VERIFY_IS_APPROX(hc(i, 4 + j), c(i, j));
}
}
}
}
// ============================================================================
// Test 10: Expression used in further Eigen operations
// Verifies Concat integrates with Eigen's expression machinery
// ============================================================================
template <typename Scalar>
void test_concat_in_expressions() {
typedef Matrix<Scalar, Dynamic, Dynamic> MatrixX;
typedef Matrix<Scalar, Dynamic, 1> VectorX;
MatrixX a = MatrixX::Random(3, 3);
MatrixX b = MatrixX::Random(3, 3);
// Use concat result in matrix multiplication
{
MatrixX wide = hcat(a, b); // 3x6
VectorX v = VectorX::Random(6);
VectorX result = wide * v;
MatrixX wide_eval = wide;
VectorX expected = wide_eval * v;
VERIFY_IS_APPROX(expected, result);
}
// Use concat result in sum
{
auto vc = vcat(a, b);
Scalar s = vc.sum();
Scalar expected_sum = a.sum() + b.sum();
VERIFY_IS_APPROX(expected_sum, s);
}
// Use concat in norm computation
{
VectorX va = VectorX::Random(4);
VectorX vb = VectorX::Random(3);
auto vc = vcat(va, vb);
Scalar n2 = vc.squaredNorm();
Scalar expected_n2 = va.squaredNorm() + vb.squaredNorm();
VERIFY_IS_APPROX(expected_n2, n2);
}
// Concat result assigned to a block
{
MatrixX target = MatrixX::Zero(6, 3);
target = vcat(a, b);
MatrixX expected(6, 3);
expected.topRows(3) = a;
expected.bottomRows(3) = b;
VERIFY_IS_APPROX(expected, target);
}
}
// ============================================================================
// Test 11: Single row / single column edge cases
// ============================================================================
template <typename Scalar>
void test_concat_single_row_col() {
typedef Matrix<Scalar, Dynamic, Dynamic> MatrixX;
typedef Matrix<Scalar, 1, Dynamic> RowVectorX;
typedef Matrix<Scalar, Dynamic, 1> VectorX;
// Single-row matrices: vcat
{
RowVectorX a = RowVectorX::Random(5);
RowVectorX b = RowVectorX::Random(5);
MatrixX result = vcat(a, b);
VERIFY_IS_EQUAL(result.rows(), 2);
VERIFY_IS_EQUAL(result.cols(), 5);
VERIFY_IS_APPROX(result.row(0), a);
VERIFY_IS_APPROX(result.row(1), b);
}
// Single-column matrices: hcat
{
VectorX a = VectorX::Random(5);
VectorX b = VectorX::Random(5);
MatrixX result = hcat(a, b);
VERIFY_IS_EQUAL(result.rows(), 5);
VERIFY_IS_EQUAL(result.cols(), 2);
VERIFY_IS_APPROX(result.col(0), a);
VERIFY_IS_APPROX(result.col(1), b);
}
// 1x1 matrices
{
MatrixX a = MatrixX::Random(1, 1);
MatrixX b = MatrixX::Random(1, 1);
MatrixX vr = vcat(a, b);
VERIFY_IS_EQUAL(vr.rows(), 2);
VERIFY_IS_EQUAL(vr.cols(), 1);
VERIFY_IS_APPROX(vr(0, 0), a(0, 0));
VERIFY_IS_APPROX(vr(1, 0), b(0, 0));
MatrixX hr = hcat(a, b);
VERIFY_IS_EQUAL(hr.rows(), 1);
VERIFY_IS_EQUAL(hr.cols(), 2);
VERIFY_IS_APPROX(hr(0, 0), a(0, 0));
VERIFY_IS_APPROX(hr(0, 1), b(0, 0));
}
}
// ============================================================================
// Test 12: RowMajor matrices
// Verifies IsRowMajor flag logic in Concat traits
// ============================================================================
template <typename Scalar>
void test_concat_row_major() {
typedef Matrix<Scalar, Dynamic, Dynamic, RowMajor> RowMajorMatrix;
typedef Matrix<Scalar, Dynamic, Dynamic> ColMajorMatrix;
typedef Matrix<Scalar, 3, 3, RowMajor> FixedRowMajor33;
// RowMajor + RowMajor
{
RowMajorMatrix a = RowMajorMatrix::Random(3, 4);
RowMajorMatrix b = RowMajorMatrix::Random(2, 4);
ColMajorMatrix result = vcat(a, b);
VERIFY_IS_EQUAL(result.rows(), 5);
VERIFY_IS_EQUAL(result.cols(), 4);
ColMajorMatrix expected(5, 4);
expected.topRows(3) = a;
expected.bottomRows(2) = b;
VERIFY_IS_APPROX(expected, result);
}
{
RowMajorMatrix a = RowMajorMatrix::Random(3, 4);
RowMajorMatrix b = RowMajorMatrix::Random(3, 2);
ColMajorMatrix result = hcat(a, b);
VERIFY_IS_EQUAL(result.rows(), 3);
VERIFY_IS_EQUAL(result.cols(), 6);
ColMajorMatrix expected(3, 6);
expected.leftCols(4) = a;
expected.rightCols(2) = b;
VERIFY_IS_APPROX(expected, result);
}
// Mixed: RowMajor + ColMajor
{
RowMajorMatrix a = RowMajorMatrix::Random(3, 4);
ColMajorMatrix b = ColMajorMatrix::Random(2, 4);
ColMajorMatrix result = vcat(a, b);
VERIFY_IS_EQUAL(result.rows(), 5);
VERIFY_IS_EQUAL(result.cols(), 4);
ColMajorMatrix expected(5, 4);
expected.topRows(3) = a;
expected.bottomRows(2) = b;
VERIFY_IS_APPROX(expected, result);
}
// Fixed-size RowMajor
{
FixedRowMajor33 a = FixedRowMajor33::Random();
FixedRowMajor33 b = FixedRowMajor33::Random();
ColMajorMatrix result = vcat(a, b);
VERIFY_IS_EQUAL(result.rows(), 6);
VERIFY_IS_EQUAL(result.cols(), 3);
ColMajorMatrix expected(6, 3);
expected.topRows(3) = a;
expected.bottomRows(3) = b;
VERIFY_IS_APPROX(expected, result);
}
}
// ============================================================================
// Test 13: Self-concatenation and aliasing
// ============================================================================
template <typename Scalar>
void test_concat_self() {
typedef Matrix<Scalar, Dynamic, Dynamic> MatrixX;
typedef Matrix<Scalar, Dynamic, 1> VectorX;
// Self-concat: vcat(a, a)
{
MatrixX a = MatrixX::Random(3, 4);
MatrixX result = vcat(a, a);
VERIFY_IS_EQUAL(result.rows(), 6);
VERIFY_IS_EQUAL(result.cols(), 4);
VERIFY_IS_APPROX(result.topRows(3), a);
VERIFY_IS_APPROX(result.bottomRows(3), a);
}
// Self-concat: hcat(a, a)
{
MatrixX a = MatrixX::Random(3, 4);
MatrixX result = hcat(a, a);
VERIFY_IS_EQUAL(result.rows(), 3);
VERIFY_IS_EQUAL(result.cols(), 8);
VERIFY_IS_APPROX(result.leftCols(4), a);
VERIFY_IS_APPROX(result.rightCols(4), a);
}
// Self-concat vector
{
VectorX v = VectorX::Random(5);
VectorX result = vcat(v, v);
VERIFY_IS_EQUAL(result.rows(), 10);
VERIFY_IS_APPROX(result.head(5), v);
VERIFY_IS_APPROX(result.tail(5), v);
}
// Concat with self-expression: vcat(a, a.transpose().transpose())
{
MatrixX a = MatrixX::Random(3, 4);
MatrixX result = vcat(a, a + MatrixX::Zero(3, 4));
VERIFY_IS_EQUAL(result.rows(), 6);
VERIFY_IS_APPROX(result.topRows(3), a);
VERIFY_IS_APPROX(result.bottomRows(3), a);
}
}
// ============================================================================
// Test 14: Runtime assertion for mismatched dimensions
// ============================================================================
template <typename Scalar>
void test_concat_mismatched_dimensions() {
typedef Matrix<Scalar, Dynamic, Dynamic> MatrixX;
MatrixX a(3, 4);
MatrixX b(3, 5);
VERIFY_RAISES_ASSERT(vcat(a, b)); // cols don't match
MatrixX c(3, 4);
MatrixX d(2, 4);
VERIFY_RAISES_ASSERT(hcat(c, d)); // rows don't match
}
EIGEN_DECLARE_TEST(concat) {
for (int i = 0; i < g_repeat; i++) {
// Dynamic-size matrix concat
CALL_SUBTEST_1(test_concat_dynamic(MatrixXf(4, 3)));
CALL_SUBTEST_1(test_concat_dynamic(MatrixXd(5, 7)));
CALL_SUBTEST_1(test_concat_dynamic(MatrixXcf(3, 4)));
// Expression inputs
CALL_SUBTEST_2(test_concat_with_expressions(MatrixXf(4, 3)));
CALL_SUBTEST_2(test_concat_with_expressions(MatrixXd(5, 7)));
// Fixed-size concat with compile-time dimension checks
CALL_SUBTEST_3((test_vcat_fixed<float, 2, 3, 4>())); // vcat: 2x3 + 4x3
CALL_SUBTEST_3((test_hcat_fixed<double, 3, 2, 5>())); // hcat: 3x2 + 3x5
CALL_SUBTEST_3((test_vcat_fixed<float, 4, 4, 4>())); // square vcat: 4x4 + 4x4
CALL_SUBTEST_3((test_hcat_fixed<float, 4, 4, 4>())); // square hcat: 4x4 + 4x4
// Mixed fixed/dynamic
CALL_SUBTEST_4(test_concat_mixed_fixed_dynamic<float>());
CALL_SUBTEST_4(test_concat_mixed_fixed_dynamic<double>());
// Rvalue temporaries (lifetime safety)
CALL_SUBTEST_5(test_concat_rvalue_temporaries<float>());
CALL_SUBTEST_5(test_concat_rvalue_temporaries<double>());
// Chained concat (binary API)
CALL_SUBTEST_6(test_concat_chained<float>());
CALL_SUBTEST_6(test_concat_chained<double>());
// Vector concat
CALL_SUBTEST_7(test_concat_vectors<float>());
CALL_SUBTEST_7(test_concat_vectors<double>());
// Array concat (XprKind propagation)
CALL_SUBTEST_8(test_concat_array<float>());
CALL_SUBTEST_8(test_concat_array<double>());
// Coefficient-level access
CALL_SUBTEST_9(test_concat_coeff_access<float>());
CALL_SUBTEST_9(test_concat_coeff_access<double>());
// Integration with other Eigen operations
CALL_SUBTEST_10(test_concat_in_expressions<float>());
CALL_SUBTEST_10(test_concat_in_expressions<double>());
// Single row/column edge cases
CALL_SUBTEST_11(test_concat_single_row_col<float>());
CALL_SUBTEST_11(test_concat_single_row_col<double>());
// RowMajor matrices
CALL_SUBTEST_12(test_concat_row_major<float>());
CALL_SUBTEST_12(test_concat_row_major<double>());
// Self-concatenation and aliasing
CALL_SUBTEST_13(test_concat_self<float>());
CALL_SUBTEST_13(test_concat_self<double>());
// Runtime assertion for mismatched dimensions
CALL_SUBTEST_14(test_concat_mismatched_dimensions<float>());
CALL_SUBTEST_14(test_concat_mismatched_dimensions<double>());
}
}