コード例 #1
0
ファイル: justatest.cpp プロジェクト: lemahdi/mglib
MG_XLObjectPtr
VolatilityCurve_Create	(	const MG_Date	& aAsOf
						,	const CellMatrix& aMaturities
						,	const CellMatrix& aTenors
						,	const CellMatrix& aVolatilities
						,	const string& aCcy
						,	const string& aUnderIndex
						,	const CellMatrix& aInterpolMeths)
{
	if (aVolatilities.Size() != aTenors.Size()*aMaturities.Size())
		MG_THROW("Volatilities matrix size and (Maturities,Tenors) size are not consistent");

	vector<double> vMaturities	= FromCellMatrixToVectorDouble(aMaturities, 0);
	vector<double> vTenors		= FromCellMatrixToVectorDouble(aTenors, 0);
	MG_Matrix vVols				= FromCellMatrixToMGMatrix		(aVolatilities);

	vector<int> vInterpolMeths = vector<int>(2, LINEAR_INTERPOL);
	if (!aInterpolMeths(0,0).IsEmpty())
		vInterpolMeths = FromCellMatrixToInterpolVector(aInterpolMeths);
	if (vInterpolMeths.size() == 1)
		vInterpolMeths.push_back(vInterpolMeths[0]);
	long vInterpolCode = MG_Interpolator::CreateInterpolCode(vInterpolMeths);

	return MG_XLObjectPtr(new MG_IRVolatilityCurve(aAsOf, vMaturities, vTenors, vVols, aCcy, aUnderIndex, vInterpolCode));
}
コード例 #2
0
ファイル: utils.cpp プロジェクト: lemahdi/mglib
	/* converting a CellMatrix to a std::vector<string> */
	vector<string> FromCellMatrixToVectorStr(const CellMatrix& aCM, const vector<bool>& aIsDate)
	{
		size_t vRows(aCM.RowsInStructure()), vCols(aCM.ColumnsInStructure());
		vector<string> vVect(vRows*vCols);
		CellValue vTmpCell;
		double vTmpDbl;
		MG_Date vTmpDate;
		for(size_t i=0; i<vRows; ++i)
		{
			for(size_t j=0; j<vCols; ++j)
			{
				vTmpCell = aCM(i,j);
				if (vTmpCell.IsAString())
				{
					vVect[j+i*vCols] = aCM(i,j);
					continue;
				}
				if (vTmpCell.IsANumber())
				{
					vTmpDbl = aCM(i,j);
					if (aIsDate[j])
					{
						vTmpDate = MG_Date(FromXLDateToJulianDay(vTmpDbl));
						vVect[j+i*vCols] = vTmpDate.ToString('/');
						continue;
					}
					vVect[j+i*vCols] = ftoa(vTmpDbl);
					continue;
				}
			}
		}
		return vVect;
	}
コード例 #3
0
ファイル: utils.cpp プロジェクト: lemahdi/mglib
	/* converting a CellMatrix to a MG_Matrix */
	MG_Matrix FromCellMatrixToMGMatrix(const CellMatrix& aCM)
	{
		size_t vRows(aCM.RowsInStructure()), vCols(aCM.ColumnsInStructure());
		MG_Matrix vMat(vRows, vCols);
		for(size_t i=0; i<vRows; ++i)
			for(size_t j=0; j<vCols; ++j)
				vMat(i, j) = aCM(i, j).NumericValue();
		return vMat;
	}
コード例 #4
0
ファイル: Attributes.cpp プロジェクト: Laeeth/d_excelsdk
bool // checks to see if there's an error
ContainsError(const CellMatrix& input // data to check for errors
                             )
{
    for (unsigned long i=0; i < input.RowsInStructure(); ++i)
        for (unsigned long j=0; j < input.ColumnsInStructure(); ++j)
            if (input(i,j).IsError())
                return true;

    return false;

}
コード例 #5
0
ファイル: DoubleOrNothing.cpp プロジェクト: alhunor/projects
xlw::DoubleOrNothing::DoubleOrNothing(const CellMatrix& cells, const std::string& identifier)
{
    if (cells.ColumnsInStructure() != 1 || cells.RowsInStructure() != 1)
        THROW_XLW("Multiple values given where one expected for DoubleOrNothing " << identifier);

    if (!cells(0,0).IsEmpty() && !cells(0,0).IsANumber() )
        THROW_XLW("expected a double or nothing, got something else " << identifier);

    Empty = cells(0,0).IsEmpty();

    Value = Empty ? 0.0 : cells(0,0).NumericValue();

}
コード例 #6
0
ファイル: Attributes.cpp プロジェクト: Laeeth/d_excelsdk
bool // checks to see if there's a div by zero
ContainsDivByZero(const CellMatrix& input // data to check for errors
                             )
{
    for (unsigned long i=0; i < input.RowsInStructure(); ++i)
        for (unsigned long j=0; j < input.ColumnsInStructure(); ++j)
            if (input(i,j).IsError())
                if (input(i,j).ErrorValue() == 7UL)
                    return true;

    return false;

}
コード例 #7
0
ファイル: justatest.cpp プロジェクト: lemahdi/mglib
MG_XLObjectPtr
ZeroCurve_Create	(	const MG_Date	& aAsOf
					,	const CellMatrix& aMaturities
					,	const CellMatrix& aZeroRates
					,	const string	& aCcy
					,	const string	& aUnderIndex
					,	const string	& aInterpolMeth)
{
	if (aZeroRates.Size() != aMaturities.Size())
		MG_THROW("Zero vector size and Maturities size are not consistent");

	vector<double> vMaturities	= FromCellMatrixToVectorDouble(aMaturities, 0);
	vector<double> vZeroRates	= FromCellMatrixToVectorDouble(aZeroRates, 0);

	int vInterpolCode = InterpolMethodConvertor[aInterpolMeth];

	return MG_XLObjectPtr(new MG_ZeroCurve(aAsOf, vMaturities, vZeroRates, aCcy, aUnderIndex, vInterpolCode));
}
コード例 #8
0
ファイル: utils.cpp プロジェクト: lemahdi/mglib
	/* converting a CellMatrix to a MG_Vector */
	MG_Vector FromCellMatrixToMGVectorDate(const CellMatrix& aCM, const size_t& aIndex)
	{
		if (aCM.ColumnsInStructure()!=1 && aCM.RowsInStructure()!=1)
			MG_THROW("CellMatrix should be a one row or column structure");
		bool vIsRow = aCM.ColumnsInStructure() == 1;

		size_t vSize = vIsRow ? aCM.RowsInStructure() : aCM.ColumnsInStructure();
		MG_Vector vRes(vSize);
		if (vIsRow)
		{
			for(size_t i=0; i<vSize; ++i)
				vRes[i] = FromXLDateToJulianDay(aCM(i, aIndex).NumericValue());
			return vRes;
		}
		for(size_t i=0; i<vSize; ++i)
			vRes[i] = FromXLDateToJulianDay(aCM(aIndex, i).NumericValue());
		return vRes;
	}
コード例 #9
0
ファイル: utils.cpp プロジェクト: lemahdi/mglib
	/* converting a CellMatrix to a std::vector<double> */
	vector<double> FromCellMatrixToVectorDouble(const CellMatrix& aCM, const size_t& aIndex)
	{
		if (aCM.ColumnsInStructure()!=1 && aCM.RowsInStructure()!=1)
			MG_THROW("CellMatrix should be a one row or column structure");
		bool vIsRow = aCM.ColumnsInStructure() == 1;

		size_t vSize = vIsRow ? aCM.RowsInStructure() : aCM.ColumnsInStructure();
		vector<double> vRes(vSize);
		if (vIsRow)
		{
			for(size_t i=0; i<vSize; ++i)
				vRes[i] = aCM(i, aIndex).NumericValue();
			return vRes;
		}
		for(size_t i=0; i<vSize; ++i)
			vRes[i] = aCM(aIndex, i).NumericValue();
		return vRes;
	}
コード例 #10
0
ファイル: justatest.cpp プロジェクト: lemahdi/mglib
MG_XLObjectPtr
DividendsTable_Create	(	const MG_Date		& aAsOf
						,	const CellMatrix	& aExDivDates
						,	const CellMatrix	& aPaymentDates
						,	const CellMatrix	& aDividends
						,	const string		& aCcy
						,	const string		& aUnderIndex
						,	const MG_XLObjectPtr& aZC)
{
	if (aExDivDates.Size() != aExDivDates.Size())
		MG_THROW("Ex dividends dates and payments days should be equal");

	vector<MG_Date> vExDivDates		= FromCellMatrixToVectorDate(aExDivDates, 0);
	vector<MG_Date> vPaymentDates	= FromCellMatrixToVectorDate(aPaymentDates, 0);
	vector<double> vDividends		= FromCellMatrixToVectorDouble(aDividends, 0);

	return MG_XLObjectPtr(new MG_DividendsTable(aAsOf, vExDivDates, vPaymentDates, vDividends, aCcy, aUnderIndex, aZC));
}
コード例 #11
0
ファイル: ArgList.cpp プロジェクト: jabogithub/fann-excel
CellMatrix ExtractCells(CellMatrix& cells,
						unsigned long row,
						unsigned long column,
						std::string ErrorId,
						std::string thisName,
						bool nonNumeric)
{
	if (!cells(row,column).IsANumber())
		throw(ErrorId+" "+thisName+" rows and columns expected.");
	if (cells.ColumnsInStructure() <= column+1)
		throw(ErrorId+" "+thisName+" rows and columns expected.");
	if (!cells(row,column+1).IsANumber())
		throw(ErrorId+" "+thisName+" rows and columns expected.");

	unsigned long numberRows = cells(row,column);
	unsigned long numberColumns = cells(row,column+1);

	cells(row,column).clear();
	cells(row,column+1).clear();

	CellMatrix result(numberRows,numberColumns);

	if (numberRows +row+1>cells.RowsInStructure())
		throw(ErrorId+" "+thisName+" insufficient rows in structure");

	if (numberColumns +column>cells.ColumnsInStructure())
		throw(ErrorId+" "+thisName+" insufficient columns in structure");

	for (unsigned long i=0; i < numberRows; i++)
		for (unsigned long j=0; j < numberColumns; j++)
		{
			result(i,j) = cells(row+1+i,column+j);
			cells(row+1+i,column+j).clear();

			if (!result(i,j).IsANumber())
				nonNumeric = true;
		}


	return result;


}
コード例 #12
0
ファイル: CellMatrix.cpp プロジェクト: DangerMouseB/xpy
xlw::CellMatrix xlw::MergeCellMatrices(const CellMatrix& Top, const CellMatrix& Bottom)
{
    size_t cols = maxi(Top.ColumnsInStructure(), Bottom.ColumnsInStructure());
    size_t rows = Top.RowsInStructure()+Bottom.RowsInStructure();

    CellMatrix merged(rows,cols);

    {for (size_t i=0; i < Top.ColumnsInStructure(); i++)
        for (size_t j=0; j < Top.RowsInStructure(); j++)
            merged(j,i) = Top(j,i);}

    for (size_t i=0; i < Bottom.ColumnsInStructure(); i++)
        for (size_t j=0; j < Bottom.RowsInStructure(); j++)
            merged(j+Top.RowsInStructure(),i) = Bottom(j,i);

    return merged;
}
コード例 #13
0
ファイル: justatest.cpp プロジェクト: lemahdi/mglib
MG_XLObjectPtr
Robot(const MG_Date& aAsOf, CellMatrix& aMktData)
{
	MG_MarketDataPtr vMktData(NULL);
	size_t vSize = aMktData.RowsInStructure();
	string vDesc;
	vector<MG_MarketDataPtr> vMDVect(vSize);
	for(size_t i=0; i<vSize; ++i)
		vMDVect[i] = MG_MarketDataPtr(aMktData(i, 0).MGObjectValue()->Clone());

	return MG_XLObjectPtr(new MG_Robot(aAsOf, vMDVect));
}
コード例 #14
0
CellMatrix MergeCellMatrices(const CellMatrix& Top, const CellMatrix& Bottom)
{
	unsigned long cols = maxi(Top.ColumnsInStructure(), Bottom.ColumnsInStructure());
	unsigned long rows = Top.RowsInStructure()+Bottom.RowsInStructure();

	CellMatrix merged(rows,cols);

	{for (unsigned long i=0; i < Top.ColumnsInStructure(); i++)
		for (unsigned long j=0; j < Top.RowsInStructure(); j++)
			merged(j,i) = Top(j,i);}

	for (unsigned long i=0; i < Bottom.ColumnsInStructure(); i++)
		for (unsigned long j=0; j < Bottom.RowsInStructure(); j++)
			merged(j+Top.RowsInStructure(),i) = Bottom(j,i);


	return merged;
}
コード例 #15
0
ファイル: utils.cpp プロジェクト: lemahdi/mglib
	/* converting a CellMatrix of interpolation types to a sid::vector<int> */
	vector<int> FromCellMatrixToInterpolVector(const CellMatrix& aCM)
	{
		if (aCM.Size() > maxInterpoltypesNb)
		{
			ostringstream vOs;
			vOs << "Maximum number of interpolations is " << maxInterpoltypesNb << ", please advise.";
			MG_THROW(vOs.str());
		}
		vector<string> vInterpolTypesStr = FromCellMatrixToVectorStr(aCM);
		size_t vSize(vInterpolTypesStr.size());
		vector<int> vInterpolTypesInt(vSize);
		for(size_t i=0; i<vSize; ++i)
			vInterpolTypesInt[i] = InterpolMethodConvertor[vInterpolTypesStr[i]];
		return vInterpolTypesInt;
	}
コード例 #16
0
ファイル: justatest.cpp プロジェクト: lemahdi/mglib
MG_XLObjectPtr
GenSec_Create(const CellMatrix& aDealDesc)
{
	size_t vColsSize = aDealDesc.ColumnsInStructure();
	vector<bool> vIsDate(vColsSize);
	string vTmp, vDateStr("DATE");
	for(size_t i=0; i<vColsSize; ++i)
	{
		vTmp = aDealDesc(0,i).StringValue();
		vTmp = ToUpper(vTmp.substr(vTmp.size()-4));
		vIsDate[i] = vTmp==vDateStr ? true : false;
	}
	
	vector<string> vDealDesc = FromCellMatrixToVectorStr(aDealDesc, vIsDate);
	return MG_XLObjectPtr(new MG_GenSecurity(vDealDesc, vColsSize));
}
コード例 #17
0
ファイル: CellMatrix.cpp プロジェクト: DangerMouseB/xpy
void xlw::CellMatrix::PushBottom(const CellMatrix& newRows)
{
    CellMatrix newRowsResize(newRows);
    size_t newColumns = maxi(newRows.ColumnsInStructure(),Columns);

    if (newColumns > Columns)
        for (size_t i=0; i < Rows; i++)
            Cells[i].resize(newColumns);

    if (newColumns > newRows.Columns)
        for (size_t i=0; i < newRowsResize.Rows; i++)
            newRowsResize.Cells[i].resize(newColumns);

    for (size_t i=0; i < newRowsResize.Rows; i++)
        Cells.push_back(newRowsResize.Cells[i]);

    Rows = static_cast<size_t>(Cells.size());
    Columns = newColumns;
}
コード例 #18
0
void CellMatrix::PushBottom(const CellMatrix& newRows)
{
	CellMatrix newRowsResize(newRows);
	unsigned long newColumns = maxi(newRows.ColumnsInStructure(),Columns);

	if (newColumns > Columns)
		for (unsigned long i=0; i < Rows; i++)
			Cells[i].resize(newColumns);

	if (newColumns > newRows.Columns)
		for (unsigned long i=0; i < newRowsResize.Rows; i++)
			newRowsResize.Cells[i].resize(newColumns);

	for (unsigned long i=0; i < newRowsResize.Rows; i++)
		Cells.push_back(newRowsResize.Cells[i]);

	Rows = static_cast<unsigned long>(Cells.size());
	Columns = newColumns;

	

}
コード例 #19
0
ファイル: Utils.cpp プロジェクト: edelmoral/levinsky-pyinex
bool
ConvertCellMatrixToPyObject( const CellMatrix& rCM,
                             PyObject*& rpObj )
{
    bool rc = true;
    long i, j, cols, rows;
    PyObject *pCols, *pValue;

    rows = rCM.RowsInStructure();
    cols = rCM.ColumnsInStructure();
    assert(rows && cols);

    if (rows == 1 && cols == 1) {
        // Don't build a nested tuple; extract the single value
        const CellValue& rCV = rCM(0, 0);
        rc = ConvertCellValueToPyObject( rCV, rpObj );
        if (rc) {
            assert(rpObj);
        } else {
            assert(!rpObj);
            ERROUT("Failed to convert single cell to PyObject");
            rc = false;
        }
    } else if (rows == 1) { // Single horizontal rows should NOT be double-nested; they're vectors, not matrices
        assert(cols > 1);
        rpObj = PyTuple_New(cols); // Just ONE ROW here, with cols # of elements
        for (j = 0; rc && j < cols; ++j) {
            const CellValue& rCV = rCM(0, j);
            rc = ConvertCellValueToPyObject( rCV, pValue );
            if (rc) {
                assert(pValue);
                PyTuple_SetItem(rpObj, j, pValue); // pValue reference stolen here
            } else {
                assert(!pValue);
                ERROUT("Failed to convert element %d of single-row cell to PyObject", j);
                Py_DECREF(rpObj);   // Get rid of the entire row; it owns elements and will delete them
                rpObj = NULL;
                rc = false;
            }
        } // end j
    } else { // 2-D matrix
        rpObj = PyTuple_New(rows);
        for (i = 0; rc && i < rows; ++i) {
            pCols = PyTuple_New(cols);
            PyTuple_SetItem(rpObj, i, pCols); // pCols reference stolen here
            for (j = 0; rc && j < cols; ++j) {
                const CellValue& rCV = rCM(i, j);
                rc = ConvertCellValueToPyObject( rCV, pValue );
                if (rc) {
                    assert(pValue);
                    PyTuple_SetItem(pCols, j, pValue); // pValue reference stolen here
                } else {
                    assert(!pValue);
                    ERROUT("Failed to convert element %d, %d of matrix cell to PyObject", i, j);
                    Py_DECREF(rpObj);   // Get rid of the entire matrix; it owns elements and will delete them
                    rpObj = NULL;
                    rc = false;
                }
            } // end j
        } // end i
    } // end 2-D matrix code

    return rc;
}
コード例 #20
0
ファイル: ArgList.cpp プロジェクト: jabogithub/fann-excel
ArgumentList::ArgumentList(CellMatrix cells, 
						   std::string ErrorId)
{
	CellValue empty;
	unsigned long rows = cells.RowsInStructure();
	unsigned long columns = cells.ColumnsInStructure();

	if (rows == 0)
		throw(std::string("Argument List requires non empty cell matix ")+ErrorId);

	if (!cells(0,0).IsAString())
		throw(std::string("a structure name must be specified for argument list class ")+ErrorId);
	else
	{
		StructureName = cells(0,0).StringValueLowerCase();
		cells(0,0) = empty;
	}


	{for (unsigned long i=1; i < columns; i++)
		if (!cells(0,i).IsEmpty() )
			throw("An argument list should only have the structure name on the first line: "+StructureName+ " " + ErrorId);
	}

	ErrorId +=" "+StructureName;

	{for (unsigned long i=1; i < rows; i++)
		for (unsigned long j=0; j < columns; j++)
			if (cells(i,j).IsError())
				GenerateThrow("Error Cell passed in ",i,j);}

	unsigned long row=1UL;

	while (row < rows)
	{
		unsigned long rowsDown=1;
		unsigned column = 0;

		while (column < columns)
		{
			if (cells(row,column).IsEmpty())
			{
				// check nothing else in row
				while (column< columns)
				{
					if (!cells(row,column).IsEmpty())
						GenerateThrow("data or value where unexpected.",row, column);

					++column;
				}
			}
			else // we have data
			{
				if (!cells(row,column).IsAString())
					GenerateThrow("data  where name expected.", row, column);

				std::string thisName(cells(row,column).StringValueLowerCase());

				if (thisName =="")
					GenerateThrow("empty name not permissible.", row, column);

				if (rows == row+1)
					GenerateThrow("No space where data expected below name", row, column);

				cells(row,column).clear();
// weird syntax to satisfy VC6
				CellValue* belowPtr = &cells(row+1,column);
				CellValue& cellBelow = *belowPtr;

				if (cellBelow.IsEmpty())
					GenerateThrow("Data expected below name", row, column);

				if (cellBelow.IsANumber())
				{
					add(thisName, cellBelow.NumericValue());

					column++;

					cellBelow=empty;
				}
				else
					if (cellBelow.IsBoolean())
					{
						add(thisName, cellBelow.BooleanValue());

						column++;

						cellBelow=empty; 
					}
					else // ok its a string
					{
						std::string stringVal = cellBelow.StringValueLowerCase();

						if ( (cellBelow.StringValueLowerCase() == "list") || 
							(cellBelow.StringValueLowerCase() == "matrix") ||
							(cellBelow.StringValueLowerCase() == "cells") )
						{
							bool nonNumeric = false;
							CellMatrix extracted(ExtractCells(cells,row+2,column,ErrorId,thisName,nonNumeric));


							if (cellBelow.StringValueLowerCase() == "list")
							{
								ArgumentList value(extracted,ErrorId+":"+thisName);

								addList(thisName, extracted); //note not value

							}

							if (cellBelow.StringValueLowerCase() == "cells")
							{
								add(thisName,extracted);
							}

							
							if (cellBelow.StringValueLowerCase() == "matrix")
							{
								if (nonNumeric)
									throw("Non numerical value in matrix argument :"+thisName+ " "+ErrorId);

								MJMatrix value(extracted.RowsInStructure(),extracted.ColumnsInStructure());

								for (unsigned long i=0; i < extracted.RowsInStructure(); i++)
									for (unsigned long j=0; j < extracted.ColumnsInStructure(); j++)
										ChangingElement(value,i,j) = extracted(i,j);

								add(thisName,value);

							}

							cellBelow = empty;
							rowsDown = maxi(rowsDown,extracted.RowsInStructure()+2);
							column+= extracted.ColumnsInStructure();
						}
						else // ok its an array or boring string
						{
							if (cellBelow.StringValueLowerCase() == "array" 
								||cellBelow.StringValueLowerCase() == "vector" )
							{
								cellBelow.clear();
							
								if (row+2>= rows)
									throw(ErrorId+" data expected below array "+thisName);

								unsigned long size = cells(row+2,column);
								cells(row+2,column).clear();

								if (row+2+size>=rows)
									throw(ErrorId+" more data expected below array "+thisName);


								MyArray theArray(size);

								for (unsigned long i=0; i < size; i++)
								{
									theArray[i] = cells(row+3+i,column);
									cells(row+3+i,column).clear();
								}

								add(thisName,theArray);

								rowsDown = maxi(rowsDown,size+2);
			
								column+=1;
							}
							else
							{
								std::string value = cellBelow.StringValueLowerCase();
								add(thisName,value);
								column++;

								cellBelow=empty; 
							}
						}

					}
			}		

		}	
		row+=rowsDown+1;

	}

    {for (unsigned long i=0; i < rows; i++)
        for (unsigned long j=0; j < columns; j++)
            if (!cells(i,j).IsEmpty())
            {
               GenerateThrow("extraneous data "+ErrorId,i,j);
	}}
}
コード例 #21
0
ファイル: pthreads.cpp プロジェクト: erikhaq/particles
void *thread_routine( void *pthread_id )
{
    int thread_id = *(int*)pthread_id;
    int num_cells = cells.size();

    int particles_per_thread = (n + n_threads - 1) / n_threads;
    int rows_per_thread = (num_cells + n_threads - 1) / n_threads;

    int first_row = min(  thread_id     * rows_per_thread, num_cells);
    int last_row  = min( (thread_id+1)  * rows_per_thread, num_cells);

    int first = min(  thread_id    * particles_per_thread, n );
    int last  = min( (thread_id+1) * particles_per_thread, n );

    Particles my_particles;
    my_particles.clear();
    get_particles_from_rows(first_row, last_row, &my_particles, cells);

 
    //
    //  simulate a number of time steps
    //
    for( int step = 0; step < NSTEPS; step++ )
    {
        my_particles.clear();
        get_particles_from_rows(first_row, last_row, &my_particles, cells);
        
        //
        //  compute forces
        //
        for(int i = 0; i < my_particles.size(); i++)
        {
            particle_t *curr_particle = my_particles[i];
            curr_particle->ax = 0;
            curr_particle->ay = 0;
            apply_force(curr_particle, cells);            
        }

        
        pthread_barrier_wait( &barrier );
        
        //
        //  move particles
        //
        for(int i = 0; i < my_particles.size(); i++)
        {
            particle_t *curr_particle = my_particles[i];
            move(*my_particles[i]);            
        }
        
        pthread_barrier_wait( &barrier );
        clear_cells(first_row, last_row, cells);

        pthread_barrier_wait( &barrier );
        update_cells_parallel(first_row, last_row, my_particles, cells);
    
        pthread_barrier_wait( &barrier );
        
        //
        //  save if necessary
        //
        if( thread_id == 0 && fsave && (step%SAVEFREQ) == 0 )
            save( fsave, n, particles );
    }
    
    return NULL;
}