/*
 * Copyright 2018, 2020-2021, 2026 Uber Technologies, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <float.h>
#include <math.h>

#include "test.h"
#include "vec3d.h"

SUITE(Vec3dInternal) {
    TEST(vec3DistSq) {
        Vec3d v1 = {0, 0, 0};
        Vec3d v2 = {1, 0, 0};
        Vec3d v3 = {0, 1, 1};
        Vec3d v4 = {1, 1, 1};
        Vec3d v5 = {1, 1, 2};

        t_assert(fabs(vec3DistSq(v1, v1)) < DBL_EPSILON,
                 "distance to self is 0");
        t_assert(fabs(vec3DistSq(v1, v2) - 1) < DBL_EPSILON,
                 "distance to <1,0,0> is 1");
        t_assert(fabs(vec3DistSq(v1, v3) - 2) < DBL_EPSILON,
                 "distance to <0,1,1> is 2");
        t_assert(fabs(vec3DistSq(v1, v4) - 3) < DBL_EPSILON,
                 "distance to <1,1,1> is 3");
        t_assert(fabs(vec3DistSq(v1, v5) - 6) < DBL_EPSILON,
                 "distance to <1,1,2> is 6");
    }

    TEST(vec3Normalize_smallNonzero) {
        // 1e-163 squared underflows to 0, so norm == 0.
        // vec3Normalize should produce the zero vector.
        Vec3d v = {1e-163, 0, 0};

        t_assert(v.x != 0.0, "vector is nonzero");
        t_assert(vec3Norm(v) == 0.0, "norm underflows to zero");

        vec3Normalize(&v);
        t_assert(v.x == 0.0 && v.y == 0.0 && v.z == 0.0,
                 "underflowed vector normalizes to zero");
    }

    TEST(vec3Normalize_dblEpsilonHalf) {
        // DBL_EPSILON/2 is small but normalizes fine.
        Vec3d v = {DBL_EPSILON / 2.0, 0, 0};

        t_assert(vec3Norm(v) < DBL_EPSILON, "norm is small but nonzero");

        vec3Normalize(&v);
        t_assert(fabs(v.x - 1.0) < DBL_EPSILON && v.y == 0 && v.z == 0,
                 "still normalizable to unit vector");
    }

    TEST(latLngToVec3) {
        Vec3d origin = {0};

        LatLng c1 = {0, 0};
        Vec3d p1 = latLngToVec3(c1);
        t_assert(fabs(vec3DistSq(origin, p1) - 1) < EPSILON_RAD,
                 "Geo point is on the unit sphere");

        LatLng c2 = {M_PI_2, 0};
        Vec3d p2 = latLngToVec3(c2);
        t_assert(fabs(vec3DistSq(p1, p2) - 2) < EPSILON_RAD,
                 "Geo point is on another axis");

        LatLng c3 = {M_PI, 0};
        Vec3d p3 = latLngToVec3(c3);
        t_assert(fabs(vec3DistSq(p1, p3) - 4) < EPSILON_RAD,
                 "Geo point is the other side of the sphere");
    }
}
