# Path: fdasrsf/fPLS.py
# def pls_svd(time, qf, qg, no, alpha=0.0):
#     """
#     This function computes the partial least squares using SVD
# 
#     :param time: vector describing time samples
#     :param qf: numpy ndarray of shape (M,N) of N functions with M samples
#     :param qg: numpy ndarray of shape (M,N) of N functions with M samples
#     :param no: number of components
#     :param alpha: amount of smoothing (Default = 0.0 i.e., none)
# 
#     :rtype: numpy ndarray
#     :return wqf: f weight function
#     :return wqg: g weight function
#     :return alpha: smoothing value
#     :return values: singular values
# 
#     """
#     binsize = np.diff(time)
#     binsize = binsize.mean()
# 
#     Kfg = np.cov(qf, qg)
#     Kfg = Kfg[0:qf.shape[0], qf.shape[0]:Kfg.shape[0]]
#     nx = Kfg.shape[0]
#     D4x = diffop(nx, binsize)
#     values, Lmat, Mmat = geigen(Kfg, np.eye(nx) + alpha * D4x, np.eye(nx) + alpha * D4x)
#     wf = Lmat[:, 0:no]
#     wg = Mmat[:, 0:no]
# 
#     for ii in range(0, no):
#         wf[:, ii] = wf[:, ii] / np.sqrt(innerprod_q(time, wf[:, ii], wf[:, ii]))
#         wg[:, ii] = wg[:, ii] / np.sqrt(innerprod_q(time, wg[:, ii], wg[:, ii]))
# 
#     wqf = wf
#     wqg = wg
# 
#     N = qf.shape[1]
#     rfi = np.zeros(N)
#     rgi = np.zeros(N)
# 
#     for l in range(0, N):
#         rfi[l] = innerprod_q(time, qf[:, l], wqf[:, 0])
#         rgi[l] = innerprod_q(time, qg[:, l], wqg[:, 0])
# 
#     cost = np.cov(rfi, rgi)[1, 0]
# 
#     return wqf, wqg, alpha, values, cost


import numpy as np
import matplotlib.pyplot as plt
import fdasrsf.utility_functions as uf
import fdasrsf.plot_style as plot
import fpls_warp as fpls
import collections
from scipy.integrate import trapz, cumtrapz
from scipy.linalg import svd
from numpy.linalg import norm
from joblib import Parallel, delayed
from fdasrsf.fPLS import pls_svd
        wqf_diff[itr] = np.sqrt(sum(wqf[:, itr + 1] - wqf[:, itr]) ** 2)

        rfi = np.zeros(N)
        rgi = np.zeros(N)

        for l in range(0, N):
            rfi[l] = uf.innerprod_q(time, qfi[:, l, itr + 1], wqf[:, itr + 1])
            rgi[l] = uf.innerprod_q(time, qgi[:, l, itr + 1], wqg[:, itr + 1])

        cost[itr] = np.cov(rfi, rgi)[1, 0]

        if itr > 1:
            cost_diff = cost[itr] - cost[itr - 1]

        print("Iteration: %d - Diff Value: %f - %f" % (itr + 1, wqf_diff[itr],
                                                       cost[itr]))
        if wqf_diff[itr] < 1e-1 or abs(cost_diff) < 1e-3:
            break

        itr += 1

    cost = cost[0:(itr + 1)]

    # Aligned data & stats
    fn = fi[:, :, itr + 1]
    gn = gi[:, :, itr + 1]
    qfn = qfi[:, :, itr + 1]
    qf0 = qfi[:, :, 0]
    qgn = qgi[:, :, itr + 1]
    qg0 = qgi[:, :, 0]

# Path: fdasrsf/fPLS.py
# def pls_svd(time, qf, qg, no, alpha=0.0):
#     """
#     This function computes the partial least squares using SVD
# 
#     :param time: vector describing time samples
#     :param qf: numpy ndarray of shape (M,N) of N functions with M samples
#     :param qg: numpy ndarray of shape (M,N) of N functions with M samples
#     :param no: number of components
#     :param alpha: amount of smoothing (Default = 0.0 i.e., none)
# 
#     :rtype: numpy ndarray
#     :return wqf: f weight function
#     :return wqg: g weight function
#     :return alpha: smoothing value
#     :return values: singular values
# 
#     """
#     binsize = np.diff(time)
#     binsize = binsize.mean()
# 
#     Kfg = np.cov(qf, qg)
#     Kfg = Kfg[0:qf.shape[0], qf.shape[0]:Kfg.shape[0]]
#     nx = Kfg.shape[0]
#     D4x = diffop(nx, binsize)
#     values, Lmat, Mmat = geigen(Kfg, np.eye(nx) + alpha * D4x, np.eye(nx) + alpha * D4x)
#     wf = Lmat[:, 0:no]
#     wg = Mmat[:, 0:no]
# 
#     for ii in range(0, no):
#         wf[:, ii] = wf[:, ii] / np.sqrt(innerprod_q(time, wf[:, ii], wf[:, ii]))
#         wg[:, ii] = wg[:, ii] / np.sqrt(innerprod_q(time, wg[:, ii], wg[:, ii]))
# 
#     wqf = wf
#     wqg = wg
# 
#     N = qf.shape[1]
#     rfi = np.zeros(N)
#     rgi = np.zeros(N)
# 
#     for l in range(0, N):
#         rfi[l] = innerprod_q(time, qf[:, l], wqf[:, 0])
#         rgi[l] = innerprod_q(time, qg[:, l], wqg[:, 0])
# 
#     cost = np.cov(rfi, rgi)[1, 0]
# 
#     return wqf, wqg, alpha, values, cost


# Path: fdasrsf/time_warping.py
import numpy as np
import matplotlib.pyplot as plt
import fdasrsf.utility_functions as uf
import fdasrsf.plot_style as plot
import fpls_warp as fpls
import collections
from scipy.integrate import trapz, cumtrapz
from scipy.linalg import svd
from numpy.linalg import norm
from joblib import Parallel, delayed
from fdasrsf.fPLS import pls_svd

        wqf_diff[itr] = np.sqrt(sum(wqf[:, itr + 1] - wqf[:, itr]) ** 2)

        rfi = np.zeros(N)
        rgi = np.zeros(N)

        for l in range(0, N):
            rfi[l] = uf.innerprod_q(time, qfi[:, l, itr + 1], wqf[:, itr + 1])
            rgi[l] = uf.innerprod_q(time, qgi[:, l, itr + 1], wqg[:, itr + 1])

        cost[itr] = np.cov(rfi, rgi)[1, 0]

        if itr > 1:
            cost_diff = cost[itr] - cost[itr - 1]

        print("Iteration: %d - Diff Value: %f - %f" % (itr + 1, wqf_diff[itr],
                                                       cost[itr]))
        if wqf_diff[itr] < 1e-1 or abs(cost_diff) < 1e-3:
            break

        itr += 1

    cost = cost[0:(itr + 1)]

    # Aligned data & stats
    fn = fi[:, :, itr + 1]
    gn = gi[:, :, itr + 1]
    qfn = qfi[:, :, itr + 1]
    qf0 = qfi[:, :, 0]
    qgn = qgi[:, :, itr + 1]
    qg0 = qgi[:, :, 0]

    wqfn, wqgn, alpha, values, costmp = pls_svd(time, qfn, qgn, comps, 0)