#define ANIMATED 

//----------begin------complex lib------------------
#define complex vec2

float cSqrt(complex val)
{
    return sqrt(val.x * val.x + val.y * val.y);
}

float cabs(complex cval)
{
    return sqrt(cval.x * cval.x + cval.y * cval.y);
}


complex cFromPolar(float angle, float len)
{
    return complex(len * sin(angle), len * cos(angle));
}

complex cPow(complex cValue, complex cPower)
{
    float ln_x1y1 = log( cabs(cValue) );
    float a = atan( cValue.y, cValue.x );
    
    float e1 = exp( cPower.x *  ln_x1y1 - cPower.y * ln_x1y1);
    float a_y2ln_x1y1 = cPower.x * a + cPower.y * ln_x1y1;
    return cFromPolar( a_y2ln_x1y1, e1 );
}
//----------end------complex lib------------------

//----------begin------palette lib------------------

#define color vec4

const int palette_red_width   = 23; 
const int palette_green_width = 13; 
const int palette_blue_width  = 9; 

const int palette_red_shift   = 7; 
const int palette_green_shift = 1; 
const int palette_blue_shift  = 3; 
const int palette_max_colors  = 65536;

ANIMATED int palette_shift = 0;

color colorFromPalettenInxed( int index )
{
    int redVal = (index + palette_red_shift) % palette_red_width;
    int greenVal = (index + palette_green_shift) % palette_green_width;
    int blueVal = (index + palette_blue_shift) % palette_blue_width;

    float rVal = abs (float(redVal) / (float(palette_red_width) * 0.5) - 1.0 ) ;
    float gVal = abs (float(greenVal) / (float(palette_green_width) * 0.5) - 1.0 ) ;
    float bVal = abs (float(blueVal) / (float(palette_blue_width) * 0.5) - 1.0 ) ;
    
    return color(rVal, gVal, bVal, 1.0f);
}
//----------end------palette lib------------------

//----------begin------fractal lib------------------

const complex fractal_constant  = complex(1.0f, 0.0f);

const complex fractal_start_power = complex(1.2f, 0.0f);
const complex fractal_end_power = complex(8.0f, 0.0f);

const float   fractal_start_scale = 2.5f;
const float   fractal_end_scale = 1.5f;

const int     fractal_max_iterations = 20;
const float   fractal_max_value_range = 50.0f;

ANIMATED complex fractal_power = fractal_start_power;
ANIMATED float   fractal_scale = fractal_start_scale;

complex complexForCoords(vec2 coords) 
{
    complex z = coords * fractal_scale;
    
    bool needContinue = true;
    for (int i = 0; (i < fractal_max_iterations) && needContinue; i++)
    {
        z = cPow (z, fractal_power);
        z = z + fractal_constant;
        float z_abs = cabs(z);
        
        needContinue = ( abs(z.x) < fractal_max_value_range ) 
                        || (abs(z.y) < fractal_max_value_range)
                        || (z_abs < fractal_max_value_range) ;
    }
    
    return z;
}

color colorForComplex(complex cVal)
{
    float cVal_abs  = cabs(cVal);
    float indexValue = log(cVal_abs) * 1.7f;
    int index = int(indexValue) % 65536;
    
    return colorFromPalettenInxed(index + palette_shift);
}
//----------end------fractal lib------------------

//----------begin------screen lib------------------

vec2 getScreenNDC(vec2 fragCoord)
{
    vec2 screenUV = fragCoord / iResolution.xy;
    float aspectRatio = float( iResolution.x ) / float( iResolution.y );
    vec2 ndc = screenUV * 2.0 - vec2(1.0);
    ndc.x *= aspectRatio;
    
    return ndc;
}
//----------end------screen lib------------------

//----------begin------animation lib------------------

const float animation_palette_time_scale = 10.0;
const float animation_fractal_time_scale = 0.3;

float timeCoeff(float timeScale)
{
    return sin(iTime * timeScale) * 0.5 + 0.5;
}

void animation_update()
{
    palette_shift = int(iTime * animation_palette_time_scale) % palette_max_colors;
    
    fractal_scale = mix(fractal_start_scale, fractal_end_scale, timeCoeff(animation_fractal_time_scale) );
    fractal_power = mix(fractal_start_power, fractal_end_power, timeCoeff(animation_fractal_time_scale) );
}

//----------end------animation lib------------------

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    animation_update();
    vec2 screenNDC = getScreenNDC( fragCoord );
    complex fractalValue = complexForCoords( screenNDC );
    fragColor = colorForComplex( fractalValue );

}
