Justifique totalmente e hifenize um bloco de texto


26
Given  a width  and  a block  of
text containing possible hyphen-
ation points,  format it  fully-
justified (in monospace).

Totalmente justificado significa que ele está alinhado à esquerda e à direita e é alcançado aumentando o espaçamento entre as palavras até que cada linha se ajuste.

Palavras-chave:

Entrada

Você pode receber entradas em qualquer formato que desejar. Você receberá:

  • Uma largura de destino (em caracteres), no intervalo de 5 a 100 (inclusive);
  • Um bloco de texto contendo palavras possivelmente hifenizadas. Pode ser uma cadeia de caracteres separada por espaço, uma matriz de palavras ou uma matriz de matrizes de fragmentos de palavras (ou qualquer outra representação de dados que você desejar).

Uma entrada típica pode ser:

Width: 25
Text:  There's no bu-si-ne-ss lik-e s-h-o-w busine-ss, n-o bus-iness I know.

Onde os hífens indicam possíveis pontos de hifenização e os espaços indicam limites de palavras. Uma possível representação alternativa do texto:

[["There's"], ["no"], ["bu", "si", "ne", "ss"], ["lik", "e"], (etc.)]

Saída

O texto de entrada com espaços adicionados entre palavras, novas linhas na largura da coluna e pontos de hifenização escolhidos para justificá-lo totalmente à largura da coluna. Para funções, uma matriz de seqüências de caracteres (uma para cada linha) pode ser retornada em vez de usar a separação de nova linha.

Uma saída possível para a entrada acima pode ser:

There's no  business like
show  business,  no  bus-
iness I know.

Observe que todos os hífens foram removidos, exceto aquele no "bus-iness" final, que é mantido para mostrar que a palavra passa para a próxima linha e foi escolhido para garantir que a segunda linha contenha o máximo de texto possível.

Regras

  • Dentro de cada linha, o número de espaços entre as palavras não pode variar em mais de 1, mas onde você insere os espaços extras depende de você:

    hello hi foo     bar    <-- not permitted (1,1,5)
    hello  hi foo    bar    <-- not permitted (2,1,4)
    hello  hi  foo   bar    <-- OK (2,2,3)
    hello  hi   foo  bar    <-- OK (2,3,2)
    hello   hi  foo  bar    <-- OK (3,2,2)
    
  • Nenhuma linha pode começar ou terminar com espaços (exceto a última linha, que pode terminar com espaços).

  • A última linha deve ser justificada à esquerda, contendo espaços únicos entre cada palavra. Pode ser seguido por espaço em branco arbitrário / uma nova linha, se desejado, mas isso não é necessário.

  • As palavras consistirão em AZ, az, 0-9 e pontuação simples ( .,'()&)

  • Você pode assumir que nenhum fragmento de palavra será maior que a largura do alvo e sempre será possível preencher linhas de acordo com as regras (ou seja, haverá pelo menos 2 fragmentos de palavra em cada linha ou 1 fragmento de palavra que preenche a linha perfeitamente)

  • Você deve escolher pontos de hifenização que maximizem o número de caracteres de palavras nas linhas anteriores (ou seja, as palavras devem ser consumidas avidamente pelas linhas), por exemplo:

    This is an input stri-ng with hyph-en-at-ion poi-nts.
    
    This     is     an     input    stri-      <-- not permitted
    ng with hyphenation points.
    
    This  is an  input string  with hyph-      <-- not permitted
    enation points.
    
    This is an input  string with hyphen-      <-- OK
    ation points.
    
  • O menor código em bytes ganha

Exemplos

Width: 20
Text:  The q-uick brown fox ju-mp-s ove-r t-h-e lazy dog.

The quick  brown fox
jumps over the  lazy
dog.

Width: 32
Text: Given a width and a block of text cont-ain-ing pos-sible hyphen-ation points, for-mat it ful-ly-just-ified (in mono-space).

Given  a width  and  a block  of
text containing possible hyphen-
ation points,  format it  fully-
justified (in monospace).

Width: 80
Text:  Pro-gram-ming Puz-zles & Code Golf is a ques-tion and ans-wer site for pro-gram-ming puz-zle enth-usi-asts and code golf-ers. It's built and run by you as part of the St-ack Exch-ange net-work of Q&A sites. With your help, we're work-ing to-g-et-her to build a lib-rary of pro-gram-ming puz-zles and their sol-ut-ions.

Programming Puzzles &  Code Golf  is a question and answer  site for programming
puzzle enthusiasts  and code golfers.  It's built and run  by you as part of the
Stack Exchange network  of Q&A sites. With your help,  we're working together to
build a library of programming puzzles and their solutions.

Width: 20
Text:  Pro-gram-ming Puz-zles & Code Golf is a ques-tion and ans-wer site for pro-gram-ming puz-zle enth-usi-asts and code golf-ers. It's built and run by you as part of the St-ack Exch-ange net-work of Q&A sites. With your help, we're work-ing to-g-et-her to build a lib-rary of pro-gram-ming puz-zles and their sol-ut-ions.

Programming  Puzzles
&  Code  Golf  is  a
question and  answer
site for programming
puzzle   enthusiasts
and  code   golfers.
It's  built  and run
by  you  as  part of
the  Stack  Exchange
network    of    Q&A
sites.   With   your
help,  we're working
together to  build a
library of  program-
ming   puzzles   and
their solutions.

Width: 5
Text:  a b c d e f g h i j k l mm nn oo p-p qq rr ss t u vv ww x yy z

a b c
d e f
g h i
j k l
mm nn
oo pp
qq rr
ss  t
u  vv
ww  x
yy z

Width: 10
Text:  It's the bl-ack be-ast of Araghhhhh-hhh-h-hhh-h-h-h-hh!

It's   the
black  be-
ast     of
Araghhhhh-
hhhhhhhhh-
hhh!

Yesss, finalmente outro (baseado em texto) tipografia desafio :-)
ETHproductions

1
@ Adám yes to builtins: não há restrições de código e o código mais curto vence. Embora, é claro, possa ser uma resposta chata! Quanto às bibliotecas, você pode, desde que a biblioteca esteja disponível gratuitamente e marque sua resposta como "idioma + biblioteca". Além disso, a versão da biblioteca deve ser anterior a esse desafio.
Dave

1
No caso de uma linha poder terminar com um hífen ou com um único caractere, por exemplo, anybod-ycom a largura 7, podemos escolher a saída anybodyou anybod-\ny?
darrylyeo

1
@JonathanAllan yes; desculpe, eu vou consertar isso
Dave

3
@darrylyeo não, você precisa exibir a palavra completa nesse caso, pois ela deve ter avidamente o máximo de caracteres possível em cada linha.
Dave

Respostas:


7

JavaScript (ES6), 218 bytes

w=>s=>s.map((c,i)=>c.map((p,j)=>(k+p)[l="length"]-w-(b=!i|j>0)+(j<c[l]-1)<0?k+=b?p:" "+p:(Array(w-k[l]-b).fill(h=k.split` `).map((_,i)=>h[i%(h[l]-1)]+=" "),o.push(h.join` `+(b?"-":"")),k=p)),o=[],k="")&&o.join`
`+`
`+k

Leva argumentos na sintaxe de currying (f(width)(text) ) e a entrada de texto está no formato de matriz dupla descrito no desafio. Strings são convertidas para esse formato via .split` `.map(a=>a.split`-`)). Além disso, as novas linhas são literalmente novas dentro das seqüências de caracteres do modelo.

Sem golfe e reorganizado

width=>string=> {
    out=[];
    line="";
    string.map((word,i)=> {
        word.map((part,j)=> {

            noSpaceBefore = i==0 || j>0;
            if ((line+part).length - width - noSpaceBefore + (j<word.length-1) < 0) {
                line += noSpaceBefore ? part : " "+part;
            }
            else {
                words=line.split` `;
                Array(width - line.length - noSpaceBefore).fill()
                    .map((_,i) => words[i % (words.length-1)] += " ");
                out.push(words.join(" ") + (noSpaceBefore? "-" : ""));
                line=part;
            }
        });
    });
    return out.join("\n") + "\n"+line
}

A idéia aqui era percorrer cada parte de toda a cadeia e construir cada linha, uma parte de cada vez. Quando uma linha é concluída, aumenta o espaçamento da palavra da esquerda para a direita até que todos os espaços extras sejam colocados.

Snippet de teste

f=
w=>s=>s.map((c,i)=>c.map((p,j)=>(k+p)[l="length"]-w-(b=!i|j>0)+(j<c[l]-1)<0?k+=b?p:" "+p:(Array(w-k[l]-b).fill(h=k.split` `).map((_,i)=>h[i%(h[l]-1)]+=" "),o.push(h.join` `+(b?"-":"")),k=p)),o=[],k="")&&o.join`
`+`
`+k
<style>*{font-family:Consolas,monospace;}</style>
<div oninput="O.innerHTML=f(+W.value)(S.value.split` `.map(a=>a.split`-`))">
Width: <input type="number" size="3" min="5" max="100" id="W">
Tests: <select id="T" style="width:20em" oninput="let x=T.value.indexOf(','),s=T.value;W.value=s.slice(0,x);S.value=s.slice(x+2)"><option></option><option>20, The q-uick brown fox ju-mp-s ove-r t-h-e lazy dog.</option><option>32, Given a width and a block of text cont-ain-ing pos-sible hyphen-ation points, for-mat it ful-ly-just-ified (in mono-space).</option><option>80, Pro-gram-ming Puz-zles & Code Golf is a ques-tion and ans-wer site for pro-gram-ming puz-zle enth-usi-asts and code golf-ers. It's built and run by you as part of the St-ack Exch-ange net-work of Q&A sites. With your help, we're work-ing to-g-et-her to build a lib-rary of pro-gram-ming puz-zles and their sol-ut-ions.</option><option>20, Pro-gram-ming Puz-zles & Code Golf is a ques-tion and ans-wer site for pro-gram-ming puz-zle enth-usi-asts and code golf-ers. It's built and run by you as part of the St-ack Exch-ange net-work of Q&A sites. With your help, we're work-ing to-g-et-her to build a lib-rary of pro-gram-ming puz-zles and their sol-ut-ions.</option><option>5, a b c d e f g h i j k l mm nn oo p-p qq rr ss t u vv ww x yy z</option><option>10, It's the bl-ack be-ast of Araghhhhh-hhh-h-hhh-h-h-h-hh</option></select><br>
Text: &nbsp;<textarea id="S" cols="55" rows="4"></textarea>
</div>
<pre id="O" style="border: 1px solid black;display:inline-block;"></pre>


8

GNU sed -r, 621 bytes

Aceita entrada como duas linhas: a largura como um número unário primeiro e a sequência de caracteres segundo.

Estou certo de que isso poderia ser jogado muito mais, mas eu já joguei muito tempo nele.

x;N
G
s/\n/!@/
:
/@\n/bZ
s/-!(.*)@ /\1 !@/
s/!(.*[- ])(@.*1)$/\1!\2/
s/@(.)(.*)1$/\1@\2/
s/-!(.*-)(@.*)\n$/\1!\2\n1/
s/(\n!@) /\1/
s/-!(.* )(@.*)\n$/\1!\2\n1/
s/-!(.*-)(@.*1)$/\1!\21/
s/!(.*)-@([^ ]) /\1\2!@ /
t
s/ !@(.*)\n$/\n!@\1#/
s/!(.*-)@(.*)\n$/\1\n!@\2#/
s/!(.*)(@ | @)(.*)\n$/\1\n!@\3#/
s/-!(.*[^-])@([^ ]) (.*)\n$/\1\2\n!@\3#/
s/!(.+)@([^ ].*)\n$/\n!@\1\2#/
/#|!@.*\n$/{s/#|\n$//;G;b}
:Z
s/-?!|@.*//g
s/ \n/\n/g
s/^/%/
:B
G
/%.*\n.+\n/!bQ
:C
s/%([^\n])(.*)1$/\1%\2/
tC
s/([^\n]+)%\n/%\1\n/
:D
s/%([^ \n]* )(.*)1$/\1 %\2/
tD
s/(^|\n)([^\n]+)%(.*1)$/\1%\2\3/
tD
s/%([^\n]*)\n(.*)\n$/\1\n%\2/
tB
:Q
s/%(.*)\n1*$/\1/

Experimente online!

Explicação

O programa funciona em duas fases: 1. Dividir e 2. Justificar. Para o abaixo, suponha que nossa entrada seja:

111111111111
I re-mem-ber a time of cha-os, ru-ined dreams, this was-ted land.

Configuração

Primeiro, lemos a entrada, movendo a primeira linha (a largura como um número unário) para o espaço de espera ( x), depois anexando a próxima linha ( N) e, em seguida, uma cópia da largura do espaço de espera ( G) para o espaço do padrão. Desde que Nnos deixou uma liderança \n, substituí-la por !@, que usaremos como cursores na Fase 1.

x;N
G
s/\n/!@/

Agora, o conteúdo do espaço de espera é 1111111111111(e não será alterado a partir de agora) e o espaço do padrão é (no formato do lcomando "imprimir sem ambiguidade" do sed ):

!@I re-mem-ber a time of cha-os, ru-ined dreams, this was-ted land.\n111111111111$

Fase 1

Na Fase 1, o @cursor principal avança um caractere de cada vez e, para cada caractere, a 1é removida do "contador" no final do espaço do padrão. Em outras palavras, @foo\n111$, f@oo\n11$, fo@o\n1$, etc.

O !cursor segue atrás do @cursor, marcando os lugares que poderíamos quebrar se o contador atingir 0 no meio da linha. Algumas rodadas ficariam assim:

!@I re-mem-ber a time of cha-os, ru-ined dreams, this was-ted land.\n111111111111$
!I@ re-mem-ber a time of cha-os, ru-ined dreams, this was-ted land.\n11111111111$
!I @re-mem-ber a time of cha-os, ru-ined dreams, this was-ted land.\n1111111111$

Aqui há um padrão que reconhecemos: um espaço imediatamente seguido pelo @cursor. Como o contador é maior que 0, avançamos o marcador de quebra e continuamos avançando no cursor principal:

I !@re-mem-ber a time of cha-os, ru-ined dreams, this was-ted land.\n1111111111$
I !r@e-mem-ber a time of cha-os, ru-ined dreams, this was-ted land.\n111111111$
I !re@-mem-ber a time of cha-os, ru-ined dreams, this was-ted land.\n11111111$
I !re-@mem-ber a time of cha-os, ru-ined dreams, this was-ted land.\n1111111$

Aqui está outro padrão:, -@e ainda temos 7 no contador, então avançamos o cursor de quebra novamente e continuamos avançando:

I re-!mem-@ber a time of cha-os, ru-ined dreams, this was-ted land.\n111$

Aqui está um padrão diferente: um hífen imediatamente anterior ao cursor de quebra e outro precedendo o cursor principal. Removemos o primeiro hífen, avançamos o cursor de quebra e, como removemos um caractere, adicionamos 1 ao contador.

I remem-!@ber a time of cha-os, ru-ined dreams, this was-ted land.\n1111$

Continuamos avançando no cursor principal:

I remem-!ber@ a time of cha-os, ru-ined dreams, this was-ted land.\n1$

Semelhante a antes, mas desta vez o cursor principal precede um espaço em vez de seguir um hífen. Removemos o hífen, mas como também avançamos o cursor principal, não incrementamos nem diminuímos o contador.

I remember !@a time of cha-os, ru-ined dreams, this was-ted land.\n1$
I remember !a@ time of cha-os, ru-ined dreams, this was-ted land.\n$

Finalmente, nosso contador chegou a zero. Como o caractere após o cursor principal é um espaço, inserimos uma nova linha e colocamos os dois cursores imediatamente após. Depois reabastecemos o contador ( G) e começamos novamente.

I remember a\n!@ time of cha-os, ru-ined dreams, this was-ted land.\n111111111111$

A fase 1 continua, avançando os cursores e combinando vários padrões, até que o @cursor chegue ao final da string.

# Phase 1
:
  # End of string; branch to :Z (end of phase 1)
  /@\n/bZ

  # Match -!.*@_
  s/-!(.*)@ /\1 !@/

  # Match [-_]@ and >0
  s/!(.*[- ])(@.*1)$/\1!\2/

  # Advance cursor
  s/@(.)(.*)1$/\1@\2/

  # Match -!.*-@ and 0; add 1
  s/-!(.*-)(@.*)\n$/\1!\2\n1/

  # Match \n!@_
  s/(\n!@) /\1/

  # Match -!.*_@ and 0; add 1
  s/-!(.* )(@.*)\n$/\1!\2\n1/

  # Match -!.*-@ and >0; add 1
  s/-!(.*-)(@.*1)$/\1!\21/

  # Match -@[^_]_
  s/!(.*)-@([^ ]) /\1\2!@ /

  # If there were any matches, branch to `:`
  t

  # Match _!@ and 0
  s/ !@(.*)\n$/\n!@\1#/

  # Match -@ and 0
  s/!(.*-)@(.*)\n$/\1\n!@\2#/

  # Match @_|_@ and 0
  s/!(.*)(@ | @)(.*)\n$/\1\n!@\3#/

  # Match -!.*[^-]@[^_]_ and 0
  s/-!(.*[^-])@([^ ]) (.*)\n$/\1\2\n!@\3#/

  # Match !.+@[^_] and 0
  s/!(.+)@([^ ].*)\n$/\n!@\1\2#/

  # Match marked line (#) or !@ and 0
  /#|!@.*\n$/{
    # Remove mark; append width and branch to `:`
    s/#|\n$//
    G
    b
  }

:Z

# Cleanup
s/-?!|@.*//g
s/ \n/\n/g

No final da Fase 1, nosso espaço padrão se parece com o seguinte:

I remember a\ntime of cha-\nos, ruined\ndreams, this\nwasted land.

Ou:

I remember a
time of cha-
os, ruined
dreams, this
wasted land.

Fase 2

Na fase 2, usamos %como cursor e usamos o contador de maneira semelhante, começando assim:

%I remember a\ntime of cha-\nos, ruined\ndreams, this\nwasted land.\n111111111111$

Primeiro, contamos os caracteres na primeira linha avançando o cursor e removendo 1s do contador, após o que temos;

I remember a%\ntime of cha-\nos, ruined\ndreams, this\nwasted land.\n$

Como o contador é 0, não fazemos mais nada nessa linha. A segunda linha também tem o mesmo número de caracteres que o contador, então vamos pular para a terceira linha:

I remember a\ntime of cha-\nos, ruined%\ndreams, this\nwasted land.\n11$

O contador é maior que 0, então movemos o cursor de volta para o início da linha. Em seguida, encontramos a primeira sequência de espaços e adicionamos um espaço, diminuindo o contador.

I remember a\ntime of cha-\nos, % ruined\ndreams, this\nwasted land.\n1$

O contador é maior que 0; Como o cursor já está na última (única) sequência de espaços na linha, nós o movemos de volta para o início da linha e fazemos novamente:

I remember a\ntime of cha-\nos,  % ruined\ndreams, this\nwasted land.\n$

Agora, o contador é 0, então movemos o cursor para o início da próxima linha. Repetimos isso para todas as linhas, exceto a última. Esse é o final da fase 2 e o final do programa! O resultado final é:

I remember a
time of cha-
os,   ruined
dreams, this
wasted land.
# Phase 2
# Insert cursor
s/^/%/
:B
  # Append counter from hold space
  G
  # This is the last line; branch to :Q (end of phase 1)
  /%.*\n.+\n/!bQ

  :C
    # Count characters
    s/%([^\n])(.*)1$/\1%\2/
    tC

  # Move cursor to beginning of line
  s/([^\n]+)%\n/%\1\n/

  :D
    # Add one to each space on the line as long as counter is >0
    s/%([^ \n]* )(.*)1$/\1 %\2/
    tD

    # Counter is still >0; go back to beginning of line
    s/(^|\n)([^\n]+)%(.*1)$/\1%\2\3/
    tD

    # Counter is 0; move cursor to next line and branch to :B
    s/%([^\n]*)\n(.*)\n$/\1\n%\2/
    tB

:Q

# Remove cursor, any remaining 1s
s/%(.*)\n1*$/\1/

Isso é incrível, mas quando eu executo usando gsed (GNU sed) 4.4eu recebo gsed: -e expression #1, char 16: ":" lacks a label. Você pode adicionar uma observação sobre exatamente como a está invocando? (Estou usando printf "%s\n%s" "$1" "$2" | gsed -r '<code here>';)
Dave

@Dave Isso funciona para mim no GNU sed 4.2. Aqui está uma lista: gist.github.com/jrunning/91a7584d95fe10ef6b036d1c82bd385c Observe que a página sed do TiO não parece respeitar a -rsinalização, e é por isso que o link do TiO acima vai para a página do bash.
Jordânia

Ah, eu não tinha notado o link TiO. Isso vai fazer por mim; tenha um +1! Existem dois pequenos erros no último exemplo (o "animal preto"): ele imprime a penúltima linha um caractere curto e perde o final !(embora desde que eu errei !na lista de possíveis caracteres especiais, eu não vai segurar isso contra isso).
Dave

5

JavaScript (ES6), 147 bytes

Toma entrada como (width)(text).

w=>F=(s,p=S=' ')=>(g=([c,...b],o='',h=c=='-')=>c?o[w-1]?c==S&&o+`
`+F(b):o[w+~h]?o+c+`
`+F(b):c>S?g(b,h?o:o+c):g(b,o+p)||g(b,o+p+c):o)(s)||F(s,p+S)

Experimente online!

Comentado

w =>                              // w = requested width
  F = (                           // F is a recursive function taking:
    s,                            //   s = either the input string (first iteration) or an
                                  //       array of remaining characters (next iterations)
    p =                           //   p = current space padding
    S = ' '                       //   S = space character
  ) => (                          //
    g = (                         // g is a recursive function taking:
      [c,                         //   c   = next character
          ...b],                  //   b[] = array of remaining characters
      o = '',                     //   o   = output for the current line
      h = c == '-'                //   h   = flag set if c is a hyphen
    ) =>                          //
      c ?                         // if c is defined:
        o[w - 1] ?                //   if the line is full:
          c == S &&               //     fail if c is not a space
          o + `\n` + F(b)         //     otherwise, append o + a linefeed and process the
                                  //     next line
        :                         //   else:
          o[w + ~h] ?             //     if this is the last character and c is a hyphen:
            o + c + `\n` + F(b)   //       append o + c + a linefeed and process the next
                                  //       line
          :                       //     else, we process the next character:
            c > S ?               //       if c is not a space:
              g(b, h ? o : o + c) //         append c if it's not a hyphen
            :                     //       else:
              g(b, o + p) ||      //         append either the current space padding
              g(b, o + p + c)     //         or the current padding and one extra space
      :                           // else:
        o                         //   success: return o
  )(s)                            // initial call to g() with s
  || F(s, p + S)                  // in case of failure, try again with a larger padding

4

APL (Dyalog Unicode) , 129 123 121 118 111 109 107 104 100 95 bytes SBCS

{⊃⌽m←⍺≥-⌿c⍪+\⊢c' -'∘.≠⍵:⊂⍵/⍨⊢⌿c⋄(⊂∊ll[(⍺-≢l)⍴⍸' '=l],←⊃0l←⍵/⍨n×⊣⌿c⊖⍨1n),⍺∇⍵/⍨~n←⌽∨\⌽m>×⌿c}

Experimente online!



1

Python 2 , 343 bytes

W,T=input()
T+=' '
L,l=[],len
while T:
 p,r=0,''
 for i in range(l(T)):
  s=T[:i].replace('-','')
  if'-'==T[i]:s+='-'
  if T[i]in' -'and W-l(s)>=0:p,r=i,s
 R=r.split()
 if R:
  d,k=W-l(''.join(R)),0
  for j in range(d):
   R[k]+=' '
   k+=1
   if k==l(R)-1:k=0
  L+=[''.join(R)]
  T=T[p+1:]
print'\n'.join(L[:-1])
print' '.join(L[-1].split())

Experimente online!

The  input  is a block of text
containing possibly hyphenated
words.  For  each space/hyphen
position  p  the code computes
l(p)  the  length  of the line
induced  by  slipping the text
to this space/hyphen. Then the
code choses the position p for
which  the  length l(p) is the
closest  to  the given width W
(and  l(p)<=W).  If l(p)<W the
code  adds spaces  fairly  in-
between  the  words to achieve
the length W.

Embora a entrada possa estar no formato que você quiser, ela ainda deve vir de STDIN ou de parâmetros. Veja padrões para I / O . Geralmente, não permitimos que "entrada" seja de variáveis ​​pré-atribuídas.
mbomb007 15/07

Você pode salvar um byte fazendo em print'\n'.join(L[:-1])vez defor e in L[:-1]:print e
mbomb007 15/07

@ mbomb007 ok sim, farei as alterações necessárias para respeitar a E / S
mdahmoune 15/07
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.