O Anagrama do Tempo


29

Originalmente publicado (e excluído) pelo @Tlink , que provavelmente foi inspirado nesta pergunta do StackOverflow .
Como foi uma pena que tenha sido excluído, porque parecia um bom desafio em geral, achei que o republicaria com formatação e regras adequadas. (Tentei entrar em contato com o @Tlink e obter sua permissão para publicá-lo, mas ele não responde mais, e foi por isso que decidi publicá-lo agora.

Entrada: seis dígitos.

Saída: a primeira ou a última hora válida no formato de 24 horas ( 00:00:00até 23:59:59). (Você pode escolher se quer imprimir a primeira ou a última hora válida.)

Exemplo:

Quando as entradas são 1,8,3,2,6,4, os seguintes horários podem ser criados:

12:36:48    12:38:46    12:46:38    12:48:36
13:26:48    13:28:46    13:46:28    13:48:26
14:26:38    14:28:36    14:36:28    14:38:26
16:23:48    16:24:38    16:28:34    16:28:43
16:32:48    16:34:28    16:38:24    16:38:42
16:42:38    16:43:28    16:48:23    16:48:32
18:23:46    18:24:36    18:26:34    18:26:43
18:32:46    18:34:26    18:36:24    18:36:42
18:42:36    18:43:26    18:46:23    18:46:32
21:36:48    21:38:46    21:46:38    21:48:36
23:16:48    23:48:16

Portanto, produziremos um 12:36:48ou 23:48:16neste caso, sendo o primeiro / o último, respectivamente.

Regras do desafio:

  • Indique se você deu a primeira ou a última hora válida na sua resposta.
  • A E / S é flexível. A entrada pode ser seis números inteiros separados; uma string contendo os seis dígitos; uma lista / matriz inteira; um único número (possivelmente octal); etc. A saída pode ser uma lista / matriz de dígitos ordenada corretamente; uma String no formato HH:mm:ss/ HHmmss/ HH mm ss; todos os dígitos impressos com delimitador de nova linha; etc. Sua ligação.
  • Você pode pegar os dígitos na ordem que desejar, para que eles já possam ser classificados do menor para o maior ou vice-versa.
  • Se não for possível criar um tempo válido com os dígitos fornecidos (ou seja 2,5,5,5,5,5), deixe-o claro da maneira que desejar. Pode retornar null/ false; "Not possible"; falha com um erro; etc. (Você não pode gerar um horário inválido como 55:55:52, ou outro válido como 00:00:00.) Indique como ele lida com entradas para as quais não é possível criar uma hora válida.
  • Você não tem permissão para gerar todos os tempos válidos possíveis. Somente os primeiros / os mais recentes devem ser enviados / retornados.
  • 24por horas (ou seja 24:00:00) ou 60por minutos / segundos (ou seja 00:60:60) não são válidos. Os intervalos são [00-23]para horas, [00-59]minutos e segundos.

Regras gerais:

  • Isso é , então a resposta mais curta em bytes vence.
    Não permita que idiomas com código de golfe o desencorajem a postar respostas com idiomas que não sejam codegolf. Tente encontrar uma resposta o mais curta possível para 'qualquer' linguagem de programação.
  • As regras padrão se aplicam à sua resposta, para que você possa usar STDIN / STDOUT, funções / método com os parâmetros adequados e programas completos do tipo retorno. Sua chamada.
  • As brechas padrão são proibidas.
  • Se possível, adicione um link com um teste para o seu código.
  • Além disso, adicione uma explicação, se necessário.

Casos de teste:

Input:          Earliest output:     Latest output:

1,2,3,4,6,8     12:36:48             23:48:16
2,5,5,5,5,5     None possible        None possible
0,0,0,1,1,1     00:01:11             11:10:00
1,1,2,2,3,3     11:22:33             23:32:11
9,9,9,9,9,9     None possible        None possible
2,3,5,5,9,9     23:59:59             23:59:59
1,2,3,4,5,6     12:34:56             23:56:41
0,0,0,0,0,0     00:00:00             00:00:00
1,5,5,8,8,8     18:58:58             18:58:58
1,5,5,5,8,8     15:58:58             18:58:55
1,1,1,8,8,8     18:18:18             18:18:18

11
Não é 23:48:16uma saída válida para o exemplo?
TFeld

devo emitir apenas uma das primeiras / últimas horas ou ambas?
tsh

@tsh Apenas um. Qual é com você. As duas respostas Python até agora produzem as primeiras.
Kevin Cruijssen

Um "tempo válido" não é responsável por alguns segundos bissextos? Por exemplo, seria 06:08:60válido, dado que houve um segundo de salto durante esse minuto?
Erik the Outgolfer

@EriktheOutgolfer Não, 60por minutos e segundos não é válido. Intervalos são [00-23], [00-59]e [00-59]. Esclarecerá isso no desafio.
Kevin Cruijssen

Respostas:


9

C (gcc) , 186 174 bytes

D[7]={0,1,10,100,1e3,1e4,1e5};G(O,L,F,T,I,M,E){if(!F)O=L<24e4&L%10000<6e3&L%100<60?L:1e9;else{for(T=1e9,I=0;I++<F;M=G(O/10,L*10+E,F-1),T=T>M?M:T,O=(O/10)+E*D[F])E=O%10;O=T;}}

Experimente online!

-12 bytes graças a Kevin Cruijssen

Provavelmente não é o ideal, mas funciona. Curiosamente, por algum motivo, com 7 argumentos, a implementação do gcc no TIO exige que você realmente os forneça ou segfaults. Na minha máquina, isso é desnecessário.

Formato: G (X, 0,6) -> Y, onde X é o número de 6 dígitos cujos dígitos devem ser utilizados e Y é o número de 6 dígitos que, quando considerado como um tempo (inserindo: apropriadamente), é mínimo.


2
Eu acho que você pode golfe {0,1,10,100,1000,10000,100000}para {0,1,10,100,1e3,1e4,1e5}. Além disso, você pode golfe for(I=0;I<F;++I){E=O%10;M=G(O/10,L*10+E,F-1);T=T>M?M:T;O=(O/10)+E*D[F];}para for(I=0;I++<F;M=G(O/10,L*10+E,F-1),T=T>M?M:T,O=(O/10)+E*D[F])E=O%10;, e retirar os suportes em torno da if. Experimente online 174 bytes . Além disso, eu gosto do G(O,L,F,T,I,M,E). :)
Kevin Cruijssen

Engraçado, na minha máquina usando ...1e3,1e4,1e5}não funcionou. Obrigado pela sugestão.
LambdaBeta

Você está em toda parte nas respostas desse cara, @ceilingcat, bom golfe por sinal.
Zachary

Agradeço a compreensão. É sempre bom ver que as pessoas realmente leem as respostas e encontram maneiras de melhorá-las. :) Você também está em todos os lugares agora.
LambdaBeta


6

Haskell , 114 96 86 bytes

import Data.List
f l=minimum[x|x@[a,b,c,d,e,f]<-permutations l,a:[b]<"24",c<'6',e<'6']

Agora com saída menos rigorosa. Recebe a entrada como uma sequência de dígitos e compara permutações contra limites com a comparação de lista. Com minutos e segundos, apenas o primeiro dígito é verificado. Falha e queima se nenhuma permutação é um tempo válido.

Experimente online!


5

Python 2 , 131 115 112 109 105 88 bytes

lambda s:min(d for d in permutations(s)if(2,4)>d[:2]>d[4]<6>d[2])
from itertools import*

Experimente online!

E / S são listas de números inteiros

Lança um erro se não houver tempos possíveis


Alternativo:

Python 2 , 88 bytes

lambda s:max(d*((2,4)>d[:2]>d[4]<6>d[2])for d in permutations(s))
from itertools import*

Experimente online!

Retorna a última hora

Retorna uma tupla vazia por horários inválidos


Salvou

  • -21 bytes, graças a ovs

5

05AB1E , 20 15 bytes

Entrada como sequência classificada.
A saída é o menor tempo como uma string.
No caso de não haver solução, uma lista vazia é a saída.

œʒ2ô•3Èñ•2ô‹P}н

Experimente online!


5

JavaScript (ES6), 93 89 88 bytes

Espera uma matriz de 6 dígitos, classificados do menor para o maior. Retorna a sequência de 6 dígitos da primeira hora válida ou falsese não houver solução.

f=(a,t='')=>t<24e4&/..([0-5].){2}/.test(t)?t:a.some((v,i)=>s=f(a.filter(_=>i--),t+v))&&s

Experimente online!

Comentado

Tentamos recursivamente todas as permutações da entrada até encontrarmos uma que seja aprovada em um teste híbrido usando expressões aritméticas e regulares.

f = (                       // f = recursive function taking
  a, t = ''                 // a[] = input array and t = current time
) =>                        //
  t < 24e4 &                // if t is less than 240000
  /..([0-5].){2}/.test(t) ? // and it matches "hhMmSs" with M and S in [0-5]:
    t                       //   return t
  :                         // else:
    a.some((v, i) =>        //   for each digit v at position i in a[]:
      s = f(                //     save in s the result of a recursive call with:
        a.filter(_ => i--), //       a copy of a[] with the current digit removed
        t + v               //       the current digit appended to t
      )                     //     end of recursive call
    ) && s                  //   end of some(); if truthy, return s

5

Japonês , 17 bytes

Recebe a entrada como uma sequência de dígitos e gera a primeira hora válida; faz loop infinitamente se não houver tempo válido.

á
@øXr':}a@ÐX ¤¯8

Tente

Aviso: extremamente lento - adicione *1000após o segundo Xpara acelerar um pouco. E não esqueça que uma entrada inválida criará um loop infinito e poderá travar seu navegador.


Explicação

                   :Implicit input of string U
á                  :Get all permutations of U
\n                 :Reassign that array to U
      }a           :Loop until true and then return the argument that was passed
        @          :By default that argument is an integer X which increments on each loop so first we'll pass X through a function
         ÐX        :  new Date(X)
            ¤      :  Get the time
             ¯8    :  Slice to the 8th character to get rid of the timezone info
@                  :The function that tests for truthiness
  Xr':             :  Remove all colons in X
 ø                 :  Does U contain the resulting string?

5

Retina , 77 74 69 65 62 bytes

$
:
6+Lv$`(.)(.*):
$%`$2:$1$%'
O`
0L`([01].|2[0-3])([0-5].){2}

Experimente online! Produz o horário mais cedo ou a sequência vazia, se não for possível encontrar tempo. Editar: salvou 5 8 bytes graças a @TwiNight. Explicação:

$
:
6+Lv$`(.)(.*):
$%`$2:$1$%'

Gere todas as permutações. Ele :funciona da mesma maneira que a string à medida que as permutações são geradas, terminando no início.

O`

Classifique os horários em ordem.

0L`([01].|2[0-3])([0-5].){2}

Saída a primeira hora válida.


Desde que você pode saída dígitos separados por nova linha, você pode salvar 5 bytes
TwiNight

Você pode até remover o: no estágio grep, pois ele deve corresponder a 6 caracteres e o primeiro deve ser 0, 1 ou 2
TwiNight

@TwiNight Oh, se o Grep for menor do que eu posso salvar mais 4 bytes de qualquer maneira.
1111 Neil

Ah, sim, você pode simplesmenteL0
TwiNight

@TwiNight, 0Gna verdade.
1111 Neil

4

Vermelho , 157 124 bytes

Agradeço a Kevin Cruijssen por me lembrar de ler as descrições com mais cuidado!

func[s][a: 0:0:0 loop 86400[b: to-string a a: a + 1 if b/1 =#"0"[insert b"0"]if s = sort replace/all copy b":"""[return b]]]

Experimente online!

Toma uma sequência classificada como entrada. Retorna nonese não for possível ganhar tempo.

Explicação:

f: func[s][                                    ; the argument is a sorted string of digits
    a: 0:0:0                                   ; time object, set to 00:00:00 h 
    loop 86400 [                               ; loop through all seconds in 24 h
        b: to-string a                         ; convert the time to string 
        a: a + 1                               ; add 1 second to the current time   
        if b/1 = #"0" [                        ; prepend "0" if necessary
            insert b "0"                       ; (Red omits the leading 0)
        ]
        if s = sort replace/all copy b ":" ""[ ; compare the input with the sorted time
            return b                           ; return it if they are equal 
        ]
    ]
]

3
O sortinício é necessário? No desafio, declaro: " Você pode pegar os dígitos na ordem que desejar, para que eles já possam ser classificados do menor para o maior ou vice-versa. "
Kevin Cruijssen

@ Kevin Cruijssen - Não, não é necessário neste caso. Vou atualizar a solução para trabalhar com entrada classificada. Obrigado!
Galen Ivanov

3

Python 2 , 78 bytes

lambda s:min(x for x in range(62**3)if x%100<60>x/100%100<s==sorted('%06d'%x))

Experimente online!

Arnauld salvou um byte. Obrigado!

Espera uma lista como ['1','2','3','4','6','8']na ordem classificada:

Você pode pegar os dígitos na ordem que desejar, para que eles já possam ser classificados do menor para o maior ou vice-versa.

Emite um número inteiro como 12364812:36:48. Espero que seja aceitável.


2
Você poderia usar em 62**3vez de 240000?
Arnauld


3

Japt , 39 23 bytes

Tenho certeza de que há uma maneira mais curta de fazer isso, mas eu queria tentar usar objetos Date em Japt.

á ®¬ò q':Ãf@T<ÐXiSiKÅ
Ì

á                     // Get all permutations of the input array.
  ®¬ò q':à            // [1,2,3,4,5,6] -> "12:34:56"
          f@          // Filter the results, keeping those that
            T<        // are valid dates
              ÐXiSiKÅ // when made into a date object.
Ì                     // Return the last remaining item, if any.

Recebe a entrada como uma matriz classificada de números, retorna o tempo válido mais recente ou a saída vazia, se não houver nenhuma.
Perdeu 10 libras bytes graças a Shaggy .

Experimente aqui .



@ Shaggy Obrigado, muito legal. Até agora, eu não sabia que o Japt tinha uma seção separada nos documentos do método para datas, o que me parece uma bobagem, eu meio que tentei contornar o fato de não tê-los.
Nit

3

Ruby , 68 67 62 56 55 bytes

->*b{b.permutation.find{|a,b,c,d,e|c<6&&e<6&&a*9+b<22}}

Experimente online!

Entrada: Ordenado matriz de dígitos (como números inteiros).

Saída: matriz de dígitos ou nilse nenhuma solução encontrada


Você pode largar o espaço, eval "eu acho.
Kevin Cruijssen

Sim, funciona, obrigado.
GB

Eu acho que você pode fazer a*9+b<22por um byte.
Jayce

2

Geléia , 17 bytes

Estou quase certo de que essa não é a abordagem mais curta ... analisarei isso novamente mais tarde :)

Œ!s2Ḍ<ẠʋÐṀ“ð<<‘ṢḢ

Experimente online!


Você está correto, essa não é a abordagem mais curta. No entanto, a humilhação pública não é boa, então não vou comentar sobre as melhorias ainda. :)
Erik the Outgolfer

O fato de que a entrada pode ser assumida como classificada economiza um pouco. Não é humilhante ver melhor depois de passar apenas 2 minutos em um golfe!
Jonathan Allan

Para ser mais preciso, você pode fazer isso em 15 bytes; para salvar um byte, você deve fazer algo trivial; para salvar o outro, não é tão trivial. Veja bem, eu ia postar essa versão de 15 bytes, mas ela usa sua abordagem. A propósito, sua saúde está boa?
Erik the Outgolfer

Vá em frente e poste. A saúde é boa, estou no trabalho, então não posso gastar tempo jogando golfe !!
Jonathan Allan

Publicado. Agora você pode ver porque eu estava me referindo à "humilhação". : P
Erik the Outgolfer

2

Wolfram Language (Mathematica) , 63 bytes

FirstCase[Permutations@#,{a:0|1|2,b_,c_,_,d_,_}/;a*b-4<6>d>=c]&

Experimente online!

Leva uma lista classificada de dígitos como entrada. Retorna Missing[NotFound]para entradas inválidas.

Explicação

Permutations@#

Encontre todas as permutações da entrada. Como a entrada é classificada, é garantido que todos os tempos válidos estejam em ordem crescente.

FirstCase[ ... ]

Encontre a primeira lista que corresponde ...

{a:0|1|2,b_,c_,_,d_,_}

O primeiro elemento, rotulado a, é 0, 1 ou 2 e rotula o segundo, terceiro e quinto elementos b,c e drespectivamente ...

... /;a*b-4<6>d>=c

... a*bseja inferior a 10, ed e cseja menor que 6, com d >= c.

O truque é que, para todos os números 00até 24, o produto dos dois dígitos é no máximo 9 e os possíveis números inválidos 25para 29(já que forçamos o primeiro dígito a ser 0, 1 ou 2) têm o produto de pelo menos 10.


2

Pitão , 37 bytes

j\:hf&&<shT24<s@T1 60<seT60mcs`Md2S.p

Suíte de teste

Explicação:
j\:hf&&<shT24<s@T1 60<seT60mcs`Md2S.pQ # Code with implicit variables
   h                                   # The first element of
                                   .pQ # The list of all permutations of the input list
                                  S    # Sorted
                           mcs`Md2     # Mapped to three two digit long strings
    f                                  # Filtered on whether
       <shT24                          #  The first number is less than 24
      &      <s@T1 60                  #  AND the second number is less than 60
     &               <seT60            #  AND the third number is less than 60
j\:                                    # Joined by a colon

2

Perl 5 com -palF, 73 bytes

$"=",";($_)=grep@F~~[sort/./g]&/([01]\d|2[0-3])([0-5]\d){2}/,glob"{@F}"x6

Experimente online!

Produz como HHmmsse gera uma linha em branco para entradas inválidas.

Todas as respostas que eu fiz recentemente foram usadas globpara permutações ... Estranho!


2

Bash + GNU sed, 83 , 72 , 69 bytes

  • Aceita entrada como 6 argumentos separados;
  • Retorna o horário mais antigo (se encontrado);
  • Não retorna nada (saída vazia) se nenhuma combinação válida existir.

seq 0 86399|sed "s/^/date +%T -ud@/e;h;`printf s/%d//\; $@`/\w/d;x;q"

Como funciona

Gere previamente todas as seqüências de tempo possíveis, para os carimbos de data e hora no intervalo de 0 a 86399, usando o comando GNU-sed e (xecute) + date.

%seq 0 86399|sed "s/^/date +%T -ud@/e;h;"

00:00:00
00:00:01
...
23:59:59

Gere sedscript com 6 comandos de substituição seqüencial, para cada dígito de entrada.

%echo sed `printf s/%d//\; $@`

sed s/1//;s/2//;s/3//;s/4//;s/6//;s/8//;

Em seguida, aplique substituições, remova quaisquer linhas de entrada que tenham pelo menos um dígito restante, imprima a primeira linha correspondente (a seqüência de tempo original é extraída do espaço de espera com x).

%echo 23:45:12|sed 's/1//;s/2//;s/3//;s/4//;s/6//;s/8//;'
:5:2 //non-matching, delete

%echo 12:36:48|sed 's/1//;s/2//;s/3//;s/4//;s/6//;s/8//;'
:: //matching, print and stop

Teste

%./timecomb 1 2 3 4 6 8
12:36:48
%./timecomb 2 5 5 5 5 5
%./timecomb 0 0 0 1 1 1
00:01:11
%./timecomb 1 1 2 2 3 3
11:22:33
%./timecomb 9 9 9 9 9 9
%./timecomb 2 3 5 5 9 9
23:59:59
%./timecomb 1 2 3 4 5 6
12:34:56
%./timecomb 0 0 0 0 0 0
00:00:00
%./timecomb 1 5 5 8 8 8
18:58:58
%./timecomb 1 5 5 5 8 8
15:58:58
%./timecomb 1 1 1 8 8 8
18:18:18

Experimente Online!


2

Kotlin , 396 391 389 bytes

Nenhuma pista de como fazer isso menor. Estou pensando que é o dobro do que é possível. Produz o primeiro tempo. Graças a Kevin por 7 bytes!

fun p(d:Array<Int>)={val s=Array(6,{0})
val f=Array(6,{1>0})
val t=Array(3,{0})
val o=Array(3,{60})
fun r(i:Int){if(i>5){var l=0>1
var e=!l
for(p in 0..2){t[p]=s[p*2]*10+s[p*2+1]
l=l||(e&&t[p]<o[p])
e=e&&t[p]==o[p]}
if(t[0]<24&&t[1]<60&&t[2]<60&&l)for(p in 0..2)o[p]=t[p]}
else
for(p in 0..5)if(f[p]){f[p]=0>1
s[i]=d[p]
r(i+1)
f[p]=1>0}}
r(0)
if(o[0]>23)0
else "${o[0]}:${o[1]}:${o[2]}"}()

Experimente online!


2
Não conheço Kotlin, mas você realmente precisa de ambos var l=0>1e var e=1>0? Além disso, por que são l=le e=enecessários? Duas coisas que parecem trabalho para golfe são var e=1>0a var e=!le removendo o espaço antes "None". Além disso, qualquer saída falsey é boa, então "None"também pode ser justa 0.
Kevin Cruijssen

@ Kevin obrigado pelos 5 bytes. Chocado, senti falta de um deles. Como não estou abortando o loop, não consigo ver de qualquer maneira para evitar saber se as duas vezes permaneceram iguais para que eu possa decidir que a nova é menor. Eu codifiquei várias maneiras e isso acabou sendo o mais curto. No entanto, o código geral é muito maior do que eu gosto.
JohnWells

11
Mais 2 bytes para o golfe em sua última versão: "0"pode ser apenas0
Kevin Cruijssen

@ Kevin que não será do tipo String e eu teria que adicionar: Qualquer um para permitir que String e Int.
JohnWells

11
Hmm ok. Porém, ele funciona no TIO e ainda imprime os 0erros sem erros. E sua função atual não especifica um tipo de retorno, tanto quanto eu poderia dizer, portanto, de qualquer forma, ele não retornará implicitamente como um objeto? PS: Eu não conheço Kotlin, apenas tentei sem as aspas e os resultados foram os mesmos. ;) Talvez algo mais não esteja funcionando por causa disso, do qual não estou ciente.
Kevin Cruijssen

2

MATL , 31 30 bytes

Y@3Xy[X1]X*!Y*t[4XX]6*<!AY)1Y)

Experimente online!

A entrada é 6 números inteiros, a saída é a hora, minutos e segundos mínimos em uma matriz. Falha nas entradas onde esse tempo não é possível.

(-1 byte graças a @Luis Mendo.)


Eu acho que você pode substituir 2&Apor !A, porque a matriz binária nunca será um vetor linha
Luis Mendo


1

Stax , 15 bytes

╝a╣=→aá≈#8(⌂≈58

Execute e depure

É necessária uma sequência de dígitos classificados para entrada. Retorna a primeira permutação que satisfaz alguns critérios.

  • lexicograficamente menor que "24"
  • todos os três pares de caracteres são lexicograficamente menores que "6"

1

Retina , 58 47 bytes

+,V^2`[0-5][6-9]{2}
G`([01].|2[0-3])([0-5].){2}

Experimente online!

A entrada tem 6 dígitos na ordem classificada. A saída é de 6 dígitos que representam o tempo válido mais antigo ou a sequência vazia, se não houver tempo válido.

EDIT: eu era um idiota, -9 bytes

Explicação

Algoritmo

Para resumir, vamos definir um dígito baixo como 0-5 e um dígito alto como 6-9.

Primeiro, reorganize os dígitos para que "baixo nível" ou "alto nível" de cada posição esteja correto. A organização correta, para cada número de dígitos altos na entrada:

# of highs  arrangment
0           LLLLLL
1           LLLLLH
2           LLLHLH
3           LHLHLH
4+          Not possible

Como qualquer rearranjo falharia, a verificação final na entrada tem mais de 4 dígitos altos, podemos ignorar completamente esse caso.

Em seguida, classifique os pontos baixos e máximos individualmente. Combinado com o rearranjo, obtém o valor mais baixo que satisfaz as restrições de minuto e segundo. Portanto, isso fornece o tempo válido mais cedo, se houver.

Por fim, verifique se temos tempo válido. Caso contrário, descarte a string.


Programa

+,V^2`[0-5][6-9]{2}

Corresponde LHHe troca os dois primeiros dígitos em que (se torna HLH) e repita isso até que não LHHexista mais . Isso fornece o arranjo correto.

Na verdade, eu menti. Nenhuma classificação é necessária porque 1) a troca ocorre apenas entre dígitos adjacentes e apenas entre um baixo e um alto; e 2) a entrada é classificada. Portanto, os mínimos e máximos individualmente já estão em ordem ordenada.

G`([01].|2[0-3])[0-5].[0-5].

Mantém a string apenas se for um tempo válido

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.