/** Multiplies tensor A, on the left by it's transpose, A^T to give A^T * A 
And returns the answer in a symmetric tensor*/
void TensorArray_MultiplyByLeftTranspose(TensorArray tensor, Dimension_Index dim, SymmetricTensor result) {

	TensorArray tensorTranspose;
	TensorArray fullTensorResult;

	TensorArray_Transpose( tensor, dim, tensorTranspose);
	TensorArray_MultiplyByTensorArray( tensorTranspose, tensor, dim, fullTensorResult );
	
	/** Answer is automatically a symmetric tensor by definition */	
	TensorArray_GetSymmetricPart( fullTensorResult, dim, result);
	return;
}
void DruckerPrager_Extra_GetSlipVector(void* druckerPrager_Extra,
                                       ConstitutiveMatrix*              constitutiveMatrix,
                                       MaterialPointsSwarm*             materialPointsSwarm,
                                       Element_LocalIndex               lElement_I,
                                       MaterialPoint*                   materialPoint,
                                       Coord                            xi,
                                       double dpFrictionCoefficient) {

    DruckerPrager_Extra*                    self             = (DruckerPrager_Extra*) druckerPrager_Extra;
    DruckerPrager_Extra_Particle* particleExt;
    double                              currentVelocityGradient[9];
    Eigenvector                         eigenvectorList[3];
    double                               strainRate_ns[2];
    SymmetricTensor								 currentStrainRate;
    XYZ                                  normal[2];
    XYZ                                  slip[2];
    double                               theta;
    int                                  favourablePlane, err;
    Dimension_Index								dim = constitutiveMatrix->dim;

    /* stupid way to recover the integration point from the materialPoint
     *     * TODO: FIXCONSTITUTIVE use integration point only */
    IntegrationPoint* integrationPoint = (IntegrationPoint*) Swarm_ParticleAt( constitutiveMatrix->integrationSwarm,
                                         constitutiveMatrix->currentParticleIndex );

    particleExt = (DruckerPrager_Extra_Particle*) ExtensionManager_Get( materialPointsSwarm->particleExtensionMgr, materialPoint, self->particleExtHandle );

    /* get velocity Gradients */
    err = PpcManager_Get( self->mgr, lElement_I, integrationPoint, self->velocityGradientsTag, currentVelocityGradient );
    if( err ) assert(0);
    TensorArray_GetSymmetricPart( currentVelocityGradient, dim, currentStrainRate );
    /* Slip vector does not have anything to do with the constitutive behaviour since isotropic so should try and find max shear strain rate by calculating eigenvectors of strainrate */
    SymmetricTensor_CalcAllEigenvectors( currentStrainRate, dim, eigenvectorList );

    /* Since isotropic, the max shear stress always lies at theta = 45 degrees on mohr circle, so to for strain rate? */
    theta = M_PI / 4.0;

    if (dim == 2) {

        /* Identify potential failure directions */
        StGermain_RotateCoordinateAxis( slip[0],   eigenvectorList[0].vector, K_AXIS, +theta);
        StGermain_RotateCoordinateAxis( slip[1],   eigenvectorList[0].vector, K_AXIS, -theta);
        StGermain_RotateCoordinateAxis( normal[0], eigenvectorList[0].vector, K_AXIS,  0.5*M_PI + theta);
        StGermain_RotateCoordinateAxis( normal[1], eigenvectorList[0].vector, K_AXIS,  0.5*M_PI - theta);
    }
    else {

        /* Identify potential failure directions */
        StGermain_RotateVector( slip[0],   eigenvectorList[0].vector, eigenvectorList[1].vector, + theta );
        StGermain_RotateVector( slip[1],   eigenvectorList[0].vector, eigenvectorList[1].vector, - theta );
        StGermain_RotateVector( normal[0], eigenvectorList[0].vector, eigenvectorList[1].vector,  0.5*M_PI + theta);
        StGermain_RotateVector( normal[1], eigenvectorList[0].vector, eigenvectorList[1].vector,  0.5*M_PI - theta);
    }

    /* Resolve shear strain-rate for the potential failure planes */
    strainRate_ns[0] = fabs(TensorArray_MultiplyByVectors( currentVelocityGradient, slip[0], normal[0], dim ));
    strainRate_ns[1] = fabs(TensorArray_MultiplyByVectors( currentVelocityGradient, slip[1], normal[1], dim ));

    /* Choose the plane which is oriented favorably for continued slip */


    favourablePlane = (strainRate_ns[0] > strainRate_ns[1] ? 0 : 1);

    memcpy( particleExt->slip,   slip[favourablePlane],   dim * sizeof(double) );

    particleExt->slipRate = strainRate_ns[ favourablePlane ];

}