{"id":24418702,"url":"https://github.com/littlefs-project/ramrsbd","last_synced_at":"2025-10-26T21:16:15.817Z","repository":{"id":258665517,"uuid":"869619739","full_name":"littlefs-project/ramrsbd","owner":"littlefs-project","description":"An example of a Reed-Solomon based error-correcting block device backed by RAM","archived":false,"fork":false,"pushed_at":"2024-10-31T19:37:21.000Z","size":191,"stargazers_count":6,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-12T06:04:14.963Z","etag":null,"topics":["embedded","filesystem","microcontroller"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/littlefs-project.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-10-08T15:46:50.000Z","updated_at":"2025-03-20T16:45:48.000Z","dependencies_parsed_at":"2024-11-19T22:46:02.030Z","dependency_job_id":null,"html_url":"https://github.com/littlefs-project/ramrsbd","commit_stats":null,"previous_names":["geky/ramrsbd","littlefs-project/ramrsbd"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/littlefs-project%2Framrsbd","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/littlefs-project%2Framrsbd/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/littlefs-project%2Framrsbd/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/littlefs-project%2Framrsbd/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/littlefs-project","download_url":"https://codeload.github.com/littlefs-project/ramrsbd/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248525144,"owners_count":21118618,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["embedded","filesystem","microcontroller"],"created_at":"2025-01-20T09:12:15.510Z","updated_at":"2025-10-26T21:16:10.763Z","avatar_url":"https://github.com/littlefs-project.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"## ramrsbd\n\nAn example of a Reed-Solomon based error-correcting block device backed\nby RAM.\n\n```\ncorrupted:\n'                 ..:::::7::::b:.. '       '  8     .:::....:::.96a:9:859.e:.dfbed5e 94ab8 4c:.4\n      .     48 .:::::::::::::::::::..   '           :::::::::::::44ef696b 6d'6:e:79e5.748f.8fc'9\n 8      '    .::::::::::':::':::::::::.            8::::::::::' e75a6ac555.f8cf8.b:9'ed57ad4a ec\n           .:::::::::::::::::::::::::::. ' .  '  . ::::::::::   'c4ddf9fd:9.7e  ef 7a5c 7fa6'c4'\n          :::::'::::::7:::7.:::::::::::::    ... :: :':::'::'   bb'c:da5a7 d6b.87d5b57b 9f7a: a:\n         4::::::::::::::'::::::::::::::'' .. '::: '     '''     '7 aece5:de .68467c79fdd'49:8597\n         ::::::::::::::::::::::'::::''.:::::: '        .  .     fb6b.6:'44f4'dcdf5e'.fac..:6fa8e\n         :7:::::::::::::::::::::'' ..:::::''  '               ' dbaa4ba6e5a'c9 5a adb5e:7f8b554e\n  8     8:::::::::::::::::::'' ..:::::'' ..               .    .' 'be4d.dd4b6c64:a5e'ba4:b578d :\n   8 ..: ::::::::::::::''' ..:::::'' ..:::                      .a55f.cf:4':9.b8:44ea9c5bfd9c7e.\n 8.::::'  :::::::::'' ..::::::'' ..:::::::              .       f8.'c7d.c9b:49a'48a:7 5c:9cc.6:b\n :::'4    '7::'' ...::::::''.5.::::::::::        .              edfe94e8:.b4ff':c655 5d6f7f5d:7'\n::'         ...::::::6' ..:::::::::::::'       8  8             c88f.'96f8fbe5787bbad4f.bf:d9b\n     9...:::::::'' ..::::::::::'::::.:'              4          ebe5eecda4fdee9b5:58:ee f:cafe::\n'::::::::'''4.  ::::::::::::::::7::''    .  8                   ece:'b 6:7:a7.e5a'6a dfe'b967fe:\n                  '':::::::::::'''                        ..   '99ade89:df5 7b7b e475ad:c98efaac\n```\n\n```\ncorrected:\n                  ..:::::::::::...                  .:::....:::.96a:9:859.e:.dfbed5e 94ab   c:.4\n               .:::::::::::::::::::..               :::::::::::::44ef696b 6d'6:e:79e5.748f.afc'9\n             .::::::::::::::::::::::::.             ::::::::::' e75a6ac555.f8cf8.b:9'ad56ad5a ec\n           .:::::::::::::::::::::::::::.         . ::::::::::   'c4dcf9fd:9.7e  ef 7a5c67fa6'c4'\n          .::::::::::::::::::::::::::::::    ... :: :'::::::'   bb'c:da5a74d6b.87d5b57b 9f7a: a:\n          :::::::::::::::::::::::::::::'' .. '::: '     '''     '7 aece5:de .68467c:9fdd'c9:8597\n         :::::::::::::::::::::::::::''..::::: '                 fb6b.6:'44f4'dcdf5e'.fac..:6fa8e\n         :::::::::::::::::::::::'' ..:::::''                    dbaa4ba6e5''c9 5b.adb5e:6f8b554e\n         :::::::::::::::::::'' ..:::::'' .                      ' 'be4d.dd4b6c64:a5ea.a4:b578d :\n     ..: ::::::::::::::''' ..:::::'' ..:::                      .a55f.cf:4':9.b8:446a8c5bfd9c7e.\n  ..:::'  :::::::::'' ..::::::'' ..:::::::                      f8 'c6d.c9b:49a'48a:7 5c79cc.6:b\n :::'     ':::'' ...::::::''...::::::::::                       edfe94e8:.b4ff':c65585d'f7b5d:7'\n::'         ...::::::'' ..:::::::::::::'                        c88f:'96f8fbc5787bbad4f.bf:d9b8\n     ....:::::::'' ..:::::::::::::::::'                         ebe5eecda4fdee8b5:58:ee e:cafe::\n'::::::::'''    :::::::::::::::::::''                           ece:'b 6:7:af9e5a'6' dfe'b967fe:\n                  '':::::::::::'''                              99adf89:df5 7b7b e475ad:c98efaac\n```\n\nReed-Solomon codes are sort of the big brother to CRCs. Operating on\nbytes instead of bits, they are both flexible and powerful, capable of\ndetecting and correcting a configurable number of byte errors\nefficiently.\n\nAssuming `ecc_size` bytes of ECC, ramrsbd can correct `floor(ecc_size/2)`\nbyte errors in up to 255 byte codewords (yes, 255 bytes, not 256 :/).\n\nThe main tradeoff is they are quite a bit more complex mathematically,\nwhich adds some code, RAM, and brain cost.\n\nA quick comparison of current ram-ecc-bds:\n\n|            | code   | tables | stack | buffers  | runtime                  |\n|:-----------|-------:|-------:|------:|---------:|-------------------------:|\n| ramcrc32bd |  940 B |   64 B |  88 B |      0 B |      $O\\left(n^e\\right)$ |\n| ramrsbd    | 1506 B |  512 B | 128 B | n + 4e B | $O\\left(ne + e^2\\right)$ |\n\nSee also:\n\n- [littlefs][littlefs]\n- [ramcrc32bd][ramcrc32bd]\n\n## RAM?\n\nRight now, [littlefs's][littlefs] block device API is limited in terms of\ncomposability. While it would be great to fix this on a major API change,\nin the meantime, a RAM-backed block device provides a simple example of\nerror-correction that users may be able to reimplement in their own\nblock devices.\n\n## Testing\n\nTesting is a bit jank right now, relying on littlefs's test runner:\n\n``` bash\n$ git clone https://github.com/littlefs-project/littlefs -b v2.9.3 --depth 1\n$ make test -j\n```\n\n## Words of warning\n\nBefore we get into how the algorithm works, a couple words of warning:\n\n1. I'm not a mathematician! Some of the definitions here are a bit\n   handwavey, and I'm skipping over the history of [BCH][w-bch] codes,\n   [PGZ][w-pgz], [Euclidean methods][w-euclidean], etc. I'd encourage you\n   to also explore [Wikipedia][w-rs] and other relevant articles to learn\n   more.\n\n   My goal is to explain, to the best of my (limited) knowledge, how to\n   implement Reed-Solomon codes, and how/why they work.\n\n2. The following math relies heavily on [finite-fields][w-gf] (sometimes\n   called Galois-fields) and the related theory.\n\n   If you're not familiar with finite-fields, they are an abstraction we\n   can make over finite numbers (bytes for [GF(256)][w-gf256], bits for\n   [GF(2)][w-gf2]) that let us do most of math without worrying about\n   pesky things like integer overflow.\n\n   But there's not enough space here to fully explain how they work, so\n   I'd suggest reading some of the above articles first.\n\n3. This is some pretty intense math! Reed-Solomon has evolved over\n   several decades and many PhDs. Just look how many names are involved\n   in this thing. So don't get discouraged if it feels impenetrable.\n\n   Feel free to take a break to remind yourself that math is not real and\n   can not hurt you.\n\n   I'd also encourage you to use GitHub's table-of-contents to jump\n   around and keep track of where you are.\n\n## How it works\n\nLike CRCs, Reed-Solomon codes are implemented by concatenating the\nmessage with the remainder after division by a predetermined \"generator\npolynomial\", giving us a [systematic code][w-systematic-code].\n\nHowever, two important differences:\n\n1. Instead of using a binary polynomial in [GF(2)][w-gf2], we use a\n   polynomial in a higher-order [finite-field][w-gf], usually\n   [GF(256)][w-gf256] because operating on bytes is convenient.\n\n2. We intentionally construct the polynomial to tell us information about\n   any errors that may occur.\n\n   Specifically, we constrain our polynomial (and implicitly our\n   codewords) to evaluate to zero at $n$ fixed points. As long as we have\n   $e \\le \\frac{n}{2}$ errors, we can solve for errors using these fixed\n   points.\n\n   Note this isn't really possible with CRCs in GF(2), because the only\n   non-zero binary number is, well, 1. GF(256) has 255 non-zero elements,\n   which we'll see becomes quite important.\n\n### Constructing a generator polynomial\n\nOk, first step, constructing a generator polynomial.\n\nIf we want to correct $e$ byte-errors, we will need $n = 2e$ fixed\npoints. We can construct a generator polynomial $P(x)$ with $n$ fixed\npoints at $g^i$ where $i \u003c n$ like so:\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"P(x) = \\prod_{i=0}^{n-1} \\left(x - g^i\\right) = \\left(x-1\\right)\\left(x-g\\right)\\cdots\\left(x-g^{n-1}\\right)\"\n    src=\"https://latex.codecogs.com/svg.image?P%28x%29%20%3d%20%5cprod_%7bi%3d%30%7d%5e%7bn%2d%31%7d%20%5cleft%28x%20%2d%20g%5ei%5cright%29%20%3d%20%5cleft%28x%2d%31%5cright%29%5cleft%28x%2dg%5cright%29%5ccdots%5cleft%28x%2dg%5e%7bn%2d%31%7d%5cright%29\"\n\u003e\n\u003c/p\u003e\n\nWe could choose any arbitrary set of fixed points, but usually we choose\n$g^i$ where $g$ is a [generator][w-generator] in GF(256), since it\nprovides a convenient mapping of integers to unique non-zero elements in\nGF(256).\n\nNote that for any fixed point $g^i$:\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"g^i - g^i = 0\"\n    src=\"https://latex.codecogs.com/svg.image?g%5ei%20%2d%20g%5ei%20%3d%20%30\"\n\u003e\n\u003c/p\u003e\n\nAnd since multiplying anything by zero is zero, this will make our entire\nproduct zero. So for any fixed point $g^i$, $P(g^i)$ should evaluate to\nzero:\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"P(g^i) = 0\"\n    src=\"https://latex.codecogs.com/svg.image?P%28g%5ei%29%20%3d%20%30\"\n\u003e\n\u003c/p\u003e\n\nThis gets real nifty when you look at the definition of our Reed-Solomon\ncode for codeword $C(x)$ given a message $M(x)$:\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"C(x) = M(x) x^n - \\left(M(x) x^n \\bmod P(x)\\right)\"\n    src=\"https://latex.codecogs.com/svg.image?C%28x%29%20%3d%20M%28x%29%20x%5en%20%2d%20%5cleft%28M%28x%29%20x%5en%20%5cbmod%20P%28x%29%5cright%29\"\n\u003e\n\u003c/p\u003e\n\nAs is true with normal math, subtracting the remainder after division\ngives us a polynomial that is a multiple of $P(x)$. And since multiplying\nanything by zero is zero, for any fixed point $g^i$, $C(g^i)$ should also\nevaluate to zero:\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"C(g^i) = 0\"\n    src=\"https://latex.codecogs.com/svg.image?C%28g%5ei%29%20%3d%20%30\"\n\u003e\n\u003c/p\u003e\n\n#### Modeling errors\n\nOk, but what if there are errors?\n\nWe can think of introducing errors as adding an error polynomial $E(x)$\nto our original codeword, where $E(x)$ contains up to $e$ non-zero terms:\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"C'(x) = C(x) + E(x)\"\n    src=\"https://latex.codecogs.com/svg.image?C%27%28x%29%20%3d%20C%28x%29%20%2b%20E%28x%29\"\n\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"E(x) = \\sum_{j \\in E} E_j x^j = E_{j_0} x^{j_0} + E_{j_1} x^{j_1} + \\cdots + E_{j_{e-1}} x^{j_{e-1}}\"\n    src=\"https://latex.codecogs.com/svg.image?E%28x%29%20%3d%20%5csum_%7bj%20%5cin%20E%7d%20E_j%20x%5ej%20%3d%20E_%7bj_%30%7d%20x%5e%7bj_%30%7d%20%2b%20E_%7bj_%31%7d%20x%5e%7bj_%31%7d%20%2b%20%5ccdots%20%2b%20E_%7bj_%7be%2d%31%7d%7d%20x%5e%7bj_%7be%2d%31%7d%7d\"\n\u003e\n\u003c/p\u003e\n\nCheck out what happens if we plug in our fixed point $g^i$:\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"\\begin{aligned} C'(g^i) \u0026= C(g^i) + E(g^i) \\\\ \u0026= 0 + E(g^i) \\\\ \u0026= E(g^i) \\end{aligned}\"\n    src=\"https://latex.codecogs.com/svg.image?%5cbegin%7baligned%7d%20C%27%28g%5ei%29%20%26%3d%20C%28g%5ei%29%20%2b%20E%28g%5ei%29%20%5c%5c%20%26%3d%20%30%20%2b%20E%28g%5ei%29%20%5c%5c%20%26%3d%20E%28g%5ei%29%20%5cend%7baligned%7d\"\n\u003e\n\u003c/p\u003e\n\nThe original codeword drops out! Leaving us with an equation defined only\nby the error polynomial.\n\nWe call these evaluations our \"syndromes\" $S_i$, since they tell us\ninformation about the errors in our codeword:\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"S_i = C'(g^i) = E(g^i) = \\sum_{j \\in E} E_j g^{ji}\"\n    src=\"https://latex.codecogs.com/svg.image?S_i%20%3d%20C%27%28g%5ei%29%20%3d%20E%28g%5ei%29%20%3d%20%5csum_%7bj%20%5cin%20E%7d%20E_j%20g%5e%7bji%7d\"\n\u003e\n\u003c/p\u003e\n\nWe usually refer to the unknowns in this equation as the\n\"error-locations\" $X_j = g^j$, and the \"error-magnitudes\" $Y_j = E_j$:\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"S_i = C'(g^i) = E(g^i) = \\sum_{j \\in E} Y_j X_j^i\"\n    src=\"https://latex.codecogs.com/svg.image?S_i%20%3d%20C%27%28g%5ei%29%20%3d%20E%28g%5ei%29%20%3d%20%5csum_%7bj%20%5cin%20E%7d%20Y_j%20X_j%5ei\"\n\u003e\n\u003c/p\u003e\n\nNote that finding $X_j$ also gives us $j$, since $j = \\log_g X_j$. We\nusually write it this way just to avoid adding a bunch of $g^j$\neverywhere.\n\nIf we can figure out both the error-locations and error-magnitudes, we\nhave enough information to reconstruct our original codeword:\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"C(x) = C'(x) - \\sum_{j \\in E} Y_j x^j\"\n    src=\"https://latex.codecogs.com/svg.image?C%28x%29%20%3d%20C%27%28x%29%20%2d%20%5csum_%7bj%20%5cin%20E%7d%20Y_j%20x%5ej\"\n\u003e\n\u003c/p\u003e\n\n### Finding the error locations\n\nOk, let's say we received a codeword $C'(x)$ with $e$ errors. Evaluating\nat our fixed points $g^i$, where $i \u003c n$ and $n \\ge 2e$, gives us our\nsyndromes $S_i$:\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"S_i = C'(g^i) = E(g^i) = \\sum_{j \\in E} Y_j X_j^i\"\n    src=\"https://latex.codecogs.com/svg.image?S_i%20%3d%20C%27%28g%5ei%29%20%3d%20E%28g%5ei%29%20%3d%20%5csum_%7bj%20%5cin%20E%7d%20Y_j%20X_j%5ei\"\n\u003e\n\u003c/p\u003e\n\nThe next step is figuring out the error-locations $X_j$.\n\nTo help with this, we introduce a very special polynomial, the\n\"error-locator polynomial\" $\\Lambda(x)$:\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"\\Lambda(x) = \\prod_{j \\in E} \\left(1 - X_j x\\right) = \\left(1 - X_{j_0} x\\right)\\left(1 - X_{j_1} x\\right)\\cdots\\left(1 - X_{j_{e-1}} x\\right)\"\n    src=\"https://latex.codecogs.com/svg.image?%5cLambda%28x%29%20%3d%20%5cprod_%7bj%20%5cin%20E%7d%20%5cleft%28%31%20%2d%20X_j%20x%5cright%29%20%3d%20%5cleft%28%31%20%2d%20X_%7bj_%30%7d%20x%5cright%29%5cleft%28%31%20%2d%20X_%7bj_%31%7d%20x%5cright%29%5ccdots%5cleft%28%31%20%2d%20X_%7bj_%7be%2d%31%7d%7d%20x%5cright%29\"\n\u003e\n\u003c/p\u003e\n\nThis polynomial has some rather useful properties:\n\n1. For any error-location $X_j$, $\\Lambda(X_j^{-1})$ evaluates to zero:\n\n   \u003cp align=\"center\"\u003e\n   \u003cimg\n       alt=\"\\Lambda(X^{-1}) = 0\"\n       src=\"https://latex.codecogs.com/svg.image?%5cLambda%28X%5e%7b%2d%31%7d%29%20%3d%20%30\"\n   \u003e\n   \u003c/p\u003e\n\n   This is for similar reasons why $P(g^i) = 0$. For any error-location\n   $X_j$:\n\n   \u003cp align=\"center\"\u003e\n   \u003cimg\n       alt=\"\\begin{aligned} 1 - X_j X_j^{-1} \u0026= 1 - 1 \\\\ \u0026= 0 \\end{aligned}\"\n       src=\"https://latex.codecogs.com/svg.image?%5cbegin%7baligned%7d%20%31%20%2d%20X_j%20X_j%5e%7b%2d%31%7d%20%26%3d%20%31%20%2d%20%31%20%5c%5c%20%26%3d%20%30%20%5cend%7baligned%7d\"\n   \u003e\n   \u003c/p\u003e\n\n   And since multiplying anything by zero is zero, the product reduces to\n   zero.\n\n2. $\\Lambda(0)$ evaluates to one:\n\n   \u003cp align=\"center\"\u003e\n   \u003cimg\n       alt=\"\\Lambda(0) = 1\"\n       src=\"https://latex.codecogs.com/svg.image?%5cLambda%28%30%29%20%3d%20%31\"\n   \u003e\n   \u003c/p\u003e\n\n   This can be seen by plugging in 0:\n\n   \u003cp align=\"center\"\u003e\n   \u003cimg\n       alt=\"\\begin{aligned} \\Lambda(0) \u0026= \\prod_{j \\in E} \\left(1 - X_j \\cdot 0\\right) \\\\ \u0026= \\prod_{j \\in E} 1 \\\\ \u0026= 1 \\end{aligned}\"\n       src=\"https://latex.codecogs.com/svg.image?%5cbegin%7baligned%7d%20%5cLambda%28%30%29%20%26%3d%20%5cprod_%7bj%20%5cin%20E%7d%20%5cleft%28%31%20%2d%20X_j%20%5ccdot%20%30%5cright%29%20%5c%5c%20%26%3d%20%5cprod_%7bj%20%5cin%20E%7d%20%31%20%5c%5c%20%26%3d%20%31%20%5cend%7baligned%7d\"\n   \u003e\n   \u003c/p\u003e\n\n   This prevents trivial solutions and is what makes $\\Lambda(x)$ useful.\n\nWhat's _really_ interesting is that these two properties allow us to\nsolve for $\\Lambda(x)$ with only our syndromes $S_i$.\n\nWe know $\\Lambda(x)$ has $e$ roots, which means we can expand it into a\npolynomial with $e+1$ terms. We also know that $\\Lambda(0) = 1$, so the\nconstant term must be 1. Giving the coefficients of this expanded\npolynomial the arbitrary names\n$\\Lambda_1, \\Lambda_2, \\cdots, \\Lambda_e$, we find another definition for\n$\\Lambda(x)$:\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"\\Lambda(x) = 1 + \\sum_{k=1}^e \\Lambda_k x^k = 1 + \\Lambda_1 x + \\Lambda_2 x^2 + \\cdots + \\Lambda_e x^e\"\n    src=\"https://latex.codecogs.com/svg.image?%5cLambda%28x%29%20%3d%20%31%20%2b%20%5csum_%7bk%3d%31%7d%5ee%20%5cLambda_k%20x%5ek%20%3d%20%31%20%2b%20%5cLambda_%31%20x%20%2b%20%5cLambda_%32%20x%5e%32%20%2b%20%5ccdots%20%2b%20%5cLambda_e%20x%5ee\"\n\u003e\n\u003c/p\u003e\n\nNote this doesn't actually change our error-locator $\\Lambda(x)$, it\nstill has all of its original properties. For example, if we plug in\n$X_j^{-1}$ it should still evaluate to zero:\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"\\Lambda(X_j^{-1}) = 1 + \\sum_{k=1}^e \\Lambda_k X_j^{-k} = 0\"\n    src=\"https://latex.codecogs.com/svg.image?%5cLambda%28X_j%5e%7b%2d%31%7d%29%20%3d%20%31%20%2b%20%5csum_%7bk%3d%31%7d%5ee%20%5cLambda_k%20X_j%5e%7b%2dk%7d%20%3d%20%30\"\n\u003e\n\u003c/p\u003e\n\nAnd since multiplying anything by zero is zero, we can multiply this by,\nsay, $Y_j X_j^i$, and the result should still be zero:\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"Y_j X_j^i \\Lambda(X_j^{-1}) = Y_j X_j^i + \\sum_{k=1}^e \\Lambda_k Y_j X_j^{i-k} = 0\"\n    src=\"https://latex.codecogs.com/svg.image?Y_j%20X_j%5ei%20%5cLambda%28X_j%5e%7b%2d%31%7d%29%20%3d%20Y_j%20X_j%5ei%20%2b%20%5csum_%7bk%3d%31%7d%5ee%20%5cLambda_k%20Y_j%20X_j%5e%7bi%2dk%7d%20%3d%20%30\"\n\u003e\n\u003c/p\u003e\n\nWe can even add a bunch of these together and the result should still be\nzero:\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"\\sum_{j \\in E} Y_j X_j^i \\Lambda(X_j^{-1}) = \\sum_{j \\in E} \\left(Y_j X_j^i + \\sum_{k=1}^e \\Lambda_k Y_j X_j^{i-k}\\right) = 0\"\n    src=\"https://latex.codecogs.com/svg.image?%5csum_%7bj%20%5cin%20E%7d%20Y_j%20X_j%5ei%20%5cLambda%28X_j%5e%7b%2d%31%7d%29%20%3d%20%5csum_%7bj%20%5cin%20E%7d%20%5cleft%28Y_j%20X_j%5ei%20%2b%20%5csum_%7bk%3d%31%7d%5ee%20%5cLambda_k%20Y_j%20X_j%5e%7bi%2dk%7d%5cright%29%20%3d%20%30\"\n\u003e\n\u003c/p\u003e\n\nWait a second...\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"\\sum_{j \\in E} Y_j X_j^i \\Lambda(X_j^{-1}) = \\left(\\sum_{j \\in E} Y_j X_j^i\\right) + \\sum_{k=1}^e \\Lambda_k \\left(\\sum_{j \\in E} Y_j X_j^{i-k}\\right) = 0\"\n    src=\"https://latex.codecogs.com/svg.image?%5csum_%7bj%20%5cin%20E%7d%20Y_j%20X_j%5ei%20%5cLambda%28X_j%5e%7b%2d%31%7d%29%20%3d%20%5cleft%28%5csum_%7bj%20%5cin%20E%7d%20Y_j%20X_j%5ei%5cright%29%20%2b%20%5csum_%7bk%3d%31%7d%5ee%20%5cLambda_k%20%5cleft%28%5csum_%7bj%20%5cin%20E%7d%20Y_j%20X_j%5e%7bi%2dk%7d%5cright%29%20%3d%20%30\"\n\u003e\n\u003c/p\u003e\n\nAren't these our syndromes? $S_i = \\sum_{j \\in E} Y_j X_j^i$?\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"\\sum_{j \\in E} Y_j X_j^i \\Lambda(X_j^{-1}) = S_i + \\sum_{k=1}^e \\Lambda_k S_{i-k} = 0\"\n    src=\"https://latex.codecogs.com/svg.image?%5csum_%7bj%20%5cin%20E%7d%20Y_j%20X_j%5ei%20%5cLambda%28X_j%5e%7b%2d%31%7d%29%20%3d%20S_i%20%2b%20%5csum_%7bk%3d%31%7d%5ee%20%5cLambda_k%20S_%7bi%2dk%7d%20%3d%20%30\"\n\u003e\n\u003c/p\u003e\n\nThey are! We can rearrange this into an equation for $S_i$ using only our\ncoefficients $\\Lambda_k$ and $e$ previously seen syndromes\n$S_{i-1}, S_{i-2}, \\cdots, S_{i-e}$:\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"S_i = \\sum_{k=1}^e \\Lambda_k S_{i-k} = \\Lambda_1 S_{i-1} + \\Lambda_2 S_{i-2} + \\cdots + \\Lambda_e S_{i-e}\"\n    src=\"https://latex.codecogs.com/svg.image?S_i%20%3d%20%5csum_%7bk%3d%31%7d%5ee%20%5cLambda_k%20S_%7bi%2dk%7d%20%3d%20%5cLambda_%31%20S_%7bi%2d%31%7d%20%2b%20%5cLambda_%32%20S_%7bi%2d%32%7d%20%2b%20%5ccdots%20%2b%20%5cLambda_e%20S_%7bi%2de%7d\"\n\u003e\n\u003c/p\u003e\n\nIf we repeat this $e$ times, for syndromes\n$S_e, S_{e+1}, \\cdots, S_{n-1}$, we end up with $e$ equations and\n$e$ unknowns. A system that is, in theory, solvable:\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"\\begin{bmatrix} S_{e} \\\\ S_{e+1} \\\\ \\vdots \\\\ S_{n-1} \\end{bmatrix} = \\begin{bmatrix} S_{e-1} \u0026 S_{e-2} \u0026 \\cdots \u0026 S_0 \\\\ S_{e} \u0026 S_{e-1} \u0026 \\cdots \u0026 S_1 \\\\ \\vdots \u0026 \\vdots \u0026 \\ddots \u0026 \\vdots \\\\ S_{n-2} \u0026 S_{n-3} \u0026 \\cdots \u0026 S_{e-1} \\end{bmatrix} \\begin{bmatrix} \\Lambda_1 \\\\ \\Lambda_2 \\\\ \\vdots \\\\ \\Lambda_e \\end{bmatrix}\"\n    src=\"https://latex.codecogs.com/svg.image?%5cbegin%7bbmatrix%7d%20S_%7be%7d%20%5c%5c%20S_%7be%2b%31%7d%20%5c%5c%20%5cvdots%20%5c%5c%20S_%7bn%2d%31%7d%20%5cend%7bbmatrix%7d%20%3d%20%5cbegin%7bbmatrix%7d%20S_%7be%2d%31%7d%20%26%20S_%7be%2d%32%7d%20%26%20%5ccdots%20%26%20S_%30%20%5c%5c%20S_%7be%7d%20%26%20S_%7be%2d%31%7d%20%26%20%5ccdots%20%26%20S_%31%20%5c%5c%20%5cvdots%20%26%20%5cvdots%20%26%20%5cddots%20%26%20%5cvdots%20%5c%5c%20S_%7bn%2d%32%7d%20%26%20S_%7bn%2d%33%7d%20%26%20%5ccdots%20%26%20S_%7be%2d%31%7d%20%5cend%7bbmatrix%7d%20%5cbegin%7bbmatrix%7d%20%5cLambda_%31%20%5c%5c%20%5cLambda_%32%20%5c%5c%20%5cvdots%20%5c%5c%20%5cLambda_e%20%5cend%7bbmatrix%7d\"\n\u003e\n\u003c/p\u003e\n\nThis is where the $n=2e$ requirement comes from, and why we need $n=2e$\nsyndromes to solve for $e$ errors at unknown locations.\n\n#### Berlekamp-Massey\n\nOk that's the theory, but solving this system of equations efficiently is\nstill quite difficult.\n\nEnter [Berlekamp-Massey][w-bm].\n\nA key observation by Massey is that solving for $\\Lambda(x)$ is\nequivalent to constructing an [LFSR][w-lfsr] that generates the sequence\n$S_e, S_{e+1}, \\dots, S_{n-1}$ given the initial state\n$S_0, S_1, \\dots, S_{e-1}$:\n\n```\n.---- + \u003c- + \u003c- + \u003c- + \u003c--- ... --- + \u003c--.\n|     ^    ^    ^    ^              ^    |\n|    *Λ1  *Λ2  *Λ3  *Λ4     ...   *Λe-1 *Λe\n|     ^    ^    ^    ^              ^    ^\n|   .-|--.-|--.-|--.-|--.--     --.-|--.-|--.\n'-\u003e |Se-1|Se-2|Se-3|Se-4|   ...   | S1 | S0 | -\u003e Sn-1 Sn-2 ... S2+3 Se+2 Se+1 Se Se-1 Se-2 ... S3 S2 S1 S0\n    '----'----'----'----'--     --'----'----'\n```\n\nPretty wild huh.\n\nWe can describe such an LFSR with a [recurrence relation][w-recurrence-relation]\nthat might look a bit familiar:\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"L(i) = s_i = \\sum_{k=1}^{|L|} L_k s_{i-k} = L_1 s_{i-1} + L_2 s_{i-2} + \\cdots + L_{|L|} s_{i-|L|}\"\n    src=\"https://latex.codecogs.com/svg.image?L%28i%29%20%3d%20s_i%20%3d%20%5csum_%7bk%3d%31%7d%5e%7b%7cL%7c%7d%20L_k%20s_%7bi%2dk%7d%20%3d%20L_%31%20s_%7bi%2d%31%7d%20%2b%20L_%32%20s_%7bi%2d%32%7d%20%2b%20%5ccdots%20%2b%20L_%7b%7cL%7c%7d%20s_%7bi%2d%7cL%7c%7d\"\n\u003e\n\u003c/p\u003e\n\nBerlekamp-Massey relies on two key observations:\n\n1. If an LFSR $L(i)$ of size $|L|$ generates the sequence\n   $s_0, s_1, \\dots, s_{n-1}$, but fails to generate the sequence\n   $s_0, s_1, \\dots, s_{n-1}, s_n$, than an LFSR $L'(i)$ that _does_\n   generate the sequence must have a size of at least:\n\n   \u003cp align=\"center\"\u003e\n   \u003cimg\n       alt=\"|L'| \\ge n+1-|L|\"\n       src=\"https://latex.codecogs.com/svg.image?%7cL%27%7c%20%5cge%20n%2b%31%2d%7cL%7c\"\n   \u003e\n   \u003c/p\u003e\n\n   Massey's proof of this is very math heavy.\n\n   Consider the equation for our LFSR $L(i)$ at $n$:\n\n   \u003cp align=\"center\"\u003e\n   \u003cimg\n       alt=\"L(n) = \\sum_{k=1}^{|L|} L_k s_{n-k} \\ne s_n\"\n       src=\"https://latex.codecogs.com/svg.image?L%28n%29%20%3d%20%5csum_%7bk%3d%31%7d%5e%7b%7cL%7c%7d%20L_k%20s_%7bn%2dk%7d%20%5cne%20s_n\"\n   \u003e\n   \u003c/p\u003e\n\n   If we have another LFSR $L'(i)$ that generates\n   $s_{n-|L|}, s_{n-|L|+1}, \\cdots, s_{n-1}$, we can substitute it in for\n   $s_{n-k}$:\n\n   \u003cp align=\"center\"\u003e\n   \u003cimg\n       alt=\"\\begin{aligned} L(n) \u0026= \\sum_{k=1}^{|L|} L_k s_{n-k} \\\\ \u0026= \\sum_{k=1}^{|L|} L_k L'(n-k) \\\\ \u0026= \\sum_{k=1}^{|L|} L_k \\sum_{l=1}^{|L'|} L'_l s_{n-k-l} \\\\ \\end{aligned}\"\n       src=\"https://latex.codecogs.com/svg.image?%5cbegin%7baligned%7d%20L%28n%29%20%26%3d%20%5csum_%7bk%3d%31%7d%5e%7b%7cL%7c%7d%20L_k%20s_%7bn%2dk%7d%20%5c%5c%20%26%3d%20%5csum_%7bk%3d%31%7d%5e%7b%7cL%7c%7d%20L_k%20L%27%28n%2dk%29%20%5c%5c%20%26%3d%20%5csum_%7bk%3d%31%7d%5e%7b%7cL%7c%7d%20L_k%20%5csum_%7bl%3d%31%7d%5e%7b%7cL%27%7c%7d%20L%27_l%20s_%7bn%2dk%2dl%7d%20%5c%5c%20%5cend%7baligned%7d\"\n   \u003e\n   \u003c/p\u003e\n\n   Multiplication is distributive, so we can move our summations around:\n\n   \u003cp align=\"center\"\u003e\n   \u003cimg\n       alt=\"L(n) = \\sum_{l=1}^{|L'|} L'_l \\sum_{k=1}^{|L|} L_k s_{n-l-k}\"\n       src=\"https://latex.codecogs.com/svg.image?L%28n%29%20%3d%20%5csum_%7bl%3d%31%7d%5e%7b%7cL%27%7c%7d%20L%27_l%20%5csum_%7bk%3d%31%7d%5e%7b%7cL%7c%7d%20L_k%20s_%7bn%2dl%2dk%7d\"\n   \u003e\n   \u003c/p\u003e\n\n   And note that right summation looks a lot like $L(i)$. If $L(i)$\n   generates $s_{n-|L'|}, s_{n-|L'|+1}, \\cdots, s_{n-1}$, we can replace\n   it with $s_{n-k'}$:\n\n   \u003cp align=\"center\"\u003e\n   \u003cimg\n       alt=\"\\begin{aligned} L(n) \u0026= \\sum_{l=1}^{|L'|} L'_l \\sum_{k=1}^{|L|} L_k s_{n-l-k} \\\\ \u0026= \\sum_{l=1}^{|L'|} L'_l L(n-l) \\\\ \u0026= \\sum_{l=1}^{|L'|} L'_l s_{n-l} \\end{aligned}\"\n       src=\"https://latex.codecogs.com/svg.image?%5cbegin%7baligned%7d%20L%28n%29%20%26%3d%20%5csum_%7bl%3d%31%7d%5e%7b%7cL%27%7c%7d%20L%27_l%20%5csum_%7bk%3d%31%7d%5e%7b%7cL%7c%7d%20L_k%20s_%7bn%2dl%2dk%7d%20%5c%5c%20%26%3d%20%5csum_%7bl%3d%31%7d%5e%7b%7cL%27%7c%7d%20L%27_l%20L%28n%2dl%29%20%5c%5c%20%26%3d%20%5csum_%7bl%3d%31%7d%5e%7b%7cL%27%7c%7d%20L%27_l%20s_%7bn%2dl%7d%20%5cend%7baligned%7d\"\n   \u003e\n   \u003c/p\u003e\n   \n   Oh hey! That's the definition of $L'(i)$:\n\n   \u003cp align=\"center\"\u003e\n   \u003cimg\n       alt=\"L(n) = L'(n) = s_n\"\n       src=\"https://latex.codecogs.com/svg.image?L%28n%29%20%3d%20L%27%28n%29%20%3d%20s_n\"\n   \u003e\n   \u003c/p\u003e\n\n   So if $L'(i)$ generates $s_n$, $L(i)$ must also generate $s_n$.\n\n   The only way to make $L'(i)$ generate a different $s_n$ would be to\n   make $|L'| \\ge n+1-|L|$ so that $L(i)$ can no longer generate\n   $s_{n-|L'|}, s_{n-|L'|+1}, \\cdots, s_{n-1}$.\n\n2. Once we've found the best LFSR $L(i)$ for a given size $|L|$, its\n   definition provides an optimal strategy for changing only the last\n   element of the generated sequence.\n\n   This is assuming $L(i)$ failed of course. If $L(i)$ generated the\n   whole sequence our algorithm is done!\n\n   If $L(i)$ failed, we assume it correctly generated\n   $s_0, s_1, \\cdots, s_{n-1}$, but failed at $s_n$. We call the\n   difference from the expected symbol the discrepancy $d$:\n\n   \u003cp align=\"center\"\u003e\n   \u003cimg\n       alt=\"L(i) = \\sum_{k=1}^{|L|} L_k s_{i-k} = \\begin{cases} s_i \u0026 i = |L|, |L|+1, \\cdots, n-1 \\\\ s_i+d \u0026 i = n \\end{cases}\"\n       src=\"https://latex.codecogs.com/svg.image?L%28i%29%20%3d%20%5csum_%7bk%3d%31%7d%5e%7b%7cL%7c%7d%20L_k%20s_%7bi%2dk%7d%20%3d%20%5cbegin%7bcases%7d%20s_i%20%26%20i%20%3d%20%7cL%7c%2c%20%7cL%7c%2b%31%2c%20%5ccdots%2c%20n%2d%31%20%5c%5c%20s_i%2bd%20%26%20i%20%3d%20n%20%5cend%7bcases%7d\"\n   \u003e\n   \u003c/p\u003e\n\n   If we know $s_i$ (which requires a larger LFSR), we can rearrange this\n   to be a bit more useful. We call this our connection polynomial\n   $C(i)$:\n\n   \u003cp align=\"center\"\u003e\n   \u003cimg\n       alt=\"C(i) = d^{-1} \\cdot \\left(L(i) - s_i\\right) = \\begin{cases} 0 \u0026 i = |L|, |L|+1,\\cdots,n-1 \\\\ 1 \u0026 i = n \\end{cases}\"\n       src=\"https://latex.codecogs.com/svg.image?C%28i%29%20%3d%20d%5e%7b%2d%31%7d%20%5ccdot%20%5cleft%28L%28i%29%20%2d%20s_i%5cright%29%20%3d%20%5cbegin%7bcases%7d%20%30%20%26%20i%20%3d%20%7cL%7c%2c%20%7cL%7c%2b%31%2c%5ccdots%2cn%2d%31%20%5c%5c%20%31%20%26%20i%20%3d%20n%20%5cend%7bcases%7d\"\n   \u003e\n   \u003c/p\u003e\n\n   Now, if we have a larger LFSR $L'(i)$ with size $|L'| \\gt |L|$ and we\n   want to change only the symbol $s'_n$ by $d'$, we can add\n   $d' \\cdot C(i)$, and only $s'_n$ will be affected:\n\n   \u003cp align=\"center\"\u003e\n   \u003cimg\n       alt=\"L'(i) + d' \\cdot C(i) = \\begin{cases} s'_i \u0026 i = |L'|,|L'|+1,\\cdots,n-1 \\\\ s'_i + d' \u0026 i = n \\end{cases}\"\n       src=\"https://latex.codecogs.com/svg.image?L%27%28i%29%20%2b%20d%27%20%5ccdot%20C%28i%29%20%3d%20%5cbegin%7bcases%7d%20s%27_i%20%26%20i%20%3d%20%7cL%27%7c%2c%7cL%27%7c%2b%31%2c%5ccdots%2cn%2d%31%20%5c%5c%20s%27_i%20%2b%20d%27%20%26%20i%20%3d%20n%20%5cend%7bcases%7d\"\n   \u003e\n   \u003c/p\u003e\n\nIf you can wrap your head around those two observations, you have\nunderstood most of Berlekamp-Massey.\n\nThe actual algorithm itself is relatively simple:  \n  \n1. Using the current LFSR $L(i)$, generate the next symbol and calculate\n   the discrepancy $d$ between it and the expected symbol $s_n$:\n\n   \u003cp align=\"center\"\u003e\n   \u003cimg\n       alt=\"d = L(n) - s_n\"\n       src=\"https://latex.codecogs.com/svg.image?d%20%3d%20L%28n%29%20%2d%20s_n\"\n   \u003e\n   \u003c/p\u003e\n  \n2. If $d=0$, great! Move on to the next symbol.  \n  \n3. If $d \\ne 0$, we need to tweak our LFSR:\n\n   1. First check if our LFSR is big enough. If $n \\ge 2|L|$, we need a\n      bigger LFSR:\n\n      \u003cp align=\"center\"\u003e\n      \u003cimg\n          alt=\"|L'| = n+1-|L|\"\n          src=\"https://latex.codecogs.com/svg.image?%7cL%27%7c%20%3d%20n%2b%31%2d%7cL%7c\"\n      \u003e\n      \u003c/p\u003e\n\n      If we're changing the size, save the best LFSR at the current size\n      for future tweaks:\n\n      \u003cp align=\"center\"\u003e\n      \u003cimg\n          alt=\"C'(i) = d^{-1} \\cdot \\left(L(i) - s_i\\right)\"\n          src=\"https://latex.codecogs.com/svg.image?C%27%28i%29%20%3d%20d%5e%7b%2d%31%7d%20%5ccdot%20%5cleft%28L%28i%29%20%2d%20s_i%5cright%29\"\n      \u003e\n      \u003c/p\u003e\n\n   2. Now we can fix the LFSR by adding our last $C(i)$ (not $C'(i)$!),\n      shifting and scaling so only $s_n$ is affected:\n\n      \u003cp align=\"center\"\u003e\n      \u003cimg\n          alt=\"L'(i) = L(i) + d \\cdot C(i-(n-m))\"\n          src=\"https://latex.codecogs.com/svg.image?L%27%28i%29%20%3d%20L%28i%29%20%2b%20d%20%5ccdot%20C%28i%2d%28n%2dm%29%29\"\n      \u003e\n      \u003c/p\u003e\n\n      Where $m$ is the value of $n$ when we saved the last $C(i)$. If we\n      shift $C(i)$ every step of the algorithm, we usually don't need to\n      track $m$ explicitly.\n\nThis is all implemented in `ramrsbd_find_λ`.\n\n#### Solving binary LFSRs for fun\n\nTaking a step away from [GF(256)][w-gf256] for a moment, let's look at a\nsimpler LFSR in [GF(2)][w-gf2], aka binary.\n\nConsider this binary sequence generated by a minimal LFSR that I know and\nyou don't :)\n\n```\n1 1 0 0 1 1 1 1\n```\n\nCan you figure out the original LFSR?\n\n\u003cdetails\u003e\n\u003csummary\u003e\nClick here to solve with Berlekamp-Massey\n\u003c/summary\u003e\n\n---\n\nUsing Berlekamp-Massey:\n\nTo start, let's assume our LFSR is an empty LFSR that just spits out a\nstream of zeros. Not the most creative LFSR, but we have to start\nsomewhere!\n\n```\n|L0| = 0\nL0(i) = 0\nC0(i) = s_i\n\nL0 = 0 -\u003e Output:   0\n          Expected: 1\n                d = 1\n```\n\nOk, so immediately we see a discrepancy. Clearly our output is not a\nstring of all zeros, and we need _some_ LFSR:\n\n```\n|L1| = 0+1-|L0| = 1\nL1(i) = L0(i) + C0(i-1) = s_i-1\nC1(i) = s_i + L0(i) = s_i\n\n     .-----.\n     |     |\n     |   .-|--.\nL1 = '-\u003e | 1  |-\u003e Output:   1 1\n         '----'   Expected: 1 1\n                        d = 0\n```\n\nThat's looking much better. This little LFSR will actually get us\ndecently far into the sequence:\n\n```\n|L2| = |L1| = 1\nL2(i) = L1(i) = s_i-1\nC2(i) = C1(i-1) = s_i-1\n\n     .-----.\n     |     |\n     |   .-|--.\nL2 = '-\u003e | 1  |-\u003e Output:   1 1 1\n         '----'   Expected: 1 1 1\n                        d = 0\n```\n\n```\n|L3| = |L2| = 1\nL3(i) = L2(i) = s_i-1\nC3(i) = C2(i-1) = s_i-2\n\n     .-----.\n     |     |\n     |   .-|--.\nL3 = '-\u003e | 1  |-\u003e Output:   1 1 1 1\n         '----'   Expected: 1 1 1 1\n                        d = 0\n```\n\n```\n|L4| = |L3| = 1\nL4(i) = L3(i) = s_i-1\nC4(i) = C3(i-1) = s_i-3\n\n     .-----.\n     |     |\n     |   .-|--.\nL4 = '-\u003e | 1  |-\u003e Output:   1 1 1 1 1\n         '----'   Expected: 0 1 1 1 1\n                        d = 1\n```\n\nAh! A discrepancy!\n\nWe're now at step 4 with only a 1-bit LFSR. $4 \\ge 2\\cdot1$, so a bigger\nLFSR is needed.\n\nResizing our LFSR to $4+1-1 = 4$, we can then add $C(i-1)$ to fix the\ndiscrepancy, save the previous LFSR as the new $C(i)$, and continue:\n\n```\n|L5| = 4+1-|L4| = 4\nL5(i) = L4(i) + C4(i-1) = s_i-1 + s_i-4\nC5(i) = s_i + L4(i) = s_i + s_i-1\n\n     .---- + \u003c------------.\n     |     ^              |\n     |   .-|--.----.----.-|--.   Expected: 0 0 1 1 1 1\nL5 = '-\u003e | 1  | 1  | 1  | 1  |-\u003e Output:   1 0 1 1 1 1\n         '----'----'----'----'         d = 1\n```\n\nAnother discrepancy! This time we don't need to resize the LFSR, just add\nthe shifted $C(i-1)$.\n\nThanks to math, we know this should have no affect on any of the\npreviously generated symbols, but feel free to regenerate the sequence to\nprove this to yourself. This property is pretty unintuitive!\n\n```\n|L6| = |L5| = 4\nL6(i) = L5(i) + C5(i-1) = s_i-2 + s_i-4\nC6(i) = C5(i-1) = s_i-1 + s_i-2\n\n     .--------- + \u003c-------.\n     |          ^         |\n     |   .----.-|--.----.-|--.\nL6 = '-\u003e | 1  | 1  | 1  | 1  |-\u003e Output:   1 0 0 1 1 1 1\n         '----'----'----'----'   Expected: 1 0 0 1 1 1 1\n                                       d = 0\n```\n\nNo discrepancy this time, let's keep going:\n\n```\n|L7| = |L6| = 4\nL7(i) = L6(i) = s_i-2 + s_i-4\nC7(i) = C6(i-1) = s_i-2 + s_i-3\n\n     .--------- + \u003c-------.\n     |          ^         |\n     |   .----.-|--.----.-|--.\nL7 = '-\u003e | 1  | 1  | 1  | 1  |-\u003e Output:   1 1 0 0 1 1 1 1\n         '----'----'----'----'   Expected: 1 1 0 0 1 1 1 1\n                                       d = 0\n```\n\nAnd now that we've generated the whole sequence, we have our LFSR:\n\n```\n|L8| = |L7| = 4\nL8(i) = L7(i) = s_i-2 + s_i-4\n\n     .--------- + \u003c-------.\n     |          ^         |\n     |   .----.-|--.----.-|--.\nL8 = '-\u003e | 1  | 1  | 1  | 1  |-\u003e Output:   1 1 0 0 1 1 1 1\n         '----'----'----'----'   Expected: 1 1 0 0 1 1 1 1\n```\n\n---\n\n\u003c/details\u003e\n\n#### bm-lfsr-solver.py\n\nIn case you want to play around with this algorithm more (and for my own\nexperiments), I've ported this LFSR solver to Python in\n[bm-lfsr-solver.py][bm-lfsr-solver.py]. Feel free to try your own binary\nsequences:\n\n``` bash\n$ ./bm-lfsr-solver.py 1 1 0 0 1 1 1 1\n\n... snip ...\n\n     .--------- + \u003c-------.\n     |          ^         |\n     |   .----.-|--.----.-|--.\nL8 = '-\u003e | 1  | 1  | 1  | 1  |-\u003e Output:   1 1 0 0 1 1 1 1\n         '----'----'----'----'   Expected: 1 1 0 0 1 1 1 1\n```\n\n```\n$ ./bm-lfsr-solver.py 01101000 01101001 00100001\n\n... snip ...\n\n      .---- + \u003c---------------- + \u003c- + \u003c- + \u003c------ + \u003c-------.\n      |     ^                   ^    ^    ^         ^         |\n      |   .-|--.----.----.----.-|--.-|--.-|--.----.-|--.----.-|--.----.\nL24 = '-\u003e | 1  | 0  | 0  | 1  | 0  | 0  | 1  | 0  | 0  | 0  | 0  | 1  |-\u003e Output:   0 1 1 0 1 0 0 0 0 1 1 0 1 0 0 1 0 0 1 0 0 0 0 1\n          '----'----'----'----'----'----'----'----'----'----'----'----'   Expected: 0 1 1 0 1 0 0 0 0 1 1 0 1 0 0 1 0 0 1 0 0 0 0 1\n```\n\nI've also implemented a similar script,\n[bm-lfsr256-solver.py][bm-lfsr256-solver.py], for full GF(256) LFSRs,\nthough it's a bit harder for follow unless you can do full GF(256)\nmultiplications in your head:\n\n```\n$ ./bm-lfsr256-solver.py 30 80 86 cb a3 78 8e 00\n\n... snip ...\n\n     .---- + \u003c- + \u003c- + \u003c--.\n     |     ^    ^    ^    |\n     |    *f0  *04  *df  *ea\n     |     ^    ^    ^    ^\n     |   .-|--.-|--.-|--.-|--.\nL8 = '-\u003e | a3 | 78 | 8e | 00 |-\u003e Output:   30 80 86 cb a3 78 8e 00\n         '----'----'----'----'   Expected: 30 80 86 cb a3 78 8e 00\n```\n\nIs Berlekamp-Massey a good compression algorithm? Probably not.\n\n#### Locating the errors\n\nComing back to Reed-Solomon, thanks to Berlekamp-Massey we can solve the\nfollowing recurrence for the terms $\\Lambda_k$ given at least $n \\ge 2e$\nsyndromes $S_i$:\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"\\Lambda(i) = S_i = \\sum_{k=1}^e \\Lambda_k S_{i-k} = \\Lambda_1 S_{i-1} + \\Lambda_2 S_{i-2} + \\cdots + \\Lambda_e S_{i-e}\"\n    src=\"https://latex.codecogs.com/svg.image?%5cLambda%28i%29%20%3d%20S_i%20%3d%20%5csum_%7bk%3d%31%7d%5ee%20%5cLambda_k%20S_%7bi%2dk%7d%20%3d%20%5cLambda_%31%20S_%7bi%2d%31%7d%20%2b%20%5cLambda_%32%20S_%7bi%2d%32%7d%20%2b%20%5ccdots%20%2b%20%5cLambda_e%20S_%7bi%2de%7d\"\n\u003e\n\u003c/p\u003e\n\nThese terms define our error-locator polynomial, which we can use to\nfind the locations of errors:\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"\\Lambda(x) = 1 + \\sum_{k=1}^e \\Lambda_k x^k = \\prod_{j \\in E} \\left(1 - X_j x\\right)\"\n    src=\"https://latex.codecogs.com/svg.image?%5cLambda%28x%29%20%3d%20%31%20%2b%20%5csum_%7bk%3d%31%7d%5ee%20%5cLambda_k%20x%5ek%20%3d%20%5cprod_%7bj%20%5cin%20E%7d%20%5cleft%28%31%20%2d%20X_j%20x%5cright%29\"\n\u003e\n\u003c/p\u003e\n\nAll we have left to do is figure out where $\\Lambda(X_j^{-1})=0$.\n\nThe easiest way to do this is just brute force: Just plug in every\nlocation $X_j=g^j$ in our codeword, and if $\\Lambda(X_j^{-1}) = 0$, we\nknow $X_j$ is the location of an error:\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"E = \\left\\{j \\mid \\Lambda(X_j^{-1}) = 0\\right\\}\"\n    src=\"https://latex.codecogs.com/svg.image?E%20%3d%20%5cleft%5c%7bj%20%5cmid%20%5cLambda%28X_j%5e%7b%2d%31%7d%29%20%3d%20%30%5cright%5c%7d\"\n\u003e\n\u003c/p\u003e\n\nWikipedia and other resources often mention an optimization called\n[Chien's search][w-chien] being applied here, but from reading up on the\nalgorithm it seems to only be useful for hardware implementations.  In\nsoftware Chien's search doesn't actually improve our runtime over brute\nforce with Horner's method and log tables ( $O(ne)$ vs $O(ne)$ ).\n\n### Finding the error magnitudes\n\nOnce we've found the error-locations $X_j$, the next step is to find the\nerror-magnitudes $Y_j$.\n\nThis step is relatively straightforward... sort of...\n\nRecall the definition of our syndromes $S_i$:\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"S_i = \\sum_{j \\in E} Y_j X_j^i\"\n    src=\"https://latex.codecogs.com/svg.image?S_i%20%3d%20%5csum_%7bj%20%5cin%20E%7d%20Y_j%20X_j%5ei\"\n\u003e\n\u003c/p\u003e\n\nWith $e$ syndromes, this can be rewritten as a system with $e$ equations\nand $e$ unknowns, which we can, in theory, solve for:\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"\\begin{bmatrix} S_0 \\\\ S_1 \\\\ \\vdots \\\\ S_{e-1} \\end{bmatrix} = \\begin{bmatrix} 1 \u0026 1 \u0026 \\dots \u0026 1 \\\\ X_{j_0} \u0026 X_{j_1} \u0026 \\dots \u0026 X_{j_{e-1}} \\\\ \\vdots \u0026 \\vdots \u0026 \\ddots \u0026 \\vdots \\\\ X_{j_0}^{e-1} \u0026 X_{j_1}^{e-1} \u0026 \\dots \u0026 X_{j_{e-1}}^{e-1} \\end{bmatrix} \\begin{bmatrix} Y_{j_0} \\\\ Y_{j_1} \\\\ \\vdots \\\\ Y_{j_{e-1}} \\end{bmatrix}\"\n    src=\"https://latex.codecogs.com/svg.image?%5cbegin%7bbmatrix%7d%20S_%30%20%5c%5c%20S_%31%20%5c%5c%20%5cvdots%20%5c%5c%20S_%7be%2d%31%7d%20%5cend%7bbmatrix%7d%20%3d%20%5cbegin%7bbmatrix%7d%20%31%20%26%20%31%20%26%20%5cdots%20%26%20%31%20%5c%5c%20X_%7bj_%30%7d%20%26%20X_%7bj_%31%7d%20%26%20%5cdots%20%26%20X_%7bj_%7be%2d%31%7d%7d%20%5c%5c%20%5cvdots%20%26%20%5cvdots%20%26%20%5cddots%20%26%20%5cvdots%20%5c%5c%20X_%7bj_%30%7d%5e%7be%2d%31%7d%20%26%20X_%7bj_%31%7d%5e%7be%2d%31%7d%20%26%20%5cdots%20%26%20X_%7bj_%7be%2d%31%7d%7d%5e%7be%2d%31%7d%20%5cend%7bbmatrix%7d%20%5cbegin%7bbmatrix%7d%20Y_%7bj_%30%7d%20%5c%5c%20Y_%7bj_%31%7d%20%5c%5c%20%5cvdots%20%5c%5c%20Y_%7bj_%7be%2d%31%7d%7d%20%5cend%7bbmatrix%7d\"\n\u003e\n\u003c/p\u003e\n\n#### Forney's algorithm\n\nBut again, solving this system of equations is easier said than done.\n\nEnter [Forney's algorithm][w-forney].\n\nAssuming we know an error-locator $X_j$, the following formula will spit\nout an error-magnitude $Y_j$:\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"Y_j = X_j \\frac{\\Omega(X_j^{-1})}{\\Lambda'(X_j^{-1})}\"\n    src=\"https://latex.codecogs.com/svg.image?Y_j%20%3d%20X_j%20%5cfrac%7b%5cOmega%28X_j%5e%7b%2d%31%7d%29%7d%7b%5cLambda%27%28X_j%5e%7b%2d%31%7d%29%7d\"\n\u003e\n\u003c/p\u003e\n\nWhere $\\Omega(x)$, called the \"error-evaluator polynomial\", is defined\nlike so:\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"\\Omega(x) = S(x) \\Lambda(x) \\bmod x^n\"\n    src=\"https://latex.codecogs.com/svg.image?%5cOmega%28x%29%20%3d%20S%28x%29%20%5cLambda%28x%29%20%5cbmod%20x%5en\"\n\u003e\n\u003c/p\u003e\n\n$S(x)$, the \"syndrome polynomial\", is defined like so (we just pretend\nour syndromes are a polynomial now):\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"S(x) = \\sum_{i=0}^{n-1} S_i x^i = S_0 + S_1 x + \\cdots + S_{n-1} x^{n-1}\"\n    src=\"https://latex.codecogs.com/svg.image?S%28x%29%20%3d%20%5csum_%7bi%3d%30%7d%5e%7bn%2d%31%7d%20S_i%20x%5ei%20%3d%20S_%30%20%2b%20S_%31%20x%20%2b%20%5ccdots%20%2b%20S_%7bn%2d%31%7d%20x%5e%7bn%2d%31%7d\"\n\u003e\n\u003c/p\u003e\n\nAnd $\\Lambda'(x)$, the [formal derivative][w-formal-derivative] of the\nerror-locator, can be calculated like so:\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"\\Lambda'(x) = \\sum_{k=1}^e k \\cdot \\Lambda_k x^{k-1} = \\Lambda_1 + 2 \\cdot \\Lambda_2 x + \\cdots + e \\cdot \\Lambda_e x^{e-1}\"\n    src=\"https://latex.codecogs.com/svg.image?%5cLambda%27%28x%29%20%3d%20%5csum_%7bk%3d%31%7d%5ee%20k%20%5ccdot%20%5cLambda_k%20x%5e%7bk%2d%31%7d%20%3d%20%5cLambda_%31%20%2b%20%32%20%5ccdot%20%5cLambda_%32%20x%20%2b%20%5ccdots%20%2b%20e%20%5ccdot%20%5cLambda_e%20x%5e%7be%2d%31%7d\"\n\u003e\n\u003c/p\u003e\n\nThough note $k$ is not a field element, so multiplication by $k$\nrepresents normal repeated addition. And since addition is xor in our\nfield, this really just cancels out every other term.\n\nThe end result is a simple formula for our error-magnitudes $Y_j$.\n\n#### WTF\n\nHaha, I know right? Where did this equation come from? Why does it work?\nHow did Forney even come up with this?\n\nI don't know the answer to most of these questions, there's very little\ninformation online about where/how/what this formula comes from.\n\nBut at the very least we can prove that it _does_ work.\n\n#### The error-evaluator polynomial\n\nLet's start with the syndrome polynomial $S(x)$:\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"S(x) = \\sum_{i=0}^{n-1} S_i x^i = S_0 + S_1 x + \\cdots + S_{n-1} x^{n-1}\"\n    src=\"https://latex.codecogs.com/svg.image?S%28x%29%20%3d%20%5csum_%7bi%3d%30%7d%5e%7bn%2d%31%7d%20S_i%20x%5ei%20%3d%20S_%30%20%2b%20S_%31%20x%20%2b%20%5ccdots%20%2b%20S_%7bn%2d%31%7d%20x%5e%7bn%2d%31%7d\"\n\u003e\n\u003c/p\u003e\n\nSubstituting in the definition of our syndromes\n$S_i = \\sum_{k \\in E} Y_k X_k^i x^i$:\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"\\begin{aligned} S(x) \u0026= \\sum_{i=0}^{n-1} \\sum_{k \\in E} Y_k X_k^i x^i \\\\ \u0026= \\sum_{k \\in E} \\left(Y_k \\sum_{i=0}^{n-1} X_k^i x^i\\right) \\end{aligned}\"\n    src=\"https://latex.codecogs.com/svg.image?%5cbegin%7baligned%7d%20S%28x%29%20%26%3d%20%5csum_%7bi%3d%30%7d%5e%7bn%2d%31%7d%20%5csum_%7bk%20%5cin%20E%7d%20Y_k%20X_k%5ei%20x%5ei%20%5c%5c%20%26%3d%20%5csum_%7bk%20%5cin%20E%7d%20%5cleft%28Y_k%20%5csum_%7bi%3d%30%7d%5e%7bn%2d%31%7d%20X_k%5ei%20x%5ei%5cright%29%20%5cend%7baligned%7d\"\n\u003e\n\u003c/p\u003e\n\nThe sum on the right turns out to be a [geometric series][w-geometric-series]:\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"S(x) = \\sum_{k \\in E} Y_k \\frac{1 - X_k^n x^n}{1 - X_k x}\"\n    src=\"https://latex.codecogs.com/svg.image?S%28x%29%20%3d%20%5csum_%7bk%20%5cin%20E%7d%20Y_k%20%5cfrac%7b%31%20%2d%20X_k%5en%20x%5en%7d%7b%31%20%2d%20X_k%20x%7d\"\n\u003e\n\u003c/p\u003e\n\nIf we then multiply with our error-locator polynomial\n$\\Lambda(x) = \\prod_{l \\in E} \\left(1 - X_l x\\right)$:\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"\\begin{aligned} S(x)\\Lambda(x) \u0026= \\sum_{k \\in E} \\left(Y_k \\frac{1 - X_k^n x^n}{1 - X_k x}\\right) \\cdot \\prod_{l \\in E} \\left(1 - X_l x\\right) \\\\ \u0026= \\sum_{k \\in E} \\left(Y_k \\left(1 - X_k^n x^n\\right) \\prod_{l \\ne k} \\left(1 - X_l x\\right)\\right) \\end{aligned}\"\n    src=\"https://latex.codecogs.com/svg.image?%5cbegin%7baligned%7d%20S%28x%29%5cLambda%28x%29%20%26%3d%20%5csum_%7bk%20%5cin%20E%7d%20%5cleft%28Y_k%20%5cfrac%7b%31%20%2d%20X_k%5en%20x%5en%7d%7b%31%20%2d%20X_k%20x%7d%5cright%29%20%5ccdot%20%5cprod_%7bl%20%5cin%20E%7d%20%5cleft%28%31%20%2d%20X_l%20x%5cright%29%20%5c%5c%20%26%3d%20%5csum_%7bk%20%5cin%20E%7d%20%5cleft%28Y_k%20%5cleft%28%31%20%2d%20X_k%5en%20x%5en%5cright%29%20%5cprod_%7bl%20%5cne%20k%7d%20%5cleft%28%31%20%2d%20X_l%20x%5cright%29%5cright%29%20%5cend%7baligned%7d\"\n\u003e\n\u003c/p\u003e\n\nWe see exactly one term in each summand cancel out, the term where\n$l = k$.\n\nAt this point, if we plug in $X_j^{-1}$, $S(X_j^{-1})\\Lambda(X_j^{-1})$\nstill evaluates to zero thanks to the error-locator polynomial\n$\\Lambda(x)$.\n\nBut if we expand the multiplication, something interesting happens:\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"S(x)\\Lambda(x) = \\sum_{k \\in E} \\left(Y_k \\prod_{l \\ne k} \\left(1 - X_l x\\right)\\right) - \\sum_{k \\in E} \\left(Y_k X_k^n x^n \\prod_{l \\ne k} \\left(1 - X_l x\\right)\\right)\"\n    src=\"https://latex.codecogs.com/svg.image?S%28x%29%5cLambda%28x%29%20%3d%20%5csum_%7bk%20%5cin%20E%7d%20%5cleft%28Y_k%20%5cprod_%7bl%20%5cne%20k%7d%20%5cleft%28%31%20%2d%20X_l%20x%5cright%29%5cright%29%20%2d%20%5csum_%7bk%20%5cin%20E%7d%20%5cleft%28Y_k%20X_k%5en%20x%5en%20%5cprod_%7bl%20%5cne%20k%7d%20%5cleft%28%31%20%2d%20X_l%20x%5cright%29%5cright%29\"\n\u003e\n\u003c/p\u003e\n\nOn the left side of the subtraction, all terms are $\\le$ degree\n$x^{e-1}$. On the right side of the subtraction, all terms are $\\ge$\ndegree $x^n$.\n\nImagine how these both contribute to the expanded form of the equation:\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"S(x)\\Lambda(x) = \\overbrace{\\Omega_0 + \\Omega_1 x + \\cdots + \\Omega_{e-1} x^{e-1}}^{\\sum_{k \\in E} \\left(Y_k \\prod_{l \\ne k} \\left(1 - X_l x\\right)\\right)} + \\overbrace{\\Omega_n x^n + \\Omega_{n+1} x^{n+1} + \\cdots + \\Omega_{n+e-1} x^{n+e-1}}^{\\sum_{k \\in E} \\left(Y_k X_k^n x^n \\prod_{l \\ne k} \\left(1 - X_l x\\right)\\right) }\"\n    src=\"https://latex.codecogs.com/svg.image?S%28x%29%5cLambda%28x%29%20%3d%20%5coverbrace%7b%5cOmega_%30%20%2b%20%5cOmega_%31%20x%20%2b%20%5ccdots%20%2b%20%5cOmega_%7be%2d%31%7d%20x%5e%7be%2d%31%7d%7d%5e%7b%5csum_%7bk%20%5cin%20E%7d%20%5cleft%28Y_k%20%5cprod_%7bl%20%5cne%20k%7d%20%5cleft%28%31%20%2d%20X_l%20x%5cright%29%5cright%29%7d%20%2b%20%5coverbrace%7b%5cOmega_n%20x%5en%20%2b%20%5cOmega_%7bn%2b%31%7d%20x%5e%7bn%2b%31%7d%20%2b%20%5ccdots%20%2b%20%5cOmega_%7bn%2be%2d%31%7d%20x%5e%7bn%2be%2d%31%7d%7d%5e%7b%5csum_%7bk%20%5cin%20E%7d%20%5cleft%28Y_k%20X_k%5en%20x%5en%20%5cprod_%7bl%20%5cne%20k%7d%20%5cleft%28%31%20%2d%20X_l%20x%5cright%29%5cright%29%20%7d\"\n\u003e\n\u003c/p\u003e\n\nIf we truncate this polynomial, $\\bmod x^n$ in math land, we can\neffectively delete part of this equation:\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"S(x)\\Lambda(x) \\bmod x^n = \\overbrace{\\Omega_0 + \\Omega_1 x + \\dots + \\Omega_{e-1} x^{e-1}}^{\\sum_{k \\in E} \\left(Y_k \\prod_{l \\ne k} \\left(1 - X_l x\\right)\\right)}\"\n    src=\"https://latex.codecogs.com/svg.image?S%28x%29%5cLambda%28x%29%20%5cbmod%20x%5en%20%3d%20%5coverbrace%7b%5cOmega_%30%20%2b%20%5cOmega_%31%20x%20%2b%20%5cdots%20%2b%20%5cOmega_%7be%2d%31%7d%20x%5e%7be%2d%31%7d%7d%5e%7b%5csum_%7bk%20%5cin%20E%7d%20%5cleft%28Y_k%20%5cprod_%7bl%20%5cne%20k%7d%20%5cleft%28%31%20%2d%20X_l%20x%5cright%29%5cright%29%7d\"\n\u003e\n\u003c/p\u003e\n\nGiving us an equation for the error-evaluator polynomial, $\\Omega(x)$:\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"\\Omega(x) = S(x)\\Lambda(x) \\bmod x^n = \\sum_{k \\in E} \\left(Y_k \\prod_{l \\ne k} \\left(1 - X_l x\\right)\\right)\"\n    src=\"https://latex.codecogs.com/svg.image?%5cOmega%28x%29%20%3d%20S%28x%29%5cLambda%28x%29%20%5cbmod%20x%5en%20%3d%20%5csum_%7bk%20%5cin%20E%7d%20%5cleft%28Y_k%20%5cprod_%7bl%20%5cne%20k%7d%20%5cleft%28%31%20%2d%20X_l%20x%5cright%29%5cright%29\"\n\u003e\n\u003c/p\u003e\n\nNote that $l \\ne k$ condition.\n\nThe error-evaluator polynomial $\\Omega(x)$ still contains a big chunk\nof our error-locator polynomial $\\Lambda(x)$, so if we plug in an\nerror-location $X_j^{-1}$, _most_ of the terms evaluate to zero.\nExcept one! The one where $j = k$:\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"\\begin{aligned} \\Omega(X_j^{-1}) \u0026= \\sum_{k \\in E} \\left(Y_k \\prod_{l \\ne k} \\left(1 - X_l X_j^{-1}\\right)\\right) \\\\ \u0026= Y_j \\prod_{l \\ne j} \\left(1 - X_l X_j^{-1}\\right) \\end{aligned}\"\n    src=\"https://latex.codecogs.com/svg.image?%5cbegin%7baligned%7d%20%5cOmega%28X_j%5e%7b%2d%31%7d%29%20%26%3d%20%5csum_%7bk%20%5cin%20E%7d%20%5cleft%28Y_k%20%5cprod_%7bl%20%5cne%20k%7d%20%5cleft%28%31%20%2d%20X_l%20X_j%5e%7b%2d%31%7d%5cright%29%5cright%29%20%5c%5c%20%26%3d%20Y_j%20%5cprod_%7bl%20%5cne%20j%7d%20%5cleft%28%31%20%2d%20X_l%20X_j%5e%7b%2d%31%7d%5cright%29%20%5cend%7baligned%7d\"\n\u003e\n\u003c/p\u003e\n\nAnd right there is our error-magnitude $Y_j$! Sure we end up with a\nbunch of extra gobbledygook, but $Y_j$ _is_ there.\n\nThe good news is that gobbledygook depends only on our error-locations\n$X_j$, which we _do_ know and can in theory remove with more math.\n\n#### The formal derivative of the error-locator polynomial\n\nBut Forney has one last trick up his sleeve: The\n[formal derivative][w-formal-derivative] of the error-locator polynomial,\n$\\Lambda'(x)$.\n\nWhat the heck is a formal derivative?\n\nWell we can't use normal derivatives in a finite-field like GF(256)\nbecause they depend on the notion of a limit which depends on the field\nbeing, well, not finite.\n\nBut derivatives are so useful mathematicians use them anyways.\n\nApplying a formal derivative looks a lot like a normal derivative in\nnormal math:\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"f(x) = \\sum_{i=0} f_i x^i = f_0 + f_1 x + \\cdots + f_i x^i\"\n    src=\"https://latex.codecogs.com/svg.image?f%28x%29%20%3d%20%5csum_%7bi%3d%30%7d%20f_i%20x%5ei%20%3d%20f_%30%20%2b%20f_%31%20x%20%2b%20%5ccdots%20%2b%20f_i%20x%5ei\"\n\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"f'(x) = \\sum_{i=1} i \\cdot f_i x^{i-1} = f_1 + 2 \\cdot f_2 x + \\dots + i \\cdot f_i x^{i-1}\"\n    src=\"https://latex.codecogs.com/svg.image?f%27%28x%29%20%3d%20%5csum_%7bi%3d%31%7d%20i%20%5ccdot%20f_i%20x%5e%7bi%2d%31%7d%20%3d%20f_%31%20%2b%20%32%20%5ccdot%20f_%32%20x%20%2b%20%5cdots%20%2b%20i%20%5ccdot%20f_i%20x%5e%7bi%2d%31%7d\"\n\u003e\n\u003c/p\u003e\n\nExcept $i$ here is not a finite-field element, so instead of doing\nfinite-field multiplication, we do normal repeated addition. And since\naddition is xor in our field, this just has the effect of canceling out\nevery other term.\n\nQuite a few properties of derivatives still hold in finite-fields. Of\nparticular interest to us is the [product rule][w-product-rule]:\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"\\left(f(x) g(x)\\right)' = f'(x) g(x) + f(x) g'(x)\"\n    src=\"https://latex.codecogs.com/svg.image?%5cleft%28f%28x%29%20g%28x%29%5cright%29%27%20%3d%20f%27%28x%29%20g%28x%29%20%2b%20f%28x%29%20g%27%28x%29\"\n\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"\\left(\\prod_{i=0}^{n-1} f_i(x)\\right)' = \\sum_{i=0}^{n-1} \\left(f_i'(x) \\prod_{j \\ne i} f_j(x)\\right)\"\n    src=\"https://latex.codecogs.com/svg.image?%5cleft%28%5cprod_%7bi%3d%30%7d%5e%7bn%2d%31%7d%20f_i%28x%29%5cright%29%27%20%3d%20%5csum_%7bi%3d%30%7d%5e%7bn%2d%31%7d%20%5cleft%28f_i%27%28x%29%20%5cprod_%7bj%20%5cne%20i%7d%20f_j%28x%29%5cright%29\"\n\u003e\n\u003c/p\u003e\n\nApplying this to our error-locator polynomial $\\Lambda(x)$:\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"\\Lambda(x) = 1 + \\sum_{k=1}^e \\Lambda_k x^k = 1 + \\Lambda_1 x + \\Lambda_2 x^2 + \\cdots + \\Lambda_e x^e\"\n    src=\"https://latex.codecogs.com/svg.image?%5cLambda%28x%29%20%3d%20%31%20%2b%20%5csum_%7bk%3d%31%7d%5ee%20%5cLambda_k%20x%5ek%20%3d%20%31%20%2b%20%5cLambda_%31%20x%20%2b%20%5cLambda_%32%20x%5e%32%20%2b%20%5ccdots%20%2b%20%5cLambda_e%20x%5ee\"\n\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"\\Lambda'(x) = \\sum_{k=1}^e k \\cdot \\Lambda_k x^{k-1} = \\Lambda_1 + 2 \\cdot \\Lambda_2 x + \\cdots + e \\cdot \\Lambda_e x^{e-1}\"\n    src=\"https://latex.codecogs.com/svg.image?%5cLambda%27%28x%29%20%3d%20%5csum_%7bk%3d%31%7d%5ee%20k%20%5ccdot%20%5cLambda_k%20x%5e%7bk%2d%31%7d%20%3d%20%5cLambda_%31%20%2b%20%32%20%5ccdot%20%5cLambda_%32%20x%20%2b%20%5ccdots%20%2b%20e%20%5ccdot%20%5cLambda_e%20x%5e%7be%2d%31%7d\"\n\u003e\n\u003c/p\u003e\n\nRecall the other definition of our error-locator polynomial $\\Lambda(x)$:\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"\\Lambda(x) = \\prod_{k \\in E} \\left(1 - X_k x\\right)\"\n    src=\"https://latex.codecogs.com/svg.image?%5cLambda%28x%29%20%3d%20%5cprod_%7bk%20%5cin%20E%7d%20%5cleft%28%31%20%2d%20X_k%20x%5cright%29\"\n\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"\\Lambda'(x) = \\left(\\prod_{k \\in E} \\left(1 - X_k x\\right)\\right)'\"\n    src=\"https://latex.codecogs.com/svg.image?%5cLambda%27%28x%29%20%3d%20%5cleft%28%5cprod_%7bk%20%5cin%20E%7d%20%5cleft%28%31%20%2d%20X_k%20x%5cright%29%5cright%29%27\"\n\u003e\n\u003c/p\u003e\n\nApplying the product rule:\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"\\Lambda'(x) = \\sum_{k \\in E} \\left(X_k \\prod_{l \\ne k} \\left(1 - X_l x\\right)\\right)\"\n    src=\"https://latex.codecogs.com/svg.image?%5cLambda%27%28x%29%20%3d%20%5csum_%7bk%20%5cin%20E%7d%20%5cleft%28X_k%20%5cprod_%7bl%20%5cne%20k%7d%20%5cleft%28%31%20%2d%20X_l%20x%5cright%29%5cright%29\"\n\u003e\n\u003c/p\u003e\n\nStarting to look familiar?\n\nJust like the error-evaluator polynomial $\\Omega(x)$, plugging in an\nerror-location $X_j^{-1}$ causes _most_ of the terms to evaluate to\nzero, except the one where $j = k$, revealing $X_j$ times our\ngobbledygook!\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"\\begin{aligned} \\Lambda'(X_j^{-1}) \u0026= \\sum_{k \\in E} \\left(X_k \\prod_{l \\ne k} \\left(1 - X_l X_j^{-1}\\right)\\right) \\\\ \u0026= X_j \\prod_{l \\ne j} \\left(1 - X_l X_j^{-1}\\right) \\end{aligned}\"\n    src=\"https://latex.codecogs.com/svg.image?%5cbegin%7baligned%7d%20%5cLambda%27%28X_j%5e%7b%2d%31%7d%29%20%26%3d%20%5csum_%7bk%20%5cin%20E%7d%20%5cleft%28X_k%20%5cprod_%7bl%20%5cne%20k%7d%20%5cleft%28%31%20%2d%20X_l%20X_j%5e%7b%2d%31%7d%5cright%29%5cright%29%20%5c%5c%20%26%3d%20X_j%20%5cprod_%7bl%20%5cne%20j%7d%20%5cleft%28%31%20%2d%20X_l%20X_j%5e%7b%2d%31%7d%5cright%29%20%5cend%7baligned%7d\"\n\u003e\n\u003c/p\u003e\n\n#### Evaluating the errors\n\nSo for a given error-location $X_j$, the error-evaluator polynomial\n$\\Omega(X_j^{-1})$ gives us the error-magnitude times some gobbledygook:\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"\\Omega(X_j^{-1}) = Y_j \\overbrace{\\prod_{l \\ne j} \\left(1 - X_l X_j^{-1}\\right)}^{\\text{gobbledygook}}\"\n    src=\"https://latex.codecogs.com/svg.image?%5cOmega%28X_j%5e%7b%2d%31%7d%29%20%3d%20Y_j%20%5coverbrace%7b%5cprod_%7bl%20%5cne%20j%7d%20%5cleft%28%31%20%2d%20X_l%20X_j%5e%7b%2d%31%7d%5cright%29%7d%5e%7b%5ctext%7bgobbledygook%7d%7d\"\n\u003e\n\u003c/p\u003e\n\nAnd the formal derivative of the error-locator polynomial $\\Lambda'(X_j^{-1})$\ngives us the error-location times the same gobbledygook:\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"\\Lambda'(X_j^{-1}) = X_j \\overbrace{\\prod_{l \\ne j} \\left(1 - X_l X_j^{-1}\\right)}^{\\text{gobbledygook}}\"\n    src=\"https://latex.codecogs.com/svg.image?%5cLambda%27%28X_j%5e%7b%2d%31%7d%29%20%3d%20X_j%20%5coverbrace%7b%5cprod_%7bl%20%5cne%20j%7d%20%5cleft%28%31%20%2d%20X_l%20X_j%5e%7b%2d%31%7d%5cright%29%7d%5e%7b%5ctext%7bgobbledygook%7d%7d\"\n\u003e\n\u003c/p\u003e\n\nIf we divide $\\Omega(X_j^{-1})$ by $\\Lambda'(X_j^{-1})$, all that\ngobbledygook cancels out, leaving us with a simply equation containing\nonly $Y_j$ and $X_j$:\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"\\frac{\\Omega(X_j^{-1})}{\\Lambda'(X_j^{-1})} = \\frac{Y_j \\prod_{l \\ne j} \\left(1 - X_l X_j^{-1}\\right)}{X_j \\prod_{l \\ne j} \\left(1 - X_l X_j^{-1}\\right)} = \\frac{Y_j}{X_j}\"\n    src=\"https://latex.codecogs.com/svg.image?%5cfrac%7b%5cOmega%28X_j%5e%7b%2d%31%7d%29%7d%7b%5cLambda%27%28X_j%5e%7b%2d%31%7d%29%7d%20%3d%20%5cfrac%7bY_j%20%5cprod_%7bl%20%5cne%20j%7d%20%5cleft%28%31%20%2d%20X_l%20X_j%5e%7b%2d%31%7d%5cright%29%7d%7bX_j%20%5cprod_%7bl%20%5cne%20j%7d%20%5cleft%28%31%20%2d%20X_l%20X_j%5e%7b%2d%31%7d%5cright%29%7d%20%3d%20%5cfrac%7bY_j%7d%7bX_j%7d\"\n\u003e\n\u003c/p\u003e\n\nAll that's left is to cancel out the $X_j$ term to get our\nerror-magnitude $Y_j$:\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"X_j \\frac{\\Omega(X_j^{-1})}{\\Lambda'(X_j^{-1})} = X_j \\frac{Y_j \\prod_{l \\ne j} \\left(1 - X_l X_j^{-1}\\right)}{X_j \\prod_{l \\ne j} \\left(1 - X_l X_j^{-1}\\right)} = Y_j\"\n    src=\"https://latex.codecogs.com/svg.image?X_j%20%5cfrac%7b%5cOmega%28X_j%5e%7b%2d%31%7d%29%7d%7b%5cLambda%27%28X_j%5e%7b%2d%31%7d%29%7d%20%3d%20X_j%20%5cfrac%7bY_j%20%5cprod_%7bl%20%5cne%20j%7d%20%5cleft%28%31%20%2d%20X_l%20X_j%5e%7b%2d%31%7d%5cright%29%7d%7bX_j%20%5cprod_%7bl%20%5cne%20j%7d%20%5cleft%28%31%20%2d%20X_l%20X_j%5e%7b%2d%31%7d%5cright%29%7d%20%3d%20Y_j\"\n\u003e\n\u003c/p\u003e\n\n### Putting it all together\n\nOnce we've figured out the error-locator polynomial $\\Lambda(x)$, the\nerror-evaluator polynomial $\\Omega(x)$, and the derivative of the\nerror-locator polynomial $\\Lambda'(x)$, we get to the fun part, fixing\nthe errors!\n\nFor each location $j$ in the malformed codeword $C'(x)$, calculate the\nerror-location $X_j = g^j$ and plug its inverse $X_j^{-1}$ into the\nerror-locator $\\Lambda(x)$. If $\\Lambda(X_j^{-1}) = 0$ we've found the\nlocation of an error!\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"E = \\left\\{j \\mid \\Lambda(X_j^{-1}) = 0\\right\\}\"\n    src=\"https://latex.codecogs.com/svg.image?E%20%3d%20%5cleft%5c%7bj%20%5cmid%20%5cLambda%28X_j%5e%7b%2d%31%7d%29%20%3d%20%30%5cright%5c%7d\"\n\u003e\n\u003c/p\u003e\n\nTo fix the error, plug the error-location $X_j$ and its inverse\n$X_j^{-1}$ into Forney's algorithm to find the error-magnitude\n$Y_j$. Xor $Y_j$ into the codeword to fix this error!\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"Y_j = X_j \\frac{\\Omega(X_j^{-1})}{\\Lambda'(X_j^{-1})}\"\n    src=\"https://latex.codecogs.com/svg.image?Y_j%20%3d%20X_j%20%5cfrac%7b%5cOmega%28X_j%5e%7b%2d%31%7d%29%7d%7b%5cLambda%27%28X_j%5e%7b%2d%31%7d%29%7d\"\n\u003e\n\u003c/p\u003e\n\nRepeat for all errors in the malformed codeword $C'(x)$, and with any\nluck we'll find the original codeword $C(x)$!\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"C(x) = C'(x) - \\sum_{j \\in E} Y_j x^j\"\n    src=\"https://latex.codecogs.com/svg.image?C%28x%29%20%3d%20C%27%28x%29%20%2d%20%5csum_%7bj%20%5cin%20E%7d%20Y_j%20x%5ej\"\n\u003e\n\u003c/p\u003e\n\nUnfortunately we're not _quite_ done yet. All of this math assumed we had\n$e \\le \\frac{n}{2}$ errors. If we had more errors, we might have just\nmade things worse.\n\nIt's worth recalculating the syndromes after fixing errors to see if we\ndid ended up with a valid codeword:\n\n\u003cp align=\"center\"\u003e\n\u003cimg\n    alt=\"S_i = C(g^i) = 0\"\n    src=\"https://latex.codecogs.com/svg.image?S_i%20%3d%20C%28g%5ei%29%20%3d%20%30\"\n\u003e\n\u003c/p\u003e\n\nIf the syndromes are all zero, chances are high we successfully repaired\nour codeword.\n\nUnless of course we had enough errors to end up overcorrecting to a\ndifferent codeword, but there's not much we can do in that case. No\nerror-correction is perfect.\n\nThis is all implemented in `ramrsbd_read`, if you're curious what it\nlooks like in code.\n\n## Tricks\n\nHeavy math aside, there are a couple minor implementation tricks worth\nnoting:\n\n1. Truncate the generator polynomial to `ecc_size`.\n\n   Calculating the generator polynomial for $n$ bytes of ECC gives us a\n   polynomial with $n+1$ terms. This is a bit annoying since `ecc_size`\n   is often a power-of-two:\n\n   \u003cp align=\"center\"\u003e\n   \u003cimg\n       alt=\"P(x) = \\prod_{i=0}^{n-1} \\left(x - g^i\\right) = \\left(x-1\\right)\\left(x-g\\right)\\cdots\\left(x-g^{n-1}\\right)\"\n       src=\"https://latex.codecogs.com/svg.image?P%28x%29%20%3d%20%5cprod_%7bi%3d%30%7d%5e%7bn%2d%31%7d%20%5cleft%28x%20%2d%20g%5ei%5cright%29%20%3d%20%5cleft%28x%2d%31%5cright%29%5cleft%28x%2dg%5cright%29%5ccdots%5cleft%28x%2dg%5e%7bn%2d%31%7d%5cright%29\"\n   \u003e\n   \u003c/p\u003e\n\n   Fortunately, because of math reasons, the first term will always be 1.\n   So, just like with our CRC polynomials, we can leave off the leading 1\n   and make it implicit.\n\n   Division with an implicit 1 is implemented in `ramrsbd_gf_p_divmod1`,\n   which has the extra benefit of being able to skip the normalization\n   step during [synthetic division][w-synthetic-division], so that's\n   nice.\n\n2. Store the generator polynomial in ROM.\n\n   We don't really need to recompute the generator polynomial every time\n   we initialize the block device, it's just convenient API-wise when we\n   don't know `ecc_size`.\n\n   If you only need to support a fixed set of block device geometries,\n   precomputing and storing the generator polynomial in ROM will save a\n   couple (`ecc_size`) bytes of RAM.\n\n   [rs-poly.py][rs-poly.py] can help generate the generator polynomial:\n\n   ``` bash\n   $ ./rs-poly.py 8\n   // generator polynomial for ecc_size=8\n   //\n   // P(x) = prod_i^n-1 (x - g^i)\n   //\n   static const uint8_t RAMRSBD_P[8] = {\n       0xff, 0x0b, 0x51, 0x36, 0xef, 0xad, 0xc8, 0x18,\n   };\n   ```\n\n   Which can then be provided to ramrsbd's `p` config option to avoid\n   allocating the `p_buffer` in RAM.\n\n   Unfortunately we still pay the code cost for generating the generator\n   polynomial, which is difficult to avoid with the current API.\n\n3. Minimizing the number of polynomial buffers.\n\n   We have quite a few polynomials flying around:\n\n   - $C(x)$ - Codeword buffer - `code_size` bytes\n   - $P(x)$ Generator polynomial - `ecc_size` bytes (truncated)\n   - $S_i$ - Syndrome buffer - `ecc_size` bytes\n   - $\\Lambda(x)$ - Error-locator polynomial - `ecc_size/2+1` (`\u003c=ecc_size`) bytes\n   - $C(i)$ - Connection polynomial - `ecc_size/2+1` (`\u003c=ecc_size`) bytes\n   - $\\Omega(x)$ - Error-evaluator polynomial - `ecc_size/2` (`\u003c=ecc_size`) bytes\n   - $\\Lambda'(x)$ - Derivative of the error-locator polynomial - `ecc_size/2` (`\u003c=ecc_size`) bytes\n\n   These get a bit annoying in a malloc-less system.\n\n   Fortunately, there are a couple places we can reuse buffers:\n\n   1. The connection polynomial $C(i)$ is only needed for\n      Berlekamp-Massey and we can throw it away as soon as the\n      error-locator $\\Lambda(x)$ is found.\n\n   2. We only need the syndrome buffer $S_i$ to find $\\Lambda(x)$,\n      $\\Omega(x)$, and $\\Lambda'(x)$. After that we have everything we\n      need to start fixing errors.\n\n   By sharing these buffers with polynomials computed later, such as the\n   error-evaluator $\\Omega(x)$ and derivative of the error-locator\n   $\\Lambda'(x)$, we can reduce the total number of buffers needed down\n   to 1 `code_size` buffer and 4 `ecc_size` buffers (3 if the generator\n   polynomial is stored in ROM).\n\n4. Fused derivative evaluation.\n\n   The formal derivative is a funny operation:\n\n   \u003cp align=\"center\"\u003e\n   \u003cimg\n       alt=\"\\Lambda(x) = 1 + \\sum_{k=1}^e \\Lambda_k x^k = 1 + \\Lambda_1 x + \\Lambda_2 x^2 + \\cdots + \\Lambda_e x^e\"\n       src=\"https://latex.codecogs.com/svg.image?%5cLambda%28x%29%20%3d%20%31%20%2b%20%5csum_%7bk%3d%31%7d%5ee%20%5cLambda_k%20x%5ek%20%3d%20%31%20%2b%20%5cLambda_%31%20x%20%2b%20%5cLambda_%32%20x%5e%32%20%2b%20%5ccdots%20%2b%20%5cLambda_e%20x%5ee\"\n   \u003e\n   \u003c/p\u003e\n   \n   \u003cp align=\"center\"\u003e\n   \u003cimg\n       alt=\"\\Lambda'(x) = \\sum_{k=1}^e k \\cdot \\Lambda_k x^{k-1} = \\Lambda_1 + 2 \\cdot \\Lambda_2 x + \\cdots + e \\cdot \\Lambda_e x^{e-1}\"\n       src=\"https://latex.codecogs.com/svg.image?%5cLambda%27%28x%29%20%3d%20%5csum_%7bk%3d%31%7d%5ee%20k%20%5ccdot%20%5cLambda_k%20x%5e%7bk%2d%31%7d%20%3d%20%5cLambda_%31%20%2b%20%32%20%5ccdot%20%5cLambda_%32%20x%20%2b%20%5ccdots%20%2b%20e%20%5ccdot%20%5cLambda_e%20x%5e%7be%2d%31%7d\"\n   \u003e\n   \u003c/p\u003e\n\n   Unlike other steps, it's a simple transformation that only affects one\n   term at a time. This makes it easy to apply lazily without needing to\n   copy the original polynomial.\n\n   We already need to keep the error-locator polynomial $\\Lambda(x)$\n   around to, you know, locate the errors. So if we merge the derivative\n   and evaluation of the error-locator into a single operation, we don't\n   actually need to store the derivative of the error-locator\n   $\\Lambda'(x)$ as a separate polynomial. In theory reducing the number\n   of buffers needed during error-evaluation from 3 down to 2.\n\n   This sort of fused derivative evaluation is implemented in\n   `ramrsbd_gf_p_deval` via a modified [Horner's method][w-horner].\n\n   Unfortunately, we still need at least 3 buffers for Berlekamp-Massey\n   ( $S_i$, $\\Lambda(i)$, and $C(i)$ ), so this doesn't actually save us\n   anything.\n\n   But it doesn't really cost us anything either, cleans up the code a\n   little bit, and lets us avoid clobbering the syndrome buffer $S_i$\n   which is useful for debugging.\n\n## Caveats\n\nAnd some caveats:\n\n1. For any error-correcting code, attempting to **correct** errors\n   reduces the code's ability to **detect** errors.\n\n   In Reed-Solomon's case, for $n$ bytes of ECC, we can detect up to\n   $n$ byte-errors, but only correct up to\n   $\\left\\lfloor\\frac{n}{2}\\right\\rfloor$ byte-errors. Attempting to\n   correct more errors can cause us to end up with a valid, but wrong,\n   codeword.\n\n   In practice this isn't that big of a problem. Fewer byte-errors are\n   more common, and correcting byte-errors is usually more useful. At\n   $n+1$ byte-errors you're going to end up with undetectable errors\n   anyways.\n\n   Still, it's good to be aware of this tradeoff.\n\n   ramrsbd's `error_correction` config option lets you control exactly\n   how many byte-errors to attempt to repair in case better detection is\n   more useful.\n\n2. Limited to 255 byte codewords - the non-zero elements of GF(256).\n\n   An important step in Reed-Solomon is mapping each possible error\n   location to a non-zero element of our finite field $X_j=g^j$.\n   Unfortunately our finite-field is, well, finite, so there's only so\n   many non-zero elements we can use before error-locations start to\n   alias.\n\n   This gives us a maximum codeword size of 255 bytes in GF(256),\n   including the bytes used for ECC. A bit annoying, but math is math.\n\n   In theory you can increase the maximum codeword size by using a larger\n   finite-field, but this gets a bit tricky because the log/pow table\n   approach used in ramrsbd stops being practical. 512 bytes of tables\n   for GF(256) is fine, but 128 KiBs of tables for GF(2^16)? Not so\n   much...\n\n   1. If you have [carryless-multiplication][w-clmul] hardware available,\n      GF(2^n) multiplication can be implemented efficiently by combining\n      multiplication and [Barret reduction][w-barret-reduction].\n\n      Division can then be implemented on top of multiplication by\n      leveraging the fact that $a^{2^n-2} = a^{-1}$ for any element $a$\n      in GF(2^n). [Binary exponentiation][w-binary-exponentiation] can\n      make this somewhat efficient.\n\n   2. In the same way GF(256) is defined as an\n      [extension field][w-extension-field] of GF(2), we can define\n      GF(2^16) as an extension field of GF(256), where each element is a\n      2 byte polynomial containing digits in GF(256).\n\n      This can be convenient if you already need GF(256) tables for other\n      parts of the codebase.\n\n   Or, a simpler alternative, you can just pack multiple \"physical\"\n   codewords into one \"logical\" codeword.\n\n   You could even consider interleaving the physical codewords if you\n   want to maintain the systematic encoding or are trying to protect\n   against specific error patterns.\n\n3. Support for known-location \"erasures\" left as an exercise for the\n   reader.\n\n   All of the above math assumes we don't know the location of errors,\n   which is the most common case for block devices.\n\n   But it turns out if we _do_ know the location of errors, via parity\n   bits or some other side-channel, we can do quite a bit better. We\n   usually call these known-location errors \"erasures\".\n\n   With Reed-Solomon, each unknown-location error requires 2 bytes of ECC\n   to find and repair, while known-location erasures require only 1 byte\n   of ECC to repair. You can even mix and match $e$ errors and $f$\n   erasures as long as you have $n$ bytes of ECC such that:\n\n   \u003cp align=\"center\"\u003e\n   \u003cimg\n       alt=\"2e + f \\le n\"\n       src=\"https://latex.codecogs.com/svg.image?%32e%20%2b%20f%20%5cle%20n\"\n   \u003e\n   \u003c/p\u003e\n\n   This isn't implemented in ramrsbd, but, _in theory_, the math isn't\n   too difficult to extend.\n\n   First note we can split $\\Lambda(x)$ into a separate error-locator\n   poylnomial $\\Lambda_E(x)$ and erasure-locator polynomial\n   $\\Lambda_F(x)$:\n\n   \u003cp align=\"center\"\u003e\n   \u003cimg\n       alt=\"\\Lambda(x) = \\Lambda_E(x) \\Lambda_F(x)\"\n       src=\"https://latex.codecogs.com/svg.image?%5cLambda%28x%29%20%3d%20%5cLambda_E%28x%29%20%5cLambda_F%28x%29\"\n   \u003e\n   \u003c/p\u003e\n\n   We know the location of the known-location erasures, so the\n   erasure-locator $\\Lambda_F(x)$ is trivial to calculate:\n\n   \u003cp align=\"center\"\u003e\n   \u003cimg\n       alt=\"\\Lambda_F(x) = \\prod_{j \\in F} \\left(1 - X_j x\\right)\"\n       src=\"https://latex.codecogs.com/svg.image?%5cLambda_F%28x%29%20%3d%20%5cprod_%7bj%20%5cin%20F%7d%20%5cleft%28%31%20%2d%20X_j%20x%5cright%29\"\n   \u003e\n   \u003c/p\u003e\n\n   Before we can find the error-locator polynomial $\\Lambda_E(x)$, we\n   need to modify our syndromes the hide the effects of the\n   erasure-locator polynomial. These are often called the Forney\n   syndromes $S_{Fi}$:\n\n   \u003cp align=\"center\"\u003e\n   \u003cimg\n       alt=\"S_F(x) = S(x) \\Lambda_F(x) \\bmod x^n\"\n       src=\"https://latex.codecogs.com/svg.image?S_F%28x%29%20%3d%20S%28x%29%20%5cLambda_F%28x%29%20%5cbmod%20x%5en\"\n   \u003e\n   \u003c/p\u003e\n\n   Note that the Forney syndromes $S_{Fi}$ sill satisfy the equation for\n   $\\Omega(x)$:\n\n   \u003cp align=\"center\"\u003e\n   \u003cimg\n       alt=\"\\begin{aligned} \\Omega(x) \u0026= S(x)\\Lambda(x) \\bmod x^n \\\\ \u0026= S(x)\\Lambda_E(x)\\Lambda_F(x) \\bmod x^n \\\\ \u0026= S_F(x)\\Lambda_E(x) \\bmod x^n \\end{aligned}\"\n       src=\"https://latex.codecogs.com/svg.image?%5cbegin%7baligned%7d%20%5cOmega%28x%29%20%26%3d%20S%28x%29%5cLambda%28x%29%20%5cbmod%20x%5en%20%5c%5c%20%26%3d%20S%28x%29%5cLambda_E%28x%29%5cLambda_F%28x%29%20%5cbmod%20x%5en%20%5c%5c%20%26%3d%20S_F%28x%29%5cLambda_E%28x%29%20%5cbmod%20x%5en%20%5cend%7baligned%7d\"\n   \u003e\n   \u003c/p\u003e\n\n   We can then use Berlekamp-Massey with the Forney syndromes $S_{Fi}$ to\n   find the error-locator polynomial $\\Lambda_E(x)$.\n\n   Combining the error-locator polynomial $\\Lambda_E(x)$ and the\n   erasure-locator polynomial $\\Lambda_F(x)$ gives us the creatively\n   named error-and-erasure-locator-polynomial $\\Lambda(x)$, which\n   contains everything we need to know to find the location of both\n   errors and erasures:\n\n   \u003cp align=\"center\"\u003e\n   \u003cimg\n       alt=\"\\Lambda(x) = \\Lambda_E(x) \\Lambda_F(x)\"\n       src=\"https://latex.codecogs.com/svg.image?%5cLambda%28x%29%20%3d%20%5cLambda_E%28x%29%20%5cLambda_F%28x%29\"\n   \u003e\n   \u003c/p\u003e\n\n   At this point we can continue Reed-Solomon as normal, finding the\n   error/easures locations where $\\Lambda(X_j^{-1})=0$, and repairing\n   them with Forney's algorithm,\n   $Y_j = X_j \\frac{\\Omega(X_j^{-1})}{\\Lambda'(X_j^{-1})}$:\n\n   \u003cp align=\"center\"\u003e\n   \u003cimg\n       alt=\"C(x) = C'(x) - \\sum_{j \\in E \\cup F} Y_j x^j\"\n       src=\"https://latex.codecogs.com/svg.image?C%28x%29%20%3d%20C%27%28x%29%20%2d%20%5csum_%7bj%20%5cin%20E%20%5ccup%20F%7d%20Y_j%20x%5ej\"\n   \u003e\n   \u003c/p\u003e\n\n## References\n\n- [Massey, J. L. - Shift-register synthesis and BCH decoding][massey-srsbchd]\n- [Gill, J. - EE387 Notes #7][gill-notes7]\n- [Truong, T. K., Hsu, I. S., Eastman, W. L., Reed, I. S. - A Simplified Procedure for Correcting Both Errors and Erasures of a Reed-Solomon Code Using the Euclidean Algorithm][truong-spcbeerscuea]\n\n- [Wikiversity - Reed Solomon for coders][wikiversity-rs]\n\n- [Wikipedia - Reed Solomon][w-rs]\n- [Wikipedia - Berlekamp-Massey algorithm][w-bm]\n- [Wikipedia - Forney Algorithm][w-forney]\n\n- [Wikipedia - BCH Code][w-bch]\n- [Wikipedia - Finite field arithmetic][w-gf]\n- [Wikipedia - GF(2)][w-gf2]\n- [Wikipedia - Systematic Code][w-systematic-code]\n- [Wikipedia - Primitive element (finite field)][w-generator]\n- [Wikipedia - Linear-feedback shift register (LFSR)][w-lfsr]\n- [Wikipedia - Recurrence Relation][w-recurrence-relation]\n- [Wikipedia - Chien search][w-chien]\n- [Wikipedia - Formal derivative][w-formal-derivative]\n- [Wikipedia - Geometric series][w-geometric-series]\n- [Wikipedia - Product rule][w-product-rule]\n- [Wikipedia - Synthetic division][w-synthetic-division]\n- [Wikipedia - Horner's method][w-horner]\n- [Wikipedia - Carry-less product][w-clmul]\n- [Wikipedia - Barret reduction][w-barret-reduction]\n- [Wikipedia - Exponentiation by squaring][w-binary-exponentiation]\n- [Wikipedia - Field extension][w-extension-field]\n\n[massey-srsbchd]: http://crypto.stanford.edu/~mironov/cs359/massey.pdf\n[gill-notes7]: https://web.archive.org/web/20140630172526/http://web.stanford.edu/class/ee387/handouts/notes7.pdf\n[truong-spcbeerscuea]: https://ntrs.nasa.gov/api/citations/19880003316/downloads/19880003316.pdf\n\n[wikiversity-rs]: https://en.wikiversity.org/wiki/Reed%E2%80%93Solomon_codes_for_coders\n\n[w-rs]: https://en.wikipedia.org/wiki/Reed%E2%80%93Solomon_error_correction\n[w-bm]: https://en.wikipedia.org/wiki/Berlekamp%E2%80%93Massey_algorithm\n[w-forney]: https://en.wikipedia.org/wiki/Forney_algorithm\n\n[w-bch]: https://en.wikipedia.org/wiki/BCH_code\n[w-pgz]: https://en.wikipedia.org/wiki/Reed%E2%80%93Solomon_error_correction#Peterson%E2%80%93Gorenstein%E2%80%93Zierler_decoder\n[w-euclidean]: https://en.wikipedia.org/wiki/Reed%E2%80%93Solomon_error_correction#Euclidean_decoder\n[w-gf]: https://en.wikipedia.org/wiki/Finite_field_arithmetic\n[w-gf2]: https://en.wikipedia.org/wiki/GF(2)\n[w-gf256]: https://en.wikipedia.org/wiki/Finite_field_arithmetic#Effective_polynomial_representation\n[w-systematic-code]: https://en.wikipedia.org/wiki/Systematic_code\n[w-generator]: https://en.wikipedia.org/wiki/Primitive_element_(finite_field)\n[w-lfsr]: https://en.wikipedia.org/wiki/Linear-feedback_shift_register\n[w-recurrence-relation]: https://en.wikipedia.org/wiki/Recurrence_relation\n[w-chien]: https://en.wikipedia.org/wiki/Chien_search\n[w-formal-derivative]: https://en.wikipedia.org/wiki/Formal_derivative\n[w-geometric-series]: https://en.wikipedia.org/wiki/Geometric_series\n[w-product-rule]: https://en.wikipedia.org/wiki/Product_rule\n[w-synthetic-division]: https://en.wikipedia.org/wiki/Synthetic_division\n[w-horner]: https://en.wikipedia.org/wiki/Horner%27s_method\n[w-clmul]: https://en.wikipedia.org/wiki/Carry-less_product\n[w-barret-reduction]: https://en.wikipedia.org/wiki/Barrett_reduction\n[w-binary-exponentiation]: https://en.wikipedia.org/wiki/Exponentiation_by_squaring\n[w-extension-field]: https://en.wikipedia.org/wiki/Field_extension\n\n[littlefs]: https://github.com/littlefs-project/littlefs\n[ramcrc32bd]: https://github.com/geky/ramcrc32bd\n\n[bm-lfsr-solver.py]: bm-lfsr-solver.py\n[bm-lfsr256-solver.py]: bm-lfsr256-solver.py\n[rs-poly.py]: rs-poly.py\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flittlefs-project%2Framrsbd","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flittlefs-project%2Framrsbd","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flittlefs-project%2Framrsbd/lists"}