using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using MathNet.Numerics.LinearAlgebra; namespace MatrixCalculator { public partial class MainWindow : Form { private WorksheetFile worksheet = new WorksheetFile(); private List> matrices = new List>(); private Matrix matrixA, matrixB, matrixResult; #region Matrix properties private Matrix MatrixA { get { return matrixA; } set { matrixA = value; GridViewHelper.PutMatrix(matrixA, dataMatrixA); } } private Matrix MatrixB { get { return matrixB; } set { matrixB = value; GridViewHelper.PutMatrix(matrixB, dataMatrixB); } } private Matrix MatrixResult { get { return matrixResult; } set { matrixResult = value; GridViewHelper.PutMatrix(matrixResult, dataResult); } } private Matrix MatrixPreview { get { if (listMatrices.SelectedItems.Count == 1) return matrices[listMatrices.SelectedIndices[0]]; else return null; } set { GridViewHelper.PutMatrix(value, dataPreview); } } #endregion #region Constructor public MainWindow() { InitializeComponent(); ResetAll(); } #endregion #region User interface #region Menus private void menuFileNew_Click(object sender, EventArgs e) { if (!ShowSaveWarningDialog()) return; ResetAll(); worksheet = new WorksheetFile(); } private void menuFileOpen_Click(object sender, EventArgs e) { if (!ShowSaveWarningDialog()) return; // Open file dialog string file; if (!OpenDialog("Open worksheet...", MatrixCalculator.Properties.Resources.WorksheetFormatFilter, out file)) return; // Open the file DataTable data = new DataTable(); try { worksheet = new WorksheetFile(file); data = worksheet.Read(); } catch (Exception ex) { MessageBox.Show("Error:" + ex.Message, "Error!"); return; } // Load data ResetAll(); AddMatrices(data); } private void menuFileSave_Click(object sender, EventArgs e) { if (worksheet.FileName == "") { string file; if (!SaveDialog("Save worksheet...", MatrixCalculator.Properties.Resources.WorksheetFormatFilter, out file)) return; worksheet.FileName = file; } try { worksheet.Save(); } catch (Exception ex) { MessageBox.Show("Error: " + ex.Message, "Error!"); } } private void menuFileSaveAs_Click(object sender, EventArgs e) { string file; if (!SaveDialog("Save worksheet...", MatrixCalculator.Properties.Resources.WorksheetFormatFilter, out file)) return; try { worksheet.SaveAs(file); } catch (Exception ex) { MessageBox.Show("Error: " + ex.Message, "Error!"); } } private void menuFileImportCsv_Click(object sender, EventArgs e) { string file; if (!OpenDialog("Open CSV file...", MatrixCalculator.Properties.Resources.CsvFormatFilter, out file)) return; ImportCsvWindow wind = new ImportCsvWindow(); try { wind.LoadFile(file); } catch (Exception ex) { MessageBox.Show("Error: " + ex.Message, "Error!"); } if (wind.ShowDialog() != System.Windows.Forms.DialogResult.OK) return; AddMatrix(wind.Matrix, wind.MatrixName, "", true); } private void menuFileImportWorksheet_Click(object sender, EventArgs e) { string file; if (!OpenDialog("Open worksheet...", MatrixCalculator.Properties.Resources.WorksheetFormatFilter, out file)) return; ImportWorksheetWindow wind = new ImportWorksheetWindow(); try { wind.LoadFile(file); } catch (Exception ex) { MessageBox.Show("Error: " + ex.Message, "Error!"); } if (wind.ShowDialog() != System.Windows.Forms.DialogResult.OK) return; AddMatrices(wind.Data); } private void menuFilePreferences_Click(object sender, EventArgs e) { new SettingsWindow().ShowDialog(); } private void menuFileExit_Click(object sender, EventArgs e) { this.Close(); } private void menuMatrixNew_Click(object sender, EventArgs e) { Editor edit = new Editor(); edit.Text = "New matrix..."; if (edit.ShowDialog() != System.Windows.Forms.DialogResult.OK) return; AddMatrix(edit.Matrix, edit.MatrixName, edit.MatrixDescription, true); } private void menuMatrixEdit_Click(object sender, EventArgs e) { if (listMatrices.SelectedItems.Count != 1) return; string old_name, new_name, desc; Matrix matrix; // Set up variables Editor edit = new Editor(); edit.Text = "Edit matrix..."; old_name = edit.MatrixName = listMatrices.SelectedItems[0].Text; edit.MatrixDescription = listMatrices.SelectedItems[0].SubItems[1].Text; edit.Matrix = matrices[listMatrices.SelectedIndices[0]]; // Dialog if (edit.ShowDialog() != System.Windows.Forms.DialogResult.OK) return; // Update new stuff new_name = listMatrices.SelectedItems[0].Text = edit.MatrixName; desc = listMatrices.SelectedItems[0].SubItems[1].Text = edit.MatrixDescription; matrix = matrices[listMatrices.SelectedIndices[0]] = edit.Matrix; // Update in file worksheet.ModifyMatrix(old_name, new_name, CsvParser.ToCSV(matrix), desc); // Update preview listMatrices_SelectedIndexChanged(this, new EventArgs()); } private void menuMatrixDelete_Click(object sender, EventArgs e) { foreach (int i in listMatrices.SelectedIndices) { worksheet.DeleteMatrix(listMatrices.Items[i].Text); listMatrices.Items.RemoveAt(i); matrices.RemoveAt(i); } } private void menuMatrixDuplicate_Click(object sender, EventArgs e) { if (listMatrices.SelectedItems.Count != 1) return; // Set up variables Editor edit = new Editor(); edit.Text = "New matrix..."; edit.MatrixName = listMatrices.SelectedItems[0].Text; edit.MatrixDescription = listMatrices.SelectedItems[0].SubItems[1].Text; edit.Matrix = matrices[listMatrices.SelectedIndices[0]]; // Dialog if (edit.ShowDialog() != System.Windows.Forms.DialogResult.OK) return; // Update new stuff AddMatrix(edit.Matrix, edit.MatrixName, edit.MatrixDescription, true); } private void menuMatrixInsertA_Click(object sender, EventArgs e) { if (listMatrices.SelectedIndices.Count == 1) MatrixA = matrices[listMatrices.SelectedIndices[0]]; } private void menuMatrixInsertB_Click(object sender, EventArgs e) { if (listMatrices.SelectedIndices.Count == 1) MatrixB = matrices[listMatrices.SelectedIndices[0]]; } private void menuMatrixCopy_Click(object sender, EventArgs e) { if (listMatrices.SelectedIndices.Count != 1) return; string csv = CsvParser.ToCSV(matrices[listMatrices.SelectedIndices[0]]); Clipboard.SetText(csv, TextDataFormat.CommaSeparatedValue); } private void menuMatrixPaste_Click(object sender, EventArgs e) { // Get data string csv = ""; if (Clipboard.ContainsText(TextDataFormat.CommaSeparatedValue)) csv = Clipboard.GetText(TextDataFormat.CommaSeparatedValue); else return; // Put it in a matrix Editor edit = new Editor(); edit.Text = "Paste matrix..."; edit.Matrix = MatrixConverter.FromTable(CsvParser.GetTable(csv, ",;\t".ToArray())); edit.MatrixName = "Pasted matrix"; if (edit.ShowDialog() != System.Windows.Forms.DialogResult.OK) return; // Get data AddMatrix(edit.Matrix, edit.MatrixName, edit.MatrixDescription, true); } private void menuMatrixPasteSpecial_Click(object sender, EventArgs e) { // Get data string csv = ""; if (Clipboard.ContainsText(TextDataFormat.CommaSeparatedValue)) csv = Clipboard.GetText(TextDataFormat.CommaSeparatedValue); else if (Clipboard.ContainsText()) csv = Clipboard.GetText(); else return; // Use CSV importer ImportCsvWindow wind = new ImportCsvWindow(); wind.Csv = csv; if (wind.ShowDialog() != System.Windows.Forms.DialogResult.OK) return; AddMatrix(wind.Matrix, wind.MatrixName, "", true); } private void menuHelpAbout_Click(object sender, EventArgs e) { DynamicLink.Launcher.About(); } private void menuHelpHelp_Click(object sender, EventArgs e) { DynamicLink.Launcher.StartModule("Help", "matrixcalc"); } private void contextMenuDataAdd_Click(object sender, EventArgs e) { var item = sender as ToolStripMenuItem; var menu = item.Owner as ContextMenuStrip; var grid = menu.SourceControl as DataGridView; if (grid == null) return; // Create editor window Editor editwindow = new Editor(); editwindow.Text = "New matrix..."; editwindow.Matrix = GridViewHelper.GetMatrix(grid); if (editwindow.ShowDialog() == System.Windows.Forms.DialogResult.OK) AddMatrix(editwindow.Matrix, editwindow.MatrixName, editwindow.MatrixDescription, true); } private void contextMenuDataClear_Click(object sender, EventArgs e) { var item = sender as ToolStripMenuItem; var menu = item.Owner as ContextMenuStrip; var grid = menu.SourceControl as DataGridView; if (grid == null) return; if (grid == dataMatrixA) matrixA = null; else if (grid == dataMatrixB) matrixB = null; grid.Rows.Clear(); grid.Columns.Clear(); } private void contextMenuDataCopy_Click(object sender, EventArgs e) { var item = sender as ToolStripMenuItem; var menu = item.Owner as ContextMenuStrip; var grid = menu.SourceControl as DataGridView; if (grid == null) return; string csv = CsvParser.ToCSV(grid); Clipboard.SetText(csv, TextDataFormat.CommaSeparatedValue); } #endregion #region Calculator buttons private void buttonAdd_Click(object sender, EventArgs e) { if (!VerifyConditions(1, 1, 1, 1, 0, 0)) return; try { MatrixResult = matrixA + matrixB; } catch (Exception ex) { textOutput.Text = "Error: " + ex.Message; } } private void buttonSub_Click(object sender, EventArgs e) { if (!VerifyConditions(1, 1, 1, 1, 0, 0)) return; try { MatrixResult = matrixA - matrixB; } catch (Exception ex) { textOutput.Text = "Error: " + ex.Message; } } private void buttonMul_Click(object sender, EventArgs e) { if (!VerifyConditions(1, 1, 1, 0, 1, 0)) return; try { MatrixResult = matrixA * matrixB; } catch (Exception ex) { textOutput.Text = "Error: " + ex.Message; } } private void buttonAddNum_Click(object sender, EventArgs e) { if (!VerifyConditions(1, 0, 0, 0, 0, 0)) return; // Get a number NumericInput input = new NumericInput(NumericInput.NumberType.Real); if (input.ShowDialog() != System.Windows.Forms.DialogResult.OK) return; double number = input.NumberReal; // Now add it Matrix m = Matrix.Build.Dense(matrixA.RowCount, matrixA.ColumnCount, number); MatrixResult = matrixA + m; } private void buttonSubNum_Click(object sender, EventArgs e) { if (!VerifyConditions(1, 0, 0, 0, 0, 0)) return; // Get a number NumericInput input = new NumericInput(NumericInput.NumberType.Real); if (input.ShowDialog() != System.Windows.Forms.DialogResult.OK) return; double number = input.NumberReal; // Now add it Matrix m = Matrix.Build.Dense(matrixA.RowCount, matrixA.ColumnCount, number); MatrixResult = matrixA - m; } private void buttonMulNum_Click(object sender, EventArgs e) { if (!VerifyConditions(1, 0, 0, 0, 0, 0)) return; // Get a number NumericInput input = new NumericInput(NumericInput.NumberType.Real); if (input.ShowDialog() != System.Windows.Forms.DialogResult.OK) return; double number = input.NumberReal; // Now add it MatrixResult = matrixA * number; } private void buttonInv_Click(object sender, EventArgs e) { if (!VerifyConditions(1, 0, 0, 0, 0, 0)) return; try { MatrixResult = matrixA.Inverse(); } catch (Exception ex) { textOutput.Text = "Error: " + ex.Message; } } private void buttonPow_Click(object sender, EventArgs e) { if (!VerifyConditions(1, 0, 0, 0, 0, 0)) return; // Get a number NumericInput input = new NumericInput(NumericInput.NumberType.Integer); if (input.ShowDialog() != System.Windows.Forms.DialogResult.OK) return; int number = input.NumberInt; // Calculate try { MatrixResult = MatrixHelper.Power (matrixA, number); } catch (Exception ex) { textOutput.Text = "Error: " + ex.Message; } } private void buttonTransp_Click(object sender, EventArgs e) { if (!VerifyConditions(1, 0, 0, 0, 0, 0)) return; try { MatrixResult = matrixA.Transpose(); } catch (Exception ex) { textOutput.Text = "Error: " + ex.Message; } } private void buttonTrace_Click(object sender, EventArgs e) { if (!VerifyConditions(1, 0, 0, 0, 0, 0)) return; if (MatrixCalculator.Properties.Settings.Default.NumberResultAsMatrix) MatrixResult = Matrix.Build.Dense(1, 1, matrixA.Trace()); else textOutput.Text = MyDouble.String(matrixA.Trace()); } private void buttonRank_Click(object sender, EventArgs e) { if (!VerifyConditions(1, 0, 0, 0, 0, 0)) return; if (MatrixCalculator.Properties.Settings.Default.NumberResultAsMatrix) MatrixResult = Matrix.Build.Dense(1, 1, Convert.ToDouble(matrixA.Rank())); else textOutput.Text = matrixA.Rank().ToString(); } private void buttonDet_Click(object sender, EventArgs e) { if (!VerifyConditions(1, 0, 0, 0, 0, 1)) return; if (MatrixCalculator.Properties.Settings.Default.NumberResultAsMatrix) MatrixResult = Matrix.Build.Dense(1, 1, matrixA.Determinant()); else textOutput.Text = MyDouble.String(matrixA.Determinant()); } private void buttonMin_Click(object sender, EventArgs e) { if (!VerifyConditions(1, 0, 0, 0, 0, 0)) return; if (MatrixCalculator.Properties.Settings.Default.NumberResultAsMatrix) MatrixResult = Matrix.Build.Dense(1, 1, MatrixHelper.Min(matrixA)); else textOutput.Text = MyDouble.String(MatrixHelper.Min(matrixA)); } private void buttonMax_Click(object sender, EventArgs e) { if (!VerifyConditions(1, 0, 0, 0, 0, 0)) return; if (MatrixCalculator.Properties.Settings.Default.NumberResultAsMatrix) MatrixResult = Matrix.Build.Dense(1, 1, MatrixHelper.Max(matrixA)); else textOutput.Text = MyDouble.String(MatrixHelper.Max(matrixA)); } #endregion #region Drag & drop private void dataMatrix_DragEnter(object sender, DragEventArgs e) { if (e.Data.GetDataPresent(typeof(ListViewItem))) e.Effect = DragDropEffects.Move; else e.Effect = DragDropEffects.None; } private void dataMatrix_DragDrop(object sender, DragEventArgs e) { ListViewItem data = e.Data.GetData(typeof(ListViewItem)) as ListViewItem; var send = sender as DataGridView; if (data == null || send == null) return; if (send == dataMatrixA) MatrixA = matrices[data.Index]; else if (send == dataMatrixB) MatrixB = matrices[data.Index]; } private void listMatrices_DragOver(object sender, DragEventArgs e) { e.Effect = DragDropEffects.Move; } private void listMatrices_MouseDown(object sender, MouseEventArgs e) { if (listMatrices.SelectedItems.Count == 1 && e.Button == System.Windows.Forms.MouseButtons.Left) listMatrices.DoDragDrop(listMatrices.SelectedItems[0], DragDropEffects.Move); } #endregion #region List Matrices Selected Index Changed private void EnableMenus(int insA, int insB, int copy, int edit, int del, int dupl) { menuMatrixInsertA.Enabled = contextMenuListInsertA.Enabled = (insA != 0); menuMatrixInsertB.Enabled = contextMenuListInsertB.Enabled = (insB != 0); menuMatrixCopy.Enabled = contextMenuListCopy.Enabled = (copy != 0); menuMatrixEdit.Enabled = contextMenuListEdit.Enabled = (edit != 0); menuMatrixDelete.Enabled = contextMenuListDelete.Enabled = (del != 0); menuMatrixDuplicate.Enabled = contextMenuListDuplicate.Enabled = (dupl != 0); } private void listMatrices_SelectedIndexChanged(object sender, EventArgs e) { switch (listMatrices.SelectedIndices.Count) { case 0: EnableMenus(0, 0, 0, 0, 0, 0); break; case 1: this.labelPreviewName.Text = "Name: " + listMatrices.SelectedItems[0].Text; this.labelPreviewDesc.Text = "Description: " + listMatrices.SelectedItems[0].SubItems[1].Text; MatrixPreview = matrices[listMatrices.SelectedIndices[0]]; EnableMenus(1, 1, 1, 1, 1, 1); break; default: EnableMenus(0, 0, 0, 0, 1, 0); break; } } #endregion #region Painting protected override void OnPaintBackground(PaintEventArgs e) { DynamicLink.Controls.BackgroundGradient.Paint(e.Graphics, new Rectangle(-1, -1, this.Width, this.Height)); } #endregion #region Others private void MainWindow_FormClosing(object sender, FormClosingEventArgs e) { if (!ShowSaveWarningDialog()) e.Cancel = true; if (!e.Cancel) MatrixCalculator.Properties.Settings.Default.Save(); } private void textOutput_TextChanged(object sender, EventArgs e) { toolTip.SetToolTip(textOutput, textOutput.Text); } #endregion #endregion #region Additional routines private string MakeUnique(string name) { List probl = new List(); // Test name uniqueness bool unique = true; foreach (ListViewItem i in this.listMatrices.Items) { if (i.Text == name) unique = false; if (i.Text.StartsWith(name)) probl.Add(i.Text); } // If not unique, generate a unique name if (!unique) { int n = 0; while (probl.Contains(name + n.ToString()) && n < 10000) n++; name += n.ToString(); } return name; } private void AddMatrix(Matrix m, string name, string desc, bool inFile = false) { matrices.Add(m); name = MakeUnique(name); // Add it ListViewItem item = listMatrices.Items.Add(name); item.SubItems.Add(desc); // To file? if (inFile) worksheet.AddMatrix(name, CsvParser.ToCSV(m), desc); } private void AddMatrices(DataTable data, bool infile = false) { AddMatrices(data, infile, false, MatrixCalculator.Properties.Settings.Default.InternalCsvSeparators.ToArray()); } private void AddMatrices(DataTable data, bool infile, bool combine, params char[] separs) { foreach (DataRow i in data.Rows) { try { string name = i["name"].ToString(); string desc = i["description"].ToString(); string csv = i["csv"].ToString(); DataTable dt = CsvParser.GetTable(csv, separs, combine); AddMatrix(MatrixConverter.FromTable(dt), name, desc, infile); } catch (Exception e) { MessageBox.Show("Error: " + e.Message, "Error!"); return; } } } private void ResetAll() { matrices.Clear(); listMatrices.Items.Clear(); AddMatrix(Matrix.Build.Dense(7, 7, 0), "Zero", "Zero matrix"); AddMatrix(Matrix.Build.DenseIdentity(7, 7), "One", "Identity matrix"); } private bool ShowSaveWarningDialog() { // Nothing to do if (worksheet.IsQueueEmpty) return true; // Show dialog switch (MessageBox.Show("All the unsaved changes will be lost. Do you want to save the current worksheet?", "Save?", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question)) { case System.Windows.Forms.DialogResult.Cancel: return false; case System.Windows.Forms.DialogResult.Yes: menuFileSave_Click(this, new EventArgs()); break; case System.Windows.Forms.DialogResult.No: break; } return true; } private bool OpenDialog(string title, string filter, out string filename) { bool result; // Initialize dialog OpenFileDialog open = new OpenFileDialog(); open.Title = title; open.Filter = filter; // Show if (open.ShowDialog() == System.Windows.Forms.DialogResult.OK) result = true; else result = false; // Output filename = open.FileName; return result; } private bool SaveDialog(string title, string filter, out string filename) { bool result; // Initialize dialog SaveFileDialog save = new SaveFileDialog(); save.Title = title; save.Filter = filter; // Show if (save.ShowDialog() == System.Windows.Forms.DialogResult.OK) result = true; else result = false; // Output filename = save.FileName; return result; } #endregion #region Additional calculator routines bool VerifyConditions(int includeA, int includeB, int changeSpecial, int checkSum, int checkMul, int checkSq) { // Verify matrix A if (includeA != 0 && matrixA == null) { textOutput.Text = "Error: Missing matrix A!"; return false; } // Verify matrix B if (includeB != 0 && matrixB == null) { textOutput.Text = "Error: Missing matrix B!"; return false; } // Replace special matrix types (0 and identity) if (changeSpecial != 0) { // Matrix A if (MatrixHelper.Equal(matrixA, Matrix.Build.Dense(7, 7, 0))) { if (checkSum != 0) matrixA = Matrix.Build.Dense(matrixB.RowCount, matrixB.ColumnCount, 0.0); if (checkMul != 0) matrixA = Matrix.Build.Dense(matrixB.ColumnCount, matrixB.ColumnCount, 0.0); } if (MatrixHelper.Equal(matrixA, Matrix.Build.DenseIdentity(7, 7))) { if (checkSum != 0) matrixA = Matrix.Build.DenseIdentity(matrixB.RowCount, matrixB.ColumnCount); if (checkMul != 0) matrixA = Matrix.Build.DenseIdentity(matrixB.ColumnCount, matrixB.ColumnCount); } // Matrix B if (MatrixHelper.Equal(matrixB, Matrix.Build.Dense(7, 7, 0))) { if (checkSum != 0) matrixB = Matrix.Build.Dense(matrixA.RowCount, matrixA.ColumnCount, 0.0); if (checkMul != 0) matrixB = Matrix.Build.Dense(matrixA.ColumnCount, matrixA.ColumnCount, 0.0); } if (MatrixHelper.Equal(matrixB, Matrix.Build.DenseIdentity(7, 7))) { if (checkSum != 0) matrixB = Matrix.Build.DenseIdentity(matrixA.RowCount, matrixA.ColumnCount); if (checkMul != 0) matrixB = Matrix.Build.DenseIdentity(matrixA.ColumnCount, matrixA.ColumnCount); } } // If sum, make sure sizes match if (checkSum != 0 && (matrixA.RowCount != matrixB.RowCount || matrixA.ColumnCount != matrixB.ColumnCount)) { textOutput.Text = "Error: Incompatible matrices."; return false; } // If multiplication, check A's width = B's height if (checkMul != 0 && (matrixA.ColumnCount != matrixB.RowCount)) { textOutput.Text = "Error: Incompatible matrices."; return false; } // If matrix A must be square if (checkSq != 0 && (matrixA.ColumnCount != matrixA.RowCount)) { textOutput.Text = "Error: Invalid matrix."; return false; } return true; } #endregion } }