"""Owned multi-row CSR inspector: duplicate entries ADD, explicit zero retained."""
import math,json
def inspect(data,indices,indptr,shape):
    if len(shape)!=2 or any(type(x)is not int or x<=0 for x in shape):raise ValueError('positive rows/columns')
    rows,cols=shape
    if len(indptr)!=rows+1 or any(type(x)is not int for x in indptr):raise ValueError('row pointer length/type')
    if indptr[0]!=0 or indptr[-1]!=len(data) or len(data)!=len(indices):raise ValueError('pointer endpoints/data lengths')
    if any(a>b for a,b in zip(indptr,indptr[1:])):raise ValueError('pointers must not decrease')
    if any(type(j)is not int or not 0<=j<cols for j in indices):raise ValueError('column out of range/type')
    if any(type(x)not in (int,float) or not math.isfinite(x) for x in data):raise ValueError('finite numeric values')
    dense=[[0]*cols for _ in range(rows)];ledger=[];canonical=True
    for i in range(rows):
        start,end=indptr[i:i+2];js=indices[start:end]
        canonical &= all(a<b for a,b in zip(js,js[1:]))
        for k in range(start,end):dense[i][indices[k]]+=data[k]
        ledger.append({'row':i,'entry_slice':[start,end],'stored_columns':js,'stored_values':data[start:end],'empty_stored_row':start==end})
    return {'dense_reconstruction_with_duplicate_sum':dense,'row_slices':ledger,'stored_entry_count':len(data),'explicit_zero_entries':sum(x==0 for x in data),'numeric_nonzero_count_after_sum':sum(x!=0 for row in dense for x in row),'sorted_unique_per_row_canonical':bool(canonical)}
def demo():
    fixture={'data':[2,0,3,4,-1,5],'indices':[0,4,1,1,3,0],'indptr':[0,2,2,5,6],'shape':[4,5]};out=inspect(**fixture)
    assert out['dense_reconstruction_with_duplicate_sum']==[[2,0,0,0,0],[0,0,0,0,0],[0,7,0,-1,0],[5,0,0,0,0]]
    assert out['stored_entry_count']==6 and out['numeric_nonzero_count_after_sum']==4 and not out['sorted_unique_per_row_canonical']
    q=[1,2,3,4,5];product=[sum(a*b for a,b in zip(row,q)) for row in out['dense_reconstruction_with_duplicate_sum']];assert product==[2,0,10,5]
    invalid=[('short_pointer',{'indptr':[0,2,5,6]}),('nonzero_start',{'indptr':[1,2,2,5,6]}),('decreasing',{'indptr':[0,3,2,5,6]}),('wrong_end',{'indptr':[0,2,2,5,5]}),('column_range',{'indices':[0,5,1,1,3,0]}),('boolean_column',{'indices':[False,4,1,1,3,0]})];rejected=[]
    for name,change in invalid:
        try:inspect(**(fixture|change))
        except ValueError:rejected.append(name)
        else:raise AssertionError(name)
    return {'authored_fixture':fixture,'inspection':out,'query_vector':q,'matrix_vector_product':product,'six_invalid_inputs_rejected':rejected,'SciPy_or_memory_benchmark_executed':False}
if __name__=='__main__':print(json.dumps(demo(),indent=2))
