{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "8bd03ae1",
   "metadata": {},
   "source": [
    "# LFSR\n",
    "\n",
    "https://en.wikipedia.org/wiki/Linear-feedback_shift_register"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "413e9cd6",
   "metadata": {},
   "source": [
    "## Theoretical\n",
    "\n",
    "$a_{i+4} = a_{i+2}+a_{i}, IV = [1, 0, 1, 0]$\n",
    "\n",
    "* next bits?\n",
    "\n",
    "$a_{i+4} = a_{i+2}+a_{i}, IV = [1, 1, 0, 1]$\n",
    "\n",
    "* next bits?\n",
    "\n",
    "    0100011010101110101010111110101 (32bit)\n",
    "\n",
    "* 32 bit LFSR generating sequence above?\n",
    "* 31 bit LFSR generating sequence above?\n",
    "* What can be the smallest degree of LFSR?"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "07018c2d",
   "metadata": {},
   "source": [
    "## LFSR generation"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "62aa14cd",
   "metadata": {},
   "source": [
    " $s_{i+4} = s_i + s_{i+1} + s_{i+3}$\n",
    " \n",
    " inicialized with vector [1, 1, 0, 0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9cdc9240",
   "metadata": {},
   "outputs": [],
   "source": [
    "from numpy import dot\n",
    "\n",
    "state = [1, 1, 0, 0]\n",
    "coef = [1, 1, 0, 1]\n",
    "\n",
    "int(dot(state, coef) % 2) #next bit"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2e317627",
   "metadata": {},
   "source": [
    "    [1, 1, 0, 0] => [1, 0, 0, 0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6978dc3b",
   "metadata": {},
   "outputs": [],
   "source": [
    "state = state[1:]+[int(dot(state, coef)) % 2]\n",
    "state"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a73cc7e6",
   "metadata": {},
   "outputs": [],
   "source": [
    "state = state[1:]+[int(dot(state, coef)) % 2]\n",
    "state"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b008e6f2",
   "metadata": {},
   "outputs": [],
   "source": [
    "state = [1, 1, 0, 0]\n",
    "coef = [1, 1, 0, 1]\n",
    "\n",
    "stream = state.copy()\n",
    "for _ in range(46):\n",
    "    bit = int(dot(state, coef) % 2) #calculate next bit\n",
    "#    print(bit)\n",
    "    stream.append(bit) #append to the strean\n",
    "    state = state[1:]+[bit]  # change state (omit first bit and append new calculated at the end\n",
    "#    print(state)\n",
    "print(*stream)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6f7a6846",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Python iterator\n",
    "def lfsr(seed, coef):\n",
    "    \"\"\"\n",
    "        generates bits of LFSR with the given seed and coefficients\n",
    "        seed and coef can be list/tuple of int or string of digits 0/1\n",
    "    \"\"\"    \n",
    "    assert len(seed) == len(coef), \"Lengths of SEED and COEF differ\"\n",
    "    state = list(map(int, seed))\n",
    "    coef = list(map(int, coef))\n",
    "    while True:\n",
    "        yield state[0] #next value from list\n",
    "        state = state[1:]+[int(dot(state, coef) % 2)] #next state"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "31611af2",
   "metadata": {},
   "outputs": [],
   "source": [
    "from itertools import islice\n",
    "\n",
    "islice(lfsr([1, 1, 0, 0], [1, 1, 0, 1]), 30) #creates new iterator"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c4036a6f",
   "metadata": {},
   "outputs": [],
   "source": [
    "list(islice(lfsr(\"1100\", \"1101\"), 10))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5258b916",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(*islice(lfsr([1, 1, 0, 0], [1, 1, 0, 1]), 30))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ef0657e4",
   "metadata": {},
   "outputs": [],
   "source": [
    "\" \".join(map(str, islice(lfsr([1, 1, 0, 0], [1, 1, 0, 1]), 30)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "affca7fc",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(*islice(lfsr([1, 1, 0, 0], [1, 1, 0, 1]), 30), sep=\",\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "aa11ba40",
   "metadata": {},
   "source": [
    "# Sum\n",
    "\n",
    "Compute the sequence, which you get by modular sum of two LFSRs. What is the period of resulting sequence?\n",
    "\n",
    "$$c_i = a_i + b_i $$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "99db5a3a",
   "metadata": {},
   "outputs": [],
   "source": [
    "a = [0,1,0]\n",
    "b = [1,1,1]\n",
    "\n",
    "c = a+b\n",
    "print(c)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6cff33bf",
   "metadata": {},
   "source": [
    "### using indices"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4e0f10a3",
   "metadata": {},
   "outputs": [],
   "source": [
    "[ (a[i]+b[i]) % 2 for i in range(len(a))] #by index"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "446c844d",
   "metadata": {},
   "outputs": [],
   "source": [
    "[ (a[i] ^ b[i]) for i in range(len(a))] #using XOR"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "121ecbd2",
   "metadata": {},
   "source": [
    "### list comprehension"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "993dd555",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(a)\n",
    "print(b)\n",
    "print(*zip(a,b))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f9854347",
   "metadata": {},
   "outputs": [],
   "source": [
    "[ (x+y) % 2 for x, y in zip(a,b) ] #list comprehension"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a722577f",
   "metadata": {},
   "outputs": [],
   "source": [
    "[ (x ^ y) for x, y in zip(a,b) ] #list comprehension"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9811299a",
   "metadata": {},
   "outputs": [],
   "source": [
    "a = [0, 1, 0]\n",
    "b = [1, 1, 1]\n",
    "c = [1, 1, 0]\n",
    "\n",
    "[ sum(vals) % 2 for vals in zip(a,b,c) ] #a+b+c"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4c3a8956",
   "metadata": {},
   "source": [
    "## Using stream cipher for encoding"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0e55db7f",
   "metadata": {},
   "outputs": [],
   "source": [
    "plain = 'AHOJ'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1d1cb712",
   "metadata": {},
   "outputs": [],
   "source": [
    "plain.encode('ascii')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e2af24cf",
   "metadata": {},
   "outputs": [],
   "source": [
    "plain_bytes = plain.encode('ascii')\n",
    "print(*plain_bytes)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "da3dbfd8",
   "metadata": {},
   "outputs": [],
   "source": [
    "text1 = 'AHOJ'.encode('ascii')\n",
    "text2 = 'TEXT'.encode('ascii')\n",
    "print([ a ^ b for a,b in zip(text1, text2)]) #ascii XOR\n",
    "for a, b in zip(text1, text2):\n",
    "    print(f'{a:08b}')\n",
    "    print(f'{b:08b}')\n",
    "    print('-'*8)\n",
    "    print(f'{a ^ b:08b}')\n",
    "    print()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d16688bc",
   "metadata": {},
   "outputs": [],
   "source": [
    "from itertools import zip_longest\n",
    "stream = [1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1]\n",
    "stream = \"110001110001110001110001110001\"\n",
    "\n",
    "#convert input to list of bytes\n",
    "[int(\"\".join([str(bit) for bit in x]),2) for x in zip_longest(*([iter(stream)] * 8), fillvalue=\"\")] "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9146863a",
   "metadata": {},
   "outputs": [],
   "source": [
    "bin(28)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0e0f4d37",
   "metadata": {},
   "outputs": [],
   "source": [
    "bin(28)[2:].zfill(8)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3adce189",
   "metadata": {},
   "outputs": [],
   "source": [
    "'{:08b}'.format(28)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ecb01b78",
   "metadata": {},
   "outputs": [],
   "source": [
    "f'{28:08b}' #Python 3.6+"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "faf350dc",
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\".join(f'{value:08b}' for value in [199, 28, 113, 49])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b44846e4",
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\".join(chr(value) for value in [199, 28, 113, 49])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0f3c727a",
   "metadata": {},
   "outputs": [],
   "source": [
    "plain = 'Rastislav (KRS2026) Krivos-Bellus' #use YOUR names"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1261b928",
   "metadata": {},
   "source": [
    "## Berlekamp-Massey algorithm\n",
    "\n",
    "https://crypto.stanford.edu/~mironov/cs359/massey.pdf"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c8a8eed0",
   "metadata": {},
   "outputs": [],
   "source": [
    "def berlekamp_massey(sequence):\n",
    "    ...\n",
    "    return (IV, coef) #minimal LFSR"
   ]
  },
  {
   "attachments": {
    "image-2.png": {
     "image/png": "iVBORw0KGgoAAAANSUhEUgAAAfwAAAAoCAYAAAAbikieAAAZHUlEQVR4Xu2dZZTcxhJG22FmZmZmZmZmZmZOnDgnzMzMicPscBxmZmZmZr9z5S292tqWRjOCge37x9bMrNSSWv1VVVe1+gwaNGiQCwQCgUAg0NH0CYIfCAQCgUDnEwQ/EAgEAoFeQBD8QCAQCAR6AUHwA4FAIBCokL/++sv9+OOP0RHHHnvsyo4cBD8QCAQCgQq57bbb3Morrxwdscq8+SD4gUCF9OvXz3322Wc9jjjHHHO47bbbLt7W/PLLL26PPfaIt5NYb7313BJLLBFvdxpXX321u//++7ud1sEHH+wmmmiieDsQqJo///zT7bzzzomHPffcc+P/C1rwd9ppJ3faaad1fVMuTRX8gw46yF111VVunHHGcY8//njcqECgE0HsjzrqqCicZxlxxBHdGWec4TbddNP4M+Gbb77JFPY79dRTUweedubuu+9266yzThwGFRD7jz/+ON4OdB4YvLPOOqs74IAD3JZbbtlSJ7jaaqu5F1980X3wwQeJ7ZpiiikiQ/y8886LP/vtt9/cbrvtFn02xBBDuC222KLb92VRiuD/+++/7rvvvqs5SOHRnHPOOdH/N9hgA3fFFVd0fVMuTz31lJt33nnjbctHH33kJp544nhb8+mnn3o9Cqy1U045xS299NLu3XffjbY5n5FHHjn+TZn8+uuv7uuvv3YTTjihG3rooePPm4W0B4YccsjE69mbQPCPPPJI9/fffyee9ssvv+xmmmmmeBu+//77KAIg+AaXEUYYwZ155pleg6ETuOuuu6LxguiINZhKGMIqYffdd3cnn3yy+/DDD90kk0xSyTHbEQSRe4yD2LdvXzfMMMM0/TQQbO4f3jvts/eP77/66qt4G4477ji31157xduw6qqrultuuSX6PwbNIYccUur5FS74N954YySY11xzjdt2223jzyeffHK3yCKLxNtw0UUXRVbOTz/95Oaff3538cUXu2mmmSb+viz69OkT/9/HaKON5m6++eYe7YX999/fHX300fF2GmeddVZimLZIfv/996gjMeAjKLSx2fTv39+tu+66UTNGGWWUqFMvuuiizW5W02HQ+uSTT6J2PPnkk+6NN97o1iaf4Ft0/11llVXc6KOP7uacc86O9e41hPAPO+yweBsKHsIqQwSf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"
    },
    "image.png": {
     "image/png": "iVBORw0KGgoAAAANSUhEUgAAAlUAAAA0CAYAAACjMc1kAAAeuUlEQVR4Xu2dBdAcRROGlx93grsHCO4Q3N3dLcHdpXAJ7q4BCnd3DxLc3d3d/a9nQy/9NTMrd3uf9lP1VXK3u+M7805Pz9xQf//999+J4ziO4ziO0xRDuahyHMdxWsUPP/yQ/PHHH8kwwwyTjDLKKNn3jtMdcVHlOE635fnnn08mnXTSZPTRR29JHgn/zTffzD4LK6+8cjLUUENln3sqr776arLWWmul5dSnT5/kiiuuSKaffnovG6cyX3zxRfo37bTTdur246LKcZxuAQP3eeedl2iPhgceeCAdzBFWAwYMqLUzJr5NNtkkeeqpp7LvhD333DM5/PDDa42vq4HY3HDDDZPBgwdndbLffvslBx54YPK///2vq2XH6WDWWWed5N13300GDhzYqYVVh4mqWWedNVl00UWT448/vkOq6vvvv08WXHDBZPfdd0/WX3/9DkmD5d5770123nnn7LMlr7xmm222NoOJZdRRR00HmJ7cyTvdl9lnnz35+uuvk3feeSeYSQbxmWaaKdltt93S973se3DMMcckK6ywQjLddNNl3wnXX399stpqqyV//fVX9p2AZeyrr77q0eLh/vvvTxZffPHkzz//zMrFRZXTCHPNNVfy5JNPpmPcnXfembarsu9we1ObqPrmm2+y/wMZzjO5S4Ewe9xjjz2SoYce+p8rrYFsXnLJJckGG2yQfVcGRMxOO+2UfRZQzFNMMUUbIbPccssl11xzTTL88MNn31XhuuuuSzvpWJUQ/tVXX52MMMII2Xew0EILJYMGDco+x1hvvfWSiy66qNM2Rief33//Pfnxxx/T/48xxhj/fNtzoTx23HHH5IwzzkjfGXx2TjzxxGTrrbf+Txvv1atX1kfdd9996Ttj77Fo0YRPUF4fpcMHF1VJ8tBDDyVLLbVU8tNPP6VlQvkdfPDByV577dWjxaZTHcbU3377Lf1/txdV4lOw6qqr/pP9IYw00kjJxRdfnP4fC4sVWLpDO+6443ItNHXAYLTYYosljz32WPZdEeOPP35y1llnJSuuuGL2HWBRIiyYccYZk/feey/57rvv0s8xEVaGRx99NF0yIHwJzxILn/JnYKE+3nrrrex70jfVVFMlI488sguqLg6DvNQzs/+evISCoDrggAOSI444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"
    }
   },
   "cell_type": "markdown",
   "id": "6f5562d9",
   "metadata": {},
   "source": [
    "![image.png](attachment:image.png)\n",
    "![image-2.png](attachment:image-2.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "accb8a36",
   "metadata": {},
   "outputs": [],
   "source": [
    "for n in range(len(s)): #Step 2\n",
    "    ..."
   ]
  },
  {
   "attachments": {
    "image.png": {
     "image/png": "iVBORw0KGgoAAAANSUhEUgAAAVoAAABUCAYAAADDLP+yAAARk0lEQVR4Xu2dB6wVxfuGBxsW7A07FixYwBIVG7ZYUCQ21BhsUWMu0WAHO6hEsaNiFBXRBLEjEhULWLH3ir0iijUYCxZ+eebvnP/cubun7p6z5+z7JCTcs+dsnX3323e++abTvHnz5hkhhBCp0UlCK4QQ6SKhFUKIlJHQCiFEykhohRAiZSS0QgiRMhJaIYRIGQmtEEKkjIRWCCFSRkIrRIX8/PPP9l8U3bp1K/xfCIeEVogKGT58uBk2bJj5999/C585Jk+ebPr27Ws6depU+EwICa0QFVJMaHfYYQczdepUM//88xc+E0JCK0SFOOtgnXXWMf/880/h848++sgstthipmvXroXPhAAJrRBVMGvWLLPqqqsWhPaxxx4zO++8sywDEYmEVogq2GWXXcy0adMMxe822WQTM378eNOjRw8JrYhEQitEFfhCe/bZZ5vzzjvPzDfffIXlQvhIaIWoAgmtqAQJrRAV4mcddO7c2YwYMcIMHjy4ENHSUbbXXnuZ5557zsydO9cssMAC//1S5BUJrRAVQCdYW1ubmThxorUNLrzwQjNkyJCCyPrLBw4caMaOHStLQUhohaiEJ5980toGLtvgr7/+ahex+svDZSK/KKIVogJI6fr6668Lf4di6i8Pl4n8IqEVogzCvFkHgxQYBXbooYdaT9bNdaq8WuEjoRWiDMK82V69etmc2VtvvbUgro5mzqudMmWK2W233ZQPnDASWiHKAAH65ptv7P979uxZENpx48Z1EFp/ebMhoU0HCa0QQqSMhFYIIVJGQiuEECkjoRVCiJSR0Ipcs/baa3dI2QKqcfXu3btDzdmQjz/+uJDeNX369A4dYyGkg5Fb677/4YcfKtc2B0hoRa5ZcMEFzd9//92wc6AZGfKBhFbkmj59+pinn366ZCSaFhLafCChFbnmq6++Mscee6x5+OGHI8X2sssuS6wozMknn9xhnrFVVlnFXH/99ZrQscWR0Ircw/Da/v37m5deeqmD2CZZr+CNN94wm2++eQfPV4XDWx8JrRD/sdxyy5kffvih3fnYZpttzCOPPGInXUyKkSNHmrPOOsuKuEO1EVobCa0QHlGdYwMGDDBXX321WWGFFQqf1co555xja9k6K0FC29pIaEVdOemkk6y4DBo0yHTv3r2u2y6HE044wVx77bUdvNT99tvP3HnnnTaVKylGjRplTjzxRLutNNYvskOmhXbOnDm2Vzhs9I5XX301sY4KUR/wO/EoyV+dMWNG5oTl119/NaeddprtoArbHVW5aHNJiuHNN99sjjnmGLutJP1gkS0yLbS//PKLrQFK44+CG1ZC2zwsu+yy5scffyzsMK/oSYpWktA59tRTT9n5v3zS8mwvuugi891330loW5RMCy288MILthHSGzxp0qR2vcIS2ubDRbSQZaF1ROXZpuHZitYm80LrQ/TjXufISbzkkkuasuZnnmk2oQ3nCHPIUxWV0DRCe/DBB9vOAhdZvPLKK2azzTb77zBEM0AO6WuvvWavIR4o3mTWi2PHCS3IUxXl0jRCi6hyk8LCCy9snn/+eVvJXjQPvkf7+OOPm5122inzQgtRea8we/Zsm3ubRejXwJpZaqmlsrh7uaMphfbyyy83gwcPboqbVLTHWQfNJLQQ5r06nnjiCVuvICuR+f3332/fGNjft99+29x1112mR48eZv311696H8kOef/999v51Ntuu61ZfvnlC3+L4khom4RLL73Ujst3nHLKKTYjo9loVqEFP+/VQa2CG264wey5555VC1kSjB492pZcZB/Dh8GWW25pJ5Fcd911K95HykAedthh7Wb4hX333desttpqNujJauZIlsiU0IZ5s4svvrj1yLiYPKF///13+3meIloiJm5uIoo//vij0Ha4ad57772mS29rZqEFP+/VseKKK9pIEkGrVMiSAKHnwcv9A+zfcccdZ7baaqvCKLdqR54RCX/wwQftRNbnyCOPNGPGjJHYliAzQksjwU+iAePBdu7c2ebREimsscYatvOEi73PPvuYiRMnVtxgoghzJGsFD3L33Xc399xzj1lkkUUKn1fL559/btZaay17Trp06VJozDxw5s6da5ZccknreTZTLnGzC+2zzz5rp+P+7bff/rtK/08jOsfCzrq+ffvaNEjayNJLL12T0C600EL2mMKiN8OHDzfDhg2z7RLbBBuI6yriyYTQvvXWWzZB/NNPPzVdu3a1otqvXz/778EHH2z3NE1SaP10sSRJKuJ2+7fRRhvZY2Y0FRDhXnXVVVbMuYG23nrrmrdVL5pdaOGOO+4wxx9/vO0MczCQgQEO9X6NHjFihBVC146d2CO+06ZNK9w7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"
    }
   },
   "cell_type": "markdown",
   "id": "b47cf7b1",
   "metadata": {},
   "source": [
    "![image.png](attachment:image.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4baf2b38",
   "metadata": {},
   "outputs": [],
   "source": [
    "d = s[n]\n",
    "for i in range(1, l+1):\n",
    "    d ^= c[i] & s[n-i]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5f120dad",
   "metadata": {},
   "outputs": [],
   "source": [
    "def berlekamp_massey(sequence):\n",
    "    \"\"\"\n",
    "    Run Berlekamp-Maddey algorithm over GF(2) on the given binary sequence.\n",
    "\n",
    "    Returns:\n",
    "        (IV, taps)\n",
    "        - IV: list of the first L bits (initial state of the LFSR)\n",
    "        - taps: list of length L with coefficients c1..cL\n",
    "                (s_t = sum_{i=1..L} c_i * s_{i} mod 2)\n",
    "    \"\"\"\n",
    "    s = list(map(int, sequence))\n",
    "\n",
    "    # Polynomial c(x) (connection), initialized to 1\n",
    "    c = [1]  # coefficients, c[0] corresponds to x^0\n",
    "    # Previous c for potential replacement\n",
    "    b = [1]\n",
    "\n",
    "    l = 0        # current LFSR length\n",
    "    m = 1        # spacing since last update of L\n",
    "    # Note: in GF(2), discrepancies d are either 0 or 1; we don't need 'b' explicitly\n",
    "\n",
    "    for n in range(len(s)):\n",
    "        # Compute discrepancy d at position n\n",
    "        d = s[n]\n",
    "        for i in range(1, l + 1):\n",
    "            d ^= c[i] & s[n-i]\n",
    "        if d == 0:\n",
    "            m += 1\n",
    "        else:\n",
    "            t = c.copy()\n",
    "            # Ensure c has enough length to XOR with b shifted by m\n",
    "            c += [0] * (len(b) + m - len(c))\n",
    "            # c = c XOR (b shifted by m)\n",
    "            for i in range(len(b)):\n",
    "                c[i + m] ^= b[i]\n",
    "\n",
    "            if 2 * l <= n:\n",
    "                l = n + 1 - l\n",
    "                b = t\n",
    "                m = 1\n",
    "            else:\n",
    "                m += 1\n",
    "\n",
    "    #IV, coefficients (in reversed order)\n",
    "    return s[:l], c[-1:0:-1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6c6df412",
   "metadata": {},
   "outputs": [],
   "source": [
    "berlekamp_massey([1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ee59c785",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(list(islice(lfsr(*berlekamp_massey([1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1])), 30)))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cd262b05",
   "metadata": {},
   "source": [
    "## hex string"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ed4695ee",
   "metadata": {},
   "outputs": [],
   "source": [
    "str_hex = \"5398\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "868ca0de",
   "metadata": {},
   "outputs": [],
   "source": [
    "bytes.fromhex(str_hex)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bb95327c",
   "metadata": {},
   "outputs": [],
   "source": [
    "[x for x in bytes.fromhex(str_hex)]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b8f242bf",
   "metadata": {},
   "outputs": [],
   "source": [
    "import binascii\n",
    "\n",
    "binascii.unhexlify(str_hex)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b1318221",
   "metadata": {},
   "outputs": [],
   "source": [
    "[int(str_hex[i:i+2], 16) for i in range(0, len(str_hex), 2)]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "48ed731e",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
