Tiêu đề thân thiện với URL


28

Mọi người trên trang này thực sự thích tô điểm tiêu đề bài viết của họ ...

Stewie's sequence: + * - / + * - /

Tuy nhiên, khi tiêu đề này cần được đưa vào URL trang, nó được đơn giản hóa:

stewies-sequence

Các thách thức

Nhiệm vụ của bạn là tạo một chương trình hoặc chức năng, được cung cấp một chuỗi đại diện cho tiêu đề bài đăng, xuất / trả về chuyển đổi "Thân thiện với URL" của nó.

Thuật toán là:

  • Chuyển đổi thành chữ thường (nếu có)
  • Thay thế mọi dấu cách ( ), period ( .), dấu phẩy ( ,) hoặc dấu gạch chéo ( /) bằng dấu gạch ngang ( -)
  • Xóa mọi ký tự không chữ và số, ngoại trừ dấu gạch ngang.
  • Giảm thiểu các nhóm dấu gạch ngang liền kề ( a---b -> a-b), loại bỏ bất kỳ dấu gạch đầu dòng / dấu nào.

Xin lưu ý rằng thuật toán này là một sự đơn giản hóa và có thể không phải lúc nào cũng tạo ra kết quả giống như phương pháp thực sự của trang web.


Quy tắc

  • Bạn có thể giả sử rằng đầu vào:
    • Sẽ không trống rỗng.
    • Sẽ chứa ít nhất một ký tự chữ và số.
    • Sẽ chỉ chứa các ký tự trong phạm vi ASCII 32-126 (có thể in)
  • Chương trình đầy đủ hoặc chức năng được cho phép.
  • Một nội dung mà đặc tả nhiệm vụ chính xác không được phép.
  • Đây là , vì vậy giải pháp ngắn nhất (tính bằng byte) sẽ thắng!

Các trường hợp thử nghiệm

Hầu hết các bài đăng trên trang web này sẽ đóng vai trò kiểm tra, nhưng đây là một danh sách tiện dụng:

Loading... Forever       -> loading-forever
N(e(s(t))) a string      -> nest-a-string
"Hello, World!"          -> hello-world
URL-Friendly titles      -> url-friendly-titles

C.U.S.R.S                -> c-u-s-r-s
1+2+3+4+...+n = -1/12?   -> 1234-n-1-12
How can I use cmp(a,b)   -> how-can-i-use-cmpa-b

Một số cái dài hơn ...

Export The $PATH Variable, Line-By-Line   -> export-the-path-variable-line-by-line
Do n and n^3 have the same set of digits? -> do-n-and-n3-have-the-same-set-of-digits
Quine Anagrams! (Cops' Thread)            -> quine-anagrams-cops-thread
The Golfer Adventure - Chapter 1          -> the-golfer-adventure-chapter-1
Bootloader golf: Brainf***                -> bootloader-golf-brainf

Và một số mẫu kiểm tra trường hợp cạnh (vui lòng đề xuất thêm):

0123   ->   0123
a a1   ->   a-a1
2-1=1  ->   2-11

Những gì về hàng đầu -? Họ sẽ phải được gỡ bỏ? Ví dụ trong asdf-, cuối cùng -sẽ phải được gỡ bỏ?
Kritixi Lithos

Chúng ta có thể sử dụng chức năng tích hợp để kiểm tra xem char có phải là chữ và số như thế này khôngif(isalphanum(ch))...
Mukul Kumar

1
@KritixiLithos Giảm thiểu các nhóm dấu gạch ngang liền kề (a --- b -> ab), xóa bất kỳ dấu đầu dòng / dấu nào. Tôi đoán điều này sẽ làm cho bạn rõ ràng.
Mukul Kumar

Và những gì về _dấu gạch dưới? Mã của tôi hoạt động trừ khi có dấu gạch dưới.
Kritixi Lithos

@ L3viathan Bây giờ không thành vấn đề, tôi đã thay đổi mã của mình để thậm chí dấu gạch dưới sẽ bị xóa
Kritixi Lithos

Câu trả lời:


7

Võng mạc, 33 31 byte

T`L`l
[^a-z ,-9]+

\W+
-
^-|-$

(Chương trình có một dòng mới)

Tôi không chắc mình có thể vắt kiệt hơn thế này. Điều này sẽ bao gồm tất cả mọi thứ. Đã đến tương tự như Mama Fun Roll. Một phiên bản 33 byte khác sử dụng biểu thức đệ quy

Hãy thử trực tuyến!

Giải trình

T`L`l

Dòng này đơn giản, nó chuyển đổi thành chữ thường bằng cách T ransliterating A-Z( L) thành a-z( l, chữ thường).


Giai đoạn này rất đơn giản, về cơ bản nó sẽ loại bỏ tất cả các nhân vật không cần thiết để tự cứu mình rất nhiều rắc rối về sau

[^a-z ,-9]+

[^a-z ,-9] Phù hợp với bất kỳ nhân vật nào KHÔNG:

  • a-z: bảng chữ cái chữ thường (nhớ toàn bộ chuỗi là chữ thường vì mục trước)
  • : không gian chacacter
  • ,-9đây là phạm vi đang char của ,để 9mà sẽ xảy ra là ,-./0123456789, chính xác các ký chúng ta cần

Tiếp theo, chúng tôi chuyển đổi tất cả các ký tự không chữ và số thành dấu gạch ngang (mà bây giờ chỉ là ,./-.

\W+
-

Điều này sẽ không (không) phù hợp _được bao gồm trong \w(phủ định \W) bởi vì nó đã bị xóa trong giai đoạn trước


Tôi nghĩ rằng điều này sẽ thất bại cho đầu vào như a = b.
Martin Ender

Tôi thực sự muốn chấp nhận điều này, nhưng như martin đã nói, nó không a = b
giảm bớt

@ Flp.Tkc xin lỗi vì phản hồi muộn (Chung kết tuần ngay bây giờ). Tôi đã quản lý để vắt kiệt thêm hai byte sửa nó. Tôi tin rằng điều này xử lý chính xác các trường hợp như vậy ngay bây giờ
Downgoat

9

JavaScript (ES6), 90 82 79 75 byte

Đây là một nỗ lực để thực hiện công việc với một lần duy nhất replace(). Mã này chỉ trích xuất các ký tự mà chúng ta quan tâm và bỏ qua mọi thứ khác. Có một số logic bổ sung để xử lý các dấu gạch nối.

s=>(s.toLowerCase().replace(/[ a-z,-9]/g,c=>S=c<'0'?s+'-':s=s?S+c:c,s=0),s)

Các trường hợp thử nghiệm


1
Đối với ,a^a,, mã này cung cấp -aa-(có dấu gạch nối hàng đầu / dấu)
Kritixi Lithos

@KritixiLithos ơi, cảm ơn vì đã chỉ ra điều này. Tôi đã không chú ý đến quy tắc đó. Điều đó nên được sửa chữa.
Arnauld

9

V , 41, 40, 37 , 36 byte

VuÍ[ .,\/]/-
Í0-9a-z­]
Í-«/-
Í^-ü-$

Hãy thử trực tuyến! hoặc Kiểm tra tất cả các trường hợp thử nghiệm cùng một lúc!

Như thường lệ, ở đây có chứa một loạt các ký tự không thể in được và không phải ASCII, vì vậy đây là một hexdump:

0000000: 5675 cd5b 202e 2c5c 2f5d 2f2d 0acd 8430  Vu.[ .,\/]/-...0
0000010: 2d39 612d 7aad 5d0a cd2d ab2f 2d0a cd5e  -9a-z.]..-./-..^
0000020: 2dfc 2d24                                -.-$

Đó là những thách thức như thế này khi hệ thống "Regex regex" của V có ích.

Giải trình

Đầu tiên, chúng tôi sẽ chuyển đổi mọi thứ thành chữ thường. May mắn thay, có một cách thực sự thuận tiện để làm điều này trong hai byte. Tôi đã viết một mẹo về điều đó ở đây . Vì vậy chúng tôi làm

V           " Visually select this whole line
 u          " Convert this whole line to lowercase

Sau đó chúng tôi thực hiện một loạt các lệnh thay thế nén. Một tổng quan đẹp về cách hoạt động của regex nén của V có thể được phát ra ở đây , nhưng ý tưởng cơ bản là chúng ta có thể đặt bit cao để tránh phải thoát một số ký tự nhất định. Một tiện lợi khác là phạm vi (như :%) và cờ (như /g) được điền tự động. Nhưng cuối cùng, tất cả đều chuyển thành các lệnh thay thế vim. Trong thực tế, chúng tôi thậm chí có thể dịch trực tiếp phần còn lại của chương trình sang vim. Điều đó sẽ cho chúng ta điều này:

:%s/[ .,/]/-/g
:%s/[^0-9a-z\-]//g
:%s/-\+/-
:%s/^-\|-$//g

Nếu bạn nói vim-regex, thì rõ ràng phần còn lại của chương trình làm gì bây giờ. Vì vậy, đây là phần còn lại của chương trình:

Í               " Substitute:
 [ .,\/]        "   a space, period, comma or forward slash. (Due to a strange bug, this needs to be escaped)
        /-      "   with a dash
Í               " Remove:
 [^0-9a-z­]     "   Any character that is not a dash or alpha-numeric
Í               " Substitute:
 -«             "   One or more dashes
   /-           "   with one dash
Í               " Remove:
 ^-             "   A dash at the beginning of a line
   ü            "   OR
    -$          "   a dash at the end of a line

8

JavaScript (ES6) 91 96

Đã lưu 1 byte thx @ETHproductions

s=>s.toLowerCase().replace(/([ .,/-])|\W|_/g,(c,d)=>d?'-':'').replace(/^-*|-*$|-(?=-)/g,'')

Kiểm tra

F=
s=>s.toLowerCase().replace(/([ .,/-])|\W|_/g,(c,d)=>d?'-':'').replace(/^-*|-*$|-(?=-)/g,'')

;[['Loading... Forever.....', 'loading-forever'],
['N(e(s(t))) a string', 'nest-a-string'],
['"Hello, World!"', 'hello-world'],
['URL-Friendly titles', 'url-friendly-titles'],
['C.U.S.R.S','c-u-s-r-s'],
['1+2+3+4+...+n = -1/12?', '1234-n-1-12'],
['How can I use cmp(a,b)', 'how-can-i-use-cmpa-b'],
['Export The $PATH Variable, Line-By-Line', 'export-the-path-variable-line-by-line'],
['Do n and n^3 have the same set of digits?', 'do-n-and-n3-have-the-same-set-of-digits'],
['Quine Anagrams! (Cops\' Thread)', 'quine-anagrams-cops-thread'],
['The Golfer Adventure - Chapter 1', 'the-golfer-adventure-chapter-1'],
['Bootloader golf: Brainf***', 'bootloader-golf-brainf'],
['0123', '0123'],
['a a1', 'a-a1'],
['2-1=1', '2-11']]
.forEach(t=>{
  var i=t[0],k=t[1],r=F(i)
  console.log(r==k?'OK':'KO',i+' -> '+r,r==k?'':k)
})


Điều này có chính xác tương tự như câu trả lời của tôi nếu nó được chuyển đổi thành hàm được đặt tên
Kritixi Lithos

Đừng nghĩ rằng bạn cần cuối cùng *trong regex cuối cùng, mặc dù tôi có thể sai
ETHproductions

Tôi có thể nhầm, nhưng bạn có chắc cái nhìn là cần thiết?
Kritixi Lithos

@KritixiLithos cái nhìn là cần thiết để giữ ít nhất 1 - bên trong chuỗi, trong khi loại bỏ tất cả khi bắt đầu và kết thúc
edc65

@ETHproductions đúng, cảm ơn
edc65

4

Python 3, 103 100 96 95 byte

5 byte được lưu nhờ Flp.Tkc

import re
lambda s,y=re.sub,d='-':y('-+',d,y('[^0-9a-z-]','',y('[ .,/]',d,s.lower()))).strip(d)

@ Flp.Tkc Thật vậy ..
L3viathan

Rất tiếc, tôi vô tình hạ cấp này. Tôi không thể đảo ngược phiếu bầu của mình cho đến khi bạn chỉnh sửa bài đăng này
Kritixi Lithos

@KritixiLithos Xong
L3viathan


3

MATL , 38 byte

'-'jyvk45y' .,/'m(t8Y245hm)'-*'45YX6L)

Hãy thử trực tuyến! Hoặc xác minh tất cả các trường hợp thử nghiệm .

Giải trình

'-'jyv       % Take input line. Append and prepend a dash. Gives a char column vector
k            % Convert to lowercase
45y' .,/'m(  % Replace any of ' .,/' by a dash, using assignment indexing
t8Y245hm)    % Keep only alphanumeric chars or dashes, using reference indexing
'-*'45YX     % Replace each run of dashes by a single dash, using a regular expression
6L)          % Remove first and last chars, which are always dashes. Implicitly display

3

Ruby , 61 60 61 64 53 byte

(52 byte mã cộng với một byte cho -p)

$_=$_.tr("A-Z ,-/","a-z ").gsub(/[^\w ]/){}.split*?-

Hãy thử trực tuyến!

tr(Tiết kiệm )- chuyển đổi ký tự chữ hoa, dấu cách, dấu phẩy, dấu chấm và dấu gạch chéo. Tạm thời thay thế -bằng khoảng trắng để tôi có thể sử dụng stripsau này.
Lưu ý rằng -ký tự trong "A-Z ,-/"biểu thức thực sự là một toán tử phạm vi, điều này cũng làm cho .đối tượng được chuyển đổi. Thao tác này sẽ không thực sự loại bỏ byte nhưng nó rất lạ mắt để nó được ở lại.

gsub(/[^\w ]/){} - loại bỏ tất cả các ký tự không có trong bộ cho phép.

split- về mặt kỹ thuật, chúng tôi không thực sự cần mảng đó nhưng splitloại bỏ khoảng trắng hàng đầu và dấu (thực sự là các -ký tự được ngụy trang). Như một phần thưởng, điều này siết chặt với nhau chạy nhiều không gian.

*?-- Viết tắt cho .join("-"); điều này đảo ngược cả splithoạt động trước chuyển đổi khoảng trắng cùng một lúc. Thêm một byte được lưu bằng cách sử dụng ký hiệu viết tắt cho các ký tự , điều này làm cho chương trình yêu cầu Ruby 1.9 hoặc mới hơn.

Cập nhật 1: Sử dụng getsthay vì chế độ chỉnh sửa luồng của Ruby tiết kiệm một byte.
Hoàn nguyên theo đề nghị của ValueInk .

Cập nhật 2: (+3 byte tổng thể)

  • Đã sửa trường hợp cạnh ..--hi, $/(→ hi) (+10 byte) - một lần nữa nhờ sự giúp đỡ của người dùng ValueInk
  • Lấy malus cho -p (+1 byte)
  • Đã loại bỏ squeezevà sử dụng gsubthay thế (+2 byte) , cho phép tôi:
  • Sử dụng stripđể xử lý dấu gạch ngang hàng đầu và dấu (-10 byte) .

Cập nhật 3: Hattrick của ValueInk. Chúng tôi tiết kiệm 11 byte bằng cách tận dụng String#splitthói quen tự động nén của cùng một dải phân cách, cho phép chúng tôi bỏ toàn bộ chuỗi strip/ gsubchuỗi cuối cùng và thay thế nó bằng split/ joincombo. (-11 byte)


Điều này chỉ trả về chuỗi trong môi trường REPL và không thành công nếu chạy như một chương trình Ruby thích hợp và điều đó không tốt. Chương trình đầy đủ hoặc chức năng / lambdas chỉ. Trên thực tế, phiên bản cũ của bạn sẽ hoạt động với -pcờ, nhưng điều này chắc chắn sẽ không xảy ra.
Mực giá trị

@ValueInk Tất nhiên bạn đúng. Tôi đã thay đổi giải pháp của mình cho phù hợp. Cám ơn bạn đã góp ý; đó chính xác là hướng dẫn mà tôi đánh giá cao vì đây là nỗ lực đầu tiên của tôi khi chơi golf.
Synoli

1
Cảm ơn bạn đã sửa chữa; Tôi đã loại bỏ downvote của tôi. Một điều cần lưu ý là việc sử dụng -pcờ sẽ thêm 1 byte vào mã của bạn (bởi vì nó thay đổi việc thực thi mã của bạn từ ruby -e 'your code'thành ruby -pe 'your code'). Tôi cũng đã tìm thấy một trường hợp cạnh trong đó nó cung cấp -hi-cho đầu vào như ..--hi, $/khi bạn nên loại bỏ tất cả các dấu gạch đầu dòng / dấu và do đó sẽ trở lại hi.
Mực giá trị

2
-2 byte bằng cách thay đổi gsub(/[^\w ]/){}thành tr('^a-z ',''), và sau đó kết thúc bằng .split*?-thay .strip.gsub...vì nó tự động xử lý các bản sao các đầu của chuỗi, tất cả trong một lần!
Mực giá trị

1
Vì không ai nói điều đó, chào mừng bạn đến với môn đánh gôn!
FlipTack

3

JavaScript (ES6), 74 69 byte

f=
s=>s.toLowerCase().replace(/[^-/,. a-z\d]/g,``).match(/\w+/g).join`-`
<input oninput=o.textContent=/[a-z\d]/i.test(this.value)?f(this.value):``><pre id=o>

Chỉnh sửa: Đã lưu 5 byte bằng cách nhận ra rằng tôi đã xóa tất cả các ký tự ngoại trừ -/,. 0-9a-zđể tôi có thể sử dụng \wđể khớp với các từ còn lại.


Tôi nghĩ rằng bạn phải đưa mã HTML vào mã byte vì nó đang được sử dụng để giải quyết thách thức
Kritixi Lithos

1
@KritixiLithos Không, nó chỉ ở đó cho mục đích trình diễn. Câu hỏi nói rằng mã của tôi có thể giả sử ít nhất một ký tự chữ và số và mã HTML chỉ đơn giản kiểm tra điều này trước khi gọi hàm.
Neil

[a-z\d]có thể là [^\W_]gì?
edc65

@ edc65 Đẹp, nhưng sau đó tôi nhận ra rằng nó có thể còn đơn giản hơn nữa!
Neil

2

PHP, 87 byte

Ý tưởng của các biểu thức thông thường đến từ các câu trả lời hiện có.

<?=trim(preg_replace(['@[^ a-z,-9]@','@[ ,-/]+@'],['','-'],strtolower($_GET[T])),'-');

Nó yêu cầu bạn phải có máy chủ chạy PHP và truy cập qua HTTP.

Tiêu đề phải nằm trên phím Tvà kết quả sẽ được in trên màn hình.

Thí dụ: http://localhost/title.php?T=<my shiny title>


2

công cụ bash / Unix, 56 byte

tr A-Z\ .,/ a-z-|tr -cds a-z0-9- -|sed s/^-//|sed s/-$//

Thay thế chữ hoa bằng chữ thường và các ký tự đặc biệt cần thiết bằng dấu gạch ngang.

Xóa (tùy chọn -d để tr) các ký tự không phải là chữ cái, chữ số và dấu gạch ngang, sau đó ép (tùy chọn -s để tr) nhiều dấu gạch ngang trong một hàng thành một dấu gạch ngang.

Xóa dấu gạch ngang ở đầu, và sau đó ở cuối.


2

Powershell, 85 byte

($args[0].ToLower()-replace'[ .,/]','-'-replace'[^a-z,-9]'-replace'-+','-').Trim('-')

làm cho nó thành chữ thường, sau đó 3 Thay thế regex trong một hàng, và cắt mọi dấu -'s


không thể $inputtiết kiệm cho bạn 2 byte?
nghĩa tự do

2

JavaScript, 90 98 94 93 91 90 91 byte

Lưu 1 byte nhờ @ edc65!

Lưu 1 byte nhờ @IsmaelMiguel vì đã phát hiện ra một dấu chấm phẩy hàng đầu!

1 byte đạt được sau khi thất bại cho ,a-^-a,

f=s=>s.toLowerCase().replace(/[^ a-z,-9]/g,"").replace(/[ ,-/]+/g,"-").replace(/^-|-$/g,"")

Điều tôi thích nhất về bài nộp đặc biệt này là các phạm vi. Trong lần đầu tiên replace, chúng tôi loại bỏ bất cứ điều gì đó không phải là chữ số và không phải là một ,, -, ., /và không phải là một không gian. Chúng tôi sử dụng a-zđể phát hiện các chữ cái và chúng tôi sử dụng ,-9để phát hiện các ký tự số đặc biệt đó vì mã ký tự của các chữ ASCII này đều được xếp thành hàng!

, = 44
- = 45
. = 46
/ = 47
0 = 48
...
9 = 57


Không xóa dấu gạch ngang hàng đầu: "-1" trở thành "-1", khi nó sẽ trở thành "1".
L3viathan

@ L3viathan Nên làm việc ngay bây giờ
Kritixi Lithos

Không cần phải đếm để số f=byte của bạn là 96 ngay bây giờ. Và không cần \ bên trong một phạm vi trong biểu thức chính quy, vì vậy nó có thể là 95. Nhưng ... vẫn không hoạt động: thử...title
edc65

1
Hei! Tôi không tuổi! (65 chứ không phải 64)
edc65

1
Tôi tin rằng bạn không cần f=;cuối cùng. Chỉ cần xác định rằng đây là một chức năng ẩn danh. Với điều này, câu trả lời của bạn phải dài 90 byte.
Ismael Miguel

1

Lua, 91 byte

a=a:lower():gsub( '[ .,/]', '-' ):gsub( '[^%w-]', '' ):gsub( '%-+', '-' ):match'%-?(.*)%-?'

Trong trường hợp alà chuỗi URL.

Giải trình:

  • Hầu hết nó khá thẳng về phía trước. a:lower()trả về hàm chữ thường
  • :gsub tìm thấy sự phù hợp của mẫu và thay thế nó bằng chuỗi.
  • '[ .,/]': Chân đế có nghĩa là "hoặc", vì vậy, giá trị này khớp với không gian, dấu chấm, dấu phẩy và dấu gạch chéo. Không cần phải tham lam vì :gsubtất cả xảy ra.
  • '[^%w-]': ^có nghĩa là "không" khi bên trong ngoặc, %wcó nghĩa là bất cứ thứ gì cả chữ và số. Vì vậy, '[^%w-]phù hợp với bất cứ điều gì không phải là chữ và số.
  • '%-+': Ghép càng nhiều dấu gạch ngang càng tốt và thay thế chúng chỉ bằng một dấu gạch ngang.
  • match'%-?(.*)%-?': Trong Lua, nếu một chuỗi là đối số duy nhất của hàm thì không cần dấu ngoặc đơn. Chỉ cần kiểm tra một dấu gạch ngang ở đầu và cuối vì dấu gạch ngang đã được thu nhỏ. Không cần nhân vật neo vì .*phù hợp với mọi thứ, tham lam.

1

C, 194 byte

i,j;f(char*s,char*d){if(*s>47&*s<58|*s>96&*s<123)d[i++]=*s;if(*s>64&*s<91)d[i++]=*s+32;if(i-j&&*s>43&*s<48|*s==32&&*(s+1)&&*(s+1)>47|(*(s+1)<44&&*(s+1)^32)){d[i++]=45;j=i;}*++s?f(s,d):(d[i]=0);}

Gọi với:

int main()
{
    char *in="Loading... Forever";
    char out[128];
    f(in,out);
    puts(out);
}

1

SÀI GÒN, 108

Một trong những câu trả lời ít cạnh tranh hơn ở đây do cú pháp dài dòng của SAS - hình phạt 9 ký tự cho mỗi regex thực sự gây tổn thương - nhưng đó là một bài tập học regex tốt:

t=prxchange('s/^-|-$//',-1,prxchange('s/-+/-/',-1,compress(translate(lowcase(t),'----',' .,/'),'-','adk')));

1

Bình thường, 35 byte

:r::rQ0"[-.,/]"d"[^\w ]"k6"[ -]+"\-

Giải trình

    rQ0                              Convert letters to lower case
   :   "[-.,/]"d                     Replace all -.,/ with spaces
  :             "[^\w ]"k            Remove all remaining symbols
 r                       6           Remove leading and trailing spaces
:                         "[ -]+"\-  Turn runs of spaces and dashes to one dash

1

Perl 6, 75

{lc .subst(/<[\ .,/]>/,"-"):g.subst(/<[\W]-[\-]>/,""):g.subst(/\-+/,"-"):g}

0

GNU Sed, 65 byte

s/.*/\L\0/
s@[ .,/]@-@g
s/[^-a-z0-9]//g
s/-\+/-/g
s/^-\|-$//g

Một loạt các thay thế regex. Sử dụng không di động \Ltừ GNU sed để viết thường đầu vào. Chạy từ một tập tin bằng cách sử dụng sed -f.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.