from unitgrade.framework import Report, UTestCase
from unitgrade import cache
from unitgrade.evaluate import evaluate_report_student
import numpy as np
import looping
from looping import bacteriaGrowth, clusterAnalysis, removeIncomplete, fermentationRate

def trlist(x):
    s = str(list(x))
    if len(s) > 30:
        s = s[:30] + "...]"
    return s

class Bacteria(UTestCase):
    """ Bacteria growth rates """

    def stest(self, n0, alpha, K, N):
        g = bacteriaGrowth(n0=n0, alpha=alpha, K=K, N=N)
        self.title = f"bacteriaGrowth({n0}, {alpha}, {K}, {N}) = {g} ?"
        self.assertEqualC(g)

    def test_growth1(self):
        """ Hints:
        * Make sure to frobulate the frobulator.
        """
        self.stest(100, 0.4, 1000, 500)

    def test_growth2(self):
        self.stest(10, 0.4, 1000, 500)

    def test_growth3(self):
        self.stest(100, 1.4, 1000, 500)

    def test_growth4(self):
        self.stest(100, 0.0004, 1000, 500)

    def test_growth5(self):
        """
        hints:
        * What happens when n0 > N? (in this case return t=0) """
        self.stest(100, 0.4, 1000, 99)

class ClusterAnalysis(UTestCase):
    """ Cluster analysis """

    def stest(self, n, seed):
        np.random.seed(seed)
        x = np.round(np.random.rand(n), 1)
        I = clusterAnalysis(x)
        self.title = f"clusterAnalysis({list(x)}) = {list(I)} ?"
        self.assertEqualC(list(I))

    def test_cluster1(self):
        """ Hints:
        * Make sure to frobulate the frobulator.
        * Just try harder
        """
        self.stest(3, 10)

    def test_cluster2(self):
        self.stest(4, 146)

    def test_cluster3(self):
        self.stest(5, 12)

    def test_cluster4(self):
        """
        Cluster analysis for tied lists
        Hints:
        * It may be that an observations has the same distance to the two clusters. Where do you assign it in this case?
        """
        x = np.array([10.0, 12.0, 10.0, 12.0, 9.0, 11.0, 11.0, 13.0])
        self.assertEqualC(list(clusterAnalysis(x) ) )


class RemoveIncomplete(UTestCase):
    """ Remove incomplete IDs """

    def stest(self, x):
        I = list( removeIncomplete(x) )
        self.title = f"removeId({trlist(x)}) = {trlist(I)} ?"
        self.assertEqualC(I)

    @cache
    def rseq(self, max, n):
        np.random.seed(42)
        return np.random.randint(max, size=(n,) ) + (np.random.randint(2, size=(n,) )+1)/10

    def test_incomplete1(self):
        self.stest( np.array([1.3, 2.2, 2.3, 4.2, 5.1, 3.2, 5.3, 3.3, 2.1, 1.1, 5.2, 3.1]) )

    def test_incomplete2(self):
        self.stest( np.array([1.1, 1.2, 1.3, 2.1, 2.2, 2.3]) )

    def test_incomplete3(self):
        self.stest(np.array([5.1, 5.2, 4.1, 4.3, 4.2, 8.1, 8.2, 8.3]) )

    def test_incomplete4(self):
        self.stest(np.array([1.1, 1.3, 2.1, 2.2, 3.1, 3.3, 4.1, 4.2, 4.3]) )

    def test_incomplete5(self):
        self.stest(self.rseq(10, 40))


class FermentationRate(UTestCase):
    """ Fermentation rate """

    def stest(self, x, lower, upper):
        I =  fermentationRate(x, lower, upper)
        s = trlist(x)
        self.title = f"fermentationRate({s}, {lower}, {upper}) = {I:.3f} ?"
        self.assertEqualC(I)

    @cache
    def rseq(self, max, n):
        np.random.seed(42)
        return np.random.randint(max, size=(n,) ) + (np.random.randint(3, size=(n,) )+1)/n

    def test_rate1(self):
        self.stest(np.array([20.1, 19.3, 1.1, 18.2, 19.7, 121.1, 20.3, 20.0]), 15, 25)

    def test_rate2(self):
        self.stest(np.array([20.1, 19.3, 1.1, 18.2, 19.7, 121.1, 20.3, 20.0]), 1, 200)

    def test_rate3(self):
        self.stest(np.array([1.75]), 1, 2)

    def test_rate4(self):
        self.stest(np.array([20.1, 19.3, 1.1, 18.2, 19.7, 121.1, 20.3, 20.0]), 18.2, 20)


class Report1Flat(Report):
    title = "02531 week 5: Looping"
    questions = [(ClusterAnalysis, 10), (RemoveIncomplete, 10), (Bacteria, 10),  (FermentationRate, 10),]
    pack_imports = [looping]

if __name__ == "__main__":
    evaluate_report_student(Report1Flat())