Виділіть обмежувальний ящик, частина II: Шестикутна сітка


24

Ви дали гексагональную сітку символів .і #, як це:

 . . . . . . . .
. . . . # . . . 
 . # . . . # . .
. . . # . . . . 
 . . . . . # . .
. . . . . . . . 

Ваше завдання полягає в тому, щоб заповнити всю вирівняний по осях прямокутника #з подальшим #:

 . . . . . . . .
. . # # # # . . 
 . # # # # # . .
. . # # # # # . 
 . . # # # # . .
. . . . . . . . 

Обмежна вісь, що обмежує вісь, - це найменша опукла шестикутна форма, яка містить усі #. Зауважте, що у випадку з шестикутною сіткою слід врахувати три осі (W / E, SW / NE, NW / SE):

введіть тут опис зображення

Ось ще один приклад, який показує, що в деяких випадках одна або кілька сторін будуть містити лише одну #:

. . . . . . . .         . . . . . . . . 
 . # . . . . . .         . # # # # . . .
. . . . . # . .         . . # # # # . . 
 . . # . . . . .         . . # # # . . .
. . . . . . . .         . . . . . . . . 

Ви можете або розглядати їх як шестикутники із виродженими сторонами, або ви можете намалювати обмежувальну коробку навколо них, як я це робив вище, і в цьому випадку вони все ще є шестикутниками:

введіть тут опис зображення

Занадто складно? Спробуйте I частину!

Правила

Ви можете використовувати будь-які два відмінних символи ASCII для друку, що не друкуються (від 0x21 до 0x7E, включно), замість #та .. Я продовжую посилатися на них як #і .на решту специфікації.

Введення та вихід можуть бути або окремим рядком, розділеним подачею рядків, або списком рядків (по одному для кожного рядка), але формат повинен бути узгодженим.

Можна припустити, що вхід містить хоча б один #і всі рядки однакової довжини. Зауважте, що є два різних "види" рядків (починаючи з пробілу або непробілу) - ви можете не припускати, що введення завжди починається з одного типу. Ви можете припустити, що обмежувальне поле завжди вписується всередину заданої вами сітки.

Ви можете написати програму чи функцію та скористатися будь-яким із наших стандартних методів отримання вводу та надання виводу.

Ви можете використовувати будь-яку мову програмування , але зауважте, що ці лазівки за замовчуванням заборонені.

Це , тому найкоротший вірний відповідь - вимірюється в байтах - виграє.

Випробування

Кожен тестовий випадок має вхід та вихід поруч.

#    #

 . .      . . 
# . #    # # #
 . .      . . 

 . #      . # 
. . .    . # .
 # .      # . 

 # .      # . 
. . .    . # .
 . #      . # 

 # .      # . 
# . .    # # .
 . #      # # 

 . #      # # 
# . .    # # #
 . #      # # 

. . #    . # #
 . .      # # 
# . .    # # .

# . .    # # .
 . .      # # 
. . #    . # #

. . . . . . . .         . . . . . . . . 
 . . # . # . . .         . . # # # . . .
. . . . . . . .         . . . # # . . . 
 . . . # . . . .         . . . # . . . .

. . . . . . . .         . . . . . . . . 
 . . # . . . # .         . . # # # # # .
. . . . . . . .         . . . # # # # . 
 . . . # . . . .         . . . # # # . .

. . . . . . . .         . . . . . . . . 
 . # . . . . . .         . # # # # . . .
. . . . . # . .         . . # # # # . . 
 . . . . . . . .         . . . . . . . .

. . . . . . . .         . . . . . . . . 
 . # . . . . . .         . # # # # . . .
. . . . . # . .         . . # # # # . . 
 . . # . . . . .         . . # # # . . .

. . . . # . . .         . . # # # # . . 
 . # . . . # . .         . # # # # # . .
. . . # . . . .         . . # # # # # . 
 . . . . . # . .         . . # # # # . .

1
Моя голова крутиться, намагаючись знайти будь-яку очевидну закономірність. Ви сказали "шестикутний", але в тестових випадках є лише два входи в шестикутники. Я загубився.
Анастасія-Романова 秀

1
@ Анастасія-Романова 秀 Якщо ви зобразите фігуру, як вона проходить через центри зовнішніх символів, то так деякі шестикутники будуть мати вироджені сторони (як у прямокутній сітці, де ви можете отримати випадки, коли прямокутник зменшується до лінії). Однак якщо ви намалюєте прямокутник навколо символів (як я це робив на схемі), всі приклади - це шестикутники (деякі з яких мають дуже короткі сторони).
Мартін Ендер

1
@ Анастасія-Романова 秀 Чи допомагає нова діаграма?
Мартін Ендер

3
Я! виглядає як II, якщо у мене неправильні окуляри ..
Ніл

1
@Neil Або, ви знаєте, занадто багато алкоголю;)
ThreeFx

Відповіді:


7

Pyth , 82 71 байт

L, hbebMqH @ S + GH1KhMyJs.e, Lkfq \ # @ bTUb.zA, ySm-FdJySsMJj.es.eXW && gKkgG-kYgH + kYZ \. \ # Bz
MqH @ S [hGHeG) 1j.es.eXW && ghMJs.e, Lkfq \ # @ bTUb.zkgSm-FdJ-kYgSsMJ + kYZ \. \ # Bz

Спробуйте в Інтернеті!

Пояснення

  • Нехай A - точка з найнижчою координатою y, а B - точка з найвищою координатою y.

  • Нехай C - точка з найменшим (x-значення мінус y-значення), D - точка з найвищим.

  • Нехай E - точка з найменшим (x-значення плюс y-значення), а F - найвищою.

Тоді це рівнозначно знаходженню координат, у яких координата у знаходиться між A і B, значення x мінус y-значення між C і D, а x-значення плюс y-значення між E і F.


Перший раз, коли я міг опублікувати рішення раніше, якби тільки додаток для андроїд SE міг правильно обробляти символи вкладок (чомусь вони зникли під час вставки): /
Sarge Borsch

@SargeBorsch Вибачте :(
Leaky Nun

ха-ха, це мене змусив програму SE Android: D
Sarge Borsch

6

Haskell, 256 254 243 байт

import Data.List
f=z(\l->(,).(,))[0..]l)[0..]
q l=m(m(\e->min(snd e).(".#"!!).fromEnum.and.z($)(m(\x y->y>=minimum x&&y<=maximum x).transpose.m b.filter((==)'#'.snd).concat$l)$b e))l
b=(m uncurry[const,(-),(+)]<*>).pure.fst
z=zipWith
m=map
q.f

Дякую @Damien за гольф f !

Введення приймається як список списку символів, вихід подається аналогічно.

Так це було звірі писати. Він заснований на ідеї LeakyNun з використанням максимальної та мінімальної фільтрації на основі координат елементів.

Я дуже здивований тим, що m=mapнасправді економить байти, оскільки це здається таким дорогим.


Пояснення:

Ось трохи менш врізана версія (наголос трохи ):

import Data.List
f=zipWith(\y l->zipWith(\x e->((y,x),e))[0..]l)[0..]
p=map(\x y->y>=minimum x&&y<=maximum x).transpose.map b.filter((==)'#'.snd).concat
q l=map(map(\e->min(snd e).(".#"!!).fromEnum.and.zipWith($)(p$l)$b e))l
b=(map uncurry[const,(-),(+)]<*>).pure.fst
  • fце функція, яка присвоює кожному символу індекс (y-index, x-index), зберігаючи початкову структуру списку.

  • b: Дано елемент індексованого списку, bобчислюється [y-index, y - x, y + x].

  • p: Враховуючи індексоване поле, поверніть 3 функції Int -> Bool, перша з яких - перевірка y-індексу, друга - різниці і третя суми. min(snd e)піклується про пробіли (простір менше обох). Ця функція вписана в код для гольфу.

  • qвраховуючи проіндексовано поле, змінити все необхідне , .щоб #, перевіряючи , якщо це спеціальне поле повернення Trueдо кожної тестової функції.

Остаточним рішенням є склад qі f.


1
f=z(\y->z((,).(,)y)[0..])[0..]
Демієн

абоh x=z x[0..] f=h$h.curry(,)
Демієн

5

Пітон 3, 380 378 348 346 байт

Зауважте, що відступ є вкладками, а не пробілами.

Версія для гольфу:

def s(i):
    L=i.splitlines();E=enumerate;A=lambda x,y:(y,x+y,x-y);N=(2**64,)*3;X=(-2**64,)*3
    for y,l in E(L):
        for x,c in E(l):
            if c=='#':p=A(x,y);X=tuple(map(max,X,p));N=tuple(map(min,N,p))
    R=''
    for y,l in E(L):
        for x,c in E(l):
            if c!='.':R+=c
            else:p=A(x,y);f=all(N[j]<=p[j]<=X[j]for j in range(0,3));R+='.#'[f]
        R+='\n'
    return R

Перевірте це на Ideone

Пояснення (для версії, що не перебуває в гольфі нижче):

Вся обробка проводиться без перетворення, символи простору просто пропускаються.
Функція axes_posобчислює 3-ма кількість уявних "3D" координат, вони накопичуються в (елементарний) мінімум і максимум 3-кортежі ( bmin, bmax) для всіх #символів.

Координати обчислюються в def axes_pos(x, y): return y, x + y, lc - y + x;
де X рахує від 0 до правого, а Y вважає від 0 донизу (від першого рядка до останнього).
Перша уявна координата в основному Y, тому що це очевидно, чому. Його сокира ортогональна до зелених меж (на знімках ОП)
Друга - ортогональна до червоних меж, третя - ортогональна до синіх.

У другому проході відбувається заміна всіх .символів, "координати яких 3D" потрапляють в діапазон bmin.. bmaxдіапазон, елемент мудрий - це перевірено в цьому виразі all(bmin[j] <= p[j] <= bmax[j] for j in range(0, 3)).

Невикористана версія з тестами, також на Ideone :

def solve(i):
    ls = i.splitlines()
    lc = len(ls)

    def axes_pos(x, y):
        return y, x + y, lc - y + x

    I = 2 ** 64
    bmin = (I, I, I)
    bmax = (0, 0, 0)

    for y, line in enumerate(ls):
        for x, char in enumerate(line):
            if char != '#': continue
            p = axes_pos(x, y)
            bmax = tuple(map(max, bmax, p))
            bmin = tuple(map(min, bmin, p))

    result = ''
    for y, line in enumerate(ls):
        for x, char in enumerate(line):
            if char != '.':
                result += char
            else:
                p = axes_pos(x, y)
                f = all(bmin[j] <= p[j] <= bmax[j] for j in range(0, 3))
                result += '#' if f else char
        result += '\n'

    return result


def run_test(a, b):
    result = solve(a)
    if result != b:
        raise AssertionError('\n' + result + '\n\nshould be equal to\n\n' + b)


def run_tests():
    run_test(
        "#\n",

        "#\n")

    run_test(
        " . . \n"
        "# . #\n"
        " . . \n",

        " . . \n"
        "# # #\n"
        " . . \n")

    run_test(
        " . # \n"
        ". . .\n"
        " # . \n",

        " . # \n"
        ". # .\n"
        " # . \n")

    run_test(
        " # . \n"
        ". . .\n"
        " . # \n",

        " # . \n"
        ". # .\n"
        " . # \n")

    run_test(
        " # . \n"
        "# . .\n"
        " . # \n",

        " # . \n"
        "# # .\n"
        " # # \n")

    run_test(
        " . # \n"
        "# . .\n"
        " . # \n",

        " # # \n"
        "# # #\n"
        " # # \n")

    run_test(
        ". . . . . . . . \n"
        " . . # . # . . .\n"
        ". . . . . . . . \n"
        " . . . # . . . .\n",

        ". . . . . . . . \n"
        " . . # # # . . .\n"
        ". . . # # . . . \n"
        " . . . # . . . .\n")

    run_test(
        ". . . . . . . . \n"
        " . . # . . . # .\n"
        ". . . . . . . . \n"
        " . . . # . . . .\n",

        ". . . . . . . . \n"
        " . . # # # # # .\n"
        ". . . # # # # . \n"
        " . . . # # # . .\n")

    run_test(
        ". . . . . . . . \n"
        " . # . . . . . .\n"
        ". . . . . # . . \n"
        " . . . . . . . .\n",

        ". . . . . . . . \n"
        " . # # # # . . .\n"
        ". . # # # # . . \n"
        " . . . . . . . .\n")

    run_test(
        ". . . . . . . . \n"
        " . # . . . . . .\n"
        ". . . . . # . . \n"
        " . . # . . . . .\n",

        ". . . . . . . . \n"
        " . # # # # . . .\n"
        ". . # # # # . . \n"
        " . . # # # . . .\n")

    run_test(
        ". . . . # . . . \n"
        " . # . . . # . .\n"
        ". . . # . . . . \n"
        " . . . . . # . .\n",

        ". . # # # # . . \n"
        " . # # # # # . .\n"
        ". . # # # # # . \n"
        " . . # # # # . .\n")


if __name__ == '__main__':
    run_tests()
Оновлення 1:

Видалено непотрібне -1для третьої уявної координати, оскільки воно нічого не змінює

Оновлення 2,3:

Частково впроваджені покращення, запропоновані також Leaky Nunмоїм власним


Ми в основному використовуємо один і той же алгоритм? Чи можете ви додати пояснення?
Leaky Nun

1
def A(x,y):return y,x+y,len(L)-1-y+x->A=lambda x,y:(y,x+y,len(L)-1-y+x)
Лина монашка

Крім того, розуміння списку може допомогти вам відібрати кілька байтів.
Leaky Nun

1
Я думаю, ти можеш перетворитись len(L)-y+xнаx-y
Leaky Nun

1
Ви можете взяти в список рядків
Leaky Nun

5

Желе , 45 35 13 42 41 байт

Ṁ€»\
ṚÇṚ«Çṁ"
ŒDṙZL$ÇṙL’$ŒḌ«Ç
ṚÇṚ«Ç
n⁶aÇo⁶

Це список посилань; останній повинен бути викликаний на вході для отримання результату.

Введення / виведення у формі рядкових масивів, де .вказується порожнім і @вказує заповненим.

Спробуйте в Інтернеті! або перевірити всі тестові випадки .

Фон

Розглянемо наступний приклад.

. . . . . . . . 
 . @ . . . . . .
. . . . . @ . . 
 . . @ . . . . .

Намалювавши пару чи паралельні лінії - найближчу пару, яка охоплює всі заповнені позиції - у кожному з трьох напрямків ми можемо визначити шестикутне обмежувальне поле.

Під час реалізації ми замінюємо всі символи між цими двома рядками на @, і все поза цим рядком ., з можливим винятком діагоналей, які містять лише пробіли).

Для горизонтальної осі це дає

................
@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@

для осі діагоналі, що падає, вона дає

..@@@@@@@......
...@@@@@@@......
....@@@@@@@.....
 ....@@@@@@@....

а для піднімаючої діагональної осі вона дає

....@@@@@@@@@...
...@@@@@@@@@....
..@@@@@@@@@....
.@@@@@@@@@.... .

Беручи символічний мінімум усіх трьох, оскільки .< @, ми отримуємо

...............
...@@@@@@@......
....@@@@@@@....
 ....@@@@@.... .

Залишилося лише відновити пробіли.

Як це працює

n⁶aÇo⁶           Main link. Argument: A (array of strings)

n⁶               Not-equal space; yield 0 for spaces, 1 otherwise.
  aÇ             Take the logical AND with the result the 4th helper link.
                 This will replace 1's (corresponding to non-space characters) with
                 the corresponding character that result from calling the link.
    o⁶           Logical OR with space; replaces the 0's with spaces.
ṚÇṚ«Ç            4th helper link. Argument: A

Ṛ                Reverse the order of the strings in A.
 Ç               Call the 3rd helper link.
  Ṛ              Reverse the order of the strings in the resulting array.
    Ç            Call the 3rd helper link with argument A (unmodified).
   «             Take the character-wise minimum of both results.
ŒDṙZL$ÇṙL’$ŒḌ«Ç  3rd helper link. Argument: L (array of strings)

ŒD               Yield all falling diagonals of L. This is a reversible operation,
                 so it begins with the main diagonal.
   ZL$           Yield the length of the transpose (number of columns).
  ṙ              Shift the array of diagonals that many units to the left.
                 This puts the diagonals in their natural order.
      Ç          Call the helper link on the result.
        L’$      Yield the decremented length (number of columns) of L.
       ṙ         Shift the result that many units to the left.
                 This puts the changed diagonals in their original order.
           ŒḌ    Undiagonal; reconstruct the string array.
              Ç  Call the 2nd helper link with argument L (unmodified).
             «   Take the character-wise minimum of both results.
ṚÇṚ«Çṁ"          2nd helper link. Argument: M (array)

Ṛ                Reverse the rows of M.
 Ç               Call the 1st helper link on the result.
  Ṛ              Reverse the rows of the result.
    Ç            Call the 1nd helper link with argument M (unmodified).
   «             Take the minimum of both results.
     ṁ"          Mold zipwith; repeat each character in the result to the left
                 as many times as needed to fill the corresponding row of M.
Ṁ€»\             1st helper link. Argument: N (array)

Ṁ€               Take the maximum of each row of N.
  »\             Take the cumulative maxima of the resulting characters.

2

Python, 237 230 байт

7 байт завдяки Деннісу.

def f(a):i=range(len(a[0]));j=range(len(a));b,c,d=map(sorted,zip(*[[x,x+y,x-y]for y in i for x in j if"?"<a[x][y]]));return[[[a[x][y],"#"][(a[x][y]>" ")*(b[0]<=x<=b[-1])*(c[0]<=x+y<=c[-1])*(d[0]<=x-y<=d[-1])]for y in i]for x in j]

Порт моєї відповіді в Pyth .

Приймає масив рядків як вхідний, виводить 2D масив символів.


2

Perl, 128 126 байт

Включає +6 для -0F\n

Запустити з введенням на STDIN. Використовувати 1для заповненого, 0для порожнього. Рядки не повинні бути прокладені пробілами в кінці:

perl -M5.010 hexafill.pl
 0 0 0 0 0 0 0 0
0 0 1 1 1 1 0 0 
 0 1 1 1 1 1 0 0
0 0 1 1 1 1 1 0 
 0 0 1 1 1 1 0 0
0 0 0 0 0 0 0 0 
^D

hexafill.pl

#!/usr/bin/perl -0F\n
$-=map{s%$=%$=^!map{/$/;grep{pos=$`;$=?$_|="!"x$`.1:!/\b.*\G./}${--$@}}@F-$-+pos,$-+pos,$-%eeg;--$-;$=||say}@F while$=--

Використовує координати куба. Визначте максимум та мінімум під час $= == 1циклу та заповніть координати між цими межами протягом$= == 0 циклу. Перші 58 петель безглузді і є лише там, щоб заповнити $-кількість рядків


1

TSQL, 768 байт

Я написав запит, щоб вирішити це - що мені було досить складно. Він не в змозі конкурувати з усіма відмінними коротшими відповідями. Але хотів опублікувати його все одно для зацікавлених. Вибачте за тривалість відповіді - сподіваючись, що кодогольф також стосується різних підходів.

Гольф:

DECLARE @ varchar(max)=
'
. . . . # . . . 
 . # . . . # . .
. . . # . . . . 
 . . . . . # . .
. . . . . . . . 
'

;WITH c as(SELECT cast(0as varchar(max))a,x=0,y=1,z=0UNION ALL SELECT SUBSTRING(@,z,1),IIF(SUBSTRING(@,z,1)=CHAR(10),1,x+1),IIF(SUBSTRING(@,z,1)=CHAR(10),y+1,y),z+1FROM c WHERE LEN(@)>z)SELECT @=stuff(@,z-1,1,'#')FROM c b WHERE((exists(SELECT*FROM c WHERE b.y=y and'#'=a)or exists(SELECT*FROM c WHERE b.y<y and'#'=a)and exists(SELECT*FROM c WHERE b.y>y and'#'=a))and a='.')and(exists(SELECT*FROM c WHERE b.x<=x-ABS(y-b.y)and'#'=a)or exists(SELECT*FROM c WHERE b.x<=x+y-b.y and a='#'and b.y<y)and exists(SELECT*FROM c WHERE b.x<=x+b.y-y and a='#'and b.y>y))and(exists(SELECT*FROM c WHERE b.x>=x+ABS(y-b.y)and'#'=a)or exists(SELECT*FROM c WHERE b.x>=x-y+b.y and b.y<y and'#'=a)and exists(SELECT*FROM c WHERE b.x>=x-b.y+y and a='#'and b.y>y))OPTION(MAXRECURSION 0)PRINT @

Безголівки:

DECLARE @ varchar(max)=
'
. . . . # . . . 
 . # . . . # . .
. . . # . . . . 
 . . . . . # . .
. . . . . . . . 
'
;WITH c as
(
  SELECT 
    cast(0as varchar(max))a,x=0,y=1,z=0
  UNION ALL
  SELECT
    SUBSTRING(@,z,1),IIF(SUBSTRING(@,z,1)=CHAR(10),1,x+1),
    IIF(SUBSTRING(@,z,1)=CHAR(10),y+1,y),
    z+1
  FROM c
  WHERE LEN(@)>z
)
SELECT @=stuff(@,z-1,1,'#')FROM c b
WHERE((exists(SELECT*FROM c WHERE b.y=y and'#'=a)
or exists(SELECT*FROM c WHERE b.y<y and'#'=a)
and exists(SELECT*FROM c WHERE b.y>y and'#'=a)
)and a='.')
and 
(exists(SELECT*FROM c WHERE b.x<=x-ABS(y-b.y)and'#'=a)
or exists(SELECT*FROM c WHERE b.x<=x+y-b.y and a='#'and b.y<y)
and exists(SELECT*FROM c WHERE b.x<=x+b.y-y and a='#'and b.y>y))
and(exists(SELECT*FROM c WHERE b.x>=x+ABS(y-b.y)and'#'=a)
or exists(SELECT*FROM c WHERE b.x>=x-y+b.y and b.y<y and'#'=a)
and exists(SELECT*FROM c WHERE b.x>=x-b.y+y and a='#'and b.y>y))
OPTION(MAXRECURSION 0) 
PRINT @

Fiddle unololfed


1

Октава GNU, 212 , 196 байт

Можливо, насправді не є улюбленою мовою вибору гравця гольфіста, але саме це викликає виклик, чи не так? Припустимо, що m взято як матрицю char: 178 байтів стоять окремо і 196 - якщо вони вбудовані у функцію .

гольф:

function k=f(m)[a,b]=size(m);[y,x]=ndgrid(1:a,1:b);t={y,y+x,x-y};k=m;s=x>0;for j=1:3l{j}=unique(sort(vec(t{j}.*(m==['#']))))([2,end]);s&=(l{j}(1)<=t{j})&(l{j}(2)>=t{j});endk(s&mod(x+y,2))=['#']end

неозорений:

function k=f(m)
[a,b]=size(m);[y,x]=ndgrid(1:a,1:b);t={y,y+x,x-y};k=m;s=x>0;
for j=1:3
  l{j}=unique(sort(vec(t{j}.*(m==['#']))))([2,end]);
  s&=(l{j}(1)<=t{j})&(l{j}(2)>=t{j});
end
k(s&mod(x+y,2))=['#']
end

Пояснення : ми будуємо систему координат, 3 осі - ортогональні сторонам шестикутників, знаходимо максимум і хв кожної координати, потім будуємо логічну маску, починаючи з 1 скрізь і логічно, і: кожне обмеження максимуму і мінімум обмежень, нарешті, перенастроювання кожна залишилася позиція "справжня" до знака "#".

Якщо ви хочете перевірити його, ви можете просто створити матрицю m так:

m = [' . . . . . . . .. . . . # . . .  . # . . . # . .. . . # . . . .  . . . . . # . .. . . . . . . . ']; m = reshape(m,[numel(m)/6,6])';

а потім викликати f (m) і порівняти з m, будуючи матрицю з обома ними в:

['     before           after      ';m,ones(6,1)*'|',f(m)]

1
(Запізнілий) Ласкаво просимо до PPCG! Відповіді Октави більш ніж вітаються. :) Однак дві речі: 1) включіть код, який ви насправді порахували (без зайвого пробілу), щоб люди могли легше перевірити рахунок. Можна включити читану версію окремо. 2) Здається, що ваше подання - це фрагмент, який передбачає, що вхід зберігається mі вихід, який слід зберігати k. Відповіді завжди мають бути повноцінними програмами або функціями дзвінка.
Мартін Ендер

Спасибі! Так, ви праві, я зараз вбудував k і m у функцію f і додав фрагмент, що будує перший тест m для перевірки.
mathreadler
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.