進行中のバブルソート


19

2つの入力を受け取る関数またはプログラムを作成します。

  • ソートされる整数のリスト(20要素未満)
  • 正の整数、Nあなたがとるべき比較の数を言う

関数は停止し、N比較後に結果の整数のリストを出力します。N比較が行われる前にリストが完全にソートされている場合、ソートされたリストが出力されます。


バブルソートのアルゴリズムはよく知られている、と私はほとんどの人がそれを知っていると思います。次の擬似コードとアニメーション(リンクされているWikipediaの記事の両方)が必要な詳細を提供する必要があります。

procedure bubbleSort( A : list of sortable items )
   n = length(A)
   repeat 
     swapped = false
     for i = 1 to n-1 inclusive do
       /* if this pair is out of order */
       if A[i-1] > A[i] then    
         /* swap them and remember something changed */
         swap( A[i-1], A[i] )
         swapped = true
       end if
     end for
   until not swapped
end procedure

以下のアニメーションは進行状況を示しています。

ここに画像の説明を入力してください

リスト(リンクされているウィキペディアの記事から直接引用)の例は、リストをソートするときの手順を示しています( 5 1 4 2 8 )

最初のパス

1: ( 5 1 4 2 8 ) ->  ( 1 5 4 2 8 ) // Here, algorithm compares the first two elements, 
                                   // and swaps since 5 > 1.
2: ( 1 5 4 2 8 ) ->  ( 1 4 5 2 8 ) // Swap since 5 > 4
3: ( 1 4 5 2 8 ) ->  ( 1 4 2 5 8 ) // Swap since 5 > 2
4: ( 1 4 2 5 8 ) ->  ( 1 4 2 5 8 ) // Now, since these elements are already in order 
                                   // (8 > 5), algorithm does not swap them.

セカンドパス

5: ( 1 4 2 5 8 ) ->  ( 1 4 2 5 8 )
6: ( 1 4 2 5 8 ) ->  ( 1 2 4 5 8 ) // Swap since 4 > 2
7: ( 1 2 4 5 8 ) ->  ( 1 2 4 5 8 )
8: ( 1 2 4 5 8 ) ->  ( 1 2 4 5 8 )

現在、配列は既にソートされていますが、アルゴリズムはそれが完了したかどうかを知りません。アルゴリズムは、ソートされていることを知るために、スワップなしの1つのパス全体を必要とします。

サードパス

9: ( 1 2 4 5 8 ) ->  ( 1 2 4 5 8 )
10:( 1 2 4 5 8 ) ->  ( 1 2 4 5 8 )
11:( 1 2 4 5 8 ) ->  ( 1 2 4 5 8 )
12:( 1 2 4 5 8 ) ->  ( 1 2 4 5 8 )

テストケース:

フォーマット: Number of comparisons (N): List after N comparisons

Input list:
5  1  4  2  8
Test cases: 
1: 1  5  4  2  8
2: 1  4  5  2  8
3: 1  4  2  5  8
4: 1  4  2  5  8
5: 1  4  2  5  8
6: 1  2  4  5  8
10: 1  2  4  5  8
14: 1  2  4  5  8

Input list:
0: 15  18  -6  18   9  -7  -1   7  19  19  -5  20  19   5  15  -5   3  18  14  19
Test cases:
1: 15  18  -6  18   9  -7  -1   7  19  19  -5  20  19   5  15  -5   3  18  14  19
21: -6  15  18   9  -7  -1   7  18  19  -5  19  19   5  15  -5   3  18  14  19  20
41: -6   9  -7  15  -1   7  18  18  -5  19  19   5  15  -5   3  18  14  19  19  20
60: -6  -7  -1   9   7  15  18  -5  18  19   5  15  -5   3  18  14  19  19  19  20
61: -6  -7  -1   7   9  15  18  -5  18  19   5  15  -5   3  18  14  19  19  19  20
81: -7  -6  -1   7   9  15  -5  18  18   5  15  -5   3  18  14  19  19  19  19  20
119: -7  -6  -1  -5   7   9  15   5  15  -5   3  18  14  18  18  19  19  19  19  20
120: -7  -6  -1  -5   7   9  15   5  15  -5   3  18  14  18  18  19  19  19  19  20
121: -7  -6  -1  -5   7   9   5  15  15  -5   3  18  14  18  18  19  19  19  19  20
122: -7  -6  -1  -5   7   9   5  15  15  -5   3  18  14  18  18  19  19  19  19  20
123: -7  -6  -1  -5   7   9   5  15  -5  15   3  18  14  18  18  19  19  19  19  20
201: -7  -6  -5  -1  -5   3   5   7   9  14  15  15  18  18  18  19  19  19  19  20
221: -7  -6  -5  -5  -1   3   5   7   9  14  15  15  18  18  18  19  19  19  19  20

  • はい、組み込みのバブルソートアルゴリズムが許可されています。
  • いいえ、正の整数または一意の整数のみを想定することはできません。
  • 並べ替えは上記の順序にする必要があります。リストの最後から始めることはできません

2
明確で完全に合理的。残念ながら、このコメントは狭すぎず、ミラー化されたバブルソートの本当に素晴らしい解決策を発見しました。)
Ton Hospel

リストは空ではありませんか?
マイル

また、リストのサイズは2以上ですか?長さ1のリストまたは空のリストに対して、以下の回答が機能しないことに気付きました。
マイル

回答:


2

ゼリー、25バイト

ḣ©ṫ-Ṣ®ṖṖ¤;;ṫḊ¥
JḊṁ¹³W¤;ç/

J での私の答えに基づく

オンラインでお試しください!

比較の数を確認します。

説明

ヘルパーリンクは[i-1, i]、バブルソートの比較と同じ結果を生成するソートによって、インデックスのリストを変更します。

ḣ©ṫ-Ṣ®ṖṖ¤;;ṫḊ¥  Helper link - Input: list A, index i
ḣ               Take the first i values
 ©              Save a copy of it
  ṫ-            Take the last two values
    Ṣ           Sort them
         ;      Append them to
     ®            Get the copy
      ṖṖ¤         Pop the last two values (Ṗ is pop)
          ;     Prepend it to
           ṫ      Take the last i values
            Ḋ¥    Dequeue - remove the head

JḊṁ¹³W¤;ç/  Input: list A and # of comparisons n
J           Enumerate the indices of A
 Ḋ          Dequeue - remove the head
  ṁ         Reshape it cyclically to length n
   ¹        Identity function (Just to avoid parsing rules)
       ;    Append it to
    ³         The list A
     W¤       Wrap it as an array
        ç/  Reduce from left to right using the helper link and return

9

JavaScript(ES6)、102 82 80 86 80バイト

バグ修正と@ edc65のおかげで1バイト節約

(a,m)=>eval("for(i=0;m;a[j=i-1]>(b=a[i])?a[a[i]=a[j],j]=b:0)1/a[++i]?m--:i=0;a")

再帰は間違いなくそうでないかもしれませ が、おそらく最善のアプローチでありませんが、私は今のところループに固執しています。

やってみて:


私はあなたの再帰バージョンを82バイトまでゴルフすることに成功しましたf=(a,m,n=0,_,b=a[n+1])=>b+.5?m?f(a,m-1,n+1,a[n]>b?a[a[n+1]=a[n],n]=b:0):a:f(a,m,0)
ニール

@Neilうわー、それは印象的です!必要に応じて、自分で投稿できます。
ETHproductions

@Neil 80でも再帰バージョンを実行できますが、最後のものを削除するだけです,0
ジョナサンアラン

お試し1/bの代わりb+.5をチェックするためにundefined
edc65

1 / bに対する私の提案はまだ
有効です

7

Haskell、83 82 81バイト

y%x@(a:b:c)=(y++x):(y++[min a b])%(max a b:c)
y%x=[]%(y++x)
[x]!_=[x] 
x!n=[]%x!!n

使用例:[5,1,4,2,8] ! 5-> [1,4,2,5,8]

関数% yは、現在のパスでこれまでにアクセスした要素を追跡しますが、xまだ調べていない要素です。aそしてb次の2つ、つまりスワップの候補です。リストの最後に到達したら、最初から開始しますy%x = []%(y++x)。すべてのステップは、メイン関数がnth要素を選択するリストに保存されます。

編集:以前のバージョンは単一の要素リストでは機能しませんでしたが、幸いなことに新しいバージョンはさらに短くなりました。


これをオンラインでテストすることはできますか?Haskellについては何も知らないので、これをオンラインIDEに直接貼り付けようとするとエラーが発生します。私はいくつかの基本的なものが欠けていると思います...?
スチューイーグリフィン

追加f=含むプログラムへの三行を追加し、その後、答えの2行目の前にmain=print(f [5,1,4,2,8] 5)。これで実行可能になります。
リン

@WeeingIfFirst:完全なプログラム
nimi

4

Python 3、77 74バイト

@Maltysenのおかげで-3バイト(j宣言の初期化)

lambda l,n,j=0:exec('j*=j<len(l)-1;l[j:j+2]=sorted(l[j:j+2]);j+=1;'*n)or l

イデオンのテストケース

sorted各比較およびスワップ操作を実行するために使用しますが、バブルソートを実行しています。

セットj=0(左インデックス)、次いで実行は、n比較するとリセット隣接リスト項目のスワップj0、このウィンドウは、境界外に出るたびに。

j*=j<len(l)-1乗算意志jによってFalse(すなわち0)その時点で、他のすべての時間のに対し、それが掛けしますjことにより、True(すなわち1)。

(空のリストでも機能します。)


1
私はあなたがプラスとラムダデフォルトのparamsにJ = 0を設定することを除去することにより、保存することができると思う
Maltysen

1
また、あなたがリセットする必要はありませんj、あなたは使用することができます%
Maltysen

@Maltysenは実際、モジュロ演算を使用してバイトを節約できません。ゼロ除算エラーが発生する場合、長さ1のリストを処理する必要があるため、バイト単位でプッシュする処理ロジックを追加します。
ジョナサンアラン

1
すべてのテストケースで正常に動作し、MATLABの回答よりもかなり短いです。+1 =)残念ながら、evalインラインの割り当てのために、MATLABで同じ手法を使用することはできません。
スティーヴィーグリフィン

1
新しいテストケースを含めるために更新
ジョナサンアラン

3

PowerShell v2 +、135 129バイト

param($a,$n)for($s=1;$s){($s=0)..($a.count-2)|%{if($a[$_]-gt$a[$_+1]){$a[$_],$a[$_+1]=$a[$_+1],$a[$_];$s=1}if(!--$n){$a;exit}}}$a

そう。たくさんの。ドル。

このチャレンジにはソートが保証されているため、各パスの最後の要素をスキップする「無料」の最適化が含まれず、代わりに毎回フルパスを実行することを認識して6バイトを節約し$a.countました。forループして$z変数を削除しました。

気の利いたスポットを1つ用意し、1ステップでスワップを実行して、バブルをまっすぐに並べ替えます-
$a[$_],$a[$_+1]=$a[$_+1],$a[$_]

終了ロジックは次の方法で処理されます if(!--$n){$a;exit}

テストケース

(配列は、配列を文字列化するためのデフォルトの出力フィールド区切り文字がスペースであるため、ここではスペースで区切られています。文字列化は、ラベルと連結しているために発生します"$_ -> "。)

PS C:\Tools\Scripts\golfing> 1,2,3,4,5,6,10,14|%{"$_ -> "+(.\bubble-sorting-in-progress.ps1 @(5,1,4,2,8) $_)}
1 -> 1 5 4 2 8
2 -> 1 4 5 2 8
3 -> 1 4 2 5 8
4 -> 1 4 2 5 8
5 -> 1 4 2 5 8
6 -> 1 2 4 5 8
10 -> 1 2 4 5 8
14 -> 1 2 4 5 8

PS C:\Tools\Scripts\golfing> 1,21,41,60,61,81,119,120,121,122,123,201,221|%{"$_ -> "+(.\bubble-sorting-in-progress.ps1 @(15,18,-6,18,9,-7,-1,7,19,19,-5,20,19,5,15,-5,3,18,14,19) $_)}
1 -> 15 18 -6 18 9 -7 -1 7 19 19 -5 20 19 5 15 -5 3 18 14 19
21 -> -6 15 18 9 -7 -1 7 18 19 -5 19 19 5 15 -5 3 18 14 19 20
41 -> -6 9 -7 15 -1 7 18 18 -5 19 19 5 15 -5 3 18 14 19 19 20
60 -> -6 -7 -1 9 7 15 18 -5 18 19 5 15 -5 3 18 14 19 19 19 20
61 -> -6 -7 -1 7 9 15 18 -5 18 19 5 15 -5 3 18 14 19 19 19 20
81 -> -7 -6 -1 7 9 15 -5 18 18 5 15 -5 3 18 14 19 19 19 19 20
119 -> -7 -6 -1 -5 7 9 15 5 15 -5 3 18 14 18 18 19 19 19 19 20
120 -> -7 -6 -1 -5 7 9 15 5 15 -5 3 18 14 18 18 19 19 19 19 20
121 -> -7 -6 -1 -5 7 9 5 15 15 -5 3 18 14 18 18 19 19 19 19 20
122 -> -7 -6 -1 -5 7 9 5 15 15 -5 3 18 14 18 18 19 19 19 19 20
123 -> -7 -6 -1 -5 7 9 5 15 -5 15 3 18 14 18 18 19 19 19 19 20
201 -> -7 -6 -5 -1 -5 3 5 7 9 14 15 15 18 18 18 19 19 19 19 20
221 -> -7 -6 -5 -5 -1 3 5 7 9 14 15 15 18 18 18 19 19 19 19 20

3

R、132 131 112 136バイト

プログラムは次のように入力を受け取ります。最初にN、次にベクトル自体。たとえば、あなたがしたい場合v = [5 1 4 2 8]n = 1、入力に入るscanIS 1 5 1 4 2 8。だから、このプログラムを実行するためには、あなたが最初の行を実行し数字にコンソールでの一つ一つを供給し、その後、残りの部分を実行します(これはREPLの答えです)。

次に、次のコードでトリックを行います。

v=scan()
s=m=0
while(!s){s=T;for(i in 3:length(v)){m=m+1
if(m>v[1]){s=T;break}
if(v[i-1]>v[i]){v[c(i-1,i)]=v[c(i,i-1)];s=F}}}
cat(v[-1])

テスト:

Input: a vector with N first and the elements to be sorted next
1 5 1 4 2 8
5 5 1 4 2 8
14 5 1 4 2 8
60 15 18 -6 18  9 -7 -1  7 19 19 -5 20 19  5 15 -5  3 18 14 19

Output: 
1 5 4 2 8
1 4 2 5 8
1 2 4 5 8
-6 -7 -1  9  7 15 18 -5 18 19  5 15 -5  3 18 14 19 19 19 20

更新:のために1バイトgolfed VLOを


2
これには、入力を変数としてハードコーディングし、REPLメカニズムを介して暗黙的に出力を表示する必要がありますが、受け入れ可能なI / Oメソッドのリストでは受け入れられません。
メゴ

@Megoさて、私はそれを修正しました。今では...完全に準拠しているかどうかを確認してください
アンドレイKostyrka

最初のs = Tを削除できるようです。それでも正しい出力が得られます。これにより、4バイト節約できます。編集:実際には、while()ループを完全に削除し、for()ループを使用して、s = Tをbreakに置き換えるだけで、中括弧を取り除くこともできます。これにより、v = scan(); s = m = 0; for(i in 3:length(v)){m = m + 1; if(m> v [1])break; if(v [i- 1]> v [i]); v [c(i-1、i)] = v [c(i、i-1)]; break}}; v [-1]合計117バイト。
rturnbull 16

@rturnbullお使いのバージョンは非常に優れています!よろしくお願いします。
アンドレイKostyrka

@rturnbullこれらの初期のコメントはどこに行きましたか?19バイト離れたゴルフの提案...バブルソートのパフォーマンスがO(n²)であるために不可欠であった余分なループを削除しましたが、その余分なループがないと(n-1)長くなります。確認する必要がありました...これで修正され、入力をフィードする方法の説明が含まれています!以前よりも良いですか?
アンドレイKostyrka 16


2

JavaScript(ES6)、82 80 79バイト

f=(a,m,n=0,_,b=a[n+1])=>1/b?m?f(a,m-1,n+1,a[n]>b?a[a[n+1]=a[n],n]=b:0):a:f(a,m)

@ETHproductionの元の回答に基づいています。編集:@JonathanAllanのおかげで2バイトを保存しました。@ edc65のおかげで1バイト節約されました。


2

J62 60バイト

>@([:({.,(2/:~@{.}.),]}.~2+[)&.>/]|.@;<:@#@]<@|i.@[)^:(]1<#)

これは、LHSでの比較の数とRHSでの整数のリストという2つの引数を取る動詞です。最初に、リストの長さが1より大きいかどうかをチェックします。そうでない場合は、リストを変更せずに返します。それ以外の場合は、指定した数の比較を実行して結果を返すことでリストを操作します。

使用法

最初のテストケースでは、extrasコマンドを使用して複数の入出力をフォーマットします。2番目のテストケースは、単一の入力/出力として示されています。

   f =: >@([:({.,(2/:~@{.}.),]}.~2+[)&.>/]|.@;<:@#@]<@|i.@[)^:(]1<#)
   1 2 3 4 5 6 10 14 ([;f)"0 1 ] 5 1 4 2 8
┌──┬─────────┐
│1 │1 5 4 2 8│
├──┼─────────┤
│2 │1 4 5 2 8│
├──┼─────────┤
│3 │1 4 2 5 8│
├──┼─────────┤
│4 │1 4 2 5 8│
├──┼─────────┤
│5 │1 4 2 5 8│
├──┼─────────┤
│6 │1 2 4 5 8│
├──┼─────────┤
│10│1 2 4 5 8│
├──┼─────────┤
│14│1 2 4 5 8│
└──┴─────────┘
   1 f 15 18 _6 18 9 _7 _1 7 19 19 _5 20 19 5 15 _5 3 18 14 19
15 18 _6 18 9 _7 _1 7 19 19 _5 20 19 5 15 _5 3 18 14 19
   123 f 15 18 _6 18 9 _7 _1 7 19 19 _5 20 19 5 15 _5 3 18 14 19
_7 _6 _1 _5 7 9 5 15 _5 15 3 18 14 18 18 19 19 19 19 20
   221 f 15 18 _6 18 9 _7 _1 7 19 19 _5 20 19 5 15 _5 3 18 14 19
_7 _6 _5 _5 _1 3 5 7 9 14 15 15 18 18 18 19 19 19 19 20

説明

可変性を使用する簡潔なコードをJで書くのは難しいので、代わりに、一連のインデックスのリストを減らすことに問題を変換します。このコードは面倒だと思うので、各プリミティブの代わりに各フレーズの仕事を説明します。最初の部分はリストの長さを取得し、範囲を生成します。次に、サイズ2の各中置記号を操作して、比較の1つのパスをエミュレートします。

   i. # 5 1 4 2 8
0 1 2 3 4
   2 <\ i. # 5 1 4 2 8
┌───┬───┬───┬───┐
│0 1│1 2│2 3│3 4│
└───┴───┴───┴───┘
   2 <@{.\ i. # 5 1 4 2 8
┌─┬─┬─┬─┐
│0│1│2│3│
└─┴─┴─┴─┘

これらは、各比較の開始インデックスです。7つの比較が実行されている場合は、目的の量を得るために比較を変更します。Jは右から左に解析するため、fold-rightのように右から左に縮小します。初期リストを追加し、それを逆にします。

   7 $ 2 <@{.\ i. # 5 1 4 2 8
┌─┬─┬─┬─┬─┬─┬─┐
│0│1│2│3│0│1│2│
└─┴─┴─┴─┴─┴─┴─┘
   |. 5 1 4 2 8 ; 7 $ 2 <@{.\ i. # 5 1 4 2 8
┌─┬─┬─┬─┬─┬─┬─┬─────────┐
│2│1│0│3│2│1│0│5 1 4 2 8│
└─┴─┴─┴─┴─┴─┴─┴─────────┘

または、範囲[0、7)を作成し、各値をリストの長さから1を引いた値をとって同じ範囲を作成できます。

   (<: # 5 1 4 2 8) <@| i. 7
┌─┬─┬─┬─┬─┬─┬─┐
│0│1│2│3│0│1│2│
└─┴─┴─┴─┴─┴─┴─┘

最後の部分は、RHSのリストと、比較の開始インデックスをマークするLHSのインデックスを取得する動詞です。そのインデックスで始まる2つの要素を選択し、並べ替え、リストにプラグインして返します。

   > ({.,(2/:~@{.}.),]}.~2+[)&.>/ |. 5 1 4 2 8 ; 7 $ 2 <@{.\ i. # 5 1 4 2 8
1 2 4 5 8

印象的で、非常に印象的な+1。
魔法のタコ

1

Matlab、93 91バイト

function l=f(l,m)
n=numel(l)-1;i=0;while n&m;i=mod(i,n)+1;m=m-1;l(i:i+1)=sort(l(i:i+1));end

を省略して11バイトを節約しif l(i)>l(i+1);l(i:i+1)=l([i+1,i])、代わりに毎回2つの要素を並べ替えます。長さ1のリストで機能します。Octaveのm--演算子を使用して1〜2バイトを保存できますが、それはそれほど多くありません。

設定により、さらに2つのバイトを保存しn=numel(l)-1;、その後、私はちょうど行うことができますので、while n代わりにwhile n>1、とi=mod(i,n)+1の代わりにi=mod(i,n-1)+1


記録のために、この回答はチャレンジが作成されてから何時間も後に書かれました。


1

Groovy(101バイト)

{l,n->(l.size()..0).each{i->(0..i-2).each{if(l[it]>l[it+1] && n>0 && it>-1){l.swap(it,it+1)};n--}};l}

編集:私は自分のスワップクロージャーを書く必要はありませんでした、groovyにはこれが組み込まれていました。
ここで試してください:https : //groovyconsole.appspot.com/script/5104724189642752

出力トレースの例:

4:[1, 5, 4, 2, 8]
3:[1, 4, 5, 2, 8]
2:[1, 4, 2, 5, 8]
1:[1, 4, 2, 5, 8]
0:[1, 4, 2, 5, 8] - Locks in the final answer.
-1:[1, 4, 2, 5, 8]
-2 (Return):[1, 4, 2, 5, 8]

古い実装(122バイト)

m={l,n->s={x,y->t=l[x];l[x]=l[y];l[y]=t};((l.size()-2)..2).each{i->(0..i).each{if(l[it]>l[it+1] && n){s(it,it+1)};n--}};l}

ここで試してください:https : //groovyconsole.appspot.com/script/6316871066320896


2つ未満の項目を持つリストの場合のこのエラー
ジョナサンアラン

私の携帯電話では、2番目のifステートメントでit = 0を追加すると、その問題が修正されます。
魔法のタコUr

負の入力を持つリストでも失敗するようです。たとえば、2番目のテストケース。
スティーヴィーグリフィン

今は動作しますが、昨晩は携帯電話を使用していたため、必要な編集を行うことができませんでした。
魔法のタコUr

1

php、148 145バイト

<?php for($i=2,$a=$argv;$a[1]--;$i=$i==count($a)-2?2:$i+1)if($a[$i]>$a[$i+1])list($a[$i],$a[$i+1])=[$a[$i+1],$a[$i]];echo substr(join(' ',$a),5);

私はループ構造にあまり満足していませんが、リストスイッチが好きで、それが機能するので、とにかく投稿しています。php7.1では、少なくとも4バイトを節約できます。

より良いフォーマットで:

<?php 
for($i=2,$a=$argv;$a[1]--;$i=$i==count($a)-2?2:$i+1)
  if($a[$i]>$a[$i+1])
    list($a[$i],$a[$i+1])=[$a[$i+1],$a[$i]];
echo substr(join(' ',$a),5);

編集:JörgHülsermannは、破裂するのではなく、参加することを思い出させました。
注:ファイル名は1文字のファイルにする必要があります。



$ t = $ a [$ i]; $ a [$ i] = $ a [$ i + 1]; $ a [$ i + 1] = $ t; list($ a [$ i]、$ a [$ i + 1])= [$ a [$ i + 1]、$ a [$ i]];より短い そして、エコーの代わりにsubstr(implode( ''、$ a)、5); これは$ a [1] = null; echo join( ''、$ a); より良い代替手段です。
ヨルクヒュルサーマン16

1
一時変数の使用は2バイト短くなりますが、複数のステートメントもあるため、これらの2バイトを使用して全体を中括弧で囲む必要があります。$ a [1] = nullの場合、空白とファイル名が先頭に付かないように、実際にunset($ a [0]、$ a [1])する必要があります。
user59178

1

ルビー、52 50バイト

待って...

->l,n{n.times{|a|l[s=a%~-l.size,2]=l[s,2].sort};l}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.