# Digit sum.
# In mathematics, the digit sum of a natural number in a given number base
# is the sum of all its digits. For example, the digit sum of the decimal
# number 9045 would be 9 + 0 + 4 + 5 = 18.
# @see en.wikipedia.org/wiki/Digit_sum
import sys
import functools
import unittest
import timeit
def digit_sum_1( n: int ) -> int :
if n < 0 :
raise ValueError ( 'n must be non-negative' )
a = 0
while n > 0 :
n, a = n // 10 , a + n % 10
return a
def digit_sum_2( n: int ) -> int :
if n < 0 :
raise ValueError ( 'n must be non-negative' )
return sum ( map ( int , str ( n) ) )
def digit_sum_3( n: int ) -> int :
if n < 0 :
raise ValueError ( 'n must be non-negative' )
return functools.reduce ( lambda a, x: a + x, map ( int , str ( n) ) )
def digit_sum_4( n: int ) -> int :
if n < 0 :
raise ValueError ( 'n must be non-negative' )
def loop( x) :
if x == 0 :
return 0
else :
return x % 10 + loop( x // 10 )
return loop( n)
def digit_sum_5( n: int ) -> int :
if n < 0 :
raise ValueError ( 'n must be non-negative' )
def loop( x, a) :
if x == 0 :
return a
else :
return loop( x // 10 , a + x % 10 )
return loop( n, 0 )
# Test.
class TestDigitSum( unittest .TestCase ) :
def _test_digit_sum( self , f) :
with self .assertRaisesRegex ( ValueError , 'negative' ) :
f( -1 )
self .assertEqual ( f( 0 ) , 0 )
self .assertEqual ( f( 123456789 ) , 45 )
for i in range ( 10 ) :
self .assertEqual ( f( 10 **i*2 -1 ) , 1 +9 *i) # 1, 19, 199, ...
def test_digit_sum_1( self ) :
self ._test_digit_sum( digit_sum_1)
def test_digit_sum_2( self ) :
self ._test_digit_sum( digit_sum_2)
def test_digit_sum_3( self ) :
self ._test_digit_sum( digit_sum_3)
def test_digit_sum_4( self ) :
self ._test_digit_sum( digit_sum_4)
def test_digit_sum_5( self ) :
self ._test_digit_sum( digit_sum_5)
def benchmark_digit_sum( n) :
def benchmark( f) :
def bench( ) :
for i in range ( n) :
f( 10 **( i%10 ) *2 -1 ) # 1, 19, 199, ...
return f.__name__, timeit .timeit ( bench, number= 1 )
fs = [
digit_sum_1,
digit_sum_2,
digit_sum_3,
digit_sum_4,
digit_sum_5
]
for name, t in map ( benchmark, fs) :
print ( f'{name}: {t:.6f}s' )
if __name__ == '__main__' :
print ( 'Running unit tests ...' , file = sys .stderr )
unittest .main ( verbosity= 2 , exit= False )
print ( '\n Benchmarking ...' )
benchmark_digit_sum( 10000 )
IyBEaWdpdCBzdW0uCiMgSW4gbWF0aGVtYXRpY3MsIHRoZSBkaWdpdCBzdW0gb2YgYSBuYXR1cmFsIG51bWJlciBpbiBhIGdpdmVuIG51bWJlciBiYXNlCiMgaXMgdGhlIHN1bSBvZiBhbGwgaXRzIGRpZ2l0cy4gIEZvciBleGFtcGxlLCB0aGUgZGlnaXQgc3VtIG9mIHRoZSBkZWNpbWFsCiMgbnVtYmVyIDkwNDUgd291bGQgYmUgOSArIDAgKyA0ICsgNSA9IDE4LgojIEBzZWUgZW4ud2lraXBlZGlhLm9yZy93aWtpL0RpZ2l0X3N1bQoKaW1wb3J0IHN5cwppbXBvcnQgZnVuY3Rvb2xzCmltcG9ydCB1bml0dGVzdAppbXBvcnQgdGltZWl0CgpkZWYgZGlnaXRfc3VtXzEobjogaW50KSAtPiBpbnQ6CiAgICBpZiBuIDwgMDoKICAgICAgICByYWlzZSBWYWx1ZUVycm9yKCduIG11c3QgYmUgbm9uLW5lZ2F0aXZlJykKICAgIGEgPSAwCiAgICB3aGlsZSBuID4gMDoKICAgICAgICBuLCBhID0gbiAvLyAxMCwgYSArIG4gJSAxMAogICAgcmV0dXJuIGEKCmRlZiBkaWdpdF9zdW1fMihuOiBpbnQpIC0+IGludDoKICAgIGlmIG4gPCAwOgogICAgICAgIHJhaXNlIFZhbHVlRXJyb3IoJ24gbXVzdCBiZSBub24tbmVnYXRpdmUnKQogICAgcmV0dXJuIHN1bShtYXAoaW50LCBzdHIobikpKQoKZGVmIGRpZ2l0X3N1bV8zKG46IGludCkgLT4gaW50OgogICAgaWYgbiA8IDA6CiAgICAgICAgcmFpc2UgVmFsdWVFcnJvcignbiBtdXN0IGJlIG5vbi1uZWdhdGl2ZScpCiAgICByZXR1cm4gZnVuY3Rvb2xzLnJlZHVjZShsYW1iZGEgYSwgeDogYSArIHgsIG1hcChpbnQsIHN0cihuKSkpCgpkZWYgZGlnaXRfc3VtXzQobjogaW50KSAtPiBpbnQ6CiAgICBpZiBuIDwgMDoKICAgICAgICByYWlzZSBWYWx1ZUVycm9yKCduIG11c3QgYmUgbm9uLW5lZ2F0aXZlJykKICAgIGRlZiBsb29wKHgpOgogICAgICAgIGlmIHggPT0gMDoKICAgICAgICAgICAgcmV0dXJuIDAKICAgICAgICBlbHNlOgogICAgICAgICAgICByZXR1cm4geCAlIDEwICsgbG9vcCh4IC8vIDEwKQogICAgcmV0dXJuIGxvb3AobikKCmRlZiBkaWdpdF9zdW1fNShuOiBpbnQpIC0+IGludDoKICAgIGlmIG4gPCAwOgogICAgICAgIHJhaXNlIFZhbHVlRXJyb3IoJ24gbXVzdCBiZSBub24tbmVnYXRpdmUnKQogICAgZGVmIGxvb3AoeCwgYSk6CiAgICAgICAgaWYgeCA9PSAwOgogICAgICAgICAgICByZXR1cm4gYQogICAgICAgIGVsc2U6CiAgICAgICAgICAgIHJldHVybiBsb29wKHggLy8gMTAsIGEgKyB4ICUgMTApCiAgICByZXR1cm4gbG9vcChuLCAwKQoKIyBUZXN0LgoKY2xhc3MgVGVzdERpZ2l0U3VtKHVuaXR0ZXN0LlRlc3RDYXNlKToKICAgIGRlZiBfdGVzdF9kaWdpdF9zdW0oc2VsZiwgZik6CiAgICAgICAgd2l0aCBzZWxmLmFzc2VydFJhaXNlc1JlZ2V4KFZhbHVlRXJyb3IsICduZWdhdGl2ZScpOgogICAgICAgICAgICBmKC0xKQogICAgICAgIHNlbGYuYXNzZXJ0RXF1YWwoZigwKSwgMCkKICAgICAgICBzZWxmLmFzc2VydEVxdWFsKGYoMTIzNDU2Nzg5KSwgNDUpCiAgICAgICAgZm9yIGkgaW4gcmFuZ2UoMTApOgogICAgICAgICAgICBzZWxmLmFzc2VydEVxdWFsKGYoMTAqKmkqMi0xKSwgMSs5KmkpICMgMSwgMTksIDE5OSwgLi4uCgogICAgZGVmIHRlc3RfZGlnaXRfc3VtXzEoc2VsZik6CiAgICAgICAgc2VsZi5fdGVzdF9kaWdpdF9zdW0oZGlnaXRfc3VtXzEpCgogICAgZGVmIHRlc3RfZGlnaXRfc3VtXzIoc2VsZik6CiAgICAgICAgc2VsZi5fdGVzdF9kaWdpdF9zdW0oZGlnaXRfc3VtXzIpCgogICAgZGVmIHRlc3RfZGlnaXRfc3VtXzMoc2VsZik6CiAgICAgICAgc2VsZi5fdGVzdF9kaWdpdF9zdW0oZGlnaXRfc3VtXzMpCgogICAgZGVmIHRlc3RfZGlnaXRfc3VtXzQoc2VsZik6CiAgICAgICAgc2VsZi5fdGVzdF9kaWdpdF9zdW0oZGlnaXRfc3VtXzQpCgogICAgZGVmIHRlc3RfZGlnaXRfc3VtXzUoc2VsZik6CiAgICAgICAgc2VsZi5fdGVzdF9kaWdpdF9zdW0oZGlnaXRfc3VtXzUpCgpkZWYgYmVuY2htYXJrX2RpZ2l0X3N1bShuKToKICAgIGRlZiBiZW5jaG1hcmsoZik6CiAgICAgICAgZGVmIGJlbmNoKCk6CiAgICAgICAgICAgIGZvciBpIGluIHJhbmdlKG4pOgogICAgICAgICAgICAgICAgZigxMCoqKGklMTApKjItMSkgIyAxLCAxOSwgMTk5LCAuLi4KICAgICAgICByZXR1cm4gZi5fX25hbWVfXywgdGltZWl0LnRpbWVpdChiZW5jaCwgbnVtYmVyPTEpCgogICAgZnMgPSBbCiAgICAgICAgZGlnaXRfc3VtXzEsCiAgICAgICAgZGlnaXRfc3VtXzIsCiAgICAgICAgZGlnaXRfc3VtXzMsCiAgICAgICAgZGlnaXRfc3VtXzQsCiAgICAgICAgZGlnaXRfc3VtXzUKICAgIF0KCiAgICBmb3IgbmFtZSwgdCBpbiBtYXAoYmVuY2htYXJrLCBmcyk6CiAgICAgICAgcHJpbnQoZid7bmFtZX06IHt0Oi42Zn1zJykKCmlmIF9fbmFtZV9fID09ICdfX21haW5fXyc6CiAgICBwcmludCgnUnVubmluZyB1bml0IHRlc3RzIC4uLicsIGZpbGU9c3lzLnN0ZGVycikKICAgIHVuaXR0ZXN0Lm1haW4odmVyYm9zaXR5PTIsIGV4aXQ9RmFsc2UpCiAgICBwcmludCgnXG5CZW5jaG1hcmtpbmcgLi4uJykKICAgIGJlbmNobWFya19kaWdpdF9zdW0oMTAwMDAp