def METHOD_NAME():
    """Main runner."""
    x = simulate_car()
    print(f"Simulated car trajectory: {x}")
    add_noise = True  # set this to False to run with "perfect" measurements
    # GPS measurements
    sigma_gps = 3.0  # assume GPS is +/- 3m
    g = [
        x[k] + (np.random.normal(scale=sigma_gps) if add_noise else 0)
        for k in range(5)
    ]
    # Odometry measurements
    sigma_odo = 0.1  # assume Odometry is 10cm accurate at 4Hz
    o = [
        x[k + 1] - x[k] +
        (np.random.normal(scale=sigma_odo) if add_noise else 0)
        for k in range(4)
    ]
    # Landmark measurements:
    sigma_lm = 1  # assume landmark measurement is accurate up to 1m
    # Assume first landmark is at x=5, we measure it at time k=0
    lm_0 = 5.0
    z_0 = x[0] - lm_0 + (np.random.normal(scale=sigma_lm) if add_noise else 0)
    # Assume other landmark is at x=28, we measure it at time k=3
    lm_3 = 28.0
    z_3 = x[3] - lm_3 + (np.random.normal(scale=sigma_lm) if add_noise else 0)
    unknown = [gtsam.symbol('x', k) for k in range(5)]
    print("unknowns = ", list(map(gtsam.DefaultKeyFormatter, unknown)))
    # We now can use nonlinear factor graphs
    factor_graph = gtsam.NonlinearFactorGraph()
    # Add factors for GPS measurements
    gps_model = gtsam.noiseModel.Isotropic.Sigma(1, sigma_gps)
    # Add the GPS factors
    for k in range(5):
        gf = gtsam.CustomFactor(gps_model, [unknown[k]],
                                partial(error_gps, np.array([g[k]])))
        factor_graph.add(gf)
    # New Values container
    v = gtsam.Values()
    # Add initial estimates to the Values container
    for i in range(5):
        v.insert(unknown[i], np.array([0.0]))
    # Initialize optimizer
    params = gtsam.GaussNewtonParams()
    optimizer = gtsam.GaussNewtonOptimizer(factor_graph, v, params)
    # Optimize the factor graph
    result = optimizer.optimize()
    # calculate the error from ground truth
    error = np.array([(result.atVector(unknown[k]) - x[k])[0]
                      for k in range(5)])
    print("Result with only GPS")
    print(result, np.round(error, 2),
          f"\nJ(X)={0.5 * np.sum(np.square(error))}")
    # Adding odometry will improve things a lot
    odo_model = gtsam.noiseModel.Isotropic.Sigma(1, sigma_odo)
    for k in range(4):
        odof = gtsam.CustomFactor(odo_model, [unknown[k], unknown[k + 1]],
                                  partial(error_odom, np.array([o[k]])))
        factor_graph.add(odof)
    params = gtsam.GaussNewtonParams()
    optimizer = gtsam.GaussNewtonOptimizer(factor_graph, v, params)
    result = optimizer.optimize()
    error = np.array([(result.atVector(unknown[k]) - x[k])[0]
                      for k in range(5)])
    print("Result with GPS+Odometry")
    print(result, np.round(error, 2),
          f"\nJ(X)={0.5 * np.sum(np.square(error))}")
    # This is great, but GPS noise is still apparent, so now we add the two landmarks
    lm_model = gtsam.noiseModel.Isotropic.Sigma(1, sigma_lm)
    factor_graph.add(
        gtsam.CustomFactor(lm_model, [unknown[0]],
                           partial(error_lm, np.array([lm_0 + z_0]))))
    factor_graph.add(
        gtsam.CustomFactor(lm_model, [unknown[3]],
                           partial(error_lm, np.array([lm_3 + z_3]))))
    params = gtsam.GaussNewtonParams()
    optimizer = gtsam.GaussNewtonOptimizer(factor_graph, v, params)
    result = optimizer.optimize()
    error = np.array([(result.atVector(unknown[k]) - x[k])[0]
                      for k in range(5)])
    print("Result with GPS+Odometry+Landmark")
    print(result, np.round(error, 2),
          f"\nJ(X)={0.5 * np.sum(np.square(error))}")