# Path: sprocket/util/distance.py
# def melcd(array1, array2):
#     """Calculate mel-cepstrum distortion
# 
#     Calculate mel-cepstrum distortion between the arrays.
#     This function assumes the shapes of arrays are same.
# 
#     Parameters
#     ----------
#     array1, array2 : array, shape (`T`, `dim`) or shape (`dim`)
#         Arrays of original and target.
# 
#     Returns
#     -------
#     mcd : scala, number > 0
#         Scala of mel-cepstrum distortion
# 
#     """
#     if array1.shape != array2.shape:
#         raise ValueError(
#             "The shapes of both arrays are different \
#             : {} / {}".format(array1.shape, array2.shape))
# 
#     if array1.ndim == 2:
#         # array based melcd calculation
#         diff = array1 - array2
#         mcd = 10.0 / np.log(10) \
#             * np.mean(np.sqrt(2.0 * np.sum(diff ** 2, axis=1)))
#     elif array1.ndim == 1:
#         diff = array1 - array2
#         mcd = 10.0 / np.log(10) * np.sqrt(2.0 * np.sum(diff ** 2))
#     else:
#         raise ValueError("Dimension mismatch")
# 
#     return mcd
# 
# Path: sprocket/util/twf.py
# def estimate_twf(orgdata, tardata, distance='melcd', fast=True, otflag=None):
#     """time warping function estimator
# 
#     Parameters
#     ---------
#     orgdata : array, shape(`T_org`, `dim`)
#         Array of source feature
#     tardata : array, shape(`T_tar`, `dim`)
#         Array of target feature
#     distance : str, optional
#         distance function
#         `melcd` : mel-cepstrum distortion
#     fast : bool, optional
#         Use fastdtw instead of dtw
#         Default set to `True`
#     otflag : str,
#         Perform alignment into either original or target length
#         `org` : align into original length
#         `tar` : align into target length
#         Default set to None
# 
#     Returns
#     ---------
#     twf : array, shape(`2`, `T`)
#         Time warping function between original and target
#     """
# 
#     if distance == 'melcd':
#         def distance_func(x, y): return melcd(x, y)
#     else:
#         raise ValueError('other distance metrics than melcd does not support.')
# 
#     if otflag is None:
#         # use dtw or fastdtw
#         if fast:
#             _, path = fastdtw(orgdata, tardata, dist=distance_func)
#             twf = np.array(path).T
#         else:
#             _, _, _, twf = dtw(orgdata, tardata, distance_func)
#     else:
#         # use dtw_c to align target/original feature vector
#         ldim = orgdata.shape[1] - 1
#         if otflag == 'org':
#             _, twf, _, _ = dtw_c.dtw_org_to_trg(tardata, orgdata,
#                                                 0, ldim, 5.0, 100.0, 100.0)
#         else:
#             _, twf, _, _ = dtw_c.dtw_org_to_trg(orgdata, tardata,
#                                                 0, ldim, 5.0, 100.0, 100.0)
#         twf[:, 1] = np.array(range(twf.shape[0]))  # replace target index by frame number
#         twf = twf.T
#         if otflag == 'org':
#             twf = twf[::-1, :]  # swap cols
#             assert twf.shape[0] == orgdata.shape[0]
#         else:
#             assert twf.shape[1] == tardata.shape[0]
# 
#     return twf


import unittest
import numpy as np
import pysptk
from sprocket.util import melcd, estimate_twf

def get_random_peseudo_mcep(order=24, alpha=0.42):
    T, N = 100, 513
    frames = np.random.rand(T, N) * pysptk.blackman(N)
    mc = pysptk.mcep(frames, order=order, alpha=alpha)
    return mc


class DistanceTest(unittest.TestCase):

    def test_melcd(self):
        org = get_random_peseudo_mcep()
        tar = get_random_peseudo_mcep()

        # perform dtw for mel-cd function test
        def distance_func(x, y): return melcd(x, y)
        twf = estimate_twf(org, tar, fast=True)
        twf = estimate_twf(org, tar, fast=False)

        # align org and tar
        orgmod = org[twf[0]]
        tarmod = tar[twf[1]]
        assert orgmod.shape == tarmod.shape

        # test for mel-cd calculation
        flen = len(twf[0])
        mcd = 0
        for t in range(flen):
            mcd += melcd(orgmod[t], tarmod[t])
        mcd1 = mcd / flen

# Path: sprocket/util/distance.py
# def melcd(array1, array2):
#     """Calculate mel-cepstrum distortion
# 
#     Calculate mel-cepstrum distortion between the arrays.
#     This function assumes the shapes of arrays are same.
# 
#     Parameters
#     ----------
#     array1, array2 : array, shape (`T`, `dim`) or shape (`dim`)
#         Arrays of original and target.
# 
#     Returns
#     -------
#     mcd : scala, number > 0
#         Scala of mel-cepstrum distortion
# 
#     """
#     if array1.shape != array2.shape:
#         raise ValueError(
#             "The shapes of both arrays are different \
#             : {} / {}".format(array1.shape, array2.shape))
# 
#     if array1.ndim == 2:
#         # array based melcd calculation
#         diff = array1 - array2
#         mcd = 10.0 / np.log(10) \
#             * np.mean(np.sqrt(2.0 * np.sum(diff ** 2, axis=1)))
#     elif array1.ndim == 1:
#         diff = array1 - array2
#         mcd = 10.0 / np.log(10) * np.sqrt(2.0 * np.sum(diff ** 2))
#     else:
#         raise ValueError("Dimension mismatch")
# 
#     return mcd
# 
# Path: sprocket/util/twf.py
# def estimate_twf(orgdata, tardata, distance='melcd', fast=True, otflag=None):
#     """time warping function estimator
# 
#     Parameters
#     ---------
#     orgdata : array, shape(`T_org`, `dim`)
#         Array of source feature
#     tardata : array, shape(`T_tar`, `dim`)
#         Array of target feature
#     distance : str, optional
#         distance function
#         `melcd` : mel-cepstrum distortion
#     fast : bool, optional
#         Use fastdtw instead of dtw
#         Default set to `True`
#     otflag : str,
#         Perform alignment into either original or target length
#         `org` : align into original length
#         `tar` : align into target length
#         Default set to None
# 
#     Returns
#     ---------
#     twf : array, shape(`2`, `T`)
#         Time warping function between original and target
#     """
# 
#     if distance == 'melcd':
#         def distance_func(x, y): return melcd(x, y)
#     else:
#         raise ValueError('other distance metrics than melcd does not support.')
# 
#     if otflag is None:
#         # use dtw or fastdtw
#         if fast:
#             _, path = fastdtw(orgdata, tardata, dist=distance_func)
#             twf = np.array(path).T
#         else:
#             _, _, _, twf = dtw(orgdata, tardata, distance_func)
#     else:
#         # use dtw_c to align target/original feature vector
#         ldim = orgdata.shape[1] - 1
#         if otflag == 'org':
#             _, twf, _, _ = dtw_c.dtw_org_to_trg(tardata, orgdata,
#                                                 0, ldim, 5.0, 100.0, 100.0)
#         else:
#             _, twf, _, _ = dtw_c.dtw_org_to_trg(orgdata, tardata,
#                                                 0, ldim, 5.0, 100.0, 100.0)
#         twf[:, 1] = np.array(range(twf.shape[0]))  # replace target index by frame number
#         twf = twf.T
#         if otflag == 'org':
#             twf = twf[::-1, :]  # swap cols
#             assert twf.shape[0] == orgdata.shape[0]
#         else:
#             assert twf.shape[1] == tardata.shape[0]
# 
#     return twf


# Path: sprocket/util/tests/test_melcd.py
import unittest
import numpy as np
import pysptk
from sprocket.util import melcd, estimate_twf


def get_random_peseudo_mcep(order=24, alpha=0.42):
    T, N = 100, 513
    frames = np.random.rand(T, N) * pysptk.blackman(N)
    mc = pysptk.mcep(frames, order=order, alpha=alpha)
    return mc


class DistanceTest(unittest.TestCase):

    def test_melcd(self):
        org = get_random_peseudo_mcep()
        tar = get_random_peseudo_mcep()

        # perform dtw for mel-cd function test
        def distance_func(x, y): return melcd(x, y)
        twf = estimate_twf(org, tar, fast=True)
        twf = estimate_twf(org, tar, fast=False)

        # align org and tar
        orgmod = org[twf[0]]
        tarmod = tar[twf[1]]
        assert orgmod.shape == tarmod.shape

        # test for mel-cd calculation
        flen = len(twf[0])
        mcd = 0
        for t in range(flen):
            mcd += melcd(orgmod[t], tarmod[t])
        mcd1 = mcd / flen

        mcd2 = melcd(orgmod, tarmod)