diff --git a/include/tadah/models/linear_regressor.h b/include/tadah/models/linear_regressor.h
index 4e7355fb66fcd165102a4f57cafb63abf2eb432d..54df74aa020a78b7bef9ffff5a60070d85d22061 100644
--- a/include/tadah/models/linear_regressor.h
+++ b/include/tadah/models/linear_regressor.h
@@ -45,22 +45,18 @@ public:
       throw std::runtime_error("LAMBDA -1 requires OALGO 3");
     }
 
-    if (oalgo==1) {
+    if (lambda > 0 && (oalgo==1 || oalgo==2)) {
+      // Augment the design matrix with the regularization term
       size_t j=0;
-      if (lambda>0) {
-        for (size_t i=Phi.rows()-Phi.cols(); i<Phi.rows();++i) {
-          Phi(i,j++) = std::sqrt(lambda);
-        }
+      for (size_t i=Phi.rows()-Phi.cols(); i<Phi.rows();++i) {
+        Phi(i,j++) = std::sqrt(lambda);
       }
+    }
+
+    if (oalgo==1) {
       OLS::solve(Phi, T, weights, rcond, OLS::Algorithm::GELSD);
     }
     else if (oalgo==2) {
-      size_t j=0;
-      if (lambda>0) {
-        for (size_t i=Phi.rows()-Phi.cols(); i<Phi.rows();++i) {
-          Phi(i,j++) = std::sqrt(lambda);
-        }
-      }
       OLS::solve(Phi, T, weights, rcond, OLS::Algorithm::GELS);
     }
     else if (oalgo==3) {
@@ -83,6 +79,9 @@ public:
       if (verbose) std::cout << std::endl << "REG LAMBDA: " << lambda << std::endl;
       OLS::solve_with_svd(Phi.rows(), Phi.cols(), T, weights, svd, rcond, lambda);
     }
+    else if (oalgo==4) {
+      OLS::solve(Phi, T, weights, rcond, OLS::Algorithm::Cholesky);
+    }
     else {
       throw std::runtime_error("Unsupported OALGO: " + std::to_string(oalgo));
     }
diff --git a/include/tadah/models/ols.h b/include/tadah/models/ols.h
index 3c4ec409f24cb6d13ff5d9e8677291dba3ffbda7..1c427a97227a46aefa9e6ac597972b09268f3717 100644
--- a/include/tadah/models/ols.h
+++ b/include/tadah/models/ols.h
@@ -20,7 +20,7 @@
  * - **GELSD (`DGELSD`):** Uses a singular value decomposition (SVD) approach, handling rank-deficient and ill-conditioned systems.
  * - **GELS (`DGELS`):** Uses QR or LQ factorization, fast but less robust for ill-conditioned systems.
  * - **SVD:** Custom implementation using SVD, allows for explicit control over singular values and regularization.
- * - **Cholesky:** Solves the normal equations using Cholesky decomposition, efficient but may be less stable for ill-conditioned systems.
+ * - **Cholesky:** Solves the normal equations \( A^\top A x = A^\top b \) using Cholesky decomposition. Efficient for well-conditioned systems; however, as it squares the condition number, it may be less stable for ill-conditioned systems.
  *
  * **Workspace Management:**
  * - The class provides a `Workspace` struct to manage memory allocations for the computations.
@@ -253,7 +253,8 @@ void OLS::solve_with_workspace(M& A, V& B, V& weights, Workspace& ws, double rco
         // Custom SVD-based solution
         int min_mn = std::min(m, n);
 
-        if (!ws.U || !ws.S || !ws.VT) {
+        //if (!ws.U || !ws.S || !ws.VT) {
+        if (ws.owns_U || ws.owns_S || ws.owns_VT) {
             // Perform SVD
 
             // Ensure workspace is allocated
@@ -273,9 +274,6 @@ void OLS::solve_with_workspace(M& A, V& B, V& weights, Workspace& ws, double rco
                 throw std::runtime_error("Error in DGESDD: info = " + std::to_string(info));
             }
 
-            ws.owns_U = true;
-            ws.owns_S = true;
-            ws.owns_VT = true;
         }
 
         if (ws.U_t_b == nullptr) {
@@ -318,109 +316,141 @@ void OLS::solve_with_workspace(M& A, V& B, V& weights, Workspace& ws, double rco
 
     } else if (algorithm == Algorithm::Cholesky) {
         // Cholesky-based solution
-        throw std::runtime_error("Cholesky algorithm is not implemented in this version.");
+
+        // Compute AtA = Aáµ— * A
+        char uplo = 'U'; // Use upper triangle
+        char trans = 'T'; // Transpose A
+        double alpha = 1.0;
+        double beta = 0.0;
+        int lda = m;
+        int ldc = n;
+        ::dsyrk_(&uplo, &trans, &n, &m, &alpha, A.ptr(), &lda, &beta, ws.AtA, &ldc);
+
+        // Compute Atb = Aáµ— * B
+        int incx = 1;
+        int incy = 1;
+        ::dgemv_(&trans, &m, &n, &alpha, A.ptr(), &lda, B.ptr(), &incx, &beta, ws.Atb, &incy);
+
+        // Perform Cholesky factorization of AtA
+        ::dpotrf_(&uplo, &n, ws.AtA, &ldc, &info);
+        if (info != 0) {
+            throw std::runtime_error("Error in DPOTRF: Matrix is not positive definite; info = " + std::to_string(info));
+        }
+
+        // Solve AtA * x = Atb
+        int nrhs = 1;
+        ::dpotrs_(&uplo, &n, &nrhs, ws.AtA, &ldc, ws.Atb, &n, &info);
+        if (info != 0) {
+            throw std::runtime_error("Error in DPOTRS: info = " + std::to_string(info));
+        }
+
+        // Copy solution to weights
+        for (int i = 0; i < n; ++i) {
+            weights[i] = ws.Atb[i];
+        }
+
+    } else {
+        throw std::runtime_error("Unknown algorithm.");
     }
 }
 
-// New methods: solve_with_svd
-
 // Overload without lambda, defaults to lambda = 0.0
 template <typename V1 ,typename V2, typename SVDType>
 void OLS::solve_with_svd(int m_, int n_, V1& B, V2& weights, SVDType& svd) {
-    double rcond = -1.0;
-    double lambda = 0.0;
-    Workspace ws;
-    solve_with_svd_with_workspace(m_, n_, B, weights, svd, ws, rcond, lambda);
+  double rcond = -1.0;
+  double lambda = 0.0;
+  Workspace ws;
+  solve_with_svd_with_workspace(m_, n_, B, weights, svd, ws, rcond, lambda);
 }
 
 // Overload with rcond and lambda
 template <typename V1 ,typename V2, typename SVDType>
 void OLS::solve_with_svd(int m_, int n_, V1& B, V2& weights, SVDType& svd, double rcond, double lambda) {
-    Workspace ws;
-    solve_with_svd_with_workspace(m_, n_, B, weights, svd, ws, rcond, lambda);
+  Workspace ws;
+  solve_with_svd_with_workspace(m_, n_, B, weights, svd, ws, rcond, lambda);
 }
 
 // Overload with workspace and default lambda = 0.0
 template <typename V1 ,typename V2, typename SVDType>
 void OLS::solve_with_svd_with_workspace(int m_, int n_, V1& B, V2& weights, SVDType& svd, Workspace& ws) {
-    double rcond = -1.0;
-    double lambda = 0.0;
-    solve_with_svd_with_workspace(m_, n_, B, weights, svd, ws, rcond, lambda);
+  double rcond = -1.0;
+  double lambda = 0.0;
+  solve_with_svd_with_workspace(m_, n_, B, weights, svd, ws, rcond, lambda);
 }
 
 // Main method with workspace, rcond, and lambda
 template <typename V1 ,typename V2, typename SVDType>
 void OLS::solve_with_svd_with_workspace(int m_, int n_, V1& B, V2& weights, SVDType& svd, Workspace& ws, double rcond, double lambda) {
-    int m = m_;
-    int n = n_;
-
-    int min_mn = std::min(m, n);
-
-    // Check that dimensions of weights match expected size
-    if (weights.size() != static_cast<std::size_t>(n)) {
-        throw std::invalid_argument("weights size is incorrect.");
+  int m = m_;
+  int n = n_;
+
+  int min_mn = std::min(m, n);
+
+  // Check that dimensions of weights match expected size
+  if (weights.size() != static_cast<std::size_t>(n)) {
+    throw std::invalid_argument("weights size is incorrect.");
+  }
+
+  // Check that B has sufficient size
+  int maxmn = std::max(m, n);
+  if (B.size() < static_cast<std::size_t>(maxmn)) {
+    throw std::invalid_argument("B size is insufficient.");
+  }
+
+  // Check if workspace is sufficient; if not, reallocate
+  if (!ws.is_sufficient(m, n, Algorithm::SVD)) {
+    ws.allocate(m, n, Algorithm::SVD);
+  }
+
+  // Copy precomputed SVD components into the workspace
+  ws.U = svd.getU();   // Assume getU() returns a pointer to U (m x min(m,n))
+  ws.S = svd.getS();   // Assume getS() returns a pointer to singular values (min(m,n))
+  ws.VT = svd.getVT(); // Assume getVT() returns a pointer to VT (min(m,n) x n)
+  ws.lambda = lambda;
+
+  // Indicate that we don't own the SVD component pointers
+  ws.owns_U = false;
+  ws.owns_S = false;
+  ws.owns_VT = false;
+
+  // Proceed with the computations
+  if (ws.U_t_b == nullptr) {
+    ws.U_t_b = new double[min_mn];
+  }
+
+  // Compute Uáµ— * B
+  char trans = 'T'; // Transpose U
+  double alpha = 1.0;
+  double beta = 0.0;
+  int incx = 1;
+  int incy = 1;
+  int lda = m;
+  ::dgemv_(&trans, &m, &min_mn, &alpha, ws.U, &lda, B.ptr(), &incx, &beta, ws.U_t_b, &incy);
+
+  // Find the maximum singular value
+  double smax = 0.0;
+  for (int i = 0; i < min_mn; ++i) {
+    if (ws.S[i] > smax) {
+      smax = ws.S[i];
     }
+  }
 
-    // Check that B has sufficient size
-    int maxmn = std::max(m, n);
-    if (B.size() < static_cast<std::size_t>(maxmn)) {
-        throw std::invalid_argument("B size is insufficient.");
-    }
+  // Threshold for singular values
+  double thresh = rcond > 0.0 ? rcond * smax : 0.0;
 
-    // Check if workspace is sufficient; if not, reallocate
-    if (!ws.is_sufficient(m, n, Algorithm::SVD)) {
-        ws.allocate(m, n, Algorithm::SVD);
-    }
-
-    // Copy precomputed SVD components into the workspace
-    ws.U = svd.getU();   // Assume getU() returns a pointer to U (m x min(m,n))
-    ws.S = svd.getS();   // Assume getS() returns a pointer to singular values (min(m,n))
-    ws.VT = svd.getVT(); // Assume getVT() returns a pointer to VT (min(m,n) x n)
-    ws.lambda = lambda;
-
-    // Indicate that we don't own the SVD component pointers
-    ws.owns_U = false;
-    ws.owns_S = false;
-    ws.owns_VT = false;
-
-    // Proceed with the computations
-    if (ws.U_t_b == nullptr) {
-        ws.U_t_b = new double[min_mn];
-    }
-
-    // Compute Uáµ— * B
-    char trans = 'T'; // Transpose U
-    double alpha = 1.0;
-    double beta = 0.0;
-    int incx = 1;
-    int incy = 1;
-    int lda = m;
-    ::dgemv_(&trans, &m, &min_mn, &alpha, ws.U, &lda, B.ptr(), &incx, &beta, ws.U_t_b, &incy);
-
-    // Find the maximum singular value
-    double smax = 0.0;
-    for (int i = 0; i < min_mn; ++i) {
-        if (ws.S[i] > smax) {
-            smax = ws.S[i];
-        }
-    }
-
-    // Threshold for singular values
-    double thresh = rcond > 0.0 ? rcond * smax : 0.0;
-
-    // Apply regularization using 'smax' approach and lambda
-    for (int i = 0; i < min_mn; ++i) {
-        if (ws.S[i] > thresh) {
-            ws.U_t_b[i] = ws.U_t_b[i] * ws.S[i] / (ws.S[i] * ws.S[i] + ws.lambda);
-        } else {
-            ws.U_t_b[i] = 0.0;
-        }
+  // Apply regularization using 'smax' approach and lambda
+  for (int i = 0; i < min_mn; ++i) {
+    if (ws.S[i] > thresh) {
+      ws.U_t_b[i] = ws.U_t_b[i] * ws.S[i] / (ws.S[i] * ws.S[i] + ws.lambda);
+    } else {
+      ws.U_t_b[i] = 0.0;
     }
+  }
 
-    // Compute x = V * (regularized solution)
-    trans = 'T'; // Transpose VT to get V
-    int lda_vt = min_mn;
-    ::dgemv_(&trans, &min_mn, &n, &alpha, ws.VT, &lda_vt, ws.U_t_b, &incx, &beta, weights.ptr(), &incy);
+  // Compute x = V * (regularized solution)
+  trans = 'T'; // Transpose VT to get V
+  int lda_vt = min_mn;
+  ::dgemv_(&trans, &min_mn, &n, &alpha, ws.VT, &lda_vt, ws.U_t_b, &incx, &beta, weights.ptr(), &incy);
 }
 
 #endif // OLS_H
diff --git a/src/ols.cpp b/src/ols.cpp
index 6de4092eaa721bd169b24b9f49f75bc3e9e82956..52bc49f09da67fdf4339aff99d56fa93e4c0b326 100644
--- a/src/ols.cpp
+++ b/src/ols.cpp
@@ -8,156 +8,158 @@
 
 // Constructor
 OLS::Workspace::Workspace()
-    : work(nullptr), lwork(-1), m(0), n(0), algo(Algorithm::GELSD),
-      s(nullptr), iwork(nullptr),
-      lambda(0.0), U(nullptr), S(nullptr), VT(nullptr), iwork_svd(nullptr), U_t_b(nullptr),
-      owns_U(false), owns_S(false), owns_VT(false),
-      AtA(nullptr), Atb(nullptr) {
+  : work(nullptr), lwork(-1), m(0), n(0), algo(Algorithm::GELSD),
+  s(nullptr), iwork(nullptr),
+  lambda(0.0), U(nullptr), S(nullptr), VT(nullptr), iwork_svd(nullptr), U_t_b(nullptr),
+  owns_U(false), owns_S(false), owns_VT(false),
+  AtA(nullptr), Atb(nullptr) {
 }
 
 // Destructor
 OLS::Workspace::~Workspace() {
-    release();
+  release();
 }
 
 // allocate method
 void OLS::Workspace::allocate(int m_, int n_, Algorithm algorithm) {
-    release(); // Release any existing memory
-
-    m = m_;
-    n = n_;
-    algo = algorithm;
-    lambda = 0.0; // Reset lambda
-
-    int info;
-
-    if (algorithm == Algorithm::GELSD) {
-        // Allocate workspace for DGELSD
-        int minmn = std::min(m, n);
-        int maxmn = std::max(m, n);
-        int nlvl = std::max(0, static_cast<int>(std::log2(minmn / 25.0)) + 1);
-
-        // Allocate s (singular values)
-        s = new double[minmn];
-
-        // Allocate iwork
-        int liwork = 3 * minmn * nlvl + 11 * minmn;
-        iwork = new int[liwork];
-
-        // Workspace query
-        double work_query;
-        int lwork_query = -1;
-        int nrhs = 1;
-        double* a_dummy = nullptr;
-        double* b_dummy = nullptr;
-        ::dgelsd_(&m, &n, &nrhs, a_dummy, &m, b_dummy, &maxmn, s, nullptr,
-                  nullptr, &work_query, &lwork_query, iwork, &info);
-        lwork = static_cast<int>(work_query);
-        work = new double[lwork];
-    } else if (algorithm == Algorithm::GELS) {
-        // Allocate workspace for DGELS
-        double work_query;
-        int lwork_query = -1;
-        int nrhs = 1;
-        double* a_dummy = nullptr;
-        double* b_dummy = nullptr;
-        char trans = 'N';
-        ::dgels_(&trans, &m, &n, &nrhs, a_dummy, &m, b_dummy, &m, &work_query, &lwork_query, &info);
-        lwork = static_cast<int>(work_query);
-        work = new double[lwork];
-    } else if (algorithm == Algorithm::SVD) {
-
-        // Allocate U, S, VT, and iwork_svd
-        int min_mn = std::min(m, n);
-
-        // Only allocate if we own the data
-        if (owns_U) {
-            U = new double[m * min_mn];      // U is m x min(m,n)
-        }
-        if (owns_S) {
-            S = new double[min_mn];          // Singular values
-        }
-        if (owns_VT) {
-            VT = new double[min_mn * n];     // VT is min(m,n) x n
-        }
-        iwork_svd = new int[8 * min_mn];     // Workspace for dgesdd_
-
-        // Query for optimal workspace size
-        char jobz = 'S';
-        int lda = m;
-        int ldu = m;
-        int ldvt = min_mn;
-        lwork = -1;
-        double wkopt;
-        int info_query;
-        double* a_dummy = nullptr;
-        ::dgesdd_(&jobz, &m, &n, a_dummy, &lda, S, U, &ldu, VT, &ldvt, &wkopt, &lwork, iwork_svd, &info_query);
-
-        if (info_query != 0) {
-            throw std::runtime_error("Error in DGESDD (workspace query): info = " + std::to_string(info_query));
-        }
-
-        lwork = static_cast<int>(wkopt);
-        work = new double[lwork];
-
-    } else if (algorithm == Algorithm::Cholesky) {
-        // Allocate space for AtA (n x n) and Atb (n)
-        AtA = new double[n * n];
-        Atb = new double[n];
-
-        // No additional workspace required for Cholesky decomposition
-        work = nullptr;
-        lwork = 0;
-    } else {
-        throw std::invalid_argument("Invalid algorithm specified in Workspace::allocate().");
+  release(); // Release any existing memory
+
+  m = m_;
+  n = n_;
+  algo = algorithm;
+  lambda = 0.0; // Reset lambda
+
+  int info;
+
+  if (algorithm == Algorithm::GELSD) {
+    // Allocate workspace for DGELSD
+    int minmn = std::min(m, n);
+    int maxmn = std::max(m, n);
+    int nlvl = std::max(0, static_cast<int>(std::log2(minmn / 25.0)) + 1);
+
+    // Allocate s (singular values)
+    s = new double[minmn];
+
+    // Allocate iwork
+    int liwork = 3 * minmn * nlvl + 11 * minmn;
+    iwork = new int[liwork];
+
+    // Workspace query
+    double work_query;
+    int lwork_query = -1;
+    int nrhs = 1;
+    double* a_dummy = nullptr;
+    double* b_dummy = nullptr;
+    ::dgelsd_(&m, &n, &nrhs, a_dummy, &m, b_dummy, &maxmn, s, nullptr,
+              nullptr, &work_query, &lwork_query, iwork, &info);
+    lwork = static_cast<int>(work_query);
+    work = new double[lwork];
+  } else if (algorithm == Algorithm::GELS) {
+    // Allocate workspace for DGELS
+    double work_query;
+    int lwork_query = -1;
+    int nrhs = 1;
+    double* a_dummy = nullptr;
+    double* b_dummy = nullptr;
+    char trans = 'N';
+    ::dgels_(&trans, &m, &n, &nrhs, a_dummy, &m, b_dummy, &m, &work_query, &lwork_query, &info);
+    lwork = static_cast<int>(work_query);
+    work = new double[lwork];
+  } else if (algorithm == Algorithm::SVD) {
+
+    // Allocate U, S, VT, and iwork_svd
+    int min_mn = std::min(m, n);
+
+    // Allocate U, S, VT regardless of ownership flags (they should be owned in this case)
+    if (U == nullptr) {
+      U = new double[m * min_mn];      // U is m x min(m,n)
+      owns_U = true;
     }
-}
-
-// release method
-void OLS::Workspace::release() {
-    if (owns_U && U != nullptr) {
-        delete[] U;
-        U = nullptr;
+    if (S == nullptr) {
+      S = new double[min_mn];          // Singular values
+      owns_S = true;
     }
-    if (owns_S && S != nullptr) {
-        delete[] S;
-        S = nullptr;
+    if (VT == nullptr) {
+      VT = new double[min_mn * n];     // VT is min(m,n) x n
+      owns_VT = true;
     }
-    if (owns_VT && VT != nullptr) {
-        delete[] VT;
-        VT = nullptr;
-    }
-    delete[] s;
-    delete[] iwork;
-    delete[] U_t_b;
-    delete[] iwork_svd;
-    delete[] work;
-    delete[] AtA;
-    delete[] Atb;
-
-    s = nullptr;
-    iwork = nullptr;
-    U_t_b = nullptr;
-    iwork_svd = nullptr;
-    work = nullptr;
-    AtA = nullptr;
-    Atb = nullptr;
+    iwork_svd = new int[8 * min_mn];     // Workspace for dgesdd_
 
+    // Query for optimal workspace size
+    char jobz = 'S';
+    int lda = m;
+    int ldu = m;
+    int ldvt = min_mn;
     lwork = -1;
-    m = 0;
-    n = 0;
-    algo = Algorithm::GELSD;
-    lambda = 0.0;
+    double wkopt;
+    int info_query;
+    double* a_dummy = nullptr;
+    ::dgesdd_(&jobz, &m, &n, a_dummy, &lda, S, U, &ldu, VT, &ldvt, &wkopt, &lwork, iwork_svd, &info_query);
+
+    if (info_query != 0) {
+      throw std::runtime_error("Error in DGESDD (workspace query): info = " + std::to_string(info_query));
+    }
 
+    lwork = static_cast<int>(wkopt);
+    work = new double[lwork];
+
+  } else if (algorithm == Algorithm::Cholesky) {
+    // Allocate space for AtA (n x n) and Atb (n)
+    AtA = new double[n * n];
+    Atb = new double[n];
+
+    // No additional workspace required for Cholesky decomposition
+    work = nullptr;
+    lwork = 0;
+  } else {
+    throw std::invalid_argument("Invalid algorithm specified in Workspace::allocate().");
+  }
+}
+
+// release method
+void OLS::Workspace::release() {
+  if (owns_U && U != nullptr) {
+    delete[] U;
+    U = nullptr;
     owns_U = false;
+  }
+  if (owns_S && S != nullptr) {
+    delete[] S;
+    S = nullptr;
     owns_S = false;
+  }
+  if (owns_VT && VT != nullptr) {
+    delete[] VT;
+    VT = nullptr;
     owns_VT = false;
+  }
+  delete[] s;
+  delete[] iwork;
+  delete[] U_t_b;
+  delete[] iwork_svd;
+  delete[] work;
+  delete[] AtA;
+  delete[] Atb;
+
+  s = nullptr;
+  iwork = nullptr;
+  U_t_b = nullptr;
+  iwork_svd = nullptr;
+  work = nullptr;
+  AtA = nullptr;
+  Atb = nullptr;
+
+  lwork = -1;
+  m = 0;
+  n = 0;
+  algo = Algorithm::GELSD;
+  lambda = 0.0;
 }
 
 // is_sufficient method
 bool OLS::Workspace::is_sufficient(int m_, int n_, Algorithm algorithm) const {
-    if (m != m_ || n != n_ || algo != algorithm) {
-        return false;
-    }
-    return true;
+  if (m != m_ || n != n_ || algo != algorithm) {
+    return false;
+  }
+  return true;
 }
diff --git a/tests/test_ols.cpp b/tests/test_ols.cpp
index 8275369dc3fa029203504d580737b938503a4ee4..98a34d02998b5b81a2eb4bfa3527b7a8b4e1287b 100644
--- a/tests/test_ols.cpp
+++ b/tests/test_ols.cpp
@@ -18,8 +18,8 @@ TEST_CASE("Testing OLS") {
 
   // Prepare test data with corrected matrix initialization
   Matrix A(3, 3, {1, 4, 7,   // First column
-    2, 5, 8,   // Second column
-    3, 6, 10}); // Third column
+                  2, 5, 8,   // Second column
+                  3, 6, 10}); // Third column
 
   Vector expected_weights = {1, 2, 3};
 
@@ -32,7 +32,51 @@ TEST_CASE("Testing OLS") {
     vectors_are_close(weights, expected_weights, 1e-4);
   }
 
-  // Other sections remain unchanged...
+  SECTION("Solve using GELS algorithm without workspace") {
+    Vector weights(3);
+    OLS::solve(A, B, weights, -1.0, OLS::Algorithm::GELS);
+    vectors_are_close(weights, expected_weights, 1e-4);
+  }
+
+  SECTION("Solve using Cholesky algorithm without workspace") {
+    Vector weights(3);
+    OLS::solve(A, B, weights, -1.0, OLS::Algorithm::Cholesky);
+    vectors_are_close(weights, expected_weights, 1e-4);
+  }
+
+  SECTION("Solve using custom SVD implementation without workspace") {
+    Vector weights(3);
+    OLS::solve(A, B, weights, -1.0, OLS::Algorithm::SVD);
+    vectors_are_close(weights, expected_weights, 1e-4);
+  }
+
+  SECTION("Solve using default algorithm (GELSD) with workspace") {
+    Vector weights(3);
+    OLS::Workspace ws;
+    OLS::solve_with_workspace(A, B, weights, ws);
+    vectors_are_close(weights, expected_weights, 1e-4);
+  }
+
+  SECTION("Solve using GELS algorithm with workspace") {
+    Vector weights(3);
+    OLS::Workspace ws;
+    OLS::solve_with_workspace(A, B, weights, ws, -1.0, OLS::Algorithm::GELS);
+    vectors_are_close(weights, expected_weights, 1e-4);
+  }
+
+  SECTION("Solve using Cholesky algorithm with workspace") {
+    Vector weights(3);
+    OLS::Workspace ws;
+    OLS::solve_with_workspace(A, B, weights, ws, -1.0, OLS::Algorithm::Cholesky);
+    vectors_are_close(weights, expected_weights, 1e-4);
+  }
+
+  SECTION("Solve using custom SVD implementation with workspace") {
+    Vector weights(3);
+    OLS::Workspace ws;
+    OLS::solve_with_workspace(A, B, weights, ws, -1.0, OLS::Algorithm::SVD);
+    vectors_are_close(weights, expected_weights, 1e-4);
+  }
 
   SECTION("Test handling of rank-deficient matrix") {
     // Corrected initialization in column-major order
@@ -82,7 +126,7 @@ TEST_CASE("Testing OLS") {
 
     // Recompute B_over
     Vector B_over = A_over * expected_weights_over;
-    Vector B_over2 = B_over;;
+    Vector B_over2 = B_over;
 
     Vector weights(n);
 
@@ -121,12 +165,204 @@ TEST_CASE("Testing OLS") {
     Vector B_computed = A_under * weights;
 
     // Since B_under has been resized, compare only the first m elements
-    Vector B_under_trimmed = Vector(B_under.ptr(),m);
-    Vector B_computed_trimmed = Vector(B_computed.ptr(),m);
+    Vector B_under_trimmed = Vector(B_under.ptr(), m);
+    Vector B_computed_trimmed = Vector(B_computed.ptr(), m);
 
     vectors_are_close(B_computed_trimmed, B_under_trimmed, 1e-4);
   }
 
+  SECTION("Test solve_with_svd without workspace") {
+    // Assuming SVDType is properly defined and we have access to an SVD implementation
+    // Replace 'SVDType' with your actual SVD class or struct
+
+    // For this test, we'll use the custom SVD algorithm on matrix A
+
+    // Placeholder SVDType and methods
+    struct SVDType {
+      double* getU() { return U.data(); }
+      double* getS() { return S.data(); }
+      double* getVT() { return VT.data(); }
+      std::vector<double> U;
+      std::vector<double> S;
+      std::vector<double> VT;
+    };
+
+    // Compute SVD of A
+    SVDType svd;
+    int m = A.rows();
+    int n = A.cols();
+    int min_mn = std::min(m, n);
+    svd.U.resize(m * min_mn);
+    svd.S.resize(min_mn);
+    svd.VT.resize(min_mn * n);
+
+    // Prepare parameters for LAPACK dgesdd_
+    char jobz = 'S';
+    int lda = m;
+    int ldu = m;
+    int ldvt = min_mn;
+    int info;
+    std::vector<double> A_copy(A.ptr(), A.ptr() + m * n);
+
+    // Workspace query
+    int lwork = -1;
+    double wkopt;
+    std::vector<int> iwork(8 * min_mn);
+    ::dgesdd_(&jobz, &m, &n, A_copy.data(), &lda, svd.S.data(), svd.U.data(), &ldu, svd.VT.data(), &ldvt, &wkopt, &lwork, iwork.data(), &info);
+
+    lwork = static_cast<int>(wkopt);
+    std::vector<double> work(lwork);
+
+    // Compute SVD
+    ::dgesdd_(&jobz, &m, &n, A_copy.data(), &lda, svd.S.data(), svd.U.data(), &ldu, svd.VT.data(), &ldvt, work.data(), &lwork, iwork.data(), &info);
+
+    REQUIRE(info == 0);
+
+    Vector weights(3);
+    OLS::solve_with_svd(m, n, B, weights, svd);
+
+    vectors_are_close(weights, expected_weights, 1e-4);
+  }
+
+  SECTION("Test solve_with_svd with workspace") {
+    // Similar to the previous section but using workspace
+    struct SVDType {
+      double* getU() { return U.data(); }
+      double* getS() { return S.data(); }
+      double* getVT() { return VT.data(); }
+      std::vector<double> U;
+      std::vector<double> S;
+      std::vector<double> VT;
+    };
+
+    // Compute SVD of A
+    SVDType svd;
+    int m = A.rows();
+    int n = A.cols();
+    int min_mn = std::min(m, n);
+    svd.U.resize(m * min_mn);
+    svd.S.resize(min_mn);
+    svd.VT.resize(min_mn * n);
+
+    // Prepare parameters for LAPACK dgesdd_
+    char jobz = 'S';
+    int lda = m;
+    int ldu = m;
+    int ldvt = min_mn;
+    int info;
+    std::vector<double> A_copy(A.ptr(), A.ptr() + m * n);
+
+    // Workspace query
+    int lwork = -1;
+    double wkopt;
+    std::vector<int> iwork(8 * min_mn);
+    ::dgesdd_(&jobz, &m, &n, A_copy.data(), &lda, svd.S.data(), svd.U.data(), &ldu, svd.VT.data(), &ldvt, &wkopt, &lwork, iwork.data(), &info);
+
+    lwork = static_cast<int>(wkopt);
+    std::vector<double> work(lwork);
+
+    // Compute SVD
+    ::dgesdd_(&jobz, &m, &n, A_copy.data(), &lda, svd.S.data(), svd.U.data(), &ldu, svd.VT.data(), &ldvt, work.data(), &lwork, iwork.data(), &info);
+
+    REQUIRE(info == 0);
+
+    Vector weights(3);
+    OLS::Workspace ws;
+    double rcond = -1.0;
+    double lambda = 0.0;
+
+    OLS::solve_with_svd_with_workspace(m, n, B, weights, svd, ws, rcond, lambda);
+
+    vectors_are_close(weights, expected_weights, 1e-4);
+  }
+
+  SECTION("Test solve_with_svd with regularization (lambda)") {
+    // Using a non-zero lambda for regularization
+    struct SVDType {
+      double* getU() { return U.data(); }
+      double* getS() { return S.data(); }
+      double* getVT() { return VT.data(); }
+      std::vector<double> U;
+      std::vector<double> S;
+      std::vector<double> VT;
+    };
+
+    // Compute SVD of A
+    SVDType svd;
+    int m = A.rows();
+    int n = A.cols();
+    int min_mn = std::min(m, n);
+    svd.U.resize(m * min_mn);
+    svd.S.resize(min_mn);
+    svd.VT.resize(min_mn * n);
+
+    // Prepare parameters for LAPACK dgesdd_
+    char jobz = 'S';
+    int lda = m;
+    int ldu = m;
+    int ldvt = min_mn;
+    int info;
+    std::vector<double> A_copy(A.ptr(), A.ptr() + m * n);
+
+    // Workspace query
+    int lwork = -1;
+    double wkopt;
+    std::vector<int> iwork(8 * min_mn);
+    ::dgesdd_(&jobz, &m, &n, A_copy.data(), &lda, svd.S.data(), svd.U.data(), &ldu, svd.VT.data(), &ldvt, &wkopt, &lwork, iwork.data(), &info);
+
+    lwork = static_cast<int>(wkopt);
+    std::vector<double> work(lwork);
+
+    // Compute SVD
+    ::dgesdd_(&jobz, &m, &n, A_copy.data(), &lda, svd.S.data(), svd.U.data(), &ldu, svd.VT.data(), &ldvt, work.data(), &lwork, iwork.data(), &info);
+
+    REQUIRE(info == 0);
+
+    Vector weights(3);
+    double rcond = -1.0;
+    double lambda = 0.0005; // Regularization parameter
+
+    OLS::solve_with_svd(m, n, B, weights, svd, rcond, lambda);
+
+    // Since lambda introduces regularization, weights may not match exactly
+    // but for this test, we can still check that the solution is reasonable
+
+    Vector B_computed = A * weights;
+
+    // Compute residual norm
+    double residual_norm = 0.0;
+    for (int i = 0; i < m; ++i) {
+      residual_norm += std::pow(B[i] - B_computed[i], 2);
+    }
+    residual_norm = std::sqrt(residual_norm);
+
+    REQUIRE(residual_norm < 1e-3); // Adjust tolerance as needed
+  }
+
+  SECTION("Test solve with Cholesky algorithm on ill-conditioned matrix") {
+    // Create an ill-conditioned matrix
+    Matrix A_ill(3, 3, {1e-11, 0,   0,
+                        0,   1e-11, 0,
+                        0,    0,   1e-11});
+
+    Vector B_ill = {1e-11, 1e-11, 1e-11};
+    Vector weights(3);
+
+    // Solve using Cholesky algorithm
+    OLS::solve(A_ill, B_ill, weights, -1.0, OLS::Algorithm::Cholesky);
+    Vector B_computed = A_ill * weights;
+    vectors_are_close(B_ill, B_computed, 1e-11);
+  }
+
+  SECTION("Test solve with Cholesky algorithm on well-conditioned matrix") {
+    Matrix A_well = A; // Use the original well-conditioned matrix
+    Vector B_well = B;
+    Vector weights(3);
+
+    OLS::solve(A_well, B_well, weights, -1.0, OLS::Algorithm::Cholesky);
+    vectors_are_close(weights, expected_weights, 1e-4);
+  }
+
   // Rest of the tests...
 }