# a9b9c27cbe38d5fe00ba811d8d9c5d39364ea9c2c63100830336a362ecc26733 Elan 2.0.0-alpha4 valid

# https://en.wikipedia.org/wiki/Barnsley_fern
# Draws a fractal image
main
  variable vg set to new List<of VectorGraphic>()
  # coordinates
  variable x set to 0.0
  variable y set to 0.16
  # random number
  variable r set to new Random()
  call r.initialiseFromClock()
  variable rect set to new RectangleVG()
  variable count set to 0
  # don't set the count limit too high
  # or the browser will run out of memory
  while count < 1000
    variable point set to onepoint(x, y, r)
    set rect to point.item_0
    set x to point.item_1
    set y to point.item_2
    set r to point.item_3
    call vg.append(rect)
    call displayVectorGraphics(vg)
    set count to count + 1
  end while
  call print("Finished")
end main

function onepoint(x as Float, y as Float, r as Random) returns (RectangleVG, Float, Float, Random)
  # next pseudo-random number (Grogono parameters)
  variable rValue set to r.asFloat()
  variable rNext set to r.nextGen()
  variable nx_ny set to onestep(x, y, rValue)
  variable nx set to nx_ny.item_0
  variable ny set to nx_ny.item_1
  variable rect set to (new RectangleVG()).withX(nx*scale + 50).withY(75 - ny*scale).withWidth(0.5).withHeight(0.5).withFillColour(0x408040).withFillColour(0x408040).withStrokeWidth(0.25)
  return (rect, nx, ny, rNext)
end function

# r is random Float 0.0 <= r < 1.0
# returns new x and y
# written in a data-driven style
function onestep(x as Float, y as Float, r as Float) returns (Float, Float)
  # Currently you can't make the params into one long line
  # as it makes the browser use 100% CPU
  # xx xy yx yy cx cy probablity
  variable p1 set to [0.0, 0.0, 0.0, 0.16, 0.0, 0.0, 0.01]
  variable p2 set to [0.85, 0.04, -0.04, 0.85, 0.0, 1.60, 0.85]
  variable p3 set to [0.20, -0.26, 0.23, 0.22, 0.0, 1.60, 0.07]
  variable p4 set to [-0.15, 0.28, 0.26, 0.24, 0.0, 0.44, 0.07]
  variable allPs set to [p1, p2, p3, p4]
  # cumulative probabilty
  variable cumuprob set to 0.0
  variable done set to false
  variable nx set to 0.0
  variable ny set to 0.0
  for pp in allPs
    set cumuprob to cumuprob + pp[6]
    if (not done) and (r < cumuprob) then
      set nx to x*pp[0] + y*pp[1] + pp[4]
      set ny to x*pp[2] + y*pp[3] + pp[5]
      set done to true
    end if
  end for
  return (nx, ny)
end function

test test_one
  assert roundtuple2(onestep(0.0, 0.16, 0.5)) is [0.0064, 1.736]
  assert roundtuple2(onestep(0.0, 0.16, 0.9)) is [-0.0416, 1.6352]
end test

# two approaches to rounding a tuple to N decimal places
function roundtuple1(n as (Float, Float)) returns (Float, Float)
  variable a set to n.item_0
  variable b set to n.item_1
  return (a.round(8), b.round(8))
end function

function roundtuple2(n as (Float, Float)) returns List<of Float>
  variable a set to n.item_0
  variable b set to n.item_1
  return [a, b].map(lambda x as Float => x.round(8))
end function

constant scale set to 7
