In Assignment 3, you will be implementing several functions that explore data about airports and flights. The data originally comes from OpenFlights, an open source project. This handout explains the problem being solved and the tasks to complete for the assignment. Please read it carefully and in its entirety.
Goals of this assignment:
The purpose of this assignment is to give you practice with understanding a complicated real-world problem domain. The programming concepts you will practice are:
Logistics:
OpenFlights is "a tool that lets you map your flights around the world, search and filter them in all sorts of interesting ways, calculate statistics automatically, and share your flights and trips with friends and the entire world (if you wish)." This assignment utilizes airport and flight data from OpenFlights, so we recommend that you go try it out for 10 minutes. It should make understanding the rest of this assignment much easier. You don't need to create an account, just try these things:
In this assignment, you will be implementing several functions that explore the flight data.
In this assignment, we will be working with three types of data:
Airports connect cities all over the world. Airports have two types of codes: IATA and ICAO, issued by two different organizations. We will use the IATA airport code throughout this assignment. IATA codes are three-letter codes used to efficiently identify airports (e.g. the IATA code for Toronto Pearson International Airport is YYZ). They are unique and thus useful for differentiating between different airports – for example, Los Angeles, USA has the code LAX. In contrast, Los Angeles, Chile has the code LSQ.
The airport data we are using is included for you in the data/airports.csv
file. We discuss this in more detail in the What to do > Part 1 section.
A route is a direct flight that can take you from one airport to another. The airport that you start from is called the source. The airport that you arrive at is called the destination. Both the source and the destination are expressed as IATA airport codes. For example, a route from Toronto, Canada to New York, USA could be expressed as YYZ to LGA. Note that some cities have multiple airports. For example, New York has two: LGA and JFK.
The routes data we are using is included for you in the data/routes.csv
file. We discuss this in more detail in the What to do > Part 1 section.
Each flight has an airport they start from called the source, an airport they arrive at called the destination, a time of departure, and a duration. Within this assignment, we are assuming that the available flights (including departure time) remain the same each day. That is, if we have a flight that departs from Toronto (YYZ) to Quebec (YQB) at 12:30am today, such a flight will also be departing at 12:30am tomorrow, and the next day at 12:30am, and so on.
The flights data we are using is included for you in the data/flights.csv
file. We discuss this in more detail in the What to do > Part 1 section.
Read the page on type annotations to familarize yourself with the annotation style below, if needed.
We are using a mixture of dictionaries and lists to store our airport, route and flight data.
The type annotation for a dictionary of all airport information is: Dict[str, Dict[str, str]]
.
The type annotation for a dictionary of all route information is: Dict[str, List[str]]
The type annotation for an individual flight's information is: Dict[str, Union[str, float]]
Rather than type those out every time, we can save them to special kinds of variables called
AirportsDict
, RoutesDict
, and FlightDict
respectively.
We have already defined these variables for you in flight_constants.py
.
In order to use these type annotations,
we can import the annotation from flight_constants
:
from flight_constants import AirportsDict, RoutesDict, FlightDict
flight_functions.py
and flight_reader.py
.
These types describe how you will store the airport, route and flight information. As mentioned above, they have been imported for you in the starter code, and you should use them in your type contracts.
Type | Description |
---|---|
AirportsDict = Dict[str, Dict[str, str]]
|
This is how you will represent the contents of file data/airports.csv . Each key is an IATA code for an airport. Each value is a sub-dictionary of information about the airport, where the key is a string description of the category of information (e.g. "Name", "City", "Country", etc.). and the value is the information associated with that category.
|
RoutesDict = Dict[str, List[str]]
|
This is how you will represent the contents of file data/routes.csv . Each key is an IATA code representing the source airport for a flight. Each value is the list of IATA codes representing the destination airports for a flight from the source airport.
|
FlightDict = Dict[str, Union[str, float]]
|
This is how you will represent EACH line in the contents of file
To represent all the information from the flights file, we will have multiple FlightDict objects stored in a list – the annotation for this would be |
a3/
├─── a3_checker.py
├─── data/
├─── airports.csv
├─── routes.csv
├─── flights.csv
├─── flight_functions.py
├─── flight_reader.py
├─── flight_constants.py
├─── flight_example_data.py
├─── pyta/
├─── many pyta files...
The following paragraphs briefly explain the files you have been given; more in-depth descriptions appear later in this handout.
We have provided airports.csv
and routes.csv
, which were downloaded from the OpenFlights website though they have been modified for our assignment, as well as flights.csv
made specifically for this assignment. These files will be discussed in more detail in the What to do > Part 1 section.
flight_reader.py
, flight_functions.py
flight_constants.py
flight_example_data
a3_checker.py
The checker tests two things:
Reminder: passing the checker does NOT mean your code will pass correctness tests. The checker checks solely for style, NOT correctness.
For each function you write in this assignment, remember to follow the Function Design Recipe we have been using in class to write the functions for this assignment.
You can assume all the functions will receive input that satisfies their type contracts. The files that you will need to read will be properly formed, as described in this handout. The dictionaries and other inputs to the other functions will be valid inputs — however your functions should deal with receiving IATA codes for airports that do not appear in your dictionaries as specified later in this handout.
As discussed in Week 9 in the PCRS exercise for the random story generation program, top-down design is an approach to designing and implementing the body of a function.
Top-down design has you write down, in English, what the major steps are to solving the function.
After that, repeatedly select one of the steps. If you know how to directly translate the English into Python, do so. Otherwise, write a call on a function (that doesn't exist yet!) that will do what the English says to do. Then use the Function Design Recipe to design and write that new function. When it's time to write the body of the new function, use top-down design!
We will be testing and marking each of these functions individually. So, even if you can't complete them all, you can earn marks for correctly implementing some of the functions.
Before we get started, as you're working on the assignment, keep these things in mind:
Let's get started! At a high-level, your next steps are to:
flight_reader.py
.flight_reader.py
as specified in the section labelled "Part 1" below.
a3_checker.py
.
flight_functions.py
(complete and test the functions specified in the section below).flight_functions.py
(complete and test the functions specified in the section below).
The data files you will work with in this assignment are included in the starter code as data/airports.csv
, data/routes.csv
and data/flights.csv
.
The purpose of this file's functions is to read and parse these data files. You will implement these three functions to do so:
read_airports(TextIO) -> AirportsDict
read_routes(TextIO, AirportsDict) -> RoutesDict
read_flights(TextIO, RoutesDict) -> List[FlightDict]
Before you start programming, read the rest of Part 1. Here are some useful tips:
Open flight_reader.py
and complete the functions that are missing bodies. We have provided you with the function headers, docstring description, and doctest examples for the functions you need to implement; you do not need to add or change them. The focus of Part 1 is implementing the bodies of these functions and testing them. The rest of this section contains more information to help you implement these functions.
When reading and storing the airport and route data, you may encounter Null values. OpenFlights represents a Null value as: \N. As a Python string, this looks like: '\\N'. We have saved this value in a constant called OPENFLIGHTS_NULL_VALUE that is defined in flight_constants.py. Use this when you need to check if a value from the data file is null (as specified in the function descriptions below).
airports.csv
This section will be useful for completing read_airports
in flight_reader.py
.
The first line in this data file is a header line containing all the categories of information about each airport:
Airport ID,Name,City,Country,IATA,ICAO,Latitude,Longitude,Altitude,Timezone,DST,Tz,Type,Source
After this header line, each line in airports.csv
represents information about one airport. Here is an example line:
193,"Lester B. Pearson International Airport","Toronto","Canada","YYZ","CYYZ",43.6772003174,-79.63059997559999,569,-5,"A","America/Toronto","airport","OurAirports"
We will mainly just be dealing with the IATA code part of the airport data, so the following table is not too important to understand for this assignment, but if you are curious about what each category means, here is a quick description of each of them:
Name | Description |
---|---|
Airport ID
|
Unique airport identifier (set by OpenFlights) |
Name
|
Name of airport which may or may not contain the City name. |
City
|
Main city served by airport. May be spelled differently from Name. |
Country
|
Country or territory where airport is located. |
IATA
|
3-letter airport IATA code. Null (represented with \N) if not assigned/unknown. (Your program should ignore entries that have a Null IATA code: skip those lines as you read the file.) |
ICAO
|
4-letter airport ICAO code. Null (\N) if not assigned. |
Latitude
|
Decimal degrees, usually to six significant digits. Negative is South, positive is North. |
Longitude
|
Decimal degrees, usually to six significant digits. Negative is West, positive is East. |
Altitude
|
In feet. |
Timezone
|
Hours offset from UTC. Fractional hours are expressed as decimals, eg. India is 5.5. |
DST
|
Daylight savings time. One of E (Europe), A (US/Canada), S (South America), O (Australia), Z (New Zealand), N (None) or U (Unknown). |
Tz
|
Timezone in "tz" (Olson) format, eg. "America/Los_Angeles". |
Type
|
Type of airport. It could be "airport" for air terminals, "station" for train stations, "port" for ferry terminals and "unknown" if not known. You will be working only with type=airport. |
Source
|
Source of this data. You will be working only with source=OurAirports , which comes from http://ourairports.com/airports/
|
We will store airport data as a dictionary of dictionaries (see AirportsDict description here). The outer dictionary will have keys that are IATA airport codes. The value associated with each airport code will be another dictionary (i.e., the inner dictionary). The inner dictionary will have keys that are categories of information about the airport (e.g., "Name", "City", "Country" etc.), and the values will be the information associated with each category for that airport.
For example, if our airports.csv file had two airports like so:
Airport ID,Name,City,Country,IATA,ICAO,Latitude,Longitude,Altitude,Timezone,DST,Tz,Type,Source 1,Apt1,Cty1,Cntry1,AA1,AAA1,-1,1,1,1,1,D1,Typ1,Src1 2,Apt2,Cty2,Cntry2,AA2,AAA2,-2,2,2,2,2,D2,Type2,Src2the AirportsDict representation of this file's data would be:
{ "AA1": { "Airport ID": "1", "Name": "Apt1", "City": "Cty1", "Country": "Cntry1", "IATA": "AA1", "ICAO": "AAA1", "Latitude": "-1", "Longitude": "1", "Altitude": "1", "Timezone": "1", "DST": "1", "Tz": "D1", "Type": "Typ1", "Source": "Src1", }, "AA2": { "Airport ID": "2", "Name": "Apt2", "City": "Cty2", "Country": "Cntry2", "IATA": "AA2", "ICAO": "AAA2", "Latitude": "-2", "Longitude": "2", "Altitude": "2", "Timezone": "2", "DST": "2", "Tz": "D2", "Type": "Type2", "Source": "Src2", } }
routes.csv
This section will be useful for completing read_routes
in flight_reader.py
.
The first line in this data file is a header line containing all the categories of information about each airport:
Airline,Airline ID,Source airport,Source airport ID,Destination airport,Destination airport ID,Codeshare,Stops,Equipment
After this header line, each line in routes.csv
represents route information about a direct flight from one airport to another. Here is an example line:
AA,24,JFK,3797,YYZ,193,Y,0,CR7
Note that routes are directional, meaning if an airline operates services from A to B and from B to A, then both A-B and B-A are listed. And as with airports.csv
, the special value \N
is used for "NULL"
to indicate that no value is available.
We will mainly just be dealing with the Source airport and Destination airport columns of the route data, so most of the following table is not too important to understand for this assignment, but if you are curious about what each category means, here is a quick description of each of them:
Name | Description |
---|---|
Airline
|
2-letter (IATA) or 3-letter (ICAO) code of the airline. |
Airline ID
|
Unique airline identifier(set by OpenFlights) |
Source airport
|
3-letter (IATA) or 4-letter (ICAO) code of the source airport. |
Source airport ID
|
Unique OpenFlights identifier for source airport (matches that in airports.dat) |
Destination airport
|
3-letter (IATA) or 4-letter (ICAO) code of the destination airport. |
Destination airport ID
|
Unique OpenFlights identifier for destination airport (matches that in airports.dat) |
Codeshare
|
"Y" if this flight is a codeshare (that is, not operated by Airline, but another carrier), and is "\N" otherwise. |
Stops
|
Number of stops on this flight ("0" for direct) |
Equipment
|
3-letter codes for plane type(s) generally used on this flight, separated by spaces |
We will store routes data as a dictionary (see RoutesDict description here). The dictionary will have keys that are IATA airport codes. The value associated with each airport code will be a list of IATA codes that represent destinations you can reach from that airport. That is, each source airport's IATA code will be a key, and any destination airport reachable from that source will be included in a list of IATA codes as its value in the dictionary.
For example, if our routes.csv file had three routes like so:
Airline,Airline ID,Source airport,Source airport ID,Destination airport,Destination airport ID,Codeshare,Stops,Equipment A1,1111,AA1,1,AA2,2,,0,EQ1 A2,2222,AA2,2,AA3,3,,0,EQ1 A1,1111,AA1,1,AA4,4,,0,EQ1this means we have a direct flight from airport AA1 to airport AA2 and AA4, and another direct flight from airport AA2 to AA3. The RoutesDict representation of this file's data would be:
{ "AA1": ["AA2", "AA4"], "AA2": ["AA3"] }
flights.csv
This section will be useful for completing read_flights
in flight_reader.py
.
The first line in this data file is a header line containing all the categories of information about each flight:
Flight ID,Source airport,Destination airport,Departure time,Duration
After this header line, each line in flights.csv
represents flight information about a direct flight from one airport to another. Here is an example line:
1486,YQB,YYZ,0.5,4.5
Note that the time is represented as a float from 0.0 to 24.0, based on the 24-hour clock. So, in the above line, the departure time being 0.5 represents 00:30 in the 24-hour clock (which is equivalent to 12:30am in the 12-hour clock).
Also, as mentioned before, within this assignment, we are assuming that the available flights (including departure time) remain the same each day. That is, the above line being in flights.csv signifies that we have a flight that departs from Toronto (YYZ) to Quebec (YQB) at 12:30am (0.5) on each day.
The last column in the file represents the flight duration as a float – this is in hours, so in the above example, the flight is 4.5 hours long.
We will store flights data as a list of dictionaries (see FlightDict description here). Each dictionary will have keys that are category headings (taken from the first line of the data file), and the values are the data associated with that category.
For example, if our flights.csv file had two flights like so:
Flight ID,Source airport,Destination airport,Departure time,Duration 1,AA1,AA2,0.00,2.00 2,AA2,AA3,4.00,3.50this means we have a direct flight from airport AA1 to airport AA2 which leaves at 0:00 (12am) each day and takes a duration of 2 hours, and another direct flight from airport AA2 to AA3 which leaves at 4:00 (4am) each day and takes 3.5 hours. In our reader function for flights, we want to create and return a list of FlightDict (dictionaries of flight information for each flight). The List[FlightDict] representation of this file's data would be:
[ {'Flight ID': '1', 'Source airport': 'AA1', 'Destination airport': 'AA2', 'Departure time': 0.00, 'Duration': 2.00}, {'Flight ID': '2', 'Source airport': 'AA2', 'Destination airport': 'AA3', 'Departure time': 4.00, 'Duration': 3.50} ]
Based on the information above, write the function body for the following functions in flight_reader.py
:
Function name (Parameter types) -> Return type |
Full Description |
---|---|
read_airports (TextIO) -> AirportsDict |
The parameter represents an airport data file that is already open for reading.
Read the file and return the data in the
We have provided the header, the docstring, and an example. Make sure you understand the example before you start writing this function. It uses type |
read_routes (TextIO, AirportsDict) -> RoutesDict |
The parameter represents a routes data file that is already open for reading and the second parameter represents airport information. Read the file and return the route information contained in the file. Include only the routes where both the source and destination are keys in the given We have provided the header, the docstring, and an example. Make sure you understand the example before you start writing this function. |
read_flights (TextIO, RoutesDict) -> List[FlightDict] |
The first parameter represents a flights data file that is already open for reading and the second parameter represents routes information.
Read the file and return all the flight information contained in the file. Include only the
flights where the source and destination of the flight is a valid route that exists in the given We have provided the header, the docstring, and an example. Make sure you understand the example before you start writing this function. |
In Part 2, you will complete four functions using the Function Design Recipe. We have included the type contracts and a specification of every function below; please read through them carefully.
The purpose of all these functions in Part 2 is to check parts of the existing data for certain conditions (e.g. Is there a direct flight from one airport to another? Is a sequence of airports a valid way to fly from the first airport in the sequence to the last? etc.)
Note that you must NOT mutate the arguments of these functions.
Open flight_functions.py
and complete the following functions. We recommend that you work on them in the following order. Remember to use existing functions as helper functions if applicable.
For the three functions below, you just have to write the function body. The examples and docstring are already provided for you - you do not need to add or modify these.
Function name (Parameter types) -> Return type |
Full Description |
---|---|
get_airport_info (AirportsDict, str, str) -> str |
The first parameter represents a dictionary of airport information (as previously described in this handout). The second parameter represents the IATA code for an airport, the third parameter represents the name of a category of airport information (e.g. "Name", "Country", "City", etc.). Return the column information about the airport.
For this function, you may assume the IATA code provided is valid and appears in the airport information dictionary. |
is_direct_flight (str, str, RoutesDict) -> bool |
The first parameter represents the IATA code for a source airport, the second parameter represents the IATA code for a destination airport, and the third parameter represents route information. Return whether there is a direct flight from the source airport to the destination airport in the given route information.
If the IATA code for the source airport (iata_src) does NOT appear in the route information dictionary (routes), the function should return False. |
is_valid_flight_sequence (List[str], RoutesDict) -> bool |
The first parameter represents a list of IATA codes and the second parameter represents route information. Determine whether the sequence of IATA codes is valid: that each adjacent pair of IATA codes represents a route in the given route information. Return True if the sequence of flights is a valid route, or False otherwise.
Note that it is possible for any of the IATA codes provided in the flight sequence to be invalid themselves (i.e. they do not appear in the route information dictionary). This would also make the flight sequence be considered invalid. |
For the fourth function, you must also complete the type contract and docstring. Include at least two examples that call the function you are designing in your docstring. The type contract and docstring example must be formatted correctly to be interpreted by the doctest module. You may find the existing examples from Parts 1, the other three functions in Part 2, and Part 3 to be a useful reference.
You will need to paraphrase the specifications of the function (see below) to get an appropriate docstring description. Make sure you review the Function Design Recipe and CSC108 Python Style Guidelines for the rules on how to write a docstring description.
Function name (Parameter types) -> Return type |
Full Description (paraphrase for your docstring) |
---|---|
get_remaining_flights_today (List[FlightDict], float) -> List[FlightDict] |
The first parameter represents a list of information about all available flights.
The second parameter represents a specific time (a float from 0.0 to 24.0 representing the 24-hour clock).
Return a list of all flights that are remaining today (their
departure time should be greater than or equal to the given current time).
Note that each flight information in the resulting list should be in a FlightDict format as previously described in this handout. You may assume the given input (the flight information and the current time) will be valid. |
In Part 3, you will implement three useful algorithms that work with the route data:
get_best_flight(str, str, List[FlightDict], float) -> Optional[FlightDict]
find_reachable_destinations(str, int, RoutesDict) -> List[str]
calculate_trip_time(List[str], List[FlightDict]) -> float
Function name (Parameter types) -> Return type |
Full Description |
---|---|
get_best_flight (str, str, List[FlightDict], float)
-> Optional[FlightDict] |
The first and second parameters represent IATA codes - the first is the source airport, and the second is our desired destination. The third parameter is a list of all available flights on any given day. The last parameter is an optional parameter that represents the current time (a float from 0.0 to 24.0, as per the 24-hour clock). If no current time is provided, the time is assumed to be 0.0 (the beginning of the day). Return the flight in the given list of flights that is departing from the souce airport and arriving at the destination airport. If there are multiple such flights, return the one that will get to the destination the soonest based on the given current time, the flight's departure time and the flight's duration. If there is no direct flight from the given source to given destination, return None. |
find_reachable_destinations (str, int, RoutesDict) -> List[str] |
The first parameter represents an IATA code. The second parameter is the maximum number of direct flights allowed. The last parameter represents route information. Return a list of IATA codes reachable from the first parameter in steps from 1 up to (and including) the maximum number of hops. For example, with IATA code 'AA1' , maximum number of flights as 2, and the following route information
{'AA1': ['AA2', 'AA4'], 'AA2': ['AA3'], 'AA3': ['AA4', 'AA1'], 'AA4': ['AA1']}the result should be ['AA1', 'AA2, 'AA3', 'AA4'] .The list should not contain an IATA airport code more than once. The airport codes in the list should appear in increasing alphabetical/numerical order (use the list.sort method on a list of strings to achieve this). Preconditions (things you can safely assume, and do NOT need to verify in your function body): n >= 1 and the given source airport appears in routes |
calculate_trip_time (List[str], List[FlightDict]) -> float |
The first parameter is a list of airports
representing the path of the trip (containing IATA codes representing airports), and the second parameter represents flight
information about all available flights. The first value in the path list represents the IATA code for a source airport, the last value in the path list represents the IATA code for a destination airport. Return a float corresponding to the amount of time required to travel from the source airport to the destination airport, as outlined by the flight path. The start time of your trip is the beginning of the day (i.e. time is 0.0). You may assume the path given is a valid path of flights (i.e. there exists a direct flight between each adjacent flight in the path list). |
Some hints for get_best_flight:
Some hints for find_reachable_destinations:
Let us expand one of the doctest examples:
find_reachable_destinations('AA1', 2, routes_example_data)
['AA1', 'AA2', 'AA3', 'AA4']
The maximum number of direct flights in this example is 2
and routes_example_data
is:
{
"AA1": ["AA2", "AA4"],
"AA2": ["AA3"],
"AA3": ["AA4", "AA1"],
"AA4": ["AA1"],
}
'AA1'
, then there are two possible direct flights to 'AA2'
and 'AA4'
. So we should accumulate 'AA2'
and 'AA4'
(these are both destinations reachable with 1 direct flight) and move on to the next step.'AA2'
, then we are now (hypothetically) at the 'AA2'
airport. This means, based on routes_example_data
, we can reach another airport via another direct flight: 'AA3'
. So we should accumulate 'AA3'
and move on to the next step.'AA4'
, then we are now (hypothetically) at the 'AA4'
airport. This means, based on routes_example_data
, we can reach another airport via another direct flight: 'AA1'
(where we came from). So we should accumulate 'AA1'
and move on to the next step.'AA3'
or 'AA4'
, we have taken the maximum number of direct flights in the example (i.e., 2
). So we should stop and then return what we have accumulated.Though this was not relevant in the current example, keep in mind that the docstring explicitly says:
The list should not contain an IATA airport code more than once.
In a more complex example, like the data loaded from the file, the number of reachable destinations returned would likely be much larger than our small doctest example.
Some hints for calculate_trip_time:
This algorithm should calculate the trip time when travelling from a source airport to a destination airport, following a provided path.
Let us expand one of the doctest examples:
calculate_trip_time(["AA4", "AA1", "AA2"], flights_example_data)
14.0
The path we're taking is from AA4 to AA1 to AA2 and flights_example_data
is:
[
{'Flight ID': '1',
'Source airport': 'AA1',
'Destination airport': 'AA2',
'Departure time': 0.00,
'Duration': 2.00},
{'Flight ID': '2',
'Source airport': 'AA2',
'Destination airport': 'AA3',
'Departure time': 4.00,
'Duration': 3.50},
{'Flight ID': '3',
'Source airport': 'AA3',
'Destination airport': 'AA4',
'Departure time': 7.00,
'Duration': 2.00},
{'Flight ID': '4',
'Source airport': 'AA4',
'Destination airport': 'AA1',
'Departure time': 2.00,
'Duration': 3.00},
{'Flight ID': '5',
'Source airport': 'AA1',
'Destination airport': 'AA4',
'Departure time': 0.00,
'Duration': 2.00},
{'Flight ID': '6',
'Source airport': 'AA1',
'Destination airport': 'AA2',
'Departure time': 12.00,
'Duration': 2.00}
]
Note: If there were no more flights from our current source to next destination available in the flights remaining today, we would have had to wait until the next day and search all the flights for one that matches our source and destination. In that case, make sure to take the extra waiting time into consideration as we wait for the next day.
It is strongly recommended that you test each function as you write it. This will make your life easier — if you write 2–3 lines of code, then test, you know where to look for your bug! If you wait until you've written a whole lot of code, it's harder to figure out where to look.
As usual, follow the Function Design Recipe. Once you've implemented a function, run it on the examples in your docstring. Here are a few tips:
These are the aspects of your work that will be marked for Assignment 3:
The very last thing you should do before submitting is to run the checker one last time. Otherwise, you could make a small error in your final changes before submitting that causes your code to receive zero for correctness. If your code does not run, you will get a zero for correctness.
For this assignment, you will hand in two files on MarkUs:
flight_reader.py
: This should contain your implementations of the three required functions for reading files, and any helper functions that you have written for them.
flight_functions.py
: This should contain implementations of the remaining required functions, and any helper functions that you have written for them.
Once you have submitted, be sure to check that you have submitted the correct version; new or missing files will not be accepted after the due date. Remember that spelling of filenames, including case, counts. If your file is not named exactly as above, your code will receive zero for correctness.