using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace GaussianElimination
{
    public partial class frm82GaussianElimination : Form
    {
        public frm82GaussianElimination()
        {
            InitializeComponent();
        }

        // Solve the system of equations.
        private void solveButton_Click(object sender, EventArgs e)
        {
            // Build the augmented matrix.
            // The values numRows and numCols are the number of rows
            // and columns in the matrix, not the augmented matrix.
            int numRows, numCols;
            double[,] arr = LoadArray(out numRows, out numCols);
            double[,] origArr = LoadArray(out numRows, out numCols);

            // Display the initial arrays.
            PrintArray(arr);
            PrintArray(origArr);

            // Solve.
            string result = "";
            switch (GaussianElimination(arr))
            {
                case GaussianResults.NoSolution:
                    result = "No solution";
                    break;
                case GaussianResults.NoUniqueSolution:
                    result = "No unique solution";
                    break;
                case GaussianResults.UniqueSolution:
                    // Display the results.
                    result = "       Values:";
                    for (int r = 0; r < numRows; r++)
                    {
                        result += "\r\nx" + r.ToString() + " = " +
                            arr[r, numCols + 1].ToString();
                    }

                    // Verify.
                    result += "\r\n    Check:";
                    for (int r = 0; r < numRows; r++)
                    {
                        double tmp = 0;
                        for (int c = 0; c < numCols; c++)
                        {
                            tmp += origArr[r, c] * arr[c, numCols + 1];
                        }
                        result += "\r\n" + tmp.ToString();
                    }

                    result = result.Substring("\r\n".Length + 1);
                    break;
            }
            resultsTextBox.Text = result;
        }

        // Possible results.
        private enum GaussianResults
        {
            UniqueSolution,
            NoUniqueSolution,
            NoSolution,
        }

        // Solve the system of equations.
        private GaussianResults GaussianElimination(double[,] arr)
        {
            const double tiny = 0.00001;
            GaussianResults result = GaussianResults.NoSolution;

            // Get the bounds of the original (non-augmented) array.
            int numRows = arr.GetUpperBound(0) + 1;
            int numCols = arr.GetUpperBound(1) - 1;

            // Zero out leading columns.
            for (int r = 0; r < numRows - 1; r++)
            {
                // Zero out all entries in column r after this row.
                // See if this row has a non-zero entry in column r.
                if (Math.Abs(arr[r, r]) < tiny)
                {
                    // Too close to zero. Try to swap with a later row.
                    for (int r2 = r + 1; r2 < numRows; r2++)
                    {
                        if (Math.Abs(arr[r2, r]) > tiny)
                        {
                            // This row will work. Swap them.
                            for (int c = 0; c <= numCols; c++)
                            {
                                double tmp = arr[r, c];
                                arr[r, c] = arr[r2, c];
                                arr[r2, c] = tmp;
                            }
                            break;
                        }
                    }
                }

                // If this row has a non-zero entry in column r, use it.
                if (Math.Abs(arr[r, r]) > tiny)
                {
                    // Zero out this column in later rows.
                    for (int r2 = r + 1; r2 < numRows; r2++)
                    {
                        double factor = -arr[r2, r] / arr[r, r];
                        for (int c = r; c <= numCols; c++)
                        {
                            arr[r2, c] = arr[r2, c] + factor * arr[r, c];
                        }
                    }
                }
            }

            // Display the (hopefully) upper-triangular array.
            PrintArray(arr);

            // See if we have a solution.
            if (arr[numRows - 1, numCols - 1] == 0)
            {
                // We have no solution.
                // See if all of the entries in this row are 0.
                bool allZeros = true;
                for (int c = 0; c <= numCols + 1; c++)
                {
                    if (arr[numRows - 1, c] != 0)
                    {
                        allZeros = false;
                        break;
                    }
                }
                if (allZeros) result = GaussianResults.NoUniqueSolution;
                else result = GaussianResults.NoSolution;
            }
            else
            {
                result = GaussianResults.UniqueSolution;

                // Backsolve.
                for (int r = numRows - 1; r >= 0; r--)
                {
                    double tmp = arr[r, numCols];
                    for (int r2 = r + 1; r2 < numRows; r2++)
                    {
                        tmp -= arr[r, r2] * arr[r2, numCols + 1];
                    }
                    arr[r, numCols + 1] = tmp / arr[r, r];
                }
            }
            return result;
        }

        // Load the augmented array.
        // Column numCols holds the result values.
        // Column numCols + 1 will hold the variables' final values after backsolving.
        private double[,] LoadArray(out int numRows, out int numCols)
        {
            // Build the augmented matrix.
            string[] valueRows = valuesTextBox.Text.Split(
                new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
            string[] coefRows = coefficientsTextBox.Text.Split(
                new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
            string[] oneRow = coefRows[0].Split(
                new string[] { " " }, StringSplitOptions.RemoveEmptyEntries);
            numRows = coefRows.GetUpperBound(0) + 1;
            numCols = oneRow.GetUpperBound(0) + 1;
            double[,] arr = new double[numRows, numCols + 2];
            for (int r = 0; r < numRows; r++)
            {
                oneRow = coefRows[r].Split(
                    new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                for (int c = 0; c < numCols; c++)
                {
                    arr[r, c] = double.Parse(oneRow[c]);
                }
                arr[r, numCols] = double.Parse(valueRows[r]);
            }

            return arr;
        }

        // Display the array's values in the Console window.
        private void PrintArray(double[,] arr)
        {
            for (int r = arr.GetLowerBound(0); r <= arr.GetUpperBound(0); r++)
            {
                for (int c = arr.GetLowerBound(1); c <= arr.GetUpperBound(1); c++)
                {
                    Console.Write(arr[r, c] + "\t");
                }
                Console.WriteLine("");
            }
            Console.WriteLine("");
        }


        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.coefficientsTextBox = new System.Windows.Forms.TextBox();
            this.resultsTextBox = new System.Windows.Forms.TextBox();
            this.label2 = new System.Windows.Forms.Label();
            this.label1 = new System.Windows.Forms.Label();
            this.valuesTextBox = new System.Windows.Forms.TextBox();
            this.solveButton = new System.Windows.Forms.Button();
            this.SuspendLayout();
            // 
            // coefficientsTextBox
            // 
            this.coefficientsTextBox.AcceptsReturn = true;
            this.coefficientsTextBox.BackColor = System.Drawing.SystemColors.Window;
            this.coefficientsTextBox.Cursor = System.Windows.Forms.Cursors.IBeam;
            this.coefficientsTextBox.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
            this.coefficientsTextBox.ForeColor = System.Drawing.SystemColors.WindowText;
            this.coefficientsTextBox.Location = new System.Drawing.Point(12, 12);
            this.coefficientsTextBox.MaxLength = 0;
            this.coefficientsTextBox.Multiline = true;
            this.coefficientsTextBox.Name = "coefficientsTextBox";
            this.coefficientsTextBox.RightToLeft = System.Windows.Forms.RightToLeft.No;
            this.coefficientsTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
            this.coefficientsTextBox.Size = new System.Drawing.Size(174, 81);
            this.coefficientsTextBox.TabIndex = 12;
            this.coefficientsTextBox.Text = "   1   1   1  1  1\r\n  32  16   8  4  2\r\n 243  81  27  9  3\r\n1024 256  64 16  4\r\n3" +
    "125 625 125 25  5";
            // 
            // resultsTextBox
            // 
            this.resultsTextBox.AcceptsReturn = true;
            this.resultsTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
            | System.Windows.Forms.AnchorStyles.Left) 
            | System.Windows.Forms.AnchorStyles.Right)));
            this.resultsTextBox.BackColor = System.Drawing.SystemColors.Window;
            this.resultsTextBox.Cursor = System.Windows.Forms.Cursors.IBeam;
            this.resultsTextBox.Font = new System.Drawing.Font("Courier New", 8.25F);
            this.resultsTextBox.ForeColor = System.Drawing.SystemColors.WindowText;
            this.resultsTextBox.Location = new System.Drawing.Point(12, 139);
            this.resultsTextBox.MaxLength = 0;
            this.resultsTextBox.Multiline = true;
            this.resultsTextBox.Name = "resultsTextBox";
            this.resultsTextBox.RightToLeft = System.Windows.Forms.RightToLeft.No;
            this.resultsTextBox.Size = new System.Drawing.Size(300, 180);
            this.resultsTextBox.TabIndex = 17;
            // 
            // label2
            // 
            this.label2.BackColor = System.Drawing.SystemColors.Control;
            this.label2.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
            this.label2.Cursor = System.Windows.Forms.Cursors.Default;
            this.label2.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
            this.label2.ForeColor = System.Drawing.SystemColors.ControlText;
            this.label2.Location = new System.Drawing.Point(192, 12);
            this.label2.Margin = new System.Windows.Forms.Padding(3);
            this.label2.Name = "label2";
            this.label2.RightToLeft = System.Windows.Forms.RightToLeft.No;
            this.label2.Size = new System.Drawing.Size(34, 81);
            this.label2.TabIndex = 14;
            this.label2.Text = "x0\r\nx1\r\nx2\r\nx3\r\n...";
            this.label2.TextAlign = System.Drawing.ContentAlignment.TopCenter;
            // 
            // label1
            // 
            this.label1.BackColor = System.Drawing.Color.Transparent;
            this.label1.Cursor = System.Windows.Forms.Cursors.Default;
            this.label1.Font = new System.Drawing.Font("Arial", 8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
            this.label1.ForeColor = System.Drawing.SystemColors.ControlText;
            this.label1.Location = new System.Drawing.Point(232, 12);
            this.label1.Name = "label1";
            this.label1.RightToLeft = System.Windows.Forms.RightToLeft.No;
            this.label1.Size = new System.Drawing.Size(14, 81);
            this.label1.TabIndex = 15;
            this.label1.Text = "=";
            this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
            // 
            // valuesTextBox
            // 
            this.valuesTextBox.AcceptsReturn = true;
            this.valuesTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
            | System.Windows.Forms.AnchorStyles.Right)));
            this.valuesTextBox.BackColor = System.Drawing.SystemColors.Window;
            this.valuesTextBox.Cursor = System.Windows.Forms.Cursors.IBeam;
            this.valuesTextBox.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
            this.valuesTextBox.ForeColor = System.Drawing.SystemColors.WindowText;
            this.valuesTextBox.Location = new System.Drawing.Point(252, 12);
            this.valuesTextBox.MaxLength = 0;
            this.valuesTextBox.Multiline = true;
            this.valuesTextBox.Name = "valuesTextBox";
            this.valuesTextBox.RightToLeft = System.Windows.Forms.RightToLeft.No;
            this.valuesTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
            this.valuesTextBox.Size = new System.Drawing.Size(60, 81);
            this.valuesTextBox.TabIndex = 13;
            this.valuesTextBox.Text = "1\r\n-1\r\n8\r\n-56\r\n569";
            this.valuesTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Right;
            // 
            // solveButton
            // 
            this.solveButton.Anchor = System.Windows.Forms.AnchorStyles.Top;
            this.solveButton.Location = new System.Drawing.Point(123, 99);
            this.solveButton.Name = "solveButton";
            this.solveButton.Size = new System.Drawing.Size(79, 34);
            this.solveButton.TabIndex = 17;
            this.solveButton.Text = "Solve";
            this.solveButton.UseVisualStyleBackColor = true;
            this.solveButton.Click += new System.EventHandler(this.solveButton_Click);
            // 
            // frm82GaussianElimination
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(324, 331);
            this.Controls.Add(this.resultsTextBox);
            this.Controls.Add(this.solveButton);
            this.Controls.Add(this.coefficientsTextBox);
            this.Controls.Add(this.label1);
            this.Controls.Add(this.label2);
            this.Controls.Add(this.valuesTextBox);
            this.Name = "frm82GaussianElimination";
            this.Text = "GaussianElimination";
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        public System.Windows.Forms.TextBox coefficientsTextBox;
        public System.Windows.Forms.TextBox resultsTextBox;
        public System.Windows.Forms.Label label2;
        public System.Windows.Forms.Label label1;
        public System.Windows.Forms.TextBox valuesTextBox;
        private System.Windows.Forms.Button solveButton;
    }
}