1..
2    PLEASE DO NOT EDIT DIRECTLY. EDIT THE .rst.in FILE PLEASE.
3
4Cookbook part 2: Random things, and some math
5================================================================
6
7Randomly selecting words from a list
8----------------------------------------------------------------
9
10Given this `word list <https://github.com/johnkerl/miller/blob/master/docs/data/english-words.txt>`_, first take a look to see what the first few lines look like:
11
12::
13
14    $ head data/english-words.txt
15    a
16    aa
17    aal
18    aalii
19    aam
20    aardvark
21    aardwolf
22    aba
23    abac
24    abaca
25
26Then the following will randomly sample ten words with four to eight characters in them:
27
28::
29
30    $ mlr --from data/english-words.txt --nidx filter -S 'n=strlen($1);4<=n&&n<=8' then sample -k 10
31    thionine
32    birchman
33    mildewy
34    avigate
35    addedly
36    abaze
37    askant
38    aiming
39    insulant
40    coinmate
41
42Randomly generating jabberwocky words
43----------------------------------------------------------------
44
45These are simple *n*-grams as `described here <http://johnkerl.org/randspell/randspell-slides-ts.pdf>`_. Some common functions are `located here <https://github.com/johnkerl/miller/blob/master/docs/ngrams/ngfuncs.mlr.txt>`_. Then here are scripts for `1-grams <https://github.com/johnkerl/miller/blob/master/docs/ngrams/ng1.mlr.txt>`_ `2-grams <https://github.com/johnkerl/miller/blob/master/docs/ngrams/ng2.mlr.txt>`_ `3-grams <https://github.com/johnkerl/miller/blob/master/docs/ngrams/ng3.mlr.txt>`_ `4-grams <https://github.com/johnkerl/miller/blob/master/docs/ngrams/ng4.mlr.txt>`_, and `5-grams <https://github.com/johnkerl/miller/blob/master/docs/ngrams/ng5.mlr.txt>`_.
46
47The idea is that words from the input file are consumed, then taken apart and pasted back together in ways which imitate the letter-to-letter transitions found in the word list -- giving us automatically generated words in the same vein as *bromance* and *spork*:
48
49::
50
51    $ mlr --nidx --from ./ngrams/gsl-2000.txt put -q -f ./ngrams/ngfuncs.mlr -f ./ngrams/ng5.mlr
52    beard
53    plastinguish
54    politicially
55    noise
56    loan
57    country
58    controductionary
59    suppery
60    lose
61    lessors
62    dollar
63    judge
64    rottendence
65    lessenger
66    diffendant
67    suggestional
68
69Program timing
70----------------------------------------------------------------
71
72This admittedly artificial example demonstrates using Miller time and stats functions to introspectively acquire some information about Miller's own runtime. The ``delta`` function computes the difference between successive timestamps.
73
74::
75
76    $ ruby -e '10000.times{|i|puts "i=#{i+1}"}' > lines.txt
77
78    $ head -n 5 lines.txt
79    i=1
80    i=2
81    i=3
82    i=4
83    i=5
84
85    mlr --ofmt '%.9le' --opprint put '$t=systime()' then step -a delta -f t lines.txt | head -n 7
86    i     t                 t_delta
87    1     1430603027.018016 1.430603027e+09
88    2     1430603027.018043 2.694129944e-05
89    3     1430603027.018048 5.006790161e-06
90    4     1430603027.018052 4.053115845e-06
91    5     1430603027.018055 2.861022949e-06
92    6     1430603027.018058 3.099441528e-06
93
94    mlr --ofmt '%.9le' --oxtab \
95      put '$t=systime()' then \
96      step -a delta -f t then \
97      filter '$i>1' then \
98      stats1 -a min,mean,max -f t_delta \
99      lines.txt
100    t_delta_min  2.861022949e-06
101    t_delta_mean 4.077508505e-06
102    t_delta_max  5.388259888e-05
103
104Computing interquartile ranges
105----------------------------------------------------------------
106
107For one or more specified field names, simply compute p25 and p75, then write the IQR as the difference of p75 and p25:
108
109::
110
111    $ mlr --oxtab stats1 -f x -a p25,p75 \
112        then put '$x_iqr = $x_p75 - $x_p25' \
113        data/medium
114    x_p25 0.246670
115    x_p75 0.748186
116    x_iqr 0.501516
117
118For wildcarded field names, first compute p25 and p75, then loop over field names with ``p25`` in them:
119
120::
121
122    $ mlr --oxtab stats1 --fr '[i-z]' -a p25,p75 \
123        then put 'for (k,v in $*) {
124          if (k =~ "(.*)_p25") {
125            $["\1_iqr"] = $["\1_p75"] - $["\1_p25"]
126          }
127        }' \
128        data/medium
129    i_p25 2501
130    i_p75 7501
131    x_p25 0.246670
132    x_p75 0.748186
133    y_p25 0.252137
134    y_p75 0.764003
135    i_iqr 5000
136    x_iqr 0.501516
137    y_iqr 0.511866
138
139Computing weighted means
140----------------------------------------------------------------
141
142This might be more elegantly implemented as an option within the ``stats1`` verb. Meanwhile, it's expressible within the DSL:
143
144::
145
146    $ mlr --from data/medium put -q '
147      # Using the y field for weighting in this example
148      weight = $y;
149
150      # Using the a field for weighted aggregation in this example
151      @sumwx[$a] += weight * $i;
152      @sumw[$a] += weight;
153
154      @sumx[$a] += $i;
155      @sumn[$a] += 1;
156
157      end {
158        map wmean = {};
159        map mean  = {};
160        for (a in @sumwx) {
161          wmean[a] = @sumwx[a] / @sumw[a]
162        }
163        for (a in @sumx) {
164          mean[a] = @sumx[a] / @sumn[a]
165        }
166        #emit wmean, "a";
167        #emit mean, "a";
168        emit (wmean, mean), "a";
169      }'
170    a=pan,wmean=4979.563722,mean=5028.259010
171    a=eks,wmean=4890.381593,mean=4956.290076
172    a=wye,wmean=4946.987746,mean=4920.001017
173    a=zee,wmean=5164.719685,mean=5123.092330
174    a=hat,wmean=4925.533162,mean=4967.743946
175
176Generating random numbers from various distributions
177----------------------------------------------------------------
178
179Here we can chain together a few simple building blocks:
180
181::
182
183    $ cat expo-sample.sh
184    # Generate 100,000 pairs of independent and identically distributed
185    # exponentially distributed random variables with the same rate parameter
186    # (namely, 2.5). Then compute histograms of one of them, along with
187    # histograms for their sum and their product.
188    #
189    # See also https://en.wikipedia.org/wiki/Exponential_distribution
190    #
191    # Here I'm using a specified random-number seed so this example always
192    # produces the same output for this web document: in everyday practice we
193    # wouldn't do that.
194
195    mlr -n \
196      --seed 0.25 \
197      --opprint \
198      seqgen --stop 100000 \
199      then put '
200        # https://en.wikipedia.org/wiki/Inverse_transform_sampling
201        func expo_sample(lambda) {
202          return -log(1-urand())/lambda
203        }
204        $u = expo_sample(2.5);
205        $v = expo_sample(2.5);
206        $s = $u + $v;
207        $p = $u * $v;
208      ' \
209      then histogram -f u,s,p --lo 0 --hi 2 --nbins 50 \
210      then bar -f u_count,s_count,p_count --auto -w 20
211
212Namely:
213
214* Set the Miller random-number seed so this webdoc looks the same every time I regenerate it.
215* Use pretty-printed tabular output.
216* Use pretty-printed tabular output.
217* Use ``seqgen`` to produce 100,000 records ``i=0``, ``i=1``, etc.
218* Send those to a ``put`` step which defines an inverse-transform-sampling function and calls it twice, then computes the sum and product of samples.
219* Send those to a histogram, and from there to a bar-plotter. This is just for visualization; you could just as well output CSV and send that off to your own plotting tool, etc.
220
221The output is as follows:
222
223::
224
225    $ sh expo-sample.sh
226    bin_lo   bin_hi   u_count                        s_count                         p_count
227    0.000000 0.040000 [78]*******************#[9497] [353]#...................[3732] [20]*******************#[39755]
228    0.040000 0.080000 [78]******************..[9497] [353]*****...............[3732] [20]*******.............[39755]
229    0.080000 0.120000 [78]****************....[9497] [353]*********...........[3732] [20]****................[39755]
230    0.120000 0.160000 [78]**************......[9497] [353]************........[3732] [20]***.................[39755]
231    0.160000 0.200000 [78]*************.......[9497] [353]**************......[3732] [20]**..................[39755]
232    0.200000 0.240000 [78]************........[9497] [353]****************....[3732] [20]*...................[39755]
233    0.240000 0.280000 [78]**********..........[9497] [353]******************..[3732] [20]*...................[39755]
234    0.280000 0.320000 [78]**********..........[9497] [353]******************..[3732] [20]*...................[39755]
235    0.320000 0.360000 [78]*********...........[9497] [353]*******************.[3732] [20]#...................[39755]
236    0.360000 0.400000 [78]********............[9497] [353]*******************.[3732] [20]#...................[39755]
237    0.400000 0.440000 [78]*******.............[9497] [353]*******************#[3732] [20]#...................[39755]
238    0.440000 0.480000 [78]******..............[9497] [353]******************..[3732] [20]#...................[39755]
239    0.480000 0.520000 [78]*****...............[9497] [353]******************..[3732] [20]#...................[39755]
240    0.520000 0.560000 [78]*****...............[9497] [353]******************..[3732] [20]#...................[39755]
241    0.560000 0.600000 [78]****................[9497] [353]*****************...[3732] [20]#...................[39755]
242    0.600000 0.640000 [78]****................[9497] [353]*****************...[3732] [20]#...................[39755]
243    0.640000 0.680000 [78]****................[9497] [353]****************....[3732] [20]#...................[39755]
244    0.680000 0.720000 [78]***.................[9497] [353]****************....[3732] [20]#...................[39755]
245    0.720000 0.760000 [78]***.................[9497] [353]**************......[3732] [20]#...................[39755]
246    0.760000 0.800000 [78]**..................[9497] [353]**************......[3732] [20]#...................[39755]
247    0.800000 0.840000 [78]**..................[9497] [353]*************.......[3732] [20]#...................[39755]
248    0.840000 0.880000 [78]**..................[9497] [353]************........[3732] [20]#...................[39755]
249    0.880000 0.920000 [78]**..................[9497] [353]***********.........[3732] [20]#...................[39755]
250    0.920000 0.960000 [78]*...................[9497] [353]***********.........[3732] [20]#...................[39755]
251    0.960000 1.000000 [78]*...................[9497] [353]**********..........[3732] [20]#...................[39755]
252    1.000000 1.040000 [78]*...................[9497] [353]*********...........[3732] [20]#...................[39755]
253    1.040000 1.080000 [78]*...................[9497] [353]*********...........[3732] [20]#...................[39755]
254    1.080000 1.120000 [78]*...................[9497] [353]********............[3732] [20]#...................[39755]
255    1.120000 1.160000 [78]*...................[9497] [353]********............[3732] [20]#...................[39755]
256    1.160000 1.200000 [78]#...................[9497] [353]*******.............[3732] [20]#...................[39755]
257    1.200000 1.240000 [78]#...................[9497] [353]******..............[3732] [20]#...................[39755]
258    1.240000 1.280000 [78]#...................[9497] [353]*****...............[3732] [20]#...................[39755]
259    1.280000 1.320000 [78]#...................[9497] [353]*****...............[3732] [20]#...................[39755]
260    1.320000 1.360000 [78]#...................[9497] [353]*****...............[3732] [20]#...................[39755]
261    1.360000 1.400000 [78]#...................[9497] [353]****................[3732] [20]#...................[39755]
262    1.400000 1.440000 [78]#...................[9497] [353]****................[3732] [20]#...................[39755]
263    1.440000 1.480000 [78]#...................[9497] [353]***.................[3732] [20]#...................[39755]
264    1.480000 1.520000 [78]#...................[9497] [353]***.................[3732] [20]#...................[39755]
265    1.520000 1.560000 [78]#...................[9497] [353]***.................[3732] [20]#...................[39755]
266    1.560000 1.600000 [78]#...................[9497] [353]**..................[3732] [20]#...................[39755]
267    1.600000 1.640000 [78]#...................[9497] [353]**..................[3732] [20]#...................[39755]
268    1.640000 1.680000 [78]#...................[9497] [353]*...................[3732] [20]#...................[39755]
269    1.680000 1.720000 [78]#...................[9497] [353]*...................[3732] [20]#...................[39755]
270    1.720000 1.760000 [78]#...................[9497] [353]*...................[3732] [20]#...................[39755]
271    1.760000 1.800000 [78]#...................[9497] [353]*...................[3732] [20]#...................[39755]
272    1.800000 1.840000 [78]#...................[9497] [353]#...................[3732] [20]#...................[39755]
273    1.840000 1.880000 [78]#...................[9497] [353]#...................[3732] [20]#...................[39755]
274    1.880000 1.920000 [78]#...................[9497] [353]#...................[3732] [20]#...................[39755]
275    1.920000 1.960000 [78]#...................[9497] [353]#...................[3732] [20]#...................[39755]
276    1.960000 2.000000 [78]#...................[9497] [353]#...................[3732] [20]#...................[39755]
277
278Sieve of Eratosthenes
279----------------------------------------------------------------
280
281The `Sieve of Eratosthenes <http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes>`_ is a standard introductory programming topic. The idea is to find all primes up to some *N* by making a list of the numbers 1 to *N*, then striking out all multiples of 2 except 2 itself, all multiples of 3 except 3 itself, all multiples of 4 except 4 itself, and so on. Whatever survives that without getting marked is a prime. This is easy enough in Miller. Notice that here all the work is in ``begin`` and ``end`` statements; there is no file input (so we use ``mlr -n`` to keep Miller from waiting for input data).
282
283::
284
285    $ cat programs/sieve.mlr
286    # ================================================================
287    # Sieve of Eratosthenes: simple example of Miller DSL as programming language.
288    # ================================================================
289
290    # Put this in a begin-block so we can do either
291    #   mlr -n put -q -f name-of-this-file.mlr
292    # or
293    #   mlr -n put -q -f name-of-this-file.mlr -e '@n = 200'
294    # i.e. 100 is the default upper limit, and another can be specified using -e.
295    begin {
296      @n = 100;
297    }
298
299    end {
300      for (int i = 0; i <= @n; i += 1) {
301        @s[i] = true;
302      }
303      @s[0] = false; # 0 is neither prime nor composite
304      @s[1] = false; # 1 is neither prime nor composite
305      # Strike out multiples
306      for (int i = 2; i <= @n; i += 1) {
307        for (int j = i+i; j <= @n; j += i) {
308          @s[j] = false;
309        }
310      }
311      # Print survivors
312      for (int i = 0; i <= @n; i += 1) {
313        if (@s[i]) {
314          print i;
315        }
316      }
317    }
318
319::
320
321    $ mlr -n put -f programs/sieve.mlr
322    2
323    3
324    5
325    7
326    11
327    13
328    17
329    19
330    23
331    29
332    31
333    37
334    41
335    43
336    47
337    53
338    59
339    61
340    67
341    71
342    73
343    79
344    83
345    89
346    97
347
348Mandelbrot-set generator
349----------------------------------------------------------------
350
351The `Mandelbrot set <http://en.wikipedia.org/wiki/Mandelbrot_set>`_ is also easily expressed. This isn't an important case of data-processing in the vein for which Miller was designed, but it is an example of Miller as a general-purpose programming language -- a test case for the expressiveness of the language.
352
353The (approximate) computation of points in the complex plane which are and aren't members is just a few lines of complex arithmetic (see the Wikipedia article); how to render them is another task.  Using graphics libraries you can create PNG or JPEG files, but another fun way to do this is by printing various characters to the screen:
354
355::
356
357    $ cat programs/mand.mlr
358    # Mandelbrot set generator: simple example of Miller DSL as programming language.
359    begin {
360      # Set defaults
361      @rcorn     = -2.0;
362      @icorn     = -2.0;
363      @side      = 4.0;
364      @iheight   = 50;
365      @iwidth    = 100;
366      @maxits    = 100;
367      @levelstep = 5;
368      @chars     = "@X*o-."; # Palette of characters to print to the screen.
369      @verbose   = false;
370      @do_julia  = false;
371      @jr        = 0.0;      # Real part of Julia point, if any
372      @ji        = 0.0;      # Imaginary part of Julia point, if any
373    }
374
375    # Here, we can override defaults from an input file (if any).  In Miller's
376    # put/filter DSL, absent-null right-hand sides result in no assignment so we
377    # can simply put @rcorn = $rcorn: if there is a field in the input like
378    # 'rcorn = -1.847' we'll read and use it, else we'll keep the default.
379    @rcorn     = $rcorn;
380    @icorn     = $icorn;
381    @side      = $side;
382    @iheight   = $iheight;
383    @iwidth    = $iwidth;
384    @maxits    = $maxits;
385    @levelstep = $levelstep;
386    @chars     = $chars;
387    @verbose   = $verbose;
388    @do_julia  = $do_julia;
389    @jr        = $jr;
390    @ji        = $ji;
391
392    end {
393      if (@verbose) {
394        print "RCORN     = ".@rcorn;
395        print "ICORN     = ".@icorn;
396        print "SIDE      = ".@side;
397        print "IHEIGHT   = ".@iheight;
398        print "IWIDTH    = ".@iwidth;
399        print "MAXITS    = ".@maxits;
400        print "LEVELSTEP = ".@levelstep;
401        print "CHARS     = ".@chars;
402      }
403
404      # Iterate over a matrix of rows and columns, printing one character for each cell.
405      for (int ii = @iheight-1; ii >= 0; ii -= 1) {
406        num pi = @icorn + (ii/@iheight) * @side;
407        for (int ir = 0; ir < @iwidth; ir += 1) {
408          num pr = @rcorn + (ir/@iwidth) * @side;
409          printn get_point_plot(pr, pi, @maxits, @do_julia, @jr, @ji);
410        }
411        print;
412      }
413    }
414
415    # This is a function to approximate membership in the Mandelbrot set (or Julia
416    # set for a given Julia point if do_julia == true) for a given point in the
417    # complex plane.
418    func get_point_plot(pr, pi, maxits, do_julia, jr, ji) {
419      num zr = 0.0;
420      num zi = 0.0;
421      num cr = 0.0;
422      num ci = 0.0;
423
424      if (!do_julia) {
425        zr = 0.0;
426        zi = 0.0;
427        cr = pr;
428        ci = pi;
429      } else {
430        zr = pr;
431        zi = pi;
432        cr = jr;
433        ci = ji;
434      }
435
436      int iti = 0;
437      bool escaped = false;
438      num zt = 0;
439      for (iti = 0; iti < maxits; iti += 1) {
440        num mag = zr*zr + zi+zi;
441        if (mag > 4.0) {
442            escaped = true;
443            break;
444        }
445        # z := z^2 + c
446        zt = zr*zr - zi*zi + cr;
447        zi = 2*zr*zi + ci;
448        zr = zt;
449      }
450      if (!escaped) {
451        return ".";
452      } else {
453        # The // operator is Miller's (pythonic) integer-division operator
454        int level = (iti // @levelstep) % strlen(@chars);
455        return substr(@chars, level, level);
456      }
457    }
458
459At standard resolution this makes a nice little ASCII plot:
460
461::
462
463    $ mlr -n put -f ./programs/mand.mlr
464    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
465    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
466    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
467    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
468    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
469    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
470    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
471    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
472    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
473    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
474    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@XXXXXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
475    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@XXXX.XXXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
476    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@XXXXXXXooXXXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
477    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@XXXXX**o..*XXXXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
478    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@XXXXXX*-....-oXXXXXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
479    @@@@@@@@@@@@@@@@@@@@@@@@@@@XXXXX@XXXXXXXXXX*......o*XXXXXXXXXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
480    @@@@@@@@@@@@@@@@@@@@@@@@@XXXXXXXXXX**oo*-.-........oo.XXXXXXXXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
481    @@@@@@@@@@@@@@@@@@@@@@@XXXXXXXXXXXXX....................X..o-XXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
482    @@@@@@@@@@@@@@@@@@XXXXXXXXXXXXXXX*oo......................oXXXXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
483    @@@@@@@@@@@@@@@@XXX*XXXXXXXXXXXX**o........................*X*X@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
484    @@@@@@@@@@@@@XXXXXXooo***o*.*XX**X..........................o-XX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
485    @@@@@@@@@@@XXXXXXXX*-.......-***.............................oXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
486    @@@@@@@@@@XXXXXXXX*@..........Xo............................*XX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
487    @@XXXX@XXXXXXXX*o@oX...........@...........................oXXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
488    .........................................................o*XXXXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
489    @@@@@@XXXXXXXXX*-.oX...........@...........................oXXXXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
490    @@@@@@@XXXXXXXXXX**@..........*o............................*XXXXXXXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
491    @@@@@@@XXXXXXXXXXXXX-........***.............................oXXXXXXXXXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@
492    @@@@@@@XXXXXXXXXXXXoo****o*.XX***@..........................o-XXXXXXXXXXXXX@@@@@@@@@@@@@@@@@@@@@@@@@
493    @@@@@@@@@@@@@@XXXXX*XXXX*XXXXXXX**-........................***XXXXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
494    @@@@@@@@@@@@@@@@@@@@XXXXXXXXXXXXX*o*.....................@o*XXXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
495    @@@@@@@@@@@@@@@@@@@@@@@XXXXXXXXXXXX*....................*..o-XX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
496    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@XXXXX*ooo*-.o........oo.X*XXXXXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
497    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@XXXXXXXXX**@.....*XXXXXXXXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
498    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@XXXXXXXXX*o....-o*XXXXXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
499    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@XXXXXXXXXXo*o..*XXXXXXXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
500    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@XXXXXXXXXXXXX*o*XXXXXXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
501    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@XXXXXXXXXXXX@XXXXXXXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
502    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@XXXXXXXXX@@XXXXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
503    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@XXXXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
504    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
505    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
506    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
507    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
508    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
509    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
510    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
511    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
512    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
513    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
514
515But using a very small font size (as small as my Mac will let me go), and by choosing the coordinates to zoom in on a particular part of the complex plane, we can get a nice little picture:
516
517::
518
519    #!/bin/bash
520    # Get the number of rows and columns from the terminal window dimensions
521    iheight=$(stty size | mlr --nidx --fs space cut -f 1)
522    iwidth=$(stty size | mlr --nidx --fs space cut -f 2)
523    echo "rcorn=-1.755350,icorn=+0.014230,side=0.000020,maxits=10000,iheight=$iheight,iwidth=$iwidth" \
524    | mlr put -f programs/mand.mlr
525
526.. image:: pix/mand.png
527