#include "Test.h"

#include "Point_3D_impl.h"
#include "Vector_3D_impl.h"

#include <math.h>

namespace MM
{

class Point_3D_test : public Test
{
    Point_3D *link;
public:
    Point_3D_test (const Text& suite_name) : Test (suite_name) { }

    void run()
    {
        test_arithmetic();          count_tests();
        test_affine_combination();  count_tests();
        test_modulus();             count_tests();
        test_product();             count_tests();
    }

    void test_arithmetic()
    {
        Vector_3D_impl   a(2,5,6),  b(-2,7,1);
        a += b;

        DOUBLES_EQUAL (a.x(),  0, 0.0001);
        DOUBLES_EQUAL (a.y(), 12, 0.0001);
        DOUBLES_EQUAL (a.z(),  7, 0.0001);

        a -= b;

        DOUBLES_EQUAL (a.x(),  2, 0.0001);
        DOUBLES_EQUAL (a.y(),  5, 0.0001);
        DOUBLES_EQUAL (a.z(),  6, 0.0001);

        a *= 10;

        DOUBLES_EQUAL (a.x(),  20, 0.0001);
        DOUBLES_EQUAL (a.y(),  50, 0.0001);
        DOUBLES_EQUAL (a.z(),  60, 0.0001);

        Point_3D_impl   p(2,5,6);
        p += b;

        DOUBLES_EQUAL (p.x(),  0, 0.0001);
        DOUBLES_EQUAL (p.y(), 12, 0.0001);
        DOUBLES_EQUAL (p.z(),  7, 0.0001);

        p -= (Vector_3D&)b;

        DOUBLES_EQUAL (p.x(),  2, 0.0001);
        DOUBLES_EQUAL (p.y(),  5, 0.0001);
        DOUBLES_EQUAL (p.z(),  6, 0.0001);

        p += (Vector_3D&)b;

        DOUBLES_EQUAL (p.x(),  0, 0.0001);
        DOUBLES_EQUAL (p.y(), 12, 0.0001);
        DOUBLES_EQUAL (p.z(),  7, 0.0001);

        p -= b;

        DOUBLES_EQUAL (p.x(),  2, 0.0001);
        DOUBLES_EQUAL (p.y(),  5, 0.0001);
        DOUBLES_EQUAL (p.z(),  6, 0.0001);

        p.difference (Point_3D_impl(2,5,6), &a);
        DOUBLES_EQUAL (a.length(),  0., 0.0001);
    }

    void test_affine_combination()
    {
        Vector_3D_impl   a(1,1,1),  b(3,3,3);
        a.affine (b, .5);

        DOUBLES_EQUAL (a.x(),  2, 0.0001);
        DOUBLES_EQUAL (a.y(),  2, 0.0001);
        DOUBLES_EQUAL (a.z(),  2, 0.0001);

        a.affine (b, 0);

        DOUBLES_EQUAL (a.x(),  2, 0.0001);
        DOUBLES_EQUAL (a.y(),  2, 0.0001);
        DOUBLES_EQUAL (a.z(),  2, 0.0001);

        Point_3D_impl A(0,0,0), B(0,2,0), C(2,2,0), D(2,0,0);
        A.affine (B, .5);
        C.affine (D, .5);
        A.affine (C, .5);

        DOUBLES_EQUAL (A.x(),  1, 0.0001);
        DOUBLES_EQUAL (A.y(),  1, 0.0001);
        DOUBLES_EQUAL (A.z(),  0, 0.0001);
    }

    void test_modulus()
    {
        Vector_3D_impl   a(10,10,0);
        DOUBLES_EQUAL (a.length  (),  sqrt(200.), 0.0001);
        DOUBLES_EQUAL (a.length_2(),       200 ,  0.0001);

        a.norm();
        DOUBLES_EQUAL (a.length (),  1, 0.0001);

        Point_3D_impl   p1(10,10,0),  p2(20,20,0);
        DOUBLES_EQUAL (p1.distance   (p2),              sqrt(200.), 0.0001);
        DOUBLES_EQUAL (p1.distance_2 (p2),                   200 ,  0.0001);
        DOUBLES_EQUAL (p1.distance   ((Point_3D&)p2),   sqrt(200.), 0.0001);
        DOUBLES_EQUAL (p1.distance_2 ((Point_3D&)p2),        200 ,  0.0001);
    }

    void test_product()
    {
        Vector_3D_impl   a(2,3,1),  b(0,4,-1);
        DOUBLES_EQUAL (a.dot (b),  11, 0.0001);

        DOUBLES_EQUAL (Vector_3D_impl(3,4,0).norm().dot 
                      (Vector_3D_impl(5,2,0).norm()),    0.85422, 0.0001);


        Vector_3D_impl   c(3,0,2),  d(4,1,8),  n(0,0,0);
        c.cross (d, &n);

        DOUBLES_EQUAL (n.x(),  -2, 0.0001);
        DOUBLES_EQUAL (n.y(), -16, 0.0001);
        DOUBLES_EQUAL (n.z(),   3, 0.0001);

        DOUBLES_EQUAL (c.x(),   3, 0.0001);
        DOUBLES_EQUAL (c.y(),   0, 0.0001);
        DOUBLES_EQUAL (c.z(),   2, 0.0001);

        c.cross (d);

        DOUBLES_EQUAL (c.x(),  -2, 0.0001);
        DOUBLES_EQUAL (c.y(), -16, 0.0001);
        DOUBLES_EQUAL (c.z(),   3, 0.0001);
    }
};

Point_3D_test test_point_3D ("correctness");

}//MM
