initial commit

This commit is contained in:
filifa 2024-05-25 17:28:47 -05:00
commit 35f82feb3e
5 changed files with 175 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
slacktime
*.gv

5
go.mod Normal file
View File

@ -0,0 +1,5 @@
module scm.dairydemon.net/filifa/slacktime
go 1.19
require gonum.org/v1/gonum v0.15.0 // indirect

2
go.sum Normal file
View File

@ -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=

42
main.go Normal file
View File

@ -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))
}

124
projectnetwork.go Normal file
View File

@ -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
}