//go:build ignore
// +build ignore

package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"math"
	"os"
	"path"
	"runtime"
	"sync"

	"xdx.jelly/xgcl/grand/statistics"
)

var suit = []struct {
	name string
	fn   func([]byte) bool
}{
	{
		name: "单比特频数检测     ",
		fn:   statistics.Frequency,
	},

	{
		name: "块内频数检测       ",
		fn:   statistics.BlockFrequency,
	},
	{
		name: "扑克检测, m=4      ",
		fn:   statistics.Poker4,
	},
	{
		name: "扑克检测, m=8      ",
		fn:   statistics.Poker8,
	},
	{
		name: "重叠子序列检测, m=2",
		fn:   statistics.Serial2,
	},
	{
		name: "重叠子序列检测, m=5",
		fn:   statistics.Serial5,
	},
	{
		name: "游程总数检测       ",
		fn:   statistics.Runs,
	},
	{
		name: "游程分布检测       ",
		fn:   statistics.RunsDistribution,
	},
	{
		name: "二元推导检测, k=3  ",
		fn:   statistics.BinaryDerivative3,
	},
	{
		name: "二元推导检测, k=7  ",
		fn:   statistics.BinaryDerivative7,
	},
	{
		name: "自相关检测, d=1    ",
		fn:   statistics.Autocorrelation1,
	},
	{
		name: "自相关检测, d=2    ",
		fn:   statistics.Autocorrelation2,
	},
	{
		name: "自相关检测, d=8    ",
		fn:   statistics.Autocorrelation8,
	},
	{
		name: "自相关检测, d=16   ",
		fn:   statistics.Autocorrelation16,
	},
	{
		name: "矩阵秩检测         ",
		fn:   statistics.Rank,
	},
	{
		name: "累加和检测         ",
		fn:   statistics.CumulativeSums,
	},
	{
		name: "近似熵检测         ",
		fn:   statistics.ApproximateEntropy,
	},
	{
		name: "线性复杂度检测     ",
		fn:   statistics.LinearComplexity,
	},
	{
		name: "Maurer通用统计检测 ",
		fn:   statistics.Universal,
	},
	{
		name: "离散傅立叶检测     ",
		fn:   statistics.DiscreteFourierTransform,
	},
}

type payload struct {
	e  []byte
	fn func([]byte) bool
}

func worker(in chan *payload, out chan bool, done chan struct{}) {
	for {
		select {
		case p := <-in:
			out <- p.fn(p.e) // fn may panic
		case <-done:
			return
		}
	}
}

func main() {
	dataDir := flag.String("d", "./data", "The directory of random data, default is \"./data\"")
	flag.Parse()
	fmt.Println("=======================================================")
	fmt.Println("    测试文件夹：", *dataDir)
	epsilons := make([][]byte, 0)

	fs, err := ioutil.ReadDir(*dataDir)
	if err != nil {
		fmt.Println("文件夹读取错误: ", err)
		return
	}

	for _, file := range fs {
		if !file.IsDir() {
			epsilon, err := readFile(path.Join(*dataDir, file.Name()))
			if err != nil {
				fmt.Println("文件读取错误: ", err)
				return
			}
			epsilons = append(epsilons, epsilon)
		}
	}
	fmt.Printf("    共有随机数文件%d个，每个文件%d比特\n", len(epsilons), len(epsilons[0]))
	bound := lowerBound(len(epsilons))
	fmt.Printf("    每组检测通过的样本数必须不小于: %d\n", bound)
	fmt.Println("=======================================================")

	threads := runtime.NumCPU()
	in := make(chan *payload, threads)
	out := make(chan bool, threads)
	done := make(chan struct{})

	for i := 0; i < threads; i++ {
		go worker(in, out, done)
	}

	// for {
	for _, t := range suit {
		// for _, t := range suit[len(suit)-1:] {
		sum := 0
		var wg sync.WaitGroup
		wg.Add(len(epsilons))
		go func() {
			for _, e := range epsilons {
				in <- &payload{
					e:  e,
					fn: t.fn,
				}
			}
		}()

		go func() {
			for i := 0; i < len(epsilons); i++ {
				b := <-out
				if b {
					sum += 1
				}
				wg.Done()
			}
		}()

		wg.Wait()

		if sum < bound {
			fmt.Printf("[x] %s 【失败】: %d / %d\n", t.name, sum, len(epsilons))
		} else {
			fmt.Printf("[o] %s 【通过】: %d / %d\n", t.name, sum, len(epsilons))
		}
	}
	// }
}

// readFile read from fileName and convert each bit to byte
func readFile(fileName string) ([]byte, error) {
	content, err := os.ReadFile(fileName)
	if err != nil {
		return nil, err
	}
	bits := make([]byte, 0, len(content)*8)
	for _, c := range content {
		bits = append(bits, (c>>7)&1)
		bits = append(bits, (c>>6)&1)
		bits = append(bits, (c>>5)&1)
		bits = append(bits, (c>>4)&1)
		bits = append(bits, (c>>3)&1)
		bits = append(bits, (c>>2)&1)
		bits = append(bits, (c>>1)&1)
		bits = append(bits, (c>>0)&1)
	}
	return bits, nil
}

const alpha float64 = 0.01

func lowerBound(samples int) int {
	return int(math.Ceil((1 - alpha - 3*math.Sqrt((alpha*(1-alpha))/float64(samples))) * float64(samples)))
}
