6**5

"""
Explanation: AVISO: Este projeto migrou para o repositório https://github.com/ramalho/dadoware
Diceware: método seguro para gerar senhas
Fonte: The Diceware Passphrase Home Page
Compilação da lista de palavras
Neste notebook comecei com uma grande lista palavras da língua portuguesa, que fui sucessivamente filtrando com diversos critérios até chegar próximo da quantidade necessária para o método diceware. Então embaralhei as 7808 palavras restantes e fatiei a lista para ficar com a quantidade exata: 7776. No último passo, gerei o arquivo 7776palavras.txt com linhas de "11111 abaci" a "66666 zurpa".
Para começar, confirmei a quantidade de palavras necessárias para usar 5 dados na escolha de palavras:
End of explanation
"""


completa = [lin.strip() for lin in open('palavras.txt').readlines()]
len(completa), len(completa) == len(set(completa))

completa[:10], completa[-10:]

"""
Explanation: O ponto de partida foi uma lista 326.716 palavras da língua portuguesa que compilei a partir de várias fontes. Aqui eu leio o arquivo, verifico que são todas palavras únicas e exibo uma amostra com as 10 primeiras e as 10 últimas:
End of explanation
"""


ate5 = [p for p in completa if len(p) <= 5]
len(ate5)

"""
Explanation: O primeiro filtro foi pegar só palavras de até 5 caracteres:
End of explanation
"""


import string
so_ascii = []
for palavra in ate5:
    if all(c in string.ascii_lowercase for c in palavra):
        so_ascii.append(palavra)
    
len(so_ascii)

so_ascii[:10], so_ascii[-10:]

"""
Explanation: Em seguida, fiquei apenas com as palavras formadas exclusivamente por letras minúsculas de "a" a "z", sem acentos ou hífens:
End of explanation
"""


ocorrencias = set(so_ascii)
singulares = []
print('Plurais removidas:')
for pal in so_ascii:
    if pal[-1] == 's' and pal[:-1] in ocorrencias:
        print(pal, end=' ')
    else:
        singulares.append(pal)

len(singulares)

"""
Explanation: Examinando a lista obtida até agora, percebi que haviam muitas palavras que eram apenas plurais, como "acres", "botas" etc. Com este código eu removi as palavras que são iguais a outras palavras apenas com um "s" colado no final. Por exemplo, removi "abios" porque a lista já tem a palavra "abio".
End of explanation
"""


tam4ou5 = [p for p in singulares if len(p) in (4,5)]
len(tam4ou5)

sobrando = len(tam4ou5) - 6**5
sobrando

"""
Explanation: Estamos chegando perto das 7776 palavras que precisamos. Aqui fiquei só com as palavras de 4 ou 5 letras:
End of explanation
"""


palavroes = {lin.strip().lower() for lin in open('palavroes.txt', encoding='latin1').readlines()}

len(palavroes)

"""
Explanation: Ainda precisamos descartar 74 palavras. Procurei na Web uma lista de palavrões, e achei nesta página. Carreguei as palavras, convertendo em minúsculas e criando um set. A notação {… for … in …} é uma set comprehension e produz um objeto set. O resultado foi um conjunto de 301 palavrões.
End of explanation
"""


limpas = list(set(tam4ou5) - palavroes)
len(limpas), len(limpas) - 6**5

"""
Explanation: Transformando a lista tam4ou5 em um set fica muito fácil remover os palavrões fazendo apenas uma subtração entre conjuntos. O resultado eu converti em list novamente, para poder embaralhar a seguir.
End of explanation
"""


import collections
mmm = []
for palavra in limpas:
    if collections.Counter(palavra)['m'] > 1:
        mmm.append(palavra)
mmm

"""
Explanation: Ainda temos 32 palavras a mais. Na diagramação que fiz depois, a palavra "mamum" causou problemas por ter 3 "m" e então fica mais larga que as demais. Vamos todas as palavras com mais de 2 "m":
End of explanation
"""


limpas.remove('mamum')
len(limpas), len(limpas) - 6**5

"""
Explanation: Ok, vamos tirar esta da lista:
End of explanation
"""


import random
random.shuffle(limpas)
final = sorted(limpas[:6**5])
len(final), final[:10], final[-10:]

"""
Explanation: Ainda sobram 31. Para ficar só com a quantidade certa eu embaralhei a lista limpas e peguei uma fatia com as 6**5 palavras iniciais da lista.
End of explanation
"""


len(final), len(final) == len(set(final))

"""
Explanation: Vamos conferir mais uma vez se temos a quantidade certa de palavras e não temos nenhuma repetida:
End of explanation
"""


import itertools
dados5 = list(''.join(dados) for dados in itertools.product('123456', repeat=5))
len(dados5)

pares = list(zip(dados5, final))
pares[:10], pares[-10:]

"""
Explanation: Com a lista final em mãos, agora falta só gerar as chaves de 11111 a 66666 para criar um índice fácil de usar com 5 dados de 6 faces.
End of explanation
"""


with open('7776palavras.txt', 'wt', encoding='ascii') as saida:
    for par in pares:
        saida.write('%s %s\n' % par)
        
!wc '7776palavras.txt'

"""
Explanation: O último passo é escrever o arquivo 7776palavras.txt. Usando o comando wc no shell confiro se o arquivo tem o número esperado de linhas:
End of explanation
"""


