แถวและคอลัมน์ที่เรียงลำดับบล็อกในอาร์เรย์ 2D


15

รับอาร์เรย์ของจำนวนเต็ม 2 มิติลองเรียงแถวและคอลัมน์ในบล็อก ซึ่งหมายความว่าคุณต้องเรียงลำดับแถวหรือคอลัมน์ที่กำหนดเท่านั้น แต่ใช้การแปลงที่จำเป็นสำหรับการจัดเรียงแถวกับทุกแถวหรือคอลัมน์อื่น ๆ ในอาร์เรย์ 2D

กฎระเบียบ

  • อินพุตจะเป็นอาร์เรย์จำนวนเต็ม 2 มิติและจำนวนเต็ม 1 ดัชนี จำนวนเต็มนี้จะแทนแถวที่จะเรียงหากตัวเลขเป็นค่าบวกหรือคอลัมน์ที่จะเรียงหากตัวเลขเป็นค่าลบ (หรือวิธีอื่น ๆ ที่คุณต้องการ) ตัวอย่าง: กำหนด4x3แถว (คอลัมน์ x คอลัมน์) คุณสามารถเรียงลำดับคอลัมน์ที่สองด้วย-2อาร์กิวเมนต์หรือแถวที่สามด้วย3อาร์กิวเมนต์ อาร์กิวเมนต์ที่สองนี้จะไม่เป็นศูนย์และค่าสัมบูรณ์ของมันจะไม่มากกว่ามิติที่สอดคล้องกันของอาร์เรย์
  • เอาต์พุตจะเป็นอาร์เรย์จำนวนเต็มแบบ 2 มิติที่มีการแปลงที่ต้องการเพื่อเรียงลำดับแถวหรือคอลัมน์ที่กำหนด หรือคุณสามารถเขียนอาร์เรย์ไปยัง STDOUT
  • อาร์เรย์เอาต์พุตจะมีแถวหรือคอลัมน์ที่ระบุเรียงตามลำดับจากน้อยไปหามาก เพิ่งทราบว่าเมื่อคุณต้องการสลับตัวเลขสองตัวในหนึ่งแถวคอลัมน์ทั้งหมดที่มีการวางตัวเลขจะถูกสลับ และเมื่อคุณต้องการสลับสองตัวเลขในคอลัมน์แถวทั้งหมดที่มีการวางตัวเลขจะถูกสลับ
  • ในกรณีที่หมายเลขเดียวกันปรากฏขึ้นหลายครั้งในแถว / คอลัมน์ที่จะเรียงลำดับจะมีวิธีแก้ปัญหาที่หลากหลายตามวิธีที่คุณสลับค่าเพียงทำตามกับส่วนที่เหลือของแถว / คอลัมน์ที่จะแลกเปลี่ยน

ตัวอย่าง

Positive indices for rows and negative indices for columns

[5  8  7  6                                  [1  3  2  4
 1  3  2  4   order by -3 (3rd column)  -->   9  6  3  0
 9  6  3  0]                                  5  8  7  6]

[5  8  7  6                                  [9  6  3  0
 1  3  2  4   order by -4 (4th column)  -->   1  3  2  4
 9  6  3  0]                                  5  8  7  6]

[5  8  7  6                                  [5  7  8  6
 1  3  2  4     order by 2 (2nd row)  -->     1  2  3  4
 9  6  3  0]                                  9  3  6  0]

[5  8  7  6                                  [6  7  8  5
 1  3  2  4     order by 3 (3rd row)  -->     4  2  3  1
 9  6  3  0]                                  0  3  6  9]

[1  2                                    [1  2     [3  2
 3  2]   order by -2 (2nd column)  -->    3  2] or  1  2]  (both are valid)

[7  5  9  7                                  [5  7  7  9     [5  7  7  9
 1  3  2  4     order by 1 (1st row)  -->     3  1  4  2  or  3  4  1  2
 9  6  3  0]                                  6  9  0  3]     6  0  9  3]

นี่คือดังนั้นอาจเป็นรหัสที่สั้นที่สุดสำหรับแต่ละภาษาที่ชนะ!


นี้มาจากกล่องเครื่องมือ
Charlie

เราสามารถเปลี่ยนการแทนจำนวนเต็มได้หรือไม่? ติดลบสำหรับแถวและบวกสำหรับคอลัมน์?
Luis felipe De jesus Munoz

1
@LuisfelipeDejesusMunoz ใช่ที่ระบุไว้ในคำถาม
Charlie

แถว / คอลัมน์สามารถมีตัวเลขที่ซ้ำกันได้หรือไม่?
Kevin Cruijssen

@KevinCruijssen ใช่ดูตัวอย่างล่าสุดและจุดสุดท้ายของกฎ
Charlie

คำตอบ:


7

R , 55 ไบต์

function(x,n)`if`(n>0,x[,+x[n,]],x[+x[,-n],])
`+`=order

ลองออนไลน์!

มอบหมาย+โอเปอเรเตอร์อีกครั้ง (จริง ๆ แล้วฟังก์ชันใน R) จะเป็นorderฟังก์ชันซึ่งจะส่งคืนดัชนีของเวกเตอร์จากขนาดเล็กที่สุดไปหามากที่สุด จากนั้นก็เป็นเพียงการจัดการอาเรย์





4

Japt , 18 17 ไบต์

เชิงลบสำหรับแถวและบวกสำหรับคอลัมน์

>0?VñgUÉ:ßUa Vy)y

ลองออนไลน์!


สิ่งนี้จะล้มเหลวเมื่อUเป็นลบ - เวอร์ชัน 17 ไบต์ก่อนหน้านี้ทำงานได้
ขนปุย

@Shaggy ไม่ดีของฉันฉันถึงแม้ว่ามันจะทำงานต่อไปไม่ได้ตรวจสอบเลย
Luis felipe De jesus Munoz

ไม่ได้เป็นความคิดที่ดี แต่ผ่านฟังก์ชั่นเป็นอาร์กิวเมนต์แรกของการที่จะได้รับนำไปใช้กับß Uมันสามารถสร้างปัญหาด้วยการพยายามส่งสตริงตัวอักษร แต่โพสต์ข้อเสนอแนะเพื่อ repo GitHub ต่อไปสำหรับการตรวจสอบต่อไป
ขนปุย

4

05AB1E , 25 24 14 ไบต์

diø}Σ¹Ä<è}¹diø

มหันต์ -10 ไบต์ขอบคุณที่@Emigna

ใช้อินพุตจำนวนเต็มบวกเพื่อเรียงลำดับแถวลบสำหรับคอลัมน์

ลองมันออนไลน์หรือตรวจสอบกรณีทดสอบทั้งหมด

คำอธิบาย:

di }      # If the (implicit) integer input is positive:
  ø       #  Swap the rows and columns of the (implicit) matrix input
          #   i.e. 3 and [[5,8,7,6],[1,3,2,4],[9,6,3,0]]
          #    → [[5,1,9],[8,3,6],[7,2,3],[6,4,0]]
Σ    }    # Sort the rows of this matrix by:
 ¹Ä       #  Take the absolute value of the input
          #   i.e. -3 → 3
   <      #  Decreased by 1 to make it 0-indexed
          #   i.e. 3 → 2
    è     #  And index it into the current row
          #   i.e. [5,8,7,6] and 2 → 7
          #   i.e. [5,1,9] and 2 → 9
          #  i.e. [[5,1,9],[8,3,6],[7,2,3],[6,4,0]] sorted by [9,6,3,0]
          #   → [[6,4,0],[7,2,3],[8,3,6],[5,1,9]]
          #  i.e. [[5,8,7,6],[1,3,2,4],[9,6,3,0]] sorted by [7,2,3]
          #   → [[1,3,2,4],[9,6,3,0],[5,8,7,6]]
¹di       # And if the integer input was positive:
   ø      #  Swap the rows and columns back again now that we've sorted them
          #   i.e. 3 and [[6,4,0],[7,2,3],[8,3,6],[5,1,9]]
          #    → [[6,7,8,5],[4,2,3,1],[0,3,6,9]]
          # (And implicitly output the now sorted matrix)

1
ฉันได้รับdiø}Σ¹Ä<è]¹diøซึ่งเป็นส่วนหนึ่งของคุณดังนั้นฉันไม่ได้โพสต์คำตอบแยกต่างหาก
Emigna

@Emigna Dang คุณทำให้มันดูง่ายมาก .. ตอนนี้ฉันเห็นแล้วฉันไม่อยากเชื่อเลยว่าไม่ได้คิดเกี่ยวกับตัวเอง แต่มันมีความคิดสร้างสรรค์ในเวลาเดียวกัน .. ขอบคุณ! พื้นที่เก็บข้อมูลขนาดใหญ่ถึง 10 ไบต์ขอบคุณคุณ
Kevin Cruijssen

4

JavaScript (ES6), 90 ไบต์

t=m=>m[0].map((_,x)=>m.map(r=>r[x]))
f=(m,k)=>k<0?m.sort((a,b)=>a[~k]-b[~k]):t(f(t(m),-k))

ลองออนไลน์!

อย่างไร?

JS ไม่มีวิธีการขนย้ายแบบเนทีฟดังนั้นเราจำเป็นต้องกำหนดหนึ่งวิธี:

t = m =>              // given a matrix m[]
  m[0].map((_, x) =>  // for each column at position x in m[]:
    m.map(r =>        //   for each row r in m[]:
      r[x]            //     map this cell to r[x]
    )                 //   end of map() over rows
  )                   // end of map() over columns

ฟังก์ชั่นหลัก:

f = (m, k) =>         // given a matrix m[] and an integer k
  k < 0 ?             // if k is negative:
    m.sort((a, b) =>  //   given a pair (a, b) of matrix rows, sort them:
      a[~k] - b[~k]   //     by comparing a[-k - 1] with b[-k - 1]
    )                 //   end of sort
  :                   // else:
    t(f(t(m), -k))    //   transpose m, call f() with -k and transpose the result

k=2

M=(587613249630)เสื้อ(M)=(519836723640)(เสื้อ(M),-2)=(519723836640)(M,2)=เสื้อ((เสื้อ(M),-2))=(578612349360)

3

MATL , 17 ไบต์

y0>XH?!]w|2$XSH?!

ลองออนไลน์!

หรือตรวจสอบกรณีทดสอบทั้งหมด

คำอธิบาย

y       % Implicit inputs: number n, matrix M. Duplicate from below: pushes n, M, n
0>      % Greater than 0?
XH      % Copy into clipboard H
?       % If true
  !     %   Transpose matrix. This way, when we sort the rows it will correspond
        %   to sorting the columns of the original M
]       % End
w       % Swap: moves n to top
|       % Absolute value
2$XS    % Two-input sortrows function: sorts rows by specified column
H       % Push contents from clipboard H
?       % If true
  !     %   Transpose again, to convert rows back to columns
        % Implicit end
        % Implicit display


2

Python 2 , 71 70 ไบต์

f=lambda m,n:n<0and sorted(m,key=lambda l:l[~n])or zip(*f(zip(*m),-n))

ลองออนไลน์!


ถ้า nnเป็นลบแถวที่จะเรียงอยู่บนพื้นฐานของคอลัมน์

มิฉะนั้นเมทริกซ์จะถูกเปลี่ยนตำแหน่งจัดเรียงในลักษณะเดียวกันและกลับมาอีกครั้ง



1

C # (.NET Core) , 186 ไบต์

(x,y)=>{Func<int[][],int[][]>shift=a=> a[0].Select((r,i)=>a.Select(c=>c[i]).ToArray()).ToArray();return y>0?shift(shift(x).OrderBy(e=>e[y-1]).ToArray()):x.OrderBy(e=>e[-y-1]).ToArray();}

ลองออนไลน์!

Ungolfed:

    private static int[][] Blocksort0a(int[][] array, int sortingInstruction)
    {
        Func<int[][], int[][]> shift = a => a[0].Select((r, i) => a.Select(c => c[i]).ToArray()).ToArray();

        sortingInstruction++;

        array = sortingInstruction < 0 ? 
        shift(shift(array).OrderBy(e => e[-sortingInstruction]).ToArray()) 
             : 
        array.OrderBy(e => e[sortingInstruction]).ToArray();

        return null;
    }

ฟังก์ชั่น shift เราจะใช้สองครั้งดังนั้นตัวแปรฟังก์ชันจะประหยัดพื้นที่ ฟังก์ชั่นวนซ้ำผ่านมิติแนวนอนของอาเรย์กับดัชนีและเพิ่มทุกรายการในดัชนีนั้นในแต่ละอาเรย์แนวนอนไปยังอาร์เรย์เอาต์พุตใหม่ (แนวนอน) - เหมือนกับในโซลูชัน JS ของ Arnoud

ตอนนี้การสั่งซื้อนั้นง่ายการเรียงลำดับของอาร์เรย์ในแนวนอนด้วยตัวเลข - ดัชนี (อาร์กิวเมนต์ -1) เลือกที่จะเลื่อนอาร์เรย์ก่อนและหลังการเรียงลำดับ

เห็นว่าคำถามพูดถึงอาร์เรย์อย่างไรโดยเฉพาะเราเปลี่ยนเป็นอาร์เรย์สองสามครั้ง (มากสิ้นเปลืองมาก) รู้สึกเซ่อที่จะใช้ภาษา verbose ในการเล่นกอล์ฟฮิเฮ


1

C # (.NET Core) , 142/139 138/135 ไบต์ (และอีก -1 โดย Kevin)

(a,s)=>s<0?a.OrderBy(e=>e[~s]).ToArray():a.Select(f=>a[s-1].Select((v,j)=>new{v,j}).OrderBy(e=>e.v).Select(e=>f[e.j]).ToArray()).ToArray()

ลองออนไลน์!

Ungolfed:

    private static int[][] Blocksort0b(int[][] array, int sortingInstruction)
    {
        if (sortingInstruction < 0) { return array.OrderBy(e => e[-sortingInstruction - 1]).ToArray(); }
        var rowIndices = array[sortingInstruction - 1].Select((value, index) => (value, index)).OrderBy(e => e.value);
        var newRow = new int[array[0].Length];
        for (var i = 0; i < array.Length; i++)
        {
            int horizontalIndexer = 0;
            foreach (var e in rowIndices)
            {
                newRow[horizontalIndexer++] = array[i][e.index];
            }
            array[i] = newRow.ToArray();
        }
        return array;
    }

ใหม่วิธีการแบบอินไลน์ทั้งหมด; คำตอบเชิงลบยังคงคำสั่งอาร์เรย์โดยองค์ประกอบ -at-index มิฉะนั้นคอลเลกชันของคู่ค่าดัชนีถูกสร้างขึ้นของอาร์เรย์ที่ดัชนีและเรียงตามค่า สิ่งนี้จะสร้างคอลเลกชันดัชนีอย่างมีประสิทธิภาพเพื่อที่จะเพิ่มเข้าไป จากนั้นสำหรับแต่ละอาร์เรย์องค์ประกอบในตำแหน่งที่กำหนดไว้จะถูกเลือก ค่อนข้างมีการตัดรหัสและน่าเกลียด, น่าเกลียด, น่าเกลียด ** เงียบ ๆ ** การใช้ซ้ำของพารามิเตอร์การป้อนข้อมูลที่เกี่ยวข้องและมีคุณไป ... 142 ไบต์

อีกครั้งอาร์กิวเมนต์ของอาร์เรย์ถูกบังคับใช้อย่างเข้มงวดโดยเพิ่มค่าใช้จ่ายสำหรับการโทร. ToArray () ค่อนข้างมาก

อ้างสิทธิ์ 135 ไบต์ใช่มั้ย! Tuples ที่อนุมานค่า C # 7.2 จะตัดเพิ่มอีกสามไบต์ แต่ tio.run ไม่อนุญาต นี่คือคำตอบที่ฉันตัดสินใจโพสต์เพื่อการตรวจสอบที่ง่าย


1
คำตอบที่ดี มีสิ่งเล็ก ๆ น้อย ๆ ในการเล่นกอล์ฟ (a,s)=>สามารถ a=>s=>currying (s<0)?ไม่จำเป็นต้องวงเล็บและสามารถ-s-1 ลองออนไลน์: 137 ไบต์~s
เควินครุยส์เซน

หวาน! ฉันไม่เคยปล่อยให้ฟังก์ชั่นกลับมาอีกแล้ว แต่ฟังก์ชั่นอื่นเพื่อบันทึกตัวละคร ขอบคุณ! ยังเป็นกรณีที่แข็งแกร่งของโจ๋งครึ่มมองเห็นผู้ประกอบการไม่ได้และวงเล็บ ฉันอัปเดตไม่ใช่และวงเล็บ แต่จะทำให้คุณมีเกียรติทั้งหมดสำหรับ function-return-function
Barodus

1

Java (OpenJDK 8) , 326 ไบต์

(a,b)->{int l=a.length,w=a[0].length,k,m,t,i;if(b>0){for(i=0;i<w;i++){for(k=1;k<(w-i);k++){if(a[b-1][k-1]>a[b-1][k]){for(m=0;m<l;m++){t=a[m][k];a[m][k]=a[m][k-1];a[m][k-1]=t;}}}}}else{b*=-1;for(i=0;i<l;i++){for(k=1;k<(l-i);k++){if(a[k-1][b-1]>a[k][b-1]){for(m=0;m<w;m++){t=a[k][m];a[k][m]=a[k-1][m];a[k-1][m]=t;}}}}}return a;}

ลองออนไลน์!

คำถามนี้น่าผิดหวังมากสำหรับฉันและฉันโพสต์คำตอบของฉันรู้ว่าฉันลืมบางสิ่งโชคดีที่เรามีตำนานอย่างKevin Cruijssenที่นี่เพื่อช่วยเรา :)

Java (OpenJDK 8) , 281 ไบต์

a->b->{int l=a.length,w=a[0].length,k,m,t,i;if(b>0)for(i=0;i<w;i++)for(k=0;++k<w-i;)for(m=0;a[b-1][k-1]>a[b-1][k]&m<l;a[m][k]=a[m][k-1],a[m++][k-1]=t)t=a[m][k];else for(b*=-1,i=0;i<l;i++)for(k=0;++k<l-i;)for(m=0;a[k-1][b-1]>a[k][b-1]&m<w;a[k][m]=a[k-1][m],a[k-1][m++]=t)t=a[k][m];}

ลองออนไลน์!


ฉันยังไม่ได้ดูอัลกอริธึมที่แท้จริง แต่คุณสามารถบันทึกได้ 35 ไบต์โดยการลบวงเล็บทั้งหมดและใส่ทุกอย่างไว้ในลูป (รวมถึงคำสั่ง if ด้านใน) ลองออนไลน์: 291 ไบต์แก้ไข: ที่นี่พร้อมการเว้นวรรคเพื่อให้คุณสามารถเห็นการเปลี่ยนแปลงที่ฉันทำได้ชัดเจนยิ่งขึ้น
Kevin Cruijssen

@KevinCruijssen ฉันรู้ว่าฉันพลาดบางสิ่งบางอย่าง
X1M4L

นอกจากนี้คุณสามารถทำให้มันเป็นอินพุต currying a->b->แทน(a,b)->และลบreturn-statement เนื่องจากคุณกำลังปรับเปลี่ยนอาร์เรย์อินพุต ถึง 281 ไบต์ยังคงเป็นคำตอบที่ดี +1 จากฉัน ฉันทำสิ่งที่ท้าทายใน 05AB1E แต่ในครั้งนี้จะไม่ลอง Java เลย ;)
Kevin Cruijssen





1

สีแดง , 190 185 ไบต์

func[b n][t: func[a][c: length? a/1 a: to[]form a
d: copy[]loop c[append/only d extract a c take a]d]d: does[if n > 0[b: t b]]d
m: absolute n sort/compare b func[x y][x/(m) < y/(m)]d b]

ลองออนไลน์!

คำอธิบาย:

f: func [ b n ] [
    t: func [ a ] [                            ; helper transpose function 
        c: length? a/1                         ; c is the length of the rows
        a: to-block form a                     ; flatten the list
        d: copy []                             ; an empty block (list)
        loop c [                               ; do as many times as the number of columns  
            append/only d extract a c          ; extract each c-th element (an entire column)
                                               ; and append it as a sublist to d
            take a                             ; drop the first element
        ] 
        d                                      ; return the transposed block (list of lists)
    ]
   d: does [ if n > 0 [ b: t b ] ]             ; a helper function (parameterless) to transpose 
                                               ; the array if positive n
   d                                           ; call the function  
   m: absolute n                               ; absolute n
   sort/compare b func[ x y ] [ x/(m) < y/(m) ]; sort the array according to the chosen column 
   d                                           ; transpose if positive n
   b                                           ; return the array  
]

โซลูชันที่แท้จริงของฉันมีความยาว 175 ไบต์ แต่ไม่สามารถใช้งานได้ใน TIO นี่มันใช้งาน normalyl ใน Red console:

สีแดง 175 ไบต์

func[b n][d: does[if n > 0[c: length? b/1 a: to-block form b
t: copy[]loop c[append/only t extract a c take a]b: t]]d
m: absolute n sort/compare b func[x y][x/(m) < y/(m)]d b]

0

VBA (Excel), 205 ไบต์

เย้! ไบต์ที่ยาวที่สุดเป็นอันดับ 2! ฉันไม่แพ้ทั้งหมด: D

แข็งแรงเล่นกอล์ฟ:

Sub d(a)
With ActiveSheet.Sort
  .SortFields.Clear
  .SortFields.Add Key:=IIf(a<0,ActiveSheet.Columns(Abs(a)),ActiveSheet.Rows(Abs(a)))
  .SetRange ActiveSheet.UsedRange
  .Orientation=IIf(a<0,1,2)
  .Apply
End With
End Sub

นี่จะเรียงลำดับข้อมูลทั้งหมดบนแผ่นงานเปิด (ใช้งานอยู่) โดยใช้ UsedRange ... ซึ่งสามารถเป็นรถได้ แต่ควรมีเซลล์ที่ได้รับการแก้ไขเท่านั้น

UnGolfed:

Sub d(a)
  'Clear any Sort preferences that already exists
  ActiveSheet.Sort.SortFields.Clear
  'Use the column if A is negative, the row if A is positive
  ActiveSheet.Sort.SortFields.Add Key:=IIf(a < 0, ActiveSheet.Columns(Abs(a)), ActiveSheet.Rows(Abs(a)))
  'Set the area to sort
  ActiveSheet.Sort.SetRange ActiveSheet.UsedRange
  'Orient sideways if sorting by row, vertical if by column
  ActiveSheet.Sort.Orientation = IIf(a < 0, xlTopToBottom, xlLeftToRight)
  'Actually sort it now
  ActiveSheet.Sort.Apply
End Sub

หากคุณสมมติว่าแผ่นกิจกรรมเป็นแผ่น 1 คุณสามารถลดขนาดได้ถึง 169 ไบต์เช่นSub d(a) With Sheet1.Sort .SortFields.Clear .SortFields.Add IIf(a<0,Columns(Abs(a)),Rows(Abs(a))) .SetRange Sheet1.UsedRange .Orientation=(a<0)+2 .Apply End With End Sub
Taylor Scott

นอกจากนี้ฉันคิดว่าคุณสามารถสรุปได้อย่างปลอดภัยว่าไม่มีการ.SortFieldsกำหนดเพื่อให้คุณสามารถลบ.Sortfields.Clearบรรทัดได้เช่นกัน
Taylor Scott

0

Perl 6 , 43 ไบต์

{($!=$_>0??&[Z]!!*[])o*.sort(*[.abs-1])o$!}

ลองออนไลน์!

ฟังก์ชั่น curried

คำอธิบาย

{                                         } # Block returning function composed of
                                       o$!  # 1. Apply $! (transpose or not)
                     o*.sort(*[.abs-1])     # 2. Sort rows by column abs(i)-1
     $_>0??&[Z]                             # 3. If i > 0 transpose matrix
               !!*[]                        #    Else identity function
 ($!=               )                       #    Store in $!

0

Physicaขนาด 45 ไบต์

คล้ายกับคำตอบ JS ของ Arnauldมาก

F=>n;m:n<0&&Sort[->u:u{~n};m]||Zip@F#Zip@m#-n

ลองออนไลน์!

มันทำงานอย่างไร?

คำอธิบายที่ละเอียดและชัดเจนยิ่งขึ้นสามารถพบได้ในคำตอบที่เชื่อมโยง

F=>n;m:           // Create a function F that takes two arguments, n and m.
       n<0&&      // If n < 0 (i.e. is negative)
Sort[->u{~n};m]   // Sort the rows u of m by the result of the function u[~n].
                  // In short, sort by indexing from the end with n.
||    F#Zip@m#-n  // Else, apply F to Zip[m] and -n. Uses a new feature, binding.
  Zip@            // And transpose the result.


โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.