Print an ordered cross table of a round robin tournament that looks like this:

```
 #  Player            1  2  3  4  5  6  7  8  9 10 11 12 13 14  Pts   SB
==========================================================================
 1  Nash King            1  0  =  1  0  =  1  1  0  1  1  1  0  8.0  52.25
 2  Karsyn Marks      0     1  1  =  1  =  =  1  0  0  1  1  0  7.5  49.75
 3  Yandel Briggs     1  0     =  0  =  1  1  1  =  0  1  0  1  7.5  47.25
    Luka Harrell      =  0  =     1  1  1  =  0  0  1  1  0  1  7.5  47.25
    Pierre Medina     0  =  1  0     =  1  =  1  1  =  0  1  =  7.5  47.25
 6  Carlos Koch       1  0  =  0  =     =  =  1  1  =  0  =  1  7.0  43.50
 7  Tristan Pitts     =  =  0  0  0  =     1  0  1  =  1  1  1  7.0  40.75
    Luke Schaefer     0  =  0  =  =  =  0     1  =  =  1  1  1  7.0  40.75
 9  Tanner Dunn       0  0  0  1  0  0  1  0     1  1  =  1  1  6.5  37.25
10  Haylee Bryan      1  1  =  1  0  0  0  =  0     1  0  0  1  6.0  39.25
11  Dylan Turner      0  1  1  0  =  =  =  =  0  0     1  =  =  6.0  38.75
12  Adyson Griffith   0  0  0  0  1  1  0  0  =  1  0     1  1  5.5  31.75
13  Dwayne Shaw       0  0  1  1  0  =  0  0  0  1  =  0     1  5.0  30.50
14  Kadin Rice        1  1  0  0  =  0  0  0  0  0  =  0  0     3.0  22.25
```

The `#` column contains the rank. Ranks may be tied.  
A colum with index numbers `i` contains the match against the player on line `i`. `1` / `=` / `0` means win / draw / loss.  
The `Pts` column contains the score, 1 for each win, 0.5 for each draw.  
The `SB` column contains the Sonneborn-Berger score (see below).  

The rank is determined by the score, the Sonneborn-Berger score is used to break ties.
Players with the same score and the same SB score share the same rank.
In the cross table those tied players are ordered by their surenames.

# Sonneborn-Berger score

The Sonneborn-Berger score (`SB`) (sometimes also called Neustadtl Sonneborn–Berger or Neustadtl score) is a system to break ties in tournaments.
This score is based on the observation that it is usually harder to score against higher ranked than against lower ranked opponents.
The score is the sum of the points of opponents this player has defeated plus the half the sum of the points of opponents this player has drawn against.
For examples, see the table above.

# Task

Write a function `crosstable(players, results)`.

Input:
* `players`: a list of names (`["Forename Surename", ...]`)
* `results`: a list of lists of results, where result may be `1`, `0.5`, `0` or `None`.
    
Output:
    
* The cross table as a string.  
  There is no whitespace at the end of the lines.  
  The columns are separated by two spaces.  
  The column headers for the rank (`#`) and for the rounds are right adjusted, `Players` is left-adjusted, `Pts` and `SB` are centered.  
  The separator line consisting of `=` is just as long as the longest line in the output.

# Examples

```python
d, _ = 0.5, None

crosstable([
    'Emmett Frost', 'Cruz Sullivan', 'Deandre Bullock', 'George Bautista', 'Norah Underwood', 'Renee Preston'], [
    [_, 1, 0, 0, d, 0],
    [0, _, d, 1, 0, 0],
    [1, d, _, d, d, d],
    [1, 0, d, _, d, d],
    [d, 1, d, d, _, d],
    [1, 1, d, d, d, _]])

# returns
'''\
#  Player           1 2 3 4 5 6  Pts   SB
==========================================
1  Renee Preston      = = = 1 1  3.5  7.25
2  Deandre Bullock  =   = = = 1  3.0  6.75
   Norah Underwood  = =   = 1 =  3.0  6.75
4  George Bautista  = = =   0 1  2.5  6.25
5  Cruz Sullivan    0 = 0 1   0  1.5  4.00
6  Emmett Frost     0 0 = 0 1    1.5  3.00'''

crosstable([
    'Luke Schaefer', 'Adyson Griffith', 'Dylan Turner', 'Carlos Koch', 'Luka Harrell', 'Karsyn Marks', 'Haylee Bryan',
    'Dwayne Shaw', 'Pierre Medina', 'Nash King', 'Kadin Rice', 'Tristan Pitts', 'Tanner Dunn', 'Yandel Briggs'], [
    [_, 1, d, d, d, d, d, 1, d, 0, 1, 0, 1, 0],
    [0, _, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, d, 0],
    [d, 1, _, d, 0, 1, 0, d, d, 0, d, d, 0, 1],
    [d, 0, d, _, 0, 0, 1, d, d, 1, 1, d, 1, d],
    [d, 1, 1, 1, _, 0, 0, 0, 1, d, 1, 1, 0, d],
    [d, 1, 0, 1, 1, _, 0, 1, d, 0, 0, d, 1, 1],
    [d, 0, 1, 0, 1, 1, _, 0, 0, 1, 1, 0, 0, d],
    [0, 0, d, d, 1, 0, 1, _, 0, 0, 1, 0, 0, 1],
    [d, 0, d, d, 0, d, 1, 1, _, 0, d, 1, 1, 1],
    [1, 1, 1, 0, d, 1, 0, 1, 1, _, 0, d, 1, 0],
    [0, 0, d, 0, 0, 1, 0, 0, d, 1, _, 0, 0, 0],
    [1, 1, d, d, 0, d, 1, 1, 0, d, 1, _, 0, 0],
    [0, d, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, _, 0],
    [1, 1, 0, d, d, 0, d, 0, 0, 1, 1, 1, 1, _]])

# returns
'''\
 #  Player            1  2  3  4  5  6  7  8  9 10 11 12 13 14  Pts   SB
==========================================================================
 1  Nash King            1  0  =  1  0  =  1  1  0  1  1  1  0  8.0  52.25
 2  Karsyn Marks      0     1  1  =  1  =  =  1  0  0  1  1  0  7.5  49.75
 3  Yandel Briggs     1  0     =  0  =  1  1  1  =  0  1  0  1  7.5  47.25
    Luka Harrell      =  0  =     1  1  1  =  0  0  1  1  0  1  7.5  47.25
    Pierre Medina     0  =  1  0     =  1  =  1  1  =  0  1  =  7.5  47.25
 6  Carlos Koch       1  0  =  0  =     =  =  1  1  =  0  =  1  7.0  43.50
 7  Tristan Pitts     =  =  0  0  0  =     1  0  1  =  1  1  1  7.0  40.75
    Luke Schaefer     0  =  0  =  =  =  0     1  =  =  1  1  1  7.0  40.75
 9  Tanner Dunn       0  0  0  1  0  0  1  0     1  1  =  1  1  6.5  37.25
10  Haylee Bryan      1  1  =  1  0  0  0  =  0     1  0  0  1  6.0  39.25
11  Dylan Turner      0  1  1  0  =  =  =  =  0  0     1  =  =  6.0  38.75
12  Adyson Griffith   0  0  0  0  1  1  0  0  =  1  0     1  1  5.5  31.75
13  Dwayne Shaw       0  0  1  1  0  =  0  0  0  1  =  0     1  5.0  30.50
14  Kadin Rice        1  1  0  0  =  0  0  0  0  0  =  0  0     3.0  22.25'''

crosstable([
    'Mikaela Orozco', 'Mekhi Mayer', 'Marcus Galvan', 'Leroy Wilkins', 'Gregory Bates', 'Jayda Lynn', 'Makena Galloway',
    'Adriel Brock', 'Morgan Gillespie', 'Darwin Mack', 'Clayton Terrell', 'Bo Schmidt', 'Xzavier Clements', 'Rex Cummings',
    'Aldo Jackson', 'Justus Sloan', 'Rudy Herrera', 'Leonard Ponce', 'Kaden Harding', 'Anastasia Dodson'], [
    [_, 1, 1, 0, 1, d, 1, 1, 0, 1, 0, d, 0, d, d, 0, d, 0, 0, d],
    [0, _, 0, d, d, 0, d, 1, d, d, 0, 1, 1, d, d, d, d, 1, d, 0],
    [0, 1, _, 0, 0, d, 1, d, 0, d, 0, d, 0, 1, 1, 0, 0, 0, 1, 1],
    [1, d, 1, _, d, 0, d, 0, d, 0, 1, d, 0, 0, 1, 0, d, 1, d, 1],
    [0, d, 1, d, _, 1, d, 1, 1, 1, d, 1, 1, d, 0, 1, 1, 1, d, d],
    [d, 1, d, 1, 0, _, 0, d, d, 1, d, 0, d, 0, 0, 0, 1, d, 1, 1],
    [0, d, 0, d, d, 1, _, 1, 1, 1, 1, 0, d, 0, 1, 1, d, 1, 1, 0],
    [0, 0, d, 1, 0, d, 0, _, 1, d, 0, d, 1, d, d, 1, d, 0, 0, 0],
    [1, d, 1, d, 0, d, 0, 0, _, 0, 1, 0, 0, 1, 1, d, d, 1, 0, 0],
    [0, d, d, 1, 0, 0, 0, d, 1, _, d, d, 1, 1, d, 0, 1, d, 1, d],
    [1, 1, 1, 0, d, d, 0, 1, 0, d, _, 1, d, 0, 0, d, 0, 0, d, d],
    [d, 0, d, d, 0, 1, 1, d, 1, d, 0, _, 1, d, d, 0, 1, 0, 0, 0],
    [1, 0, 1, 1, 0, d, d, 0, 1, 0, d, 0, _, 1, d, d, 0, 1, d, d],
    [d, d, 0, 1, d, 1, 1, d, 0, 0, 1, d, 0, _, d, 0, 0, 0, 1, 1],
    [d, d, 0, 0, 1, 1, 0, d, 0, d, 1, d, d, d, _, d, 1, 0, 1, 1],
    [1, d, 1, 1, 0, 1, 0, 0, d, 1, d, 1, d, 1, d, _, 1, 1, 0, d],
    [d, d, 1, d, 0, 0, d, d, d, 0, 1, 0, 1, 1, 0, 0, _, 0, 0, 1],
    [1, 0, 1, 0, 0, d, 0, 1, 0, d, 1, 1, 0, 1, 1, 0, 1, _, d, d],
    [1, d, 0, d, d, 0, 0, 1, 1, 0, d, 1, d, 0, 0, 1, 1, d, _, 0],
    [d, 1, 0, 0, d, 0, 1, 1, 1, d, d, 1, d, 0, 0, d, 0, d, 1, _]])

# returns
'''\
 #  Player             1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20  Pts     SB
===============================================================================================
 1  Gregory Bates         1  =  0  1  1  =  1  =  1  0  =  =  =  1  1  =  1  1  1  13.5  124.50
 2  Justus Sloan       0     0  =  1  1  =  =  1  1  1  1  =  0  =  1  =  1  1  0  12.0  109.00
 3  Makena Galloway    =  1     1  1  1  0  =  =  1  0  0  =  1  1  0  1  0  =  1  11.5  109.75
 4  Aldo Jackson       1  =  0     =  0  1  =  0  1  =  =  =  1  0  =  1  0  1  =  10.0   95.25
 5  Darwin Mack        0  0  0  =     =  =  1  1  0  0  1  =  1  1  =  =  =  1  =  10.0   89.00
 6  Leonard Ponce      0  0  0  1  =     =  0  0  =  1  1  0  =  0  1  1  1  1  1  10.0   87.50
 7  Anastasia Dodson   =  =  1  0  =  =     =  0  0  =  0  1  1  1  1  =  0  0  1   9.5   90.25
 8  Xzavier Clements   0  =  =  =  0  1  =     1  =  1  1  0  =  1  0  =  1  0  0   9.5   89.00
    Leroy Wilkins      =  0  =  1  0  1  1  0     0  1  0  =  =  =  =  1  1  =  0   9.5   89.00
10  Jayda Lynn         0  0  0  0  1  =  1  =  1     =  0  1  1  =  0  =  =  1  =   9.5   85.50
11  Mikaela Orozco     1  0  1  =  1  0  =  0  0  =     =  1  0  0  =  0  1  =  1   9.0   86.75
12  Rex Cummings       =  0  1  =  0  0  1  0  1  1  =     =  1  0  =  1  0  0  =   9.0   86.25
13  Mekhi Mayer        =  =  =  =  =  1  0  1  =  0  0  =     =  =  1  0  0  =  1   9.0   86.00
14  Kaden Harding      =  1  0  0  0  =  0  =  =  0  1  0  =     1  1  =  0  1  1   9.0   83.50
15  Morgan Gillespie   0  =  0  1  0  1  0  0  =  =  1  1  =  0     0  1  1  =  0   8.5   78.50
    Bo Schmidt         0  0  1  =  =  0  0  1  =  1  =  =  0  0  1     0  =  1  =   8.5   78.50
    Clayton Terrell    =  =  0  0  =  0  =  =  0  =  1  0  1  =  0  1     1  0  1   8.5   78.50
18  Marcus Galvan      0  0  1  1  =  0  1  0  0  =  0  1  1  1  0  =  0     0  =   8.0   75.75
19  Rudy Herrera       0  0  =  0  0  0  1  1  =  0  =  1  =  0  =  0  1  1     =   8.0   72.00
20  Adriel Brock       0  1  0  =  =  0  0  1  1  =  0  =  0  0  1  =  0  =  =      7.5   71.00'''
```

Difficulty: interview

Starter code:

def crosstable(players, results):
	

Solutions:
def crosstable(players, scores):
    points, le = {j:sum(k or 0 for k in scores[i]) for i, j in enumerate(players)}, len(players)
    SB = {j:sum(points[players[k]] / ([1, 2][l == 0.5]) for k, l in enumerate(scores[i]) if l) for i, j in enumerate(players)}

    SORTED, li = [[i, players.index(i)] for i in sorted(players, key=lambda x: (-points[x], -SB[x], x.split()[1]))], []

    ps = [format(i, '.1f') for i in points.values()]
    Ss = [format(i, '.2f') for i in SB.values()]
    
    digit = len(str(le))
    name = len(max(players, key=len))
    pts = len(str(max(ps, key=lambda x: len(str(x)))))
    sb = len(str(max(Ss, key=lambda x: len(str(x)))))

    for i, j in enumerate(SORTED):
        ten_ = [" ", "  "][le >= 10]
        index = [str(i + 1), " "][points[j[0]] == points[SORTED[i - 1][0]] and SB[j[0]] == SB[SORTED[i - 1][0]]].rjust(digit)
        name_ = j[0].ljust(name)
        team = ten_.join(['1=0 '[[1, 0.5, 0, None].index(scores[j[1]][l])] or '_' for k, l in SORTED])
        pt = str(format(points[j[0]], ".1f")).rjust(pts)
        Sb = str(format(SB[j[0]], ".2f")).rjust(sb)
        li.append(f'{index}  {name_}{["  ", "   "][le >= 10]}{team}  {pt}  {Sb}')
    
    fline = ' '.join(['#'.rjust(digit) + '  ' +
                    'Player'.ljust(name) +
                    ['  ', '   '][len(players) >= 10] +
                    ''.join([[' ', '  '][i < 10 and le >= 10] + str(i) for i in range(1, le + 1)]).strip() + '  ' +
                    'Pts'.center(pts) + '  ' +
                    'SB'.center(sb - [0, 2][sb & 1])]).rstrip()
    return '\n'.join([fline, '=' * len(max(li, key=len))] + li)

def transform(ch):
    s = ''
    for car in ch:
        car1 = car
        if 'A' <= car <= 'Z': car1 = chr(ord('Z')-ord(car)+ord('A'))
        if 'a' <= car <= 'z': car1 = chr(ord('z')-ord(car)+ord('a'))
        s += car1
    return s
        
def crosstable(players, results):
    scores = [sum([sc for sc in line if not sc is None]) for line in results]
    sb = [sum([scores[i] if results[j][i] == 1 \
                        else scores[i]/2 if results[j][i] == 0.5 \
                        else 0 \
                        for i in range(len(results)) if not results[j][i] is None]) \
                        for j in range(len(results))]
    rank = [sum([1 if (scores[j] > scores[i]) or (scores[i] == scores[j] and sb[j] > sb[i])\
                   else 0 \
                   for j in range(len(scores))])+1 for i in range(len(scores))]
    res = list(zip(scores, 
                  sb, 
                  [transform(player.split()[1]) for player in players], 
                  range(len(results))))
    #print(res)
    res.sort(reverse=True)
    #print(rank, res)
    ranksize = max([len(str(r)) for r in rank])
    namesize = max([len(name) for name in players])
    ptssize = max([len('%0.1f' % (score,)) for score in scores])
    SBsize = max([len('%0.2f' % (sbs,)) for sbs in sb])
    
    scdetails = [' '.join([(' ' if results[pl[3]][pl1[3]] is None \
         else '=' if results[pl[3]][pl1[3]] == 0.5 \
         else '1' if results[pl[3]][pl1[3]] == 1 \
         else '0').rjust(ranksize) for pl1 in res]) for pl in res]
    
    resultat = [
        '  '.join(['#'.rjust(ranksize), 
         'Player'.ljust(namesize),
         ' '.join([str(i).rjust(ranksize) for i in range(1, len(players) + 1)]),
         'Pts'.ljust(ptssize),
         'SB'.rjust(2+(SBsize-2)//2)
        ])
    ]
    for i in range(len(res)):
        pl = res[i]
        ll = [(str(rank[pl[3]]) if i == 0 or rank[pl[3]] != rank[res[i-1][3]] \
                               else ' ').rjust(ranksize),
              players[pl[3]].ljust(namesize),
              scdetails[i],
              ('%0.1f' % (pl[0],)).rjust(ptssize),
              ('%0.2f' % (pl[1],)).rjust(SBsize)]
        ll = '  '.join(ll)
        if i == 0: resultat.append('=' * len(ll))
        resultat.append(ll)
    return '\n'.join(resultat)

def crosstable(players, results):
    count = len(players)
    pts = [sum(res[:i] + res[i + 1:]) for res, i in zip(results, range(count))]
    sb = [sum([r * p for r, p in zip(res, pts) if r is not None]) for res in results]

    l_rank, l_pl = len(str(count)), len(max(players, key=len))
    l_one_score = len(str(count))
    l_score = (l_one_score + 1) * count - 1
    template_pts, template_sb = f"{{:.1f}}", f"{{:.2f}}"
    l_pts, l_sb = max([len(template_pts.format(num)) for num in pts]), max([len(template_sb.format(num)) for num in sb])
    row_len = l_rank + l_pl + l_score + l_pts + l_sb + 8  # 8 is count sep between columns

    template_rank = f"{{: >{l_rank}}}  {{: <{l_pl}}}  {{: >{l_score}}}  "
    template_stat = f"{{:>{l_pts}.1f}}  {{:>{l_sb}.2f}}"
    template_stat_header = f"{{: ^{l_pts}}}  {{: ^{l_sb}}}"
    template_score = f"{{: >{l_one_score}}}"
    header = template_rank.format('#', 'Player', " ".join([template_score.format(i) for i in range(1, count + 1)])) + \
        template_stat_header.format("Pts", "SB").rstrip() + "\n" + ("=" * row_len) + '\n'
    score = []
    for res in results:
        score.append([template_score.format(" " if el is None else int(el) if el != 0.5 else "=") for el in res])

    sort_pts = list(sorted([dict(pts=pts[i], sb=sb[i], ind=i, name=players[i], res=score[i]) for i in range(count)],
                           key=lambda el: el['pts'], reverse=True))
    duplicate: bool = False
    start = 0
    for i in range(0, count):
        if not duplicate and i != count-1 and sort_pts[i + 1]['pts'] == sort_pts[i]['pts']:
            duplicate, start = True, i
        elif duplicate and (i == count-1 or sort_pts[i + 1]['pts'] != sort_pts[i]['pts']):
            sort_pts[start:i+1] = sort_by_sb(list(sorted(sort_pts[start:i+1], key=lambda el: el['sb'], reverse=True)),
                                             rank=start+1)
            duplicate = False
        elif not duplicate:
            sort_pts[i]['rank'] = i + 1
    order = [rank['ind'] for rank in sort_pts]
    result = header
    for row in sort_pts:
        row['res'] = [row['res'][order[i]] for i in range(count)]
        result += template_rank.format(row['rank'], row["name"], " ".join(row['res'])) + \
            template_stat.format(row['pts'], row['sb']) + "\n"
    return result.rstrip()


def sort_by_sb(arr: list, rank: int):
    duplicate, start, rank_duplicate = False, 0, rank
    for i in range(len(arr)):
        if not duplicate and i != len(arr)-1 and arr[i]['sb'] == arr[i + 1]['sb']:
            duplicate, start, rank_duplicate = True, i, rank
        elif duplicate and (i == len(arr)-1 or arr[i]['sb'] != arr[i+1]['sb']):
            arr[start:i + 1] = list(sorted(arr[start:i + 1], key=lambda el: el['name'].split(' ')[1]))
            for el in arr[start:i + 1]:
                el['rank'] = " "
            arr[start]['rank'] = rank_duplicate
            duplicate = False
        else:
            arr[i]['rank'] = rank
        rank += 1
    return arr

def crosstable(players, results):
    count = len(players)
    pts = [sum(res[:i] + res[i+1:]) for res, i in zip(results, range(count))]
    sb = [res[i] * pts[i] for res in results for i in range(count) if res[i] is not None]
    sb = [sum(sb[i * (count - 1): i * (count - 1) + count - 1]) for i in range(count)]

    l_rank, l_pl = len(str(count)), len(max(players, key=len))
    l_one_score = len(str(count))
    l_score = (l_one_score + 1) * count - 1
    template_pts, template_sb = f"{{:.1f}}", f"{{:.2f}}"
    l_pts, l_sb = max([len(template_pts.format(num)) for num in pts]), max([len(template_sb.format(num)) for num in sb])
    row_len = l_rank + l_pl + l_score + l_pts + l_sb + 8  # 8 is count sep between columns

    template_rank = f"{{: >{l_rank}}}  "
    template = f"{{: <{l_pl}}}  {{: >{l_score}}}  "
    template_score = f"{{:>{l_pts}.1f}}  {{:>{l_sb}.2f}}"
    template_score_header = f"{{: ^{l_pts}}}  {{: ^{l_sb}}}"
    template_one_score = f"{{: >{l_one_score}}}"
    header = template_rank.format('#') + \
             template.format('Player', " ".join([template_one_score.format(i) for i in range(1, count+1)])) + \
             template_score_header.format("Pts", "SB").rstrip() + "\n"
    delimiter = "=" * row_len + '\n'
    result = header + delimiter
    score = []
    for res in results:
        score.append([template_one_score.format(" " if el is None else int(el) if el != 0.5 else "=") for el in res])

    list_sort = [dict(pts=pts[i], sb=sb[i], ind=i, name=players[i], res=score[i]) for i in range(count)]
    sort_pts = list(sorted(list_sort, key=lambda el: el['pts'], reverse=True))
    duplicate: bool = False
    start = 0
    for i in range(0, count):
        if not duplicate and i != count-1 and sort_pts[i + 1]['pts'] == sort_pts[i]['pts']:
            duplicate = True
            start = i
        elif duplicate and (i == count-1 or sort_pts[i + 1]['pts'] != sort_pts[i]['pts']):
            sort_pts[start:i+1] = sort_by_sb(list(sorted(sort_pts[start:i+1], key=lambda el: el['sb'], reverse=True)),
                                             rank=start+1)
            duplicate = False
            continue
        elif not duplicate:
            sort_pts[i]['rank'] = i + 1
    order = [rank['ind'] for rank in sort_pts]
    for rank in sort_pts:
        el = rank
        el['res'] = [el['res'][order[i]] for i in range(count)]
        s = template_rank.format(el['rank']) + template.format(el["name"], " ".join(el['res'])) + \
            template_score.format(el['pts'], el['sb']) + "\n"
        result += s
    return result.rstrip('\n')


def sort_by_sb(arr: list, rank: int):
    duplicate, start = False, 0
    for i in range(len(arr)):
        if not duplicate and i != len(arr)-1 and arr[i]['sb'] == arr[i + 1]['sb']:
            arr[i]['rank'] = " "
            duplicate = True
            start = i
            rank_duplicate = rank
        elif duplicate and (i == len(arr)-1 or arr[i]['sb'] != arr[i+1]['sb']):
            arr[start:i + 1] = list(sorted(arr[start:i + 1], key=lambda el: el['name'].split(' ')[1]))
            for el in arr[start:i + 1]:
                el['rank'] = " "
            arr[start]['rank'] = rank_duplicate
            duplicate = False
        else:
            arr[i]['rank'] = rank
        rank += 1
    return arr

def score_string(array, col_width):
    scoreString = ''
    for elem in array:
        if elem is None:
            to_print = ''
        elif elem == 0.5:
            to_print = '='
        else:
            to_print = int(elem)
        scoreString += f"{to_print:{' '}{'>'}{col_width}}"
    return scoreString

def crosstable(players, results):
    tournament_results = {}
    firstnames, lastnames = zip(*[player.split(" ") for player in players])
    points_list = []
    for player_idx, player in enumerate(players):
        points = sum(filter(None, results[player_idx]))
        points_list.append(points)
        tournament_results[player] = {'Pts': points, 'Results': results[player_idx], 'Name': player}
        
    SB_list = []
    for player_idx, player_result in enumerate(results):
        player_SB = 0
        for opponent_idx, score in enumerate(player_result):
            if score is not None and score > 0:
                player_SB += tournament_results[players[opponent_idx]]['Pts'] * score
        SB_list.append(player_SB)
        tournament_results[players[player_idx]]['SB'] = player_SB
    
    sorted_points = sorted(range(len(points_list)), key=lambda k: (-points_list[k], -SB_list[k], lastnames[k][0]))
        
    for player, data in tournament_results.items():
        sorted_result = [data['Results'][idx] for idx in sorted_points]
        tournament_results[player]['Results'] = sorted_result
    
    player_ranks = ['1']
    for rank in range(1,len(players)):
        cur_player_idx = sorted_points[rank]
        one_pos_better_player_idx = sorted_points[rank - 1]
        if points_list[cur_player_idx] == points_list[one_pos_better_player_idx] and SB_list[cur_player_idx] == SB_list[one_pos_better_player_idx]:
            player_ranks.append('')
        else:
            player_ranks.append(str(rank+1))
    
    col_spacer = 2
    delimitter = ' '
    col_delimitter = col_spacer * delimitter
    width_rank_col = len(str(len(players)))
    width_player_col = max([len('Player'), len(max(players, key=len))])
    width_score_col = width_rank_col + 1
    width_pts_col = len(str(int(points_list[sorted_points[0]]))) + 2
    width_SB_col = len(str(int(SB_list[sorted_points[0]]))) + 3

    score_line = score_string(range(1,len(players)+1), width_score_col)
    col_width = [width_rank_col, col_spacer, width_player_col, 1, len(score_line), col_spacer, width_pts_col, col_spacer, width_SB_col]
    total_width = sum(col_width)
    
    SB_string = f"{'SB':{' '}{'^'}{width_SB_col}}".rstrip()
    header = f"{'#':{' '}{'>'}{width_rank_col}}" + col_delimitter + f"{'Player':{' '}{'<'}{width_player_col}}" + ' ' + score_line + col_delimitter + f"{'Pts':{' '}{'^'}{width_pts_col}}" + col_delimitter + SB_string + '\n'
    separator_line = total_width * '=' + '\n'
    totalString = header + separator_line
    for idx in range(len(players)):
        namePlayer = players[sorted_points[idx]]
        rankPlayer = player_ranks[idx] 
        scorePlayer = tournament_results[namePlayer]['Results']
        ptsPlayer = f"{tournament_results[namePlayer]['Pts']:{' '}{'>'}{width_pts_col}.1f}"
        SBPlayer = f"{tournament_results[namePlayer]['SB']:{' '}{'>'}{width_SB_col}.2f}"
        playerString = f"{rankPlayer:{' '}{'>'}{width_rank_col}}" + col_delimitter + f"{namePlayer:{' '}{'<'}{width_player_col}}" + ' ' + score_string(scorePlayer, width_score_col) + col_delimitter + ptsPlayer + col_delimitter + SBPlayer
        if idx < len(players)-1:
            playerString += '\n'
        totalString += playerString
        

    
    return(totalString)

def crosstable(players, results):
    scores=[sum(x for x in result if x) for result in results]
    sb=[]
    for i,result in enumerate(results):
        s=0
        for j,w in enumerate(result):
            if w is None:
                continue
            s+=w*scores[j]
        sb.append(s)
    rank=sorted(list(range(len(players))),key=lambda x:(-scores[x],-sb[x],players[x].split(' ')[1]))
    no_len=len(str(len(players)))
    name_len=max(len(p) for p in players)
    score_len=max(len('{:.1f}'.format(s)) for s in scores)
    sb_len=max(len('{:.2f}'.format(s)) for s in sb)
    r=[]
    title='#'.rjust(no_len)+'  '+'Player'.ljust(name_len)+'  '
    for i in range(1,len(players)+1):
        title+=str(i).rjust(no_len)+' '
    bar='='*len(title)
    header_sb='SB'.center(sb_len)
    if sb_len%2==1:
        header_sb=header_sb[1:]+' '
    title+=' '+'Pts'.center(score_len)+'  '+header_sb
    bar+='='*(score_len+sb_len+3)
    r.append(title.rstrip())
    r.append(bar)
    prev_sb=None
    for i,j in enumerate(rank):
        if prev_sb and prev_sb==(scores[j],sb[j]):
            no=' '
        else:
            no=str(i+1)
        line=no.rjust(no_len)+'  '+players[j].ljust(name_len)+'  '+' '*(no_len-1)
        mat=[]
        for k in rank:
            if j==k:
                mat.append(' ')
            else:
                mat.append({0:'0',0.5:'=',1:'1'}[results[j][k]])
        space=' '*no_len
        line+=space.join(mat)+'  '
        score_j='{:.1f}'.format(scores[j])
        sb_j='{:.2f}'.format(sb[j])
        line+=score_j.rjust(score_len)+'  '+sb_j.rjust(sb_len)
        prev_sb=(scores[j],sb[j])
        r.append(line)
    return '\n'.join(r)

import math

def crosstable(players, results):
    scores = {}
    places = []
    
    for s, p in zip(results, players):
        scores[p] = [sum(y for y in s if y != None)]
    
    c = 0
    for x, p in zip(results, players):
        scores[p].append(0)
        for y, p_2 in zip(x, players):
            if y != None:
                scores[p][1] += scores[p_2][0] * y
        scores[p].append(c)
        c += 1
    del c
    
    for x, y in zip(scores.keys(), scores.values()):
        for c, z in enumerate(places):
            if z[0]['score'][0] < y[0] or (z[0]['score'][0] == y[0] and z[0]['score'][1] < y[1]):
                places.insert(c, [{'Player': x, 'score': y}])
                break
            elif z[0]['score'][0] == y[0] and z[0]['score'][1] == y[1]:
                places[c].append({'Player': x, 'score': y})
                break
        else:
            places.append([{'Player': x, 'score': y}])
    
    for c, x in enumerate(places):
        if len(x) > 1:
            places[c] = sorted((y for y in x), key=lambda a: a['Player'].split(' ')[1])
    
    cur_line = 0
    display_place = True
    longest_name = max(len(x) for x in players) + 2
    longest_score = max(len('{:0.1f}'.format(x[0])) for x in scores.values())
    longest_SB = max(len('{:0.2f}'.format(x[1])) for x in scores.values())
    longest_rank = len(str(len(players) - len(places[-1]) + 1))
    longest_round_number = len(str(len(players)))
    ans = ["", ""]
    new_results = []
    ids = []
    
    for y in places:
        for a in y:
            ids.append(a['score'][2])
    
    results_in_order = [results[x] for x in ids]
    
    for x in results_in_order:
        new_results.append([])
        for y in ids:
            new_results[-1].append(x[y])
    
    for x in places:
        display_place = True
        for y in x:
            line = ''
            if display_place:
                line += (" " * (longest_rank - len(str(cur_line+1) ) ) ) + str(cur_line+1) + "  "
                display_place = False
            else:
                line += (" " * longest_rank) + "  "
            
            line += y['Player']
            spaces = longest_name - len(y['Player']) + (longest_round_number-1)
            line += " " * spaces
            for x in new_results[cur_line]:
                if x == None:
                    line += ' '
                elif x == 0.5:
                    line += '='
                elif x == 1:
                    line += '1'
                elif x == 0:
                    line  += '0'
                line += ' ' * longest_round_number
            line = line[:-longest_round_number]
            len_score = len('{:0.1f}'.format(y['score'][0]))
            len_SB = len('{:0.2f}'.format(y['score'][1]))
            line += ('  '+(' ' * (longest_score - len_score)) + '{:0.1f}' + (' ' * (longest_SB - len_SB + 2)) + '{:0.2f}')\
            .format(y['score'][0], y['score'][1])
            cur_line += 1
            ans.append(line)
    ans[1] = '=' * len(ans[2])
    ans[0] = ' ' * (longest_rank - 1) + '#  Player' + ' ' * (longest_name - 6 + longest_round_number - 1)
    for x in range(1, len(players) + 1):
        ans[0] += str(x)
        ans[0] += ' ' * (longest_round_number - len(str(x + 1)) + 1)
    ans[0] = ans[0].rstrip()
    ans[0] += '  ' + ' ' * math.floor(longest_score/3 - 1) + 'Pts' + ' ' *  math.ceil(longest_score/3 - 1)
    ans[0] += '  ' + ' ' * math.floor(longest_SB/2 - 1) + 'SB'
    return '\n'.join(ans)

def crosstable(players, results):
    playersInputRow = {}
    playersScores = {}
    playersSB = {}
    playersSUM = {}
    longestName = float('-inf')
    for number,player in enumerate(players):
        playersInputRow[player]=number
        if len(player) > longestName:
            longestName = len(player)
        playersScores[player]=0
        for score in results[number]:
            if score != None:
                playersScores[player] += score
                
    for number,player in enumerate(players):
        playersSB[player] = 0
        for i,score in enumerate(results[number]):
            if score == 0.5:
                playersSB[player] += 0.5 * playersScores[players[i]]
            if score == 1:
                playersSB[player] += playersScores[players[i]]
    for player in players:
        playersSUM[player] = playersScores[player] * 100 + playersSB[player]
        
    i=1
    placments = {}
    while playersSUM:
        placments[i] = []
        maxValue = max(playersSUM.values())
        for player in playersSUM:
            if playersSUM[player] == maxValue:
                placments[i].append(player)
        for player in placments[i]:
            del playersSUM[player]
        i+=1
        
        
    placmentLength = len(str(len(placments)))
    playersLength = len(str(len(players)))
    ptsLength = len('{:.1f}'.format(max(playersScores.values())))
    sbLength = len('{:.2f}'.format(max(playersSB.values())))
    
    
    string=""
    spaces="  "
    i=1
    for place in placments:
        first=True
        for player in sorted(sorted(placments[place]), key=lambda s: s.split()[1]):
            if first==True:
                string+=str(i).rjust(placmentLength)+spaces
                first=False
            else:
                string+="".rjust(placmentLength)+spaces
            string+=player.ljust(longestName)+spaces
            for place2 in placments:
                for player2 in sorted(sorted(placments[place2]),key=lambda s: s.split()[1]):
                    result = results[playersInputRow[player]][playersInputRow[player2]]
                    if result == None:
                        string+=" ".rjust(playersLength) + " "
                    elif result == 1:
                        string+="1".rjust(playersLength) + " "
                    elif result == 0.5:
                        string+="=".rjust(playersLength) + " "
                    elif result == 0:
                        string+="0".rjust(playersLength) + " "
            string+=" "
            string += str("{:.1f}".format(playersScores[player])).rjust(ptsLength)+spaces
            string += str("{:.2f}".format((playersSB[player]))).rjust(sbLength)
            if place==1:
                lineLength = len(string)
            if i != len(players):
                string+="\n"
            i+=1
            
    
    head = "#".rjust(placmentLength)+spaces
    head += "Player".ljust(longestName)+spaces
    for i in range(1,len(players)+1):
        head += str(i).rjust(playersLength)+" "
    head += " "
    head += "Pts".center(ptsLength)+spaces
    head += "SB".center(sbLength-1)
    while head[-1] == " ":
        head = head[:-1]
    head += "\n"
    head += "".ljust(lineLength, "=")+"\n"
     
            

    head+=string
    
    
    
    
    return head
            
            

        
    
        
        
    """
    
    return (
        "#  Player             1 2 3  Pts  SB\n"
        "=====================================\n"
        "1  Boris Spassky        1 =  1.5 1.25\n"
        "2  Garry Kasparov     0   1  1.0 0.50\n"
        "3  Viswanathan Anand  = 0    0.5 0.75")
     """

import re

def crosstable(players, results):
    pts = [sum(r for r in result if r is not None) for result in results]
    sb = [sum(r*pt for r,pt in zip(result, pts) if r is not None) for result in results]
    order = sorted(sorted(range(len(players)), key=lambda player:players[player].split(' ')[-1]), key=lambda player:(pts[player],sb[player]), reverse=True)
    width_rank = len(str(next(rank+1 for rank,player in enumerate(order) if pts[player] == pts[order[-1]] and sb[player] == sb[order[-1]])))
    width_player = max(len(player) for player in players)
    width_result = len(str(len(players)))
    width_pts = len(str(f'{pts[order[0]]:0.1f}'))
    width_sb = len(str(f'{max(sb):0.2f}'))
    lines = []
    lines.append(
        f'{"#":>{width_rank}}  '
        f'{"Player":<{width_player}}  '
        f'{" ".join(f"{i+1:>{width_result}}" for i in range(len(players)))}  '
        f'{"Pts":^{width_pts}}  '
        + f'{"SB":^{width_sb}}'.rstrip()
    )
    lines.append("="*(width_rank+width_player+(width_result+1)*len(players)+width_pts+width_sb+7))
    for rank, player in enumerate(order):
        lines.append(
            f'{str(rank+1) if rank==0 or pts[order[rank-1]] != pts[player] or sb[order[rank-1]] != sb[player] else "":>{width_rank}}  '
            f'{players[player]:<{width_player}}  '
            + ' '.join(f'{ {None:"", 0:"0", 0.5:"=", 1:"1"}[results[player][opponent]]:>{width_result}}' for opponent in order) + '  ' +
            f'{pts[player]:>{width_pts}.1f}  '
            f'{sb[player]:>{width_sb}.2f}'
        )
    return '\n'.join(lines)

def sortme_sensei(name, ls, ref, p):
    ref = [val[0] for val in ref]
    ls = list(zip(ls, p))
    ls.sort(key=lambda x: ref.index(x[1]))
    return [val[0] for val in ls]

def crosstable(players, results):
    points, i = {v: [float(sum([val for val in x if val != None])), 0, x] for v, x in zip(players, results)}, 0
    for n in players:
        indo = results[i]
        for idx, num in enumerate(indo):
            if num == 1:
                points[n][1] += points[players[idx]][0]
            elif num == 0.5:
                points[n][1] += points[players[idx]][0] / 2
        i += 1
    D = [(p, pts[0], f"{pts[1]:.2f}") for p, pts in list(points.items())]
    sep_points, playa, ptsz, Sb = len(str(len(players))), len(max(players, key=len)) + 2, len(
        max([str(val[1]) for val in D], key=len)), \
                                  len(max([str(val[2]) for val in D], key=len))
    D.sort(key=lambda x:(-x[1],-float(x[2]),x[0].split()[1]))
    points = {k: [v[0], v[1], sortme_sensei(k, v[2], D, players)] for k, v in list(points.items())}
    string = "#".rjust(sep_points) + "  " + "Player".ljust(playa) + "".join(
        str(i).rjust(sep_points + 1) if i - 1 else str(i).rjust(sep_points) for i in range(1, len(players) + 1)) \
             + "  " + "Pts".center(ptsz) + "  " + (
                 "SB".center(Sb - 1)[:-1] if "SB".center(Sb - 1)[-1] == " " else "SB".center(Sb - 1)) + "\n"
    tempo,C = "",0
    for i in range(len(D)):
        tempo += str(i + 1 if D[i - 1][2] != D[i][2] or D[i - 1][1] != D[i][1] else "").rjust(sep_points) + "  " + \
                  D[i][0].ljust(playa)
        for idx, res in enumerate(points[D[i][0]][2]):
            if idx:
                tempo += res in [1, 0] and str(int(res)).rjust(sep_points + 1) or res == 0.5 and "=".rjust(
                    sep_points + 1) or (sep_points + 1) * " "
            else:
                tempo += res in [1, 0] and str(int(res)).rjust(sep_points) or res == 0.5 and "=".rjust(sep_points) or (
                    sep_points) * " "
        tempo += "  " + str(D[i][1]).rjust(ptsz) + "  " + \
                  str(D[i][2]).rjust(Sb) + ("\n" if i != len(D) - 1 else "")
        if not C: string += (len(tempo)-1)*"="+"\n" ; C = 1
    return string+tempo