636 lines
17 KiB
C++
636 lines
17 KiB
C++
// Copyright (c) 2010-2020, Lawrence Livermore National Security, LLC. Produced
|
|
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
|
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
|
//
|
|
// This file is part of the MFEM library. For more information and source code
|
|
// availability visit https://mfem.org.
|
|
//
|
|
// MFEM is free software; you can redistribute it and/or modify it under the
|
|
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
|
// CONTRIBUTING.md for details.
|
|
|
|
#include "../general/array.hpp"
|
|
#include "../general/globals.hpp"
|
|
#include "matrix.hpp"
|
|
#include "sparsemat.hpp"
|
|
#include "blockvector.hpp"
|
|
#include "blockmatrix.hpp"
|
|
|
|
namespace mfem
|
|
{
|
|
|
|
BlockMatrix::BlockMatrix(const Array<int> & offsets):
|
|
AbstractSparseMatrix(offsets.Last()),
|
|
owns_blocks(false),
|
|
nRowBlocks(offsets.Size()-1),
|
|
nColBlocks(offsets.Size()-1),
|
|
row_offsets(const_cast< Array<int>& >(offsets).GetData(), offsets.Size()),
|
|
col_offsets(const_cast< Array<int>& >(offsets).GetData(), offsets.Size()),
|
|
Aij(nRowBlocks, nColBlocks)
|
|
{
|
|
Aij = (SparseMatrix *)NULL;
|
|
}
|
|
|
|
BlockMatrix::BlockMatrix(const Array<int> & row_offsets_,
|
|
const Array<int> & col_offsets_):
|
|
AbstractSparseMatrix(row_offsets_.Last(), col_offsets_.Last()),
|
|
owns_blocks(false),
|
|
nRowBlocks(row_offsets_.Size()-1),
|
|
nColBlocks(col_offsets_.Size()-1),
|
|
row_offsets(const_cast< Array<int>& >(row_offsets_).GetData(),
|
|
row_offsets_.Size()),
|
|
col_offsets(const_cast< Array<int>& >(col_offsets_).GetData(),
|
|
col_offsets_.Size()),
|
|
Aij(nRowBlocks, nColBlocks)
|
|
{
|
|
Aij = (SparseMatrix *)NULL;
|
|
}
|
|
|
|
|
|
BlockMatrix::~BlockMatrix()
|
|
{
|
|
if (owns_blocks)
|
|
{
|
|
for (SparseMatrix ** it = Aij.GetRow(0);
|
|
it != Aij.GetRow(0)+(Aij.NumRows()*Aij.NumCols()); ++it)
|
|
{
|
|
delete *it;
|
|
}
|
|
}
|
|
}
|
|
|
|
void BlockMatrix::SetBlock(int i, int j, SparseMatrix * mat)
|
|
{
|
|
#ifdef MFEM_DEBUG
|
|
if (nRowBlocks <= i || nColBlocks <= j)
|
|
{
|
|
mfem_error("BlockMatrix::SetBlock #0");
|
|
}
|
|
|
|
if (mat->Height() != row_offsets[i+1] - row_offsets[i])
|
|
{
|
|
mfem_error("BlockMatrix::SetBlock #1");
|
|
}
|
|
|
|
if (mat->Width() != col_offsets[j+1] - col_offsets[j])
|
|
{
|
|
mfem_error("BlockMatrix::SetBlock #2");
|
|
}
|
|
#endif
|
|
Aij(i,j) = mat;
|
|
}
|
|
|
|
SparseMatrix & BlockMatrix::GetBlock(int i, int j)
|
|
{
|
|
#ifdef MFEM_DEBUG
|
|
if (nRowBlocks <= i || nColBlocks <= j)
|
|
{
|
|
mfem_error("BlockMatrix::Block #0");
|
|
}
|
|
|
|
if (IsZeroBlock(i,j))
|
|
{
|
|
mfem_error("BlockMatrix::Block #1");
|
|
}
|
|
#endif
|
|
return *Aij(i,j);
|
|
}
|
|
|
|
const SparseMatrix & BlockMatrix::GetBlock(int i, int j) const
|
|
{
|
|
#ifdef MFEM_DEBUG
|
|
if (nRowBlocks <= i || nColBlocks <= j)
|
|
{
|
|
mfem_error("BlockMatrix::Block const #0");
|
|
}
|
|
|
|
if (IsZeroBlock(i,j))
|
|
{
|
|
mfem_error("BlockMatrix::Block const #1");
|
|
}
|
|
#endif
|
|
|
|
return *Aij(i,j);
|
|
}
|
|
|
|
|
|
int BlockMatrix::NumNonZeroElems() const
|
|
{
|
|
int nnz_elem = 0;
|
|
for (int jcol = 0; jcol != nColBlocks; ++jcol)
|
|
{
|
|
for (int irow = 0; irow != nRowBlocks; ++irow)
|
|
{
|
|
if (Aij(irow,jcol))
|
|
{
|
|
nnz_elem+= Aij(irow,jcol)->NumNonZeroElems();
|
|
}
|
|
}
|
|
}
|
|
return nnz_elem;
|
|
}
|
|
|
|
|
|
double& BlockMatrix::Elem (int i, int j)
|
|
{
|
|
int iloc, iblock;
|
|
int jloc, jblock;
|
|
|
|
findGlobalRow(i, iblock, iloc);
|
|
findGlobalCol(j, jblock, jloc);
|
|
|
|
if (IsZeroBlock(i, j))
|
|
{
|
|
mfem_error("BlockMatrix::Elem");
|
|
}
|
|
|
|
return Aij(iblock, jblock)->Elem(iloc, jloc);
|
|
}
|
|
|
|
const double& BlockMatrix::Elem (int i, int j) const
|
|
{
|
|
int iloc, iblock;
|
|
int jloc, jblock;
|
|
|
|
findGlobalRow(i, iblock, iloc);
|
|
findGlobalCol(j, jblock, jloc);
|
|
|
|
if (IsZeroBlock(i, j))
|
|
{
|
|
mfem_error("BlockMatrix::Elem");
|
|
}
|
|
|
|
return Aij(iblock, jblock)->Elem(iloc, jloc);
|
|
}
|
|
|
|
int BlockMatrix::RowSize(const int i) const
|
|
{
|
|
int rowsize = 0;
|
|
|
|
int iblock, iloc;
|
|
findGlobalRow(i, iblock, iloc);
|
|
|
|
for (int jblock = 0; jblock < nColBlocks; ++jblock)
|
|
{
|
|
if (Aij(iblock,jblock) != NULL)
|
|
{
|
|
rowsize += Aij(iblock,jblock)->RowSize(iloc);
|
|
}
|
|
}
|
|
|
|
return rowsize;
|
|
}
|
|
|
|
int BlockMatrix::GetRow(const int row, Array<int> &cols, Vector &srow) const
|
|
{
|
|
int iblock, iloc, rowsize;
|
|
findGlobalRow(row, iblock, iloc);
|
|
rowsize = RowSize(row);
|
|
cols.SetSize(rowsize);
|
|
srow.SetSize(rowsize);
|
|
|
|
Array<int> bcols;
|
|
Vector bsrow;
|
|
|
|
int * it_cols = cols.GetData();
|
|
double *it_srow = srow.GetData();
|
|
|
|
for (int jblock = 0; jblock < nColBlocks; ++jblock)
|
|
{
|
|
if (Aij(iblock,jblock) != NULL)
|
|
{
|
|
Aij(iblock,jblock)->GetRow(iloc, bcols, bsrow);
|
|
for (int i = 0; i < bcols.Size(); ++i)
|
|
{
|
|
*(it_cols++) = bcols[i] + col_offsets[jblock];
|
|
*(it_srow++) = bsrow(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void BlockMatrix::EliminateRowCol(int rc, DiagonalPolicy dpolicy)
|
|
{
|
|
// Find the block to which the dof belongs and its local number
|
|
int idx, iiblock;
|
|
for (iiblock = 0; iiblock < nRowBlocks; ++iiblock)
|
|
{
|
|
idx = rc - row_offsets[iiblock];
|
|
if (idx < 0 ) { break; }
|
|
}
|
|
iiblock--;
|
|
idx = rc - row_offsets[iiblock];
|
|
|
|
// Asserts
|
|
MFEM_ASSERT(nRowBlocks == nColBlocks,
|
|
"BlockMatrix::EliminateRowCol: nRowBlocks != nColBlocks");
|
|
|
|
MFEM_ASSERT(row_offsets[iiblock] == col_offsets[iiblock],
|
|
"BlockMatrix::EliminateRowCol: row_offests["
|
|
<< iiblock << "] != col_offsets["<<iiblock<<"]");
|
|
|
|
MFEM_ASSERT(Aij(iiblock, iiblock),
|
|
"BlockMatrix::EliminateRowCol: Null diagonal block");
|
|
|
|
// Apply the constraint idx to the iiblock
|
|
for (int jjblock = 0; jjblock < nRowBlocks; ++jjblock)
|
|
{
|
|
if (iiblock == jjblock) { continue; }
|
|
if (Aij(iiblock,jjblock)) { Aij(iiblock,jjblock)->EliminateRow(idx); }
|
|
}
|
|
for (int jjblock = 0; jjblock < nRowBlocks; ++jjblock)
|
|
{
|
|
if (iiblock == jjblock) { continue; }
|
|
if (Aij(jjblock,iiblock)) { Aij(jjblock,iiblock)->EliminateCol(idx); }
|
|
}
|
|
Aij(iiblock, iiblock)->EliminateRowCol(idx,dpolicy);
|
|
}
|
|
|
|
void BlockMatrix::EliminateRowCol(Array<int> & ess_bc_dofs, Vector & sol,
|
|
Vector & rhs)
|
|
{
|
|
if (nRowBlocks != nColBlocks)
|
|
{
|
|
mfem_error("BlockMatrix::EliminateRowCol: nRowBlocks != nColBlocks");
|
|
}
|
|
|
|
for (int iiblock = 0; iiblock < nRowBlocks; ++iiblock)
|
|
{
|
|
if (row_offsets[iiblock] != col_offsets[iiblock])
|
|
{
|
|
mfem::out << "BlockMatrix::EliminateRowCol: row_offests["
|
|
<< iiblock << "] != col_offsets["<<iiblock<<"]\n";
|
|
mfem_error();
|
|
}
|
|
}
|
|
|
|
// We also have to do the same for each Aij
|
|
Array<int> block_dofs;
|
|
Vector block_sol, block_rhs;
|
|
|
|
for (int iiblock = 0; iiblock < nRowBlocks; ++iiblock)
|
|
{
|
|
int dsize = row_offsets[iiblock+1] - row_offsets[iiblock];
|
|
block_dofs.MakeRef(ess_bc_dofs.GetData()+row_offsets[iiblock], dsize);
|
|
block_sol.SetDataAndSize(sol.GetData()+row_offsets[iiblock], dsize);
|
|
block_rhs.SetDataAndSize(rhs.GetData()+row_offsets[iiblock], dsize);
|
|
|
|
if (Aij(iiblock, iiblock))
|
|
{
|
|
for (int i = 0; i < block_dofs.Size(); ++i)
|
|
{
|
|
if (block_dofs[i])
|
|
{
|
|
Aij(iiblock, iiblock)->EliminateRowCol(i,block_sol(i), block_rhs);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int i = 0; i < block_dofs.Size(); ++i)
|
|
{
|
|
if (block_dofs[i])
|
|
{
|
|
mfem_error("BlockMatrix::EliminateRowCol: Null diagonal block \n");
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int jjblock = 0; jjblock < nRowBlocks; ++jjblock)
|
|
{
|
|
if (jjblock != iiblock && Aij(iiblock, jjblock))
|
|
{
|
|
for (int i = 0; i < block_dofs.Size(); ++i)
|
|
{
|
|
if (block_dofs[i])
|
|
{
|
|
Aij(iiblock, jjblock)->EliminateRow(i);
|
|
}
|
|
}
|
|
}
|
|
if (jjblock != iiblock && Aij(jjblock, iiblock))
|
|
{
|
|
block_rhs.SetDataAndSize(rhs.GetData()+row_offsets[jjblock],
|
|
row_offsets[jjblock+1] - row_offsets[jjblock]);
|
|
Aij(jjblock, iiblock)->EliminateCols(block_dofs, &block_sol, &block_rhs);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void BlockMatrix::EliminateZeroRows(const double threshold)
|
|
{
|
|
MFEM_VERIFY(nRowBlocks == nColBlocks, "not a square matrix");
|
|
|
|
for (int iblock = 0; iblock < nRowBlocks; ++iblock)
|
|
{
|
|
if (Aij(iblock,iblock))
|
|
{
|
|
double norm;
|
|
for (int i = 0; i < Aij(iblock, iblock)->NumRows(); ++i)
|
|
{
|
|
norm = 0.;
|
|
for (int jblock = 0; jblock < nColBlocks; ++jblock)
|
|
if (Aij(iblock,jblock))
|
|
{
|
|
norm += Aij(iblock,jblock)->GetRowNorml1(i);
|
|
}
|
|
|
|
if (norm <= threshold)
|
|
{
|
|
for (int jblock = 0; jblock < nColBlocks; ++jblock)
|
|
{
|
|
if (Aij(iblock,jblock))
|
|
{
|
|
Aij(iblock,jblock)->EliminateRow(
|
|
i, (iblock==jblock) ? DIAG_ONE : DIAG_ZERO);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
double norm;
|
|
for (int i = 0; i < row_offsets[iblock+1] - row_offsets[iblock]; ++i)
|
|
{
|
|
norm = 0.;
|
|
for (int jblock = 0; jblock < nColBlocks; ++jblock)
|
|
{
|
|
if (Aij(iblock,jblock))
|
|
{
|
|
norm += Aij(iblock,jblock)->GetRowNorml1(i);
|
|
}
|
|
}
|
|
|
|
MFEM_VERIFY(!(norm <= threshold), "diagonal block is NULL:"
|
|
" iblock = " << iblock << ", i = " << i << ", norm = "
|
|
<< norm);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void BlockMatrix::Finalize(int skip_zeros, bool fix_empty_rows)
|
|
{
|
|
for (int iblock = 0; iblock < nRowBlocks; ++iblock)
|
|
{
|
|
for (int jblock = 0; jblock < nColBlocks; ++jblock)
|
|
{
|
|
if (!Aij(iblock,jblock)) { continue; }
|
|
if (!Aij(iblock,jblock)->Finalized())
|
|
{
|
|
Aij(iblock,jblock)->Finalize(skip_zeros, fix_empty_rows);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void BlockMatrix::Mult(const Vector & x, Vector & y) const
|
|
{
|
|
if (x.GetData() == y.GetData())
|
|
{
|
|
mfem_error("Error: x and y can't point to the same datas \n");
|
|
}
|
|
|
|
MFEM_ASSERT(width == x.Size(), "Input vector size (" << x.Size()
|
|
<< ") must match matrix width (" << width << ")");
|
|
MFEM_ASSERT(height == y.Size(), "Output vector size (" << y.Size()
|
|
<< ") must match matrix height (" << height << ")");
|
|
y = 0.;
|
|
AddMult(x, y, 1.0);
|
|
}
|
|
|
|
void BlockMatrix::AddMult(const Vector & x, Vector & y, const double val) const
|
|
{
|
|
if (x.GetData() == y.GetData())
|
|
{
|
|
mfem_error("Error: x and y can't point to the same datas \n");
|
|
}
|
|
|
|
Vector xblockview, yblockview;
|
|
|
|
for (int iblock = 0; iblock != nRowBlocks; ++iblock)
|
|
{
|
|
yblockview.SetDataAndSize(y.GetData() + row_offsets[iblock],
|
|
row_offsets[iblock+1] - row_offsets[iblock]);
|
|
|
|
for (int jblock = 0; jblock != nColBlocks; ++jblock)
|
|
{
|
|
if (Aij(iblock, jblock) != NULL)
|
|
{
|
|
xblockview.SetDataAndSize(
|
|
x.GetData() + col_offsets[jblock],
|
|
col_offsets[jblock+1] - col_offsets[jblock]);
|
|
|
|
Aij(iblock, jblock)->AddMult(xblockview, yblockview, val);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void BlockMatrix::MultTranspose(const Vector & x, Vector & y) const
|
|
{
|
|
if (x.GetData() == y.GetData())
|
|
{
|
|
mfem_error("Error: x and y can't point to the same datas \n");
|
|
}
|
|
|
|
y = 0.;
|
|
AddMultTranspose(x, y, 1.0);
|
|
}
|
|
|
|
void BlockMatrix::AddMultTranspose(const Vector & x, Vector & y,
|
|
const double val) const
|
|
{
|
|
if (x.GetData() == y.GetData())
|
|
{
|
|
mfem_error("Error: x and y can't point to the same datas \n");
|
|
}
|
|
|
|
Vector xblockview, yblockview;
|
|
|
|
for (int iblock = 0; iblock != nColBlocks; ++iblock)
|
|
{
|
|
yblockview.SetDataAndSize(y.GetData() + col_offsets[iblock],
|
|
col_offsets[iblock+1] - col_offsets[iblock]);
|
|
|
|
for (int jblock = 0; jblock != nRowBlocks; ++jblock)
|
|
{
|
|
if (Aij(jblock, iblock) != NULL)
|
|
{
|
|
xblockview.SetDataAndSize(
|
|
x.GetData() + row_offsets[jblock],
|
|
row_offsets[jblock+1] - row_offsets[jblock]);
|
|
|
|
Aij(jblock, iblock)->AddMultTranspose(xblockview, yblockview, val);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SparseMatrix * BlockMatrix::CreateMonolithic() const
|
|
{
|
|
int nnz = NumNonZeroElems();
|
|
|
|
int * i_amono = Memory<int>(row_offsets[nRowBlocks]+2);
|
|
int * j_amono = Memory<int>(nnz);
|
|
double * data = Memory<double>(nnz);
|
|
|
|
for (int i = 0; i < row_offsets[nRowBlocks]+2; i++)
|
|
{
|
|
i_amono[i] = 0;
|
|
}
|
|
|
|
int * i_amono_construction = i_amono+1;
|
|
|
|
for (int iblock = 0; iblock != nRowBlocks; ++iblock)
|
|
{
|
|
for (int irow(row_offsets[iblock]); irow < row_offsets[iblock+1]; ++irow)
|
|
{
|
|
int local_row = irow - row_offsets[iblock];
|
|
int ind = i_amono_construction[irow];
|
|
for (int jblock = 0; jblock < nColBlocks; ++jblock)
|
|
{
|
|
if (Aij(iblock,jblock) != NULL)
|
|
ind += Aij(iblock, jblock)->GetI()[local_row+1]
|
|
- Aij(iblock, jblock)->GetI()[local_row];
|
|
}
|
|
i_amono_construction[irow+1] = ind;
|
|
}
|
|
}
|
|
|
|
// Fill in the jarray and copy the data
|
|
for (int iblock = 0; iblock != nRowBlocks; ++iblock)
|
|
{
|
|
for (int jblock = 0; jblock != nColBlocks; ++jblock)
|
|
{
|
|
if (Aij(iblock,jblock) != NULL)
|
|
{
|
|
int nrow = row_offsets[iblock+1]-row_offsets[iblock];
|
|
int * i_aij = Aij(iblock, jblock)->GetI();
|
|
int * j_aij = Aij(iblock, jblock)->GetJ();
|
|
double * data_aij = Aij(iblock, jblock)->GetData();
|
|
int *i_it = i_amono_construction+row_offsets[iblock];
|
|
|
|
int loc_start_index = 0;
|
|
int loc_end_index = 0;
|
|
int glob_start_index = 0;
|
|
|
|
int shift(col_offsets[jblock]);
|
|
for (int * i_it_aij(i_aij+1); i_it_aij != i_aij+nrow+1; ++i_it_aij)
|
|
{
|
|
glob_start_index = *i_it;
|
|
|
|
#ifdef MFEM_DEBUG
|
|
if (glob_start_index > nnz)
|
|
{
|
|
mfem::out<<"glob_start_index = " << glob_start_index << "\n";
|
|
mfem::out<<"Block:" << iblock << " " << jblock << "\n";
|
|
mfem::out<<std::endl;
|
|
}
|
|
#endif
|
|
loc_end_index = *(i_it_aij);
|
|
for (int cnt = 0; cnt < loc_end_index-loc_start_index; cnt++)
|
|
{
|
|
data[glob_start_index+cnt] = data_aij[loc_start_index+cnt];
|
|
j_amono[glob_start_index+cnt] = j_aij[loc_start_index+cnt] + shift;
|
|
}
|
|
|
|
*i_it += loc_end_index-loc_start_index;
|
|
++i_it;
|
|
loc_start_index = loc_end_index;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return new SparseMatrix(i_amono, j_amono, data, row_offsets[nRowBlocks],
|
|
col_offsets[nColBlocks]);
|
|
}
|
|
|
|
void BlockMatrix::PrintMatlab(std::ostream & os) const
|
|
{
|
|
|
|
Vector row_data;
|
|
Array<int> row_ind;
|
|
int nnz_elem = NumNonZeroElems();
|
|
os<<"% size " << row_offsets.Last() << " " << col_offsets.Last() << "\n";
|
|
os<<"% Non Zeros " << nnz_elem << "\n";
|
|
int i, j;
|
|
std::ios::fmtflags old_fmt = os.flags();
|
|
os.setf(std::ios::scientific);
|
|
std::streamsize old_prec = os.precision(14);
|
|
for (i = 0; i < row_offsets.Last(); i++)
|
|
{
|
|
GetRow(i, row_ind, row_data);
|
|
for (j = 0; j < row_ind.Size(); j++)
|
|
{
|
|
os << i+1 << " " << row_ind[j]+1 << " " << row_data[j] << std::endl;
|
|
}
|
|
}
|
|
|
|
os.precision(old_prec);
|
|
os.flags(old_fmt);
|
|
}
|
|
|
|
BlockMatrix * Transpose(const BlockMatrix & A)
|
|
{
|
|
BlockMatrix * At = new BlockMatrix(A.ColOffsets(), A.RowOffsets());
|
|
At->owns_blocks = 1;
|
|
|
|
for (int irowAt = 0; irowAt < At->NumRowBlocks(); ++irowAt)
|
|
{
|
|
for (int jcolAt = 0; jcolAt < At->NumColBlocks(); ++jcolAt)
|
|
{
|
|
if (!A.IsZeroBlock(jcolAt, irowAt))
|
|
{
|
|
At->SetBlock(irowAt, jcolAt, Transpose(A.GetBlock(jcolAt, irowAt)));
|
|
}
|
|
}
|
|
}
|
|
return At;
|
|
}
|
|
|
|
BlockMatrix * Mult(const BlockMatrix & A, const BlockMatrix & B)
|
|
{
|
|
BlockMatrix * C= new BlockMatrix(A.RowOffsets(), B.ColOffsets());
|
|
C->owns_blocks = 1;
|
|
Array<SparseMatrix *> CijPieces(A.NumColBlocks());
|
|
|
|
for (int irowC = 0; irowC < A.NumRowBlocks(); ++irowC)
|
|
{
|
|
for (int jcolC = 0; jcolC < B.NumColBlocks(); ++jcolC)
|
|
{
|
|
CijPieces.SetSize(0, static_cast<SparseMatrix *>(NULL));
|
|
for (int k = 0; k < A.NumColBlocks(); ++k)
|
|
{
|
|
if (!A.IsZeroBlock(irowC, k) && !B.IsZeroBlock(k, jcolC))
|
|
{
|
|
CijPieces.Append(Mult(A.GetBlock(irowC, k), B.GetBlock(k, jcolC)));
|
|
}
|
|
}
|
|
|
|
if (CijPieces.Size() > 1)
|
|
{
|
|
C->SetBlock(irowC, jcolC, Add(CijPieces));
|
|
for (SparseMatrix ** it = CijPieces.GetData();
|
|
it != CijPieces.GetData()+CijPieces.Size(); ++it)
|
|
{
|
|
delete *it;
|
|
}
|
|
}
|
|
else if (CijPieces.Size() == 1)
|
|
{
|
|
C->SetBlock(irowC, jcolC, CijPieces[0]);
|
|
}
|
|
}
|
|
}
|
|
|
|
return C;
|
|
}
|
|
|
|
}
|