@@ -713,10 +713,16 @@ impl<AB: InteractionBuilder, const SBOX_REGISTERS: usize> Air<AB>
713713 let & MultiObserveCols {
714714 pc,
715715 final_timestamp_increment,
716+ state_ptr_register,
717+ ctx_register,
718+ input_ptr_register,
719+ hint_id_register,
716720 state_ptr,
721+ ctx_ptr,
717722 input_ptr,
718- init_pos,
719- len,
723+ hint_id,
724+ ctx,
725+ read_ctx,
720726 is_first,
721727 is_last,
722728 curr_len,
@@ -731,35 +737,38 @@ impl<AB: InteractionBuilder, const SBOX_REGISTERS: usize> Air<AB>
731737 should_permute,
732738 write_sponge_state,
733739 write_final_idx,
734- input_register_1,
735- input_register_2,
736- input_register_3,
737- output_register,
738740 } = multi_observe_specific;
739741
742+ // Alias context values
743+ let init_pos = ctx[ 0 ] ;
744+ let len = ctx[ 1 ] ;
745+ let is_hint = ctx[ 2 ] ;
746+
740747 builder. when ( multi_observe_row) . assert_bool ( is_first) ;
741748 builder. when ( multi_observe_row) . assert_bool ( is_last) ;
742749 builder. when ( multi_observe_row) . assert_bool ( should_permute) ;
750+ builder. when ( multi_observe_row) . assert_bool ( is_hint) ;
743751
744752 self . execution_bridge
745753 . execute_and_increment_pc (
746754 AB :: F :: from_canonical_usize ( MULTI_OBSERVE . global_opcode ( ) . as_usize ( ) ) ,
747755 [
748- output_register . into ( ) ,
749- input_register_1 . into ( ) ,
750- input_register_2 . into ( ) ,
756+ state_ptr_register . into ( ) ,
757+ ctx_register . into ( ) ,
758+ input_ptr_register . into ( ) ,
751759 self . address_space . into ( ) ,
752760 self . address_space . into ( ) ,
753- input_register_3 . into ( ) ,
761+ hint_id_register . into ( ) ,
754762 ] ,
755763 ExecutionState :: new ( pc, very_first_timestamp) ,
756764 final_timestamp_increment,
757765 )
758766 . eval ( builder, multi_observe_row * is_first) ;
759767
768+ // Head row: 3 register reads + 1 context array read + 1 hint_id register read
760769 self . memory_bridge
761770 . read (
762- MemoryAddress :: new ( self . address_space , output_register ) ,
771+ MemoryAddress :: new ( self . address_space , state_ptr_register ) ,
763772 [ state_ptr] ,
764773 very_first_timestamp,
765774 & read_data[ 0 ] ,
@@ -768,50 +777,82 @@ impl<AB: InteractionBuilder, const SBOX_REGISTERS: usize> Air<AB>
768777
769778 self . memory_bridge
770779 . read (
771- MemoryAddress :: new ( self . address_space , input_register_1 ) ,
772- [ init_pos ] ,
780+ MemoryAddress :: new ( self . address_space , ctx_register ) ,
781+ [ ctx_ptr ] ,
773782 very_first_timestamp + AB :: F :: ONE ,
774783 & read_data[ 1 ] ,
775784 )
776785 . eval ( builder, multi_observe_row * is_first) ;
777786
778787 self . memory_bridge
779788 . read (
780- MemoryAddress :: new ( self . address_space , input_register_2 ) ,
789+ MemoryAddress :: new ( self . address_space , input_ptr_register ) ,
781790 [ input_ptr] ,
782791 very_first_timestamp + AB :: F :: TWO ,
783792 & read_data[ 2 ] ,
784793 )
785794 . eval ( builder, multi_observe_row * is_first) ;
786795
796+ // Read context array: [init_pos, len, is_hint, reserved] from ctx_ptr
787797 self . memory_bridge
788798 . read (
789- MemoryAddress :: new ( self . address_space , input_register_3 ) ,
790- [ len ] ,
799+ MemoryAddress :: new ( self . address_space , ctx_ptr ) ,
800+ ctx ,
791801 very_first_timestamp + AB :: F :: from_canonical_usize ( 3 ) ,
802+ & read_ctx,
803+ )
804+ . eval ( builder, multi_observe_row * is_first) ;
805+
806+ // Read hint_id from register (reuse spare read_data[3] on head row)
807+ self . memory_bridge
808+ . read (
809+ MemoryAddress :: new ( self . address_space , hint_id_register) ,
810+ [ hint_id] ,
811+ very_first_timestamp + AB :: F :: from_canonical_usize ( 4 ) ,
792812 & read_data[ 3 ] ,
793813 )
794814 . eval ( builder, multi_observe_row * is_first) ;
795815
816+ // ts_per_element = 2 - is_hint (non-hint: read+write=2, hint: write-only=1)
817+ let is_hint_expr: AB :: Expr = is_hint. into ( ) ;
818+ let ts_per_element: AB :: Expr = AB :: Expr :: TWO - is_hint_expr. clone ( ) ;
796819 for i in 0 ..CHUNK {
797- let i_var = AB :: F :: from_canonical_usize ( i) ;
820+ let i_var: AB :: Expr = AB :: F :: from_canonical_usize ( i) . into ( ) ;
821+ let start_idx_expr: AB :: Expr = start_idx. into ( ) ;
822+ let element_start_ts: AB :: Expr =
823+ start_timestamp. into ( ) + ( i_var. clone ( ) - start_idx_expr. clone ( ) ) * ts_per_element. clone ( ) ;
824+
825+ // Non-hint mode: read from memory
798826 self . memory_bridge
799827 . read (
800828 MemoryAddress :: new (
801829 self . address_space ,
802- input_ptr + curr_len + i_var - start_idx ,
830+ input_ptr + curr_len + i_var. clone ( ) - start_idx_expr . clone ( ) ,
803831 ) ,
804832 [ data[ i] ] ,
805- start_timestamp + i_var * AB :: F :: TWO - start_idx * AB :: F :: TWO ,
833+ element_start_ts . clone ( ) ,
806834 & read_data[ i] ,
807835 )
808- . eval ( builder, multi_observe_row * aux_read_enabled[ i] ) ;
836+ . eval (
837+ builder,
838+ multi_observe_row * aux_read_enabled[ i] * ( AB :: Expr :: ONE - is_hint_expr. clone ( ) ) ,
839+ ) ;
809840
841+ // Hint mode: lookup from hint space
842+ self . hint_bridge . lookup (
843+ builder,
844+ hint_id,
845+ curr_len + i_var. clone ( ) - start_idx_expr. clone ( ) ,
846+ data[ i] ,
847+ multi_observe_row * aux_read_enabled[ i] * is_hint_expr. clone ( ) ,
848+ ) ;
849+
850+ // Write to sponge state (always, for both modes)
810851 self . memory_bridge
811852 . write (
812853 MemoryAddress :: new ( self . address_space , state_ptr + i_var) ,
813854 [ data[ i] ] ,
814- start_timestamp + i_var * AB :: F :: TWO - start_idx * AB :: F :: TWO + AB :: F :: ONE ,
855+ element_start_ts + ( AB :: Expr :: ONE - is_hint_expr . clone ( ) ) ,
815856 & write_data[ i] ,
816857 )
817858 . eval ( builder, multi_observe_row * aux_read_enabled[ i] ) ;
@@ -885,7 +926,7 @@ impl<AB: InteractionBuilder, const SBOX_REGISTERS: usize> Air<AB>
885926 . write (
886927 MemoryAddress :: new ( self . address_space , state_ptr) ,
887928 full_sponge_output,
888- start_timestamp + ( end_idx - start_idx) * AB :: F :: TWO ,
929+ start_timestamp + ( end_idx - start_idx) * ( AB :: Expr :: TWO - is_hint_expr . clone ( ) ) ,
889930 & write_sponge_state,
890931 )
891932 . eval ( builder, multi_observe_row * should_permute) ;
@@ -909,11 +950,12 @@ impl<AB: InteractionBuilder, const SBOX_REGISTERS: usize> Air<AB>
909950 // final_idx = aux_read_enabled[CHUNK-1] * 0 + (1 - aux_read_enabled[CHUNK-1]) * end_idx
910951 let final_idx = aux_read_enabled[ CHUNK - 1 ] * AB :: Expr :: ZERO
911952 + ( AB :: Expr :: ONE - aux_read_enabled[ CHUNK - 1 ] ) * end_idx;
953+ // Write final_idx back to ctx[0] (ctx_ptr address)
912954 self . memory_bridge
913955 . write (
914- MemoryAddress :: new ( self . address_space , input_register_1 ) ,
956+ MemoryAddress :: new ( self . address_space , ctx_ptr ) ,
915957 [ final_idx] ,
916- start_timestamp + ( end_idx - start_idx) * AB :: F :: TWO + should_permute,
958+ start_timestamp + ( end_idx - start_idx) * ( AB :: Expr :: TWO - is_hint_expr ) + should_permute,
917959 & write_final_idx,
918960 )
919961 . eval ( builder, multi_observe_row * is_last) ;
@@ -962,41 +1004,59 @@ impl<AB: InteractionBuilder, const SBOX_REGISTERS: usize> Air<AB>
9621004 builder
9631005 . when ( next. multi_observe_row )
9641006 . when ( not ( next_multi_observe_specific. is_first ) )
965- . assert_eq ( init_pos, next_multi_observe_specific. init_pos ) ;
1007+ . assert_eq ( init_pos, next_multi_observe_specific. ctx [ 0 ] ) ;
9661008
9671009 builder
9681010 . when ( next. multi_observe_row )
9691011 . when ( not ( next_multi_observe_specific. is_first ) )
970- . assert_eq ( len, next_multi_observe_specific. len ) ;
1012+ . assert_eq ( len, next_multi_observe_specific. ctx [ 1 ] ) ;
1013+
1014+ builder
1015+ . when ( next. multi_observe_row )
1016+ . when ( not ( next_multi_observe_specific. is_first ) )
1017+ . assert_eq (
1018+ state_ptr_register,
1019+ next_multi_observe_specific. state_ptr_register ,
1020+ ) ;
9711021
9721022 builder
9731023 . when ( next. multi_observe_row )
9741024 . when ( not ( next_multi_observe_specific. is_first ) )
9751025 . assert_eq (
976- input_register_1 ,
977- next_multi_observe_specific. input_register_1 ,
1026+ ctx_register ,
1027+ next_multi_observe_specific. ctx_register ,
9781028 ) ;
9791029
9801030 builder
9811031 . when ( next. multi_observe_row )
9821032 . when ( not ( next_multi_observe_specific. is_first ) )
9831033 . assert_eq (
984- input_register_2 ,
985- next_multi_observe_specific. input_register_2 ,
1034+ input_ptr_register ,
1035+ next_multi_observe_specific. input_ptr_register ,
9861036 ) ;
9871037
1038+ builder
1039+ . when ( next. multi_observe_row )
1040+ . when ( not ( next_multi_observe_specific. is_first ) )
1041+ . assert_eq ( ctx_ptr, next_multi_observe_specific. ctx_ptr ) ;
1042+
1043+ builder
1044+ . when ( next. multi_observe_row )
1045+ . when ( not ( next_multi_observe_specific. is_first ) )
1046+ . assert_eq ( hint_id, next_multi_observe_specific. hint_id ) ;
1047+
9881048 builder
9891049 . when ( next. multi_observe_row )
9901050 . when ( not ( next_multi_observe_specific. is_first ) )
9911051 . assert_eq (
992- input_register_3 ,
993- next_multi_observe_specific. input_register_3 ,
1052+ hint_id_register ,
1053+ next_multi_observe_specific. hint_id_register ,
9941054 ) ;
9951055
9961056 builder
9971057 . when ( next. multi_observe_row )
9981058 . when ( not ( next_multi_observe_specific. is_first ) )
999- . assert_eq ( output_register , next_multi_observe_specific. output_register ) ;
1059+ . assert_eq ( is_hint , next_multi_observe_specific. ctx [ 2 ] ) ;
10001060
10011061 // Timestamp constraints
10021062 builder
0 commit comments