/*
 *  Work in this file is derived from code originally written by Hans-Peter Moser:
 *  http://www.mosismath.com/Basics/Basics.html
 *  http://www.mosismath.com/Matrix_Gauss/MatrixGauss.html
 */

namespace Connected.Components;

// Matrix equation solver using the Gaussian elimination algorithm
public class MatrixSolver
{
   public readonly Matrix m;
   public readonly int maxOrder;
   public bool calcError;

   public MatrixSolver(int size, Matrix mi)
   {
      maxOrder = size;
      m = mi;
   }

   void SwitchRows(int n)
   {
      double tempD;
      int i, j;
      for (i = n; i <= maxOrder - 2; i++)
      {
         for (j = 0; j <= maxOrder - 1; j++)
         {
            tempD = m.a[i, j];
            m.a[i, j] = m.a[i + 1, j];
            m.a[i + 1, j] = tempD;
         }
         tempD = m.y[i];
         m.y[i] = m.y[i + 1];
         m.y[i + 1] = tempD;
      }
   }

   public bool Eliminate()
   {
      int i, k, l;
      calcError = false;
      for (k = 0; k <= maxOrder - 2; k++)
      {
         for (i = k; i <= maxOrder - 2; i++)
         {
            if (Math.Abs(m.a[i + 1, i]) < 1e-8)
            {
               SwitchRows(i + 1);
            }
            if (m.a[i + 1, k] != 0.0)
            {
               for (l = k + 1; l <= maxOrder - 1; l++)
               {
                  if (!calcError)
                  {
                     m.a[i + 1, l] = m.a[i + 1, l] * m.a[k, k] - m.a[k, l] * m.a[i + 1, k];
                     if (m.a[i + 1, l] > 10E260)
                     {
                        m.a[i + 1, k] = 0;
                        calcError = true;
                     }
                  }
               }
               m.y[i + 1] = m.y[i + 1] * m.a[k, k] - m.y[k] * m.a[i + 1, k];
               m.a[i + 1, k] = 0;
            }
         }
      }
      return !calcError;
   }

   public void Solve()
   {
      int k, l;
      for (k = maxOrder - 1; k >= 0; k--)
      {
         for (l = maxOrder - 1; l >= k; l--)
         {
            m.y[k] = m.y[k] - m.x[l] * m.a[k, l];
         }
         if (m.a[k, k] != 0)
            m.x[k] = m.y[k] / m.a[k, k];
         else
            m.x[k] = 0;
      }
   }
}
