Gitarren-Tabs generieren?


24

Schreiben Sie das kürzeste Programm, das Gitarren-Tabs für die als Eingabe angegebenen Akkorde generiert.

Damit die Gitarristen unter Ihnen keinen Vorteil haben und es deterministisch (und wahrscheinlich einfacher zu codieren) machen, sind hier die einzigen Formen der zugelassenen Akkorde:

Major chords:

  E   F   F#  G   G#  A   A#  B   C   C#  D   D#
e 0---1---2---3---4---0---1---2---3---4---5---6---
B 0---1---2---3---4---2---3---4---5---6---7---8---
G 1---2---3---4---5---2---3---4---5---6---7---8---
D 2---3---4---5---6---2---3---4---5---6---7---8---
A 2---3---4---5---6---0---1---2---3---4---5---6---
E 0---1---2---3---4---0---1---2---3---4---5---6---

Minor chords:

  Em  Fm  F#m Gm  G#m Am  A#m Bm  Cm  C#m Dm  D#m
e 0---1---2---3---4---0---1---2---3---4---5---6---
B 0---1---2---3---4---1---2---3---4---5---6---7---
G 0---1---2---3---4---2---3---4---5---6---7---8---
D 2---3---4---5---6---2---3---4---5---6---7---8---
A 2---3---4---5---6---0---1---2---3---4---5---6---
E 0---1---2---3---4---0---1---2---3---4---5---6---

Beachten Sie, dass die 5 ersten Akkorde und die 7 letzten Akkorde jeder Serie unterschiedliche Formen haben.

Alle Akkorde sind einfache Dur- oder Moll-Akkorde (Nr. 7 oder andere Variationen).

Sie sollten sich auch um Wohnungen kümmern. Erinnerung:

A# = Bb
C# = Db
D# = Eb
F# = Gb
G# = Ab

B#, Cb, E# and Fb are not used

Die Ausgabe muss die erste Spalte mit den Kabelnamen enthalten, wie oben gezeigt. Der Akkordname muss nicht oben stehen. Akkorde müssen -wie oben gezeigt durch 3 getrennt werden . Die letzten 3 -sind optional.

Die Eingabe ist eine Zeichenfolge, die aus durch Leerzeichen getrennten Akkordnamen besteht.

Ein Beispiel für eine Eingabe ist:

Bm Gb A E G D Em F#

und die entsprechende Ausgabe ist:

e 2---2---0---0---3---5---0---2---
B 3---2---2---0---3---7---0---2---
G 4---3---2---1---4---7---0---3---
D 4---4---2---2---5---7---2---4---
A 2---4---0---2---5---5---2---4---
E 2---2---0---0---3---5---0---2---

... und Nebenfrage: Wie heißt der Beispielsong? :)
Jules Olléon

5
Hotel California: P
Matthew Gelesen am

Ja, du gewinnst! :)
Jules Olléon

Tolle Idee. Ich wünschte, ich hätte Zeit zum Spielen!
Igby Largeman

Antworten:


9

JavaScript, 297 277 262 235 223 Zeichen

In der Golfversion sind keine Zeilenumbrüche von Bedeutung. Sie dienen nur dazu, die Antwort lesbar zu machen. Semikolons sind wichtig.

Bearbeiten: Ersetzt das äußere mapdurch eine while-Schleife und andere Bearbeitungen. Endlich innerhalb von 2 × der Größe der Golfscript-Version (vorerst)!

Bearbeiten: Ersetzt das indexOfdurch Mathe, zerlegt die Nachschlagetabelle, weitere kleine Verbesserungen.

Edit: Ein weiterer mapzu forund in einem Finale hatte \nich unnötig gegessen. Endlich in Jules 'Python-Version.

i=prompt(o='').split(' ');for(r=6;o+=' EADGBe'[r]+' ',r--;o+='\n')
for(j=0;n=i[j++];o+=(([84,13,52,5][2*/m/.test(n)+x]*8>>2*r&3)+y-7*x)+'---')
y=n.charCodeAt(0),y=(2*y-(y>66)-(y>69)+(n[1]<'$')-(n[1]=='b')+2)%12,x=y>6;alert(o)

Die Ausgabe nutzt das ---optionale Nachziehen nicht mehr aus , da:

e 2---2---0---0---3---5---0---2---
B 3---2---2---0---3---7---0---2---
G 4---3---2---1---4---7---0---3---
D 4---4---2---2---5---7---2---4---
A 2---4---0---2---5---5---2---4---
E 2---2---0---0---3---5---0---2---

Verdammt, ich habe Javascript Peen-Neid, denke ich. Schön gemacht.
Kekekela

7

Golfscript, 136 Zeichen

[["eBGDAE"{[]+" "+}/]]\" "/{.-2\{"bm#A B C D E F G"?.)!!*(+}/14%.3>-.8>-.7/@109?0>2*+[963 780 882 753]{3base(;}%=\7%{+'---'+}+%}%+zip n*

Volle 23 Zeichen (17,5%) behandeln diese beiden Zeichen am Anfang jeder Ausgabezeile.

Beispielausgabe, Testen der Edge Cases:

$ golfscript.rb tabs.gs <<<"E G# Ab A Db D# Em G#m Abm Am D#m"
e 0---4---4---0---4---6---0---4---4---0---5---
B 0---4---4---2---6---8---0---4---4---1---6---
G 1---5---5---2---6---8---0---4---4---2---7---
D 2---6---6---2---6---8---2---6---6---2---7---
A 2---6---6---0---4---6---2---6---6---0---5---
E 0---4---4---0---4---6---0---4---4---0---5---

Ich habe nur ungefähr eine Stunde damit verbracht, daher kann es wahrscheinlich um mindestens 5 bis 10 Zeichen reduziert werden. Konzeptionell stellt sich heraus, dass es der DocMax-Lösung ziemlich ähnlich ist: Nachschlagetabelle für vier Fälle, dann inkrementieren um einen Versatz und die Zeichenfolgen in der richtigen Reihenfolge verbinden.


+1: Mann, ich liebe Golfscript! Heute habe ich mehrmals Stellen gefunden, an denen ich meinen Code kürzen kann, aber nicht um 50%! Ich habe keinen Dolmetscher zur Hand: Gibt es D # für Eb zurück?
DocMax

Übrigens stimmt die letzte Note in Ihrem Beispiel mit C # m überein, obwohl in der Befehlszeile D # m angezeigt wird. Tippfehler oder Fehler?
DocMax

@ DocMax, Fehler. Ich verstehe nicht, warum es nur D # m und nicht D # betrifft - das ist interessant zu debuggen. Ich ordne Dinge neu, weil es praktisch ist, zuerst den 7er-Block zu haben, also ist Eb nicht wirklich ein Randfall.
Peter Taylor

Es stellte sich heraus, dass der letzte ein \ n enthielt, was nicht in der Nachschlagetabelle stand und den Wert um das Äquivalent eines Buchstabens senkte.
Peter Taylor

4

Nachdem ich das geschrieben hatte, wurde mir klar, dass ich das viel schlauer hätte machen können ... vielleicht mache ich einen weiteren Eintrag. Hoffentlich bekomme ich nur Punkte dafür, dass ich der Schnellste bin!

Auf jeden Fall gibt es 962 Zeichen von Perl.

%c =(B=>{E=>0,F=>1,Gb=>2,G=>3,Ab=>4,A=>2,Bb=>3,B=>4,C=>5,Db=>6,D=>7,Eb=>8,Em=>0,Fm=>1,Gbm=>2,Gm=>3,Abm=>0,Am=>1,Bbm=>2,Bm=>3,Cm=>4,Dbm=>5,Dm=>6,Ebm=>7},G=>{E=>1,F=>2,Gb=>3,G=>4,Ab=>5,A=>2,Bb=>3,B=>4,C=>5,Db=>6,D=>7,Eb=>8,Em=>0,Fm=>1,Gbm=>2,Gm=>3,Abm=>4,Am=>2,Bbm=>3,Bm=>4,Cm=>5,Dbm=>6,Dm=>7,Ebm=>8},D=>{E=>2,F=>3,Gb=>4,G=>5,Ab=>6,A=>2,Bb=>3,B=>4,C=>5,Db=>6,D=>7,Eb=>8,Em=>2,Fm=>3,Gbm=>4,Gm=>5,Abm=>6,Am=>2,Bbm=>3,Bm=>4,Cm=>5,Dbm=>6,Dm=>7,Ebm=>8},A=>{E=>2,F=>3,Gb=>4,G=>5,Ab=>6,A=>0,Bb=>1,B=>2,C=>3,Db=>4,D=>5,Eb=>6,Em=>2,Fm=>3,Gbm=>4,Gm=>5,Abm=>6,Am=>0,Bbm=>1,Bm=>2,Cm=>3,Dbm=>4,Dm=>5,Ebm=>6},E=>{E=>0,F=>1,Gb=>2,G=>3,Ab=>4,A=>0,Bb=>1,B=>2,C=>3,Db=>4,D=>5,Eb=>6,Em=>0,Fm=>1,Gbm=>2,Gm=>3,Abm=>4,Am=>0,Bbm=>1,Bm=>2,Cm=>3,Dbm=>4,Dm=>5,Ebm=>6});
%b=('A#'=>'Bb','C#'=>'Db','D#'=>'Eb','F#'=>'Gb','G#'=>'Ab');
foreach(qw(e B G D A E)){p($_,@ARGV)}
sub p{$s = shift;print "$s ";$s = uc($s);foreach(@_){while(($h,$f)=each(%b)){s/$h/$f/}print "$c{$s}->{$_}---"}print "\n"}

Hier ist die entsprechende Ausgabe.

dhrasmus:Desktop standage$ perl guitar Bm Gb A E G D Em F#
e 2---2---0---0---3---5---0---2---
B 3---2---2---0---3---7---0---2---
G 4---3---2---1---4---7---0---3---
D 4---4---2---2---5---7---2---4---
A 2---4---0---2---5---5---2---4---
E 2---2---0---0---3---5---0---2---

4

Da es schon kürzere Lösungen gibt (verdammt GolfScript!), Ist hier meine:

Python, 229 Zeichen

s=[("E EmF FmF#GbG GmG#AbA AmA#BbB BmC CmC#DbD DmD#Eb".find("%-02s"%s[:2])/4,s[-1]!='m')for s in raw_input().split()]
for c in range(6):
 l='eBGDAE'[c]+' '
 for(i,M)in s:x=i>4;l+=`i-5*x+2*(2<c+x<5)+(M+x)*(c==2-x)`+"---"
 print l

Ausgabe:

> echo "Bm Gb A E G D Em F#" | python guitar.py
e 2---2---0---0---3---5---0---2---
B 3---2---2---0---3---7---0---2---
G 4---3---2---1---4---7---0---3---
D 4---4---2---2---5---7---2---4---
A 2---4---0---2---5---5---2---4---
E 2---2---0---0---3---5---0---2---

3

Python, 449 Zeichen

z=int
f=str
r=range
j=''.join
n='E F F# G G# A A# B C C# D D#'.split()
n+=[x+'m'for x in n]
c=[j([f(z(x)+i)for x in'001220'])for i in r(5)]+[j([f(z(x)+i)for x in'022200'])for i in r(7)]
c+=[x[:2]+f(z(x[2])-1)+x[3:]for x in c[:5]]+[x[0]+f(z(x[1])-1)+x[2:]for x in c[5:]]
a=[c[n.index((chr(ord(i[0])-1)+'#'+i[2:]).replace('@','G')if len(i)-1 and i[1]=='b'else i)]for i in raw_input().split()] 
for i in r(6):print'eBGDAE'[i],j([x[i]+'-'*3 for x in a])

3

C99 - 231 Zeichen

Die Akkorde werden in der Befehlszeile angegeben, ein Argument pro Akkord, und natürlich gibt es keine Eingabevalidierung.

#include<stdio.h>
int main(int c,char**v){for(char*o="e0)B2)G2*D2+A0+E0)",i,m;*o;o+=3,v-=c,puts(""))for(printf("%c ",*o);*++v;printf("%c---",i-(i>2)-i/9+o[1+i/8]-(*o-66-i/8*5?0:m?m+2[*v]>99:0)))m=1[*v],i=(**v*2-4+m/35-m/98*3)%14;}

Probelauf:

$ ./a.out Bm Gb A E G D Em F#
e 2---2---0---0---3---5---0---2---
B 3---2---2---0---3---7---0---2---
G 4---3---2---1---4---7---0---3---
D 4---4---2---2---5---7---2---4---
A 2---4---0---2---5---5---2---4---
E 2---2---0---0---3---5---0---2---

Nicht golfen

#include<stdio.h>
int main(int c,char**v){
     // o points to three characters per output line:
     //   string name, number for A, adjusted number for E (ASCII code minus 7)
     char* o="e0)B2)G2*D2+A0+E0)",
          i, // chord: A=0, A#=1, ..., G#=13, allowing also 3="E#" and 9="B#"
          m; // second character in chord name
     for (; *o; o+=3) {
          printf("%c ", *o);
          for (; *++v; ) {
               m = 1[*v],
               i = (**v*2-4+m/35-m/98*3)%14; // parse & adjust for sharp, flat
               printf("%c---",
                      i-(i>2)-i/9 // eliminate "E#", "B#"
                      +o[1+i/8] // get the number for a major chord
                      // adjust for minor...
                      -(*o-66-i/8*5
                        ? 0
                        : m ? m+2[*v]>99 : 0));
          }
          v -= c; // rewind argument pointer
          puts("");
     }
}

Nichtstandardisierte C - 206 Zeichen

Wenn es uns nicht um Sprachspezifikationen geht, kann GCC den folgenden Einzeiler in eine funktionale Binärdatei kompilieren, obwohl C99-Variablendeklarationen mit einer K & R-Argumentdeklaration (und einer impliziten Deklaration von printf) gemischt werden.

main(c,v)char**v;{for(char*o="e0)B2)G2*D2+A0+E0)",i,m;*o;o+=3,v-=c,puts(""))for(printf("%c ",*o);*++v;printf("%c---",i-(i>2)-i/9+o[1+i/8]-(*o-66-i/8*5?0:m?m+2[*v]>99:0)))m=1[*v],i=(**v*2-4+m/35-m/98*3)%14;}

2

C ++, 432

#include <cmath>
#include <iostream>
F(int c){c-=65;return c*1.6+sin(c/5.+.3);}
T(int a,int s){if(s)return(a=(T(a,s-1)+2)%3)-=(a==1&s>2);return(a<7)*2;}
#define c(a,b) while(*(++j)==a)b;--j; 
#define O std::cout<<
main(int a,char*p[]){
int P=2;for(int i=-1;++i<6;P=2){O p[1][i];O" ";while(P<a){char*j=p[P++];
int f=F(*j);c('#',++f)c('b',--f)
int t=T(f,i)*3.5;if(*(++j)!='m'){--j;t+=(t==3);}
O(f-F(p[1][i])+t+24)%12;O"---";
}O'\n';}}

Beachten Sie, dass hierfür die Gitarrenstimmung als erster Parameter erforderlich ist. (Die meisten Nicht-Standard-Stimmungen führen zu lächerlichen Ergebnissen, aber ich vermute, Sie geben sich mit der Standard-Stimmungen zufrieden.)

Für Hotel California können Sie tun $./a.out EBGDAE Cbm Gb Bbb Fb G D Em F# Bm F# G## D## F## C## D##m E##. Ergebnis:

E 2---2---0---0---3---5---0---2---2---2---5---0---3---5---0---2---
B 3---2---2---0---3---7---0---2---3---2---5---0---3---7---0---2---
G 4---3---2---1---4---7---0---3---4---3---6---1---4---7---0---3---
D 4---4---2---2---5---7---2---4---4---4---7---2---5---7---2---4---
A 2---4---0---2---5---5---2---4---2---4---7---2---5---5---2---4---
E 2---2---0---0---3---5---0---2---2---2---5---0---3---5---0---2---

Das Stimmen der oberen vier Saiten auf kleine Terzen macht es sehr einfach, Drei- und Vier-Saiten-Akkorde in vielen Inversionen ohne offene Saiten zu spielen, und ohne einen Finger "über" eine Saite legen zu müssen, ohne diese zu berühren. Mit den Strings DFG # B funktioniert eine Akkordfolge wie "Bbm F Bbm Gb Db Ebm Db F Bbm F F7 Bbm" (Mermaid Song) sehr einfach. Es muss nur ein Bund hoch und runter geschoben werden. Es gibt einen Schlüsselwechsel in einem halben Schritt, aber das bedeutet nur, einen Bund nach oben zu bewegen. Ich habe jedoch nicht herausgefunden, was ich mit den anderen beiden Saiten am besten machen soll.
Supercat

@supercat: interessant, ich werde das morgen auf meiner Gitarre ausprobieren ...
nicht mehr gegen den Uhrzeigersinn

Ich würde gerne hören, was Sie denken. Ich hatte ein paar Mal im Abstand von Jahren eine Gitarre aufgegriffen und immer wieder aufgegeben, weil die Fingersätze sowohl willkürlich als auch unangenehm wirkten. Dann musste ich darüber nachdenken, welche Stimmungen ein einfaches Fingerspiel ermöglichen würden. Da geschlossene Akkorde Intervalle haben, die von einem kleinen Drittel bis zu einem perfekten Viertel reichen, bedeutet das Stimmen von Saiten zu kleinen Dritteln, dass jede Saite an einem Punkt gedämpft wird, der nicht niedriger als die darunter liegende Saite ist. Wenn ich eine linkshändige Gitarre probieren kann, könnte ich auch perfekte Quarten mit umgekehrter Saitenreihenfolge probieren, da es ähnlich sein sollte.
Supercat

Stimmen auf kleine Drittel bedeutet, dass für jede Position des ersten Fingers auf der untersten Saite drei Hauptakkordumkehrungen und drei Nebenakkordumkehrungen verfügbar sind. Sie können auch einen siebten Akkord spielen, indem Sie den zweiten Finger über die oberen drei Saiten legen. Beginnen Sie für Mermaid Song im dritten Bund und spielen Sie F-Bb-DF (mit den Fingern 1-3-3-4). Dann ist F FACF (1-2-2-4). Gb ist ein Bund hoch, gefingert 1-2-2-4 (wie F). Db ist wieder einen Bund runter, 1-3-4-4. Ebm ist wieder da, 1-2-4-4.
Supercat

Ich brauchte nur ein paar Stunden, um zu dem Punkt zu gelangen, an dem ich einige Stücke (einschließlich des oben erwähnten Mermaid-Songs) problemlos spielen konnte, nachdem ich mit Hilfe eines Keyboards herausgefunden hatte, wie die richtigen Akkordnoten lauten sollten. Als ich diesen Stil ausprobiert habe, fühlte er sich erstaunlich natürlich an und ich mag die Art und Weise, wie man alle drei Umkehrungen jedes Dur- und Moll-Akkords verwenden kann. Wenn man nur Dur- und Moll-Akkorde haben möchte, könnte eine Stimmung wie F-Ab-B-Eb-Gb-D theoretisch Dur- und Moll-Akkorde mit sechs Fingern und einfachen Fingersätzen (1-2-2-3-4-4 oder 1) zulassen -1-2-3-3-4) aber ohne die Umkehrungen.
Supercat

2

390 345 340 Postscript

Vereinfacht zu einem gitarren-pragmatischen Ansatz (die E-Form ist nur eine Variation der A-Form, die mit einem Fingerwechsel eine Saite nach unten verschoben wird). Leihte die Idee der codierten Zeichenfolge aus den anderen Antworten.

[/p{print}/x{exch}/e{cvx exec}/d{dup 0
get}/f{forall}(A0#1B2C3D5E7F8G:b;){}forall/m{dup
4 2 copy get 1 sub put}109{m 48}/+{[3 1 roll x{1
index add x}f]}/*{[0 0 2 2 2 0 0]x{load e 48 sub
+}f d 12 gt{-12 +}if d 6 gt{m -7 + 1}{0}ifelse 6
getinterval}>>begin[ARGUMENTS{*}f][(E)(A)(D)(G)(B)(e)]6{[x
e p( )p]x[x{[x e( )cvs p(---)p]}f]x()=}repeat

Vorher:

450 442 418 Postscript

Auch hier habe ich das Ausgabeformat korrigiert. (Frühere Versionen begannen mit "E ---" anstatt mit "e".)

<</i{index}/a{add}/p{pop}/x{exch}/g{getinterval}/r{print}/f{forall}/e{exec}>>begin<<65[0
2 3 5 -5 -4 -2]{1 i 1 a}f p 35{1 a}98{1 sub}109{x dup
4 20 put x}>>begin[ARGUMENTS{[x[0 5 12 17 21 24 29
0]x{load e}f x{1 i a x}f]dup 0 get 0 ge{0}{1}ifelse 7
g[0 -5 -10 -15 -19 -24 -29]0 1 6{2 copy get 3 i 2 i
get a 3 copy put p p}for x p 0 6
g}f][(E)(A)(D)(G)(B)(e)]6{[x cvx e r( )r]x[x{[x cvx
e( )cvs r(---)r]}f]x(\n)r}repeat

Wie man es laufen lässt: gsnd -q -- tab.ps Bm Gb A E G D Em F\#(verstecke die Spitze von der Schale).

Die Version ohne Golf war fast schwieriger als die mit Golf. Aber ich habe versucht, gründlich zu sein. Edit: noch ein paar Kommentare zu den Tricksy-Bits.

%!PS
<<    %axioms and operations
/t{2 2 1}    %major tetrachord
/m{t t 2}    %mixolydian mode
/u{2 1 2}    %minor tetrachord
/a{u u}      %aolian mode
/s{m m t}    %2.5-octave mixolydian intervals
/r{3 1 roll}
/${[exch 0 exch{1 index add}forall]}    %running sum: convert (relative)intervals to (abstract)fretstops
/+{[r exch{1 index add exch}forall pop]}    %scale array by scalar
/@{[r{2 copy get r pop}forall pop]}    %select array elements from array of indices
/&{0 1 3 index length 1 sub{    %array2 += array1
    2 copy get 3 index 2 index get add 3 copy put pop pop}for exch pop}
>>begin<<    %map ascii values to scaling functions
65[a]$    %generate fretstops of the A aolian scale to assign scalars to note names
[0 0 0 0 -12 -12 -12]&    %drop E F and G down an octave
{[exch/+ cvx]cvx 1 index 1 add}forall pop    %generate the pairs 'A'->{0 +}, 'B'->{2 +}
35{1 +}     %'#'-> scale up by one
98{-1 +}    %'b'-> scale down by one
109{dup 4 2 copy get 1 sub put}     %'m'-> tweak the 'third' down by one
%generate chord pattern from (string)
/*{[s]$       %generate fretstops of the E mixolydian scale
  [1 4 8 11 13 15 18]    %A-shape figured bass: IV chord of E mixolydian
  -1 +       %convert scale degrees to array indices
  @       %generate chord template by selecting indices from mixolydian scale
  exch{load exec}forall       %execute ascii values, scaling the pattern
  dup 0 get 0 ge{0}{1}ifelse 6 getinterval    %discard first note if it has fallen off the bottom
  [0 -5 -10 -15 -19 -24]&}    %subtract the string offsets
>>begin    %activate definitions
%(A)* pstack()= clear    %[0 0 2 2 2 0]
%(B)* pstack()= clear    %[2 2 4 4 4 2]
%(F#)* pstack()= clear    %[2 4 4 3 2 2]
%(Abm)* pstack()=    %[4 6 6 4 4 4]
[ARGUMENTS{*}forall]    %convert array of strings to array of patterns
[(E)(A)(D)(G)(B)(e)]    %array of string names
6{    %for each "string"
    [exch cvx exec print( )print]    %pop string name and print with space
    exch       %put names behind numbers
    [exch{     %for each "chord"
        [exch cvx exec( )cvs print(---)print]    %pop number, convert, print with trailing hyphens
    }forall]    %zip up chord array for next iteration
    ()=         %print a newline
    exch        %put numbers behind names
}repeat

Und wie wäre es mit Haus der aufgehenden Sonne als Test?

04:51 PM:~ 0> gsnd -q -- tabb.ps Em G A C Em G B B Em G A C Em B Em B|sed 's/^/    /'
e 0---3---0---3---0---3---2---2---0---3---0---3---0---2---0---2---
B 0---3---2---5---0---3---4---4---0---3---2---5---0---4---0---4---
G 0---4---2---5---0---4---4---4---0---4---2---5---0---4---0---4---
D 2---5---2---5---2---5---4---4---2---5---2---5---2---4---2---4---
A 2---5---0---3---2---5---2---2---2---5---0---3---2---2---2---2---
E 0---3---0---3---0---3---2---2---0---3---0---3---0---2---0---2---

Ich habe einen Kommentar zu diesem Code in einer anderen Antwort hier geschrieben .
luser droog
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.