Skip to content

Instantly share code, notes, and snippets.

@leagris
Last active January 6, 2023 17:27
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save leagris/59e1b7e72462024b278652696f375e71 to your computer and use it in GitHub Desktop.
Save leagris/59e1b7e72462024b278652696f375e71 to your computer and use it in GitHub Desktop.
Mandelbrot for POSIX shell
#!/usr/bin/env sh
#
# Modified version of bash mandelbrot from Mecki stackoverflow.com/users/15809
# found at https://stackoverflow.com/a/63749612/1765658
# Avoiding forks and running bc as background task, reducing
# execution time, from many hours to less than 10' (on my desktop)
#
# Modified version for POSIX shell by Léa Gris:
# https://gist.github.com/leagris/59e1b7e72462024b278652696f375e71
# Improved to addressing fifos by FDs.
# Spawn 4 instances of bc to parallel compute 2 characters of 2 pixels
BAILOUT=16
MAX_ITERATIONS=1000
# Cleanup resources and state at end of execution
unset tempdir
trap 'rm -fr -- "$tempdir";stty "$old_settings";printf "\\e(B\\e[m\\n";exit' EXIT INT
if [ -t 0 ] && [ -t 1 ]; then
old_settings=$(stty -g) || exit
stty -icanon -echo min 0 time 3 || exit
# CSI CUP 1000 1000 move cursor to 1000 1000
# CSI CSR Get status report cursor position y;xR
printf '\e[1000;1000H\e[6n'
pos="$(dd count=1 2> /dev/null)"
pos="${pos%R*}"
pos="${pos##*\[}"
x="${pos##*;}"
y="${pos%%;*}"
else
x=80
y=24
fi
ALPHWIDTH=$((x / 2))
AWIDE=$((ALPHWIDTH - 1))
tempdir=$(mktemp -d) || exit 1
bc1in="$tempdir/bc1in"
bc1out="$tempdir/bc1out"
bc2in="$tempdir/bc2in"
bc2out="$tempdir/bc2out"
bc3in="$tempdir/bc3in"
bc3out="$tempdir/bc3out"
bc4in="$tempdir/bc4in"
bc4out="$tempdir/bc4out"
mkfifo \
"$bc1in" "$bc1out" \
"$bc2in" "$bc2out" \
"$bc3in" "$bc3out" \
"$bc4in" "$bc4out"
# Spawn bc instances on their own FD
exec 2<&-;
exec 2<>"$bc1in";
exec 3<>"$bc1out"
bc -l <&2 >&3 &
exec 4<>"$bc2in"
exec 5<>"$bc2out"
bc -l <&4 >&5 &
exec 6<>"$bc3in"
exec 7<>"$bc3out"
bc -l <&6 >&7 &
exec 8<>"$bc4in"
exec 9<>"$bc4out"
bc -l <&8 >&9 &
# bc initialization code shared by all instances
init_bc="
scale=16
bailout=$BAILOUT
alphwidth=$ALPHWIDTH
maxiter=$MAX_ITERATIONS
# Integer division reminder
define reminder(x,y) {
os=scale # Save scale setting
scale=0 # Integer scale
x/=1 # x to integer
y/=1 # y to integer
x%=y # Integer division reminder
scale=os # Restore scale setting
return x
}
# Index to ANSI CSI x-term 256-clours code to form a gradient
define color(i) {
c=reminder(i+23,26) # Repeat 26-colours gradient
if(c<6)return c+16 # Black to Blue
if(c<12)return (c-6)*42+21 # Blue to White
if(c<16)return 243-c # White to Yellow
if(c<21)return 220-(c-16)*6 # Yellow to Red
return 196-(c-21)*36 # Red to Black
}
define iterate (x,y) {
zi=0; zr=0; cr=x-.5
for ( i=1 ; i < maxiter ; i++ ) {
zr2=zr^2-zi^2+cr
zi2=2*zr*zi+y
if ( (zi^2+zr^2) > bailout ) return color(i)
zr=zr2;zi=zi2
}
return 0
}
0
"
# Initialise all bc instances
printf %s "$init_bc" >&2 # init bc1
printf %s "$init_bc" >&4 # Init bc2
printf %s "$init_bc" >&6 # init bc3
printf %s "$init_bc" >&8 # Init bc4
# Wait for all bc instances to be ready
read -r _ <&3
read -r _ <&5
read -r _ <&7
read -r _ <&9
paintchar() {
fg=$1 bg=$2
if [ "$obg" -ne "$bg" ]; then
obg=$bg
printf '\e[48;5;%dm' "$bg" #tput setab "$bg"
fi
if [ "$fg" -ne "$bg" ]; then
if [ "$ofg" -ne "$fg" ]; then
ofg=$fg
printf '\e[38;5;%dm' "$fg" #tput setaf "$fg"
fi
printf '\342\226\200' # UTF-8 U+2580 UPPER HALF BLOCK
else
printf ' '
fi
}
mandelbrot() {
printf \\n
y=$((0 - AWIDE))
while [ "$y" -lt "$AWIDE" ]; do
x=$((0 - AWIDE))
y2=$((y + 1))
ofg=-1 obg=-1
while [ "$x" -lt "$AWIDE" ]; do
printf 'iterate ( %d / alphwidth, %d / alphwidth )\n' $x $y >&2
printf 'iterate ( %d / alphwidth, %d / alphwidth )\n' $x $y2 >&4
x=$((x + 1))
printf 'iterate ( %d / alphwidth, %d / alphwidth )\n' $x $y >&6
printf 'iterate ( %d / alphwidth, %d / alphwidth )\n' $x $y2 >&8
read -r fg1 <&3
read -r bg1 <&5
read -r fg2 <&7
read -r bg2 <&9
paintchar "$fg1" "$bg1"
paintchar "$fg2" "$bg2"
x=$((x + 1))
done
y=$((y + 2))
printf '\e(B\e[m\n' #tput sgr0; printf \\n
done
printf \\n
}
mandelbrot
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment