From f98b585c6254584bf5cccb405fd4a0221a873014 Mon Sep 17 00:00:00 2001
From: Piyush Katyal <109459034+piyushk77@users.noreply.github.com>
Date: Fri, 6 Oct 2023 05:39:51 +0530
Subject: [PATCH 1/6] feat: add row echelon matrix algorithm

---
 Maths/RowEchelon.js | 138 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 138 insertions(+)
 create mode 100644 Maths/RowEchelon.js

diff --git a/Maths/RowEchelon.js b/Maths/RowEchelon.js
new file mode 100644
index 0000000000..6203edbaf3
--- /dev/null
+++ b/Maths/RowEchelon.js
@@ -0,0 +1,138 @@
+/**
+ * Given a two dimensional matrix, find its row echelon form.
+ *
+ * For more info: https://en.wikipedia.org/wiki/Row_echelon_form
+ *
+ * @param {number[[]]} matrix - Two dimensional array of rational numbers.
+ * @returns {number[[]]} - Two dimensional array of rational numbers (row echelon form).
+ *
+ * @example
+ * const matrix = [
+ *                          [2,3,4,5,7],
+ *                          [9,8,4,0,9],
+ *                          [5,7,4,3,9],
+ *                          [3,4,0,2,1]
+ *                ]
+ *
+ * const result = rowEchelon(matrix)
+ *
+ * // The function returns the corresponding row echelon form:
+ * // result:
+ * //             [
+ * //                       [1,  1.5,  2,        2.5,      3.5],
+ * //                       [0,  1,    2.54545,  4.09091,  4.09091],
+ * //                       [0,  0,    1,        1.57692,  1.36539],
+ * //                       [0,  0,    0,        1,        -0.25]
+ * //             ]
+ */
+
+const isMatrixValid = (matrix) => {
+  let numRows = matrix.length
+  let numCols = matrix[0].length
+  for (let i = 0; i < numRows; i++) {
+    if (numCols !== matrix[i].length) {
+      return false
+    }
+  }
+  if (
+    !Array.isArray(matrix) ||
+    matrix.length === 0 ||
+    !Array.isArray(matrix[0])
+  ) {
+    return false
+  }
+  return true
+}
+
+const checkNonZero = (currentRow, currentCol, matrix) => {
+  let numRows = matrix.length
+  for (let i = currentRow; i < numRows; i++) {
+    if (matrix[i][currentCol] !== 0) {
+      return true
+    }
+  }
+  return false
+}
+
+const swapRows = (currentRow, withRow, matrix) => {
+  let numCols = matrix[0].length
+  let tempValue = 0
+  for (let j = 0; j < numCols; j++) {
+    tempValue = matrix[currentRow][j]
+    matrix[currentRow][j] = matrix[withRow][j]
+    matrix[withRow][j] = tempValue
+  }
+}
+
+const selectPivot = (currentRow, currentCol, matrix) => {
+  let numRows = matrix.length
+  for (let i = currentRow; i < numRows; i++) {
+    if (matrix[i][currentCol] !== 0) {
+      swapRows(currentRow, i, matrix)
+      return
+    }
+  }
+}
+
+const scalarMultiplication = (currentRow, factor, matrix) => {
+  let numCols = matrix[0].length
+  for (let j = 0; j < numCols; j++) {
+    matrix[currentRow][j] *= factor
+  }
+}
+
+const subtractRow = (currentRow, fromRow, matrix) => {
+  let numCols = matrix[0].length
+  for (let j = 0; j < numCols; j++) {
+    matrix[fromRow][j] -= matrix[currentRow][j]
+  }
+}
+
+const formatResult = (matrix) => {
+  let precision = 5
+  let numRows = matrix.length
+  let numCols = matrix[0].length
+  for (let i = 0; i < numRows; i++) {
+    for (let j = 0; j < numCols; j++) {
+      matrix[i][j] = parseFloat(matrix[i][j].toFixed(precision))
+    }
+  }
+}
+
+const rowEchelon = (matrix) => {
+  if (isMatrixValid(matrix) === false) {
+    return 'Input is not a valid 2D matrix.'
+  }
+
+  let numRows = matrix.length
+  let numCols = matrix[0].length
+  let result = matrix
+
+  for (let i = 0, j = 0; i < numRows && j < numCols; ) {
+    if (checkNonZero(i, j, result) === false) {
+      j++
+      continue
+    }
+
+    selectPivot(i, j, result)
+    let factor = 1 / result[i][j]
+    scalarMultiplication(i, factor, result)
+
+    //..............make bottom elements zero...............
+    for (let x = i + 1; x < numRows; x++) {
+      factor = result[x][j]
+      if (factor === 0) {
+        continue
+      }
+      scalarMultiplication(i, factor, result)
+      subtractRow(i, x, result)
+      factor = 1 / factor
+      scalarMultiplication(i, factor, result)
+    }
+    formatResult(result)
+    i++
+  }
+  return result
+}
+
+export { rowEchelon }

From d9e7ffa07ae1a0d0f66f71f52e04d899f98d7a04 Mon Sep 17 00:00:00 2001
From: Piyush Katyal <109459034+piyushk77@users.noreply.github.com>
Date: Fri, 6 Oct 2023 05:45:25 +0530
Subject: [PATCH 2/6] test: add self-tests for row echelon algorithm

---
 Maths/test/RowEchelon.test.js | 84 +++++++++++++++++++++++++++++++++++
 1 file changed, 84 insertions(+)
 create mode 100644 Maths/test/RowEchelon.test.js

diff --git a/Maths/test/RowEchelon.test.js b/Maths/test/RowEchelon.test.js
new file mode 100644
index 0000000000..b3e2e1803c
--- /dev/null
+++ b/Maths/test/RowEchelon.test.js
@@ -0,0 +1,84 @@
+import { rowEchelon } from '../RowEchelon'
+describe('Determinant', () => {
+  const testCases = [
+    [
+      [
+        [8, 1, 3, 5],
+        [4, 6, 8, 2],
+        [3, 5, 6, 8]
+      ],
+      [
+        [1, 0.125, 0.375, 0.625],
+        [0, 1, 1.18182, -0.09091],
+        [0, 0, 1, -11.0769]
+      ]
+    ],
+    [
+      [
+        [6, 8, 1, 3, 5],
+        [1, 4, 6, 8, 2],
+        [0, 3, 5, 6, 8],
+        [2, 5, 9, 7, 8],
+        [5, 5, 7, 0, 1]
+      ],
+      [
+        [1, 1.33333, 0.16667, 0.5, 0.83333],
+        [0, 1, 2.1875, 2.8125, 0.4375],
+        [0, 0, 1, 1.56, -4.28003],
+        [0, 0, 0, 1, -3.3595],
+        [0, 0, 0, 0, 1]
+      ]
+    ],
+    [
+      [
+        [1, 3, 5],
+        [6, 8, 2],
+        [5, 6, 8],
+        [7, 9, 9],
+        [5, 0, 6]
+      ],
+      [
+        [1, 3, 5],
+        [0, 1, 2.8],
+        [0, 0, 1],
+        [0, 0, 0],
+        [0, 0, 0]
+      ]
+    ],
+    [
+      [
+        [8, 1, 3, 5],
+        [4, 6, 8, 2, 7],
+        [3, 5, 6, 8]
+      ],
+      'Input is not a valid 2D matrix.'
+    ],
+    [
+      [
+        [0, 7, 8, 1, 3, 5],
+        [0, 6, 4, 6, 8, 2],
+        [0, 7, 3, 5, 6, 8],
+        [6, 8, 1, 0, 0, 4],
+        [3, 3, 5, 7, 3, 1],
+        [1, 2, 1, 0, 9, 7],
+        [8, 8, 0, 2, 3, 1]
+      ],
+      [
+        [1, 1.33333, 0.16667, 0, 0, 0.66667],
+        [0, 1, 0.66667, 1, 1.33333, 0.33333],
+        [0, 0, 1, 1.2, 1.99999, -3.4],
+        [0, 0, 0, 1, 1.3, -1.4],
+        [0, 0, 0, 0, 1, -2.32854],
+        [0, 0, 0, 0, 0, 1],
+        [0, 0, 0, 0, 0, 0]
+      ]
+    ]
+  ]
+
+  test.each(testCases)(
+    'Should return the matrix in row echelon form.',
+    (matrix, expected) => {
+      expect(rowEchelon(matrix)).toEqual(expected)
+    }
+  )
+})

From 5a7fb8792085a1b816ca32c197ff7fa4ac1bb93a Mon Sep 17 00:00:00 2001
From: Piyush Katyal <109459034+piyushk77@users.noreply.github.com>
Date: Tue, 10 Oct 2023 14:21:22 +0530
Subject: [PATCH 3/6] fix: replace rounding with float tolerance

---
 Maths/RowEchelon.js | 25 ++++++++++---------------
 1 file changed, 10 insertions(+), 15 deletions(-)

diff --git a/Maths/RowEchelon.js b/Maths/RowEchelon.js
index 6203edbaf3..9efefc0d5c 100644
--- a/Maths/RowEchelon.js
+++ b/Maths/RowEchelon.js
@@ -26,6 +26,8 @@
  * //             ]
  */
 
+const tolerance = 0.000001
+
 const isMatrixValid = (matrix) => {
   let numRows = matrix.length
   let numCols = matrix[0].length
@@ -47,7 +49,7 @@ const isMatrixValid = (matrix) => {
 const checkNonZero = (currentRow, currentCol, matrix) => {
   let numRows = matrix.length
   for (let i = currentRow; i < numRows; i++) {
-    if (matrix[i][currentCol] !== 0) {
+    if (!isTolerant(0,matrix[i][currentCol], tolerance)) {
       return true
     }
   }
@@ -88,20 +90,14 @@ const subtractRow = (currentRow, fromRow, matrix) => {
   }
 }
 
-const formatResult = (matrix) => {
-  let precision = 5
-  let numRows = matrix.length
-  let numCols = matrix[0].length
-  for (let i = 0; i < numRows; i++) {
-    for (let j = 0; j < numCols; j++) {
-      matrix[i][j] = parseFloat(matrix[i][j].toFixed(precision))
-    }
-  }
+const isTolerant = (a, b, tolerance) => {
+  const absoluteDifference = Math.abs(a - b);
+  return (absoluteDifference <= tolerance);
 }
 
 const rowEchelon = (matrix) => {
-  if (isMatrixValid(matrix) === false) {
-    return 'Input is not a valid 2D matrix.'
+  if (!isMatrixValid(matrix)) {
+    throw new Error('Input is not a valid 2D matrix.')
   }
 
   let numRows = matrix.length
@@ -109,7 +105,7 @@ const rowEchelon = (matrix) => {
   let result = matrix
 
   for (let i = 0, j = 0; i < numRows && j < numCols; ) {
-    if (checkNonZero(i, j, result) === false) {
+    if (!checkNonZero(i, j, result)) {
       j++
       continue
     }
@@ -121,7 +117,7 @@ const rowEchelon = (matrix) => {
     //..............make bottom elements zero...............
     for (let x = i + 1; x < numRows; x++) {
       factor = result[x][j]
-      if (factor === 0) {
+      if (isTolerant(0,factor,tolerance)) {
         continue
       }
       scalarMultiplication(i, factor, result)
@@ -129,7 +125,6 @@ const rowEchelon = (matrix) => {
       factor = 1 / factor
       scalarMultiplication(i, factor, result)
     }
-    formatResult(result)
     i++
   }
   return result

From 64d1afc58064a9d7e58b8f112559d351085ec51e Mon Sep 17 00:00:00 2001
From: Piyush Katyal <109459034+piyushk77@users.noreply.github.com>
Date: Tue, 10 Oct 2023 14:23:55 +0530
Subject: [PATCH 4/6] chore: use correct style

---
 Maths/RowEchelon.js | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/Maths/RowEchelon.js b/Maths/RowEchelon.js
index 9efefc0d5c..478c98db68 100644
--- a/Maths/RowEchelon.js
+++ b/Maths/RowEchelon.js
@@ -49,7 +49,7 @@ const isMatrixValid = (matrix) => {
 const checkNonZero = (currentRow, currentCol, matrix) => {
   let numRows = matrix.length
   for (let i = currentRow; i < numRows; i++) {
-    if (!isTolerant(0,matrix[i][currentCol], tolerance)) {
+    if (!isTolerant(0, matrix[i][currentCol], tolerance)) {
       return true
     }
   }
@@ -91,8 +91,8 @@ const subtractRow = (currentRow, fromRow, matrix) => {
 }
 
 const isTolerant = (a, b, tolerance) => {
-  const absoluteDifference = Math.abs(a - b);
-  return (absoluteDifference <= tolerance);
+  const absoluteDifference = Math.abs(a - b)
+  return absoluteDifference <= tolerance
 }
 
 const rowEchelon = (matrix) => {
@@ -117,7 +117,7 @@ const rowEchelon = (matrix) => {
     //..............make bottom elements zero...............
     for (let x = i + 1; x < numRows; x++) {
       factor = result[x][j]
-      if (isTolerant(0,factor,tolerance)) {
+      if (isTolerant(0, factor, tolerance)) {
         continue
       }
       scalarMultiplication(i, factor, result)

From 2200c0e74e1a02bec53c5e8605f6e27a3d64d15e Mon Sep 17 00:00:00 2001
From: Piyush Katyal <109459034+piyushk77@users.noreply.github.com>
Date: Tue, 10 Oct 2023 14:27:11 +0530
Subject: [PATCH 5/6] fix: use error tolerance and segregate testcases

---
 Maths/test/RowEchelon.test.js | 37 ++++++++++++++++++++---------------
 1 file changed, 21 insertions(+), 16 deletions(-)

diff --git a/Maths/test/RowEchelon.test.js b/Maths/test/RowEchelon.test.js
index b3e2e1803c..5575bc6d39 100644
--- a/Maths/test/RowEchelon.test.js
+++ b/Maths/test/RowEchelon.test.js
@@ -1,6 +1,7 @@
 import { rowEchelon } from '../RowEchelon'
 describe('Determinant', () => {
-  const testCases = [
+  const tolerance = 0.000001
+  test.each([
     [
       [
         [8, 1, 3, 5],
@@ -45,14 +46,6 @@ describe('Determinant', () => {
         [0, 0, 0]
       ]
     ],
-    [
-      [
-        [8, 1, 3, 5],
-        [4, 6, 8, 2, 7],
-        [3, 5, 6, 8]
-      ],
-      'Input is not a valid 2D matrix.'
-    ],
     [
       [
         [0, 7, 8, 1, 3, 5],
@@ -73,12 +66,24 @@ describe('Determinant', () => {
         [0, 0, 0, 0, 0, 0]
       ]
     ]
-  ]
-
-  test.each(testCases)(
-    'Should return the matrix in row echelon form.',
-    (matrix, expected) => {
-      expect(rowEchelon(matrix)).toEqual(expected)
+  ])('Should return the matrix in row echelon form.', (matrix, expected) => {
+    for (let i = 0; i < matrix.length; i++) {
+      for (let j = 0; j < matrix[i].length; j++) {
+        expect(rowEchelon(matrix)[i][j]).toBeCloseTo(expected[i][j], tolerance)
+      }
     }
-  )
+  })
+
+  test.each([
+    [
+      [
+        [8, 1, 3, 5],
+        [4, 6, 8, 2, 7],
+        [3, 5, 6, 8]
+      ],
+      'Input is not a valid 2D matrix.'
+    ]
+  ])('Should return the error message.', (matrix, expected) => {
+    expect(() => rowEchelon(matrix)).toThrowError(expected)
+  })
 })

From 39fed98097a0b5b44e25abbf22f688a3e6ba1ef1 Mon Sep 17 00:00:00 2001
From: Piyush Katyal <109459034+piyushk77@users.noreply.github.com>
Date: Tue, 10 Oct 2023 14:52:17 +0530
Subject: [PATCH 6/6] chore: add necessary explaining comments

---
 Maths/RowEchelon.js | 19 ++++++++++++++++++-
 1 file changed, 18 insertions(+), 1 deletion(-)

diff --git a/Maths/RowEchelon.js b/Maths/RowEchelon.js
index 478c98db68..c773bb80a9 100644
--- a/Maths/RowEchelon.js
+++ b/Maths/RowEchelon.js
@@ -26,8 +26,10 @@
  * //             ]
  */
 
+// Set a tolerance value for floating-point comparisons
 const tolerance = 0.000001
 
+// Check if all the rows have same length of elements
 const isMatrixValid = (matrix) => {
   let numRows = matrix.length
   let numCols = matrix[0].length
@@ -36,6 +38,8 @@ const isMatrixValid = (matrix) => {
       return false
     }
   }
+
+  // Check for input other than a 2D matrix
   if (
     !Array.isArray(matrix) ||
     matrix.length === 0 ||
@@ -49,6 +53,7 @@ const isMatrixValid = (matrix) => {
 const checkNonZero = (currentRow, currentCol, matrix) => {
   let numRows = matrix.length
   for (let i = currentRow; i < numRows; i++) {
+    // Checks if the current element is not very near to zero.
     if (!isTolerant(0, matrix[i][currentCol], tolerance)) {
       return true
     }
@@ -66,6 +71,9 @@ const swapRows = (currentRow, withRow, matrix) => {
   }
 }
 
+// Select a pivot element in the current column to facilitate row operations.
+// Pivot element is the first non-zero element found from the current row
+// down to the last row.
 const selectPivot = (currentRow, currentCol, matrix) => {
   let numRows = matrix.length
   for (let i = currentRow; i < numRows; i++) {
@@ -76,6 +84,7 @@ const selectPivot = (currentRow, currentCol, matrix) => {
   }
 }
 
+// Multiply each element of the given row with a factor.
 const scalarMultiplication = (currentRow, factor, matrix) => {
   let numCols = matrix[0].length
   for (let j = 0; j < numCols; j++) {
@@ -83,6 +92,7 @@ const scalarMultiplication = (currentRow, factor, matrix) => {
   }
 }
 
+// Subtract one row from another row
 const subtractRow = (currentRow, fromRow, matrix) => {
   let numCols = matrix[0].length
   for (let j = 0; j < numCols; j++) {
@@ -90,12 +100,14 @@ const subtractRow = (currentRow, fromRow, matrix) => {
   }
 }
 
+// Check if two numbers are equal within a given tolerance
 const isTolerant = (a, b, tolerance) => {
   const absoluteDifference = Math.abs(a - b)
   return absoluteDifference <= tolerance
 }
 
 const rowEchelon = (matrix) => {
+  // Check if the input matrix is valid; if not, throw an error.
   if (!isMatrixValid(matrix)) {
     throw new Error('Input is not a valid 2D matrix.')
   }
@@ -104,17 +116,22 @@ const rowEchelon = (matrix) => {
   let numCols = matrix[0].length
   let result = matrix
 
+  // Iterate through the rows (i) and columns (j) of the matrix.
   for (let i = 0, j = 0; i < numRows && j < numCols; ) {
+    // If the current column has all zero elements below the current row,
+    // move to the next column.
     if (!checkNonZero(i, j, result)) {
       j++
       continue
     }
 
+    // Select a pivot element and normalize the current row.
     selectPivot(i, j, result)
     let factor = 1 / result[i][j]
     scalarMultiplication(i, factor, result)
 
-    //..............make bottom elements zero...............
+    // Make elements below the pivot element zero by performing
+    // row operations on subsequent rows.
     for (let x = i + 1; x < numRows; x++) {
       factor = result[x][j]
       if (isTolerant(0, factor, tolerance)) {
