The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

double m_limit = 5;
double m_epsilon = 0.001;
unsigned int m_max_iter = 600;
unsigned int m_w = 640;
unsigned int m_h = 480;
double m_x1 = -2.2;
double m_x2 = +1;
double m_y1 = -1.1;
double m_y2 = +1.1;

/* C version (since using point() in XS code fails make test (but passes
   manual runs huh?) */

unsigned int _point(unsigned int x1, unsigned int y1)
  {
  double		za,zb,za1;
  double		za2,zb2;
  double		x,y;
  unsigned int		RETVAL;

  x = (x1 * (m_x2 - m_x1) / m_w) + m_x1;
  y = (y1 * (m_y2 - m_y1) / m_h) + m_y1;
  za = 0;
  zb = 0;
  za2 = 0;
  zb2 = 0;
  RETVAL = 0;
  while (RETVAL++ < m_max_iter)
    {
    za1 = za2 - zb2 + x;
    zb = 2 * za * zb + y;
    za = za1;
    za2 = za * za; zb2 = zb * zb; 
    if (za2 + zb2 > m_limit)
      {
      break;
      }
    }
  if (RETVAL >= m_max_iter)
    {
    RETVAL = 0;
    }
  return RETVAL;
  }


MODULE = Math::Fractal::Mandelbrot	PACKAGE = Math::Fractal::Mandelbrot
PROTOTYPES: ENABLE

##############################################################################
# point() - calculate fractal at this point
# input: X and Y   coordinates of the point

unsigned int
point(myclass, x1, y1)
	unsigned int x1
	unsigned int y1
  INIT:
    double		za,zb,za1;
    double		za2,zb2;
    double		x,y;

  CODE:
    x = (x1 * (m_x2 - m_x1) / m_w) + m_x1;
    y = (y1 * (m_y2 - m_y1) / m_h) + m_y1;
    za = 0;
    zb = 0;
    za2 = 0;
    zb2 = 0;
    RETVAL = 0;
    while (RETVAL++ < m_max_iter)
      {
      za1 = za2 - zb2 + x;
      zb = 2 * za * zb + y;
      za = za1;
      za2 = za * za; zb2 = zb * zb; 
      if (za2 + zb2 > m_limit)
        {
        break;
        }
      }
    if (RETVAL >= m_max_iter)
      {
      RETVAL = 0;
      }
  OUTPUT:
    RETVAL

##############################################################################
# hor_line() - calculate fractal at a horizontal stripe
# input: X and Y   coordinates of the start point
#        L 	   length of line

AV*
hor_line(myclass, x1, y1, l)
	unsigned int	x1
	unsigned int	y1
	unsigned int	l
  INIT:
    unsigned int	x, x2, i, last, iter, same;
    AV*			a2;

  CODE:
    a2 = (AV*)sv_2mortal((SV*)newAV());
    av_extend (a2, l + 1);
    x2 = x1 + l;
    i = 1; same = 0; last = _point (x1,y1);
    for (x = x1; x < x2; x++)
      {
      iter = _point (x,y1);
      av_push( a2, newSViv(iter) );
      if (iter == last) { same++; } else { break; }
      }
    for (;x < x2; x++)
      {
      iter = _point (x,y1);
      av_push( a2, newSViv(iter) );
      }
    av_push( a2, newSViv(same) );
    RETVAL = (AV*)a2;
  OUTPUT:
    RETVAL

##############################################################################
# ver_line() - calculate fractal at a vertical stripe
# input: X and Y   coordinates of the start point
#        L 	   length of line

AV*
ver_line(myclass, x1, y1, l)
	unsigned int	x1
	unsigned int	y1
	unsigned int	l

  INIT:
    unsigned int	y, y2, i, last, iter, same;
    AV*			a2;

  CODE:
    a2 = (AV*)sv_2mortal((SV*)newAV());
    av_extend (a2, l + 1);
    y2 = y1 + l;   
    i = 1; same = 0; last = _point (x1,y1);
    for (y = y1; y < y2; y++)
      {
      iter = _point (x1,y);
      av_push( a2, newSViv(iter) );
      if (iter == last) { same++; } else { break; }
      }
    for (;y < y2; y++)
      {
      iter = _point (x1,y);
      av_push( a2, newSViv(iter) );
      }
    av_push( a2, newSViv(same) );
    RETVAL = a2;
  OUTPUT:
    RETVAL


##############################################################################
# set_max_iter() - set maximum iterations
# input: new max_iter

unsigned int
set_max_iter(myclass, new_max_iter)
	unsigned int	new_max_iter
  CODE:
    m_max_iter = new_max_iter;
    if (new_max_iter == 0) { m_max_iter = 1; }		/* at least 1 */
    RETVAL = m_max_iter;
  OUTPUT:
    RETVAL


##############################################################################
# set_limit()
# input: new limit

double
set_limit(myclass, new_limit)
	double	new_limit
  CODE:
    m_limit = new_limit;
    RETVAL = m_limit;
  OUTPUT:
    RETVAL


##############################################################################
# set_epsilon()
# input: new limit

double
set_epsilon(myclass, new_e)
	double	new_e
  CODE:
    m_epsilon = new_e;
    RETVAL = m_epsilon;
  OUTPUT:
    RETVAL


##############################################################################
# set_bounds()
# input: x1,y1, x2,y2, w,h

void
set_bounds(myclass, nx1, ny1, nx2, ny2, nw, nh)
	double	nx1
	double	ny1
	double	nx2
	double	ny2
	unsigned int nw
	unsigned int nh
  CODE:
    m_x1 = nx1;
    m_y1 = ny1;
    m_x2 = nx2;
    m_y2 = ny2;
    m_w = nw;
    m_h = nh;