[{"id":247031,"date":"2026-05-21T12:31:13","date_gmt":"2026-05-21T17:31:13","guid":{"rendered":"https:\/\/www.johndcook.com\/blog\/?p=247031"},"modified":"2026-05-21T12:55:31","modified_gmt":"2026-05-21T17:55:31","slug":"couth-and-uncouth-function-pairs","status":"publish","type":"post","link":"https:\/\/www.johndcook.com\/blog\/2026\/05\/21\/couth-and-uncouth-function-pairs\/","title":{"rendered":"Couth and uncouth function pairs"},"content":{"rendered":"<p>&#8220;You can&#8217;t always get what you want. But sometimes you get what you need.&#8221; \u2014 The Rolling Stones<\/p>\n<p>Circular functions and hyperbolic functions aren&#8217;t invertible, but we invert them anyway. These functions map many points in the domain to each point in the range, and we invert them by mapping a point in the range back to\u00a0<em>some<\/em> point in the domain. Often this works as expected, but sometimes it doesn&#8217;t.<\/p>\n<p>In the <a href=\"https:\/\/www.johndcook.com\/blog\/2026\/05\/21\/circular-hyperbolic-rotations\/\">previous post<\/a> I said<\/p>\n<blockquote><p>You can relate each trig function &#8220;foo&#8221; with its hyperbolic counterpart &#8220;fooh&#8221; by applying one of the functions to\u00a0<em>iz<\/em> then multiplying by a constant <em>c<\/em> that depends on foo:\u00a0<em>c<\/em> =\u00a0<em>i<\/em> for sin and tan,\u00a0<em>c<\/em> = 1 for cos and sec, and\u00a0<em>c<\/em> = \u2212<em>i<\/em> for csc and cot.<\/p><\/blockquote>\n<p>In symbols,<\/p>\n<p style=\"padding-left: 40px;\"><em>c<\/em> foo(<em>z<\/em>) = fooh(<em>iz<\/em>).<\/p>\n<p>Let&#8217;s suppose foo and fooh are invertible, ignoring any complications, and solve foo(<em>z<\/em>) =\u00a0<em>w<\/em> for\u00a0<em>z<\/em>. We get<\/p>\n<p style=\"padding-left: 40px;\"><em>i<\/em> foo<sup>\u22121<\/sup>(<em>w<\/em>) = fooh<sup>\u22121<\/sup>(<em>cw<\/em>)<\/p>\n<p>or using &#8220;arc&#8221; nomenclature for inverse functions<\/p>\n<p style=\"padding-left: 40px;\"><em>i<\/em> arcfoo(<em>w<\/em>) = arcfooh(<em>cw<\/em>).<\/p>\n<p>When the naive calculation above holds, except possibly at a finite number of points, we say the pair (foo, fooh) is\u00a0<strong>couth<\/strong>. Otherwise we say the pair is\u00a0<strong>uncouth<\/strong>. These term were coined by Robert Corless and his coauthors in their paper [1].<\/p>\n<p>Whether the pair (foo, fooh) is couth depends not only on foo and fooh, but also on the details of how arcfoo and arcfooh are defined.<\/p>\n<p>In Python&#8217;s NumPy library, the pairs (sin, sinh) and (tan, tanh) are couth, but the pair (cos, cosh) is uncouth.<\/p>\n<p>Numpy doesn&#8217;t define the reciprocal functions sec, sech, csc, csch, cot, and coth. I used to find that annoying, but I&#8217;m beginning to think that was wise. These functions cause problems. For example, there may be two reasonable ways to define these functions, one of which forms a couth pair and one of which forms an uncouth pair.<\/p>\n<p>For example, how should you define cot and coth? There would be no disagreement over the definition<\/p>\n<pre>cot = lambda x: 1\/tan(x)<\/pre>\n<p>but there are at least two definitions of coth that you&#8217;ll find in practice:<\/p>\n<pre>\r\narccot = lambda z: 0.5*pi - arctan(z)\r\narccot = lambda z: arctan(1\/z).\r\n<\/pre>\n<p>Both definitions have their advantages, but the former is uncouth while the latter is couth. You can verify that both definitions are the same at <em>z<\/em> = 1 but not at <em>z<\/em> = &minus;1.<\/p>\n<p>With the following definitions, the pairs (cos, cosh) and (sec, sech) are uncouth and the rest are couth.<\/p>\n<pre>\r\nfrom numpy import *\r\n\r\ncsc     = lambda x: 1\/sin(x)\r\nsec     = lambda x: 1\/cos(x)\r\ncot     = lambda x: 1\/tan(x)\r\ncsch    = lambda x: 1\/sinh(x)\r\nsech    = lambda x: 1\/cosh(x)\r\ncoth    = lambda x: 1\/tanh(x)\r\n\r\narccot  = lambda z: arctan(1\/z)\r\narcsec  = lambda z: arccos(1\/z)\r\narccsc  = lambda z: arcsin(1\/z)\r\narccoth = lambda z: arctanh(1\/z)\r\narcsech = lambda z: arccosh(1\/z)\r\narccsch = lambda z: arcsinh(1\/z)\r\n<\/pre>\n<p>[1] &#8220;According to Abramowitz and Stegun&#8221; or arccoth needn&#8217;t be uncouth. Robert M. Corless <em>et al<\/em>. ACM SIGSAM Bulletin, Volume 34, Issue 2, pp 58 &#8211; 65 https:\/\/doi.org\/10.1145\/362001.362023<\/p>\n","protected":false},"excerpt":{"rendered":"<p>&#8220;You can&#8217;t always get what you want. But sometimes you get what you need.&#8221; \u2014 The Rolling Stones Circular functions and hyperbolic functions aren&#8217;t invertible, but we invert them anyway. These functions map many points in the domain to each point in the range, and we invert them by mapping a point in the range [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[9],"tags":[181,129],"class_list":["post-247031","post","type-post","status-publish","format-standard","hentry","category-math","tag-complex-analysis","tag-special-functions"],"acf":[],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/posts\/247031","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/comments?post=247031"}],"version-history":[{"count":0,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/posts\/247031\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/media?parent=247031"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/categories?post=247031"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/tags?post=247031"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}},{"id":247032,"date":"2026-05-21T10:53:56","date_gmt":"2026-05-21T15:53:56","guid":{"rendered":"https:\/\/www.johndcook.com\/blog\/?p=247032"},"modified":"2026-05-21T12:56:32","modified_gmt":"2026-05-21T17:56:32","slug":"circular-hyperbolic-rotations","status":"publish","type":"post","link":"https:\/\/www.johndcook.com\/blog\/2026\/05\/21\/circular-hyperbolic-rotations\/","title":{"rendered":"Circular and hyperbolic functions differ by rotations"},"content":{"rendered":"<p>The difference between a circular function and a hyperbolic function is a rotation or two.<\/p>\n<p>For example, cosh(<em>z<\/em>) = cos(<em>iz<\/em>). You can read that as saying that to find the hyperbolic cosine of <em>z<\/em>, first you rotate\u00a0<em>z<\/em> a quarter turn to the left (i.e. multiply by <em>i<\/em>) and then take the cosine.<\/p>\n<p>For another example, sinh(<em>z<\/em>) = \u2212<em>i<\/em> sin(<em>iz<\/em>). This says that you can calculate the hyperbolic sine of\u00a0<em>z<\/em> by rotating\u00a0<em>z<\/em> to the left, taking the sine, and then rotating to the right.<\/p>\n<p>You can relate each trig function &#8220;foo&#8221; with its hyperbolic counterpart &#8220;fooh&#8221; by applying one of the functions to\u00a0<em>iz<\/em> then multiplying by a constant <em>c<\/em> that depends on foo:\u00a0<em>c<\/em> =\u00a0<em>i<\/em> for sin and tan,\u00a0<em>c<\/em> = 1 for cos and sec, and\u00a0<em>c<\/em> = \u2212<em>i<\/em> for csc and cot.<\/p>\n<p>Note that if the constant for foo is\u00a0<em>c<\/em>, the constant for 1\/foo is 1\/<em>c<\/em>. So, for example, the constant for tan is\u00a0<em>i<\/em> and the constant for cot is 1\/<em>i<\/em> = \u2212<em>i<\/em>.<\/p>\n<p>We have four groups of equations, depending on whether the left side of the equation is foo(<em>iz<\/em>), fooh(<em>iz<\/em>), foo(<em>z<\/em>), or fooh(<em>z<\/em>).<\/p>\n<p>This post was written as a warm-up for the <a href=\"https:\/\/www.johndcook.com\/blog\/2026\/05\/21\/couth-and-uncouth-function-pairs\/\">next post<\/a> on couth and uncouth function pairs.<\/p>\n<h2>foo(iz)<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-medium\" src=\"https:\/\/www.johndcook.com\/fooiz2.png\" width=\"320\" height=\"291\" \/><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter\" style=\"background-color: white;\" src=\"https:\/\/www.johndcook.com\/fooiz.svg\" alt=\"\\begin{align*} \\sin(iz) &amp; = \\phantom{-}i\\sinh(z) \\\\ \\cos(iz) &amp; = \\phantom{-i}\\cosh(z) \\\\ \\tan(iz) &amp; = \\phantom{-}i\\tanh(z) \\\\ \\csc(iz) &amp; = -i\\text{csch}(z) \\\\ \\sec(iz) &amp; = \\phantom{-i}\\text{sech}(z) \\\\ \\cot(iz) &amp; = -i\\coth(z) \\\\ \\end{align*}\" width=\"163\" height=\"163\" \/><\/p>\n<h2>fooh(iz)<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-medium aligncenter\" src=\"https:\/\/www.johndcook.com\/foohiz2.png\" width=\"320\" height=\"291\" \/><br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter\" style=\"background-color: white;\" src=\"https:\/\/www.johndcook.com\/foohiz.svg\" alt=\"\\begin{align*} \\sinh(iz) &amp; = \\phantom{-}i\\sin(z) \\\\ \\cosh(iz) &amp; = \\phantom{-i}\\cos(z) \\\\ \\tanh(iz) &amp; = \\phantom{-}i\\tan(z) \\\\ \\text{csch}(iz) &amp; = -i\\csc(z) \\\\ \\text{sech}(iz) &amp; = \\phantom{-i}\\sec(z) \\\\ \\coth(iz) &amp; = -i\\cot(z) \\\\ \\end{align*}\" width=\"163\" height=\"163\" \/><\/p>\n<h2>foo(z)<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-medium\" src=\"https:\/\/www.johndcook.com\/fooz2.png\" width=\"320\" height=\"297\" \/><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter\" style=\"background-color: white;\" src=\"https:\/\/www.johndcook.com\/fooz.svg\" alt=\"\\begin{align*} \\sin(z) &amp; = -i\\sinh(iz) \\\\ \\cos(z) &amp; = \\phantom{-i}\\cosh(iz) \\\\ \\tan(z) &amp; = -i\\tanh(iz) \\\\ \\csc(z) &amp; = \\phantom{-}i\\text{csch}(iz) \\\\ \\sec(z) &amp; = \\phantom{-i}\\text{sech}(iz) \\\\ \\cot(z) &amp; = \\phantom{-}i\\coth(iz) \\\\ \\end{align*}\" width=\"163\" height=\"163\" \/><\/p>\n<h2>fooh(z)<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-medium\" src=\"https:\/\/www.johndcook.com\/foohz2.png\" width=\"320\" height=\"296\" \/><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter\" style=\"background-color: white;\" src=\"https:\/\/www.johndcook.com\/fooh.svg\" alt=\"\\begin{align*} \\sinh(z) &amp; = -i\\sin(iz) \\\\ \\cosh(z) &amp; = \\phantom{-i}\\cos(iz) \\\\ \\tanh(z) &amp; = -i\\tan(iz) \\\\ \\text{csch}(z) &amp; = \\phantom{-}i\\csc(iz) \\\\ \\text{sech}(z) &amp; = \\phantom{-i}\\sec(iz) \\\\ \\coth(z) &amp; = \\phantom{-}i\\cot(iz) \\\\ \\end{align*} \" width=\"163\" height=\"163\" \/><\/p>\n","protected":false},"excerpt":{"rendered":"<p>The difference between a circular function and a hyperbolic function is a rotation or two. For example, cosh(z) = cos(iz). You can read that as saying that to find the hyperbolic cosine of z, first you rotate\u00a0z a quarter turn to the left (i.e. multiply by i) and then take the cosine. For another example, [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[9],"tags":[],"class_list":["post-247032","post","type-post","status-publish","format-standard","hentry","category-math"],"acf":[],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/posts\/247032","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/comments?post=247032"}],"version-history":[{"count":0,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/posts\/247032\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/media?parent=247032"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/categories?post=247032"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/tags?post=247032"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}},{"id":247029,"date":"2026-05-19T19:49:50","date_gmt":"2026-05-20T00:49:50","guid":{"rendered":"https:\/\/www.johndcook.com\/blog\/?p=247029"},"modified":"2026-05-21T14:04:16","modified_gmt":"2026-05-21T19:04:16","slug":"square-root-of-x-squared-minus-one","status":"publish","type":"post","link":"https:\/\/www.johndcook.com\/blog\/2026\/05\/19\/square-root-of-x-squared-minus-one\/","title":{"rendered":"Square root of x\u00b2 \u2212 1"},"content":{"rendered":"<p>How should we define \u221a(<em>z<\/em>\u00b2 \u2212 1)? Well, you could square <em>z<\/em>, subtract 1, and take the square root. What else would you do?!<\/p>\n<p>The question turns out to be more subtle than it looks.<\/p>\n<p>When\u00a0<em>x<\/em> is a non-negative real number, \u221a<em>x<\/em> is defined to be the non-negative real number whose square is\u00a0<em>x<\/em>. When\u00a0<em>x<\/em> is a complex number \u221a<em>x<\/em> is defined to be <strong>a<\/strong> function that extends \u221a<em>x<\/em> from the real line to the complex plane by analytic continuation. But we can&#8217;t extend \u221a<em>x<\/em> as an analytic function to the entire complex plane \u2102. We have to choose to make a &#8220;cut&#8221; somewhere, and the conventional choice is to make a cut along the negative real axis.<\/p>\n<h2>Using the principal branch<\/h2>\n<p>The &#8220;principal branch&#8221; of the square root function is defined to be the unique function that analytically extends \u221a<em>x<\/em> from the positive reals to \u2102 \\ (\u2212\u221e, 0].<\/p>\n<p>Assume for now that by \u221a<em>x<\/em> we mean the principal branch of the square root function. Now what does \u221a(<em>z<\/em>\u00b2 \u2212 1) mean? It\u00a0<em>could<\/em> mean just what we said at the top of the post: we square\u00a0<em>z<\/em>, subtract 1, and apply the (principal branch of the) square root function. If we do that, we must exclude those values of <em>z<\/em> such that (<em>z<\/em>\u00b2 \u2212 1) is negative. This means we have to cut out the imaginary axis and the interval [\u22121, 1].<\/p>\n<p>This is what Mathematica will do when asked to evaluate <code>Sqrt[z^2 - 1]<\/code>. The command<\/p>\n<pre>ComplexPlot[Sqrt[z^2 - 1], {z, -2 - 2 I, 2 + 2 I}]<\/pre>\n<p>makes the branch cuts clear by abrupt changes in color.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-medium\" src=\"https:\/\/www.johndcook.com\/sqrt_branch1.png\" width=\"480\" height=\"483\" \/><\/p>\n<h2>A different approach<\/h2>\n<p>Now let&#8217;s take a different approach. Consider the function \u221a(<em>z<\/em>\u00b2 \u2212 1) as a whole. Do not think of it procedurally as above, first squaring <em>z<\/em> etc. Instead, think of a it as a black box that takes in <em>z<\/em> and returns a complex number whose square is <em>z<\/em>\u00b2 \u2212 1.<\/p>\n<p>This function has an obvious definition for <em>z<\/em> &gt; 1. And we can extend this function, via analytic continuation, to more of the complex plane. We can do this <em>directly<\/em>, not by extending the square root function. But as before, we cannot extend the function analytically to all of \u2102. We have to cut something out. A common choice is to cut out [\u22121, 1]. This eliminates the need for a branch cut along the imaginary axis.<\/p>\n<p>The function<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter\" style=\"background-color: white;\" src=\"https:\/\/www.johndcook.com\/cosh_sum5.svg\" alt=\"f(z) = \\exp\\left( \\tfrac{1}{2}\\left( \\log(z - 1) + \\log(z + 1) \\right) \\right)\" width=\"320\" height=\"36\" \/><\/p>\n<p>extends \u221a(<em>z<\/em>\u00b2 \u2212 1) the way we want [1].<\/p>\n<p>The Mathematica code<\/p>\n<pre>ComplexPlot[Exp[(1\/2) (Log[z - 1 ] + Log[z + 1])], {z, -2 - 2 I, 2 + 2 I}]<\/pre>\n<p>shows that our function is now continuous across the imaginary axis, though there&#8217;s still a discontinuity as you cross [\u22121, 1].<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-medium\" src=\"https:\/\/www.johndcook.com\/sqrt_branch2.png\" width=\"480\" height=\"483\" \/><\/p>\n<p>We used this analytic extension of \u221a(<em>z<\/em>\u00b2 \u2212 1) in the <a href=\"https:\/\/www.johndcook.com\/blog\/2026\/05\/19\/closer-look-at-an-identity\/\">previous post<\/a> to eliminate branch cuts in an identity.<\/p>\n<h2>Related posts<\/h2>\n<ul>\n<li class=\"link\"><a href=\"https:\/\/www.johndcook.com\/blog\/2026\/03\/09\/trig-composition-table\/\">Trig composition table<\/a><\/li>\n<li class=\"link\"><a href=\"https:\/\/www.johndcook.com\/blog\/2026\/03\/10\/sinh-arccosh\/\">sinh( arccosh(x) )<\/a><\/li>\n<li class=\"link\"><a href=\"https:\/\/www.johndcook.com\/blog\/2025\/03\/03\/mathematica-contour-plot\/\">Duplicating a hand-drawn plot<\/a><\/li>\n<\/ul>\n<p>[1] The principal branch of the logarithm has a cut along the negative real axis. Why does our square root function, defined using log, not have a branch cut along the negative axis?<\/p>\n<p>First of all, the log function, and Mathematica&#8217;s implementation of it <code>Log[]<\/code>, isn&#8217;t undefined on (\u2212\u221e, 1), it just isn&#8217;t continuous there. The function still has a value. By convention, the value is taken to be the limit of log(<em>z<\/em>) approaching <em>z<\/em>\u00a0from above, i.e. from the 2nd quadrant.<\/p>\n<p>Second, the value of (log(<em>z<\/em> &#8211; 1) + log(<em>z<\/em> + 1))\/2 differs by a factor of 2\u03c0<em>i<\/em> when approaching a value <em>z<\/em> &lt; \u22121 from above versus from below. This factor goes away when taking the exponential. So our function\u00a0<em>f<\/em>(<em>z<\/em>) is analytic across (\u2212\u221e, 1) even though the log functions in its definition are not.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>How should we define \u221a(z\u00b2 \u2212 1)? Well, you could square z, subtract 1, and take the square root. What else would you do?! The question turns out to be more subtle than it looks. When\u00a0x is a non-negative real number, \u221ax is defined to be the non-negative real number whose square is\u00a0x. When\u00a0x is [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[9],"tags":[181],"class_list":["post-247029","post","type-post","status-publish","format-standard","hentry","category-math","tag-complex-analysis"],"acf":[],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/posts\/247029","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/comments?post=247029"}],"version-history":[{"count":0,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/posts\/247029\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/media?parent=247029"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/categories?post=247029"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/tags?post=247029"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}},{"id":247027,"date":"2026-05-19T18:37:31","date_gmt":"2026-05-19T23:37:31","guid":{"rendered":"https:\/\/www.johndcook.com\/blog\/?p=247027"},"modified":"2026-05-19T19:38:25","modified_gmt":"2026-05-20T00:38:25","slug":"closer-look-at-an-identity","status":"publish","type":"post","link":"https:\/\/www.johndcook.com\/blog\/2026\/05\/19\/closer-look-at-an-identity\/","title":{"rendered":"Closer look at an identity"},"content":{"rendered":"<p>The previous post derived the identity<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter\" style=\"background-color: white;\" src=\"https:\/\/www.johndcook.com\/cosh_sum1.svg\" alt=\"\\cosh\\Big( \\operatorname{arccosh}(x) + \\operatorname{arccosh}(y)\\Big) = xy + \\sqrt{x^2 -1} \\sqrt{y^2 -1}\" width=\"452\" height=\"38\" \/><\/p>\n<p>and said in a footnote that the identity holds at least for\u00a0<em>x<\/em> &gt; 1 and\u00a0<em>y<\/em> &gt; 1. That&#8217;s true, but let&#8217;s see why the footnote is necessary.<\/p>\n<p>Let&#8217;s have Mathematica plot<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter\" style=\"background-color: white;\" src=\"https:\/\/www.johndcook.com\/cosh_sum2.svg\" alt=\"\\left| \\cosh\\Big( \\operatorname{arccosh}(x) + \\operatorname{arccosh}(y)\\Big) - xy - \\sqrt{x^2 -1} \\sqrt{y^2 -1} \\right|\" width=\"461\" height=\"48\" \/><\/p>\n<p>The plot will be 0 where the identity above holds.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-medium\" src=\"https:\/\/www.johndcook.com\/coshacosh1.png\" width=\"480\" height=\"399\" \/><\/p>\n<p>The plot is indeed flat for\u00a0<em>x<\/em> &gt; 1 and\u00a0<em>y<\/em> &gt; 1, and more, but not everywhere.<\/p>\n<p>If we combine the two square roots<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter\" style=\"background-color: white;\" src=\"https:\/\/www.johndcook.com\/cosh_sum3.svg\" alt=\"\\left| \\cosh\\Big( \\operatorname{arccosh}(x) + \\operatorname{arccosh}(y)\\Big) - xy - \\sqrt{(x^2 -1) (y^2 -1)} \\right|\" width=\"479\" height=\"48\" \/><\/p>\n<p>and plot again we still get a valid identity for\u00a0<em>x<\/em> &gt; 1 and\u00a0<em>y<\/em> &gt; 1, but the plot changes.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-medium\" src=\"https:\/\/www.johndcook.com\/coshacosh2.png\" width=\"480\" height=\"399\" \/><\/p>\n<p>This is because \u221a<em>a<\/em> \u221a<em>b<\/em> does not necessarily equal \u221a(<em>ab<\/em>) when the arguments may be negative.<\/p>\n<p>The square root function and the arccosh function are not naturally single-valued functions. They require branch cuts to force them to be single-valued, and the two functions require <em>different<\/em> branch cuts. I go into this in some detail <a href=\"https:\/\/www.johndcook.com\/blog\/2026\/03\/10\/sinh-arccosh\/\">here<\/a>.<\/p>\n<p>There is a way to reformulate our identity so that it holds everywhere. If we replace<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter\" style=\"background-color: white;\" src=\"https:\/\/www.johndcook.com\/cosh_sum4.svg\" alt=\"\\sqrt{z^2 - 1}\" width=\"63\" height=\"24\" \/><\/p>\n<p>with<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter\" style=\"background-color: white;\" src=\"https:\/\/www.johndcook.com\/cosh_sum5.svg\" alt=\"f(z) = \\exp\\left( \\tfrac{1}{2}\\left( \\log(z - 1) + \\log(z + 1) \\right) \\right)\" width=\"320\" height=\"36\" \/><\/p>\n<p>which is equivalent for <em>z<\/em> &gt; 1, the corresponding identity holds everywhere.<\/p>\n<p>We can verify this with the following Mathematica code.<\/p>\n<pre>f[z_] := Exp[(1\/2) (Log[z - 1 ] + Log[z + 1])]\r\nFullSimplify[Cosh[ArcCosh[x] + ArcCosh[y]] - x y - f[x] f[y]]\r\n<\/pre>\n<p>This returns 0.<\/p>\n<p>By contrast, the code<\/p>\n<pre>FullSimplify[\r\n Cosh[ArcCosh[x] + ArcCosh[y]] - x y - Sqrt[x^2 - 1] Sqrt[y^2 - 1]]<\/pre>\n<p>simply returns its input with no simplification, unless we add restrictions on <em>x<\/em> and <em>y<\/em>. The code<\/p>\n<pre>FullSimplify[\r\n Cosh[ArcCosh[x] + ArcCosh[y]] - x y - Sqrt[x^2 - 1] Sqrt[y^2 - 1], \r\n Assumptions -&gt; {x &gt; -1 &amp;&amp; y &gt; -1}]\r\n<\/pre>\n<p>does return 0.<\/p>\n<h2>Related posts<\/h2>\n<ul>\n<li class=\"link\"><a href=\"https:\/\/www.johndcook.com\/blog\/2026\/03\/12\/arccos\/\">Inverse cosine<\/a><\/li>\n<li class=\"link\"><a href=\"https:\/\/www.johndcook.com\/blog\/2016\/12\/17\/branch-cuts-and-common-lisp\/\">Branch cuts and Common Lisp<\/a><\/li>\n<li class=\"link\"><a href=\"https:\/\/www.johndcook.com\/blog\/2025\/01\/22\/duplicating-hankel-plot-from-as\/\">Duplicating a plot from A &amp; S<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>The previous post derived the identity and said in a footnote that the identity holds at least for\u00a0x &gt; 1 and\u00a0y &gt; 1. That&#8217;s true, but let&#8217;s see why the footnote is necessary. Let&#8217;s have Mathematica plot The plot will be 0 where the identity above holds. The plot is indeed flat for\u00a0x &gt; 1 [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[9],"tags":[181,87],"class_list":["post-247027","post","type-post","status-publish","format-standard","hentry","category-math","tag-complex-analysis","tag-mathematica"],"acf":[],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/posts\/247027","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/comments?post=247027"}],"version-history":[{"count":0,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/posts\/247027\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/media?parent=247027"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/categories?post=247027"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/tags?post=247027"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}},{"id":247025,"date":"2026-05-19T07:09:54","date_gmt":"2026-05-19T12:09:54","guid":{"rendered":"https:\/\/www.johndcook.com\/blog\/?p=247025"},"modified":"2026-05-19T11:51:38","modified_gmt":"2026-05-19T16:51:38","slug":"zagiers-equation","status":"publish","type":"post","link":"https:\/\/www.johndcook.com\/blog\/2026\/05\/19\/zagiers-equation\/","title":{"rendered":"Approximating Markov&#8217;s equation"},"content":{"rendered":"<p>Markov numbers are integer solutions to<\/p>\n<p style=\"padding-left: 40px;\"><em>x<\/em>\u00b2 +\u00a0<em>y<\/em>\u00b2 +\u00a0<em>z<\/em>\u00b2 = 3<em>xyz<\/em>.<\/p>\n<p>The Wikipedia article on Markov numbers mentions that Don Zagier studied Markov numbers by looking the approximating equation<\/p>\n<p style=\"padding-left: 40px;\"><em>x<\/em>\u00b2 +\u00a0<em>y<\/em>\u00b2 +\u00a0<em>z<\/em>\u00b2 = 3<em>xyz<\/em>\u00a0+ 4\/9<\/p>\n<p>which is equivalent to<\/p>\n<p style=\"padding-left: 40px;\"><em>f<\/em>(<em>x<\/em>) +\u00a0<em>f<\/em>(<em>y<\/em>) = <em>f<\/em>(<em>z<\/em>)<\/p>\n<p>where\u00a0<em>f<\/em>(<em>t<\/em>) is defined as arccosh(3<em>t<\/em>\/2). It wasn&#8217;t clear to me why the two previous equations are equivalent, so I&#8217;m writing this post to show that they are equivalent.<\/p>\n<h2>Examples<\/h2>\n<p>Before showing the equivalence of Zagier&#8217;s two equations, let&#8217;s look at an example that shows solutions to his second equation approximate solutions to Markov&#8217;s equation.<\/p>\n<p>The following code verifies that (5, 13, 194) is a solution to Markov&#8217;s equation.<\/p>\n<pre>x, y, z = 5, 13, 194\r\nassert(x**2 + y**2 + z**2 == 3*x*y*z)\r\n<\/pre>\n<p>With the same\u00a0<em>x<\/em> and\u00a0<em>y<\/em> above, let&#8217;s show that the\u00a0<em>z<\/em> in Zagier&#8217;s second equation is close to the\u00a0<em>z<\/em> above.<\/p>\n<pre>from math import cosh, acosh\r\n\r\nf = lambda t: acosh(3*t\/2)\r\ng = lambda t: cosh(t)*2\/3\r\nz = g(f(x) + f(y))\r\nprint(z)\r\n<\/pre>\n<p>This gives <em>z<\/em> = 194.0023, which is close to the value of <em>z<\/em> in the Markov triple above.<\/p>\n<h2>Applying Osborn&#8217;s rule<\/h2>\n<p>Now suppose<\/p>\n<p style=\"padding-left: 40px;\"><em>f<\/em>(<em>x<\/em>) +\u00a0<em>f<\/em>(<em>y<\/em>) = <em>f<\/em>(<em>z<\/em>)<\/p>\n<p>which expands to<\/p>\n<p style=\"padding-left: 40px;\">arccosh(3<em>x<\/em>\/2) + arccosh(3<em>y<\/em>\/2)\u00a0 = arccosh(3<em>z<\/em>\/2).<\/p>\n<p>It seems sensible to apply cosh to both sides. Is there some identity for cosh of a sum? Maybe you recall the equation for cosine of a sum:<\/p>\n<p style=\"padding-left: 40px;\">cos(<em>a<\/em> +\u00a0<em>b<\/em>) = cos(<em>a<\/em>) cos(<em>b<\/em>) \u2212 sin(<em>a<\/em>) sin(<em>b<\/em>).<\/p>\n<p>Then <a href=\"https:\/\/www.johndcook.com\/blog\/2024\/08\/20\/osborn-rule\/\">Osborn&#8217;s rule<\/a> says the corresponding hyperbolic identity is<\/p>\n<p style=\"padding-left: 40px;\">cosh(<em>a<\/em> +\u00a0<em>b<\/em>) = cosh(<em>a<\/em>) cosh(<em>b<\/em>) \u2212 sinh(<em>a<\/em>) sinh(<em>b<\/em>).<\/p>\n<p>Osborn&#8217;s rule also says that the analog of the familiar identity<\/p>\n<p style=\"padding-left: 40px;\">sin\u00b2(<em>a<\/em>) + cos\u00b2(<em>b<\/em>) = 1<\/p>\n<p>is<\/p>\n<p style=\"padding-left: 40px;\">sinh\u00b2(<em>a<\/em>) = cosh\u00b2(<em>b<\/em>) \u2212 1.<\/p>\n<p>From these two hyperbolic identities we can show that [1]<\/p>\n<p style=\"padding-left: 40px;\">cosh( arccosh(<em>a<\/em>) + arccosh(<em>b<\/em>) ) =\u00a0<em>ab<\/em> + \u221a(<em>a<\/em>\u00b2 \u2212 1) \u221a(<em>b<\/em>\u00b2 \u2212 1).<\/p>\n<h2>Slug it out<\/h2>\n<p>The identity derived above is the tool we need to reduce our task to routine algebra.<\/p>\n<p>If<\/p>\n<p style=\"padding-left: 40px;\">arccosh(3<em>x<\/em>\/2) + arccosh(3<em>y<\/em>\/2)\u00a0 = arccosh(3<em>z<\/em>\/2)<\/p>\n<p>then<\/p>\n<p style=\"padding-left: 40px;\">(3<em>x<\/em>\/2)\u00a0 (3<em>y<\/em>\/2)\u00a0 + \u221a((3<em>x<\/em>\/2)\u00b2 \u2212 1) \u221a((3<em>y<\/em>\/2)\u00b2 \u2212 1) = 3<em>z<\/em> \/ 2<\/p>\n<p>which simplifies to Zagier&#8217;s equation<\/p>\n<p style=\"padding-left: 40px;\"><em>x<\/em>\u00b2 +\u00a0<em>y<\/em>\u00b2 +\u00a0<em>z<\/em>\u00b2 = 3<em>xyz<\/em> + 4\/9.<\/p>\n<h2>Related posts<\/h2>\n<ul>\n<li class=\"link\"><a href=\"https:\/\/www.johndcook.com\/blog\/2023\/09\/22\/prime-weeds\/\">Primes and weeds<\/a><\/li>\n<li class=\"link\"><a href=\"https:\/\/www.johndcook.com\/blog\/2026\/03\/10\/sinh-arccosh\/\">sinh( arccosh(x) )<\/a><\/li>\n<li class=\"link\"><a href=\"https:\/\/www.johndcook.com\/blog\/2022\/01\/08\/diophantine\/\">Three paths converge<\/a><\/li>\n<\/ul>\n<p>[1] The equation holds at least for <em>x<\/em> &gt; 1 and\u00a0<em>y<\/em> &gt; 1, which is enough for this post. More general arguments run into complications due to branch cuts.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Markov numbers are integer solutions to x\u00b2 +\u00a0y\u00b2 +\u00a0z\u00b2 = 3xyz. The Wikipedia article on Markov numbers mentions that Don Zagier studied Markov numbers by looking the approximating equation x\u00b2 +\u00a0y\u00b2 +\u00a0z\u00b2 = 3xyz\u00a0+ 4\/9 which is equivalent to f(x) +\u00a0f(y) = f(z) where\u00a0f(t) is defined as arccosh(3t\/2). It wasn&#8217;t clear to me why the [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[9],"tags":[],"class_list":["post-247025","post","type-post","status-publish","format-standard","hentry","category-math"],"acf":[],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/posts\/247025","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/comments?post=247025"}],"version-history":[{"count":0,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/posts\/247025\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/media?parent=247025"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/categories?post=247025"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/tags?post=247025"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}},{"id":247024,"date":"2026-05-15T07:34:29","date_gmt":"2026-05-15T12:34:29","guid":{"rendered":"https:\/\/www.johndcook.com\/blog\/?p=247024"},"modified":"2026-05-15T08:15:16","modified_gmt":"2026-05-15T13:15:16","slug":"xorshift128-state","status":"publish","type":"post","link":"https:\/\/www.johndcook.com\/blog\/2026\/05\/15\/xorshift128-state\/","title":{"rendered":"Recovering the state of xorshift128"},"content":{"rendered":"<p>I&#8217;ve written a couple posts lately about reverse engineering the internal state of a random number generator, first <a href=\"https:\/\/www.johndcook.com\/blog\/2026\/05\/10\/reverse-mersenne-twister\/\">Mersenne Twister<\/a> then <a href=\"https:\/\/www.johndcook.com\/blog\/2026\/05\/12\/hacking-the-lehmer64-rng\/\">lehmer64<\/a>. This post will look at xorshift128, implemented below.<\/p>\n<pre>import random\r\n\r\n# Seed the generator state\r\na: int = random.getrandbits(32)\r\nb: int = random.getrandbits(32)\r\nc: int = random.getrandbits(32)\r\nd: int = random.getrandbits(32)\r\n\r\nMASK = 0xFFFFFFFF\r\n\r\ndef xorshift128() -&gt; int:\r\n    global a, b, c, d\r\n\r\n    t = d\r\n    s = a\r\n\r\n    t ^= (t &lt;&lt; 11) &amp; MASK t ^= (t &gt;&gt;  8) &amp; MASK\r\n    s ^= (s &gt;&gt; 19) &amp; MASK\r\n\r\n    a, b, c, d = (t ^ s) &amp; MASK, a, b, c\r\n\r\n    return a\r\n<\/pre>\n<h2>Recovering state<\/h2>\n<p>Recovering the internal state of the generator is simple: it&#8217;s the four latest outputs in reverse order. This is illustrated by the following chart.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-medium\" src=\"https:\/\/www.johndcook.com\/xorshift128_chart.png\" alt=\" a b c d output 5081e5ca 79259a41 63e12955 651e537d c793d11c c793d11c 5081e5ca 79259a41 63e12955 ad52e33a ad52e33a c793d11c 5081e5ca 79259a41 f8f09343 f8f09343 ad52e33a c793d11c 5081e5ca a7009622 a7009622 f8f09343 ad52e33a c793d11c fe42a8ef \" width=\"400\" height=\"111\" \/><\/p>\n<p>This means that once we&#8217;ve seen four outputs, we can predict the rest of the outputs. The following code demonstrates this.<\/p>\n<p>Let&#8217;s generate five random values.<\/p>\n<pre>out = [xorshift128() for _ in range(5)]<\/pre>\n<p>Running<\/p>\n<pre>print(hex(out[4]))\r\n<\/pre>\n<p>shows the output 0xc3f4795d.<\/p>\n<p>If we reset the state of the generator using the first four outputs<\/p>\n<pre>d, c, b, a, _ = out\r\nprint(hex(xorshift128()))\r\n<\/pre>\n<p>we get the same result.<\/p>\n<h2>Good stats, bad security<\/h2>\n<p>Mersenne Twister and lehmer64 have good statistical properties, despite being predictable. The xorshift128 generator is even easier to predict, but it also has good statistical properties. These generators would be fine for many applications, such as Monte Carlo simulation, but disastrous for use in cryptography.<\/p>\n<p>The post on lehmer64 mentioned at the end that the internal state of PCG64 can also be recovered from its output. However, doing so requires far more sophisticated math and thousands of hours of compute time. Still, it&#8217;s not adequate for cryptography. For that you&#8217;d need a random number generator designed to be secure, such as <a href=\"https:\/\/www.johndcook.com\/blog\/2019\/03\/02\/chacha-adiantum\/\">ChaCha<\/a>.<\/p>\n<p>So why not just use a cryptographically secure random number generator (CSPRNG) for everything? You could, but the other generators mentioned in this post use less memory and are much faster. PCG64 occupies an interesting middle ground: simple and fast, but not easily reversible.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I&#8217;ve written a couple posts lately about reverse engineering the internal state of a random number generator, first Mersenne Twister then lehmer64. This post will look at xorshift128, implemented below. import random # Seed the generator state a: int = random.getrandbits(32) b: int = random.getrandbits(32) c: int = random.getrandbits(32) d: int = random.getrandbits(32) MASK = [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[5],"tags":[118],"class_list":["post-247024","post","type-post","status-publish","format-standard","hentry","category-computing","tag-rng"],"acf":[],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/posts\/247024","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/comments?post=247024"}],"version-history":[{"count":0,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/posts\/247024\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/media?parent=247024"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/categories?post=247024"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/tags?post=247024"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}},{"id":247022,"date":"2026-05-12T07:20:20","date_gmt":"2026-05-12T12:20:20","guid":{"rendered":"https:\/\/www.johndcook.com\/blog\/?p=247022"},"modified":"2026-05-12T07:20:20","modified_gmt":"2026-05-12T12:20:20","slug":"c-128-bit-int","status":"publish","type":"post","link":"https:\/\/www.johndcook.com\/blog\/2026\/05\/12\/c-128-bit-int\/","title":{"rendered":"Initialize and print 128-bit integers in C"},"content":{"rendered":"<p>If you look very closely at my previous post, you&#8217;ll notice that I initialize a 128-bit integer with a 64-bit value. The 128-bit unsigned integer represents the internal state of a random number generator. Why not initialize it to a 128-bit value? I was trying to keep the code simple.<\/p>\n<p>A surprising feature of C compilers, at least of GCC and Clang, is that you cannot initialize a 128-bit integer to a 128-bit integer <strong>literal<\/strong>. You can&#8217;t directly print a 128-bit integer either, which is why the previous post introduces a function <code>print_u128<\/code>.<\/p>\n<p>The code<\/p>\n<pre>__uint128_t x = 0x00112233445566778899aabbccddeeff;<\/pre>\n<p>Produces the following error message.<\/p>\n<pre>error: integer literal is too large to be represented in any integer type<\/pre>\n<p>The problem isn&#8217;t initializing a 128-bit number to a 128-bit value; the problem is that the compiler cannot parse the literal expression<\/p>\n<pre>0x00112233445566778899aabbccddeeff<\/pre>\n<p>One solution to the problem is to introduce the macro<\/p>\n<pre>#define U128(hi, lo) (((__uint128_t)(hi) &lt;&lt; 64) | (lo))<\/pre>\n<p>and use it to initialize the variable.<\/p>\n<pre>__uint128_t x = U128(0x0011223344556677, 0x8899aabbccddeeff);<\/pre>\n<p>You can verify that <code>x<\/code> has the intended state by calling <code>print_u128<\/code> from the previous post.<\/p>\n<pre>void print_u128(__uint128_t n)\r\n{\r\n    printf(\"0x%016lx%016lx\\n\",\r\n           (uint64_t)(n &gt;&gt; 64),      \/\/ upper 64 bits\r\n           (uint64_t)n);             \/\/ lower 64 bits\r\n}\r\n<\/pre>\n<p>Then<\/p>\n<pre>print_u128(x);<\/pre>\n<p>prints<\/p>\n<pre>0x00112233445566778899aabbccddeeff<\/pre>\n<p><b>Update<\/b>. The code for <code>print_u128<\/code> above compiles cleanly with <code>gcc<\/code> but <code>clang<\/code> gives the following warning.<\/p>\n<pre>warning: format specifies type 'unsigned long' but the argument has type 'uint64_t' (aka 'unsigned long long') [-Wformat]<\/pre>\n<p>You can suppress the warning by including the <code>inttypes<\/code> header and modifying the\u00a0<code>print_u128<\/code> function.<\/p>\n<p>Here&#8217;s the final code. It compiles cleanly under <code>gcc<\/code> and <code>clang<\/code>.<\/p>\n<pre>#include &lt;stdio.h&gt;\r\n#include &lt;stdint.h&gt;\r\n#include &lt;inttypes.h&gt;\r\n#define U128(hi, lo) (((__uint128_t)(hi) &lt;&lt; 64) | (lo))\r\n\r\nvoid print_u128(__uint128_t n)\r\n{\r\n    printf(\"0x%016\" PRIx64 \"%016\" PRIx64 \"\\n\",\r\n           (uint64_t)(n &gt;&gt; 64),\r\n           (uint64_t)n);\r\n}\r\n\r\nint main(void)\r\n{\r\n    __uint128_t x = U128(0x0011223344556677, 0x8899aabbccddeeff);\r\n    print_u128(x);\r\n    return 0;\r\n}\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>If you look very closely at my previous post, you&#8217;ll notice that I initialize a 128-bit integer with a 64-bit value. The 128-bit unsigned integer represents the internal state of a random number generator. Why not initialize it to a 128-bit value? I was trying to keep the code simple. A surprising feature of C [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[5],"tags":[108],"class_list":["post-247022","post","type-post","status-publish","format-standard","hentry","category-computing","tag-programming"],"acf":[],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/posts\/247022","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/comments?post=247022"}],"version-history":[{"count":0,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/posts\/247022\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/media?parent=247022"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/categories?post=247022"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/tags?post=247022"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}},{"id":247020,"date":"2026-05-12T06:07:31","date_gmt":"2026-05-12T11:07:31","guid":{"rendered":"https:\/\/www.johndcook.com\/blog\/?p=247020"},"modified":"2026-05-12T06:07:31","modified_gmt":"2026-05-12T11:07:31","slug":"hacking-the-lehmer64-rng","status":"publish","type":"post","link":"https:\/\/www.johndcook.com\/blog\/2026\/05\/12\/hacking-the-lehmer64-rng\/","title":{"rendered":"Hacking the lehmer64 RNG"},"content":{"rendered":"<p>A couple days ago I wrote about hacking the Mersenne Twister. I explained how to recover the random number generator&#8217;s internal state from a stream of 640 outputs.<\/p>\n<p>This post will do something similar with the lehmer64 random number generator. This generator is very simple to implement. <a href=\"https:\/\/lemire.me\/blog\/2019\/03\/19\/the-fastest-conventional-random-number-generator-that-can-pass-big-crush\/\">Daniel Lemire<\/a> found it to be &#8220;the fastest conventional random number generator that can pass Big Crush,&#8221; a well-respected test for pseudorandom number generators.<\/p>\n<h2>Implementing lehmer64<\/h2>\n<p>The lehmer64 generator can be implemented in C by<\/p>\n<pre>__uint128_t g_lehmer64_state;\r\n\r\nuint64_t lehmer64() {\r\n    g_lehmer64_state *= 0xda942042e4dd58b5ULL;  \r\n  return g_lehmer64_state &gt;&gt; 64;\r\n}\r\n<\/pre>\n<p>The analogous code in Python would have to simulate the overflow behavior of a 128-bit integer by reducing the state mod 2<sup>128<\/sup> after the multiplication.<\/p>\n<p>Reverse engineering lehmer64 is easier than reverse engineering the Mersenne Twister because only three outputs are needed. However, the theory behind the exploit is more sophisticated. See [1].<\/p>\n<p>The following code sets the state to an arbitrary initial seed value and generates three values.<\/p>\n<pre>#include &lt;stdio.h&gt;\r\n#include &lt;stdint.h&gt;\r\n\r\nint main(void)\r\n{\r\n    g_lehmer64_state = 0x4789499d78770934; \/\/ seed\r\n    for (int i = 0; i &lt; 3; i++) {\r\n        printf(\"0x%016lx\\n\", lehmer64());\r\n    }\r\n\r\n    return 0;\r\n}\r\n<\/pre>\n<p>The code prints the following.<\/p>\n<pre>0x3d144d12822bcc2e\r\n0x85a67226191a568d\r\n0x53e803dffc88e8f8\r\n<\/pre>\n<h2>Exploiting lehmer64<\/h2>\n<p>Here is Python code for recovering the state of the lehmer64 generator given in [1].<\/p>\n<pre>def reconstruct(X):\r\n    a = 0xda942042e4dd58b5\r\n    r = round(2.64929081169728e-7 * X[0] + 3.51729342107376e-7 * X[1] + 3.89110109147656e-8 * X[2])\r\n    s = round(3.12752538137199e-7 * X[0] - 1.00664345453760e-7 * X[1] - 2.16685184476959e-7 * X[2])\r\n    t = round(3.54263598631140e-8 * X[0] - 2.05535734808162e-7 * X[1] + 2.73269247090513e-7 * X[2])\r\n    u = r * 1556524 + s * 2249380 + t * 1561981\r\n    v = r * 8429177212358078682 + s * 4111469003616164778 + t * 3562247178301810180\r\n    state = (a*u + v) % (2**128)\r\n    return state\r\n<\/pre>\n<p>Let&#8217;s call <code>reconstruct<\/code> with the output of our C code.<\/p>\n<pre>\r\nX = [0x3d144d12822bcc2e, 0x85a67226191a568d, 0x53e803dffc88e8f8]\r\nprint( hex( reconstruct(X) ) )\r\n<\/pre>\n<p>This prints <\/p>\n<pre>0x3d144d12822bcc2e1b81101c593761c4<\/pre>\n<p>Now for the confusing part: at what point is the number above the state of the generator? Is it the state before or after generating the three values? Neither! It is the state after generating the first value.<\/p>\n<p>We can verify this by modifying the C code as follows and rerunning it.<\/p>\n<pre>\r\nvoid print_u128(__uint128_t n)\r\n{\r\n    printf(\"0x%016lx%016lx\\n\",\r\n           (uint64_t)(n >> 64),      \/\/ upper 64 bits\r\n           (uint64_t)n);             \/\/ lower 64 bits\r\n}\r\n\r\nint main(void)\r\n{\r\n    g_lehmer64_state = 0x4789499d78770934; \/\/ seed\r\n    for (int i = 0; i < 3; i++) {\r\n        printf(\"0x%016lx\\n\", lehmer64());\r\n        printf(\"state: \");\r\n        print_u128(g_lehmer64_state);\r\n    }\r\n \r\n    return 0;\r\n}\r\n<\/pre>\n<p>The main goal of [1] is to recover the state of the PCG generator, not the lehmer64 generator. The latter was a side quest. Recovering the state of PCG64 is much harder; the authors estimate it takes about 20,000 CPU-hours. The paper shows that a technique used as part of pursuing their main goal can quickly recover the lehmer64 state.<\/p>\n<h2>Related posts<\/h2>\n<ul>\n<li class='link'><a href='https:\/\/www.johndcook.com\/blog\/2026\/05\/10\/reverse-mersenne-twister\/'>Recovering the Mersenne Twister internal state with linear algebra<\/a><\/li>\n<li class='link'><a href='https:\/\/www.johndcook.com\/blog\/2017\/07\/07\/testing-the-pcg-random-number-generator\/'>Testing the PCG random number generator<\/a><\/li>\n<li class='link'><a href='https:\/\/www.johndcook.com\/blog\/rng-testing\/'>Random number generator testing<\/a><\/li>\n<\/ul>\n<p>[1] Charles Bouillaguet, Florette Martinez, and Julia Sauvage. Practical seed-recovery for the PCG Pseudo-Random Number Generator. IACR Transactions on Symmetric Cryptology. ISSN 2519-173X, Vol. 2020, No. 3, pp. 175\u2013196.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A couple days ago I wrote about hacking the Mersenne Twister. I explained how to recover the random number generator&#8217;s internal state from a stream of 640 outputs. This post will do something similar with the lehmer64 random number generator. This generator is very simple to implement. Daniel Lemire found it to be &#8220;the fastest [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[5],"tags":[43,118],"class_list":["post-247020","post","type-post","status-publish","format-standard","hentry","category-computing","tag-cryptography","tag-rng"],"acf":[],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/posts\/247020","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/comments?post=247020"}],"version-history":[{"count":0,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/posts\/247020\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/media?parent=247020"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/categories?post=247020"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/tags?post=247020"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}},{"id":247019,"date":"2026-05-11T19:49:41","date_gmt":"2026-05-12T00:49:41","guid":{"rendered":"https:\/\/www.johndcook.com\/blog\/?p=247019"},"modified":"2026-05-11T19:49:41","modified_gmt":"2026-05-12T00:49:41","slug":"euler-function","status":"publish","type":"post","link":"https:\/\/www.johndcook.com\/blog\/2026\/05\/11\/euler-function\/","title":{"rendered":"Euler function"},"content":{"rendered":"<p>This morning I wrote a <a href=\"https:\/\/www.johndcook.com\/blog\/2026\/05\/11\/random-binary-matrices\/\">post<\/a> about the probability that a random matrix over a finite field is invertible. If the field has <em>q<\/em> elements and the matrix has dimensions <em>n<\/em> \u00d7 <em>n<\/em> then the probability is<\/p>\n<p><img class='aligncenter' src='https:\/\/www.johndcook.com\/euler_fun1.svg' alt='p(q, n) = \\prod_{i=1}^n \\left(1 - \\frac{1}{q^i}\\right)' style='background-color:white' height='54' width='175' \/><\/p>\n<p>In that post I made observation that <em>p<\/em>(<em>q<\/em>, <em>n<\/em>) converges very quickly as a function of <em>n<\/em> [1]. One way to see that the convergence is quick is to note that<\/p>\n<p><img class='aligncenter' src='https:\/\/www.johndcook.com\/euler_fun2.svg' alt='\\prod_{i=1}^\\infty \\left(1 - \\frac{1}{q^i}\\right) = \\prod_{i=1}^n \\left(1 - \\frac{1}{q^i}\\right) \\, \\prod_{i=n+1}^\\infty \\left(1 - \\frac{1}{q^i}\\right)' style='background-color:white' height='54' width='336' \/><\/p>\n<p>and<\/p>\n<p><img class='aligncenter' src='https:\/\/www.johndcook.com\/euler_fun3.svg' alt='\\prod_{i=n+1}^\\infty \\left(1 - \\frac{1}{q^i}\\right) = 1 - {\\cal O}\\left(\\frac{1}{q^{n+1}}\\right)' style='background-color:white' height='54' width='237' \/><\/p>\n<p>John Baez pointed out in the comments that <em>p<\/em>(<em>q<\/em>, \u221e) = \u03c6(1\/<em>q<\/em>) where \u03c6 is the Euler function.<\/p>\n<p>Euler was extremely prolific, and many things are named after him. Several functions are known as Euler&#8217;s function, the most common being his totient function in number theory. The Euler function we&#8217;re interested in here is<\/p>\n<p><img class='aligncenter' src='https:\/\/www.johndcook.com\/euler_fun4.svg' alt='\\phi(x) = \\prod_{i=1}^\\infty \\left( 1 - x^i \\right)' style='background-color:white' height='54' width='154' \/><\/p>\n<p>for \u22121 &lt; x &lt; 1. Usually the argument of \u03c6 is denoted &#8220;<em>q<\/em>&#8221; but that would be confusing in our context because our <em>q<\/em>, the number of elements in a field, is the reciprocal of Euler&#8217;s <em>q<\/em>, i.e. <em>x<\/em> = 1\/<em>q<\/em>.<\/p>\n<p>Euler&#8217;s identity [2] (in this context, not to be confused with other Euler identities!) says<\/p>\n<p><img class='aligncenter' src='https:\/\/www.johndcook.com\/euler_fun5.svg' alt='\\phi(x) = \\sum_{n=-\\infty}^\\infty (-1)^n x^{(3n^2 - n)\/2}' style='background-color:white' height='53' width='222' \/><\/p>\n<p>This function is easy to calculate because the series converges very quickly. From the alternating series theorem we have<\/p>\n<p><img class='aligncenter' src='https:\/\/www.johndcook.com\/euler_fun6.svg' alt='\\phi(x) = \\sum_{n=-N}^N (-1)^n (-1)^n x^{(3n^2 - n)\/2} + {\\cal O}\\left( x^{(3(N+1)^2 - (N+1))\/2} \\right)' style='background-color:white' height='57' width='464' \/><\/p>\n<p>When <em>q<\/em> = 2 and so <em>x<\/em> = 1\/2, <em>N<\/em> = 6 is enough to compute \u03c6(<em>x<\/em>) with an error less than 2<sup>\u221270<\/sup>, beyond the precision of a floating point number. When <em>q<\/em> is larger, even fewer terms are needed.<\/p>\n<p>To illustrate this, we have the following Python script.<\/p>\n<pre>\r\ndef phi(x, N):\r\n    s = 0\r\n    for n in range(-N, N+1):\r\n        s += (-1)**n * x**((3*n**2 - n)\/2)\r\n    return s\r\n\r\nprint(phi(0.5, 6))\r\n<\/pre>\n<p>Every digit in the output is correct.<\/p>\n<h2>Related posts<\/h2>\n<ul>\n<li class='link'><a href='https:\/\/www.johndcook.com\/blog\/2024\/07\/23\/q-pochhammer\/'>q-analogs of rising powers<\/a><\/li>\n<li class='link'><a href='https:\/\/www.johndcook.com\/blog\/2021\/11\/11\/another-pentagonal-number-theorem\/'>Another pentagonal number theorem<\/a><\/li>\n<\/ul>\n<p>[1] I didn&#8217;t say that explicitly, but I pointed out that <em>p<\/em>(2, 8) was close to <em>p<\/em>(2, \u221e).<\/p>\n<p>[2] This identity is also known as the pentagonal number theorem because of its connection to <a href=\"https:\/\/www.johndcook.com\/blog\/2021\/11\/10\/partitions-and-pentagons\/\">pentagonal numbers<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This morning I wrote a post about the probability that a random matrix over a finite field is invertible. If the field has q elements and the matrix has dimensions n \u00d7 n then the probability is In that post I made observation that p(q, n) converges very quickly as a function of n [1]. [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[9],"tags":[],"class_list":["post-247019","post","type-post","status-publish","format-standard","hentry","category-math"],"acf":[],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/posts\/247019","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/comments?post=247019"}],"version-history":[{"count":0,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/posts\/247019\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/media?parent=247019"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/categories?post=247019"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/tags?post=247019"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}},{"id":247017,"date":"2026-05-11T10:30:30","date_gmt":"2026-05-11T15:30:30","guid":{"rendered":"https:\/\/www.johndcook.com\/blog\/?p=247017"},"modified":"2026-05-11T10:31:05","modified_gmt":"2026-05-11T15:31:05","slug":"inverse-shift","status":"publish","type":"post","link":"https:\/\/www.johndcook.com\/blog\/2026\/05\/11\/inverse-shift\/","title":{"rendered":"Inverse shift"},"content":{"rendered":"<p>What is the inverse of shifting a sequence to the right? Shifting it to the left, obviously.<\/p>\n<p>But wait a minute. Suppose you have a sequence of eight bits<\/p>\n<p style=\"padding-left: 40px;\"><em>abcdefgh<\/em><\/p>\n<p>and you shift it to the right. You get<\/p>\n<p style=\"padding-left: 40px;\">0<em>abcdefg.<\/em><\/p>\n<p>If you shift this sequence to the left you get<\/p>\n<p style=\"padding-left: 40px;\"><em>abcdefg<\/em>0<\/p>\n<p>You can&#8217;t recover the last element\u00a0<em>h<\/em> because the right-shift destroyed information about\u00a0<em>h<\/em>.<\/p>\n<p>A left-shift doesn&#8217;t fully recover a right-shift, and yet surely left shift and right shift are in some sense inverses.<\/p>\n<p><a href=\"https:\/\/www.johndcook.com\/blog\/2026\/05\/10\/the-linear-algebra-of-bit-twiddling\/\">Yesterday<\/a> I wrote a post about representing bit manipulations, including shifts, as matrix operators. The matrix corresponding to shifting right by\u00a0<em>k<\/em> bits has 1s on the\u00a0<em>k<\/em>th diagonal above the main diagonal and 0s everywhere else. For example, here is the matrix for shifting an 8-bit number right two bits. A black square represents a 1 and a white square represents a 0.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter\" src=\"https:\/\/www.johndcook.com\/right_shift_2_matrix.png\" width=\"360\" height=\"360\" \/><\/p>\n<p>This matrix isn&#8217;t invertible. When you&#8217;d like to take the inverse of a non-invertible matrix, your kneejerk response should be to compute the pseudoinverse. (Technically the <a href=\"https:\/\/www.johndcook.com\/blog\/2018\/05\/05\/svd\/\">Moore-Penrose pseudoinverse<\/a>. There are <a href=\"https:\/\/www.johndcook.com\/blog\/2025\/05\/21\/drazin-pseudoinverse\/\">other<\/a> pseudoinverses, but Moore-Penrose is the most common.)<\/p>\n<p>As you might hope\/expect, the pseudoinverse of a right-shift matrix is a left-shift matrix. In this case the pseudoinverse is simply the transpose, though of course that isn&#8217;t always the case.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter\" src=\"https:\/\/www.johndcook.com\/left_shift_2_matrix.png\" width=\"360\" height=\"360\" \/><\/p>\n<p>If you&#8217;d like to prove that the pseudoinverse of a matrix that shifts right by <em>k<\/em> places is a matrix that shifts left by <em>k<\/em> places, you don&#8217;t have to compute the pseudo inverse per se: you can verify your guess. <a href=\"https:\/\/www.johndcook.com\/blog\/2018\/04\/24\/moore-penrose-pseudoinverse-is-not-an-adjoint\/\">This post<\/a> gives four requirements for a pseudoinverse. You can prove that left shift is the inverse of right shift by showing that it satisfies the four equations.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>What is the inverse of shifting a sequence to the right? Shifting it to the left, obviously. But wait a minute. Suppose you have a sequence of eight bits abcdefgh and you shift it to the right. You get 0abcdefg. If you shift this sequence to the left you get abcdefg0 You can&#8217;t recover the [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[5],"tags":[198],"class_list":["post-247017","post","type-post","status-publish","format-standard","hentry","category-computing","tag-linear-algebra"],"acf":[],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/posts\/247017","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/comments?post=247017"}],"version-history":[{"count":0,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/posts\/247017\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/media?parent=247017"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/categories?post=247017"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.johndcook.com\/blog\/wp-json\/wp\/v2\/tags?post=247017"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}]