@@ -390,3 +390,53 @@ def calculate_moon_phase(moon_abs_pos: float, sun_abs_pos: float) -> LunarPhaseM
390390 }
391391
392392 return LunarPhaseModel (** lunar_phase_dictionary )
393+
394+
395+ def circular_sort (degrees : list [Union [int , float ]]) -> list [Union [int , float ]]:
396+ """
397+ Sort a list of degrees in a circular manner, starting from the first element
398+ and progressing clockwise around the circle.
399+
400+ Args:
401+ degrees: A list of numeric values representing degrees
402+
403+ Returns:
404+ A list sorted based on circular clockwise progression from the first element
405+
406+ Raises:
407+ ValueError: If the list is empty or contains non-numeric values
408+ """
409+ # Input validation
410+ if not degrees :
411+ raise ValueError ("Input list cannot be empty" )
412+
413+ if not all (isinstance (degree , (int , float )) for degree in degrees ):
414+ invalid = next (d for d in degrees if not isinstance (d , (int , float )))
415+ raise ValueError (f"All elements must be numeric, found: { invalid } of type { type (invalid ).__name__ } " )
416+
417+ # If list has 0 or 1 element, return it as is
418+ if len (degrees ) <= 1 :
419+ return degrees .copy ()
420+
421+ # Save the first element as the reference
422+ reference = degrees [0 ]
423+
424+ # Define a function to calculate clockwise distance from reference
425+ def clockwise_distance (angle : Union [int , float ]) -> Union [int , float ]:
426+ # Normalize angles to 0-360 range
427+ ref_norm = reference % 360
428+ angle_norm = angle % 360
429+
430+ # Calculate clockwise distance
431+ distance = angle_norm - ref_norm
432+ if distance < 0 :
433+ distance += 360
434+
435+ return distance
436+
437+ # Sort the rest of the elements based on circular distance
438+ remaining = degrees [1 :]
439+ sorted_remaining = sorted (remaining , key = clockwise_distance )
440+
441+ # Return the reference followed by the sorted remaining elements
442+ return [reference ] + sorted_remaining
0 commit comments