import math
import unittest
from analyze import quantile,stats,bin_spec,histogram,modal_summary,valley_scan


class StatisticsContract(unittest.TestCase):
    def test_r7_hand_values(self):
        self.assertEqual(quantile([0,1,2,3],.25),.75)
        self.assertEqual(quantile([0,1,2,3],.5),1.5)
        self.assertAlmostEqual(quantile([0,1,2,3],.95),2.85)
        self.assertEqual(quantile([5],.95),5)

    def test_missing_not_zero(self):
        self.assertIsNone(quantile([],.5));self.assertIsNone(stats([])['mean'])
        self.assertEqual(stats([.2,.4])['n'],2)

    def test_histogram_edges_and_max_included(self):
        h=histogram([0,.5,1],dict(lo=0,hi=1,bins=2))
        self.assertEqual(h['counts'],[1,2]);self.assertEqual(h['modes'][0]['within_bin_mean'],.75)
        self.assertTrue(h['modes'][0]['upper_closed'])

    def test_mode_ties(self):
        h=histogram([0,1],dict(lo=0,hi=1,bins=2))
        self.assertEqual(len(h['modes']),2)
        self.assertEqual(modal_summary([0,1],dict(lo=0,hi=1,bins=2))['flag'],'MULTIPLE_MAX_BINS')

    def test_constant(self):
        spec=bin_spec([.2]*10);h=histogram([.2]*10,spec)
        self.assertEqual((spec['bins'],h['width']),(1,0))
        self.assertEqual(h['modes'][0]['share'],1)

    def test_fd_and_iqr_zero_fallback(self):
        self.assertEqual(bin_spec(list(range(8)))['bins'],2)
        self.assertIn('Sturges',bin_spec([0]*9+[1])['method'])

    def test_threshold_equality(self):
        values=[.1,.1,.2,.3];cutoff=quantile(values,.25)
        self.assertEqual(cutoff,.1);self.assertEqual(sum(x>=cutoff for x in values),4)
        self.assertEqual(sum(x>=math.nextafter(.1,1) for x in values),2)

    def test_top5_ties_by_id(self):
        scores=[0.]*60
        self.assertEqual(sorted(range(60),key=lambda k:(-scores[k],k))[:5],list(range(5)))

    def test_no_forced_valley(self):
        self.assertEqual(valley_scan(dict(counts=[100,40,20,10,5]))['candidates'],[])
        self.assertTrue(valley_scan(dict(counts=[100,5,0,5,100]))['candidates'])


if __name__=='__main__':unittest.main()
