/********************************************************
 *  ██████╗  ██████╗████████╗██╗
 * ██╔════╝ ██╔════╝╚══██╔══╝██║
 * ██║  ███╗██║        ██║   ██║
 * ██║   ██║██║        ██║   ██║
 * ╚██████╔╝╚██████╗   ██║   ███████╗
 *  ╚═════╝  ╚═════╝   ╚═╝   ╚══════╝
 * Geophysical Computational Tools & Library (GCTL)
 *
 * Copyright (c) 2023  Yi Zhang (yizhang-geo@zju.edu.cn)
 *
 * GCTL is distributed under a dual licensing scheme. You can redistribute 
 * it and/or modify it under the terms of the GNU Lesser General Public 
 * License as published by the Free Software Foundation, either version 2 
 * of the License, or (at your option) any later version. You should have 
 * received a copy of the GNU Lesser General Public License along with this 
 * program. If not, see <http://www.gnu.org/licenses/>.
 * 
 * If the terms and conditions of the LGPL v.2. would prevent you from using 
 * the GCTL, please consider the option to obtain a commercial license for a 
 * fee. These licenses are offered by the GCTL's original author. As a rule, 
 * licenses are provided "as-is", unlimited in time for a one time fee. Please 
 * send corresponding requests to: yizhang-geo@zju.edu.cn. Please do not forget 
 * to include some description of your company and the realm of its activities. 
 * Also add information on how to contact you by electronic and paper mail.
 ******************************************************/

#include "shapefunc.h"

double gctl::linear_sf::line(double x, double x1, double x2, size_t idx, gradient_type_e vt)
{
#ifdef GCTL_CHECK_BOUNDER
    if (x1 >= x2)
    {
        throw length_error("Invalid definition range. From gctl::linear_sf::line(...)");
    }

    if (x < x1 || x > x2)
    {
        throw out_of_range("Invalid calculating point. From gctl::linear_sf::line(...)");
    }
#endif

    switch (10*idx + vt)
    {
        case 0:
            return (x2 - x)/(x2 - x1);
        case 1:
            return -1.0/(x2 - x1);
        case 10:
            return (x - x1)/(x2 - x1);
        case 11:
            return 1.0/(x2 - x1);
        default:
            throw runtime_error("Invalid calculating type. From gctl::linear_sf::line(...)");
    }
}

double gctl::linear_sf::quad(double x, double y, double x1, double x2, double y1, double y2, 
    size_t idx, gradient_type_e vt)
{
#ifdef GCTL_CHECK_BOUNDER
    if (x1 >= x2 || y1 >= y2)
    {
        throw length_error("Invalid definition range. From gctl::linear_sf::quad(...)");
    }

    if (x < x1 || x > x2 || y < y1 || y > y2)
    {
        throw out_of_range("Invalid calculating point. From gctl::linear_sf::quad(...)");
    }
#endif

    double square;
    switch (10*idx + vt)
    {
        case 0:
            square = (x2 - x1)*(y2 - y1);
            return (x - x2)*(y - y2)/square;
        case 1:
            square = (x2 - x1)*(y2 - y1);
            return (y - y2)/square;
        case 2:
            square = (x2 - x1)*(y2 - y1);
            return (x - x2)/square;
        case 10:
            square = (x2 - x1)*(y2 - y1);
            return -1.0*(x - x1)*(y - y2)/square;
        case 11:
            square = (x2 - x1)*(y2 - y1);
            return -1.0*(y - y2)/square;
        case 12:
            square = (x2 - x1)*(y2 - y1);
            return -1.0*(x - x1)/square;
        case 20:
            square = (x2 - x1)*(y2 - y1);
            return (x - x1)*(y - y1)/square;
        case 21:
            square = (x2 - x1)*(y2 - y1);
            return (y - y1)/square;
        case 22:
            square = (x2 - x1)*(y2 - y1);
            return (x - x1)/square;
        case 30:
            square = (x2 - x1)*(y2 - y1);
            return -1.0*(x - x2)*(y - y1)/square;
        case 31:
            square = (x2 - x1)*(y2 - y1);
            return -1.0*(y - y1)/square;
        case 32:
            square = (x2 - x1)*(y2 - y1);
            return -1.0*(x - x2)/square;
        default:
            throw runtime_error("Invalid calculating type. From gctl::linear_sf::quad(...)");
    }
}

double gctl::linear_sf::triangle(double x, double y, double x1, double x2, double x3, 
    double y1, double y2, double y3, size_t idx, gradient_type_e vt)
{
#ifdef GCTL_CHECK_BOUNDER
    if ((x2-x1)*(y3-y1)-(y2-y1)*(x3-x1) <= FEM_SF_ZERO) // 这里没有设置为0是为了检测三个点是否共线
    {
        throw length_error("Invalid definition range. From gctl::linear_sf::triangle(...)");
    }

    if ((x1-x)*(y2-y)-(y1-y)*(x2-x) < 0.0 || 
        (x2-x)*(y3-y)-(y2-y)*(x3-x) < 0.0 || 
        (x3-x)*(y1-y)-(y3-y)*(x1-x) < 0.0)
    {
        throw out_of_range("Invalid calculating point. From gctl::linear_sf::triangle(...)");
    }
#endif

    double a, b, c, d;
    switch (10*idx + vt)
    {
        case 0:
            d = (x2 - x3)*(y1 - y2) - (x1 - x2)*(y2 - y3);
            a = (y3 - y2)/d;
            b = (x2 - x3)/d;
            c = 1.0 - x1*a - y1*b;
            return a*x+b*y+c;
        case 1:
            d = (x2 - x3)*(y1 - y2) - (x1 - x2)*(y2 - y3);
            return (y3 - y2)/d;
        case 2:
            d = (x2 - x3)*(y1 - y2) - (x1 - x2)*(y2 - y3);
            return (x2 - x3)/d;
        case 10:
            d = (x3 - x1)*(y2 - y3) - (x2 - x3)*(y3 - y1);
            a = (y1 - y3)/d;
            b = (x3 - x1)/d;
            c = 1.0 - x2*a - y2*b;
            return a*x+b*y+c;
        case 11:
            d = (x3 - x1)*(y2 - y3) - (x2 - x3)*(y3 - y1);
            return (y1 - y3)/d;
        case 12:
            d = (x3 - x1)*(y2 - y3) - (x2 - x3)*(y3 - y1);
            return (x3 - x1)/d;
        case 20:
            d = (x1 - x2)*(y3 - y1) - (x3 - x1)*(y1 - y2);
            a = (y2 - y1)/d;
            b = (x1 - x2)/d;
            c = 1.0 - x3*a - y3*b;
            return a*x+b*y+c;
        case 21:
            d = (x1 - x2)*(y3 - y1) - (x3 - x1)*(y1 - y2);
            return (y2 - y1)/d;
        case 22:
            d = (x1 - x2)*(y3 - y1) - (x3 - x1)*(y1 - y2);
            return (x1 - x2)/d;
        default:
            throw runtime_error("Invalid calculating type. From gctl::linear_sf::triangle(...)");
    }
}

double gctl::linear_sf::triangle_interpolate(double x, double y, double x1, double x2, double x3, 
	double y1, double y2, double y3, double v1, double v2, double v3, gradient_type_e vt)
{
	double sum = triangle(x, y, x1, x2, x3, y1, y2, y3, 0, vt) * v1;
	sum += triangle(x, y, x1, x2, x3, y1, y2, y3, 1, vt) * v2;
	sum += triangle(x, y, x1, x2, x3, y1, y2, y3, 2, vt) * v3;
	return sum;
}

double gctl::linear_sf::cube(double x, double y, double z, double x1, double x2, 
        double y1, double y2, double z1, double z2, size_t idx, gradient_type_e vt)
{
#ifdef GCTL_CHECK_BOUNDER
    if (x1 >= x2 || y1 >= y2 || z1 >= z2)
    {
        throw length_error("Invalid definition range. From gctl::linear_sf::cube(...)");
    }

    if (x < x1 || x > x2 || y < y1 || y > y2 || z < z1 || z > z2)
    {
        throw out_of_range("Invalid calculating point. From gctl::linear_sf::cube(...)");
    }
#endif

    double vol;
    switch (10*idx + vt)
    {
        case 0:
            vol = (x2 - x1)*(y2 - y1)*(z2 - z1);
            return (x2 - x)*(y2 - y)*(z2 - z)/vol;
        case 1:
            vol = (x2 - x1)*(y2 - y1)*(z2 - z1);
            return -1.0*(y2 - y)*(z2 - z)/vol;
        case 2:
            vol = (x2 - x1)*(y2 - y1)*(z2 - z1);
            return -1.0*(x2 - x)*(z2 - z)/vol;
        case 3:
            vol = (x2 - x1)*(y2 - y1)*(z2 - z1);
            return -1.0*(x2 - x)*(y2 - y)/vol;
        case 10:
            vol = (x2 - x1)*(y2 - y1)*(z2 - z1);
            return (x - x1)*(y2 - y)*(z2 - z)/vol;
        case 11:
            vol = (x2 - x1)*(y2 - y1)*(z2 - z1);
            return (y2 - y)*(z2 - z)/vol;
        case 12:
            vol = (x2 - x1)*(y2 - y1)*(z2 - z1);
            return -1.0*(x - x1)*(z2 - z)/vol;
        case 13:
            vol = (x2 - x1)*(y2 - y1)*(z2 - z1);
            return -1.0*(x - x1)*(y2 - y)/vol;
        case 20:
            vol = (x2 - x1)*(y2 - y1)*(z2 - z1);
            return (x - x1)*(y - y1)*(z2 - z)/vol;
        case 21:
            vol = (x2 - x1)*(y2 - y1)*(z2 - z1);
            return (y - y1)*(z2 - z)/vol;
        case 22:
            vol = (x2 - x1)*(y2 - y1)*(z2 - z1);
            return (x - x1)*(z2 - z)/vol;
        case 23:
            vol = (x2 - x1)*(y2 - y1)*(z2 - z1);
            return -1.0*(x - x1)*(y - y1)/vol;
        case 30:
            vol = (x2 - x1)*(y2 - y1)*(z2 - z1);
            return (x2 - x)*(y - y1)*(z2 - z)/vol;
        case 31:
            vol = (x2 - x1)*(y2 - y1)*(z2 - z1);
            return -1.0*(y - y1)*(z2 - z)/vol;
        case 32:
            vol = (x2 - x1)*(y2 - y1)*(z2 - z1);
            return (y - y1)*(z2 - z)/vol;
        case 33:
            vol = (x2 - x1)*(y2 - y1)*(z2 - z1);
            return -1.0*(x2 - x)*(y - y1)/vol;
        case 40:
            vol = (x2 - x1)*(y2 - y1)*(z2 - z1);
            return (x2 - x)*(y2 - y)*(z - z1)/vol;
        case 41:
            vol = (x2 - x1)*(y2 - y1)*(z2 - z1);
            return -1.0*(y2 - y)*(z - z1)/vol;
        case 42:
            vol = (x2 - x1)*(y2 - y1)*(z2 - z1);
            return -1.0*(x2 - x)*(z - z1)/vol;
        case 43:
            vol = (x2 - x1)*(y2 - y1)*(z2 - z1);
            return (x2 - x)*(y2 - y)/vol;
        case 50:
            vol = (x2 - x1)*(y2 - y1)*(z2 - z1);
            return (x - x1)*(y2 - y)*(z - z1)/vol;
        case 51:
            vol = (x2 - x1)*(y2 - y1)*(z2 - z1);
            return (y2 - y)*(z - z1)/vol;
        case 52:
            vol = (x2 - x1)*(y2 - y1)*(z2 - z1);
            return -1.0*(x - x1)*(z - z1)/vol;
        case 53:
            vol = (x2 - x1)*(y2 - y1)*(z2 - z1);
            return (x - x1)*(y2 - y)/vol;
        case 60:
            vol = (x2 - x1)*(y2 - y1)*(z2 - z1);
            return (x - x1)*(y - y1)*(z - z1)/vol;
        case 61:
            vol = (x2 - x1)*(y2 - y1)*(z2 - z1);
            return (y - y1)*(z - z1)/vol;
        case 62:
            vol = (x2 - x1)*(y2 - y1)*(z2 - z1);
            return (x - x1)*(z - z1)/vol;
        case 63:
            vol = (x2 - x1)*(y2 - y1)*(z2 - z1);
            return (x - x1)*(y - y1)/vol;
        case 70:
            vol = (x2 - x1)*(y2 - y1)*(z2 - z1);
            return (x2 - x)*(y - y1)*(z - z1)/vol;
        case 71:
            vol = (x2 - x1)*(y2 - y1)*(z2 - z1);
            return -1.0*(y - y1)*(z - z1)/vol;
        case 72:
            vol = (x2 - x1)*(y2 - y1)*(z2 - z1);
            return (x2 - x)*(z - z1)/vol;
        case 73:
            vol = (x2 - x1)*(y2 - y1)*(z2 - z1);
            return (x2 - x)*(y - y1)/vol;
        default:
            throw runtime_error("Invalid calculating type. From gctl::linear_sf::cube(...)");
    }
}

double gctl::linear_sf::tetrahedron(double ksi, double eta, double zta, double x1, double x2, double x3, double x4, 
	double y1, double y2, double y3, double y4, double z1, double z2, double z3, double z4,  
	size_t idx, gradient_type_e vt)
{
	double J[9], invJ[9];
	J[0] = x2 - x1; J[1] = x3 - x1; J[2] = x4 - x1;
	J[3] = y2 - y1; J[4] = y3 - y1; J[5] = y4 - y1;
	J[6] = z2 - z1; J[7] = z3 - z1; J[8] = z4 - z1;
	if (!inverse3x3(&J[0], &invJ[0])) throw runtime_error("gctl::linear_sf::tetrahedron(...) got an invalid Det(J) value.");

	switch (10*idx + vt)
	{
		case 0:
			return 1.0 - ksi - eta - zta;
		case 1:
			return -1.0*(invJ[0] + invJ[3] + invJ[6]);
		case 2:
			return -1.0*(invJ[1] + invJ[4] + invJ[7]);
		case 3:
			return -1.0*(invJ[2] + invJ[5] + invJ[8]);
		case 10:
			return ksi;
		case 11:
			return invJ[0];
		case 12:
			return invJ[1];
		case 13:
			return invJ[2];
		case 20:
			return eta;
		case 21:
			return invJ[3];
		case 22:
			return invJ[4];
		case 23:
			return invJ[5];
		case 30:
			return zta;
		case 31:
			return invJ[6];
		case 32:
			return invJ[7];
		case 33:
			return invJ[8];
		default:
			throw invalid_argument("gctl::linear_sf::tetrahedron(...) got an invalid calculating type.");
			return 0;
	}
}

void gctl::linear_sf::local2global_tetrahedron(double &x, double &y, double &z, double ksi, double eta, double zta, 
	double x1, double x2, double x3, double x4, double y1, double y2, double y3, double y4, 
	double z1, double z2, double z3, double z4)
{
	if (ksi < 0.0 || ksi > 1.0 || eta < 0.0 || eta > 1.0 || zta < 0.0 || zta > 1.0)
	{
		throw runtime_error("gctl::linear_sf::local2global_tetrahedron(...) got an invalid local coordinate.");
	}

	x = (x2-x1)*ksi + (x3-x1)*eta + (x4-x1)*zta + x1;
	y = (y2-y1)*ksi + (y3-y1)*eta + (y4-y1)*zta + y1;
	z = (z2-z1)*ksi + (z3-z1)*eta + (z4-z1)*zta + z1;
	return;
}

void gctl::linear_sf::global2local_tetrahedron(double &ksi, double &eta, double &zta, double x, double y, double z,  
	double x1, double x2, double x3, double x4, double y1, double y2, double y3, double y4, 
	double z1, double z2, double z3, double z4)
{
	double J[9], invJ[9];
	J[0] = x2 - x1; J[1] = x3 - x1; J[2] = x4 - x1;
	J[3] = y2 - y1; J[4] = y3 - y1; J[5] = y4 - y1;
	J[6] = z2 - z1; J[7] = z3 - z1; J[8] = z4 - z1;
	if (!inverse3x3(&J[0], &invJ[0])) throw runtime_error("gctl::linear_sf::global2local_tetrahedron(...) got an invalid Det(J) value.");

	ksi = (x - x1)*invJ[0] + (y - y1)*invJ[1] + (z - z1)*invJ[2];
	eta = (x - x1)*invJ[3] + (y - y1)*invJ[4] + (z - z1)*invJ[5];  
	zta = (x - x1)*invJ[6] + (y - y1)*invJ[7] + (z - z1)*invJ[8];
	return;
}

double gctl::linear_sf::tetrahedron_interpolate(double x, double y, double z, double x1, double x2, 
    double x3, double x4, double y1, double y2, double y3, double y4, double z1, double z2, 
    double z3, double z4, double v1, double v2, double v3, double v4, gradient_type_e vt)
{
    double ksi, eta, zta;
    global2local_tetrahedron(ksi, eta, zta, x, y, z, x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4);

    double sum = tetrahedron(ksi, eta, zta, x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4, 0, vt) * v1;
	sum += tetrahedron(ksi, eta, zta, x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4, 1, vt) * v2;
	sum += tetrahedron(ksi, eta, zta, x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4, 2, vt) * v3;
    sum += tetrahedron(ksi, eta, zta, x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4, 3, vt) * v4;
	return sum;
}

void gctl::linear_sf::local2global_triprism(double &x, double &y, double &z, double ksi, double eta, double zta, 
	double *xs, double *ys, double *zs)
{
	if (ksi < 0.0 || ksi > 1.0 || eta < 0.0 || eta > 1.0 || zta < 0.0 || zta > 1.0)
	{
		throw runtime_error("gctl::linear_sf::local2global_triprism(...) got an invalid local coordinate.");
	}

	x = (1.0 - ksi - eta)*(1.0 - zta)*xs[0] + ksi*(1.0 - zta)*xs[1] + eta*(1.0 - zta)*xs[2] 
		+ (1.0 - ksi - eta)*zta*xs[3] + ksi*zta*xs[4] + eta*zta*xs[5];
	y = (1.0 - ksi - eta)*(1.0 - zta)*ys[0] + ksi*(1.0 - zta)*ys[1] + eta*(1.0 - zta)*ys[2] 
		+ (1.0 - ksi - eta)*zta*ys[3] + ksi*zta*ys[4] + eta*zta*ys[5];
	z = (1.0 - ksi - eta)*(1.0 - zta)*zs[0] + ksi*(1.0 - zta)*zs[1] + eta*(1.0 - zta)*zs[2] 
		+ (1.0 - ksi - eta)*zta*zs[3] + ksi*zta*zs[4] + eta*zta*zs[5];
	return;
}

double gctl::linear_sf::triprism(double ksi, double eta, double zta, double *xs, double *ys, double *zs, size_t idx, gradient_type_e vt)
{
	double J[9], invJ[9];
	J[0] = zta*(xs[0] - xs[1] - xs[3] + xs[4]) + xs[1] - xs[0];
	J[1] = zta*(xs[0] - xs[2] - xs[3] + xs[5]) + xs[2] - xs[0];
	J[2] = ksi*(xs[0] - xs[1] - xs[3] + xs[4]) + eta*(xs[0] - xs[2] - xs[3] + xs[5]) + xs[3] - xs[0];
	J[3] = zta*(ys[0] - ys[1] - ys[3] + ys[4]) + ys[1] - ys[0];
	J[4] = zta*(ys[0] - ys[2] - ys[3] + ys[5]) + ys[2] - ys[0];
	J[5] = ksi*(ys[0] - ys[1] - ys[3] + ys[4]) + eta*(ys[0] - ys[2] - ys[3] + ys[5]) + ys[3] - ys[0];
	J[6] = zta*(zs[0] - zs[1] - zs[3] + zs[4]) + zs[1] - zs[0];
	J[7] = zta*(zs[0] - zs[2] - zs[3] + zs[5]) + zs[2] - zs[0];
	J[8] = ksi*(zs[0] - zs[1] - zs[3] + zs[4]) + eta*(zs[0] - zs[2] - zs[3] + zs[5]) + zs[3] - zs[0];
	if (!inverse3x3(&J[0], &invJ[0])) throw runtime_error("gctl::linear_sf::triprism(...) got an invalid Det(J) value.");

	switch (10*idx + vt)
	{
		case 0:
			return (1.0 - ksi - eta)*(1.0 - zta);
		case 1:
			return (zta - 1.0)*invJ[0] + (zta - 1.0)*invJ[3] + (ksi + eta - 1.0)*invJ[6];
		case 2:
			return (zta - 1.0)*invJ[1] + (zta - 1.0)*invJ[4] + (ksi + eta - 1.0)*invJ[7];
		case 3:
			return (zta - 1.0)*invJ[2] + (zta - 1.0)*invJ[5] + (ksi + eta - 1.0)*invJ[8];
		case 10:
			return ksi*(1.0 - zta);
		case 11:
			return (1.0 - zta)*invJ[0] - ksi*invJ[6];
		case 12:
			return (1.0 - zta)*invJ[1] - ksi*invJ[7];
		case 13:
			return (1.0 - zta)*invJ[2] - ksi*invJ[8];
		case 20:
			return eta*(1.0 - zta);
		case 21:
			return (1.0 - zta)*invJ[3] - eta*invJ[6];
		case 22:
			return (1.0 - zta)*invJ[4] - eta*invJ[7];
		case 23:
			return (1.0 - zta)*invJ[5] - eta*invJ[8];
		case 30:
			return (1.0 - ksi - eta)*zta;
		case 31:
			return -1.0*zta*invJ[0] - zta*invJ[3] + (1.0 - ksi - eta)*invJ[6];
		case 32:
			return -1.0*zta*invJ[1] - zta*invJ[4] + (1.0 - ksi - eta)*invJ[7];
		case 33:
			return -1.0*zta*invJ[2] - zta*invJ[5] + (1.0 - ksi - eta)*invJ[8];
		case 40:
			return ksi*zta;
		case 41:
			return zta*invJ[0] + ksi*invJ[6];
		case 42:
			return zta*invJ[1] + ksi*invJ[7];
		case 43:
			return zta*invJ[2] + ksi*invJ[8];
		case 50:
			return eta*zta;
		case 51:
			return zta*invJ[3] + eta*invJ[6];
		case 52:
			return zta*invJ[4] + eta*invJ[7];
		case 53:
			return zta*invJ[5] + eta*invJ[8];
		default:
			throw std::invalid_argument("gctl::linear_sf::triprism(...) got an invalid calculating type.");
			return 0;
	}
}

void gctl::linear_esf::triangle(double x, double y, double x1, double x2, double x3, 
    double y1, double y2, double y3, size_t idx, eshape_value_e vt, edge_orient_e ot, double &xval, double &yval)
{
    // Assign output values to defaults
    xval = yval = 0.0;

    double len;
    if (idx == 0) len = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
    else if (idx == 1) len = sqrt((x3-x2)*(x3-x2) + (y3-y2)*(y3-y2));
    else if (idx == 2) len = sqrt((x3-x1)*(x3-x1) + (y3-y1)*(y3-y1));
    else throw std::invalid_argument("gctl::linear_esf::triangle(...) got an invalid calculating index.");

    gctl::linear_sf lsf;
    if (vt == VecValue)
    {
        xval = len*(lsf.triangle(x,y,x1,x2,x3,y1,y2,y3,idx,gctl::Value)*lsf.triangle(x,y,x1,x2,x3,y1,y2,y3,(idx+1)%3,gctl::Dx)
                - lsf.triangle(x,y,x1,x2,x3,y1,y2,y3,(idx+1)%3,gctl::Value)*lsf.triangle(x,y,x1,x2,x3,y1,y2,y3,idx,gctl::Dx));

        yval = len*(lsf.triangle(x,y,x1,x2,x3,y1,y2,y3,idx,gctl::Value)*lsf.triangle(x,y,x1,x2,x3,y1,y2,y3,(idx+1)%3,gctl::Dy) 
                - lsf.triangle(x,y,x1,x2,x3,y1,y2,y3,(idx+1)%3,gctl::Value)*lsf.triangle(x,y,x1,x2,x3,y1,y2,y3,idx,gctl::Dy));
    }
    else
    {
        xval = len*(lsf.triangle(x,y,x1,x2,x3,y1,y2,y3,idx,gctl::Dy)*lsf.triangle(x,y,x1,x2,x3,y1,y2,y3,(idx+1)%3,gctl::Dx)
                - lsf.triangle(x,y,x1,x2,x3,y1,y2,y3,(idx+1)%3,gctl::Dy)*lsf.triangle(x,y,x1,x2,x3,y1,y2,y3,idx,gctl::Dx)); // dVx/dy

        yval = len*(lsf.triangle(x,y,x1,x2,x3,y1,y2,y3,idx,gctl::Dx)*lsf.triangle(x,y,x1,x2,x3,y1,y2,y3,(idx+1)%3,gctl::Dy) 
                - lsf.triangle(x,y,x1,x2,x3,y1,y2,y3,(idx+1)%3,gctl::Dx)*lsf.triangle(x,y,x1,x2,x3,y1,y2,y3,idx,gctl::Dy)); // dVy/dx

        if (vt == VecCurl)
        {
            xval = yval = yval - xval;
        }
    }

    if (ot) // Ordered in reversed direction
    {
        xval *= -1.0;
        yval *= -1.0;
    }
    return;
}

void gctl::linear_esf::tetrahedron(double x, double y, double z, double x1, double x2, double x3, double x4, 
    double y1, double y2, double y3, double y4, double z1, double z2, double z3, double z4, 
    size_t idx, eshape_value_e vt, edge_orient_e ot, double &xval, double &yval, double &zval, 
    double &xval2, double &yval2, double &zval2)
{
    // Assign output values to defaults
    xval = yval = zval = 0.0;
    xval2= yval2= zval2= 0.0;

    double x12 = x2-x1, y12 = y2-y1, z12 = z2-z1;
	double x13 = x3-x1, y13 = y3-y1, z13 = z3-z1;
	double x14 = x4-x1, y14 = y4-y1, z14 = z4-z1;
	double crx = y12*z13-z12*y13;
	double cry = z12*x13-x12*z13;
	double crz = x12*y13-y12*x13;
	double scl = (x14*crx+y14*cry+z14*crz);
	// No need to get the absolute value here
	//if (scl < 0) scl = -1.0*scl;

    size_t l_id, k_id;
    double len;
    if (idx == 0)
    {
        l_id = 0; k_id = 1;
        len = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1) + (z2-z1)*(z2-z1));
    }
    else if (idx == 1)
    {
        l_id = 0; k_id = 2;
        len = sqrt((x3-x1)*(x3-x1) + (y3-y1)*(y3-y1) + (z3-z1)*(z3-z1));
    }
    else if (idx == 2)
    {
        l_id = 0; k_id = 3;
        len = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1) + (z4-z1)*(z4-z1));
    }
    else if (idx == 3)
    {
        l_id = 1; k_id = 2;
        len = sqrt((x3-x2)*(x3-x2) + (y3-y2)*(y3-y2) + (z3-z2)*(z3-z2));
    }
    else if (idx == 4)
    {
        l_id = 1; k_id = 3;
        len = sqrt((x4-x2)*(x4-x2) + (y4-y2)*(y4-y2) + (z4-z2)*(z4-z2));
    }
    else if (idx == 5)
    {
        l_id = 2; k_id = 3;
        len = sqrt((x4-x3)*(x4-x3) + (y4-y3)*(y4-y3) + (z4-z3)*(z4-z3));
    }
    else throw std::invalid_argument("gctl::linear_esf::tetrahedron(...) got an invalid calculating index.");

    if (vt == VecValue)
    {
        gctl::linear_sf lsf;
        xval = (lsf.tetrahedron(x,y,z,x1,x2,x3,x4,y1,y2,y3,y4,z1,z2,z3,z4,l_id,gctl::Value)*lsf.tetrahedron(x,y,z,x1,x2,x3,x4,y1,y2,y3,y4,z1,z2,z3,z4,k_id,gctl::Dx) 
            - lsf.tetrahedron(x,y,z,x1,x2,x3,x4,y1,y2,y3,y4,z1,z2,z3,z4,k_id,gctl::Value)*lsf.tetrahedron(x,y,z,x1,x2,x3,x4,y1,y2,y3,y4,z1,z2,z3,z4,l_id,gctl::Dx))*len;

        yval = (lsf.tetrahedron(x,y,z,x1,x2,x3,x4,y1,y2,y3,y4,z1,z2,z3,z4,l_id,gctl::Value)*lsf.tetrahedron(x,y,z,x1,x2,x3,x4,y1,y2,y3,y4,z1,z2,z3,z4,k_id,gctl::Dy) 
            - lsf.tetrahedron(x,y,z,x1,x2,x3,x4,y1,y2,y3,y4,z1,z2,z3,z4,k_id,gctl::Value)*lsf.tetrahedron(x,y,z,x1,x2,x3,x4,y1,y2,y3,y4,z1,z2,z3,z4,l_id,gctl::Dy))*len;

        zval = (lsf.tetrahedron(x,y,z,x1,x2,x3,x4,y1,y2,y3,y4,z1,z2,z3,z4,l_id,gctl::Value)*lsf.tetrahedron(x,y,z,x1,x2,x3,x4,y1,y2,y3,y4,z1,z2,z3,z4,k_id,gctl::Dz) 
            - lsf.tetrahedron(x,y,z,x1,x2,x3,x4,y1,y2,y3,y4,z1,z2,z3,z4,k_id,gctl::Value)*lsf.tetrahedron(x,y,z,x1,x2,x3,x4,y1,y2,y3,y4,z1,z2,z3,z4,l_id,gctl::Dz))*len;

        if (ot)  // Ordered in reversed direction
        {
            xval *= -1.0; // Vx
            yval *= -1.0; // Vy
            zval *= -1.0; // Vz
        }
        return;
    }

    // Gradient or Curl
    double a1, a2, b1, b2, c1, c2;
    if (idx == 0)
    {
        // edge 0->1
        a1 = -1.0*jacoby3(1.0, y2, z2, 1.0, y3, z3, 1.0, y4, z4)/scl;
		b1 = jacoby3(1.0, x2, z2, 1.0, x3, z3, 1.0, x4, z4)/scl;
		c1 = -1.0*jacoby3(1.0, x2, y2, 1.0, x3, y3, 1.0, x4, y4)/scl;

        a2 = jacoby3(1.0, y1, z1, 1.0, y3, z3, 1.0, y4, z4)/scl;
		b2 = -1.0*jacoby3(1.0, x1, z1, 1.0, x3, z3, 1.0, x4, z4)/scl;
		c2 = jacoby3(1.0, x1, y1, 1.0, x3, y3, 1.0, x4, y4)/scl;
    }
    else if (idx == 1)
    {
        // edge 0->2
        a1 = -1.0*jacoby3(1.0, y2, z2, 1.0, y3, z3, 1.0, y4, z4)/scl;
		b1 = jacoby3(1.0, x2, z2, 1.0, x3, z3, 1.0, x4, z4)/scl;
		c1 = -1.0*jacoby3(1.0, x2, y2, 1.0, x3, y3, 1.0, x4, y4)/scl;

        a2 = -1.0*jacoby3(1.0, y1, z1, 1.0, y2, z2, 1.0, y4, z4)/scl;
		b2 = jacoby3(1.0, x1, z1, 1.0, x2, z2, 1.0, x4, z4)/scl;
		c2 = -1.0*jacoby3(1.0, x1, y1, 1.0, x2, y2, 1.0, x4, y4)/scl;
    }
    else if (idx == 2)
    {
        // edge 0->3
        a1 = -1.0*jacoby3(1.0, y2, z2, 1.0, y3, z3, 1.0, y4, z4)/scl;
		b1 = jacoby3(1.0, x2, z2, 1.0, x3, z3, 1.0, x4, z4)/scl;
		c1 = -1.0*jacoby3(1.0, x2, y2, 1.0, x3, y3, 1.0, x4, y4)/scl;

        a2 = jacoby3(1.0, y1, z1, 1.0, y2, z2, 1.0, y3, z3)/scl;
		b2 = -1.0*jacoby3(1.0, x1, z1, 1.0, x2, z2, 1.0, x3, z3)/scl;
		c2 = jacoby3(1.0, x1, y1, 1.0, x2, y2, 1.0, x3, y3)/scl;
    }
    else if (idx == 3)
    {
        // edge 1->2
        a1 = jacoby3(1.0, y1, z1, 1.0, y3, z3, 1.0, y4, z4)/scl;
		b1 = -1.0*jacoby3(1.0, x1, z1, 1.0, x3, z3, 1.0, x4, z4)/scl;
		c1 = jacoby3(1.0, x1, y1, 1.0, x3, y3, 1.0, x4, y4)/scl;

        a2 = -1.0*jacoby3(1.0, y1, z1, 1.0, y2, z2, 1.0, y4, z4)/scl;
		b2 = jacoby3(1.0, x1, z1, 1.0, x2, z2, 1.0, x4, z4)/scl;
		c2 = -1.0*jacoby3(1.0, x1, y1, 1.0, x2, y2, 1.0, x4, y4)/scl;
    }
    else if (idx == 4)
    {
        // edge 1->3
        a1 = jacoby3(1.0, y1, z1, 1.0, y3, z3, 1.0, y4, z4)/scl;
        b1 = -1.0*jacoby3(1.0, x1, z1, 1.0, x3, z3, 1.0, x4, z4)/scl;
        c1 = jacoby3(1.0, x1, y1, 1.0, x3, y3, 1.0, x4, y4)/scl;

        a2 = jacoby3(1.0, y1, z1, 1.0, y2, z2, 1.0, y3, z3)/scl;
        b2 = -1.0*jacoby3(1.0, x1, z1, 1.0, x2, z2, 1.0, x3, z3)/scl;
        c2 = jacoby3(1.0, x1, y1, 1.0, x2, y2, 1.0, x3, y3)/scl;
    }
    else if (idx == 5)
    {
        // edge 2->3
        a1 = -1.0*jacoby3(1.0, y1, z1, 1.0, y2, z2, 1.0, y4, z4)/scl;
		b1 = jacoby3(1.0, x1, z1, 1.0, x2, z2, 1.0, x4, z4)/scl;
		c1 = -1.0*jacoby3(1.0, x1, y1, 1.0, x2, y2, 1.0, x4, y4)/scl;

        a2 = jacoby3(1.0, y1, z1, 1.0, y2, z2, 1.0, y3, z3)/scl;
		b2 = -1.0*jacoby3(1.0, x1, z1, 1.0, x2, z2, 1.0, x3, z3)/scl;
		c2 = jacoby3(1.0, x1, y1, 1.0, x2, y2, 1.0, x3, y3)/scl;
    }
    else throw std::invalid_argument("gctl::linear_esf::tetrahedron(...) got an invalid calculating index.");

    if (vt == VecGradient)
    {
        xval = (b1*a2 - b2*a1)*len; // dVx/dy
        xval2= (c1*a2 - c2*a1)*len; // dVx/dz
        yval = (a1*b2 - a2*b1)*len; // dVy/dx
        yval2= (c1*b2 - c2*b1)*len; // dVy/dz
        zval = (a1*c2 - a2*c1)*len; // dVz/dx
        zval2= (b1*c2 - b2*c1)*len; // dVz/dy
    }
    else
    {
        xval = 2.0*(b1*c2 - b2*c1)*len; // curl(V)_x
        yval = 2.0*(c1*a2 - c2*a1)*len; // curl(V)_y
        zval = 2.0*(a1*b2 - a2*b1)*len; // curl(V)_z
    }

    if (ot)  // Ordered in reversed direction
    {
        xval *= -1.0;
        yval *= -1.0;
        zval *= -1.0;
        xval2*= -1.0;
        yval2*= -1.0;
        zval2*= -1.0;
    }
    return;
}

void gctl::linear_esf::cube(double x, double y, double z, double x1, double x2, 
    double y1, double y2, double z1, double z2, size_t idx, eshape_value_e vt, edge_orient_e ot, 
    double &xval, double &yval, double &zval, double &xval2, double &yval2, double &zval2)
{
    // Assign output values to defaults
    xval = yval = zval = 0.0;
    xval2= yval2= zval2= 0.0;

    double lex = x2 - x1, ley = y2 - y1, lez = z2 - z1;
    double cx = 0.5*(x1 + x2), cy = 0.5*(y1 + y2), cz = 0.5*(z1 + z2);

    if (vt == VecValue)
    {
        if (idx >= 0 && idx <= 3)
        {
            if (idx == 0) xval = (cy + 0.5*ley - y)*(cz + 0.5*lez - z)/(ley*lez);
            else if (idx == 1) xval = (y - cy + 0.5*ley)*(cz + 0.5*lez - z)/(ley*lez);
            else if (idx == 2) xval = (cy + 0.5*ley - y)*(z - cz + 0.5*lez)/(ley*lez);
            else if (idx == 3) xval = (y - cy + 0.5*ley)*(z - cz + 0.5*lez)/(ley*lez);
        }
        else if (idx >= 4 && idx <= 7)
        {
            if (idx == 4) yval = (cz + 0.5*lez - z)*(cx + 0.5*lex - x)/(lex*lez);
            else if (idx == 5) yval = (z - cz + 0.5*lez)*(cx + 0.5*lex - x)/(lex*lez);
            else if (idx == 6) yval = (cz + 0.5*lez - z)*(x - cx + 0.5*lex)/(lex*lez);
            else if (idx == 7) yval = (z - cz + 0.5*lez)*(x - cx + 0.5*lex)/(lex*lez);
        }
        else if (idx >= 8 && idx <= 11)
        {
            if (idx == 8) zval = (cx + 0.5*lex - x)*(cy + 0.5*ley - y)/(lex*ley);
            else if (idx == 9) zval = (x - cx + 0.5*lex)*(cy + 0.5*ley - y)/(lex*ley);
            else if (idx == 10)zval = (cx + 0.5*lex - x)*(y - cy + 0.5*ley)/(lex*ley);
            else if (idx == 11)zval = (x - cx + 0.5*lex)*(y - cy + 0.5*ley)/(lex*ley);
        }
        else throw std::invalid_argument("gctl::linear_esf::cube(...) got an invalid calculating index.");
        
        if (ot)  // Ordered in reversed direction
        {
            xval *= -1.0; // Vx
            yval *= -1.0; // Vy
            zval *= -1.0; // Vz
        }
        return;
    }

    if (vt == VecGradient)
    {
        if (idx >= 0 && idx <= 3)
        {
            if (idx == 0)
            {
                xval = -1.0*(cz + 0.5*lez - z)/(ley*lez); // dVx/Dy
                xval2= -1.0*(cy + 0.5*ley - y)/(ley*lez); // dVx/Dz
            }
            else if (idx == 1)
            {
                xval = (cz + 0.5*lez - z)/(ley*lez);
                xval2= -1.0*(y - cy + 0.5*ley)/(ley*lez);
            }
            else if (idx == 2)
            {
                xval = -1.0*(z - cz + 0.5*lez)/(ley*lez);
                xval2= (cy + 0.5*ley - y)/(ley*lez);
            }
            else if (idx == 3)
            {
                xval = (z - cz + 0.5*lez)/(ley*lez);
                xval2= (y - cy + 0.5*ley)/(ley*lez);
            }
        }
        else if (idx >= 4 && idx <= 7)
        {
            if (idx == 4)
            {
                yval = -1.0*(cz + 0.5*lez - z)/(lex*lez); // dVy/dx
                yval2= -1.0*(cx + 0.5*lex - x)/(lex*lez); // dVy/dz
            }
            else if (idx == 5)
            {
                yval = -1.0*(z - cz + 0.5*lez)/(lex*lez);
                yval2= (cx + 0.5*lex - x)/(lex*lez);
            }
            else if (idx == 6)
            {
                yval = (cz + 0.5*lez - z)/(lex*lez);
                yval2= -1.0*(x - cx + 0.5*lex)/(lex*lez);
            }
            else if (idx == 7)
            {
                yval = (z - cz + 0.5*lez)/(lex*lez);
                yval2= (x - cx + 0.5*lex)/(lex*lez);
            }
        }
        else if (idx >= 8 && idx <= 11)
        {
            if (idx == 8)
            {
                zval = -1.0*(cy + 0.5*ley - y)/(lex*ley); // dVz/dx
                zval2= -1.0*(cx + 0.5*lex - x)/(lex*ley); // dVz/dy
            }
            else if (idx == 9)
            {
                zval = (cy + 0.5*ley - y)/(lex*ley);
                zval2= -1.0*(x - cx + 0.5*lex)/(lex*ley);
            }
            else if (idx == 10)
            {
                zval = -1.0*(y - cy + 0.5*ley)/(lex*ley);
                zval2= (cx + 0.5*lex - x)/(lex*ley);
            }
            else if (idx == 11)
            {
                zval = (y - cy + 0.5*ley)/(lex*ley);
                zval2= (x - cx + 0.5*lex)/(lex*ley);
            }
        }
        else throw std::invalid_argument("gctl::linear_esf::cube(...) got an invalid calculating index.");

        if (ot)  // Ordered in reversed direction
        {
            xval *= -1.0;
            yval *= -1.0;
            zval *= -1.0;
            xval2*= -1.0;
            yval2*= -1.0;
            zval2*= -1.0;
        }
    }

    // Curl
    if (idx >= 0 && idx <= 3)
    {
        if (idx == 0)
        {
            yval = -1.0*(cy + 0.5*ley - y)/(ley*lez); // dVx/Dz
            zval = (cz + 0.5*lez - z)/(ley*lez); // dVx/Dy
        }
        else if (idx == 1)
        {
            yval = -1.0*(y - cy + 0.5*ley)/(ley*lez);
            zval = -1.0*(cz + 0.5*lez - z)/(ley*lez);
        }
        else if (idx == 2)
        {
            yval= (cy + 0.5*ley - y)/(ley*lez);
            zval = (z - cz + 0.5*lez)/(ley*lez);
        }
        else if (idx == 3)
        {
            yval= (y - cy + 0.5*ley)/(ley*lez);
            zval = -1.0*(z - cz + 0.5*lez)/(ley*lez);
        }
    }
    else if (idx >= 4 && idx <= 7)
    {
        if (idx == 4)
        {
            xval= (cx + 0.5*lex - x)/(lex*lez); // dVy/dz
            zval = -1.0*(cz + 0.5*lez - z)/(lex*lez); // dVy/dx
        }
        else if (idx == 5)
        {
            xval = -1.0*(cx + 0.5*lex - x)/(lex*lez);
            zval = -1.0*(z - cz + 0.5*lez)/(lex*lez);
        }
        else if (idx == 6)
        {
            xval = (x - cx + 0.5*lex)/(lex*lez);
            zval = (cz + 0.5*lez - z)/(lex*lez);
        }
        else if (idx == 7)
        {
            xval = -1.0*(x - cx + 0.5*lex)/(lex*lez);
            zval = (z - cz + 0.5*lez)/(lex*lez);
        }
    }
    else if (idx >= 8 && idx <= 11)
    {
        if (idx == 8)
        {
            xval = -1.0*(cx + 0.5*lex - x)/(lex*ley); // dVz/dy
            yval = (cy + 0.5*ley - y)/(lex*ley); // dVz/dx
        }
        else if (idx == 9)
        {
            xval = -1.0*(x - cx + 0.5*lex)/(lex*ley);
            yval = -1.0*(cy + 0.5*ley - y)/(lex*ley);
        }
        else if (idx == 10)
        {
            xval = (cx + 0.5*lex - x)/(lex*ley);
            yval = (y - cy + 0.5*ley)/(lex*ley);
        }
        else if (idx == 11)
        {
            xval = (x - cx + 0.5*lex)/(lex*ley);
            yval = -1.0*(y - cy + 0.5*ley)/(lex*ley);
        }
    }
    else throw std::invalid_argument("gctl::linear_esf::cube(...) got an invalid calculating index.");

    if (ot)  // Ordered in reversed direction
    {
        xval *= -1.0;
        yval *= -1.0;
        zval *= -1.0;
        xval2*= -1.0;
        yval2*= -1.0;
        zval2*= -1.0;
    }
    return;
}

/*
double gctl::linear_sf::tetrahedron(double x, double y, double z, double x1, double x2, double x3, double x4, 
    double y1, double y2, double y3, double y4, double z1, double z2, double z3, double z4,  
    size_t idx, gradient_type_e vt)
{
    double x12 = x2-x1, y12 = y2-y1, z12 = z2-z1;
    double x13 = x3-x1, y13 = y3-y1, z13 = z3-z1;
    double x14 = x4-x1, y14 = y4-y1, z14 = z4-z1;
    double crx = y12*z13-z12*y13;
    double cry = z12*x13-x12*z13;
    double crz = x12*y13-y12*x13;
    double scl = (x14*crx+y14*cry+z14*crz);
    //if (scl < 0) scl = -1.0*scl; // 不要判断正负 正好后面有用

    double a, b, c, d;
    switch (10*idx + vt)
    {
        case 0:
            a = -1.0*jacoby3(1.0, y2, z2, 1.0, y3, z3, 1.0, y4, z4)/scl;
            b = jacoby3(1.0, x2, z2, 1.0, x3, z3, 1.0, x4, z4)/scl;
            c = -1.0*jacoby3(1.0, x2, y2, 1.0, x3, y3, 1.0, x4, y4)/scl;
            d = jacoby3(x2, y2, z2, x3, y3, z3, x4, y4, z4)/scl;
            return a*x+b*y+c*z+d;
        case 1:
            return -1.0*jacoby3(1.0, y2, z2, 1.0, y3, z3, 1.0, y4, z4)/scl;
        case 2:
            return jacoby3(1.0, x2, z2, 1.0, x3, z3, 1.0, x4, z4)/scl;
        case 3:
            return -1.0*jacoby3(1.0, x2, y2, 1.0, x3, y3, 1.0, x4, y4)/scl;
        case 10:
            a = jacoby3(1.0, y1, z1, 1.0, y3, z3, 1.0, y4, z4)/scl;
            b = -1.0*jacoby3(1.0, x1, z1, 1.0, x3, z3, 1.0, x4, z4)/scl;
            c = jacoby3(1.0, x1, y1, 1.0, x3, y3, 1.0, x4, y4)/scl;
            d = -1.0*jacoby3(x1, y1, z1, x3, y3, z3, x4, y4, z4)/scl;
            return a*x+b*y+c*z+d;
        case 11:
            return jacoby3(1.0, y1, z1, 1.0, y3, z3, 1.0, y4, z4)/scl;
        case 12:
            return -1.0*jacoby3(1.0, x1, z1, 1.0, x3, z3, 1.0, x4, z4)/scl;
        case 13:
            return jacoby3(1.0, x1, y1, 1.0, x3, y3, 1.0, x4, y4)/scl;
        case 20:
            a = -1.0*jacoby3(1.0, y1, z1, 1.0, y2, z2, 1.0, y4, z4)/scl;
            b = jacoby3(1.0, x1, z1, 1.0, x2, z2, 1.0, x4, z4)/scl;
            c = -1.0*jacoby3(1.0, x1, y1, 1.0, x2, y2, 1.0, x4, y4)/scl;
            d = jacoby3(x1, y1, z1, x2, y2, z2, x4, y4, z4)/scl;
            return a*x+b*y+c*z+d;
        case 21:
            return -1.0*jacoby3(1.0, y1, z1, 1.0, y2, z2, 1.0, y4, z4)/scl;
        case 22:
            return jacoby3(1.0, x1, z1, 1.0, x2, z2, 1.0, x4, z4)/scl;
        case 23:
            return -1.0*jacoby3(1.0, x1, y1, 1.0, x2, y2, 1.0, x4, y4)/scl;
        case 30:
            a = jacoby3(1.0, y1, z1, 1.0, y2, z2, 1.0, y3, z3)/scl;
            b = -1.0*jacoby3(1.0, x1, z1, 1.0, x2, z2, 1.0, x3, z3)/scl;
            c = jacoby3(1.0, x1, y1, 1.0, x2, y2, 1.0, x3, y3)/scl;
            d = -1.0*jacoby3(x1, y1, z1, x2, y2, z2, x3, y3, z3)/scl;
            return a*x+b*y+c*z+d;
        case 31:
            return jacoby3(1.0, y1, z1, 1.0, y2, z2, 1.0, y3, z3)/scl;
        case 32:
            return -1.0*jacoby3(1.0, x1, z1, 1.0, x2, z2, 1.0, x3, z3)/scl;
        case 33:
            return jacoby3(1.0, x1, y1, 1.0, x2, y2, 1.0, x3, y3)/scl;
        default:
            throw runtime_error("Invalid calculating type. From gctl::linear_sf::tetrahedron(...)");
    }
}
*/