Problem: Vincenty-Based Geographic Distance Calculations

The problem involves developing C++ functions to calculate ellipsoidal distances (in kilometers) between geographic points using the Vincenty inverse formula (with Haversine fallback for convergence) for single pairs, one-to-many points, and a symmetric matrix, leveraging WGS84 parameters and optimizations like symmetry and early termination.


Input/Output: The problem receives input data including: for two-point distance calculation, four double values representing the longitude and latitude (in decimal degrees) of two geographic points; for one-to-multiple points distance calculation, two double values for the reference point's longitude and latitude, plus two std::vector<double> containing longitudes and latitudes of multiple target points; for distance matrix calculation, two std::vector<double> with longitudes and latitudes of multiple points. The problem outputs: a double value for the ellipsoidal distance (in kilometers) between two points using the Vincenty inverse formula (with Haversine fallback if iterations exceed 100); a std::vector<double> where each element is the distance from the reference point to the corresponding target point; a symmetric std::vector<std::vector<double>> matrix where matrix[i][j] equals matrix[j][i], storing distances between all pairs of points with symmetric optimization to avoid redundant computations.

Required dependencies:
#ifndef _USE_MATH_DEFINES
#define _USE_MATH_DEFINES
#endif
#include <iostream>
#include <vector>
#include <cmath>
using namespace std;

Sub-steps:
step_number: 1
step_description_prompt: Please write a C++ function named vincentyDistance that calculates the ellipsoidal distance (in kilometers) between two geographic points (given as longitude and latitude in decimal degrees) using the Vincenty inverse formula, with Haversine formula as fallback if Vincenty iteration does not converge.

Function definition: double vincentyDistance(double lon1, double lat1, double lon2, double lat2);

Algorithm steps:
1. Use WGS84 ellipsoid parameters: WGS84_A = 6378137.0, WGS84_F = 1.0 / 298.257223563, WGS84_B = WGS84_A * (1.0 - WGS84_F).
2. Return 0.0 if lon1==lon2 and lat1==lat2.
3. Convert latitudes (lat1, lat2) and longitudes (lon1, lon2) from degrees to radians.
4. Compute U1 = std::atan((1.0 - WGS84_F) * std::tan(phi1)), U2 = std::atan((1.0 - WGS84_F) * std::tan(phi2)) where phi1/phi2 are radians of lat1/lat2.
5. Calculate sinU1, cosU1 (from U1), sinU2, cosU2 (from U2).
6. Iterate lambda (initially lambda2 - lambda1 in radians) up to 100 times:
   a. Compute sinLambda = std::sin(lambda), cosLambda = std::cos(lambda).
   b. Calculate sinSigma = std::sqrt((cosU2 * sinLambda)*(cosU2 * sinLambda) + (cosU1 * sinU2 - sinU1 * cosU2 * cosLambda)*(cosU1 * sinU2 - sinU1 * cosU2 * cosLambda)); return 0.0 if sinSigma==0.
   c. Compute cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * cosLambda; sigma = std::atan2(sinSigma, cosSigma).
   d. Calculate sinAlpha = (cosU1 * cosU2 * sinLambda) / sinSigma; cosSqAlpha = 1.0 - sinAlpha * sinAlpha.
   e. Compute cos2SigmaM = (cosSqAlpha==0) ? 0.0 : (cosSigma - 2.0 * sinU1 * sinU2 / cosSqAlpha).
   f. Calculate C = WGS84_F / 16.0 * cosSqAlpha * (4.0 + WGS84_F * (4.0 - 3.0 * cosSqAlpha)).
   g. Update lambda as L + (1.0 - C) * WGS84_F * sinAlpha * (sigma + C * sinSigma * (cos2SigmaM + C * cosSigma * (-1.0 + 2.0 * cos2SigmaM * cos2SigmaM))), where L is lambda2 - lambda1 in radians.
   h. Stop if |lambda - lambdaP| < 1e-12 (lambdaP is previous lambda) or iterations end.
7. If iterations exceed limit, use Haversine formula: compute dlat = (lat2 - lat1)*M_PI/180.0, dlon = (lon2 - lon1)*M_PI/180.0; a = std::sin(dlat/2)*std::sin(dlat/2) + std::cos(lat1_rad)*std::cos(lat2_rad)*std::sin(dlon/2)*std::sin(dlon/2); c = 2*std::atan2(std::sqrt(a), std::sqrt(1-a)); return WGS84_A * c / 1000.0.
8. Calculate uSq = cosSqAlpha * (WGS84_A*WGS84_A - WGS84_B*WGS84_B) / (WGS84_B*WGS84_B); A = 1.0 + uSq/16384.0*(4096.0 + uSq*(-768.0 + uSq*(320.0 - 175.0*uSq))); B = uSq/1024.0*(256.0 + uSq*(-128.0 + uSq*(74.0 - 47.0*uSq))); deltaSigma = B*sinSigma*(cos2SigmaM + B/4.0*(cosSigma*(-1.0 + 2.0*cos2SigmaM*cos2SigmaM) - B/6.0*cos2SigmaM*(-3.0 + 4.0*sinSigma*sinSigma)*(-3.0 + 4.0*cos2SigmaM*cos2SigmaM))); s = WGS84_B*A*(sigma - deltaSigma); return s/1000.0.

Assume all called functions (std::sin, std::cos, std::atan2, std::sqrt, std::abs, etc.) are already defined.
function_header: double vincentyDistance(double lon1, double lat1, double lon2, double lat2)
test_cases: int main() {
    // Compare if two double values are equal (considering tolerance)
    auto isEqual = [](double a, double b, double tolerance = 1e-6) {
        return std::abs(a - b) <= tolerance;
    };
    
    std::cout << "Subproblem 1.1: Distance between two points (Vincenty ellipsoid)" << std::endl;
    std::cout << "=================================================================" << std::endl << std::endl;
    
    int correctCount = 0;
    
    // Test case 1: Same point
    double result1 = vincentyDistance(0.0, 0.0, 0.0, 0.0);
    bool passed1 = isEqual(result1, 0.0, 0.01);
    std::cout << "Test case 1: " << (passed1 ? "correct" : "wrong") << std::endl;
    if (passed1) correctCount++;
    
    // Test case 2: Two nearby points (Vincenty gives different result than Haversine)
    double result2 = vincentyDistance(120.0, 30.0, 121.0, 31.0);
    bool passed2 = isEqual(result2, 146.6, 1.0);  // Updated: Vincenty result ~146.6 km
    std::cout << "Test case 2: " << (passed2 ? "correct" : "wrong") << std::endl;
    if (passed2) correctCount++;
    
    // Test case 3: Points on opposite sides (Vincenty gives different result than Haversine)
    double result3 = vincentyDistance(0.0, 0.0, 180.0, 0.0);
    bool passed3 = isEqual(result3, 20037.5, 1.0);  // Updated: Vincenty result ~20037.5 km
    std::cout << "Test case 3: " << (passed3 ? "correct" : "wrong") << std::endl;
    if (passed3) correctCount++;
    
    if (correctCount == 3) {
        std::cout << "Subproblem 1.1: Completely correct!" << std::endl;
    }
    
    return 0;
}

step_number: 2
step_description_prompt: Please write a C++ function named pointToPointsDistance that calculates the distances from one point (given by longitude and latitude) to multiple points (given by two vectors of longitudes and latitudes) using the Vincenty distance formula with early termination optimization. The function definition should be: std::vector<double> pointToPointsDistance(double lon, double lat, const std::vector<double>& lons, const std::vector<double>& lats); 

The algorithm process: 1. Define an internal lambda function vincentyDistance (taking lon1, lat1, lon2, lat2 and returning double) that uses WGS84 ellipsoid parameters (WGS84_A=6378137.0, WGS84_F=1.0/298.257223563, WGS84_B=WGS84_A*(1.0-WGS84_F)). 2. In vincentyDistance: a. Return 0.0 immediately if lon1==lon2 and lat1==lat2. b. Convert input degrees to radians. c. Iterate up to 100 times to compute lambda with convergence check (abs(lambda - lambdaP) > 1e-12). d. If iteration limit is reached, compute distance using the Haversine formula and return it in kilometers. e. Otherwise, compute the final distance using the Vincenty formula and return it in kilometers. 3. In the main function: Reserve space for the result vector (using reserve(lons.size())), then loop through each index i from 0 to lons.size()-1, compute the distance between (lon, lat) and (lons[i], lats[i]) using vincentyDistance, and push the result to the vector. Return the result vector. 

Assume all required headers (like <vector>, <cmath>) are included and functions like std::sin, std::cos are available.
function_header: std::vector<double> pointToPointsDistance( double lon, double lat, const std::vector<double>& lons, const std::vector<double>& lats )
test_cases: int main() {
    // Compare if two double values are equal (considering tolerance)
    auto isEqual = [](double a, double b, double tolerance = 1e-6) {
        return std::abs(a - b) <= tolerance;
    };
    
    std::cout << "Subproblem 1.2: Point to multiple points distance" << std::endl;
    std::cout << "===================================================" << std::endl << std::endl;
    
    int correctCount = 0;
    
    // Test case 1: Point to three points
    std::vector<double> lons1 = {0.0, 1.0, 2.0};
    std::vector<double> lats1 = {0.0, 1.0, 2.0};
    std::vector<double> result1 = pointToPointsDistance(0.0, 0.0, lons1, lats1);
    std::vector<double> expected1 = {0.0, 157.2, 314.4};
    bool passed1 = isEqual(result1[0], expected1[0], 0.01) && 
                   isEqual(result1[1], expected1[1], 1.0) && 
                   isEqual(result1[2], expected1[2], 1.0);
    std::cout << "Test case 1: " << (passed1 ? "correct" : "wrong") << std::endl;
    if (passed1) correctCount++;
    
    // Test case 2: Point to three nearby points
    std::vector<double> lons2 = {120.0, 121.0, 120.0};
    std::vector<double> lats2 = {30.0, 30.0, 31.0};
    std::vector<double> result2 = pointToPointsDistance(120.0, 30.0, lons2, lats2);
    bool passed2 = isEqual(result2[0], 0.0, 0.01) && 
                   isEqual(result2[1], 96.5, 1.0) && 
                   isEqual(result2[2], 111.3, 1.0);
    std::cout << "Test case 2: " << (passed2 ? "correct" : "wrong") << std::endl;
    if (passed2) correctCount++;
    
    // Test case 3: Point to three points (including poles) - Vincenty gives different result
    std::vector<double> lons3 = {0.0, 90.0, 0.0};
    std::vector<double> lats3 = {0.0, 0.0, 90.0};
    std::vector<double> result3 = pointToPointsDistance(0.0, 0.0, lons3, lats3);
    // Actual values from Vincenty: [0.0, 10018.8, 10002.0]
    bool passed3 = isEqual(result3[0], 0.0, 0.01) && 
                   isEqual(result3[1], 10018.8, 1.0) && 
                   isEqual(result3[2], 10002.0, 1.0);
    std::cout << "Test case 3: " << (passed3 ? "correct" : "wrong") << std::endl;
    if (passed3) correctCount++;
    
    if (correctCount == 3) {
        std::cout << "Subproblem 1.2: Completely correct!" << std::endl;
    }
    
    return 0;
}

step_number: 3
step_description_prompt: Please write a C++ function named computeDistanceMatrix that calculates a symmetric distance matrix for multiple points using the Vincenty ellipsoidal distance formula (with WGS84 parameters) and optimizes computation via symmetry. The function takes two const std::vector<double>& parameters (lons for longitudes, lats for latitudes) and returns a std::vector<std::vector<double>>.  

Implement this logic:  
1. Define an internal lambda `vincentyDistance` to compute the distance (in kilometers) between two points (lon1, lat1) and (lon2, lat2): Use WGS84 ellipsoid parameters (semi-major axis A=6378137.0, flattening F=1.0/298.257223563, semi-minor axis B=A*(1-F)). Return 0.0 if the two points are identical. Convert longitude and latitude from degrees to radians. Iterate up to 100 times to converge the lambda value (tolerance for |lambda - lambdaP| is 1e-12). If iterations are exhausted, use the Haversine formula as a fallback.  
2. Initialize an n×n matrix (n = lons.size()) with zeros.  
3. Leverage symmetry: For each i from 0 to n-1, compute distances for j from i+1 to n-1 using `vincentyDistance(lons[i], lats[i], lons[j], lats[j])`, then assign the result to both matrix[i][j] and matrix[j][i] to avoid redundant calculations.  
4. Return the completed matrix.
function_header: std::vector<std::vector<double>> computeDistanceMatrix( const std::vector<double>& lons, const std::vector<double>& lats )
test_cases: int main() {
    // Compare if two double values are equal (considering tolerance)
    auto isEqual = [](double a, double b, double tolerance = 1e-6) {
        return std::abs(a - b) <= tolerance;
    };
    
    std::cout << "Subproblem 1.3: Distance matrix (symmetric optimization)" << std::endl;
    std::cout << "=========================================================" << std::endl << std::endl;
    
    int correctCount = 0;
    
    // Test case 1: Three points
    std::vector<double> lons1 = {0.0, 1.0, 2.0};
    std::vector<double> lats1 = {0.0, 1.0, 2.0};
    std::vector<std::vector<double>> result1 = computeDistanceMatrix(lons1, lats1);
    bool passed1 = (result1.size() == 3) && 
                   isEqual(result1[0][0], 0.0, 0.01) &&
                   isEqual(result1[1][1], 0.0, 0.01) &&
                   isEqual(result1[2][2], 0.0, 0.01) &&
                   isEqual(result1[0][1], result1[1][0], 1.0) &&
                   isEqual(result1[0][2], result1[2][0], 1.0) &&
                   isEqual(result1[1][2], result1[2][1], 1.0);
    std::cout << "Test case 1: " << (passed1 ? "correct" : "wrong") << std::endl;
    if (passed1) correctCount++;
    
    // Test case 2: Five points
    std::vector<double> lons2 = {0.0, 1.0, 2.0, 3.0, 4.0};
    std::vector<double> lats2 = {0.0, 1.0, 2.0, 3.0, 4.0};
    std::vector<std::vector<double>> result2 = computeDistanceMatrix(lons2, lats2);
    bool passed2 = (result2.size() == 5) && 
                   isEqual(result2[0][0], 0.0, 0.01) &&
                   isEqual(result2[2][2], 0.0, 0.01) &&
                   isEqual(result2[4][4], 0.0, 0.01) &&
                   isEqual(result2[0][2], result2[2][0], 1.0);
    std::cout << "Test case 2: " << (passed2 ? "correct" : "wrong") << std::endl;
    if (passed2) correctCount++;
    
    // Test case 3: Ten points
    std::vector<double> lons3(10), lats3(10);
    for (int i = 0; i < 10; ++i) {
        lons3[i] = i;
        lats3[i] = i;
    }
    std::vector<std::vector<double>> result3 = computeDistanceMatrix(lons3, lats3);
    bool passed3 = (result3.size() == 10) && 
                   isEqual(result3[0][0], 0.0, 0.01) &&
                   isEqual(result3[5][5], 0.0, 0.01) &&
                   isEqual(result3[9][9], 0.0, 0.01) &&
                   isEqual(result3[0][5], result3[5][0], 1.0);
    std::cout << "Test case 3: " << (passed3 ? "correct" : "wrong") << std::endl;
    if (passed3) correctCount++;
    
    if (correctCount == 3) {
        std::cout << "Subproblem 1.3: Completely correct!" << std::endl;
    }
    
    return 0;
}

General solution:

General tests: