@@ -510,6 +510,154 @@ impl<G: PrimeGroup> CanonicalLinearRelation<G> {
510510
511511 out
512512 }
513+
514+ /// Parse a canonical linear relation from its label representation
515+ pub fn from_label ( data : & [ u8 ] ) -> Result < Self , Error > {
516+ use crate :: errors:: InvalidInstance ;
517+ use crate :: serialization:: group_elt_serialized_len;
518+
519+ let mut offset = 0 ;
520+
521+ // Read number of equations (4 bytes, little endian)
522+ if data. len ( ) < 4 {
523+ return Err ( InvalidInstance :: new ( "Invalid label: too short for equation count" ) . into ( ) ) ;
524+ }
525+ let num_equations = u32:: from_le_bytes ( [ data[ 0 ] , data[ 1 ] , data[ 2 ] , data[ 3 ] ] ) as usize ;
526+ offset += 4 ;
527+
528+ // Parse constraints and collect unique group element indices
529+ let mut constraint_data = Vec :: new ( ) ;
530+ let mut max_scalar_index = 0u32 ;
531+ let mut max_group_index = 0u32 ;
532+
533+ for _ in 0 ..num_equations {
534+ // Read LHS index (4 bytes)
535+ if offset + 4 > data. len ( ) {
536+ return Err ( InvalidInstance :: new ( "Invalid label: truncated LHS index" ) . into ( ) ) ;
537+ }
538+ let lhs_index = u32:: from_le_bytes ( [
539+ data[ offset] ,
540+ data[ offset + 1 ] ,
541+ data[ offset + 2 ] ,
542+ data[ offset + 3 ] ,
543+ ] ) ;
544+ offset += 4 ;
545+ max_group_index = max_group_index. max ( lhs_index) ;
546+
547+ // Read number of RHS terms (4 bytes)
548+ if offset + 4 > data. len ( ) {
549+ return Err ( InvalidInstance :: new ( "Invalid label: truncated RHS count" ) . into ( ) ) ;
550+ }
551+ let num_rhs_terms = u32:: from_le_bytes ( [
552+ data[ offset] ,
553+ data[ offset + 1 ] ,
554+ data[ offset + 2 ] ,
555+ data[ offset + 3 ] ,
556+ ] ) as usize ;
557+ offset += 4 ;
558+
559+ // Read RHS terms
560+ let mut rhs_terms = Vec :: new ( ) ;
561+ for _ in 0 ..num_rhs_terms {
562+ // Read scalar index (4 bytes)
563+ if offset + 4 > data. len ( ) {
564+ return Err (
565+ InvalidInstance :: new ( "Invalid label: truncated scalar index" ) . into ( ) ,
566+ ) ;
567+ }
568+ let scalar_index = u32:: from_le_bytes ( [
569+ data[ offset] ,
570+ data[ offset + 1 ] ,
571+ data[ offset + 2 ] ,
572+ data[ offset + 3 ] ,
573+ ] ) ;
574+ offset += 4 ;
575+ max_scalar_index = max_scalar_index. max ( scalar_index) ;
576+
577+ // Read group index (4 bytes)
578+ if offset + 4 > data. len ( ) {
579+ return Err ( InvalidInstance :: new ( "Invalid label: truncated group index" ) . into ( ) ) ;
580+ }
581+ let group_index = u32:: from_le_bytes ( [
582+ data[ offset] ,
583+ data[ offset + 1 ] ,
584+ data[ offset + 2 ] ,
585+ data[ offset + 3 ] ,
586+ ] ) ;
587+ offset += 4 ;
588+ max_group_index = max_group_index. max ( group_index) ;
589+
590+ rhs_terms. push ( ( scalar_index, group_index) ) ;
591+ }
592+
593+ constraint_data. push ( ( lhs_index, rhs_terms) ) ;
594+ }
595+
596+ // Calculate expected number of group elements
597+ let num_group_elements = ( max_group_index + 1 ) as usize ;
598+ let group_element_size = group_elt_serialized_len :: < G > ( ) ;
599+ let expected_remaining = num_group_elements * group_element_size;
600+
601+ if data. len ( ) - offset != expected_remaining {
602+ return Err ( InvalidInstance :: new ( format ! (
603+ "Invalid label: expected {} bytes for {} group elements, got {}" ,
604+ expected_remaining,
605+ num_group_elements,
606+ data. len( ) - offset
607+ ) )
608+ . into ( ) ) ;
609+ }
610+
611+ // Parse group elements
612+ let mut group_elements_ordered = Vec :: new ( ) ;
613+ for i in 0 ..num_group_elements {
614+ let start = offset + i * group_element_size;
615+ let end = start + group_element_size;
616+ let elem_bytes = & data[ start..end] ;
617+
618+ let mut repr = G :: Repr :: default ( ) ;
619+ repr. as_mut ( ) . copy_from_slice ( elem_bytes) ;
620+
621+ let elem = Option :: < G > :: from ( G :: from_bytes ( & repr) ) . ok_or_else ( || {
622+ Error :: from ( InvalidInstance :: new ( format ! (
623+ "Invalid group element at index {}" ,
624+ i
625+ ) ) )
626+ } ) ?;
627+
628+ group_elements_ordered. push ( elem) ;
629+ }
630+
631+ // Build the canonical relation
632+ let mut canonical = Self :: new ( ) ;
633+ canonical. num_scalars = ( max_scalar_index + 1 ) as usize ;
634+
635+ // Add all group elements to the map
636+ let mut group_var_map = Vec :: new ( ) ;
637+ for elem in & group_elements_ordered {
638+ let var = canonical. group_elements . push ( * elem) ;
639+ group_var_map. push ( var) ;
640+ }
641+
642+ // Build constraints
643+ for ( lhs_index, rhs_terms) in constraint_data {
644+ // Add image element
645+ canonical
646+ . image
647+ . push ( group_elements_ordered[ lhs_index as usize ] ) ;
648+
649+ // Build linear combination
650+ let mut linear_combination = Vec :: new ( ) ;
651+ for ( scalar_index, group_index) in rhs_terms {
652+ let scalar_var = ScalarVar ( scalar_index as usize , PhantomData ) ;
653+ let group_var = group_var_map[ group_index as usize ] ;
654+ linear_combination. push ( ( scalar_var, group_var) ) ;
655+ }
656+ canonical. linear_combinations . push ( linear_combination) ;
657+ }
658+
659+ Ok ( canonical)
660+ }
513661}
514662
515663impl < G : PrimeGroup > TryFrom < & LinearRelation < G > > for CanonicalLinearRelation < G > {
0 commit comments