package beast.base.evolution.likelihood;

import java.util.List;


/** standard likelihood core, uses no caching **/
public class ThreadedBeerLikelihoodCore extends ThreadedLikelihoodCore {
    protected int m_nStates;
    protected int m_nNodes;
    protected int m_nPatterns;
    protected int m_nPartialsSize;
    protected int m_nMatrixSize;
    protected int m_nMatrices;

    protected boolean m_bIntegrateCategories;

    protected double[][][] m_fPartials;

    protected int[][] m_iStates;

    protected double[][][] m_fMatrices;

    protected int[] m_iCurrentMatrices;
    protected int[] m_iStoredMatrices;
    protected int[] m_iCurrentPartials;
    protected int[] m_iStoredPartials;

    protected boolean m_bUseScaling = false;

    protected double[][][] m_fScalingFactors;

    private double m_fScalingThreshold = 1.0E-100;
    double SCALE = 2;

    int [] weights;
    double [] m_fPatternLogLikelihoods;
    List<Integer> m_iConstantPattern;
    /** memory allocation for the root partials **/
    double[][] m_fRootPartials;

    
	public ThreadedBeerLikelihoodCore(int nStateCount) {
		this.m_nStates = nStateCount;
	} // c'tor
	

	/**
	 * Calculates partial likelihoods at a node when both children have states.
	 */
	protected void calculateStatesStatesPruning(int[] iStates1, double[] fMatrices1,
												int[] iStates2, double[] fMatrices2,
												double[] fPartials3, int iFrom, int iTo)
	{

		for (int l = 0; l < m_nMatrices; l++) {
			int v = m_nStates * iFrom  +  m_nStates * l * m_nPatterns;

			for (int k = iFrom; k < iTo; k++) {

				int state1 = iStates1[k];
				int state2 = iStates2[k];

				int w = l * m_nMatrixSize;

                if (state1 < m_nStates && state2 < m_nStates) {

					for (int i = 0; i < m_nStates; i++) {

						fPartials3[v] = fMatrices1[w + state1] * fMatrices2[w + state2];

						v++;
						w += m_nStates;
					}

				} else if (state1 < m_nStates) {
					// child 2 has a gap or unknown state so treat it as unknown

					for (int i = 0; i < m_nStates; i++) {

						fPartials3[v] = fMatrices1[w + state1];

						v++;
						w += m_nStates;
					}
				} else if (state2 < m_nStates) {
					// child 2 has a gap or unknown state so treat it as unknown

					for (int i = 0; i < m_nStates; i++) {

						fPartials3[v] = fMatrices2[w + state2];

						v++;
						w += m_nStates;
					}
				} else {
					// both children have a gap or unknown state so set partials to 1

					for (int j = 0; j < m_nStates; j++) {
						fPartials3[v] = 1.0;
						v++;
					}
				}
			}
		}
	}

	/**
	 * Calculates partial likelihoods at a node when one child has states and one has partials.
	 */
	protected void calculateStatesPartialsPruning(	int[] iStates1, double[] fMatrices1,
													double[] fPartials2, double[] fMatrices2,
													double[] fPartials3, int iFrom, int iTo)
	{

		double sum, tmp;


		for (int l = 0; l < m_nMatrices; l++) {
			int v = m_nStates * iFrom  +  m_nStates * l * m_nPatterns;
			int u = v;
			for (int k = iFrom; k < iTo; k++) {

				int state1 = iStates1[k];

                int w = l * m_nMatrixSize;

				if (state1 < m_nStates) {


					for (int i = 0; i < m_nStates; i++) {

						tmp = fMatrices1[w + state1];

						sum = 0.0;
						for (int j = 0; j < m_nStates; j++) {
							sum += fMatrices2[w] * fPartials2[v + j];
							w++;
						}

						fPartials3[u] = tmp * sum;
						u++;
					}

					v += m_nStates;
				} else {
					// Child 1 has a gap or unknown state so don't use it

					for (int i = 0; i < m_nStates; i++) {

						sum = 0.0;
						for (int j = 0; j < m_nStates; j++) {
							sum += fMatrices2[w] * fPartials2[v + j];
							w++;
						}

						fPartials3[u] = sum;
						u++;
					}

					v += m_nStates;
				}
			}
		}
	}

	/**
	 * Calculates partial likelihoods at a node when both children have partials.
	 */
	protected void calculatePartialsPartialsPruning(double[] fPartials1, double[] fMatrices1,
													double[] fPartials2, double[] fMatrices2,
													double[] fPartials3, int iFrom, int iTo)
	{
		double sum1, sum2;

		for (int l = 0; l < m_nMatrices; l++) {
			int v = m_nStates * iFrom  +  m_nStates * l * m_nPatterns;
			int u = v;

			for (int k = iFrom; k < iTo; k++) {

                int w = l * m_nMatrixSize;

				for (int i = 0; i < m_nStates; i++) {

					sum1 = sum2 = 0.0;

					for (int j = 0; j < m_nStates; j++) {
						sum1 += fMatrices1[w] * fPartials1[v + j];
						sum2 += fMatrices2[w] * fPartials2[v + j];

						w++;
					}

					fPartials3[u] = sum1 * sum2;
					u++;
				}
				v += m_nStates;
			}
		}
	}

	/**
	 * Calculates partial likelihoods at a node when both children have states.
	 */
	protected void calculateStatesStatesPruning(int[] iStates1, double[] fMatrices1,
												int[] iStates2, double[] fMatrices2,
												double[] fPartials3, int[] iMatrixMap, int iFrom, int iTo)
	{
		int v = m_nStates * iFrom * m_nMatrices;

		for (int k = iFrom; k < iTo; k++) {

			int state1 = iStates1[k];
			int state2 = iStates2[k];

			int w = iMatrixMap[k] * m_nMatrixSize;

			if (state1 < m_nStates && state2 < m_nStates) {

				for (int i = 0; i < m_nStates; i++) {

					fPartials3[v] = fMatrices1[w + state1] * fMatrices2[w + state2];

					v++;
					w += m_nStates;
				}

			} else if (state1 < m_nStates) {
				// child 2 has a gap or unknown state so treat it as unknown

				for (int i = 0; i < m_nStates; i++) {

					fPartials3[v] = fMatrices1[w + state1];

					v++;
					w += m_nStates;
				}
			} else if (state2 < m_nStates) {
				// child 2 has a gap or unknown state so treat it as unknown

				for (int i = 0; i < m_nStates; i++) {

					fPartials3[v] = fMatrices2[w + state2];

					v++;
					w += m_nStates;
				}
			} else {
				// both children have a gap or unknown state so set partials to 1

				for (int j = 0; j < m_nStates; j++) {
					fPartials3[v] = 1.0;
					v++;
				}
			}
		}
	}

	/**
	 * Calculates partial likelihoods at a node when one child has states and one has partials.
	 */
	protected void calculateStatesPartialsPruning(	int[] iStates1, double[] fMatrices1,
													double[] fPartials2, double[] fMatrices2,
													double[] fPartials3, int[] iMatrixMap, int iFrom, int iTo)
	{

		double sum, tmp;

		int u = m_nStates * iFrom * m_nMatrices;
		int v = u;

		for (int k = iFrom; k < iTo; k++) {

			int state1 = iStates1[k];

			int w = iMatrixMap[k] * m_nMatrixSize;

			if (state1 < m_nStates) {

				for (int i = 0; i < m_nStates; i++) {

					tmp = fMatrices1[w + state1];

					sum = 0.0;
					for (int j = 0; j < m_nStates; j++) {
						sum += fMatrices2[w] * fPartials2[v + j];
						w++;
					}

					fPartials3[u] = tmp * sum;
					u++;
				}

				v += m_nStates;
			} else {
				// Child 1 has a gap or unknown state so don't use it

				for (int i = 0; i < m_nStates; i++) {

					sum = 0.0;
					for (int j = 0; j < m_nStates; j++) {
						sum += fMatrices2[w] * fPartials2[v + j];
						w++;
					}

					fPartials3[u] = sum;
					u++;
				}

				v += m_nStates;
			}
		}
	}

	/**
	 * Calculates partial likelihoods at a node when both children have partials.
	 */
	protected void calculatePartialsPartialsPruning(double[] fPartials1, double[] fMatrices1,
													double[] fPartials2, double[] fMatrices2,
													double[] fPartials3, int[] iMatrixMap, int iFrom, int iTo)
	{
		double sum1, sum2;

		int u = m_nStates * iFrom * m_nMatrices;
		int v = u;

		for (int k = iFrom; k < iTo; k++) {

			int w = iMatrixMap[k] * m_nMatrixSize;

			for (int i = 0; i < m_nStates; i++) {

				sum1 = sum2 = 0.0;

				for (int j = 0; j < m_nStates; j++) {
					sum1 += fMatrices1[w] * fPartials1[v + j];
					sum2 += fMatrices2[w] * fPartials2[v + j];

					w++;
				}

				fPartials3[u] = sum1 * sum2;
				u++;
			}
			v += m_nStates;
		}
	}

	/**
	 * Integrates partials across categories.
     * @param fInPartials the array of partials to be integrated
	 * @param fProportions the proportions of sites in each category
	 * @param fOutPartials an array into which the partials will go
	 */
	protected void calculateIntegratePartials(double[] fInPartials, double[] fProportions, double[] fOutPartials, int iFrom, int iTo)
	{

		int u = iFrom * m_nStates;
		int v = iFrom * m_nStates;
		for (int k = iFrom; k < iTo; k++) {

			for (int i = 0; i < m_nStates; i++) {

				fOutPartials[u] = fInPartials[v] * fProportions[0];
				u++;
				v++;
			}
		}


		for (int l = 1; l < m_nMatrices; l++) {
			u = iFrom * m_nStates;
			v = iFrom * m_nStates + l * m_nPatterns * m_nStates;
			
			for (int k = iFrom; k < iTo; k++) {

				for (int i = 0; i < m_nStates; i++) {

					fOutPartials[u] += fInPartials[v] * fProportions[l];
					u++;
					v++;
				}
			}
		}
	}

	/**
	 * Calculates pattern log likelihoods at a node.
	 * @param fPartials the partials used to calculate the likelihoods
	 * @param fFrequencies an array of state frequencies
	 * @param fOutLogLikelihoods an array into which the likelihoods will go
	 */
	//@Override
	public void calculateLogLikelihoods(int iThread, double[] fFrequencies, int iFrom, int iTo)
	{
		double[] fPartials = m_fRootPartials[iThread];
        int v = m_nStates * iFrom;
		for (int k = iFrom; k < iTo; k++) {

            double sum = 0.0;
			for (int i = 0; i < m_nStates; i++) {

				sum += fFrequencies[i] * fPartials[v];
				v++;
			}
            m_fPatternLogLikelihoods[k] = Math.log(sum) + getLogScalingFactor(k);
		}
	}



    /**
     * initializes partial likelihood arrays.
     *
     * @param nNodeCount           the number of nodes in the tree
     * @param nPatternCount        the number of patterns
     * @param nMatrixCount         the number of matrices (i.e., number of categories)
     * @param bIntegrateCategories whether sites are being integrated over all matrices
     */
	@Override
    public void initialize(int nNodeCount, int nPatternCount, int nMatrixCount, 
    		int [] weights, 
    		List<Integer> iConstantPattern,
			int nThreads,
    		boolean bIntegrateCategories) {

		this.weights = weights;
		m_iConstantPattern = iConstantPattern;;
        this.m_nNodes = nNodeCount;
        this.m_nPatterns = nPatternCount;
        this.m_nMatrices = nMatrixCount;

        this.m_bIntegrateCategories = bIntegrateCategories;

        if (bIntegrateCategories) {
            m_nPartialsSize = nPatternCount * m_nStates * nMatrixCount;
        } else {
            m_nPartialsSize = nPatternCount * m_nStates;
        }

        m_fPartials = new double[2][nNodeCount][];

        m_iCurrentMatrices = new int[nNodeCount];
        m_iStoredMatrices = new int[nNodeCount];

        m_iCurrentPartials = new int[nNodeCount];
        m_iStoredPartials = new int[nNodeCount];

        m_iStates = new int[nNodeCount][];

        for (int i = 0; i < nNodeCount; i++) {
            m_fPartials[0][i] = null;
            m_fPartials[1][i] = null;

            m_iStates[i] = null;
        }

        m_nMatrixSize = m_nStates * m_nStates;

        m_fMatrices = new double[2][nNodeCount][nMatrixCount * m_nMatrixSize];
        m_fPatternLogLikelihoods = new double[nPatternCount];
        m_fRootPartials = new double[nThreads][nPatternCount * m_nStates];

    }

    /**
     * cleans up and deallocates arrays.
     */
    public void finalize() throws java.lang.Throwable  {
        m_nNodes = 0;
        m_nPatterns = 0;
        m_nMatrices = 0;

        m_fPartials = null;
        m_iCurrentPartials = null;
        m_iStoredPartials = null;
        m_iStates = null;
        m_fMatrices = null;
        m_iCurrentMatrices = null;
        m_iStoredMatrices = null;

        m_fScalingFactors = null;
    }

    @Override
    public void setUseScaling(double  fScale) {
   		m_bUseScaling = Math.abs(fScale - 1.0) < 1e-10;

        if (m_bUseScaling) {
            m_fScalingFactors = new double[2][m_nNodes][m_nPatterns];
        }
    }

    /**
     * Allocates partials for a node
     */
    public void createNodePartials(int iNodeIndex) {

        this.m_fPartials[0][iNodeIndex] = new double[m_nPartialsSize];
        this.m_fPartials[1][iNodeIndex] = new double[m_nPartialsSize];
    }

    /**
     * Sets partials for a node
     */
    public void setNodePartials(int iNodeIndex, double[] fPartials) {

        if (this.m_fPartials[0][iNodeIndex] == null) {
            createNodePartials(iNodeIndex);
        }
        if (fPartials.length < m_nPartialsSize) {
            int k = 0;
            for (int i = 0; i < m_nMatrices; i++) {
                System.arraycopy(fPartials, 0, this.m_fPartials[0][iNodeIndex], k, fPartials.length);
                k += fPartials.length;
            }
        } else {
            System.arraycopy(fPartials, 0, this.m_fPartials[0][iNodeIndex], 0, fPartials.length);
        }
    }

    @Override
    public void getNodePartials(int iNodeIndex, double[] fPartials) {
        System.arraycopy(m_fPartials[m_iCurrentPartials[iNodeIndex]][iNodeIndex], 0, fPartials, 0, fPartials.length);
    }

    
    /**
     * Allocates states for a node
     */
    public void createNodeStates(int iNodeIndex) {

        this.m_iStates[iNodeIndex] = new int[m_nPatterns];
    }

    /**
     * Sets states for a node
     */
    public void setNodeStates(int iNodeIndex, int[] iStates) {

        if (this.m_iStates[iNodeIndex] == null) {
            createNodeStates(iNodeIndex);
        }
        System.arraycopy(iStates, 0, this.m_iStates[iNodeIndex], 0, m_nPatterns);
    }

    /**
     * Gets states for a node
     */
    public void getNodeStates(int iNodeIndex, int[] iStates) {
        System.arraycopy(this.m_iStates[iNodeIndex], 0, iStates, 0, m_nPatterns);
    }
    
    @Override
    public void setNodeMatrixForUpdate(int iNodeIndex) {
        m_iCurrentMatrices[iNodeIndex] = 1 - m_iCurrentMatrices[iNodeIndex];

    }


    /**
     * Sets probability matrix for a node
     */
    public void setNodeMatrix(int iNodeIndex, int iMatrixIndex, double[] fMatrix) {
        System.arraycopy(fMatrix, 0, m_fMatrices[m_iCurrentMatrices[iNodeIndex]][iNodeIndex],
                iMatrixIndex * m_nMatrixSize, m_nMatrixSize);
    }

	public void setPaddedNodeMatrices(int iNode, double[] fMatrix) {
        System.arraycopy(fMatrix, 0, m_fMatrices[m_iCurrentMatrices[iNode]][iNode],
                0, m_nMatrices * m_nMatrixSize);
    }
    
    

    /**
     * Gets probability matrix for a node
     */
    public void getNodeMatrix(int iNodeIndex, int iMatrixIndex, double[] fMatrix) {
        System.arraycopy(m_fMatrices[m_iCurrentMatrices[iNodeIndex]][iNodeIndex],
                iMatrixIndex * m_nMatrixSize, fMatrix, 0, m_nMatrixSize);
    }

//    @Override
//    public void setNodePartialsForUpdate(int iNodeIndex) {
//        m_iCurrentPartials[iNodeIndex] = 1 - m_iCurrentPartials[iNodeIndex];
//    }

    /**
     * Sets the currently updating node partials for node nodeIndex. This may
     * need to repeatedly copy the partials for the different category partitions
     */
    public void setCurrentNodePartials(int iNodeIndex, double[] fPartials) {
        if (fPartials.length < m_nPartialsSize) {
            int k = 0;
            for (int i = 0; i < m_nMatrices; i++) {
                System.arraycopy(fPartials, 0, this.m_fPartials[m_iCurrentPartials[iNodeIndex]][iNodeIndex], k, fPartials.length);
                k += fPartials.length;
            }
        } else {
            System.arraycopy(fPartials, 0, this.m_fPartials[m_iCurrentPartials[iNodeIndex]][iNodeIndex], 0, fPartials.length);
        }
    }

    /**
     * Calculates partial likelihoods at a node.
     *
     * @param iNodeIndex1 the 'child 1' node
     * @param iNodeIndex2 the 'child 2' node
     * @param iNodeIndex3 the 'parent' node
     */
    public void calculateAllPartials(int [] cacheNode1, int [] cacheNode2, int [] cacheNode3, int cacheNodeCount, int iFrom, int iTo) {
    	for (int i = 0; i < cacheNodeCount; i++) {
    		calculatePartials(cacheNode1[i], cacheNode2[i], cacheNode3[i], iFrom, iTo);
    	}
    }
    
    public void calculatePartials(int iNodeIndex1, int iNodeIndex2, int iNodeIndex3, int iFrom, int iTo) {
        m_iCurrentPartials[iNodeIndex3] = 1 - m_iStoredPartials[iNodeIndex3];
        
        if (m_iStates[iNodeIndex1] != null) {
            if (m_iStates[iNodeIndex2] != null) {
                calculateStatesStatesPruning(
                        m_iStates[iNodeIndex1], m_fMatrices[m_iCurrentMatrices[iNodeIndex1]][iNodeIndex1],
                        m_iStates[iNodeIndex2], m_fMatrices[m_iCurrentMatrices[iNodeIndex2]][iNodeIndex2],
                        m_fPartials[m_iCurrentPartials[iNodeIndex3]][iNodeIndex3], iFrom, iTo);
            } else {
                calculateStatesPartialsPruning(m_iStates[iNodeIndex1], m_fMatrices[m_iCurrentMatrices[iNodeIndex1]][iNodeIndex1],
                        m_fPartials[m_iCurrentPartials[iNodeIndex2]][iNodeIndex2], m_fMatrices[m_iCurrentMatrices[iNodeIndex2]][iNodeIndex2],
                        m_fPartials[m_iCurrentPartials[iNodeIndex3]][iNodeIndex3], iFrom, iTo);
            }
        } else {
            if (m_iStates[iNodeIndex2] != null) {
                calculateStatesPartialsPruning(m_iStates[iNodeIndex2], m_fMatrices[m_iCurrentMatrices[iNodeIndex2]][iNodeIndex2],
                        m_fPartials[m_iCurrentPartials[iNodeIndex1]][iNodeIndex1], m_fMatrices[m_iCurrentMatrices[iNodeIndex1]][iNodeIndex1],
                        m_fPartials[m_iCurrentPartials[iNodeIndex3]][iNodeIndex3], iFrom, iTo);
            } else {
                calculatePartialsPartialsPruning(m_fPartials[m_iCurrentPartials[iNodeIndex1]][iNodeIndex1], m_fMatrices[m_iCurrentMatrices[iNodeIndex1]][iNodeIndex1],
                        m_fPartials[m_iCurrentPartials[iNodeIndex2]][iNodeIndex2], m_fMatrices[m_iCurrentMatrices[iNodeIndex2]][iNodeIndex2],
                        m_fPartials[m_iCurrentPartials[iNodeIndex3]][iNodeIndex3], iFrom, iTo);
            }
        }

        if (m_bUseScaling) {
            scalePartials(iNodeIndex3, iFrom, iTo);
        }

//
//        int k =0;
//        for (int i = 0; i < patternCount; i++) {
//            double f = 0.0;
//
//            for (int j = 0; j < stateCount; j++) {
//                f += partials[currentPartialsIndices[nodeIndex3]][nodeIndex3][k];
//                k++;
//            }
//            if (f == 0.0) {
//                Logger.getLogger("error").severe("A partial likelihood (node index = " + nodeIndex3 + ", pattern = "+ i +") is zero for all states.");
//            }
//        }
    }

    /**
     * Calculates partial likelihoods at a node.
     *
     * @param iNodeIndex1 the 'child 1' node
     * @param iNodeIndex2 the 'child 2' node
     * @param iNodeIndex3 the 'parent' node
     * @param iMatrixMap  a map of which matrix to use for each pattern (can be null if integrating over categories)
     */
    public void calculatePartials(int iNodeIndex1, int iNodeIndex2, int iNodeIndex3, int[] iMatrixMap, int iFrom, int iTo) {
        if (m_iStates[iNodeIndex1] != null) {
            if (m_iStates[iNodeIndex2] != null) {
                calculateStatesStatesPruning(
                        m_iStates[iNodeIndex1], m_fMatrices[m_iCurrentMatrices[iNodeIndex1]][iNodeIndex1],
                        m_iStates[iNodeIndex2], m_fMatrices[m_iCurrentMatrices[iNodeIndex2]][iNodeIndex2],
                        m_fPartials[m_iCurrentPartials[iNodeIndex3]][iNodeIndex3], iMatrixMap, iFrom, iTo);
            } else {
                calculateStatesPartialsPruning(
                        m_iStates[iNodeIndex1], m_fMatrices[m_iCurrentMatrices[iNodeIndex1]][iNodeIndex1],
                        m_fPartials[m_iCurrentPartials[iNodeIndex2]][iNodeIndex2], m_fMatrices[m_iCurrentMatrices[iNodeIndex2]][iNodeIndex2],
                        m_fPartials[m_iCurrentPartials[iNodeIndex3]][iNodeIndex3], iMatrixMap, iFrom, iTo);
            }
        } else {
            if (m_iStates[iNodeIndex2] != null) {
                calculateStatesPartialsPruning(
                        m_iStates[iNodeIndex2], m_fMatrices[m_iCurrentMatrices[iNodeIndex2]][iNodeIndex2],
                        m_fPartials[m_iCurrentPartials[iNodeIndex1]][iNodeIndex1], m_fMatrices[m_iCurrentMatrices[iNodeIndex1]][iNodeIndex1],
                        m_fPartials[m_iCurrentPartials[iNodeIndex3]][iNodeIndex3], iMatrixMap, iFrom, iTo);
            } else {
                calculatePartialsPartialsPruning(
                        m_fPartials[m_iCurrentPartials[iNodeIndex1]][iNodeIndex1], m_fMatrices[m_iCurrentMatrices[iNodeIndex1]][iNodeIndex1],
                        m_fPartials[m_iCurrentPartials[iNodeIndex2]][iNodeIndex2], m_fMatrices[m_iCurrentMatrices[iNodeIndex2]][iNodeIndex2],
                        m_fPartials[m_iCurrentPartials[iNodeIndex3]][iNodeIndex3], iMatrixMap, iFrom, iTo);
            }
        }

        if (m_bUseScaling) {
            scalePartials(iNodeIndex3, iFrom, iTo);
        }
    }



    public void integratePartials(int iNodeIndex, double[] fProportions, int iThread, int iFrom, int iTo) {
    	double [] fOutPartials = m_fRootPartials[iThread];
        calculateIntegratePartials(m_fPartials[m_iCurrentPartials[iNodeIndex]][iNodeIndex], fProportions, fOutPartials, iFrom, iTo);
    }


    /**
     * Scale the partials at a given node. This uses a scaling suggested by Ziheng Yang in
     * Yang (2000) J. Mol. Evol. 51: 423-432
     * <p/>
     * This function looks over the partial likelihoods for each state at each pattern
     * and finds the largest. If this is less than the scalingThreshold (currently set
     * to 1E-40) then it rescales the partials for that pattern by dividing by this number
     * (i.e., normalizing to between 0, 1). It then stores the log of this scaling.
     * This is called for every internal node after the partials are calculated so provides
     * most of the performance hit. Ziheng suggests only doing this on a proportion of nodes
     * but this sounded like a headache to organize (and he doesn't use the threshold idea
     * which improves the performance quite a bit).
     *
     * @param iNodeIndex
     */
    protected void scalePartials(int iNodeIndex, int iFrom, int iTo) {
//        int v = 0;
//    	double [] fPartials = m_fPartials[m_iCurrentPartialsIndices[iNodeIndex]][iNodeIndex];
//        for (int i = 0; i < m_nPatternCount; i++) {
//            for (int k = 0; k < m_nMatrixCount; k++) {
//                for (int j = 0; j < m_nStateCount; j++) {
//                	fPartials[v] *= SCALE;
//                	v++;
//                }
//            }
//        }
        int u = m_nStates * iFrom;

        for (int i = iFrom; i < iTo; i++) {

            double scaleFactor = 0.0;
            int v = u;
            for (int k = 0; k < m_nMatrices; k++) {
                for (int j = 0; j < m_nStates; j++) {
                    if (m_fPartials[m_iCurrentPartials[iNodeIndex]][iNodeIndex][v] > scaleFactor) {
                        scaleFactor = m_fPartials[m_iCurrentPartials[iNodeIndex]][iNodeIndex][v];
                    }
                    v++;
                }
                v += (m_nPatterns - 1) * m_nStates;
            }

            if (scaleFactor < m_fScalingThreshold) {

                v = u;
                for (int k = 0; k < m_nMatrices; k++) {
                    for (int j = 0; j < m_nStates; j++) {
                        m_fPartials[m_iCurrentPartials[iNodeIndex]][iNodeIndex][v] /= scaleFactor;
                        v++;
                    }
                    v += (m_nPatterns - 1) * m_nStates;
                }
                m_fScalingFactors[m_iCurrentPartials[iNodeIndex]][iNodeIndex][i] = Math.log(scaleFactor);

            } else {
                m_fScalingFactors[m_iCurrentPartials[iNodeIndex]][iNodeIndex][i] = 0.0;
            }
            u += m_nStates;


        }
    }

    /**
     * This function returns the scaling factor for that pattern by summing over
     * the log scalings used at each node. If scaling is off then this just returns
     * a 0.
     *
     * @return the log scaling factor
     */
    public double getLogScalingFactor(int iPattern) {
//    	if (m_bUseScaling) {
//    		return -(m_nNodeCount/2) * Math.log(SCALE);
//    	} else {
//    		return 0;
//    	}        
    	double logScalingFactor = 0.0;
        if (m_bUseScaling) {
            for (int i = 0; i < m_nNodes; i++) {
                logScalingFactor += m_fScalingFactors[m_iCurrentPartials[i]][i][iPattern];
            }
        }
        return logScalingFactor;
    }

    /**
     * Gets the partials for a particular node.
     *
     * @param iNodeIndex   the node
     * @param fOutPartials an array into which the partials will go
     */
    public void getPartials(int iNodeIndex, double[] fOutPartials) {
        double[] partials1 = m_fPartials[m_iCurrentPartials[iNodeIndex]][iNodeIndex];

        System.arraycopy(partials1, 0, fOutPartials, 0, m_nPartialsSize);
    }

    /**
     * Store current state
     */
	@Override
	public void restore() {
        // Rather than copying the stored stuff back, just swap the pointers...
        int[] iTmp1 = m_iCurrentMatrices;
        m_iCurrentMatrices = m_iStoredMatrices;
        m_iStoredMatrices = iTmp1;

        int[] iTmp2 = m_iCurrentPartials;
        m_iCurrentPartials = m_iStoredPartials;
        m_iStoredPartials = iTmp2;
    }

    public void unstore() {
        System.arraycopy(m_iStoredMatrices, 0, m_iCurrentMatrices, 0, m_nNodes);
        System.arraycopy(m_iStoredPartials, 0, m_iCurrentPartials, 0, m_nNodes);
    }
    
    /**
     * Restore the stored state
     */
	@Override
	public void store() {
        System.arraycopy(m_iCurrentMatrices, 0, m_iStoredMatrices, 0, m_nNodes);
        System.arraycopy(m_iCurrentPartials, 0, m_iStoredPartials, 0, m_nNodes);
    }


	@Override
	public double calcPartialLogP(int iFrom, int iTo) {
        double logP = 0.0;
//        if (m_bAscertainedSitePatterns) {
//        	return 0;
////            double ascertainmentCorrection = ((AscertainedAlignment)m_data.get()).getAscertainmentCorrection(m_fPatternLogLikelihoods);
////            for (int i = iFrom; i < iTo; i++) {
////            	logP += (m_fPatternLogLikelihoods[i] - ascertainmentCorrection) * m_data.get().getPatternWeight(i);
////            }
//        } else {
            for (int i = iFrom; i < iTo; i++) {
	            logP += m_fPatternLogLikelihoods[i] * weights[i];
	        }
//        }
        return logP;
	}

	
	@Override
	public double [] getPatternLogLikelihoods() {
		return m_fPatternLogLikelihoods;
	}


	//@Override
	void calcInvarCorrection(double fProportionInvariant, int iThread) {
    	double [] fRootPartials = m_fRootPartials[iThread];
    	for (int i : m_iConstantPattern) {
    		if (fRootPartials[i] != 0) {
    			fRootPartials[i] += fProportionInvariant;
    		}
    	}
	}


	@Override
	double calcLogP(int iThread, 
			int [] cacheNode1, int [] cacheNode2, int [] cacheNode3, int cacheNodeCount, 
			int iFrom, int iTo,	int rootNodeNr,
			double[] proportions, double fProportionInvariant,
			double[] frequencies) {
		calculateAllPartials(cacheNode1, cacheNode2, cacheNode3, cacheNodeCount, iFrom, iTo);
        // process root of the beast.tree -
        integratePartials(rootNodeNr, proportions, iThread, iFrom, iTo);

        if (m_iConstantPattern != null) { // && !SiteModel.g_bUseOriginal) {
        	//double [] fRootPartials = m_fRootPartials[m_iThread];
        	// some portion of sites is invariant, so adjust root partials for this
        	calcInvarCorrection(fProportionInvariant, iThread);
        }
        calculateLogLikelihoods(iThread, frequencies, iFrom, iTo);
        double logP = calcPartialLogP(iFrom, iTo);
        return logP;
	}

//	@Override
//    public void calcRootPsuedoRootPartials(double[] fFrequencies, int iNode, double [] fPseudoPartials) {
//		int u = 0;
//		double [] fInPartials = m_fPartials[m_iCurrentPartials[iNode]][iNode];
//		for (int k = 0; k < m_nPatterns; k++) {
//			for (int l = 0; l < m_nMatrices; l++) {
//				for (int i = 0; i < m_nStates; i++) {
//					fPseudoPartials[u] = fInPartials[u] * fFrequencies[i];
//					u++;
//				}
//			}
//		}
//    }
//	@Override
//    public void calcNodePsuedoRootPartials(double[] fInPseudoPartials, int iNode, double [] fOutPseudoPartials) {
//		double [] fPartials = m_fPartials[m_iCurrentPartials[iNode]][iNode];
//		double [] fOldPartials = m_fPartials[m_iStoredPartials[iNode]][iNode];
//		int nMaxK = m_nPatterns * m_nMatrices * m_nStates; 
//		for (int k = 0; k < nMaxK; k++) {
//			fOutPseudoPartials[k] = fInPseudoPartials[k] * fPartials[k] / fOldPartials[k];
//		}
//	}
//    
//	@Override
//    public void calcPsuedoRootPartials(double [] fParentPseudoPartials, int iNode, double [] fPseudoPartials) {
//		int v = 0;
//		int u = 0;
//		double [] fMatrices = m_fMatrices[m_iCurrentMatrices[iNode]][iNode];
//		for (int k = 0; k < m_nPatterns; k++) {
//			for (int l = 0; l < m_nMatrices; l++) {
//				for (int i = 0; i < m_nStates; i++) {
//					int w = 0;
//					double fSum = 0;
//					for (int j = 0; j < m_nStates; j++) {
//					      fSum += fParentPseudoPartials[u+j] * fMatrices[w + i];
//					      w+=m_nStates;
//					}
//					fPseudoPartials[v] = fSum;
//					v++;
////					int w = l * m_nMatrixSize;
////					double fSum = 0;
////					for (int j = 0; j < m_nStates; j++) {
////					      fSum += fParentPseudoPartials[u+j] * fMatrices[w+j];
////					}
////					fPseudoPartials[v] = fSum;
////					v++;
//				}
//				u += m_nStates;
//			}
//		}
//    }
//
//
//    @Override
//    void integratePartialsP(double [] fInPartials, double [] fProportions, double [] m_fRootPartials, int iFrom, int iTo) {
//		int nMaxK = m_nPatterns * m_nStates;
//		for (int k = 0; k < nMaxK; k++) {
//			m_fRootPartials[k] = fInPartials[k] * fProportions[0];
//		}
//
//		for (int l = 1; l < m_nMatrices; l++) {
//			int n = nMaxK * l;
//			for (int k = 0; k < nMaxK; k++) {
//				m_fRootPartials[k] += fInPartials[n+k] * fProportions[l];
//			}
//		}
//    } // integratePartials
//
//	/**
//	 * Calculates pattern log likelihoods at a node.
//	 * @param fPartials the partials used to calculate the likelihoods
//	 * @param fFrequencies an array of state frequencies
//	 * @param fOutLogLikelihoods an array into which the likelihoods will go
//	 */
//    @Override
//	public void calculateLogLikelihoodsP(double[] fPartials,double[] fOutLogLikelihoods, int iFrom, int iTo)
//	{
//        int v = m_nStates * iFrom;
//		for (int k = iFrom; k < iTo; k++) {
//            double sum = 0.0;
//			for (int i = 0; i < m_nStates; i++) {
//				sum += fPartials[v];
//				v++;
//			}
//            fOutLogLikelihoods[k] = Math.log(sum) + getLogScalingFactor(k);
//		}
//	}
//	
//	
//	//    @Override
////    LikelihoodCore feelsGood() {return null;}
} // class BeerLikelihoodCore
