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

def convolve_with_ones(x):
    return np.convolve(x, np.ones(100))

def positive_feedback(x):
    y = np.zeros(x.shape)
    y[0] = x[0]
    for n in range(1,len(x)):
        with np.errstate(over='ignore'):
            y[n] = x[n] + 1.1*y[n-1]
    return y

# TestSequence
class TestStep(unittest.TestCase):
    @weight(6.25)
    def test_linear(self):
        x1 = np.random.randn(100000)
        x2 = np.random.randn(100000)
        a = 0.3
        resp = submitted.test_linearity(convolve_with_ones, x1, x2, a)
        self.assertTrue(resp, 'Convolution with ones is a linear operation!')

    @weight(6.25)
    def test_nonlinear(self):
        x1 = np.random.randn(100000)
        x2 = np.random.randn(100000)
        a = 0.3
        resp = submitted.test_linearity(np.square, x1, x2, a)
        self.assertFalse(resp, 'Squaring the input is a nonlinear operation!')

    @weight(6.25)
    def test_timeinvariant(self):
        x = np.concatenate((np.zeros(100000),np.random.randn(100000),np.zeros(100000)))
        d = 30
        resp = submitted.test_timeinvariance(convolve_with_ones, x, d)
        self.assertTrue(resp, 'Convolution with ones is a time-invariant operation!')

    @weight(6.25)
    def test_timevarying(self):
        x = np.concatenate((np.zeros(100000),np.random.randn(100000),np.zeros(100000)))
        d = 30
        resp = submitted.test_timeinvariance(np.flip, x, d)
        self.assertFalse(resp, 'Flipping the input is a time-varying operation!')

    @weight(6.25)
    def test_causal(self):
        x = np.random.randn(100000)
        n = 30000
        resp = submitted.test_causality(convolve_with_ones, x, n)
        self.assertTrue(resp, 'Full-mode convolution is a causal operation!')

    @weight(6.25)
    def test_noncausal(self):
        x = np.random.randn(100000)
        n = 30000
        resp = submitted.test_causality(np.flip, x, n)
        self.assertFalse(resp, 'Flipping the input is a non-causal operation!')

    @weight(6.25)
    def test_stable(self):
        x = np.random.randn(100000)
        resp = submitted.test_stability(convolve_with_ones, x)
        self.assertTrue(resp, 'Convolution with ones is a stable operation!')

    @weight(6.25)
    def test_unstable(self):
        x = np.random.randn(100000)
        resp = submitted.test_stability(positive_feedback, x)
        self.assertFalse(resp, 'Positive feedback is an unstable operation!')
