Saturday, January 31, 2015

Edward Lear's poems

Из детства ещё помню, найти бы эти записи, что показывали по ТВ, в таком переводе:

Где же где в каком искать далеке,
Зеленоликих, синеруких плывущих в дуршлаге.
Это из The Jumblies.

Чиппати флип, флиппати чип - Меня зовут Мерихлюндия Пип.
Как только его увидали все звери и тут же запели:
Клиппати чип, чиппати клип - Его зовут Мерихлюндия Пип.
Это из The Scroobious Pip.

И ещё алфавит был. Помню, про цинк:
Ц - это цинк...
Это из Alphabet Poem.

Wednesday, January 14, 2015

Статья про dict в Python 2.7

Сегодня опубликовали мою первую статью на хабре «Реализация словаря в Python 2.7».

Monday, January 12, 2015

Python numbers obfuscation

Python numbers obfuscation can be done the next way:

>>> {} > [] # i.e. 'd' > 'l' ([d]ict > [l]ist)
False 
>>> [] > {} # i.e. 'l' > 'd' ([l]ist > [d]ict)
True

And it's possible to get values 0 and 1 from False and True, by implicit converting bool to int.

>>> False == 0
True
>>> True == 1
True

Other numbers could be received by applying logical operations (like "~" = bitwise NOT, or "<<" bitwise left shift).
For example 2:
2 = -(-2) =  -(~1)

Let's take 16 or more first numbers, and all other numbers could be obtained by sum and/or multiplication of these first numbers.
nums = [
    '([]<{})',  # 0
    '([]>{})',  # 1 
    '-~([]>{})',  # 2   
    '-~-~([]>{})',  # 3
    '-~-~-~([]>{})',  # 4
    '-~-~-~-~([]>{})',  # 5
    '-~-~-~-~-~([]>{})',  # 6
    '-~-~-~-~-~-~([]>{})',  # 7
    '-~-~-~-~-~-~-~([]>{})',  # 8
    '~-~([]>{})*~-~([]>{})',  # 9
    '~-~-~-~([]>{})*~([]>{})',  # 10
    '-~-~(~-~([]>{})*~-~([]>{}))',  # 11
    '(-~-~([]>{})<<-~([]>{}))',  # 12
    '-~(-~-~([]>{})<<-~([]>{}))',  # 13
    '-~-~-~-~-~-~([]>{})*-~([]>{})',  # 14
    '~-~([]>{})*~-~-~-~([]>{})',  # 15
    '(-~([]>{})<<-~-~([]>{}))',  # 16
]
numbers_count = len(nums) - 1

def get_number(num):
    r = ''
    if num > numbers_count:
        if num // numbers_count > 1:
            if num % numbers_count == 0:
                r += '(%s*%s)' % (get_number(num // numbers_count), numbers_count)
            else:
                r += '(%s*%s+%s)' % (get_number(num // numbers_count), numbers_count, num % numbers_count)
        else:
            r += '(%s+%s)' % (numbers_count, get_number(num - numbers_count))
    else:
        r = '%s' % num
    return r

def convert_nums(s):
    res = s
    for n in xrange(numbers_count, -1, -1):
        res = res.replace(str(n), nums[n])
    res = res.replace('+-', '-')
    return res
Example:
num = 1234567
num_str = get_number(num)
num_str = num_str[1:-1] if num_str[0] == '(' and num_str[-1] == ')' else num_str
print num_str
#((((16+2)*16+13)*16+6)*16+8)*16+7
obfuscated = convert_nums(num_str)
print obfuscated
#(((((-~([]>{})<<-~-~([]>{}))-~([]>{}))*(-~([]>{})<<-~-~([]>{}))-~(-~-~([]>{})<<-~([]>{})))*(-~([]>{})<<-~-~([]>{}))-~-~-~-~-~([]>{}))*(-~([]>{})<<-~-~([]>{}))-~-~-~-~-~-~-~([]>{}))*(-~([]>{})<<-~-~([]>{}))-~-~-~-~-~-~([]>{})
print eval(obfuscated)
#1234567 
So here the number 1234567 was successfully obfuscated and restored using eval function.
1234567 = ((((16+2)*16+13)*16+6)*16+8)*16+7 = (((((-~([]>{})<<-~-~([]>{}))-~([]>{}))*(-~([]>{})<<-~-~([]>{}))-~(-~-~([]>{})<<-~([]>{})))*(-~([]>{})<<-~-~([]>{}))-~-~-~-~-~([]>{}))*(-~([]>{})<<-~-~([]>{}))-~-~-~-~-~-~-~([]>{}))*(-~([]>{})<<-~-~([]>{}))-~-~-~-~-~-~([]>{})

For negative numbers you only need to change the sign in front of the parentheses.

But for huge numbers like 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789, will be an error:
_push: parser stack overflow
Traceback (most recent call last):
  File "obfuscation.py", line 51, in 
    print eval(obfuscated)
MemoryError
So be careful and use nested parentheses carefully :)

The presentation with updated version of obfuscator (added Python 3 support):
http://speakerdeck.com/delimitry/numbers-obfuscation-in-python