xref: /titanic_41/usr/src/lib/libshell/common/scripts/mandelbrotset1.sh (revision cde2885fdf538266ee2a3b08dee2d5075ce8fa2b)
1#!/usr/bin/ksh93
2
3#
4# CDDL HEADER START
5#
6# The contents of this file are subject to the terms of the
7# Common Development and Distribution License (the "License").
8# You may not use this file except in compliance with the License.
9#
10# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
11# or http://www.opensolaris.org/os/licensing.
12# See the License for the specific language governing permissions
13# and limitations under the License.
14#
15# When distributing Covered Code, include this CDDL HEADER in each
16# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
17# If applicable, add the following below this CDDL HEADER, with the
18# fields enclosed by brackets "[]" replaced with your own identifying
19# information: Portions Copyright [yyyy] [name of copyright owner]
20#
21# CDDL HEADER END
22#
23
24#
25# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
26# Use is subject to license terms.
27#
28
29#
30# mandelbrotset1 - a simple mandelbrot set generation and
31# parallel execution demo
32#
33
34# Solaris needs /usr/xpg6/bin:/usr/xpg4/bin because the tools in /usr/bin are not POSIX-conformant
35export PATH=/usr/xpg6/bin:/usr/xpg4/bin:/bin:/usr/bin
36
37# Make sure all math stuff runs in the "C" locale to avoid problems
38# with alternative # radix point representations (e.g. ',' instead of
39# '.' in de_DE.*-locales). This needs to be set _before_ any
40# floating-point constants are defined in this script).
41if [[ "${LC_ALL}" != "" ]] ; then
42    export \
43        LC_MONETARY="${LC_ALL}" \
44        LC_MESSAGES="${LC_ALL}" \
45        LC_COLLATE="${LC_ALL}" \
46        LC_CTYPE="${LC_ALL}"
47        unset LC_ALL
48fi
49export LC_NUMERIC=C
50
51function printmsg
52{
53	print -u2 "$*"
54}
55
56function fatal_error
57{
58	print -u2 "${progname}: $*"
59	exit 1
60}
61
62# Get terminal size and put values into a compound variable with the integer
63# members "columns" and "lines"
64function get_term_size
65{
66	nameref rect=$1
67
68	rect.columns=${ tput cols ; } || return 1
69	rect.lines=${ tput lines ; }  || return 1
70
71	return 0
72}
73
74function print_color
75{
76	print -r -n -- "${symbollist:${1}:1}"
77	return 0
78}
79
80function mandelbrot
81{
82	nameref result=$1
83	float   x=$2
84	float   y=$3
85	float   xx
86	float   yy
87	float   x1=$4
88	float   y1=$5
89	integer iteration=$6
90	integer max_iteration=$7
91	float   mag
92
93	for (( mag=0 ; mag < max_mag && iteration < max_iteration ; iteration++ )) ; do
94		((
95			xx=x*x ,
96			yy=y*y ,
97			mag=xx+yy ,
98			y=x*y*2+y1 ,
99			x=xx-yy+x1
100		))
101	done
102
103	(( result=iteration ))
104
105	return 0
106}
107
108# build mandelbrot image serially
109function loop_serial
110{
111	integer value
112
113	for (( y=y_min ; y < y_max ; y+=stepwidth )) ; do
114		for (( x=x_min ; x < x_max ; x+=stepwidth )) ; do
115			mandelbrot value ${x} ${y} ${x} ${y} 1 ${symbollistlen}
116			print_color ${value}
117		done
118
119		print
120	done
121
122	return 0
123}
124
125# build mandelbrot image using parallel worker jobs
126function loop_parallel
127{
128	integer numjobs=0
129	# the following calculation suffers from rounding errors
130	integer lines_per_job=$(( ((m_height+(numcpus-1)) / numcpus) ))
131
132	printmsg $"# lines_per_job=${lines_per_job}"
133	printmsg $"# numcpus=${numcpus}"
134
135	# "renice" worker jobs
136	set -o bgnice
137
138	if [[ "${TMPDIR}" == "" ]] ; then
139		TMPDIR="/tmp"
140	fi
141
142	# try to generate a job identifer prefix which is unique across multiple hosts
143	jobident="job_host_$(uname -n)pid_$$_ppid${PPID}"
144
145	printmsg $"## prepare..."
146	for (( y=y_min ; y < y_max ; y+=(stepwidth*lines_per_job) )) ; do
147		rm -f "${TMPDIR}/mandelbrot_${jobident}_child_$y.joboutput"
148
149		(( numjobs++ ))
150	done
151
152	printmsg $"## running ${numjobs} children..."
153	for (( y=y_min ; y < y_max ; y+=(stepwidth*lines_per_job) )) ; do
154		(
155			integer value
156
157			for (( ; y < y_max && lines_per_job-- > 0 ; y+=stepwidth )) ; do
158				for (( x=x_min ; x < x_max ; x+=stepwidth )) ; do
159					mandelbrot value ${x} ${y} ${x} ${y} 1 ${symbollistlen}
160					print_color ${value}
161				done
162
163				print
164			done >"${TMPDIR}/mandelbrot_${jobident}_child_$y.joboutput"
165		) &
166	done
167
168	printmsg $"## waiting for ${numjobs} children..."
169	wait
170
171	printmsg $"## output:"
172	for (( y=y_min ; y < y_max ; y+=(stepwidth*lines_per_job) )) ; do
173		print -- "$( < "${TMPDIR}/mandelbrot_${jobident}_child_$y.joboutput")"
174		rm "${TMPDIR}/mandelbrot_${jobident}_child_$y.joboutput"
175	done
176
177	return 0
178}
179
180function usage
181{
182	OPTIND=0
183	getopts -a "${progname}" "${mandelbrotset1_usage}" OPT '-?'
184	exit 2
185}
186
187# main
188builtin basename
189builtin cat
190builtin rm
191builtin uname # loop_parallel needs the ksh93 builtin version to generate unique job file names
192
193typeset progname="${ basename "${0}" ; }"
194
195float x_max
196float x_min
197float y_max
198float y_min
199float m_width
200float m_height
201float max_mag
202float stepwidth
203integer numcpus
204
205# terminal size rect
206typeset -C termsize=(
207	integer columns=-1
208	integer lines=-1
209)
210
211get_term_size termsize || fatal_error $"Could not get terminal size."
212
213typeset symbollist='    .:0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ%#'
214typeset symbollistlen=$(( ${#symbollist} - 1))
215typeset mode="parallel"
216
217max_mag=400
218stepwidth=0.1
219numcpus=16
220
221(( m_width=termsize.columns-1 , m_height=termsize.lines-2 ))
222
223typeset -r mandelbrotset1_usage=$'+
224[-?\n@(#)\$Id: mandelbrotset1 (Roland Mainz) 2008-11-04 \$\n]
225[-author?Roland Mainz <roland.mainz@nrubsig.org>]
226[+NAME?mandelbrotset1 - generate mandelbrot set fractals with ksh93]
227[+DESCRIPTION?\bmandelbrotset1\b mandelbrot set fractal generator
228	which runs either in serial or parallel mode (using multiple worker jobs).]
229[w:width?Width of fractal.]:[width]
230[h:height?Height of fractal.]:[height]
231[s:symbols?Symbols to build the fractal from.]:[symbolstring]
232[m:mag?Magnification level.]:[magnificationlevel]
233[p:stepwidth?Width per step.]:[widthperstep]
234[S:serial?Run in serial mode.]
235[P:parallel?Run in parallel mode.]
236[M:mode?Execution mode.]:[mode]
237[C:numcpus?Number of processors used for parallel execution.]:[numcpus]
238[+SEE ALSO?\bjuliaset1\b(1), \bksh93\b(1)]
239'
240
241while getopts -a "${progname}" "${mandelbrotset1_usage}" OPT ; do
242#	printmsg "## OPT=|${OPT}|, OPTARG=|${OPTARG}|"
243	case ${OPT} in
244		w)	m_width="${OPTARG}"	;;
245		h)	m_height="${OPTARG}"	;;
246		s)	symbollist="${OPTARG}"	;;
247		m)	max_mag="${OPTARG}"	;;
248		p)	stepwidth="${OPTARG}"	;;
249		S)	mode="serial"		;;
250		P)	mode="parallel"		;;
251		M)	mode="${OPTARG}"	;;
252		C)	numcpus="${OPTARG}"	;;
253		*)	usage			;;
254	esac
255done
256shift $((OPTIND-1))
257
258printmsg "# width=${m_width}"
259printmsg "# height=${m_height}"
260printmsg "# max_mag=${max_mag}"
261printmsg "# stepwidth=${stepwidth}"
262printmsg "# symbollist='${symbollist}'"
263printmsg "# mode=${mode}"
264
265(( symbollistlen=${#symbollist}-1 ))
266
267((
268	x_max=m_width*stepwidth/2. ,
269	x_min=-x_max ,
270	y_max=m_height*stepwidth/2. ,
271	y_min=-y_max
272))
273
274case "${mode}" in
275	parallel)	loop_parallel	; exit $? ;;
276	serial)		loop_serial	; exit $? ;;
277	*)		fatal_error $"Unknown mode \"${mode}\"." ;;
278esac
279
280fatal_error "not reached."
281# EOF.
282