diff --git a/bob/math/cpp/gsvd.cpp b/bob/math/cpp/gsvd.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..47e0a5fc0dfa84ec19d6f8fcd3b47b75c8b00c64
--- /dev/null
+++ b/bob/math/cpp/gsvd.cpp
@@ -0,0 +1,328 @@
+/**
+ * @date Tue Jan 10 21:14 2016 +0100
+ * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch>
+ *
+ * Copyright (C) Idiap Research Institute, Martigny, Switzerland
+ */
+
+#include <stdexcept>
+#include <boost/shared_array.hpp>
+
+#include <bob.math/gsvd.h>
+
+#include <bob.core/assert.h>
+#include <bob.core/check.h>
+#include <bob.core/array_copy.h>
+#include <bob.math/linear.h>
+
+
+// Declaration of the external LAPACK function (Divide and conquer SVD)
+extern "C" void dggsvd3_(const char *jobu,
+                         const char *jobv, 
+                         const char *jobq,
+                         const int *M,
+                         const int *N,
+                         const int *P,
+                         const int *K,
+                         const int *L,
+                         double *A, 
+                         const int *lda, 
+                         double *B, 
+                         const int *ldb,
+                         double *alpha,
+                         double *beta,
+                         double *U,
+                         const int *ldu, 
+                         double *V,
+                         const int *ldv, 
+                         double *Q,
+                         const int *ldq, 
+                         double *work,
+                         const int *lwork,
+                         int *iwork,
+                         int *info);
+
+
+void bob::math::gsvd( blitz::Array<double,2>& A,
+                      blitz::Array<double,2>& B,
+                      blitz::Array<double,2>& U,
+                      blitz::Array<double,2>& V,
+                      blitz::Array<double,2>& zeroR,
+                      blitz::Array<double,2>& Q,
+                      blitz::Array<double,2>& X,
+                      blitz::Array<double,2>& C,
+                      blitz::Array<double,2>& S)
+{
+  const char jobu = 'U';
+  const char jobv = 'V';
+  const char jobq = 'Q';
+
+  // Size variables
+  const int M = A.extent(0);
+  const int N = A.extent(1);
+  const int P = B.extent(0);
+  
+  const int lda = std::max(1,M);
+  const int ldb = std::max(1,P);
+  const int ldu = std::max(1,M);
+  const int ldv = std::max(1,P);
+  const int ldq = std::max(1,N);
+  
+  int K = 0; //out
+  int L = 0; //out  
+  
+  // Prepares to call LAPACK function:
+  // We will decompose A^T rather than A and B^T rather than B to reduce the required number of copy
+  // We recall that FORTRAN/LAPACK is column-major order whereas blitz arrays
+  // are row-major order by default.
+
+  //A_lapack = A^T
+  blitz::Array<double,2> A_blitz_lapack(bob::core::array::ccopy(const_cast<blitz::Array<double,2>&>(A).transpose(1,0)));
+  double* A_lapack = A_blitz_lapack.data();
+
+
+  //B_lapack = B^T
+  blitz::Array<double,2> B_blitz_lapack(bob::core::array::ccopy(const_cast<blitz::Array<double,2>&>(B).transpose(1,0)));
+  double* B_lapack = B_blitz_lapack.data();
+  
+  
+  // U. We will trainpose this one in the end
+  double *U_lapack = U.data();
+
+  // V. We will trainpose this one in the end
+  double *V_lapack = V.data();
+
+  // For Q, we will return the transpose  
+  double *Q_lapack = Q.data();
+
+  // In LAPACK C and S is 1-d. Our code makes it diagonal
+  blitz::Array<double,1> C_1d(N); C_1d = 0;
+  double *C_lapack = C_1d.data();
+  
+  blitz::Array<double,1> S_1d(N); S_1d = 0;
+  double *S_lapack = S_1d.data();
+
+  const int lwork_query = -1;
+  double work_query;
+
+  boost::shared_array<int> iwork(new int[N]);
+
+  int info = 0;
+  // A/ Queries the optimal size of the working array
+  
+  dggsvd3_(&jobu,
+          &jobv, 
+          &jobq,
+          &M,
+          &N,
+          &P,
+          &K,
+          &L,
+          A_lapack, 
+          &lda, 
+          B_lapack, 
+          &ldb,
+          C_lapack,
+          S_lapack,
+          U_lapack,
+          &ldu, 
+          V_lapack,
+          &ldv, 
+          Q_lapack,
+          &ldq, 
+          &work_query,
+          &lwork_query,
+          iwork.get(),
+          &info);
+          
+  if (info != 0)
+    throw std::runtime_error("The LAPACK dggsvd3 function returned a non-zero value during the checking");
+
+  // B/ Computes
+  
+  const int lwork = static_cast<int>(work_query);
+  boost::shared_array<double> work(new double[lwork]);
+  
+  dggsvd3_(&jobu,
+            &jobv, 
+           &jobq,
+           &M,
+           &N,
+           &P,
+           &K,
+           &L,
+           A_lapack, 
+           &lda, 
+           B_lapack, 
+           &ldb,
+           C_lapack,
+           S_lapack,
+           U_lapack,
+           &ldu, 
+           V_lapack,
+           &ldv, 
+           Q_lapack,
+           &ldq, 
+           work.get(),
+           &lwork,
+           iwork.get(),
+           &info);
+  if (info != 0)
+    throw std::runtime_error("The LAPACK dggsvd3 function returned a non-zero value during the computation.");
+  
+
+  /* 
+  According to the website http://www.netlib.org/lapack/explore-html/d1/d7e/group__double_g_esing_ga4a187519e5c71da3b3f67c85e9baf0f2.html#ga4a187519e5c71da3b3f67c85e9baf0f2
+  
+  if (M-K-L >= 0)
+                      N-K-L  K    L
+        ( 0 R ) = K (  0   R11  R12 )
+                  L (  0    0   R22 )
+                  
+                  
+        Where R11, R12, R22, = A(1:K+L,N-K-L+1:N)
+  
+   else
+   
+                        N-K-L  K   M-K  K+L-M
+       ( 0 R ) =     K ( 0    R11  R12  R13  )
+                   M-K ( 0     0   R22  R23  )
+                 K+L-M ( 0     0    0   R33  )
+
+
+       where R11, R12, R13, R22, R23 = A(1:M, N-K-L+1:N)
+       where R33 = B(M-K+1:L,N+M-K-L+1:N)
+
+  */
+  int r = K + L; //Dimension of R
+  C.resize(M, r);
+  S.resize(P, r);
+  S = 0;
+  C = 0;
+  zeroR.resize(r, N);
+  zeroR = 0;
+  X.resize(r,N);
+  if (M-K-L >= 0){
+
+    //1. First we need the [0 R], which has the shape (N-K-L + K + L, K+L)    
+    zeroR(blitz::Range(0, r-1), blitz::Range(N-r, N-1)) = A_blitz_lapack.transpose(1,0)(blitz::Range(0,r-1), blitz::Range(N-r,N-1));
+    // 2. Now we have to deal with C and S according to http://www.netlib.org/lapack/lug/node36.html
+    //    In the end C is m-by-r and is p-by-r, both are real, nonnegative and diagonal and C'C + S'S = I ,
+    //    They have the following structure 
+    //    COPY AND PASTE
+
+    //2.1 Preparing C
+    //           K  L
+    //   C = K ( I  0 )
+    //       L ( 0  C )
+    //   M-K-L ( 0  0 )
+    
+    // A - Identity part
+    if (K>0){
+      blitz::Array<double,2> I (K,K); I= 0;
+      bob::math::eye_(I);
+      C(blitz::Range(0, K-1), blitz::Range(0, K-1)) = I;
+    }
+    // B - diag(C) part. Here the C is LxL
+    // Swaping
+    bob::math::swap_(C_1d, iwork.get(), K, std::min(M,r));
+    blitz::Array<double,2> C_diag (L,L); C_diag = 0;
+    bob::math::diag(C_1d(blitz::Range(0,L-1)), C_diag);
+    C(blitz::Range(K, M-1), blitz::Range(K, K+L-1)) = C_diag;
+
+    //2.2 Preparing S
+    //           K  L
+    //D2 =   L ( 0  S )
+    //     P-L ( 0  0 )
+
+    // A - diag(S) part
+    // Swap
+    bob::math::swap_(S_1d, iwork.get(), K, std::min(M,r));
+    blitz::Array<double,2> S_diag (L,L); S_diag = 0;
+    bob::math::diag(S_1d(blitz::Range(0,L-1)), S_diag);
+    S(blitz::Range(0, L-1), blitz::Range(K, K+L-1)) = S_diag;
+
+  }
+  else{
+
+    //1. First we need the [0 R], which has the shape (N-K-L  K   M-K  K+L-M  ,  k + M-K  K+L-M  )
+
+    //A. First part of R is in A(1:M, N-K-L+1:N) 
+    zeroR(blitz::Range(0,M-1), blitz::Range(N-K-L, N-1)) = A_blitz_lapack.transpose(1,0)(blitz::Range(0,M-1) , blitz::Range(N-K-L,N-1));
+
+    //B. Second part of R is in B(M-K+1:L,N+M-K-L+1:N)
+    zeroR(blitz::Range(M, r - 1), blitz::Range(N-L+M-K,N-1)) = B_blitz_lapack.transpose(1,0)(blitz::Range(M-K, L-1) , blitz::Range(N+M-K-L, N-1));
+
+    //2. Now we have to deal with C and S according to http://www.netlib.org/lapack/lug/node36.html
+    //    In the end C is m-by-r and is p-by-r, both are real, nonnegative and diagonal and C'C + S'S = I ,
+    //    They have the following structure 
+    //    COPY AND PASTE
+
+    //2.1 Preparing C, where C=diag( ALPHA(K+1), ... , ALPHA(M) ),
+    //             K M-K K+L-M
+    //  D1 =   K ( I  0    0   )
+    //       M-K ( 0  C    0   )
+
+    // A - Identity part
+    if (K>0){
+      blitz::Array<double,2> I (K,K); I = 0;
+      bob::math::eye_(I);
+      C(blitz::Range(0, K-1), blitz::Range(0, K-1)) = I;
+    }
+
+    // B - diag(C) part
+    // Swaping
+    blitz::Array<double,1> C_1d_cropped(M-K); C_1d_cropped = 0;
+    C_1d_cropped = C_1d(blitz::Range(K,K+M-1));
+    bob::math::swap_(C_1d_cropped, iwork.get(), K, std::min(M,r));
+    blitz::Array<double,2> C_diag (M,M); C_diag = 0;
+    bob::math::diag(C_1d_cropped, C_diag);
+    C(blitz::Range(K,M-1), blitz::Range(K,M-1)) = C_diag;
+
+
+    //2.2 Preparing S
+    //               K M-K K+L-M
+    //  D2 =   M-K ( 0  S    0  )
+    //       K+L-M ( 0  0    I  )
+    //         P-L ( 0  0    0  )
+
+    // A - Identity part
+    if (K+L-M>0){
+      blitz::Array<double,2> I (K+L-M,K+L-M); I= 0;
+      bob::math::eye_(I);
+      S(blitz::Range(M-K,L-1), blitz::Range(M,K+L-1)) = I;
+    }
+
+    // B - diag(S) part
+    // Swaping
+    blitz::Array<double,1> S_1d_cropped(M-K); S_1d_cropped = 0;
+    S_1d_cropped = S_1d(blitz::Range(K,K+M-1));
+    
+    bob::math::swap_(S_1d_cropped, iwork.get(), K, std::min(M,r));
+    blitz::Array<double,2> S_diag (M,M); S_diag = 0;
+    bob::math::diag(S_1d_cropped, S_diag);
+    S(blitz::Range(0,M-K-1), blitz::Range(K,M-1)) = S_diag;
+  }
+
+  // Transposing U and V
+  blitz::Array<double,2> Ut(
+    bob::core::array::ccopy(const_cast<blitz::Array<double,2>&>(U).transpose(1,0)));
+  U = Ut;
+
+  blitz::Array<double,2> Vt(
+    bob::core::array::ccopy(const_cast<blitz::Array<double,2>&>(V).transpose(1,0)));
+  V = Vt;
+
+  // Swaping U
+  bob::math::swap_(U, iwork.get(), K, std::min(M,r));
+  // Swaping V
+  bob::math::swap_(V, iwork.get(), K, std::min(M,r));
+
+  //Computing X
+  bob::math::prod_(zeroR, Q, X);
+  blitz::Array<double,2> Xt(
+    bob::core::array::ccopy(const_cast<blitz::Array<double,2>&>(X).transpose(1,0)));
+  X = Xt;
+  bob::math::swap_(X, iwork.get(), K, std::min(M,r));
+
+} 
diff --git a/bob/math/gsvd.cpp b/bob/math/gsvd.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..aa20a8156b06b67c1e39b8fa278d5230640ab81d
--- /dev/null
+++ b/bob/math/gsvd.cpp
@@ -0,0 +1,77 @@
+/**
+ * @date Tue Jan 10 21:14 2016 +0100
+ * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch>
+ *
+ * @brief Binds the Generalized SVD
+ */
+
+#include "gsvd.h"
+#include <bob.blitz/cppapi.h>
+#include <bob.blitz/cleanup.h>
+#include <bob.math/gsvd.h>
+
+PyObject* py_gsvd (PyObject*, PyObject* args, PyObject* kwds) {
+
+  /* Parses input arguments in a single shot */
+  static const char* const_kwlist[] = { "A", "B", 0 /* Sentinel */ };
+  static char** kwlist = const_cast<char**>(const_kwlist);
+
+  PyBlitzArrayObject* A = 0;
+  PyBlitzArrayObject* B = 0;
+
+  if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&O&", kwlist, 
+                                   &PyBlitzArray_Converter, &A,
+                                   &PyBlitzArray_Converter, &B
+      ))
+      return 0;
+
+   auto A_ = make_safe(A);
+   auto B_ = make_safe(B);
+
+  if (A->ndim != 2 || A->type_num != NPY_FLOAT64) {
+    PyErr_Format(PyExc_TypeError, "`A` matrix only supports 2D 64-bit float array");
+    return 0;
+  }
+
+  if (B->ndim != 2 || B->type_num != NPY_FLOAT64) {
+    PyErr_Format(PyExc_TypeError, "`B` matrix only supports 2D 64-bit float array");
+    return 0;
+  }
+
+  auto A_bz = PyBlitzArrayCxx_AsBlitz<double,2>(A);
+  auto B_bz = PyBlitzArrayCxx_AsBlitz<double,2>(B);
+
+ const int M = A_bz->extent(0);
+ const int N = A_bz->extent(1);
+ const int P = B_bz->extent(0);
+
+  // Creating the output matrices
+  blitz::Array<double,2> U(M, M); U=0;
+  blitz::Array<double,2> V(P, P); V=0;
+  blitz::Array<double,2> Q(N, N); Q=0;
+  blitz::Array<double,2> zeroR(N, N); zeroR=0;
+  blitz::Array<double,2> X(N, N); X=0;
+  blitz::Array<double,2> C(N, N); C=0;
+  blitz::Array<double,2> S(N, N); S=0;
+  
+
+  try {
+    bob::math::gsvd(*A_bz,*B_bz,U,V,zeroR,Q,X,C,S);
+    return Py_BuildValue("NNNNN",
+                         PyBlitzArrayCxx_AsConstNumpy(U),
+                         PyBlitzArrayCxx_AsConstNumpy(V),
+                         PyBlitzArrayCxx_AsConstNumpy(X),
+                         PyBlitzArrayCxx_AsConstNumpy(C),
+                         PyBlitzArrayCxx_AsConstNumpy(S));
+  }
+  catch (std::exception& e) {
+    PyErr_SetString(PyExc_RuntimeError, e.what());
+  }
+  catch (...) {
+    PyErr_SetString(PyExc_RuntimeError, "norminv failed: unknown exception caught");
+  }
+
+
+  return 0;
+
+}
diff --git a/bob/math/gsvd.h b/bob/math/gsvd.h
new file mode 100644
index 0000000000000000000000000000000000000000..cc38f9fc38360e041536a83cca82c2e6ad3f33e8
--- /dev/null
+++ b/bob/math/gsvd.h
@@ -0,0 +1,10 @@
+/**
+ * @author Andre Anjos <andre.anjos@idiap.ch>
+ * @date Thu  5 Dec 12:01:57 2013 
+ *
+ * @brief Declaration of components relevant for main.cpp
+ */
+
+#include <Python.h>
+
+PyObject* py_gsvd(PyObject*, PyObject* args, PyObject* kwds);
diff --git a/bob/math/include/bob.math/gsvd.h b/bob/math/include/bob.math/gsvd.h
new file mode 100644
index 0000000000000000000000000000000000000000..4da252418ec7ffde991138cc8639dafb6e0be3fb
--- /dev/null
+++ b/bob/math/include/bob.math/gsvd.h
@@ -0,0 +1,107 @@
+/**
+ * @date Tue Jan 10 21:14 2016 +0100
+ * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch>
+ *
+ * @brief This file defines a function to determine the SVD decomposition
+ * a 2D blitz array using LAPACK.
+ *
+ * Copyright (C) Idiap Research Institute, Martigny, Switzerland
+ */
+
+#ifndef BOB_MATH_GSVD_H
+#define BOB_MATH_GSVD_H
+
+#include <blitz/array.h>
+
+namespace bob { namespace math {
+/**
+ * @ingroup MATH
+ * @{
+ */
+
+/**
+ * @brief Function which performs the Generalized Singular Value Decomposition
+ *   using the 'simple' driver routine dggsvd3 of LAPACK.
+ *   Just remembering that given A and B as input we have:
+ *
+ *    A = U C X and B = V S X
+ *    where X = [0,R]Q^T
+ *
+ *
+ *
+ * @warning The output blitz::array sigma should have the correct 
+ *   size, with zero base index. Checks are performed.
+ * @param A The A matrix to decompose (size MxP)
+ * @param B The B matrix to decompose (size NxP)
+ * @warning A and B must have the same number of columns, but may have different numbers of rows. If A is m-by-p and B is n-by-p, then U is m-by-m, V is n-by-n, X is p-by-q, C is m-by-q
+ *          and  S is n-by-q, where q = min(m+n,p).
+ * @param U The U matrix (MxM)
+ * @param V The V matrix (NxN)
+ * @param zeroR The X matrix (rxN) 
+ * @param Q The Q matrix (Q) 
+ * @param C The C matrix (MxQ)
+ * @param S The S matrix (NxQ)  
+ */
+void gsvd(blitz::Array<double,2>& A,
+         blitz::Array<double,2>& B,
+
+         blitz::Array<double,2>& U,
+         blitz::Array<double,2>& V,
+         blitz::Array<double,2>& zeroR,
+         blitz::Array<double,2>& Q,
+         blitz::Array<double,2>& X,
+         blitz::Array<double,2>& C,
+         blitz::Array<double,2>& S
+         );
+  /**
+   * @brief Swaping using the LAPACK variable iWork
+   *        http://www.netlib.org/lapack/explore-html/d1/d7e/group__double_g_esing_ga4a187519e5c71da3b3f67c85e9baf0f2.html#ga4a187519e5c71da3b3f67c85e9baf0f2
+   *
+   *        @param A 1D Matrix
+   *        @param indexes Sorting information
+   *        @param n number of operations
+   */
+    template<typename T>
+        void swap_(blitz::Array<T,1>& A, int* indexes, int begin, int end) {
+            T aux = 0;
+            int fortran_index = 0;
+            for (int i=0; i<A.extent(0); i++){
+                fortran_index = indexes[i]-1;
+                aux = A(i);
+                A(i) = A(fortran_index);
+                A(fortran_index) = aux;
+            }
+        }
+
+
+  /**
+   * @brief Swaping using the LAPACK variable iWork
+   *        http://www.netlib.org/lapack/explore-html/d1/d7e/group__double_g_esing_ga4a187519e5c71da3b3f67c85e9baf0f2.html#ga4a187519e5c71da3b3f67c85e9baf0f2
+   *
+   *        @param A D Matrix
+   *        @param indexes Sorting information
+   *        @param n number of operations
+   */
+    template<typename T>
+        void swap_(blitz::Array<T,2>& A, int* indexes, int begin, int end) {
+            blitz::Array<T,1> aux(A.extent(1)); aux = 0;
+            int fortran_index = 0;
+            for (int i=begin; i<end; i++){
+                fortran_index = indexes[i]-1;
+                aux = A(blitz::Range::all(), i);
+                A(blitz::Range::all(), i) = A(blitz::Range::all(), fortran_index);
+                A(blitz::Range::all(), fortran_index) = aux;
+            }
+        }
+
+
+
+
+
+
+/**
+ * @}
+ */
+}}
+
+#endif /* BOB_MATH_GSVD_H */
diff --git a/bob/math/main.cpp b/bob/math/main.cpp
index 4f70e351c17edf0a8e537a44af1b5479eb92aecc..e78fe28de0135d7fdf1e9a78f94f803175404202 100644
--- a/bob/math/main.cpp
+++ b/bob/math/main.cpp
@@ -20,6 +20,7 @@
 #include "norminv.h"
 #include "scatter.h"
 #include "lp_interior_point.h"
+#include "gsvd.h"
 
 static bob::extension::FunctionDoc s_histogram_intersection = bob::extension::FunctionDoc(
     "histogram_intersection",
@@ -355,6 +356,26 @@ static bob::extension::FunctionDoc s_scatters_nocheck = bob::extension::Function
 ;
 
 
+static bob::extension::FunctionDoc s_gsvd = bob::extension::FunctionDoc(
+  "gsvd",
+  "Computes the Generalized SVD",
+  "Computes the Generalized SVD. The output of this function is similar with the one found in Matlab\n"
+  "[U,V,X,C,S] = gsvd(A,B) returns unitary matrices :math:`U` and :math:`V`, the square matrix :math:`X` (which is :math:`[0 R] Q^{T}`), and nonnegative diagonal matrices :math:`C` and :math:`S` such that:\n\n"
+  ".. math:: C^{T}C + S^{T}S = I \n"
+  ".. math:: A = (XC^{T}U^{T})^{T}\n"
+  ".. math:: B = (XS^{T}V^{T})^{T}\n"
+  )
+  .add_prototype("A, B", "")
+  .add_parameter("A", "[array_like (float, 2D)]", "Must be :math:`m \\times n`")
+  .add_parameter("B", "[array_like (float, 2D)]", "Must be :math:`p \\times n`")
+  .add_return("U", "[array_like (float, 2D)]", "Contains a :math:`m \\times m` orthogonal matrix.")
+  .add_return("V", "[array_like (float, 2D)]", "Contains a :math:`n \\times n` orthogonal matrix.")
+  .add_return("X", "[array_like (float, 2D)]", "Contains a :math:`p \\times q` matrix, where :math:`p=\\min(m+n,p)` and :math:`X=[0, R] Q^{T}` (Check LAPACK documentation).")
+  .add_return("C", "[array_like (float, 2D)]", "Contains a :math:`p \\times q` matrix.")
+  .add_return("S", "[array_like (float, 2D)]", "Contains a :math:`n \\times q` matrix.")
+;
+
+
 static PyMethodDef module_methods[] = {
     {
       s_histogram_intersection.name(),
@@ -470,6 +491,13 @@ static PyMethodDef module_methods[] = {
       METH_VARARGS|METH_KEYWORDS,
       s_scatters_nocheck.doc()
     },
+    {
+      s_gsvd.name(),
+      (PyCFunction)py_gsvd,
+      METH_VARARGS|METH_KEYWORDS,
+      s_gsvd.doc()
+    },
+
     {0}  /* Sentinel */
 };
 
diff --git a/bob/math/test_gsvd.py b/bob/math/test_gsvd.py
new file mode 100644
index 0000000000000000000000000000000000000000..1a93b3ca61ec83afe890f6a537f39fe5fe9b7dac
--- /dev/null
+++ b/bob/math/test_gsvd.py
@@ -0,0 +1,67 @@
+#!/usr/bin/env python
+# Tiago de Freitas Pereira <tiago.pereira@idiap.ch>
+# Sun Jan  15 19:12:43 CET 2017
+#
+
+"""
+Tests GSVD
+
+ Basically these tests test the GSVD relation.
+ Given 2 matrices A and B  GSVD(A,B) = [U,V,X,C,S] where,
+
+  A= (X * C.T * U^T)^T and 
+  B= (X * S.T * V^T)^T and
+  
+  C**2 + S**2 = 1
+
+"""
+
+import bob.math
+import numpy
+import nose.tools
+numpy.random.seed(10)
+
+
+def gsvd_relations(A,B):
+  [U,V,X,C,S] = bob.math.gsvd(A, B)
+
+  # Cheking the relation  C**2 + S**2 = 1
+  I = numpy.eye(A.shape[1])
+  I_check = numpy.dot(C.T, C) + numpy.dot(S.T, S)
+  nose.tools.eq_( (abs(I-I_check) < 1e-10).all(), True )
+
+  # Cheking the relation A= (X * C.T * U^T)^T
+  A_check = numpy.dot(numpy.dot(X,C.T),U.T).T
+  nose.tools.eq_( (abs(A-A_check) < 1e-10).all(), True )
+
+  # Cheking the relation B= (X * S.T * V^T)^T 
+  B_check = numpy.dot(numpy.dot(X,S.T),V.T).T
+  nose.tools.eq_( (abs(B-B_check) < 1e-10).all(), True )
+
+  del U,V,X,C,S
+
+
+def test_first_case():
+  """
+  Testing the first scenario:
+  M-K-L >= 0 (check http://www.netlib.org/lapack/explore-html/d1/d7e/group__double_g_esing_ga4a187519e5c71da3b3f67c85e9baf0f2.html#ga4a187519e5c71da3b3f67c85e9baf0f2)  
+  """
+
+  A = numpy.random.rand(10,10)
+  B = numpy.random.rand(790,10)
+
+  gsvd_relations(A, B)
+
+
+def test_second_case():
+  """
+  Testing the second scenario:
+  M-K-L < 0 (check http://www.netlib.org/lapack/explore-html/d1/d7e/group__double_g_esing_ga4a187519e5c71da3b3f67c85e9baf0f2.html#ga4a187519e5c71da3b3f67c85e9baf0f2)  
+  """
+
+  A = numpy.random.rand(4,5)
+  B = numpy.random.rand(11,5)
+
+  gsvd_relations(A, B)
+
+
diff --git a/doc/py_api.rst b/doc/py_api.rst
index aa88ba3b3870ab08c62daa27c09b409451c0dbf9..252233d3e0a009b637dd39cf2de4cc17448f1c99 100644
--- a/doc/py_api.rst
+++ b/doc/py_api.rst
@@ -15,7 +15,19 @@ Summary
   bob.math.LPInteriorPoint
   bob.math.LPInteriorPointShortstep
   bob.math.LPInteriorPointLongstep
-
+  bob.math.chi_square
+  bob.math.gsvd
+  bob.math.histogram_intersection
+  bob.math.kullback_leibler
+  bob.math.linsolve
+  bob.math.linsolve_cg_sympos
+  bob.math.linsolve_sympos  
+  bob.math.norminv  
+  bob.math.pavx
+  bob.math.pavxWidth
+  bob.math.pavxWidthHeight
+  bob.math.scatter
+  bob.math.scatters
 
 Details
 .......
diff --git a/setup.py b/setup.py
index e93ec9260100e1d2ecba7d869f29de8004e20326..8935cc7c6e3b2a1c9a33b089c5438f46c98379ae 100644
--- a/setup.py
+++ b/setup.py
@@ -179,6 +179,7 @@ setup(
           "bob/math/cpp/pavx.cpp",
           "bob/math/cpp/pinv.cpp",
           "bob/math/cpp/svd.cpp",
+          "bob/math/cpp/gsvd.cpp",
           "bob/math/cpp/sqrtm.cpp",
         ],
         version = version,
@@ -195,6 +196,7 @@ setup(
           "bob/math/linsolve.cpp",
           "bob/math/pavx.cpp",
           "bob/math/norminv.cpp",
+          "bob/math/gsvd.cpp",
           "bob/math/scatter.cpp",
           "bob/math/lp_interior_point.cpp",
           "bob/math/main.cpp",