//========= Copyright Valve Corporation, All rights reserved. ============//
//
//===========================================================================//

#include "tier1/sparsematrix.h"

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"


void CSparseMatrix::AdjustAllRowIndicesAfter( int nStartRow, int nDelta )
{
	// now, we need to offset the starting position of all subsequent rows by -1 to compensate for the removal of this element
	for( int nOtherRow = nStartRow + 1 ; nOtherRow < Height(); nOtherRow++ )
	{
		m_rowDescriptors[nOtherRow].m_nDataIndex += nDelta;
	}
}

void CSparseMatrix::SetDimensions( int nNumRows, int nNumCols )
{
	m_nNumRows = nNumRows;
	m_nNumCols = nNumCols;
	m_entries.SetCount( 0 );
	m_rowDescriptors.SetCount( m_nNumRows );
	// and set all rows to be empty
	for( int i = 0; i < m_nNumRows; i++ )
	{
		m_rowDescriptors[i].m_nNonZeroCount = 0;
		m_rowDescriptors[i].m_nDataIndex = 0;
	}
	m_nHighestRowAppendedTo = -1;

}


void CSparseMatrix::SetElement( int nRow, int nCol, float flValue ) 
{
	Assert( nCol < m_nNumCols );
	int nCount = m_rowDescriptors[nRow].m_nNonZeroCount;
	bool bValueIsZero = ( flValue == 0.0 );
	int nFirstEntryIndex = m_rowDescriptors[nRow].m_nDataIndex;
	if ( nCount )
	{
		NonZeroValueDescriptor_t *pValue = &( m_entries[nFirstEntryIndex] );
		int i;
		for( i = 0; i < nCount; i++ )
		{
			int nIdx = pValue->m_nColumnNumber;
			if ( nIdx == nCol )								// we found it!
			{
				if ( !bValueIsZero )
				{
					// we want to overwrite the existing value
					pValue->m_flValue = flValue;
				}
				else
				{
					// there is a non-zero element currently at this position. We need to remove it
					// and we need to remove its storage.
					m_rowDescriptors[nRow].m_nNonZeroCount--;
					m_entries.Remove( nFirstEntryIndex + i );
					// now, we need to offset the starting position of all subsequent rows by -1 to compensate for the removal of this element
					AdjustAllRowIndicesAfter( nRow, -1 );
				}
				return;
			}
			if ( nIdx > nCol )
			{
				break;
			}
			pValue++;
		}
		// we did not find an entry for this cell. If we were writing zero, fine - we are
		// done, otherwise insert
		if (! bValueIsZero )
		{
			m_rowDescriptors[nRow].m_nNonZeroCount++;
			NonZeroValueDescriptor_t newValue;
			newValue.m_nColumnNumber = nCol;
			newValue.m_flValue = flValue;
			if ( i == nCount )								// need to append
			{
				m_entries.InsertAfter( nFirstEntryIndex + nCount - 1, newValue );
			}
			else
			{
				m_entries.InsertBefore( nFirstEntryIndex + i, newValue );
			}
			// now, we need to offset the starting position of all subsequent rows by -1 to compensate for the addition of this element
			AdjustAllRowIndicesAfter( nRow, +1 );
		}
	}
	else
	{
		// row is empty. We may need to insert
		if ( ! bValueIsZero )
		{
			m_rowDescriptors[nRow].m_nNonZeroCount++;
			NonZeroValueDescriptor_t newValue;
			newValue.m_nColumnNumber = nCol;
			newValue.m_flValue = flValue;
			m_entries.InsertBefore( nFirstEntryIndex, newValue );
			AdjustAllRowIndicesAfter( nRow, +1 );
		}
	}
}

void CSparseMatrix::FinishedAppending( void )
{
	// set all pointers to space for subsequent rows to the right value
	for( int i = m_nHighestRowAppendedTo + 1 ; i < Height(); i++ )
	{
		m_rowDescriptors[i].m_nDataIndex = m_entries.Count();
	}
}

void CSparseMatrix::AppendElement( int nRow, int nColumn, float flValue )
{
	if ( flValue != 0.0 )
	{
		if ( m_nHighestRowAppendedTo != nRow )
		{
			Assert( nRow > m_nHighestRowAppendedTo );
			for( int i = m_nHighestRowAppendedTo + 1; i <= nRow; i++ )
			{
				m_rowDescriptors[i].m_nDataIndex = m_entries.Count();
			}
		}
		m_nHighestRowAppendedTo = nRow;
		m_rowDescriptors[nRow].m_nNonZeroCount++;
		NonZeroValueDescriptor_t newDesc;
		newDesc.m_nColumnNumber = nColumn;
		newDesc.m_flValue = flValue;
		m_entries.AddToTail( newDesc );
	}
}