// Copyright ©2016 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build ignore

// generate_blas creates a blas.go file from the provided C header file
// with optionally added documentation from the documentation package.
package main

import (
	"bytes"
	"fmt"
	"go/ast"
	"go/format"
	"io/ioutil"
	"log"
	"strings"
	"text/template"

	"github.com/cznic/cc"

	"github.com/gonum/internal/binding"
)

const (
	header        = "cblas.h"
	documentation = "../native"
	target        = "blas.go"

	typ = "Implementation"

	prefix = "cblas_"

	warning = "Float32 implementations are autogenerated and not directly tested."
)

const (
	cribDocs      = true
	elideRepeat   = true
	noteOrigin    = true
	separateFuncs = false
)

var skip = map[string]bool{
	"cblas_errprn":    true,
	"cblas_srotg":     true,
	"cblas_srotmg":    true,
	"cblas_srotm":     true,
	"cblas_drotg":     true,
	"cblas_drotmg":    true,
	"cblas_drotm":     true,
	"cblas_crotg":     true,
	"cblas_zrotg":     true,
	"cblas_cdotu_sub": true,
	"cblas_cdotc_sub": true,
	"cblas_zdotu_sub": true,
	"cblas_zdotc_sub": true,

	// ATLAS extensions.
	"cblas_csrot": true,
	"cblas_zdrot": true,
}

var cToGoType = map[string]string{
	"int":    "int",
	"float":  "float32",
	"double": "float64",
}

var blasEnums = map[string]*template.Template{
	"CBLAS_ORDER":     template.Must(template.New("order").Parse("order")),
	"CBLAS_DIAG":      template.Must(template.New("diag").Parse("blas.Diag")),
	"CBLAS_TRANSPOSE": template.Must(template.New("trans").Parse("blas.Transpose")),
	"CBLAS_UPLO":      template.Must(template.New("uplo").Parse("blas.Uplo")),
	"CBLAS_SIDE":      template.Must(template.New("side").Parse("blas.Side")),
}

var cgoEnums = map[string]*template.Template{
	"CBLAS_ORDER":     template.Must(template.New("order").Parse("C.enum_CBLAS_ORDER(rowMajor)")),
	"CBLAS_DIAG":      template.Must(template.New("diag").Parse("C.enum_CBLAS_DIAG({{.}})")),
	"CBLAS_TRANSPOSE": template.Must(template.New("trans").Parse("C.enum_CBLAS_TRANSPOSE({{.}})")),
	"CBLAS_UPLO":      template.Must(template.New("uplo").Parse("C.enum_CBLAS_UPLO({{.}})")),
	"CBLAS_SIDE":      template.Must(template.New("side").Parse("C.enum_CBLAS_SIDE({{.}})")),
}

var cgoTypes = map[binding.TypeKey]*template.Template{
	{Kind: cc.Void, IsPointer: true}: template.Must(template.New("void*").Parse(
		`unsafe.Pointer(&{{.}}{{if eq . "alpha" "beta"}}{{else}}[0]{{end}})`,
	)),
}

var (
	complex64Type = map[binding.TypeKey]*template.Template{
		{Kind: cc.Void, IsPointer: true}: template.Must(template.New("void*").Parse(
			`{{if eq . "alpha" "beta"}}complex64{{else}}[]complex64{{end}}`,
		))}

	complex128Type = map[binding.TypeKey]*template.Template{
		{Kind: cc.Void, IsPointer: true}: template.Must(template.New("void*").Parse(
			`{{if eq . "alpha" "beta"}}complex128{{else}}[]complex128{{end}}`,
		))}
)

var names = map[string]string{
	"uplo":   "ul",
	"trans":  "t",
	"transA": "tA",
	"transB": "tB",
	"side":   "s",
	"diag":   "d",
}

func shorten(n string) string {
	s, ok := names[n]
	if ok {
		return s
	}
	return n
}

func main() {
	decls, err := binding.Declarations(header)
	if err != nil {
		log.Fatal(err)
	}
	var docs map[string]map[string][]*ast.Comment
	if cribDocs {
		docs, err = binding.DocComments(documentation)
		if err != nil {
			log.Fatal(err)
		}
	}

	var buf bytes.Buffer

	h, err := template.New("handwritten").Parse(handwritten)
	if err != nil {
		log.Fatal(err)
	}
	err = h.Execute(&buf, header)
	if err != nil {
		log.Fatal(err)
	}

	var n int
	for _, d := range decls {
		if !strings.HasPrefix(d.Name, prefix) || skip[d.Name] {
			continue
		}
		if n != 0 && (separateFuncs || cribDocs) {
			buf.WriteByte('\n')
		}
		n++
		goSignature(&buf, d, docs[typ])
		if noteOrigin {
			fmt.Fprintf(&buf, "\t// declared at %s %s %s ...\n\n", d.Position(), d.Return, d.Name)
		}
		parameterChecks(&buf, d, parameterCheckRules)
		buf.WriteByte('\t')
		cgoCall(&buf, d)
		buf.WriteString("}\n")
	}

	b, err := format.Source(buf.Bytes())
	if err != nil {
		log.Fatal(err)
	}
	err = ioutil.WriteFile(target, b, 0664)
	if err != nil {
		log.Fatal(err)
	}
}

func goSignature(buf *bytes.Buffer, d binding.Declaration, docs map[string][]*ast.Comment) {
	blasName := strings.TrimPrefix(d.Name, prefix)
	goName := binding.UpperCaseFirst(blasName)

	if docs != nil {
		if doc, ok := docs[goName]; ok {
			if strings.Contains(doc[len(doc)-1].Text, warning) {
				doc = doc[:len(doc)-2]
			}
			for _, c := range doc {
				buf.WriteString(c.Text)
				buf.WriteByte('\n')
			}
		}
	}

	parameters := d.Parameters()

	var voidPtrType map[binding.TypeKey]*template.Template
	for _, p := range parameters {
		if p.Kind() == cc.Ptr && p.Elem().Kind() == cc.Void {
			switch {
			case blasName[0] == 'c', blasName[1] == 'c' && blasName[0] != 'z':
				voidPtrType = complex64Type
			case blasName[0] == 'z', blasName[1] == 'z':
				voidPtrType = complex128Type
			}
			break
		}
	}

	fmt.Fprintf(buf, "func (%s) %s(", typ, goName)
	c := 0
	for i, p := range parameters {
		if p.Kind() == cc.Enum && binding.GoTypeForEnum(p.Type(), "", blasEnums) == "order" {
			continue
		}
		if c != 0 {
			buf.WriteString(", ")
		}
		c++

		n := shorten(binding.LowerCaseFirst(p.Name()))
		var this, next string

		if p.Kind() == cc.Enum {
			this = binding.GoTypeForEnum(p.Type(), n, blasEnums)
		} else {
			this = binding.GoTypeFor(p.Type(), n, voidPtrType)
		}

		if elideRepeat && i < len(parameters)-1 && p.Type().Kind() == parameters[i+1].Type().Kind() {
			p := parameters[i+1]
			n := shorten(binding.LowerCaseFirst(p.Name()))
			if p.Kind() == cc.Enum {
				next = binding.GoTypeForEnum(p.Type(), n, blasEnums)
			} else {
				next = binding.GoTypeFor(p.Type(), n, voidPtrType)
			}
		}
		if next == this {
			buf.WriteString(n)
		} else {
			fmt.Fprintf(buf, "%s %s", n, this)
		}
	}
	if d.Return.Kind() != cc.Void {
		fmt.Fprintf(buf, ") %s {\n", cToGoType[d.Return.String()])
	} else {
		buf.WriteString(") {\n")
	}
}

func parameterChecks(buf *bytes.Buffer, d binding.Declaration, rules []func(*bytes.Buffer, binding.Declaration, binding.Parameter) bool) {
	done := make(map[int]bool)
	for _, p := range d.Parameters() {
		for i, r := range rules {
			if done[i] {
				continue
			}
			done[i] = r(buf, d, p)
		}
	}
}

func cgoCall(buf *bytes.Buffer, d binding.Declaration) {
	if d.Return.Kind() != cc.Void {
		fmt.Fprintf(buf, "return %s(", cToGoType[d.Return.String()])
	}
	fmt.Fprintf(buf, "C.%s(", d.Name)
	for i, p := range d.Parameters() {
		if i != 0 {
			buf.WriteString(", ")
		}
		if p.Type().Kind() == cc.Enum {
			buf.WriteString(binding.CgoConversionForEnum(shorten(binding.LowerCaseFirst(p.Name())), p.Type(), cgoEnums))
		} else {
			buf.WriteString(binding.CgoConversionFor(shorten(binding.LowerCaseFirst(p.Name())), p.Type(), cgoTypes))
		}
	}
	if d.Return.Kind() != cc.Void {
		buf.WriteString(")")
	}
	buf.WriteString(")\n")
}

var parameterCheckRules = []func(*bytes.Buffer, binding.Declaration, binding.Parameter) bool{
	trans,
	uplo,
	diag,
	side,

	shape,
	apShape,
	zeroInc,
	sidedShape,
	mvShape,
	rkShape,
	gemmShape,
	scalShape,
	amaxShape,
	nrmSumShape,
	vectorShape,
	othersShape,

	noWork,
}

func amaxShape(buf *bytes.Buffer, d binding.Declaration, p binding.Parameter) bool {
	switch d.Name {
	case "cblas_isamax", "cblas_idamax", "cblas_icamax", "cblas_izamax":
	default:
		return true
	}

	if d.CParameters[len(d.CParameters)-1] != p.Parameter {
		return false // Come back later.
	}

	fmt.Fprint(buf, `	if n == 0 || incX < 0 {
		return -1
	}
	if incX > 0 && (n-1)*incX >= len(x) {
		panic("blas: x index out of range")
	}
`)
	return true
}

func apShape(buf *bytes.Buffer, _ binding.Declaration, p binding.Parameter) bool {
	n := binding.LowerCaseFirst(p.Name())
	if n != "ap" {
		return false
	}
	fmt.Fprint(buf, `	if n*(n+1)/2 > len(ap) {
		panic("blas: index of ap out of range")
	}
`)
	return true
}

func diag(buf *bytes.Buffer, _ binding.Declaration, p binding.Parameter) bool {
	if p.Name() != "Diag" {
		return false
	}
	fmt.Fprint(buf, `	if d != blas.NonUnit && d != blas.Unit {
		panic("blas: illegal diagonal")
	}
`)
	return true
}

func gemmShape(buf *bytes.Buffer, d binding.Declaration, p binding.Parameter) bool {
	switch d.Name {
	case "cblas_sgemm", "cblas_dgemm", "cblas_cgemm", "cblas_zgemm":
	default:
		return true
	}

	if d.CParameters[len(d.CParameters)-1] != p.Parameter {
		return false // Come back later.
	}

	fmt.Fprint(buf, `	var rowA, colA, rowB, colB int
	if tA == blas.NoTrans {
		rowA, colA = m, k
	} else {
		rowA, colA = k, m
	}
	if tB == blas.NoTrans {
		rowB, colB = k, n
	} else {
		rowB, colB = n, k
	}
	if lda*(rowA-1)+colA > len(a) || lda < max(1, colA) {
		panic("blas: index of a out of range")
	}
	if ldb*(rowB-1)+colB > len(b) || ldb < max(1, colB) {
		panic("blas: index of b out of range")
	}
	if ldc*(m-1)+n > len(c) || ldc < max(1, n) {
		panic("blas: index of c out of range")
	}
`)
	return true
}

func mvShape(buf *bytes.Buffer, d binding.Declaration, p binding.Parameter) bool {
	switch d.Name {
	case "cblas_sgbmv", "cblas_dgbmv", "cblas_cgbmv", "cblas_zgbmv",
		"cblas_sgemv", "cblas_dgemv", "cblas_cgemv", "cblas_zgemv":
	default:
		return true
	}

	if d.CParameters[len(d.CParameters)-1] != p.Parameter {
		return false // Come back later.
	}

	fmt.Fprint(buf, `	var lenX, lenY int
	if tA == blas.NoTrans {
		lenX, lenY = n, m
	} else {
		lenX, lenY = m, n
	}
	if (incX > 0 && (lenX-1)*incX >= len(x)) || (incX < 0 && (1-lenX)*incX >= len(x)) {
		panic("blas: x index out of range")
	}
	if (incY > 0 && (lenY-1)*incY >= len(y)) || (incY < 0 && (1-lenY)*incY >= len(y)) {
		panic("blas: y index out of range")
	}
`)
	return true
}

func noWork(buf *bytes.Buffer, d binding.Declaration, p binding.Parameter) bool {
	var hasN, hasLda, hasLdb bool
	for _, p := range d.Parameters() {
		switch shorten(binding.LowerCaseFirst(p.Name())) {
		case "n":
			hasN = true
		case "lda":
			hasLda = true
		case "ldb":
			hasLdb = true
		}
	}
	if !hasN || hasLda || hasLdb {
		return true
	}

	if d.CParameters[len(d.CParameters)-1] != p.Parameter {
		return false // Come back later.
	}

	var value string
	switch d.Return.String() {
	case "int":
		value = " -1"
	case "float", "double":
		value = " 0"
	}
	fmt.Fprintf(buf, `	if n == 0 {
		return%s
	}
`, value)
	return true
}

func nrmSumShape(buf *bytes.Buffer, d binding.Declaration, p binding.Parameter) bool {
	switch d.Name {
	case "cblas_snrm2", "cblas_dnrm2", "cblas_scnrm2", "cblas_dznrm2",
		"cblas_sasum", "cblas_dasum", "cblas_scasum", "cblas_dzasum":
	default:
		return true
	}

	if d.CParameters[len(d.CParameters)-1] != p.Parameter {
		return false // Come back later.
	}

	fmt.Fprint(buf, `	if incX < 0 {
		return 0
	}
	if incX > 0 && (n-1)*incX >= len(x) {
		panic("blas: x index out of range")
	}
`)
	return true
}

func rkShape(buf *bytes.Buffer, d binding.Declaration, p binding.Parameter) bool {
	switch d.Name {
	case "cblas_ssyrk", "cblas_dsyrk", "cblas_csyrk", "cblas_zsyrk",
		"cblas_ssyr2k", "cblas_dsyr2k", "cblas_csyr2k", "cblas_zsyr2k",
		"cblas_cherk", "cblas_zherk", "cblas_cher2k", "cblas_zher2k":
	default:
		return true
	}

	if d.CParameters[len(d.CParameters)-1] != p.Parameter {
		return false // Come back later.
	}

	fmt.Fprint(buf, `	var row, col int
	if t == blas.NoTrans {
		row, col = n, k
	} else {
		row, col = k, n
	}
`)
	has := make(map[string]bool)
	for _, p := range d.Parameters() {
		if p.Kind() != cc.Ptr {
			continue
		}
		has[shorten(binding.LowerCaseFirst(p.Name()))] = true
	}
	for _, label := range []string{"a", "b"} {
		if has[label] {
			fmt.Fprintf(buf, `	if ld%[1]s*(row-1)+col > len(%[1]s) || ld%[1]s < max(1, col) {
		panic("blas: index of %[1]s out of range")
	}
`, label)
		}
	}
	if has["c"] {
		fmt.Fprint(buf, `	if ldc*(n-1)+n > len(c) || ldc < max(1, n) {
		panic("blas: index of c out of range")
	}
`)
	}

	return true
}

func scalShape(buf *bytes.Buffer, d binding.Declaration, p binding.Parameter) bool {
	switch d.Name {
	case "cblas_sscal", "cblas_dscal", "cblas_cscal", "cblas_zscal", "cblas_csscal":
	default:
		return true
	}

	if d.CParameters[len(d.CParameters)-1] != p.Parameter {
		return false // Come back later.
	}

	fmt.Fprint(buf, `	if incX < 0 {
		return
	}
	if incX > 0 && (n-1)*incX >= len(x) {
		panic("blas: x index out of range")
	}
`)
	return true
}

func shape(buf *bytes.Buffer, _ binding.Declaration, p binding.Parameter) bool {
	switch n := binding.LowerCaseFirst(p.Name()); n {
	case "m", "n", "k", "kL", "kU":
		fmt.Fprintf(buf, `	if %[1]s < 0 {
		panic("blas: %[1]s < 0")
	}
`, n)
		return false
	}
	return false
}

func side(buf *bytes.Buffer, _ binding.Declaration, p binding.Parameter) bool {
	if p.Name() != "Side" {
		return false
	}
	fmt.Fprint(buf, `	if s != blas.Left && s != blas.Right {
		panic("blas: illegal side")
	}
`)
	return true
}

func sidedShape(buf *bytes.Buffer, d binding.Declaration, p binding.Parameter) bool {
	var hasS, hasA, hasB, hasC bool
	for _, p := range d.Parameters() {
		switch shorten(binding.LowerCaseFirst(p.Name())) {
		case "s":
			hasS = true
		case "a":
			hasA = true
		case "b":
			hasB = true
		case "c":
			hasC = true
		}
	}
	if !hasS {
		return true
	}

	if d.CParameters[len(d.CParameters)-1] != p.Parameter {
		return false // Come back later.
	}

	if hasA && hasB {
		fmt.Fprint(buf, `	var k int
	if s == blas.Left {
		k = m
	} else {
		k = n
	}
	if lda*(k-1)+k > len(a) || lda < max(1, k) {
		panic("blas: index of a out of range")
	}
	if ldb*(m-1)+n > len(b) || ldb < max(1, n) {
		panic("blas: index of b out of range")
	}
`)
	} else {
		return true
	}
	if hasC {
		fmt.Fprint(buf, `	if ldc*(m-1)+n > len(c) || ldc < max(1, n) {
		panic("blas: index of c out of range")
	}
`)
	}

	return true
}

func trans(buf *bytes.Buffer, d binding.Declaration, p binding.Parameter) bool {
	switch n := shorten(binding.LowerCaseFirst(p.Name())); n {
	case "t", "tA", "tB":
		switch {
		case strings.HasPrefix(d.Name, "cblas_ch"), strings.HasPrefix(d.Name, "cblas_zh"):
			fmt.Fprintf(buf, `	if %[1]s != blas.NoTrans && %[1]s != blas.ConjTrans {
		panic("blas: illegal transpose")
	}
`, n)
		case strings.HasPrefix(d.Name, "cblas_cs"), strings.HasPrefix(d.Name, "cblas_zs"):
			fmt.Fprintf(buf, `	if %[1]s != blas.NoTrans && %[1]s != blas.Trans {
		panic("blas: illegal transpose")
	}
`, n)
		default:
			fmt.Fprintf(buf, `	if %[1]s != blas.NoTrans && %[1]s != blas.Trans && %[1]s != blas.ConjTrans {
		panic("blas: illegal transpose")
	}
`, n)
		}
	}
	return false
}

func uplo(buf *bytes.Buffer, _ binding.Declaration, p binding.Parameter) bool {
	if p.Name() != "Uplo" {
		return false
	}
	fmt.Fprint(buf, `	if ul != blas.Upper && ul != blas.Lower {
		panic("blas: illegal triangle")
	}
`)
	return true
}

func vectorShape(buf *bytes.Buffer, d binding.Declaration, p binding.Parameter) bool {
	switch d.Name {
	case "cblas_sgbmv", "cblas_dgbmv", "cblas_cgbmv", "cblas_zgbmv",
		"cblas_sgemv", "cblas_dgemv", "cblas_cgemv", "cblas_zgemv",
		"cblas_sscal", "cblas_dscal", "cblas_cscal", "cblas_zscal", "cblas_csscal",
		"cblas_isamax", "cblas_idamax", "cblas_icamax", "cblas_izamax",
		"cblas_snrm2", "cblas_dnrm2", "cblas_scnrm2", "cblas_dznrm2",
		"cblas_sasum", "cblas_dasum", "cblas_scasum", "cblas_dzasum":
		return true
	}

	var hasN, hasM, hasIncX, hasIncY bool
	for _, p := range d.Parameters() {
		switch shorten(binding.LowerCaseFirst(p.Name())) {
		case "n":
			hasN = true
		case "m":
			hasM = true
		case "incX":
			hasIncX = true
		case "incY":
			hasIncY = true
		}
	}
	if !hasN && !hasM {
		return true
	}

	if d.CParameters[len(d.CParameters)-1] != p.Parameter {
		return false // Come back later.
	}

	var label string
	if hasM {
		label = "m"
	} else {
		label = "n"
	}
	if hasIncX {
		fmt.Fprintf(buf, `	if (incX > 0 && (%[1]s-1)*incX >= len(x)) || (incX < 0 && (1-%[1]s)*incX >= len(x)) {
		panic("blas: x index out of range")
	}
`, label)
	}
	if hasIncY {
		fmt.Fprint(buf, `	if (incY > 0 && (n-1)*incY >= len(y)) || (incY < 0 && (1-n)*incY >= len(y)) {
		panic("blas: y index out of range")
	}
`)
	}
	return true
}

func zeroInc(buf *bytes.Buffer, _ binding.Declaration, p binding.Parameter) bool {
	switch n := binding.LowerCaseFirst(p.Name()); n {
	case "incX":
		fmt.Fprintf(buf, `	if incX == 0 {
		panic("blas: zero x index increment")
	}
`)
	case "incY":
		fmt.Fprintf(buf, `	if incY == 0 {
		panic("blas: zero y index increment")
	}
`)
		return true
	}
	return false
}

func othersShape(buf *bytes.Buffer, d binding.Declaration, p binding.Parameter) bool {
	switch d.Name {
	case "cblas_sgemm", "cblas_dgemm", "cblas_cgemm", "cblas_zgemm",
		"cblas_ssyrk", "cblas_dsyrk", "cblas_csyrk", "cblas_zsyrk",
		"cblas_ssyr2k", "cblas_dsyr2k", "cblas_csyr2k", "cblas_zsyr2k",
		"cblas_cherk", "cblas_zherk", "cblas_cher2k", "cblas_zher2k":
		return true
	}

	has := make(map[string]bool)
	for _, p := range d.Parameters() {
		has[shorten(binding.LowerCaseFirst(p.Name()))] = true
	}
	if !has["a"] || has["s"] {
		return true
	}

	if d.CParameters[len(d.CParameters)-1] != p.Parameter {
		return false // Come back later.
	}

	switch {
	case has["kL"] && has["kU"]:
		fmt.Fprintf(buf, `	if lda*(m-1)+kL+kU+1 > len(a) || lda < kL+kU+1 {
		panic("blas: index of a out of range")
	}
`)
	case has["m"]:
		fmt.Fprintf(buf, `	if lda*(m-1)+n > len(a) || lda < max(1, n) {
		panic("blas: index of a out of range")
	}
`)
	case has["k"]:
		fmt.Fprintf(buf, `	if lda*(n-1)+k+1 > len(a) || lda < k+1 {
		panic("blas: index of a out of range")
	}
`)
	default:
		fmt.Fprintf(buf, `	if lda*(n-1)+n > len(a) || lda < max(1, n) {
		panic("blas: index of a out of range")
	}
`)
	}

	return true
}

const handwritten = `// Do not manually edit this file. It was created by the generate_blas.go from {{.}}.

// Copyright ©2014 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package cgo

/*
#cgo CFLAGS: -g -O2
#include "{{.}}"
*/
import "C"

import (
	"unsafe"

	"github.com/gonum/blas"
)

// Type check assertions:
var (
	_ blas.Float32    = Implementation{}
	_ blas.Float64    = Implementation{}
	_ blas.Complex64  = Implementation{}
	_ blas.Complex128 = Implementation{}
)

// Type order is used to specify the matrix storage format. We still interact with
// an API that allows client calls to specify order, so this is here to document that fact.
type order int

const (
	rowMajor order = 101 + iota
)

func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

type Implementation struct{}

// Special cases...

type srotmParams struct {
	flag float32
	h    [4]float32
}

type drotmParams struct {
	flag float64
	h    [4]float64
}

func (Implementation) Srotg(a float32, b float32) (c float32, s float32, r float32, z float32) {
	C.cblas_srotg((*C.float)(&a), (*C.float)(&b), (*C.float)(&c), (*C.float)(&s))
	return c, s, a, b
}
func (Implementation) Srotmg(d1 float32, d2 float32, b1 float32, b2 float32) (p blas.SrotmParams, rd1 float32, rd2 float32, rb1 float32) {
	var pi srotmParams
	C.cblas_srotmg((*C.float)(&d1), (*C.float)(&d2), (*C.float)(&b1), C.float(b2), (*C.float)(unsafe.Pointer(&pi)))
	return blas.SrotmParams{Flag: blas.Flag(pi.flag), H: pi.h}, d1, d2, b1
}
func (Implementation) Srotm(n int, x []float32, incX int, y []float32, incY int, p blas.SrotmParams) {
	if n < 0 {
		panic("blas: n < 0")
	}
	if incX == 0 {
		panic("blas: zero x index increment")
	}
	if incY == 0 {
		panic("blas: zero y index increment")
	}
	if (incX > 0 && (n-1)*incX >= len(x)) || (incX < 0 && (1-n)*incX >= len(x)) {
		panic("blas: x index out of range")
	}
	if (incY > 0 && (n-1)*incY >= len(y)) || (incY < 0 && (1-n)*incY >= len(y)) {
		panic("blas: y index out of range")
	}
	if p.Flag < blas.Identity || p.Flag > blas.Diagonal {
		panic("blas: illegal blas.Flag value")
	}
	if n == 0 {
		return
	}
	pi := srotmParams{
		flag: float32(p.Flag),
		h:    p.H,
	}
	C.cblas_srotm(C.int(n), (*C.float)(&x[0]), C.int(incX), (*C.float)(&y[0]), C.int(incY), (*C.float)(unsafe.Pointer(&pi)))
}
func (Implementation) Drotg(a float64, b float64) (c float64, s float64, r float64, z float64) {
	C.cblas_drotg((*C.double)(&a), (*C.double)(&b), (*C.double)(&c), (*C.double)(&s))
	return c, s, a, b
}
func (Implementation) Drotmg(d1 float64, d2 float64, b1 float64, b2 float64) (p blas.DrotmParams, rd1 float64, rd2 float64, rb1 float64) {
	var pi drotmParams
	C.cblas_drotmg((*C.double)(&d1), (*C.double)(&d2), (*C.double)(&b1), C.double(b2), (*C.double)(unsafe.Pointer(&pi)))
	return blas.DrotmParams{Flag: blas.Flag(pi.flag), H: pi.h}, d1, d2, b1
}
func (Implementation) Drotm(n int, x []float64, incX int, y []float64, incY int, p blas.DrotmParams) {
	if n < 0 {
		panic("blas: n < 0")
	}
	if incX == 0 {
		panic("blas: zero x index increment")
	}
	if incY == 0 {
		panic("blas: zero y index increment")
	}
	if (incX > 0 && (n-1)*incX >= len(x)) || (incX < 0 && (1-n)*incX >= len(x)) {
		panic("blas: x index out of range")
	}
	if (incY > 0 && (n-1)*incY >= len(y)) || (incY < 0 && (1-n)*incY >= len(y)) {
		panic("blas: y index out of range")
	}
	if p.Flag < blas.Identity || p.Flag > blas.Diagonal {
		panic("blas: illegal blas.Flag value")
	}
	if n == 0 {
		return
	}
	pi := drotmParams{
		flag: float64(p.Flag),
		h:    p.H,
	}
	C.cblas_drotm(C.int(n), (*C.double)(&x[0]), C.int(incX), (*C.double)(&y[0]), C.int(incY), (*C.double)(unsafe.Pointer(&pi)))
}
func (Implementation) Cdotu(n int, x []complex64, incX int, y []complex64, incY int) (dotu complex64) {
	if n < 0 {
		panic("blas: n < 0")
	}
	if incX == 0 {
		panic("blas: zero x index increment")
	}
	if incY == 0 {
		panic("blas: zero y index increment")
	}
	if (incX > 0 && (n-1)*incX >= len(x)) || (incX < 0 && (1-n)*incX >= len(x)) {
		panic("blas: x index out of range")
	}
	if (incY > 0 && (n-1)*incY >= len(y)) || (incY < 0 && (1-n)*incY >= len(y)) {
		panic("blas: y index out of range")
	}
	if n == 0 {
		return 0
	}
	C.cblas_cdotu_sub(C.int(n), unsafe.Pointer(&x[0]), C.int(incX), unsafe.Pointer(&y[0]), C.int(incY), unsafe.Pointer(&dotu))
	return dotu
}
func (Implementation) Cdotc(n int, x []complex64, incX int, y []complex64, incY int) (dotc complex64) {
	if n < 0 {
		panic("blas: n < 0")
	}
	if incX == 0 {
		panic("blas: zero x index increment")
	}
	if incY == 0 {
		panic("blas: zero y index increment")
	}
	if (incX > 0 && (n-1)*incX >= len(x)) || (incX < 0 && (1-n)*incX >= len(x)) {
		panic("blas: x index out of range")
	}
	if (incY > 0 && (n-1)*incY >= len(y)) || (incY < 0 && (1-n)*incY >= len(y)) {
		panic("blas: y index out of range")
	}
	if n == 0 {
		return 0
	}
	C.cblas_cdotc_sub(C.int(n), unsafe.Pointer(&x[0]), C.int(incX), unsafe.Pointer(&y[0]), C.int(incY), unsafe.Pointer(&dotc))
	return dotc
}
func (Implementation) Zdotu(n int, x []complex128, incX int, y []complex128, incY int) (dotu complex128) {
	if n < 0 {
		panic("blas: n < 0")
	}
	if incX == 0 {
		panic("blas: zero x index increment")
	}
	if incY == 0 {
		panic("blas: zero y index increment")
	}
	if (incX > 0 && (n-1)*incX >= len(x)) || (incX < 0 && (1-n)*incX >= len(x)) {
		panic("blas: x index out of range")
	}
	if (incY > 0 && (n-1)*incY >= len(y)) || (incY < 0 && (1-n)*incY >= len(y)) {
		panic("blas: y index out of range")
	}
	if n == 0 {
		return 0
	}
	C.cblas_zdotu_sub(C.int(n), unsafe.Pointer(&x[0]), C.int(incX), unsafe.Pointer(&y[0]), C.int(incY), unsafe.Pointer(&dotu))
	return dotu
}
func (Implementation) Zdotc(n int, x []complex128, incX int, y []complex128, incY int) (dotc complex128) {
	if n < 0 {
		panic("blas: n < 0")
	}
	if incX == 0 {
		panic("blas: zero x index increment")
	}
	if incY == 0 {
		panic("blas: zero y index increment")
	}
	if (incX > 0 && (n-1)*incX >= len(x)) || (incX < 0 && (1-n)*incX >= len(x)) {
		panic("blas: x index out of range")
	}
	if (incY > 0 && (n-1)*incY >= len(y)) || (incY < 0 && (1-n)*incY >= len(y)) {
		panic("blas: y index out of range")
	}
	if n == 0 {
		return 0
	}
	C.cblas_zdotc_sub(C.int(n), unsafe.Pointer(&x[0]), C.int(incX), unsafe.Pointer(&y[0]), C.int(incY), unsafe.Pointer(&dotc))
	return dotc
}

// Generated cases ...

`
