12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455typeorigin_zero_line=intmoduleOrigin_zero_placement=structtypet=Auto|Lineoforigin_zero_line|Spanofintendtypetrack_counts={negative_implicit:int;explicit:int;positive_implicit:int;}typegrid_line=intletgrid_line_to_origin_zero_line(line:grid_line)explicit_track_count=ifline>0then(* Positive lines start at 1, so subtract 1 for 0-based index *)line-1elseifline<0then(* Negative lines count from the end *)explicit_track_count+line+1else(* Line 0 is invalid, treated as Auto *)0(* TrackCounts utilities *)(** Create track counts from explicit track count *)letmake_track_counts~negative_implicit~explicit~positive_implicit={negative_implicit;explicit;positive_implicit}(** Get total track count *)lettotal_track_countcounts=counts.negative_implicit+counts.explicit+counts.positive_implicit(** Convert origin-zero line to track index *)letoz_line_to_track(line:origin_zero_line)(counts:track_counts):intoption=letimplicit_start_line=-counts.negative_implicitinletend_line=counts.explicit+counts.positive_implicitinifline<implicit_start_linethenNoneelseifline>=end_linethenNoneelselettrack_index=line+counts.negative_implicitiniftrack_index<total_track_countcountsthenSometrack_indexelseNone(** Get the next track index from an origin-zero line *)letoz_line_to_next_track(line:origin_zero_line):int=ifline<0then0elseline(** Convert an origin-zero line range to track range *)letoz_line_range_to_track_rangestart_lineend_line=letstart_track=oz_line_to_next_trackstart_lineinletend_track=oz_line_to_next_trackend_linein(start_track,end_track)