// Copyright (C) 2003-2009 Marc Duruflé
// Copyright (C) 2001-2009 Vivien Mallet
//
// This file is part of the linear-algebra library Seldon,
// http://seldon.sourceforge.net/.
//
// Seldon is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the Free
// Software Foundation; either version 2.1 of the License, or (at your option)
// any later version.
//
// Seldon is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
// more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Seldon. If not, see http://www.gnu.org/licenses/.


#ifndef SELDON_FILE_RELAXATION_MATVECT_CXX

/*
  Functions defined in this file
  
  SOR(A, X, B, omega, iter, type_ssor)

*/

namespace Seldon
{
  
  //! Successive overrelaxation.
  /*!
    Solving A X = B by using S.O.R algorithm.
    omega is the relaxation parameter, iter the number of iterations.
    type_ssor = 2 forward sweep
    type_ssor = 3 backward sweep
    type_ssor = 0 forward and backward sweep
  */
  template <class T0, class Prop0, class Allocator0,
	    class T1, class Storage1, class Allocator1,
	    class T2, class Storage2, class Allocator2, class T3>
  void SOR(const Matrix<T0, Prop0, RowSparse, Allocator0>& A,
	   Vector<T2, Storage2, Allocator2>& X,
	   const Vector<T1, Storage1, Allocator1>& B,
	   const T3& omega, int iter, int type_ssor = 2)
  {
    T1 temp(0);
    
    int ma = A.GetM();
    
#ifdef SELDON_CHECK_BOUNDS
    int na = A.GetN();
    if (na != ma)
      throw WrongDim("SOR", "Matrix must be squared.");
    
    if (ma != X.GetLength() || ma != B.GetLength())
      throw WrongDim("SOR", "Matrix and vector dimensions are incompatible.");
#endif
    
    int* ptr = A.GetPtr();
    int* ind = A.GetInd();
    typename Matrix<T0, Prop0, RowSparse, Allocator0>::pointer data
      = A.GetData();
    T0 ajj(1);
    
    // Forward sweep.
    if (type_ssor % 2 == 0)
      for (int i = 0; i < iter; i++)
	for (int j = 0; j < ma; j++)
	  {
	    temp = T1(0);
	    ajj = A(j, j);
	    for (int k = ptr[j] ; k < ptr[j+1]; k++)
	      temp += data[k] * X(ind[k]);
	    
	    temp = B(j) - temp + ajj * X(j);
	    X(j) = (T2(1) - omega) * X(j) + omega * temp / ajj;
	  }
    
    // Backward sweep.
    if (type_ssor % 3 == 0)
      for (int i = 0; i < iter; i++)
	for (int j = ma-1 ; j >= 0; j--)
	  {
	    temp = T1(0);
	    ajj = A(j, j);
	    for (int k = ptr[j]; k < ptr[j+1]; k++)
	      temp += data[k] * X(ind[k]);
	    
	    temp = B(j) - temp + ajj * X(j);
	    X(j) = (T2(1) - omega) * X(j) + omega * temp / ajj;
	  }
  }
  
  
  //! Successive overrelaxation.
  /*!
    Solving A X = B by using S.O.R algorithm.
    omega is the relaxation parameter, iter the number of iterations.
    type_ssor = 2 forward sweep
    type_ssor = 3 backward sweep
    type_ssor = 0 forward and backward sweep
  */
  template <class T0, class Prop0, class Allocator0,
	    class T1, class Storage1, class Allocator1,
	    class T2, class Storage2, class Allocator2, class T3>
  void SOR(const Matrix<T0, Prop0, ArrayRowSparse, Allocator0>& A,
	   Vector<T2, Storage2, Allocator2>& X,
	   const Vector<T1, Storage1, Allocator1>& B,
	   const T3& omega,
	   int iter, int type_ssor = 2)
  {
    T1 temp(0);
    
    int ma = A.GetM();
    
#ifdef SELDON_CHECK_BOUNDS
    int na = A.GetN();
    if (na != ma)
      throw WrongDim("SOR", "Matrix must be squared.");
    
    if (ma != X.GetLength() || ma != B.GetLength())
      throw WrongDim("SOR", "Matrix and vector dimensions are incompatible.");
#endif
    
    T0 ajj(1);
    
    // Forward sweep.
    if (type_ssor % 2 == 0)
      for (int i = 0; i < iter; i++)
	for (int j = 0; j < ma; j++)
	  {
	    temp = T1(0);
	    ajj = T0(0);
	    for (int k = 0; k < A.GetRowSize(j); k++)
	      {
		temp += A.Value(j,k) * X(A.Index(j,k));
		if (A.Index(j,k) == j)
		  ajj += A.Value(j,k);
	      }
	    
	    temp = B(j) - temp + ajj * X(j);
	    X(j) = (T2(1) - omega) * X(j) + omega * temp / ajj;
	  }
    
    // Backward sweep.
    if (type_ssor % 3 == 0)
      for (int i = 0; i < iter; i++)
	for (int j = ma-1; j >= 0; j--)
	  {
	    temp = T1(0);
	    ajj = T0(0);
	    for (int k = 0; k < A.GetRowSize(j); k++)
	      {
		temp += A.Value(j,k) * X(A.Index(j,k));
		if (A.Index(j,k) == j)
		  ajj += A.Value(j,k);
	      }
	    
	    temp = B(j) - temp + ajj * X(j);
	    X(j) = (T2(1) - omega) * X(j) + omega * temp / ajj;
	  }
  }
  
  
  //! Successive overrelaxation.
  /*!
    Solving A X = B by using S.O.R algorithm.
    omega is the relaxation parameter, iter the number of iterations.
    type_ssor = 2 forward sweep
    type_ssor = 3 backward sweep
    type_ssor = 0 forward and backward sweep
  */
  template <class T0, class Prop0, class Allocator0,
	    class T1, class Storage1, class Allocator1,
	    class T2, class Storage2, class Allocator2, class T3>
  void SOR(const Matrix<T0, Prop0, RowSymSparse, Allocator0>& A,
	   Vector<T2, Storage2, Allocator2>& X,
	   const Vector<T1, Storage1, Allocator1>& B,
	   const T3& omega,int iter, int type_ssor = 2)
  {
    T1 temp(0);
    
    int ma = A.GetM();

#ifdef SELDON_CHECK_BOUNDS
    int na = A.GetN();
    if (na != ma)
      throw WrongDim("SOR", "Matrix must be squared.");
    
    if (ma != X.GetLength() || ma != B.GetLength())
      throw WrongDim("SOR", "Matrix and vector dimensions are incompatible.");
#endif
    
    int* ptr = A.GetPtr();
    int* ind = A.GetInd();
    T0* data = A.GetData();

    Vector<T2, Storage2, Allocator2> Y(ma);
    Y.Zero();
    T0 ajj(1);
    int p;
    T0 val(0);
    // Let us consider the following splitting : A = D - L - U
    // D diagonal of A
    // L lower part of A
    // U upper part of A, A is symmetric, so L = U^t
    
    // Forward sweep
    // (D/omega - L) X^{n+1/2} = (U + (1-omega)/omega D) X^n + B
    if (type_ssor % 2 == 0)
      for (int i = 0; i < iter; i++)
	{
	  for (int j = 0; j < ma; j++)
	    {
	      // First we do X = (U + (1-omega)/omega D) X + B
	      temp = T1(0);
	      ajj = T0(0);
	      for (int k = ptr[j]; k < ptr[j+1]; k++)
		{
		  p = ind[k];
		  val = data[k];
		  if (p == j)
		    ajj += val;
		  else
		    temp += val * X(p);
		}
	      
	      temp = B(j) - temp;
	      X(j) = (T2(1) - omega) / omega * ajj * X(j) + temp;
	    }
	  
	  for (int j = 0; j < ma; j++)
	    {
	      ajj = T0(0);
	      // Then we solve (D/omega - L) X = X
	      for (int k = ptr[j]; k < ptr[j+1]; k++)
		{
		  p = ind[k];
		  val = data[k];
		  if (p == j)
		    ajj += val;
		}
	      X(j) *= omega / ajj;
	      for (int k = ptr[j]; k < ptr[j+1]; k++)
		{
		  p = ind[k];
		  val = data[k];
		  if (p != j)
		    X(p) -= val*X(j);
		}
	    }
	}
    

    // Backward sweep.
    // (D/omega - U) X^{n+1} = (L + (1-omega)/omega D) X^{n+1/2} + B
    if (type_ssor % 3 == 0)
      for (int i = 0; i < iter; i++)
	{
	  Y.Zero();
	  for (int j = 0; j < ma; j++)
	    {
	      ajj = T0(0);
	      // Then we compute X = (L + (1-omega)/omega D) X + B.
	      for (int k = ptr[j]; k < ptr[j+1]; k++)
		{
		  p = ind[k];
		  val = data[k];
		  if (p == j)
		    ajj += val;
		  else
		    Y(p) += val*X(j);
		}
	      
	      X(j) = (T2(1) - omega) / omega * ajj * X(j) + B(j) - Y(j);
	    }
	  
	  for (int j = ma-1; j >= 0; j--)
	    {
	      temp = T1(0);
	      ajj = T0(0);
	      // Then we solve (D/omega - U) X = X.
	      for (int k = ptr[j]; k < ptr[j+1]; k++)
		{
		  p = ind[k];
		  val = data[k];
		  if (p == j)
		    ajj += val;
		  else
		    temp += val*X(p);
		}
	      X(j) = (X(j) - temp) * omega / ajj;
	    }
	}
  }
  
  
  //! Successive overrelaxation.
  /*!
    Solving A X = B by using S.O.R algorithm.
    omega is the relaxation parameter, iter the number of iterations.
    type_ssor = 2 forward sweep
    type_ssor = 3 backward sweep
    type_ssor = 0 forward and backward sweep
  */
  template <class T0, class Prop0, class Allocator0,
	    class T1, class Storage1, class Allocator1,
	    class T2, class Storage2, class Allocator2, class T3>
  void SOR(const Matrix<T0, Prop0, ArrayRowSymSparse, Allocator0>& A,
	   Vector<T2, Storage2, Allocator2>& X,
	   const Vector<T1, Storage1, Allocator1>& B,
	   const T3& omega,int iter, int type_ssor = 2)
  {
    T1 temp(0);
    
    int ma = A.GetM();

#ifdef SELDON_CHECK_BOUNDS
    int na = A.GetN();
    if (na != ma)
      throw WrongDim("SOR", "Matrix must be squared.");
    
    if (ma != X.GetLength() || ma != B.GetLength())
      throw WrongDim("SOR", "Matrix and vector dimensions are incompatible.");
#endif
    

    Vector<T2,Storage2,Allocator2> Y(ma);
    Y.Zero();
    T0 ajj(1);
    int p; T0 val(0);
    // Let us consider the following splitting : A = D - L - U
    // D diagonal of A
    // L lower part of A
    // U upper part of A, A is symmetric, so L = U^t
    
    // Forward sweep.
    // (D/omega - L) X^{n+1/2} = (U + (1-omega)/omega D) X^n + B
    if (type_ssor % 2 == 0)
      for (int i = 0; i < iter; i++)
	{
	  for (int j = 0; j < ma; j++)
	    {
	      // First we do X = (U + (1-omega)/omega D) X + B.
	      temp = T1(0);
	      ajj = T0(0);
	      for (int k = 0; k < A.GetRowSize(j); k++)
		{
		  p = A.Index(j,k);
		  val = A.Value(j,k);
		  if (p == j)
		    ajj += val;
		  else
		    temp += val * X(p);
		}
	      
	      temp = B(j) - temp;
	      X(j) = (T2(1) - omega) / omega * ajj * X(j) + temp;
	    }
	  
	  for (int j = 0; j < ma; j++)
	    {
	      ajj = T0(0);
	      // Then we solve (D/omega - L) X = X.
	      for (int k = 0; k < A.GetRowSize(j); k++)
		{
		  p = A.Index(j,k);
		  val = A.Value(j,k);
		  if (p == j)
		    ajj += val;
		}
	      X(j) *= omega / ajj;
	      for (int k = 0 ; k < A.GetRowSize(j); k++)
		{
		  p = A.Index(j,k);
		  val = A.Value(j,k);
		  if (p != j)
		    X(p) -= val*X(j);
		}
	    }
	}
    

    // Backward sweep.
    // (D/omega - U) X^{n+1} = (L + (1-omega)/omega D) X^{n+1/2} + B
    if (type_ssor % 3 == 0)
      for (int i = 0; i < iter; i++)
	{
	  Y.Zero();
	  for (int j = 0; j < ma; j++)
	    {
	      ajj = T0(0);
	      // Then we compute X = (L + (1-omega)/omega D) X + B.
	      for (int k = 0; k < A.GetRowSize(j); k++)
		{
		  p = A.Index(j,k);
		  val = A.Value(j,k);
		  if (p == j)
		    ajj += val;
		  else
		    Y(p) += val*X(j);
		}
	      
	      X(j) = (T2(1) - omega) / omega * ajj * X(j) + B(j) - Y(j);
	    }
	  
	  for (int j = ma-1; j >= 0; j--)
	    {
	      temp = T1(0);
	      ajj = T0(0);
	      // Then we solve (D/omega - U) X = X.
	      for (int k = 0; k < A.GetRowSize(j); k++)
		{
		  p = A.Index(j,k);
		  val = A.Value(j,k);
		  if (p == j)
		    ajj += val;
		  else
		    temp += val * X(p);
		}
	      X(j) = (X(j) - temp) * omega / ajj;
	    }
	}
    
  }
  
  
  //! Successive overrelaxation.
  /*!
    Solving A X = B by using S.O.R algorithm.
    omega is the relaxation parameter, iter the number of iterations.
    type_ssor = 2 forward sweep
    type_ssor = 3 backward sweep
    type_ssor = 0 forward and backward sweep
  */
  template <class T0, class Prop0, class Allocator0,
	    class T1, class Storage1, class Allocator1,
	    class T2, class Storage2, class Allocator2, class T3>
  void SOR(const Matrix<T0, Prop0, RowComplexSparse, Allocator0>& A,
	   Vector<complex<T2>, Storage2, Allocator2>& X,
	   const Vector<complex<T1>, Storage1, Allocator1>& B,
	   const T3& omega, int iter, int type_ssor = 2)
  {
    complex<T1> temp(0);
    
    int ma = A.GetM();
    
#ifdef SELDON_CHECK_BOUNDS
    int na = A.GetN();
    if (na != ma)
      throw WrongDim("SOR", "Matrix must be squared.");
    
    if (ma != X.GetLength() || ma != B.GetLength())
      throw WrongDim("SOR", "Matrix and vector dimensions are incompatible.");
#endif
    
    int* ptr_real = A.GetRealPtr();
    int* ptr_imag = A.GetImagPtr();
    int* ind_real = A.GetRealInd();
    int* ind_imag = A.GetImagInd();
    typename Matrix<T0, Prop0, RowComplexSparse, Allocator0>::pointer
      data_real = A.GetRealData();
    typename Matrix<T0, Prop0, RowComplexSparse, Allocator0>::pointer
      data_imag = A.GetImagData();
    complex<T0> ajj(1);
    
    if (type_ssor % 2 == 0)
      for (int i = 0; i < iter; i++)
	for (int j = 0; j < ma; j++)
	  {
	    temp = complex<T1>(0);
	    ajj = A(j,j);
	    for (int k = ptr_real[j]; k < ptr_real[j+1]; k++)
	      temp += data_real[k] * X(ind_real[k]);
	    
	    for (int k = ptr_imag[j]; k < ptr_imag[j+1]; k++)
	      temp += complex<T1>(0, data_imag[k]) * X(ind_imag[k]);
	    
	    temp = B(j) - temp + ajj*X(j);
	    X(j) = (T2(1) - omega) * X(j) + omega * temp / ajj;
	  }
	
    if (type_ssor % 3 == 0)
      for (int i = 0; i < iter; i++)
	for (int j = ma-1; j >= 0; j--)
	  {
	    temp = complex<T1>(0);
	    ajj = A(j,j);
	    for (int k = ptr_real[j]; k < ptr_real[j+1]; k++)
	      temp += data_real[k] * X(ind_real[k]);
	    
	    for (int k = ptr_imag[j]; k < ptr_imag[j+1]; k++)
	      temp += complex<T1>(0, data_imag[k]) * X(ind_imag[k]);
	    
	    temp = B(j) - temp + ajj * X(j);
	    X(j) = (T2(1) - omega) * X(j) + omega * temp / ajj;
	  }
  }
  
  
  //! Successive overrelaxation.
  /*!
    Solving A X = B by using S.O.R algorithm.
    omega is the relaxation parameter, iter the number of iterations.
    type_ssor = 2 forward sweep
    type_ssor = 3 backward sweep
    type_ssor = 0 forward and backward sweep
  */
  template <class T0, class Prop0, class Allocator0,
	    class T1, class Storage1, class Allocator1,
	    class T2, class Storage2, class Allocator2, class T3>
  void SOR(const Matrix<T0, Prop0, ArrayRowComplexSparse, Allocator0>& A,
	   Vector<complex<T2>, Storage2, Allocator2>& X,
	   const Vector<complex<T1>, Storage1, Allocator1>& B,
	   const T3& omega, int iter, int type_ssor = 2)
  {
    complex<T1> temp(0);
    
    int ma = A.GetM();
    
#ifdef SELDON_CHECK_BOUNDS
    int na = A.GetN();
    if (na != ma)
      throw WrongDim("SOR", "Matrix must be squared.");
    
    if (ma != X.GetLength() || ma != B.GetLength())
      throw WrongDim("SOR", "Matrix and vector dimensions are incompatible.");
#endif
    
    complex<T0> ajj(1);
    
    if (type_ssor%2 == 0)
      for (int i = 0; i < iter; i++)
	{
	  for (int j = 0; j < ma; j++)
	    {
	      temp = complex<T1>(0);
	      ajj = complex<T0>(0);
	      for (int k = 0; k < A.GetRealRowSize(j); k++)
		{
		  temp += A.ValueReal(j,k) * X(A.IndexReal(j,k));
		  if (A.IndexReal(j,k) == j)
		    ajj += complex<T0>(A.ValueReal(j,k), 0);
		}
	      for (int k = 0; k < A.GetImagRowSize(j); k++)
		{
		  temp += complex<T0>(0,A.ValueImag(j,k))
		    * X(A.IndexImag(j,k));
		  if (A.IndexImag(j,k) == j)
		    ajj += complex<T0>(0, A.ValueImag(j,k));
		}
	      
	      temp = B(j) - temp + ajj * X(j);
	      X(j) = (T2(1) - omega) * X(j) + omega * temp / ajj;
	    }
	}
    
    if (type_ssor % 3 == 0)
      for (int i = 0; i < iter; i++)
	for (int j = ma-1; j >= 0; j--)
	  {
	    temp = complex<T1>(0);
	    ajj = complex<T0>(0);
	    for (int k = 0; k < A.GetRealRowSize(j); k++)
	      {
		temp += A.ValueReal(j,k) * X(A.IndexReal(j,k));
		if (A.IndexReal(j,k) == j)
		  ajj += complex<T0>(A.ValueReal(j,k), 0);
	      }
	    for (int k = 0; k < A.GetImagRowSize(j); k++)
	      {
		temp += complex<T0>(0, A.ValueImag(j,k))
		  * X(A.IndexImag(j,k));
		if (A.IndexImag(j,k) == j)
		  ajj += complex<T0>(0, A.ValueImag(j,k));
	      }
	    
	    temp = B(j) - temp + ajj * X(j);
	    X(j) = (T2(1) - omega) * X(j) + omega * temp / ajj;
	  }
  }
  
  
  //! Successive overrelaxation.
  /*!
    Solving A X = B by using S.O.R algorithm.
    omega is the relaxation parameter, iter the number of iterations.
    type_ssor = 2 forward sweep
    type_ssor = 3 backward sweep
    type_ssor = 0 forward and backward sweep
  */
  template <class T0, class Prop0, class Allocator0,
	    class T1, class Storage1, class Allocator1,
	    class T2, class Storage2, class Allocator2, class T3>
  void SOR(const Matrix<T0, Prop0, RowSymComplexSparse, Allocator0>& A,
	   Vector<complex<T2>, Storage2, Allocator2>& X,
	   const Vector<complex<T1>, Storage1, Allocator1>& B,
	   const T3& omega, int iter, int type_ssor = 2)
  {
    complex<T1> temp(0);
    
    int ma = A.GetM();
    
#ifdef SELDON_CHECK_BOUNDS
    int na = A.GetN();
    if (na != ma)
      throw WrongDim("SOR", "Matrix must be squared.");
    
    if (ma != X.GetLength() || ma != B.GetLength())
      throw WrongDim("SOR", "Matrix and vector dimensions are incompatible.");
#endif
    
    int* ptr_real = A.GetRealPtr();
    int* ptr_imag = A.GetImagPtr();
    int* ind_real = A.GetRealInd();
    int* ind_imag = A.GetImagInd();
    T0* data_real = A.GetRealData();
    T0* data_imag = A.GetImagData();
    
    Vector<complex<T2>, Storage2, Allocator2> Y(ma);
    Y.Zero();
    complex<T0> ajj(1);
    int p;
    complex<T0> val(0);

    // Let us consider the following splitting : A = D - L - U
    // D diagonal of A
    // L lower part of A
    // U upper part of A, A is symmetric, so L = U^t
    // forward sweep
    // (D/omega - L) X^{n+1/2} = (U + (1-omega)/omega D) X^n + B
    if (type_ssor % 2 == 0)
      for (int i = 0; i < iter; i++)
	{
	  for (int j = 0; j < ma; j++)
	    {
	      // first we do X = (U + (1-omega)/omega D) X + B
	      temp = complex<T1>(0);
	      ajj = complex<T0>(0);
	      for (int k = ptr_real[j]; k < ptr_real[j+1]; k++)
		{
		  p = ind_real[k];
		  val = complex<T0>(data_real[k], 0);
		  if (p == j)
		    ajj += val;
		  else
		    temp += val * X(p);
		}
	      for (int k = ptr_imag[j]; k < ptr_imag[j+1]; k++)
		{
		  p = ind_imag[k];
		  val = complex<T0>(0, data_imag[k]);
		  if (p == j)
		    ajj += val;
		  else
		    temp += val * X(p);
		}
	      
	      temp = B(j) - temp;
	      X(j) = (T2(1) - omega) / omega * ajj * X(j) + temp;
	    }
	
	  for (int j = 0; j < ma; j++)
	    {
	      ajj = complex<T0>(0);
	      // Then we solve (D/omega - L) X = X.
	      for (int k = ptr_real[j]; k < ptr_real[j+1]; k++)
		{
		  p = ind_real[k];
		  val = complex<T0>(data_real[k], 0);
		  if (p == j)
		    ajj += val;
		}
	      
	      for (int k = ptr_imag[j]; k < ptr_imag[j+1]; k++)
		{
		  p = ind_imag[k];
		  val = complex<T0>(0, data_imag[k]);
		  if (p == j)
		    ajj += val;
		}
	      X(j) *= omega / ajj;
	      
	      for (int k = ptr_real[j]; k < ptr_real[j+1]; k++)
		{
		  p = ind_real[k];
		  val = complex<T0>(data_real[k], 0);
		  if (p != j)
		    X(p) -= val*X(j);
		}
	      
	      for (int k = ptr_imag[j]; k < ptr_imag[j+1]; k++)
		{
		  p = ind_imag[k];
		  val = complex<T0>(0, data_imag[k]);
		  if (p != j)
		    X(p) -= val*X(j);
		}
	    }
	}
    
    // Backward sweep.
    // (D/omega - U) X^{n+1} = (L + (1-omega)/omega D) X^{n+1/2} + B
    if (type_ssor % 3 == 0)
      for (int i = 0; i < iter; i++)
	{
	  Y.Zero();
	  
	  for (int j = 0; j < ma; j++)
	    {
	      ajj = complex<T0>(0);
	      // Then we compute X = (L + (1-omega)/omega D) X + B.
	      for (int k = ptr_real[j]; k < ptr_real[j+1]; k++)
		{
		  p = ind_real[k];
		  val = complex<T0>(data_real[k], 0);
		  if (p == j)
		    ajj += val;
		  else
		    Y(p) += val * X(j);
		}
	      
	      for (int k = ptr_imag[j]; k < ptr_imag[j+1]; k++)
		{
		  p = ind_imag[k];
		  val = complex<T0>(0, data_imag[k]);
		  if (p == j)
		    ajj += val;
		  else
		    Y(p) += val * X(j);
		}
	      X(j) = (T2(1) - omega) / omega * ajj * X(j) + B(j) - Y(j);
	    }
	
	  for (int j = (ma-1); j >= 0; j--)
	    {
	      temp = complex<T1>(0);
	      ajj = complex<T0>(0);
	      // Then we solve (D/omega - U) X = X.
	      for (int k = ptr_real[j]; k < ptr_real[j+1]; k++)
		{
		  p = ind_real[k];
		  val = complex<T0>(data_real[k], 0);
		  if (p == j)
		    ajj += val;
		  else
		    temp += val * X(p);
		}
	      
	      for (int k = ptr_imag[j]; k < ptr_imag[j+1]; k++)
		{
		  p = ind_imag[k];
		  val = complex<T0>(0, data_imag[k]);
		  if (p == j)
		    ajj += val;
		  else
		    temp += val * X(p);
		}
	      X(j) = (X(j) - temp) * omega / ajj;
	    }
	}
  }
  
  
  //! Successive overrelaxation.
  /*!
    Solving A X = B by using S.O.R algorithm.
    omega is the relaxation parameter, iter the number of iterations.
    type_ssor = 2 forward sweep
    type_ssor = 3 backward sweep
    type_ssor = 0 forward and backward sweep
  */
  template <class T0, class Prop0, class Allocator0,
	    class T1, class Storage1, class Allocator1,
	    class T2, class Storage2, class Allocator2, class T3>
  void SOR(const Matrix<T0, Prop0, ArrayRowSymComplexSparse, Allocator0>& A,
	   Vector<complex<T2>, Storage2, Allocator2>& X,
	   const Vector<complex<T1>, Storage1, Allocator1>& B,
	   const T3& omega, int iter, int type_ssor = 2)
  {
    complex<T1> temp(0);
    
    int ma = A.GetM();
    
#ifdef SELDON_CHECK_BOUNDS
    int na = A.GetN();
    if (na != ma)
      throw WrongDim("SOR", "Matrix must be squared.");
    
    if (ma != X.GetLength() || ma != B.GetLength())
      throw WrongDim("SOR", "Matrix and vector dimensions are incompatible.");
#endif
    
    Vector<complex<T2>, Storage2, Allocator2> Y(ma);
    Y.Zero();
    complex<T0> ajj(1);
    int p;
    complex<T0> val(0);
    // Let us consider the following splitting : A = D - L - U
    // D diagonal of A
    // L lower part of A
    // U upper part of A, A is symmetric, so L = U^t
    // forward sweep
    // (D/omega - L) X^{n+1/2} = (U + (1-omega)/omega D) X^n + B
    if (type_ssor % 2 == 0)
      for (int i = 0; i < iter; i++)
	{
	  for (int j = 0; j < ma; j++)
	    {
	      // First we do X = (U + (1-omega)/omega D) X + B
	      temp = complex<T1>(0);
	      ajj = complex<T0>(0);
	      for (int k = 0; k < A.GetRealRowSize(j); k++)
		{
		  p = A.IndexReal(j,k);
		  val = complex<T0>(A.ValueReal(j,k), 0);
		  if (p == j)
		    ajj += val;
		  else
		    temp += val * X(p);
		}
	      for (int k = 0; k < A.GetImagRowSize(j); k++)
		{
		  p = A.IndexImag(j,k);
		  val = complex<T0>(0, A.ValueImag(j,k));
		  if (p == j)
		    ajj += val;
		  else
		    temp += val * X(p);
		}
	      
	      temp = B(j) - temp;
	      X(j) = (T2(1) - omega) / omega * ajj * X(j) + temp;
	    }
	
	  for (int j = 0; j < ma; j++)
	    {
	      ajj = complex<T0>(0);
	      // Then we solve (D/omega - L) X = X.
	      for (int k = 0; k < A.GetRealRowSize(j); k++)
		{
		  p = A.IndexReal(j,k);
		  val = complex<T0>(A.ValueReal(j,k), 0);
		  if (p == j)
		    ajj += val;
		}
	      for (int k = 0; k < A.GetImagRowSize(j); k++)
		{
		  p = A.IndexImag(j,k);
		  val = complex<T0>(0, A.ValueImag(j,k));
		  if (p == j)
		    ajj += val;
		}
	      X(j) *= omega / ajj;
	      for (int k = 0; k < A.GetRealRowSize(j); k++)
		{
		  p = A.IndexReal(j,k);
		  val = complex<T0>(A.ValueReal(j,k), 0);
		  if (p != j)
		    X(p) -= val * X(j);
		}
	      for (int k = 0; k < A.GetImagRowSize(j); k++)
		{
		  p = A.IndexImag(j,k);
		  val = complex<T0>(0, A.ValueImag(j,k));
		  if (p != j)
		    X(p) -= val*X(j);
		}
	    }
	}
    
    // Backward sweep.
    // (D/omega - U) X^{n+1} = (L + (1-omega)/omega D) X^{n+1/2} + B
    if (type_ssor % 3 == 0)
      for (int i = 0; i < iter; i++)
	{
	  Y.Zero();
	  
	  for (int j = 0; j < ma; j++)
	    {
	      ajj = complex<T0>(0);
	      // Then we compute X = (L + (1-omega)/omega D) X + B.
	      for (int k = 0; k < A.GetRealRowSize(j); k++)
		{
		  p = A.IndexReal(j,k);
		  val = complex<T0>(A.ValueReal(j,k), 0);
		  if (p == j)
		    ajj += val;
		  else
		    Y(p) += val * X(j);
		}
	      for (int k = 0; k < A.GetImagRowSize(j); k++)
		{
		  p = A.IndexImag(j,k);
		  val = complex<T0>(0, A.ValueImag(j,k));
		  if (p == j)
		    ajj += val;
		  else
		    Y(p) += val * X(j);
		}
	      X(j) = (T2(1) - omega) / omega * ajj * X(j) + B(j) - Y(j);
	    }
	
	  for (int j = ma-1; j >= 0; j--)
	    {
	      temp = complex<T1>(0);
	      ajj = complex<T0>(0);
	      // Then we solve (D/omega - U) X = X.
	      for (int k = 0; k < A.GetRealRowSize(j); k++)
		{
		  p = A.IndexReal(j,k);
		  val = complex<T0>(A.ValueReal(j,k), 0);
		  if (p == j)
		    ajj += val;
		  else
		    temp += val * X(p);
		}
	      for (int k = 0; k < A.GetImagRowSize(j); k++)
		{
		  p = A.IndexImag(j,k);
		  val = complex<T0>(0, A.ValueImag(j,k));
		  if (p == j)
		    ajj += val;
		  else
		    temp += val * X(p);
		}
	      X(j) = (X(j) - temp) * omega / ajj;
	    }
	}
  }


} // end namespace

#define SELDON_FILE_RELAXATION_MATVECT_CXX
#endif