// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package graphalg

import "github.com/aclements/go-moremath/graph"

// SimplifyMulti simplifies a multigraph to a weighted simple graph.
//
// If g is a weighted graph, each edge in the result receives the sum
// of the weights of the combined edges in g. If g is not weighted,
// each edge in g is assumed to have a weight of 1.
func SimplifyMulti(g graph.Graph) graph.Weighted {
	gw, ok := g.(graph.Weighted)
	if !ok {
		gw = graph.WeightedUnit{g}
	}

	indexes := make([]int, gw.NumNodes()+1)
	var edges []int
	var weights []float64
	edgeMap := make(map[int]int)
	for n := range indexes[1:] {
		for k := range edgeMap {
			delete(edgeMap, k)
		}
		for i, o := range gw.Out(n) {
			if idx, ok := edgeMap[o]; ok {
				// Already have an edge.
				weights[idx] += gw.OutWeight(n, i)
			} else {
				edgeMap[o] = len(edges)
				edges = append(edges, o)
				weights = append(weights, gw.OutWeight(n, i))
			}
		}
		indexes[n+1] = len(edges)
	}
	return &simplified{indexes, edges, weights}
}

type simplified struct {
	indexes []int
	edges   []int
	weights []float64
}

func (g *simplified) NumNodes() int {
	return len(g.indexes) - 1
}

func (g *simplified) Out(n int) []int {
	return g.edges[g.indexes[n]:g.indexes[n+1]]
}

func (g *simplified) OutWeight(n, e int) float64 {
	return g.weights[g.indexes[n]:g.indexes[n+1]][e]
}
