def thin(image, mask=None, iterations=1):
    '''Thin an image to lines, preserving Euler number
    
    Implements thinning as described in algorithm # 1 from
    Guo, "Parallel Thinning with Two Subiteration Algorithms",
    Communications of the ACM, Vol 32 #3 page 359.
    '''
    global thin_table, eight_connect
    if thin_table is None:
        thin_table = np.zeros((2,512),bool)
        for i in range(512):
            if (i & 16) == 0:
                # All zeros -> 0
                continue
            pat = pattern_of(i & ~ 16)
            ipat = pat.astype(int)
            if scind.label(pat, eight_connect)[1] != 1:
                thin_table[:,i] = True
                continue
            n1 = ((ipat[0,0] or ipat[0,1]) + (ipat[0,2] or ipat[1,2])+
                  (ipat[2,2] or ipat[2,1]) + (ipat[2,0] or ipat[1,0]))
            n2 = ((ipat[0,1] or ipat[0,2]) + (ipat[1,2] or ipat[2,2])+
                  (ipat[2,1] or ipat[2,0]) + (ipat[1,0] or ipat[0,0]))
            if min(n1,n2) not in (2,3):
                thin_table[:,i] = True
                continue
            thin_table[0,i] = ((pat[0,1] or pat[0,2] or not pat[2,2]) and 
                               pat[1,2])
            thin_table[1,i] = ((pat[2,1] or pat[2,0] or not pat[0,0]) and
                               pat[1,0])
    if mask is None:
        masked_image = image.copy()
    else:
        masked_image = image.copy()
        masked_image[~mask] = False
    index_i, index_j, masked_image = prepare_for_index_lookup(masked_image, False)
    if iterations is None:
        iterations = len(index_i)
    for i in range(iterations):
        hit_count = len(index_i)
        for j in range(2):
            index_i, index_j, = index_lookup(index_i, index_j, 
                                             masked_image,
                                             thin_table[j], 1)
        if hit_count == len(index_i):
            break
    masked_image = extract_from_image_lookup(image, index_i, index_j)
    if not mask is None:
        masked_image[~mask] = masked_image[~mask]
    return masked_image