<commit_before>package main_test

import (
	"github.com/codegangsta/cli"

	. "github.com/goldeneggg/gat"
)

const (
	testCommandsDir = "./test/commands_test"
	testTextFile    = testCommandsDir + "/test.txt"
	testTextFile2   = testCommandsDir + "/test2.txt"
	testEmptyFile   = testCommandsDir + "/test_empty.txt"
)

var app *cli.App

func init() {
	app = cli.NewApp()
	app.Name = "gatTest"
	app.Version = Version
	app.Usage = "Test gat"
	app.Author = "@goldeneggg"
	app.Email = "jpshadowapps@gmail.com"

	app.Flags = GlobalFlags
	app.Commands = Commands
}

// global flags
func ExampleHelp() {
	app.Run([]string{"", "-h"})
}

func ExampleVersion() {
	app.Run([]string{"", "-v"})
	// Output:
	// gatTest version 0.3.0
}

func ExampleVersionRunningCommand() {
	app.Run([]string{"", "-v", "gist", testTextFile})
	// Output:
	// gatTest version 0.3.0
}

// "gist" Command
func ExampleRunGistEmptyDomain() {
	app.Run([]string{"", "-c", testCommandsDir + "/test_conf_gist_e_domain.json", "gist", testTextFile})
	// Output:
}

func ExampleRunGistEmptyToken() {
	app.Run([]string{"", "-c", testCommandsDir + "/test_conf_gist_e_token.json", "gist", testTextFile})
	// Output:
}

func ExampleRunGistNullDomain() {
	app.Run([]string{"", "-c", testCommandsDir + "/test_conf_gist_null_domain.json", "gist", testTextFile})
	// Output:
}

func ExampleRunGistNullToken() {
	app.Run([]string{"", "-c", testCommandsDir + "/test_conf_gist_null_token.json", "gist", testTextFile})
	// Output:
}

func ExampleRunGistInvalidDomain() {
	app.Run([]string{"", "-c", testCommandsDir + "/test_conf_gist_i_domain.json", "gist", testTextFile})
	// Output:
}

func ExampleRunGistNotFound() {
	app.Run([]string{"", "-c", testCommandsDir + "/test_conf_gist_notfound.json", "gist", testTextFile})
	// Output:
}

func ExampleRunGistHelp() {
	app.Run([]string{"", "-c", testCommandsDir + "/test_conf_gist_e_domain.json", "gist", "-h"})
	// Output:
	// NAME:
	//    gist - Cat to gist
	//
	// USAGE:
	//    command gist [command options] [arguments...]
	//
	// OPTIONS:
	//    --api-domain 	Github api domain
	//    --access-token 	Github api access token
	//    --timeout '0'	Timeout for connection
	//    --description, -d 	A description of the gist
	//    --public, -p		Indicates whether the gist is public. Default: false
}

// "slack" command
func ExampleRunSlackEmptyUrl() {
	app.Run([]string{"", "-c", testCommandsDir + "/test_conf_slack_e_domain.json", "slack", testTextFile})
	// Output:
}

func ExampleRunSlackNullUrl() {
	app.Run([]string{"", "-c", testCommandsDir + "/test_conf_slack_null_domain.json", "slack", testTextFile})
	// Output:
}

func ExampleRunSlackHelp() {
	app.Run([]string{"", "-c", testCommandsDir + "/test_conf_slack_e_domain.json", "slack", "-h"})
	// Output:
	// NAME:
	//    slack - Cat to slack
	//
	// USAGE:
	//    command slack [command options] [arguments...]
	//
	// OPTIONS:
	//    --webhook-url 	Webhook URL
	//    --channel, -c 	Target channel
	//    --username, -u 	Username
	//    --icon, -i 		Icon url or emoji format text (:EMOJI_NAME:)
	//    --timeout '0'	Timeout for connection
	//    --without-markdown	Not format slack's markdown
	//    --without-unfurl	Not unfurl media links
	//    --linkfy, -l		Linkify channel names (starting with a '#') and usernames (starting with an '@')
}

func ExampleRunPlaygoHelp() {
	app.Run([]string{"", "-c", testCommandsDir + "/test_conf_slack_e_domain.json", "playgo", "-h"})
	// Output:
	// NAME:
	//    playgo - Cat to play.golang.org
	//
	// USAGE:
	//    command playgo [arguments...]
}

// "list" Command
func ExampleRunListCommand() {
	app.Run([]string{"", "list"})
	// Output:
	// Supported gat commands are:
	//   gist  - Cat to gist
	//   slack  - Cat to slack
	//   playgo  - Cat to play.golang.org
}

func ExampleRunListCommandWithInput() {
	app.Run([]string{"", "list", testTextFile})
	// Output:
	// Supported gat commands are:
	//   gist  - Cat to gist
	//   slack  - Cat to slack
	//   playgo  - Cat to play.golang.org
}

/*
// "os" Command
func ExampleRunOs() {
	app.Run([]string{"", "-c", testCommandsDir + "/test_conf_os.json", "os", testTextFile})
	// Output:
	// test1
	// test2
	// test3
}
*/

/* XXX
func ExampleRunOsMultiInput() {
	app.Run([]string{"", "-c", testCommandsDir + "/test_conf_os.json", "os", testTextFile, testTextFile2})
	// Output:
	// test1
	// test2
	// test3
	// TEST1
	// TEST2
	// TEST3
}
*/

/*
func ExampleRunOsN() {
	app.Run([]string{"", "-c", testCommandsDir + "/test_conf_os_n.json", "os", testTextFile})
	// Output:
	//      1	test1
	//      2	test2
	//      3	test3
}

func ExampleRunOsB() {
	app.Run([]string{"", "--confpath", testCommandsDir + "/test_conf_os_b.json", "os", testTextFile})
	// Output:
	//      1	test1
	//      2	test2
	//      3	test3
}

// use conf.json that does not have keys.
func ExampleRunOsNoKey() {
	app.Run([]string{"", "-c", testCommandsDir + "/test_conf_os_nokey.json", "os", testTextFile})
	// Output:
	// test1
	// test2
	// test3
}

func ExampleRunOsNoKeyOptN() {
	app.Run([]string{"", "-c", testCommandsDir + "/test_conf_os_nokey.json", "os", "-n", testTextFile})
	// Output:
	//      1	test1
	//      2	test2
	//      3	test3
}

func ExampleRunOsConfNFalseOptN() {
	app.Run([]string{"", "-c", testCommandsDir + "/test_conf_os_n_false.json", "os", "-n", testTextFile})
	// Output:
	//      1	test1
	//      2	test2
	//      3	test3
}

func ExampleRunOsEmptyTarget() {
	app.Run([]string{"", "-c", testCommandsDir + "/test_conf_os.json", "os", testEmptyFile})
	// Output:
	//
}

func ExampleRunOsDirTarget() {
	app.Run([]string{"", "-c", testCommandsDir + "/test_conf_os.json", "os", testCommandsDir})
	// Output:
	//
}

func ExampleRunOsNoTarget() {
	app.Run([]string{"", "-c", testCommandsDir + "/test_conf_os.json", "os"})
	// Output:
	//
}
*/

// abnormal cases
func ExampleInvalidCommand() {
	app.Run([]string{"", "invalid", testTextFile})
	// Output:
	// No help topic for 'invalid'
}

func ExampleEmptyCommand() {
	app.Run([]string{"", testTextFile})
	// Output:
	// No help topic for './test/commands_test/test.txt'
}

func ExampleNotExistFile() {
	app.Run([]string{"", "gist", testCommandsDir + "/notexist.txt"})
	// Output:
	//
}
<commit_msg>fix fail tests using codegangsta/cli<commit_after>package main_test

import (
	"github.com/codegangsta/cli"

	. "github.com/goldeneggg/gat"
)

const (
	testCommandsDir = "./test/commands_test"
	testTextFile    = testCommandsDir + "/test.txt"
	testTextFile2   = testCommandsDir + "/test2.txt"
	testEmptyFile   = testCommandsDir + "/test_empty.txt"
)

func getApp() *cli.App {
	app := cli.NewApp()
	app.Name = "gatTest"
	app.Version = Version
	app.Usage = "Test gat"
	app.Author = "@goldeneggg"
	app.Email = "jpshadowapps@gmail.com"

	app.Flags = GlobalFlags
	app.Commands = Commands

	return app
}

// global flags
func ExampleHelp() {
	getApp().Run([]string{"", "-h"})
}

func ExampleVersion() {
	getApp().Run([]string{"", "-v"})
	// Output:
	// gatTest version 0.3.0
}

func ExampleVersionRunningCommand() {
	getApp().Run([]string{"", "-v", "gist", testTextFile})
	// Output:
	// gatTest version 0.3.0
}

// "gist" Command
func ExampleRunGistEmptyDomain() {
	getApp().Run([]string{"", "-c", testCommandsDir + "/test_conf_gist_e_domain.json", "gist", testTextFile})
	// Output:
}

func ExampleRunGistEmptyToken() {
	getApp().Run([]string{"", "-c", testCommandsDir + "/test_conf_gist_e_token.json", "gist", testTextFile})
	// Output:
}

func ExampleRunGistNullDomain() {
	getApp().Run([]string{"", "-c", testCommandsDir + "/test_conf_gist_null_domain.json", "gist", testTextFile})
	// Output:
}

func ExampleRunGistNullToken() {
	getApp().Run([]string{"", "-c", testCommandsDir + "/test_conf_gist_null_token.json", "gist", testTextFile})
	// Output:
}

func ExampleRunGistInvalidDomain() {
	getApp().Run([]string{"", "-c", testCommandsDir + "/test_conf_gist_i_domain.json", "gist", testTextFile})
	// Output:
}

func ExampleRunGistNotFound() {
	getApp().Run([]string{"", "-c", testCommandsDir + "/test_conf_gist_notfound.json", "gist", testTextFile})
	// Output:
}

func ExampleRunGistHelp() {
	getApp().Run([]string{"", "-c", testCommandsDir + "/test_conf_gist_e_domain.json", "gist", "-h"})
	// Output:
	// NAME:
	//    gist - Cat to gist
	//
	// USAGE:
	//    command gist [command options] [arguments...]
	//
	// OPTIONS:
	//    --api-domain 	Github api domain
	//    --access-token 	Github api access token
	//    --timeout '0'	Timeout for connection
	//    --description, -d 	A description of the gist
	//    --public, -p		Indicates whether the gist is public. Default: false
}

// "slack" command
func ExampleRunSlackEmptyUrl() {
	getApp().Run([]string{"", "-c", testCommandsDir + "/test_conf_slack_e_domain.json", "slack", testTextFile})
	// Output:
}

func ExampleRunSlackNullUrl() {
	getApp().Run([]string{"", "-c", testCommandsDir + "/test_conf_slack_null_domain.json", "slack", testTextFile})
	// Output:
}

func ExampleRunSlackHelp() {
	getApp().Run([]string{"", "-c", testCommandsDir + "/test_conf_slack_e_domain.json", "slack", "-h"})
	// Output:
	// NAME:
	//    slack - Cat to slack
	//
	// USAGE:
	//    command slack [command options] [arguments...]
	//
	// OPTIONS:
	//    --webhook-url 	Webhook URL
	//    --channel, -c 	Target channel
	//    --username, -u 	Username
	//    --icon, -i 		Icon url or emoji format text (:EMOJI_NAME:)
	//    --timeout '0'	Timeout for connection
	//    --without-markdown	Not format slack's markdown
	//    --without-unfurl	Not unfurl media links
	//    --linkfy, -l		Linkify channel names (starting with a '#') and usernames (starting with an '@')
}

func ExampleRunPlaygoHelp() {
	getApp().Run([]string{"", "-c", testCommandsDir + "/test_conf_slack_e_domain.json", "playgo", "-h"})
	// Output:
	// NAME:
	//    playgo - Cat to play.golang.org
	//
	// USAGE:
	//    command playgo [arguments...]
}

// "list" Command
func ExampleRunListCommand() {
	getApp().Run([]string{"", "list"})
	// Output:
	// Supported gat commands are:
	//   gist  - Cat to gist
	//   slack  - Cat to slack
	//   playgo  - Cat to play.golang.org
}

func ExampleRunListCommandWithInput() {
	getApp().Run([]string{"", "list", testTextFile})
	// Output:
	// Supported gat commands are:
	//   gist  - Cat to gist
	//   slack  - Cat to slack
	//   playgo  - Cat to play.golang.org
}

/*
// "os" Command
func ExampleRunOs() {
	getApp().Run([]string{"", "-c", testCommandsDir + "/test_conf_os.json", "os", testTextFile})
	// Output:
	// test1
	// test2
	// test3
}
*/

/* XXX
func ExampleRunOsMultiInput() {
	getApp().Run([]string{"", "-c", testCommandsDir + "/test_conf_os.json", "os", testTextFile, testTextFile2})
	// Output:
	// test1
	// test2
	// test3
	// TEST1
	// TEST2
	// TEST3
}
*/

/*
func ExampleRunOsN() {
	getApp().Run([]string{"", "-c", testCommandsDir + "/test_conf_os_n.json", "os", testTextFile})
	// Output:
	//      1	test1
	//      2	test2
	//      3	test3
}

func ExampleRunOsB() {
	getApp().Run([]string{"", "--confpath", testCommandsDir + "/test_conf_os_b.json", "os", testTextFile})
	// Output:
	//      1	test1
	//      2	test2
	//      3	test3
}

// use conf.json that does not have keys.
func ExampleRunOsNoKey() {
	getApp().Run([]string{"", "-c", testCommandsDir + "/test_conf_os_nokey.json", "os", testTextFile})
	// Output:
	// test1
	// test2
	// test3
}

func ExampleRunOsNoKeyOptN() {
	getApp().Run([]string{"", "-c", testCommandsDir + "/test_conf_os_nokey.json", "os", "-n", testTextFile})
	// Output:
	//      1	test1
	//      2	test2
	//      3	test3
}

func ExampleRunOsConfNFalseOptN() {
	getApp().Run([]string{"", "-c", testCommandsDir + "/test_conf_os_n_false.json", "os", "-n", testTextFile})
	// Output:
	//      1	test1
	//      2	test2
	//      3	test3
}

func ExampleRunOsEmptyTarget() {
	getApp().Run([]string{"", "-c", testCommandsDir + "/test_conf_os.json", "os", testEmptyFile})
	// Output:
	//
}

func ExampleRunOsDirTarget() {
	getApp().Run([]string{"", "-c", testCommandsDir + "/test_conf_os.json", "os", testCommandsDir})
	// Output:
	//
}

func ExampleRunOsNoTarget() {
	getApp().Run([]string{"", "-c", testCommandsDir + "/test_conf_os.json", "os"})
	// Output:
	//
}
*/

// abnormal cases
func ExampleInvalidCommand() {
	getApp().Run([]string{"", "invalid", testTextFile})
	// Output:
	// No help topic for 'invalid'
}

func ExampleEmptyCommand() {
	getApp().Run([]string{"", testTextFile})
	// Output:
	// No help topic for './test/commands_test/test.txt'
}

func ExampleNotExistFile() {
	getApp().Run([]string{"", "gist", testCommandsDir + "/notexist.txt"})
	// Output:
	//
}
<|endoftext|>


<commit_before>package gogl

type mutableDirected struct {
	al_basic_mut
}

/* mutableDirected additions */

// Returns the outdegree of the provided vertex. If the vertex is not present in the
// graph, the second return value will be false.
func (g *mutableDirected) OutDegreeOf(vertex Vertex) (degree int, exists bool) {
	g.mu.RLock()
	defer g.mu.RUnlock()

	if exists = g.hasVertex(vertex); exists {
		degree = len(g.list[vertex])
	}
	return
}

// Returns the indegree of the provided vertex. If the vertex is not present in the
// graph, the second return value will be false.
//
// Note that getting indegree is inefficient for directed adjacency lists; it requires
// a full scan of the graph's edge set.
func (g *mutableDirected) InDegreeOf(vertex Vertex) (degree int, exists bool) {
	g.mu.RLock()
	defer g.mu.RUnlock()
	return inDegreeOf(g, vertex)
}

// Returns the degree of the provided vertex, counting both in and out-edges.
func (g *mutableDirected) DegreeOf(vertex Vertex) (degree int, exists bool) {
	g.mu.RLock()
	defer g.mu.RUnlock()

	indegree, exists := inDegreeOf(g, vertex)
	outdegree, exists := g.OutDegreeOf(vertex)
	return indegree + outdegree, exists
}

// Traverses the set of edges in the graph, passing each edge to the
// provided closure.
func (g *mutableDirected) EachEdge(f EdgeStep) {
	g.mu.RLock()
	defer g.mu.RUnlock()

	for source, adjacent := range g.list {
		for target, _ := range adjacent {
			if f(NewEdge(source, target)) {
				return
			}
		}
	}
}

// Enumerates the set of all edges incident to the provided vertex.
func (g *mutableDirected) EachEdgeIncidentTo(v Vertex, f EdgeStep) {
	g.mu.RLock()
	defer g.mu.RUnlock()
	eachEdgeIncidentToDirected(g, v, f)
}

// Enumerates the vertices adjacent to the provided vertex.
func (g *mutableDirected) EachAdjacentTo(start Vertex, f VertexStep) {
	g.mu.RLock()
	defer g.mu.RUnlock()

	g.EachEdgeIncidentTo(start, func(e Edge) bool {
		u, v := e.Both()
		if u == start {
			return f(v)
		} else {
			return f(u)
		}
	})
}

// Enumerates the set of out-edges for the provided vertex.
func (g *mutableDirected) EachArcFrom(v Vertex, f EdgeStep) {
	g.mu.RLock()
	defer g.mu.RUnlock()

	if !g.hasVertex(v) {
		return
	}

	for adjacent, _ := range g.list[v] {
		if f(NewEdge(v, adjacent)) {
			return
		}
	}
}

func (g *mutableDirected) EachSuccessorOf(v Vertex, f VertexStep) {
	g.mu.RLock()
	defer g.mu.RUnlock()

	eachVertexInAdjacencyList(g.list, v, f)
}

// Enumerates the set of in-edges for the provided vertex.
func (g *mutableDirected) EachArcTo(v Vertex, f EdgeStep) {
	g.mu.RLock()
	defer g.mu.RUnlock()

	if !g.hasVertex(v) {
		return
	}

	for candidate, adjacent := range g.list {
		for target, _ := range adjacent {
			if target == v {
				if f(NewEdge(candidate, target)) {
					return
				}
			}
		}
	}
}

func (g *mutableDirected) EachPredecessorOf(v Vertex, f VertexStep) {
	g.mu.RLock()
	defer g.mu.RUnlock()

	if !g.hasVertex(v) {
		return
	}

	for candidate, adjacent := range g.list {
		for target, _ := range adjacent {
			if target == v {
				if f(candidate) {
					return
				}
			}
		}
	}
}

// Indicates whether or not the given edge is present in the graph.
func (g *mutableDirected) HasEdge(edge Edge) bool {
	g.mu.RLock()
	defer g.mu.RUnlock()

	_, exists := g.list[edge.Source()][edge.Target()]
	return exists
}

// Returns the density of the graph. Density is the ratio of edge count to the
// number of edges there would be in complete graph (maximum edge count).
func (g *mutableDirected) Density() float64 {
	g.mu.RLock()
	defer g.mu.RUnlock()

	order := g.Order()
	return float64(g.Size()) / float64(order*(order-1))
}

// Removes a vertex from the graph. Also removes any edges of which that
// vertex is a member.
func (g *mutableDirected) RemoveVertex(vertices ...Vertex) {
	if len(vertices) == 0 {
		return
	}

	g.mu.Lock()
	defer g.mu.Unlock()

	for _, vertex := range vertices {
		if g.hasVertex(vertex) {
			// TODO Is the expensive search good to do here and now...
			// while read-locked?
			g.size -= len(g.list[vertex])
			delete(g.list, vertex)

			// TODO consider chunking the list and parallelizing into goroutines
			for _, adjacent := range g.list {
				if _, has := adjacent[vertex]; has {
					delete(adjacent, vertex)
					g.size--
				}
			}
		}
	}
	return
}

// Adds edges to the graph.
func (g *mutableDirected) AddEdges(edges ...Edge) {
	if len(edges) == 0 {
		return
	}

	g.mu.Lock()
	defer g.mu.Unlock()

	g.addEdges(edges...)
}

// Adds a new edge to the graph.
func (g *mutableDirected) addEdges(edges ...Edge) {
	for _, edge := range edges {
		g.ensureVertex(edge.Source(), edge.Target())

		if _, exists := g.list[edge.Source()][edge.Target()]; !exists {
			g.list[edge.Source()][edge.Target()] = keyExists
			g.size++
		}
	}
}

// Removes edges from the graph. This does NOT remove vertex members of the
// removed edges.
func (g *mutableDirected) RemoveEdges(edges ...Edge) {
	if len(edges) == 0 {
		return
	}

	g.mu.Lock()
	defer g.mu.Unlock()

	for _, edge := range edges {
		s, t := edge.Both()
		if _, exists := g.list[s][t]; exists {
			delete(g.list[s], t)
			g.size--
		}
	}
}

// Returns a graph with the same vertex and edge set, but with the
// directionality of all its edges reversed.
//
// This implementation returns a new graph object (doubling memory use),
// but not all implementations do so.
func (g *mutableDirected) Transpose() Digraph {
	g.mu.RLock()
	defer g.mu.RUnlock()

	g2 := &mutableDirected{}
	g2.list = make(map[Vertex]map[Vertex]struct{})

	// Guess at average indegree by looking at ratio of edges to vertices, use that to initially size the adjacency maps
	startcap := int(g.Size() / g.Order())

	for source, adjacent := range g.list {
		if !g2.hasVertex(source) {
			g2.list[source] = make(map[Vertex]struct{}, startcap+1)
		}
		for target, _ := range adjacent {
			if !g2.hasVertex(target) {
				g2.list[target] = make(map[Vertex]struct{}, startcap+1)
			}
			g2.list[target][source] = keyExists
		}
	}

	return g2
}

/* immutableDirected implementation */

type immutableDirected struct {
	al_basic_immut
}

// Traverses the set of edges in the graph, passing each edge to the
// provided closure.
func (g *immutableDirected) EachEdge(f EdgeStep) {
	for source, adjacent := range g.list {
		for target, _ := range adjacent {
			if f(NewEdge(source, target)) {
				return
			}
		}
	}
}

// Enumerates the set of all edges incident to the provided vertex.
func (g *immutableDirected) EachEdgeIncidentTo(v Vertex, f EdgeStep) {
	eachEdgeIncidentToDirected(g, v, f)
}

// Enumerates the vertices adjacent to the provided vertex.
func (g *immutableDirected) EachAdjacentTo(start Vertex, f VertexStep) {
	g.EachEdgeIncidentTo(start, func(e Edge) bool {
		u, v := e.Both()
		if u == start {
			return f(v)
		} else {
			return f(u)
		}
	})
}

// Enumerates the set of out-edges for the provided vertex.
func (g *immutableDirected) EachArcFrom(v Vertex, f EdgeStep) {
	if !g.hasVertex(v) {
		return
	}

	for adjacent, _ := range g.list[v] {
		if f(NewEdge(v, adjacent)) {
			return
		}
	}
}

func (g *immutableDirected) EachSuccessorOf(v Vertex, f VertexStep) {
	eachVertexInAdjacencyList(g.list, v, f)
}

// Enumerates the set of in-edges for the provided vertex.
func (g *immutableDirected) EachArcTo(v Vertex, f EdgeStep) {
	if !g.hasVertex(v) {
		return
	}

	for candidate, adjacent := range g.list {
		for target, _ := range adjacent {
			if target == v {
				if f(NewEdge(candidate, target)) {
					return
				}
			}
		}
	}
}

func (g *immutableDirected) EachPredecessorOf(v Vertex, f VertexStep) {
	if !g.hasVertex(v) {
		return
	}

	for candidate, adjacent := range g.list {
		for target, _ := range adjacent {
			if target == v {
				if f(candidate) {
					return
				}
			}
		}
	}
}

// Returns the density of the graph. Density is the ratio of edge count to the
// number of edges there would be in complete graph (maximum edge count).
func (g *immutableDirected) Density() float64 {
	order := g.Order()
	return float64(g.Size()) / float64(order*(order-1))
}

// Indicates whether or not the given edge is present in the graph.
func (g *immutableDirected) HasEdge(edge Edge) bool {
	_, exists := g.list[edge.Source()][edge.Target()]
	return exists
}

// Returns the outdegree of the provided vertex. If the vertex is not present in the
// graph, the second return value will be false.
func (g *immutableDirected) OutDegreeOf(vertex Vertex) (degree int, exists bool) {
	if exists = g.hasVertex(vertex); exists {
		degree = len(g.list[vertex])
	}
	return
}

// Returns the indegree of the provided vertex. If the vertex is not present in the
// graph, the second return value will be false.
//
// Note that getting indegree is inefficient for directed adjacency lists; it requires
// a full scan of the graph's edge set.
func (g *immutableDirected) InDegreeOf(vertex Vertex) (degree int, exists bool) {
	if exists = g.hasVertex(vertex); exists {
		g.EachEdge(func(e Edge) (terminate bool) {
			if vertex == e.Target() {
				degree++
			}
			return
		})
	}

	return
}

// Returns the degree of the vertex, counting both in and out-edges.
func (g *immutableDirected) DegreeOf(vertex Vertex) (degree int, exists bool) {
	indegree, exists := g.InDegreeOf(vertex)
	outdegree, exists := g.OutDegreeOf(vertex)
	return indegree + outdegree, exists
}

// Returns a graph with the same vertex and edge set, but with the
// directionality of all its edges reversed.
//
// This implementation returns a new graph object (doubling memory use),
// but not all implementations do so.
func (g *immutableDirected) Transpose() Digraph {
	g2 := &immutableDirected{}
	g2.list = make(map[Vertex]map[Vertex]struct{})

	// Guess at average indegree by looking at ratio of edges to vertices, use that to initially size the adjacency maps
	startcap := int(g.Size() / g.Order())

	for source, adjacent := range g.list {
		if !g2.hasVertex(source) {
			g2.list[source] = make(map[Vertex]struct{}, startcap+1)
		}
		for target, _ := range adjacent {
			if !g2.hasVertex(target) {
				g2.list[target] = make(map[Vertex]struct{}, startcap+1)
			}
			g2.list[target][source] = keyExists
		}
	}

	return g2
}

// Adds a new edge to the graph.
func (g *immutableDirected) addEdges(edges ...Edge) {
	for _, edge := range edges {
		g.ensureVertex(edge.Source(), edge.Target())

		if _, exists := g.list[edge.Source()][edge.Target()]; !exists {
			g.list[edge.Source()][edge.Target()] = keyExists
			g.size++
		}
	}
}
<commit_msg>Use shared pred/succ method for basic directed al.<commit_after>package gogl

type mutableDirected struct {
	al_basic_mut
}

/* mutableDirected additions */

// Returns the outdegree of the provided vertex. If the vertex is not present in the
// graph, the second return value will be false.
func (g *mutableDirected) OutDegreeOf(vertex Vertex) (degree int, exists bool) {
	g.mu.RLock()
	defer g.mu.RUnlock()

	if exists = g.hasVertex(vertex); exists {
		degree = len(g.list[vertex])
	}
	return
}

// Returns the indegree of the provided vertex. If the vertex is not present in the
// graph, the second return value will be false.
//
// Note that getting indegree is inefficient for directed adjacency lists; it requires
// a full scan of the graph's edge set.
func (g *mutableDirected) InDegreeOf(vertex Vertex) (degree int, exists bool) {
	g.mu.RLock()
	defer g.mu.RUnlock()
	return inDegreeOf(g, vertex)
}

// Returns the degree of the provided vertex, counting both in and out-edges.
func (g *mutableDirected) DegreeOf(vertex Vertex) (degree int, exists bool) {
	g.mu.RLock()
	defer g.mu.RUnlock()

	indegree, exists := inDegreeOf(g, vertex)
	outdegree, exists := g.OutDegreeOf(vertex)
	return indegree + outdegree, exists
}

// Traverses the set of edges in the graph, passing each edge to the
// provided closure.
func (g *mutableDirected) EachEdge(f EdgeStep) {
	g.mu.RLock()
	defer g.mu.RUnlock()

	for source, adjacent := range g.list {
		for target, _ := range adjacent {
			if f(NewEdge(source, target)) {
				return
			}
		}
	}
}

// Enumerates the set of all edges incident to the provided vertex.
func (g *mutableDirected) EachEdgeIncidentTo(v Vertex, f EdgeStep) {
	g.mu.RLock()
	defer g.mu.RUnlock()
	eachEdgeIncidentToDirected(g, v, f)
}

// Enumerates the vertices adjacent to the provided vertex.
func (g *mutableDirected) EachAdjacentTo(start Vertex, f VertexStep) {
	g.mu.RLock()
	defer g.mu.RUnlock()

	g.EachEdgeIncidentTo(start, func(e Edge) bool {
		u, v := e.Both()
		if u == start {
			return f(v)
		} else {
			return f(u)
		}
	})
}

// Enumerates the set of out-edges for the provided vertex.
func (g *mutableDirected) EachArcFrom(v Vertex, f EdgeStep) {
	g.mu.RLock()
	defer g.mu.RUnlock()

	if !g.hasVertex(v) {
		return
	}

	for adjacent, _ := range g.list[v] {
		if f(NewEdge(v, adjacent)) {
			return
		}
	}
}

func (g *mutableDirected) EachSuccessorOf(v Vertex, f VertexStep) {
	g.mu.RLock()
	defer g.mu.RUnlock()

	eachVertexInAdjacencyList(g.list, v, f)
}

// Enumerates the set of in-edges for the provided vertex.
func (g *mutableDirected) EachArcTo(v Vertex, f EdgeStep) {
	g.mu.RLock()
	defer g.mu.RUnlock()

	if !g.hasVertex(v) {
		return
	}

	for candidate, adjacent := range g.list {
		for target, _ := range adjacent {
			if target == v {
				if f(NewEdge(candidate, target)) {
					return
				}
			}
		}
	}
}

func (g *mutableDirected) EachPredecessorOf(v Vertex, f VertexStep) {
	g.mu.RLock()
	defer g.mu.RUnlock()

	eachPredecessorOf(g.list, v, f)
}

// Indicates whether or not the given edge is present in the graph.
func (g *mutableDirected) HasEdge(edge Edge) bool {
	g.mu.RLock()
	defer g.mu.RUnlock()

	_, exists := g.list[edge.Source()][edge.Target()]
	return exists
}

// Returns the density of the graph. Density is the ratio of edge count to the
// number of edges there would be in complete graph (maximum edge count).
func (g *mutableDirected) Density() float64 {
	g.mu.RLock()
	defer g.mu.RUnlock()

	order := g.Order()
	return float64(g.Size()) / float64(order*(order-1))
}

// Removes a vertex from the graph. Also removes any edges of which that
// vertex is a member.
func (g *mutableDirected) RemoveVertex(vertices ...Vertex) {
	if len(vertices) == 0 {
		return
	}

	g.mu.Lock()
	defer g.mu.Unlock()

	for _, vertex := range vertices {
		if g.hasVertex(vertex) {
			// TODO Is the expensive search good to do here and now...
			// while read-locked?
			g.size -= len(g.list[vertex])
			delete(g.list, vertex)

			// TODO consider chunking the list and parallelizing into goroutines
			for _, adjacent := range g.list {
				if _, has := adjacent[vertex]; has {
					delete(adjacent, vertex)
					g.size--
				}
			}
		}
	}
	return
}

// Adds edges to the graph.
func (g *mutableDirected) AddEdges(edges ...Edge) {
	if len(edges) == 0 {
		return
	}

	g.mu.Lock()
	defer g.mu.Unlock()

	g.addEdges(edges...)
}

// Adds a new edge to the graph.
func (g *mutableDirected) addEdges(edges ...Edge) {
	for _, edge := range edges {
		g.ensureVertex(edge.Source(), edge.Target())

		if _, exists := g.list[edge.Source()][edge.Target()]; !exists {
			g.list[edge.Source()][edge.Target()] = keyExists
			g.size++
		}
	}
}

// Removes edges from the graph. This does NOT remove vertex members of the
// removed edges.
func (g *mutableDirected) RemoveEdges(edges ...Edge) {
	if len(edges) == 0 {
		return
	}

	g.mu.Lock()
	defer g.mu.Unlock()

	for _, edge := range edges {
		s, t := edge.Both()
		if _, exists := g.list[s][t]; exists {
			delete(g.list[s], t)
			g.size--
		}
	}
}

// Returns a graph with the same vertex and edge set, but with the
// directionality of all its edges reversed.
//
// This implementation returns a new graph object (doubling memory use),
// but not all implementations do so.
func (g *mutableDirected) Transpose() Digraph {
	g.mu.RLock()
	defer g.mu.RUnlock()

	g2 := &mutableDirected{}
	g2.list = make(map[Vertex]map[Vertex]struct{})

	// Guess at average indegree by looking at ratio of edges to vertices, use that to initially size the adjacency maps
	startcap := int(g.Size() / g.Order())

	for source, adjacent := range g.list {
		if !g2.hasVertex(source) {
			g2.list[source] = make(map[Vertex]struct{}, startcap+1)
		}
		for target, _ := range adjacent {
			if !g2.hasVertex(target) {
				g2.list[target] = make(map[Vertex]struct{}, startcap+1)
			}
			g2.list[target][source] = keyExists
		}
	}

	return g2
}

/* immutableDirected implementation */

type immutableDirected struct {
	al_basic_immut
}

// Traverses the set of edges in the graph, passing each edge to the
// provided closure.
func (g *immutableDirected) EachEdge(f EdgeStep) {
	for source, adjacent := range g.list {
		for target, _ := range adjacent {
			if f(NewEdge(source, target)) {
				return
			}
		}
	}
}

// Enumerates the set of all edges incident to the provided vertex.
func (g *immutableDirected) EachEdgeIncidentTo(v Vertex, f EdgeStep) {
	eachEdgeIncidentToDirected(g, v, f)
}

// Enumerates the vertices adjacent to the provided vertex.
func (g *immutableDirected) EachAdjacentTo(start Vertex, f VertexStep) {
	g.EachEdgeIncidentTo(start, func(e Edge) bool {
		u, v := e.Both()
		if u == start {
			return f(v)
		} else {
			return f(u)
		}
	})
}

// Enumerates the set of out-edges for the provided vertex.
func (g *immutableDirected) EachArcFrom(v Vertex, f EdgeStep) {
	if !g.hasVertex(v) {
		return
	}

	for adjacent, _ := range g.list[v] {
		if f(NewEdge(v, adjacent)) {
			return
		}
	}
}

func (g *immutableDirected) EachSuccessorOf(v Vertex, f VertexStep) {
	eachVertexInAdjacencyList(g.list, v, f)
}

// Enumerates the set of in-edges for the provided vertex.
func (g *immutableDirected) EachArcTo(v Vertex, f EdgeStep) {
	if !g.hasVertex(v) {
		return
	}

	for candidate, adjacent := range g.list {
		for target, _ := range adjacent {
			if target == v {
				if f(NewEdge(candidate, target)) {
					return
				}
			}
		}
	}
}

func (g *immutableDirected) EachPredecessorOf(v Vertex, f VertexStep) {
	eachPredecessorOf(g.list, v, f)
}

// Returns the density of the graph. Density is the ratio of edge count to the
// number of edges there would be in complete graph (maximum edge count).
func (g *immutableDirected) Density() float64 {
	order := g.Order()
	return float64(g.Size()) / float64(order*(order-1))
}

// Indicates whether or not the given edge is present in the graph.
func (g *immutableDirected) HasEdge(edge Edge) bool {
	_, exists := g.list[edge.Source()][edge.Target()]
	return exists
}

// Returns the outdegree of the provided vertex. If the vertex is not present in the
// graph, the second return value will be false.
func (g *immutableDirected) OutDegreeOf(vertex Vertex) (degree int, exists bool) {
	if exists = g.hasVertex(vertex); exists {
		degree = len(g.list[vertex])
	}
	return
}

// Returns the indegree of the provided vertex. If the vertex is not present in the
// graph, the second return value will be false.
//
// Note that getting indegree is inefficient for directed adjacency lists; it requires
// a full scan of the graph's edge set.
func (g *immutableDirected) InDegreeOf(vertex Vertex) (degree int, exists bool) {
	if exists = g.hasVertex(vertex); exists {
		g.EachEdge(func(e Edge) (terminate bool) {
			if vertex == e.Target() {
				degree++
			}
			return
		})
	}

	return
}

// Returns the degree of the vertex, counting both in and out-edges.
func (g *immutableDirected) DegreeOf(vertex Vertex) (degree int, exists bool) {
	indegree, exists := g.InDegreeOf(vertex)
	outdegree, exists := g.OutDegreeOf(vertex)
	return indegree + outdegree, exists
}

// Returns a graph with the same vertex and edge set, but with the
// directionality of all its edges reversed.
//
// This implementation returns a new graph object (doubling memory use),
// but not all implementations do so.
func (g *immutableDirected) Transpose() Digraph {
	g2 := &immutableDirected{}
	g2.list = make(map[Vertex]map[Vertex]struct{})

	// Guess at average indegree by looking at ratio of edges to vertices, use that to initially size the adjacency maps
	startcap := int(g.Size() / g.Order())

	for source, adjacent := range g.list {
		if !g2.hasVertex(source) {
			g2.list[source] = make(map[Vertex]struct{}, startcap+1)
		}
		for target, _ := range adjacent {
			if !g2.hasVertex(target) {
				g2.list[target] = make(map[Vertex]struct{}, startcap+1)
			}
			g2.list[target][source] = keyExists
		}
	}

	return g2
}

// Adds a new edge to the graph.
func (g *immutableDirected) addEdges(edges ...Edge) {
	for _, edge := range edges {
		g.ensureVertex(edge.Source(), edge.Target())

		if _, exists := g.list[edge.Source()][edge.Target()]; !exists {
			g.list[edge.Source()][edge.Target()] = keyExists
			g.size++
		}
	}
}
<|endoftext|>


<commit_before>package llvm_module

import (
	"github.com/grainlang/grain/ast"
	"llvm.org/llvm/bindings/go/llvm"
)

func CreateLlvmModuleFromFunction(function ast.Function, allFunctions []ast.Function) llvm.Module {
	context := llvm.GlobalContext()
	builder := context.NewBuilder()
	module := context.NewModule(function.Id + " " + function.Name)
	llvmFunction := createFunctionDeclarationInModule(function, module)
	bodyBlock := llvm.AddBasicBlock(llvmFunction, "body")
	builder.SetInsertPoint(bodyBlock, bodyBlock.FirstInstruction())
	returnValueToLlvmValue := make(map[string]llvm.Value)
	functionToLlvmDeclaration := make(map[string]llvm.Value)
	for expressionIndex, body := range function.Body {
		switch typedBody := body.(type) {
		case ast.NativeFunctionCall:
			nativeFunctionParamTypes := make([]llvm.Type, len(typedBody.Parameters))
			for i := range typedBody.Parameters {
				nativeFunctionParamTypes[i] = llvm.Int32Type()
			}
			var nativeFunctionReturnType llvm.Type
			if typedBody.ReturnValue == ast.NativeValueVoid {
				nativeFunctionReturnType = llvm.VoidType()
			} else if typedBody.ReturnValue == ast.NativeValueInt {
				nativeFunctionReturnType = llvm.Int32Type()
			} else {
				panic("Unknown type")
			}
			nativeFunctionType := llvm.FunctionType(nativeFunctionReturnType, nativeFunctionParamTypes, false)
			nativeFunction := llvm.AddFunction(module, typedBody.Name, nativeFunctionType)
			nativeFunctionParamValues := make([]llvm.Value, len(typedBody.Parameters))
			for i, nativeFunctionParam := range typedBody.Parameters {
				for index, param := range function.Parameters {
					if nativeFunctionParam.Id == param.Id {
						nativeFunctionParamValues[i] = llvmFunction.Param(index)
						break
					}
				}
			}
			nativeFunctionReturnValue := builder.CreateCall(nativeFunction, nativeFunctionParamValues, "ret")
			builder.CreateRet(nativeFunctionReturnValue)
		case ast.BinaryOperationCall:
			var opcode llvm.Opcode
			if typedBody.Name == "+" {
				opcode = llvm.Add
			} else if typedBody.Name == "-" {
				opcode = llvm.Sub
			} else if typedBody.Name == "*" {
				opcode = llvm.Mul
			} else {
				panic("Unknown operator: " + typedBody.Name)
			}
			var leftParam, rightParam llvm.Value
			for index, param := range function.Parameters {
				if typedBody.LeftParameter.Id == param.Id {
					leftParam = llvmFunction.Param(index)
				} else if typedBody.RightParameter.Id == param.Id {
					rightParam = llvmFunction.Param(index)
				}
			}
			binaryOperationReturnValue := builder.CreateBinOp(opcode, leftParam, rightParam, "ret")
			builder.CreateRet(binaryOperationReturnValue)
		case ast.FunctionUse:
			consumingFunction := FindUsedFunction(typedBody, allFunctions)
			var consumingLlvmFunction llvm.Value
			if foundDeclaration, ok := functionToLlvmDeclaration[consumingFunction.Id]; ok {
				consumingLlvmFunction = foundDeclaration
			} else {
				consumingLlvmFunction = createFunctionDeclarationInModule(consumingFunction, module)
				functionToLlvmDeclaration[consumingFunction.Id] = consumingLlvmFunction
			}
			llvmParams := make([]llvm.Value, len(typedBody.Bindings))
			for i, binding := range typedBody.Bindings {
				llvmParams[i] = returnValueToLlvmValue[binding.FromFunctionUseId + " " + binding.FromReturnValue]
			}
			consumingFunctionReturnValue := builder.CreateCall(consumingLlvmFunction, llvmParams, "ret")
			if expressionIndex == len(function.Body) - 1 {
				builder.CreateRet(consumingFunctionReturnValue)
			} else {
				if len(consumingFunction.ReturnValues) > 0 {
					returnValueToLlvmValue[typedBody.Id + " " + consumingFunction.ReturnValues[0].Id] = consumingFunctionReturnValue
				}
			}
		}
	}
	return module
}

func createFunctionDeclarationInModule(function ast.Function, module llvm.Module) llvm.Value {
	paramTypes := make([]llvm.Type, len(function.Parameters))
	for i, param := range function.Parameters {
		if param.ValueType == ast.Integer {
			paramTypes[i] = llvm.Int32Type()
		} else {
			paramTypes[i] = llvm.Int32Type()
		}
	}
	returnTypes := make([]llvm.Type, len(function.ReturnValues))
	for i, returnValue := range function.ReturnValues {
		if returnValue.ValueType == ast.Integer {
			returnTypes[i] = llvm.Int32Type()
		} else {
			returnTypes[i] = llvm.Int32Type()
		}
	}
	returnType := llvm.StructType(returnTypes, false)
	llvmFunctionType := llvm.FunctionType(returnType, paramTypes, false)
	llvmFunction := llvm.AddFunction(module, "$" + function.Id, llvmFunctionType)
	return llvmFunction
}

func FindUsedFunction(bodyPart ast.FunctionUse, allFunctions []ast.Function) ast.Function {
	for _, fn := range allFunctions {
		if bodyPart.FunctionId == fn.Id {
			return fn
		}
	}
	panic("No such function " + bodyPart.FunctionId)
}<commit_msg>Update Integer type to 64 bit.<commit_after>package llvm_module

import (
	"github.com/grainlang/grain/ast"
	"llvm.org/llvm/bindings/go/llvm"
)

func CreateLlvmModuleFromFunction(function ast.Function, allFunctions []ast.Function) llvm.Module {
	context := llvm.GlobalContext()
	builder := context.NewBuilder()
	module := context.NewModule(function.Id + " " + function.Name)
	llvmFunction := createFunctionDeclarationInModule(function, module)
	bodyBlock := llvm.AddBasicBlock(llvmFunction, "body")
	builder.SetInsertPoint(bodyBlock, bodyBlock.FirstInstruction())
	returnValueToLlvmValue := make(map[string]llvm.Value)
	functionToLlvmDeclaration := make(map[string]llvm.Value)
	for expressionIndex, body := range function.Body {
		switch typedBody := body.(type) {
		case ast.NativeFunctionCall:
			nativeFunctionParamTypes := make([]llvm.Type, len(typedBody.Parameters))
			for i := range typedBody.Parameters {
				nativeFunctionParamTypes[i] = llvm.Int32Type()
			}
			var nativeFunctionReturnType llvm.Type
			if typedBody.ReturnValue == ast.NativeValueVoid {
				nativeFunctionReturnType = llvm.VoidType()
			} else if typedBody.ReturnValue == ast.NativeValueInt {
				nativeFunctionReturnType = llvm.Int32Type()
			} else {
				panic("Unknown type")
			}
			nativeFunctionType := llvm.FunctionType(nativeFunctionReturnType, nativeFunctionParamTypes, false)
			nativeFunction := llvm.AddFunction(module, typedBody.Name, nativeFunctionType)
			nativeFunctionParamValues := make([]llvm.Value, len(typedBody.Parameters))
			for i, nativeFunctionParam := range typedBody.Parameters {
				for index, param := range function.Parameters {
					if nativeFunctionParam.Id == param.Id {
						nativeFunctionParamValues[i] = llvmFunction.Param(index)
						break
					}
				}
			}
			nativeFunctionReturnValue := builder.CreateCall(nativeFunction, nativeFunctionParamValues, "ret")
			builder.CreateRet(nativeFunctionReturnValue)
		case ast.BinaryOperationCall:
			var opcode llvm.Opcode
			if typedBody.Name == "+" {
				opcode = llvm.Add
			} else if typedBody.Name == "-" {
				opcode = llvm.Sub
			} else if typedBody.Name == "*" {
				opcode = llvm.Mul
			} else {
				panic("Unknown operator: " + typedBody.Name)
			}
			var leftParam, rightParam llvm.Value
			for index, param := range function.Parameters {
				if typedBody.LeftParameter.Id == param.Id {
					leftParam = llvmFunction.Param(index)
				} else if typedBody.RightParameter.Id == param.Id {
					rightParam = llvmFunction.Param(index)
				}
			}
			binaryOperationReturnValue := builder.CreateBinOp(opcode, leftParam, rightParam, "ret")
			builder.CreateRet(binaryOperationReturnValue)
		case ast.FunctionUse:
			consumingFunction := FindUsedFunction(typedBody, allFunctions)
			var consumingLlvmFunction llvm.Value
			if foundDeclaration, ok := functionToLlvmDeclaration[consumingFunction.Id]; ok {
				consumingLlvmFunction = foundDeclaration
			} else {
				consumingLlvmFunction = createFunctionDeclarationInModule(consumingFunction, module)
				functionToLlvmDeclaration[consumingFunction.Id] = consumingLlvmFunction
			}
			llvmParams := make([]llvm.Value, len(typedBody.Bindings))
			for i, binding := range typedBody.Bindings {
				llvmParams[i] = returnValueToLlvmValue[binding.FromFunctionUseId + " " + binding.FromReturnValue]
			}
			consumingFunctionReturnValue := builder.CreateCall(consumingLlvmFunction, llvmParams, "ret")
			if expressionIndex == len(function.Body) - 1 {
				builder.CreateRet(consumingFunctionReturnValue)
			} else {
				if len(consumingFunction.ReturnValues) > 0 {
					returnValueToLlvmValue[typedBody.Id + " " + consumingFunction.ReturnValues[0].Id] = consumingFunctionReturnValue
				}
			}
		}
	}
	return module
}

func createFunctionDeclarationInModule(function ast.Function, module llvm.Module) llvm.Value {
	paramTypes := make([]llvm.Type, len(function.Parameters))
	for i, param := range function.Parameters {
		if param.ValueType == ast.Integer {
			paramTypes[i] = llvm.Int64Type()
		} else {
			paramTypes[i] = llvm.Int32Type()
		}
	}
	returnTypes := make([]llvm.Type, len(function.ReturnValues))
	for i, returnValue := range function.ReturnValues {
		if returnValue.ValueType == ast.Integer {
			returnTypes[i] = llvm.Int32Type()
		} else {
			returnTypes[i] = llvm.Int32Type()
		}
	}
	returnType := llvm.StructType(returnTypes, false)
	llvmFunctionType := llvm.FunctionType(returnType, paramTypes, false)
	llvmFunction := llvm.AddFunction(module, "$" + function.Id, llvmFunctionType)
	return llvmFunction
}

func FindUsedFunction(bodyPart ast.FunctionUse, allFunctions []ast.Function) ast.Function {
	for _, fn := range allFunctions {
		if bodyPart.FunctionId == fn.Id {
			return fn
		}
	}
	panic("No such function " + bodyPart.FunctionId)
}<|endoftext|>


<commit_before>package oauth2device

import (
	"fmt"
	"log"
	"net/http"

	"github.com/rjw57/oauth2device"
	"github.com/rjw57/oauth2device/googledevice"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
	"google.golang.org/api/youtube/v3"
)

// An simple example of using this package for device authorization.
func ExampleDeviceAuthorizationFlow() {
	// The usual OAuth2 configuration
	var clientOAuthConfig = &oauth2.Config{
		ClientID:     "<insert client id here>",
		ClientSecret: "<insert client secret here>",
		Endpoint:     google.Endpoint,

		// for example...
		Scopes: []string{youtube.YoutubeReadonlyScope},
	}

	// Augment OAuth2 configuration with device endpoints.
	var clientDeviceOAuthConfig = &oauth2device.Config{
		Config:         clientOAuthConfig,
		DeviceEndpoint: googledevice.DeviceEndpoint,
	}

	// Use default HTTP client.
	client := http.DefaultClient

	// Get URL and code for user.
	dcr, err := oauth2device.RequestDeviceCode(client, clientDeviceOAuthConfig)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Visit: %v and enter: %v\n", dcr.VerificationURL, dcr.UserCode)

	// Wait for a token. It will be a standard oauth2.Token.
	accessToken, err := oauth2device.WaitForDeviceAuthorization(client,
		clientDeviceOAuthConfig, dcr)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Access token: %v\n", accessToken)

	// Now use the token as usual...
}
<commit_msg>fix name of example function<commit_after>package oauth2device

import (
	"fmt"
	"log"
	"net/http"

	"github.com/rjw57/oauth2device"
	"github.com/rjw57/oauth2device/googledevice"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
	"google.golang.org/api/youtube/v3"
)

// An simple example of using this package for device authorization.
func Example() {
	// The usual OAuth2 configuration
	var clientOAuthConfig = &oauth2.Config{
		ClientID:     "<insert client id here>",
		ClientSecret: "<insert client secret here>",
		Endpoint:     google.Endpoint,

		// for example...
		Scopes: []string{youtube.YoutubeReadonlyScope},
	}

	// Augment OAuth2 configuration with device endpoints.
	var clientDeviceOAuthConfig = &oauth2device.Config{
		Config:         clientOAuthConfig,
		DeviceEndpoint: googledevice.DeviceEndpoint,
	}

	// Use default HTTP client.
	client := http.DefaultClient

	// Get URL and code for user.
	dcr, err := oauth2device.RequestDeviceCode(client, clientDeviceOAuthConfig)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Visit: %v and enter: %v\n", dcr.VerificationURL, dcr.UserCode)

	// Wait for a token. It will be a standard oauth2.Token.
	accessToken, err := oauth2device.WaitForDeviceAuthorization(client,
		clientDeviceOAuthConfig, dcr)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Access token: %v\n", accessToken)

	// Now use the token as usual...
}
<|endoftext|>


<commit_before>// Copyright 2011, Bryan Matsuo. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/*
 *  Filename:    godirs.go
 *  Author:      Bryan Matsuo <bmatsuo@soe.ucsc.edu>
 *  Created:     Tue Jul  5 22:13:49 PDT 2011
 *  Description:
 *  Usage:       godirs [options] ARGUMENT ...
 */

//  Package dispatch provides goroutine dispatch and concurrency limiting.
//  It provides an object Dispatch which is a queueing system for concurrent
//  functions. It implements a dynamic limit on the number of routines that
//  it runs simultaneously. It also uses a Queue interface, allowing for
//  alternate queue implementations.
//
//  See github.com/bmatsuo/dispatch/queues for more about the Queue interface.
//
//  See github.com/bmatsuo/dispatch/examples for usage examples.
package dispatch
import (
    "sync"
    "github.com/bmatsuo/dispatch/queues"
)

//  A Dispatch is an automated function dispatch queue with a limited
//  number of concurrent gorountines.
type Dispatch struct {
    // The maximum number of goroutines can be changed while the queue is
    // processing.
    MaxGo      int

    // Handle waiting when the limit of concurrent goroutines has been reached.
    waitingToRun bool
    //nextWake     chan bool
    nextWait     *sync.WaitGroup

    // Handle waiting when function queue is empty.
    waitingOnQ   bool
    restart      *sync.WaitGroup

    // Manage the Start()'ing of a Dispatch, avoiding race conditions.
    startLock    *sync.Mutex
    started      bool

    // Handle goroutine-safe queue operations.
    qLock        *sync.Mutex
    queue        queues.Queue

    // Handle goroutine-safe limiting and identifier operations.
    pLock        *sync.Mutex
    processing   int         // Number of QueueTasks running
    idcount      int64       // pid counter

    // Handle stopping of the Start() method.
    kill         chan bool
}

//  Create a new queue object with a specified limit on concurrency.
func New(maxroutines int) *Dispatch {
    return NewCustom(maxroutines, queues.NewFIFO())
}
func NewCustom(maxroutines int, queue queues.Queue) *Dispatch {
    var rl = new(Dispatch)
    rl.startLock = new(sync.Mutex)
    rl.qLock     = new(sync.Mutex)
    rl.pLock     = new(sync.Mutex)
    rl.restart   = new(sync.WaitGroup)
    rl.kill      = make(chan bool)
    //rl.nextWake  = make(chan bool)
    rl.nextWait  = new(sync.WaitGroup)
    rl.queue     = queue
    rl.MaxGo     = maxroutines
    rl.idcount   = 0
    return rl
}

//  Goroutines called from a Dispatch are given an int identifier unique
//  to that routine.
type StdTask struct {
    F func(id int64)
}
func (dt StdTask) Type() string {
    return "StdTask"
}
func (dt StdTask) SetFunc(f func(id int64)) {
    dt.F = f
}
func (dt StdTask) Func() func(id int64) {
    return dt.F
}
type dispatchTaskWrapper struct {
    id int64
    t  queues.Task
}
func (dtw dispatchTaskWrapper) Func() func(id int64) {
    return dtw.t.Func()
}
func (dtw dispatchTaskWrapper) Id() int64 {
    return dtw.id
}
func (dtw dispatchTaskWrapper) Task() queues.Task {
    return dtw.t
}

//  Enqueue a task for execution as a goroutine.
func (gq *Dispatch) Enqueue(t queues.Task) int64 {
    // Wrap the function so it works with the goroutine limiting code.
    var f = t.Func()
    var dtFunc = func (id int64) {
        // Run the given function.
        f(id)

        // Decrement the process counter.
        gq.pLock.Lock()
        gq.processing--
        if procWaiting := gq.waitingToRun ; procWaiting {
            gq.nextWait.Done()
            gq.waitingToRun = false
        }
        gq.pLock.Unlock()
    }
    t.SetFunc(dtFunc)

    // Lock the queue and enqueue a new task.
    gq.qLock.Lock()
    gq.idcount++
    var id = gq.idcount
    gq.queue.Enqueue(dispatchTaskWrapper{id, t})
    var loopWaiting = gq.waitingOnQ
    if loopWaiting {
        gq.waitingOnQ = false
    }
    gq.qLock.Unlock()

    // Restart the Start() loop if it was deemed necessary.
    if loopWaiting {
        gq.restart.Done()
    }

    return id
}

//  Stop the queue after gq.Start() has been called. Any goroutines which
//  have not already been dequeued will not be executed until gq.Start()
//  is called again.
func (gq *Dispatch) Stop() {
    // Lock out Start() and queue ops for the entire call.
    gq.startLock.Lock()
    defer gq.startLock.Unlock()
    gq.qLock.Lock()
    defer gq.qLock.Unlock()

    if !gq.started {
        return
    }

    // Clear channel flags and close channels, stoping further processing.
    close(gq.kill)
    gq.started = false
    if gq.waitingOnQ {
        gq.waitingOnQ = false
        gq.restart.Done()
    }
    if gq.waitingToRun {
        gq.waitingToRun = false
        gq.nextWait.Done()
    }
    //close(gq.nextWake)
}

//  Start the next task in the queue. It's assumed that the queue is non-
//  empty. Furthermore, there should only be one goroutine in this method
//  (for this object) at a time. Both conditions are enforced in
//  gq.Start(), which calls gq.next() exclusively.
func (gq *Dispatch) next() {
    for true {
        // Attempt to start processing the file.
        gq.pLock.Lock()
        if gq.processing >= gq.MaxGo {
            gq.waitingToRun = true
            gq.nextWait.Add(1)
            gq.pLock.Unlock()
            gq.nextWait.Wait()
            /*
            var cont, ok =<-gq.nextWake
            if !ok {
                gq.nextWake = make(chan bool)
                return
            }
            if !cont {
                return
            }
            */
            continue
        }
        // Keep the books and reset wait time before unlocking.
        gq.processing++
        gq.pLock.Unlock()

        // Get an element from the queue.
        gq.qLock.Lock()
        var wrapper = gq.queue.Dequeue().(queues.RegisteredTask)
        gq.qLock.Unlock()

        // Begin processing and asyncronously return.
        //var task = taskelm.Value.(dispatchTaskWrapper)
        var task = wrapper.Func()
        go task(wrapper.Id())
        return
    }
}

//  Start executing goroutines. Don't stop until gq.Stop() is called.
func (gq *Dispatch) Start() {
    // Avoid multiple gq.Start() methods and avoid race conditions.
    gq.startLock.Lock()
    if gq.started {
        panic("already started")
    }
    gq.started = true
    gq.startLock.Unlock()

    // Recreate any channels that were closed by a previous Stop().
    var inited = false
    for !inited {
        select {
        case _, okKill :=<-gq.kill:
            if !okKill {
                gq.kill = make(chan bool)
            }
        /*
        case _, okWake :=<-gq.nextWake:
            if !okWake {
                gq.nextWake = make(chan bool)
            }
        */
        default:
            inited = true
        }
    }

    // Process the queue
    for true {
        select {
        case die, ok :=<-gq.kill:
            // If something came out of this channel, we must stop.
            if !ok {
                // Recreate the channel on a closure.
                gq.kill = make(chan bool)
                return
            }
            if die {
                return
            }
        default:
            // Check the queue size and determine if we need to wait.
            gq.qLock.Lock()
            if gq.waitingOnQ = gq.queue.Len() == 0 ; gq.waitingOnQ {
                gq.restart.Add(1)
            }
            gq.qLock.Unlock()

            if gq.waitingOnQ {
                // Wait for a restart signal from gq.Enqueue
                gq.restart.Wait()
            } else {
                // Process the head of the queue and start the loop again.
                gq.next()
            }
        }
    }
}
<commit_msg>Clean things up a little bit.<commit_after>// Copyright 2011, Bryan Matsuo. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/*
 *  Filename:    godirs.go
 *  Author:      Bryan Matsuo <bmatsuo@soe.ucsc.edu>
 *  Created:     Tue Jul  5 22:13:49 PDT 2011
 *  Description:
 *  Usage:       godirs [options] ARGUMENT ...
 */

//  Package dispatch provides goroutine dispatch and concurrency limiting.
//  It provides an object Dispatch which is a queueing system for concurrent
//  functions. It implements a dynamic limit on the number of routines that
//  it runs simultaneously. It also uses a Queue interface, allowing for
//  alternate queue implementations.
//
//  See github.com/bmatsuo/dispatch/queues for more about the Queue interface.
//
//  See github.com/bmatsuo/dispatch/examples for usage examples.
package dispatch
import (
    "sync"
    "github.com/bmatsuo/dispatch/queues"
)

//  A Dispatch is an automated function dispatch queue with a limited
//  number of concurrent gorountines.
type Dispatch struct {
    // The maximum number of goroutines can be changed while the queue is
    // processing.
    MaxGo      int

    // Handle waiting when the limit of concurrent goroutines has been reached.
    waitingToRun bool
    //nextWake     chan bool
    nextWait     *sync.WaitGroup

    // Handle waiting when function queue is empty.
    waitingOnQ   bool
    restart      *sync.WaitGroup

    // Manage the Start()'ing of a Dispatch, avoiding race conditions.
    startLock    *sync.Mutex
    started      bool

    // Handle goroutine-safe queue operations.
    qLock        *sync.Mutex
    queue        queues.Queue

    // Handle goroutine-safe limiting and identifier operations.
    pLock        *sync.Mutex
    processing   int         // Number of QueueTasks running
    idcount      int64       // pid counter

    // Handle stopping of the Start() method.
    kill         chan bool
}

//  Create a new queue object with a specified limit on concurrency.
func New(maxroutines int) *Dispatch {
    return NewCustom(maxroutines, queues.NewFIFO())
}
func NewCustom(maxroutines int, queue queues.Queue) *Dispatch {
    var rl = new(Dispatch)
    rl.startLock = new(sync.Mutex)
    rl.qLock     = new(sync.Mutex)
    rl.pLock     = new(sync.Mutex)
    rl.restart   = new(sync.WaitGroup)
    rl.kill      = make(chan bool)
    //rl.nextWake  = make(chan bool)
    rl.nextWait  = new(sync.WaitGroup)
    rl.queue     = queue
    rl.MaxGo     = maxroutines
    rl.idcount   = 0
    return rl
}

//  Goroutines called from a Dispatch are given an int identifier unique
//  to that routine.
type StdTask struct {
    F func(id int64)
}
func (dt StdTask) Type() string {
    return "StdTask"
}
func (dt StdTask) SetFunc(f func(id int64)) {
    dt.F = f
}
func (dt StdTask) Func() func(id int64) {
    return dt.F
}
type dispatchTaskWrapper struct {
    id int64
    t  queues.Task
}
func (dtw dispatchTaskWrapper) Func() func(id int64) {
    return dtw.t.Func()
}
func (dtw dispatchTaskWrapper) Id() int64 {
    return dtw.id
}
func (dtw dispatchTaskWrapper) Task() queues.Task {
    return dtw.t
}

//  Enqueue a task for execution as a goroutine.
func (gq *Dispatch) Enqueue(t queues.Task) int64 {
    // Wrap the function so it works with the goroutine limiting code.
    var f = t.Func()
    var dtFunc = func (id int64) {
        // Run the given function.
        f(id)

        // Decrement the process counter.
        gq.pLock.Lock()
        gq.processing--
        if procWaiting := gq.waitingToRun ; procWaiting {
            gq.nextWait.Done()
            gq.waitingToRun = false
        }
        gq.pLock.Unlock()
    }
    t.SetFunc(dtFunc)

    // Lock the queue and enqueue a new task.
    gq.qLock.Lock()
    gq.idcount++
    var id = gq.idcount
    gq.queue.Enqueue(dispatchTaskWrapper{id, t})
    if gq.waitingOnQ {
        gq.waitingOnQ = false
        gq.restart.Done()
    }
    gq.qLock.Unlock()

    return id
}

//  Stop the queue after gq.Start() has been called. Any goroutines which
//  have not already been dequeued will not be executed until gq.Start()
//  is called again.
func (gq *Dispatch) Stop() {
    // Lock out Start() and queue ops for the entire call.
    gq.startLock.Lock()
    defer gq.startLock.Unlock()
    gq.qLock.Lock()
    defer gq.qLock.Unlock()

    if !gq.started {
        return
    }

    // Clear channel flags and close channels, stoping further processing.
    close(gq.kill)
    gq.started = false
    if gq.waitingOnQ {
        gq.waitingOnQ = false
        gq.restart.Done()
    }
    if gq.waitingToRun {
        gq.waitingToRun = false
        gq.nextWait.Done()
    }
    //close(gq.nextWake)
}

//  Start the next task in the queue. It's assumed that the queue is non-
//  empty. Furthermore, there should only be one goroutine in this method
//  (for this object) at a time. Both conditions are enforced in
//  gq.Start(), which calls gq.next() exclusively.
func (gq *Dispatch) next() {
    for true {
        // Attempt to start processing the file.
        gq.pLock.Lock()
        if gq.processing >= gq.MaxGo {
            gq.waitingToRun = true
            gq.nextWait.Add(1)
            gq.pLock.Unlock()
            gq.nextWait.Wait()
            /*
            var cont, ok =<-gq.nextWake
            if !ok {
                gq.nextWake = make(chan bool)
                return
            }
            if !cont {
                return
            }
            */
            continue
        }
        // Keep the books and reset wait time before unlocking.
        gq.processing++
        gq.pLock.Unlock()

        // Get an element from the queue.
        gq.qLock.Lock()
        var wrapper = gq.queue.Dequeue().(queues.RegisteredTask)
        gq.qLock.Unlock()

        // Begin processing and asyncronously return.
        //var task = taskelm.Value.(dispatchTaskWrapper)
        var task = wrapper.Func()
        go task(wrapper.Id())
        return
    }
}

//  Start executing goroutines. Don't stop until gq.Stop() is called.
func (gq *Dispatch) Start() {
    // Avoid multiple gq.Start() methods and avoid race conditions.
    gq.startLock.Lock()
    if gq.started {
        panic("already started")
    }
    gq.started = true
    gq.startLock.Unlock()

    // Recreate any channels that were closed by a previous Stop().
    var inited = false
    for !inited {
        select {
        case _, okKill :=<-gq.kill:
            if !okKill {
                gq.kill = make(chan bool)
            }
        /*
        case _, okWake :=<-gq.nextWake:
            if !okWake {
                gq.nextWake = make(chan bool)
            }
        */
        default:
            inited = true
        }
    }

    // Process the queue
    for true {
        select {
        case die, ok :=<-gq.kill:
            // If something came out of this channel, we must stop.
            if !ok {
                // Recreate the channel on a closure.
                gq.kill = make(chan bool)
                return
            }
            if die {
                return
            }
        default:
            // Check the queue size and determine if we need to wait.
            gq.qLock.Lock()
            var wait = gq.queue.Len() == 0
            if gq.waitingOnQ = wait ; wait {
                gq.restart.Add(1)
            }
            gq.qLock.Unlock()

            if wait {
                // Wait for a restart signal from gq.Enqueue
                gq.restart.Wait()
            } else {
                // Process the head of the queue and start the loop again.
                gq.next()
            }
        }
    }
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"context"
	"encoding/base64"
	"fmt"
	"github.com/hanjm/log"
	"io"
	"net"
	"net/http"
	"os"
	"regexp"
	"runtime"
	"strings"
	"time"
)

type Task interface {
	Download(downloadDir string, limitByteSize int64, limitTimeout time.Duration) error
	IsCompleted() bool
	FileName() string
	ContentLength() int64
}

func NewDownloadTask(sourceURL string) (Task, error) {
	switch {
	case strings.HasPrefix(sourceURL, "http"):
		return NewHTTPTask(sourceURL), nil
	case strings.HasPrefix(sourceURL, "magnet:?xt=urn:btih:") || IsBase64String(sourceURL):
		return NewMagnetTask(sourceURL), nil
	default:
		return nil, fmt.Errorf("sourceURL expect http or magnet, not %s", sourceURL)
	}
}

const (
	DownloadTaskTypeHTTP = iota
	DownloadTaskTypeMagnet
)

type TaskInfo struct {
	SourceURL     string
	StartTime     time.Time
	FileName      string
	ContentLength int64         // B 总大小
	Size          int64         // B 已下载的大小
	Duration      time.Duration // s 耗时
	Speed         int64         // B/s 速度
	IsCompleted   bool          // 是否完成
	IsError       bool          // 是否出错
	Error         string        // 错误消息
}

// download http content
type HTTPTask struct {
	TaskType int
	TaskInfo
}

func NewHTTPTask(sourceUrl string) *HTTPTask {
	return &HTTPTask{
		TaskType: DownloadTaskTypeHTTP,
		TaskInfo: TaskInfo{
			SourceURL: sourceUrl,
			FileName:  getSafeFilename(sourceUrl),
		}}
}

func (t *HTTPTask) Download(downloadDir string, limitByteSize int64, limitTimeout time.Duration) error {
	var httpClient = &http.Client{
		Timeout: limitTimeout,
		Transport: &http.Transport{
			Dial: (&net.Dialer{
				Timeout: 20 * time.Second,
			}).Dial,
			TLSHandshakeTimeout: 20 * time.Second,
		},
	}
	t.StartTime = time.Now()
	resp, err := httpClient.Get(t.SourceURL)
	if err != nil {
		return t.Errorf("http.Client error:%s", err)
	}
	defer resp.Body.Close()
	t.TaskInfo.ContentLength = resp.ContentLength
	contentDisposition := strings.SplitN(resp.Header.Get("Content-Disposition"), "=", 2)
	var attachmentName string
	if len(contentDisposition) > 1 {
		attachmentName = contentDisposition[1]
	}
	if t.TaskInfo.ContentLength <= 0 {
		resp.Body.Close()
		//一些资源是动态生成的,请求第一次是chunked stream,Header不带Content-Length,第二次请求就有Content-length
		resp, err = httpClient.Get(t.SourceURL)
		if err != nil {
			return t.Errorf("http.Client error:%s", err)
		}
		t.TaskInfo.ContentLength = resp.ContentLength
	}
	// if header has attach filename, update it
	if attachmentName != "" {
		attachmentName2 := getSafeFilename(attachmentName)
		if attachmentName2 != t.TaskInfo.FileName {
			t.TaskInfo.FileName = attachmentName2
		}
	}
	log.Infof("create HTTP task: length:%s source:%s filename:%s", getHumanSizeString(t.TaskInfo.ContentLength), t.SourceURL, t.TaskInfo.FileName)
	if t.TaskInfo.ContentLength > limitByteSize {
		return t.Errorf("the content length of sourceUrl is too big:%d, limit:%d", t.TaskInfo.ContentLength, limitByteSize)
	}
	// write file
	filename := downloadDir + "/" + t.TaskInfo.FileName
	os.Remove(filename)
	fp, err := os.Create(filename)
	if err != nil {
		return t.Errorf("create file error:%s", err)
	}
	defer fp.Sync()
	defer fp.Close()
	bufSize := 4096
	bodyReader := bufio.NewReaderSize(resp.Body, bufSize)
	if err != nil {
		return t.Errorf("create file error:%s", err)
	}
	buf := make([]byte, bufSize)
	size := 0
	readSize := 0
	completed := false
	for i := 0; ; i++ {
		readSize, err = bodyReader.Read(buf)
		if err != nil {
			if err == io.EOF {
				// 正常下载完
				completed = true
			} else {
				return t.Errorf("body read error:%s", err)
			}
		}
		_, err = fp.Write(buf[:readSize])
		size += readSize
		t.Size = int64(size)
		if i%1000 == 0 {
			t.Duration = time.Now().Sub(t.StartTime)
			t.Speed = calculateDownloadSpeed(t.Size, t.Duration)
		}
		if err != nil {
			return t.Errorf("body write error:%s", err)
		}
		if completed {
			break
		}
	}
	t.TaskInfo.IsCompleted = true
	t.Size = int64(size)
	t.TaskInfo.ContentLength = t.Size
	t.Duration = time.Now().Sub(t.StartTime)
	t.Speed = calculateDownloadSpeed(t.Size, t.Duration)
	log.Infof("complete HTTP task: length:%s source:%s filename:%s, duration:%s", getHumanSizeString(t.TaskInfo.ContentLength), t.SourceURL, t.TaskInfo.FileName, t.Duration)
	return nil
}

func (t *HTTPTask) IsCompleted() bool {
	return t.TaskInfo.IsCompleted
}

func (t *HTTPTask) FileName() string {
	return t.TaskInfo.FileName
}

func (t *HTTPTask) ContentLength() int64 {
	return t.TaskInfo.ContentLength
}

func (t *HTTPTask) Errorf(format string, a ...interface{}) (err error) {
	err = fmt.Errorf(format, a...)
	_, file, line, ok := runtime.Caller(1)
	if ok {
		log.Errorf("[%s:%d]%s", file, line, err.Error())
	}
	t.TaskInfo.IsError = true
	t.TaskInfo.IsCompleted = true
	t.TaskInfo.Error = err.Error()
	return err
}

// download magnet content
type MagnetTask struct {
	TaskType int
	TaskInfo
}

func NewMagnetTask(sourceUrl string) *MagnetTask {
	return &MagnetTask{
		TaskType: DownloadTaskTypeMagnet,
		TaskInfo: TaskInfo{
			SourceURL: sourceUrl,
			FileName:  getSafeFilename(sourceUrl),
		}}
}
func (t *MagnetTask) Download(downloadDir string, limitByteSize int64, limitTimeout time.Duration) (err error) {
	if !IsAria2cRunning() {
		return t.Errorf("aria2c is not running, cannot download magnet")
	}
	aria2cRPCClient := NewAria2cRPCClient()
	var taskGID string
	// try base64 decode
	data, err := base64.StdEncoding.DecodeString(t.SourceURL)
	if err != nil {
		taskGID, err = aria2cRPCClient.AddURI(t.SourceURL)
		if err != nil {
			return t.Errorf("call aria2c AddURI error:%s", err)
		}
	} else {
		taskGID, err = aria2cRPCClient.AddTorrent(t.SourceURL)
		if err != nil {
			return t.Errorf("call aria2c AddTorrent error:%s", err)
		}
		// save to file and change the sourceURL
		torrentFilename := fmt.Sprintf("%s/%s.torrent", downloadDir, t.SourceURL[:16])
		fp, err := os.Create(torrentFilename)
		if err != nil {
			log.Warnf("os.Create file error:%s", err)
		}
		_, err = fp.Write(data)
		if err != nil {
			log.Warnf("fp.Write error:%s", err)
		}
		t.SourceURL = torrentFilename
	}
	log.Infof("create Magnet task: sourceURL:%s, taskGID:%s", t.SourceURL, taskGID)
	t.StartTime = time.Now()
	timeout := limitTimeout
	ctx, cancel := context.WithTimeout(context.Background(), timeout)
	defer cancel()
MagnetLoop:
	complete := false
	ticker := time.NewTicker(time.Second * 5)
	for !complete {
		select {
		case <-ticker.C:
			result, err := aria2cRPCClient.TellStatus(taskGID)
			if err != nil {
				return t.Errorf("call aria2c TellStatus error:%s", err)
			}
			// update break condition
			complete = result.Completed()
			// udpate taskInfo
			t.TaskInfo.ContentLength = result.TotalLength
			t.Size = result.CompletedLength
			t.Duration = time.Now().Sub(t.StartTime)
			t.Speed = result.DownloadSpeed
			if result.CompletedLength == result.TotalLength && !result.Completed() {
				// why aria2c wait long time even it seems the download is completed.
				log.Debugf("task status:%+v goto MagnetLoop", result)
			}
			// 磁力链接建立任务时无法指定文件名 获得真实文件名后需要重命名
			realFilename := strings.TrimPrefix(result.GetFilePath(), downloadDir+"/")
			pos := strings.Index(realFilename, "/")
			if pos != -1 && pos < len(realFilename)-1 {
				realFilename = realFilename[:pos]
			}
			t.TaskInfo.FileName = realFilename
			// 检查是否有继续下载磁力链接包含的其他文件
			followedBys := result.FollowedBy
			for _, followedTaskGID := range followedBys {
				taskGID = followedTaskGID
				complete = false
				log.Debugf("task status:%+v goto MagnetLoop", result)
				goto MagnetLoop
			}
		case <-ctx.Done():
			if ctx.Err() == context.DeadlineExceeded {
				return t.Errorf("task timeout:%s", timeout)
			}
		}
	}
	t.TaskInfo.IsCompleted = true
	t.Duration = time.Now().Sub(t.StartTime)
	t.Speed = calculateDownloadSpeed(t.Size, t.Duration)
	return err
}

func (t *MagnetTask) IsCompleted() bool {
	return t.TaskInfo.IsCompleted
}

func (t *MagnetTask) FileName() string {
	return t.TaskInfo.FileName
}

func (t *MagnetTask) ContentLength() int64 {
	return t.TaskInfo.ContentLength
}

func (t *MagnetTask) Errorf(format string, a ...interface{}) (err error) {
	err = fmt.Errorf(format, a...)
	_, file, line, ok := runtime.Caller(1)
	if ok {
		log.Errorf("[%s:%d]%s", file, line, err.Error())
	}
	t.TaskInfo.IsError = true
	t.TaskInfo.IsCompleted = true
	t.TaskInfo.Error = err.Error()
	return err
}

//utils
var safeFilenameRegexp = regexp.MustCompile(`[\w\d\-.]+`)

func getSafeFilename(str string) string {
	filename := strings.Join(safeFilenameRegexp.FindAllString(str, -1), "")
	if lenOfFilename := len(filename); lenOfFilename > 50 {
		filename = filename[lenOfFilename-50 : lenOfFilename]
	}
	return fmt.Sprintf("%d-%s", time.Now().Unix(), filename)
}

func getHumanSizeString(byteSize int64) string {
	units := []string{"B", "KB", "MB", "GB", "TB", "EB"}
	index := 0
	byteSizeFloat := float64(byteSize)
	for ; byteSizeFloat > 1024; index += 1 {
		byteSizeFloat /= 1024
	}
	var unit string
	if index < len(units) {
		unit = units[index]
	} else {
		unit = "INF"
	}
	return fmt.Sprintf("%.2f %s", byteSizeFloat, unit)
}

func calculateDownloadSpeed(byteSize int64, duration time.Duration) (bytePerSecond int64) {
	if duration <= 0 {
		duration = 1
	}
	return byteSize * 1e9 / int64(duration)
}

func IsBase64String(str string) bool {
	_, err := base64.StdEncoding.DecodeString(str)
	if err != nil {
		return false
	}
	return true
}
<commit_msg>aria2下载magnet时,下载完后会有很长的seeder期, 如果CompletedLength==TotalLength, 那么就可以认为已经下载完了<commit_after>package main

import (
	"bufio"
	"context"
	"encoding/base64"
	"fmt"
	"github.com/hanjm/log"
	"io"
	"net"
	"net/http"
	"os"
	"regexp"
	"runtime"
	"strings"
	"time"
	"io/ioutil"
)

type Task interface {
	Download(downloadDir string, limitByteSize int64, limitTimeout time.Duration) error
	IsCompleted() bool
	FileName() string
	ContentLength() int64
}

func NewDownloadTask(sourceURL string) (Task, error) {
	switch {
	case strings.HasPrefix(sourceURL, "http"):
		return NewHTTPTask(sourceURL), nil
	case strings.HasPrefix(sourceURL, "magnet:?xt=urn:btih:") || IsBase64String(sourceURL):
		return NewMagnetTask(sourceURL), nil
	default:
		return nil, fmt.Errorf("sourceURL expect http or magnet, not %s", sourceURL)
	}
}

const (
	DownloadTaskTypeHTTP = iota
	DownloadTaskTypeMagnet
)

type TaskInfo struct {
	SourceURL     string
	StartTime     time.Time
	FileName      string
	ContentLength int64         // B 总大小
	Size          int64         // B 已下载的大小
	Duration      time.Duration // s 耗时
	Speed         int64         // B/s 速度
	IsCompleted   bool          // 是否完成
	IsError       bool          // 是否出错
	Error         string        // 错误消息
}

// download http content
type HTTPTask struct {
	TaskType int
	TaskInfo
}

func NewHTTPTask(sourceUrl string) *HTTPTask {
	return &HTTPTask{
		TaskType: DownloadTaskTypeHTTP,
		TaskInfo: TaskInfo{
			SourceURL: sourceUrl,
			FileName:  getSafeFilename(sourceUrl),
		}}
}

func (t *HTTPTask) Download(downloadDir string, limitByteSize int64, limitTimeout time.Duration) error {
	var httpClient = &http.Client{
		Timeout: limitTimeout,
		Transport: &http.Transport{
			Dial: (&net.Dialer{
				Timeout: 20 * time.Second,
			}).Dial,
			TLSHandshakeTimeout: 20 * time.Second,
		},
	}
	t.StartTime = time.Now()
	resp, err := httpClient.Get(t.SourceURL)
	if err != nil {
		return t.Errorf("http.Client error:%s", err)
	}
	defer resp.Body.Close()
	t.TaskInfo.ContentLength = resp.ContentLength
	contentDisposition := strings.SplitN(resp.Header.Get("Content-Disposition"), "=", 2)
	var attachmentName string
	if len(contentDisposition) > 1 {
		attachmentName = contentDisposition[1]
	}
	if t.TaskInfo.ContentLength <= 0 {
		resp.Body.Close()
		//一些资源是动态生成的,请求第一次是chunked stream,Header不带Content-Length,第二次请求就有Content-length
		resp, err = httpClient.Get(t.SourceURL)
		if err != nil {
			return t.Errorf("http.Client error:%s", err)
		}
		t.TaskInfo.ContentLength = resp.ContentLength
	}
	// if header has attach filename, update it
	if attachmentName != "" {
		attachmentName2 := getSafeFilename(attachmentName)
		if attachmentName2 != t.TaskInfo.FileName {
			t.TaskInfo.FileName = attachmentName2
		}
	}
	log.Infof("create HTTP task: length:%s source:%s filename:%s", getHumanSizeString(t.TaskInfo.ContentLength), t.SourceURL, t.TaskInfo.FileName)
	if t.TaskInfo.ContentLength > limitByteSize {
		return t.Errorf("the content length of sourceUrl is too big:%d, limit:%d", t.TaskInfo.ContentLength, limitByteSize)
	}
	// write file
	filename := downloadDir + "/" + t.TaskInfo.FileName
	os.Remove(filename)
	fp, err := os.Create(filename)
	if err != nil {
		return t.Errorf("create file error:%s", err)
	}
	defer fp.Sync()
	defer fp.Close()
	bufSize := 4096
	bodyReader := bufio.NewReaderSize(resp.Body, bufSize)
	if err != nil {
		return t.Errorf("create file error:%s", err)
	}
	buf := make([]byte, bufSize)
	size := 0
	readSize := 0
	completed := false
	for i := 0; ; i++ {
		readSize, err = bodyReader.Read(buf)
		if err != nil {
			if err == io.EOF {
				// 正常下载完
				completed = true
			} else {
				return t.Errorf("body read error:%s", err)
			}
		}
		_, err = fp.Write(buf[:readSize])
		size += readSize
		t.Size = int64(size)
		if i%1000 == 0 {
			t.Duration = time.Now().Sub(t.StartTime)
			t.Speed = calculateDownloadSpeed(t.Size, t.Duration)
		}
		if err != nil {
			return t.Errorf("body write error:%s", err)
		}
		if completed {
			break
		}
	}
	t.TaskInfo.IsCompleted = true
	t.Size = int64(size)
	t.TaskInfo.ContentLength = t.Size
	t.Duration = time.Now().Sub(t.StartTime)
	t.Speed = calculateDownloadSpeed(t.Size, t.Duration)
	log.Infof("complete HTTP task: length:%s source:%s filename:%s, duration:%s", getHumanSizeString(t.TaskInfo.ContentLength), t.SourceURL, t.TaskInfo.FileName, t.Duration)
	return nil
}

func (t *HTTPTask) IsCompleted() bool {
	return t.TaskInfo.IsCompleted
}

func (t *HTTPTask) FileName() string {
	return t.TaskInfo.FileName
}

func (t *HTTPTask) ContentLength() int64 {
	return t.TaskInfo.ContentLength
}

func (t *HTTPTask) Errorf(format string, a ...interface{}) (err error) {
	err = fmt.Errorf(format, a...)
	_, file, line, ok := runtime.Caller(1)
	if ok {
		log.Errorf("[%s:%d]%s", file, line, err.Error())
	}
	t.TaskInfo.IsError = true
	t.TaskInfo.IsCompleted = true
	t.TaskInfo.Error = err.Error()
	return err
}

// download magnet content
type MagnetTask struct {
	TaskType int
	TaskInfo
}

func NewMagnetTask(sourceUrl string) *MagnetTask {
	return &MagnetTask{
		TaskType: DownloadTaskTypeMagnet,
		TaskInfo: TaskInfo{
			SourceURL: sourceUrl,
			FileName:  getSafeFilename(sourceUrl),
		}}
}
func (t *MagnetTask) Download(downloadDir string, limitByteSize int64, limitTimeout time.Duration) (err error) {
	if !IsAria2cRunning() {
		return t.Errorf("aria2c is not running, cannot download magnet")
	}
	aria2cRPCClient := NewAria2cRPCClient()
	var taskGID string
	// magnet? / torrent? / torrent file in downloadDir
	var isMagnetLink bool
	var torrentBase64 string
	data, err := base64.StdEncoding.DecodeString(t.SourceURL)
	if err != nil {
		if data, err = ioutil.ReadFile(t.SourceURL); err != nil {
			isMagnetLink = true
		} else {
			// try read from torrent file in downloadDir, for reDownload torrent
			isMagnetLink = false
			torrentBase64 = base64.StdEncoding.EncodeToString(data)
		}
	} else {
		isMagnetLink = false
		torrentBase64 = t.SourceURL
	}
	if isMagnetLink {
		taskGID, err = aria2cRPCClient.AddURI(torrentBase64)
		if err != nil {
			return t.Errorf("call aria2c AddURI error:%s", err)
		}
	} else {
		taskGID, err = aria2cRPCClient.AddTorrent(torrentBase64)
		if err != nil {
			return t.Errorf("call aria2c AddTorrent error:%s", err)
		}
		// save to file and change the sourceURL
		torrentFilename := fmt.Sprintf("%s/%s.torrent", downloadDir, torrentBase64[:16])
		fp, err := os.Create(torrentFilename)
		if err != nil {
			log.Warnf("os.Create file error:%s", err)
		}
		_, err = fp.Write(data)
		if err != nil {
			log.Warnf("fp.Write error:%s", err)
		}
		t.SourceURL = torrentFilename
	}
	log.Infof("create Magnet task: sourceURL:%s, taskGID:%s", t.SourceURL, taskGID)
	t.StartTime = time.Now()
	timeout := limitTimeout
	ctx, cancel := context.WithTimeout(context.Background(), timeout)
	defer cancel()
MagnetLoop:
	complete := false
	ticker := time.NewTicker(time.Second * 5)
	for !complete {
		select {
		case <-ticker.C:
			result, err := aria2cRPCClient.TellStatus(taskGID)
			if err != nil {
				return t.Errorf("call aria2c TellStatus error:%s", err)
			}
			// update break condition
			complete = result.Completed()
			// udpate taskInfo
			t.TaskInfo.ContentLength = result.TotalLength
			t.Size = result.CompletedLength
			t.Duration = time.Now().Sub(t.StartTime)
			t.Speed = result.DownloadSpeed
			if !complete && result.CompletedLength > 0 && result.CompletedLength == result.TotalLength {
				// why aria2c wait so long time even it seems the download task is completed, may be as a seeder?
				log.Infof("force to set task status complete, status:%+v", result)
				time.Sleep(time.Second * 5)
				log.Infof("force to set task status complete, status:%+v", result)
				complete = true
			}
			// 磁力链接建立任务时无法指定文件名 获得真实文件名后需要重命名
			realFilename := strings.TrimPrefix(result.GetFilePath(), downloadDir+"/")
			pos := strings.Index(realFilename, "/")
			if pos != -1 && pos < len(realFilename)-1 {
				realFilename = realFilename[:pos]
			}
			t.TaskInfo.FileName = realFilename
			// 检查是否有继续下载磁力链接包含的其他文件
			followedBys := result.FollowedBy
			for _, followedTaskGID := range followedBys {
				taskGID = followedTaskGID
				complete = false
				log.Debugf("goto MagnetLoop: task status:%+v", result)
				goto MagnetLoop
			}
		case <-ctx.Done():
			if ctx.Err() == context.DeadlineExceeded {
				return t.Errorf("task timeout:%s", timeout)
			}
		}
	}
	err = aria2cRPCClient.RemoveDownloadResult(taskGID)
	if err != nil {
		log.Warnf("RemoveDownloadResult error:%s", err)
	}
	t.TaskInfo.IsCompleted = true
	t.Duration = time.Now().Sub(t.StartTime)
	t.Speed = calculateDownloadSpeed(t.Size, t.Duration)
	return nil
}

func (t *MagnetTask) IsCompleted() bool {
	return t.TaskInfo.IsCompleted
}

func (t *MagnetTask) FileName() string {
	return t.TaskInfo.FileName
}

func (t *MagnetTask) ContentLength() int64 {
	return t.TaskInfo.ContentLength
}

func (t *MagnetTask) Errorf(format string, a ...interface{}) (err error) {
	err = fmt.Errorf(format, a...)
	_, file, line, ok := runtime.Caller(1)
	if ok {
		log.Errorf("[%s:%d]%s", file, line, err.Error())
	}
	t.TaskInfo.IsError = true
	t.TaskInfo.IsCompleted = true
	t.TaskInfo.Error = err.Error()
	return err
}

//utils
var safeFilenameRegexp = regexp.MustCompile(`[\w\d\-.]+`)

func getSafeFilename(str string) string {
	filename := strings.Join(safeFilenameRegexp.FindAllString(str, -1), "")
	if lenOfFilename := len(filename); lenOfFilename > 50 {
		filename = filename[lenOfFilename-50 : lenOfFilename]
	}
	return fmt.Sprintf("%d-%s", time.Now().Unix(), filename)
}

func getHumanSizeString(byteSize int64) string {
	units := []string{"B", "KB", "MB", "GB", "TB", "EB"}
	index := 0
	byteSizeFloat := float64(byteSize)
	for ; byteSizeFloat > 1024; index += 1 {
		byteSizeFloat /= 1024
	}
	var unit string
	if index < len(units) {
		unit = units[index]
	} else {
		unit = "INF"
	}
	return fmt.Sprintf("%.2f %s", byteSizeFloat, unit)
}

func calculateDownloadSpeed(byteSize int64, duration time.Duration) (bytePerSecond int64) {
	if duration <= 0 {
		duration = 1
	}
	return byteSize * 1e9 / int64(duration)
}

func IsBase64String(str string) bool {
	_, err := base64.StdEncoding.DecodeString(str)
	if err != nil {
		return false
	}
	return true
}
<|endoftext|>


<commit_before>package navmesh

import (
	"container/heap"
	. "github.com/spate/vectormath"
	"math"
)

import ()

// Triangle Heap
type WeightedTriangle struct {
	id     int32 // triangle id
	weight float32
}

type TriangleHeap struct {
	triangles []WeightedTriangle
}

func (th *TriangleHeap) Len() int {
	return len(th.triangles)
}

func (th *TriangleHeap) Less(i, j int) bool {
	return th.triangles[i].weight < th.triangles[j].weight
}

func (th *TriangleHeap) Swap(i, j int) {
	th.triangles[i], th.triangles[j] = th.triangles[j], th.triangles[i]
}

func (th *TriangleHeap) Push(x interface{}) {
	th.triangles = append(th.triangles, x.(WeightedTriangle))
}

func (th *TriangleHeap) Pop() interface{} {
	n := len(th.triangles)
	x := th.triangles[n-1]
	th.triangles = th.triangles[:n-1]
	return x
}

type Mesh struct {
	Vertices  []Point3   // vertices
	Triangles [][3]int32 // triangles
}

// Dijkstra
type Dijkstra struct {
	Matrix map[int32][]WeightedTriangle // all edge for nodes
}

// create neighbour matrix
func (d *Dijkstra) CreateMatrixFromMesh(mesh Mesh) {
	d.Matrix = make(map[int32][]WeightedTriangle)
	for i := 0; i < len(mesh.Triangles); i++ {
		for j := 0; j < len(mesh.Triangles); j++ {
			if i == j {
				continue
			}

			if len(intersect(mesh.Triangles[i], mesh.Triangles[j])) == 2 {
				x1 := (mesh.Vertices[mesh.Triangles[i][0]].X + mesh.Vertices[mesh.Triangles[i][1]].X + mesh.Vertices[mesh.Triangles[i][2]].X) / 3.0
				y1 := (mesh.Vertices[mesh.Triangles[i][0]].Y + mesh.Vertices[mesh.Triangles[i][1]].Y + mesh.Vertices[mesh.Triangles[i][2]].Y) / 3.0
				x2 := (mesh.Vertices[mesh.Triangles[j][0]].X + mesh.Vertices[mesh.Triangles[j][1]].X + mesh.Vertices[mesh.Triangles[j][2]].X) / 3.0
				y2 := (mesh.Vertices[mesh.Triangles[j][0]].Y + mesh.Vertices[mesh.Triangles[j][1]].Y + mesh.Vertices[mesh.Triangles[j][2]].Y) / 3.0
				weight := float32(math.Sqrt(float64((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1))))
				d.Matrix[int32(i)] = append(d.Matrix[int32(i)], WeightedTriangle{int32(j), weight})
			}
		}
	}
}

func intersect(a [3]int32, b [3]int32) []int32 {
	var inter []int32
	for i := range a {
		for j := range b {
			if a[i] == b[j] {
				inter = append(inter, a[i])
			}
		}
	}
	return inter
}

func (d *Dijkstra) Run(src_id int32) map[int32]int32 {
	// triangle heap
	h := &TriangleHeap{}
	heap.Init(h)

	// min distance records
	dist := make(map[int32]float32)

	// previous map
	prev := make(map[int32]int32)

	// visit map
	visited := make(map[int32]bool)

	// set initial distance to each node as MaxFloat32
	for k := range d.Matrix {
		dist[k] = math.MaxFloat32
	}

	// source vertex, the first vertex in Heap
	heap.Push(h, WeightedTriangle{src_id, 0})
	dist[src_id] = 0.0

	for h.Len() > 0 { // for every un-visited vertex, try relaxing the path
		// pop the min element
		cur := h.Pop().(WeightedTriangle)
		if visited[cur.id] {
			continue
		}
		// current known shortest distance to u
		dist_u := dist[cur.id]
		// mark the vertex as visited.
		visited[cur.id] = true

		// for each neighbor v of u:
		for _, v := range d.Matrix[cur.id] {
			alt := dist_u + v.weight
			if alt < dist[v.id] {
				dist[v.id] = alt
				prev[v.id] = cur.id
				heap.Push(h, WeightedTriangle{v.id, alt})
			}
		}
	}
	return prev
}
<commit_msg>fix dijkstra<commit_after>package navmesh

import (
	"container/heap"
	. "github.com/spate/vectormath"
	"math"
)

import ()

// Triangle Heap
type WeightedTriangle struct {
	id     int32 // triangle id
	weight float32
}

type TriangleHeap struct {
	triangles []WeightedTriangle
}

func (th *TriangleHeap) Len() int {
	return len(th.triangles)
}

func (th *TriangleHeap) Less(i, j int) bool {
	return th.triangles[i].weight < th.triangles[j].weight
}

func (th *TriangleHeap) Swap(i, j int) {
	th.triangles[i], th.triangles[j] = th.triangles[j], th.triangles[i]
}

func (th *TriangleHeap) Push(x interface{}) {
	th.triangles = append(th.triangles, x.(WeightedTriangle))
}

func (th *TriangleHeap) Pop() interface{} {
	n := len(th.triangles)
	x := th.triangles[n-1]
	th.triangles = th.triangles[:n-1]
	return x
}

func (th *TriangleHeap) DecreaseKey(id int32, weight float32) {
	for k := range th.triangles {
		if th.triangles[k].id == id {
			th.triangles[k].weight = weight
			heap.Fix(th, k)
			println("fixed", id)
			return
		}
	}
}

type Mesh struct {
	Vertices  []Point3   // vertices
	Triangles [][3]int32 // triangles
}

// Dijkstra
type Dijkstra struct {
	Matrix map[int32][]WeightedTriangle // all edge for nodes
}

// create neighbour matrix
func (d *Dijkstra) CreateMatrixFromMesh(mesh Mesh) {
	d.Matrix = make(map[int32][]WeightedTriangle)
	for i := 0; i < len(mesh.Triangles); i++ {
		for j := 0; j < len(mesh.Triangles); j++ {
			if i == j {
				continue
			}

			if len(intersect(mesh.Triangles[i], mesh.Triangles[j])) == 2 {
				x1 := (mesh.Vertices[mesh.Triangles[i][0]].X + mesh.Vertices[mesh.Triangles[i][1]].X + mesh.Vertices[mesh.Triangles[i][2]].X) / 3.0
				y1 := (mesh.Vertices[mesh.Triangles[i][0]].Y + mesh.Vertices[mesh.Triangles[i][1]].Y + mesh.Vertices[mesh.Triangles[i][2]].Y) / 3.0
				x2 := (mesh.Vertices[mesh.Triangles[j][0]].X + mesh.Vertices[mesh.Triangles[j][1]].X + mesh.Vertices[mesh.Triangles[j][2]].X) / 3.0
				y2 := (mesh.Vertices[mesh.Triangles[j][0]].Y + mesh.Vertices[mesh.Triangles[j][1]].Y + mesh.Vertices[mesh.Triangles[j][2]].Y) / 3.0
				weight := float32(math.Sqrt(float64((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1))))
				d.Matrix[int32(i)] = append(d.Matrix[int32(i)], WeightedTriangle{int32(j), weight})
			}
		}
	}
}

func intersect(a [3]int32, b [3]int32) []int32 {
	var inter []int32
	for i := range a {
		for j := range b {
			if a[i] == b[j] {
				inter = append(inter, a[i])
			}
		}
	}
	return inter
}

func (d *Dijkstra) Run(src_id int32) map[int32]int32 {
	// triangle heap
	h := &TriangleHeap{}
	heap.Init(h)

	// min distance records
	dist := make(map[int32]float32)

	// previous map
	prev := make(map[int32]int32)

	// visit map
	visited := make(map[int32]bool)

	// set initial distance to a very large value
	for k := range d.Matrix {
		dist[k] = 99999
	}

	// source vertex, the first vertex in Heap
	heap.Push(h, WeightedTriangle{src_id, 0})
	dist[src_id] = 0.0

	for h.Len() > 0 { // for every un-visited vertex, try relaxing the path
		// pop the min element
		u := h.Pop().(WeightedTriangle)
		// current known shortest distance to u
		dist_u := dist[u.id]
		// mark the vertex as visited.
		visited[u.id] = true

		// for each neighbor v of u:
		for _, v := range d.Matrix[u.id] {
			alt := dist_u + v.weight // from src->u->v
			if !visited[v.id] {
				heap.Push(h, WeightedTriangle{v.id, alt})
			}
			if alt < dist[v.id] {
				dist[v.id] = alt
				prev[v.id] = u.id
				if !visited[v.id] {
					h.DecreaseKey(v.id, alt)
				}
			}
		}
	}
	return prev
}
<|endoftext|>