# Mandelbrot set generator: simple example of Miller DSL as programming language.
begin {
  # Set defaults
  @rcorn     = -2.0;
  @icorn     = -2.0;
  @side      = 4.0;
  @iheight   = 50;
  @iwidth    = 100;
  @maxits    = 100;
  @levelstep = 5;
  @chars     = "@X*o-."; # Palette of characters to print to the screen.
  @verbose   = false;
  @do_julia  = false;
  @jr        = 0.0;      # Real part of Julia point, if any
  @ji        = 0.0;      # Imaginary part of Julia point, if any
}

# Here, we can override defaults from an input file (if any).  In Miller's
# put/filter DSL, absent-null right-hand sides result in no assignment so we
# can simply put @rcorn = $rcorn: if there is a field in the input like
# 'rcorn = -1.847' we'll read and use it, else we'll keep the default.
@rcorn     = $rcorn;
@icorn     = $icorn;
@side      = $side;
@iheight   = $iheight;
@iwidth    = $iwidth;
@maxits    = $maxits;
@levelstep = $levelstep;
@chars     = $chars;
@verbose   = $verbose;
@do_julia  = $do_julia;
@jr        = $jr;
@ji        = $ji;

end {
  if (@verbose) {
    print "RCORN     = ".@rcorn;
    print "ICORN     = ".@icorn;
    print "SIDE      = ".@side;
    print "IHEIGHT   = ".@iheight;
    print "IWIDTH    = ".@iwidth;
    print "MAXITS    = ".@maxits;
    print "LEVELSTEP = ".@levelstep;
    print "CHARS     = ".@chars;
  }

  # Iterate over a matrix of rows and columns, printing one character for each cell.
  for (int ii = @iheight-1; ii >= 0; ii -= 1) {
    num pi = @icorn + (ii/@iheight) * @side;
    for (int ir = 0; ir < @iwidth; ir += 1) {
      num pr = @rcorn + (ir/@iwidth) * @side;
      printn get_point_plot(pr, pi, @maxits, @do_julia, @jr, @ji);
    }
    print;
  }
}

# This is a function to approximate membership in the Mandelbrot set (or Julia
# set for a given Julia point if do_julia == true) for a given point in the
# complex plane.
func get_point_plot(pr, pi, maxits, do_julia, jr, ji) {
  num zr = 0.0;
  num zi = 0.0;
  num cr = 0.0;
  num ci = 0.0;

  if (!do_julia) {
    zr = 0.0;
    zi = 0.0;
    cr = pr;
    ci = pi;
  } else {
    zr = pr;
    zi = pi;
    cr = jr;
    ci = ji;
  }

  int iti = 0;
  bool escaped = false;
  num zt = 0;
  for (iti = 0; iti < maxits; iti += 1) {
    num mag = zr*zr + zi+zi;
    if (mag > 4.0) {
        escaped = true;
        break;
    }
    # z := z^2 + c
    zt = zr*zr - zi*zi + cr;
    zi = 2*zr*zi + ci;
    zr = zt;
  }
  if (!escaped) {
    return ".";
  } else {
    # The // operator is Miller's (pythonic) integer-division operator
    int level = (iti // @levelstep) % strlen(@chars);
    return substr(@chars, level, level);
  }
}
