// File: graph_adjacency_list.go
// Created Time: 2023-01-31
// Author: Reanon (793584285@qq.com)

package chapter_graph

import (
	"fmt"
	"strconv"
	"strings"
)

/* 顶点类 */
type vertex struct {
	val int
}

func newVertex(val int) vertex {
	return vertex{
		val: val,
	}
}

/* 基于邻接表实现的无向图类 */
type graphAdjList struct {
	// 请注意，vertices 和 adjList 中存储的都是 Vertex 对象
	// 邻接表（使用哈希表实现）, 使用哈希表模拟集合
	adjList map[vertex]map[vertex]struct{}
}

/* 构造函数 */
func newGraphAdjList(edges [][]vertex) *graphAdjList {
	g := &graphAdjList{
		adjList: make(map[vertex]map[vertex]struct{}),
	}
	// 添加所有顶点和边
	for _, edge := range edges {
		g.addVertex(edge[0])
		g.addVertex(edge[1])
		g.addEdge(edge[0], edge[1])
	}
	return g
}

/* 获取顶点数量 */
func (g *graphAdjList) size() int {
	return len(g.adjList)
}

/* 添加边 */
func (g *graphAdjList) addEdge(vet1 vertex, vet2 vertex) {
	_, ok1 := g.adjList[vet1]
	_, ok2 := g.adjList[vet2]
	if !ok1 || !ok2 || vet1 == vet2 {
		panic("error")
	}
	// 添加边 vet1 - vet2, 添加匿名 struct{},
	g.adjList[vet1][vet2] = struct{}{}
	g.adjList[vet2][vet1] = struct{}{}
}

/* 删除边 */
func (g *graphAdjList) removeEdge(vet1 vertex, vet2 vertex) {
	_, ok1 := g.adjList[vet1]
	_, ok2 := g.adjList[vet2]
	if !ok1 || !ok2 || vet1 == vet2 {
		panic("error")
	}
	// 删除边 vet1 - vet2, 借助 delete 来删除 map 中的键
	delete(g.adjList[vet1], vet2)
	delete(g.adjList[vet2], vet1)
}

/* 添加顶点 */
func (g *graphAdjList) addVertex(vet vertex) {
	_, ok := g.adjList[vet]
	if ok {
		return
	}
	// 在邻接表中添加一个新链表（即 set）
	g.adjList[vet] = make(map[vertex]struct{})
}

/* 删除顶点 */
func (g *graphAdjList) removeVertex(vet vertex) {
	_, ok := g.adjList[vet]
	if !ok {
		panic("error")
	}
	// 在邻接表中删除顶点 vet 对应的链表
	delete(g.adjList, vet)
	// 遍历其它顶点的链表（即 Set），删除所有包含 vet 的边
	for _, set := range g.adjList {
		// 操作
		delete(set, vet)
	}
}

/* 打印邻接表 */
func (g *graphAdjList) print() {
	var builder strings.Builder
	fmt.Printf("邻接表 = \n")
	for k, v := range g.adjList {
		builder.WriteString("\t\t" + strconv.Itoa(k.val) + ": ")
		for vet := range v {
			builder.WriteString(strconv.Itoa(vet.val) + " ")
		}
		fmt.Println(builder.String())
		builder.Reset()
	}
}
