def serial(generator, n_bits, m=16, misc=None): """Serial Test. Test purpose as described in [NIST10, section 2.11]: "The focus of this test is the frequency of all possible overlapping m-bit patterns across the entire sequence. The purpose of this test is to determine whether the number of occurrences of the 2m m-bit overlapping patterns is approximately the same as would be expected for a random sequence. Random sequences have uniformity; that is, every m-bit pattern has the same chance of appearing as every other m-bit pattern. Note that for m = 1, the Serial test is equivalent to the Frequency test of Section 2.1." """ if m < 1: raise ValueError("Parameter m must be an integer greater than zero") if m >= int(log2(n_bits)) - 2: print("Warning: Parameter m should be less than [log2(n)]-2", file=stderr) sequence_start = [generator.random_bit() for _ in range(m - 1)] window = deque(sequence_start, maxlen=m) vm0 = [0] * (2**m) vm1 = [0] * (2**(m - 1)) if m >= 2 else [] vm2 = [0] * (2**(m - 2)) if m >= 3 else [] for i in range(m - 1, n_bits + (m - 1)): bit = generator.random_bit() if i < n_bits else sequence_start[i - n_bits] window.append(bit) # when full with m items, left item is discarded vm0[concat_chunks(window, bits=1)] += 1 if m >= 2: vm1[concat_chunks(islice(window, 0, m - 1), bits=1)] += 1 if m >= 3: vm2[concat_chunks(islice(window, 0, m - 2), bits=1)] += 1 psi2m0 = (2**m) / n_bits * sum(map(lambda x: x**2, vm0)) - n_bits psi2m1 = (2**(m - 1)) / n_bits * sum(map(lambda x: x**2, vm1)) - n_bits if vm1 else 0 psi2m2 = (2**(m - 2)) / n_bits * sum(map(lambda x: x**2, vm2)) - n_bits if vm2 else 0 delta_psi2m = psi2m0 - psi2m1 delta2_psi2m = psi2m0 - 2 * psi2m1 + psi2m2 p_value1 = igamc(2**(m - 2), delta_psi2m / 2) p_value2 = igamc(2**(m - 3), delta2_psi2m / 2) if type(misc) == dict: misc.update(vm0=vm0, vm1=vm1, vm2=vm2, psi2m0=psi2m0, psi2m1=psi2m1, psi2m2=psi2m2, delta_psi2m=delta_psi2m, delta2_psi2m=delta2_psi2m) return p_value1, p_value2
def block_frequency(generator, n_bits, m=None, misc=None): """Frequency Test within a Block. Test purpose as described in [NIST10, section 2.2]: "The focus of the test is the proportion of ones within M-bit blocks. The purpose of this test is to determine whether the frequency of ones in an M-bit block is approximately M/2, as would be expected under an assumption of randomness. For block size M=1, this test degenerates to test 1, the Frequency (Monobit) test." """ if n_bits < 100: print("Warning: Sequence should be at least 100 bits long", file=stderr) if m: if m < 20 or m <= 0.01 * n_bits: print("Warning: Parameter m should satisfy m >= 20 and m > .01*n", file=stderr) else: m = max(20, int(0.01 * n_bits) + 1) n_blocks = n_bits // m assert (n_bits >= m * n_blocks) if n_blocks >= 100: print("Warning: Test should have less than 100 blocks", file=stderr) pi = [] for i in range(n_blocks): pi.append(sum([generator.random_bit() for _ in range(m)]) / m) chi2_obs = 4 * m * sum(map(lambda p: (p - 0.5)**2, pi)) p_value = igamc(n_blocks / 2, chi2_obs / 2) if type(misc) is dict: misc.update(m=m, n_blocks=n_blocks, pi=pi, chi2=chi2_obs) return p_value
def test(bits, blocklen=None, template=None): n = len(bits) M = blocklen if blocklen else n // 8 if M <= 0.01 * n: return (-1, -1, False) N = n // M blocks = [bits[i * M:(i + 1) * M] for i in range(N)] B = template if template else random.choices(templates[8]) m = len(B) W = [] for block in blocks: position = 0 count = 0 while position < (M - m): if block[position:position + m] == B: position += m count += 1 else: position += 1 W.append(count) μ = Fraction(M - m + 1, 2**m) σ2 = M * (Fraction(1, 2**m) - Fraction(2 * m - 1, 2**(2 * m))) χ2_obs = sum([(W[j] - μ)**2 for j in range(N)]) / σ2 P_value = igamc(N / 2.0, χ2_obs / 2.0) level = 0.01 return (P_value, level, P_value >= level)
def longest_run_of_ones(generator, n_bits, m=None, misc=None): """Test for the Longest Run of Ones in a Block. Test purpose as described in [NIST10, section 2.4]: "The focus of the test is the longest run of ones within M-bit blocks. The purpose of this test is to determine whether the length of the longest run of ones within the tested sequence is consistent with the length of the longest run of ones that would be expected in a random sequence. Note that an irregularity in the expected length of the longest run of ones implies that there is also an irregularity in the expected length of the longest run of zeroes. Therefore, only a test for ones is necessary." """ if not m: if n_bits >= 750000: m = 10000 elif n_bits >= 6272: m = 128 elif n_bits >= 128: m = 8 else: raise ValueError("Sequence must be at least 128 bits long") elif m not in (8, 128, 10000): raise ValueError("Parameter m must be 8, 128 or 10000") if m == 8: min_run_length, max_run_length = 1, 4 pi = (0.2148, 0.3672, 0.2305, 0.1875) elif m == 128: min_run_length, max_run_length = 4, 9 pi = (0.1174, 0.2430, 0.2493, 0.1752, 0.1027, 0.1124) elif m == 10000: min_run_length = 10 max_run_length = 16 pi = (0.0882, 0.2092, 0.2483, 0.1933, 0.1208, 0.0675, 0.0727) else: raise Exception("This shouldn't happen") k = max_run_length - min_run_length n_blocks = n_bits // m v = [0] * (k + 1) for _ in range(n_blocks): block = [generator.random_bit() for _ in range(m)] runs_of_ones = [list(group) for k, group in groupby(block, bool) if k] if runs_of_ones: longest_run_length = max(map(len, runs_of_ones)) # if run length is out of bounds, set it to the nearest bound # then the run length is the index (minus min_run_length because zero-indexed) index = min(max(longest_run_length, min_run_length), max_run_length) - min_run_length v[index] += 1 chi2_obs = sum( map(lambda x: (x[0] - n_blocks * x[1])**2 / (n_blocks * x[1]), zip(v, pi))) p_value = igamc(k / 2, chi2_obs / 2) if type(misc) is dict: misc.update(m=m, k=k, n=n_blocks, v=v, chi2_obs=chi2_obs) return p_value
def approximate_entropy(generator, n_bits, m=10, misc=None): """Approximate Entropy Test. Test purpose as described in [NIST10, section 2.12]: "As with the Serial test of Section 2.11, the focus of this test is the frequency of all possible overlapping m-bit patterns across the entire sequence. The purpose of the test is to compare the frequency of overlapping blocks of two consecutive/adjacent lengths (m and m+1) against the expected result for a random sequence." """ if m < 1: raise ValueError("Parameter m must be an integer greater than zero") if m >= int(log2(n_bits)) - 5: print("Warning: Parameter m should be less than [log2(n)]-5", file=stderr) vm0 = [0] * (2**m) vm1 = [0] * (2**(m + 1)) sequence_start = [generator.random_bit() for _ in range(m)] window0 = deque(sequence_start, maxlen=m) window1 = deque(sequence_start, maxlen=m + 1) # take care of the first step for the smaller window vm0[concat_chunks(sequence_start, bits=1)] += 1 all_seq = sequence_start for i in range(m, n_bits + m): bit = generator.random_bit() if i < n_bits else sequence_start[i - n_bits] all_seq.append(bit) window0.append(bit) # when full with m items, left item is discarded window1.append(bit) # when full with m+1 items, left item is discarded if i < n_bits + m - 1: # skip the last step for the smaller window vm0[concat_chunks(window0, bits=1)] += 1 vm1[concat_chunks(window1, bits=1)] += 1 cm0 = map(lambda x: x / n_bits, vm0) cm1 = map(lambda x: x / n_bits, vm1) phi_m0 = sum([x * log(x) for x in cm0 if x > 0]) phi_m1 = sum([x * log(x) for x in cm1 if x > 0]) chi2_obs = 2 * n_bits * (log(2) - (phi_m0 - phi_m1)) p_value = igamc(2**(m - 1), chi2_obs / 2) if type(misc) == dict: misc.update(vm0=vm0, vm1=vm1, phi_m0=phi_m0, phi_m1=phi_m1, chi2_obs=chi2_obs) return p_value
def rank(generator, n_bits, misc=None): """Binary Matrix Rank Test. Test purpose as described in [NIST10, section 2.5]: "The focus of the test is the rank of disjoint sub-matrices of the entire sequence. The purpose of this test is to check for linear dependence among fixed length substrings of the original sequence. Note that this test also appears in the DIEHARD battery of tests." """ m = q = 32 n_blocks = n_bits // (m * q) if n_blocks < 1: raise ValueError( "Sequence must be longer, should be at least 38*m*q =" + str(38 * m * q) + "bits long") elif n_blocks < 38: print("Warning: Sequence should be at least 38*m*q =", 38 * m * q, "bits long", file=stderr) f_m = f_m1 = 0 for _ in range(n_blocks): block = [generator.random_bit() for _ in range(m * q)] matrix = [block[i * q:(i + 1) * q] for i in range(m)] r = _matrix_rank_bin(matrix, m, q) if r == m: f_m += 1 elif r == m - 1: f_m1 += 1 # pre-calculate more precise values for probability values p1, p2, and p3 # (see section 3.5 in NIST SP 800-22) p_ = 0 p1 = 1 # for x in range(1, 50): p1 *= 1 - (1.0 / (2 ** x)) x = 1 while abs(p1 - p_) > 1e-9: p_ = p1 p1 *= 1 - (1.0 / (2**x)) x += 1 p2 = 2 * p1 p3 = 1 - p1 - p2 chi2_obs = (f_m - p1 * n_blocks) ** 2 / p1 / n_blocks + \ (f_m1 - p2 * n_blocks) ** 2 / p2 / n_blocks + \ (n_blocks - f_m - f_m1 - p3 * n_blocks) ** 2 / p3 / n_blocks # p_value = exp(-chi2_obs/2) p_value = igamc(1, chi2_obs / 2) if type(misc) is dict: misc.update(n=n_blocks, f_m=f_m, f_m1=f_m1, chi2_obs=chi2_obs) return p_value
def test(bits, blocklen=10): n = len(bits) M = blocklen N = n // M π = [0] * N for i in range(N): π[i] = Fraction(sum([bits[i * M + j] for j in range(M)]), M) χ2_obs = 4 * M * sum([(π[i] - Fraction(1, 2))**2 for i in range(N)]) P_value = igamc(N / 2.0, χ2_obs / 2.0) level = 0.01 return (P_value, level, P_value >= level)
def linear_complexity(generator, n_bits, m=500, misc=None, use_old_nist_pi=False): """Linear Complexity Test. Test purpose as described in [NIST10, section 2.10]: "The focus of this test is the length of a linear feedback shift register (LFSR). The purpose of this test is to determine whether or not the sequence is complex enough to be considered random. Random sequences are characterized by longer LFSRs. An LFSR that is too short implies non-randomness." """ n_blocks = n_bits // m if n_bits < 10**6: print("Warning: Sequence should be at least 10^6 bits long", file=stderr) if not (500 <= m < 5000): print("Warning: Parameter m should be in range [500, 5000]", file=stderr) if n_blocks < 200: print("Warning: The sequence should be split to at least 200 blocks", file=stderr) exp_mean = m / 2 + (9 + (-1)**(m + 1)) / 36 - (m / 3 + 2 / 9) / (2**m) v = [0] * 7 for i in range(n_blocks): block = [generator.random_bit() for _ in range(m)] l = _berlekamp_massey(block) t = ((-1)**m) * (l - exp_mean) + 2 / 9 index = int(min(max(ceil(t + 2.5), 0), 6)) v[index] += 1 if use_old_nist_pi: # old NIST values - these pass the test samples but the first value is incorrect pi = (0.01047, 0.03125, 0.125, 0.5, 0.25, 0.0625, 0.020833) else: # correct pi values pi = (1 / 96, 0.03125, 0.125, 0.5, 0.25, 0.0625, 1 / 48) chi2_obs = sum( map(lambda v_i, pi_i: (v_i - n_blocks * pi_i)**2 / (n_blocks * pi_i), v, pi)) p_value = igamc(6 / 2., chi2_obs / 2.) if type(misc) == dict: misc.update(exp_mean=exp_mean, v=v, chi2_obs=chi2_obs) return p_value
def overlapping_template_matching(generator, n_bits, b=(1, 1, 1, 1, 1, 1, 1, 1, 1), misc=None, use_old_nist_pi=False): """Overlapping Template Matching Test. Test purpose as described in [NIST10, section 2.8]: "The focus of the Overlapping Template Matching test is the number of occurrences of pre-specified target strings. Both this test and the Non-overlapping Template Matching test of Section 2.7 use an m-bit window to search for a specific m-bit pattern. As with the test in Section 2.7, if the pattern is not found, the window slides one bit position. The difference between this test and the test in Section 2.7 is that when the pattern is found, the window slides only one bit before resuming the search." """ if type(b) == tuple: b = list(b) b_len = len(b) # if b_len != 9: # raise ValueError("Length of pattern b must be exactly 9 bits") m = 1032 # hardcoded to match the pre-computed pi values that depend on m and length of b n_blocks = n_bits // m lambda_ = (m - b_len + 1) / (2**b_len) eta = lambda_ / 2 v = [0] * 6 for _ in range(n_blocks): block = [generator.random_bit() for _ in range(m)] w = 0 for i in range(m - b_len + 1): if block[i:i + b_len] == b: w += 1 v[min(w, 5)] += 1 if use_old_nist_pi: pi = (0.367879, 0.183940, 0.137955, 0.099634, 0.069935, 0.140657) else: # use improved pi values given by Hamano and Kaneko pi = (0.364091, 0.185659, 0.139381, 0.100571, 0.0704323, 0.139865) chi2_obs = sum( map(lambda x: (x[0] - n_blocks * x[1])**2 / (n_blocks * x[1]), zip(v, pi))) p_value = igamc(5 / 2, chi2_obs / 2) if type(misc) == dict: misc.update(v=v, lambda_=lambda_, eta=eta, chi2_obs=chi2_obs) return p_value
def non_overlapping_template_matching(generator, n_bits, b=None, n_blocks=None, misc=None): """Non-overlapping Template Matching Test. Test purpose as described in [NIST10, section 2.7]: "The focus of this test is the number of occurrences of pre-specified target strings. The purpose of this test is to detect generators that produce too many occurrences of a given non-periodic (aperiodic) pattern. For this test and for the Overlapping Template Matching test of Section 2.8, an m-bit window is used to search for a specific m-bit pattern. If the pattern is not found, the window slides one bit position. If the pattern is found, the window is reset to the bit after the found pattern, and the search resumes." """ if not b: b = [0, 0, 0, 0, 0, 0, 0, 0, 1] elif type(b) == tuple: b = list(b) b_len = len(b) if not n_blocks: n_blocks = 8 m = n_bits // n_blocks exp_mean = (m - b_len + 1) / (2**b_len) exp_var = m * (1 / (2**b_len) - (2 * b_len - 1) / (2**(2 * b_len))) n_blocks = n_bits // m chi2_obs = 0 for _ in range(n_blocks): block = [generator.random_bit() for _ in range(m)] i, w = 0, 0 while i < m - b_len + 1: if block[i:i + b_len] == b: w += 1 i += b_len else: i += 1 chi2_obs += ((w - exp_mean)**2) / exp_var p_value = igamc(n_blocks / 2, chi2_obs / 2) if type(misc) == dict: misc.update(exp_mean=exp_mean, exp_var=exp_var, n=n_blocks, chi2_obs=chi2_obs) return p_value
def test(bits, blocklen=2): n = len(bits) m = blocklen N = n // m bits += bits[0:m - 1] ν_m = [[0 for j in range(2**(m - i))] for i in range(3)] for i in range(3): for position in range(n): idx = block2int(bits[position:position + (m - i)]) ν_m[i][idx] += 1 ψ2_m = [0] * 3 for i in range(3): a = Fraction(2**(m - i), n) ψ2_m[i] = a * sum([ν_m[i][j]**2 for j in range(2**(m - i))]) - n nabla_ψ2_m = ψ2_m[0] - ψ2_m[1] nabla2_ψ2_m = ψ2_m[0] - 2 * ψ2_m[1] + ψ2_m[2] P_value1 = igamc(2**(m - 2), nabla_ψ2_m / 2.0) P_value2 = igamc(2**(m - 3), nabla2_ψ2_m / 2.0) level = 0.01 ok = (P_value1 >= level and P_value2 >= level) return ((P_value1, P_value2), level, ok)
def test(bits, blocklen=1000): n = len(bits) M = blocklen N = n // M L = [] for i in range(N): x = bits[i * M:(i + 1) * M] L.append(berelekamp_massey(x)[0]) μ = Fraction(M, 2) + Fraction(9 + (-1)**(M+1), 36) \ - Fraction(Fraction(M, 3) + Fraction(2, 9), 2**M) T = [] for i in range(N): T.append(((-1.0)**M) * (L[i] - μ) + (2 / 9.0)) ν = [0] * 7 for t in T: if t <= -2.5: ν[0] += 1 elif t <= -1.5: ν[1] += 1 elif t <= -0.5: ν[2] += 1 elif t <= 0.5: ν[3] += 1 elif t <= 1.5: ν[4] += 1 elif t <= 2.5: ν[5] += 1 else: ν[6] += 1 K = 6 π = [ Fraction(0.010417), Fraction(0.03125), Fraction(0.125), Fraction(0.5), Fraction(0.25), Fraction(0.0625), Fraction(0.020833) ] χ2_obs = sum([(ν[i] - N * π[i])**2 / (N * π[i]) for i in range(K)]) P_value = igamc(K / 2.0, χ2_obs / 2.0) level = 0.01 return (P_value, level, P_value >= level)
def test(bits, rowlen=32, collen=32): n = len(bits) M = rowlen Q = collen N = n // (M * Q) blocks = [] for j in range(N): blocks.append( [bits[(j * Q + i) * M:(j * Q + i + 1) * M] for i in range(Q)]) R = [matrix_rank(block) for block in blocks] F_M = 0 F_M_1 = 0 for i in range(N): if R[i] == M: F_M += 1 elif R[i] == M - 1: F_M_1 += 1 F = [F_M, F_M_1, N - F_M - F_M_1] π = [Fraction(0.2888), Fraction(0.5776), Fraction(0.1336)] χ2_obs = sum([(F[i] - N * π[i])**2 / (N * π[i]) for i in range(3)]) P_value = igamc(1, χ2_obs / 2.0) level = 0.01 return (P_value, level, P_value >= level)
def test(bits, blocklen=None, template=None): n = len(bits) M = blocklen if blocklen else n // 8 if M <= 0.01 * n: return (-1, -1, False) N = n // M blocks = [bits[i * M:(i + 1) * M] for i in range(N)] B = template if template else random.choices(templates[8]) m = len(B) ν = [0] * 6 for block in blocks: count = 0 for position in range(M - m + 1): if block[position:position + m] == B: count += 1 if count >= 5: ν[5] += 1 else: ν[count] += 1 # λ = (M - m + 1) / 2**m ~~ 2 π = [ Fraction(0.364091), Fraction(0.185659), Fraction(0.139381), Fraction(0.100571), Fraction(0.070432), Fraction(0.139865) ] χ2_obs = sum([(ν[i] - N * π[i])**2 / (N * π[i]) for i in range(6)]) P_value = igamc(5 / 2.0, χ2_obs / 2.0) level = 0.01 return (P_value, level, P_value >= level)
def test(bits): n = len(bits) if n < 128: return (-1, -1, False) # Not enough bits length elif n < 6272: M = 8 elif n < 750000: M = 128 else: M = 10000 N = n // M blocks = [bits[i * M:(i + 1) * M] for i in range(N)] ν = [0] * 7 for i in range(N): # get longest run of ones run = 0 longest = 0 for bit in blocks[i]: if bit == 1: run += 1 if run > longest: longest = run else: run = 0 if M == 8: if longest <= 1: ν[0] += 1 elif longest == 2: ν[1] += 1 elif longest <= 3: ν[2] += 1 else: ν[3] += 1 elif M == 128: if longest <= 4: ν[0] += 1 elif longest == 5: ν[1] += 1 elif longest == 6: ν[2] += 1 elif longest == 7: ν[3] += 1 elif longest == 8: ν[4] += 1 else: ν[5] += 1 else: if longest <= 10: ν[0] += 1 elif longest == 11: ν[1] += 1 elif longest == 12: ν[2] += 1 elif longest == 13: ν[3] += 1 elif longest == 14: ν[4] += 1 elif longest == 15: ν[5] += 1 else: ν[6] += 1 if M == 8: K = 3 N = 16 elif M == 128: K = 5 N = 49 else: K = 6 N = 75 π = prob_table(M) χ2_obs = sum([(ν[i] - N * π[i])**2 / (N * π[i]) for i in range(K + 1)]) P_value = igamc(K / 2.0, χ2_obs / 2.0) level = 0.01 return (P_value, level, P_value >= level)
def random_excursions(generator, n_bits, misc=None): """Random Excursions Test. Test purpose as described in [NIST10, section 2.14]: "The focus of this test is the number of cycles having exactly K visits in a cumulative sum random walk. The cumulative sum random walk is derived from partial sums after the (0,1) sequence is transferred to the appropriate (-1, +1) sequence. A cycle of a random walk consists of a sequence of steps of unit length taken at random that begin at and return to the origin. The purpose of this test is to determine if the number of visits to a particular state within a cycle deviates from what one would expect for a random sequence. This test is actually a series of eight tests (and conclusions), one test and conclusion for each of the states: -4, -3, -2, -1 and +1, +2, +3, +4." """ if n_bits < 10**6: print("Warning: Sequence should be at least 10^6 bits long", file=stderr) s = [0] * n_bits for i in range(n_bits): x = 1 if generator.random_bit() else -1 s[i] = s[i - 1] + x s.append(0) # leading zero not needed for our implementation v = [[0] * 6 for _ in range(8)] f = [0] * 8 j = 0 for x in s: if x == 0: j += 1 for i, y in enumerate(f): v[i][min(y, 5)] += 1 f = [0] * 8 elif -4 <= x <= 4: f[x + 4 - (x > 0)] += 1 if j < 500: print( "Warning: The number of cycles (zero crossings) should be at least 500 (is %d)" % j, file=stderr) def pi(k, x): a = 1 - 1 / 2 / abs(x) if k == 0: return a elif k < 5: return 1 / 4 / (x**2) * (a**(k - 1)) else: # k >= 5 return 1 / 2 / abs(x) * (a**4) chi2_obs = [] for i, x in enumerate(list(range(-4, 0)) + list(range(1, 5))): chi2_obs.append( sum( map(lambda v_k_x, pi_k_x: (v_k_x - j * pi_k_x)**2 / j / pi_k_x, v[i], [pi(k, x) for k in range(0, 6)]))) p_value = list(map(lambda chi: igamc(5 / 2, chi / 2), chi2_obs)) if type(misc) == dict: misc.update(cumsum=s, j=j, v=v, chi2_obs=chi2_obs) return p_value
def test(bits, mode=0): n = len(bits) X = [2 * x - 1 for x in bits] total = 0 S = [] for i in range(n): total += X[i] S.append(total) S_dash = [0] + S + [0] zero_positions = [i for i in range(1, len(S_dash)) if S_dash[i] == 0] J = len(zero_positions) Cycles = [] pre = 0 for pos in zero_positions: Cycles.append(S_dash[pre:pos + 1]) pre = pos states = [] for cycle in Cycles: state = {-4: 0, -3: 0, -2: 0, -1: 0, 1: 0, 2: 0, 3: 0, 4: 0} for x in cycle: if x == -4: state[x] += 1 elif x == -3: state[x] += 1 elif x == -2: state[x] += 1 elif x == -1: state[x] += 1 elif x == +1: state[x] += 1 elif x == +2: state[x] += 1 elif x == +3: state[x] += 1 elif x == +4: state[x] += 1 else: pass states.append(state) table = { -4: [0] * 6, -3: [0] * 6, -2: [0] * 6, -1: [0] * 6, +1: [0] * 6, +2: [0] * 6, +3: [0] * 6, +4: [0] * 6 } for x in (-4, -3, -2, -1, 1, 2, 3, 4): # state x for n in range(6): # number of cycles (0..5) count = len([True for state in states if state[x] == n]) table[x][n] = count πxk = { 1: [0.5, 0.25, 0.125, 0.0625, 0.0312, 0.0312], 2: [0.75, 0.0625, 0.0469, 0.0352, 0.0264, 0.0791], 3: [0.8333, 0.0278, 0.0231, 0.0193, 0.0161, 0.0804], 4: [0.875, 0.0156, 0.0137, 0.012, 0.0105, 0.0733], 5: [0.9, 0.01, 0.009, 0.0081, 0.0073, 0.0656], 6: [0.9167, 0.0069, 0.0064, 0.0058, 0.0053, 0.0588], 7: [0.9286, 0.0051, 0.0047, 0.0044, 0.0041, 0.0531] } χ2_obs = {-4: 0, -3: 0, -2: 0, -1: 0, 1: 0, 2: 0, 3: 0, 4: 0} f = lambda obs, exp: (obs - exp)**2 / exp for x in (-4, -3, -2, -1, 1, 2, 3, 4): χ2_obs[x] = sum( [f(table[x][k], Fraction(J * πxk[abs(x)][k])) for k in range(6)]) P_value = {-4: 0, -3: 0, -2: 0, -1: 0, 1: 0, 2: 0, 3: 0, 4: 0} for x in (-4, -3, -2, -1, 1, 2, 3, 4): P_value[x] = igamc(5 / 2.0, χ2_obs[x] / 2.0) level = 0.01 ok = all(P_value[x] >= level for x in (-4, -3, -2, -1, 1, 2, 3, 4)) return (P_value, level, ok)