@@ -843,66 +843,72 @@ set_i_classify(VALUE set)
843
843
return args [0 ];
844
844
}
845
845
846
- struct set_divide_args {
847
- VALUE self ;
848
- VALUE set_class ;
849
- VALUE final_set ;
850
- VALUE hash ;
851
- VALUE current_set ;
852
- VALUE current_item ;
853
- unsigned long ni ;
854
- unsigned long nj ;
855
- };
846
+ // Union-find with path compression
847
+ static long
848
+ set_divide_union_find_root (long * uf_parents , long index , long * tmp_array )
849
+ {
850
+ long root = uf_parents [index ];
851
+ long update_size = 0 ;
852
+ while (root != index ) {
853
+ tmp_array [update_size ++ ] = index ;
854
+ index = root ;
855
+ root = uf_parents [index ];
856
+ }
857
+ for (long j = 0 ; j < update_size ; j ++ ) {
858
+ long idx = tmp_array [j ];
859
+ uf_parents [idx ] = root ;
860
+ }
861
+ return root ;
862
+ }
856
863
857
- static VALUE
858
- set_divide_block0 (RB_BLOCK_CALL_FUNC_ARGLIST (j , arg ))
859
- {
860
- struct set_divide_args * args = (struct set_divide_args * )arg ;
861
- if (args -> nj > args -> ni ) {
862
- VALUE i = args -> current_item ;
863
- if (RTEST (rb_yield_values (2 , i , j )) && RTEST (rb_yield_values (2 , j , i ))) {
864
- VALUE hash = args -> hash ;
865
- if (args -> current_set == Qnil ) {
866
- VALUE set = rb_hash_aref (hash , j );
867
- if (set == Qnil ) {
868
- VALUE both [2 ] = {i , j };
869
- set = set_s_create (2 , both , args -> set_class );
870
- rb_hash_aset (hash , i , set );
871
- rb_hash_aset (hash , j , set );
872
- set_i_add (args -> final_set , set );
873
- }
874
- else {
875
- set_i_add (set , i );
876
- rb_hash_aset (hash , i , set );
877
- }
878
- args -> current_set = set ;
879
- }
880
- else {
881
- set_i_add (args -> current_set , j );
882
- rb_hash_aset (hash , j , args -> current_set );
864
+ static void
865
+ set_divide_union_find_merge (long * uf_parents , long i , long j , long * tmp_array )
866
+ {
867
+ long root_i = set_divide_union_find_root (uf_parents , i , tmp_array );
868
+ long root_j = set_divide_union_find_root (uf_parents , j , tmp_array );
869
+ if (root_i != root_j ) uf_parents [root_j ] = root_i ;
870
+ }
871
+
872
+ static VALUE
873
+ set_divide_arity2 (VALUE set )
874
+ {
875
+ VALUE tmp , uf ;
876
+ long size , * uf_parents , * tmp_array ;
877
+ VALUE set_class = rb_obj_class (set );
878
+ VALUE items = set_i_to_a (set );
879
+ rb_ary_freeze (items );
880
+ size = RARRAY_LEN (items );
881
+ tmp_array = ALLOCV_N (long , tmp , size );
882
+ uf_parents = ALLOCV_N (long , uf , size );
883
+ for (long i = 0 ; i < size ; i ++ ) {
884
+ uf_parents [i ] = i ;
885
+ }
886
+ for (long i = 0 ; i < size - 1 ; i ++ ) {
887
+ VALUE item1 = RARRAY_AREF (items , i );
888
+ for (long j = i + 1 ; j < size ; j ++ ) {
889
+ VALUE item2 = RARRAY_AREF (items , j );
890
+ if (RTEST (rb_yield_values (2 , item1 , item2 )) &&
891
+ RTEST (rb_yield_values (2 , item2 , item1 ))) {
892
+ set_divide_union_find_merge (uf_parents , i , j , tmp_array );
883
893
}
884
894
}
885
895
}
886
- args -> nj ++ ;
887
- return j ;
888
- }
889
-
890
- static VALUE
891
- set_divide_block (RB_BLOCK_CALL_FUNC_ARGLIST (i , arg ))
892
- {
893
- struct set_divide_args * args = (struct set_divide_args * )arg ;
894
- VALUE hash = args -> hash ;
895
- args -> current_set = rb_hash_aref (hash , i );
896
- args -> current_item = i ;
897
- args -> nj = 0 ;
898
- rb_block_call (args -> self , id_each , 0 , 0 , set_divide_block0 , arg );
899
- if (args -> current_set == Qnil ) {
900
- VALUE set = set_s_create (1 , & i , args -> set_class );
901
- rb_hash_aset (hash , i , set );
902
- set_i_add (args -> final_set , set );
903
- }
904
- args -> ni ++ ;
905
- return i ;
896
+ VALUE final_set = set_s_create (0 , 0 , rb_cSet );
897
+ VALUE hash = rb_hash_new ();
898
+ for (long i = 0 ; i < size ; i ++ ) {
899
+ VALUE v = RARRAY_AREF (items , i );
900
+ long root = set_divide_union_find_root (uf_parents , i , tmp_array );
901
+ VALUE set = rb_hash_aref (hash , LONG2FIX (root ));
902
+ if (set == Qnil ) {
903
+ set = set_s_create (0 , 0 , set_class );
904
+ rb_hash_aset (hash , LONG2FIX (root ), set );
905
+ set_i_add (final_set , set );
906
+ }
907
+ set_i_add (set , v );
908
+ }
909
+ ALLOCV_END (tmp );
910
+ ALLOCV_END (uf );
911
+ return final_set ;
906
912
}
907
913
908
914
static void set_merge_enum_into (VALUE set , VALUE arg );
@@ -936,19 +942,7 @@ set_i_divide(VALUE set)
936
942
RETURN_SIZED_ENUMERATOR (set , 0 , 0 , set_enum_size );
937
943
938
944
if (rb_block_arity () == 2 ) {
939
- VALUE final_set = set_s_create (0 , 0 , rb_cSet );
940
- struct set_divide_args args = {
941
- .self = set ,
942
- .set_class = rb_obj_class (set ),
943
- .final_set = final_set ,
944
- .hash = rb_hash_new (),
945
- .current_set = 0 ,
946
- .current_item = 0 ,
947
- .ni = 0 ,
948
- .nj = 0
949
- };
950
- rb_block_call (set , id_each , 0 , 0 , set_divide_block , (VALUE )& args );
951
- return final_set ;
945
+ return set_divide_arity2 (set );
952
946
}
953
947
954
948
VALUE values = rb_hash_values (set_i_classify (set ));
0 commit comments