| t | import sys | t | import sys |
| from graphlib import TopologicalSorter, CycleError | | from graphlib import TopologicalSorter, CycleError |
| | | |
| def read_input(): | | def read_input(): |
| lines = [] | | lines = [] |
| for line in sys.stdin: | | for line in sys.stdin: |
| line = line.rstrip('\n') | | line = line.rstrip('\n') |
| if line == '': | | if line == '': |
| break | | break |
| lines.append(line) | | lines.append(line) |
| return lines | | return lines |
| | | |
| def parse(lines): | | def parse(lines): |
| order = [] | | order = [] |
| plans = {} | | plans = {} |
| for line in lines: | | for line in lines: |
| if ':' not in line: | | if ':' not in line: |
| name = line.strip() | | name = line.strip() |
| rest = '' | | rest = '' |
| else: | | else: |
| name, rest = line.split(':', 1) | | name, rest = line.split(':', 1) |
| name = name.strip() | | name = name.strip() |
| rest = rest.strip() | | rest = rest.strip() |
| if rest == '': | | if rest == '': |
| asks = [] | | asks = [] |
| else: | | else: |
| asks = [part.strip() for part in rest.split(',') if part.str | | asks = [part.strip() for part in rest.split(',') if part.str |
| ip() != ''] | | ip() != ''] |
| if name in plans: | | if name in plans: |
| pass | | pass |
| else: | | else: |
| order.append(name) | | order.append(name) |
| plans[name] = asks | | plans[name] = asks |
| return (order, plans) | | return (order, plans) |
| | | |
| def main(): | | def main(): |
| lines = read_input() | | lines = read_input() |
| if not lines: | | if not lines: |
| return | | return |
| order, plans = parse(lines) | | order, plans = parse(lines) |
| names = set(order) | | names = set(order) |
| for name, asks in plans.items(): | | for name, asks in plans.items(): |
| for a in asks: | | for a in asks: |
| if a not in names: | | if a not in names: |
| print('UNKNOWN') | | print('UNKNOWN') |
| return | | return |
| deps = {name: set(plans[name]) for name in order} | | deps = {name: set(plans[name]) for name in order} |
| try: | | try: |
| topo = list(TopologicalSorter(deps).static_order()) | | topo = list(TopologicalSorter(deps).static_order()) |
| except CycleError: | | except CycleError: |
| print('CYCLE') | | print('CYCLE') |
| return | | return |
| classes = {} | | classes = {} |
| try: | | try: |
| for nm in topo: | | for nm in topo: |
| base_names = plans[nm] | | base_names = plans[nm] |
| bases = tuple((classes[b] for b in base_names)) | | bases = tuple((classes[b] for b in base_names)) |
| cls = type(nm, bases, {}) | | cls = type(nm, bases, {}) |
| classes[nm] = cls | | classes[nm] = cls |
| except TypeError: | | except TypeError: |
| print('INEFFECTIVE') | | print('INEFFECTIVE') |
| return | | return |
| for nm in order: | | for nm in order: |
| cls = classes[nm] | | cls = classes[nm] |
| mro = cls.mro() | | mro = cls.mro() |
| plan = [c.__name__ for c in mro[1:] if c.__name__ in names] | | plan = [c.__name__ for c in mro[1:] if c.__name__ in names] |
| if plan: | | if plan: |
| print(f"{nm}: {', '.join(plan)}") | | print(f"{nm}: {', '.join(plan)}") |
| else: | | else: |
| print(f'{nm}:') | | print(f'{nm}:') |
| if __name__ == '__main__': | | if __name__ == '__main__': |
| main() | | main() |