void gcm::TsaiHillFailureCriterion::checkFailure(ICalcNode& node, const float tau) {
	if( node.isDestroyed() )
		return;
	MaterialPtr mat = node.getMaterial();
	auto props = mat->getFailureProperties();
	int dir = props[FAILURE_TYPE_HASHIN][FAILURE_TYPE_HASHIN_DIR];
	real Xc = props[FAILURE_TYPE_HASHIN][FAILURE_TYPE_HASHIN_XC];
	real Xt = props[FAILURE_TYPE_HASHIN][FAILURE_TYPE_HASHIN_XT];
	real Yc = props[FAILURE_TYPE_HASHIN][FAILURE_TYPE_HASHIN_YC];
	real Yt = props[FAILURE_TYPE_HASHIN][FAILURE_TYPE_HASHIN_YT];
	real St = props[FAILURE_TYPE_HASHIN][FAILURE_TYPE_HASHIN_ST];
	real S = props[FAILURE_TYPE_HASHIN][FAILURE_TYPE_HASHIN_S];
	gcm::real s11, s12, s13, s22, s23, s33;
	switch (dir)
	{
		case 1:
			s11 = node.sxx;
			s12 = node.sxy;
			s13 = node.sxz;
			s22 = node.syy;
			s23 = node.syz;
			s33 = node.szz;
			break;
		case 2:
			s11 = node.syy;
			s12 = node.sxy;
			s13 = node.syz;
			s22 = node.sxx;
			s23 = node.sxz;
			s33 = node.szz;
			break;
		case 3:
			s11 = node.szz;
			s12 = node.sxz;
			s13 = node.syz;
			s22 = node.sxx;
			s23 = node.sxy;
			s33 = node.syy;
			break;
		default:
			return;
	}
	gcm::real X = (Xc + Xt)/2.0,
		Y = (Yc + Yt)/2.0,
		Z = Y,
		F = (1/Y/Y + 1/Z/Z - 1/X/X)/2.0,
		G = (1/Z/Z + 1/X/X - 1/Y/Y)/2.0,
		H = (1/X/X + 1/Y/Y - 1/Z/Z)/2.0,
		L = 1/St/St/2.0,
		M = 1/S/S/2.0,
		N = M;
	if ( (F*(s22-s33)*(s22-s33) + G*(s33-s11)*(s33-s11) + H*(s11-s22)*(s11-s22) + 2*L*s23*s23 + 2*M*s12*s12 + 2*N*s13*s13) >= 1.0 )
	{
		node.setDestroyed(true);
		//node.setDamageMeasure(4);
	}
}
void gcm::AnisotropicMatrix3DAnalytical::createAx(const ICalcNode& node)
{
    clear();

#ifdef NDEBUG
    auto mat = static_cast<IAnisotropicElasticMaterial*> (node.getMaterial());
#else
    auto mat = dynamic_cast<IAnisotropicElasticMaterial*> (node.getMaterial());
    assert_true(mat);
#endif

	auto rho = node.getRho();
	auto C = mat->getParameters();

	// Setting values of A
	A(0,3) = A(1,8) = A(2,7) = -1.0/rho;

	A(3,0) = -C.c11;	A(3,1) = -C.c16;	A(3,2) = -C.c15;
	A(4,0) = -C.c12;	A(4,1) = -C.c26;	A(4,2) = -C.c25;
	A(5,0) = -C.c13;	A(5,1) = -C.c36;	A(5,2) = -C.c35;
	A(6,0) = -C.c14;	A(6,1) = -C.c46;	A(6,2) = -C.c45;
	A(7,0) = -C.c15;	A(7,1) = -C.c56;	A(7,2) = -C.c55;
	A(8,0) = -C.c16;	A(8,1) = -C.c66;	A(8,2) = -C.c56;

	ThirdDegreePolynomial tdp (rho, C, 0);
	double roots[3];
	tdp.getRoots(roots);
	MatrixInverter matInv;
	
	if ( ! tdp.isMultiple() ) {
		// Search eigenvalues and filling the diagonal matrix
		L(0,0) = sqrt(roots[0]);
		L(1,1) = -L(0,0);
		L(2,2) = sqrt(roots[1]);
		L(3,3) = -L(2,2);
		L(4,4) = sqrt(roots[2]);
		L(5,5) = -L(4,4);

		// Search eigenvectors and filling the transition matrix
		// (  A = U1 * L * U  and so eigenvectors are columns of the U1  )
		double eigenVec[9];
		for (int i = 0; i < 6; i++) {
			findEigenVec(eigenVec, sqrt(roots[i/2]) * ( (i%2) ? -1 : 1 ), rho, C, 0);
			U1.setColumn(eigenVec, i);
			matInv.setColumn(eigenVec, i);
		}
	} else {
		// Search eigenvalues and filling the diagonal matrix
		// We hope L is diagonizable
		L(0, 0) = sqrt(roots[0]);
		L(1, 1) = -L(0, 0);
		L(2, 2) = L(3, 3) = sqrt(roots[1]);
		L(4, 4) = L(5, 5) = - sqrt(roots[1]);
		// Search eigenvectors and filling the transition matrix
		// (  A = U1 * L * U  and so eigenvectors are columns of the U1  )
		double eigenVec1[9];
		double eigenVec2[9];
		for (int i = 0; i < 2; i++) {
			findEigenVec(eigenVec1, sqrt(roots[0]) * ( (i%2) ? -1 : 1 ), rho, C, 0);
			U1.setColumn(eigenVec1, i);
			matInv.setColumn(eigenVec1, i);
		}
		for (int i = 2; i < 5; i+=2) {
			findEigenVec(eigenVec1, eigenVec2, sqrt(roots[1]) * ( ((i/2)%2) ? 1 : -1 ), rho, C, 0);
			U1.setColumn(eigenVec1, i);
			matInv.setColumn(eigenVec1, i);
			U1.setColumn(eigenVec2, i+1);
			matInv.setColumn(eigenVec2, i+1);
		}
	}

	U1(4,6) = U1(5,7) = U1(6,8) = 1;
	matInv.setUnity(4,6, 5,7, 6,8);

	// Search U = U1^(-1)
	matInv.inv(U);
	
	fixValuesOrder();
};
void CrackResponseFailedMaterialCorrector::applyCorrection(ICalcNode& node, const float tau) {
    node.exciseByCrack();
}
void PuckFailureCriterion::checkFailure(ICalcNode& node, const float tau) {
    if( node.isDestroyed() )
        return;
    MaterialPtr mat = node.getMaterial();
    auto props = mat->getFailureProperties();
    int dir = props[FAILURE_TYPE_HASHIN][FAILURE_TYPE_HASHIN_DIR];
    real Xc = props[FAILURE_TYPE_HASHIN][FAILURE_TYPE_HASHIN_XC];
    real Xt = props[FAILURE_TYPE_HASHIN][FAILURE_TYPE_HASHIN_XT];
    real Yc = props[FAILURE_TYPE_HASHIN][FAILURE_TYPE_HASHIN_YC];
    real Yt = props[FAILURE_TYPE_HASHIN][FAILURE_TYPE_HASHIN_YT];
    real St = props[FAILURE_TYPE_HASHIN][FAILURE_TYPE_HASHIN_ST];
    real S  = props[FAILURE_TYPE_HASHIN][FAILURE_TYPE_HASHIN_S];
    real s11, s12, s13, s22, s23, s33;
    int dir1 = 0, dir2 = 0;
    switch (dir)
    {
    case 1:
	s11 = node.sxx;
	s12 = node.sxy;
	s13 = node.sxz;
	s22 = node.syy;
	s23 = node.syz;
	s33 = node.szz;
	dir1 = 1;
	dir2 = 2;
	break;
    case 2:   
        s11 = node.syy;
        s12 = node.syz;
        s13 = node.sxy;
        s22 = node.szz;
        s23 = node.sxz;
        s33 = node.sxx;
	dir1 = 2;
	dir2 = 0;
	break;
    case 3:
        s11 = node.szz;
        s12 = node.sxz;
        s13 = node.syz;
        s22 = node.sxx;
        s23 = node.sxy;
        s33 = node.syy;
	dir1 = 0;
	dir2 = 1;
        break;
    default:
		return;
    }

    real m_s[3] = {0};
//FF
    if (s11 > 0 && s11/Xt - 1 > 0)
    {
	m_s[dir-1] = 1;
        node.createCrack(m_s[0],m_s[1],m_s[2]);
        node.setDestroyed(true);
	node.setDamageMeasure(1);
	return;
    }
    if (s11 < 0 && -s11/Xc - 1 > 0)
    {
        m_s[dir-1] = 1;
        node.createCrack(m_s[0],m_s[1],m_s[2]);
        node.setDestroyed(true);
	node.setDamageMeasure(2);
        return;
    }
//IFF
    real	RATII = S,
		pTII_minus = 0.000003,
		pTII_plus = 0.000003,
		pTT_minus = 0.000003,
		pTT_plus = 0.000003,
		RATT = Yc/((1+pTT_minus)),
		RAT_plus = Yt;
//--- Mode A
 /*   real	f = sqrt((s23/RATII)*(s23/RATII) + (1-pTII_plus*RAT_plus/RATII)*(1-pTII_plus*RAT_plus/RATII) * (s22/RAT_plus)*(s22/RAT_plus)) + pTII_plus*s22/RATII - 1;
    if (s22 > 0 && f > 0)
    {
	m_s[dir1] = 1;
        node.createCrack(m_s[0],m_s[1],m_s[2]);
       	node.setDestroyed(true);
	node.setDamageMeasure(3);
	return;
    }
//--- Mode B
    f = sqrt((s23/RATII)*(s23/RATII) - (pTII_plus/RATII)*(pTII_plus/RATII)*s22*s22) + (pTII_plus/RATII)*s22 - 1;
    if (s22 < 0 && f > 0)
    {
        m_s[dir2] = 1;
        node.createCrack(m_s[0],m_s[1],m_s[2]);
        node.setDestroyed(true);
	node.setDamageMeasure(4);
        return;
    }*/
//--- Mode C
    real 	s1 = (s22+s33)/2 + sqrt(((s22-s33)/2)*((s22-s33)/2) + s23*s23),
		s2 = (s22+s33)/2 - sqrt(((s22-s33)/2)*((s22-s33)/2) + s23*s23);
    real	t12 = (s1-s2)/2;
    real 	cosO = sqrt(((t12/s2)*(t12/s2) * (RATT/S)*(RATT/S) + 1)/(2*(1+pTT_minus)));
    if (s23 > 0) cosO = -cosO;	//plus
    //if (s23 < 0) cosO = -cosO;  //minus
    real	sn = s22*cosO*cosO + s33*(1-cosO*cosO) + 2*s23*cosO*sqrt(1-cosO*cosO),
		tnt = (s33-s22)*cosO*sqrt(1-cosO*cosO) + s23*(2*cosO*cosO - 1),
		tnl = s13*sqrt(1-cosO*cosO) + s12*cosO;
    real	cos2psi = tnt*tnt/(tnt*tnt + tnl*tnl),
		sin2psi = tnl*tnl/(tnt*tnt + tnl*tnl);
    real	pR_plus = pTT_plus*cos2psi/RATT + pTII_plus*sin2psi/RATII,
		pR_minus = pTT_minus*cos2psi/RATT + pTII_minus*sin2psi/RATII;
    real	f_plus = sqrt(( (1/RAT_plus-pR_plus)*sn )*( (1/RAT_plus-pR_plus)*sn ) + (tnt/RATT)*(tnt/RATT) + (tnl/RATII)*(tnl/RATII)) + pR_plus*sn - 1,
		f_minus = sqrt((tnt/RATT)*(tnt/RATT) + (tnl/RATII)*(tnl/RATII) + (pR_minus*sn)*(pR_minus*sn)) + pR_minus*sn - 1;
    if (sn >= 0 && f_plus > 0) 
    {
	m_s[dir] = 0;
	m_s[dir1] = cosO;
	m_s[dir2] = sqrt(1 - cosO*cosO);
        node.createCrack(m_s[0],m_s[1],m_s[2]);
        node.setDestroyed(true);
	node.setDamageMeasure(5);
        return;
    }
    if (sn < 0 && f_minus > 0)
    {   
        m_s[dir] = 0;
        m_s[dir1] = cosO;
        m_s[dir2] = sqrt(1 - cosO*cosO);
        node.createCrack(m_s[0],m_s[1],m_s[2]);
        node.setDestroyed(true);
	node.setDamageMeasure(6);
        return;
    }

}