def performance_report(similar, nonsimilar, limit=None, add_std=False):
    """
    Prints a performance report.
    
    Parameters:
    ------------------------------
    
        similar: (array), containing the hash values found
                            for similar groups
        nonsimilar: (array), containing the hash values found
                            for non-similar groups
        limit: (float), threshold value, if None, mean is used
        add_std: (bool), if True, standard deviation is added to the
                         mean threshold, works only if limit is None
                         
    Returns:
    ------------------------------
        tuple (precision_score, recall_score, f1_score)
    
    """
    def precision_score(tp, fp, eps=1e-10):
        return (tp + eps) / (tp + fp + eps) 
    
    def recall_score(tp, fn, eps=1e-10):
        return (tp + eps) / (tp + fn + eps)
    
    def f1_score(tp, fp, fn, eps=1e-10):
        return 2 / (1/precision_score(tp, fp) + 1/recall_score(tp, fn))
    
    if limit is None:
        limit = similar.mean()
        if add_std is True:
            limit += similar.std()
        
    tp = sum([1 for c in similar if c >= limit])
    fn = len(similar) - tp
    tn = sum([1 for c in nonsimilar if c < limit])
    fp = len(nonsimilar) - tn

    p_score, r_score = precision_score(tp, fp), recall_score(tp, fn)
    f_score = f1_score(tp, fp, fn)

    print('Performance report\n'+'-'*50)
    print('True positive: {} -- False negative: {}'.format(tp, fn))
    print('True negative: {} -- False positive: {}'.format(tn, fp))
    print('\nPrecision score: {:.4f}'.format(p_score))
    print('Recall score: {:.4f}'.format(r_score))
    print('F1 score: {:.4f}'.format(f_score))
    return (p_score, r_score, f_score)