<commit_before>
struct LaneTag
    segment :: Int # segment id
    lane    :: Int # index in segment.lanes of this lane
end
Base.show(io::IO, tag::LaneTag) = @printf(io, "LaneTag(%d, %d)", tag.segment, tag.lane)
hash(id::LaneTag, h::UInt=zero(UInt)) = hash(id.segment, hash(id.lane, h))
Base.:(==)(a::LaneTag, b::LaneTag) = a.segment == b.segment && a.lane == b.lane
const NULL_LANETAG = LaneTag(0,0)

#######################################

struct RoadIndex
    ind::CurveIndex
    tag::LaneTag
end
const NULL_ROADINDEX = RoadIndex(CurveIndex(-1,NaN), LaneTag(-1,-1))

Base.show(io::IO, r::RoadIndex) = @printf(io, "RoadIndex({%d, %3.f}, {%d, %d})", r.ind.i, r.ind.t, r.tag.segment, r.tag.lane)
Base.write(io::IO, r::RoadIndex) = @printf(io, "%d %.6f %d %d", r.ind.i, r.ind.t, r.tag.segment, r.tag.lane)

#######################################

struct LaneConnection
    downstream::Bool # if true, mylane → target, else target → mylane
    mylane::CurveIndex
    target::RoadIndex
end

Base.show(io::IO, c::LaneConnection) = print(io, "LaneConnection(", c.downstream ? "D" : "U", ", ", c.mylane, ", ", c.target)
function Base.write(io::IO, c::LaneConnection)
    @printf(io, "%s (%d %.6f) ", c.downstream ? "D" : "U", c.mylane.i, c.mylane.t)
    write(io, c.target)
end
function Base.parse(::Type{LaneConnection}, line::String)
    cleanedline = replace(line, r"(\(|\))", "")
    tokens = split(cleanedline, ' ')

    @assert(tokens[1] == "D" || tokens[1] == "U")
    downstream = tokens[1] == "D"
    mylane = CurveIndex(parse(Int, tokens[2]), parse(Float64, tokens[3]))
    target = RoadIndex(
                CurveIndex(parse(Int, tokens[4]), parse(Float64, tokens[5])),
                LaneTag(parse(Int, tokens[6]), parse(Int, tokens[7]))
            )
    LaneConnection(downstream, mylane, target)
end

#######################################

const DEFAULT_LANE_WIDTH = 3.0 # [m]

mutable struct Lane
    tag            :: LaneTag
    curve          :: Curve
    width          :: Float64 # [m]
    speed_limit    :: SpeedLimit
    boundary_left  :: LaneBoundary
    boundary_right :: LaneBoundary
    exits          :: Vector{LaneConnection} # list of exits; put the primary exit (at end of lane) first
    entrances      :: Vector{LaneConnection} # list of entrances; put the primary entrance (at start of lane) first

    function Lane(
        tag::LaneTag,
        curve::Curve;
        width::Float64 = DEFAULT_LANE_WIDTH,
        speed_limit::SpeedLimit = DEFAULT_SPEED_LIMIT,
        boundary_left::LaneBoundary = NULL_BOUNDARY,
        boundary_right::LaneBoundary = NULL_BOUNDARY,
        exits::Vector{LaneConnection} = LaneConnection[],
        entrances::Vector{LaneConnection} = LaneConnection[],
        next::RoadIndex=NULL_ROADINDEX,
        prev::RoadIndex=NULL_ROADINDEX,
        )

        retval = new()
        retval.tag = tag
        retval.curve = curve
        retval.width = width
        retval.speed_limit = speed_limit
        retval.boundary_left = boundary_left
        retval.boundary_right = boundary_right
        retval.exits = exits
        retval.entrances = entrances

        if next != NULL_ROADINDEX
            unshift!(retval.exits, LaneConnection(true, curveindex_end(retval.curve), next))
        end
        if prev != NULL_ROADINDEX
            unshift!(retval.entrances, LaneConnection(false, CURVEINDEX_START, prev))
        end

        retval
    end
end

has_next(lane::Lane) = !isempty(lane.exits) && lane.exits[1].mylane == curveindex_end(lane.curve)
has_prev(lane::Lane) = !isempty(lane.entrances) && lane.entrances[1].mylane == CURVEINDEX_START

is_in_exits(lane::Lane, target::LaneTag) = findfirst(lc->lc.target.tag == target, lane.exits) != 0
is_in_entrances(lane::Lane, target::LaneTag) = findfirst(lc->lc.target.tag == target, lane.entrances) != 0

<commit_msg>added == and hash for Lane<commit_after>
struct LaneTag
    segment :: Int # segment id
    lane    :: Int # index in segment.lanes of this lane
end
Base.show(io::IO, tag::LaneTag) = @printf(io, "LaneTag(%d, %d)", tag.segment, tag.lane)
hash(id::LaneTag, h::UInt=zero(UInt)) = hash(id.segment, hash(id.lane, h))
Base.:(==)(a::LaneTag, b::LaneTag) = a.segment == b.segment && a.lane == b.lane
const NULL_LANETAG = LaneTag(0,0)

#######################################

struct RoadIndex
    ind::CurveIndex
    tag::LaneTag
end
const NULL_ROADINDEX = RoadIndex(CurveIndex(-1,NaN), LaneTag(-1,-1))

Base.show(io::IO, r::RoadIndex) = @printf(io, "RoadIndex({%d, %3.f}, {%d, %d})", r.ind.i, r.ind.t, r.tag.segment, r.tag.lane)
Base.write(io::IO, r::RoadIndex) = @printf(io, "%d %.6f %d %d", r.ind.i, r.ind.t, r.tag.segment, r.tag.lane)

#######################################

struct LaneConnection
    downstream::Bool # if true, mylane → target, else target → mylane
    mylane::CurveIndex
    target::RoadIndex
end

Base.show(io::IO, c::LaneConnection) = print(io, "LaneConnection(", c.downstream ? "D" : "U", ", ", c.mylane, ", ", c.target)
function Base.write(io::IO, c::LaneConnection)
    @printf(io, "%s (%d %.6f) ", c.downstream ? "D" : "U", c.mylane.i, c.mylane.t)
    write(io, c.target)
end
function Base.parse(::Type{LaneConnection}, line::String)
    cleanedline = replace(line, r"(\(|\))", "")
    tokens = split(cleanedline, ' ')

    @assert(tokens[1] == "D" || tokens[1] == "U")
    downstream = tokens[1] == "D"
    mylane = CurveIndex(parse(Int, tokens[2]), parse(Float64, tokens[3]))
    target = RoadIndex(
                CurveIndex(parse(Int, tokens[4]), parse(Float64, tokens[5])),
                LaneTag(parse(Int, tokens[6]), parse(Int, tokens[7]))
            )
    LaneConnection(downstream, mylane, target)
end

#######################################

const DEFAULT_LANE_WIDTH = 3.0 # [m]

mutable struct Lane
    tag            :: LaneTag
    curve          :: Curve
    width          :: Float64 # [m]
    speed_limit    :: SpeedLimit
    boundary_left  :: LaneBoundary
    boundary_right :: LaneBoundary
    exits          :: Vector{LaneConnection} # list of exits; put the primary exit (at end of lane) first
    entrances      :: Vector{LaneConnection} # list of entrances; put the primary entrance (at start of lane) first

    function Lane(
        tag::LaneTag,
        curve::Curve;
        width::Float64 = DEFAULT_LANE_WIDTH,
        speed_limit::SpeedLimit = DEFAULT_SPEED_LIMIT,
        boundary_left::LaneBoundary = NULL_BOUNDARY,
        boundary_right::LaneBoundary = NULL_BOUNDARY,
        exits::Vector{LaneConnection} = LaneConnection[],
        entrances::Vector{LaneConnection} = LaneConnection[],
        next::RoadIndex=NULL_ROADINDEX,
        prev::RoadIndex=NULL_ROADINDEX,
        )

        retval = new()
        retval.tag = tag
        retval.curve = curve
        retval.width = width
        retval.speed_limit = speed_limit
        retval.boundary_left = boundary_left
        retval.boundary_right = boundary_right
        retval.exits = exits
        retval.entrances = entrances

        if next != NULL_ROADINDEX
            unshift!(retval.exits, LaneConnection(true, curveindex_end(retval.curve), next))
        end
        if prev != NULL_ROADINDEX
            unshift!(retval.entrances, LaneConnection(false, CURVEINDEX_START, prev))
        end

        retval
    end
end

function Base.:(==)(l1::Lane, l2::Lane)
    return l1.tag == l2.tag &&
            l1.curve == l2.curve &&
            l1.width == l2.width &&
            l1.speed_limit == l2.speed_limit &&
            l1.boundary_left == l2.boundary_left &&
            l1.boundary_right == l2.boundary_right &&
            l1.exits == l2.exits &&
            l1.entrances == l2.entrances
end
Base.hash(l::Lane, h::UInt) = hash(l.curve, hash(l.width, hash(l.speed_limit, hash(l.boundary_left, hash(l.boundary_right, hash(l.exits, hash(l.entrances, h)))))))

has_next(lane::Lane) = !isempty(lane.exits) && lane.exits[1].mylane == curveindex_end(lane.curve)
has_prev(lane::Lane) = !isempty(lane.entrances) && lane.entrances[1].mylane == CURVEINDEX_START

is_in_exits(lane::Lane, target::LaneTag) = findfirst(lc->lc.target.tag == target, lane.exits) != 0
is_in_entrances(lane::Lane, target::LaneTag) = findfirst(lc->lc.target.tag == target, lane.entrances) != 0


{'fraction_non_alphanumeric': 0.17752918287937744, 'fraction_numerical': 0.009727626459143969, 'mean_word_length': 3.3449550977284734, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1176845', 'n_tokens_mistral': 2819, 'n_tokens_neox': 2801, 'n_words': 563}