#!/usr/bin/python

import sys
import os
import skein
import random
from binascii import unhexlify
import re
import requests

qlen = 100
# hack to get submit to match local submit, unclear why is needed
# perhaps unprinting characters (punctuation?) are escaped in the post request
ranup = 122
randown = 97

HASH = skein.skein1024
DIGEST_BITS = 1024
DIGEST_BYTES = DIGEST_BITS // 8

search = ""

# search = unhexlify(sys.stdin.buffer.read())
# 
# atomically modified (best so far, # bits off)
best = (bytes([0 for i in range(qlen)]), DIGEST_BITS)

def countbits(byte):
  sum = 0
  while byte > 0:
    sum += byte & 1
    byte = byte >> 1
  return sum

def bitsoff(thehash):
  return sum(bitsoff.tbl[thehash[i] ^ search[i]] for i in range(DIGEST_BYTES))
bitsoff.tbl = [countbits(i) for i in range(256)]

def atomicupdate(bstring, bitsoff):
  global best
  if bitsoff < best[1]:
    best = (bytes(bstring), bitsoff)
    print(bitsoff, str(bstring))
    submit(bstring)

def incstr(bytearr):
  i = 0
  while bytearr[i]==ranup:
    bytearr[i] = randown 
    i += 1
  if i < qlen:
    bytearr[i] += 1

def findstart(start):
  while True:
    h = HASH(digest_bits=DIGEST_BITS)
    bstring = bytes(start)
    h.update(bstring)
    bits = bitsoff(h.digest())
    atomicupdate(bstring, bits)
    incstr(start)

def localsubmit(bstring):
  global search
  search = unhexlify(re.search("([0-9a-fA-F]+)<br>",
      requests.get("http://almamater.xkcd.com").text).group(1))
  h = HASH(digest_bits=DIGEST_BITS)
  h.update(bytes(bstring))
  print(bitsoff(h.digest()))

def submit(bstring):
  try:
    r=requests.post("http://almamater.xkcd.com/?edu=vanderbilt.edu",data={'hashable':bstring})
    print(r.status_code,r.reason,re.search("\d+ bits", r.text).group())
  except:
    print("POST ERROR: skipping", bstring)

def main():
  global search

  print("obtaining search string...")
  search = unhexlify(re.search("([0-9a-fA-F]+)<br>",
      requests.get("http://almamater.xkcd.com").text).group(1))

  print("looking for", search)

  findstart([random.randint(randown,ranup) for i in range(qlen)])


if __name__=="__main__":
  main()
