Найди закрученные слова!


41

Не спрашивайте меня, как и почему, но во время написания кода для проекта я заметил, что символы пары слов имеют определенный шаблон, относящийся к алфавиту, я связал каждый символ слова карандашом на алфавите и получил два затем я заметил, что первая спираль была по часовой стрелке, а другая - против часовой стрелки, и другие характеристики ... поэтому я назвал их закрученными словами !

Вихревой Слово может быть:

  1. по часовой стрелке или против часовой стрелки
  2. центростремительный или центробежный

Вот несколько примеров закрученных слов :

Диаграмма закрученных слов

Задание 1:

Напишите полную программу или функцию, которая будет брать слово из стандартного ввода и выводить, если это закрученное слово и его характеристики, в читаемом формате, расширенный текст, 3 символа, флаги и т. Д.

Тестовые случаи и примеры выходных данных для разных слов (но вы можете решить, как представлять результаты):

EARTH, GROUP            > NO        // NOT A SWIRLING WORD
OPERA, STAY, IRIS       > SW,CF,CW  // SWIRLING WORD, CENTRIFUGAL, CLOCKWISE
MINER, TAX, PLUG, META  > SW,CF,CC  // SWIRLING WORD, CENTRIFUGAL, COUNTERCLOCKWISE
AXIOM, AXIS, COOK       > SW,CP,CW  // SWIRLING WORD, CENTRIPETAL, CLOCKWISE
WATCH, YETI, PILL       > SW,CP,CC  // SWIRLING WORD, CENTRIPETAL, COUNTERCLOCKWISE

MORE EXAMPLES OF FALSE TEST CASES (NOT SWIRLING WORDS): 
EARTH, GROUP, OUTPUT, WORD, CONNECTION, ODD, MOM, DAD, 
CHARACTER, EXAMPLE, QUESTION, NEWSLETTER, OTHER

Правила:

  1. Связь между первыми двумя символами должны быть вверх (как в графике), все еще соединение должно быть вниз , все нечетные соединения должны быть вверх .
  2. Вы можете игнорировать верхний / нижний регистр или рассматривать / конвертировать все в верхний регистр или все в нижний регистр.
  3. Вводимые слова - это только символы в диапазоне алфавита AZ, без пробелов, без знаков препинания и т. Д.
  4. Если слово содержит двойные символы, такие как «GROOVE», вы должны свернуть двойные символы до одного символа: «GROOVE»> «GROVE».
  5. Входные слова будут содержать не менее 3 различных символов. Такие слова, как «мама», «папа», «LOL» не являются допустимыми словами.
  6. Можно передать несколько раз в одном и том же символе, например, "IRIS".
  7. Самый короткий код выигрывает.

Задача 2:

Чтобы получить больше репутации, найдите самые длинные закрученные слова и их характеристики, которые вы можете найти в словаре английского языка, следуя приведенным выше правилам. Вы можете взять, например, в качестве ссылки полный список английских слов здесь .

Удачного кодирования!


15
Хорошие диаграммы! :) (И хороший вызов тоже.;))
Мартин Эндер

Будет ли пропуск «Вихревой» допустимым выходным форматом, поскольку он подразумевается, когда вход не «не вихревой»?
Мартин Эндер

@MartinEnder Да, поскольку понятно, когда он крутится или нет, может быть пустым для «не» и «1» для «да» и т. Д. (Рад, что вам понравились диаграммы и вызов! :))
Марио

1
@TimmyD Но не все ухабистые слова крутятся. :)
Мартин Эндер

2
@Lynn Спасибо за оценку и предложения, я постараюсь улучшить в будущем. Я добавил правило «удалить двойное», чтобы предвидеть, что люди спрашивают меня: «Что мы делаем, когда есть двойники?» > Вы можете рассматривать двойные числа как 1 единственный символ, потому что от "L" переход к "L" равен нулю :) Не то чтобы я хотел добавить каверзные трудности ради самого себя.
Марио

Ответы:


11

MATL , 33 31 30 байт

lydhg)dt|dZSXz&=wZSdh?4M1)3M1)

Ввод прописными буквами (или строчными буквами, но не смешанный).

Выход:

  • Если слово не закручено: вывод не производится
  • Если это кружится: два числа производятся в разных строках:
    • Первый номер 1/ -1 обозначает центробежный / центростремительный.
    • Второе число 1/ `-1 'указывает по часовой стрелке / против часовой стрелки.

Попробуйте онлайн! Или проверьте все контрольные примеры (код изменен, чтобы принять все входные данные и вывести два выходных числа в одной строке)

объяснение

Давайте возьмем входные данные 'OPERAA'в качестве примера.

Первая часть кода удаляет двойные буквы:

l     % Push 1
      %   STACK: 1
y     % Take input implicitly from below, and duplicate
      %   STACK: 'OPERAA', 1, 'OPERAA'
d     % Convert to code points and compute differences
      %   STACK: 'OPERAA', 1, [1 -11  13 -17 0]
h     % Concatenate horizontally
      %   STACK: 'OPERAA', [1 1 -11  13 -17 0]
g     % Convert to logical
      %   STACK: 'OPERAA', [true true true true true false]
)     % Index
      %   STACK: 'OPERA'

Теперь мы проверяем, не уменьшаются ли расстояния между буквами (необходимое условие для закручивания слова):

d     % Convert to code points and compute differences
      %   STACK: [1 -11  13 -17]
t|    % Duplicate and take absolute value
      %   STACK: [1 -11  13 -17], [1 11  13 17]
d     % Differences
      %   STACK: [1 -11  13 -17], [10 2 4]
ZS    % Signum
      %   STACK: [1 -11  13 -17], [1 1 1]
Xz    % Remove zeros (gives a vertical vector). Needed for words like 'IRIS',
      % where some consecutive distances are equal
      %   STACK: [1 -11  13 -17], [1; 1; 1]
&=    % All pairwise equality comparisons. Gives a matrix. If all the signs 
      % were equal the matrix will contain all ones
      %   STACK: [1 -11  13 -17], [1 1 1; 1 1 1; 1 1 1]

Затем мы проверяем, идут ли буквы туда-сюда (это другое условие для закручивания слова):

w     % Swap
      %   STACK: [1 1 1; 1 1 1; 1 1 1], [1 -11  13 -17]
ZS    % Signum
      %   STACK: [1 1 1; 1 1 1; 1 1 1], [1 -1 1 -1]
d     % Differences
      %   STACK: [1 1 1; 1 1 1; 1 1 1], [-2 2 -2]

Наконец, мы проверяем, выполняются ли два условия, и в этом случае выводим результат:

h     % Concatenate horizontally
      %   STACK: [1 1 1 1 1 1 1 1 1 -2 2 -2]
?     % If all elements are nonzero
  4M  %   Push first signum array without zeros, from the automatic clipboard
      %     STACK: [1; 1; 1]
  1)  %   Get first element (tells if first difference was positive or negative)
      %     STACK: 1
  3M  %   Push second signum array, from the automatic clipboard
      %     STACK: 1, [1 -1 1 -1]
  1)  %   Get first element (tells if first movement was right or left)
      %     STACK: 1, 1
      %   Implicitly end if
      % Implicitly display

6

Mathematica, 117 111 байт

Спасибо JHM за сохранение 6 байтов и без учета регистра при загрузке!

 {o=OrderedQ/@{a=Abs[d=Differences[#&@@@Split@LetterNumber@#]],Reverse@a},d[[1]]>0,Or@@o&&Max[Most[d]Rest@d]<0}&

Безымянная функция, которая принимает строку и возвращает вложенный список логических значений в форме {{B1,B2},B3,B4}. B4 записывает, закручивается ли слово (а если нет, то остальная часть вывода является мусором). Если слово вращается, то B1 записывает, является ли слово центробежным, B2 записывает, является ли слово центростремительным, а B3 записывает, является ли слово по часовой стрелке (True) или против часовой стрелки (False).

Вот более длинная версия , что пост-процессы (первая линия) выше функции (разнесены на протяжении 2 - го-пятые линии) , чтобы сделать его идентичным OP: NOесли слово не крутятся, и соответствующий выбор {SW,CF,CW}, {SW,CF,CC}, {SW,CP,CW}или {SW,CP,CC}если слово крутится

If[#3, {SW, If[#[[1]], CF, CP], If[#2, CW, CC]}, NO] & @@
  {o = OrderedQ /@
    {a = Abs[d = Differences[# & @@@ Split@LetterNumber@#]], Reverse@a},
  d[[1]] > 0,
  Or @@ o && Max[Most[d] Rest@d] < 0} &

Объяснение такое же, как и в ответе CJam Мартина Эндера, с одним дополнительным примечанием: список последовательных различий должен чередоваться в знаке для закручивания слова, и это можно обнаружить, убедившись, что все произведения пар последовательных разностей отрицательны (вот что Max[Most[d]Rest@d]<0делает).

Запустив функцию для всех 40 000 слов Mathematica WordList[], мы находим следующие 8-буквенные закрученные слова, которые являются самыми длинными из соответствующих типов закрученных:

operetta    {SW, CF, CW}
opposite    {SW, CF, CW}
stowaway    {SW, CF, CW}
assassin    {SW, CP, CW}
assessor    {SW, CP, CW}
baccarat    {SW, CF, CC}
keenness    {SW, CF, CC}
positive    {SW, CF, CC}

(Брауни указывает на positive отсутствие двойных букв и меньше повторяющихся букв, чем stowaway.)

Но абсолютный чемпион - 9-буквенное центростремительное слово, вращающееся против часовой стрелки vassalage!


1
Вы можете сохранить 3 байта, используя LetterNumberвместо, ToCharacterCodeи еще 3 байта, используя Most[d]вместо Drop[d,-1].
JungHwan Мин

5

Скала, 110 байт

def/(s:String)={val ? =s.sliding(2).map(t=>(t(0)-t(1)).abs).toSeq
(Seq(?,?reverse)indexOf(?sorted),s(0)<s(1))}

Возвращает кортеж (a,b)с

  • a == 1 если s центростремительный
  • a == 0 если s центробежный
  • a == -1 если с не кружится

а также

  • b == true если s по часовой стрелке
  • b == false если s против часовой стрелки
  • b может быть истинным или ложным, если s не вращается

Объяснение:

def/(s:String)={      //define a method called / with a String argument
  val ? =s            //define ? as...
    .sliding(2)       //an iterator for each two consecutive elements
    .map(t=>          //foreach 2 chars
      (t(0)-t(1)).abs //get the absolute value of their difference
    ) 
    .toSeq            //and convert the iterator to a Seq, because iterator doesn't have reverse and sorted methods
  (                   //return a tuple of
    Seq(?,?reverse)     //a Seq of ? and reversed ?
    .indexOf(?sorted)   //and check which of them is sorted ?
  ,                   //and
   s(0)< s(1)          //the difference bewteen the first two elements of the string.
  )
}

5

Желе , 30 байт

3Ŀḟ0ṠE
ÑṠḟ0Ṃ
ÑAI
OIḟ0
ÇṠḢ;2Ŀ;Ñ

TryItOnline
Или посмотрите тестовые случаи (с небольшим изменением, так как самый последнийÑбудет указывать на новую главную ссылку)

(Мой недостаток навыка цепочки, вероятно, стоит здесь несколько байтов)
Все верхнее или все нижнее.
Возвращает список флагов [D, F, S]:
S: вращающийся = 1 / не вращающийся = 0
F: центробежный = 1 (круговой = 0) центростремительный = -1
D: по часовой стрелке = 1 / против часовой стрелки = -1
- если S = 0 другие флаги все еще оцениваются, даже если они не содержат никакой полезной информации.

Как?

3Ŀḟ0ṠE      - Link 1, isSpinning: s
3Ŀ          - call link 3 as a monad with s
  ḟ0        - filter out zeros
    Ṡ       - sign
     E      - all equal?

ÑṠḟ0Ṃ       - Link 2, centrifugal(-1), circular(0) or centripetal(1): s
Ñ           - call next link (3) as a monad with s
 Ṡ          - sign (+1 for positive changes, -1 for negative changes, 0 for no change)
  ḟ0        - filter out zeros (ignore these for cases like "IRIS")
    Ṃ       - minimum (will be the only value for spinning words)
            -    (circular words like "DAD", now excluded, yield min([])=0)

ÑAI         - Link 3, absolute change of moves over alphabet: s
Ñ           - call next link (4) as a monad with s
 A          - absolute
  I         - differences

OIḟ0        - Link 4, non-zero moves over alphabet: s
O           - ordinal cast
 I          - differences
  ḟ0        - filter out zeros

ÇṠḢ;2Ŀ;Ñ    - Main link: s
Ç           - call last link (4) as a monad with s
 Ṡ          - sign
  Ḣ         - head (clockwise / anticlockwise: 1 / -1)
   ;  ;     - concatenate
    2Ŀ      - call link (2) as a monad with s
       Ñ    - call next link (1) as a monad with s

1
Я думаю, что вы испытываете то, что я называю «синдромом новичка» здесь. Я чувствую себя точно так же, как вы. Может быть, Деннис мог бы помочь здесь. Но я добавил +1 только потому, что увидел, что это возможно в желе. Также вы можете снять круглый корпус; это больше не существует.
Эрик Outgolfer

Спасибо за оскорбление в круглых словах - так как оказывается, что 6 байтов для их обработки на самом деле были излишни, так как минимум пустого списка таков, 0что это также работает и для них!
Джонатан Аллан

Итак, это должно работать для них? Я вижу, у вас все еще есть circular(0)объяснение, может быть, пришло время удалить его.
Эрик Outgolfer

Это не обязательно, нет - но этот код все еще делает после удаления того, что я использовал, чтобы явно сделать это, из-за того, что min([])=0 jelly.tryitonline.net/#code=W13huYI&input= - Обратите внимание, что круговые слова теперь никогда Ожидаемый вклад, нет проблем в организации питания для них.
Джонатан Аллан

Я просто попросил вас перепроверить. И я поняла, что вы имели в виду min([])==0, но я подумала, что это все еще игра в гольф.
Эрик Outgolfer

3

CJam , 39 байт

r{2ew::-V}:D~-_:g_0=\D#)!@:zD-:g_0=\(-!

Попробуйте онлайн!

Ввод может быть прописным или строчным, но не смешанным.

Программа непреднамеренно указывает слова, которые не обязательно являются центробежными или центростремительными, но в остальном отвечают требованиям спиральности. Они описаны как "круговые" в таблице ниже.

Чтобы интерпретировать выходные данные, используйте эту диаграмму:

SPIRAL (output contains four 1s)
-11-11 : Clockwise Centrifugal
-1111  : Clockwise Centripetal
11-11  : Counter-clockwise Centrifugal
1111   : Counter-clockwise Centripetal

CIRCULAR (output contains two 1s)
-11    : Clockwise Circular
11     : Counter-clockwise Circular

NONSPIRAL (output contains a 0)


Объяснение:

Программа на самом деле оценивает, начинается ли последовательность ненулевых различий между символами положительно или отрицательно, меняется ли она по знаку, начинаются ли увеличивающиеся или уменьшающиеся величины, и продолжает ли она это делать. Если величины не увеличиваются или уменьшаются, программа прерывается, работая с пустым массивом. Основные шаги показаны ниже (этот код также покажет прогресс стека):

r{2ew::-V}:D~-   e# take difference of overlapping pairs, removing 0s handles duplicates
               ede# store difference function plus 0 as D, it's multipurpose
_:g_0=\          e# compute signs differences, keep first to show starting direction
               ede# -1 = CLOCKWISE, 1 = COUNTERCLOCKWISE
D#)!@            e# difference of signs includes 0 if not alternating, keep in stack
               ede# 1 = ALTERNATING, 0 = NOT ALTERNATING
:zD-:g           e# signs of difference of absolute values, ignoring 0s (fixed magnitude)
_0=\             e# keep first sign in stack to indicate how the sequence starts
               ede# -1 = INCREASING, 1 = DECREASING
(-!              e# remove first item from entire list and see if nothing remains
               ede# 1 = EMPTY(MONOTONE), 0 = NONEMPTY

3

PHP, 322 байта

for(;++$i<strlen($z=preg_replace("#(.)\\1#","$1",$argv[1]));){$t[]=$z[$i-1]<=>$z[$i]?:0;$o[]=$z[0]<=>$z[$i];$i<2?:$k[]=$z[$i-2]<=>$z[$i];}$s=preg_match("#^1?(-11)*(-1)?$#",join($t))?($t[0]!=1?1:2):0;$s+=2*preg_match($r="#^(-1|0)?([01](-1|0))*[01]?$#",join($o));$s*=preg_match($r,join($k));count_chars($z,3)[2]?:$s=0;echo$s;

для более симпатичного вывода echo["n","+P","-P","+F","-F"][$s];

Расширенная версия

for(;++$i<strlen($z=preg_replace("#(.)\\1#","$1",$argv[1]));){
    $t[]=$z[$i-1]<=>$z[$i]?:0;
    $o[]=$z[0]<=>$z[$i];
    $i<2?:$k[]=$z[$i-2]<=>$z[$i];
    }
$s=preg_match("#^1?(-11)*(-1)?$#",join($t))?($t[0]!=1?1:2):0; #Clockwise direction or not
$s+=2*preg_match($r="#^(-1|0)?([01](-1|0))*[01]?$#",join($o)); # True centrifugal
$s*=preg_match($r,join($k)); #true or false second test for not
count_chars($z,3)[2]?:$s=0; # word must have >2 different characters
echo$s;# short output
echo["n","+P","-P","+F","-F"][$s]; #long output alternative

Второе значение задачи 2 без правила коротких пар

4 -F убийство 11 байт позитивизировать 10 байт

3 + F, противоположный 10 байтов, логогог 9 байтов

2-P вассаладж 9 байтов саркокол, сасарара 8 байтов

1 + P оценка 9 байтов apanage, aramaic, argonon, аукцион, avision, присуждается, crenele, exesion, exition, eyewink 7 байтов

Визуализируйте слово

header('Content-Type: image/svg+xml; charset=UTF-8');
$w=$_GET["w"]??"OOPERRA";
$w=strtoupper($w);
echo '<?xml version="1.0" encoding="UTF-8"?>'
.'<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">'

.'<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 -100 420 400">'
.'<title>Swirl Word</title><desc>Viualize a Word</desc>';
echo '<text x="210" y="-50" text-anchor="middle" font-family="arial">'.$w.'</text>';

foreach(range("A","Z")as $x=>$c){
    echo '<text x="'.(15+$x*15).'" y="110" text-anchor="middle" font-family="arial">'.$c.'</text>';
    $r[$c]=15+$x*15;
}
for($i=0;++$i<strlen($w);){
    echo '<path d="M '.($r[$w[$i-1]]).',105 A '.($radius=abs($r[$w[$i]]-$r[$w[$i-1]])/2).' '.($radius).' 0 0 0 '.($r[$w[$i]]).',105" style="stroke:gold; stroke-width:1px;fill:none;" />';
}
echo '</svg>';  

во фрагменте это результат SVG, который я создал

<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 -100 420 400"><title>Swirl Word</title><desc>Viualize a Word</desc><text x="210" y="-50"  text-anchor="middle" font-family="arial">KILLINGNESS</text><text x="15" y="110" text-anchor="middle" font-family="arial">A</text><text x="30" y="110" text-anchor="middle" font-family="arial">B</text><text x="45" y="110" text-anchor="middle" font-family="arial">C</text><text x="60" y="110" text-anchor="middle" font-family="arial">D</text><text x="75" y="110" text-anchor="middle" font-family="arial">E</text><text x="90" y="110" text-anchor="middle" font-family="arial">F</text><text x="105" y="110" text-anchor="middle" font-family="arial">G</text><text x="120" y="110" text-anchor="middle" font-family="arial">H</text><text x="135" y="110" text-anchor="middle" font-family="arial">I</text><text x="150" y="110" text-anchor="middle" font-family="arial">J</text><text x="165" y="110" text-anchor="middle" font-family="arial">K</text><text x="180" y="110" text-anchor="middle" font-family="arial">L</text><text x="195" y="110" text-anchor="middle" font-family="arial">M</text><text x="210" y="110" text-anchor="middle" font-family="arial">N</text><text x="225" y="110" text-anchor="middle" font-family="arial">O</text><text x="240" y="110" text-anchor="middle" font-family="arial">P</text><text x="255" y="110" text-anchor="middle" font-family="arial">Q</text><text x="270" y="110" text-anchor="middle" font-family="arial">R</text><text x="285" y="110" text-anchor="middle" font-family="arial">S</text><text x="300" y="110" text-anchor="middle" font-family="arial">T</text><text x="315" y="110" text-anchor="middle" font-family="arial">U</text><text x="330" y="110" text-anchor="middle" font-family="arial">V</text><text x="345" y="110" text-anchor="middle" font-family="arial">W</text><text x="360" y="110" text-anchor="middle" font-family="arial">X</text><text x="375" y="110" text-anchor="middle" font-family="arial">Y</text><text x="390" y="110" text-anchor="middle" font-family="arial">Z</text><path d="M 165,105 A 15 15 0 0 0 135,105" style="stroke:gold; stroke-width:1px;fill:none;" /><path d="M 135,105 A 22.5 22.5 0 0 0 180,105" style="stroke:gold; stroke-width:1px;fill:none;" /><path d="M 180,105 A 0 0 0 0 0 180,105" style="stroke:gold; stroke-width:1px;fill:none;" /><path d="M 180,105 A 22.5 22.5 0 0 0 135,105" style="stroke:gold; stroke-width:1px;fill:none;" /><path d="M 135,105 A 37.5 37.5 0 0 0 210,105" style="stroke:gold; stroke-width:1px;fill:none;" /><path d="M 210,105 A 52.5 52.5 0 0 0 105,105" style="stroke:gold; stroke-width:1px;fill:none;" /><path d="M 105,105 A 52.5 52.5 0 0 0 210,105" style="stroke:gold; stroke-width:1px;fill:none;" /><path d="M 210,105 A 67.5 67.5 0 0 0 75,105" style="stroke:gold; stroke-width:1px;fill:none;" /><path d="M 75,105 A 105 105 0 0 0 285,105" style="stroke:gold; stroke-width:1px;fill:none;" /><path d="M 285,105 A 0 0 0 0 0 285,105" style="stroke:gold; stroke-width:1px;fill:none;" /></svg>


Великий закрученный зритель слов! :) Может быть, вы можете попытаться соединить персонажей с полуэллипсами вместо полукругов. Он будет более компактным и будет выглядеть более «динамично». Но все равно выглядит великолепно!
Марио

@Mario это нужно только фактор '.(.8*$radius).'вместо '.($radius).'и , если заменить ($radius).' 0 0 0с ($radius).' 0 0 '.(($w[$i-1]<$w[$i]?1:0)^(($i-1)%2)).'программой не имеет направление починки
Йорг Hülsermann

2

Haskell, 148 байт

z f=tail>>=zipWith f
g c=and.z c.filter(/=0).map abs.z(-).map fromEnum
(a:b:r)%c|a==b=(b:r)%c|1<3=c a b
f s|a<-[g(>=)s,g(<=)s]=or a:a++[s%(<),s%(>)]

Попробуйте это на Ideone.

Ввод должен быть либо в нижнем, либо в верхнем регистре.
Выход список из пяти булевых: [SW?, CF?, CP?, CW?, CC?].

f "positive" -> [True,True,False,False,True]

Это оказалось дольше, чем ожидалось, особенно передача свернутых символов занимает около 40 байт.

Сначала я сравнивал только первые два символа, чтобы получить CWили CCпрежде, чем заметил, что тестовые случаи тоже похожи bbaили bbcявляются действительными, и проиграл этот подход.


2

Python, 152 байта:

lambda C:[C[-1]in max(C)+min(C),C[1]>C[0]]*all([[i>g,i<g][[h%2>0,h%2<1][C[1]>C[0]]]for i,g,h in filter(lambda i:i[0]!=i[1],zip(C,C[1:],range(len(C))))])

Анонимная лямбда-функция. Звоните как print(<Function Name>('<String>')).

Принимает ввод как все строчные или прописные, но не смешанный регистр.

Выводит массив, не содержащий ничего ([] ), если слово не является swirly, или массив в следующем формате в противном случае:

  • 1-й элемент True/False для Centrifugal/Centripetal.
  • 2-й элемент True/False для Clockwise/Counterclockwise.

Попробуйте онлайн! (Ideone)

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.