/*
 * LVroutines.java
 * @author  Michael Anderson
 *
 *  Common and useful routines for use with the 
 * Lotka Volterra Model.  Like the java Math class,
 * this class is non-instantiable.
 *
 * Created on July 29, 2004, 6:53 PM
 * Last Updated on January 16, 2005
 */

package LotkaVolterra;

import Jama.*;

public final class LVroutines {
    
    
    //  A private constructor will make this
    // class non-instantiable
    private LVroutines() {}
    
    
    // Initialize each species in the animal array
    // animal is the array of species.
    // numberOfConnections is the number of species each species is
    // connected to.
    // p ranges from 0 to 1.  p = 0 is a perfectly ordered network
    // p = 1 is a totally random network.
    public static void initializeAnimals(Animal[] animal, 
                                         int numberOfConnections, double p) {
        
        int numberOfSpecies = animal.length;
        
        double[][] alphaMatrix = LVmatrix.createAlphaMatrix(numberOfSpecies, numberOfConnections, p);
        
        for (int i = 0; i < numberOfSpecies; i++) {
          animal[i] = new Animal(alphaMatrix[i]);
        }
        
    }  // End of the initializeAnimals method
        
    
    // Initialize the animals with the paramaters from a file
    public static Animal[] createAnimals(String paramFile) {
        
        Animal[] animal = LVfileIO.createAnimalsFromFile(paramFile);
        
        return animal;
    }
    
    
    // Create a copy of an Animal array
    public static Animal[] createCopy(Animal[] animal) {
        
        int numberOfSpecies = animal.length;
        Animal[] copy = new Animal[numberOfSpecies];
        
        
        for (int i = 0; i < numberOfSpecies; i++) {
            copy[i] = new Animal(animal[i].r, animal[i].getX(), (int[])animal[i].connection.clone(), (double[])animal[i].conStrength.clone());
        }
        
        return copy;
    }
    
    
    // Create an ordered ring of animals with specified:
    // size, initial populations, number of connections and connection strengths 
    public static Animal[] createOrderedAnimals(int size, double[] initialPopulations, int numConnections, double connectionStrengths) {
        
        Animal[] orderedAnimals = new Animal[size];
        double[][] orderedAlphaMatrix = LVmatrix.createOrderedMatrix(size, numConnections, connectionStrengths);
        
        for (int i = 0; i < size; i++) {
            orderedAnimals[i] = new Animal(1.0, initialPopulations[i], orderedAlphaMatrix[i]);
        }
        
        return orderedAnimals;
    }  // End createOrderedAnimals
    
    
    // Set the populations near some fixed point
    // by some value +- d
    public static void setPopulationsNearPoint(Animal[] animal, double d, double[] point) {
        
        for (int i = 0; i < animal.length; i++) {
            animal[i].setX(Math.random() * 2 * d + (point[i] - d));
        }
        
    }  // End setPopulationsNearPoint
    
    
    // Mix up the populations of the animals
    // (Usually done in the initialization process)
    public static void randomizePopulations(Animal[] animal) {
        
        for (int i = 0; i < animal.length; i++) {
            animal[i].setX(Math.random());
        }
    
    }  // End of randomizePopulations
    
    
    // Given an Animal array, mix up a specific number, p
    // of the links and return that new array
    public static Animal[] createAnimals(Animal[] animal, int p) {
        
        int numberOfSpecies = animal.length;
        Animal[] mixedUpAnimal = createCopy(animal);
        
        // Change p number of links
        for (int i = 0; i < p; i++) {
            
            // pick a species at random 0 thru N-1
            int q = (int)(Math.random() * numberOfSpecies);
            int linkToMove;
            int moveHere;
            
            // now pick one of that species 
            // links, but don't move a self link
            do {
                linkToMove = (int)(Math.random() * animal[q].connection.length);
            } while (animal[q].connection[linkToMove] == q);
            
            // now find somewhere else to put that link
            // but don't move it to an occupied spot
            boolean occupied = false;
            do {
                
                occupied = false;
                
                //pick a place to move it to
                moveHere = (int)(Math.random() * numberOfSpecies);
                
                //look through q's links and see
                //if that place is occupied already
                for (int j = 0; j < animal[q].connection.length; j++) {
                    if (animal[q].connection[j] == moveHere) {
                        occupied = true;
                    }
                }
                
            } while (occupied == true);
            
            mixedUpAnimal[q].connection[linkToMove] = moveHere;
        }  // end of change number of p links
        
        return mixedUpAnimal;
    }
    
    
    // Given an Animal array, mix up a percentage, p
    // of the links and return that new array
    public static Animal[] createAnimals(Animal[] animal, double p) {
        
        int numberOfSpecies = animal.length;
        Animal[] mixedUpAnimal = createCopy(animal);
        
        int newLocation = 0;
        boolean LinkThereAlready = false;
        int i, j, k;  // counters
        
        for (i = 0; i < numberOfSpecies; i++) {
            for (j = 0; j < animal[i].connection.length; j++) {
                
                if ((i != animal[i].connection[j]) && (Math.random() < p)) {
                    // Don't change an animal's connection to itself
                    // and only change a percentage, p, of the links
                    
                    do {
                        newLocation = (int)(numberOfSpecies * Math.random());
                        
                        LinkThereAlready = false;
                        for (k = 0; k < animal[i].connection.length; k++) {
                            if (animal[i].connection[k] == newLocation) {
                                LinkThereAlready = true;
                            }
                        }
                        
                    } while (LinkThereAlready == true);
                    
                    mixedUpAnimal[i].connection[j] = newLocation;
                    
                    
                }
                
            }  // end of j loop
        }  // end of i loop
        
        return mixedUpAnimal;
    }  // End of the mixUpConnections method
    
    
    // Given an Animal array, a percentage, p
    // of the links and return that new array
    public static Animal[] deleteConnections(Animal[] animal, double p) {
        
        int numberOfSpecies = animal.length;
        Animal[] smallerAnimal = createCopy(animal);
        
        for (int i = 0; i < numberOfSpecies; i++) {
            for (int j = 0; j < animal[i].connection.length; j++) {
                
                if ((i != animal[i].connection[j]) && (Math.random() < p)) {
                    // Don't delete an animal's connection to itself
                    // and only delete a percentage, p, of the links
                    
                    smallerAnimal[i].conStrength[j] = 0;
                    //smallerAnimal[i].removeConnection(animal[i].connection[j]);
                }
                
            }  // end of j loop
        }  // end of i loop
        
        return smallerAnimal;
    }  // End of the mixUpConnections method
    
    
    // Let the system play out through time.
    // Pass in an array of animals,
    // the number of time units to go through, and the time step size (dt)
    public static void evolveThruTime(Animal[] animal, double timeUnits, double dt) {
        
        int numberOfSpecies = animal.length;
        int i = 0;  // counter
        
        // Create the storage area for an array of populations
        double[] x = new double[numberOfSpecies];
        
        for (double t = 0; t < timeUnits; t = t + dt) {
            
            //Copy all the populations into an array before
            //steping through time.
            for (i = 0; i < numberOfSpecies; i++) {
                x[i] = animal[i].getX();
            }
            
            //Have each species step once through time dt.
            for (i = 0; i < numberOfSpecies; i++) {
                animal[i].stepOnce(x, dt);
            }
        }
        
    }  // End of the evolveThruTime method
    
    
    // Let the system play out through time.
    // Change links if a species comes close to dying.
    // Pass in an array of animals,
    // the number of time units to go through, and the time step size (dt)
    public static void evolveLinks(Animal[] animal, double timeUnits, double dt) {
        
        int numberOfSpecies = animal.length;
        int i = 0;  // counter
        
        // Create the storage area for an array of populations
        double[] x = new double[numberOfSpecies];
        
        for (double t = 0; t < timeUnits; t = t + dt) {
            
            //Copy all the populations into an array before
            //steping through time.
            for (i = 0; i < numberOfSpecies; i++) {
                x[i] = animal[i].getX();
            }
            
            //Have each species step once through time dt.
            for (i = 0; i < numberOfSpecies; i++) {
                animal[i].stepOnce(x, dt);
            }
            
            //Check populations and shift link if too low
            for (i = 0; i < numberOfSpecies; i++) {
                
                // check to see if population is too low
                if (animal[i].getX() < Math.pow(10, -7)) {
                    System.out.println(t);
                    
                    // Move a random link one over
                    moveRandomLinksByOne(animal, 1);
                    
                    // Reset populations near fixed point
                    for (int j = 0; j < numberOfSpecies; j++) {
                        animal[j].setX(Math.random() * 0.05 + 0.3083333);
                    }
                    
                } // end of IF pop is low
            }  // end of i loop (for checking populations)
            
        } // end of t loop
        
    }  // End of the evolveLinks method
    
    
    // Let the system play out through time.  Find info
    // about it's distance from the All-Alive-Fixed-Point
    // Pass in an array of animals,
    // the number of time units to go through, and the time step size (dt)
    // and also the location of the All-Alive-Fixed-Point
    public static double[] findMinAveMaxDist(Animal[] animal, double timeUnits, double dt, double[] allAlivePoint) {
        
        //these variables are for determining size of attractor
        double[] minAveMax = new double[3];
        double min = Integer.MAX_VALUE;
        double ave = 0;
        double max = Integer.MIN_VALUE;
        double currentDistance;
        long bigCount = 0;
        
        int numberOfSpecies = animal.length;
        int i = 0;  // counter
        
        // Create the storage area for an array of populations
        double[] x = new double[numberOfSpecies];
        
        for (double t = 0; t < timeUnits; t = t + dt) {
            
            //Copy all the populations into an array before
            //steping through time.
            for (i = 0; i < numberOfSpecies; i++) {
                x[i] = animal[i].getX();
            }
            
            //Have each species step once through time dt.
            for (i = 0; i < numberOfSpecies; i++) {
                animal[i].stepOnce(x, dt);
            }
            
            //now determine distance
            currentDistance = 0;
            for (i = 0; i < numberOfSpecies; i++) {
                currentDistance = currentDistance + Math.pow(animal[i].getX() - allAlivePoint[i], 2);
            }
            currentDistance = Math.pow(currentDistance, 0.5);
            
            //check to see if that distance is a min or max
            if (currentDistance < min) {
                min = currentDistance;
            }
            if (currentDistance > max) {
                max = currentDistance;
            }
            ave = ave + currentDistance;
            bigCount = bigCount + 1;
            
        }
            
        ave = ave / bigCount;
        
        minAveMax[0] = min;
        minAveMax[1] = ave;
        minAveMax[2] = max;
        
        return minAveMax;
    }  // End of the evolveThruTime method
    
    
    // Check to see if any species is dead
    public static boolean noneDead(Animal[] animal) {
        boolean noneDead = true;
        for (int i = 0; i < animal.length; i++) {
            if (animal[i].getX() < Math.pow(10, -7)) {
                noneDead = false;
            }
        }
        
        return noneDead;
    }
    
    
    // Calculate the Largest Lyapunov Exponent
    public static double calculateLE(Animal[] animal, int timeUnits, double dt) {
        
        int numberOfSpecies = animal.length;
        
        
        // First Create a copy of both animal systems
        Animal[] animalSet1 = createCopy(animal);
        Animal[] animalSet2 = createCopy(animal);
        
        // Now create the variables used in calculating LE
        double d0 = Math.pow(10, -10);
        double d1 = 0;
        long count = 0;
        double sum = 0;
        int i = 0;  // counter
        
        // Move the location of Set 2 by some small d0
        animalSet2[0].setX(animalSet2[0].getX() + d0);
        
        //int times = 0;  * for calculating StDev of LLE
        for (double t = 0; t < timeUnits; t = t + dt) {
            
            // Have both systems move through one step dt
            allStepOnce(animalSet1, dt);
            allStepOnce(animalSet2, dt);
            //allStepOnceRK(animalSet1, dt);  // RK has higher accuracy?
            //allStepOnceRK(animalSet2, dt);
            
            // Calculate new separation
            d1 = 0;
            for (i = 0; i < numberOfSpecies; i++) {
                d1 = d1 + Math.pow(animalSet2[i].getX() - animalSet1[i].getX(), 2);
            }
            d1 = Math.pow(d1, 0.5);
            
            sum = sum + Math.log(d1/d0);
            count++;
            
            // Re-adjust the orbit the orbit of Set 2
            // to be only a distance d0 away
            for (i = 0; i < numberOfSpecies; i++) {
                animalSet2[i].setX(animalSet1[i].getX() + (d0/d1) * (animalSet2[i].getX() - animalSet1[i].getX()));
            }
            
            //maybe print off some of the LLE
            //if ((times % 20000) == 0) {
            //    System.out.println(t + ";" + (1 / dt) * (sum / count));
            //}
            //times++;
            
        } // end of t loop
        
        return (1 / dt) * (sum / count);
    }  // end of the calculateLE method
    
    
    // Returns the location of the all-alive fixed point
    public static double[] allAliveFixedPoint(Animal[] animal) {
        
        int numberOfSpecies = animal.length;
        
        // Ax = 1
        // is the location of the All-Alive fixed point
        Matrix alphaMatrix = new Matrix(LVmatrix.createAlphaMatrix(animal));
        Matrix onesVector = new Matrix(numberOfSpecies, 1, 1.0);
        Matrix x = alphaMatrix.solve(onesVector);
        
        return x.transpose().getArray()[0];
    }
    
    // Have all the animals step one timestep, dt, into the future.
    // pop is the array of populations before any took a step.
    // This method is using the "Improved Euler Method"
    // which is why it looks a little wacky.
    public static void allStepOnce(Animal[] animal, double dt) {
        
        int numberOfSpecies = animal.length;
        double[] xNow = new double[numberOfSpecies];
        double[] xAfter = new double[numberOfSpecies];
        
        //Copy all the populations into an array before
        //steping through time.
        for (int i = 0; i < numberOfSpecies; i++) {
            xNow[i] = animal[i].getX();
        }
        
        // Get the dx/dt for all the species now
        double[] dxdtNow = new double[numberOfSpecies];
        for (int i = 0; i < numberOfSpecies; i++) {
            dxdtNow[i] = animal[i].getdxdt(xNow);
        }
        
        // Find the population of each after
        for (int i = 0; i < numberOfSpecies; i++) {
            xAfter[i] = xNow[i] + dt * dxdtNow[i];
        }
        
        // Get the dx/dt for all the species after the timestep
        double[] dxdtAfter = new double[numberOfSpecies];
        for (int i = 0; i < numberOfSpecies; i++) {
            dxdtAfter[i] = animal[i].getdxdt(xAfter);
        }
        
        for (int i = 0; i < numberOfSpecies; i++) {
            animal[i].setX(animal[i].getX() + (dt/2) * (dxdtNow[i] + dxdtAfter[i]));
        }
        
    }  // End of the allStepOnce method 
    
    
    public static void allStepOnceRK(Animal[] animal, double dt) {
        
        int numberOfSpecies = animal.length;
        double[] x0 = new double[numberOfSpecies];
        double[] x1 = new double[numberOfSpecies];
        double[] x2 = new double[numberOfSpecies];
        double[] x3 = new double[numberOfSpecies];
        double[] dxdt0 = new double[numberOfSpecies];
        double[] dxdt1 = new double[numberOfSpecies];
        double[] dxdt2 = new double[numberOfSpecies];
        double[] dxdt3 = new double[numberOfSpecies];
        
        
        for (int i = 0; i < numberOfSpecies; i++) {
            x0[i] = animal[i].getX();
        }
        
        // dxdt0 is dx/dt now
        for (int i = 0; i < numberOfSpecies; i++) {
            dxdt0[i] = animal[i].getdxdt(x0);
        }
        
        // dxdt1 is dx/dt now + 1/2*dt
        for (int i = 0; i < numberOfSpecies; i++) {
            x1[i] = x0[i] + 0.5 * dt * dxdt0[i];
        }
        for (int i = 0; i< numberOfSpecies; i++) {
            dxdt1[i] = animal[i].getdxdt(x1);
        }
        
        // dxdt2 is dx/dt now + 1/2*dt
        for (int i = 0; i < numberOfSpecies; i++) {
            x2[i] = x0[i] + 0.5 * dt * dxdt1[i];
        }
        for (int i = 0; i< numberOfSpecies; i++) {
            dxdt2[i] = animal[i].getdxdt(x2);
        }
        
        // dxdt3 is dx/dt now + dt
        for (int i = 0; i < numberOfSpecies; i++) {
            x3[i] = x0[i] + dt * dxdt2[i];
        }
        for (int i = 0; i< numberOfSpecies; i++) {
            dxdt3[i] = animal[i].getdxdt(x3);
        }
        
        // now set the new population of each animal
        for (int i = 0; i < numberOfSpecies; i++) {
            animal[i].setX(x0[i] + dt / 6 * (dxdt0[i] + 2 * dxdt1[i] + 2 * dxdt2[i] + dxdt3[i]));
        }
    }
    
    //Count the total number of links
    public static int totalLinks(Animal[] animal) {
        
        int totalLinks = 0;
        
        for (int i = 0; i < animal.length; i++) {
            totalLinks = totalLinks + animal[i].connection.length;
        }
        
        return totalLinks;
    }  // end of the countTotalLinks method
    
    
    //Find the species with the most links and return that number
    public static int maxLinks(Animal[] animal) {
        int max = 0;
        
        for (int i = 0; i < animal.length; i++) {
            if (animal[i].connection.length > max) {
                max = animal[i].connection.length;
            }
        }
        
        return max;
    }
    
    
    //Find the species with the least links and return that number
    public static int minLinks(Animal[] animal) {
        int min = animal[0].connection.length;
        
        for (int i = 1; i < animal.length; i++) {
            if (animal[i].connection.length < min) {
                min = animal[i].connection.length;
            }
        }
        
        return min;
    }
    
    
    // Randomly move link +-1 space
    public static Animal[] moveRandomLinksByOne(Animal[] template, int numLinksToMove) {
        
        int numberOfSpecies = template.length;
        Animal[] animal = LVroutines.createCopy(template);
        
        // Change a number of links
        for (int i = 0; i < numLinksToMove; i++) {
            
            // p