Showing 1 of 1 files from the diff.

@@ -307,23 +307,21 @@
Loading
307 307
end
308 308
309 309
function reduce_nodes(dfa::DFA)
310 -
    Q = Set(traverse(dfa.start))
311 -
    distinct = distinct_nodes(Q)
310 +
    equivalents = get_equivalent(collect(traverse(dfa.start)))
312 311
    newnodes = Dict{Set{DFANode},DFANode}()
313 312
    new(S) = get!(newnodes, S) do
314 313
        s = first(S)
315 314
        return DFANode(s.final, s.eof_actions, foldl((x, s) -> union(x, s.nfanodes), S, init=Set{NFANode}()))
316 315
    end
317 -
    equivalent(s) = filter(t -> (s, t) ∉ distinct, Q)
318 316
    isvisited(T) = haskey(newnodes, T)
319 -
    S = equivalent(dfa.start)
317 +
    S = equivalents[dfa.start]
320 318
    start = new(S)
321 319
    unvisited = [S]
322 320
    while !isempty(unvisited)
323 321
        S = pop!(unvisited)
324 322
        s′ = new(S)
325 323
        for (e, t) in first(S).edges
326 -
            T = equivalent(t)
324 +
            T = equivalents[t]
327 325
            if !isvisited(T)
328 326
                push!(unvisited, T)
329 327
            end
@@ -346,35 +344,100 @@
Loading
346 344
    return DFA(start)
347 345
end
348 346
349 -
function distinct_nodes(S::Set{DFANode})
350 -
    labels = Dict(s => foldl((x, y) -> union(x, y[1].labels), s.edges, init=ByteSet()) for s in S)
351 -
    distinct = Set{Tuple{DFANode,DFANode}}()
352 -
353 -
    for s1 in S, s2 in S
354 -
        if s1.final != s2.final || labels[s1] != labels[s2] || s1.eof_actions != s2.eof_actions
355 -
            push!(distinct, (s1, s2))
347 +
function get_groupof(v::Vector{DFANode})
348 +
    # These are sets of the outgoing bytes from every node
349 +
    labels = Dict(s => foldl((x, y) -> union(x, y[1].labels), s.edges, init=ByteSet()) for s in v)
350 +
    
351 +
    # First we create groups of nodes that MAY be identical based on quick-to-compute
352 +
    # characteristics. This narrows down the number of comparisons needed later.
353 +
    # Nodes may be equal if the have the same outgoing bytes, same final node and same
354 +
    # EOF actions.
355 +
    groupof = Dict{DFANode,Vector{DFANode}}()
356 +
    for s1 in v
357 +
        unique = true
358 +
        for group in values(groupof)
359 +
            s2 = first(group)
360 +
            if s1.final == s2.final && labels[s1] == labels[s2] && s1.eof_actions == s2.eof_actions
361 +
                push!(group, s1)
362 +
                groupof[s1] = group
363 +
                unique = false
364 +
                break
365 +
            end
356 366
        end
367 +
        unique && (groupof[s1] = [s1])
357 368
    end
369 +
    return groupof
370 +
end
358 371
359 -
    converged = false
360 -
    while !converged
361 -
        converged = true
362 -
        for s1 in S, s2 in S
363 -
            if s1 == s2 || (s1, s2) ∈ distinct
364 -
                continue
365 -
            end
366 -
            @assert labels[s1] == labels[s2] && s1.eof_actions == s2.eof_actions
372 +
function equivalent_pairs(groupof, v)
373 +
    # Here, we check each pair in each group. If they share an edge with an overlapping byte,
374 +
    # compatible preconditions, but the edge have different actions or leads no a non-equivalent
375 +
    # node, they're different. Because a parent's nonequivalence relies on the status of
376 +
    # its children, we need to update the parents every time we update a node.
377 +
    equivalent_pairs = Set{Tuple{DFANode,DFANode}}()
378 +
    for group in values(groupof), s1 in group, s2 in group
379 +
        push!(equivalent_pairs, (s1, s2))
380 +
    end
381 +
    
382 +
    # Get a node => vector of parents of node dict
383 +
    parentsof = Dict{DFANode,Vector{DFANode}}()
384 +
    for s in v, (e,c) in s.edges
385 +
        if haskey(parentsof, c)
386 +
            push!(parentsof[c], s)
387 +
        else
388 +
            parentsof[c] = [s]
389 +
        end
390 +
    end
391 +
    unupdated = Set(v)
392 +
    while !isempty(unupdated)
393 +
        s1 = pop!(unupdated)
394 +
        group = groupof[s1]
395 +
        for s2 in group
396 +
            (s1 == s2 || (s1, s2) ∉ equivalent_pairs) && continue
367 397
            for (e1, t1) in s1.edges, (e2, t2) in s2.edges
368 -
                if overlaps(e1, e2) && ((t1, t2) ∈ distinct || e1.actions != e2.actions)
369 -
                    push!(distinct, (s1, s2), (s2, s1))
370 -
                    converged = false
398 +
                if overlaps(e1, e2) && ((t1, t2) ∉ equivalent_pairs || e1.actions != e2.actions)
399 +
                    delete!(equivalent_pairs, (s1, s2))
400 +
                    haskey(parentsof, s1) && union!(unupdated, parentsof[s1])
371 401
                    break
372 402
                end
373 403
            end
374 404
        end
375 405
    end
406 +
    return equivalent_pairs
407 +
end
376 408
377 -
    return distinct
409 +
function split_group(group::Vector{DFANode}, pairs)
410 +
    remaining = copy(group)
411 +
    result = Vector{DFANode}[]
412 +
    nonequivalents = DFANode[]
413 +
    while !isempty(remaining)
414 +
        s1 = first(remaining)
415 +
        equivalents = DFANode[]
416 +
        for s2 in remaining
417 +
            (s1, s2) ∈ pairs ? push!(equivalents, s2) : push!(nonequivalents, s2)
418 +
        end
419 +
        push!(result, equivalents)
420 +
        (nonequivalents, remaining) = (remaining, nonequivalents)
421 +
        empty!(nonequivalents)
422 +
    end
423 +
    return result
424 +
end
425 +
426 +
"Creates a DFANode => Set{DFANode} dict with equivalent nodes for every node in v"
427 +
function get_equivalent(v::Vector{DFANode})
428 +
    groupof = get_groupof(v)
429 +
    pairs = equivalent_pairs(groupof, v)
430 +
    groups = collect(keys(IdDict{Vector{DFANode},Nothing}(v => nothing for v in values(groupof))))
431 +
    result = Dict{DFANode,Set{DFANode}}()
432 +
    for group in groups
433 +
        for subgroup in split_group(group, pairs)
434 +
            S = Set(subgroup)
435 +
            for node in subgroup
436 +
                result[node] = S
437 +
            end
438 +
        end
439 +
    end
440 +
    return result
378 441
end
379 442
380 443
function overlaps(e1::Edge, e2::Edge)
Files Coverage
src 95.24%
Project Totals (17 files) 95.24%
Sunburst
The inner-most circle is the entire project, moving away from the center are folders then, finally, a single file. The size and color of each slice is representing the number of statements and the coverage, respectively.
Icicle
The top section represents the entire project. Proceeding with folders and finally individual files. The size and color of each slice is representing the number of statements and the coverage, respectively.
Grid
Each block represents a single file in the project. The size and color of each block is represented by the number of statements and the coverage, respectively.
Loading