package position import ( "math" "strings" "github.com/metatube-community/metatube-sdk-go/common/cluster" ) var ( _ cluster.Locatable[Vector, float64] = (*Vector)(nil) _ cluster.WeightedLocatable[WeightedVector, float64, float64] = (*WeightedVector)(nil) ) type Vector struct { vector []Position } func NewVector(pos ...Position) Vector { return Vector{append([]Position(nil), pos...)} } func (v Vector) At(i int) Position { if i < 0 || i >= len(v.vector) { panic("index out of range") } return v.vector[i] } func (v Vector) Dim() int { return len(v.vector) } func (v Vector) DistanceTo(o Vector) float64 { if v.Dim() != o.Dim() { panic("dimension mismatch") } switch v.Dim() { case 0: return 0.0 case 1: return v.vector[0].DistanceTo(o.vector[0]) case 2: return math.Hypot( v.vector[0].DistanceTo(o.vector[0]), v.vector[1].DistanceTo(o.vector[1]), ) default: var sum float64 for i := range v.vector { d := v.vector[i].DistanceTo(o.vector[i]) sum += d * d } return math.Sqrt(sum) } } func (v Vector) IsValid() bool { for _, p := range v.vector { if !p.IsValid() { return false } } return true } func (v Vector) Select(dims ...int) Vector { vec := make([]Position, len(dims)) for i, d := range dims { if d < 0 || d >= len(v.vector) { panic("index out of range") } vec[i] = v.vector[d] } return Vector{vec} } func (v Vector) String() string { var parts []string for _, p := range v.vector { parts = append(parts, p.String()) } return "(" + strings.Join(parts, ",") + ")" } type WeightedVector struct { Vector weight float64 } func NewWeightedVector(vector Vector, weight float64) WeightedVector { if weight < 0 { panic("weight must be non-negative") } return WeightedVector{ Vector: vector, weight: weight, } } func (v WeightedVector) DistanceTo(o WeightedVector) float64 { return v.Vector.DistanceTo(o.Vector) } func (v WeightedVector) Weight() float64 { return v.weight }