"""Authored deterministic group-size greedy K-fold assignment; not sklearn GroupKFold."""
from collections import Counter
import json
GROUPS=['G1']*3+['G2']*2+['G3']*2+['G4','G5','G6']
def split(groups,k):
    if type(k) is not int or k<2 or any(not isinstance(g,str) or not g for g in groups):raise ValueError('explicit nonempty string groups and integer k>=2 required')
    sizes=Counter(groups)
    if len(sizes)<k:raise ValueError('too few groups')
    bins=[[] for _ in range(k)];loads=[0]*k
    for g in sorted(sizes,key=lambda x:(-sizes[x],x)):
        b=min(range(k),key=lambda i:(loads[i],i));bins[b].append(g);loads[b]+=sizes[g]
    rows=[]
    for fold,heldgroups in enumerate(bins):
        held=[i for i,g in enumerate(groups) if g in heldgroups];train=[i for i,g in enumerate(groups) if g not in heldgroups]
        tg={groups[i] for i in train};vg={groups[i] for i in held};assert not tg&vg
        rows.append({'fold':fold,'heldout_groups':heldgroups,'heldout_rows':held,'training_rows':train,'group_intersection':sorted(tg&vg)})
    assert sorted(i for r in rows for i in r['heldout_rows'])==list(range(len(groups)))
    assert sorted(g for r in rows for g in r['heldout_groups'])==sorted(sizes)
    return rows
def demo():
    folds=split(GROUPS,3);bad=[]
    for f in range(3):
        held=[i for i in range(len(GROUPS)) if i%3==f];train=[i for i in range(len(GROUPS)) if i%3!=f]
        overlap=sorted({GROUPS[i] for i in held}&{GROUPS[i] for i in train});assert overlap
        bad.append({'fold':f,'heldout_rows':held,'shared_groups_with_training':overlap})
    rejected=[]
    for name,groups,k in [('too_few_groups',['A','A','B'],3),('missing_group',['A',None,'B'],2),('boolean_k',['A','B'],True)]:
        try:split(groups,k)
        except ValueError:rejected.append(name)
        else:raise AssertionError(name)
    return {'authored_row_groups':GROUPS,'group_sizes':dict(Counter(GROUPS)),'grouped_folds':folds,'grouped_heldout_sizes':[len(r['heldout_rows']) for r in folds],'incorrect_row_round_robin_folds':bad,'rejected':rejected,'declared_group_identity_not_discovered_semantic_independence':True,'shuffle_stratification_model_training_or_sklearn_run':False}
if __name__=='__main__':print(json.dumps(demo(),ensure_ascii=False,indent=2))
