import itertools as it
import numpy as np
import re
from StringIO import StringIO



def grouper(n, iterable, fillvalue=None):
    "grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
    args = [iter(iterable)] * n
    return it.izip_longest(fillvalue=fillvalue, *args)

tot = 600
perLine = 5
n = tot/perLine

tostr = lambda s : ' '.join(map(str, s))
numbers = grouper(perLine, xrange(tot))
S = '\n'.join(map(tostr, numbers))
f = StringIO(S)


dt = np.dtype(','.join(['i4']*perLine))

#Simplest case, split on space and use list comprehension
def f1():
    f.seek(0)
    arr = np.empty(n, dtype=dt)
    for j, line in enumerate(f):
        tmp = [int(i) for i in line.split(' ')]
        arr[j] = tuple(tmp)
    return arr

#Split on space and use generator instead
def f2():
    f.seek(0)
    arr = np.empty(n, dtype=dt)
    for j, line in enumerate(f):
        tmp = tuple(int(i) for i in line.split(' '))
        arr[j] = tmp
    return arr

s = r'(\d+)\s+'
s1 = [s]*(perLine-1)
s1.insert(0,'^')
s1.append(r'(\d+)$')
s2 = ''.join(s1)

# Use regular expression. This most closely matches what is
# currently done in the loadtable code
def f3():
    f.seek(0)
    arr = np.empty(n, dtype=dt)
    m = re.compile(s2)
    for j, line in enumerate(f):
        args = m.match(line).groups()
        args = (int(i) for i in args)
        arr[j] = tuple(args)
    return arr

#Use regular expression and grab multiple lines at once
#This has been suggested as an alternative to attempt
#in loadtable.
#Unforunately Python's re module won't let the caller
#have more than 100 captured groups.
def f4(ch):
    f.seek(0)
    arr = np.empty(n, dtype=dt)
    m = re.compile('\n'.join([s2]*ch), re.MULTILINE)
    g = grouper(ch,iter(f))
    count = 0
    for lines in g:
        args = m.match(''.join(lines)).groups()
        args = (int(i) for i in args)
        args = grouper(perLine, args)
        arr[count:count+ch] = list((tuple(g) for g in args))
        count += ch
    return arr

def f5():
    f.seek(0)
    m = re.compile(s2)
    l = []
    for line in f:
        args = m.match(line).groups()
        args = (int(i) for i in args)
        l.append(tuple(args))
    return np.fromiter(l, dt)

# The next two are checking times for two versions of accumulator
# numpy arrays. Thanks to Chris Barker for the idea.
class sAccum:

    def __init__(self, dtype, isize):
        self._arr = np.empty((isize,), dtype=dtype)
        self.size = 0

    def __call__(self):
        return self._arr[:self.size]

    def add(self, row):
        if self.size+1<self._arr.size:
            self._arr[self.size] = row
            self.size += 1
        else:
            temp = int(self._arr.size * 1.25)
            self._arr = np.resize(self._arr, temp)
            self._arr[self.size] = row
            self.size += 1

class sAccumAppend:

    def __init__(self, dtype, isize):
        self._arr = np.empty((isize,), dtype=dtype)
        self.size = 0

    def __call__(self):
        return self._arr[:self.size]

    def add(self, row):
        if self.size+1<self._arr.size:
            self._arr[self.size] = row
            self.size += 1
        else:
            temp = np.empty(int(self._arr.size * 0.25), dtype=self._arr.dtype)
            self._arr = np.append(self._arr, temp)
            self._arr[self.size] = row
            self.size += 1

def f6():

    f.seek(0)
    arr = sAccum(dt, 10)
    m = re.compile(s2)
    for j, line in enumerate(f):
        args = m.match(line).groups()
        args = (int(i) for i in args)
        arr.add(tuple(args))
    return arr()

def f7():

    f.seek(0)
    arr = sAccumAppend(dt, 10)
    m = re.compile(s2)
    for j, line in enumerate(f):
        args = m.match(line).groups()
        args = (int(i) for i in args)
        arr.add(tuple(args))
    return arr()

# Adding checking sizes just to get approximate times for the overhead
def f8():
    f.seek(0)
    arr = np.empty(n, dtype=dt)
    m = re.compile(s2)
    sizes = [0]*perLine
    for j, line in enumerate(f):
        args = m.match(line).groups()
        sizes = map(max, zip(sizes, map(len, args)))
        args = (int(i) for i in args)
        arr[j] = tuple(args)
    return arr



# Checking against existing functions

def f9():
    f.seek(0)
    return np.genfromtxt(f, dtype=None)

# This is only relevant if you have the loadtable code

def f10():
    f.seek(0)
    return np.loadtable(f)

def f11():
    f.seek(0)
    return np.loadtable(f, check_sizes=False)


