>
>   import SequenceUtilities(pam, reps, rightJustify)
>   import IOutilities(spaces) 
>   import NumericUtilities(digitize)


---------------  Project 4A: Ecology Equations -----------------


rabbit population update function (simplified Lotka-Volterra model)

>   nextRabbitPopulation :: RealFrac num => [num] -> [num] -> num
>   nextRabbitPopulation
>    [rabbitIncreaseWithoutWolves, coyoteDecreaseWithoutRabbits,
>     chanceThatCoyoteAEatsRabbitX, coyoteIncreaseByEatingRabbit]
>    [currentRabbitPopulation, currentCoyotePopulation]
>    =
>    (1 + rabbitIncreaseWithoutWolves)*currentRabbitPopulation -
>    (chanceThatCoyoteAEatsRabbitX*currentCoyotePopulation)*
>                                          currentRabbitPopulation


coyote population update function (simplified Lotka-Volterra model)

>   nextCoyotePopulation :: RealFrac num => [num] -> [num] -> num
>   nextCoyotePopulation   
>     [rabbitIncreaseWithoutWolves,  coyoteDecreaseWithoutRabbits, 
>      chanceThatCoyoteAEatsRabbitX, coyoteIncreaseByEatingRabbit]
>     [currentRabbitPopulation, currentCoyotePopulation]
>     =
>     (1 - coyoteDecreaseWithoutRabbits)*currentCoyotePopulation +
>     (chanceThatCoyoteAEatsRabbitX*currentRabbitPopulation*
>              coyoteIncreaseByEatingRabbit) * currentCoyotePopulation


delivers rabbit/coyote population trajectory via
simplified Lotka-Volterra model

>   rabbitCoyotePopulationTrajectory ::
>     RealFrac num => [num] -> [num] -> [[num]]
>   rabbitCoyotePopulationTrajectory populationChangeCoefficients
>                                    initialRabbitCoyotePopulations =
>     populationTrajectory
>       (rabbitCoyoteUpdateFunctions populationChangeCoefficients)
>       initialRabbitCoyotePopulations


update functions, packaged for populationTrajectory function

>   rabbitCoyoteUpdateFunctions::RealFrac num => [num] -> [[num]->num]
>   rabbitCoyoteUpdateFunctions populationChangeCoefficients =
>    [nextRabbitPopulation populationChangeCoefficients,
>     nextCoyotePopulation populationChangeCoefficients ]


deliver time-sequence of population figures given
population update functions and initial populations
Note: can handle any number of species

>   populationTrajectory ::
>    [[population] -> population] -> [population] -> [[population]]
>   populationTrajectory updateFunctions = iterate(pam updateFunctions)


------------------ Project 4B: makeChart ----------------

deliver picture and associated scale figures
given character to use to mark points, picture dimensions, and points

>   makeChart ::
>    RealFrac num => Char -> Int -> Int -> [[num]] -> ([num], [String])
>   makeChart dot width height points =
>     ([minHorizontal,maxHorizontal,minVertical,maxVertical], picture) 
>     where
>     minHorizontal = minimum[x | [x, y] <- points]
>     maxHorizontal = maximum[x | [x, y] <- points]
>     minVertical   = minimum[y | [x, y] <- points]
>     maxVertical   = maximum[y | [x, y] <- points]
>     picture = sequenceOfUpdates (blankPicture width height)  
>     sequenceOfUpdates =
>       foldr (.) id [updatePicture dot digitalPoint |
>                                  digitalPoint <- digitalPoints]
>     digitalPoints =
>      [[digitize width minHorizontal maxHorizontal x,
>        digitize height minVertical maxVertical y] | [x, y] <- points]


make blank picture

>   blankPicture :: Int -> Int -> [String]
>   blankPicture width height = reps height (spaces width)   


---------------  Project 4C: updatePicture -----------------


function: updatePicture
  inserts a dot in a picture at a specified point;
  a point's position in the picture is represented by
  a sequence of two integers [i, j], where i is the
  distance from the left edge and j is the distance
  from the top;
  a picture is represented as a sequence of strings,
  each of which has the same length; the top of the
  picture is the first string and the left edge is
  the first character of each string; indexing starts
  at zero in both cases

>   updatePicture :: Char -> [Int] -> [String] -> [String]
>   updatePicture dot [distanceFromLeft, distanceFromTop] =
>     update (update dotSetter distanceFromLeft) distanceFromTop
>     where
>     dotSetter anything = dot


function: update
  uses a function supplied as an argument to make a change
  in an element of a sequence; the element to be changed
  is specified in the second argument as a zero-based index

>   update :: (a -> a) -> Int  -> [a] -> [a]
>   update modifier zeroBasedIndex xs =
>     before ++ [modifier x] ++ after
>     where
>     (before, atAndBeyond) = splitAt zeroBasedIndex xs
>     x = head atAndBeyond
>     after = tail atAndBeyond


------------------ Project 4D: displayChart ----------------

convert chart (from makeChart function) to displayable string

>   displayChart ::
>     RealFrac num => [String] -> ([num], [String]) -> String
>   displayChart
>     [horizontalAxisName, verticalAxisName]
>     ([minHorizontal,maxHorizontal,minVertical,maxVertical], picture)=
>     unlines(
>       [topBorder] ++
>       [addSideBorders verticalBarOnLeft row | row <- chartAbove]   ++
>       [addSideBorders barWithAxisName row | row <- chartAtAxisName]++
>       [addSideBorders verticalBarOnLeft row | row <- chartBelow]   ++
>       [bottomBorder] ++
>       [horizontalLabels])
>     where
>     topBorder    = maxVerticalLabelPadded ++ horizontalBorder
>     bottomBorder = minVerticalLabelPadded ++ horizontalBorder
>     maxVerticalLabel   = show maxVertical
>     minVerticalLabel   = show minVertical
>     maxHorizontalLabel = show maxHorizontal
>     minHorizontalLabel = show minHorizontal
>     pictureWithMaxAtTop = reverse picture
>     (chartAbove, chartAtAndBelow) =
>       splitAt (pictureHeight `div` 2) pictureWithMaxAtTop
>     (chartAtAxisName, chartBelow) = splitAt 1 chartAtAndBelow
>     verticalBarOnLeft = rightJustify verticalLabelWidth ' ' "|"
>     barWithAxisName =
>       rightJustify verticalLabelWidth ' ' (verticalAxisName ++ "|")
>     verticalLabelWidth =
>       1 + maximum[length label | label <- verticalAxisLabels]
>     verticalAxisLabels =
>       [maxVerticalLabel, minVerticalLabel, verticalAxisName]
>     maxVerticalLabelPadded =
>       rightJustify verticalLabelWidth ' ' (maxVerticalLabel ++ "|")
>     minVerticalLabelPadded =
>       rightJustify verticalLabelWidth ' ' (minVerticalLabel ++ "|")  
>     horizontalBorder = reps pictureWidth '-'  ++ "|"
>     addSideBorders label row = label ++  row  ++ "|"
>     horizontalLabels =   
>       verticalBarOnLeft ++ minHorizontalLabel ++
>       spaces before ++ horizontalAxisName ++ spaces after ++ "|" ++
>       maxHorizontalLabel
>     spaceRemaining =
>       max 0 (pictureWidth -
>              (length minHorizontalLabel + length horizontalAxisName))
>     before = spaceRemaining `div` 2
>     after  = spaceRemaining - before
>     pictureWidth  = (length . head) picture
>     pictureHeight = length picture


-----------  unit testing ----------------

For ecology equations:

>   coeffs, inits :: [Float]
>   coeffs = [0.04, 0.1, 0.002, 0.03]
>   inits  = [1000, 10]

>   period :: Int
>   period = 120

>   tst1 =
>     (take period .
>       populationTrajectory(rabbitCoyoteUpdateFunctions coeffs)) inits


For drawing utilities:

>   blankPic = blankPicture 5 5
>   xPosn, yPosn :: Int 
>   xPosn = 2
>   yPosn = 2
>   dot = '*'

>   tst2 = unlines(updatePicture dot [xPosn, yPosn] blankPic)


For overall computation:

>   width, height :: Int
>   width = 30
>   height = 20

>   tst3 =  makeChart dot width height tst1
>   tst4 = (putStr . unlines . snd) tst3
>   tst5 = (putStr . displayChart["Rabbits", "Coyotes"] .
>             makeChart dot width height) tst1
Last Modified: