# -*- coding: cp1252 -*-
from random import randint

symbols = '0123456789abcdefghijklmnopqrstuvwxyzåäö'
symbol_values = dict(reversed(v) for v in enumerate(symbols))

def int_to_base(number, base):
    if base > len(symbols) or base < 2:
        raise ValueError('Invalid base %i' % base)
    s = ''
    if number < 0:
        number, neg = -number, True
    else:
        neg = False
        
    while number:
        number, this = divmod(number, base)
        s = symbols[this] + s 

    return (neg * '-' ) + s

def value_in_base(string, base):
    minus = string.lstrip().startswith('-')       
    v = sum(symbol_values[sym] * base ** power
               for power, sym in enumerate(reversed(string.strip(' -'))))
    return -v if minus else v

num_check = 100
for count in range(num_check):
    num, b = randint(-100000, 10000), randint(2,len(symbols))
    print('Original %i' % num)
    v = int_to_base(num, b)
    print('%s base %i' % (v, b))
    print(value_in_base(v, b))
    if b < 37:
        print('check: %s\n' % int(v, b))
        assert int(v, b) == num

Specialties:
IT/Science/Contracts/Religious translation/interpreting FIN-ENG-FIN
Python programming

Original was not really buggy, but was missing base check for other function, so here one slightly improved, with also balanced range for testing, instead of mostly negative:

# -*- coding: cp1252 -*-
from random import randint

symbols = '0123456789abcdefghijklmnopqrstuvwxyzåäö'
symbol_values = dict(reversed(v) for v in enumerate(symbols))

def int_to_base(number, base):
    if base > len(symbols) or base < 2:
        raise ValueError('Invalid base %i' % base)
    s = ''
    if number < 0:
        number, neg = -number, True
    else:
        neg = False
        
    while number:
        number, this = divmod(number, base)
        s = symbols[this] + s 

    return str((neg * '-' ) + s)

def value_in_base(string, base):
    if base > len(symbols) or base < 2:
        raise ValueError('Invalid base %i' % base)
    minus = string.lstrip().startswith('-')       
    v = sum(symbol_values[sym] * base ** power
               for power, sym in enumerate(reversed(string.lower().strip(' -'))))
    return -v if minus else v

num_check = 100
for count in range(num_check):
    num, b = randint(-10000, 10000), randint(2,len(symbols))
    print('\nOriginal %i' % num)
    v = int_to_base(num, b)
    print('%s base %i' % (v, b))
    print(value_in_base(v, b))
    assert value_in_base(v, b) == num

What is missing is unary coding. Also if all symbols would be defined for UTF-16, big numbers could be printed in UTF-16 in base 65536, otherwise with printable set of 256 letters, numbers binary representation could be easily printed as base 256, one letter per byte.

Edited 5 Years Ago by pyTony: Spelling

Of course the bug had to be there, if number is 0, the loop is not entered so line 20 must be revised to:

return str((neg * '-') + s) if s else '0'

Edited 5 Years Ago by pyTony: n/a

To be more restrictive on value of the symbols in numbers you can initialize each time the dictionary for acceptable symbols:

def value_in_base(string, base):
    if base > len(symbols) or base < 2:
        raise ValueError('Invalid base %i' % base)
    symbol_values = dict(reversed(v) for v in enumerate(symbols[:base]))
    minus = string.lstrip().startswith('-')
    v = sum(symbol_values[sym] * base ** power
               for power, sym in enumerate(reversed(string.lower().lstrip(' -'))))
    return -v if minus else v

Or you can be satisfied with simple linear search:

def value_in_base(string, base):
    if base > len(symbols) or base < 2:
        raise ValueError('Invalid base %i' % base)
    minus = string.lstrip().startswith('-')
    string = string.lower().lstrip('-')
    assert all(c in symbols[:base] for c in string)
    v = sum(symbols.find(sym) * base ** power
               for power, sym in enumerate(reversed(string)))
    return -v if minus else v

Random testing is bad practice.

If your test fails, you may not be able to recreate the scenario which caused the bug.

It is usually best to try to build both deterministic programs and unit-tests. (Unless randomness is strictly required in your application)

I have stated in post in related thread in C forum, that also planned corner cases are necessary, but in my opinion it is good to through in some random values to sometimes catch left out corner values. Of course that is connected with planning for the program, so for new program I think it is good to do the logic of program and test cases side by side. First test to pass would be example usage scenarios for the code. Finding bug places is usually closely rfelated to branching conditions, user input values, exceptions from IO...

Like Dijkstra said it is generally not possible to prove that bug does not exist, only that you have .

Edited 5 Years Ago by pyTony: n/a

The article starter has earned a lot of community kudos, and such articles offer a bounty for quality replies.