// Renders an ascii art mandelbrot.
// This uses integer based fixed point because
// float support is not yet implemented.

using s32 = sint(32)
using u32 = uint(32)
using u8 = uint(8)

s32 IntToFixedWidthDec( s32 i )
    // Restrict the input range to avoid overflows
    requires [
        i < ( 1 << 12 )
        i > ( -1 << 12 )
    ]
{
    return i << 12
}

using width = 100
using height = 32

using minU = IntToFixedWidthDec( -2 )
using maxU = IntToFixedWidthDec( 1 )

using minV = IntToFixedWidthDec( -1 )
using maxV = IntToFixedWidthDec( 1 )

using maxIterations = 50
using threshold = 4 << 24

using fflush = ExternalFunction( u32 ( pointer(void) stream ), "fflush" )

using putchar = ExternalFunction( u32 ( u8 c ), "putchar" )
void put( $c )
{
    putchar( cast( ct_int, $c ) )
}

void renderPixel( s32 x, s32 y )
    requires [
        width > 0
        height > 0
        x < width
        y < height
    ]
{
    // Using tuples to declare and initialize both variables
    // at once isn't really necessary here but it serves as
    // a drive-by test for that feature.
    var cr, var ci = minU + x * ( maxU - minU ) / width,
                     minV + y * ( maxV - minV ) / height

    s32 zr = 0 s32 zi = 0

    u32 i = 0
    while i < maxIterations
    {
        // By using a tuple assignment, we compute
        // the new values of both zr and zi first, and only after
        // we update the variables. With individual assignemnts,
        // we'd need to create temporary variables to compute
        // the new values of zr and zi and then copy
        // the results back in zr and zi.
        zr, zi = ( zr*zr - zi*zi ) >> 12,
                 2 * zr * zi >> 12

        zr, zi += cr, ci

        if ( zr*zr + zi*zi ) > threshold
        {
            if ( i & 1 ) == 0
                put( ' ' )
            else
                put( '.' )
            return
        }

        i += 1
    }

    put( '#' )
}

s32 y = 0
while y < height
{
    s32 x = 0
    while x < width
    {
        renderPixel( x, y )
        x += 1
    }

    put( '\n' )
    y += 1
}

fflush( nullptr )
