```// Copyright (C) 2013  Davis E. King (davis@dlib.net)
#ifndef DLIB_CCA_hh_
#define DLIB_CCA_hh_

#include "cca_abstract.h"
#include "../algs.h"
#include "../matrix.h"
#include "../sparse_vector.h"
#include "random_subset_selector.h"

namespace dlib
{

// ----------------------------------------------------------------------------------------

template <
typename T
>
matrix<typename T::type,0,1> compute_correlations (
const matrix_exp<T>& L,
const matrix_exp<T>& R
)
{
DLIB_ASSERT( L.size() > 0 && R.size() > 0 && L.nr() == R.nr(),
"\t matrix compute_correlations()"
<< "\n\t Invalid inputs were given to this function."
<< "\n\t L.size(): " << L.size()
<< "\n\t R.size(): " << R.size()
<< "\n\t L.nr():   " << L.nr()
<< "\n\t R.nr():   " << R.nr()
);

typedef typename T::type type;
matrix<type> A, B, C;
A = diag(trans(R)*L);
B = sqrt(diag(trans(L)*L));
C = sqrt(diag(trans(R)*R));
A = pointwise_multiply(A , reciprocal(pointwise_multiply(B,C)));
return A;
}

// ----------------------------------------------------------------------------------------

template <
typename matrix_type,
typename T
>
matrix<T,0,1> impl_cca (
const matrix_type& L,
const matrix_type& R,
matrix<T>& Ltrans,
matrix<T>& Rtrans,
unsigned long num_correlations,
unsigned long extra_rank,
unsigned long q,
unsigned long num_output_correlations,
double regularization
)
{
matrix<T> Ul, Vl;
matrix<T> Ur, Vr;
matrix<T> U, V;
matrix<T,0,1> Dr, Dl, D;

// Note that we add a few more singular vectors in because it helps improve the
// final results if we run this part with a little higher rank than the final SVD.
svd_fast(L, Ul, Dl, Vl, num_correlations+extra_rank, q);
svd_fast(R, Ur, Dr, Vr, num_correlations+extra_rank, q);

// Zero out singular values that are essentially zero so they don't cause numerical
// difficulties in the code below.
const double eps = std::numeric_limits<T>::epsilon()*std::max(max(Dr),max(Dl))*100;
Dl = round_zeros(Dl+regularization,eps);
Dr = round_zeros(Dr+regularization,eps);

// This matrix is really small so we can do a normal full SVD on it.  Note that we
// also throw away the columns of Ul and Ur corresponding to zero singular values.
svd3(diagm(Dl>0)*tmp(trans(Ul)*Ur)*diagm(Dr>0), U, D, V);

// now throw away extra columns of the transformations.  We do this in a way
// that keeps the directions that have the highest correlations.
matrix<T,0,1> temp = D;
rsort_columns(U, temp);
rsort_columns(V, D);
U = colm(U, range(0, num_output_correlations-1));
V = colm(V, range(0, num_output_correlations-1));
D = rowm(D, range(0, num_output_correlations-1));

Ltrans = Vl*inv(diagm(Dl))*U;
Rtrans = Vr*inv(diagm(Dr))*V;

// Note that the D matrix contains the correlation values for the transformed
// vectors.  However, when the L and R matrices have rank higher than
// num_correlations+extra_rank then the values in D become only approximate.
return D;
}

// ----------------------------------------------------------------------------------------

template <typename T>
matrix<T,0,1> cca (
const matrix<T>& L,
const matrix<T>& R,
matrix<T>& Ltrans,
matrix<T>& Rtrans,
unsigned long num_correlations,
unsigned long extra_rank = 5,
unsigned long q = 2,
double regularization = 0
)
{
DLIB_ASSERT( num_correlations > 0 && L.size() > 0 && R.size() > 0 && L.nr() == R.nr() &&
regularization >= 0,
"\t matrix cca()"
<< "\n\t Invalid inputs were given to this function."
<< "\n\t num_correlations: " << num_correlations
<< "\n\t regularization:   " << regularization
<< "\n\t L.size(): " << L.size()
<< "\n\t R.size(): " << R.size()
<< "\n\t L.nr():   " << L.nr()
<< "\n\t R.nr():   " << R.nr()
);

using std::min;
const unsigned long n = min(num_correlations, (unsigned long)min(R.nr(),min(L.nc(), R.nc())));
return impl_cca(L,R,Ltrans, Rtrans, num_correlations, extra_rank, q, n, regularization);
}

// ----------------------------------------------------------------------------------------

template <typename sparse_vector_type, typename T>
matrix<T,0,1> cca (
const std::vector<sparse_vector_type>& L,
const std::vector<sparse_vector_type>& R,
matrix<T>& Ltrans,
matrix<T>& Rtrans,
unsigned long num_correlations,
unsigned long extra_rank = 5,
unsigned long q = 2,
double regularization = 0
)
{
DLIB_ASSERT( num_correlations > 0 && L.size() == R.size() &&
max_index_plus_one(L) > 0 && max_index_plus_one(R) > 0 &&
regularization >= 0,
"\t matrix cca()"
<< "\n\t Invalid inputs were given to this function."
<< "\n\t num_correlations: " << num_correlations
<< "\n\t regularization:   " << regularization
<< "\n\t L.size(): " << L.size()
<< "\n\t R.size(): " << R.size()
<< "\n\t max_index_plus_one(L):   " << max_index_plus_one(L)
<< "\n\t max_index_plus_one(R):   " << max_index_plus_one(R)
);

using std::min;
const unsigned long n = min(max_index_plus_one(L), max_index_plus_one(R));
const unsigned long num_output_correlations = min(num_correlations, std::min<unsigned long>(R.size(),n));
return impl_cca(L,R,Ltrans, Rtrans, num_correlations, extra_rank, q, num_output_correlations, regularization);
}

// ----------------------------------------------------------------------------------------

template <typename sparse_vector_type, typename Rand_type, typename T>
matrix<T,0,1> cca (
const random_subset_selector<sparse_vector_type,Rand_type>& L,
const random_subset_selector<sparse_vector_type,Rand_type>& R,
matrix<T>& Ltrans,
matrix<T>& Rtrans,
unsigned long num_correlations,
unsigned long extra_rank = 5,
unsigned long q = 2
)
{
return cca(L.to_std_vector(), R.to_std_vector(), Ltrans, Rtrans, num_correlations, extra_rank, q);
}

// ----------------------------------------------------------------------------------------

}

#endif // DLIB_CCA_hh_

```