diff --git a/pi.go b/pi.go index c123891415ba30063559c159406f8a350b45ff5e..783f67110e90dc249455d33091936f704db3932b 100644 --- a/pi.go +++ b/pi.go @@ -1,14 +1,16 @@ // 29 april 2021 -// If I get time, it would be fun to do the second uebung in go. +// An example for the second Uebung in Struktur und Simulation. // 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. +// them in arrays, rather than doing i/o on every step. This is actually +// necessary if we want to make plots. We have to store all the points. // 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. +// use the canvas in vggio, although that pulls in all of the fyne library. +// There is an egregious bug in the clipping of lines in plotter, so do +// not use it. Use a scatter plot. package main import ( - "bufio" "errors" "flag" "fmt" @@ -16,13 +18,6 @@ import ( "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 ( @@ -30,18 +25,12 @@ const ( exitFailure ) -const ( - sizeHrznt font.Length = 15 * vg.Centimeter - sizeVert font.Length = 10 * vg.Centimeter - nPointPlot = 1000 -) - -func breaker(x interface{}) {} - // usage prints out anything the caller gives us then bails. func usage(e string) { - u := "usage: pi seed nsteps nstep_nprint" - fmt.Fprintln(os.Stderr, e+"\n"+u) + u := "usage: pi [flags] seed nsteps nstep_nprint" + if e != ""{ + fmt.Fprintln(os.Stderr, e+"\n"+u) + } flag.Usage() os.Exit(exitFailure) } @@ -60,7 +49,7 @@ func getnums(cmdArgs cmdArgs) ([]float32, []float32) { } } if inPoint[0] { - inCnt[0] = 1 + inCnt[0] = 1 // avoid divide by zero errors in loop } for i := 1; i < cmdArgs.nstep; i++ { inCnt[i] = inCnt[i-1] @@ -73,98 +62,20 @@ func getnums(cmdArgs cmdArgs) ([]float32, []float32) { 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 + ptmp := float64(inCnt[i]) / float64(i+1) pi[i] = float32(4 * ptmp) - stdErr[i] = float32(4 * math.Sqrt((ptmp*qtmp)/(float64(i+1)))) + stdErr[i] = float32(4 * math.Sqrt((ptmp*(1-ptmp))/(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() + fmt.Printf("%8s %7s %7s %7s\n", "step", "pi", "err", "stderr") for i := 0; i < cmdArgs.nstep; i = i + cmdArgs.nstepPrint { e := math.Pi - pi[i] - b.WriteString(fmt.Sprintf("%8d %.5f %.5f %.5f\n", i+1, pi[i], e, stdErr[i])) - } -} - -// addline saves us doing the same point copying, adding three times. -func oneplot(y []float32) (*plot.Plot, error) { - p := plot.New() - points := make(plotter.XYs, len(y)) - for i, piV := range y { - points[i] = plotter.XY{X: float64(i + 1), Y: float64(piV)} - } - if line, err := plotter.NewLine(points); err != nil { - return nil, err - } else { - line.LineStyle.Width = 4 - p.Add(line) - } - return p, nil -} - -// doplot makes a primitive plot without any interesting options -// 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 - } - const step = "step" - pPi, err := oneplot(pi) // pi estimate - if err != nil { - return err - } - pPi.Title.Text = "" - pPi.X.Label.Text = step - pPi.Y.Label.Text = "π estimate" - - pStd, err := oneplot(stdErr) // std error - if err != nil { - return err - } - pStd.Title.Text = "" - pStd.X.Label.Text = step - pStd.Y.Label.Text = "std error" - - pAbs := plot.New() - { - tmperr := make([]float32, len(pi)) // the extra bracing makes - for i := range tmperr { // the lifetime of tmperr clear... - tmperr[i] = pi[i] - math.Pi // for garbage collector - } - pAbs, err = oneplot(tmperr) - if err != nil { - return err - } - } - pAbs.X.Label.Text = step - pAbs.Y.Label.Text = "error" - dt := draw.Tiles{Rows: 3, Cols: 1, PadTop: 2} - img := vgimg.New(vg.Points(600), vg.Points(500)) - dc := draw.New(img) - dCnvs := plot.Align([][]*plot.Plot{{pPi}, {pStd}, {pAbs}}, dt, dc) - - pPi.Draw(dCnvs[0][0]) - pStd.Draw(dCnvs[1][0]) - pAbs.Draw(dCnvs[2][0]) - w, err := os.Create(plotName) - if err != nil { - return fmt.Errorf("Opening plotfile for writing %w", err) - } - defer w.Close() - breaker("before vgimg") - jpg := vgimg.JpegCanvas{Canvas: img} - if _, err := jpg.WriteTo(w); err != nil { - return err + fmt.Printf("%8d %.5f %.5f %.5f\n", i+1, pi[i], e, stdErr[i]) } - pStd.Save(vg.Points(150), vg.Points(250), "foo.png") - return nil } type cmdArgs struct { @@ -178,8 +89,8 @@ type cmdArgs struct { func cmdline(cmdArgs *cmdArgs) error { var err error var suppress bool - flag.StringVar(&cmdArgs.plotName, "p", "", "Plot filename") - flag.BoolVar(&suppress, "s", false, "Suppress stdout") + flag.StringVar(&cmdArgs.plotName, "p", "", "filename to Plot to. No name means no plofile") + flag.BoolVar(&suppress, "s", false, "Suppress stdout - the long table of numbers") flag.Parse() if flag.NArg() != 3 { return errors.New("Wrong number of command line args") @@ -201,7 +112,6 @@ func cmdline(cmdArgs *cmdArgs) error { return nil } -// main. The rules say ./pi seed nsteps nstep_print func main() { var cmdArgs cmdArgs if err := cmdline(&cmdArgs); err != nil {