@@ -395,68 +395,73 @@ def _add(self, q: int, r: int):
395395
396396 def _remove_element (self , q : int , r : int ) -> None :
397397 idx = self ._contained_at_loc (q , r )
398-
399- # element not in the filter, exit
400398 if idx == - 1 :
401399 return
402400
403401 next_idx = (idx + 1 ) & self .__mod_size
402+ remove_orig_idx = self ._should_remove_orig_idx (idx , next_idx )
404403
405- # track if this is the only element in this run...
406- remove_orig_idx = False
407- if self ._is_run_or_cluster_start (idx ) and self ._is_continuation [next_idx ] == 0 :
408- remove_orig_idx = True
409-
410- # element is the end of a cluster and the next element is either the beginning of a cluster or empty
411404 if self ._is_empty_element (next_idx ) or self ._is_cluster_start (next_idx ):
412- self ._filter [idx ] = 0
413- self ._is_occupied .clear_bit (idx )
414- self ._is_continuation .clear_bit (idx )
415- self ._is_shifted .clear_bit (idx )
416-
417- if remove_orig_idx :
418- self ._is_occupied [q ] = 0
405+ self ._remove_and_clear_bits (idx , q , remove_orig_idx )
419406 return
420407
421- # find the minimum idx for the cluster; will be needed to determine if elements are in cluster start positions.
408+ min_idx = self ._find_cluster_start (idx )
409+ idx , next_idx = self ._handle_first_move (idx , next_idx )
410+ idx , next_idx = self ._shift_elements (idx , next_idx )
411+ self ._clear_last_element (idx )
412+ if remove_orig_idx :
413+ self ._is_occupied [q ] = 0
414+ self ._fixup_cluster (min_idx , next_idx )
415+
416+ def _should_remove_orig_idx (self , idx : int , next_idx : int ) -> bool :
417+ return self ._is_run_or_cluster_start (idx ) and self ._is_continuation [next_idx ] == 0
418+
419+ def _remove_and_clear_bits (self , idx : int , q : int , remove_orig_idx : bool ) -> None :
420+ self ._filter [idx ] = 0
421+ self ._is_occupied .clear_bit (idx )
422+ self ._is_continuation .clear_bit (idx )
423+ self ._is_shifted .clear_bit (idx )
424+ if remove_orig_idx :
425+ self ._is_occupied [q ] = 0
426+
427+ def _find_cluster_start (self , idx : int ) -> int :
422428 min_idx = idx
423429 while not self ._is_cluster_start (min_idx ):
424430 min_idx = (min_idx - 1 ) & self .__mod_size
431+ return min_idx
425432
426- # this is an edge case for first move...
433+ def _handle_first_move ( self , idx : int , next_idx : int ):
427434 if self ._is_run_or_cluster_start (idx ) and self ._is_continuation [next_idx ] == 1 :
428435 self ._filter [idx ] = self ._filter [next_idx ]
429436 self ._is_continuation [idx ] = 0
430437 self ._is_shifted [idx ] = self ._is_shifted [next_idx ]
431-
432438 idx = next_idx
433439 next_idx = (idx + 1 ) & self .__mod_size
440+ return idx , next_idx
434441
442+ def _shift_elements (self , idx : int , next_idx : int ):
435443 while not self ._is_cluster_start (next_idx ) and not self ._is_empty_element (next_idx ):
436444 self ._filter [idx ] = self ._filter [next_idx ]
437445 self ._is_continuation [idx ] = self ._is_continuation [next_idx ]
438446 self ._is_shifted [idx ] = self ._is_shifted [next_idx ]
439-
440447 idx = next_idx
441448 next_idx = (idx + 1 ) & self .__mod_size
442- # clean out the last element
449+ return idx , next_idx
450+
451+ def _clear_last_element (self , idx : int ) -> None :
443452 self ._filter [idx ] = 0
444453 self ._is_continuation [idx ] = 0
445454 self ._is_shifted [idx ] = 0
446455 self ._is_occupied [idx ] = 0
447456
448- if remove_orig_idx :
449- self ._is_occupied [q ] = 0
450-
451- # now figure out if things are in the correct place....
457+ def _fixup_cluster (self , min_idx : int , next_idx : int ) -> None :
452458 cur_quot = - 1
453459 queue : list [int ] = []
454460 while min_idx != next_idx :
455461 if self ._is_occupied [min_idx ] == 1 :
456462 queue .append (min_idx )
457463 if self ._is_run_start (min_idx ) == 1 :
458464 cur_quot = queue .pop (0 )
459-
460465 if cur_quot == min_idx :
461466 self ._is_continuation [min_idx ] = 0
462467 self ._is_shifted [min_idx ] = 0
0 commit comments