
def line_break(text, width):
    words = text.split()
    count = len(words)
    slack = [[0] * count for i in range(count)] # count x count array
    for i in range(count):
        slack[i][i] = width - len(words[i]) # line i is broken at i: slack is rest of line
        for j in range(i + 1, count):
            # line i is broken at j: slack is previous - whitespace - word len
            slack[i][j] = slack[i][j - 1] - len(words[j]) - 1

    minima = [0] + [10 ** 20] * count
    breaks = [0] * count

    # compute ideal breaking point
    for j in range(count):
        i = j
        while i >= 0:
            if slack[i][j] < 0:
                # terrible; line is too wide
                cost = 10**10
            else:
                cost = minima[i] + slack[i][j] ** 2

            if minima[j + 1] > cost:
                minima[j + 1] = cost
                breaks[j] = i

            i -= 1

    lines = []
    j = count
    while j > 0:
        i = breaks[j - 1]
        lines.append(' '.join(words[i:j]))
        j = i

    lines.reverse()

    return '\n'.join(lines)


def rewrap(text):
    text = text.strip()
    # Un-DOS it...
    text = text.replace("\r\n", "\n")
    print(f"Raw text: {repr(text)}")
    paragraphs = [par.strip() for par in text.split('\n\n')]

    print(f"Number of paragraphs: {len(paragraphs)}")

    new_pars = []
    for par in paragraphs:
        raw_text = par.replace('\n', ' ')
        new_text = line_break(raw_text, width=56)
        new_pars.append(new_text)
    return '\n\n'.join(new_pars)
