func NewCuckooSolver(edgeBits, sipHash int) *OrigCuckoo {
	return &OrigCuckoo{
		EdgeBits: edgeBits,
		SipHash:  sipHash,
	}
}
NewCuckooSolver returns a new OrigCuckoo type, which
implements the Solver interface.
package cuckoo
import (
	"bufio"
	"bytes"
	"encoding/binary"
	"encoding/hex"
	"fmt"
	"github.com/Qitmeer/qitmeer-lib/crypto/cuckoo"
	"github.com/soteria-dag/soterd/chaincfg/chainhash"
	"github.com/soteria-dag/soterd/soterutil"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"runtime"
	"strings"
)
const (
	DefaultEdgeBits = 19
	DefaultSipHash = 1
	DefaultGPUEdgeBits = 29
)
type Solver interface {
	CheckRequirements() error
	Solve([]byte, uint32) ([][]uint32, error)
	Verify([]byte, uint32, []uint32) error
}
type OrigCuckoo struct {
	EdgeBits int
	SipHash int
}
type GPUCuckoo struct {
	EdgeBits int
	SipHash int
}
type QitmeerCuckaroo struct {
	c *cuckoo.Cuckoo
}
func maxInt(a, b int) int {
	if a > b {
		return a
	}
	return b
}
func minInt(a, b int) int {
	if a < b {
		return a
	}
	return b
}
func dumpToFile(data []byte, pattern string) (*os.File, error) {
	fh, err := ioutil.TempFile("", pattern)
	if err != nil {
		return nil, err
	}
	_, err = fh.Write(data)
	if err != nil {
		return nil, err
	}
	err = fh.Close()
	if err != nil {
		return nil, err
	}
	return fh, nil
}
func callSolver(name string, args ...string) ([][]uint32, error) {
	cmd := exec.Command(name, args...)
	output, err := cmd.Output()
	if err != nil {
		return [][]uint32{}, err
	}
	var solutions = make([][]uint32, 0)
	scanner := bufio.NewScanner(bytes.NewReader(output))
	for scanner.Scan() {
		parts := strings.SplitN(scanner.Text(), " ", 2)
		if len(parts) != 2 {
			continue
		} else if parts[0] != "Solution" {
			continue
		}
		cycle := make([]uint32, 0)
		for i, h := range strings.Split(parts[1], " ") {
			nonceBytes, err := hex.DecodeString(fmt.Sprintf("%08s", h))
			if err != nil {
				return [][]uint32{}, fmt.Errorf("Failed to parse solution %d cycle nonce %d value %s: %s",
					len(solutions), i, h, err)
			}
			nonce := binary.BigEndian.Uint32(nonceBytes)
			cycle = append(cycle, nonce)
		}
		if len(cycle) > 0 {
			solutions = append(solutions, cycle)
		}
	}
	return solutions, nil
}
func CallCuckooGPUSolver(name string, header []byte, nonce uint32) ([][]uint32, error) {
	pattern := fmt.Sprintf("cuckoogpusolve-%d-*.bin", nonce)
	fh, err := dumpToFile(header, pattern)
	defer func() {
		_ = os.Remove(fh.Name())
	}()
	if err != nil {
		return [][]uint32{}, err
	}
	args := []string{
		"-i", fh.Name(),
		"-n", fmt.Sprintf("%d", nonce),
	}
	return callSolver(name, args...)
}
func CallCuckooSolver(name string, header []byte, nonce uint32) ([][]uint32, error) {
	threadCount := minInt(maxInt(runtime.NumCPU(), 1), 4)
	pattern := fmt.Sprintf("cuckoosolve-%d-*.bin", nonce)
	fh, err := dumpToFile(header, pattern)
	defer func() {
		_ = os.Remove(fh.Name())
	}()
	if err != nil {
		return [][]uint32{}, err
	}
	args := []string{
		"-i", fh.Name(),
		"-n", fmt.Sprintf("%d", nonce),
		"-t", fmt.Sprintf("%d", threadCount),
	}
	return callSolver(name, args...)
}
func CallCuckooVerify(name string, header []byte, nonce uint32, cycle []uint32) error {
	fh, err := dumpToFile(header, "cuckooverify-*.bin")
	defer func() {
		_ = os.Remove(fh.Name())
	}()
	if err != nil {
		return err
	}
	cmd := exec.Command(name,
		"-i", fh.Name(),
		"-n", fmt.Sprintf("%d", nonce))
	stdin, err := cmd.StdinPipe()
	if err != nil {
		return err
	}
	var cycleHex = make([]string, len(cycle))
	for i, nonce := range cycle {
		b := make([]byte, 4)
		binary.BigEndian.PutUint32(b, nonce)
		cycleHex[i] = strings.TrimLeft(hex.EncodeToString(b), "0")
	}
	solution := fmt.Sprintf("Solution %s\n", strings.Join(cycleHex, " "))
	_, err = io.WriteString(stdin, solution)
	if err != nil {
		_ = stdin.Close()
		return err
	}
	err = stdin.Close()
	if err != nil {
		return err
	}
	output, err := cmd.CombinedOutput()
	if err != nil {
		return err
	}
	scanner := bufio.NewScanner(bytes.NewReader(output))
	for scanner.Scan() {
		line := scanner.Text()
		i := strings.Index(line, "Verified with cyclehash")
		if i != -1 {
			return nil
		}
		i = strings.Index(line, "FAILED due to")
		if i != -1 {
			return fmt.Errorf(line)
		}
	}
	return fmt.Errorf("Verification failed without a specific error message")
}
func DefaultSolver() Solver {
	var solver Solver
	s := NewCuckarooSolver()
	solver = s
	return solver
}