"""Count connectivity only: neutral carbon trees / one carbon ring, valence <=4.
Single-group counts follow the current editor's fixed templates; no stereoisomers.
"""
import json
from pathlib import Path
import networkx as nx

def rooted_code(g,node,parent=None):
    return '('+''.join(sorted(rooted_code(g,n,node) for n in g[node] if n!=parent))+')'

def trees(n):
    if n==1: return [nx.empty_graph(1)]
    return [g for g in nx.nonisomorphic_trees(n) if max(dict(g.degree()).values())<=4]

rows=[]
root_counts={0:0,-1:0}
for n in range(1,9):
    ts=trees(n)
    roots={rooted_code(g,i) for g in ts for i in g if g.degree(i)<4}
    root_counts[n]=len(roots)
    buckets={}
    for g in ts:
        for a in g:
            for b in g:
                if b<=a or g.has_edge(a,b) or g.degree(a)>=4 or g.degree(b)>=4: continue
                ring=g.copy();ring.add_edge(a,b)
                assert nx.is_connected(ring) and ring.number_of_edges()==n
                key=nx.weisfeiler_lehman_graph_hash(ring)
                bucket=buckets.setdefault(key,[])
                if not any(nx.is_isomorphic(ring,other) for other in bucket): bucket.append(ring)
    rows.append(dict(carbons=n,acyclic_carbon_skeletons=len(ts),single_ring_carbon_skeletons=sum(map(len,buckets.values())),acyclic_one_OH=len(roots),acyclic_one_NH2=len(roots),acyclic_one_CHO=root_counts[n-1],acyclic_one_COOH=root_counts[n-1],acyclic_one_methoxy=root_counts[n-1],acyclic_one_methyl_ester=root_counts[n-2]))
assert [r['acyclic_carbon_skeletons'] for r in rows]==[1,1,1,2,3,5,9,18]
assert [r['acyclic_one_OH'] for r in rows]==[1,1,2,4,8,17,39,89]
result={'scope':'Constitutional connectivity only; no stereo; carbon valence <=4; connected acyclic or exactly one all-carbon ring. Functional-group columns are acyclic and exactly one current-editor template. Ring counts include substituted monocyclic skeletons but not cis/trans variants. No stability claim.','rows':rows,'acyclic_zero_or_one_group_total':sum(sum(v for k,v in r.items() if k not in ['carbons','single_ring_carbon_skeletons']) for r in rows)}
Path(__file__).with_name('structure-space.json').write_text(json.dumps(result,indent=2)+'\n')
print(json.dumps(result,indent=2))
