#!/usr/bin/env python

import random
import copy
import math

def GenCorelationMatrix(letters):
    ''' Assume all letters are same length 
        The main diagon filled with zeros'''
    oneLetterLen = len(letters[0][0])
    matrix = [[0 for x in xrange(oneLetterLen)] for x in xrange(oneLetterLen)]
    for col1 in range(oneLetterLen-1):
        matrix[col1][col1] = 0
        for col2 in range(col1+1,oneLetterLen):
            value = 0
            for row in letters:
                for letter in row:
                    if letter[col1] == letter[col2]:
                        value += 1
                    else:
                        value -= 1
                    matrix[col1][col2] = value
                    matrix[col2][col1] = value
    return matrix

def MutateLetter(letter, percentOfMutation):
    '''
    Get a letter - 1d array of binary chars
    returns the same letter but with percentOfMutation
    '''
    samplesNumber = len(letter) * percentOfMutation / 100
    indexesToChange = random.sample(xrange(len(letter)), samplesNumber)

    mutation = list(copy.deepcopy(letter)) 
    for i in indexesToChange:
        mutation[i] = toggle(mutation[i])
    return "".join(mutation)

def toggle(value):
    ''' value is a string value of '0' or '1'
        returns the oposite value as a string '''
    return str(int(not(int(value))))

def recoverLetter(HopfieldMatrix, corruptedletter, bipolarMode=False):
    ''' Get the hopfield corelation matrix and some corrupted letter
        and returns the recovered letter'''
    recovered = list(copy.deepcopy(corruptedletter))
    allPositionsAgrees = False
    while not allPositionsAgrees:
        allPositionsAgrees = True
        indexes = range(len(recovered))
        random.shuffle(indexes)
        for pos in indexes:
            lastBit = recovered[pos]
            recovered[pos] = '0' #so that position will not take effect
            newValue = 0
            for i in range(len(recovered)):
                newValue += HopfieldMatrix[i][pos]*int(recovered[i])
                if bipolarMode and 0==int(recovered[i]):
                    newValue -= HopfieldMatrix[i][pos]
            if newValue >= 0:
                recovered[pos] = '1'
            if lastBit != recovered[pos]:
                allPositionsAgrees = False
    return "".join(recovered)
def mostFit(letters, letter):
    featness = 0
    let = list(letter)
    retLetter = None
    x = 0
    y = 0
    for row in range(len(letters)):
        for l in range(len(letters[row])):
            ll = list(letters[row][l])
            lfeatness = 0
            for i in range(len(ll)):
                if ll[i] == let[i]:
                    lfeatness += 1
            if abs(lfeatness) > featness:
                featness = abs(lfeatness)
                retLetter = copy.deepcopy(letters[row][l])
                x = row
                y = l
    return retLetter,featness,x,y


def printLetter(letter, _2dim=True):
    if not _2dim:
        print letter
        return
    rowLen = int(math.sqrt(len(letter)))
    for i in range(rowLen):
        print letter[i*rowLen:(i+1)*rowLen]

def Test0():
    letters = [['001010','111100', '010001', '011000', '101010']]
    hopfieldMatrix = GenCorelationMatrix(letters)
    print "matrix:"
    for row in hopfieldMatrix:
        print row
    print "training: %s"%str(letters)
    for i in range(2**6):
        x = str(bin(i)[2:])
        test = '0'*(6-len(x))+x
        rec = recoverLetter(hopfieldMatrix, test)
        print "%s --> %s "%(test, rec)

if __name__ == "__main__":
    Test0()

"""
Sample Run

matrix:
[0, -1, 1, 3, 1, -1]
[-1, 0, -1, 1, -5, 1]
[1, -1, 0, -1, 1, -5]
[3, 1, -1, 0, -1, 1]
[1, -5, 1, -1, 0, -1]
[-1, 1, -5, 1, -1, 0]
training: [['001010', '111100', '010001', '011000', '101010']]
000000 --> 101110 
000001 --> 110101 
000010 --> 101110
:
:
111101 --> 110101 
111110 --> 110101 
111111 --> 110101 
[Finished in 0.4s]
"""