<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="init(false)"
    >
    <!--
    Superfractals
    Michael Fielding Barnsley
    Professor of Mathematics, Australian National University, Canberra
    
    From Introduction
    page 1
     -->
    
    <mx:Script>
        <![CDATA[
            import flash.display.BitmapData;
            
            private static var planeWidth:int = 300;
            private static var planeHeight:int = 300;
            
            private static var xy:BitmapData;
            
            private static var pointsABC:Array = new Array();
            private static var lastX:int;
            private static var lastY:int;
            
            private static var opRuns:int = 0;
            private static var unOpRuns:int = 0;
            
            private static var opTimeSum:Number = 0;
            private static var unOpTimeSum:Number = 0;
            
            private function init(isRND:Boolean):void {
                xy = new BitmapData(planeWidth, planeHeight, false, 0xFFFFFFFF);
                pointsABC = new Array();
                            
                if( isRND ) {
                    for( var i:int = 0; i < 3; ++i ) {
                        pointsABC.push( rndDot(planeWidth, planeHeight) );
                        graphPoint( pointsABC[i][0], pointsABC[i][1], 0xFF0000 );
                    }
                } else {
                    pointsABC.push( [planeWidth / 2, 0] );
                    pointsABC.push( [0, planeWidth] );
                    pointsABC.push( [planeWidth, planeHeight] );
        
                    for( var j:int = 0; j < 3; ++j ) {
                        graphPoint( pointsABC[j][0], pointsABC[j][1], 0xFF0000 );
                    }
                }
                
                lastX = rndDot(800, 800)[0];
                lastY = rndDot(800, 800)[1];
                
                xyPlane.source = new Bitmap( xy );
                
                var startTime:Date = new Date();
                
                var numIterations:int = int( iterations.value );
                if( serpinski.selected ) {
                    if( optimized.selected ) {
                        opRuns ++;
                        makeSierpinskiOptimized( numIterations );
                    } else {
                        unOpRuns ++;
                        makeSierpinski( numIterations );
                    }
                } else {
                    if( optimized.selected ) {
                        opRuns ++;
                        makeKoshOptimized( numIterations );
                    } else {
                        unOpRuns ++;
                        makeKosh( numIterations );
                    }
                    
                }
                
                var endTime:Date = new Date();
                
                var computeTime:int = endTime.getTime() - startTime.getTime();
                
                runTimeDisplay.text = "compute time: " + String( computeTime / 1000 ) + " seconds";
                
                if( optimized.selected ) {
                    opTimeSum += computeTime
                    avgOp.text = String( opTimeSum / opRuns );
                } else {
                    unOpTimeSum += computeTime;
                    avgUnOp.text = String( unOpTimeSum / unOpRuns );
                }
                
                if( unOpRuns > 0 && opRuns > 0 ) {
                    perc.text = String( Number( avgOp.text ) / Number( avgUnOp.text ) );
                }
            }
            
            private static function makeSierpinski(iterate:int):void {
                for( var i:int = 0; i < iterate; ++i ) {
                    var rndNum:int = Math.floor( Math.random() * 3 );
                    
                    var abcX:int = pointsABC[rndNum][0];
                    var abcY:int = pointsABC[rndNum][1];
                    
                    var midX:int = ( lastX + abcX ) / 2;
                    var midY:int = ( lastY + abcY ) / 2;
                    
                    graphPoint(midX, midY, 0x000000);
                    
                    lastX = midX;
                    lastY = midY;
                    
                }
            }

            private static function makeSierpinskiOptimized(iterate:int):void {
                for( var i:int = 0; i < iterate; ++i ) {
                    var rndNum:int = int( Math.random() * 3 );
                    
                    var abcX:int = pointsABC[rndNum][0];
                    var abcY:int = pointsABC[rndNum][1];
                    
                    var midX:int = ( lastX + abcX ) >> 1;
                    var midY:int = ( lastY + abcY ) >> 1;
                    
                    graphPoint(midX, midY, 0x000000);
                    
                    lastX = midX;
                    lastY = midY;
                }
            }
            
            private static function makeKosh(iterate:int):void {
                
                for( var i:int = 0; i < iterate; ++i ) {
                    var rndNum:int = Math.floor( Math.random() * 3 );
                    
                    var abcX:int = pointsABC[rndNum][0];
                    var abcY:int = pointsABC[rndNum][1];
                    
                    var midX:int = ( abcX - lastX ) / 2;
                    var midY:int = ( abcY - lastY ) / 2;
                    
                    graphPoint(midX + ( planeWidth / 3 ), midY + ( planeHeight / 3 ), 0x000000);
                    
                    lastX = midX;
                    lastY = midY;    
                }
            }

            private static function makeKoshOptimized(iterate:int):void {
                var offsetW:int = int( planeWidth / 3 );
                var offsetH:int = int( planeHeight / 3 );
                for( var i:int = 0; i < iterate; ++i ) {
                    var rndNum:int = int( Math.random() * 3 );
                    
                    var abcX:int = pointsABC[rndNum][0];
                    var abcY:int = pointsABC[rndNum][1];
                    
                    var midX:int = ( abcX - lastX ) >> 1;
                    var midY:int = ( abcY - lastY ) >> 1;
                    
                    graphPoint(midX + offsetW, midY + offsetH, 0x000000);
                    
                    lastX = midX;
                    lastY = midY;    
                }
            }
            
            
            
            private static function rndDot(xMax:int, yMax:int):Array {
                var xPoint:int = Math.floor( Math.random() * xMax );
                var yPoint:int = Math.floor( Math.random() * yMax );
                
                var points:Array = new Array();
                points.push(xPoint, yPoint);
                return points;
            }
            
            private static function graphPoint(x:int, y:int, c:int):void {
                xy.setPixel(x, y, c);
            }
            
        ]]>
    </mx:Script>
    

    
    <mx:Image 
        id="xyPlane" 
        x="5" y="80" width="300" height="300"/>
        
    <mx:Button 
        label="random points" 
        x="2" y="2" 
        click="init(true)"/>
    <mx:Button 
        label="perfect triangle"
        x="118" y="2" 
        click="init(false)"/>    
    

    <mx:RadioButton 
        id="serpinski" 
        label="Serpinski" 
        x="264" y="3" 
        selected="true" 
        groupName="midPointType" />
    <mx:RadioButton 
        id="kosh" 
        label="Kosh" 
        x="364" y="3"
        groupName="midPointType" />    
        
    <mx:Label id="runTimeDisplay" 
        x="320" y="110" 
        text="compute time: " 
        width="200"/>
    
    <mx:CheckBox 
        id="optimized"
        x="320" y="80" 
        label="optimized" 
        selected="true"/>

    <mx:Label x="320" y="140" text="ave. optimized time ="/>
    <mx:Label x="320" y="160" text="avg. unoptimized time ="/>
    <mx:Label x="320" y="180" text="avg. optimized / unoptimized ="/>
    <mx:Label id="avgOp" x="505" y="140" width="60"/>
    <mx:Label id="avgUnOp" x="505" y="160" width="60"/>
    <mx:Label id="perc" x="505" y="180" width="82"/>
    <mx:NumericStepper 
        id="iterations" 
        x="120" y="40" 
        width="100" 
        stepSize="10000" 
        minimum="10000" maximum="20000000" 
        value="1000000"/>
    <mx:Label x="4" y="40" text="Iterations to run:"/>


</mx:Application>