x86-64 Codice macchina, 22 byte
48 B8 41 92 34 6D DB F7 FF FF 83 F9 40 7D 03 48 D3 E8 83 E0 01 C3
I byte precedenti definiscono una funzione nel codice macchina x86 a 64 bit che determina se il valore di input è un numero Chicken McNugget. Il singolo parametro intero positivo viene passato nel ECX
registro, in base alla convenzione di chiamata Microsoft a 64 bit utilizzata su Windows. Il risultato è un valore booleano restituito nel EAX
registro.
Mnemonici di assemblaggio non golfati:
; bool IsMcNuggetNumber(int n)
; n is passed in ECX
movabs rax, 0xFFFFF7DB6D349241 ; load a 64-bit constant (the bit field)
cmp ecx, 64
jge TheEnd ; if input value >= 64, branch to end
shr rax, cl
TheEnd:
and eax, 1 ; mask off all but LSB
ret
Ovviamente, ciò gioca pesantemente sulla soluzione di Anders Kaseorg in Python , in quanto si basa su un bit-field che rappresenta i valori che sono i numeri di Chicken McNugget. In particolare, ogni bit in questo campo che corrisponde a un numero Chicken McNugget valido è impostato su 1; tutti gli altri bit sono impostati su 0. (Questo considera 0 un numero Chicken McNugget valido, ma se non ti piace, la tua preferenza è una modifica a singolo bit di distanza.)
Iniziamo semplicemente caricando questo valore in un registro. Si tratta di un valore a 64 bit, che richiede già 8 byte per la codifica, inoltre abbiamo bisogno di un prefisso REX.W a un byte, quindi siamo davvero abbastanza spesi in termini di byte, ma questo è il cuore della soluzione, quindi Immagino ne valga la pena.
Spostiamo quindi il campo a destra in base al valore di input. * Infine, mascheriamo tutto tranne il bit di ordine più basso, e questo diventa il nostro risultato booleano.
Tuttavia, poiché non è possibile spostare più del numero di bit effettivamente presenti nel valore, questo funziona solo per input da 0 a 63. Per supportare valori di input più elevati, inseriamo un test nella parte superiore della funzione che si dirama verso la parte inferiore del valore di input è> = 64. L'unica cosa interessante di questo è il precarico della costante del campo di bit in RAX
, quindi ramo fino all'istruzione che maschera il bit di ordine più basso, assicurando così di restituire sempre 1.
Provalo online!
(La chiamata della funzione C è annotata con un attributo che fa sì che GCC la chiami utilizzando la convenzione di chiamata Microsoft utilizzata dal mio codice assembly. Se TIO avesse fornito MSVC, ciò non sarebbe necessario.)
__
* In alternativa a un turno, avremmo potuto usare l' BT
istruzione x86 , ma è 1 byte più lungo da codificare, quindi nessun vantaggio. A meno che non fossimo costretti a utilizzare una convenzione di chiamata diversa che non passasse convenientemente il valore di input nel ECX
registro. Questo sarebbe un problema perché SHR
richiede che il suo operando di origine sia CL
per un conteggio di turni dinamico. Pertanto, una convenzione di chiamata diversa richiederebbe di modificare MOV
il valore di input da qualsiasi registro in cui è stato passato ECX
, il che ci costerebbe 2 byte. L' BT
istruzione può utilizzare qualsiasi registro come operando di origine, al costo di solo 1 byte. Quindi, in quella situazione, sarebbe preferibile.BT
inserisce il valore del bit corrispondente nel flag carry (CF), quindi useresti aSETC
istruzioni per ottenere quel valore in un registro intero come in AL
modo che possa essere restituito al chiamante.
Implementazione alternativa, 23 byte
Ecco un'implementazione alternativa che utilizza le operazioni di modulo e moltiplicazione per determinare se il valore di input è un numero Chicken McNugget.
Utilizza la convenzione di chiamata AMD64 di System V , che passa il valore di input nel EDI
registro. Il risultato è ancora un booleano, restituito EAX
.
Si noti, tuttavia, che a differenza del codice sopra, questo è un valore booleano inverso (per comodità di implementazione). Restituisce false
se il valore di input è un numero Chicken McNugget o true
se il valore di input non è un numero Chicken McNugget.
; bool IsNotMcNuggetNumber(int n)
; n is passed in EDI
8D 04 3F lea eax, [rdi+rdi*1] ; multiply input by 2, and put result in EAX
83 FF 2B cmp edi, 43
7D 0E jge TheEnd ; everything >= 43 is a McNugget number
99 cdq ; zero EDX in only 1 byte
6A 03 push 3
59 pop rcx ; short way to put 3 in ECX for DIV
F7 F1 div ecx ; divide input value by 3
6B D2 14 imul edx, edx, 20 ; multiply remainder of division by 20
39 D7 cmp edi, edx
0F 9C C0 setl al ; AL = (original input) < (input % 3 * 20)
TheEnd:
C3 ret
La cosa brutta di questo è la necessità di gestire esplicitamente i valori di input> = 43 mediante un confronto e un ramo in alto. Esistono ovviamente altri modi per farlo che non richiedono il branching, come l'algoritmo caher coinheringaahing , ma questo richiederebbe molti più byte per codificare, quindi non è una soluzione ragionevole. Immagino che probabilmente mi sto perdendo qualche trucco bit-twiddling che renderebbe questo lavoro più elegante ed essere meno byte rispetto alla soluzione basata su bitfield sopra (poiché la codifica del bitfield stesso richiede così tanti byte), ma ho studiato questo per un po 'e ancora non riesco a vederlo.
Oh bene, provalo comunque online !