Eventi più alti o più bassi?


13

Sfida:

ingressi:

  • Una stringa contenente ASCII stampabile (esclusi spazi, tabulazioni e nuove righe)
  • Un booleano

Produzione:

Le parti della stringa sono divise in quattro gruppi:

  • Lettere minuscole
  • Lettere maiuscole
  • cifre
  • Altro

Sulla base del valore booleano, produciamo la più alta occorrenza di uno (o più) di questi quattro gruppi, o il più basso, sostituendo tutto il resto con spazi.

Per esempio:

Input: "Just_A_Test!"
contiene:
- 3 lettere maiuscole: JAT
- 6 lettere minuscole: ustest
- 0 cifre
- 3 altre:__!

Questi sarebbero gli output per trueo false:

true:   " ust    est "

// digits have the lowest occurrence (none), so everything is replaced with a space
false:  "            "

(Nota: è consentito ignorare gli spazi finali, quindi anche gli output possono essere " ust est"e ""rispettivamente.)

Regole della sfida:

  • L'input non sarà mai vuoto o con spazi e consisterà solo di ASCII stampabili nell'intervallo 33-126o '!'attraverso '~'.
  • Se lo si desidera, è possibile accettare input e / o output come array di caratteri o elenco.
  • Sono consentiti due valori coerenti e distinti per il valore booleano: true/ false; 1/ 0; 'H'/ 'L'; "highest"/ "lowest"; ecc. Nota che questi valori distinti dovrebbero essere usati (in qualche modo) come valori booleani! Quindi non è consentito inserire due programmi completi, uno che fornisce il risultato corretto per truee l'altro per false, e quindi avere solo il codice effettivo <run input with parameter>. Ho aggiunto una nuova scappatoia predefinita, sebbene possa ancora usare molta ottimizzazione per quanto riguarda le definizioni.
  • Se la ricorrenza di due o più gruppi è la stessa, generiamo tutte quelle occorrenze.
  • Gli spazi finali necessari sono facoltativi e anche una singola nuova riga finale è facoltativa. Gli spazi iniziali necessari sono obbligatori. E non sono ammessi altri spazi iniziali o nuove righe.

Regole generali:

  • Questo è , quindi vince la risposta più breve in byte.
    Non lasciare che le lingue di code-golf ti scoraggino dal pubblicare risposte con lingue non codegolfing. Prova a trovare una risposta il più breve possibile per "qualsiasi" linguaggio di programmazione.
  • Per la tua risposta valgono regole standard , quindi puoi usare STDIN / STDOUT, funzioni / metodo con i parametri corretti, programmi completi. La tua chiamata.
  • Le scappatoie predefinite sono vietate.
  • Se possibile, aggiungi un link con un test per il tuo codice.
  • Inoltre, si prega di aggiungere una spiegazione, se necessario.

Casi test:

Inputs:                              Output:

"Just_A_Test!", true                 " ust    est "     (or " ust    est")
"Just_A_Test!", false                "            "     (or "")
"Aa1!Bb2@Cc3#Dd4$", either           "Aa1!Bb2@Cc3#Dd4$"
"H@$h!n9_!$_fun?", true              " @$ !  _!$_   ?"
"H@$h!n9_!$_fun?", false             "H     9        "  (or "H     9")
"A", true                            "A"
"A", false                           " "                (or "")
"H.ngm.n", true                      "  ngm n"
"H.ngm.n", false                     "       "          (or "")
"H.ngm4n", false                     "H.   4 "          (or "H.   4")

È accettabile produrre il maggior numero / meno come voci separate? Ad esempio, per il caso di test "hashing is fun", è possibile "H "e " 9 "(con spazi appropriati) essere emesso anziché "H 9"?
AdmBorkBork,

@AdmBorkBork Non capisco cosa intendi; entrambi i He 9fanno parte del "meno".
Erik the Outgolfer,

Il valore di input booleano può essere "max"/ "min", che viene quindi utilizzato come Math[b]riferimento a Math.maxo Math.min?
Justin Mariner

@JustinMariner Sai .. mi sono ricreduto, mi dispiace. Immagino sia per JS? Penso che molti linguaggi di programmazione possano utilizzare qualcosa del genere, quindi troppe risposte esistenti dovrebbero essere cambiate. Mi dispiace, dovrai mantenere la b?"max":"min"risposta. È una linea sottile, immagino, forse dovrei usare un valore di verità / falsità la prossima volta ..
Kevin Cruijssen,

Risposte:


3

Buccia , 27 26 24 22 byte

-2 byte grazie a Zgarb

-2 byte grazie a Leo

Prende ' 'come Falsee 'a'come True(In Husk, gli spazi bianchi in Fasly e tutti gli altri personaggi sono Truthy)

Fż▲→ġ#¬Ö#≡⁰Ṫḟë½D±o¬□m;

Provalo online!

Come funziona?

Fż▲→ġ#¬Ö#≡⁰Ṫḟë½D±o¬□m;   Function, takes a character c and a string S as arguments
                    m;   Wrap each character in S into it's own string
             ë           List of four functions returning booleans:
              ½D±o¬      Lower case?,Upper case?,Digit?,Not alphanumeric?
           Ṫḟ            Outer product with find†
       Ö#≡⁰              Sort on how many characters have the same Truthyness as c
    ġ#¬                  Group strings with equal numbers of spaces
   →                     Take the last group
Fż▲                      Squash it all into one list

è una funzione che accetta un predicato pe un elenco Le restituisce il primo elemento Lsoddisfacente p. Se nessun elemento soddisfa pun argomento predefinito viene restituito. In questo caso ' '. Applicando a una stringa di un carattere, stiamo essenzialmente dicendoif p c then c else ' ' .

È la funzione che prende una funzione fe due liste L1, L2. Restituisce una tabella di fapplicata su tutte le coppie di L1e L2. In questo caso fè , L1è la nostra lista di 4 funzioni, ed L2è l'elenco di uno stringhe di caratteri.

Dopo Ṫḟabbiamo un elenco di stringhe in cui ogni stringa è il risultato della sostituzione di caratteri che non soddisfano una delle regole con a ' '.

NB: Nelle versioni più recenti di Husk, ġ#¬Ö#≡⁰può essere sostituito da k#≡⁰un salvataggio di 3 byte!


Per curiosità: perché ' 'e 'a'? Forse lo capisco meglio quando viene aggiunta la spiegazione, perché non riesco a leggere Husk. ;)
Kevin Cruijssen,

Bello! Ecco 24 byte usando .
Zgarb,

@Zgarb Grazie! Non ho davvero capito cosa Mmmstesse facendo :)
H.Piz il

Sfortunatamente questo non aiuta molto con il golf, ma S`?' potrebbe essere più semplice come?IK'
Leo

Tendo ad evitare l'uso I, a volte fa sì che l'interprete impieghi un'eternità. Sembra anche dispendioso.
H.Piz,

7

Gelatina , 31 byte

ØṖḟØBṭØBUs26¤f€³Lİ⁴¡$ÐṀFf
¹⁶Ç?€

Provalo online!

I valori booleani sono 2e 1(o qualsiasi altra coppia pari / dispari positiva), che rappresentanoTrue e Falserispettivamente. Proverò ad aggiungere una spiegazione dopo ulteriore golf.

Grazie a caird coinheringaahing per il salvataggio di 2 byte e a Lynn per il salvataggio di 4 byte! Grazie a uno dei trucchi Erik , che mi ha ispirato a salvare 4 byte!

Come funziona

Questa è la spiegazione per la versione a 35 byte. Il nuovo fa più o meno lo stesso (ma ottimizzato un po 'da Lynn), quindi non lo cambierò.

ØBUs26f€³µ³ḟØBW,µẎLİ⁴¡$ÐṀF - Niladic helper link.
ØB                         - String of base digits: '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ
                             abcdefghijklmnopqrstuvwxyz'. 
  U                        - Reverse.
   s26                     - Chop into sublists of length 26, preserving shorter
                             trailing substrings.
      f€³                  - For each, keep the common characters with the input.
            ØB             - Base digits.
          ³ḟ               - Get the signs in the input. Filter the characters of the
                             input that aren't alphanumeric.
              W,µẎ         - Concatenate (wrap, two element list, tighten).
                       ÐṀ  - Keep the elements with maximal link value.
                  L        - Length.
                    ⁴¡     - Do N times, where N is the second input.
                   İ       - Inverse. Computes 1 ÷ Length. 2 maps to the length itself,
                             because 1 ÷ (1 ÷ Length) = length; 1 yields
                             (1 ÷ Length), swapping the maximal numbers with minimal ones.
                         F - Flatten.

¹⁶e¢$?€ - Main link.
      € - For each character.
   e¢?  - If it is contained by the last link (called niladically), then:
¹       - Identity, the character itself, else:
 ⁶      - A space.


@cairdcoinheringaahing Grazie! :) Volevo giocare a golf quella parte da quando ho pubblicato la risposta, ma non sono riuscito a capire perché non ha funzionato ... Avevo un µD estraneo :
Mr. Xcoder

31 byte : genera le classi come ØṖḟØBṭØBUs26¤, quindi verifica l'appartenenza con fe Çanziché e¢$.
Lynn,


5

R , 193 186 179 158 byte

-7 byte grazie a NofP e al suo suggerimento di cbind

-6 byte usando outer, -1 byte commutando [^a-zA-Z0-9]con[[:punct:]]

-21 byte grazie a MickyT per indicare un elenco di caratteri è consentito

function(S,B){y=outer(c("[a-z]","[A-Z]","\\d","[[:punct:]]"),S,Vectorize(grepl))
S[!colSums(y[(s=rowSums(y))=="if"(B,max,min)(s),,drop=F])]=" "
cat(S,sep='')}

Verifica tutti i casi di test

Prende 1/Tcome truey ( max) e 0/Fcome falsey ( min) e prende Scome un elenco di singoli caratteri.

Provalo online!

Nella mia versione originale (con i suggerimenti di NofP), la matrice yviene costruita valutando grepl(regex, S)per ciascuna regex, quindi concatenandole insieme come colonne di una matrice. Ciò comporta più chiamate a grepl, ma come Sè stato risolto, sembrava che fosse necessario fare qualcos'altro. Come ho notato:

Esistono approcci potenzialmente più brevi; mapply, per esempio:

y=mapply(grepl,c("[a-z]","[A-Z]","\\d","[^a-zA-Z0-9]"),list(S))

sfortunatamente, ciò non si semplifica come matrice nell'esempio a 1 carattere di "A".

Ho usato outerpiuttosto che mapply, che restituisce sempre un array (una matrice in questo caso), ed è stato costretto a farlo Vectorize grepl, che in realtà è solo un mapplyinvolucro attorno ad esso.

Ho anche scoperto il gruppo di caratteri predefinito [:punct:]che corrisponde ai caratteri di punteggiatura (non spaziale, non alfanumerico).


1
Se sostituisci la matrice con un cbind, puoi ridurre a 186 byte: y = cbind (g ("[az]", S), g ("[AZ]", S), g ("\\ d", S), g ("[^ a-zA-Z0-9]", S))
NofP

@NofP oh, molto bello. Inoltre, puoi circondare il codice con i backtick (`) per visualizzarlo like this. :)
Giuseppe,

Le regole stabiliscono che è possibile utilizzare una matrice di caratteri o un elenco come input, quindi è possibile rimuovere probabilmenteS=el(strsplit(G,""))
MickyT

@MickyT ah, l'ho trascurato, grazie.
Giuseppe,

4

Buccia , 31 29 28 byte

SMS?' `ṁ§foSM≠?▲▼⁰M#Kë½D±o¬□

Utilizza 0 per il numero minimo e 1 per il numero massimo di caratteri. Provalo online!

Spiegazione

Gli elenchi di funzioni sono interessanti.

SMS?' `ṁ§foSM≠?▲▼⁰M#Kë½D±o¬□  Inputs are bit B and string S.
                     ë        Make a list L of the four functions
                      ½       is-lowercase-letter,
                       D      is-uppercase-letter,
                        ±     is-digit, and
                         o¬□  not is-alphanumeric.
                  M#          For each of them, take number of matches in S,
              ?▲▼⁰            take maximum or minimum depending on B,
          oSM≠                and mark those entries that are not equal to it.
        §f          K         Remove from L the functions that correspond to marked entries, call the result L2.
                              These functions test whether a character should be replaced by a space.
SM                            Do this for each character C in S:
      `ṁ                      Apply each function in L2 to C and sum the results.
  S?'                         If the result is positive, return space, otherwise return C.

4

Python 2 , 140 byte

g=lambda x:x.isalnum()-~(x>'Z')*x.isalpha()
def f(s,m):k=map(g,s).count;print''.join([' ',c][k(g(c))==sorted(map(k,range(4)))[m]]for c in s)

Provalo online!

Jonathan Frech ha salvato un byte. Grazie!

Il più alto è m=-1, il più basso è m=0.


1
Penso che puoi salvare un byte sostituendolo +x.isalpha()*-~(x>'Z')con -~(x>'Z')*x.isalpha().
Jonathan Frech,


3

Java (OpenJDK 8) , 448 439 432 362 361 354 352 348 343 320 byte

s->b->{int w[]=new int[4],m=0,n=-1>>>1,l;s.chars().forEach(c->{if(c>96&c<123)w[0]++;else if(c>64&c<91)w[1]++;else if(c>47&c<58)w[2]++;else++w[3];});for(int W:w){m=W>m?W:m;n=W<n?W:n;}l=m-n;m=b?m:n;return l<1?s:s.replaceAll("["+(w[0]!=m?"a-z":"")+(w[1]!=m?"A-Z":"")+(w[2]!=m?"\\d]":"]")+(w[3]!=m?"|[^a-zA-Z0-9]":"")," ");}

Provalo online!



È possibile rimuovere +in \\|+$per un ulteriore -1 byte.
Kevin Cruijssen,

È possibile salvare altri tre byte modificando l'ultima parte in String r=(w[0]!=m?"[a-z]|":"")+(w[1]!=m?"[A-Z]|":"")+(w[2]!=m?"[0-9]|":"")+(w[3]!=m?"[^a-zA-Z0-9]|":"");return r.isEmpty()?s:s.replaceAll(r.replaceAll(".$","")," ");}.
Kevin Cruijssen,

Oh, e n=s.length()può essere n=-1>>>1per un ulteriore -4.
Kevin Cruijssen,

Oh, un'altra piccola cosa da giocare a golf: [0-9]->\\d
Kevin Cruijssen,

3

Rubino , 118 116 byte

Prende 0(il più basso) o -1(il più alto) per il suo secondo argomento.

-2 byte grazie a Lynn.

->s,t{s.gsub(/./){|c|[/\d/,/[a-z]/,/[A-Z]/,/[^\da-z]/i].group_by{|x|s.scan(x).size}.sort[t][1].any?{|x|x=~c}?c:" "}}

Provalo online!

Ungolfed

->s,t{
  s.gsub(/./) {|c|
    [ /\d/,
      /[a-z]/,
      /[A-Z]/,
      /[^\da-z]/i
    ]
    .group_by {|x| s.scan(x).size }
    .sort[t][1]
    .any? {|x| x =~ c } ? c : " "
  }
}

Risposta molto interessante! È possibile utilizzare -1come valore "più alto" e sostituirlo minmax[t]con sort[t].
Lynn,

3

Python 2, 190 183 174 173 byte

Grazie a Jonathan Frech per averlo abbreviato

from re import*
def f(i,c):
 q='[a-z]','[A-Z]','\d','[\W_]';l=[len(set(finditer(p,i)))for p in q]
 for j,k in enumerate(l):
	if k-eval(c):i=compile(q[j]).sub(' ',i)
 print i

Questo prende le stringhe 'max(l)'e 'min(l)'come vero e falso. (Non credo che questo infrange le regole ...?) Questo è più lungo delle altre due risposte di Python ma diverso, quindi ho pensato di pubblicarlo. Non sono un grande giocatore di golf, quindi immagino che questo potrebbe essere ulteriormente migliorato, ma tutte le cose che ho provato non hanno funzionato.

Provalo online!


Ciao e benvenuto nel sito! Quando provo a eseguirlo, ricevo errori e non sono del tutto sicuro del perché. Una ragione potrebbe essere quella che sum(1for m...dovrebbe essere sum(1 for m..., ma penso che ci siano anche altri problemi. Potresti fornire un link a un interprete online (come tio ) per dimostrare come lo chiami e per mostrare che non è un errore?
James,

@DJMcMayhem Ho appena aggiunto un link, grazie per aver fornito il link non ero sicuro di come farlo. Non ricevo un errore quando lo eseguo lì.
dylnan,

Ah, non sapevo dire che stavi inserendo max(l)e min(l)come stringhe, ecco perché stavo ricevendo errori. Grazie per averlo chiarito! Anche se ora, questo è al limite della violazione della regola # 3, `Nota che questi valori distinti dovrebbero essere usati (in qualche modo) come un valore booleano`, ma è sicuramente un po 'una zona grigia.
James,

A proposito, ecco un suggerimento TIO: se metti le tue chiamate di funzione nel campo piè di pagina , non verranno conteggiate nel conteggio dei byte, quindi puoi facilmente vedere quanto dura la tua risposta: provala online!
James,

@DJMcMayhem Ah grazie. Sono d'accordo che è una specie di area grigia. Potrei prendere 'max' e 'min' come vero falso quindi fare eval (c + '(l)') che aggiunge 6 byte e sembra più accettabile ma fino a quando OP non rifiuta la mia risposta, presumo che sia ok.
dylnan,

2

JavaScript (ES6), 151 149 byte

g=
(s,f,a=[/\d/,/[A-Z]/,/[a-z]/,/[_\W]/],b=a.map(r=>s.split(r).length))=>s.replace(/./g,c=>b[a.findIndex(r=>r.test(c))]-Math[f?"max":"min"](...b)?' ':c)
<input id=s oninput=o.textContent=g(s.value,f.checked)><input id=f type=checkbox onclick=o.textContent=g(s.value,f.checked)><pre id=o>

Purtroppo le regole probabilmente non mi permettono di passare Math.maxo Math.mincome bandiera. Modifica: salvato 2 byte grazie a @JustinMariner.



1

Java (OpenJDK 8) , 307 + 34306 + 27 295 byte

Il mio approccio "interessante" alla sfida.

Grazie a Kevin Cruijssen per aver ridotto i byte di importazione rimuovendo completamente l'importazione!

s->b->{String t=s.replaceAll("\\d","2").replaceAll("[a-z]","0").replaceAll("[A-Z]","1").replaceAll("\\D","3"),v="";int a[]={0,0,0,0},i=0,z=0,y=-1>>>1;t.chars().forEach(j->{a[j%4]++;});for(int x:a){z=x>z?x:z;y=x<y?x:y;}for(;i<s.length();i++)v+=a[t.charAt(i)%4]!=(b?z:y)?" ":s.charAt(i);return v;}

Provalo online!

Spiegazione:

String t=s.replaceAll("\\d","2")
          .replaceAll("[a-z]","0")
          .replaceAll("[A-Z]","1")
          .replaceAll("\\D","3")

In primo luogo sostituisce ogni gruppo con un numero intero compreso tra 0 e 3 usando un semplice regex e lo memorizza in una nuova stringa.

int a[]={0,0,0,0},m,i=0,z=0,y=-1>>>1;

Inizializza un array di numeri interi e un paio di altri numeri interi da utilizzare in seguito. Imposta ily variabile sulla dimensione massima int usando lo spostamento bit destro senza segno.

t.chars().forEach(j->{a[j%4]++;});

Per ogni carattere nella stringa modificata, utilizza il suo valore ASCII modulo 4 per calcolare l'indice dell'array di cui sopra per incrementare.

for(int x:a){
    z=x>z?x:z;
    y=x<y?x:y;
}

Questo quindi scorre attraverso i conteggi di ciascun gruppo archiviato nella matrice e calcola il minimo (y ) e il massimo ( z).

for(;i<s.length();i++)
    v+=a[t.charAt(i)%4]!=(b?z:y)?" ":s.charAt(i);

Ripete in sequenza tutti i caratteri della stringa, verificando se il gruppo di quel gruppo di caratteri è uguale al min / max (usando il trucco del modulo menzionato in precedenza). Se non è uguale, viene aggiunto uno spazio alla nuova stringa nella posizione dei caratteri, altrimenti viene aggiunto il carattere originale.

return v;

Finalmente restituisci la nuova stringa!


1
Bella risposta, +1 da parte mia! Due piccole cose da golf: import java.util.stream.IntStream;può essere import java.util.stream.*;e ,ipuò essere ,i=0dopo il quale è possibile rimuovere i=0dal for-loop. Oh, e (s,b)->può essere s->b->.
Kevin Cruijssen,

@KevinCruijssen Grazie! Non mi ero reso conto che potevi incatenare lambda
Luke Stevens,

Si chiama curry. :) Puoi farlo in questo modo java.util.function.Function<String, java.util.function.Function<Boolean, String>> g = s->b->{...};.
Kevin Cruijssen,

1
Oh, e un'altra cosa da golf: IntStream z=IntStream.of(a);m=(b?z.max():z.min()).getAsInt();può essere l=s.length(),x=0,y=le for(int x:a){z=x>z?x:z;y=x<y?x:y;}e (b?z:y), quindi non è più necessario l'importazione. Mettendo tutto insieme diventa: s->b->{String t=s.replaceAll("\\d","2").replaceAll("[a-z]","0").replaceAll("[A-Z]","1").replaceAll("\\D","3"),v="";int a[]={0,0,0,0},i=0,l=s.length(),z=0,y=l;t.chars().forEach(j->{a[j%4]++;});for(int x:a){z=x>z?x:z;y=x<y?x:y;}for(;i<l;i++)v+=a[t.charAt(i)%4]!=(b?z:y)?" ":s.charAt(i);return v;}( 294 byte ) .
Kevin Cruijssen,

1
@KevinCruijssen Oooooh bel pensiero! Potrei aspettare un po 'prima di cambiarlo nel caso in cui ti venisse in mente qualcos'altro;)
Luke Stevens,

1

Bash, 229 227 212 byte

LANG=C;g="A-Z a-z 0-9 !A-Za-z0-9";declare -A H
f()(((i=$2?99:-1));r=$1;for h in $g;{ t=${r//[$h]};t=${#t};(($2?t<i:t>i))&&i=$t&&H=([$h]=1);((t-i))||H[$h]=1;};for h in $g;{((${H[$h]}))||r=${r//[$h]/ };};echo "$r")

Provalo online


Non sono sicuro di come funzionano gli spazi attorno alle parentesi quadre e ai blocchi quadrati in Bash, ma sembra che funzioni ancora senza lo spazio inf(){(( .
Kevin Cruijssen,

1
sì, lo spazio è obbligatorio in generale tranne (, ma anche 2 byte potrebbero essere salvati usando (invece di {, peggiorando le prestazioni perché creando una subshell
Nahuel Fouilleul

1

PHP, 161 158 byte

for([,$s,$z]=$argv;~$c=$s[$i++];)foreach([punct,upper,lower,digit]as$f)(ctype_.$f)($c)?$$i=$f:$g[$f]++;while(~$c=$s[$k++])echo$g[$$k]-($z?min:max)($g)?" ":$c;

Corri con -nro provalo online .

  • primo ciclo: per ogni posizione, ricorda il gruppo del personaggio
    e conta le occorrenze di gruppi che il personaggio attuale non è .
    (quella negazione ha salvato 3 byte)
  • a seconda del secondo parametro, selezionare min non-count per verità, max non-contare per falsy.
  • secondo ciclo: se il (conteggio del carattere corrente) diverso
    dal conteggio differisce dal conteggio minimo / massimo allora stampa lo spazio, altrimenti stampa il carattere.

1
@KevinCruijssen Assicurati di aver selezionato l'ultima versione di PHP (7.1.0).
Tito

1

JavaScript (ES6), 139 byte

s=>b=>s.map(c=>++a[g(c)]&&c,a=[0,0,0,0],g=c=>c>-1?0:/[a-z]/i.test(c)?c<"a"?2:1:3).map(c=>a.map(v=>v-Math[b?"max":"min"](...a))[g(c)]?" ":c)

L'input e l'output sono una matrice di caratteri. Accetta i valori booleani effettivi per l'input.

Un approccio diverso dalla risposta di @ Neil ; quasi evitando le espressioni regolari. Invece, ho usato una serie di controlli per determinare la categoria di ciascun personaggio:

  • Le cifre ritornano trueperc>-1 perché non riescono cifre confronti matematici
  • Le lettere maiuscole corrispondono al regex /[a-z]/ie hanno punti di codice inferiori a"a"
  • Le lettere minuscole corrispondono a quel regex ma hanno punti di codice non inferiori a "a"
  • I simboli non superano nessuno di questi test

Casi test

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.