function CPU_vs_GPU(x,y)

% if the gui doesn't exist initialize it, if it does exist, get its handle
if isempty(findobj('type','figure','name','Calculate Mandlebrot Set'))
    hh = init_gui;
else
    hh = findobj('type','figure','name','Calculate Mandlebrot Set');
end
data = guidata(hh);

% get the current data from the figure
xlim = data.this_x;
ylim = data.this_y;
axes(data.mand_disp)

% main control loop
while data.start_stop.Value == true

    % get and set some values that can change on each iteration
    data.start_stop.String = 'Pause';
    gpu_flag = data.cpu_gpu.Value;

    % calculate the mandelbrot set at the current position
    [x, y, count, calctime] = mandelbrot(gpu_flag,xlim,ylim);

    % plot the mandelbrot set at the current position
    update_disp(x, y, count, calctime, gpu_flag, data)

    % zoom in to the next position
    xlim = ((xlim-mean(xlim))/1.1) + mean(xlim);
    ylim = ((ylim-mean(ylim))/1.1) + mean(ylim);

    % loop back to beginning if we are zoomed in too much
    if diff(xlim) <= 3e-13
        xlim = data.start_x;
        ylim = data.start_y;
    end
end

% if we are here the user paused execution
% stash the data in the figure and exit
data.this_x = xlim;
data.this_y = ylim;
guidata(hh,data);

data.start_stop.String = 'Start';


function [x, y, count, calctime] = mandelbrot(gpu_flag,xlim,ylim)
% Setup
maxIterations = 250;
gridSize = 400;
t = tic();

if gpu_flag
    x = gpuArray.linspace( xlim(1), xlim(2), gridSize );
    y = gpuArray.linspace( ylim(1), ylim(2), gridSize );
    [xGrid,yGrid] = meshgrid( x, y );
    z0 = complex( xGrid, yGrid );
    count = ones( size(z0), 'gpuArray' );
else
    x = linspace( xlim(1), xlim(2), gridSize );
    y = linspace( ylim(1), ylim(2), gridSize );
    [xGrid,yGrid] = meshgrid( x, y );
    z0 = complex( xGrid, yGrid );
    count = ones(size(z0));
end

% Calculate
z = z0;
for n = 0:maxIterations
    z = z.*z + z0;
    inside = abs( z )<=2;
    count = count + inside;
end
count = log(count);

count = gather(count); % Fetch the data back from the GPU
calctime = toc(t);


function hh = init_gui
% initialize the GUI display, controls, and data

% figure
hh = figure('name','Calculate Mandlebrot Set',...
    'units','pixels',...
    'position',[50 50 650 550],...
    'menubar','none',...
    'color', 'w');

% display
mand_disp = axes('units', 'pixels',...
    'position',[120 20 510 510],...
    'color', 'k');

colormap('gray')
image(0)
axis off

% controls
start_stop = uicontrol('style','togglebutton',...
    'string','Start',...
    'position', [20 510 80 20], ...
    'callback', @CPU_vs_GPU);

cpu_gpu = uicontrol('style','togglebutton',...
    'string', 'Use GPU',...
    'position', [20 470 80 20]);

cmap_select = uicontrol('style', 'popupmenu',...
    'String', {'gray',...
    'jet',...
    'hsv',...
    'hot',...
    'cool',...
    'parula',...
    'spring',...
    'summer',...
    'autumn',...
    'winter',...
    'bone',...
    'copper',...
    'pink',...
    'lines',...
    'colorcube',...
    'prism',...
    'flag'},...
    'position', [20 430 80 20]);

% stash all the handles and data in the gui so that it will be there next
% time we call this function
data.mand_disp   = mand_disp;
data.start_stop  = start_stop;
data.cpu_gpu     = cpu_gpu;
data.cmap_select = cmap_select;
data.start_x     = [-2.128560877517527   0.631027455859301];
data.start_y     = [-1.256153093513134   1.503434789488989];
data.this_x      = data.start_x;
data.this_y      = data.start_y;

guidata(hh,data);


function update_disp(x, y, count, calctime, gpu_flag, data)

if gpu_flag
    hardware = 'GPU' ;
    data.cpu_gpu.String = 'Use CPU';
else
    hardware = 'CPU';
    data.cpu_gpu.String = 'Use GPU';
end

cmap = data.cmap_select.String{data.cmap_select.Value};

imagesc( x, y, count )
axis off
title(sprintf( '%1.3fsecs to calculate using %s', ...
    calctime, ...
    hardware))
colormap(cmap)
drawnow