Die ursprüngliche Nummer (II)


18

Diese Herausforderung ist im Wesentlichen identisch mit dieser, mit nur einem Unterschied: Es ist jetzt zulässig, Buchstaben an einer beliebigen Stelle in der Zeichenfolge zu mischen.

Szenario

John hat eine wichtige Nummer und er möchte nicht, dass andere sie sehen.

Er beschloss, die Nummer mit den folgenden Schritten zu verschlüsseln:

Seine Nummer ist immer eine nicht abnehmende Folge (d. H. "1123")

Er wandelte jede Ziffer in englische Wörter um. (dh. "123" -> "ONETWOTHREE")

Ordnen Sie die Buchstaben dann nach dem Zufallsprinzip neu an. (dh. "ONETWOTHREE" -> "EEWOOHRNTET")

John hatte das Gefühl, dass seine Nummer in Sicherheit war. In der Tat kann eine solche Verschlüsselung leicht entschlüsselt werden :(


Aufgabe

Angesichts der verschlüsselten Zeichenfolge s besteht Ihre Aufgabe darin, sie zu entschlüsseln und die ursprüngliche Nummer zurückzugeben.


Regeln

  • Dies ist Codegolf, daher gewinnt die kürzeste Antwort in Bytes
  • Sie können davon ausgehen, dass die Eingabezeichenfolge immer gültig ist
  • Die Eingabezeichenfolge enthält nur Großbuchstaben
  • Die ursprünglichen Nummern sind immer in aufsteigender Reihenfolge angeordnet
  • Sie können die Zahl im String- oder Integer-Format zurückgeben
  • Die Buchstaben werden nur zwischen einem Wort und nicht zwischen der gesamten Zeichenfolge gemischt. Die Buchstaben können an einer beliebigen Stelle in der Zeichenfolge gemischt werden.
  • Die Zahlen sind nur von 1 bis einschließlich 9 ( ONEbis NINE)

Mögliche nicht verschlüsselte Zeichenfolge

Hier ist eine Liste der Zeichenfolgen, nachdem sie aus den Zahlen in Zeichenfolgen konvertiert wurden:

 1 -> ONE 
 2 -> TWO
 3 -> THREE
 4 -> FOUR
 5 -> FIVE
 6 -> SIX
 7 -> SEVEN
 8 -> EIGHT
 9 -> NINE

Beispiele

"NEO" -> 1

"NWEOOT" -> 12

"TOEERWNEHOT" -> 123

"IHNEVGENNEISTE" -> 789

"WEETVTRFSVUHNEEFRHIXEOINSNIEGTOONIEE" -> 123456789

"EWHEWROETOTTON" -> 1223

"ONEWESTV" -> 27 (Danke, ETHproductions!)


7
Vorgeschlagener Testfall: so etwas wie "ONEWESTV" -> 27(enthält eine Nummer, die nicht wirklich erscheint)
ETHproductions

@ETHproductions Großartige Idee! Hinzugefügt.
Cristian Lupascu

Warum gibt es nicht die "NULL"?
RosLuP

@ RosLuP John hasst führende Nullen ...
Cristian Lupascu

Antworten:


9

Python 2 , 123 Bytes

c=map(input().count,"OWHUFXSGIQ")
i=4
for j in"71735539994":c[i*2]-=c[int(j)];i=-~i%5
s=""
for n in c:i+=1;s+=`i`*n
print s

Ein vollständiges Programm, bei dem die angegebene Nummer eingegeben und ausgedruckt wird.

Probieren Sie es online! oder sehen Sie sich eine Testsuite an

Wie?

Lassen Sie uns mit dem Beispiel "NEONSEXTOWNII" arbeiten (um 1269 zu erhalten und etwas Leisure Suite Larry -esque zu sein!)

Nimmt zuerst c=map(input().count,"OWHUFXSGIQ")die Eingabe und zählt die Anzahl von jedem von OWHUFXSGIQ- dies sind Buchstaben, die in jeder Zahl in aufsteigender Reihenfolge erscheinen, wobei 2,4,6 und 8 ihre "eigenen" Buchstaben ( WUXG) plus einen zusätzlichen Buchstaben haben, Qum eine Null anzufügen bis zum Ende und machen Sie die Länge der resultierenden Liste gerade. Zum Beispiel:

[2,1,0,0,0,1,1,0,2,0] <- c
 O W H U F X S G I Q  <- is the counts of these letters
 1 2 3 4 5 6 7 8 9 0  <- which "relate to" these digits in John's number
   2   4   6   8   0  <- these will be correct as the letters are unique to their words

Die Einträge für 1, 3, 5, 7 und 9 müssen angepasst werden, um die Häufigkeit der anderen Buchstaben zu korrigieren. Dies wird von der nächsten Schleife ausgeführt:

i=4
for j in"71735539994":c[i*2]-=c[int(j)];i=-~i%5

Beachten Sie, dass es sich bei den anzupassenden Einträgen um alternative Einträge handelt (1,3,5,7,9,1,3,5, ...). Daher können wir bei jedem Schritt zwei zu einer Indexvariablen hinzufügen und modulo um 10 addieren, um darin zu bleiben Bereich, wenn wir mehr als einmal überqueren müssen (was wir tun). Um einige Bytes zu sparen, können wir um eins und modulo um 5 erhöhen und den doppelten Index verwenden.
Da die Anpassungen für 9 die meiste Arbeit erfordern, die wir dort beginnen, befindet sie sich bei Index 8, also beginnen wir bei i=4. Die Zeichenfolge "71735539994"gibt dann die Indizes jder Werte an, die in jeder Phase entfernt werden sollen (wobei wir sichergestellt haben, dass der neunte Index "Q"bei der Erstellung Null enthält c). c[i*2]-=c[int(j)]führt jede einzelne Einstellung und i=-~i%5bewegt sich izu dem nächsten Index (wobei -~iist -(-1-i)oder i+1Spar Klammern (i+1)%5) keepingi*2 innerhalb der Grenzen c.
Also subtrahieren wir zuerst die Zahl am Index j=7von der Zahl am Index i*2=8, subtrahieren die Anzahl der gezählten "G" von der Anzahl der "I" und passen den "NINE" Countdown um die (richtige) Anzahl der "EIGHT" an ( das hat auch ein "ich"). Wir bewegen uns dann auf i=0( -~4%5 = (4+1)%5 = 0), referenzieren den Index, i*2 = 0der für "EINS" steht, und subtrahieren den am Index gefundenen Wert von j=1dem Eintrag, der "W" und somit "ZWEI" zählt, und passen die Anzahl der "O" abwärts an. Am Ende der Schleife haben wir die korrigierten Zählungen:

[1,1,0,0,0,1,0,0,1,0] <- c   (for 1223333448 it would be: [1,2,4,2,0,0,0,1,0,0])
 1 2 3 4 5 6 7 8 9 0

Sie müssen also nur noch ausdrucken, was cjetzt für ( 1269) steht. iist jetzt wieder bei 0, also erhöhen wir es am Anfang der Schleife und verwenden es als unsere Ziffer:

s=""
for n in c:i+=1;s+=`i`*n
print s

Die hinteren `i`Häkchen sind Python2-Abkürzungen, für repr(i)die eine Zeichenfolgendarstellung eines Objekts (das betreffende Ziffernzeichen als Zeichenfolge) abgerufen und durch Multiplizieren einer Zeichenfolge mit einer Zahl eine neue Zeichenfolge mit so vielen Wiederholungen erstellt wird (hier zeigen wir nur das n=0Drehen `i`von etwa) "5"zu ""und n=1drehen sagen "6"wie "6", aber es funktioniert auch für größere positive ganze Zahlen, so "3"*4wird "3333"zum Beispiel.)


8

05AB1E , 31 Bytes

[{‘Z€µ‚•„í†ìˆÈŒšï¿Ÿ¯¥Š‘#NSèJ{Q#

Probieren Sie es online!

Erläuterung

[                                   # start loop
 {                                  # sort input
  ‘Z€µ‚•„í†ìˆÈŒšï¿Ÿ¯¥Š‘#            # push the list ['Z','ONE','TWO','THREE','FOUR','FIVE','SIX','SEVEN','EIGHT','NINE']
                        N           # push the current iteration counter
                         S          # split to list of digits
                          è         # index into the list with each
                           J{       # join to string and sort
                             Q#     # if the strings are equal, exit loop
                                    # implicitly print iteration counter

Sehr ineffizient für große Eingaben.


‘Z€µ‚•„í†ìˆÈŒšï¿Ÿ¯¥Š‘# # push the list ['Z','ONE','TWO','THREE','FOUR','FIVE','SIX','SEVEN','EIGHT','NINE']: Kannst du etwas erklären, ich habe Mühe zu verstehen, wie ein String erzeugt werden kann.
Cyril Gandon

1
@CyrilGandon: Begrenzt eine komprimierte Zeichenfolge mit durch Leerzeichen getrennten Wörtern in Großbuchstaben. Zbedeutet Z. Alle anderen 2-Symbol-Paare bezeichnen ein komprimiertes Wort aus dem 05AB1E- Wörterbuch . Also zum Beispiel €µübersetzt als ONE.
Emigna

Schön, wie komprimiert man einen String, der im Wörterbuch enthalten ist? Etwas mit dem Unicode-Wert des Paares?
Cyril Gandon

1
@CyrilGandon: Sie nehmen die Zeilennummer des Wortes im Diktat (2420 für hallo ) und subtrahieren 1. Dies gibt uns 2419. Die Symbole, die wir benötigen, sind die Symbole, die von 24und 19in den Dokumenten gefolgt werden . In unserem Fall ist dies 24=Ÿund 19=™so HELLOwäre es‘Ÿ™‘
Emigna

1
Es gibt auch einen von Adnan geschriebenen Kompressor, den Sie in den meisten Fällen verwenden können. Der Link ist etwas lang, aber Sie finden ihn im Chatraum von 05AB1E .
Dies

8

Retina , 112 97 Bytes

O`.
}`GH
8
X
6
H
3
+`F(.*)O(.*)U
4$1$2
+`O(.*)W
2$1
+`F(.*)V
5$1
+`N(.*)V
7$1
}`NO
1
NN
9
T`L
O`.

Probieren Sie es online!

-12 Bytes dank @Neil

-3 Bytes durch Verwendung von L Zeichenklassen bei der Transposition

Wie es funktioniert

Grundsätzlich beruht dies auf der Tatsache, dass Buchstaben nur in bestimmten Nummernnamen verwendet werden. Zum Beispiel SIXist der einzige Name, der ein enthält X. Dies wird schwieriger, wenn einige Wörter in Buchstaben überlappen, z. B. beide FIVEund SEVENusing V. Dies könnte korrigiert werden, indem man sich FIVEmit identifiziert F(.*)V.


1
@ RickHitchcock Behoben. Die Rekursion bei der Konvertierung nach 8 funktionierte nicht richtig
fireflame241

1
@ RickHitchcock. Die Rekursion wurde für alle behoben.
fireflame241

Ärgerlich GHund NOwäre nebeneinander, außer für alle vorherigen 8oder 1von einem vorherigen Ersatz ...
Neil

Vielleicht }`GH 8würde das funktionieren 8- das }würde dazu führen, dass die Zeichen erneut sortiert werden, wodurch alle verbleibenden Gund Hzusammengefügt werden.
Neil

@Neil Schöne Idee. Ich war auch in der Lage, das zu tun NO -> 1, was praktisch war.
Fireflame241

5

Kotlin 1.1 , 359 352 331 327 325 Bytes

Einreichung

fun r(r:String):String{var s=""
val f=r.split(s).groupingBy{it}.eachCount()
val c=Array(10,{0})
c[8]=f["G"]?:0
c[6]=f["X"]?:0
c[4]=f["U"]?:0
c[2]=f["W"]?:0
c[1]=(f["O"]?:0)-c[2]-c[4]
c[3]=(f["R"]?:0)-c[4]
c[7]=(f["S"]?:0)-c[6]
c[5]=(f["V"]?:0)-c[7]
c[9]=((f["N"]?:0)-c[1]-c[7])/2
for(i in 1..9)for(x in 1..c[i])s+=i
return s}

Funktioniert nicht mit TryItOnline, da Kotlin 1.1 nicht unterstützt wird

Prüfung

fun r(r:String):String{
val f=r.split("").groupingBy{it}.eachCount()
val c=Array(10,{0})
c[8]=f["G"]?:0
c[6]=f["X"]?:0
c[4]=f["U"]?:0
c[2]=f["W"]?:0
c[1]=(f["O"]?:0)-c[2]-c[4]
c[3]=(f["R"]?:0)-c[4]
c[7]=(f["S"]?:0)-c[6]
c[5]=(f["V"]?:0)-c[7]
c[9]=((f["N"]?:0)-c[1]-c[7])/2
var s=""
for(i in 1..9)for(x in 1..c[i])s+=i
return s}

data class TestData(val input: String, val output: String)

fun main(vararg args:String) {
    val items = listOf(
    TestData("NEO" , "1"),
    TestData("NWEOOT" , "12"),
    TestData("TOEERWNEHOT" , "123"),
    TestData("IHNEVGENNEISTE" , "789"),
    TestData("WEETVTRFSVUHNEEFRHIXEOINSNIEGTOONIEE" , "123456789"),
    TestData("EWHEWROETOTTON" , "1223")
    )
    for (item in items) {
        val out = r(item.input)
        if (out != item.output) {
            throw AssertionError("Bad result: $item : $out")
        }
    }
}

Logik

Spickzettel

Ich habe das Blatt oben verwendet, um die einfachste Lösung für jeden Buchstaben zu finden

  • Grün = Selbst lösen
  • Blau = Benötigt Grüns zum Lösen
  • Orange = Benötigt Blues zum Lösen
  • Rot = Benötigt Orangen zum Lösen

Bearbeitungen

  • -7 - Whitespace wird von w0lf geändert
  • -21 - Liste auf Array verkleinern
  • -4 - Nicht benötigte Klammern entfernt
  • 0 - Logik hinzugefügt
  • -2 - Wiederverwendung der leeren Zeichenfolge dank Kevin-Cruijssen

1
Ich habe gerade bemerkt, dass ich mit meiner Java 8-Antwort (127 Bytes) genau mit Ihnen verbunden bin, wobei ich einen ähnlichen Ansatz verwende. ;) Aber eine Frage: kann man nicht ändern var s=""und return szu r=""und return r, die Sie nicht mehr benötigen an dieser Stelle durch die Eingabe-String Wiederverwendung? Ich habe noch nie in Kotlin programmiert, also könnte es sein, dass ich hier Unsinn rede. ; p
Kevin Cruijssen


1
Ah ja, das war natürlich eine Möglichkeit; Die Parameter sind finalstandardmäßig eingestellt. Hmm, eine andere Sache, die Sie vielleicht golfen können: Platzieren Sie die var s=""als erstes in der Methode und ersetzen Sie sie val f=r.split("").durch val f=r.split(s).. Auch hier keine Ahnung, ob es funktioniert. Schade, dass TIO v1.1 noch nicht unterstützt, ansonsten würde ich diese Vorschläge selbst ausprobieren, bevor ich mich blöd
anhöre

4

Jelly , 37 Bytes

1ðDị“©ȯ¿w¶&ÇhṆỌƘ#Ȯʋ~¢CNẓ_»ŒuḲ¤ẎŒ!ċð1#

Probieren Sie es online!

-1 Danke an Jonathan Allan .


Bei einigen Eingaben mit mehr als 7 Zeichen (z. B. NINEFIVE, THREEFIVE) tritt eine Zeitüberschreitung auf . Ist es ein Fehler oder ist der Code nur ineffizient?
Cristian Lupascu

@ w0lf letzteres ( Œ!bedeutet "Permutationen")
Erik der Outgolfer

Speichern Sie ein Byte mit "AA" anstelle von "!":...“©ȯ¿w¶&ÇhṆỌƘ#Ȯʋ~¢CNẓ_»...
Jonathan Allan

@ JonathanAllan Oh, ist AA ein Wort?
Erik der Outgolfer

Es ist das erste Wort im kurzen Wörterbuch, ja.
Jonathan Allan

3

Java 8, 248 234 Bytes

s->{int x=0,a[]=new int[10];for(String t:"2WO;4UORF;6XSI;8GI;5FI;7S;3R;1O;9I".split(";"))for(;s.indexOf(t.charAt(1))>=0;a[t.charAt(0)-48]++)for(String z:t.split(""))s=s.replaceFirst(z,"");for(s="";x++<9;)for(;a[x]-->0;)s+=x;return s;}

Code Erklärung:

s->{
    // Array to count how often which number appears
    int a[]=new int[10];
    // The first character behind the number serves the identification
    // the other characters get removed to identify the other numbers later
    for(String t:"2WO;4UORF;6XSI;8GI;5FI;7S;3R;1O;9I".split(";"))
        // Check if the string contains the id 
        for(;s.indexOf(t.charAt(1))>=0;a[t.charAt(0)-48]++)
            // Remove the relevant charcters
            for(String z:t.split(""))
                s=s.replaceFirst(z,"");
    // Clear the string to write the output
    s="";
    // write the numbers sequential into the output 
    for(int x=0;x++<9;)
        for(;a[x]-->0;)
            s+=x;
    return s;
}

-14 Dank an Olivier Grégoire



2

Java 8, 346 345 344 336 327 Bytes

s->{int g=c(s+=" ","G"),u=c(s,"U"),w=c(s,"W"),x=c(s,"X"),f=c(s,"F")-u,h=c(s,"H")-g,v=c(s,"V")-f,o=c(s,"O")-u-w,i=c(s,"I")-f-x-g;return d(s=d(s=d(s=d(s=d(s=d(s=d(s=d(s=d("",o,1),w,2),h,3),u,4),f,5),x,6),v,7),g,8),n,9);}int c(String...s){return~-s[0].split(s[1]).length;}String d(String s,int i,int n){for(;i-->0;s+=n);return s;}

Probieren Sie es hier aus.

Allgemeine Erklärung:

Ich habe mir die Vorkommen der einzelnen Zeichen im Alphabet angesehen:

E 13357789
F 45
G 8
H 38
I 5689
N 1799
O 124
R 34
S 67
T 238
U 4
V 57
W 2
X 6
  • Ich habe zuerst alle Vorkommen der einzelnen Übereinstimmungen gezählt: G=8; U=4; W=2; X=6 .
  • Dann alle Vorkommen von zwei übereinstimmenden Zeichen, die auch mit einem der vier oben genannten übereinstimmen, die ich von ihrer Anzahl abziehen kann: F=5; H=3 .
  • Dann habe ich dasselbe noch einmal für V=7(durch Subtrahieren vonF=5 ).
  • Dann dasselbe für alle verbleibenden Drei-Übereinstimmungen-Charaktere: O=1; N=9 .
    • Aber weil Nes zwei Vorkommen in gibt NINE, musste ich -1für jedes Vorkommen von ein zusätzliches machen N, also habe ich I=9stattdessen verwendet (indem ich drei vorherige Übereinstimmungen anstelle von zwei subtrahiere).

Code Erklärung:

s->{                    // Method with String as parameter and return-type
  int g=c(s+=" ","G"),  //  Amount of 8s (and append a space to `s` first, for the .split)
      u=c(s,"U"),       //  Amount of 4s
      w=c(s,"W"),       //  Amount of 2s
      x=c(s,"X"),       //  Amount of 6s
      f=c(s,"F")-u,     //  Amount of 5s
      h=c(s,"H")-g,     //  Amount of 3s
      v=c(s,"V")-f,     //  Amount of 7s
      o=c(s,"O")-u-w,   //  Amount of 1s
      i=c(s,"I")-f-x-g; //  Amount of 9s
  return d(             //  Return the result by:
   s=d(
    s=d(
     s=d(
      s=d(
       s=d(
        s=d(
         s=d(
          s=d("",       //   Making the input String `s` empty, since we no longer need it
                 o,1),  //   Append all 1s to `s`
         w,2),          //   Append all 2s to `s`
        h,3),           //   Append all 3s to `s`
       u,4),            //   Append all 4s to `s`
      f,5),             //   Append all 5s to `s`
     x,6),              //   Append all 6s to `s`
    v,7),               //   Append all 7s to `s`
   g,8),                //   Append all 8s to `s`
  i,9);                 //   And then returning `s` + all 9s
}                       // End of method

int c(String...s){  // Separate method with String-varargs parameter and int return-type
                    //  `s[0]` is the input-String
                    //  `s[1]` is the character to check
  return~-s[0].split(s[1]).length;
                    //  Return the amount of times the character occurs in the String
}                   // End of separated method (1)

String d(String s,int i,int n){
               // Separate method with String and two int parameters and String return-type
  for(;i-->0;  //  Loop from the first integer-input down to 0
      s+=n     //   And append the input-String with the second input-integer
  );           //  End of loop
  return s;    //  Return the resulting String
}              // End of separated method (2)

1
Verdammt, ich hätte gedacht, dass es kürzer wäre, einer Liste etwas hinzuzufügen und sie dann zu sortieren (ist es nicht). Gut gemacht!
Olivier Grégoire

1
Nun, am Ende habe ich dich übertroffen , aber nicht viel;)
Olivier Grégoire


1

Python 3 , 225 Bytes

def f(s):
	r=[]
	for i,w in zip([2,4,6,8,3,5,7,1,9],["WTO","UFOR","XSI","GEIHT","HTREE","FIVE","VSEEN","ONE","NINE"]):
		while s.count(w[0]):
			r+=[i]
			for l in w:s="".join(s.split(l,1))
	return "".join(sorted(map(str,r)))

Probieren Sie es online!

Ganz einfach: Entfernen Sie zuerst die Ziffern, die mit einem bestimmten Buchstaben gekennzeichnet sind.


1

Python 3 , 125 Bytes

lambda s:''.join(min(w)*(2*sum(map(s.count,w[:2]))-sum(map(s.count,w)))for w in"O1WU W2 H3G U4 F5U X6 S7X G8 IUFXG9".split())

Probieren Sie es online!

Nachdem ich die verknüpfte Herausforderung gelesen hatte, stellte ich fest, dass es sich um eine Variation der Python-Lösung von mdahmoune handelt , auf der sie selbst basiert der ES6-Lösung Draco18s basiert .

Wie diese Lösung berechnen wir die Antwort durch eine lineare Kombination der Anzahl der Vorkommen bestimmter Buchstaben. Wir kodieren die linearen Kombinationen kurz, indem wir sie als Wörter schreiben, bei denen die ersten beiden Buchstaben hinzugefügt und anschließend alles subtrahiert werden soll. Manchmal wird ein Zeichen benötigt, um die ersten beiden Zeichen aufzufüllen. Wir verwenden dies, um die auszugebende Ziffer auszublenden (die in der Eingabe niemals vorkommt und daher unseren Algorithmus nicht beeinflusst), mit der wir extrahieren min.



1

Axiom, 351 Bytes

s:="GXUWRFVIONETHS";e:EqTable(CHAR,INT):=table();v:=[8,6,4,2,3,5,7,9,1];z:=[k for k in 1..46|prime?(k)];F(x,y)==>for i in 1..#x repeat y;F(z,e.(s.i):=z.i);t:=[1787026,2451,16445,5957,16036207,130169,20372239,495349,20677];h(a)==(r:=1;F(a,r:=r*e.(a.i));j:=[];F(v,while r rem z.i=0 repeat(r:=r quo t.i;j:=cons(v.i,j)));j:=sort j;k:=0;F(j,k:=k*10+j.i);k)

ungolfed kommentierte Ergebnisse

s:="GXUWRFVIONETHS" -- tutte le lettere di ONE..NINE in ordine di importanza 
e:EqTable(Character,Integer):=table()
v:=[8,6,4,2,3,5,7,9,1]              -- numeri da controllare in quell'ordine di apparizione di v
z:=[k for k in 1..46|prime?(k)]     -- 14 numeri primi da associare a s
F(x,y)==>for i in 1..#x repeat y 
F(z,e.(s.i):=z.i)                   -- riempie la tavola associando numeri primi alle lettere "GXUW..."
t:=[1787026,2451,16445,5957,16036207,130169,20372239,495349,20677]  -- prodotto di numeri primi 1787026 dovrebbe essere HEIGHT
h(a)==
     r:=1 ;F(a,r:=r*e.(a.i))        -- calcola il numero associato alla stringa a
     j:=[];F(v,while r rem z.i=0 repeat(r:=r quo t.i;j:=cons(v.i,j)));j:=sort j  -- leva il nome dei numeri che man mano trova, aggiunge a j
     k:=0 ;F(j,k:=k*10+j.i)         -- costruisce il numero decimale k, da j vettore ordinato
     k                              -- ritorna tale numero k
------------------------------------------------------
(8) -> h("IHNEVGENNEISTE")
   (8)  789
                                                    Type: PositiveInteger
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.