Eco sensibile al tempo


39

sfondo

Il echoprogramma è così pulito. Puoi dirgli qualsiasi cosa e ripete perfettamente le tue parole, ogni volta! Quant'è fico! Deludente, ripete l'input tutto in una volta, indipendentemente dalla velocità di digitazione, che non è molto realistico. Dovremo sistemarlo.

L'obiettivo

Il tuo programma deve prendere il suo contributo da STDIN o equivalente più vicino. Deve leggere le righe dell'utente una alla volta, possibilmente mostrando qualche prompt, fino a quando non inseriscono una riga vuota. Successivamente, stampa le righe su STDOUT o sull'equivalente più vicino, nello stesso ordine in cui sono state fornite. L'ultima riga (vuota) non viene stampata e l'ultima riga stampata non ha bisogno di avere una nuova riga finale.

Inoltre, il programma deve preservare gli intervalli di tempo tra ciascuna riga: se l'utente ha impiegato xsecondi per inserire una riga, occorrerà xqualche secondo prima che il programma la stampi. Questo vale anche per la prima e l'ultima riga; la riga vuota non viene stampata, ma il programma attende comunque prima di terminare.

Esempio

Ecco una sessione di esempio con il programma. Tutte le azioni che non producono testo sono descritte tra parentesi e il prompt (opzionale) viene visualizzato come >.

[begin program]
> fhtagn[enter; 1.48s passed since starting program]
> yum yum[enter; 3.33s passed since previous enter]
> so cool![enter; 2.24s passed since previous enter]
> [enter; 0.23s passed since previous enter]
[wait 1.48s]fhtagn
[wait 3.33s]yum yum
[wait 2.24s]so cool!
[wait 0.23s, then end program]

Senza le azioni, la sessione è simile alla seguente:

> fhtagn
> yum yum
> so cool!
> 
fhtagn
yum yum
so cool!

Regole e punteggio

I tempi di attesa dovrebbero essere precisi entro 0,01 secondi (in pratica, se l'essere umano medio non può dire la differenza, stai bene). Vince il conteggio di byte più basso e non sono consentite scappatoie standard . Se la tua lingua ha una funzione integrata proprio per questo compito, non puoi usarla.


9
Prossimo passo: Golf un programma che gioca un gioco ritmico di base: P
Sp3000,

Possiamo ignorare il tempo impiegato dal programma per emettere caratteri? Voglio dire, se posso misurare che la mia lingua impiega 0,1 secondi per generare un carattere, dovrei tenerne conto? Informativa completa, sto pianificando di usare il tempo di tick>> dell'interprete per attuare il ritardo; in questo caso, posso avere un loop che trascorre il tempo di input e quindi ignorare il tempo trascorso dal mio loop di visualizzazione?
Aaron,

1
@AaronGOUZIT Lo permetterò, purché tu sia coerente: o gli intervalli di tempo tra i momenti in cui il tuo programma inizia a stampare una linea sono tutti presi dall'utente, O i tempi di attesa tra il completamento della stampa di una linea e l'inizio della stampa i successivi sono tutti presi dall'utente.
Zgarb,

1
@TessellatingHeckler Quest'ultimo; vedi la sessione di esempio.
Zgarb,

1
@KritixiLithos Ho usato solo la mia mano destra yum yum, che era piuttosto ingombrante.
Zgarb,

Risposte:


16

CJam, 45 41 39 36 34 byte

{eslN1$}g;es](es-fm3/{){_es>}g;o}/

Questo non ha molto senso nell'interprete online ovviamente, ma funziona nell'interprete Java.

Non visualizza un prompt.

Spiegazione

{        e# Do while... (popping the condition from the stack)
  es     e#   Get the current timestamp.
  l      e#   Wait for a line to be entered and read it.
  N      e#   Push a linefeed.
  1$     e#   Copy the line we read - this terminates if the line is empty, because
         e#   empty strings/arrays are falsy.
}g
;        e# Discard the last linefeed (the one after the empty input).
es       e# Push the current timestamp (corresponding to the last, empty, input).
]        e# Wrap everything in an array. This is now a flat array containing:
         e#   - The initial timestamp.
         e#   - Three elements for each line: the line, a linefeed, the timestamp.
         e#   - Two elements for the last line: the empty string and the timestamp.
(        e# Pull off the initial time.
es-      e# Subtract the current time, which gives (minus) the difference between
         e# when a line was entered and when it should be printed back.
fm       e# This maps "minus that value" onto each element in the array. Now the lines
         e# and linefeeds are strings (arrays) - so minus is set difference, but they
         e# only contain characters, not any integers (like the difference value), so
         e# none of the strings will be affected.
         e# The timestamps on the other hand will be incremented by the time difference
         e# between reading and printing, giving the time at which each line should be
         e# printed back.
3/       e# Split the array into chunks of 3 (where the remaining two elements are
         e# just grouped as a pair).
{        e# For each of those chunks...
  )      e#   Pull off the timestamp.
  {      e#   Do while... (popping the condition from the stack)
    _    e#     Duplicate the target time.
    es>  e#     Check if it's still greater than the current time.
  }g
  ;o     e# Discard the target time and print the rest of the current chunk, which will
         e# automatically be flattened/concatenated into a single string.
}/

9

JavaScript, 119 112 byte

k=(d=Date.now)(i=j=[]);do{i[++j]=[prompt(),d()-k]}while(i[j][0]);i.map(a=>setTimeout(b=>console.log(a[0]),a[1]))

Sperando di trovare un altro paio di byte da ritagliare.


1
Potresti salvare un paio di byte con j=i=[]( ++funzionerà ancora!) Inoltre, whilenon è necessario in !=''quanto è falso! Così deluso che mi mancava map! +1
Dom Hastings,

1
Buona nota sul !=''. Era preoccupato se l'input fosse 0, ma sembra che gestisca bene. Avevo notato la []possibilità di incremento prima, ma ero stato sciocco e ho provato a farcela j++. Facendo ++jlavori, dal momento che []++è apparentemente 0 XD Grazie!
Mwr247,

1
Segna questo il giorno in cui ho appreso che c'erano dei do...whileloop in JS
Conor O'Brien,

6

JavaScript, 120 byte

Nessuna possibilità di avvicinarsi a CJam con questo approccio, ma una sceneggiatura semplice.

a=[];t=+new Date;while(s=prompt()){a.push({s:s,t:+new Date})}while(v=a.pop()){setTimeout(`console.log('${v.s}')`,v.t-t)}

1
Sembra che entrambi abbiamo scelto JS allo stesso tempo, ahah, anche se hai avuto il tuo poco prima del mio. Tuttavia, approcci diversi.
Mwr247,

@ Mwr247 In effetti, il tuo è più elegante!
Dom Hastings,

6

Pyth, 68 byte

M&p+Gb$__import__('time').sleep(H)$J].dZWeaYwaJ.dZ)aJ.dZp&gVPY-VtJJk

Sprecato molti byte nella chiamata a sleep, poiché Pyth non ha alcuna sleepfunzione.


3
Forse dovresti suggerirlo come aggiunta a Pyth.
mbomb007,

Credo che tu abbia riscontrato un errore "uno a uno" nell'attesa. Prova ad avviare il programma, aspettando, quindi digitando qualcosa e premi due volte rapidamente Invio. Stamperà immediatamente la prima riga, quindi attenderà un po 'prima di terminare.
FryAmTheEggman,

6

Ruby, 74

t,*a=Time.now
a<<[$_,t-t=Time.now]while$/<gets
a.map{|l,i|sleep -i;puts l}

Trucchi: *asulla prima riga inizializza un array vuoto. Potrei usare $*invece, ma è leggermente abbozzato poiché è popolato da alcune invocazioni e mi salva solo un byte. $/è una nuova riga ed $_è l'ultima riga recuperata da gets.

Modifica: Dormire alla fine costa 20 byte in più, probabilmente un modo per giocare a golf

t,*a=Time.now
a<<[$_,t-t=Time.now]while$/<gets
t-=Time.now
a.map{|l,i|sleep -i;puts l}
sleep -t

Penso che tu abbia bisogno di dormire sull'ultima riga, a seconda di quanto tempo ha impiegato l'utente a fornire una linea vuota.
Konrad Borowski,

Per dormire alla fine (soluzione 2), chiami Time.nowabbastanza volte che usando def n;Time.now;end, risparmiando 2 byte interi
Value Ink

6

Python 3, 124

Funziona solo su piattaforme Windows

from time import*
s=[(1,clock())]
while s[-1][0]:s+=[(input(),clock()-s[-1][1])]
[sleep(y)or x and print(x)for x,y in s[1:]]

Mantenere l'input e i tempi in elenchi separati mi è costato 3 byte in più . Probabilmente non è l'approccio migliore.

Una versione Unix da 129 byte, con credito a Mego :

from time import*
t=time
s=[(1,t())]
while s[-1][0]:s+=[(input(),t(),t()-s[-1][1])]
[sleep(y)or x and print(x)for x,z,y in s[1:]]

Non puoi usare time()invece di clock()salvare 2 byte?
Kirbyfan64sos,

4

SWI-Prolog, 185 byte

a:-b([],S),reverse(S,T),c(T),!.
b(R,S):-get_time(X),read_string(user_input,"\n","",_,A),get_time(Y),Z is Y-X,(A="",S=[A:Z|R];b([A:Z|R],S)).
c([A:Z|T]):-sleep(Z),T=[];(write(A),nl,c(T)).

Probabilmente c'è molto da golf qui, ma per ora lo farà ...


4

PowerShell, 261 190 121 95 byte

$(do{Measure-Command{$l=read-host};$l}while($l))|%{($_,(sleep -m($_.Ticks/1e4)))[($b=!$b+!$_)]}

Puntelli a Tessellatng Heckler e Tomkandy per l'assistenza e l'ispirazione del golf

Questo concetto è molto simile alla versione a 121 byte di seguito, stiamo solo creando e costruendo dinamicamente un elenco di oggetti, invece di passare attraverso un ciclo while per memorizzarli in un array esplicito $a. In entrambi i casi, tale elenco di oggetti viene pipeline nello stesso ciclo foreach |%{...}. L'indicizzazione nel selettore di array di risultati ($b=!$b+!$_)è questa volta formulata per eliminare le if($_){$_}iterazioni seguenti, che consente di risparmiare qualche byte in più.


Precedente, 121 byte

$l,$a=1,@();while($l){$t=Measure-Command{$l=read-host};$a+=$t,$l}$a|%{($(if($_){$_}),(sleep -m($_.Ticks/1e4)))[($b=!$b)]}

Ampliato e spiegato:

$l,$a=1,@()                        # Set variable $l and create array $a
while($l){                         # So long as we don't have a blank line
  $t=Measure-Command{$l=read-host} # Read the input and measure time to input
  $a+=$t,$l                        # Add those values into the array
}
$a|%{                              # For each item in $a, do
  ($(if($_){$_}),(sleep -m($_.Ticks/1e4)))[($b=!$b)]
  # Magic happens here ... first, we set $b to the NOT of it's uninitialized
  # value, so $b is initially set to truthy
  # This value in [...] selects which of the two elements ( , ) get selected
  # Truthy to start means the second command, sleep, gets chosen first, and
  # then it alternates every next item, so it sleeps, then prints, then
  # sleeps, then prints, etc., until we run out of $a
}

Precedente, 190 byte

function f {param($m)sleep -m $a[$m].totalmilliseconds}$a=1,1;while($a[-1]-ne""){$a+=Measure-Command{$b=read-host};$a+=$b}if(!($a[3])){f 2;exit}$i=2;while($i-lt$a.length){f($i++);$a[($i++)]}

function f {                        # Define a new function
  param($m)                         # with $m as input
  sleep -m $a[$m].totalmilliseconds # sleep for $a[$m] milliseconds
}
$a=1,1                              # Create new array with two elements
while($a[-1]-ne""){                 # While the last element isn't empty
  $a+=Measure-Command{$b=read-host} # Read into $b and measure how long that took,
                                    # and add the time into $a
  $a+=$b                            # Then add the input into $a
}
if(!($a[3])){                       # If the third element is empty, the user entered
                                    # a blank as the only input, so...
  f 2                               # sleep for $a[2] ms (how long it took them to hit enter)...
  exit                              # and exit the script
}                                   # Else ...
$i=2                                # Set a counter variable
while($i-lt$a.length){              # While we haven't reached the end of $a
  f($i++)                           # Sleep
  $a[($i++)]                        # Write the output
}

Precedente, 261 byte

$a=$d=@();$d+=,@(date);$x=Read-Host
while($x){$a+=,@($x);$d+=,@(date);$x=Read-Host}
if($x){0..($a.Length-1)|%{sleep -m((($d[$_+1]).ticks-($d[$_]).ticks)/1e4);$a[$_]};sleep -m((($d[-1]).ticks-($d[-2]).ticks)/1e4)}
else{sleep -m(((date).Ticks-($d[0]).Ticks)/1e4)}

Santa verbosità, Batman! Analizziamolo:

$a=$d=@()                  # Create two empty arrays
$d+=,@(date)               # Add the current time into $d
$x=Read-Host               # Read the first line
while($x){                 # So long as it's not empty
  $a+=,@($x)               # Add it into our output array
  $d+=,@(date)             # Add the current time into $d
  $x=Read-Host             # Get the next line
}
if($a){                    # So long as $a exists (i.e., the first input wasn't blank)
  0..($a.Length-1)|%{      # For-loop over the length
                           # Sleep for how long it took to do input
    sleep -m((($d[$_+1]).ticks-($d[$_]).ticks)/1e4)
    $a[$_]                 # Print out the input
  }
                           # Sleep the length it took for the final blank
  sleep -m((($d[-1]).ticks-($d[-2]).ticks)/1e4)
}
else{
                           # If we're here, the initial input was blank, so just sleep
  sleep -m(((date).Ticks-($d[0]).Ticks)/1e4)
}

144$a=1,1;while($a[-1]-ne""){$a+=Measure-Command{$b=read-host};$a+=$b};$i=2;while($i-lt$a.length){sleep -m $a[($i++)].totalmilliseconds;$a[($i++)]}
tomkandy,

@tomkandy Grazie! Aggiornato con miglioramenti.
AdmBorkBork,

@TessellatingHeckler Excellent! Stavo lottando con un modo per controllare l'alternanza in modo efficace e indicizzare in un array come quello è la scelta ovvia ora che lo vedo. Per inciso, ho golfato un altro byte rimuovendo il @da quell'array, poiché non è necessario in questo contesto, quindi fino a 121 .
AdmBorkBork,

@TimmyD quello che stavo cercando ieri era mettere coppie ($ t, $ l) in $ a creando un array nidificato. Non riuscivo a farlo funzionare, ma oggi potrei e aiuta un po 'perché non c'è bisogno di alternare, basta leggere ogni coppia e usarli. Poi ho capito: abbiamo una pipeline perfettamente funzionante che può mettere in coda le cose, perché mantenere un array? $($l=1;while($l){Measure-Command{$l=read-host};$l})|%{($_,(sleep -m($_.Ticks/1e4)))[($b=!$b+!$_)]}- e con una modifica al toggle, in modo che quando la stringa è vuota non si attiva e dorme invece - 98
TessellatingHeckler

(Fai un do{...}while($l)giro e rilascialo $l=1;per ottenere 95 )
TessellatingHeckler,

3

Perl 6, 70 caratteri

repeat {$/=now;.push($!=get,now -$/)}while $!;.map:{sleep $^b;say $^a}

L'interprete Perl 6 definisce solo tre variabili simboliche (diversamente dalla follia di Perl 5). Per essere precisi, $/, $!, e $_. Questo programma li usa tutti, per evitare il costo di dichiarare variabili usandomy .

getlegge una riga da STDIN. Non contiene una nuova riga, a differenza di Perl 5.

nowbuiltin restituisce l'ora corrente. Quando viene sottratto, fornisce un intervallo che può essere passato a una stringa.

Un metodo con nulla alla sua sinistra (come .pushe .mapin questo codice) funziona$_ .

Usando il repeat whileloop (noto come do whilein altri linguaggi di programmazione), Perl 6 sta scrivendo il timestamp corrente $/e inserisce la linea ricevuta (che memorizza anche $!) e la differenza tra l'ora corrente e il timestamp $/. A causa dell'ordine dei parametri, nownon viene calcolato fino alla ricezione di una riga.

La whilecondizione controlla se la riga non è vuota (in Perl 6, "0"è un valore vero, a differenza di Perl 5).

Dopo aver ottenuto tutti i timestamp e le linee, fornisco solo quelli per maprichiamare che dorme un po 'e dice ciò che è stato detto.


2

Groovy, 202 byte

def b={System.currentTimeMillis()};def h=[];for(;;){def t=b();def s=System.console().readLine();h.add(s+" "+(b()-t));if(s=="")break};for(def s:h){Thread.sleep((s=s.split(" "))[1].toLong());println s[0]}

Radicale.

Versione non golfata:

def b = {System.currentTimeMillis()}; // Creates a closure (short function) b that returns the current time since the epoch in milliseconds.
def h = []; // Makes an empty list
for(;;) { // Infinite loop
  def t = b(); // Get the time
  def s = System.console().readLine(); // Read a line
  h.add(s + " " + b()-t); // Add the string plus the amount of time elapsed to the list
  if(s=="") // If the string is blank
    break; // Exit loop
}
for(def s : h) { // Iterate through array
  Thread.sleep((s=s.split(" "))[1].toLong()); // Splits s into an array and puts the value in s, then takes the second element (the time), converts into a long and sleeps for that time.
  println s[0] // Print the first element (text)
}

2

JavaScript (ES6) 102

Mettere insieme gli sforzi di Mwr247 e Dom Hastings (CW)

/* for TEST */ console.log=x=>O.innerHTML+=x+'\n'

for(k=new Date,i=[];p=prompt();i.push([p,new Date]));i.map(a=>setTimeout(b=>console.log(a[0]),a[1]-k))
<pre id=O></pre>


2

MATLAB, 107 99

tic;a={};i=1;while nnz(i);i=input('','s');a=[a;{i,toc}];tic;end;for b=a';pause(b{2});disp(b{1});end

E non golfato:

tic; %Start timer
a={};
i=1; %Make us enter the while loop
while nnz(i); %While i has some non-zero elements (this is used to detect a zero length input where we end)
    i=input('','s'); %Get an input string
    a=[a;{i,toc}]; %Append the string and current time as a new cell in a
    tic; %Restart timer
end
for b=a' %For each input
    pause(b{2}); %Wait for the required time
    disp(b{1}); %Then print the string
end

Questo non sarà preciso al 100% nei tempi poiché non tiene conto del tempo impiegato per visualizzare ogni stringa, ma dovrebbe essere piuttosto veloce, quindi dal punto di vista temporale dovrebbe essere abbastanza vicino.


Dopo una rapida revisione, ho salvato alcuni byte rimuovendo l'array di celle profonde a doppio strato. Risulta che tutto ciò di cui avevo bisogno era ;di farlo dividere correttamente durante l'imballaggio.


1
Forse potresti creare una versione giocata a golf in MATL.
ckjbgames,

1

Java, usando la versione 1.04 di questa libreria , 385 byte

import sj224.lib.util.*;import java.util.*;class E{static long t(){return System.currentTimeMillis();}public static void main(String[]a) throws Exception{List<Pair<?,Long>>l=new ArrayList();Scanner i=new Scanner(System.in);while(true){long t=t();String s=i.nextLine();if(s.isEmpty())break;l.add(new Pair(s,t()-t));}for(Pair<?,Long>p:l){Thread.sleep(p.two);System.out.println(p.one);}}}

1

Cache ObjectScript, 123 byte

w() q $P($ZTS,",",2)
r f  s i=i+1,t=$$w() r x,! q:x=""  s g(i,x)=$$w()-t
    f i=1:1 s s=$O(g(i,"")) q:s=""  w s,! h g(i,s)
    q

Come al solito, questo presuppone una tabella dei simboli pulita prima di essere eseguita d r .

Questo problema non può essere risolto in ANSI MUMPS, poiché lo standard ANSI richiede una risoluzione di secondo livello solo per il tempo intrinseco $H[OROLOG]. Fortunatamente, Intersystems Caché, che è attualmente la piattaforma leader del settore per MUMPS, fornisce l' $ZT[IME]S[TAMP]intrinseca definita dall'implementazione , che fornisce una risoluzione a livello di microsecondi.

(Il punteggio era precedentemente 105 byte, ma c'era un bug.)


1

C ++ 11, 343 338 byte

Volevo vedere di quanti byte avrebbe bisogno un codice per questo in c ++. Molto più di quanto mi aspettassi. Forse ho complicato troppo la soluzione.

#include<iostream>
#include<vector>
#include<chrono>
int i;using namespace std;int main(){auto n=chrono::system_clock::now;auto t=n();string s{1};vector<string>r;vector<decltype(t-t)>w;while(s.size())getline(cin,s),r.push_back(s),w.push_back(n()-t),t=n();while(i<r.size()){while((n()-t)<w[i]);t=n();cout<<r[i++]<<(i<r.size()-1?"\n":0);}}  

Vediamo se posso ridurlo in qualche modo.


È possibile rimuovere gli spazi nella #includes e la dichiarazione del tipo per main. Sono 7 byte - non molto, ma un inizio. Potresti anche essere in grado di utilizzare autoanziché stringper s.
Alex A.

Grazie per il feedback. Terrò il tipo di ritorno per il principale. Se ricordo bene, solo per cquesto non dobbiamo specificarlo. Inizialmente ho provato a usare auto s... ma sembra che sia convertito in const char *e non std::string. Mi chiedo se posso creare un alias perwhile .
Wendelbsilva,

La rimozione del tipo restituito funziona per C ++ anche se "non dovrebbe" per lo standard. Potresti provare a creare un alias per whileusare un #defineforse.
Alex A.

1

Bash, 91 90 byte

while r=`\time -fsleep\ %e head -1`
[[ $r ]]
do printf{,\ %%b\ %q\;} "$r
"
done>t 2>&1
. t

Questo crea un file temporaneo t. Sovrascriverà un file esistente con lo stesso nome.

L'idea in sé è piuttosto breve, ma occuparsi di caratteri speciali nell'input aggiunge circa 15 byte ...


1

VBA, 233 228 byte

Sono sicuro che questo può essere giocato molto a golf. non hanno specificato quanti input ho quindi programmato duramente le lunghezze del mio array perché è più breve di allora Redim preserve.

L'input è tramite popup, l'output è debug.printperché msgboxproduce un MODAL e ferma il codice.

Non so come testare se questo è preciso agli 0,01. Forse qualcuno può testare ma sto dando al comando wait il numero in modo che DOVREBBE usare i millisecondi, ma VBA non è noto per fare ciò che dovrebbe.

Il If gotopossono essere in grado di essere sostituito da un pozzo golfed Do Loop While.

Sub a()
Dim k(99) As String
Dim h(99) As Date
b:
t=Now()
i=i+1
k(i)=InputBox("")
h(i)=Now()-t
If k(i)<>"" Then GoTo b
For u=1 To i
Application.Wait (Now()+(Format(h(u),"s")&Format(h(u),"ms"))/10^8)
Debug.Print k(u)
Next
End Sub

Non funzionerà in Access VBA, perché l'accesso non ha un comando wait perché Microsoft odia la coerenza


0

SmileBASIC, 122 byte

DIM A$[0],T[0]@L
C=MAINCNT
LINPUT S$PUSH A$,S$PUSH T,MAINCNT-C
IF""<S$GOTO@L@P
WAIT SHIFT(T)IF""<A$[0]THEN?SHIFT(A$)GOTO@P

Penso che questo potrebbe essere leggermente ridotto.


0

C UNIX, 272 byte

#include <stdio.h>
#include <unistd.h>
#define P printf
i;r;c;main(){char*L[99]={0};size_t s;long T[99]={0};while(1){P(">  ");T[c]=time(0);r=getline(&L[c],&s,stdin);T[c]=time(0)-T[c];if(r==-1|!(*L[c]-10))break;c++;}while(i<c){P("> ");usleep(T[i]*1000);P("%s", L[i]);i++;}}

dettagliata

#include <stdio.h>
#include <unistd.h>

int main(void)
{
    int i = 0, c = 0, r;
    char * L[99] = {0};
    size_t size;
    long T[99] = {0L};

    while(1)
    {
        printf("> ");
        T[c] = time(0);
        r = getline(&L[c], &size, stdin);
        T[c] = time(0) - T[c];
        if(r == (-1)) break;
        if(*L[c]=='\0' || *L[c]=='\n') break;
        c = c + 1;
    }

    while(i < c)
    {
        printf(" %ld > ",T[i]);
        usleep(T[i]*1000);
        printf("%s", L[i]);
        i = i + 1;
    }

    return 0;
}

0

Bash, 22 byte

nc -l 3|nc localhost 3

Quindi digitare l'ingresso e premere Invio.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.