import unittest, submitted
from gradescope_utils.autograder_utils.decorators import weight
import numpy as np
import librosa

# TestSequence
class TestStep(unittest.TestCase):
    @weight(12.5)
    def test_analysis(self):
        speech = np.random.randn(10000)
        frames, coefficients = submitted.lpc_analysis(speech, 200, 100, 10)
        self.assertEqual(len(frames.shape), 2, 'frames should be an (nframes,framelength) array')
        self.assertEqual(frames.shape[0], 99,
                         '10000 samples, framelength 200, frameskip 100 should give 99 frames')
        self.assertEqual(frames.shape[1], 200, 'framelength should be 200')
        self.assertAlmostEqual(frames[0,45], speech[45], places=1,
                               msg='frames[0,45] should equal speech[45]')
        self.assertAlmostEqual(frames[42,45], speech[4245], places=1,
                               msg='frames[m,n] should equal speech[m*S+n]')
    @weight(12.5)
    def test_inverse(self):
        frames = np.random.randn(45,200)
        A = np.zeros((45,4))
        A[:,0] = 1
        A[:,1] = -0.5
        A[:,2] = 0.1
        A[:,3] = -0.05
        order = A.shape[1]-1
        excitation = submitted.lpc_inverse(frames, A)
        self.assertEqual(len(excitation.shape), 2,
                         'excitation should be an (nframes, framelength) array')
        self.assertEqual(excitation.shape[0], 45,
                         'excitation should have nframes rows')
        self.assertEqual(excitation.shape[1], 200-order,
                         'excitation should have framelength-order columns')
        ref = np.convolve(A[23,:], frames[23,:], 'valid')
        self.assertAlmostEqual(excitation[23,5], ref[5], places=1,
                               msg='excitation[23,5] is %g, should be %g'%(excitation[23,5],ref[5]))
        
    @weight(12.5)
    def test_synthesis(self):
        e = np.random.randn(45*100)
        A = np.zeros((45,4))
        A[:,0] = 1
        A[:,1] = -0.5
        A[:,2] = 0.1
        A[:,3] = -0.05
        synthesis = submitted.lpc_synthesis(e, A, 100)
        self.assertEqual(len(synthesis.shape), 1, 'synthesis should be a 1d array')
        self.assertEqual(len(synthesis), 45*100, 'len(synthesis) should be len(e)')
        self.assertAlmostEqual(synthesis[0], e[0], places=1,
                               msg='synthesis[0] should equal e[0]')
        ref = e[:]
        for n in range(len(ref)):
            for m in range(min(n,4)):
                ref[n] -= A[0,m]*ref[n-m]
        self.assertAlmostEqual(synthesis[345], ref[345], places=1,
                               msg='synthesis[345] is %g, should be %g'%(synthesis[345],ref[345]))
        
    @weight(12.5)
    def test_gain(self):
        excitation = np.random.randn(45,200)
        gain = submitted.lpc_gain(excitation)
        self.assertEqual(len(gain.shape), 1, 'gain should be a 1d array')
        self.assertEqual(len(gain), 45, 'len(gain) should be nframes')
        ref=np.sqrt(np.average(np.square(excitation[23,:])))
        self.assertAlmostEqual(gain[23], ref, places=1,
                               msg='gain[23] is %g, should be %g'%(gain[23], ref))
                               
