Math::N...nCarpet(3)
okay bestie, sit down, hydrate, and let me tell you about the User Contributed Perl Documentation page for Math::N...nCarpet(3) β aka the Haferman carpet, and yes it's as pretty as it sounds π
β¨ NAME β¨
Math::NumSeq::HafermanCarpet β bits of the Haferman carpet π
π¦ SYNOPSIS π¦
use Math::NumSeq::HafermanCarpet;
my $seq = Math::NumSeq::HafermanCarpet->new;
my ($i, $value) = $seq->next;
πΈ DESCRIPTION πΈ
okay so this sequence is literally just 0,1 bits of the Haferman carpet pattern, flattened out for plotting in Z-Order or whatever similar vibe you're going for. iconic, no notes.
0,1,0,1,0,1,0,1,0, 0,1,0,1,0,1,0,1,0, 0,1,0,1,0,1,0,1,0, 0,..
starting i=0
When you plot that in Z-Order with radix=3, it begins like this. And ngl, at a bigger size you get this stunning pattern of interlocking loops π β it's giving woven. it's giving textile. it's giving art.
* * * *** * *** * * * * * * *** * *** * * * * * *
* ** ** ***** ***** ** ** ** ** ** ***** ***** ** ** ** ** ** *
* * * *** * *** * * * * * * *** * *** * * * * * *
* * * * *** * * * * * * * * *** * * * * * * *
* ** ** ** ***** ** ** ** ** ** ** ** ***** ** ** ** ** ** ** *
* * * * *** * * * * * * * * *** * * * * * * *
* * * *** * *** * * * * * * *** * *** * * * * * *
* ** ** ***** ***** ** ** ** ** ** ***** ***** ** ** ** ** ** *
* * * *** * *** * * * * * * *** * *** * * * * * *
*** * *** * * * *** * ****** * *** * * * *** * ****** * ***
**** ***** ** ** ***** ******** ***** ** ** ***** ******** ****
*** * *** * * * *** * ****** * *** * * * *** * ****** * ***
* *** * * * * * *** * * *** * * * * * *** * * *** *
* ***** ** ** ** ** ***** ** ***** ** ** ** ** ***** ** ***** *
* *** * * * * * *** * * *** * * * * * *** * * *** *
*** * *** * * * *** * ****** * *** * * * *** * ****** * ***
**** ***** ** ** ***** ******** ***** ** ** ***** ******** ****
*** * *** * * * *** * ****** * *** * * * *** * ****** * ***
* * * *** * *** * * * * * * *** * *** * * * * * *
* ** ** ***** ***** ** ** ** ** ** ***** ***** ** ** ** ** ** *
* * * *** * *** * * * * * * *** * *** * * * * * *
* * * * *** * * * * * * * * *** * * * * * * *
* ** ** ** ***** ** ** ** ** ** ** ** ***** ** ** ** ** ** ** *
* * * * *** * * * * * * * * *** * * * * * * *
* * * *** * *** * * * * * * *** * *** * * * * * *
* ** ** ***** ***** ** ** ** ** ** ***** ***** ** ** ** ** ** *
* * * *** * *** * * * * * * *** * *** * * * * * *
So the pattern is formed by a "morphism" β literally every single 0 or 1 bit expands into a 3x3 array, like so:
1 1 1 0 1 0
0 -> 1 1 1 1 -> 1 0 1
1 1 1 0 1 0
For the purpose of this sequence those arrays get flattened, so:
0 -> 1,1,1,1,1,1,1,1,1
1 -> 0,1,0,1,0,1,0,1,0
The sequence starts from a single initial value 0. The expansion rules get applied twice, growing that one little value into 9*9=81 values. Then the rules get applied to each of those values twice again β 9^4=6561 values, and so on, forever and ever, amen. π₯
An even number of expansion steps means all the existing values stay unchanged. But if an odd number of expansions were done, then the first bit flips 0<->1 π (so like, don't do that unless you want chaos). The even number of expansions can also be expressed as each bit morphing into an 81-long run:
0 -> 0,1,0,1,0,1,0,1,0, # 9 times repeat
0,1,0,1,0,1,0,1,0,
0,1,0,1,0,1,0,1,0,
...
1 -> 1,1,1,1,1,1,1,1,1, # 9 times repeat
0,1,0,1,0,1,0,1,0, # alternate 111..111 or 010..010
1,1,1,1,1,1,1,1,1,
0,1,0,1,0,1,0,1,0,
...
See "Ith" below for how the position of the lowest odd digit of i in base-9 determines the sequence values. π
π Initial 1 π
Option initial_value => 1 can start the sequence from a single value 1 instead. plot twist, but make it math.
# initial_value => 1
1,1,1,1,1,1,1,1,1,0,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,0,1,0,1,0,...
When plotted in Z-Order this begins:
*** * ****** * ****** * *** * * * *** * *** * * * *** * ***
**** ******** ******** ***** ** ** ***** ***** ** ** ***** ****
*** * ****** * ****** * *** * * * *** * *** * * * *** * ***
* *** * * *** * * *** * * * * * *** * * * * * *** *
* ***** ** ***** ** ***** ** ** ** ** ***** ** ** ** ** ***** *
* *** * * *** * * *** * * * * * *** * * * * * *** *
*** * ****** * ****** * *** * * * *** * *** * * * *** * ***
**** ******** ******** ***** ** ** ***** ***** ** ** ***** ****
*** * ****** * ****** * *** * * * *** * *** * * * *** * ***
*** * ****** * ****** * ****** * *** * * * *** * ****** * ***
**** ******** ******** ******** ***** ** ** ***** ******** ****
*** * ****** * ****** * ****** * *** * * * *** * ****** * ***
* *** * * *** * * *** * * *** * * * * * *** * * *** *
* ***** ** ***** ** ***** ** ***** ** ** ** ** ***** ** ***** *
* *** * * *** * * *** * * *** * * * * * *** * * *** *
*** * ****** * ****** * ****** * *** * * * *** * ****** * ***
**** ******** ******** ******** ***** ** ** ***** ******** ****
*** * ****** * ****** * ****** * *** * * * *** * ****** * ***
*** * ****** * ****** * *** * * * *** * *** * * * *** * ***
**** ******** ******** ***** ** ** ***** ***** ** ** ***** ****
*** * ****** * ****** * *** * * * *** * *** * * * *** * ***
* *** * * *** * * *** * * * * * *** * * * * * *** *
* ***** ** ***** ** ***** ** ** ** ** ***** ** ** ** ** ***** *
* *** * * *** * * *** * * * * * *** * * * * * *** *
*** * ****** * ****** * *** * * * *** * *** * * * *** * ***
**** ******** ******** ***** ** ** ***** ***** ** ** ***** ****
*** * ****** * ****** * *** * * * *** * *** * * * *** * ***
This form has some 1s where the initial_value=0 had 0s. The positions of these extra 1s are the box fractal β obsessed with her actually:
* * * * * * * *
* * * *
* * * * * * * *
* * * *
* *
* * * *
* * * * * * * *
* * * *
* * * * * * * *
* * * *
* *
* * * *
* *
*
* *
* * * *
* *
* * * *
* * * * * * * *
* * * *
* * * * * * * *
* * * *
* *
* * * *
* * * * * * * *
* * * *
* * * * * * * *
π Inverse π
The inverse => 1 option (it's a boolean, bestie) can invert the 0,1 bits to instead be 1,0. And yes, you can apply this to any of the types. For example, on the default initial_value=0:
# inverse => 1
1,0,1,0,1,0,1,0,1,1,0,1,0,1,0,1,0,1,1,0,1,0,1,0,1,0,1,1,0,1,0,1,...
π§ Plotting Order π§
The Z-Order curve (per for example Math::PlanePath::ZOrderCurve) numbers its sub-parts like this:
+---+---+---+
| 6 | 7 | 8 |
+---+---+---+
| 3 | 4 | 5 |
+---+---+---+
| 0 | 1 | 2 |
+---+---+---+
This suits the sequence here because the numbering is alternately odd and even in adjacent sub-parts β the way it just alternates, we love a checkerboard moment:
+------+------+------+
| even | odd | even |
+------+------+------+
| odd | even | odd |
+------+------+------+
| even | odd | even |
+------+------+------+
Any self-similar expansion which numbers by an odd/even alternation like this gives the same result. That includes the Peano curve, Wunderlich's serpentine and meander, Haverkort's Kochel curve, and the reflected Gray code path (radix=3). a whole squad of curves. π
Incidentally, drawing each pixel by this sequence is not very efficient β lowkey wasteful, ngl. It's much faster to follow the array expansions described above and block copy areas of "0" or "1". An initial single pixel 0 expands to 3x3 then 9x9, etc. Two images representing a "0" or a "1" can be maintained, though with care some copying of sub-parts allows just one image to be built up. Peep examples/other/haferman-carpet-x11.pl for some code doing exactly that. π«Ά
π FUNCTIONS π
See "FUNCTIONS" in Math::NumSeq for the behaviour common to all path classes.
"$seq = Math::NumSeq::HafermanCarpet->new (initial_value => $integer, inverse => $bool)"β Create and return a new sequence object."initial_value"can be0or1.
π Random Access π
"$value = $seq->ith($i)"β Return the$i'th value from the sequence. simple as that, bestie β¨
β¨ FORMULAS β¨
π Ith
Okay bestie, so the tea on that morphism from earlier is that it hunts down the least significant odd digit β as in 1, 3, 5, or 7 β when i is written out in base-9. Periodt.
i lowest base-9
digit 1,3,5,7 value
-------------- -------------
even position 1
odd position 0
no such digit initial_value
Position counted from low end.
Least significant digit is position=0 so is an "even position".
Heads up: positions get counted from the low end, so the least significant digit is position=0, which makes it an "even position" β yes, zero is even, no cap.
Example time, because we love a concrete moment. If i=609, that's base-9 "746" and its lowest odd digit is the "7" which is 2 digits from the low end, so that's an "even position", which means value=1. Slay.
Or if i=357, that's base-9 "436" and its lowest odd digit is the "3" which is 1 digit from the low end, so that's an "odd position", so value=0. There we go.
And if i contains, like, zero odd base-9 digits at all? Then it's the "no such digit" case and the result is the "initial_value". For example i=58 is base-9 "64" and has no odd digits, so value=0 in the default "initial_value=0". These i with no odd base-9 digit are literally the box fractal pattern shown above β they're the places where initial_value 0 or 1 changes the sequence value. Iconic behavior tbh.
You can also think of "Position of lowest odd base-9 digit" as "count trailing even base-9 digits". If i is entirely even digits then that count should be reckoned as infinite by imagining 0 digits extending infinitely at the high end of i. That "infinite" case is then the "no such digit" of the table. We love a dramatic infinite moment.
So the value assigned to the three cases β odd, even, none β can each be 0 or 1, giving a total of 8 combinations. The cases above are 1,0,0 and 1,0,1, and their 1<->0 inverses 0,1,1 and 0,1,0 per the "inverse" option are the four most interesting combinations. The box fractal 0,0,1 and its inverse 1,1,0 are interesting but not generated by the code here. And the remaining two β 0,0,0 which is all zeros, or 1,1,1 all ones β are not very interesting. We said what we said. π
π Density
The number of 1s in the first 9^k many values from the sequence is as follows. Get your reading glasses, bestie, this is a whole formula moment.
9^(k+1) - (2*(-1)^k + 7) * 5^k
Seq1s_init0(k) = ------------------------------
14
and for initial_value=1:
9^(k+1) - (2*(-1)^k - 7) * 5^k
Seq1s_init1(k) = ------------------------------
14
The difference between the two is 5^k, like:
Seq1s_init1 = Seq1s_init0 + 5^k
That difference is literally the box fractal 1s described above β the ones you gain in the initial_value=1 form. They're at positions where i has only even digits 0,2,4,6,8 in base 9, so 5 digit possibilities at each of k many digit positions, giving 5^k. Math is so giving.
The formulas can be calculated by considering how the 0s and 1s expand. The array morphism form with initial_value=1 is given by Eric Weinstein β shoutout:
<http://mathworld.wolfram.com/HafermanCarpet.html>,
<http://oeis.org/A118005>
Each point expands like:
0 -> 9 of 1s
1 -> 4 of 1s plus 5 of 0s
So the 1s in the expanded form are therefore 9 from each existing "0" and 4 from each existing "1". Since 0s+1s = 9^k, this can be expressed in terms of Array1s:
Array1s(k+1) = 4*Array1s(k) + 9*Array0s(k)
= 4*Array1s(k) + 9*(9^k - Array1s(k)) # 0s+1s=9^k
= 9^(k+1) - 5*Array1s(k)
Expanding this recurrence repeatedly gives:
Array1s(k) = 5^0 * 9^k
- 5^1 * 9^(k-1)
+ 5^2 * 9^(k-2)
...
- (-5)^(k-1) * 9^1
- (-5)^k * 9^0 * Array1s(0)
The alternating signs in each term are -5 as the increasing power. Since Array1s(0)=1 the last term is included, and the powers sum as follows in the usual way:
9^(k+1) - (-5)^(k+1)
Array1s_init1(k) = --------------------
9 - (-5)
If the initial starting cell is 0 instead of 1, then Array1s(0)=0 and the last term (-1)^k * 5^k is omitted. Subtracting that leaves:
9^(k+1) - 9*(-5)^k
Array1s_init0(k) = ------------------
9 - (-5)
For the sequence forms here, the initial value does not change β unlike the array alternating 0<->1. The sequence takes the array starting 0 or 1 according as k is even or odd, thereby ensuring the first value is 0. So:
Seq1s_init0(k) = / Array1s_init0(k) if k even
\ Array1s_init1(k) if k odd
The term (2*(-1)^k - 7) seen above in the Seq1s_init0() formula selects between 9 or 5 as the multiplier for (-5)^k. That 9 or 5 multiplier is the difference between the two Array1s forms. It's giving selector energy. π₯
π SEE ALSO
Math::NumSeqMath::PlanePath::ZOrderCurve,Math::PlanePath::PeanoCurve,Math::PlanePath::WunderlichSerpentine,Math::PlanePath::KochelCurve,Math::PlanePath::GrayCode,Math::PlanePath::SquareReplicateexamples/other/haferman-carpet-x11.pldraws the carpet interactively with"X11::Protocol". Obsessed with that tbh. π
π HOME PAGE
<http://user42.tuxfamily.org/math-numseq/index.html>
π LICENSE
Copyright 2013, 2014, 2016, 2017, 2019, 2020 Kevin Ryde
Math-NumSeq is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version.
Math-NumSeq is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with Math-NumSeq. If not, see <http://www.gnu.org/licenses/>.
No cap bestie, that's the whole page. Perlv5.42.0, dated2025-07-25β and you just read the entireMath::N...nCarpet(3)manual like the main character you are. Stay slaying, drink water, and may your base-9 digits always be even. πβ¨π¦
π¦ where this page comes from
- fedora 43: perl-Math-NumSeq 75-11.fc43 Β· GPL-3.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms