# Path: pycoind/util/pyscrypt/pbkdf2.py
# def pbkdf2(password, salt, count, key_length, prf):
#     '''Returns the result of the Password-Based Key Derivation Function 2.
#       prf - a psuedorandom function
# 
#        See http://en.wikipedia.org/wiki/PBKDF2
#     '''
# 
#     def f(block_number):
#         '''The function "f".'''
# 
#         U = prf(password, salt + struct.pack('>L', block_number))
# 
#         if count > 1:
#             U = [ c for c in U ]
#             for i in xrange(2, 1 + count):
#                 block_xor(prf(password, ''.join(U)), 0, U, 0, len(U))
#         U = ''.join(U)
# 
#         return U
# 
#     size = 0
# 
#     block_number = 0
#     blocks = [ ]
# 
#     # The iterations
#     while size < key_length:
#         block_number += 1
#         block = f(block_number)
# 
#         blocks.append(block)
#         size += len(block)
# 
#     return ''.join(blocks)[:key_length]
# 
# Path: pycoind/util/pyscrypt/util.py
# def array_overwrite(source, source_start, dest, dest_start, length):
#     '''Overwrites the dest array with the source array.'''
# 
#     for i in xrange(0, length):
#         dest[dest_start + i] = source[source_start + i]
# 
# def block_xor(source, source_start, dest, dest_start, length):
#     '''Performs xor on arrays source and dest, storing the result back in dest.'''
# 
#     for i in xrange(0, length):
#         dest[dest_start + i] = chr(ord(dest[dest_start + i]) ^ ord(source[source_start + i]))


import hashlib
import hmac
from .pbkdf2 import pbkdf2
from .util import array_overwrite, block_xor

def blockmix_salsa8(BY, Bi, Yi, r):
    '''Blockmix; Used by SMix.'''

    start = Bi + (2 * r - 1) * 64
    X = [ BY[i] for i in xrange(start, start + 64) ]              # BlockMix - 1

    for i in xrange(0, 2 * r):                                    # BlockMix - 2
        block_xor(BY, i * 64, X, 0, 64)                           # BlockMix - 3(inner)
        salsa20_8(X)                                              # BlockMix - 3(outer)
        array_overwrite(X, 0, BY, Yi + (i * 64), 64)              # BlockMix - 4

    for i in xrange(0, r):                                        # BlockMix - 6 (and below)
        array_overwrite(BY, Yi + (i * 2) * 64, BY, Bi + (i * 64), 64)

    for i in xrange(0, r):
        array_overwrite(BY, Yi + (i * 2 + 1) * 64, BY, Bi + (i + r) * 64, 64)


def smix(B, Bi, r, N, V, X):
    '''SMix; a specific case of ROMix. See scrypt.pdf in the links above.'''

    array_overwrite(B, Bi, X, 0, 128 * r)                 # ROMix - 1

    for i in xrange(0, N):                                # ROMix - 2
        array_overwrite(X, 0, V, i * (128 * r), 128 * r)  # ROMix - 3
        blockmix_salsa8(X, 0, 128 * r, r)                 # ROMix - 4

    for i in xrange(0, N):                                # ROMix - 6
        j = integerify(X, 0, r) & (N - 1)                 # ROMix - 7

# Path: pycoind/util/pyscrypt/pbkdf2.py
# def pbkdf2(password, salt, count, key_length, prf):
#     '''Returns the result of the Password-Based Key Derivation Function 2.
#       prf - a psuedorandom function
# 
#        See http://en.wikipedia.org/wiki/PBKDF2
#     '''
# 
#     def f(block_number):
#         '''The function "f".'''
# 
#         U = prf(password, salt + struct.pack('>L', block_number))
# 
#         if count > 1:
#             U = [ c for c in U ]
#             for i in xrange(2, 1 + count):
#                 block_xor(prf(password, ''.join(U)), 0, U, 0, len(U))
#         U = ''.join(U)
# 
#         return U
# 
#     size = 0
# 
#     block_number = 0
#     blocks = [ ]
# 
#     # The iterations
#     while size < key_length:
#         block_number += 1
#         block = f(block_number)
# 
#         blocks.append(block)
#         size += len(block)
# 
#     return ''.join(blocks)[:key_length]
# 
# Path: pycoind/util/pyscrypt/util.py
# def array_overwrite(source, source_start, dest, dest_start, length):
#     '''Overwrites the dest array with the source array.'''
# 
#     for i in xrange(0, length):
#         dest[dest_start + i] = source[source_start + i]
# 
# def block_xor(source, source_start, dest, dest_start, length):
#     '''Performs xor on arrays source and dest, storing the result back in dest.'''
# 
#     for i in xrange(0, length):
#         dest[dest_start + i] = chr(ord(dest[dest_start + i]) ^ ord(source[source_start + i]))


# Path: pycoind/util/pyscrypt/hash.py
import hashlib
import hmac
from .pbkdf2 import pbkdf2
from .util import array_overwrite, block_xor


def blockmix_salsa8(BY, Bi, Yi, r):
    '''Blockmix; Used by SMix.'''

    start = Bi + (2 * r - 1) * 64
    X = [ BY[i] for i in xrange(start, start + 64) ]              # BlockMix - 1

    for i in xrange(0, 2 * r):                                    # BlockMix - 2
        block_xor(BY, i * 64, X, 0, 64)                           # BlockMix - 3(inner)
        salsa20_8(X)                                              # BlockMix - 3(outer)
        array_overwrite(X, 0, BY, Yi + (i * 64), 64)              # BlockMix - 4

    for i in xrange(0, r):                                        # BlockMix - 6 (and below)
        array_overwrite(BY, Yi + (i * 2) * 64, BY, Bi + (i * 64), 64)

    for i in xrange(0, r):
        array_overwrite(BY, Yi + (i * 2 + 1) * 64, BY, Bi + (i + r) * 64, 64)


def smix(B, Bi, r, N, V, X):
    '''SMix; a specific case of ROMix. See scrypt.pdf in the links above.'''

    array_overwrite(B, Bi, X, 0, 128 * r)                 # ROMix - 1

    for i in xrange(0, N):                                # ROMix - 2
        array_overwrite(X, 0, V, i * (128 * r), 128 * r)  # ROMix - 3
        blockmix_salsa8(X, 0, 128 * r, r)                 # ROMix - 4

    for i in xrange(0, N):                                # ROMix - 6
        j = integerify(X, 0, r) & (N - 1)                 # ROMix - 7

        block_xor(V, j * (128 * r), X, 0, 128 * r)        # ROMix - 8(inner)