"""
MikoshiLang Extended Functions - Set 29
Information theory, cryptography, coding theory, and data compression
"""
import sympy as sp
from sympy import symbols, sqrt, exp, log, pi, factorial

def get_rules():
    """Extended set 29: Information theory and cryptography (150 functions)"""
    x, p, n = symbols('x p n')
    
    return [
        # ===== INFORMATION THEORY (40 functions) =====
        ('EntropyShannon[p_]', lambda p: -sum(pi*log(pi, 2) for pi in (p.args if hasattr(p, 'args') else p) if pi > 0)),
        ('JointEntropy[p_xy_]', lambda pxy: -sum(sum(pij*log(pij, 2) for pij in row if pij > 0) for row in pxy)),
        ('ConditionalEntropyInfo[p_xy_, p_x_]', lambda pxy, px: sp.Symbol('H(X,Y)') - sp.Symbol('H(X)')),
        ('MutualInformationInfo[H_X_, H_Y_, H_XY_]', lambda HX, HY, HXY: HX + HY - HXY),
        ('RelativeEntropy[p_, q_]', lambda p, q: sum(pi*log(pi/qi, 2) for pi, qi in zip(p, q) if pi > 0)),
        ('KullbackLeiblerDivergence[p_, q_]', lambda p, q: sum(pi*log(pi/qi, 2) for pi, qi in zip(p, q) if pi > 0)),
        ('CrossEntropyInfo[p_, q_]', lambda p, q: -sum(pi*log(qi, 2) for pi, qi in zip(p, q) if pi > 0)),
        ('DifferentialEntropy[f_]', lambda f: -sp.integrate(f*log(f, 2), (sp.Symbol('x'), -sp.oo, sp.oo))),
        ('MaxEntropyPrinciple[constraints_]', lambda c: "max entropy distribution"),
        ('InformationGain[H_before_, H_after_]', lambda Hb, Ha: Hb - Ha),
        ('GiniImpurity[p_]', lambda p: 1 - sum(pi**2 for pi in p)),
        ('ChannelCapacity[C_]', lambda C: C),  # bits/s
        ('ShannonHartleyTheorem[B_, SNR_]', lambda B, SNR: B*log(1 + SNR, 2)),
        ('NyquistRate[f_max_]', lambda fmax: 2*fmax),
        ('SamplingTheorem[f_s_, f_max_]', lambda fs, fmax: fs >= 2*fmax),
        ('SourceCoding[H_, R_]', lambda H, R: R >= H),  # Source coding theorem
        ('ChannelCodingTheorem[R_, C_]', lambda R, C: R < C),
        ('RateDistortionFunction[D_]', lambda D: sp.Symbol('R(D)')),
        ('BinarySymmetricChannel[p_]', lambda p: 1 - sp.Symbol('H')(p)),
        ('BinaryErasureChannel[epsilon_]', lambda eps: 1 - eps),
        ('GaussianChannel[P_, N_]', lambda P, N: sp.Rational(1,2)*log(1 + P/N, 2)),
        ('FanoInequality[Pe_, H_]', lambda Pe, H: sp.Symbol('H(X|Y)') <= sp.Symbol('h')(Pe) + Pe*log(sp.Symbol('|X|') - 1, 2)),
        ('DataProcessingInequality[I_XY_, I_YZ_]', lambda IXY, IYZ: sp.Symbol('I(X;Z)') <= min(IXY, IYZ)),
        ('AsymptoticEquipartitionProperty[n_, H_]', lambda n, H: 2**(-n*H)),
        ('TypicalSet[epsilon_, n_, H_]', lambda eps, n, H: 2**(-n*(H + eps))),
        ('KolmogorovComplexity[K_]', lambda K: K),
        ('AlgorithmicInformation[x_]', lambda x: sp.Symbol('K(x)')),
        ('IncompressibilityTheorem[random_]', lambda r: sp.Symbol('K(x)') >= sp.Symbol('|x|')),
        ('MinimumDescriptionLength[model_, data_]', lambda m, d: sp.Symbol('L(m)') + sp.Symbol('L(d|m)')),
        ('BayesianInformationCriterion[L_, k_, n_]', lambda L, k, n: -2*L + k*log(n)),
        ('AkaikeInformationCriterion[L_, k_]', lambda L, k: -2*L + 2*k),
        ('FisherInformation[score_]', lambda s: sp.Symbol('E')[s**2]),
        ('CramerRaoBound[I_]', lambda I: 1/I),
        ('SufficientStatistic[T_]', lambda T: T),
        ('MinimalSufficientStatistic[T_]', lambda T: T),
        ('ExponentialFamily[theta_]', lambda theta: exp(sp.Symbol('eta')*theta)),
        ('MaximumEntropyDist[constraints_]', lambda c: "distribution"),
        ('InformationGeometry[manifold_]', lambda m: m),
        ('AmariChentsovTensor[metric_]', lambda g: g),
        ('InformationProjection[p_, q_]', lambda p, q: sp.Symbol('KL(p||q)')),
        
        # ===== CODING THEORY (40 functions) =====
        ('HammingDistance[x_, y_]', lambda x, y: sum(xi != yi for xi, yi in zip(str(x), str(y)))),
        ('HammingWeight[x_]', lambda x: sum(int(xi) for xi in str(x))),
        ('MinimumDistance[code_]', lambda c: 1),  # Placeholder
        ('CodeRate[k_, n_]', lambda k, n: k/n),
        ('RedundancyBits[n_, k_]', lambda n, k: n - k),
        ('SpherePackingBound[n_, t_]', lambda n, t: 2**n / sum(sp.binomial(n, i) for i in range(int(t)+1))),
        ('HammingBound[n_, d_]', lambda n, d: 2**n / sum(sp.binomial(n, i) for i in range(int((d-1)/2)+1))),
        ('SingletonBound[n_, k_, d_]', lambda n, k, d: d <= n - k + 1),
        ('PlotkinBound[n_, d_]', lambda n, d: 2**n <= 2*d if 2*d > n else 2**(n-1)),
        ('GilbertVarshamovBound[n_, d_]', lambda n, d: 2**n >= sum(sp.binomial(n-1, i) for i in range(int(d-1)))),
        ('PerfectCode[n_, d_]', lambda n, d: 2**n == sum(sp.binomial(n, i) for i in range(int((d-1)/2)+1))),
        ('HammingCode[m_]', lambda m: (2**m - 1, 2**m - m - 1, 3)),
        ('GolayCode[]', lambda: (23, 12, 7)),
        ('ReedMullerCode[r_, m_]', lambda r, m: (2**m, sum(sp.binomial(m, i) for i in range(int(r)+1)), 2**(m-r))),
        ('BCHCode[n_, t_]', lambda n, t: (n, sp.Symbol('k'), 2*t + 1)),
        ('ReedSolomonCode[n_, k_]', lambda n, k: (n, k, n - k + 1)),
        ('CyclicCode[g_]', lambda g: g),  # Generator polynomial
        ('GeneratorMatrix[G_]', lambda G: G),
        ('ParityCheckMatrix[H_]', lambda H: H),
        ('SyndromeDecoding[s_]', lambda s: s),
        ('ErrorDetectionCapability[d_]', lambda d: d - 1),
        ('ErrorCorrectionCapability[d_]', lambda d: (d - 1)/2),
        ('BurstErrorLength[b_]', lambda b: b),
        ('InterleavingDepth[n_]', lambda n: n),
        ('ConvolutionalCode[constraint_length_]', lambda K: K),
        ('TrellisCode[states_]', lambda s: s),
        ('ViterbiAlgorithm[trellis_]', lambda t: "decoded sequence"),
        ('TurboCode[interleaver_]', lambda i: i),
        ('LDPCCode[parity_check_]', lambda H: H),
        ('BeliefPropagationDecoding[graph_]', lambda g: "decoded"),
        ('SumProductAlgorithm[messages_]', lambda m: m),
        ('PolarCode[N_]', lambda N: N),
        ('SuccessiveCancellation[y_]', lambda y: "decoded"),
        ('FountainCode[k_]', lambda k: k),
        ('RaptorCode[overhead_]', lambda oh: oh),
        ('LTCode[degree_distribution_]', lambda dd: dd),
        ('NetworkCoding[flows_]', lambda f: f),
        ('ReedSolomonErasureCode[n_, k_]', lambda n, k: (n, k)),
        ('MaximumLikelihoodDecoding[y_, code_]', lambda y, c: "ML estimate"),
        ('MaximumAPosteridecoding[y_, prior_]', lambda y, pr: "MAP estimate"),
        
        # ===== CRYPTOGRAPHY (40 functions) =====
        ('CaesarCipher[plaintext_, shift_]', lambda pt, s: ''.join(chr((ord(c) - 65 + int(s)) % 26 + 65) for c in str(pt).upper())),
        ('VigenereCipher[plaintext_, key_]', lambda pt, k: "ciphertext"),
        ('SubstitutionCipher[plaintext_, mapping_]', lambda pt, m: "ciphertext"),
        ('TranspositionCipher[plaintext_, key_]', lambda pt, k: "ciphertext"),
        ('PlayfairCipher[plaintext_, key_]', lambda pt, k: "ciphertext"),
        ('HillCipher[plaintext_, key_matrix_]', lambda pt, K: "ciphertext"),
        ('VernamCipher[plaintext_, key_]', lambda pt, k: "ciphertext"),  # One-time pad
        ('OneTimePad[plaintext_, key_]', lambda pt, k: [pi ^ ki for pi, ki in zip(pt, k)]),
        ('StreamCipher[plaintext_, keystream_]', lambda pt, ks: [pi ^ ksi for pi, ksi in zip(pt, ks)]),
        ('BlockCipher[block_, key_]', lambda b, k: "encrypted block"),
        ('FeistelNetwork[L_, R_, f_, k_]', lambda L, R, f, k: (R, sp.Symbol('L') ^ f(R, k))),
        ('SubstitutionPermutationNetwork[state_, key_]', lambda s, k: "encrypted"),
        ('ConfusionProperty[cipher_]', lambda c: "obscures relationship"),
        ('DiffusionProperty[cipher_]', lambda c: "spreads influence"),
        ('AvalancheEffect[bit_change_]', lambda bc: bc >= 0.5),
        ('ModularExponentiation[base_, exp_, mod_]', lambda b, e, m: pow(int(b), int(e), int(m))),
        ('EuclideanAlgorithm[a_, b_]', lambda a, b: sp.gcd(a, b)),
        ('ExtendedEuclidean[a_, b_]', lambda a, b: (sp.gcd(a, b), sp.Symbol('x'), sp.Symbol('y'))),
        ('ModularInverse[a_, m_]', lambda a, m: pow(int(a), -1, int(m)) if sp.gcd(a, m) == 1 else None),
        ('ChineseRemainderTheorem[remainders_, moduli_]', lambda r, m: sp.Symbol('x')),
        ('FermatLittleTheorem[a_, p_]', lambda a, p: pow(int(a), int(p)-1, int(p)) == 1),
        ('EulerTotientFunction[n_]', lambda n: n - 1 if sp.isprime(n) else sp.Symbol('phi(n)')),
        ('RSAKeyGeneration[p_, q_]', lambda p, q: (p*q, sp.Symbol('e'), sp.Symbol('d'))),
        ('RSAEncryption[m_, e_, n_]', lambda m, e, n: pow(int(m), int(e), int(n))),
        ('RSADecryption[c_, d_, n_]', lambda c, d, n: pow(int(c), int(d), int(n))),
        ('RSASignature[m_, d_, n_]', lambda m, d, n: pow(int(m), int(d), int(n))),
        ('RSAVerification[s_, e_, n_]', lambda s, e, n: pow(int(s), int(e), int(n))),
        ('DiffieHellmanKeyExchange[g_, p_, a_, b_]', lambda g, p, a, b: pow(pow(int(g), int(a), int(p)), int(b), int(p))),
        ('ElGamalEncryption[m_, y_, g_, p_]', lambda m, y, g, p: (sp.Symbol('c1'), sp.Symbol('c2'))),
        ('ElGamalDecryption[c1_, c2_, x_, p_]', lambda c1, c2, x, p: sp.Symbol('m')),
        ('EllipticCurvePoint[x_, y_, a_, b_]', lambda x, y, a, b: y**2 - x**3 - a*x - b),
        ('ECPointAddition[P_, Q_]', lambda P, Q: sp.Symbol('R')),
        ('ECScalarMultiplication[k_, P_]', lambda k, P: sp.Symbol('Q')),
        ('ECDHKeyExchange[P_, a_, b_]', lambda P, a, b: sp.Symbol('shared')),
        ('ECDSASignature[m_, d_, k_]', lambda m, d, k: (sp.Symbol('r'), sp.Symbol('s'))),
        ('ECDSAVerification[m_, r_, s_, Q_]', lambda m, r, s, Q: True),
        ('HashFunction[m_]', lambda m: hash(str(m))),
        ('SHA256[message_]', lambda m: "hash"),
        ('SHA3[message_]', lambda m: "hash"),
        ('HMAC[key_, message_]', lambda k, m: "MAC"),
        
        # ===== DATA COMPRESSION (30 functions) =====
        ('CompressionRatio[original_, compressed_]', lambda orig, comp: orig/comp),
        ('SpaceSavings[original_, compressed_]', lambda orig, comp: (orig - comp)/orig*100),
        ('LosslessCompression[data_]', lambda d: "compressed"),
        ('LossyCompression[data_, quality_]', lambda d, q: "compressed"),
        ('RunLengthEncoding[data_]', lambda d: [(len(list(g)), k) for k, g in sp.Symbol('groupby')(d)]),
        ('HuffmanCoding[frequencies_]', lambda f: "tree"),
        ('HuffmanTree[symbols_, frequencies_]', lambda s, f: "tree"),
        ('OptimalPrefixCode[frequencies_]', lambda f: "Huffman"),
        ('ArithmeticCoding[symbols_, probabilities_]', lambda s, p: "encoded"),
        ('RangeCoding[cumulative_]', lambda cum: "encoded"),
        ('LempelZiv77[data_]', lambda d: "compressed"),
        ('LempelZiv78[data_]', lambda d: "compressed"),
        ('LZW[data_]', lambda d: "compressed"),
        ('DEFLATE[data_]', lambda d: "compressed"),  # LZ77 + Huffman
        ('GZIP[data_]', lambda d: "compressed"),
        ('BZIP2[data_]', lambda d: "compressed"),  # BWT + MTF + Huffman
        ('BurrowsWheelerTransform[text_]', lambda t: "transformed"),
        ('MoveToFrontEncoding[data_]', lambda d: "encoded"),
        ('DeltaEncoding[sequence_]', lambda s: [s[i] - s[i-1] for i in range(1, len(s))]),
        ('DifferentialEncoding[signal_]', lambda s: [s[i] - s[i-1] for i in range(1, len(s))]),
        ('DictionaryCompression[data_]', lambda d: "compressed"),
        ('AdaptiveDictionary[data_]', lambda d: "dictionary"),
        ('StaticDictionary[frequencies_]', lambda f: "dictionary"),
        ('EntropyEncoding[data_]', lambda d: "encoded"),
        ('VariableLengthCode[symbols_]', lambda s: "code"),
        ('UniversalCode[integer_]', lambda n: "code"),
        ('EliasGamma[n_]', lambda n: bin(int(n))[2:]),
        ('EliasDelta[n_]', lambda n: bin(int(n))[2:]),
        ('EliasOmega[n_]', lambda n: bin(int(n))[2:]),
        ('GolombCode[n_, m_]', lambda n, m: (int(n)//int(m), int(n)%int(m))),
    ]


def register():
    """Register all extended29 rules"""
    from .extended_helper import convert_rules
    return convert_rules(get_rules())
