go-hep.org/x/hep@v0.38.1/hbook/rootcnv/root_test.go (about)

     1  // Copyright ©2017 The go-hep Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package rootcnv_test
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"log"
    11  	"math/rand/v2"
    12  	"reflect"
    13  	"runtime"
    14  	"testing"
    15  
    16  	"github.com/google/go-cmp/cmp"
    17  	"go-hep.org/x/hep/groot"
    18  	"go-hep.org/x/hep/groot/rhist"
    19  	"go-hep.org/x/hep/groot/rtypes"
    20  	"go-hep.org/x/hep/hbook"
    21  	"go-hep.org/x/hep/hbook/rootcnv"
    22  	"go-hep.org/x/hep/hbook/yodacnv"
    23  	"gonum.org/v1/gonum/stat/distuv"
    24  )
    25  
    26  func ExampleH1D() {
    27  	f, err := groot.Open("testdata/gauss-h1.root")
    28  	if err != nil {
    29  		log.Fatal(err)
    30  	}
    31  	defer f.Close()
    32  
    33  	obj, err := f.Get("h1d")
    34  	if err != nil {
    35  		log.Fatal(err)
    36  	}
    37  
    38  	var (
    39  		root = obj.(*rhist.H1D)
    40  		h    = rootcnv.H1D(root)
    41  	)
    42  
    43  	fmt.Printf("name:    %q\n", root.Name())
    44  	fmt.Printf("mean:    %v\n", h.XMean())
    45  	fmt.Printf("std-dev: %v\n", h.XStdDev())
    46  	fmt.Printf("std-err: %v\n", h.XStdErr())
    47  
    48  	// Output:
    49  	// name:    "h1d"
    50  	// mean:    0.028120158262930028
    51  	// std-dev: 2.5450388861661377
    52  	// std-err: 0.025447023184829384
    53  }
    54  
    55  func ExampleH2D() {
    56  	f, err := groot.Open("testdata/gauss-h2.root")
    57  	if err != nil {
    58  		log.Fatal(err)
    59  	}
    60  	defer f.Close()
    61  
    62  	obj, err := f.Get("h2d")
    63  	if err != nil {
    64  		log.Fatal(err)
    65  	}
    66  
    67  	var (
    68  		root = obj.(*rhist.H2D)
    69  		h    = rootcnv.H2D(root)
    70  	)
    71  
    72  	fmt.Printf("name:      %q\n", root.Name())
    73  	fmt.Printf("x-mean:    %v\n", h.XMean())
    74  	fmt.Printf("x-std-dev: %v\n", h.XStdDev())
    75  	fmt.Printf("x-std-err: %v\n", h.XStdErr())
    76  	fmt.Printf("y-mean:    %v\n", h.YMean())
    77  	fmt.Printf("y-std-dev: %v\n", h.YStdDev())
    78  	fmt.Printf("y-std-err: %v\n", h.YStdErr())
    79  
    80  	// Output:
    81  	// name:      "h2d"
    82  	// x-mean:    -0.005792199729986178
    83  	// x-std-dev: 2.270805729597938
    84  	// x-std-err: 0.06540325772462689
    85  	// y-mean:    0.8942018621292575
    86  	// y-std-dev: 1.830794214602073
    87  	// y-std-err: 0.05273014080318356
    88  }
    89  
    90  func ExampleS2D() {
    91  	f, err := groot.Open("../../groot/testdata/graphs.root")
    92  	if err != nil {
    93  		log.Fatal(err)
    94  	}
    95  	defer f.Close()
    96  
    97  	obj, err := f.Get("tgae")
    98  	if err != nil {
    99  		log.Fatal(err)
   100  	}
   101  
   102  	var (
   103  		root = obj.(rhist.GraphErrors)
   104  		g    = rootcnv.S2D(root)
   105  	)
   106  
   107  	fmt.Printf("name:  %q\n", g.Annotation()["name"])
   108  	fmt.Printf("title: %q\n", g.Annotation()["title"])
   109  	fmt.Printf("#pts:  %v\n", g.Len())
   110  	for i, pt := range g.Points() {
   111  		x := pt.X
   112  		y := pt.Y
   113  		xlo := pt.ErrX.Min
   114  		xhi := pt.ErrX.Max
   115  		ylo := pt.ErrY.Min
   116  		yhi := pt.ErrY.Max
   117  		fmt.Printf("(x,y)[%d] = (%+e +/- [%+e, %+e], %+e +/- [%+e, %+e])\n", i, x, xlo, xhi, y, ylo, yhi)
   118  	}
   119  
   120  	// Output:
   121  	// name:  "tgae"
   122  	// title: "graph with asymmetric errors"
   123  	// #pts:  4
   124  	// (x,y)[0] = (+1.000000e+00 +/- [+1.000000e-01, +2.000000e-01], +2.000000e+00 +/- [+3.000000e-01, +4.000000e-01])
   125  	// (x,y)[1] = (+2.000000e+00 +/- [+2.000000e-01, +4.000000e-01], +4.000000e+00 +/- [+6.000000e-01, +8.000000e-01])
   126  	// (x,y)[2] = (+3.000000e+00 +/- [+3.000000e-01, +6.000000e-01], +6.000000e+00 +/- [+9.000000e-01, +1.200000e+00])
   127  	// (x,y)[3] = (+4.000000e+00 +/- [+4.000000e-01, +8.000000e-01], +8.000000e+00 +/- [+1.200000e+00, +1.600000e+00])
   128  }
   129  
   130  func TestH1D(t *testing.T) {
   131  	f, err := groot.Open("testdata/gauss-h1.root")
   132  	if err != nil {
   133  		t.Fatal(err)
   134  	}
   135  	defer f.Close()
   136  
   137  	for _, test := range []struct {
   138  		name string
   139  		want []byte
   140  	}{
   141  		{
   142  			name: "h1d",
   143  			want: []byte(`BEGIN YODA_HISTO1D_V2 /h1d
   144  Path: /h1d
   145  Title: h1d
   146  Type: Histo1D
   147  ---
   148  # Mean: 2.812016e-02
   149  # Area: 1.100600e+04
   150  # ID	 ID	 sumw	 sumw2	 sumwx	 sumwx2	 numEntries
   151  Total   	Total   	1.100600e+04	1.211000e+04	3.094905e+02	7.128989e+04	1.000400e+04
   152  Underflow	Underflow	2.000000e+00	2.000000e+00	0.000000e+00	0.000000e+00	2.000000e+00
   153  Overflow	Overflow	4.000000e+00	8.000000e+00	0.000000e+00	0.000000e+00	2.000000e+00
   154  # xlow	 xhigh	 sumw	 sumw2	 sumwx	 sumwx2	 numEntries
   155  -4.000000e+00	-3.200000e+00	6.600000e+00	7.260000e+00	0.000000e+00	0.000000e+00	6.000000e+00
   156  -3.200000e+00	-2.400000e+00	7.260000e+01	7.986000e+01	0.000000e+00	0.000000e+00	6.600000e+01
   157  -2.400000e+00	-1.600000e+00	5.434000e+02	5.977400e+02	0.000000e+00	0.000000e+00	4.940000e+02
   158  -1.600000e+00	-8.000000e-01	1.708300e+03	1.879130e+03	0.000000e+00	0.000000e+00	1.553000e+03
   159  -8.000000e-01	2.220446e-16	3.130600e+03	3.443660e+03	0.000000e+00	0.000000e+00	2.846000e+03
   160  0.000000e+00	8.000000e-01	3.136100e+03	3.449710e+03	0.000000e+00	0.000000e+00	2.851000e+03
   161  8.000000e-01	1.600000e+00	1.753400e+03	1.928740e+03	0.000000e+00	0.000000e+00	1.594000e+03
   162  1.600000e+00	2.400000e+00	5.401000e+02	5.941100e+02	0.000000e+00	0.000000e+00	4.910000e+02
   163  2.400000e+00	3.200000e+00	1.012000e+02	1.113200e+02	0.000000e+00	0.000000e+00	9.200000e+01
   164  3.200000e+00	4.000000e+00	7.700000e+00	8.470000e+00	0.000000e+00	0.000000e+00	7.000000e+00
   165  END YODA_HISTO1D_V2
   166  
   167  `),
   168  		},
   169  		{
   170  			name: "h1f",
   171  			want: []byte(`BEGIN YODA_HISTO1D_V2 /h1f
   172  Path: /h1f
   173  Title: h1f
   174  Type: Histo1D
   175  ---
   176  # Mean: 2.812016e-02
   177  # Area: 1.100600e+04
   178  # ID	 ID	 sumw	 sumw2	 sumwx	 sumwx2	 numEntries
   179  Total   	Total   	1.100600e+04	1.211000e+04	3.094905e+02	7.128989e+04	1.000400e+04
   180  Underflow	Underflow	2.000000e+00	2.000000e+00	0.000000e+00	0.000000e+00	2.000000e+00
   181  Overflow	Overflow	4.000000e+00	8.000000e+00	0.000000e+00	0.000000e+00	2.000000e+00
   182  # xlow	 xhigh	 sumw	 sumw2	 sumwx	 sumwx2	 numEntries
   183  -4.000000e+00	-3.200000e+00	6.600000e+00	7.260000e+00	0.000000e+00	0.000000e+00	6.000000e+00
   184  -3.200000e+00	-2.400000e+00	7.259995e+01	7.986000e+01	0.000000e+00	0.000000e+00	6.600000e+01
   185  -2.400000e+00	-1.600000e+00	5.434013e+02	5.977400e+02	0.000000e+00	0.000000e+00	4.940000e+02
   186  -1.600000e+00	-8.000000e-01	1.708276e+03	1.879130e+03	0.000000e+00	0.000000e+00	1.553000e+03
   187  -8.000000e-01	2.220446e-16	3.130664e+03	3.443660e+03	0.000000e+00	0.000000e+00	2.846000e+03
   188  0.000000e+00	8.000000e-01	3.136165e+03	3.449710e+03	0.000000e+00	0.000000e+00	2.851000e+03
   189  8.000000e-01	1.600000e+00	1.753375e+03	1.928740e+03	0.000000e+00	0.000000e+00	1.594000e+03
   190  1.600000e+00	2.400000e+00	5.401014e+02	5.941100e+02	0.000000e+00	0.000000e+00	4.910000e+02
   191  2.400000e+00	3.200000e+00	1.011999e+02	1.113200e+02	0.000000e+00	0.000000e+00	9.200000e+01
   192  3.200000e+00	4.000000e+00	7.700000e+00	8.470000e+00	0.000000e+00	0.000000e+00	7.000000e+00
   193  END YODA_HISTO1D_V2
   194  
   195  `),
   196  		},
   197  		{
   198  			name: "h1d-var",
   199  			want: []byte(`BEGIN YODA_HISTO1D_V2 /h1d-var
   200  Path: /h1d-var
   201  Title: h1d-var
   202  Type: Histo1D
   203  ---
   204  # Mean: 2.812016e-02
   205  # Area: 1.100600e+04
   206  # ID	 ID	 sumw	 sumw2	 sumwx	 sumwx2	 numEntries
   207  Total   	Total   	1.100600e+04	1.211000e+04	3.094905e+02	7.128989e+04	1.000400e+04
   208  Underflow	Underflow	2.000000e+00	2.000000e+00	0.000000e+00	0.000000e+00	2.000000e+00
   209  Overflow	Overflow	4.000000e+00	8.000000e+00	0.000000e+00	0.000000e+00	2.000000e+00
   210  # xlow	 xhigh	 sumw	 sumw2	 sumwx	 sumwx2	 numEntries
   211  -4.000000e+00	-3.200000e+00	6.600000e+00	7.260000e+00	0.000000e+00	0.000000e+00	6.000000e+00
   212  -3.200000e+00	-2.400000e+00	7.259995e+01	7.986000e+01	0.000000e+00	0.000000e+00	6.600000e+01
   213  -2.400000e+00	-1.600000e+00	5.434013e+02	5.977400e+02	0.000000e+00	0.000000e+00	4.940000e+02
   214  -1.600000e+00	-8.000000e-01	1.708276e+03	1.879130e+03	0.000000e+00	0.000000e+00	1.553000e+03
   215  -8.000000e-01	0.000000e+00	3.130664e+03	3.443660e+03	0.000000e+00	0.000000e+00	2.846000e+03
   216  0.000000e+00	8.000000e-01	3.136165e+03	3.449710e+03	0.000000e+00	0.000000e+00	2.851000e+03
   217  8.000000e-01	1.600000e+00	1.753375e+03	1.928740e+03	0.000000e+00	0.000000e+00	1.594000e+03
   218  1.600000e+00	2.400000e+00	5.401014e+02	5.941100e+02	0.000000e+00	0.000000e+00	4.910000e+02
   219  2.400000e+00	3.200000e+00	1.011999e+02	1.113200e+02	0.000000e+00	0.000000e+00	9.200000e+01
   220  3.200000e+00	4.000000e+00	7.700000e+00	8.470000e+00	0.000000e+00	0.000000e+00	7.000000e+00
   221  END YODA_HISTO1D_V2
   222  
   223  `),
   224  		},
   225  		{
   226  			name: "h1f-var",
   227  			want: []byte(`BEGIN YODA_HISTO1D_V2 /h1f-var
   228  Path: /h1f-var
   229  Title: h1f-var
   230  Type: Histo1D
   231  ---
   232  # Mean: 2.812016e-02
   233  # Area: 1.100600e+04
   234  # ID	 ID	 sumw	 sumw2	 sumwx	 sumwx2	 numEntries
   235  Total   	Total   	1.100600e+04	1.211000e+04	3.094905e+02	7.128989e+04	1.000400e+04
   236  Underflow	Underflow	2.000000e+00	2.000000e+00	0.000000e+00	0.000000e+00	2.000000e+00
   237  Overflow	Overflow	4.000000e+00	8.000000e+00	0.000000e+00	0.000000e+00	2.000000e+00
   238  # xlow	 xhigh	 sumw	 sumw2	 sumwx	 sumwx2	 numEntries
   239  -4.000000e+00	-3.200000e+00	6.600000e+00	7.260000e+00	0.000000e+00	0.000000e+00	6.000000e+00
   240  -3.200000e+00	-2.400000e+00	7.259995e+01	7.986000e+01	0.000000e+00	0.000000e+00	6.600000e+01
   241  -2.400000e+00	-1.600000e+00	5.434013e+02	5.977400e+02	0.000000e+00	0.000000e+00	4.940000e+02
   242  -1.600000e+00	-8.000000e-01	1.708276e+03	1.879130e+03	0.000000e+00	0.000000e+00	1.553000e+03
   243  -8.000000e-01	0.000000e+00	3.130664e+03	3.443660e+03	0.000000e+00	0.000000e+00	2.846000e+03
   244  0.000000e+00	8.000000e-01	3.136165e+03	3.449710e+03	0.000000e+00	0.000000e+00	2.851000e+03
   245  8.000000e-01	1.600000e+00	1.753375e+03	1.928740e+03	0.000000e+00	0.000000e+00	1.594000e+03
   246  1.600000e+00	2.400000e+00	5.401014e+02	5.941100e+02	0.000000e+00	0.000000e+00	4.910000e+02
   247  2.400000e+00	3.200000e+00	1.011999e+02	1.113200e+02	0.000000e+00	0.000000e+00	9.200000e+01
   248  3.200000e+00	4.000000e+00	7.700000e+00	8.470000e+00	0.000000e+00	0.000000e+00	7.000000e+00
   249  END YODA_HISTO1D_V2
   250  
   251  `),
   252  		},
   253  	} {
   254  		t.Run(test.name, func(t *testing.T) {
   255  			obj, err := f.Get(test.name)
   256  			if err != nil {
   257  				t.Fatalf("error: %+v", err)
   258  			}
   259  
   260  			var (
   261  				rhisto = obj.(rhist.H1)
   262  				h      = rootcnv.H1D(rhisto)
   263  				buf    = new(bytes.Buffer)
   264  			)
   265  
   266  			err = yodacnv.Write(buf, h)
   267  			if err != nil {
   268  				t.Fatalf("YODA error: %+v", err)
   269  			}
   270  
   271  			if !reflect.DeepEqual(buf.Bytes(), test.want) {
   272  				fatalf := t.Fatalf
   273  				if runtime.GOOS == "darwin" {
   274  					fatalf = t.Logf
   275  				}
   276  				fatalf("invalid h1:\n%s",
   277  					cmp.Diff(
   278  						string(test.want),
   279  						buf.String(),
   280  					),
   281  				)
   282  			}
   283  		})
   284  	}
   285  }
   286  
   287  func TestH2D(t *testing.T) {
   288  	f, err := groot.Open("testdata/gauss-h2.root")
   289  	if err != nil {
   290  		t.Fatal(err)
   291  	}
   292  	defer f.Close()
   293  
   294  	for _, test := range []struct {
   295  		name string
   296  		want []byte
   297  	}{
   298  		{
   299  			name: "h2f",
   300  			want: []byte(`BEGIN YODA_HISTO2D_V2 /h2f
   301  Path: /h2f
   302  Title: h2f
   303  Type: Histo2D
   304  ---
   305  # Mean: (-5.792200e-03, 8.942019e-01)
   306  # Volume: 1.083600e+04
   307  # ID	 ID	 sumw	 sumw2	 sumwx	 sumwx2	 sumwy	 sumwy2	 sumwxy	 numEntries
   308  Total   	Total   	1.083600e+04	9.740400e+04	-6.276428e+01	5.583048e+04	9.689571e+03	4.495449e+04	-1.878975e+02	1.000800e+04
   309  # 2D outflow persistency not currently supported until API is stable
   310  # xlow	 xhigh	 ylow	 yhigh	 sumw	 sumw2	 sumwx	 sumwx2	 sumwy	 sumwy2	 sumwxy	 numEntries
   311  0.000000e+00	1.000000e+00	0.000000e+00	1.000000e+00	5.010000e+02	5.010000e+02	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	5.010000e+02
   312  0.000000e+00	1.000000e+00	1.000000e+00	2.000000e+00	4.880000e+02	4.880000e+02	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	4.880000e+02
   313  0.000000e+00	1.000000e+00	2.000000e+00	3.000000e+00	3.140000e+02	3.140000e+02	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	3.140000e+02
   314  1.000000e+00	2.000000e+00	0.000000e+00	1.000000e+00	3.850000e+02	3.850000e+02	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	3.850000e+02
   315  1.000000e+00	2.000000e+00	1.000000e+00	2.000000e+00	3.790000e+02	3.790000e+02	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	3.790000e+02
   316  1.000000e+00	2.000000e+00	2.000000e+00	3.000000e+00	2.210000e+02	2.210000e+02	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	2.210000e+02
   317  2.000000e+00	3.000000e+00	0.000000e+00	1.000000e+00	2.280000e+02	2.280000e+02	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	2.280000e+02
   318  2.000000e+00	3.000000e+00	1.000000e+00	2.000000e+00	2.320000e+02	2.320000e+02	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	2.320000e+02
   319  2.000000e+00	3.000000e+00	2.000000e+00	3.000000e+00	1.640000e+02	1.640000e+02	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	1.640000e+02
   320  END YODA_HISTO2D_V2
   321  
   322  `),
   323  		},
   324  		{
   325  			name: "h2d",
   326  			want: []byte(`BEGIN YODA_HISTO2D_V2 /h2d
   327  Path: /h2d
   328  Title: h2d
   329  Type: Histo2D
   330  ---
   331  # Mean: (-5.792200e-03, 8.942019e-01)
   332  # Volume: 1.083600e+04
   333  # ID	 ID	 sumw	 sumw2	 sumwx	 sumwx2	 sumwy	 sumwy2	 sumwxy	 numEntries
   334  Total   	Total   	1.083600e+04	9.740400e+04	-6.276428e+01	5.583048e+04	9.689571e+03	4.495449e+04	-1.878975e+02	1.000800e+04
   335  # 2D outflow persistency not currently supported until API is stable
   336  # xlow	 xhigh	 ylow	 yhigh	 sumw	 sumw2	 sumwx	 sumwx2	 sumwy	 sumwy2	 sumwxy	 numEntries
   337  0.000000e+00	1.000000e+00	0.000000e+00	1.000000e+00	5.010000e+02	5.010000e+02	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	5.010000e+02
   338  0.000000e+00	1.000000e+00	1.000000e+00	2.000000e+00	4.880000e+02	4.880000e+02	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	4.880000e+02
   339  0.000000e+00	1.000000e+00	2.000000e+00	3.000000e+00	3.140000e+02	3.140000e+02	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	3.140000e+02
   340  1.000000e+00	2.000000e+00	0.000000e+00	1.000000e+00	3.850000e+02	3.850000e+02	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	3.850000e+02
   341  1.000000e+00	2.000000e+00	1.000000e+00	2.000000e+00	3.790000e+02	3.790000e+02	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	3.790000e+02
   342  1.000000e+00	2.000000e+00	2.000000e+00	3.000000e+00	2.210000e+02	2.210000e+02	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	2.210000e+02
   343  2.000000e+00	3.000000e+00	0.000000e+00	1.000000e+00	2.280000e+02	2.280000e+02	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	2.280000e+02
   344  2.000000e+00	3.000000e+00	1.000000e+00	2.000000e+00	2.320000e+02	2.320000e+02	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	2.320000e+02
   345  2.000000e+00	3.000000e+00	2.000000e+00	3.000000e+00	1.640000e+02	1.640000e+02	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	1.640000e+02
   346  END YODA_HISTO2D_V2
   347  
   348  `),
   349  		},
   350  	} {
   351  		t.Run(test.name, func(t *testing.T) {
   352  			obj, err := f.Get(test.name)
   353  			if err != nil {
   354  				t.Fatalf("error: %+v", err)
   355  			}
   356  			var (
   357  				rhisto = obj.(rhist.H2)
   358  				h      = rootcnv.H2D(rhisto)
   359  				buf    = new(bytes.Buffer)
   360  			)
   361  
   362  			err = yodacnv.Write(buf, h)
   363  			if err != nil {
   364  				t.Fatalf("YODA error: %+v", err)
   365  			}
   366  
   367  			if !reflect.DeepEqual(buf.Bytes(), test.want) {
   368  				t.Fatalf("invalid h2d:\n%s",
   369  					cmp.Diff(
   370  						string(test.want),
   371  						buf.String(),
   372  					),
   373  				)
   374  			}
   375  		})
   376  	}
   377  }
   378  
   379  func TestFromH1D(t *testing.T) {
   380  	const npoints = 10000
   381  
   382  	// Create a normal distribution.
   383  	dist := distuv.Normal{
   384  		Mu:    0,
   385  		Sigma: 1,
   386  		Src:   rand.New(rand.NewPCG(0, 0)),
   387  	}
   388  
   389  	// Draw some random values from the standard
   390  	// normal distribution.
   391  	h := hbook.NewH1D(20, -4, +4)
   392  	for range npoints {
   393  		v := dist.Rand()
   394  		h.Fill(v, 1)
   395  	}
   396  	h.Fill(-10, 1) // fill underflow
   397  	h.Fill(-20, 2)
   398  	h.Fill(+10, 1) // fill overflow
   399  	h.Fill(+10, 2)
   400  	h.Annotation()["name"] = "my-name"
   401  	h.Annotation()["title"] = "my-title"
   402  
   403  	for _, tc := range []struct {
   404  		name   string
   405  		h1     rhist.H1
   406  		sumw   float64
   407  		sumw2  float64
   408  		sumwx  float64
   409  		sumwx2 float64
   410  	}{
   411  		{
   412  			name:   "TH1D",
   413  			h1:     rootcnv.FromH1D(h),
   414  			sumw:   h.SumW(),
   415  			sumw2:  h.SumW2(),
   416  			sumwx:  h.SumWX(),
   417  			sumwx2: h.SumWX2(),
   418  		},
   419  	} {
   420  		t.Run(tc.name, func(t *testing.T) {
   421  			if got, want := tc.h1.SumW(), h.SumW(); got != want {
   422  				t.Fatalf("sumw: got=%v, want=%v", got, want)
   423  			}
   424  			if got, want := tc.h1.SumW2(), h.SumW2(); got != want {
   425  				t.Fatalf("sumw2: got=%v, want=%v", got, want)
   426  			}
   427  			if got, want := tc.h1.SumWX(), h.SumWX(); got != want {
   428  				t.Fatalf("sumwx: got=%v, want=%v", got, want)
   429  			}
   430  			if got, want := tc.h1.SumWX2(), h.SumWX2(); got != want {
   431  				t.Fatalf("sumwx2: got=%v, want=%v", got, want)
   432  			}
   433  
   434  			rraw, err := tc.h1.(yodacnv.Marshaler).MarshalYODA()
   435  			if err != nil {
   436  				t.Fatal(err)
   437  			}
   438  
   439  			hh := rootcnv.H1D(tc.h1)
   440  
   441  			hraw, err := hh.MarshalYODA()
   442  			if err != nil {
   443  				t.Fatal(err)
   444  			}
   445  
   446  			var hr = rtypes.Factory.Get(tc.name)().Interface().(rhist.H1)
   447  			if err := hr.(yodacnv.Unmarshaler).UnmarshalYODA(hraw); err != nil {
   448  				t.Fatal(err)
   449  			}
   450  
   451  			rgot, err := hr.(yodacnv.Marshaler).MarshalYODA()
   452  			if err != nil {
   453  				t.Fatal(err)
   454  			}
   455  
   456  			if !bytes.Equal(rgot, rraw) {
   457  				t.Fatalf("round trip error:\nraw:\n%s\ngot:\n%s\n", rraw, rgot)
   458  			}
   459  		})
   460  	}
   461  }
   462  
   463  func TestFromH2D(t *testing.T) {
   464  	const npoints = 10000
   465  
   466  	// Create a normal distribution.
   467  	dist := distuv.Normal{
   468  		Mu:    0,
   469  		Sigma: 1,
   470  		Src:   rand.New(rand.NewPCG(0, 0)),
   471  	}
   472  
   473  	// Draw some random values from the standard
   474  	// normal distribution.
   475  	h := hbook.NewH2D(5, -4, +4, 6, -4, +4)
   476  	for range npoints {
   477  		x := dist.Rand()
   478  		y := dist.Rand()
   479  		h.Fill(x, y, 1)
   480  	}
   481  	h.Fill(+0, +5, 1) // N
   482  	h.Fill(-5, +5, 2) // N-W
   483  	h.Fill(-5, +0, 3) // W
   484  	h.Fill(-5, -5, 4) // S-W
   485  	h.Fill(+0, -5, 5) // S
   486  	h.Fill(+5, -5, 6) // S-E
   487  	h.Fill(+5, +0, 7) // E
   488  	h.Fill(+5, +5, 8) // N-E
   489  
   490  	h.Annotation()["name"] = "my-name"
   491  	h.Annotation()["title"] = "my-title"
   492  
   493  	for _, tc := range []struct {
   494  		name   string
   495  		h2     rhist.H2
   496  		sumw   float64
   497  		sumw2  float64
   498  		sumwx  float64
   499  		sumwx2 float64
   500  	}{
   501  		{
   502  			name:   "TH2D",
   503  			h2:     rootcnv.FromH2D(h),
   504  			sumw:   h.SumW(),
   505  			sumw2:  h.SumW2(),
   506  			sumwx:  h.SumWX(),
   507  			sumwx2: h.SumWX2(),
   508  		},
   509  	} {
   510  		t.Run(tc.name, func(t *testing.T) {
   511  			if got, want := tc.h2.SumW(), h.SumW(); got != want {
   512  				t.Fatalf("sumw: got=%v, want=%v", got, want)
   513  			}
   514  			if got, want := tc.h2.SumW2(), h.SumW2(); got != want {
   515  				t.Fatalf("sumw2: got=%v, want=%v", got, want)
   516  			}
   517  			if got, want := tc.h2.SumWX(), h.SumWX(); got != want {
   518  				t.Fatalf("sumwx: got=%v, want=%v", got, want)
   519  			}
   520  			if got, want := tc.h2.SumWX2(), h.SumWX2(); got != want {
   521  				t.Fatalf("sumwx2: got=%v, want=%v", got, want)
   522  			}
   523  
   524  			rraw, err := tc.h2.(yodacnv.Marshaler).MarshalYODA()
   525  			if err != nil {
   526  				t.Fatal(err)
   527  			}
   528  
   529  			hh := rootcnv.H2D(tc.h2)
   530  
   531  			hraw, err := hh.MarshalYODA()
   532  			if err != nil {
   533  				t.Fatal(err)
   534  			}
   535  
   536  			var hr = rtypes.Factory.Get(tc.name)().Interface().(rhist.H2)
   537  			if err := hr.(yodacnv.Unmarshaler).UnmarshalYODA(hraw); err != nil {
   538  				t.Fatal(err)
   539  			}
   540  
   541  			rgot, err := hr.(yodacnv.Marshaler).MarshalYODA()
   542  			if err != nil {
   543  				t.Fatal(err)
   544  			}
   545  
   546  			// rounding errors... // FIXME(sbinet)
   547  			rraw = bytes.Replace(rraw,
   548  				[]byte("# Mean: (1.990041e-02, 2.039840e-04)"),
   549  				[]byte("# Mean: (1.990041e-02, 2.039841e-04)"),
   550  				-1,
   551  			)
   552  			if !bytes.Equal(rgot, rraw) {
   553  				t.Fatalf("round trip error:\n%s\n",
   554  					cmp.Diff(
   555  						string(rraw),
   556  						string(rgot),
   557  					),
   558  				)
   559  			}
   560  		})
   561  	}
   562  }
   563  
   564  func TestFromS2D(t *testing.T) {
   565  	hg := hbook.NewS2D(
   566  		hbook.Point2D{X: 1, Y: 1, ErrX: hbook.Range{Min: 1, Max: 2}, ErrY: hbook.Range{Min: 3, Max: 4}},
   567  		hbook.Point2D{X: 2, Y: 1.5, ErrX: hbook.Range{Min: 1, Max: 2}, ErrY: hbook.Range{Min: 3, Max: 4}},
   568  		hbook.Point2D{X: -1, Y: +2, ErrX: hbook.Range{Min: 1, Max: 2}, ErrY: hbook.Range{Min: 3, Max: 4}},
   569  	)
   570  
   571  	rg := rootcnv.FromS2D(hg)
   572  
   573  	hr := rootcnv.S2D(rg)
   574  
   575  	want, err := hg.MarshalYODA()
   576  	if err != nil {
   577  		t.Fatal(err)
   578  	}
   579  
   580  	got, err := hr.MarshalYODA()
   581  	if err != nil {
   582  		t.Fatal(err)
   583  	}
   584  
   585  	if !bytes.Equal(got, want) {
   586  		t.Fatalf("invalid s2d:\n%s",
   587  			cmp.Diff(
   588  				string(want),
   589  				string(got),
   590  			),
   591  		)
   592  	}
   593  }