# Path: marathoner/commands/base.py
# class BaseCommand(object):
#     syntax = ''
#     help = ''
# 
#     def __init__(self, project, executor):
#         self.project = project
#         self.executor = executor
#         self.contest = project.contest
# 
#     def is_match(self, command):
#         '''Given is the text that user typed into command line, return True
#         if this command should execute.
#         '''
#         raise NotImplementedError()
# 
#     def handle(self, command):
#         '''Given is the text that user typed into command line, execute the
#         command.
#         '''
# 
# Path: marathoner/scores.py
# class Score(object):
#     '''Encapsulates score and run-time of the single test.'''
#     def __init__(self, seed, score, run_time=None):
#         self.seed = int(seed)
#         self.score = float(score)
#         self.run_time = float(run_time or 0.0)
# 
#     def __str__(self):
#         return '<Test-%s Score: %s Run time: %s>' % (self.seed, self.score, self.time)
# 
#     @classmethod
#     def better(cls, maximize, score1, score2):
#         '''Return better of the two scores.'''
#         # zero score is always bad
#         if not score1.score:
#             return score2
#         if not score2.score:
#             return score1
# 
#         # for the same score, compare run times
#         if score1.score == score2.score:
#             if not score1.run_time:
#                 return score2
#             if not score2.run_time:
#                 return score1
#             return score1 if score1.run_time < score2.run_time else score2
# 
#         if maximize == (score1.score > score2.score):
#             return score1
#         else:
#             return score2
# 
#     @classmethod
#     def relative_score(cls, maximize, current_score, best_score):
#         if not current_score.score:
#             return 0.0
#         if maximize:
#             return current_score.score / max(best_score.score, best_score.score)
#         else:
#             return min(best_score.score, current_score.score) / current_score.score
# 
# Path: marathoner/utils/print_table.py
# def print_table(*tables):
#     '''Print tabular data in format:
# 
#     If `tables` is: [header, data, footer], then print:
#         ----------|---------|-----|
#         | header1 | header2 | ... |
#         | headerx | headery | ... |
#         |---------|---------|-----|
#         |   data1 |   data2 | ... |
#         |   datax |   datay | ... |
#         |---------|---------|-----|
#         | footer1 | footer2 | ... |
#         | footerx | footery | ... |
#         |---------|---------|-----|
# 
#     '''
#     # calculate length for each column
#     column_width = [0] * len(tables[0][0])
#     def update_column_widths(row):
#         for i, data in enumerate(row):
#             column_width[i] = max(column_width[i], len(str(data)))
#     for table in tables:
#         for row in table:
#             update_column_widths(row)
# 
#     # create formatting string
#     format = '| %s |' %  ' | '.join('%%%ss' % width for width in column_width)
#     hline = '|-%s-|' % '-|-'.join('-' * width for width in column_width)  # horizontal line
# 
#     print_(hline)
#     for table in tables:
#         print_('\n'.join(format % tuple(row) for row in table))
#         print_(hline)


import re
from six import print_
from six.moves import reduce
from marathoner.commands.base import BaseCommand
from marathoner.scores import Score
from marathoner.utils.print_table import print_table
            tag = self.project.tags.get(tag_name)
            if tag is None:
                print_('Tag "%s" doesn\'t exist.' % tag_name)
                return
            tags.append(tag)
            if seeds is None:
                seeds = set(tag.scores.seeds)
            else:
                seeds &= set(tag.scores.seeds)
        if not seeds:
            print_('Selected tags have no seeds in common.')
            return

        # prepare the data for table:
        #   Seed | <tag1> | <tag2> | ... | Best score
        header = [['Seed'] + [tag.name for tag in tags] + ['Best']]
        table = []
        relative_score = [0.0] * len(tags)
        num_absolute_best = [0] * len(tags)
        num_best = [0] * len(tags)

        for seed in sorted(seeds):
            best_score = self.project.scores[seed]
            row = []
            local_best_score = reduce(
                lambda x, tag: Score.better(self.project.maximize, x, tag.scores[seed]),
                tags,
                Score(seed, 0))
            for i, tag in enumerate(tags):
                current_score = tag.scores[seed]

# Path: marathoner/commands/base.py
# class BaseCommand(object):
#     syntax = ''
#     help = ''
# 
#     def __init__(self, project, executor):
#         self.project = project
#         self.executor = executor
#         self.contest = project.contest
# 
#     def is_match(self, command):
#         '''Given is the text that user typed into command line, return True
#         if this command should execute.
#         '''
#         raise NotImplementedError()
# 
#     def handle(self, command):
#         '''Given is the text that user typed into command line, execute the
#         command.
#         '''
# 
# Path: marathoner/scores.py
# class Score(object):
#     '''Encapsulates score and run-time of the single test.'''
#     def __init__(self, seed, score, run_time=None):
#         self.seed = int(seed)
#         self.score = float(score)
#         self.run_time = float(run_time or 0.0)
# 
#     def __str__(self):
#         return '<Test-%s Score: %s Run time: %s>' % (self.seed, self.score, self.time)
# 
#     @classmethod
#     def better(cls, maximize, score1, score2):
#         '''Return better of the two scores.'''
#         # zero score is always bad
#         if not score1.score:
#             return score2
#         if not score2.score:
#             return score1
# 
#         # for the same score, compare run times
#         if score1.score == score2.score:
#             if not score1.run_time:
#                 return score2
#             if not score2.run_time:
#                 return score1
#             return score1 if score1.run_time < score2.run_time else score2
# 
#         if maximize == (score1.score > score2.score):
#             return score1
#         else:
#             return score2
# 
#     @classmethod
#     def relative_score(cls, maximize, current_score, best_score):
#         if not current_score.score:
#             return 0.0
#         if maximize:
#             return current_score.score / max(best_score.score, best_score.score)
#         else:
#             return min(best_score.score, current_score.score) / current_score.score
# 
# Path: marathoner/utils/print_table.py
# def print_table(*tables):
#     '''Print tabular data in format:
# 
#     If `tables` is: [header, data, footer], then print:
#         ----------|---------|-----|
#         | header1 | header2 | ... |
#         | headerx | headery | ... |
#         |---------|---------|-----|
#         |   data1 |   data2 | ... |
#         |   datax |   datay | ... |
#         |---------|---------|-----|
#         | footer1 | footer2 | ... |
#         | footerx | footery | ... |
#         |---------|---------|-----|
# 
#     '''
#     # calculate length for each column
#     column_width = [0] * len(tables[0][0])
#     def update_column_widths(row):
#         for i, data in enumerate(row):
#             column_width[i] = max(column_width[i], len(str(data)))
#     for table in tables:
#         for row in table:
#             update_column_widths(row)
# 
#     # create formatting string
#     format = '| %s |' %  ' | '.join('%%%ss' % width for width in column_width)
#     hline = '|-%s-|' % '-|-'.join('-' * width for width in column_width)  # horizontal line
# 
#     print_(hline)
#     for table in tables:
#         print_('\n'.join(format % tuple(row) for row in table))
#         print_(hline)


# Path: marathoner/commands/tag_many.py
import re
from six import print_
from six.moves import reduce
from marathoner.commands.base import BaseCommand
from marathoner.scores import Score
from marathoner.utils.print_table import print_table

            tag = self.project.tags.get(tag_name)
            if tag is None:
                print_('Tag "%s" doesn\'t exist.' % tag_name)
                return
            tags.append(tag)
            if seeds is None:
                seeds = set(tag.scores.seeds)
            else:
                seeds &= set(tag.scores.seeds)
        if not seeds:
            print_('Selected tags have no seeds in common.')
            return

        # prepare the data for table:
        #   Seed | <tag1> | <tag2> | ... | Best score
        header = [['Seed'] + [tag.name for tag in tags] + ['Best']]
        table = []
        relative_score = [0.0] * len(tags)
        num_absolute_best = [0] * len(tags)
        num_best = [0] * len(tags)

        for seed in sorted(seeds):
            best_score = self.project.scores[seed]
            row = []
            local_best_score = reduce(
                lambda x, tag: Score.better(self.project.maximize, x, tag.scores[seed]),
                tags,
                Score(seed, 0))
            for i, tag in enumerate(tags):
                current_score = tag.scores[seed]

                relative_score[i] += Score.relative_score(