 
 // MARK: - Operators
 
-public func + (lhs: Matrix, rhs: Matrix) -> Matrix {
+public func + (lhs: Matrix<Float>, rhs: Matrix<Float>) -> Matrix<Float> {
     return add(lhs, rhs)
 }
 
-public func * (lhs: Double, rhs: Matrix) -> Matrix {
-    return mul(lhs, rhs)
-}
-
-public func * (lhs: Matrix, rhs: Matrix) -> Matrix {
+public func + (lhs: Matrix<Double>, rhs: Matrix<Double>) -> Matrix<Double> {
+    return add(lhs, rhs)
+}
+
+public func * (lhs: Float, rhs: Matrix<Float>) -> Matrix<Float> {
+    return mul(lhs, rhs)
+}
+
+public func * (lhs: Double, rhs: Matrix<Double>) -> Matrix<Double> {
+    return mul(lhs, rhs)
+}
+
+public func * (lhs: Matrix<Float>, rhs: Matrix<Float>) -> Matrix<Float> {
+    return mul(lhs, rhs)
+}
+
+public func * (lhs: Matrix<Double>, rhs: Matrix<Double>) -> Matrix<Double> {
     return mul(lhs, rhs)
 }
 
 postfix operator ′ {}
-public postfix func ′ (value: Matrix) {
-    transpose(value)
-}
+public postfix func ′ (value: Matrix<Float>) -> Matrix<Float> {
+    return transpose(value)
+}
+
+public postfix func ′ (value: Matrix<Double>) -> Matrix<Double> {
+    return transpose(value)
+}

    return results
}

public func transpose(x: Matrix) -> Matrix {
    var results = Matrix(rows: x.columns, columns: x.rows, repeatedValue: 0.0)
    vDSP_mtransD(x.grid, 1, &(results.grid), 1, vDSP_Length(results.rows), vDSP_Length(results.columns))

    return results

    return results
}

public func mul(alpha: Float, x: Matrix<Float>) -> Matrix<Float> {
    var results = x
    cblas_sscal(Int32(x.grid.count), alpha, &(results.grid), 1)

    return results
}

public func mul(alpha: Double, x: Matrix<Double>) -> Matrix<Double> {
    var results = x
    cblas_dscal(Int32(x.grid.count), alpha, &(results.grid), 1)

    return results
}

public func mul(x: Matrix<Float>, y: Matrix<Float>) -> Matrix<Float> {
    precondition(x.columns == y.rows, "Matrix dimensions not compatible with multiplication")

    var results = Matrix<Float>(rows: x.rows, columns: y.columns, repeatedValue: 0.0)
    cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, Int32(x.rows), Int32(y.columns), Int32(x.columns), 1.0, x.grid, Int32(x.columns), y.grid, Int32(y.columns), 0.0, &(results.grid), Int32(results.columns))

    return results
}

public func mul(x: Matrix<Double>, y: Matrix<Double>) -> Matrix<Double> {
    precondition(x.columns == y.rows, "Matrix dimensions not compatible with multiplication")

    var results = Matrix<Double>(rows: x.rows, columns: y.columns, repeatedValue: 0.0)
    cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, Int32(x.rows), Int32(y.columns), Int32(x.columns), 1.0, x.grid, Int32(x.columns), y.grid, Int32(y.columns), 0.0, &(results.grid), Int32(results.columns))

    return results
}

public func inv(x : Matrix<Float>) -> Matrix<Float> {
    precondition(x.rows == x.columns, "Matrix must be square")

    var results = x

    var ipiv = [__CLPK_integer](count: x.rows * x.rows, repeatedValue: 0)
    var lwork = __CLPK_integer(x.columns * x.columns)
    var work = [CFloat](count: Int(lwork), repeatedValue: 0.0)
    var error: __CLPK_integer = 0
    var nc = __CLPK_integer(x.columns)

    sgetrf_(&nc, &nc, &(results.grid), &nc, &ipiv, &error)
    sgetri_(&nc, &(results.grid), &nc, &ipiv, &work, &lwork, &error)

    assert(error == 0, "Matrix not invertible")

    return results
}

public func inv(x : Matrix<Double>) -> Matrix<Double> {
    precondition(x.rows == x.columns, "Matrix must be square")

    var results = x
