diff --git a/go.mod b/go.mod index 184b9ef0c896f3343e0f718c91a4578af2d83321..2ac83b2c429b939cf7aea7a747c6119eedc4c6c3 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,7 @@ module pi go 1.16 -require gonum.org/v1/plot v0.9.0 +require ( + github.com/gizak/termui/v3 v3.1.0 + gonum.org/v1/plot v0.9.0 +) diff --git a/go.sum b/go.sum index 4104d696ba080123894d85ee44f408d3ec2cacf2..bed3ecc344170309930ede4d6845cf855345865c 100644 --- a/go.sum +++ b/go.sum @@ -8,6 +8,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/gizak/termui/v3 v3.1.0 h1:ZZmVDgwHl7gR7elfKf1xc4IudXZ5qqfDh4wExk4Iajc= +github.com/gizak/termui/v3 v3.1.0/go.mod h1:bXQEBkJpzxUAKf0+xq9MSWAvWZlE7c+aidmyFlkYTrY= github.com/go-fonts/dejavu v0.1.0 h1:JSajPXURYqpr+Cu8U9bt8K+XcACIHWqWrvWCKyeFmVQ= github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= @@ -21,6 +23,12 @@ github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF0 github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= +github.com/mattn/go-runewidth v0.0.2 h1:UnlwIPBGaTZfPQ6T1IGzPI0EkYAQmT9fAEJ/poFC63o= +github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM= +github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d h1:x3S6kxmy49zXVVyhcnrFqxvNVCBPb2KZ9hV2RBdS840= +github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ= github.com/phpdave11/gofpdf v1.4.2 h1:KPKiIbfwbvC/wOncwhrpRdXVj2CZTCFlw4wnoyjtHfQ= github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= @@ -35,6 +43,7 @@ golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3 h1:n9HxLrNxWWtEb1cA950nuEEj3QnKbtsCJ6KjcgisNUs= golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= @@ -64,6 +73,7 @@ golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= +gonum.org/v1/gonum v0.8.2 h1:CCXrcPKiGGotvnN6jfUsKk4rRqm7q09/YbKb5xCEvtM= gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= diff --git a/pi.go b/pi.go index f1f72255628eabbe78f35563dbd3fc9b8d49d6db..6b12771fa3503d3ece14df368cbb4c00058ba4b0 100644 --- a/pi.go +++ b/pi.go @@ -2,19 +2,28 @@ // If I get time, it would be fun to do the second uebung in go. // For the sake of speed, we can do all the numbers at once, put // them in arrays, rather than doing i/o on every step. +// The plotting library I used does not draw to the screen. Bummer. One could +// use the canvas in vggio, although that pulls in all of fyne. + package main import ( + "bufio" "errors" "flag" "fmt" + "math" "math/rand" "os" "strconv" "gonum.org/v1/plot" + "gonum.org/v1/plot/font" + "gonum.org/v1/plot/plotter" "gonum.org/v1/plot/vg" + "gonum.org/v1/plot/vg/draw" + "gonum.org/v1/plot/vg/vgimg" ) const ( @@ -22,6 +31,12 @@ const ( exitFailure ) +const ( + sizeHrznt font.Length = 15 * vg.Centimeter + sizeVert font.Length = 10 * vg.Centimeter + nPointPlot = 1000 +) + // usage prints out anything the caller gives us then bails. func usage(e string) { u := "usage: pi seed nsteps nstep_nprint" @@ -30,7 +45,7 @@ func usage(e string) { } // getnums does the work of filling out the arrays of results -func getnums(cmdArgs cmdArgs) { +func getnums(cmdArgs cmdArgs) ([]float32, []float32) { rand.Seed(cmdArgs.seed) inCnt := make([]uint32, cmdArgs.nstep) { // The braces limit the lifetime of inPoint, to save a bit of memory @@ -51,20 +66,57 @@ func getnums(cmdArgs cmdArgs) { inCnt[i]++ } } + } // We have the counts. Now calculate the errors and standard error + pi := make([]float32, cmdArgs.nstep) // estimated pi + stdErr := make([]float32, cmdArgs.nstep) // standard error + + for i := range inCnt { + ptmp := float64(inCnt[i]) / float64(i+1) // do we need more precision ? + qtmp := 1 - ptmp + pi[i] = float32(4 * ptmp) + stdErr[i] = float32(4 * math.Sqrt((ptmp*qtmp)/(float64(i+1)))) } + return pi, stdErr +} + +// wrtTable prints out the table, as per the Uebung instructions +func wrtTable(cmdArgs cmdArgs, pi, stdErr []float32) { + b := bufio.NewWriter(os.Stdout) + b.WriteString(fmt.Sprintf("%8s %7s %7s %7s\n", "step", "pi", "err", "stderr")) + defer b.Flush() for i := 0; i < cmdArgs.nstep; i = i + cmdArgs.nstepPrint { - pi := 4 * float32(inCnt[i]) / float32(i+1) - fmt.Println(i, inCnt[i], pi) + e := math.Pi - pi[i] + b.WriteString(fmt.Sprintf("%8d %.5f %.5f %.5f\n", i+1, pi[i], e, stdErr[i])) } } // doplot makes a primitive plot without any interesting options -func doplot(plotName string) error { +// If no plotname is given, it means no plot is wanted. We have to rewrite the data +// to fit to the plot function's expectations (x/y pairs). +func doplot(pi, stdErr []float32, plotName string) error { + if plotName == "" { + return nil + } p := plot.New() - p.Title.Text = "ugly plot" - if err := p.Save(15*vg.Centimeter, 10*vg.Centimeter, plotName); err != nil { + p.Title.Text = "" + cnvs := draw.New(vgimg.New(vg.Points(10), vg.Points(16))) + points := make (plotter.XYs, len(pi)) + for i, piV := range pi { + points[i] = plotter.XY{X: float64(i+1), Y:float64(piV)} + } + line, err := plotter.NewLine(points) + if err != nil { return err } + p.X.Label.Text = "step" + p.Y.Label.Text = "π estimate" + p.Add(line) + p.Draw(cnvs) + if plotName != "" { + if err := p.Save(sizeHrznt, sizeVert, plotName); err != nil { + return fmt.Errorf("Plotting, opening \"%s\": %w", plotName, err) + } + } return nil } @@ -108,10 +160,16 @@ func main() { if err := cmdline(&cmdArgs); err != nil { usage(err.Error()) } - fmt.Println("doStdout is", cmdArgs.doStdout) - getnums(cmdArgs) - if err := doplot(cmdArgs.plotName); err != nil { + pi, stdErr := getnums(cmdArgs) + wrtTable(cmdArgs, pi, stdErr) + if err := doplot(pi, stdErr, cmdArgs.plotName); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(exitFailure) + } + if err := scrnPlt(pi, stdErr); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(exitFailure) } + + os.Exit(exitSuccess) }