initial commit
This commit is contained in:
		
						commit
						35f82feb3e
					
				| 
						 | 
				
			
			@ -0,0 +1,2 @@
 | 
			
		|||
slacktime
 | 
			
		||||
*.gv
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,5 @@
 | 
			
		|||
module scm.dairydemon.net/filifa/slacktime
 | 
			
		||||
 | 
			
		||||
go 1.19
 | 
			
		||||
 | 
			
		||||
require gonum.org/v1/gonum v0.15.0 // indirect
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,2 @@
 | 
			
		|||
gonum.org/v1/gonum v0.15.0 h1:2lYxjRbTYyxkJxlhC+LvJIx3SsANPdRybu1tGj9/OrQ=
 | 
			
		||||
gonum.org/v1/gonum v0.15.0/go.mod h1:xzZVBJBtS+Mz4q0Yl2LJTk+OxOg4jiXZ7qBoM0uISGo=
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,42 @@
 | 
			
		|||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"flag"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log"
 | 
			
		||||
	"os"
 | 
			
		||||
 | 
			
		||||
	"gonum.org/v1/gonum/graph/encoding/dot"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	f := flag.String("f", "", "graphviz file")
 | 
			
		||||
	flag.Parse()
 | 
			
		||||
 | 
			
		||||
	if *f == "" {
 | 
			
		||||
		log.Fatal("-f is required")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	contents, err := os.ReadFile(*f)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	g := newProjectNetwork()
 | 
			
		||||
	err = dot.Unmarshal(contents, g)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = g.calculateTimes()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	data, err := dot.Marshal(g, "", "", "\t")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fmt.Println(string(data))
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,124 @@
 | 
			
		|||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"math"
 | 
			
		||||
	"strconv"
 | 
			
		||||
 | 
			
		||||
	"gonum.org/v1/gonum/graph"
 | 
			
		||||
	"gonum.org/v1/gonum/graph/encoding"
 | 
			
		||||
	"gonum.org/v1/gonum/graph/simple"
 | 
			
		||||
	"gonum.org/v1/gonum/graph/topo"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type projectNetwork struct {
 | 
			
		||||
	*simple.DirectedGraph
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newProjectNetwork() *projectNetwork {
 | 
			
		||||
	return &projectNetwork{DirectedGraph: simple.NewDirectedGraph()}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (g *projectNetwork) NewNode() graph.Node {
 | 
			
		||||
	return &activity{Node: g.DirectedGraph.NewNode()}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (g *projectNetwork) calculateTimes() error {
 | 
			
		||||
	sorted, err := topo.Sort(g)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	toposort := make([]*activity, len(sorted))
 | 
			
		||||
	for i := range sorted {
 | 
			
		||||
		toposort[i] = sorted[i].(*activity)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	g.forwardPass(toposort)
 | 
			
		||||
	g.backwardPass(toposort)
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (g *projectNetwork) forwardPass(toposort []*activity) {
 | 
			
		||||
	for _, a := range toposort {
 | 
			
		||||
		g.setEarlyTimes(a)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (g *projectNetwork) backwardPass(toposort []*activity) {
 | 
			
		||||
	n := len(toposort)
 | 
			
		||||
 | 
			
		||||
	end := toposort[n-1]
 | 
			
		||||
	end.lateFinish = end.earlyFinish
 | 
			
		||||
	end.lateStart = end.lateFinish - end.duration
 | 
			
		||||
 | 
			
		||||
	for i := n - 2; i >= 0; i-- {
 | 
			
		||||
		a := toposort[i]
 | 
			
		||||
		g.setLateTimes(a)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (g *projectNetwork) setEarlyTimes(a *activity) {
 | 
			
		||||
	es := 0.0
 | 
			
		||||
	predecessors := g.To(a.ID())
 | 
			
		||||
	for predecessors.Next() {
 | 
			
		||||
		p := predecessors.Node().(*activity)
 | 
			
		||||
		es = math.Max(es, p.earlyFinish)
 | 
			
		||||
	}
 | 
			
		||||
	a.earlyStart = es
 | 
			
		||||
	a.earlyFinish = a.earlyStart + a.duration
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (g *projectNetwork) setLateTimes(a *activity) {
 | 
			
		||||
	lf := math.Inf(1)
 | 
			
		||||
	successors := g.From(a.ID())
 | 
			
		||||
	for successors.Next() {
 | 
			
		||||
		s := successors.Node().(*activity)
 | 
			
		||||
		lf = math.Min(lf, s.lateStart)
 | 
			
		||||
	}
 | 
			
		||||
	a.lateFinish = lf
 | 
			
		||||
	a.lateStart = a.lateFinish - a.duration
 | 
			
		||||
	a.slack = a.lateFinish - a.earlyFinish
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type activity struct {
 | 
			
		||||
	graph.Node
 | 
			
		||||
	dotID       string
 | 
			
		||||
	duration    float64
 | 
			
		||||
	earlyStart  float64
 | 
			
		||||
	earlyFinish float64
 | 
			
		||||
	lateStart   float64
 | 
			
		||||
	lateFinish  float64
 | 
			
		||||
	slack	float64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (a *activity) Attributes() []encoding.Attribute {
 | 
			
		||||
	return []encoding.Attribute{
 | 
			
		||||
		{"DUR", strconv.FormatFloat(a.duration, 'g', -1, 64)},
 | 
			
		||||
		{"ES", strconv.FormatFloat(a.earlyStart, 'g', -1, 64)},
 | 
			
		||||
		{"EF", strconv.FormatFloat(a.earlyStart, 'g', -1, 64)},
 | 
			
		||||
		{"LS", strconv.FormatFloat(a.lateStart, 'g', -1, 64)},
 | 
			
		||||
		{"LF", strconv.FormatFloat(a.lateFinish, 'g', -1, 64)},
 | 
			
		||||
		{"SLACK", strconv.FormatFloat(a.slack, 'g', -1, 64)},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (a *activity) SetAttribute(attr encoding.Attribute) error {
 | 
			
		||||
	var err error
 | 
			
		||||
	switch attr.Key {
 | 
			
		||||
	case "DUR":
 | 
			
		||||
		a.duration, err = strconv.ParseFloat(attr.Value, 64)
 | 
			
		||||
	default:
 | 
			
		||||
		err = errors.New("can't set attribute " + attr.Key)
 | 
			
		||||
	}
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (a *activity) DOTID() string {
 | 
			
		||||
	return a.dotID
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (a *activity) SetDOTID(id string) {
 | 
			
		||||
	a.dotID = id
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue