Derived& SparseBlockMatrixBase< Derived >::prune( const Scalar precision )
{
	MajorIndexType oldIndex = m_majorIndex ;

	typename Traits::BlocksArrayType old_blocks ;
	old_blocks.swap( m_blocks ) ;

	reserve( old_blocks.size() ) ;
	clear() ;

	for( Index outer = 0 ; outer < oldIndex.outerSize() ; ++outer )
	{
		for( typename MajorIndexType::InnerIterator it( oldIndex, outer ) ; it ; ++it )
		{
			if( ! is_zero( old_blocks[ it.ptr() ], precision ) )
			{
				m_majorIndex.insertBack( outer, it.inner(), m_blocks.size() ) ;
				m_blocks.push_back( old_blocks[ it.ptr() ] ) ;
			}
		}
	}

	m_minorIndex.valid = empty() ;
	finalize() ;

	return derived() ;
}
Derived& SparseBlockMatrixBase<Derived>::add( const SparseBlockMatrixBase< OtherDerived > &rhs, Scalar alpha )
{
	BOGUS_STATIC_ASSERT( !Transpose || IsTransposable< typename OtherDerived::BlockType >::Value,
						 TRANSPOSE_IS_NOT_DEFINED_FOR_THIS_BLOCK_TYPE
	) ;

	typedef typename SparseBlockMatrixBase< OtherDerived >::Traits OtherTraits ;
	typedef std::pair< BlockPtr, typename OtherTraits::BlockPtr > PtrPair ;
	typedef std::pair< Index, PtrPair > NonZero ;

	std::vector< std::vector< NonZero > > nonZeros ;

	// I - Compute non-zeros
	{

		SparseBlockIndexComputer< OtherDerived, Traits::is_col_major, Transpose >
				indexComputer( rhs ) ;
		typedef typename SparseBlockIndexComputer< OtherDerived, Traits::is_col_major, Transpose >::ReturnType
				SourceIndexType ;
		const SourceIndexType &rhsIndex = indexComputer.get() ;

		const MajorIndexType &lhsIndex = majorIndex() ;

		assert( rhsIndex.outerSize() == lhsIndex.outerSize() ) ;
		assert( rhsIndex.innerSize() == lhsIndex.innerSize() ) ;

		nonZeros.resize( lhsIndex.outerSize() ) ;

	#ifndef BOGUS_DONT_PARALLELIZE
	#pragma omp parallel for
	#endif
		for ( Index i = 0 ; i < lhsIndex.outerSize() ; ++i )
		{
			typename MajorIndexType::InnerIterator lhs_it ( lhsIndex, i ) ;
			typename SourceIndexType::InnerIterator rhs_it ( rhsIndex, i ) ;

			NonZero nz ;
			while( lhs_it || rhs_it )
			{
				if( lhs_it && ( !rhs_it || lhs_it.inner() < rhs_it.inner() ) )
				{
					nz.first = lhs_it.inner() ;
					nz.second.first = lhs_it.ptr() ;
					nz.second.second = OtherDerived::InvalidBlockPtr ;
					++ lhs_it ;
				} else if ( rhs_it && ( !lhs_it || rhs_it.inner() < lhs_it.inner() ) ) {
					nz.first = rhs_it.inner() ;
					nz.second.first = InvalidBlockPtr ;
					nz.second.second = rhs_it.ptr() ;
					++ rhs_it ;
				} else {
					nz.first = lhs_it.inner() ;
					nz.second.first = lhs_it.ptr() ;
					nz.second.second = rhs_it.ptr() ;
					++lhs_it ;
					++rhs_it ;
				}

				if( Traits::is_symmetric && nz.first > i )
					break ;

				nonZeros[i].push_back( nz ) ;
			}
		}
	}


	std::vector< BlockPtr > offsets( nonZeros.size() + 1 ) ;

	MajorIndexType resIndex ;
	resIndex.resizeOuter( nonZeros.size() ) ;
	offsets[0] = 0 ;
	for( unsigned i = 0 ; i < nonZeros.size() ; ++i )
	{
		offsets[i+1] = offsets[i] + nonZeros[i].size() ;
	}

	resIndex.reserve( offsets.back() ) ;
	for( unsigned i = 0 ; i < nonZeros.size() ; ++i )
	{
		for( unsigned j = 0 ; j < nonZeros[i].size() ; ++j )
		{
			const BlockPtr ptr = offsets[i]+j ;
			const NonZero &nz = nonZeros[i][j] ;
			resIndex.insertBack( i, nz.first, ptr ) ;
		}
	}

	typename Traits::BlocksArrayType resBlocks( offsets.back() ) ;

	typedef BlockTransposeOption<
			OtherTraits::is_symmetric && !( BlockTraits< typename OtherTraits::BlockType >::is_self_transpose ),
			Transpose > RhsGetter ;

	// II - Proper addition

#ifndef BOGUS_DONT_PARALLELIZE
#pragma omp parallel for
#endif
	for( std::ptrdiff_t i = 0 ; i < (std::ptrdiff_t) nonZeros.size() ; ++i )
	{
		for( unsigned j = 0 ; j < nonZeros[i].size() ; ++j )
		{
			const BlockPtr ptr = offsets[i]+j ;
			const NonZero &nz = nonZeros[i][j] ;

			BlockType &res = resBlocks[ptr] ;
			const bool afterDiag = ( (bool) Traits::is_col_major )  == ( (bool) OtherTraits::is_col_major )
									? (nz.first > i) : (nz.first < i) ;
			if( nz.second.first == InvalidBlockPtr )
			{
				RhsGetter::assign( rhs.block( nz.second.second ), res, alpha, afterDiag ) ;
			} else if( nz.second.second == OtherDerived::InvalidBlockPtr )
			{
				res = block( nz.second.first ) ;
			} else {
				RhsGetter::assign( rhs.block( nz.second.second ), res, alpha, afterDiag ) ;
				res += block( nz.second.first) ;
			}
		}
	}
	resIndex.finalize() ;

	clear() ;
	m_majorIndex.move( resIndex );
	resBlocks.swap( m_blocks ) ;
	m_minorIndex.valid = false ;

	Finalizer::finalize( *this ) ;

	return derived() ;
}