You are an expert in writing code in many different languages. Your goal is to perform code completion for the following code, keeping in mind the rest of the code and the file meta data.
    Metadata details:
    filename: batchnorm_cuda.go
    path of file: ./repos/gorgonia/ops/nn
    
    the code of the file until where you have to start completion: // +build cuda

package nnops

import (
	"fmt"
	"hash"

	"github.com/chewxy/hm"
	"gorgonia.org/cu/dnn"
	t2cudnn "gorgonia.org/cu/dnn/interop"
	"gorgonia.org/gorgonia"
	"gorgonia.org/tensor"
)

type BatchNormOp struct {
	mode              cudnn.BatchNormMode
	momentum, epsilon float64

	xDesc     *cudnn.TensorDescriptor
	bnScratch *cudnn.TensorDescriptor

	training bool
}

func newBatchNormOp(momentum, epsilon float64) *BatchNormOp {
	return &BatchNormOp{
		mode:     cudnn.PerActivation,
		momentum: momentum,
		epsilon:  epsilon,
		training: true,
	}
}

func (op *BatchNormOp) Arity() int { return 7 }

func (op *BatchNormOp) Type() hm.Type {
	t := gorgonia.TensorType{Dims: 4, Of: hm.TypeVariable('a')}
	return hm.NewFnType(t, // x
		t, // scale
		t, // bias
		t, // running mean / expected mean
		t, // running var / expected var
		t, // cached mean
		t, // cachedVar
		t) // retVal
}

func (op *BatchNormOp) InferShape(inputs ...gorgonia.DimSizer) (tensor.Shape, error) {
	if err := checkArity(op, len(inputs)); err != nil {
		return nil, err
	}
	return inputs[0].(tensor.Shape).Clone(), nil
}

func (op *BatchNormOp) Do(...gorgonia.Value) (gorgonia.Value, error) { panic("
    
func (op *BatchNormOp) CUDADo(extern gorgonia.External, dev gorgonia.Device, prealloc gorgonia.Value, inputs ...gorgonia.Value) (retVal gorgonia.Value, err error) {
	// panic("not implemented")

	machine := extern.(gorgonia.CUDAMachine)
	ctx := machine.CUDNNContexts()[int(dev)]

	x, bnScale, bnBias, mean, variance, cachedMean, cachedVar := inputs[0], inputs[1], inputs[2], inputs[3], inputs[4], inputs[5], inputs[6]
	if op.xDesc == nil {
		if op.xDesc, err = t2cudnn.Describe(x.(tensor.Tensor)); err != nil {
			return
		}
	}

	if op.bnScratch == nil {
		if op.bnScratch, err = t2cudnn.Describe(mean.(tensor.Tensor)); err != nil {
			return
		}
	}

	alpha := 0.0
	beta := 1.0
	if op.training {
		err = ctx.BatchNormalizationForwardTraining(op.mode, alpha, beta,
			op.xDesc, x.(cudnn.Memory),
			op.xDesc, prealloc.(cudnn.Memory), // yDesc, y
			op.bnScratch,
			bnScale.(cudnn.Memory),
			bnBias.(cudnn.Memory),
			op.momentum,
			mean.(cudnn.Memory),     // runniing mean
			variance.(cudnn.Memory), // running variance
			op.epsilon,
			cachedMean.(cudnn.Memory),
			cachedVar.(cudnn.Memory),
		)
	} else {
		err = ctx.BatchNormalizationForwardInference(op.mode, alpha, beta,
			op.xDesc, x.(cudnn.Memory),
			op.xDesc, prealloc.(cudnn.Memory),
			op.bnScratch,
			bnScale.(cudnn.Memory),
			bnBias.(cudnn.Memory),
			mean.(cudnn.Memory),     // expected mean
			variance.(cudnn.Memory), // expected variance
			op.epsilon)
	}
	return prealloc, err
}