"""Excentury Sample Package

This package contains only one function. The comments written in this
section should be used to give an overall description of the
excentury package being written.

"""
#include <excentury/excentury.h>
#define VECTOR excentury::DynamicTensor<1, double>
#define MATRIX excentury::DynamicTensor<2, double>
#define MTRAND excentury::MTRand

----------------------------------------------------------------------
@def{lotka_volterra}
    """Generate a trajectory from the Lokta-Volterra model using
    Gillespie's Stochastic Simulation Algorithm. The output is a
    vector `t` and a matrix `v` where the first column contains the
    size of the prey population and the second column the size of the
    predator population. """
    @param{double, A(1.0), "birth rate of prey"}
    @param{double, B(0.001), "death rate of prey"}
    @param{double, C(0.002), "birth rate of predator"}
    @param{double, D(1.0), "death rate of predator"}
    @param{int, x0(1000), "initial prey population size"}
    @param{int, y0(1000), "initial predator population size"}
    @param{int, num_points(50000), "total number of points"}
@body[[
    // z(m,n) is the change to the m-th population
    // due to the n-th event.
    MATRIX z(2, 4);
    z(0,0) = 1; z(0,1) = -1; z(0,2) = 0; z(0,3) =  0;
    z(1,0) = 0; z(1,1) =  0; z(1,2) = 1; z(1,3) = -1;

    // Random number generator
    MTRAND rg;

    double x = x0;
    double y = y0;
    
    double a[4], a0, tau, prop_sum, rand_num;
    int event;

    VECTOR t(num_points);
    MATRIX v(2, num_points);
    t[0] = 0;
    v(0,0) = x0;
    v(1,0) = y0;

    for (int point = 1; point < num_points; ++point) {
        // Evaluating propensity functions
        a[0] = A*x;
        a[1] = B*x*y;
        a[2] = C*x*y;
        a[3] = D*y;
        // Sum of propensity functions
        a0 = 0;
        for (int i=0; i < 4; ++i) a0 += a[i];
        if (a0 == 0) {
            break;
        }
        // Time between events
        tau = log(1/rg.rand())/a0;
        // Next event computation
        event = -1;
        prop_sum = 0;
        rand_num = rg.rand()*a0;
        while (prop_sum < rand_num) {
            event = event + 1;
            prop_sum = prop_sum + a[event];
        }
        // Updating system
        x = x + z(0,event);
        y = y + z(1,event);
        // Storing point
        t[point] = t[point-1] + tau;
        v(0,point) = x;
        v(1,point) = y;
    }
]]
@ret[[
    @ret{t, "t", t[0]}
    @ret{v, "v", v[0]}
]]
----------------------------------------------------------------------
