# Path: echotorch/utils/error_measures.py
# def nrmse(outputs, targets):
#     """
#     Normalized root-mean square error
#     :param outputs: Module's outputs
#     :param targets: Target signal to be learned
#     :return: Normalized root-mean square deviation
#     """
#     # Flatten tensors
#     outputs = outputs.view(outputs.nelement())
#     targets = targets.view(targets.nelement())
# 
#     # Check dim
#     if outputs.size() != targets.size():
#         raise ValueError(u"Ouputs and targets tensors don have the same number of elements")
#     # end if
# 
#     # Normalization with N-1
#     var = torch.std(targets) ** 2
# 
#     # Error
#     error = (targets - outputs) ** 2
# 
#     # Return
#     return float(math.sqrt(torch.mean(error) / var))
# 
# def generalized_squared_cosine(Sa, Ua, Sb, Ub):
#     """
#     Generalized square cosine
#     :param Sa:
#     :param Ua:
#     :param Sb:
#     :param Ub:
#     :return:
#     """
#     # Diag
#     Sa = torch.diag(Sa)
#     Sb = torch.diag(Sb)
# 
#     # Compute Va and Vb
#     Va = torch.sqrt(Sa).mm(Ua.t())
#     Vb = Ub.mm(torch.sqrt(Sb))
# 
#     # Vab
#     Vab = Va.mm(Vb)
# 
#     # Num
#     num = torch.pow(torch.norm(Vab), 2)
# 
#     # Den
#     den = torch.norm(torch.diag(Sa), p=2) * torch.norm(torch.diag(Sb), p=2)
# 
#     return num / den


import torch
import numpy as np
import numpy.linalg as lin
from .error_measures import nrmse, generalized_squared_cosine
from scipy.interpolate import interp1d
    # Generated interpolated pattern length
    L = generated_pattern_int.shape[0]

    # Truth interpolated pattern length
    M = truth_pattern_int.shape[0]

    # Save L2 distance for each phase shift
    phase_matches = np.zeros(L - M)

    # For each phase shift
    for phases_hift in range(L - M):
        phase_matches[phases_hift] = lin.norm(truth_pattern_int - generated_pattern_int[phases_hift:phases_hift + M])
    # end for

    # Best match
    max_ind = int(np.argmax(-phase_matches))

    # Get the position in the original signal
    coarse_max_ind = int(np.ceil(max_ind / interpolation_rate))

    # Get the generated output matching the original signal
    generated_aligned = generated_pattern_int[
        np.arange(max_ind, max_ind + interpolation_rate * truth_length, interpolation_rate)
    ]

    return max_ind, coarse_max_ind, torch.from_numpy(generated_aligned).view(-1, 1)
# end align_pattern


# Find phase shift

# Path: echotorch/utils/error_measures.py
# def nrmse(outputs, targets):
#     """
#     Normalized root-mean square error
#     :param outputs: Module's outputs
#     :param targets: Target signal to be learned
#     :return: Normalized root-mean square deviation
#     """
#     # Flatten tensors
#     outputs = outputs.view(outputs.nelement())
#     targets = targets.view(targets.nelement())
# 
#     # Check dim
#     if outputs.size() != targets.size():
#         raise ValueError(u"Ouputs and targets tensors don have the same number of elements")
#     # end if
# 
#     # Normalization with N-1
#     var = torch.std(targets) ** 2
# 
#     # Error
#     error = (targets - outputs) ** 2
# 
#     # Return
#     return float(math.sqrt(torch.mean(error) / var))
# 
# def generalized_squared_cosine(Sa, Ua, Sb, Ub):
#     """
#     Generalized square cosine
#     :param Sa:
#     :param Ua:
#     :param Sb:
#     :param Ub:
#     :return:
#     """
#     # Diag
#     Sa = torch.diag(Sa)
#     Sb = torch.diag(Sb)
# 
#     # Compute Va and Vb
#     Va = torch.sqrt(Sa).mm(Ua.t())
#     Vb = Ub.mm(torch.sqrt(Sb))
# 
#     # Vab
#     Vab = Va.mm(Vb)
# 
#     # Num
#     num = torch.pow(torch.norm(Vab), 2)
# 
#     # Den
#     den = torch.norm(torch.diag(Sa), p=2) * torch.norm(torch.diag(Sb), p=2)
# 
#     return num / den


# Path: echotorch/utils/utility_functions.py
import torch
import numpy as np
import numpy.linalg as lin
from .error_measures import nrmse, generalized_squared_cosine
from scipy.interpolate import interp1d

    # Generated interpolated pattern length
    L = generated_pattern_int.shape[0]

    # Truth interpolated pattern length
    M = truth_pattern_int.shape[0]

    # Save L2 distance for each phase shift
    phase_matches = np.zeros(L - M)

    # For each phase shift
    for phases_hift in range(L - M):
        phase_matches[phases_hift] = lin.norm(truth_pattern_int - generated_pattern_int[phases_hift:phases_hift + M])
    # end for

    # Best match
    max_ind = int(np.argmax(-phase_matches))

    # Get the position in the original signal
    coarse_max_ind = int(np.ceil(max_ind / interpolation_rate))

    # Get the generated output matching the original signal
    generated_aligned = generated_pattern_int[
        np.arange(max_ind, max_ind + interpolation_rate * truth_length, interpolation_rate)
    ]

    return max_ind, coarse_max_ind, torch.from_numpy(generated_aligned).view(-1, 1)
# end align_pattern


# Find phase shift

def find_phase_shift(p, y, interpolation_rate, error_measure=nrmse):