s0rg/decompose

View on GitHub
internal/cluster/node.go

Summary

Maintainability
A
0 mins
Test Coverage
A
96%
package cluster

import (
    "math"
    "slices"

    "github.com/s0rg/set"

    "github.com/s0rg/decompose/internal/node"
)

type Node struct {
    Inbounds  set.Set[string]
    Outbounds set.Set[string]
    Ports     *node.Ports
}

func (n *Node) Clone() *Node {
    return &Node{
        Inbounds:  n.Inbounds.Clone(),
        Outbounds: n.Outbounds.Clone(),
        Ports:     n.Ports,
    }
}

const (
    onei = 1
    onef = 1.0
    half = 0.5
)

func (n *Node) Match(id string, o *Node) (rv float64) {
    rv = (n.matchConns(id) + n.matchPorts(o.Ports)) * half

    return rv
}

func (n *Node) Merge(o *Node) {
    n.Ports.Join(o.Ports)
    n.Ports.Compact()

    n.Inbounds = set.Union(n.Inbounds, o.Inbounds)
    n.Outbounds = set.Union(n.Outbounds, o.Outbounds)
}

func (n *Node) matchConns(id string) (rv float64) {
    if n.Inbounds.Has(id) {
        rv += half
    }

    if n.Outbounds.Has(id) {
        rv += half
    }

    return rv
}

func (n *Node) matchPorts(p *node.Ports) (rv float64) {
    var (
        a = portsToProtos(n.Ports)
        b = portsToProtos(p)
    )

    if len(a) > len(b) {
        a, b = b, a
    }

    for k, ap := range a {
        bp := b[k]

        rv += matchSlices(ap, bp) / float64(len(a))
    }

    return rv
}

func portsToProtos(ports *node.Ports) (rv map[string][]int) {
    rv = make(map[string][]int)

    ports.Iter(func(_ string, pl []*node.Port) {
        for _, p := range pl {
            if p.Kind == "unix" {
                continue
            }

            rv[p.Kind] = append(rv[p.Kind], p.Number)
        }
    })

    for k := range rv {
        slices.Sort(rv[k])
    }

    return rv
}

func matchSlices(a, b []int) (rv float64) {
    sa, sb := make(set.Unordered[int]), make(set.Unordered[int])

    if len(a) < len(b) {
        a, b = b, a
    }

    set.Load(sa, a...)
    set.Load(sb, b...)

    u := set.Union(sa, sb).Len()
    c := set.Intersect(sa, sb).Len()

    if u == c {
        return onef
    }

    da, db := set.Diff(sa, sb), set.Diff(sb, sa)

    if da.Len() < db.Len() {
        da, db = db, da
    }

    rv = float64(da.Len()) / math.Abs(float64(u)-float64(c))

    das, dab := set.ToSlice(da), set.ToSlice(db)

    slices.Sort(das)
    slices.Sort(dab)

    m := float64(db.Len()) / float64(u)

    for i := 0; i < len(das); i++ {
        for j := 0; j < len(dab); j++ {
            if abs(das[i]-dab[j]) == onei {
                rv += m
            }
        }
    }

    return rv
}

func abs(v int) int {
    if v < 0 {
        v = -v
    }

    return v
}