from collections import Counter, defaultdict

import unittest
import warnings
import random


def get_dolzine(zemljevid):
    return [x2 - x1 + 1 for (x1, x2, y) in zemljevid]


def nabava(stari, novi):
    nove_dolzine = get_dolzine(novi)
    for d in get_dolzine(stari):
        if d in nove_dolzine:
            nove_dolzine.remove(d)

    return Counter(nove_dolzine)


def rekonstrukcija(kocke):
    ovire = []
    start_x = None
    end_x = None
    start_y = None
    for y, x in sorted(kocke):
        if start_x is None:
            start_x = x
            end_x = x
            start_y = y
            continue
        if y == start_y and end_x + 1 == x:
            end_x = x
            continue
        ovire.append((start_x, end_x, start_y))
        start_x = x
        end_x = x
        start_y = y

    if start_x is not None:
        ovire.append((start_x, end_x, start_y))

    return ovire


def dekodiraj_vrstico(vrstica: str):
    start_x = None
    ovire = []
    for x, c in enumerate(vrstica, start=1):
        if c == '<':
            start_x = x
        elif c == '>':
            ovire.append((start_x, x))
    return ovire


def preberi(ime_datoteke):
    ovire = []
    f = open(ime_datoteke)
    for y, vrstica in enumerate(f, start=1):
        ovire.extend((x1, x2, y) for (x1, x2) in dekodiraj_vrstico(vrstica))
    return ovire


def vrhovi(skladovnica, ovira, visina):
    def vrhovi_r(ovira, visina):
        if ovira not in skladovnica:
            if visina > 0:
                return set()
            return {ovira}
        else:
            vrh = set()
            for o in skladovnica[ovira]:
                vrh.update(vrhovi_r(o, visina - 1))
            return vrh

    return vrhovi_r(ovira, visina)


class Ovire:
    def __init__(self, ovire):
        self.ovire = ovire
        self.zadetki = 0
        self.zadete_ovire = defaultdict(lambda: 0)

    def strel(self, x, y):
        for (x1, x2, y1) in self.vse_ovire():
            if x1 <= x <= x2 and y == y1:
                self.zadetki += 1
                self.zadete_ovire[(x1, x2, y1)] += 1
                return True
        return False

    def zadetkov(self):
        return self.zadetki

    def vse_ovire(self):
        razbite_ovire = [ovira for ovira, zadetki in self.zadete_ovire.items() if zadetki >= 3]
        return {ovira for ovira in self.ovire if ovira not in razbite_ovire}

    def zmaga(self):
        return len(self.vse_ovire()) == 0


with open("ovire.txt", "wt", encoding="utf-8") as f:
    f.write("""
...<-->........
<->......<--->.
...............
...<-->..<--->.
...............
...<-->........
<->..<>...<--->.
""".lstrip())


class Test(unittest.TestCase):
    def setUp(self):
        warnings.simplefilter("ignore", ResourceWarning)

    def test_1_nabava(self):
        self.assertEqual(nabava([], []), {})
        self.assertEqual(nabava([(1, 1, 1)], [(1, 1, 1)]), {})
        self.assertEqual(nabava([(1, 1, 1)], [(3, 3, 2)]), {})
        self.assertEqual(nabava([(5, 8, 3)], [(6, 9, 4)]), {})
        self.assertEqual(nabava([(1, 1, 1), (5, 8, 3)], [(3, 3, 8), (6, 9, 4)]), {})

        self.assertEqual(nabava([], [(1, 1, 2)]), {1: 1})
        self.assertEqual(nabava([], [(4, 8, 3)]), {5: 1})
        self.assertEqual(nabava([], [(1, 1, 2), (4, 8, 3)]), {1: 1, 5: 1})
        self.assertEqual(nabava([], [(1, 1, 2), (5, 9, 10), (4, 8, 3)]), {1: 1, 5: 2})
        self.assertEqual(nabava([(1, 1, 1)], [(1, 1, 2), (5, 9, 10), (4, 8, 3)]), {5: 2})
        self.assertEqual(nabava([], [(1, 1, 2), (10, 14, 7), (5, 9, 10), (4, 8, 3)]), {1: 1, 5: 3})
        self.assertEqual(nabava([(9, 13, 5)], [(1, 1, 2), (10, 14, 7), (5, 9, 10), (4, 8, 3)]), {1: 1, 5: 2})
        self.assertEqual(nabava([(1, 3, 1), (9, 13, 5)], [(1, 1, 2), (10, 14, 7), (5, 9, 10), (4, 8, 3)]), {1: 1, 5: 2})
        self.assertEqual(nabava([(1, 3, 1), (9, 13, 5)], [(1, 1, 2), (10, 14, 7), (5, 9, 10), (4, 8, 3)]), {1: 1, 5: 2})
        self.assertEqual(nabava([(1, 3, 1), (2, 2, 3), (9, 13, 5)], [(1, 1, 2), (10, 14, 7), (5, 9, 10), (4, 8, 3)]),
                         {5: 2})

    def test_2_rekonstrukcija(self):
        self.assertEqual([], rekonstrukcija([]))
        self.assertEqual(
            [(3, 3, 1)],
            rekonstrukcija([(1, 3)])
        )

        self.assertEqual(
            [(3, 4, 1)],
            rekonstrukcija([(1, 3), (1, 4)]))

        self.assertEqual(
            [(3, 4, 1)],
            rekonstrukcija([(1, 4), (1, 3)]))

        self.assertEqual(
            [(3, 5, 1)],
            rekonstrukcija([(1, 3), (1, 4), (1, 5)]))

        self.assertEqual(
            [(3, 5, 1)],
            rekonstrukcija([(1, 5), (1, 3), (1, 4)]))

        self.assertEqual(
            [(3, 5, 1), (3, 4, 2)],
            rekonstrukcija([(1, 5), (1, 3), (1, 4), (2, 3), (2, 4)]))

        self.assertEqual(
            [(3, 5, 1), (3, 4, 2)],
            rekonstrukcija([(1, 5), (1, 3), (1, 4), (2, 4), (2, 3)]))

        self.assertEqual(
            [(1, 2, 1), (2, 4, 2), (4, 4, 3)],
            rekonstrukcija([(1, 1), (1, 2), (2, 2), (2, 3), (2, 4), (3, 4)]))
        # isto kot zgoraj, le pomešano
        self.assertEqual(
            [(1, 2, 1), (2, 4, 2), (4, 4, 3)],
            rekonstrukcija([(2, 3), (1, 1), (2, 2), (2, 4), (1, 2), (3, 4)]))

        self.assertEqual(
            [(1, 2, 1), (2, 4, 2), (4, 5, 3)],
            rekonstrukcija([(1, 1), (1, 2), (2, 2), (2, 3), (2, 4), (3, 4), (3, 5)]))
        # isto kot zgoraj, le pomešano
        self.assertEqual(
            [(1, 2, 1), (2, 4, 2), (4, 5, 3)],
            rekonstrukcija([(3, 5), (1, 1), (2, 4), (3, 4), (1, 2), (2, 2), (2, 3)]))

        self.assertEqual(
            [(1, 2, 1), (2, 4, 2), (4, 5, 3), (5, 5, 4)],
            rekonstrukcija([(1, 1), (1, 2), (2, 2), (2, 3), (2, 4), (3, 4), (3, 5), (4, 5)]))
        # isto kot zgoraj, le pomešano
        self.assertEqual(
            [(1, 2, 1), (2, 4, 2), (4, 5, 3), (5, 5, 4)],
            rekonstrukcija([(1, 1), (2, 4), (3, 4), (1, 2), (2, 2), (2, 3), (3, 5), (4, 5)]))

        kocke = [(1, 2), (1, 3), (1, 4), (1, 8), (1, 9), (1, 10),
                 (2, 5),
                 (3, 2), (3, 3), (3, 4), (3, 8), (3, 9), (3, 10),
                 (4, 5),
                 (5, 1), (5, 2), (5, 3), (5, 7), (5, 8), (5, 9),
                 (6, 4),
                 (7, 1), (7, 2), (7, 3), (7, 7), (7, 8), (7, 9)]

        for _ in range(10):
            random.shuffle(kocke)
            self.assertEqual(
                [(2, 4, 1), (8, 10, 1), (5, 5, 2), (2, 4, 3), (8, 10, 3), (5, 5, 4),
                 (1, 3, 5), (7, 9, 5), (4, 4, 6), (1, 3, 7), (7, 9, 7)],
                rekonstrukcija(kocke))

    def test_3a_dekodiraj_vrstico(self):
        self.assertEqual([], dekodiraj_vrstico("........"))
        self.assertEqual([(1, 2)], dekodiraj_vrstico("<>......"))
        self.assertEqual([(3, 4)], dekodiraj_vrstico("..<>......"))
        self.assertEqual([(3, 6)], dekodiraj_vrstico("..<-->....."))
        self.assertEqual([(3, 6), (10, 15)], dekodiraj_vrstico("..<-->...<---->.."))
        self.assertEqual([(3, 6), (10, 15), (18, 19)], dekodiraj_vrstico("..<-->...<---->..<>"))
        self.assertEqual([(1, 2), (4, 7), (11, 16), (19, 20)], dekodiraj_vrstico("<>.<-->...<---->..<>"))

    def test_3b_preberi(self):
        self.assertEqual([(4, 7, 1),
                          (1, 3, 2),
                          (10, 14, 2),
                          (4, 7, 4),
                          (10, 14, 4),
                          (4, 7, 6),
                          (1, 3, 7),
                          (6, 7, 7),
                          (11, 15, 7)], preberi("ovire.txt"))

    def test_4_vrhovi(self):
        """
               T
          j    l         z      B  A
          w i oo  pp     s      gg n
          c r uu vvv     x y    qq mm
          aaa bbbbbb     ttt ee fffff
          dddddddddd     hhhhhhhhhhhh
        ..............................
        """
        skladovnica = {
            ".": "dh",
            "d": "ab",
            "h": "tef",
            "a": "cr",
            "b": "uv",
            "t": "xy",
            "f": "qm",
            "c": "w",
            "r": "i",
            "u": "o",
            "v": "p",
            "x": "s",
            "y": "",
            "q": "g",
            "m": "n",
            "w": "j",
            "o": "l",
            "s": "z",
            "g": "B",
            "n": "A",
            "l": "T"
        }
        self.assertEqual(set("jiTp"), vrhovi(skladovnica, "d", 0))
        self.assertEqual(set("jiTp"), vrhovi(skladovnica, "d", -2))
        self.assertEqual(set("jiTp"), vrhovi(skladovnica, "d", 3))
        self.assertEqual(set("jT"), vrhovi(skladovnica, "d", 4))
        self.assertEqual(set("T"), vrhovi(skladovnica, "d", 5))
        self.assertEqual(set(), vrhovi(skladovnica, "d", 6))

        self.assertEqual(set("T"), vrhovi(skladovnica, "u", 2))
        self.assertEqual(set("Tp"), vrhovi(skladovnica, "b", 2))
        self.assertEqual(set("T"), vrhovi(skladovnica, "b", 3))

        self.assertEqual({'i', 'A', 'p', 'T', 'B', 'z', 'j', 'e'}, vrhovi(skladovnica, ".", 2))
        self.assertEqual({'i', 'A', 'p', 'T', 'B', 'z', 'j'}, vrhovi(skladovnica, ".", 3))
        self.assertEqual({'i', 'A', 'p', 'T', 'B', 'z', 'j'}, vrhovi(skladovnica, ".", 4))
        self.assertEqual({'A', 'T', 'B', 'z', 'j'}, vrhovi(skladovnica, ".", 5))
        self.assertEqual({'T'}, vrhovi(skladovnica, ".", 6))
        self.assertEqual(set(), vrhovi(skladovnica, ".", 7))

    def test_5_potapljanje(self):
        zacetne = {(1, 2, 5), (2, 4, 2), (5, 10, 4)}
        kopija = zacetne.copy()
        ovire = Ovire(zacetne)
        self.assertEqual(zacetne, ovire.vse_ovire())
        self.assertEqual(0, ovire.zadetkov())

        ovire2 = Ovire(set())
        self.assertEqual(0, ovire2.zadetkov())
        self.assertEqual(set(), ovire2.vse_ovire())
        self.assertTrue(ovire2.zmaga())

        self.assertFalse(ovire.strel(1, 1))
        self.assertEqual(zacetne, ovire.vse_ovire())
        self.assertEqual(0, ovire.zadetkov())

        self.assertTrue(ovire.strel(3, 2))
        self.assertEqual(zacetne, ovire.vse_ovire())
        self.assertEqual(1, ovire.zadetkov())
        self.assertFalse(ovire.zmaga())

        self.assertTrue(ovire.strel(2, 5))
        self.assertEqual(zacetne, ovire.vse_ovire())
        self.assertEqual(2, ovire.zadetkov())

        self.assertTrue(ovire.strel(3, 2))
        self.assertEqual(zacetne, ovire.vse_ovire())
        self.assertEqual(3, ovire.zadetkov())

        self.assertTrue(ovire.strel(4, 2))
        self.assertEqual({(1, 2, 5), (5, 10, 4)}, ovire.vse_ovire())
        self.assertEqual(4, ovire.zadetkov())

        self.assertFalse(ovire.strel(4, 2))
        self.assertEqual({(1, 2, 5), (5, 10, 4)}, ovire.vse_ovire())
        self.assertEqual(4, ovire.zadetkov())

        self.assertFalse(ovire.strel(2, 2))
        self.assertEqual({(1, 2, 5), (5, 10, 4)}, ovire.vse_ovire())
        self.assertEqual(4, ovire.zadetkov())

        self.assertFalse(ovire.zmaga())
        self.assertEqual(kopija, zacetne)

        self.assertEqual(0, ovire2.zadetkov())
        self.assertEqual(set(), ovire2.vse_ovire())
        self.assertTrue(ovire2.zmaga())

        self.assertTrue(ovire.strel(5, 4))
        self.assertEqual({(1, 2, 5), (5, 10, 4)}, ovire.vse_ovire())
        self.assertEqual(5, ovire.zadetkov())

        self.assertTrue(ovire.strel(10, 4))
        self.assertEqual({(1, 2, 5), (5, 10, 4)}, ovire.vse_ovire())
        self.assertEqual(6, ovire.zadetkov())

        self.assertFalse(ovire.strel(4, 2))
        self.assertEqual({(1, 2, 5), (5, 10, 4)}, ovire.vse_ovire())
        self.assertEqual(6, ovire.zadetkov())

        self.assertTrue(ovire.strel(1, 5))
        self.assertEqual({(1, 2, 5), (5, 10, 4)}, ovire.vse_ovire())
        self.assertEqual(7, ovire.zadetkov())

        self.assertTrue(ovire.strel(1, 5))
        self.assertEqual({(5, 10, 4)}, ovire.vse_ovire())
        self.assertEqual(8, ovire.zadetkov())

        self.assertFalse(ovire.strel(1, 5))
        self.assertEqual({(5, 10, 4)}, ovire.vse_ovire())
        self.assertEqual(8, ovire.zadetkov())

        self.assertFalse(ovire.zmaga())

        self.assertTrue(ovire.strel(7, 4))
        self.assertEqual(set(), ovire.vse_ovire())
        self.assertEqual(9, ovire.zadetkov())

        self.assertTrue(ovire.zmaga())

        self.assertFalse(ovire.strel(7, 4))
        self.assertEqual(set(), ovire.vse_ovire())
        self.assertEqual(9, ovire.zadetkov())

        self.assertTrue(ovire.zmaga())

        self.assertEqual(0, ovire2.zadetkov())
        self.assertEqual(set(), ovire2.vse_ovire())
        self.assertTrue(ovire2.zmaga())


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