GasimV / Yandex.Practicum

This repository showcase my educational projects from Yandex.Practicum and books, spanning Data Science, C++ development, Python Backend development, Frontend development and more.
0 stars 0 forks source link

Transport Catalogue Debugging #2

Open GasimV opened 3 days ago

GasimV commented 3 days ago

Below is a structured approach to debugging a program, broken into key steps. This method can be applied to most software development scenarios.


Structured Debugging Approach

1. Understand the Problem


2. Narrow Down the Scope


3. Add Debug Statements

Example Debug Outputs:

std::cout << "[FunctionName] Description of current state: variable = " << variable << "\n";

Debugging Categories:

  1. Input Data:
    • Verify what is being passed into the function.
  2. Intermediate Results:
    • Check results after key calculations.
  3. Final Output:
    • Compare the computed result with expectations.

4. Isolate the Issue


5. Cross-Check Assumptions


6. Fix the Issue


7. Clean Up Debug Statements


Example Method Implementation

Here’s how you could apply the above to debug route parsing (ParseRoute) in the future:

std::vector<std::string_view> ParseRoute(std::string_view route) {
    std::cout << "[ParseRoute] Input route: \"" << route << "\"\n";
    std::vector<std::string_view> stops;

    if (route.find('>') != route.npos) {
        // Cyclic route
        stops = Split(route, '>');
        std::cout << "[ParseRoute] Detected cyclic route with " << stops.size() << " stops.\n";
    } else {
        // Non-cyclic route
        stops = Split(route, '-');
        std::cout << "[ParseRoute] Detected non-cyclic route with " << stops.size() << " stops.\n";
    }

    // Log each stop in the route
    for (size_t i = 0; i < stops.size(); ++i) {
        std::cout << "[ParseRoute] Stop " << i + 1 << ": " << stops[i] << "\n";
    }

    return stops;
}

Reusable Debug Checklist

  1. Input Verification:

    • Ensure input matches the expected format and content.
  2. Intermediate Calculation Logging:

    • Log data as it flows through major functions or loops.
  3. Branch/Condition Testing:

    • Check which conditions are triggered in if, else, or switch blocks.
  4. Output Validation:

    • Compare outputs at every stage with expected values.

Example General Debugging Function

For consistent debugging in all functions, create a macro or utility:

#define DEBUG(var) std::cout << "[DEBUG] " << #var << " = " << var << "\n";

Usage:

int distance = 27400;
DEBUG(distance); // Prints: [DEBUG] distance = 27400

Conclusion

By following this structured approach, you can systematically identify and resolve issues. Keeping this as a reference will help you debug future programs efficiently.

GasimV commented 3 days ago

Here’s how you can add std::cout statements to inspect the intermediate values in ParseCoordinates, ParseDistances, and ApplyCommands. This will help debug why the output doesn't match expectations.


1. ParseCoordinates Debug Statements

In ParseCoordinates, print the input string and the parsed latitude and longitude:

Coordinates ParseCoordinates(std::string_view str) {
    static const double nan = std::nan("");

    auto not_space = str.find_first_not_of(' ');
    auto comma = str.find(',');

    if (comma == str.npos) {
        std::cout << "[ParseCoordinates] Invalid input: \"" << str << "\"\n";
        return {nan, nan};
    }

    auto not_space2 = str.find_first_not_of(' ', comma + 1);

    double lat = std::stod(std::string(str.substr(not_space, comma - not_space)));
    double lng = std::stod(std::string(str.substr(not_space2)));

    std::cout << "[ParseCoordinates] Input: \"" << str << "\" -> Lat: " 
              << lat << ", Lng: " << lng << "\n";

    return {lat, lng};
}

2. ParseDistances Debug Statements

In ParseDistances, print the input string and each extracted distance:

std::vector<std::pair<std::string_view, int>> ParseDistances(std::string_view distances_part) {
    std::vector<std::pair<std::string_view, int>> distances;

    std::cout << "[ParseDistances] Input: \"" << distances_part << "\"\n";

    size_t pos = 0;
    while (pos < distances_part.size()) {
        auto m_pos = distances_part.find("m to ", pos);
        if (m_pos == distances_part.npos) {
            break;
        }

        int distance = std::stoi(std::string(distances_part.substr(pos, m_pos - pos)));

        auto stop_pos = m_pos + 5; // "m to " is 5 characters long
        auto stop_end = distances_part.find_first_of(',', stop_pos);
        if (stop_end == distances_part.npos) {
            stop_end = distances_part.size();
        }

        std::string_view stop_name = distances_part.substr(stop_pos, stop_end - stop_pos);
        distances.emplace_back(stop_name, distance);

        std::cout << "[ParseDistances] Found: Distance = " << distance 
                  << "m to Stop \"" << stop_name << "\"\n";

        pos = stop_end + 1;
    }

    return distances;
}

3. ApplyCommands Debug Statements

Add debug statements to each section in ApplyCommands to track the parsing of stops, distances, and bus routes:

void InputReader::ApplyCommands(TransportCatalogue& catalogue) const {
    // First pass: Add stops and their coordinates
    for (const auto& command : commands_) {
        if (command.command == "Stop") {
            auto colon_pos = command.description.find(',');
            auto coords = ParseCoordinates(command.description.substr(0, colon_pos));
            catalogue.AddStop(command.id, coords);

            std::cout << "[ApplyCommands] Added Stop: \"" << command.id 
                      << "\" with Coordinates: Lat = " << coords.lat 
                      << ", Lng = " << coords.lng << "\n";
        }
    }

    // Second pass: Add distances for stops
    for (const auto& command : commands_) {
        if (command.command == "Stop") {
            auto colon_pos = command.description.find(',');
            auto distances_part = command.description.substr(colon_pos + 1);

            const auto* from_stop = catalogue.GetStopInfo(command.id);
            if (!from_stop) {
                std::cout << "[ApplyCommands] Skip undefined Stop: \"" 
                          << command.id << "\"\n";
                continue;
            }

            auto distances = ParseDistances(distances_part);
            for (const auto& [to_stop_name, distance] : distances) {
                const auto* to_stop = catalogue.GetStopInfo(to_stop_name);
                if (to_stop) {
                    catalogue.SetDistance(from_stop, to_stop, distance);
                    std::cout << "[ApplyCommands] Set Distance: " << distance 
                              << "m from \"" << command.id 
                              << "\" to \"" << to_stop_name << "\"\n";

                    if (catalogue.GetDistance(to_stop, from_stop) == 0) {
                        catalogue.SetDistance(to_stop, from_stop, distance);
                        std::cout << "[ApplyCommands] Set Reverse Distance: " 
                                  << distance << "m from \"" 
                                  << to_stop_name << "\" to \"" << command.id << "\"\n";
                    }
                } else {
                    std::cout << "[ApplyCommands] Destination Stop not found: \"" 
                              << to_stop_name << "\"\n";
                }
            }
        }
    }

    // Third pass: Add bus routes
    for (const auto& command : commands_) {
        if (command.command == "Bus") {
            bool is_cyclic = command.description.find('>') != std::string::npos;
            auto stops = ParseRoute(command.description);
            catalogue.AddRoute(command.id, stops, is_cyclic);

            std::cout << "[ApplyCommands] Added Bus Route: \"" << command.id 
                      << "\" with " << stops.size() << " stops. "
                      << "Cyclic: " << std::boolalpha << is_cyclic << "\n";
        }
    }
}

4. Recompile the Code

After adding the debug statements, recompile your code as before:

g++ -std=c++17 -o transport_catalogue main.cpp input_reader.cpp stat_reader.cpp transport_catalogue.cpp -lm

5. Run the Program

Run the program with your input. For example:

./transport_catalogue < input.txt

6. Analyze the Output

The debug output will include detailed information about:

Check for discrepancies or unexpected behavior. For example:

If something doesn’t match expectations, note where the process diverges and update me with the relevant debug output for further analysis.