package entropy

import (
	"fmt"
	"math"
)

func MarkovTests(data []byte, verbose bool) float64 {
	// 将data转换为0,1比特串表示
	epsilon := make([]byte, 0, len(data)*8)
	for _, b := range data {
		epsilon = append(epsilon, (b>>7)&1)
		epsilon = append(epsilon, (b>>6)&1)
		epsilon = append(epsilon, (b>>5)&1)
		epsilon = append(epsilon, (b>>4)&1)
		epsilon = append(epsilon, (b>>3)&1)
		epsilon = append(epsilon, (b>>2)&1)
		epsilon = append(epsilon, (b>>1)&1)
		epsilon = append(epsilon, (b>>0)&1)
	}

	// double markov_test(byte* data, long len, const int verbose, const char *label){
	var i, C_0, C_1, C_00, C_10 int64
	var H_min, tmp_min_entropy, P_0, P_1, P_00, P_01, P_10, P_11, entEst float64

	C_0 = 0
	C_00 = 0
	C_10 = 0
	dataLen := int64(len(epsilon))
	// get counts for unconditional and transition probabilities
	for i = 0; i < dataLen-1; i++ {
		if epsilon[i] == 0 {
			C_0++
			if epsilon[i+1] == 0 {
				C_00++
			}
		} else if epsilon[i+1] == 0 {
			C_10++
		}
	}

	//C_0 is now  the number of 0 bits from S[0] to S[len-2]

	C_1 = dataLen - 1 - C_0 //C_1 is the number of 1 bits from S[0] to S[len-2]

	//Note that P_X1 = C_X1 / C_X = (C_X - C_X0)/C_X = 1.0 - C_X0/C_X = 1.0 - P_X0
	if C_0 > 0 {
		P_00 = float64(C_00) / float64(C_0)
		P_01 = 1.0 - P_00
	} else {
		P_00 = 0.0
		P_01 = 0.0
	}

	if C_1 > 0 {
		P_10 = float64(C_10) / float64(C_1)
		P_11 = 1.0 - P_10
	} else {
		P_10 = 0.0
		P_11 = 0.0
	}

	// account for the last symbol
	if epsilon[dataLen-1] == 0 {
		C_0++
	}
	//C_0 is now  the number of 0 bits from S[0] to S[len-1]

	P_0 = float64(C_0) / float64(dataLen)
	P_1 = 1.0 - P_0

	if verbose {
		fmt.Printf("P_0 = %.17g\n", P_0)
		fmt.Printf("P_1 = %.17g\n", P_1)
		fmt.Printf("P_{0,0} = %.17g\n", P_00)
		fmt.Printf("P_{0,1} = %.17g\n", P_01)
		fmt.Printf("P_{1,0} = %.17g\n", P_10)
		fmt.Printf("P_{1,1} = %.17g\n", P_11)
	}

	H_min = 128.0

	//In the next block, note that if P_0X > 0.0, then P_0 > 0.0
	//and similarly if P_1X > 0.0, then P_1 > 0.0

	// Sequence 00...0
	if P_00 > 0.0 {
		tmp_min_entropy = -math.Log2(P_0) - 127.0*math.Log2(P_00)
		if tmp_min_entropy < H_min {
			H_min = tmp_min_entropy
		}
	}

	// Sequence 0101...01
	if (P_01 > 0.0) && (P_10 > 0.0) {
		tmp_min_entropy = -math.Log2(P_0) - 64.0*math.Log2(P_01) - 63.0*math.Log2(P_10)
		if tmp_min_entropy < H_min {
			H_min = tmp_min_entropy
		}
	}

	// Sequence 011...1
	if (P_01 > 0.0) && (P_11 > 0.0) {
		tmp_min_entropy = -math.Log2(P_0) - math.Log2(P_01) - 126.0*math.Log2(P_11)
		if tmp_min_entropy < H_min {
			H_min = tmp_min_entropy
		}
	}

	// Sequence 100...0
	if (P_10 > 0.0) && (P_00 > 0.0) {
		tmp_min_entropy = -math.Log2(P_1) - math.Log2(P_10) - 126.0*math.Log2(P_00)
		if tmp_min_entropy < H_min {
			H_min = tmp_min_entropy
		}
	}

	// Sequence 1010...10
	if (P_10 > 0.0) && (P_01 > 0.0) {
		tmp_min_entropy = -math.Log2(P_1) - 64.0*math.Log2(P_10) - 63.0*math.Log2(P_01)
		if tmp_min_entropy < H_min {
			H_min = tmp_min_entropy
		}
	}

	// Sequence 11...1
	if P_11 > 0.0 {
		tmp_min_entropy = -math.Log2(P_1) - 127.0*math.Log2(P_11)
		if tmp_min_entropy < H_min {
			H_min = tmp_min_entropy
		}
	}

	entEst = math.Min(H_min/128.0, 1.0)

	if verbose {
		fmt.Printf("p_max = %.17g\n", math.Pow(2.0, -H_min))
		fmt.Printf("min entropy = %.17g\n", entEst)
	}

	return entEst
}
