function assertValidQRDecomposition (A, Q, R) {
  const Asize = math.size(A).valueOf()

  const rows = Asize[0]
  const cols = Asize[1]

  // sizes match
  assert.deepStrictEqual(math.size(Q).valueOf(), [rows, rows])
  assert.deepStrictEqual(math.size(R).valueOf(), [rows, cols])

  // A = Q * R
  approxDeepEqual(math.multiply(Q, R).valueOf(), A.valueOf())

  // Q has unitary (orthonormal for real A) columns
  // use math.equal as approx.deepEqual cannot handle complex vs real number comparision
  assert(math.equal(math.multiply(math.conj(math.transpose(Q)), Q).valueOf(), math.identity([Asize[0], Asize[0]]).valueOf()),
    'Matrix Q is not unitary/orthonormal')

  // R is upper triangular
  for (let i = 0; i < rows; i++) {
    for (let j = 0; j < i && j < cols; j++) {
      assert(math.isZero(math.subset(R, math.index(i, j))), 'R is not an upper triangular matrix')
    }
  }

  // All elements on leading diagonal of R are positive
  for (let i = 0; i < Math.min(rows, cols); i++) {
    const diagonalElement = math.subset(R, math.index(i, i))

    assert(!math.isNegative(math.re(diagonalElement)),
      'R has elements on the leading diagonal with a negative real part (R[' + i + '][' + i + '] = ' + diagonalElement + ')')
  }

  const raw = math.qr._denseQRimpl(Array.isArray(A) ? math.matrix(A) : A)

  for (let i = 0; i < raw.R._data.length; ++i) {
    for (let j = 0; j < i && j < (raw.R._data[0] || []).length; ++j) {
      approxEqual(raw.R._data[i][j], 0, 1e-10)
    }
  }
}
Tests whether `Q` and `R` are the valid QR decomposition of `A`.

Given a real matrix `A`, `Q` and `R` should be the solutions to the equation
`A = Q*R` where Q is [orthoganal](https://en.wikipedia.org/wiki/Orthogonal_matrix) and
R is [upper triangular](https://en.wikipedia.org/wiki/Triangular_matrix).

If `A` is a complex matrix then `Q` should be a [unitary](https://en.wikipedia.org/wiki/Unitary_matrix)

Syntax:

   math.isValidQRDecomposition(A);

Example:

   const m = [
     [1, -1,  4],
     [1,  4, -2],
     [1,  4,  2],
     [1,  -1, 0]
   ]
   const  result = math.qr(m)
   // r = {
   //   Q: [
   //     [0.5, -0.5,   0.5],
   //     [0.5,  0.5,  -0.5],
   //     [0.5,  0.5,   0.5],
   //     [0.5, -0.5,  -0.5],
   //   ],
   //   R: [
   //     [2, 3,  2],
   //     [0, 5, -2],
   //     [0, 0,  4],
   //     [0, 0,  0]
   //   ]
   // }

   isValidQRDecomposition(m, r.Q, r.R)
     // true

   r.Q[2][1] = 9

   isValidQRDecomposition(m, r.Q, r.R)
     // false


@param {Matrix | Array} A    A two dimensional matrix or array from which the QR decomposition was formed.
@param {Matrix | Array} Q    A two dimensional matrix or array equal to `Q` is an QR decomposition.
@param {Matrix | Array} R    A two dimensional matrix or array equal to `R` is an QR decomposition.

@return {Boolean} Returns true if `Q` and `R` form a valid QR decomposition of `A`