using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace _04_2_Gauss
{

  public enum MetodaWyboru { Bez, Czesciowy, Pelny }

  public class Gauss
  {
    public double[,] A;
    public double[] b;
    public int n;
    public MetodaWyboru Wybor = MetodaWyboru.Bez;
    private int[] sorter;

    public void PostProste()
    {
      n = b.Length;
      sorter = new int[n];
      for (int i = 0; i < n; i++) sorter[i] = i;

      for (int i = 1; i < n; i++)
      {
        if (Wybor == MetodaWyboru.Czesciowy)
          WyborCzesciowy(i - 1);
        else if (Wybor == MetodaWyboru.Pelny)
          WyborPelny(i - 1);

        for (int k = i; k < n; k++)
        {
          double m = A[k, i - 1] / A[i - 1, i - 1];
          for (int j = 0; j < n; j++)
            A[k, j] = A[k, j] - A[i - 1, j] * m;
          b[k] = b[k] - b[i - 1] * m;
        }
      }
    }



    public double[] PostOdwrotne()
    {
      double[] x = new double[n];
      for (int i = n - 1; i >= 0; i--)
      {
        double u = b[i];
        for (int j = n - 1; j > i; j--)
          u = u - A[i, j] * x[j];
        x[i] = u / A[i, i];
      }
      if (Wybor == MetodaWyboru.Pelny)
        PrzywracanieKolejnosci(x);
      return x;
    }


    public double[] Oblicz()
    {
      PostProste();
      return PostOdwrotne();
    }


    private void WyborCzesciowy(int i0)
    {
      int imax = i0;
      for (int i = i0 + 1; i < n; i++)
      {
        if (Math.Abs(A[i, i0]) > Math.Abs(A[imax, i0]))
          imax = i;
      }
      if (imax != i0)
      {
        double g;
        for (int j = 0; j < n; j++)
        {
          g = A[i0, j];
          A[i0, j] = A[imax, j];
          A[imax, j] = g;
        }
        g = b[i0];
        b[i0] = b[imax];
        b[imax] = g;
      }
    }

    private void WyborPelny(int i0)
    {
      //szukanie elementu o maksymalnym module
      //w kwadratowym sektorze od A[i0, i0] do A[n-1, n-1]
      int maxR = i0;
      int maxC = i0;
      for (int i = i0; i < n; i++)
        for (int j = i0; j < n; j++)
          if (Math.Abs(A[i, j]) > Math.Abs(A[maxR, maxC]))
          {
            maxR = i;
            maxC = j;
          }
      //zamiana miejscami wiersza i0-tego z wierszem maxR-tym
      //w macierzy A ...
      double tmp;
      if (maxR != i0)
      {
        for (int j = 0; j < n; j++)
        {
          tmp = A[maxR, j];
          A[maxR, j] = A[i0, j];
          A[i0, j] = tmp;
        }
        tmp = b[maxR];
        b[maxR] = b[i0];
        b[i0] = tmp;
      }
      //... oraz w wektorze b.
      if (maxC != i0)
      {
        for (int i = 0; i < n; i++)
        {
          tmp = A[i, maxC];
          A[i, maxC] = A[i, i0];
          A[i, i0] = tmp;
        }
        int t = sorter[maxC];
        sorter[maxC] = sorter[i0];
        sorter[i0] = t;
      }
    }

    private void PrzywracanieKolejnosci(double[] x)
    {
      double d;
      int m, mi;
      for (int i = 0; i < n - 1; i++)
      {
        m = i;
        while (sorter[m] != i) m++;
        if (m != i)
        {
          d = x[i];
          x[i] = x[m];
          x[m] = d;

          mi = sorter[i];
          sorter[i] = sorter[m];
          sorter[m] = mi;
        }
      }
    }

  }
}
