"""
前置知识（建议要看，非常自信可以不看）
https://zhuanlan.zhihu.com/p/448198789

信息论的一些基础
可以结合这份ppt来看
https://web.xidian.edu.cn/jwliu/files/20190521_003040.pdf

同时b站上的国防科技大学-信息论与编码基础也可以学习一下
https://www.bilibili.com/video/BV1pJ411U7G8?p=61


读题可知，我们需要获取一个7bit的有效信息，每次返回的信息可以看作1bit，共15bit。
而其中1-2次的说谎我们可以视为1-2bit的传输错误。
因此我们需要一种最少可以纠正2bit的编码，也就是循环冗余校验

根据题目描述，我们需要构造一个（15，7）循环码，也就是15bit的编码数，7bit的有效信息数
"""
# 首先定义伽罗瓦域
R.<x> = PolynomialRing(GF(2))

"""
然后寻找生成元g
根据定理，（n，k）循环码的生成元g一定是x^n+1的n-k次因子
（ppt中说是x^n-1,这里因为是GF(2)，所以+1和-1是等价的）
带入计算得知，g应当为x^15+1的8次因子
"""
ff = x^15+1
g = factor(ff)
# (x + 1) * (x^2 + x + 1) * (x^4 + x + 1) * (x^4 + x^3 + 1) * (x^4 + x^3 + x^2 + x + 1)
factors = []
for i in range(len(g)):
    factors.append(g[i][0])

possible_g = []

# x + 2 * y + 4 * z == 8
# root = [(8,0,0),(6,1,0),(4,2,0),(4,0,1),(2,3,0),(2,1,1),(0,4,0),(0,2,1),(0,0,2)]


possible_g.append(factors[0]^8)

possible_g.append(factors[0]^6 * factors[1])

possible_g.append(factors[0]^4 * factors[1]^2)

possible_g.append(factors[0]^4 * factors[2])
possible_g.append(factors[0]^4 * factors[3])
possible_g.append(factors[0]^4 * factors[4])

possible_g.append(factors[0]^2 * factors[1]^3)

possible_g.append(factors[0]^2 * factors[1] * factors[2])
possible_g.append(factors[0]^2 * factors[1] * factors[3])
possible_g.append(factors[0]^2 * factors[1] * factors[4])

possible_g.append(factors[1]^4)

possible_g.append(factors[1]^2 * factors[2])
possible_g.append(factors[1]^2 * factors[3])
possible_g.append(factors[1]^2 * factors[4])

possible_g.append(factors[2] * factors[2])
possible_g.append(factors[2] * factors[3])
possible_g.append(factors[2] * factors[4])
possible_g.append(factors[3] * factors[3])
possible_g.append(factors[3] * factors[4])
possible_g.append(factors[4] * factors[4])

"""
至此我们已经找出所有的生成元了，但是不要忘了还有纠错的要求。
纠正2bit的要求则是： 假设用d表示码组的最小汉明距离，纠正错误时，设可纠正t位的错误，则d >= 2t+1
（也就是保证任一点A的错误状态不落到其余点B，并且A的错误状态离A更近）
带入公式则是d >= 5
"""

# 定义函数 n2p，将二进制数转换为多项式的形式
def n2p(a):
    a = bin(a)[2:]
    p = 0
    for i in range(len(a)):
        if a[len(a) - i - 1] == '1':
            p += x^i
    return p

# 定义函数enc，将7bit的信息编码成循环码
def enc(i):
    m = n2p(i)
    c = (x^8) * m + (((x^8) * m) % g)
    return "".join([str(int(i in c.exponents())) for i in range(15)])[::-1]

# 定义函数dif，计算ab之间的汉明距离
def dif(a, b):
        cnt = 0
        for i in range(len(a)):
            if a[i] != b[i]:
                cnt += 1
        return cnt

"""
通过构造校验表来检查所有的生成元是否满足条件
"""
for g in possible_g: 
    # 创建一个列表 dic，存储不同输入值的编码
    dic = []

    for i in range(2**7): 
        dic.append(enc(i))    

    minHD = 15
    for i in dic:
        for j in dic:
            if i == j:
                continue
            if dif(i,j) <= minHD:
                minHD = dif(i,j)

    #if minHD >= 5:
        #print(g)
    # x^8 + x^7 + x^6 + x^4 + 1
    # x^8 + x^4 + x^2 + x + 1


# 下面两个随便挑一个作为生成元就好了
# g = x^8 + x^7 + x^6 + x^4 + 1
g = x^8 + x^4 + x^2 + x + 1
dic = []

for i in range(2^7): 
    dic.append(enc(i))

"""
构造问题这个地方有点巧妙。
我们可以发现系统码是一个对称互补的码，也就是说0和127，1和126的系统码每一位都相反。

那么对于所有系统码来说0和1就是平均分布的。（因为成对来看，30个bit中必有15bit的0和15bit的1）
对于系统码的任意一位为0的概率就是0.5，为1的概率也是0.5

因此我们可以把questions[i]和j[i]等价来看，
我们可以找出所有令j[i]==1的信息码情况，共64种，把这些进行or运算。
把这个问题发送出去，如果返回true，那么证明j[i]就是1。
以此类推，我们就可以得到一串15bit的返回值。

但是因为存在最多2bit的错误，我们需要跟码表进行对比，找出汉明距离小于等于2的系统码。
然后这个系统码对应的信息码就是正确的信息码
"""

# 定义字符串模板 part，用于后面构造问题
part = "( C0 == {} and C1 == {} and C2 == {} and C3 == {} and C4 == {} and C5 == {} and C6 == {} ) "

# 创建问题列表 questions，用于存储构造的问题模板
questions = []
for i in range(15):
    question = ""
    nums = [j[:7] for j in dic if j[i] == '1']
    for j in range(64):
        n = nums[j]
        question += part.format(n[0], n[1], n[2], n[3], n[4], n[5], n[6]) + "or "
    questions.append(question[:-4])



# 导入 pwn 模块，用于与远程或本地进程进行交互
from pwn import *

# 设置日志级别为 debug，以便在执行过程中输出详细日志信息
#context.log_level = 'debug'

r = process(['python3', 'main_local.py'])
#r = remote("localhost", "10010")

for i in range(50):
    msg = ''
    # 逐个发送问题并接收答案
    for j in range(15):
        r.sendlineafter(b"Shannon:", questions[j].encode())
        r.recvuntil(b"answers:")
        if b'True' in r.recvuntil(b"!"):
            msg += '1'
        else:
            msg += '0'
    # 查找与收到的答案最接近的编码，这里答案肯定只会有一个
    for j in range(128):
        if (dif(msg, dic[j]) <= 2):
            break
    ans = ""
    for k in dic[j][:7]:
        ans += k + " "
    # 发送解码后的答案
    r.sendlineafter(b"chests:", ans.encode())

flag = r.recvall()
print(flag)
r.close()

