#!/usr/bin/python2
# coding: utf-8
"""This one-page (80 column x 66 line) program generates a book-length poem.

The Truelist, Copyright 2018 Nick Montfort <nickm@nickm.com>
Copying and distribution of this file, with or without modification, are
permitted in any medium without royalty provided the copyright notice and this
notice are preserved. This file is offered as-is, without any warranty.

I dedicate The Truelist to David Ferry.
Thank you, Christian Bök, Flourish Klink, Allison Parrish, Stephanie Strickland,
Babycastles and particularly WordHack, and the School for Poetic Computation."""

VERB_I = 'alight danc delight dream hurtl listen ponder mourn sleep'.split()
VERB_T = 'welcom exceed fac follow lead oppos reflect shadow support'.split()
PRE_A = 'back down flat fore free hard high long over soft true'.split()
PRE_N = """air bear bird blood blue blur bone book cloud corn cross dead
  door ear earth fire fish fly foot hand head heart home horn horse house land
  life light lock love man moon night past play plum port post rest ring river
  root salt sand ship sick side sky slip snow star stone tail trap turn void
  water whip wind woman wood work""".split()
POST_A = 'away back bound less like path side'.split()
POST_N = """bed bird board boat book cloth eye field fire fish hand head hills
  horse town house king wing light line list lock man ring room sack ship stone
  storm tail hound water way weed land woman wood word work worm yard""".split()
POST_S, T, ALL, OUT, X = 'hood ness'.split(), '    ', [], '', 3223

def shift(limit):
    'Xorshift to create a random-like but deterministic sequence.'
    global X
    X ^= (X << 19) & (2 ** 64 - 1) # ANDing by (2...) keeps X a 64-bit value.
    X ^= (X >> 35) & (2 ** 64 - 1) # Bit shifts >> and << along with OR give
    X ^= (X << 4) & (2 ** 64 - 1) # a random-like sequence.
    return X % limit # MOD gives a difficult-to-predict number in [0, limit-1].

for com in PRE_N + PRE_A: # Generate all PRE-POST combos except tautonyms.
    for pound in POST_N + POST_A + POST_S:
        mid, before, after = '', '', ''
        mid = ['', ' and '][com[-3:] == 'man' and pound[-3:] == 'man']
        if pound in POST_S: # Substantive compounds have a possessive before.
            before = POST_N[shift(len(POST_N))]
            before = before + ['’s ', '’ '][before[-1] == 's']
        elif pound in POST_A: # Adjective compounds have a noun after.
            after = ' ' + PRE_N[shift(len(PRE_N))]
        mid = [mid, '-'][com[-1] == pound[0] and not pound == 'ness']
        if mid == ' and ' or not (com == pound):
            ALL.append('the ' + before + com + mid + pound + after)

for i in range(len(ALL)):
    swap = shift(len(ALL))
    ALL[i], ALL[swap] = ''.join(ALL[swap]), ALL[i]

OUT = ALL[0].title() + '\n\nNick Montfort\n\f'

for s in range(1, 21):
    OUT += '\n\n' + str(s) + '\n\n\nNow they saw ' + ''.join(ALL.pop()) + ',\n'
    for line in range(1, 183):
        OUT += ['\n', T, T * 2, T * 3][line % 4] + \
            ['', 'and '][len(ALL[-1]) < 12] + ALL.pop() + \
            [' ' + VERB_T[shift(9)] + 'ing the ' + POST_N[shift(len(POST_N))],
             ' ' + VERB_I[shift(9)] + 'ing', '', '', '', ''][shift(6)] + ',\n'
    OUT = OUT[:-2] + [',\n' + T * 3 + 'and then ', '; \n' + T * 3 + \
                      'now they saw '][s > 19] + ALL.pop() + '.\n\f'

print OUT + ''.join(open(__file__)) + '\f'
