Return a dict of test,train,validation sets with information regarding the split (lists of paths to data).

    Currently only separates by subjectID.

    Prioitises having a diverse set than a well fitting set. This means that the
    'knapsacking' is done by subjects with the least videos first, so that each set can have
    as many different subjects in as possible. If shuffle is set to true, the set is shuffled, which means this property does not hold

    returns {
        'subjects': list of subjectIDs in the set
        'number_of_files': number of files the set has
        'expected_size' : (ttv_ratio * number_of_resources) or the limit_per_set (if not None),
        'paths': list of paths to the files
    }

def make_ttv(dataset, ttv_ratio=DEFAULT_TTV_RATIO, deterministic=False, limit_per_set=None):
    """
    Return a dict of test,train,validation sets with information regarding the split (lists of paths to data).

    Currently only separates by subjectID.

    Prioitises having a diverse set than a well fitting set. This means that the
    'knapsacking' is done by subjects with the least videos first, so that each set can have
    as many different subjects in as possible. If shuffle is set to true, the set is shuffled, which means this property does not hold

    returns {
        'subjects': list of subjectIDs in the set
        'number_of_files': number of files the set has
        'expected_size' : (ttv_ratio * number_of_resources) or the limit_per_set (if not None),
        'paths': list of paths to the files
    }
    """
    sizes_and_ids = [(len(dataset[key]), key) for key in dataset]
    number_of_resources = sum([x[0] for x in sizes_and_ids])


    # Get the smallest first
    sizes_and_ids.sort()
    if not deterministic:
        random.shuffle(sizes_and_ids)

    # normalise ttv_ratio
    ttv_ratio = [float(x) / sum(ttv_ratio) for x in ttv_ratio]

    data_sets = {}

    for key, ratio in zip(['test', 'train', 'validation'], ttv_ratio):
        expected_size = min(limit_per_set * ratio, (ratio * number_of_resources)) \
            if limit_per_set is not None else (ratio * number_of_resources)

        data_sets[key] = {
            'subjects': [],
            'number_of_files': 0,
            'expected_size': expected_size
        }


    set_names = list(data_sets.keys())
    i = 0
    while len(sizes_and_ids) > 0:
        if limit_per_set is not None:
            # if all the sets have more than the limit
            if all(map(lambda x: x['number_of_files'] >= x['expected_size'], data_sets.values())):
                break
        i += 1
        s = data_sets[set_names[i % len(set_names)]]
        if s['number_of_files'] < s['expected_size']:
            size, subjectID = sizes_and_ids.pop(0)
            s['subjects'].append(subjectID)
            s['number_of_files'] += size

    def get_filenames(subjects):
        return sum(list(map(lambda x: dataset[x], subjects)), [])

    # turn subjectIDs into paths to files
    for data_set in data_sets:
        s = data_sets[data_set]
        s['paths'] = get_filenames(s['subjects'])

    return data_sets