x86-64 मशीन कोड, 24 बाइट्स
6A 0A 5E 31 C9 89 F8 99 F7 F6 01 D1 85 C0 75 F7 8D 04 09 99 F7 F7 92 C3
उपरोक्त कोड 64-बिट x86 मशीन कोड में एक फ़ंक्शन को परिभाषित करता है जो यह निर्धारित करता है कि इनपुट मान उसके अंकों के योग से दोगुना है या नहीं। फ़ंक्शन सिस्टम V AMD64 कॉलिंग कन्वेंशन के अनुरूप है, ताकि यह लगभग किसी भी भाषा से कॉल करने योग्य हो, जैसे कि यह एक सी फ़ंक्शन था।
यह EDI
कॉलिंग कन्वेंशन के अनुसार, रजिस्टर के माध्यम से इनपुट के रूप में एकल पैरामीटर लेता है , जो परीक्षण करने के लिए पूर्णांक है। (यह एक सकारात्मक पूर्णांक माना जाता है , जो चुनौती के नियमों के अनुरूप है, और जो CDQ
निर्देश हम सही ढंग से काम करने के लिए उपयोग करते हैं , उसके लिए आवश्यक है।)
यह EAX
कॉलिंग कन्वेंशन के अनुसार, फिर से, रजिस्टर में अपना परिणाम देता है । परिणाम 0 होगा यदि इनपुट मान उसके अंकों के योग से विभाज्य था , और अन्यथा शून्य नहीं। (मूल रूप से, उलटा बूलियन, बिल्कुल चुनौती के नियमों में दिए गए उदाहरण की तरह।)
इसका सी प्रोटोटाइप होगा:
int DivisibleByDoubleSumOfDigits(int value);
यहाँ असंबद्ध असेंबली भाषा निर्देश दिए गए हैं, प्रत्येक निर्देश के उद्देश्य की संक्षिप्त व्याख्या के साथ एनोटेट किया गया है:
; EDI == input value
DivisibleByDoubleSumOfDigits:
push 10
pop rsi ; ESI <= 10
xor ecx, ecx ; ECX <= 0
mov eax, edi ; EAX <= EDI (make copy of input)
SumDigits:
cdq ; EDX <= 0
div esi ; EDX:EAX / 10
add ecx, edx ; ECX += remainder (EDX)
test eax, eax
jnz SumDigits ; loop while EAX != 0
lea eax, [rcx+rcx] ; EAX <= (ECX * 2)
cdq ; EDX <= 0
div edi ; EDX:EAX / input
xchg edx, eax ; put remainder (EDX) in EAX
ret ; return, with result in EAX
पहले ब्लॉक में, हम रजिस्टरों के कुछ प्रारंभिक आरंभ करते हैं:
PUSH
+ POP
निर्देशों का उपयोग धीमी लेकिन संक्षिप्त विधि के रूप में किया जाता है ESI
। इसे शुरू करने के लिए 10. यह आवश्यक है क्योंकि DIV
x86 पर निर्देश के लिए एक रजिस्टर ऑपरेंड की आवश्यकता होती है। (कोई ऐसा रूप नहीं है, जो तत्काल मान से विभाजित हो, कहते हैं, 10.)
XOR
ECX
रजिस्टर को खाली करने के लिए एक छोटे और तेज तरीके के रूप में उपयोग किया जाता है । यह रजिस्टर आगामी लूप के अंदर "संचायक" के रूप में काम करेगा।
- अंत में, इनपुट मूल्य (से
EDI
) की एक प्रति बनाई जाती है, और इसमें संग्रहीत किया जाता है EAX
, जिसे हम लूप के माध्यम से जाने के रूप में देखा जाएगा।
फिर, हम लूपिंग शुरू करते हैं और इनपुट मूल्य में अंकों को जोड़ते हैं। यह x86 DIV
इंस्ट्रक्शन पर आधारित है , जो EDX:EAX
अपने ऑपरेंड से विभाजित होता है, और भागफल में EAX
और शेष में रिटर्न करता है EDX
। हम यहां क्या करेंगे इनपुट मान को 10 से विभाजित करते हैं, जैसे कि शेष अंतिम स्थान में अंक है (जो हम अपने संचायक रजिस्टर में जोड़ देंगे ECX
), और भागफल शेष अंक है।
CDQ
शिक्षा की स्थापना की एक छोटी तरीका है EDX
0. यह वास्तव में करने के लिए साइन-फैली में मूल्य EAX
के लिए EDX:EAX
है, जो क्या है DIV
लाभांश के रूप में उपयोग करता है। हमें वास्तव में यहां साइन-एक्सटेंशन की आवश्यकता नहीं है, क्योंकि इनपुट वैल्यू अहस्ताक्षरित है, लेकिन CDQ
1 बाइट है, जैसा कि XOR
स्पष्ट उपयोग करने के लिए विरोध है EDX
, जो 2 बाइट्स होगा।
- फिर हम
DIV
आईडीई EDX:EAX
द्वारा ESI
(10)।
- शेष (
EDX
) संचयकर्ता ( ECX
) में जोड़ा जाता है ।
EAX
रजिस्टर (भागफल) अगर यह 0. के बराबर है तो, हम इसे अंकों के सभी के माध्यम से किया है और हम के माध्यम से आता है तो यह देखने के लिए परीक्षण किया जाता है। यदि नहीं, तो हमारे पास योग करने के लिए अभी भी अधिक अंक हैं, इसलिए हम लूप के शीर्ष पर वापस जाते हैं।
अंत में, लूप समाप्त होने के बाद, हम लागू करते हैं number % ((sum_of_digits)*2)
:
LEA
अनुदेश एक छोटी तरीके के रूप में प्रयोग किया जाता है गुणा करने के लिए ECX
2 से (या, समतुल्य रूप, जोड़ने ECX
(इस मामले में खुद के लिए), और परिणाम की दुकान एक अलग रजिस्टर में EAX
)।
(हम भी कर सकते थे add ecx, ecx
+ xchg ecx, eax
, दोनों 3 बाइट्स हैं, लेकिन LEA
निर्देश तेज और अधिक विशिष्ट है।)
- फिर, हम
CDQ
फिर से विभाजन की तैयारी करते हैं। क्योंकि EAX
सकारात्मक होगा (यानी, अहस्ताक्षरित), इस पर EDX
पहले की तरह ही शून्यकरण का प्रभाव है ।
- अगला विभाजन है, इस बार
EDX:EAX
इनपुट मान से विभाजित (एक अनमोल कॉपी जिसमें अभी भी रहता है EDI
)। यह मोडुलो के बराबर है, जिसमें शेष है EDX
। (भागफल भी डाला गया है EAX
, लेकिन हमें इसकी आवश्यकता नहीं है।)
- अंत में, हम
XCHG
(विनिमय) की सामग्री EAX
और EDX
। आम तौर पर, आप यहां करेंगे MOV
, लेकिन XCHG
केवल 1 बाइट (यद्यपि धीमी) है। क्योंकि EDX
विभाजन के बाद शेष रहता है, यह 0 होगा यदि मान समान रूप से विभाज्य या गैर-शून्य था अन्यथा। इस प्रकार, जब हम RET
कलश करते हैं, EAX
(परिणाम) 0 है यदि इनपुट मान को उसके अंकों के योग से दोगुना या गैर-शून्य से अन्यथा विभाजित किया गया था।
उम्मीद है कि स्पष्टीकरण के लिए पर्याप्त है।
यह सबसे छोटी प्रविष्टि नहीं है, लेकिन हे, ऐसा लग रहा है कि यह लगभग सभी गैर-गोल्फ भाषाओं में धड़कता है! :-)